diff --git a/.clang-format b/.clang-format index a74fda4b67345..60a6f945ec457 100644 --- a/.clang-format +++ b/.clang-format @@ -1,2 +1,2 @@ BasedOnStyle: LLVM -AlwaysBreakTemplateDeclarations: Yes +AlwaysBreakTemplateDeclarations: true diff --git a/.dir-locals.el b/.dir-locals.el index 1adf35a33ad76..c80d39abf03f0 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -16,7 +16,6 @@ (set (make-local-variable 'swift-project-directory) this-directory) ) - (tab-width . 2) (fill-column . 80) (c-file-style . "swift")) (c++-mode diff --git a/.gitattributes b/.gitattributes index 4a44c64a6dbd5..1ba346f2420de 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.swift.gyb linguist-language=Swift *.cpp.gyb linguist-language=C++ +*.bat text eol=crlf diff --git a/CHANGELOG.md b/CHANGELOG.md index f6990b5ab1b45..9e9f22b92ecab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,26 +3,219 @@ CHANGELOG _**Note:** This is in reverse chronological order, so newer entries are added to the top._ +Swift 5.6 +--------- +* [SE-0315][]: + + Type expressions and annotations can now include "type placeholders" which + directs the compiler to fill in that portion of the type according to the usual + type inference rules. Type placeholders are spelled as an underscore ("`_`") in + a type name. For instance: + + ```swift + // This is OK--the compiler can infer the key type as `Int`. + let dict: [_: String] = [0: "zero", 1: "one", 2: "two"] + ``` + +* [SE-0290][]: + + It is now possible to write inverted availability conditions by using the new `#unavailable` keyword: + + ```swift + if #unavailable(iOS 15.0) { + // Old functionality + } else { + // iOS 15 functionality + } + ``` + +**Add new entries to the top of this section, not here!** + Swift 5.5 --------- +* [SE-0313][]: + + Parameters of actor type can be declared as `isolated`, which means that they + represent the actor on which that code will be executed. `isolated` parameters + extend the actor-isolated semantics of the `self` parameter of actor methods + to arbitrary parameters. For example: + + ```swift + actor MyActor { + func f() { } + } + + func g(actor: isolated MyActor) { + actor.f() // okay, this code is always executing on "actor" + } + + func h(actor: MyActor) async { + g(actor: actor) // error, call must be asynchronous + await g(actor: actor) // okay, hops to "actor" before calling g + } + ``` + + The `self` parameter of actor methods are implicitly `isolated`. The + `nonisolated` keyword makes the `self` parameter no longer `isolated`. + +* [SR-14731][]: + + The compiler now correctly rejects the application of generic arguments to the + special `Self` type: + + ```swift + struct Box { + // previously interpreted as a return type of Box, ignoring the part; + // now we diagnose an error with a fix-it suggesting replacing `Self` with `Box` + static func makeBox() -> Self {...} + } + ``` + +* [SR-14878][]: + + The compiler now correctly rejects `@available` annotations on enum cases with + associated values with an OS version newer than the current deployment target: + + ```swift + @available(macOS 12, *) + public struct Crayon {} + + public enum Pen { + case pencil + + @available(macOS 12, *) + case crayon(Crayon) + } + ``` + + While this worked with some examples, there is no way for the Swift runtime to + perform the requisite dynamic layout needed to support this in general, which + could cause crashes at runtime. + + Note that conditional availability on stored properties in structs and classes + is not supported for similar reasons; it was already correctly detected and + diagnosed. + +* [SE-0311][]: + + Task local values can be defined using the new `@TaskLocal` property wrapper. + Such values are carried implicitly by the task in which the binding was made, + as well as any child-tasks, and unstructured task created from the tasks context. + + ```swift + struct TraceID { + @TaskLocal + static var current: TraceID? + } + + func printTraceID() { + if let traceID = TraceID.current { + print("\(traceID)") + } else { + print("nil") + } + } + + func run() async { + printTraceID() // prints: nil + TraceID.$current.withValue("1234-5678") { + printTraceID() // prints: 1234-5678 + inner() // prints: 1234-5678 + } + printTraceID() // prints: nil + } + + func inner() { + // if called from a context in which the task-local value + // was bound, it will print it (or 'nil' otherwise) + printTraceID() + } + ``` + +* [SE-0316][]: + + A type can be defined as a global actor. Global actors extend the notion + of actor isolation outside of a single actor type, so that global state + (and the functions that access it) can benefit from actor isolation, + even if the state and functions are scattered across many different + types, functions and modules. Global actors make it possible to safely + work with global variables in a concurrent program, as well as modeling + other global program constraints such as code that must only execute on + the "main thread" or "UI thread". A new global actor can be defined with + the `globalActor` attribute: + + ```swift + @globalActor + struct DatabaseActor { + actor ActorType { } + + static let shared: ActorType = ActorType() + } + ``` + + Global actor types can be used as custom attributes on various declarations, + which ensures that those declarations are only accessed on the actor described + by the global actor's `shared` instance. For example: + + ```swift + @DatabaseActor func queryDB(query: Query) throws -> QueryResult + + func runQuery(queryString: String) async throws -> QueryResult { + let query = try Query(parsing: queryString) + return try await queryDB(query: query) // 'await' because this implicitly hops to DatabaseActor.shared + } + ``` + + The concurrency library defines one global actor, `MainActor`, which + represents the main thread of execution. It should be used for any code that + must execute on the main thread, e.g., for updating UI. + +* [SE-0313][]: + + Declarations inside an actor that would normally be actor-isolated can + explicitly become non-isolated using the `nonisolated` keyword. Non-isolated + declarations can be used to conform to synchronous protocol requirements: + + ```swift + actor Account: Hashable { + let idNumber: Int + var balance: Double + + nonisolated func hash(into hasher: inout Hasher) { // okay, non-isolated satisfies synchronous requirement + hasher.combine(idNumber) // okay, can reference idNumber from outside the let + hasher.combine(balance) // error: cannot synchronously access actor-isolated property + } + } + ``` + +* [SE-0300][]: + + Async functions can now be suspended using the `withUnsafeContinuation` + and `withUnsafeThrowingContinuation` functions. These both take a closure, + and then suspend the current async task, executing that closure with a + continuation value for the current task. The program must use that + continuation at some point in the future to resume the task, passing in + a value or error, which then becomes the result of the `withUnsafeContinuation` + call in the resumed task. + * Type names are no longer allowed as an argument to a subscript parameter that expects a metatype type -```swift -struct MyValue { -} + ```swift + struct MyValue { + } -struct MyStruct { - subscript(a: MyValue.Type) -> Int { get { ... } } -} + struct MyStruct { + subscript(a: MyValue.Type) -> Int { get { ... } } + } -func test(obj: MyStruct) { - let _ = obj[MyValue] -} -``` + func test(obj: MyStruct) { + let _ = obj[MyValue] + } + ``` -Accepting subscripts with `MyValue` as an argument was an oversight because `MyValue` requires explicit `.self` -to reference its metatype, so correct syntax would be to use `obj[MyValue.self]`. + Accepting subscripts with `MyValue` as an argument was an oversight because `MyValue` requires explicit `.self` + to reference its metatype, so correct syntax would be to use `obj[MyValue.self]`. * [SE-0310][]: @@ -8488,13 +8681,19 @@ Swift 1.0 [SE-0284]: [SE-0286]: [SE-0287]: +[SE-0290]: [SE-0293]: [SE-0296]: [SE-0297]: [SE-0298]: [SE-0299]: +[SE-0300]: [SE-0306]: [SE-0310]: +[SE-0311]: +[SE-0313]: +[SE-0315]: +[SE-0316]: [SR-75]: [SR-106]: @@ -8533,3 +8732,5 @@ Swift 1.0 [SR-11429]: [SR-11700]: [SR-11841]: +[SR-14731]: +[SR-14878]: diff --git a/CMakeLists.txt b/CMakeLists.txt index 337029b3f71fd..c737090182c22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12.4) +cmake_minimum_required(VERSION 3.19.6) # TODO: Fix RPATH usage to be CMP0068 compliant # Disable Policy CMP0068 for CMake 3.9 @@ -51,6 +51,17 @@ set(CMAKE_CXX_EXTENSIONS NO) include(SwiftUtils) include(CheckSymbolExists) include(CMakeDependentOption) +include(CheckLanguage) + +# Enable Swift for the host compiler build if we have the language. It is +# optional until we have a bootstrap story. +check_language(Swift) +if(CMAKE_Swift_COMPILER) + enable_language(Swift) +else() + message(STATUS "WARNING! Did not find a host compiler swift?! Can not build + any compiler host sources written in Swift") +endif() # # User-configurable options that control the inclusion and default build @@ -148,7 +159,7 @@ set(SWIFT_ANALYZE_CODE_COVERAGE FALSE CACHE STRING # SWIFT_VERSION is deliberately /not/ cached so that an existing build directory # can be reused when a new version of Swift comes out (assuming the user hasn't # manually set it as part of their own CMake configuration). -set(SWIFT_VERSION "5.5") +set(SWIFT_VERSION "5.6") set(SWIFT_VENDOR "" CACHE STRING "The vendor name of the Swift compiler") @@ -172,7 +183,7 @@ endif() set(SWIFT_USE_LINKER ${SWIFT_USE_LINKER_default} CACHE STRING "Build Swift with a non-default linker") -option(SWIFT_DISABLE_DEAD_STRIPPING +option(SWIFT_DISABLE_DEAD_STRIPPING "Turn off Darwin-specific dead stripping for Swift host tools." FALSE) set(SWIFT_TOOLS_ENABLE_LTO OFF CACHE STRING "Build Swift tools with LTO. One @@ -180,6 +191,11 @@ set(SWIFT_TOOLS_ENABLE_LTO OFF CACHE STRING "Build Swift tools with LTO. One option only affects the tools that run on the host (the compiler), and has no effect on the target libraries (the standard library and the runtime).") +# NOTE: We do not currently support building libswift with the Xcode generator. +cmake_dependent_option(SWIFT_TOOLS_ENABLE_LIBSWIFT + "Enable building libswift and linking libswift into the compiler itself." FALSE + "NOT CMAKE_GENERATOR STREQUAL \"Xcode\"" FALSE) + # The following only works with the Ninja generator in CMake >= 3.0. set(SWIFT_PARALLEL_LINK_JOBS "" CACHE STRING "Define the maximum number of linker jobs for swift.") @@ -234,6 +250,10 @@ option(SWIFT_BUILD_RUNTIME_WITH_HOST_COMPILER "Use the host compiler and not the internal clang to build the swift runtime" FALSE) +option(SWIFT_RUN_TESTS_WITH_HOST_COMPILER + "Run tests against the host compiler and not the just built swift" + FALSE) + set(SWIFT_SDKS "" CACHE STRING "If non-empty, limits building target binaries only to specified SDKs (despite other SDKs being available)") @@ -359,7 +379,7 @@ option(SWIFT_RUNTIME_CLOBBER_FREED_OBJECTS "${SWIFT_RUNTIME_CLOBBER_FREED_OBJECTS_default}") option(SWIFT_STDLIB_SIL_DEBUGGING - "Compile the Swift standard library with -gsil to enable debugging and profiling on SIL level" + "Compile the Swift standard library with -sil-based-debuginfo to enable debugging and profiling on SIL level" FALSE) option(SWIFT_CHECK_INCREMENTAL_COMPILATION @@ -403,14 +423,26 @@ option(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING "Enable experimental Swift differentiable programming features" FALSE) +option(SWIFT_IMPLICIT_CONCURRENCY_IMPORT + "Implicitly import the Swift concurrency module" + TRUE) + option(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY - "Enable experimental Swift concurrency model" + "Enable build of the Swift concurrency module" + FALSE) + +option(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED + "Enable experimental distributed actors and functions" FALSE) option(SWIFT_ENABLE_DISPATCH "Enable use of libdispatch" TRUE) +option(SWIFT_ENABLE_GLOBAL_ISEL_ARM64 + "Enable global isel on arm64, arm64e, arm64_32" + FALSE) + cmake_dependent_option(SWIFT_BUILD_SYNTAXPARSERLIB "Build the Swift Syntax Parser library" TRUE "SWIFT_ENABLE_DISPATCH" FALSE) @@ -457,7 +489,9 @@ endif() set(SWIFT_BUILD_HOST_DISPATCH FALSE) if(SWIFT_ENABLE_DISPATCH AND NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) - if(SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT) + # Only build libdispatch for the host if the host tools are being built and + # specifically if these two libraries that depend on it are built. + if(SWIFT_INCLUDE_TOOLS AND (SWIFT_BUILD_SYNTAXPARSERLIB OR SWIFT_BUILD_SOURCEKIT)) set(SWIFT_BUILD_HOST_DISPATCH TRUE) endif() @@ -484,6 +518,11 @@ execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} --version message(STATUS "CMake Make Program (${CMAKE_MAKE_PROGRAM}) Version: ${_CMAKE_MAKE_PROGRAM_VERSION}") message(STATUS "C Compiler (${CMAKE_C_COMPILER}) Version: ${CMAKE_C_COMPILER_VERSION}") message(STATUS "C++ Compiler (${CMAKE_CXX_COMPILER}) Version: ${CMAKE_CXX_COMPILER_VERSION}") +if (CMAKE_Swift_COMPILER) + message(STATUS "Swift Compiler (${CMAKE_Swift_COMPILER}) Version: ${CMAKE_Swift_COMPILER_VERSION}") +else() + message(STATUS "Swift Compiler (None).") +endif() if(SWIFT_PATH_TO_CMARK_BUILD) execute_process(COMMAND ${SWIFT_PATH_TO_CMARK_BUILD}/src/cmark --version OUTPUT_VARIABLE _CMARK_VERSION @@ -500,6 +539,13 @@ else() set(SWIFT_PREBUILT_CLANG TRUE) endif() +# Also mark if we have a prebuilt swift before we do anything. +if("${SWIFT_NATIVE_SWIFT_TOOLS_PATH}" STREQUAL "") + set(SWIFT_PREBUILT_SWIFT FALSE) +else() + set(SWIFT_PREBUILT_SWIFT TRUE) +endif() + include(SwiftSharedCMakeConfig) # NOTE: We include this before SwiftComponents as it relies on some LLVM CMake @@ -566,10 +612,15 @@ set(SWIFT_DARWIN_VARIANTS "^(macosx|iphoneos|iphonesimulator|appletvos|appletvsi set(SWIFT_DARWIN_EMBEDDED_VARIANTS "^(iphoneos|iphonesimulator|appletvos|appletvsimulator|watchos|watchsimulator)") # A convenient list to match Darwin SDKs. Example: -# if("${SWIFT_HOST_VARIANT_SDK}" IN_LIST SWIFT_APPLE_PLATFORMS) +# if("${SWIFT_HOST_VARIANT_SDK}" IN_LIST SWIFT_DARWIN_PLATFORMS) # ... # endif() -set(SWIFT_APPLE_PLATFORMS "IOS" "IOS_SIMULATOR" "TVOS" "TVOS_SIMULATOR" "WATCHOS" "WATCHOS_SIMULATOR" "OSX") +set(SWIFT_DARWIN_PLATFORMS "IOS" "IOS_SIMULATOR" "TVOS" "TVOS_SIMULATOR" "WATCHOS" "WATCHOS_SIMULATOR" "OSX") + +set(SWIFT_APPLE_PLATFORMS ${SWIFT_DARWIN_PLATFORMS}) +if(SWIFT_FREESTANDING_FLAVOR STREQUAL "apple") + list(APPEND SWIFT_APPLE_PLATFORMS "FREESTANDING") +endif() # Configuration flags passed to all of our invocations of gyb. Try to # avoid making up new variable names here if you can find a CMake @@ -698,6 +749,12 @@ if("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") set(SWIFT_PRIMARY_VARIANT_ARCH_default "${SWIFT_HOST_VARIANT_ARCH}") endif() + is_sdk_requested(FREESTANDING swift_build_freestanding) + if(swift_build_freestanding AND (SWIFT_FREESTANDING_FLAVOR STREQUAL "linux")) + # TODO + # configure_sdk_unix("FREESTANDING" "${SWIFT_HOST_VARIANT_ARCH}") + endif() + elseif("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "FREEBSD") set(SWIFT_HOST_VARIANT "freebsd" CACHE STRING @@ -872,6 +929,7 @@ if(SWIFT_INCLUDE_TOOLS) message(STATUS " Build type: ${CMAKE_BUILD_TYPE}") message(STATUS " Assertions: ${LLVM_ENABLE_ASSERTIONS}") message(STATUS " LTO: ${SWIFT_TOOLS_ENABLE_LTO}") + message(STATUS " libswift: ${SWIFT_TOOLS_ENABLE_LIBSWIFT}") message(STATUS "") else() message(STATUS "Not building host Swift tools") @@ -890,6 +948,7 @@ if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_SDK_OVERLAY) message(STATUS "Differentiable Programming Support: ${SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING}") message(STATUS "Concurrency Support: ${SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY}") + message(STATUS "Distributed Support: ${SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED}") message(STATUS "") else() message(STATUS "Not building Swift standard library, SDK overlays, and runtime") @@ -998,6 +1057,11 @@ else() add_subdirectory(stdlib/toolchain) endif() + if (BUILD_SWIFT_CONCURRENCY_BACK_DEPLOYMENT_LIBRARIES) + # Build the back-deployed concurrency library. + add_subdirectory(stdlib/public/BackDeployConcurrency) + endif() + # Some tools (e.g. swift-reflection-dump) rely on a host swiftReflection, so # ensure we build that when building tools. if(SWIFT_INCLUDE_TOOLS) @@ -1014,6 +1078,13 @@ add_subdirectory(include) if(SWIFT_INCLUDE_TOOLS) add_subdirectory(lib) + # "libswift" must come before "tools". + # It adds libswift module names to the global property "libswift_modules" + # which is used in add_swift_host_tool for the lldb workaround. + # + # NOTE: We do not currently support libswift with the Xcode generator. + add_subdirectory(libswift) + # Always include this after including stdlib/! # Refer to the large comment above the add_subdirectory(stdlib) call. # https://bugs.swift.org/browse/SR-5975 @@ -1041,6 +1112,7 @@ endif() if(SWIFT_INCLUDE_TESTS) add_subdirectory(test) + add_subdirectory(validation-test) add_subdirectory(unittests) endif() if(SWIFT_INCLUDE_DOCS) diff --git a/README.md b/README.md index 6e4685ee4780c..168bcfe2b45d3 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ | **Ubuntu 18.04** | x86_64 | [![Build Status](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-18_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-incremental-RA-linux-ubuntu-18_04)|[![Build Status](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-18_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-linux-ubuntu-18_04)| | **Ubuntu 20.04** | x86_64 | [![Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04)|[![Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04)| | **CentOS 8** | x86_64 | [![Build Status](https://ci.swift.org/job/oss-swift-package-centos-8/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-centos-8)|[![Build Status](https://ci.swift.org/job/oss-swift-package-centos-8/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-centos-8)| +| **CentOS 7** | x86_64 | [![Build Status](https://ci.swift.org/job/oss-swift-package-centos-7/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-centos-7)|[![Build Status](https://ci.swift.org/job/oss-swift-package-centos-7/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-centos-7)| | **Amazon Linux 2** | x86_64 | [![Build Status](https://ci.swift.org/job/oss-swift-package-amazon-linux-2/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-amazon-linux-2)|[![Build Status](https://ci.swift.org/job/oss-swift-package-amazon-linux-2/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-amazon-linux-2)| **Swift Community-Hosted CI Platforms** @@ -26,13 +27,6 @@ |**[Windows 2019 (VS 2017)](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_windows_2019.json)** | x86_64 | [![Build Status](https://ci-external.swift.org/job/oss-swift-windows-x86_64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-windows-x86_64)| |**[Windows 2019 (VS 2019)](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_windows_2019_VS2019.json)** | x86_64 | [![Build Status](https://ci-external.swift.org/job/oss-swift-windows-x86_64-vs2019/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-windows-x86_64-vs2019)| -**Swift TensorFlow Community-Hosted CI Platforms** - -| **OS** | **Architecture** | **Build** | -|---|:---:|:---:| -|**[Ubuntu 18.04](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_ubuntu_18_04_tensorflow.json)** | x86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-18.04-tensorflow/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-18.04-tensorflow)| -|**[macOS 10.13](https://github.com/apple/swift-community-hosted-continuous-integration/blob/master/nodes/x86_64_macos_high_sierra_tensorflow.json)** | x86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-macOS-tensorflow/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-macOS-tensorflow)| - ## Welcome to Swift Swift is a high-performance system programming language. It has a clean diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 6666d7f88c5dc..b4b1a6ab86883 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -1,6 +1,6 @@ # -*- mode: cmake -*- -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.19.6) # Add path for custom CMake modules. list(APPEND CMAKE_MODULE_PATH @@ -44,6 +44,7 @@ set(SWIFT_BENCH_MODULES single-source/BitCount single-source/Breadcrumbs single-source/BucketSort + single-source/BufferFill single-source/ByteSwap single-source/COWTree single-source/COWArrayGuaranteedParameterOverhead diff --git a/benchmark/README.md b/benchmark/README.md index 915d320535889..01ea4aa7c8ee1 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -43,7 +43,7 @@ library should be distributed alongside them. ### CMake Standalone (no build-script) To build the Swift benchmarks using only an Xcode installation: install an -Xcode version with Swift support, install cmake 2.8.12, and ensure Xcode is +Xcode version with Swift support, install cmake 3.19.6 or higher, and ensure Xcode is selected with xcode-select. The following build options are available: diff --git a/benchmark/scripts/Benchmark_Driver b/benchmark/scripts/Benchmark_Driver index ff10dd67e6ea0..4fe25104f238b 100755 --- a/benchmark/scripts/Benchmark_Driver +++ b/benchmark/scripts/Benchmark_Driver @@ -31,6 +31,7 @@ import glob import logging import math import os +import platform import re import subprocess import sys @@ -54,6 +55,16 @@ class BenchmarkDriver(object): Optional parameters are for injecting dependencies -- used for testing. """ self.args = args + self.run_env = os.environ.copy() + if hasattr(args, 'libdir') and args.libdir: + # The benchmark binaries should pick up the built swift libraries + # automatically, because their RPATH should point to ../lib/swift + # But it does not harm to additionally set the dynamic library path, + # e.g. as a workaround to rdar://78584073 + if platform.system() == "Darwin": + self.run_env["DYLD_LIBRARY_PATH"] = args.libdir + elif platform.system() == "Linux": + self.run_env["LD_LIBRARY_PATH"] = args.libdir self._subprocess = _subprocess or subprocess self.all_tests = [] self.test_number = {} @@ -66,7 +77,8 @@ class BenchmarkDriver(object): def _invoke(self, cmd): return self._subprocess.check_output( - cmd, stderr=self._subprocess.STDOUT, universal_newlines=True + cmd, stderr=self._subprocess.STDOUT, universal_newlines=True, + env=self.run_env ) @property diff --git a/benchmark/scripts/Template.swift b/benchmark/scripts/Template.swift index ea25b249d4679..3dcc2e03ad7c6 100644 --- a/benchmark/scripts/Template.swift +++ b/benchmark/scripts/Template.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2021 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 diff --git a/benchmark/scripts/run_smoke_bench b/benchmark/scripts/run_smoke_bench index a09c59d003e5b..72d434c33996a 100755 --- a/benchmark/scripts/run_smoke_bench +++ b/benchmark/scripts/run_smoke_bench @@ -48,13 +48,14 @@ VERBOSE = False class DriverArgs(object): """Arguments for BenchmarkDriver.""" - def __init__(self, tests, optimization="O", architecture=None): + def __init__(self, benchmark_dir, architecture, platform, optimization="O"): """Initialize with path to the build-dir and optimization level.""" self.benchmarks = None self.filters = None - self.tests = os.path.join(tests, "bin") + self.tests = os.path.join(benchmark_dir, "bin") self.optimization = optimization self.architecture = architecture + self.libdir = os.path.join(benchmark_dir, "lib", "swift", platform) def log(msg): @@ -165,7 +166,8 @@ def test_opt_levels(args): args.num_samples, args.num_reruns, output_file, - args.arch + args.arch, + args.platform ): changes = True @@ -232,7 +234,7 @@ def merge(results, other_results): def test_performance( opt_level, old_dir, new_dir, threshold, num_samples, num_reruns, - output_file, arch + output_file, arch, platform ): """Detect performance changes in benchmarks. @@ -242,7 +244,8 @@ def test_performance( i, unchanged_length_count = 0, 0 old, new = [ - BenchmarkDriver(DriverArgs(dir, optimization=opt_level, architecture=arch)) + BenchmarkDriver(DriverArgs(dir, architecture=arch, platform=platform, + optimization=opt_level)) for dir in [old_dir, new_dir] ] results = [measure(driver, driver.tests, i) for driver in [old, new]] @@ -266,8 +269,9 @@ def test_performance( unchanged_length_count = 0 log("") + report_title = "Performance ({}): -{}".format(arch, opt_level) return report_results( - "Performance: -" + opt_level, None, None, threshold * 1.4, output_file, *results + report_title, None, None, threshold * 1.4, output_file, *results ) @@ -382,8 +386,10 @@ performance team (@eeckstein). def check_added(args, output_file=None): - old = BenchmarkDriver(DriverArgs(args.oldbuilddir[0], architecture=args.arch)) - new = BenchmarkDriver(DriverArgs(args.newbuilddir[0], architecture=args.arch)) + old = BenchmarkDriver(DriverArgs(args.oldbuilddir[0], architecture=args.arch, + platform=args.platform)) + new = BenchmarkDriver(DriverArgs(args.newbuilddir[0], architecture=args.arch, + platform=args.platform)) added = set(new.tests).difference(set(old.tests)) new.tests = list(added) doctor = BenchmarkDoctor(args, driver=new) diff --git a/benchmark/scripts/test_Benchmark_Driver.py b/benchmark/scripts/test_Benchmark_Driver.py index 7690aacf31bac..0f83eb74ca701 100644 --- a/benchmark/scripts/test_Benchmark_Driver.py +++ b/benchmark/scripts/test_Benchmark_Driver.py @@ -186,6 +186,7 @@ def _check_output( stderr=None, shell=False, universal_newlines=False, + env=None ): return self.record_and_respond(args, stdin, stdout, stderr, shell) @@ -263,6 +264,7 @@ def test_filters_benchmarks_by_pattern(self): self.assertEqual(driver.tests, ["Benchmark3"]) self.assertEqual(driver.all_tests, ["Benchmark1", "Benchmark2", "Benchmark3"]) + @unittest.skip("comparing against localtime() is flaky. rdar://79701124") def test_log_file(self): """When swift-repo is set, log is tied to Git branch and revision.""" self.assertIsNone( diff --git a/benchmark/single-source/BufferFill.swift b/benchmark/single-source/BufferFill.swift new file mode 100644 index 0000000000000..b676cca06354e --- /dev/null +++ b/benchmark/single-source/BufferFill.swift @@ -0,0 +1,166 @@ +//===--- BufferFill.swift -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 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 +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +public let BufferFill = [ + BenchmarkInfo(name: "BufferFillFromSlice", + runFunction: bufferFillFromSliceExecute, + tags: [.validation, .api], + setUpFunction: bufferFillFromSliceSetup, + tearDownFunction: bufferFillFromSliceTeardown), + BenchmarkInfo(name: "RawBufferCopyBytes", + runFunction: rawBufferCopyBytesExecute, + tags: [.validation, .api], + setUpFunction: rawBufferCopyBytesSetup, + tearDownFunction: rawBufferCopyBytesTeardown), + BenchmarkInfo(name: "RawBufferInitializeMemory", + runFunction: rawBufferInitializeMemoryExecute, + tags: [.validation, .api], + setUpFunction: rawBufferInitializeMemorySetup, + tearDownFunction: rawBufferInitializeMemoryTeardown), + BenchmarkInfo(name: "RawBuffer.copyContents", + runFunction: rawBufferCopyContentsExecute, + tags: [.validation, .api], + setUpFunction: rawBufferCopyContentsSetup, + tearDownFunction: rawBufferCopyContentsTeardown), +] + +let c = 100_000 +let a = Array(0.. = .init(start: nil, count: 0) +var r = Int.zero + +public func bufferFillFromSliceSetup() { + assert(b.baseAddress == nil) + b = .allocate(capacity: c) + r = a.indices.randomElement()! +} + +public func bufferFillFromSliceTeardown() { + b.deallocate() + b = .init(start: nil, count: 0) +} + +@inline(never) +public func bufferFillFromSliceExecute(n: Int) { + // Measure performance when filling an UnsafeBuffer from a Slice + // of a Collection that supports `withContiguousStorageIfAvailable` + // See: https://bugs.swift.org/browse/SR-14491 + + for _ in 0...stride, alignment: 1) + r = a.indices.randomElement()! +} + +public func rawBufferInitializeMemoryTeardown() { + rb.deallocate() + rb = .init(start: nil, count: 0) +} + +@inline(never) +public func rawBufferInitializeMemoryExecute(n: Int) { + // Measure performance when initializing an UnsafeRawBuffer + // from a Collection that supports `withContiguousStorageIfAvailable` + // See: https://bugs.swift.org/browse/SR-14982 + + for _ in 0...stride) + let value = offset.load(as: Int.self) + CheckResults(value == a[r]) +} + +var r8: UnsafeRawBufferPointer = .init(start: nil, count: 0) +var b8: UnsafeMutableBufferPointer = .init(start: nil, count: 0) + +public func rawBufferCopyContentsSetup() { + assert(r8.baseAddress == nil) + let count = a.count * MemoryLayout.stride + let rb = UnsafeMutableRawBufferPointer.allocate( + byteCount: count, + alignment: MemoryLayout.alignment) + a.withUnsafeBytes { + rb.copyMemory(from: $0) + } + r8 = UnsafeRawBufferPointer(rb) + assert(b8.baseAddress == nil) + b8 = .allocate(capacity: rb.count) + r = rb.indices.randomElement()! +} + +public func rawBufferCopyContentsTeardown() { + r8.deallocate() + r8 = .init(start: nil, count: 0) + b8.deallocate() + b8 = .init(start: nil, count: 0) +} + +@inline(never) +public func rawBufferCopyContentsExecute(n: Int) { + // Measure performance of copying bytes from an + // `UnsafeRawBufferPointer` to an `UnsafeMutableBufferPointer`. + // See: https://bugs.swift.org/browse/SR-9604 + + for _ in 0..:${_Swift_SANITIZER_FLAGS}>) + endif() +endfunction() + # Usage: # _add_host_variant_c_compile_link_flags(name) function(_add_host_variant_c_compile_link_flags name) - if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_APPLE_PLATFORMS) + if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) set(DEPLOYMENT_VERSION "${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_DEPLOYMENT_VERSION}") endif() @@ -91,16 +123,25 @@ function(_add_host_variant_c_compile_link_flags name) get_target_triple(target target_variant "${SWIFT_HOST_VARIANT_SDK}" "${SWIFT_HOST_VARIANT_ARCH}" MACCATALYST_BUILD_FLAVOR "" DEPLOYMENT_VERSION "${DEPLOYMENT_VERSION}") - target_compile_options(${name} PRIVATE -target;${target}) - target_link_options(${name} PRIVATE -target;${target}) + target_compile_options(${name} PRIVATE $<$:-target;${target}>) + target_link_options(${name} PRIVATE $<$:-target;${target}>) + endif() + + if (CMAKE_Swift_COMPILER) + get_target_triple(target target_variant "${SWIFT_HOST_VARIANT_SDK}" "${SWIFT_HOST_VARIANT_ARCH}" + MACCATALYST_BUILD_FLAVOR "" + DEPLOYMENT_VERSION "${DEPLOYMENT_VERSION}") + target_compile_options(${name} PRIVATE $<$:-target;${target}>) + + _add_host_variant_swift_sanitizer_flags(${name}) endif() set(_sysroot "${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_ARCH_${SWIFT_HOST_VARIANT_ARCH}_PATH}") if(SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_USE_ISYSROOT) - target_compile_options(${name} PRIVATE -isysroot;${_sysroot}) + target_compile_options(${name} PRIVATE $<$:-isysroot;${_sysroot}>) elseif(NOT SWIFT_COMPILER_IS_MSVC_LIKE AND NOT "${_sysroot}" STREQUAL "/") - target_compile_options(${name} PRIVATE --sysroot=${_sysroot}) + target_compile_options(${name} PRIVATE $<$:--sysroot=${_sysroot}>) endif() if(SWIFT_HOST_VARIANT_SDK STREQUAL ANDROID) @@ -108,22 +149,24 @@ function(_add_host_variant_c_compile_link_flags name) # enabled, then fallback to the linker included in the android NDK. if(NOT SWIFT_USE_LINKER STREQUAL "lld") swift_android_tools_path(${SWIFT_HOST_VARIANT_ARCH} tools_path) - target_compile_options(${name} PRIVATE -B${tools_path}) + target_compile_options(${name} PRIVATE $<$:-B${tools_path}>) endif() endif() - if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_APPLE_PLATFORMS) + if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) # We collate -F with the framework path to avoid unwanted deduplication # of options by target_compile_options -- this way no undesired # side effects are introduced should a new search path be added. target_compile_options(${name} PRIVATE + $<$: -arch ${SWIFT_HOST_VARIANT_ARCH} - "-F${SWIFT_SDK_${SWIFT_HOST_VARIANT_ARCH}_PATH}/../../../Developer/Library/Frameworks") + "-F${SWIFT_SDK_${SWIFT_HOST_VARIANT_ARCH}_PATH}/../../../Developer/Library/Frameworks" + >) endif() _compute_lto_flag("${SWIFT_TOOLS_ENABLE_LTO}" _lto_flag_out) if (_lto_flag_out) - target_compile_options(${name} PRIVATE ${_lto_flag_out}) + target_compile_options(${name} PRIVATE $<$:${_lto_flag_out}>) target_link_options(${name} PRIVATE ${_lto_flag_out}) endif() endfunction() @@ -135,9 +178,9 @@ function(_add_host_variant_c_compile_flags target) is_build_type_optimized("${CMAKE_BUILD_TYPE}" optimized) if(optimized) if("${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel") - target_compile_options(${target} PRIVATE -Os) + target_compile_options(${target} PRIVATE $<$:-Os>) else() - target_compile_options(${target} PRIVATE -O2) + target_compile_options(${target} PRIVATE $<$:-O2>) endif() # Omit leaf frame pointers on x86 production builds (optimized, no debug @@ -146,17 +189,17 @@ function(_add_host_variant_c_compile_flags target) if(NOT debug AND NOT LLVM_ENABLE_ASSERTIONS) if(SWIFT_HOST_VARIANT_ARCH MATCHES "i?86") if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) - target_compile_options(${target} PRIVATE -momit-leaf-frame-pointer) + target_compile_options(${target} PRIVATE $<$:-momit-leaf-frame-pointer>) else() - target_compile_options(${target} PRIVATE /Oy) + target_compile_options(${target} PRIVATE $<$:/Oy>) endif() endif() endif() else() if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) - target_compile_options(${target} PRIVATE -O0) + target_compile_options(${target} PRIVATE $<$:-O0>) else() - target_compile_options(${target} PRIVATE /Od) + target_compile_options(${target} PRIVATE $<$:/Od>) endif() endif() @@ -166,12 +209,13 @@ function(_add_host_variant_c_compile_flags target) if(debuginfo) _compute_lto_flag("${SWIFT_TOOLS_ENABLE_LTO}" _lto_flag_out) if(_lto_flag_out) - target_compile_options(${target} PRIVATE -gline-tables-only) + target_compile_options(${target} PRIVATE $<$:-gline-tables-only>) else() target_compile_options(${target} PRIVATE -g) endif() else() - target_compile_options(${target} PRIVATE -g0) + target_compile_options(${target} PRIVATE $<$:-g0>) + target_compile_options(${target} PRIVATE $<$:-gnone>) endif() endif() @@ -179,26 +223,24 @@ function(_add_host_variant_c_compile_flags target) # MSVC/clang-cl don't support -fno-pic or -fms-compatibility-version. if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) target_compile_options(${target} PRIVATE - -fms-compatibility-version=1900 - -fno-pic) + $<$:-fms-compatibility-version=1900 -fno-pic>) endif() target_compile_definitions(${target} PRIVATE - LLVM_ON_WIN32 - _CRT_SECURE_NO_WARNINGS - _CRT_NONSTDC_NO_WARNINGS) + $<$:LLVM_ON_WIN32 _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS>) if(NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") target_compile_definitions(${target} PRIVATE - _CRT_USE_BUILTIN_OFFSETOF) + $<$:_CRT_USE_BUILTIN_OFFSETOF>) endif() # TODO(compnerd) permit building for different families target_compile_definitions(${target} PRIVATE - _CRT_USE_WINAPI_FAMILY_DESKTOP_APP) + $<$:_CRT_USE_WINAPI_FAMILY_DESKTOP_APP>) if(SWIFT_HOST_VARIANT_ARCH MATCHES arm) target_compile_definitions(${target} PRIVATE - _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE) + $<$:_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE>) endif() target_compile_definitions(${target} PRIVATE + $<$: # TODO(compnerd) handle /MT _MD _DLL @@ -207,32 +249,32 @@ function(_add_host_variant_c_compile_flags target) # NOTE: We use over-aligned values for the RefCount side-table # (see revision d913eefcc93f8c80d6d1a6de4ea898a2838d8b6f) # This is required to build with VS2017 15.8+ - _ENABLE_EXTENDED_ALIGNED_STORAGE=1) + _ENABLE_EXTENDED_ALIGNED_STORAGE=1>) # msvcprt's std::function requires RTTI, but we do not want RTTI data. # Emulate /GR-. # TODO(compnerd) when moving up to VS 2017 15.3 and newer, we can disable # RTTI again if(SWIFT_COMPILER_IS_MSVC_LIKE) - target_compile_options(${target} PRIVATE /GR-) + target_compile_options(${target} PRIVATE $<$:/GR->) else() target_compile_options(${target} PRIVATE - -frtti - "SHELL:-Xclang -fno-rtti-data") + $<$:-frtti> + $<$"SHELL:-Xclang -fno-rtti-data">) endif() # NOTE: VS 2017 15.3 introduced this to disable the static components of # RTTI as well. This requires a newer SDK though and we do not have # guarantees on the SDK version currently. target_compile_definitions(${target} PRIVATE - _HAS_STATIC_RTTI=0) + $<$:_HAS_STATIC_RTTI=0>) # NOTE(compnerd) workaround LLVM invoking `add_definitions(-D_DEBUG)` which # causes failures for the runtime library when cross-compiling due to # undefined symbols from the standard library. if(NOT CMAKE_BUILD_TYPE STREQUAL Debug) target_compile_options(${target} PRIVATE - -U_DEBUG) + $<$:-U_DEBUG>) endif() endif() @@ -246,40 +288,39 @@ function(_add_host_variant_c_compile_flags target) # the build. if(CMAKE_C_COMPILER_ID MATCHES Clang AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0.0) - target_compile_options(${target} PRIVATE -mcx16) + target_compile_options(${target} PRIVATE $<$:-mcx16>) endif() endif() endif() if(LLVM_ENABLE_ASSERTIONS) - target_compile_options(${target} PRIVATE -UNDEBUG) + target_compile_options(${target} PRIVATE $<$:-UNDEBUG>) else() - target_compile_definitions(${target} PRIVATE -DNDEBUG) + target_compile_definitions(${target} PRIVATE $<$:NDEBUG>) endif() if(SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS) target_compile_definitions(${target} PRIVATE - SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS) + $<$:SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS>) endif() if(SWIFT_ANALYZE_CODE_COVERAGE) target_compile_options(${target} PRIVATE - -fprofile-instr-generate - -fcoverage-mapping) + $<$:-fprofile-instr-generate -fcoverage-mapping>) endif() if((SWIFT_HOST_VARIANT_ARCH STREQUAL armv7 OR SWIFT_HOST_VARIANT_ARCH STREQUAL aarch64) AND (SWIFT_HOST_VARIANT_SDK STREQUAL LINUX OR SWIFT_HOST_VARIANT_SDK STREQUAL ANDROID)) - target_compile_options(${target} PRIVATE -funwind-tables) + target_compile_options(${target} PRIVATE $<$:-funwind-tables>) endif() if(SWIFT_HOST_VARIANT_SDK STREQUAL "LINUX") if(SWIFT_HOST_VARIANT_ARCH STREQUAL x86_64) # this is the minimum architecture that supports 16 byte CAS, which is # necessary to avoid a dependency to libatomic - target_compile_options(${target} PRIVATE -march=core2) + target_compile_options(${target} PRIVATE $<$:-march=core2>) endif() endif() @@ -289,8 +330,7 @@ function(_add_host_variant_c_compile_flags target) if("${SWIFT_HOST_VARIANT_ARCH}" MATCHES "armv6|armv7|i686" AND NOT (SWIFT_HOST_VARIANT_SDK STREQUAL ANDROID AND SWIFT_ANDROID_API_LEVEL LESS 24)) target_compile_definitions(${target} PRIVATE - _LARGEFILE_SOURCE - _FILE_OFFSET_BITS=64) + $<$:_LARGEFILE_SOURCE _FILE_OFFSET_BITS=64>) endif() endfunction() @@ -365,7 +405,11 @@ function(_add_host_variant_link_flags target) if(NOT SWIFT_COMPILER_IS_MSVC_LIKE) if(SWIFT_USE_LINKER) target_link_options(${target} PRIVATE - -fuse-ld=${SWIFT_USE_LINKER}$<$:.exe>) + $<$:-fuse-ld=${SWIFT_USE_LINKER}$<$:.exe>>) + if (CMAKE_Swift_COMPILER) + target_link_options(${target} PRIVATE + $<$:-use-ld=${SWIFT_USE_LINKER}$<$:.exe>>) + endif() endif() endif() @@ -391,6 +435,8 @@ endfunction() # add_swift_host_library(name # [SHARED] # [STATIC] +# [OBJECT] +# [PURE_SWIFT] # [LLVM_LINK_COMPONENTS comp1 ...] # source1 [source2 source3 ...]) # @@ -403,15 +449,25 @@ endfunction() # STATIC # Build a static library. # +# OBJECT +# Build an object library +# # LLVM_LINK_COMPONENTS # LLVM components this library depends on. # +# PURE_SWIFT +# This has two effects if set: we do not use llvm_update_compile_flags to +# generate cflags/etc and we leave the linking mode of the library as swift. +# # source1 ... # Sources to add into this library. function(add_swift_host_library name) set(options SHARED - STATIC) + STATIC + OBJECT + PURE_SWIFT + HAS_LIBSWIFT) set(single_parameter_options) set(multiple_parameter_options LLVM_LINK_COMPONENTS) @@ -425,8 +481,15 @@ function(add_swift_host_library name) translate_flags(ASHL "${options}") - if(NOT ASHL_SHARED AND NOT ASHL_STATIC) - message(FATAL_ERROR "Either SHARED or STATIC must be specified") + if(NOT ASHL_SHARED AND NOT ASHL_STATIC AND NOT ASHL_OBJECT) + message(FATAL_ERROR "One of SHARED/STATIC/OBJECT must be specified") + endif() + + # Using `support` llvm component ends up adding `-Xlinker /path/to/lib/libLLVMDemangle.a` + # to `LINK_FLAGS` but `libLLVMDemangle.a` is not added as an input to the linking ninja statement. + # As a workaround, include `demangle` component whenever `support` is mentioned. + if("support" IN_LIST ASHL_LLVM_LINK_COMPONENTS) + list(APPEND ASHL_LLVM_LINK_COMPONENTS "demangle") endif() if(XCODE) @@ -451,17 +514,37 @@ function(add_swift_host_library name) set(libkind SHARED) elseif(ASHL_STATIC) set(libkind STATIC) + elseif(ASHL_OBJECT) + set(libkind OBJECT) endif() add_library(${name} ${libkind} ${ASHL_SOURCES}) - add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) - llvm_update_compile_flags(${name}) + + if (ASHL_HAS_LIBSWIFT AND SWIFT_TOOLS_ENABLE_LIBSWIFT) + # Workaround for a linker crash related to autolinking: rdar://77839981 + set_property(TARGET ${name} APPEND_STRING PROPERTY + LINK_FLAGS " -lobjc ") + endif() + + # Respect LLVM_COMMON_DEPENDS if it is set. + # + # LLVM_COMMON_DEPENDS if a global variable set in ./lib that provides targets + # such as swift-syntax or tblgen that all LLVM/Swift based tools depend on. If + # we don't have it defined, then do not add the dependency since some parts of + # swift host tools do not interact with LLVM/Swift tools and do not define + # LLVM_COMMON_DEPENDS. + if (LLVM_COMMON_DEPENDS) + add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) + endif() + if (NOT ASHL_PURE_SWIFT) + llvm_update_compile_flags(${name}) + endif() swift_common_llvm_config(${name} ${ASHL_LLVM_LINK_COMPONENTS}) set_output_directory(${name} BINARY_DIR ${SWIFT_RUNTIME_OUTPUT_INTDIR} LIBRARY_DIR ${SWIFT_LIBRARY_OUTPUT_INTDIR}) - if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_APPLE_PLATFORMS) + if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) set_target_properties(${name} PROPERTIES INSTALL_NAME_DIR "@rpath") elseif(SWIFT_HOST_VARIANT_SDK STREQUAL LINUX) @@ -499,29 +582,72 @@ function(add_swift_host_library name) if(NOT ${CMAKE_C_COMPILER_ID} STREQUAL MSVC) swift_windows_get_sdk_vfs_overlay(ASHL_VFS_OVERLAY) target_compile_options(${name} PRIVATE - "SHELL:-Xclang -ivfsoverlay -Xclang ${ASHL_VFS_OVERLAY}") + $<$:"SHELL:-Xclang -ivfsoverlay -Xclang ${ASHL_VFS_OVERLAY}">) # MSVC doesn't support -Xclang. We don't need to manually specify # the dependent libraries as `cl` does so. target_compile_options(${name} PRIVATE - "SHELL:-Xclang --dependent-lib=oldnames" + $<$:"SHELL:-Xclang --dependent-lib=oldnames"> # TODO(compnerd) handle /MT, /MTd - "SHELL:-Xclang --dependent-lib=msvcrt$<$:d>") + $<$:"SHELL:-Xclang --dependent-lib=msvcrt$<$:d>"> + ) endif() set_target_properties(${name} PROPERTIES NO_SONAME YES) endif() - if(${SWIFT_HOST_VARIANT_SDK} IN_LIST SWIFT_APPLE_PLATFORMS) + # Always link as CXX even if we have swift content unless we only contain + # swift content signaled via us being marked "PURE_SWIFT". + if (NOT ASHL_PURE_SWIFT) + set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX) + endif() + + if(${SWIFT_HOST_VARIANT_SDK} IN_LIST SWIFT_DARWIN_PLATFORMS) target_link_options(${name} PRIVATE "LINKER:-compatibility_version,1") if(SWIFT_COMPILER_VERSION) target_link_options(${name} PRIVATE "LINKER:-current_version,${SWIFT_COMPILER_VERSION}") endif() + + # If we found a swift compiler and are going to use swift code in swift + # host side tools but link with clang, add the appropriate -L paths so we + # find all of the necessary swift libraries on Darwin. + if (NOT ASHL_PURE_SWIFT) + if (CMAKE_Swift_COMPILER) + # Add in the toolchain directory so we can grab compatibility libraries + get_filename_component(TOOLCHAIN_BIN_DIR ${CMAKE_Swift_COMPILER} DIRECTORY) + get_filename_component(TOOLCHAIN_LIB_DIR "${TOOLCHAIN_BIN_DIR}/../lib/swift/macosx" ABSOLUTE) + target_link_directories(${name} PUBLIC ${TOOLCHAIN_LIB_DIR}) + + # Add in the SDK directory for the host platform. + # + # NOTE: We do this /after/ target_link_directorying TOOLCHAIN_LIB_DIR to + # ensure that we first find libraries from the toolchain, rather than + # from the SDK. The reason why this is important is that when we perform + # a stage2 build, this path is into the stage1 build. This is not a pure + # SDK and also contains compatibility libraries. We need to make sure + # that the compiler sees the actual toolchain's compatibility libraries + # first before the just built compability libraries or build errors occur. + target_link_directories(${name} PRIVATE + ${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_ARCH_${SWIFT_HOST_VARIANT_ARCH}_PATH}/usr/lib/swift) + endif() + endif() + + # For now turn off on Darwin swift targets, debug info if we are compiling a static + # library and set up an rpath so that if someone works around this by using + # shared libraries that in the short term we can find shared libraries. + if (ASHL_STATIC) + target_compile_options(${name} PRIVATE $<$:-gnone>) + endif() endif() + # If we are compiling in release or release with deb info, compile swift code + # with -cross-module-optimization enabled. + target_compile_options(${name} PRIVATE + $<$,$,$>>:-cross-module-optimization>) + add_dependencies(dev ${name}) if(NOT LLVM_INSTALL_TOOLCHAIN_ONLY) swift_install_in_component(TARGETS ${name} @@ -538,6 +664,121 @@ function(add_swift_host_library name) endif() endfunction() +# Add a module of libswift +# +# Creates a target to compile a module which is part of libswift. +# Adds the module name to the global property "libswift_modules". +# +# This is a temporary workaround until it's possible to compile libswift with +# cmake's builtin swift support. +function(add_libswift_module module) + cmake_parse_arguments(ALSM + "" + "DEPENDS" + "" + ${ARGN}) + set(sources ${ALSM_UNPARSED_ARGUMENTS}) + list(TRANSFORM sources PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") + + set(target_name "LibSwift${module}") + + # Add a target which depends on the actual compilation target, which + # will be created in add_libswift. + # This target is mainly used to add properties, like the list of source files. + add_custom_target( + ${target_name} + SOURCES ${sources} + COMMENT "libswift module ${module}") + + set_property(TARGET ${target_name} PROPERTY "module_name" ${module}) + set_property(TARGET ${target_name} PROPERTY "module_depends" ${ALSM_DEPENDS}) + + get_property(modules GLOBAL PROPERTY "libswift_modules") + set_property(GLOBAL PROPERTY "libswift_modules" ${modules} ${module}) +endfunction() + +# Add source files to a libswift module. +# +# This is a temporary workaround until it's possible to compile libswift with +# cmake's builtin swift support. +function(libswift_sources module) + cmake_parse_arguments(LSS + "" + "" + "" + ${ARGN}) + set(sources ${LSS_UNPARSED_ARGUMENTS}) + list(TRANSFORM sources PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") + + set(target_name "LibSwift${module}") + set_property(TARGET "LibSwift${module}" APPEND PROPERTY SOURCES ${sources}) +endfunction() + +# Add the libswift library. +# +# Adds targets to compile all modules of libswift and a target for the +# libswift library itself. +# +# This is a temporary workaround until it's possible to compile libswift with +# cmake's builtin swift support. +function(add_libswift name) + if(CMAKE_BUILD_TYPE STREQUAL Debug) + set(libswift_compile_options "-g") + else() + set(libswift_compile_options "-O" "-cross-module-optimization") + endif() + + set(build_dir ${CMAKE_CURRENT_BINARY_DIR}) + + if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) + set(deployment_version "${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_DEPLOYMENT_VERSION}") + endif() + get_versioned_target_triple(target ${SWIFT_HOST_VARIANT_SDK} + ${SWIFT_HOST_VARIANT_ARCH} "${deployment_version}") + + get_property(modules GLOBAL PROPERTY "libswift_modules") + foreach(module ${modules}) + + set(module_target "LibSwift${module}") + get_target_property(module ${module_target} "module_name") + get_target_property(sources ${module_target} SOURCES) + get_target_property(dependencies ${module_target} "module_depends") + if(dependencies) + list(TRANSFORM dependencies PREPEND "LibSwift") + else() + set(dependencies "") + endif() + + set(module_obj_file "${build_dir}/${module}.o") + set(module_file "${build_dir}/${module}.swiftmodule") + set_property(TARGET ${module_target} PROPERTY "module_file" "${module_file}") + + set(all_obj_files ${all_obj_files} ${module_obj_file}) + + # Compile the libswift module into an object file + add_custom_command_target(dep_target OUTPUT ${module_obj_file} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${sources} ${dependencies} + COMMAND ${CMAKE_Swift_COMPILER} "-c" "-o" ${module_obj_file} + "-sdk" "${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_ARCH_${SWIFT_HOST_VARIANT_ARCH}_PATH}" + "-target" ${target} + "-module-name" ${module} "-emit-module" + "-emit-module-path" "${build_dir}/${module}.swiftmodule" + "-parse-as-library" ${sources} + "-wmo" ${libswift_compile_options} + "-I" "${CMAKE_SOURCE_DIR}/include/swift" + "-I" "${build_dir}" + COMMENT "Building libswift module ${module}") + + add_dependencies(${module_target} ${dep_target}) + + endforeach() + + # Create a static libswift library containing all module object files. + add_library(${name} STATIC ${all_obj_files}) + set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX) +endfunction() + macro(add_swift_tool_subdirectory name) add_llvm_subdirectory(SWIFT TOOL ${name}) endmacro() @@ -547,7 +788,7 @@ macro(add_swift_lib_subdirectory name) endmacro() function(add_swift_host_tool executable) - set(options) + set(options HAS_LIBSWIFT) set(single_parameter_options SWIFT_COMPONENT) set(multiple_parameter_options LLVM_LINK_COMPONENTS) @@ -560,14 +801,32 @@ function(add_swift_host_tool executable) precondition(ASHT_SWIFT_COMPONENT MESSAGE "Swift Component is required to add a host tool") + # Using `support` llvm component ends up adding `-Xlinker /path/to/lib/libLLVMDemangle.a` + # to `LINK_FLAGS` but `libLLVMDemangle.a` is not added as an input to the linking ninja statement. + # As a workaround, include `demangle` component whenever `support` is mentioned. + if("support" IN_LIST ASHT_LLVM_LINK_COMPONENTS) + list(APPEND ASHT_LLVM_LINK_COMPONENTS "demangle") + endif() add_executable(${executable} ${ASHT_UNPARSED_ARGUMENTS}) _add_host_variant_c_compile_flags(${executable}) _add_host_variant_link_flags(${executable}) _add_host_variant_c_compile_link_flags(${executable}) - target_link_directories(${executable} PRIVATE - ${SWIFTLIB_DIR}/${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}) - add_dependencies(${executable} ${LLVM_COMMON_DEPENDS}) + + # Force executables linker language to be CXX so that we do not link using the + # host toolchain swiftc. + set_target_properties(${executable} PROPERTIES LINKER_LANGUAGE CXX) + + # Respect LLVM_COMMON_DEPENDS if it is set. + # + # LLVM_COMMON_DEPENDS if a global variable set in ./lib that provides targets + # such as swift-syntax or tblgen that all LLVM/Swift based tools depend on. If + # we don't have it defined, then do not add the dependency since some parts of + # swift host tools do not interact with LLVM/Swift tools and do not define + # LLVM_COMMON_DEPENDS. + if (LLVM_COMMON_DEPENDS) + add_dependencies(${executable} ${LLVM_COMMON_DEPENDS}) + endif() set_target_properties(${executable} PROPERTIES FOLDER "Swift executables") @@ -575,10 +834,80 @@ function(add_swift_host_tool executable) set_target_properties(${executable} PROPERTIES JOB_POOL_LINK swift_link_job_pool) endif() - if(${SWIFT_HOST_VARIANT_SDK} IN_LIST SWIFT_APPLE_PLATFORMS) + if(${SWIFT_HOST_VARIANT_SDK} IN_LIST SWIFT_DARWIN_PLATFORMS) + # If we found a swift compiler and are going to use swift code in swift + # host side tools but link with clang, add the appropriate -L paths so we + # find all of the necessary swift libraries on Darwin. + if (CMAKE_Swift_COMPILER) + # Add in the toolchain directory so we can grab compatibility libraries + get_filename_component(TOOLCHAIN_BIN_DIR ${CMAKE_Swift_COMPILER} DIRECTORY) + get_filename_component(TOOLCHAIN_LIB_DIR "${TOOLCHAIN_BIN_DIR}/../lib/swift/macosx" ABSOLUTE) + target_link_directories(${executable} PUBLIC ${TOOLCHAIN_LIB_DIR}) + + # Add in the SDK directory for the host platform and add an rpath. + # + # NOTE: We do this /after/ target_link_directorying TOOLCHAIN_LIB_DIR to + # ensure that we first find libraries from the toolchain, rather than from + # the SDK. The reason why this is important is that when we perform a + # stage2 build, this path is into the stage1 build. This is not a pure SDK + # and also contains compatibility libraries. We need to make sure that the + # compiler sees the actual toolchain's compatibility libraries first + # before the just built compability libraries or build errors occur. + target_link_directories(${executable} PRIVATE + ${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_ARCH_${SWIFT_HOST_VARIANT_ARCH}_PATH}/usr/lib/swift) + + if (ASHT_HAS_LIBSWIFT AND SWIFT_TOOLS_ENABLE_LIBSWIFT) + # Workaround to make lldb happy: we have to explicitly add all libswift modules + # to the linker command line. + set(libswift_ast_path_flags "-Wl") + get_property(modules GLOBAL PROPERTY "libswift_modules") + foreach(module ${modules}) + get_target_property(module_file "LibSwift${module}" "module_file") + string(APPEND libswift_ast_path_flags ",-add_ast_path,${module_file}") + endforeach() + + set_property(TARGET ${executable} APPEND_STRING PROPERTY + LINK_FLAGS ${libswift_ast_path_flags}) + + # Workaround for a linker crash related to autolinking: rdar://77839981 + set_property(TARGET ${executable} APPEND_STRING PROPERTY + LINK_FLAGS " -lobjc ") + endif() + endif() + + # Lists of rpaths that we are going to add to our executables. + # + # Please add each rpath separately below to the list, explaining why you are + # adding it. + set(RPATH_LIST) + + # We also want to be able to find libraries from the base toolchain + # directory. This is so swiftc can rely on its own host side dylibs that may + # contain swift content. + list(APPEND RPATH_LIST "@executable_path/../lib") + + # Also include the abi stable system stdlib in our rpath. + list(APPEND RPATH_LIST "/usr/lib/swift") + set_target_properties(${executable} PROPERTIES BUILD_WITH_INSTALL_RPATH YES - INSTALL_RPATH "@executable_path/../lib/swift/${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}") + INSTALL_RPATH "${RPATH_LIST}") + + elseif(SWIFT_HOST_VARIANT_SDK STREQUAL "LINUX") + if (ASHT_HAS_LIBSWIFT AND SWIFT_TOOLS_ENABLE_LIBSWIFT) + # At build time and and run time, link against the swift libraries in the + # installed host toolchain. + get_filename_component(swift_bin_dir ${CMAKE_Swift_COMPILER} DIRECTORY) + get_filename_component(swift_dir ${swift_bin_dir} DIRECTORY) + set(host_lib_dir "${swift_dir}/lib/swift/linux") + + target_link_libraries(${executable} PRIVATE "swiftCore") + + target_link_directories(${executable} PRIVATE ${host_lib_dir}) + set_target_properties(${executable} PROPERTIES + BUILD_WITH_INSTALL_RPATH YES + INSTALL_RPATH "${host_lib_dir}") + endif() endif() llvm_update_compile_flags(${executable}) @@ -597,9 +926,10 @@ function(add_swift_host_tool executable) # MSVC doesn't support -Xclang. We don't need to manually specify # the dependent libraries as `cl` does so. target_compile_options(${executable} PRIVATE - "SHELL:-Xclang --dependent-lib=oldnames" + $<$:"SHELL:-Xclang --dependent-lib=oldnames"> # TODO(compnerd) handle /MT, /MTd - "SHELL:-Xclang --dependent-lib=msvcrt$<$:d>") + $<$:"SHELL:-Xclang --dependent-lib=msvcrt$<$:d>"> + ) endif() endif() @@ -627,7 +957,9 @@ function(add_swift_fuzzer_host_tool executable) # Then make sure that we pass the -fsanitize=fuzzer flag both on the cflags # and cxx flags line. - target_compile_options(${executable} PRIVATE "-fsanitize=fuzzer") + target_compile_options(${executable} PRIVATE + $<$:-fsanitize=fuzzer> + $<$:-sanitize=fuzzer>) target_link_libraries(${executable} PRIVATE "-fsanitize=fuzzer") endfunction() @@ -638,6 +970,7 @@ endmacro() # Declare that files in this library are built with LLVM's support # libraries available. -macro(set_swift_llvm_is_available) - add_compile_options(-DSWIFT_LLVM_SUPPORT_IS_AVAILABLE) -endmacro() +function(set_swift_llvm_is_available name) + target_compile_definitions(${name} PRIVATE + $<$:SWIFT_LLVM_SUPPORT_IS_AVAILABLE>) +endfunction() diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 9c9047cb3d742..ff44121a69f8c 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -21,28 +21,12 @@ function(add_swift_unittest test_dirname) set_property(TARGET "${test_dirname}" APPEND_STRING PROPERTY LINK_FLAGS " ${_lto_flag_out} ") endif() - if(SWIFT_BUILT_STANDALONE AND NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".") - # Replace target references with full paths, so that we use LLVM's - # build configuration rather than Swift's. - get_target_property(libnames ${test_dirname} LINK_LIBRARIES) - - set(new_libnames) - foreach(dep ${libnames}) - if("${dep}" MATCHES "^(LLVM|Clang|gtest)" AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") - list(APPEND new_libnames "${LLVM_LIBRARY_OUTPUT_INTDIR}/lib${dep}.a") - else() - list(APPEND new_libnames "${dep}") - endif() - endforeach() - - set_property(TARGET ${test_dirname} PROPERTY LINK_LIBRARIES ${new_libnames}) - swift_common_llvm_config(${test_dirname} support) - endif() - if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - # Add an @rpath to the swift library directory. + # Add an @rpath to the swift library directory + # and one to the OS dylibs we require but + # are not building ourselves (e.g Foundation overlay) set_target_properties(${test_dirname} PROPERTIES - BUILD_RPATH ${SWIFT_LIBRARY_OUTPUT_INTDIR}/swift/macosx) + BUILD_RPATH "${SWIFT_LIBRARY_OUTPUT_INTDIR}/swift/macosx;${SWIFT_DARWIN_STDLIB_INSTALL_NAME_DIR}") # Force all the swift libraries to be found via rpath. add_custom_command(TARGET "${test_dirname}" POST_BUILD COMMAND "${SWIFT_SOURCE_DIR}/utils/swift-rpathize.py" @@ -87,4 +71,3 @@ function(add_swift_unittest test_dirname) endif() endfunction() - diff --git a/cmake/modules/DarwinSDKs.cmake b/cmake/modules/DarwinSDKs.cmake index 98b79e1f2795c..b50f542c3b7db 100644 --- a/cmake/modules/DarwinSDKs.cmake +++ b/cmake/modules/DarwinSDKs.cmake @@ -14,7 +14,7 @@ set(SUPPORTED_TVOS_ARCHS "arm64") set(SUPPORTED_TVOS_SIMULATOR_ARCHS "x86_64;arm64") set(SUPPORTED_WATCHOS_ARCHS "armv7k;arm64_32") set(SUPPORTED_WATCHOS_SIMULATOR_ARCHS "i386;x86_64;arm64") -set(SUPPORTED_OSX_ARCHS "x86_64;arm64;arm64e") +set(SUPPORTED_OSX_ARCHS "x86_64;arm64") is_sdk_requested(OSX swift_build_osx) if(swift_build_osx) @@ -27,7 +27,7 @@ if(swift_build_osx) endif() is_sdk_requested(FREESTANDING swift_build_freestanding) -if(swift_build_freestanding) +if(swift_build_freestanding AND (SWIFT_FREESTANDING_FLAVOR STREQUAL "apple")) set(SWIFT_FREESTANDING_SDK "" CACHE STRING "Which SDK to use when building the FREESTANDING stdlib") set(SWIFT_FREESTANDING_TRIPLE_NAME "" CACHE STRING @@ -45,6 +45,8 @@ if(swift_build_freestanding) configure_target_variant(FREESTANDING-RA "FREESTANDING Release+Asserts" FREESTANDING RA "Release+Asserts") configure_target_variant(FREESTANDING-R "FREESTANDING Release" FREESTANDING R "Release") configure_target_variant(FREESTANDING-S "FREESTANDING MinSizeRelease" FREESTANDING S "MinSizeRelease") + + set(SWIFT_FREESTANDING_TEST_DEPENDENCIES "Darwin") endif() # Compatible cross-compile SDKS for Darwin OSes: IOS, IOS_SIMULATOR, TVOS, diff --git a/cmake/modules/Libdispatch.cmake b/cmake/modules/Libdispatch.cmake index 3b07ac14f5b7e..8791ea888ecfa 100644 --- a/cmake/modules/Libdispatch.cmake +++ b/cmake/modules/Libdispatch.cmake @@ -46,8 +46,8 @@ endif() # Build any target libdispatch if needed. foreach(sdk ${SWIFT_SDKS}) - # Apple targets have libdispatch available, do not build it. - if(NOT "${sdk}" IN_LIST SWIFT_APPLE_PLATFORMS) + # Darwin targets have libdispatch available, do not build it. + if(NOT "${sdk}" IN_LIST SWIFT_DARWIN_PLATFORMS) list(APPEND DISPATCH_SDKS "${sdk}") endif() endforeach() @@ -69,6 +69,11 @@ foreach(sdk ${DISPATCH_SDKS}) foreach(arch ${ARCHS}) set(LIBDISPATCH_VARIANT_NAME "libdispatch-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}") + if(sdk MATCHES WINDOWS) + set(SWIFT_LIBDISPATCH_COMPILER_TRIPLE_CMAKE_ARGS -DCMAKE_C_COMPILER_TARGET=${SWIFT_SDK_WINDOWS_ARCH_${arch}_TRIPLE};-DCMAKE_CXX_COMPILER_TARGET=${SWIFT_SDK_WINDOWS_ARCH_${arch}_TRIPLE}) + endif() + + if(NOT sdk STREQUAL ANDROID) set(SWIFT_LIBDISPATCH_SYSTEM_PROCESSOR -DCMAKE_SYSTEM_PROCESSOR=${arch}) endif() @@ -80,12 +85,14 @@ foreach(sdk ${DISPATCH_SDKS}) -DCMAKE_AR=${CMAKE_AR} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} ${SWIFT_LIBDISPATCH_COMPILER_CMAKE_ARGS} + ${SWIFT_LIBDISPATCH_COMPILER_TRIPLE_CMAKE_ARGS} -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_INSTALL_PREFIX= -DCMAKE_LINKER=${CMAKE_LINKER} + -DCMAKE_MT=${CMAKE_MT} -DCMAKE_RANLIB=${CMAKE_RANLIB} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_SYSTEM_NAME=${SWIFT_SDK_${sdk}_NAME} @@ -109,6 +116,14 @@ foreach(sdk ${DISPATCH_SDKS}) ${CMAKE_COMMAND} -E copy /${LIBDISPATCH_RUNTIME_DIR}/${SWIFT_SDK_${sdk}_SHARED_LIBRARY_PREFIX}BlocksRuntime${SWIFT_SDK_${sdk}_SHARED_LIBRARY_SUFFIX} ${SWIFTLIB_DIR}/${SWIFT_SDK_${sdk}_LIB_SUBDIR}/${arch}/${SWIFT_SDK_${sdk}_SHARED_LIBRARY_PREFIX}BlocksRuntime${SWIFT_SDK_${sdk}_SHARED_LIBRARY_SUFFIX} + COMMAND + ${CMAKE_COMMAND} -E copy + /${LIBDISPATCH_RUNTIME_DIR}/${SWIFT_SDK_${sdk}_SHARED_LIBRARY_PREFIX}dispatch${SWIFT_SDK_${sdk}_SHARED_LIBRARY_SUFFIX} + ${SWIFTLIB_DIR}/${SWIFT_SDK_${sdk}_LIB_SUBDIR}/${SWIFT_SDK_${sdk}_SHARED_LIBRARY_PREFIX}dispatch${SWIFT_SDK_${sdk}_SHARED_LIBRARY_SUFFIX} + COMMAND + ${CMAKE_COMMAND} -E copy + /${LIBDISPATCH_RUNTIME_DIR}/${SWIFT_SDK_${sdk}_SHARED_LIBRARY_PREFIX}BlocksRuntime${SWIFT_SDK_${sdk}_SHARED_LIBRARY_SUFFIX} + ${SWIFTLIB_DIR}/${SWIFT_SDK_${sdk}_LIB_SUBDIR}/${SWIFT_SDK_${sdk}_SHARED_LIBRARY_PREFIX}BlocksRuntime${SWIFT_SDK_${sdk}_SHARED_LIBRARY_SUFFIX} STEP_TARGETS install BUILD_BYPRODUCTS diff --git a/cmake/modules/StandaloneOverlay.cmake b/cmake/modules/StandaloneOverlay.cmake index f84b10d6727af..140dc2c985e02 100644 --- a/cmake/modules/StandaloneOverlay.cmake +++ b/cmake/modules/StandaloneOverlay.cmake @@ -90,7 +90,7 @@ set(CMAKE_INSTALL_PREFIX "${SWIFT_DEST_ROOT}${TOOLCHAIN_DIR}/usr") -set(SWIFT_APPLE_PLATFORMS +set(SWIFT_DARWIN_PLATFORMS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR WATCHOS WATCHOS_SIMULATOR) # Flags used to indicate we are building a standalone overlay. diff --git a/cmake/modules/SwiftComponents.cmake b/cmake/modules/SwiftComponents.cmake index 401cff7a6ef86..d2b7e27b04d66 100644 --- a/cmake/modules/SwiftComponents.cmake +++ b/cmake/modules/SwiftComponents.cmake @@ -46,6 +46,7 @@ # The set of "defined" swift components are as follows: # # * autolink-driver -- the Swift driver support tools +# * back-deployment -- Swift back-deployment libraries # * compiler -- the Swift compiler and (on supported platforms) the REPL. # * clang-builtin-headers -- install a copy of Clang builtin headers under # 'lib/swift/clang'. This is useful when Swift compiler is installed in @@ -66,7 +67,7 @@ # * toolchain-dev-tools -- install development tools useful in a shared toolchain # * dev -- headers and libraries required to use Swift compiler as a library. set(_SWIFT_DEFINED_COMPONENTS - "autolink-driver;compiler;clang-builtin-headers;clang-resource-dir-symlink;clang-builtin-headers-in-clang-resource-dir;stdlib;stdlib-experimental;sdk-overlay;parser-lib;editor-integration;tools;testsuite-tools;toolchain-tools;toolchain-dev-tools;dev;license;sourcekit-xpc-service;sourcekit-inproc;swift-remote-mirror;swift-remote-mirror-headers") + "autolink-driver;back-deployment;compiler;clang-builtin-headers;clang-resource-dir-symlink;clang-builtin-headers-in-clang-resource-dir;stdlib;stdlib-experimental;sdk-overlay;parser-lib;editor-integration;tools;testsuite-tools;toolchain-tools;toolchain-dev-tools;dev;license;sourcekit-xpc-service;sourcekit-inproc;swift-remote-mirror;swift-remote-mirror-headers") # The default install components include all of the defined components, except # for the following exceptions. @@ -85,6 +86,8 @@ else() list(REMOVE_ITEM _SWIFT_DEFAULT_COMPONENTS "sourcekit-xpc-service") endif() list(REMOVE_ITEM _SWIFT_DEFAULT_COMPONENTS "stdlib-experimental") +# back-deployment libraries are opt-in +list(REMOVE_ITEM _SWIFT_DEFAULT_COMPONENTS "back-deployment") macro(swift_configure_components) # Set the SWIFT_INSTALL_COMPONENTS variable to the default value if it is not passed in via -D diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 8a32c12abb126..cfa152ff851ea 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -24,7 +24,7 @@ function(_report_sdk prefix) message(STATUS " ${CMAKE_BUILD_TYPE} VC++ CRT: MD") endif() endif() - if(prefix IN_LIST SWIFT_APPLE_PLATFORMS) + if(prefix IN_LIST SWIFT_DARWIN_PLATFORMS) message(STATUS " Version: ${SWIFT_SDK_${prefix}_VERSION}") message(STATUS " Build number: ${SWIFT_SDK_${prefix}_BUILD_NUMBER}") message(STATUS " Deployment version: ${SWIFT_SDK_${prefix}_DEPLOYMENT_VERSION}") @@ -60,7 +60,7 @@ function(_report_sdk prefix) endforeach() endif() - if(NOT prefix IN_LIST SWIFT_APPLE_PLATFORMS) + if(NOT prefix IN_LIST SWIFT_DARWIN_PLATFORMS) foreach(arch ${SWIFT_SDK_${prefix}_ARCHITECTURES}) message(STATUS " ${arch} libc header path: ${SWIFT_SDK_${prefix}_ARCH_${arch}_LIBC_INCLUDE_DIRECTORY}") message(STATUS " ${arch} libc architecture specific header path: ${SWIFT_SDK_${prefix}_ARCH_${arch}_LIBC_ARCHITECTURE_INCLUDE_DIRECTORY}") diff --git a/cmake/modules/SwiftHandleGybSources.cmake b/cmake/modules/SwiftHandleGybSources.cmake index e74489f56989f..3a1265bdebd17 100644 --- a/cmake/modules/SwiftHandleGybSources.cmake +++ b/cmake/modules/SwiftHandleGybSources.cmake @@ -77,7 +77,8 @@ endfunction() # handle_gyb_sources( # dependency_out_var_name # sources_var_name -# arch) +# [ARCH arch] +# [DEPENDS [depends ...]]) # # Replace, in ${sources_var_name}, the given .gyb-suffixed sources with # their un-suffixed intermediate files, which will be generated by processing @@ -93,11 +94,21 @@ endfunction() # false, the files are architecture-independent and will be emitted # into ${CMAKE_CURRENT_BINARY_DIR} instead of an architecture-specific # destination; this is useful for generated include files. -function(handle_gyb_sources dependency_out_var_name sources_var_name arch) +# +# depends +# Additional file dependencies beyond the standard dependencies that all gyb +# invocations get. +function(handle_gyb_sources dependency_out_var_name sources_var_name) + set(options) + set(single_value_args ARCH) + set(multi_value_args DEPENDS) + cmake_parse_arguments(GYB + "${options}" "${single_value_args}" "${multi_value_args}" ${ARGN}) + set(extra_gyb_flags "") - if (arch) + if (GYB_ARCH) set_if_arch_bitness(ptr_size - ARCH "${arch}" + ARCH "${GYB_ARCH}" CASE_32_BIT "4" CASE_64_BIT "8") set(extra_gyb_flags "-DCMAKE_SIZEOF_VOID_P=${ptr_size}") @@ -108,11 +119,13 @@ function(handle_gyb_sources dependency_out_var_name sources_var_name arch) set(gyb_extra_sources "${SWIFT_SOURCE_DIR}/utils/GYBUnicodeDataUtils.py" "${SWIFT_SOURCE_DIR}/utils/SwiftIntTypes.py" + "${SWIFT_SOURCE_DIR}/utils/SwiftFloatingPointTypes.py" "${SWIFT_SOURCE_DIR}/utils/UnicodeData/GraphemeBreakProperty.txt" "${SWIFT_SOURCE_DIR}/utils/UnicodeData/GraphemeBreakTest.txt" "${SWIFT_SOURCE_DIR}/utils/gyb_stdlib_support.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/__init__.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/Child.py" + "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/Classification.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/kinds.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/Node.py" "${SWIFT_SOURCE_DIR}/utils/gyb_syntax_support/AttributeNodes.py" @@ -141,7 +154,7 @@ function(handle_gyb_sources dependency_out_var_name sources_var_name arch) set(dir_root ${CMAKE_CURRENT_BINARY_DIR}) endif() - if (arch) + if (GYB_ARCH) set(dir "${dir_root}/${ptr_size}") else() set(dir "${dir_root}") @@ -154,7 +167,7 @@ function(handle_gyb_sources dependency_out_var_name sources_var_name arch) SOURCE "${src}" OUTPUT "${output_file_name}" FLAGS ${extra_gyb_flags} - DEPENDS "${gyb_extra_sources}" + DEPENDS "${GYB_DEPENDS}" "${gyb_extra_sources}" COMMENT "with ptr size = ${ptr_size}") list(APPEND dependency_targets "${dependency_target}") endforeach() @@ -163,13 +176,7 @@ function(handle_gyb_sources dependency_out_var_name sources_var_name arch) endfunction() function(add_gyb_target target sources) - set(options) - set(single_value_args ARCH) - set(multi_value_args) - cmake_parse_arguments(GYB - "${options}" "${single_value_args}" "${multi_value_args}" ${ARGN}) - - handle_gyb_sources(gyb_sources_depends sources "${GYB_ARCH}") + handle_gyb_sources(gyb_sources_depends sources ${ARGN}) add_custom_target(${target} DEPENDS "${gyb_sources_depends}") diff --git a/cmake/modules/SwiftSharedCMakeConfig.cmake b/cmake/modules/SwiftSharedCMakeConfig.cmake index 0b830e33ce1bd..26ada5dc9acb0 100644 --- a/cmake/modules/SwiftSharedCMakeConfig.cmake +++ b/cmake/modules/SwiftSharedCMakeConfig.cmake @@ -98,7 +98,49 @@ macro(swift_common_standalone_build_config_llvm product) endif() if(LLVM_ENABLE_ZLIB) - find_package(ZLIB REQUIRED) + find_package(ZLIB REQUIRED) + endif() + + # Work around a bug in the swift-driver that causes the swift-driver to not be + # able to accept .tbd files when linking without passing in the .tbd file with + # a -Xlinker flag. + # + # Both clang and swiftc can accept an -Xlinker flag so we use that to pass the + # value. + if (APPLE) + get_target_property(LLVMSUPPORT_INTERFACE_LINK_LIBRARIES LLVMSupport INTERFACE_LINK_LIBRARIES) + get_target_property(LLVMSUPPORT_INTERFACE_LINK_OPTIONS LLVMSupport INTERFACE_LINK_OPTIONS) + set(new_libraries) + set(new_options) + if (LLVMSUPPORT_INTERFACE_LINK_OPTIONS) + set(new_options ${LLVMSUPPORT_INTERFACE_LINK_OPTIONS}) + endif() + foreach(lib ${LLVMSUPPORT_INTERFACE_LINK_LIBRARIES}) + # The reason why we also fix link libraries that are specified as a full + # target is since those targets can still be a tbd file. + # + # Example: ZLIB::ZLIB's library path is defined by + # ZLIB_LIBRARY_{DEBUG,RELEASE} which can on Darwin have a tbd file as a + # value. So we need to work around this until we get a newer swiftc that + # can accept a .tbd file. + if (TARGET ${lib}) + list(APPEND new_options "LINKER:$") + continue() + endif() + + # If we have an interface library dependency that is just a path to a tbd + # file, pass the tbd file via -Xlinker so it gets straight to the linker. + get_filename_component(LIB_FILENAME_COMPONENT ${lib} LAST_EXT) + if ("${LIB_FILENAME_COMPONENT}" STREQUAL ".tbd") + list(APPEND new_options "LINKER:${lib}") + continue() + endif() + + list(APPEND new_libraries "${lib}") + endforeach() + + set_target_properties(LLVMSupport PROPERTIES INTERFACE_LINK_LIBRARIES "${new_libraries}") + set_target_properties(LLVMSupport PROPERTIES INTERFACE_LINK_OPTIONS "${new_options}") endif() include(AddLLVM) diff --git a/cmake/modules/SwiftUtils.cmake b/cmake/modules/SwiftUtils.cmake index 19f17223ec3bc..d6573db26cd7f 100644 --- a/cmake/modules/SwiftUtils.cmake +++ b/cmake/modules/SwiftUtils.cmake @@ -152,6 +152,35 @@ function(swift_create_post_build_symlink target) COMMENT "${CS_COMMENT}") endfunction() +# Once swift-frontend is built, if the standalone (early) swift-driver has been built, +# we create a `swift-driver` symlink adjacent to the `swift` and `swiftc` executables +# to ensure that `swiftc` forwards to the standalone driver when invoked. +function(swift_create_early_driver_symlinks target) + # Early swift-driver is built adjacent to the compiler (swift build dir) + set(driver_bin_dir "${CMAKE_BINARY_DIR}/../earlyswiftdriver-${SWIFT_HOST_VARIANT}-${SWIFT_HOST_VARIANT_ARCH}/release/bin") + set(swift_bin_dir "${SWIFT_RUNTIME_OUTPUT_INTDIR}") + # If early swift-driver wasn't built, nothing to do here. + if(NOT EXISTS "${driver_bin_dir}/swift-driver" OR NOT EXISTS "${driver_bin_dir}/swift-help") + message(STATUS "Skipping creating early SwiftDriver symlinks - no early SwiftDriver build found.") + return() + endif() + + message(STATUS "Creating early SwiftDriver symlinks.") + message(STATUS "From: ${driver_bin_dir}/swift-driver") + message(STATUS "To: ${swift_bin_dir}/swift-driver") + swift_create_post_build_symlink(swift-frontend + SOURCE "${driver_bin_dir}/swift-driver" + DESTINATION "${swift_bin_dir}/swift-driver" + COMMENT "Creating early SwiftDriver symlinks: swift-driver") + + message(STATUS "From: ${driver_bin_dir}/swift-help") + message(STATUS "To: ${swift_bin_dir}/swift-help") + swift_create_post_build_symlink(swift-frontend + SOURCE "${driver_bin_dir}/swift-help" + DESTINATION "${swift_bin_dir}/swift-help" + COMMENT "Creating early SwiftDriver symlinks: swift-help") +endfunction() + function(dump_swift_vars) set(SWIFT_STDLIB_GLOBAL_CMAKE_CACHE) get_cmake_property(variableNames VARIABLES) diff --git a/docs/ABI/CallConvSummary.rst b/docs/ABI/CallConvSummary.rst new file mode 100644 index 0000000000000..dc8a6f692863f --- /dev/null +++ b/docs/ABI/CallConvSummary.rst @@ -0,0 +1,208 @@ +:orphan: + +Calling Convention Summary +========================== + +Below is a summary of the calling conventions used on macOS and iOS. + +The `ABI stability manifesto <../ABIStabilityManifesto.md>`_ gives more details +on the use of the Swift error return and ``self`` registers, while `The Swift +Calling Convention `_ covers the specifics in more +details. (The Swift ``self`` register is known in other documents as the +"Context register".) + +x86-64 +------ + +See `Apple x86-64 Documentation`_, `System V ABI AMD64 Processor Supplement`_. + +.. _Apple x86-64 Documentation: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/LowLevelABI/140-x86-64_Function_Calling_Conventions/x86_64.html +.. _System V ABI AMD64 Processor Supplement: https://www.uclibc.org/docs/psABI-x86_64.pdf + +Register usage +^^^^^^^^^^^^^^ + ++-----------+----------------------------------+----------+----------+----------+ +| Register | Purpose | C++ | ObjC | Swift | ++===========+==================================+==========+==========+==========+ +| ``rax`` | Return value; also, for varargs, | | | | +| | number of ``xmm`` registers used | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``rbx`` | Callee-saved register | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``rdi`` | Integer argument 1 | ``this`` | ``self`` | | ++-----------+----------------------------------+----------+----------+----------+ +| ``rsi`` | Integer argument 2 | | ``_cmd`` | | ++-----------+----------------------------------+----------+----------+----------+ +| ``rdx`` | Integer argument 3 | | | | +| | (2nd return value) | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``rcx`` | Integer argument 4 | | | | +| | (3rd return value) | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``r8`` | Integer argument 5 | | | | +| | (4th return value) | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``r9`` | Integer argument 6 | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``r12`` | Callee-saved register | | | Error | +| | | | | return | ++-----------+----------------------------------+----------+----------+----------+ +| ``r13`` | Callee-saved register | | | ``self`` | ++-----------+----------------------------------+----------+----------+----------+ +| ``r14`` | Callee-saved register | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``r15`` | Callee-saved register | | | | +| | (other platforms use as GOT ptr) | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``st0`` | Used to return ``long double`` | | | | +| | values | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``st1`` | Used to return ``long double`` | | | | +| | values | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``xmm0``- | Floating point arguments 1-8 | | | | +| ``xmm7`` | (``xmm0``-``xmm3`` also used | | | | +| | for return) | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``rsp`` | Stack pointer | | | | ++-----------+----------------------------------+----------+----------+----------+ +| ``rbp`` | Callee-saved register, | | | | +| | used as frame pointer | | | | ++-----------+----------------------------------+----------+----------+----------+ + +Stack frame +^^^^^^^^^^^ + +On function entry, ``rsp+8`` is **16-byte aligned**, i.e. the start of the memory +arguments is 16-byte aligned; the initial stack pointer is shown below as "entry +``rsp``", but a typical non-leaf function will start by doing:: + + push %rbp + mov %rsp, %rbp + sub , %rsp + +Frameless leaf functions, however, will often not set up the frame pointer, +``rbp``, in which case they may refer to arguments relative to ``rsp`` instead. + ++---------------+---------------+------------------------+ +| | ``rbp+8n+16`` | memory argument *n* | +| | | | +| | ... | ... | +| | | | +| | ``rbp+16`` | memory argument 0 | ++---------------+-----------+---+------------------------+ +| ↓ Current Frame | ↑ Previous Frame | ++---------------+-----------+---+------------------------+ +| | ``rbp+8`` | return address | +| | | | ++---------------+---------------+------------------------+ +| entry ``rsp`` | ``rbp`` | previous ``rbp`` value | ++---------------+---------------+------------------------+ +| | ``rbp-8`` | | +| | | | +| | ... | local storage | +| | | | +| | ``rsp`` | | ++---------------+---------------+------------------------+ +| | ``rsp-8`` | | +| | | | +| | ... | red zone | +| | | | +| | ``rsp-128`` | | ++---------------+---------------+------------------------+ + + +ARM64 +----- + +See `Apple ARM64 Documentation`_, `Procedure Call Standard for the Arm 64-bit Architecture`_. + +.. _Apple ARM64 Documentation: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms +.. _Procedure Call Standard for the Arm 64-bit Architecture: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + +Register usage +^^^^^^^^^^^^^^ + ++----------+---------+-------------------------+----------+----------+----------+ +| Register | Special | Purpose | C++ | ObjC | Swift | ++==========+=========+=========================+==========+==========+==========+ +| ``x0`` | | Integer argument 1 | ``this`` | ``self`` | | +| | | (1st return value) | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x1`` | | Integer argument 2 | | ``_cmd`` | | +| | | (2nd return value) | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x2``- | | Integer arguments 3-8 | | | | +| ``x7`` | | (3rd-8th return values) | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x8`` | | Indirect result | | | | +| | | location register | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x16`` | ``ip0`` | Scratch registers (used | | | | ++----------+---------+ by dyld, can be used | | | | +| ``x17`` | ``ip1`` | freely otherwise) | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x18`` | | RESERVED **DO NOT USE** | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x19`` | | Callee-saved register | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x20`` | | Callee-saved register | | | ``self`` | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x21`` | | Callee-saved register | | | Error | +| | | | | | return | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x22``- | | Callee-saved registers | | | | +| ``x28`` | | | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x29`` | ``fp`` | Frame pointer | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``x30`` | ``lr`` | Link register | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``sp`` | | Stack pointer | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``v0``- | | Floating point/SIMD | | | | +| ``v7`` | | arguments 1-8 | | | | +| | | (also for return) | | | | ++----------+---------+-------------------------+----------+----------+----------+ +| ``v8``- | | Callee-saved registers | | | | +| ``v15`` | | (**lower 64-bits only**)| | | | ++----------+---------+-------------------------+----------+----------+----------+ + +Stack frame +^^^^^^^^^^^ + +The stack pointer is **16-byte aligned**; on function entry, ``sp`` points at +the location shown by "entry ``sp``" below. As with x86, frameless leaf +functions may not set up ``fp``, in which case they will use ``sp`` relative +accesses. + ++--------------+---------------+------------------------+ +| | ``fp+8n+16`` | last memory argument | +| | | | +| | ... | ... | +| | | | +| | ``fp+16`` | memory argument 0 [1]_ | ++--------------+------------+--+------------------------+ +| ↓ Current Frame | ↑ Previous Frame | ++--------------+------------+--+------------------------+ +| entry ``sp`` | ``fp+8`` | saved ``lr`` | +| | | (return address) | ++--------------+---------------+------------------------+ +| | ``fp`` | previous ``fp`` value | ++--------------+---------------+------------------------+ +| | ``fp-8`` | | +| | | | +| | ... | local storage | +| | | | +| | ``sp`` | | ++--------------+---------------+------------------------+ +| | ``sp-8`` | | +| | | | +| | ... | red zone | +| | | | +| | ``sp-128`` | | ++--------------+---------------+------------------------+ + +.. [1] See Apple documentation, however. Unlike the official ARM64 ABI, we pack + arguments, so this might also hold argument 1, argument 2 and so on. diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 6c172bcdf28f7..13797d3c30c82 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -251,6 +251,8 @@ types where the metadata itself has unknown layout.) REABSTRACT-THUNK-TYPE ::= 'R' // reabstraction thunk REABSTRACT-THUNK-TYPE ::= 'r' // reabstraction thunk (obsolete) + global ::= reabstraction-thunk type 'TU' // reabstraction thunk with global actor constraint + The `from-type` and `to-type` in a reabstraction thunk helper function are always non-polymorphic ```` types. @@ -464,6 +466,7 @@ Types KNOWN-TYPE-KIND ::= 'a' // Swift.Array KNOWN-TYPE-KIND ::= 'B' // Swift.BinaryFloatingPoint KNOWN-TYPE-KIND ::= 'b' // Swift.Bool + KNOWN-TYPE-KIND ::= 'c' KNOWN-TYPE-KIND-2 // Second set of standard types KNOWN-TYPE-KIND ::= 'D' // Swift.Dictionary KNOWN-TYPE-KIND ::= 'd' // Swift.Float64 KNOWN-TYPE-KIND ::= 'E' // Swift.Encodable @@ -509,6 +512,25 @@ Types KNOWN-TYPE-KIND ::= 'Z' // Swift.SignedInteger KNOWN-TYPE-KIND ::= 'z' // Swift.BinaryInteger + KNOWN-TYPE-KIND-2 ::= 'A' // Swift.Actor + KNOWN-TYPE-KIND-2 ::= 'C' // Swift.CheckedContinuation + KNOWN-TYPE-KIND-2 ::= 'c' // Swift.UnsafeContinuation + KNOWN-TYPE-KIND-2 ::= 'E' // Swift.CancellationError + KNOWN-TYPE-KIND-2 ::= 'e' // Swift.UnownedSerialExecutor + KNOWN-TYPE-KIND-2 ::= 'F' // Swift.Executor + KNOWN-TYPE-KIND-2 ::= 'f' // Swift.SerialExecutor + KNOWN-TYPE-KIND-2 ::= 'G' // Swift.TaskGroup + KNOWN-TYPE-KIND-2 ::= 'g' // Swift.ThrowingTaskGroup + KNOWN-TYPE-KIND-2 ::= 'I' // Swift.AsyncIteratorProtocol + KNOWN-TYPE-KIND-2 ::= 'i' // Swift.AsyncSequence + KNOWN-TYPE-KIND-2 ::= 'J' // Swift.UnownedJob + KNOWN-TYPE-KIND-2 ::= 'M' // Swift.MainActor + KNOWN-TYPE-KIND-2 ::= 'P' // Swift.TaskPriority + KNOWN-TYPE-KIND-2 ::= 'S' // Swift.AsyncStream + KNOWN-TYPE-KIND-2 ::= 's' // Swift.AsyncThrowingStream + KNOWN-TYPE-KIND-2 ::= 'T' // Swift.Task + KNOWN-TYPE-KIND-2 ::= 't' // Swift.UnsafeCurrentTask + protocol ::= context decl-name protocol ::= standard-substitutions @@ -567,7 +589,7 @@ Types C-TYPE is mangled according to the Itanium ABI, and prefixed with the length. Non-ASCII identifiers are preserved as-is; we do not use Punycode. - function-signature ::= params-type params-type async? sendable? throws? differentiable? // results and parameters + function-signature ::= params-type params-type async? sendable? throws? differentiable? global-actor? // results and parameters params-type ::= type 'z'? 'h'? // tuple in case of multiple parameters or a single parameter with a single tuple type // with optional inout convention, shared convention. parameters don't have labels, @@ -577,6 +599,7 @@ Types #if SWIFT_RUNTIME_VERSION >= 5.5 async ::= 'Ya' // 'async' annotation on function types sendable ::= 'Yb' // @Sendable on function types + global-actor :: = type 'Yc' // Global actor on function type #endif throws ::= 'K' // 'throws' annotation on function types differentiable ::= 'Yjf' // @differentiable(_forward) on function type @@ -588,7 +611,7 @@ Types type-list ::= empty-list // FIXME: Consider replacing 'h' with a two-char code - list-type ::= type identifier? 'Yk'? 'z'? 'h'? 'n'? 'd'? // type with optional label, '@noDerivative', inout convention, shared convention, owned convention, and variadic specifier + list-type ::= type identifier? 'Yk'? 'z'? 'h'? 'n'? 'Yi'? 'd'? // type with optional label, '@noDerivative', inout convention, shared convention, owned convention, actor 'isolated', and variadic specifier METATYPE-REPR ::= 't' // Thin metatype representation METATYPE-REPR ::= 'T' // Thick metatype representation diff --git a/docs/ABI/OldMangling.rst b/docs/ABI/OldMangling.rst new file mode 100644 index 0000000000000..ba44e7575365a --- /dev/null +++ b/docs/ABI/OldMangling.rst @@ -0,0 +1,548 @@ +:orphan: + +.. _ABI: + +.. highlight:: none + +Mangling +-------- + +This file documents ONLY the old mangling scheme in use before Swift 4.0, +which is still used for the Objective-C class names of Swift classes. + +:: + + mangled-name ::= '_T' global + +All Swift-mangled names begin with this prefix. + +Globals +~~~~~~~ + +:: + + global ::= 't' type // standalone type (for DWARF) + global ::= 'M' type // type metadata (address point) + // -- type starts with [BCOSTV] + global ::= 'Mf' type // 'full' type metadata (start of object) + global ::= 'MP' type // type metadata pattern + global ::= 'Ma' type // type metadata access function + global ::= 'ML' type // type metadata lazy cache variable + global ::= 'Mm' type // class metaclass + global ::= 'Mn' nominal-type // nominal type descriptor + global ::= 'Mp' protocol // protocol descriptor + global ::= 'MR' remote-reflection-record // metadata for remote mirrors + global ::= 'PA' .* // partial application forwarder + global ::= 'PAo' .* // ObjC partial application forwarder + global ::= 'w' value-witness-kind type // value witness + global ::= 'Wa' protocol-conformance // protocol witness table accessor + global ::= 'WG' protocol-conformance // generic protocol witness table + global ::= 'WI' protocol-conformance // generic protocol witness table instantiation function + global ::= 'Wl' type protocol-conformance // lazy protocol witness table accessor + global ::= 'WL' protocol-conformance // lazy protocol witness table cache variable + global ::= 'Wo' entity // witness table offset + global ::= 'WP' protocol-conformance // protocol witness table + global ::= 'Wt' protocol-conformance identifier // associated type metadata accessor + global ::= 'WT' protocol-conformance identifier nominal-type // associated type witness table accessor + global ::= 'Wv' directness entity // field offset + global ::= 'WV' type // value witness table + global ::= entity // some identifiable thing + global ::= 'TO' global // ObjC-as-swift thunk + global ::= 'To' global // swift-as-ObjC thunk + global ::= 'TD' global // dynamic dispatch thunk + global ::= 'Td' global // direct method reference thunk + global ::= 'TR' reabstract-signature // reabstraction thunk helper function + global ::= 'Tr' reabstract-signature // reabstraction thunk + + global ::= 'TS' specializationinfo '_' mangled-name + specializationinfo ::= 'g' passid (type protocol-conformance* '_')+ // Generic specialization info. + specializationinfo ::= 'f' passid (funcspecializationarginfo '_')+ // Function signature specialization kind + passid ::= integer // The id of the pass that generated this specialization. + funcsigspecializationarginfo ::= 'cl' closurename type* // Closure specialized with closed over types in argument order. + funcsigspecializationarginfo ::= 'n' // Unmodified argument + funcsigspecializationarginfo ::= 'cp' funcsigspecializationconstantproppayload // Constant propagated argument + funcsigspecializationarginfo ::= 'd' // Dead argument + funcsigspecializationarginfo ::= 'g' 's'? // Owned => Guaranteed and Exploded if 's' present. + funcsigspecializationarginfo ::= 's' // Exploded + funcsigspecializationarginfo ::= 'k' // Exploded + funcsigspecializationconstantpropinfo ::= 'fr' mangled-name + funcsigspecializationconstantpropinfo ::= 'g' mangled-name + funcsigspecializationconstantpropinfo ::= 'i' 64-bit-integer + funcsigspecializationconstantpropinfo ::= 'fl' float-as-64-bit-integer + funcsigspecializationconstantpropinfo ::= 'se' stringencoding 'v' md5hash + + global ::= 'TV' global // vtable override thunk + global ::= 'TW' protocol-conformance entity + // protocol witness thunk + global ::= 'TB' identifier context identifier + // property behavior initializer thunk + global ::= 'Tb' identifier context identifier + // property behavior setter thunk + + entity ::= nominal-type // named type declaration + entity ::= static? entity-kind context entity-name + entity-kind ::= 'F' // function (ctor, accessor, etc.) + entity-kind ::= 'v' // variable (let/var) + entity-kind ::= 'i' // subscript ('i'ndex) itself (not the individual accessors) + entity-kind ::= 'I' // initializer + entity-name ::= decl-name type // named declaration + entity-name ::= 'A' index // default argument generator + entity-name ::= 'a' addressor-kind decl-name type // mutable addressor + entity-name ::= 'C' type // allocating constructor + entity-name ::= 'c' type // non-allocating constructor + entity-name ::= 'D' // deallocating destructor; untyped + entity-name ::= 'd' // non-deallocating destructor; untyped + entity-name ::= 'g' decl-name type // getter + entity-name ::= 'i' // non-local variable initializer + entity-name ::= 'l' addressor-kind decl-name type // non-mutable addressor + entity-name ::= 'm' decl-name type // materializeForSet + entity-name ::= 's' decl-name type // setter + entity-name ::= 'U' index type // explicit anonymous closure expression + entity-name ::= 'u' index type // implicit anonymous closure + entity-name ::= 'w' decl-name type // willSet + entity-name ::= 'W' decl-name type // didSet + static ::= 'Z' // entity is a static member of a type + decl-name ::= identifier + decl-name ::= local-decl-name + decl-name ::= private-decl-name + local-decl-name ::= 'L' index identifier // locally-discriminated declaration + private-decl-name ::= 'P' identifier identifier // file-discriminated declaration + reabstract-signature ::= ('G' generic-signature)? type type + addressor-kind ::= 'u' // unsafe addressor (no owner) + addressor-kind ::= 'O' // owning addressor (non-native owner) + addressor-kind ::= 'o' // owning addressor (native owner) + addressor-kind ::= 'p' // pinning addressor (native owner) + + remote-reflection-record ::= 'f' type // field descriptor + remote-reflection-record ::= 'a' protocol-conformance // associated type descriptor + remote-reflection-record ::= 'b' type // builtin type descriptor + +An ``entity`` starts with a ``nominal-type-kind`` (``[COPV]``), a +substitution (``[Ss]``) of a nominal type, or an ``entity-kind`` +(``[FIiv]``). + +An ``entity-name`` starts with ``[AaCcDggis]`` or a ``decl-name``. +A ``decl-name`` starts with ``[LP]`` or an ``identifier`` (``[0-9oX]``). + +A ``context`` starts with either an ``entity``, an ``extension`` (which starts +with ``[Ee]``), or a ``module``, which might be an ``identifier`` (``[0-9oX]``) +or a substitution of a module (``[Ss]``). + +A global mangling starts with an ``entity`` or ``[MTWw]``. + +If a partial application forwarder is for a static symbol, its name will +start with the sequence ``_TPA_`` followed by the mangled symbol name of the +forwarder's destination. + +A generic specialization mangling consists of a header, specifying the types +and conformances used to specialize the generic function, followed by the +full mangled name of the original unspecialized generic symbol. + +The first identifier in a ```` is a string that represents +the file the original declaration came from. It should be considered unique +within the enclosing module. The second identifier is the name of the entity. + +Not all declarations marked ``private`` declarations will use the +```` mangling; if the entity's context is enough to uniquely +identify the entity, the simple ``identifier`` form is preferred. + +The types in a ```` are always non-polymorphic +```` types. + +Direct and Indirect Symbols +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + directness ::= 'd' // direct + directness ::= 'i' // indirect + +A direct symbol resolves directly to the address of an object. An +indirect symbol resolves to the address of a pointer to the object. +They are distinct manglings to make a certain class of bugs +immediately obvious. + +The terminology is slightly overloaded when discussing offsets. A +direct offset resolves to a variable holding the true offset. An +indirect offset resolves to a variable holding an offset to be applied +to type metadata to get the address of the true offset. (Offset +variables are required when the object being accessed lies within a +resilient structure. When the layout of the object may depend on +generic arguments, these offsets must be kept in metadata. Indirect +field offsets are therefore required when accessing fields in generic +types where the metadata itself has unknown layout.) + +Declaration Contexts +~~~~~~~~~~~~~~~~~~~~ + +:: + + context ::= module + context ::= extension + context ::= entity + module ::= substitution // other substitution + module ::= identifier // module name + module ::= known-module // abbreviation + extension ::= 'E' module entity + extension ::= 'e' module generic-signature entity + +These manglings identify the enclosing context in which an entity was declared, +such as its enclosing module, function, or nominal type. + +An ``extension`` mangling is used whenever an entity's declaration context is +an extension *and* the entity being extended is in a different module. In this +case the extension's module is mangled first, followed by the entity being +extended. If the extension and the extended entity are in the same module, the +plain ``entity`` mangling is preferred. If the extension is constrained, the +constraints on the extension are mangled in its generic signature. + +When mangling the context of a local entity within a constructor or +destructor, the non-allocating or non-deallocating variant is used. + +Types +~~~~~ + +:: + + type ::= 'Bb' // Builtin.BridgeObject + type ::= 'BB' // Builtin.UnsafeValueBuffer + type ::= 'Bf' natural '_' // Builtin.Float + type ::= 'Bi' natural '_' // Builtin.Int + type ::= 'BO' // Builtin.UnknownObject + type ::= 'Bo' // Builtin.NativeObject + type ::= 'Bp' // Builtin.RawPointer + type ::= 'Bv' natural type // Builtin.Vecx + type ::= 'Bw' // Builtin.Word + type ::= nominal-type + type ::= associated-type + type ::= 'a' context identifier // Type alias (DWARF only) + type ::= 'b' type type // objc block function type + type ::= 'c' type type // C function pointer type + type ::= 'F' throws-annotation? type type // function type + type ::= 'f' throws-annotation? type type // uncurried function type + type ::= 'G' type + '_' // generic type application + type ::= 'K' type type // @auto_closure function type + type ::= 'M' type // metatype without representation + type ::= 'XM' metatype-repr type // metatype with representation + type ::= 'P' protocol-list '_' // protocol type + type ::= 'PM' type // existential metatype without representation + type ::= 'XPM' metatype-repr type // existential metatype with representation + type ::= archetype + type ::= 'R' type // inout + type ::= 'T' tuple-element* '_' // tuple + type ::= 't' tuple-element* '_' // variadic tuple + type ::= 'Xo' type // @unowned type + type ::= 'Xu' type // @unowned(unsafe) type + type ::= 'Xw' type // @weak type + type ::= 'XF' impl-function-type // function implementation type + type ::= 'Xf' type type // @thin function type + type ::= 'Xb' type // SIL @box type + nominal-type ::= known-nominal-type + nominal-type ::= substitution + nominal-type ::= nominal-type-kind declaration-name + nominal-type-kind ::= 'C' // class + nominal-type-kind ::= 'O' // enum + nominal-type-kind ::= 'V' // struct + declaration-name ::= context decl-name + archetype ::= 'Q' index // archetype with depth=0, idx=N + archetype ::= 'Qd' index index // archetype with depth=M+1, idx=N + archetype ::= associated-type + archetype ::= qualified-archetype + associated-type ::= substitution + associated-type ::= 'Q' protocol-context // self type of protocol + associated-type ::= 'Q' archetype identifier // associated type + qualified-archetype ::= 'Qq' index context // archetype+context (DWARF only) + protocol-context ::= 'P' protocol + tuple-element ::= identifier? type + metatype-repr ::= 't' // Thin metatype representation + metatype-repr ::= 'T' // Thick metatype representation + metatype-repr ::= 'o' // ObjC metatype representation + throws-annotation ::= 'z' // 'throws' annotation on function types + + + type ::= 'u' generic-signature type // generic type + type ::= 'x' // generic param, depth=0, idx=0 + type ::= 'q' generic-param-index // dependent generic parameter + type ::= 'q' type assoc-type-name // associated type of non-generic param + type ::= 'w' generic-param-index assoc-type-name // associated type + type ::= 'W' generic-param-index assoc-type-name+ '_' // associated type at depth + + generic-param-index ::= 'x' // depth = 0, idx = 0 + generic-param-index ::= index // depth = 0, idx = N+1 + generic-param-index ::= 'd' index index // depth = M+1, idx = N + +```` never begins or ends with a number. +```` never begins with an underscore. +```` never begins with ``d``. +```` never begins with ``z``. + +Note that protocols mangle differently as types and as contexts. A protocol +context always consists of a single protocol name and so mangles without a +trailing underscore. A protocol type can have zero, one, or many protocol bounds +which are juxtaposed and terminated with a trailing underscore. + +:: + + assoc-type-name ::= ('P' protocol-name)? identifier + assoc-type-name ::= substitution + +Associated types use an abbreviated mangling when the base generic parameter +or associated type is constrained by a single protocol requirement. The +associated type in this case can be referenced unambiguously by name alone. +If the base has multiple conformance constraints, then the protocol name is +mangled in to disambiguate. + +:: + + impl-function-type ::= + impl-callee-convention impl-function-attribute* generic-signature? '_' + impl-parameter* '_' impl-result* '_' + impl-callee-convention ::= 't' // thin + impl-callee-convention ::= impl-convention // thick, callee transferred with given convention + impl-convention ::= 'a' // direct, autoreleased + impl-convention ::= 'd' // direct, no ownership transfer + impl-convention ::= 'D' // direct, no ownership transfer, + // dependent on 'self' parameter + impl-convention ::= 'g' // direct, guaranteed + impl-convention ::= 'e' // direct, deallocating + impl-convention ::= 'i' // indirect, ownership transfer + impl-convention ::= 'l' // indirect, inout + impl-convention ::= 'G' // indirect, guaranteed + impl-convention ::= 'o' // direct, ownership transfer + impl-convention ::= 'z' impl-convention // error result + impl-function-attribute ::= 'Cb' // compatible with C block invocation function + impl-function-attribute ::= 'Cc' // compatible with C global function + impl-function-attribute ::= 'Cm' // compatible with Swift method + impl-function-attribute ::= 'CO' // compatible with ObjC method + impl-function-attribute ::= 'Cw' // compatible with protocol witness + impl-function-attribute ::= 'G' // generic + impl-function-attribute ::= 'g' // pseudogeneric + impl-parameter ::= impl-convention type + impl-result ::= impl-convention type + +For the most part, manglings follow the structure of formal language +types. However, in some cases it is more useful to encode the exact +implementation details of a function type. + +Any ```` productions must appear in the order +in which they are specified above: e.g. a pseudogeneric C function is +mangled with ``Ccg``. ``g`` and ``G`` are exclusive and mark the presence +of a generic signature immediately following. + +Note that the convention and function-attribute productions do not +need to be disambiguated from the start of a ````. + +Generics +~~~~~~~~ + +:: + + protocol-conformance ::= ('u' generic-signature)? type protocol module + +```` refers to a type's conformance to a protocol. The +named module is the one containing the extension or type declaration that +declared the conformance. + +:: + + // Property behavior conformance + protocol-conformance ::= ('u' generic-signature)? + 'b' identifier context identifier protocol + +Property behaviors are implemented using private protocol conformances. + +:: + + generic-signature ::= (generic-param-count+)? ('R' requirement*)? 'r' + generic-param-count ::= 'z' // zero parameters + generic-param-count ::= index // N+1 parameters + requirement ::= type-param protocol-name // protocol requirement + requirement ::= type-param type // base class requirement + // type starts with [CS] + requirement ::= type-param 'z' type // 'z'ame-type requirement + + // Special type mangling for type params that saves the initial 'q' on + // generic params + type-param ::= generic-param-index // generic parameter + type-param ::= 'w' generic-param-index assoc-type-name // associated type + type-param ::= 'W' generic-param-index assoc-type-name+ '_' + +A generic signature begins by describing the number of generic parameters at +each depth of the signature, followed by the requirements. As a special case, +no ``generic-param-count`` values indicates a single generic parameter at +the outermost depth:: + + urFq_q_ // T_0_0 -> T_0_0 + u_0_rFq_qd_0_ // T_0_0 -> T_1_1 + +Value Witnesses +~~~~~~~~~~~~~~~ + +TODO: document these + +:: + + value-witness-kind ::= 'al' // allocateBuffer + value-witness-kind ::= 'ca' // assignWithCopy + value-witness-kind ::= 'ta' // assignWithTake + value-witness-kind ::= 'de' // deallocateBuffer + value-witness-kind ::= 'xx' // destroy + value-witness-kind ::= 'XX' // destroyBuffer + value-witness-kind ::= 'Xx' // destroyArray + value-witness-kind ::= 'CP' // initializeBufferWithCopyOfBuffer + value-witness-kind ::= 'Cp' // initializeBufferWithCopy + value-witness-kind ::= 'cp' // initializeWithCopy + value-witness-kind ::= 'TK' // initializeBufferWithTakeOfBuffer + value-witness-kind ::= 'Tk' // initializeBufferWithTake + value-witness-kind ::= 'tk' // initializeWithTake + value-witness-kind ::= 'pr' // projectBuffer + value-witness-kind ::= 'xs' // storeExtraInhabitant + value-witness-kind ::= 'xg' // getExtraInhabitantIndex + value-witness-kind ::= 'Cc' // initializeArrayWithCopy + value-witness-kind ::= 'Tt' // initializeArrayWithTakeFrontToBack + value-witness-kind ::= 'tT' // initializeArrayWithTakeBackToFront + value-witness-kind ::= 'ug' // getEnumTag + value-witness-kind ::= 'up' // destructiveProjectEnumData + value-witness-kind ::= 'ui' // destructiveInjectEnumTag + +```` differentiates the kinds of value +witness functions for a type. + +Identifiers +~~~~~~~~~~~ + +:: + + identifier ::= natural identifier-start-char identifier-char* + identifier ::= 'o' operator-fixity natural operator-char+ + + operator-fixity ::= 'p' // prefix operator + operator-fixity ::= 'P' // postfix operator + operator-fixity ::= 'i' // infix operator + + operator-char ::= 'a' // & 'and' + operator-char ::= 'c' // @ 'commercial at' + operator-char ::= 'd' // / 'divide' + operator-char ::= 'e' // = 'equals' + operator-char ::= 'g' // > 'greater' + operator-char ::= 'l' // < 'less' + operator-char ::= 'm' // * 'multiply' + operator-char ::= 'n' // ! 'not' + operator-char ::= 'o' // | 'or' + operator-char ::= 'p' // + 'plus' + operator-char ::= 'q' // ? 'question' + operator-char ::= 'r' // % 'remainder' + operator-char ::= 's' // - 'subtract' + operator-char ::= 't' // ~ 'tilde' + operator-char ::= 'x' // ^ 'xor' + operator-char ::= 'z' // . 'zperiod' + +```` is run-length encoded: the natural indicates how many +characters follow. Operator characters are mapped to letter characters as +given. In neither case can an identifier start with a digit, so +there's no ambiguity with the run-length. + +:: + + identifier ::= 'X' natural identifier-start-char identifier-char* + identifier ::= 'X' 'o' operator-fixity natural identifier-char* + +Identifiers that contain non-ASCII characters are encoded using the Punycode +algorithm specified in RFC 3492, with the modifications that ``_`` is used +as the encoding delimiter, and uppercase letters A through J are used in place +of digits 0 through 9 in the encoding character set. The mangling then +consists of an ``X`` followed by the run length of the encoded string and the +encoded string itself. For example, the identifier ``vergüenza`` is mangled +to ``X12vergenza_JFa``. (The encoding in standard Punycode would be +``vergenza-95a``) + +Operators that contain non-ASCII characters are mangled by first mapping the +ASCII operator characters to letters as for pure ASCII operator names, then +Punycode-encoding the substituted string. The mangling then consists of +``Xo`` followed by the fixity, run length of the encoded string, and the encoded +string itself. For example, the infix operator ``«+»`` is mangled to +``Xoi7p_qcaDc`` (``p_qcaDc`` being the encoding of the substituted +string ``«p»``). + +Substitutions +~~~~~~~~~~~~~ + +:: + + substitution ::= 'S' index + +```` is a back-reference to a previously mangled entity. The mangling +algorithm maintains a mapping of entities to substitution indices as it runs. +When an entity that can be represented by a substitution (a module, nominal +type, or protocol) is mangled, a substitution is first looked for in the +substitution map, and if it is present, the entity is mangled using the +associated substitution index. Otherwise, the entity is mangled normally, and +it is then added to the substitution map and associated with the next +available substitution index. + +For example, in mangling a function type +``(zim.zang.zung, zim.zang.zung, zim.zippity) -> zim.zang.zoo`` (with module +``zim`` and class ``zim.zang``), +the recurring contexts ``zim``, ``zim.zang``, and ``zim.zang.zung`` +will be mangled using substitutions after being mangled +for the first time. The first argument type will mangle in long form, +``CC3zim4zang4zung``, and in doing so, ``zim`` will acquire substitution ``S_``, +``zim.zang`` will acquire substitution ``S0_``, and ``zim.zang.zung`` will +acquire ``S1_``. The second argument is the same as the first and will mangle +using its substitution, ``S1_``. The +third argument type will mangle using the substitution for ``zim``, +``CS_7zippity``. (It also acquires substitution ``S2_`` which would be used +if it mangled again.) The result type will mangle using the substitution for +``zim.zang``, ``CS0_3zoo`` (and acquire substitution ``S3_``). The full +function type thus mangles as ``fTCC3zim4zang4zungS1_CS_7zippity_CS0_3zoo``. + +:: + + substitution ::= 's' + +The special substitution ``s`` is used for the ``Swift`` standard library +module. + +Predefined Substitutions +~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + known-module ::= 's' // Swift + known-module ::= 'SC' // C + known-module ::= 'So' // Objective-C + known-nominal-type ::= 'Sa' // Swift.Array + known-nominal-type ::= 'Sb' // Swift.Bool + known-nominal-type ::= 'Sc' // Swift.UnicodeScalar + known-nominal-type ::= 'Sd' // Swift.Float64 + known-nominal-type ::= 'Sf' // Swift.Float32 + known-nominal-type ::= 'Si' // Swift.Int + known-nominal-type ::= 'SV' // Swift.UnsafeRawPointer + known-nominal-type ::= 'Sv' // Swift.UnsafeMutableRawPointer + known-nominal-type ::= 'SP' // Swift.UnsafePointer + known-nominal-type ::= 'Sp' // Swift.UnsafeMutablePointer + known-nominal-type ::= 'SQ' // Swift.ImplicitlyUnwrappedOptional + known-nominal-type ::= 'Sq' // Swift.Optional + known-nominal-type ::= 'SR' // Swift.UnsafeBufferPointer + known-nominal-type ::= 'Sr' // Swift.UnsafeMutableBufferPointer + known-nominal-type ::= 'SS' // Swift.String + known-nominal-type ::= 'Su' // Swift.UInt + +```` and ```` are built-in substitutions for +certain common entities. Like any other substitution, they all start +with 'S'. + +The Objective-C module is used as the context for mangling Objective-C +classes as ````\ s. + +Indexes +~~~~~~~ + +:: + + index ::= '_' // 0 + index ::= natural '_' // N+1 + natural ::= [0-9]+ + +```` is a production for encoding numbers in contexts that can't +end in a digit; it's optimized for encoding smaller numbers. diff --git a/docs/ABI/RegisterUsage.md b/docs/ABI/RegisterUsage.md index aa29f84529727..4a41da06193db 100644 --- a/docs/ABI/RegisterUsage.md +++ b/docs/ABI/RegisterUsage.md @@ -1,13 +1,5 @@ ## 64-Bit Architecture Register Usage -From Swift 5, the calling convention register allocation for 64-bit architectures is largely based on [existing](https://developer.apple.com/library/content/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html) [standards](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/LowLevelABI/140-x86-64_Function_Calling_Conventions/x86_64.html), with the addition of the context and error return registers discussed in the [ABI stability manifesto](https://github.com/apple/swift/blob/main/docs/ABIStabilityManifesto.md): - -| Register Purpose | ARM64 | x86_64 | -| ------------- |:-------------:| ----- | -| Context register (self) | x20 | r13 | -| Error return register | x21 | r12 | -| Struct return pointer | x8 | rax | -| Float call arguments | v0 … v7 | xmm0 … xmm7 | -| Integer call arguments | x0 … x7 | rdi, rsi, rdx, rcx, r8, r9 | -| Float return | v0 … v3 | xmm0 … xmm3 | -| Integer return | x0 … x3 | rax, rdx, rcx, r8 | +This file has been replaced by [CallConvSummary.rst](CallConvSummary.rst), +which gives information about register usage and also shows the stack frame +layouts for the 64-bit architectures. diff --git a/docs/ABIStabilityManifesto.md b/docs/ABIStabilityManifesto.md index 0954cce83aaa7..364acf93b1b0e 100644 --- a/docs/ABIStabilityManifesto.md +++ b/docs/ABIStabilityManifesto.md @@ -337,7 +337,7 @@ Having the call context register be callee-saved is advantageous. It keeps the r Throwing functions communicate error values to their callers through the *error* register on some platforms. The error register holds a pointer to the error value if an error occurred, otherwise 0. The caller of a throwing function is expected to quickly check for 0 before continuing on with non-error code, otherwise branching to code to handle or propagate the error. Using a callee-saved register for the error register enables free conversion from non-throwing to throwing functions, which is required to honor the subtyping relationship. -The specific registers used in these roles are documented in [another document on register usage](https://github.com/apple/swift/blob/main/docs/ABI/RegisterUsage.md). +The specific registers used in these roles are documented in [the calling convention summary document](ABI/CallConvSummary.rst). ### Function Signature Lowering diff --git a/docs/Android.md b/docs/Android.md index aa45bf3643a5a..644fd9d2f6ef6 100644 --- a/docs/Android.md +++ b/docs/Android.md @@ -72,7 +72,7 @@ Android NDK, as well as the directories that contain the `libicuucswift.so` and `libicui18nswift.so` you downloaded or built in step one: ``` -$ ARM_DIR=path/to/libicu-libiconv-android +$ ARM_DIR=path/to/libiconv-libicu-android/armeabi-v7a $ NDK_PATH=path/to/android-ndk-r21e $ utils/build-script \ -R \ # Build in ReleaseAssert mode. @@ -103,7 +103,7 @@ $ NDK_PATH="path/to/android-ndk-r21e" $ build/Ninja-ReleaseAssert/swift-linux-x86_64/bin/swiftc \ # The Swift compiler built in the previous step # The location of the tools used to build Android binaries -tools-directory ${NDK_PATH}/toolchains/llvm/prebuilt/linux-x86_64/bin/ \ - -target armv7a-unknown-linux-androideabi21 \ # Targeting Android armv7 at API 21 + -target armv7-unknown-linux-androideabi21 \ # Targeting Android armv7 at API 21 -sdk ${NDK_PATH}/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ # The SDK is the Android unified sysroot and the resource-dir is where you just built the Swift stdlib. -resource-dir build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift hello.swift diff --git a/docs/CppInteroperabilityManifesto.md b/docs/CppInteroperabilityManifesto.md index c1c6e5487ea80..738580ccd68d5 100644 --- a/docs/CppInteroperabilityManifesto.md +++ b/docs/CppInteroperabilityManifesto.md @@ -407,13 +407,13 @@ void printInt(const int &value); ```swift // C++ header imported in Swift. -void printInt(_ value: UnsafePointer) +func printInt(_ value: UnsafePointer) ``` ```swift // Usage example. -void caller() { +func caller() { var x = 42 printInt(&x) // OK @@ -450,13 +450,13 @@ void printInt(const int &value); ```swift // C++ header imported in Swift. -void printInt(_ value: Int) +func printInt(_ value: Int) ``` ```swift // Usage example. -void caller() { +func caller() { let x = 42 printInt(y) // OK } diff --git a/docs/DebuggingTheCompiler.md b/docs/DebuggingTheCompiler.md index 27bcaf9ca6e28..8a49add5f17ad 100644 --- a/docs/DebuggingTheCompiler.md +++ b/docs/DebuggingTheCompiler.md @@ -25,7 +25,7 @@ benefit of all Swift developers. - [Getting CommandLine for swift stdlib from Ninja to enable dumping stdlib SIL](#getting-commandline-for-swift-stdlib-from-ninja-to-enable-dumping-stdlib-sil) - [Dumping the SIL and other Data in LLDB](#dumping-the-sil-and-other-data-in-lldb) - [Debugging and Profiling on SIL level](#debugging-and-profiling-on-sil-level) - - [SIL source level profiling using -gsil](#sil-source-level-profiling-using--gsil) + - [SIL source level profiling using -sil-based-debuginfo](#sil-source-level-profiling) - [ViewCFG: Regex based CFG Printer for SIL/LLVM-IR](#viewcfg-regex-based-cfg-printer-for-silllvm-ir) - [Debugging the Compiler using advanced LLDB Breakpoints](#debugging-the-compiler-using-advanced-lldb-breakpoints) - [Debugging the Compiler using LLDB Scripts](#debugging-the-compiler-using-lldb-scripts) @@ -36,6 +36,14 @@ benefit of all Swift developers. - [Bisecting on SIL optimizer pass counts to identify optimizer bugs](#bisecting-on-sil-optimizer-pass-counts-to-identify-optimizer-bugs) - [Using git-bisect in the presence of branch forwarding/feature branches](#using-git-bisect-in-the-presence-of-branch-forwardingfeature-branches) - [Reducing SIL test cases using bug_reducer](#reducing-sil-test-cases-using-bug_reducer) +- [Debugging the Compiler Build](#debugging-the-compiler-build) + - [Build Dry Run](#build-dry-run) +- [Debugging the Compiler Driver](#debugging-the-compiler-driver-build) + - [Swift Compiler Driver F.A.Q](#swift-compiler-driver-f.a.q.) + - [Building the compiler without using the standalone driver](#building-the-compiler-without-the-standalone-driver) + - [Invoking the compiler without forwarding to the standalone driver](#invoking-the-compiler-without-forwarding-to-the-standalone-driver) + - [Reproducing the Compiler Driver build steps](#reproducing-the-compiler-driver-build-steps) + - [Installing the Compiler Driver](#installing-the-compiler-driver) - [Debugging Swift Executables](#debugging-swift-executables) - [Determining the mangled name of a function in LLDB](#determining-the-mangled-name-of-a-function-in-lldb) - [Manually symbolication using LLDB](#manually-symbolication-using-lldb) @@ -93,6 +101,12 @@ swiftc -emit-sil -Onone file.swift swiftc -emit-sil -O file.swift ``` +* **Debug info in SIL** To print debug info from `file.swift` in SIL: + +```sh +swiftc -g -emit-sil -O file.swift +``` + * **IRGen** To print the LLVM IR after IR generation: ```sh @@ -291,12 +305,13 @@ has a `dump()` method you can call. ## Debugging and Profiling on SIL level -### SIL source level profiling using -gsil +### SIL source level profiling The compiler provides a way to debug and profile on SIL level. To enable SIL -debugging add the front-end option -gsil together with -g. Example: +debugging add the front-end option -sil-based-debuginfo together with -g. +Example: - swiftc -g -Xfrontend -gsil -O test.swift -o a.out + swiftc -g -Xfrontend -sil-based-debuginfo -O test.swift -o a.out This writes the SIL after optimizations into a file and generates debug info for it. In the debugger and profiler you can then see the SIL code instead of @@ -807,6 +822,131 @@ reducing SIL test cases by: For more information and a high level example, see: ./swift/utils/bug_reducer/README.md. +# Debugging the Compiler Build + +## Build Dry Run + +A "dry-run" invocation of the `build-script` (using the `--dry-run` flag) will +print the commands that would be executed in a given build, without executing +them. A dry-run script invocation output can be used to inspect the build stages +of a given `build-script` configuration, or create script corresponding to one +such configuration. + +# Debugging the Compiler Driver + +The Swift compiler uses a standalone compiler-driver application written in +Swift: [swift-driver](https://github.com/apple/swift-driver). When building the +compiler using `build-script`, by default, the standalone driver will be built +first, using the host toolchain, if the host toolchain contains a Swift +compiler. If the host toolchain does not contain Swift, a warning is emitted and +the legacy compiler-driver (integrated in the C++ code-base) will be used. In +the future, a host toolchain containing a Swift compiler may become mandatory. +Once the compiler is built, the compiler build directory (`swift--`) +is updated with a symlink to the standalone driver, ensuring calls to the build +directory's `swift` and `swiftc` always forward to the standalone driver. + +For more information about the driver, see: +[github.com/apple/swift-driver/blob/main/README.md](https://github.com/apple/swift-driver/blob/main/README.md) + +## Swift Compiler Driver F.A.Q. +> What's the difference between invoking 'swiftc' vs. 'swift-driver' at the top + level? + +Today, `swift` and `swiftc` are symbolic links to the compiler binary +(`swift-frontend`). Invoking `swiftc` causes the executable to detects that it +is a compiler-driver invocation, and not a direct compiler-frontend invocation, +by examining the invoked program's name. The compiler frontend can be invoked +directly by invoking the `swift-frontend` executable, or passing in the +`-frontend` option to `swiftc`. + +The standalone [Compiler Driver](https://github.com/apple/swift-driver) is +installed as a separate `swift-driver` executable in the Swift toolchain's `bin` +directory. When a user launches the compiler by invoking `swiftc`, the C++ based +compiler executable forwards the invocation to the `swift-driver` executable if +one is found alongside it. This forwarding mechanism is in-place temporarily, to +allow for an easy fallback to the legacy driver via one of the two escape +hatches: + +- `-disallow-use-new-driver` command line flag +- `SWIFT_USE_OLD_DRIVER` environment variable + +If the user is to directly invoke the `swift-driver` executable, the behaviour +should be the same as invoking the `swiftc` executable, but without the option +for a legacy driver fallback. + +Once the legacy driver is deprecated, `swift` and `swiftc` executables will +become symbolic links to the `swift-driver` executable directly. + + +> Will 'swiftc ... -###' always print the same set of commands for the old/new + driver? Do they call 'swift-frontend' the same way? + +The standalone [Compiler Driver](https://github.com/apple/swift-driver) is meant +to be a direct drop-in replacement for the C++-based legacy driver. It has the +exact same command-line interface. The expectation is that its behaviour closely +matches the legacy driver; however, during, and after the transition to the new +driver being the default its behaviour may start to diverge from the legacy +driver as par for the course of its evolution and gaining new features, etc. +Today, broadly-speaking, sets of `swift-frontend` invocations generated by the +two drivers are expected to be very similar. + +## Building the compiler without the standalone driver +One can build the compiler that does not rely on the standalone driver and +instead uses the legacy, built-in driver using the `build-script` option: +`--skip-early-swift-driver`. + +## Invoking the compiler without forwarding to the standalone driver +The Swift compiler can currently be invoked in an execution mode that will use +the legacy C++-based compiler driver using one of the following two options: +- Passing `-disallow-use-new-driver` argument to the `swiftc` invocation +- Setting the `SWIFT_USE_OLD_DRIVER` environment variable + +## Reproducing the Compiler Driver build steps +A "[dry-run](#build-dry-run)" invocation of the `build-script` can be used to +examine the SwiftDriver build stage and commands, without executing it. For +example: +``` +$ utils/build-script --release-debuginfo --dry-run ++ mkdir -p /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert +--- Building earlyswiftdriver --- ++ /SwiftWorkspace/swift-driver/Utilities/build-script-helper.py build --package-path /SwiftWorkspace/swift-driver --build-path /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/earlyswiftdriver-macosx-x86_64 --configuration release --toolchain /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr --ninja-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/ninja --cmake-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/cmake --local_compiler_build +Building the standard library for: swift-test-stdlib-macosx-x86_64 +... +``` +One of the first steps is an invocation of the driver's +`build-script-helper.py` script which specifies that the driver us to be built +(`build`) using the host toolchain (`--toolchain`) to a specified location +(`--build-path`). + +## Installing the Compiler Driver +In order to create a Swift compiler installation (`--install-swift`), the +standalone driver must be built as a separate build product using the +*just-built* Swift compiler and toolchain (the ones built in the same +`build-script` invocation, preceding the SwiftDriver build product). The +additional build product is added to the build by specifying the +`--swift-driver` option of the `build-script`. The driver product is istalled +into the resulting toolchain installation by specifying the +`--install-swift-driver` option of the `build-script`. + +Note, a "dry-run" `build-script` invocation when installing the standalone +driver will demonstrate the commands required to build and install the driver as +a standalone build product: +``` +$ utils/build-script --release-debuginfo --dry-run --swift-driver --install-swift-driver +... +--- Cleaning swiftdriver --- ++ /SwiftWorkspace/swift-driver/Utilities/build-script-helper.py clean --package-path /SwiftWorkspace/swift-driver --build-path /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/swiftdriver-macosx-x86_64 --configuration release --toolchain /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/toolchain-macosx-x86_64/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr --ninja-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/ninja --cmake-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/cmake +--- Building swiftdriver --- ++ /SwiftWorkspace/swift-driver/Utilities/build-script-helper.py build --package-path /SwiftWorkspace/swift-driver --build-path /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/swiftdriver-macosx-x86_64 --configuration release --toolchain /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/toolchain-macosx-x86_64/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr --ninja-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/ninja --cmake-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/cmake +--- Installing swiftdriver --- ++ /SwiftWorkspace/swift-driver/Utilities/build-script-helper.py install --package-path /SwiftWorkspace/swift-driver --build-path /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/swiftdriver-macosx-x +86_64 --configuration release --toolchain /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/toolchain-macosx-x86_64/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr --ninja-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/ninja --cmake-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/cmake +``` +These invocations of the driver's `build-script-helper.py` script specify the +individual build actions (`clean`, `build`, `install`), the product build path +(`--build-path`), and the *just-built* toolchain which should be used +(`--toolchain`). + # Debugging Swift Executables One can use the previous tips for debugging the Swift compiler with Swift diff --git a/docs/Diagnostics.md b/docs/Diagnostics.md index e75d64a63bc41..87137fdcf5680 100644 --- a/docs/Diagnostics.md +++ b/docs/Diagnostics.md @@ -51,6 +51,7 @@ Clang also has a kind of diagnostic called a "remark", which represents informat - "...to silence this warning" - "...here" (for a purely locational note) +- If possible, it is best to include the name of the type or function that has the error, e.g. "non-actor type 'Nope' cannot ..." is better than "non-actor type cannot ...". It helps developers relate the error message to the specific type the error is about, even if the error would highlight the appropriate line / function in other ways. ### Locations and Highlights ### diff --git a/docs/HowToGuides/GettingStarted.md b/docs/HowToGuides/GettingStarted.md index 49592e8e0be4b..fa1a2137d3a92 100644 --- a/docs/HowToGuides/GettingStarted.md +++ b/docs/HowToGuides/GettingStarted.md @@ -130,7 +130,7 @@ Double-check that running `pwd` prints a path ending with `swift`. ⚠️ Since version 0.2.14, `sccache` no longer caches compile commands issued by `build-script` because of [sccache PR 898](https://github.com/mozilla/sccache/pull/898), since `build-script` adds the `-arch x86_64` argument twice. The instructions below may install `sccache` 0.2.14 or newer. You may want to instead download and install an older release from their [Releases page](https://github.com/mozilla/sccache/releases) until this issue is resolved. -1. Install [Xcode 12.3][Xcode] or newer: +1. Install [Xcode 13 beta 4][Xcode] or newer: The required version of Xcode changes frequently and is often a beta release. Check this document or the host information on for the current required version. @@ -151,35 +151,18 @@ Double-check that running `pwd` prints a path ending with `swift`. [Homebrew]: https://brew.sh/ [Homebrew Bundle]: https://github.com/Homebrew/homebrew-bundle -### Ubuntu Linux +### Linux -1. For Ubuntu 16.04 LTS and 18.04 LTS, run the following: +1. The latest Linux dependencies are listed in the respective Dockerfiles: + * [Ubuntu 20.04](https://github.com/apple/swift-docker/blob/main/swift-ci/master/ubuntu/20.04/Dockerfile) + * [CentOS 7](https://github.com/apple/swift-docker/blob/main/swift-ci/master/centos/7/Dockerfile) + * [CentOS 8](https://github.com/apple/swift-docker/blob/main/swift-ci/master/centos/8/Dockerfile) + * [Amazon Linux 2](https://github.com/apple/swift-docker/blob/main/swift-ci/master/amazon-linux/2/Dockerfile) - ```sh - sudo apt-get install \ - clang \ - cmake \ - git \ - icu-devtools \ - libcurl4-openssl-dev \ - libedit-dev \ - libicu-dev \ - libncurses5-dev \ - libpython3-dev \ - libsqlite3-dev \ - libxml2-dev \ - ninja-build \ - pkg-config \ - python \ - python-six \ - rsync \ - swig \ - systemtap-sdt-dev \ - tzdata \ - uuid-dev +2. To install sccache (optional): + ``` sudo snap install sccache --candidate --classic ``` - **Note:** LLDB currently requires at least `swig-1.3.40` but will successfully build with version 2 shipped with Ubuntu. @@ -187,7 +170,7 @@ Double-check that running `pwd` prints a path ending with `swift`. ### Spot check dependencies -* Run `cmake --version`: This should be 3.18.1 or higher for macOS. +* Run `cmake --version`: This should be 3.19.6 or higher. * Run `python3 --version`: Check that this succeeds. * Run `ninja --version`: Check that this succeeds. * Run `sccache --version`: Check that this succeeds. @@ -246,6 +229,7 @@ Phew, that's a lot to digest! Now let's proceed to the actual build itself! [using both Ninja and Xcode](#using-both-ninja-and-xcode). 3. Build the toolchain with optimizations, debuginfo, and assertions and run the tests. + macOS: - Via Ninja: ```sh utils/build-script --skip-build-benchmarks \ @@ -259,10 +243,14 @@ Phew, that's a lot to digest! Now let's proceed to the actual build itself! --sccache --release-debuginfo --swift-disable-dead-stripping --test \ --xcode ``` + Linux (uses Ninja): + ```sh + utils/build-script --release-debuginfo --test --skip-early-swift-driver + ``` This will create a directory `swift-project/build/Ninja-RelWithDebInfoAssert` (with `Xcode` instead of `Ninja` if you used `--xcode`) - containing the build artifacts. + containing the Swift compiler and standard library and clang/LLVM build artifacts. - If the build succeeds: Once the build is complete, the tests will run. - If the tests are passing: Great! We can go to the next step. - If some tests are failing: @@ -272,6 +260,10 @@ Phew, that's a lot to digest! Now let's proceed to the actual build itself! - If the build fails: See [Troubleshooting build issues](#troubleshooting-build-issues). + If you would like to additionally build the Swift corelibs, + ie swift-corelibs-libdispatch, swift-corelibs-foundation, and swift-corelibs-xctest, + on Linux, add the `--xctest` flag to `build-script`. + In the following sections, for simplicity, we will assume that you are using a `Ninja-RelWithDebInfoAssert` build on macOS running on an Intel-based Mac, unless explicitly mentioned otherwise. You will need to slightly tweak the paths diff --git a/docs/Lexicon.md b/docs/Lexicon.md index b2d8b14a8b436..987b32cf2a4cd 100644 --- a/docs/Lexicon.md +++ b/docs/Lexicon.md @@ -67,6 +67,17 @@ and the combination of module path + access path is an "import path".) See `ImportPath` and the types nested inside it for more on this. +## access pattern + +Defines how some particular storage (a property or a subscript) is accessed. +For example, when accessing a property `let y = a.x`, the compiler could potentially +use `get` accessor or the `_read` accessor. Similarly, for a modification like +`a.x += 1`, the compiler could use `get` + `set` or it could use `_modify`. + +The access pattern can differ for call-sites which can/cannot see the underlying +implementation. Clients which cannot see the underlying implementation are said +to use the conservative access pattern. + ## archetype A placeholder for a generic parameter or an associated type within a @@ -172,10 +183,13 @@ the same bug ("I have a dup of this"); as a verb, the act of marking a bug written "dupe". Pronounced the same way as the first syllable of "duplicate", which for most American English speakers is "doop". -## existential +## existential type -A value whose type is a protocol composition (including a single protocol -and *zero* protocols; the latter is the `Any` type). +A type that is a protocol composition (including a single protocol and *zero* protocols; the latter is the `Any` type). + +## existential value + +A value of [existential type](#existential-type), commonly referred to simply as an "existential". ## explicit module build @@ -473,6 +487,38 @@ See [mandatory passes](#mandatory-passes--mandatory-optimizations). An implicit representation change that occurs when a value is used with a different [abstraction pattern](#abstraction-pattern) from its current representation. +## realization + +The process of initializing an ObjC class for use by the ObjC runtime. +This consists of allocating runtime tracking data, fixing up method lists +and attaching categories. + +This is distinct from the initialization performed by `+initialize`, which +happens only when the first message (other than `+load`) is sent to the class. + +The order of operations is: realization, followed by `+load` (if present), +followed by `+initialize`. There are few cases where these can happen +at different times. + +- Common case (no `+load` or special attributes): Realization is lazy and + happens when the first message is sent to a class. After that, `+initialize` + is run. +- If the class has a `+load` method: `+load`, as the name suggests, runs at + load time; it is the ObjC equivalent of a static initializer in C++. For + such a class, realization eagerly happens at load time before `+load` runs. + (Fun aside: C++ static initializers run after `+load`.) `+initialize` still + runs lazily on the first message. +- If the class is marked [`@_objc_non_lazy_realization`](/docs/ReferenceGuides/UnderscoredAttributes.md#_objc_non_lazy_realization): + Realization happens at load time. `+initialize` still runs lazily on the first + message. + +It's possible to create a class that is realized but not initialized by +using a runtime function like `objc_getClass` before the class has been used. + +See also: Mike Ash's blog post on +[Objective-C Class Loading and Initialization](https://www.mikeash.com/pyblog/friday-qa-2009-05-22-objective-c-class-loading-and-initialization.html), +which covers `+load` and `+initialize` in more detail. + ## refutable pattern A pattern that may not always match. These include patterns such as: diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index 4cb08a50833cd..3cdf7fceccf83 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -145,14 +145,22 @@ The following changes are permitted: body, not the labels that are part of the function's full name). - Reordering generic requirements (but not the generic parameters themselves). - Adding a default argument expression to a parameter. -- Changing or removing a default argument is a `binary-compatible - source-breaking change`. +- Adding, changing, reordering, or removing property wrappers that either are + implementation-detail or in a composition where the outermost wrapper is + implementation-detail. +- Changing or removing a default argument is a `binary-compatible source-breaking change`. - Adding or removing the ``@discardableResult`` and ``@warn_unqualified_access`` attributes. No other changes are permitted; the following are particularly of note: - An ABI-public function may not change its parameters or return type. +- An ABI-public function may not, in any way, change API-level property + wrappers or compositions where the outermost wrapper is + API-level. +- An ABI-public function may not change an API-level property-wrapper attribute + to an implementation-detail one and vice versa, if it is the only wrapper + applied to a given parameter or the outermost wrapper in a composition. - An ABI-public function may not change its generic requirements. - An ABI-public function may not change its external parameter names (labels). - An ABI-public function may not add, remove, or reorder parameters, whether or @@ -261,15 +269,16 @@ Always Emit Into Client A function, computed property or subscript annotated as ``@_alwaysEmitIntoClient`` is similar to an ``@inlinable`` declaration, except the declaration is not part of the module's ABI, meaning that the client must always emit -their own copy. - -As a result, removing a declaration annotated as ``@_alwaysEmitIntoClient`` -is a binary-compatible source-breaking change. - -.. admonition:: TODO +their own copy. As a result: - The implementation of ``@_alwaysEmitIntoClient`` is incomplete and - should probably graduate to having its own evolution proposal. +- Removing a declaration annotated as ``@_alwaysEmitIntoClient`` is a + `binary-compatible source-breaking change`. +- Adding ``@_alwaysEmitIntoClient`` to a declaration breaks ABI but is a + source-compatible change. +- Removing ``@_alwaysEmitIntoClient`` from a declaration is a + binary-compatible change. It also requires updating the availability + to at least the OS version where the attribute was removed. As a result, + it may be a source-breaking change. Default Argument Expressions ---------------------------- @@ -341,7 +350,8 @@ the following changes are permitted: - Reordering any existing members, including stored properties (unless the struct is marked ``@frozen``; see below). -- Adding any new members, including stored properties. +- Adding any new members, including stored properties (see below for an + exception). - Changing existing properties from stored to computed or vice versa (unless the struct is marked ``@frozen``; see below). - As a special case of the above, adding or removing ``lazy`` from a stored @@ -362,6 +372,9 @@ change even with Swift's synthesis of memberwise and no-argument initializers; these initializers are always ``internal`` and thus not exposed to clients outside the module. +Adding a new stored property with availability newer than the deployment +target for the library is an error. + It is not safe to add or remove ``mutating`` or ``nonmutating`` from a member or accessor within a struct. @@ -413,6 +426,9 @@ stored subscripts. This means that the following changes are permitted: accessor bodies, not the labels that are part of the subscript's full name). - Reordering generic requirements (but not the generic parameters themselves). - Adding a default argument expression to an index parameter. +- Adding, changing, reordering, or removing property wrappers that either are + implementation-detail or in a composition where the outermost wrapper is + implementation-detail. - Changing or removing a default argument is a `binary-compatible source-breaking change`. @@ -496,9 +512,10 @@ without breaking binary compatibility. As with structs, this results in a fair amount of indirection when dealing with enum values, in order to potentially accommodate new values. More specifically, the following changes are permitted: -- Adding a new case (unless the enum is marked ``@frozen``; see below). +- Adding a new case (see below for exceptions around ``@frozen`` and + availability). - Reordering existing cases is a `binary-compatible source-breaking change` - (unless the struct is marked ``@frozen``; see below). In particular, both + (unless the enum is marked ``@frozen``; see below). In particular, both CaseIterable and RawRepresentable default implementations may affect client behavior. - Adding a raw type to an enum that does not have one. @@ -515,6 +532,10 @@ accommodate new values. More specifically, the following changes are permitted: representation for the value, just as it may discard fields of structs that are provably never accessed. +Adding a new case with one or more associated values and with availability +newer than the deployment target for the library is an error. +This limitation is similar to the limitation for stored properties on structs. + Adding or removing the ``@objc`` attribute from an enum is not permitted; this affects the enum's memory representation and is not backwards-compatible. @@ -778,6 +799,9 @@ counterparts with a few small changes: - Changing index parameter internal names is permitted. - Reordering generic requirements (but not the generic parameters themselves) is permitted. +- Adding, changing, reordering, or removing property wrappers that either are + implementation-detail or in a composition where the outermost wrapper is + implementation-detail. - Adding a default argument expression to an index parameter is permitted. - Changing or removing a default argument is a `binary-compatible source-breaking change`. @@ -822,6 +846,14 @@ Additionally, non-protocol extensions allow a few additional changes: protocol extensions that do *not* satisfy protocol requirements are not overridable, even when the conforming type is a class. +.. note:: + + It is an ABI incompatible change to move a member to an extension with + different constraints. Similarly, it is an ABI incompatible change to move a member + from a constrained extension back to its base type. Note that this is the case + even if the constraints from the extension are restated as constraints in the + where clause of e.g. a function or subscript member. + Operators and Precedence Groups ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/OpenBSD.md b/docs/OpenBSD.md index fa67670ffe529..a0793660b907c 100644 --- a/docs/OpenBSD.md +++ b/docs/OpenBSD.md @@ -82,6 +82,7 @@ $ ./utils/build-script \ -DSWIFT_BUILD_SOURCEKIT=OFF,\ -DSWIFT_BUILD_SYNTAXPARSERLIB=OFF,\ -DSWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY=OFF,\ + -DSWIFT_IMPLICIT_CONCURRENCY_IMPORT=OFF,\ -DSWIFT_USE_LINKER=lld,\ -DCMAKE_INSTALL_DIR=/usr/local" ``` diff --git a/docs/README.md b/docs/README.md index 7ebcc82af6539..c9d89fc9aa2bb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -177,9 +177,17 @@ documentation, please create a thread on the Swift forums under the - [Lexicon.md](/docs/Lexicon.md): Canonical reference for terminology used throughout the project. - +- [UnderscoredAttributes.md](/docs/ReferenceGuides/UnderscoredAttributes.md): + Documents semantics for underscored (unstable) attributes. + ### ABI +- [CallConvSummary.rst](/docs/ABI/CallConvSummary.rst): + A concise summary of the calling conventions used for C/C++, Objective-C + and Swift on Apple platforms. Contains references to source documents, + where further detail is required. +- [CallingConvention.rst](/docs/ABI/CallingConvention.rst): + Describes in detail the Swift calling convention. - [GenericSignature.md](/docs/ABI/GenericSignature.md): Describes what generic signatures are and how they are used in the ABI, including the algorithms for minimization and canonicalization. @@ -191,9 +199,6 @@ documentation, please create a thread on the Swift forums under the - [Mangling.rst](/docs/ABI/Mangling.rst): Describes the stable mangling scheme, which produces unique symbols for ABI-public declarations. -- [RegisterUsage.md](/docs/ABI/RegisterUsage.md): - Summarizes the register allocation for ARM64 and x86_64 calling conventions, - including the context register (self) and error return register. - [TypeLayout.rst](/docs/ABI/TypeLayout.rst): Describes the algorithms/strategies for fragile struct and tuple layout; class layout; fragile enum layout; and existential container layout. diff --git a/docs/ReferenceGuides/UnderscoredAttributes.md b/docs/ReferenceGuides/UnderscoredAttributes.md new file mode 100644 index 0000000000000..2110657901339 --- /dev/null +++ b/docs/ReferenceGuides/UnderscoredAttributes.md @@ -0,0 +1,614 @@ +# Underscored Attributes Reference + +**WARNING:** This information is provided primarily for compiler and standard +library developers. Usage of these attributes outside of the Swift monorepo +is STRONGLY DISCOURAGED. + +The Swift reference has a chapter discussing [stable attributes][Attributes]. +This document is intended to serve as a counterpart describing underscored +attributes, whose semantics are subject to change and most likely need to +go through the Swift evolution process before being stabilized. + +[Attributes]: https://docs.swift.org/swift-book/ReferenceManual/Attributes.html + +The attributes are organized in alphabetical order. + +## `@_alignment(numericValue)` + +Allows controlling the alignment of a type. + +The alignment value specified must be a power of two, and cannot be less +than the "natural" alignment of the type that would otherwise be used by +the Swift ABI. This attribute is intended for the SIMD types in the standard +library which use it to increase the alignment of their internal storage to at +least 16 bytes. + +## `@_alwaysEmitIntoClient` + +Forces the body of a function to be emitted into client code. + +Note that this is distinct from `@inline(__always)`; it doesn't force inlining +at call-sites, it only means that the implementation is compiled into the +module which uses the code. + +This means that `@_alwaysEmitIntoClient` definitions are _not_ part of the +defining module's ABI, so changing the implementation at a later stage +does not break ABI. + +Most notably, default argument expressions are implicitly +`@_alwaysEmitIntoClient`, which means that adding a default argument to a +function which did not have one previously does not break ABI. + +## `@_assemblyVision` + +Forces emission of assembly vision remarks for a function or method, showing +where various runtime calls and performance impacting hazards are in the code +at source level after optimization. + +Adding this attribute to a type leads to remarks being emitted for all methods. + +## `@_borrowed` + +Indicates that the [conservative access pattern](/docs/Lexicon.md#access-pattern) +for some storage (a subscript or a property) should use the `_read` accessor +instead of `get`. + +For more details, see the forum post on +[Value ownership when reading from a storage declaration](https://forums.swift.org/t/value-ownership-when-reading-from-a-storage-declaration/15076). + +## `@_cdecl("cName")` + +Similar to `@_silgen_name` but uses the C calling convention. + +This attribute doesn't have very well-defined semantics. Type bridging is not +done, so the parameter and return types should correspond directly to types +accessible in C. In most cases, it is preferable to define a static method +on an `@objc` class instead of using `@_cdecl`. + +For potential ideas on stabilization, see +[Formalizing `@cdecl`](https://forums.swift.org/t/formalizing-cdecl/40677). + +## `@_disfavoredOverload` + +Marks an overload that the type checker should try to avoid using. When the +expression type checker is considering overloads, it will prefer a solution +with fewer `@_disfavoredOverload` declarations over one with more of them. + +Use `@_disfavoredOverload` to work around known bugs in the overload +resolution rules that cannot be immediately fixed without a source break. +Don't use it to adjust overload resolution rules that are otherwise sensible +but happen to produce undesirable results for your particular API; it will +likely be removed or made into a no-op eventually, and then you will be +stuck with an overload set that cannot be made to function in the way +you intend. + +`@_disfavoredOverload` was first introduced to work around a bug in overload +resolution with `ExpressibleByXYZLiteral` types. The type checker strongly +prefers to give literals their default type (e.g. `Int` for +`ExpressibleByIntegerLiteral`, `String` for `ExpressibleByStringLiteral`, +etc.). If an API should prefer some other type, but accept the default too, + marking the declaration taking the default type with `@_disfavoredOverload` + gives the desired behavior: + +```swift +extension LocalizedStringKey: ExpressibleByStringLiteral { ... } + +extension Text { + // We want `Text("foo")` to use this initializer: + init(_ key: LocalizedStringKey) { ... } + + // But without @_disfavoredOverload, it would use this one instead, + // because that lets it give the literal its default type: + @_disfavoredOverload init(_ str: S) { ... } +} +``` + +## `@_dynamicReplacement(for: targetFunc(label:))` + +Marks a function as the dynamic replacement for another `dynamic` function. +This is similar to method swizzling in other languages such as Objective-C, +except that the replacement happens at program start (or loading a shared +library), instead of at an arbitrary point in time. + +For more details, see the forum post on +[dynamic method replacement](https://forums.swift.org/t/dynamic-method-replacement/16619). + +## `@_distributedActorIndependent` + +Marks a specific property of a distributed actor to be available even if the +actor is remote. + +This only applies to two distributed actor properties `address` and `transport`. +It cannot be safely declared on any properties defined by ordinary Swift code. + +## `@_effects(effectname)` + +Tells the compiler that the implementation of the defined function is limited +to certain side effects. The attribute argument specifies the kind of side +effect limitations that apply to the function including any other functions +it calls. This is used to provide information to the optimizer that it can't +already infer from static analysis. + +Changing the implementation in a way that violates the optimizer's assumptions +about the effects results in undefined behavior. + +For more details, see [OptimizerEffects.rst](/docs/proposals/OptimizerEffects.rst). + +## `@_exported` + +Re-exports all declarations from an imported module. + +This attribute is most commonly used by overlays. + +```swift +// module M +public func f() {} + +// module N +@_exported import M + +// module P +import N +func g() { + N.f() // OK +} +``` + +## `@_fixed_layout` + +Same as `@frozen` but also works for classes. + +With `@_fixed_layout` classes, vtable layout still happens dynamically, so +non-public virtual methods can be removed, new virtual methods can be added, +and existing virtual methods can be reordered. + +## `@_hasInitialValue` + +Marks that a property has an initializing expression. + +This information is lost in the swiftinterface, +but it is required as it results in a symbol for the initializer +(if a class/struct `init` is inlined, it will call initializers +for properties that it doesn't initialize itself). +This information is necessary for correct TBD file generation. + +## `@_hasMissingDesignatedInitializers` + +Indicates that there may be designated initializers that are +not printed in the swiftinterface file for a particular class. + +This attribute is needed for the initializer model to maintain correctness when +[library evolution](/docs/LibraryEvolution.rst) is enabled. This is because a +class may have non-public designated initializers, and Swift allows the +inheritance of convenience initializers if and only if the subclass overrides +(or has synthesized overrides) of every designated initializer in its +superclass. Consider the following code: + +```swift +// Lib.swift +open class A { + init(invisible: ()) {} + + public init(visible: ()) {} + public convenience init(hi: ()) { self.init(invisible: ()) } +} + +// Client.swift +class B : A { + var x: String + + public override init(visible: ()) { + self.x = "Garbage" + super.init(visible: ()) + } +} +``` + +In this case, if `B` were allowed to inherit the convenience initializer +`A.init(invisible:)` then an instance created via `B(hi: ())` would fail +to initialize `B.x` resulting in a memory safety hole. What's worse is +there is no way to close this safety hole because the user cannot override +the invisible designated initializer because they lack sufficient visibility. + +## `@_hasStorage` + +Marks a property as being a stored property in a swiftinterface. + +For `@frozen` types, the compiler needs to be able to tell whether a particular +property is stored or computed to correctly perform type layout. + +```swift +@frozen struct S { + @_hasStorage var x: Int { get set } // stored + var y: Int { get set } // computed +} +``` + +## `@_implementationOnly` + +Used to mark an imported module as an implementation detail. +This prevents types from that module being exposed in API +(types of public functions, constraints in public extension etc.) +and ABI (usage in `@inlinable` code). + +## `@_implements(ProtocolName, Requirement)` + +An attribute that indicates that a function with one name satisfies +a protocol requirement with a different name. This is especially useful +when two protocols declare a requirement with the same name, but the +conforming type wishes to offer two separate implementations. + +```swift +protocol P { func foo() } + +protocol Q { func foo() } + +struct S : P, Q { + @_implements(P, foo()) + func foo_p() {} + @_implements(Q, foo()) + func foo_q() {} +} +``` + +## `@_implicitSelfCapture` + +Allows access to `self` inside a closure without explicitly capturing it, +even when `Self` is a reference type. + +```swift +class C { + func f() {} + func g(_: @escaping () -> Void { + g({ f() }) // error: call to method 'f' in closure requires explicit use of 'self' + } + func h(@_implicitSelfCapture _: @escaping () -> Void) { + h({ f() }) // ok + } +} +``` + +## `@_inheritActorContext` + +(Note that it is "inherit", not "inherits", unlike below.) + +Marks that a `@Sendable async` closure argument should inherit the actor +context (i.e. what actor it should be run on) based on the declaration site +of the closure. This is different from the typical behavior, where the closure +may be runnable anywhere unless its type specifically declares that it will +run on a specific actor. + +## `@_inheritsConvenienceInitializers` + +An attribute that signals that a class declaration inherits its convenience +initializers from its superclass. This implies that all designated initializers +-- even those that may not be visible in a swiftinterface file -- are +overridden. This attribute is often printed alongside +`@_hasMissingDesignatedInitializers` in this case. + +## `@_marker` + +Indicates that a protocol is a marker protocol. Marker protocols represent some +meaningful property at compile-time but have no runtime representation. + +For more details, see [SE-0302](https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md#marker-protocols), which introduces marker protocols. +At the moment, the language only has one marker protocol: `Sendable`. + +Fun fact: Rust has a very similar concept called +[marker traits](https://doc.rust-lang.org/std/marker/index.html), +including one called `Send`, +which inspired the design of `Sendable`. + +## `@_nonEphemeral` + +Marks a function parameter that cannot accept a temporary pointer produced from +an inout-to-pointer, array-to-pointer, or string-to-pointer conversion. Such a +parameter may only accept a pointer that is guaranteed to outlive the duration +of the function call. + +Attempting to pass a temporary pointer to an `@_nonEphemeral` parameter will +produce a warning. This attribute is primarily used within the standard library +on the various `UnsafePointer` initializers to warn users about +the undefined behavior caused by using a temporary pointer conversion as an +argument: + +```swift +func baz() { + var x = 0 + + // warning: Initialization of 'UnsafePointer' results in a dangling pointer + let ptr = UnsafePointer(&x) + + // warning: Initialization of 'UnsafePointer' results in a dangling pointer + let ptr2 = UnsafePointer([1, 2, 3]) +} +``` + +The temporary pointer conversion produces a pointer that is only +guaranteed to be valid for the duration of the call to the initializer, +and becomes invalid once the call ends. +So the newly created `UnsafePointer` will be dangling. + +One exception to this is that inout-to-pointer conversions +on static stored properties and global stored properties +produce non-ephemeral pointers, as long as they have no observers: + +```swift +var global = 0 + +struct S { + static var staticVar = 0 +} + +func baz() { + let ptr = UnsafePointer(&global) // okay + let ptr2 = UnsafePointer(&S.staticVar) // okay +} +``` + +Additionally, if they are of a tuple or struct type, their stored members +without observers may also be passed inout as non-ephemeral pointers. + +For more details, see the educational note on +[temporary pointer usage](/userdocs/diagnostics/temporary-pointers.md). + +## `@_nonoverride` + +Marks a declaration that is not an override of another. +When the `-warn-implicit-overrides` flag is used, a warning is issued when a +protocol restates a requirement from another protocol it refines without +annotating the declaration with either `override` or `@_nonoverride`. + +An `override` annotation causes the overriding declaration to be treated +identically to the overridden declaration; a conforming type can only +provide one implementation ("witness"). Restating a protocol requirement +and then marking it as an `override` is generally only needed to help +associated type inference, and many `override` annotations correlate +closely with ABI FIXMEs. + +Meanwhile, `@_nonoverride` is the "opposite" of `override`, allowing two +protocol requirements to be treated independently; a conforming type can +provide a distinct witness for each requirement (for example, by using +`@_implements`). Use `@_nonoverride` when semantics differ between the +two requirements. For example, `BidirectionalCollection.index(_:offsetBy:)` +allows negative offsets, while `Collection.index(_:offsetBy:)` does not, +and therefore the former is marked `@_nonoverride`. + +The `@_nonoverride` annotation can also be specified on class members in +addition to protocol members. Since it is the "opposite" of `override`, it can +be used to suppress "near-miss" diagnostics for declarations that are similar +to but not meant to override another declaration, and it can be used to +intentionally break the override chain, creating an overload instead of an +override. + +This attribute and the corresponding `-warn-implicit-overrides` flag are +used when compiling the standard library and overlays. + +## `@_objc_non_lazy_realization` + +Marks a class as being non-lazily (i.e. eagerly) [realized](/docs/Lexicon.md#realization). + +This is used for declarations which may be statically referenced and wouldn't +go through the normal lazy realization paths. For example, the empty array +class must be non-lazily realized, because empty arrays are statically +allocated. Otherwise, passing the empty array object to other code without +triggering realization could allow for the unrealized empty array class to be +passed to ObjC runtime APIs which only operate on realized classes, resulting +in a crash. + +## `@_optimize([none|size|speed])` + +Controls the compiler's optimization mode. This attribute is analogous to the +command-line flags `-Onone`, `-Osize` and `-Ospeed` respectively, but limited +to a single function body. + +`@_optimize(none)` is handy for diagnosing and reducing compiler bugs as well +as improving debugging in Release builds. + +## `@_originallyDefinedIn(module: "ModuleName", availabilitySpec...)` + +Marks a declaration as being originally defined in a different module, +changing the name mangling. This can be used to move declarations +from a module to one of the modules it imports without breaking clients. + +Consider the following example where a framework ToasterKit needs +to move some APIs to a lower-level framework ToasterKitCore. +Here are the necessary changes: + +1. Add a linker flag `-reexport_framework ToasterKitCore` for ToasterKit. + This ensures all symbols defined in ToasterKitCore will be accessible during + runtime via ToasterKit, so existing apps continue to run. +2. In ToasterKit, use `@_exported import ToasterKitCore`. + This ensures existing source code that only imports ToasterKit continues to + type-check. +3. Move the necessary declarations from ToasterKit to ToasterKitCore. + The moved declaration should have two attributes: + - `@available` indicating when the declaration was introduced in ToasterKit. + - `@_originallyDefinedIn` indicating the original module and when the + declaration was moved to ToasterKitCore. + ```swift + @available(toasterOS 42, *) + @_originallyDefinedIn(module: "ToasterKit", toasterOS 57) + enum Toast { + case underdone + case perfect + case burnt + } + ``` +4. Add Swift compiler flags `-Xfrontend -emit-ldadd-cfile-path -Xfrontend /tmp/t.c` + to ToasterKitCore's build settings. Add the emitted `/tmp/t.c` file to + ToasterKit's compilation. + This ensures when an app is built for deployment targets prior to the symbols' move, + the app will look for these symbols in ToasterKit instead of ToasterKitCore. + +More generally, mutliple availabilities can be specified, like so: + +```swift +@available(toasterOS 42, bowlOS 54, mugOS 54, *) +@_originallyDefinedIn(module: "ToasterKit", toasterOS 57, bowlOS 69, mugOS 69) +enum Toast { ... } +``` + +## `@_private(sourceFile: "FileName.swift")` + +Fully bypasses access control, allowing access to private declarations +in the imported module. The imported module needs to be compiled with +`-Xfrontend -enable-private-imports` for this to work. + +## `@_semantics("uniquely.recognized.id")` + +Allows the optimizer to make use of some key invariants in performance critical +data types, especially `Array`. Since the implementation of these data types +is written in Swift using unsafe APIs, without these attributes the optimizer +would need to make conservative assumptions. + +Changing the implementation in a way that violates the optimizer's assumptions +about the semantics results in undefined behavior. + +## `@_show_in_interface` + +Shows underscored protocols from the standard library in the generated interface. + +By default, SourceKit hides underscored protocols from the generated +swiftinterface (for all modules, not just the standard library), but this +attribute can be used to override that behavior for the standard library. + +## `@_silgen_name("cName")` + +Changes the symbol name for a function, similar to an ASM label in C, +except that the platform symbol mangling (leading underscore on Darwin) +is maintained. + +Since this has label-like behavior, it may not correspond to any declaration; +if so, it is assumed that the function is implemented in C. + +A function defined by `@_silgen_name` is assumed to use the Swift ABI. + +For more details, see the +[Standard Library Programmer's Manual](https://github.com/apple/swift/blob/main/docs/StandardLibraryProgrammersManual.md#_silgen_name). + +## `@_specialize(...)` + +Forces generation of a specialized implementation for a generic declaration. + +See [Generics.rst](/docs/Generics.rst) for more details. + +## `@_specializeExtension` + +Allows extending `@usableFromInline` internal types from foreign modules. +Consider the following example involving two modules: + +```swift +// Module A +@usableFromInline +internal struct S { /* ... */ } + +// Module B +import A + +@_specializeExtension +extension S { // OK + // add methods here +} + +extension S /* or A.S */ { // error: cannot find 'S' in scope +} +``` + +This ability can be used to add specializations of existing methods +in downstream libraries when used in conjunction with `@_specialize`. + +```swift +// Module A +@usableFromInline +internal struct S { + @inlinable + internal func doIt() { /* body */ } +} + +// Module B +import A + +@_specializeExtension +extension S { // ok + @_specialize(exported: true, target: doIt(), where T == Int) + public func specializedDoIt() {} +} + +// Module C +import A +import B + +func f(_ s: S) { + s.doIt() // will call specialized version of doIt() where T == Int from B +} +``` + +## `@_spi(spiName)` + +Marks a declaration as SPI (System Programming Interface), instead of API. +Modules exposing SPI and using library evolution generate an additional +`.private.swiftinterface` file (with `-emit-private-module-interface-path`) +in addition to the usual `.swiftinterface` file. This private interface exposes +both API and SPI. + +Clients can access SPI by marking the import as `@_spi(spiName) import Module`. +This design makes it easy to find out which clients are using certain SPIs by +doing a textual search. + +## `@_staticInitializeObjCMetadata` + +Indicates that a static initializer should be emitted to register the +Objective-C metadata when the image is loaded, rather than on first use of the +Objective-C metadata. + +This attribute is inferred for `NSCoding` classes that won't +have static Objective-C metadata or have an `@NSKeyedArchiveLegacy` attribute. + +## `@_transparent` + +Marks a function to be "macro-like", i.e., it is guaranteed to be inlined +in debug builds. + +See [TransparentAttr.md](/docs/TransparentAttr.md) for more details. + +## `@_typeEraser(Proto)` + +Marks a concrete nominal type as one that implements type erasure for a +protocol `Proto`. + +A type eraser has the following restrictions: + +1. It must be a concrete nominal type. +2. It must not have more restrictive access than `Proto`. +3. It must conform to `Proto`. +4. It must have an initializer of the form `init(erasing: T)`. + - Other generic requirements are permitted as long as the `init` can always + be called with a value of any type conforming to `Proto`. + - The `init` cannot have more restrictive access than `Proto`. + +This feature was designed to be used for compiler-driven type erasure for +dynamic replacement of functions with an opaque return type. + +## `@_weakLinked` + +Allows a declaration to be weakly-referenced, i.e., any references emitted by +client modules to the declaration's symbol will have weak linkage. This means +that client code will compile without the guarantee that the symbol will be +available at runtime. This requires a dynamic safety check (such as using +`dlsym (3)`); otherwise, accessing the symbol when it is unavailable leads +to a runtime crash. + +This is an unsafe alternative to using `@available`, which is statically checked. +If the availability of a library symbol is newer than the deployment target of +the client, the symbol will be weakly linked, but checking for `@available` and +`#(un)available` ensures that a symbol is not accessed when it is unavailable. + +## `@_unsafeMainActor`, `@_unsafeSendable` + +Marks a parameter's (function) type as `@MainActor` (`@Sendable`) in Swift 6 and +within Swift 5 code that has adopted concurrency, but non-`@MainActor` +(non-`@Sendable`) everywhere else. + +See the forum post on [Concurrency in Swift 5 and 6](https://forums.swift.org/t/concurrency-in-swift-5-and-6/49337) +for more details. diff --git a/docs/RequestEvaluator.md b/docs/RequestEvaluator.md index fe878a82913d7..9487c462b762d 100644 --- a/docs/RequestEvaluator.md +++ b/docs/RequestEvaluator.md @@ -21,8 +21,6 @@ All existing requests inherit the [`SimpleRequest`](https://github.com/apple/swi Each request can issue other requests, also through the evaluator. The evaluator automatically tracks such dependencies (by keeping a stack of the currently-active request evaluations). This information is valuable for a few reasons. First, it can help with debugging both correctness and performance, allowing one to visualize the dependencies evaluated when compiling a program. The current protocol has both full-graph visualization (via GraphViz output) and dependencies-for-a-single-request visualization (via a tree-like dump). Second, it ensures that we can detect cyclic dependencies (e.g., a cyclic inheritance hierarchy) correctly, because they show up as cycles in the dependency graph, allowing for proper diagnostics (for the user) and recovery (in the compiler). Third, it can eventually be leveraged to enable better incremental compilation--providing the ability to discover what information affected a particular type-checking decision, and propagate the effects of a specific change through the dependency graph. -The complete dependency graph formed by processing a source file can be visualized by passing the frontend option `-output-request-graphviz `. The dependency graph will be emitted using the [GraphViz](https://www.graphviz.org) format. - The frontend option `-debug-cycles` will provide debugging output for any cycle detected while processing the given source files. For example, running the [`circular_inheritance.swift` test](https://github.com/apple/swift/blob/main/test/decl/class/circular_inheritance.swift) from the Swift repository using this flag, e.g., ``` diff --git a/docs/SIL.rst b/docs/SIL.rst index 77be3d6325dab..488986dc55665 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -3489,7 +3489,7 @@ debug_value :: - sil-instruction ::= debug_value '[poison]'? sil-operand (',' debug-var-attr)* + sil-instruction ::= debug_value '[poison]'? sil-operand (',' debug-var-attr)* advanced-debug-var-attr* (',' 'expr' debug-info-expr)? debug_value %1 : $Int @@ -3505,11 +3505,29 @@ The operand must have loadable type. debug-var-attr ::= 'let' debug-var-attr ::= 'name' string-literal debug-var-attr ::= 'argno' integer-literal + debug-var-attr ::= 'implicit' + +:: + + advanced-debug-var-attr ::= '(' 'name' string-literal (',' sil-instruction-source-info)? ')' + advanced-debug-var-attr ::= 'type' sil-type + +:: + + debug-info-expr ::= di-expr-operand (':' di-expr-operand)* + di-expr-operand ::= di-expr-operator (':' sil-operand)* + di-expr-operator ::= 'op_fragment' There are a number of attributes that provide details about the source variable that is being described, including the name of the variable. For function and closure arguments ``argno`` is the number -of the function argument starting with 1. +of the function argument starting with 1. A compiler-generated source +variable will be marked ``implicit`` and optimizers are free to remove +it even in -Onone. The advanced debug variable attributes represent source +locations and type of the source variable when it was originally declared. +It is useful when we're indirectly associating the SSA value with the +source variable (via di-expression, for example) in which case SSA value's +type is different from that of source variable. If the '[poison]' flag is set, then all references within this debug value will be overwritten with a sentinel at this point in the @@ -3520,12 +3538,32 @@ generated until OSSA islowered. They are not expected to be serialized within the module, and the pipeline is not expected to do any significant code motion after lowering. +Debug info expression (di-expression) is a powerful method to connect SSA +value with the source variable in an indirect fashion. For example, +we can use the ``op_fragment`` operator to specify that the SSA value +is originated from a struct field inside the source variable (which has +an aggregate data type). Di-expression in SIL works similarly to LLVM's +``!DIExpression`` metadata. Where both of them adopt a stack based +execution model to evaluate the expression. The biggest difference between +them is that LLVM always represent ``!DIExpression`` elements as 64-bit +integers, while SIL's di-expression can have elements with various types, +like AST nodes or strings. Here is an example:: + + struct MyStruct { + var x: Int + var y: Int + } + ... + debug_value %1 : $Int, var, (name "the_struct", loc "file.swift":8:7), type $MyStruct, expr op_fragment:#MyStruct.y, loc "file.swift":9:4 + +In the snippet above, source variable "the_struct" has an aggregate type ``$MyStruct`` and we use di-expression with ``op_fragment`` operator to associate ``%1`` to the ``y`` member variable inside "the_struct". Note that the extra source location directive follows rigt after ``name "the_struct"`` indicate that "the_struct" was originally declared in line 8, but not until line 9, the current ``debug_value`` instruction's source location, does member ``y`` got updated with SSA value ``%1``. + debug_value_addr ```````````````` :: - sil-instruction ::= debug_value_addr sil-operand (',' debug-var-attr)* + sil-instruction ::= debug_value_addr sil-operand (',' debug-var-attr)* advanced-debug-var-attr* (',' 'expr' debug-info-expr)? debug_value_addr %7 : $*SomeProtocol @@ -3534,6 +3572,7 @@ has changed value to the specified operand. The declaration in question is identified by the SILLocation attached to the debug_value_addr instruction. +Note that this instruction can be replaced by ``debug_value`` + di-expression operator that is equivalent to LLVM's ``DW_OP_deref``. Accessing Memory ~~~~~~~~~~~~~~~~ @@ -3613,7 +3652,7 @@ begin_borrow :: - sil-instruction ::= 'begin_borrow' sil-operand + sil-instruction ::= 'begin_borrow' '[defined]'? sil-operand %1 = begin_borrow %0 : $T @@ -3626,6 +3665,10 @@ region in between this borrow and its lifetime ending use, ``%0`` must be live. This makes sense semantically since ``%1`` is modeling a new value with a dependent lifetime on ``%0``. +The optional ``defined`` attribute specifies that the operand corresponds to a +local variable in the Swift source, so special care must be taken when moving +the end_borrow. + This instruction is only valid in functions in Ownership SSA form. end_borrow diff --git a/docs/SILProgrammersManual.md b/docs/SILProgrammersManual.md index e74e2e69f4764..35f6a88e44d34 100644 --- a/docs/SILProgrammersManual.md +++ b/docs/SILProgrammersManual.md @@ -161,6 +161,9 @@ idioms, it becomes overly burdensome to evolve these APIs over time. ## `AccessedStorage` and `AccessPath` +TODO: move this section to a separate document and refer to it from +SIL.rst. + The `AccessedStorage` and `AccessPath` types formalize memory access in SIL. Given an address-typed SIL value, it is possible to reliably identify the storage location of the accessed @@ -193,17 +196,17 @@ address is immutable for the duration of its access scope Computing `AccessedStorage` and `AccessPath` for any given SIL address involves a use-def traversal to determine the origin of the -address. It may traverse operations on address, pointer, box, and -reference types. The logic that formalizes which SIL operations may be -involved in the def-use chain is encapsulated with the -`AccessUseDefChainVisitor`. The traversal can be customized by -implementing this visitor. Customization is not expected to change the -meaning of AccessedStorage or AccessPath. Rather, it is intended for -additional pass-specific book-keeping or for higher-level convenience -APIs that operate on the use-def chain bypassing AccessedStorage -completely. - -Access def-use chains are divided by four points: the "root", the +address. It may traverse operations on values of type address, +Builtin.RawPointer, box, and reference. The logic that +formalizes which SIL operations may be involved in the def-use chain +is encapsulated with the `AccessUseDefChainVisitor`. The traversal can +be customized by implementing this visitor. Customization is not +expected to change the meaning of AccessedStorage or +AccessPath. Rather, it is intended for additional pass-specific +book-keeping or for higher-level convenience APIs that operate on the +use-def chain bypassing AccessedStorage completely. + +Access def-use chains are divided by four points: the object "root", the access "base", the outer-most "access" scope, and the "address" of a memory operation. For example: ``` @@ -222,18 +225,28 @@ memory operation. For example: end_access %access : $*S ``` +OR + +``` + %root = alloc_box $S + %base = project_box %root : ${ var S } + %access = begin_access [read] [static] %base : $*S + %address = struct_element_addr %access : $*S, #.field + %value = load [trivial] %address : $*Int64 + end_access %access : $*S +``` + #### Reference root The first part of the def-use chain computes the formal access base -from the root of the object (e.g. `alloc_ref -> -ref_element_addr`). The reference root might be a locally allocated -object, a function argument, a function result, or a reference loaded -from storage. There is no enforcement on the type of operation that -can produce a reference; however, only reference types or -Builtin.BridgeObject types are only allowed in this part of the +from the root of the object (e.g. `alloc_ref -> ref_element_addr` and +`alloc_box -> project_box`). The reference root might be a locally +allocated object, a function argument, a function result, or a +reference loaded from storage. There is no enforcement on the type of +operation that can produce a reference; however, only reference types, Builtin.BridgeObject types, and box types are allowed in this part of the def-use chain. The reference root is the greatest common ancestor in the def-use graph that can identify an object by a single SILValue. If -the root as an `alloc_ref`, then it is *uniquely identified*. The +the root is an `alloc_ref`, then it is *uniquely identified*. The def-use chain from the root to the base may contain reference casts (`isRCIdentityPreservingCast`) and phis. @@ -268,18 +281,28 @@ formal access base. The reference root is only one component of an `AccessedStorage` location. AccessedStorage also identifies the class property being accessed within that object. +A reference root may be borrowed, so the use-def path from the base to +the root may cross a borrow scope. This means that uses of one base +may not be replaced with a different base even if it has the same +AccessedStorage because they may not be contained within the same +borrow scope. However, this is the only part of the access path that +may be borrowed. Address uses with the same base can be substituted +without checking the borrow scope. + #### Access base -The access base is the SILValue produced by an instruction that -directly identifies the kind of storage being accessed without further -use-def traversal. Common access bases are `alloc_box`, `alloc_stack`, -`global_addr`, `ref_element_addr`, and function arguments (see +The access base is the address or Builtin.RawPointer type SILValue +produced by an instruction that directly identifies the kind of +storage being accessed without further use-def traversal. Common +access bases are `alloc_stack`, `global_addr`, +`ref_element_addr`, `project_box`, and function arguments (see `AccessedStorage::Kind`). The access base is the same as the "root" SILValue for all storage -kinds except global and class storage. Global storage has no root. For -class storage the root is the SILValue that identifies object, -described as the "reference root" above. +kinds except global and reference storage. Reference storage includes +class, tail and box storage. Global storage has no root. For reference +storage the root is the SILValue that identifies object, described as +the "reference root" above. "Box" storage is uniquely identified by an `alloc_box` instruction. Therefore, we consider the `alloc_box` to be the base of @@ -287,10 +310,16 @@ the access. Box storage does not apply to all box types or box projections, which may instead originate from arguments or indirect enums for example. +An access scope, identified by a `begin_access` marker, may only occur +on the def-use path between the access base and any address +projections. The def-use path from the root to the base cannot cross +an access scope. Likewise, the def-use between an access projection +and the memory operation cannot cross an access scope. + Typically, the base is the address-type source operand of a `begin_access`. However, the path from the access base to the `begin_access` may include *storage casts* (see -`isAccessedStorageCast`). It may involve address, pointer, and box +`isAccessedStorageCast`). It may involve address an pointer types, and may traverse phis. For some kinds of storage, the base may itself even be a non-address pointer. For phis that cannot be uniquely resolved, the base may even be a box type. @@ -322,9 +351,9 @@ which address storage is always uniquely determined. Currently, if a (non-address) phi on the access path from `base` to `access` does not have a common base, then it is considered an invalid access (the AccessedStorage object is not valid). SIL verification ensures that a -formal access always has valid AccessedStorage (WIP). In other words, the -source of a `begin_access` marker must be a single, non-phi base. In -the future, for further simplicity, we may generally disallow box and +formal access always has valid AccessedStorage (WIP). In other words, +the source of a `begin_access` marker must be a single, non-phi +base. In the future, for further simplicity, we may also disallow pointer phis unless they have a common base. Not all SIL memory access is part of a formal access, but the @@ -334,8 +363,8 @@ the use-def search does not begin at a `begin_access` marker. For non-formal access, SIL verification is not as strict. An invalid access is allowed, but handled conservatively. This is safe as long as those non-formal accesses can never alias with class and global -storage. Class and global access is always guarded by formal access -markers--at least until static markers are stripped from SIL. +storage. Class and global access must always be guarded by formal +access markers--at least until static markers are stripped from SIL. #### Nested access diff --git a/docs/Testing.md b/docs/Testing.md index 6b3e211bf8c00..0c71346fee1b3 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -1,4 +1,3 @@ - # Testing Swift This document describes how we test the Swift compiler, the Swift runtime, and @@ -27,22 +26,6 @@ We use multiple approaches to test the Swift toolchain. [Smoke testing](ContinuousIntegration.md#smoke-testing) skips the iOS, tvOS, and watchOS platforms. -The [test/lit.cfg](https://github.com/apple/swift/blob/main/test/lit.cfg) -uses an iOS 10.3 simulator configuration named "iPhone 5" for 32-bit testing. - -1. Download and install the iOS 10.3 simulator runtime, in Xcode's - [Components](https://help.apple.com/xcode/#/deva7379ae35) preferences. - -2. Create an "iPhone 5" simulator configuration, either in Xcode's - [Devices and Simulators](https://help.apple.com/xcode/#/devf225e58da) - window, or with the command line: - - ```sh - xcrun simctl create 'iPhone 5' com.apple.CoreSimulator.SimDeviceType.iPhone-5 com.apple.CoreSimulator.SimRuntime.iOS-10-3 - ``` - -3. Append `--ios` to the `utils/build-script` command line (see below). - ### Testsuite subsets The testsuite is split into five subsets: @@ -367,6 +350,9 @@ code for the target that is not the build machine: * ``%target-cc-options``: the clang flags to setup the target with the right architecture and platform version. +* ``%target-sanitizer-opt``: if sanitizers are enabled for the build, the + corresponding ``-fsanitize=`` option. + * ``%target-triple``: a triple composed of the ``%target-cpu``, the vendor, the ``%target-os``, and the operating system version number. Possible values include ``i386-apple-ios7.0`` or ``armv7k-apple-watchos2.0``. @@ -398,6 +384,16 @@ code for the target that is not the build machine: Add ``REQUIRES: static_stdlib`` to the test. +* ``%target-rtti-opt``: the ``-frtti`` or ``-fno-rtti`` option required to + link with the Swift libraries on the target platform. + +* ``%target-cxx-lib``: the argument to add to the command line when using + ``swiftc`` and linking in a C++ object file. Typically ``-lc++`` or + ``-lstdc++`` depending on platform. + +* ``%target-msvc-runtime-opt``: for Windows, the MSVC runtime option, e.g. + ``-MD``, to use when building C/C++ code to link with Swift. + Always use ``%target-*`` substitutions unless you have a good reason. For example, an exception would be a test that checks how the compiler handles mixing module files for incompatible platforms (that test would need to compile diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index ef1683e6881cc..666607955a1ef 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -11,6 +11,7 @@ The commands below (with the exception of installing Visual Studio) must be ente An easy way to get most of the tools to build Swift is using the [Visual Studio installer](https://www.visualstudio.com/downloads/). This command installs all needed Visual Studio components as well as Python, Git, CMake and Ninja: ``` +curl.exe -sOL https://aka.ms/vs/16/release/vs_community.exe vs_community ^ --add Component.CPython3.x64 ^ --add Microsoft.VisualStudio.Component.Git ^ @@ -19,6 +20,7 @@ vs_community ^ --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ^ --add Microsoft.VisualStudio.Component.Windows10SDK ^ --add Microsoft.VisualStudio.Component.Windows10SDK.17763 +del /q vs_community.exe ``` If you prefer you can install everything by hand, but make sure to include "Programming Languages|Visual C++" and "Windows and Web Development|Universal Windows App Development|Windows SDK" in your installation. The components listed above are required. @@ -135,10 +137,12 @@ cmake -B "S:\b\1" ^ ninja -C S:\b\1 ``` +> **NOTE:** Linking with debug information (`-D LLVM_ENABLE_PDB=YES`) is very memory intensive. When building with parallel jobs, it is possible to consume upwards of 32 GiB of RAM. You can append `-D LLVM_PARALLEL_LINK_JOBS=N -D DLLVM_PARALLEL_LINK_JOBS=N` to reduce the number of parallel link operations to `N` which should help reduce the memory pressure. You may need to set this to a low number (e.g. 1) if you see build failures due to memory exhaustion. + ## Running Swift tests on Windows ```cmd -path S:\Library\icu-67\usr\bin;S:\b\1\bin;S:\b\1\tools\swift\libdispatch-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin +path S:\Library\icu-67\usr\bin;S:\b\1\bin;S:\b\1\tools\swift\libdispatch-windows-x86_64-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin ninja -C S:\b\1 check-swift ``` @@ -177,6 +181,7 @@ cmake -B S:\b\3 ^ -D ICU_I18N_LIBRARY_RELEASE=S:\library\icu-67\usr\lib\icuin67.lib ^ -D ICU_ROOT=S:\Library\icu-67\usr ^ -D ICU_UC_LIBRARY_RELEASE=S:\Library\icu-67\usr\lib\icuuc67.lib ^ + -D LIBXML2_DEFINITIONS="/DLIBXML_STATIC" ^ -D LIBXML2_LIBRARY=S:\Library\libxml2-development\usr\lib\libxml2s.lib ^ -D LIBXML2_INCLUDE_DIR=S:\Library\libxml2-development\usr\include\libxml2 ^ -D ENABLE_TESTING=NO ^ diff --git a/docs/refactoring/SwiftLocalRefactoring.md b/docs/refactoring/SwiftLocalRefactoring.md index 6824b7e6e6f6e..70203c7d73e8d 100644 --- a/docs/refactoring/SwiftLocalRefactoring.md +++ b/docs/refactoring/SwiftLocalRefactoring.md @@ -1,3 +1,5 @@ +# Swift Local Refactoring + Xcode 9 includes a brand new refactoring engine. It can transform code locally within a single Swift source file, or globally, such as renaming a method or property that occurs in multiple files and even different languages. The logic behind local refactorings is @@ -334,7 +336,7 @@ This post just touches on some of the things that are now possible to implement If you are excited about extending the refactoring engine to implement additional transformations, Swift's [issue database](https://bugs.swift.org) contains [several ideas of refactoring transformations](https://bugs.swift.org/issues/?jql=labels%3DStarterProposal%20AND%20labels%3DRefactoring%20AND%20resolution%3DUnresolved) awaiting implementations. -For further help with implementing refactoring transformations, please see the [documentation] or feel free to ask questions on the [swift-dev](https://lists.swift.org/mailman/listinfo/swift-dev) mailing list. +For further help with implementing refactoring transformations, please feel free to ask questions on the [Swift forums](https://forums.swift.org/c/development/compiler/9). [sourcekitd]: https://github.com/apple/swift/tree/main/tools/SourceKit [ResolvedCursorInfo]: https://github.com/apple/swift/blob/60a91bb7360dde5ce9531889e0ed10a2edbc961a/include/swift/IDE/Utils.h#L158 @@ -345,5 +347,4 @@ For further help with implementing refactoring transformations, please see the [ [DiagnosticsRefactoring.def]: https://github.com/apple/swift/blob/60a91bb7360dde5ce9531889e0ed10a2edbc961a/include/swift/AST/DiagnosticsRefactoring.def [swift-refactor]: https://github.com/apple/swift/tree/60a91bb7360dde5ce9531889e0ed10a2edbc961a/tools/swift-refactor [Refactoring.cpp]: https://github.com/apple/swift/blob/60a91bb7360dde5ce9531889e0ed10a2edbc961a/lib/IDE/Refactoring.cpp -[documentation]: https://github.com/apple/swift/blob/main/docs/Refactoring.md [EditConsumer]: https://github.com/apple/swift/blob/60a91bb7360dde5ce9531889e0ed10a2edbc961a/include/swift/IDE/Utils.h#L506 diff --git a/docs/tools/swift.pod b/docs/tools/swift.pod index ead511b2ad0de..b1077e3d86a64 100644 --- a/docs/tools/swift.pod +++ b/docs/tools/swift.pod @@ -20,7 +20,7 @@ To execute a Swift program: =over -B program.swift -- +B program.swift =back diff --git a/include/swift-c/DependencyScan/DependencyScan.h b/include/swift-c/DependencyScan/DependencyScan.h index be2c9257c494c..eac9f9294848d 100644 --- a/include/swift-c/DependencyScan/DependencyScan.h +++ b/include/swift-c/DependencyScan/DependencyScan.h @@ -337,6 +337,33 @@ swiftscan_batch_scan_result_create(swiftscan_scanner_t scanner, SWIFTSCAN_PUBLIC swiftscan_import_set_t swiftscan_import_set_create( swiftscan_scanner_t scanner, swiftscan_scan_invocation_t invocation); +//=== Scanner Cache Operations --------------------------------------------===// +// The following operations expose an implementation detail of the dependency +// scanner: its module dependencies cache. This is done in order +// to allow clients to perform incremental dependency scans by having the +// scanner's state be serializable and re-usable. + +/// For the specified \c scanner instance, serialize its state to the specified file-system \c path . +SWIFTSCAN_PUBLIC void +swiftscan_scanner_cache_serialize(swiftscan_scanner_t scanner, + const char * path); + +/// For the specified \c scanner instance, load in scanner state from a file at +/// the specified file-system \c path . +SWIFTSCAN_PUBLIC bool +swiftscan_scanner_cache_load(swiftscan_scanner_t scanner, + const char * path); + +/// For the specified \c scanner instance, reset its internal state, ensuring subsequent +/// scanning queries are done "from-scratch". +SWIFTSCAN_PUBLIC void +swiftscan_scanner_cache_reset(swiftscan_scanner_t scanner); + +/// An entry point to invoke the compiler via a library call. +SWIFTSCAN_PUBLIC int invoke_swift_compiler(int argc, const char **argv); + +//===----------------------------------------------------------------------===// + SWIFTSCAN_END_DECLS #endif // SWIFT_C_DEPENDENCY_SCAN_H diff --git a/include/swift/ABI/AsyncLet.h b/include/swift/ABI/AsyncLet.h index 334c36b2b8f8d..9fa8ea953cf2a 100644 --- a/include/swift/ABI/AsyncLet.h +++ b/include/swift/ABI/AsyncLet.h @@ -36,7 +36,6 @@ class alignas(Alignment_AsyncLet) AsyncLet { constexpr AsyncLet() : PrivateData{} {} - // FIXME: not sure how many words we should reserve void *PrivateData[NumWords_AsyncLet]; // TODO: we could offer a "was awaited on" check here @@ -44,7 +43,22 @@ class alignas(Alignment_AsyncLet) AsyncLet { /// Returns the child task that is associated with this async let. /// The tasks completion is used to fulfil the value represented by this async let. AsyncTask *getTask() const; - + + // The compiler preallocates a large fixed space for the `async let`, with the + // intent that most of it be used for the child task context. The next two + // methods return the address and size of that space. + + /// Return a pointer to the unused space within the async let block. + void *getPreallocatedSpace(); + + /// Return the size of the unused space within the async let block. + static size_t getSizeOfPreallocatedSpace(); + + /// Was the task allocated out of the parent's allocator? + bool didAllocateFromParentTask(); + + /// Flag that the task was allocated from the parent's allocator. + void setDidAllocateFromParentTask(bool value = true); }; } // end namespace swift diff --git a/include/swift/ABI/Executor.h b/include/swift/ABI/Executor.h index 92f270f1795e6..8ca7bc21839bc 100644 --- a/include/swift/ABI/Executor.h +++ b/include/swift/ABI/Executor.h @@ -82,6 +82,15 @@ class ExecutorRef { return ExecutorRef(actor, 0); } + /// Given a pointer to a serial executor and its SerialExecutor + /// conformance, return an executor reference for it. + static ExecutorRef forOrdinary(HeapObject *identity, + const SerialExecutorWitnessTable *witnessTable) { + assert(identity); + assert(witnessTable); + return ExecutorRef(identity, reinterpret_cast(witnessTable)); + } + HeapObject *getIdentity() const { return Identity; } @@ -112,6 +121,9 @@ class ExecutorRef { return Identity != newExecutor.Identity; } + /// Is this executor the main executor? + bool isMainExecutor() const; + bool operator==(ExecutorRef other) const { return Identity == other.Identity; } @@ -128,6 +140,11 @@ using TaskContinuationFunction = SWIFT_CC(swiftasync) void (SWIFT_ASYNC_CONTEXT AsyncContext *); +using ThrowingTaskFutureWaitContinuationFunction = + SWIFT_CC(swiftasync) + void (SWIFT_ASYNC_CONTEXT AsyncContext *, SWIFT_CONTEXT void *); + + template class AsyncFunctionPointer; template diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 537a10a089a87..b32dc943ff5c8 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -1168,8 +1168,25 @@ struct TargetClassMetadata : public TargetAnyClassMetadata { Description = description; } + // [NOTE: Dynamic-subclass-KVO] + // + // Using Objective-C runtime, KVO can modify object behavior without needing + // to modify the object's code. This is done by dynamically creating an + // artificial subclass of the the object's type. + // + // The isa pointer of the observed object is swapped out to point to + // the artificial subclass, which has the following properties: + // - Setters for observed keys are overridden to additionally post + // notifications. + // - The `-class` method is overridden to return the original class type + // instead of the artificial subclass type. + // + // For more details, see: + // https://www.mikeash.com/pyblog/friday-qa-2009-01-23.html + /// Is this class an artificial subclass, such as one dynamically /// created for various dynamic purposes like KVO? + /// See [NOTE: Dynamic-subclass-KVO] bool isArtificialSubclass() const { assert(isTypeMetadata()); return Description == nullptr; @@ -1665,6 +1682,7 @@ struct TargetFunctionTypeMetadata : public TargetMetadata { bool isDifferentiable() const { return Flags.isDifferentiable(); } bool hasParameterFlags() const { return Flags.hasParameterFlags(); } bool isEscaping() const { return Flags.isEscaping(); } + bool hasGlobalActor() const { return Flags.hasGlobalActor(); } static constexpr StoredSize OffsetToFlags = sizeof(TargetMetadata); @@ -1702,6 +1720,31 @@ struct TargetFunctionTypeMetadata : public TargetMetadata { return TargetFunctionMetadataDifferentiabilityKind ::NonDifferentiable; } + + ConstTargetMetadataPointer * + getGlobalActorAddr() { + assert(hasGlobalActor()); + + void *endAddr = + isDifferentiable() + ? reinterpret_cast(getDifferentiabilityKindAddress() + 1) : + hasParameterFlags() + ? reinterpret_cast(getParameterFlags() + getNumParameters()) : + reinterpret_cast(getParameters() + getNumParameters()); + return reinterpret_cast< + ConstTargetMetadataPointer *>( + llvm::alignAddr( + endAddr, llvm::Align(alignof(typename Runtime::StoredPointer)))); + } + + ConstTargetMetadataPointer + getGlobalActor() const { + if (!hasGlobalActor()) + return ConstTargetMetadataPointer(); + + return *const_cast *>(this) + ->getGlobalActorAddr(); + } }; using FunctionTypeMetadata = TargetFunctionTypeMetadata; diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index d2d2c966e4b93..daabac7944398 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -45,13 +45,16 @@ enum { /// The number of words (in addition to the heap-object header) /// in a default actor. - NumWords_DefaultActor = 10, + NumWords_DefaultActor = 12, + + /// The number of words in a task. + NumWords_AsyncTask = 24, /// The number of words in a task group. NumWords_TaskGroup = 32, - /// The number of words in an AsyncLet (flags + task pointer) - NumWords_AsyncLet = 8, // TODO: not sure how much is enough, these likely could be pretty small + /// The number of words in an AsyncLet (flags + child task context & allocation) + NumWords_AsyncLet = 80, // 640 bytes ought to be enough for anyone }; struct InProcess; @@ -831,8 +834,10 @@ class TargetFunctionTypeFlags { ParamFlagsMask = 0x02000000U, EscapingMask = 0x04000000U, DifferentiableMask = 0x08000000U, + GlobalActorMask = 0x10000000U, AsyncMask = 0x20000000U, SendableMask = 0x40000000U, + // NOTE: The next bit will need to introduce a separate flags word. }; int_type Data; @@ -888,6 +893,12 @@ class TargetFunctionTypeFlags { (isSendable ? SendableMask : 0)); } + constexpr TargetFunctionTypeFlags + withGlobalActor(bool globalActor) const { + return TargetFunctionTypeFlags( + (Data & ~GlobalActorMask) | (globalActor ? GlobalActorMask : 0)); + } + unsigned getNumParameters() const { return Data & NumParametersMask; } FunctionMetadataConvention getConvention() const { @@ -912,6 +923,10 @@ class TargetFunctionTypeFlags { return bool (Data & DifferentiableMask); } + bool hasGlobalActor() const { + return bool (Data & GlobalActorMask); + } + int_type getIntValue() const { return Data; } @@ -935,7 +950,8 @@ class TargetParameterTypeFlags { ValueOwnershipMask = 0x7F, VariadicMask = 0x80, AutoClosureMask = 0x100, - NoDerivativeMask = 0x200 + NoDerivativeMask = 0x200, + IsolatedMask = 0x400, }; int_type Data; @@ -968,10 +984,17 @@ class TargetParameterTypeFlags { (Data & ~NoDerivativeMask) | (isNoDerivative ? NoDerivativeMask : 0)); } + constexpr TargetParameterTypeFlags + withIsolated(bool isIsolated) const { + return TargetParameterTypeFlags( + (Data & ~IsolatedMask) | (isIsolated ? IsolatedMask : 0)); + } + bool isNone() const { return Data == 0; } bool isVariadic() const { return Data & VariadicMask; } bool isAutoClosure() const { return Data & AutoClosureMask; } bool isNoDerivative() const { return Data & NoDerivativeMask; } + bool isIsolated() const { return Data & IsolatedMask; } ValueOwnership getValueOwnership() const { return (ValueOwnership)(Data & ValueOwnershipMask); @@ -1983,7 +2006,8 @@ enum class JobKind : size_t { DefaultActorInline = First_Reserved, DefaultActorSeparate, - DefaultActorOverride + DefaultActorOverride, + NullaryContinuation }; /// The priority of a job. Higher priorities are larger values. @@ -1998,8 +2022,45 @@ enum class JobPriority : size_t { Unspecified = 0x00, }; +/// Flags for task creation. +class TaskCreateFlags : public FlagSet { +public: + enum { + Priority = 0, + Priority_width = 8, + + Task_IsChildTask = 8, + // bit 9 is unused + Task_CopyTaskLocals = 10, + Task_InheritContext = 11, + Task_EnqueueJob = 12, + Task_AddPendingGroupTaskUnconditionally = 13, + }; + + explicit constexpr TaskCreateFlags(size_t bits) : FlagSet(bits) {} + constexpr TaskCreateFlags() {} + + FLAGSET_DEFINE_FIELD_ACCESSORS(Priority, Priority_width, JobPriority, + getPriority, setPriority) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask, + isChildTask, + setIsChildTask) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_CopyTaskLocals, + copyTaskLocals, + setCopyTaskLocals) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_InheritContext, + inheritContext, + setInheritContext) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_EnqueueJob, + enqueueJob, + setEnqueueJob) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_AddPendingGroupTaskUnconditionally, + addPendingGroupTaskUnconditionally, + setAddPendingGroupTaskUnconditionally) +}; + /// Flags for schedulable jobs. -class JobFlags : public FlagSet { +class JobFlags : public FlagSet { public: enum { Kind = 0, @@ -2012,13 +2073,14 @@ class JobFlags : public FlagSet { // Kind-specific flags. - Task_IsChildTask = 24, - Task_IsFuture = 25, - Task_IsGroupChildTask = 26, - Task_IsContinuingAsyncTask = 27, + Task_IsChildTask = 24, + Task_IsFuture = 25, + Task_IsGroupChildTask = 26, + // 27 is currently unused + Task_IsAsyncLetTask = 28, }; - explicit JobFlags(size_t bits) : FlagSet(bits) {} + explicit JobFlags(uint32_t bits) : FlagSet(bits) {} JobFlags(JobKind kind) { setKind(kind); } JobFlags(JobKind kind, JobPriority priority) { setKind(kind); @@ -2045,9 +2107,9 @@ class JobFlags : public FlagSet { FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsGroupChildTask, task_isGroupChildTask, task_setIsGroupChildTask) - FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsContinuingAsyncTask, - task_isContinuingAsyncTask, - task_setIsContinuingAsyncTask) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsAsyncLetTask, + task_isAsyncLetTask, + task_setIsAsyncLetTask) }; /// Kinds of task status record. @@ -2077,6 +2139,19 @@ enum class TaskStatusRecordKind : uint8_t { Private_RecordLock = 192 }; +/// Kinds of option records that can be passed to creating asynchronous tasks. +enum class TaskOptionRecordKind : uint8_t { + /// Request a task to be kicked off, or resumed, on a specific executor. + Executor = 0, + /// Request a child task to be part of a specific task group. + TaskGroup = 1, + /// DEPRECATED. AsyncLetWithBuffer is used instead. + /// Request a child task for an 'async let'. + AsyncLet = 2, + /// Request a child task for an 'async let'. + AsyncLetWithBuffer = 3, +}; + /// Flags for cancellation records. class TaskStatusRecordFlags : public FlagSet { public: @@ -2095,6 +2170,24 @@ class TaskStatusRecordFlags : public FlagSet { getKind, setKind) }; +/// Flags for task option records. +class TaskOptionRecordFlags : public FlagSet { +public: + enum { + Kind = 0, + Kind_width = 8, + }; + + explicit TaskOptionRecordFlags(size_t bits) : FlagSet(bits) {} + constexpr TaskOptionRecordFlags() {} + TaskOptionRecordFlags(TaskOptionRecordKind kind) { + setKind(kind); + } + + FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, TaskOptionRecordKind, + getKind, setKind) +}; + /// Kinds of async context. enum class AsyncContextKind { /// An ordinary asynchronous function. @@ -2121,7 +2214,10 @@ class AsyncContextFlags : public FlagSet { Kind_width = 8, CanThrow = 8, - ShouldNotDeallocate = 9 + + // Kind-specific flags should grow down from 31. + + Continuation_IsExecutorSwitchForced = 31, }; explicit AsyncContextFlags(uint32_t bits) : FlagSet(bits) {} @@ -2137,17 +2233,10 @@ class AsyncContextFlags : public FlagSet { /// Whether this context is permitted to throw. FLAGSET_DEFINE_FLAG_ACCESSORS(CanThrow, canThrow, setCanThrow) - /// Whether a function should avoid deallocating its context before - /// returning. It should still pass its caller's context to its - /// return continuation. - /// - /// This flag can be set in the caller to optimize context allocation, - /// e.g. if the callee's context size is known statically and simply - /// allocated as part of the caller's context, or if the callee will - /// be called multiple times. - FLAGSET_DEFINE_FLAG_ACCESSORS(ShouldNotDeallocate, - shouldNotDeallocateInCallee, - setShouldNotDeallocateInCallee) + /// See AsyncContinuationFlags::isExecutorSwitchForced. + FLAGSET_DEFINE_FLAG_ACCESSORS(Continuation_IsExecutorSwitchForced, + continuation_isExecutorSwitchForced, + continuation_setIsExecutorSwitchForced) }; /// Flags passed to swift_continuation_init. @@ -2157,6 +2246,7 @@ class AsyncContinuationFlags : public FlagSet { CanThrow = 0, HasExecutorOverride = 1, IsPreawaited = 2, + IsExecutorSwitchForced = 3, }; explicit AsyncContinuationFlags(size_t bits) : FlagSet(bits) {} @@ -2172,10 +2262,27 @@ class AsyncContinuationFlags : public FlagSet { hasExecutorOverride, setHasExecutorOverride) + /// Whether the switch to the target executor should be forced + /// by swift_continuation_await. If this is not set, and + /// swift_continuation_await finds that the continuation has + /// already been resumed, then execution will continue on the + /// current executor. This has no effect in combination with + /// pre-awaiting. + /// + /// Setting this flag when you know statically that you're + /// already on the right executor is suboptimal. In particular, + /// there's no good reason to set this if you're not also using + /// an executor override. + FLAGSET_DEFINE_FLAG_ACCESSORS(IsExecutorSwitchForced, + isExecutorSwitchForced, + setIsExecutorSwitchForced) + /// Whether the continuation is "pre-awaited". If so, it should /// be set up in the already-awaited state, and so resumptions /// will immediately schedule the continuation to begin - /// asynchronously. + /// asynchronously. The continuation must not be subsequently + /// awaited if this is set. The task is immediately treated as + /// suspended. FLAGSET_DEFINE_FLAG_ACCESSORS(IsPreawaited, isPreawaited, setIsPreawaited) diff --git a/include/swift/ABI/ObjectFile.h b/include/swift/ABI/ObjectFile.h index cd570132cdc0a..06ad7ae5f8d21 100644 --- a/include/swift/ABI/ObjectFile.h +++ b/include/swift/ABI/ObjectFile.h @@ -31,7 +31,7 @@ class SwiftObjectFileFormat { }; /// Responsible for providing the Mach-O reflection section identifiers. -class SwiftObjectFileFormatMachO : SwiftObjectFileFormat { +class SwiftObjectFileFormatMachO : public SwiftObjectFileFormat { public: llvm::StringRef getSectionName(ReflectionSectionKind section) override { switch (section) { @@ -53,7 +53,7 @@ class SwiftObjectFileFormatMachO : SwiftObjectFileFormat { }; /// Responsible for providing the ELF reflection section identifiers. -class SwiftObjectFileFormatELF : SwiftObjectFileFormat { +class SwiftObjectFileFormatELF : public SwiftObjectFileFormat { public: llvm::StringRef getSectionName(ReflectionSectionKind section) override { switch (section) { @@ -75,7 +75,7 @@ class SwiftObjectFileFormatELF : SwiftObjectFileFormat { }; /// Responsible for providing the COFF reflection section identifiers -class SwiftObjectFileFormatCOFF : SwiftObjectFileFormat { +class SwiftObjectFileFormatCOFF : public SwiftObjectFileFormat { public: llvm::StringRef getSectionName(ReflectionSectionKind section) override { switch (section) { diff --git a/include/swift/ABI/Task.h b/include/swift/ABI/Task.h index 5a2ad8e0302ff..0c5c6c0e162dc 100644 --- a/include/swift/ABI/Task.h +++ b/include/swift/ABI/Task.h @@ -34,6 +34,7 @@ class Job; struct OpaqueValue; struct SwiftError; class TaskStatusRecord; +class TaskOptionRecord; class TaskGroup; extern FullMetadata jobHeapMetadata; @@ -68,6 +69,11 @@ class alignas(2 * alignof(void*)) Job : JobFlags Flags; + // Derived classes can use this to store a Job Id. + uint32_t Id = 0; + + void *Reserved[2] = {}; + // We use this union to avoid having to do a second indirect branch // when resuming an asynchronous task, which we expect will be the // common case. @@ -110,59 +116,45 @@ class alignas(2 * alignof(void*)) Job : /// Given that we've fully established the job context in the current /// thread, actually start running this job. To establish the context /// correctly, call swift_job_run or runJobInExecutorContext. + SWIFT_CC(swiftasync) void runInFullyEstablishedContext(); /// Given that we've fully established the job context in the /// current thread, and that the job is a simple (non-task) job, /// actually start running this job. + SWIFT_CC(swiftasync) void runSimpleInFullyEstablishedContext() { - RunJob(this); + return RunJob(this); // 'return' forces tail call } }; // The compiler will eventually assume these. -static_assert(sizeof(Job) == 6 * sizeof(void*), +#if SWIFT_POINTER_IS_8_BYTES +static_assert(sizeof(Job) == 8 * sizeof(void*), + "Job size is wrong"); +#else +static_assert(sizeof(Job) == 10 * sizeof(void*), "Job size is wrong"); +#endif static_assert(alignof(Job) == 2 * alignof(void*), "Job alignment is wrong"); -/// The current state of a task's status records. -class ActiveTaskStatus { - enum : uintptr_t { - IsCancelled = 0x1, - IsLocked = 0x2, - RecordMask = ~uintptr_t(IsCancelled | IsLocked) - }; +class NullaryContinuationJob : public Job { - uintptr_t Value; +private: + AsyncTask* Task; + AsyncTask* Continuation; public: - constexpr ActiveTaskStatus() : Value(0) {} - ActiveTaskStatus(TaskStatusRecord *innermostRecord, - bool cancelled, bool locked) - : Value(reinterpret_cast(innermostRecord) - + (locked ? IsLocked : 0) - + (cancelled ? IsCancelled : 0)) {} - - /// Is the task currently cancelled? - bool isCancelled() const { return Value & IsCancelled; } - - /// Is there an active lock on the cancellation information? - bool isLocked() const { return Value & IsLocked; } - - /// Return the innermost cancellation record. Code running - /// asynchronously with this task should not access this record - /// without having first locked it; see swift_taskCancel. - TaskStatusRecord *getInnermostRecord() const { - return reinterpret_cast(Value & RecordMask); - } + NullaryContinuationJob(AsyncTask *task, JobPriority priority, AsyncTask *continuation) + : Job({JobKind::NullaryContinuation, priority}, &process), + Task(task), Continuation(continuation) {} - static TaskStatusRecord *getStatusRecordParent(TaskStatusRecord *ptr); + SWIFT_CC(swiftasync) + static void process(Job *job); - using record_iterator = - LinkedListIterator; - llvm::iterator_range records() const { - return record_iterator::rangeBeginning(getInnermostRecord()); + static bool classof(const Job *job) { + return job->Flags.getKind() == JobKind::NullaryContinuation; } }; @@ -183,6 +175,16 @@ class ActiveTaskStatus { /// it can hold, and thus must be the *last* fragment. class AsyncTask : public Job { public: + // On 32-bit targets, there is a word of tail padding remaining + // in Job, and ResumeContext will fit into that, at offset 28. + // Private then has offset 32. + // On 64-bit targets, there is no tail padding in Job, and so + // ResumeContext has offset 48. There is therefore another word + // of reserved storage prior to Private (which needs to have + // double-word alignment), which has offset 64. + // We therefore converge and end up with 16 words of storage on + // all platforms. + /// The context for resuming the job. When a task is scheduled /// as a job, the next continuation should be installed as the /// ResumeTask pointer in the job header, with this serving as @@ -193,36 +195,59 @@ class AsyncTask : public Job { /// prevent it from being corrupted in flight. AsyncContext * __ptrauth_swift_task_resume_context ResumeContext; - /// The currently-active information about cancellation. - std::atomic Status; +#if SWIFT_POINTER_IS_8_BYTES + void *Reserved64; +#endif + + struct PrivateStorage; - /// Reserved for the use of the task-local stack allocator. - void *AllocatorPrivate[4]; + /// Private storage for the use of the runtime. + struct alignas(2 * alignof(void*)) OpaquePrivateStorage { + void *Storage[14]; - /// Task local values storage container. - TaskLocal::Storage Local; + /// Initialize this storage during the creation of a task. + void initialize(AsyncTask *task); + void initializeWithSlab(AsyncTask *task, + void *slab, size_t slabCapacity); + /// React to the completion of the enclosing task's execution. + void complete(AsyncTask *task); + + /// React to the final destruction of the enclosing task. + void destroy(); + + PrivateStorage &get(); + const PrivateStorage &get() const; + }; + PrivateStorage &_private(); + const PrivateStorage &_private() const; + + OpaquePrivateStorage Private; + + /// Create a task. + /// This does not initialize Private; callers must call + /// Private.initialize separately. AsyncTask(const HeapMetadata *metadata, JobFlags flags, TaskContinuationFunction *run, AsyncContext *initialContext) : Job(flags, run, metadata), - ResumeContext(initialContext), - Status(ActiveTaskStatus()), - Local(TaskLocal::Storage()) { + ResumeContext(initialContext) { assert(flags.isAsyncTask()); + Id = getNextTaskId(); } /// Create a task with "immortal" reference counts. /// Used for async let tasks. + /// This does not initialize Private; callers must call + /// Private.initialize separately. AsyncTask(const HeapMetadata *metadata, InlineRefCounts::Immortal_t immortal, JobFlags flags, TaskContinuationFunction *run, AsyncContext *initialContext) : Job(flags, run, metadata, immortal), - ResumeContext(initialContext), - Status(ActiveTaskStatus()), - Local(TaskLocal::Storage()) { + ResumeContext(initialContext) { assert(flags.isAsyncTask()); + Id = getNextTaskId(); } ~AsyncTask(); @@ -231,30 +256,47 @@ class AsyncTask : public Job { /// in the current thread, start running this task. To establish /// the job context correctly, call swift_job_run or /// runInExecutorContext. + SWIFT_CC(swiftasync) void runInFullyEstablishedContext() { - ResumeTask(ResumeContext); + return ResumeTask(ResumeContext); // 'return' forces tail call } - + + /// Flag that this task is now running. This can update + /// the priority stored in the job flags if the priority has been + /// escalated. + /// + /// Generally this should be done immediately after updating + /// ActiveTask. + void flagAsRunning(); + void flagAsRunning_slow(); + + /// Flag that this task is now suspended. This can update the + /// priority stored in the job flags if the priority hsa been + /// escalated. Generally this should be done immediately after + /// clearing ActiveTask and immediately before enqueuing the task + /// somewhere. TODO: record where the task is enqueued if + /// possible. + void flagAsSuspended(); + void flagAsSuspended_slow(); + + /// Flag that this task is now completed. This normally does not do anything + /// but can be used to locally insert logging. + void flagAsCompleted(); + /// Check whether this task has been cancelled. /// Checking this is, of course, inherently race-prone on its own. - bool isCancelled() const { - return Status.load(std::memory_order_relaxed).isCancelled(); - } + bool isCancelled() const; // ==== Task Local Values ---------------------------------------------------- void localValuePush(const HeapObject *key, - /* +1 */ OpaqueValue *value, const Metadata *valueType) { - Local.pushValue(this, key, value, valueType); - } + /* +1 */ OpaqueValue *value, + const Metadata *valueType); - OpaqueValue* localValueGet(const HeapObject *key) { - return Local.getValue(this, key); - } + OpaqueValue *localValueGet(const HeapObject *key); - void localValuePop() { - Local.popValue(this); - } + /// Returns true if storage has still more bindings. + bool localValuePop(); // ==== Child Fragment ------------------------------------------------------- @@ -464,7 +506,13 @@ class AsyncTask : public Job { /// \c Executing, then \c waitingTask has been added to the /// wait queue and will be scheduled when the future completes. Otherwise, /// the future has completed and can be queried. - FutureFragment::Status waitFuture(AsyncTask *waitingTask); + /// The waiting task's async context will be intialized with the parameters if + /// the current's task state is executing. + FutureFragment::Status waitFuture(AsyncTask *waitingTask, + AsyncContext *waitingTaskContext, + TaskContinuationFunction *resumeFn, + AsyncContext *callerContext, + OpaqueValue *result); /// Complete this future. /// @@ -485,21 +533,35 @@ class AsyncTask : public Job { return reinterpret_cast( SchedulerPrivate[NextWaitingTaskIndex]); } + + /// Get the next non-zero Task ID. + uint32_t getNextTaskId() { + static std::atomic Id(1); + uint32_t Next = Id.fetch_add(1, std::memory_order_relaxed); + if (Next == 0) Next = Id.fetch_add(1, std::memory_order_relaxed); + return Next; + } }; // The compiler will eventually assume these. -static_assert(sizeof(AsyncTask) == 14 * sizeof(void*), +static_assert(sizeof(AsyncTask) == NumWords_AsyncTask * sizeof(void*), "AsyncTask size is wrong"); static_assert(alignof(AsyncTask) == 2 * alignof(void*), "AsyncTask alignment is wrong"); +// Libc hardcodes this offset to extract the TaskID +static_assert(offsetof(AsyncTask, Id) == 4 * sizeof(void *) + 4, + "AsyncTask::Id offset is wrong"); +SWIFT_CC(swiftasync) inline void Job::runInFullyEstablishedContext() { if (auto task = dyn_cast(this)) - task->runInFullyEstablishedContext(); + return task->runInFullyEstablishedContext(); // 'return' forces tail call else - runSimpleInFullyEstablishedContext(); + return runSimpleInFullyEstablishedContext(); // 'return' forces tail call } +// ==== ------------------------------------------------------------------------ + /// An asynchronous context within a task. Generally contexts are /// allocated using the task-local stack alloc/dealloc operations, but /// there's no guarantee of that, and the ABI is designed to permit @@ -588,6 +650,14 @@ class ContinuationAsyncContext : public AsyncContext { /// The executor that should be resumed to. ExecutorRef ResumeToExecutor; + void setErrorResult(SwiftError *error) { + ErrorResult = error; + } + + bool isExecutorSwitchForced() const { + return Flags.continuation_isExecutorSwitchForced(); + } + static bool classof(const AsyncContext *context) { return context->Flags.getKind() == AsyncContextKind::Continuation; } diff --git a/include/swift/ABI/TaskGroup.h b/include/swift/ABI/TaskGroup.h index c79d870dc8b25..8f76c34478f7c 100644 --- a/include/swift/ABI/TaskGroup.h +++ b/include/swift/ABI/TaskGroup.h @@ -39,6 +39,9 @@ class alignas(Alignment_TaskGroup) TaskGroup { /// Upon a future task's completion, offer it to the task group it belongs to. void offer(AsyncTask *completed, AsyncContext *context); + + /// Checks the cancellation status of the group. + bool isCancelled(); }; } // end namespace swift diff --git a/include/swift/ABI/TaskLocal.h b/include/swift/ABI/TaskLocal.h index 7b5c1df751bf4..cbe17932d6236 100644 --- a/include/swift/ABI/TaskLocal.h +++ b/include/swift/ABI/TaskLocal.h @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// // -// Swift ABI describing tasks. +// Swift ABI describing task locals. // //===----------------------------------------------------------------------===// @@ -69,6 +69,15 @@ class TaskLocal { // Trailing storage for the value itself. The storage will be // uninitialized or contain an instance of \c valueType. + /// Returns true if this item is a 'parent pointer'. + /// + /// A parent pointer is special kind of `Item` is created when pointing at + /// the parent storage, forming a chain of task local items spanning multiple + /// tasks. + bool isParentPointer() const { + return !valueType; + } + protected: explicit Item() : next(0), @@ -118,6 +127,8 @@ class TaskLocal { reinterpret_cast(this) + storageOffset(valueType)); } + void copyTo(AsyncTask *task); + /// Compute the offset of the storage from the base of the item. static size_t storageOffset(const Metadata *valueType) { size_t offset = sizeof(Item); @@ -172,7 +183,7 @@ class TaskLocal { /// returns, as such, any child tasks potentially accessing the value stack /// are guaranteed to be completed by the time we pop values off the stack /// (after the body has completed). - TaskLocal::Item *head; + TaskLocal::Item *head = nullptr; public: @@ -184,7 +195,22 @@ class TaskLocal { OpaqueValue* getValue(AsyncTask *task, const HeapObject *key); - void popValue(AsyncTask *task); + /// Returns `true` of more bindings remain in this storage, + /// and `false` if the just popped value was the last one and the storage + /// can be safely disposed of. + bool popValue(AsyncTask *task); + + /// Copy all task-local bindings to the target task. + /// + /// The new bindings allocate their own items and can out-live the current task. + /// + /// ### Optimizations + /// Only the most recent binding of a value is copied over, i.e. given + /// a key bound to `A` and then `B`, only the `B` binding will be copied. + /// This is safe and correct because the new task would never have a chance + /// to observe the `A` value, because it semantically will never observe a + /// "pop" of the `B` value - it was spawned from a scope where only B was observable. + void copyTo(AsyncTask *target); /// Destroy and deallocate all items stored by this specific task. /// diff --git a/include/swift/ABI/TaskOptions.h b/include/swift/ABI/TaskOptions.h new file mode 100644 index 0000000000000..950fdeb554b50 --- /dev/null +++ b/include/swift/ABI/TaskOptions.h @@ -0,0 +1,146 @@ +//===--- TaskOptions.h - ABI structures for task options --------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// +// +// Swift ABI describing task options. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ABI_TASK_OPTIONS_H +#define SWIFT_ABI_TASK_OPTIONS_H + +#include "swift/ABI/TaskLocal.h" +#include "swift/ABI/Executor.h" +#include "swift/ABI/HeapObject.h" +#include "swift/ABI/Metadata.h" +#include "swift/ABI/MetadataValues.h" +#include "swift/Runtime/Config.h" +#include "swift/Basic/STLExtras.h" +#include "llvm/Support/Casting.h" + +namespace swift { + +// ==== ------------------------------------------------------------------------ +// ==== Task Options, for creating and waiting on tasks + +/// The abstract base class for all options that may be used +/// to configure a newly spawned task. +class TaskOptionRecord { +public: + const TaskOptionRecordFlags Flags; + TaskOptionRecord *Parent; + + TaskOptionRecord(TaskOptionRecordKind kind, + TaskOptionRecord *parent = nullptr) + : Flags(kind), Parent(parent) { } + + TaskOptionRecord(const TaskOptionRecord &) = delete; + TaskOptionRecord &operator=(const TaskOptionRecord &) = delete; + + TaskOptionRecordKind getKind() const { + return Flags.getKind(); + } + + TaskOptionRecord *getParent() const { + return Parent; + } +}; + +/******************************************************************************/ +/****************************** TASK OPTIONS **********************************/ +/******************************************************************************/ + +class TaskGroupTaskOptionRecord : public TaskOptionRecord { + TaskGroup * const Group; + + public: + TaskGroupTaskOptionRecord(TaskGroup *group) + : TaskOptionRecord(TaskOptionRecordKind::TaskGroup), + Group(group) {} + + TaskGroup *getGroup() const { + return Group; + } + + static bool classof(const TaskOptionRecord *record) { + return record->getKind() == TaskOptionRecordKind::TaskGroup; + } +}; + + +/// Task option to specify on what executor the task should be executed. +/// +/// Not passing this option implies that that a "best guess" or good default +/// executor should be used instead, most often this may mean the global +/// concurrent executor, or the enclosing actor's executor. +class ExecutorTaskOptionRecord : public TaskOptionRecord { + const ExecutorRef Executor; + +public: + ExecutorTaskOptionRecord(ExecutorRef executor) + : TaskOptionRecord(TaskOptionRecordKind::Executor), + Executor(executor) {} + + ExecutorRef getExecutor() const { + return Executor; + } + + static bool classof(const TaskOptionRecord *record) { + return record->getKind() == TaskOptionRecordKind::Executor; + } +}; + +/// DEPRECATED. AsyncLetWithBufferTaskOptionRecord is used instead. +/// Task option to specify that the created task is for an 'async let'. +class AsyncLetTaskOptionRecord : public TaskOptionRecord { + AsyncLet *asyncLet; + +public: + AsyncLetTaskOptionRecord(AsyncLet *asyncLet) + : TaskOptionRecord(TaskOptionRecordKind::AsyncLet), + asyncLet(asyncLet) {} + + AsyncLet *getAsyncLet() const { + return asyncLet; + } + + static bool classof(const TaskOptionRecord *record) { + return record->getKind() == TaskOptionRecordKind::AsyncLet; + } +}; + +class AsyncLetWithBufferTaskOptionRecord : public TaskOptionRecord { + AsyncLet *asyncLet; + void *resultBuffer; + +public: + AsyncLetWithBufferTaskOptionRecord(AsyncLet *asyncLet, + void *resultBuffer) + : TaskOptionRecord(TaskOptionRecordKind::AsyncLetWithBuffer), + asyncLet(asyncLet), + resultBuffer(resultBuffer) {} + + AsyncLet *getAsyncLet() const { + return asyncLet; + } + + void *getResultBuffer() const { + return resultBuffer; + } + + static bool classof(const TaskOptionRecord *record) { + return record->getKind() == TaskOptionRecordKind::AsyncLetWithBuffer; + } +}; + +} // end namespace swift + +#endif diff --git a/include/swift/ABI/TaskStatus.h b/include/swift/ABI/TaskStatus.h index 3f6cb68264dda..774991340bc15 100644 --- a/include/swift/ABI/TaskStatus.h +++ b/include/swift/ABI/TaskStatus.h @@ -20,8 +20,8 @@ #ifndef SWIFT_ABI_TASKSTATUS_H #define SWIFT_ABI_TASKSTATUS_H -#include "swift/ABI/Task.h" #include "swift/ABI/MetadataValues.h" +#include "swift/ABI/Task.h" namespace swift { @@ -30,7 +30,7 @@ namespace swift { /// TaskStatusRecords are typically allocated on the stack (possibly /// in the task context), partially initialized, and then atomically /// added to the task with `swift_task_addTaskStatusRecord`. While -/// registered with the task, a status record should only be +/// registered with the task, a status record should only be /// modified in ways that respect the possibility of asynchronous /// access by a cancelling thread. In particular, the chain of /// status records must not be disturbed. When the task leaves @@ -51,13 +51,9 @@ class TaskStatusRecord { TaskStatusRecord(const TaskStatusRecord &) = delete; TaskStatusRecord &operator=(const TaskStatusRecord &) = delete; - TaskStatusRecordKind getKind() const { - return Flags.getKind(); - } + TaskStatusRecordKind getKind() const { return Flags.getKind(); } - TaskStatusRecord *getParent() const { - return Parent; - } + TaskStatusRecord *getParent() const { return Parent; } /// Change the parent of this unregistered status record to the /// given record. @@ -77,16 +73,9 @@ class TaskStatusRecord { /// Unlike resetParent, this assumes that it's just removing one or /// more records from the chain and that there's no need to do any /// extra cache manipulation. - void spliceParent(TaskStatusRecord *newParent) { - Parent = newParent; - } + void spliceParent(TaskStatusRecord *newParent) { Parent = newParent; } }; -inline TaskStatusRecord * -ActiveTaskStatus::getStatusRecordParent(TaskStatusRecord *ptr) { - return ptr->getParent(); -} - /// A deadline for the task. If this is reached, the task will be /// automatically cancelled. The deadline can also be queried and used /// in other ways. @@ -107,14 +96,12 @@ struct TaskDeadline { /// within the task. class DeadlineStatusRecord : public TaskStatusRecord { TaskDeadline Deadline; + public: DeadlineStatusRecord(TaskDeadline deadline) - : TaskStatusRecord(TaskStatusRecordKind::Deadline), - Deadline(deadline) {} + : TaskStatusRecord(TaskStatusRecordKind::Deadline), Deadline(deadline) {} - TaskDeadline getDeadline() const { - return Deadline; - } + TaskDeadline getDeadline() const { return Deadline; } static bool classof(const TaskStatusRecord *record) { return record->getKind() == TaskStatusRecordKind::Deadline; @@ -128,25 +115,22 @@ class ChildTaskStatusRecord : public TaskStatusRecord { public: ChildTaskStatusRecord(AsyncTask *child) - : TaskStatusRecord(TaskStatusRecordKind::ChildTask), - FirstChild(child) {} + : TaskStatusRecord(TaskStatusRecordKind::ChildTask), FirstChild(child) {} ChildTaskStatusRecord(AsyncTask *child, TaskStatusRecordKind kind) - : TaskStatusRecord(kind), - FirstChild(child) { + : TaskStatusRecord(kind), FirstChild(child) { assert(kind == TaskStatusRecordKind::ChildTask); assert(!child->hasGroupChildFragment() && - "Group child tasks must be tracked in their respective " - "TaskGroupTaskStatusRecord, and not as independent ChildTaskStatusRecord " - "records."); + "Group child tasks must be tracked in their respective " + "TaskGroupTaskStatusRecord, and not as independent " + "ChildTaskStatusRecord " + "records."); } /// Return the first child linked by this record. This may be null; /// if not, it (and all of its successors) are guaranteed to satisfy /// `isChildTask()`. - AsyncTask *getFirstChild() const { - return FirstChild; - } + AsyncTask *getFirstChild() const { return FirstChild; } static AsyncTask *getNextChildTask(AsyncTask *task) { return task->childFragment()->getNextChild(); @@ -180,25 +164,21 @@ class ChildTaskStatusRecord : public TaskStatusRecord { /// and are only tracked by their respective `TaskGroupTaskStatusRecord`. class TaskGroupTaskStatusRecord : public TaskStatusRecord { AsyncTask *FirstChild; + public: TaskGroupTaskStatusRecord() - : TaskStatusRecord(TaskStatusRecordKind::TaskGroup), - FirstChild(nullptr) {} + : TaskStatusRecord(TaskStatusRecordKind::TaskGroup), FirstChild(nullptr) { + } TaskGroupTaskStatusRecord(AsyncTask *child) - : TaskStatusRecord(TaskStatusRecordKind::TaskGroup), - FirstChild(child) {} + : TaskStatusRecord(TaskStatusRecordKind::TaskGroup), FirstChild(child) {} - TaskGroup* getGroup() { - return reinterpret_cast(this); - } + TaskGroup *getGroup() { return reinterpret_cast(this); } /// Return the first child linked by this record. This may be null; /// if not, it (and all of its successors) are guaranteed to satisfy /// `isChildTask()`. - AsyncTask *getFirstChild() const { - return FirstChild; - } + AsyncTask *getFirstChild() const { return FirstChild; } /// Attach the passed in `child` task to this group. void attachChild(AsyncTask *child) { @@ -212,7 +192,8 @@ class TaskGroupTaskStatusRecord : public TaskStatusRecord { return; } - // We need to traverse the siblings to find the last one and add the child there. + // We need to traverse the siblings to find the last one and add the child + // there. // FIXME: just set prepend to the current head, no need to traverse. auto cur = FirstChild; @@ -254,20 +235,18 @@ class TaskGroupTaskStatusRecord : public TaskStatusRecord { /// subsequently used. class CancellationNotificationStatusRecord : public TaskStatusRecord { public: - using FunctionType = SWIFT_CC(swift) void (SWIFT_CONTEXT void *); + using FunctionType = SWIFT_CC(swift) void(SWIFT_CONTEXT void *); private: - FunctionType * __ptrauth_swift_cancellation_notification_function Function; + FunctionType *__ptrauth_swift_cancellation_notification_function Function; void *Argument; public: CancellationNotificationStatusRecord(FunctionType *fn, void *arg) - : TaskStatusRecord(TaskStatusRecordKind::CancellationNotification), - Function(fn), Argument(arg) {} + : TaskStatusRecord(TaskStatusRecordKind::CancellationNotification), + Function(fn), Argument(arg) {} - void run() { - Function(Argument); - } + void run() { Function(Argument); } static bool classof(const TaskStatusRecord *record) { return record->getKind() == TaskStatusRecordKind::CancellationNotification; @@ -284,20 +263,18 @@ class CancellationNotificationStatusRecord : public TaskStatusRecord { /// subsequently used. class EscalationNotificationStatusRecord : public TaskStatusRecord { public: - using FunctionType = void (void *, JobPriority); + using FunctionType = void(void *, JobPriority); private: - FunctionType * __ptrauth_swift_escalation_notification_function Function; + FunctionType *__ptrauth_swift_escalation_notification_function Function; void *Argument; public: EscalationNotificationStatusRecord(FunctionType *fn, void *arg) - : TaskStatusRecord(TaskStatusRecordKind::EscalationNotification), - Function(fn), Argument(arg) {} + : TaskStatusRecord(TaskStatusRecordKind::EscalationNotification), + Function(fn), Argument(arg) {} - void run(JobPriority newPriority) { - Function(Argument, newPriority); - } + void run(JobPriority newPriority) { Function(Argument, newPriority); } static bool classof(const TaskStatusRecord *record) { return record->getKind() == TaskStatusRecordKind::EscalationNotification; diff --git a/include/swift/APIDigester/ModuleAnalyzerNodes.h b/include/swift/APIDigester/ModuleAnalyzerNodes.h index 91cf567f93a4c..964f09c26bceb 100644 --- a/include/swift/APIDigester/ModuleAnalyzerNodes.h +++ b/include/swift/APIDigester/ModuleAnalyzerNodes.h @@ -23,6 +23,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/TinyPtrVector.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/FileSystem.h" @@ -464,7 +465,7 @@ class SDKNodeTypeNominal : public SDKNodeType { StringRef USR; public: SDKNodeTypeNominal(SDKNodeInitInfo Info); - // Get the usr of the correspoding nominal type decl. + // Get the usr of the corresponding nominal type decl. StringRef getUsr() const { return USR; } static bool classof(const SDKNode *N); void jsonize(json::Output &Out) override; @@ -717,6 +718,12 @@ class SDKNodeDeclAccessor: public SDKNodeDeclAbstractFunc { void jsonize(json::Output &Out) override; }; +class SDKNodeDeclImport: public SDKNodeDecl { +public: + SDKNodeDeclImport(SDKNodeInitInfo Info); + static bool classof(const SDKNode *N); +}; + // The additional information we need for a type node in the digest. // We use type node to represent entities more than types, e.g. parameters, so // this struct is necessary to pass down to create a type node. @@ -781,6 +788,8 @@ class SwiftDeclCollector: public VisibleDeclConsumer { void lookupVisibleDecls(ArrayRef Modules); }; +void detectRename(SDKNode *L, SDKNode *R); + int dumpSwiftModules(const CompilerInvocation &InitInvok, const llvm::StringSet<> &ModuleNames, StringRef OutputDir, @@ -799,6 +808,8 @@ int dumpSDKContent(const CompilerInvocation &InitInvok, const llvm::StringSet<> &ModuleNames, StringRef OutputFile, CheckerOptions Opts); +void dumpModuleContent(ModuleDecl *MD, StringRef OutputFile, bool ABI); + /// Mostly for testing purposes, this function de-serializes the SDK dump in /// dumpPath and re-serialize them to OutputPath. If the tool performs correctly, /// the contents in dumpPath and OutputPath should be identical. diff --git a/include/swift/AST/ASTAllocated.h b/include/swift/AST/ASTAllocated.h new file mode 100644 index 0000000000000..2b3f82594be89 --- /dev/null +++ b/include/swift/AST/ASTAllocated.h @@ -0,0 +1,73 @@ +//===--- ASTAllocated.h - Allocation in the AST Context ---------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_ASTALLOCATED_H +#define SWIFT_AST_ASTALLOCATED_H + +#include +#include + +namespace swift { +class ASTContext; + +/// The arena in which a particular ASTContext allocation will go. +enum class AllocationArena { + /// The permanent arena, which is tied to the lifetime of + /// the ASTContext. + /// + /// All global declarations and types need to be allocated into this arena. + /// At present, everything that is not a type involving a type variable is + /// allocated in this arena. + Permanent, + /// The constraint solver's temporary arena, which is tied to the + /// lifetime of a particular instance of the constraint solver. + /// + /// Any type involving a type variable is allocated in this arena. + ConstraintSolver +}; + +namespace detail { +void *allocateInASTContext(size_t bytes, const ASTContext &ctx, + AllocationArena arena, unsigned alignment); +} + +/// Types inheriting from this class are intended to be allocated in an +/// \c ASTContext allocator; you cannot allocate them by using a normal \c new, +/// and instead you must either provide an \c ASTContext or use a placement +/// \c new. +/// +/// The template parameter is a type with the desired alignment. It is usually, +/// but not always, the type that is inheriting \c ASTAllocated. +template +class ASTAllocated { +public: + // Make vanilla new/delete illegal. + void *operator new(size_t Bytes) throw() = delete; + void operator delete(void *Data) throw() = delete; + + // Only allow allocation using the allocator in ASTContext + // or by doing a placement new. + void *operator new(size_t bytes, const ASTContext &ctx, + AllocationArena arena = AllocationArena::Permanent, + unsigned alignment = alignof(AlignTy)) { + return detail::allocateInASTContext(bytes, ctx, arena, alignment); + } + + void *operator new(size_t Bytes, void *Mem) throw() { + assert(Mem && "placement new into failed allocation"); + return Mem; + } +}; + +} + +#endif diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 679df58b91870..411c759fa592f 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -17,6 +17,7 @@ #ifndef SWIFT_AST_ASTCONTEXT_H #define SWIFT_AST_ASTCONTEXT_H +#include "swift/AST/ASTAllocated.h" #include "swift/AST/Evaluator.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" @@ -28,6 +29,7 @@ #include "swift/Basic/LangOptions.h" #include "swift/Basic/Located.h" #include "swift/Basic/Malloc.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "clang/AST/DeclTemplate.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -36,6 +38,7 @@ #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/Allocator.h" @@ -104,6 +107,8 @@ namespace swift { class InheritedProtocolConformance; class SelfProtocolConformance; class SpecializedProtocolConformance; + enum class BuiltinConformanceKind; + class BuiltinProtocolConformance; enum class ProtocolConformanceState; class Pattern; enum PointerTypeKind : unsigned; @@ -133,26 +138,14 @@ namespace namelookup { class ImportCache; } +namespace rewriting { + class RequirementMachine; +} + namespace syntax { class SyntaxArena; } -/// The arena in which a particular ASTContext allocation will go. -enum class AllocationArena { - /// The permanent arena, which is tied to the lifetime of - /// the ASTContext. - /// - /// All global declarations and types need to be allocated into this arena. - /// At present, everything that is not a type involving a type variable is - /// allocated in this arena. - Permanent, - /// The constraint solver's temporary arena, which is tied to the - /// lifetime of a particular instance of the constraint solver. - /// - /// Any type involving a type variable is allocated in this arena. - ConstraintSolver -}; - /// Lists the set of "known" Foundation entities that are used in the /// compiler. /// @@ -230,6 +223,7 @@ class ASTContext final { ASTContext(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, SearchPathOptions &SearchPathOpts, ClangImporterOptions &ClangImporterOpts, + symbolgraphgen::SymbolGraphOptions &SymbolGraphOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags); @@ -245,6 +239,7 @@ class ASTContext final { static ASTContext *get(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, SearchPathOptions &SearchPathOpts, ClangImporterOptions &ClangImporterOpts, + symbolgraphgen::SymbolGraphOptions &SymbolGraphOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags); ~ASTContext(); @@ -266,6 +261,9 @@ class ASTContext final { /// The clang importer options used by this AST context. ClangImporterOptions &ClangImporterOpts; + /// The symbol graph generation options used by this AST context. + symbolgraphgen::SymbolGraphOptions &SymbolGraphOpts; + /// The source manager object. SourceManager &SourceMgr; @@ -520,6 +518,11 @@ class ASTContext final { FuncDecl *get##Name() const; #include "swift/AST/KnownDecls.def" + // Declare accessors for the known declarations. +#define KNOWN_SDK_FUNC_DECL(Module, Name, Id) \ + FuncDecl *get##Name() const; +#include "swift/AST/KnownSDKDecls.def" + /// Get the '+' function on two RangeReplaceableCollection. FuncDecl *getPlusFunctionOnRangeReplaceableCollection() const; @@ -591,6 +594,11 @@ class ASTContext final { // Retrieve the declaration of Swift._stdlib_isOSVersionAtLeast. FuncDecl *getIsOSVersionAtLeastDecl() const; + /// Look for the declaration with the given name within the + /// passed in module. + void lookupInModule(ModuleDecl *M, StringRef name, + SmallVectorImpl &results) const; + /// Look for the declaration with the given name within the /// Swift module. void lookupInSwiftModule(StringRef name, @@ -730,9 +738,20 @@ class ASTContext final { /// Get the runtime availability of support for concurrency. AvailabilityContext getConcurrencyAvailability(); + /// Get the back-deployed availability for concurrency. + AvailabilityContext getBackDeployedConcurrencyAvailability(); + /// Get the runtime availability of support for differentiation. AvailabilityContext getDifferentiationAvailability(); + /// Get the runtime availability of getters and setters of multi payload enum + /// tag single payloads. + AvailabilityContext getMultiPayloadEnumTagSinglePayload(); + + /// Get the runtime availability of the Objective-C enabled + /// swift_isUniquelyReferenced functions. + AvailabilityContext getObjCIsUniquelyReferencedAvailability(); + /// Get the runtime availability of features introduced in the Swift 5.2 /// compiler for the target platform. AvailabilityContext getSwift52Availability(); @@ -749,6 +768,15 @@ class ASTContext final { /// compiler for the target platform. AvailabilityContext getSwift55Availability(); + /// Get the runtime availability of features introduced in the Swift 5.6 + /// compiler for the target platform. + AvailabilityContext getSwift56Availability(); + + // Note: Update this function if you add a new getSwiftXYAvailability above. + /// Get the runtime availability for a particular version of Swift (5.0+). + AvailabilityContext + getSwift5PlusAvailability(llvm::VersionTuple swiftVersion); + /// Get the runtime availability of features that have been introduced in the /// Swift compiler for future versions of the target platform. AvailabilityContext getSwiftFutureAvailability(); @@ -825,6 +853,13 @@ class ASTContext final { ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate); + /// Compute the extra implicit framework search paths on Apple platforms: + /// $SDKROOT/System/Library/Frameworks/ and $SDKROOT/Library/Frameworks/. + std::vector getDarwinImplicitFrameworkSearchPaths() const; + + /// Return a set of all possible filesystem locations where modules can be found. + llvm::StringSet<> getAllModuleSearchPathsSet() const; + /// Load extensions to the given nominal type from the external /// module loaders. /// @@ -994,12 +1029,20 @@ class ASTContext final { ProtocolDecl *protocol, SourceLoc loc, DeclContext *dc, - ProtocolConformanceState state); + ProtocolConformanceState state, + bool isUnchecked); /// Produce a self-conformance for the given protocol. SelfProtocolConformance * getSelfConformance(ProtocolDecl *protocol); + /// Produce the builtin conformance for some structural type to some protocol. + BuiltinProtocolConformance * + getBuiltinConformance(Type type, ProtocolDecl *protocol, + GenericSignature genericSig, + ArrayRef conditionalRequirements, + BuiltinConformanceKind kind); + /// A callback used to produce a diagnostic for an ill-formed protocol /// conformance that was type-checked before we're actually walking the /// conformance itself, along with a bit indicating whether this diagnostic @@ -1132,6 +1175,16 @@ class ASTContext final { GenericSignatureBuilder *getOrCreateGenericSignatureBuilder( CanGenericSignature sig); + /// Retrieve or create a term rewriting system for answering queries on + /// type parameters written against the given generic signature. + rewriting::RequirementMachine *getOrCreateRequirementMachine( + CanGenericSignature sig); + + /// This is a hack to break cycles. Don't introduce new callers of this + /// method. + bool isRecursivelyConstructingRequirementMachine( + CanGenericSignature sig); + /// Retrieve a generic signature with a single unconstrained type parameter, /// like ``. CanGenericSignature getSingleGenericParameterSignature() const; diff --git a/include/swift/AST/ASTDemangler.h b/include/swift/AST/ASTDemangler.h index 4308cb18ea0fd..76c90f017c9a5 100644 --- a/include/swift/AST/ASTDemangler.h +++ b/include/swift/AST/ASTDemangler.h @@ -98,7 +98,7 @@ class ASTBuilder { Type createFunctionType( ArrayRef> params, Type output, FunctionTypeFlags flags, - FunctionMetadataDifferentiabilityKind diffKind); + FunctionMetadataDifferentiabilityKind diffKind, Type globalActor); Type createImplFunctionType( Demangle::ImplParameterConvention calleeConvention, diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 75b1ebf6c1bde..7934d54c91db9 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -32,7 +32,6 @@ namespace Mangle { /// The mangler for AST declarations. class ASTMangler : public Mangler { protected: - CanGenericSignature CurGenericSignature; ModuleDecl *Mod = nullptr; /// Optimize out protocol names if a type only conforms to one protocol. @@ -85,21 +84,21 @@ class ASTMangler : public Mangler { public: enum class SymbolKind { Default, - AsyncHandlerBody, DynamicThunk, SwiftAsObjCThunk, ObjCAsSwiftThunk, + DistributedThunk, }; ASTMangler(bool DWARFMangling = false) : DWARFMangling(DWARFMangling) {} - void addTypeSubstitution(Type type) { - type = dropProtocolsFromAssociatedTypes(type); + void addTypeSubstitution(Type type, GenericSignature sig) { + type = dropProtocolsFromAssociatedTypes(type, sig); addSubstitution(type.getPointer()); } - bool tryMangleTypeSubstitution(Type type) { - type = dropProtocolsFromAssociatedTypes(type); + bool tryMangleTypeSubstitution(Type type, GenericSignature sig) { + type = dropProtocolsFromAssociatedTypes(type, sig); return tryMangleSubstitution(type.getPointer()); } @@ -164,6 +163,7 @@ class ASTMangler : public Mangler { std::string mangleReabstractionThunkHelper(CanSILFunctionType ThunkType, Type FromType, Type ToType, Type SelfType, + Type GlobalActorBound, ModuleDecl *Module); /// Mangle a completion handler block implementation function, used for importing ObjC @@ -254,7 +254,7 @@ class ASTMangler : public Mangler { std::string mangleObjCRuntimeName(const NominalTypeDecl *Nominal); std::string mangleTypeWithoutPrefix(Type type) { - appendType(type); + appendType(type, nullptr); return finalize(); } @@ -292,18 +292,18 @@ class ASTMangler : public Mangler { void appendSymbolKind(SymbolKind SKind); - void appendType(Type type, const ValueDecl *forDecl = nullptr); + void appendType(Type type, GenericSignature sig, + const ValueDecl *forDecl = nullptr); void appendDeclName(const ValueDecl *decl); GenericTypeParamType *appendAssocType(DependentMemberType *DepTy, + GenericSignature sig, bool &isAssocTypeAtDepth); void appendOpWithGenericParamIndex(StringRef, const GenericTypeParamType *paramTy); - void bindGenericParameters(GenericSignature sig); - /// Mangles a sugared type iff we are mangling for the debugger. template void appendSugaredType(Type type, const ValueDecl *forDecl) { @@ -313,7 +313,8 @@ class ASTMangler : public Mangler { appendType(BlandTy, forDecl); } - void appendBoundGenericArgs(Type type, bool &isFirstArgList); + void appendBoundGenericArgs(Type type, GenericSignature sig, + bool &isFirstArgList); /// Append the bound generics arguments for the given declaration context /// based on a complete substitution map. @@ -321,17 +322,20 @@ class ASTMangler : public Mangler { /// \returns the number of generic parameters that were emitted /// thus far. unsigned appendBoundGenericArgs(DeclContext *dc, + GenericSignature sig, SubstitutionMap subs, bool &isFirstArgList); /// Append the bound generic arguments as a flat list, disregarding depth. - void appendFlatGenericArgs(SubstitutionMap subs); + void appendFlatGenericArgs(SubstitutionMap subs, + GenericSignature sig); /// Append any retroactive conformances. - void appendRetroactiveConformances(Type type); + void appendRetroactiveConformances(Type type, GenericSignature sig); void appendRetroactiveConformances(SubstitutionMap subMap, + GenericSignature sig, ModuleDecl *fromModule); - void appendImplFunctionType(SILFunctionType *fn); + void appendImplFunctionType(SILFunctionType *fn, GenericSignature sig); void appendContextOf(const ValueDecl *decl); @@ -347,30 +351,35 @@ class ASTMangler : public Mangler { enum FunctionManglingKind { NoFunctionMangling, FunctionMangling, - AsyncHandlerBodyMangling }; - void appendFunction(AnyFunctionType *fn, + void appendFunction(AnyFunctionType *fn, GenericSignature sig, FunctionManglingKind functionMangling = NoFunctionMangling, const ValueDecl *forDecl = nullptr); - void appendFunctionType(AnyFunctionType *fn, bool isAutoClosure = false, + void appendFunctionType(AnyFunctionType *fn, GenericSignature sig, + bool isAutoClosure = false, const ValueDecl *forDecl = nullptr); void appendClangType(AnyFunctionType *fn); template void appendClangType(FnType *fn, llvm::raw_svector_ostream &os); void appendFunctionSignature(AnyFunctionType *fn, + GenericSignature sig, const ValueDecl *forDecl, FunctionManglingKind functionMangling); void appendFunctionInputType(ArrayRef params, + GenericSignature sig, const ValueDecl *forDecl = nullptr); void appendFunctionResultType(Type resultType, + GenericSignature sig, const ValueDecl *forDecl = nullptr); - void appendTypeList(Type listTy, const ValueDecl *forDecl = nullptr); + void appendTypeList(Type listTy, GenericSignature sig, + const ValueDecl *forDecl = nullptr); void appendTypeListElement(Identifier name, Type elementType, ParameterTypeFlags flags, + GenericSignature sig, const ValueDecl *forDecl = nullptr); /// Append a generic signature to the mangling. @@ -385,16 +394,21 @@ class ASTMangler : public Mangler { bool appendGenericSignature(GenericSignature sig, GenericSignature contextSig = nullptr); - void appendRequirement(const Requirement &reqt); + void appendRequirement(const Requirement &reqt, + GenericSignature sig); - void appendGenericSignatureParts(TypeArrayView params, + void appendGenericSignatureParts(GenericSignature sig, + ArrayRef> params, unsigned initialParamDepth, ArrayRef requirements); - DependentMemberType *dropProtocolFromAssociatedType(DependentMemberType *dmt); - Type dropProtocolsFromAssociatedTypes(Type type); + DependentMemberType *dropProtocolFromAssociatedType(DependentMemberType *dmt, + GenericSignature sig); + Type dropProtocolsFromAssociatedTypes(Type type, + GenericSignature sig); - void appendAssociatedTypeName(DependentMemberType *dmt); + void appendAssociatedTypeName(DependentMemberType *dmt, + GenericSignature sig); void appendClosureEntity(const SerializedAbstractClosureExpr *closure); @@ -433,16 +447,18 @@ class ASTMangler : public Mangler { void appendEntity(const ValueDecl *decl, StringRef EntityOp, bool isStatic); - void appendEntity(const ValueDecl *decl, bool isAsyncHandlerBody = false); + void appendEntity(const ValueDecl *decl); void appendProtocolConformance(const ProtocolConformance *conformance); void appendProtocolConformanceRef(const RootProtocolConformance *conformance); - void appendAnyProtocolConformance(CanGenericSignature genericSig, + void appendAnyProtocolConformance(GenericSignature genericSig, CanType conformingType, ProtocolConformanceRef conformance); void appendConcreteProtocolConformance( - const ProtocolConformance *conformance); - void appendDependentProtocolConformance(const ConformanceAccessPath &path); + const ProtocolConformance *conformance, + GenericSignature sig); + void appendDependentProtocolConformance(const ConformanceAccessPath &path, + GenericSignature sig); void appendOpParamForLayoutConstraint(LayoutConstraint Layout); void appendSymbolicReference(SymbolicReferent referent); diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index 593c9f2075ea2..c67db8a0a2372 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -17,6 +17,7 @@ #include "swift/Basic/QuotedString.h" #include "swift/Basic/UUID.h" #include "swift/AST/Identifier.h" +#include "swift/AST/Decl.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/DenseSet.h" @@ -47,6 +48,8 @@ enum class PrintNameContext { Normal, /// Keyword context, where no keywords are escaped. Keyword, + /// Keyword for introducing a declarations e.g. 'func', 'struct'. + IntroducerKeyword, /// Type member context, e.g. properties or enum cases. TypeMember, /// Generic parameter context, where 'Self' is not escaped. @@ -216,6 +219,17 @@ class ASTPrinter { *this << Suffix; } + void printIntroducerKeyword(StringRef name, + const PrintOptions &Opts, + StringRef Suffix = "") { + if (Opts.SkipIntroducerKeywords) + return; + callPrintNamePre(PrintNameContext::IntroducerKeyword); + *this << name; + printNamePost(PrintNameContext::IntroducerKeyword); + *this << Suffix; + } + void printAttrName(StringRef name, bool needAt = false) { callPrintNamePre(PrintNameContext::Attribute); if (needAt) @@ -351,8 +365,9 @@ void printEnumElementsAsCases( llvm::DenseSet &UnhandledElements, llvm::raw_ostream &OS); -void getInheritedForPrinting(const Decl *decl, const PrintOptions &options, - llvm::SmallVectorImpl &Results); +void getInheritedForPrinting( + const Decl *decl, const PrintOptions &options, + llvm::SmallVectorImpl &Results); StringRef getAccessorKindString(AccessorKind value); diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 1e8e2789463ea..bc2d38af9ab0b 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -119,7 +119,7 @@ SourceLoc extractNearestSourceLoc(std::tuple); /// \code /// -dump-scope-maps expanded /// \endcode -class ASTScopeImpl { +class ASTScopeImpl : public ASTAllocated { friend class NodeAdder; friend class Portion; friend class GenericTypeOrExtensionWholePortion; @@ -159,20 +159,9 @@ class ASTScopeImpl { ASTScopeImpl(const ASTScopeImpl &) = delete; ASTScopeImpl &operator=(const ASTScopeImpl &) = delete; - // Make vanilla new illegal for ASTScopes. - void *operator new(size_t bytes) = delete; // Need this because have virtual destructors void operator delete(void *data) {} - // Only allow allocation of scopes using the allocator of a particular source - // file. - void *operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment = alignof(ASTScopeImpl)); - void *operator new(size_t Bytes, void *Mem) { - ASTScopeAssert(Mem, "Allocation failed"); - return Mem; - } - #pragma mark - tree declarations protected: NullablePtr getParent() { @@ -425,26 +414,15 @@ class ASTSourceFileScope final : public ASTScopeImpl { expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &); }; -class Portion { +class Portion : public ASTAllocated { public: const char *portionName; Portion(const char *n) : portionName(n) {} virtual ~Portion() {} - // Make vanilla new illegal for ASTScopes. - void *operator new(size_t bytes) = delete; // Need this because have virtual destructors void operator delete(void *data) {} - // Only allow allocation of scopes using the allocator of a particular source - // file. - void *operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment = alignof(ASTScopeImpl)); - void *operator new(size_t Bytes, void *Mem) { - ASTScopeAssert(Mem, "Allocation failed"); - return Mem; - } - /// Return the new insertion point virtual ASTScopeImpl *expandScope(GenericTypeOrExtensionScope *, ScopeCreator &) const = 0; @@ -957,7 +935,9 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope { public: PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex) : AbstractPatternEntryScope(pbDecl, entryIndex), - initAsWrittenWhenCreated(pbDecl->getOriginalInit(entryIndex)) {} + initAsWrittenWhenCreated(pbDecl->isDebuggerBinding() ? + pbDecl->getInit(entryIndex) : + pbDecl->getOriginalInit(entryIndex)) {} virtual ~PatternEntryInitializerScope() {} protected: diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 898438e86290c..7015ca0402ce5 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -25,6 +25,7 @@ class raw_ostream; namespace swift { class DeclContext; +class ModuleDecl; class NominalTypeDecl; class SubstitutionMap; @@ -33,7 +34,7 @@ class SubstitutionMap; bool areTypesEqual(Type type1, Type type2); /// Determine whether the given type is suitable as a concurrent value type. -bool isSendableType(const DeclContext *dc, Type type); +bool isSendableType(ModuleDecl *module, Type type); /// Describes the actor isolation of a given declaration, which determines /// the actors with which it can interact. @@ -47,6 +48,10 @@ class ActorIsolation { /// For example, a mutable stored property or synchronous function within /// the actor is isolated to the instance of that actor. ActorInstance, + /// The declaration is isolated to a (potentially) distributed actor. + /// Distributed actors may access _their_ state (same as 'ActorInstance') + /// however others may not access any properties on other distributed actors. + DistributedActorInstance, /// The declaration is explicitly specified to be independent of any actor, /// meaning that it can be used from any actor but is also unable to /// refer to the isolated state of any given actor. @@ -87,6 +92,10 @@ class ActorIsolation { return ActorIsolation(ActorInstance, actor); } + static ActorIsolation forDistributedActorInstance(NominalTypeDecl *actor) { + return ActorIsolation(DistributedActorInstance, actor); + } + static ActorIsolation forGlobalActor(Type globalActor, bool unsafe) { return ActorIsolation( unsafe ? GlobalActorUnsafe : GlobalActor, globalActor); @@ -99,7 +108,7 @@ class ActorIsolation { bool isUnspecified() const { return kind == Unspecified; } NominalTypeDecl *getActor() const { - assert(getKind() == ActorInstance); + assert(getKind() == ActorInstance || getKind() == DistributedActorInstance); return actor; } @@ -130,6 +139,7 @@ class ActorIsolation { return true; case ActorInstance: + case DistributedActorInstance: return lhs.actor == rhs.actor; case GlobalActor: diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index 1026be1e61aa2..6630190351f15 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -210,9 +210,9 @@ class AnyFunctionRef { llvm_unreachable("unexpected AnyFunctionRef representation"); } - SourceLoc getLoc() const { + SourceLoc getLoc(bool SerializedOK = true) const { if (auto afd = TheFunction.dyn_cast()) { - return afd->getLoc(); + return afd->getLoc(SerializedOK); } if (auto ce = TheFunction.dyn_cast()) { return ce->getLoc(); @@ -269,7 +269,7 @@ class AnyFunctionRef { } friend SourceLoc extractNearestSourceLoc(AnyFunctionRef fn) { - return fn.getLoc(); + return fn.getLoc(/*SerializedOK=*/false); } private: diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 212b7be78dac4..3396e9bac1e79 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -55,6 +55,7 @@ TYPE_ATTR(differentiable) TYPE_ATTR(noDerivative) TYPE_ATTR(async) TYPE_ATTR(Sendable) +TYPE_ATTR(unchecked) // SIL-specific attributes TYPE_ATTR(block_storage) @@ -565,10 +566,7 @@ SIMPLE_DECL_ATTR(noDerivative, NoDerivative, ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, 100) -SIMPLE_DECL_ATTR(asyncHandler, AsyncHandler, - OnFunc | ConcurrencyOnly | - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, - 101) +// 101 was @asyncHandler and is now unused // TODO: Remove this once we don't need to support 'actor' as a modifier CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor, @@ -577,15 +575,14 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor, APIBreakingToAdd | APIBreakingToRemove, 102) -DECL_ATTR(actorIndependent, ActorIndependent, - OnClass | OnStruct | OnEnum | OnExtension | OnFunc | OnConstructor | - OnVar | OnSubscript | ConcurrencyOnly | +CONTEXTUAL_SIMPLE_DECL_ATTR(isolated, Isolated, + DeclModifier | OnParam | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, 103) SIMPLE_DECL_ATTR(globalActor, GlobalActor, - OnClass | OnStruct | OnEnum | ConcurrencyOnly | + OnClass | OnStruct | OnEnum | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, 104) @@ -596,7 +593,7 @@ SIMPLE_DECL_ATTR(_specializeExtension, SpecializeExtension, 105) CONTEXTUAL_SIMPLE_DECL_ATTR(async, Async, - DeclModifier | OnVar | OnFunc | ConcurrencyOnly | + DeclModifier | OnVar | OnFunc | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, 106) @@ -624,15 +621,10 @@ SIMPLE_DECL_ATTR(reasync, AtReasync, ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, 110) -DECL_ATTR(completionHandlerAsync, CompletionHandlerAsync, - OnAbstractFunction | ConcurrencyOnly | LongAttribute | - ABIStableToAdd | ABIStableToRemove | - APIStableToAdd | APIStableToRemove, - 111) +// 111 was an experimental @completionHandlerAsync and is now unused CONTEXTUAL_SIMPLE_DECL_ATTR(nonisolated, Nonisolated, DeclModifier | OnFunc | OnConstructor | OnVar | OnSubscript | - ConcurrencyOnly | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, 112) @@ -657,11 +649,29 @@ SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove, 116) -CONTEXTUAL_SIMPLE_DECL_ATTR(spawn, Spawn, - DeclModifier | OnVar | ConcurrencyOnly | +// 117 was 'spawn' and is now unused + +CONTEXTUAL_SIMPLE_DECL_ATTR(distributed, DistributedActor, + DeclModifier | OnClass | OnFunc | + DistributedOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, - 117) + 118) + +SIMPLE_DECL_ATTR(_distributedActorIndependent, DistributedActorIndependent, + OnFunc | OnVar | + DistributedOnly | UserInaccessible | + ABIStableToAdd | ABIStableToRemove | + APIBreakingToAdd | APIBreakingToRemove, + 119) + +SIMPLE_DECL_ATTR(_assemblyVision, EmitAssemblyVisionRemarks, + OnFunc | UserInaccessible | NotSerialized | OnNominalType | + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + 120) + +// If you're adding a new underscored attribute here, please document it in +// docs/ReferenceGuides/UnderscoredAttributes.md. #undef TYPE_ATTR #undef DECL_ATTR_ALIAS diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 7396c5a580989..891e7fd8a9301 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -26,6 +26,7 @@ #include "swift/Basic/OptimizationMode.h" #include "swift/Basic/Version.h" #include "swift/Basic/Located.h" +#include "swift/AST/ASTAllocated.h" #include "swift/AST/Identifier.h" #include "swift/AST/AttrKind.h" #include "swift/AST/AutoDiff.h" @@ -60,7 +61,8 @@ class PatternBindingInitializer; class TrailingWhereClause; class TypeExpr; -class alignas(1 << AttrAlignInBits) AttributeBase { +class alignas(1 << AttrAlignInBits) AttributeBase + : public ASTAllocated { public: /// The location of the '@'. const SourceLoc AtLoc; @@ -80,17 +82,6 @@ class alignas(1 << AttrAlignInBits) AttributeBase { return Range; } - // Only allow allocation of attributes using the allocator in ASTContext - // or by doing a placement new. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(AttributeBase)); - - void operator delete(void *Data) throw() { } - void *operator new(size_t Bytes, void *Mem) throw() { return Mem; } - - // Make vanilla new/delete illegal for attributes. - void *operator new(size_t Bytes) throw() = delete; - AttributeBase(const AttributeBase &) = delete; protected: @@ -162,10 +153,6 @@ class DeclAttribute : public AttributeBase { kind : NumInlineKindBits ); - SWIFT_INLINE_BITFIELD(ActorIndependentAttr, DeclAttribute, NumActorIndependentKindBits, - kind : NumActorIndependentKindBits - ); - SWIFT_INLINE_BITFIELD(OptimizeAttr, DeclAttribute, NumOptimizationModeBits, mode : NumOptimizationModeBits ); @@ -298,6 +285,9 @@ class DeclAttribute : public AttributeBase { /// Whether this attribute is only valid when concurrency is enabled. ConcurrencyOnly = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 16), + + /// Whether this attribute is only valid when distributed is enabled. + DistributedOnly = 1ull << (unsigned(DeclKindIndex::Last_Decl) + 17), }; LLVM_READNONE @@ -392,6 +382,10 @@ class DeclAttribute : public AttributeBase { return getOptions(DK) & ConcurrencyOnly; } + static bool isDistributedOnly(DeclAttrKind DK) { + return getOptions(DK) & DistributedOnly; + } + static bool isUserInaccessible(DeclAttrKind DK) { return getOptions(DK) & UserInaccessible; } @@ -626,7 +620,7 @@ class AvailableAttr : public DeclAttribute { AvailableAttr(SourceLoc AtLoc, SourceRange Range, PlatformKind Platform, - StringRef Message, StringRef Rename, + StringRef Message, StringRef Rename, ValueDecl *RenameDecl, const llvm::VersionTuple &Introduced, SourceRange IntroducedRange, const llvm::VersionTuple &Deprecated, @@ -636,7 +630,7 @@ class AvailableAttr : public DeclAttribute { PlatformAgnosticAvailabilityKind PlatformAgnostic, bool Implicit) : DeclAttribute(DAK_Available, AtLoc, Range, Implicit), - Message(Message), Rename(Rename), + Message(Message), Rename(Rename), RenameDecl(RenameDecl), INIT_VER_TUPLE(Introduced), IntroducedRange(IntroducedRange), INIT_VER_TUPLE(Deprecated), DeprecatedRange(DeprecatedRange), INIT_VER_TUPLE(Obsoleted), ObsoletedRange(ObsoletedRange), @@ -657,6 +651,12 @@ class AvailableAttr : public DeclAttribute { /// the `NS_SWIFT_NAME` annotation in Objective-C. const StringRef Rename; + /// The declaration referred to by \c Rename. Note that this is only set for + /// deserialized attributes or inferred attributes from ObjectiveC code. + /// \c ValueDecl::getRenamedDecl should be used to find the declaration + /// corresponding to \c Rename. + ValueDecl *RenameDecl; + /// Indicates when the symbol was introduced. const Optional Introduced; @@ -743,6 +743,11 @@ class AvailableAttr : public DeclAttribute { llvm::VersionTuple Obsoleted = llvm::VersionTuple()); + /// Create an AvailableAttr that indicates the given \p AsyncFunc should be + /// preferentially used in async contexts + static AvailableAttr *createForAlternative(ASTContext &C, + AbstractFunctionDecl *AsyncFunc); + AvailableAttr *clone(ASTContext &C, bool implicit) const; static bool classof(const DeclAttribute *DA) { @@ -858,6 +863,7 @@ class ObjCAttr final : public DeclAttribute, /// Determine whether the name associated with this attribute was /// implicit. bool isNameImplicit() const { return Bits.ObjCAttr.ImplicitName; } + void setNameImplicit(bool newValue) { Bits.ObjCAttr.ImplicitName = newValue; } /// Set the name of this entity. void setName(ObjCSelector name, bool implicit) { @@ -886,11 +892,6 @@ class ObjCAttr final : public DeclAttribute, Bits.ObjCAttr.Swift3Inferred = inferred; } - /// Clear the name of this entity. - void clearName() { - NameData = nullptr; - } - /// Retrieve the source locations for the names in a non-implicit /// nullary or selector attribute. ArrayRef getNameLocs() const; @@ -1220,25 +1221,6 @@ class ReferenceOwnershipAttr : public DeclAttribute { } }; -/// Represents an actorIndependent/actorIndependent(unsafe) decl attribute. -class ActorIndependentAttr : public DeclAttribute { -public: - ActorIndependentAttr(SourceLoc atLoc, SourceRange range, ActorIndependentKind kind) - : DeclAttribute(DAK_ActorIndependent, atLoc, range, /*Implicit=*/false) { - Bits.ActorIndependentAttr.kind = unsigned(kind); - } - - ActorIndependentAttr(ActorIndependentKind kind, bool IsImplicit=false) - : ActorIndependentAttr(SourceLoc(), SourceRange(), kind) { - setImplicit(IsImplicit); - } - - ActorIndependentKind getKind() const { return ActorIndependentKind(Bits.ActorIndependentAttr.kind); } - static bool classof(const DeclAttribute *DA) { - return DA->getKind() == DAK_ActorIndependent; - } -}; - /// Defines the attribute that we use to model documentation comments. class RawDocCommentAttr : public DeclAttribute { /// Source range of the attached comment. This comment is located before @@ -2041,57 +2023,6 @@ class TransposeAttr final } }; -/// The `@completionHandlerAsync` attribute marks a function as having an async -/// alternative, optionally providing a name (for cases when the alternative -/// has a different name). -class CompletionHandlerAsyncAttr final : public DeclAttribute { -public: - /// Reference to the async alternative function. Only set for deserialized - /// attributes or inferred attributes from ObjectiveC code. - AbstractFunctionDecl *AsyncFunctionDecl; - - /// DeclName of the async function in the attribute. Only set from actual - /// Swift code, deserialization/ObjectiveC imports will set the decl instead. - const DeclNameRef AsyncFunctionName; - - /// Source location of the async function name in the attribute - const SourceLoc AsyncFunctionNameLoc; - - /// The index of the completion handler - const size_t CompletionHandlerIndex; - - /// Source location of the completion handler index passed to the index - const SourceLoc CompletionHandlerIndexLoc; - - CompletionHandlerAsyncAttr(DeclNameRef asyncFunctionName, - SourceLoc asyncFunctionNameLoc, - size_t completionHandlerIndex, - SourceLoc completionHandlerIndexLoc, - SourceLoc atLoc, SourceRange range) - : DeclAttribute(DAK_CompletionHandlerAsync, atLoc, range, - /*implicit*/ false), - AsyncFunctionDecl(nullptr), - AsyncFunctionName(asyncFunctionName), - AsyncFunctionNameLoc(asyncFunctionNameLoc), - CompletionHandlerIndex(completionHandlerIndex), - CompletionHandlerIndexLoc(completionHandlerIndexLoc) {} - - CompletionHandlerAsyncAttr(AbstractFunctionDecl &asyncFunctionDecl, - size_t completionHandlerIndex, - SourceLoc completionHandlerIndexLoc, - SourceLoc atLoc, SourceRange range, - bool implicit) - : DeclAttribute(DAK_CompletionHandlerAsync, atLoc, range, - implicit), - AsyncFunctionDecl(&asyncFunctionDecl) , - CompletionHandlerIndex(completionHandlerIndex), - CompletionHandlerIndexLoc(completionHandlerIndexLoc) {} - - static bool classof(const DeclAttribute *DA) { - return DA->getKind() == DAK_CompletionHandlerAsync; - } -}; - /// Attributes that may be applied to declarations. class DeclAttributes { /// Linked list of declaration attributes. @@ -2148,6 +2079,10 @@ class DeclAttributes { /// a declaration is deprecated on all deployment targets, or null otherwise. const AvailableAttr *getDeprecated(const ASTContext &ctx) const; + /// Returns the first @available attribute that indicates + /// a declaration will be deprecated in the future, or null otherwise. + const AvailableAttr *getSoftDeprecated(const ASTContext &ctx) const; + SWIFT_DEBUG_DUMPER(dump(const Decl *D = nullptr)); void print(ASTPrinter &Printer, const PrintOptions &Options, const Decl *D = nullptr) const; diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index 7ca1a8153fdd0..3640bfdf14466 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -80,16 +80,6 @@ enum : unsigned { NumInlineKindBits = countBitsUsed(static_cast(InlineKind::Last_InlineKind)) }; -/// Indicates whether an actorIndependent decl is unsafe or not -enum class ActorIndependentKind : uint8_t { - Safe = 0, - Unsafe = 1, - Last_InlineKind = Unsafe -}; - -enum : unsigned { NumActorIndependentKindBits = - countBitsUsed(static_cast(ActorIndependentKind::Last_InlineKind)) }; - /// This enum represents the possible values of the @_effects attribute. /// These values are ordered from the strongest guarantee to the weakest, /// so please do not reorder existing values. diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index 044af2b8089cc..3bcdc5dc0852e 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -727,10 +727,7 @@ template <> struct DenseMapInfo { } static unsigned getHashValue(const AutoDiffConfig &Val) { - auto canGenSig = - Val.derivativeGenericSignature - ? Val.derivativeGenericSignature->getCanonicalSignature() - : nullptr; + auto canGenSig = Val.derivativeGenericSignature.getCanonicalSignature(); unsigned combinedHash = hash_combine( ~1U, DenseMapInfo::getHashValue(Val.parameterIndices), DenseMapInfo::getHashValue(Val.resultIndices), @@ -739,14 +736,8 @@ template <> struct DenseMapInfo { } static bool isEqual(const AutoDiffConfig &LHS, const AutoDiffConfig &RHS) { - auto lhsCanGenSig = - LHS.derivativeGenericSignature - ? LHS.derivativeGenericSignature->getCanonicalSignature() - : nullptr; - auto rhsCanGenSig = - RHS.derivativeGenericSignature - ? RHS.derivativeGenericSignature->getCanonicalSignature() - : nullptr; + auto lhsCanGenSig = LHS.derivativeGenericSignature.getCanonicalSignature(); + auto rhsCanGenSig = RHS.derivativeGenericSignature.getCanonicalSignature(); return LHS.parameterIndices == RHS.parameterIndices && LHS.resultIndices == RHS.resultIndices && DenseMapInfo::isEqual(lhsCanGenSig, rhsCanGenSig); diff --git a/include/swift/AST/AvailabilitySpec.h b/include/swift/AST/AvailabilitySpec.h index b8eb72c890c4b..667b0f576494c 100644 --- a/include/swift/AST/AvailabilitySpec.h +++ b/include/swift/AST/AvailabilitySpec.h @@ -44,7 +44,7 @@ enum class AvailabilitySpecKind { /// The root class for specifications of API availability in availability /// queries. -class AvailabilitySpec { +class AvailabilitySpec : public ASTAllocated { AvailabilitySpecKind Kind; public: @@ -53,12 +53,6 @@ class AvailabilitySpec { AvailabilitySpecKind getKind() const { return Kind; } SourceRange getSourceRange() const; - - void * - operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(AvailabilitySpec)); - void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() = delete; }; /// An availability specification that guards execution based on the @@ -133,7 +127,8 @@ class PlatformVersionConstraintAvailabilitySpec : public AvailabilitySpec { void * operator new(size_t Bytes, ASTContext &C, unsigned Alignment = alignof(PlatformVersionConstraintAvailabilitySpec)){ - return AvailabilitySpec::operator new(Bytes, C, Alignment); + return AvailabilitySpec::operator new(Bytes, C, AllocationArena::Permanent, + Alignment); } }; @@ -180,7 +175,8 @@ class PlatformAgnosticVersionConstraintAvailabilitySpec : public AvailabilitySpe void * operator new(size_t Bytes, ASTContext &C, unsigned Alignment = alignof(PlatformAgnosticVersionConstraintAvailabilitySpec)){ - return AvailabilitySpec::operator new(Bytes, C, Alignment); + return AvailabilitySpec::operator new(Bytes, C, AllocationArena::Permanent, + Alignment); } }; @@ -214,7 +210,8 @@ class OtherPlatformAvailabilitySpec : public AvailabilitySpec { void * operator new(size_t Bytes, ASTContext &C, unsigned Alignment = alignof(OtherPlatformAvailabilitySpec)) { - return AvailabilitySpec::operator new(Bytes, C, Alignment); + return AvailabilitySpec::operator new(Bytes, C, AllocationArena::Permanent, + Alignment); } }; diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 6e591fc119570..27d71fba227c4 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -512,6 +512,9 @@ BUILTIN_SIL_OPERATION(WithUnsafeContinuation, "withUnsafeContinuation", Special) /// the continuation is resumed. BUILTIN_SIL_OPERATION(WithUnsafeThrowingContinuation, "withUnsafeThrowingContinuation", Special) +/// Force the current task to be rescheduled on the specified actor. +BUILTIN_SIL_OPERATION(HopToActor, "hopToActor", None) + #undef BUILTIN_SIL_OPERATION // BUILTIN_RUNTIME_CALL - A call into a runtime function. @@ -724,6 +727,14 @@ BUILTIN_MISC_OPERATION(InitializeDefaultActor, "initializeDefaultActor", "", Spe /// Destroy the default-actor instance in a default actor object. BUILTIN_MISC_OPERATION(DestroyDefaultActor, "destroyDefaultActor", "", Special) +/// Allocate a "proxy" for a distributed remote actor. TODO(distributed) change the name of this to create throughout. +BUILTIN_MISC_OPERATION(InitializeDistributedRemoteActor, + "initializeDistributedRemoteActor", "", Special) + +/// Destroy the distributed-actor instance in a "proxy" actor object. +BUILTIN_MISC_OPERATION(DestroyDistributedActor, + "destroyDistributedActor", "", Special) + /// Resume a non-throwing continuation normally with the given result. BUILTIN_MISC_OPERATION(ResumeNonThrowingContinuationReturning, "resumeNonThrowingContinuationReturning", "", Special) @@ -782,11 +793,28 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(CancelAsyncTask, "cancelAsyncTask", "", Speci /// __owned @Sendable @escaping () async throws -> T /// ) -> Builtin.RawPointer /// +/// DEPRECATED. startAsyncLetWithLocalBuffer is used instead. +/// /// Create, initialize and start a new async-let and associated task. /// Returns an AsyncLet* that must be passed to endAsyncLet for destruction. BUILTIN_MISC_OPERATION(StartAsyncLet, "startAsyncLet", "", Special) -/// asyncLetEnd(): (Builtin.RawPointer) -> Void +/// startAsyncLetWithLocalBuffer(): ( +/// __owned @Sendable @escaping () async throws -> T, +/// _ resultBuf: Builtin.RawPointer +/// ) -> Builtin.RawPointer +/// +/// Create, initialize and start a new async-let and associated task, with a +/// locally-allocated buffer assigned to receive the result if the task +/// completes. +/// Returns an AsyncLet* that must be passed to endAsyncLetLifetime for +/// destruction. +BUILTIN_MISC_OPERATION(StartAsyncLetWithLocalBuffer, "startAsyncLetWithLocalBuffer", "", Special) + +/// endAsyncLet(): (Builtin.RawPointer) -> Void +/// +/// DEPRECATED. The swift_asyncLet_finish intrinsic and endAsyncLetLifetime +/// builtin are used instead. /// /// Ends and destroys an async-let. /// The ClosureLifetimeFixup pass adds a second operand to the builtin to @@ -794,26 +822,34 @@ BUILTIN_MISC_OPERATION(StartAsyncLet, "startAsyncLet", "", Special) /// until the endAsyncLet. BUILTIN_MISC_OPERATION_WITH_SILGEN(EndAsyncLet, "endAsyncLet", "", Special) -/// createAsyncTaskFuture(): ( -/// Int, Builtin.NativeObject?, @escaping () async throws -> T +/// endAsyncLetLifetime(): (Builtin.RawPointer) -> Void +/// +/// Marks the end of an async-let's lifetime. +/// The ClosureLifetimeFixup pass adds a second operand to the builtin to +/// ensure that optimizations keep the stack-allocated closure arguments alive +/// until the endAsyncLet. +BUILTIN_MISC_OPERATION(EndAsyncLetLifetime, "endAsyncLetLifetime", "", Special) + +/// createAsyncTask(): ( +/// Int, // task-creation flags +/// @escaping () async throws -> T // function /// ) -> Builtin.NativeObject /// -/// Create a new asynchronous task future, given flags, an (optional) parent -/// task and a function to execute. -BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTaskFuture, - "createAsyncTaskFuture", "", Special) +/// Create a new asynchronous task, given flags, options, and a function to +/// execute. +BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTask, + "createAsyncTask", "", Special) -/// createAsyncTaskGroupFuture(): ( -/// Int, // flags -/// Builtin.NativeObject?, // parent -/// Builtin.RawPointer?, // group -/// @escaping () async throws -> T +/// createAsyncTaskInGroup(): ( +/// Int, // flags +/// Builtin.RawPointer, // group +/// @escaping () async throws -> T // function /// ) -> Builtin.NativeObject /// /// Create a new asynchronous task future, given flags, a parent task, /// task group and a function to execute. -BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTaskGroupFuture, - "createAsyncTaskGroupFuture", "", Special) +BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTaskInGroup, + "createAsyncTaskInGroup", "", Special) /// globalStringTablePointer has type String -> Builtin.RawPointer. /// It returns an immortal, global string table pointer for strings constructed diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 64e28f1a970ad..77c2fc454cd0a 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -236,6 +236,8 @@ class ClangModuleLoader : public ModuleLoader { instantiateCXXFunctionTemplate(ASTContext &ctx, clang::FunctionTemplateDecl *func, SubstitutionMap subst) = 0; + + virtual bool isCXXMethodMutating(const clang::CXXMethodDecl *method) = 0; }; /// Describes a C++ template instantiation error. diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index b254f60ab1256..1c0be923650d5 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -228,6 +228,9 @@ struct OverloadSignature { /// Whether this is a function. unsigned IsFunction : 1; + /// Whether this is an async function. + unsigned IsAsyncFunction : 1; + /// Whether this is a enum element. unsigned IsEnumElement : 1; @@ -249,8 +252,9 @@ struct OverloadSignature { OverloadSignature() : UnaryOperator(UnaryOperatorKind::None), IsInstanceMember(false), - IsVariable(false), IsFunction(false), InProtocolExtension(false), - InExtensionOfGenericType(false), HasOpaqueReturnType(false) { } + IsVariable(false), IsFunction(false), IsAsyncFunction(false), + InProtocolExtension(false), InExtensionOfGenericType(false), + HasOpaqueReturnType(false) { } }; /// Determine whether two overload signatures conflict. @@ -288,7 +292,7 @@ enum class ArtificialMainKind : uint8_t { }; /// Decl - Base class for all declarations in Swift. -class alignas(1 << DeclAlignInBits) Decl { +class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated { protected: union { uint64_t OpaqueBits; @@ -318,10 +322,13 @@ class alignas(1 << DeclAlignInBits) Decl { Hoisted : 1 ); - SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+2+16, + SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+1+2+16, /// Whether this pattern binding declares static variables. IsStatic : 1, + /// Whether this pattern binding is synthesized by the debugger. + IsDebugger : 1, + /// Whether 'static' or 'class' was used. StaticSpelling : 2, @@ -396,10 +403,13 @@ class alignas(1 << DeclAlignInBits) Decl { SWIFT_INLINE_BITFIELD(SubscriptDecl, VarDecl, 2, StaticSpelling : 2 ); - SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+8+1+1+1+1+1+1, /// \see AbstractFunctionDecl::BodyKind BodyKind : 3, + /// \see AbstractFunctionDecl::SILSynthesizeKind + SILSynthesizeKind : 2, + /// Import as member status. IAMStatus : 8, @@ -423,7 +433,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasNestedTypeDeclarations : 1 ); - SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2+1+1+1, + SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2+1, /// Whether we've computed the 'static' flag yet. IsStaticComputed : 1, @@ -442,12 +452,6 @@ class alignas(1 << DeclAlignInBits) Decl { /// Backing bits for 'self' access kind. SelfAccess : 2, - /// Whether we've computed the IsAsyncHandlerRequest. - IsAsyncHandlerComputed : 1, - - /// The value of IsAsyncHandlerRequest. - IsAsyncHandler : 1, - /// Whether this is a top-level function which should be treated /// as if it were in local context for the purposes of capture /// analysis. @@ -523,12 +527,6 @@ class alignas(1 << DeclAlignInBits) Decl { /// Whether the existential of this protocol conforms to itself. ExistentialConformsToSelf : 1, - /// Whether the \c ExistentialTypeSupported bit is valid. - ExistentialTypeSupportedValid : 1, - - /// Whether the existential of this protocol can be represented. - ExistentialTypeSupported : 1, - /// True if the protocol has requirements that cannot be satisfied (e.g. /// because they could not be imported from Objective-C). HasMissingRequirements : 1, @@ -539,6 +537,12 @@ class alignas(1 << DeclAlignInBits) Decl { /// Whether we have a lazy-loaded requirement signature. HasLazyRequirementSignature : 1, + /// Whether we have computed the list of associated types. + HasAssociatedTypes : 1, + + /// Whether we have a lazy-loaded list of associated types. + HasLazyAssociatedTypes : 1, + : NumPadBits, /// If this is a compiler-known protocol, this will be a KnownProtocolKind @@ -588,7 +592,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasAnyUnavailableValues : 1 ); - SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1, /// If the module is compiled as static library. StaticLibrary : 1, @@ -623,7 +627,11 @@ class alignas(1 << DeclAlignInBits) Decl { IsMainModule : 1, /// Whether this module has incremental dependency information available. - HasIncrementalInfo : 1 + HasIncrementalInfo : 1, + + /// Whether this module has been compiled with comprehensive checking for + /// concurrency, e.g., Sendable checking. + IsConcurrencyChecked : 1 ); SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2, @@ -1008,19 +1016,6 @@ class alignas(1 << DeclAlignInBits) Decl { /// Retrieve the diagnostic engine for diagnostics emission. LLVM_READONLY DiagnosticEngine &getDiags() const; - - // Make vanilla new/delete illegal for Decls. - void *operator new(size_t Bytes) = delete; - void operator delete(void *Data) = delete; - - // Only allow allocation of Decls using the allocator in ASTContext - // or by doing a placement new. - void *operator new(size_t Bytes, const ASTContext &C, - unsigned Alignment = alignof(Decl)); - void *operator new(size_t Bytes, void *Mem) { - assert(Mem); - return Mem; - } }; /// Allocates memory for a Decl with the given \p baseSize. If necessary, @@ -1208,6 +1203,17 @@ class ImportDecl final : public Decl, } }; +/// An entry in the "inherited" list of a type or extension. +struct InheritedEntry : public TypeLoc { + /// Whether there was an @unchecked attribute. + bool isUnchecked = false; + + InheritedEntry(const TypeLoc &typeLoc); + + InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked) + : TypeLoc(typeLoc), isUnchecked(isUnchecked) { } +}; + /// ExtensionDecl - This represents a type extension containing methods /// associated with the type. This is not a ValueDecl and has no Type because /// there are no runtime values of the Extension's type. @@ -1226,7 +1232,7 @@ class ExtensionDecl final : public GenericContext, public Decl, /// extended nominal. llvm::PointerIntPair ExtendedNominal; - ArrayRef Inherited; + ArrayRef Inherited; /// The next extension in the linked list of extensions. /// @@ -1245,7 +1251,7 @@ class ExtensionDecl final : public GenericContext, public Decl, friend class IterableDeclContext; ExtensionDecl(SourceLoc extensionLoc, TypeRepr *extendedType, - ArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause); @@ -1270,7 +1276,7 @@ class ExtensionDecl final : public GenericContext, public Decl, /// Create a new extension declaration. static ExtensionDecl *create(ASTContext &ctx, SourceLoc extensionLoc, TypeRepr *extendedType, - ArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause, ClangNode clangNode = ClangNode()); @@ -1318,13 +1324,16 @@ class ExtensionDecl final : public GenericContext, public Decl, bool alreadyBoundToNominal() const { return NextExtension.getInt(); } /// Retrieve the extended type definition as written in the source, if it exists. + /// + /// Repr would not be available if the extension was been loaded + /// from a serialized module. TypeRepr *getExtendedTypeRepr() const { return ExtendedTypeRepr; } /// Retrieve the set of protocols that this type inherits (i.e, /// explicitly conforms to). - ArrayRef getInherited() const { return Inherited; } + ArrayRef getInherited() const { return Inherited; } - void setInherited(ArrayRef i) { Inherited = i; } + void setInherited(ArrayRef i) { Inherited = i; } bool hasDefaultAccessLevel() const { return Bits.ExtensionDecl.DefaultAndMaxAccessLevel != 0; @@ -1389,6 +1398,7 @@ class ExtensionDecl final : public GenericContext, public Decl, } using DeclContext::operator new; + using DeclContext::operator delete; }; /// Iterator that walks the extensions of a particular type. @@ -1477,9 +1487,10 @@ class PatternBindingEntry { enum class PatternFlags { IsText = 1 << 0, IsFullyValidated = 1 << 1, + IsFromDebugger = 1 << 2, }; /// The initializer context used for this pattern binding entry. - llvm::PointerIntPair> + llvm::PointerIntPair> InitContextAndFlags; /// Values captured by this initializer. @@ -1507,6 +1518,14 @@ class PatternBindingEntry { PatternFlags::IsFullyValidated); } + /// Set if this pattern binding came from the debugger. + /// + /// Stay away unless you are \c PatternBindingDecl::createForDebugger + void setFromDebugger() { + InitContextAndFlags.setInt(InitContextAndFlags.getInt() | + PatternFlags::IsFromDebugger); + } + public: /// \p E is the initializer as parsed. PatternBindingEntry(Pattern *P, SourceLoc EqualLoc, Expr *E, @@ -1589,6 +1608,11 @@ class PatternBindingEntry { PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Subsumed); } + /// Returns \c true if the debugger created this pattern binding entry. + bool isFromDebugger() const { + return InitContextAndFlags.getInt().contains(PatternFlags::IsFromDebugger); + } + // Return the first variable initialized by this pattern. VarDecl *getAnchoringVarDecl() const; @@ -1674,6 +1698,13 @@ class PatternBindingDecl final : public Decl, unsigned NumPatternEntries, DeclContext *Parent); + // A dedicated entrypoint that allows LLDB to create pattern bindings + // that look implicit to the compiler but contain user code. + static PatternBindingDecl *createForDebugger(ASTContext &Ctx, + StaticSpellingKind Spelling, + Pattern *Pat, Expr *E, + DeclContext *Parent); + SourceLoc getStartLoc() const { return StaticLoc.isValid() ? StaticLoc : VarLoc; } @@ -1865,7 +1896,7 @@ class PatternBindingDecl final : public Decl, bool isComputingPatternBindingEntry(const VarDecl *vd) const; /// Is this an "async let" declaration? - bool isSpawnLet() const; + bool isAsyncLet() const; /// Gets the text of the initializer expression for the pattern entry at the /// given index, stripping out inactive branches of any #ifs inside the @@ -1875,6 +1906,9 @@ class PatternBindingDecl final : public Decl, return getPatternList()[i].getInitStringRepresentation(scratch); } + /// Returns \c true if this pattern binding was created by the debugger. + bool isDebuggerBinding() const { return Bits.PatternBindingDecl.IsDebugger; } + static bool classof(const Decl *D) { return D->getKind() == DeclKind::PatternBinding; } @@ -1919,6 +1953,7 @@ class TopLevelCodeDecl : public DeclContext, public Decl { } using DeclContext::operator new; + using DeclContext::operator delete; }; /// SerializedTopLevelCodeDeclContext - This represents what was originally a @@ -2346,12 +2381,18 @@ class ValueDecl : public Decl { /// Note whether this declaration is known to be exposed to Objective-C. void setIsObjC(bool Value); + /// Is this declaration semantically 'final', meaning that the type checker + /// should treat it as final even if the ABI does not? + bool isSemanticallyFinal() const; + /// Is this declaration 'final'? bool isFinal() const; /// Is this declaration marked with 'dynamic'? bool isDynamic() const; + bool isDistributedActorIndependent() const; + private: bool isObjCDynamic() const { return isObjC() && isDynamic(); @@ -2486,7 +2527,10 @@ class ValueDecl : public Decl { OpaqueTypeDecl *getOpaqueResultTypeDecl() const; /// Get the representative for this value's opaque result type, if it has one. - OpaqueReturnTypeRepr *getOpaqueResultTypeRepr() const; + /// Returns a `TypeRepr` instead of an `OpaqueReturnTypeRepr` because 'some' + /// types might appear in one or more structural positions, e.g. (some P, + /// some Q), or we might have a `NamedOpaqueReturnTypeRepr`. + TypeRepr *getOpaqueResultTypeRepr() const; /// Retrieve the attribute associating this declaration with a /// result builder, if there is one. @@ -2504,12 +2548,12 @@ class ValueDecl : public Decl { /// This is a common base class for declarations which declare a type. class TypeDecl : public ValueDecl { - ArrayRef Inherited; + ArrayRef Inherited; protected: TypeDecl(DeclKind K, llvm::PointerUnion context, Identifier name, SourceLoc NameLoc, - ArrayRef inherited) : + ArrayRef inherited) : ValueDecl(K, context, name, NameLoc), Inherited(inherited) {} public: @@ -2527,9 +2571,9 @@ class TypeDecl : public ValueDecl { /// Retrieve the set of protocols that this type inherits (i.e, /// explicitly conforms to). - ArrayRef getInherited() const { return Inherited; } + ArrayRef getInherited() const { return Inherited; } - void setInherited(ArrayRef i) { Inherited = i; } + void setInherited(ArrayRef i) { Inherited = i; } static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_TypeDecl && @@ -2554,12 +2598,13 @@ class GenericTypeDecl : public GenericContext, public TypeDecl { public: GenericTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc nameLoc, - ArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams); // Resolve ambiguity due to multiple base classes. using TypeDecl::getASTContext; using DeclContext::operator new; + using DeclContext::operator delete; using TypeDecl::getDeclaredInterfaceType; static bool classof(const DeclContext *C) { @@ -2578,26 +2623,30 @@ class GenericTypeDecl : public GenericContext, public TypeDecl { /// clients of the opaque type, only exposing the type as something conforming /// to a given set of constraints. /// -/// Currently, opaque types do not normally have an explicit spelling in source -/// code. One is formed implicitly when a declaration is written with an opaque -/// result type, as in: +/// An `OpaqueTypeDecl` is formed implicitly when a declaration is written with +/// an opaque result type, as in the following example: /// /// func foo() -> some SignedInteger { return 1 } /// -/// The declared type is a special kind of ArchetypeType representing the -/// abstracted underlying type. +/// The declared type uses a special kind of archetype type to represent +/// abstracted types, e.g. `(some P, some Q)` becomes `((opaque archetype 0), +/// (opaque archetype 1))`. class OpaqueTypeDecl : public GenericTypeDecl { /// The original declaration that "names" the opaque type. Although a specific /// opaque type cannot be explicitly named, oapque types can propagate /// arbitrarily through expressions, so we need to know *which* opaque type is /// propagated. ValueDecl *NamingDecl; - + /// The generic signature of the opaque interface to the type. This is the - /// outer generic signature with an added generic parameter representing the - /// underlying type. + /// outer generic signature with added generic parameters representing the + /// abstracted underlying types. GenericSignature OpaqueInterfaceGenericSignature; - + + /// The type repr of the underlying type. Might be null if no source location + /// is availble, e.g. if this decl was loaded from a serialized module. + OpaqueReturnTypeRepr *UnderlyingInterfaceRepr; + /// The generic parameter that represents the underlying type. GenericTypeParamType *UnderlyingInterfaceType; @@ -2610,12 +2659,12 @@ class OpaqueTypeDecl : public GenericTypeDecl { mutable Identifier OpaqueReturnTypeIdentifier; public: - OpaqueTypeDecl(ValueDecl *NamingDecl, - GenericParamList *GenericParams, + OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams, DeclContext *DC, GenericSignature OpaqueInterfaceGenericSignature, + OpaqueReturnTypeRepr *UnderlyingInterfaceRepr, GenericTypeParamType *UnderlyingInterfaceType); - + ValueDecl *getNamingDecl() const { return NamingDecl; } void setNamingDecl(ValueDecl *D) { @@ -2629,6 +2678,11 @@ class OpaqueTypeDecl : public GenericTypeDecl { /// function could also be the getter of a storage declaration. bool isOpaqueReturnTypeOfFunction(const AbstractFunctionDecl *func) const; + /// Get the ordinal of the anonymous opaque parameter of this decl with type + /// repr `repr`, as introduce implicitly by an occurrence of "some" in return + /// position e.g. `func f() -> some P`. Returns -1 if `repr` is not found. + unsigned getAnonymousOpaqueParamOrdinal(OpaqueReturnTypeRepr *repr) const; + GenericSignature getOpaqueInterfaceGenericSignature() const { return OpaqueInterfaceGenericSignature; } @@ -3081,7 +3135,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { NominalTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc NameLoc, - ArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams) : GenericTypeDecl(K, DC, name, NameLoc, inherited, GenericParams), IterableDeclContext(IterableDeclContextKind::NominalTypeDecl) @@ -3151,12 +3205,6 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Whether to include @_implements members. /// Used by conformance-checking to find special @_implements members. IncludeAttrImplements = 1 << 0, - /// Whether to avoid loading lazy members from any new extensions that would otherwise be found - /// by deserialization. - /// - /// Used by the module loader to break recursion and as an optimization e.g. when it is known that a - /// particular member declaration will never appear in an extension. - IgnoreNewExtensions = 1 << 1, }; /// Find all of the declarations with the given name within this nominal type @@ -3170,6 +3218,11 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { OptionSet flags = OptionSet()); + /// Find the '_remote_<...>' counterpart function to a 'distributed func'. + /// + /// If the passed in function is not distributed this function returns null. + AbstractFunctionDecl* lookupDirectRemoteFunc(AbstractFunctionDecl *func); + /// Collect the set of protocols to which this type should implicitly /// conform, such as AnyObject (for classes). void getImplicitProtocols(SmallVectorImpl &protocols); @@ -3177,16 +3230,13 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Look for conformances of this nominal type to the given /// protocol. /// - /// \param module The module from which we initiate the search. - /// FIXME: This is currently unused. - /// /// \param protocol The protocol whose conformance is requested. /// \param conformances Will be populated with the set of protocol /// conformances found for this protocol. /// /// \returns true if any conformances were found. bool lookupConformance( - ModuleDecl *module, ProtocolDecl *protocol, + ProtocolDecl *protocol, SmallVectorImpl &conformances) const; /// Retrieve all of the protocols that this nominal type conforms to. @@ -3201,7 +3251,8 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// /// This is used by deserialization of module files to report /// conformances. - void registerProtocolConformance(ProtocolConformance *conformance); + void registerProtocolConformance(ProtocolConformance *conformance, + bool synthesized = false); void setConformanceLoader(LazyMemberLoader *resolver, uint64_t contextData); @@ -3226,6 +3277,13 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// `Actor` protocol. bool isActor() const; + /// Whether this nominal type qualifies as a distributed actor, meaning that + /// it is either a distributed actor. + bool isDistributedActor() const; + + /// Whether this nominal type qualifies as any actor (plain or distributed). + bool isAnyActor() const; + /// Return the range of semantics attributes attached to this NominalTypeDecl. auto getSemanticsAttrs() const -> decltype(getAttrs().getSemanticsAttrs()) { @@ -3236,6 +3294,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { return getAttrs().hasSemanticsAttr(attrValue); } + /// Returns true if we should emit assembly vision remarks on all methods of + /// this nominal type. + bool shouldEmitAssemblyVisionRemarksOnMethods() const { + return getAttrs().hasAttribute(); + } + /// Whether this declaration has a synthesized memberwise initializer. bool hasMemberwiseInitializer() const; @@ -3360,7 +3424,7 @@ class EnumDecl final : public NominalTypeDecl { public: EnumDecl(SourceLoc EnumLoc, Identifier Name, SourceLoc NameLoc, - ArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC); SourceLoc getStartLoc() const { return EnumLoc; } @@ -3528,7 +3592,7 @@ class StructDecl final : public NominalTypeDecl { public: StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, - ArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC); SourceLoc getStartLoc() const { return StructLoc; } @@ -3674,7 +3738,7 @@ class ClassDecl final : public NominalTypeDecl { public: ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, - ArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *DC, bool isActor); @@ -3778,6 +3842,12 @@ class ClassDecl final : public NominalTypeDecl { /// Whether the class was explicitly declared with the `actor` keyword. bool isExplicitActor() const { return Bits.ClassDecl.IsActor; } + /// Whether the class was explicitly declared with the `distributed actor` keywords. + bool isExplicitDistributedActor() const { + return isExplicitActor() && + getAttrs().hasAttribute(); + } + /// Get the closest-to-root superclass that's an actor class. const ClassDecl *getRootActorClass() const; @@ -3908,7 +3978,7 @@ class ClassDecl final : public NominalTypeDecl { /// Returns true if the decl uses the Objective-C generics model. /// /// This is true of imported Objective-C classes. - bool usesObjCGenericsModel() const { + bool isTypeErasedGenericClass() const { return hasClangNode() && isGenericContext() && isObjC(); } @@ -4009,6 +4079,7 @@ enum class KnownDerivableProtocolKind : uint8_t { AdditiveArithmetic, Differentiable, Actor, + DistributedActor, }; /// ProtocolDecl - A declaration of a protocol, for example: @@ -4028,6 +4099,7 @@ class ProtocolDecl final : public NominalTypeDecl { SourceLoc ProtocolLoc; ArrayRef InheritedProtocols; + ArrayRef AssociatedTypes; struct { /// The superclass decl and a bit to indicate whether the @@ -4073,21 +4145,6 @@ class ProtocolDecl final : public NominalTypeDecl { Bits.ProtocolDecl.ExistentialConformsToSelf = result; } - /// Returns the cached result of \c existentialTypeSupported or \c None if it - /// hasn't yet been computed. - Optional getCachedExistentialTypeSupported() { - if (Bits.ProtocolDecl.ExistentialTypeSupportedValid) - return Bits.ProtocolDecl.ExistentialTypeSupported; - - return None; - } - - /// Caches the result of \c existentialTypeSupported - void setCachedExistentialTypeSupported(bool supported) { - Bits.ProtocolDecl.ExistentialTypeSupportedValid = true; - Bits.ProtocolDecl.ExistentialTypeSupported = supported; - } - bool hasLazyRequirementSignature() const { return Bits.ProtocolDecl.HasLazyRequirementSignature; } @@ -4097,12 +4154,11 @@ class ProtocolDecl final : public NominalTypeDecl { friend class RequirementSignatureRequest; friend class ProtocolRequiresClassRequest; friend class ExistentialConformsToSelfRequest; - friend class ExistentialTypeSupportedRequest; friend class InheritedProtocolsRequest; public: ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc, - Identifier Name, ArrayRef Inherited, + Identifier Name, ArrayRef Inherited, TrailingWhereClause *TrailingWhere); using Decl::getASTContext; @@ -4126,7 +4182,7 @@ class ProtocolDecl final : public NominalTypeDecl { /// Retrieve the set of AssociatedTypeDecl members of this protocol; this /// saves loading the set of members in cases where there's no possibility of /// a protocol having nested types (ObjC protocols). - llvm::TinyPtrVector getAssociatedTypeMembers() const; + ArrayRef getAssociatedTypeMembers() const; /// Returns a protocol requirement with the given name, or nullptr if the /// name has multiple overloads, or no overloads at all. @@ -4186,12 +4242,6 @@ class ProtocolDecl final : public NominalTypeDecl { /// contain 'Self' in 'parameter' or 'other' position. bool isAvailableInExistential(const ValueDecl *decl) const; - /// Determine whether we are allowed to refer to an existential type - /// conforming to this protocol. This is only permitted if the types of - /// all the members do not contain any associated types, and do not - /// contain 'Self' in 'parameter' or 'other' position. - bool existentialTypeSupported() const; - /// Returns a list of protocol requirements that must be assessed to /// determine a concrete's conformance effect polymorphism kind. PolymorphicEffectRequirementList getPolymorphicEffectRequirements( @@ -4202,6 +4252,10 @@ class ProtocolDecl final : public NominalTypeDecl { /// semantics but has no corresponding witness table. bool isMarkerProtocol() const; + /// Is a protocol that can only be conformed by distributed actors. + /// Such protocols are allowed to contain distributed functions. + bool inheritsFromDistributedActor() const; + private: void computeKnownProtocolKind() const; @@ -4306,6 +4360,9 @@ class ProtocolDecl final : public NominalTypeDecl { void setLazyRequirementSignature(LazyMemberLoader *lazyLoader, uint64_t requirementSignatureData); + void setLazyAssociatedTypeMembers(LazyMemberLoader *lazyLoader, + uint64_t associatedTypesData); + private: ArrayRef getCachedRequirementSignature() const; @@ -4720,6 +4777,8 @@ class AbstractStorageDecl : public ValueDecl { bool hasAnyNativeDynamicAccessors() const; + bool isDistributedActorIndependent() const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_AbstractStorageDecl && @@ -4945,7 +5004,7 @@ class VarDecl : public AbstractStorageDecl { bool isLet() const { return getIntroducer() == Introducer::Let; } /// Is this an "async let" property? - bool isSpawnLet() const; + bool isAsyncLet() const; Introducer getIntroducer() const { return Introducer(Bits.VarDecl.Introducer); @@ -5176,7 +5235,7 @@ class VarDecl : public AbstractStorageDecl { return getAttrs().getAttributes(); } - /// Returns true if this VarDelc has the string \p attrValue as a semantics + /// Returns true if this VarDecl has the string \p attrValue as a semantics /// attribute. bool hasSemanticsAttr(StringRef attrValue) const { return llvm::any_of(getSemanticsAttrs(), [&](const SemanticsAttr *attr) { @@ -5209,7 +5268,7 @@ class ParamDecl : public VarDecl { TypeRepr *TyRepr = nullptr; - struct StoredDefaultArgument { + struct alignas(1 << DeclAlignInBits) StoredDefaultArgument { PointerUnion DefaultArg; /// Stores the context for the default argument as well as a bit to @@ -5230,10 +5289,13 @@ class ParamDecl : public VarDecl { /// Whether or not this parameter is `@autoclosure`. IsAutoClosure = 1 << 1, + + /// Whether or not this parameter is 'isolated'. + IsIsolated = 1 << 2, }; /// The default value, if any, along with flags. - llvm::PointerIntPair> + llvm::PointerIntPair> DefaultValueAndFlags; friend class ParamSpecifierRequest; @@ -5245,7 +5307,10 @@ class ParamDecl : public VarDecl { /// Create a new ParamDecl identical to the first except without the interface type. static ParamDecl *cloneWithoutType(const ASTContext &Ctx, ParamDecl *PD); - + + /// Create a an identical copy of this ParamDecl. + static ParamDecl *clone(const ASTContext &Ctx, ParamDecl *PD); + /// Retrieve the argument (API) name for this function parameter. Identifier getArgumentName() const { return ArgumentNameAndDestructured.getPointer(); @@ -5384,6 +5449,17 @@ class ParamDecl : public VarDecl { : flags - Flags::IsAutoClosure); } + /// Whether or not this parameter is marked with 'isolated'. + bool isIsolated() const { + return DefaultValueAndFlags.getInt().contains(Flags::IsIsolated); + } + + void setIsolated(bool value = true) { + auto flags = DefaultValueAndFlags.getInt(); + DefaultValueAndFlags.setInt(value ? flags | Flags::IsIsolated + : flags - Flags::IsIsolated); + } + /// Does this parameter reject temporary pointer conversions? bool isNonEphemeral() const; @@ -5620,6 +5696,7 @@ class SubscriptDecl : public GenericContext, public AbstractStorageDecl { } using DeclContext::operator new; + using DeclContext::operator delete; using Decl::getASTContext; }; @@ -5660,6 +5737,15 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { friend class NeedsNewVTableEntryRequest; public: + /// records the kind of SILGen-synthesized body this decl represents + enum class SILSynthesizeKind { + None, + MemberwiseInitializer, + DistributedActorFactory + + // This enum currently needs to fit in a 2-bit bitfield. + }; + enum class BodyKind { /// The function did not have a body in the source code file. None, @@ -5679,8 +5765,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Function body is present and type-checked. TypeChecked, - /// This is a memberwise initializer that will be synthesized by SILGen. - MemberwiseInitializer, + // Function body will be synthesized by SILGen. + SILSynthesize, /// Function body text was deserialized from a .swiftmodule. Deserialized @@ -5780,6 +5866,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { Bits.AbstractFunctionDecl.BodyKind = unsigned(K); } + void setSILSynthesizeKind(SILSynthesizeKind K) { + Bits.AbstractFunctionDecl.SILSynthesizeKind = unsigned(K); + } + + SILSynthesizeKind getSILSynthesizeKind() const { + return SILSynthesizeKind(Bits.AbstractFunctionDecl.SILSynthesizeKind); + } + public: void setHasSingleExpressionBody(bool Has = true) { Bits.AbstractFunctionDecl.HasSingleExpressionBody = Has; @@ -5849,16 +5943,9 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// /// Functions that are an 'async' context can make calls to 'async' functions. bool isAsyncContext() const { - return hasAsync() || isAsyncHandler(); + return hasAsync(); } - /// Returns true if the function is an @asyncHandler. - bool isAsyncHandler() const; - - /// Returns true if the function signature matches the form of an - /// @asyncHandler. - bool canBeAsyncHandler() const; - /// Returns true if the function body throws. bool hasThrows() const { return Bits.AbstractFunctionDecl.Throws; } @@ -5868,6 +5955,13 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Returns if the function is 'rethrows' or 'reasync'. bool hasPolymorphicEffect(EffectKind kind) const; + /// Returns 'true' if the function is distributed. + bool isDistributed() const; + + /// Get (or synthesize) the associated remote function for this one. + /// For example, for `distributed func hi()` get `func _remote_hi()`. + AbstractFunctionDecl *getDistributedActorRemoteFuncDecl() const; + PolymorphicEffectKind getPolymorphicEffectKind(EffectKind kind) const; // FIXME: Hack that provides names with keyword arguments for accessors. @@ -5960,7 +6054,17 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { void setIsMemberwiseInitializer() { assert(getBodyKind() == BodyKind::None); assert(isa(this)); - setBodyKind(BodyKind::MemberwiseInitializer); + setBodyKind(BodyKind::SILSynthesize); + setSILSynthesizeKind(SILSynthesizeKind::MemberwiseInitializer); + } + + /// Mark that the body should be filled in to be a factory method for creating + /// a distributed actor. + void setDistributedActorFactory() { + assert(getBodyKind() == BodyKind::None); + assert(isa(this)); + setBodyKind(BodyKind::SILSynthesize); + setSILSynthesizeKind(SILSynthesizeKind::DistributedActorFactory); } /// Gets the body of this function, stripping the unused portions of #if @@ -5984,7 +6088,16 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { } bool isMemberwiseInitializer() const { - return getBodyKind() == BodyKind::MemberwiseInitializer; + return getBodyKind() == BodyKind::SILSynthesize + && getSILSynthesizeKind() == SILSynthesizeKind::MemberwiseInitializer; + } + + /// Determines whether this function represents a distributed actor + /// initialization factory. Such functions do not have a body that is + /// representable in the AST, so it must be synthesized during SILGen. + bool isDistributedActorFactory() const { + return getBodyKind() == BodyKind::SILSynthesize + && getSILSynthesizeKind() == SILSynthesizeKind::DistributedActorFactory; } /// For a method of a class, checks whether it will require a new entry in the @@ -6111,9 +6224,22 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// constructor. bool hasDynamicSelfResult() const; - + /// The async function marked as the alternative to this function, if any. AbstractFunctionDecl *getAsyncAlternative() const; + /// If \p asyncAlternative is set, then compare its parameters to this + /// (presumed synchronous) function's parameters to find the index of the + /// completion handler parameter. This should be the the only missing + /// parameter in \p asyncAlternative, ignoring defaulted parameters if they + /// have the same label. It must have a void-returning function type and be + /// attributed with @escaping but not @autoclosure. + /// + /// Returns the last index of the parameter that looks like a completion + /// handler if \p asyncAlternative is not set (with the same conditions on + /// its type as above). + Optional findPotentialCompletionHandlerParam( + const AbstractFunctionDecl *asyncAlternative = nullptr) const; + /// Determine whether this function is implicitly known to have its /// parameters of function type be @_unsafeSendable. /// @@ -6123,6 +6249,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { bool hasKnownUnsafeSendableFunctionParams() const; using DeclContext::operator new; + using DeclContext::operator delete; using Decl::getASTContext; }; @@ -6143,7 +6270,6 @@ class FuncDecl : public AbstractFunctionDecl { friend class SelfAccessKindRequest; friend class IsStaticRequest; friend class ResultTypeRequest; - friend class IsAsyncHandlerRequest; SourceLoc StaticLoc; // Location of the 'static' token or invalid. SourceLoc FuncLoc; // Location of the 'func' token. @@ -6175,8 +6301,6 @@ class FuncDecl : public AbstractFunctionDecl { Bits.FuncDecl.SelfAccessComputed = false; Bits.FuncDecl.IsStaticComputed = false; Bits.FuncDecl.IsStatic = false; - Bits.FuncDecl.IsAsyncHandlerComputed = false; - Bits.FuncDecl.IsAsyncHandler = false; Bits.FuncDecl.HasTopLevelLocalContextCaptures = false; } @@ -6207,18 +6331,6 @@ class FuncDecl : public AbstractFunctionDecl { return None; } - Optional getCachedIsAsyncHandler() const { - if (Bits.FuncDecl.IsAsyncHandlerComputed) - return Bits.FuncDecl.IsAsyncHandler; - - return None; - } - - void setIsAsyncHandler(bool value) { - Bits.FuncDecl.IsAsyncHandlerComputed = true; - Bits.FuncDecl.IsAsyncHandler = value; - } - public: /// Factory function only for use by deserialization. static FuncDecl *createDeserialized(ASTContext &Context, @@ -6506,6 +6618,10 @@ class AccessorDecl final : public FuncDecl { Bits.AccessorDecl.IsTransparentComputed = 1; } + /// A representation of the name to be displayed to users. \c getNameStr + /// for anything other than a getter or setter. + void printUserFacingName(llvm::raw_ostream &out) const; + static bool classof(const Decl *D) { return D->getKind() == DeclKind::Accessor; } @@ -6665,6 +6781,7 @@ class EnumElementDecl : public DeclContext, public ValueDecl { } using DeclContext::operator new; + using DeclContext::operator delete; using Decl::getASTContext; }; @@ -7166,22 +7283,13 @@ class OperatorDecl : public Decl { Identifier name; - ArrayRef> Identifiers; - ArrayRef DesignatedNominalTypes; SourceLoc getLocFromSource() const { return NameLoc; } friend class Decl; public: OperatorDecl(DeclKind kind, DeclContext *DC, SourceLoc OperatorLoc, - Identifier Name, SourceLoc NameLoc, - ArrayRef> Identifiers) - : Decl(kind, DC), OperatorLoc(OperatorLoc), NameLoc(NameLoc), name(Name), - Identifiers(Identifiers) {} - - OperatorDecl(DeclKind kind, DeclContext *DC, SourceLoc OperatorLoc, - Identifier Name, SourceLoc NameLoc, - ArrayRef DesignatedNominalTypes) - : Decl(kind, DC), OperatorLoc(OperatorLoc), NameLoc(NameLoc), name(Name), - DesignatedNominalTypes(DesignatedNominalTypes) {} + Identifier Name, SourceLoc NameLoc) + : Decl(kind, DC), OperatorLoc(OperatorLoc), NameLoc(NameLoc), name(Name) + {} /// Retrieve the operator's fixity, corresponding to the concrete subclass /// of the OperatorDecl. @@ -7208,25 +7316,6 @@ class OperatorDecl : public Decl { // OperatorDecls. DeclBaseName getBaseName() const { return name; } - /// Get the list of identifiers after the colon in the operator declaration. - /// - /// This list includes the names of designated types. For infix operators, the - /// first item in the list is a precedence group instead. - /// - /// \todo These two purposes really ought to be in separate properties and the - /// designated type list should be of TypeReprs instead of Identifiers. - ArrayRef> getIdentifiers() const { - return Identifiers; - } - - ArrayRef getDesignatedNominalTypes() const { - return DesignatedNominalTypes; - } - - void setDesignatedNominalTypes(ArrayRef nominalTypes) { - DesignatedNominalTypes = nominalTypes; - } - static bool classof(const Decl *D) { // Workaround: http://llvm.org/PR35906 if (DeclKind::Last_Decl == DeclKind::Last_OperatorDecl) @@ -7242,22 +7331,23 @@ class OperatorDecl : public Decl { /// infix operator /+/ : AdditionPrecedence, Numeric /// \endcode class InfixOperatorDecl : public OperatorDecl { - SourceLoc ColonLoc; + SourceLoc ColonLoc, PrecedenceGroupLoc; + Identifier PrecedenceGroupName; public: InfixOperatorDecl(DeclContext *DC, SourceLoc operatorLoc, Identifier name, SourceLoc nameLoc, SourceLoc colonLoc, - ArrayRef> identifiers) - : OperatorDecl(DeclKind::InfixOperator, DC, operatorLoc, name, nameLoc, - identifiers), - ColonLoc(colonLoc) {} + Identifier precedenceGroupName, + SourceLoc precedenceGroupLoc) + : OperatorDecl(DeclKind::InfixOperator, DC, operatorLoc, name, nameLoc), + ColonLoc(colonLoc), PrecedenceGroupLoc(precedenceGroupLoc), + PrecedenceGroupName(precedenceGroupName) {} SourceLoc getEndLoc() const { - auto identifiers = getIdentifiers(); - if (identifiers.empty()) - return getNameLoc(); + if (getPrecedenceGroupLoc().isValid()) + return getPrecedenceGroupLoc(); - return identifiers.back().Loc; + return getNameLoc(); } SourceRange getSourceRange() const { @@ -7266,6 +7356,8 @@ class InfixOperatorDecl : public OperatorDecl { SourceLoc getColonLoc() const { return ColonLoc; } + Identifier getPrecedenceGroupName() const { return PrecedenceGroupName; } + SourceLoc getPrecedenceGroupLoc() const { return PrecedenceGroupLoc; } PrecedenceGroupDecl *getPrecedenceGroup() const; static bool classof(const Decl *D) { @@ -7281,16 +7373,9 @@ class InfixOperatorDecl : public OperatorDecl { class PrefixOperatorDecl : public OperatorDecl { public: PrefixOperatorDecl(DeclContext *DC, SourceLoc OperatorLoc, Identifier Name, - SourceLoc NameLoc, - ArrayRef> Identifiers) - : OperatorDecl(DeclKind::PrefixOperator, DC, OperatorLoc, Name, NameLoc, - Identifiers) {} - - PrefixOperatorDecl(DeclContext *DC, SourceLoc OperatorLoc, Identifier Name, - SourceLoc NameLoc, - ArrayRef designatedNominalTypes) - : OperatorDecl(DeclKind::PrefixOperator, DC, OperatorLoc, Name, NameLoc, - designatedNominalTypes) {} + SourceLoc NameLoc) + : OperatorDecl(DeclKind::PrefixOperator, DC, OperatorLoc, Name, NameLoc) + {} SourceRange getSourceRange() const { return { getOperatorLoc(), getNameLoc() }; @@ -7309,16 +7394,9 @@ class PrefixOperatorDecl : public OperatorDecl { class PostfixOperatorDecl : public OperatorDecl { public: PostfixOperatorDecl(DeclContext *DC, SourceLoc OperatorLoc, Identifier Name, - SourceLoc NameLoc, - ArrayRef> Identifiers) - : OperatorDecl(DeclKind::PostfixOperator, DC, OperatorLoc, Name, NameLoc, - Identifiers) {} - - PostfixOperatorDecl(DeclContext *DC, SourceLoc OperatorLoc, Identifier Name, - SourceLoc NameLoc, - ArrayRef designatedNominalTypes) - : OperatorDecl(DeclKind::PostfixOperator, DC, OperatorLoc, Name, NameLoc, - designatedNominalTypes) {} + SourceLoc NameLoc) + : OperatorDecl(DeclKind::PostfixOperator, DC, OperatorLoc, Name, NameLoc) + {} SourceRange getSourceRange() const { return { getOperatorLoc(), getNameLoc() }; @@ -7486,7 +7564,8 @@ inline bool Decl::isPotentiallyOverridable() const { isa(this) || isa(this) || isa(this)) { - return getDeclContext()->getSelfClassDecl(); + auto classDecl = getDeclContext()->getSelfClassDecl(); + return classDecl && !classDecl->isActor(); } else { return false; } diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index 81e77457ba18d..2c5f68ce1c8c2 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -19,6 +19,7 @@ #ifndef SWIFT_DECLCONTEXT_H #define SWIFT_DECLCONTEXT_H +#include "swift/AST/ASTAllocated.h" #include "swift/AST/Identifier.h" #include "swift/AST/LookupKinds.h" #include "swift/AST/ResilienceExpansion.h" @@ -221,7 +222,8 @@ struct FragileFunctionKind { /// and therefore can safely access trailing memory. If you need to create a /// macro context, please see GenericContext for how to minimize new entries in /// the ASTHierarchy enum below. -class alignas(1 << DeclContextAlignInBits) DeclContext { +class alignas(1 << DeclContextAlignInBits) DeclContext + : public ASTAllocated { enum class ASTHierarchy : unsigned { Decl, Expr, @@ -618,16 +620,16 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { /// is also included. unsigned getSemanticDepth() const; + /// Returns if this extension is always available on the current deployment + /// target. Used for conformance lookup disambiguation. + bool isAlwaysAvailableConformanceContext() const; + /// \returns true if traversal was aborted, false otherwise. bool walkContext(ASTWalker &Walker); SWIFT_DEBUG_DUMPER(dumpContext()); unsigned printContext(llvm::raw_ostream &OS, unsigned indent = 0, bool onlyAPartialLine = false) const; - - // Only allow allocation of DeclContext using the allocator in ASTContext. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(DeclContext)); // Some Decls are DeclContexts, but not all. See swift/AST/Decl.h static bool classof(const Decl *D); diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 3267f7c125797..e34e3461005a3 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -22,6 +22,7 @@ #include "swift/AST/DeclNameLoc.h" #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/TypeLoc.h" +#include "swift/Basic/Version.h" #include "swift/Localization/LocalizationFormat.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/StringRef.h" @@ -115,6 +116,7 @@ namespace swift { VersionTuple, LayoutConstraint, ActorIsolation, + Diagnostic }; namespace diag { @@ -146,6 +148,7 @@ namespace swift { llvm::VersionTuple VersionVal; LayoutConstraint LayoutConstraintVal; ActorIsolation ActorIsolationVal; + DiagnosticInfo *DiagnosticVal; }; public: @@ -243,6 +246,11 @@ namespace swift { ActorIsolationVal(AI) { } + DiagnosticArgument(DiagnosticInfo *D) + : Kind(DiagnosticArgumentKind::Diagnostic), + DiagnosticVal(D) { + } + /// Initializes a diagnostic argument using the underlying type of the /// given enum. template< @@ -343,6 +351,11 @@ namespace swift { assert(Kind == DiagnosticArgumentKind::ActorIsolation); return ActorIsolationVal; } + + DiagnosticInfo *getAsDiagnostic() const { + assert(Kind == DiagnosticArgumentKind::Diagnostic); + return DiagnosticVal; + } }; /// Describes the current behavior to take with a diagnostic. @@ -410,6 +423,7 @@ namespace swift { DiagnosticBehavior BehaviorLimit = DiagnosticBehavior::Unspecified; friend DiagnosticEngine; + friend class InFlightDiagnostic; public: // All constructors are intentionally implicit. @@ -515,6 +529,60 @@ namespace swift { /// emitted as a warning, but a note will still be emitted as a note. InFlightDiagnostic &limitBehavior(DiagnosticBehavior limit); + /// Limit the diagnostic behavior to warning until the specified version. + /// + /// This helps stage in fixes for stricter diagnostics as warnings + /// until the next major language version. + InFlightDiagnostic &warnUntilSwiftVersion(unsigned majorVersion); + + /// Conditionally limit the diagnostic behavior to warning until + /// the specified version. If the condition is false, no limit is + /// imposed, meaning (presumably) it is treated as an error. + /// + /// This helps stage in fixes for stricter diagnostics as warnings + /// until the next major language version. + InFlightDiagnostic &warnUntilSwiftVersionIf(bool shouldLimit, + unsigned majorVersion) { + if (!shouldLimit) return *this; + return warnUntilSwiftVersion(majorVersion); + } + + /// Wraps this diagnostic in another diagnostic. That is, \p wrapper will be + /// emitted in place of the diagnostic that otherwise would have been + /// emitted. + /// + /// The first argument of \p wrapper must be of type 'Diagnostic *'. + /// + /// The emitted diagnostic will have: + /// + /// \li The ID, message, and behavior of \c wrapper. + /// \li The arguments of \c wrapper, with the last argument replaced by the + /// diagnostic currently in \c *this. + /// \li The location, ranges, decl, fix-its, and behavior limit of the + /// diagnostic currently in \c *this. + InFlightDiagnostic &wrapIn(const Diagnostic &wrapper); + + /// Wraps this diagnostic in another diagnostic. That is, \p ID and + /// \p VArgs will be emitted in place of the diagnostic that otherwise would + /// have been emitted. + /// + /// The first argument of \p ID must be of type 'Diagnostic *'. + /// + /// The emitted diagnostic will have: + /// + /// \li The ID, message, and behavior of \c ID. + /// \li The arguments of \c VArgs, with an argument appended for the + /// diagnostic currently in \c *this. + /// \li The location, ranges, decl, fix-its, and behavior limit of the + /// diagnostic currently in \c *this. + template + InFlightDiagnostic & + wrapIn(Diag ID, + typename detail::PassArgument::type... VArgs) { + Diagnostic wrapper{ID, nullptr, std::move(VArgs)...}; + return wrapIn(wrapper); + } + /// Add a token-based range to the currently-active diagnostic. InFlightDiagnostic &highlight(SourceRange R); @@ -678,6 +746,16 @@ namespace swift { ignoredDiagnostics[(unsigned)id] = ignored; } + void swap(DiagnosticState &other) { + std::swap(showDiagnosticsAfterFatalError, other.showDiagnosticsAfterFatalError); + std::swap(suppressWarnings, other.suppressWarnings); + std::swap(warningsAsErrors, other.warningsAsErrors); + std::swap(fatalErrorOccurred, other.fatalErrorOccurred); + std::swap(anyErrorOccurred, other.anyErrorOccurred); + std::swap(previousBehavior, other.previousBehavior); + std::swap(ignoredDiagnostics, other.ignoredDiagnostics); + } + private: // Make the state movable only DiagnosticState(const DiagnosticState &) = delete; @@ -706,6 +784,10 @@ namespace swift { /// The currently active diagnostic, if there is one. Optional ActiveDiagnostic; + /// Diagnostics wrapped by ActiveDiagnostic, if any. + SmallVector WrappedDiagnostics; + SmallVector, 4> WrappedDiagnosticArgs; + /// All diagnostics that have are no longer active but have not yet /// been emitted due to an open transaction. SmallVector TentativeDiagnostics; @@ -740,6 +822,10 @@ namespace swift { /// Path to diagnostic documentation directory. std::string diagnosticDocumentationPath = ""; + /// The Swift language version. This is used to limit diagnostic behavior + /// until a specific language version, e.g. Swift 6. + version::Version languageVersion; + /// Whether we are actively pretty-printing a declaration as part of /// diagnostics. bool IsPrettyPrintingDecl = false; @@ -802,29 +888,13 @@ namespace swift { bool isPrettyPrintingDecl() const { return IsPrettyPrintingDecl; } - void setLocalization(std::string locale, std::string path) { + void setLanguageVersion(version::Version v) { languageVersion = v; } + + void setLocalization(StringRef locale, StringRef path) { assert(!locale.empty()); assert(!path.empty()); - llvm::SmallString<128> filePath(path); - llvm::sys::path::append(filePath, locale); - llvm::sys::path::replace_extension(filePath, ".db"); - - // If the serialized diagnostics file not available, - // fallback to the `YAML` file. - if (llvm::sys::fs::exists(filePath)) { - if (auto file = llvm::MemoryBuffer::getFile(filePath)) { - localization = std::make_unique( - std::move(file.get()), getPrintDiagnosticNames()); - } - } else { - llvm::sys::path::replace_extension(filePath, ".yaml"); - // In case of missing localization files, we should fallback to messages - // from `.def` files. - if (llvm::sys::fs::exists(filePath)) { - localization = std::make_unique( - filePath.str(), getPrintDiagnosticNames()); - } - } + localization = diag::LocalizationProducer::producerFor( + locale, path, getPrintDiagnosticNames()); } void ignoreDiagnostic(DiagID id) { @@ -1017,6 +1087,12 @@ namespace swift { /// diagnostic. bool isAPIDigesterBreakageDiagnostic(DiagID id) const; + /// \returns true if the diagnostic is marking a deprecation. + bool isDeprecationDiagnostic(DiagID id) const; + + /// \returns true if the diagnostic is marking an unused element. + bool isNoUsageDiagnostic(DiagID id) const; + /// \returns true if any diagnostic consumer gave an error while invoking //// \c finishProcessing. bool finishProcessing(); @@ -1052,9 +1128,13 @@ namespace swift { void emitTentativeDiagnostics(); public: + DiagnosticKind declaredDiagnosticKindFor(const DiagID id); + llvm::StringRef diagnosticStringFor(const DiagID id, bool printDiagnosticNames); + static llvm::StringRef diagnosticIDStringFor(const DiagID id); + /// If there is no clear .dia file for a diagnostic, put it in the one /// corresponding to the SourceLoc given here. /// In particular, in batch mode when a diagnostic is located in @@ -1247,6 +1327,26 @@ namespace swift { builder(); } +/// Temporary on-stack storage and unescaping for encoded diagnostic +/// messages. +class EncodedDiagnosticMessage { + llvm::SmallString<128> Buf; + +public: + /// \param S A string with an encoded message + EncodedDiagnosticMessage(StringRef S); + + /// The unescaped message to display to the user. + const StringRef Message; +}; + +/// Returns a value that can be used to select between accessor kinds in +/// diagnostics. +/// +/// This is correlated with diag::availability_deprecated and others. +std::pair +getAccessorKindAndNameForDiagnostics(const ValueDecl *D); + } // end namespace swift #endif diff --git a/include/swift/AST/DiagnosticsAll.def b/include/swift/AST/DiagnosticsAll.def index 8392da03c5351..0dee5d2c934ff 100644 --- a/include/swift/AST/DiagnosticsAll.def +++ b/include/swift/AST/DiagnosticsAll.def @@ -20,6 +20,7 @@ #define DIAG_NO_UNDEF #include "DiagnosticsCommon.def" +#include "DiagnosticsIDE.def" #include "DiagnosticsParse.def" #include "DiagnosticsSema.def" #include "DiagnosticsClangImporter.def" diff --git a/include/swift/AST/DiagnosticsClangImporter.def b/include/swift/AST/DiagnosticsClangImporter.def index 5600be3e5d6db..55471a38df136 100644 --- a/include/swift/AST/DiagnosticsClangImporter.def +++ b/include/swift/AST/DiagnosticsClangImporter.def @@ -67,6 +67,13 @@ WARNING(inconsistent_swift_name,none, (bool, StringRef, StringRef, DeclName, StringRef, DeclName, StringRef)) +WARNING(swift_name_circular_context_import,none, + "cycle detected while resolving '%0' in swift_name attribute for '%1'", + (StringRef, StringRef)) +NOTE(swift_name_circular_context_import_other,none, + "while resolving '%0' in swift_name attribute for '%1'", + (StringRef, StringRef)) + WARNING(unresolvable_clang_decl,none, "imported declaration '%0' could not be mapped to '%1'", (StringRef, StringRef)) @@ -75,8 +82,8 @@ NOTE(unresolvable_clang_decl_is_a_framework_bug,none, "please report this issue to the owners of '%0'", (StringRef)) -WARNING(clang_swift_attr_without_at,none, - "Swift attribute '%0' does not start with '@'", (StringRef)) +WARNING(clang_swift_attr_unhandled,none, + "Ignoring unknown Swift attribute or modifier '%0'", (StringRef)) WARNING(implicit_bridging_header_imported_from_module,none, "implicit import of bridging header '%0' via module %1 " diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index d79dfbc137669..a53c81d159e25 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -44,6 +44,10 @@ NOTE(previous_decldef,none, NOTE(brace_stmt_suggest_do,none, "did you mean to use a 'do' statement?", ()) +WARNING(error_in_future_swift_version,none, + "%0; this is an error in Swift %1", + (DiagnosticInfo *, unsigned)) + // Generic disambiguation NOTE(while_parsing_as_left_angle_bracket,none, "while parsing this '<' as a type parameter bracket", ()) @@ -192,6 +196,15 @@ ERROR(scanner_find_cycle, none, ERROR(scanner_arguments_invalid, none, "dependencies scanner cannot be configured with arguments: '%0'", (StringRef)) +WARNING(warn_scaner_deserialize_failed, none, + "Failed to load module scanning dependency cache from: '%0', re-building scanner cache from scratch.", (StringRef)) + +REMARK(remark_reuse_cache, none, + "Re-using serialized module scanning dependency cache from: '%0'.", (StringRef)) + +REMARK(remark_save_cache, none, + "Serializing module scanning dependency cache to: '%0'.", (StringRef)) + //------------------------------------------------------------------------------ // MARK: custom attribute diagnostics //------------------------------------------------------------------------------ diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index cb7961f1e2292..b6db1acd78d8d 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -119,9 +119,6 @@ ERROR(error_input_changed_during_build,none, ERROR(error_conflicting_options, none, "conflicting options '%0' and '%1'", (StringRef, StringRef)) -ERROR(error_option_not_supported, none, - "'%0' is not supported with '%1'", - (StringRef, StringRef)) ERROR(error_requirement_not_met, none, "'%0' requires '%1'", (StringRef, StringRef)) @@ -173,5 +170,10 @@ WARNING(warn_drv_darwin_sdk_invalid_settings, none, "SDK settings were ignored because 'SDKSettings.json' could not be parsed", ()) +WARNING(warning_unsupported_driver_option,none, + "option '%0' is ony supported in swift-driver", (StringRef)) + +WARNING(old_driver_deprecated,none, + "legacy driver is now deprecated; consider avoiding specifying `%0`", (StringRef)) #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 11c20cb8e329d..1e6be6b85717b 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -129,6 +129,8 @@ ERROR(error_mode_cannot_emit_module_summary,none, "this mode does not support emitting module summary files", ()) ERROR(error_mode_cannot_emit_symbol_graph,none, "this mode does not support emitting symbol graph files", ()) +ERROR(error_mode_cannot_emit_abi_descriptor,none, + "this mode does not support emitting ABI descriptor", ()) ERROR(cannot_emit_ir_skipping_function_bodies,none, "the -experimental-skip-*-function-bodies* flags do not support " "emitting IR", ()) @@ -251,11 +253,6 @@ ERROR(tbd_not_supported_with_cmo,none, "Test-Based InstallAPI (TBD) is not support with cross-module-optimization", ()) -WARNING(linker_directives_choice_confusion,none, - "only one of -emit-ldadd-cfile-path and -module-installname-map-file can be specified;" - "the c file won't be generated", - ()) - ERROR(previous_installname_map_missing,none, "cannot open previous install name map from %0", (StringRef)) @@ -288,12 +285,6 @@ ERROR(batch_scan_input_file_corrupted,none, "batch dependencies scan input file from %0 is malformed", (StringRef)) -REMARK(default_previous_install_name, none, - "default previous install name for %0 is %1", (StringRef, StringRef)) - -REMARK(platform_previous_install_name, none, - "previous install name for %0 in %1 is %2", (StringRef, StringRef, StringRef)) - ERROR(unknown_platform_name, none, "unkown platform name %0", (StringRef)) @@ -371,6 +362,11 @@ WARNING(warning_module_shadowing_may_break_module_interface,none, /*shadowedModule=*/ModuleDecl *, /*interfaceModule*/ModuleDecl *)) REMARK(rebuilding_module_from_interface,none, "rebuilding module '%0' from interface '%1'", (StringRef, StringRef)) +REMARK(rebuilding_stdlib_from_interface,none, + "did not find a prebuilt standard library for target '%0' compatible " + "with this Swift compiler; building it may take a few minutes, but it " + "should only happen once for this combination of compiler and target", + (StringRef)) NOTE(sdk_version_pbm_version,none, "SDK build version is '%0'; prebuilt modules were " "built using SDK build version: '%1'", (StringRef, StringRef)) @@ -405,6 +401,8 @@ ERROR(error_option_required,none, "option '%0' is required", (StringRef)) ERROR(error_nonexistent_output_dir,none, "'-output-dir' argument '%0' does not exist or is not a directory", (StringRef)) +REMARK(interface_file_backup_used,none, + "building module from '%0' failed; retrying building module from '%1'", (StringRef, StringRef)) // Dependency Verifier Diagnostics ERROR(missing_member_dependency,none, diff --git a/include/swift/AST/DiagnosticsIDE.def b/include/swift/AST/DiagnosticsIDE.def new file mode 100644 index 0000000000000..11993176e976a --- /dev/null +++ b/include/swift/AST/DiagnosticsIDE.def @@ -0,0 +1,52 @@ +//===--- DiagnosticsIDE.def - Diagnostics Text ------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#define DEFINE_DIAGNOSTIC_MACROS +#include "DefineDiagnosticMacros.h" + +//===----------------------------------------------------------------------===// + +ERROR(ide_async_in_nonasync_context, none, + "async %0 used in a context that does not support concurrency", + (DeclName)) + +// NOTE: This is WARNING because this is emitted for cross actor references with +// non-'Sendable' types. That is optionally ('-warn-concurrency') warning in +// Swift 5.5. +WARNING(ide_cross_actor_reference_swift5, none, + "actor-isolated %0 should only be referenced from inside the actor", + (DeclName)) + +WARNING(ide_redundant_import, none, + "module %0 is already imported", (DeclName)) + +// FIXME: Inform which other 'import' this module came from. +NOTE(ide_redundant_import_indirect, none, + "module %0 is already imported via another module import", (DeclName)) + +WARNING(ide_availability_softdeprecated, Deprecation, + "%select{getter for |setter for |}0%1 will be deprecated" + " in %select{a future version|%select{a future version of %3|%3 %5}4}2" + "%select{|: %6}6", + (unsigned, DeclName, bool, StringRef, bool, llvm::VersionTuple, + StringRef)) + +WARNING(ide_availability_softdeprecated_rename, Deprecation, + "%select{getter for |setter for |}0%1 will be deprecated" + " in %select{a future version|%select{a future version of %3|%3 %5}4}2" + ": renamed to '%6'", + (unsigned, DeclName, bool, StringRef, bool, llvm::VersionTuple, StringRef)) + +//===----------------------------------------------------------------------===// + +#define UNDEFINE_DIAGNOSTIC_MACROS +#include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsIDE.h b/include/swift/AST/DiagnosticsIDE.h new file mode 100644 index 0000000000000..293e7aac16864 --- /dev/null +++ b/include/swift/AST/DiagnosticsIDE.h @@ -0,0 +1,32 @@ +//===--- DiagnosticsIDE.h - Diagnostic Definitions --------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file defines diagnostics used only in IDE. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DIAGNOSTICSIDE_H +#define SWIFT_DIAGNOSTICSIDE_H + +#include "swift/AST/DiagnosticsCommon.h" + +namespace swift { + namespace diag { + // Declare common diagnostics objects with their appropriate types. +#define DIAG(KIND,ID,Options,Text,Signature) \ + extern detail::DiagWithArguments::type ID; +#include "DiagnosticsIDE.def" + } +} + +#endif diff --git a/include/swift/AST/DiagnosticsModuleDiffer.def b/include/swift/AST/DiagnosticsModuleDiffer.def index 443117a8d6afa..b8d7276a150d5 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.def +++ b/include/swift/AST/DiagnosticsModuleDiffer.def @@ -88,6 +88,8 @@ ERROR(not_inheriting_convenience_inits,APIDigesterBreakage,"%0 no longer inherit ERROR(enum_case_added,APIDigesterBreakage,"%0 has been added as a new enum case", (StringRef)) +ERROR(demangled_name_changed,APIDigesterBreakage,"%0 has mangled name changing from '%1' to '%2'", (StringRef, StringRef, StringRef)) + WARNING(cannot_read_allowlist,none,"cannot read breakage allowlist at '%0'", (StringRef)) #define UNDEFINE_DIAGNOSTIC_MACROS diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 61299a1333555..ff2766a2b4503 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -435,11 +435,12 @@ ERROR(deprecated_operator_body_use_group,PointsToFirstBadToken, "use a precedence group instead", ()) ERROR(operator_decl_no_fixity,none, "operator must be declared as 'prefix', 'postfix', or 'infix'", ()) +ERROR(operator_decl_expected_precedencegroup, none, + "expected precedence group name after ':' in operator declaration", ()) -ERROR(operator_decl_expected_type,none, - "expected designated type in operator declaration", ()) -ERROR(operator_decl_trailing_comma,none, - "trailing comma in operator declaration", ()) +WARNING(operator_decl_remove_designated_types,none, + "designated types are no longer used by the compiler; please remove " + "the designated type list from this operator declaration", ()) // PrecedenceGroup ERROR(precedencegroup_not_infix,none, @@ -487,6 +488,7 @@ ERROR(referenced_value_no_accessor,none, "referenced declaration has no %select{getter|setter}0", (unsigned)) ERROR(expected_sil_value_ownership_kind,none, "expected value ownership kind in SIL code", ()) +ERROR(unrecognized_sil_qualifier,none, "unrecognized SIL qualifier", ()) ERROR(silfunc_and_silarg_have_incompatible_sil_value_ownership,none, "SILFunction and SILArgument have mismatching ValueOwnershipKinds. " "Function type specifies: '@%0'. SIL argument specifies: '@%1'.", @@ -589,8 +591,6 @@ ERROR(sil_missing_substitutions,none, "missing substitutions", ()) ERROR(sil_too_many_substitutions,none, "too many substitutions", ()) -ERROR(sil_dbg_unknown_key,none, - "unknown key '%0' in debug variable declaration", (StringRef)) ERROR(sil_objc_with_tail_elements,none, "alloc_ref [objc] cannot have tail allocated elements", ()) ERROR(sil_expected_access_kind,none, @@ -598,6 +598,13 @@ ERROR(sil_expected_access_kind,none, ERROR(sil_expected_access_enforcement,none, "%0 instruction must have explicit access enforcement", (StringRef)) +ERROR(sil_dbg_unknown_key,none, + "unknown key '%0' in debug variable declaration", (StringRef)) +ERROR(sil_dbg_unknown_expr_part,none, + "unrecognized debug info expression %0", (StringRef)) +ERROR(sil_dbg_expr_expect_operand_kind,none, + "operator %0 expects a %1 kind operand here", (StringRef, StringRef)) + ERROR(sil_keypath_expected_component_kind,none, "expected keypath component kind", ()) ERROR(sil_keypath_unknown_component_kind,none, @@ -931,6 +938,12 @@ WARNING(parameter_unnamed_warn,none, ERROR(parameter_curry_syntax_removed,none, "cannot have more than one parameter list", ()) +ERROR(availability_cannot_be_mixed,none, + "#available and #unavailable cannot be in the same statement", ()) + +ERROR(false_available_is_called_unavailable,none, + "#available cannot be used as an expression, did you mean to use '#unavailable'?", ()) + ERROR(initializer_as_typed_pattern,none, "unexpected initializer in pattern; did you mean to use '='?", ()) @@ -1133,7 +1146,7 @@ ERROR(default_with_where,none, "'default' cannot be used with a 'where' guard expression", ()) ERROR(case_stmt_without_body,none, - "%select{'case'|'default'}0 label in a 'switch' should have at least one " + "%select{'case'|'default'}0 label in a 'switch' must have at least one " "executable statement", (bool)) // 'try' on statements @@ -1223,8 +1236,8 @@ ERROR(invalid_float_literal_missing_leading_zero,none, "'.%0' is not a valid floating point literal; it must be written '0.%0'", (StringRef)) ERROR(availability_query_outside_if_stmt_guard, none, - "#available may only be used as condition of an 'if', 'guard'" - " or 'while' statement", ()) + "%0 may only be used as condition of an 'if', 'guard'" + " or 'while' statement", (StringRef)) ERROR(empty_arg_label_underscore, none, "an empty argument label is spelled with '_'", ()) @@ -1428,6 +1441,8 @@ ERROR(attr_renamed, none, "'@%0' has been renamed to '@%1'", (StringRef, StringRef)) WARNING(attr_renamed_warning, none, "'@%0' has been renamed to '@%1'", (StringRef, StringRef)) +WARNING(attr_renamed_to_modifier_warning, none, + "'@%0' has been renamed to '%1'", (StringRef, StringRef)) ERROR(attr_name_close_match, none, "no attribute named '@%0'; did you mean '@%1'?", (StringRef, StringRef)) ERROR(attr_unsupported_on_target, none, @@ -1690,11 +1705,6 @@ ERROR(sil_inst_autodiff_invalid_witness_generic_signature,PointsToFirstBadToken, "parameters as original function generic signature '%1'", (StringRef, StringRef)) -// completionHandlerAsync -ERROR(attr_completion_handler_async_invalid_name, none, - "argument of '%0' attribute must be an identifier or full function name", - (StringRef)) - //------------------------------------------------------------------------------ // MARK: Generics parsing diagnostics //------------------------------------------------------------------------------ @@ -1753,7 +1763,7 @@ ERROR(version_component_not_number,none, "version component contains non-numeric characters", ()) ERROR(compiler_version_too_many_components,none, "compiler version must not have more than five components", ()) -WARNING(unused_compiler_version_component,none, +WARNING(unused_compiler_version_component,NoUsage, "the second version component is not used for comparison", ()) ERROR(empty_version_component,none, "found empty version component", ()) @@ -1810,23 +1820,31 @@ ERROR(avail_query_version_comparison_not_needed, ERROR(availability_query_wildcard_required, none, "must handle potential future platforms with '*'", ()) +ERROR(unavailability_query_wildcard_not_required, none, + "platform wildcard '*' is always implicit in #unavailable", ()) + ERROR(availability_must_occur_alone, none, "'%0' version-availability must be specified alone", (StringRef)) ERROR(pound_available_swift_not_allowed, none, - "Swift language version checks not allowed in #available(...)", ()) + "Swift language version checks not allowed in %0(...)", (StringRef)) ERROR(pound_available_package_description_not_allowed, none, - "PackageDescription version checks not allowed in #available(...)", ()) + "PackageDescription version checks not allowed in %0(...)", (StringRef)) ERROR(availability_query_repeated_platform, none, "version for '%0' already specified", (StringRef)) -ERROR(attr_requires_concurrency,none, +ERROR(attr_requires_concurrency, none, "'%0' %select{attribute|modifier}1 is only valid when experimental " "concurrency is enabled", (StringRef, bool)) +ERROR(attr_requires_distributed, none, + "'%0' %select{attribute|modifier}1 is only valid when experimental " + "distributed support is enabled", + (StringRef, bool)) + //------------------------------------------------------------------------------ // MARK: syntax parsing diagnostics //------------------------------------------------------------------------------ diff --git a/include/swift/AST/DiagnosticsRefactoring.def b/include/swift/AST/DiagnosticsRefactoring.def index e6075087a96d7..2010499224f7b 100644 --- a/include/swift/AST/DiagnosticsRefactoring.def +++ b/include/swift/AST/DiagnosticsRefactoring.def @@ -62,8 +62,6 @@ ERROR(unknown_callback_conditions, none, "cannot refactor complex if conditions" ERROR(mixed_callback_conditions, none, "cannot refactor mixed nil and not-nil conditions", ()) -ERROR(callback_multiple_bound_names, none, "cannot refactor when multiple names bound to single declaration, had '%0' and found '%1'", (StringRef, StringRef)) - ERROR(callback_with_fallthrough, none, "cannot refactor switch with fallthrough", ()) ERROR(callback_with_default, none, "cannot refactor switch with default case", ()) @@ -72,5 +70,7 @@ ERROR(callback_multiple_case_items, none, "cannot refactor switch using a case w ERROR(callback_where_case_item, none, "cannot refactor switch using a case with where clause", ()) +ERROR(unknown_callback_case_item, none, "cannot refactor complex case conditions", ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index d18655ba35225..d7e1dfb1ac57e 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -168,6 +168,22 @@ ERROR(variable_defer_use_uninit,none, ERROR(self_closure_use_uninit,none, "'self' captured by a closure before all members were initialized", ()) +/// false == sync; true == global-actor isolated +ERROR(self_use_actor_init,none, + "this use of actor 'self' %select{can only|cannot}0 appear in " + "%select{an async|a global-actor isolated}0 initializer", + (bool)) +ERROR(self_disallowed_actor_init,none, + "actor 'self' %select{can only|cannot}0 %1 from " + "%select{an async|a global-actor isolated}0 initializer", + (bool, StringRef)) +NOTE(actor_convenience_init,none, + "convenience initializers allow non-isolated use of 'self' once " + "initialized", + ()) + + + ERROR(variable_addrtaken_before_initialized,none, "address of %select{variable|constant}1 '%0' taken before it is" @@ -429,7 +445,7 @@ NOTE(constexpr_witness_call_with_no_conformance, none, "%select{for this call|for a witness-method invoked during this call}0", (bool)) -NOTE(constexpr_unknown_control_flow_due_to_skip,none, "branch depends on " +REMARK(constexpr_unknown_control_flow_due_to_skip,none, "branch depends on " "non-constant value produced by an unevaluated instructions", ()) NOTE(constexpr_returned_by_unevaluated_instruction,none, "result of an unevaluated instruction is not a constant", ()) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 20b61f6ca3d2e..3c7c764f0eff8 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -30,6 +30,8 @@ NOTE(decl_declared_here,none, "%0 declared here", (DeclName)) NOTE(kind_declared_here,none, "%0 declared here", (DescriptiveDeclKind)) +NOTE(descriptive_decl_declared_here,none, + "'%0' declared here", (StringRef)) NOTE(implicit_member_declared_here,none, "%1 '%0' is implicitly declared", (StringRef, StringRef)) NOTE(extended_type_declared_here,none, @@ -273,12 +275,15 @@ ERROR(cannot_infer_closure_parameter_type,none, ERROR(cannot_infer_closure_type,none, "unable to infer closure type in the current context", ()) ERROR(cannot_infer_closure_result_type,none, - "unable to infer%select{ complex|}0 closure return type; " - "add explicit type to disambiguate", (bool)) + "cannot infer return type for closure with multiple statements; " + "add explicit type to disambiguate", ()) FIXIT(insert_closure_return_type_placeholder, "%select{| () }0-> <#Result#> %select{|in }0", (bool)) +NOTE(use_of_anon_closure_param,none, + "anonymous closure parameter %0 is used here", (Identifier)) + ERROR(incorrect_explicit_closure_result,none, "declared closure result %0 is incompatible with contextual type %1", (Type, Type)) @@ -358,8 +363,8 @@ ERROR(cannot_convert_argument_value,none, (Type,Type)) NOTE(candidate_has_invalid_argument_at_position,none, - "candidate expects %select{|in-out }2value of type %0 for parameter #%1", - (Type, unsigned, bool)) + "candidate expects %select{|in-out }2value of type %0 for parameter #%1 (got %3)", + (Type, unsigned, bool, Type)) ERROR(cannot_convert_array_to_variadic,none, "cannot pass array of type %0 as variadic arguments of type %1", @@ -377,6 +382,12 @@ ERROR(cannot_convert_argument_value_generic,none, ERROR(conflicting_arguments_for_generic_parameter,none, "conflicting arguments to generic parameter %0 (%1)", (Type, StringRef)) +NOTE(generic_parameter_inferred_from_closure,none, + "generic parameter %0 inferred as %1 from closure return expression", + (Type, Type)) +NOTE(generic_parameter_inferred_from_result_context,none, + "generic parameter %0 inferred as %1 from context", + (Type, Type)) // @_nonEphemeral conversion diagnostics ERROR(cannot_pass_type_to_non_ephemeral,none, @@ -572,12 +583,12 @@ NOTE(async_inferred_from_operation,none, // Key-path expressions. ERROR(expr_keypath_no_objc_runtime,none, "'#keyPath' can only be used with the Objective-C runtime", ()) -ERROR(expression_unused_keypath_result,none, +ERROR(expression_unused_keypath_result,NoUsage, "result of key path is unused", ()) ERROR(expr_keypath_non_objc_property,none, "argument of '#keyPath' refers to non-'@objc' property %0", (Identifier)) -WARNING(expr_keypath_swift3_objc_inference,none, +WARNING(expr_keypath_swift3_objc_inference,Deprecation, "argument of '#keyPath' refers to property %0 in %1 that depends on " "'@objc' inference deprecated in Swift 4", (Identifier, Identifier)) @@ -637,7 +648,7 @@ ERROR(expr_swift_keypath_anyobject_root,none, "the root type of a Swift key path cannot be 'AnyObject'", ()) ERROR(expr_keypath_multiparam_func_conversion, none, "cannot convert key path into a multi-argument function type %0", (Type)) -WARNING(expr_deprecated_writable_keypath,none, +WARNING(expr_deprecated_writable_keypath,Deprecation, "forming a writable keypath to property %0 that is read-only in this context " "is deprecated and will be removed in a future release",(DeclName)) @@ -680,7 +691,7 @@ ERROR(expr_selector_not_objc,none, NOTE(make_decl_objc,none, "add '@objc' to expose this %0 to Objective-C", (DescriptiveDeclKind)) -WARNING(expr_selector_swift3_objc_inference,none, +WARNING(expr_selector_swift3_objc_inference,Deprecation, "argument of '#selector' refers to %0 %1 in %2 that depends on " "'@objc' inference deprecated in Swift 4", (DescriptiveDeclKind, DeclName, Identifier)) @@ -690,10 +701,10 @@ WARNING(selector_literal_invalid,none, "string literal is not a valid Objective-C selector", ()) WARNING(selector_literal_undeclared,none, "no method declared with Objective-C selector %0", (ObjCSelector)) -WARNING(selector_literal_deprecated,none, +WARNING(selector_literal_deprecated,Deprecation, "use of string literal for Objective-C selectors is deprecated; " "use '#selector' or explicitly construct a 'Selector'", ()) -WARNING(selector_literal_deprecated_suggest,none, +WARNING(selector_literal_deprecated_suggest,Deprecation, "use of string literal for Objective-C selectors is deprecated; " "use '#selector' instead", ()) WARNING(selector_construction_suggest,none, @@ -789,13 +800,6 @@ ERROR(serialization_fatal,Fatal, "fatal error encountered while reading from module '%0'; " SWIFT_BUG_REPORT_MESSAGE, (StringRef)) -NOTE(serialization_misc_version,none, - "module '%0' full misc version is '%1'", - (StringRef, StringRef)) -NOTE(serialization_compatibility_version_mismatch,none, - "compiling as Swift %0, with '%1' built as Swift %2 " - "(this is supported but may expose additional compiler issues)", - (StringRef, StringRef, StringRef)) ERROR(serialization_invalid_decl,Fatal, "deserialized invalid declaration %0 (%1) in module '%2'", @@ -810,7 +814,9 @@ ERROR(serialization_allowing_error_type,none, "allowing deserialization of error type '%0' in module '%1'", (StringRef, StringRef)) WARNING(serialization_malformed_sourceinfo,none, - "unable to use malformed module source info '%0'", (StringRef)) + "'%0' is either malformed or generated by a different Swift version. " + "Note that it uses an unstable format and may leak internal project " + "details, it should not be distributed alongside modules", (StringRef)) ERROR(reserved_member_name,none, "type member must not be named %0, since it would conflict with the" @@ -828,7 +834,7 @@ ERROR(invalid_redecl_implicit,none, "invalid redeclaration of synthesized " "%select{%0|implementation for protocol requirement}1 %2", (DescriptiveDeclKind, bool, DeclName)) -WARNING(invalid_redecl_swift5_warning,none, +WARNING(invalid_redecl_swift5_warning,Deprecation, "redeclaration of %0 is deprecated and will be an error in Swift 5", (DeclName)) @@ -914,9 +920,6 @@ NOTE(object_literal_resolve_import,none, ERROR(use_local_before_declaration,none, "use of local variable %0 before its declaration", (DeclNameRef)) -ERROR(unsupported_existential_type,none, - "protocol %0 can only be used as a generic constraint because it has " - "Self or associated type requirements", (Identifier)) ERROR(decl_does_not_exist_in_module,none, "%select{%error|type|struct|class|enum|protocol|variable|function}0 " @@ -1263,7 +1266,8 @@ ERROR(extra_argument_named,none, ERROR(extra_argument_positional,none, "extra argument in call", ()) ERROR(extra_arguments_in_call,none, - "extra arguments at positions %0 in call", (StringRef)) + "extra %select{arguments|trailing closures}0 at positions %1 in call", + (bool, StringRef)) ERROR(extra_argument_to_nullary_call,none, "argument passed to call that takes no arguments", ()) ERROR(extra_trailing_closure_in_call,none, @@ -1271,7 +1275,7 @@ ERROR(extra_trailing_closure_in_call,none, ERROR(trailing_closure_bad_param,none, "trailing closure passed to parameter of type %0 that does not " "accept a closure", (Type)) -WARNING(unlabeled_trailing_closure_deprecated,none, +WARNING(unlabeled_trailing_closure_deprecated,Deprecation, "backward matching of the unlabeled trailing closure is deprecated; label the argument with %0 to suppress this warning", (Identifier)) NOTE(decl_multiple_defaulted_closure_parameters,none, @@ -1533,7 +1537,7 @@ NOTE(requires_stored_property_inits_here,none, "%select{superclass|class}1 %0 requires all stored properties to have " "initial values%select{| or use @NSManaged}2", (Type, bool, bool)) ERROR(class_without_init,none, - "class %0 has no initializers", (Type)) + "%select{class|actor}0 %1 has no initializers", (bool, Type)) NOTE(note_no_in_class_init_1,none, "stored property %0 without initial value prevents synthesized " "initializers", @@ -1551,7 +1555,7 @@ ERROR(missing_unimplemented_init_runtime,none, ERROR(missing_undefined_runtime,none, "standard library error: missing _undefined", ()) -WARNING(expr_dynamic_lookup_swift3_objc_inference,none, +WARNING(expr_dynamic_lookup_swift3_objc_inference,Deprecation, "reference to %0 %1 of %2 depends on '@objc' inference " "deprecated in Swift 4", (DescriptiveDeclKind, DeclName, Identifier)) @@ -1854,9 +1858,17 @@ WARNING(spi_attribute_on_import_of_public_module,none, (DeclName, StringRef)) // Opaque return types +ERROR(structural_opaque_types_are_experimental,none, + "'opaque' types cannot be nested inside other types; " + "structural 'opaque' types are an experimental feature", ()) ERROR(opaque_type_invalid_constraint,none, "an 'opaque' type must specify only 'Any', 'AnyObject', protocols, " "and/or a base class", ()) +NOTE(opaque_of_optional_rewrite,none, + "did you mean to write an optional of an 'opaque' type?", ()) +ERROR(more_than_one_opaque_type,none, + "%0 contains multiple 'opaque' types, but only one 'opaque' type is " + "supported", (TypeRepr*)) ERROR(inferred_opaque_type,none, "property definition has inferred type %0, involving the 'some' " "return type of another declaration", (Type)) @@ -1919,6 +1931,18 @@ ERROR(type_cannot_conform, none, NOTE(only_concrete_types_conform_to_protocols,none, "only concrete types such as structs, enums and classes can conform to protocols", ()) +NOTE(nonsendable_function_type,none, + "a function type must be marked '@Sendable' to conform to 'Sendable'", ()) +NOTE(nonsendable_tuple_type,none, + "a tuple type must be composed of 'Sendable' elements to conform to " + "'Sendable'", ()) +NOTE(non_sendable_nominal,none, + "%0 %1 does not conform to the `Sendable` protocol", + (DescriptiveDeclKind, DeclName)) +NOTE(add_nominal_sendable_conformance,none, + "consider making %0 %1 conform to the 'Sendable' protocol", + (DescriptiveDeclKind, DeclName)) + NOTE(required_by_opaque_return,none, "required by opaque return type of %0 %1", (DescriptiveDeclKind, DeclName)) NOTE(required_by_decl,none, @@ -1986,16 +2010,16 @@ NOTE(wrapped_type_satisfies_requirement,none, "wrapped type %0 satisfies this requirement; did you mean to unwrap?", (Type)) NOTE(candidate_types_conformance_requirement,none, "candidate requires that %0 conform to %1 " - "(requirement specified as %2 == %3%4)", - (Type, Type, Type, Type, StringRef)) + "(requirement specified as %2 : %3)", + (Type, Type, Type, Type)) NOTE(candidate_types_equal_requirement,none, "candidate requires that the types %0 and %1 be equivalent " - "(requirement specified as %2 == %3%4)", - (Type, Type, Type, Type, StringRef)) + "(requirement specified as %2 == %3)", + (Type, Type, Type, Type)) NOTE(candidate_types_inheritance_requirement,none, "candidate requires that %1 inherit from %2 " - "(requirement specified as %2 : %3%4)", - (Type, Type, Type, Type, StringRef)) + "(requirement specified as %2 : %3)", + (Type, Type, Type, Type)) NOTE(types_not_equal_requirement,none, "requirement specified as %0 == %1%2", (Type, Type, StringRef)) ERROR(type_is_not_a_class,none, @@ -2307,8 +2331,8 @@ NOTE(declared_protocol_conformance_here,none, (Type, unsigned, Identifier, Identifier)) ERROR(witness_unavailable,none, - "unavailable %0 %1 was used to satisfy a requirement of protocol %2", - (DescriptiveDeclKind, DeclName, Identifier)) + "unavailable %0 %1 was used to satisfy a requirement of protocol %2%select{|: %3}3", + (DescriptiveDeclKind, DeclName, Identifier, StringRef)) ERROR(redundant_conformance,none, "redundant conformance of %0 to protocol %1", (Type, Identifier)) @@ -2421,8 +2445,9 @@ NOTE(redundant_conformance_here,none, "conformance constraint %0 : %1 implied here", (Type, ProtocolDecl *)) -ERROR(unsupported_recursive_requirements, none, - "requirement involves recursion that is not currently supported", ()) +WARNING(missing_protocol_refinement, none, + "protocol %0 should be declared to refine %1 due to a same-type constraint on 'Self'", + (ProtocolDecl *, ProtocolDecl *)) ERROR(same_type_conflict,none, "%select{generic parameter |protocol |}0%1 cannot be equal to both " @@ -2676,14 +2701,10 @@ WARNING(implicitly_final_cannot_be_open_swift4,none, "static declarations}0 are implicitly 'final'; use 'public' instead of " "'open'", (unsigned)) -WARNING(override_swift3_objc_inference,none, +WARNING(override_swift3_objc_inference,Deprecation, "override of %0 %1 from extension of %2 depends on deprecated " "inference of '@objc'", (DescriptiveDeclKind, DeclName, Identifier)) -ERROR(override_method_different_generic_sig,none, - "overridden method %0 has generic signature %1 which is incompatible with " - "base method's generic signature %2; expected generic signature to be %3", - (DeclBaseName, StringRef, StringRef, StringRef)) // Inheritance ERROR(duplicate_inheritance,none, @@ -2692,7 +2713,7 @@ WARNING(duplicate_anyobject_class_inheritance,none, "redundant inheritance from 'AnyObject' and Swift 3 'class' keyword", ()) ERROR(inheritance_from_protocol_with_superclass,none, "inheritance from class-constrained protocol composition type %0", (Type)) -WARNING(anyobject_class_inheritance_deprecated,none, +WARNING(anyobject_class_inheritance_deprecated,Deprecation, "using 'class' keyword to define a class-constrained protocol is deprecated; " "use 'AnyObject' instead", ()) ERROR(multiple_inheritance,none, @@ -2972,7 +2993,8 @@ NOTE(codable_enum_duplicate_case_name_here,none, NOTE(codable_enum_duplicate_parameter_name_here,none, "cannot automatically synthesize %0 for %1 because " "user defined parameter name %2 in %3 conflicts with " - "automatically generated parameter name", (Type, Type, Identifier, Identifier)) + "automatically generated parameter name", + (Type, Type, Identifier, Identifier)) WARNING(decodable_property_will_not_be_decoded, none, "immutable property will not be decoded because it is declared with " @@ -3381,32 +3403,6 @@ ERROR(diff_params_clause_param_not_differentiable,none, "'Differentiable', but %0 does not conform to 'Differentiable'", (Type)) // completionHanderAsync attribute -ERROR(attr_completion_handler_async_handler_not_func,none, - "'%0' should be attached to a non-async completion-handler function", - (DeclAttribute)) - -NOTE(note_attr_function_declared_async,none, - "function declared async", ()) - -NOTE(note_attr_completion_function_must_return_void,none, - "completion handler must return 'Void'", ()) - -NOTE(note_attr_completion_handler_async_type_is_not_function,none, - "%0 is not a function type", (Type)) - -NOTE(note_attr_completion_handler_async_handler_attr_req,none, - "completion handler must%select{ not|}0 be '@%1'", - (bool, StringRef)) - -ERROR(attr_completion_handler_async_handler_out_of_range,none, - "completion handler index out of range of the function parameters", ()) - -ERROR(attr_completion_handler_async_ambiguous_function,none, - "ambiguous '%0' async function %1", (DeclAttribute, DeclNameRef)) - -ERROR(attr_completion_handler_async_no_suitable_function,none, - "no corresponding async function named %0", (DeclNameRef)) - WARNING(warn_use_async_alternative,none, "consider using asynchronous alternative function",()) @@ -3593,6 +3589,11 @@ ERROR(unresolved_nil_literal,none, ERROR(cannot_force_unwrap_nil_literal,none, "'nil' literal cannot be force unwrapped", ()) +ERROR(could_not_infer_placeholder,none, + "could not infer type for placeholder", ()) +ERROR(top_level_placeholder_type,none, + "placeholders are not allowed as top-level types", ()) + ERROR(type_of_expression_is_ambiguous,none, "type of expression is ambiguous without more context", ()) @@ -3656,6 +3657,10 @@ ERROR(not_a_generic_definition,none, "cannot specialize a non-generic definition", ()) ERROR(not_a_generic_type,none, "cannot specialize non-generic type %0", (Type)) +ERROR(cannot_specialize_self,none, + "cannot specialize 'Self'", ()) +NOTE(specialize_explicit_type_instead,none, + "did you mean to explicitly reference %0 instead?", (Type)) ERROR(type_parameter_count_mismatch,none, "generic type %0 specialized with %select{too many|too few}3 type " "parameters (got %2, but expected %1)", @@ -3666,7 +3671,7 @@ NOTE(descriptive_generic_type_declared_here,none, "%0 declared here", (StringRef)) ERROR(placeholder_type_not_allowed,none, - "you cannot use a placeholder type here", ()) + "type placeholder not allowed here", ()) WARNING(use_of_void_pointer,none, "Unsafe%0Pointer has been replaced by Unsafe%0RawPointer", (StringRef)) @@ -3701,6 +3706,10 @@ WARNING(partial_application_of_function_invalid_swift4,none, "be an error in future Swift versions", (unsigned)) +// Cannot capture `async let` +ERROR(capture_async_let_not_supported,none, + "capturing 'async let' variables is not supported", ()) + ERROR(self_assignment_var,none, "assigning a variable to itself", ()) ERROR(self_assignment_prop,none, @@ -3756,7 +3765,7 @@ NOTE(fix_unqualified_access_top_level_multi,none, "use '%0' to reference the %1 in module %2", (StringRef, DescriptiveDeclKind, Identifier)) -WARNING(warn_deprecated_conditional_conformance_outer_access,none, +WARNING(warn_deprecated_conditional_conformance_outer_access,Deprecation, "use of %0 as reference to %1 in %2 %3 will change in future versions of Swift to reference %4 in %5 %6 " "which comes via a conditional conformance", (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName, @@ -3882,6 +3891,12 @@ ERROR(converting_noattrfunc_to_type,none, "converting %select{non-escaping|non-concurrent function}0 value to %1 " "may %select{allow it to escape|introduce data races}0", (unsigned, Type)) +NOTE(escape_expected_at_parameter_position,none, + "parameter #%0 expects escaping value of type %1", + (unsigned, Type)) +NOTE(add_explicit_escaping,none, + "add explicit @escaping to function parameter #%0", + (unsigned)) ERROR(converting_func_loses_global_actor,none, "converting function value of type %0 to %1 loses global actor %2", @@ -3932,32 +3947,32 @@ WARNING(guard_always_succeeds,none, "'guard' condition is always true, body is unreachable", ()) -ERROR(expression_unused_closure,none, +ERROR(expression_unused_closure,NoUsage, "closure expression is unused", ()) -ERROR(expression_unused_function,none, +ERROR(expression_unused_function,NoUsage, "function is unused", ()) -WARNING(expression_unused_lvalue,none, +WARNING(expression_unused_lvalue,NoUsage, "%select{" "variable|" "property is accessed but result|" "subscript is accessed but result" "}0 is unused", (unsigned)) -WARNING(expression_unused_result_call,none, +WARNING(expression_unused_result_call,NoUsage, "result of call to %0 is unused", (DeclName)) -WARNING(expression_unused_result_operator,none, +WARNING(expression_unused_result_operator,NoUsage, "result of operator %0 is unused", (DeclName)) -WARNING(expression_unused_result_unknown, none, +WARNING(expression_unused_result_unknown, NoUsage, "result of call to %select{function|closure}0 returning %1 is unused", (bool, Type)) -WARNING(expression_unused_result, none, +WARNING(expression_unused_result, NoUsage, "expression of type %0 is unused", (Type)) -WARNING(expression_unused_init_result,none, +WARNING(expression_unused_init_result,NoUsage, "result of %0 initializer is unused", (Type)) -WARNING(expression_unused_optional_try,none, +WARNING(expression_unused_optional_try,NoUsage, "result of 'try?' is unused", ()) -WARNING(expression_unused_selector_result, none, +WARNING(expression_unused_selector_result, NoUsage, "result of '#selector' is unused", ()) -WARNING(expression_unused_literal,none, +WARNING(expression_unused_literal,NoUsage, "%0 literal is unused", (StringRef)) ERROR(assignment_lhs_not_lvalue,none, @@ -4059,9 +4074,6 @@ WARNING(trailing_closure_requires_parens,none, " statement; pass as a parenthesized argument to silence this warning", ()) -ERROR(opaque_type_var_no_init,none, - "property declares an opaque return type, but has no initializer " - "expression from which to infer an underlying type", ()) ERROR(opaque_type_no_underlying_type_candidates,none, "function declares an opaque return type, but has no return statements " "in its body from which to infer an underlying type", ()) @@ -4073,6 +4085,9 @@ NOTE(opaque_type_underlying_type_candidate_here,none, ERROR(opaque_type_self_referential_underlying_type,none, "function opaque return type was inferred as %0, which defines the " "opaque type in terms of itself", (Type)) +ERROR(opaque_type_var_no_init,none, + "property declares an opaque return type, but has no initializer " + "expression from which to infer an underlying type", ()) ERROR(opaque_type_var_no_underlying_type,none, "property declares an opaque return type, but cannot infer the " "underlying type from its initializer expression", ()) @@ -4101,7 +4116,7 @@ ERROR(pattern_type_mismatch_context,none, ERROR(tuple_pattern_in_non_tuple_context,none, "tuple pattern cannot match values of the non-tuple type %0", (Type)) -WARNING(found_one_pattern_for_several_associated_values,none, +WARNING(found_one_pattern_for_several_associated_values,Deprecation, "enum case '%0' has %1 associated values; matching them as a tuple " "is deprecated", (StringRef, unsigned)) WARNING(converting_tuple_into_several_associated_values,none, @@ -4246,8 +4261,8 @@ ERROR(throwing_interpolation_without_try,none, "interpolation can throw but is not marked with 'try'", ()) ERROR(throwing_call_without_try,none, "call can throw but is not marked with 'try'", ()) -ERROR(throwing_spawn_let_without_try,none, - "reading 'spawn let' can throw but is not marked with 'try'", ()) +ERROR(throwing_async_let_without_try,none, + "reading 'async let' can throw but is not marked with 'try'", ()) ERROR(throwing_prop_access_without_try,none, "property access can throw but is not marked with 'try'", ()) ERROR(throwing_subscript_access_without_try,none, @@ -4276,8 +4291,8 @@ NOTE(async_access_without_await,none, NOTE(async_call_without_await_in_autoclosure,none, "call is 'async' in an autoclosure argument", ()) -NOTE(async_call_without_await_in_spawn_let,none, - "call is 'async' in an 'spawn let' initializer", ()) +NOTE(async_call_without_await_in_async_let,none, + "call is 'async' in an 'async let' initializer", ()) WARNING(no_async_in_await,none, "no 'async' operations occur within 'await' expression", ()) @@ -4290,13 +4305,11 @@ ERROR(await_in_illegal_context,none, "%select{<>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0", (unsigned)) ERROR(async_in_nonasync_function,none, - "%select{'async'|'async' call|'await'|'spawn let'|'async' property access|'async' subscript access}0 in " + "%select{'async'|'async' call|'await'|'async let'|'async' property access|'async' subscript access}0 in " "%select{a function|an autoclosure}1 that does not support concurrency", (unsigned, bool)) NOTE(note_add_async_to_function,none, "add 'async' to function %0 to make it asynchronous", (DeclName)) -NOTE(note_add_asynchandler_to_function,none, - "add '@asyncHandler' to function %0 to create an implicit asynchronous context", (DeclName)) NOTE(note_add_nonisolated_to_decl,none, "add 'nonisolated' to %0 to make this %1 not isolated to the actor", (DeclName, DescriptiveDeclKind)) @@ -4317,48 +4330,21 @@ NOTE(protocol_witness_async_conflict,none, ERROR(async_autoclosure_nonasync_function,none, "'async' autoclosure parameter in a non-'async' function", ()) -WARNING(async_let_is_spawn_let,none, - "'async let' is now 'spawn let'", ()) - -ERROR(spawn_not_let,none, - "'spawn' can only be used with 'let' declarations", ()) -ERROR(spawn_let_not_local,none, - "'spawn let' can only be used on local declarations", ()) -ERROR(spawn_let_not_initialized,none, - "'spawn let' binding requires an initializer expression", ()) -ERROR(spawn_let_no_variables,none, - "'spawn let' requires at least one named variable", ()) -NOTE(spawn_let_without_await,none, - "reference to spawn let %0 is 'async'", (DeclName)) -ERROR(spawn_let_in_illegal_context,none, - "spawn let %0 cannot be referenced in " +ERROR(async_not_let,none, + "'async' can only be used with 'let' declarations", ()) +ERROR(async_let_not_local,none, + "'async let' can only be used on local declarations", ()) +ERROR(async_let_not_initialized,none, + "'async let' binding requires an initializer expression", ()) +ERROR(async_let_no_variables,none, + "'async let' requires at least one named variable", ()) +NOTE(async_let_without_await,none, + "reference to async let %0 is 'async'", (DeclName)) +ERROR(async_let_in_illegal_context,none, + "async let %0 cannot be referenced in " "%select{<>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}1", (DeclName, unsigned)) -ERROR(asynchandler_non_func,none, - "'@asyncHandler' can only be applied to functions", - ()) -ERROR(asynchandler_returns_value,none, - "'@asyncHandler' function can only return 'Void'", - ()) -ERROR(asynchandler_throws,none, - "'@asyncHandler' function cannot throw", - ()) -ERROR(asynchandler_async,none, - "'@asyncHandler' function cannot be 'async' itself", - ()) -ERROR(asynchandler_inout_parameter,none, - "'inout' parameter is not allowed in '@asyncHandler' function", - ()) -ERROR(asynchandler_noescape_closure_parameter,none, - "non-escaping closure parameter is not allowed in '@asyncHandler' function", - ()) -ERROR(asynchandler_mutating,none, - "'@asyncHandler' function cannot be 'mutating'", - ()) -ERROR(asynchandler_removed,none, - "'@asyncHandler' has been removed from the language", ()) - ERROR(objc_ambiguous_async_convention,none, "%0 overrides or implements protocol requirements for Objective-C " "declarations with incompatible async conventions", @@ -4370,7 +4356,15 @@ ERROR(async_objc_dynamic_self,none, "asynchronous method returning 'Self' cannot be '@objc'", ()) ERROR(actor_inheritance,none, - "actor types do not support inheritance", ()) + "%select{actor|distributed actor}0 types do not support inheritance", + (bool)) + +ERROR(actor_protocol_illegal_inheritance,none, + "non-actor type %0 cannot conform to the 'Actor' protocol", + (DeclName)) +ERROR(distributed_actor_protocol_illegal_inheritance,none, + "non-distributed actor type %0 cannot conform to the 'DistributedActor' protocol", + (DeclName)) ERROR(unowned_executor_outside_actor,none, "'unownedExecutor' can only be implemented within the main " @@ -4380,13 +4374,20 @@ ERROR(override_implicit_unowned_executor,none, "explicitly defined", ()) ERROR(actor_isolated_non_self_reference,none, - "actor-isolated %0 %1 can only be %select{referenced|mutated|used 'inout'}3 " - "%select{from inside the actor|on 'self'}2", - (DescriptiveDeclKind, DeclName, bool, unsigned)) -ERROR(actor_isolated_self_independent_context,none, - "actor-isolated %0 %1 can not be %select{referenced|mutated|used 'inout'}2 from a " - "non-isolated context", - (DescriptiveDeclKind, DeclName, unsigned)) + "actor-isolated %0 %1 can not be " + "%select{referenced|mutated|used 'inout'}2 " + "%select{on a non-isolated actor instance|" + "from a Sendable function|from a Sendable closure|" + "from an 'async let' initializer|from global actor %4|" + "from the main actor|from a non-isolated context}3", + (DescriptiveDeclKind, DeclName, unsigned, unsigned, Type)) +ERROR(distributed_actor_isolated_non_self_reference,none, + "distributed actor-isolated %0 %1 can only be referenced " + "inside the distributed actor", + (DescriptiveDeclKind, DeclName)) +ERROR(distributed_actor_needs_explicit_distributed_import,none, + "'_Distributed' module not imported, required for 'distributed actor'", + ()) ERROR(actor_isolated_inout_state,none, "actor-isolated %0 %1 cannot be passed 'inout' to" "%select{| implicitly}2 'async' function call", @@ -4394,10 +4395,6 @@ ERROR(actor_isolated_inout_state,none, ERROR(actor_isolated_mutating_func,none, "cannot call mutating async function %0 on actor-isolated %1 %2", (DeclName, DescriptiveDeclKind, DeclName)) -ERROR(actor_isolated_global_actor_context,none, - "actor-isolated %0 %1 can not be %select{referenced|mutated|used 'inout'}3 " - "from%select{| synchronous}4 context of global actor %2", - (DescriptiveDeclKind, DeclName, Type, unsigned, bool)) ERROR(global_actor_from_instance_actor_context,none, "%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4" " from actor %3 %select{|in a synchronous context}5", @@ -4422,18 +4419,9 @@ ERROR(actor_isolated_partial_apply,none, ERROR(concurrent_access_local,none, "use of local %0 %1 in concurrently-executing code", (DescriptiveDeclKind, DeclName)) -ERROR(actor_isolated_from_concurrent_closure,none, - "actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from a concurrent closure", - (DescriptiveDeclKind, DeclName, unsigned)) -ERROR(actor_isolated_from_concurrent_function,none, - "actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from a concurrent function", - (DescriptiveDeclKind, DeclName, unsigned)) -ERROR(actor_isolated_from_spawn_let,none, - "actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from 'spawn let' initializer", - (DescriptiveDeclKind, DeclName, unsigned)) ERROR(actor_isolated_keypath_component,none, - "cannot form key path to actor-isolated %0 %1", - (DescriptiveDeclKind, DeclName)) + "cannot form key path to%select{| distributed}0 actor-isolated %1 %2", + (bool, DescriptiveDeclKind, DeclName)) ERROR(effectful_keypath_component,none, "cannot form key path to %0 with 'throws' or 'async'", (DescriptiveDeclKind)) @@ -4448,12 +4436,39 @@ NOTE(actor_isolated_sync_func,none, "calls to %0 %1 from outside of its actor context are " "implicitly asynchronous", (DescriptiveDeclKind, DeclName)) +NOTE(distributed_actor_isolated_method_note,none, + "only 'distributed' functions can be called from outside the distributed actor", // TODO: improve error message + ()) +ERROR(distributed_actor_isolated_method,none, + "only 'distributed' functions can be called from outside the distributed actor", // TODO: improve error message to be more like 'non-distributed' ... defined here + ()) +ERROR(distributed_actor_func_param_not_codable,none, + "distributed function parameter '%0' of type %1 does not conform to 'Codable'", + (StringRef, Type)) +ERROR(distributed_actor_func_result_not_codable,none, + "distributed function result type %0 does not conform to 'Codable'", + (Type)) +ERROR(distributed_actor_remote_func_implemented_manually,none, + "Distributed function's %0 remote counterpart %1 cannot not be implemented manually.", + (Identifier, Identifier)) +ERROR(nonisolated_distributed_actor_storage,none, + "'nonisolated' can not be applied to distributed actor stored properties", + ()) +ERROR(distributed_actor_func_nonisolated, none, + "function %0 cannot be both 'nonisolated' and 'distributed'", + (DeclName)) +ERROR(distributed_actor_remote_func_is_not_static,none, + "remote function %0 must be static.", + (DeclName)) +ERROR(distributed_actor_remote_func_is_not_async_throws,none, + "remote function %0 must be 'async throws'.", + (DeclName)) +ERROR(distributed_actor_remote_func_must_not_be_distributed,none, + "remote function %0 must be 'async throws'.", + (DeclName)) NOTE(actor_mutable_state,none, "mutation of this %0 is only permitted within the actor", (DescriptiveDeclKind)) -NOTE(actor_isolated_let,none, - "use `nonisolated` to allow synchronous access to 'let' from outside " - "the actor", ()) WARNING(shared_mutable_state_access,none, "reference to %0 %1 is not concurrency-safe because it involves " "shared mutable state", (DescriptiveDeclKind, DeclName)) @@ -4472,50 +4487,65 @@ ERROR(global_actor_isolated_requirement_witness_conflict,none, "%0 %1 isolated to global actor %2 can not satisfy corresponding " "requirement from protocol %3 isolated to global actor %4", (DescriptiveDeclKind, DeclName, Type, Identifier, Type)) +ERROR(actor_cannot_conform_to_global_actor_protocol,none, + "actor %0 cannot conform to global actor isolated protocol %1", + (Type, Type)) +NOTE(protocol_isolated_to_global_actor_here,none, + "%0 is isolated to global actor %1 here", (Type, Type)) -WARNING(non_concurrent_param_type,none, +ERROR(isolated_parameter_not_actor,none, + "'isolated' parameter has non-actor type %0", (Type)) + +WARNING(non_sendable_param_type,none, "cannot pass argument of non-sendable type %0 across actors", (Type)) -WARNING(non_concurrent_result_type,none, +WARNING(non_sendable_result_type,none, "cannot call function returning non-sendable type %0 across " "actors", (Type)) -WARNING(non_concurrent_property_type,none, - "cannot use %0 %1 with a non-sendable type %2 " +WARNING(non_sendable_property_type,none, + "cannot use %1 %2 with a non-sendable type %0 " "%select{across actors|from concurrently-executed code}3", - (DescriptiveDeclKind, DeclName, Type, bool)) -WARNING(non_concurrent_keypath_capture,none, + (Type, DescriptiveDeclKind, DeclName, bool)) +WARNING(non_sendable_keypath_capture,none, "cannot form key path that captures non-sendable type %0", (Type)) +WARNING(non_sendable_keypath_access,none, + "cannot form key path that accesses non-sendable type %0", + (Type)) ERROR(non_concurrent_type_member,none, "%select{stored property %1|associated value %1}0 of " "'Sendable'-conforming %2 %3 has non-sendable type %4", (bool, DeclName, DescriptiveDeclKind, DeclName, Type)) -ERROR(non_sendable_nonisolated_let,none, - "non-isolated let property %0 has non-Sendable type %1", (DeclName, Type)) ERROR(concurrent_value_class_mutable_property,none, "stored property %0 of 'Sendable'-conforming %1 %2 is mutable", (DeclName, DescriptiveDeclKind, DeclName)) ERROR(concurrent_value_outside_source_file,none, "conformance to 'Sendable' must occur in the same source file as " - "%0 %1; use 'UnsafeSendable' for retroactive conformance", + "%0 %1; use '@unchecked Sendable' for retroactive conformance", (DescriptiveDeclKind, DeclName)) ERROR(concurrent_value_nonfinal_class,none, "non-final class %0 cannot conform to `Sendable`; " - "use `UnsafeSendable`", (DeclName)) + "use `@unchecked Sendable`", (DeclName)) ERROR(concurrent_value_inherit,none, "`Sendable` class %1 cannot inherit from another class" "%select{| other than 'NSObject'}0", (bool, DeclName)) - -ERROR(actorindependent_mutable_storage,none, - "'@actorIndependent' can not be applied to stored properties", +ERROR(non_sendable_type,none, + "type %0 does not conform to the 'Sendable' protocol", (Type)) + +WARNING(unchecked_conformance_not_special,none, + "@unchecked conformance to %0 has no meaning", (Type)) +ERROR(unchecked_not_inheritance_clause,none, + "'unchecked' attribute only applies in inheritance clauses", ()) +ERROR(unchecked_not_existential,none, + "'unchecked' attribute cannot apply to non-protocol type %0", (Type)) + +ERROR(nonisolated_let,none, + "'nonisolated' is meaningless on 'let' declarations because " + "they are immutable", ()) -ERROR(actorindependent_local_var,none, - "'@actorIndependent' can not be applied to local variables", - ()) - ERROR(nonisolated_mutable_storage,none, - "nonisolated' can not be applied to stored properties", + "'nonisolated' can not be applied to stored properties", ()) ERROR(nonisolated_local_var,none, "'nonisolated' can not be applied to local variables", @@ -4526,28 +4556,57 @@ ERROR(actor_instance_property_wrapper,none, "the actor instance; consider 'nonisolated'", (Identifier, Identifier)) +ERROR(distributed_actor_func_defined_outside_of_distributed_actor,none, + "distributed function %0 is declared outside of an distributed actor", + (DeclName)) +ERROR(distributed_actor_local_var,none, + "'distributed' can not be applied to local variables", + ()) +ERROR(distributed_actor_property,none, + "'distributed' can not be applied to local properties", + ()) +ERROR(distributed_actor_storage,none, + "'distributed' can not be applied to actor properties; " + "Only functions can be 'distributed'", + ()) +ERROR(distributed_actor_not_actor,none, + "'distributed' can only be applied to 'actor' definitions, " + "and distributed actor-isolated async functions", + ()) +ERROR(distributed_actor_not_actor_func,none, + "'distributed' can only be applied to distributed actor async functions", + ()) +ERROR(distributed_actor_func_static,none, + "'distributed' functions cannot be 'static'", + ()) +ERROR(distributed_actor_func_not_in_distributed_actor,none, + "'distributed' function can only be declared within 'distributed actor'", + ()) +ERROR(distributed_actor_designated_ctor_must_have_one_transport_param,none, + "designated distributed actor initializer %0 must accept exactly one " + "ActorTransport parameter, found %1", + (DeclName, int)) +ERROR(distributed_actor_designated_ctor_missing_transport_param,none, + "designated distributed actor initializer %0 is missing required " + "ActorTransport parameter", + (DeclName)) +ERROR(distributed_actor_user_defined_special_property,none, + "property %0 cannot be defined explicitly, as it conflicts with " + "distributed actor synthesized stored property", + (DeclName)) +ERROR(distributed_actor_independent_property_must_be_let,none, + "_distributedActorIndependent can be applied to properties, however they must be 'let'", + ()) +NOTE(distributed_actor_isolated_property,none, + "distributed actor state is only available within the actor instance", // TODO: reword in terms of isolation + ()) + ERROR(concurrency_lib_missing,none, "missing '%0' declaration, probably because the '_Concurrency' " "module was not imported", (StringRef)) ERROR(async_main_no_concurrency,none, "'_Concurrency' module not imported, required for async main", ()) -ERROR(global_actor_missing_shared,none, - "global actor %0 requires a static property 'shared' that produces an " - "actor instance", (Identifier)) -NOTE(global_actor_shared_not_static,none, - "'shared' property in global actor is not 'static'", ()) -NOTE(global_actor_shared_inaccessible,none, - "'shared' property has more restrictive access (%0) than its global actor " - "(%1)", - (StringRef, StringRef)) -NOTE(global_actor_shared_constrained_extension,none, - "'shared' property in global actor cannot be in a constrained extension", - ()) -NOTE(global_actor_shared_non_actor_type,none, - "'shared' property type %0 does not conform to the 'Actor' protocol", - (Type)) - ERROR(multiple_global_actors,none, "declaration can not have multiple global actor attributes (%0 and %1)", (Identifier, Identifier)) @@ -4559,6 +4618,8 @@ ERROR(global_actor_on_local_variable,none, "local variable %0 cannot have a global actor", (DeclName)) ERROR(global_actor_non_unsafe_init,none, "global actor attribute %0 argument can only be '(unsafe)'", (Type)) +ERROR(global_actor_non_final_class,none, + "non-final class %0 cannot be a global actor", (DeclName)) ERROR(actor_isolation_multiple_attr,none, "%0 %1 has multiple actor-isolation attributes ('%2' and '%3')", @@ -4566,6 +4627,9 @@ ERROR(actor_isolation_multiple_attr,none, ERROR(actor_isolation_override_mismatch,none, "%0 %1 %2 has different actor isolation from %3 overridden declaration", (ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation)) +ERROR(actor_isolation_superclass_mismatch,none, + "%0 class %1 has different actor isolation from %2 superclass %3", + (ActorIsolation, DeclName, ActorIsolation, DeclName)) //------------------------------------------------------------------------------ // MARK: Type Check Types @@ -4643,7 +4707,7 @@ ERROR(enum_element_ellipsis,none, WARNING(implicitly_unwrapped_optional_in_illegal_position_interpreted_as_optional,none, "using '!' is not allowed here; treating this as '?' instead", ()) -WARNING(implicitly_unwrapped_optional_deprecated_in_this_position,none, +WARNING(implicitly_unwrapped_optional_deprecated_in_this_position,Deprecation, "using '!' here is deprecated and will be removed in a future release", ()) ERROR(implicitly_unwrapped_optional_in_illegal_position,none, @@ -4671,10 +4735,6 @@ ERROR(invalid_ownership_is_let,none, ERROR(ownership_invalid_in_protocols,none, "%0 cannot be applied to a property declaration in a protocol", (ReferenceOwnership)) -WARNING(ownership_invalid_in_protocols_compat_warning,none, - "%0 should not be applied to a property declaration " - "in a protocol and will be disallowed in future versions", - (ReferenceOwnership)) // required ERROR(required_initializer_nonclass,none, @@ -4707,7 +4767,7 @@ ERROR(attribute_requires_function_type,none, "@%0 attribute only applies to function types", (StringRef)) ERROR(unsupported_convention,none, "convention '%0' not supported", (StringRef)) -ERROR(unreferenced_generic_parameter,none, +ERROR(unreferenced_generic_parameter,NoUsage, "generic parameter '%0' is not used in function signature", (StringRef)) ERROR(unexpected_ctype_for_non_c_convention,none, "convention '%0' does not support the 'cType' argument label, did you " @@ -4822,9 +4882,9 @@ ERROR(objc_operator, none, "operator methods cannot be declared @objc", ()) ERROR(objc_operator_proto, none, "@objc protocols must not have operator requirements", ()) -WARNING(objc_inference_swift3_dynamic,none, +WARNING(objc_inference_swift3_dynamic,Deprecation, "inference of '@objc' for 'dynamic' members is deprecated", ()) -WARNING(objc_inference_swift3_objc_derived,none, +WARNING(objc_inference_swift3_objc_derived,Deprecation, "inference of '@objc' for members of Objective-C-derived classes is " "deprecated", ()) @@ -4849,7 +4909,7 @@ ERROR(objc_setter_for_nonobjc_property,none, "'@objc' setter for non-'@objc' property", ()) ERROR(objc_setter_for_nonobjc_subscript,none, "'@objc' setter for non-'@objc' subscript", ()) -WARNING(accessor_swift3_objc_inference,none, +WARNING(accessor_swift3_objc_inference,Deprecation, "%select{%0 %1|%1}2 with '@objc' %select{getter|setter}3 depends on " "deprecated inference of '@objc'", (DescriptiveDeclKind, DeclName, bool, bool)) @@ -4944,7 +5004,7 @@ NOTE(objc_overriding_objc_decl,none, NOTE(objc_witness_objc_requirement,none, "satisfying requirement for %0 %1 in protocol %2", (DescriptiveDeclKind, DeclName, Identifier)) -WARNING(witness_swift3_objc_inference,none, +WARNING(witness_swift3_objc_inference,Deprecation, "use of %0 %1 to satisfy a requirement of protocol %2 depends on " "'@objc' inference deprecated in Swift 4", (DescriptiveDeclKind, DeclName, Type)) @@ -5039,10 +5099,11 @@ NOTE(objc_declared_here,none, OBJC_DIAG_SELECT " declared here", (unsigned, DeclName)) +// %2 and %3 are unused to make signature match with diag::objc_redecl. ERROR(objc_redecl_same,none, - OBJC_DIAG_SELECT " with Objective-C selector %2 conflicts with " + OBJC_DIAG_SELECT " with Objective-C selector %4 conflicts with " "previous declaration with the same Objective-C selector", - (unsigned, DeclName, ObjCSelector)) + (unsigned, DeclName, unsigned, DeclName, ObjCSelector)) ERROR(objc_override_other,none, OBJC_DIAG_SELECT " with Objective-C selector %4 conflicts with " @@ -5226,13 +5287,13 @@ NOTE(availability_obsoleted, none, "%select{getter for |setter for |}0%1 was obsoleted in %2 %3", (unsigned, DeclName, StringRef, llvm::VersionTuple)) -WARNING(availability_deprecated, none, +WARNING(availability_deprecated, Deprecation, "%select{getter for |setter for |}0%1 %select{is|%select{is|was}4}2 " "deprecated%select{| in %3%select{| %5}4}2%select{|: %6}6", (unsigned, DeclName, bool, StringRef, bool, llvm::VersionTuple, StringRef)) -WARNING(availability_deprecated_rename, none, +WARNING(availability_deprecated_rename, Deprecation, "%select{getter for |setter for |}0%1 %select{is|%select{is|was}4}2 " "deprecated%select{| in %3%select{| %5}4}2: " "%select{renamed to|replaced by}6%" REPLACEMENT_DECL_KIND_SELECT "7 " @@ -5258,6 +5319,10 @@ ERROR(availability_opaque_types_only_version_newer, none, "'some' return types are only available in %0 %1 or newer", (StringRef, llvm::VersionTuple)) +ERROR(availability_concurrency_only_version_newer, none, + "concurrency is only available in %0 %1 or newer", + (StringRef, llvm::VersionTuple)) + NOTE(availability_guard_with_version_check, none, "add 'if #available' version check", ()) @@ -5299,6 +5364,10 @@ ERROR(availability_enum_element_no_potential, none, "enum cases with associated values cannot be marked potentially unavailable with " "'@available'", ()) +WARNING(availability_enum_element_no_potential_warn, + none, "enum cases with associated values cannot be marked potentially unavailable with " + "'@available'", ()) + ERROR(availability_protocol_requires_version, none, "protocol %0 requires %1 to be available in %2 %3 and newer", (Identifier, DeclName, StringRef, llvm::VersionTuple)) @@ -5344,7 +5413,7 @@ NOTE(conformance_availability_obsoleted, none, "conformance of %0 to %1 was obsoleted in %2 %3", (Type, Type, StringRef, llvm::VersionTuple)) -WARNING(conformance_availability_deprecated, none, +WARNING(conformance_availability_deprecated, Deprecation, "conformance of %0 to %1 %select{is|%select{is|was}4}2 " "deprecated%select{| in %3%select{| %5}4}2%select{|: %6}6", (Type, Type, bool, StringRef, bool, llvm::VersionTuple, @@ -5490,31 +5559,31 @@ NOTE(specialize_found_function_of_type, none, // MARK: Variable usage diagnostics //------------------------------------------------------------------------------ -WARNING(pbd_never_used_stmtcond, none, +WARNING(pbd_never_used_stmtcond, NoUsage, "value %0 was defined but never used; consider replacing " "with boolean test", (Identifier)) -WARNING(unused_setter_parameter, none, +WARNING(unused_setter_parameter, NoUsage, "setter argument %0 was never used, but the property was accessed", (Identifier)) NOTE(fixit_for_unused_setter_parameter, none, "did you mean to use %0 instead of accessing the property's current value?", (Identifier)) -WARNING(pbd_never_used, none, +WARNING(pbd_never_used, NoUsage, "initialization of %select{variable|immutable value}1 %0 was never used" "; consider replacing with assignment to '_' or removing it", (Identifier, unsigned)) -WARNING(capture_never_used, none, +WARNING(capture_never_used, NoUsage, "capture %0 was never used", (Identifier)) -WARNING(variable_never_used, none, +WARNING(variable_never_used, NoUsage, "%select{variable|immutable value}1 %0 was never used; " "consider replacing with '_' or removing it", (Identifier, unsigned)) -WARNING(immutable_value_never_used_but_assigned, none, +WARNING(immutable_value_never_used_but_assigned, NoUsage, "immutable value %0 was never used; consider removing it", (Identifier)) WARNING(variable_never_mutated, none, @@ -5599,7 +5668,7 @@ ERROR(override_nsobject_hash_error,none, "'NSObject.hash(into:)' is not overridable; " "did you mean to override 'NSObject.hash'?", ()) -WARNING(hashvalue_implementation,none, +WARNING(hashvalue_implementation,Deprecation, "'Hashable.hashValue' is deprecated as a protocol requirement; " "conform type %0 to 'Hashable' by implementing 'hash(into:)' instead", (Type)) @@ -5719,10 +5788,10 @@ ERROR(property_wrapper_type_not_usable_from_inline,none, "%select{%select{variable|constant}0|property}1 " "must be '@usableFromInline' or public", (bool, bool)) -WARNING(property_wrapper_wrapperValue,none, +WARNING(property_wrapper_wrapperValue,Deprecation, "property wrapper's 'wrapperValue' property should be renamed to " "'projectedValue'; use of 'wrapperValue' is deprecated", ()) -WARNING(property_wrapper_init_initialValue,none, +WARNING(property_wrapper_init_initialValue,Deprecation, "property wrapper's 'init(initialValue:)' should be renamed " "to 'init(wrappedValue:)'; use of 'init(initialValue:)' is deprecated", ()) @@ -5814,12 +5883,18 @@ NOTE(result_builder_missing_build_array, none, NOTE(result_builder_missing_build_limited_availability, none, "add 'buildLimitedAvailability(_:)' to the result " "builder %0 to erase type information for less-available types", (Type)) +ERROR(result_builder_requires_explicit_var_initialization,none, + "local variable%select{| '%1'}0 requires explicit initializer to be used with " + "result builder %2", (bool, StringRef, DeclName)) +ERROR(cannot_declare_computed_var_in_result_builder,none, + "cannot declare local %select{lazy|wrapped|computed|observed}0 variable " + "in result builder", (unsigned)) //------------------------------------------------------------------------------ // MARK: Tuple Shuffle Diagnostics //------------------------------------------------------------------------------ - WARNING(warn_reordering_tuple_shuffle_deprecated,none, + WARNING(warn_reordering_tuple_shuffle_deprecated,Deprecation, "expression shuffles the elements of this tuple; " "this behavior is deprecated", ()) @@ -5873,31 +5948,43 @@ ERROR(atomics_ordering_must_be_constant, none, // MARK: access notes //------------------------------------------------------------------------------ +#define WHICH_ACCESS_NOTE(reason) "specified by access note for %" #reason + REMARK(attr_added_by_access_note, none, - "access note for %0 adds %select{attribute|modifier}1 '%2' to this %3", - (StringRef, bool, StringRef, DescriptiveDeclKind)) + "implicitly added '%1' to this %2, as " WHICH_ACCESS_NOTE(0), + (StringRef, StringRef, DescriptiveDeclKind)) NOTE(fixit_attr_added_by_access_note, none, - "add %select{attribute|modifier}0 explicitly to silence this warning", - (bool)) + "add '%0' explicitly to silence this warning", + (StringRef)) REMARK(attr_removed_by_access_note, none, - "access note for %0 removes %select{attribute|modifier}1 '%2' from " - "this %3", - (StringRef, bool, StringRef, DescriptiveDeclKind)) + "implicitly removed '%1' from this %3, as " WHICH_ACCESS_NOTE(0), + (StringRef, StringRef, DescriptiveDeclKind)) NOTE(fixit_attr_removed_by_access_note, none, - "remove %select{attribute|modifier}0 explicitly to silence this warning", - (bool)) + "remove '%0' explicitly to silence this warning", + (StringRef)) REMARK(attr_objc_name_changed_by_access_note, none, - "access note for %0 changes the '@objc' name of this %1 to %2", - (StringRef, DescriptiveDeclKind, ObjCSelector)) + "implicitly changed Objective-C name of this %1 to %2, as " + WHICH_ACCESS_NOTE(0), + (StringRef, DescriptiveDeclKind, ObjCSelector)) NOTE(fixit_attr_objc_name_changed_by_access_note, none, - "change '@objc' name in source code explicitly to silence this warning", + "change Objective-C name explicitly to silence this warning", ()) -REMARK(attr_objc_name_conflicts_with_access_note, none, - "access note for %0 changes the '@objc' name of this %1 to %2, but " - "source code specifies %3; the access note will be ignored", - (StringRef, DescriptiveDeclKind, ObjCSelector, ObjCSelector)) + +// Bad access note diagnostics. These are usually emitted as remarks, but +// '-Raccess-note=all-validate' emits them as errors. + +ERROR(attr_objc_name_conflicts_with_access_note, none, + "ignored access note: did not change Objective-C name of this %1 from %2 " + "to %3, even though it was " WHICH_ACCESS_NOTE(0), + (StringRef, DescriptiveDeclKind, ObjCSelector, ObjCSelector)) +ERROR(wrap_invalid_attr_added_by_access_note, none, + "ignored access note: %0; did not implicitly add '%2' to this %3, even " + "though it was " WHICH_ACCESS_NOTE(1), + (DiagnosticInfo *, StringRef, StringRef, DescriptiveDeclKind)) + +#undef WHICH_ACCESS_NOTE #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/EducationalNotes.def b/include/swift/AST/EducationalNotes.def index b2a2914e85afb..bc57ec20410c0 100644 --- a/include/swift/AST/EducationalNotes.def +++ b/include/swift/AST/EducationalNotes.def @@ -21,8 +21,8 @@ // EDUCATIONAL_NOTES(DIAG_ID, EDUCATIONAL_NOTE_FILENAMES...) -EDUCATIONAL_NOTES(unsupported_existential_type, - "associated-type-requirements.md") +EDUCATIONAL_NOTES(could_not_use_member_on_existential, + "existential-member-access-limitations.md") EDUCATIONAL_NOTES(cannot_pass_type_to_non_ephemeral, "temporary-pointers.md") EDUCATIONAL_NOTES(cannot_pass_type_to_non_ephemeral_warning, @@ -91,5 +91,8 @@ EDUCATIONAL_NOTES(result_builder_missing_build_either, "result-builder-methods.md") EDUCATIONAL_NOTES(result_builder_missing_build_array, "result-builder-methods.md") - + +EDUCATIONAL_NOTES(multiple_inheritance, + "multiple-inheritance.md") + #undef EDUCATIONAL_NOTES diff --git a/include/swift/AST/Effects.h b/include/swift/AST/Effects.h index f69614ade7898..33a6412c8f274 100644 --- a/include/swift/AST/Effects.h +++ b/include/swift/AST/Effects.h @@ -26,6 +26,7 @@ #define SWIFT_EFFECTS_H #include "swift/AST/Type.h" +#include "swift/Basic/OptionSet.h" #include @@ -34,11 +35,14 @@ class raw_ostream; } namespace swift { +class AbstractFunctionDecl; +class ProtocolDecl; enum class EffectKind : uint8_t { - Throws, - Async + Throws = 1 << 0, + Async = 1 << 1 }; +using PossibleEffects = OptionSet; void simple_display(llvm::raw_ostream &out, const EffectKind kind); diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 133a27a04d8af..817b3f94ec7d1 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -138,7 +138,7 @@ enum class AccessSemantics : uint8_t { }; /// Expr - Base class for all expressions in swift. -class alignas(8) Expr { +class alignas(8) Expr : public ASTAllocated { Expr(const Expr&) = delete; void operator=(const Expr&) = delete; @@ -166,9 +166,10 @@ class alignas(8) Expr { SWIFT_INLINE_BITFIELD_EMPTY(LiteralExpr, Expr); SWIFT_INLINE_BITFIELD_EMPTY(IdentityExpr, Expr); - SWIFT_INLINE_BITFIELD(LookupExpr, Expr, 1+1, + SWIFT_INLINE_BITFIELD(LookupExpr, Expr, 1+1+1, IsSuper : 1, - IsImplicitlyAsync : 1 + IsImplicitlyAsync : 1, + IsImplicitlyThrows : 1 ); SWIFT_INLINE_BITFIELD_EMPTY(DynamicLookupExpr, LookupExpr); @@ -194,10 +195,11 @@ class alignas(8) Expr { LiteralCapacity : 32 ); - SWIFT_INLINE_BITFIELD(DeclRefExpr, Expr, 2+2+1, + SWIFT_INLINE_BITFIELD(DeclRefExpr, Expr, 2+2+1+1, Semantics : 2, // an AccessSemantics FunctionRefKind : 2, - IsImplicitlyAsync : 1 + IsImplicitlyAsync : 1, + IsImplicitlyThrows : 1 ); SWIFT_INLINE_BITFIELD(UnresolvedDeclRefExpr, Expr, 2+2, @@ -345,10 +347,11 @@ class alignas(8) Expr { NumCaptures : 32 ); - SWIFT_INLINE_BITFIELD(ApplyExpr, Expr, 1+1+1+1, + SWIFT_INLINE_BITFIELD(ApplyExpr, Expr, 1+1+1+1+1, ThrowsIsSet : 1, Throws : 1, ImplicitlyAsync : 1, + ImplicitlyThrows : 1, NoAsync : 1 ); @@ -555,6 +558,9 @@ class alignas(8) Expr { /// the parent map. llvm::DenseMap getParentMap(); + /// Whether this expression is a valid parent for a given TypeExpr. + bool isValidParentOfTypeExpr(Expr *typeExpr) const; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS, unsigned Indent = 0) const; void dump(raw_ostream &OS, llvm::function_ref getType, @@ -564,20 +570,6 @@ class alignas(8) Expr { unsigned Indent = 0) const; void print(ASTPrinter &Printer, const PrintOptions &Opts) const; - - // Only allow allocation of Exprs using the allocator in ASTContext - // or by doing a placement new. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(Expr)); - - // Make placement new and vanilla new/delete illegal for Exprs. - void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() = delete; - - void *operator new(size_t Bytes, void *Mem) { - assert(Mem); - return Mem; - } }; /// ErrorExpr - Represents a semantically erroneous subexpression in the AST, @@ -1186,11 +1178,74 @@ class DiscardAssignmentExpr : public Expr { } }; +/// Describes the actor to which an implicit-async expression will hop. +struct ImplicitActorHopTarget { + enum Kind { + /// The "self" instance. + InstanceSelf, + /// A global actor with the given type. + GlobalActor, + /// An isolated parameter in a call. + IsolatedParameter, + }; + +private: + /// The lower two bits are the Kind, and the remaining bits are used for + /// the payload, which might by a TypeBase * (for a global actor) or a + /// integer value (for an isolated parameter). + uintptr_t bits; + + constexpr ImplicitActorHopTarget(uintptr_t bits) : bits(bits) { } + +public: + /// Default-initialized to instance "self". + constexpr ImplicitActorHopTarget() : bits(0) { } + + static ImplicitActorHopTarget forInstanceSelf() { + return ImplicitActorHopTarget(InstanceSelf); + } + + static ImplicitActorHopTarget forGlobalActor(Type globalActor) { + uintptr_t bits = + reinterpret_cast(globalActor.getPointer()) | GlobalActor; + return ImplicitActorHopTarget(bits); + } + + static ImplicitActorHopTarget forIsolatedParameter(unsigned index) { + uintptr_t bits = static_cast(index) << 2 | IsolatedParameter; + return ImplicitActorHopTarget(bits); + } + + /// Determine the kind of implicit actor hop being performed. + Kind getKind() const { + return static_cast(bits & 0x03); + } + + operator Kind() const { + return getKind(); + } + + /// Retrieve the global actor type for an implicit hop to a global actor. + Type getGlobalActor() const { + assert(getKind() == GlobalActor); + return Type(reinterpret_cast(bits & ~0x03)); + } + + /// Retrieve the (zero-based) parameter index for the isolated parameter + /// in a call. + unsigned getIsolatedParameterIndex() const { + assert(getKind() == IsolatedParameter); + return bits >> 2; + } +}; + + /// DeclRefExpr - A reference to a value, "x". class DeclRefExpr : public Expr { /// The declaration pointer. ConcreteDeclRef D; DeclNameLoc Loc; + ImplicitActorHopTarget implicitActorHopTarget; public: DeclRefExpr(ConcreteDeclRef D, DeclNameLoc Loc, bool Implicit, @@ -1202,6 +1257,7 @@ class DeclRefExpr : public Expr { static_cast(Loc.isCompound() ? FunctionRefKind::Compound : FunctionRefKind::Unapplied); Bits.DeclRefExpr.IsImplicitlyAsync = false; + Bits.DeclRefExpr.IsImplicitlyThrows = false; } /// Retrieve the declaration to which this expression refers. @@ -1216,13 +1272,34 @@ class DeclRefExpr : public Expr { } /// Determine whether this reference needs to happen asynchronously, i.e., - /// guarded by hop_to_executor - bool isImplicitlyAsync() const { return Bits.DeclRefExpr.IsImplicitlyAsync; } + /// guarded by hop_to_executor, and if so describe the target. + Optional isImplicitlyAsync() const { + if (!Bits.DeclRefExpr.IsImplicitlyAsync) + return None; - /// Set whether this reference needs to happen asynchronously, i.e., - /// guarded by hop_to_executor - void setImplicitlyAsync(bool isImplicitlyAsync) { - Bits.DeclRefExpr.IsImplicitlyAsync = isImplicitlyAsync; + return implicitActorHopTarget; + } + + /// Note that this reference is implicitly async and set the target. + void setImplicitlyAsync(ImplicitActorHopTarget target) { + Bits.DeclRefExpr.IsImplicitlyAsync = true; + implicitActorHopTarget = target; + } + + /// Determine whether this reference needs may implicitly throw. + /// + /// This is the case for non-throwing `distributed func` declarations, + /// which are cross-actor invoked, because such calls actually go over the + /// transport/network, and may throw from this, rather than the function + /// implementation itself.. + bool isImplicitlyThrows() const { return Bits.DeclRefExpr.IsImplicitlyThrows; } + + /// Set whether this reference must account for a `throw` occurring for reasons + /// other than the function implementation itself throwing, e.g. an + /// `ActorTransport` implementing a `distributed func` call throwing a + /// networking error. + void setImplicitlyThrows(bool isImplicitlyThrows) { + Bits.DeclRefExpr.IsImplicitlyThrows = isImplicitlyThrows; } /// Retrieve the concrete declaration reference. @@ -1534,6 +1611,7 @@ class UnresolvedDeclRefExpr : public Expr { class LookupExpr : public Expr { Expr *Base; ConcreteDeclRef Member; + ImplicitActorHopTarget implicitActorHopTarget; protected: explicit LookupExpr(ExprKind Kind, Expr *base, ConcreteDeclRef member, @@ -1541,6 +1619,7 @@ class LookupExpr : public Expr { : Expr(Kind, Implicit), Base(base), Member(member) { Bits.LookupExpr.IsSuper = false; Bits.LookupExpr.IsImplicitlyAsync = false; + Bits.LookupExpr.IsImplicitlyThrows = false; assert(Base); } @@ -1556,7 +1635,7 @@ class LookupExpr : public Expr { /// Determine whether the operation has a known underlying declaration or not. bool hasDecl() const { return static_cast(Member); } - + /// Retrieve the declaration that this /// operation refers to. /// Only valid when \c hasDecl() is true. ConcreteDeclRef getDecl() const { @@ -1571,13 +1650,34 @@ class LookupExpr : public Expr { void setIsSuper(bool isSuper) { Bits.LookupExpr.IsSuper = isSuper; } /// Determine whether this reference needs to happen asynchronously, i.e., - /// guarded by hop_to_executor - bool isImplicitlyAsync() const { return Bits.LookupExpr.IsImplicitlyAsync; } + /// guarded by hop_to_executor, and if so describe the target. + Optional isImplicitlyAsync() const { + if (!Bits.LookupExpr.IsImplicitlyAsync) + return None; + + return implicitActorHopTarget; + } - /// Set whether this reference needs to happen asynchronously, i.e., - /// guarded by hop_to_executor - void setImplicitlyAsync(bool isImplicitlyAsync) { - Bits.LookupExpr.IsImplicitlyAsync = isImplicitlyAsync; + /// Note that this reference is implicitly async and set the target. + void setImplicitlyAsync(ImplicitActorHopTarget target) { + Bits.LookupExpr.IsImplicitlyAsync = true; + implicitActorHopTarget = target; + } + + /// Determine whether this reference needs may implicitly throw. + /// + /// This is the case for non-throwing `distributed func` declarations, + /// which are cross-actor invoked, because such calls actually go over the + /// transport/network, and may throw from this, rather than the function + /// implementation itself.. + bool isImplicitlyThrows() const { return Bits.LookupExpr.IsImplicitlyThrows; } + + /// Set whether this reference must account for a `throw` occurring for reasons + /// other than the function implementation itself throwing, e.g. an + /// `ActorTransport` implementing a `distributed func` call throwing a + /// networking error. + void setImplicitlyThrows(bool isImplicitlyThrows) { + Bits.LookupExpr.IsImplicitlyThrows = isImplicitlyThrows; } static bool classof(const Expr *E) { @@ -3082,8 +3182,9 @@ class LinearToDifferentiableFunctionExpr : public ImplicitConversionExpr { } }; - -/// Use an opaque type to abstract a value of the underlying concrete type. +/// Use an opaque type to abstract a value of the underlying concrete type, +/// possibly nested inside other types. E.g. can perform coversions "T ---> +/// (opaque type)" and "S ---> S<(opaque type)>". class UnderlyingToOpaqueExpr : public ImplicitConversionExpr { public: UnderlyingToOpaqueExpr(Expr *subExpr, Type ty) @@ -3752,6 +3853,7 @@ class AbstractClosureExpr : public DeclContext, public Expr { } using DeclContext::operator new; + using DeclContext::operator delete; using Expr::dump; }; @@ -4143,13 +4245,11 @@ class AutoClosureExpr : public AbstractClosureExpr { /// Instances of this structure represent elements of the capture list that can /// optionally occur in a capture expression. struct CaptureListEntry { - VarDecl *Var; - PatternBindingDecl *Init; + PatternBindingDecl *PBD; - CaptureListEntry(VarDecl *Var, PatternBindingDecl *Init) - : Var(Var), Init(Init) { - } + explicit CaptureListEntry(PatternBindingDecl *PBD); + VarDecl *getVar() const; bool isSimpleSelfCapture() const; }; @@ -4446,7 +4546,9 @@ class ApplyExpr : public Expr { /// The argument being passed to it. Expr *Arg; - + + ImplicitActorHopTarget implicitActorHopTarget; + /// Returns true if \c e could be used as the call's argument. For most \c ApplyExpr /// subclasses, this means it is a \c ParenExpr or \c TupleExpr. bool validateArg(Expr *e) const; @@ -4458,6 +4560,7 @@ class ApplyExpr : public Expr { assert(validateArg(Arg) && "Arg is not a permitted expr kind"); Bits.ApplyExpr.ThrowsIsSet = false; Bits.ApplyExpr.ImplicitlyAsync = false; + Bits.ApplyExpr.ImplicitlyThrows = false; Bits.ApplyExpr.NoAsync = false; } @@ -4518,11 +4621,32 @@ class ApplyExpr : public Expr { /// /// where the new closure is declared to be async. /// - bool implicitlyAsync() const { - return Bits.ApplyExpr.ImplicitlyAsync; + /// When the application is implciitly async, the result describes + /// the actor to which we need to need to hop. + Optional isImplicitlyAsync() const { + if (!Bits.ApplyExpr.ImplicitlyAsync) + return None; + + return implicitActorHopTarget; + } + + /// Note that this application is implicitly async and set the target. + void setImplicitlyAsync(ImplicitActorHopTarget target) { + Bits.ApplyExpr.ImplicitlyAsync = true; + implicitActorHopTarget = target; + } + + /// Is this application _implicitly_ required to be a throwing call? + /// This can happen if the function is actually a proxy function invocation, + /// which may throw, regardless of the target function throwing, e.g. + /// a distributed function call on a 'remote' actor, may throw due to network + /// issues reported by the transport, regardless if the actual target function + /// can throw. + bool implicitlyThrows() const { + return Bits.ApplyExpr.ImplicitlyThrows; } - void setImplicitlyAsync(bool flag) { - Bits.ApplyExpr.ImplicitlyAsync = flag; + void setImplicitlyThrows(bool flag) { + Bits.ApplyExpr.ImplicitlyThrows = flag; } ValueDecl *getCalledValue() const; @@ -4588,6 +4712,14 @@ class CallExpr final : public ApplyExpr, /*trailingClosures=*/{}, /*implicit=*/true, getType); } + /// Create a new implicit call expression with no arguments and no + /// source-location information. + /// + /// \param fn The nullary function being called. + static CallExpr *createImplicitEmpty(ASTContext &ctx, Expr *fn) { + return createImplicit(ctx, fn, {}, {}); + } + /// Create a new call expression. /// /// \param fn The function being called @@ -4646,9 +4778,12 @@ class CallExpr final : public ApplyExpr, /// PrefixUnaryExpr - Prefix unary expressions like '!y'. class PrefixUnaryExpr : public ApplyExpr { + PrefixUnaryExpr(Expr *fn, Expr *operand, Type ty = Type()) + : ApplyExpr(ExprKind::PrefixUnary, fn, operand, /*implicit*/ false, ty) {} + public: - PrefixUnaryExpr(Expr *Fn, Expr *Arg, Type Ty = Type()) - : ApplyExpr(ExprKind::PrefixUnary, Fn, Arg, /*Implicit=*/false, Ty) {} + static PrefixUnaryExpr *create(ASTContext &ctx, Expr *fn, Expr *operand, + Type ty = Type()); SourceLoc getLoc() const { return getFn()->getStartLoc(); } @@ -4666,9 +4801,13 @@ class PrefixUnaryExpr : public ApplyExpr { /// PostfixUnaryExpr - Postfix unary expressions like 'y!'. class PostfixUnaryExpr : public ApplyExpr { + PostfixUnaryExpr(Expr *fn, Expr *operand, Type ty = Type()) + : ApplyExpr(ExprKind::PostfixUnary, fn, operand, /*implicit*/ false, ty) { + } + public: - PostfixUnaryExpr(Expr *Fn, Expr *Arg, Type Ty = Type()) - : ApplyExpr(ExprKind::PostfixUnary, Fn, Arg, /*Implicit=*/false, Ty) {} + static PostfixUnaryExpr *create(ASTContext &ctx, Expr *fn, Expr *operand, + Type ty = Type()); SourceLoc getLoc() const { return getFn()->getStartLoc(); } @@ -4687,9 +4826,20 @@ class PostfixUnaryExpr : public ApplyExpr { /// BinaryExpr - Infix binary expressions like 'x+y'. The argument is always /// an implicit tuple expression of the type expected by the function. class BinaryExpr : public ApplyExpr { + BinaryExpr(Expr *fn, TupleExpr *arg, bool implicit, Type ty = Type()) + : ApplyExpr(ExprKind::Binary, fn, arg, implicit, ty) { + assert(arg->getNumElements() == 2); + } + public: - BinaryExpr(Expr *Fn, TupleExpr *Arg, bool Implicit, Type Ty = Type()) - : ApplyExpr(ExprKind::Binary, Fn, Arg, Implicit, Ty) {} + static BinaryExpr *create(ASTContext &ctx, Expr *lhs, Expr *fn, Expr *rhs, + bool implicit, Type ty = Type()); + + /// The left-hand argument of the binary operation. + Expr *getLHS() const { return cast(getArg())->getElement(0); } + + /// The right-hand argument of the binary operation. + Expr *getRHS() const { return cast(getArg())->getElement(1); } SourceLoc getLoc() const { return getFn()->getLoc(); } @@ -4697,8 +4847,6 @@ class BinaryExpr : public ApplyExpr { SourceLoc getStartLoc() const { return getArg()->getStartLoc(); } SourceLoc getEndLoc() const { return getArg()->getEndLoc(); } - TupleExpr *getArg() const { return cast(ApplyExpr::getArg()); } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::Binary;} }; @@ -4727,15 +4875,19 @@ class SelfApplyExpr : public ApplyExpr { /// is modeled as a DeclRefExpr or OverloadSetRefExpr on the method. class DotSyntaxCallExpr : public SelfApplyExpr { SourceLoc DotLoc; - -public: - DotSyntaxCallExpr(Expr *FnExpr, SourceLoc DotLoc, Expr *BaseExpr, - Type Ty = Type()) - : SelfApplyExpr(ExprKind::DotSyntaxCall, FnExpr, BaseExpr, Ty), - DotLoc(DotLoc) { + + DotSyntaxCallExpr(Expr *fnExpr, SourceLoc dotLoc, Expr *baseExpr, + Type ty = Type()) + : SelfApplyExpr(ExprKind::DotSyntaxCall, fnExpr, baseExpr, ty), + DotLoc(dotLoc) { setImplicit(DotLoc.isInvalid()); } +public: + static DotSyntaxCallExpr *create(ASTContext &ctx, Expr *fnExpr, + SourceLoc dotLoc, Expr *baseExpr, + Type ty = Type()); + SourceLoc getDotLoc() const { return DotLoc; } SourceLoc getLoc() const; @@ -4751,9 +4903,12 @@ class DotSyntaxCallExpr : public SelfApplyExpr { /// actual reference to function which returns the constructor is modeled /// as a DeclRefExpr. class ConstructorRefCallExpr : public SelfApplyExpr { + ConstructorRefCallExpr(Expr *fnExpr, Expr *baseExpr, Type ty = Type()) + : SelfApplyExpr(ExprKind::ConstructorRefCall, fnExpr, baseExpr, ty) {} + public: - ConstructorRefCallExpr(Expr *FnExpr, Expr *BaseExpr, Type Ty = Type()) - : SelfApplyExpr(ExprKind::ConstructorRefCall, FnExpr, BaseExpr, Ty) {} + static ConstructorRefCallExpr *create(ASTContext &ctx, Expr *fnExpr, + Expr *baseExpr, Type ty = Type()); SourceLoc getLoc() const { return getFn()->getLoc(); } SourceLoc getStartLoc() const { return getBase()->getStartLoc(); } @@ -5404,6 +5559,7 @@ class KeyPathExpr : public Expr { /// - a resolved ValueDecl, referring to /// - a subscript index expression, which may or may not be resolved /// - an optional chaining, forcing, or wrapping component + /// - a code completion token class Component { public: enum class Kind: unsigned { @@ -5418,6 +5574,7 @@ class KeyPathExpr : public Expr { Identity, TupleElement, DictionaryKey, + CodeCompletion, }; private: @@ -5443,7 +5600,8 @@ class KeyPathExpr : public Expr { Kind KindValue; Type ComponentType; SourceLoc Loc; - + + // Private constructor for subscript component. explicit Component(ASTContext *ctxForCopyingLabels, DeclNameOrRef decl, Expr *indexExpr, @@ -5453,27 +5611,31 @@ class KeyPathExpr : public Expr { Type type, SourceLoc loc); - // Private constructor for tuple element kind - Component(unsigned tupleIndex, Type elementType, SourceLoc loc) - : Component(nullptr, {}, nullptr, {}, {}, Kind::TupleElement, - elementType, loc) { + // Private constructor for property or #keyPath dictionary key. + explicit Component(DeclNameOrRef decl, Kind kind, Type type, SourceLoc loc) + : Component(kind, type, loc) { + assert(kind == Kind::Property || kind == Kind::UnresolvedProperty || + kind == Kind::DictionaryKey); + Decl = decl; + } + + // Private constructor for tuple element kind. + explicit Component(unsigned tupleIndex, Type elementType, SourceLoc loc) + : Component(Kind::TupleElement, elementType, loc) { TupleIndex = tupleIndex; } - + + // Private constructor for basic components with no additional information. + explicit Component(Kind kind, Type type, SourceLoc loc) + : Decl(), KindValue(kind), ComponentType(type), Loc(loc) {} + public: - Component() - : Component(nullptr, {}, nullptr, {}, {}, Kind::Invalid, - Type(), SourceLoc()) - {} - + Component() : Component(Kind::Invalid, Type(), SourceLoc()) {} + /// Create an unresolved component for a property. static Component forUnresolvedProperty(DeclNameRef UnresolvedName, SourceLoc Loc) { - return Component(nullptr, - UnresolvedName, nullptr, {}, {}, - Kind::UnresolvedProperty, - Type(), - Loc); + return Component(UnresolvedName, Kind::UnresolvedProperty, Type(), Loc); } /// Create an unresolved component for a subscript. @@ -5503,38 +5665,26 @@ class KeyPathExpr : public Expr { /// Create an unresolved optional force `!` component. static Component forUnresolvedOptionalForce(SourceLoc BangLoc) { - return Component(nullptr, {}, nullptr, {}, {}, - Kind::OptionalForce, - Type(), - BangLoc); + return Component(Kind::OptionalForce, Type(), BangLoc); } /// Create an unresolved optional chain `?` component. static Component forUnresolvedOptionalChain(SourceLoc QuestionLoc) { - return Component(nullptr, {}, nullptr, {}, {}, - Kind::OptionalChain, - Type(), - QuestionLoc); + return Component(Kind::OptionalChain, Type(), QuestionLoc); } /// Create a component for a property. static Component forProperty(ConcreteDeclRef property, Type propertyType, SourceLoc loc) { - return Component(nullptr, property, nullptr, {}, {}, - Kind::Property, - propertyType, - loc); + return Component(property, Kind::Property, propertyType, loc); } /// Create a component for a dictionary key (#keyPath only). static Component forDictionaryKey(DeclNameRef UnresolvedName, Type valueType, SourceLoc loc) { - return Component(nullptr, UnresolvedName, nullptr, {}, {}, - Kind::DictionaryKey, - valueType, - loc); + return Component(UnresolvedName, Kind::DictionaryKey, valueType, loc); } /// Create a component for a subscript. @@ -5560,32 +5710,24 @@ class KeyPathExpr : public Expr { /// Create an optional-forcing `!` component. static Component forOptionalForce(Type forcedType, SourceLoc bangLoc) { - return Component(nullptr, {}, nullptr, {}, {}, - Kind::OptionalForce, forcedType, - bangLoc); + return Component(Kind::OptionalForce, forcedType, bangLoc); } /// Create an optional-chaining `?` component. static Component forOptionalChain(Type unwrappedType, SourceLoc questionLoc) { - return Component(nullptr, {}, nullptr, {}, {}, - Kind::OptionalChain, unwrappedType, - questionLoc); + return Component(Kind::OptionalChain, unwrappedType, questionLoc); } /// Create an optional-wrapping component. This doesn't have a surface /// syntax but may appear when the non-optional result of an optional chain /// is implicitly wrapped. static Component forOptionalWrap(Type wrappedType) { - return Component(nullptr, {}, nullptr, {}, {}, - Kind::OptionalWrap, wrappedType, - SourceLoc()); + return Component(Kind::OptionalWrap, wrappedType, SourceLoc()); } static Component forIdentity(SourceLoc selfLoc) { - return Component(nullptr, {}, nullptr, {}, {}, - Kind::Identity, Type(), - selfLoc); + return Component(Kind::Identity, Type(), selfLoc); } static Component forTupleElement(unsigned fieldNumber, @@ -5593,8 +5735,11 @@ class KeyPathExpr : public Expr { SourceLoc loc) { return Component(fieldNumber, elementType, loc); } - - + + static Component forCodeCompletion(SourceLoc Loc) { + return Component(Kind::CodeCompletion, Type(), Loc); + } + SourceLoc getLoc() const { return Loc; } @@ -5632,6 +5777,7 @@ class KeyPathExpr : public Expr { case Kind::UnresolvedSubscript: case Kind::UnresolvedProperty: case Kind::Invalid: + case Kind::CodeCompletion: return false; } llvm_unreachable("unhandled kind"); @@ -5652,6 +5798,7 @@ class KeyPathExpr : public Expr { case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: + case Kind::CodeCompletion: return nullptr; } llvm_unreachable("unhandled kind"); @@ -5672,6 +5819,7 @@ class KeyPathExpr : public Expr { case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: + case Kind::CodeCompletion: llvm_unreachable("no subscript labels for this kind"); } llvm_unreachable("unhandled kind"); @@ -5695,6 +5843,7 @@ class KeyPathExpr : public Expr { case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: + case Kind::CodeCompletion: return {}; } llvm_unreachable("unhandled kind"); @@ -5718,6 +5867,7 @@ class KeyPathExpr : public Expr { case Kind::Property: case Kind::Identity: case Kind::TupleElement: + case Kind::CodeCompletion: llvm_unreachable("no unresolved name for this kind"); } llvm_unreachable("unhandled kind"); @@ -5738,6 +5888,7 @@ class KeyPathExpr : public Expr { case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: + case Kind::CodeCompletion: return false; } llvm_unreachable("unhandled kind"); @@ -5758,6 +5909,7 @@ class KeyPathExpr : public Expr { case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: + case Kind::CodeCompletion: llvm_unreachable("no decl ref for this kind"); } llvm_unreachable("unhandled kind"); @@ -5778,6 +5930,7 @@ class KeyPathExpr : public Expr { case Kind::Property: case Kind::Subscript: case Kind::DictionaryKey: + case Kind::CodeCompletion: llvm_unreachable("no field number for this kind"); } llvm_unreachable("unhandled kind"); @@ -5833,7 +5986,10 @@ class KeyPathExpr : public Expr { /// Indicates if the key path expression is composed by a single invalid /// component. e.g. missing component `\Root` bool hasSingleInvalidComponent() const { - return Components.size() == 1 && !Components.front().isValid(); + if (ParsedRoot && ParsedRoot->getKind() == ExprKind::Type) { + return Components.size() == 1 && !Components.front().isValid(); + } + return false; } /// Retrieve the string literal expression, which will be \c NULL prior to diff --git a/include/swift/AST/FileUnit.h b/include/swift/AST/FileUnit.h index 991c077b2d9ad..46eade5882941 100644 --- a/include/swift/AST/FileUnit.h +++ b/include/swift/AST/FileUnit.h @@ -18,8 +18,6 @@ #include "swift/Basic/BasicSourceInfo.h" namespace swift { -static inline unsigned alignOfFileUnit(); - /// A container for module-scope declarations that itself provides a scope; the /// smallest unit of code organization. /// @@ -28,7 +26,7 @@ static inline unsigned alignOfFileUnit(); /// file. A module can contain several file-units. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnon-virtual-dtor" -class FileUnit : public DeclContext { +class FileUnit : public DeclContext, public ASTAllocated { #pragma clang diagnostic pop virtual void anchor(); @@ -273,7 +271,7 @@ class FileUnit : public DeclContext { return dyn_cast_or_null(getMainDecl()); } bool hasMainDecl() const { return getMainDecl(); } - virtual Decl *getMainDecl() const { return nullptr; } + virtual ValueDecl *getMainDecl() const { return nullptr; } FuncDecl *getMainFunc() const { return dyn_cast_or_null(getMainDecl()); } @@ -313,23 +311,10 @@ class FileUnit : public DeclContext { return DC->getContextKind() == DeclContextKind::FileUnit; } -private: - // Make placement new and vanilla new/delete illegal for FileUnits. - void *operator new(size_t Bytes) throw() = delete; - void *operator new(size_t Bytes, void *Mem) throw() = delete; - void operator delete(void *Data) throw() = delete; - -public: - // Only allow allocation of FileUnits using the allocator in ASTContext - // or by doing a placement new. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignOfFileUnit()); + using ASTAllocated::operator new; + using ASTAllocated::operator delete; }; -static inline unsigned alignOfFileUnit() { - return alignof(FileUnit&); -} - /// This represents the compiler's implicitly generated declarations in the /// Builtin module. class BuiltinUnit final : public FileUnit { diff --git a/include/swift/AST/ForeignInfo.h b/include/swift/AST/ForeignInfo.h index 40ed8757c111d..ebde47d84e86a 100644 --- a/include/swift/AST/ForeignInfo.h +++ b/include/swift/AST/ForeignInfo.h @@ -26,9 +26,9 @@ namespace swift { struct ForeignInfo { - ImportAsMemberStatus Self; - Optional Error; - Optional Async; + ImportAsMemberStatus self; + Optional error; + Optional async; }; } // end namespace swift diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index 3c269644ea980..3bb903fda2508 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -31,7 +31,6 @@ namespace swift { class ArchetypeType; -class GenericSignatureBuilder; class ASTContext; class GenericTypeParamType; class SILModule; @@ -60,7 +59,6 @@ class QueryInterfaceTypeSubstitutions { class alignas(1 << DeclAlignInBits) GenericEnvironment final : private llvm::TrailingObjects { GenericSignature Signature = GenericSignature(); - GenericSignatureBuilder *Builder = nullptr; friend TrailingObjects; @@ -76,16 +74,18 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final /// generic signature. ArrayRef getContextTypes() const; - GenericEnvironment(GenericSignature signature, - GenericSignatureBuilder *builder); + explicit GenericEnvironment(GenericSignature signature); friend ArchetypeType; - friend GenericSignatureBuilder; - - GenericSignatureBuilder *getGenericSignatureBuilder() const { return Builder; } - friend QueryInterfaceTypeSubstitutions; + Type getOrCreateArchetypeFromInterfaceType(Type depType); + + /// Retrieve the mapping for the given generic parameter, if present. + /// + /// This is only useful when lazily populating a generic environment. + Optional getMappingIfPresent(GenericParamKey key) const; + public: GenericSignature getGenericSignature() const { return Signature; @@ -96,18 +96,12 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final /// Create a new, "incomplete" generic environment that will be populated /// by calls to \c addMapping(). static - GenericEnvironment *getIncomplete(GenericSignature signature, - GenericSignatureBuilder *builder); + GenericEnvironment *getIncomplete(GenericSignature signature); /// Add a mapping of a generic parameter to a specific type (which may be /// an archetype) void addMapping(GenericParamKey key, Type contextType); - /// Retrieve the mapping for the given generic parameter, if present. - /// - /// This is only useful when lazily populating a generic environment. - Optional getMappingIfPresent(GenericParamKey key) const; - /// Make vanilla new/delete illegal. void *operator new(size_t Bytes) = delete; void operator delete(void *Data) = delete; @@ -151,6 +145,8 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final mapConformanceRefIntoContext(Type conformingType, ProtocolConformanceRef conformance) const; + /// Returns a substitution map that sends every generic parameter to its + /// corresponding archetype in this generic environment. SubstitutionMap getForwardingSubstitutionMap() const; void dump(raw_ostream &os) const; diff --git a/include/swift/AST/GenericParamList.h b/include/swift/AST/GenericParamList.h index 4ab8c1a67c1ea..ac9730cdeb07a 100644 --- a/include/swift/AST/GenericParamList.h +++ b/include/swift/AST/GenericParamList.h @@ -81,8 +81,6 @@ class RequirementRepr { : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), FirstType(FirstType), SecondLayout(SecondLayout) { } - void printImpl(ASTPrinter &OS) const; - public: /// Construct a new type-constraint requirement. /// @@ -339,14 +337,15 @@ class GenericParamList final : /// to the given DeclContext. GenericParamList *clone(DeclContext *dc) const; - void print(raw_ostream &OS) const; - SWIFT_DEBUG_DUMP; - bool walk(ASTWalker &walker); /// Finds a generic parameter declaration by name. This should only /// be used from the SIL parser. GenericTypeParamDecl *lookUpGenericParam(Identifier name) const; + + SWIFT_DEBUG_DUMP; + void print(raw_ostream &OS, const PrintOptions &PO = PrintOptions()) const; + void print(ASTPrinter &Printer, const PrintOptions &PO) const; }; /// A trailing where clause. diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index d4e7495739aff..ee9e0fcb37c34 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -36,6 +36,10 @@ class ProtocolType; class SubstitutionMap; class GenericEnvironment; +namespace rewriting { + class RequirementMachine; +} + /// An access path used to find a particular protocol conformance within /// a generic signature. /// @@ -80,14 +84,19 @@ class ConformanceAccessPath { ConformanceAccessPath(ArrayRef path) : path(path) {} friend class GenericSignatureImpl; + friend class GenericSignatureBuilder; + friend class rewriting::RequirementMachine; public: typedef const Entry *const_iterator; typedef const_iterator iterator; + unsigned size() const { return path.size(); } const_iterator begin() const { return path.begin(); } const_iterator end() const { return path.end(); } + const Entry &back() const { return path.back(); } + void print(raw_ostream &OS) const; SWIFT_DEBUG_DUMP; @@ -131,9 +140,6 @@ class GenericSignature { explicit operator bool() const { return Ptr != 0; } - /// Whether the given set of requirements involves a type variable. - static bool hasTypeVariable(ArrayRef requirements); - friend llvm::hash_code hash_value(GenericSignature sig) { using llvm::hash_value; return hash_value(sig.getPointer()); @@ -159,11 +165,41 @@ class GenericSignature { public: using RequiredProtocols = SmallVector; + /// Stores a set of requirements on a type parameter. Used by + /// GenericEnvironment for building archetypes. + struct LocalRequirements { + Type anchor; + + Type concreteType; + Type superclass; + + RequiredProtocols protos; + LayoutConstraint layout; + }; + private: // Direct comparison is disabled for generic signatures. Canonicalize them // first, or use isEqual. void operator==(GenericSignature T) const = delete; void operator!=(GenericSignature T) const = delete; + +public: + /// Retrieve the generic parameters. + TypeArrayView getGenericParams() const; + + /// Retrieve the innermost generic parameters. + /// + /// Given a generic signature for a nested generic type, produce an + /// array of the generic parameters for the innermost generic type. + TypeArrayView getInnermostGenericParams() const; + + /// Retrieve the requirements. + ArrayRef getRequirements() const; + + /// Returns the generic environment that provides fresh contextual types + /// (archetypes) that correspond to the interface types in this generic + /// signature. + GenericEnvironment *getGenericEnvironment() const; }; /// A reference to a canonical generic signature. @@ -237,23 +273,6 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final friend class ArchetypeType; public: - /// Retrieve the generic parameters. - TypeArrayView getGenericParams() const { - return TypeArrayView( - {getTrailingObjects(), NumGenericParams}); - } - - /// Retrieve the innermost generic parameters. - /// - /// Given a generic signature for a nested generic type, produce an - /// array of the generic parameters for the innermost generic type. - TypeArrayView getInnermostGenericParams() const; - - /// Retrieve the requirements. - ArrayRef getRequirements() const { - return {getTrailingObjects(), NumRequirements}; - } - /// Only allow allocation by doing a placement new. void *operator new(size_t Bytes, void *Mem) { assert(Mem); @@ -294,16 +313,15 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final ASTContext &getASTContext() const; - /// Returns the canonical generic signature. The result is cached. - CanGenericSignature getCanonicalSignature() const; - /// Retrieve the generic signature builder for the given generic signature. GenericSignatureBuilder *getGenericSignatureBuilder() const; - /// Returns the generic environment that provides fresh contextual types - /// (archetypes) that correspond to the interface types in this generic - /// signature. - GenericEnvironment *getGenericEnvironment() const; + /// Retrieve the requirement machine for the given generic signature. + rewriting::RequirementMachine *getRequirementMachine() const; + + /// Collects a set of requirements on a type parameter. Used by + /// GenericEnvironment for building archetypes. + GenericSignature::LocalRequirements getLocalRequirements(Type depType) const; /// Uniquing for the ASTContext. void Profile(llvm::FoldingSetNodeID &ID) const { @@ -349,7 +367,8 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final /// T: Foo or T == U (etc.) with the information it knows. This includes /// checking against global state, if any/all of the types in the requirement /// are concrete, not type parameters. - bool isRequirementSatisfied(Requirement requirement) const; + bool isRequirementSatisfied( + Requirement requirement, bool allowMissing = false) const; /// Return the requirements of this generic signature that are not also /// satisfied by \c otherSig. @@ -382,6 +401,9 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final ConformanceAccessPath getConformanceAccessPath(Type type, ProtocolDecl *protocol) const; + /// Lookup a nested type with the given name within this type parameter. + TypeDecl *lookupNestedType(Type type, Identifier name) const; + /// Get the ordinal of a generic parameter in this generic signature. /// /// For example, if you have a generic signature for a nested context like: @@ -400,9 +422,6 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final /// generic parameter types by their sugared form. Type getSugaredType(Type type) const; - /// Whether this generic signature involves a type variable. - bool hasTypeVariable() const; - static void Profile(llvm::FoldingSetNodeID &ID, TypeArrayView genericParams, ArrayRef requirements); @@ -411,6 +430,35 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final void print(ASTPrinter &Printer, PrintOptions Opts = PrintOptions()) const; SWIFT_DEBUG_DUMP; std::string getAsString() const; + +private: + friend GenericSignature; + friend CanGenericSignature; + + /// Retrieve the generic parameters. + TypeArrayView getGenericParams() const { + return TypeArrayView( + {getTrailingObjects(), NumGenericParams}); + } + + /// Retrieve the innermost generic parameters. + /// + /// Given a generic signature for a nested generic type, produce an + /// array of the generic parameters for the innermost generic type. + TypeArrayView getInnermostGenericParams() const; + + /// Retrieve the requirements. + ArrayRef getRequirements() const { + return {getTrailingObjects(), NumRequirements}; + } + + /// Returns the canonical generic signature. The result is cached. + CanGenericSignature getCanonicalSignature() const; + + /// Returns the generic environment that provides fresh contextual types + /// (archetypes) that correspond to the interface types in this generic + /// signature. + GenericEnvironment *getGenericEnvironment() const; }; void simple_display(raw_ostream &out, GenericSignature sig); @@ -424,6 +472,11 @@ inline bool CanGenericSignature::isActuallyCanonicalOrNull() const { getPointer()->isCanonical(); } +int compareAssociatedTypes(AssociatedTypeDecl *assocType1, + AssociatedTypeDecl *assocType2); + +int compareDependentTypes(Type type1, Type type2); + } // end namespace swift namespace llvm { diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index 5f6875cd2ae06..2984507bc71d4 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -31,6 +31,7 @@ #include "swift/AST/TypeRepr.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ilist.h" #include "llvm/ADT/PointerUnion.h" @@ -97,10 +98,6 @@ class GenericSignatureBuilder { using RequirementRHS = llvm::PointerUnion; - /// The location of a requirement as written somewhere in the source. - typedef llvm::PointerUnion - WrittenRequirementLoc; - class RequirementSource; class FloatingRequirementSource; @@ -231,24 +228,15 @@ class GenericSignatureBuilder { /// Lookup a nested type with the given name within this equivalence /// class. - /// - /// \param otherConcreteTypes If non-null, will be filled in the all of the - /// concrete types we found (other than the result) with the same name. TypeDecl *lookupNestedType( GenericSignatureBuilder &builder, - Identifier name, - SmallVectorImpl *otherConcreteTypes = nullptr); + Identifier name); /// Retrieve the "anchor" type that canonically describes this equivalence /// class, for use in the canonical type. Type getAnchor(GenericSignatureBuilder &builder, TypeArrayView genericParams); - /// Retrieve (or build) the contextual type corresponding to - /// this equivalence class within the given generic environment. - Type getTypeInContext(GenericSignatureBuilder &builder, - GenericEnvironment *genericEnv); - /// Dump a debugging representation of this equivalence class, void dump(llvm::raw_ostream &out, GenericSignatureBuilder *builder = nullptr) const; @@ -271,16 +259,12 @@ class GenericSignatureBuilder { unsigned numConformancesPresent; CanType superclassPresent; CanType concreteTypePresent; - llvm::TinyPtrVector types; + TypeDecl *type = nullptr; }; /// Cached nested-type information, which contains the best declaration /// for a given name. llvm::SmallDenseMap nestedTypeNameCache; - - /// Cached access paths. - llvm::SmallDenseMap - conformanceAccessPathCache; }; friend class RequirementSource; @@ -345,10 +329,8 @@ class GenericSignatureBuilder { UnresolvedHandlingKind unresolvedHandling); /// Add any conditional requirements from the given conformance. - /// - /// \returns \c true if an error occurred, \c false if not. - bool addConditionalRequirements(ProtocolConformanceRef conformance, - ModuleDecl *inferForModule, SourceLoc loc); + void addConditionalRequirements(ProtocolConformanceRef conformance, + ModuleDecl *inferForModule); /// Resolve the conformance of the given type to the given protocol when the /// potential archetype is known to be equivalent to a concrete type. @@ -633,8 +615,7 @@ class GenericSignatureBuilder { /// generic signature builder no longer has valid state. GenericSignature computeGenericSignature( bool allowConcreteGenericParams = false, - const ProtocolDecl *requirementSignatureSelfProto = nullptr, - bool rebuildingWithoutRedundantConformances = false) &&; + const ProtocolDecl *requirementSignatureSelfProto = nullptr) &&; /// Compute the requirement signature for the given protocol. static GenericSignature computeRequirementSignature(ProtocolDecl *proto); @@ -658,11 +639,48 @@ class GenericSignatureBuilder { bool isRedundantExplicitRequirement(const ExplicitRequirement &req) const; private: - void computeRedundantRequirements(const ProtocolDecl *requirementSignatureSelfProto); + using GetKindAndRHS = llvm::function_ref()>; + void getBaseRequirements( + GetKindAndRHS getKindAndRHS, + const RequirementSource *source, + const ProtocolDecl *requirementSignatureSelfProto, + SmallVectorImpl &result); + + /// Determine if an explicit requirement can be derived from the + /// requirement given by \p otherSource and \p otherRHS, using the + /// knowledge of any existing redundant requirements discovered so far. + Optional + isValidRequirementDerivationPath( + llvm::SmallDenseSet &visited, + RequirementKind otherKind, + const RequirementSource *otherSource, + RequirementRHS otherRHS, + const ProtocolDecl *requirementSignatureSelfProto); + + /// Determine if the explicit requirement \p req can be derived from any + /// of the constraints in \p constraints, using the knowledge of any + /// existing redundant requirements discovered so far. + /// + /// Use \p filter to screen out less-specific and conflicting constraints + /// if the requirement is a superclass, concrete type or layout requirement. + template + void checkIfRequirementCanBeDerived( + const ExplicitRequirement &req, + const std::vector> &constraints, + const ProtocolDecl *requirementSignatureSelfProto, + Filter filter); + + void computeRedundantRequirements( + const ProtocolDecl *requirementSignatureSelfProto); - void diagnoseRedundantRequirements() const; + void diagnoseProtocolRefinement( + const ProtocolDecl *requirementSignatureSelfProto); - void diagnoseConflictingConcreteTypeRequirements() const; + void diagnoseRedundantRequirements( + bool onlyDiagnoseExplicitConformancesImpliedByConcrete=false) const; + + void diagnoseConflictingConcreteTypeRequirements( + const ProtocolDecl *requirementSignatureSelfProto); /// Describes the relationship between a given constraint and /// the canonical constraint of the equivalence class. @@ -711,23 +729,6 @@ class GenericSignatureBuilder { TypeArrayView genericParams, EquivalenceClass *equivClass); - /// Check the superclass constraints within the equivalence - /// class of the given potential archetype. - void checkSuperclassConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass); - - /// Check conformance constraints within the equivalence class of the - /// given potential archetype. - void checkConformanceConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass); - - /// Check layout constraints within the equivalence class of the given - /// potential archetype. - void checkLayoutConstraints(TypeArrayView genericParams, - EquivalenceClass *equivClass); - /// Check same-type constraints within the equivalence class of the /// given potential archetype. void checkSameTypeConstraints( @@ -786,6 +787,22 @@ class GenericSignatureBuilder { Type getCanonicalTypeInContext(Type type, TypeArrayView genericParams); + /// Retrieve the conformance access path used to extract the conformance of + /// interface \c type to the given \c protocol. + /// + /// \param type The interface type whose conformance access path is to be + /// queried. + /// \param protocol A protocol to which \c type conforms. + /// + /// \returns the conformance access path that starts at a requirement of + /// this generic signature and ends at the conformance that makes \c type + /// conform to \c protocol. + /// + /// \seealso ConformanceAccessPath + ConformanceAccessPath getConformanceAccessPath(Type type, + ProtocolDecl *protocol, + GenericSignature sig); + /// Verify the correctness of the given generic signature. /// /// This routine will test that the given generic signature is both minimal @@ -813,7 +830,7 @@ class GenericSignatureBuilder { class GenericSignatureBuilder::RequirementSource final : public llvm::FoldingSetNode, private llvm::TrailingObjects { + SourceLoc> { friend class FloatingRequirementSource; friend class GenericSignature; @@ -915,7 +932,7 @@ class GenericSignatureBuilder::RequirementSource final const StorageKind storageKind; /// Whether there is a trailing written requirement location. - const bool hasTrailingWrittenRequirementLoc; + const bool hasTrailingSourceLoc; private: /// The actual storage, described by \c storageKind. @@ -955,8 +972,8 @@ class GenericSignatureBuilder::RequirementSource final } /// The trailing written requirement location, if there is one. - size_t numTrailingObjects(OverloadToken) const { - return hasTrailingWrittenRequirementLoc ? 1 : 0; + size_t numTrailingObjects(OverloadToken) const { + return hasTrailingSourceLoc ? 1 : 0; } #ifndef NDEBUG @@ -1007,9 +1024,9 @@ class GenericSignatureBuilder::RequirementSource final RequirementSource(Kind kind, Type rootType, ProtocolDecl *protocol, - WrittenRequirementLoc writtenReqLoc) + SourceLoc writtenReqLoc) : kind(kind), storageKind(StorageKind::StoredType), - hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()), + hasTrailingSourceLoc(writtenReqLoc.isValid()), parent(nullptr) { assert(isAcceptableStorageKind(kind, storageKind) && "RequirementSource kind/storageKind mismatch"); @@ -1017,15 +1034,15 @@ class GenericSignatureBuilder::RequirementSource final storage.type = rootType.getPointer(); if (kind == RequirementSignatureSelf) getTrailingObjects()[0] = protocol; - if (hasTrailingWrittenRequirementLoc) - getTrailingObjects()[0] = writtenReqLoc; + if (hasTrailingSourceLoc) + getTrailingObjects()[0] = writtenReqLoc; } RequirementSource(Kind kind, const RequirementSource *parent, Type type, ProtocolDecl *protocol, - WrittenRequirementLoc writtenReqLoc) + SourceLoc writtenReqLoc) : kind(kind), storageKind(StorageKind::StoredType), - hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()), + hasTrailingSourceLoc(writtenReqLoc.isValid()), parent(parent) { assert((static_cast(parent) != isRootKind(kind)) && "Root RequirementSource should not have parent (or vice versa)"); @@ -1035,14 +1052,14 @@ class GenericSignatureBuilder::RequirementSource final storage.type = type.getPointer(); if (isProtocolRequirement()) getTrailingObjects()[0] = protocol; - if (hasTrailingWrittenRequirementLoc) - getTrailingObjects()[0] = writtenReqLoc; + if (hasTrailingSourceLoc) + getTrailingObjects()[0] = writtenReqLoc; } RequirementSource(Kind kind, const RequirementSource *parent, ProtocolConformanceRef conformance) : kind(kind), storageKind(StorageKind::ProtocolConformance), - hasTrailingWrittenRequirementLoc(false), parent(parent) { + hasTrailingSourceLoc(false), parent(parent) { assert((static_cast(parent) != isRootKind(kind)) && "Root RequirementSource should not have parent (or vice versa)"); assert(isAcceptableStorageKind(kind, storageKind) && @@ -1054,7 +1071,7 @@ class GenericSignatureBuilder::RequirementSource final RequirementSource(Kind kind, const RequirementSource *parent, AssociatedTypeDecl *assocType) : kind(kind), storageKind(StorageKind::AssociatedTypeDecl), - hasTrailingWrittenRequirementLoc(false), parent(parent) { + hasTrailingSourceLoc(false), parent(parent) { assert((static_cast(parent) != isRootKind(kind)) && "Root RequirementSource should not have parent (or vice versa)"); assert(isAcceptableStorageKind(kind, storageKind) && @@ -1065,7 +1082,7 @@ class GenericSignatureBuilder::RequirementSource final RequirementSource(Kind kind, const RequirementSource *parent) : kind(kind), storageKind(StorageKind::None), - hasTrailingWrittenRequirementLoc(false), parent(parent) { + hasTrailingSourceLoc(false), parent(parent) { assert((static_cast(parent) != isRootKind(kind)) && "Root RequirementSource should not have parent (or vice versa)"); assert(isAcceptableStorageKind(kind, storageKind) && @@ -1075,7 +1092,7 @@ class GenericSignatureBuilder::RequirementSource final RequirementSource(Kind kind, const RequirementSource *parent, Type newType) : kind(kind), storageKind(StorageKind::StoredType), - hasTrailingWrittenRequirementLoc(false), parent(parent) { + hasTrailingSourceLoc(false), parent(parent) { assert((static_cast(parent) != isRootKind(kind)) && "Root RequirementSource should not have parent (or vice versa)"); assert(isAcceptableStorageKind(kind, storageKind) && @@ -1092,14 +1109,14 @@ class GenericSignatureBuilder::RequirementSource final /// stated in an 'inheritance' or 'where' clause. static const RequirementSource *forExplicit(GenericSignatureBuilder &builder, Type rootType, - WrittenRequirementLoc writtenLoc); + SourceLoc writtenLoc); /// Retrieve a requirement source representing a requirement that is /// inferred from some part of a generic declaration's signature, e.g., the /// parameter or result type of a generic function. static const RequirementSource *forInferred(GenericSignatureBuilder &builder, Type rootType, - const TypeRepr *typeRepr); + SourceLoc writtenLoc); /// Retrieve a requirement source representing the requirement signature /// computation for a protocol. @@ -1121,8 +1138,8 @@ class GenericSignatureBuilder::RequirementSource final Type dependentType, ProtocolDecl *protocol, bool inferred, - WrittenRequirementLoc writtenLoc = - WrittenRequirementLoc()) const; + SourceLoc writtenLoc = + SourceLoc()) const; public: /// A requirement source that describes a conformance requirement resolved /// via a superclass requirement. @@ -1230,8 +1247,7 @@ class GenericSignatureBuilder::RequirementSource final /// requirement redundant, because without said original requirement, the /// derived requirement ceases to hold. bool isSelfDerivedSource(GenericSignatureBuilder &builder, - Type type, - bool &derivedViaConcrete) const; + Type type) const; /// For a requirement source that describes the requirement \c type:proto, /// retrieve the minimal subpath of this requirement source that will @@ -1243,8 +1259,7 @@ class GenericSignatureBuilder::RequirementSource final const RequirementSource *getMinimalConformanceSource( GenericSignatureBuilder &builder, Type type, - ProtocolDecl *proto, - bool &derivedViaConcrete) const; + ProtocolDecl *proto) const; /// Retrieve a source location that corresponds to the requirement. SourceLoc getLoc() const; @@ -1257,20 +1272,9 @@ class GenericSignatureBuilder::RequirementSource final int compare(const RequirementSource *other) const; /// Retrieve the written requirement location, if there is one. - WrittenRequirementLoc getWrittenRequirementLoc() const { - if (!hasTrailingWrittenRequirementLoc) return WrittenRequirementLoc(); - return getTrailingObjects()[0]; - } - - /// Retrieve the type representation for this requirement, if there is one. - const TypeRepr *getTypeRepr() const { - return getWrittenRequirementLoc().dyn_cast(); - } - - /// Retrieve the requirement representation for this requirement, if there is - /// one. - const RequirementRepr *getRequirementRepr() const { - return getWrittenRequirementLoc().dyn_cast(); + SourceLoc getSourceLoc() const { + if (!hasTrailingSourceLoc) return SourceLoc(); + return getTrailingObjects()[0]; } /// Retrieve the type stored in this requirement. @@ -1324,39 +1328,35 @@ class GenericSignatureBuilder::RequirementSource final /// The root will be supplied as soon as the appropriate dependent type is /// resolved. class GenericSignatureBuilder::FloatingRequirementSource { - enum Kind { + enum Kind : uint8_t { /// A fully-resolved requirement source, which does not need a root. Resolved, - /// An explicit requirement source lacking a root. + /// An explicit requirement in a generic signature. Explicit, - /// An inferred requirement source lacking a root. + /// A requirement inferred from a concrete type application in a + /// generic signature. Inferred, - /// A requirement source augmented by an abstract protocol requirement - AbstractProtocol, + /// An explicit requirement written inside a protocol. + ProtocolRequirement, + /// A requirement inferred from a concrete type application inside a + /// protocol. + InferredProtocolRequirement, /// A requirement source for a nested-type-name match introduced by /// the given source. NestedTypeNameMatch, } kind; - using Storage = - llvm::PointerUnion; - - Storage storage; + const RequirementSource *source; + SourceLoc loc; // Additional storage for an abstract protocol requirement. union { - struct { - ProtocolDecl *protocol = nullptr; - WrittenRequirementLoc written; - bool inferred = false; - } protocolReq; - + ProtocolDecl *protocol = nullptr; Identifier nestedName; }; - FloatingRequirementSource(Kind kind, Storage storage) - : kind(kind), storage(storage) { } + FloatingRequirementSource(Kind kind, const RequirementSource *source) + : kind(kind), source(source) { } public: /// Implicit conversion from a resolved requirement source. @@ -1364,48 +1364,46 @@ class GenericSignatureBuilder::FloatingRequirementSource { : FloatingRequirementSource(Resolved, source) { } static FloatingRequirementSource forAbstract() { - return { Explicit, Storage() }; + return { Explicit, nullptr }; } - static FloatingRequirementSource forExplicit(const TypeRepr *typeRepr) { - return { Explicit, typeRepr }; - } - - static FloatingRequirementSource forExplicit( - const RequirementRepr *requirementRepr) { - return { Explicit, requirementRepr }; + static FloatingRequirementSource forExplicit(SourceLoc loc) { + FloatingRequirementSource result{ Explicit, nullptr }; + result.loc = loc; + return result; } - static FloatingRequirementSource forInferred(const TypeRepr *typeRepr) { - return { Inferred, typeRepr }; + static FloatingRequirementSource forInferred(SourceLoc loc) { + FloatingRequirementSource result{ Inferred, nullptr }; + result.loc = loc; + return result; } static FloatingRequirementSource viaProtocolRequirement( const RequirementSource *base, ProtocolDecl *inProtocol, bool inferred) { - FloatingRequirementSource result{ AbstractProtocol, base }; - result.protocolReq.protocol = inProtocol; - result.protocolReq.written = WrittenRequirementLoc(); - result.protocolReq.inferred = inferred; + auto kind = (inferred ? InferredProtocolRequirement : ProtocolRequirement); + FloatingRequirementSource result{ kind, base }; + result.protocol = inProtocol; return result; } static FloatingRequirementSource viaProtocolRequirement( const RequirementSource *base, ProtocolDecl *inProtocol, - WrittenRequirementLoc written, + SourceLoc written, bool inferred) { - FloatingRequirementSource result{ AbstractProtocol, base }; - result.protocolReq.protocol = inProtocol; - result.protocolReq.written = written; - result.protocolReq.inferred = inferred; + auto kind = (inferred ? InferredProtocolRequirement : ProtocolRequirement); + FloatingRequirementSource result{ kind, base }; + result.protocol = inProtocol; + result.loc = written; return result; } static FloatingRequirementSource forNestedTypeNameMatch( Identifier nestedName) { - FloatingRequirementSource result{ NestedTypeNameMatch, Storage() }; + FloatingRequirementSource result{ NestedTypeNameMatch, nullptr }; result.nestedName = nestedName; return result; }; @@ -1679,9 +1677,6 @@ inline bool isErrorResult(GenericSignatureBuilder::ConstraintResult result) { llvm_unreachable("unhandled result"); } -/// Canonical ordering for dependent types. -int compareDependentTypes(Type type1, Type type2); - template Type GenericSignatureBuilder::Constraint::getSubjectDependentType( TypeArrayView genericParams) const { diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index b446950b1cb97..c4d903f8029a6 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -204,6 +204,9 @@ class IRGenOptions { /// The libraries and frameworks specified on the command line. SmallVector LinkLibraries; + /// The public dependent libraries specified on the command line. + std::vector PublicLinkLibraries; + /// If non-empty, the (unmangled) name of a dummy symbol to emit that can be /// used to force-load this module. std::string ForceLoadSymbolName; @@ -250,9 +253,6 @@ class IRGenOptions { /// Whether we should run swift specific LLVM optimizations after IRGen. unsigned DisableSwiftSpecificLLVMOptzns : 1; - /// Whether we should run LLVM SLP vectorizer. - unsigned DisableLLVMSLPVectorizer : 1; - /// Special codegen for playgrounds. unsigned Playground : 1; @@ -323,9 +323,6 @@ class IRGenOptions { /// measurements on a non-clean build directory. unsigned UseIncrementalLLVMCodeGen : 1; - /// Enable use of the swiftcall calling convention. - unsigned UseSwiftCall : 1; - /// Enable the use of type layouts for value witness functions and use /// vw functions instead of outlined copy/destroy functions. unsigned UseTypeLayoutValueHandling : 1; @@ -346,6 +343,8 @@ class IRGenOptions { /// Whether to disable using mangled names for accessing concrete type metadata. unsigned DisableConcreteTypeMetadataMangledNameAccessors : 1; + unsigned EnableGlobalISel : 1; + /// The number of threads for multi-threaded code generation. unsigned NumThreads = 0; @@ -373,6 +372,8 @@ class IRGenOptions { /// Pull in runtime compatibility shim libraries by autolinking. Optional AutolinkRuntimeCompatibilityLibraryVersion; Optional AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion; + Optional + AutolinkRuntimeCompatibilityConcurrencyLibraryVersion; JITDebugArtifact DumpJIT = JITDebugArtifact::None; @@ -387,7 +388,7 @@ class IRGenOptions { DebugInfoFormat(IRGenDebugInfoFormat::None), DisableClangModuleSkeletonCUs(false), UseJIT(false), DisableLLVMOptzns(false), DisableSwiftSpecificLLVMOptzns(false), - DisableLLVMSLPVectorizer(false), Playground(false), + Playground(false), EmitStackPromotionChecks(false), FunctionSections(false), PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None), LLVMLTOKind(IRGenLLVMLTOKind::None), HasValueNamesSetting(false), @@ -396,10 +397,11 @@ class IRGenOptions { ForcePublicLinkage(false), LazyInitializeClassMetadata(false), LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false), PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true), - UseSwiftCall(false), UseTypeLayoutValueHandling(true), + UseTypeLayoutValueHandling(true), GenerateProfile(false), EnableDynamicReplacementChaining(false), DisableRoundTripDebugTypes(false), DisableDebuggerShadowCopies(false), - DisableConcreteTypeMetadataMangledNameAccessors(false), CmdArgs(), + DisableConcreteTypeMetadataMangledNameAccessors(false), + EnableGlobalISel(false), CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()), TypeInfoFilter(TypeInfoDumpFilter::All) {} diff --git a/include/swift/AST/ImportCache.h b/include/swift/AST/ImportCache.h index 5334b5ddddd36..665c98bcb6589 100644 --- a/include/swift/AST/ImportCache.h +++ b/include/swift/AST/ImportCache.h @@ -98,6 +98,8 @@ class ImportSet final : return {getTrailingObjects(), NumTopLevelImports + NumTransitiveImports}; } + + SWIFT_DEBUG_DUMP; }; class alignas(ImportedModule) ImportCache { diff --git a/include/swift/AST/Initializer.h b/include/swift/AST/Initializer.h index 18df9b744d56a..0656a65893293 100644 --- a/include/swift/AST/Initializer.h +++ b/include/swift/AST/Initializer.h @@ -209,15 +209,16 @@ class PropertyWrapperInitializer : public Initializer { }; private: - ParamDecl *param; + VarDecl *wrappedVar; Kind kind; public: - explicit PropertyWrapperInitializer(DeclContext *parent, ParamDecl *param, Kind kind) + explicit PropertyWrapperInitializer(DeclContext *parent, VarDecl *wrappedVar, + Kind kind) : Initializer(InitializerKind::PropertyWrapper, parent), - param(param), kind(kind) {} + wrappedVar(wrappedVar), kind(kind) {} - ParamDecl *getParam() const { return param; } + VarDecl *getWrappedVar() const { return wrappedVar; } Kind getKind() const { return kind; } diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 4f3e8188b21b6..bc2f166c26723 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -28,6 +28,7 @@ IDENTIFIER(allKeys) IDENTIFIER(alloc) IDENTIFIER(allocWithZone) IDENTIFIER(allZeros) +IDENTIFIER(ActorType) IDENTIFIER(Any) IDENTIFIER(ArrayLiteralElement) IDENTIFIER(atIndexedSubscript) @@ -56,6 +57,7 @@ IDENTIFIER(CoreFoundation) IDENTIFIER(count) IDENTIFIER(CVarArg) IDENTIFIER(Darwin) +IDENTIFIER_(Distributed) IDENTIFIER(dealloc) IDENTIFIER(debugDescription) IDENTIFIER(Decodable) @@ -64,6 +66,7 @@ IDENTIFIER(decodeIfPresent) IDENTIFIER(Decoder) IDENTIFIER(decoder) IDENTIFIER_(Differentiation) +IDENTIFIER_WITH_NAME(PatternMatchVar, "$match") IDENTIFIER(dynamicallyCall) IDENTIFIER(dynamicMember) IDENTIFIER(Element) @@ -142,6 +145,7 @@ IDENTIFIER(Type) IDENTIFIER(type) IDENTIFIER(typeMismatch) IDENTIFIER(underlyingError) +IDENTIFIER(unsafelyUnwrapped) IDENTIFIER(Value) IDENTIFIER(value) IDENTIFIER_WITH_NAME(value_, "_value") @@ -244,6 +248,23 @@ IDENTIFIER(pullback) IDENTIFIER(TangentVector) IDENTIFIER(zero) +// Distributed actors +IDENTIFIER(transport) +IDENTIFIER(using) +IDENTIFIER(actor) +IDENTIFIER(actorTransport) +IDENTIFIER(actorType) +IDENTIFIER(actorReady) +IDENTIFIER(assignIdentity) +IDENTIFIER(resignIdentity) +IDENTIFIER(resolve) +IDENTIFIER(id) +IDENTIFIER(identity) +IDENTIFIER(identifier) +IDENTIFIER(_distributedActorRemoteInitialize) +IDENTIFIER(_distributedActorDestroy) +IDENTIFIER(__isRemoteActor) + #undef IDENTIFIER #undef IDENTIFIER_ #undef IDENTIFIER_WITH_NAME diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 2880a26523571..922ba0e00d998 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -73,6 +73,7 @@ PROTOCOL(SIMDScalar) PROTOCOL(BinaryInteger) PROTOCOL(RangeReplaceableCollection) PROTOCOL(SerialExecutor) +PROTOCOL(GlobalActor) PROTOCOL_(BridgedNSError) PROTOCOL_(BridgedStoredNSError) @@ -93,6 +94,11 @@ PROTOCOL(StringInterpolationProtocol) PROTOCOL(AdditiveArithmetic) PROTOCOL(Differentiable) +// Distributed Actors +PROTOCOL(DistributedActor) +PROTOCOL(ActorIdentity) +PROTOCOL(ActorTransport) + PROTOCOL(AsyncSequence) PROTOCOL(AsyncIteratorProtocol) diff --git a/stdlib/public/Darwin/Foundation/NSDate.swift b/include/swift/AST/KnownSDKDecls.def similarity index 50% rename from stdlib/public/Darwin/Foundation/NSDate.swift rename to include/swift/AST/KnownSDKDecls.def index 2c4a0bef13a1d..724f5ca9d46d1 100644 --- a/stdlib/public/Darwin/Foundation/NSDate.swift +++ b/include/swift/AST/KnownSDKDecls.def @@ -1,4 +1,4 @@ -//===----------------------------------------------------------------------===// +//===--- KnownSDKDecls.def - Compiler decl metaprogramming --*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -9,20 +9,18 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +// +// This file defines macros used for macro-metaprogramming with compiler-known +// declarations, in modules other than the standard library. +// +//===----------------------------------------------------------------------===// + +#ifndef KNOWN_SDK_FUNC_DECL +# define KNOWN_SDK_FUNC_DECL(Module, Name, Id) +#endif -@_exported import Foundation // Clang module +KNOWN_SDK_FUNC_DECL(Distributed, MissingDistributedActorTransport, "_missingDistributedActorTransport") +KNOWN_SDK_FUNC_DECL(Distributed, IsRemoteDistributedActor, "__isRemoteActor") -extension NSDate : _CustomPlaygroundQuickLookable { - @nonobjc - var summary: String { - let df = DateFormatter() - df.dateStyle = .medium - df.timeStyle = .short - return df.string(from: self as Date) - } +#undef KNOWN_SDK_FUNC_DECL - @available(*, deprecated, message: "NSDate.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - return .text(summary) - } -} diff --git a/include/swift/AST/KnownSDKTypes.def b/include/swift/AST/KnownSDKTypes.def index 4ff96c5d253e1..3f2b1136bf5a1 100644 --- a/include/swift/AST/KnownSDKTypes.def +++ b/include/swift/AST/KnownSDKTypes.def @@ -43,4 +43,10 @@ KNOWN_SDK_TYPE_DECL(Concurrency, UnownedSerialExecutor, NominalTypeDecl, 0) KNOWN_SDK_TYPE_DECL(Concurrency, TaskLocal, ClassDecl, 1) +// Distributed actors +KNOWN_SDK_TYPE_DECL(Distributed, DistributedActor, ProtocolDecl, 0) +KNOWN_SDK_TYPE_DECL(Distributed, ActorTransport, ProtocolDecl, 0) +KNOWN_SDK_TYPE_DECL(Distributed, ActorIdentity, ProtocolDecl, 0) +KNOWN_SDK_TYPE_DECL(Distributed, AnyActorIdentity, StructDecl, 0) + #undef KNOWN_SDK_TYPE_DECL diff --git a/include/swift/AST/LayoutConstraint.h b/include/swift/AST/LayoutConstraint.h index 5d0ec66ce5f72..7745b59ef3c4b 100644 --- a/include/swift/AST/LayoutConstraint.h +++ b/include/swift/AST/LayoutConstraint.h @@ -17,6 +17,7 @@ #ifndef SWIFT_LAYOUT_CONSTRAINT_H #define SWIFT_LAYOUT_CONSTRAINT_H +#include "swift/AST/ASTAllocated.h" #include "swift/AST/LayoutConstraintKind.h" #include "swift/AST/PrintOptions.h" #include "swift/AST/TypeAlignments.h" @@ -29,12 +30,12 @@ namespace swift { -enum class AllocationArena; -class ASTContext; class ASTPrinter; /// This is a class representing the layout constraint. -class LayoutConstraintInfo : public llvm::FoldingSetNode { +class LayoutConstraintInfo + : public llvm::FoldingSetNode, + public ASTAllocated::type> { friend class LayoutConstraint; // Alignment of the layout in bytes. const unsigned Alignment : 16; @@ -208,16 +209,6 @@ class LayoutConstraintInfo : public llvm::FoldingSetNode { LayoutConstraintKind Kind, unsigned SizeInBits, unsigned Alignment); - private: - // Make vanilla new/delete illegal for LayoutConstraintInfo. - void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() = delete; - public: - // Only allow allocation of LayoutConstraintInfo using the allocator in - // ASTContext or by doing a placement new. - void *operator new(size_t bytes, const ASTContext &ctx, - AllocationArena arena, unsigned alignment = 8); - void *operator new(size_t Bytes, void *Mem) throw() { return Mem; } // Representation of the non-parameterized layouts. static LayoutConstraintInfo UnknownLayoutConstraintInfo; @@ -284,6 +275,10 @@ class LayoutConstraint { bool operator!=(LayoutConstraint rhs) const { return !(*this == rhs); } + + /// Defines a somewhat arbitrary linear order on layout constraints. + /// -1 if this < rhs, 0 if this == rhs, 1 if this > rhs. + int compare(LayoutConstraint rhs) const; }; // Permit direct uses of isa/cast/dyn_cast on LayoutConstraint. @@ -313,12 +308,6 @@ struct LayoutConstraintLoc { bool isError() const; - // FIXME: We generally shouldn't need to build LayoutConstraintLoc without - // a location. - static LayoutConstraintLoc withoutLoc(LayoutConstraint Layout) { - return LayoutConstraintLoc(Layout, SourceLoc()); - } - /// Get the representative location of this type, for diagnostic /// purposes. SourceLoc getLoc() const { return Loc; } @@ -328,13 +317,7 @@ struct LayoutConstraintLoc { bool hasLocation() const { return Loc.isValid(); } LayoutConstraint getLayoutConstraint() const { return Layout; } - void setLayoutConstraint(LayoutConstraint value) { - Layout = value; - } - bool isNull() const { return Layout.isNull(); } - - LayoutConstraintLoc clone(ASTContext &ctx) const { return *this; } }; /// Checks if ID is a name of a layout constraint and returns this diff --git a/include/swift/AST/LayoutConstraintKind.h b/include/swift/AST/LayoutConstraintKind.h index 2b59f2b58db3f..f67497919352b 100644 --- a/include/swift/AST/LayoutConstraintKind.h +++ b/include/swift/AST/LayoutConstraintKind.h @@ -14,6 +14,8 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Support/DataTypes.h" + #ifndef SWIFT_LAYOUT_CONSTRAINTKIND_H #define SWIFT_LAYOUT_CONSTRAINTKIND_H diff --git a/include/swift/AST/LazyResolver.h b/include/swift/AST/LazyResolver.h index 97632e4cbe45c..982ed2ea438aa 100644 --- a/include/swift/AST/LazyResolver.h +++ b/include/swift/AST/LazyResolver.h @@ -60,9 +60,10 @@ class LazyIterableDeclContextData : public LazyContextData { /// Context data for protocols. class LazyProtocolData : public LazyIterableDeclContextData { public: - /// The context data used for loading all of the members of the iterable - /// context. + /// The context data used for loading a requirement signature. uint64_t requirementSignatureData = 0; + /// The context data used for loading the list of associated types. + uint64_t associatedTypesData = 0; }; /// A class that can lazily load members from a serialized format. @@ -100,6 +101,11 @@ class alignas(void*) LazyMemberLoader { loadRequirementSignature(const ProtocolDecl *proto, uint64_t contextData, SmallVectorImpl &requirements) = 0; + /// Loads the associated types of a protocol. + virtual void + loadAssociatedTypes(const ProtocolDecl *proto, uint64_t contextData, + SmallVectorImpl &assocTypes) = 0; + /// Returns the replaced decl for a given @_dynamicReplacement(for:) attribute. virtual ValueDecl * loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 3d29bbf954611..a5efe092f93a1 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -163,7 +163,8 @@ class OverlayFile; /// output binary and logical module (such as a single library or executable). /// /// \sa FileUnit -class ModuleDecl : public DeclContext, public TypeDecl { +class ModuleDecl + : public DeclContext, public TypeDecl, public ASTAllocated { friend class DirectOperatorLookupRequest; friend class DirectPrecedenceGroupLookupRequest; @@ -513,6 +514,16 @@ class ModuleDecl : public DeclContext, public TypeDecl { return Bits.ModuleDecl.IsMainModule; } + /// Whether this module has been compiled with comprehensive checking for + /// concurrency, e.g., Sendable checking. + bool isConcurrencyChecked() const { + return Bits.ModuleDecl.IsConcurrencyChecked; + } + + void setIsConcurrencyChecked(bool value = true) { + Bits.ModuleDecl.IsConcurrencyChecked = value; + } + /// For the main module, retrieves the list of primary source files being /// compiled, that is, the files we're generating code for. ArrayRef getPrimarySourceFiles() const; @@ -578,10 +589,15 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// /// \param protocol The protocol to which we are computing conformance. /// + /// \param allowMissing When \c true, the resulting conformance reference + /// might include "missing" conformances, which are synthesized for some + /// protocols as an error recovery mechanism. + /// /// \returns The result of the conformance search, which will be /// None if the type does not conform to the protocol or contain a /// ProtocolConformanceRef if it does conform. - ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *protocol); + ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *protocol, + bool allowMissing = false); /// Look for the conformance of the given existential type to the given /// protocol. @@ -661,9 +677,10 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// This assumes that \p module was imported. bool isImportedImplementationOnly(const ModuleDecl *module) const; - /// Returns true if a function, which is using \p nominal, can be serialized - /// by cross-module-optimization. - bool canBeUsedForCrossModuleOptimization(NominalTypeDecl *nominal) const; + /// Returns true if decl context or its content can be serialized by + /// cross-module-optimization. + /// The \p ctxt can e.g. be a NominalType or the context of a function. + bool canBeUsedForCrossModuleOptimization(DeclContext *ctxt) const; /// Finds all top-level decls of this module. /// @@ -729,6 +746,9 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// \returns true if this module is the "swift" standard library module. bool isStdlibModule() const; + /// \returns true if this module has standard substitutions for mangling. + bool hasStandardSubstitutions() const; + /// \returns true if this module is the "SwiftShims" module; bool isSwiftShimsModule() const; @@ -771,7 +791,6 @@ class ModuleDecl : public DeclContext, public TypeDecl { void collectBasicSourceFileInfo( llvm::function_ref callback) const; -public: /// Retrieve a fingerprint value that summarizes the contents of this module. /// /// This interface hash a of a module is guaranteed to change if the interface @@ -784,6 +803,15 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// contents have been made. Fingerprint getFingerprint() const; + /// Returns an approximation of whether the given module could be + /// redistributed and consumed by external clients. + /// + /// FIXME: The scope of this computation should be limited entirely to + /// RenamedDeclRequest. Unfortunately, it has been co-opted to support the + /// \c SerializeOptionsForDebugging hack. Once this information can be + /// transferred from module files to the dSYMs, remove this. + bool isExternallyConsumed() const; + SourceRange getSourceRange() const { return SourceRange(); } static bool classof(const DeclContext *DC) { @@ -796,16 +824,8 @@ class ModuleDecl : public DeclContext, public TypeDecl { return D->getKind() == DeclKind::Module; } -private: - // Make placement new and vanilla new/delete illegal for Modules. - void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() = delete; - void *operator new(size_t Bytes, void *Mem) throw() = delete; -public: - // Only allow allocation of Modules using the allocator in ASTContext - // or by doing a placement new. - void *operator new(size_t Bytes, const ASTContext &C, - unsigned Alignment = alignof(ModuleDecl)); + using ASTAllocated::operator new; + using ASTAllocated::operator delete; }; /// Wraps either a swift module or a clang one. diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 992df4e4a3764..c4ccbf8e861dc 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -25,6 +25,7 @@ #include "llvm/ADT/StringSet.h" #include #include +#include namespace swift { @@ -35,7 +36,8 @@ class Identifier; /// Which kind of module dependencies we are looking for. enum class ModuleDependenciesKind : int8_t { - SwiftTextual, + FirstKind, + SwiftTextual = FirstKind, SwiftBinary, // Placeholder dependencies are a kind of dependencies used only by the // dependency scanner. They are swift modules that the scanner will not be @@ -61,6 +63,20 @@ enum class ModuleDependenciesKind : int8_t { // of all targets, individually, have been computed. SwiftPlaceholder, Clang, + LastKind = Clang + 1 +}; + +struct ModuleDependenciesKindHash { + std::size_t operator()(ModuleDependenciesKind k) const { + using UnderlyingType = std::underlying_type::type; + return std::hash{}(static_cast(k)); + } +}; + +/// Details of a given module used for dependency scanner cache queries. +struct ModuleLookupSpecifics { + Optional kind; + llvm::StringSet<> currentSearchPaths; }; /// Base class for the variant storage of ModuleDependencies. @@ -275,8 +291,8 @@ class ModuleDependencies { /// Describe the module dependencies for a Swift module that can be /// built from a Swift interface file (\c .swiftinterface). - static ModuleDependencies forSwiftInterface( - const std::string &swiftInterfaceFile, + static ModuleDependencies forSwiftTextualModule( + const Optional &swiftInterfaceFile, ArrayRef compiledCandidates, ArrayRef buildCommands, ArrayRef extraPCMArgs, @@ -386,6 +402,9 @@ class ModuleDependencies { /// Add a bridging header to a Swift module's dependencies. void addBridgingHeader(StringRef bridgingHeader); + /// Add source files + void addSourceFile(StringRef sourceFile); + /// Add source files that the bridging header depends on. void addBridgingSourceFile(StringRef bridgingSourceFile); @@ -400,25 +419,30 @@ class ModuleDependencies { }; using ModuleDependencyID = std::pair; +using ModuleDependenciesVector = llvm::SmallVector; /// A cache describing the set of module dependencies that has been queried -/// thus far. -class ModuleDependenciesCache { +/// thus far. This cache records/stores the actual Dependency values and can be +/// preserved across different scanning actions (e.g. in +/// `DependencyScanningTool`). It is not to be queried directly, but is rather +/// meant to be wrapped in an instance of `ModuleDependenciesCache`, responsible +/// for recording new dependencies and answering cache queries in a given scan. +/// Queries to this cache must be disambiguated with a set of search paths to +/// ensure that the returned cached dependency was one that can be found in the +/// current scanning action's filesystem view. +class GlobalModuleDependenciesCache { /// All cached module dependencies, in the order in which they were /// encountered. std::vector AllModules; - /// Dependencies for Textual Swift modules that have already been computed. - llvm::StringMap SwiftTextualModuleDependencies; - - /// Dependencies for Binary Swift modules that have already been computed. - llvm::StringMap SwiftBinaryModuleDependencies; - - /// Dependencies for Swift placeholder dependency modules that have already been computed. - llvm::StringMap SwiftPlaceholderModuleDependencies; - - /// Dependencies for Clang modules that have already been computed. - llvm::StringMap ClangModuleDependencies; + /// Dependencies for modules that have already been computed. + /// This maps a dependency kind to a map of a module's name to a vector of Dependency objects, + /// which correspond to instances of the same module that may have been found + /// in different sets of search paths. + std::unordered_map, + ModuleDependenciesKindHash> + ModuleDependenciesKindMap; /// Additional information needed for Clang dependency scanning. ClangModuleDependenciesCacheImpl *clangImpl = nullptr; @@ -434,46 +458,148 @@ class ModuleDependenciesCache { /// Retrieve the dependencies map that corresponds to the given dependency /// kind. - llvm::StringMap &getDependenciesMap( - ModuleDependenciesKind kind); - const llvm::StringMap &getDependenciesMap( - ModuleDependenciesKind kind) const; + llvm::StringMap & + getDependenciesMap(ModuleDependenciesKind kind); + const llvm::StringMap & + getDependenciesMap(ModuleDependenciesKind kind) const; public: - ModuleDependenciesCache() { } + GlobalModuleDependenciesCache(); + GlobalModuleDependenciesCache(const GlobalModuleDependenciesCache &) = delete; + GlobalModuleDependenciesCache & + operator=(const GlobalModuleDependenciesCache &) = delete; - ModuleDependenciesCache(const ModuleDependenciesCache &) = delete; - ModuleDependenciesCache &operator=(const ModuleDependenciesCache &) = delete; + virtual ~GlobalModuleDependenciesCache() { destroyClangImpl(); } - ~ModuleDependenciesCache() { - destroyClangImpl(); - } +private: + /// Enforce clients not being allowed to query this cache directly, it must be + /// wrapped in an instance of `ModuleDependenciesCache`. + friend class ModuleDependenciesCache; /// Set the Clang-specific implementation data. - void setClangImpl( - ClangModuleDependenciesCacheImpl *clangImpl, - void (*clangImplDeleter)(ClangModuleDependenciesCacheImpl *)) { + virtual void + setClangImpl(ClangModuleDependenciesCacheImpl *clangImpl, + void (*clangImplDeleter)(ClangModuleDependenciesCacheImpl *)) { destroyClangImpl(); this->clangImpl = clangImpl; this->clangImplDeleter = clangImplDeleter; } + /// Retrieve the Clang-specific implementation data; + ClangModuleDependenciesCacheImpl *getClangImpl() const { return clangImpl; } + + /// Whether we have cached dependency information for the given module. + bool hasDependencies(StringRef moduleName, + ModuleLookupSpecifics details) const; + + /// Look for module dependencies for a module with the given name given + /// current search paths. + /// + /// \returns the cached result, or \c None if there is no cached entry. + Optional + findDependencies(StringRef moduleName, ModuleLookupSpecifics details) const; + +public: + /// Look for module dependencies for a module with the given name. + /// This method has a deliberately-obtuse name to indicate that it is not to + /// be used for general queries. + /// + /// \returns the cached result, or \c None if there is no cached entry. + Optional + findAllDependenciesIrrespectiveOfSearchPaths( + StringRef moduleName, Optional kind) const; + + /// Record dependencies for the given module. + const ModuleDependencies *recordDependencies(StringRef moduleName, + ModuleDependencies dependencies); + + /// Update stored dependencies for the given module. + const ModuleDependencies *updateDependencies(ModuleDependencyID moduleID, + ModuleDependencies dependencies); + + /// Reference the list of all module dependencies. + const std::vector &getAllModules() const { + return AllModules; + } +}; + +/// This "local" dependencies cache persists only for the duration of a given +/// scanning action, and wraps an instance of a `GlobalModuleDependenciesCache` +/// which may carry cached scanning information from prior scanning actions. +/// This cache maintains a store of references to all dependencies found within +/// the current scanning action (with their values stored in the global Cache), +/// since these do not require clients to disambiguate them with search paths. +class ModuleDependenciesCache { +private: + GlobalModuleDependenciesCache &globalCache; + + /// References to data in `globalCache` for dependencies accimulated during + /// the current scanning action. + std::unordered_map, + ModuleDependenciesKindHash> + ModuleDependenciesKindMap; + + /// Retrieve the dependencies map that corresponds to the given dependency + /// kind. + llvm::StringMap & + getDependencyReferencesMap(ModuleDependenciesKind kind); + const llvm::StringMap & + getDependencyReferencesMap(ModuleDependenciesKind kind) const; + + /// Local cache results lookup, only for modules which were discovered during + /// the current scanner invocation. + bool hasDependencies(StringRef moduleName, + Optional kind) const; + + /// Local cache results lookup, only for modules which were discovered during + /// the current scanner invocation. + Optional + findDependencies(StringRef moduleName, + Optional kind) const; + +public: + ModuleDependenciesCache(GlobalModuleDependenciesCache &globalCache); + ModuleDependenciesCache(const ModuleDependenciesCache &) = delete; + ModuleDependenciesCache &operator=(const ModuleDependenciesCache &) = delete; + virtual ~ModuleDependenciesCache() {} + +public: + /// Set the Clang-specific implementation data. + void + setClangImpl(ClangModuleDependenciesCacheImpl *clangImpl, + void (*clangImplDeleter)(ClangModuleDependenciesCacheImpl *)) { + globalCache.setClangImpl(clangImpl, clangImplDeleter); + } + /// Retrieve the Clang-specific implementation data; ClangModuleDependenciesCacheImpl *getClangImpl() const { - return clangImpl; + return globalCache.getClangImpl(); } /// Whether we have cached dependency information for the given module. bool hasDependencies(StringRef moduleName, - Optional kind) const; + ModuleLookupSpecifics details) const; + + /// Look for module dependencies for a module with the given name given + /// current search paths. + /// + /// \returns the cached result, or \c None if there is no cached entry. + Optional + findDependencies(StringRef moduleName, ModuleLookupSpecifics details) const; /// Look for module dependencies for a module with the given name. + /// This method has a deliberately-obtuse name to indicate that it is not to + /// be used for general queries. /// /// \returns the cached result, or \c None if there is no cached entry. - Optional findDependencies( - StringRef moduleName, - Optional kind) const; + Optional + findAllDependenciesIrrespectiveOfSearchPaths( + StringRef moduleName, Optional kind) const { + return globalCache.findAllDependenciesIrrespectiveOfSearchPaths(moduleName, + kind); + } /// Record dependencies for the given module. void recordDependencies(StringRef moduleName, @@ -485,10 +611,10 @@ class ModuleDependenciesCache { /// Reference the list of all module dependencies. const std::vector &getAllModules() const { - return AllModules; + return globalCache.getAllModules(); } }; -} +} // namespace swift #endif /* SWIFT_AST_MODULE_DEPENDENCIES_H */ diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 68d1cd4dc21b5..6b2ba8581fb48 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -17,8 +17,8 @@ #ifndef SWIFT_AST_NAME_LOOKUP_H #define SWIFT_AST_NAME_LOOKUP_H -#include "llvm/ADT/SmallVector.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" #include "swift/AST/Module.h" #include "swift/Basic/Compiler.h" @@ -26,11 +26,11 @@ #include "swift/Basic/NullablePtr.h" #include "swift/Basic/SourceLoc.h" #include "swift/Basic/SourceManager.h" +#include "llvm/ADT/SmallVector.h" namespace swift { class ASTContext; class DeclName; -class GenericSignatureBuilder; class Type; class TypeDecl; class ValueDecl; @@ -487,7 +487,7 @@ void lookupVisibleMemberDecls(VisibleDeclConsumer &Consumer, bool includeInstanceMembers, bool includeDerivedRequirements, bool includeProtocolExtensionMembers, - GenericSignatureBuilder *GSB = nullptr); + GenericSignature genericSig = GenericSignature()); namespace namelookup { @@ -503,6 +503,19 @@ void filterForDiscriminator(SmallVectorImpl &results, } // end namespace namelookup +/// Describes an inherited nominal entry. +struct InheritedNominalEntry : Located { + /// The location of the "unchecked" attribute, if present. + SourceLoc uncheckedLoc; + + InheritedNominalEntry() { } + + InheritedNominalEntry( + NominalTypeDecl *item, SourceLoc loc, + SourceLoc uncheckedLoc + ) : Located(item, loc), uncheckedLoc(uncheckedLoc) { } +}; + /// Retrieve the set of nominal type declarations that are directly /// "inherited" by the given declaration at a particular position in the /// list of "inherited" types. @@ -511,7 +524,7 @@ void filterForDiscriminator(SmallVectorImpl &results, /// AnyObject type, set \c anyObject true. void getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, - unsigned i, llvm::SmallVectorImpl> &result, + unsigned i, llvm::SmallVectorImpl &result, bool &anyObject); /// Retrieve the set of nominal type declarations that are directly @@ -519,7 +532,7 @@ void getDirectlyInheritedNominalTypeDecls( /// and splitting out the components of compositions. /// /// If we come across the AnyObject type, set \c anyObject true. -SmallVector, 4> getDirectlyInheritedNominalTypeDecls( +SmallVector getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, bool &anyObject); @@ -683,7 +696,7 @@ class ASTScopeDeclGatherer : public AbstractASTScopeDeclConsumer { } // end namespace namelookup /// The interface into the ASTScope subsystem -class ASTScope { +class ASTScope : public ASTAllocated { friend class ast_scope::ASTScopeImpl; ast_scope::ASTSourceFileScope *const impl; @@ -742,20 +755,6 @@ class ASTScope { void print(llvm::raw_ostream &) const; void dumpOneScopeMapLocation(std::pair); - // Make vanilla new illegal for ASTScopes. - void *operator new(size_t bytes) = delete; - // Need this because have virtual destructors - void operator delete(void *data) {} - - // Only allow allocation of scopes using the allocator of a particular source - // file. - void *operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment = alignof(ASTScope)); - void *operator new(size_t Bytes, void *Mem) { - assert(Mem); - return Mem; - } - private: static ast_scope::ASTSourceFileScope *createScopeTree(SourceFile *); diff --git a/include/swift/AST/ParameterList.h b/include/swift/AST/ParameterList.h index a5af4a8e81d74..13c79871e8e08 100644 --- a/include/swift/AST/ParameterList.h +++ b/include/swift/AST/ParameterList.h @@ -27,15 +27,11 @@ namespace swift { /// This describes a list of parameters. Each parameter descriptor is tail /// allocated onto this list. class alignas(ParamDecl *) ParameterList final : + // FIXME: Do we really just want to allocate these pointer-aligned? + public ASTAllocated::type>, private llvm::TrailingObjects { friend TrailingObjects; - void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() = delete; - void *operator new(size_t Bytes, void *Mem) throw() = delete; - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = 8); - SourceLoc LParenLoc, RParenLoc; size_t numParameters; @@ -67,6 +63,14 @@ class alignas(ParamDecl *) ParameterList final : return create(decl->getASTContext(), decl); } + static ParameterList *clone(const ASTContext &Ctx, ParameterList *PL) { + SmallVector params; + params.reserve(PL->size()); + for (auto p : *PL) + params.push_back(ParamDecl::clone(Ctx, p)); + return ParameterList::create(Ctx, params); + } + SourceLoc getLParenLoc() const { return LParenLoc; } SourceLoc getRParenLoc() const { return RParenLoc; } @@ -76,7 +80,10 @@ class alignas(ParamDecl *) ParameterList final : iterator end() { return getArray().end(); } const_iterator begin() const { return getArray().begin(); } const_iterator end() const { return getArray().end(); } - + + ParamDecl *front() const { return getArray().front(); } + ParamDecl *back() const { return getArray().back(); } + MutableArrayRef getArray() { return {getTrailingObjects(), numParameters}; } diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index f1ac363f351a4..43340aed18bc7 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -20,6 +20,7 @@ #include "swift/Basic/AnyValue.h" #include "swift/Basic/SourceLoc.h" #include "swift/Basic/type_traits.h" +#include "swift/AST/ASTAllocated.h" #include "swift/AST/Decl.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" @@ -50,7 +51,7 @@ enum : unsigned { NumPatternKindBits = llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, PatternKind kind); /// Pattern - Base class for all patterns in Swift. -class alignas(8) Pattern { +class alignas(8) Pattern : public ASTAllocated { protected: union { uint64_t OpaqueBits; @@ -202,17 +203,13 @@ class alignas(8) Pattern { /// Does this binding declare something that requires storage? bool hasStorage() const; + /// Does this pattern have any mutable 'var' bindings? + bool hasAnyMutableBindings() const; + static bool classof(const Pattern *P) { return true; } //*** Allocation Routines ************************************************/ - void *operator new(size_t bytes, const ASTContext &C); - - // Make placement new and vanilla new/delete illegal for Patterns. - void *operator new(size_t bytes) = delete; - void operator delete(void *data) = delete; - void *operator new(size_t bytes, void *data) = delete; - void print(llvm::raw_ostream &OS, const PrintOptions &Options = PrintOptions()) const; SWIFT_DEBUG_DUMP; diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index a4b0d03f9bbe0..3ec56c721b829 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -195,6 +195,9 @@ struct PrintOptions { /// type might be ambiguous. bool FullyQualifiedTypesIfAmbiguous = false; + /// Print fully qualified extended types if ambiguous. + bool FullyQualifiedExtendedTypesIfAmbiguous = false; + /// If true, printed module names will use the "exported" name, which may be /// different from the regular name. /// @@ -343,6 +346,10 @@ struct PrintOptions { /// Whether to print 'static' or 'class' on static decls. bool PrintStaticKeyword = true; + /// Whether to print 'mutating', 'nonmutating', or '__consuming' keyword on + /// specified decls. + bool PrintSelfAccessKindKeyword = true; + /// Whether to print 'override' keyword on overridden decls. bool PrintOverrideKeyword = true; diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 20ebcaf7ef2f4..822f3448ebb9e 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -66,7 +66,10 @@ enum class ProtocolConformanceKind { Specialized, /// Conformance of a generic class type projected through one of its /// superclass's conformances. - Inherited + Inherited, + /// Builtin conformances are special conformaces that the runtime handles + /// and isn't implemented directly in Swift. + Builtin }; /// Describes the state of a protocol conformance, which may be complete, @@ -88,7 +91,8 @@ enum class ProtocolConformanceState { /// /// ProtocolConformance is an abstract base class, implemented by subclasses /// for the various kinds of conformance (normal, specialized, inherited). -class alignas(1 << DeclAlignInBits) ProtocolConformance { +class alignas(1 << DeclAlignInBits) ProtocolConformance + : public ASTAllocated { /// The kind of protocol conformance. ProtocolConformanceKind Kind; @@ -281,20 +285,6 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { /// is either the default definition or was otherwise deduced. bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const; - // Make vanilla new/delete illegal for protocol conformances. - void *operator new(size_t bytes) = delete; - void operator delete(void *data) = delete; - - // Only allow allocation of protocol conformances using the allocator in - // ASTContext or by doing a placement new. - void *operator new(size_t bytes, ASTContext &context, - AllocationArena arena, - unsigned alignment = alignof(ProtocolConformance)); - void *operator new(size_t bytes, void *mem) { - assert(mem); - return mem; - } - /// Print a parseable and human-readable description of the identifying /// information of the protocol conformance. void printName(raw_ostream &os, @@ -329,7 +319,9 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { /// - the type is directly declared to conform to the protocol (a /// normal conformance) or /// - the protocol's existential type is known to conform to itself (a -/// self-conformance). +/// self-conformance) or +/// - the type's conformance is declared within the runtime (a builtin +/// conformance). class RootProtocolConformance : public ProtocolConformance { protected: RootProtocolConformance(ProtocolConformanceKind kind, Type conformingType) @@ -380,7 +372,8 @@ class RootProtocolConformance : public ProtocolConformance { static bool classof(const ProtocolConformance *conformance) { return conformance->getKind() == ProtocolConformanceKind::Normal || - conformance->getKind() == ProtocolConformanceKind::Self; + conformance->getKind() == ProtocolConformanceKind::Self || + conformance->getKind() == ProtocolConformanceKind::Builtin; } }; @@ -412,11 +405,20 @@ class NormalProtocolConformance : public RootProtocolConformance, /// The location of this protocol conformance in the source. SourceLoc Loc; + // Flag bits used in ContextAndBits. + enum { + /// The conformance is invalid. + InvalidFlag = 0x01, + + /// The conformance was labeled with @unchecked. + UncheckedFlag = 0x02, + }; + /// The declaration context containing the ExtensionDecl or /// NominalTypeDecl that declared the conformance. /// - /// Also stores the "invalid" bit. - llvm::PointerIntPair ContextAndInvalid; + /// Also stores the "invalid" and "unchecked" bits. + llvm::PointerIntPair ContextAndBits; /// The reason that this conformance exists. /// @@ -457,11 +459,12 @@ class NormalProtocolConformance : public RootProtocolConformance, public: NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol, SourceLoc loc, DeclContext *dc, - ProtocolConformanceState state) + ProtocolConformanceState state, + bool isUnchecked) : RootProtocolConformance(ProtocolConformanceKind::Normal, conformingType), ProtocolAndState(protocol, state), Loc(loc), - ContextAndInvalid(dc, false) { + ContextAndBits(dc, isUnchecked ? UncheckedFlag : 0) { assert(!conformingType->hasArchetype() && "ProtocolConformances should store interface types"); } @@ -475,7 +478,7 @@ class NormalProtocolConformance : public RootProtocolConformance, /// Get the declaration context that contains the conforming extension or /// nominal type declaration. DeclContext *getDeclContext() const { - return ContextAndInvalid.getPointer(); + return ContextAndBits.getPointer(); } /// Get any additional requirements that are required for this conformance to @@ -497,15 +500,20 @@ class NormalProtocolConformance : public RootProtocolConformance, /// Determine whether this conformance is invalid. bool isInvalid() const { - return ContextAndInvalid.getInt(); + return ContextAndBits.getInt() & InvalidFlag; } /// Mark this conformance as invalid. void setInvalid() { - ContextAndInvalid.setInt(true); + ContextAndBits.setInt(ContextAndBits.getInt() | InvalidFlag); SignatureConformances = {}; } + /// Whether this is an "unchecked" conformance. + bool isUnchecked() const { + return ContextAndBits.getInt() & UncheckedFlag; + } + /// Get the kind of source from which this conformance comes. ConformanceEntryKind getSourceKind() const { return SourceKindAndImplyingConformance.getInt(); @@ -972,6 +980,124 @@ class InheritedProtocolConformance : public ProtocolConformance, } }; +/// Describes the kind of a builtin conformance. +enum class BuiltinConformanceKind { + // A builtin conformance that has been synthesized by the implementation. + Synthesized = 0, + // A missing conformance that we have nonetheless synthesized so that + // we can diagnose it later. + Missing, +}; + +/// A builtin conformance appears when a non-nominal type has a +/// conformance that is synthesized by the implementation. +class BuiltinProtocolConformance final : public RootProtocolConformance, + private llvm::TrailingObjects { + friend ASTContext; + friend TrailingObjects; + + ProtocolDecl *protocol; + GenericSignature genericSig; + size_t numConditionalRequirements : 31; + unsigned builtinConformanceKind : 1; + + size_t numTrailingObjects(OverloadToken) const { + return numConditionalRequirements; + } + + BuiltinProtocolConformance(Type conformingType, ProtocolDecl *protocol, + GenericSignature genericSig, + ArrayRef conditionalRequirements, + BuiltinConformanceKind kind); + +public: + /// Get the protocol being conformed to. + ProtocolDecl *getProtocol() const { + return protocol; + } + + /// Retrieve the generic signature that describes the type parameters used + /// within the conforming type. + GenericSignature getGenericSignature() const { + return genericSig; + } + + BuiltinConformanceKind getBuiltinConformanceKind() const { + return static_cast(builtinConformanceKind); + } + + /// Whether this represents a "missing" conformance that should be diagnosed + /// later. + bool isMissing() const { + return getBuiltinConformanceKind() == BuiltinConformanceKind::Missing; + } + + /// Get any requirements that must be satisfied for this conformance to apply. + Optional> + getConditionalRequirementsIfAvailable() const { + return getConditionalRequirements(); + } + + /// Get any requirements that must be satisfied for this conformance to apply. + ArrayRef getConditionalRequirements() const { + return {getTrailingObjects(), numConditionalRequirements}; + } + + /// Get the declaration context that contains the nominal type declaration. + DeclContext *getDeclContext() const { + return getProtocol(); + } + + /// Retrieve the state of this conformance. + ProtocolConformanceState getState() const { + return ProtocolConformanceState::Complete; + } + + /// Get the kind of source from which this conformance comes. + ConformanceEntryKind getSourceKind() const { + return ConformanceEntryKind::Synthesized; + } + /// Get the protocol conformance which implied this implied conformance. + NormalProtocolConformance *getImplyingConformance() const { + return nullptr; + } + + bool hasTypeWitness(AssociatedTypeDecl *assocType) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Retrieve the type witness and type decl (if one exists) + /// for the given associated type. + TypeWitnessAndDecl + getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, + SubstOptions options=None) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Given that the requirement signature of the protocol directly states + /// that the given dependent type must conform to the given protocol, + /// return its associated conformance. + ProtocolConformanceRef + getAssociatedConformance(Type assocType, ProtocolDecl *protocol) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + /// Retrieve the witness corresponding to the given value requirement. + ConcreteDeclRef getWitnessDeclRef(ValueDecl *requirement) const { + return ConcreteDeclRef(requirement); + } + + /// Determine whether the witness for the given requirement + /// is either the default definition or was otherwise deduced. + bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const { + llvm_unreachable("builtin-conformances never have associated types"); + } + + static bool classof(const ProtocolConformance *conformance) { + return conformance->getKind() == ProtocolConformanceKind::Builtin; + } +}; + inline bool ProtocolConformance::isInvalid() const { return getRootConformance()->isInvalid(); } diff --git a/include/swift/AST/ProtocolConformanceRef.h b/include/swift/AST/ProtocolConformanceRef.h index 11551a0e57aa1..47f12176cb869 100644 --- a/include/swift/AST/ProtocolConformanceRef.h +++ b/include/swift/AST/ProtocolConformanceRef.h @@ -19,6 +19,8 @@ #include "swift/Basic/Debug.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/STLExtras.h" +#include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/Requirement.h" #include "swift/AST/TypeAlignments.h" #include "swift/AST/Type.h" @@ -29,6 +31,7 @@ namespace llvm { namespace swift { +class BuiltinProtocolConformance; class ConcreteDeclRef; class ProtocolConformance; enum class EffectKind : uint8_t; @@ -72,6 +75,11 @@ class ProtocolConformanceRef { return ProtocolConformanceRef(); } + /// Retrieve an invalid or missing conformance, as appropriate, when a + /// legitimate conformance doesn't exist. + static ProtocolConformanceRef forMissingOrInvalid( + Type type, ProtocolDecl *proto); + bool isInvalid() const { return !Union; } @@ -98,6 +106,24 @@ class ProtocolConformanceRef { return Union.get(); } + /// Determine whether this conformance (or a conformance it depends on) + /// involves a "missing" conformance anywhere. Such conformances + /// cannot be depended on to always exist. + bool hasMissingConformance(ModuleDecl *module) const; + + /// Enumerate the missing conformances in this conformance. + /// + /// Calls \c fn with each missing conformance found within this conformance, + /// including this conformance or any conditional conformances it depends on. + /// If the invocation of \c fn returns \c true, the traversal exits early + /// and the overall function returns \c true. + /// + /// \returns \c true if any invocation of \c fn returned true, + /// \c false otherwise. + bool forEachMissingConformance( + ModuleDecl *module, + llvm::function_ref fn) const; + using OpaqueValue = void*; OpaqueValue getOpaqueValue() const { return Union.getOpaqueValue(); } static ProtocolConformanceRef getFromOpaqueValue(OpaqueValue value) { diff --git a/include/swift/AST/Requirement.h b/include/swift/AST/Requirement.h index d0c1eef287a2d..cc8b5b07abc34 100644 --- a/include/swift/AST/Requirement.h +++ b/include/swift/AST/Requirement.h @@ -76,6 +76,25 @@ class Requirement ProtocolDecl *getProtocolDecl() const; + /// Determines if this substituted requirement is satisfied. + /// + /// \param conditionalRequirements An out parameter initialized to an + /// array of requirements that the caller must check to ensure this + /// requirement is completely satisfied. + bool isSatisfied(ArrayRef &conditionalRequirements, + bool allowMissing = false) const; + + /// Determines if this substituted requirement can ever be satisfied, + /// possibly with additional substitutions. + /// + /// For example, if 'T' is unconstrained, then a superclass requirement + /// 'T : C' can be satisfied; however, if 'T' already has an unrelated + /// superclass requirement, 'T : C' cannot be satisfied. + bool canBeSatisfied() const; + + /// Linear order on requirements in a generic signature. + int compare(const Requirement &other) const; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &out) const; void print(raw_ostream &os, const PrintOptions &opts) const; diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index 0d66424e0e86b..f4eccba75d03c 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -145,7 +145,11 @@ class SILOptions { /// Don't generate code using partial_apply in SIL generation. bool DisableSILPartialApply = false; - /// The name of the SIL outputfile if compiled with SIL debugging (-gsil). + /// Print debug information into the SIL file + bool PrintDebugInfo = false; + + /// The name of the SIL outputfile if compiled with SIL debugging + /// (-sil-based-debuginfo). std::string SILOutputFileNameForDebugging; /// If set to true, compile with the SIL Ownership Model enabled. diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index 52eeace6a1460..f873e82ada368 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -82,9 +82,6 @@ class SearchPathOptions { /// would for a non-system header. bool DisableModulesValidateSystemDependencies = false; - /// The paths to a set of explicitly built modules from interfaces. - std::vector ExplicitSwiftModules; - /// A set of compiled modules that may be ready to use. std::vector CandidateCompiledModules; diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 42389862e095e..7e82bd8a272e6 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -99,7 +99,7 @@ class SourceFile final : public FileUnit { /// Either the class marked \@NS/UIApplicationMain or the synthesized FuncDecl /// that calls main on the type marked @main. - Decl *MainDecl = nullptr; + ValueDecl *MainDecl = nullptr; /// The source location of the main type. SourceLoc MainDeclDiagLoc; @@ -241,11 +241,18 @@ class SourceFile final : public FileUnit { /// unsatisfied, which might conflict with other Objective-C methods. std::vector ObjCUnsatisfiedOptReqs; + /// A selector that is used by two different declarations in the same class. + /// Fields: classDecl, selector, isInstanceMethod. using ObjCMethodConflict = std::tuple; /// List of Objective-C member conflicts we have found during type checking. std::vector ObjCMethodConflicts; + /// List of attributes added by access notes, used to emit remarks for valid + /// ones. + llvm::DenseMap> + AttrsAddedByAccessNotes; + /// Describes what kind of file this is, which can affect some type checking /// and other behavior. const SourceFileKind Kind; @@ -480,7 +487,7 @@ class SourceFile final : public FileUnit { llvm_unreachable("bad SourceFileKind"); } - Decl *getMainDecl() const override { return MainDecl; } + ValueDecl *getMainDecl() const override { return MainDecl; } SourceLoc getMainDeclDiagLoc() const { assert(hasMainDecl()); return MainDeclDiagLoc; @@ -494,7 +501,7 @@ class SourceFile final : public FileUnit { /// one. /// /// Should only be called during type-checking. - bool registerMainDecl(Decl *mainDecl, SourceLoc diagLoc); + bool registerMainDecl(ValueDecl *mainDecl, SourceLoc diagLoc); /// True if this source file has an application entry point. /// diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 5df42a9ac8550..0e15a031ad344 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -17,6 +17,7 @@ #ifndef SWIFT_AST_STMT_H #define SWIFT_AST_STMT_H +#include "swift/AST/ASTAllocated.h" #include "swift/AST/ASTNode.h" #include "swift/AST/Availability.h" #include "swift/AST/AvailabilitySpec.h" @@ -53,7 +54,7 @@ enum : unsigned { NumStmtKindBits = countBitsUsed(static_cast(StmtKind::Last_Stmt)) }; /// Stmt - Base class for all statements in swift. -class alignas(8) Stmt { +class alignas(8) Stmt : public ASTAllocated { Stmt(const Stmt&) = delete; Stmt& operator=(const Stmt&) = delete; @@ -138,16 +139,6 @@ class alignas(8) Stmt { SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS, const ASTContext *Ctx = nullptr, unsigned Indent = 0) const; - - // Only allow allocation of Exprs using the allocator in ASTContext - // or by doing a placement new. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(Stmt)); - - // Make vanilla new/delete illegal for Stmts. - void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() = delete; - void *operator new(size_t Bytes, void *Mem) throw() = delete; }; /// BraceStmt - A brace enclosed sequence of expressions, stmts, or decls, like @@ -341,11 +332,17 @@ class alignas(8) PoundAvailableInfo final : /// This is filled in by Sema. VersionRange VariantAvailableRange; + /// Indicates that the expression is checking if a version range + /// is **not** available. + bool _isUnavailability; + PoundAvailableInfo(SourceLoc PoundLoc, SourceLoc LParenLoc, - ArrayRef queries, SourceLoc RParenLoc) + ArrayRef queries, SourceLoc RParenLoc, + bool isUnavailability) : PoundLoc(PoundLoc), LParenLoc(LParenLoc), RParenLoc(RParenLoc), NumQueries(queries.size()), AvailableRange(VersionRange::empty()), - VariantAvailableRange(VersionRange::empty()) { + VariantAvailableRange(VersionRange::empty()), + _isUnavailability(isUnavailability) { std::uninitialized_copy(queries.begin(), queries.end(), getTrailingObjects()); } @@ -354,7 +351,8 @@ class alignas(8) PoundAvailableInfo final : static PoundAvailableInfo *create(ASTContext &ctx, SourceLoc PoundLoc, SourceLoc LParenLoc, ArrayRef queries, - SourceLoc RParenLoc); + SourceLoc RParenLoc, + bool isUnavailability); ArrayRef getQueries() const { return llvm::makeArrayRef(getTrailingObjects(), @@ -379,6 +377,8 @@ class alignas(8) PoundAvailableInfo final : void setVariantAvailableRange(const VersionRange &Range) { VariantAvailableRange = Range; } + + bool isUnavailability() const { return _isUnavailability; } }; diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index 6097cf9a29fc5..372df2f21afe0 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -38,6 +38,7 @@ namespace swift { class DifferentiableAttr; class Expr; class ExtensionDecl; + class FileUnit; class GenericEnvironment; class GenericParamList; class GenericTypeParamDecl; @@ -123,6 +124,7 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::BraceStmt, swift::StmtAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::ASTContext, swift::ASTContextAlignInBits); LLVM_DECLARE_TYPE_ALIGNMENT(swift::DeclContext, swift::DeclContextAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::FileUnit, swift::DeclContextAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::DifferentiableAttr, swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::Expr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::CaptureListExpr, swift::ExprAlignInBits) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index e9d003583dd6f..7fb61d640f822 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -287,32 +287,6 @@ class ExistentialConformsToSelfRequest : void cacheResult(bool value) const; }; -/// Determine whether we are allowed to refer to an existential type conforming -/// to this protocol. -class ExistentialTypeSupportedRequest : - public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - // Evaluation. - bool evaluate(Evaluator &evaluator, ProtocolDecl *decl) const; - -public: - // Cycle handling. - void diagnoseCycle(DiagnosticEngine &diags) const; - void noteCycleStep(DiagnosticEngine &diags) const; - - // Separate caching. - bool isCached() const { return true; } - Optional getCachedResult() const; - void cacheResult(bool value) const; -}; - class PolymorphicEffectRequirementsRequest : public SimpleRequest { +/// Determine whether the given nominal type is an actor. +class IsActorRequest : + public SimpleRequest { public: using SimpleRequest::SimpleRequest; private: friend SimpleRequest; - bool evaluate(Evaluator &evaluator, FuncDecl *func) const; + bool evaluate(Evaluator &evaluator, NominalTypeDecl *nominal) const; public: - // Separate caching. + // Caching bool isCached() const { return true; } - Optional getCachedResult() const; - void cacheResult(bool value) const; }; -/// Determine whether the given function can be an @asyncHandler, without -/// producing any diagnostics. -class CanBeAsyncHandlerRequest : - public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -910,35 +881,36 @@ class CanBeAsyncHandlerRequest : private: friend SimpleRequest; - bool evaluate(Evaluator &evaluator, FuncDecl *func) const; + bool evaluate(Evaluator &evaluator, ClassDecl *classDecl, + ModuleDecl *M, ResilienceExpansion expansion) const; public: // Caching bool isCached() const { return true; } }; -/// Determine whether the given nominal type is an actor. -class IsActorRequest : - public SimpleRequest { +/// Determine whether the given class is a distributed actor. +class IsDistributedActorRequest : + public SimpleRequest { public: - using SimpleRequest::SimpleRequest; + using SimpleRequest::SimpleRequest; private: - friend SimpleRequest; + friend SimpleRequest; - bool evaluate(Evaluator &evaluator, NominalTypeDecl *nominal) const; + bool evaluate(Evaluator &evaluator, NominalTypeDecl *nominal) const; public: - // Caching - bool isCached() const { return true; } + // Caching + bool isCached() const { return true; } }; -/// Determine whether the given class is a default actor. -class IsDefaultActorRequest : - public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -946,12 +918,11 @@ class IsDefaultActorRequest : private: friend SimpleRequest; - bool evaluate(Evaluator &evaluator, ClassDecl *classDecl, - ModuleDecl *M, ResilienceExpansion expansion) const; + AbstractFunctionDecl *evaluate(Evaluator &evaluator, AbstractFunctionDecl *func) const; public: - // Caching - bool isCached() const { return true; } + // Caching + bool isCached() const { return true; } }; /// Retrieve the static "shared" property within a global actor that provides @@ -1023,6 +994,20 @@ class ActorIsolationRequest : bool isCached() const { return true; } }; +/// Determine whether the given function should have an isolated 'self'. +class HasIsolatedSelfRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + bool evaluate(Evaluator &evaluator, ValueDecl *func) const; +}; + /// Request whether the storage has a mutating getter. class IsGetterMutatingRequest : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + bool evaluate(Evaluator &evaluator, Decl *decl) const; + +public: + // Cached. + bool isCached() const { return true; } +}; + /// Computes an initializer context for a parameter with a default argument. class DefaultArgumentInitContextRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -2965,8 +2971,8 @@ class AsyncAlternativeRequest private: friend SimpleRequest; - AbstractFunctionDecl *evaluate( - Evaluator &evaluator, AbstractFunctionDecl *attachedFunctionDecl) const; + ValueDecl *evaluate(Evaluator &evaluator, const ValueDecl *attached, + const AvailableAttr *attr) const; public: bool isCached() const { return true; } diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 95e74962969ae..70a7d52115aa4 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -87,21 +87,19 @@ SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest, Type(EnumDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest, bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, ExistentialTypeSupportedRequest, - bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResultBuilderTypeRequest, Type(ValueDecl *), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, IsAsyncHandlerRequest, bool(FuncDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, CanBeAsyncHandlerRequest, bool(FuncDecl *), - Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsActorRequest, bool(NominalTypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsDefaultActorRequest, bool(ClassDecl *, ModuleDecl *, ResilienceExpansion), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, IsDistributedActorRequest, bool(NominalTypeDecl *), + Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, GetDistributedRemoteFuncRequest, AbstractFunctionDecl *(AbstractFunctionDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, GlobalActorInstanceRequest, VarDecl *(NominalTypeDecl *), Cached, NoLocationInfo) @@ -112,6 +110,9 @@ SWIFT_REQUEST(TypeChecker, GlobalActorAttributeRequest, SWIFT_REQUEST(TypeChecker, ActorIsolationRequest, ActorIsolationState(ValueDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasIsolatedSelfRequest, + bool(ValueDecl *), + Uncached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, FunctionOperatorRequest, OperatorDecl *(FuncDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(NameLookup, GenericSignatureRequest, @@ -135,6 +136,8 @@ SWIFT_REQUEST(TypeChecker, InferredGenericSignatureRequest, SmallVector, SmallVector, bool), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DistributedModuleIsAvailableRequest, + bool(ModuleDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, InheritedTypeRequest, Type(llvm::PointerUnion, unsigned, TypeResolutionStage), @@ -326,6 +329,6 @@ SWIFT_REQUEST(TypeChecker, SynthesizeMainFunctionRequest, SWIFT_REQUEST(TypeChecker, GetImplicitSendableRequest, NormalProtocolConformance *(NominalTypeDecl *), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, AsyncAlternativeRequest, - AbstractFunctionDecl *(AbstractFunctionDecl *), +SWIFT_REQUEST(TypeChecker, RenamedDeclRequest, + ValueDecl *(const ValueDecl *), Cached, NoLocationInfo) diff --git a/include/swift/AST/TypeLoc.h b/include/swift/AST/TypeLoc.h index 9cdab2c61be43..8804e5d7de849 100644 --- a/include/swift/AST/TypeLoc.h +++ b/include/swift/AST/TypeLoc.h @@ -29,7 +29,7 @@ class TypeRepr; /// TypeLoc - Provides source location information for a parsed type. /// A TypeLoc is stored in AST nodes which use an explicitly written type. -class alignas(1 << TypeReprAlignInBits) TypeLoc final { +class alignas(1 << TypeReprAlignInBits) TypeLoc { Type Ty; TypeRepr *TyR = nullptr; diff --git a/include/swift/AST/TypeMatcher.h b/include/swift/AST/TypeMatcher.h index bae91a53192ec..c0da030791ab5 100644 --- a/include/swift/AST/TypeMatcher.h +++ b/include/swift/AST/TypeMatcher.h @@ -195,8 +195,32 @@ class TypeMatcher { TRIVIAL_CASE(ModuleType) TRIVIAL_CASE(DynamicSelfType) TRIVIAL_CASE(ArchetypeType) - TRIVIAL_CASE(GenericTypeParamType) - TRIVIAL_CASE(DependentMemberType) + + bool visitDependentMemberType(CanDependentMemberType firstType, + Type secondType, + Type sugaredFirstType) { + /* If the types match, continue. */ + if (!Matcher.asDerived().alwaysMismatchTypeParameters() && + firstType->isEqual(secondType)) + return true; + + /* Otherwise, let the derived class deal with the mismatch. */ + return mismatch(firstType.getPointer(), secondType, + sugaredFirstType); + } + + bool visitGenericTypeParamType(CanGenericTypeParamType firstType, + Type secondType, + Type sugaredFirstType) { + /* If the types match, continue. */ + if (!Matcher.asDerived().alwaysMismatchTypeParameters() && + firstType->isEqual(secondType)) + return true; + + /* Otherwise, let the derived class deal with the mismatch. */ + return mismatch(firstType.getPointer(), secondType, + sugaredFirstType); + } /// FIXME: Split this out into cases? bool visitAnyFunctionType(CanAnyFunctionType firstFunc, Type secondType, @@ -300,6 +324,8 @@ class TypeMatcher { #undef TRIVIAL_CASE }; + bool alwaysMismatchTypeParameters() const { return false; } + ImplClass &asDerived() { return static_cast(*this); } const ImplClass &asDerived() const { diff --git a/include/swift/AST/TypeNodes.def b/include/swift/AST/TypeNodes.def index c9e39d561f728..e117562a82117 100644 --- a/include/swift/AST/TypeNodes.def +++ b/include/swift/AST/TypeNodes.def @@ -171,7 +171,8 @@ ABSTRACT_SUGARED_TYPE(Sugar, Type) ABSTRACT_SUGARED_TYPE(UnarySyntaxSugar, SyntaxSugarType) SUGARED_TYPE(ArraySlice, UnarySyntaxSugarType) SUGARED_TYPE(Optional, UnarySyntaxSugarType) - TYPE_RANGE(UnarySyntaxSugar, ArraySlice, Optional) + SUGARED_TYPE(VariadicSequence, UnarySyntaxSugarType) + TYPE_RANGE(UnarySyntaxSugar, ArraySlice, VariadicSequence) SUGARED_TYPE(Dictionary, SyntaxSugarType) TYPE_RANGE(SyntaxSugar, ArraySlice, Dictionary) TYPE_RANGE(Sugar, Paren, Dictionary) diff --git a/include/swift/AST/TypeRefinementContext.h b/include/swift/AST/TypeRefinementContext.h index 2dca10739444e..f2c557b997435 100644 --- a/include/swift/AST/TypeRefinementContext.h +++ b/include/swift/AST/TypeRefinementContext.h @@ -46,7 +46,7 @@ namespace swift { /// These refinement contexts form a lexical tree parallel to the AST but much /// more sparse: we only introduce refinement contexts when there is something /// to refine. -class TypeRefinementContext { +class TypeRefinementContext : public ASTAllocated { public: /// Describes the reason a type refinement context was introduced. @@ -154,13 +154,21 @@ class TypeRefinementContext { SourceRange SrcRange; + /// A canonical availiability info for this context, computed top-down from the root + /// context (compilation deployment target). AvailabilityContext AvailabilityInfo; + /// If this context was annotated with an availability attribute, this property captures that. + /// It differs from the above `AvailabilityInfo` by being independent of the deployment target, + /// and is used for providing availability attribute redundancy warning diagnostics. + AvailabilityContext ExplicitAvailabilityInfo; + std::vector Children; TypeRefinementContext(ASTContext &Ctx, IntroNode Node, TypeRefinementContext *Parent, SourceRange SrcRange, - const AvailabilityContext &Info); + const AvailabilityContext &Info, + const AvailabilityContext &ExplicitInfo); public: @@ -172,6 +180,7 @@ class TypeRefinementContext { static TypeRefinementContext *createForDecl(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent, const AvailabilityContext &Info, + const AvailabilityContext &ExplicitInfo, SourceRange SrcRange); /// Create a refinement context for the Then branch of the given IfStmt. @@ -245,6 +254,12 @@ class TypeRefinementContext { return AvailabilityInfo; } + /// Returns the information on what availability was specified by the programmer + /// on this context (if any). + const AvailabilityContext &getExplicitAvailabilityInfo() const { + return ExplicitAvailabilityInfo; + } + /// Adds a child refinement context. void addChild(TypeRefinementContext *Child) { assert(Child->getSourceRange().isValid()); @@ -261,11 +276,6 @@ class TypeRefinementContext { void print(raw_ostream &OS, SourceManager &SrcMgr, unsigned Indent = 0) const; static StringRef getReasonName(Reason R); - - // Only allow allocation of TypeRefinementContext using the allocator in - // ASTContext. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(TypeRefinementContext)); }; } // end namespace swift diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index c04cf12fca565..cf3e61405ce8d 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -48,7 +48,8 @@ enum : unsigned { NumTypeReprKindBits = countBitsUsed(static_cast(TypeReprKind::Last_TypeRepr)) }; /// Representation of a type as written in source. -class alignas(1 << TypeReprAlignInBits) TypeRepr { +class alignas(1 << TypeReprAlignInBits) TypeRepr + : public ASTAllocated { TypeRepr(const TypeRepr&) = delete; void operator=(const TypeRepr&) = delete; @@ -135,6 +136,10 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr { SourceLoc getEndLoc() const; SourceRange getSourceRange() const; + /// Find an @unchecked attribute and return its source location, or return + /// an invalid source location if there is no such attribute. + SourceLoc findUncheckedAttrLoc() const; + /// Is this type grammatically a type-simple? inline bool isSimple() const; // bottom of this file @@ -146,19 +151,21 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr { return walk(walker); } - //*** Allocation Routines ************************************************/ - - void *operator new(size_t bytes, const ASTContext &C, - unsigned Alignment = alignof(TypeRepr)); + /// Look through the given type and its children to find a type for + /// which the given predicate returns true. + /// + /// \param pred A predicate function object. It should return true if the + /// given type node satisfies the criteria. + /// + /// \returns true if the predicate returns true for the given type or any of + /// its children. + bool findIf(llvm::function_ref pred); - void *operator new(size_t bytes, void *data) { - assert(data); - return data; - } + /// Check recursively whether this type repr or any of its decendants are + /// opaque return type reprs. + bool hasOpaque(); - // Make placement new and vanilla new/delete illegal for TypeReprs. - void *operator new(size_t bytes) = delete; - void operator delete(void *data) = delete; + //*** Allocation Routines ************************************************/ void print(raw_ostream &OS, const PrintOptions &Opts = PrintOptions()) const; void print(ASTPrinter &Printer, const PrintOptions &Opts) const; @@ -939,7 +946,8 @@ class SpecifierTypeRepr : public TypeRepr { static bool classof(const TypeRepr *T) { return T->getKind() == TypeReprKind::InOut || T->getKind() == TypeReprKind::Shared || - T->getKind() == TypeReprKind::Owned; + T->getKind() == TypeReprKind::Owned || + T->getKind() == TypeReprKind::Isolated; } static bool classof(const SpecifierTypeRepr *T) { return true; } @@ -995,6 +1003,21 @@ class OwnedTypeRepr : public SpecifierTypeRepr { static bool classof(const OwnedTypeRepr *T) { return true; } }; +/// An 'isolated' type. +/// \code +/// x : isolated Actor +/// \endcode +class IsolatedTypeRepr : public SpecifierTypeRepr { +public: + IsolatedTypeRepr(TypeRepr *Base, SourceLoc InOutLoc) + : SpecifierTypeRepr(TypeReprKind::Isolated, Base, InOutLoc) {} + + static bool classof(const TypeRepr *T) { + return T->getKind() == TypeReprKind::Isolated; + } + static bool classof(const IsolatedTypeRepr *T) { return true; } +}; + /// A TypeRepr for a known, fixed type. /// /// Fixed type representations should be used sparingly, in places @@ -1045,13 +1068,13 @@ class SILBoxTypeReprField { TypeRepr *getFieldType() const { return FieldTypeAndMutable.getPointer(); } bool isMutable() const { return FieldTypeAndMutable.getInt(); } }; - -/// TypeRepr for opaque return types. + +/// A TypeRepr for anonymous opaque return types. /// -/// This can occur in the return position of a function declaration, or the -/// top-level type of a property, to specify that the concrete return type -/// should be abstracted from callers, given a set of generic constraints that -/// the concrete return type satisfies: +/// This can occur in the return type of a function declaration, or the type of +/// a property, to specify that the concrete return type should be abstracted +/// from callers, given a set of generic constraints that the concrete return +/// type satisfies: /// /// func foo() -> some Collection { return [1,2,3] } /// var bar: some SignedInteger = 1 @@ -1085,6 +1108,41 @@ class OpaqueReturnTypeRepr : public TypeRepr { friend class TypeRepr; }; +/// A TypeRepr for a type with a generic parameter list of named opaque return +/// types. +/// +/// This can occur only as the return type of a function declaration, or the +/// type of a property, to specify types which should be abstracted from +/// callers, given a set of generic constraints that the concrete types satisfy: +/// +/// func foo() -> T { return [1] } +class NamedOpaqueReturnTypeRepr : public TypeRepr { + TypeRepr *Base; + GenericParamList *GenericParams; + +public: + NamedOpaqueReturnTypeRepr(TypeRepr *Base, GenericParamList *GenericParams) + : TypeRepr(TypeReprKind::NamedOpaqueReturn), Base(Base), + GenericParams(GenericParams) { + assert(Base && GenericParams); + } + + TypeRepr *getBase() const { return Base; } + GenericParamList *getGenericParams() const { return GenericParams; } + + static bool classof(const TypeRepr *T) { + return T->getKind() == TypeReprKind::NamedOpaqueReturn; + } + static bool classof(const NamedOpaqueReturnTypeRepr *T) { return true; } + +private: + SourceLoc getStartLocImpl() const; + SourceLoc getEndLocImpl() const; + SourceLoc getLocImpl() const; + void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + friend class TypeRepr; +}; + /// TypeRepr for a user-specified placeholder (essentially, a user-facing /// representation of an anonymous type variable. /// @@ -1211,6 +1269,7 @@ inline bool TypeRepr::isSimple() const { case TypeReprKind::InOut: case TypeReprKind::Composition: case TypeReprKind::OpaqueReturn: + case TypeReprKind::NamedOpaqueReturn: return false; case TypeReprKind::SimpleIdent: case TypeReprKind::GenericIdent: @@ -1226,6 +1285,7 @@ inline bool TypeRepr::isSimple() const { case TypeReprKind::SILBox: case TypeReprKind::Shared: case TypeReprKind::Owned: + case TypeReprKind::Isolated: case TypeReprKind::Placeholder: return true; } diff --git a/include/swift/AST/TypeReprNodes.def b/include/swift/AST/TypeReprNodes.def index aa6b38c269529..37f9955b12adb 100644 --- a/include/swift/AST/TypeReprNodes.def +++ b/include/swift/AST/TypeReprNodes.def @@ -54,11 +54,13 @@ TYPEREPR(Composition, TypeRepr) TYPEREPR(Metatype, TypeRepr) TYPEREPR(Protocol, TypeRepr) TYPEREPR(OpaqueReturn, TypeRepr) +TYPEREPR(NamedOpaqueReturn, TypeRepr) TYPEREPR(Placeholder, TypeRepr) ABSTRACT_TYPEREPR(Specifier, TypeRepr) TYPEREPR(InOut, SpecifierTypeRepr) TYPEREPR(Shared, SpecifierTypeRepr) TYPEREPR(Owned, SpecifierTypeRepr) + TYPEREPR(Isolated, SpecifierTypeRepr) TYPEREPR(Fixed, TypeRepr) TYPEREPR(SILBox, TypeRepr) LAST_TYPEREPR(SILBox) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 105aee97bfeb0..b6450c108dc74 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -18,6 +18,7 @@ #ifndef SWIFT_TYPES_H #define SWIFT_TYPES_H +#include "swift/AST/ASTAllocated.h" #include "swift/AST/AutoDiff.h" #include "swift/AST/DeclContext.h" #include "swift/AST/ExtInfo.h" @@ -293,7 +294,8 @@ using TypeMatchOptions = OptionSet; /// Base class for all types which describe the Swift and SIL ASTs. /// /// See TypeNodes.def for a succinct description of the full class hierarchy. -class alignas(1 << TypeAlignInBits) TypeBase { +class alignas(1 << TypeAlignInBits) TypeBase + : public ASTAllocated::type> { friend class ASTContext; TypeBase(const TypeBase&) = delete; @@ -492,13 +494,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { TypeBase *getWithoutSyntaxSugar(); /// getASTContext - Return the ASTContext that this type belongs to. - ASTContext &getASTContext() { - // If this type is canonical, it has the ASTContext in it. - if (isCanonical()) - return *const_cast(Context); - // If not, canonicalize it to get the Context. - return *const_cast(getCanonicalType()->Context); - } + ASTContext &getASTContext(); /// isEqual - Return true if these two types are equal, ignoring sugar. /// @@ -1122,10 +1118,22 @@ class alignas(1 << TypeAlignInBits) TypeBase { TypeSubstitutionMap getMemberSubstitutions(const ValueDecl *member, GenericEnvironment *genericEnv=nullptr); + /// Retrieve the type of the given property as seen through the given base + /// type, substituting generic arguments where necessary. This is the same as + /// the more general overload of \c TypeBase::getTypeOfMember, but defaults to + /// the property's interface type for the \c memberType. + /// + /// \param module The module in which the substitution occurs. + /// + /// \param member The property whose type we are substituting. + /// + /// \returns The resulting property type. + Type getTypeOfMember(ModuleDecl *module, const VarDecl *member); + /// Retrieve the type of the given member as seen through the given base /// type, substituting generic arguments where necessary. /// - /// This routine allows one to take a concrete type (the "this" type) and + /// This routine allows one to take a concrete type (the "self" type) and /// and a member of that type (or one of its superclasses), then determine /// what type an access to that member through the base type will have. /// For example, given: @@ -1136,20 +1144,23 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// } /// \endcode /// - /// Given the type \c Vector and the member \c add, the resulting type - /// of the member will be \c (self : Vector) -> (value : Int) -> (). + /// Given the type \c Vector, the member \c add, and its method interface + /// type (value: T) -> Void the resulting type will be (value: Int) -> Void. /// /// \param module The module in which the substitution occurs. /// /// \param member The member whose type we are substituting. /// - /// \param memberType The type of the member, in which archetypes will be - /// replaced by the generic arguments provided by the base type. If null, - /// the member's type will be used. + /// \param memberType The type of the member in which generic parameters will + /// be replaced by the generic arguments provided by the base type. Note this + /// must not be a GenericFunctionType. For a method, either strip the self + /// parameter and generic signature using e.g \c getMethodInterfaceType, or + /// use \c substGenericArgs if you want to substitute types for any of the + /// method's generic parameters. /// - /// \returns the resulting member type. + /// \returns The resulting member type. Type getTypeOfMember(ModuleDecl *module, const ValueDecl *member, - Type memberType = Type()); + Type memberType); /// Get the type of a superclass member as seen from the subclass, /// substituting generic parameters, dynamic Self return, and the @@ -1223,17 +1234,6 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// return `None`. Optional getAutoDiffTangentSpace(LookupConformanceFn lookupConformance); - -private: - // Make vanilla new/delete illegal for Types. - void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() = delete; -public: - // Only allow allocation of Types using the allocator in ASTContext - // or by doing a placement new. - void *operator new(size_t bytes, const ASTContext &ctx, - AllocationArena arena, unsigned alignment = 8); - void *operator new(size_t Bytes, void *Mem) throw() { return Mem; } }; /// AnyGenericType - This abstract class helps types ensure that fields @@ -1816,12 +1816,6 @@ class SugarType : public TypeBase { Bits.SugarType.HasCachedType = true; } - void setUnderlyingType(Type type) { - assert(!Bits.SugarType.HasCachedType && "Cached type already set"); - Bits.SugarType.HasCachedType = true; - UnderlyingType = type.getPointer(); - } - public: /// Remove one level of top-level sugar from this type. Type getSinglyDesugaredTypeSlow(); @@ -1929,10 +1923,11 @@ class ParameterTypeFlags { OwnershipShift = 3, Ownership = 7 << OwnershipShift, NoDerivative = 1 << 6, - NumBits = 7 + Isolated = 1 << 7, + NumBits = 8 }; OptionSet value; - static_assert(NumBits < 8*sizeof(OptionSet), "overflowed"); + static_assert(NumBits <= 8*sizeof(OptionSet), "overflowed"); ParameterTypeFlags(OptionSet val) : value(val) {} @@ -1943,17 +1938,18 @@ class ParameterTypeFlags { } ParameterTypeFlags(bool variadic, bool autoclosure, bool nonEphemeral, - ValueOwnership ownership, bool noDerivative) + ValueOwnership ownership, bool isolated, bool noDerivative) : value((variadic ? Variadic : 0) | (autoclosure ? AutoClosure : 0) | (nonEphemeral ? NonEphemeral : 0) | uint8_t(ownership) << OwnershipShift | + (isolated ? Isolated : 0) | (noDerivative ? NoDerivative : 0)) {} /// Create one from what's present in the parameter type inline static ParameterTypeFlags fromParameterType(Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral, ValueOwnership ownership, - bool isNoDerivative); + bool isolated, bool isNoDerivative); bool isNone() const { return !value; } bool isVariadic() const { return value.contains(Variadic); } @@ -1962,6 +1958,7 @@ class ParameterTypeFlags { bool isInOut() const { return getValueOwnership() == ValueOwnership::InOut; } bool isShared() const { return getValueOwnership() == ValueOwnership::Shared;} bool isOwned() const { return getValueOwnership() == ValueOwnership::Owned; } + bool isIsolated() const { return value.contains(Isolated); } bool isNoDerivative() const { return value.contains(NoDerivative); } ValueOwnership getValueOwnership() const { @@ -2005,6 +2002,12 @@ class ParameterTypeFlags { : value - ParameterTypeFlags::NonEphemeral); } + ParameterTypeFlags withIsolated(bool isolated) const { + return ParameterTypeFlags(isolated + ? value | ParameterTypeFlags::Isolated + : value - ParameterTypeFlags::Isolated); + } + ParameterTypeFlags withNoDerivative(bool noDerivative) const { return ParameterTypeFlags(noDerivative ? value | ParameterTypeFlags::NoDerivative @@ -2078,7 +2081,7 @@ class YieldTypeFlags { return ParameterTypeFlags(/*variadic*/ false, /*autoclosure*/ false, /*nonEphemeral*/ false, getValueOwnership(), - /*noDerivative*/ false); + /*isolated*/ false, /*noDerivative*/ false); } bool operator ==(const YieldTypeFlags &other) const { @@ -2857,6 +2860,9 @@ class AnyFunctionType : public TypeBase { /// Whether the parameter is marked '@_nonEphemeral' bool isNonEphemeral() const { return Flags.isNonEphemeral(); } + /// Whether the parameter is 'isolated'. + bool isIsolated() const { return Flags.isIsolated(); } + /// Whether the parameter is marked '@noDerivative'. bool isNoDerivative() const { return Flags.isNoDerivative(); } @@ -2997,19 +3003,16 @@ class AnyFunctionType : public TypeBase { } public: - /// Break an input type into an array of \c AnyFunctionType::Params. - static void decomposeInput(Type type, + /// Break a tuple or paren type into an array of \c AnyFunctionType::Params. + static void decomposeTuple(Type type, SmallVectorImpl &result); - /// Take an array of parameters and turn it into an input type. - /// - /// The result type is only there as a way to extract the ASTContext when - /// needed. - static Type composeInput(ASTContext &ctx, ArrayRef params, + /// Take an array of parameters and turn it into a tuple or paren type. + static Type composeTuple(ASTContext &ctx, ArrayRef params, bool canonicalVararg); - static Type composeInput(ASTContext &ctx, CanParamArrayRef params, + static Type composeTuple(ASTContext &ctx, CanParamArrayRef params, bool canonicalVararg) { - return composeInput(ctx, params.getOriginalArray(), canonicalVararg); + return composeTuple(ctx, params.getOriginalArray(), canonicalVararg); } /// Given two arrays of parameters determine if they are equal in their @@ -3497,7 +3500,8 @@ class GenericFunctionType final : public AnyFunctionType, /// Substitute the given generic arguments into this generic /// function type and return the resulting non-generic type. - FunctionType *substGenericArgs(SubstitutionMap subs); + FunctionType *substGenericArgs(SubstitutionMap subs, + SubstOptions options = None); FunctionType *substGenericArgs(llvm::function_ref substFn) const; void Profile(llvm::FoldingSetNodeID &ID) { @@ -5026,6 +5030,21 @@ class DictionaryType : public SyntaxSugarType { } }; +/// The type T..., which is sugar for a sequence of argument values. +class VariadicSequenceType : public UnarySyntaxSugarType { + VariadicSequenceType(const ASTContext &ctx, Type base, + RecursiveTypeProperties properties) + : UnarySyntaxSugarType(TypeKind::VariadicSequence, ctx, base, properties) {} + +public: + /// Return a uniqued variadic sequence type with the specified base type. + static VariadicSequenceType *get(Type baseTy); + + static bool classof(const TypeBase *T) { + return T->getKind() == TypeKind::VariadicSequence; + } +}; + /// ProtocolType - A protocol type describes an abstract interface implemented /// by another type. class ProtocolType : public NominalType { @@ -5453,13 +5472,13 @@ class OpaqueTypeArchetypeType final : public ArchetypeType, GenericEnvironment *Environment; public: - /// Get - /// Get an opaque archetype representing the underlying type of the given - /// opaque type decl. - static OpaqueTypeArchetypeType *get(OpaqueTypeDecl *Decl, + /// opaque type decl's opaque param with ordinal `ordinal`. For example, in + /// `(some P, some Q)`, `some P`'s type param would have ordinal 0 and `some + /// Q`'s type param would have ordinal 1. + static OpaqueTypeArchetypeType *get(OpaqueTypeDecl *Decl, unsigned ordinal, SubstitutionMap Substitutions); - + OpaqueTypeDecl *getDecl() const { return OpaqueDecl; } @@ -5490,7 +5509,7 @@ class OpaqueTypeArchetypeType final : public ArchetypeType, /// /// then the underlying type of `some P` would be ordinal 0, and `some Q` would be ordinal 1. unsigned getOrdinal() const { - // TODO: multiple opaque types + // TODO [OPAQUE SUPPORT]: multiple opaque types return 0; } @@ -5982,6 +6001,22 @@ class PlaceholderType : public TypeBase { }; DEFINE_EMPTY_CAN_TYPE_WRAPPER(PlaceholderType, Type) +/// getASTContext - Return the ASTContext that this type belongs to. +inline ASTContext &TypeBase::getASTContext() { + // If this type is canonical, it has the ASTContext in it. + if (isCanonical()) + return *const_cast(Context); + + // getCanonicalType() on GenericFunctionType is very expensive; + // instead we can more easily fish the ASTContext out of any + // structural sub-component. + if (auto *genericFnType = dyn_cast(this)) + return genericFnType->getGenericParams()[0]->getASTContext(); + + // If not, canonicalize it to get the Context. + return *const_cast(getCanonicalType()->Context); +} + inline bool TypeBase::isTypeVariableOrMember() { if (is()) return true; @@ -6204,7 +6239,7 @@ inline bool CanType::isActuallyCanonicalOrNull() const { inline Type TupleTypeElt::getVarargBaseTy() const { TypeBase *T = getType().getPointer(); - if (auto *AT = dyn_cast(T)) + if (auto *AT = dyn_cast(T)) return AT->getBaseType(); if (auto *BGT = dyn_cast(T)) { // It's the stdlib Array. @@ -6227,7 +6262,7 @@ inline TupleTypeElt TupleTypeElt::getWithType(Type T) const { /// Create one from what's present in the parameter decl and type inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral, - ValueOwnership ownership, bool isNoDerivative) { + ValueOwnership ownership, bool isolated, bool isNoDerivative) { // FIXME(Remove InOut): The last caller that needs this is argument // decomposition. Start by enabling the assertion there and fixing up those // callers, then remove this, then remove @@ -6237,7 +6272,8 @@ inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( ownership == ValueOwnership::InOut); ownership = ValueOwnership::InOut; } - return {isVariadic, isAutoClosure, isNonEphemeral, ownership, isNoDerivative}; + return {isVariadic, isAutoClosure, isNonEphemeral, ownership, isolated, + isNoDerivative}; } inline const Type *BoundGenericType::getTrailingObjectsPointer() const { diff --git a/include/swift/AST/USRGeneration.h b/include/swift/AST/USRGeneration.h index 7b96c4d3e3c27..5f799a06abd02 100644 --- a/include/swift/AST/USRGeneration.h +++ b/include/swift/AST/USRGeneration.h @@ -21,6 +21,8 @@ #include "swift/Basic/LLVM.h" +#include + namespace swift { class Decl; class AbstractStorageDecl; @@ -61,6 +63,9 @@ bool printExtensionUSR(const ExtensionDecl *ED, raw_ostream &OS); /// \returns true if it failed, false on success. bool printDeclUSR(const Decl *D, raw_ostream &OS); +/// Demangle a mangle-name-based USR to a human readable name. +std::string demangleUSR(StringRef mangled); + } // namespace ide } // namespace swift diff --git a/include/swift/Basic/Compiler.h b/include/swift/Basic/Compiler.h index 823fc35f7f64c..e181a24139d82 100644 --- a/include/swift/Basic/Compiler.h +++ b/include/swift/Basic/Compiler.h @@ -128,4 +128,38 @@ #define SWIFT_ASSERT_ONLY(...) do { __VA_ARGS__; } while (false) #endif +#if defined(__LP64__) || defined(_WIN64) +#define SWIFT_POINTER_IS_8_BYTES 1 +#define SWIFT_POINTER_IS_4_BYTES 0 +#else +// TODO: consider supporting 16-bit targets +#define SWIFT_POINTER_IS_8_BYTES 0 +#define SWIFT_POINTER_IS_4_BYTES 1 +#endif + +// Produce a string literal for the raw argument tokens. +#define SWIFT_STRINGIZE_RAW(TOK) #TOK + +// Produce a string literal for the macro-expanded argument tokens. +#define SWIFT_STRINGIZE_EXPANDED(TOK) SWIFT_STRINGIZE_RAW(TOK) + +#if defined(__USER_LABEL_PREFIX__) +#define SWIFT_SYMBOL_PREFIX_STRING \ + SWIFT_STRINGIZE_EXPANDED(__USER_LABEL_PREFIX__) +#else +// Clang and GCC always define __USER_LABEL_PREFIX__, so this should +// only come up with MSVC, and Windows doesn't use a prefix. +#define SWIFT_SYMBOL_PREFIX_STRING "" +#endif + +// An attribute to override the symbol name of a declaration. +// This does not compensate for platform symbol prefixes; for that, +// use SWIFT_ASM_LABEL_WITH_PREFIX. +// +// This only actually works on Clang or GCC; MSVC does not provide +// an attribute to change the asm label. +#define SWIFT_ASM_LABEL_RAW(STRING) __asm__(STRING) +#define SWIFT_ASM_LABEL_WITH_PREFIX(STRING) \ + SWIFT_ASM_LABEL_RAW(SWIFT_SYMBOL_PREFIX_STRING STRING) + #endif // SWIFT_BASIC_COMPILER_H diff --git a/include/swift/Basic/DAGNodeWorklist.h b/include/swift/Basic/DAGNodeWorklist.h new file mode 100644 index 0000000000000..a168862a4775c --- /dev/null +++ b/include/swift/Basic/DAGNodeWorklist.h @@ -0,0 +1,64 @@ +//===--- DAGNodeWorklist.h --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BASIC_DAGNODEWORKLIST_H +#define SWIFT_BASIC_DAGNODEWORKLIST_H + +#include "swift/Basic/LLVM.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" + +/// Worklist of pointer-like things that have an invalid default value. This not +/// only avoids duplicates in the worklist, but also avoids revisiting +/// already-popped nodes. This makes it suitable for DAG traversal. This can +/// also be used within hybrid worklist/recursive traversal by recording the +/// size of the worklist at each level of recursion. +/// +/// The primary API has two methods: intialize() and pop(). Others are provided +/// for flexibility. +template struct DAGNodeWorklist { + llvm::SmallPtrSet nodeVisited; + llvm::SmallVector nodeVector; + + DAGNodeWorklist() = default; + + DAGNodeWorklist(const DAGNodeWorklist &) = delete; + + void initialize(T t) { + clear(); + insert(t); + } + + template void initializeRange(R &&range) { + clear(); + nodeVisited.insert(range.begin(), range.end()); + nodeVector.append(range.begin(), range.end()); + } + + T pop() { return empty() ? T() : nodeVector.pop_back_val(); } + + bool empty() const { return nodeVector.empty(); } + + unsigned size() const { return nodeVector.size(); } + + void clear() { + nodeVector.clear(); + nodeVisited.clear(); + } + + void insert(T t) { + if (nodeVisited.insert(t).second) + nodeVector.push_back(t); + } +}; + +#endif // SWIFT_BASIC_DAGNODEWORKLIST_H diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index e042b97949a74..30eb7fea786ff 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -39,6 +39,7 @@ LANGUAGE_FEATURE(AsyncAwait, 296, "async/await", true) LANGUAGE_FEATURE(EffectfulProp, 310, "Effectful properties", true) LANGUAGE_FEATURE(MarkerProtocol, 0, "@_marker protocol", true) LANGUAGE_FEATURE(Actors, 0, "actors", true) +LANGUAGE_FEATURE(Actors2, 0, "actors #2 (TEMPORARY)", true) LANGUAGE_FEATURE(ConcurrentFunctions, 0, "@concurrent functions", true) LANGUAGE_FEATURE(RethrowsProtocol, 0, "@rethrows protocol", true) LANGUAGE_FEATURE(GlobalActors, 0, "Global actors", langOpts.EnableExperimentalConcurrency) @@ -46,8 +47,12 @@ LANGUAGE_FEATURE(BuiltinJob, 0, "Builtin.Job type", true) LANGUAGE_FEATURE(Sendable, 0, "Sendable and @Sendable", true) LANGUAGE_FEATURE(BuiltinExecutor, 0, "Builtin.Executor type", true) LANGUAGE_FEATURE(BuiltinContinuation, 0, "Continuation builtins", true) -LANGUAGE_FEATURE(BuiltinTaskGroup, 0, "TaskGroup builtins", true) +LANGUAGE_FEATURE(BuiltinHopToActor, 0, "Builtin.HopToActor", true) +LANGUAGE_FEATURE(BuiltinTaskGroupWithArgument, 0, "TaskGroup builtins", true) LANGUAGE_FEATURE(InheritActorContext, 0, "@_inheritActorContext attribute", true) +LANGUAGE_FEATURE(ImplicitSelfCapture, 0, "@_implicitSelfCapture attribute", true) LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins", true) +LANGUAGE_FEATURE(BuiltinBuildMainExecutor, 0, "MainActor executor building builtin", true) +LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroup, 0, "MainActor executor building builtin", true) #undef LANGUAGE_FEATURE diff --git a/stdlib/public/SwiftShims/NSUndoManagerShims.h b/include/swift/Basic/InitializeLibSwift.h similarity index 50% rename from stdlib/public/SwiftShims/NSUndoManagerShims.h rename to include/swift/Basic/InitializeLibSwift.h index 5d4c706196d27..6e2838e0022e6 100644 --- a/stdlib/public/SwiftShims/NSUndoManagerShims.h +++ b/include/swift/Basic/InitializeLibSwift.h @@ -1,8 +1,8 @@ -//===--- NSUndoManagerShims.h - Foundation decl. for NSUndoManager overlay ===// +//===--- InitializeLibSwift.h -----------------------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2021 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 @@ -10,12 +10,17 @@ // //===----------------------------------------------------------------------===// -#import "FoundationShimSupport.h" +#ifndef SWIFT_BASIC_INITIALIZELIBSWIFT_H +#define SWIFT_BASIC_INITIALIZELIBSWIFT_H -NS_BEGIN_DECLS +#ifdef __cplusplus +extern "C" { +#endif -NS_INLINE void __NSUndoManagerRegisterWithTargetHandler(NSUndoManager * self_, id target, void (^handler)(id)) { - [self_ registerUndoWithTarget:target handler:handler]; +void initializeLibSwift(); + +#ifdef __cplusplus } +#endif -NS_END_DECLS +#endif // SWIFT_BASIC_INITIALIZELIBSWIFT_H diff --git a/include/swift/Basic/LLVM.h b/include/swift/Basic/LLVM.h index 40ab0fe664509..35eb452f2459d 100644 --- a/include/swift/Basic/LLVM.h +++ b/include/swift/Basic/LLVM.h @@ -73,6 +73,7 @@ namespace llvm { namespace swift { // Casting operators. using llvm::isa; + using llvm::isa_and_nonnull; using llvm::cast; using llvm::dyn_cast; using llvm::dyn_cast_or_null; diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 24ab5ae287191..f8d161d346e51 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -36,6 +36,8 @@ namespace swift { + enum class DiagnosticBehavior : uint8_t; + /// Kind of implicit platform conditions. enum class PlatformConditionKind { #define PLATFORM_CONDITION(LABEL, IDENTIFIER) LABEL, @@ -69,6 +71,25 @@ namespace swift { Other }; + enum class AccessNoteDiagnosticBehavior : uint8_t { + Ignore, + RemarkOnFailure, + RemarkOnFailureOrSuccess, + ErrorOnFailureRemarkOnSuccess + }; + + /// Value for LangOptions::EnableRequirementMachine. + enum class RequirementMachineMode { + /// Use the GenericSignatureBuilder for all queries. + Disabled = 0, + + /// Use the RequirementMachine for all queries. + Enabled = 1, + + /// Use both and assert if the results do not match. + Verify = 2 + }; + /// A collection of options that affect the language dialect and /// provide compiler debugging facilities. class LangOptions final { @@ -90,6 +111,16 @@ namespace swift { /// performed. llvm::Optional TargetVariant; + /// The target triple to instantiate the internal clang instance. + /// When not specified, the compiler will use the value of -target to + /// instantiate the clang instance. + /// This is mainly used to avoid lowering the target triple to use for clang when + /// importing a .swiftinterface whose -target value may be different from + /// the loading module. + /// The lowering triple may result in multiple versions of the same Clang + /// modules being built. + llvm::Optional ClangTarget; + /// The SDK version, if known. Optional SDKVersion; @@ -115,6 +146,12 @@ namespace swift { /// Should conformance availability violations be diagnosed as errors? bool EnableConformanceAvailabilityErrors = false; + /// Should potential unavailability on enum cases be downgraded to a warning? + bool WarnOnPotentiallyUnavailableEnumCase = false; + + /// Should the editor placeholder error be downgraded to a warning? + bool WarnOnEditorPlaceholder = false; + /// Maximum number of typo corrections we are allowed to perform. /// This is disabled by default until we can get typo-correction working within acceptable performance bounds. unsigned TypoCorrectionLimit = 0; @@ -264,8 +301,13 @@ namespace swift { /// Enable experimental concurrency model. bool EnableExperimentalConcurrency = false; - /// Enable experimental asyncHandler support. - bool EnableExperimentalAsyncHandler = false; + /// Enable experimental support for named opaque result types, e.g. + /// `func f() -> T`. + bool EnableExperimentalNamedOpaqueTypes = false; + + /// Enable experimental support for structural opaque result types, e.g. + /// `func f() -> (some P)?`. + bool EnableExperimentalStructuralOpaqueTypes = false; /// Enable experimental flow-sensitive concurrent captures. bool EnableExperimentalFlowSensitiveConcurrentCaptures = false; @@ -273,9 +315,12 @@ namespace swift { /// Enable inference of Sendable conformances for public types. bool EnableInferPublicSendable = false; + /// Enable experimental 'distributed' actors and functions. + bool EnableExperimentalDistributed = false; + /// Disable the implicit import of the _Concurrency module. bool DisableImplicitConcurrencyModuleImport = - !SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY; + !SWIFT_IMPLICIT_CONCURRENCY_IMPORT; /// Should we check the target OSs of serialized modules to see that they're /// new enough? @@ -291,14 +336,6 @@ namespace swift { /// behavior. This is a staging flag, and will be removed in the future. bool EnableNewOperatorLookup = false; - /// Whether to enable the "fuzzy" forward-scanning behavior for trailing - /// closure matching, which skips over defaulted closure parameters - /// to match later (non-defaulted) closure parameters - /// - /// This is a backward-compatibility hack for unlabeled trailing closures, - /// to be disabled in Swift 6+. - bool EnableFuzzyForwardScanTrailingClosureMatching = true; - /// Use Clang function types for computing canonical types. /// If this option is false, the clang function types will still be computed /// but will not be used for checking type equality. @@ -342,6 +379,17 @@ namespace swift { std::shared_ptr OptimizationRemarkPassedPattern; std::shared_ptr OptimizationRemarkMissedPattern; + /// How should we emit diagnostics about access notes? + AccessNoteDiagnosticBehavior AccessNoteBehavior = + AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess; + + DiagnosticBehavior getAccessNoteFailureLimit() const; + + bool shouldRemarkOnAccessNoteSuccess() const { + return AccessNoteBehavior >= + AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess; + } + /// Whether collect tokens during parsing for syntax coloring. bool CollectParsedToken = false; @@ -407,6 +455,27 @@ namespace swift { ASTVerifierOverrideKind ASTVerifierOverride = ASTVerifierOverrideKind::NoOverride; + /// Whether the new experimental generics implementation is enabled. + RequirementMachineMode EnableRequirementMachine = + RequirementMachineMode::Disabled; + + /// Enables dumping rewrite systems from the requirement machine. + bool DumpRequirementMachine = false; + + /// Enables statistics output from the requirement machine. + bool AnalyzeRequirementMachine = false; + + /// Enables fine-grained debug output from the requirement machine. + std::string DebugRequirementMachine; + + /// Maximum iteration count for requirement machine confluent completion + /// algorithm. + unsigned RequirementMachineStepLimit = 2000; + + /// Maximum term length for requirement machine confluent completion + /// algorithm. + unsigned RequirementMachineDepthLimit = 10; + /// Sets the target we are building for and updates platform conditions /// to match. /// @@ -543,6 +612,9 @@ namespace swift { /// Flags for developers /// + /// Debug the generic signatures computed by the generic signature builder. + bool DebugGenericSignatures = false; + /// Whether we are debugging the constraint solver. /// /// This option enables verbose debugging output from the constraint @@ -557,9 +629,6 @@ namespace swift { /// Should be stored sorted. llvm::SmallVector DebugConstraintSolverOnLines; - /// Debug the generic signatures computed by the generic signature builder. - bool DebugGenericSignatures = false; - /// Triggers llvm fatal_error if typechecker tries to typecheck a decl or an /// identifier reference with the provided prefix name. /// This is for testing purposes. diff --git a/include/swift/Basic/Malloc.h b/include/swift/Basic/Malloc.h index 4f937dab803f5..4d40d079b18da 100644 --- a/include/swift/Basic/Malloc.h +++ b/include/swift/Basic/Malloc.h @@ -34,11 +34,11 @@ inline void *AlignedAlloc(size_t size, size_t align) { if (align < sizeof(void*)) align = sizeof(void*); - void *r; #if defined(_WIN32) - r = _aligned_malloc(size, align); + void *r = _aligned_malloc(size, align); assert(r && "_aligned_malloc failed"); #else + void *r = nullptr; int res = posix_memalign(&r, align, size); assert(res == 0 && "posix_memalign failed"); (void)res; // Silence the unused variable warning. diff --git a/include/swift/Basic/SourceLoc.h b/include/swift/Basic/SourceLoc.h index d7b68c787aa13..c638e50ee72e2 100644 --- a/include/swift/Basic/SourceLoc.h +++ b/include/swift/Basic/SourceLoc.h @@ -123,6 +123,14 @@ class SourceRange { /// includes both this range and the other one. void widen(SourceRange Other); + /// Checks whether this range contains the given location. Note that the given + /// location should correspond to the start of a token, since locations inside + /// the last token may be considered outside the range by this function. + bool contains(SourceLoc Loc) const; + + /// Checks whether this range overlaps with the given range. + bool overlaps(SourceRange Other) const; + bool operator==(const SourceRange &other) const { return Start == other.Start && End == other.End; } diff --git a/include/swift/Basic/Statistic.h b/include/swift/Basic/Statistic.h index 984c19ce790ec..549238eba77fd 100644 --- a/include/swift/Basic/Statistic.h +++ b/include/swift/Basic/Statistic.h @@ -70,6 +70,10 @@ class Stmt; class TypeRepr; struct FingerprintAndMembers; +/// Get the number of instructions executed since this process was launched. +/// Returns 0 if the number of instructions executed could not be determined. +uint64_t getInstructionsExecuted(); + // There are a handful of cases where the swift compiler can introduce // counter-measurement noise via nondeterminism, especially via // parallelism; inhibiting all such cases reliably using existing avenues diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index a0db12f991109..8d31dfe33c084 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -220,10 +220,27 @@ FRONTEND_STATISTIC(Sema, NumAccessorsSynthesized) /// Number of synthesized accessor bodies. FRONTEND_STATISTIC(Sema, NumAccessorBodiesSynthesized) +/// Number of requirement machines constructed. Rough proxy for +/// amount of work the requirement machine does analyzing type signatures. +FRONTEND_STATISTIC(Sema, NumRequirementMachines) + +/// Number of new rules added by Knuth-Bendix completion procedure. +FRONTEND_STATISTIC(Sema, NumRequirementMachineCompletionSteps) + +/// Number of new rules added by concrete term unification. +FRONTEND_STATISTIC(Sema, NumRequirementMachineUnifiedConcreteTerms) + /// Number of generic signature builders constructed. Rough proxy for /// amount of work the GSB does analyzing type signatures. FRONTEND_STATISTIC(Sema, NumGenericSignatureBuilders) +/// Number of steps in the GSB's redundant requirements algorithm, which is in +/// the worst-case exponential. +FRONTEND_STATISTIC(Sema, NumRedundantRequirementSteps) + +/// Number of conformance access paths we had to compute. +FRONTEND_STATISTIC(Sema, NumConformanceAccessPathsRecorded) + /// Number of lazy requirement signatures registered. FRONTEND_STATISTIC(Sema, NumLazyRequirementSignatures) diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index e72f0ecb02b10..9e51fdfb65010 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -140,19 +140,12 @@ struct SupplementaryOutputPaths { /// \sa ModuleInterfaceOutputPath std::string PrivateModuleInterfaceOutputPath; - /// The path to a .c file where we should declare $ld$add symbols for those - /// symbols moved to the current module. - /// When symbols are moved to this module, this module declares them as HIDE - /// for the OS versions prior to when the move happened. On the other hand, the - /// original module should ADD them for these OS versions. An executable - /// can choose the right library to link against depending on the deployment target. - /// This is a walk-around that linker directives cannot specify other install - /// name per symbol, we should eventually remove this. - std::string LdAddCFilePath; - /// The path to which we should emit module summary file. std::string ModuleSummaryOutputPath; + /// The output path to generate ABI baseline. + std::string ABIDescriptorOutputPath; + SupplementaryOutputPaths() = default; SupplementaryOutputPaths(const SupplementaryOutputPaths &) = default; @@ -182,10 +175,10 @@ struct SupplementaryOutputPaths { fn(ModuleInterfaceOutputPath); if (!PrivateModuleInterfaceOutputPath.empty()) fn(PrivateModuleInterfaceOutputPath); - if (!LdAddCFilePath.empty()) - fn(LdAddCFilePath); if (!ModuleSummaryOutputPath.empty()) fn(ModuleSummaryOutputPath); + if (!ABIDescriptorOutputPath.empty()) + fn(ABIDescriptorOutputPath); } bool empty() const { @@ -194,7 +187,7 @@ struct SupplementaryOutputPaths { ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && - ModuleSourceInfoOutputPath.empty() && LdAddCFilePath.empty(); + ModuleSourceInfoOutputPath.empty() && ABIDescriptorOutputPath.empty(); } }; } // namespace swift diff --git a/include/swift/Basic/ValueEnumerator.h b/include/swift/Basic/ValueEnumerator.h deleted file mode 100644 index f066416ca3550..0000000000000 --- a/include/swift/Basic/ValueEnumerator.h +++ /dev/null @@ -1,58 +0,0 @@ -//===--- ValueEnumerator.h --- Enumerates values ----------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_BASIC_VALUEENUMERATOR_H -#define SWIFT_BASIC_VALUEENUMERATOR_H - -#include "llvm/ADT/DenseMap.h" -#include "llvm/Support/raw_ostream.h" - -namespace swift { - -/// / This class maps values to unique indices. -template -class ValueEnumerator { - /// A running counter to enumerate values. - IndexTy counter = 0; - - /// Maps values to unique integers. - llvm::DenseMap ValueToIndex; - -public: - /// Return the index of value \p v. - IndexTy getIndex(const ValueTy &v) { - // Return the index of this Key, if we've assigned one already. - auto It = ValueToIndex.find(v); - if (It != ValueToIndex.end()) { - return It->second; - } - - // Generate a new counter for the key. - ValueToIndex[v] = ++counter; - return counter; - } - - ValueEnumerator() = default; - - /// Forget about key \p v. - void invalidateValue(const ValueTy &v) { ValueToIndex.erase(v); } - - /// Clear the enumeration state of the - void clear() { - ValueToIndex.clear(); - counter = 0; - } -}; - -} // end namespace swift - -#endif // SWIFT_BASIC_VALUEENUMERATOR_H diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 9b077fcf7c344..08e2630cae1a1 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -25,6 +25,9 @@ namespace llvm { class Triple; class FileCollectorBase; template class function_ref; + namespace vfs { + class FileSystem; + } } namespace clang { @@ -158,6 +161,7 @@ class ClangImporter final : public ClangModuleLoader { static std::unique_ptr createClangInvocation(ClangImporter *importer, const ClangImporterOptions &importerOpts, + llvm::IntrusiveRefCntPtr VFS, ArrayRef invocationArgStrs, std::vector *CC1Args = nullptr); ClangImporter(const ClangImporter &) = delete; @@ -482,6 +486,8 @@ class ClangImporter final : public ClangModuleLoader { instantiateCXXFunctionTemplate(ASTContext &ctx, clang::FunctionTemplateDecl *func, SubstitutionMap subst) override; + + bool isCXXMethodMutating(const clang::CXXMethodDecl *method) override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/Config.h.in b/include/swift/Config.h.in index cb027afe1cd25..495c34d1fefb7 100644 --- a/include/swift/Config.h.in +++ b/include/swift/Config.h.in @@ -8,6 +8,10 @@ #cmakedefine HAVE_PROC_PID_RUSAGE 1 -#cmakedefine01 SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY +#cmakedefine01 SWIFT_IMPLICIT_CONCURRENCY_IMPORT + +#cmakedefine01 SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED + +#cmakedefine01 SWIFT_ENABLE_GLOBAL_ISEL_ARM64 #endif // SWIFT_CONFIG_H diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 8a02f3972f799..d3ad6b8288ac5 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -82,6 +82,7 @@ NODE(ErrorType) NODE(EscapingAutoClosureType) NODE(NoEscapeFunctionType) NODE(ConcurrentFunctionType) +NODE(GlobalActorFunctionType) NODE(DifferentiableFunctionType) NODE(ExistentialMetatype) CONTEXT_NODE(ExplicitClosure) @@ -132,6 +133,7 @@ NODE(ImplErrorResult) NODE(InOut) NODE(InfixOperator) CONTEXT_NODE(Initializer) +NODE(Isolated) NODE(KeyPathGetterThunkHelper) NODE(KeyPathSetterThunkHelper) NODE(KeyPathEqualsThunkHelper) @@ -194,6 +196,7 @@ NODE(ProtocolWitnessTablePattern) NODE(ReabstractionThunk) NODE(ReabstractionThunkHelper) NODE(ReabstractionThunkHelperWithSelf) +NODE(ReabstractionThunkHelperWithGlobalActor) CONTEXT_NODE(ReadAccessor) NODE(RelatedEntityDeclName) NODE(RetroactiveConformance) diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index eb0b4cfba92da..811e400b216bf 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -494,7 +494,7 @@ class Demangler : public NodeFactory { NodePointer pushMultiSubstitutions(int RepeatCount, size_t SubstIdx); NodePointer createSwiftType(Node::Kind typeKind, const char *name); NodePointer demangleStandardSubstitution(); - NodePointer createStandardSubstitution(char Subst); + NodePointer createStandardSubstitution(char Subst, bool SecondLevel); NodePointer demangleLocalIdentifier(); NodePointer popModule(); diff --git a/include/swift/Demangling/ManglingUtils.h b/include/swift/Demangling/ManglingUtils.h index e15872004ddab..d0f750454e3f8 100644 --- a/include/swift/Demangling/ManglingUtils.h +++ b/include/swift/Demangling/ManglingUtils.h @@ -99,7 +99,7 @@ char translateOperatorChar(char op); std::string translateOperator(StringRef Op); /// Returns the standard type kind for an 'S' substitution, e.g. 'i' for "Int". -char getStandardTypeSubst(StringRef TypeName); +llvm::Optional getStandardTypeSubst(StringRef TypeName); /// Mangles an identifier using a generic Mangler class. /// @@ -274,8 +274,9 @@ class SubstitutionMerging { /// *) getBufferStr(): Returns a StringRef of the current content of Buffer. /// *) resetBuffer(size_t): Resets the buffer to an old position. template - bool tryMergeSubst(Mangler &M, char Subst, bool isStandardSubst) { - assert(isUpperLetter(Subst) || (isStandardSubst && isLowerLetter(Subst))); + bool tryMergeSubst(Mangler &M, StringRef Subst, bool isStandardSubst) { + assert(isUpperLetter(Subst.back()) || + (isStandardSubst && isLowerLetter(Subst.back()))); StringRef BufferStr = M.getBufferStr(); if (lastNumSubsts > 0 && lastNumSubsts < MaxRepeatCount && BufferStr.size() == lastSubstPosition + lastSubstSize @@ -284,17 +285,20 @@ class SubstitutionMerging { // The last mangled thing is a substitution. assert(lastSubstPosition > 0 && lastSubstPosition < BufferStr.size()); assert(lastSubstSize > 0); - char lastSubst = BufferStr.back(); - assert(isUpperLetter(lastSubst) - || (isStandardSubst && isLowerLetter(lastSubst))); + StringRef lastSubst = BufferStr.take_back(lastSubstSize) + .drop_while([](char c) { + return isDigit(c); + }); + assert(isUpperLetter(lastSubst.back()) + || (isStandardSubst && isLowerLetter(lastSubst.back()))); if (lastSubst != Subst && !isStandardSubst) { // We can merge with a different 'A' substitution, // e.g. 'AB' -> 'AbC'. lastSubstPosition = BufferStr.size(); lastNumSubsts = 1; M.resetBuffer(BufferStr.size() - 1); - assert(isUpperLetter(lastSubst)); - M.Buffer << (char)(lastSubst - 'A' + 'a') << Subst; + assert(isUpperLetter(lastSubst.back())); + M.Buffer << (char)(lastSubst.back() - 'A' + 'a') << Subst; lastSubstSize = 1; return true; } @@ -312,7 +316,7 @@ class SubstitutionMerging { // We can't merge with the previous substitution, but let's remember this // substitution which will be mangled by the caller. lastSubstPosition = BufferStr.size() + 1; - lastSubstSize = 1; + lastSubstSize = Subst.size(); lastNumSubsts = 1; lastSubstIsStandardSubst = isStandardSubst; return false; diff --git a/include/swift/Demangling/StandardTypesMangling.def b/include/swift/Demangling/StandardTypesMangling.def index 336ef32ca32c0..c092eb702abd1 100644 --- a/include/swift/Demangling/StandardTypesMangling.def +++ b/include/swift/Demangling/StandardTypesMangling.def @@ -12,7 +12,12 @@ /// STANDARD_TYPE(KIND, MANGLING, TYPENAME) /// The 1-character MANGLING for a known TYPENAME of KIND. - +/// +/// STANDARD_TYPE_2(KIND, MANGLING, TYPENAME) +/// The 1-character MANGLING for a known TYPENAME of KIND that is in the +/// second level of standard types, all of which are mangled with the form +/// Sc. +/// /// OBJC_INTEROP_STANDARD_TYPE(KIND, MANGLING, TYPENAME) /// The 1-character MANGLING for a known TYPENAME of KIND, for a type that's /// only available with ObjC interop enabled. @@ -22,6 +27,7 @@ STANDARD_TYPE(KIND, MANGLING, TYPENAME) #endif + OBJC_INTEROP_STANDARD_TYPE(Structure, A, AutoreleasingUnsafeMutablePointer) STANDARD_TYPE(Structure, a, Array) STANDARD_TYPE(Structure, b, Bool) @@ -73,5 +79,25 @@ STANDARD_TYPE(Protocol, y, StringProtocol) STANDARD_TYPE(Protocol, Z, SignedInteger) STANDARD_TYPE(Protocol, z, BinaryInteger) +STANDARD_TYPE_2(Protocol, A, Actor) +STANDARD_TYPE_2(Structure, C, CheckedContinuation) +STANDARD_TYPE_2(Structure, c, UnsafeContinuation) +STANDARD_TYPE_2(Structure, E, CancellationError) +STANDARD_TYPE_2(Structure, e, UnownedSerialExecutor) +STANDARD_TYPE_2(Protocol, F, Executor) +STANDARD_TYPE_2(Protocol, f, SerialExecutor) +STANDARD_TYPE_2(Structure, G, TaskGroup) +STANDARD_TYPE_2(Structure, g, ThrowingTaskGroup) +STANDARD_TYPE_2(Protocol, I, AsyncIteratorProtocol) +STANDARD_TYPE_2(Protocol, i, AsyncSequence) +STANDARD_TYPE_2(Structure, J, UnownedJob) +STANDARD_TYPE_2(Class, M, MainActor) +STANDARD_TYPE_2(Structure, P, TaskPriority) +STANDARD_TYPE_2(Structure, S, AsyncStream) +STANDARD_TYPE_2(Structure, s, AsyncThrowingStream) +STANDARD_TYPE_2(Structure, T, Task) +STANDARD_TYPE_2(Structure, t, UnsafeCurrentTask) + #undef STANDARD_TYPE #undef OBJC_INTEROP_STANDARD_TYPE +#undef STANDARD_TYPE_2 diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index b2f88e6645090..7edc5691a02df 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -73,6 +73,7 @@ class FunctionParam { Flags = Flags.withValueOwnership(ownership); } void setNoDerivative() { Flags = Flags.withNoDerivative(true); } + void setIsolated() { Flags = Flags.withIsolated(true); } void setFlags(ParameterFlags flags) { Flags = flags; }; FunctionParam withLabel(StringRef label) const { @@ -468,6 +469,17 @@ class TypeDecoder { /// Given a demangle tree, attempt to turn it into a type. TypeLookupErrorOr decodeMangledType(NodePointer Node) { + return decodeMangledType(Node, 0); + } + +protected: + static const unsigned MaxDepth = 1024; + + TypeLookupErrorOr decodeMangledType(NodePointer Node, + unsigned depth) { + if (depth > TypeDecoder::MaxDepth) + return TypeLookupError("Mangled type is too complex"); + if (!Node) return TypeLookupError("Node is NULL"); @@ -477,17 +489,17 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children."); - return decodeMangledType(Node->getChild(0)); + return decodeMangledType(Node->getChild(0), depth + 1); case NodeKind::TypeMangling: if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children."); - return decodeMangledType(Node->getChild(0)); + return decodeMangledType(Node->getChild(0), depth + 1); case NodeKind::Type: if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children."); - return decodeMangledType(Node->getChild(0)); + return decodeMangledType(Node->getChild(0), depth + 1); case NodeKind::Class: { #if SWIFT_OBJC_INTEROP @@ -504,7 +516,8 @@ class TypeDecoder { BuiltTypeDecl typeDecl = BuiltTypeDecl(); BuiltType parent = BuiltType(); bool typeAlias = false; - if (auto error = decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias)) + if (auto error = + decodeMangledTypeDecl(Node, depth, typeDecl, parent, typeAlias)) return *error; if (typeAlias) @@ -530,7 +543,7 @@ class TypeDecoder { return MAKE_NODE_TYPE_ERROR0(genericArgs, "is not TypeList"); for (auto genericArg : *genericArgs) { - auto paramType = decodeMangledType(genericArg); + auto paramType = decodeMangledType(genericArg, depth + 1); if (paramType.isError()) return paramType; args.push_back(paramType.getType()); @@ -550,8 +563,8 @@ class TypeDecoder { BuiltTypeDecl typeDecl = BuiltTypeDecl(); BuiltType parent = BuiltType(); bool typeAlias = false; - if (auto error = - decodeMangledTypeDecl(ChildNode, typeDecl, parent, typeAlias)) + if (auto error = decodeMangledTypeDecl(ChildNode, depth, typeDecl, parent, + typeAlias)) return *error; return Builder.createBoundGenericType(typeDecl, args, parent); @@ -592,7 +605,7 @@ class TypeDecoder { "expected 1 generic argument, saw %zu", genericArgs->getNumChildren()); - return decodeMangledType(genericArgs->getChild(0)); + return decodeMangledType(genericArgs->getChild(0), depth + 1); } case NodeKind::BuiltinTypeName: { auto mangledName = Demangle::mangleNode(Node); @@ -621,7 +634,7 @@ class TypeDecoder { return MAKE_NODE_TYPE_ERROR0(Node, "no children"); } - auto instance = decodeMangledType(Node->getChild(i)); + auto instance = decodeMangledType(Node->getChild(i), depth + 1); if (instance.isError()) return instance; if (Node->getKind() == NodeKind::Metatype) { @@ -651,7 +664,7 @@ class TypeDecoder { // Demangle the protocol list. for (auto componentType : *TypeList) { - if (auto Protocol = decodeMangledProtocolType(componentType)) + if (auto Protocol = decodeMangledProtocolType(componentType, depth + 1)) Protocols.push_back(Protocol); else return MAKE_NODE_TYPE_ERROR0(componentType, @@ -668,7 +681,7 @@ class TypeDecoder { Node->getNumChildren()); auto superclassNode = Node->getChild(1); - auto result = decodeMangledType(superclassNode); + auto result = decodeMangledType(superclassNode, depth + 1); if (result.isError()) return result; Superclass = result.getType(); @@ -684,7 +697,7 @@ class TypeDecoder { case NodeKind::Protocol: case NodeKind::ProtocolSymbolicReference: { - if (auto Proto = decodeMangledProtocolType(Node)) { + if (auto Proto = decodeMangledProtocolType(Node, depth + 1)) { return Builder.createProtocolCompositionType(Proto, BuiltType(), /*IsClassBound=*/false); } @@ -696,7 +709,7 @@ class TypeDecoder { return MAKE_NODE_TYPE_ERROR(Node, "expected 1 child, saw %zu", Node->getNumChildren()); - auto selfType = decodeMangledType(Node->getChild(0)); + auto selfType = decodeMangledType(Node->getChild(0), depth + 1); if (selfType.isError()) return selfType; @@ -738,6 +751,24 @@ class TypeDecoder { ++firstChildIdx; } + BuiltType globalActorType = BuiltType(); + if (Node->getChild(firstChildIdx)->getKind() == + NodeKind::GlobalActorFunctionType) { + auto child = Node->getChild(firstChildIdx); + if (child->getNumChildren() < 1) { + return MAKE_NODE_TYPE_ERROR0(child, + "Global actor node is missing child"); + } + + auto globalActorResult = + decodeMangledType(child->getChild(0), depth + 1); + if (globalActorResult.isError()) + return globalActorResult; + + globalActorType = globalActorResult.getType(); + ++firstChildIdx; + } + FunctionMetadataDifferentiabilityKind diffKind; if (Node->getChild(firstChildIdx)->getKind() == NodeKind::DifferentiableFunctionType) { @@ -796,7 +827,7 @@ class TypeDecoder { bool hasParamFlags = false; llvm::SmallVector, 8> parameters; if (!decodeMangledFunctionInputType(Node->getChild(firstChildIdx), - parameters, hasParamFlags)) + depth + 1, parameters, hasParamFlags)) return MAKE_NODE_TYPE_ERROR0(Node->getChild(firstChildIdx), "failed to decode function type"); flags = @@ -807,11 +838,12 @@ class TypeDecoder { Node->getKind() == NodeKind::EscapingAutoClosureType || Node->getKind() == NodeKind::EscapingObjCBlock); - auto result = decodeMangledType(Node->getChild(firstChildIdx+1)); + auto result = + decodeMangledType(Node->getChild(firstChildIdx + 1), depth + 1); if (result.isError()) return result; return Builder.createFunctionType( - parameters, result.getType(), flags, diffKind); + parameters, result.getType(), flags, diffKind, globalActorType); } case NodeKind::ImplFunctionType: { auto calleeConvention = ImplParameterConvention::Direct_Unowned; @@ -876,15 +908,15 @@ class TypeDecoder { } else if (child->getKind() == NodeKind::ImplEscaping) { flags = flags.withEscaping(); } else if (child->getKind() == NodeKind::ImplParameter) { - if (decodeImplFunctionParam(child, parameters)) + if (decodeImplFunctionParam(child, depth + 1, parameters)) return MAKE_NODE_TYPE_ERROR0(child, "failed to decode function parameter"); } else if (child->getKind() == NodeKind::ImplResult) { - if (decodeImplFunctionParam(child, results)) + if (decodeImplFunctionParam(child, depth + 1, results)) return MAKE_NODE_TYPE_ERROR0(child, "failed to decode function parameter"); } else if (child->getKind() == NodeKind::ImplErrorResult) { - if (decodeImplFunctionPart(child, errorResults)) + if (decodeImplFunctionPart(child, depth + 1, errorResults)) return MAKE_NODE_TYPE_ERROR0(child, "failed to decode function part"); } else { @@ -918,13 +950,13 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - return decodeMangledType(Node->getChild(0)); + return decodeMangledType(Node->getChild(0), depth + 1); case NodeKind::ReturnType: if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - return decodeMangledType(Node->getChild(0)); + return decodeMangledType(Node->getChild(0), depth + 1); case NodeKind::Tuple: { llvm::SmallVector elements; @@ -955,7 +987,8 @@ class TypeDecoder { } // Decode the element type. - auto elementType = decodeMangledType(element->getChild(typeChildIndex)); + auto elementType = + decodeMangledType(element->getChild(typeChildIndex), depth + 1); if (elementType.isError()) return elementType; @@ -973,9 +1006,9 @@ class TypeDecoder { "fewer children (%zu) than required (2)", Node->getNumChildren()); - return decodeMangledType(Node->getChild(1)); + return decodeMangledType(Node->getChild(1), depth + 1); } - return decodeMangledType(Node->getChild(0)); + return decodeMangledType(Node->getChild(0), depth + 1); case NodeKind::DependentGenericType: { if (Node->getNumChildren() < 2) @@ -983,7 +1016,7 @@ class TypeDecoder { "fewer children (%zu) than required (2)", Node->getNumChildren()); - return decodeMangledType(Node->getChild(1)); + return decodeMangledType(Node->getChild(1), depth + 1); } case NodeKind::DependentMemberType: { if (Node->getNumChildren() < 2) @@ -991,7 +1024,7 @@ class TypeDecoder { "fewer children (%zu) than required (2)", Node->getNumChildren()); - auto base = decodeMangledType(Node->getChild(0)); + auto base = decodeMangledType(Node->getChild(0), depth + 1); if (base.isError()) return base; auto assocTypeChild = Node->getChild(1); @@ -999,7 +1032,8 @@ class TypeDecoder { if (assocTypeChild->getNumChildren() < 2) return Builder.createDependentMemberType(member.str(), base.getType()); - auto protocol = decodeMangledProtocolType(assocTypeChild->getChild(1)); + auto protocol = + decodeMangledProtocolType(assocTypeChild->getChild(1), depth + 1); if (!protocol) return BuiltType(); return Builder.createDependentMemberType(member.str(), base.getType(), @@ -1011,13 +1045,13 @@ class TypeDecoder { "fewer children (%zu) than required (2)", Node->getNumChildren()); - return decodeMangledType(Node->getChild(1)); + return decodeMangledType(Node->getChild(1), depth + 1); } case NodeKind::Unowned: { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - auto base = decodeMangledType(Node->getChild(0)); + auto base = decodeMangledType(Node->getChild(0), depth + 1); if (base.isError()) return base; return Builder.createUnownedStorageType(base.getType()); @@ -1026,7 +1060,7 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - auto base = decodeMangledType(Node->getChild(0)); + auto base = decodeMangledType(Node->getChild(0), depth + 1); if (base.isError()) return base; return Builder.createUnmanagedStorageType(base.getType()); @@ -1035,7 +1069,7 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - auto base = decodeMangledType(Node->getChild(0)); + auto base = decodeMangledType(Node->getChild(0), depth + 1); if (base.isError()) return base; return Builder.createWeakStorageType(base.getType()); @@ -1044,7 +1078,7 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - auto base = decodeMangledType(Node->getChild(0)); + auto base = decodeMangledType(Node->getChild(0), depth + 1); if (base.isError()) return base; return Builder.createSILBoxType(base.getType()); @@ -1070,7 +1104,7 @@ class TypeDecoder { } if (fieldNode->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(fieldNode, "no children"); - auto type = decodeMangledType(fieldNode->getChild(0)); + auto type = decodeMangledType(fieldNode->getChild(0), depth + 1); if (type.isError()) return type; fields.emplace_back(type.getType(), isMutable); @@ -1092,18 +1126,19 @@ class TypeDecoder { "fewer children (%zu) than required (1)", dependentGenericSignatureNode->getNumChildren()); decodeRequirement( - dependentGenericSignatureNode, requirements, Builder/*, - [&](NodePointer Node) -> BuiltType { - return decodeMangledType(Node).getType(); - }, - [&](LayoutConstraintKind Kind) -> BuiltLayoutConstraint { - return {}; // Not implemented! - }, - [&](LayoutConstraintKind Kind, unsigned SizeInBits, - unsigned Alignment) -> BuiltLayoutConstraint { - return {}; // Not Implemented! - }*/); + BuilderType>(dependentGenericSignatureNode, + requirements, + Builder /*, +[&](NodePointer Node) -> BuiltType { +return decodeMangledType(Node, depth + 1).getType(); +}, +[&](LayoutConstraintKind Kind) -> BuiltLayoutConstraint { +return {}; // Not implemented! +}, +[&](LayoutConstraintKind Kind, unsigned SizeInBits, +unsigned Alignment) -> BuiltLayoutConstraint { +return {}; // Not Implemented! +}*/); // The number of generic parameters at each depth are in a mini // state machine and come first. llvm::SmallVector genericParamsAtDepth; @@ -1111,20 +1146,20 @@ class TypeDecoder { if (reqNode->getKind() == NodeKind::DependentGenericParamCount) if (reqNode->hasIndex()) genericParamsAtDepth.push_back(reqNode->getIndex()); - unsigned depth = 0; + unsigned paramDepth = 0; unsigned index = 0; for (auto *subst : *substNode) { - if (depth >= genericParamsAtDepth.size()) + if (paramDepth >= genericParamsAtDepth.size()) return MAKE_NODE_TYPE_ERROR0( dependentGenericSignatureNode, "more substitutions than generic params"); - while (index >= genericParamsAtDepth[depth]) - ++depth, index = 0; - auto substTy = decodeMangledType(subst); + while (index >= genericParamsAtDepth[paramDepth]) + ++paramDepth, index = 0; + auto substTy = decodeMangledType(subst, depth + 1); if (substTy.isError()) return substTy; substitutions.emplace_back( - Builder.createGenericTypeParameterType(depth, index), + Builder.createGenericTypeParameterType(paramDepth, index), substTy.getType()); ++index; } @@ -1137,7 +1172,7 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - auto base = decodeMangledType(Node->getChild(0)); + auto base = decodeMangledType(Node->getChild(0), depth + 1); if (base.isError()) return base; @@ -1147,7 +1182,7 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - auto base = decodeMangledType(Node->getChild(0)); + auto base = decodeMangledType(Node->getChild(0), depth + 1); if (base.isError()) return base; @@ -1159,11 +1194,11 @@ class TypeDecoder { "fewer children (%zu) than required (2)", Node->getNumChildren()); - auto key = decodeMangledType(Node->getChild(0)); + auto key = decodeMangledType(Node->getChild(0), depth + 1); if (key.isError()) return key; - auto value = decodeMangledType(Node->getChild(1)); + auto value = decodeMangledType(Node->getChild(1), depth + 1); if (value.isError()) return value; @@ -1173,7 +1208,7 @@ class TypeDecoder { if (Node->getNumChildren() < 1) return MAKE_NODE_TYPE_ERROR0(Node, "no children"); - auto base = decodeMangledType(Node->getChild(0)); + auto base = decodeMangledType(Node->getChild(0), depth + 1); if (base.isError()) return base; @@ -1202,7 +1237,7 @@ class TypeDecoder { if (genericsNode->getKind() != NodeKind::TypeList) break; for (auto argNode : *genericsNode) { - auto arg = decodeMangledType(argNode); + auto arg = decodeMangledType(argNode, depth + 1); if (arg.isError()) return arg; genericArgsBuf.push_back(arg.getType()); @@ -1227,8 +1262,11 @@ class TypeDecoder { private: template - bool decodeImplFunctionPart(Demangle::NodePointer node, + bool decodeImplFunctionPart(Demangle::NodePointer node, unsigned depth, llvm::SmallVectorImpl &results) { + if (depth > TypeDecoder::MaxDepth) + return true; + if (node->getNumChildren() != 2) return true; @@ -1241,7 +1279,7 @@ class TypeDecoder { T::getConventionFromString(conventionString); if (!convention) return true; - auto type = decodeMangledType(node->getChild(1)); + auto type = decodeMangledType(node->getChild(1), depth + 1); if (type.isError()) return true; @@ -1250,8 +1288,11 @@ class TypeDecoder { } template - bool decodeImplFunctionParam(Demangle::NodePointer node, + bool decodeImplFunctionParam(Demangle::NodePointer node, unsigned depth, llvm::SmallVectorImpl &results) { + if (depth > TypeDecoder::MaxDepth) + return true; + // Children: `convention, differentiability?, type` if (node->getNumChildren() != 2 && node->getNumChildren() != 3) return true; @@ -1266,7 +1307,7 @@ class TypeDecoder { auto convention = T::getConventionFromString(conventionString); if (!convention) return true; - auto result = decodeMangledType(typeNode); + auto result = decodeMangledType(typeNode, depth + 1); if (result.isError()) return true; @@ -1288,10 +1329,14 @@ class TypeDecoder { } llvm::Optional - decodeMangledTypeDecl(Demangle::NodePointer node, BuiltTypeDecl &typeDecl, - BuiltType &parent, bool &typeAlias) { + decodeMangledTypeDecl(Demangle::NodePointer node, unsigned depth, + BuiltTypeDecl &typeDecl, BuiltType &parent, + bool &typeAlias) { + if (depth > TypeDecoder::MaxDepth) + return TypeLookupError("Mangled type is too complex"); + if (node->getKind() == NodeKind::Type) - return decodeMangledTypeDecl(node->getChild(0), typeDecl, + return decodeMangledTypeDecl(node->getChild(0), depth + 1, typeDecl, parent, typeAlias); Demangle::NodePointer declNode; @@ -1325,7 +1370,7 @@ class TypeDecoder { parentContext = parentContext->getChild(1); LLVM_FALLTHROUGH; default: - parent = decodeMangledType(parentContext).getType(); + parent = decodeMangledType(parentContext, depth + 1).getType(); // Remove any generic arguments from the context node, producing a // node that references the nominal type declaration. declNode = Demangle::getUnspecialized(node, Builder.getNodeFactory()); @@ -1339,9 +1384,13 @@ class TypeDecoder { return llvm::None; } - BuiltProtocolDecl decodeMangledProtocolType(Demangle::NodePointer node) { + BuiltProtocolDecl decodeMangledProtocolType(Demangle::NodePointer node, + unsigned depth) { + if (depth > TypeDecoder::MaxDepth) + return BuiltProtocolDecl(); + if (node->getKind() == NodeKind::Type) - return decodeMangledProtocolType(node->getChild(0)); + return decodeMangledProtocolType(node->getChild(0), depth + 1); if ((node->getNumChildren() < 2 || node->getKind() != NodeKind::Protocol) && node->getKind() != NodeKind::ProtocolSymbolicReference) @@ -1356,14 +1405,17 @@ class TypeDecoder { } bool decodeMangledFunctionInputType( - Demangle::NodePointer node, + Demangle::NodePointer node, unsigned depth, llvm::SmallVectorImpl> ¶ms, bool &hasParamFlags) { + if (depth > TypeDecoder::MaxDepth) + return false; + // Look through a couple of sugar nodes. if (node->getKind() == NodeKind::Type || node->getKind() == NodeKind::ArgumentTuple) { - return decodeMangledFunctionInputType(node->getFirstChild(), params, - hasParamFlags); + return decodeMangledFunctionInputType(node->getFirstChild(), depth + 1, + params, hasParamFlags); } auto decodeParamTypeAndFlags = @@ -1398,6 +1450,12 @@ class TypeDecoder { hasParamFlags = true; break; + case NodeKind::Isolated: + param.setIsolated(); + node = node->getFirstChild(); + hasParamFlags = true; + break; + case NodeKind::AutoClosureType: case NodeKind::EscapingAutoClosureType: param.setAutoClosure(); @@ -1411,7 +1469,7 @@ class TypeDecoder { } } - auto paramType = decodeMangledType(node); + auto paramType = decodeMangledType(node, depth + 1); if (paramType.isError()) return false; diff --git a/include/swift/Demangling/TypeLookupError.h b/include/swift/Demangling/TypeLookupError.h index c9929876af37d..6bc418c14ef4a 100644 --- a/include/swift/Demangling/TypeLookupError.h +++ b/include/swift/Demangling/TypeLookupError.h @@ -207,7 +207,10 @@ template static TypeLookupError TypeLookupErrorImpl(const char *fmt, Args... args) { return TypeLookupError([=] { char *str; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-security" swift_asprintf(&str, fmt, args...); +#pragma clang diagnostic pop return str; }); } diff --git a/include/swift/DependencyScan/DependencyScanningTool.h b/include/swift/DependencyScan/DependencyScanningTool.h index d18d5bf09167d..54783b6735631 100644 --- a/include/swift/DependencyScan/DependencyScanningTool.h +++ b/include/swift/DependencyScan/DependencyScanningTool.h @@ -31,7 +31,7 @@ class DependencyScanningTool { /// Construct a dependency scanning tool. DependencyScanningTool(); - /// Collect the full module depenedency graph for the input, ignoring any + /// Collect the full module dependency graph for the input, ignoring any /// placeholder modules. /// /// \returns a \c StringError with the diagnostic output if errors @@ -47,16 +47,24 @@ class DependencyScanningTool { llvm::ErrorOr getImports(ArrayRef Command); - /// Collect the full module depenedency graph for the input collection of + /// Collect the full module dependency graph for the input collection of /// module names (batch inputs) and output them to the /// BatchScanInput-specified output locations. /// - /// \returns a \c std::error_code if errors occured during scan. + /// \returns a \c std::error_code if errors occurred during scan. std::vector> getDependencies(ArrayRef Command, const std::vector &BatchInput, const llvm::StringSet<> &PlaceholderModules); + /// Writes the current `SharedCache` instance to a specified FileSystem path. + void serializeCache(llvm::StringRef path); + /// Loads an instance of a `GlobalModuleDependenciesCache` to serve as the `SharedCache` + /// from a specified FileSystem path. + bool loadCache(llvm::StringRef path); + /// Discard the tool's current `SharedCache` and start anew. + void resetCache(); + private: /// Using the specified invocation command, instantiate a CompilerInstance /// that will be used for this scan. @@ -65,7 +73,7 @@ class DependencyScanningTool { /// Shared cache of module dependencies, re-used by individual full-scan queries /// during the lifetime of this Tool. - std::unique_ptr SharedCache; + std::unique_ptr SharedCache; /// Shared cache of compiler instances created during batch scanning, corresponding to /// command-line options specified in the batch scan input entry. diff --git a/include/swift/DependencyScan/ScanDependencies.h b/include/swift/DependencyScan/ScanDependencies.h index e1d5edb7970e5..4a9fd7ee0b676 100644 --- a/include/swift/DependencyScan/ScanDependencies.h +++ b/include/swift/DependencyScan/ScanDependencies.h @@ -27,12 +27,18 @@ namespace swift { class CompilerInvocation; class CompilerInstance; class ModuleDependenciesCache; +class GlobalModuleDependenciesCache; namespace dependencies { +//using CompilerArgInstanceCacheMap = +// llvm::StringMap, +// std::unique_ptr>>; + using CompilerArgInstanceCacheMap = - llvm::StringMap, - std::unique_ptr>>; + llvm::StringMap, + std::unique_ptr, + std::unique_ptr>>; struct BatchScanInput { llvm::StringRef moduleName; diff --git a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h new file mode 100644 index 0000000000000..1b77a7510992c --- /dev/null +++ b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h @@ -0,0 +1,185 @@ +//=== SerializedModuleDependencyCacheFormat.h - serialized format -*- C++-*-=// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DEPENDENCY_SERIALIZEDCACHEFORMAT_H +#define SWIFT_DEPENDENCY_SERIALIZEDCACHEFORMAT_H + +#include "llvm/Bitcode/BitcodeConvenience.h" +#include "llvm/Bitstream/BitCodes.h" + +namespace llvm { +class MemoryBuffer; +} + +namespace swift { + +class DiagnosticEngine; +class GlobalModuleDependenciesCache; + +namespace dependencies { +namespace module_dependency_cache_serialization { + +using llvm::BCArray; +using llvm::BCBlob; +using llvm::BCFixed; +using llvm::BCRecordLayout; +using llvm::BCVBR; + +/// Every .moddepcache file begins with these 4 bytes, for easy identification. +const unsigned char MODULE_DEPENDENCY_CACHE_FORMAT_SIGNATURE[] = {'I', 'M', 'D', + 'C'}; +const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR = 1; +/// Increment this on every change. +const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR = 0; + +/// Various identifiers in this format will rely on having their strings mapped +/// using this ID. +using IdentifierIDField = BCVBR<13>; +using FileIDField = IdentifierIDField; +using ModuleIDField = IdentifierIDField; +using CompilerFlagField = IdentifierIDField; +using ContextHashField = IdentifierIDField; + +/// A bit that indicates whether or not a module is a framework +using IsFrameworkField = BCFixed<1>; + +/// Arrays of various identifiers, distinguised for readability +using IdentifierIDArryField = llvm::BCArray; + +/// Identifiers used to refer to the above arrays +using FileIDArrayIDField = IdentifierIDField; +using DependencyIDArrayIDField = IdentifierIDField; +using FlagIDArrayIDField = IdentifierIDField; + +/// The ID of the top-level block containing the dependency graph +const unsigned GRAPH_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID; + +/// The .moddepcache file format consists of a METADATA record, followed by +/// zero or more IDENTIFIER records that contain various strings seen in the graph +/// (e.g. file names or compiler flags), followed by zero or more IDENTIFIER_ARRAY records +/// which are arrays of identifiers seen in the graph (e.g. list of source files or list of compile flags), +/// followed by zero or more MODULE_NODE, *_DETAILS_NODE pairs of records. +namespace graph_block { +enum { + METADATA = 1, + MODULE_NODE, + SWIFT_TEXTUAL_MODULE_DETAILS_NODE, + SWIFT_PLACEHOLDER_MODULE_DETAILS_NODE, + SWIFT_BINARY_MODULE_DETAILS_NODE, + CLANG_MODULE_DETAILS_NODE, + IDENTIFIER_NODE, + IDENTIFIER_ARRAY_NODE +}; + +// Always the first record in the file. +using MetadataLayout = BCRecordLayout< + METADATA, // ID + BCFixed<16>, // Inter-Module Dependency graph format major version + BCFixed<16>, // Inter-Module Dependency graph format minor version + BCBlob // Compiler version string + >; + +// After the metadata record, we have zero or more identifier records, +// for each unique string that is referenced in the graph. +// +// Identifiers are referenced by their sequence number, starting from 1. +// The identifier value 0 is special; it always represents the empty string. +// There is no IDENTIFIER_NODE serialized that corresponds to it, instead +// the first IDENTIFIER_NODE always has a sequence number of 1. +using IdentifierNodeLayout = BCRecordLayout; + +// After the identifier records we have zero or more identifier array records. +// +// These arrays are also referenced by their sequence number, +// starting from 1, similar to identifiers above. Value 0 indicates an +// empty array. This record is used because individiual array fields must +// appear as the last field of whatever record they belong to, and several of +// the below record layouts contain multiple arrays. +using IdentifierArrayLayout = + BCRecordLayout; + +// After the array records, we have a sequence of Module info +// records, each of which is followed by one of: +// - SwiftTextualModuleDetails +// - SwiftBinaryModuleDetails +// - SwiftPlaceholderModuleDetails +// - ClangModuleDetails +using ModuleInfoLayout = + BCRecordLayout; + +using SwiftTextualModuleDetailsLayout = + BCRecordLayout; + +using SwiftBinaryModuleDetailsLayout = + BCRecordLayout; + +using SwiftPlaceholderModuleDetailsLayout = + BCRecordLayout; + +using ClangModuleDetailsLayout = + BCRecordLayout; +} // namespace graph_block + +/// Tries to read the dependency graph from the given buffer. +/// Returns \c true if there was an error. +bool readInterModuleDependenciesCache(llvm::MemoryBuffer &buffer, + GlobalModuleDependenciesCache &cache); + +/// Tries to read the dependency graph from the given path name. +/// Returns true if there was an error. +bool readInterModuleDependenciesCache(llvm::StringRef path, + GlobalModuleDependenciesCache &cache); + +/// Tries to write the dependency graph to the given path name. +/// Returns true if there was an error. +bool writeInterModuleDependenciesCache(DiagnosticEngine &diags, + llvm::StringRef path, + const GlobalModuleDependenciesCache &cache); + +/// Tries to write out the given dependency cache with the given +/// bitstream writer. +void writeInterModuleDependenciesCache(llvm::BitstreamWriter &Out, + const GlobalModuleDependenciesCache &cache); + +} // end namespace module_dependency_cache_serialization +} // end namespace dependencies +} // end namespace swift + +#endif diff --git a/include/swift/Driver/Driver.h b/include/swift/Driver/Driver.h index 8e0849ebbcd56..11da8cccf6fdb 100644 --- a/include/swift/Driver/Driver.h +++ b/include/swift/Driver/Driver.h @@ -109,6 +109,8 @@ class OutputInfo { LTOKind LTOVariant = LTOKind::None; + std::string LibLTOPath; + /// Describes if and how the output of compile actions should be /// linked together. LinkKind LinkAction = LinkKind::None; @@ -262,13 +264,17 @@ class Driver { /// Construct a compilation object for a given ToolChain and command line /// argument vector. /// + /// If \p AllowErrors is set to \c true, this method tries to build a + /// compilation even if there were errors. + /// /// \return A Compilation, or nullptr if none was built for the given argument /// vector. A null return value does not necessarily indicate an error /// condition; the diagnostics should be queried to determine if an error /// occurred. std::unique_ptr buildCompilation(const ToolChain &TC, - std::unique_ptr ArgList); + std::unique_ptr ArgList, + bool AllowErrors = false); /// Parse the given list of strings into an InputArgList. std::unique_ptr diff --git a/include/swift/Driver/ToolChain.h b/include/swift/Driver/ToolChain.h index 7be6dffbe37cf..60aa1fd99836e 100644 --- a/include/swift/Driver/ToolChain.h +++ b/include/swift/Driver/ToolChain.h @@ -249,6 +249,14 @@ class ToolChain { const Driver &getDriver() const { return D; } const llvm::Triple &getTriple() const { return Triple; } + /// Special handling for passing down '-l' arguments. + /// + /// Not all downstream tools (lldb, ld etc.) consistently accept + /// a space between the '-l' flag and its argument, so we remove + /// the extra space if it was present in \c Args. + static void addLinkedLibArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &FrontendArgs); + /// Construct a Job for the action \p JA, taking the given information into /// account. /// diff --git a/stdlib/public/Darwin/Foundation/NSPredicate.swift b/include/swift/DriverTool/DriverTool.h similarity index 59% rename from stdlib/public/Darwin/Foundation/NSPredicate.swift rename to include/swift/DriverTool/DriverTool.h index 6a1aea8855173..a387ab3527db9 100644 --- a/stdlib/public/Darwin/Foundation/NSPredicate.swift +++ b/include/swift/DriverTool/DriverTool.h @@ -1,4 +1,4 @@ -//===----------------------------------------------------------------------===// +//===--- DriverTool.h - Driver control ----------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -9,14 +9,19 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +// +// This file provides a high-level API for interacting with the basic +// driver operation. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DRIVERTOOL_H +#define SWIFT_DRIVERTOOL_H + +#include "swift/Basic/LLVM.h" -@_exported import Foundation // Clang module +namespace swift { + int mainEntry(int argc_, const char **argv_); +} // namespace swift -extension NSPredicate { - // + (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...; - public - convenience init(format predicateFormat: __shared String, _ args: CVarArg...) { - let va_args = getVaList(args) - self.init(format: predicateFormat, arguments: va_args) - } -} +#endif diff --git a/include/swift/Frontend/BackDeploymentLibs.def b/include/swift/Frontend/BackDeploymentLibs.def index b2d9f8a84acd9..6fcc4f2b13b86 100644 --- a/include/swift/Frontend/BackDeploymentLibs.def +++ b/include/swift/Frontend/BackDeploymentLibs.def @@ -27,5 +27,6 @@ BACK_DEPLOYMENT_LIB((5, 0), all, "swiftCompatibility50") BACK_DEPLOYMENT_LIB((5, 1), all, "swiftCompatibility51") BACK_DEPLOYMENT_LIB((5, 0), executable, "swiftCompatibilityDynamicReplacements") +BACK_DEPLOYMENT_LIB((5, 5), all, "swiftCompatibilityConcurrency") #undef BACK_DEPLOYMENT_LIB diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index a4939cb448793..44bf8e550f17c 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -41,6 +41,7 @@ #include "swift/Serialization/Validation.h" #include "swift/Subsystems.h" #include "swift/TBDGen/TBDGen.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SetVector.h" #include "llvm/Option/ArgList.h" @@ -86,6 +87,7 @@ class CompilerInvocation { TypeCheckerOptions TypeCheckerOpts; FrontendOptions FrontendOpts; ClangImporterOptions ClangImporterOpts; + symbolgraphgen::SymbolGraphOptions SymbolGraphOpts; SearchPathOptions SearchPathOpts; DiagnosticOptions DiagnosticOpts; MigratorOptions MigratorOpts; @@ -257,6 +259,11 @@ class CompilerInvocation { return ClangImporterOpts; } + symbolgraphgen::SymbolGraphOptions &getSymbolGraphOptions() { return SymbolGraphOpts; } + const symbolgraphgen::SymbolGraphOptions &getSymbolGraphOptions() const { + return SymbolGraphOpts; + } + SearchPathOptions &getSearchPathOptions() { return SearchPathOpts; } const SearchPathOptions &getSearchPathOptions() const { return SearchPathOpts; @@ -381,8 +388,6 @@ class CompilerInvocation { std::string getModuleInterfaceOutputPathForWholeModule() const; std::string getPrivateModuleInterfaceOutputPathForWholeModule() const; - std::string getLdAddCFileOutputPathForWholeModule() const; - public: /// Given the current configuration of this frontend invocation, a set of /// supplementary output paths, and a module, compute the appropriate set of @@ -393,15 +398,6 @@ class CompilerInvocation { SerializationOptions computeSerializationOptions(const SupplementaryOutputPaths &outs, const ModuleDecl *module) const; - - /// Returns an approximation of whether the given module could be - /// redistributed and consumed by external clients. - /// - /// FIXME: The scope of this computation should be limited entirely to - /// PrintAsObjC. Unfortunately, it has been co-opted to support the - /// \c SerializeOptionsForDebugging hack. Once this information can be - /// transferred from module files to the dSYMs, remove this. - bool isModuleExternallyConsumed(const ModuleDecl *mod) const; }; /// A class which manages the state and execution of the compiler. diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index 28f60519074b4..4ccb1fc14860d 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -256,6 +256,7 @@ class FrontendInputsAndOutputs { bool hasModuleSourceInfoOutputPath() const; bool hasModuleInterfaceOutputPath() const; bool hasPrivateModuleInterfaceOutputPath() const; + bool hasABIDescriptorOutputPath() const; bool hasModuleSummaryOutputPath() const; bool hasTBDPath() const; diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index b8ad3035a4fa5..930614d484e9e 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -81,6 +81,10 @@ class FrontendOptions { /// binary module has already been built for use by the compiler. std::string PrebuiltModuleCachePath; + /// The path to look in to find backup .swiftinterface files if those found + /// from SDKs are failing. + std::string BackupModuleInterfaceDir; + /// For these modules, we should prefer using Swift interface when importing them. std::vector PreferInterfaceForModules; @@ -178,12 +182,6 @@ class FrontendOptions { /// the module. bool CheckOnoneSupportCompleteness = false; - /// If set, dumps wall time taken to check each function body to llvm::errs(). - bool DebugTimeFunctionBodies = false; - - /// If set, dumps wall time taken to check each expression. - bool DebugTimeExpressionTypeChecking = false; - /// The path to which we should output statistics files. std::string StatsOutputDir; @@ -300,6 +298,18 @@ class FrontendOptions { /// of the main Swift module's source files. bool ImportPrescan = false; + /// After performing a dependency scanning action, serialize the scanner's internal state. + bool SerializeDependencyScannerCache = false; + + /// Load and re-use a prior serialized dependency scanner cache. + bool ReuseDependencyScannerCache = false; + + /// The path at which to either serialize or deserialize the dependency scanner cache. + std::string SerializedDependencyScannerCachePath; + + /// Emit remarks indicating use of the serialized module dependency scanning cache + bool EmitDependencyScannerCacheRemarks = false; + /// When performing an incremental build, ensure that cross-module incremental /// build metadata is available in any swift modules emitted by this frontend /// job. @@ -405,6 +415,9 @@ class FrontendOptions { /// which are inherited through classes or default implementations. bool SkipInheritedDocs = false; + /// Whether to include symbols with SPI information in the symbol graph. + bool IncludeSPISymbolsInSymbolGraph = false; + private: static bool canActionEmitDependencies(ActionType); static bool canActionEmitReferenceDependencies(ActionType); @@ -414,6 +427,7 @@ class FrontendOptions { static bool canActionEmitModuleDoc(ActionType); static bool canActionEmitModuleSummary(ActionType); static bool canActionEmitInterface(ActionType); + static bool canActionEmitABIDescriptor(ActionType); public: static bool doesActionGenerateSIL(ActionType); diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index ae72306005693..69aed16d73ab2 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -161,7 +161,6 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { static std::unique_ptr create(ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, - ArrayRef ExplicitModulePaths, StringRef ExplicitSwiftModuleMap, bool IgnoreSwiftSourceInfoFile); @@ -180,10 +179,11 @@ struct ExplicitModuleInfo { std::string moduleDocPath; // Path of the .swiftsourceinfo file. std::string moduleSourceInfoPath; - // Opened buffer for the .swiftmodule file. - std::unique_ptr moduleBuffer; // A flag that indicates whether this module is a framework bool isFramework; + // A flag that indicates whether this module is a system module + // Set the default to be false. + bool isSystem = false; }; /// Parser of explicit module maps passed into the compiler. @@ -242,7 +242,18 @@ class ExplicitModuleMapParser { SmallString<32> Buffer; return Saver.save(cast(N)->getValue(Buffer)); } - + + static bool parseBoolValue(StringRef val) { + auto valStr = val.str(); + valStr.erase(std::remove(valStr.begin(), valStr.end(), '\n'), valStr.end()); + if (valStr.compare("true") == 0) + return true; + else if (valStr.compare("false") == 0) + return false; + else + llvm_unreachable("Unexpected JSON value for isFramework"); + } + bool parseSingleModuleEntry(llvm::yaml::Node &node, llvm::StringMap &moduleMap) { using namespace llvm::yaml; @@ -263,14 +274,9 @@ class ExplicitModuleMapParser { } else if (key == "sourceInfoPath") { result.moduleSourceInfoPath = val.str(); } else if (key == "isFramework") { - auto valStr = val.str(); - valStr.erase(std::remove(valStr.begin(), valStr.end(), '\n'), valStr.end()); - if (valStr.compare("true") == 0) - result.isFramework = true; - else if (valStr.compare("false") == 0) - result.isFramework = false; - else - llvm_unreachable("Unexpected JSON value for isFramework"); + result.isFramework = parseBoolValue(val); + } else if (key == "isSystem") { + result.isSystem = parseBoolValue(val); } else { // Being forgiving for future fields. continue; @@ -336,17 +342,25 @@ class ModuleInterfaceCheckerImpl: public ModuleInterfaceChecker { ASTContext &Ctx; std::string CacheDir; std::string PrebuiltCacheDir; + std::string BackupInterfaceDir; ModuleInterfaceLoaderOptions Opts; RequireOSSAModules_t RequiresOSSAModules; public: explicit ModuleInterfaceCheckerImpl(ASTContext &Ctx, StringRef cacheDir, StringRef prebuiltCacheDir, + StringRef BackupInterfaceDir, ModuleInterfaceLoaderOptions opts, RequireOSSAModules_t requiresOSSAModules) : Ctx(Ctx), CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir), + BackupInterfaceDir(BackupInterfaceDir), Opts(opts), RequiresOSSAModules(requiresOSSAModules) {} - + explicit ModuleInterfaceCheckerImpl(ASTContext &Ctx, StringRef cacheDir, + StringRef prebuiltCacheDir, + ModuleInterfaceLoaderOptions opts, + RequireOSSAModules_t requiresOSSAModules): + ModuleInterfaceCheckerImpl(Ctx, cacheDir, prebuiltCacheDir, StringRef(), + opts, requiresOSSAModules) {} std::vector getCompiledModuleCandidatesForInterface(StringRef moduleName, StringRef interfacePath) override; @@ -414,8 +428,10 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { SourceManager &SourceMgr, DiagnosticEngine &Diags, const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts, const ClangImporterOptions &ClangOpts, StringRef CacheDir, - StringRef PrebuiltCacheDir, StringRef ModuleName, StringRef InPath, - StringRef OutPath, bool SerializeDependencyHashes, + StringRef PrebuiltCacheDir, StringRef BackupInterfaceDir, + StringRef ModuleName, StringRef InPath, + StringRef OutPath, StringRef ABIOutputPath, + bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions Opts, RequireOSSAModules_t RequireOSSAModules); }; @@ -423,7 +439,9 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { private: SourceManager &SM; - DiagnosticEngine &Diags; +public: + DiagnosticEngine *Diags; +private: llvm::BumpPtrAllocator Allocator; llvm::StringSaver ArgSaver; std::vector GenericArgs; @@ -439,7 +457,7 @@ struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { // Diagnose this inside the interface file, if possible. loc = SM.getLocFromExternalSource(interfacePath, 1, 1); } - return Diags.diagnose(loc, ID, std::move(Args)...); + return Diags->diagnose(loc, ID, std::move(Args)...); } void inheritOptionsForBuildingInterface(const SearchPathOptions &SearchPathOpts, @@ -452,11 +470,12 @@ struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate { SourceLoc diagnosticLoc); public: InterfaceSubContextDelegateImpl( - SourceManager &SM, DiagnosticEngine &Diags, + SourceManager &SM, DiagnosticEngine *Diags, const SearchPathOptions &searchPathOpts, const LangOptions &langOpts, const ClangImporterOptions &clangImporterOpts, ModuleInterfaceLoaderOptions LoaderOpts, bool buildModuleCacheDirIfAbsent, StringRef moduleCachePath, StringRef prebuiltCachePath, + StringRef backupModuleInterfaceDir, bool serializeDependencyHashes, bool trackSystemDependencies, RequireOSSAModules_t requireOSSAModules); std::error_code runInSubContext(StringRef moduleName, diff --git a/include/swift/Frontend/ModuleInterfaceSupport.h b/include/swift/Frontend/ModuleInterfaceSupport.h index eb586bf42b6a7..086117212cfcf 100644 --- a/include/swift/Frontend/ModuleInterfaceSupport.h +++ b/include/swift/Frontend/ModuleInterfaceSupport.h @@ -20,6 +20,7 @@ #define SWIFT_INTERFACE_FORMAT_VERSION_KEY "swift-interface-format-version" #define SWIFT_COMPILER_VERSION_KEY "swift-compiler-version" #define SWIFT_MODULE_FLAGS_KEY "swift-module-flags" +#define SWIFT_MODULE_FLAGS_IGNORABLE_KEY "swift-module-flags-ignorable" namespace swift { @@ -41,6 +42,10 @@ struct ModuleInterfaceOptions { /// back .swiftinterface and reconstructing .swiftmodule. std::string Flags; + /// Flags that should be emitted to the .swiftinterface file but are OK to be + /// ignored by the earlier version of the compiler. + std::string IgnorableFlags; + /// Print SPI decls and attributes. bool PrintSPIs = false; diff --git a/include/swift/IDE/CodeCompletion.h b/include/swift/IDE/CodeCompletion.h index a3e3d89dec38a..53dcb0bd93bc8 100644 --- a/include/swift/IDE/CodeCompletion.h +++ b/include/swift/IDE/CodeCompletion.h @@ -16,6 +16,7 @@ #include "swift/AST/Identifier.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/OptionSet.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -32,12 +33,14 @@ class Decl; class DeclContext; class FrontendOptions; class ModuleDecl; +class SourceFile; namespace ide { class CodeCompletionCache; class CodeCompletionContext; class CodeCompletionResultBuilder; +struct CodeCompletionResultSink; struct RequestedCachedModule; /// A routine to remove code completion tokens from code completion @@ -135,29 +138,32 @@ class CodeCompletionStringChunk { /// Generic type parameter name. GenericParameterName, - /// The first chunk of a substring that describes the parameter for a + /// The first chunk of a substring that describes the argument for a /// function call. - CallParameterBegin, - /// Function call parameter name. - CallParameterName, - /// Function call parameter internal / local name. If the parameter has no - /// formal API name, it can still have a local name which can be useful - /// for display purposes. + CallArgumentBegin, + + /// Function call argument label. + CallArgumentName, + + /// Function parameter internal / local name for an call argument. If the + /// parameter has no formal API name, it can still have a local name which + /// can be useful for display purposes. /// /// This chunk should not be inserted into the editor buffer. - CallParameterInternalName, - /// A colon between parameter name and value. Should be inserted in the - /// editor buffer if the preceding CallParameterName was inserted. - CallParameterColon, + CallArgumentInternalName, + + /// A colon between argument name and value. Should be inserted in the + /// editor buffer if the preceding CallArgumentName was inserted. + CallArgumentColon, /// A colon between parameter name and value. Used in decl attribute. DeclAttrParamColon, - /// Required parameter type. - CallParameterType, + /// Required argument type. + CallArgumentType, - /// Parameter type tag for annotated results. - CallParameterTypeBegin, + /// Argument type tag for annotated results. + CallArgumentTypeBegin, /// System type name. TypeIdSystem, @@ -165,15 +171,15 @@ class CodeCompletionStringChunk { /// Non-system type name. TypeIdUser, - /// Desugared closure parameter type. This can be used to get the - /// closure type if CallParameterType is a TypeAliasType. - CallParameterClosureType, + /// Desugared closure argument type. This can be used to get the + /// closure type if CallArgumentType is a TypeAliasType. + CallArgumentClosureType, - /// An expanded closure expression for the value of a parameter, including + /// An expanded closure expression for the value of an argument, including /// the left and right braces and possible signature. The preferred /// position to put the cursor after the completion result is inserted /// into the editor buffer is between the braces. - CallParameterClosureExpr, + CallArgumentClosureExpr, /// A placeholder for \c ! or \c ? in a call to a method found by dynamic /// lookup. @@ -209,10 +215,10 @@ class CodeCompletionStringChunk { }; static bool chunkStartsNestedGroup(ChunkKind Kind) { - return Kind == ChunkKind::CallParameterBegin || + return Kind == ChunkKind::CallArgumentBegin || Kind == ChunkKind::GenericParameterBegin || Kind == ChunkKind::OptionalBegin || - Kind == ChunkKind::CallParameterTypeBegin || + Kind == ChunkKind::CallArgumentTypeBegin || Kind == ChunkKind::TypeAnnotationBegin; } @@ -240,14 +246,14 @@ class CodeCompletionStringChunk { Kind == ChunkKind::Ampersand || Kind == ChunkKind::Equal || Kind == ChunkKind::Whitespace || - Kind == ChunkKind::CallParameterName || - Kind == ChunkKind::CallParameterInternalName || - Kind == ChunkKind::CallParameterColon || + Kind == ChunkKind::CallArgumentName || + Kind == ChunkKind::CallArgumentInternalName || + Kind == ChunkKind::CallArgumentColon || Kind == ChunkKind::DeclAttrParamColon || Kind == ChunkKind::DeclAttrParamKeyword || - Kind == ChunkKind::CallParameterType || - Kind == ChunkKind::CallParameterClosureType || - Kind == ChunkKind::CallParameterClosureExpr || + Kind == ChunkKind::CallArgumentType || + Kind == ChunkKind::CallArgumentClosureType || + Kind == ChunkKind::CallArgumentClosureExpr || Kind == ChunkKind::GenericParameterName || Kind == ChunkKind::DynamicLookupMethodCallTail || Kind == ChunkKind::OptionalMethodCallTail || @@ -359,9 +365,6 @@ class alignas(detail::CodeCompletionStringChunk) CodeCompletionString final : Optional getFirstTextChunkIndex(bool includeLeadingPunctuation = false) const; - /// Concatenates all text chunks considered part of the name to \p OS. - void getName(raw_ostream &OS) const; - /// Print a debug representation of the code completion string to \p OS. void print(raw_ostream &OS) const; SWIFT_DEBUG_DUMP; @@ -375,33 +378,6 @@ enum class SemanticContextKind { /// Used in cases when the concept of semantic context is not applicable. None, - /// This is a highly-likely expression-context-specific completion - /// result. This description is intentionally vague: this is a catch-all - /// category for all heuristics for highly-likely results. - /// - /// For example, the name of an overridden superclass member inside a nominal - /// member function has ExpressionSpecific context: - /// \code - /// class Base { - /// init() {} - /// init(a: Int) {} - /// func foo() {} - /// func bar() {} - /// } - /// class Derived { - /// init() { - /// super. // init() -- ExpressionSpecific - /// // init(a: Int) -- Super - /// } - /// - /// func foo() { - /// super. // foo() -- ExpressionSpecific - /// // bar() -- Super - /// } - /// } - /// \endcode - ExpressionSpecific, - /// A declaration from the same function. Local, @@ -434,6 +410,33 @@ enum class SemanticContextKind { OtherModule, }; +enum class CodeCompletionFlairBit: uint8_t { + /// **Deprecated**. Old style catch-all prioritization. + ExpressionSpecific = 1 << 0, + + /// E.g. override func foo() { super.foo() ... + SuperChain = 1 << 1, + + /// Argument label and type. i.e. 'label: <#Ty#>'. + ArgumentLabels = 1 << 2, + + /// E.g. decl introducer or modifiers ('enum', 'protocol', 'public', etc.) at + /// top-level. + CommonKeywordAtCurrentPosition = 1 << 3, + + /// E.g. type decl introducer ('enum', 'class', etc.) in a function body. + RareKeywordAtCurrentPosition = 1 << 4, + + /// E.g. protocol names at an expression position. + RareTypeAtCurrentPosition = 1 << 5, + + /// E.g. referencing a type, function, etc… at top level position in a non + /// script/main.swift file + ExpressionAtNonScriptOrMainFileScope = 1 << 6, +}; + +using CodeCompletionFlair = OptionSet; + /// The declaration kind of a code completion result, if it is a declaration. enum class CodeCompletionDeclKind { Module, @@ -561,6 +564,16 @@ enum class CompletionKind { GenericRequirement, PrecedenceGroup, StmtLabel, + ForEachPatternBeginning, + TypeAttrBeginning, +}; + +enum class CodeCompletionDiagnosticSeverity: uint8_t { + None, + Error, + Warning, + Remark, + Note, }; /// A single code completion result. @@ -602,7 +615,9 @@ class CodeCompletionResult { enum class NotRecommendedReason { None = 0, RedundantImport, + RedundantImportIndirect, Deprecated, + SoftDeprecated, InvalidAsyncContext, CrossActorReference, VariableUsedInOwnDefinition, @@ -613,7 +628,7 @@ class CodeCompletionResult { unsigned AssociatedKind : 8; unsigned KnownOperatorKind : 6; unsigned SemanticContext : 3; - unsigned IsArgumentLabels : 1; + unsigned Flair: 8; unsigned NotRecommended : 4; unsigned IsSystem : 1; @@ -628,25 +643,26 @@ class CodeCompletionResult { private: CodeCompletionString *CompletionString; StringRef ModuleName; + StringRef SourceFilePath; StringRef BriefDocComment; ArrayRef AssociatedUSRs; - ArrayRef> DocWords; unsigned TypeDistance : 3; + unsigned DiagnosticSeverity: 3; + StringRef DiagnosticMessage; public: /// Constructs a \c Pattern, \c Keyword or \c BuiltinOperator result. /// /// \note The caller must ensure \c CodeCompletionString outlives this result. CodeCompletionResult(ResultKind Kind, SemanticContextKind SemanticContext, - bool IsArgumentLabels, unsigned NumBytesToErase, + CodeCompletionFlair Flair, unsigned NumBytesToErase, CodeCompletionString *CompletionString, ExpectedTypeRelation TypeDistance, CodeCompletionOperatorKind KnownOperatorKind = CodeCompletionOperatorKind::None, StringRef BriefDocComment = StringRef()) : Kind(Kind), KnownOperatorKind(unsigned(KnownOperatorKind)), - SemanticContext(unsigned(SemanticContext)), - IsArgumentLabels(unsigned(IsArgumentLabels)), + SemanticContext(unsigned(SemanticContext)), Flair(unsigned(Flair.toRaw())), NotRecommended(unsigned(NotRecommendedReason::None)), NumBytesToErase(NumBytesToErase), CompletionString(CompletionString), BriefDocComment(BriefDocComment), TypeDistance(TypeDistance) { @@ -659,6 +675,7 @@ class CodeCompletionResult { getOperatorKind() != CodeCompletionOperatorKind::None); AssociatedKind = 0; IsSystem = 0; + DiagnosticSeverity = 0; } /// Constructs a \c Keyword result. @@ -666,19 +683,20 @@ class CodeCompletionResult { /// \note The caller must ensure \c CodeCompletionString outlives this result. CodeCompletionResult(CodeCompletionKeywordKind Kind, SemanticContextKind SemanticContext, - bool IsArgumentLabels, unsigned NumBytesToErase, + CodeCompletionFlair Flair, + unsigned NumBytesToErase, CodeCompletionString *CompletionString, ExpectedTypeRelation TypeDistance, StringRef BriefDocComment = StringRef()) : Kind(Keyword), KnownOperatorKind(0), - SemanticContext(unsigned(SemanticContext)), - IsArgumentLabels(unsigned(IsArgumentLabels)), + SemanticContext(unsigned(SemanticContext)), Flair(unsigned(Flair.toRaw())), NotRecommended(unsigned(NotRecommendedReason::None)), NumBytesToErase(NumBytesToErase), CompletionString(CompletionString), BriefDocComment(BriefDocComment), TypeDistance(TypeDistance) { assert(CompletionString); AssociatedKind = static_cast(Kind); IsSystem = 0; + DiagnosticSeverity = 0; } /// Constructs a \c Literal result. @@ -686,17 +704,17 @@ class CodeCompletionResult { /// \note The caller must ensure \c CodeCompletionString outlives this result. CodeCompletionResult(CodeCompletionLiteralKind LiteralKind, SemanticContextKind SemanticContext, - bool IsArgumentLabels, unsigned NumBytesToErase, + CodeCompletionFlair Flair, unsigned NumBytesToErase, CodeCompletionString *CompletionString, ExpectedTypeRelation TypeDistance) : Kind(Literal), KnownOperatorKind(0), - SemanticContext(unsigned(SemanticContext)), - IsArgumentLabels(unsigned(IsArgumentLabels)), + SemanticContext(unsigned(SemanticContext)), Flair(unsigned(Flair.toRaw())), NotRecommended(unsigned(NotRecommendedReason::None)), NumBytesToErase(NumBytesToErase), CompletionString(CompletionString), TypeDistance(TypeDistance) { AssociatedKind = static_cast(LiteralKind); IsSystem = 0; + DiagnosticSeverity = 0; assert(CompletionString); } @@ -706,25 +724,23 @@ class CodeCompletionResult { /// arguments outlive this result, typically by storing them in the same /// \c CodeCompletionResultSink as the result itself. CodeCompletionResult(SemanticContextKind SemanticContext, - bool IsArgumentLabels, unsigned NumBytesToErase, + CodeCompletionFlair Flair, unsigned NumBytesToErase, CodeCompletionString *CompletionString, const Decl *AssociatedDecl, StringRef ModuleName, CodeCompletionResult::NotRecommendedReason NotRecReason, StringRef BriefDocComment, ArrayRef AssociatedUSRs, - ArrayRef> DocWords, enum ExpectedTypeRelation TypeDistance) : Kind(ResultKind::Declaration), KnownOperatorKind(0), - SemanticContext(unsigned(SemanticContext)), - IsArgumentLabels(unsigned(IsArgumentLabels)), + SemanticContext(unsigned(SemanticContext)), Flair(unsigned(Flair.toRaw())), NotRecommended(unsigned(NotRecReason)), NumBytesToErase(NumBytesToErase), CompletionString(CompletionString), ModuleName(ModuleName), BriefDocComment(BriefDocComment), - AssociatedUSRs(AssociatedUSRs), DocWords(DocWords), - TypeDistance(TypeDistance) { + AssociatedUSRs(AssociatedUSRs), TypeDistance(TypeDistance) { assert(AssociatedDecl && "should have a decl"); AssociatedKind = unsigned(getCodeCompletionDeclKind(AssociatedDecl)); IsSystem = getDeclIsSystem(AssociatedDecl); + DiagnosticSeverity = 0; assert(CompletionString); if (isOperator()) KnownOperatorKind = @@ -735,31 +751,37 @@ class CodeCompletionResult { // Used by deserialization. CodeCompletionResult(SemanticContextKind SemanticContext, - bool IsArgumentLabels, unsigned NumBytesToErase, + CodeCompletionFlair Flair, unsigned NumBytesToErase, CodeCompletionString *CompletionString, CodeCompletionDeclKind DeclKind, bool IsSystem, - StringRef ModuleName, + StringRef ModuleName, StringRef SourceFilePath, CodeCompletionResult::NotRecommendedReason NotRecReason, - StringRef BriefDocComment, + CodeCompletionDiagnosticSeverity diagSeverity, + StringRef DiagnosticMessage, StringRef BriefDocComment, ArrayRef AssociatedUSRs, - ArrayRef> DocWords, ExpectedTypeRelation TypeDistance, CodeCompletionOperatorKind KnownOperatorKind) : Kind(ResultKind::Declaration), KnownOperatorKind(unsigned(KnownOperatorKind)), - SemanticContext(unsigned(SemanticContext)), - IsArgumentLabels(unsigned(IsArgumentLabels)), + SemanticContext(unsigned(SemanticContext)), Flair(unsigned(Flair.toRaw())), NotRecommended(unsigned(NotRecReason)), IsSystem(IsSystem), NumBytesToErase(NumBytesToErase), CompletionString(CompletionString), - ModuleName(ModuleName), BriefDocComment(BriefDocComment), - AssociatedUSRs(AssociatedUSRs), DocWords(DocWords), - TypeDistance(TypeDistance) { + ModuleName(ModuleName), SourceFilePath(SourceFilePath), + BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs), + TypeDistance(TypeDistance), DiagnosticSeverity(unsigned(diagSeverity)), + DiagnosticMessage(DiagnosticMessage) { AssociatedKind = static_cast(DeclKind); assert(CompletionString); assert(!isOperator() || getOperatorKind() != CodeCompletionOperatorKind::None); } + /// Copy this result to \p Sink with \p newFlair . Note that this does NOT + /// copy the value of \c CompletionString , \c AssociatedUSRs etc. it only + /// copies the pointers to them. + CodeCompletionResult *withFlair(CodeCompletionFlair newFlair, + CodeCompletionResultSink &Sink); + ResultKind getKind() const { return static_cast(Kind); } CodeCompletionDeclKind getAssociatedDeclKind() const { @@ -811,8 +833,13 @@ class CodeCompletionResult { return static_cast(SemanticContext); } - bool isArgumentLabels() const { - return static_cast(IsArgumentLabels); + CodeCompletionFlair getFlair() const { + return static_cast(Flair); + } + + /// Modify "flair" of this result *in place*. + void setFlair(CodeCompletionFlair flair) { + Flair = unsigned(flair.toRaw()); } bool isNotRecommended() const { @@ -837,8 +864,27 @@ class CodeCompletionResult { return AssociatedUSRs; } - ArrayRef> getDeclKeywords() const { - return DocWords; + void setSourceFilePath(StringRef value) { + SourceFilePath = value; + } + + void setDiagnostics(CodeCompletionDiagnosticSeverity severity, StringRef message) { + DiagnosticSeverity = static_cast(severity); + DiagnosticMessage = message; + } + + CodeCompletionDiagnosticSeverity getDiagnosticSeverity() const { + return static_cast(DiagnosticSeverity); + } + + StringRef getDiagnosticMessage() const { + return DiagnosticMessage; + } + + /// Returns the source file path where the associated decl was declared. + /// Returns an empty string if the information is not available. + StringRef getSourceFilePath() const { + return SourceFilePath; } /// Print a debug representation of the code completion result to \p OS. @@ -853,6 +899,15 @@ class CodeCompletionResult { static bool getDeclIsSystem(const Decl *D); }; +/// A pair of a file path and its up-to-date-ness. +struct SourceFileAndUpToDate { + StringRef FilePath; + bool IsUpToDate; + + SourceFileAndUpToDate(StringRef FilePath, bool IsUpToDate) + : FilePath(FilePath), IsUpToDate(IsUpToDate) {} +}; + struct CodeCompletionResultSink { using AllocatorPtr = std::shared_ptr; @@ -865,8 +920,13 @@ struct CodeCompletionResultSink { /// Whether to annotate the results with XML. bool annotateResult = false; + bool requiresSourceFileInfo = false; + + /// Whether to emit object literals if desired. + bool includeObjectLiterals = true; std::vector Results; + std::vector SourceFiles; /// A single-element cache for module names stored in Allocator, keyed by a /// clang::Module * or swift::ModuleDecl *. @@ -935,7 +995,15 @@ class CodeCompletionContext { : Cache(Cache) {} void setAnnotateResult(bool flag) { CurrentResults.annotateResult = flag; } - bool getAnnotateResult() { return CurrentResults.annotateResult; } + bool getAnnotateResult() const { return CurrentResults.annotateResult; } + + void setRequiresSourceFileInfo(bool flag) { CurrentResults.requiresSourceFileInfo = flag; } + bool requiresSourceFileInfo() const { return CurrentResults.requiresSourceFileInfo; } + + void setIncludeObjectLiterals(bool flag) { + CurrentResults.includeObjectLiterals = flag; + } + bool includeObjectLiterals() { return CurrentResults.includeObjectLiterals; } /// Allocate a string owned by the code completion context. StringRef copyString(StringRef Str); @@ -961,7 +1029,7 @@ class CodeCompletionConsumer { virtual void handleResultsAndModules(CodeCompletionContext &context, ArrayRef requestedModules, - DeclContext *DCForModules) = 0; + DeclContext *DC) = 0; }; /// A simplified code completion consumer interface that clients can use to get @@ -975,8 +1043,7 @@ struct SimpleCachingCodeCompletionConsumer : public CodeCompletionConsumer { DeclContext *DCForModules) override; /// Clients should override this method to receive \p Results. - virtual void handleResults( - MutableArrayRef Results) = 0; + virtual void handleResults(CodeCompletionContext &context) = 0; }; /// A code completion result consumer that prints the results to a @@ -987,6 +1054,7 @@ class PrintingCodeCompletionConsumer bool IncludeKeywords; bool IncludeComments; bool PrintAnnotatedDescription; + bool RequiresSourceFileInfo = false; public: PrintingCodeCompletionConsumer(llvm::raw_ostream &OS, @@ -998,7 +1066,8 @@ class PrintingCodeCompletionConsumer IncludeComments(IncludeComments), PrintAnnotatedDescription(PrintAnnotatedDescription) {} - void handleResults(MutableArrayRef Results) override; + void handleResults(CodeCompletionContext &context) override; + void handleResults(MutableArrayRef Results); }; /// Create a factory for code completion callbacks. @@ -1014,14 +1083,15 @@ void lookupCodeCompletionResultsFromModule(CodeCompletionResultSink &targetSink, const ModuleDecl *module, ArrayRef accessPath, bool needLeadingDot, - const DeclContext *currDeclContext); + const SourceFile *SF); /// Copy code completion results from \p sourceSink to \p targetSink, possibly -/// restricting by \p onlyTypes. -void copyCodeCompletionResults(CodeCompletionResultSink &targetSink, - CodeCompletionResultSink &sourceSink, - bool onlyTypes, - bool onlyPrecedenceGroups); +/// restricting by \p onlyTypes. Returns copied results in \p targetSink. +MutableArrayRef +copyCodeCompletionResults(CodeCompletionResultSink &targetSink, + CodeCompletionResultSink &sourceSink, + bool onlyTypes, + bool onlyPrecedenceGroups); } // end namespace ide } // end namespace swift diff --git a/include/swift/IDE/DigesterEnums.def b/include/swift/IDE/DigesterEnums.def index 415db666c919d..b58b49fe0b471 100644 --- a/include/swift/IDE/DigesterEnums.def +++ b/include/swift/IDE/DigesterEnums.def @@ -72,6 +72,7 @@ NODE_KIND(DeclOperator, OperatorDecl) NODE_KIND(DeclType, TypeDecl) NODE_KIND(DeclVar, Var) NODE_KIND(DeclTypeAlias, TypeAlias) +NODE_KIND(DeclImport, Import) NODE_KIND(DeclAssociatedType, AssociatedType) NODE_KIND_RANGE(Decl, DeclFunction, DeclAssociatedType) diff --git a/include/swift/IDE/ModuleSourceFileInfo.h b/include/swift/IDE/ModuleSourceFileInfo.h new file mode 100644 index 0000000000000..0fb9142b3e2ab --- /dev/null +++ b/include/swift/IDE/ModuleSourceFileInfo.h @@ -0,0 +1,39 @@ +//===--- ModuleSourceFileInfo.h ---------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_MODULESOURCEFILEINFO_H +#define SWIFT_IDE_MODULESOURCEFILEINFO_H + +#include "swift/AST/RawComment.h" +#include "swift/Basic/BasicSourceInfo.h" +#include "swift/Basic/LLVM.h" + +namespace swift { +class ASTContext; +class Decl; +namespace ide { + +/// Get the source file path where \p D is declared. Returns an empty string +/// if the information is not available. +StringRef getSourceFilePathForDecl(const Decl *D); + +/// Check if the source file of \p info is up-to-date. +/// * \c true if the mtime and the size are the same. +/// * \c true if the interface has hasn't changed. +/// * \c false otherwise. +bool isSourceFileUpToDate(const BasicSourceFileInfo &info, ASTContext &Ctx); + + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_MODULESOURCEFILEINFO_H diff --git a/include/swift/IDE/RefactoringKinds.def b/include/swift/IDE/RefactoringKinds.def index 6adebad2c6ede..4f4633252d99a 100644 --- a/include/swift/IDE/RefactoringKinds.def +++ b/include/swift/IDE/RefactoringKinds.def @@ -60,6 +60,8 @@ CURSOR_REFACTORING(ConvertToAsync, "Convert Function to Async", convert.func-to- CURSOR_REFACTORING(AddAsyncAlternative, "Add Async Alternative", add.async-alternative) +CURSOR_REFACTORING(AddAsyncWrapper, "Add Async Wrapper", add.async-wrapper) + RANGE_REFACTORING(ExtractExpr, "Extract Expression", extract.expr) RANGE_REFACTORING(ExtractFunction, "Extract Method", extract.function) diff --git a/include/swift/IDE/SourceEntityWalker.h b/include/swift/IDE/SourceEntityWalker.h index 4c9f35d7c3d11..6d679e11095c0 100644 --- a/include/swift/IDE/SourceEntityWalker.h +++ b/include/swift/IDE/SourceEntityWalker.h @@ -14,9 +14,11 @@ #define SWIFT_IDE_SOURCE_ENTITY_WALKER_H #include "swift/AST/ASTWalker.h" +#include "swift/Basic/Defer.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/PointerUnion.h" +#include "llvm/Support/SaveAndRestore.h" namespace clang { class Module; @@ -71,6 +73,9 @@ class SourceEntityWalker { /// Walks the provided Expr. /// \returns true if traversal was aborted, false otherwise. bool walk(Expr *E); + /// Walks the provided Pattern. + /// \returns true if traversal was aborted, false otherwise. + bool walk(Pattern *P); /// Walks the provided ASTNode. /// \returns true if traversal was aborted, false otherwise. bool walk(ASTNode N); @@ -99,6 +104,14 @@ class SourceEntityWalker { /// returns false, the remaining traversal is terminated and returns failure. virtual bool walkToExprPost(Expr *E) { return true; } + /// This method is called when first visiting a pattern, before walking + /// into its children. If it returns false, the subtree is skipped. + virtual bool walkToPatternPre(Pattern *P) { return true; } + + /// This method is called after visiting the children of a pattern. If it + /// returns false, the remaining traversal is terminated and returns failure. + virtual bool walkToPatternPost(Pattern *P) { return true; } + /// This method is called when a ValueDecl is referenced in source. If it /// returns false, the remaining traversal is terminated and returns failure. /// @@ -176,6 +189,21 @@ class SourceEntityWalker { virtual ~SourceEntityWalker() {} virtual void anchor(); + + /// Retrieve the current ASTWalker being used to traverse the AST. + const ASTWalker &getWalker() const { + assert(Walker && "Not walking!"); + return *Walker; + } + +private: + ASTWalker *Walker = nullptr; + + /// Utility that lets us keep track of an ASTWalker when walking. + bool performWalk(ASTWalker &W, llvm::function_ref DoWalk) { + llvm::SaveAndRestore SV(Walker, &W); + return DoWalk(); + } }; } // namespace swift diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index 0538f9ad22851..944168ebeb02b 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -17,6 +17,7 @@ #include "swift/Basic/LLVM.h" #include "swift/AST/ASTNode.h" #include "swift/AST/DeclNameLoc.h" +#include "swift/AST/Effects.h" #include "swift/AST/Module.h" #include "swift/AST/ASTPrinter.h" #include "swift/IDE/SourceEntityWalker.h" @@ -345,7 +346,7 @@ struct ResolvedRangeInfo { ArrayRef TokensInRange; CharSourceRange ContentRange; bool HasSingleEntry; - bool ThrowingUnhandledError; + PossibleEffects UnhandledEffects; OrphanKind Orphan; // The topmost ast nodes contained in the given range. @@ -359,7 +360,7 @@ struct ResolvedRangeInfo { ArrayRef TokensInRange, DeclContext* RangeContext, Expr *CommonExprParent, bool HasSingleEntry, - bool ThrowingUnhandledError, + PossibleEffects UnhandledEffects, OrphanKind Orphan, ArrayRef ContainedNodes, ArrayRef DeclaredDecls, ArrayRef ReferencedDecls): Kind(Kind), @@ -367,7 +368,7 @@ struct ResolvedRangeInfo { TokensInRange(TokensInRange), ContentRange(calculateContentRange(TokensInRange)), HasSingleEntry(HasSingleEntry), - ThrowingUnhandledError(ThrowingUnhandledError), + UnhandledEffects(UnhandledEffects), Orphan(Orphan), ContainedNodes(ContainedNodes), DeclaredDecls(DeclaredDecls), ReferencedDecls(ReferencedDecls), @@ -376,7 +377,7 @@ struct ResolvedRangeInfo { ResolvedRangeInfo(ArrayRef TokensInRange) : ResolvedRangeInfo(RangeKind::Invalid, {nullptr, ExitState::Unsure}, TokensInRange, nullptr, /*Commom Expr Parent*/nullptr, - /*Single entry*/true, /*unhandled error*/false, + /*Single entry*/true, /*UnhandledEffects*/{}, OrphanKind::None, {}, {}, {}) {} ResolvedRangeInfo(): ResolvedRangeInfo(ArrayRef()) {} void print(llvm::raw_ostream &OS) const; @@ -526,8 +527,7 @@ class SourceEditTextConsumer : public SourceEditConsumer { llvm::raw_ostream &OS; public: - SourceEditTextConsumer(llvm::raw_ostream &OS); - + SourceEditTextConsumer(llvm::raw_ostream &OS) : OS(OS) {} void accept(SourceManager &SM, RegionType RegionType, ArrayRef Replacements) override; }; @@ -543,6 +543,18 @@ class SourceEditOutputConsumer : public SourceEditConsumer { void accept(SourceManager &SM, RegionType RegionType, ArrayRef Replacements) override; }; +/// Broadcasts `accept` to all `Consumers` +class BroadcastingSourceEditConsumer : public SourceEditConsumer { + ArrayRef> Consumers; + +public: + BroadcastingSourceEditConsumer( + ArrayRef> Consumers) + : Consumers(Consumers) {} + void accept(SourceManager &SM, RegionType RegionType, + ArrayRef Replacements) override; +}; + enum class LabelRangeEndAt: int8_t { BeforeElemStart, LabelNameOnly, @@ -576,8 +588,10 @@ ClangNode extensionGetClangNode(const ExtensionDecl *ext); /// Utility for finding the referenced declaration from a call, which might /// include a second level of function application for a 'self.' expression, -/// or a curry thunk, etc. -std::pair getReferencedDecl(Expr *expr); +/// or a curry thunk, etc. If \p semantic is true then the underlying semantic +/// expression of \p expr is used. +std::pair getReferencedDecl(Expr *expr, + bool semantic = true); /// Whether the last expression in \p ExprStack is being called. bool isBeingCalled(ArrayRef ExprStack); diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index fce8410e3e8ee..f4d93aa5bbcd4 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -1196,9 +1196,12 @@ class LinkEntity { return entity; } - static LinkEntity forAsyncFunctionPointer(AbstractFunctionDecl *decl) { + static LinkEntity forAsyncFunctionPointer(SILDeclRef declRef) { LinkEntity entity; - entity.setForDecl(Kind::AsyncFunctionPointerAST, decl); + entity.setForDecl(Kind::AsyncFunctionPointerAST, + declRef.getAbstractFunctionDecl()); + entity.SecondaryPointer = + reinterpret_cast(static_cast(declRef.kind)); return entity; } @@ -1417,6 +1420,7 @@ struct IRLinkage { static const IRLinkage InternalWeakODR; static const IRLinkage Internal; + static const IRLinkage ExternalCommon; static const IRLinkage ExternalImport; static const IRLinkage ExternalWeakImport; static const IRLinkage ExternalExport; diff --git a/include/swift/Localization/LocalizationFormat.h b/include/swift/Localization/LocalizationFormat.h index 93cb165c621c2..63b6e7d85f2ef 100644 --- a/include/swift/Localization/LocalizationFormat.h +++ b/include/swift/Localization/LocalizationFormat.h @@ -44,6 +44,12 @@ namespace diag { using namespace llvm::support; +enum LocalizationProducerState : uint8_t { + NotInitialized, + Initialized, + FailedInitialization +}; + class DefToYAMLConverter { llvm::ArrayRef IDs; llvm::ArrayRef Messages; @@ -162,6 +168,7 @@ class LocalizationProducer { llvm::BumpPtrAllocator localizationAllocator; llvm::StringSaver localizationSaver; bool printDiagnosticNames; + LocalizationProducerState state = NotInitialized; public: LocalizationProducer(bool printDiagnosticNames = false) @@ -173,9 +180,25 @@ class LocalizationProducer { virtual llvm::StringRef getMessageOr(swift::DiagID id, llvm::StringRef defaultMessage); + /// \returns a `SerializedLocalizationProducer` pointer if the serialized + /// diagnostics file available, otherwise returns a `YAMLLocalizationProducer` + /// if the `YAML` file is available. If both files aren't available returns a + /// `nullptr`. + static std::unique_ptr + producerFor(llvm::StringRef locale, llvm::StringRef path, + bool printDiagnosticNames); + virtual ~LocalizationProducer() {} protected: + LocalizationProducerState getState() const; + + /// Used to lazily initialize `LocalizationProducer`s. + /// \returns true if the producer is successfully initialized, false + /// otherwise. + virtual bool initializeImpl() = 0; + virtual void initializeIfNeeded() final; + /// Retrieve a message for the given diagnostic id. /// \returns empty string if message couldn't be found. virtual llvm::StringRef getMessage(swift::DiagID id) const = 0; @@ -183,6 +206,7 @@ class LocalizationProducer { class YAMLLocalizationProducer final : public LocalizationProducer { std::vector diagnostics; + std::string filePath; public: /// The diagnostics IDs that are no longer available in `.def` @@ -194,9 +218,10 @@ class YAMLLocalizationProducer final : public LocalizationProducer { /// maintained by this producer, callback gets each translation /// with its unique identifier. void forEachAvailable( - llvm::function_ref callback) const; + llvm::function_ref callback); protected: + bool initializeImpl() override; llvm::StringRef getMessage(swift::DiagID id) const override; }; @@ -213,6 +238,7 @@ class SerializedLocalizationProducer final : public LocalizationProducer { bool printDiagnosticNames = false); protected: + bool initializeImpl() override; llvm::StringRef getMessage(swift::DiagID id) const override; }; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index bba4435087e06..0097ea215f0ff 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -64,6 +64,10 @@ def emit_fixits_path : Separate<["-"], "emit-fixits-path">, MetaVarName<"">, HelpText<"Output compiler fixits as source edits to ">; +def emit_abi_descriptor_path + : Separate<["-"], "emit-abi-descriptor-path">, MetaVarName<"">, + HelpText<"Output the ABI descriptor of current module to ">; + def serialize_module_interface_dependency_hashes : Flag<["-"], "serialize-module-interface-dependency-hashes">, Flags<[HelpHidden]>; @@ -126,9 +130,6 @@ def diagnostic_documentation_path : Separate<["-"], "diagnostic-documentation-path">, MetaVarName<"">, HelpText<"Path to diagnostic documentation resources">; -def enable_swiftcall : Flag<["-"], "enable-swiftcall">, - HelpText<"Enable the use of LLVM swiftcall support">; - def enable_testable_attr_requires_testable_module : Flag<["-"], "enable-testable-attr-requires-testable-module">, HelpText<"Enable checking of @testable">; @@ -163,16 +164,15 @@ def no_serialize_debugging_options : def autolink_library : Separate<["-"], "autolink-library">, HelpText<"Add dependent library">, Flags<[FrontendOption]>; +def public_autolink_library : Separate<["-"], "public-autolink-library">, + HelpText<"Add public dependent library">, Flags<[FrontendOption]>; + def disable_typo_correction : Flag<["-"], "disable-typo-correction">, HelpText<"Disable typo correction">; def disable_implicit_swift_modules: Flag<["-"], "disable-implicit-swift-modules">, HelpText<"Disable building Swift modules implicitly by the compiler">; -def swift_module_file - : Separate<["-"], "swift-module-file">, MetaVarName<"">, - HelpText<"Specify Swift module explicitly built from textual interface">; - def explict_swift_module_map : Separate<["-"], "explicit-swift-module-map-file">, MetaVarName<"">, HelpText<"Specify a JSON file containing information of explict Swift modules">; @@ -188,6 +188,18 @@ def batch_scan_input_file def import_prescan : Flag<["-"], "import-prescan">, HelpText<"When performing a dependency scan, only dentify all imports of the main Swift module sources">; +def serialize_dependency_scan_cache : Flag<["-"], "serialize-dependency-scan-cache">, + HelpText<"After performing a dependency scan, serialize the scanner's internal state.">; + +def reuse_dependency_scan_cache : Flag<["-"], "load-dependency-scan-cache">, + HelpText<"After performing a dependency scan, serialize the scanner's internal state.">; + +def dependency_scan_cache_path : Separate<["-"], "dependency-scan-cache-path">, + HelpText<"The path to output the dependency scanner's internal state.">; + +def dependency_scan_cache_remarks : Flag<["-"], "Rdependency-scan-cache">, + HelpText<"Emit remarks indicating use of the serialized module dependency scanning cache.">; + def enable_copy_propagation : Flag<["-"], "enable-copy-propagation">, HelpText<"Run SIL copy propagation to shorten object lifetime.">; def disable_copy_propagation : Flag<["-"], "disable-copy-propagation">, @@ -199,6 +211,12 @@ def enable_infer_public_concurrent_value : Flag<["-"], "enable-infer-public-send def disable_infer_public_concurrent_value : Flag<["-"], "disable-infer-public-sendable">, HelpText<"Disable inference of Sendable conformances for public structs and enums">; +def Raccess_note : Separate<["-"], "Raccess-note">, + MetaVarName<"none|failures|all|all-validate">, + HelpText<"Control access note remarks (default: all)">; +def Raccess_note_EQ : Joined<["-"], "Raccess-note=">, + Alias; + } // end let Flags = [FrontendOption, NoDriverOption] def debug_crash_Group : OptionGroup<"">; @@ -229,9 +247,9 @@ def enable_experimental_concurrency : Flag<["-"], "enable-experimental-concurrency">, HelpText<"Enable experimental concurrency model">; -def enable_experimental_async_handler : - Flag<["-"], "enable-experimental-async-handler">, - HelpText<"Enable experimental @asyncHandler feature">; +def enable_experimental_distributed : + Flag<["-"], "enable-experimental-distributed">, + HelpText<"Enable experimental 'distributed' actors and functions">; def enable_experimental_flow_sensitive_concurrent_captures : Flag<["-"], "enable-experimental-flow-sensitive-concurrent-captures">, @@ -259,6 +277,24 @@ def debug_constraints_on_line_EQ : Joined<["-"], "debug-constraints-on-line=">, def disable_named_lazy_member_loading : Flag<["-"], "disable-named-lazy-member-loading">, HelpText<"Disable per-name lazy member loading">; +def dump_requirement_machine : Flag<["-"], "dump-requirement-machine">, + HelpText<"Enables dumping rewrite systems from the generics implementation">; + +def debug_requirement_machine : Joined<["-"], "debug-requirement-machine=">, + HelpText<"Fine-grained debug output from the generics implementation">; + +def analyze_requirement_machine : Flag<["-"], "analyze-requirement-machine">, + Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, + HelpText<"Print out requirement machine statistics at the end of the compilation job">; + +def requirement_machine_step_limit : Separate<["-"], "requirement-machine-step-limit">, + Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, + HelpText<"Set the maximum steps before we give up on confluent completion">; + +def requirement_machine_depth_limit : Separate<["-"], "requirement-machine-depth-limit">, + Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, + HelpText<"Set the maximum depth before we give up on confluent completion">; + def debug_generic_signatures : Flag<["-"], "debug-generic-signatures">, HelpText<"Debug generic signatures">; @@ -286,6 +322,9 @@ def debug_crash_immediately : Flag<["-"], "debug-crash-immediately">, def debug_crash_after_parse : Flag<["-"], "debug-crash-after-parse">, DebugCrashOpt, HelpText<"Force a crash after parsing">; +def debug_test_dependency_scan_cache_serialization: Flag<["-"], "test-dependency-scan-cache-serialization">, + HelpText<"After performing a dependency scan, serialize and then deserialize the scanner's internal state.">; + def debugger_support : Flag<["-"], "debugger-support">, HelpText<"Process swift code as if running in the debugger">; @@ -297,6 +336,10 @@ def disable_implicit_concurrency_module_import : Flag<["-"], "disable-implicit-concurrency-module-import">, HelpText<"Disable the implicit import of the _Concurrency module.">; +def disable_implicit_distributed_module_import : Flag<["-"], + "disable-implicit-distributed-module-import">, + HelpText<"Disable the implicit import of the _Distributed module.">; + def disable_arc_opts : Flag<["-"], "disable-arc-opts">, HelpText<"Don't run SIL ARC optimization passes.">; def disable_ossa_opts : Flag<["-"], "disable-ossa-opts">, @@ -333,9 +376,6 @@ def disable_sil_perf_optzns : Flag<["-"], "disable-sil-perf-optzns">, def disable_swift_specific_llvm_optzns : Flag<["-"], "disable-swift-specific-llvm-optzns">, HelpText<"Don't run Swift specific LLVM optimization passes.">; -def disable_llvm_slp_vectorizer : Flag<["-"], "disable-llvm-slp-vectorizer">, - HelpText<"Don't run LLVM SLP vectorizer">; - def disable_llvm_verify : Flag<["-"], "disable-llvm-verify">, HelpText<"Don't run the LLVM IR verifier.">; @@ -398,6 +438,14 @@ def enable_experimental_static_assert : Flag<["-"], "enable-experimental-static-assert">, HelpText<"Enable experimental #assert">; +def enable_experimental_named_opaque_types : + Flag<["-"], "enable-experimental-named-opaque-types">, + HelpText<"Enable experimental support for named opaque result types">; + +def enable_experimental_structural_opaque_types : + Flag<["-"], "enable-experimental-structural-opaque-types">, + HelpText<"Enable experimental support for structural opaque result types">; + def enable_deserialization_recovery : Flag<["-"], "enable-deserialization-recovery">, HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">; @@ -417,6 +465,14 @@ def disable_conformance_availability_errors : Flag<["-"], "disable-conformance-availability-errors">, HelpText<"Diagnose conformance availability violations as warnings">; +def warn_on_potentially_unavailable_enum_case : Flag<["-"], + "warn-on-potentially-unavailable-enum-case">, + HelpText<"Downgrade potential unavailability of enum case to a warning">; + +def warn_on_editor_placeholder : Flag<["-"], + "warn-on-editor-placeholder">, + HelpText<"Downgrade the editor placeholder error to a warning">; + def report_errors_to_debugger : Flag<["-"], "report-errors-to-debugger">, HelpText<"Deprecated, will be removed in future versions.">; @@ -429,11 +485,6 @@ def disable_swift3_objc_inference : Flags<[FrontendOption, HelpHidden]>, HelpText<"Disable Swift 3's @objc inference rules for NSObject-derived classes and 'dynamic' members (emulates Swift 4 behavior)">; -def library_level : Separate<["-"], "library-level">, - MetaVarName<"">, - Flags<[FrontendOption, ModuleInterfaceOption]>, - HelpText<"Library distribution level 'api', 'spi' or 'other' (the default)">; - def enable_implicit_dynamic : Flag<["-"], "enable-implicit-dynamic">, Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>, HelpText<"Add 'dynamic' to all declarations">; @@ -598,9 +649,11 @@ def print_inst_counts : Flag<["-"], "print-inst-counts">, HelpText<"Before IRGen, count all the various SIL instructions. Must be used " "in conjunction with -print-stats.">; -def debug_on_sil : Flag<["-"], "gsil">, +def debug_on_sil : Flag<["-"], "sil-based-debuginfo">, HelpText<"Write the SIL into a file and generate debug-info to debug on SIL " " level.">; +def legacy_gsil : Flag<["-"], "gsil">, + HelpText<"Deprecated, use '-sil-based-debuginfo' instead">; def print_llvm_inline_tree : Flag<["-"], "print-llvm-inline-tree">, HelpText<"Print the LLVM inline tree.">; @@ -695,6 +748,13 @@ def prebuilt_module_cache_path_EQ : Joined<["-"], "prebuilt-module-cache-path=">, Alias; +def backup_module_interface_path : + Separate<["-"], "backup-module-interface-path">, + HelpText<"Directory of module interfaces as backups to those from SDKs">; +def backup_module_interface_path_EQ : + Joined<["-"], "backup-module-interface-path=">, + Alias; + def force_public_linkage : Flag<["-"], "force-public-linkage">, HelpText<"Force public linkage for private symbols. Used by LLDB.">; @@ -738,10 +798,6 @@ def type_info_dump_filter_EQ : Joined<["-"], "type-info-dump-filter=">, Flags<[FrontendOption]>, HelpText<"One of 'all', 'resilient' or 'fragile'">; -def emit_ldadd_cfile_path - : Separate<["-"], "emit-ldadd-cfile-path">, MetaVarName<"">, - HelpText<"Generate .c file defining symbols to add back">; - def previous_module_installname_map_file : Separate<["-"], "previous-module-installname-map-file">, MetaVarName<"">, HelpText<"Path to a Json file indicating module name to installname map for @_originallyDefinedIn">; diff --git a/include/swift/Option/Options.h b/include/swift/Option/Options.h index b75a28a12f4c6..0daa2f2aef587 100644 --- a/include/swift/Option/Options.h +++ b/include/swift/Option/Options.h @@ -40,6 +40,8 @@ namespace options { SwiftAPIExtractOption = (1 << 15), SwiftSymbolGraphExtractOption = (1 << 16), SwiftAPIDigesterOption = (1 << 17), + NewDriverOnlyOption = (1 << 18), + ModuleInterfaceOptionIgnorable = (1 << 19), }; enum ID { diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 792fd61593b47..8c91e1b210055 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -51,6 +51,11 @@ def ArgumentIsPath : OptionFlag; // and read/parsed from there when reconstituting a .swiftmodule from it. def ModuleInterfaceOption : OptionFlag; +// The option should be written into a .swiftinterface module interface file, +// and read/parsed from there when reconstituting a .swiftmodule from it. +// The option can be safely ignored by the older compiler. +def ModuleInterfaceOptionIgnorable : OptionFlag; + // The option causes the output of a supplementary output, or is the path option // for a supplementary output. E.g., `-emit-module` and `-emit-module-path`. def SupplementaryOutput : OptionFlag; @@ -64,6 +69,9 @@ def SwiftSymbolGraphExtractOption : OptionFlag; // The option should be accepted by swift-api-digester. def SwiftAPIDigesterOption : OptionFlag; +// The option only functions in the new driver. +def NewDriverOnlyOption : OptionFlag; + ///////// // Options @@ -410,6 +418,11 @@ def define_availability : Separate<["-"], "define-availability">, HelpText<"Define an availability macro in the format 'macroName : iOS 13.0, macOS 10.15'">, MetaVarName<"">; +def library_level : Separate<["-"], "library-level">, + MetaVarName<"">, + Flags<[HelpHidden, FrontendOption, ModuleInterfaceOption]>, + HelpText<"Library distribution level 'api', 'spi' or 'other' (the default)">; + def module_name : Separate<["-"], "module-name">, Flags<[FrontendOption, ModuleInterfaceOption, SwiftAPIExtractOption, SwiftSymbolGraphExtractOption]>, @@ -541,6 +554,10 @@ def disable_bridging_pch : Flag<["-"], "disable-bridging-pch">, def lto : Joined<["-"], "lto=">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Specify the LTO type to either 'llvm-thin' or 'llvm-full'">; + +def lto_library : Separate<["-"], "lto-library">, + Flags<[FrontendOption, ArgumentIsPath, NoInteractiveOption]>, + HelpText<"Perform LTO with ">, MetaVarName<"">; def access_notes_path : Separate<["-"], "access-notes-path">, Flags<[FrontendOption, ArgumentIsPath]>, @@ -568,16 +585,6 @@ def enable_experimental_concise_pound_file : Flag<["-"], Flags<[FrontendOption, ModuleInterfaceOption]>, HelpText<"Enable experimental concise '#file' identifier">; -def disable_fuzzy_forward_scan_trailing_closure_matching : Flag<["-"], - "disable-fuzzy-forward-scan-trailing-closure-matching">, - Flags<[FrontendOption]>, - HelpText<"Disable fuzzy forward-scan trailing closure matching">; - -def enable_fuzzy_forward_scan_trailing_closure_matching : Flag<["-"], - "enable-fuzzy-forward-scan-trailing-closure-matching">, - Flags<[FrontendOption]>, - HelpText<"Enable fuzzy forward-scan trailing closure matching">; - def enable_experimental_cxx_interop : Flag<["-"], "enable-experimental-cxx-interop">, HelpText<"Allow importing C++ modules into Swift (experimental feature)">; @@ -586,6 +593,14 @@ def experimental_cxx_stdlib : Separate<["-"], "experimental-cxx-stdlib">, HelpText<"C++ standard library to use; forwarded to Clang's -stdlib flag">; +def experimental_emit_module_separately: + Flag<["-"], "experimental-emit-module-separately">, + Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>, + HelpText<"Schedule a swift module emission job instead of a merge-modules job (new Driver only)">; + +def requirement_machine_EQ : Joined<["-"], "requirement-machine=">, + Flags<[FrontendOption, ModuleInterfaceOption]>, + HelpText<"Control usage of experimental generics implementation: 'on', 'off', or 'verify'">; // Diagnostic control options def suppress_warnings : Flag<["-"], "suppress-warnings">, @@ -639,7 +654,7 @@ def warn_swift3_objc_inference : Flag<["-"], "warn-swift3-objc-inference">, Flags<[FrontendOption, DoesNotAffectIncrementalBuild, HelpHidden]>; def warn_concurrency : Flag<["-"], "warn-concurrency">, - Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, + Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ModuleInterfaceOptionIgnorable]>, HelpText<"Warn about code that is unsafe according to the Swift Concurrency " "model and will become ill-formed in a future language version">; @@ -681,7 +696,7 @@ def libc : Separate<["-"], "libc">, HelpText<"libc runtime library to use">; def linker_option_Group : OptionGroup<"">; -def l : Joined<["-"], "l">, Group, +def l : JoinedOrSeparate<["-"], "l">, Group, Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, HelpText<"Specifies a library which should be linked against">; def framework : Separate<["-"], "framework">, Group, @@ -1074,6 +1089,14 @@ def target_variant : Separate<["-"], "target-variant">, HelpText<"Generate 'zippered' code for macCatalyst that can run on the specified" " variant target triple in addition to the main -target triple">; +def clang_target : Separate<["-"], "clang-target">, + Flags<[FrontendOption, SwiftAPIExtractOption, SwiftSymbolGraphExtractOption, SwiftAPIDigesterOption]>, + HelpText<"Separately set the target we should use for internal Clang instance">; + +def disable_clang_target : Flag<["-"], "disable-clang-target">, + Flags<[NewDriverOnlyOption]>, + HelpText<"Disable a separately specified target triple for Clang instance to use">; + def profile_generate : Flag<["-"], "profile-generate">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Generate instrumented code to collect execution counts">; @@ -1181,7 +1204,7 @@ def working_directory_EQ : Joined<["-"], "working-directory=">, Alias; def user_module_version : Separate<["-"], "user-module-version">, - Flags<[FrontendOption, ModuleInterfaceOption]>, + Flags<[FrontendOption, ModuleInterfaceOptionIgnorable, NewDriverOnlyOption]>, HelpText<"Module version specified from Swift module authors">, MetaVarName<"">; @@ -1208,6 +1231,12 @@ def disable_autolinking_runtime_compatibility_dynamic_replacements HelpText<"Do not use autolinking for the dynamic replacement runtime " "compatibility library">; +def disable_autolinking_runtime_compatibility_concurrency + : Flag<[ "-" ], "disable-autolinking-runtime-compatibility-concurrency">, + Flags<[ FrontendOption ]>, + HelpText<"Do not use autolinking for the concurrency runtime " + "compatibility library">; + def emit_symbol_graph: Flag<["-"], "emit-symbol-graph">, Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>, HelpText<"Emit a symbol graph">; @@ -1244,6 +1273,11 @@ def skip_inherited_docs : Flag<["-"], "skip-inherited-docs">, HelpText<"Skip emitting doc comments for members inherited through classes or " "default implementations">; +def include_spi_symbols : Flag<["-"], "include-spi-symbols">, + Flags<[SwiftSymbolGraphExtractOption, FrontendOption, + NoInteractiveOption, SupplementaryOutput, HelpHidden]>, + HelpText<"Add symbols with SPI information to the symbol graph">; + // swift-api-digester-only options def dump_sdk: Flag<["-", "--"], "dump-sdk">, Flags<[NoDriverOption, SwiftAPIDigesterOption]>, @@ -1341,6 +1375,10 @@ def abort_on_module_fail: Flag<["-", "--"], "abort-on-module-fail">, Flags<[NoDriverOption, SwiftAPIDigesterOption]>, HelpText<"Abort if a module failed to load">; +def disable_fail_on_error: Flag<["-", "--"], "disable-fail-on-error">, + Flags<[NoDriverOption, SwiftAPIDigesterOption]>, + HelpText<"Don't exit with a nonzero status if errors are emitted">; + def debug_mapping: Flag<["-", "--"], "debug-mapping">, Flags<[NoDriverOption, SwiftAPIDigesterOption]>, HelpText<"Dumping information for debug purposes">; diff --git a/include/swift/Parse/CodeCompletionCallbacks.h b/include/swift/Parse/CodeCompletionCallbacks.h index 6b5b161f83567..6bb9f57342cd1 100644 --- a/include/swift/Parse/CodeCompletionCallbacks.h +++ b/include/swift/Parse/CodeCompletionCallbacks.h @@ -231,6 +231,11 @@ class CodeCompletionCallbacks { virtual void completeStmtLabel(StmtKind ParentKind) {}; + virtual + void completeForEachPatternBeginning(bool hasTry, bool hasAwait) {}; + + virtual void completeTypeAttrBeginning() {}; + /// Signals that the AST for the all the delayed-parsed code was /// constructed. No \c complete*() callbacks will be done after this. virtual void doneParsing() = 0; diff --git a/include/swift/Parse/Lexer.h b/include/swift/Parse/Lexer.h index 419f75816f5df..6ef6cfcab557f 100644 --- a/include/swift/Parse/Lexer.h +++ b/include/swift/Parse/Lexer.h @@ -448,7 +448,7 @@ class Lexer { /// the byte content. /// /// If a copy needs to be made, it will be allocated out of the provided - /// \p Buffer. + /// \p Buffer. If \p IndentToStrip is '~0U', the indent is auto-detected. static StringRef getEncodedStringSegment(StringRef Str, SmallVectorImpl &Buffer, bool IsFirstSegment = false, diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index b040e7a0c5c39..132c971e43a17 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -556,6 +556,29 @@ class Parser { /// Return the next token that will be installed by \c consumeToken. const Token &peekToken(); + /// Consumes K tokens within a backtracking scope before calling \c f and + /// providing it with the backtracking scope. Unless if the backtracking is + /// explicitly cancelled, the parser's token state is restored after \c f + /// returns. + /// + /// \param K The number of tokens ahead to skip. Zero is the current token. + /// \param f The function to apply after skipping K tokens ahead. + /// The value returned by \c f will be returned by \c peekToken + /// after the parser is rolled back. + /// \returns the value returned by \c f + /// \note When calling, you may need to specify the \c Val type + /// explicitly as a type parameter. + template + Val lookahead(unsigned char K, + llvm::function_ref f) { + CancellableBacktrackingScope backtrackScope(*this); + + for (unsigned char i = 0; i < K; ++i) + consumeToken(); + + return f(backtrackScope); + } + /// Consume a token that we created on the fly to correct the original token /// stream from lexer. void consumeExtraToken(Token K); @@ -745,6 +768,14 @@ class Parser { Context.LangOpts.ParseForSyntaxTreeOnly; } + /// Returns true to indicate that experimental 'distributed actor' syntax + /// should be parsed if the parser is only a syntax tree or if the user has + /// passed the `-enable-experimental-distributed' flag to the frontend. + bool shouldParseExperimentalDistributed() const { + return Context.LangOpts.EnableExperimentalDistributed || + Context.LangOpts.ParseForSyntaxTreeOnly; + } + public: InFlightDiagnostic diagnose(SourceLoc Loc, Diagnostic Diag) { if (Diags.isDiagnosticPointsToFirstBadToken(Diag.getID()) && @@ -996,7 +1027,8 @@ class Parser { /// Parse the optional modifiers before a declaration. bool parseDeclModifierList(DeclAttributes &Attributes, SourceLoc &StaticLoc, - StaticSpellingKind &StaticSpelling); + StaticSpellingKind &StaticSpelling, + bool isFromClangAttribute = false); /// Parse an availability attribute of the form /// @available(*, introduced: 1.0, deprecated: 3.1). @@ -1090,31 +1122,35 @@ class Parser { bool parseVersionTuple(llvm::VersionTuple &Version, SourceRange &Range, const Diagnostic &D); - bool parseTypeAttributeList(ParamDecl::Specifier &Specifier, - SourceLoc &SpecifierLoc, - TypeAttributes &Attributes) { + ParserStatus parseTypeAttributeList(ParamDecl::Specifier &Specifier, + SourceLoc &SpecifierLoc, + SourceLoc &IsolatedLoc, + TypeAttributes &Attributes) { if (Tok.isAny(tok::at_sign, tok::kw_inout) || (Tok.is(tok::identifier) && (Tok.getRawText().equals("__shared") || - Tok.getRawText().equals("__owned")))) - return parseTypeAttributeListPresent(Specifier, SpecifierLoc, Attributes); - return false; + Tok.getRawText().equals("__owned") || + Tok.isContextualKeyword("isolated")))) + return parseTypeAttributeListPresent( + Specifier, SpecifierLoc, IsolatedLoc, Attributes); + return makeParserSuccess(); } - bool parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, - SourceLoc &SpecifierLoc, - TypeAttributes &Attributes); + + ParserStatus parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, + SourceLoc &SpecifierLoc, + SourceLoc &IsolatedLoc, + TypeAttributes &Attributes); bool parseConventionAttributeInternal(bool justChecking, TypeAttributes::Convention &convention); - bool parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, - PatternBindingInitializer *&initContext, - bool justChecking = false); - - + ParserStatus parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, + PatternBindingInitializer *&initContext, + bool justChecking = false); + ParserResult parseDeclImport(ParseDeclOptions Flags, DeclAttributes &Attributes); - ParserStatus parseInheritance(SmallVectorImpl &Inherited, + ParserStatus parseInheritance(SmallVectorImpl &Inherited, bool allowClassRequirement, bool allowAnyObject); ParserStatus parseDeclItem(bool &PreviousHadSemi, @@ -1205,15 +1241,32 @@ class Parser { //===--------------------------------------------------------------------===// // Type Parsing - + + enum class ParseTypeReason { + /// Any type parsing context. + Unspecified, + + /// Whether the type is for a closure attribute. + CustomAttribute, + }; + ParserResult parseType(); - ParserResult parseType(Diag<> MessageID, - bool IsSILFuncDecl = false); + ParserResult parseType( + Diag<> MessageID, + ParseTypeReason reason = ParseTypeReason::Unspecified); + + /// Parse a type optionally prefixed by a list of named opaque parameters. If + /// no params present, return 'type'. Otherwise, return 'type-named-opaque'. + /// + /// type-named-opaque: + /// generic-params type + ParserResult parseTypeWithOpaqueParams(Diag<> MessageID); ParserResult - parseTypeSimpleOrComposition(Diag<> MessageID); + parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason); - ParserResult parseTypeSimple(Diag<> MessageID); + ParserResult parseTypeSimple( + Diag<> MessageID, ParseTypeReason reason); /// Parse layout constraint. LayoutConstraint parseLayoutConstraint(Identifier LayoutConstraintID); @@ -1241,7 +1294,7 @@ class Parser { const TypeAttributes &attrs); ParserResult parseTypeTupleBody(); - ParserResult parseTypeArray(TypeRepr *Base); + ParserResult parseTypeArray(ParserResult Base); /// Parse a collection type. /// type-simple: @@ -1249,9 +1302,10 @@ class Parser { /// '[' type ':' type ']' ParserResult parseTypeCollection(); - ParserResult parseTypeOptional(TypeRepr *Base); + ParserResult parseTypeOptional(ParserResult Base); - ParserResult parseTypeImplicitlyUnwrappedOptional(TypeRepr *Base); + ParserResult + parseTypeImplicitlyUnwrappedOptional(ParserResult Base); bool isOptionalToken(const Token &T) const; SourceLoc consumeOptionalToken(); @@ -1261,7 +1315,8 @@ class Parser { TypeRepr *applyAttributeToType(TypeRepr *Ty, const TypeAttributes &Attr, ParamDecl::Specifier Specifier, - SourceLoc SpecifierLoc); + SourceLoc SpecifierLoc, + SourceLoc IsolatedLoc); //===--------------------------------------------------------------------===// // Pattern Parsing @@ -1320,6 +1375,9 @@ class Parser { /// The second name, the presence of which is indicated by \c SecondNameLoc. Identifier SecondName; + /// The location of the 'isolated' keyword, if present. + SourceLoc IsolatedLoc; + /// The type following the ':'. TypeRepr *Type = nullptr; @@ -1354,6 +1412,9 @@ class Parser { EnumElement, }; + /// Whether we are at the start of a parameter name when parsing a parameter. + bool startsParameterName(bool isClosure); + /// Parse a parameter-clause. /// /// \verbatim @@ -1731,11 +1792,21 @@ class Parser { //===--------------------------------------------------------------------===// // Availability Specification Parsing + /// The source of an availability spec list. + enum class AvailabilitySpecSource: uint8_t { + /// A spec from '@available(, ...)' or '#available(, ...)'. + Available, + /// A spec from '#unavailable(, ...)'. + Unavailable, + /// A spec from a '-define-availability "Name:, ..."' frontend arg. + Macro, + }; + /// Parse a comma-separated list of availability specifications. Try to - /// expand availability macros when /p ParsingMacroDefinition is false. + /// expand availability macros when /p Source is not a command line macro. ParserStatus parseAvailabilitySpecList(SmallVectorImpl &Specs, - bool ParsingMacroDefinition = false); + AvailabilitySpecSource Source); /// Does the current matches an argument macro name? Parsing compiler /// arguments as required without consuming tokens from the source file @@ -1798,6 +1869,8 @@ struct ParsedDeclName { /// Whether this is a setter for the named property. bool IsSetter = false; + bool IsSubscript = false; + /// For a declaration name that makes the declaration into an /// instance member, the index of the "Self" parameter. Optional SelfIndex; @@ -1863,9 +1936,6 @@ DeclNameRef formDeclNameRef(ASTContext &ctx, bool isInitializer, bool isSubscript = false); -/// Parse a stringified Swift declaration name, e.g. "init(frame:)". -DeclName parseDeclName(ASTContext &ctx, StringRef name); - /// Whether a given token can be the start of a decl. bool isKeywordPossibleDeclStart(const Token &Tok); diff --git a/include/swift/PrintAsObjC/PrintAsObjC.h b/include/swift/PrintAsObjC/PrintAsObjC.h index 2788a6c8ac723..dab65f9f8b36e 100644 --- a/include/swift/PrintAsObjC/PrintAsObjC.h +++ b/include/swift/PrintAsObjC/PrintAsObjC.h @@ -25,8 +25,7 @@ namespace swift { /// header. /// /// Returns true on error. - bool printAsObjC(raw_ostream &out, ModuleDecl *M, StringRef bridgingHeader, - AccessLevel minRequiredAccess); + bool printAsObjC(raw_ostream &out, ModuleDecl *M, StringRef bridgingHeader); } #endif diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index 275b877fc2769..6f8e331d980a2 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -23,6 +23,7 @@ #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Memory.h" +#include "llvm/ADT/STLExtras.h" #include "swift/ABI/Enum.h" #include "swift/ABI/ObjectFile.h" @@ -92,7 +93,7 @@ class ReflectionContext using super = remote::MetadataReader; using super::readMetadata; using super::readObjCClassName; - + using super::readResolvedPointerValue; std::unordered_map Cache; /// All buffers we need to keep around long term. This will automatically free them @@ -644,6 +645,47 @@ class ReflectionContext return false; } + /// Adds an image using the FindSection closure to find the swift metadata + /// sections. \param FindSection + /// Closure that finds sections by name. ReflectionContext is in charge + /// of freeing the memory buffer in the RemoteRef return value. + /// process. + /// \return + /// \b True if any of the reflection sections were registered, + /// \b false otherwise. + bool addImage(llvm::function_ref< + std::pair, uint64_t>(ReflectionSectionKind)> + FindSection) { + auto Sections = { + ReflectionSectionKind::fieldmd, ReflectionSectionKind::assocty, + ReflectionSectionKind::builtin, ReflectionSectionKind::capture, + ReflectionSectionKind::typeref, ReflectionSectionKind::reflstr}; + + llvm::SmallVector, uint64_t>, 6> Pairs; + for (auto Section : Sections) { + Pairs.push_back(FindSection(Section)); + auto LatestRemoteRef = std::get>(Pairs.back()); + if (LatestRemoteRef) { + MemoryReader::ReadBytesResult Buffer( + LatestRemoteRef.getLocalBuffer(), + [](const void *Ptr) { free(const_cast(Ptr)); }); + + savedBuffers.push_back(std::move(Buffer)); + } + } + + // If we didn't find any sections, return. + if (llvm::all_of(Pairs, [](const auto &Pair) { return !Pair.first; })) + return false; + + ReflectionInfo Info = { + {Pairs[0].first, Pairs[0].second}, {Pairs[1].first, Pairs[1].second}, + {Pairs[2].first, Pairs[2].second}, {Pairs[3].first, Pairs[3].second}, + {Pairs[4].first, Pairs[4].second}, {Pairs[5].first, Pairs[5].second}}; + this->addReflectionInfo(Info); + return true; + } + void addReflectionInfo(ReflectionInfo I) { getBuilder().addReflectionInfo(I); } @@ -687,7 +729,16 @@ class ReflectionContext return false; } - + + /// Returns the address of the nominal type descriptor given a metadata + /// address. + StoredPointer nominalTypeDescriptorFromMetadata(StoredPointer MetadataAddress) { + auto Metadata = readMetadata(MetadataAddress); + if (!Metadata) + return 0; + return super::readAddressOfNominalTypeDescriptor(Metadata, true); + } + /// Return a description of the layout of a class instance with the given /// metadata as its isa pointer. const TypeInfo * @@ -789,6 +840,52 @@ class ReflectionContext } } + llvm::Optional> + getDynamicTypeAndAddressClassExistential(RemoteAddress ExistentialAddress) { + auto PointerValue = + readResolvedPointerValue(ExistentialAddress.getAddressData()); + if (!PointerValue) + return {}; + auto Result = readMetadataFromInstance(*PointerValue); + if (!Result) + return {}; + auto TypeResult = readTypeFromMetadata(Result.getValue()); + if (!TypeResult) + return {}; + return {{std::move(TypeResult), RemoteAddress(*PointerValue)}}; + } + + llvm::Optional> + getDynamicTypeAndAddressErrorExistential(RemoteAddress ExistentialAddress, + bool *IsBridgedError = nullptr) { + auto Result = readMetadataAndValueErrorExistential(ExistentialAddress); + if (!Result) + return {}; + + auto TypeResult = + readTypeFromMetadata(Result->MetadataAddress.getAddressData()); + if (!TypeResult) + return {}; + + if (IsBridgedError) + *IsBridgedError = Result->IsBridgedError; + + return {{TypeResult, Result->PayloadAddress}}; + } + + llvm::Optional> + getDynamicTypeAndAddressOpaqueExistential(RemoteAddress ExistentialAddress) { + auto Result = readMetadataAndValueOpaqueExistential(ExistentialAddress); + if (!Result) + return {}; + + auto TypeResult = + readTypeFromMetadata(Result->MetadataAddress.getAddressData()); + if (!TypeResult) + return {}; + return {{std::move(TypeResult), Result->PayloadAddress}}; + } + bool projectExistential(RemoteAddress ExistentialAddress, const TypeRef *ExistentialTR, const TypeRef **OutInstanceTR, @@ -850,6 +947,75 @@ class ReflectionContext return false; } } + /// A version of `projectExistential` tailored for LLDB. + /// This version dereferences the resulting TypeRef if it wraps + /// a class type, it also dereferences the input `ExistentialAddress` before + /// attempting to find its dynamic type and address when dealing with error + /// existentials. + llvm::Optional> + projectExistentialAndUnwrapClass(RemoteAddress ExistentialAddress, + const TypeRef &ExistentialTR) { + auto IsClass = [](const TypeRef *TypeResult) { + // When the existential wraps a class type, LLDB expects that the + // address returned is the class instance itself and not the address + // of the reference. + bool IsClass = TypeResult->getKind() == TypeRefKind::ForeignClass || + TypeResult->getKind() == TypeRefKind::ObjCClass; + if (auto *nominal = llvm::dyn_cast(TypeResult)) + IsClass = nominal->isClass(); + else if (auto *boundGeneric = + llvm::dyn_cast(TypeResult)) + IsClass = boundGeneric->isClass(); + return IsClass; + }; + + auto DereferenceAndSet = [&](RemoteAddress &Address) { + auto PointerValue = readResolvedPointerValue(Address.getAddressData()); + if (!PointerValue) + return false; + Address = RemoteAddress(*PointerValue); + return true; + }; + + auto ExistentialRecordTI = getRecordTypeInfo(&ExistentialTR, nullptr); + if (!ExistentialRecordTI) + return {}; + + switch (ExistentialRecordTI->getRecordKind()) { + case RecordKind::ClassExistential: + return getDynamicTypeAndAddressClassExistential(ExistentialAddress); + case RecordKind::ErrorExistential: { + // LLDB stores the address of the error pointer. + if (!DereferenceAndSet(ExistentialAddress)) + return {}; + + bool IsBridgedError = false; + auto Pair = getDynamicTypeAndAddressErrorExistential(ExistentialAddress, + &IsBridgedError); + if (!Pair) + return {}; + + if (!IsBridgedError && IsClass(std::get(*Pair))) + if (!DereferenceAndSet(std::get(*Pair))) + return {}; + + return Pair; + } + case RecordKind::OpaqueExistential: { + auto Pair = getDynamicTypeAndAddressOpaqueExistential(ExistentialAddress); + if (!Pair) + return {}; + + if (IsClass(std::get(*Pair))) + if (!DereferenceAndSet(std::get(*Pair))) + return {}; + + return Pair; + } + default: + return {}; + } + } /// Projects the value of an enum. /// @@ -889,6 +1055,12 @@ class ReflectionContext } } + const RecordTypeInfo *getRecordTypeInfo(const TypeRef *TR, + remote::TypeInfoProvider *ExternalTypeInfo) { + auto *TypeInfo = getTypeInfo(TR, ExternalTypeInfo); + return dyn_cast_or_null(TypeInfo); + } + /// Iterate the protocol conformance cache tree rooted at NodePtr, calling /// Call with the type and protocol in each node. void iterateConformanceTree(StoredPointer NodePtr, @@ -1189,9 +1361,7 @@ class ReflectionContext if (!AsyncTaskObj) return std::string("failure reading async task"); - auto *Allocator = reinterpret_cast( - &AsyncTaskObj->AllocatorPrivate); - StoredPointer SlabPtr = Allocator->FirstSlab; + StoredPointer SlabPtr = AsyncTaskObj->PrivateStorage.Allocator.FirstSlab; while (SlabPtr) { auto SlabBytes = getReader().readBytes( RemoteAddress(SlabPtr), sizeof(typename StackAllocator::Slab)); diff --git a/include/swift/Reflection/RuntimeInternals.h b/include/swift/Reflection/RuntimeInternals.h index 197c4dbca9acf..730e9fd8cf470 100644 --- a/include/swift/Reflection/RuntimeInternals.h +++ b/include/swift/Reflection/RuntimeInternals.h @@ -21,6 +21,8 @@ #ifndef SWIFT_REFLECTION_RUNTIME_INTERNALS_H #define SWIFT_REFLECTION_RUNTIME_INTERNALS_H +#include + namespace swift { namespace reflection { @@ -68,7 +70,12 @@ struct HeapObject { template struct Job { - typename Runtime::StoredPointer Opaque[4]; + HeapObject HeapObject; + typename Runtime::StoredPointer SchedulerPrivate[2]; + uint32_t Flags; + uint32_t Id; + typename Runtime::StoredPointer Reserved[2]; + typename Runtime::StoredPointer RunJob; }; template @@ -86,12 +93,25 @@ struct StackAllocator { }; template -struct AsyncTask { - HeapObject HeapObject; - Job Job; - typename Runtime::StoredPointer ResumeContext; - typename Runtime::StoredSize Status; - typename Runtime::StoredPointer AllocatorPrivate[4]; +struct ActiveTaskStatus { + typename Runtime::StoredPointer Record; + typename Runtime::StoredSize Flags; +}; + +template +struct AsyncTaskPrivateStorage { + ActiveTaskStatus Status; + StackAllocator Allocator; + typename Runtime::StoredPointer Local; +}; + +template +struct AsyncTask: Job { + // On 64-bit, there's a Reserved64 after ResumeContext. + typename Runtime::StoredPointer ResumeContextAndReserved[ + sizeof(typename Runtime::StoredPointer) == 8 ? 2 : 1]; + + AsyncTaskPrivateStorage PrivateStorage; }; } // end namespace reflection diff --git a/include/swift/Reflection/TypeRef.h b/include/swift/Reflection/TypeRef.h index ed7132eddb69e..10b1a938fd863 100644 --- a/include/swift/Reflection/TypeRef.h +++ b/include/swift/Reflection/TypeRef.h @@ -462,10 +462,12 @@ class FunctionTypeRef final : public TypeRef { const TypeRef *Result; FunctionTypeFlags Flags; FunctionMetadataDifferentiabilityKind DifferentiabilityKind; + const TypeRef *GlobalActor; static TypeRefID Profile(const std::vector &Parameters, const TypeRef *Result, FunctionTypeFlags Flags, - FunctionMetadataDifferentiabilityKind DiffKind) { + FunctionMetadataDifferentiabilityKind DiffKind, + const TypeRef *GlobalActor) { TypeRefID ID; for (const auto &Param : Parameters) { ID.addString(Param.getLabel().str()); @@ -475,21 +477,27 @@ class FunctionTypeRef final : public TypeRef { ID.addPointer(Result); ID.addInteger(static_cast(Flags.getIntValue())); ID.addInteger(static_cast(DiffKind.getIntValue())); + ID.addPointer(GlobalActor); + return ID; } public: FunctionTypeRef(std::vector Params, const TypeRef *Result, FunctionTypeFlags Flags, - FunctionMetadataDifferentiabilityKind DiffKind) + FunctionMetadataDifferentiabilityKind DiffKind, + const TypeRef *GlobalActor) : TypeRef(TypeRefKind::Function), Parameters(Params), Result(Result), - Flags(Flags), DifferentiabilityKind(DiffKind) {} + Flags(Flags), DifferentiabilityKind(DiffKind), + GlobalActor(GlobalActor) {} template static const FunctionTypeRef *create( Allocator &A, std::vector Params, const TypeRef *Result, - FunctionTypeFlags Flags, FunctionMetadataDifferentiabilityKind DiffKind) { - FIND_OR_CREATE_TYPEREF(A, FunctionTypeRef, Params, Result, Flags, DiffKind); + FunctionTypeFlags Flags, FunctionMetadataDifferentiabilityKind DiffKind, + const TypeRef *GlobalActor) { + FIND_OR_CREATE_TYPEREF( + A, FunctionTypeRef, Params, Result, Flags, DiffKind, GlobalActor); } const std::vector &getParameters() const { return Parameters; }; @@ -506,6 +514,10 @@ class FunctionTypeRef final : public TypeRef { return DifferentiabilityKind; } + const TypeRef *getGlobalActor() const { + return GlobalActor; + } + static bool classof(const TypeRef *TR) { return TR->getKind() == TypeRefKind::Function; } diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index 05e64bfa509d7..9c2413e8b493c 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -24,7 +24,6 @@ #include "swift/Reflection/TypeLowering.h" #include "swift/Reflection/TypeRef.h" #include "llvm/ADT/Optional.h" - #include #include @@ -415,8 +414,10 @@ class TypeRefBuilder { const FunctionTypeRef *createFunctionType( llvm::ArrayRef> params, const TypeRef *result, FunctionTypeFlags flags, - FunctionMetadataDifferentiabilityKind diffKind) { - return FunctionTypeRef::create(*this, params, result, flags, diffKind); + FunctionMetadataDifferentiabilityKind diffKind, + const TypeRef *globalActor) { + return FunctionTypeRef::create( + *this, params, result, flags, diffKind, globalActor); } const FunctionTypeRef *createImplFunctionType( @@ -472,7 +473,8 @@ class TypeRefBuilder { } auto result = createTupleType({}, ""); - return FunctionTypeRef::create(*this, {}, result, funcFlags, diffKind); + return FunctionTypeRef::create( + *this, {}, result, funcFlags, diffKind, nullptr); } const ProtocolCompositionTypeRef * @@ -630,7 +632,7 @@ class TypeRefBuilder { private: std::vector ReflectionInfos; - std::string normalizeReflectionName(RemoteRef name); + llvm::Optional normalizeReflectionName(RemoteRef name); bool reflectionNameMatches(RemoteRef reflectionName, StringRef searchName); @@ -654,7 +656,7 @@ class TypeRefBuilder { // TypeRefBuilder struct, to isolate its template-ness from the rest of // TypeRefBuilder. unsigned PointerSize; - std::function)> + std::function, bool)> TypeRefDemangler; std::function OpaqueUnderlyingTypeReader; @@ -665,10 +667,10 @@ class TypeRefBuilder { : TC(*this), PointerSize(sizeof(typename Runtime::StoredPointer)), TypeRefDemangler( - [this, &reader](RemoteRef string) -> Demangle::Node * { + [this, &reader](RemoteRef string, bool useOpaqueTypeSymbolicReferences) -> Demangle::Node * { return reader.demangle(string, remote::MangledNameKind::Type, - Dem, /*useOpaqueTypeSymbolicReferences*/ true); + Dem, useOpaqueTypeSymbolicReferences); }), OpaqueUnderlyingTypeReader( [&reader](uint64_t descriptorAddr, unsigned ordinal) -> const TypeRef* { @@ -677,8 +679,9 @@ class TypeRefBuilder { }) {} - Demangle::Node *demangleTypeRef(RemoteRef string) { - return TypeRefDemangler(string); + Demangle::Node *demangleTypeRef(RemoteRef string, + bool useOpaqueTypeSymbolicReferences = true) { + return TypeRefDemangler(string, useOpaqueTypeSymbolicReferences); } TypeConverter &getTypeConverter() { return TC; } diff --git a/include/swift/Remote/MemoryReader.h b/include/swift/Remote/MemoryReader.h index 6dfd4c755a487..579e4bea66407 100644 --- a/include/swift/Remote/MemoryReader.h +++ b/include/swift/Remote/MemoryReader.h @@ -100,6 +100,8 @@ class MemoryReader { virtual ReadBytesResult readBytes(RemoteAddress address, uint64_t size) { auto *Buf = malloc(size); + if (!Buf) + return ReadBytesResult{}; ReadBytesResult Result(Buf, [](const void *ptr) { free(const_cast(ptr)); }); diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index de9d02ad372f5..091098611f518 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -170,21 +170,24 @@ class MetadataReader { using StoredSize = typename Runtime::StoredSize; private: + /// The maximum number of bytes to read when reading metadata. Anything larger + /// will automatically return failure. This prevents us from reading absurd + /// amounts of data when we encounter corrupt values for sizes/counts. + static const uint64_t MaxMetadataSize = 1048576; // 1MB + /// A cache of built types, keyed by the address of the type. std::unordered_map TypeCache; - using MetadataRef = RemoteRef>; - using OwnedMetadataRef = - std::unique_ptr, delete_with_free>; + using MetadataRef = RemoteRef>; + using OwnedMetadataRef = MemoryReader::ReadBytesResult; /// A cache of read type metadata, keyed by the address of the metadata. std::unordered_map MetadataCache; - using ContextDescriptorRef = RemoteRef>; - using OwnedContextDescriptorRef = - std::unique_ptr, - delete_with_free>; + using ContextDescriptorRef = + RemoteRef>; + using OwnedContextDescriptorRef = MemoryReader::ReadBytesResult; /// A reference to a context descriptor that may be in an unloaded image. class ParentContextDescriptorRef { @@ -811,6 +814,13 @@ class MetadataReader { .withEscaping(Function->isEscaping()) .withDifferentiable(Function->isDifferentiable()); + BuiltType globalActor = BuiltType(); + if (Function->hasGlobalActor()) { + globalActor = readTypeFromMetadata(Function->getGlobalActor()); + if (globalActor) + flags = flags.withGlobalActor(true); + } + FunctionMetadataDifferentiabilityKind diffKind; switch (Function->getDifferentiabilityKind().Value) { #define CASE(X) \ @@ -827,7 +837,7 @@ class MetadataReader { } auto BuiltFunction = Builder.createFunctionType( - Parameters, Result, flags, diffKind); + Parameters, Result, flags, diffKind, globalActor); TypeCache[MetadataAddress] = BuiltFunction; return BuiltFunction; } @@ -977,7 +987,9 @@ class MetadataReader { auto cached = ContextDescriptorCache.find(address); if (cached != ContextDescriptorCache.end()) - return ContextDescriptorRef(address, cached->second.get()); + return ContextDescriptorRef( + address, reinterpret_cast *>( + cached->second.get())); // Read the flags to figure out how much space we should read. ContextDescriptorFlags flags; @@ -986,9 +998,9 @@ class MetadataReader { return nullptr; TypeContextDescriptorFlags typeFlags(flags.getKindSpecificFlags()); - unsigned baseSize = 0; - unsigned genericHeaderSize = sizeof(GenericContextDescriptorHeader); - unsigned metadataInitSize = 0; + uint64_t baseSize = 0; + uint64_t genericHeaderSize = sizeof(GenericContextDescriptorHeader); + uint64_t metadataInitSize = 0; bool hasVTable = false; auto readMetadataInitSize = [&]() -> unsigned { @@ -1052,7 +1064,7 @@ class MetadataReader { // Determine the full size of the descriptor. This is reimplementing a fair // bit of TrailingObjects but for out-of-process; maybe there's a way to // factor the layout stuff out... - unsigned genericsSize = 0; + uint64_t genericsSize = 0; if (flags.isGeneric()) { GenericContextDescriptorHeader header; auto headerAddr = address @@ -1070,7 +1082,7 @@ class MetadataReader { * sizeof(TargetGenericRequirementDescriptor); } - unsigned vtableSize = 0; + uint64_t vtableSize = 0; if (hasVTable) { TargetVTableDescriptorHeader header; auto headerAddr = address @@ -1085,22 +1097,20 @@ class MetadataReader { vtableSize = sizeof(header) + header.VTableSize * sizeof(TargetMethodDescriptor); } - - unsigned size = baseSize + genericsSize + metadataInitSize + vtableSize; - auto buffer = (uint8_t *)malloc(size); - if (buffer == nullptr) { + + uint64_t size = baseSize + genericsSize + metadataInitSize + vtableSize; + if (size > MaxMetadataSize) return nullptr; - } - if (!Reader->readBytes(RemoteAddress(address), buffer, size)) { - free(buffer); + auto readResult = Reader->readBytes(RemoteAddress(address), size); + if (!readResult) return nullptr; - } - auto descriptor - = reinterpret_cast *>(buffer); + auto descriptor = + reinterpret_cast *>( + readResult.get()); ContextDescriptorCache.insert( - std::make_pair(address, OwnedContextDescriptorRef(descriptor))); + std::make_pair(address, std::move(readResult))); return ContextDescriptorRef(address, descriptor); } @@ -1626,7 +1636,9 @@ class MetadataReader { MetadataRef readMetadata(StoredPointer address) { auto cached = MetadataCache.find(address); if (cached != MetadataCache.end()) - return MetadataRef(address, cached->second.get()); + return MetadataRef(address, + reinterpret_cast *>( + cached->second.get())); StoredPointer KindValue = 0; if (!Reader->readInteger(RemoteAddress(address), &KindValue)) @@ -1800,15 +1812,15 @@ class MetadataReader { } MetadataRef _readMetadata(StoredPointer address, size_t sizeAfter) { - auto size = sizeAfter; - uint8_t *buffer = (uint8_t *) malloc(size); - if (!Reader->readBytes(RemoteAddress(address), buffer, size)) { - free(buffer); + if (sizeAfter > MaxMetadataSize) + return nullptr; + auto readResult = Reader->readBytes(RemoteAddress(address), sizeAfter); + if (!readResult) return nullptr; - } - auto metadata = reinterpret_cast*>(buffer); - MetadataCache.insert(std::make_pair(address, OwnedMetadataRef(metadata))); + auto metadata = + reinterpret_cast *>(readResult.get()); + MetadataCache.insert(std::make_pair(address, std::move(readResult))); return MetadataRef(address, metadata); } @@ -2477,6 +2489,9 @@ class MetadataReader { // Use private declaration names for anonymous context references. if (parentDemangling->getKind() == Node::Kind::AnonymousContext && nameNode->getKind() == Node::Kind::Identifier) { + if (parentDemangling->getNumChildren() < 2) + return nullptr; + auto privateDeclName = dem.createNode(Node::Kind::PrivateDeclName); privateDeclName->addChild(parentDemangling->getChild(0), dem); @@ -2519,6 +2534,8 @@ class MetadataReader { std::string readObjCProtocolName(StoredPointer Address) { auto Size = sizeof(TargetObjCProtocolPrefix); auto Buffer = (uint8_t *)malloc(Size); + if (!Buffer) + return std::string(); SWIFT_DEFER { free(Buffer); }; diff --git a/include/swift/RemoteAST/RemoteAST.h b/include/swift/RemoteAST/RemoteAST.h index ba3d83200c296..5f0823392a53b 100644 --- a/include/swift/RemoteAST/RemoteAST.h +++ b/include/swift/RemoteAST/RemoteAST.h @@ -184,11 +184,11 @@ class RemoteASTContext { /// resolve it to a specific type in the local AST. /// /// \param skipArtificial If true, the address may be an artificial type - /// wrapper that should be ignored. For example, it could be a + /// wrapper that should be ignored. For example, it could be a /// dynamic subclass created by (e.g.) CoreData or KVO; if so, and this /// flag is set, this method will implicitly ignore the subclass /// and instead attempt to resolve a type for the first non-artificial - /// superclass. + /// superclass. See [NOTE: Dynamic-subclass-KVO]. Result getTypeForRemoteTypeMetadata(remote::RemoteAddress address, bool skipArtificial = false); diff --git a/include/swift/Runtime/Atomic.h b/include/swift/Runtime/Atomic.h index 67d0bca1018b7..7e2d13aa92a71 100644 --- a/include/swift/Runtime/Atomic.h +++ b/include/swift/Runtime/Atomic.h @@ -53,10 +53,15 @@ class alignas(Size) atomic_impl { constexpr atomic_impl(Value value) : value(value) {} /// Force clients to always pass an order. - Value load(std::memory_order order) { + Value load(std::memory_order order) const { return value.load(order); } + /// Force clients to always pass an order. + void store(Value newValue, std::memory_order order) { + return value.store(newValue, order); + } + /// Force clients to always pass an order. bool compare_exchange_weak(Value &oldValue, Value newValue, std::memory_order successOrder, @@ -75,14 +80,14 @@ class alignas(Size) atomic_impl { /// AMD processors that lack cmpxchg16b, so we just use the intrinsic. template class alignas(2 * sizeof(void*)) atomic_impl { - volatile Value atomicValue; + mutable volatile Value atomicValue; public: constexpr atomic_impl(Value initialValue) : atomicValue(initialValue) {} atomic_impl(const atomic_impl &) = delete; atomic_impl &operator=(const atomic_impl &) = delete; - Value load(std::memory_order order) { + Value load(std::memory_order order) const { assert(order == std::memory_order_relaxed || order == std::memory_order_acquire || order == std::memory_order_consume); @@ -107,6 +112,17 @@ class alignas(2 * sizeof(void*)) atomic_impl { return reinterpret_cast(resultArray); } + void store(Value newValue, std::memory_order order) { + assert(order == std::memory_order_relaxed || + order == std::memory_order_release); + Value oldValue = load(std::memory_order_relaxed); + while (!compare_exchange_weak(oldValue, newValue, + /*success*/ order, + /*failure*/ std::memory_order_relaxed)) { + // try again + } + } + bool compare_exchange_weak(Value &oldValue, Value newValue, std::memory_order successOrder, std::memory_order failureOrder) { diff --git a/include/swift/Runtime/CMakeLists.txt b/include/swift/Runtime/CMakeLists.txt index 7dea0d2c2a39f..fa237caabf5a0 100644 --- a/include/swift/Runtime/CMakeLists.txt +++ b/include/swift/Runtime/CMakeLists.txt @@ -11,3 +11,6 @@ endif() configure_file(CMakeConfig.h.in ${CMAKE_CURRENT_BINARY_DIR}/CMakeConfig.h ESCAPE_QUOTES @ONLY) +swift_install_in_component(FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeConfig.h + DESTINATION "include/swift/Runtime" + COMPONENT testsuite-tools) diff --git a/include/swift/Runtime/Casting.h b/include/swift/Runtime/Casting.h index 61c3d2474b9ba..0b34dab6fcfab 100644 --- a/include/swift/Runtime/Casting.h +++ b/include/swift/Runtime/Casting.h @@ -224,7 +224,7 @@ swift_getDynamicType(OpaqueValue *value, const Metadata *self, /// Fetch the type metadata associated with the formal dynamic /// type of the given (possibly Objective-C) object. The formal /// dynamic type ignores dynamic subclasses such as those introduced -/// by KVO. +/// by KVO. See [NOTE: Dynamic-subclass-KVO] /// /// The object pointer may be a tagged pointer, but cannot be null. SWIFT_RUNTIME_EXPORT diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index e406730cf2b6a..7b9a229b22cf0 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -17,6 +17,7 @@ #ifndef SWIFT_RUNTIME_CONCURRENCY_H #define SWIFT_RUNTIME_CONCURRENCY_H +#include "swift/ABI/Task.h" #include "swift/ABI/TaskGroup.h" #include "swift/ABI/AsyncLet.h" #include "swift/ABI/TaskStatus.h" @@ -26,6 +27,7 @@ namespace swift { class DefaultActor; +class TaskOptionRecord; struct SwiftError; @@ -34,53 +36,26 @@ struct AsyncTaskAndContext { AsyncContext *InitialContext; }; -/// Create a task object with no future which will run the given -/// function. +/// Create a task object. SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -AsyncTaskAndContext swift_task_create_f(JobFlags flags, - ThinNullaryAsyncSignature::FunctionType *function, - size_t initialContextSize); +AsyncTaskAndContext swift_task_create( + size_t taskCreateFlags, + TaskOptionRecord *options, + const Metadata *futureResultType, + void *closureEntry, HeapObject *closureContext); /// Caution: not all future-initializing functions actually throw, so /// this signature may be incorrect. using FutureAsyncSignature = AsyncSignature; -/// Create a task object with a future which will run the given -/// closure. -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -AsyncTaskAndContext swift_task_create_future( - JobFlags flags, - const Metadata *futureResultType, - void *closureEntryPoint, - HeapObject * /* +1 */ closureContext); - -/// Create a task object with a future which will run the given -/// function. -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -AsyncTaskAndContext swift_task_create_future_f( - JobFlags flags, - const Metadata *futureResultType, - FutureAsyncSignature::FunctionType *function, - size_t initialContextSize); - -/// Create a task object with a future which will run the given -/// closure, and offer its result to the task group -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -AsyncTaskAndContext swift_task_create_group_future( - JobFlags flags, TaskGroup *group, - const Metadata *futureResultType, - void *closureEntryPoint, - HeapObject * /* +1 */ closureContext); - -/// Create a task object with a future which will run the given -/// function, and offer its result to the task group +/// Create a task object. SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -AsyncTaskAndContext swift_task_create_group_future_f( - JobFlags flags, - TaskGroup *group, +AsyncTaskAndContext swift_task_create_common( + size_t taskCreateFlags, + TaskOptionRecord *options, const Metadata *futureResultType, - FutureAsyncSignature::FunctionType *function, + FutureAsyncSignature::FunctionType *function, void *closureContext, size_t initialContextSize); /// Allocate memory in a task. @@ -124,12 +99,6 @@ swift_task_escalate(AsyncTask *task, JobPriority newPriority); // TODO: "async let wait" and "async let destroy" would be expressed // similar to like TaskFutureWait; -/// This matches the ABI of a closure `(Builtin.NativeObject) async -> T` -using TaskFutureWaitSignature = - SWIFT_CC(swiftasync) - void(OpaqueValue *, - SWIFT_ASYNC_CONTEXT AsyncContext *, AsyncTask *, Metadata *); - /// Wait for a non-throwing future task to complete. /// /// This can be called from any thread. Its Swift signature is @@ -140,12 +109,9 @@ using TaskFutureWaitSignature = /// \endcode SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) void swift_task_future_wait(OpaqueValue *, - SWIFT_ASYNC_CONTEXT AsyncContext *, AsyncTask *, Metadata *); - -using TaskFutureWaitThrowingSignature = - SWIFT_CC(swiftasync) - void(OpaqueValue *, - SWIFT_ASYNC_CONTEXT AsyncContext *, AsyncTask *, Metadata *); + SWIFT_ASYNC_CONTEXT AsyncContext *, AsyncTask *, + TaskContinuationFunction *, + AsyncContext *); /// Wait for a potentially-throwing future task to complete. /// @@ -156,15 +122,12 @@ using TaskFutureWaitThrowingSignature = /// async throws -> Success /// \endcode SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) -void swift_task_future_wait_throwing(OpaqueValue *, - SWIFT_ASYNC_CONTEXT AsyncContext *, - AsyncTask *, Metadata *); - -using TaskGroupFutureWaitThrowingSignature = -SWIFT_CC(swiftasync) - void(OpaqueValue *, - SWIFT_ASYNC_CONTEXT AsyncContext *, AsyncTask *, TaskGroup *, - const Metadata *successType); +void swift_task_future_wait_throwing( + OpaqueValue *, + SWIFT_ASYNC_CONTEXT AsyncContext *, + AsyncTask *, + ThrowingTaskFutureWaitContinuationFunction *, + AsyncContext *); /// Wait for a readyQueue of a Channel to become non empty. /// @@ -179,8 +142,9 @@ SWIFT_CC(swiftasync) SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) void swift_taskGroup_wait_next_throwing( - OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - TaskGroup *group, const Metadata *successType); + OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + TaskGroup *group, ThrowingTaskFutureWaitContinuationFunction *resumeFn, + AsyncContext *callContext); /// Initialize a `TaskGroup` in the passed `group` memory location. /// The caller is responsible for retaining and managing the group's lifecycle. @@ -191,7 +155,7 @@ void swift_taskGroup_wait_next_throwing( /// func swift_taskGroup_initialize(group: Builtin.RawPointer) /// \endcode SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -void swift_taskGroup_initialize(TaskGroup *group); +void swift_taskGroup_initialize(TaskGroup *group, const Metadata *T); /// Attach a child task to the parent task's task group record. /// @@ -276,20 +240,39 @@ bool swift_taskGroup_isCancelled(TaskGroup *group); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) bool swift_taskGroup_isEmpty(TaskGroup *group); +/// DEPRECATED. swift_asyncLet_begin is used instead. /// Its Swift signature is /// /// \code /// func swift_asyncLet_start( -/// _ alet: Builtin.RawPointer, +/// asyncLet: Builtin.RawPointer, +/// options: Builtin.RawPointer?, /// operation: __owned @Sendable () async throws -> T /// ) /// \endcode SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -void swift_asyncLet_start( - AsyncLet *alet, - const Metadata *futureResultType, - void *closureEntryPoint, - void *closureContext); +void swift_asyncLet_start(AsyncLet *alet, + TaskOptionRecord *options, + const Metadata *futureResultType, + void *closureEntryPoint, HeapObject *closureContext); + +/// Begin an async let child task. +/// Its Swift signature is +/// +/// \code +/// func swift_asyncLet_start( +/// asyncLet: Builtin.RawPointer, +/// options: Builtin.RawPointer?, +/// operation: __owned @Sendable () async throws -> T, +/// resultBuffer: Builtin.RawPointer +/// ) +/// \endcode +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_asyncLet_begin(AsyncLet *alet, + TaskOptionRecord *options, + const Metadata *futureResultType, + void *closureEntryPoint, HeapObject *closureContext, + void *resultBuffer); /// This matches the ABI of a closure `(Builtin.RawPointer) async -> T` using AsyncLetWaitSignature = @@ -297,6 +280,7 @@ using AsyncLetWaitSignature = void(OpaqueValue *, SWIFT_ASYNC_CONTEXT AsyncContext *, AsyncTask *, Metadata *); +/// DEPRECATED. swift_asyncLet_get is used instead. /// Wait for a non-throwing async-let to complete. /// /// This can be called from any thread. Its Swift signature is @@ -309,8 +293,10 @@ using AsyncLetWaitSignature = SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) void swift_asyncLet_wait(OpaqueValue *, SWIFT_ASYNC_CONTEXT AsyncContext *, - AsyncLet *, Metadata *); + AsyncLet *, TaskContinuationFunction *, + AsyncContext *); +/// DEPRECATED. swift_asyncLet_get_throwing is used instead. /// Wait for a potentially-throwing async-let to complete. /// /// This can be called from any thread. Its Swift signature is @@ -323,8 +309,11 @@ void swift_asyncLet_wait(OpaqueValue *, SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) void swift_asyncLet_wait_throwing(OpaqueValue *, SWIFT_ASYNC_CONTEXT AsyncContext *, - AsyncLet *, Metadata *); + AsyncLet *, + ThrowingTaskFutureWaitContinuationFunction *, + AsyncContext *); +/// DEPRECATED. swift_asyncLet_finish is used instead. /// Its Swift signature is /// /// \code @@ -333,6 +322,121 @@ void swift_asyncLet_wait_throwing(OpaqueValue *, SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_asyncLet_end(AsyncLet *alet); +/// Get the value of a non-throwing async-let, awaiting the result if necessary. +/// +/// This can be called from any thread. Its Swift signature is +/// +/// \code +/// func swift_asyncLet_get( +/// _ asyncLet: Builtin.RawPointer, +/// _ resultBuffer: Builtin.RawPointer +/// ) async +/// \endcode +/// +/// \c result points at the variable storage for the binding. It is +/// uninitialized until the first call to \c swift_asyncLet_get or +/// \c swift_asyncLet_get_throwing. That first call initializes the storage +/// with the result of the child task. Subsequent calls do nothing and leave +/// the value in place. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) +void swift_asyncLet_get(SWIFT_ASYNC_CONTEXT AsyncContext *, + AsyncLet *, + void *, + TaskContinuationFunction *, + AsyncContext *); + +/// Get the value of a throwing async-let, awaiting the result if necessary. +/// +/// This can be called from any thread. Its Swift signature is +/// +/// \code +/// func swift_asyncLet_get_throwing( +/// _ asyncLet: Builtin.RawPointer, +/// _ resultBuffer: Builtin.RawPointer +/// ) async throws +/// \endcode +/// +/// \c result points at the variable storage for the binding. It is +/// uninitialized until the first call to \c swift_asyncLet_get or +/// \c swift_asyncLet_get_throwing. That first call initializes the storage +/// with the result of the child task. Subsequent calls do nothing and leave +/// the value in place. A pointer to the storage inside the child task is +/// returned if the task completes successfully, otherwise the error from the +/// child task is thrown. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) +void swift_asyncLet_get_throwing(SWIFT_ASYNC_CONTEXT AsyncContext *, + AsyncLet *, + void *, + ThrowingTaskFutureWaitContinuationFunction *, + AsyncContext *); + +/// Exit the scope of an async-let binding. If the task is still running, it +/// is cancelled, and we await its completion; otherwise, we destroy the +/// value in the variable storage. +/// +/// Its Swift signature is +/// +/// \code +/// func swift_asyncLet_finish(_ asyncLet: Builtin.RawPointer, +/// _ resultBuffer: Builtin.RawPointer) async +/// \endcode +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) +void swift_asyncLet_finish(SWIFT_ASYNC_CONTEXT AsyncContext *, + AsyncLet *, + void *, + TaskContinuationFunction *, + AsyncContext *); + +/// Get the value of a non-throwing async-let, awaiting the result if necessary, +/// and then destroy the child task. The result buffer is left initialized after +/// returning. +/// +/// This can be called from any thread. Its Swift signature is +/// +/// \code +/// func swift_asyncLet_get( +/// _ asyncLet: Builtin.RawPointer, +/// _ resultBuffer: Builtin.RawPointer +/// ) async +/// \endcode +/// +/// \c result points at the variable storage for the binding. It is +/// uninitialized until the first call to \c swift_asyncLet_get or +/// \c swift_asyncLet_get_throwing. The child task will be invalidated after +/// this call, so the `async let` can not be gotten or finished afterward. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) +void swift_asyncLet_consume(SWIFT_ASYNC_CONTEXT AsyncContext *, + AsyncLet *, + void *, + TaskContinuationFunction *, + AsyncContext *); + +/// Get the value of a throwing async-let, awaiting the result if necessary, +/// and then destroy the child task. The result buffer is left initialized after +/// returning. +/// +/// This can be called from any thread. Its Swift signature is +/// +/// \code +/// func swift_asyncLet_get_throwing( +/// _ asyncLet: Builtin.RawPointer, +/// _ resultBuffer: Builtin.RawPointer +/// ) async throws +/// \endcode +/// +/// \c result points at the variable storage for the binding. It is +/// uninitialized until the first call to \c swift_asyncLet_get or +/// \c swift_asyncLet_get_throwing. That first call initializes the storage +/// with the result of the child task. Subsequent calls do nothing and leave +/// the value in place. The child task will be invalidated after +/// this call, so the `async let` can not be gotten or finished afterward. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) +void swift_asyncLet_consume_throwing(SWIFT_ASYNC_CONTEXT AsyncContext *, + AsyncLet *, + void *, + ThrowingTaskFutureWaitContinuationFunction *, + AsyncContext *); + /// Returns true if the currently executing AsyncTask has a /// 'TaskGroupTaskStatusRecord' present. /// @@ -419,56 +523,75 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_task_removeCancellationHandler( CancellationNotificationStatusRecord *record); +/// Create a NullaryContinuationJob from a continuation. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +NullaryContinuationJob* +swift_task_createNullaryContinuationJob( + size_t priority, + AsyncTask *continuation); + /// Report error about attempting to bind a task-local value from an illegal context. SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroup( const unsigned char *file, uintptr_t fileLength, bool fileIsASCII, uintptr_t line); -/// Get a task local value from the passed in task. Its Swift signature is +/// Get a task local value from either the current task, or fallback task-local +/// storage. +/// +/// Its Swift signature is /// /// \code /// func _taskLocalValueGet( -/// _ task: Builtin.NativeObject, /// keyType: Any.Type /*Key.Type*/ /// ) -> UnsafeMutableRawPointer? where Key: TaskLocalKey /// \endcode SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) OpaqueValue* -swift_task_localValueGet(AsyncTask* task, const HeapObject *key); +swift_task_localValueGet(const HeapObject *key); -/// Add a task local value to the passed in task. -/// -/// This must be only invoked by the task itself to avoid concurrent writes. +/// Bind a task local key to a value in the context of either the current +/// AsyncTask if present, or in the thread-local fallback context if no task +/// available. /// /// Its Swift signature is /// /// \code /// public func _taskLocalValuePush( -/// _ task: Builtin.NativeObject, /// keyType: Any.Type/*Key.Type*/, /// value: __owned Value /// ) /// \endcode SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -void swift_task_localValuePush(AsyncTask* task, - const HeapObject *key, - /* +1 */ OpaqueValue *value, - const Metadata *valueType); +void swift_task_localValuePush(const HeapObject *key, + /* +1 */ OpaqueValue *value, + const Metadata *valueType); -/// Remove task a local binding from the task local values stack. +/// Pop a single task local binding from the binding stack of the current task, +/// or the fallback thread-local storage if no task is available. +/// +/// This operation must be paired up with a preceding "push" operation, as otherwise +/// it may attempt to "pop" off an empty value stuck which will lead to a crash. /// -/// This must be only invoked by the task itself to avoid concurrent writes. +/// The Swift surface API ensures proper pairing of push and pop operations. /// /// Its Swift signature is /// /// \code -/// public func _taskLocalValuePop( -/// _ task: Builtin.NativeObject -/// ) +/// public func _taskLocalValuePop() +/// \endcode +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_localValuePop(); + +/// Copy all task locals from the current context to the target task. +/// +/// Its Swift signature is +/// +/// \code +/// func _taskLocalValueGet(AsyncTask* task) /// \endcode SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -void swift_task_localValuePop(AsyncTask* task); +void swift_task_localsCopyTo(AsyncTask* target); /// This should have the same representation as an enum like this: /// enum NearestTaskDeadline { @@ -495,19 +618,6 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) NearestTaskDeadline swift_task_getNearestDeadline(AsyncTask *task); -/// Run the given async function and block the current thread until -/// it returns. This is a hack added for testing purposes; eventually -/// top-level code will be an async context, and the need for this in -/// tests should go away. We *definitely* do not want this to be part -/// of the standard feature set. -/// -/// The argument is a `() async -> ()` function, whose ABI is currently -/// quite complex. Eventually this should use a different convention; -/// that's rdar://72105841. -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -void swift_task_runAndBlockThread(const void *function, - HeapObject *functionContext); - /// Switch the current task to a new executor if we aren't already /// running on a compatible executor. /// @@ -551,6 +661,10 @@ void swift_task_enqueueGlobalWithDelay(unsigned long long delay, Job *job); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_task_enqueueMainExecutor(Job *job); +/// Enqueue the given job on the main executor. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_enqueueOnDispatchQueue(Job *job, HeapObject *queue); + /// A hook to take over global enqueuing. typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobal_original)(Job *job); SWIFT_EXPORT_FROM(swift_Concurrency) @@ -588,6 +702,15 @@ void swift_defaultActor_deallocate(DefaultActor *actor); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_defaultActor_deallocateResilient(HeapObject *actor); +/// Initialize the runtime storage for a distributed remote actor. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +OpaqueValue* +swift_distributedActor_remote_initialize(const Metadata *actorType); + +/// Destroy the runtime storage for a default actor. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_distributedActor_destroy(DefaultActor *actor); + /// Enqueue a job on the default actor implementation. /// /// The job must be ready to run. Notably, if it's a task, that @@ -603,20 +726,32 @@ void swift_defaultActor_deallocateResilient(HeapObject *actor); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_defaultActor_enqueue(Job *job, DefaultActor *actor); +/// Check if the actor is a distributed 'remote' actor instance. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +bool swift_distributed_actor_is_remote(DefaultActor *actor); + +/// Do a primitive suspension of the current task, as if part of +/// a continuation, although this does not provide any of the +/// higher-level continuation semantics. The current task is returned; +/// its ResumeFunction and ResumeContext will need to be initialized, +/// and then it will need to be enqueued or run as a job later. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +AsyncTask *swift_task_suspend(); + /// Prepare a continuation in the current task. /// /// The caller should initialize the Parent, ResumeParent, /// and NormalResult fields. This function will initialize the other -/// fields with appropriate defaaults; the caller may then overwrite +/// fields with appropriate defaults; the caller may then overwrite /// them if desired. -/// -/// This function is provided as a code-size and runtime-usage -/// optimization; calling it is not required if code is willing to -/// do all its work inline. SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) AsyncTask *swift_continuation_init(ContinuationAsyncContext *context, AsyncContinuationFlags flags); +/// Await an initialized continuation. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync) +void swift_continuation_await(ContinuationAsyncContext *continuationContext); + /// Resume a task from a non-throwing continuation, given a normal /// result which has already been stored into the continuation. SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) @@ -656,6 +791,10 @@ AsyncTask *swift_task_getCurrent(void); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) ExecutorRef swift_task_getCurrentExecutor(void); +/// Return the main-actor executor reference. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +ExecutorRef swift_task_getMainExecutor(void); + SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) bool swift_task_isCurrentExecutor(ExecutorRef executor); @@ -667,6 +806,18 @@ void swift_task_reportUnexpectedExecutor( SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) JobPriority swift_task_getCurrentThreadPriority(void); +#ifdef __APPLE__ +/// A magic symbol whose address is the mask to apply to a frame pointer to +/// signal that it is an async frame. Do not try to read the actual value of +/// this global, it will crash. +/// +/// On ARM64_32, the address is only 32 bits, and therefore this value covers +/// the top 32 bits of the in-memory frame pointer. On other 32-bit platforms, +/// the bit is not used and the address is always 0. +SWIFT_EXPORT_FROM(swift_Concurrency) +struct { char c; } swift_async_extendedFramePointerFlags; +#endif + } #pragma clang diagnostic pop diff --git a/include/swift/Runtime/Concurrent.h b/include/swift/Runtime/Concurrent.h index 693f2104d9ad8..e5452d0b7bc28 100644 --- a/include/swift/Runtime/Concurrent.h +++ b/include/swift/Runtime/Concurrent.h @@ -639,7 +639,7 @@ struct ConcurrentReadableHashMap { /// Otherwise, just return the passed-in size, which is always valid even if /// not necessarily optimal. static size_t goodSize(size_t size) { -#if defined(__APPLE__) && defined(__MACH__) +#if defined(__APPLE__) && defined(__MACH__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC return malloc_good_size(size); #else return size; diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index 51d473b6b7707..e4828c874c5c7 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -20,6 +20,39 @@ #include "swift/Basic/Compiler.h" #include "swift/Runtime/CMakeConfig.h" +/// SWIFT_RUNTIME_WEAK_IMPORT - Marks a symbol for weak import. +#if (__has_attribute(weak_import)) +#define SWIFT_RUNTIME_WEAK_IMPORT __attribute__((weak_import)) +#else +#define SWIFT_RUNTIME_WEAK_IMPORT +#endif + +/// SWIFT_RUNTIME_WEAK_CHECK - Tests if a potentially weakly linked function +/// is linked into the runtime. This is useful on Apple platforms where it is +/// possible that system functions are only available on newer versions. +#ifdef __clang__ +#define SWIFT_RUNTIME_WEAK_CHECK(x) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunguarded-availability\"") \ + _Pragma("clang diagnostic ignored \"-Wunguarded-availability-new\"") \ + (&x) \ + _Pragma("clang diagnostic pop") +#else +#define SWIFT_RUNTIME_WEAK_CHECK(x) &x +#endif + +/// SWIFT_RUNTIME_WEAK_USE - Use a potentially weakly imported symbol. +#ifdef __clang__ +#define SWIFT_RUNTIME_WEAK_USE(x) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunguarded-availability\"") \ + _Pragma("clang diagnostic ignored \"-Wunguarded-availability-new\"") \ + (x) \ + _Pragma("clang diagnostic pop") +#else +#define SWIFT_RUNTIME_WEAK_USE(x) x +#endif + /// SWIFT_RUNTIME_LIBRARY_VISIBILITY - If a class marked with this attribute is /// linked into a shared library, then the class should be private to the /// library and not accessible from outside it. Can also be used to mark diff --git a/include/swift/Runtime/Enum.h b/include/swift/Runtime/Enum.h index dea2c6f047bb8..d35badee02c7e 100644 --- a/include/swift/Runtime/Enum.h +++ b/include/swift/Runtime/Enum.h @@ -129,6 +129,24 @@ void swift_storeEnumTagMultiPayload(OpaqueValue *value, const EnumMetadata *enumType, unsigned whichCase); +/// The unspecialized getEnumTagSinglePayload value witness to be used by the +/// VWTs for for specialized generic enums that are multi-payload. +/// +/// Runtime availability: Swift 5.6 +SWIFT_RUNTIME_EXPORT +unsigned swift_getMultiPayloadEnumTagSinglePayload(const OpaqueValue *value, + uint32_t numExtraCases, + const Metadata *enumType); + +/// The unspecialized storeEnumTagSinglePayload value witness to be used by the +/// VWTs for for specialized generic enums that are multi-payload. +/// +/// Runtime availability: Swift 5.6 +SWIFT_RUNTIME_EXPORT +void swift_storeMultiPayloadEnumTagSinglePayload(OpaqueValue *value, + uint32_t index, + uint32_t numExtraCases, + const Metadata *enumType); } #endif diff --git a/include/swift/Runtime/Exclusivity.h b/include/swift/Runtime/Exclusivity.h index bb64e1ada2abb..baf3fce276d02 100644 --- a/include/swift/Runtime/Exclusivity.h +++ b/include/swift/Runtime/Exclusivity.h @@ -40,25 +40,6 @@ SWIFT_RUNTIME_EXPORT void swift_beginAccess(void *pointer, ValueBuffer *buffer, ExclusivityFlags flags, void *pc); -/// Loads the replacement function pointer from \p ReplFnPtr and returns the -/// replacement function if it should be called. -/// Returns null if the original function (which is passed in \p CurrFn) should -/// be called. -#ifdef __APPLE__ -__attribute__((weak_import)) -#endif -SWIFT_RUNTIME_EXPORT -char *swift_getFunctionReplacement(char **ReplFnPtr, char *CurrFn); - -/// Returns the original function of a replaced function, which is loaded from -/// \p OrigFnPtr. -/// This function is called from a replacement function to call the original -/// function. -#ifdef __APPLE__ -__attribute__((weak_import)) -#endif -SWIFT_RUNTIME_EXPORT -char *swift_getOrigOfReplaceable(char **OrigFnPtr); /// Stop dynamically tracking an access. SWIFT_RUNTIME_EXPORT @@ -91,6 +72,37 @@ void swift_dumpTrackedAccesses(); #endif +// When building the concurrency library for back deployment, we rename these +// symbols unformly so they don't conflict with the real concurrency library. +#ifdef SWIFT_CONCURRENCY_BACK_DEPLOYMENT +# define swift_task_enterThreadLocalContext swift_task_enterThreadLocalContextBackDeploy +# define swift_task_exitThreadLocalContext swift_task_exitThreadLocalContextBackDeploy +#endif + +/// Called when a task inits, resumes and returns control to caller synchronous +/// code to update any exclusivity specific state associated with the task. +/// +/// State is assumed to point to a buffer of memory with +/// swift_task_threadLocalContextSize bytes that was initialized with +/// swift_task_initThreadLocalContext. +/// +/// We describe the algorithm in detail on SwiftTaskThreadLocalContext in +/// Exclusivity.cpp. +SWIFT_RUNTIME_EXPORT +void swift_task_enterThreadLocalContext(char *state); + +/// Called when a task suspends and returns control to caller synchronous code +/// to update any exclusivity specific state associated with the task. +/// +/// State is assumed to point to a buffer of memory with +/// swift_task_threadLocalContextSize bytes that was initialized with +/// swift_task_initThreadLocalContext. +/// +/// We describe the algorithm in detail on SwiftTaskThreadLocalContext in +/// Exclusivity.cpp. +SWIFT_RUNTIME_EXPORT +void swift_task_exitThreadLocalContext(char *state); + } // end namespace swift #endif diff --git a/include/swift/Runtime/FunctionReplacement.h b/include/swift/Runtime/FunctionReplacement.h new file mode 100644 index 0000000000000..14bc616f49970 --- /dev/null +++ b/include/swift/Runtime/FunctionReplacement.h @@ -0,0 +1,42 @@ +//===--- FunctionReplacement.h --------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_FUNCTIONREPLACEMENT_H +#define SWIFT_RUNTIME_FUNCTIONREPLACEMENT_H + +#include "swift/Runtime/Config.h" + +namespace swift { + +/// Loads the replacement function pointer from \p ReplFnPtr and returns the +/// replacement function if it should be called. +/// Returns null if the original function (which is passed in \p CurrFn) should +/// be called. +#ifdef __APPLE__ +__attribute__((weak_import)) +#endif +SWIFT_RUNTIME_EXPORT char * +swift_getFunctionReplacement(char **ReplFnPtr, char *CurrFn); + +/// Returns the original function of a replaced function, which is loaded from +/// \p OrigFnPtr. +/// This function is called from a replacement function to call the original +/// function. +#ifdef __APPLE__ +__attribute__((weak_import)) +#endif +SWIFT_RUNTIME_EXPORT char * +swift_getOrigOfReplaceable(char **OrigFnPtr); + +} // namespace swift + +#endif diff --git a/include/swift/Runtime/HeapObject.h b/include/swift/Runtime/HeapObject.h index 8fbcb99d37f29..e3487eaef3ced 100644 --- a/include/swift/Runtime/HeapObject.h +++ b/include/swift/Runtime/HeapObject.h @@ -211,6 +211,18 @@ size_t swift_unownedRetainCount(HeapObject *object); SWIFT_RUNTIME_EXPORT size_t swift_weakRetainCount(HeapObject *object); +/// Is this pointer a non-null unique reference to an object? +SWIFT_RUNTIME_EXPORT +bool swift_isUniquelyReferenced(const void *); + +/// Is this non-null pointer a unique reference to an object? +SWIFT_RUNTIME_EXPORT +bool swift_isUniquelyReferenced_nonNull(const void *); + +/// Is this non-null BridgeObject a unique reference to an object? +SWIFT_RUNTIME_EXPORT +bool swift_isUniquelyReferenced_nonNull_bridgeObject(uintptr_t bits); + /// Is this pointer a non-null unique reference to an object /// that uses Swift reference counting? SWIFT_RUNTIME_EXPORT diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 67d5cfa6439de..18f54d5f2c35e 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -499,6 +499,13 @@ swift_getFunctionTypeMetadataDifferentiable( const Metadata *const *parameters, const uint32_t *parameterFlags, const Metadata *result); +SWIFT_RUNTIME_EXPORT +const FunctionTypeMetadata * +swift_getFunctionTypeMetadataGlobalActor( + FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind, + const Metadata *const *parameters, const uint32_t *parameterFlags, + const Metadata *result, const Metadata *globalActor); + SWIFT_RUNTIME_EXPORT const FunctionTypeMetadata * swift_getFunctionTypeMetadata0(FunctionTypeFlags flags, diff --git a/include/swift/Runtime/MutexPThread.h b/include/swift/Runtime/MutexPThread.h index a6f92a955ad3a..64eedaea57c2e 100644 --- a/include/swift/Runtime/MutexPThread.h +++ b/include/swift/Runtime/MutexPThread.h @@ -37,12 +37,11 @@ typedef os_unfair_lock MutexHandle; typedef pthread_mutex_t MutexHandle; #endif -#if defined(__CYGWIN__) || defined(__ANDROID__) || defined(__HAIKU__) || defined(__wasi__) +#if defined(__CYGWIN__) || defined(__HAIKU__) || defined(__wasi__) // At the moment CYGWIN pthreads implementation doesn't support the use of // constexpr for static allocation versions. The way they define things -// results in a reinterpret_cast which violates constexpr. Similarly, Android's -// pthread implementation makes use of volatile attributes that prevent it from -// being marked as constexpr. WASI currently doesn't support threading/locking at all. +// results in a reinterpret_cast which violates constexpr. +// WASI currently doesn't support threading/locking at all. #define SWIFT_CONDITION_SUPPORTS_CONSTEXPR 0 #define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 0 #define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 0 diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index b0c677b135e70..6de7a45a2ba06 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -473,6 +473,30 @@ FUNCTION(IsUniquelyReferencedNonObjC_nonNull_bridgeObject, ARGS(BridgeObjectPtrTy), ATTRS(NoUnwind, ZExt)) +// bool swift_isUniquelyReferenced(const void *); +FUNCTION(IsUniquelyReferenced, swift_isUniquelyReferenced, + C_CC, ObjCIsUniquelyReferencedAvailability, + RETURNS(Int1Ty), + ARGS(UnknownRefCountedPtrTy), + ATTRS(NoUnwind, ZExt)) + +// bool swift_isUniquelyReferenced_nonNull(const void *); +FUNCTION(IsUniquelyReferenced_nonNull, + swift_isUniquelyReferenced_nonNull, + C_CC, ObjCIsUniquelyReferencedAvailability, + RETURNS(Int1Ty), + ARGS(UnknownRefCountedPtrTy), + ATTRS(NoUnwind, ZExt)) + +// bool swift_isUniquelyReferenced_nonNull_bridgeObject( +// uintptr_t bits); +FUNCTION(IsUniquelyReferenced_nonNull_bridgeObject, + swift_isUniquelyReferenced_nonNull_bridgeObject, + C_CC, ObjCIsUniquelyReferencedAvailability, + RETURNS(Int1Ty), + ARGS(BridgeObjectPtrTy), + ATTRS(NoUnwind, ZExt)) + // bool swift_isUniquelyReferenced_native(const struct HeapObject *); FUNCTION(IsUniquelyReferenced_native, swift_isUniquelyReferenced_native, C_CC, AlwaysAvailable, @@ -591,6 +615,26 @@ FUNCTION(GetFunctionMetadataDifferentiable, TypeMetadataPtrTy), ATTRS(NoUnwind, ReadOnly)) +// Metadata * +// swift_getFunctionTypeMetadataGlobalActor(unsigned long flags, +// unsigned long diffKind, +// const Metadata **parameters, +// const uint32_t *parameterFlags, +// const Metadata *result, +// const Metadata *globalActor); +FUNCTION(GetFunctionMetadataGlobalActor, + swift_getFunctionTypeMetadataGlobalActor, + C_CC, ConcurrencyAvailability, + RETURNS(TypeMetadataPtrTy), + ARGS(SizeTy, + SizeTy, + TypeMetadataPtrTy->getPointerTo(0), + Int32Ty->getPointerTo(0), + TypeMetadataPtrTy, + TypeMetadataPtrTy), + ATTRS(NoUnwind, ReadOnly)) + + // Metadata *swift_getFunctionTypeMetadata0(unsigned long flags, // const Metadata *resultMetadata); FUNCTION(GetFunctionMetadata0, swift_getFunctionTypeMetadata0, @@ -1527,71 +1571,20 @@ FUNCTION(TaskCancel, ARGS(SwiftTaskPtrTy), ATTRS(NoUnwind, ArgMemOnly)) -// AsyncTaskAndContext swift_task_create_f( -// size_t flags, -// TaskContinuationFunction* function, -// size_t contextSize); -FUNCTION(TaskCreateFunc, - swift_task_create_f, SwiftCC, - ConcurrencyAvailability, - RETURNS(AsyncTaskAndContextTy), - ARGS(SizeTy, TaskContinuationFunctionPtrTy, SizeTy), - ATTRS(NoUnwind, ArgMemOnly)) - -// AsyncTaskAndContext swift_task_create_future( -// size_t flags, -// const Metadata *futureResultType, -// void *closureEntry, -// HeapObject *closureContext); -FUNCTION(TaskCreateFuture, - swift_task_create_future, SwiftCC, - ConcurrencyAvailability, - RETURNS(AsyncTaskAndContextTy), - ARGS(SizeTy, TypeMetadataPtrTy, - Int8PtrTy, RefCountedPtrTy), - ATTRS(NoUnwind, ArgMemOnly)) - -// AsyncTaskAndContext swift_task_create_future_f( -// size_t flags, -// const Metadata *futureResultType, -// TaskContinuationFunction *function, -// size_t contextSize); -FUNCTION(TaskCreateFutureFunc, - swift_task_create_future_f, SwiftCC, - ConcurrencyAvailability, - RETURNS(AsyncTaskAndContextTy), - ARGS(SizeTy, TypeMetadataPtrTy, - TaskContinuationFunctionPtrTy, SizeTy), - ATTRS(NoUnwind, ArgMemOnly)) - -// AsyncTaskAndContext swift_task_create_group_future( -// size_t flags, -// TaskGroup *group, +// AsyncTaskAndContext swift_task_create( +// size_t taskCreateFlags, +// TaskOptionRecord *options, // const Metadata *futureResultType, -// void *closureEntry, -// HeapObject *closureContext); -FUNCTION(TaskCreateGroupFuture, - swift_task_create_group_future, SwiftCC, +// void *closureEntry, HeapObject *closureContext); +FUNCTION(TaskCreate, + swift_task_create, SwiftCC, ConcurrencyAvailability, RETURNS(AsyncTaskAndContextTy), - ARGS(SizeTy, SwiftTaskGroupPtrTy, - TypeMetadataPtrTy, - Int8PtrTy, RefCountedPtrTy), - ATTRS(NoUnwind, ArgMemOnly)) - -// AsyncTaskAndContext swift_task_create_group_future_f( -// size_t flags, -// TaskGroup *group, -// const Metadata *futureResultType, -// TaskContinuationFunction *function, -// size_t contextSize); -FUNCTION(TaskCreateGroupFutureFunc, - swift_task_create_group_future_f, SwiftCC, - ConcurrencyAvailability, - RETURNS(AsyncTaskAndContextTy), - ARGS(SizeTy, SwiftTaskGroupPtrTy, + ARGS(SizeTy, + SwiftTaskOptionRecordPtrTy, TypeMetadataPtrTy, - TaskContinuationFunctionPtrTy, SizeTy), + Int8PtrTy, + RefCountedPtrTy), ATTRS(NoUnwind, ArgMemOnly)) // void swift_task_switch(AsyncContext *resumeContext, @@ -1613,6 +1606,14 @@ FUNCTION(ContinuationInit, ARGS(ContinuationAsyncContextPtrTy, SizeTy), ATTRS(NoUnwind)) +// void swift_continuation_await(AsyncContext *continuationContext); +FUNCTION(ContinuationAwait, + swift_continuation_await, SwiftAsyncCC, + ConcurrencyAvailability, + RETURNS(VoidTy), + ARGS(ContinuationAsyncContextPtrTy), + ATTRS(NoUnwind)) + // void swift_continuation_resume(AsyncTask *continuation); FUNCTION(ContinuationResume, swift_continuation_resume, SwiftCC, @@ -1646,6 +1647,14 @@ FUNCTION(TaskGetCurrentExecutor, ARGS(), ATTRS(NoUnwind, ArgMemOnly)) +// ExecutorRef swift_task_getMainExecutor(); +FUNCTION(TaskGetMainExecutor, + swift_task_getMainExecutor, SwiftCC, + ConcurrencyAvailability, + RETURNS(SwiftExecutorTy), + ARGS(), + ATTRS(NoUnwind, ArgMemOnly)) + // void swift_defaultActor_initialize(DefaultActor *actor); FUNCTION(DefaultActorInitialize, swift_defaultActor_initialize, SwiftCC, @@ -1678,20 +1687,61 @@ FUNCTION(DefaultActorDeallocateResilient, ARGS(RefCountedPtrTy), ATTRS(NoUnwind)) +// OpaqueValue* swift_distributedActor_remote_initialize( +// const Metadata *actorType +// ); +FUNCTION(DistributedActorInitializeRemote, + swift_distributedActor_remote_initialize, SwiftCC, + ConcurrencyAvailability, // TODO(distributed): Introduce DistributedAvailability once shipping somewhere + RETURNS(OpaquePtrTy), + ARGS(TypeMetadataPtrTy), + ATTRS(NoUnwind)) + +// void swift_distributedActor_destroy(DefaultActor *actor); +FUNCTION(DistributedActorDestroy, + swift_distributedActor_destroy, SwiftCC, + ConcurrencyAvailability, // TODO(distributed): Introduce DistributedAvailability once shipping somewhere + RETURNS(VoidTy), + ARGS(RefCountedPtrTy), + ATTRS(NoUnwind)) + /// void swift_asyncLet_start( /// AsyncLet *alet, +/// TaskOptionRecord *options, /// const Metadata *futureResultType, /// void *closureEntryPoint, -/// OpaqueValue *closureContext +/// HeapObject *closureContext /// ); FUNCTION(AsyncLetStart, swift_asyncLet_start, SwiftCC, ConcurrencyAvailability, RETURNS(VoidTy), - ARGS(SwiftAsyncLetPtrTy, // AsyncLet*, alias for Int8PtrTy - TypeMetadataPtrTy, // futureResultType - Int8PtrTy, // closureEntry - OpaquePtrTy, // closureContext + ARGS(SwiftAsyncLetPtrTy, // AsyncLet* + SwiftTaskOptionRecordPtrTy, // options + TypeMetadataPtrTy, // futureResultType + Int8PtrTy, // closureEntry + OpaquePtrTy // closureContext + ), + ATTRS(NoUnwind, ArgMemOnly)) + +/// void swift_asyncLet_begin( +/// AsyncLet *alet, +/// TaskOptionRecord *options, +/// const Metadata *futureResultType, +/// void *closureEntryPoint, +/// HeapObject *closureContext, +/// void *resultBuffer +/// ); +FUNCTION(AsyncLetBegin, + swift_asyncLet_begin, SwiftCC, + ConcurrencyAvailability, + RETURNS(VoidTy), + ARGS(SwiftAsyncLetPtrTy, // AsyncLet* + SwiftTaskOptionRecordPtrTy, // options + TypeMetadataPtrTy, // futureResultType + Int8PtrTy, // closureEntry + OpaquePtrTy, // closureContext + Int8PtrTy ), ATTRS(NoUnwind, ArgMemOnly)) @@ -1708,7 +1758,7 @@ FUNCTION(TaskGroupInitialize, swift_taskGroup_initialize, SwiftCC, ConcurrencyAvailability, RETURNS(VoidTy), - ARGS(Int8PtrTy), + ARGS(Int8PtrTy, TypeMetadataPtrTy), ATTRS(NoUnwind)) // void swift_taskGroup_destroy(TaskGroup *group); @@ -1743,6 +1793,29 @@ FUNCTION(AutoDiffAllocateSubcontext, ARGS(RefCountedPtrTy, SizeTy), ATTRS(NoUnwind, ArgMemOnly)) +// SWIFT_RUNTIME_EXPORT +// unsigned swift_getMultiPayloadEnumTagSinglePayload(const OpaqueValue *value, +// uint32_t numExtraCases, +// const Metadata *enumType) +FUNCTION(GetMultiPayloadEnumTagSinglePayload, + swift_getMultiPayloadEnumTagSinglePayload, C_CC, + MultiPayloadEnumTagSinglePayloadAvailability, + RETURNS(Int32Ty), + ARGS(OpaquePtrTy, Int32Ty, TypeMetadataPtrTy), + ATTRS(NoUnwind)) + +// SWIFT_RUNTIME_EXPORT +// void swift_storeMultiPayloadEnumTagSinglePayload(OpaqueValue *value, +// uint32_t index, +// uint32_t numExtraCases, +// const Metadata *enumType); +FUNCTION(StoreMultiPayloadEnumTagSinglePayload, + swift_storeMultiPayloadEnumTagSinglePayload, C_CC, + MultiPayloadEnumTagSinglePayloadAvailability, + RETURNS(VoidTy), + ARGS(OpaquePtrTy, Int32Ty, Int32Ty, TypeMetadataPtrTy), + ATTRS(NoUnwind)) + #undef RETURNS #undef ARGS #undef ATTRS diff --git a/include/swift/Runtime/ThreadLocal.h b/include/swift/Runtime/ThreadLocal.h index 1af2eb9cb38a2..b3e906a445226 100644 --- a/include/swift/Runtime/ThreadLocal.h +++ b/include/swift/Runtime/ThreadLocal.h @@ -18,9 +18,15 @@ #ifndef SWIFT_RUNTIME_THREADLOCAL_H #define SWIFT_RUNTIME_THREADLOCAL_H +#include +#include "ThreadLocalStorage.h" + /// SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL - Does the current configuration /// allow the use of SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL? -#if defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) +#if defined(__APPLE__) +// The pthread TLS APIs work better than C++ TLS on Apple platforms. +#define SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL 0 +#elif defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) // We define SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL to nothing in this // configuration and just use a global variable, so this is okay. #define SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL 1 @@ -57,6 +63,17 @@ #endif namespace swift { +// Validate a type stored in thread-local storage, using static asserts. Such +// types must fit in a pointer and be trivially copyable/destructible. +#define VALIDATE_THREAD_LOCAL_TYPE(T) \ + static_assert(sizeof(T) <= sizeof(void *), \ + "cannot store more than a pointer"); \ + static_assert(std::is_trivially_copyable::value, \ + "ThreadLocal values must be trivially copyable"); \ + static_assert(std::is_trivially_destructible::value, \ + "ThreadLocal cleanup is not supported, stored types must be " \ + "trivially destructible"); + // A wrapper class for thread-local storage. // // - On platforms that report SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL @@ -65,57 +82,77 @@ namespace swift { // itself thread-local, and no internal support is required. // // Note that this includes platforms that set -// SWIFT_STDLIB_SINGLE_THREADED_RUNTIME, for whhch +// SWIFT_STDLIB_SINGLE_THREADED_RUNTIME, for which // SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL is empty; // thread-local declarations then create an ordinary global. // // - On platforms that don't report SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL, // we have to simulate thread-local storage. Fortunately, all of // these platforms (at least for now) support pthread_getspecific. +#if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL template class ThreadLocal { - static_assert(sizeof(T) <= sizeof(void*), "cannot store more than a pointer"); + VALIDATE_THREAD_LOCAL_TYPE(T) -#if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL T value; + +public: + constexpr ThreadLocal() {} + + T get() { return value; } + + void set(T newValue) { value = newValue; } +}; #else +// A wrapper around a pthread_key_t that is lazily initialized using +// dispatch_once. +class ThreadLocalKey { // We rely on the zero-initialization of objects with static storage // duration. dispatch_once_t once; pthread_key_t key; +public: pthread_key_t getKey() { - dispatch_once_f(&once, this, [](void *ctx) { - pthread_key_create(&reinterpret_cast(ctx)->key, nullptr); + dispatch_once_f(&once, &key, [](void *ctx) { + pthread_key_create(reinterpret_cast(ctx), nullptr); }); return key; } -#endif +}; + +// A type representing a constant pthread_key_t, for use on platforms that +// provide reserved keys. +template +class ConstantThreadLocalKey { +public: + pthread_key_t getKey() { return constantKey; } +}; + +template +class ThreadLocal { + VALIDATE_THREAD_LOCAL_TYPE(T) + + Key key; public: constexpr ThreadLocal() {} T get() { -#if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL - return value; -#else - void *storedValue = pthread_getspecific(getKey()); + void *storedValue = SWIFT_THREAD_GETSPECIFIC(key.getKey()); T value; memcpy(&value, &storedValue, sizeof(T)); return value; -#endif } void set(T newValue) { -#if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL - value = newValue; -#else void *storedValue; memcpy(&storedValue, &newValue, sizeof(T)); - pthread_setspecific(getKey(), storedValue); -#endif + SWIFT_THREAD_SETSPECIFIC(key.getKey(), storedValue); } }; +#endif + } // end namespace swift /// SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME) - Declare a variable @@ -124,13 +161,16 @@ class ThreadLocal { /// /// Because of the fallback path, the default-initialization of the /// type must be equivalent to a bitwise zero-initialization, and the -/// type must be small and trivially copyable. +/// type must be small and trivially copyable and destructible. #if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL -#define SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME) \ +#define SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME, KEY) \ SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL swift::ThreadLocal NAME +#elif SWIFT_TLS_HAS_RESERVED_PTHREAD_SPECIFIC +#define SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME, KEY) \ + swift::ThreadLocal> NAME #else -#define SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME) \ - swift::ThreadLocal NAME +#define SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME, KEY) \ + swift::ThreadLocal NAME #endif #endif diff --git a/stdlib/public/runtime/ThreadLocalStorage.h b/include/swift/Runtime/ThreadLocalStorage.h similarity index 83% rename from stdlib/public/runtime/ThreadLocalStorage.h rename to include/swift/Runtime/ThreadLocalStorage.h index 89b3418004dcb..1137fb4d31c00 100644 --- a/stdlib/public/runtime/ThreadLocalStorage.h +++ b/include/swift/Runtime/ThreadLocalStorage.h @@ -48,12 +48,33 @@ extern "C" int pthread_key_init_np(int key, void (*destructor)(void *)); # ifndef __PTK_FRAMEWORK_SWIFT_KEY3 # define __PTK_FRAMEWORK_SWIFT_KEY3 103 # endif +# ifndef __PTK_FRAMEWORK_SWIFT_KEY4 +# define __PTK_FRAMEWORK_SWIFT_KEY4 104 +# endif +# ifndef __PTK_FRAMEWORK_SWIFT_KEY5 +# define __PTK_FRAMEWORK_SWIFT_KEY5 105 +# endif +# ifndef __PTK_FRAMEWORK_SWIFT_KEY6 +# define __PTK_FRAMEWORK_SWIFT_KEY6 106 +# endif +# ifndef __PTK_FRAMEWORK_SWIFT_KEY7 +# define __PTK_FRAMEWORK_SWIFT_KEY7 107 +# endif +# ifndef __PTK_FRAMEWORK_SWIFT_KEY8 +# define __PTK_FRAMEWORK_SWIFT_KEY8 108 +# endif +# ifndef __PTK_FRAMEWORK_SWIFT_KEY9 +# define __PTK_FRAMEWORK_SWIFT_KEY9 109 +# endif # define SWIFT_RUNTIME_TLS_KEY __PTK_FRAMEWORK_SWIFT_KEY0 # define SWIFT_STDLIB_TLS_KEY __PTK_FRAMEWORK_SWIFT_KEY1 # define SWIFT_COMPATIBILITY_50_TLS_KEY __PTK_FRAMEWORK_SWIFT_KEY2 # define SWIFT_CONCURRENCY_TASK_KEY __PTK_FRAMEWORK_SWIFT_KEY3 +# define SWIFT_CONCURRENCY_EXECUTOR_TRACKING_INFO_KEY __PTK_FRAMEWORK_SWIFT_KEY4 +# define SWIFT_CONCURRENCY_FALLBACK_TASK_LOCAL_STORAGE_KEY \ + __PTK_FRAMEWORK_SWIFT_KEY5 #endif diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index 58dccba09dbc7..c1898e028a063 100644 --- a/include/swift/SIL/AbstractionPattern.h +++ b/include/swift/SIL/AbstractionPattern.h @@ -794,8 +794,7 @@ class AbstractionPattern { /// Note that, for most purposes, you should lower a field's type against its /// *unsubstituted* interface type. AbstractionPattern - unsafeGetSubstFieldType(ValueDecl *member, - CanType origMemberType = CanType()) const; + unsafeGetSubstFieldType(ValueDecl *member, CanType origMemberType) const; private: /// Return an abstraction pattern for the curried type of an @@ -1379,6 +1378,13 @@ class AbstractionPattern { /// Swift type. AbstractionPattern getObjCMethodAsyncCompletionHandlerType( CanType swiftCompletionHandlerType) const; + + /// If this pattern refers to a foreign ObjC method that was imported as + /// async, return the bridged-back-to-ObjC completion handler type. + CanType getObjCMethodAsyncCompletionHandlerForeignType( + ForeignAsyncConvention convention, + Lowering::TypeConverter &TC + ) const; void dump() const LLVM_ATTRIBUTE_USED; void print(raw_ostream &OS) const; diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index 84019426c3b86..a0d6e866030b7 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -377,6 +377,7 @@ class ApplySite { case SILArgumentConvention::Indirect_Out: llvm_unreachable("partial_apply cannot have an @out operand"); } + llvm_unreachable("covered switch"); } /// Return true if 'self' is an applied argument. @@ -454,7 +455,9 @@ class ApplySite { case ApplySiteKind::PartialApplyInst: return ApplyOptions(); } + llvm_unreachable("covered switch"); } + /// Return whether the given apply is of a formally-throwing function /// which is statically known not to throw. bool isNonThrowing() const { diff --git a/include/swift/SIL/BasicBlockUtils.h b/include/swift/SIL/BasicBlockUtils.h index 35ca0fa56077e..9c7546c77ac20 100644 --- a/include/swift/SIL/BasicBlockUtils.h +++ b/include/swift/SIL/BasicBlockUtils.h @@ -87,6 +87,10 @@ class DeadEndBlocks { /// Used to determine if we need to verify a DeadEndBlocks. bool isComputed() const { return didComputeValue; } + /// Add any (new) blocks that are backward-reachable from \p reachableBB to + /// the set of reachable blocks. + void updateForReachableBlock(SILBasicBlock *reachableBB); + const SILFunction *getFunction() const { return f; } /// Performs a simple check if \p block (or its single successor) ends in an @@ -95,6 +99,9 @@ class DeadEndBlocks { /// This handles the common case of failure-handling blocks, which e.g. /// contain a call to fatalError(). static bool triviallyEndsInUnreachable(SILBasicBlock *block); + +protected: + void propagateNewlyReachableBlocks(unsigned startIdx); }; /// Compute joint-postdominating set for \p dominatingBlock and \p diff --git a/include/swift/SIL/BridgedSwiftObject.h b/include/swift/SIL/BridgedSwiftObject.h new file mode 100644 index 0000000000000..39048ff3e5aea --- /dev/null +++ b/include/swift/SIL/BridgedSwiftObject.h @@ -0,0 +1,43 @@ +//===--- BridgedSwiftObject.h - C header which defines SwiftObject --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 is a C header, which defines the SwiftObject header. For the C++ version +// see SwiftObjectHeader.h. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_BRIDGEDSWIFTOBJECT_H +#define SWIFT_SIL_BRIDGEDSWIFTOBJECT_H + +#include + +#if !__has_feature(nullability) +# define _Nullable +# define _Nonnull +# define _Null_unspecified +#endif + +typedef const void * _Nonnull SwiftMetatype; + +/// The header of a Swift object. +/// +/// This must be in sync with HeapObject, which is defined in the runtime lib. +/// It must be layout compatible with the Swift object header. +struct BridgedSwiftObject { + SwiftMetatype metatype; + int64_t refCounts; +}; + +typedef struct BridgedSwiftObject * _Nonnull SwiftObject; +typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject; + +#endif diff --git a/include/swift/SIL/DebugUtils.h b/include/swift/SIL/DebugUtils.h index 2e2553bee6f05..ebe5fc7f015a5 100644 --- a/include/swift/SIL/DebugUtils.h +++ b/include/swift/SIL/DebugUtils.h @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// // // This file contains utilities to work with debug-info related instructions: -// debug_value and debug_value_addr. +// debug_value, alloc_stack, and alloc_box. // // SIL optimizations should deal with debug-info related instructions when // looking at the uses of a value. @@ -228,7 +228,6 @@ struct DebugVarCarryingInst { enum class Kind { Invalid = 0, DebugValue, - DebugValueAddr, AllocStack, AllocBox, }; @@ -239,8 +238,6 @@ struct DebugVarCarryingInst { DebugVarCarryingInst() : kind(Kind::Invalid), inst(nullptr) {} DebugVarCarryingInst(DebugValueInst *dvi) : kind(Kind::DebugValue), inst(dvi) {} - DebugVarCarryingInst(DebugValueAddrInst *dvai) - : kind(Kind::DebugValueAddr), inst(dvai) {} DebugVarCarryingInst(AllocStackInst *asi) : kind(Kind::AllocStack), inst(asi) {} DebugVarCarryingInst(AllocBoxInst *abi) : kind(Kind::AllocBox), inst(abi) {} @@ -252,9 +249,6 @@ struct DebugVarCarryingInst { case SILInstructionKind::DebugValueInst: kind = Kind::DebugValue; break; - case SILInstructionKind::DebugValueAddrInst: - kind = Kind::DebugValueAddr; - break; case SILInstructionKind::AllocStackInst: kind = Kind::AllocStack; break; @@ -283,13 +277,12 @@ struct DebugVarCarryingInst { llvm_unreachable("Invalid?!"); case Kind::DebugValue: return cast(inst)->getDecl(); - case Kind::DebugValueAddr: - return cast(inst)->getDecl(); case Kind::AllocStack: return cast(inst)->getDecl(); case Kind::AllocBox: return cast(inst)->getDecl(); } + llvm_unreachable("covered switch"); } Optional getVarInfo() const { @@ -298,13 +291,27 @@ struct DebugVarCarryingInst { llvm_unreachable("Invalid?!"); case Kind::DebugValue: return cast(inst)->getVarInfo(); - case Kind::DebugValueAddr: - return cast(inst)->getVarInfo(); case Kind::AllocStack: return cast(inst)->getVarInfo(); case Kind::AllocBox: return cast(inst)->getVarInfo(); } + llvm_unreachable("covered switch"); + } + + void setDebugVarScope(const SILDebugScope *NewDS) { + switch (kind) { + case Kind::Invalid: + llvm_unreachable("Invalid?!"); + case Kind::DebugValue: + cast(inst)->setDebugVarScope(NewDS); + break; + case Kind::AllocStack: + cast(inst)->setDebugVarScope(NewDS); + break; + case Kind::AllocBox: + llvm_unreachable("Not implemented"); + } } }; diff --git a/include/swift/SIL/DynamicCasts.h b/include/swift/SIL/DynamicCasts.h index d98d63616b87c..8b9e0a562f3b6 100644 --- a/include/swift/SIL/DynamicCasts.h +++ b/include/swift/SIL/DynamicCasts.h @@ -168,6 +168,7 @@ struct SILDynamicCastInst { return SILDynamicCastInst(cast(inst)); #include "swift/SIL/SILNodes.def" } + llvm_unreachable("covered switch"); } SILDynamicCastKind getKind() const { @@ -359,6 +360,7 @@ struct SILDynamicCastInst { case SILDynamicCastKind::UnconditionalCheckedCastValueInst: return cast(inst)->getSourceLoweredType(); } + llvm_unreachable("covered switch"); } CanType getTargetFormalType() const { diff --git a/include/swift/SIL/FormalLinkage.h b/include/swift/SIL/FormalLinkage.h index 512046b40a6c9..d1530b190a05e 100644 --- a/include/swift/SIL/FormalLinkage.h +++ b/include/swift/SIL/FormalLinkage.h @@ -43,6 +43,7 @@ enum class FormalLinkage { }; FormalLinkage getDeclLinkage(const ValueDecl *decl); +FormalLinkage getTypeLinkage(CanType formalType); SILLinkage getSILLinkage(FormalLinkage linkage, ForDefinition_t forDefinition); SILLinkage diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index 933a981f4cdf0..02e2dcb3a79ab 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -27,10 +27,6 @@ SILValue getUnderlyingObject(SILValue V); SILValue getUnderlyingObjectStopAtMarkDependence(SILValue V); -/// Given an address look through address to address projections and indexing -/// insts. -SILValue getUnderlyingObjectStoppingAtObjectToAddrProjections(SILValue v); - SILValue stripSinglePredecessorArgs(SILValue V); /// Return the underlying SILValue after stripping off all casts from the diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index 77fe0ee5a57b7..c57ed3411da00 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -44,7 +44,7 @@ /// 3. getAccessBase(): Find the ultimate base of any address corresponding to /// the accessed object, regardless of whether the address is nested within /// access scopes, and regardless of any storage casts. This returns either an -/// address type, pointer type, or box type, but never a reference type. +/// address or pointer type, but never a reference or box type. /// Each object's property or its tail storage is separately accessed. /// /// For better identification an access base, use @@ -166,6 +166,11 @@ SILValue getAccessScope(SILValue address); /// return the same base address, then they must also have the same storage. SILValue getAccessBase(SILValue address); +/// Find the root of a reference, which may be a non-trivial type, box type, or +/// BridgeObject. This is guaranteed to be consistent with +/// AccessedStorage::getRoot() and AccessPath::getRoot(). +SILValue findReferenceRoot(SILValue ref); + /// Return true if \p address points to a let-variable. /// /// let-variables are only written during let-variable initialization, which is @@ -237,7 +242,8 @@ enum class AccessUseType { Exact, Inner, Overlapping }; /// represent any arbitrary base address--it must at least been proven not to /// correspond to any class or global variable access, unless it's nested within /// another access to the same object. So, Unidentified can overlap with -/// Class/Global access, but it cannot be the only formal access to that memory. +/// Class/Global access because it was derived from another Class/Global access, +/// but Unidentified can never be the only formal access to that memory. /// /// An *invalid* AccessedStorage object is Unidentified and associated with an /// invalid SILValue. This signals that analysis has failed to recognize an @@ -268,7 +274,7 @@ class AccessedStorage { Global, Class, Tail, - Argument, + Argument, // Address or RawPointer argument Yield, Nested, Unidentified, @@ -280,22 +286,11 @@ class AccessedStorage { // Give object tail storage a fake large property index for convenience. static constexpr unsigned TailIndex = std::numeric_limits::max(); - /// Directly create an AccessedStorage for class or tail property access. - static AccessedStorage forClass(SILValue object, unsigned propertyIndex) { - AccessedStorage storage; - if (propertyIndex == TailIndex) - storage.initKind(Tail); - else - storage.initKind(Class, propertyIndex); - storage.value = object; - return storage; - } - /// Return an AccessedStorage value that best identifies a formally accessed /// variable pointed to by \p sourceAddress, looking through any nested /// formal accesses to find the underlying storage. /// - /// \p sourceAddress may be an address, pointer, or box type. + /// \p sourceAddress may be an address type or Builtin.RawPointer. /// /// If \p sourceAddress is within a formal access scope, which does not have /// "Unsafe" enforcement, then this always returns valid storage. @@ -308,7 +303,7 @@ class AccessedStorage { /// Return an AccessedStorage object that identifies formal access scope that /// immediately encloses \p sourceAddress. /// - /// \p sourceAddress may be an address, pointer, or box type. + /// \p sourceAddress may be an address type or Builtin.RawPointer. /// /// If \p sourceAddress is within a formal access scope, this always returns a /// valid "Nested" storage value. @@ -317,6 +312,14 @@ class AccessedStorage { /// the formal storage if possible, otherwise returning invalid storage. static AccessedStorage computeInScope(SILValue sourceAddress); + /// Create storage for the tail elements of \p object. + static AccessedStorage forObjectTail(SILValue object) { + AccessedStorage storage; + storage.initKind(Tail, TailIndex); + storage.value = findReferenceRoot(object); + return storage; + } + protected: // Checking the storage kind is far more common than other fields. Make sure // it can be byte load with no shift. @@ -389,7 +392,7 @@ class AccessedStorage { SILGlobalVariable *global; }; - void initKind(Kind k, unsigned elementIndex = InvalidElementIndex) { + void initKind(Kind k, unsigned elementIndex) { Bits.opaqueBits = 0; Bits.AccessedStorage.kind = k; Bits.AccessedStorage.elementIndex = elementIndex; @@ -401,7 +404,7 @@ class AccessedStorage { } public: - AccessedStorage() : value() { initKind(Unidentified); } + AccessedStorage() : value() { initKind(Unidentified, InvalidElementIndex); } AccessedStorage(SILValue base, Kind kind); @@ -418,6 +421,7 @@ class AccessedStorage { SILValue getValue() const { assert(getKind() != Global && getKind() != Class && getKind() != Tail); + assert(value && "Invalid storage has an invalid value"); return value; } @@ -436,7 +440,9 @@ class AccessedStorage { return global; } - bool isReference() const { return getKind() == Class || getKind() == Tail; } + bool isReference() const { + return getKind() == Box || getKind() == Class || getKind() == Tail; + } SILValue getObject() const { assert(isReference()); @@ -447,6 +453,15 @@ class AccessedStorage { return getElementIndex(); } + /// Return a new AccessedStorage for Class/Tail/Box access based on + /// existing storage and a new object. + AccessedStorage transformReference(SILValue object) const { + AccessedStorage storage; + storage.initKind(getKind(), getElementIndex()); + storage.value = findReferenceRoot(object); + return storage; + } + /// Return the address or reference root that the storage was based /// on. Returns an invalid SILValue for globals or invalid storage. SILValue getRoot() const { @@ -457,13 +472,14 @@ class AccessedStorage { case AccessedStorage::Argument: case AccessedStorage::Yield: case AccessedStorage::Unidentified: - return getValue(); // Can be invalid for Unidentified storage. + return getValue(); case AccessedStorage::Global: return SILValue(); case AccessedStorage::Class: case AccessedStorage::Tail: return getObject(); } + llvm_unreachable("covered switch"); } /// Visit all access roots. If any roots are visited then the original memory @@ -484,7 +500,7 @@ class AccessedStorage { /// This compares only the AccessedStorage base class bits, ignoring the /// subclass bits. It is used for hash lookup equality, so it should not /// perform any additional lookups or dereference memory outside itself. - bool hasIdenticalBase(const AccessedStorage &other) const { + bool hasIdenticalStorage(const AccessedStorage &other) const { if (getKind() != other.getKind()) return false; @@ -510,6 +526,7 @@ class AccessedStorage { bool isLocal() const { switch (getKind()) { case Box: + return isa(value); case Stack: return true; case Global: @@ -537,6 +554,7 @@ class AccessedStorage { bool isUniquelyIdentified() const { switch (getKind()) { case Box: + return isa(value); case Stack: case Global: return true; @@ -608,7 +626,7 @@ class AccessedStorage { template bool isDistinctFrom(const AccessedStorage &other) const { if ((this->*IsUniqueFn)()) { - if ((other.*IsUniqueFn)() && !hasIdenticalBase(other)) + if ((other.*IsUniqueFn)() && !hasIdenticalStorage(other)) return true; if (other.isObjectAccess()) @@ -685,11 +703,14 @@ template <> struct DenseMapInfo { static unsigned getHashValue(swift::AccessedStorage storage) { switch (storage.getKind()) { + case swift::AccessedStorage::Unidentified: + if (!storage) + return DenseMapInfo::getHashValue(swift::SILValue()); + LLVM_FALLTHROUGH; case swift::AccessedStorage::Box: case swift::AccessedStorage::Stack: case swift::AccessedStorage::Nested: case swift::AccessedStorage::Yield: - case swift::AccessedStorage::Unidentified: return DenseMapInfo::getHashValue(storage.getValue()); case swift::AccessedStorage::Argument: return storage.getParamIndex(); @@ -705,7 +726,7 @@ template <> struct DenseMapInfo { } static bool isEqual(swift::AccessedStorage LHS, swift::AccessedStorage RHS) { - return LHS.hasIdenticalBase(RHS); + return LHS.hasIdenticalStorage(RHS); } }; @@ -943,8 +964,9 @@ class AccessPath { bool operator==(AccessPath other) const { return - storage.hasIdenticalBase(other.storage) && pathNode == other.pathNode && - offset == other.offset; + storage.hasIdenticalStorage(other.storage) + && pathNode == other.pathNode + && offset == other.offset; } bool operator!=(AccessPath other) const { return !(*this == other); } @@ -1006,15 +1028,19 @@ class AccessPath { // recover the def-use chain for a specific global_addr or ref_element_addr. struct AccessPathWithBase { AccessPath accessPath; - // The address-type value that is the base of the formal access. For - // class storage, it is the ref_element_addr. For global storage it is the - // global_addr or initializer apply. For other storage, it is the same as - // accessPath.getRoot(). + // The address-type value that is the base of the formal access. For class + // storage, it is the ref_element_addr; for box storage, the project_box; for + // global storage the global_addr or initializer apply. For other + // storage, it is the same as accessPath.getRoot(). // - // Note: base may be invalid for global_addr -> address_to_pointer -> phi - // patterns, while the accessPath is still valid. + // Note: base may be invalid for phi patterns, even though the accessPath is + // valid because we don't currently keep track of multiple bases. Multiple + // bases for the same storage can happen with global_addr, ref_element_addr, + // ref_tail_addr, and project_box. // - // FIXME: add a structural requirement to SIL so base is always valid in OSSA. + // FIXME: add a structural requirement to SIL/OSSA so valid storage has + // a single base. For most cases, it is as simple by sinking the + // projection. For index_addr, it may require hoisting ref_tail_addr. SILValue base; /// Compute the access path at \p address, and record the access base. This @@ -1219,8 +1245,8 @@ void checkSwitchEnumBlockArg(SILPhiArgument *arg); /// This is not a member of AccessedStorage because it only makes sense to use /// in SILGen before access markers are emitted, or when verifying access /// markers. -bool isPossibleFormalAccessBase(const AccessedStorage &storage, - SILFunction *F); +bool isPossibleFormalAccessStorage(const AccessedStorage &storage, + SILFunction *F); /// Perform a RAUW operation on begin_access with it's own source operand. /// Then erase the begin_access and all associated end_access instructions. @@ -1307,14 +1333,7 @@ inline bool isAccessedStorageCast(SingleValueInstruction *svi) { case SILInstructionKind::MarkUninitializedInst: case SILInstructionKind::UncheckedAddrCastInst: case SILInstructionKind::MarkDependenceInst: - // Look through a project_box to identify the underlying alloc_box as the - // accesed object. It must be possible to reach either the alloc_box or the - // containing enum in this loop, only looking through simple value - // propagation such as copy_value and begin_borrow. - case SILInstructionKind::ProjectBoxInst: - case SILInstructionKind::ProjectBlockStorageInst: case SILInstructionKind::CopyValueInst: - case SILInstructionKind::BeginBorrowInst: // Casting to RawPointer does not affect the AccessPath. When converting // between address types, they must be layout compatible (with truncation). case SILInstructionKind::AddressToPointerInst: @@ -1364,7 +1383,7 @@ class AccessUseDefChainVisitor { Result visitArgumentAccess(SILFunctionArgument *arg) { return asImpl().visitBase(arg, AccessedStorage::Argument); } - Result visitBoxAccess(AllocBoxInst *box) { + Result visitBoxAccess(ProjectBoxInst *box) { return asImpl().visitBase(box, AccessedStorage::Box); } /// \p global may be either a GlobalAddrInst or the ApplyInst for a global @@ -1412,9 +1431,8 @@ Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { // MARK: Handle immediately-identifiable instructions. - // An AllocBox is a fully identified memory location. - case ValueKind::AllocBoxInst: - return asImpl().visitBoxAccess(cast(sourceAddr)); + case ValueKind::ProjectBoxInst: + return asImpl().visitBoxAccess(cast(sourceAddr)); // An AllocStack is a fully identified memory location, which may occur // after inlining code already subjected to stack promotion. @@ -1589,6 +1607,11 @@ class AccessUseDefChainCloner return cloneProjection(cast, sourceOper); } + SILValue visitNestedAccess(BeginAccessInst *access) { + // The cloner does not currently know how to handle begin_access + return SILValue(); + } + SILValue visitAccessProjection(SingleValueInstruction *projectedAddr, Operand *sourceOper) { return cloneProjection(projectedAddr, sourceOper); diff --git a/include/swift/SIL/Notifications.h b/include/swift/SIL/Notifications.h index 91ab29c2c5bff..f7ecd87024c70 100644 --- a/include/swift/SIL/Notifications.h +++ b/include/swift/SIL/Notifications.h @@ -239,27 +239,6 @@ class DeserializationNotificationHandlerSet final void didDeserialize(ModuleDecl *mod, SILDefaultWitnessTable *wtable) override; }; - -/// A protocol (or interface) for handling value deletion notifications. -/// -/// This class is used as a base class for any class that need to accept -/// instruction deletion notification messages. This is used by passes and -/// analysis that need to invalidate data structures that contain pointers. -/// This is similar to LLVM's ValueHandle. -struct DeleteNotificationHandler { - DeleteNotificationHandler() { } - virtual ~DeleteNotificationHandler() {} - - /// Handle the invalidation message for the value \p Value. - virtual void handleDeleteNotification(SILNode *value) { } - - /// Returns True if the pass, analysis or other entity wants to receive - /// notifications. This callback is called once when the class is being - /// registered, and not once per notification. Entities that implement - /// this callback should always return a constant answer (true/false). - virtual bool needsNotifications() { return false; } -}; - } // namespace swift #endif diff --git a/include/swift/SIL/OptimizationRemark.h b/include/swift/SIL/OptimizationRemark.h index 47a74d1e72165..8f8fd037579c3 100644 --- a/include/swift/SIL/OptimizationRemark.h +++ b/include/swift/SIL/OptimizationRemark.h @@ -153,6 +153,7 @@ enum class SourceLocInferenceBehavior : unsigned { BackwardsThenForwards = BackwardScan | ForwardScan2nd, ForwardScanAlwaysInfer = ForwardScan | AlwaysInfer, BackwardScanAlwaysInfer = BackwardScan | AlwaysInfer, + BackwardThenForwardAlwaysInfer = BackwardScan | ForwardScan2nd | AlwaysInfer, }; inline SourceLocInferenceBehavior operator&(SourceLocInferenceBehavior lhs, diff --git a/include/swift/SIL/OwnershipUtils.h b/include/swift/SIL/OwnershipUtils.h index 94efc3b8d2585..5dd9dc012d5d1 100644 --- a/include/swift/SIL/OwnershipUtils.h +++ b/include/swift/SIL/OwnershipUtils.h @@ -255,6 +255,9 @@ struct BorrowedValue; /// borrowed and thus the incoming value must implicitly be borrowed until the /// user's corresponding end scope instruction. /// +/// Invariant: For a given operand, BorrowingOperand is valid iff +/// it has OperandOwnership::Borrow or OperandOwnership::Reborrow. +/// /// NOTE: We do not require that the guaranteed scope be represented by a /// guaranteed value in the same function: see begin_apply. In such cases, we /// require instead an end_* instruction to mark the end of the scope's region. @@ -263,7 +266,16 @@ struct BorrowingOperand { BorrowingOperandKind kind; BorrowingOperand(Operand *op) - : op(op), kind(BorrowingOperandKind::get(op->getUser()->getKind())) {} + : op(op), kind(BorrowingOperandKind::get(op->getUser()->getKind())) { + auto ownership = op->getOperandOwnership(); + if (ownership != OperandOwnership::Borrow + && ownership != OperandOwnership::Reborrow) { + // consuming applies and branch arguments are not borrowing operands. + kind = BorrowingOperandKind::Invalid; + return; + } + assert(kind != BorrowingOperandKind::Invalid && "missing case"); + } BorrowingOperand(const BorrowingOperand &other) : op(other.op), kind(other.kind) {} BorrowingOperand &operator=(const BorrowingOperand &other) { @@ -279,23 +291,7 @@ struct BorrowingOperand { const Operand *operator->() const { return op; } Operand *operator->() { return op; } - operator bool() const { return kind != BorrowingOperandKind::Invalid && op; } - - /// If \p op is a borrow introducing operand return it after doing some - /// checks. - static BorrowingOperand get(Operand *op) { - auto *user = op->getUser(); - auto kind = BorrowingOperandKind::get(user->getKind()); - if (!kind) - return {nullptr, kind}; - return {op, kind}; - } - - /// If \p op is a borrow introducing operand return it after doing some - /// checks. - static BorrowingOperand get(const Operand *op) { - return get(const_cast(op)); - } + operator bool() const { return kind != BorrowingOperandKind::Invalid; } /// If this borrowing operand results in the underlying value being borrowed /// over a region of code instead of just for a single instruction, visit @@ -337,8 +333,14 @@ struct BorrowingOperand { llvm_unreachable("Covered switch isn't covered?!"); } - /// Return true if the user instruction introduces a borrow scope? This is - /// true for both reborrows and nested borrows. + /// Return true if the user instruction defines a borrowed value that + /// introduces a borrow scope and therefore may be reborrowed. This is true + /// for both reborrows and nested borrows. + /// + /// Note that begin_apply does create a borrow scope, and may define + /// guaranteed value within that scope. The difference is that those yielded + /// values do not themselves introduce a borrow scope. In other words, they + /// cannot be reborrowed. /// /// If true, the visitBorrowIntroducingUserResults() can be called to acquire /// each BorrowedValue that introduces a new borrow scopes. @@ -589,7 +591,7 @@ struct BorrowedValue { &foundReborrows) const { bool foundAnyReborrows = false; for (auto *op : value->getUses()) { - if (auto borrowingOperand = BorrowingOperand::get(op)) { + if (auto borrowingOperand = BorrowingOperand(op)) { if (borrowingOperand.isReborrow()) { foundReborrows.push_back( {value->getParentBlock(), op->getOperandNumber()}); @@ -647,6 +649,7 @@ class InteriorPointerOperandKind { RefElementAddr, RefTailAddr, OpenExistentialBox, + ProjectBox, StoreBorrow, }; @@ -674,6 +677,8 @@ class InteriorPointerOperandKind { return Kind::RefTailAddr; case SILInstructionKind::OpenExistentialBoxInst: return Kind::OpenExistentialBox; + case SILInstructionKind::ProjectBoxInst: + return Kind::ProjectBox; case SILInstructionKind::StoreBorrowInst: return Kind::StoreBorrow; } @@ -692,6 +697,8 @@ class InteriorPointerOperandKind { return Kind::RefTailAddr; case ValueKind::OpenExistentialBoxInst: return Kind::OpenExistentialBox; + case ValueKind::ProjectBoxInst: + return Kind::ProjectBox; case ValueKind::StoreBorrowInst: return Kind::StoreBorrow; } @@ -720,6 +727,8 @@ struct InteriorPointerOperand { return kind != InteriorPointerOperandKind::Invalid && operand; } + SILInstruction *getUser() const { return operand->getUser(); } + /// If \p op has a user that is an interior pointer, return a valid /// value. Otherwise, return None. static InteriorPointerOperand get(Operand *op) { @@ -745,6 +754,7 @@ struct InteriorPointerOperand { case InteriorPointerOperandKind::RefElementAddr: case InteriorPointerOperandKind::RefTailAddr: case InteriorPointerOperandKind::OpenExistentialBox: + case InteriorPointerOperandKind::ProjectBox: case InteriorPointerOperandKind::StoreBorrow: { // Ok, we have a valid instruction. Return the relevant operand. auto *op = @@ -752,6 +762,7 @@ struct InteriorPointerOperand { return InteriorPointerOperand(op, kind); } } + llvm_unreachable("covered switch"); } /// Return the end scope of all borrow introducers of the parent value of this @@ -785,6 +796,8 @@ struct InteriorPointerOperand { return cast(operand->getUser()); case InteriorPointerOperandKind::OpenExistentialBox: return cast(operand->getUser()); + case InteriorPointerOperandKind::ProjectBox: + return cast(operand->getUser()); case InteriorPointerOperandKind::StoreBorrow: return cast(operand->getUser()); } @@ -824,6 +837,25 @@ struct InteriorPointerOperand { : operand(op), kind(kind) {} }; +/// Utility to check if an address may originate from a borrowed value. If so, +/// then uses of the address cannot be replaced without ensuring that they are +/// also within the same scope. +/// +/// If mayBeBorrowed is false, then there is no enclosing borrow scope and +/// interiorPointerOp is irrelevant. +/// +/// If mayBeBorrowed is true, then interiorPointerOp refers to the operand that +/// converts a non-address value into the address from which the contructor's +/// address is derived. If the best-effort to find an InteriorPointerOperand +/// fails, then interiorPointerOp remains invalid, and clients must be +/// conservative. +struct BorrowedAddress { + bool mayBeBorrowed = true; + InteriorPointerOperand interiorPointerOp; + + BorrowedAddress(SILValue address); +}; + class OwnedValueIntroducerKind { public: enum Kind : uint8_t { @@ -1035,6 +1067,7 @@ struct OwnedValueIntroducer { case OwnedValueIntroducerKind::AllocRefInit: return false; } + llvm_unreachable("covered switch"); } bool operator==(const OwnedValueIntroducer &other) const { @@ -1076,10 +1109,10 @@ void findTransitiveReborrowBaseValuePairs( BorrowingOperand initialScopeOperand, SILValue origBaseValue, function_ref visitReborrowBaseValuePair); -/// Given a begin_borrow visit all end_borrow users of the borrow or its -/// reborrows. +/// Given a begin of a borrow scope, visit all end_borrow users of the borrow or +/// its reborrows. void visitTransitiveEndBorrows( - BeginBorrowInst *borrowInst, + BorrowedValue beginBorrow, function_ref visitEndBorrow); } // namespace swift diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index d80b253cef181..c0d57a0c0aece 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -859,7 +859,7 @@ class ProjectionTree { ProjectionTree &operator=(ProjectionTree &&) = default; /// Compute liveness and use information in this projection tree using Base. - /// All debug instructions (debug_value, debug_value_addr) are ignored. + /// All debug_value instructions are ignored. void computeUsesAndLiveness(SILValue Base); /// Create a root SILValue iout of the given leaf node values by walking on diff --git a/include/swift/SIL/SILArgument.h b/include/swift/SIL/SILArgument.h index c106450f982d9..724fd6f830abd 100644 --- a/include/swift/SIL/SILArgument.h +++ b/include/swift/SIL/SILArgument.h @@ -183,7 +183,7 @@ class SILArgument : public ValueBase { /// Return the terminator instruction for which this argument is a result, /// otherwise return nullptr. - TermInst *getTerminatorForResultArg() const; + TermInst *getTerminatorForResult() const; /// Return the SILArgumentKind of this argument. SILArgumentKind getKind() const { @@ -196,6 +196,10 @@ class SILArgument : public ValueBase { } }; +inline SILArgument *castToArgument(SwiftObject argument) { + return static_cast(argument); +} + class SILPhiArgument : public SILArgument { friend class SILBasicBlock; @@ -295,7 +299,7 @@ class SILPhiArgument : public SILArgument { /// Return the terminator instruction for which this argument is a result, /// otherwise return nullptr. - TermInst *getTerminatorForResultArg() const; + TermInst *getTerminatorForResult() const; static bool classof(const SILInstruction *) = delete; static bool classof(const SILUndef *) = delete; @@ -441,10 +445,10 @@ inline TermInst *SILArgument::getSingleTerminator() const { llvm_unreachable("Covered switch is not covered?!"); } -inline TermInst *SILArgument::getTerminatorForResultArg() const { +inline TermInst *SILArgument::getTerminatorForResult() const { switch (getKind()) { case SILArgumentKind::SILPhiArgument: - return cast(this)->getTerminatorForResultArg(); + return cast(this)->getTerminatorForResult(); case SILArgumentKind::SILFunctionArgument: return nullptr; } diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index 3168541b2c756..f258a1455ef5d 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -22,6 +22,7 @@ #include "swift/SIL/SILArgumentArrayRef.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILArgument.h" +#include "swift/SIL/SwiftObjectHeader.h" #include "llvm/ADT/TinyPtrVector.h" namespace swift { @@ -31,13 +32,16 @@ class SILArgument; class SILPrintContext; class SILBasicBlock : -public llvm::ilist_node, public SILAllocated { +public llvm::ilist_node, public SILAllocated, +public SwiftObjectHeader { friend class SILSuccessor; friend class SILFunction; friend class SILGlobalVariable; template friend class BasicBlockData; friend class BasicBlockBitfield; + static SwiftMetatype registeredMetatype; + public: using InstListType = llvm::iplist; private: @@ -47,7 +51,7 @@ public llvm::ilist_node, public SILAllocated { /// PrevList - This is a list of all of the terminator operands that are /// branching to this block, forming the predecessor list. This is /// automatically managed by the SILSuccessor class. - SILSuccessor *PredList; + SILSuccessor *PredList = nullptr; /// This is the list of basic block arguments for this block. /// A TinyPtrVector is the right choice, because ~98% of blocks have 0 or 1 @@ -82,14 +86,18 @@ public llvm::ilist_node, public SILAllocated { uint64_t lastInitializedBitfieldID = 0; friend struct llvm::ilist_traits; - SILBasicBlock() : Parent(nullptr) {} - void operator=(const SILBasicBlock &) = delete; - void operator delete(void *Ptr, size_t) = delete; + SILBasicBlock(); + SILBasicBlock(SILFunction *parent); - SILBasicBlock(SILFunction *parent) : Parent(parent), PredList(nullptr) { } + void operator=(const SILBasicBlock &) = delete; + void operator delete(void *Ptr, size_t) = delete; public: + static void registerBridgedMetatype(SwiftMetatype metatype) { + registeredMetatype = metatype; + } + ~SILBasicBlock(); /// Gets the ID (= index in the function's block list) of the block. @@ -127,7 +135,10 @@ public llvm::ilist_node, public SILAllocated { void push_back(SILInstruction *I); void push_front(SILInstruction *I); void remove(SILInstruction *I); - iterator erase(SILInstruction *I); + void erase(SILInstruction *I); + void erase(SILInstruction *I, SILModule &module); + + void eraseAllInstructions(SILModule &module); SILInstruction &back() { return InstList.back(); } const SILInstruction &back() const { @@ -439,8 +450,6 @@ public llvm::ilist_node, public SILAllocated { I.dropAllReferences(); } - void eraseInstructions(); - private: friend class SILArgument; diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h new file mode 100644 index 0000000000000..6d02c63d57f57 --- /dev/null +++ b/include/swift/SIL/SILBridging.h @@ -0,0 +1,234 @@ +//===--- SILBridging.h - header for the swift SILBridging module ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_SILBRIDGING_H +#define SWIFT_SIL_SILBRIDGING_H + +#include "BridgedSwiftObject.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const unsigned char * _Nullable data; + size_t length; +} BridgedStringRef; + +typedef struct { + const unsigned char * _Nonnull data; + size_t numElements; +} BridgedArrayRef; + +enum { + BridgedOperandSize = 4 * sizeof(uintptr_t), + BridgedSuccessorSize = 4 * sizeof(uintptr_t) + sizeof(uint64_t) +}; + +typedef struct { + void * _Nullable data; +} BridgedSlab; + +enum { + BridgedSlabCapacity = 64 * sizeof(uintptr_t) +}; + +enum ChangeNotificationKind { + instructionsChanged, + callsChanged, + branchesChanged +}; + +typedef struct { + const void * _Nonnull opaqueCtxt; +} BridgedPassContext; + +typedef struct { + void * _Null_unspecified word0; + void * _Null_unspecified word1; + void * _Null_unspecified word2; +} BridgedLocation; + +typedef struct { + void * _Nullable typePtr; +} BridgedType; + +typedef struct { + const void * _Nullable data; + size_t count; +} BridgedValueArray; + +typedef struct { + const void * _Nonnull op; +} BridgedOperand; + +typedef struct { + const void * _Nullable op; +} OptionalBridgedOperand; + +typedef struct { + const void * _Nonnull succ; +} BridgedSuccessor; + +typedef struct { + const void * _Nullable succ; +} OptionalBridgedSuccessor; + +typedef struct { + SwiftObject obj; +} BridgedFunction; + +typedef struct { + SwiftObject obj; +} BridgedGlobalVar; + +typedef struct { + SwiftObject obj; +} BridgedBasicBlock; + +typedef struct { + OptionalSwiftObject obj; +} OptionalBridgedBasicBlock; + +typedef struct { + SwiftObject obj; +} BridgedArgument; + +typedef struct { + OptionalSwiftObject obj; +} OptionalBridgedArgument; + +typedef struct { + SwiftObject obj; +} BridgedNode; + +typedef struct { + SwiftObject obj; +} BridgedValue; + +typedef struct { + SwiftObject obj; +} BridgedInstruction; + +typedef struct { + OptionalSwiftObject obj; +} OptionalBridgedInstruction; + +typedef struct { + SwiftObject obj; +} BridgedMultiValueResult; + +// Must be in sync with SILInstruction::MemoryBehavior +// TODO: do this less hacky. +typedef enum { + NoneBehavior, + MayReadBehavior, + MayWriteBehavior, + MayReadWriteBehavior, + MayHaveSideEffectsBehavior +} BridgedMemoryBehavior; + +typedef long SwiftInt; + +void registerBridgedClass(BridgedStringRef className, SwiftMetatype metatype); + +void freeBridgedStringRef(BridgedStringRef str); + +void PassContext_notifyChanges(BridgedPassContext passContext, + enum ChangeNotificationKind changeKind); +void PassContext_eraseInstruction(BridgedPassContext passContext, + BridgedInstruction inst); +BridgedSlab PassContext_getNextSlab(BridgedSlab slab); +BridgedSlab PassContext_allocSlab(BridgedPassContext passContext, + BridgedSlab afterSlab); +BridgedSlab PassContext_freeSlab(BridgedPassContext passContext, + BridgedSlab slab); + +BridgedStringRef SILFunction_getName(BridgedFunction function); +BridgedStringRef SILFunction_debugDescription(BridgedFunction function); +OptionalBridgedBasicBlock SILFunction_firstBlock(BridgedFunction function); +OptionalBridgedBasicBlock SILFunction_lastBlock(BridgedFunction function); + +BridgedStringRef SILGlobalVariable_getName(BridgedGlobalVar global); +BridgedStringRef SILGlobalVariable_debugDescription(BridgedGlobalVar global); + +OptionalBridgedBasicBlock SILBasicBlock_next(BridgedBasicBlock block); +OptionalBridgedBasicBlock SILBasicBlock_previous(BridgedBasicBlock block); +BridgedFunction SILBasicBlock_getFunction(BridgedBasicBlock block); +BridgedStringRef SILBasicBlock_debugDescription(BridgedBasicBlock block); +OptionalBridgedInstruction SILBasicBlock_firstInst(BridgedBasicBlock block); +OptionalBridgedInstruction SILBasicBlock_lastInst(BridgedBasicBlock block); +SwiftInt SILBasicBlock_getNumArguments(BridgedBasicBlock block); +BridgedArgument SILBasicBlock_getArgument(BridgedBasicBlock block, SwiftInt index); +OptionalBridgedSuccessor SILBasicBlock_getFirstPred(BridgedBasicBlock block); +OptionalBridgedSuccessor SILSuccessor_getNext(BridgedSuccessor succ); +BridgedBasicBlock SILSuccessor_getTargetBlock(BridgedSuccessor succ); +BridgedInstruction SILSuccessor_getContainingInst(BridgedSuccessor succ); + +BridgedValue Operand_getValue(BridgedOperand); +OptionalBridgedOperand Operand_nextUse(BridgedOperand); +BridgedInstruction Operand_getUser(BridgedOperand); + +BridgedStringRef SILNode_debugDescription(BridgedNode node); +OptionalBridgedOperand SILValue_firstUse(BridgedValue value); +BridgedType SILValue_getType(BridgedValue value); + +SwiftInt SILType_isAddress(BridgedType); + +BridgedBasicBlock SILArgument_getParent(BridgedArgument argument); + +OptionalBridgedInstruction SILInstruction_next(BridgedInstruction inst); +OptionalBridgedInstruction SILInstruction_previous(BridgedInstruction inst); +BridgedBasicBlock SILInstruction_getParent(BridgedInstruction inst); +BridgedArrayRef SILInstruction_getOperands(BridgedInstruction inst); +void SILInstruction_setOperand(BridgedInstruction inst, SwiftInt index, + BridgedValue value); +BridgedLocation SILInstruction_getLocation(BridgedInstruction inst); +BridgedMemoryBehavior SILInstruction_getMemBehavior(BridgedInstruction inst); + +BridgedInstruction MultiValueInstResult_getParent(BridgedMultiValueResult result); +SwiftInt MultipleValueInstruction_getNumResults(BridgedInstruction inst); +BridgedMultiValueResult + MultipleValueInstruction_getResult(BridgedInstruction inst, SwiftInt index); + +BridgedArrayRef TermInst_getSuccessors(BridgedInstruction term); + +BridgedStringRef CondFailInst_getMessage(BridgedInstruction cfi); +BridgedGlobalVar GlobalAccessInst_getGlobal(BridgedInstruction globalInst); +SwiftInt TupleExtractInst_fieldIndex(BridgedInstruction tei); +SwiftInt TupleElementAddrInst_fieldIndex(BridgedInstruction teai); +SwiftInt StructExtractInst_fieldIndex(BridgedInstruction sei); +SwiftInt StructElementAddrInst_fieldIndex(BridgedInstruction seai); +SwiftInt EnumInst_caseIndex(BridgedInstruction ei); +SwiftInt UncheckedEnumDataInst_caseIndex(BridgedInstruction uedi); +SwiftInt RefElementAddrInst_fieldIndex(BridgedInstruction reai); +SwiftInt PartialApplyInst_numArguments(BridgedInstruction ai); +SwiftInt ApplyInst_numArguments(BridgedInstruction ai); +SwiftInt BeginApplyInst_numArguments(BridgedInstruction ai); +SwiftInt TryApplyInst_numArguments(BridgedInstruction ai); +BridgedBasicBlock BranchInst_getTargetBlock(BridgedInstruction bi); +SwiftInt SwitchEnumInst_getNumCases(BridgedInstruction se); +SwiftInt SwitchEnumInst_getCaseIndex(BridgedInstruction se, SwiftInt idx); + +BridgedInstruction SILBuilder_createBuiltinBinaryFunction( + BridgedInstruction insertionPoint, + BridgedLocation loc, BridgedStringRef name, + BridgedType operandType, BridgedType resultType, BridgedValueArray arguments); +BridgedInstruction SILBuilder_createCondFail(BridgedInstruction insertionPoint, + BridgedLocation loc, BridgedValue condition, BridgedStringRef messge); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/include/swift/SIL/SILBridgingUtils.h b/include/swift/SIL/SILBridgingUtils.h new file mode 100644 index 0000000000000..c96dea650ec3b --- /dev/null +++ b/include/swift/SIL/SILBridgingUtils.h @@ -0,0 +1,99 @@ +//===--- SILBridgingUtils.h - utilities for swift bridging ----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_SILBRIDGINGUTILS_H +#define SWIFT_SIL_SILBRIDGINGUTILS_H + +#include "swift/SIL/SILBridging.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILGlobalVariable.h" +#include "llvm/ADT/StringRef.h" + +#include + +namespace swift { + +inline BridgedStringRef getBridgedStringRef(llvm::StringRef str) { + return { (const unsigned char *)str.data(), str.size() }; +} + +inline StringRef getStringRef(BridgedStringRef str) { + return StringRef((const char *)str.data, str.length); +} + +/// Copies the string in an malloc'ed memory and the caller is responsible for +/// freeing it. +inline BridgedStringRef getCopiedBridgedStringRef(std::string str, + bool removeTrailingNewline = false) { + // A couple of mallocs are needed for passing a std::string to libswift. But + // it's currently only used or debug descriptions. So, its' maybe not so bad - + // for now. + // TODO: find a better way to pass std::strings to libswift. + StringRef strRef(str); + if (removeTrailingNewline) + strRef.consume_back("\n"); + llvm::MallocAllocator allocator; + StringRef copy = strRef.copy(allocator); + return getBridgedStringRef(copy); +} + +inline SILLocation getSILLocation(BridgedLocation loc) { + return reinterpret_cast(&loc)->getLocation(); +} + +inline RegularLocation getRegularLocation(BridgedLocation loc) { + return RegularLocation(getSILLocation(loc)); +} + +inline const SILDebugScope *getSILDebugScope(BridgedLocation loc) { + return reinterpret_cast(&loc)->getScope(); +} + +inline SILType getSILType(BridgedType ty) { + return SILType::getFromOpaqueValue(ty.typePtr); +} + +inline SILNode *castToSILNode(BridgedNode node) { + return static_cast(node.obj); +} + +inline SILValue castToSILValue(BridgedValue value) { + return static_cast(value.obj); +} + +inline SILType castToSILType(BridgedType type) { + return SILType::getFromOpaqueValue(type.typePtr); +} + +template I *castToInst(BridgedInstruction inst) { + return cast(static_cast(inst.obj)->castToInstruction()); +} + +inline SILBasicBlock *castToBasicBlock(BridgedBasicBlock block) { + return static_cast(block.obj); +} + +inline SILFunction *castToFunction(BridgedFunction function) { + return static_cast(function.obj); +} + +inline SILGlobalVariable *castToGlobal(BridgedGlobalVar global) { + return static_cast(global.obj); +} + +ArrayRef getSILValues(BridgedValueArray values, + SmallVectorImpl &storage); + +} // namespace swift + +#endif + diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 88ac2cfdad675..42c4bba51d8e8 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -123,6 +123,13 @@ class SILBuilder { setInsertionPoint(I); } + // Used by libswift bridging. + explicit SILBuilder(SILInstruction *I, const SILDebugScope *debugScope) + : TempContext(I->getFunction()->getModule()), + C(TempContext), F(I->getFunction()), CurDebugScope(debugScope) { + setInsertionPoint(I); + } + explicit SILBuilder(SILBasicBlock::iterator I, SmallVectorImpl *InsertedInstrs = 0) : SILBuilder(&*I, InsertedInstrs) {} @@ -706,10 +713,11 @@ class SILBuilder { LoadBorrowInst(getSILDebugLocation(Loc), LV)); } - BeginBorrowInst *createBeginBorrow(SILLocation Loc, SILValue LV) { + BeginBorrowInst *createBeginBorrow(SILLocation Loc, SILValue LV, + bool defined = false) { assert(!LV->getType().isAddress()); return insert(new (getModule()) - BeginBorrowInst(getSILDebugLocation(Loc), LV)); + BeginBorrowInst(getSILDebugLocation(Loc), LV, defined)); } /// Convenience function for creating a load_borrow on non-trivial values and @@ -919,10 +927,10 @@ class SILBuilder { DebugValueInst *createDebugValue(SILLocation Loc, SILValue src, SILDebugVariable Var, bool poisonRefs = false); - DebugValueAddrInst *createDebugValueAddr(SILLocation Loc, SILValue src, - SILDebugVariable Var); + DebugValueInst *createDebugValueAddr(SILLocation Loc, SILValue src, + SILDebugVariable Var); - /// Create a debug_value_addr if \p src is an address; a debug_value if not. + /// Create a debug_value according to the type of \p src SILInstruction *emitDebugDescription(SILLocation Loc, SILValue src, SILDebugVariable Var) { if (src->getType().isAddress()) @@ -2100,9 +2108,11 @@ class SILBuilder { Throws)); } - HopToExecutorInst *createHopToExecutor(SILLocation Loc, SILValue Actor) { + HopToExecutorInst *createHopToExecutor(SILLocation Loc, SILValue Actor, + bool mandatory) { return insert(new (getModule()) HopToExecutorInst(getSILDebugLocation(Loc), - Actor, hasOwnership())); + Actor, hasOwnership(), + mandatory)); } ExtractExecutorInst *createExtractExecutor(SILLocation Loc, SILValue Actor) { @@ -2622,6 +2632,11 @@ class SILBuilder { C.notifyInserted(TheInst); #ifndef NDEBUG + // If we are inserting into a specific function (rather than a block for a + // global_addr), verify that our instruction/the associated location are in + // sync. We don't care if an instruction is used in global_addr. + if (F) + TheInst->verifyDebugInfo(); TheInst->verifyOperandOwnership(); #endif } diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 1fa35ff60ee7f..0c2779898b47b 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -19,6 +19,7 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/DebugUtils.h" #include "swift/SIL/Dominance.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILDebugScope.h" @@ -245,6 +246,16 @@ class SILCloner : protected SILInstructionVisitor { registerOpenedExistentialRemapping(archetypeTy, replacementTy); } + // SILCloner will take care of debug scope on the instruction + // and this helper will remap the auxiliary debug scope too, if there is any. + void remapDebugVarInfo(DebugVarCarryingInst DbgVarInst) { + if (!DbgVarInst) + return; + auto VarInfo = DbgVarInst.getVarInfo(); + if (VarInfo && VarInfo->Scope) + DbgVarInst.setDebugVarScope(getOpScope(VarInfo->Scope)); + } + ProtocolConformanceRef getOpConformance(Type ty, ProtocolConformanceRef conformance) { // If we have open existentials to substitute, do so now. @@ -748,9 +759,11 @@ SILCloner::visitAllocStackInst(AllocStackInst *Inst) { Loc = MandatoryInlinedLocation::getAutoGeneratedLocation(); VarInfo = None; } - recordClonedInstruction(Inst, getBuilder().createAllocStack( - Loc, getOpType(Inst->getElementType()), - VarInfo, Inst->hasDynamicLifetime())); + auto *NewInst = + getBuilder().createAllocStack(Loc, getOpType(Inst->getElementType()), + VarInfo, Inst->hasDynamicLifetime()); + remapDebugVarInfo(DebugVarCarryingInst(NewInst)); + recordClonedInstruction(Inst, NewInst); } template @@ -1070,7 +1083,8 @@ void SILCloner::visitBeginBorrowInst(BeginBorrowInst *Inst) { recordClonedInstruction( Inst, getBuilder().createBeginBorrow(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()))); + getOpValue(Inst->getOperand()), + Inst->isDefined())); } template @@ -1232,28 +1246,13 @@ SILCloner::visitDebugValueInst(DebugValueInst *Inst) { return; // Since we want the debug info to survive, we do not remap the location here. + SILDebugVariable VarInfo = *Inst->getVarInfo(); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createDebugValue(Inst->getLoc(), - getOpValue(Inst->getOperand()), - *Inst->getVarInfo(), - Inst->poisonRefs())); -} -template -void -SILCloner::visitDebugValueAddrInst(DebugValueAddrInst *Inst) { - // We cannot inline/clone debug intrinsics without a scope. If they - // describe function arguments there is no way to determine which - // function they belong to. - if (!Inst->getDebugScope()) - return; - - // Do not remap the location for a debug Instruction. - SILValue OpValue = getOpValue(Inst->getOperand()); - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createDebugValueAddr(Inst->getLoc(), OpValue, - *Inst->getVarInfo())); + auto *NewInst = getBuilder().createDebugValue(Inst->getLoc(), + getOpValue(Inst->getOperand()), + VarInfo, Inst->poisonRefs()); + remapDebugVarInfo(DebugVarCarryingInst(NewInst)); + recordClonedInstruction(Inst, NewInst); } #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ @@ -3009,7 +3008,8 @@ ::visitHopToExecutorInst(HopToExecutorInst *Inst) { recordClonedInstruction(Inst, getBuilder().createHopToExecutor( getOpLocation(Inst->getLoc()), - getOpValue(Inst->getTargetExecutor()))); + getOpValue(Inst->getTargetExecutor()), + Inst->isMandatory())); } template diff --git a/include/swift/SIL/SILDebugInfoExpression.h b/include/swift/SIL/SILDebugInfoExpression.h new file mode 100644 index 0000000000000..c5d9b42f24912 --- /dev/null +++ b/include/swift/SIL/SILDebugInfoExpression.h @@ -0,0 +1,236 @@ +//===--- SILDebugInfoExpression.h - DIExpression for SIL --------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains types that model debug info expressions in SIL. Including +/// (debug info) operator and operand. +/// +//===----------------------------------------------------------------------===// +#ifndef SWIFT_SIL_DEBUGINFOEXPRESSION_H +#define SWIFT_SIL_DEBUGINFOEXPRESSION_H +#include "swift/AST/Decl.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/raw_ostream.h" + +namespace swift { +class TailAllocatedDebugVariable; + +/// Operator in a debug info expression +enum class SILDIExprOperator : unsigned { + INVALID = 0, + /// Dereferences the SSA value + Dereference, + /// Specifies that the SSA value is a fragment (sub-field) of the + /// associated source variable. This operator takes a single + /// VarDecl operand pointing to the field declaration. + /// Note that this directive can only appear at the end of an + /// expression. + Fragment +}; + +/// Represents a single component in a debug info expression. +/// Including operator and operand. +struct SILDIExprElement { + enum Kind { + /// A di-expression operator + OperatorKind, + /// An operand that has declaration type + DeclKind + }; + +private: + Kind OpKind; + + union { + SILDIExprOperator Operator; + Decl *Declaration; + }; + + explicit SILDIExprElement(Kind OpK) : OpKind(OpK) {} + +public: + Kind getKind() const { return OpKind; } + + SILDIExprOperator getAsOperator() const { + return OpKind == OperatorKind ? Operator : SILDIExprOperator::INVALID; + } + + Decl *getAsDecl() const { return OpKind == DeclKind ? Declaration : nullptr; } + + static SILDIExprElement createOperator(SILDIExprOperator Op) { + SILDIExprElement DIOp(OperatorKind); + DIOp.Operator = Op; + return DIOp; + } + + static SILDIExprElement createDecl(Decl *D) { + SILDIExprElement DIOp(DeclKind); + DIOp.Declaration = D; + return DIOp; + } +}; + +/// For a given SILDIExprOperator, provides information +/// like its textual name and operand types. +struct SILDIExprInfo { + StringRef OpText; + SmallVector OperandKinds; + + static const SILDIExprInfo *get(SILDIExprOperator Op); +}; + +/// A DIExpr operand is consisting of a SILDIExprOperator and +/// SILDIExprElement arguments following after. +struct SILDIExprOperand : public llvm::ArrayRef { + // Reuse all the ctors + using llvm::ArrayRef::ArrayRef; + + SILDIExprOperator getOperator() const { + assert(size() && "empty DIExpr operand"); + const SILDIExprElement &First = front(); + return First.getAsOperator(); + } + + size_t getNumArg() const { + assert(size() && "empty DIExpr operand"); + return size() - 1; + } + + llvm::ArrayRef args() const { + return drop_front(); + } +}; + +/// Represents a debug info expression in SIL +class SILDebugInfoExpression { + friend class TailAllocatedDebugVariable; + llvm::SmallVector Elements; + +public: + SILDebugInfoExpression() = default; + + explicit SILDebugInfoExpression(llvm::ArrayRef EL) + : Elements(EL.begin(), EL.end()) {} + + void clear() { Elements.clear(); } + + size_t getNumElements() const { return Elements.size(); } + + using iterator = typename decltype(Elements)::iterator; + using const_iterator = typename decltype(Elements)::const_iterator; + + iterator element_begin() { return Elements.begin(); } + iterator element_end() { return Elements.end(); } + + const_iterator element_begin() const { return Elements.begin(); } + const_iterator element_end() const { return Elements.end(); } + + llvm::iterator_range elements() { + return llvm::make_range(element_begin(), element_end()); + } + + llvm::iterator_range elements() const { + return llvm::make_range(element_begin(), element_end()); + } + + const SILDIExprElement &getElement(size_t index) const { + assert(index < Elements.size()); + return Elements[index]; + } + + void push_back(const SILDIExprElement &Element) { + Elements.push_back(Element); + } + + void appendElements(llvm::ArrayRef NewElements) { + if (NewElements.size()) + Elements.append(NewElements.begin(), NewElements.end()); + } + + void append(const SILDebugInfoExpression &Tail) { + appendElements(Tail.Elements); + } + + void prependElements(llvm::ArrayRef NewElements) { + Elements.insert(Elements.begin(), + NewElements.begin(), NewElements.end()); + } + + void eraseElement(const_iterator It) { + Elements.erase(It); + } + + /// The iterator for SILDIExprOperand + class op_iterator { + friend class SILDebugInfoExpression; + + SILDIExprOperand Current; + llvm::ArrayRef Remain; + + void increment(); + + explicit + op_iterator(llvm::ArrayRef Remain): Remain(Remain) { + increment(); + } + + public: + op_iterator() = default; + op_iterator(const op_iterator &) = default; + + const SILDIExprOperand &operator*() const { return Current; } + const SILDIExprOperand *operator->() const { return &Current; } + + // Pre increment + op_iterator &operator++() { + increment(); + return *this; + } + + // Post increment + op_iterator operator++(int) { + op_iterator This(*this); + increment(); + return This; + } + + bool operator==(const op_iterator &Other) const { + return (Current.empty() && Other.Current.empty()) || + (Current.data() == Other.Current.data() && + Current.size() == Other.Current.size()); + } + bool operator!=(const op_iterator &Other) const { + return !(Other == *this); + } + }; + + op_iterator operand_begin() const { + return op_iterator(Elements); + } + op_iterator operand_end() const { + return op_iterator(llvm::ArrayRef{}); + } + + llvm::iterator_range operands() const { + return llvm::make_range(operand_begin(), operand_end()); + } + + /// Return true if this expression is not empty + inline operator bool() const { return Elements.size(); } + + /// Create a op_fragment expression + static SILDebugInfoExpression createFragment(VarDecl *Field); +}; +} // end namespace swift +#endif diff --git a/include/swift/SIL/SILDebugScope.h b/include/swift/SIL/SILDebugScope.h index 0349ed6dbffc8..f167b57feed06 100644 --- a/include/swift/SIL/SILDebugScope.h +++ b/include/swift/SIL/SILDebugScope.h @@ -95,7 +95,7 @@ class SILDebugScope : public SILAllocated { }; /// Determine whether an instruction may not have a SILDebugScope. -bool maybeScopeless(SILInstruction &I); +bool maybeScopeless(const SILInstruction &inst); /// Knows how to make a deep copy of a debug scope. class ScopeCloner { diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 1a480602b03f7..b5d939eede36b 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -42,6 +42,7 @@ namespace swift { class AutoClosureExpr; class ASTContext; class ClassDecl; + class FileUnit; class SILFunctionType; enum IsSerialized_t : unsigned char; enum class SubclassScope : unsigned char; @@ -83,7 +84,14 @@ enum ForDefinition_t : bool { /// declaration, such as uncurry levels of a function, the allocating and /// initializing entry points of a constructor, etc. struct SILDeclRef { - using Loc = llvm::PointerUnion; + /// The type of AST node location being stored. + enum LocKind { + Decl, + Closure, + File + }; + using Loc = llvm::PointerUnion; /// Represents the "kind" of the SILDeclRef. For some Swift decls there /// are multiple SIL entry points, and the kind is used to distinguish them. @@ -144,14 +152,20 @@ struct SILDeclRef { /// References the function used to initialize a property wrapper storage /// instance from a projected value. PropertyWrapperInitFromProjectedValue, + + /// The main entry-point function. This may reference a SourceFile for a + /// top-level main, or a decl for e.g an @main decl. + EntryPoint, }; - /// The ValueDecl or AbstractClosureExpr represented by this SILDeclRef. + /// The AST node represented by this SILDeclRef. Loc loc; /// The Kind of this SILDeclRef. Kind kind : 4; /// True if this references a foreign entry point for the referenced decl. unsigned isForeign : 1; + /// True if this references a distributed function. + unsigned isDistributed : 1; /// The default argument index for a default argument getter. unsigned defaultArgIndex : 10; @@ -159,6 +173,17 @@ struct SILDeclRef { const GenericSignatureImpl *> pointer; + /// Returns the type of AST node location being stored by the SILDeclRef. + LocKind getLocKind() const { + if (loc.is()) + return LocKind::Decl; + if (loc.is()) + return LocKind::Closure; + if (loc.is()) + return LocKind::File; + llvm_unreachable("Unhandled location kind!"); + } + /// The derivative function identifier. AutoDiffDerivativeFunctionIdentifier * getDerivativeFunctionIdentifier() const { if (!pointer.is()) @@ -175,11 +200,13 @@ struct SILDeclRef { /// Produces a null SILDeclRef. SILDeclRef() - : loc(), kind(Kind::Func), isForeign(0), defaultArgIndex(0) {} + : loc(), kind(Kind::Func), isForeign(0), isDistributed(0), defaultArgIndex(0) {} /// Produces a SILDeclRef of the given kind for the given decl. explicit SILDeclRef( - ValueDecl *decl, Kind kind, bool isForeign = false, + ValueDecl *decl, Kind kind, + bool isForeign = false, + bool isDistributed = false, AutoDiffDerivativeFunctionIdentifier *derivativeId = nullptr); /// Produces a SILDeclRef for the given ValueDecl or @@ -193,7 +220,7 @@ struct SILDeclRef { /// for the containing ClassDecl. /// - If 'loc' is a global VarDecl, this returns its GlobalAccessor /// SILDeclRef. - explicit SILDeclRef(Loc loc, bool isForeign = false); + explicit SILDeclRef(Loc loc, bool isForeign = false, bool isDistributed = false); /// See above put produces a prespecialization according to the signature. explicit SILDeclRef(Loc loc, GenericSignature prespecializationSig); @@ -201,6 +228,12 @@ struct SILDeclRef { /// Produce a SIL constant for a default argument generator. static SILDeclRef getDefaultArgGenerator(Loc loc, unsigned defaultArgIndex); + /// Produces a SILDeclRef for a synthetic main entry-point such as @main. + static SILDeclRef getMainDeclEntryPoint(ValueDecl *decl); + + /// Produces a SILDeclRef for the entry-point of a main FileUnit. + static SILDeclRef getMainFileEntryPoint(FileUnit *file); + bool isNull() const { return loc.isNull(); } explicit operator bool() const { return !isNull(); } @@ -217,7 +250,13 @@ struct SILDeclRef { AutoClosureExpr *getAutoClosureExpr() const; FuncDecl *getFuncDecl() const; AbstractFunctionDecl *getAbstractFunctionDecl() const; - + FileUnit *getFileUnit() const { + return loc.get(); + } + + /// Retrieves the ASTContext from the underlying AST node being stored. + ASTContext &getASTContext() const; + llvm::Optional getAnyFunctionRef() const; SILLocation getAsRegularLocation() const; @@ -225,7 +264,6 @@ struct SILDeclRef { enum class ManglingKind { Default, DynamicThunk, - AsyncHandlerBody }; /// Produce a mangled form of this constant. @@ -306,12 +344,14 @@ struct SILDeclRef { friend llvm::hash_code hash_value(const SILDeclRef &ref) { return llvm::hash_combine(ref.loc.getOpaqueValue(), static_cast(ref.kind), - ref.isForeign, ref.defaultArgIndex); + ref.isForeign, ref.isDistributed, + ref.defaultArgIndex); } bool operator==(SILDeclRef rhs) const { return loc.getOpaqueValue() == rhs.loc.getOpaqueValue() && kind == rhs.kind && isForeign == rhs.isForeign && + isDistributed == rhs.isDistributed && defaultArgIndex == rhs.defaultArgIndex && pointer == rhs.pointer; } @@ -327,7 +367,19 @@ struct SILDeclRef { /// Returns the foreign (or native) entry point corresponding to the same /// decl. SILDeclRef asForeign(bool foreign = true) const { - return SILDeclRef(loc.getOpaqueValue(), kind, foreign, defaultArgIndex, + return SILDeclRef(loc.getOpaqueValue(), kind, + /*foreign=*/foreign, + /*distributed=*/false, + defaultArgIndex, + pointer.get()); + } + /// Returns the distributed entry point corresponding to the same + /// decl. + SILDeclRef asDistributed(bool distributed = true) const { + return SILDeclRef(loc.getOpaqueValue(), kind, + /*foreign=*/false, + /*distributed=*/distributed, + defaultArgIndex, pointer.get()); } @@ -357,14 +409,20 @@ struct SILDeclRef { return result; } + /// True is the decl ref references any kind of thunk. + bool isAnyThunk() const; + /// True if the decl ref references a thunk from a natively foreign /// declaration to Swift calling convention. bool isForeignToNativeThunk() const; - + /// True if the decl ref references a thunk from a natively Swift declaration /// to foreign C or ObjC calling convention. bool isNativeToForeignThunk() const; + /// True if the decl ref references a thunk handling potentially distributed actor functions + bool isDistributedThunk() const; + /// True if the decl ref references a method which introduces a new vtable /// entry. bool requiresNewVTableEntry() const; @@ -439,11 +497,14 @@ struct SILDeclRef { private: friend struct llvm::DenseMapInfo; /// Produces a SILDeclRef from an opaque value. - explicit SILDeclRef(void *opaqueLoc, Kind kind, bool isForeign, + explicit SILDeclRef(void *opaqueLoc, Kind kind, + bool isForeign, + bool isDistributed, unsigned defaultArgIndex, AutoDiffDerivativeFunctionIdentifier *derivativeId) : loc(Loc::getFromOpaqueValue(opaqueLoc)), kind(kind), - isForeign(isForeign), defaultArgIndex(defaultArgIndex), + isForeign(isForeign), isDistributed(isDistributed), + defaultArgIndex(defaultArgIndex), pointer(derivativeId) {} }; @@ -465,12 +526,12 @@ template<> struct DenseMapInfo { using UnsignedInfo = DenseMapInfo; static SILDeclRef getEmptyKey() { - return SILDeclRef(PointerInfo::getEmptyKey(), Kind::Func, false, 0, + return SILDeclRef(PointerInfo::getEmptyKey(), Kind::Func, false, false, 0, nullptr); } static SILDeclRef getTombstoneKey() { - return SILDeclRef(PointerInfo::getTombstoneKey(), Kind::Func, false, 0, - nullptr); + return SILDeclRef(PointerInfo::getTombstoneKey(), Kind::Func, false, false, + 0, nullptr); } static unsigned getHashValue(swift::SILDeclRef Val) { unsigned h1 = PointerInfo::getHashValue(Val.loc.getOpaqueValue()); @@ -480,7 +541,8 @@ template<> struct DenseMapInfo { : 0; unsigned h4 = UnsignedInfo::getHashValue(Val.isForeign); unsigned h5 = PointerInfo::getHashValue(Val.pointer.getOpaqueValue()); - return h1 ^ (h2 << 4) ^ (h3 << 9) ^ (h4 << 7) ^ (h5 << 11); + unsigned h6 = UnsignedInfo::getHashValue(Val.isDistributed); + return h1 ^ (h2 << 4) ^ (h3 << 9) ^ (h4 << 7) ^ (h5 << 11) ^ (h6 << 8); } static bool isEqual(swift::SILDeclRef const &LHS, swift::SILDeclRef const &RHS) { diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 31f48012bfe18..b1e3379e5ec0e 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -21,6 +21,7 @@ #include "swift/AST/Availability.h" #include "swift/AST/ResilienceExpansion.h" #include "swift/Basic/ProfileCounter.h" +#include "swift/SIL/SwiftObjectHeader.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILDeclRef.h" @@ -129,7 +130,11 @@ class SILSpecializeAttr final { /// zero or more SIL SILBasicBlock objects that contain the SILInstruction /// objects making up the function. class SILFunction - : public llvm::ilist_node, public SILAllocated { + : public llvm::ilist_node, public SILAllocated, + public SwiftObjectHeader { + + static SwiftMetatype registeredMetatype; + public: using BlockListType = llvm::iplist; @@ -315,6 +320,9 @@ class SILFunction /// The function's effects attribute. unsigned EffectsKindAttr : NumEffectsKindBits; + /// The function is in a statically linked module. + unsigned IsStaticallyLinked : 1; + static void validateSubclassScope(SubclassScope scope, IsThunk_t isThunk, const GenericSpecializationInformation *genericInfo) { @@ -385,6 +393,10 @@ class SILFunction void setHasOwnership(bool newValue) { HasOwnership = newValue; } public: + static void registerBridgedMetatype(SwiftMetatype metatype) { + registeredMetatype = metatype; + } + ~SILFunction(); SILModule &getModule() const { return Module; } @@ -534,6 +546,12 @@ class SILFunction WasDeserializedCanonical = val; } + bool isStaticallyLinked() const { return IsStaticallyLinked; } + + void setIsStaticallyLinked(bool value) { + IsStaticallyLinked = value; + } + /// Returns true if this is a reabstraction thunk of escaping function type /// whose single argument is a potentially non-escaping closure. i.e. the /// thunks' function argument may itself have @inout_aliasable parameters. @@ -820,7 +838,8 @@ class SILFunction DeclCtxt = (DS ? DebugScope->Loc.getAsDeclContext() : nullptr); } - /// Initialize the debug scope for debug info on SIL level (-gsil). + /// Initialize the debug scope for debug info on SIL level + /// (-sil-based-debuginfo). void setSILDebugScope(const SILDebugScope *DS) { DebugScope = DS; } @@ -981,6 +1000,10 @@ class SILFunction void clear(); + /// Like `clear`, but does not call `dropAllReferences`, which is the + /// responsibility of the caller. + void eraseAllBlocks(); + /// Return the identity substitutions necessary to forward this call if it is /// generic. SubstitutionMap getForwardingSubstitutionMap(); diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index 096247af2d3b4..c7bd9c17538f2 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -21,6 +21,7 @@ #include "swift/SIL/SILLocation.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILType.h" +#include "swift/SIL/SwiftObjectHeader.h" #include "llvm/ADT/ilist_node.h" #include "llvm/ADT/ilist.h" @@ -35,8 +36,11 @@ class VarDecl; /// A global variable that has been referenced in SIL. class SILGlobalVariable : public llvm::ilist_node, - public SILAllocated + public SILAllocated, + public SwiftObjectHeader { + static SwiftMetatype registeredMetatype; + public: using const_iterator = SILBasicBlock::const_iterator; @@ -97,6 +101,10 @@ class SILGlobalVariable Optional loc, VarDecl *decl); public: + static void registerBridgedMetatype(SwiftMetatype metatype) { + registeredMetatype = metatype; + } + static SILGlobalVariable *create(SILModule &Module, SILLinkage Linkage, IsSerialized_t IsSerialized, StringRef MangledName, SILType LoweredType, @@ -181,6 +189,11 @@ class SILGlobalVariable StaticInitializerBlock.dropAllReferences(); } + void clear() { + dropAllReferences(); + StaticInitializerBlock.eraseAllInstructions(Module); + } + /// Return whether this variable corresponds to a Clang node. bool hasClangNode() const; diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 341749fc74376..57991dc768e79 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -32,6 +32,7 @@ #include "swift/SIL/Consumption.h" #include "swift/SIL/SILAllocated.h" #include "swift/SIL/SILArgumentArrayRef.h" +#include "swift/SIL/SILDebugInfoExpression.h" #include "swift/SIL/SILDeclRef.h" #include "swift/SIL/SILFunctionConventions.h" #include "swift/SIL/SILLocation.h" @@ -320,6 +321,7 @@ class SILInstruction : public llvm::ilist_node { friend llvm::ilist_traits; friend llvm::ilist_traits; friend SILBasicBlock; + friend SILModule; /// A backreference to the containing basic block. This is maintained by /// ilist_traits. @@ -364,10 +366,15 @@ class SILInstruction : public llvm::ilist_node { SILInstructionResultArray getResultsImpl() const; protected: + friend class LibswiftPassInvocation; + SILInstruction() { NumCreatedInstructions++; } + /// This method unlinks 'self' from the containing basic block. + void removeFromParent(); + ~SILInstruction() { NumDeletedInstructions++; } @@ -381,6 +388,10 @@ class SILInstruction : public llvm::ilist_node { return C.allocateInst(Bytes, Alignment); } + /// Returns true if this instruction is removed from its function and + /// scheduled to be deleted. + bool isDeleted() const { return !ParentBB; } + enum class MemoryBehavior { None, /// The instruction may read memory. @@ -464,6 +475,9 @@ class SILInstruction : public llvm::ilist_node { /// Drops all uses that belong to this instruction. void dropAllReferences(); + /// Drops all references that aren't represented by operands. + void dropNonOperandReferences(); + /// Replace all uses of all results of this instruction with undef. void replaceAllUsesOfAllResultsWithUndef(); @@ -720,8 +734,7 @@ class SILInstruction : public llvm::ilist_node { /// Returns true if the instruction is only relevant for debug /// informations and has no other impact on program semantics. bool isDebugInstruction() const { - return getKind() == SILInstructionKind::DebugValueInst || - getKind() == SILInstructionKind::DebugValueAddrInst; + return getKind() == SILInstructionKind::DebugValueInst; } /// Returns true if the instruction is a meta instruction which is @@ -733,6 +746,10 @@ class SILInstruction : public llvm::ilist_node { /// with this instruction. void verifyOperandOwnership() const; + /// Verify that this instruction and its associated debug information follow + /// all SIL debug info invariants. + void verifyDebugInfo() const; + /// Get the number of created SILInstructions. static int getNumCreatedInstructions() { return NumCreatedInstructions; @@ -1702,20 +1719,45 @@ class UnaryInstructionWithTypeDependentOperandsBase }; /// Holds common debug information about local variables and function -/// arguments that are needed by DebugValueInst, DebugValueAddrInst, -/// AllocStackInst, and AllocBoxInst. +/// arguments that are needed by DebugValueInst, AllocStackInst, +/// and AllocBoxInst. struct SILDebugVariable { StringRef Name; unsigned ArgNo : 16; unsigned Constant : 1; - - SILDebugVariable() : ArgNo(0), Constant(false) {} + unsigned Implicit : 1; + Optional Type; + Optional Loc; + const SILDebugScope *Scope; + SILDebugInfoExpression DIExpr; + + // Use vanilla copy ctor / operator + SILDebugVariable(const SILDebugVariable &) = default; + SILDebugVariable &operator=(const SILDebugVariable &) = default; + + SILDebugVariable() + : ArgNo(0), Constant(false), Implicit(false), Scope(nullptr) {} SILDebugVariable(bool Constant, uint16_t ArgNo) - : ArgNo(ArgNo), Constant(Constant) {} - SILDebugVariable(StringRef Name, bool Constant, unsigned ArgNo) - : Name(Name), ArgNo(ArgNo), Constant(Constant) {} + : ArgNo(ArgNo), Constant(Constant), Implicit(false), Scope(nullptr) {} + SILDebugVariable(StringRef Name, bool Constant, unsigned ArgNo, + bool IsImplicit = false, Optional AuxType = {}, + Optional DeclLoc = {}, + const SILDebugScope *DeclScope = nullptr, + llvm::ArrayRef ExprElements = {}) + : Name(Name), ArgNo(ArgNo), Constant(Constant), Implicit(IsImplicit), + Type(AuxType), Loc(DeclLoc), Scope(DeclScope), DIExpr(ExprElements) {} + + /// Created from either AllocStack or AllocBox instruction + static Optional + createFromAllocation(const AllocationInst *AI); + + // We're not comparing DIExpr here because strictly speaking, + // DIExpr is not part of the debug variable. We simply piggyback + // it in this class so that's it's easier to carry DIExpr around. bool operator==(const SILDebugVariable &V) { - return ArgNo == V.ArgNo && Constant == V.Constant && Name == V.Name; + return ArgNo == V.ArgNo && Constant == V.Constant && Name == V.Name && + Implicit == V.Implicit && Type == V.Type && Loc == V.Loc && + Scope == V.Scope; } }; @@ -1730,17 +1772,23 @@ class TailAllocatedDebugVariable { int_type HasValue : 1; /// True if this is a let-binding. int_type Constant : 1; + /// True if this variable is created by compiler + int_type Implicit : 1; /// When this is nonzero there is a tail-allocated string storing /// variable name present. This typically only happens for /// instructions that were created from parsing SIL assembler. - int_type NameLength : 14; + int_type NameLength : 13; /// The source function argument position from left to right /// starting with 1 or 0 if this is a local variable. int_type ArgNo : 16; } Data; } Bits; public: - TailAllocatedDebugVariable(Optional, char *buf); + TailAllocatedDebugVariable(Optional, char *buf, + SILType *AuxVarType = nullptr, + SILLocation *DeclLoc = nullptr, + const SILDebugScope **DeclScope = nullptr, + SILDIExprElement *DIExprOps = nullptr); TailAllocatedDebugVariable(int_type RawValue) { Bits.RawValue = RawValue; } int_type getRawValue() const { return Bits.RawValue; } @@ -1751,19 +1799,73 @@ class TailAllocatedDebugVariable { StringRef getName(const char *buf) const; bool isLet() const { return Bits.Data.Constant; } - Optional get(VarDecl *VD, const char *buf) const { + bool isImplicit() const { return Bits.Data.Implicit; } + void setImplicit(bool V = true) { Bits.Data.Implicit = V; } + + Optional + get(VarDecl *VD, const char *buf, Optional AuxVarType = {}, + Optional DeclLoc = {}, + const SILDebugScope *DeclScope = nullptr, + llvm::ArrayRef DIExprElements = {}) const { if (!Bits.Data.HasValue) return None; + if (VD) return SILDebugVariable(VD->getName().empty() ? "" : VD->getName().str(), - VD->isLet(), getArgNo()); + VD->isLet(), getArgNo(), isImplicit(), AuxVarType, + DeclLoc, DeclScope, DIExprElements); else - return SILDebugVariable(getName(buf), isLet(), getArgNo()); + return SILDebugVariable(getName(buf), isLet(), getArgNo(), isImplicit(), + AuxVarType, DeclLoc, DeclScope, DIExprElements); } }; static_assert(sizeof(TailAllocatedDebugVariable) == 4, "SILNode inline bitfield needs updating"); +/// Used for keeping track of advanced / supplement debug variable info +/// stored in trailing objects space inside debug instructions (e.g. +/// debug_value) +class SILDebugVariableSupplement { +protected: + enum SourceLocKind : unsigned { SLK_Loc = 0b01, SLK_Scope = 0b10 }; + + unsigned NumDIExprOperands : 8; + + unsigned HasAuxDebugVariableType : 1; + + unsigned AuxVariableSourceLoc : 2; + + SILDebugVariableSupplement(unsigned NumDIExprOps, bool AuxType, bool AuxLoc, + bool AuxScope) + : NumDIExprOperands(NumDIExprOps), HasAuxDebugVariableType(AuxType), + AuxVariableSourceLoc((AuxLoc ? SLK_Loc : 0) | + (AuxScope ? SLK_Scope : 0)) {} +}; + +#define SIL_DEBUG_VAR_SUPPLEMENT_TRAILING_OBJS_IMPL() \ + inline bool hasAuxDebugLocation() const { \ + return AuxVariableSourceLoc & SLK_Loc; \ + } \ + inline bool hasAuxDebugScope() const { \ + return AuxVariableSourceLoc & SLK_Scope; \ + } \ + \ + size_t numTrailingObjects(OverloadToken) const { \ + return HasAuxDebugVariableType ? 1 : 0; \ + } \ + \ + size_t numTrailingObjects(OverloadToken) const { \ + return hasAuxDebugLocation() ? 1 : 0; \ + } \ + \ + size_t numTrailingObjects(OverloadToken) const { \ + return hasAuxDebugScope() ? 1 : 0; \ + } \ + \ + size_t numTrailingObjects(OverloadToken) const { \ + return NumDIExprOperands; \ + } + //===----------------------------------------------------------------------===// // Allocation Instructions //===----------------------------------------------------------------------===// @@ -1791,7 +1893,10 @@ class DeallocStackInst; class AllocStackInst final : public InstructionBase, - private llvm::TrailingObjects { + private SILDebugVariableSupplement, + private llvm::TrailingObjects { friend TrailingObjects; friend SILBuilder; @@ -1807,6 +1912,8 @@ class AllocStackInst final Optional Var, bool hasDynamicLifetime); + SIL_DEBUG_VAR_SUPPLEMENT_TRAILING_OBJS_IMPL() + size_t numTrailingObjects(OverloadToken) const { return SILNode::Bits.AllocStackInst.NumOperands; } @@ -1836,9 +1943,24 @@ class AllocStackInst final /// Return the debug variable information attached to this instruction. Optional getVarInfo() const { + Optional AuxVarType; + Optional VarDeclLoc; + const SILDebugScope *VarDeclScope = nullptr; + if (HasAuxDebugVariableType) + AuxVarType = *getTrailingObjects(); + + if (hasAuxDebugLocation()) + VarDeclLoc = *getTrailingObjects(); + if (hasAuxDebugScope()) + VarDeclScope = *getTrailingObjects(); + + llvm::ArrayRef DIExprElements( + getTrailingObjects(), NumDIExprOperands); + auto RawValue = SILNode::Bits.AllocStackInst.VarInfo; auto VI = TailAllocatedDebugVariable(RawValue); - return VI.get(getDecl(), getTrailingObjects()); + return VI.get(getDecl(), getTrailingObjects(), AuxVarType, VarDeclLoc, + VarDeclScope, DIExprElements); }; void setArgNo(unsigned N) { auto RawValue = SILNode::Bits.AllocStackInst.VarInfo; @@ -1847,6 +1969,11 @@ class AllocStackInst final SILNode::Bits.AllocStackInst.VarInfo = VI.getRawValue(); } + void setDebugVarScope(const SILDebugScope *NewDS) { + if (hasAuxDebugScope()) + *getTrailingObjects() = NewDS; + } + /// getElementType - Get the type of the allocated memory (as opposed to the /// type of the instruction itself, which will be an address type). SILType getElementType() const { @@ -3460,11 +3587,15 @@ class HopToExecutorInst friend SILBuilder; HopToExecutorInst(SILDebugLocation debugLoc, SILValue executor, - bool hasOwnership) - : UnaryInstructionBase(debugLoc, executor) { } + bool hasOwnership, bool isMandatory) + : UnaryInstructionBase(debugLoc, executor) { + SILNode::Bits.HopToExecutorInst.mandatory = isMandatory; + } public: SILValue getTargetExecutor() const { return getOperand(); } + + bool isMandatory() const { return SILNode::Bits.HopToExecutorInst.mandatory; } }; /// Extract the ex that the code is executing on the operand executor already. @@ -3895,14 +4026,21 @@ class BeginBorrowInst SingleValueInstruction> { friend class SILBuilder; - BeginBorrowInst(SILDebugLocation DebugLoc, SILValue LValue) + bool defined; + + BeginBorrowInst(SILDebugLocation DebugLoc, SILValue LValue, bool defined) : UnaryInstructionBase(DebugLoc, LValue, - LValue->getType().getObjectType()) {} + LValue->getType().getObjectType()), + defined(defined) {} public: using EndBorrowRange = decltype(std::declval().getUsersOfType()); + /// Whether the borrow scope defined by this instruction corresponds to a + /// source-level VarDecl. + bool isDefined() const { return defined; } + /// Return a range over all EndBorrow instructions for this BeginBorrow. EndBorrowRange getEndBorrows() const; @@ -4519,9 +4657,13 @@ class MarkFunctionEscapeInst final class DebugValueInst final : public UnaryInstructionBase, - private llvm::TrailingObjects { + private SILDebugVariableSupplement, + private llvm::TrailingObjects { friend TrailingObjects; friend SILBuilder; + TailAllocatedDebugVariable VarInfo; DebugValueInst(SILDebugLocation DebugLoc, SILValue Operand, @@ -4529,6 +4671,10 @@ class DebugValueInst final static DebugValueInst *create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &M, SILDebugVariable Var, bool poisonRefs); + static DebugValueInst *createAddr(SILDebugLocation DebugLoc, SILValue Operand, + SILModule &M, SILDebugVariable Var); + + SIL_DEBUG_VAR_SUPPLEMENT_TRAILING_OBJS_IMPL() size_t numTrailingObjects(OverloadToken) const { return 1; } @@ -4538,9 +4684,46 @@ class DebugValueInst final VarDecl *getDecl() const; /// Return the debug variable information attached to this instruction. Optional getVarInfo() const { - return VarInfo.get(getDecl(), getTrailingObjects()); + Optional AuxVarType; + Optional VarDeclLoc; + const SILDebugScope *VarDeclScope = nullptr; + if (HasAuxDebugVariableType) + AuxVarType = *getTrailingObjects(); + + if (hasAuxDebugLocation()) + VarDeclLoc = *getTrailingObjects(); + if (hasAuxDebugScope()) + VarDeclScope = *getTrailingObjects(); + + llvm::ArrayRef DIExprElements( + getTrailingObjects(), NumDIExprOperands); + + return VarInfo.get(getDecl(), getTrailingObjects(), AuxVarType, + VarDeclLoc, VarDeclScope, DIExprElements); + } + + void setDebugVarScope(const SILDebugScope *NewDS) { + if (hasAuxDebugScope()) + *getTrailingObjects() = NewDS; } + /// Whether the SSA value associated with the current debug_value + /// instruction has an address type. + bool hasAddrVal() const { + return getOperand()->getType().isAddress(); + } + + /// An utility to check if \p I is DebugValueInst and + /// whether it's associated with address type SSA value. + static DebugValueInst *hasAddrVal(SILInstruction *I) { + auto *DVI = dyn_cast_or_null(I); + return DVI && DVI->hasAddrVal()? DVI : nullptr; + } + + /// Whether the attached di-expression (if there is any) starts + /// with `op_deref`. + bool exprStartsWithDeref() const; + /// True if all references within this debug value will be overwritten with a /// poison sentinel at this point in the program. This is used in debug builds /// when shortening non-trivial value lifetimes to ensure the debugger cannot @@ -4557,33 +4740,6 @@ class DebugValueInst final } }; -/// Define the start or update to a symbolic variable value (for address-only -/// types) . -class DebugValueAddrInst final - : public UnaryInstructionBase, - private llvm::TrailingObjects { - friend TrailingObjects; - friend SILBuilder; - TailAllocatedDebugVariable VarInfo; - - DebugValueAddrInst(SILDebugLocation DebugLoc, SILValue Operand, - SILDebugVariable Var); - static DebugValueAddrInst *create(SILDebugLocation DebugLoc, - SILValue Operand, SILModule &M, - SILDebugVariable Var); - -public: - /// Return the underlying variable declaration that this denotes, - /// or null if we don't have one. - VarDecl *getDecl() const; - /// Return the debug variable information attached to this instruction. - Optional getVarInfo() const { - return VarInfo.get(getDecl(), getTrailingObjects()); - }; -}; - - /// An abstract class representing a load from some kind of reference storage. template class LoadReferenceInstBase @@ -6246,6 +6402,8 @@ class TupleElementAddrInst /// object, including properties declared in a superclass. unsigned getFieldIndex(NominalTypeDecl *decl, VarDecl *property); +unsigned getCaseIndex(EnumElementDecl *enumElement); + /// Get the property for a struct or class by its unique index, or nullptr if /// the index does not match a property declared in this struct or class or /// one its superclasses. diff --git a/include/swift/SIL/SILInstructionWorklist.h b/include/swift/SIL/SILInstructionWorklist.h index 1159674cd70be..a62b79907674e 100644 --- a/include/swift/SIL/SILInstructionWorklist.h +++ b/include/swift/SIL/SILInstructionWorklist.h @@ -35,6 +35,7 @@ #include "swift/Basic/BlotSetVector.h" #include "swift/SIL/SILInstruction.h" +#include "swift/SIL/InstructionUtils.h" #include "swift/SIL/SILValue.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "llvm/ADT/DenseMap.h" @@ -65,6 +66,9 @@ template , class SILInstructionWorklist : SILInstructionWorklistBase { BlotSetVector worklist; + /// For invoking Swift instruction passes in libswift. + LibswiftPassInvocation *libswiftPassInvocation = nullptr; + void operator=(const SILInstructionWorklist &rhs) = delete; SILInstructionWorklist(const SILInstructionWorklist &worklist) = delete; @@ -72,6 +76,10 @@ class SILInstructionWorklist : SILInstructionWorklistBase { SILInstructionWorklist(const char *loggingName = "InstructionWorklist") : SILInstructionWorklistBase(loggingName) {} + void setLibswiftPassInvocation(LibswiftPassInvocation *invocation) { + libswiftPassInvocation = invocation; + } + /// Returns true if the worklist is empty. bool isEmpty() const { return worklist.empty(); } @@ -128,13 +136,15 @@ class SILInstructionWorklist : SILInstructionWorklistBase { } } - /// All operands of \p instruction to the worklist when performing 2 stage - /// instruction deletion. Meant to be used right before deleting an - /// instruction in callbacks like InstModCallback::onNotifyWillBeDeleted(). + /// Add operands of \p instruction to the worklist. Meant to be used once it + /// is certain that \p instruction will be deleted but may have operands that + /// are still alive. With fewer uses, the operand definition may be + /// optimizable. + /// + /// \p instruction may still have uses because this is called before + /// InstructionDeleter begins deleting it and some instructions are deleted at + /// the same time as their uses. void addOperandsToWorklist(SILInstruction &instruction) { - assert(!instruction.hasUsesOfAnyResult() && - "Cannot erase instruction that is used!"); - // Make sure that we reprocess all operands now that we reduced their // use counts. if (instruction.getNumOperands() < 8) { diff --git a/include/swift/SIL/SILLocation.h b/include/swift/SIL/SILLocation.h index 4836da43e598f..6f4fe02bfc694 100644 --- a/include/swift/SIL/SILLocation.h +++ b/include/swift/SIL/SILLocation.h @@ -51,8 +51,9 @@ class SILLocation { /// Describes a position in a source file by explicitly storing the file name, /// line and column. /// - /// This is used for parsed locations from a SIL file, for "-gsil" (see - /// SILDebugInfoGenerator) and for the "compiler-generated" singleton location. + /// This is used for parsed locations from a SIL file, for + /// "-sil-based-debuginfo" (see SILDebugInfoGenerator) and for the + /// "compiler-generated" singleton location. /// In future we might also use it for de-serialized locations from a /// swiftmodule file. struct FilenameAndLocation : public SILAllocated { @@ -207,6 +208,7 @@ class SILLocation { case FilenameAndLocationKind: llvm_unreachable("location type has no AST node"); } + llvm_unreachable("covered switch"); } /// Returns true if the location has a separate AST node for debugging. @@ -300,6 +302,7 @@ class SILLocation { case FilenameAndLocationKind: return storage.filePositionLoc == nullptr;; case SourceLocKind: return storage.sourceLoc.isInvalid(); } + llvm_unreachable("covered switch"); } explicit operator bool() const { return !isNull(); } @@ -313,6 +316,7 @@ class SILLocation { case FilenameAndLocationKind: return false; } + llvm_unreachable("covered switch"); } /// Returns true if this location came from a SIL file. @@ -329,6 +333,13 @@ class SILLocation { /// Marks the location as coming from auto-generated body. void markAutoGenerated() { kindAndFlags.fields.autoGenerated = true; } + /// Returns this location with the auto-generated flag set. + SILLocation asAutoGenerated() const { + SILLocation loc = *this; + loc.markAutoGenerated(); + return loc; + } + /// Returns true if the location represents an artificially generated /// body, such as thunks or default destructors. /// diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index bd9622f55a4c0..5737b941171c6 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -57,10 +57,10 @@ class Output; namespace swift { -/// A fixed size slab of memory, which can be allocated and freed by the -/// SILModule at (basically) zero cost. -class FixedSizeSlab : public llvm::ilist_node, - public SILAllocated { +/// The payload for the FixedSizeSlab. +/// This is a super-class rather than a member of FixedSizeSlab to make bridging +/// with libswift easier. +class FixedSizeSlabPayload { public: /// The capacity of the payload. static constexpr size_t capacity = 64 * sizeof(uintptr_t); @@ -78,7 +78,7 @@ class FixedSizeSlab : public llvm::ilist_node, uintptr_t overflowGuard = magicNumber; public: - void operator=(const FixedSizeSlab &) = delete; + void operator=(const FixedSizeSlabPayload &) = delete; void operator delete(void *Ptr, size_t) = delete; /// Returns the payload pointing to \p T. @@ -88,6 +88,17 @@ class FixedSizeSlab : public llvm::ilist_node, template const T *dataFor() const { return (const T *)(&data[0]); } }; +/// A fixed size slab of memory, which can be allocated and freed by the +/// SILModule at (basically) zero cost. +/// See SILModule::allocSlab(). +class FixedSizeSlab : public llvm::ilist_node, + public SILAllocated, + public FixedSizeSlabPayload { +public: + void operator=(const FixedSizeSlab &) = delete; + void operator delete(void *Ptr, size_t) = delete; +}; + class AnyFunctionType; class ASTContext; class FileUnit; @@ -189,6 +200,17 @@ class SILModule { /// For consistency checking. size_t numAllocatedSlabs = 0; + /// When an instruction is "deleted" from the SIL, it is put into this list. + /// The instructions in this list are eventually deleted for real in + /// flushDeletedInsts(), which is called by the pass manager after each pass + /// run. + /// In other words: instruction deletion is deferred to the end of a pass. + /// + /// This avoids dangling instruction pointers within the run of a pass and in + /// analysis caches. Note that the analysis invalidation mechanism ensures + /// that analysis caches are invalidated before flushDeletedInsts(). + llvm::iplist scheduledForDeletion; + /// The swift Module associated with this SILModule. ModuleDecl *TheSwiftModule; @@ -334,10 +356,6 @@ class SILModule { /// Action to be executed for serializing the SILModule. ActionCallback SerializeSILAction; - /// A list of clients that need to be notified when an instruction - /// invalidation message is sent. - llvm::SetVector NotificationHandlers; - SILModule(llvm::PointerUnion context, Lowering::TypeConverter &TC, const SILOptions &Options); @@ -413,16 +431,6 @@ class SILModule { /// Called after an instruction is moved from one function to another. void notifyMovedInstruction(SILInstruction *inst, SILFunction *fromFunction); - /// Add a delete notification handler \p Handler to the module context. - void registerDeleteNotificationHandler(DeleteNotificationHandler* Handler); - - /// Remove the delete notification handler \p Handler from the module context. - void removeDeleteNotificationHandler(DeleteNotificationHandler* Handler); - - /// Send the invalidation message that \p V is being deleted to all - /// registered handlers. The order of handlers is deterministic but arbitrary. - void notifyDeleteHandlers(SILNode *node); - /// Set a serialization action. void setSerializeSILAction(ActionCallback SerializeSILAction); ActionCallback getSerializeSILAction() const; @@ -849,8 +857,17 @@ class SILModule { /// Allocate memory for an instruction using the module's internal allocator. void *allocateInst(unsigned Size, unsigned Align) const; - /// Deallocate memory of an instruction. - void deallocateInst(SILInstruction *I); + /// Called before \p I is removed from its basic block and scheduled for + /// deletion. + void willDeleteInstruction(SILInstruction *I); + + /// Schedules the (already removed) instruction \p I for deletion. + /// See scheduledForDeletion for details. + void scheduleForDeletion(SILInstruction *I); + + /// Deletes all scheuled instructions for real. + /// See scheduledForDeletion for details. + void flushDeletedInsts(); /// Looks up the llvm intrinsic ID and type for the builtin function. /// diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index a3a425e064fe2..f4410afc9842b 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -21,6 +21,7 @@ #include "llvm/Support/PointerLikeTypeTraits.h" #include "swift/Basic/InlineBitfield.h" #include "swift/Basic/LLVM.h" +#include "swift/SIL/SwiftObjectHeader.h" #include namespace swift { @@ -114,7 +115,10 @@ class SILNodePointer { /// subobject. If the SILNode is actually the base subobject of a /// ValueBase subobject, the cast will yield a corrupted value. /// Always use the LLVM casts (cast<>, dyn_cast<>, etc.) instead. -class alignas(8) SILNode { +class alignas(8) SILNode : + // SILNode contains a swift object header for bridging with libswift. + // For details see libswift/README.md. + public SwiftObjectHeader { public: enum { NumVOKindBits = 3 }; enum { NumStoreOwnershipQualifierBits = 2 }; @@ -338,6 +342,10 @@ class alignas(8) SILNode { Immutable : 1 ); + SWIFT_INLINE_BITFIELD(HopToExecutorInst, NonValueInstruction, 1, + mandatory : 1 + ); + SWIFT_INLINE_BITFIELD(DestroyValueInst, NonValueInstruction, 1, PoisonRefs : 1); @@ -422,8 +430,11 @@ class alignas(8) SILNode { } Bits; +private: + SwiftMetatype getSILNodeMetatype(SILNodeKind kind); + protected: - SILNode(SILNodeKind kind) { + SILNode(SILNodeKind kind) : SwiftObjectHeader(getSILNodeMetatype(kind)) { Bits.OpaqueBits = 0; Bits.SILNode.Kind = unsigned(kind); } diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 20e2ffb187e9f..7faf5b34d1888 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -102,6 +102,11 @@ #endif #endif +#ifndef BRIDGED_SINGLE_VALUE_INST +#define BRIDGED_SINGLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ + SINGLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) +#endif + /// DYNAMICCAST_SINGLE_VALUE_INST(ID, TEXTUALNAME, PARENT, MEMBEHAVIOR, MAYRELEASE) /// /// A SINGLE_VALUE_INST that is a cast instruction. ID is a member of @@ -112,7 +117,7 @@ DYNAMICCAST_INST(ID, TEXTUALNAME) #else #define DYNAMICCAST_SINGLE_VALUE_INST(ID, TEXTUALNAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ - SINGLE_VALUE_INST(ID, TEXTUALNAME, PARENT, MEMBEHAVIOR, MAYRELEASE) + BRIDGED_SINGLE_VALUE_INST(ID, TEXTUALNAME, PARENT, MEMBEHAVIOR, MAYRELEASE) #endif #endif @@ -127,7 +132,7 @@ APPLYSITE_INST(ID, PARENT) #else #define APPLYSITE_SINGLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ - SINGLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) + BRIDGED_SINGLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) #endif #endif @@ -150,7 +155,7 @@ /// /// A concrete subclass of MultipleValueInstruction. ID is a member of /// SILInstructionKind. The Node's class name is ID and the name of the base -/// class in the heirarchy is PARENT. +/// class in the hierarchy is PARENT. #ifndef MULTIPLE_VALUE_INST #define MULTIPLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ FULL_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) @@ -190,7 +195,7 @@ /// /// A concrete subclass of MultipleValueInstructionResult. ID is a member of /// ValueKind. The Node's class name is ID and the name of the base class in -/// the heirarchy is PARENT. +/// the hierarchy is PARENT. #ifndef MULTIPLE_VALUE_INST_RESULT #define MULTIPLE_VALUE_INST_RESULT(ID, PARENT) VALUE(ID, PARENT) #endif @@ -245,13 +250,18 @@ FULL_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) #endif +#ifndef BRIDGED_NON_VALUE_INST +#define BRIDGED_NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ + NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) +#endif + #ifndef DYNAMICCAST_NON_VALUE_INST #ifdef DYNAMICCAST_INST #define DYNAMICCAST_NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ DYNAMICCAST_INST(ID, NAME) #else #define DYNAMICCAST_NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ - NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) + BRIDGED_NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) #endif #endif @@ -260,7 +270,7 @@ /// ID is a member of TerminatorKind and the name of a subclass of TermInst. #ifndef TERMINATOR #define TERMINATOR(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ - NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) + BRIDGED_NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) #endif /// DYNAMICCAST_TERMINATOR(ID, TEXTUAL_NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) @@ -424,18 +434,18 @@ ABSTRACT_NODE(SILInstruction, SILNode) ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) // Allocation instructions. ABSTRACT_SINGLE_VALUE_INST(AllocationInst, SingleValueInstruction) - SINGLE_VALUE_INST(AllocStackInst, alloc_stack, - AllocationInst, None, DoesNotRelease) - SINGLE_VALUE_INST(AllocRefInst, alloc_ref, - AllocationInst, None, DoesNotRelease) - SINGLE_VALUE_INST(AllocRefDynamicInst, alloc_ref_dynamic, - AllocationInst, None, DoesNotRelease) - SINGLE_VALUE_INST(AllocValueBufferInst, alloc_value_buffer, - AllocationInst, None, DoesNotRelease) - SINGLE_VALUE_INST(AllocBoxInst, alloc_box, - AllocationInst, None, DoesNotRelease) - SINGLE_VALUE_INST(AllocExistentialBoxInst, alloc_existential_box, - AllocationInst, MayWrite, DoesNotRelease) + BRIDGED_SINGLE_VALUE_INST(AllocStackInst, alloc_stack, + AllocationInst, None, DoesNotRelease) + BRIDGED_SINGLE_VALUE_INST(AllocRefInst, alloc_ref, + AllocationInst, None, DoesNotRelease) + BRIDGED_SINGLE_VALUE_INST(AllocRefDynamicInst, alloc_ref_dynamic, + AllocationInst, None, DoesNotRelease) + BRIDGED_SINGLE_VALUE_INST(AllocValueBufferInst, alloc_value_buffer, + AllocationInst, None, DoesNotRelease) + BRIDGED_SINGLE_VALUE_INST(AllocBoxInst, alloc_box, + AllocationInst, None, DoesNotRelease) + BRIDGED_SINGLE_VALUE_INST(AllocExistentialBoxInst, alloc_existential_box, + AllocationInst, MayWrite, DoesNotRelease) SINGLE_VALUE_INST_RANGE(AllocationInst, AllocStackInst, AllocExistentialBoxInst) ABSTRACT_SINGLE_VALUE_INST(IndexingInst, SingleValueInstruction) @@ -455,11 +465,11 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) LiteralInst, None, DoesNotRelease) SINGLE_VALUE_INST(PreviousDynamicFunctionRefInst, prev_dynamic_function_ref, LiteralInst, None, DoesNotRelease) - SINGLE_VALUE_INST(GlobalAddrInst, global_addr, + BRIDGED_SINGLE_VALUE_INST(GlobalAddrInst, global_addr, LiteralInst, None, DoesNotRelease) SINGLE_VALUE_INST(BaseAddrForOffsetInst, base_addr_for_offset, LiteralInst, None, DoesNotRelease) - SINGLE_VALUE_INST(GlobalValueInst, global_value, + BRIDGED_SINGLE_VALUE_INST(GlobalValueInst, global_value, LiteralInst, None, DoesNotRelease) SINGLE_VALUE_INST(IntegerLiteralInst, integer_literal, LiteralInst, None, DoesNotRelease) @@ -485,13 +495,13 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) // Conversions ABSTRACT_SINGLE_VALUE_INST(ConversionInst, SingleValueInstruction) - SINGLE_VALUE_INST(UpcastInst, upcast, + BRIDGED_SINGLE_VALUE_INST(UpcastInst, upcast, ConversionInst, None, DoesNotRelease) - SINGLE_VALUE_INST(AddressToPointerInst, address_to_pointer, + BRIDGED_SINGLE_VALUE_INST(AddressToPointerInst, address_to_pointer, ConversionInst, None, DoesNotRelease) SINGLE_VALUE_INST(PointerToAddressInst, pointer_to_address, ConversionInst, None, DoesNotRelease) - SINGLE_VALUE_INST(UncheckedRefCastInst, unchecked_ref_cast, + BRIDGED_SINGLE_VALUE_INST(UncheckedRefCastInst, unchecked_ref_cast, ConversionInst, None, DoesNotRelease) SINGLE_VALUE_INST(UncheckedAddrCastInst, unchecked_addr_cast, ConversionInst, None, DoesNotRelease) @@ -503,7 +513,7 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) ConversionInst, None, DoesNotRelease) SINGLE_VALUE_INST(RefToRawPointerInst, ref_to_raw_pointer, ConversionInst, None, DoesNotRelease) - SINGLE_VALUE_INST(RawPointerToRefInst, raw_pointer_to_ref, + BRIDGED_SINGLE_VALUE_INST(RawPointerToRefInst, raw_pointer_to_ref, ConversionInst, None, DoesNotRelease) #define LOADABLE_REF_STORAGE(Name, name, ...) \ SINGLE_VALUE_INST(RefTo##Name##Inst, ref_to_##name, \ @@ -511,7 +521,7 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SINGLE_VALUE_INST(Name##ToRefInst, name##_to_ref, \ ConversionInst, None, DoesNotRelease) #include "swift/AST/ReferenceStorage.def" - SINGLE_VALUE_INST(ConvertFunctionInst, convert_function, + BRIDGED_SINGLE_VALUE_INST(ConvertFunctionInst, convert_function, ConversionInst, None, DoesNotRelease) SINGLE_VALUE_INST(ConvertEscapeToNoEscapeInst, convert_escape_to_noescape, ConversionInst, None, DoesNotRelease) @@ -525,15 +535,15 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) ConversionInst, None, DoesNotRelease) SINGLE_VALUE_INST(BridgeObjectToWordInst, bridge_object_to_word, ConversionInst, None, DoesNotRelease) - SINGLE_VALUE_INST(ThinToThickFunctionInst, thin_to_thick_function, + BRIDGED_SINGLE_VALUE_INST(ThinToThickFunctionInst, thin_to_thick_function, ConversionInst, None, DoesNotRelease) SINGLE_VALUE_INST(ThickToObjCMetatypeInst, thick_to_objc_metatype, ConversionInst, None, DoesNotRelease) SINGLE_VALUE_INST(ObjCToThickMetatypeInst, objc_to_thick_metatype, ConversionInst, None, DoesNotRelease) - SINGLE_VALUE_INST(ObjCMetatypeToObjectInst, objc_metatype_to_object, + BRIDGED_SINGLE_VALUE_INST(ObjCMetatypeToObjectInst, objc_metatype_to_object, ConversionInst, None, DoesNotRelease) - SINGLE_VALUE_INST(ObjCExistentialMetatypeToObjectInst, objc_existential_metatype_to_object, + BRIDGED_SINGLE_VALUE_INST(ObjCExistentialMetatypeToObjectInst, objc_existential_metatype_to_object, ConversionInst, None, DoesNotRelease) // unconditional_checked_cast_value reads the source value and produces // a new value with a potentially different representation. @@ -554,9 +564,9 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) ConversionInst, MayRead, DoesNotRelease) SINGLE_VALUE_INST_RANGE(ConversionInst, UpcastInst, UnconditionalCheckedCastInst) - SINGLE_VALUE_INST(ClassifyBridgeObjectInst, classify_bridge_object, + BRIDGED_SINGLE_VALUE_INST(ClassifyBridgeObjectInst, classify_bridge_object, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(ValueToBridgeObjectInst, value_to_bridge_object, + BRIDGED_SINGLE_VALUE_INST(ValueToBridgeObjectInst, value_to_bridge_object, SingleValueInstruction, None, DoesNotRelease) SINGLE_VALUE_INST(MarkDependenceInst, mark_dependence, SingleValueInstruction, None, DoesNotRelease) @@ -564,8 +574,10 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) SINGLE_VALUE_INST(CopyBlockWithoutEscapingInst, copy_block_without_escaping, SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) - SINGLE_VALUE_INST(CopyValueInst, copy_value, - SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) + // A copy_value's retain semantics are fully encapsulated in OSSA + // invariants. It has no side effects relative to other OSSA values. + BRIDGED_SINGLE_VALUE_INST(CopyValueInst, copy_value, + SingleValueInstruction, None, DoesNotRelease) #define UNCHECKED_REF_STORAGE(Name, name, ...) \ SINGLE_VALUE_INST(StrongCopy##Name##ValueInst, strong_copy_##name##_value, \ SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) @@ -590,18 +602,18 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SingleValueInstruction, MayRead, DoesNotRelease) // Accessing memory - SINGLE_VALUE_INST(LoadInst, load, + BRIDGED_SINGLE_VALUE_INST(LoadInst, load, SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) - SINGLE_VALUE_INST(LoadBorrowInst, load_borrow, + BRIDGED_SINGLE_VALUE_INST(LoadBorrowInst, load_borrow, SingleValueInstruction, MayRead, DoesNotRelease) - SINGLE_VALUE_INST(BeginBorrowInst, begin_borrow, + BRIDGED_SINGLE_VALUE_INST(BeginBorrowInst, begin_borrow, SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) SINGLE_VALUE_INST(StoreBorrowInst, store_borrow, SILInstruction, MayWrite, DoesNotRelease) // begin_access may trap. Trapping is unordered with respect to memory access, // and with respect to other traps, but it is still conservatively considered // a side effect. - SINGLE_VALUE_INST(BeginAccessInst, begin_access, + BRIDGED_SINGLE_VALUE_INST(BeginAccessInst, begin_access, SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ SINGLE_VALUE_INST(Load##Name##Inst, load_##name, \ @@ -619,7 +631,7 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) // Function Application FULLAPPLYSITE_SINGLE_VALUE_INST(ApplyInst, apply, SingleValueInstruction, MayHaveSideEffects, MayRelease) - SINGLE_VALUE_INST(BuiltinInst, builtin, + BRIDGED_SINGLE_VALUE_INST(BuiltinInst, builtin, SingleValueInstruction, MayHaveSideEffects, MayRelease) APPLYSITE_SINGLE_VALUE_INST(PartialApplyInst, partial_apply, SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) @@ -627,9 +639,9 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) // Metatypes SINGLE_VALUE_INST(MetatypeInst, metatype, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(ValueMetatypeInst, value_metatype, + BRIDGED_SINGLE_VALUE_INST(ValueMetatypeInst, value_metatype, SingleValueInstruction, MayRead, DoesNotRelease) - SINGLE_VALUE_INST(ExistentialMetatypeInst, existential_metatype, + BRIDGED_SINGLE_VALUE_INST(ExistentialMetatypeInst, existential_metatype, SingleValueInstruction, MayRead, DoesNotRelease) SINGLE_VALUE_INST(ObjCProtocolInst, objc_protocol, SingleValueInstruction, None, DoesNotRelease) @@ -637,27 +649,27 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) // Aggregate Types SINGLE_VALUE_INST(ObjectInst, object, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(TupleInst, tuple, + BRIDGED_SINGLE_VALUE_INST(TupleInst, tuple, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(TupleExtractInst, tuple_extract, + BRIDGED_SINGLE_VALUE_INST(TupleExtractInst, tuple_extract, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(TupleElementAddrInst, tuple_element_addr, + BRIDGED_SINGLE_VALUE_INST(TupleElementAddrInst, tuple_element_addr, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(StructInst, struct, + BRIDGED_SINGLE_VALUE_INST(StructInst, struct, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(StructExtractInst, struct_extract, + BRIDGED_SINGLE_VALUE_INST(StructExtractInst, struct_extract, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(StructElementAddrInst, struct_element_addr, + BRIDGED_SINGLE_VALUE_INST(StructElementAddrInst, struct_element_addr, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(RefElementAddrInst, ref_element_addr, + BRIDGED_SINGLE_VALUE_INST(RefElementAddrInst, ref_element_addr, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(RefTailAddrInst, ref_tail_addr, + BRIDGED_SINGLE_VALUE_INST(RefTailAddrInst, ref_tail_addr, SingleValueInstruction, None, DoesNotRelease) // Enums - SINGLE_VALUE_INST(EnumInst, enum, + BRIDGED_SINGLE_VALUE_INST(EnumInst, enum, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(UncheckedEnumDataInst, unchecked_enum_data, + BRIDGED_SINGLE_VALUE_INST(UncheckedEnumDataInst, unchecked_enum_data, SingleValueInstruction, None, DoesNotRelease) SINGLE_VALUE_INST(InitEnumDataAddrInst, init_enum_data_addr, SingleValueInstruction, None, DoesNotRelease) @@ -677,13 +689,13 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SingleValueInstruction, MayWrite, DoesNotRelease) SINGLE_VALUE_INST(OpenExistentialAddrInst, open_existential_addr, SingleValueInstruction, MayRead, DoesNotRelease) - SINGLE_VALUE_INST(InitExistentialRefInst, init_existential_ref, + BRIDGED_SINGLE_VALUE_INST(InitExistentialRefInst, init_existential_ref, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(OpenExistentialRefInst, open_existential_ref, + BRIDGED_SINGLE_VALUE_INST(OpenExistentialRefInst, open_existential_ref, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(InitExistentialMetatypeInst, init_existential_metatype, + BRIDGED_SINGLE_VALUE_INST(InitExistentialMetatypeInst, init_existential_metatype, SingleValueInstruction, None, DoesNotRelease) - SINGLE_VALUE_INST(OpenExistentialMetatypeInst, open_existential_metatype, + BRIDGED_SINGLE_VALUE_INST(OpenExistentialMetatypeInst, open_existential_metatype, SingleValueInstruction, None, DoesNotRelease) SINGLE_VALUE_INST(OpenExistentialBoxInst, open_existential_box, SingleValueInstruction, MayRead, DoesNotRelease) @@ -776,9 +788,9 @@ ABSTRACT_INST(TermInst, SILInstruction) // Deallocation instructions. ABSTRACT_INST(DeallocationInst, SILInstruction) - NON_VALUE_INST(DeallocStackInst, dealloc_stack, + BRIDGED_NON_VALUE_INST(DeallocStackInst, dealloc_stack, DeallocationInst, MayHaveSideEffects, DoesNotRelease) - NON_VALUE_INST(DeallocRefInst, dealloc_ref, + BRIDGED_NON_VALUE_INST(DeallocRefInst, dealloc_ref, DeallocationInst, MayHaveSideEffects, DoesNotRelease) NON_VALUE_INST(DeallocPartialRefInst, dealloc_partial_ref, DeallocationInst, MayHaveSideEffects, DoesNotRelease) @@ -792,9 +804,9 @@ ABSTRACT_INST(DeallocationInst, SILInstruction) // Reference Counting ABSTRACT_INST(RefCountingInst, SILInstruction) - NON_VALUE_INST(StrongRetainInst, strong_retain, + BRIDGED_NON_VALUE_INST(StrongRetainInst, strong_retain, RefCountingInst, MayHaveSideEffects, DoesNotRelease) - NON_VALUE_INST(StrongReleaseInst, strong_release, + BRIDGED_NON_VALUE_INST(StrongReleaseInst, strong_release, RefCountingInst, MayHaveSideEffects, MayRelease) NON_VALUE_INST(UnmanagedRetainValueInst, unmanaged_retain_value, RefCountingInst, MayHaveSideEffects, DoesNotRelease) @@ -810,15 +822,15 @@ ABSTRACT_INST(RefCountingInst, SILInstruction) NON_VALUE_INST(Name##ReleaseInst, name##_release, \ RefCountingInst, MayHaveSideEffects, MayRelease) #include "swift/AST/ReferenceStorage.def" - NON_VALUE_INST(RetainValueInst, retain_value, + BRIDGED_NON_VALUE_INST(RetainValueInst, retain_value, RefCountingInst, MayHaveSideEffects, DoesNotRelease) NON_VALUE_INST(RetainValueAddrInst, retain_value_addr, RefCountingInst, MayHaveSideEffects, DoesNotRelease) - NON_VALUE_INST(ReleaseValueInst, release_value, + BRIDGED_NON_VALUE_INST(ReleaseValueInst, release_value, RefCountingInst, MayHaveSideEffects, MayRelease) NON_VALUE_INST(ReleaseValueAddrInst, release_value_addr, RefCountingInst, MayHaveSideEffects, MayRelease) - NON_VALUE_INST(SetDeallocatingInst, set_deallocating, + BRIDGED_NON_VALUE_INST(SetDeallocatingInst, set_deallocating, RefCountingInst, MayHaveSideEffects, DoesNotRelease) NON_VALUE_INST(AutoreleaseValueInst, autorelease_value, @@ -833,27 +845,27 @@ NON_VALUE_INST(BindMemoryInst, bind_memory, SILInstruction, MayWrite, DoesNotRelease) // FIXME: Is MayHaveSideEffects appropriate? -NON_VALUE_INST(FixLifetimeInst, fix_lifetime, +BRIDGED_NON_VALUE_INST(FixLifetimeInst, fix_lifetime, SILInstruction, MayHaveSideEffects, DoesNotRelease) NON_VALUE_INST(HopToExecutorInst, hop_to_executor, SILInstruction, MayHaveSideEffects, DoesNotRelease) -NON_VALUE_INST(DestroyValueInst, destroy_value, +BRIDGED_NON_VALUE_INST(DestroyValueInst, destroy_value, SILInstruction, MayHaveSideEffects, MayRelease) -NON_VALUE_INST(EndBorrowInst, end_borrow, +BRIDGED_NON_VALUE_INST(EndBorrowInst, end_borrow, SILInstruction, MayHaveSideEffects, DoesNotRelease) // end_access is considered to have side effects because it modifies runtime // state outside of the memory modified by the access that is visible to // Swift. This "side effect" only needs to creates a dependency on begin_access // instructions. -NON_VALUE_INST(EndAccessInst, end_access, +BRIDGED_NON_VALUE_INST(EndAccessInst, end_access, SILInstruction, MayHaveSideEffects, DoesNotRelease) NON_VALUE_INST(BeginUnpairedAccessInst, begin_unpaired_access, SILInstruction, MayHaveSideEffects, DoesNotRelease) NON_VALUE_INST(EndUnpairedAccessInst, end_unpaired_access, SILInstruction, MayHaveSideEffects, DoesNotRelease) -NON_VALUE_INST(StoreInst, store, +BRIDGED_NON_VALUE_INST(StoreInst, store, SILInstruction, MayHaveSideEffects, MayRelease) NON_VALUE_INST(AssignInst, assign, SILInstruction, MayWrite, DoesNotRelease) @@ -861,17 +873,15 @@ NON_VALUE_INST(AssignByWrapperInst, assign_by_wrapper, SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(MarkFunctionEscapeInst, mark_function_escape, SILInstruction, None, DoesNotRelease) -NON_VALUE_INST(DebugValueInst, debug_value, - SILInstruction, None, DoesNotRelease) -NON_VALUE_INST(DebugValueAddrInst, debug_value_addr, +BRIDGED_NON_VALUE_INST(DebugValueInst, debug_value, SILInstruction, None, DoesNotRelease) #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ NON_VALUE_INST(Store##Name##Inst, store_##name, \ SILInstruction, MayWrite, DoesNotRelease) #include "swift/AST/ReferenceStorage.def" -NON_VALUE_INST(CopyAddrInst, copy_addr, +BRIDGED_NON_VALUE_INST(CopyAddrInst, copy_addr, SILInstruction, MayHaveSideEffects, MayRelease) -NON_VALUE_INST(DestroyAddrInst, destroy_addr, +BRIDGED_NON_VALUE_INST(DestroyAddrInst, destroy_addr, SILInstruction, MayHaveSideEffects, MayRelease) NON_VALUE_INST(EndLifetimeInst, end_lifetime, SILInstruction, MayHaveSideEffects, MayRelease) @@ -895,7 +905,7 @@ NON_VALUE_INST(AbortApplyInst, abort_apply, // Runtime failure // FIXME: Special MemBehavior for runtime failure? -NON_VALUE_INST(CondFailInst, cond_fail, +BRIDGED_NON_VALUE_INST(CondFailInst, cond_fail, SILInstruction, MayHaveSideEffects, DoesNotRelease) NODE_RANGE(NonValueInstruction, UnreachableInst, CondFailInst) @@ -935,6 +945,7 @@ NODE_RANGE(SILNode, SILPhiArgument, DestructureTupleInst) #undef DYNAMICCAST_TERMINATOR #undef TERMINATOR #undef NON_VALUE_INST +#undef BRIDGED_NON_VALUE_INST #undef DYNAMICCAST_NON_VALUE_INST #undef MULTIPLE_VALUE_INST_RESULT #undef FULLAPPLYSITE_MULTIPLE_VALUE_INST @@ -945,6 +956,7 @@ NODE_RANGE(SILNode, SILPhiArgument, DestructureTupleInst) #undef DYNAMICCAST_SINGLE_VALUE_INST #undef DYNAMICCAST_INST #undef SINGLE_VALUE_INST +#undef BRIDGED_SINGLE_VALUE_INST #undef FULL_INST #undef INST #undef ARGUMENT diff --git a/include/swift/SIL/SILPrintContext.h b/include/swift/SIL/SILPrintContext.h index 39a0e9683abd4..fc83fe2118641 100644 --- a/include/swift/SIL/SILPrintContext.h +++ b/include/swift/SIL/SILPrintContext.h @@ -78,7 +78,8 @@ class SILPrintContext { /// Constructor based on SILOptions. /// - /// DebugInfo will be set according to the -sil-print-debuginfo option. + /// DebugInfo will be set according to SILOptions::PrintDebugInfo or + /// the -sil-print-debuginfo option. SILPrintContext(llvm::raw_ostream &OS, const SILOptions &Opts); SILPrintContext(llvm::raw_ostream &OS, bool Verbose, bool SortedSIL, diff --git a/include/swift/SIL/SILSuccessor.h b/include/swift/SIL/SILSuccessor.h index bb9574dcec2f3..f52a623bf3c3b 100644 --- a/include/swift/SIL/SILSuccessor.h +++ b/include/swift/SIL/SILSuccessor.h @@ -76,6 +76,8 @@ class SILSuccessor { operator SILBasicBlock*() const { return SuccessorBlock; } SILBasicBlock *getBB() const { return SuccessorBlock; } + TermInst *getContainingInst() const { return ContainingInst; } + SILSuccessor *getNext() const { return Next; } ProfileCounter getCount() const { return Count; } diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h index ab1c87f99085a..19ebd27f1dbd4 100644 --- a/include/swift/SIL/SILValue.h +++ b/include/swift/SIL/SILValue.h @@ -43,6 +43,7 @@ class DeadEndBlocks; class ValueBaseUseIterator; class ConsumingUseIterator; class NonConsumingUseIterator; +class NonTypeDependentUseIterator; class SILValue; /// An enumeration which contains values for all the concrete ValueBase @@ -387,6 +388,9 @@ class ValueBase : public SILNode, public SILAllocated { using consuming_use_range = iterator_range; using non_consuming_use_iterator = NonConsumingUseIterator; using non_consuming_use_range = iterator_range; + using non_typedependent_use_iterator = NonTypeDependentUseIterator; + using non_typedependent_use_range = + iterator_range; inline use_iterator use_begin() const; inline use_iterator use_end() const; @@ -397,6 +401,9 @@ class ValueBase : public SILNode, public SILAllocated { inline non_consuming_use_iterator non_consuming_use_begin() const; inline non_consuming_use_iterator non_consuming_use_end() const; + inline non_typedependent_use_iterator non_typedependent_use_begin() const; + inline non_typedependent_use_iterator non_typedependent_use_end() const; + /// Returns a range of all uses, which is useful for iterating over all uses. /// To ignore debug-info instructions use swift::getNonDebugUses instead /// (see comment in DebugUtils.h). @@ -421,6 +428,10 @@ class ValueBase : public SILNode, public SILAllocated { /// Returns a range of all non consuming uses inline non_consuming_use_range getNonConsumingUses() const; + /// Returns a range of uses that are not classified as a type dependent + /// operand of the user. + inline non_typedependent_use_range getNonTypeDependentUses() const; + template inline T *getSingleUserOfType() const; @@ -467,6 +478,8 @@ class ValueBase : public SILNode, public SILAllocated { /// For instruction results, this returns getDefiningInstruction(). For /// arguments, this returns SILBasicBlock::begin() for the argument's parent /// block. Returns nullptr for SILUndef. + /// + /// FIXME: remove this redundant API from SILValue. SILInstruction *getDefiningInsertionPoint(); // Const version of \see getDefiningInsertionPoint. @@ -603,8 +616,6 @@ class SILValue { /// If this SILValue is a result of an instruction, return its /// defining instruction. Returns nullptr otherwise. - /// - /// FIXME: remove this redundant API from SILValue. SILInstruction *getDefiningInstruction() { return Value->getDefiningInstruction(); } @@ -875,6 +886,7 @@ inline OwnershipConstraint OperandOwnership::getOwnershipConstraint() { case OperandOwnership::Reborrow: return {OwnershipKind::Guaranteed, UseLifetimeConstraint::LifetimeEnding}; } + llvm_unreachable("covered switch"); } /// Return true if this use can accept Unowned values. @@ -900,8 +912,12 @@ inline bool canAcceptUnownedValue(OperandOwnership operandOwnership) { case OperandOwnership::Reborrow: return false; } + llvm_unreachable("covered switch"); } +/// Return true if all OperandOwnership invariants hold. +bool checkOperandOwnershipInvariants(const Operand *operand); + /// Return the OperandOwnership for a forwarded operand when the forwarding /// operation has this "forwarding ownership" (as returned by /// getForwardingOwnershipKind()). \p allowUnowned is true for a subset of @@ -1004,6 +1020,8 @@ class Operand { SILInstruction *getUser() { return Owner; } const SILInstruction *getUser() const { return Owner; } + Operand *getNextUse() const { return NextUse; } + /// Return true if this operand is a type dependent operand. /// /// Implemented in SILInstruction.h @@ -1037,9 +1055,17 @@ class Operand { /// guaranteed scope. bool isLifetimeEnding() const; + /// Returns true if this ends the lifetime of an owned operand. + bool isConsuming() const; + SILBasicBlock *getParentBlock() const; SILFunction *getParentFunction() const; + LLVM_ATTRIBUTE_DEPRECATED( + void dump() const LLVM_ATTRIBUTE_USED, + "Dump the operand's state. Only for use in the debugger!"); + void print(llvm::raw_ostream &os) const; + private: void removeFromCurrent() { if (!Back) @@ -1061,6 +1087,7 @@ class Operand { friend class ValueBaseUseIterator; friend class ConsumingUseIterator; friend class NonConsumingUseIterator; + friend class NonTypeDependentUseIterator; template friend class FixedOperandList; friend class TrailingOperandsList; }; @@ -1187,6 +1214,41 @@ ValueBase::non_consuming_use_end() const { return ValueBase::non_consuming_use_iterator(nullptr); } +class NonTypeDependentUseIterator : public ValueBaseUseIterator { +public: + explicit NonTypeDependentUseIterator(Operand *cur) + : ValueBaseUseIterator(cur) {} + NonTypeDependentUseIterator &operator++() { + assert(Cur && "incrementing past end()!"); + assert(!Cur->isTypeDependent()); + while ((Cur = Cur->NextUse)) { + if (!Cur->isTypeDependent()) + break; + } + return *this; + } + + NonTypeDependentUseIterator operator++(int unused) { + NonTypeDependentUseIterator copy = *this; + ++*this; + return copy; + } +}; + +inline ValueBase::non_typedependent_use_iterator +ValueBase::non_typedependent_use_begin() const { + auto cur = FirstUse; + while (cur && cur->isTypeDependent()) { + cur = cur->NextUse; + } + return ValueBase::non_typedependent_use_iterator(cur); +} + +inline ValueBase::non_typedependent_use_iterator +ValueBase::non_typedependent_use_end() const { + return ValueBase::non_typedependent_use_iterator(nullptr); +} + inline bool ValueBase::hasOneUse() const { auto I = use_begin(), E = use_end(); if (I == E) return false; @@ -1232,6 +1294,11 @@ ValueBase::getNonConsumingUses() const { return {non_consuming_use_begin(), non_consuming_use_end()}; } +inline ValueBase::non_typedependent_use_range +ValueBase::getNonTypeDependentUses() const { + return {non_typedependent_use_begin(), non_typedependent_use_end()}; +} + inline bool ValueBase::hasTwoUses() const { auto iter = use_begin(), end = use_end(); for (unsigned i = 0; i < 2; ++i) { diff --git a/include/swift/SIL/SwiftObjectHeader.h b/include/swift/SIL/SwiftObjectHeader.h new file mode 100644 index 0000000000000..73e6f07f26902 --- /dev/null +++ b/include/swift/SIL/SwiftObjectHeader.h @@ -0,0 +1,36 @@ +//===--- SwiftObjectHeader.h - Defines SwiftObjectHeader ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +#ifndef SWIFT_SIL_SWIFTOBJECTHEADER_H +#define SWIFT_SIL_SWIFTOBJECTHEADER_H + +#include "BridgedSwiftObject.h" + +/// The C++ version of SwiftObject. +/// +/// It is used for bridging the SIL core classes (e.g. SILFunction, SILNode, +/// etc.) with libswift. +/// For details see libswift/README.md. +/// +/// In C++ code, never use BridgedSwiftObject directly. SwiftObjectHeader has +/// the proper constructor, which avoids the header to be uninitialized. +struct SwiftObjectHeader : BridgedSwiftObject { + SwiftObjectHeader(SwiftMetatype metatype) { + this->metatype = metatype; + this->refCounts = ~(uint64_t)0; + } + + bool isBridged() const { + return metatype != nullptr; + } +}; + +#endif diff --git a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h index ac59ec026d00c..1ac6109973359 100644 --- a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h @@ -13,53 +13,19 @@ #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ALIASANALYSIS_H #define SWIFT_SILOPTIMIZER_ANALYSIS_ALIASANALYSIS_H -#include "swift/Basic/ValueEnumerator.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Analysis/Analysis.h" -#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" #include "llvm/ADT/DenseMap.h" -using swift::RetainObserveKind; - -namespace { - - /// A key used for the AliasAnalysis cache. - /// - /// This struct represents the argument list to the method 'alias'. The two - /// SILValue pointers are mapped to size_t indices because we need an - /// efficient way to invalidate them (the mechanism is described below). The - /// Type arguments are translated to void* because their underlying storage is - /// opaque pointers that never goes away. - struct AliasKeyTy { - // The SILValue pair: - size_t V1, V2; - // The TBAAType pair: - void *T1, *T2; - }; - - /// A key used for the MemoryBehavior Analysis cache. - /// - /// The two SILValue pointers are mapped to size_t indices because we need an - /// efficient way to invalidate them (the mechanism is described below). The - /// RetainObserveKind represents the inspection mode for the memory behavior - /// analysis. - struct MemBehaviorKeyTy { - // The SILValue pair: - size_t V1, V2; - }; -} - namespace swift { -class SILInstruction; -class ValueBase; class SideEffectAnalysis; class EscapeAnalysis; /// This class is a simple wrapper around an alias analysis cache. This is /// needed since we do not have an "analysis" infrastructure. -class AliasAnalysis : public SILAnalysis { +class AliasAnalysis { public: /// This enum describes the different kinds of aliasing relations between @@ -89,12 +55,28 @@ class AliasAnalysis : public SILAnalysis { }; private: - SILModule *Mod; - SideEffectAnalysis *SEA; - EscapeAnalysis *EA; + /// A key used for the AliasAnalysis cache. + /// + /// This struct represents the argument list to the method 'alias'. + struct AliasCacheKey { + // The SILValue pair: + SILValue V1, V2; + // The TBAAType pair: + void *T1, *T2; + }; + + friend struct ::llvm::DenseMapInfo; + + /// A key used for the MemoryBehavior Analysis cache. + using MemBehaviorCacheKey = std::pair; + + using ScopeCacheKey = std::pair; using TBAACacheKey = std::pair; + SideEffectAnalysis *SEA; + EscapeAnalysis *EA; + /// A cache for the computation of TBAA. True means that the types may /// alias. False means that the types must not alias. /// @@ -105,20 +87,27 @@ class AliasAnalysis : public SILAnalysis { /// AliasAnalysis value cache. /// /// The alias() method uses this map to cache queries. - llvm::DenseMap AliasCache; + llvm::DenseMap AliasCache; using MemoryBehavior = SILInstruction::MemoryBehavior; + /// MemoryBehavior value cache. /// /// The computeMemoryBehavior() method uses this map to cache queries. - llvm::DenseMap MemoryBehaviorCache; + llvm::DenseMap MemoryBehaviorCache; - /// The caches can't directly map a pair of value/instruction pointers - /// to results because we'd like to be able to remove deleted instruction - /// pointers without having to scan the whole map. So, instead of storing - /// pointers we map pointers to indices and store the indices. - ValueEnumerator ValueToIndex; - ValueEnumerator InstructionToIndex; + /// Set of instructions inside immutable-scopes. + /// + /// Contains pairs of intructions: the first instruction is the begin-scope + /// instruction (e.g. begin_access), the second instruction is an + /// instruction inside the scope (only may-write instructions are considered). + llvm::DenseSet instsInImmutableScopes; + + /// Computed immutable scopes. + /// + /// Contains the begin-scope instructions (e.g. begin_access) of all computed + /// scopes. + llvm::SmallPtrSet immutableScopeComputed; AliasResult aliasAddressProjection(SILValue V1, SILValue V2, SILValue O1, SILValue O2); @@ -131,30 +120,15 @@ class AliasAnalysis : public SILAnalysis { /// Returns True if memory of type \p T1 and \p T2 may alias. bool typesMayAlias(SILType T1, SILType T2, const SILFunction &F); - virtual void handleDeleteNotification(SILNode *node) override; - - virtual bool needsNotifications() override { return true; } + void computeImmutableScope(SingleValueInstruction *beginScopeInst); + bool isInImmutableScope(SILInstruction *inst, SILValue V); public: - AliasAnalysis(SILModule *M) - : SILAnalysis(SILAnalysisKind::Alias), Mod(M), SEA(nullptr), EA(nullptr) { - } + AliasAnalysis(SideEffectAnalysis *SEA, EscapeAnalysis *EA) + : SEA(SEA), EA(EA) {} - static bool classof(const SILAnalysis *S) { - return S->getKind() == SILAnalysisKind::Alias; - } - - virtual void initialize(SILPassManager *PM) override; - - /// Explicitly invalidate an instruction. - /// - /// This can be useful to update the alias analysis within a pass. - /// It's needed if e.g. \p inst is an address projection and its operand gets - /// replaced with a different underlying object. - void invalidateInstruction(SILInstruction *inst) { - handleDeleteNotification(inst->asSILNode()); - } + static SILAnalysisKind getAnalysisKind() { return SILAnalysisKind::Alias; } /// Perform an alias query to see if V1, V2 refer to the same values. AliasResult alias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(), @@ -232,35 +206,6 @@ class AliasAnalysis : public SILAnalysis { /// Returns true if \p Ptr may be released by the builtin \p BI. bool canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr); - - /// Encodes the alias query as a AliasKeyTy. - /// The parameters to this function are identical to the parameters of alias() - /// and this method serializes them into a key for the alias analysis cache. - AliasKeyTy toAliasKey(SILValue V1, SILValue V2, SILType Type1, SILType Type2); - - /// Encodes the memory behavior query as a MemBehaviorKeyTy. - MemBehaviorKeyTy toMemoryBehaviorKey(SILInstruction *V1, SILValue V2); - - virtual void invalidate() override { - AliasCache.clear(); - MemoryBehaviorCache.clear(); - InstructionToIndex.clear(); - ValueToIndex.clear(); - } - - virtual void invalidate(SILFunction *, - SILAnalysis::InvalidationKind K) override { - invalidate(); - } - - /// Notify the analysis about a newly created function. - virtual void notifyAddedOrModifiedFunction(SILFunction *F) override {} - - /// Notify the analysis about a function which will be deleted from the - /// module. - virtual void notifyWillDeleteFunction(SILFunction *F) override {} - - virtual void invalidateFunctionTables() override { } }; @@ -274,51 +219,32 @@ SILType computeTBAAType(SILValue V); } // end namespace swift namespace llvm { - template <> struct DenseMapInfo { - static inline AliasKeyTy getEmptyKey() { - auto Allone = std::numeric_limits::max(); - return {0, Allone, nullptr, nullptr}; - } - static inline AliasKeyTy getTombstoneKey() { - auto Allone = std::numeric_limits::max(); - return {Allone, 0, nullptr, nullptr}; - } - static unsigned getHashValue(const AliasKeyTy Val) { - unsigned H = 0; - H ^= DenseMapInfo::getHashValue(Val.V1); - H ^= DenseMapInfo::getHashValue(Val.V2); - H ^= DenseMapInfo::getHashValue(Val.T1); - H ^= DenseMapInfo::getHashValue(Val.T2); - return H; - } - static bool isEqual(const AliasKeyTy LHS, const AliasKeyTy RHS) { - return LHS.V1 == RHS.V1 && - LHS.V2 == RHS.V2 && - LHS.T1 == RHS.T1 && - LHS.T2 == RHS.T2; - } - }; +template <> struct DenseMapInfo { + using AliasCacheKey = swift::AliasAnalysis::AliasCacheKey; - template <> struct DenseMapInfo { - static inline MemBehaviorKeyTy getEmptyKey() { - auto Allone = std::numeric_limits::max(); - return {0, Allone}; - } - static inline MemBehaviorKeyTy getTombstoneKey() { - auto Allone = std::numeric_limits::max(); - return {Allone, 0}; - } - static unsigned getHashValue(const MemBehaviorKeyTy V) { - unsigned H = 0; - H ^= DenseMapInfo::getHashValue(V.V1); - H ^= DenseMapInfo::getHashValue(V.V2); - return H; - } - static bool isEqual(const MemBehaviorKeyTy LHS, - const MemBehaviorKeyTy RHS) { - return LHS.V1 == RHS.V1 && LHS.V2 == RHS.V2; - } - }; + static inline AliasCacheKey getEmptyKey() { + return {DenseMapInfo::getEmptyKey(), swift::SILValue(), + nullptr, nullptr}; + } + static inline AliasCacheKey getTombstoneKey() { + return {DenseMapInfo::getTombstoneKey(), swift::SILValue(), + nullptr, nullptr}; + } + static unsigned getHashValue(const AliasCacheKey Val) { + unsigned H = 0; + H ^= DenseMapInfo::getHashValue(Val.V1); + H ^= DenseMapInfo::getHashValue(Val.V2); + H ^= DenseMapInfo::getHashValue(Val.T1); + H ^= DenseMapInfo::getHashValue(Val.T2); + return H; + } + static bool isEqual(const AliasCacheKey LHS, const AliasCacheKey RHS) { + return LHS.V1 == RHS.V1 && + LHS.V2 == RHS.V2 && + LHS.T1 == RHS.T1 && + LHS.T2 == RHS.T2; + } +}; } #endif diff --git a/include/swift/SILOptimizer/Analysis/Analysis.h b/include/swift/SILOptimizer/Analysis/Analysis.h index 938f4f9e1a2b8..e0c5c931574e5 100644 --- a/include/swift/SILOptimizer/Analysis/Analysis.h +++ b/include/swift/SILOptimizer/Analysis/Analysis.h @@ -36,7 +36,7 @@ struct SILAnalysisKind { }; /// The base class for all SIL-level analysis. -class SILAnalysis : public DeleteNotificationHandler { +class SILAnalysis { public: /// This is a list of values that allow passes to communicate to analysis /// which traits of the code were invalidated. Based on this information diff --git a/include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h b/include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h index eb4f5775912e5..8b564792c860d 100644 --- a/include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h @@ -37,22 +37,43 @@ class SILWitnessTable; /// allows a client to determine whether the list is incomplete in the /// sense that there may be unrepresented callees. class CalleeList { - llvm::TinyPtrVector CalleeFunctions; - bool IsIncomplete; + friend class CalleeCache; + + using Callees = llvm::SmallVector; + + void *functionOrCallees; + + enum class Kind : uint8_t { + empty, + singleFunction, + multipleCallees + } kind; + + bool incomplete; + + CalleeList(void *ptr, Kind kind, bool isIncomplete) : + functionOrCallees(ptr), kind(kind), incomplete(isIncomplete) {} public: /// Constructor for when we know nothing about the callees and must /// assume the worst. - CalleeList() : IsIncomplete(true) {} + CalleeList() : CalleeList(nullptr, Kind::empty, /*incomplete*/ true) {} /// Constructor for the case where we know an apply can target only /// a single function. - CalleeList(SILFunction *F) : CalleeFunctions(F), IsIncomplete(false) {} + CalleeList(SILFunction *F) + : CalleeList(F, Kind::singleFunction, /*incomplete*/ false) {} /// Constructor for arbitrary lists of callees. - CalleeList(llvm::SmallVectorImpl &List, bool IsIncomplete) - : CalleeFunctions(llvm::makeArrayRef(List.begin(), List.end())), - IsIncomplete(IsIncomplete) {} + CalleeList(Callees *callees, bool IsIncomplete) + : CalleeList(callees, Kind::multipleCallees, IsIncomplete) {} + + static CalleeList fromOpaque(void *ptr, unsigned char kind, unsigned char isComplete) { + return CalleeList(ptr, (Kind)kind, (bool)isComplete); + } + + void *getOpaquePtr() const { return functionOrCallees; } + unsigned char getOpaqueKind() const { return (unsigned char)kind; } SWIFT_DEBUG_DUMP; @@ -60,15 +81,29 @@ class CalleeList { /// Return an iterator for the beginning of the list. ArrayRef::iterator begin() const { - return CalleeFunctions.begin(); + switch (kind) { + case Kind::empty: + return nullptr; + case Kind::singleFunction: + return (SILFunction * const *)&functionOrCallees; + case Kind::multipleCallees: + return ((Callees *)functionOrCallees)->begin(); + } } /// Return an iterator for the end of the list. ArrayRef::iterator end() const { - return CalleeFunctions.end(); + switch (kind) { + case Kind::empty: + return nullptr; + case Kind::singleFunction: + return (SILFunction * const *)&functionOrCallees + 1; + case Kind::multipleCallees: + return ((Callees *)functionOrCallees)->end(); + } } - bool isIncomplete() const { return IsIncomplete; } + bool isIncomplete() const { return incomplete; } /// Returns true if all callees are known and not external. bool allCalleesVisible() const; @@ -80,14 +115,13 @@ class CalleeList { /// any function application site (including those that are simple /// function_ref, thin_to_thick, or partial_apply callees). class CalleeCache { - using Callees = llvm::SmallVector; - using CalleesAndCanCallUnknown = llvm::PointerIntPair; + using CalleesAndCanCallUnknown = llvm::PointerIntPair; using CacheType = llvm::DenseMap; SILModule &M; // Allocator for the SmallVectors that we will be allocating. - llvm::SpecificBumpPtrAllocator Allocator; + llvm::SpecificBumpPtrAllocator Allocator; // The cache of precomputed callee lists for function decls appearing // in class virtual dispatch tables and witness tables. @@ -106,6 +140,11 @@ class CalleeCache { /// Return the list of callees that can potentially be called at the /// given apply site. CalleeList getCalleeList(FullApplySite FAS) const; + + CalleeList getCalleeListOfValue(SILValue callee) const { + return getCalleeListForCalleeKind(callee); + } + /// Return the list of callees that can potentially be called at the /// given instruction. E.g. it could be destructors. CalleeList getCalleeList(SILInstruction *I) const; @@ -180,6 +219,11 @@ class BasicCalleeAnalysis : public SILAnalysis { return Cache->getCalleeList(FAS); } + CalleeList getCalleeListOfValue(SILValue callee) { + updateCache(); + return Cache->getCalleeListOfValue(callee); + } + CalleeList getCalleeList(SILInstruction *I) { updateCache(); return Cache->getCalleeList(I); diff --git a/include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h b/include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h index d483ca695e2ea..2f752d4935431 100644 --- a/include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h @@ -235,12 +235,6 @@ class EpilogueARCFunctionInfo { llvm::DenseMap EpilogueReleaseInstCache; public: - void handleDeleteNotification(SILNode *node) { - // Being conservative and clear everything for now. - EpilogueRetainInstCache.clear(); - EpilogueReleaseInstCache.clear(); - } - /// Constructor. EpilogueARCFunctionInfo(SILFunction *F, PostOrderAnalysis *PO, AliasAnalysis *AA, RCIdentityAnalysis *RC) @@ -273,38 +267,21 @@ class EpilogueARCFunctionInfo { }; class EpilogueARCAnalysis : public FunctionAnalysisBase { + /// Backlink to the pass manager. + SILPassManager *passManager = nullptr; /// Current post order analysis we are using. - PostOrderAnalysis *PO; - /// Current alias analysis we are using. - AliasAnalysis *AA; + PostOrderAnalysis *PO = nullptr; /// Current RC Identity analysis we are using. - RCIdentityAnalysis *RC; - + RCIdentityAnalysis *RC = nullptr; + public: EpilogueARCAnalysis(SILModule *) : FunctionAnalysisBase( - SILAnalysisKind::EpilogueARC), - PO(nullptr), AA(nullptr), RC(nullptr) {} + SILAnalysisKind::EpilogueARC) {} EpilogueARCAnalysis(const EpilogueARCAnalysis &) = delete; EpilogueARCAnalysis &operator=(const EpilogueARCAnalysis &) = delete; - virtual void handleDeleteNotification(SILNode *node) override { - // If the parent function of this instruction was just turned into an - // external declaration, bail. This happens during SILFunction destruction. - SILFunction *F = node->getFunction(); - if (F->isExternalDeclaration()) { - return; - } - - // If we do have an analysis, tell it to handle its delete notifications. - if (auto A = maybeGet(F)) { - A.get()->handleDeleteNotification(node); - } - } - - virtual bool needsNotifications() override { return true; } - static bool classof(const SILAnalysis *S) { return S->getKind() == SILAnalysisKind::EpilogueARC; } @@ -312,9 +289,7 @@ class EpilogueARCAnalysis : public FunctionAnalysisBase virtual void initialize(SILPassManager *PM) override; virtual std::unique_ptr - newFunctionAnalysis(SILFunction *F) override { - return std::make_unique(F, PO, AA, RC); - } + newFunctionAnalysis(SILFunction *F) override; virtual bool shouldInvalidate(SILAnalysis::InvalidationKind K) override { return true; diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 46c618b35f60d..ff62e3e4147f5 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -832,11 +832,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Propagates the escape states through the graph. void propagateEscapeStates(); - /// Remove a value from the graph. Do not delete the mapped node, but reset - /// mappedValue if it is set to this value, and make sure that the node - /// cannot be looked up with getNode(). - void removeFromGraph(ValueBase *V); - enum class Traversal { Follow, Backtrack, Halt }; /// Traverse backward from startNode, following predecessor edges. @@ -1237,10 +1232,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Notify the analysis about changed witness or vtables. virtual void invalidateFunctionTables() override { } - virtual void handleDeleteNotification(SILNode *N) override; - - virtual bool needsNotifications() override { return true; } - virtual void verify() const override; virtual void verify(SILFunction *F) const override; diff --git a/include/swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h b/include/swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h index b7d660e11982f..994a17924145e 100644 --- a/include/swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h @@ -88,7 +88,10 @@ class NonLocalAccessBlockAnalysis virtual void verify(NonLocalAccessBlocks *accessBlocks) const override { NonLocalAccessBlocks checkAccessBlocks(accessBlocks->function); checkAccessBlocks.compute(); - assert(checkAccessBlocks.accessBlocks == accessBlocks->accessBlocks); + assert(llvm::all_of(checkAccessBlocks.accessBlocks, + [&](SILBasicBlock *bb) { + return accessBlocks->accessBlocks.count(bb); + })); }) }; diff --git a/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h b/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h index 3064fc889abb2..9311142ccbdb5 100644 --- a/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h @@ -69,20 +69,6 @@ class RCIdentityFunctionInfo { /// intermediate array. void visitRCUses(SILValue V, function_ref Visitor); - void handleDeleteNotification(SILNode *node) { - auto value = dyn_cast(node); - if (!value) - return; - - // Check the cache. If we don't find it, there is nothing to do. - auto Iter = RCCache.find(SILValue(value)); - if (Iter == RCCache.end()) - return; - - // Then erase Iter from the cache. - RCCache.erase(Iter); - } - private: SILValue getRCIdentityRootInner(SILValue V, unsigned RecursionDepth); SILValue stripRCIdentityPreservingOps(SILValue V, unsigned RecursionDepth); @@ -104,18 +90,6 @@ class RCIdentityAnalysis : public FunctionAnalysisBase { RCIdentityAnalysis(const RCIdentityAnalysis &) = delete; RCIdentityAnalysis &operator=(const RCIdentityAnalysis &) = delete; - virtual void handleDeleteNotification(SILNode *node) override { - // If the parent function of this instruction was just turned into an - // external declaration, bail. This happens during SILFunction destruction. - SILFunction *F = node->getFunction(); - if (F->isExternalDeclaration()) { - return; - } - get(F)->handleDeleteNotification(node); - } - - virtual bool needsNotifications() override { return true; } - static bool classof(const SILAnalysis *S) { return S->getKind() == SILAnalysisKind::RCIdentity; } diff --git a/include/swift/SILOptimizer/Analysis/SimplifyInstruction.h b/include/swift/SILOptimizer/Analysis/SimplifyInstruction.h index cac8dc4d82ca5..f4b5361f788c6 100644 --- a/include/swift/SILOptimizer/Analysis/SimplifyInstruction.h +++ b/include/swift/SILOptimizer/Analysis/SimplifyInstruction.h @@ -25,7 +25,7 @@ namespace swift { class SILInstruction; -class InstModCallbacks; +struct InstModCallbacks; /// Replace an instruction with a simplified result and erase it. If the /// instruction initiates a scope, do not replace the end of its scope; it will diff --git a/include/swift/SILOptimizer/OptimizerBridging.h b/include/swift/SILOptimizer/OptimizerBridging.h new file mode 100644 index 0000000000000..04b07709418e7 --- /dev/null +++ b/include/swift/SILOptimizer/OptimizerBridging.h @@ -0,0 +1,75 @@ +//===--- OptimizerBridging.h - header for the OptimizerBridging module ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_OPTIMIZERBRIDGING_H +#define SWIFT_SILOPTIMIZER_OPTIMIZERBRIDGING_H + +#include "../SIL/SILBridging.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + BridgedFunction function; + BridgedPassContext passContext; +} BridgedFunctionPassCtxt; + +typedef struct { + BridgedInstruction instruction; + BridgedPassContext passContext; +} BridgedInstructionPassCtxt; + +typedef struct { + const void * _Nonnull aliasAnalysis; +} BridgedAliasAnalysis; + +typedef struct { + void * _Nullable bca; +} BridgedCalleeAnalysis; + +typedef struct { + void * _Nonnull opaquePtr; + unsigned char kind; + unsigned char incomplete; +} BridgedCalleeList; + +typedef void (* _Nonnull BridgedFunctionPassRunFn)(BridgedFunctionPassCtxt); +typedef void (* _Nonnull BridgedInstructionPassRunFn)(BridgedInstructionPassCtxt); + +void SILPassManager_registerFunctionPass(BridgedStringRef name, + BridgedFunctionPassRunFn runFn); + +void SILCombine_registerInstructionPass(BridgedStringRef name, + BridgedInstructionPassRunFn runFn); + +SwiftInt PassContext_isSwift51RuntimeAvailable(BridgedPassContext context); + +BridgedAliasAnalysis PassContext_getAliasAnalysis(BridgedPassContext context); + +BridgedMemoryBehavior AliasAnalysis_getMemBehavior(BridgedAliasAnalysis aa, + BridgedInstruction inst, + BridgedValue addr); + +BridgedCalleeAnalysis PassContext_getCalleeAnalysis(BridgedPassContext context); + +BridgedCalleeList CalleeAnalysis_getCallees(BridgedCalleeAnalysis calleeAnalysis, + BridgedValue callee); +SwiftInt BridgedFunctionArray_size(BridgedCalleeList callees); +BridgedFunction BridgedFunctionArray_get(BridgedCalleeList callees, + SwiftInt index); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index 8c82ff0169dc0..0bccf878b264b 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/SIL/Notifications.h" +#include "swift/SIL/InstructionUtils.h" #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/PassManager/PassPipeline.h" #include "swift/SILOptimizer/PassManager/Passes.h" @@ -31,6 +32,8 @@ class SILModule; class SILModuleTransform; class SILOptions; class SILTransform; +class SILPassManager; +class SILCombiner; namespace irgen { class IRGenModule; @@ -41,6 +44,49 @@ void executePassPipelinePlan(SILModule *SM, const SILPassPipelinePlan &plan, bool isMandatory = false, irgen::IRGenModule *IRMod = nullptr); +/// Utility class to invoke passes in libswift. +class LibswiftPassInvocation { + /// Backlink to the pass manager. + SILPassManager *passManager; + + /// The currently optimized function. + SILFunction *function = nullptr; + + /// Non-null if this is an instruction pass, invoked from SILCombine. + SILCombiner *silCombiner = nullptr; + + /// All slabs, allocated by the pass. + SILModule::SlabList allocatedSlabs; + +public: + LibswiftPassInvocation(SILPassManager *passManager, SILFunction *function, + SILCombiner *silCombiner) : + passManager(passManager), function(function), silCombiner(silCombiner) {} + + LibswiftPassInvocation(SILPassManager *passManager) : + passManager(passManager) {} + + SILPassManager *getPassManager() const { return passManager; } + + SILFunction *getFunction() const { return function; } + + FixedSizeSlab *allocSlab(FixedSizeSlab *afterSlab); + + FixedSizeSlab *freeSlab(FixedSizeSlab *slab); + + /// The top-level API to erase an instruction, called from the Swift pass. + void eraseInstruction(SILInstruction *inst); + + /// Called by the pass when changes are made to the SIL. + void notifyChanges(SILAnalysis::InvalidationKind invalidationKind); + + /// Called by the pass manager before the pass starts running. + void startPassRun(SILFunction *function); + + /// Called by the pass manager when the pass has finished. + void finishedPassRun(); +}; + /// The SIL pass manager. class SILPassManager { friend class ExecuteSILPipelineRequest; @@ -79,6 +125,13 @@ class SILPassManager { /// The number of passes run so far. unsigned NumPassesRun = 0; + /// For invoking Swift passes in libswift. + LibswiftPassInvocation libswiftPassInvocation; + + /// Change notifications, collected during a bridged pass run. + SILAnalysis::InvalidationKind changeNotifications = + SILAnalysis::InvalidationKind::Nothing; + /// A mask which has one bit for each pass. A one for a pass-bit means that /// the pass doesn't need to run, because nothing has changed since the /// previous run of that pass. @@ -132,6 +185,15 @@ class SILPassManager { llvm_unreachable("Unable to find analysis for requested type."); } + template + T *getAnalysis(SILFunction *f) { + for (SILAnalysis *A : Analyses) { + if (A->getKind() == T::getAnalysisKind()) + return static_cast *>(A)->get(f); + } + llvm_unreachable("Unable to find analysis for requested type."); + } + /// \returns the module that the pass manager owns. SILModule *getModule() { return Mod; } @@ -139,6 +201,10 @@ class SILPassManager { /// pass manager. irgen::IRGenModule *getIRGenModule() { return IRMod; } + LibswiftPassInvocation *getLibswiftPassInvocation() { + return &libswiftPassInvocation; + } + /// Restart the function pass pipeline on the same function /// that is currently being processed. void restartWithCurrentFunction(SILTransform *T); @@ -217,6 +283,11 @@ class SILPassManager { CompletedPassesMap[F].reset(); } + void notifyPassChanges(SILAnalysis::InvalidationKind invalidationKind) { + changeNotifications = (SILAnalysis::InvalidationKind) + (changeNotifications | invalidationKind); + } + /// Reset the state of the pass manager and remove all transformation /// owned by the pass manager. Analysis passes will be kept. void resetAndRemoveTransformations(); @@ -306,6 +377,11 @@ class SILPassManager { void viewCallGraph(); }; +inline void LibswiftPassInvocation:: +notifyChanges(SILAnalysis::InvalidationKind invalidationKind) { + passManager->notifyPassChanges(invalidationKind); +} + } // end namespace swift #endif diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 1828d6926a69f..a2916f90da4cf 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -43,6 +43,52 @@ #define IRGEN_PASS(Id, Tag, Description) PASS(Id, Tag, Description) #endif +/// SWIFT_FUNCTION_PASS(Id, Tag, Description) +/// This macro follows the same conventions as PASS(Id, Tag, Description), +/// but is used for function passes which are implemented in libswift. +/// +/// No further code is need on the C++ side. In libswift a function pass with +/// the same name must be registered with 'registerPass()'. +/// +#ifndef SWIFT_FUNCTION_PASS +#define SWIFT_FUNCTION_PASS(Id, Tag, Description) PASS(Id, Tag, Description) +#endif + +/// SWIFT_FUNCTION_PASS_WITH_LEGACY(Id, Tag, Description) +/// Like SWIFT_FUNCTION_PASS, but the a C++ legacy pass is used if the not +/// built with libswift. +/// The C++ legacy creation function must be named 'createLegacy' +/// +#ifndef SWIFT_FUNCTION_PASS_WITH_LEGACY +#define SWIFT_FUNCTION_PASS_WITH_LEGACY(Id, Tag, Description) \ + SWIFT_FUNCTION_PASS(Id, Tag, Description) +#endif + +/// SWIFT_INSTRUCTION_PASS(Inst, Tag) +/// Similar to SWIFT_FUNCTION_PASS, but defines an instruction pass which is +/// implemented in libswift and is run by the SILCombiner. +/// The \p Inst argument specifies the instruction class, and \p Tag a name +/// for the pass. +/// +/// No further code is need on the C++ side. In libswift an instruction pass +/// with the same name must be registered with 'registerPass()'. +/// +#ifndef SWIFT_INSTRUCTION_PASS +#define SWIFT_INSTRUCTION_PASS(Inst, Tag) +#endif + +/// SWIFT_INSTRUCTION_PASS_WITH_LEGACY(Inst, Tag) +/// Like SWIFT_INSTRUCTION_PASS, but the a C++ legacy SILCombine visit +/// function is used if the not +/// built with libswift. +/// The C++ legacy visit function must be named +/// 'SILCombiner::legacyVisit'. +/// +#ifndef SWIFT_INSTRUCTION_PASS_WITH_LEGACY +#define SWIFT_INSTRUCTION_PASS_WITH_LEGACY(Inst, Tag) \ + SWIFT_INSTRUCTION_PASS(Inst, Tag) +#endif + /// PASS_RANGE(RANGE_ID, START, END) /// Pass IDs between PassKind::START and PassKind::END, inclusive, /// fall within the set known as @@ -250,7 +296,7 @@ PASS(MemBehaviorDumper, "mem-behavior-dump", "Print SIL Instruction MemBehavior from Alias Analysis over all Pairs") PASS(LSLocationPrinter, "lslocation-dump", "Print Load-Store Location Results Covering all Accesses") -PASS(MergeCondFails, "merge-cond_fails", +SWIFT_FUNCTION_PASS_WITH_LEGACY(MergeCondFails, "merge-cond_fails", "Merge SIL cond_fail to Eliminate Redundant Overflow Checks") PASS(MoveCondFailToPreds, "move-cond-fail-to-preds", "Move SIL cond_fail by Hoisting Checks") @@ -318,6 +364,8 @@ PASS(SILDebugInfoGenerator, "sil-debuginfo-gen", "Generate Debug Information with Source Locations into Textual SIL") PASS(EarlySROA, "early-sroa", "Scalar Replacement of Aggregate Stack Objects on high-level SIL") +SWIFT_FUNCTION_PASS(SILPrinter, "sil-printer", + "Test pass which prints the SIL of a function") PASS(SROA, "sroa", "Scalar Replacement of Aggregate Stack Objects") PASS(SROABBArgs, "sroa-bb-args", @@ -367,12 +415,20 @@ PASS(OptimizedMandatoryCombine, "optimized-mandatory-combine", "Perform -O level mandatory peephole combines") PASS(BugReducerTester, "bug-reducer-tester", "sil-bug-reducer Tool Testing by Asserting on a Sentinel Function") -PASS(OptRemarkGenerator, "sil-opt-remark-generator", - "Emit small peephole opt remarks that do not use large analyses") +PASS(AssemblyVisionRemarkGenerator, "assembly-vision-remark-generator", + "Emit assembly vision remarks that provide source level guidance of where runtime calls ended up") PASS(PruneVTables, "prune-vtables", "Mark class methods that do not require vtable dispatch") PASS_RANGE(AllPasses, AADumper, PruneVTables) +SWIFT_INSTRUCTION_PASS_WITH_LEGACY(GlobalValueInst, "simplify-global_value") +SWIFT_INSTRUCTION_PASS_WITH_LEGACY(StrongRetainInst, "simplify-strong_retain") +SWIFT_INSTRUCTION_PASS_WITH_LEGACY(StrongReleaseInst, "simplify-strong_release") + #undef IRGEN_PASS +#undef SWIFT_FUNCTION_PASS +#undef SWIFT_FUNCTION_PASS_WITH_LEGACY +#undef SWIFT_INSTRUCTION_PASS +#undef SWIFT_INSTRUCTION_PASS_WITH_LEGACY #undef PASS #undef PASS_RANGE diff --git a/include/swift/SILOptimizer/PassManager/Passes.h b/include/swift/SILOptimizer/PassManager/Passes.h index 063f1fd1d552a..1e99fe372318f 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.h +++ b/include/swift/SILOptimizer/PassManager/Passes.h @@ -74,7 +74,11 @@ namespace swift { StringRef PassKindID(PassKind Kind); StringRef PassKindTag(PassKind Kind); -#define PASS(ID, TAG, NAME) SILTransform *create##ID(); +#define PASS(ID, TAG, NAME) \ + SILTransform *create##ID(); +#define SWIFT_FUNCTION_PASS_WITH_LEGACY(ID, TAG, NAME) \ + PASS(ID, TAG, NAME) \ + SILTransform *createLegacy##ID(); #define IRGEN_PASS(ID, TAG, NAME) #include "Passes.def" diff --git a/include/swift/SILOptimizer/PassManager/Transforms.h b/include/swift/SILOptimizer/PassManager/Transforms.h index edc9177a11c4b..ce8d8169eb2a9 100644 --- a/include/swift/SILOptimizer/PassManager/Transforms.h +++ b/include/swift/SILOptimizer/PassManager/Transforms.h @@ -22,7 +22,7 @@ namespace swift { class PrettyStackTraceSILFunctionTransform; /// The base class for all SIL-level transformations. - class SILTransform : public DeleteNotificationHandler { + class SILTransform { public: /// The kind of transformation passes we use. enum class TransformKind { @@ -86,6 +86,9 @@ namespace swift { template T* getAnalysis() { return PM->getAnalysis(); } + template + T* getAnalysis(SILFunction *f) { return PM->getAnalysis(f); } + const SILOptions &getOptions() { return PM->getOptions(); } }; diff --git a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h index b2f31776e6e3c..b2ca777fc254c 100644 --- a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h +++ b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h @@ -170,6 +170,9 @@ class BasicBlockCloner : public SILCloner { /// state is reset each time analyzeAddressProjections is called. SinkAddressProjections sinkProj; + // If available, the current DeadEndBlocks for incremental update. + DeadEndBlocks *deBlocks; + public: /// An ordered list of old to new available value pairs. /// @@ -178,8 +181,8 @@ class BasicBlockCloner : public SILCloner { SmallVector, 16> availVals; // Clone blocks starting at `origBB`, within the same function. - BasicBlockCloner(SILBasicBlock *origBB) - : SILCloner(*origBB->getParent()), origBB(origBB) {} + BasicBlockCloner(SILBasicBlock *origBB, DeadEndBlocks *deBlocks = nullptr) + : SILCloner(*origBB->getParent()), origBB(origBB), deBlocks(deBlocks) {} bool canCloneBlock() { for (auto &inst : *origBB) { @@ -218,6 +221,12 @@ class BasicBlockCloner : public SILCloner { successorBBs.reserve(origBB->getSuccessors().size()); llvm::copy(origBB->getSuccessors(), std::back_inserter(successorBBs)); cloneReachableBlocks(origBB, successorBBs, insertAfterBB); + + if (deBlocks) { + for (auto *succBB : successorBBs) { + deBlocks->updateForReachableBlock(succBB); + } + } } /// Clone the given branch instruction's destination block, splitting diff --git a/include/swift/SILOptimizer/Utils/CFGOptUtils.h b/include/swift/SILOptimizer/Utils/CFGOptUtils.h index 9a44802cacbe7..318de8a9641ff 100644 --- a/include/swift/SILOptimizer/Utils/CFGOptUtils.h +++ b/include/swift/SILOptimizer/Utils/CFGOptUtils.h @@ -36,7 +36,7 @@ namespace swift { class DominanceInfo; class SILLoop; class SILLoopInfo; -class InstModCallbacks; +struct InstModCallbacks; /// Adds a new argument to an edge between a branch and a destination /// block. Allows for user injected callbacks via \p callbacks. @@ -47,21 +47,7 @@ class InstModCallbacks; /// \return The created branch. The old branch is deleted. /// The argument is appended at the end of the argument tuple. TermInst *addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest, - SILValue val, InstModCallbacks &callbacks); - -/// Adds a new argument to an edge between a branch and a destination -/// block. -/// -/// \param branch The terminator to add the argument to. -/// \param dest The destination block of the edge. -/// \param val The value to the arguments of the branch. -/// \return The created branch. The old branch is deleted. -/// The argument is appended at the end of the argument tuple. -inline TermInst *addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest, - SILValue val) { - InstModCallbacks callbacks; - return addNewEdgeValueToBranch(branch, dest, val, callbacks); -} + SILValue val, InstructionDeleter &deleter); /// Changes the edge value between a branch and destination basic block /// at the specified index. Changes all edges from \p Branch to \p Dest to carry diff --git a/include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h b/include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h index 41ad76d4529b9..ff283bc349d36 100644 --- a/include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h +++ b/include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2021 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 @@ -10,52 +10,60 @@ // //===----------------------------------------------------------------------===// /// -/// Canonicalize the copies and destroys of a single owned or guaranteed OSSA -/// value. +/// Canonicalize the copies and destroys of a single owned OSSA value. /// /// This top-level API rewrites the extended OSSA lifetime of a SILValue: /// -/// void canonicalizeValueLifetime(SILValue def, CanonicalOSSALifetime &) +/// void canonicalizeValueLifetime(SILValue def, CanonicalizeOSSALifetime &) /// -/// The extended lifetime transitively includes the uses of `def` itself along -/// with the uses of any copies of `def`. Canonicalization provably minimizes -/// the OSSA lifetime and its copies by rewriting all copies and destroys. Only -/// consusming uses that are not on the liveness boundary require a copy. +/// The "extended lifetime" of the references defined by 'def' transitively +/// includes the uses of 'def' itself along with the uses of any copies of +/// 'def'. Canonicalization provably minimizes the OSSA lifetime and its copies +/// by rewriting all copies and destroys. Only consusming uses that are not on +/// the liveness boundary require a copy. /// -/// Example #1: Handle consuming and nonconsuming uses. +/// Example #1: The last consuming use ends the reference lifetime. /// /// bb0(%arg : @owned $T, %addr : @trivial $*T): /// %copy = copy_value %arg : $T -/// debug_value %copy : $T /// store %copy to [init] %addr : $*T -/// debug_value %arg : $T -/// debug_value_addr %addr : $*T +/// debug_value %addr : $*T, expr op_deref /// destroy_value %arg : $T /// /// Will be transformed to: /// /// bb0(%arg : @owned $T, %addr : @trivial $*T): -/// // The original copy is deleted. -/// debug_value %arg : $T -/// // A new copy_value is inserted before the consuming store. -/// %copy = copy_value %arg : $T /// store %copy to [init] %addr : $*T -/// // The non-consuming use now uses the original value. -/// debug_value %arg : $T -/// // A new destroy is inserted after the last use. +/// debug_value %addr : $*T, expr op_deref +/// +/// Example #2: Destroys are hoisted to the last use. Copies are inserted only +/// at consumes within the lifetime (to directly satisfy ownership conventions): +/// +/// bb0(%arg : @owned $T, %addr : @trivial $*T): +/// %copy1 = copy_value %arg : $T +/// store %arg to [init] %addr : $*T +/// %_ = apply %_(%copy1) : $@convention(thin) (@guaranteed T) -> () +/// debug_value %addr : $*T, expr op_deref +/// destroy_value %copy1 : $T +/// +/// Will be transformed to: +/// +/// bb0(%arg : @owned $T, %addr : @trivial $*T): +/// %copy1 = copy_value %arg : $T +/// store %copy1 to [init] %addr : $*T +/// %_ = apply %_(%arg) : $@convention(thin) (@guaranteed T) -> () /// destroy_value %arg : $T -/// debug_value_addr %addr : $*T -/// // The original destroy is deleted. +/// debug_value %addr : $*T, expr op_deref /// -/// Example #2: Handle control flow. +/// Example #3: Handle control flow. /// /// bb0(%arg : @owned $T): /// cond_br %_, bb1, bb2 /// bb1: /// br bb3 /// bb2: -/// debug_value %arg : $T /// %copy = copy_value %arg : $T +/// %_ = apply %_(%copy) : $@convention(thin) (@guaranteed T) -> () /// destroy_value %copy : $T /// br bb3 /// bb3: @@ -69,30 +77,26 @@ /// destroy_value %arg : $T /// br bb3 /// bb2: -/// // The original copy is deleted. -/// debug_value %arg : $T +/// %_ = apply %_(%arg) : $@convention(thin) (@guaranteed T) -> () /// destroy_value %arg : $T /// br bb3 /// bb2: /// // The original destroy is deleted. /// -/// FIXME: Canonicalization currently bails out if any uses of the def has -/// OperandOwnership::PointerEscape. Once project_box is protected by a borrow -/// scope and mark_dependence is associated with an end_dependence, -/// canonicalization will work everywhere as intended. The intention is to keep -/// the canonicalization algorithm as simple and robust, leaving the remaining -/// performance opportunities contingent on fixing the SIL representation. +/// Pass Requirements: /// -/// FIXME: Canonicalization currently fails to eliminate copies downstream of a -/// ForwardingBorrow. Aggregates should be fixed to be Reborrow instead of -/// ForwardingBorrow, then computeCanonicalLiveness() can be fixed to extend -/// liveness through ForwardingBorrows. +/// This utility invalidates instructions but both uses and preserves +/// NonLocalAccessBlockAnalysis. +/// +/// The use-def walks in this utility, e.g. getCanonicalCopiedDef, assume no +/// cycles in the data flow graph without a phi. /// //===----------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_UTILS_CANONICALOSSALIFETIME_H #define SWIFT_SILOPTIMIZER_UTILS_CANONICALOSSALIFETIME_H +#include "swift/Basic/DAGNodeWorklist.h" #include "swift/Basic/SmallPtrSetVector.h" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" @@ -104,23 +108,11 @@ namespace swift { -/// Convert this struct_extract into a copy+destructure. Return the destructured -/// result or invalid SILValue. The caller must delete the extract and its -/// now-dead copy use. -/// -/// If a copied-def is a struct-extract, attempt a destructure conversion -/// %extract = struct_extract %... : $TypeWithSingleOwnershipValue -/// %copy = copy_value %extract : $OwnershipValue -/// To: -/// %copy = copy_value %extract : $TypeWithSingleOwnershipValue -/// (%extracted,...) = destructure %copy : $TypeWithSingleOwnershipValue -/// -/// \p instModCallbacks If non-null, this routine uses -/// InstModCallbacks::{setUseValue,RAUW}() internally to modify code. Otherwise, -/// just performs standard operations. -SILValue -convertExtractToDestructure(StructExtractInst *extract, - InstModCallbacks *instModCallbacks = nullptr); +extern llvm::Statistic NumCopiesEliminated; +extern llvm::Statistic NumCopiesGenerated; + +/// Insert a copy on this operand. Trace and update stats. +void copyLiveUse(Operand *use, InstModCallbacks &instModCallbacks); /// Information about consumes on the extended-lifetime boundary. Consuming uses /// within the lifetime are not included--they will consume a copy after @@ -137,12 +129,6 @@ class CanonicalOSSAConsumeInfo { /// Record any debug_value instructions found after a final consume. SmallVector debugAfterConsume; - /// For borrowed defs, track per-block copies of the borrowed value that only - /// have uses outside the borrow scope and will not be removed by - /// canonicalization. These copies are effectively distinct OSSA lifetimes - /// that should be canonicalized separately. - llvm::SmallDenseMap persistentCopies; - /// The set of non-destroy consumes that need to be poisoned. This is /// determined in two steps. First findOrInsertDestroyInBlock() checks if the /// lifetime shrank within the block. Second rewriteCopies() checks if the @@ -151,15 +137,17 @@ class CanonicalOSSAConsumeInfo { SmallPtrSetVector needsPoisonConsumes; public: - bool hasUnclaimedConsumes() const { return !finalBlockConsumes.empty(); } - void clear() { finalBlockConsumes.clear(); debugAfterConsume.clear(); - persistentCopies.clear(); needsPoisonConsumes.clear(); } + bool empty() { + return finalBlockConsumes.empty() && debugAfterConsume.empty() + && needsPoisonConsumes.empty(); + } + void recordNeedsPoison(SILInstruction *consume) { needsPoisonConsumes.insert(consume); } @@ -172,7 +160,7 @@ class CanonicalOSSAConsumeInfo { return needsPoisonConsumes.getArrayRef(); } - bool hasFinalConsumes() const { return !finalBlockConsumes.empty(); } + bool hasUnclaimedConsumes() const { return !finalBlockConsumes.empty(); } void recordFinalConsume(SILInstruction *inst) { assert(!finalBlockConsumes.count(inst->getParent())); @@ -207,81 +195,52 @@ class CanonicalOSSAConsumeInfo { return debugAfterConsume; } - bool hasPersistentCopies() const { return !persistentCopies.empty(); } - - bool isPersistentCopy(CopyValueInst *copy) const { - auto iter = persistentCopies.find(copy->getParent()); - if (iter == persistentCopies.end()) { - return false; - } - return iter->second == copy; - } - SWIFT_ASSERT_ONLY_DECL(void dump() const LLVM_ATTRIBUTE_USED); }; -// Worklist of pointer-like things that have an invalid default value. Avoid -// revisiting nodes--suitable for DAGs, but pops finished nodes without -// preserving them in the vector. -// -// The primary API has two methods: intialize() and pop(). Others are provided -// for flexibility. -// -// TODO: make this a better utility. -template struct PtrWorklist { - SmallPtrSet ptrVisited; - SmallVector ptrVector; - - PtrWorklist() = default; - - PtrWorklist(const PtrWorklist &) = delete; - - void initialize(T t) { - clear(); - insert(t); - } - - template void initializeRange(R &&range) { - clear(); - ptrVisited.insert(range.begin(), range.end()); - ptrVector.append(range.begin(), range.end()); - } - - T pop() { return empty() ? T() : ptrVector.pop_back_val(); } - - bool empty() const { return ptrVector.empty(); } - - unsigned size() const { return ptrVector.size(); } - - void clear() { - ptrVector.clear(); - ptrVisited.clear(); - } - - void insert(T t) { - if (ptrVisited.insert(t).second) - ptrVector.push_back(t); - } -}; - /// Canonicalize OSSA lifetimes. /// /// Allows the allocation of analysis state to be reused across calls to /// canonicalizeValueLifetime(). +/// +/// TODO: Move all the private per-definition members into an implementation +/// class in the .cpp file. class CanonicalizeOSSALifetime { public: - /// Find the original definition of a potentially copied value. - static SILValue getCanonicalCopiedDef(SILValue v); + /// Find the original definition of a potentially copied value. \p copiedValue + /// must be an owned value. It is usually a copy but may also be a destroy. + /// + /// This single helper specifies the root of an owned extended lifetime. Owned + /// extended lifetimes may not overlap. This ensures that canonicalization can + /// run as a utility without invalidating instruction worklists in the client, + /// as long as the client works on each canonical def independently. + /// + /// If the source of a copy is guaranteed, then the copy itself is the root of + /// an owned extended lifetime. Note that it will also be part of a borrowed + /// extended lifetime, which will be canonicalized separately by + /// CanonicalizeBorrowScope. + /// + /// This use-def walk must be consistent with the def-use walks performed + /// within the canonicalizeValueLifetime() and canonicalizeBorrowScopes() + /// implementations. + static SILValue getCanonicalCopiedDef(SILValue v) { + while (auto *copy = dyn_cast(v)) { + auto def = copy->getOperand(); + if (def.getOwnershipKind() != OwnershipKind::Owned) { + // This guaranteed value cannot be handled, treat the copy as an owned + // live range def instead. + return copy; + } + v = def; + } + return v; + } private: /// If true, then debug_value instructions outside of non-debug /// liveness may be pruned during canonicalization. bool pruneDebugMode; - /// If true, borrows scopes will be canonicalized allowing copies of - /// guaranteed values to be eliminated. - bool canonicalizeBorrowMode; - /// If true, then new destroy_value instructions will be poison. bool poisonRefsMode; @@ -290,17 +249,13 @@ class CanonicalizeOSSALifetime { // extendLivenessThroughOverlappingAccess is invoked. NonLocalAccessBlocks *accessBlocks = nullptr; - DominanceAnalysis *dominanceAnalysis; + DominanceInfo *domTree; + + InstructionDeleter &deleter; /// Current copied def for which this state describes the liveness. SILValue currentDef; - /// If an outer copy is created for uses outside the borrow scope - CopyValueInst *outerCopy = nullptr; - - /// Cumulatively, have any instructions been modified by canonicalization? - bool changed = false; - /// Original points in the CFG where the current value's lifetime is consumed /// or destroyed. For guaranteed values it remains empty. A backward walk from /// these blocks must discover all uses on paths that lead to a return or @@ -315,11 +270,11 @@ class CanonicalizeOSSALifetime { /// outisde the pruned liveness at the time it is discovered. llvm::SmallPtrSet debugValues; - /// Reuse a general visited set for def-use traversal. - PtrWorklist defUseWorklist; + /// Visited set for general def-use traversal that prevents revisiting values. + DAGNodeWorklist defUseWorklist; - /// Reuse a general worklist for CFG traversal. - PtrWorklist blockWorklist; + /// Visited set general CFG traversal that prevents revisiting blocks. + DAGNodeWorklist blockWorklist; /// Pruned liveness for the extended live range including copies. For this /// purpose, only consuming instructions are considered "lifetime @@ -336,25 +291,17 @@ class CanonicalizeOSSALifetime { /// may be in PrunedLiveness::LiveWithin). SmallSetVector remnantLiveOutBlocks; - /// Information about consuming instructions discovered in this caonical OSSA + /// Information about consuming instructions discovered in this canonical OSSA /// lifetime. CanonicalOSSAConsumeInfo consumes; - /// The callbacks to use when deleting/rauwing instructions. - InstModCallbacks instModCallbacks; - public: - CanonicalizeOSSALifetime( - bool pruneDebugMode, bool canonicalizeBorrowMode, bool poisonRefsMode, - NonLocalAccessBlockAnalysis *accessBlockAnalysis, - DominanceAnalysis *dominanceAnalysis, - InstModCallbacks instModCallbacks = InstModCallbacks()) - : pruneDebugMode(pruneDebugMode), - canonicalizeBorrowMode(canonicalizeBorrowMode), - poisonRefsMode(poisonRefsMode), - accessBlockAnalysis(accessBlockAnalysis), - dominanceAnalysis(dominanceAnalysis), - instModCallbacks(instModCallbacks) {} + CanonicalizeOSSALifetime(bool pruneDebugMode, bool poisonRefsMode, + NonLocalAccessBlockAnalysis *accessBlockAnalysis, + DominanceInfo *domTree, InstructionDeleter &deleter) + : pruneDebugMode(pruneDebugMode), poisonRefsMode(poisonRefsMode), + accessBlockAnalysis(accessBlockAnalysis), domTree(domTree), + deleter(deleter) {} SILValue getCurrentDef() const { return currentDef; } @@ -366,7 +313,6 @@ class CanonicalizeOSSALifetime { consumes.clear(); currentDef = def; - outerCopy = nullptr; liveness.initializeDefBlock(def->getParentBlock()); } @@ -377,30 +323,21 @@ class CanonicalizeOSSALifetime { remnantLiveOutBlocks.clear(); } - bool hasChanged() const { return changed; } - - void setChanged() { changed = true; } - - SILValue createdOuterCopy() const { return outerCopy; } - /// Top-Level API: rewrites copies and destroys within \p def's extended /// lifetime. \p lifetime caches transient analysis state across multiple /// calls. /// - /// Return false if the OSSA structure cannot be recognized (with a proper - /// OSSA representation this will always return true). + /// Return true if any change was made to \p def's extended lifetime. \p def + /// itself will not be deleted and no instructions outside of \p def's + /// extended lifetime will be affected (only copies and destroys are + /// rewritten). /// - /// Upon returning, isChanged() indicates, cumulatively, whether any SIL - /// changes were made. - /// - /// Upon returning, createdOuterCopy() indicates whether a new copy was - /// created for uses outside the borrow scope. To canonicalize the new outer - /// lifetime, call this API again on the value defined by the new copy. + /// This only deletes instructions within \p def's extended lifetime. Use + /// InstructionDeleter::cleanUpDeadInstructions() to recursively delete dead + /// operands. bool canonicalizeValueLifetime(SILValue def); - /// Return the inst mod callbacks struct used by this CanonicalizeOSSALifetime - /// to pass to other APIs that need to compose with CanonicalizeOSSALifetime. - InstModCallbacks getInstModCallbacks() const { return instModCallbacks; } + InstModCallbacks &getCallbacks() { return deleter.getCallbacks(); } protected: void recordDebugValue(DebugValueInst *dvi) { @@ -410,20 +347,6 @@ class CanonicalizeOSSALifetime { void recordConsumingUse(Operand *use) { consumingBlocks.insert(use->getUser()->getParent()); } - - bool computeBorrowLiveness(); - - bool consolidateBorrowScope(); - - bool findBorrowScopeUses(llvm::SmallPtrSetImpl &useInsts); - - void filterOuterBorrowUseInsts( - llvm::SmallPtrSetImpl &outerUseInsts); - - void rewriteOuterBorrowUsesAndFindConsumes( - SILValue incomingValue, - llvm::SmallPtrSetImpl &outerUseInsts); - bool computeCanonicalLiveness(); bool endsAccessOverlappingPrunedBoundary(SILInstruction *inst); @@ -442,29 +365,6 @@ class CanonicalizeOSSALifetime { void injectPoison(); }; -/// Canonicalize the passed in set of defs, eliminating in one batch any that -/// are not needed given the canonical lifetime of the various underlying owned -/// value introducers. -/// -/// On success, returns the invalidation kind that the caller must use to -/// invalidate analyses. Currently it will only ever return -/// SILAnalysis::InvalidationKind::Instructions or None. -/// -/// NOTE: This routine is guaranteed to not invalidate -/// NonLocalAccessBlockAnalysis, so callers should lock it before invalidating -/// instructions. E.x.: -/// -/// accessBlockAnalysis->lockInvalidation(); -/// pass->invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); -/// accessBlockAnalysis->unlockInvalidation(); -/// -/// NOTE: We assume that all \p inputDefs is a set (that is it has no duplicate -/// elements) and that all values have been generated by running a copy through -/// CanonicalizeOSSALifetime::getCanonicalCopiedDef(copy)). -SILAnalysis::InvalidationKind -canonicalizeOSSALifetimes(CanonicalizeOSSALifetime &canonicalizeLifetime, - ArrayRef inputDefs); - } // end namespace swift #endif diff --git a/include/swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h b/include/swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h new file mode 100644 index 0000000000000..b7c1beeb7d31f --- /dev/null +++ b/include/swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h @@ -0,0 +1,155 @@ +//===--- CanonicalizeBorrowScope.h - Canonicalize OSSA borrows --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 utility canonicalizes borrow scopes by rewriting them to restrict them +/// to only the uses within the scope. To do this, it hoists forwarding +/// operations out of the scope. This exposes many useless scopes that can be +/// deleted, which in turn allows canonicalization of the outer owned values +/// (via CanonicalizeOSSALifetime). +/// +/// This does not shrink borrow scopes; it does not rewrite end_borrows. +/// +/// TODO: A separate utility to shrink borrow scopes should eventually run +/// before this utility. It should hoist end_borrow up to the latest "destroy +/// barrier" whenever the scope does not contain a PointerEscape. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEBORROWSCOPES_H +#define SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEBORROWSCOPES_H + +#include "swift/Basic/DAGNodeWorklist.h" +#include "swift/Basic/SmallPtrSetVector.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" +#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/PrunedLiveness.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" + +namespace swift { + +//===----------------------------------------------------------------------===// +// MARK: CanonicalizeBorrowScope +//===----------------------------------------------------------------------===// + +class CanonicalizeBorrowScope { +public: + /// Return true if \p inst is an instructions that forwards ownership is its + /// first operand and can be trivially duplicated, sunk to its uses, hoisted + /// outside a borrow scope and have it's ownership kind flipped from + /// guaranteed to owned if needed, as long as OSSA invariants are preserved. + static bool isRewritableOSSAForward(SILInstruction *inst); + + /// Return the root of a borrowed extended lifetime for \p def or invalid. + /// + /// \p def may be any guaranteed value. + static SILValue getCanonicalBorrowedDef(SILValue def); + +private: + // The borrow that begins this scope. + BorrowedValue borrowedValue; + + /// Pruned liveness for the extended live range including copies. For this + /// purpose, only consuming instructions are considered "lifetime + /// ending". end_borrows do not end a liverange that may include owned copies. + PrunedLiveness liveness; + + InstructionDeleter &deleter; + + /// Visited set for general def-use traversal that prevents revisiting values. + DAGNodeWorklist defUseWorklist; + + /// Visited set general CFG traversal that prevents revisiting blocks. + DAGNodeWorklist blockWorklist; + + /// Record any copies outside the borrow scope that were updated. This + /// includes the outer copy that us used by outer uses and copies for any + /// hoisted forwarding instructions. + SmallVector updatedCopies; + + /// For borrowed defs, track per-block copies of the borrowed value that only + /// have uses outside the borrow scope and will not be removed by + /// canonicalization. These copies are effectively distinct OSSA lifetimes + /// that should be canonicalized separately. + llvm::SmallDenseMap persistentCopies; + +public: + CanonicalizeBorrowScope(InstructionDeleter &deleter) : deleter(deleter) {} + + BorrowedValue getBorrowedValue() const { return borrowedValue; } + + const PrunedLiveness &getLiveness() const { return liveness; } + + InstructionDeleter &getDeleter() { return deleter; } + + InstModCallbacks &getCallbacks() { return deleter.getCallbacks(); } + + /// Top-level entry point for canonicalizing a SILFunctionArgument. This is a + /// straightforward canonicalization that can be performed as a stand-alone + /// utility anywhere. + bool canonicalizeFunctionArgument(SILFunctionArgument *arg); + + /// Top-level entry point for canonicalizing any borrow scope. + /// + /// This creates OSSA compensation code outside the borrow scope. It should + /// only be called within copy propagation, which knows how to cleanup any new + /// copies outside the borrow scope, along with hoisted forwarding + /// instructions, etc. + bool canonicalizeBorrowScope(BorrowedValue borrow); + + bool hasPersistentCopies() const { return !persistentCopies.empty(); } + + bool isPersistentCopy(CopyValueInst *copy) const { + auto iter = persistentCopies.find(copy->getParent()); + if (iter == persistentCopies.end()) { + return false; + } + return iter->second == copy; + } + + // Get all the copies that inserted during canonicalizeBorrowScopes(). These + // are cleared at the next call to canonicalizeBorrowScopes(). + ArrayRef getUpdatedCopies() const { return updatedCopies; } + + using OuterUsers = llvm::SmallPtrSet; + + SILValue findDefInBorrowScope(SILValue value); + + template + bool visitBorrowScopeUses(SILValue innerValue, Visitor &visitor); + + void beginVisitBorrowScopeUses() { defUseWorklist.clear(); } + + void recordOuterCopy(CopyValueInst *copy) { updatedCopies.push_back(copy); } + +protected: + void initBorrow(BorrowedValue borrow) { + assert(borrow && liveness.empty() && persistentCopies.empty()); + + updatedCopies.clear(); + borrowedValue = borrow; + liveness.initializeDefBlock(borrowedValue->getParentBlock()); + } + + bool computeBorrowLiveness(); + + void filterOuterBorrowUseInsts(OuterUsers &outerUseInsts); + + bool consolidateBorrowScope(); +}; + +} // namespace swift + +#endif // SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEBORROWSCOPES_H diff --git a/include/swift/SILOptimizer/Utils/DebugOptUtils.h b/include/swift/SILOptimizer/Utils/DebugOptUtils.h index 2f4033c8e707a..181150a1ac248 100644 --- a/include/swift/SILOptimizer/Utils/DebugOptUtils.h +++ b/include/swift/SILOptimizer/Utils/DebugOptUtils.h @@ -43,6 +43,10 @@ inline void deleteAllDebugUses(SILInstruction *inst, } } +/// Transfer debug info associated with (the result of) \p I to a +/// new `debug_value` instruction before \p I is deleted. +void salvageDebugInfo(SILInstruction *I); + /// Erases the instruction \p I from it's parent block and deletes it, including /// all debug instructions which use \p I. /// Precondition: The instruction may only have debug instructions as uses. @@ -76,6 +80,7 @@ eraseFromParentWithDebugInsts(SILInstruction *inst, // Just matching what eraseFromParentWithDebugInsts is today. if (nextII == inst->getIterator()) ++nextII; + swift::salvageDebugInfo(inst); callbacks.deleteInst(inst, false /*do not notify*/); return nextII; } diff --git a/include/swift/SILOptimizer/Utils/InstModCallbacks.h b/include/swift/SILOptimizer/Utils/InstModCallbacks.h new file mode 100644 index 0000000000000..5a053fb550d4e --- /dev/null +++ b/include/swift/SILOptimizer/Utils/InstModCallbacks.h @@ -0,0 +1,278 @@ +//===--- InstModCallbacks.h - intruction modification callbacks -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +/// +/// InstModCallbacks: callbacks for instruction modification. +/// +/// Callbacks are generally problematic because a pass cannot anticipate the +/// state that SIL will be in when lower-level utilties invoke the +/// callback. This creates implicit coupling across the layers of SIL utilities. +/// +/// Alternatives: +/// +/// For an Analyses that caches SILInstruction pointers, check +/// SILInstruction::isDeleted() upon retrieval, and use the PassManager's +/// analysis invalidation mechanism to clear the pointers at the end of each +/// pass. The pointers remain valid in the "deleted" state until the end the +/// pass. +/// +/// For iterating over instructions during instruction creation and deletion, +/// use an UpdatingInstructionIterator provided by the InstructionDeleter +/// object: +/// +/// for (SILInstruction *inst : deleter.updatingRange(bb)) ... +/// +/// Make sure the pass uses the same deleter object for all deletion within the +/// iterator scope. +/// +/// To defer instruction deletion so that deletions happen in bulk at a +/// convenient point in the pass, use InstructionDeleter::trackIfDead() and +/// cleanupDeadInstructions(). +/// +/// To determine whether multiple layers of utilities made any change to the +/// SIL, have each utility report whether it made a change. +/// +/// For uses that don't fall into the categories above, restructure the pass so +/// that low-level operations on individual instructions don't require +/// callbacks. The SILCombine worklist is currently the main client of +/// callbacks. It's possible to work around this by running more complex +/// utilities as a separate SILCombine subpass in between draining the worklist +/// so those utilities do not require callbacks. +/// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/SILInstruction.h" +#include + +#ifndef SWIFT_SILOPTIMIZER_UTILS_INSTMODCALLBACKS_H +#define SWIFT_SILOPTIMIZER_UTILS_INSTMODCALLBACKS_H + +namespace swift { + +/// A structure containing callbacks that are called when an instruction is +/// removed or added. +/// +/// PERFORMANCE NOTES: This code can be used in loops, so we want to make sure +/// to not have overhead when the user does not specify a callback. To do that +/// instead of defining a "default" std::function, we represent the "default" +/// functions as nullptr. Then, in the helper function trampoline that actually +/// gets called, we check if we have a nullptr and if we do, we perform the +/// default operation inline. What is nice about this from a perf perspective is +/// that in a loop this property should predict well since you have a single +/// branch that is going to go the same way everytime. +struct InstModCallbacks { + /// A function that is called to notify that a new function was created. + /// + /// Default implementation is a no-op, but we still mark madeChange. + std::function createdNewInstFunc; + + /// A function sets the value in \p use to be \p newValue. + /// + /// Default implementation just calls use->set(newValue). + /// + /// NOTE: It is assumed that this operation will never invalidate instruction + /// iterators. + /// + /// This can have compile-time implications and should be avoided + /// whenever possible in favor of more structured optimization passes. + std::function setUseValueFunc; + + /// A function that takes in an instruction and deletes the inst. + /// + /// This is used to invalidate dangling instruction pointers. The SIL will be + /// invalid when it is invoked. The callback is only allowed to inspect the + /// inline fields of \p instToDelete and iterate over the results. It is not + /// allowed to dereference operands or iterate uses. + /// + /// See comments for notifyWillBeDeletedFunc. + /// + /// The default implementation is: + /// + /// instToDelete->eraseFromParent(); + /// + /// The reason this callback is reponsible for deleting the instruction is to + /// interoperate more easily with + /// CanonicalizeInstruction::killInstruction(). This allows updates to choose + /// whether to happen before or after deleting the instruction and possibly + /// keep it around as a zombie object. All implementations must at least + /// immediately remove all references to the instruction, including the parent + /// block list. + /// + /// TODO: Now that instructions deletion can be delayed via + /// SILModule::scheduleForDeletion(); there's no longer a good use case for + /// calling eraseFromParent() within this callback. Rewrite all clients + /// without doing the instruction deletion within the callback. + std::function deleteInstFunc; + + /// If non-null, called before a salient instruction is deleted or has its + /// references dropped. If null, no-op. + /// + /// This can be used to respond to dead instructions that will be deleted in + /// the future. Unlike deleteInstFunc, the SIL will be in a valid + /// state. However, arbitrary SIL transformations may happen between this + /// invocation and actual instruction deletion. + /// + /// This callback is not guaranteed to be called for every deleted + /// instruction. It cannot be used to invalidate dangling pointers. It is only + /// called for "salient" instructions that likely create additional + /// optimization opportunities when deleted. If a dead def-use chain is + /// deleted, notification only occurs for the initial def. + /// + /// This is used in rare circumstances to update an optimization worklist. It + /// should be avoided whenever possible in favor of more structured + /// optimization passes. + std::function + notifyWillBeDeletedFunc; + + /// A boolean that tracks if any of our callbacks were ever called. + bool wereAnyCallbacksInvoked = false; + + InstModCallbacks() = default; + ~InstModCallbacks() = default; + InstModCallbacks(const InstModCallbacks &) = default; + + /// Return a copy of self with deleteInstFunc set to \p newDeleteInstFunc. + LLVM_ATTRIBUTE_UNUSED InstModCallbacks + onDelete(decltype(deleteInstFunc) newDeleteInstFunc) const { + InstModCallbacks result = *this; + result.deleteInstFunc = newDeleteInstFunc; + return result; + } + + /// Return a copy of self with createdNewInstFunc set to \p + /// newCreatedNewInstFunc. + LLVM_ATTRIBUTE_UNUSED InstModCallbacks + onCreateNewInst(decltype(createdNewInstFunc) newCreatedNewInstFunc) const { + InstModCallbacks result = *this; + result.createdNewInstFunc = newCreatedNewInstFunc; + return result; + } + + /// Return a copy of self with setUseValueFunc set to \p newSetUseValueFunc. + LLVM_ATTRIBUTE_UNUSED InstModCallbacks + onSetUseValue(decltype(setUseValueFunc) newSetUseValueFunc) const { + InstModCallbacks result = *this; + result.setUseValueFunc = newSetUseValueFunc; + return result; + } + + /// Return a copy of self with notifyWillBeDeletedFunc set to \p + /// newNotifyWillBeDeletedFunc. + LLVM_ATTRIBUTE_UNUSED + InstModCallbacks onNotifyWillBeDeleted( + decltype(notifyWillBeDeletedFunc) newNotifyWillBeDeletedFunc) const { + InstModCallbacks result = *this; + result.notifyWillBeDeletedFunc = newNotifyWillBeDeletedFunc; + return result; + } + + void deleteInst(SILInstruction *instToDelete, + bool notifyWhenDeleting = true) { + wereAnyCallbacksInvoked = true; + if (notifyWhenDeleting && notifyWillBeDeletedFunc) + notifyWillBeDeletedFunc(instToDelete); + if (deleteInstFunc) + return deleteInstFunc(instToDelete); + instToDelete->eraseFromParent(); + } + + void createdNewInst(SILInstruction *newlyCreatedInst) { + wereAnyCallbacksInvoked = true; + if (createdNewInstFunc) + createdNewInstFunc(newlyCreatedInst); + } + + void setUseValue(Operand *use, SILValue newValue) { + wereAnyCallbacksInvoked = true; + if (setUseValueFunc) + return setUseValueFunc(use, newValue); + use->set(newValue); + } + + /// Notify via our callbacks that an instruction will be deleted/have its + /// operands dropped. + /// + /// DISCUSSION: Since we do not delete instructions in any specific order, we + /// drop all references of the instructions before we call deleteInst. Thus + /// one can not in deleteInst look at operands. Certain parts of the optimizer + /// rely on this ability, so we preserve it. + void notifyWillBeDeleted(SILInstruction *instThatWillBeDeleted) { + wereAnyCallbacksInvoked = true; + if (notifyWillBeDeletedFunc) + return notifyWillBeDeletedFunc(instThatWillBeDeleted); + } + + void replaceValueUsesWith(SILValue oldValue, SILValue newValue) { + wereAnyCallbacksInvoked = true; + + // If setUseValueFunc is not set, just call RAUW directly. RAUW in this case + // is equivalent to what we do below. We just enable better + // performance. This ensures that the default InstModCallback is really + // fast. + if (!setUseValueFunc) + return oldValue->replaceAllUsesWith(newValue); + + while (!oldValue->use_empty()) { + auto *use = *oldValue->use_begin(); + setUseValue(use, newValue); + } + } + + /// Replace all uses of the results of \p oldInst pairwise with new uses of + /// the results of \p newInst. + /// + /// If \p setUseValueFunc is not set to a value, we just call inline + /// SILInstruction::replaceAllUsesPairwiseWith(...) to ensure we only pay a + /// cost if we actually set setUseValueFunc. + void replaceAllInstUsesPairwiseWith(SILInstruction *oldInst, SILInstruction *newInst) { + wereAnyCallbacksInvoked = true; + + // If setUseValueFunc is not set, just call RAUW directly. RAUW in this case + // is equivalent to what we do below. We just enable better + // performance. This ensures that the default InstModCallback is really + // fast. + if (!setUseValueFunc) + return oldInst->replaceAllUsesPairwiseWith(newInst); + + auto results = oldInst->getResults(); + + // If we don't have any results, fast-path out without asking the other + // instruction for its results. + if (results.empty()) { + assert(newInst->getResults().empty()); + return; + } + + // Replace values with the corresponding values of the other instruction. + auto otherResults = newInst->getResults(); + assert(results.size() == otherResults.size()); + for (auto i : indices(results)) { + replaceValueUsesWith(results[i], otherResults[i]); + } + } + + void eraseAndRAUWSingleValueInst(SingleValueInstruction *oldInst, + SILValue newValue) { + wereAnyCallbacksInvoked = true; + replaceValueUsesWith(oldInst, newValue); + deleteInst(oldInst); + } + + bool hadCallbackInvocation() const { return wereAnyCallbacksInvoked; } + + /// Set \p wereAnyCallbacksInvoked to false. Useful if one wants to reuse an + /// InstModCallback in between iterations. + void resetHadCallbackInvocation() { wereAnyCallbacksInvoked = false; } +}; + +} // end namespace swift + +#endif diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index f5ea771cf6f67..5308c8b83deed 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -27,183 +27,16 @@ #include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h" #include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h" #include "swift/SILOptimizer/Analysis/SimplifyInstruction.h" +#include "swift/SILOptimizer/Utils/InstModCallbacks.h" +#include "swift/SILOptimizer/Utils/UpdatingInstructionIterator.h" #include "llvm/ADT/SmallPtrSet.h" -#include -#include namespace swift { class DominanceInfo; +class DeadEndBlocks; template class NullablePtr; -/// A structure containing callbacks that are called when an instruction is -/// removed or added. -/// -/// PERFORMANCE NOTES: This code can be used in loops, so we want to make sure -/// to not have overhead when the user does not specify a callback. To do that -/// instead of defining a "default" std::function, we represent the "default" -/// functions as nullptr. Then, in the helper function trampoline that actually -/// gets called, we check if we have a nullptr and if we do, we perform the -/// default operation inline. What is nice about this from a perf perspective is -/// that in a loop this property should predict well since you have a single -/// branch that is going to go the same way everytime. -class InstModCallbacks { - /// A function that is called to notify that a new function was created. - /// - /// Default implementation is a no-op, but we still mark madeChange. - std::function createdNewInstFunc; - - /// A function sets the value in \p use to be \p newValue. - /// - /// Default implementation just calls use->set(newValue). - /// - /// NOTE: It is assumed that this operation will never invalidate instruction - /// iterators. - std::function setUseValueFunc; - - /// A function that takes in an instruction and deletes the inst. - /// - /// Default implementation is instToDelete->eraseFromParent(); - /// - /// NOTE: The reason why we have deleteInstFunc and notifyWillBeDeletedFunc is - /// InstModCallback supports 2 stage deletion where a callee passed - /// InstModCallback is allowed to drop all references to the instruction - /// before calling deleteInstFunc. In contrast, notifyWillBeDeletedFunc - /// assumes that the IR is in a good form before being called so that the - /// caller can via the callback gather state about the instruction that will - /// be deleted. As an example, see InstructionDeleter::deleteInstruction() in - /// InstOptUtils.cpp. - std::function deleteInstFunc; - - /// If non-null, called before an instruction is deleted or has its references - /// dropped. If null, no-op. - /// - /// NOTE: The reason why we have deleteInstFunc and notifyWillBeDeletedFunc is - /// InstModCallback supports 2 stage deletion where a callee passed - /// InstModCallback is allowed to drop all references to the instruction - /// before calling deleteInstFunc. In contrast, notifyWillBeDeletedFunc - /// assumes that the IR is in a good form before being called so that the - /// caller can via the callback gather state about the instruction that will - /// be deleted. As an example, see InstructionDeleter::deleteInstruction() in - /// InstOptUtils.cpp. - /// - /// NOTE: This is called in InstModCallback::deleteInst() if one does not use - /// a default bool argument to disable the notification. In general that - /// should only be done when one is writing custom handling and is performing - /// the notification ones self. It is assumed that the notification will be - /// called with a valid instruction. - std::function - notifyWillBeDeletedFunc; - - /// A boolean that tracks if any of our callbacks were ever called. - bool wereAnyCallbacksInvoked = false; - -public: - InstModCallbacks() = default; - ~InstModCallbacks() = default; - InstModCallbacks(const InstModCallbacks &) = default; - - /// Return a copy of self with deleteInstFunc set to \p newDeleteInstFunc. - LLVM_ATTRIBUTE_UNUSED InstModCallbacks - onDelete(decltype(deleteInstFunc) newDeleteInstFunc) const { - InstModCallbacks result = *this; - result.deleteInstFunc = newDeleteInstFunc; - return result; - } - - /// Return a copy of self with createdNewInstFunc set to \p - /// newCreatedNewInstFunc. - LLVM_ATTRIBUTE_UNUSED InstModCallbacks - onCreateNewInst(decltype(createdNewInstFunc) newCreatedNewInstFunc) const { - InstModCallbacks result = *this; - result.createdNewInstFunc = newCreatedNewInstFunc; - return result; - } - - /// Return a copy of self with setUseValueFunc set to \p newSetUseValueFunc. - LLVM_ATTRIBUTE_UNUSED InstModCallbacks - onSetUseValue(decltype(setUseValueFunc) newSetUseValueFunc) const { - InstModCallbacks result = *this; - result.setUseValueFunc = newSetUseValueFunc; - return result; - } - - /// Return a copy of self with notifyWillBeDeletedFunc set to \p - /// newNotifyWillBeDeletedFunc. - LLVM_ATTRIBUTE_UNUSED - InstModCallbacks onNotifyWillBeDeleted( - decltype(notifyWillBeDeletedFunc) newNotifyWillBeDeletedFunc) const { - InstModCallbacks result = *this; - result.notifyWillBeDeletedFunc = newNotifyWillBeDeletedFunc; - return result; - } - - void deleteInst(SILInstruction *instToDelete, - bool notifyWhenDeleting = true) { - wereAnyCallbacksInvoked = true; - if (notifyWhenDeleting && notifyWillBeDeletedFunc) - notifyWillBeDeletedFunc(instToDelete); - if (deleteInstFunc) - return deleteInstFunc(instToDelete); - instToDelete->eraseFromParent(); - } - - void createdNewInst(SILInstruction *newlyCreatedInst) { - wereAnyCallbacksInvoked = true; - if (createdNewInstFunc) - createdNewInstFunc(newlyCreatedInst); - } - - void setUseValue(Operand *use, SILValue newValue) { - wereAnyCallbacksInvoked = true; - if (setUseValueFunc) - return setUseValueFunc(use, newValue); - use->set(newValue); - } - - /// Notify via our callbacks that an instruction will be deleted/have its - /// operands dropped. - /// - /// DISCUSSION: Since we do not delete instructions in any specific order, we - /// drop all references of the instructions before we call deleteInst. Thus - /// one can not in deleteInst look at operands. Certain parts of the optimizer - /// rely on this ability, so we preserve it. - void notifyWillBeDeleted(SILInstruction *instThatWillBeDeleted) { - wereAnyCallbacksInvoked = true; - if (notifyWillBeDeletedFunc) - return notifyWillBeDeletedFunc(instThatWillBeDeleted); - } - - void replaceValueUsesWith(SILValue oldValue, SILValue newValue) { - wereAnyCallbacksInvoked = true; - - // If setUseValueFunc is not set, just call RAUW directly. RAUW in this case - // is equivalent to what we do below. We just enable better - // performance. This ensures that the default InstModCallback is really - // fast. - if (!setUseValueFunc) - return oldValue->replaceAllUsesWith(newValue); - - while (!oldValue->use_empty()) { - auto *use = *oldValue->use_begin(); - setUseValue(use, newValue); - } - } - - void eraseAndRAUWSingleValueInst(SingleValueInstruction *oldInst, - SILValue newValue) { - wereAnyCallbacksInvoked = true; - replaceValueUsesWith(oldInst, newValue); - deleteInst(oldInst); - } - - bool hadCallbackInvocation() const { return wereAnyCallbacksInvoked; } - - /// Set \p wereAnyCallbacksInvoked to false. Useful if one wants to reuse an - /// InstModCallback in between iterations. - void resetHadCallbackInvocation() { wereAnyCallbacksInvoked = false; } -}; - /// Transform a Use Range (Operand*) into a User Range (SILInstruction *) using UserTransform = std::function; using ValueBaseUserRange = @@ -236,6 +69,10 @@ NullablePtr createDecrementBefore(SILValue ptr, /// Get the insertion point after \p val. Optional getInsertAfterPoint(SILValue val); +/// True if this instruction's only uses are debug_value (in -O mode), +/// destroy_value, end_lifetime or end-of-scope instruction such as end_borrow. +bool hasOnlyEndOfScopeOrEndOfLifetimeUses(SILInstruction *inst); + /// Return the number of @inout arguments passed to the given apply site. unsigned getNumInOutArguments(FullApplySite applySite); @@ -265,6 +102,9 @@ void recursivelyDeleteTriviallyDeadInstructions( SILInstruction *inst, bool force = false, InstModCallbacks callbacks = InstModCallbacks()); +/// True if this instruction can be deleted if all its uses can also be deleted. +bool isInstructionTriviallyDeletable(SILInstruction *inst); + /// Perform a fast local check to see if the instruction is dead. /// /// This routine only examines the state of the instruction at hand. @@ -279,11 +119,6 @@ bool isIntermediateRelease(SILInstruction *inst, EpilogueARCFunctionInfo *erfi); void collectUsesOfValue(SILValue V, llvm::SmallPtrSetImpl &Insts); -/// Recursively erase all of the uses of the instruction (but not the -/// instruction itself) -void eraseUsesOfInstruction(SILInstruction *inst, - InstModCallbacks callbacks = InstModCallbacks()); - /// Recursively erase all of the uses of the value (but not the /// value itself) void eraseUsesOfValue(SILValue value); @@ -368,53 +203,107 @@ SILLinkage getSpecializedLinkage(SILFunction *f, SILLinkage linkage); /// Tries to perform jump-threading on all checked_cast_br instruction in /// function \p Fn. bool tryCheckedCastBrJumpThreading( - SILFunction *fn, DominanceInfo *dt, - SmallVectorImpl &blocksForWorklist); + SILFunction *fn, DominanceInfo *dt, DeadEndBlocks *deBlocks, + SmallVectorImpl &blocksForWorklist, + bool EnableOSSARewriteTerminator); /// A utility for deleting one or more instructions belonging to a function, and /// cleaning up any dead code resulting from deleting those instructions. Use -/// this utility instead of -/// \c recursivelyDeleteTriviallyDeadInstruction. +/// this utility instead of \p recursivelyDeleteTriviallyDeadInstruction +/// as follows: +/// InstructionDeleter deleter; +/// deleter.deleteIfDead(instruction); +/// deleter.cleanupDeadInstructions(); +/// +/// This is designed to be used with a single 'onDelete' callback, which is +/// invoked consistently just before deleting each instruction. It's usually +/// used to avoid iterator invalidation (see the updatingIterator() factory +/// method). The other InstModCallbacks should generally be handled at a higher +/// level, and avoided altogether if possible. The following two are supported +/// for flexibility: +/// +/// callbacks.createdNewInst() is invoked incrementally when it fixes lifetimes +/// while deleting a set of instructions, but the SIL may still be invalid +/// relative to the new instruction. +/// +/// callbacks.notifyWillBeDeletedFunc() is invoked when a dead instruction is +/// first recognized and was not already passed in by the client. During the +/// callback, the to-be-deleted instruction has valid SIL. It's operands and +/// uses can be inspected and cached. It will be deleted later during +/// cleanupDeadInstructions(). +/// +/// Note that the forceDelete* APIs only invoke notifyWillBeDeletedFunc() when +/// an operand's definition will become dead after force-deleting the specified +/// instruction. Some clients force-delete related instructions one at a +/// time. It is the client's responsiblity to invoke notifyWillBeDeletedFunc() +/// on those explicitly deleted instructions if needed. class InstructionDeleter { -private: /// A set vector of instructions that are found to be dead. The ordering of /// instructions in this set is important as when a dead instruction is /// removed, new instructions will be generated to fix the lifetime of the /// instruction's operands. This has to be deterministic. SmallSetVector deadInstructions; - /// Callbacks used when adding/deleting instructions. - InstModCallbacks instModCallbacks; + UpdatingInstructionIteratorRegistry iteratorRegistry; public: - InstructionDeleter() : deadInstructions(), instModCallbacks() {} - InstructionDeleter(InstModCallbacks inputCallbacks) - : deadInstructions(), instModCallbacks(inputCallbacks) {} + InstructionDeleter(InstModCallbacks chainedCallbacks = InstModCallbacks()) + : deadInstructions(), iteratorRegistry(chainedCallbacks) {} + + UpdatingInstructionIteratorRegistry &getIteratorRegistry() { + return iteratorRegistry; + } + + InstModCallbacks &getCallbacks() { return iteratorRegistry.getCallbacks(); } + + llvm::iterator_range + updatingRange(SILBasicBlock *bb) { + return iteratorRegistry.makeIteratorRange(bb); + } + + llvm::iterator_range + updatingReverseRange(SILBasicBlock *bb) { + return iteratorRegistry.makeReverseIteratorRange(bb); + } + + bool hadCallbackInvocation() const { + return const_cast(this) + ->getCallbacks() + .hadCallbackInvocation(); + } /// If the instruction \p inst is dead, record it so that it can be cleaned /// up. - void trackIfDead(SILInstruction *inst); + /// + /// Calls callbacks.notifyWillBeDeleted(). + bool trackIfDead(SILInstruction *inst); - /// If the instruction \p inst is dead, delete it immediately and record - /// its operands so that they can be cleaned up later. - void deleteIfDead(SILInstruction *inst); + /// Track this instruction as dead even if it has side effects. Used to enable + /// the deletion of a bunch of instructions at the same time. + /// + /// Calls callbacks.notifyWillBeDeleted(). + void forceTrackAsDead(SILInstruction *inst); - /// Delete the instruction \p inst and record instructions that may become - /// dead because of the removal of \c inst. This function will add necessary - /// ownership instructions to fix the lifetimes of the operands of \c inst to - /// compensate for its deletion. This function will not clean up dead code - /// resulting from the instruction's removal. To do so, invoke the method \c - /// cleanupDeadCode of this instance, once the SIL of the contaning function - /// is made consistent. + /// If the instruction \p inst is dead, delete it immediately along with its + /// destroys and scope-ending uses. If any operand definitions will become + /// dead after deleting this instruction, track them so they can be deleted + /// later during cleanUpDeadInstructions(). /// - /// \pre the function containing \c inst must be using ownership SIL. + /// Calls callbacks.notifyWillBeDeleted(). + bool deleteIfDead(SILInstruction *inst); + + /// Delete the instruction \p inst, ignoring its side effects. If any operand + /// definitions will become dead after deleting this instruction, track them + /// so they can be deleted later during cleanUpDeadInstructions(). This + /// function will add necessary ownership instructions to fix the lifetimes of + /// the operands of \p inst to compensate for its deletion. + /// + /// \pre the function containing \p inst must be using ownership SIL. /// \pre the instruction to be deleted must not have any use other than /// incidental uses. /// - /// \p callback is called on each deleted instruction before deleting any - /// instructions. This way, the SIL is valid in the callback. However, the - /// callback cannot be used to update instruction iterators since other - /// instructions to be deleted remain in the instruction list. + /// callbacks.notifyWillBeDeleted will not be called for \p inst but will be + /// called for any other instructions that become dead as a result. void forceDeleteAndFixLifetimes(SILInstruction *inst); /// Delete the instruction \p inst and record instructions that may become @@ -429,11 +318,19 @@ class InstructionDeleter { /// /// \pre the instruction to be deleted must not have any use other than /// incidental uses. + /// + /// callbacks.notifyWillBeDeleted will not be called for \p inst but will be + /// called for any other instructions that become dead as a result. void forceDelete(SILInstruction *inst); - /// Force track this instruction as dead. Used to enable the deletion of a - /// bunch of instructions at the same time. - void forceTrackAsDead(SILInstruction *inst); + /// Recursively delete all of the uses of the instruction before deleting the + /// instruction itself. Does not fix lifetimes. + /// + /// callbacks.notifyWillBeDeleted will not be called for \p inst but will + /// be called for any other instructions that become dead as a result. + void forceDeleteWithUsers(SILInstruction *inst) { + deleteWithUses(inst, /*fixLifetimes*/ false, /*forceDeleteUsers*/ true); + } /// Clean up dead instructions that are tracked by this instance and all /// instructions that transitively become dead. @@ -442,19 +339,30 @@ class InstructionDeleter { /// under or over releases). Note that if \c forceDelete call leaves the /// function body in an inconsistent state, it needs to be made consistent /// before this method is invoked. - void cleanUpDeadInstructions(); + /// + /// callbacks.notifyWillBeDeletedFunc will only be called for instructions + /// that become dead during cleanup but were not already tracked. + void cleanupDeadInstructions(); - /// Recursively visit users of \c inst (including \c inst)and delete - /// instructions that are dead (including \c inst). + /// Recursively visit users of \p inst and delete instructions that are dead + /// including \p inst. + /// + /// callbacks.notifyWillBeDeletedFunc will be called for any dead + /// instructions. void recursivelyDeleteUsersIfDead(SILInstruction *inst); - /// Recursively visit users of \c inst (including \c inst)and force delete - /// them. Also, destroy the consumed operands of the deleted instructions + /// Recursively visit users of \p inst and force delete them including \p + /// inst. Also, destroy the consumed operands of the deleted instructions /// whenever necessary. + /// + /// callbacks.notifyWillBeDeletedFunc will not be called for \p inst or its + /// users but will be called for any other instructions that become dead as a + /// result. void recursivelyForceDeleteUsersAndFixLifetimes(SILInstruction *inst); private: - void deleteInstruction(SILInstruction *inst, bool fixOperandLifetimes); + void deleteWithUses(SILInstruction *inst, bool fixLifetimes, + bool forceDeleteUsers = false); }; /// If \c inst is dead, delete it and recursively eliminate all code that @@ -767,15 +675,10 @@ SILBasicBlock::iterator replaceSingleUse(Operand *use, SILValue newValue, SILValue makeCopiedValueAvailable(SILValue value, SILBasicBlock *inBlock); -/// Given a newly created @owned value \p value without any uses, this utility +/// Given an existing @owned value \p value, this utility /// inserts control equivalent copy and destroy at leaking blocks to adjust /// ownership and make \p value available for use at \p inBlock. -/// -/// inBlock must be the only point at which \p value will be consumed. If this -/// consuming point is within a loop, this will create and return a copy of \p -/// value inside \p inBlock. -SILValue -makeNewValueAvailable(SILValue value, SILBasicBlock *inBlock); +SILValue makeValueAvailable(SILValue value, SILBasicBlock *inBlock); /// Given an ssa value \p value, create destroy_values at leaking blocks /// diff --git a/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h b/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h index 38858762eb8bf..f10fc9f64f543 100644 --- a/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h +++ b/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h @@ -30,7 +30,6 @@ #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/Projection.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" -#include "swift/SILOptimizer/Analysis/EscapeAnalysis.h" #include "swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h" #include "swift/SILOptimizer/Analysis/ValueTracking.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" @@ -366,9 +365,6 @@ class LSLocation : public LSBase { /// Check whether the 2 LSLocations must alias each other or not. bool isMustAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA); - /// Check whether the LSLocation can escape the current function. - bool isNonEscapingLocalLSLocation(SILFunction *Fn, EscapeAnalysis *EA); - /// Expand this location to all individual fields it contains. /// /// In SIL, we can have a store to an aggregate and loads from its individual diff --git a/include/swift/SILOptimizer/Utils/OwnershipOptUtils.h b/include/swift/SILOptimizer/Utils/OwnershipOptUtils.h index 8369124609ea7..7e9c246198b21 100644 --- a/include/swift/SILOptimizer/Utils/OwnershipOptUtils.h +++ b/include/swift/SILOptimizer/Utils/OwnershipOptUtils.h @@ -144,6 +144,11 @@ class OwnershipRAUWHelper { private: SILBasicBlock::iterator replaceAddressUses(SingleValueInstruction *oldValue, SILValue newValue); + + void invalidate() { + ctx->clear(); + ctx = nullptr; + } }; /// A utility composed ontop of OwnershipFixupContext that knows how to replace @@ -175,6 +180,12 @@ class OwnershipReplaceSingleUseHelper { /// Perform the actual RAUW. SILBasicBlock::iterator perform(); + +private: + void invalidate() { + ctx->clear(); + ctx = nullptr; + } }; /// An abstraction over LoadInst/LoadBorrowInst so one can handle both types of diff --git a/include/swift/SILOptimizer/Utils/SILSSAUpdater.h b/include/swift/SILOptimizer/Utils/SILSSAUpdater.h index f595b817eda3c..79d7de17d0121 100644 --- a/include/swift/SILOptimizer/Utils/SILSSAUpdater.h +++ b/include/swift/SILOptimizer/Utils/SILSSAUpdater.h @@ -14,6 +14,7 @@ #define SWIFT_SIL_SILSSAUPDATER_H #include "llvm/Support/Allocator.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILValue.h" @@ -58,6 +59,10 @@ class SILSSAUpdater { // If not null updated with inserted 'phi' nodes (SILArgument). SmallVectorImpl *insertedPhis; + // Used to delete branch instructions when they are replaced for adding + // phi arguments. + InstructionDeleter deleter; + // Not copyable. void operator=(const SILSSAUpdater &) = delete; SILSSAUpdater(const SILSSAUpdater &) = delete; @@ -67,6 +72,8 @@ class SILSSAUpdater { SmallVectorImpl *insertedPhis = nullptr); ~SILSSAUpdater(); + InstructionDeleter &getDeleter() { return deleter; } + void setInsertedPhis(SmallVectorImpl *inputInsertedPhis) { insertedPhis = inputInsertedPhis; } diff --git a/include/swift/SILOptimizer/Utils/UpdatingInstructionIterator.h b/include/swift/SILOptimizer/Utils/UpdatingInstructionIterator.h new file mode 100644 index 0000000000000..240e522ef75d6 --- /dev/null +++ b/include/swift/SILOptimizer/Utils/UpdatingInstructionIterator.h @@ -0,0 +1,281 @@ +//===--- UpdatingInstructionIterator.h --------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +/// +/// Classes for tracking instruction iterators to be updated when instructions +/// are added or deleted. +/// +/// UpdatingInstructionIteratorRegistry typically resides in the currently +/// active InstructionDeleter object. +/// +/// UpdatingListIterator is the iterator adapter. +/// It is produced by: UpdatingInstructionIteratorRegistry::makeIterator() +/// +/// Iterators are typically encapsulated in a range returned by +/// UpdatingInstructionIteratorRegistry::iteratorRange() for use in +/// range-based for loops: +/// +/// for (SILInstruction *inst : registry.iteratorRange(bb)) ... +/// +/// Or more commonly, directly from the InstructionDeleter: +/// +/// for (SILInstruction *inst : deleter.updatingRange(bb)) ... +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_UTILS_UPDATINGINSTRUCTIONITERATOR_H +#define SWIFT_SILOPTIMIZER_UTILS_UPDATINGINSTRUCTIONITERATOR_H + +#include "swift/SIL/SILInstruction.h" + +namespace swift { + +/// Safely iterate over list elements while deleting elements and inserting new +/// elements. Advancing the iterator moves to the element following most +/// recently visited element. This holds even if the most recent element has +/// been deleted and new instructions have been added after its original +/// position. +/// +/// Iterator copies are expensive because each copy registers +/// itself in a central data structure. Postorder increment is currently +/// unavailable to avoid excessive copies. +/// +/// This adapter converts the base iterator's value_type into a pointer to the +/// same type. When the element has been deleted, dereferencing the iterator +/// returns a nullptr. This works with any bidirectional base iterator in which +/// the storage of each element is stable. Typically an llvm::ilist. The +/// implementation assumes that both forward and reverse iterators point to the +/// storage of the current element. +template +class UpdatingListIterator { + using Self = UpdatingListIterator; + + Registry *registry; + IteratorBase base; + bool isDeleted = false; + +public: + using value_type = typename IteratorBase::pointer; + using difference_type = ptrdiff_t; + using pointer = value_type *; + using iterator_category = std::bidirectional_iterator_tag; + + UpdatingListIterator(Registry ®istry, IteratorBase base) + : registry(®istry), base(base) { + registry.registerIterator(this); + } + + ~UpdatingListIterator() { + if (registry) + registry->destroyIterator(this); + } + + // Copying forces a new entry in the registry. + UpdatingListIterator(const Self &rhs) + : registry(rhs.registry), base(rhs.base), isDeleted(rhs.isDeleted) { + registry->registerIterator(this); + } + + Self &operator=(const Self &rhs) { + this->regsitry = rhs.registry; + this->base = rhs.base; + this->advanced = rhs.advanced; + registry->registerIterator(this); + return *this; + } + + /// Explicit conversion between forward/reverse iterators. + /// + /// Note that this does not produce the same result as getting the iterator's + /// instruction and producing a new iterator in the opposite + /// direction. Instead, the new iterator points to the previous instruction in + /// the original iteration order. This ensures that forward and reverse ranges + /// enclose the same set of instructions. + template + explicit UpdatingListIterator( + const UpdatingListIterator &rhs) + : UpdatingListIterator(IteratorBase(rhs.base)) {} + + /// This returns nullptr for deleted instructions. + value_type operator*() const { return isDeleted ? nullptr : &*base; } + + SILInstruction *operator->() const { return operator*(); } + + Self &operator++() { + advance(); + return *this; + } + + bool operator==(const Self &rhs) const { + return this->registry == rhs.registry && this->base == rhs.base + && this->isDeleted == rhs.isDeleted; + } + bool operator!=(const Self &rhs) const { return !(*this == rhs); } + + void advance() { + if (isDeleted) + isDeleted = false; + else + ++base; + } + + void notifyDelete(IteratorBase positionToDelete) { + if (base == positionToDelete) { + isDeleted = true; + ++base; + } + } + + void notifyNew(IteratorBase newPosition) { + if (isDeleted && std::prev(base) == newPosition) { + // The deleted forward iterator was already advanced. Move it back to the + // position of the new element. + --base; + } + } +}; + +class UpdatingInstructionIteratorRegistry; + +using UpdatingInstructionIterator = + UpdatingListIterator; +using UpdatingReverseInstructionIterator = + UpdatingListIterator; + +/// Track instruction iterators that need updating when intructions are added or +/// deleted. Iterators can be tracked across multiple levels of the call +/// stack. This registry object must outlive any iterators that it vends. +/// +/// While the registry is active, all instruction modificaiton must go through +/// its callbacks. +class UpdatingInstructionIteratorRegistry { + + /// Track iterators that need updating. Seldom expect to have more than 4 + /// (making a single range creates 4 but immediately discards 2). It is + /// possible for iterators to be copied and destroyed on each iteration of a + /// loop (although we should try hard to avoid that), so this does need to + /// immediately reuse old slots. + SmallVector forwardIterators; + SmallVector reverseIterators; + + /// Callbacks used when adding/deleting instructions. + InstModCallbacks callbacks; + +public: + UpdatingInstructionIteratorRegistry( + InstModCallbacks chainedCallbacks = InstModCallbacks()) { + rechainCallbacks(chainedCallbacks); + } + + // The callbacks capture 'this'. So copying is invalid. + UpdatingInstructionIteratorRegistry( + const UpdatingInstructionIteratorRegistry &) = delete; + + UpdatingInstructionIteratorRegistry & + operator=(const UpdatingInstructionIteratorRegistry &) = delete; + + InstModCallbacks &getCallbacks() { return callbacks; } + + void rechainCallbacks(InstModCallbacks chainedCallbacks) { + // Copy the two std::functions that we need. The rest of the callbacks are + // copied implicitly by assignment. + auto chainedDelete = chainedCallbacks.deleteInstFunc; + auto chainedNew = chainedCallbacks.createdNewInstFunc; + callbacks = chainedCallbacks + .onDelete([this, chainedDelete](SILInstruction *toDelete) { + notifyDelete(toDelete); + if (chainedDelete) { + chainedDelete(toDelete); + return; + } + toDelete->eraseFromParent(); + }) + .onCreateNewInst( + [this, chainedNew](SILInstruction *newlyCreatedInst) { + notifyNew(newlyCreatedInst); + if (chainedNew) { + chainedNew(newlyCreatedInst); + } + }); + } + + void registerIterator(UpdatingInstructionIterator *i) { + forwardIterators.push_back(i); + } + + void registerIterator(UpdatingReverseInstructionIterator *i) { + reverseIterators.push_back(i); + } + + void destroyIterator(UpdatingInstructionIterator *i) { + auto pos = std::find(forwardIterators.begin(), forwardIterators.end(), i); + assert(pos != forwardIterators.end() && "unregistered iterator"); + forwardIterators.erase(pos); + } + void destroyIterator(UpdatingReverseInstructionIterator *i) { + auto pos = std::find(reverseIterators.begin(), reverseIterators.end(), i); + assert(pos != reverseIterators.end() && "unregistered iterator"); + reverseIterators.erase(pos); + } + + UpdatingInstructionIterator makeIterator(SILBasicBlock::iterator i) { + return {*this, i}; + } + + UpdatingInstructionIterator makeIterator(SILInstruction *inst) { + return makeIterator(inst->getIterator()); + } + + UpdatingReverseInstructionIterator + makeReverseIterator(SILBasicBlock::reverse_iterator i) { + return {*this, i}; + } + + UpdatingReverseInstructionIterator + makeReverseIterator(SILInstruction *inst) { + return makeReverseIterator(inst->getReverseIterator()); + } + + llvm::iterator_range + makeIteratorRange(SILBasicBlock *bb) { + return {makeIterator(bb->begin()), makeIterator(bb->end())}; + } + + llvm::iterator_range + makeReverseIteratorRange(SILBasicBlock *bb) { + return {makeReverseIterator(bb->rbegin()), makeReverseIterator(bb->rend())}; + } + +protected: + void notifyDelete(SILInstruction *toDelete) { + for (auto *iterator : forwardIterators) { + iterator->notifyDelete(toDelete->getIterator()); + } + for (auto *iterator : reverseIterators) { + iterator->notifyDelete(toDelete->getReverseIterator()); + } + } + + void notifyNew(SILInstruction *newlyCreatedInst) { + for (auto *iterator : forwardIterators) { + iterator->notifyNew(newlyCreatedInst->getIterator()); + } + for (auto *iterator : reverseIterators) { + iterator->notifyNew(newlyCreatedInst->getReverseIterator()); + } + } +}; + +} // end namespace swift + +#endif diff --git a/include/swift/SILOptimizer/Utils/ValueLifetime.h b/include/swift/SILOptimizer/Utils/ValueLifetime.h index 8afe9f5d8fb00..7fba70b1d069b 100644 --- a/include/swift/SILOptimizer/Utils/ValueLifetime.h +++ b/include/swift/SILOptimizer/Utils/ValueLifetime.h @@ -37,6 +37,14 @@ namespace swift { /// Frontier implementation by adding a utility method that populates a vector /// of insertion points based on this boundary, where each each /// block-terminating lastUser adds an insertion point at each successor block. +/// +/// Note: A dead live range will have no last users and no boundary +/// edges. Always check if the definition passed to ValueLifetimeAnalysis is +/// dead before assuming that this boundary covers all points at which a value +/// must be destroyed. Normally, a live range with uses will have at least one +/// lastUser or boundaryEdge. But with infinite loops, it is possible for both +/// lastUsers and boundaryEdges to be empty even if there are uses within the +/// loop. struct ValueLifetimeBoundary { SmallVector lastUsers; SmallVector boundaryEdges; diff --git a/include/swift/Sema/CSBindings.h b/include/swift/Sema/CSBindings.h index 83f7688dd688f..62511ea90bfe2 100644 --- a/include/swift/Sema/CSBindings.h +++ b/include/swift/Sema/CSBindings.h @@ -391,6 +391,13 @@ class BindingSet { if (Bindings.empty()) return false; + // Literal requirements always result in a subtype/supertype + // relationship to a concrete type. + if (llvm::any_of(Literals, [](const auto &literal) { + return literal.second.viableAsBinding(); + })) + return false; + return llvm::all_of(Bindings, [](const PotentialBinding &binding) { return binding.BindingType->isExistentialType() && binding.Kind == AllowedBindingKind::Subtypes; diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index 9e2089211e94c..b92854294a09f 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -328,6 +328,10 @@ enum class FixKind : uint8_t { /// another property wrapper that is a part of the same composed /// property wrapper. AllowWrappedValueMismatch, + + /// Specify a type for an explicitly written placeholder that could not be + /// resolved. + SpecifyTypeForPlaceholder }; class ConstraintFix { @@ -2203,17 +2207,9 @@ class AllowKeyPathWithoutComponents final : public ConstraintFix { class IgnoreInvalidResultBuilderBody : public ConstraintFix { protected: - enum class ErrorInPhase { - PreCheck, - ConstraintGeneration, - }; - - ErrorInPhase Phase; - - IgnoreInvalidResultBuilderBody(ConstraintSystem &cs, ErrorInPhase phase, - ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::IgnoreInvalidResultBuilderBody, locator), - Phase(phase) {} + IgnoreInvalidResultBuilderBody(ConstraintSystem &cs, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::IgnoreInvalidResultBuilderBody, locator) {} public: std::string getName() const override { @@ -2226,19 +2222,8 @@ class IgnoreInvalidResultBuilderBody : public ConstraintFix { return diagnose(*commonFixes.front().first); } - static IgnoreInvalidResultBuilderBody * - duringPreCheck(ConstraintSystem &cs, ConstraintLocator *locator) { - return create(cs, ErrorInPhase::PreCheck, locator); - } - - static IgnoreInvalidResultBuilderBody * - duringConstraintGeneration(ConstraintSystem &cs, ConstraintLocator *locator) { - return create(cs, ErrorInPhase::ConstraintGeneration, locator); - } - -private: - static IgnoreInvalidResultBuilderBody * - create(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator); + static IgnoreInvalidResultBuilderBody *create(ConstraintSystem &cs, + ConstraintLocator *locator); }; class IgnoreResultBuilderWithReturnStmts final @@ -2247,8 +2232,7 @@ class IgnoreResultBuilderWithReturnStmts final IgnoreResultBuilderWithReturnStmts(ConstraintSystem &cs, Type builderTy, ConstraintLocator *locator) - : IgnoreInvalidResultBuilderBody(cs, ErrorInPhase::PreCheck, locator), - BuilderType(builderTy) {} + : IgnoreInvalidResultBuilderBody(cs, locator), BuilderType(builderTy) {} public: bool diagnose(const Solution &solution, bool asNote = false) const override; @@ -2277,6 +2261,25 @@ class SpecifyContextualTypeForNil final : public ConstraintFix { ConstraintLocator * locator); }; +class SpecifyTypeForPlaceholder final : public ConstraintFix { + SpecifyTypeForPlaceholder(ConstraintSystem &cs, ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyTypeForPlaceholder, locator) {} + +public: + std::string getName() const override { + return "specify type for placeholder"; + } + + bool diagnose(const Solution &solution, bool asNote = false) const override; + + bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override { + return diagnose(*commonFixes.front().first); + } + + static SpecifyTypeForPlaceholder *create(ConstraintSystem &cs, + ConstraintLocator *locator); +}; + class AllowRefToInvalidDecl final : public ConstraintFix { AllowRefToInvalidDecl(ConstraintSystem &cs, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::AllowRefToInvalidDecl, locator) {} diff --git a/include/swift/Sema/CodeCompletionTypeChecking.h b/include/swift/Sema/CodeCompletionTypeChecking.h index a9900f7a95a61..b54ade5688da0 100644 --- a/include/swift/Sema/CodeCompletionTypeChecking.h +++ b/include/swift/Sema/CodeCompletionTypeChecking.h @@ -89,21 +89,26 @@ namespace swift { /// formed during expression type-checking. class UnresolvedMemberTypeCheckCompletionCallback: public TypeCheckCompletionCallback { public: - struct Result { + struct ExprResult { Type ExpectedTy; bool IsImplicitSingleExpressionReturn; }; private: CodeCompletionExpr *CompletionExpr; - SmallVector Results; + SmallVector ExprResults; + SmallVector EnumPatternTypes; bool GotCallback = false; public: UnresolvedMemberTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr) : CompletionExpr(CompletionExpr) {} - ArrayRef getResults() const { return Results; } + ArrayRef getExprResults() const { return ExprResults; } + + /// If we are completing in a pattern matching position, the types of all + /// enums for whose cases are valid as an \c EnumElementPattern. + ArrayRef getEnumPatternTypes() const { return EnumPatternTypes; } /// True if at least one solution was passed via the \c sawSolution /// callback. @@ -116,6 +121,29 @@ namespace swift { void sawSolution(const constraints::Solution &solution) override; }; + class KeyPathTypeCheckCompletionCallback + : public TypeCheckCompletionCallback { + public: + struct Result { + /// The type on which completion should occur, i.e. a result type of the + /// previous component. + Type BaseType; + /// Whether code completion happens on the key path's root. + bool OnRoot; + }; + + private: + KeyPathExpr *KeyPath; + SmallVector Results; + + public: + KeyPathTypeCheckCompletionCallback(KeyPathExpr *KeyPath) + : KeyPath(KeyPath) {} + + ArrayRef getResults() const { return Results; } + + void sawSolution(const constraints::Solution &solution) override; + }; } #endif diff --git a/include/swift/Sema/Constraint.h b/include/swift/Sema/Constraint.h index 4be3f466c95f7..7d0037e38aa2b 100644 --- a/include/swift/Sema/Constraint.h +++ b/include/swift/Sema/Constraint.h @@ -154,9 +154,6 @@ enum class ConstraintKind : char { /// The first type is a function type, the second is the function's /// result type. FunctionResult, - /// The first type is a type that's a candidate to be the underlying type of - /// the second opaque archetype. - OpaqueUnderlyingType, /// The first type will be equal to the second type, but only when the /// second type has been fully determined (and mapped down to a concrete /// type). At that point, this constraint will be treated like an `Equal` @@ -604,7 +601,6 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::BindOverload: case ConstraintKind::OptionalObject: - case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: diff --git a/include/swift/Sema/ConstraintGraph.h b/include/swift/Sema/ConstraintGraph.h index f166723ad29a5..6c7c22574da3d 100644 --- a/include/swift/Sema/ConstraintGraph.h +++ b/include/swift/Sema/ConstraintGraph.h @@ -125,16 +125,16 @@ class ConstraintGraphNode { /// Infer bindings from the given constraint and notify referenced variables /// about its arrival (if requested). This happens every time a new constraint /// gets added to a constraint graph node. - void introduceToInference(Constraint *constraint, bool notifyReferencedVars); + void introduceToInference(Constraint *constraint); /// Forget about the given constraint. This happens every time a constraint /// gets removed for a constraint graph. - void retractFromInference(Constraint *constraint, bool notifyReferencedVars); + void retractFromInference(Constraint *constraint); /// Re-evaluate the given constraint. This happens when there are changes /// in associated type variables e.g. bound/unbound to/from a fixed type, /// equivalence class changes. - void reintroduceToInference(Constraint *constraint, bool notifyReferencedVars); + void reintroduceToInference(Constraint *constraint); /// Similar to \c introduceToInference(Constraint *, ...) this method is going /// to notify inference that this type variable has been bound to a concrete @@ -166,6 +166,10 @@ class ConstraintGraphNode { /// or equivalence class changes. void notifyReferencingVars() const; + /// Notify all of the type variables referenced by this one about a change. + void notifyReferencedVars( + llvm::function_ref notification); + /// } /// The constraint graph this node belongs to. diff --git a/include/swift/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index a3ae397ae8d3f..cd8ca5b2a27f0 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -581,6 +581,20 @@ class LocatorPathElt::SynthesizedArgument final : public StoredIntegerElement<2> } }; +class LocatorPathElt::TupleType : public StoredPointerElement { +public: + TupleType(Type type) + : StoredPointerElement(PathElementKind::TupleType, type.getPointer()) { + assert(type->getDesugaredType()->is()); + } + + Type getType() const { return getStoredPointer(); } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == PathElementKind::TupleType; + } +}; + /// Abstract superclass for any kind of tuple element. class LocatorPathElt::AnyTupleElement : public StoredIntegerElement<1> { protected: @@ -771,6 +785,19 @@ class LocatorPathElt::OpenedGeneric final : public StoredPointerElement { +public: + OpenedOpaqueArchetype(OpaqueTypeDecl *decl) + : StoredPointerElement(PathElementKind::OpenedOpaqueArchetype, decl) {} + + OpaqueTypeDecl *getDecl() const { return getStoredPointer(); } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == ConstraintLocator::OpenedOpaqueArchetype; + } +}; + class LocatorPathElt::KeyPathDynamicMember final : public StoredPointerElement { public: KeyPathDynamicMember(NominalTypeDecl *keyPathDecl) @@ -910,6 +937,22 @@ class LocatorPathElt::ContextualType final : public StoredIntegerElement<1> { } }; +class LocatorPathElt::KeyPathType final + : public StoredPointerElement { +public: + KeyPathType(Type valueType) + : StoredPointerElement(PathElementKind::KeyPathType, + valueType.getPointer()) { + assert(valueType); + } + + Type getValueType() const { return getStoredPointer(); } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == PathElementKind::KeyPathType; + } +}; + namespace details { template class PathElement { diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 63835c63f6a05..a24c41f2bbb2e 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -114,7 +114,7 @@ CUSTOM_LOCATOR_PATH_ELT(KeyPathDynamicMember) SIMPLE_LOCATOR_PATH_ELT(KeyPathRoot) /// The type of the key path expression. -SIMPLE_LOCATOR_PATH_ELT(KeyPathType) +CUSTOM_LOCATOR_PATH_ELT(KeyPathType) /// The value of a key path. SIMPLE_LOCATOR_PATH_ELT(KeyPathValue) @@ -134,6 +134,10 @@ SIMPLE_LOCATOR_PATH_ELT(MemberRefBase) /// base of the locator. CUSTOM_LOCATOR_PATH_ELT(OpenedGeneric) +/// This is referring to a type produced by opening an opaque type archetype +/// type at the base of the locator. +CUSTOM_LOCATOR_PATH_ELT(OpenedOpaqueArchetype) + /// An optional payload. SIMPLE_LOCATOR_PATH_ELT(OptionalPayload) @@ -164,6 +168,10 @@ SIMPLE_LOCATOR_PATH_ELT(SubscriptMember) /// The missing argument synthesized by the solver. CUSTOM_LOCATOR_PATH_ELT(SynthesizedArgument) +/// A tuple type, which provides context for subsequent tuple element +/// path components. +CUSTOM_LOCATOR_PATH_ELT(TupleType) + /// Tuple elements. ABSTRACT_LOCATOR_PATH_ELT(AnyTupleElement) /// A tuple element referenced by position. diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 905d971a91265..72cfb3981b23a 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -356,6 +356,10 @@ class TypeVariableType::Implementation { /// Determine whether this type variable represents a closure result type. bool isClosureResultType() const; + /// Determine whether this type variable represents + /// a type of a key path expression. + bool isKeyPathType() const; + /// Retrieve the representative of the equivalence class to which this /// type variable belongs. /// @@ -788,7 +792,7 @@ struct AppliedBuilderTransform { Type builderType; /// The result type of the body, to which the returned expression will be - /// converted. + /// converted. Opaque types should be unopened. Type bodyResultType; /// An expression whose value has been recorded for later use. @@ -992,7 +996,7 @@ class SolutionApplicationTargetsKey { SolutionApplicationTargetsKey( const PatternBindingDecl *patternBinding, unsigned index) { - kind = Kind::stmt; + kind = Kind::patternBindingEntry; storage.patternBindingEntry.patternBinding = patternBinding; storage.patternBindingEntry.index = index; } @@ -1181,6 +1185,10 @@ class Solution { /// The node -> type mappings introduced by this solution. llvm::DenseMap nodeTypes; + /// The key path component types introduced by this solution. + llvm::DenseMap, Type> + keyPathComponentTypes; + /// Contextual types introduced by this solution. std::vector> contextualTypes; @@ -1289,9 +1297,16 @@ class Solution { bool hasType(ASTNode node) const; + /// Returns \c true if the \p ComponentIndex-th component in \p KP has a type + /// associated with it. + bool hasType(const KeyPathExpr *KP, unsigned ComponentIndex) const; + /// Retrieve the type of the given node, as recorded in this solution. Type getType(ASTNode node) const; + /// Retrieve the type of the \p ComponentIndex-th component in \p KP. + Type getType(const KeyPathExpr *KP, unsigned ComponentIndex) const; + /// Retrieve the type of the given node as recorded in this solution /// and resolve all of the type variables in contains to form a fully /// "resolved" concrete type. @@ -1379,11 +1394,11 @@ enum class ConstraintSystemFlags { /// If set, verbose output is enabled for this constraint system. /// - /// Note that this flag is automatically applied to all constraint systems + /// Note that this flag is automatically applied to all constraint systems, /// when \c DebugConstraintSolver is set in \c TypeCheckerOptions. It can be /// automatically enabled for select constraint solving attempts by setting - /// \c DebugConstraintSolverAttempt. Finally, it be automatically enabled - /// for a pre-configured set of expressions on line numbers by setting + /// \c DebugConstraintSolverAttempt. Finally, it can also be automatically + /// enabled for a pre-configured set of expressions on line numbers by setting /// \c DebugConstraintSolverOnLines. DebugConstraints = 0x10, @@ -1544,7 +1559,7 @@ class SolutionApplicationTarget { stmtCondition, caseLabelItem, patternBinding, - uninitializedWrappedVar, + uninitializedVar, } kind; private: @@ -1616,9 +1631,16 @@ class SolutionApplicationTarget { DeclContext *dc; } caseLabelItem; - PatternBindingDecl *patternBinding; + struct { + PatternBindingDecl *binding; + /// Index into pattern binding declaration (if any). + unsigned index; + PointerUnion declaration; + /// Type associated with the declaration. + Type type; + } uninitializedVar; - VarDecl *uninitializedWrappedVar; + PatternBindingDecl *patternBinding; }; // If the pattern contains a single variable that has an attached @@ -1664,9 +1686,28 @@ class SolutionApplicationTarget { this->patternBinding = patternBinding; } - SolutionApplicationTarget(VarDecl *wrappedVar) { - kind = Kind::uninitializedWrappedVar; - this->uninitializedWrappedVar= wrappedVar; + SolutionApplicationTarget(VarDecl *uninitializedWrappedVar) + : kind(Kind::uninitializedVar) { + if (auto *PDB = uninitializedWrappedVar->getParentPatternBinding()) { + uninitializedVar.binding = PDB; + uninitializedVar.index = + PDB->getPatternEntryIndexForVarDecl(uninitializedWrappedVar); + } else { + uninitializedVar.binding = nullptr; + uninitializedVar.index = 0; + } + + uninitializedVar.declaration = uninitializedWrappedVar; + uninitializedVar.type = Type(); + } + + SolutionApplicationTarget(PatternBindingDecl *binding, unsigned index, + Pattern *var, Type patternTy) + : kind(Kind::uninitializedVar) { + uninitializedVar.binding = binding; + uninitializedVar.index = index; + uninitializedVar.declaration = var; + uninitializedVar.type = patternTy; } /// Form a target for the initialization of a pattern from an expression. @@ -1688,8 +1729,20 @@ class SolutionApplicationTarget { /// Form a target for a property with an attached property wrapper that is /// initialized out-of-line. - static SolutionApplicationTarget forUninitializedWrappedVar( - VarDecl *wrappedVar); + static SolutionApplicationTarget + forUninitializedWrappedVar(VarDecl *wrappedVar) { + return {wrappedVar}; + } + + static SolutionApplicationTarget + forUninitializedVar(PatternBindingDecl *binding, unsigned index, Pattern *var, + Type patternTy) { + return {binding, index, var, patternTy}; + } + + /// Form a target for a synthesized property wrapper initializer. + static SolutionApplicationTarget forPropertyWrapperInitializer( + VarDecl *wrappedVar, DeclContext *dc, Expr *initializer); Expr *getAsExpr() const { switch (kind) { @@ -1700,7 +1753,7 @@ class SolutionApplicationTarget { case Kind::stmtCondition: case Kind::caseLabelItem: case Kind::patternBinding: - case Kind::uninitializedWrappedVar: + case Kind::uninitializedVar: return nullptr; } llvm_unreachable("invalid expression type"); @@ -1723,8 +1776,13 @@ class SolutionApplicationTarget { case Kind::patternBinding: return patternBinding->getDeclContext(); - case Kind::uninitializedWrappedVar: - return uninitializedWrappedVar->getDeclContext(); + case Kind::uninitializedVar: { + if (auto *wrappedVar = + uninitializedVar.declaration.dyn_cast()) + return wrappedVar->getDeclContext(); + + return uninitializedVar.binding->getInitContext(uninitializedVar.index); + } } llvm_unreachable("invalid decl context type"); } @@ -1780,6 +1838,9 @@ class SolutionApplicationTarget { /// For a pattern initialization target, retrieve the pattern. Pattern *getInitializationPattern() const { + if (kind == Kind::uninitializedVar) + return uninitializedVar.declaration.get(); + assert(kind == Kind::expression); assert(expression.contextualPurpose == CTP_Initialization); return expression.pattern; @@ -1798,7 +1859,7 @@ class SolutionApplicationTarget { bool isOptionalSomePatternInit() const { return kind == Kind::expression && expression.contextualPurpose == CTP_Initialization && - isa(expression.pattern) && + dyn_cast_or_null(expression.pattern) && !expression.pattern->isImplicit(); } @@ -1822,7 +1883,9 @@ class SolutionApplicationTarget { // Don't create property wrapper generator functions for static variables and // local variables with initializers. - if (wrappedVar->isStatic() || wrappedVar->getDeclContext()->isLocalContext()) + bool hasInit = expression.propertyWrapper.hasInitialWrappedValue; + if (wrappedVar->isStatic() || + (hasInit && wrappedVar->getDeclContext()->isLocalContext())) return false; return expression.propertyWrapper.innermostWrappedValueInit == apply; @@ -1882,6 +1945,12 @@ class SolutionApplicationTarget { } void setPattern(Pattern *pattern) { + if (kind == Kind::uninitializedVar) { + assert(uninitializedVar.declaration.is()); + uninitializedVar.declaration = pattern; + return; + } + assert(kind == Kind::expression); assert(expression.contextualPurpose == CTP_Initialization || expression.contextualPurpose == CTP_ForEachStmt); @@ -1894,7 +1963,7 @@ class SolutionApplicationTarget { case Kind::stmtCondition: case Kind::caseLabelItem: case Kind::patternBinding: - case Kind::uninitializedWrappedVar: + case Kind::uninitializedVar: return None; case Kind::function: @@ -1909,7 +1978,7 @@ class SolutionApplicationTarget { case Kind::function: case Kind::caseLabelItem: case Kind::patternBinding: - case Kind::uninitializedWrappedVar: + case Kind::uninitializedVar: return None; case Kind::stmtCondition: @@ -1924,7 +1993,7 @@ class SolutionApplicationTarget { case Kind::function: case Kind::stmtCondition: case Kind::patternBinding: - case Kind::uninitializedWrappedVar: + case Kind::uninitializedVar: return None; case Kind::caseLabelItem: @@ -1939,7 +2008,7 @@ class SolutionApplicationTarget { case Kind::function: case Kind::stmtCondition: case Kind::caseLabelItem: - case Kind::uninitializedWrappedVar: + case Kind::uninitializedVar: return nullptr; case Kind::patternBinding: @@ -1957,8 +2026,68 @@ class SolutionApplicationTarget { case Kind::patternBinding: return nullptr; - case Kind::uninitializedWrappedVar: - return uninitializedWrappedVar; + case Kind::uninitializedVar: + return uninitializedVar.declaration.dyn_cast(); + } + llvm_unreachable("invalid case label type"); + } + + Pattern *getAsUninitializedVar() const { + switch (kind) { + case Kind::expression: + case Kind::function: + case Kind::stmtCondition: + case Kind::caseLabelItem: + case Kind::patternBinding: + return nullptr; + + case Kind::uninitializedVar: + return uninitializedVar.declaration.dyn_cast(); + } + llvm_unreachable("invalid case label type"); + } + + Type getTypeOfUninitializedVar() const { + switch (kind) { + case Kind::expression: + case Kind::function: + case Kind::stmtCondition: + case Kind::caseLabelItem: + case Kind::patternBinding: + return nullptr; + + case Kind::uninitializedVar: + return uninitializedVar.type; + } + llvm_unreachable("invalid case label type"); + } + + PatternBindingDecl *getPatternBindingOfUninitializedVar() const { + switch (kind) { + case Kind::expression: + case Kind::function: + case Kind::stmtCondition: + case Kind::caseLabelItem: + case Kind::patternBinding: + return nullptr; + + case Kind::uninitializedVar: + return uninitializedVar.binding; + } + llvm_unreachable("invalid case label type"); + } + + unsigned getIndexOfUninitializedVar() const { + switch (kind) { + case Kind::expression: + case Kind::function: + case Kind::stmtCondition: + case Kind::caseLabelItem: + case Kind::patternBinding: + return 0; + + case Kind::uninitializedVar: + return uninitializedVar.index; } llvm_unreachable("invalid case label type"); } @@ -1992,8 +2121,13 @@ class SolutionApplicationTarget { case Kind::patternBinding: return patternBinding->getSourceRange(); - case Kind::uninitializedWrappedVar: - return uninitializedWrappedVar->getSourceRange(); + case Kind::uninitializedVar: { + if (auto *wrappedVar = + uninitializedVar.declaration.dyn_cast()) { + return wrappedVar->getSourceRange(); + } + return uninitializedVar.declaration.get()->getSourceRange(); + } } llvm_unreachable("invalid target type"); } @@ -2016,8 +2150,13 @@ class SolutionApplicationTarget { case Kind::patternBinding: return patternBinding->getLoc(); - case Kind::uninitializedWrappedVar: - return uninitializedWrappedVar->getLoc(); + case Kind::uninitializedVar: { + if (auto *wrappedVar = + uninitializedVar.declaration.dyn_cast()) { + return wrappedVar->getLoc(); + } + return uninitializedVar.declaration.get()->getLoc(); + } } llvm_unreachable("invalid target type"); } @@ -2126,6 +2265,11 @@ class ConstraintSystem { /// explored. size_t MaxMemory = 0; + /// Flag to indicate to the solver that the system is in invalid + /// state and it shouldn't proceed but instead produce a fallback + /// diagnostic. + bool InvalidState = false; + /// Cached member lookups. llvm::DenseMap, Optional> MemberLookups; @@ -2168,9 +2312,23 @@ class ConstraintSystem { /// run through various diagnostics passes without actually mutating /// the types on the nodes. llvm::MapVector NodeTypes; - llvm::DenseMap, TypeBase *> + + /// The nodes for which we have produced types, along with the prior type + /// each node had before introducing this type. + llvm::SmallVector, 8> addedNodeTypes; + + /// Maps components in a key path expression to their type. Needed because + /// KeyPathExpr + Index isn't an \c ASTNode and thus can't be stored in \c + /// NodeTypes. + llvm::DenseMap, + Type> KeyPathComponentTypes; + /// Same as \c addedNodeTypes for \c KeyPathComponentTypes. + llvm::SmallVector< + std::tuple> + addedKeyPathComponentTypes; + /// Maps AST entries to their solution application targets. llvm::MapVector solutionApplicationTargets; @@ -2251,10 +2409,6 @@ class ConstraintSystem { SmallVector, 4> OpenedExistentialTypes; - /// The nodes for which we have produced types, along with the prior type - /// each node had before introducing this type. - llvm::SmallVector, 8> addedNodeTypes; - /// The set of functions that have been transformed by a result builder. std::vector> resultBuilderTransformed; @@ -2370,7 +2524,7 @@ class ConstraintSystem { /// The best solution computed so far. Optional BestScore; - /// The number of the solution attempt we're looking at. + /// The number of the solution attempts we're looking at. unsigned SolutionAttempt; /// Refers to the innermost partial solution scope. @@ -2517,8 +2671,8 @@ class ConstraintSystem { private: /// The list of constraints that have been retired along the - /// current path, this list is used in LIFO fashion when constraints - /// are added back to the circulation. + /// current path, this list is used in LIFO fashion when + /// constraints are added back to the circulation. ConstraintList retiredConstraints; /// The set of constraints which were active at the time of this state @@ -2615,6 +2769,11 @@ class ConstraintSystem { Phase = newPhase; } + /// Check whether constraint system is in valid state e.g. + /// has left-over active/inactive constraints which should + /// have been simplified. + bool inInvalidState() const { return InvalidState; } + /// Cache the types of the given expression and all subexpressions. void cacheExprTypes(Expr *expr) { bool excludeRoot = false; @@ -2724,6 +2883,8 @@ class ConstraintSystem { unsigned numAddedNodeTypes; + unsigned numAddedKeyPathComponentTypes; + unsigned numDisabledConstraints; unsigned numFavoredConstraints; @@ -2817,8 +2978,8 @@ class ConstraintSystem { /// able to emit an error message, or false if none of the fixits worked out. bool applySolutionFixes(const Solution &solution); - /// If there is more than one viable solution, - /// attempt to pick the best solution and remove all of the rest. + /// If there is more than one viable solution, attempt + /// to pick the best solution and remove all of the rest. /// /// \param solutions The set of solutions to filter. /// @@ -2861,7 +3022,7 @@ class ConstraintSystem { void addKeyPathApplicationRootConstraint(Type root, ConstraintLocatorBuilder locator); public: - /// Lookup for a member with the given name in the given base type. + /// Lookup for a member with the given name which is in the given base type. /// /// This routine caches the results of member lookups in the top constraint /// system, to avoid. @@ -2939,10 +3100,15 @@ class ConstraintSystem { /// map is used throughout the expression type checker in order to /// avoid mutating expressions until we know we have successfully /// type-checked them. - void setType(KeyPathExpr *KP, unsigned I, Type T) { + void setType(const KeyPathExpr *KP, unsigned I, Type T) { assert(KP && "Expected non-null key path parameter!"); assert(T && "Expected non-null type!"); - KeyPathComponentTypes[std::make_pair(KP, I)] = T.getPointer(); + + Type &entry = KeyPathComponentTypes[{KP, I}]; + Type oldType = entry; + entry = T; + + addedKeyPathComponentTypes.push_back(std::make_tuple(KP, I, oldType)); } /// Check to see if we have a type for a node. @@ -3286,9 +3452,8 @@ class ConstraintSystem { bool isFavored = false); /// Add the appropriate constraint for a contextual conversion. - void addContextualConversionConstraint( - Expr *expr, Type conversionType, ContextualTypePurpose purpose, - bool isOpaqueReturnType); + void addContextualConversionConstraint(Expr *expr, Type conversionType, + ContextualTypePurpose purpose); /// Convenience function to pass an \c ArrayRef to \c addJoinConstraint Type addJoinConstraint(ConstraintLocator *locator, @@ -3824,6 +3989,16 @@ class ConstraintSystem { /// \returns The opened type, or \c type if there are no archetypes in it. Type openType(Type type, OpenedTypeMap &replacements); +private: + /// "Open" an opaque archetype type, similar to \c openType. + Type openOpaqueType(OpaqueTypeArchetypeType *type, + ConstraintLocatorBuilder locator); + +public: + /// Recurse over the given type and open any opaque archetype types. + Type openOpaqueType(Type type, ContextualTypePurpose context, + ConstraintLocatorBuilder locator); + /// "Open" the given function type. /// /// If the function type is non-generic, this is equivalent to calling @@ -3951,7 +4126,7 @@ class ConstraintSystem { } private: - /// Adjust the constraint system to accomodate the given selected overload, and + /// Adjust the constraint system to accommodate the given selected overload, and /// recompute the type of the referenced declaration. /// /// \returns a pair containing the adjusted opened type of a reference to @@ -4088,10 +4263,9 @@ class ConstraintSystem { /// Generate constraints for the body of the given closure. /// /// \param closure the closure expression - /// \param resultType the closure's result type /// /// \returns \c true if constraint generation failed, \c false otherwise - bool generateConstraints(ClosureExpr *closure, Type resultType); + bool generateConstraints(ClosureExpr *closure); /// Generate constraints for the given (unchecked) expression. /// @@ -4350,8 +4524,25 @@ class ConstraintSystem { Type underlyingType; if (auto *fnTy = type->getAs()) underlyingType = replaceFinalResultTypeWithUnderlying(fnTy); - else + else if (auto *typeVar = + type->getWithoutSpecifierType()->getAs()) { + auto *locator = typeVar->getImpl().getLocator(); + + // If `type` hasn't been resolved yet, we need to allocate a type + // variable to represent an object type of a future optional, and + // add a constraint beetween `type` and `underlyingType` to model it. + underlyingType = createTypeVariable( + getConstraintLocator(locator, LocatorPathElt::GenericArgument(0)), + TVO_PrefersSubtypeBinding | TVO_CanBindToLValue | + TVO_CanBindToNoEscape); + + // Using a `typeVar` here because l-value is going to be applied + // to the underlying type below. + addConstraint(ConstraintKind::OptionalObject, typeVar, underlyingType, + locator); + } else { underlyingType = type->getWithoutSpecifierType()->getOptionalObjectType(); + } assert(underlyingType); @@ -4429,9 +4620,25 @@ class ConstraintSystem { /// Build implicit autoclosure expression wrapping a given expression. /// Given expression represents computed result of the closure. + /// + /// The \p ClosureDC must be the deepest possible context that + /// contains this autoclosure expression. For example, + /// + /// func foo() { + /// _ = { $0 || $1 || $2 } + /// } + /// + /// Even though the decl context of $1 (after solution application) is + /// `||`'s autoclosure parameter, we cannot know this until solution + /// application has finished because autoclosure expressions are expanded in + /// depth-first order then \c ContextualizeClosures comes around to clean up. + /// All that is required is that the explicit closure be the context since it + /// is the innermost context that can introduce potential new capturable + /// declarations. Expr *buildAutoClosureExpr(Expr *expr, FunctionType *closureType, + DeclContext *ClosureDC, bool isDefaultWrappedValue = false, - bool isSpawnLetWrapper = false); + bool isAsyncLetWrapper = false); /// Builds a type-erased return expression that can be used in dynamic /// replacement. @@ -4546,12 +4753,6 @@ class ConstraintSystem { TypeMatchOptions flags, ConstraintLocatorBuilder locator); - /// Attempt to simplify an OpaqueUnderlyingType constraint. - SolutionKind simplifyOpaqueUnderlyingTypeConstraint(Type type1, - Type type2, - TypeMatchOptions flags, - ConstraintLocatorBuilder locator); - /// Attempt to simplify the BridgingConversion constraint. SolutionKind simplifyBridgingConstraint(Type type1, Type type2, @@ -4682,10 +4883,10 @@ class ConstraintSystem { /// /// \returns \c None when the result builder cannot be applied at all, /// otherwise the result of applying the result builder. - Optional matchResultBuilder( - AnyFunctionRef fn, Type builderType, Type bodyResultType, - ConstraintKind bodyResultConstraintKind, - ConstraintLocatorBuilder locator); + Optional + matchResultBuilder(AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintKind bodyResultConstraintKind, + ConstraintLocatorBuilder locator); /// Matches a wrapped or projected value parameter type to its backing /// property wrapper type by applying the property wrapper. @@ -5269,17 +5470,9 @@ bool hasAppliedSelf(ConstraintSystem &cs, const OverloadChoice &choice); bool hasAppliedSelf(const OverloadChoice &choice, llvm::function_ref getFixedType); -/// Check whether type conforms to a given known protocol. -bool conformsToKnownProtocol(DeclContext *dc, Type type, - KnownProtocolKind protocol); - -/// Check whether given type conforms to `RawPepresentable` protocol +/// Check whether given type conforms to `RawRepresentable` protocol /// and return witness type. Type isRawRepresentable(ConstraintSystem &cs, Type type); -/// Check whether given type conforms to a specific known kind -/// `RawPepresentable` protocol and return witness type. -Type isRawRepresentable(ConstraintSystem &cs, Type type, - KnownProtocolKind rawRepresentableProtocol); /// Compute the type that shall stand in for dynamic 'Self' in a member /// reference with a base of the given object type. @@ -5373,6 +5566,9 @@ class TypeVariableBinding { TypeVariableBinding(TypeVariableType *typeVar, PotentialBinding &binding) : TypeVar(typeVar), Binding(binding) {} + TypeVariableType *getTypeVariable() const { return TypeVar; } + Type getType() const { return Binding.BindingType; } + bool isDefaultable() const { return Binding.isDefaultableBinding(); } bool hasDefaultedProtocol() const { @@ -5415,7 +5611,9 @@ class BindingProducer { /// This is useful to be able to exhaustively attempt bindings /// for type variables found at one level, before proceeding to /// supertypes or literal defaults etc. - virtual bool needsToComputeNext() const { return false; } + virtual bool needsToComputeNext() const = 0; + + virtual bool isExhausted() const = 0; }; class TypeVarBindingProducer : public BindingProducer { @@ -5442,6 +5640,8 @@ class TypeVarBindingProducer : public BindingProducer { /// to that protocol or be wrapped in an optional. bool CanBeNil; + bool IsExhausted = false; + public: using Element = TypeVariableBinding; @@ -5451,16 +5651,39 @@ class TypeVarBindingProducer : public BindingProducer { ArrayRef getCurrentBindings() const { return Bindings; } Optional operator()() override { + if (isExhausted()) + return None; + // Once we reach the end of the current bindings // let's try to compute new ones, e.g. supertypes, // literal defaults, if that fails, we are done. - if (needsToComputeNext() && !computeNext()) + if (needsToComputeNext() && !computeNext()) { + IsExhausted = true; return None; + } + + auto &binding = Bindings[Index++]; + + // Record produced type as bound/explored early, otherwise + // it could be possible to re-discover it during `computeNext()`, + // which leads to duplicate bindings e.g. inferring fallback + // `Void` for a closure result type when `Void` was already + // inferred as a direct/transitive binding. + { + auto type = binding.BindingType; + + BoundTypes.insert(type.getPointer()); + ExploredTypes.insert(type->getCanonicalType()); + } - return TypeVariableBinding(TypeVar, Bindings[Index++]); + return TypeVariableBinding(TypeVar, binding); } - bool needsToComputeNext() const override { return Index >= Bindings.size(); } + bool needsToComputeNext() const override { + return isExhausted() ? false : Index >= Bindings.size(); + } + + bool isExhausted() const override { return IsExhausted; } private: /// Compute next batch of bindings if possible, this could @@ -5533,10 +5756,10 @@ class DisjunctionChoiceProducer : public BindingProducer { } Optional operator()() override { - unsigned currIndex = Index; - if (currIndex >= Choices.size()) + if (isExhausted()) return None; + unsigned currIndex = Index; bool isBeginningOfPartition = PartitionIndex < PartitionBeginning.size() && PartitionBeginning[PartitionIndex] == Index; if (isBeginningOfPartition) @@ -5560,6 +5783,10 @@ class DisjunctionChoiceProducer : public BindingProducer { IsExplicitConversion, isBeginningOfPartition); } + bool needsToComputeNext() const override { return false; } + + bool isExhausted() const override { return Index >= Choices.size(); } + private: // Partition the choices in the disjunction into groups that we will // iterate over in an order appropriate to attempt to stop before we @@ -5701,9 +5928,9 @@ bool exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC, // Return true if, when replacing "" with " as T", parentheses need // to be added around the new expression in order to maintain the correct // precedence. -bool exprNeedsParensAfterAddingNilCoalescing(DeclContext *DC, - Expr *expr, - Expr *rootExpr); +bool exprNeedsParensAfterAddingNilCoalescing( + DeclContext *DC, Expr *expr, + llvm::function_ref getParent); /// Return true if, when replacing "" with " op ", /// parentheses must be added around "" to allow the new operator @@ -5712,13 +5939,12 @@ bool exprNeedsParensInsideFollowingOperator(DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG); -/// Return true if, when replacing "" with " op " -/// within the given root expression, parentheses must be added around -/// the new operator to prevent it from binding incorrectly in the -/// surrounding context. +/// Return true if, when replacing "" with " op ", +/// parentheses must be added around the new operator to prevent it from binding +/// incorrectly in the surrounding context. bool exprNeedsParensOutsideFollowingOperator( - DeclContext *DC, Expr *expr, Expr *rootExpr, - PrecedenceGroupDecl *followingPG); + DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG, + llvm::function_ref getParent); /// Determine whether this is a SIMD operator. bool isSIMDOperator(ValueDecl *value); diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index deef58b7006c2..a4ede7727c54a 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -206,6 +206,33 @@ namespace swift { /// the decl context. ProtocolDecl *resolveProtocolName(DeclContext *dc, StringRef Name); + /// Reported type of a variable declaration. + struct VariableTypeInfo { + /// The start of the variable identifier. + uint32_t Offset; + + /// The length of the variable identifier. + uint32_t Length; + + /// Whether the variable has an explicit type annotation. + bool HasExplicitType; + + /// The start of the printed type in a separate string buffer. + uint32_t TypeOffset; + + VariableTypeInfo(uint32_t Offset, uint32_t Length, bool HasExplicitType, + uint32_t TypeOffset); + }; + + /// Collect type information for every variable declaration in \c SF + /// within the given range into \c VariableTypeInfos. + /// All types will be printed to \c OS and the type offsets of the + /// \c VariableTypeInfos will index into the string that backs this + /// stream. + void collectVariableType(SourceFile &SF, SourceRange Range, + std::vector &VariableTypeInfos, + llvm::raw_ostream &OS); + /// FIXME: All of the below goes away once CallExpr directly stores its /// arguments. @@ -216,7 +243,32 @@ namespace swift { SmallVector labelLocs; SourceLoc lParenLoc; SourceLoc rParenLoc; - bool hasTrailingClosure = false; + Optional unlabeledTrailingClosureIdx; + + /// The number of trailing closures in the argument list. + unsigned getNumTrailingClosures() const { + if (!unlabeledTrailingClosureIdx) + return 0; + return args.size() - *unlabeledTrailingClosureIdx; + } + + /// Whether any unlabeled or labeled trailing closures are present. + bool hasAnyTrailingClosures() const { + return unlabeledTrailingClosureIdx.hasValue(); + } + + /// Whether the given index is for an unlabeled trailing closure. + bool isUnlabeledTrailingClosureIdx(unsigned i) const { + return unlabeledTrailingClosureIdx && *unlabeledTrailingClosureIdx == i; + } + + /// Whether the given index is for a labeled trailing closure in an + /// argument list with multiple trailing closures. + bool isLabeledTrailingClosureIdx(unsigned i) const { + if (!unlabeledTrailingClosureIdx) + return false; + return i > *unlabeledTrailingClosureIdx && i < args.size(); + } }; /// When applying a solution to a constraint system, the type checker rewrites @@ -278,6 +330,9 @@ namespace swift { /// for a Fix-It that adds a new build* function to a result builder. std::tuple determineResultBuilderBuildFixItInfo(NominalTypeDecl *builder); + + /// Just a proxy to swift::contextUsesConcurrencyFeatures() from lib/IDE code. + bool completionContextUsesConcurrencyFeatures(const DeclContext *dc); } #endif diff --git a/include/swift/Serialization/SerializationOptions.h b/include/swift/Serialization/SerializationOptions.h index 94725c3fe3b5d..2b180591a19d7 100644 --- a/include/swift/Serialization/SerializationOptions.h +++ b/include/swift/Serialization/SerializationOptions.h @@ -33,6 +33,7 @@ namespace swift { const char *SourceInfoOutputPath = nullptr; std::string SymbolGraphOutputDir; bool SkipSymbolGraphInheritedDocs = true; + bool IncludeSPISymbolsInSymbolGraph = false; llvm::VersionTuple UserModuleVersion; StringRef GroupInfoPath; @@ -130,6 +131,7 @@ namespace swift { uint64_t getSize() const { return Size; } }; ArrayRef Dependencies; + ArrayRef PublicDependentLibraries; bool AutolinkForceLoad = false; bool SerializeAllSIL = false; diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 784a15504b752..ad990cc375339 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -443,7 +443,7 @@ class SerializedASTFile final : public LoadedFile { virtual StringRef getModuleDefiningPath() const override; - Decl *getMainDecl() const override; + ValueDecl *getMainDecl() const override; bool hasEntryPoint() const override; @@ -469,8 +469,12 @@ class SerializedASTFile final : public LoadedFile { }; /// Extract compiler arguments from an interface file buffer. -bool extractCompilerFlagsFromInterface(StringRef buffer, llvm::StringSaver &ArgSaver, +bool extractCompilerFlagsFromInterface(StringRef interfacePath, + StringRef buffer, llvm::StringSaver &ArgSaver, SmallVectorImpl &SubArgs); + +/// Extract the user module version number from an interface file. +llvm::VersionTuple extractUserModuleVersionFromInterface(StringRef moduleInterfacePath); } // end namespace swift #endif diff --git a/include/swift/Serialization/Validation.h b/include/swift/Serialization/Validation.h index 28af3d140b476..e5564a139473d 100644 --- a/include/swift/Serialization/Validation.h +++ b/include/swift/Serialization/Validation.h @@ -103,6 +103,7 @@ class ExtendedValidationInfo { unsigned ResilienceStrategy : 2; unsigned IsImplicitDynamicEnabled : 1; unsigned IsAllowModuleWithCompilerErrorsEnabled : 1; + unsigned IsConcurrencyChecked : 1; } Bits; public: ExtendedValidationInfo() : Bits() {} @@ -155,6 +156,13 @@ class ExtendedValidationInfo { StringRef getModuleABIName() const { return ModuleABIName; } void setModuleABIName(StringRef name) { ModuleABIName = name; } + + bool isConcurrencyChecked() const { + return Bits.IsConcurrencyChecked; + } + void setIsConcurrencyChecked(bool val = true) { + Bits.IsConcurrencyChecked = val; + } }; /// Returns info about the serialized AST in the given data. diff --git a/include/swift/Strings.h b/include/swift/Strings.h index 409355055342a..ad580a496ff97 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -24,6 +24,8 @@ constexpr static const StringLiteral STDLIB_NAME = "Swift"; constexpr static const StringLiteral SWIFT_ONONE_SUPPORT = "SwiftOnoneSupport"; /// The name of the Concurrency module, which supports that extension. constexpr static const StringLiteral SWIFT_CONCURRENCY_NAME = "_Concurrency"; +/// The name of the Distributed module, which supports that extension. +constexpr static const StringLiteral SWIFT_DISTRIBUTED_NAME = "_Distributed"; /// The name of the SwiftShims module, which contains private stdlib decls. constexpr static const StringLiteral SWIFT_SHIMS_NAME = "SwiftShims"; /// The name of the Builtin module, which contains Builtin functions. @@ -56,6 +58,10 @@ constexpr static const StringLiteral DEFAULT_ACTOR_STORAGE_FIELD_NAME = /// The name of the Builtin type prefix constexpr static const StringLiteral BUILTIN_TYPE_NAME_PREFIX = "Builtin."; +/// The default SPI group name to associate with Clang SPIs. +constexpr static const StringLiteral CLANG_MODULE_DEFUALT_SPI_GROUP_NAME = + "OBJC_DEFAULT_SPI_GROUP"; + /// A composition class containing a StringLiteral for the names of /// Swift builtins. The reason we use this is to ensure that we when /// necessary slice off the "Builtin." prefix from these names in a diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h index ffbeadc4b4f4b..c96c73c3f142f 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h @@ -26,13 +26,6 @@ #include -/// Major version changes when there are ABI or source incompatible changes. -#define SWIFT_REFLECTION_VERSION_MAJOR 3 - -/// Minor version changes when new APIs are added in ABI- and source-compatible -/// way. -#define SWIFT_REFLECTION_VERSION_MINOR 0 - #ifdef __cplusplus extern "C" { #endif @@ -43,6 +36,16 @@ __attribute__((__weak_import__)) #endif extern unsigned long long swift_reflection_classIsSwiftMask; +/// An arbitrary version number for this library. Incremented to indicate the +/// presence of a bug fix or feature that can't be detected from the outside +/// otherwise. The currently used version numbers are: +/// +/// 0 - Indicates that swift_reflection_iterateAsyncTaskAllocations has the +/// first attempted fix to use the right AsyncTask layout. +/// 1 - Indicates that swift_reflection_iterateAsyncTaskAllocations has been +/// actually fixed to use the right AsyncTask layout. +SWIFT_REMOTE_MIRROR_LINKAGE extern uint32_t swift_reflection_libraryVersion; + /// Get the metadata version supported by the Remote Mirror library. SWIFT_REMOTE_MIRROR_LINKAGE uint16_t swift_reflection_getSupportedMetadataVersion(void); @@ -136,6 +139,13 @@ uintptr_t swift_reflection_metadataForObject(SwiftReflectionContextRef ContextRef, uintptr_t Object); +/// Returns the nominal type descriptor given the metadata +SWIFT_REMOTE_MIRROR_LINKAGE +swift_reflection_ptr_t +swift_reflection_metadataNominalTypeDescriptor(SwiftReflectionContextRef ContextRef, + swift_reflection_ptr_t Metadata); + + /// Returns an opaque type reference for a class or closure context /// instance pointer, or NULL if one can't be constructed. /// @@ -247,6 +257,19 @@ int swift_reflection_projectExistential(SwiftReflectionContextRef ContextRef, swift_typeref_t *OutInstanceTypeRef, swift_addr_t *OutStartOfInstanceData); +/// Like swift_reflection_projectExistential, with 2 differences: +/// +/// - When dealing with an error existential, this version will dereference +/// the ExistentialAddress before proceeding. +/// - After setting OutInstanceTypeRef and OutStartOfInstanceData this version +/// may derefence and set OutStartOfInstanceData if OutInstanceTypeRef is a +/// class TypeRef. +SWIFT_REMOTE_MIRROR_LINKAGE +int swift_reflection_projectExistentialAndUnwrapClass( + SwiftReflectionContextRef ContextRef, swift_addr_t ExistentialAddress, + swift_typeref_t ExistentialTypeRef, swift_typeref_t *OutInstanceTypeRef, + swift_addr_t *OutStartOfInstanceData); + /// Projects the value of an enum. /// /// Takes the address and typeref for an enum and determines the diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorLegacyInterop.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorLegacyInterop.h index 0b07ec7633390..dfc6b09427b10 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorLegacyInterop.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorLegacyInterop.h @@ -149,6 +149,10 @@ swift_reflection_interop_projectExistential(SwiftReflectionInteropContextRef Con swift_typeref_interop_t *OutInstanceTypeRef, swift_addr_t *OutStartOfInstanceData); +static inline int swift_reflection_interop_projectEnum( + SwiftReflectionInteropContextRef ContextRef, swift_addr_t EnumAddress, + swift_typeref_interop_t EnumTypeRef, int *CaseIndex); + static inline void swift_reflection_interop_dumpTypeRef(SwiftReflectionInteropContextRef ContextRef, swift_typeref_interop_t OpaqueTypeRef); @@ -289,6 +293,10 @@ struct SwiftReflectionFunctions { swift_typeref_t *OutInstanceTypeRef, swift_addr_t *OutStartOfInstanceData); + int (*projectEnumValue)(SwiftReflectionContextRef ContextRef, + swift_addr_t EnumAddress, swift_typeref_t EnumTypeRef, + int *CaseIndex); + void (*dumpTypeRef)(swift_typeref_t OpaqueTypeRef); void (*dumpInfoForTypeRef)(SwiftReflectionContextRef ContextRef, @@ -482,6 +490,7 @@ swift_reflection_interop_loadFunctions(struct SwiftReflectionInteropContext *Con LOAD(genericArgumentCountOfTypeRef); LOAD(genericArgumentOfTypeRef); LOAD(projectExistential); + LOAD_OPT(projectEnumValue); LOAD(dumpTypeRef); LOAD(dumpInfoForTypeRef); @@ -1113,6 +1122,15 @@ swift_reflection_interop_projectExistential(SwiftReflectionInteropContextRef Con return 1; } +static inline int swift_reflection_interop_projectEnumValue( + SwiftReflectionInteropContextRef ContextRef, swift_addr_t EnumAddress, + swift_typeref_interop_t EnumTypeRef, int *CaseIndex) { + DECLARE_LIBRARY(EnumTypeRef.Library); + return Library->Functions.projectEnumValue && + Library->Functions.projectEnumValue(Library->Context, EnumAddress, + EnumTypeRef.Typeref, CaseIndex); +} + static inline void swift_reflection_interop_dumpTypeRef(SwiftReflectionInteropContextRef ContextRef, swift_typeref_interop_t OpaqueTypeRef) { diff --git a/include/swift/SymbolGraphGen/SymbolGraphOptions.h b/include/swift/SymbolGraphGen/SymbolGraphOptions.h index cd17181bca9b1..648c4dfd2b9b5 100644 --- a/include/swift/SymbolGraphGen/SymbolGraphOptions.h +++ b/include/swift/SymbolGraphGen/SymbolGraphOptions.h @@ -42,6 +42,9 @@ struct SymbolGraphOptions { /// Whether to skip docs for symbols with compound, "SYNTHESIZED" USRs. bool SkipInheritedDocs; + + /// Whether to emit symbols with SPI information. + bool IncludeSPISymbols; }; } // end namespace symbolgraphgen diff --git a/include/swift/Syntax/SyntaxData.h b/include/swift/Syntax/SyntaxData.h index 848ffd42aaad1..370fc3ea54fb2 100644 --- a/include/swift/Syntax/SyntaxData.h +++ b/include/swift/Syntax/SyntaxData.h @@ -296,7 +296,7 @@ class SyntaxData final : public SyntaxDataRef { ~SyntaxData() { assert(RefCount == 0 && - "Destruction occured when there are still references to this."); + "Destruction occurred when there are still references to this."); if (auto Parent = getParent()) { Parent->Release(); } diff --git a/include/swift/module.modulemap b/include/swift/module.modulemap new file mode 100644 index 0000000000000..0521c463f6ea0 --- /dev/null +++ b/include/swift/module.modulemap @@ -0,0 +1,10 @@ +module SILBridging { + header "SIL/SILBridging.h" + export * +} + +module OptimizerBridging { + header "SILOptimizer/OptimizerBridging.h" + export * +} + diff --git a/lib/APIDigester/CMakeLists.txt b/lib/APIDigester/CMakeLists.txt index 203cc07bbf96c..13fc81a17a789 100644 --- a/lib/APIDigester/CMakeLists.txt +++ b/lib/APIDigester/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftAPIDigester STATIC ModuleAnalyzerNodes.cpp @@ -8,3 +8,5 @@ target_link_libraries(swiftAPIDigester PRIVATE swiftFrontend swiftSIL swiftIDE) + +set_swift_llvm_is_available(swiftAPIDigester) diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index d991219662bc4..eaebc11e43bc4 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -46,6 +46,7 @@ struct swift::ide::api::SDKNodeInitInfo { SDKNodeInitInfo(SDKContext &Ctx, Decl *D); SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD); SDKNodeInitInfo(SDKContext &Ctx, OperatorDecl *D); + SDKNodeInitInfo(SDKContext &Ctx, ImportDecl *ID); SDKNodeInitInfo(SDKContext &Ctx, ProtocolConformance *Conform); SDKNodeInitInfo(SDKContext &Ctx, Type Ty, TypeInitInfo Info = TypeInitInfo()); SDKNode* createSDKNode(SDKNodeKind Kind); @@ -80,6 +81,9 @@ void SDKNodeRoot::registerDescendant(SDKNode *D) { // Operator doesn't have usr if (isa(D)) return; + // Import doesn't have usr + if (isa(D)) + return; if (auto DD = dyn_cast(D)) { assert(!DD->getUsr().empty()); DescendantDeclTable[DD->getUsr()].insert(DD); @@ -166,6 +170,9 @@ SDKNodeDeclAccessor::SDKNodeDeclAccessor(SDKNodeInitInfo Info): SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclAccessor), AccKind(Info.AccKind) {} +SDKNodeDeclImport::SDKNodeDeclImport(SDKNodeInitInfo Info): + SDKNodeDecl(Info, SDKNodeKind::DeclImport) {} + SDKNodeDeclAssociatedType::SDKNodeDeclAssociatedType(SDKNodeInitInfo Info): SDKNodeDecl(Info, SDKNodeKind::DeclAssociatedType) {}; @@ -375,6 +382,7 @@ StringRef SDKNodeType::getTypeRoleDescription() const { case SDKNodeKind::DeclType: case SDKNodeKind::DeclOperator: case SDKNodeKind::Conformance: + case SDKNodeKind::DeclImport: llvm_unreachable("Type Parent is wrong"); case SDKNodeKind::DeclFunction: case SDKNodeKind::DeclConstructor: @@ -936,10 +944,13 @@ static bool isSDKNodeEqual(SDKContext &Ctx, const SDKNode &L, const SDKNode &R) if (Left->getFixedBinaryOrder() != Right->getFixedBinaryOrder()) return false; } + if (Left->getUsr() != Right->getUsr()) + return false; LLVM_FALLTHROUGH; } case SDKNodeKind::Conformance: case SDKNodeKind::TypeWitness: + case SDKNodeKind::DeclImport: case SDKNodeKind::Root: { return L.getPrintedName() == R.getPrintedName() && L.hasSameChildren(R); @@ -1251,7 +1262,7 @@ StringRef SDKContext::getPlatformIntroVersion(Decl *D, PlatformKind Kind) { } } } - return getPlatformIntroVersion(D->getDeclContext()->getAsDecl(), Kind); + return StringRef(); } StringRef SDKContext::getLanguageIntroVersion(Decl *D) { @@ -1358,6 +1369,13 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, OperatorDecl *OD): PrintedName = OD->getName().str(); } +SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ImportDecl *ID): + SDKNodeInitInfo(Ctx, cast(ID)) { + std::string content; + llvm::raw_string_ostream OS(content); + ID->getModulePath().print(OS); + Name = PrintedName = Ctx.buffer(content); +} SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ProtocolConformance *Conform): SDKNodeInitInfo(Ctx, Conform->getProtocol()) { @@ -1500,7 +1518,7 @@ SwiftDeclCollector::constructTypeNode(Type T, TypeInitInfo Info) { // Still, return type first Root->addChild(constructTypeNode(Fun->getResult())); - auto Input = AnyFunctionType::composeInput(Fun->getASTContext(), + auto Input = AnyFunctionType::composeTuple(Fun->getASTContext(), Fun->getParams(), /*canonicalVararg=*/false); Root->addChild(constructTypeNode(Input)); @@ -1888,6 +1906,10 @@ void SwiftDeclCollector::processDecl(Decl *D) { if (auto *OD = dyn_cast(D)) { RootNode->addChild(constructOperatorDeclNode(OD)); } + if (auto *IM = dyn_cast(D)) { + RootNode->addChild(SDKNodeInitInfo(Ctx, IM) + .createSDKNode(SDKNodeKind::DeclImport)); + } } void SwiftDeclCollector::processValueDecl(ValueDecl *VD) { @@ -2274,6 +2296,22 @@ int swift::ide::api::deserializeSDKDump(StringRef dumpPath, StringRef OutputPath return 0; } +void swift::ide::api::dumpModuleContent(ModuleDecl *MD, StringRef OutputFile, + bool ABI) { + CheckerOptions opts; + opts.ABI = ABI; + opts.SwiftOnly = true; + opts.AvoidLocation = true; + opts.AvoidToolArgs = true; + opts.Migrator = false; + opts.SkipOSCheck = false; + opts.Verbose = false; + SDKContext ctx(opts); + SwiftDeclCollector collector(ctx); + collector.lookupVisibleDecls({MD}); + dumpSDKRoot(collector.getSDKRoot(), OutputFile); +} + int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) { std::error_code EC; if (!fs::exists(dumpPath)) { @@ -2303,3 +2341,308 @@ int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) { } return 0; } + +void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (getDeclKind() != R->getDeclKind()) { + emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind(), + getSDKContext().getOpts().CompilerStyle)); + return; + } + + assert(getDeclKind() == R->getDeclKind()); + auto DKind = getDeclKind(); + switch (DKind) { + case DeclKind::Class: { + auto LSuperClass = getSuperClassName(); + auto RSuperClass = R->getSuperClassName(); + if (!LSuperClass.empty() && LSuperClass != RSuperClass) { + if (RSuperClass.empty()) { + emitDiag(Loc, diag::super_class_removed, LSuperClass); + } else if (!llvm::is_contained(R->getClassInheritanceChain(), LSuperClass)) { + emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); + } + } + + // Check for @_hasMissingDesignatedInitializers and + // @_inheritsConvenienceInitializers changes. + if (isOpen() && R->isOpen()) { + // It's not safe to add new, invisible designated inits to open + // classes. + if (!hasMissingDesignatedInitializers() && + R->hasMissingDesignatedInitializers()) + R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); + } + + // It's not safe to stop inheriting convenience inits, it changes + // the set of initializers that are available. + if (!Ctx.checkingABI() && + inheritsConvenienceInitializers() && + !R->inheritsConvenienceInitializers()) + R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); + break; + } + default: + break; + } +} + +void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (!isThrowing() && R->isThrowing()) { + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing")); + } + if (Ctx.checkingABI()) { + if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) { + emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry()); + } + } +} + +void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) { + SDKNodeDeclAbstractFunc::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (getSelfAccessKind() != R->getSelfAccessKind()) { + emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(), + R->getSelfAccessKind()); + } + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) { + emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder()); + } + } +} + +static StringRef getAttrName(DeclAttrKind Kind) { + switch (Kind) { +#define DECL_ATTR(NAME, CLASS, ...) \ + case DAK_##CLASS: \ + return DeclAttribute::isDeclModifier(DAK_##CLASS) ? #NAME : "@"#NAME; +#include "swift/AST/Attr.def" + case DAK_Count: + llvm_unreachable("unrecognized attribute kind."); + } + llvm_unreachable("covered switch"); +} + +static bool shouldDiagnoseAddingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { + return true; +} + +static bool shouldDiagnoseRemovingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { + return true; +} + +static bool isOwnershipEquivalent(ReferenceOwnership Left, + ReferenceOwnership Right) { + if (Left == Right) + return true; + if (Left == ReferenceOwnership::Unowned && Right == ReferenceOwnership::Weak) + return true; + if (Left == ReferenceOwnership::Weak && Right == ReferenceOwnership::Unowned) + return true; + return false; +} + +void swift::ide::api::detectRename(SDKNode *L, SDKNode *R) { + if (L->getKind() == R->getKind() && isa(L) && + L->getPrintedName() != R->getPrintedName()) { + L->annotate(NodeAnnotation::Rename); + L->annotate(NodeAnnotation::RenameOldName, L->getPrintedName()); + L->annotate(NodeAnnotation::RenameNewName, R->getPrintedName()); + } +} + +void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { + SDKNode::diagnose(Right); + auto *RD = dyn_cast(Right); + if (!RD) + return; + detectRename(this, RD); + auto Loc = RD->getLoc(); + if (isOpen() && !RD->isOpen()) { + emitDiag(Loc, diag::no_longer_open); + } + + // Diagnose static attribute change. + if (isStatic() ^ RD->isStatic()) { + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" : + "static")); + } + + // Diagnose ownership change. + if (!isOwnershipEquivalent(getReferenceOwnership(), + RD->getReferenceOwnership())) { + auto getOwnershipDescription = [&](swift::ReferenceOwnership O) { + if (O == ReferenceOwnership::Strong) + return Ctx.buffer("strong"); + return keywordOf(O); + }; + emitDiag(Loc, diag::decl_attr_change, + getOwnershipDescription(getReferenceOwnership()), + getOwnershipDescription(RD->getReferenceOwnership())); + } + // Diagnose generic signature change + if (getGenericSignature() != RD->getGenericSignature()) { + // Prefer sugared signature in diagnostics to be more user-friendly. + if (Ctx.commonVersionAtLeast(2) && + getSugaredGenericSignature() != RD->getSugaredGenericSignature()) { + emitDiag(Loc, diag::generic_sig_change, + getSugaredGenericSignature(), RD->getSugaredGenericSignature()); + } else { + emitDiag(Loc, diag::generic_sig_change, + getGenericSignature(), RD->getGenericSignature()); + } + } + + // ObjC name changes are considered breakage + if (getObjCName() != RD->getObjCName()) { + if (Ctx.commonVersionAtLeast(4)) { + emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName()); + } + } + + if (isOptional() != RD->isOptional()) { + if (Ctx.checkingABI()) { + // Both adding/removing optional is ABI-breaking. + emitDiag(Loc, diag::optional_req_changed, isOptional()); + } else if (isOptional()) { + // Removing optional is source-breaking. + emitDiag(Loc, diag::optional_req_changed, isOptional()); + } + } + + // Diagnose removing attributes. + for (auto Kind: getDeclAttributes()) { + if (!RD->hasDeclAttribute(Kind)) { + if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) : + DeclAttribute::isRemovingBreakingAPI(Kind)) && + shouldDiagnoseRemovingAttribute(this, Kind)) { + emitDiag(Loc, diag::decl_new_attr, + Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str())); + } + } + } + + // Diagnose adding attributes. + for (auto Kind: RD->getDeclAttributes()) { + if (!hasDeclAttribute(Kind)) { + if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) : + DeclAttribute::isAddingBreakingAPI(Kind)) && + shouldDiagnoseAddingAttribute(this, Kind)) { + emitDiag(Loc, diag::decl_new_attr, + Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str())); + } + } + } + + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() && + getFixedBinaryOrder() != RD->getFixedBinaryOrder()) { + emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(), + RD->getFixedBinaryOrder()); + } + if (getUsr() != RD->getUsr()) { + auto left = demangleUSR(getUsr()); + auto right = demangleUSR(RD->getUsr()); + if (left != right) { + emitDiag(Loc, diag::demangled_name_changed, left, right); + } + } + } +} + +void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *RO = dyn_cast(Right); + if (!RO) + return; + auto Loc = RO->getLoc(); + if (getDeclKind() != RO->getDeclKind()) { + emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind(), + getSDKContext().getOpts().CompilerStyle)); + } +} + +void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *RV = dyn_cast(Right); + if (!RV) + return; + auto Loc = RV->getLoc(); + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) { + emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder()); + } + } +} + +static bool shouldDiagnoseType(SDKNodeType *T) { + return T->isTopLevelType(); +} + +void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) { + SDKNode::diagnose(Right); + auto *RT = dyn_cast(Right); + if (!RT || !shouldDiagnoseType(this)) + return; + assert(isTopLevelType()); + + // Diagnose type witness changes when diagnosing ABI breakages. + if (auto *Wit = dyn_cast(getParent())) { + auto *Conform = Wit->getParent()->getAs(); + if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) { + auto *LD = Conform->getNominalTypeDecl(); + LD->emitDiag(SourceLoc(), diag::type_witness_change, + Wit->getWitnessedTypeName(), + getPrintedName(), RT->getPrintedName()); + } + return; + } + + StringRef Descriptor = getTypeRoleDescription(); + assert(isa(getParent())); + auto LParent = cast(getParent()); + assert(LParent->getKind() == RT->getParent()->getAs()->getKind()); + auto Loc = RT->getParent()->getAs()->getLoc(); + if (getPrintedName() != RT->getPrintedName()) { + LParent->emitDiag(Loc, diag::decl_type_change, + Descriptor, getPrintedName(), RT->getPrintedName()); + } + + if (hasDefaultArgument() && !RT->hasDefaultArgument()) { + LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor); + } + if (getParamValueOwnership() != RT->getParamValueOwnership()) { + LParent->emitDiag(Loc, diag::param_ownership_change, + getTypeRoleDescription(), + getParamValueOwnership(), + RT->getParamValueOwnership()); + } +} + +void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) { + SDKNodeType::diagnose(Right); + auto *RT = dyn_cast(Right); + if (!RT || !shouldDiagnoseType(this)) + return; + assert(isTopLevelType()); + auto Loc = RT->getParent()->getAs()->getLoc(); + if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) { + getParent()->getAs()->emitDiag(Loc, + diag::func_type_escaping_changed, + getTypeRoleDescription(), + isEscaping()); + } +} diff --git a/lib/APIDigester/ModuleDiagsConsumer.cpp b/lib/APIDigester/ModuleDiagsConsumer.cpp index 044c9d05926d9..c73fd148d5947 100644 --- a/lib/APIDigester/ModuleDiagsConsumer.cpp +++ b/lib/APIDigester/ModuleDiagsConsumer.cpp @@ -54,6 +54,7 @@ static StringRef getCategoryName(uint32_t ID) { case LocalDiagID::raw_type_change: return "/* RawRepresentable Changes */"; case LocalDiagID::generic_sig_change: + case LocalDiagID::demangled_name_changed: return "/* Generic Signature Changes */"; case LocalDiagID::enum_case_added: case LocalDiagID::decl_added: diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index c00b1d8c2bfcf..42f41b66b9cd9 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -41,6 +41,7 @@ #include "swift/AST/PropertyWrappers.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/RawComment.h" +#include "swift/AST/SearchPathOptions.h" #include "swift/AST/SILLayout.h" #include "swift/AST/SemanticAttrs.h" #include "swift/AST/SourceFile.h" @@ -52,6 +53,7 @@ #include "swift/Basic/StringExtras.h" #include "swift/Strings.h" #include "swift/Subsystems.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "swift/Syntax/References.h" #include "swift/Syntax/SyntaxArena.h" #include "clang/AST/Type.h" @@ -65,6 +67,9 @@ #include #include +#include "RequirementMachine/RequirementMachine.h" +#include "RequirementMachine/RewriteContext.h" + using namespace swift; #define DEBUG_TYPE "ASTContext" @@ -245,6 +250,10 @@ struct ASTContext::Implementation { // Declare cached declarations for each of the known declarations. #define FUNC_DECL(Name, Id) FuncDecl *Get##Name = nullptr; #include "swift/AST/KnownDecls.def" + + // Declare cached declarations for each of the known declarations. +#define KNOWN_SDK_FUNC_DECL(Module, Name, Id) FuncDecl *Get##Name = nullptr; +#include "swift/AST/KnownSDKDecls.def" /// func Bool FuncDecl *LessThanIntDecl = nullptr; @@ -385,6 +394,7 @@ struct ASTContext::Implementation { llvm::DenseMap, ExistentialMetatypeType*> ExistentialMetatypeTypes; llvm::DenseMap ArraySliceTypes; + llvm::DenseMap VariadicSequenceTypes; llvm::DenseMap, DictionaryType *> DictionaryTypes; llvm::DenseMap OptionalTypes; llvm::DenseMap SimpleParenTypes; // Most are simple @@ -405,12 +415,6 @@ struct ASTContext::Implementation { llvm::FoldingSet LayoutConstraints; llvm::FoldingSet OpaqueArchetypes; - llvm::FoldingSet GenericSignatures; - - /// Stored generic signature builders for canonical generic signatures. - llvm::DenseMap> - GenericSignatureBuilders; - /// The set of function types. llvm::FoldingSet FunctionTypes; @@ -426,6 +430,10 @@ struct ASTContext::Implementation { /// The set of inherited protocol conformances. llvm::FoldingSet InheritedConformances; + /// The set of builtin protocol conformances. + llvm::DenseMap, + BuiltinProtocolConformance *> BuiltinConformances; + /// The set of substitution maps (uniqued by their storage). llvm::FoldingSet SubstitutionMaps; @@ -467,6 +475,12 @@ struct ASTContext::Implementation { llvm::FoldingSet AutoDiffDerivativeFunctionIdentifiers; + llvm::FoldingSet GenericSignatures; + + /// Stored generic signature builders for canonical generic signatures. + llvm::DenseMap> + GenericSignatureBuilders; + /// A cache of information about whether particular nominal types /// are representable in a foreign language. llvm::DenseMap @@ -521,6 +535,17 @@ struct ASTContext::Implementation { /// The scratch context used to allocate intrinsic data on behalf of \c swift::IntrinsicInfo std::unique_ptr IntrinsicScratchContext; #endif + + /// Memory allocation arena for the term rewriting system. + std::unique_ptr TheRewriteContext; + + /// Stored requirement machines for canonical generic signatures. + /// + /// This should come after TheRewriteContext above, since various destructors + /// compile stats in the histograms stored in our RewriteContext. + llvm::DenseMap> + RequirementMachines; }; ASTContext::Implementation::Implementation() @@ -570,6 +595,7 @@ ASTContext *ASTContext::get(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, SearchPathOptions &SearchPathOpts, ClangImporterOptions &ClangImporterOpts, + symbolgraphgen::SymbolGraphOptions &SymbolGraphOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags) { // If more than two data structures are concatentated, then the aggregate @@ -584,17 +610,19 @@ ASTContext *ASTContext::get(LangOptions &langOpts, new (impl) Implementation(); return new (mem) ASTContext(langOpts, typeckOpts, SearchPathOpts, ClangImporterOpts, - SourceMgr, Diags); + SymbolGraphOpts, SourceMgr, Diags); } ASTContext::ASTContext(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, SearchPathOptions &SearchPathOpts, ClangImporterOptions &ClangImporterOpts, + symbolgraphgen::SymbolGraphOptions &SymbolGraphOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags) : LangOpts(langOpts), TypeCheckerOpts(typeckOpts), SearchPathOpts(SearchPathOpts), ClangImporterOpts(ClangImporterOpts), + SymbolGraphOpts(SymbolGraphOpts), SourceMgr(SourceMgr), Diags(Diags), evaluator(Diags, langOpts), TheBuiltinModule(createBuiltinModule(*this)), @@ -656,6 +684,11 @@ llvm::BumpPtrAllocator &ASTContext::getAllocator(AllocationArena arena) const { llvm_unreachable("bad AllocationArena"); } +void *detail::allocateInASTContext(size_t bytes, const ASTContext &ctx, + AllocationArena arena, unsigned alignment) { + return ctx.Allocate(bytes, alignment, arena); +} + ImportPath::Raw swift::detail::ImportPathBuilder_copyToImpl(ASTContext &ctx, ImportPath::Raw raw) { @@ -694,10 +727,10 @@ Identifier ASTContext::getIdentifier(StringRef Str) const { return Identifier(I->getKeyData()); } -void ASTContext::lookupInSwiftModule( - StringRef name, - SmallVectorImpl &results) const { - ModuleDecl *M = getStdlibModule(); +void ASTContext::lookupInModule( + ModuleDecl *M, + StringRef name, + SmallVectorImpl &results) const { if (!M) return; @@ -706,6 +739,12 @@ void ASTContext::lookupInSwiftModule( M->lookupValue(identifier, NLKind::UnqualifiedLookup, results); } +void ASTContext::lookupInSwiftModule( + StringRef name, + SmallVectorImpl &results) const { + lookupInModule(getStdlibModule(), name, results); +} + FuncDecl *ASTContext::getPlusFunctionOnRangeReplaceableCollection() const { if (getImpl().PlusFunctionOnRangeReplaceableCollection) { return getImpl().PlusFunctionOnRangeReplaceableCollection; @@ -889,8 +928,7 @@ static VarDecl *getPointeeProperty(VarDecl *&cache, NominalTypeDecl *nominal = (ctx.*getNominal)(); if (!nominal) return nullptr; auto sig = nominal->getGenericSignature(); - if (!sig) return nullptr; - if (sig->getGenericParams().size() != 1) return nullptr; + if (sig.getGenericParams().size() != 1) return nullptr; // There must be a property named "pointee". auto identifier = ctx.getIdentifier("pointee"); @@ -900,7 +938,7 @@ static VarDecl *getPointeeProperty(VarDecl *&cache, // The property must have type T. auto *property = dyn_cast(results[0]); if (!property) return nullptr; - if (!property->getInterfaceType()->isEqual(sig->getGenericParams()[0])) + if (!property->getInterfaceType()->isEqual(sig.getGenericParams()[0])) return nullptr; cache = property; @@ -1000,11 +1038,17 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { M = getLoadedModule(Id_Differentiation); break; case KnownProtocolKind::Actor: + case KnownProtocolKind::GlobalActor: case KnownProtocolKind::AsyncSequence: case KnownProtocolKind::AsyncIteratorProtocol: case KnownProtocolKind::SerialExecutor: M = getLoadedModule(Id_Concurrency); break; + case KnownProtocolKind::DistributedActor: + case KnownProtocolKind::ActorTransport: + case KnownProtocolKind::ActorIdentity: + M = getLoadedModule(Id_Distributed); + break; default: M = getStdlibModule(); break; @@ -1025,16 +1069,24 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { return nullptr; } -/// Find the implementation for the given "intrinsic" library function. +/// Find the implementation for the given "intrinsic" library function, +/// in the passed in module. static FuncDecl *findLibraryIntrinsic(const ASTContext &ctx, + ModuleDecl *M, StringRef name) { SmallVector results; - ctx.lookupInSwiftModule(name, results); + ctx.lookupInModule(M, name, results); if (results.size() == 1) return dyn_cast_or_null(results.front()); return nullptr; } +/// Find the implementation for the given "intrinsic" library function. +static FuncDecl *findLibraryIntrinsic(const ASTContext &ctx, + StringRef name) { + return findLibraryIntrinsic(ctx, ctx.getStdlibModule(), name); +} + /// Returns the type of an intrinsic function if it is not generic, otherwise /// returns nullptr. static FunctionType * @@ -1484,7 +1536,7 @@ ASTContext::associateInfixOperators(PrecedenceGroupDecl *left, } // Find library intrinsic function. -static FuncDecl *findLibraryFunction(const ASTContext &ctx, FuncDecl *&cache, +static FuncDecl *findLibraryFunction(const ASTContext &ctx, FuncDecl *&cache, StringRef name) { if (cache) return cache; @@ -1493,12 +1545,33 @@ static FuncDecl *findLibraryFunction(const ASTContext &ctx, FuncDecl *&cache, return cache; } -#define FUNC_DECL(Name, Id) \ -FuncDecl *ASTContext::get##Name() const { \ +// Find library intrinsic function in passed in module +static FuncDecl *findLibraryFunction(const ASTContext &ctx, + ModuleDecl *M, FuncDecl *&cache, + StringRef name) { + if (cache) return cache; + + // Look for a generic function. + cache = findLibraryIntrinsic(ctx, M, name); + return cache; +} + +#define FUNC_DECL(Name, Id) \ +FuncDecl *ASTContext::get##Name() const { \ return findLibraryFunction(*this, getImpl().Get##Name, Id); \ } #include "swift/AST/KnownDecls.def" +#define KNOWN_SDK_FUNC_DECL(Module, Name, Id) \ +FuncDecl *ASTContext::get##Name() const { \ + if (ModuleDecl *M = getLoadedModule(Id_##Module)) { \ + return findLibraryFunction(*this, M, getImpl().Get##Name, Id); \ + } else { \ + return findLibraryFunction(*this, getImpl().Get##Name, Id); \ + } \ +} +#include "swift/AST/KnownSDKDecls.def" + bool ASTContext::hasOptionalIntrinsics() const { return getOptionalDecl() && getOptionalSomeDecl() && @@ -1584,20 +1657,23 @@ Optional ASTContext::getModuleDependencies( bool cacheOnly) { // Retrieve the dependencies for this module. if (cacheOnly) { + auto searchPathSet = getAllModuleSearchPathsSet(); // Check whether we've cached this result. if (!isUnderlyingClangModule) { - if (auto found = cache.findDependencies(moduleName, - ModuleDependenciesKind::SwiftTextual)) + if (auto found = cache.findDependencies( + moduleName, + {ModuleDependenciesKind::SwiftTextual, searchPathSet})) return found; - if (auto found = cache.findDependencies(moduleName, - ModuleDependenciesKind::SwiftTextual)) + if (auto found = cache.findDependencies( + moduleName, {ModuleDependenciesKind::SwiftBinary, searchPathSet})) return found; - if (auto found = cache.findDependencies(moduleName, - ModuleDependenciesKind::SwiftPlaceholder)) + if (auto found = cache.findDependencies( + moduleName, + {ModuleDependenciesKind::SwiftPlaceholder, searchPathSet})) return found; } - if (auto found = cache.findDependencies(moduleName, - ModuleDependenciesKind::Clang)) + if (auto found = cache.findDependencies( + moduleName, {ModuleDependenciesKind::Clang, searchPathSet})) return found; } else { for (auto &loader : getImpl().ModuleLoaders) { @@ -1605,8 +1681,8 @@ Optional ASTContext::getModuleDependencies( loader.get() != getImpl().TheClangModuleLoader) continue; - if (auto dependencies = loader->getModuleDependencies(moduleName, cache, - delegate)) + if (auto dependencies = + loader->getModuleDependencies(moduleName, cache, delegate)) return dependencies; } } @@ -1629,6 +1705,65 @@ ASTContext::getSwiftModuleDependencies(StringRef moduleName, return None; } +namespace { + static StringRef + pathStringFromFrameworkSearchPath(const SearchPathOptions::FrameworkSearchPath &next) { + return next.Path; + }; +} + +std::vector ASTContext::getDarwinImplicitFrameworkSearchPaths() +const { + assert(LangOpts.Target.isOSDarwin()); + SmallString<128> systemFrameworksScratch; + systemFrameworksScratch = SearchPathOpts.SDKPath; + llvm::sys::path::append(systemFrameworksScratch, "System", "Library", "Frameworks"); + + SmallString<128> frameworksScratch; + frameworksScratch = SearchPathOpts.SDKPath; + llvm::sys::path::append(frameworksScratch, "Library", "Frameworks"); + return {systemFrameworksScratch.str().str(), frameworksScratch.str().str()}; +} + +llvm::StringSet<> ASTContext::getAllModuleSearchPathsSet() +const { + llvm::StringSet<> result; + result.insert(SearchPathOpts.ImportSearchPaths.begin(), + SearchPathOpts.ImportSearchPaths.end()); + + // Framework paths are "special", they contain more than path strings, + // but path strings are all we care about here. + using FrameworkPathView = ArrayRefView; + FrameworkPathView frameworkPathsOnly{SearchPathOpts.FrameworkSearchPaths}; + result.insert(frameworkPathsOnly.begin(), frameworkPathsOnly.end()); + + if (LangOpts.Target.isOSDarwin()) { + auto implicitFrameworkSearchPaths = getDarwinImplicitFrameworkSearchPaths(); + result.insert(implicitFrameworkSearchPaths.begin(), + implicitFrameworkSearchPaths.end()); + } + result.insert(SearchPathOpts.RuntimeLibraryImportPaths.begin(), + SearchPathOpts.RuntimeLibraryImportPaths.end()); + + // ClangImporter special-cases the path for SwiftShims, so do the same here + // If there are no shims in the resource dir, add a search path in the SDK. + SmallString<128> shimsPath(SearchPathOpts.RuntimeResourcePath); + llvm::sys::path::append(shimsPath, "shims"); + if (!llvm::sys::fs::exists(shimsPath)) { + shimsPath = SearchPathOpts.SDKPath; + llvm::sys::path::append(shimsPath, "usr", "lib", "swift", "shims"); + } + result.insert(shimsPath.str()); + + // Clang system modules are found in the SDK root + SmallString<128> clangSysRootPath(SearchPathOpts.SDKPath); + llvm::sys::path::append(clangSysRootPath, "usr", "include"); + result.insert(clangSysRootPath.str()); + return result; +} + void ASTContext::loadExtensions(NominalTypeDecl *nominal, unsigned previousGeneration) { PrettyStackTraceDecl stackTrace("loading extensions for", nominal); @@ -1715,23 +1850,14 @@ void ASTContext::addLoadedModule(ModuleDecl *M) { getImpl().LoadedModules[M->getName()] = M; } -static AllocationArena getArena(GenericSignature genericSig) { - if (!genericSig) - return AllocationArena::Permanent; - - if (genericSig->hasTypeVariable()) - return AllocationArena::ConstraintSolver; - - return AllocationArena::Permanent; -} - void ASTContext::registerGenericSignatureBuilder( GenericSignature sig, GenericSignatureBuilder &&builder) { + if (LangOpts.EnableRequirementMachine == RequirementMachineMode::Enabled) + return; + auto canSig = sig.getCanonicalSignature(); - auto arena = getArena(sig); - auto &genericSignatureBuilders = - getImpl().getArena(arena).GenericSignatureBuilders; + auto &genericSignatureBuilders = getImpl().GenericSignatureBuilders; auto known = genericSignatureBuilders.find(canSig); if (known != genericSignatureBuilders.end()) { ++NumRegisteredGenericSignatureBuildersAlready; @@ -1745,11 +1871,15 @@ void ASTContext::registerGenericSignatureBuilder( GenericSignatureBuilder *ASTContext::getOrCreateGenericSignatureBuilder( CanGenericSignature sig) { + // We should only create GenericSignatureBuilders if the requirement machine + // mode is ::Disabled or ::Verify. + assert(LangOpts.EnableRequirementMachine != RequirementMachineMode::Enabled && + "Shouldn't create GenericSignatureBuilder when RequirementMachine " + "is enabled"); + // Check whether we already have a generic signature builder for this // signature and module. - auto arena = getArena(sig); - auto &genericSignatureBuilders = - getImpl().getArena(arena).GenericSignatureBuilders; + auto &genericSignatureBuilders = getImpl().GenericSignatureBuilders; auto known = genericSignatureBuilders.find(sig); if (known != genericSignatureBuilders.end()) return known->second.get(); @@ -1776,13 +1906,13 @@ GenericSignatureBuilder *ASTContext::getOrCreateGenericSignatureBuilder( reprocessedSig->print(llvm::errs()); llvm::errs() << "\n"; - if (sig->getGenericParams().size() == - reprocessedSig->getGenericParams().size() && - sig->getRequirements().size() == - reprocessedSig->getRequirements().size()) { - for (unsigned i : indices(sig->getRequirements())) { - auto sigReq = sig->getRequirements()[i]; - auto reprocessedReq = reprocessedSig->getRequirements()[i]; + if (sig.getGenericParams().size() == + reprocessedSig.getGenericParams().size() && + sig.getRequirements().size() == + reprocessedSig.getRequirements().size()) { + for (unsigned i : indices(sig.getRequirements())) { + auto sigReq = sig.getRequirements()[i]; + auto reprocessedReq = reprocessedSig.getRequirements()[i]; if (sigReq.getKind() != reprocessedReq.getKind()) { llvm::errs() << "Requirement mismatch:\n"; llvm::errs() << " Original: "; @@ -1825,6 +1955,55 @@ GenericSignatureBuilder *ASTContext::getOrCreateGenericSignatureBuilder( return builder; } +rewriting::RequirementMachine * +ASTContext::getOrCreateRequirementMachine(CanGenericSignature sig) { + auto &rewriteCtx = getImpl().TheRewriteContext; + if (!rewriteCtx) + rewriteCtx.reset(new rewriting::RewriteContext(*this)); + + // Check whether we already have a requirement machine for this + // signature. + auto &machines = getImpl().RequirementMachines; + + auto &machinePtr = machines[sig]; + if (machinePtr) { + auto *machine = machinePtr.get(); + if (!machine->isComplete()) { + llvm::errs() << "Re-entrant construction of requirement " + << "machine for " << sig << "\n"; + abort(); + } + + return machine; + } + + auto *machine = new rewriting::RequirementMachine(*rewriteCtx); + + // Store this requirement machine before adding the signature, + // to catch re-entrant construction via addGenericSignature() + // below. + machinePtr.reset(machine); + + machine->addGenericSignature(sig); + + return machine; +} + +bool ASTContext::isRecursivelyConstructingRequirementMachine( + CanGenericSignature sig) { + auto &rewriteCtx = getImpl().TheRewriteContext; + if (!rewriteCtx) + return false; + + auto &machines = getImpl().RequirementMachines; + + auto found = machines.find(sig); + if (found == machines.end()) + return false; + + return !found->second->isComplete(); +} + Optional> OverriddenDeclsRequest::getCachedResult() const { auto decl = std::get<0>(getStorage()); @@ -2101,7 +2280,8 @@ ASTContext::getConformance(Type conformingType, ProtocolDecl *protocol, SourceLoc loc, DeclContext *dc, - ProtocolConformanceState state) { + ProtocolConformanceState state, + bool isUnchecked) { assert(dc->isTypeContext()); llvm::FoldingSetNodeID id; @@ -2117,7 +2297,8 @@ ASTContext::getConformance(Type conformingType, // Build a new normal protocol conformance. auto result = new (*this, AllocationArena::Permanent) - NormalProtocolConformance(conformingType, protocol, loc, dc, state); + NormalProtocolConformance( + conformingType, protocol, loc, dc, state,isUnchecked); normalConformances.InsertNode(result, insertPos); return result; @@ -2136,6 +2317,29 @@ ASTContext::getSelfConformance(ProtocolDecl *protocol) { return entry; } +/// Produce the builtin conformance for some non-nominal to some protocol. +BuiltinProtocolConformance * +ASTContext::getBuiltinConformance( + Type type, ProtocolDecl *protocol, + GenericSignature genericSig, + ArrayRef conditionalRequirements, + BuiltinConformanceKind kind +) { + auto key = std::make_pair(type, protocol); + AllocationArena arena = getArena(type->getRecursiveProperties()); + auto &builtinConformances = getImpl().getArena(arena).BuiltinConformances; + + auto &entry = builtinConformances[key]; + if (!entry) { + auto size = BuiltinProtocolConformance:: + totalSizeToAlloc(conditionalRequirements.size()); + auto mem = this->Allocate(size, alignof(BuiltinProtocolConformance), arena); + entry = new (mem) BuiltinProtocolConformance( + type, protocol, genericSig, conditionalRequirements, kind); + } + return entry; +} + /// If one of the ancestor conformances already has a matching type, use /// that instead. static ProtocolConformance *collapseSpecializedConformance( @@ -2152,6 +2356,7 @@ static ProtocolConformance *collapseSpecializedConformance( case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Inherited: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: // If the conformance matches, return it. if (conformance->getType()->isEqual(type)) { for (auto subConformance : substitutions.getConformances()) @@ -2372,6 +2577,7 @@ size_t ASTContext::Implementation::Arena::getTotalMemory() const { llvm::capacity_in_bytes(ArraySliceTypes) + llvm::capacity_in_bytes(DictionaryTypes) + llvm::capacity_in_bytes(OptionalTypes) + + llvm::capacity_in_bytes(VariadicSequenceTypes) + llvm::capacity_in_bytes(SimpleParenTypes) + llvm::capacity_in_bytes(ParenTypes) + llvm::capacity_in_bytes(ReferenceStorageTypes) + @@ -2389,6 +2595,7 @@ size_t ASTContext::Implementation::Arena::getTotalMemory() const { // NormalConformances ? // SpecializedConformances ? // InheritedConformances ? + // BuiltinConformances ? } void AbstractFunctionDecl::setForeignErrorConvention( @@ -2708,6 +2915,7 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, bool isStatic = false; SelfAccessKind selfAccess = SelfAccessKind::NonMutating; bool isDynamicSelf = false; + bool isIsolated = false; if (auto *FD = dyn_cast(AFD)) { isStatic = FD->isStatic(); @@ -2724,12 +2932,40 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, // FIXME: All methods of non-final classes should have this. else if (wantDynamicSelf && FD->hasDynamicSelfResult()) isDynamicSelf = true; + + // If this is a non-static method within an actor, the 'self' parameter + // is isolated if this declaration is isolated. + isIsolated = evaluateOrDefault( + Ctx.evaluator, HasIsolatedSelfRequest{AFD}, false); } else if (auto *CD = dyn_cast(AFD)) { if (isInitializingCtor) { // initializing constructors of value types always have an implicitly // inout self. if (!containerTy->hasReferenceSemantics()) selfAccess = SelfAccessKind::Mutating; + + // FIXME(distributed): pending swift-evolution, allow `self =` in class + // inits in general. + // See also: https://github.com/apple/swift/pull/19151 general impl + if (Ctx.LangOpts.EnableExperimentalDistributed) { + auto ext = dyn_cast(AFD->getDeclContext()); + auto distProto = + Ctx.getProtocol(KnownProtocolKind::DistributedActor); + if (distProto && ext && ext->getExtendedNominal() && + ext->getExtendedNominal()->getInterfaceType() + ->isEqual(distProto->getInterfaceType())) { + auto name = CD->getName(); + auto params = name.getArgumentNames(); + if (params.size() == 1 && params[0] == Ctx.Id_from) { + // FIXME(distributed): this is a workaround to allow init(from:) to + // be implemented in AST by allowing the self to be mutable in the + // decoding initializer. This should become a general Swift + // feature, allowing this in all classes: + // https://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924 + selfAccess = SelfAccessKind::Mutating; + } + } + } } else { // allocating constructors have metatype 'self'. isStatic = true; @@ -2739,7 +2975,7 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, if (Ctx.isSwiftVersionAtLeast(5)) { if (wantDynamicSelf && CD->isConvenienceInit()) if (auto *classDecl = selfTy->getClassOrBoundGenericClass()) - if (!classDecl->isFinal()) + if (!classDecl->isSemanticallyFinal()) isDynamicSelf = true; } } else if (isa(AFD)) { @@ -2757,7 +2993,7 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, if (isStatic) return AnyFunctionType::Param(MetatypeType::get(selfTy, Ctx)); - auto flags = ParameterTypeFlags(); + auto flags = ParameterTypeFlags().withIsolated(isIsolated); switch (selfAccess) { case SelfAccessKind::Consuming: flags = flags.withOwned(true); @@ -3204,7 +3440,7 @@ AnyFunctionType *AnyFunctionType::withExtInfo(ExtInfo info) const { getParams(), getResult(), info); } -void AnyFunctionType::decomposeInput( +void AnyFunctionType::decomposeTuple( Type type, SmallVectorImpl &result) { switch (type->getKind()) { case TypeKind::Tuple: { @@ -3231,7 +3467,8 @@ void AnyFunctionType::decomposeInput( result.emplace_back( type->getInOutObjectType(), Identifier(), ParameterTypeFlags::fromParameterType(type, false, false, false, - ValueOwnership::Default, false)); + ValueOwnership::Default, false, + false)); return; } } @@ -3247,12 +3484,12 @@ Type AnyFunctionType::Param::getParameterType(bool forCanonical, else if (forCanonical) type = BoundGenericType::get(arrayDecl, Type(), {type}); else - type = ArraySliceType::get(type); + type = VariadicSequenceType::get(type); } return type; } -Type AnyFunctionType::composeInput(ASTContext &ctx, ArrayRef params, +Type AnyFunctionType::composeTuple(ASTContext &ctx, ArrayRef params, bool canonicalVararg) { SmallVector elements; for (const auto ¶m : params) { @@ -3494,12 +3731,12 @@ GenericTypeParamType *GenericTypeParamType::get(unsigned depth, unsigned index, TypeArrayView GenericFunctionType::getGenericParams() const { - return Signature->getGenericParams(); + return Signature.getGenericParams(); } /// Retrieve the requirements of this polymorphic function type. ArrayRef GenericFunctionType::getRequirements() const { - return Signature->getRequirements(); + return Signature.getRequirements(); } void SILFunctionType::Profile( @@ -3633,7 +3870,7 @@ SILFunctionType::SILFunctionType( "If all generic parameters are concrete, SILFunctionType should " "not have a generic signature at all"); - for (auto gparam : genericSig->getGenericParams()) { + for (auto gparam : genericSig.getGenericParams()) { (void)gparam; assert(gparam->isCanonical() && "generic signature is not canonicalized"); } @@ -3819,6 +4056,18 @@ ArraySliceType *ArraySliceType::get(Type base) { return entry = new (C, arena) ArraySliceType(C, base, properties); } +VariadicSequenceType *VariadicSequenceType::get(Type base) { + auto properties = base->getRecursiveProperties(); + auto arena = getArena(properties); + + const ASTContext &C = base->getASTContext(); + + VariadicSequenceType *&entry = C.getImpl().getArena(arena).VariadicSequenceTypes[base]; + if (entry) return entry; + + return entry = new (C, arena) VariadicSequenceType(C, base, properties); +} + DictionaryType *DictionaryType::get(Type keyType, Type valueType) { auto properties = keyType->getRecursiveProperties() | valueType->getRecursiveProperties(); @@ -3938,9 +4187,12 @@ DependentMemberType *DependentMemberType::get(Type base, } OpaqueTypeArchetypeType * -OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl, - SubstitutionMap Substitutions) -{ +OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl, unsigned ordinal, + SubstitutionMap Substitutions) { + // TODO [OPAQUE SUPPORT]: multiple opaque types + assert(ordinal == 0 && "we only support one 'some' type per composite type"); + auto opaqueParamType = Decl->getUnderlyingInterfaceType(); + // TODO: We could attempt to preserve type sugar in the substitution map. // Currently archetypes are assumed to be always canonical in many places, // though, so doing so would require fixing those places. @@ -4009,25 +4261,25 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl, // Same-type-constrain the arguments in the outer signature to their // replacements in the substitution map. if (auto outerSig = Decl->getGenericSignature()) { - for (auto outerParam : outerSig->getGenericParams()) { + for (auto outerParam : outerSig.getGenericParams()) { auto boundType = Type(outerParam).subst(Substitutions); newRequirements.push_back( Requirement(RequirementKind::SameType, Type(outerParam), boundType)); } } #else - // Assert that there are no same type constraints on the underlying type - // or its associated types. + // Assert that there are no same type constraints on the opaque type or its + // associated types. // // This should not be possible until we add where clause support, with the // exception of generic base class constraints (handled below). (void)newRequirements; # ifndef NDEBUG - for (auto reqt : - Decl->getOpaqueInterfaceGenericSignature()->getRequirements()) { - auto reqtBase = reqt.getFirstType()->getRootGenericParam(); - if (reqtBase->isEqual(Decl->getUnderlyingInterfaceType())) { - assert(reqt.getKind() != RequirementKind::SameType + for (auto req : + Decl->getOpaqueInterfaceGenericSignature().getRequirements()) { + auto reqBase = req.getFirstType()->getRootGenericParam(); + if (reqBase->isEqual(opaqueParamType)) { + assert(req.getKind() != RequirementKind::SameType && "supporting where clauses on opaque types requires correctly " "setting up the generic environment for " "OpaqueTypeArchetypeTypes; see comment above"); @@ -4043,9 +4295,8 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl, std::move(newRequirements)}, nullptr); - auto opaqueInterfaceTy = Decl->getUnderlyingInterfaceType(); - auto layout = signature->getLayoutConstraint(opaqueInterfaceTy); - auto superclass = signature->getSuperclassBound(opaqueInterfaceTy); + auto reqs = signature->getLocalRequirements(opaqueParamType); + auto superclass = reqs.superclass; #if !DO_IT_CORRECTLY // Ad-hoc substitute the generic parameters of the superclass. // If we correctly applied the substitutions to the generic signature @@ -4054,26 +4305,23 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl, superclass = superclass.subst(Substitutions); } #endif - const auto protos = signature->getRequiredProtocols(opaqueInterfaceTy); auto mem = ctx.Allocate( OpaqueTypeArchetypeType::totalSizeToAlloc( - protos.size(), superclass ? 1 : 0, layout ? 1 : 0), + reqs.protos.size(), superclass ? 1 : 0, reqs.layout ? 1 : 0), alignof(OpaqueTypeArchetypeType), arena); - - auto newOpaque = ::new (mem) OpaqueTypeArchetypeType(Decl, Substitutions, - properties, - opaqueInterfaceTy, - protos, superclass, layout); - + + auto newOpaque = ::new (mem) + OpaqueTypeArchetypeType(Decl, Substitutions, properties, opaqueParamType, + reqs.protos, superclass, reqs.layout); + // Create a generic environment and bind the opaque archetype to the // opaque interface type from the decl's signature. - auto *builder = signature->getGenericSignatureBuilder(); - auto *env = GenericEnvironment::getIncomplete(signature, builder); - env->addMapping(GenericParamKey(opaqueInterfaceTy), newOpaque); + auto *env = GenericEnvironment::getIncomplete(signature); + env->addMapping(GenericParamKey(opaqueParamType), newOpaque); newOpaque->Environment = env; - + // Look up the insertion point in the folding set again in case something // invalidated it above. { @@ -4083,7 +4331,7 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl, assert(!existing && "race to create opaque archetype?!"); set.InsertNode(newOpaque, insertPos); } - + return newOpaque; } @@ -4147,9 +4395,8 @@ GenericEnvironment *OpenedArchetypeType::getGenericEnvironment() const { auto &ctx = thisType->getASTContext(); // Create a generic environment to represent the opened type. auto signature = ctx.getOpenedArchetypeSignature(Opened); - auto *builder = signature->getGenericSignatureBuilder(); - auto *env = GenericEnvironment::getIncomplete(signature, builder); - env->addMapping(signature->getGenericParams()[0], thisType); + auto *env = GenericEnvironment::getIncomplete(signature); + env->addMapping(signature.getGenericParams().front().getPointer(), thisType); Environment = env; return env; @@ -4269,21 +4516,21 @@ GenericSignature::get(TypeArrayView params, assert(!params.empty()); #ifndef NDEBUG - for (auto req : requirements) + for (auto req : requirements) { assert(req.getFirstType()->isTypeParameter()); + assert(!req.getFirstType()->hasTypeVariable()); + assert(req.getKind() == RequirementKind::Layout || + !req.getSecondType()->hasTypeVariable()); + } #endif // Check for an existing generic signature. llvm::FoldingSetNodeID ID; GenericSignatureImpl::Profile(ID, params, requirements); - auto arena = GenericSignature::hasTypeVariable(requirements) - ? AllocationArena::ConstraintSolver - : AllocationArena::Permanent; - auto &ctx = getASTContext(params, requirements); void *insertPos; - auto &sigs = ctx.getImpl().getArena(arena).GenericSignatures; + auto &sigs = ctx.getImpl().GenericSignatures; if (auto *sig = sigs.FindNodeOrInsertPos(ID, insertPos)) { if (isKnownCanonical) sig->CanonicalSignatureOrASTContext = &ctx; @@ -4298,20 +4545,19 @@ GenericSignature::get(TypeArrayView params, void *mem = ctx.Allocate(bytes, alignof(GenericSignatureImpl)); auto *newSig = new (mem) GenericSignatureImpl(params, requirements, isKnownCanonical); - ctx.getImpl().getArena(arena).GenericSignatures.InsertNode(newSig, insertPos); + ctx.getImpl().GenericSignatures.InsertNode(newSig, insertPos); return newSig; } GenericEnvironment *GenericEnvironment::getIncomplete( - GenericSignature signature, - GenericSignatureBuilder *builder) { + GenericSignature signature) { auto &ctx = signature->getASTContext(); // Allocate and construct the new environment. - unsigned numGenericParams = signature->getGenericParams().size(); + unsigned numGenericParams = signature.getGenericParams().size(); size_t bytes = totalSizeToAlloc(numGenericParams); void *mem = ctx.Allocate(bytes, alignof(GenericEnvironment)); - return new (mem) GenericEnvironment(signature, builder); + return new (mem) GenericEnvironment(signature); } void DeclName::CompoundDeclName::Profile(llvm::FoldingSetNodeID &id, @@ -4802,7 +5048,7 @@ CanGenericSignature ASTContext::getOpenedArchetypeSignature(Type type) { // The opened archetype signature for a protocol type is identical // to the protocol's own canonical generic signature. if (const auto protoTy = dyn_cast(existential)) { - return protoTy->getDecl()->getGenericSignature()->getCanonicalSignature(); + return protoTy->getDecl()->getGenericSignature().getCanonicalSignature(); } auto found = getImpl().ExistentialSignatures.find(existential); @@ -4871,9 +5117,9 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, unsigned derivedDepth = 0; unsigned baseDepth = 0; if (derivedClassSig) - derivedDepth = derivedClassSig->getGenericParams().back()->getDepth() + 1; + derivedDepth = derivedClassSig.getGenericParams().back()->getDepth() + 1; if (const auto baseClassSig = baseClass->getGenericSignature()) - baseDepth = baseClassSig->getGenericParams().back()->getDepth() + 1; + baseDepth = baseClassSig.getGenericParams().back()->getDepth() + 1; SmallVector addedGenericParams; if (const auto *gpList = derived->getAsGenericContext()->getGenericParams()) { @@ -4907,7 +5153,7 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, }; SmallVector addedRequirements; - for (auto reqt : baseGenericSig->getRequirements()) { + for (auto reqt : baseGenericSig.getRequirements()) { if (auto substReqt = reqt.subst(substFn, lookupConformanceFn)) { addedRequirements.push_back(*substReqt); } @@ -4927,11 +5173,22 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, bool ASTContext::overrideGenericSignatureReqsSatisfied( const ValueDecl *base, const ValueDecl *derived, const OverrideGenericSignatureReqCheck direction) { + auto *baseCtx = base->getAsGenericContext(); + auto *derivedCtx = derived->getAsGenericContext(); + + if (baseCtx->isGeneric() != derivedCtx->isGeneric()) + return false; + + if (baseCtx->isGeneric() && + (baseCtx->getGenericParams()->size() != + derivedCtx->getGenericParams()->size())) + return false; + auto sig = getOverrideGenericSignature(base, derived); if (!sig) return true; - auto derivedSig = derived->getAsGenericContext()->getGenericSignature(); + auto derivedSig = derivedCtx->getGenericSignature(); switch (direction) { case OverrideGenericSignatureReqCheck::BaseReqSatisfiedByDerived: @@ -5005,7 +5262,7 @@ CanSILBoxType SILBoxType::get(ASTContext &C, CanSILBoxType SILBoxType::get(CanType boxedType) { auto &ctx = boxedType->getASTContext(); auto singleGenericParamSignature = ctx.getSingleGenericParameterSignature(); - auto genericParam = singleGenericParamSignature->getGenericParams()[0]; + auto genericParam = singleGenericParamSignature.getGenericParams()[0]; auto layout = SILLayout::get(ctx, singleGenericParamSignature, SILField(CanType(genericParam), /*mutable*/ true)); @@ -5163,9 +5420,7 @@ AutoDiffDerivativeFunctionIdentifier *AutoDiffDerivativeFunctionIdentifier::get( llvm::FoldingSetNodeID id; id.AddInteger((unsigned)kind); id.AddPointer(parameterIndices); - CanGenericSignature derivativeCanGenSig; - if (derivativeGenericSignature) - derivativeCanGenSig = derivativeGenericSignature->getCanonicalSignature(); + auto derivativeCanGenSig = derivativeGenericSignature.getCanonicalSignature(); id.AddPointer(derivativeCanGenSig.getPointer()); void *insertPos; diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 80267a04890ce..355521b7f5e30 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -205,7 +205,7 @@ createSubstitutionMapFromGenericArgs(GenericSignature genericSig, if (!genericSig) return SubstitutionMap(); - if (genericSig->getGenericParams().size() != args.size()) + if (genericSig.getGenericParams().size() != args.size()) return SubstitutionMap(); return SubstitutionMap::get( @@ -262,7 +262,7 @@ Type ASTBuilder::resolveOpaqueType(NodePointer opaqueDescriptor, auto opaqueDecl = parentModule->lookupOpaqueResultType(mangledName); if (!opaqueDecl) return Type(); - // TODO: multiple opaque types + // TODO [OPAQUE SUPPORT]: multiple opaque types assert(ordinal == 0 && "not implemented"); if (ordinal != 0) return Type(); @@ -275,7 +275,7 @@ Type ASTBuilder::resolveOpaqueType(NodePointer opaqueDescriptor, SubstitutionMap subs = createSubstitutionMapFromGenericArgs( opaqueDecl->getGenericSignature(), allArgs, LookUpConformanceInModule(parentModule)); - return OpaqueTypeArchetypeType::get(opaqueDecl, subs); + return OpaqueTypeArchetypeType::get(opaqueDecl, ordinal, subs); } // TODO: named opaque types @@ -306,7 +306,7 @@ Type ASTBuilder::createBoundGenericType(GenericTypeDecl *decl, auto genericSig = aliasDecl->getGenericSignature(); for (unsigned i = 0, e = args.size(); i < e; ++i) { - auto origTy = genericSig->getInnermostGenericParams()[i]; + auto origTy = genericSig.getInnermostGenericParams()[i]; auto substTy = args[i]; subs[origTy->getCanonicalType()->castTo()] = @@ -344,7 +344,7 @@ Type ASTBuilder::createTupleType(ArrayRef eltTypes, StringRef labels) { Type ASTBuilder::createFunctionType( ArrayRef> params, Type output, FunctionTypeFlags flags, - FunctionMetadataDifferentiabilityKind diffKind) { + FunctionMetadataDifferentiabilityKind diffKind, Type globalActor) { // The result type must be materializable. if (!output->isMaterializable()) return Type(); @@ -407,14 +407,12 @@ Type ASTBuilder::createFunctionType( clangFunctionType = Ctx.getClangFunctionType(funcParams, output, representation); - Type globalActor; - // FIXME: Demangle global actors. - auto einfo = FunctionType::ExtInfoBuilder(representation, noescape, flags.isThrowing(), resultDiffKind, clangFunctionType, globalActor) .withAsync(flags.isAsync()) + .withConcurrent(flags.isSendable()) .build(); return FunctionType::get(funcParams, output, einfo); @@ -1060,6 +1058,15 @@ ASTBuilder::findTypeDecl(DeclContext *dc, result = candidate; } + // If we looked into the standard library module, but didn't find anything, + // try the _Concurrency module, which is also mangled into the Swift module. + if (!result && !dc->getParent() && module->isStdlibModule()) { + ASTContext &ctx = module->getASTContext(); + if (auto concurrencyModule = ctx.getLoadedModule(ctx.Id_Concurrency)) { + return findTypeDecl(concurrencyModule, name, privateDiscriminator, kind); + } + } + return result; } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 10d458ec57a63..eed46cbb9542f 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -21,7 +21,6 @@ #include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" -#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" @@ -114,107 +113,6 @@ namespace { }; } // end anonymous namespace -//===----------------------------------------------------------------------===// -// Generic param list printing. -//===----------------------------------------------------------------------===// - -void RequirementRepr::dump() const { - print(llvm::errs()); - llvm::errs() << "\n"; -} - -void RequirementRepr::printImpl(ASTPrinter &out) const { - auto printLayoutConstraint = - [&](const LayoutConstraintLoc &LayoutConstraintLoc) { - LayoutConstraintLoc.getLayoutConstraint()->print(out, PrintOptions()); - }; - - switch (getKind()) { - case RequirementReprKind::LayoutConstraint: - if (auto *repr = getSubjectRepr()) { - repr->print(out, PrintOptions()); - } - out << " : "; - printLayoutConstraint(getLayoutConstraintLoc()); - break; - - case RequirementReprKind::TypeConstraint: - if (auto *repr = getSubjectRepr()) { - repr->print(out, PrintOptions()); - } - out << " : "; - if (auto *repr = getConstraintRepr()) { - repr->print(out, PrintOptions()); - } - break; - - case RequirementReprKind::SameType: - if (auto *repr = getFirstTypeRepr()) { - repr->print(out, PrintOptions()); - } - out << " == "; - if (auto *repr = getSecondTypeRepr()) { - repr->print(out, PrintOptions()); - } - break; - } -} - -void RequirementRepr::print(raw_ostream &out) const { - StreamPrinter printer(out); - printImpl(printer); -} -void RequirementRepr::print(ASTPrinter &out) const { - printImpl(out); -} - -static void printTrailingRequirements(ASTPrinter &Printer, - ArrayRef Reqs, - bool printWhereKeyword) { - if (Reqs.empty()) return; - - if (printWhereKeyword) - Printer << " where "; - interleave( - Reqs, - [&](const RequirementRepr &req) { - Printer.callPrintStructurePre(PrintStructureKind::GenericRequirement); - req.print(Printer); - Printer.printStructurePost(PrintStructureKind::GenericRequirement); - }, - [&] { Printer << ", "; }); -} - -void GenericParamList::print(llvm::raw_ostream &OS) const { - OS << '<'; - interleave(*this, - [&](const GenericTypeParamDecl *P) { - OS << P->getName(); - if (!P->getInherited().empty()) { - OS << " : "; - P->getInherited()[0].getType().print(OS); - } - }, - [&] { OS << ", "; }); - - StreamPrinter Printer(OS); - printTrailingRequirements(Printer, getRequirements(), - /*printWhereKeyword*/true); - OS << '>'; -} - -void GenericParamList::dump() const { - print(llvm::errs()); - llvm::errs() << '\n'; -} - -void TrailingWhereClause::print(llvm::raw_ostream &OS, - bool printWhereKeyword) const { - StreamPrinter Printer(OS); - printTrailingRequirements(Printer, getRequirements(), - printWhereKeyword); -} - static void printGenericParameters(raw_ostream &OS, GenericParamList *Params) { if (!Params) return; @@ -601,11 +499,12 @@ namespace { PrintWithColorRAII(OS, DeclModifierColor) << " trailing_semi"; } - void printInherited(ArrayRef Inherited) { + void printInherited(ArrayRef Inherited) { if (Inherited.empty()) return; OS << " inherits: "; - interleave(Inherited, [&](TypeLoc Super) { Super.getType().print(OS); }, + interleave(Inherited, + [&](InheritedEntry Super) { Super.getType().print(OS); }, [&] { OS << ", "; }); } @@ -956,6 +855,10 @@ namespace { D->getCaptureInfo().print(OS); } + if (D->isDistributed()) { + OS << " distributed"; + } + if (auto fac = D->getForeignAsyncConvention()) { OS << " foreign_async="; if (auto type = fac->completionHandlerType()) @@ -1242,43 +1145,23 @@ namespace { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void printOperatorIdentifiers(OperatorDecl *OD) { - auto identifiers = OD->getIdentifiers(); - for (auto index : indices(identifiers)) { - OS.indent(Indent + 2); - OS << "identifier #" << index << " " << identifiers[index].Item; - if (index != identifiers.size() - 1) - OS << "\n"; - } - } - void visitInfixOperatorDecl(InfixOperatorDecl *IOD) { printCommon(IOD, "infix_operator_decl"); OS << " " << IOD->getName(); - if (!IOD->getIdentifiers().empty()) { - OS << "\n"; - printOperatorIdentifiers(IOD); - } + if (!IOD->getPrecedenceGroupName().empty()) + OS << " precedence_group_name=" << IOD->getPrecedenceGroupName(); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitPrefixOperatorDecl(PrefixOperatorDecl *POD) { printCommon(POD, "prefix_operator_decl"); OS << " " << POD->getName(); - if (!POD->getIdentifiers().empty()) { - OS << "\n"; - printOperatorIdentifiers(POD); - } PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitPostfixOperatorDecl(PostfixOperatorDecl *POD) { printCommon(POD, "postfix_operator_decl"); OS << " " << POD->getName(); - if (!POD->getIdentifiers().empty()) { - OS << "\n"; - printOperatorIdentifiers(POD); - } PrintWithColorRAII(OS, ParenthesisColor) << ')'; } @@ -1417,9 +1300,11 @@ std::string ValueDecl::printRef() const { } void ValueDecl::dumpRef(raw_ostream &os) const { - // Print the context. - printContext(os, getDeclContext()); - os << "."; + if (!isa(this)) { + // Print the context. + printContext(os, getDeclContext()); + os << "."; + } // Print name. getName().printPretty(os); @@ -2496,8 +2381,7 @@ class PrintExpr : public ExprVisitor { for (auto capture : E->getCaptureList()) { OS << '\n'; Indent += 2; - printRec(capture.Var); - printRec(capture.Init); + printRec(capture.PBD); Indent -= 2; } printRec(E->getClosureBody()); @@ -2878,6 +2762,9 @@ class PrintExpr : public ExprVisitor { PrintWithColorRAII(OS, IdentifierColor) << " key='" << component.getUnresolvedDeclName() << "'"; break; + case KeyPathExpr::Component::Kind::CodeCompletion: + PrintWithColorRAII(OS, ASTNodeColor) << "completion"; + break; } PrintWithColorRAII(OS, TypeColor) << " type='" << GetTypeOfKeyPathComponent(E, i) << "'"; @@ -3115,6 +3002,12 @@ class PrintTypeRepr : public TypeReprVisitor { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + void visitIsolatedTypeRepr(IsolatedTypeRepr *T) { + printCommon("isolated") << '\n'; + printRec(T->getBase()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + void visitOptionalTypeRepr(OptionalTypeRepr *T) { printCommon("type_optional") << '\n'; printRec(T->getBase()); @@ -3134,6 +3027,12 @@ class PrintTypeRepr : public TypeReprVisitor { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + void visitNamedOpaqueReturnTypeRepr(NamedOpaqueReturnTypeRepr *T) { + printCommon("type_named_opaque_return") << '\n'; + printRec(T->getBase()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + void visitPlaceholderTypeRepr(PlaceholderTypeRepr *T) { printCommon("type_placeholder"); PrintWithColorRAII(OS, ParenthesisColor) << ')'; @@ -3343,6 +3242,10 @@ static void dumpProtocolConformanceRec( visited); break; } + + case ProtocolConformanceKind::Builtin: { + printCommon("builtin"); + } } PrintWithColorRAII(out, ParenthesisColor) << ')'; @@ -3367,7 +3270,7 @@ static void dumpSubstitutionMapRec( } genericSig->print(out); - auto genericParams = genericSig->getGenericParams(); + auto genericParams = genericSig.getGenericParams(); auto replacementTypes = static_cast(map).getReplacementTypesBuffer(); for (unsigned i : indices(genericParams)) { @@ -3396,7 +3299,7 @@ static void dumpSubstitutionMapRec( return; auto conformances = map.getConformances(); - for (const auto &req : genericSig->getRequirements()) { + for (const auto &req : genericSig.getRequirements()) { if (req.getKind() != RequirementKind::Conformance) continue; @@ -3937,6 +3840,12 @@ namespace { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + void visitVariadicSequenceType(VariadicSequenceType *T, StringRef label) { + printCommon(label, "variadic_sequence_type"); + printRec(T->getBaseType()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + void visitProtocolCompositionType(ProtocolCompositionType *T, StringRef label) { printCommon(label, "protocol_composition_type"); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 11d9d132411a1..adcd303f0b9a5 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -89,7 +89,7 @@ std::string ASTMangler::mangleClosureEntity(const AbstractClosureExpr *closure, std::string ASTMangler::mangleEntity(const ValueDecl *decl, SymbolKind SKind) { beginMangling(); - appendEntity(decl, SKind == SymbolKind::AsyncHandlerBody); + appendEntity(decl); appendSymbolKind(SKind); return finalize(); } @@ -209,9 +209,11 @@ std::string ASTMangler::mangleWitnessTable(const RootProtocolConformance *C) { if (isa(C)) { appendProtocolConformance(C); appendOperator("WP"); - } else { + } else if (isa(C)) { appendProtocolName(cast(C)->getProtocol()); appendOperator("WS"); + } else { + llvm_unreachable("mangling unknown conformance kind"); } return finalize(); } @@ -235,7 +237,11 @@ std::string ASTMangler::mangleWitnessThunk( } if (Conformance) { - appendOperator(isa(Conformance) ? "TS" : "TW"); + if (isa(Conformance)) { + appendOperator("TS"); + } else { + appendOperator("TW"); + } } return finalize(); } @@ -282,12 +288,12 @@ std::string ASTMangler::mangleKeyPathGetterThunkHelper( appendEntity(property); if (signature) appendGenericSignature(signature); - appendType(baseType); + appendType(baseType, signature); if (isa(property)) { // Subscripts can be generic, and different key paths could capture the same // subscript at different generic arguments. for (auto sub : subs.getReplacementTypes()) { - appendType(sub->mapTypeOutOfContext()->getCanonicalType()); + appendType(sub->mapTypeOutOfContext()->getCanonicalType(), signature); } } appendOperator("TK"); @@ -306,12 +312,12 @@ std::string ASTMangler::mangleKeyPathSetterThunkHelper( appendEntity(property); if (signature) appendGenericSignature(signature); - appendType(baseType); + appendType(baseType, signature); if (isa(property)) { // Subscripts can be generic, and different key paths could capture the same // subscript at different generic arguments. for (auto sub : subs.getReplacementTypes()) { - appendType(sub->mapTypeOutOfContext()->getCanonicalType()); + appendType(sub->mapTypeOutOfContext()->getCanonicalType(), signature); } } appendOperator("Tk"); @@ -325,7 +331,7 @@ std::string ASTMangler::mangleKeyPathEqualsHelper(ArrayRef indices, ResilienceExpansion expansion) { beginMangling(); for (auto &index : indices) - appendType(index); + appendType(index, nullptr); if (signature) appendGenericSignature(signature); appendOperator("TH"); @@ -339,7 +345,7 @@ std::string ASTMangler::mangleKeyPathHashHelper(ArrayRef indices, ResilienceExpansion expansion) { beginMangling(); for (auto &index : indices) - appendType(index); + appendType(index, nullptr); if (signature) appendGenericSignature(signature); appendOperator("Th"); @@ -379,18 +385,17 @@ std::string ASTMangler::mangleReabstractionThunkHelper( Type FromType, Type ToType, Type SelfType, + Type GlobalActorBound, ModuleDecl *Module) { Mod = Module; assert(ThunkType->getPatternSubstitutions().empty() && "not implemented"); GenericSignature GenSig = ThunkType->getInvocationGenericSignature(); - if (GenSig) - CurGenericSignature = GenSig.getCanonicalSignature(); beginMangling(); - appendType(FromType); - appendType(ToType); + appendType(FromType, GenSig); + appendType(ToType, GenSig); if (SelfType) - appendType(SelfType); + appendType(SelfType, GenSig); if (GenSig) appendGenericSignature(GenSig); @@ -399,6 +404,11 @@ std::string ASTMangler::mangleReabstractionThunkHelper( appendOperator("Ty"); else appendOperator("TR"); + + if (GlobalActorBound) { + appendType(GlobalActorBound, GenSig); + appendOperator("TU"); + } return finalize(); } @@ -410,8 +420,8 @@ std::string ASTMangler::mangleObjCAsyncCompletionHandlerImpl( Optional ErrorOnZero, bool predefined) { beginMangling(); - appendType(BlockType); - appendType(ResultType); + appendType(BlockType, Sig); + appendType(ResultType, Sig); if (Sig) appendGenericSignature(Sig); if (ErrorOnZero) @@ -447,6 +457,14 @@ void ASTMangler::beginManglingWithAutoDiffOriginalFunction( appendOperator(attr->Name); return; } + // For imported Clang declarations, use the Clang name in order to match how + // DifferentiationMangler handles these. + auto clangDecl = getClangDeclForMangling(afd); + if (clangDecl) { + beginManglingWithoutPrefix(); + appendOperator(clangDecl->getName()); + return; + } beginMangling(); if (auto *cd = dyn_cast(afd)) appendConstructorEntity(cd, /*isAllocating*/ !cd->isConvenienceInit()); @@ -471,8 +489,8 @@ std::string ASTMangler::mangleAutoDiffSelfReorderingReabstractionThunk( CanType fromType, CanType toType, GenericSignature signature, AutoDiffLinearMapKind linearMapKind) { beginMangling(); - appendType(fromType); - appendType(toType); + appendType(fromType, signature); + appendType(toType, signature); if (signature) appendGenericSignature(signature); auto kindCode = (char)getAutoDiffFunctionKind(linearMapKind); @@ -616,15 +634,14 @@ std::string ASTMangler::mangleTypeForDebugger(Type Ty, GenericSignature sig) { Ty = getTypeForDWARFMangling(Ty); - bindGenericParameters(sig); - appendType(Ty); + appendType(Ty, sig); appendOperator("D"); return finalize(); } std::string ASTMangler::mangleTypeForTypeName(Type type) { beginManglingWithoutPrefix(); - appendType(type); + appendType(type, nullptr); return finalize(); } @@ -723,9 +740,9 @@ std::string ASTMangler::mangleTypeAsUSR(Type Ty) { Ty = getTypeForDWARFMangling(Ty); if (auto *fnType = Ty->getAs()) { - appendFunction(fnType); + appendFunction(fnType, nullptr); } else { - appendType(Ty); + appendType(Ty, nullptr); } appendOperator("D"); @@ -743,9 +760,6 @@ std::string ASTMangler::mangleDeclAsUSR(const ValueDecl *Decl, llvm::SaveAndRestore allowUnnamedRAII(AllowNamelessEntities, true); Buffer << USRPrefix; - auto Sig = Decl->getInnermostDeclContext()->getGenericSignatureOfContext(); - bindGenericParameters(Sig); - if (auto Ctor = dyn_cast(Decl)) { appendConstructorEntity(Ctor, /*isAllocating=*/false); } else if (auto Dtor = dyn_cast(Decl)) { @@ -818,10 +832,10 @@ std::string ASTMangler::mangleGenericSignature(const GenericSignature sig) { void ASTMangler::appendSymbolKind(SymbolKind SKind) { switch (SKind) { case SymbolKind::Default: return; - case SymbolKind::AsyncHandlerBody: return; case SymbolKind::DynamicThunk: return appendOperator("TD"); case SymbolKind::SwiftAsObjCThunk: return appendOperator("To"); case SymbolKind::ObjCAsSwiftThunk: return appendOperator("TO"); + case SymbolKind::DistributedThunk: return appendOperator("Td"); } } @@ -995,8 +1009,6 @@ void ASTMangler::appendOpaqueDeclName(const OpaqueTypeDecl *opaqueDecl) { if (canSymbolicReference(opaqueDecl)) { appendSymbolicReference(opaqueDecl); } else if (auto namingDecl = opaqueDecl->getNamingDecl()) { - llvm::SaveAndRestore savedSignature( - CurGenericSignature); appendEntity(namingDecl); appendOperator("QO"); } else { @@ -1006,7 +1018,8 @@ void ASTMangler::appendOpaqueDeclName(const OpaqueTypeDecl *opaqueDecl) { /// Mangle a type into the buffer. /// -void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { +void ASTMangler::appendType(Type type, GenericSignature sig, + const ValueDecl *forDecl) { assert((DWARFMangling || type->isCanonical()) && "expecting canonical types when not mangling for the debugger"); TypeBase *tybase = type.getPointer(); @@ -1066,7 +1079,7 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { case TypeKind::SILToken: return appendOperator("Bt"); case TypeKind::BuiltinVector: - appendType(cast(tybase)->getElementType(), + appendType(cast(tybase)->getElementType(), sig, forDecl); // The mangling calls for using the actual element count, which we have // to adjust by 1 in order to mangle it as an index. @@ -1082,26 +1095,26 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { auto underlyingType = aliasTy->getSinglyDesugaredType(); TypeAliasDecl *decl = aliasTy->getDecl(); if (decl->getModuleContext() == decl->getASTContext().TheBuiltinModule) { - return appendType(underlyingType, forDecl); + return appendType(underlyingType, sig, forDecl); } if (decl->getDeclaredInterfaceType() .subst(aliasTy->getSubstitutionMap()).getPointer() != aliasTy) { - return appendType(underlyingType, forDecl); + return appendType(underlyingType, sig, forDecl); } if (aliasTy->getSubstitutionMap()) { // Try to mangle the entire name as a substitution. - if (tryMangleTypeSubstitution(tybase)) + if (tryMangleTypeSubstitution(tybase, sig)) return; appendAnyGenericType(decl); bool isFirstArgList = true; - appendBoundGenericArgs(type, isFirstArgList); - appendRetroactiveConformances(type); + appendBoundGenericArgs(type, sig, isFirstArgList); + appendRetroactiveConformances(type, sig); appendOperator("G"); - addTypeSubstitution(type); + addTypeSubstitution(type, sig); return; } @@ -1110,32 +1123,38 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { case TypeKind::Paren: assert(DWARFMangling && "sugared types are only legal for the debugger"); - appendType(cast(tybase)->getUnderlyingType()); + appendType(cast(tybase)->getUnderlyingType(), sig, forDecl); appendOperator("XSp"); return; case TypeKind::ArraySlice: assert(DWARFMangling && "sugared types are only legal for the debugger"); - appendType(cast(tybase)->getBaseType()); + appendType(cast(tybase)->getBaseType(), sig, forDecl); + appendOperator("XSa"); + return; + + case TypeKind::VariadicSequence: + assert(DWARFMangling && "sugared types are only legal for the debugger"); + appendType(cast(tybase)->getBaseType(), sig, forDecl); appendOperator("XSa"); return; case TypeKind::Optional: assert(DWARFMangling && "sugared types are only legal for the debugger"); - appendType(cast(tybase)->getBaseType()); + appendType(cast(tybase)->getBaseType(), sig, forDecl); appendOperator("XSq"); return; case TypeKind::Dictionary: assert(DWARFMangling && "sugared types are only legal for the debugger"); - appendType(cast(tybase)->getKeyType()); - appendType(cast(tybase)->getValueType()); + appendType(cast(tybase)->getKeyType(), sig, forDecl); + appendType(cast(tybase)->getValueType(), sig, forDecl); appendOperator("XSD"); return; case TypeKind::ExistentialMetatype: { ExistentialMetatypeType *EMT = cast(tybase); - appendType(EMT->getInstanceType(), forDecl); + appendType(EMT->getInstanceType(), sig, forDecl); if (EMT->hasRepresentation()) { appendOperator("Xm", getMetatypeRepresentationOp(EMT->getRepresentation())); @@ -1146,7 +1165,7 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { } case TypeKind::Metatype: { MetatypeType *MT = cast(tybase); - appendType(MT->getInstanceType(), forDecl); + appendType(MT->getInstanceType(), sig, forDecl); if (MT->hasRepresentation()) { appendOperator("XM", getMetatypeRepresentationOp(MT->getRepresentation())); @@ -1159,17 +1178,17 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { llvm_unreachable("@lvalue types should not occur in function interfaces"); case TypeKind::InOut: - appendType(cast(tybase)->getObjectType(), forDecl); + appendType(cast(tybase)->getObjectType(), sig, forDecl); return appendOperator("z"); #define REF_STORAGE(Name, ...) \ case TypeKind::Name##Storage: \ - appendType(cast(tybase)->getReferentType(), forDecl); \ + appendType(cast(tybase)->getReferentType(), sig, forDecl); \ return appendOperator(manglingOf(ReferenceOwnership::Name)); #include "swift/AST/ReferenceStorage.def" case TypeKind::Tuple: - appendTypeList(type, forDecl); + appendTypeList(type, sig, forDecl); return appendOperator("t"); case TypeKind::Protocol: { @@ -1192,7 +1211,7 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { appendOperator("y"); if (auto superclass = layout.explicitSuperclass) { - appendType(superclass, forDecl); + appendType(superclass, sig, forDecl); return appendOperator("Xc"); } else if (layout.hasExplicitAnyObject) { return appendOperator("Xl"); @@ -1214,22 +1233,22 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { Decl = type->getAnyGeneric(); if (shouldMangleAsGeneric(type)) { // Try to mangle the entire name as a substitution. - if (tryMangleTypeSubstitution(tybase)) + if (tryMangleTypeSubstitution(tybase, sig)) return; if (Decl->isStdlibDecl() && Decl->getName().str() == "Optional") { auto GenArgs = type->castTo()->getGenericArgs(); assert(GenArgs.size() == 1); - appendType(GenArgs[0], forDecl); + appendType(GenArgs[0], sig, forDecl); appendOperator("Sg"); } else { appendAnyGenericType(Decl); bool isFirstArgList = true; - appendBoundGenericArgs(type, isFirstArgList); - appendRetroactiveConformances(type); + appendBoundGenericArgs(type, sig, isFirstArgList); + appendRetroactiveConformances(type, sig); appendOperator("G"); } - addTypeSubstitution(type); + addTypeSubstitution(type, sig); return; } appendAnyGenericType(type->getAnyGeneric()); @@ -1237,7 +1256,7 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { } case TypeKind::SILFunction: - return appendImplFunctionType(cast(tybase)); + return appendImplFunctionType(cast(tybase), sig); // type ::= archetype case TypeKind::PrimaryArchetype: @@ -1255,23 +1274,23 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { } // Otherwise, try to substitute it. - if (tryMangleTypeSubstitution(type)) + if (tryMangleTypeSubstitution(type, sig)) return; // Use the fully elaborated explicit mangling. appendOpaqueDeclName(opaqueDecl); bool isFirstArgList = true; - appendBoundGenericArgs(opaqueDecl, + appendBoundGenericArgs(opaqueDecl, sig, opaqueType->getSubstitutions(), isFirstArgList); - appendRetroactiveConformances(opaqueType->getSubstitutions(), + appendRetroactiveConformances(opaqueType->getSubstitutions(), sig, opaqueDecl->getParentModule()); // TODO: If we support multiple opaque types in a return, put the // ordinal for this archetype here. appendOperator("Qo", Index(0)); - addTypeSubstitution(type); + addTypeSubstitution(type, sig); return; } @@ -1282,18 +1301,19 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { // types, so that they can be accurately demangled at runtime. if (auto opaque = dyn_cast(nestedType->getRoot())) { - if (tryMangleTypeSubstitution(nestedType)) + if (tryMangleTypeSubstitution(nestedType, sig)) return; - appendType(opaque); + appendType(opaque, sig); bool isAssocTypeAtDepth = false; - appendAssocType(nestedType->getInterfaceType(), isAssocTypeAtDepth); + appendAssocType(nestedType->getInterfaceType(), + sig, isAssocTypeAtDepth); appendOperator(isAssocTypeAtDepth ? "QX" : "Qx"); - addTypeSubstitution(nestedType); + addTypeSubstitution(nestedType, sig); return; } - appendType(nestedType->getParent()); + appendType(nestedType->getParent(), sig); appendIdentifier(nestedType->getName().str()); appendOperator("Qa"); return; @@ -1302,15 +1322,16 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { case TypeKind::DynamicSelf: { auto dynamicSelf = cast(tybase); if (dynamicSelf->getSelfType()->getAnyNominal()) { - appendType(dynamicSelf->getSelfType(), forDecl); + appendType(dynamicSelf->getSelfType(), sig, forDecl); return appendOperator("XD"); } - return appendType(dynamicSelf->getSelfType(), forDecl); + return appendType(dynamicSelf->getSelfType(), sig, forDecl); } case TypeKind::GenericFunction: { auto genFunc = cast(tybase); - appendFunctionType(genFunc, /*autoclosure*/ false, forDecl); + appendFunctionType(genFunc, genFunc->getGenericSignature(), + /*autoclosure*/ false, forDecl); appendGenericSignature(genFunc->getGenericSignature()); appendOperator("u"); return; @@ -1333,11 +1354,11 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { case TypeKind::DependentMember: { auto *DepTy = cast(tybase); - if (tryMangleTypeSubstitution(DepTy)) + if (tryMangleTypeSubstitution(DepTy, sig)) return; bool isAssocTypeAtDepth = false; - if (GenericTypeParamType *gpBase = appendAssocType(DepTy, + if (GenericTypeParamType *gpBase = appendAssocType(DepTy, sig, isAssocTypeAtDepth)) { if (gpBase->getDepth() == 0 && gpBase->getIndex() == 0) { appendOperator(isAssocTypeAtDepth ? "QZ" : "Qz"); @@ -1348,16 +1369,17 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { } else { // Dependent members of non-generic-param types are not canonical, but // we may still want to mangle them for debugging or indexing purposes. - appendType(DepTy->getBase(), forDecl); + appendType(DepTy->getBase(), sig, forDecl); appendIdentifier(DepTy->getName().str()); appendOperator("Qa"); } - addTypeSubstitution(DepTy); + addTypeSubstitution(DepTy, sig); return; } case TypeKind::Function: - appendFunctionType(cast(tybase), /*autoclosure*/ false, + appendFunctionType(cast(tybase), sig, + /*autoclosure*/ false, forDecl); return; @@ -1366,7 +1388,7 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { auto layout = box->getLayout(); bool firstField = true; for (auto &field : layout->getFields()) { - appendType(field.getLoweredType(), forDecl); + appendType(field.getLoweredType(), sig, forDecl); if (field.isMutable()) { // Use the `inout` mangling to represent a mutable field. appendOperator("z"); @@ -1379,7 +1401,7 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { if (auto sig = layout->getGenericSignature()) { bool firstType = true; for (Type type : box->getSubstitutions().getReplacementTypes()) { - appendType(type, forDecl); + appendType(type, sig, forDecl); appendListSeparator(firstType); } if (firstType) @@ -1401,11 +1423,12 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { } GenericTypeParamType *ASTMangler::appendAssocType(DependentMemberType *DepTy, + GenericSignature sig, bool &isAssocTypeAtDepth) { auto base = DepTy->getBase()->getCanonicalType(); // 't_0_0.Member' if (auto gpBase = dyn_cast(base)) { - appendAssociatedTypeName(DepTy); + appendAssociatedTypeName(DepTy, sig); isAssocTypeAtDepth = false; return gpBase; } @@ -1420,7 +1443,7 @@ GenericTypeParamType *ASTMangler::appendAssocType(DependentMemberType *DepTy, if (auto gpRoot = dyn_cast(base)) { bool first = true; for (auto *member : llvm::reverse(path)) { - appendAssociatedTypeName(member); + appendAssociatedTypeName(member, sig); appendListSeparator(first); } isAssocTypeAtDepth = true; @@ -1445,24 +1468,19 @@ void ASTMangler::appendOpWithGenericParamIndex(StringRef Op, appendOperator(Op, Index(paramTy->getIndex() - 1)); } - -/// Bind the generic parameters from the given signature. -void ASTMangler::bindGenericParameters(GenericSignature sig) { - if (sig) - CurGenericSignature = sig.getCanonicalSignature(); -} - -void ASTMangler::appendFlatGenericArgs(SubstitutionMap subs) { +void ASTMangler::appendFlatGenericArgs(SubstitutionMap subs, + GenericSignature sig) { appendOperator("y"); for (auto replacement : subs.getReplacementTypes()) { if (replacement->hasArchetype()) replacement = replacement->mapTypeOutOfContext(); - appendType(replacement); + appendType(replacement, sig); } } unsigned ASTMangler::appendBoundGenericArgs(DeclContext *dc, + GenericSignature sig, SubstitutionMap subs, bool &isFirstArgList) { auto decl = dc->getInnermostDeclarationDeclContext(); @@ -1478,7 +1496,7 @@ unsigned ASTMangler::appendBoundGenericArgs(DeclContext *dc, // Handle the generic arguments of the parent. unsigned currentGenericParamIdx = - appendBoundGenericArgs(decl->getDeclContext(), subs, isFirstArgList); + appendBoundGenericArgs(decl->getDeclContext(), sig, subs, isFirstArgList); // If this is potentially a generic context, emit a generic argument list. if (auto genericContext = decl->getAsGenericContext()) { @@ -1492,7 +1510,7 @@ unsigned ASTMangler::appendBoundGenericArgs(DeclContext *dc, // If we are generic at this level, emit all of the replacements at // this level. if (genericContext->isGeneric()) { - auto genericParams = subs.getGenericSignature()->getGenericParams(); + auto genericParams = subs.getGenericSignature().getGenericParams(); unsigned depth = genericParams[currentGenericParamIdx]->getDepth(); auto replacements = subs.getReplacementTypes(); for (unsigned lastGenericParamIdx = genericParams.size(); @@ -1503,7 +1521,7 @@ unsigned ASTMangler::appendBoundGenericArgs(DeclContext *dc, if (replacementType->hasArchetype()) replacementType = replacementType->mapTypeOutOfContext(); - appendType(replacementType); + appendType(replacementType, sig); } } } @@ -1511,11 +1529,12 @@ unsigned ASTMangler::appendBoundGenericArgs(DeclContext *dc, return currentGenericParamIdx; } -void ASTMangler::appendBoundGenericArgs(Type type, bool &isFirstArgList) { +void ASTMangler::appendBoundGenericArgs(Type type, GenericSignature sig, + bool &isFirstArgList) { TypeBase *typePtr = type.getPointer(); ArrayRef genericArgs; if (auto *typeAlias = dyn_cast(typePtr)) { - appendBoundGenericArgs(typeAlias->getDecl(), + appendBoundGenericArgs(typeAlias->getDecl(), sig, typeAlias->getSubstitutionMap(), isFirstArgList); return; @@ -1523,17 +1542,17 @@ void ASTMangler::appendBoundGenericArgs(Type type, bool &isFirstArgList) { if (auto *unboundType = dyn_cast(typePtr)) { if (Type parent = unboundType->getParent()) - appendBoundGenericArgs(parent->getDesugaredType(), isFirstArgList); + appendBoundGenericArgs(parent->getDesugaredType(), sig, isFirstArgList); } else if (auto *nominalType = dyn_cast(typePtr)) { if (Type parent = nominalType->getParent()) - appendBoundGenericArgs(parent->getDesugaredType(), isFirstArgList); + appendBoundGenericArgs(parent->getDesugaredType(), sig, isFirstArgList); } else { auto boundType = cast(typePtr); genericArgs = boundType->getGenericArgs(); if (Type parent = boundType->getParent()) { GenericTypeDecl *decl = boundType->getAnyGeneric(); if (!getSpecialManglingContext(decl, UseObjCRuntimeNames)) - appendBoundGenericArgs(parent->getDesugaredType(), isFirstArgList); + appendBoundGenericArgs(parent->getDesugaredType(), sig, isFirstArgList); } } if (isFirstArgList) { @@ -1543,14 +1562,15 @@ void ASTMangler::appendBoundGenericArgs(Type type, bool &isFirstArgList) { appendOperator("_"); } for (Type arg : genericArgs) { - appendType(arg); + appendType(arg, sig); } } static bool conformanceHasIdentity(const RootProtocolConformance *root) { auto conformance = dyn_cast(root); if (!conformance) { - assert(isa(root)); + assert(isa(root) || + isa(root)); return true; } @@ -1571,8 +1591,9 @@ static bool conformanceHasIdentity(const RootProtocolConformance *root) { static bool isRetroactiveConformance(const RootProtocolConformance *root) { auto conformance = dyn_cast(root); if (!conformance) { - assert(isa(root)); - return false; // self-conformances are never retroactive. + assert(isa(root) || + isa(root)); + return false; // self-conformances are never retroactive. nor are builtin. } return conformance->isRetroactive(); @@ -1614,6 +1635,7 @@ static bool containsRetroactiveConformance( } void ASTMangler::appendRetroactiveConformances(SubstitutionMap subMap, + GenericSignature sig, ModuleDecl *fromModule) { if (subMap.empty()) return; @@ -1631,12 +1653,12 @@ void ASTMangler::appendRetroactiveConformances(SubstitutionMap subMap, if (!containsRetroactiveConformance(conformance.getConcrete(), fromModule)) continue; - appendConcreteProtocolConformance(conformance.getConcrete()); + appendConcreteProtocolConformance(conformance.getConcrete(), sig); appendOperator("g", Index(numProtocolRequirements)); } } -void ASTMangler::appendRetroactiveConformances(Type type) { +void ASTMangler::appendRetroactiveConformances(Type type, GenericSignature sig) { // Dig out the substitution map to use. SubstitutionMap subMap; ModuleDecl *module; @@ -1654,7 +1676,7 @@ void ASTMangler::appendRetroactiveConformances(Type type) { subMap = type->getContextSubstitutionMap(module, nominal); } - appendRetroactiveConformances(subMap, module); + appendRetroactiveConformances(subMap, sig, module); } static char getParamConvention(ParameterConvention conv) { @@ -1707,7 +1729,8 @@ getResultDifferentiability(SILResultDifferentiability diffKind) { llvm_unreachable("bad result differentiability"); }; -void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { +void ASTMangler::appendImplFunctionType(SILFunctionType *fn, + GenericSignature outerGenericSig) { llvm::SmallVector OpArgs; @@ -1803,15 +1826,14 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { OpArgs.push_back('H'); } - auto outerGenericSig = CurGenericSignature; - CurGenericSignature = fn->getSubstGenericSignature(); + GenericSignature sig = fn->getSubstGenericSignature(); // Mangle the parameters. for (auto param : fn->getParameters()) { OpArgs.push_back(getParamConvention(param.getConvention())); if (auto diffKind = getParamDifferentiability(param.getDifferentiability())) OpArgs.push_back(*diffKind); - appendType(param.getInterfaceType()); + appendType(param.getInterfaceType(), sig); } // Mangle the results. @@ -1820,14 +1842,14 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { if (auto diffKind = getResultDifferentiability(result.getDifferentiability())) OpArgs.push_back(*diffKind); - appendType(result.getInterfaceType()); + appendType(result.getInterfaceType(), sig); } // Mangle the yields. for (auto yield : fn->getYields()) { OpArgs.push_back('Y'); OpArgs.push_back(getParamConvention(yield.getConvention())); - appendType(yield.getInterfaceType()); + appendType(yield.getInterfaceType(), sig); } // Mangle the error result if present. @@ -1835,26 +1857,25 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { auto error = fn->getErrorResult(); OpArgs.push_back('z'); OpArgs.push_back(getResultConvention(error.getConvention())); - appendType(error.getInterfaceType()); + appendType(error.getInterfaceType(), sig); } - if (auto sig = fn->getInvocationGenericSignature()) { - appendGenericSignature(sig); - CurGenericSignature = outerGenericSig; + if (auto invocationSig = fn->getInvocationGenericSignature()) { + appendGenericSignature(invocationSig); + sig = outerGenericSig; } if (auto subs = fn->getInvocationSubstitutions()) { - appendFlatGenericArgs(subs); - appendRetroactiveConformances(subs, Mod); + appendFlatGenericArgs(subs, sig); + appendRetroactiveConformances(subs, sig, Mod); } if (auto subs = fn->getPatternSubstitutions()) { appendGenericSignature(subs.getGenericSignature()); - CurGenericSignature = + sig = fn->getInvocationGenericSignature() ? fn->getInvocationGenericSignature() : outerGenericSig; - appendFlatGenericArgs(subs); - appendRetroactiveConformances(subs, Mod); - CurGenericSignature = outerGenericSig; + appendFlatGenericArgs(subs, sig); + appendRetroactiveConformances(subs, sig, Mod); } OpArgs.push_back('_'); @@ -2124,10 +2145,10 @@ void ASTMangler::appendContext(const DeclContext *ctx, StringRef useModuleName) auto wrapperInit = cast(ctx); switch (wrapperInit->getKind()) { case PropertyWrapperInitializer::Kind::WrappedValue: - appendBackingInitializerEntity(wrapperInit->getParam()); + appendBackingInitializerEntity(wrapperInit->getWrappedVar()); break; case PropertyWrapperInitializer::Kind::ProjectedValue: - appendInitFromProjectedValueEntity(wrapperInit->getParam()); + appendInitFromProjectedValueEntity(wrapperInit->getWrappedVar()); break; } return; @@ -2245,7 +2266,7 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) { // For generic types, this uses the unbound type. if (nominal) { - if (tryMangleTypeSubstitution(nominal->getDeclaredType())) + if (tryMangleTypeSubstitution(nominal->getDeclaredType(), nullptr)) return; } else { if (tryMangleSubstitution(cast(decl))) @@ -2256,7 +2277,7 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) { if (nominal && canSymbolicReference(nominal)) { appendSymbolicReference(nominal); // Substitutions can refer back to the symbolic reference. - addTypeSubstitution(nominal->getDeclaredType()); + addTypeSubstitution(nominal->getDeclaredType(), nullptr); return; } @@ -2341,12 +2362,12 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) { } if (nominal) - addTypeSubstitution(nominal->getDeclaredType()); + addTypeSubstitution(nominal->getDeclaredType(), nullptr); else addSubstitution(cast(decl)); } -void ASTMangler::appendFunction(AnyFunctionType *fn, +void ASTMangler::appendFunction(AnyFunctionType *fn, GenericSignature sig, FunctionManglingKind functionMangling, const ValueDecl *forDecl) { // Append parameter labels right before the signature/type. @@ -2368,18 +2389,19 @@ void ASTMangler::appendFunction(AnyFunctionType *fn, } if (functionMangling != NoFunctionMangling) { - appendFunctionSignature(fn, forDecl, functionMangling); + appendFunctionSignature(fn, sig, forDecl, functionMangling); } else { - appendFunctionType(fn, /*autoclosure*/ false, forDecl); + appendFunctionType(fn, sig, /*autoclosure*/ false, forDecl); } } -void ASTMangler::appendFunctionType(AnyFunctionType *fn, bool isAutoClosure, +void ASTMangler::appendFunctionType(AnyFunctionType *fn, GenericSignature sig, + bool isAutoClosure, const ValueDecl *forDecl) { assert((DWARFMangling || fn->isCanonical()) && "expecting canonical types when not mangling for the debugger"); - appendFunctionSignature(fn, forDecl, NoFunctionMangling); + appendFunctionSignature(fn, sig, forDecl, NoFunctionMangling); bool mangleClangType = fn->getASTContext().LangOpts.UseClangFunctionTypes && fn->hasNonDerivableClangType(); @@ -2445,11 +2467,12 @@ void ASTMangler::appendClangType(AnyFunctionType *fn) { } void ASTMangler::appendFunctionSignature(AnyFunctionType *fn, - const ValueDecl *forDecl, - FunctionManglingKind functionMangling) { - appendFunctionResultType(fn->getResult(), forDecl); - appendFunctionInputType(fn->getParams(), forDecl); - if (fn->isAsync() || functionMangling == AsyncHandlerBodyMangling) + GenericSignature sig, + const ValueDecl *forDecl, + FunctionManglingKind functionMangling) { + appendFunctionResultType(fn->getResult(), sig, forDecl); + appendFunctionInputType(fn->getParams(), sig, forDecl); + if (fn->isAsync()) appendOperator("Ya"); if (fn->isSendable()) appendOperator("Yb"); @@ -2471,10 +2494,16 @@ void ASTMangler::appendFunctionSignature(AnyFunctionType *fn, appendOperator("Yjl"); break; } + + if (Type globalActor = fn->getGlobalActor()) { + appendType(globalActor, sig); + appendOperator("Yc"); + } } void ASTMangler::appendFunctionInputType( ArrayRef params, + GenericSignature sig, const ValueDecl *forDecl) { switch (params.size()) { case 0: @@ -2490,7 +2519,7 @@ void ASTMangler::appendFunctionInputType( if (!param.hasLabel() && !param.isVariadic() && !isa(type.getPointer())) { appendTypeListElement(Identifier(), type, param.getParameterFlags(), - forDecl); + sig, forDecl); break; } @@ -2503,7 +2532,7 @@ void ASTMangler::appendFunctionInputType( bool isFirstParam = true; for (auto ¶m : params) { appendTypeListElement(Identifier(), param.getPlainType(), - param.getParameterFlags(), forDecl); + param.getParameterFlags(), sig, forDecl); appendListSeparator(isFirstParam); } appendOperator("t"); @@ -2511,13 +2540,14 @@ void ASTMangler::appendFunctionInputType( } } -void ASTMangler::appendFunctionResultType(Type resultType, +void ASTMangler::appendFunctionResultType(Type resultType, GenericSignature sig, const ValueDecl *forDecl) { return resultType->isVoid() ? appendOperator("y") - : appendType(resultType, forDecl); + : appendType(resultType, sig, forDecl); } -void ASTMangler::appendTypeList(Type listTy, const ValueDecl *forDecl) { +void ASTMangler::appendTypeList(Type listTy, GenericSignature sig, + const ValueDecl *forDecl) { if (TupleType *tuple = listTy->getAs()) { if (tuple->getNumElements() == 0) return appendOperator("y"); @@ -2526,22 +2556,23 @@ void ASTMangler::appendTypeList(Type listTy, const ValueDecl *forDecl) { assert(field.getParameterFlags().isNone()); appendTypeListElement(field.getName(), field.getRawType(), ParameterTypeFlags(), - forDecl); + sig, forDecl); appendListSeparator(firstField); } } else { - appendType(listTy, forDecl); + appendType(listTy, sig, forDecl); appendListSeparator(); } } void ASTMangler::appendTypeListElement(Identifier name, Type elementType, ParameterTypeFlags flags, + GenericSignature sig, const ValueDecl *forDecl) { if (auto *fnType = elementType->getAs()) - appendFunctionType(fnType, flags.isAutoClosure(), forDecl); + appendFunctionType(fnType, sig, flags.isAutoClosure(), forDecl); else - appendType(elementType, forDecl); + appendType(elementType, sig, forDecl); if (flags.isNoDerivative()) { appendOperator("Yk"); @@ -2560,6 +2591,9 @@ void ASTMangler::appendTypeListElement(Identifier name, Type elementType, appendOperator("n"); break; } + if (flags.isIsolated()) + appendOperator("Yi"); + if (!name.empty()) appendIdentifier(name.str()); if (flags.isVariadic()) @@ -2569,10 +2603,9 @@ void ASTMangler::appendTypeListElement(Identifier name, Type elementType, bool ASTMangler::appendGenericSignature(GenericSignature sig, GenericSignature contextSig) { auto canSig = sig.getCanonicalSignature(); - CurGenericSignature = canSig; unsigned initialParamDepth; - TypeArrayView genericParams; + ArrayRef> genericParams; ArrayRef requirements; SmallVector requirementsBuffer; if (contextSig) { @@ -2583,12 +2616,12 @@ bool ASTMangler::appendGenericSignature(GenericSignature sig, } // The signature depth starts above the depth of the context signature. - if (!contextSig->getGenericParams().empty()) { - initialParamDepth = contextSig->getGenericParams().back()->getDepth() + 1; + if (!contextSig.getGenericParams().empty()) { + initialParamDepth = contextSig.getGenericParams().back()->getDepth() + 1; } // Find the parameters at this depth (or greater). - genericParams = canSig->getGenericParams(); + genericParams = canSig.getGenericParams(); unsigned firstParam = genericParams.size(); while (firstParam > 1 && genericParams[firstParam-1]->getDepth() >= initialParamDepth) @@ -2600,11 +2633,11 @@ bool ASTMangler::appendGenericSignature(GenericSignature sig, // it's better to mangle the complete canonical signature because we // have a special-case mangling for that. if (genericParams.empty() && - contextSig->getGenericParams().size() == 1 && - contextSig->getRequirements().empty()) { + contextSig.getGenericParams().size() == 1 && + contextSig.getRequirements().empty()) { initialParamDepth = 0; - genericParams = canSig->getGenericParams(); - requirements = canSig->getRequirements(); + genericParams = canSig.getGenericParams(); + requirements = canSig.getRequirements(); } else { requirementsBuffer = canSig->requirementsNotSatisfiedBy(contextSig); requirements = requirementsBuffer; @@ -2612,18 +2645,20 @@ bool ASTMangler::appendGenericSignature(GenericSignature sig, } else { // Use the complete canonical signature. initialParamDepth = 0; - genericParams = canSig->getGenericParams(); - requirements = canSig->getRequirements(); + genericParams = canSig.getGenericParams(); + requirements = canSig.getRequirements(); } if (genericParams.empty() && requirements.empty()) return false; - appendGenericSignatureParts(genericParams, initialParamDepth, requirements); + appendGenericSignatureParts(sig, genericParams, + initialParamDepth, requirements); return true; } -void ASTMangler::appendRequirement(const Requirement &reqt) { +void ASTMangler::appendRequirement(const Requirement &reqt, + GenericSignature sig) { Type FirstTy = reqt.getFirstType()->getCanonicalType(); @@ -2636,13 +2671,13 @@ void ASTMangler::appendRequirement(const Requirement &reqt) { case RequirementKind::Superclass: case RequirementKind::SameType: { Type SecondTy = reqt.getSecondType(); - appendType(SecondTy->getCanonicalType()); + appendType(SecondTy->getCanonicalType(), sig); } break; } if (auto *DT = FirstTy->getAs()) { bool isAssocTypeAtDepth = false; - if (tryMangleTypeSubstitution(DT)) { + if (tryMangleTypeSubstitution(DT, sig)) { switch (reqt.getKind()) { case RequirementKind::Conformance: return appendOperator("RQ"); @@ -2657,8 +2692,9 @@ void ASTMangler::appendRequirement(const Requirement &reqt) { } llvm_unreachable("bad requirement type"); } - GenericTypeParamType *gpBase = appendAssocType(DT, isAssocTypeAtDepth); - addTypeSubstitution(DT); + GenericTypeParamType *gpBase = appendAssocType(DT, sig, + isAssocTypeAtDepth); + addTypeSubstitution(DT, sig); assert(gpBase); switch (reqt.getKind()) { case RequirementKind::Conformance: @@ -2694,12 +2730,13 @@ void ASTMangler::appendRequirement(const Requirement &reqt) { } void ASTMangler::appendGenericSignatureParts( - TypeArrayView params, + GenericSignature sig, + ArrayRef> params, unsigned initialParamDepth, ArrayRef requirements) { // Mangle the requirements. for (const Requirement &reqt : requirements) { - appendRequirement(reqt); + appendRequirement(reqt, sig); } if (params.size() == 1 && params[0]->getDepth() == initialParamDepth) @@ -2748,14 +2785,15 @@ void ASTMangler::appendGenericSignatureParts( // in the current generic context, then we don't need to disambiguate the // associated type name by protocol. DependentMemberType * -ASTMangler::dropProtocolFromAssociatedType(DependentMemberType *dmt) { +ASTMangler::dropProtocolFromAssociatedType(DependentMemberType *dmt, + GenericSignature sig) { auto baseTy = dmt->getBase(); bool unambiguous = (!dmt->getAssocType() || - CurGenericSignature->getRequiredProtocols(baseTy).size() <= 1); + sig->getRequiredProtocols(baseTy).size() <= 1); if (auto *baseDMT = baseTy->getAs()) - baseTy = dropProtocolFromAssociatedType(baseDMT); + baseTy = dropProtocolFromAssociatedType(baseDMT, sig); if (unambiguous) return DependentMemberType::get(baseTy, dmt->getName()); @@ -2764,8 +2802,9 @@ ASTMangler::dropProtocolFromAssociatedType(DependentMemberType *dmt) { } Type -ASTMangler::dropProtocolsFromAssociatedTypes(Type type) { - if (!OptimizeProtocolNames || !CurGenericSignature) +ASTMangler::dropProtocolsFromAssociatedTypes(Type type, + GenericSignature sig) { + if (!OptimizeProtocolNames || !sig) return type; if (!type->hasDependentMember()) @@ -2773,20 +2812,21 @@ ASTMangler::dropProtocolsFromAssociatedTypes(Type type) { return type.transform([&](Type t) -> Type { if (auto *dmt = dyn_cast(t.getPointer())) - return dropProtocolFromAssociatedType(dmt); + return dropProtocolFromAssociatedType(dmt, sig); return t; }); } -void ASTMangler::appendAssociatedTypeName(DependentMemberType *dmt) { +void ASTMangler::appendAssociatedTypeName(DependentMemberType *dmt, + GenericSignature sig) { if (auto assocTy = dmt->getAssocType()) { appendIdentifier(assocTy->getName().str()); // If the base type is known to have a single protocol conformance // in the current generic context, then we don't need to disambiguate the // associated type name by protocol. - if (!OptimizeProtocolNames || !CurGenericSignature || - CurGenericSignature->getRequiredProtocols(dmt->getBase()).size() > 1) { + if (!OptimizeProtocolNames || !sig || + sig->getRequiredProtocols(dmt->getBase()).size() > 1) { appendAnyGenericType(assocTy->getProtocol()); } return; @@ -2817,8 +2857,9 @@ void ASTMangler::appendClosureComponents(Type Ty, unsigned discriminator, if (!Ty) Ty = ErrorType::get(parentContext->getASTContext()); + auto Sig = parentContext->getGenericSignatureOfContext(); Ty = Ty->mapTypeOutOfContext(); - appendType(Ty->getCanonicalType()); + appendType(Ty->getCanonicalType(), Sig); appendOperator(isImplicit ? "fu" : "fU", Index(discriminator)); } @@ -2870,23 +2911,10 @@ CanType ASTMangler::getDeclTypeForMangling( Type ty = decl->getInterfaceType()->getReferenceStorageReferent(); - // Strip the global actor out of the mangling. - ty = ty.transform([](Type type) { - if (auto fnType = type->getAs()) { - if (fnType->getGlobalActor()) { - return Type(fnType->withExtInfo( - fnType->getExtInfo().withGlobalActor(Type()))); - } - } - - return type; - }); - auto canTy = ty->getCanonicalType(); if (auto gft = dyn_cast(canTy)) { genericSig = gft.getGenericSignature(); - CurGenericSignature = gft.getGenericSignature(); canTy = CanFunctionType::get(gft.getParams(), gft.getResult(), gft->getExtInfo()); @@ -2913,10 +2941,14 @@ void ASTMangler::appendDeclType(const ValueDecl *decl, GenericSignature parentGenericSig; auto type = getDeclTypeForMangling(decl, genericSig, parentGenericSig); + auto sig = (genericSig + ? genericSig + : decl->getDeclContext()->getGenericSignatureOfContext()); + if (AnyFunctionType *FuncTy = type->getAs()) { - appendFunction(FuncTy, functionMangling, decl); + appendFunction(FuncTy, sig, functionMangling, decl); } else { - appendType(type, decl); + appendType(type, sig, decl); } // Mangle the generic signature, if any. @@ -2929,13 +2961,15 @@ void ASTMangler::appendDeclType(const ValueDecl *decl, bool ASTMangler::tryAppendStandardSubstitution(const GenericTypeDecl *decl) { // Bail out if our parent isn't the swift standard library. - if (!decl->isStdlibDecl()) + auto dc = decl->getDeclContext(); + if (!dc->isModuleScopeContext() || + !dc->getParentModule()->hasStandardSubstitutions()) return false; if (isa(decl)) { - if (char Subst = getStandardTypeSubst(decl->getName().str())) { - if (!SubstMerging.tryMergeSubst(*this, Subst, /*isStandardSubst*/ true)) { - appendOperator("S", StringRef(&Subst, 1)); + if (auto Subst = getStandardTypeSubst(decl->getName().str())) { + if (!SubstMerging.tryMergeSubst(*this, *Subst, /*isStandardSubst*/ true)){ + appendOperator("S", *Subst); } return true; } @@ -2966,12 +3000,10 @@ void ASTMangler::appendAccessorEntity(StringRef accessorKindCode, bool isStatic) { appendContextOf(decl); if (auto *varDecl = dyn_cast(decl)) { - bindGenericParameters(varDecl->getDeclContext()->getGenericSignatureOfContext()); appendDeclName(decl); appendDeclType(decl); appendOperator("v", accessorKindCode); } else if (auto *subscriptDecl = dyn_cast(decl)) { - bindGenericParameters(subscriptDecl->getGenericSignature()); appendDeclType(decl); StringRef privateDiscriminator = getPrivateDiscriminatorIfNecessary(decl); @@ -2998,7 +3030,7 @@ void ASTMangler::appendEntity(const ValueDecl *decl, StringRef EntityOp, appendOperator("Z"); } -void ASTMangler::appendEntity(const ValueDecl *decl, bool isAsyncHandlerBody) { +void ASTMangler::appendEntity(const ValueDecl *decl) { assert(!isa(decl)); assert(!isa(decl)); @@ -3019,8 +3051,7 @@ void ASTMangler::appendEntity(const ValueDecl *decl, bool isAsyncHandlerBody) { appendContextOf(decl); appendDeclName(decl); - appendDeclType(decl, isAsyncHandlerBody ? AsyncHandlerBodyMangling - : FunctionMangling); + appendDeclType(decl, FunctionMangling); appendOperator("F"); if (decl->isStatic()) appendOperator("Z"); @@ -3028,13 +3059,12 @@ void ASTMangler::appendEntity(const ValueDecl *decl, bool isAsyncHandlerBody) { void ASTMangler::appendProtocolConformance(const ProtocolConformance *conformance) { - GenericSignature contextSig; auto topLevelSubcontext = conformance->getDeclContext()->getModuleScopeContext(); Mod = topLevelSubcontext->getParentModule(); auto conformingType = conformance->getType(); - appendType(conformingType->getCanonicalType()); + appendType(conformingType->getCanonicalType(), nullptr); appendProtocolName(conformance->getProtocol()); @@ -3055,7 +3085,11 @@ ASTMangler::appendProtocolConformance(const ProtocolConformance *conformance) { appendModule(Mod, DC->getAsDecl()->getAlternateModuleName()); } - contextSig = + // If this is a non-nominal type, we're done. + if (!conformingType->getAnyNominal()) + return; + + auto contextSig = conformingType->getAnyNominal()->getGenericSignatureOfContext(); if (GenericSignature Sig = conformance->getGenericSignature()) { @@ -3079,6 +3113,9 @@ void ASTMangler::appendProtocolConformanceRef( assert(DC->getAsDecl()); appendModule(conformance->getDeclContext()->getParentModule(), DC->getAsDecl()->getAlternateModuleName()); + // Builtin conformances are always from the Swift module. + } else if (isa(conformance)) { + appendOperator("HP"); } else if (conformance->getDeclContext()->getParentModule() == conformance->getType()->getAnyNominal()->getParentModule()) { appendOperator("HP"); @@ -3108,23 +3145,25 @@ static unsigned conformanceRequirementIndex( } void ASTMangler::appendDependentProtocolConformance( - const ConformanceAccessPath &path) { + const ConformanceAccessPath &path, + GenericSignature sig) { ProtocolDecl *currentProtocol = nullptr; for (const auto &entry : path) { // After each step, update the current protocol to refer to where we // are. SWIFT_DEFER { currentProtocol = entry.second; + sig = currentProtocol->getGenericSignature(); }; // The first entry is the "root". Find this requirement in the generic // signature. if (!currentProtocol) { - appendType(entry.first); + appendType(entry.first, sig); appendProtocolName(entry.second); auto index = conformanceRequirementIndex(entry, - CurGenericSignature->getRequirements()); + sig.getRequirements()); // This is never an unknown index and so must be adjusted by 2 per ABI. appendOperator("HD", Index(index + 2)); continue; @@ -3148,7 +3187,7 @@ void ASTMangler::appendDependentProtocolConformance( // Associated conformance. // FIXME: Symbolic reference. - appendType(entry.first); + appendType(entry.first, sig); appendProtocolName(entry.second); // For resilient protocols, the index is unknown, so we use the special @@ -3160,54 +3199,40 @@ void ASTMangler::appendDependentProtocolConformance( } void ASTMangler::appendAnyProtocolConformance( - CanGenericSignature genericSig, + GenericSignature genericSig, CanType conformingType, ProtocolConformanceRef conformance) { if (conformingType->isTypeParameter()) { assert(genericSig && "Need a generic signature to resolve conformance"); auto path = genericSig->getConformanceAccessPath(conformingType, conformance.getAbstract()); - appendDependentProtocolConformance(path); + appendDependentProtocolConformance(path, genericSig); } else if (auto opaqueType = conformingType->getAs()) { GenericSignature opaqueSignature = opaqueType->getBoundSignature(); - GenericTypeParamType *opaqueTypeParam = opaqueSignature->getGenericParams().back(); + GenericTypeParamType *opaqueTypeParam = opaqueSignature.getGenericParams().back(); ConformanceAccessPath conformanceAccessPath = opaqueSignature->getConformanceAccessPath(opaqueTypeParam, conformance.getAbstract()); // Append the conformance access path with the signature of the opaque type. - { - llvm::SaveAndRestore savedSignature( - CurGenericSignature, opaqueSignature.getCanonicalSignature()); - appendDependentProtocolConformance(conformanceAccessPath); - } - appendType(conformingType); + appendDependentProtocolConformance(conformanceAccessPath, opaqueSignature); + appendType(conformingType, genericSig); appendOperator("HO"); } else { - appendConcreteProtocolConformance(conformance.getConcrete()); + appendConcreteProtocolConformance(conformance.getConcrete(), genericSig); } } void ASTMangler::appendConcreteProtocolConformance( - const ProtocolConformance *conformance) { + const ProtocolConformance *conformance, + GenericSignature sig) { auto module = conformance->getDeclContext()->getParentModule(); - // It's possible that we might not have a generic signature here to get - // the conformance access path (for example, when mangling types for - // debugger). In that case, we can use the generic signature of the - // conformance (if it's present). - auto conformanceSig = conformance->getGenericSignature(); - auto shouldUseConformanceSig = !CurGenericSignature && conformanceSig; - llvm::SaveAndRestore savedSignature( - CurGenericSignature, shouldUseConformanceSig - ? conformanceSig.getCanonicalSignature() - : CurGenericSignature); - // Conforming type. Type conformingType = conformance->getType(); if (conformingType->hasArchetype()) conformingType = conformingType->mapTypeOutOfContext(); - appendType(conformingType->getCanonicalType()); + appendType(conformingType->getCanonicalType(), sig); // Protocol conformance reference. appendProtocolConformanceRef(conformance->getRootConformance()); @@ -3225,7 +3250,7 @@ void ASTMangler::appendConcreteProtocolConformance( auto type = conditionalReq.getFirstType(); if (type->hasArchetype()) type = type->mapTypeOutOfContext(); - CanType canType = type->getCanonicalType(CurGenericSignature); + CanType canType = type->getCanonicalType(sig); auto proto = conditionalReq.getProtocolDecl(); ProtocolConformanceRef conformance; @@ -3235,7 +3260,7 @@ void ASTMangler::appendConcreteProtocolConformance( } else { conformance = module->lookupConformance(canType, proto); } - appendAnyProtocolConformance(CurGenericSignature, canType, conformance); + appendAnyProtocolConformance(sig, canType, conformance); appendListSeparator(firstRequirement); break; } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 44f3339e4e59e..2d2b3d49cb718 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -26,6 +26,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Expr.h" #include "swift/AST/FileUnit.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" @@ -121,6 +122,8 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint, result.PrintIfConfig = false; result.CurrentModule = ModuleToPrint; result.FullyQualifiedTypes = true; + result.FullyQualifiedTypesIfAmbiguous = true; + result.FullyQualifiedExtendedTypesIfAmbiguous = true; result.UseExportedModuleNames = true; result.AllowNullTypes = false; result.SkipImports = true; @@ -473,6 +476,7 @@ static bool escapeKeywordInContext(StringRef keyword, PrintNameContext context){ case PrintNameContext::Attribute: return isKeyword; case PrintNameContext::Keyword: + case PrintNameContext::IntroducerKeyword: return false; case PrintNameContext::ClassDynamicSelf: @@ -874,7 +878,7 @@ class PrintAST : public ASTVisitor { bool shouldPrintPattern(const Pattern *P); void printPatternType(const Pattern *P); void printAccessors(const AbstractStorageDecl *ASD); - void printMutabilityModifiersIfNeeded(const FuncDecl *FD); + void printSelfAccessKindModifiersIfNeeded(const FuncDecl *FD); void printMembersOfDecl(Decl * NTD, bool needComma = false, bool openBracket = true, bool closeBracket = true); void printMembers(ArrayRef members, bool needComma = false, @@ -911,6 +915,7 @@ class PrintAST : public ASTVisitor { void printSynthesizedExtension(Type ExtendedType, ExtensionDecl *ExtDecl); void printExtension(ExtensionDecl* ExtDecl); + void printExtendedTypeName(TypeLoc ExtendedTypeLoc); public: PrintAST(ASTPrinter &Printer, const PrintOptions &Options) @@ -995,18 +1000,6 @@ class PrintAST : public ASTVisitor { ASTVisitor::visit(D); if (haveFeatureChecks) { - // If we guarded a marker protocol, print an alternative typealias - // for Any. - if (auto proto = dyn_cast(D)) { - if (proto->isMarkerProtocol()) { - Printer.printNewline(); - Printer << "#else"; - Printer.printNewline(); - printAccess(proto); - Printer << "typealias " << proto->getName() << " = Any"; - } - } - printCompatibilityFeatureChecksPost(Printer); } @@ -1151,6 +1144,7 @@ void PrintAST::printAttributes(const Decl *D) { if (isa(D)) { Options.ExcludeAttrList.push_back(DAK_Mutating); Options.ExcludeAttrList.push_back(DAK_NonMutating); + Options.ExcludeAttrList.push_back(DAK_Consuming); } D->getAttrs().print(Printer, Options, D); @@ -1299,10 +1293,9 @@ void PrintAST::printPattern(const Pattern *pattern) { break; case PatternKind::Binding: - if (!Options.SkipIntroducerKeywords) - Printer << (cast(pattern)->isLet() ? tok::kw_let - : tok::kw_var) - << " "; + Printer.printIntroducerKeyword( + cast(pattern)->isLet() ? "let" : "var", + Options, " "); printPattern(cast(pattern)->getSubPattern()); } } @@ -1514,7 +1507,7 @@ static unsigned getDepthOfRequirement(const Requirement &req) { static void getRequirementsAtDepth(GenericSignature genericSig, unsigned depth, SmallVectorImpl &result) { - for (auto reqt : genericSig->getRequirements()) { + for (auto reqt : genericSig.getRequirements()) { unsigned currentDepth = getDepthOfRequirement(reqt); assert(currentDepth != ErrorDepth); if (currentDepth == depth) @@ -1532,17 +1525,17 @@ void PrintAST::printGenericSignature(GenericSignature genericSig, void PrintAST::printGenericSignature( GenericSignature genericSig, unsigned flags, llvm::function_ref filter) { - auto requirements = genericSig->getRequirements(); + auto requirements = genericSig.getRequirements(); if (flags & InnermostOnly) { - auto genericParams = genericSig->getInnermostGenericParams(); + auto genericParams = genericSig.getInnermostGenericParams(); printSingleDepthOfGenericSignature(genericParams, requirements, flags, filter); return; } - auto genericParams = genericSig->getGenericParams(); + auto genericParams = genericSig.getGenericParams(); if (!Options.PrintInSILBody) { printSingleDepthOfGenericSignature(genericParams, requirements, flags, @@ -1595,17 +1588,34 @@ void PrintAST::printSingleDepthOfGenericSignature( bool swapSelfAndDependentMemberType = (flags & SwapSelfAndDependentMemberType); + unsigned typeContextDepth = 0; SubstitutionMap subMap; + ModuleDecl *M = nullptr; if (CurrentType && Current) { if (!CurrentType->isExistentialType()) { auto *DC = Current->getInnermostDeclContext()->getInnermostTypeContext(); - auto *M = DC->getParentModule(); + M = DC->getParentModule(); subMap = CurrentType->getContextSubstitutionMap(M, DC); + if (!subMap.empty()) { + typeContextDepth = subMap.getGenericSignature() + .getGenericParams().back()->getDepth() + 1; + } } } auto substParam = [&](Type param) -> Type { - return param.subst(subMap); + if (subMap.empty()) + return param; + + return param.subst( + [&](SubstitutableType *type) -> Type { + if (cast(type)->getDepth() < typeContextDepth) + return Type(type).subst(subMap); + return type; + }, + [&](CanType depType, Type substType, ProtocolDecl *proto) { + return M->lookupConformance(substType, proto); + }); }; if (printParams) { @@ -1615,10 +1625,7 @@ void PrintAST::printSingleDepthOfGenericSignature( genericParams, [&](GenericTypeParamType *param) { if (!subMap.empty()) { - if (auto argTy = substParam(param)) - printType(argTy); - else - printType(param); + printType(substParam(param)); } else if (auto *GP = param->getDecl()) { Printer.callPrintStructurePre(PrintStructureKind::GenericParameter, GP); @@ -1815,7 +1822,7 @@ bool ShouldPrintChecker::shouldPrint(const Decl *D, auto Ext = cast(D); // If the extension doesn't add protocols or has no members that we should // print then skip printing it. - SmallVector ProtocolsToPrint; + SmallVector ProtocolsToPrint; getInheritedForPrinting(Ext, Options, ProtocolsToPrint); if (ProtocolsToPrint.empty()) { bool HasMemberToPrint = false; @@ -1909,16 +1916,27 @@ void PrintAST::printBodyIfNecessary(const AbstractFunctionDecl *decl) { printBraceStmt(decl->getBody(), /*newlineIfEmpty*/!isa(decl)); } -void PrintAST::printMutabilityModifiersIfNeeded(const FuncDecl *FD) { +void PrintAST::printSelfAccessKindModifiersIfNeeded(const FuncDecl *FD) { + if (!Options.PrintSelfAccessKindKeyword) + return; + const auto *AD = dyn_cast(FD); - if (FD->isMutating()) { - if (AD == nullptr || AD->isAssumedNonMutating()) - if (!Options.excludeAttrKind(DAK_Mutating)) - Printer.printKeyword("mutating", Options, " "); - } else if (AD && AD->isExplicitNonMutating() && - !Options.excludeAttrKind(DAK_Mutating)) { - Printer.printKeyword("nonmutating", Options, " "); + switch (FD->getSelfAccessKind()) { + case SelfAccessKind::Mutating: + if ((!AD || AD->isAssumedNonMutating()) && + !Options.excludeAttrKind(DAK_Mutating)) + Printer.printKeyword("mutating", Options, " "); + break; + case SelfAccessKind::NonMutating: + if (AD && AD->isExplicitNonMutating() && + !Options.excludeAttrKind(DAK_NonMutating)) + Printer.printKeyword("nonmutating", Options, " "); + break; + case SelfAccessKind::Consuming: + if (!Options.excludeAttrKind(DAK_Consuming)) + Printer.printKeyword("__consuming", Options, " "); + break; } } @@ -2045,7 +2063,7 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) { return true; if (!PrintAccessorBody) { Printer << " "; - printMutabilityModifiersIfNeeded(Accessor); + printSelfAccessKindModifiersIfNeeded(Accessor); Printer.printKeyword(getAccessorLabel(Accessor->getAccessorKind()), Options); @@ -2233,15 +2251,18 @@ void PrintAST::printInherited(const Decl *decl) { if (!Options.PrintInherited) { return; } - SmallVector TypesToPrint; + SmallVector TypesToPrint; getInheritedForPrinting(decl, Options, TypesToPrint); if (TypesToPrint.empty()) return; Printer << " : "; - interleave(TypesToPrint, [&](TypeLoc TL) { - printTypeLoc(TL); + interleave(TypesToPrint, [&](InheritedEntry inherited) { + if (inherited.isUnchecked) + Printer << "@unchecked "; + + printTypeLoc(inherited); }, [&]() { Printer << ", "; }); @@ -2276,7 +2297,7 @@ static void getModuleEntities(ImportDecl *Import, void PrintAST::visitImportDecl(ImportDecl *decl) { printAttributes(decl); - Printer << tok::kw_import << " "; + Printer.printIntroducerKeyword("import", Options, " "); switch (decl->getImportKind()) { case ImportKind::Module: @@ -2329,15 +2350,18 @@ void PrintAST::visitImportDecl(ImportDecl *decl) { [&] { Printer << "."; }); } -static void printExtendedTypeName(Type ExtendedType, ASTPrinter &Printer, - PrintOptions Options) { - Options.FullyQualifiedTypes = false; - Options.FullyQualifiedTypesIfAmbiguous = false; +void PrintAST::printExtendedTypeName(TypeLoc ExtendedTypeLoc) { + bool OldFullyQualifiedTypesIfAmbiguous = + Options.FullyQualifiedTypesIfAmbiguous; + Options.FullyQualifiedTypesIfAmbiguous = + Options.FullyQualifiedExtendedTypesIfAmbiguous; + SWIFT_DEFER { + Options.FullyQualifiedTypesIfAmbiguous = OldFullyQualifiedTypesIfAmbiguous; + }; // Strip off generic arguments, if any. - auto Ty = ExtendedType->getAnyNominal()->getDeclaredType(); - - Ty->print(Printer, Options); + auto Ty = ExtendedTypeLoc.getType()->getAnyNominal()->getDeclaredType(); + printTypeLoc(TypeLoc(ExtendedTypeLoc.getTypeRepr(), Ty)); } @@ -2351,8 +2375,8 @@ void PrintAST::printSynthesizedExtension(Type ExtendedType, auto printRequirementsFrom = [&](ExtensionDecl *ED, bool &IsFirst) { auto Sig = ED->getGenericSignature(); - printSingleDepthOfGenericSignature(Sig->getGenericParams(), - Sig->getRequirements(), + printSingleDepthOfGenericSignature(Sig.getGenericParams(), + Sig.getRequirements(), IsFirst, PrintRequirements, [](const Requirement &Req){ return true; @@ -2393,9 +2417,9 @@ void PrintAST::printSynthesizedExtension(Type ExtendedType, if (Options.BracketOptions.shouldOpenExtension(ExtDecl)) { printDocumentationComment(ExtDecl); printAttributes(ExtDecl); - Printer << tok::kw_extension << " "; + Printer.printIntroducerKeyword("extension", Options, " "); - printExtendedTypeName(ExtendedType, Printer, Options); + printExtendedTypeName(TypeLoc::withoutLoc(ExtendedType)); printInherited(ExtDecl); // We may need to combine requirements from ExtDecl (which has the members @@ -2431,7 +2455,7 @@ void PrintAST::printExtension(ExtensionDecl *decl) { if (Options.BracketOptions.shouldOpenExtension(decl)) { printDocumentationComment(decl); printAttributes(decl); - Printer << "extension "; + Printer.printIntroducerKeyword("extension", Options, " "); recordDeclLoc(decl, [&]{ // We cannot extend sugared types. Type extendedType = decl->getExtendedType(); @@ -2446,7 +2470,7 @@ void PrintAST::printExtension(ExtensionDecl *decl) { printTypeLoc(TypeLoc::withoutLoc(extendedType)); return; } - printExtendedTypeName(extendedType, Printer, Options); + printExtendedTypeName(TypeLoc(decl->getExtendedTypeRepr(), extendedType)); }); printInherited(decl); @@ -2508,60 +2532,6 @@ static bool usesFeatureAsyncAwait(Decl *decl) { } static bool usesFeatureMarkerProtocol(Decl *decl) { - // Check an inheritance clause for a marker protocol. - auto checkInherited = [&](ArrayRef inherited) -> bool { - for (const auto &inheritedEntry : inherited) { - if (auto inheritedType = inheritedEntry.getType()) { - if (inheritedType->isExistentialType()) { - auto layout = inheritedType->getExistentialLayout(); - for (ProtocolType *protoTy : layout.getProtocols()) { - if (protoTy->getDecl()->isMarkerProtocol()) - return true; - } - } - } - } - - return false; - }; - - // Check generic requirements for a marker protocol. - auto checkRequirements = [&](ArrayRef requirements) -> bool { - for (const auto &req: requirements) { - if (req.getKind() == RequirementKind::Conformance && - req.getSecondType()->castTo()->getDecl() - ->isMarkerProtocol()) - return true; - } - - return false; - }; - - if (auto proto = dyn_cast(decl)) { - if (proto->isMarkerProtocol()) - return true; - - // Swift.Error and Swift.CodingKey "don't" use the marker protocol. - if (proto->isSpecificProtocol(KnownProtocolKind::Error) || - proto->isSpecificProtocol(KnownProtocolKind::CodingKey)) { - return false; - } - - if (checkInherited(proto->getInherited())) - return true; - - if (checkRequirements(proto->getRequirementSignature())) - return true; - } - - if (auto ext = dyn_cast(decl)) { - if (checkRequirements(ext->getGenericRequirements())) - return true; - - if (checkInherited(ext->getInherited())) - return true; - } - return false; } @@ -2601,6 +2571,10 @@ static bool usesFeatureConcurrentFunctions(Decl *decl) { return false; } +static bool usesFeatureActors2(Decl *decl) { + return false; +} + static bool usesFeatureSendable(Decl *decl) { if (auto func = dyn_cast(decl)) { if (func->isSendable()) @@ -2634,7 +2608,7 @@ static bool usesFeatureRethrowsProtocol( return false; // Check an inheritance clause for a marker protocol. - auto checkInherited = [&](ArrayRef inherited) -> bool { + auto checkInherited = [&](ArrayRef inherited) -> bool { for (const auto &inheritedEntry : inherited) { if (auto inheritedType = inheritedEntry.getType()) { if (inheritedType->isExistentialType()) { @@ -2671,7 +2645,7 @@ static bool usesFeatureRethrowsProtocol( if (auto genericSig = decl->getInnermostDeclContext() ->getGenericSignatureOfContext()) { - for (const auto &req : genericSig->getRequirements()) { + for (const auto &req : genericSig.getRequirements()) { if (req.getKind() == RequirementKind::Conformance && usesFeatureRethrowsProtocol(req.getProtocolDecl(), checked)) return true; @@ -2757,11 +2731,23 @@ static bool usesFeatureBuiltinBuildExecutor(Decl *decl) { return false; } +static bool usesFeatureBuiltinBuildMainExecutor(Decl *decl) { + return false; +} + static bool usesFeatureBuiltinContinuation(Decl *decl) { return false; } -static bool usesFeatureBuiltinTaskGroup(Decl *decl) { +static bool usesFeatureBuiltinHopToActor(Decl *decl) { + return false; +} + +static bool usesFeatureBuiltinTaskGroupWithArgument(Decl *decl) { + return false; +} + +static bool usesFeatureBuiltinCreateAsyncTaskInGroup(Decl *decl) { return false; } @@ -2776,6 +2762,17 @@ static bool usesFeatureInheritActorContext(Decl *decl) { return false; } +static bool usesFeatureImplicitSelfCapture(Decl *decl) { + if (auto func = dyn_cast(decl)) { + for (auto param : *func->getParameters()) { + if (param->getAttrs().hasAttribute()) + return true; + } + } + + return false; +} + /// Determine the set of "new" features used on a given declaration. /// /// Note: right now, all features we check for are "new". At some point, we'll @@ -2990,8 +2987,7 @@ void PrintAST::visitTypeAliasDecl(TypeAliasDecl *decl) { printDocumentationComment(decl); printAttributes(decl); printAccess(decl); - if (!Options.SkipIntroducerKeywords) - Printer << tok::kw_typealias << " "; + Printer.printIntroducerKeyword("typealias", Options, " "); printContextIfNeeded(decl); recordDeclLoc(decl, [&]{ @@ -3034,8 +3030,7 @@ void PrintAST::visitGenericTypeParamDecl(GenericTypeParamDecl *decl) { void PrintAST::visitAssociatedTypeDecl(AssociatedTypeDecl *decl) { printDocumentationComment(decl); printAttributes(decl); - if (!Options.SkipIntroducerKeywords) - Printer << tok::kw_associatedtype << " "; + Printer.printIntroducerKeyword("associatedtype", Options, " "); recordDeclLoc(decl, [&]{ Printer.printName(decl->getName(), PrintNameContext::TypeMember); @@ -3064,8 +3059,7 @@ void PrintAST::visitEnumDecl(EnumDecl *decl) { printSourceRange(CharSourceRange(Ctx.SourceMgr, decl->getStartLoc(), decl->getBraces().Start.getAdvancedLoc(-1)), Ctx); } else { - if (!Options.SkipIntroducerKeywords) - Printer << tok::kw_enum << " "; + Printer.printIntroducerKeyword("enum", Options, " "); printContextIfNeeded(decl); recordDeclLoc(decl, [&]{ @@ -3092,8 +3086,7 @@ void PrintAST::visitStructDecl(StructDecl *decl) { printSourceRange(CharSourceRange(Ctx.SourceMgr, decl->getStartLoc(), decl->getBraces().Start.getAdvancedLoc(-1)), Ctx); } else { - if (!Options.SkipIntroducerKeywords) - Printer << tok::kw_struct << " "; + Printer.printIntroducerKeyword("struct", Options, " "); printContextIfNeeded(decl); recordDeclLoc(decl, [&]{ @@ -3120,13 +3113,8 @@ void PrintAST::visitClassDecl(ClassDecl *decl) { printSourceRange(CharSourceRange(Ctx.SourceMgr, decl->getStartLoc(), decl->getBraces().Start.getAdvancedLoc(-1)), Ctx); } else { - if (!Options.SkipIntroducerKeywords) { - if (decl->isExplicitActor()) { - Printer.printKeyword("actor", Options, " "); - } else { - Printer << tok::kw_class << " "; - } - } + Printer.printIntroducerKeyword( + decl->isExplicitActor() ? "actor" : "class", Options, " "); printContextIfNeeded(decl); recordDeclLoc(decl, [&]{ @@ -3155,8 +3143,7 @@ void PrintAST::visitProtocolDecl(ProtocolDecl *decl) { printSourceRange(CharSourceRange(Ctx.SourceMgr, decl->getStartLoc(), decl->getBraces().Start.getAdvancedLoc(-1)), Ctx); } else { - if (!Options.SkipIntroducerKeywords) - Printer << tok::kw_protocol << " "; + Printer.printIntroducerKeyword("protocol", Options, " "); printContextIfNeeded(decl); recordDeclLoc(decl, [&]{ @@ -3219,6 +3206,9 @@ static void printParameterFlags(ASTPrinter &printer, break; } + if (flags.isIsolated()) + printer.printKeyword("isolated", options, " "); + if (!options.excludeAttrKind(TAK_escaping) && escaping) printer.printKeyword("@escaping", options, " "); } @@ -3233,20 +3223,12 @@ void PrintAST::visitVarDecl(VarDecl *decl) { Printer << "@_hasStorage "; printAttributes(decl); printAccess(decl); - if (!Options.SkipIntroducerKeywords) { - if (decl->isStatic() && Options.PrintStaticKeyword) - printStaticKeyword(decl->getCorrectStaticSpelling()); - if (decl->getKind() == DeclKind::Var - || Options.PrintParameterSpecifiers) { - // Map all non-let specifiers to 'var'. This is not correct, but - // SourceKit relies on this for info about parameter decls. - if (decl->isLet()) - Printer << tok::kw_let; - else - Printer << tok::kw_var; - - Printer << " "; - } + if (decl->isStatic() && Options.PrintStaticKeyword) + printStaticKeyword(decl->getCorrectStaticSpelling()); + if (decl->getKind() == DeclKind::Var || Options.PrintParameterSpecifiers) { + // Map all non-let specifiers to 'var'. This is not correct, but + // SourceKit relies on this for info about parameter decls. + Printer.printIntroducerKeyword(decl->isLet() ? "let" : "var", Options, " "); } printContextIfNeeded(decl); recordDeclLoc(decl, @@ -3330,6 +3312,10 @@ void PrintAST::printOneParameter(const ParamDecl *param, }; printAttributes(param); + + if (param->isIsolated()) + Printer << "isolated "; + printArgName(); TypeLoc TheTypeLoc; @@ -3453,7 +3439,7 @@ void PrintAST::visitAccessorDecl(AccessorDecl *decl) { printDocumentationComment(decl); printAttributes(decl); // Explicitly print 'mutating' and 'nonmutating' if needed. - printMutabilityModifiersIfNeeded(decl); + printSelfAccessKindModifiersIfNeeded(decl); switch (auto kind = decl->getAccessorKind()) { case AccessorKind::Get: @@ -3512,16 +3498,12 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) { SourceRange(StartLoc, EndLoc)); printSourceRange(Range, Ctx); } else { - if (!Options.SkipIntroducerKeywords) { - if (decl->isStatic() && Options.PrintStaticKeyword) - printStaticKeyword(decl->getCorrectStaticSpelling()); + if (decl->isStatic() && Options.PrintStaticKeyword) + printStaticKeyword(decl->getCorrectStaticSpelling()); + + printSelfAccessKindModifiersIfNeeded(decl); + Printer.printIntroducerKeyword("func", Options, " "); - printMutabilityModifiersIfNeeded(decl); - if (decl->isConsuming() && !decl->getAttrs().hasAttribute()) { - Printer.printKeyword("__consuming", Options, " "); - } - Printer << tok::kw_func << " "; - } printContextIfNeeded(decl); recordDeclLoc(decl, [&]{ // Name @@ -3679,7 +3661,7 @@ void PrintAST::visitEnumCaseDecl(EnumCaseDecl *decl) { printDocumentationComment(elems[0]); printAttributes(elems[0]); } - Printer << tok::kw_case << " "; + Printer.printIntroducerKeyword("case", Options, " "); llvm::interleave(elems.begin(), elems.end(), [&](EnumElementDecl *elt) { @@ -3693,7 +3675,7 @@ void PrintAST::visitEnumElementDecl(EnumElementDecl *decl) { // In cases where there is no parent EnumCaseDecl (such as imported or // deserialized elements), print the element independently. printAttributes(decl); - Printer << tok::kw_case << " "; + Printer.printIntroducerKeyword("case", Options, " "); printEnumElement(decl); } @@ -3701,8 +3683,7 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) { printDocumentationComment(decl); printAttributes(decl); printAccess(decl); - if (!Options.SkipIntroducerKeywords && decl->isStatic() && - Options.PrintStaticKeyword) + if (decl->isStatic() && Options.PrintStaticKeyword) printStaticKeyword(decl->getCorrectStaticSpelling()); printContextIfNeeded(decl); recordDeclLoc(decl, [&]{ @@ -3802,26 +3783,17 @@ void PrintAST::visitDestructorDecl(DestructorDecl *decl) { void PrintAST::visitInfixOperatorDecl(InfixOperatorDecl *decl) { Printer.printKeyword("infix", Options, " "); - Printer << tok::kw_operator << " "; + Printer.printIntroducerKeyword("operator", Options, " "); recordDeclLoc(decl, [&]{ Printer.printName(decl->getName()); }); if (auto *group = decl->getPrecedenceGroup()) Printer << " : " << group->getName(); - auto designatedNominalTypes = decl->getDesignatedNominalTypes(); - auto first = true; - for (auto typeDecl : designatedNominalTypes) { - if (first && !decl->getPrecedenceGroup()) - Printer << " : " << typeDecl->getName(); - else - Printer << ", " << typeDecl->getName(); - first = false; - } } void PrintAST::visitPrecedenceGroupDecl(PrecedenceGroupDecl *decl) { - Printer << tok::kw_precedencegroup << " "; + Printer.printIntroducerKeyword("precedencegroup", Options, " "); recordDeclLoc(decl, [&]{ Printer.printName(decl->getName()); @@ -3881,38 +3853,20 @@ void PrintAST::visitPrecedenceGroupDecl(PrecedenceGroupDecl *decl) { void PrintAST::visitPrefixOperatorDecl(PrefixOperatorDecl *decl) { Printer.printKeyword("prefix", Options, " "); - Printer << tok::kw_operator << " "; + Printer.printIntroducerKeyword("operator", Options, " "); recordDeclLoc(decl, [&]{ Printer.printName(decl->getName()); }); - auto designatedNominalTypes = decl->getDesignatedNominalTypes(); - auto first = true; - for (auto typeDecl : designatedNominalTypes) { - if (first) - Printer << " : " << typeDecl->getName(); - else - Printer << ", " << typeDecl->getName(); - first = false; - } } void PrintAST::visitPostfixOperatorDecl(PostfixOperatorDecl *decl) { Printer.printKeyword("postfix", Options, " "); - Printer << tok::kw_operator << " "; + Printer.printIntroducerKeyword("operator", Options, " "); recordDeclLoc(decl, [&]{ Printer.printName(decl->getName()); }); - auto designatedNominalTypes = decl->getDesignatedNominalTypes(); - auto first = true; - for (auto typeDecl : designatedNominalTypes) { - if (first) - Printer << " : " << typeDecl->getName(); - else - Printer << ", " << typeDecl->getName(); - first = false; - } } void PrintAST::visitModuleDecl(ModuleDecl *decl) { } @@ -5188,6 +5142,11 @@ class TypePrinter : public TypeVisitor { Printer << "?"; } + void visitVariadicSequenceType(VariadicSequenceType *T) { + visit(T->getBaseType()); + Printer << "..."; + } + void visitProtocolType(ProtocolType *T) { printQualifiedType(T); } @@ -5719,6 +5678,12 @@ void ProtocolConformance::printName(llvm::raw_ostream &os, os << ")"; break; } + case ProtocolConformanceKind::Builtin: { + auto builtin = cast(this); + os << builtin->getProtocol()->getName() + << " type " << builtin->getType(); + break; + } } } @@ -5765,9 +5730,10 @@ void swift::printEnumElementsAsCases( } void -swift::getInheritedForPrinting(const Decl *decl, const PrintOptions &options, - llvm::SmallVectorImpl &Results) { - ArrayRef inherited; +swift::getInheritedForPrinting( + const Decl *decl, const PrintOptions &options, + llvm::SmallVectorImpl &Results) { + ArrayRef inherited; if (auto td = dyn_cast(decl)) { inherited = td->getInherited(); } else if (auto ed = dyn_cast(decl)) { @@ -5775,37 +5741,29 @@ swift::getInheritedForPrinting(const Decl *decl, const PrintOptions &options, } // Collect explicit inherited types. - for (auto TL: inherited) { - if (auto ty = TL.getType()) { + for (auto entry: inherited) { + if (auto ty = entry.getType()) { bool foundUnprintable = ty.findIf([&](Type subTy) { if (auto aliasTy = dyn_cast(subTy.getPointer())) return !options.shouldPrint(aliasTy->getDecl()); if (auto NTD = subTy->getAnyNominal()) { if (!options.shouldPrint(NTD)) return true; - - if (auto PD = dyn_cast(NTD)) { - // Marker protocols are unprintable on concrete types, but they're - // okay on extension declarations and protocols. - if (PD->isMarkerProtocol() && !isa(decl) && - !isa(decl)) - return true; - } } return false; }); if (foundUnprintable) continue; } - Results.push_back(TL); + + Results.push_back(entry); } // Collect synthesized conformances. auto &ctx = decl->getASTContext(); + llvm::SetVector protocols; for (auto attr : decl->getAttrs().getAttributes()) { if (auto *proto = ctx.getProtocol(attr->getProtocolKind())) { - if (!options.shouldPrint(proto)) - continue; // The SerialExecutor conformance is only synthesized on the root // actor class, so we can just test resilience immediately. if (proto->isSpecificProtocol(KnownProtocolKind::SerialExecutor) && @@ -5815,7 +5773,137 @@ swift::getInheritedForPrinting(const Decl *decl, const PrintOptions &options, isa(decl) && cast(decl)->hasRawType()) continue; - Results.push_back(TypeLoc::withoutLoc(proto->getDeclaredInterfaceType())); + protocols.insert(proto); + } + } + + for (size_t i = 0; i < protocols.size(); i++) { + auto proto = protocols[i]; + + if (!options.shouldPrint(proto)) { + // If private stdlib protocols are skipped and this is a private stdlib + // protocol, see if any of its inherited protocols are public. Those + // protocols can affect the user-visible behavior of the declaration, and + // should be printed. + if (options.SkipPrivateStdlibDecls && + proto->isPrivateStdlibDecl(!options.SkipUnderscoredStdlibProtocols)) { + auto inheritedProtocols = proto->getInheritedProtocols(); + protocols.insert(inheritedProtocols.begin(), inheritedProtocols.end()); + } + continue; + } + + Results.push_back({TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), + /*isUnchecked=*/false}); + } +} + +//===----------------------------------------------------------------------===// +// Generic param list printing. +//===----------------------------------------------------------------------===// + +void RequirementRepr::dump() const { + print(llvm::errs()); + llvm::errs() << "\n"; +} + +void RequirementRepr::print(raw_ostream &out) const { + StreamPrinter printer(out); + print(printer); +} + +void RequirementRepr::print(ASTPrinter &out) const { + auto printLayoutConstraint = + [&](const LayoutConstraintLoc &LayoutConstraintLoc) { + LayoutConstraintLoc.getLayoutConstraint()->print(out, PrintOptions()); + }; + + switch (getKind()) { + case RequirementReprKind::LayoutConstraint: + if (auto *repr = getSubjectRepr()) { + repr->print(out, PrintOptions()); } + out << " : "; + printLayoutConstraint(getLayoutConstraintLoc()); + break; + + case RequirementReprKind::TypeConstraint: + if (auto *repr = getSubjectRepr()) { + repr->print(out, PrintOptions()); + } + out << " : "; + if (auto *repr = getConstraintRepr()) { + repr->print(out, PrintOptions()); + } + break; + + case RequirementReprKind::SameType: + if (auto *repr = getFirstTypeRepr()) { + repr->print(out, PrintOptions()); + } + out << " == "; + if (auto *repr = getSecondTypeRepr()) { + repr->print(out, PrintOptions()); + } + break; } } + +void GenericParamList::dump() const { + print(llvm::errs()); + llvm::errs() << '\n'; +} + +void GenericParamList::print(raw_ostream &out, const PrintOptions &PO) const { + StreamPrinter printer(out); + print(printer, PO); +} + +static void printTrailingRequirements(ASTPrinter &Printer, + ArrayRef Reqs, + bool printWhereKeyword) { + if (Reqs.empty()) + return; + + if (printWhereKeyword) + Printer << " where "; + interleave( + Reqs, + [&](const RequirementRepr &req) { + Printer.callPrintStructurePre(PrintStructureKind::GenericRequirement); + req.print(Printer); + Printer.printStructurePost(PrintStructureKind::GenericRequirement); + }, + [&] { Printer << ", "; }); +} + +void GenericParamList::print(ASTPrinter &Printer, + const PrintOptions &PO) const { + Printer << '<'; + interleave( + *this, + [&](const GenericTypeParamDecl *P) { + Printer << P->getName(); + if (!P->getInherited().empty()) { + Printer << " : "; + + auto loc = P->getInherited()[0]; + if (willUseTypeReprPrinting(loc, nullptr, PO)) { + loc.getTypeRepr()->print(Printer, PO); + } else { + loc.getType()->print(Printer, PO); + } + } + }, + [&] { Printer << ", "; }); + + printTrailingRequirements(Printer, getRequirements(), + /*printWhereKeyword*/ true); + Printer << '>'; +} + +void TrailingWhereClause::print(llvm::raw_ostream &OS, + bool printWhereKeyword) const { + StreamPrinter Printer(OS); + printTrailingRequirements(Printer, getRequirements(), printWhereKeyword); +} diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 98eaccaaaaa2d..b7b78abdd0daf 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -44,7 +44,7 @@ namespace ast_scope { #pragma mark ScopeCreator -class ScopeCreator final { +class ScopeCreator final : public ASTAllocated { friend class ASTSourceFileScope; /// For allocating scopes. ASTContext &ctx; @@ -181,18 +181,6 @@ class ScopeCreator final { void print(raw_ostream &out) const { out << "(swift::ASTSourceFileScope*) " << sourceFileScope << "\n"; } - - // Make vanilla new illegal. - void *operator new(size_t bytes) = delete; - - // Only allow allocation of scopes using the allocator of a particular source - // file. - void *operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment = alignof(ScopeCreator)); - void *operator new(size_t Bytes, void *Mem) { - ASTScopeAssert(Mem, "Allocation failed"); - return Mem; - } }; } // ast_scope } // namespace swift @@ -480,9 +468,15 @@ ScopeCreator::addToScopeTreeAndReturnInsertionPoint(ASTNode n, if (!n) return parent; + // HACK: LLDB creates implicit pattern bindings that... contain user + // expressions. We need to actually honor lookups through those bindings + // in case they contain closures that bind additional variables in further + // scopes. if (auto *d = n.dyn_cast()) if (d->isImplicit()) - return parent; + if (!isa(d) + || !cast(d)->isDebuggerBinding()) + return parent; NodeAdder adder(endLoc); if (auto *p = n.dyn_cast()) @@ -733,6 +727,23 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint( this, decl, patternEntryIndex); } + // If this pattern binding entry was created by the debugger, it will always + // have a synthesized init that is created from user code. We special-case + // lookups into these scopes to look through the debugger's chicanery to the + // underlying user-defined scopes, if any. + if (patternEntry.isFromDebugger() && patternEntry.getInit()) { + ASTScopeAssert( + patternEntry.getInit()->getSourceRange().isValid(), + "pattern initializer has invalid source range"); + ASTScopeAssert( + !getSourceManager().isBeforeInBuffer( + patternEntry.getInit()->getStartLoc(), decl->getStartLoc()), + "inits are always after the '='"); + scopeCreator + .constructExpandAndInsert( + this, decl, patternEntryIndex); + } + // Add accessors for the variables in this pattern. patternEntry.getPattern()->forEachVariable([&](VarDecl *var) { scopeCreator.addChildrenForParsedAccessors(var, this); @@ -751,8 +762,7 @@ void PatternEntryInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { // Create a child for the initializer expression. - scopeCreator.addToScopeTree(ASTNode(getPatternEntry().getOriginalInit()), - this); + scopeCreator.addToScopeTree(ASTNode(initAsWrittenWhenCreated), this); } @@ -1143,25 +1153,6 @@ AbstractPatternEntryScope::AbstractPatternEntryScope( "out of bounds"); } -#pragma mark new operators -void *ASTScopeImpl::operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment) { - return ctx.Allocate(bytes, alignment); -} - -void *Portion::operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment) { - return ctx.Allocate(bytes, alignment); -} -void *ASTScope::operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment) { - return ctx.Allocate(bytes, alignment); -} -void *ScopeCreator::operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment) { - return ctx.Allocate(bytes, alignment); -} - #pragma mark - expandBody void FunctionBodyScope::expandBody(ScopeCreator &scopeCreator) { diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 61231ab336d11..a5b63cb53f04f 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -408,7 +408,7 @@ bool PatternEntryInitializerScope::lookupLocalsOrMembers( bool CaptureListScope::lookupLocalsOrMembers(DeclConsumer consumer) const { for (auto &e : expr->getCaptureList()) { - if (consumer.consume({e.Var})) + if (consumer.consume({e.getVar()})) return true; } return false; diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index baf1ef50aebf3..f6919d24d1d80 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -40,6 +40,13 @@ static SourceLoc getLocAfterExtendedNominal(const ExtensionDecl *); void ASTScopeImpl::checkSourceRangeBeforeAddingChild(ASTScopeImpl *child, const ASTContext &ctx) const { + // Ignore debugger bindings - they're a special mix of user code and implicit + // wrapper code that is too difficult to check for consistency. + if (auto d = getDeclIfAny().getPtrOrNull()) + if (auto *PBD = dyn_cast(d)) + if (PBD->isDebuggerBinding()) + return; + auto &sourceMgr = ctx.SourceMgr; auto range = getCharSourceRangeOfScope(sourceMgr); diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index f052501970096..a046d786f91d7 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -475,7 +475,7 @@ class Verifier : public ASTWalker { case AbstractFunctionDecl::BodyKind::None: case AbstractFunctionDecl::BodyKind::TypeChecked: case AbstractFunctionDecl::BodyKind::Skipped: - case AbstractFunctionDecl::BodyKind::MemberwiseInitializer: + case AbstractFunctionDecl::BodyKind::SILSynthesize: case AbstractFunctionDecl::BodyKind::Deserialized: return true; @@ -1758,6 +1758,26 @@ class Verifier : public ASTWalker { } } + /// A version of AnyFunctionType::equalParams() that ignores "isolated" + /// parameters, which aren't represented in the type system. + static bool equalParamsIgnoringIsolation( + ArrayRef a, + ArrayRef b) { + auto withoutIsolation = [](AnyFunctionType::Param param) { + return param.withFlags(param.getParameterFlags().withIsolated(false)); + }; + + if (a.size() != b.size()) + return false; + + for (unsigned i = 0, n = a.size(); i != n; ++i) { + if (withoutIsolation(a[i]) != withoutIsolation(b[i])) + return false; + } + + return true; + } + void verifyChecked(ApplyExpr *E) { PrettyStackTraceExpr debugStack(Ctx, "verifying ApplyExpr", E); @@ -1780,9 +1800,9 @@ class Verifier : public ASTWalker { SmallVector Args; Type InputExprTy = E->getArg()->getType(); - AnyFunctionType::decomposeInput(InputExprTy, Args); + AnyFunctionType::decomposeTuple(InputExprTy, Args); auto Params = FT->getParams(); - if (!AnyFunctionType::equalParams(Args, Params)) { + if (!equalParamsIgnoringIsolation(Args, Params)) { Out << "Argument type does not match parameter type in ApplyExpr:" "\nArgument type: "; InputExprTy.print(Out); @@ -1799,7 +1819,7 @@ class Verifier : public ASTWalker { E->dump(Out); Out << "\n"; abort(); - } else if (E->throws() && !FT->isThrowing()) { + } else if (E->throws() && !FT->isThrowing() && !E->implicitlyThrows()) { PolymorphicEffectKind rethrowingKind = PolymorphicEffectKind::Invalid; if (auto DRE = dyn_cast(E->getFn())) { if (auto fnDecl = dyn_cast(DRE->getDecl())) { @@ -1988,6 +2008,15 @@ class Verifier : public ASTWalker { verifyCheckedBase(E); } + void verifyChecked(ParenExpr *E) { + PrettyStackTraceExpr debugStack(Ctx, "verifying ParenExpr", E); + if (!isa(E->getType().getPointer())) { + Out << "ParenExpr not of ParenType\n"; + abort(); + } + verifyCheckedBase(E); + } + void verifyChecked(AnyTryExpr *E) { PrettyStackTraceExpr debugStack(Ctx, "verifying AnyTryExpr", E); diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 9042a9866e5a6..a2adbee92cd57 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -796,14 +796,15 @@ class Traversal : public ASTVisitorgetCaptureList()) { if (Walker.shouldWalkCaptureInitializerExpressions()) { - for (auto entryIdx : range(c.Init->getNumPatternEntries())) { - if (auto newInit = doIt(c.Init->getInit(entryIdx))) - c.Init->setInit(entryIdx, newInit); + for (auto entryIdx : range(c.PBD->getNumPatternEntries())) { + if (auto newInit = doIt(c.PBD->getInit(entryIdx))) + c.PBD->setInit(entryIdx, newInit); else return nullptr; } - } else if (doIt(c.Var) || doIt(c.Init)) { - return nullptr; + } else { + if (doIt(c.PBD)) + return nullptr; } } @@ -1125,6 +1126,7 @@ class Traversal : public ASTVisitor(D)) { + if (auto *VD = dyn_cast(D)) { // VarDecls are walked via their NamedPattern, ignore them if we encounter // then in the few cases where they are also pushed outside as members. // In all those cases we can walk them via the pattern binding decl. @@ -1248,10 +1250,10 @@ class Traversal : public ASTVisitorgetDeclContext()->getParentSourceFile()) return true; - if (Decl *ParentD = Walker.Parent.getAsDecl()) - return (isa(ParentD) || isa(ParentD)); + if (Walker.Parent.getAsDecl() && VD->getParentPatternBinding()) + return true; auto walkerParentAsStmt = Walker.Parent.getAsStmt(); - if (walkerParentAsStmt && isa(walkerParentAsStmt)) + if (isa_and_nonnull(walkerParentAsStmt)) return true; } return false; @@ -1563,13 +1565,6 @@ Stmt *Traversal::visitForEachStmt(ForEachStmt *S) { return nullptr; } - if (Expr *Where = S->getWhere()) { - if ((Where = doIt(Where))) - S->setWhere(Where); - else - return nullptr; - } - // The iterator decl is built directly on top of the sequence // expression, so don't visit both. if (Expr *Sequence = S->getSequence()) { @@ -1579,6 +1574,13 @@ Stmt *Traversal::visitForEachStmt(ForEachStmt *S) { return nullptr; } + if (Expr *Where = S->getWhere()) { + if ((Where = doIt(Where))) + S->setWhere(Where); + else + return nullptr; + } + if (auto IteratorNext = S->getConvertElementExpr()) { if ((IteratorNext = doIt(IteratorNext))) S->setConvertElementExpr(IteratorNext); @@ -1849,10 +1851,18 @@ bool Traversal::visitOwnedTypeRepr(OwnedTypeRepr *T) { return doIt(T->getBase()); } +bool Traversal::visitIsolatedTypeRepr(IsolatedTypeRepr *T) { + return doIt(T->getBase()); +} + bool Traversal::visitOpaqueReturnTypeRepr(OpaqueReturnTypeRepr *T) { return doIt(T->getConstraint()); } +bool Traversal::visitNamedOpaqueReturnTypeRepr(NamedOpaqueReturnTypeRepr *T) { + return doIt(T->getBase()); +} + bool Traversal::visitPlaceholderTypeRepr(PlaceholderTypeRepr *T) { return false; } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 008fc36e5f9cd..48af246aeb413 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -68,12 +68,6 @@ static_assert(DeclAttribute::isOptionSetFor##Id(DeclAttribute::DeclAttrOptions:: #Name " needs to specify either APIBreakingToRemove or APIStableToRemove"); #include "swift/AST/Attr.def" -// Only allow allocation of attributes using the allocator in ASTContext. -void *AttributeBase::operator new(size_t Bytes, ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - StringRef swift::getAccessLevelSpelling(AccessLevel value) { switch (value) { case AccessLevel::Private: return "private"; @@ -326,6 +320,38 @@ DeclAttributes::getDeprecated(const ASTContext &ctx) const { return conditional; } +const AvailableAttr * +DeclAttributes::getSoftDeprecated(const ASTContext &ctx) const { + const AvailableAttr *conditional = nullptr; + const AvailableAttr *bestActive = findMostSpecificActivePlatform(ctx); + for (auto Attr : *this) { + if (auto AvAttr = dyn_cast(Attr)) { + if (AvAttr->isInvalid()) + continue; + + if (AvAttr->hasPlatform() && + (!bestActive || AvAttr != bestActive)) + continue; + + if (!AvAttr->isActivePlatform(ctx) && + !AvAttr->isLanguageVersionSpecific() && + !AvAttr->isPackageDescriptionVersionSpecific()) + continue; + + Optional DeprecatedVersion = AvAttr->Deprecated; + if (!DeprecatedVersion.hasValue()) + continue; + + llvm::VersionTuple ActiveVersion = AvAttr->getActiveVersion(ctx); + + if (DeprecatedVersion.getValue() > ActiveVersion) { + conditional = AvAttr; + } + } + } + return conditional; +} + void DeclAttributes::dump(const Decl *D) const { StreamPrinter P(llvm::errs()); PrintOptions PO = PrintOptions::printEverything(); @@ -425,7 +451,9 @@ static void printShortFormAvailable(ArrayRef Attrs, for (auto *DA : Attrs) { auto *AvailAttr = cast(DA); assert(AvailAttr->Introduced.hasValue()); - if (isShortFormAvailabilityImpliedByOther(AvailAttr, Attrs)) + // Avoid omitting available attribute when we are printing module interface. + if (!Options.IsForSwiftInterface && + isShortFormAvailabilityImpliedByOther(AvailAttr, Attrs)) continue; Printer << platformString(AvailAttr->Platform) << " " << AvailAttr->Introduced.getValue().getAsString() << ", "; @@ -598,7 +626,7 @@ static void printDifferentiableAttrArguments( // generic signature. They should not be printed. ArrayRef derivativeRequirements; if (auto derivativeGenSig = attr->getDerivativeGenericSignature()) - derivativeRequirements = derivativeGenSig->getRequirements(); + derivativeRequirements = derivativeGenSig.getRequirements(); auto requirementsToPrint = llvm::make_filter_range(derivativeRequirements, [&](Requirement req) { if (const auto &originalGenSig = original->getGenericSignature()) @@ -747,6 +775,10 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, // implication. Thus we can skip them. if (auto *VD = dyn_cast(D)) { if (auto *BD = VD->getOverriddenDecl()) { + // If the overriden decl won't be printed, printing override will fail + // the build of the interface file. + if (!Options.shouldPrint(BD)) + return false; if (!BD->hasClangNode() && !BD->getFormalAccessScope(VD->getDeclContext(), /*treatUsableFromInlineAsPublic*/ true) @@ -790,7 +822,6 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_ReferenceOwnership: case DAK_Effects: case DAK_Optimize: - case DAK_ActorIndependent: if (DeclAttribute::isDeclModifier(getKind())) { Printer.printKeyword(getAttrName(), Options); } else if (Options.IsForSwiftInterface && getKind() == DAK_ResultBuilder) { @@ -878,8 +909,20 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, if (Attr->Obsoleted) Printer << ", obsoleted: " << Attr->Obsoleted.getValue().getAsString(); - if (!Attr->Rename.empty()) + if (!Attr->Rename.empty()) { Printer << ", renamed: \"" << Attr->Rename << "\""; + } else if (Attr->RenameDecl) { + Printer << ", renamed: \""; + if (auto *Accessor = dyn_cast(Attr->RenameDecl)) { + SmallString<32> Name; + llvm::raw_svector_ostream OS(Name); + Accessor->printUserFacingName(OS); + Printer << Name.str(); + } else { + Printer << Attr->RenameDecl->getName(); + } + Printer << "\""; + } // If there's no message, but this is specifically an imported // "unavailable in Swift" attribute, synthesize a message to look good in @@ -942,10 +985,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, if (target) Printer << "target: " << target << ", "; SmallVector requirementsScratch; - ArrayRef requirements; - if (auto sig = attr->getSpecializedSignature()) - requirements = sig->getRequirements(); - + auto requirements = attr->getSpecializedSignature().getRequirements(); auto *FnDecl = dyn_cast_or_null(D); if (FnDecl && FnDecl->getGenericSignature()) { auto genericSig = FnDecl->getGenericSignature(); @@ -1088,20 +1128,6 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, #include "swift/AST/Attr.def" llvm_unreachable("handled above"); - case DAK_CompletionHandlerAsync: { - auto *attr = cast(this); - Printer.printAttrName("@completionHandlerAsync"); - Printer << "(\""; - if (attr->AsyncFunctionDecl) { - Printer << attr->AsyncFunctionDecl->getName(); - } else { - Printer << attr->AsyncFunctionName; - } - Printer << "\", completionHandlerIndex: " << - attr->CompletionHandlerIndex << ')'; - break; - } - default: assert(DeclAttribute::isDeclModifier(getKind()) && "handled above"); @@ -1178,15 +1204,6 @@ StringRef DeclAttribute::getAttrName() const { } llvm_unreachable("Invalid inline kind"); } - case DAK_ActorIndependent: { - switch (cast(this)->getKind()) { - case ActorIndependentKind::Safe: - return "actorIndependent"; - case ActorIndependentKind::Unsafe: - return "actorIndependent(unsafe)"; - } - llvm_unreachable("Invalid actorIndependent kind"); - } case DAK_Optimize: { switch (cast(this)->getMode()) { case OptimizationMode::NoOptimization: @@ -1246,8 +1263,6 @@ StringRef DeclAttribute::getAttrName() const { return "derivative"; case DAK_Transpose: return "transpose"; - case DAK_CompletionHandlerAsync: - return "completionHandlerAsync"; } llvm_unreachable("bad DeclAttrKind"); } @@ -1469,13 +1484,24 @@ AvailableAttr::createPlatformAgnostic(ASTContext &C, assert(!Obsoleted.empty()); } return new (C) AvailableAttr( - SourceLoc(), SourceRange(), PlatformKind::none, Message, Rename, + SourceLoc(), SourceRange(), PlatformKind::none, Message, Rename, nullptr, NoVersion, SourceRange(), NoVersion, SourceRange(), Obsoleted, SourceRange(), Kind, /* isImplicit */ false); } +AvailableAttr *AvailableAttr::createForAlternative( + ASTContext &C, AbstractFunctionDecl *AsyncFunc) { + llvm::VersionTuple NoVersion; + return new (C) AvailableAttr( + SourceLoc(), SourceRange(), PlatformKind::none, "", "", AsyncFunc, + NoVersion, SourceRange(), + NoVersion, SourceRange(), + NoVersion, SourceRange(), + PlatformAgnosticAvailabilityKind::None, /*Implicit=*/true); +} + bool AvailableAttr::isActivePlatform(const ASTContext &ctx) const { return isPlatformActive(Platform, ctx.LangOpts); } @@ -1483,7 +1509,7 @@ bool AvailableAttr::isActivePlatform(const ASTContext &ctx) const { AvailableAttr *AvailableAttr::clone(ASTContext &C, bool implicit) const { return new (C) AvailableAttr(implicit ? SourceLoc() : AtLoc, implicit ? SourceRange() : getRange(), - Platform, Message, Rename, + Platform, Message, Rename, RenameDecl, Introduced ? *Introduced : llvm::VersionTuple(), implicit ? SourceRange() : IntroducedRange, Deprecated ? *Deprecated : llvm::VersionTuple(), @@ -1809,7 +1835,7 @@ void DifferentiableAttr::setParameterIndices(IndexSubset *paramIndices) { GenericEnvironment *DifferentiableAttr::getDerivativeGenericEnvironment( AbstractFunctionDecl *original) const { if (auto derivativeGenSig = getDerivativeGenericSignature()) - return derivativeGenSig->getGenericEnvironment(); + return derivativeGenSig.getGenericEnvironment(); return original->getGenericEnvironment(); } diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 883e9ff878c0e..7cb7d1863c890 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -99,7 +99,7 @@ createAvailableAttr(PlatformKind Platform, return new (Context) AvailableAttr( SourceLoc(), SourceRange(), Platform, /*Message=*/StringRef(), - /*Rename=*/StringRef(), + /*Rename=*/StringRef(), /*RenameDecl=*/nullptr, Introduced, /*IntroducedRange=*/SourceRange(), Deprecated, /*DeprecatedRange=*/SourceRange(), Obsoleted, /*ObsoletedRange=*/SourceRange(), @@ -330,10 +330,22 @@ AvailabilityContext ASTContext::getConcurrencyAvailability() { return getSwift55Availability(); } +AvailabilityContext ASTContext::getBackDeployedConcurrencyAvailability() { + return getSwift51Availability(); +} + AvailabilityContext ASTContext::getDifferentiationAvailability() { return getSwiftFutureAvailability(); } +AvailabilityContext ASTContext::getMultiPayloadEnumTagSinglePayload() { + return getSwift56Availability(); +} + +AvailabilityContext ASTContext::getObjCIsUniquelyReferencedAvailability() { + return getSwift56Availability(); +} + AvailabilityContext ASTContext::getSwift52Availability() { auto target = LangOpts.Target; @@ -413,9 +425,25 @@ AvailabilityContext ASTContext::getSwift54Availability() { } AvailabilityContext ASTContext::getSwift55Availability() { - return getSwiftFutureAvailability(); + auto target = LangOpts.Target; + + if (target.isMacOSX() ) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(12, 0, 0))); + } else if (target.isiOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(15, 0, 0))); + } else if (target.isWatchOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(8, 0, 0))); + } else { + return AvailabilityContext::alwaysAvailable(); + } } +AvailabilityContext ASTContext::getSwift56Availability() { + return getSwiftFutureAvailability(); +} AvailabilityContext ASTContext::getSwiftFutureAvailability() { auto target = LangOpts.Target; @@ -433,3 +461,21 @@ AvailabilityContext ASTContext::getSwiftFutureAvailability() { return AvailabilityContext::alwaysAvailable(); } } + +AvailabilityContext +ASTContext::getSwift5PlusAvailability(llvm::VersionTuple swiftVersion) { + if (swiftVersion.getMajor() == 5) { + switch (*swiftVersion.getMinor()) { + case 0: return getSwift50Availability(); + case 1: return getSwift51Availability(); + case 2: return getSwift52Availability(); + case 3: return getSwift53Availability(); + case 4: return getSwift54Availability(); + case 5: return getSwift55Availability(); + case 6: return getSwift56Availability(); + default: break; + } + } + llvm::report_fatal_error("Missing call to getSwiftXYAvailability for Swift " + + swiftVersion.getAsString()); +} diff --git a/lib/AST/AvailabilitySpec.cpp b/lib/AST/AvailabilitySpec.cpp index 727efd4b6dd1b..4a555a59d848f 100644 --- a/lib/AST/AvailabilitySpec.cpp +++ b/lib/AST/AvailabilitySpec.cpp @@ -35,14 +35,6 @@ SourceRange AvailabilitySpec::getSourceRange() const { llvm_unreachable("bad AvailabilitySpecKind"); } -// Only allow allocation of AvailabilitySpecs using the -// allocator in ASTContext. -void *AvailabilitySpec::operator new(size_t Bytes, ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - - SourceRange PlatformVersionConstraintAvailabilitySpec::getSourceRange() const { return SourceRange(PlatformLoc, VersionSrcRange.End); } diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index c67e9ec70ed0c..892fa0c321f97 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -1424,27 +1424,27 @@ Type swift::getAsyncTaskAndContextType(ASTContext &ctx) { return TupleType::get(resultTupleElements, ctx); } -static ValueDecl *getCreateAsyncTaskFuture(ASTContext &ctx, Identifier id) { +static ValueDecl *getCreateAsyncTask(ASTContext &ctx, Identifier id) { BuiltinFunctionBuilder builder(ctx); auto genericParam = makeGenericParam().build(builder); - builder.addParameter(makeConcrete(ctx.getIntType())); + builder.addParameter(makeConcrete(ctx.getIntType())); // 0 flags auto extInfo = ASTExtInfoBuilder().withAsync().withThrows().build(); builder.addParameter( - makeConcrete(FunctionType::get({ }, genericParam, extInfo))); + makeConcrete(FunctionType::get({ }, genericParam, extInfo))); // 1 operation builder.setResult(makeConcrete(getAsyncTaskAndContextType(ctx))); return builder.build(id); } -static ValueDecl *getCreateAsyncTaskGroupFuture(ASTContext &ctx, Identifier id) { +static ValueDecl *getCreateAsyncTaskInGroup(ASTContext &ctx, Identifier id) { BuiltinFunctionBuilder builder(ctx); - auto genericParam = makeGenericParam().build(builder); - builder.addParameter(makeConcrete(ctx.getIntType())); // flags - builder.addParameter( - makeConcrete(OptionalType::get(ctx.TheRawPointerType))); // group + auto genericParam = makeGenericParam().build(builder); // + builder.addParameter(makeConcrete(ctx.getIntType())); // 0 flags + builder.addParameter(makeConcrete(ctx.TheRawPointerType)); // 1 group auto extInfo = ASTExtInfoBuilder().withAsync().withThrows().build(); builder.addParameter( - makeConcrete(FunctionType::get({ }, genericParam, extInfo))); + makeConcrete(FunctionType::get({ }, genericParam, extInfo))); // 2 operation builder.setResult(makeConcrete(getAsyncTaskAndContextType(ctx))); + return builder.build(id); } @@ -1462,6 +1462,21 @@ static ValueDecl *getDefaultActorInitDestroy(ASTContext &ctx, _void); } +static ValueDecl *getDistributedActorInitializeRemote(ASTContext &ctx, + Identifier id) { + return getBuiltinFunction(ctx, id, _thin, + _generics(_unrestricted), // TODO(distributed): restrict to DistributedActor + _parameters(_metatype(_typeparam(0))), + _rawPointer); +} + +static ValueDecl *getDistributedActorDestroy(ASTContext &ctx, + Identifier id) { + return getBuiltinFunction(ctx, id, _thin, + _parameters(_nativeObject), + _void); +} + static ValueDecl *getResumeContinuationReturning(ASTContext &ctx, Identifier id) { return getBuiltinFunction(ctx, id, _thin, @@ -1480,11 +1495,6 @@ static ValueDecl *getResumeContinuationThrowing(ASTContext &ctx, } static ValueDecl *getStartAsyncLet(ASTContext &ctx, Identifier id) { -// return getBuiltinFunction(ctx, id, _thin, -// _generics(_unrestricted), -// _parameters(_rawPointer, ), TODO: seems we can't express function here? -// _rawPointer) - ModuleDecl *M = ctx.TheBuiltinModule; DeclContext *DC = &M->getMainFile(FileUnitKind::Builtin); SynthesisContext SC(ctx, DC); @@ -1495,6 +1505,9 @@ static ValueDecl *getStartAsyncLet(ASTContext &ctx, Identifier id) { // AsyncLet* builder.addParameter(makeConcrete(OptionalType::get(ctx.TheRawPointerType))); + // TaskOptionRecord* + builder.addParameter(makeConcrete(OptionalType::get(ctx.TheRawPointerType))); + // operation async function pointer: () async throws -> T auto extInfo = ASTExtInfoBuilder().withAsync().withThrows().withNoEscape().build(); builder.addParameter( @@ -1513,7 +1526,8 @@ static ValueDecl *getEndAsyncLet(ASTContext &ctx, Identifier id) { static ValueDecl *getCreateTaskGroup(ASTContext &ctx, Identifier id) { return getBuiltinFunction(ctx, id, _thin, - _parameters(), + _generics(_unrestricted), + _parameters(_metatype(_typeparam(0))), _rawPointer); } @@ -1838,6 +1852,17 @@ static ValueDecl *getWithUnsafeContinuation(ASTContext &ctx, return builder.build(id); } +static ValueDecl *getHopToActor(ASTContext &ctx, Identifier id) { + BuiltinFunctionBuilder builder(ctx); + auto *actorProto = ctx.getProtocol(KnownProtocolKind::Actor); + // Create type parameters and add conformance constraints. + auto actorParam = makeGenericParam(); + builder.addParameter(actorParam); + builder.addConformanceRequirement(actorParam, actorProto); + builder.setResult(makeConcrete(TupleType::getEmpty(ctx))); + return builder.build(id); +} + /// An array of the overloaded builtin kinds. static const OverloadedBuiltinKind OverloadedBuiltinKinds[] = { OverloadedBuiltinKind::None, @@ -2739,11 +2764,11 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::CancelAsyncTask: return getCancelAsyncTask(Context, Id); - case BuiltinValueKind::CreateAsyncTaskFuture: - return getCreateAsyncTaskFuture(Context, Id); + case BuiltinValueKind::CreateAsyncTask: + return getCreateAsyncTask(Context, Id); - case BuiltinValueKind::CreateAsyncTaskGroupFuture: - return getCreateAsyncTaskGroupFuture(Context, Id); + case BuiltinValueKind::CreateAsyncTaskInGroup: + return getCreateAsyncTaskInGroup(Context, Id); case BuiltinValueKind::ConvertTaskToJob: return getConvertTaskToJob(Context, Id); @@ -2790,10 +2815,18 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::DestroyDefaultActor: return getDefaultActorInitDestroy(Context, Id); + case BuiltinValueKind::InitializeDistributedRemoteActor: + return getDistributedActorInitializeRemote(Context, Id); + + case BuiltinValueKind::DestroyDistributedActor: + return getDistributedActorDestroy(Context, Id); + case BuiltinValueKind::StartAsyncLet: + case BuiltinValueKind::StartAsyncLetWithLocalBuffer: return getStartAsyncLet(Context, Id); case BuiltinValueKind::EndAsyncLet: + case BuiltinValueKind::EndAsyncLetLifetime: return getEndAsyncLet(Context, Id); case BuiltinValueKind::CreateTaskGroup: @@ -2815,6 +2848,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::WithUnsafeThrowingContinuation: return getWithUnsafeContinuation(Context, Id, /*throws=*/true); + case BuiltinValueKind::HopToActor: + return getHopToActor(Context, Id); + case BuiltinValueKind::AutoDiffCreateLinearMapContext: return getAutoDiffCreateLinearMapContext(Context, Id); diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 3f19cfdf0346a..dca9b2f4a351b 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -9,8 +9,6 @@ else() ) endif() -set_swift_llvm_is_available() - add_swift_host_library(swiftAST STATIC AbstractSourceFileDepGraphFactory.cpp AccessNotes.cpp @@ -75,6 +73,15 @@ add_swift_host_library(swiftAST STATIC ProtocolConformance.cpp RawComment.cpp RequirementEnvironment.cpp + RequirementMachine/GenericSignatureQueries.cpp + RequirementMachine/PropertyMap.cpp + RequirementMachine/ProtocolGraph.cpp + RequirementMachine/RequirementMachine.cpp + RequirementMachine/RewriteContext.cpp + RequirementMachine/RewriteSystem.cpp + RequirementMachine/RewriteSystemCompletion.cpp + RequirementMachine/Symbol.cpp + RequirementMachine/Term.cpp SILLayout.cpp Stmt.cpp SubstitutionMap.cpp @@ -146,3 +153,5 @@ endif() # headers. # For more information see the comment at the top of lib/CMakeLists.txt. add_dependencies(swiftAST intrinsics_gen clang-tablegen-targets) + +set_swift_llvm_is_available(swiftAST) diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 57429daf26d1d..6c72c3012b31a 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -54,13 +54,6 @@ ProtocolDecl *ConformanceLookupTable::ConformanceEntry::getProtocol() const { return Conformance.get()->getProtocol(); } -void *ConformanceLookupTable::ConformanceEntry::operator new( - size_t Bytes, - ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - void ConformanceLookupTable::ConformanceEntry::markSupersededBy( ConformanceLookupTable &table, ConformanceEntry *entry, @@ -122,12 +115,6 @@ void ConformanceLookupTable::ConformanceEntry::dump(raw_ostream &os, os << ")\n"; } -void *ConformanceLookupTable::operator new(size_t Bytes, - ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - ConformanceLookupTable::ConformanceLookupTable(ASTContext &ctx) { // Register a cleanup with the ASTContext to call the conformance // table destructor. @@ -141,7 +128,17 @@ void ConformanceLookupTable::destroy() { } namespace { - using ConformanceConstructionInfo = Located; + struct ConformanceConstructionInfo : public Located { + /// The location of the "unchecked" attribute, if this + const SourceLoc uncheckedLoc; + + ConformanceConstructionInfo() { } + + ConformanceConstructionInfo( + ProtocolDecl *item, SourceLoc loc, + SourceLoc uncheckedLoc + ) : Located(item, loc), uncheckedLoc(uncheckedLoc) { } + }; } template @@ -195,14 +192,14 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, loader.first->loadAllConformances(next, loader.second, conformances); loadAllConformances(next, conformances); for (auto conf : conformances) { - protocols.push_back({conf->getProtocol(), SourceLoc()}); + protocols.push_back({conf->getProtocol(), SourceLoc(), SourceLoc()}); } } else if (next->getParentSourceFile()) { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(next, anyObject)) { if (auto proto = dyn_cast(found.Item)) - protocols.push_back({proto, found.Loc}); + protocols.push_back({proto, found.Loc, found.uncheckedLoc}); } } @@ -282,7 +279,9 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); for (auto locAndProto : protos) - addProtocol(locAndProto.Item, locAndProto.Loc, source); + addProtocol( + locAndProto.Item, locAndProto.Loc, + source.withUncheckedLoc(locAndProto.uncheckedLoc)); }); break; @@ -471,8 +470,10 @@ void ConformanceLookupTable::addInheritedProtocols( bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto proto = dyn_cast(found.Item)) - addProtocol(proto, found.Loc, source); + if (auto proto = dyn_cast(found.Item)) { + addProtocol( + proto, found.Loc, source.withUncheckedLoc(found.uncheckedLoc)); + } } } @@ -529,6 +530,18 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances( ConformanceEntry *lhs, ConformanceEntry *rhs, bool &diagnoseSuperseded) { + // If only one of the conformances is unconditionally available on the + // current deployment target, pick that one. + // + // FIXME: Conformance lookup should really depend on source location for + // this to be 100% correct. + if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() != + rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) { + return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() + ? Ordering::Before + : Ordering::After); + } + // If one entry is fixed and the other is not, we have our answer. if (lhs->isFixed() != rhs->isFixed()) { // If the non-fixed conformance is not replaceable, we have a failure to @@ -772,7 +785,30 @@ DeclContext *ConformanceLookupTable::getConformingContext( // Grab the superclass entry and continue searching for a // non-inherited conformance. // FIXME: Ambiguity detection and resolution. - entry = superclassDecl->ConformanceTable->Conformances[protocol].front(); + const auto &superclassConformances = + superclassDecl->ConformanceTable->Conformances[protocol]; + if (superclassConformances.empty()) { + assert(protocol->isSpecificProtocol(KnownProtocolKind::Sendable)); + + // Go dig for a superclass that does conform to Sendable. + // FIXME: This is a hack because the inherited conformances aren't + // getting updated properly. + Type classTy = nominal->getDeclaredInterfaceType(); + ModuleDecl *module = nominal->getParentModule(); + do { + Type superclassTy = classTy->getSuperclassForDecl(superclassDecl); + if (superclassTy->is()) + return nullptr; + auto inheritedConformance = module->lookupConformance( + superclassTy, protocol); + if (inheritedConformance) + return superclassDecl; + } while ((superclassDecl = superclassDecl->getSuperclassDecl())); + + return nullptr; + } + + entry = superclassConformances.front(); nominal = superclassDecl; } @@ -831,7 +867,8 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal, auto normalConf = ctx.getConformance(conformingType, protocol, conformanceLoc, - conformingDC, ProtocolConformanceState::Incomplete); + conformingDC, ProtocolConformanceState::Incomplete, + entry->Source.getUncheckedLoc().isValid()); // Invalid code may cause the getConformance call below to loop, so break // the infinite recursion by setting this eagerly to shortcircuit with the // early return at the start of this function. @@ -923,7 +960,6 @@ void ConformanceLookupTable::registerProtocolConformance( } bool ConformanceLookupTable::lookupConformance( - ModuleDecl *module, NominalTypeDecl *nominal, ProtocolDecl *protocol, SmallVectorImpl &conformances) { diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index 47e50b310d90d..5f1fbeae79253 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -39,7 +39,7 @@ class ModuleDecl; /// This table is a lower-level detail that clients should generally not /// access directly. Rather, one should use the protocol- and /// conformance-centric entry points in \c NominalTypeDecl and \c DeclContext. -class ConformanceLookupTable { +class ConformanceLookupTable : public ASTAllocated { /// Describes the stage at which a particular nominal type or /// extension's conformances has been processed. enum class ConformanceStage : uint8_t { @@ -85,6 +85,9 @@ class ConformanceLookupTable { class ConformanceSource { llvm::PointerIntPair Storage; + /// The location of the "unchecked" attribute, if there is one. + SourceLoc uncheckedLoc; + ConformanceSource(void *ptr, ConformanceEntryKind kind) : Storage(ptr, kind) { } @@ -123,6 +126,14 @@ class ConformanceLookupTable { return ConformanceSource(typeDecl, ConformanceEntryKind::Synthesized); } + /// Return a new conformance source with the given location of "@unchecked". + ConformanceSource withUncheckedLoc(SourceLoc uncheckedLoc) { + ConformanceSource result(*this); + if (uncheckedLoc.isValid()) + result.uncheckedLoc = uncheckedLoc; + return result; + } + /// Retrieve the kind of conformance formed from this source. ConformanceEntryKind getKind() const { return Storage.getInt(); } @@ -149,6 +160,11 @@ class ConformanceLookupTable { llvm_unreachable("Unhandled ConformanceEntryKind in switch."); } + /// The location of the @unchecked attribute, if any. + SourceLoc getUncheckedLoc() const { + return uncheckedLoc; + } + /// For an inherited conformance, retrieve the class declaration /// for the inheriting class. ClassDecl *getInheritingClass() const { @@ -183,7 +199,7 @@ class ConformanceLookupTable { }; /// An entry in the conformance table. - struct ConformanceEntry { + struct ConformanceEntry : public ASTAllocated { /// The source location within the current context where the /// protocol conformance was specified. SourceLoc Loc; @@ -284,11 +300,6 @@ class ConformanceLookupTable { return Loc; } - // Only allow allocation of conformance entries using the - // allocator in ASTContext. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(ConformanceEntry)); - SWIFT_DEBUG_DUMP; void dump(raw_ostream &os, unsigned indent = 0) const; }; @@ -429,8 +440,7 @@ class ConformanceLookupTable { /// conformances found for this protocol and nominal type. /// /// \returns true if any conformances were found. - bool lookupConformance(ModuleDecl *module, - NominalTypeDecl *nominal, + bool lookupConformance(NominalTypeDecl *nominal, ProtocolDecl *protocol, SmallVectorImpl &conformances); @@ -464,16 +474,6 @@ class ConformanceLookupTable { NominalTypeDecl *nominal, bool sorted); - // Only allow allocation of conformance lookup tables using the - // allocator in ASTContext or by doing a placement new. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(ConformanceLookupTable)); - - void *operator new(size_t Bytes, void *Mem) { - assert(Mem); - return Mem; - } - SWIFT_DEBUG_DUMP; void dump(raw_ostream &os) const; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 37025501fc9fd..1651d85758dbb 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -134,18 +134,6 @@ void ClangNode::dump() const { llvm::errs() << "ClangNode contains nullptr\n"; } -// Only allow allocation of Decls using the allocator in ASTContext. -void *Decl::operator new(size_t Bytes, const ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - -// Only allow allocation of Modules using the allocator in ASTContext. -void *ModuleDecl::operator new(size_t Bytes, const ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - StringRef Decl::getKindName(DeclKind K) { switch (K) { #define DECL(Id, Parent) case DeclKind::Id: return #Id; @@ -636,9 +624,18 @@ const ExternalSourceLocs *Decl::getSerializedLocs() const { return &NullLocs; } + CharSourceRange BufferRange = SM.getRangeForBuffer(BufferID); auto ResolveLoc = [&](const ExternalSourceLocs::RawLoc &Raw) -> SourceLoc { + // If the underlying source has been updated and the swiftsourceinfo hasn't, + // make sure we don't produce invalid source locations. Ideally would check + // the file hasn't been modified. + if (Raw.Offset > BufferRange.getByteLength()) + return SourceLoc(); + // If the decl had a presumed loc, create its virtual file so that - // getPresumedLineAndColForLoc works from serialized locations as well. + // getPresumedLineAndColForLoc works from serialized locations as well. No + // need to check the buffer range, the directive must be before the location + // itself. if (Raw.Directive.isValid()) { auto &LD = Raw.Directive; SourceLoc Loc = SM.getLocForOffset(BufferID, LD.Offset); @@ -880,6 +877,9 @@ AvailabilityContext Decl::getAvailabilityForLinkage() const { if (auto *accessor = dyn_cast(this)) return accessor->getStorage()->getAvailabilityForLinkage(); + if (auto *opaqueTypeDecl = dyn_cast(this)) + return opaqueTypeDecl->getNamingDecl()->getAvailabilityForLinkage(); + if (auto *ext = dyn_cast(this)) if (auto *nominal = ext->getExtendedNominal()) return nominal->getAvailabilityForLinkage(); @@ -905,6 +905,9 @@ bool Decl::isAlwaysWeakImported() const { if (auto *accessor = dyn_cast(this)) return accessor->getStorage()->isAlwaysWeakImported(); + if (auto *opaqueTypeDecl = dyn_cast(this)) + return opaqueTypeDecl->getNamingDecl()->isAlwaysWeakImported(); + if (auto *ext = dyn_cast(this)) if (auto *nominal = ext->getExtendedNominal()) return nominal->isAlwaysWeakImported(); @@ -950,18 +953,12 @@ GenericContext::GenericContext(DeclContextKind Kind, DeclContext *Parent, TypeArrayView GenericContext::getInnermostGenericParamTypes() const { - if (auto sig = getGenericSignature()) - return sig->getInnermostGenericParams(); - else - return { }; + return getGenericSignature().getInnermostGenericParams(); } /// Retrieve the generic requirements. ArrayRef GenericContext::getGenericRequirements() const { - if (auto sig = getGenericSignature()) - return sig->getRequirements(); - else - return { }; + return getGenericSignature().getRequirements(); } GenericParamList *GenericContext::getGenericParams() const { @@ -992,10 +989,7 @@ GenericSignature GenericContext::getGenericSignature() const { } GenericEnvironment *GenericContext::getGenericEnvironment() const { - if (auto genericSig = getGenericSignature()) - return genericSig->getGenericEnvironment(); - - return nullptr; + return getGenericSignature().getGenericEnvironment(); } void GenericContext::setGenericSignature(GenericSignature genericSig) { @@ -1148,9 +1142,15 @@ NominalTypeDecl::takeConformanceLoaderSlow() { return { contextInfo->loader, contextInfo->allConformancesData }; } +InheritedEntry::InheritedEntry(const TypeLoc &typeLoc) + : TypeLoc(typeLoc), isUnchecked(false) { + if (auto typeRepr = typeLoc.getTypeRepr()) + isUnchecked = typeRepr->findUncheckedAttrLoc().isValid(); +} + ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc, TypeRepr *extendedType, - ArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause) : GenericContext(DeclContextKind::ExtensionDecl, parent, nullptr), @@ -1167,7 +1167,7 @@ ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc, ExtensionDecl *ExtensionDecl::create(ASTContext &ctx, SourceLoc extensionLoc, TypeRepr *extendedType, - ArrayRef inherited, + ArrayRef inherited, DeclContext *parent, TrailingWhereClause *trailingWhereClause, ClangNode clangNode) { @@ -1341,6 +1341,17 @@ PatternBindingDecl *PatternBindingDecl::createImplicit( return Result; } +PatternBindingDecl *PatternBindingDecl::createForDebugger( + ASTContext &Ctx, StaticSpellingKind StaticSpelling, Pattern *Pat, Expr *E, + DeclContext *Parent) { + auto *Result = createImplicit(Ctx, StaticSpelling, Pat, E, Parent); + Result->Bits.PatternBindingDecl.IsDebugger = true; + for (auto &entry : Result->getMutablePatternList()) { + entry.setFromDebugger(); + } + return Result; +} + PatternBindingDecl * PatternBindingDecl::create(ASTContext &Ctx, SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, @@ -1409,6 +1420,15 @@ ParamDecl *PatternBindingInitializer::getImplicitSelfDecl() const { LazySelfParam->setImplicit(); LazySelfParam->setSpecifier(specifier); LazySelfParam->setInterfaceType(DC->getSelfInterfaceType()); + + // Lazy members of actors have an isolated 'self', assuming there is + // no "nonisolated" attribute. + if (auto nominal = DC->getSelfNominalTypeDecl()) { + if (nominal->isActor() && + !singleVar->getAttrs().hasAttribute()) + LazySelfParam->setIsolated(); + } + mutableThis->SelfParam = LazySelfParam; } } @@ -1541,7 +1561,7 @@ SourceRange PatternBindingEntry::getSourceRange(bool omitAccessors) const { bool PatternBindingEntry::hasInitStringRepresentation() const { if (InitContextAndFlags.getInt().contains(PatternFlags::IsText)) return !InitStringRepresentation.empty(); - return getInit() && getInit()->getSourceRange().isValid(); + return getOriginalInit() && getOriginalInit()->getSourceRange().isValid(); } StringRef PatternBindingEntry::getInitStringRepresentation( @@ -1566,10 +1586,12 @@ SourceRange PatternBindingDecl::getSourceRange() const { } static StaticSpellingKind getCorrectStaticSpellingForDecl(const Decl *D) { - if (!D->getDeclContext()->getSelfClassDecl()) - return StaticSpellingKind::KeywordStatic; + if (auto classDecl = D->getDeclContext()->getSelfClassDecl()) { + if (!classDecl->isActor()) + return StaticSpellingKind::KeywordClass; + } - return StaticSpellingKind::KeywordClass; + return StaticSpellingKind::KeywordStatic; } StaticSpellingKind PatternBindingDecl::getCorrectStaticSpelling() const { @@ -1581,9 +1603,9 @@ StaticSpellingKind PatternBindingDecl::getCorrectStaticSpelling() const { return getCorrectStaticSpellingForDecl(this); } -bool PatternBindingDecl::isSpawnLet() const { +bool PatternBindingDecl::isAsyncLet() const { if (auto var = getAnchoringVarDecl(0)) - return var->isSpawnLet(); + return var->isAsyncLet(); return false; } @@ -2454,6 +2476,10 @@ bool swift::conflicting(const OverloadSignature& sig1, if (sig1.IsInstanceMember != sig2.IsInstanceMember) return false; + // If one is an async function and the other is not, they can't conflict. + if (sig1.IsAsyncFunction != sig2.IsAsyncFunction) + return false; + // If one is a compound name and the other is not, they do not conflict // if one is a property and the other is a non-nullary function. if (sig1.Name.isCompoundName() != sig2.Name.isCompoundName()) { @@ -2641,8 +2667,10 @@ static Type mapSignatureFunctionType(ASTContext &ctx, Type type, for (const auto ¶m : funcTy->getParams()) { auto newParamType = mapSignatureParamType(ctx, param.getPlainType()); - // Don't allow overloading by @_nonEphemeral. - auto newFlags = param.getParameterFlags().withNonEphemeral(false); + // Don't allow overloading by @_nonEphemeral or isolated. + auto newFlags = param.getParameterFlags() + .withNonEphemeral(false) + .withIsolated(false); // For the 'self' of a method, strip off 'inout'. if (isMethod) { @@ -2680,7 +2708,6 @@ OverloadSignature ValueDecl::getOverloadSignature() const { = static_cast(getDeclContext()->getExtendedProtocolDecl()); signature.IsInstanceMember = isInstanceMember(); signature.IsVariable = isa(this); - signature.IsFunction = isa(this); signature.IsEnumElement = isa(this); signature.IsNominal = isa(this); signature.IsTypeAlias = isa(this); @@ -2694,6 +2721,13 @@ OverloadSignature ValueDecl::getOverloadSignature() const { } } + // Functions include async/not-async. + if (auto func = dyn_cast(this)) { + signature.IsFunction = true; + if (func->hasAsync()) + signature.IsAsyncFunction = true; + } + if (auto *extension = dyn_cast(getDeclContext())) if (extension->isGeneric()) signature.InExtensionOfGenericType = true; @@ -2763,7 +2797,7 @@ void ValueDecl::setOverriddenDecls(ArrayRef overridden) { request.cacheResult(overriddenVec); } -OpaqueReturnTypeRepr *ValueDecl::getOpaqueResultTypeRepr() const { +TypeRepr *ValueDecl::getOpaqueResultTypeRepr() const { TypeRepr *returnRepr = nullptr; if (auto *VD = dyn_cast(this)) { if (auto *P = VD->getParentPattern()) { @@ -2788,7 +2822,11 @@ OpaqueReturnTypeRepr *ValueDecl::getOpaqueResultTypeRepr() const { returnRepr = SD->getElementTypeRepr(); } - return dyn_cast_or_null(returnRepr); + if (returnRepr && returnRepr->hasOpaque()) { + return returnRepr; + } else { + return nullptr; + } } OpaqueTypeDecl *ValueDecl::getOpaqueResultTypeDecl() const { @@ -2829,6 +2867,23 @@ void ValueDecl::setIsObjC(bool value) { LazySemanticInfo.isObjC = value; } +bool ValueDecl::isSemanticallyFinal() const { + // Actor types are semantically final. + if (auto classDecl = dyn_cast(this)) { + if (classDecl->isActor()) + return true; + } + + // As are members of actor types. + if (auto classDecl = getDeclContext()->getSelfClassDecl()) { + if (classDecl->isActor()) + return true; + } + + // For everything else, the same as 'final'. + return isFinal(); +} + bool ValueDecl::isFinal() const { return evaluateOrDefault(getASTContext().evaluator, IsFinalRequest { const_cast(this) }, @@ -2842,6 +2897,10 @@ bool ValueDecl::isDynamic() const { getAttrs().hasAttribute()); } +bool ValueDecl::isDistributedActorIndependent() const { + return getAttrs().hasAttribute(); +} + bool ValueDecl::isObjCDynamicInGenericClass() const { if (!isObjCDynamic()) return false; @@ -2851,7 +2910,8 @@ bool ValueDecl::isObjCDynamicInGenericClass() const { if (!classDecl) return false; - return classDecl->isGenericContext() && !classDecl->usesObjCGenericsModel(); + return classDecl->isGenericContext() + && !classDecl->isTypeErasedGenericClass(); } bool ValueDecl::shouldUseObjCMethodReplacement() const { @@ -3061,7 +3121,7 @@ bool ValueDecl::canInferObjCFromRequirement(ValueDecl *requirement) { // If the nominal type doesn't conform to the protocol at all, we // cannot infer @objc no matter what we do. SmallVector conformances; - if (!nominal->lookupConformance(getModuleContext(), proto, conformances)) + if (!nominal->lookupConformance(proto, conformances)) return false; // If any of the conformances is attributed to the context in which @@ -3169,13 +3229,13 @@ bool ValueDecl::shouldHideFromEditor() const { static AccessLevel getMaximallyOpenAccessFor(const ValueDecl *decl) { // Non-final classes are considered open to @testable importers. if (auto cls = dyn_cast(decl)) { - if (!cls->isFinal()) + if (!cls->isSemanticallyFinal()) return AccessLevel::Open; // Non-final overridable class members are considered open to // @testable importers. } else if (decl->isPotentiallyOverridable()) { - if (!cast(decl)->isFinal()) + if (!cast(decl)->isSemanticallyFinal()) return AccessLevel::Open; } @@ -3289,9 +3349,6 @@ AccessLevel ValueDecl::getFormalAccess() const { } bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const { - assert(isa(this) || isa(this) || - isPotentiallyOverridable()); - AccessLevel access = getAdjustedFormalAccess(this, useDC, /*treatUsableFromInlineAsPublic*/false); @@ -3823,10 +3880,20 @@ bool NominalTypeDecl::isActor() const { false); } +bool NominalTypeDecl::isDistributedActor() const { + auto mutableThis = const_cast(this); + return evaluateOrDefault(getASTContext().evaluator, + IsDistributedActorRequest{mutableThis}, + false); +} + +bool NominalTypeDecl::isAnyActor() const { + return isActor() || isDistributedActor(); +} GenericTypeDecl::GenericTypeDecl(DeclKind K, DeclContext *DC, Identifier name, SourceLoc nameLoc, - ArrayRef inherited, + ArrayRef inherited, GenericParamList *GenericParams) : GenericContext(DeclContextKind::GenericTypeDecl, DC, GenericParams), TypeDecl(K, DC, name, nameLoc, inherited) {} @@ -4036,7 +4103,7 @@ AssociatedTypeDecl *AssociatedTypeDecl::getAssociatedTypeAnchor() const { EnumDecl::EnumDecl(SourceLoc EnumLoc, Identifier Name, SourceLoc NameLoc, - ArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Enum, Parent, Name, NameLoc, Inherited, GenericParams), @@ -4060,7 +4127,7 @@ void EnumDecl::setRawType(Type rawType) { } StructDecl::StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, - ArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Struct, Parent, Name, NameLoc, Inherited, GenericParams), @@ -4115,7 +4182,7 @@ bool NominalTypeDecl::isTypeErasedGenericClass() const { // ObjC classes are type erased. // TODO: Unless they have magic methods... if (auto clas = dyn_cast(this)) - return clas->hasClangNode() && clas->isGenericContext(); + return clas->isTypeErasedGenericClass(); return false; } @@ -4151,16 +4218,25 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { } } else { auto argumentNames = member.getArgumentNames(); - if (!member.isCompoundName() || argumentNames.size() == 1) { - if (baseName == DeclBaseName::createConstructor() && - (member.isSimpleName() || argumentNames.front() == Context.Id_from)) { - action.emplace(ImplicitMemberAction::ResolveDecodable); + if (member.isSimpleName() || argumentNames.size() == 1) { + if (baseName == DeclBaseName::createConstructor()) { + if ((member.isSimpleName() || argumentNames.front() == Context.Id_from)) { + action.emplace(ImplicitMemberAction::ResolveDecodable); + } else if (argumentNames.front() == Context.Id_transport) { + action.emplace(ImplicitMemberAction::ResolveDistributedActorTransport); + } } else if (!baseName.isSpecial() && - baseName.getIdentifier() == Context.Id_encode && - (member.isSimpleName() || - argumentNames.front() == Context.Id_to)) { + baseName.getIdentifier() == Context.Id_encode && + (member.isSimpleName() || argumentNames.front() == Context.Id_to)) { action.emplace(ImplicitMemberAction::ResolveEncodable); } + } else if (member.isSimpleName() || argumentNames.size() == 2) { + if (baseName == DeclBaseName::createConstructor()) { + if (argumentNames[0] == Context.Id_resolve && + argumentNames[1] == Context.Id_using) { + action.emplace(ImplicitMemberAction::ResolveDistributedActor); + } + } } } @@ -4178,7 +4254,7 @@ VarDecl *NominalTypeDecl::getGlobalActorInstance() const { } ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, - ArrayRef Inherited, + ArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent, bool isActor) : NominalTypeDecl(DeclKind::Class, Parent, Name, NameLoc, Inherited, @@ -4683,7 +4759,7 @@ bool EnumDecl::hasCircularRawValue() const { ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc, Identifier Name, - ArrayRef Inherited, + ArrayRef Inherited, TrailingWhereClause *TrailingWhere) : NominalTypeDecl(DeclKind::Protocol, DC, Name, NameLoc, Inherited, nullptr), @@ -4696,13 +4772,20 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, Bits.ProtocolDecl.NumRequirementsInSignature = 0; Bits.ProtocolDecl.HasMissingRequirements = false; Bits.ProtocolDecl.KnownProtocol = 0; - setTrailingWhereClause(TrailingWhere); + Bits.ProtocolDecl.HasAssociatedTypes = 0; + Bits.ProtocolDecl.HasLazyAssociatedTypes = 0; + setTrailingWhereClause(TrailingWhere); } bool ProtocolDecl::isMarkerProtocol() const { return getAttrs().hasAttribute(); } +bool ProtocolDecl::inheritsFromDistributedActor() const { + auto &C = getASTContext(); + return inheritsFrom(C.getDistributedActorDecl()); +} + ArrayRef ProtocolDecl::getInheritedProtocols() const { auto *mutThis = const_cast(this); return evaluateOrDefault(getASTContext().evaluator, @@ -4710,26 +4793,40 @@ ArrayRef ProtocolDecl::getInheritedProtocols() const { {}); } -llvm::TinyPtrVector +ArrayRef ProtocolDecl::getAssociatedTypeMembers() const { - llvm::TinyPtrVector result; + if (Bits.ProtocolDecl.HasAssociatedTypes) + return AssociatedTypes; + + auto *self = const_cast(this); + self->Bits.ProtocolDecl.HasAssociatedTypes = 1; // Clang-imported protocols never have associated types. if (hasClangNode()) - return result; + return ArrayRef(); // Deserialized @objc protocols never have associated types. - if (!getParentSourceFile() && isObjC()) - return result; + if (getParentSourceFile() == nullptr && isObjC()) + return ArrayRef(); - // Find the associated type declarations. - for (auto member : getMembers()) { - if (auto ATD = dyn_cast(member)) { - result.push_back(ATD); + SmallVector result; + if (Bits.ProtocolDecl.HasLazyAssociatedTypes) { + auto &ctx = getASTContext(); + auto contextData = static_cast( + ctx.getOrCreateLazyContextData(this, nullptr)); + + contextData->loader->loadAssociatedTypes( + this, contextData->associatedTypesData, result); + } else { + for (auto member : getMembers()) { + if (auto ATD = dyn_cast(member)) { + result.push_back(ATD); + } } } - return result; + self->AssociatedTypes = getASTContext().AllocateCopy(result); + return AssociatedTypes; } ValueDecl *ProtocolDecl::getSingleRequirement(DeclName name) const { @@ -4750,8 +4847,7 @@ ValueDecl *ProtocolDecl::getSingleRequirement(DeclName name) const { } AssociatedTypeDecl *ProtocolDecl::getAssociatedType(Identifier name) const { - const auto flags = NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; - auto results = const_cast(this)->lookupDirect(name, flags); + auto results = const_cast(this)->lookupDirect(name); for (auto candidate : results) { if (candidate->getDeclContext() == this && isa(candidate)) { @@ -4912,24 +5008,31 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type, return findProtocolSelfReferences(proto, selfType->getSelfType(), position); } - // Most bound generic types are invariant. - if (auto *const bgt = type->getAs()) { + if (auto *const nominal = type->getAs()) { auto info = SelfReferenceInfo(); - if (bgt->isArray()) { - // Swift.Array preserves variance in its Value type. - info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(), - position); - } else if (bgt->isDictionary()) { - // Swift.Dictionary preserves variance in its Element type. - info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(), - SelfReferencePosition::Invariant); - info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().back(), - position); - } else { - for (auto paramType : bgt->getGenericArgs()) { - info |= findProtocolSelfReferences(proto, paramType, + // Don't forget to look in the parent. + if (const auto parent = nominal->getParent()) { + info |= findProtocolSelfReferences(proto, parent, position); + } + + // Most bound generic types are invariant. + if (auto *const bgt = type->getAs()) { + if (bgt->isArray()) { + // Swift.Array preserves variance in its Value type. + info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(), + position); + } else if (bgt->isDictionary()) { + // Swift.Dictionary preserves variance in its Element type. + info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(), SelfReferencePosition::Invariant); + info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().back(), + position); + } else { + for (auto paramType : bgt->getGenericArgs()) { + info |= findProtocolSelfReferences(proto, paramType, + SelfReferencePosition::Invariant); + } } } @@ -4941,6 +5044,16 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type, if (type->is()) return SelfReferenceInfo::forSelfRef(SelfReferencePosition::Invariant); + // Protocol compositions preserve variance. + if (auto *comp = type->getAs()) { + // 'Self' may be referenced only in a superclass component. + if (const auto superclass = comp->getSuperclass()) { + return findProtocolSelfReferences(proto, superclass, position); + } + + return SelfReferenceInfo(); + } + // A direct reference to 'Self'. if (proto->getSelfInterfaceType()->isEqual(type)) return SelfReferenceInfo::forSelfRef(position); @@ -5040,11 +5153,6 @@ bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const { return true; } -bool ProtocolDecl::existentialTypeSupported() const { - return evaluateOrDefault(getASTContext().evaluator, - ExistentialTypeSupportedRequest{const_cast(this)}, true); -} - StringRef ProtocolDecl::getObjCRuntimeName( llvm::SmallVectorImpl &buffer) const { // If there is an 'objc' attribute with a name, use that name. @@ -5095,6 +5203,18 @@ ProtocolDecl::setLazyRequirementSignature(LazyMemberLoader *lazyLoader, ++Stats->getFrontendCounters().NumLazyRequirementSignatures; } +void +ProtocolDecl::setLazyAssociatedTypeMembers(LazyMemberLoader *lazyLoader, + uint64_t associatedTypesData) { + assert(!Bits.ProtocolDecl.HasAssociatedTypes); + assert(!Bits.ProtocolDecl.HasLazyAssociatedTypes); + + auto contextData = static_cast( + getASTContext().getOrCreateLazyContextData(this, lazyLoader)); + contextData->associatedTypesData = associatedTypesData; + Bits.ProtocolDecl.HasLazyAssociatedTypes = true; +} + ArrayRef ProtocolDecl::getCachedRequirementSignature() const { assert(RequirementSignature && "getting requirement signature before computing it"); @@ -5107,7 +5227,8 @@ void ProtocolDecl::computeKnownProtocolKind() const { if (module != module->getASTContext().getStdlibModule() && !module->getName().is("Foundation") && !module->getName().is("_Differentiation") && - !module->getName().is("_Concurrency")) { + !module->getName().is("_Concurrency") && + !module->getName().is("_Distributed")) { const_cast(this)->Bits.ProtocolDecl.KnownProtocol = 1; return; } @@ -5155,6 +5276,8 @@ Optional return KnownDerivableProtocolKind::Differentiable; case KnownProtocolKind::Actor: return KnownDerivableProtocolKind::Actor; + case KnownProtocolKind::DistributedActor: + return KnownDerivableProtocolKind::DistributedActor; default: return None; } } @@ -5197,6 +5320,10 @@ bool AbstractStorageDecl::hasAnyNativeDynamicAccessors() const { return false; } +bool AbstractStorageDecl::isDistributedActorIndependent() const { + return getAttrs().hasAttribute(); +} + void AbstractStorageDecl::setAccessors(SourceLoc lbraceLoc, ArrayRef accessors, SourceLoc rbraceLoc) { @@ -5740,11 +5867,24 @@ void VarDecl::setNamingPattern(NamedPattern *Pat) { } TypeRepr *VarDecl::getTypeReprOrParentPatternTypeRepr() const { - if (auto *param = dyn_cast(this)) - return param->getTypeRepr(); - - if (auto *parentPattern = dyn_cast_or_null(getParentPattern())) - return parentPattern->getTypeRepr(); + if (auto *Param = dyn_cast(this)) + return Param->getTypeRepr(); + + auto *ParentPattern = getParentPattern(); + + if (auto *TyPattern = dyn_cast_or_null(ParentPattern)) + return TyPattern->getTypeRepr(); + + // Handle typed if/guard/while-let as a special case (e.g. `if let x: Int + // = Optional.some(4)`), since the `TypedPattern` is not the top-level + // pattern here - instead it is an implicit `OptionalSomePattern` + if (auto *SomePattern = + dyn_cast_or_null(ParentPattern)) { + if (auto *TyPattern = + dyn_cast(SomePattern->getSubPattern())) { + return TyPattern->getTypeRepr(); + } + } return nullptr; } @@ -5877,8 +6017,8 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const { return true; } -bool VarDecl::isSpawnLet() const { - return getAttrs().hasAttribute() || getAttrs().hasAttribute(); +bool VarDecl::isAsyncLet() const { + return getAttrs().hasAttribute(); } void ParamDecl::setSpecifier(Specifier specifier) { @@ -5936,17 +6076,33 @@ llvm::TinyPtrVector VarDecl::getAttachedPropertyWrappers() const { /// Whether this property has any attached property wrappers. bool VarDecl::hasAttachedPropertyWrapper() const { - return !getAttachedPropertyWrappers().empty() || hasImplicitPropertyWrapper(); + if (getAttrs().hasAttribute()) { + if (!getAttachedPropertyWrappers().empty()) + return true; + } + + if (hasImplicitPropertyWrapper()) + return true; + + return false; } bool VarDecl::hasImplicitPropertyWrapper() const { - if (!getAttachedPropertyWrappers().empty()) + if (getAttrs().hasAttribute()) { + if (!getAttachedPropertyWrappers().empty()) + return false; + } + + if (isImplicit()) return false; - auto *dc = getDeclContext(); - bool isClosureParam = isa(this) && - dc->getContextKind() == DeclContextKind::AbstractClosureExpr; - return !isImplicit() && getName().hasDollarPrefix() && isClosureParam; + if (!isa(this)) + return false; + + if (!isa(getDeclContext())) + return false; + + return getName().hasDollarPrefix(); } bool VarDecl::hasExternalPropertyWrapper() const { @@ -6260,6 +6416,12 @@ ParamDecl *ParamDecl::cloneWithoutType(const ASTContext &Ctx, ParamDecl *PD) { return Clone; } +ParamDecl *ParamDecl::clone(const ASTContext &Ctx, ParamDecl *PD) { + auto *Clone = ParamDecl::cloneWithoutType(Ctx, PD); + Clone->setInterfaceType(PD->getInterfaceType()); + return Clone; +} + /// Retrieve the type of 'self' for the given context. Type DeclContext::getSelfTypeInContext() const { assert(isTypeContext()); @@ -6359,7 +6521,7 @@ void ParamDecl::setNonEphemeralIfPossible() { Type ParamDecl::getVarargBaseTy(Type VarArgT) { TypeBase *T = VarArgT.getPointer(); - if (auto *AT = dyn_cast(T)) + if (auto *AT = dyn_cast(T)) return AT->getBaseType(); if (auto *BGT = dyn_cast(T)) { // It's the stdlib Array. @@ -6386,8 +6548,7 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { auto internalLabel = getParameterName(); auto flags = ParameterTypeFlags::fromParameterType( type, isVariadic(), isAutoClosure(), isNonEphemeral(), - getValueOwnership(), - /*isNoDerivative*/ false); + getValueOwnership(), isIsolated(), /*isNoDerivative*/ false); return AnyFunctionType::Param(type, label, flags, internalLabel); } @@ -6458,9 +6619,12 @@ Expr *ParamDecl::getTypeCheckedDefaultExpr() const { } auto &ctx = getASTContext(); - return evaluateOrDefault( - ctx.evaluator, DefaultArgumentExprRequest{const_cast(this)}, - new (ctx) ErrorExpr(getSourceRange(), ErrorType::get(ctx))); + if (Expr *E = evaluateOrDefault( + ctx.evaluator, + DefaultArgumentExprRequest{const_cast(this)}, nullptr)) { + return E; + } + return new (ctx) ErrorExpr(getSourceRange(), ErrorType::get(ctx)); } void ParamDecl::setDefaultExpr(Expr *E, bool isTypeChecked) { @@ -6914,9 +7078,132 @@ bool AbstractFunctionDecl::hasDynamicSelfResult() const { } AbstractFunctionDecl *AbstractFunctionDecl::getAsyncAlternative() const { - auto mutableFunc = const_cast(this); - return evaluateOrDefault(getASTContext().evaluator, - AsyncAlternativeRequest{mutableFunc}, nullptr); + // Async functions can't have async alternatives + if (hasAsync()) + return nullptr; + + const AvailableAttr *avAttr = nullptr; + for (const auto *attr : getAttrs().getAttributes()) { + // If there's an attribute with an already-resolved rename decl, use it + if (attr->RenameDecl) { + avAttr = attr; + break; + } + + // Otherwise prefer the first availability attribute with no platform and + // rename parameter, falling back to the first with a rename. Note that + // `getAttrs` is in reverse source order, so the last attribute is the + // first in source + if (!attr->Rename.empty() && (attr->Platform == PlatformKind::none || + !avAttr)) + avAttr = attr; + } + + auto *renamedDecl = evaluateOrDefault( + getASTContext().evaluator, RenamedDeclRequest{this, avAttr}, nullptr); + auto *alternative = dyn_cast_or_null(renamedDecl); + if (!alternative || !alternative->hasAsync()) + return nullptr; + return alternative; +} + +static bool isPotentialCompletionHandler(const ParamDecl *param) { + if (!param->getType()) + return false; + + const AnyFunctionType *paramType = param->getType()->getAs(); + return paramType && paramType->getResult()->isVoid() && + !paramType->isNoEscape() && !param->isAutoClosure(); +} + +Optional AbstractFunctionDecl::findPotentialCompletionHandlerParam( + const AbstractFunctionDecl *asyncAlternative) const { + const ParameterList *params = getParameters(); + if (params->size() == 0) + return None; + + // If no async alternative given, just find the last parameter that matches + // a completion handler signature + if (!asyncAlternative) { + for (int i = params->size() - 1; i >= 0; --i) { + if (isPotentialCompletionHandler(params->get(i))) + return i; + } + return None; + } + + // If this is an imported function with an async convention then we already + // have the index, grab it from there + auto asyncConvention = asyncAlternative->getForeignAsyncConvention(); + if (asyncConvention) { + auto errorConvention = asyncAlternative->getForeignErrorConvention(); + unsigned handlerIndex = asyncConvention->completionHandlerParamIndex(); + if (errorConvention && + !errorConvention->isErrorParameterReplacedWithVoid() && + handlerIndex >= errorConvention->getErrorParameterIndex()) { + handlerIndex--; + } + return handlerIndex; + } + + // Otherwise, match up the parameters of each function and return the single + // missing parameter that must also match a completion handler signature. + // Ignore any defaulted params in the alternative if their label is different + // to the corresponding param in the original function. + + const ParameterList *asyncParams = asyncAlternative->getParameters(); + unsigned paramIndex = 0; + unsigned asyncParamIndex = 0; + Optional potentialParam; + while (paramIndex < params->size() || asyncParamIndex < asyncParams->size()) { + if (paramIndex >= params->size()) { + // Have more async params than original params, if we haven't found a + // completion handler then there isn't going to be any. If we have then + // ensure the rest of the async params are defaulted + if (!potentialParam || + !asyncParams->get(asyncParamIndex)->isDefaultArgument()) + return None; + asyncParamIndex++; + continue; + } + + auto *param = params->get(paramIndex); + bool paramMatches = false; + if (asyncParamIndex < asyncParams->size()) { + const ParamDecl *asyncParam = asyncParams->get(asyncParamIndex); + + // Skip if the labels are different and it's defaulted + if (param->getArgumentName() != asyncParam->getArgumentName() && + asyncParam->isDefaultArgument()) { + asyncParamIndex++; + continue; + } + + // Don't have types for some reason, just return no match + if (!param->getType() || !asyncParam->getType()) + return None; + + paramMatches = param->getType()->matchesParameter(asyncParam->getType(), + TypeMatchOptions()); + } + + if (paramMatches) { + paramIndex++; + asyncParamIndex++; + continue; + } + + // Param doesn't match, either it's the first completion handler or these + // functions don't match + if (potentialParam || !isPotentialCompletionHandler(param)) + return None; + + // The next original param should match the current async, so don't + // increment the async index + potentialParam = paramIndex; + paramIndex++; + } + return potentialParam; } bool AbstractFunctionDecl::argumentNameIsAPIByDefault() const { @@ -6941,26 +7228,20 @@ bool AbstractFunctionDecl::isSendable() const { return getAttrs().hasAttribute(); } -bool AbstractFunctionDecl::isAsyncHandler() const { - auto func = dyn_cast(this); - if (!func) - return false; - - auto mutableFunc = const_cast(func); - return evaluateOrDefault(getASTContext().evaluator, - IsAsyncHandlerRequest{mutableFunc}, - false); +bool AbstractFunctionDecl::isDistributed() const { + return this->getAttrs().hasAttribute(); } -bool AbstractFunctionDecl::canBeAsyncHandler() const { - auto func = dyn_cast(this); - if (!func) - return false; +AbstractFunctionDecl* +AbstractFunctionDecl::getDistributedActorRemoteFuncDecl() const { + if (!this->isDistributed()) + return nullptr; - auto mutableFunc = const_cast(func); - return evaluateOrDefault(getASTContext().evaluator, - CanBeAsyncHandlerRequest{mutableFunc}, - false); + auto mutableThis = const_cast(this); + return evaluateOrDefault( + getASTContext().evaluator, + GetDistributedRemoteFuncRequest{mutableThis}, + nullptr); } BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const { @@ -7022,7 +7303,7 @@ void AbstractFunctionDecl::setBodyToBeReparsed(SourceRange bodyRange) { SourceRange AbstractFunctionDecl::getBodySourceRange() const { switch (getBodyKind()) { case BodyKind::None: - case BodyKind::MemberwiseInitializer: + case BodyKind::SILSynthesize: case BodyKind::Deserialized: case BodyKind::Synthesize: return SourceRange(); @@ -7268,16 +7549,16 @@ void AbstractFunctionDecl::setParameters(ParameterList *BodyParams) { } OpaqueTypeDecl::OpaqueTypeDecl(ValueDecl *NamingDecl, - GenericParamList *GenericParams, - DeclContext *DC, + GenericParamList *GenericParams, DeclContext *DC, GenericSignature OpaqueInterfaceGenericSignature, + OpaqueReturnTypeRepr *UnderlyingInterfaceRepr, GenericTypeParamType *UnderlyingInterfaceType) - : GenericTypeDecl(DeclKind::OpaqueType, DC, Identifier(), SourceLoc(), {}, - GenericParams), - NamingDecl(NamingDecl), - OpaqueInterfaceGenericSignature(OpaqueInterfaceGenericSignature), - UnderlyingInterfaceType(UnderlyingInterfaceType) -{ + : GenericTypeDecl(DeclKind::OpaqueType, DC, Identifier(), SourceLoc(), {}, + GenericParams), + NamingDecl(NamingDecl), + OpaqueInterfaceGenericSignature(OpaqueInterfaceGenericSignature), + UnderlyingInterfaceRepr(UnderlyingInterfaceRepr), + UnderlyingInterfaceType(UnderlyingInterfaceType) { // Always implicit. setImplicit(); } @@ -7297,6 +7578,15 @@ bool OpaqueTypeDecl::isOpaqueReturnTypeOfFunction( return false; } +unsigned OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal( + OpaqueReturnTypeRepr *repr) const { + // TODO [OPAQUE SUPPORT]: we will need to generalize here when we allow + // multiple "some" types. + assert(UnderlyingInterfaceRepr && + "can't do opaque param lookup without underlying interface repr"); + return repr == UnderlyingInterfaceRepr ? 0 : -1; +} + Identifier OpaqueTypeDecl::getOpaqueReturnTypeIdentifier() const { assert(getNamingDecl() && "not an opaque return type"); if (!OpaqueReturnTypeIdentifier.empty()) @@ -7328,7 +7618,7 @@ bool AbstractFunctionDecl::hasInlinableBodyText() const { case BodyKind::None: case BodyKind::Synthesize: case BodyKind::Skipped: - case BodyKind::MemberwiseInitializer: + case BodyKind::SILSynthesize: return false; } llvm_unreachable("covered switch"); @@ -7348,14 +7638,8 @@ StringRef AbstractFunctionDecl::getInlinableBodyText( /// A uniqued list of derivative function configurations. struct AbstractFunctionDecl::DerivativeFunctionConfigurationList - : public llvm::SetVector { - // Necessary for `ASTContext` allocation. - void *operator new( - size_t bytes, ASTContext &ctx, - unsigned alignment = alignof(DerivativeFunctionConfigurationList)) { - return ctx.Allocate(bytes, alignment); - } -}; + : public ASTAllocated, + public llvm::SetVector {}; void AbstractFunctionDecl::prepareDerivativeFunctionConfigurations() { if (DerivativeFunctionConfigs) @@ -7631,6 +7915,28 @@ bool AccessorDecl::isSimpleDidSet() const { SimpleDidSetRequest{mutableThis}, false); } +void AccessorDecl::printUserFacingName(raw_ostream &out) const { + switch (getAccessorKind()) { + case AccessorKind::Get: + out << "getter:"; + break; + case AccessorKind::Set: + out << "setter:"; + break; + default: + out << getName(); + return; + } + + out << getStorage()->getName() << "("; + if (this->isSetter()) { + for (const auto *param : *getParameters()) { + out << param->getName() << ":"; + } + } + out << ")"; +} + StaticSpellingKind FuncDecl::getCorrectStaticSpelling() const { assert(getDeclContext()->isTypeContext()); if (!isStatic()) @@ -7741,6 +8047,7 @@ bool ConstructorDecl::isObjCZeroParameterWithLongSelector() const { return params->get(0)->getInterfaceType()->isVoid(); } + DestructorDecl::DestructorDecl(SourceLoc DestructorLoc, DeclContext *Parent) : AbstractFunctionDecl(DeclKind::Destructor, Parent, DeclBaseName::createDestructor(), DestructorLoc, @@ -8136,15 +8443,19 @@ bool ClassDecl::isRootDefaultActor(ModuleDecl *M, } bool ClassDecl::isNativeNSObjectSubclass() const { - // Only if we inherit from NSObject. - auto superclass = getSuperclassDecl(); - if (!superclass || !superclass->isNSObject()) - return false; + // @objc actors implicitly inherit from NSObject. + if (isActor()) { + if (getAttrs().hasAttribute()) { + return true; + } + ClassDecl *superclass = getSuperclassDecl(); + return superclass && superclass->isNSObject(); + } - // For now, only actors (regardless of whether they're default actors). - // Eventually we should roll this out to more classes, but we have to - // do it with ABI compatibility. - return isActor(); + // For now, non-actor classes cannot use the native NSObject subclass. + // Eventually we should roll this out to more classes that directly + // inherit NSObject, but we have to do it with ABI compatibility. + return false; } bool ClassDecl::isNSObject() const { @@ -8208,6 +8519,7 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) { auto selfDecl = isolation.getActorInstance(); auto actorClass = selfDecl->getType()->getRValueType() ->getClassOrBoundGenericClass(); + // FIXME: Doesn't work properly with generics assert(actorClass && "Bad closure actor isolation?"); return ActorIsolation::forActorInstance(actorClass); } @@ -8348,7 +8660,7 @@ void swift::simple_display(llvm::raw_ostream &out, AccessorKind kind) { } SourceLoc swift::extractNearestSourceLoc(const Decl *decl) { - auto loc = decl->getLoc(); + auto loc = decl->getLoc(/*SerializedOK=*/false); if (loc.isValid()) return loc; @@ -8361,7 +8673,7 @@ ParseAbstractFunctionBodyRequest::getCachedResult() const { auto afd = std::get<0>(getStorage()); switch (afd->getBodyKind()) { case BodyKind::Deserialized: - case BodyKind::MemberwiseInitializer: + case BodyKind::SILSynthesize: case BodyKind::None: case BodyKind::Skipped: return nullptr; @@ -8382,7 +8694,7 @@ void ParseAbstractFunctionBodyRequest::cacheResult(BraceStmt *value) const { auto afd = std::get<0>(getStorage()); switch (afd->getBodyKind()) { case BodyKind::Deserialized: - case BodyKind::MemberwiseInitializer: + case BodyKind::SILSynthesize: case BodyKind::None: case BodyKind::Skipped: // The body is always empty, so don't cache anything. diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index c918257671764..a4140c6b8bab0 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -39,12 +39,6 @@ STATISTIC(NumLazyIterableDeclContexts, STATISTIC(NumUnloadedLazyIterableDeclContexts, "# of serialized iterable declaration contexts never loaded"); -// Only allow allocation of DeclContext using the allocator in ASTContext. -void *DeclContext::operator new(size_t Bytes, ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - ASTContext &DeclContext::getASTContext() const { return getParentModule()->getASTContext(); } @@ -679,7 +673,7 @@ unsigned DeclContext::printContext(raw_ostream &OS, const unsigned indent, } case InitializerKind::PropertyWrapper: { auto init = cast(this); - OS << "PropertyWrapper 0x" << (void*)init->getParam() << ", kind="; + OS << "PropertyWrapper 0x" << (void*)init->getWrappedVar() << ", kind="; switch (init->getKind()) { case PropertyWrapperInitializer::Kind::WrappedValue: OS << "wrappedValue"; @@ -1038,11 +1032,22 @@ IterableDeclContext::castDeclToIterableDeclContext(const Decl *D) { } Optional IterableDeclContext::getBodyFingerprint() const { - auto mutableThis = const_cast(this); - return evaluateOrDefault(getASTContext().evaluator, - ParseMembersRequest{mutableThis}, - FingerprintAndMembers()) - .fingerprint; + auto fileUnit = dyn_cast(getAsGenericContext()->getModuleScopeContext()); + if (!fileUnit) + return None; + + if (isa(fileUnit)) { + auto mutableThis = const_cast(this); + return evaluateOrDefault(getASTContext().evaluator, + ParseMembersRequest{mutableThis}, + FingerprintAndMembers()) + .fingerprint; + } + + if (getDecl()->isImplicit()) + return None; + + return fileUnit->loadFingerprint(this); } /// Return the DeclContext to compare when checking private access in @@ -1294,3 +1299,22 @@ static bool isSpecializeExtensionContext(const DeclContext *dc) { bool DeclContext::isInSpecializeExtensionContext() const { return isSpecializeExtensionContext(this); } + +bool DeclContext::isAlwaysAvailableConformanceContext() const { + auto *ext = dyn_cast(this); + if (ext == nullptr) + return true; + + if (AvailableAttr::isUnavailable(ext)) + return false; + + auto &ctx = getASTContext(); + + AvailabilityContext conformanceAvailability{ + AvailabilityInference::availableRange(ext, ctx)}; + + auto deploymentTarget = + AvailabilityContext::forDeploymentTarget(ctx); + + return deploymentTarget.isContainedIn(conformanceAvailability); +} diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 8033fa90affe4..67f90cfc13d06 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" @@ -55,22 +56,34 @@ enum class DiagnosticOptions { /// An API or ABI breakage diagnostic emitted by the API digester. APIDigesterBreakage, + + /// A deprecation warning or error. + Deprecation, + + /// A diagnostic warning about an unused element. + NoUsage, }; struct StoredDiagnosticInfo { DiagnosticKind kind : 2; bool pointsToFirstBadToken : 1; bool isFatal : 1; bool isAPIDigesterBreakage : 1; + bool isDeprecation : 1; + bool isNoUsage : 1; constexpr StoredDiagnosticInfo(DiagnosticKind k, bool firstBadToken, - bool fatal, bool isAPIDigesterBreakage) + bool fatal, bool isAPIDigesterBreakage, + bool deprecation, bool noUsage) : kind(k), pointsToFirstBadToken(firstBadToken), isFatal(fatal), - isAPIDigesterBreakage(isAPIDigesterBreakage) {} + isAPIDigesterBreakage(isAPIDigesterBreakage), isDeprecation(deprecation), + isNoUsage(noUsage) {} constexpr StoredDiagnosticInfo(DiagnosticKind k, DiagnosticOptions opts) : StoredDiagnosticInfo(k, opts == DiagnosticOptions::PointsToFirstBadToken, opts == DiagnosticOptions::Fatal, - opts == DiagnosticOptions::APIDigesterBreakage) {} + opts == DiagnosticOptions::APIDigesterBreakage, + opts == DiagnosticOptions::Deprecation, + opts == DiagnosticOptions::NoUsage) {} }; // Reproduce the DiagIDs, as we want both the size and access to the raw ids @@ -110,6 +123,12 @@ static constexpr const char *const debugDiagnosticStrings[] = { "", }; +static constexpr const char *const diagnosticIDStrings[] = { +#define DIAG(KIND, ID, Options, Text, Signature) #ID, +#include "swift/AST/DiagnosticsAll.def" + "", +}; + static constexpr const char *const fixItStrings[] = { #define DIAG(KIND, ID, Options, Text, Signature) #define FIXIT(ID, Text, Signature) Text, @@ -301,6 +320,48 @@ InFlightDiagnostic::limitBehavior(DiagnosticBehavior limit) { return *this; } +InFlightDiagnostic & +InFlightDiagnostic::warnUntilSwiftVersion(unsigned majorVersion) { + if (!Engine->languageVersion.isVersionAtLeast(majorVersion)) { + limitBehavior(DiagnosticBehavior::Warning) + .wrapIn(diag::error_in_future_swift_version, majorVersion); + } + + return *this; +} + +InFlightDiagnostic & +InFlightDiagnostic::wrapIn(const Diagnostic &wrapper) { + // Save current active diagnostic into WrappedDiagnostics, ignoring state + // so we don't get a None return or influence future diagnostics. + DiagnosticState tempState; + Engine->state.swap(tempState); + llvm::SaveAndRestore + limit(Engine->getActiveDiagnostic().BehaviorLimit, + DiagnosticBehavior::Unspecified); + + Engine->WrappedDiagnostics.push_back( + *Engine->diagnosticInfoForDiagnostic(Engine->getActiveDiagnostic())); + + Engine->state.swap(tempState); + + auto &wrapped = Engine->WrappedDiagnostics.back(); + + // Copy and update its arg list. + Engine->WrappedDiagnosticArgs.emplace_back(wrapped.FormatArgs); + wrapped.FormatArgs = Engine->WrappedDiagnosticArgs.back(); + + // Overwrite the ID and argument with those from the wrapper. + Engine->getActiveDiagnostic().ID = wrapper.ID; + Engine->getActiveDiagnostic().Args = wrapper.Args; + + // Set the argument to the diagnostic being wrapped. + assert(wrapper.getArgs().front().getKind() == DiagnosticArgumentKind::Diagnostic); + Engine->getActiveDiagnostic().Args.front() = &wrapped; + + return *this; +} + void InFlightDiagnostic::flush() { if (!IsActive) return; @@ -326,6 +387,14 @@ bool DiagnosticEngine::isAPIDigesterBreakageDiagnostic(DiagID ID) const { return storedDiagnosticInfos[(unsigned)ID].isAPIDigesterBreakage; } +bool DiagnosticEngine::isDeprecationDiagnostic(DiagID ID) const { + return storedDiagnosticInfos[(unsigned)ID].isDeprecation; +} + +bool DiagnosticEngine::isNoUsageDiagnostic(DiagID ID) const { + return storedDiagnosticInfos[(unsigned)ID].isNoUsage; +} + bool DiagnosticEngine::finishProcessing() { bool hadError = false; for (auto &Consumer : Consumers) { @@ -433,6 +502,19 @@ static bool isInterestingTypealias(Type type) { return true; } +/// Walks the type recursivelly desugaring types to display, but skipping +/// `GenericTypeParamType` because we would lose association with its original +/// declaration and end up presenting the parameter in τ_0_0 format on +/// diagnostic. +static Type getAkaTypeForDisplay(Type type) { + return type.transform([](Type visitTy) -> Type { + if (isa(visitTy.getPointer()) && + !isa(visitTy.getPointer())) + return getAkaTypeForDisplay(visitTy->getDesugaredType()); + return visitTy; + }); +} + /// Decide whether to show the desugared type or not. We filter out some /// cases to avoid too much noise. static bool shouldShowAKA(Type type, StringRef typeName) { @@ -448,7 +530,7 @@ static bool shouldShowAKA(Type type, StringRef typeName) { // If they are textually the same, don't show them. This can happen when // they are actually different types, because they exist in different scopes // (e.g. everyone names their type parameters 'T'). - if (typeName == type->getCanonicalType()->getString()) + if (typeName == getAkaTypeForDisplay(type).getString()) return false; return true; @@ -463,7 +545,7 @@ static bool typeSpellingIsAmbiguous(Type type, for (auto arg : Args) { if (arg.getKind() == DiagnosticArgumentKind::Type) { auto argType = arg.getAsType(); - if (argType && !argType->isEqual(type) && + if (argType && argType->getWithoutParens().getPointer() != type.getPointer() && argType->getWithoutParens().getString(PO) == type.getString(PO)) { return true; } @@ -472,19 +554,6 @@ static bool typeSpellingIsAmbiguous(Type type, return false; } -/// Walks the type recursivelly desugaring types to display, but skipping -/// `GenericTypeParamType` because we would lose association with its original -/// declaration and end up presenting the parameter in τ_0_0 format on -/// diagnostic. -static Type getAkaTypeForDisplay(Type type) { - return type.transform([](Type visitTy) -> Type { - if (isa(visitTy.getPointer()) && - !isa(visitTy.getPointer())) - return getAkaTypeForDisplay(visitTy->getDesugaredType()); - return visitTy; - }); -} - /// Determine whether this is the main actor type. static bool isMainActor(Type type) { if (auto nominal = type->getAnyNominal()) { @@ -499,7 +568,7 @@ static bool isMainActor(Type type) { /// Format a single diagnostic argument and write it to the given /// stream. -static void formatDiagnosticArgument(StringRef Modifier, +static void formatDiagnosticArgument(StringRef Modifier, StringRef ModifierArguments, ArrayRef Args, unsigned ArgIndex, @@ -726,11 +795,16 @@ static void formatDiagnosticArgument(StringRef Modifier, << FormatOpts.ClosingQuotationMark; break; case DiagnosticArgumentKind::ActorIsolation: + assert(Modifier.empty() && "Improper modifier for ActorIsolation argument"); switch (auto isolation = Arg.getAsActorIsolation()) { case ActorIsolation::ActorInstance: Out << "actor-isolated"; break; + case ActorIsolation::DistributedActorInstance: + Out << "distributed actor-isolated"; + break; + case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: { Type globalActor = isolation.getGlobalActor(); @@ -749,6 +823,15 @@ static void formatDiagnosticArgument(StringRef Modifier, Out << "nonisolated"; break; } + break; + + case DiagnosticArgumentKind::Diagnostic: { + assert(Modifier.empty() && "Improper modifier for Diagnostic argument"); + auto diagArg = Arg.getAsDiagnostic(); + DiagnosticEngine::formatDiagnosticText(Out, diagArg->FormatString, + diagArg->FormatArgs); + break; + } } } @@ -927,6 +1010,8 @@ void DiagnosticEngine::flushActiveDiagnostic() { assert(ActiveDiagnostic && "No active diagnostic to flush"); if (TransactionCount == 0) { emitDiagnostic(*ActiveDiagnostic); + WrappedDiagnostics.clear(); + WrappedDiagnosticArgs.clear(); } else { onTentativeDiagnosticFlush(*ActiveDiagnostic); TentativeDiagnostics.emplace_back(std::move(*ActiveDiagnostic)); @@ -939,6 +1024,8 @@ void DiagnosticEngine::emitTentativeDiagnostics() { emitDiagnostic(diag); } TentativeDiagnostics.clear(); + WrappedDiagnostics.clear(); + WrappedDiagnosticArgs.clear(); } /// Returns the access level of the least accessible PrettyPrintedDeclarations @@ -1102,10 +1189,13 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) { } } - // Currently, only API digester diagnostics are assigned a category. StringRef Category; if (isAPIDigesterBreakageDiagnostic(diagnostic.getID())) Category = "api-digester-breaking-change"; + else if (isDeprecationDiagnostic(diagnostic.getID())) + Category = "deprecation"; + else if (isNoUsageDiagnostic(diagnostic.getID())) + Category = "no-usage"; return DiagnosticInfo( diagnostic.getID(), loc, toDiagnosticKind(behavior), @@ -1154,20 +1244,29 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) { emitDiagnostic(childNote); } +DiagnosticKind DiagnosticEngine::declaredDiagnosticKindFor(const DiagID id) { + return storedDiagnosticInfos[(unsigned)id].kind; +} + llvm::StringRef DiagnosticEngine::diagnosticStringFor(const DiagID id, bool printDiagnosticNames) { auto defaultMessage = printDiagnosticNames ? debugDiagnosticStrings[(unsigned)id] : diagnosticStrings[(unsigned)id]; - if (localization) { - auto localizedMessage = - localization.get()->getMessageOr(id, defaultMessage); + + if (auto producer = localization.get()) { + auto localizedMessage = producer->getMessageOr(id, defaultMessage); return localizedMessage; } return defaultMessage; } +llvm::StringRef +DiagnosticEngine::diagnosticIDStringFor(const DiagID id) { + return diagnosticIDStrings[(unsigned)id]; +} + const char *InFlightDiagnostic::fixItStringFor(const FixItID id) { return fixItStrings[(unsigned)id]; } @@ -1227,3 +1326,24 @@ void DiagnosticEngine::onTentativeDiagnosticFlush(Diagnostic &diagnostic) { argument = DiagnosticArgument(StringRef(I->getKeyData())); } } + +EncodedDiagnosticMessage::EncodedDiagnosticMessage(StringRef S) + : Message(Lexer::getEncodedStringSegment(S, Buf, /*IsFirstSegment=*/true, + /*IsLastSegment=*/true, + /*IndentToStrip=*/~0U)) {} + +std::pair +swift::getAccessorKindAndNameForDiagnostics(const ValueDecl *D) { + // This should always be one more than the last AccessorKind supported in + // the diagnostics. If you need to change it, change the assertion below as + // well. + static const unsigned NOT_ACCESSOR_INDEX = 2; + + if (auto *accessor = dyn_cast(D)) { + DeclName Name = accessor->getStorage()->getName(); + assert(accessor->isGetterOrSetter()); + return {static_cast(accessor->getAccessorKind()), Name}; + } + + return {NOT_ACCESSOR_INDEX, D->getName()}; +} diff --git a/lib/AST/Evaluator.cpp b/lib/AST/Evaluator.cpp index 67d9200986ced..774cc60c69b79 100644 --- a/lib/AST/Evaluator.cpp +++ b/lib/AST/Evaluator.cpp @@ -71,11 +71,37 @@ bool Evaluator::checkDependency(const ActiveRequest &request) { void Evaluator::diagnoseCycle(const ActiveRequest &request) { if (debugDumpCycles) { - llvm::errs() << "===CYCLE DETECTED===\n"; - for (auto &req : activeRequests) { - simple_display(llvm::errs(), req); - llvm::errs() << "\n"; + const auto printIndent = [](llvm::raw_ostream &OS, unsigned indent) { + OS.indent(indent); + OS << "`--"; + }; + + unsigned indent = 1; + auto &OS = llvm::errs(); + + OS << "===CYCLE DETECTED===\n"; + for (const auto &step : activeRequests) { + printIndent(OS, indent); + if (step == request) { + OS.changeColor(llvm::raw_ostream::GREEN); + simple_display(OS, step); + OS.resetColor(); + } else { + simple_display(OS, step); + } + OS << "\n"; + indent += 4; } + + printIndent(OS, indent); + OS.changeColor(llvm::raw_ostream::GREEN); + simple_display(OS, request); + + OS.changeColor(llvm::raw_ostream::RED); + OS << " (cyclic dependency)"; + OS.resetColor(); + + OS << "\n"; } request.diagnoseCycle(diags); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 10fec931e365c..8e3149aa01904 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -57,12 +57,6 @@ StringRef swift::getFunctionRefKindStr(FunctionRefKind refKind) { // Expr methods. //===----------------------------------------------------------------------===// -// Only allow allocation of Stmts using the allocator in ASTContext. -void *Expr::operator new(size_t Bytes, ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - StringRef Expr::getKindName(ExprKind K) { switch (K) { #define EXPR(Id, Parent) case ExprKind::Id: return #Id; @@ -742,6 +736,135 @@ llvm::DenseMap Expr::getParentMap() { return parentMap; } +bool Expr::isValidParentOfTypeExpr(Expr *typeExpr) const { + // Allow references to types as a part of: + // - member references T.foo, T.Type, T.self, etc. + // - constructor calls T() + // - Subscripts T[] + // + // This is an exhaustive list of the accepted syntactic forms. + switch (getKind()) { + case ExprKind::Error: + case ExprKind::DotSelf: + case ExprKind::MemberRef: + case ExprKind::UnresolvedMember: + case ExprKind::DotSyntaxCall: + case ExprKind::ConstructorRefCall: + case ExprKind::UnresolvedDot: + case ExprKind::DotSyntaxBaseIgnored: + case ExprKind::UnresolvedSpecialize: + case ExprKind::OpenExistential: + return true; + + // For these cases we need to ensure the type expr is the function or base. + // We do not permit e.g 'foo(T)'. + case ExprKind::Call: + return cast(this)->getFn() == typeExpr; + case ExprKind::Subscript: + return cast(this)->getBase() == typeExpr; + + case ExprKind::NilLiteral: + case ExprKind::BooleanLiteral: + case ExprKind::IntegerLiteral: + case ExprKind::FloatLiteral: + case ExprKind::StringLiteral: + case ExprKind::MagicIdentifierLiteral: + case ExprKind::InterpolatedStringLiteral: + case ExprKind::ObjectLiteral: + case ExprKind::DiscardAssignment: + case ExprKind::DeclRef: + case ExprKind::SuperRef: + case ExprKind::Type: + case ExprKind::OtherConstructorDeclRef: + case ExprKind::OverloadedDeclRef: + case ExprKind::UnresolvedDeclRef: + case ExprKind::DynamicMemberRef: + case ExprKind::DynamicSubscript: + case ExprKind::Sequence: + case ExprKind::Paren: + case ExprKind::Await: + case ExprKind::UnresolvedMemberChainResult: + case ExprKind::Try: + case ExprKind::ForceTry: + case ExprKind::OptionalTry: + case ExprKind::Tuple: + case ExprKind::Array: + case ExprKind::Dictionary: + case ExprKind::KeyPathApplication: + case ExprKind::TupleElement: + case ExprKind::CaptureList: + case ExprKind::Closure: + case ExprKind::AutoClosure: + case ExprKind::InOut: + case ExprKind::VarargExpansion: + case ExprKind::DynamicType: + case ExprKind::RebindSelfInConstructor: + case ExprKind::OpaqueValue: + case ExprKind::PropertyWrapperValuePlaceholder: + case ExprKind::AppliedPropertyWrapper: + case ExprKind::DefaultArgument: + case ExprKind::BindOptional: + case ExprKind::OptionalEvaluation: + case ExprKind::ForceValue: + case ExprKind::MakeTemporarilyEscapable: + case ExprKind::PrefixUnary: + case ExprKind::PostfixUnary: + case ExprKind::Binary: + case ExprKind::Load: + case ExprKind::DestructureTuple: + case ExprKind::UnresolvedTypeConversion: + case ExprKind::FunctionConversion: + case ExprKind::CovariantFunctionConversion: + case ExprKind::CovariantReturnConversion: + case ExprKind::ImplicitlyUnwrappedFunctionConversion: + case ExprKind::MetatypeConversion: + case ExprKind::CollectionUpcastConversion: + case ExprKind::Erasure: + case ExprKind::AnyHashableErasure: + case ExprKind::BridgeToObjC: + case ExprKind::BridgeFromObjC: + case ExprKind::ConditionalBridgeFromObjC: + case ExprKind::DerivedToBase: + case ExprKind::ArchetypeToSuper: + case ExprKind::InjectIntoOptional: + case ExprKind::ClassMetatypeToObject: + case ExprKind::ExistentialMetatypeToObject: + case ExprKind::ProtocolMetatypeToObject: + case ExprKind::InOutToPointer: + case ExprKind::ArrayToPointer: + case ExprKind::StringToPointer: + case ExprKind::PointerToPointer: + case ExprKind::ForeignObjectConversion: + case ExprKind::UnevaluatedInstance: + case ExprKind::UnderlyingToOpaque: + case ExprKind::DifferentiableFunction: + case ExprKind::LinearFunction: + case ExprKind::DifferentiableFunctionExtractOriginal: + case ExprKind::LinearFunctionExtractOriginal: + case ExprKind::LinearToDifferentiableFunction: + case ExprKind::ForcedCheckedCast: + case ExprKind::ConditionalCheckedCast: + case ExprKind::Is: + case ExprKind::Coerce: + case ExprKind::Arrow: + case ExprKind::If: + case ExprKind::EnumIsCase: + case ExprKind::Assign: + case ExprKind::CodeCompletion: + case ExprKind::UnresolvedPattern: + case ExprKind::LazyInitializer: + case ExprKind::EditorPlaceholder: + case ExprKind::ObjCSelector: + case ExprKind::KeyPath: + case ExprKind::KeyPathDot: + case ExprKind::OneWay: + case ExprKind::Tap: + return false; + } + + llvm_unreachable("Unhandled ExprKind in switch."); +} + //===----------------------------------------------------------------------===// // Support methods for Exprs. //===----------------------------------------------------------------------===// @@ -1229,7 +1352,18 @@ UnresolvedSpecializeExpr *UnresolvedSpecializeExpr::create(ASTContext &ctx, UnresolvedParams, RAngleLoc); } +CaptureListEntry::CaptureListEntry(PatternBindingDecl *PBD) : PBD(PBD) { + assert(PBD); + assert(PBD->getSingleVar() && + "Capture lists only support single-var patterns"); +} + +VarDecl *CaptureListEntry::getVar() const { + return PBD->getSingleVar(); +} + bool CaptureListEntry::isSimpleSelfCapture() const { + auto *Var = getVar(); auto &ctx = Var->getASTContext(); if (Var->getName() != ctx.Id_self) @@ -1239,10 +1373,10 @@ bool CaptureListEntry::isSimpleSelfCapture() const { if (attr->get() == ReferenceOwnership::Weak) return false; - if (Init->getPatternList().size() != 1) + if (PBD->getPatternList().size() != 1) return false; - auto *expr = Init->getInit(0); + auto *expr = PBD->getInit(0); if (auto *DRE = dyn_cast(expr)) { if (auto *VD = dyn_cast(DRE->getDecl())) { @@ -1265,7 +1399,7 @@ CaptureListExpr *CaptureListExpr::create(ASTContext &ctx, auto *expr = ::new(mem) CaptureListExpr(captureList, closureBody); for (auto capture : captureList) - capture.Var->setParentCaptureList(expr); + capture.getVar()->setParentCaptureList(expr); return expr; } @@ -1768,6 +1902,30 @@ Expr *CallExpr::getDirectCallee() const { } } +PrefixUnaryExpr *PrefixUnaryExpr::create(ASTContext &ctx, Expr *fn, + Expr *operand, Type ty) { + return new (ctx) PrefixUnaryExpr(fn, operand, ty); +} + +PostfixUnaryExpr *PostfixUnaryExpr::create(ASTContext &ctx, Expr *fn, + Expr *operand, Type ty) { + return new (ctx) PostfixUnaryExpr(fn, operand, ty); +} + +BinaryExpr *BinaryExpr::create(ASTContext &ctx, Expr *lhs, Expr *fn, Expr *rhs, + bool implicit, Type ty) { + auto *packedArg = TupleExpr::createImplicit(ctx, {lhs, rhs}, /*labels*/ {}); + computeSingleArgumentType(ctx, packedArg, /*implicit*/ true, + [](Expr *E) { return E->getType(); }); + return new (ctx) BinaryExpr(fn, packedArg, implicit, ty); +} + +DotSyntaxCallExpr *DotSyntaxCallExpr::create(ASTContext &ctx, Expr *fnExpr, + SourceLoc dotLoc, Expr *baseExpr, + Type ty) { + return new (ctx) DotSyntaxCallExpr(fnExpr, dotLoc, baseExpr, ty); +} + SourceLoc DotSyntaxCallExpr::getLoc() const { if (isImplicit()) { SourceLoc baseLoc = getBase()->getLoc(); @@ -1795,6 +1953,13 @@ SourceLoc DotSyntaxCallExpr::getEndLoc() const { return getFn()->getEndLoc(); } +ConstructorRefCallExpr *ConstructorRefCallExpr::create(ASTContext &ctx, + Expr *fnExpr, + Expr *baseExpr, + Type ty) { + return new (ctx) ConstructorRefCallExpr(fnExpr, baseExpr, ty); +} + void ExplicitCastExpr::setCastType(Type type) { CastTy->setType(MetatypeType::get(type)); } @@ -1931,10 +2096,11 @@ void AbstractClosureExpr::setParameterList(ParameterList *P) { Type AbstractClosureExpr::getResultType( llvm::function_ref getType) const { auto *E = const_cast(this); - if (getType(E)->hasError()) - return getType(E); + Type T = getType(E); + if (!T || T->hasError()) + return T; - return getType(E)->castTo()->getResult(); + return T->castTo()->getResult(); } bool AbstractClosureExpr::isBodyThrowing() const { @@ -2359,7 +2525,7 @@ KeyPathExpr::Component::Component(ASTContext *ctxForCopyingLabels, : Decl(decl), SubscriptIndexExpr(indexExpr), KindValue(kind), ComponentType(type), Loc(loc) { - assert(kind != Kind::TupleElement || subscriptLabels.empty()); + assert(kind == Kind::Subscript || kind == Kind::UnresolvedSubscript); assert(subscriptLabels.size() == indexHashables.size() || indexHashables.empty()); SubscriptLabelsData = subscriptLabels.data(); @@ -2398,6 +2564,7 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances( case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: + case Kind::CodeCompletion: llvm_unreachable("no hashable conformances for this kind"); } } diff --git a/lib/AST/GenericEnvironment.cpp b/lib/AST/GenericEnvironment.cpp index 8b29c9c85d1ef..fecd3ba623f5b 100644 --- a/lib/AST/GenericEnvironment.cpp +++ b/lib/AST/GenericEnvironment.cpp @@ -15,14 +15,15 @@ //===----------------------------------------------------------------------===// #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/ASTContext.h" -#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/Basic/Defer.h" using namespace swift; size_t GenericEnvironment::numTrailingObjects(OverloadToken) const { - return Signature->getGenericParams().size(); + return Signature.getGenericParams().size(); } /// Retrieve the array containing the context types associated with the @@ -30,7 +31,7 @@ size_t GenericEnvironment::numTrailingObjects(OverloadToken) const { /// generic signature. MutableArrayRef GenericEnvironment::getContextTypes() { return MutableArrayRef(getTrailingObjects(), - Signature->getGenericParams().size()); + Signature.getGenericParams().size()); } /// Retrieve the array containing the context types associated with the @@ -38,17 +39,16 @@ MutableArrayRef GenericEnvironment::getContextTypes() { /// generic signature. ArrayRef GenericEnvironment::getContextTypes() const { return ArrayRef(getTrailingObjects(), - Signature->getGenericParams().size()); + Signature.getGenericParams().size()); } TypeArrayView GenericEnvironment::getGenericParams() const { - return Signature->getGenericParams(); + return Signature.getGenericParams(); } -GenericEnvironment::GenericEnvironment(GenericSignature signature, - GenericSignatureBuilder *builder) - : Signature(signature), Builder(builder) +GenericEnvironment::GenericEnvironment(GenericSignature signature) + : Signature(signature) { // Clear out the memory that holds the context types. std::uninitialized_fill(getContextTypes().begin(), getContextTypes().end(), @@ -59,12 +59,14 @@ void GenericEnvironment::addMapping(GenericParamKey key, Type contextType) { // Find the index into the parallel arrays of generic parameters and // context types. - auto genericParams = Signature->getGenericParams(); + auto genericParams = Signature.getGenericParams(); unsigned index = key.findIndexIn(genericParams); assert(genericParams[index] == key && "Bad generic parameter"); // Add the mapping from the generic parameter to the context type. - assert(getContextTypes()[index].isNull() && "Already recoded this mapping"); + assert(getContextTypes()[index].isNull() || + getContextTypes()[index]->is() && + "Already recoded this mapping"); getContextTypes()[index] = contextType; } @@ -72,7 +74,7 @@ Optional GenericEnvironment::getMappingIfPresent( GenericParamKey key) const { // Find the index into the parallel arrays of generic parameters and // context types. - auto genericParams = Signature->getGenericParams(); + auto genericParams = Signature.getGenericParams(); unsigned index = key.findIndexIn(genericParams); assert(genericParams[index] == key && "Bad generic parameter"); @@ -107,11 +109,109 @@ Type TypeBase::mapTypeOutOfContext() { SubstFlags::AllowLoweredTypes); } +Type +GenericEnvironment::getOrCreateArchetypeFromInterfaceType(Type depType) { + auto genericSig = getGenericSignature(); + LookUpConformanceInSignature conformanceLookupFn(genericSig.getPointer()); + + auto requirements = genericSig->getLocalRequirements(depType); + + // FIXME: With the RequirementMachine, we will always have an anchor. + if (requirements.concreteType && !requirements.anchor) { + if (requirements.concreteType->is()) + return requirements.concreteType; + + return mapTypeIntoContext(requirements.concreteType, + conformanceLookupFn); + } + + assert(requirements.anchor && "No anchor or concrete type?"); + + auto &ctx = genericSig->getASTContext(); + + // First, write an ErrorType to the location where this type is cached, + // to catch re-entrant lookups that might arise from an invalid generic + // signature (eg, >). + ArchetypeType *parentArchetype = nullptr; + GenericTypeParamType *genericParam = nullptr; + if (auto depMemTy = requirements.anchor->getAs()) { + parentArchetype = + getOrCreateArchetypeFromInterfaceType(depMemTy->getBase()) + ->getAs(); + if (!parentArchetype) + return ErrorType::get(depMemTy); + + auto name = depMemTy->getName(); + if (auto type = parentArchetype->getNestedTypeIfKnown(name)) + return *type; + + parentArchetype->registerNestedType(name, ErrorType::get(ctx)); + } else { + genericParam = requirements.anchor->castTo(); + if (auto type = getMappingIfPresent(genericParam)) + return *type; + addMapping(genericParam, ErrorType::get(ctx)); + } + + Type result; + + // If this equivalence class is mapped to a concrete type, produce that + // type. + if (requirements.concreteType) { + result = mapTypeIntoContext(requirements.concreteType, + conformanceLookupFn); + } else { + // Substitute into the superclass. + Type superclass = requirements.superclass; + if (superclass && superclass->hasTypeParameter()) { + superclass = mapTypeIntoContext(superclass, + conformanceLookupFn); + if (superclass->is()) + superclass = Type(); + } + + if (parentArchetype) { + auto *depMemTy = requirements.anchor->castTo(); + result = NestedArchetypeType::getNew(ctx, parentArchetype, depMemTy, + requirements.protos, superclass, + requirements.layout); + } else { + result = PrimaryArchetypeType::getNew(ctx, this, genericParam, + requirements.protos, superclass, + requirements.layout); + } + } + + // Cache the new archetype for future lookups. + if (auto depMemTy = requirements.anchor->getAs()) { + parentArchetype->registerNestedType(depMemTy->getName(), result); + } else { + addMapping(genericParam, result); + } + + return result; +} + +void ArchetypeType::resolveNestedType( + std::pair &nested) const { + Type interfaceType = getInterfaceType(); + Type memberInterfaceType = + DependentMemberType::get(interfaceType, nested.first); + + Type result = getGenericEnvironment()->getOrCreateArchetypeFromInterfaceType( + memberInterfaceType); + + assert(!nested.second || + nested.second->isEqual(result) || + nested.second->is()); + nested.second = result; +} + Type QueryInterfaceTypeSubstitutions::operator()(SubstitutableType *type) const{ if (auto gp = type->getAs()) { // Find the index into the parallel arrays of generic parameters and // context types. - auto genericParams = self->Signature->getGenericParams(); + auto genericParams = self->Signature.getGenericParams(); GenericParamKey key(gp); // Make sure that this generic parameter is from this environment. @@ -120,24 +220,18 @@ Type QueryInterfaceTypeSubstitutions::operator()(SubstitutableType *type) const{ return Type(); // If the context type isn't already known, lazily create it. - Type contextType = self->getContextTypes()[index]; - if (!contextType) { - assert(self->Builder &&"Missing generic signature builder for lazy query"); - auto equivClass = - self->Builder->resolveEquivalenceClass( - type, - ArchetypeResolutionKind::CompleteWellFormed); - - auto mutableSelf = const_cast(self); - contextType = equivClass->getTypeInContext(*mutableSelf->Builder, - mutableSelf); - - // FIXME: Redundant mapping from key -> index. - if (self->getContextTypes()[index].isNull()) - mutableSelf->addMapping(key, contextType); - } - - return contextType; + auto mutableSelf = const_cast(self); + Type &contextType = mutableSelf->getContextTypes()[index]; + if (contextType) + return contextType; + + auto result = mutableSelf->getOrCreateArchetypeFromInterfaceType(type); + + assert (!contextType || + contextType->isEqual(result) || + contextType->is()); + contextType = result; + return result; } return Type(); diff --git a/lib/AST/GenericSignature.cpp b/lib/AST/GenericSignature.cpp index 9c79beb27241e..c0a4b918f7c02 100644 --- a/lib/AST/GenericSignature.cpp +++ b/lib/AST/GenericSignature.cpp @@ -24,6 +24,7 @@ #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/Types.h" #include "swift/Basic/STLExtras.h" +#include "RequirementMachine/RequirementMachine.h" #include using namespace swift; @@ -155,6 +156,30 @@ ASTContext &GenericSignature::getASTContext( return requirements.front().getFirstType()->getASTContext(); } +/// Retrieve the generic parameters. +TypeArrayView GenericSignature::getGenericParams() const { + return isNull() + ? TypeArrayView{} + : getPointer()->getGenericParams(); +} + +/// Retrieve the innermost generic parameters. +/// +/// Given a generic signature for a nested generic type, produce an +/// array of the generic parameters for the innermost generic type. +TypeArrayView GenericSignature::getInnermostGenericParams() const { + return isNull() + ? TypeArrayView{} + : getPointer()->getInnermostGenericParams(); +} + +/// Retrieve the requirements. +ArrayRef GenericSignature::getRequirements() const { + return isNull() + ? ArrayRef{} + : getPointer()->getRequirements(); +} + GenericSignatureBuilder * GenericSignatureImpl::getGenericSignatureBuilder() const { // The generic signature builder is associated with the canonical signature. @@ -166,6 +191,17 @@ GenericSignatureImpl::getGenericSignatureBuilder() const { CanGenericSignature(this)); } +rewriting::RequirementMachine * +GenericSignatureImpl::getRequirementMachine() const { + // The requirement machine is associated with the canonical signature. + if (!isCanonical()) + return getCanonicalSignature()->getRequirementMachine(); + + // Requirement machines are stored on the ASTContext. + return getASTContext().getOrCreateRequirementMachine( + CanGenericSignature(this)); +} + bool GenericSignatureImpl::isEqual(GenericSignature Other) const { return getCanonicalSignature() == Other.getCanonicalSignature(); } @@ -233,16 +269,177 @@ CanGenericSignature GenericSignatureImpl::getCanonicalSignature() const { CanonicalSignatureOrASTContext.get()); } +GenericEnvironment *GenericSignature::getGenericEnvironment() const { + if (isNull()) + return nullptr; + return getPointer()->getGenericEnvironment(); +} + GenericEnvironment *GenericSignatureImpl::getGenericEnvironment() const { if (GenericEnv == nullptr) { - auto *builder = getGenericSignatureBuilder(); const auto impl = const_cast(this); - impl->GenericEnv = GenericEnvironment::getIncomplete(this, builder); + impl->GenericEnv = GenericEnvironment::getIncomplete(this); } return GenericEnv; } +GenericSignature::LocalRequirements +GenericSignatureImpl::getLocalRequirements(Type depType) const { + assert(depType->isTypeParameter() && "Expected a type parameter here"); + + auto computeViaGSB = [&]() { + GenericSignature::LocalRequirements result; + + auto &builder = *getGenericSignatureBuilder(); + + auto resolved = + builder.maybeResolveEquivalenceClass( + depType, + ArchetypeResolutionKind::CompleteWellFormed, + /*wantExactPotentialArchetype=*/false); + if (!resolved) { + result.concreteType = ErrorType::get(depType); + return result; + } + + if (auto concreteType = resolved.getAsConcreteType()) { + result.concreteType = concreteType; + return result; + } + + auto *equivClass = resolved.getEquivalenceClass(builder); + + auto genericParams = getGenericParams(); + result.anchor = equivClass->getAnchor(builder, genericParams); + + if (equivClass->concreteType) { + result.concreteType = equivClass->concreteType; + return result; + } + + result.superclass = equivClass->superclass; + + for (const auto &conforms : equivClass->conformsTo) { + auto proto = conforms.first; + + if (!equivClass->isConformanceSatisfiedBySuperclass(proto)) + result.protos.push_back(proto); + } + + result.layout = equivClass->layout; + + return result; + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->getLocalRequirements(depType, getGenericParams()); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + auto typesEqual = [&](Type lhs, Type rhs, bool canonical) { + if (!lhs || !rhs) + return !lhs == !rhs; + if (lhs->isEqual(rhs)) + return true; + + if (canonical) + return false; + + if (getCanonicalTypeInContext(lhs) == + getCanonicalTypeInContext(rhs)) + return true; + + return false; + }; + + auto compare = [&]() { + // If the types are concrete, we don't care about the rest. + if (gsbResult.concreteType || rqmResult.concreteType) { + if (!typesEqual(gsbResult.concreteType, + rqmResult.concreteType, + false)) + return false; + + return true; + } + + if (!typesEqual(gsbResult.anchor, + rqmResult.anchor, + true)) + return false; + + if (gsbResult.layout != rqmResult.layout) + return false; + + auto lhsProtos = gsbResult.protos; + ProtocolType::canonicalizeProtocols(lhsProtos); + auto rhsProtos = rqmResult.protos; + ProtocolType::canonicalizeProtocols(rhsProtos); + + if (lhsProtos != rhsProtos) + return false; + + if (!typesEqual(gsbResult.superclass, + rqmResult.superclass, + false)) + return false; + + return true; + }; + + auto dumpReqs = [&](const GenericSignature::LocalRequirements &reqs) { + if (reqs.anchor) { + llvm::errs() << "- Anchor: " << reqs.anchor << "\n"; + reqs.anchor.dump(llvm::errs()); + } + if (reqs.concreteType) { + llvm::errs() << "- Concrete type: " << reqs.concreteType << "\n"; + reqs.concreteType.dump(llvm::errs()); + } + if (reqs.superclass) { + llvm::errs() << "- Superclass: " << reqs.superclass << "\n"; + reqs.superclass.dump(llvm::errs()); + } + if (reqs.layout) { + llvm::errs() << "- Layout: " << reqs.layout << "\n"; + } + for (const auto *proto : reqs.protos) { + llvm::errs() << "- Conforms to: " << proto->getName() << "\n"; + } + }; + + if (!compare()) { + llvm::errs() << "RequirementMachine::getLocalRequirements() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; depType.dump(llvm::errs()); + llvm::errs() << "GenericSignatureBuilder says:\n"; + dumpReqs(gsbResult); + llvm::errs() << "\n"; + llvm::errs() << "RequirementMachine says:\n"; + dumpReqs(rqmResult); + llvm::errs() << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } + + return rqmResult; + } + } +} + ASTContext &GenericSignatureImpl::getASTContext() const { // Canonical signatures store the ASTContext directly. if (auto ctx = CanonicalSignatureOrASTContext.dyn_cast()) @@ -261,28 +458,61 @@ GenericSignatureImpl::lookupConformance(CanType type, if (type->isTypeParameter()) return ProtocolConformanceRef(proto); - return M->lookupConformance(type, proto); + return M->lookupConformance(type, proto, /*allowMissing=*/true); } bool GenericSignatureImpl::requiresClass(Type type) const { assert(type->isTypeParameter() && "Only type parameters can have superclass requirements"); - auto &builder = *getGenericSignatureBuilder(); - auto equivClass = - builder.resolveEquivalenceClass( - type, - ArchetypeResolutionKind::CompleteWellFormed); - if (!equivClass) return false; + auto computeViaGSB = [&]() { + auto &builder = *getGenericSignatureBuilder(); + auto equivClass = + builder.resolveEquivalenceClass( + type, + ArchetypeResolutionKind::CompleteWellFormed); + if (!equivClass) return false; + + // If this type was mapped to a concrete type, then there is no + // requirement. + if (equivClass->concreteType) return false; + + // If there is a layout constraint, it might be a class. + if (equivClass->layout && equivClass->layout->isClass()) return true; + + return false; + }; - // If this type was mapped to a concrete type, then there is no - // requirement. - if (equivClass->concreteType) return false; + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->requiresClass(type); + }; - // If there is a layout constraint, it might be a class. - if (equivClass->layout && equivClass->layout->isClass()) return true; + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + llvm::errs() << "RequirementMachine::requiresClass() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } - return false; + return rqmResult; + } + } } /// Determine the superclass bound on the given dependent type. @@ -290,19 +520,64 @@ Type GenericSignatureImpl::getSuperclassBound(Type type) const { assert(type->isTypeParameter() && "Only type parameters can have superclass requirements"); - auto &builder = *getGenericSignatureBuilder(); - auto equivClass = - builder.resolveEquivalenceClass( - type, - ArchetypeResolutionKind::CompleteWellFormed); - if (!equivClass) return nullptr; + auto computeViaGSB = [&]() -> Type { + auto &builder = *getGenericSignatureBuilder(); + auto equivClass = + builder.resolveEquivalenceClass( + type, + ArchetypeResolutionKind::CompleteWellFormed); + if (!equivClass) return nullptr; + + // If this type was mapped to a concrete type, then there is no + // requirement. + if (equivClass->concreteType) return nullptr; + + // Retrieve the superclass bound. + return equivClass->superclass; + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->getSuperclassBound(type); + }; - // If this type was mapped to a concrete type, then there is no - // requirement. - if (equivClass->concreteType) return nullptr; + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + auto check = [&]() { + if (!gsbResult || !rqmResult) + return !gsbResult == !rqmResult; + return gsbResult->isEqual(rqmResult); + }; + + if (!check()) { + llvm::errs() << "RequirementMachine::getSuperclassBound() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + if (gsbResult) + gsbResult.dump(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + if (rqmResult) + rqmResult.dump(llvm::errs()); + llvm::errs() << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } - // Retrieve the superclass bound. - return equivClass->superclass; + return rqmResult; + } + } } /// Determine the set of protocols to which the given type parameter is @@ -311,53 +586,170 @@ GenericSignature::RequiredProtocols GenericSignatureImpl::getRequiredProtocols(Type type) const { assert(type->isTypeParameter() && "Expected a type parameter"); - auto &builder = *getGenericSignatureBuilder(); - auto equivClass = - builder.resolveEquivalenceClass( - type, - ArchetypeResolutionKind::CompleteWellFormed); - if (!equivClass) return { }; + auto computeViaGSB = [&]() -> GenericSignature::RequiredProtocols { + auto &builder = *getGenericSignatureBuilder(); + auto equivClass = + builder.resolveEquivalenceClass( + type, + ArchetypeResolutionKind::CompleteWellFormed); + if (!equivClass) return { }; - // If this type parameter was mapped to a concrete type, then there - // are no requirements. - if (equivClass->concreteType) return { }; + // If this type parameter was mapped to a concrete type, then there + // are no requirements. + if (equivClass->concreteType) return { }; - // Retrieve the protocols to which this type conforms. - GenericSignature::RequiredProtocols result; - for (const auto &conforms : equivClass->conformsTo) - result.push_back(conforms.first); + // Retrieve the protocols to which this type conforms. + GenericSignature::RequiredProtocols result; + for (const auto &conforms : equivClass->conformsTo) + result.push_back(conforms.first); - // Canonicalize the resulting set of protocols. - ProtocolType::canonicalizeProtocols(result); + // Canonicalize the resulting set of protocols. + ProtocolType::canonicalizeProtocols(result); - return result; + return result; + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->getRequiredProtocols(type); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + llvm::errs() << "RequirementMachine::getRequiredProtocols() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "GenericSignatureBuilder says: "; + for (auto *otherProto : gsbResult) + llvm::errs() << " " << otherProto->getName(); + llvm::errs() << "\n"; + llvm::errs() << "RequirementMachine says: "; + for (auto *otherProto : rqmResult) + llvm::errs() << " " << otherProto->getName(); + llvm::errs() << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } + + return rqmResult; + } + } } bool GenericSignatureImpl::requiresProtocol(Type type, ProtocolDecl *proto) const { assert(type->isTypeParameter() && "Expected a type parameter"); - auto &builder = *getGenericSignatureBuilder(); - auto equivClass = - builder.resolveEquivalenceClass( - type, - ArchetypeResolutionKind::CompleteWellFormed); - if (!equivClass) return false; + auto computeViaGSB = [&]() { + auto &builder = *getGenericSignatureBuilder(); + auto equivClass = + builder.resolveEquivalenceClass( + type, + ArchetypeResolutionKind::CompleteWellFormed); + if (!equivClass) return false; + + // FIXME: Optionally deal with concrete conformances here + // or have a separate method do that additionally? + // + // If this type parameter was mapped to a concrete type, then there + // are no requirements. + if (equivClass->concreteType) return false; - // FIXME: Optionally deal with concrete conformances here - // or have a separate method do that additionally? - // - // If this type parameter was mapped to a concrete type, then there - // are no requirements. - if (equivClass->concreteType) return false; + // Check whether the representative conforms to this protocol. + return equivClass->conformsTo.count(proto) > 0; + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->requiresProtocol(type, proto); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + llvm::errs() << "RequirementMachine::requiresProtocol() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "Protocol: "; proto->dumpRef(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } - // Check whether the representative conforms to this protocol. - return equivClass->conformsTo.count(proto) > 0; + return rqmResult; + } + } } /// Determine whether the given dependent type is equal to a concrete type. bool GenericSignatureImpl::isConcreteType(Type type) const { - return bool(getConcreteType(type)); + assert(type->isTypeParameter() && "Expected a type parameter"); + + auto computeViaGSB = [&]() { + auto &builder = *getGenericSignatureBuilder(); + auto equivClass = + builder.resolveEquivalenceClass( + type, + ArchetypeResolutionKind::CompleteWellFormed); + if (!equivClass) return false; + + return bool(equivClass->concreteType); + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->isConcreteType(type); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + llvm::errs() << "RequirementMachine::isConcreteType() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } + + return rqmResult; + } + } } /// Return the concrete type that the given type parameter is constrained to, @@ -366,28 +758,111 @@ bool GenericSignatureImpl::isConcreteType(Type type) const { Type GenericSignatureImpl::getConcreteType(Type type) const { assert(type->isTypeParameter() && "Expected a type parameter"); - auto &builder = *getGenericSignatureBuilder(); - auto equivClass = + auto computeViaGSB = [&]() -> Type { + auto &builder = *getGenericSignatureBuilder(); + auto equivClass = builder.resolveEquivalenceClass( type, ArchetypeResolutionKind::CompleteWellFormed); - if (!equivClass) return Type(); + if (!equivClass) return nullptr; + + return equivClass->concreteType; + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->getConcreteType(type); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + auto check = [&]() { + if (!gsbResult || !rqmResult) + return !gsbResult == !rqmResult; + if (gsbResult->isEqual(rqmResult)) + return true; + + return (getCanonicalTypeInContext(gsbResult) + == getCanonicalTypeInContext(rqmResult)); + }; + + if (!check()) { + llvm::errs() << "RequirementMachine::getConcreteType() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + if (gsbResult) + gsbResult.dump(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + if (rqmResult) + rqmResult.dump(llvm::errs()); + llvm::errs() << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } - return equivClass->concreteType; + return rqmResult; + } + } } LayoutConstraint GenericSignatureImpl::getLayoutConstraint(Type type) const { assert(type->isTypeParameter() && "Only type parameters can have layout constraints"); - auto &builder = *getGenericSignatureBuilder(); - auto equivClass = - builder.resolveEquivalenceClass( - type, - ArchetypeResolutionKind::CompleteWellFormed); - if (!equivClass) return LayoutConstraint(); + auto computeViaGSB = [&]() { + auto &builder = *getGenericSignatureBuilder(); + auto equivClass = + builder.resolveEquivalenceClass( + type, + ArchetypeResolutionKind::CompleteWellFormed); + if (!equivClass) return LayoutConstraint(); - return equivClass->layout; + return equivClass->layout; + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->getLayoutConstraint(type); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + llvm::errs() << "RequirementMachine::getLayoutConstraint() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } + + return rqmResult; + } + } } bool GenericSignatureImpl::areSameTypeParameterInContext(Type type1, @@ -398,8 +873,47 @@ bool GenericSignatureImpl::areSameTypeParameterInContext(Type type1, if (type1.getPointer() == type2.getPointer()) return true; - return areSameTypeParameterInContext(type1, type2, - *getGenericSignatureBuilder()); + auto computeViaGSB = [&]() { + return areSameTypeParameterInContext(type1, type2, + *getGenericSignatureBuilder()); + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->areSameTypeParameterInContext(type1, type2); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + auto firstConcreteType = getConcreteType(type1); + auto secondConcreteType = getConcreteType(type2); + if (!firstConcreteType->isEqual(secondConcreteType)) { + llvm::errs() << "RequirementMachine::areSameTypeParameterInContext() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "First dependent type: "; type1.dump(llvm::errs()); + llvm::errs() << "Second dependent type: "; type2.dump(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } + } + + return rqmResult; + } + } } bool GenericSignatureImpl::areSameTypeParameterInContext(Type type1, @@ -427,63 +941,29 @@ bool GenericSignatureImpl::areSameTypeParameterInContext(Type type1, } bool GenericSignatureImpl::isRequirementSatisfied( - Requirement requirement) const { - auto GSB = getGenericSignatureBuilder(); - - auto firstType = requirement.getFirstType(); - auto canFirstType = getCanonicalTypeInContext(firstType); - - switch (requirement.getKind()) { - case RequirementKind::Conformance: { - auto *protocol = requirement.getProtocolDecl(); - - if (canFirstType->isTypeParameter()) - return requiresProtocol(canFirstType, protocol); - else - return (bool)GSB->lookupConformance(canFirstType, protocol); - } + Requirement requirement, bool allowMissing) const { + if (requirement.getFirstType()->hasTypeParameter()) { + auto *genericEnv = getGenericEnvironment(); - case RequirementKind::SameType: { - auto canSecondType = getCanonicalTypeInContext(requirement.getSecondType()); - return canFirstType->isEqual(canSecondType); - } + auto substituted = requirement.subst( + [&](SubstitutableType *type) -> Type { + if (auto *paramType = type->getAs()) + return genericEnv->mapTypeIntoContext(paramType); - case RequirementKind::Superclass: { - auto requiredSuperclass = - getCanonicalTypeInContext(requirement.getSecondType()); - - // The requirement could be in terms of type parameters like a user-written - // requirement, but it could also be in terms of concrete types if it has - // been substituted/otherwise 'resolved', so we need to handle both. - auto baseType = canFirstType; - if (baseType->isTypeParameter()) { - auto directSuperclass = getSuperclassBound(baseType); - if (!directSuperclass) - return false; + return type; + }, + LookUpConformanceInSignature(this)); - baseType = getCanonicalTypeInContext(directSuperclass); - } + if (!substituted) + return false; - return requiredSuperclass->isExactSuperclassOf(baseType); + requirement = *substituted; } - case RequirementKind::Layout: { - auto requiredLayout = requirement.getLayoutConstraint(); - - if (canFirstType->isTypeParameter()) { - if (auto layout = getLayoutConstraint(canFirstType)) - return static_cast(layout.merge(requiredLayout)); - - return false; - } + // FIXME: Need to check conditional requirements here. + ArrayRef conditionalRequirements; - // The requirement is on a concrete type, so it's either globally correct - // or globally incorrect, independent of this generic context. The latter - // case should be diagnosed elsewhere, so let's assume it's correct. - return true; - } - } - llvm_unreachable("unhandled kind"); + return requirement.isSatisfied(conditionalRequirements, allowMissing); } SmallVector GenericSignatureImpl::requirementsNotSatisfiedBy( @@ -494,7 +974,7 @@ SmallVector GenericSignatureImpl::requirementsNotSatisfiedBy( if (otherSig.getPointer() == this) return result; // If there is no other signature, no requirements are satisfied. - if (!otherSig){ + if (!otherSig) { const auto reqs = getRequirements(); result.append(reqs.begin(), reqs.end()); return result; @@ -524,8 +1004,42 @@ bool GenericSignatureImpl::isCanonicalTypeInContext(Type type) const { if (!type->hasTypeParameter()) return true; - auto &builder = *getGenericSignatureBuilder(); - return isCanonicalTypeInContext(type, builder); + auto computeViaGSB = [&]() { + auto &builder = *getGenericSignatureBuilder(); + return isCanonicalTypeInContext(type, builder); + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->isCanonicalTypeInContext(type); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + llvm::errs() << "RequirementMachine::isCanonicalTypeInContext() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } + + return rqmResult; + } + } } bool GenericSignatureImpl::isCanonicalTypeInContext( @@ -564,166 +1078,180 @@ CanType GenericSignatureImpl::getCanonicalTypeInContext(Type type) const { if (!type->hasTypeParameter()) return CanType(type); - auto &builder = *getGenericSignatureBuilder(); - return builder.getCanonicalTypeInContext(type, { })->getCanonicalType(); + auto computeViaGSB = [&]() { + auto &builder = *getGenericSignatureBuilder(); + return builder.getCanonicalTypeInContext(type, { })->getCanonicalType(); + }; + + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->getCanonicalTypeInContext(type, { })->getCanonicalType(); + }; + + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + llvm::errs() << "RequirementMachine::getCanonicalTypeInContext() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "GenericSignatureBuilder says: " << gsbResult << "\n"; + gsbResult.dump(llvm::errs()); + llvm::errs() << "RequirementMachine says: " << rqmResult << "\n"; + rqmResult.dump(llvm::errs()); + llvm::errs() << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); + } + + return rqmResult; + } + } } ArrayRef> -CanGenericSignature::getGenericParams() const{ +CanGenericSignature::getGenericParams() const { auto params = getPointer()->getGenericParams().getOriginalArray(); auto base = static_cast*>( params.data()); return {base, params.size()}; } -namespace { - typedef GenericSignatureBuilder::RequirementSource RequirementSource; - - template - using GSBConstraint = GenericSignatureBuilder::Constraint; -} // end anonymous namespace - -/// Determine whether there is a conformance of the given -/// subject type to the given protocol within the given set of explicit -/// requirements. -static bool hasConformanceInSignature(ArrayRef requirements, - Type subjectType, - ProtocolDecl *proto) { - // Make sure this requirement exists in the requirement signature. - for (const auto &req: requirements) { - if (req.getKind() == RequirementKind::Conformance && - req.getFirstType()->isEqual(subjectType) && - req.getProtocolDecl() == proto) { - return true; - } - } - - return false; -} - ConformanceAccessPath GenericSignatureImpl::getConformanceAccessPath(Type type, ProtocolDecl *protocol) const { - assert(type->isTypeParameter() && "not a type parameter"); - - // Look up the equivalence class for this type. - auto &builder = *getGenericSignatureBuilder(); - auto equivClass = - builder.resolveEquivalenceClass( - type, - ArchetypeResolutionKind::CompleteWellFormed); - - assert(!equivClass->concreteType && - "Concrete types don't have conformance access paths"); - - auto cached = equivClass->conformanceAccessPathCache.find(protocol); - if (cached != equivClass->conformanceAccessPathCache.end()) - return cached->second; + auto computeViaGSB = [&]() { + return getGenericSignatureBuilder()->getConformanceAccessPath( + type, protocol, this); + }; - // Dig out the conformance of this type to the given protocol, because we - // want its requirement source. - auto conforms = equivClass->conformsTo.find(protocol); - assert(conforms != equivClass->conformsTo.end()); + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->getConformanceAccessPath(type, protocol); + }; - auto rootType = equivClass->getAnchor(builder, { }); - if (hasConformanceInSignature(getRequirements(), rootType, protocol)) { - ConformanceAccessPath::Entry root(rootType, protocol); - ArrayRef path(root); + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); - ConformanceAccessPath result(builder.getASTContext().AllocateCopy(path)); - equivClass->conformanceAccessPathCache.insert({protocol, result}); + case RequirementMachineMode::Enabled: + return computeViaRQM(); - return result; - } + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); - // This conformance comes from a derived source. - // - // To recover this the conformance, we recursively recover the conformance - // of the shortest parent type to the parent protocol first. - Type shortestParentType; - Type shortestSubjectType; - ProtocolDecl *shortestParentProto = nullptr; + auto compare = [&]() { + if (gsbResult.size() != rqmResult.size()) + return false; - auto isShortestPath = [&](Type parentType, - Type subjectType, - ProtocolDecl *parentProto) -> bool { - if (!shortestParentType) - return true; + auto *begin1 = gsbResult.begin(); + auto *end1 = gsbResult.end(); + auto *begin2 = rqmResult.begin(); + auto *end2 = rqmResult.end(); - int cmpParentTypes = compareDependentTypes(parentType, shortestParentType); - if (cmpParentTypes != 0) - return cmpParentTypes < 0; + while (begin1 < end1) { + assert(begin2 < end2); - int cmpSubjectTypes = compareDependentTypes(subjectType, shortestSubjectType); - if (cmpSubjectTypes != 0) - return cmpSubjectTypes < 0; + if (!begin1->first->isEqual(begin2->first)) + return false; + if (begin1->second != begin2->second) + return false; - int cmpParentProtos = TypeDecl::compare(parentProto, shortestParentProto); - return cmpParentProtos < 0; - }; + ++begin1; + ++begin2; + } - auto recordShortestParentType = [&](Type parentType, - Type subjectType, - ProtocolDecl *parentProto) { - if (isShortestPath(parentType, subjectType, parentProto)) { - shortestParentType = parentType; - shortestSubjectType = subjectType; - shortestParentProto = parentProto; + return true; + }; + + if (!compare()) { + llvm::errs() << "RequirementMachine::getConformanceAccessPath() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "Protocol: "; protocol->dumpRef(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "GenericSignatureBuilder says: "; + gsbResult.print(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "RequirementMachine says: "; + rqmResult.print(llvm::errs()); + llvm::errs() << "\n\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); } - }; - for (auto constraint : conforms->second) { - auto *source = constraint.source; + return rqmResult; + } + } +} - switch (source->kind) { - case RequirementSource::Explicit: - case RequirementSource::Inferred: - // This is not a derived source, so it contributes nothing to the - // "shortest parent type" computation. - break; +TypeDecl * +GenericSignatureImpl::lookupNestedType(Type type, Identifier name) const { + assert(type->isTypeParameter()); - case RequirementSource::ProtocolRequirement: - case RequirementSource::InferredProtocolRequirement: { - assert(source->parent->kind != RequirementSource::RequirementSignatureSelf); + auto computeViaGSB = [&]() -> TypeDecl * { + auto *builder = getGenericSignatureBuilder(); + auto equivClass = + builder->resolveEquivalenceClass( + type, + ArchetypeResolutionKind::CompleteWellFormed); + if (!equivClass) + return nullptr; - // If we have a derived conformance requirement like T[.P].X : Q, we can - // recursively compute the conformance access path for T : P, and append - // the path element (Self.X : Q). - auto parentType = source->parent->getAffectedType()->getCanonicalType(); - auto subjectType = source->getStoredType()->getCanonicalType(); - auto *parentProto = source->getProtocolDecl(); + return equivClass->lookupNestedType(*builder, name); + }; - // We might have multiple candidate parent types and protocols for the - // recursive step, so pick the shortest one. - recordShortestParentType(parentType, subjectType, parentProto); + auto computeViaRQM = [&]() { + auto *machine = getRequirementMachine(); + return machine->lookupNestedType(type, name); + }; - break; + auto &ctx = getASTContext(); + switch (ctx.LangOpts.EnableRequirementMachine) { + case RequirementMachineMode::Disabled: + return computeViaGSB(); + + case RequirementMachineMode::Enabled: + return computeViaRQM(); + + case RequirementMachineMode::Verify: { + auto rqmResult = computeViaRQM(); + auto gsbResult = computeViaGSB(); + + if (gsbResult != rqmResult) { + llvm::errs() << "RequirementMachine::lookupNestedType() is broken\n"; + llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n"; + llvm::errs() << "Dependent type: "; type.dump(llvm::errs()); + llvm::errs() << "GenericSignatureBuilder says: "; + if (gsbResult) + gsbResult->dumpRef(llvm::errs()); + else + llvm::errs() << ""; + llvm::errs() << "\n"; + llvm::errs() << "RequirementMachine says: "; + if (rqmResult) + rqmResult->dumpRef(llvm::errs()); + else + llvm::errs() << ""; + llvm::errs() << "\n"; + getRequirementMachine()->dump(llvm::errs()); + abort(); } - default: - // There should be no other way of testifying to a conformance on a - // dependent type. - llvm_unreachable("Bad requirement source for conformance on dependent type"); - } + return rqmResult; + } } - - assert(shortestParentType); - - SmallVector path; - - auto parentPath = getConformanceAccessPath( - shortestParentType, shortestParentProto); - for (auto entry : parentPath) - path.push_back(entry); - - // Then, we add the subject type from the parent protocol's requirement - // signature. - path.emplace_back(shortestSubjectType, protocol); - - ConformanceAccessPath result(builder.getASTContext().AllocateCopy(path)); - equivClass->conformanceAccessPathCache.insert({protocol, result}); - - return result; } unsigned GenericParamKey::findIndexIn( @@ -777,34 +1305,9 @@ unsigned GenericSignatureImpl::getGenericParamOrdinal( return GenericParamKey(param).findIndexIn(getGenericParams()); } -bool GenericSignatureImpl::hasTypeVariable() const { - return GenericSignature::hasTypeVariable(getRequirements()); -} - -bool GenericSignature::hasTypeVariable(ArrayRef requirements) { - for (const auto &req : requirements) { - if (req.getFirstType()->hasTypeVariable()) - return true; - - switch (req.getKind()) { - case RequirementKind::Layout: - break; - - case RequirementKind::Conformance: - case RequirementKind::SameType: - case RequirementKind::Superclass: - if (req.getSecondType()->hasTypeVariable()) - return true; - break; - } - } - - return false; -} - void GenericSignature::Profile(llvm::FoldingSetNodeID &id) const { return GenericSignature::Profile(id, getPointer()->getGenericParams(), - getPointer()->getRequirements()); + getPointer()->getRequirements()); } void GenericSignature::Profile(llvm::FoldingSetNodeID &ID, @@ -865,3 +1368,171 @@ ProtocolDecl *Requirement::getProtocolDecl() const { assert(getKind() == RequirementKind::Conformance); return getSecondType()->castTo()->getDecl(); } + +bool +Requirement::isSatisfied(ArrayRef &conditionalRequirements, + bool allowMissing) const { + switch (getKind()) { + case RequirementKind::Conformance: { + auto *proto = getProtocolDecl(); + auto *module = proto->getParentModule(); + auto conformance = module->lookupConformance( + getFirstType(), proto, allowMissing); + if (!conformance) + return false; + + conditionalRequirements = conformance.getConditionalRequirements(); + return true; + } + + case RequirementKind::Layout: { + if (auto *archetypeType = getFirstType()->getAs()) { + auto layout = archetypeType->getLayoutConstraint(); + return (layout && layout.merge(getLayoutConstraint())); + } + + if (getLayoutConstraint()->isClass()) + return getFirstType()->satisfiesClassConstraint(); + + // TODO: Statically check other layout constraints, once they can + // be spelled in Swift. + return true; + } + + case RequirementKind::Superclass: + return getSecondType()->isExactSuperclassOf(getFirstType()); + + case RequirementKind::SameType: + return getFirstType()->isEqual(getSecondType()); + } + + llvm_unreachable("Bad requirement kind"); +} + +bool Requirement::canBeSatisfied() const { + switch (getKind()) { + case RequirementKind::Conformance: + return getFirstType()->is(); + + case RequirementKind::Layout: { + if (auto *archetypeType = getFirstType()->getAs()) { + auto layout = archetypeType->getLayoutConstraint(); + return (!layout || layout.merge(getLayoutConstraint())); + } + + return false; + } + + case RequirementKind::Superclass: + return (getFirstType()->isBindableTo(getSecondType()) || + getSecondType()->isBindableTo(getFirstType())); + + case RequirementKind::SameType: + return (getFirstType()->isBindableTo(getSecondType()) || + getSecondType()->isBindableTo(getFirstType())); + } + + llvm_unreachable("Bad requirement kind"); +} + +/// Determine the canonical ordering of requirements. +static unsigned getRequirementKindOrder(RequirementKind kind) { + switch (kind) { + case RequirementKind::Conformance: return 2; + case RequirementKind::Superclass: return 0; + case RequirementKind::SameType: return 3; + case RequirementKind::Layout: return 1; + } + llvm_unreachable("unhandled kind"); +} + +/// Linear order on requirements in a generic signature. +int Requirement::compare(const Requirement &other) const { + int compareLHS = + compareDependentTypes(getFirstType(), other.getFirstType()); + + if (compareLHS != 0) + return compareLHS; + + int compareKind = (getRequirementKindOrder(getKind()) - + getRequirementKindOrder(other.getKind())); + + if (compareKind != 0) + return compareKind; + + // We should only have multiple conformance requirements. + assert(getKind() == RequirementKind::Conformance); + + int compareProtos = + TypeDecl::compare(getProtocolDecl(), other.getProtocolDecl()); + + assert(compareProtos != 0 && "Duplicate conformance requirement"); + return compareProtos; +} + +/// Compare two associated types. +int swift::compareAssociatedTypes(AssociatedTypeDecl *assocType1, + AssociatedTypeDecl *assocType2) { + // - by name. + if (int result = assocType1->getName().str().compare( + assocType2->getName().str())) + return result; + + // Prefer an associated type with no overrides (i.e., an anchor) to one + // that has overrides. + bool hasOverridden1 = !assocType1->getOverriddenDecls().empty(); + bool hasOverridden2 = !assocType2->getOverriddenDecls().empty(); + if (hasOverridden1 != hasOverridden2) + return hasOverridden1 ? +1 : -1; + + // - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q) + auto proto1 = assocType1->getProtocol(); + auto proto2 = assocType2->getProtocol(); + if (int compareProtocols = TypeDecl::compare(proto1, proto2)) + return compareProtocols; + + // Error case: if we have two associated types with the same name in the + // same protocol, just tie-break based on address. + if (assocType1 != assocType2) + return assocType1 < assocType2 ? -1 : +1; + + return 0; +} + +/// Canonical ordering for type parameters. +int swift::compareDependentTypes(Type type1, Type type2) { + // Fast-path check for equality. + if (type1->isEqual(type2)) return 0; + + // Ordering is as follows: + // - Generic params + auto gp1 = type1->getAs(); + auto gp2 = type2->getAs(); + if (gp1 && gp2) + return GenericParamKey(gp1) < GenericParamKey(gp2) ? -1 : +1; + + // A generic parameter is always ordered before a nested type. + if (static_cast(gp1) != static_cast(gp2)) + return gp1 ? -1 : +1; + + // - Dependent members + auto depMemTy1 = type1->castTo(); + auto depMemTy2 = type2->castTo(); + + // - by base, so t_0_n.`P.T` < t_1_m.`P.T` + if (int compareBases = + compareDependentTypes(depMemTy1->getBase(), depMemTy2->getBase())) + return compareBases; + + // - by name, so t_n_m.`P.T` < t_n_m.`P.U` + if (int compareNames = depMemTy1->getName().str().compare( + depMemTy2->getName().str())) + return compareNames; + + auto *assocType1 = depMemTy1->getAssocType(); + auto *assocType2 = depMemTy2->getAssocType(); + if (int result = compareAssociatedTypes(assocType1, assocType2)) + return result; + + return 0; +} \ No newline at end of file diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 602cbbbca79e4..296864ec62be5 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -445,21 +445,6 @@ class GenericSignatureBuilder::ExplicitRequirement { this->rhs = type->getCanonicalType(); } - /// For a Constraint with an explicit requirement source, we recover the - /// subject type from the requirement source and the right hand side from the - /// constraint. - /// - /// The requirement kind must be passed in since Constraint's kind is - /// implicit by virtue of which list it appears under inside its equivalence - /// class, and is not stored inside the constraint. - template - static ExplicitRequirement fromExplicitConstraint(RequirementKind kind, - Constraint constraint) { - assert(!constraint.source->isDerivedNonRootRequirement()); - - return ExplicitRequirement(kind, constraint.source, constraint.value); - } - static std::pair getKindAndRHS(const RequirementSource *source, ASTContext &ctx) { @@ -532,6 +517,10 @@ class GenericSignatureBuilder::ExplicitRequirement { return std::make_pair(RequirementKind::Superclass, type); } + case RequirementSource::RequirementSignatureSelf: + return std::make_pair(RequirementKind::Conformance, + source->getProtocolDecl()); + default: { source->dump(llvm::errs(), &ctx.SourceMgr, 0); llvm::errs() << "\n"; @@ -540,49 +529,6 @@ class GenericSignatureBuilder::ExplicitRequirement { } } - static ExplicitRequirement fromRequirementSource(const RequirementSource *source, - ASTContext &ctx) { - auto *parent = source->parent; - - RequirementKind kind; - RequirementRHS rhs; - std::tie(kind, rhs) = getKindAndRHS(source, ctx); - - return ExplicitRequirement(kind, parent, rhs); - } - - /// To recover the root explicit requirement from a Constraint, we walk the - /// requirement source up the root, and look at both the root and the next - /// innermost child. - /// Together, these give us the subject type (via the root) and the right hand - /// side of the requirement (from the next innermost child). - /// - /// The requirement kind must be passed in since Constraint's kind is - /// implicit by virtue of which list it appears under inside its equivalence - /// class, and is not stored inside the constraint. - /// - /// The constraint's actual value is ignored; that's the right hand - /// side of the derived constraint, not the right hand side of the original - /// explicit requirement. - template - static ExplicitRequirement fromDerivedConstraint(Constraint constraint, - ASTContext &ctx) { - assert(constraint.source->isDerivedNonRootRequirement()); - - const RequirementSource *root = constraint.source; - const RequirementSource *child = nullptr; - - while (root->parent && root->isDerivedRequirement()) { - child = root; - root = root->parent; - } - - assert(child != nullptr); - assert(root != nullptr); - - return fromRequirementSource(child, ctx); - } - RequirementKind getKind() const { return sourceAndKind.getInt(); } @@ -661,7 +607,6 @@ template<> struct DenseMapInfo ExplicitRequirements; /// All explicit same-type requirements that were added to the builder. - SmallVector ExplicitSameTypeRequirements; + SmallVector ExplicitSameTypeRequirements; + + /// Whether we are rebuilding a signature without redundant conformance + /// requirements. + bool RebuildingWithoutRedundantConformances = false; /// A mapping of redundant explicit requirements to the best root requirement /// that implies them. Built by computeRedundantRequirements(). @@ -744,6 +696,12 @@ struct GenericSignatureBuilder::Implementation { llvm::DenseSet ExplicitConformancesImpliedByConcrete; + llvm::DenseMap, + ConformanceAccessPath> ConformanceAccessPaths; + + std::vector> + CurrentConformanceAccessPaths; + #ifndef NDEBUG /// Whether we've already computed redundant requiremnts. bool computedRedundantRequirements = false; @@ -903,16 +861,16 @@ const void *RequirementSource::getOpaqueStorage1() const { const void *RequirementSource::getOpaqueStorage2() const { if (numTrailingObjects(OverloadToken()) == 1) return getTrailingObjects()[0]; - if (numTrailingObjects(OverloadToken()) == 1) - return getTrailingObjects()[0].getOpaqueValue(); + if (numTrailingObjects(OverloadToken()) == 1) + return getTrailingObjects()[0].getOpaquePointerValue(); return nullptr; } const void *RequirementSource::getOpaqueStorage3() const { if (numTrailingObjects(OverloadToken()) == 1 && - numTrailingObjects(OverloadToken()) == 1) - return getTrailingObjects()[0].getOpaqueValue(); + numTrailingObjects(OverloadToken()) == 1) + return getTrailingObjects()[0].getOpaquePointerValue(); return nullptr; } @@ -985,10 +943,8 @@ bool RequirementSource::shouldDiagnoseRedundancy(bool primary) const { } bool RequirementSource::isSelfDerivedSource(GenericSignatureBuilder &builder, - Type type, - bool &derivedViaConcrete) const { - return getMinimalConformanceSource(builder, type, /*proto=*/nullptr, - derivedViaConcrete) + Type type) const { + return getMinimalConformanceSource(builder, type, /*proto=*/nullptr) != this; } @@ -1038,10 +994,7 @@ static bool isSelfDerivedProtocolRequirementInProtocol( const RequirementSource *RequirementSource::getMinimalConformanceSource( GenericSignatureBuilder &builder, Type currentType, - ProtocolDecl *proto, - bool &derivedViaConcrete) const { - derivedViaConcrete = false; - + ProtocolDecl *proto) const { // If it's not a derived requirement, it's not self-derived. if (!isDerivedRequirement()) return this; @@ -1075,10 +1028,8 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource( return addConstraint(equivClass, proto, source); }; - bool sawProtocolRequirement = false; const ProtocolDecl *requirementSignatureSelfProto = nullptr; - Type rootType = nullptr; Optional> redundantSubpath; bool isSelfDerived = visitPotentialArchetypesAlongPath( @@ -1086,37 +1037,12 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource( switch (source->kind) { case ProtocolRequirement: case InferredProtocolRequirement: { - // Special handling for top-level requirement signature requirements; - // pretend the root type is the subject type as written in the - // protocol, and not 'Self', so that we can consider this requirement - // self-derived if it depends on one of the conformances that make - // the root type valid. - if (requirementSignatureSelfProto) { - if (source->getProtocolDecl() == requirementSignatureSelfProto && - source->parent->kind == RequirementSource::RequirementSignatureSelf) { - rootType = source->getAffectedType(); - return false; - } - } - - // Note that we've seen a protocol requirement. - sawProtocolRequirement = true; - // If the base has been made concrete, note it. auto parentEquivClass = builder.resolveEquivalenceClass(parentType, ArchetypeResolutionKind::WellFormed); assert(parentEquivClass && "Not a well-formed type?"); - if (requirementSignatureSelfProto) { - if (parentEquivClass->concreteType) - derivedViaConcrete = true; - else if (parentEquivClass->superclass && - builder.lookupConformance(parentEquivClass->superclass, - source->getProtocolDecl())) - derivedViaConcrete = true; - } - // The parent potential archetype must conform to the protocol in which // this requirement resides. Add this constraint. if (auto startOfPath = @@ -1162,7 +1088,6 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource( case Explicit: case Inferred: case NestedTypeNameMatch: - rootType = parentType; return false; } llvm_unreachable("unhandled kind"); @@ -1185,27 +1110,13 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource( redundantSubpath->first, redundantSubpath->second); return shorterSource - ->getMinimalConformanceSource(builder, currentType, proto, derivedViaConcrete); + ->getMinimalConformanceSource(builder, currentType, proto); } // It's self-derived but we don't have a redundant subpath to eliminate. if (isSelfDerived) return nullptr; - // If we haven't seen a protocol requirement, we're done. - if (!sawProtocolRequirement) return this; - - // The root might be a nested type, which implies constraints - // for each of the protocols of the associated types referenced (if any). - for (auto depMemTy = rootType->getAs(); depMemTy; - depMemTy = depMemTy->getBase()->getAs()) { - auto assocType = depMemTy->getAssocType(); - assert(assocType); - if (addTypeConstraint(depMemTy->getBase(), assocType->getProtocol(), - nullptr)) - return nullptr; - } - return this; } @@ -1221,9 +1132,9 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource( return known; \ \ unsigned size = \ - totalSizeToAlloc( \ + totalSizeToAlloc( \ NumProtocolDecls, \ - WrittenReq.isNull()? 0 : 1); \ + WrittenReq.isValid()? 1 : 0); \ void *mem = \ builder.Impl->Allocator.Allocate(size, alignof(RequirementSource)); \ auto result = new (mem) RequirementSource ConstructorArgs; \ @@ -1236,17 +1147,16 @@ const RequirementSource *RequirementSource::forAbstract( REQUIREMENT_SOURCE_FACTORY_BODY( (nodeID, Explicit, nullptr, rootType.getPointer(), nullptr, nullptr), - (Explicit, rootType, nullptr, WrittenRequirementLoc()), - 0, WrittenRequirementLoc()); + (Explicit, rootType, nullptr, SourceLoc()), + 0, SourceLoc()); } const RequirementSource *RequirementSource::forExplicit( GenericSignatureBuilder &builder, - Type rootType, - GenericSignatureBuilder::WrittenRequirementLoc writtenLoc) { + Type rootType, SourceLoc writtenLoc) { REQUIREMENT_SOURCE_FACTORY_BODY( (nodeID, Explicit, nullptr, rootType.getPointer(), - writtenLoc.getOpaqueValue(), nullptr), + writtenLoc.getOpaquePointerValue(), nullptr), (Explicit, rootType, nullptr, writtenLoc), 0, writtenLoc); } @@ -1254,11 +1164,10 @@ const RequirementSource *RequirementSource::forExplicit( const RequirementSource *RequirementSource::forInferred( GenericSignatureBuilder &builder, Type rootType, - const TypeRepr *typeRepr) { - WrittenRequirementLoc writtenLoc = typeRepr; + SourceLoc writtenLoc) { REQUIREMENT_SOURCE_FACTORY_BODY( (nodeID, Inferred, nullptr, rootType.getPointer(), - writtenLoc.getOpaqueValue(), nullptr), + writtenLoc.getOpaquePointerValue(), nullptr), (Inferred, rootType, nullptr, writtenLoc), 0, writtenLoc); } @@ -1271,8 +1180,8 @@ const RequirementSource *RequirementSource::forRequirementSignature( (nodeID, RequirementSignatureSelf, nullptr, rootType.getPointer(), protocol, nullptr), (RequirementSignatureSelf, rootType, protocol, - WrittenRequirementLoc()), - 1, WrittenRequirementLoc()); + SourceLoc()), + 1, SourceLoc()); } @@ -1283,22 +1192,20 @@ const RequirementSource *RequirementSource::forNestedTypeNameMatch( (nodeID, NestedTypeNameMatch, nullptr, rootType.getPointer(), nullptr, nullptr), (NestedTypeNameMatch, rootType, nullptr, - WrittenRequirementLoc()), - 0, WrittenRequirementLoc()); + SourceLoc()), + 0, SourceLoc()); } const RequirementSource *RequirementSource::viaProtocolRequirement( GenericSignatureBuilder &builder, Type dependentType, - ProtocolDecl *protocol, - bool inferred, - GenericSignatureBuilder::WrittenRequirementLoc writtenLoc) const { + ProtocolDecl *protocol, bool inferred, SourceLoc writtenLoc) const { REQUIREMENT_SOURCE_FACTORY_BODY( (nodeID, inferred ? InferredProtocolRequirement : ProtocolRequirement, this, dependentType.getPointer(), protocol, - writtenLoc.getOpaqueValue()), + writtenLoc.getOpaquePointerValue()), (inferred ? InferredProtocolRequirement : ProtocolRequirement, this, dependentType, @@ -1313,7 +1220,7 @@ const RequirementSource *RequirementSource::viaSuperclass( (nodeID, Superclass, this, conformance.getOpaqueValue(), nullptr, nullptr), (Superclass, this, conformance), - 0, WrittenRequirementLoc()); + 0, SourceLoc()); } const RequirementSource *RequirementSource::viaConcrete( @@ -1323,7 +1230,7 @@ const RequirementSource *RequirementSource::viaConcrete( (nodeID, Concrete, this, conformance.getOpaqueValue(), nullptr, nullptr), (Concrete, this, conformance), - 0, WrittenRequirementLoc()); + 0, SourceLoc()); } const RequirementSource *RequirementSource::viaConcrete( @@ -1334,7 +1241,7 @@ const RequirementSource *RequirementSource::viaConcrete( (nodeID, Concrete, this, existentialType.getPointer(), nullptr, nullptr), (Concrete, this, existentialType), - 0, WrittenRequirementLoc()); + 0, SourceLoc()); } const RequirementSource *RequirementSource::viaParent( @@ -1343,7 +1250,7 @@ const RequirementSource *RequirementSource::viaParent( REQUIREMENT_SOURCE_FACTORY_BODY( (nodeID, Parent, this, assocType, nullptr, nullptr), (Parent, this, assocType), - 0, WrittenRequirementLoc()); + 0, SourceLoc()); } const RequirementSource *RequirementSource::viaLayout( @@ -1353,7 +1260,7 @@ const RequirementSource *RequirementSource::viaLayout( (nodeID, Layout, this, superclass.getPointer(), nullptr, nullptr), (Layout, this, superclass), - 0, WrittenRequirementLoc()); + 0, SourceLoc()); } const RequirementSource *RequirementSource::viaEquivalentType( @@ -1363,7 +1270,7 @@ const RequirementSource *RequirementSource::viaEquivalentType( (nodeID, EquivalentType, this, newType.getPointer(), nullptr, nullptr), (EquivalentType, this, newType), - 0, WrittenRequirementLoc()); + 0, SourceLoc()); } #undef REQUIREMENT_SOURCE_FACTORY_BODY @@ -1400,13 +1307,13 @@ const RequirementSource *RequirementSource::withoutRedundantSubpath( return parent->withoutRedundantSubpath(builder, start, end) ->viaProtocolRequirement(builder, getStoredType(), getProtocolDecl(), /*inferred=*/false, - getWrittenRequirementLoc()); + getSourceLoc()); case InferredProtocolRequirement: return parent->withoutRedundantSubpath(builder, start, end) ->viaProtocolRequirement(builder, getStoredType(), getProtocolDecl(), /*inferred=*/true, - getWrittenRequirementLoc()); + getSourceLoc()); case Concrete: if (auto existentialType = getStoredType()) { @@ -1556,11 +1463,9 @@ SourceLoc RequirementSource::getLoc() const { parent->kind != RequirementSignatureSelf) return parent->getLoc(); - if (auto typeRepr = getTypeRepr()) - return typeRepr->getStartLoc(); - - if (auto requirementRepr = getRequirementRepr()) - return requirementRepr->getSeparatorLoc(); + auto loc = getSourceLoc(); + if (loc.isValid()) + return loc; if (parent) return parent->getLoc(); @@ -1711,8 +1616,8 @@ void RequirementSource::print(llvm::raw_ostream &out, break; } - if (getTypeRepr() || getRequirementRepr()) { - dumpSourceLoc(getLoc()); + if (auto loc = getLoc()) { + dumpSourceLoc(loc); } } @@ -1742,40 +1647,34 @@ const RequirementSource *FloatingRequirementSource::getSource( ResolvedType type) const { switch (kind) { case Resolved: - return storage.get(); + return source; case Explicit: { auto depType = type.getDependentType(builder); - - if (auto requirementRepr = storage.dyn_cast()) - return RequirementSource::forExplicit(builder, depType, requirementRepr); - if (auto typeRepr = storage.dyn_cast()) - return RequirementSource::forExplicit(builder, depType, typeRepr); - return RequirementSource::forAbstract(builder, depType); + return RequirementSource::forExplicit(builder, depType, loc); } case Inferred: { auto depType = type.getDependentType(builder); - return RequirementSource::forInferred(builder, depType, - storage.get()); + return RequirementSource::forInferred(builder, depType, loc); } - case AbstractProtocol: { + case ProtocolRequirement: + case InferredProtocolRequirement: { auto depType = type.getDependentType(); // Derive the dependent type on which this requirement was written. It is // the path from the requirement source on which this requirement is based // to the potential archetype on which the requirement is being placed. - auto baseSource = storage.get(); - auto baseSourceType = baseSource->getAffectedType(); + auto baseSourceType = source->getAffectedType(); auto dependentType = - formProtocolRelativeType(protocolReq.protocol, baseSourceType, depType); + formProtocolRelativeType(protocol, baseSourceType, depType); - return storage.get() - ->viaProtocolRequirement(builder, dependentType, - protocolReq.protocol, protocolReq.inferred, - protocolReq.written); + return source + ->viaProtocolRequirement(builder, dependentType, protocol, + kind == InferredProtocolRequirement, + loc); } case NestedTypeNameMatch: { @@ -1784,29 +1683,18 @@ const RequirementSource *FloatingRequirementSource::getSource( } } - llvm_unreachable("Unhandled FloatingPointRequirementSourceKind in switch."); + llvm_unreachable("unhandled kind"); } SourceLoc FloatingRequirementSource::getLoc() const { - // For an explicit abstract protocol source, we can get a more accurate source - // location from the written protocol requirement. - if (kind == Kind::AbstractProtocol && isExplicit()) { - auto written = protocolReq.written; - if (auto typeRepr = written.dyn_cast()) - return typeRepr->getLoc(); - if (auto requirementRepr = written.dyn_cast()) - return requirementRepr->getSeparatorLoc(); + if (kind == Inferred || isExplicit()) { + if (loc.isValid()) + return loc; } - if (auto source = storage.dyn_cast()) + if (source) return source->getLoc(); - if (auto typeRepr = storage.dyn_cast()) - return typeRepr->getLoc(); - - if (auto requirementRepr = storage.dyn_cast()) - return requirementRepr->getSeparatorLoc(); - return SourceLoc(); } @@ -1817,8 +1705,9 @@ bool FloatingRequirementSource::isDerived() const { case NestedTypeNameMatch: return false; - case AbstractProtocol: - switch (storage.get()->kind) { + case ProtocolRequirement: + case InferredProtocolRequirement: + switch (source->kind) { case RequirementSource::RequirementSignatureSelf: return false; @@ -1836,7 +1725,7 @@ bool FloatingRequirementSource::isDerived() const { } case Resolved: - return storage.get()->isDerivedRequirement(); + return source->isDerivedRequirement(); } llvm_unreachable("unhandled kind"); } @@ -1850,14 +1739,14 @@ bool FloatingRequirementSource::isExplicit() const { case NestedTypeNameMatch: return false; - case AbstractProtocol: + case ProtocolRequirement: // Requirements implied by other protocol conformance requirements are // implicit, except when computing a requirement signature, where // non-inferred ones are explicit, to allow flagging of redundant // requirements. - switch (storage.get()->kind) { + switch (source->kind) { case RequirementSource::RequirementSignatureSelf: - return !protocolReq.inferred; + return true; case RequirementSource::Concrete: case RequirementSource::Explicit: @@ -1872,14 +1761,16 @@ bool FloatingRequirementSource::isExplicit() const { return false; } + case InferredProtocolRequirement: + return false; + case Resolved: - switch (storage.get()->kind) { + switch (source->kind) { case RequirementSource::Explicit: return true; case RequirementSource::ProtocolRequirement: - return storage.get()->parent->kind - == RequirementSource::RequirementSignatureSelf; + return source->parent->kind == RequirementSource::RequirementSignatureSelf; case RequirementSource::Inferred: case RequirementSource::InferredProtocolRequirement: @@ -1899,19 +1790,19 @@ bool FloatingRequirementSource::isExplicit() const { FloatingRequirementSource FloatingRequirementSource::asInferred( const TypeRepr *typeRepr) const { + auto loc = typeRepr ? typeRepr->getStartLoc() : SourceLoc(); switch (kind) { case Explicit: - return forInferred(typeRepr); + return forInferred(loc); case Inferred: case Resolved: case NestedTypeNameMatch: return *this; - case AbstractProtocol: - return viaProtocolRequirement(storage.get(), - protocolReq.protocol, typeRepr, - /*inferred=*/true); + case ProtocolRequirement: + case InferredProtocolRequirement: + return viaProtocolRequirement(source, protocol, loc, /*inferred=*/true); } llvm_unreachable("unhandled kind"); } @@ -1919,8 +1810,8 @@ FloatingRequirementSource FloatingRequirementSource::asInferred( bool FloatingRequirementSource::isRecursive( GenericSignatureBuilder &builder) const { llvm::SmallSet, 32> visitedAssocReqs; - for (auto storedSource = storage.dyn_cast(); - storedSource; storedSource = storedSource->parent) { + for (auto storedSource = source; storedSource; + storedSource = storedSource->parent) { // FIXME: isRecursive() is completely misnamed if (storedSource->kind == RequirementSource::EquivalentType) return true; @@ -2088,35 +1979,6 @@ bool EquivalenceClass::isConformanceSatisfiedBySuperclass( return false; } -/// Compare two associated types. -static int compareAssociatedTypes(AssociatedTypeDecl *assocType1, - AssociatedTypeDecl *assocType2) { - // - by name. - if (int result = assocType1->getName().str().compare( - assocType2->getName().str())) - return result; - - // Prefer an associated type with no overrides (i.e., an anchor) to one - // that has overrides. - bool hasOverridden1 = !assocType1->getOverriddenDecls().empty(); - bool hasOverridden2 = !assocType2->getOverriddenDecls().empty(); - if (hasOverridden1 != hasOverridden2) - return hasOverridden1 ? +1 : -1; - - // - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q) - auto proto1 = assocType1->getProtocol(); - auto proto2 = assocType2->getProtocol(); - if (int compareProtocols = TypeDecl::compare(proto1, proto2)) - return compareProtocols; - - // Error case: if we have two associated types with the same name in the - // same protocol, just tie-break based on address. - if (assocType1 != assocType2) - return assocType1 < assocType2 ? -1 : +1; - - return 0; -} - static void lookupConcreteNestedType(NominalTypeDecl *decl, Identifier name, SmallVectorImpl &concreteDecls) { @@ -2129,36 +1991,16 @@ static void lookupConcreteNestedType(NominalTypeDecl *decl, concreteDecls.push_back(cast(member)); } -static auto findBestConcreteNestedType(SmallVectorImpl &concreteDecls) { - return std::min_element(concreteDecls.begin(), concreteDecls.end(), - [](TypeDecl *type1, TypeDecl *type2) { - return TypeDecl::compare(type1, type2) < 0; - }); +static TypeDecl *findBestConcreteNestedType(SmallVectorImpl &concreteDecls) { + return *std::min_element(concreteDecls.begin(), concreteDecls.end(), + [](TypeDecl *type1, TypeDecl *type2) { + return TypeDecl::compare(type1, type2) < 0; + }); } TypeDecl *EquivalenceClass::lookupNestedType( GenericSignatureBuilder &builder, - Identifier name, - SmallVectorImpl *otherConcreteTypes) { - // Populates the result structures from the given cache entry. - auto populateResult = [&](const CachedNestedType &cache) -> TypeDecl * { - if (otherConcreteTypes) - otherConcreteTypes->clear(); - - // If there aren't any types in the cache, we're done. - if (cache.types.empty()) return nullptr; - - // The first type in the cache is always the final result. - // Collect the rest in the concrete-declarations list, if needed. - if (otherConcreteTypes) { - for (auto type : ArrayRef(cache.types).slice(1)) { - otherConcreteTypes->push_back(type); - } - } - - return cache.types.front(); - }; - + Identifier name) { // If we have a cached value that is up-to-date, use that. auto cached = nestedTypeNameCache.find(name); if (cached != nestedTypeNameCache.end() && @@ -2168,7 +2010,7 @@ TypeDecl *EquivalenceClass::lookupNestedType( (!concreteType || cached->second.concreteTypePresent == concreteType->getCanonicalType())) { ++NumNestedTypeCacheHits; - return populateResult(cached->second); + return cached->second.type; } // Cache miss; go compute the result. @@ -2221,24 +2063,16 @@ TypeDecl *EquivalenceClass::lookupNestedType( entry.concreteTypePresent = concreteType ? concreteType->getCanonicalType() : CanType(); if (bestAssocType) { - entry.types.push_back(bestAssocType); - entry.types.insert(entry.types.end(), - concreteDecls.begin(), concreteDecls.end()); + entry.type = bestAssocType; assert(bestAssocType->getOverriddenDecls().empty() && "Lookup should never keep a non-anchor associated type"); } else if (!concreteDecls.empty()) { // Find the best concrete type. - auto bestConcreteTypeIter = findBestConcreteNestedType(concreteDecls); - - // Put the best concrete type first; the rest will follow. - entry.types.push_back(*bestConcreteTypeIter); - entry.types.insert(entry.types.end(), - concreteDecls.begin(), bestConcreteTypeIter); - entry.types.insert(entry.types.end(), - bestConcreteTypeIter + 1, concreteDecls.end()); + entry.type = findBestConcreteNestedType(concreteDecls); } - return populateResult((nestedTypeNameCache[name] = std::move(entry))); + nestedTypeNameCache[name] = entry; + return entry.type; } static Type getSugaredDependentType(Type type, @@ -2306,127 +2140,6 @@ Type EquivalenceClass::getAnchor( return substAnchor(); } -Type EquivalenceClass::getTypeInContext(GenericSignatureBuilder &builder, - GenericEnvironment *genericEnv) { - auto genericParams = genericEnv->getGenericParams(); - - // The anchor descr - Type anchor = getAnchor(builder, genericParams); - - // If this equivalence class is mapped to a concrete type, produce that - // type. - if (concreteType) { - if (recursiveConcreteType) - return ErrorType::get(anchor); - - // Prevent recursive substitution. - this->recursiveConcreteType = true; - SWIFT_DEFER { - this->recursiveConcreteType = false; - }; - - return genericEnv->mapTypeIntoContext(concreteType, - builder.getLookupConformanceFn()); - } - - // Local function to check whether we have a generic parameter that has - // already been recorded - auto getAlreadyRecoveredGenericParam = [&]() -> Type { - auto genericParam = anchor->getAs(); - if (!genericParam) return Type(); - - auto type = genericEnv->getMappingIfPresent(genericParam); - if (!type) return Type(); - - // We already have a mapping for this generic parameter in the generic - // environment. Return it. - return *type; - }; - - AssociatedTypeDecl *assocType = nullptr; - ArchetypeType *parentArchetype = nullptr; - if (auto depMemTy = anchor->getAs()) { - // Resolve the equivalence class of the parent. - auto parentEquivClass = - builder.resolveEquivalenceClass( - depMemTy->getBase(), - ArchetypeResolutionKind::CompleteWellFormed); - if (!parentEquivClass) - return ErrorType::get(anchor); - - // Map the parent type into this context. - parentArchetype = - parentEquivClass->getTypeInContext(builder, genericEnv) - ->castTo(); - - // If we already have a nested type with this name, return it. - assocType = depMemTy->getAssocType(); - if (auto nested = - parentArchetype->getNestedTypeIfKnown(assocType->getName())) { - return *nested; - } - - // We will build the archetype below. - } else if (auto result = getAlreadyRecoveredGenericParam()) { - // Return already-contextualized generic type parameter. - return result; - } - - // Substitute into the superclass. - Type superclass = this->recursiveSuperclassType ? Type() : this->superclass; - if (superclass && superclass->hasTypeParameter()) { - // Prevent recursive substitution. - this->recursiveSuperclassType = true; - SWIFT_DEFER { - this->recursiveSuperclassType = false; - }; - - superclass = genericEnv->mapTypeIntoContext( - superclass, - builder.getLookupConformanceFn()); - if (superclass->is()) - superclass = Type(); - - // We might have recursively recorded the archetype; if so, return early. - // FIXME: This should be detectable before we end up building archetypes. - if (auto result = getAlreadyRecoveredGenericParam()) - return result; - } - - // Build a new archetype. - - // Collect the protocol conformances for the archetype. - SmallVector protos; - for (const auto &conforms : conformsTo) { - auto proto = conforms.first; - - if (!isConformanceSatisfiedBySuperclass(proto)) - protos.push_back(proto); - } - - ArchetypeType *archetype; - ASTContext &ctx = builder.getASTContext(); - if (parentArchetype) { - // Create a nested archetype. - auto *depMemTy = anchor->castTo(); - archetype = NestedArchetypeType::getNew(ctx, parentArchetype, depMemTy, - protos, superclass, layout); - - // Register this archetype with its parent. - parentArchetype->registerNestedType(assocType->getName(), archetype); - } else { - // Create a top-level archetype. - auto genericParam = anchor->castTo(); - archetype = PrimaryArchetypeType::getNew(ctx, genericEnv, genericParam, - protos, superclass, layout); - - // Register the archetype with the generic environment. - genericEnv->addMapping(genericParam, archetype); - } - - return archetype; -} - void EquivalenceClass::dump(llvm::raw_ostream &out, GenericSignatureBuilder *builder) const { auto dumpSource = [&](const RequirementSource *source) { @@ -2522,7 +2235,7 @@ void DelayedRequirement::dump(llvm::raw_ostream &out) const { case Type: case Layout: out << ": "; - break; + break; case SameType: out << " == "; @@ -2584,28 +2297,25 @@ ConstraintResult GenericSignatureBuilder::handleUnresolvedRequirement( llvm_unreachable("unhandled handling"); } -bool GenericSignatureBuilder::addConditionalRequirements( - ProtocolConformanceRef conformance, ModuleDecl *inferForModule, - SourceLoc loc) { +void GenericSignatureBuilder::addConditionalRequirements( + ProtocolConformanceRef conformance, ModuleDecl *inferForModule) { + // When rebuilding a signature, don't add requirements inferred from conditional + // conformances, since we've already added them to our ExplicitRequirements list. + // + // FIXME: We need to handle conditional conformances earlier, in the same place + // as other forms of requirement inference, to eliminate this bit of state. + if (Impl->RebuildingWithoutRedundantConformances) + return; + // Abstract conformances don't have associated decl-contexts/modules, but also // don't have conditional requirements. if (conformance.isConcrete()) { - if (auto condReqs = conformance.getConditionalRequirementsIfAvailable()) { - auto source = FloatingRequirementSource::forInferred(nullptr); - for (auto requirement : *condReqs) { - addRequirement(requirement, source, inferForModule); - ++NumConditionalRequirementsAdded; - } - } else { - if (loc.isValid()) - Diags.diagnose(loc, diag::unsupported_recursive_requirements); - - Impl->HadAnyError = true; - return true; + auto source = FloatingRequirementSource::forInferred(SourceLoc()); + for (auto requirement : conformance.getConditionalRequirements()) { + addRequirement(requirement, source, inferForModule); + ++NumConditionalRequirementsAdded; } } - - return false; } const RequirementSource * @@ -2672,9 +2382,7 @@ GenericSignatureBuilder::resolveConcreteConformance(ResolvedType type, }); if (hasExplicitSource) { - if (addConditionalRequirements(conformance, /*inferForModule=*/nullptr, - concreteSource->getLoc())) - return nullptr; + addConditionalRequirements(conformance, /*inferForModule=*/nullptr); } return concreteSource; @@ -2715,9 +2423,7 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance( }); if (hasExplicitSource) { - if (addConditionalRequirements(conformance, /*inferForModule=*/nullptr, - superclassSource->getLoc())) - return nullptr; + addConditionalRequirements(conformance, /*inferForModule=*/nullptr); } return superclassSource; @@ -2776,44 +2482,6 @@ auto PotentialArchetype::getRepresentative() const -> PotentialArchetype * { return result; } -/// Canonical ordering for dependent types. -int swift::compareDependentTypes(Type type1, Type type2) { - // Fast-path check for equality. - if (type1->isEqual(type2)) return 0; - - // Ordering is as follows: - // - Generic params - auto gp1 = type1->getAs(); - auto gp2 = type2->getAs(); - if (gp1 && gp2) - return GenericParamKey(gp1) < GenericParamKey(gp2) ? -1 : +1; - - // A generic parameter is always ordered before a nested type. - if (static_cast(gp1) != static_cast(gp2)) - return gp1 ? -1 : +1; - - // - Dependent members - auto depMemTy1 = type1->castTo(); - auto depMemTy2 = type2->castTo(); - - // - by base, so t_0_n.`P.T` < t_1_m.`P.T` - if (int compareBases = - compareDependentTypes(depMemTy1->getBase(), depMemTy2->getBase())) - return compareBases; - - // - by name, so t_n_m.`P.T` < t_n_m.`P.U` - if (int compareNames = depMemTy1->getName().str().compare( - depMemTy2->getName().str())) - return compareNames; - - auto *assocType1 = depMemTy1->getAssocType(); - auto *assocType2 = depMemTy2->getAssocType(); - if (int result = compareAssociatedTypes(assocType1, assocType2)) - return result; - - return 0; -} - /// Compare two dependent paths to determine which is better. static int compareDependentPaths(ArrayRef path1, ArrayRef path2) { @@ -2877,6 +2545,7 @@ static void concretizeNestedTypeFromConcreteParent( assert(parentEquiv->conformsTo.count(proto) > 0 && "No conformance requirement"); const RequirementSource *parentConcreteSource = nullptr; + for (const auto &constraint : parentEquiv->conformsTo.find(proto)->second) { if (!isSuperclassConstrained) { if (constraint.source->kind == RequirementSource::Concrete) { @@ -2969,38 +2638,6 @@ PotentialArchetype *PotentialArchetype::getOrCreateNestedType( return resultPA; } -void ArchetypeType::resolveNestedType( - std::pair &nested) const { - auto genericEnv = getGenericEnvironment(); - auto &builder = *genericEnv->getGenericSignatureBuilder(); - - Type interfaceType = getInterfaceType(); - Type memberInterfaceType = - DependentMemberType::get(interfaceType, nested.first); - auto resolved = - builder.maybeResolveEquivalenceClass( - memberInterfaceType, - ArchetypeResolutionKind::CompleteWellFormed, - /*wantExactPotentialArchetype=*/false); - if (!resolved) { - nested.second = ErrorType::get(interfaceType); - return; - } - - Type result; - if (auto concrete = resolved.getAsConcreteType()) { - result = concrete; - } else { - auto *equivClass = resolved.getEquivalenceClass(builder); - result = equivClass->getTypeInContext(builder, genericEnv); - } - - assert(!nested.second || - nested.second->isEqual(result) || - (nested.second->hasError() && result->hasError())); - nested.second = result; -} - Type GenericSignatureBuilder::PotentialArchetype::getDependentType( TypeArrayView genericParams) const { auto depType = getDependentType(); @@ -3944,8 +3581,7 @@ ResolvedType GenericSignatureBuilder::maybeResolveEquivalenceClass( if (concreteDecls.empty()) return ResolvedType::forUnresolved(nullptr); - auto bestConcreteTypeIter = findBestConcreteNestedType(concreteDecls); - concreteDecl = *bestConcreteTypeIter; + concreteDecl = findBestConcreteNestedType(concreteDecls); } } @@ -4066,7 +3702,7 @@ auto GenericSignatureBuilder::resolve(UnresolvedType paOrT, // Determine what kind of resolution we want. ArchetypeResolutionKind resolutionKind = ArchetypeResolutionKind::WellFormed; - if (!source.isExplicit() && source.isRecursive(*this)) + if (source.isDerived() && source.isRecursive(*this)) resolutionKind = ArchetypeResolutionKind::AlreadyKnown; Type type = paOrT.get(); @@ -4125,6 +3761,127 @@ Type GenericSignatureBuilder::getCanonicalTypeInContext(Type type, }); } +ConformanceAccessPath +GenericSignatureBuilder::getConformanceAccessPath(Type type, + ProtocolDecl *protocol, + GenericSignature sig) { + auto canType = getCanonicalTypeInContext(type, { })->getCanonicalType(); + assert(canType->isTypeParameter()); + + // Check if we've already cached the result before doing anything else. + auto found = Impl->ConformanceAccessPaths.find( + std::make_pair(canType, protocol)); + if (found != Impl->ConformanceAccessPaths.end()) { + return found->second; + } + + auto *Stats = Context.Stats; + + FrontendStatsTracer tracer(Stats, "get-conformance-access-path"); + + auto recordPath = [&](CanType type, ProtocolDecl *proto, + ConformanceAccessPath path) { + // Add the path to the buffer. + Impl->CurrentConformanceAccessPaths.emplace_back(type, path); + + // Add the path to the map. + auto key = std::make_pair(type, proto); + auto inserted = Impl->ConformanceAccessPaths.insert( + std::make_pair(key, path)); + assert(inserted.second); + (void) inserted; + + if (Stats) + ++Stats->getFrontendCounters().NumConformanceAccessPathsRecorded; + }; + + // If this is the first time we're asked to look up a conformance access path, + // visit all of the root conformance requirements in our generic signature and + // add them to the buffer. + if (Impl->ConformanceAccessPaths.empty()) { + for (const auto &req : sig.getRequirements()) { + // We only care about conformance requirements. + if (req.getKind() != RequirementKind::Conformance) + continue; + + auto rootType = req.getFirstType()->getCanonicalType(); + auto *rootProto = req.getProtocolDecl(); + + ConformanceAccessPath::Entry root(rootType, rootProto); + ArrayRef path(root); + ConformanceAccessPath result(Context.AllocateCopy(path)); + + recordPath(rootType, rootProto, result); + } + } + + // We enumerate conformance access paths in lexshort order until we find the + // path whose corresponding type canonicalizes to the one we are looking for. + while (true) { + auto found = Impl->ConformanceAccessPaths.find( + std::make_pair(canType, protocol)); + if (found != Impl->ConformanceAccessPaths.end()) { + return found->second; + } + + assert(Impl->CurrentConformanceAccessPaths.size() > 0); + + // The buffer consists of all conformance access paths of length N. + // Swap it out with an empty buffer, and fill it with all paths of + // length N+1. + std::vector> oldPaths; + std::swap(Impl->CurrentConformanceAccessPaths, oldPaths); + + for (const auto &pair : oldPaths) { + const auto &lastElt = pair.second.back(); + auto *lastProto = lastElt.second; + + // A copy of the current path, populated as needed. + SmallVector entries; + + for (const auto &req : lastProto->getRequirementSignature()) { + // We only care about conformance requirements. + if (req.getKind() != RequirementKind::Conformance) + continue; + + auto nextSubjectType = req.getFirstType()->getCanonicalType(); + auto *nextProto = req.getProtocolDecl(); + + // Compute the canonical anchor for this conformance requirement. + auto nextType = replaceSelfWithType(pair.first, nextSubjectType); + auto nextCanType = getCanonicalTypeInContext(nextType, { }) + ->getCanonicalType(); + + // Skip "derived via concrete" sources. + if (!nextCanType->isTypeParameter()) + continue; + + // If we've already seen a path for this conformance, skip it and + // don't add it to the buffer. Note that because we iterate over + // conformance access paths in lexshort order, the existing + // conformance access path is shorter than the one we found just now. + if (Impl->ConformanceAccessPaths.count( + std::make_pair(nextCanType, nextProto))) + continue; + + if (entries.empty()) { + // Fill our temporary vector. + entries.insert(entries.begin(), + pair.second.begin(), + pair.second.end()); + } + + // Add the next entry. + entries.emplace_back(nextSubjectType, nextProto); + ConformanceAccessPath result = Context.AllocateCopy(entries); + entries.pop_back(); + + recordPath(nextCanType, nextProto, result); + } + } + } +} + TypeArrayView GenericSignatureBuilder::getGenericParams() const { return TypeArrayView(Impl->GenericParams); @@ -4196,8 +3953,9 @@ static ConstraintResult visitInherited( ASTContext &ctx = typeDecl ? typeDecl->getASTContext() : extDecl->getASTContext(); auto &evaluator = ctx.evaluator; - ArrayRef inheritedTypes = typeDecl ? typeDecl->getInherited() - : extDecl->getInherited(); + ArrayRef inheritedTypes = + typeDecl ? typeDecl->getInherited() + : extDecl->getInherited(); for (unsigned index : indices(inheritedTypes)) { Type inheritedType = evaluateOrDefault(evaluator, @@ -4261,9 +4019,9 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( return false; auto innerSource = FloatingRequirementSource::viaProtocolRequirement( - source, proto, reqRepr, /*inferred=*/false); - addRequirement(req, reqRepr, innerSource, &protocolSubMap, - nullptr); + source, proto, reqRepr->getSeparatorLoc(), /*inferred=*/false); + addRequirement(req, reqRepr, innerSource, + &protocolSubMap, nullptr); return false; }); @@ -4373,7 +4131,7 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( auto inferredSameTypeSource = FloatingRequirementSource::viaProtocolRequirement( - source, proto, WrittenRequirementLoc(), /*inferred=*/true); + source, proto, SourceLoc(), /*inferred=*/true); auto rawReq = Requirement(RequirementKind::SameType, firstType, secondType); if (auto req = rawReq.subst(protocolSubMap)) @@ -4403,7 +4161,7 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( return false; auto innerSource = FloatingRequirementSource::viaProtocolRequirement( - source, proto, reqRepr, /*inferred=*/false); + source, proto, reqRepr->getSeparatorLoc(), /*inferred=*/false); addRequirement(req, reqRepr, innerSource, &protocolSubMap, /*inferForModule=*/nullptr); return false; @@ -4669,7 +4427,7 @@ bool GenericSignatureBuilder::updateSuperclass( auto layout = LayoutConstraint::getLayoutConstraint( - superclass->getClassOrBoundGenericClass()->isObjC() + superclass->getClassOrBoundGenericClass()->usesObjCObjectModel() ? LayoutConstraintKind::Class : LayoutConstraintKind::NativeClass, getASTContext()); @@ -4807,9 +4565,7 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement( if (conformance) { // Only infer conditional requirements from explicit sources. if (!source.isDerived()) { - if (addConditionalRequirements(conformance, inferForModule, - source.getLoc())) - return ConstraintResult::Conflicting; + addConditionalRequirements(conformance, inferForModule); } } } @@ -4926,7 +4682,7 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters( if (!source->isDerivedRequirement()) { Impl->ExplicitSameTypeRequirements.emplace_back(RequirementKind::SameType, - depType1, depType2); + source, depType1); } // Record the same-type constraint, and bail out if it was already known. @@ -5043,6 +4799,9 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters( equivClass->concreteTypeConstraints.end(), equivClass2->concreteTypeConstraints.begin(), equivClass2->concreteTypeConstraints.end()); + + for (const auto &conforms : equivClass->conformsTo) + (void)resolveConcreteConformance(T1, conforms.first); } // Make T1 the representative of T2, merging the equivalence classes. @@ -5057,7 +4816,7 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters( updateLayout(T1, equivClass2->layout); equivClass->layoutConstraints.insert( equivClass->layoutConstraints.end(), - equivClass2->layoutConstraints.begin() + 1, + equivClass2->layoutConstraints.begin(), equivClass2->layoutConstraints.end()); } } @@ -5153,8 +4912,7 @@ ConstraintResult GenericSignatureBuilder::addSameTypeRequirementToConcrete( const RequirementSource *source) { if (!source->isDerivedRequirement()) { Impl->ExplicitSameTypeRequirements.emplace_back(RequirementKind::SameType, - type.getDependentType(*this), - concrete); + source, concrete); } auto equivClass = type.getEquivalenceClass(*this); @@ -5328,29 +5086,27 @@ ConstraintResult GenericSignatureBuilder::addInheritedRequirements( ModuleDecl *inferForModule) { // Local function to get the source. auto getFloatingSource = [&](const TypeRepr *typeRepr, bool forInferred) { + auto loc = typeRepr ? typeRepr->getStartLoc() : SourceLoc(); + if (parentSource) { + ProtocolDecl *proto; if (auto assocType = dyn_cast(decl)) { - auto proto = assocType->getProtocol(); - return FloatingRequirementSource::viaProtocolRequirement( - parentSource, proto, typeRepr, forInferred); + proto = assocType->getProtocol(); + } else { + proto = cast(decl); } - auto proto = cast(decl); return FloatingRequirementSource::viaProtocolRequirement( - parentSource, proto, typeRepr, forInferred); + parentSource, proto, loc, forInferred); } // We are inferring requirements. if (forInferred) { - return FloatingRequirementSource::forInferred(typeRepr); + return FloatingRequirementSource::forInferred(loc); } // Explicit requirement. - if (typeRepr) - return FloatingRequirementSource::forExplicit(typeRepr); - - // An abstract explicit requirement. - return FloatingRequirementSource::forAbstract(); + return FloatingRequirementSource::forExplicit(loc); }; auto visitType = [&](Type inheritedType, const TypeRepr *typeRepr) { @@ -5475,12 +5231,8 @@ class GenericSignatureBuilder::InferRequirementsWalker : public TypeWalker { // Infer from generic typealiases. if (auto TypeAlias = dyn_cast(ty.getPointer())) { auto decl = TypeAlias->getDecl(); - auto genericSig = decl->getGenericSignature(); - if (!genericSig) - return Action::Continue; - auto subMap = TypeAlias->getSubstitutionMap(); - for (const auto &rawReq : genericSig->getRequirements()) { + for (const auto &rawReq : decl->getGenericSignature().getRequirements()) { if (auto req = rawReq.subst(subMap)) Builder.addRequirement(*req, source, nullptr); } @@ -5554,7 +5306,7 @@ class GenericSignatureBuilder::InferRequirementsWalker : public TypeWalker { // Handle the requirements. // FIXME: Inaccurate TypeReprs. - for (const auto &rawReq : genericSig->getRequirements()) { + for (const auto &rawReq : genericSig.getRequirements()) { if (auto req = rawReq.subst(subMap)) Builder.addRequirement(*req, source, nullptr); } @@ -5580,7 +5332,8 @@ void GenericSignatureBuilder::inferRequirements( ParameterList *params) { for (auto P : *params) { inferRequirements(module, P->getInterfaceType(), - FloatingRequirementSource::forInferred(P->getTypeRepr())); + FloatingRequirementSource::forInferred( + P->getTypeRepr()->getStartLoc())); } } @@ -5649,13 +5402,13 @@ namespace { bool thisIsNonRedundantExplicit = (!constraint.source->isDerivedNonRootRequirement() && !builder.isRedundantExplicitRequirement( - ExplicitRequirement::fromExplicitConstraint( - kind, constraint))); + ExplicitRequirement( + kind, constraint.source, constraint.value))); bool representativeIsNonRedundantExplicit = (!representativeConstraint->source->isDerivedNonRootRequirement() && !builder.isRedundantExplicitRequirement( - ExplicitRequirement::fromExplicitConstraint( - kind, *representativeConstraint))); + ExplicitRequirement( + kind, representativeConstraint->source, representativeConstraint->value))); if (thisIsNonRedundantExplicit != representativeIsNonRedundantExplicit) { if (thisIsNonRedundantExplicit) @@ -5724,16 +5477,21 @@ static void expandSameTypeConstraints(GenericSignatureBuilder &builder, bool alreadyFound = false; const RequirementSource *conformsSource = nullptr; for (const auto &constraint : conforms.second) { - if (constraint.source->getAffectedType()->isEqual(dependentType)) { + auto *minimal = constraint.source->getMinimalConformanceSource( + builder, constraint.getSubjectDependentType({ }), proto); + + if (minimal == nullptr) + continue; + + if (minimal->getAffectedType()->isEqual(dependentType)) { alreadyFound = true; break; } // Capture the source for later use, skipping if (!conformsSource && - constraint.source->kind - != RequirementSource::RequirementSignatureSelf) - conformsSource = constraint.source; + minimal->kind != RequirementSource::RequirementSignatureSelf) + conformsSource = minimal; } if (alreadyFound) continue; @@ -5749,563 +5507,307 @@ static void expandSameTypeConstraints(GenericSignatureBuilder &builder, } } -static bool compareSourceLocs(SourceManager &SM, SourceLoc lhsLoc, SourceLoc rhsLoc) { - if (lhsLoc.isValid() != rhsLoc.isValid()) - return lhsLoc.isValid(); +void GenericSignatureBuilder::ExplicitRequirement::dump( + llvm::raw_ostream &out, SourceManager *SM) const { + switch (getKind()) { + case RequirementKind::Conformance: + out << "conforms_to: "; + break; + case RequirementKind::Layout: + out << "layout: "; + break; + case RequirementKind::Superclass: + out << "superclass: "; + break; + case RequirementKind::SameType: + out << "same_type: "; + break; + } - if (lhsLoc.isValid() && rhsLoc.isValid()) { - unsigned lhsBuffer = SM.findBufferContainingLoc(lhsLoc); - unsigned rhsBuffer = SM.findBufferContainingLoc(rhsLoc); + out << getSubjectType(); + if (getKind() == RequirementKind::SameType) + out << " == "; + else + out << " : "; - // If the buffers are the same, use source location ordering. - if (lhsBuffer == rhsBuffer) { - if (SM.isBeforeInBuffer(lhsLoc, rhsLoc)) - return true; - } else { - // Otherwise, order by buffer identifier. - if (SM.getIdentifierForBuffer(lhsBuffer) - .compare(SM.getIdentifierForBuffer(lhsBuffer))) - return true; - } - } + if (auto type = rhs.dyn_cast()) + out << type; + else if (auto *proto = rhs.dyn_cast()) + out << proto->getName(); + else + out << rhs.get(); - return false; + out << "(source: "; + getSource()->dump(out, SM, 0); + out << ")"; } -/// A directed graph where the vertices are explicit requirement sources and -/// an edge (v, w) records that the explicit source v implies the explicit -/// source w. -/// -/// The roots of the strongly connected component graph are the basic set of -/// explicit requirements which imply everything else. We pick the best -/// representative from each root strongly connected component to form the -/// final set of non-redundant explicit requirements. -class RedundantRequirementGraph { - /// 2**16 explicit requirements ought to be enough for everybody. - using VertexID = uint16_t; - using ComponentID = uint16_t; +static bool typeImpliesLayoutConstraint(Type t, LayoutConstraint layout) { + if (layout->isRefCounted() && t->satisfiesClassConstraint()) + return true; - struct Vertex { - ExplicitRequirement req; + return false; +} - explicit Vertex(ExplicitRequirement req) : req(req) {} +static bool typeConflictsWithLayoutConstraint(Type t, LayoutConstraint layout) { + if (layout->isClass() && !t->satisfiesClassConstraint()) + return true; - /// The SCC that this vertex belongs to. - ComponentID component = UndefinedComponent; + return false; +} - /// State for the SCC algorithm. - VertexID index = UndefinedIndex; - VertexID lowLink = -1; - bool onStack = false; +static bool isConcreteConformance(const EquivalenceClass &equivClass, + ProtocolDecl *proto, + GenericSignatureBuilder &builder) { + if (equivClass.concreteType && + builder.lookupConformance(equivClass.concreteType, proto)) { + return true; + } else if (equivClass.superclass && + builder.lookupConformance(equivClass.superclass, proto)) { + return true; + } - /// Outgoing edges. - SmallVector successors; + return false; +} -#ifndef NDEBUG - bool sawVertex = false; -#endif +void GenericSignatureBuilder::getBaseRequirements( + GenericSignatureBuilder::GetKindAndRHS getKindAndRHS, + const RequirementSource *source, + const ProtocolDecl *requirementSignatureSelfProto, + SmallVectorImpl &result) { + // If we're building a generic signature, the base requirement is + // built from the root of the path. + if (source->parent == nullptr) { + RequirementKind kind; + RequirementRHS rhs; + std::tie(kind, rhs) = getKindAndRHS(); + result.push_back(ExplicitRequirement(kind, source, rhs)); + return; + } - static const ComponentID UndefinedComponent = -1; - static const VertexID UndefinedIndex = -1; - }; + // If we're building a requirement signature, there can be multiple + // base requirements from the same protocol appearing along the path. + if (requirementSignatureSelfProto != nullptr && + source->isProtocolRequirement() && + source->getProtocolDecl() == requirementSignatureSelfProto) { + auto *shortSource = source->withoutRedundantSubpath( + *this, source->getRoot(), source->parent); + RequirementKind kind; + RequirementRHS rhs; + std::tie(kind, rhs) = getKindAndRHS(); + result.push_back(ExplicitRequirement(kind, shortSource, rhs)); + } - /// Maps each requirement source to a unique 16-bit ID. - llvm::SmallDenseMap reqs; + getBaseRequirements( + [&]() { return ExplicitRequirement::getKindAndRHS(source, Context); }, + source->parent, requirementSignatureSelfProto, result); +} - SmallVector vertices; +Optional +GenericSignatureBuilder::isValidRequirementDerivationPath( + llvm::SmallDenseSet &visited, + RequirementKind otherKind, + const RequirementSource *otherSource, + RequirementRHS otherRHS, + const ProtocolDecl *requirementSignatureSelfProto) { + if (auto *Stats = Context.Stats) + ++Stats->getFrontendCounters().NumRedundantRequirementSteps; + + SmallVector result; + getBaseRequirements( + [&]() { return std::make_pair(otherKind, otherRHS); }, + otherSource, requirementSignatureSelfProto, result); + assert(result.size() > 0); + + for (const auto &otherReq : result) { + // Don't consider paths that are based on the requirement + // itself; such a path doesn't "prove" this requirement, + // since if we drop the requirement the path is no longer + // valid. + if (visited.count(otherReq)) + return None; - ComponentID componentCount = 0; - VertexID vertexCount = 0; - - SmallVector stack; - -public: - template - void handleConstraintsImpliedByConcrete( - RequirementKind kind, - const SmallVectorImpl> &impliedByConcrete, - const Optional &concreteTypeRequirement) { - // This can occur if the combination of a superclass requirement and - // protocol conformance requirement force a type to become concrete. - if (!concreteTypeRequirement) - return; + SWIFT_DEFER { + visited.erase(otherReq); + }; + visited.insert(otherReq); - for (auto constraint : impliedByConcrete) { - if (constraint.source->isDerivedNonRootRequirement()) - continue; + auto otherSubjectType = otherReq.getSubjectType(); - auto req = ExplicitRequirement::fromExplicitConstraint(kind, constraint); - addEdge(*concreteTypeRequirement, req); - } - } + auto *equivClass = resolveEquivalenceClass(otherSubjectType, + ArchetypeResolutionKind::AlreadyKnown); + assert(equivClass && + "Explicit requirement names an unknown equivalence class?"); - template - Optional - addConstraintsFromEquivClass( - RequirementKind kind, - const SmallVectorImpl> &exact, - const SmallVectorImpl> &lessSpecific, - ASTContext &ctx) { - // The set of 'redundant explicit requirements', which are known to be less - // specific than some other explicit requirements. An example is a type - // parameter with multiple superclass constraints: - // - // class A {} - // class B : A {} - // class C : B {} + // If our requirement is based on a path involving some other + // redundant requirement, see if we can derive the redundant + // requirement using requirements we haven't visited yet. + // If not, we go ahead and drop it from consideration. // - // func f(_: T) where T : A, T : B, T : C {} + // We need to do this because sometimes, we don't record all possible + // requirement sources that derive a given requirement. // - // 'T : C' supercedes 'T : A' and 'T : B', because C is a subclass of - // 'A' and 'B'. - SmallVector redundantExplicitReqs; - - // The set of all other explicit requirements, which are known to be the most - // specific. These imply the less specific 'redundant explicit sources' - // above. - // - // Also, if there is more than one most specific explicit requirements, they - // all imply each other. - SmallVector explicitReqs; - - // The set of root explicit requirements for each derived requirements. - // - // These 'root requirements' imply the 'explicit requirements'. - SmallVector rootReqs; - - for (auto constraint : exact) { - if (constraint.source->isDerivedNonRootRequirement()) { - auto req = ExplicitRequirement::fromDerivedConstraint( - constraint, ctx); - - rootReqs.push_back(req); - } else { - auto req = ExplicitRequirement::fromExplicitConstraint( - kind, constraint); + // For example, when a nested type of a type parameter is concretized by + // adding a superclass requirement, we only record the requirement source + // for the concrete conformance the first time. A subsequently-added + // superclass requirement on the same parent type does not record a + // redundant concrete conformance for the child type. + if (isRedundantExplicitRequirement(otherReq)) { + // If we have a redundant explicit requirement source, it really is + // redundant; there's no other derivation that would not be redundant. + if (!otherSource->isDerivedNonRootRequirement()) + return None; + + switch (otherReq.getKind()) { + case RequirementKind::Conformance: { + auto *proto = otherReq.getRHS().get(); + + auto found = equivClass->conformsTo.find(proto); + assert(found != equivClass->conformsTo.end()); + + bool foundValidDerivation = false; + for (const auto &constraint : found->second) { + if (isValidRequirementDerivationPath( + visited, otherReq.getKind(), + constraint.source, proto, + requirementSignatureSelfProto)) { + foundValidDerivation = true; + break; + } + } - VertexID v = addVertex(req); -#ifndef NDEBUG - // Record that we saw an actual explicit requirement rooted at this vertex, - // for verification purposes. - vertices[v].sawVertex = true; -#endif - (void) v; + if (!foundValidDerivation) + return None; - explicitReqs.push_back(req); + break; } - } - for (auto constraint : lessSpecific) { - if (constraint.source->isDerivedNonRootRequirement()) - continue; - - auto req = ExplicitRequirement::fromExplicitConstraint(kind, constraint); - - VertexID v = addVertex(req); -#ifndef NDEBUG - // Record that we saw an actual explicit requirement rooted at this vertex, - // for verification purposes. - vertices[v].sawVertex = true; -#endif - (void) v; - - redundantExplicitReqs.push_back(req); - } - - // Get the "best" explicit requirement that implies the rest. - Optional result; - if (!rootReqs.empty()) - result = rootReqs[0]; - else if (!explicitReqs.empty()) - result = explicitReqs[0]; + case RequirementKind::Superclass: { + auto superclass = getCanonicalTypeInContext( + otherReq.getRHS().get(), { }); + + for (const auto &constraint : equivClass->superclassConstraints) { + auto otherSuperclass = getCanonicalTypeInContext( + constraint.value, { }); + + if (superclass->isExactSuperclassOf(otherSuperclass)) { + bool foundValidDerivation = false; + if (isValidRequirementDerivationPath( + visited, otherReq.getKind(), + constraint.source, otherSuperclass, + requirementSignatureSelfProto)) { + foundValidDerivation = true; + break; + } - // If all requirements are derived, there is nothing to do. - if (explicitReqs.empty()) { - if (!redundantExplicitReqs.empty()) { - assert(!rootReqs.empty()); - for (auto rootReq : rootReqs) { - for (auto redundantReq : redundantExplicitReqs) { - addEdge(rootReq, redundantReq); + if (!foundValidDerivation) + return None; } } - } - return result; - } - - // If there are multiple most specific explicit requirements, they will form a cycle. - if (explicitReqs.size() > 1) { - for (unsigned index : indices(explicitReqs)) { - auto firstReq = explicitReqs[index]; - auto secondReq = explicitReqs[(index + 1) % explicitReqs.size()]; - addEdge(firstReq, secondReq); - } - } - - // The cycle of most specific explicit requirements implies all less specific - // explicit requirements. - for (auto redundantReq : redundantExplicitReqs) { - addEdge(explicitReqs[0], redundantReq); - } - - // The root explicit requirement of each derived requirement implies the cycle of - // most specific explicit requirements. - for (auto rootReq : rootReqs) { - addEdge(rootReq, explicitReqs[0]); - } - - return result; - } - -private: - /// If we haven't seen this requirement yet, add it and - /// return a new ID. Otherwise, return an existing ID. - VertexID addVertex(ExplicitRequirement req) { - assert(!req.getSource()->isDerivedNonRootRequirement()); - - VertexID id = vertices.size(); - - // Check for wrap-around. - if (id != vertices.size()) { - llvm::errs() << "Too many explicit requirements"; - abort(); - } - assert(reqs.size() == vertices.size()); - - auto inserted = reqs.insert(std::make_pair(req, id)); - - // Check if we've already seen this vertex. - if (!inserted.second) - return inserted.first->second; - - vertices.emplace_back(req); - return id; - } - - /// Add a directed edge from one requirement to another. - /// If we haven't seen either requirement yet, we add a - /// new vertex; this allows visiting equivalence classes in - /// a single pass, without having to build the set of vertices - /// first. - void addEdge(ExplicitRequirement fromReq, - ExplicitRequirement toReq) { - assert(!fromReq.getSource()->isDerivedNonRootRequirement()); - assert(!toReq.getSource()->isDerivedNonRootRequirement()); - - VertexID from = addVertex(fromReq); - VertexID to = addVertex(toReq); - - vertices[from].successors.push_back(to); - } - - /// Tarjan's algorithm. - void computeSCCs() { - for (VertexID v : indices(vertices)) { - if (vertices[v].index == Vertex::UndefinedIndex) - strongConnect(v); - } - } - - void strongConnect(VertexID v) { - // Set the depth index for v to the smallest unused index. - assert(vertices[v].index == Vertex::UndefinedIndex); - vertices[v].index = vertexCount; - assert(vertices[v].lowLink == Vertex::UndefinedIndex); - vertices[v].lowLink = vertexCount; - - vertexCount++; - - stack.push_back(v); - assert(!vertices[v].onStack); - vertices[v].onStack = true; - - // Consider successors of v. - for (VertexID w : vertices[v].successors) { - if (vertices[w].index == Vertex::UndefinedIndex) { - // Successor w has not yet been visited; recurse on it. - strongConnect(w); - - assert(vertices[w].lowLink != Vertex::UndefinedIndex); - vertices[v].lowLink = std::min(vertices[v].lowLink, - vertices[w].lowLink); - } else if (vertices[w].onStack) { - // Successor w is in stack S and hence in the current SCC. - // - // If w is not on stack, then (v, w) is an edge pointing - // to an SCC already found and must be ignored. - assert(vertices[w].lowLink != Vertex::UndefinedIndex); - vertices[v].lowLink = std::min(vertices[v].lowLink, - vertices[w].index); + break; } - } - - // If v is a root node, pop the stack and generate an SCC. - if (vertices[v].lowLink == vertices[v].index) { - VertexID w = Vertex::UndefinedIndex; - - do { - w = stack.back(); - assert(vertices[w].onStack); - stack.pop_back(); - vertices[w].onStack = false; - assert(vertices[w].component == Vertex::UndefinedComponent); + case RequirementKind::Layout: { + auto layout = otherReq.getRHS().get(); - vertices[w].component = componentCount; - } while (v != w); + for (const auto &constraint : equivClass->layoutConstraints) { + auto otherLayout = constraint.value; - componentCount++; - } - } - -public: - using RedundantRequirementMap = - llvm::DenseMap>; + if (layout == otherLayout) { + bool foundValidDerivation = false; + if (isValidRequirementDerivationPath( + visited, otherReq.getKind(), + constraint.source, otherLayout, + requirementSignatureSelfProto)) { + foundValidDerivation = true; + break; + } - void computeRedundantRequirements(SourceManager &SM, - RedundantRequirementMap &redundant) { - // First, compute SCCs. - computeSCCs(); - - // The set of edges pointing to each connected component. - SmallVector, 2> inboundComponentEdges; - inboundComponentEdges.resize(componentCount); - - // The set of edges originating from this connected component. - SmallVector, 2> outboundComponentEdges; - outboundComponentEdges.resize(componentCount); - - // Visit all vertices and build the adjacency sets for the connected - // component graph. - for (const auto &vertex : vertices) { - assert(vertex.component != Vertex::UndefinedComponent); - for (auto successor : vertex.successors) { - ComponentID otherComponent = vertices[successor].component; - if (vertex.component != otherComponent) { - inboundComponentEdges[otherComponent].push_back(vertex.component); - outboundComponentEdges[vertex.component].push_back(otherComponent); + if (!foundValidDerivation) + return None; + } } - } - } - - auto isRootComponent = [&](ComponentID component) -> bool { - return inboundComponentEdges[component].empty(); - }; - // The set of root components. - llvm::SmallDenseSet rootComponents; - - // The best explicit requirement for each root component. - SmallVector, 2> bestExplicitReq; - bestExplicitReq.resize(componentCount); - - // Visit all vertices and find the best requirement for each root component. - for (const auto &vertex : vertices) { - if (isRootComponent(vertex.component)) { - rootComponents.insert(vertex.component); - - // If this vertex is part of a root SCC, see if the requirement is - // better than the one we have so far. - auto &best = bestExplicitReq[vertex.component]; - if (isBetterExplicitRequirement(SM, best, vertex.req)) - best = vertex.req; + break; } - } - - // The set of root components that each component is reachable from. - SmallVector, 2> reachableFromRoot; - reachableFromRoot.resize(componentCount); - // Traverse the graph of connected components starting from the roots. - for (auto rootComponent : rootComponents) { - SmallVector worklist; - - auto addToWorklist = [&](ComponentID nextComponent) { - if (!reachableFromRoot[nextComponent].count(rootComponent)) - worklist.push_back(nextComponent); - }; - - addToWorklist(rootComponent); - - while (!worklist.empty()) { - auto component = worklist.back(); - worklist.pop_back(); - - reachableFromRoot[component].insert(rootComponent); - - for (auto nextComponent : outboundComponentEdges[component]) - addToWorklist(nextComponent); + case RequirementKind::SameType: + llvm_unreachable("Should not see same type requirements here"); } } - // Compute the mapping of redundant requirements to the best root - // requirement that implies them. - for (const auto &vertex : vertices) { - if (isRootComponent(vertex.component)) { - // A root component is reachable from itself, and itself only. - assert(reachableFromRoot[vertex.component].size() == 1); - assert(reachableFromRoot[vertex.component].count(vertex.component) == 1); - } else { - assert(!bestExplicitReq[vertex.component].hasValue() && - "Recorded best requirement for non-root SCC?"); - } + auto anchor = equivClass->getAnchor(*this, { }); - // We have a non-root component. This requirement is always - // redundant. - auto reachableFromRootSet = reachableFromRoot[vertex.component]; - assert(reachableFromRootSet.size() > 0); + if (auto *depMemType = anchor->getAs()) { + // If 'req' is based on some other conformance requirement + // `T.[P.]A : Q', we want to make sure that we have a + // non-redundant derivation for 'T : P'. + auto baseType = depMemType->getBase(); + auto *proto = depMemType->getAssocType()->getProtocol(); - for (auto rootComponent : reachableFromRootSet) { - assert(isRootComponent(rootComponent)); + auto *baseEquivClass = resolveEquivalenceClass(baseType, + ArchetypeResolutionKind::AlreadyKnown); + assert(baseEquivClass && + "Explicit requirement names an unknown equivalence class?"); - auto best = bestExplicitReq[rootComponent]; - assert(best.hasValue() && - "Did not record best requirement for root SCC?"); + auto found = baseEquivClass->conformsTo.find(proto); + assert(found != baseEquivClass->conformsTo.end()); - assert(vertex.req != *best || vertex.component == rootComponent); - if (vertex.req != *best) { - redundant[vertex.req].insert(*best); + bool foundValidDerivation = false; + for (const auto &constraint : found->second) { + if (isValidRequirementDerivationPath( + visited, RequirementKind::Conformance, + constraint.source, proto, + requirementSignatureSelfProto)) { + foundValidDerivation = true; + break; } } + + if (!foundValidDerivation) + return None; } } -private: - bool isBetterExplicitRequirement(SourceManager &SM, - Optional optExisting, - ExplicitRequirement current) { - if (!optExisting.hasValue()) - return true; - - auto existing = *optExisting; - - auto *existingSource = existing.getSource(); - auto *currentSource = current.getSource(); - - // Prefer explicit sources over inferred ones, so that you can - // write either of the following without a warning: - // - // func foo(_: Set) {} - // func foo(_: Set) {} - bool existingInferred = existingSource->isInferredRequirement(); - bool currentInferred = currentSource->isInferredRequirement(); - if (existingInferred != currentInferred) - return currentInferred; - - // Prefer a RequirementSignatureSelf over anything else. - if ((currentSource->kind == RequirementSource::RequirementSignatureSelf) || - (existingSource->kind == RequirementSource::RequirementSignatureSelf)) - return (currentSource->kind == RequirementSource::RequirementSignatureSelf); - - // Prefer sources with a shorter type. - auto existingType = existingSource->getStoredType(); - auto currentType = currentSource->getStoredType(); - int cmpTypes = compareDependentTypes(currentType, existingType); - if (cmpTypes != 0) - return cmpTypes < 0; - - auto existingLoc = existingSource->getLoc(); - auto currentLoc = currentSource->getLoc(); + return result.front(); +} - return compareSourceLocs(SM, currentLoc, existingLoc); - } +template +void GenericSignatureBuilder::checkIfRequirementCanBeDerived( + const ExplicitRequirement &req, + const std::vector> &constraints, + const ProtocolDecl *requirementSignatureSelfProto, + Filter filter) { + assert(!constraints.empty()); -public: - void dump(llvm::raw_ostream &out, SourceManager *SM) const { - out << "* Vertices:\n"; - for (const auto &vertex : vertices) { - out << "Component " << vertex.component << ", "; - vertex.req.dump(out, SM); - out << "\n"; - } + for (const auto &constraint : constraints) { + if (filter(constraint)) + continue; - out << "* Edges:\n"; - for (const auto &from : vertices) { - for (VertexID w : from.successors) { - const auto &to = vertices[w]; + // If this requirement can be derived from a set of + // non-redundant base requirements, then this requirement + // is redundant. + llvm::SmallDenseSet visited; + visited.insert(req); - from.req.dump(out, SM); - out << " -> "; - to.req.dump(out, SM); - out << "\n"; - } + if (auto representative = isValidRequirementDerivationPath( + visited, req.getKind(), + constraint.source, + constraint.value, + requirementSignatureSelfProto)) { + Impl->RedundantRequirements[req].insert(*representative); } } - -#ifndef NDEBUG - void verifyAllVerticesWereSeen(SourceManager *SM) const { - for (const auto &vertex : vertices) { - if (!vertex.sawVertex) { - // We found a vertex that was referenced by an edge, but was - // never added explicitly. This means we have a derived - // requirement rooted at the source, but we never saw the - // corresponding explicit requirement. This should not happen. - llvm::errs() << "Found an orphaned vertex:\n"; - vertex.req.dump(llvm::errs(), SM); - llvm::errs() << "\nRedundant requirement graph:\n"; - dump(llvm::errs(), SM); - abort(); - } - } - } -#endif - -}; - -void GenericSignatureBuilder::ExplicitRequirement::dump( - llvm::raw_ostream &out, SourceManager *SM) const { - switch (getKind()) { - case RequirementKind::Conformance: - out << "conforms_to: "; - break; - case RequirementKind::Layout: - out << "layout: "; - break; - case RequirementKind::Superclass: - out << "superclass: "; - break; - case RequirementKind::SameType: - out << "same_type: "; - break; - } - - out << getSubjectType() << " : "; - if (auto type = rhs.dyn_cast()) - out << type; - else if (auto *proto = rhs.dyn_cast()) - out << proto->getName(); - else - out << rhs.get(); - - out << "(source: "; - getSource()->dump(out, SM, 0); - out << ")"; -} - -static bool typeImpliesLayoutConstraint(Type t, LayoutConstraint layout) { - if (layout->isRefCounted() && t->satisfiesClassConstraint()) - return true; - - return false; } -static bool typeConflictsWithLayoutConstraint(Type t, LayoutConstraint layout) { - if (layout->isClass() && !t->satisfiesClassConstraint()) - return true; - - return false; -} +static bool involvesNonSelfSubjectTypes(const RequirementSource *source) { + while (source->kind != RequirementSource::RequirementSignatureSelf) { + if (source->isProtocolRequirement() && + !source->getStoredType()->is()) + return true; -static bool isConcreteConformance(const EquivalenceClass &equivClass, - ProtocolDecl *proto, - GenericSignatureBuilder &builder) { - if (equivClass.concreteType && - builder.lookupConformance(equivClass.concreteType, proto)) { - return true; - } else if (equivClass.superclass && - builder.lookupConformance(equivClass.superclass, proto)) { - return true; + source = source->parent; } return false; @@ -6319,262 +5821,329 @@ void GenericSignatureBuilder::computeRedundantRequirements( Impl->computedRedundantRequirements = true; #endif - RedundantRequirementGraph graph; + FrontendStatsTracer tracer(Context.Stats, + "compute-redundant-requirements"); - // Visit each equivalence class, recording all explicit requirement sources, - // and the root of each derived requirement source that implies an explicit - // source. - for (auto &equivClass : Impl->EquivalenceClasses) { - for (auto &entry : equivClass.conformsTo) { - SmallVector, 2> exact; - SmallVector, 2> lessSpecific; - - for (const auto &constraint : entry.second) { - auto *source = constraint.source; - - // FIXME: Remove this check. - bool derivedViaConcrete = false; - if (source->getMinimalConformanceSource( - *this, constraint.getSubjectDependentType({ }), entry.first, - derivedViaConcrete) - != source) - continue; + // This sort preserves iteration order with the legacy algorithm. + SmallVector requirements( + Impl->ExplicitRequirements.begin(), + Impl->ExplicitRequirements.end()); + std::stable_sort(requirements.begin(), requirements.end(), + [](const ExplicitRequirement &req1, + const ExplicitRequirement &req2) { + if (req1.getSource()->isInferredRequirement() && + !req2.getSource()->isInferredRequirement()) + return true; - if (derivedViaConcrete) - continue; + if (compareDependentTypes(req1.getSubjectType(), + req2.getSubjectType()) > 0) + return true; - // FIXME: Check for a conflict via the concrete type. - exact.push_back(constraint); + return false; + }); - if (!source->isDerivedRequirement()) { - if (isConcreteConformance(equivClass, entry.first, *this)) { - Impl->ExplicitConformancesImpliedByConcrete.insert( - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::Conformance, constraint)); - } - } - } + for (const auto &req : requirements) { + auto subjectType = req.getSubjectType(); - graph.addConstraintsFromEquivClass(RequirementKind::Conformance, - exact, lessSpecific, Context); - } + auto *equivClass = resolveEquivalenceClass(subjectType, + ArchetypeResolutionKind::AlreadyKnown); + assert(equivClass && + "Explicit requirement names an unknown equivalence class?"); Type resolvedConcreteType; - Optional concreteTypeRequirement; - - if (equivClass.concreteType) { + if (equivClass->concreteType) { resolvedConcreteType = - getCanonicalTypeInContext(equivClass.concreteType, { }); - - SmallVector, 2> exact; - SmallVector, 2> lessSpecific; - - for (const auto &constraint : equivClass.concreteTypeConstraints) { - auto *source = constraint.source; - Type t = constraint.value; - - // FIXME: Remove this check. - bool derivedViaConcrete = false; - if (source->getMinimalConformanceSource( - *this, constraint.getSubjectDependentType({ }), nullptr, - derivedViaConcrete) - != source) - continue; + getCanonicalTypeInContext(equivClass->concreteType, + getGenericParams()); + } - if (derivedViaConcrete) - continue; + Type resolvedSuperclass; + if (equivClass->superclass) { + resolvedSuperclass = + getCanonicalTypeInContext(equivClass->superclass, + getGenericParams()); + } - if (t->isEqual(resolvedConcreteType)) { - exact.push_back(constraint); - continue; - } + switch (req.getKind()) { + case RequirementKind::Conformance: { + // TODO: conflicts with concrete + auto *proto = req.getRHS().get(); - auto resolvedType = getCanonicalTypeInContext(t, { }); - if (resolvedType->isEqual(resolvedConcreteType)) { - exact.push_back(constraint); - } + auto found = equivClass->conformsTo.find(proto); + assert(found != equivClass->conformsTo.end()); - // Record the conflict. - if (!source->isDerivedRequirement()) { - auto req = - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::SameType, constraint); - Impl->ConflictingRequirements.insert( - std::make_pair(req, resolvedConcreteType)); - } + // If this is a conformance requirement on 'Self', only consider it + // redundant if it can be derived from other sources involving 'Self'. + // + // What this means in practice is that we will never try to build + // a witness table for an inherited conformance from an associated + // conformance. + // + // This is important since witness tables for inherited conformances + // are realized eagerly before the witness table for the original + // conformance, and allowing more complex requirement sources for + // inherited conformances could introduce cycles at runtime. + bool isProtocolRefinement = + (requirementSignatureSelfProto && + equivClass->getAnchor(*this, { })->is()); + + checkIfRequirementCanBeDerived( + req, found->second, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + if (isProtocolRefinement) + return involvesNonSelfSubjectTypes(constraint.source); + return false; + }); - lessSpecific.push_back(constraint); - } + if (isConcreteConformance(*equivClass, proto, *this)) + Impl->ExplicitConformancesImpliedByConcrete.insert(req); - concreteTypeRequirement = - graph.addConstraintsFromEquivClass(RequirementKind::SameType, - exact, lessSpecific, Context); + break; } - Type resolvedSuperclass; - Optional superclassRequirement; - - if (equivClass.superclass) { - // Resolve any thus-far-unresolved dependent types. - resolvedSuperclass = - getCanonicalTypeInContext(equivClass.superclass, getGenericParams()); - - SmallVector, 2> exact; - SmallVector, 2> lessSpecific; - SmallVector, 2> impliedByConcrete; - - for (const auto &constraint : equivClass.superclassConstraints) { - auto *source = constraint.source; - Type t = constraint.value; - - // FIXME: Remove this check. - bool derivedViaConcrete = false; - if (source->getMinimalConformanceSource( - *this, constraint.getSubjectDependentType({ }), nullptr, - derivedViaConcrete) - != source) - continue; + case RequirementKind::Superclass: { + auto superclass = getCanonicalTypeInContext( + req.getRHS().get(), { }); - if (derivedViaConcrete) - continue; + if (!superclass->isExactSuperclassOf(resolvedSuperclass)) { + // Case 1. We have a requirement 'T : C', and we've + // previously resolved the superclass of 'T' to some + // class 'D' where 'C' is not a superclass of 'D'. + // + // This means 'T : C' conflicts with some other + // requirement that implies 'T : D'. + Impl->ConflictingRequirements[req] = resolvedSuperclass; + + checkIfRequirementCanBeDerived( + req, equivClass->superclassConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherSuperclass = getCanonicalTypeInContext( + constraint.value, { }); + // Filter out requirements where the superclass + // is not equal to the resolved superclass. + if (!resolvedSuperclass->isEqual(otherSuperclass)) + return true; + + return false; + }); + } else { + // Case 2. We have a requirement 'T : C', and we've + // previously resolved the superclass of 'T' to some + // class 'D' where 'C' is a superclass of 'D'. + // + // This means that 'T : C' is made redundant by any + // requirement that implies 'T : B', such that 'C' is a + // superclass of 'B'. + checkIfRequirementCanBeDerived( + req, equivClass->superclassConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherSuperclass = getCanonicalTypeInContext( + constraint.value, { }); + // Filter out requirements where the superclass is less + // specific than the original requirement. + if (!superclass->isExactSuperclassOf(otherSuperclass)) + return true; + + return false; + }); if (resolvedConcreteType) { - Type resolvedType = getCanonicalTypeInContext(t, { }); - if (resolvedType->isExactSuperclassOf(resolvedConcreteType)) - impliedByConcrete.push_back(constraint); - } - - if (t->isEqual(resolvedSuperclass)) { - exact.push_back(constraint); - continue; - } - - Type resolvedType = getCanonicalTypeInContext(t, { }); - if (resolvedType->isEqual(resolvedSuperclass)) { - exact.push_back(constraint); - continue; - } - - // Check for a conflict. - if (!source->isDerivedRequirement() && - !resolvedType->isExactSuperclassOf(resolvedSuperclass)) { - auto req = - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::Superclass, constraint); - Impl->ConflictingRequirements.insert( - std::make_pair(req, resolvedSuperclass)); + // We have a superclass requirement 'T : C' and a same-type + // requirement 'T == D'. + if (resolvedSuperclass->isExactSuperclassOf(resolvedConcreteType)) { + // 'C' is a superclass of 'D', so 'T : C' is redundant. + checkIfRequirementCanBeDerived( + req, equivClass->concreteTypeConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherType = getCanonicalTypeInContext( + constraint.value, { }); + if (!resolvedSuperclass->isExactSuperclassOf(otherType)) + return true; + + return false; + }); + } else { + // 'C' is not a superclass of 'D'; we have a conflict. + Impl->ConflictingConcreteTypeRequirements.push_back( + {req, resolvedConcreteType, resolvedSuperclass}); + } } - - lessSpecific.push_back(constraint); } - superclassRequirement = - graph.addConstraintsFromEquivClass(RequirementKind::Superclass, - exact, lessSpecific, Context); - - graph.handleConstraintsImpliedByConcrete(RequirementKind::Superclass, - impliedByConcrete, - concreteTypeRequirement); + break; } - Optional layoutRequirement; - - if (equivClass.layout) { - SmallVector, 2> exact; - SmallVector, 2> lessSpecific; - SmallVector, 2> impliedByConcrete; - - for (const auto &constraint : equivClass.layoutConstraints) { - auto *source = constraint.source; - auto layout = constraint.value; + case RequirementKind::Layout: { + // TODO: less-specific constraints + // TODO: conflicts with other layout constraints + // TODO: conflicts with concrete and superclass + auto layout = req.getRHS().get(); - // FIXME: Remove this check. - bool derivedViaConcrete = false; - if (source->getMinimalConformanceSource( - *this, constraint.getSubjectDependentType({ }), nullptr, - derivedViaConcrete) - != source) - continue; + if (!layout.merge(equivClass->layout)->isKnownLayout()) { + // Case 1. We have a requirement 'T : L1', and we've + // previously resolved the layout of 'T' to some + // layout constraint 'L2', where 'L1' and 'L2' are not + // mergeable. + // + // This means that 'T : L1' conflicts with some other + // requirement that implies 'T : L2'. + Impl->ConflictingRequirements[req] = equivClass->layout; + + checkIfRequirementCanBeDerived( + req, equivClass->layoutConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherLayout = constraint.value; + // Filter out requirements where the layout is not + // equal to the resolved layout. + if (equivClass->layout != otherLayout) + return true; + + return false; + }); + } else if (layout == equivClass->layout) { + // Case 2. We have a requirement 'T : L1', and we know + // the most specific resolved layout for 'T' is also 'L1'. + // + // This means that 'T : L1' is made redundant by any + // requirement that implies 'T : L1'. + checkIfRequirementCanBeDerived( + req, equivClass->layoutConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + // Filter out requirements where the implied layout is + // not equal to the resolved layout. + auto otherLayout = constraint.value; + if (layout != otherLayout) + return true; - if (derivedViaConcrete) - continue; + return false; + }); if (resolvedConcreteType) { - if (typeImpliesLayoutConstraint(resolvedConcreteType, layout)) { - impliedByConcrete.push_back(constraint); - - if (!source->isDerivedRequirement()) { - Impl->ExplicitConformancesImpliedByConcrete.insert( - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::Layout, constraint)); - } + auto layout = req.getRHS().get(); + if (typeImpliesLayoutConstraint(resolvedConcreteType, + layout)) { + Impl->ExplicitConformancesImpliedByConcrete.insert(req); + + checkIfRequirementCanBeDerived( + req, equivClass->concreteTypeConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + // Filter out requirements where the concrete type + // does not have the resolved layout. + auto otherType = getCanonicalTypeInContext( + constraint.value, { }); + if (!typeImpliesLayoutConstraint(otherType, layout)) + return true; + + return false; + }); + } else if (typeConflictsWithLayoutConstraint(resolvedConcreteType, + layout)) { + // We have a concrete type requirement 'T == C' and 'C' does + // not satisfy 'L1'. + Impl->ConflictingConcreteTypeRequirements.push_back( + {req, resolvedConcreteType, equivClass->layout}); } } + } else { + // Case 3. We have a requirement 'T : L1', and we know + // the most specific resolved layout for 'T' is some other + // layout 'L2', such that 'L2' is mergeable with 'L1'. + // + // This means that 'T : L1' is made redundant by any + // requirement that implies 'T : L3', where L3 is + // mergeable with L1. + checkIfRequirementCanBeDerived( + req, equivClass->layoutConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + auto otherLayout = constraint.value; + // Filter out requirements where the implied layout + // is not mergeable with the original requirement's + // layout. + if (!layout.merge(otherLayout)->isKnownLayout()) + return true; - if (layout == equivClass.layout) { - exact.push_back(constraint); - continue; - } + return false; + }); - // Check for a conflict. - if (!source->isDerivedRequirement() && - !layout.merge(equivClass.layout)->isKnownLayout()) { - auto req = - ExplicitRequirement::fromExplicitConstraint( - RequirementKind::Layout, constraint); - Impl->ConflictingRequirements.insert( - std::make_pair(req, equivClass.layout)); + if (resolvedConcreteType) { + auto layout = req.getRHS().get(); + if (typeImpliesLayoutConstraint(resolvedConcreteType, layout)) { + Impl->ExplicitConformancesImpliedByConcrete.insert(req); + + checkIfRequirementCanBeDerived( + req, equivClass->concreteTypeConstraints, + requirementSignatureSelfProto, + [&](const Constraint &constraint) { + // Filter out requirements where the concrete type + // does not have the resolved layout. + auto otherType = getCanonicalTypeInContext( + constraint.value, { }); + + if (!typeImpliesLayoutConstraint(otherType, + equivClass->layout)) + return true; + + return false; + }); + } } - - lessSpecific.push_back(constraint); } - layoutRequirement = - graph.addConstraintsFromEquivClass(RequirementKind::Layout, - exact, lessSpecific, Context); - - graph.handleConstraintsImpliedByConcrete(RequirementKind::Layout, - impliedByConcrete, - concreteTypeRequirement); + break; } - if (resolvedConcreteType && resolvedSuperclass) { - if (!resolvedSuperclass->isExactSuperclassOf(resolvedConcreteType)) { - Impl->ConflictingConcreteTypeRequirements.push_back( - {*concreteTypeRequirement, *superclassRequirement, - resolvedConcreteType, resolvedSuperclass}); - } - } else if (resolvedConcreteType && equivClass.layout) { - if (typeConflictsWithLayoutConstraint(resolvedConcreteType, - equivClass.layout)) { - Impl->ConflictingConcreteTypeRequirements.push_back( - {*concreteTypeRequirement, *layoutRequirement, - resolvedConcreteType, equivClass.layout}); - } + case RequirementKind::SameType: + llvm_unreachable("Should not see same type requirements here"); } } - - auto &SM = getASTContext().SourceMgr; - -#ifndef NDEBUG - graph.verifyAllVerticesWereSeen(&SM); -#endif - - // Determine which explicit requirement sources are actually redundant. - assert(Impl->RedundantRequirements.empty()); - graph.computeRedundantRequirements(SM, Impl->RedundantRequirements); } void GenericSignatureBuilder::finalize(TypeArrayView genericParams, bool allowConcreteGenericParams, const ProtocolDecl *requirementSignatureSelfProto) { - // Process any delayed requirements that we can handle now. - processDelayedRequirements(); - - computeRedundantRequirements(requirementSignatureSelfProto); + diagnoseProtocolRefinement(requirementSignatureSelfProto); diagnoseRedundantRequirements(); - diagnoseConflictingConcreteTypeRequirements(); + diagnoseConflictingConcreteTypeRequirements(requirementSignatureSelfProto); + + { + // In various places below, we iterate over the list of equivalence classes + // and call getMinimalConformanceSource(). Unfortunately, this function + // ends up calling maybeResolveEquivalenceClass(), which can delete equivalence + // classes. The workaround is to first iterate safely over a copy of the list, + // and pre-compute all minimal conformance sources, before proceeding with the + // rest of the function. + // + // FIXME: This is not even correct, because we may not have reached fixed point + // after one round of this. getMinimalConformanceSource() should be removed + // instead. + SmallVector equivalenceClassPAs; + for (auto &equivClass : Impl->EquivalenceClasses) { + equivalenceClassPAs.push_back(equivClass.members.front()); + } + + for (auto *pa : equivalenceClassPAs) { + auto &equivClass = *pa->getOrCreateEquivalenceClass(*this); + + // Copy the vector and iterate over the copy to avoid iterator invalidation + // issues. + auto conformsTo = equivClass.conformsTo; + for (auto entry : conformsTo) { + for (const auto &constraint : entry.second) { + (void) constraint.source->getMinimalConformanceSource( + *this, constraint.getSubjectDependentType({ }), entry.first); + } + } + } + } assert(!Impl->finalized && "Already finalized builder"); #ifndef NDEBUG @@ -6701,14 +6270,9 @@ GenericSignatureBuilder::finalize(TypeArrayView genericPar } equivClass.recursiveSuperclassType = true; - } else { - checkSuperclassConstraints(genericParams, &equivClass); } } - - checkConformanceConstraints(genericParams, &equivClass); - checkLayoutConstraints(genericParams, &equivClass); - }; + } if (!Impl->ExplicitSameTypeRequirements.empty()) { // FIXME: Expand all conformance requirements. This is expensive :( @@ -6930,27 +6494,21 @@ void GenericSignatureBuilder::processDelayedRequirements() { namespace { /// Remove self-derived sources from the given vector of constraints. - /// - /// \returns true if any derived-via-concrete constraints were found. template - bool removeSelfDerived(GenericSignatureBuilder &builder, + void removeSelfDerived(GenericSignatureBuilder &builder, std::vector> &constraints, ProtocolDecl *proto, - bool dropDerivedViaConcrete = true, bool allCanBeSelfDerived = false) { auto genericParams = builder.getGenericParams(); - bool anyDerivedViaConcrete = false; - Optional> remainingConcrete; SmallVector, 4> minimalSources; constraints.erase( std::remove_if(constraints.begin(), constraints.end(), [&](const Constraint &constraint) { - bool derivedViaConcrete; auto minimalSource = constraint.source->getMinimalConformanceSource( builder, constraint.getSubjectDependentType(genericParams), - proto, derivedViaConcrete); + proto); if (minimalSource != constraint.source) { // The minimal source is smaller than the original source, so the // original source is self-derived. @@ -6967,21 +6525,8 @@ namespace { return true; } - if (!derivedViaConcrete) - return false; - - anyDerivedViaConcrete = true; - - if (!dropDerivedViaConcrete) - return false; - - // Drop derived-via-concrete requirements. - if (!remainingConcrete) - remainingConcrete = constraint; - - ++NumSelfDerived; - return true; - }), + return false; + }), constraints.end()); // If we found any minimal sources, add them now, avoiding introducing any @@ -7001,13 +6546,8 @@ namespace { } } - // If we only had concrete conformances, put one back. - if (constraints.empty() && remainingConcrete) - constraints.push_back(*remainingConcrete); - assert((!constraints.empty() || allCanBeSelfDerived) && "All constraints were self-derived!"); - return anyDerivedViaConcrete; } } // end anonymous namespace @@ -7179,19 +6719,52 @@ static bool isRedundantlyInheritableObjCProtocol( return true; } -void GenericSignatureBuilder::checkConformanceConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass) { - for (auto &entry : equivClass->conformsTo) { - // Remove self-derived constraints. - assert(!entry.second.empty() && "No constraints to work with?"); +/// Diagnose any conformance requirements on 'Self' that are not derived from +/// the protocol's inheritance clause. +void GenericSignatureBuilder::diagnoseProtocolRefinement( + const ProtocolDecl *requirementSignatureSelfProto) { + if (requirementSignatureSelfProto == nullptr) + return; + + auto selfType = requirementSignatureSelfProto->getSelfInterfaceType(); + auto *equivClass = resolveEquivalenceClass( + selfType, ArchetypeResolutionKind::AlreadyKnown); + assert(equivClass != nullptr); + + for (auto pair : equivClass->conformsTo) { + auto *proto = pair.first; + bool found = false; + SourceLoc loc; + for (const auto &constraint : pair.second) { + if (!involvesNonSelfSubjectTypes(constraint.source)) { + found = true; + break; + } - // Remove any self-derived constraints. - removeSelfDerived(*this, entry.second, entry.first); + auto *parent = constraint.source; + while (parent->kind != RequirementSource::RequirementSignatureSelf) { + loc = parent->getLoc(); + if (loc) + break; + } + } + + if (!found) { + requirementSignatureSelfProto->diagnose( + diag::missing_protocol_refinement, + const_cast(requirementSignatureSelfProto), + proto); + + if (loc.isValid()) { + Context.Diags.diagnose(loc, diag::redundant_conformance_here, + selfType, proto); + } + } } } -void GenericSignatureBuilder::diagnoseRedundantRequirements() const { +void GenericSignatureBuilder::diagnoseRedundantRequirements( + bool onlyDiagnoseExplicitConformancesImpliedByConcrete) const { for (const auto &req : Impl->ExplicitRequirements) { auto *source = req.getSource(); auto loc = source->getLoc(); @@ -7209,6 +6782,10 @@ void GenericSignatureBuilder::diagnoseRedundantRequirements() const { if (found == Impl->RedundantRequirements.end()) continue; + if (onlyDiagnoseExplicitConformancesImpliedByConcrete && + Impl->ExplicitConformancesImpliedByConcrete.count(req) == 0) + continue; + // Don't diagnose explicit requirements that are implied by // inferred requirements. if (llvm::all_of(found->second, @@ -7351,15 +6928,43 @@ void GenericSignatureBuilder::diagnoseRedundantRequirements() const { } } -void GenericSignatureBuilder::diagnoseConflictingConcreteTypeRequirements() const { +void GenericSignatureBuilder::diagnoseConflictingConcreteTypeRequirements( + const ProtocolDecl *requirementSignatureSelfProto) { for (auto pair : Impl->ConflictingConcreteTypeRequirements) { - SourceLoc loc = pair.concreteTypeRequirement.getSource()->getLoc(); + auto subjectType = pair.otherRequirement.getSubjectType(); + + auto *equivClass = resolveEquivalenceClass(subjectType, + ArchetypeResolutionKind::AlreadyKnown); + assert(equivClass && + "Explicit requirement names an unknown equivalence class?"); + + auto foundConcreteRequirement = llvm::find_if( + equivClass->concreteTypeConstraints, + [&](const Constraint &constraint) { + SmallVector result; + getBaseRequirements( + [&]() { + return std::make_pair(RequirementKind::SameType, + constraint.value); + }, + constraint.source, requirementSignatureSelfProto, result); + + for (const auto &otherReq : result) { + if (isRedundantExplicitRequirement(otherReq)) + return false; + } + + return true; + }); + + assert(foundConcreteRequirement != equivClass->concreteTypeConstraints.end()); + + SourceLoc loc = foundConcreteRequirement->source->getLoc(); SourceLoc otherLoc = pair.otherRequirement.getSource()->getLoc(); if (loc.isInvalid() && otherLoc.isInvalid()) continue; - auto subjectType = pair.concreteTypeRequirement.getSubjectType(); SourceLoc subjectLoc = (loc.isInvalid() ? otherLoc : loc); Impl->HadAnyError = true; @@ -7557,9 +7162,7 @@ static void computeDerivedSameTypeComponents( // construction of self-derived sources really don't work, because we // discover more information later, so we need a more on-line or // iterative approach. - bool derivedViaConcrete; - if (concrete.source->isSelfDerivedSource(builder, subjectType, - derivedViaConcrete)) + if (concrete.source->isSelfDerivedSource(builder, subjectType)) continue; // If it has a better source than we'd seen before for this component, @@ -7893,13 +7496,10 @@ void GenericSignatureBuilder::checkSameTypeConstraints( if (!equivClass->derivedSameTypeComponents.empty()) return; - bool anyDerivedViaConcrete = false; // Remove self-derived constraints. - if (removeSelfDerived(*this, equivClass->sameTypeConstraints, - /*proto=*/nullptr, - /*dropDerivedViaConcrete=*/false, - /*allCanBeSelfDerived=*/true)) - anyDerivedViaConcrete = true; + removeSelfDerived(*this, equivClass->sameTypeConstraints, + /*proto=*/nullptr, + /*allCanBeSelfDerived=*/true); // Sort the constraints, so we get a deterministic ordering of diagnostics. llvm::array_pod_sort(equivClass->sameTypeConstraints.begin(), @@ -7977,16 +7577,6 @@ void GenericSignatureBuilder::checkSameTypeConstraints( IntercomponentEdge(firstComponentIdx, secondComponentIdx, constraint)); } - // If there were any derived-via-concrete constraints, drop them now before - // we emit other diagnostics. - if (anyDerivedViaConcrete) { - // Remove derived-via-concrete constraints. - (void)removeSelfDerived(*this, equivClass->sameTypeConstraints, - /*proto=*/nullptr, - /*dropDerivedViaConcrete=*/true, - /*allCanBeSelfDerived=*/true); - } - // Walk through each of the components, checking the intracomponent edges. // This will diagnose any explicitly-specified requirements within a // component, all of which are redundant. @@ -8184,7 +7774,6 @@ void GenericSignatureBuilder::checkConcreteTypeConstraints( removeSelfDerived(*this, equivClass->concreteTypeConstraints, /*proto=*/nullptr, - /*dropDerivedViaConcrete=*/true, /*allCanBeSelfDerived=*/true); // This can occur if the combination of a superclass requirement and @@ -8222,22 +7811,6 @@ void GenericSignatureBuilder::checkConcreteTypeConstraints( diag::same_type_redundancy_here); } -void GenericSignatureBuilder::checkSuperclassConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass) { - assert(equivClass->superclass && "No superclass constraint?"); - - removeSelfDerived(*this, equivClass->superclassConstraints, /*proto=*/nullptr); -} - -void GenericSignatureBuilder::checkLayoutConstraints( - TypeArrayView genericParams, - EquivalenceClass *equivClass) { - if (!equivClass->layout) return; - - removeSelfDerived(*this, equivClass->layoutConstraints, /*proto=*/nullptr); -} - bool GenericSignatureBuilder::isRedundantExplicitRequirement( const ExplicitRequirement &req) const { assert(Impl->computedRedundantRequirements && @@ -8275,43 +7848,9 @@ static Optional createRequirement(RequirementKind kind, } } -/// Determine the canonical ordering of requirements. -static unsigned getRequirementKindOrder(RequirementKind kind) { - switch (kind) { - case RequirementKind::Conformance: return 2; - case RequirementKind::Superclass: return 0; - case RequirementKind::SameType: return 3; - case RequirementKind::Layout: return 1; - } - llvm_unreachable("unhandled kind"); -} - static int compareRequirements(const Requirement *lhsPtr, const Requirement *rhsPtr) { - auto &lhs = *lhsPtr; - auto &rhs = *rhsPtr; - - int compareLHS = - compareDependentTypes(lhs.getFirstType(), rhs.getFirstType()); - - if (compareLHS != 0) - return compareLHS; - - int compareKind = (getRequirementKindOrder(lhs.getKind()) - - getRequirementKindOrder(rhs.getKind())); - - if (compareKind != 0) - return compareKind; - - // We should only have multiple conformance requirements. - assert(lhs.getKind() == RequirementKind::Conformance); - - int compareProtos = - TypeDecl::compare(lhs.getProtocolDecl(), rhs.getProtocolDecl()); - - assert(compareProtos != 0 && "Duplicate conformance requirement"); - - return compareProtos; + return lhsPtr->compare(*rhsPtr); } void GenericSignatureBuilder::enumerateRequirements( @@ -8322,6 +7861,8 @@ void GenericSignatureBuilder::enumerateRequirements( RequirementRHS rhs) { if (auto req = createRequirement(kind, depTy, rhs, genericParams)) requirements.push_back(*req); + else + Impl->HadAnyError = true; }; // Collect all non-same type requirements. @@ -8434,12 +7975,10 @@ void GenericSignatureBuilder::dump(llvm::raw_ostream &out) { } void GenericSignatureBuilder::addGenericSignature(GenericSignature sig) { - if (!sig) return; - - for (auto param : sig->getGenericParams()) + for (auto param : sig.getGenericParams()) addGenericParameter(param); - for (auto &reqt : sig->getRequirements()) + for (auto &reqt : sig.getRequirements()) addRequirement(reqt, FloatingRequirementSource::forAbstract(), nullptr); } @@ -8449,7 +7988,7 @@ static void checkGenericSignature(CanGenericSignature canSig, GenericSignatureBuilder &builder) { PrettyStackTraceGenericSignature debugStack("checking", canSig); - auto canonicalRequirements = canSig->getRequirements(); + auto canonicalRequirements = canSig.getRequirements(); // Check that the signature is canonical. for (unsigned idx : indices(canonicalRequirements)) { @@ -8488,7 +8027,7 @@ static void checkGenericSignature(CanGenericSignature canSig, assert(compareDependentTypes(firstType, secondType) < 0 && "Out-of-order type parameters in same-type constraint"); } else { - assert(canSig->isCanonicalTypeInContext(secondType) && + assert(canSig->isCanonicalTypeInContext(secondType, builder) && "Concrete same-type isn't canonical in its own context"); } break; @@ -8540,7 +8079,7 @@ static void checkGenericSignature(CanGenericSignature canSig, "Concrete subject type should not have any other requirements"); } - assert(compareRequirements(&prevReqt, &reqt) < 0 && + assert(prevReqt.compare(reqt) < 0 && "Out-of-order requirements"); } } @@ -8585,48 +8124,115 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir NumSignaturesRebuiltWithoutRedundantRequirements++; GenericSignatureBuilder newBuilder(Context); + newBuilder.Impl->RebuildingWithoutRedundantConformances = true; for (auto param : getGenericParams()) newBuilder.addGenericParameter(param); - auto newSource = FloatingRequirementSource::forAbstract(); + const RequirementSource *requirementSignatureSource = nullptr; + if (auto *proto = const_cast(requirementSignatureSelfProto)) { + auto selfType = proto->getSelfInterfaceType(); + requirementSignatureSource = + RequirementSource::forRequirementSignature(newBuilder, selfType, proto); + + // Add the conformance requirement 'Self : Proto' directly without going + // through addConformanceRequirement(), since the latter calls + // expandConformanceRequirement(), which we want to skip since we're + // re-adding the requirements directly below. + auto resolvedType = ResolvedType(newBuilder.Impl->PotentialArchetypes[0]); + auto equivClass = resolvedType.getEquivalenceClass(newBuilder); + + (void) equivClass->recordConformanceConstraint(newBuilder, resolvedType, proto, + requirementSignatureSource); + } + + auto getRebuiltSource = [&](const RequirementSource *source) { + assert(!source->isDerivedRequirement()); + + if (auto *proto = const_cast(requirementSignatureSelfProto)) { + return FloatingRequirementSource::viaProtocolRequirement( + requirementSignatureSource, proto, source->getLoc(), + source->isInferredRequirement()); + } + + if (source->isInferredRequirement()) + return FloatingRequirementSource::forInferred(source->getLoc()); + + return FloatingRequirementSource::forExplicit(source->getLoc()); + }; for (const auto &req : Impl->ExplicitRequirements) { + if (Impl->DebugRedundantRequirements) { + req.dump(llvm::dbgs(), &Context.SourceMgr); + llvm::dbgs() << "\n"; + } + assert(req.getKind() != RequirementKind::SameType && "Should not see same-type requirement here"); if (isRedundantExplicitRequirement(req) && - Impl->ExplicitConformancesImpliedByConcrete.count(req)) + Impl->ExplicitConformancesImpliedByConcrete.count(req)) { + if (Impl->DebugRedundantRequirements) { + llvm::dbgs() << "... skipping\n"; + } + continue; + } auto subjectType = req.getSubjectType(); - subjectType = stripBoundDependentMemberTypes(subjectType); - auto resolvedSubjectType = - resolveDependentMemberTypes(*this, subjectType, - ArchetypeResolutionKind::WellFormed); + auto resolvedSubject = + maybeResolveEquivalenceClass(subjectType, + ArchetypeResolutionKind::WellFormed, + /*wantExactPotentialArchetype=*/false); + + auto *resolvedEquivClass = resolvedSubject.getEquivalenceClass(*this); + Type resolvedSubjectType; + if (resolvedEquivClass != nullptr) + resolvedSubjectType = resolvedEquivClass->getAnchor(*this, { }); // This can occur if the combination of a superclass requirement and // protocol conformance requirement force a type to become concrete. // // FIXME: Is there a more principled formulation of this? - if (req.getKind() == RequirementKind::Superclass && - !resolvedSubjectType->isTypeParameter()) { - newBuilder.addRequirement(Requirement(RequirementKind::SameType, - subjectType, resolvedSubjectType), - newSource, nullptr); - continue; + if (req.getKind() == RequirementKind::Superclass) { + if (resolvedSubject.getAsConcreteType()) { + auto unresolvedSubjectType = stripBoundDependentMemberTypes(subjectType); + newBuilder.addRequirement(Requirement(RequirementKind::SameType, + unresolvedSubjectType, + resolvedSubject.getAsConcreteType()), + getRebuiltSource(req.getSource()), nullptr); + continue; + } + + if (resolvedEquivClass->concreteType) { + auto unresolvedSubjectType = stripBoundDependentMemberTypes( + resolvedSubjectType); + newBuilder.addRequirement(Requirement(RequirementKind::SameType, + unresolvedSubjectType, + resolvedEquivClass->concreteType), + getRebuiltSource(req.getSource()), nullptr); + continue; + } } - assert(resolvedSubjectType->isTypeParameter()); + assert(resolvedSubjectType && resolvedSubjectType->isTypeParameter()); if (auto optReq = createRequirement(req.getKind(), resolvedSubjectType, req.getRHS(), getGenericParams())) { auto newReq = stripBoundDependentMemberTypes(*optReq); - newBuilder.addRequirement(newReq, newSource, nullptr); + newBuilder.addRequirement(newReq, getRebuiltSource(req.getSource()), + nullptr); + } else { + Impl->HadAnyError = true; } } for (const auto &req : Impl->ExplicitSameTypeRequirements) { + if (Impl->DebugRedundantRequirements) { + req.dump(llvm::dbgs(), &Context.SourceMgr); + llvm::dbgs() << "\n"; + } + auto resolveType = [this](Type t) -> Type { t = stripBoundDependentMemberTypes(t); if (t->is()) { @@ -8648,14 +8254,54 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir } }; - auto subjectType = resolveType(req.getFirstType()); - auto constraintType = resolveType(req.getSecondType()); + // If we have a same-type requirement where the right hand side is concrete, + // canonicalize the left hand side, in case dropping some redundant + // conformance requirement turns the original left hand side into an + // unresolvable type. + if (!req.getRHS().get()->isTypeParameter()) { + auto resolvedSubject = + maybeResolveEquivalenceClass(req.getSubjectType(), + ArchetypeResolutionKind::WellFormed, + /*wantExactPotentialArchetype=*/false); + + auto *resolvedEquivClass = resolvedSubject.getEquivalenceClass(*this); + auto resolvedSubjectType = resolvedEquivClass->getAnchor(*this, { }); + + auto constraintType = resolveType(req.getRHS().get()); + + auto newReq = stripBoundDependentMemberTypes( + Requirement(RequirementKind::SameType, + resolvedSubjectType, constraintType)); + + if (Impl->DebugRedundantRequirements) { + llvm::dbgs() << "=> "; + newReq.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + newBuilder.addRequirement(newReq, getRebuiltSource(req.getSource()), + nullptr); + + continue; + } + + // Otherwise, we can't canonicalize the two sides of the requirement, since + // doing so will produce a trivial same-type requirement T == T. Instead, + // apply some ad-hoc rules to improve the odds that the requirement will + // resolve in the rebuilt GSB. + auto subjectType = resolveType(req.getSubjectType()); + auto constraintType = resolveType(req.getRHS().get()); auto newReq = stripBoundDependentMemberTypes( Requirement(RequirementKind::SameType, subjectType, constraintType)); - newBuilder.addRequirement(newReq, newSource, nullptr); + if (Impl->DebugRedundantRequirements) { + llvm::dbgs() << "=> "; + newReq.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + newBuilder.addRequirement(newReq, getRebuiltSource(req.getSource()), + nullptr); } // Wipe out the internal state of the old builder, since we don't need it anymore. @@ -8664,33 +8310,16 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir // Build a new signature using the new builder. return std::move(newBuilder).computeGenericSignature( allowConcreteGenericParams, - requirementSignatureSelfProto, - /*rebuildingWithoutRedundantConformances=*/true); + requirementSignatureSelfProto); } GenericSignature GenericSignatureBuilder::computeGenericSignature( bool allowConcreteGenericParams, - const ProtocolDecl *requirementSignatureSelfProto, - bool rebuildingWithoutRedundantConformances) && { - // Finalize the builder, producing any necessary diagnostics. - finalize(getGenericParams(), - allowConcreteGenericParams, - requirementSignatureSelfProto); - - if (rebuildingWithoutRedundantConformances) { - assert(requirementSignatureSelfProto == nullptr && - "Rebuilding a requirement signature?"); - - assert(!Impl->HadAnyError && - "Rebuilt signature had errors"); + const ProtocolDecl *requirementSignatureSelfProto) && { + // Process any delayed requirements that we can handle now. + processDelayedRequirements(); -#ifndef NDEBUG - for (const auto &req : Impl->ExplicitConformancesImpliedByConcrete) { - assert(!isRedundantExplicitRequirement(req) && - "Rebuilt signature still had redundant conformance requirements"); - } -#endif - } + computeRedundantRequirements(requirementSignatureSelfProto); // If any of our explicit conformance requirements were implied by // superclass or concrete same-type requirements, we have to build the @@ -8701,16 +8330,44 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature( // we might end up emitting duplicate diagnostics. // // Also, don't do this when building a requirement signature. - if (!rebuildingWithoutRedundantConformances && - requirementSignatureSelfProto == nullptr && + if (!Impl->RebuildingWithoutRedundantConformances && !Impl->HadAnyError && !Impl->ExplicitConformancesImpliedByConcrete.empty()) { + diagnoseRedundantRequirements( + /*onlyDiagnoseExplicitConformancesImpliedByConcrete=*/true); + + if (Impl->DebugRedundantRequirements) { + llvm::dbgs() << "Going to rebuild signature\n"; + } return std::move(*this).rebuildSignatureWithoutRedundantRequirements( allowConcreteGenericParams, requirementSignatureSelfProto); } - // Collect the requirements placed on the generic parameter types. + if (Impl->RebuildingWithoutRedundantConformances) { + if (Impl->DebugRedundantRequirements) { + llvm::dbgs() << "Rebuilding signature\n"; + } + +#ifndef NDEBUG + for (const auto &req : Impl->ExplicitConformancesImpliedByConcrete) { + if (isRedundantExplicitRequirement(req)) { + llvm::errs() << "Rebuilt signature still had redundant conformance requirement\n"; + req.dump(llvm::errs(), &Context.SourceMgr); + llvm::errs() << "\n"; + abort(); + } + } +#endif + } + + // Diagnose redundant requirements, check for recursive concrete types + // and compute minimized same-type requirements. + finalize(getGenericParams(), + allowConcreteGenericParams, + requirementSignatureSelfProto); + + // Collect all non-redundant explicit requirements. SmallVector requirements; enumerateRequirements(getGenericParams(), requirements); @@ -8753,8 +8410,8 @@ void GenericSignatureBuilder::verifyGenericSignature(ASTContext &context, llvm::errs() << "\n"; // Try building a new signature having the same requirements. - auto genericParams = sig->getGenericParams(); - auto requirements = sig->getRequirements(); + auto genericParams = sig.getGenericParams(); + auto requirements = sig.getRequirements(); { PrettyStackTraceGenericSignature debugStack("verifying", sig); @@ -8854,14 +8511,6 @@ void GenericSignatureBuilder::verifyGenericSignaturesInModule( verifyGenericSignature(context, canGenericSig); } } - -bool AbstractGenericSignatureRequest::isCached() const { - return true; -} - -bool InferredGenericSignatureRequest::isCached() const { - return true; -} /// Check whether the inputs to the \c AbstractGenericSignatureRequest are /// all canonical. @@ -8887,9 +8536,10 @@ static bool isCanonicalRequest(GenericSignature baseSignature, GenericSignature AbstractGenericSignatureRequest::evaluate( Evaluator &evaluator, - const GenericSignatureImpl *baseSignature, + const GenericSignatureImpl *baseSignatureImpl, SmallVector addedParameters, SmallVector addedRequirements) const { + GenericSignature baseSignature = GenericSignature{baseSignatureImpl}; // If nothing is added to the base signature, just return the base // signature. if (addedParameters.empty() && addedRequirements.empty()) @@ -8902,24 +8552,19 @@ AbstractGenericSignatureRequest::evaluate( // If there are no added requirements, we can form the signature directly // with the added parameters. if (addedRequirements.empty()) { - ArrayRef requirements; - if (baseSignature) { - addedParameters.insert(addedParameters.begin(), - baseSignature->getGenericParams().begin(), - baseSignature->getGenericParams().end()); - requirements = baseSignature->getRequirements(); - } + addedParameters.insert(addedParameters.begin(), + baseSignature.getGenericParams().begin(), + baseSignature.getGenericParams().end()); - return GenericSignature::get(addedParameters, requirements); + return GenericSignature::get(addedParameters, + baseSignature.getRequirements()); } // If the request is non-canonical, we won't need to build our own // generic signature builder. if (!isCanonicalRequest(baseSignature, addedParameters, addedRequirements)) { // Canonicalize the inputs so we can form the canonical request. - GenericSignature canBaseSignature; - if (baseSignature) - canBaseSignature = baseSignature->getCanonicalSignature(); + auto canBaseSignature = baseSignature.getCanonicalSignature(); SmallVector canAddedParameters; canAddedParameters.reserve(addedParameters.size()); @@ -8946,18 +8591,18 @@ AbstractGenericSignatureRequest::evaluate( // result the original request wanted. auto canSignature = *canSignatureResult; SmallVector resugaredParameters; - resugaredParameters.reserve(canSignature->getGenericParams().size()); + resugaredParameters.reserve(canSignature.getGenericParams().size()); if (baseSignature) { - resugaredParameters.append(baseSignature->getGenericParams().begin(), - baseSignature->getGenericParams().end()); + resugaredParameters.append(baseSignature.getGenericParams().begin(), + baseSignature.getGenericParams().end()); } resugaredParameters.append(addedParameters.begin(), addedParameters.end()); assert(resugaredParameters.size() == - canSignature->getGenericParams().size()); + canSignature.getGenericParams().size()); SmallVector resugaredRequirements; - resugaredRequirements.reserve(canSignature->getRequirements().size()); - for (const auto &req : canSignature->getRequirements()) { + resugaredRequirements.reserve(canSignature.getRequirements().size()); + for (const auto &req : canSignature.getRequirements()) { auto resugaredReq = req.subst( [&](SubstitutableType *type) { if (auto gp = dyn_cast(type)) { @@ -9011,7 +8656,8 @@ InferredGenericSignatureRequest::evaluate( const auto visitRequirement = [&](const Requirement &req, RequirementRepr *reqRepr) { - const auto source = FloatingRequirementSource::forExplicit(reqRepr); + const auto source = FloatingRequirementSource::forExplicit( + reqRepr->getSeparatorLoc()); // If we're extending a protocol and adding a redundant requirement, // for example, `extension Foo where Self: Foo`, then emit a @@ -9104,8 +8750,10 @@ InferredGenericSignatureRequest::evaluate( /// Perform any remaining requirement inference. for (auto sourcePair : inferenceSources) { + auto *typeRepr = sourcePair.getTypeRepr(); auto source = - FloatingRequirementSource::forInferred(sourcePair.getTypeRepr()); + FloatingRequirementSource::forInferred( + typeRepr ? typeRepr->getStartLoc() : SourceLoc()); builder.inferRequirements(*parentModule, sourcePair.getType(), @@ -9114,7 +8762,7 @@ InferredGenericSignatureRequest::evaluate( // Finish by adding any remaining requirements. auto source = - FloatingRequirementSource::forInferred(nullptr); + FloatingRequirementSource::forInferred(SourceLoc()); for (const auto &req : addedRequirements) builder.addRequirement(req, source, parentModule); diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index e824d99111f4e..a43f575df7416 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -63,6 +63,24 @@ void ImportSet::Profile( } } +void ImportSet::dump() const { + llvm::errs() << "HasHeaderImportModule: " << HasHeaderImportModule << "\n"; + + llvm::errs() << "TopLevelImports:"; + for (auto import : getTopLevelImports()) { + llvm::errs() << "\n- "; + simple_display(llvm::errs(), import); + } + llvm::errs() << "\n"; + + llvm::errs() << "TransitiveImports:"; + for (auto import : getTransitiveImports()) { + llvm::errs() << "\n- "; + simple_display(llvm::errs(), import); + } + llvm::errs() << "\n"; +} + static void collectExports(ImportedModule next, SmallVectorImpl &stack) { SmallVector exports; diff --git a/lib/AST/LayoutConstraint.cpp b/lib/AST/LayoutConstraint.cpp index 42da19c1e1cd4..d379359793ba5 100644 --- a/lib/AST/LayoutConstraint.cpp +++ b/lib/AST/LayoutConstraint.cpp @@ -140,12 +140,6 @@ bool LayoutConstraintInfo::isNativeRefCounted(LayoutConstraintKind Kind) { Kind == LayoutConstraintKind::NativeClass; } -void *LayoutConstraintInfo::operator new(size_t bytes, const ASTContext &ctx, - AllocationArena arena, - unsigned alignment) { - return ctx.Allocate(bytes, alignment, arena); -} - SourceRange LayoutConstraintLoc::getSourceRange() const { return getLoc(); } #define MERGE_LOOKUP(lhs, rhs) \ @@ -358,4 +352,18 @@ LayoutConstraintInfo LayoutConstraintInfo::NativeClassConstraintInfo( LayoutConstraintInfo LayoutConstraintInfo::TrivialConstraintInfo( LayoutConstraintKind::Trivial); +int LayoutConstraint::compare(LayoutConstraint rhs) const { + if (Ptr->getKind() != rhs->getKind()) + return int(rhs->getKind()) - int(Ptr->getKind()); + + if (Ptr->SizeInBits != rhs->SizeInBits) + return int(rhs->SizeInBits) - int(Ptr->SizeInBits); + + if (Ptr->Alignment != rhs->Alignment) + return int(rhs->Alignment) - int(Ptr->Alignment); + + assert(*this == rhs); + return 0; +} + } // end namespace swift diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 012d4986c7a8f..a295905ab1c74 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -276,8 +276,11 @@ void SourceLookupCache::populateMemberCache(const ModuleDecl &Mod) { "populate-module-class-member-cache"); for (const FileUnit *file : Mod.getFiles()) { - auto &SF = *cast(file); - addToMemberCache(SF.getTopLevelDecls()); + assert(isa(file) || + isa(file)); + SmallVector decls; + file->getTopLevelDecls(decls); + addToMemberCache(decls); } MemberCachePopulated = true; @@ -486,6 +489,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx, Bits.ModuleDecl.IsNonSwiftModule = 0; Bits.ModuleDecl.IsMainModule = 0; Bits.ModuleDecl.HasIncrementalInfo = 0; + Bits.ModuleDecl.IsConcurrencyChecked = 0; } ImplicitImportList ModuleDecl::getImplicitImports() const { @@ -970,8 +974,29 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { return ProtocolConformanceRef::forInvalid(); } +/// Whether we should create missing conformances to the given protocol. +static bool shouldCreateMissingConformances(ProtocolDecl *proto) { + return proto->isSpecificProtocol(KnownProtocolKind::Sendable); +} + +ProtocolConformanceRef ProtocolConformanceRef::forMissingOrInvalid( + Type type, ProtocolDecl *proto) { + // Introduce "missing" conformances when appropriate, so that type checking + // (and even code generation) can continue. + ASTContext &ctx = proto->getASTContext(); + if (shouldCreateMissingConformances(proto)) { + return ProtocolConformanceRef( + ctx.getBuiltinConformance( + type, proto, GenericSignature(), { }, + BuiltinConformanceKind::Missing)); + } + + return ProtocolConformanceRef::forInvalid(); +} + ProtocolConformanceRef ModuleDecl::lookupConformance(Type type, - ProtocolDecl *protocol) { + ProtocolDecl *protocol, + bool allowMissing) { // If we are recursively checking for implicit conformance of a nominal // type to Sendable, fail without evaluating this request. This // squashes cycles. @@ -985,8 +1010,142 @@ ProtocolConformanceRef ModuleDecl::lookupConformance(Type type, } } - return evaluateOrDefault( + auto result = evaluateOrDefault( getASTContext().evaluator, request, ProtocolConformanceRef::forInvalid()); + + // If we aren't supposed to allow missing conformances through for this + // protocol, replace the result with an "invalid" result. + if (!allowMissing && + shouldCreateMissingConformances(protocol) && + result.hasMissingConformance(this)) + return ProtocolConformanceRef::forInvalid(); + + return result; +} + +/// Synthesize a builtin tuple type conformance to the given protocol, if +/// appropriate. +static ProtocolConformanceRef getBuiltinTupleTypeConformance( + Type type, const TupleType *tupleType, ProtocolDecl *protocol, + ModuleDecl *module) { + // Tuple type are Sendable when all of their element types are Sendable. + if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) { + ASTContext &ctx = protocol->getASTContext(); + + // Create the pieces for a generic tuple type (T1, T2, ... TN) and a + // generic signature . + SmallVector genericParams; + SmallVector typeSubstitutions; + SmallVector genericElements; + SmallVector conditionalRequirements; + for (const auto &elt : tupleType->getElements()) { + auto genericParam = GenericTypeParamType::get(0, genericParams.size(), ctx); + genericParams.push_back(genericParam); + typeSubstitutions.push_back(elt.getRawType()); + genericElements.push_back(elt.getWithType(genericParam)); + conditionalRequirements.push_back( + Requirement(RequirementKind::Conformance, genericParam, + protocol->getDeclaredType())); + } + + // If there were no generic parameters, just form the builtin conformance. + if (genericParams.empty()) { + return ProtocolConformanceRef( + ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }, + BuiltinConformanceKind::Synthesized)); + } + + // Form a generic conformance of (T1, T2, ..., TN): Sendable with signature + // and conditional requirements T1: Sendable, + // T2: Sendable, ..., TN: Sendable. + auto genericTupleType = TupleType::get(genericElements, ctx); + auto genericSig = GenericSignature::get( + genericParams, conditionalRequirements); + auto genericConformance = ctx.getBuiltinConformance( + genericTupleType, protocol, genericSig, conditionalRequirements, + BuiltinConformanceKind::Synthesized); + + // Compute the substitution map from the generic parameters of the + // generic conformance to actual types that were in the tuple type. + // Form a specialized conformance from that. + auto subMap = SubstitutionMap::get( + genericSig, [&](SubstitutableType *type) { + if (auto gp = dyn_cast(type)) { + if (gp->getDepth() == 0) + return typeSubstitutions[gp->getIndex()]; + } + + return Type(type); + }, + LookUpConformanceInModule(module)); + return ProtocolConformanceRef( + ctx.getSpecializedConformance(type, genericConformance, subMap)); + } + + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); +} + +/// Whether the given function type conforms to Sendable. +static bool isSendableFunctionType(const FunctionType *functionType) { + if (functionType->isSendable()) + return true; + + // C and thin function types have no captures, so they are Sendable. + switch (functionType->getExtInfo().getRepresentation()) { + case FunctionTypeRepresentation::Block: + case FunctionTypeRepresentation::Swift: + return false; + + case FunctionTypeRepresentation::CFunctionPointer: + case FunctionTypeRepresentation::Thin: + return true; + } +} + +/// Synthesize a builtin function type conformance to the given protocol, if +/// appropriate. +static ProtocolConformanceRef getBuiltinFunctionTypeConformance( + Type type, const FunctionType *functionType, ProtocolDecl *protocol) { + // @Sendable function types are Sendable. + if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) && + isSendableFunctionType(functionType)) { + ASTContext &ctx = protocol->getASTContext(); + return ProtocolConformanceRef( + ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }, + BuiltinConformanceKind::Synthesized)); + } + + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); +} + +/// Synthesize a builtin metatype type conformance to the given protocol, if +/// appropriate. +static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance( + Type type, const AnyMetatypeType *metatypeType, ProtocolDecl *protocol) { + // All metatypes are Sendable. + if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) { + ASTContext &ctx = protocol->getASTContext(); + return ProtocolConformanceRef( + ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }, + BuiltinConformanceKind::Synthesized)); + } + + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); +} + +/// Synthesize a builtin type conformance to the given protocol, if +/// appropriate. +static ProtocolConformanceRef getBuiltinBuiltinTypeConformance( + Type type, const BuiltinType *builtinType, ProtocolDecl *protocol) { + // All builtin are Sendable. + if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) { + ASTContext &ctx = protocol->getASTContext(); + return ProtocolConformanceRef( + ctx.getBuiltinConformance(type, protocol, GenericSignature(), { }, + BuiltinConformanceKind::Synthesized)); + } + + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); } ProtocolConformanceRef @@ -1024,14 +1183,18 @@ LookupConformanceInModuleRequest::evaluate( return ProtocolConformanceRef(protocol); } - return ProtocolConformanceRef::forInvalid(); + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); } // An existential conforms to a protocol if the protocol is listed in the // existential's list of conformances and the existential conforms to // itself. - if (type->isExistentialType()) - return mod->lookupExistentialConformance(type, protocol); + if (type->isExistentialType()) { + auto result = mod->lookupExistentialConformance(type, protocol); + if (result.isInvalid()) + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); + return result; + } // Type variables have trivial conformances. if (type->isTypeVariableOrMember()) @@ -1043,17 +1206,37 @@ LookupConformanceInModuleRequest::evaluate( if (type->is() || type->is()) return ProtocolConformanceRef(protocol); + // Tuple types can conform to protocols. + if (auto tupleType = type->getAs()) { + return getBuiltinTupleTypeConformance(type, tupleType, protocol, mod); + } + + // Function types can conform to protocols. + if (auto functionType = type->getAs()) { + return getBuiltinFunctionTypeConformance(type, functionType, protocol); + } + + // Metatypes can conform to protocols. + if (auto metatypeType = type->getAs()) { + return getBuiltinMetaTypeTypeConformance(type, metatypeType, protocol); + } + + // Builtin types can conform to protocols. + if (auto builtinType = type->getAs()) { + return getBuiltinBuiltinTypeConformance(type, builtinType, protocol); + } + auto nominal = type->getAnyNominal(); // If we don't have a nominal type, there are no conformances. if (!nominal || isa(nominal)) - return ProtocolConformanceRef::forInvalid(); + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); // Find the (unspecialized) conformance. SmallVector conformances; - if (!nominal->lookupConformance(mod, protocol, conformances)) { + if (!nominal->lookupConformance(protocol, conformances)) { if (!protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) - return ProtocolConformanceRef::forInvalid(); + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); // Try to infer Sendable conformance. GetImplicitSendableRequest cvRequest{nominal}; @@ -1062,11 +1245,31 @@ LookupConformanceInModuleRequest::evaluate( conformances.clear(); conformances.push_back(conformance); } else { - return ProtocolConformanceRef::forInvalid(); + return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); + } + } + + assert(!conformances.empty()); + + // If we have multiple conformances, first try to filter out any that are + // unavailable on the current deployment target. + // + // FIXME: Conformance lookup should really depend on source location for + // this to be 100% correct. + if (conformances.size() > 1) { + SmallVector availableConformances; + + for (auto *conformance : conformances) { + if (conformance->getDeclContext()->isAlwaysAvailableConformanceContext()) + availableConformances.push_back(conformance); } + + // Don't filter anything out if all conformances are unavailable. + if (!availableConformances.empty()) + std::swap(availableConformances, conformances); } - // FIXME: Ambiguity resolution. + // If we still have multiple conformances, just pick the first one. auto conformance = conformances.front(); // Rebuild inherited conformances based on the root normal conformance. @@ -1407,6 +1610,12 @@ bool ModuleDecl::isStdlibModule() const { return !getParent() && getName() == getASTContext().StdlibModuleName; } +bool ModuleDecl::hasStandardSubstitutions() const { + return !getParent() && + (getName() == getASTContext().StdlibModuleName || + getName() == getASTContext().Id_Concurrency); +} + bool ModuleDecl::isSwiftShimsModule() const { return !getParent() && getName() == getASTContext().SwiftShimsModuleName; } @@ -1423,7 +1632,7 @@ bool ModuleDecl::isBuiltinModule() const { return this == getASTContext().TheBuiltinModule; } -bool SourceFile::registerMainDecl(Decl *mainDecl, SourceLoc diagLoc) { +bool SourceFile::registerMainDecl(ValueDecl *mainDecl, SourceLoc diagLoc) { assert(mainDecl); if (mainDecl == MainDecl) return false; @@ -1612,13 +1821,39 @@ Fingerprint ModuleDecl::getFingerprint() const { return Fingerprint{std::move(hasher)}; } +bool ModuleDecl::isExternallyConsumed() const { + // Modules for executables aren't expected to be consumed by other modules. + // This picks up all kinds of entrypoints, including script mode, + // @UIApplicationMain and @NSApplicationMain. + if (hasEntryPoint()) { + return false; + } + + // If an implicit Objective-C header was needed to construct this module, it + // must be the product of a library target. + if (!getImplicitImportInfo().BridgingHeaderPath.empty()) { + return false; + } + + // App extensions are special beasts because they build without entrypoints + // like library targets, but they behave like executable targets because + // their associated modules are not suitable for distribution. + if (getASTContext().LangOpts.EnableAppExtensionRestrictions) { + return false; + } + + // FIXME: This is still a lousy approximation of whether the module file will + // be externally consumed. + return true; +} + //===----------------------------------------------------------------------===// // Cross-Import Overlays //===----------------------------------------------------------------------===// namespace swift { /// Represents a file containing information about cross-module overlays. -class OverlayFile { +class OverlayFile : public ASTAllocated { friend class ModuleDecl; /// The file that data should be loaded from. StringRef filePath; @@ -1641,13 +1876,6 @@ class OverlayFile { StringRef bystandingModule, SourceLoc diagLoc); public: - // Only allocate in ASTContexts. - void *operator new(size_t bytes) = delete; - void *operator new(size_t bytes, const ASTContext &ctx, - unsigned alignment = alignof(OverlayFile)) { - return ctx.Allocate(bytes, alignment); - } - OverlayFile(StringRef filePath) : filePath(filePath) { assert(!filePath.empty()); @@ -1912,8 +2140,8 @@ OverlayFileContents::load(std::unique_ptr input, return error; if (contents.version > 1) { - auto message = Twine("key 'version' has invalid value: ") + Twine(contents.version); - errorMessages.push_back(message.str()); + std::string message = (Twine("key 'version' has invalid value: ") + Twine(contents.version)).str(); + errorMessages.emplace_back(std::move(message)); return make_error_code(std::errc::result_out_of_range); } @@ -2118,14 +2346,15 @@ bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const { } bool ModuleDecl:: -canBeUsedForCrossModuleOptimization(NominalTypeDecl *nominal) const { - ModuleDecl *moduleOfNominal = nominal->getParentModule(); +canBeUsedForCrossModuleOptimization(DeclContext *ctxt) const { + ModuleDecl *moduleOfCtxt = ctxt->getParentModule(); - // If the nominal is defined in the same module, it's fine. - if (moduleOfNominal == this) + // If the context defined in the same module - or is the same module, it's + // fine. + if (moduleOfCtxt == this) return true; - // See if nominal is imported in a "regular" way, i.e. not with + // See if context is imported in a "regular" way, i.e. not with // @_implementationOnly or @_spi. ModuleDecl::ImportFilter filter = { ModuleDecl::ImportFilterKind::Exported, @@ -2135,7 +2364,7 @@ canBeUsedForCrossModuleOptimization(NominalTypeDecl *nominal) const { auto &imports = getASTContext().getImportCache(); for (auto &desc : results) { - if (imports.isImportedBy(moduleOfNominal, desc.importedModule)) + if (imports.isImportedBy(moduleOfCtxt, desc.importedModule)) return true; } return false; @@ -2779,10 +3008,6 @@ void SynthesizedFileUnit::getTopLevelDecls( //===----------------------------------------------------------------------===// void FileUnit::anchor() {} -void *FileUnit::operator new(size_t Bytes, ASTContext &C, unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - void FileUnit::getTopLevelDeclsWhereAttributesMatch( SmallVectorImpl &Results, llvm::function_ref matchAttributes) const { diff --git a/lib/AST/ModuleDependencies.cpp b/lib/AST/ModuleDependencies.cpp index d33dddf5c90fe..885104e28e70f 100644 --- a/lib/AST/ModuleDependencies.cpp +++ b/lib/AST/ModuleDependencies.cpp @@ -18,6 +18,14 @@ #include "swift/AST/SourceFile.h" using namespace swift; +ModuleDependenciesKind& operator++(ModuleDependenciesKind& e) { + if (e == ModuleDependenciesKind::LastKind) { + llvm_unreachable("Attempting to incrementa last enum value on ModuleDependenciesKind"); + } + e = ModuleDependenciesKind(static_cast::type>(e) + 1); + return e; +} + ModuleDependenciesStorageBase::~ModuleDependenciesStorageBase() { } bool ModuleDependencies::isSwiftModule() const { @@ -118,6 +126,11 @@ void ModuleDependencies::addBridgingSourceFile(StringRef bridgingSourceFile) { swiftStorage->bridgingSourceFiles.push_back(bridgingSourceFile.str()); } +void ModuleDependencies::addSourceFile(StringRef sourceFile) { + auto swiftStorage = cast(storage.get()); + swiftStorage->sourceFiles.push_back(sourceFile.str()); +} + /// Add (Clang) module on which the bridging header depends. void ModuleDependencies::addBridgingModuleDependency( StringRef module, llvm::StringSet<> &alreadyAddedModules) { @@ -126,90 +139,296 @@ void ModuleDependencies::addBridgingModuleDependency( swiftStorage->bridgingModuleDependencies.push_back(module.str()); } -llvm::StringMap & -ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) { - switch (kind) { - case ModuleDependenciesKind::SwiftTextual: - return SwiftTextualModuleDependencies; - case ModuleDependenciesKind::SwiftBinary: - return SwiftBinaryModuleDependencies; - case ModuleDependenciesKind::SwiftPlaceholder: - return SwiftPlaceholderModuleDependencies; - case ModuleDependenciesKind::Clang: - return ClangModuleDependencies; +llvm::StringMap & +GlobalModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) { + auto it = ModuleDependenciesKindMap.find(kind); + assert(it != ModuleDependenciesKindMap.end() && "invalid dependency kind"); + return it->second; +} + +const llvm::StringMap & +GlobalModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) const { + auto it = ModuleDependenciesKindMap.find(kind); + assert(it != ModuleDependenciesKindMap.end() && "invalid dependency kind"); + return it->second; +} + +static std::string moduleBasePath(const StringRef modulePath) { + auto parent = llvm::sys::path::parent_path(modulePath); + // If the modulePath is that of a .swiftinterface contained in a .swiftmodule, + // disambiguate further to parent. + if (llvm::sys::path::extension(parent) == ".swiftmodule") { + parent = llvm::sys::path::parent_path(parent); + } + + // If the module is a part of a framework, disambiguate to the framework's parent + if (llvm::sys::path::filename(parent) == "Modules") { + auto grandParent = llvm::sys::path::parent_path(parent); + if (llvm::sys::path::extension(grandParent) == ".framework") { + parent = llvm::sys::path::parent_path(grandParent); + } } - llvm_unreachable("invalid dependency kind"); + + return parent.str(); } -const llvm::StringMap & -ModuleDependenciesCache::getDependenciesMap(ModuleDependenciesKind kind) const { - switch (kind) { - case ModuleDependenciesKind::SwiftTextual: - return SwiftTextualModuleDependencies; - case ModuleDependenciesKind::SwiftBinary: - return SwiftBinaryModuleDependencies; - case ModuleDependenciesKind::SwiftPlaceholder: - return SwiftPlaceholderModuleDependencies; - case ModuleDependenciesKind::Clang: - return ClangModuleDependencies; +static bool moduleContainedInImportPathSet(const StringRef modulePath, + const llvm::StringSet<> &importPaths) +{ + return importPaths.contains(moduleBasePath(modulePath)); +} + +static bool moduleContainedInImportPathSet(const ModuleDependencies &module, + const llvm::StringSet<> &importPaths) +{ + std::string modulePath = ""; + switch (module.getKind()) { + case swift::ModuleDependenciesKind::SwiftTextual: { + auto *swiftDep = module.getAsSwiftTextualModule(); + if (swiftDep->swiftInterfaceFile) + modulePath = *(swiftDep->swiftInterfaceFile); + else { + // If we encountered a Swift textual dependency without an interface + // file, we are seeing the main scan module itself. This means that + // our search-path disambiguation is not necessary here. + return true; + } + break; + } + case swift::ModuleDependenciesKind::SwiftBinary: { + auto *swiftBinaryDep = module.getAsSwiftBinaryModule(); + modulePath = swiftBinaryDep->compiledModulePath; + break; + } + case swift::ModuleDependenciesKind::Clang: { + auto *clangDep = module.getAsClangModule(); + modulePath = clangDep->moduleMapFile; + break; + } + case swift::ModuleDependenciesKind::SwiftPlaceholder: { + // Placeholders are resolved as `true` because they are not associated with + // any specific search path. + return true; + } + default: + llvm_unreachable("Unhandled dependency kind."); + } + + if (moduleContainedInImportPathSet(modulePath, importPaths)) { + return true; } - llvm_unreachable("invalid dependency kind"); + return false; } -bool ModuleDependenciesCache::hasDependencies( +GlobalModuleDependenciesCache::GlobalModuleDependenciesCache() { + for (auto kind = ModuleDependenciesKind::FirstKind; + kind != ModuleDependenciesKind::LastKind; ++kind) { + ModuleDependenciesKindMap.insert( + {kind, llvm::StringMap()}); + } + + ModuleDependenciesKindMap.insert( + {ModuleDependenciesKind::SwiftBinary, + llvm::StringMap()}); + ModuleDependenciesKindMap.insert( + {ModuleDependenciesKind::SwiftPlaceholder, + llvm::StringMap()}); + ModuleDependenciesKindMap.insert( + {ModuleDependenciesKind::Clang, + llvm::StringMap()}); +} + +Optional GlobalModuleDependenciesCache::findDependencies( StringRef moduleName, - Optional kind) const { - if (!kind) { - return hasDependencies(moduleName, ModuleDependenciesKind::SwiftTextual) || - hasDependencies(moduleName, ModuleDependenciesKind::SwiftBinary) || - hasDependencies(moduleName, ModuleDependenciesKind::SwiftPlaceholder) || - hasDependencies(moduleName, ModuleDependenciesKind::Clang); + ModuleLookupSpecifics details) const { + if (!details.kind) { + for (auto kind = ModuleDependenciesKind::FirstKind; + kind != ModuleDependenciesKind::LastKind; ++kind) { + auto dep = findDependencies(moduleName, {kind, details.currentSearchPaths}); + if (dep.hasValue()) + return dep.getValue(); + } + return None; } - const auto &map = getDependenciesMap(*kind); - return map.count(moduleName) > 0; + const auto &map = getDependenciesMap(*details.kind); + auto known = map.find(moduleName); + if (known != map.end()) { + assert(!known->second.empty()); + for (auto &dep : known->second) { + if (moduleContainedInImportPathSet(dep, details.currentSearchPaths)) + return dep; + } + return None; + } + return None; } -Optional ModuleDependenciesCache::findDependencies( +bool GlobalModuleDependenciesCache::hasDependencies( StringRef moduleName, - Optional kind) const { + ModuleLookupSpecifics details) const { + return findDependencies(moduleName, details).hasValue(); +} + +Optional +GlobalModuleDependenciesCache::findAllDependenciesIrrespectiveOfSearchPaths( + StringRef moduleName, Optional kind) const { if (!kind) { - if (auto swiftTextualDep = findDependencies( - moduleName, ModuleDependenciesKind::SwiftTextual)) - return swiftTextualDep; - else if (auto swiftBinaryDep = findDependencies( - moduleName, ModuleDependenciesKind::SwiftBinary)) - return swiftBinaryDep; - else if (auto swiftPlaceholderDep = findDependencies( - moduleName, ModuleDependenciesKind::SwiftPlaceholder)) - return swiftPlaceholderDep; - else - return findDependencies(moduleName, ModuleDependenciesKind::Clang); + for (auto kind = ModuleDependenciesKind::FirstKind; + kind != ModuleDependenciesKind::LastKind; ++kind) { + auto deps = findAllDependenciesIrrespectiveOfSearchPaths(moduleName, kind); + if (deps.hasValue()) + return deps.getValue(); + } + return None; } const auto &map = getDependenciesMap(*kind); auto known = map.find(moduleName); - if (known != map.end()) + if (known != map.end()) { + assert(!known->second.empty()); return known->second; - + } return None; } -void ModuleDependenciesCache::recordDependencies( +static std::string modulePathForVerification(const ModuleDependencies &module) { + std::string existingModulePath = ""; + switch (module.getKind()) { + case swift::ModuleDependenciesKind::SwiftTextual: { + auto *swiftDep = module.getAsSwiftTextualModule(); + if (swiftDep->swiftInterfaceFile) + existingModulePath = *(swiftDep->swiftInterfaceFile); + break; + } + case swift::ModuleDependenciesKind::SwiftBinary: { + auto *swiftBinaryDep = module.getAsSwiftBinaryModule(); + existingModulePath = swiftBinaryDep->compiledModulePath; + break; + } + case swift::ModuleDependenciesKind::Clang: { + auto *clangDep = module.getAsClangModule(); + existingModulePath = clangDep->moduleMapFile; + break; + } + case swift::ModuleDependenciesKind::SwiftPlaceholder: + default: + llvm_unreachable("Unhandled dependency kind."); + } + return existingModulePath; +} + +const ModuleDependencies* GlobalModuleDependenciesCache::recordDependencies( StringRef moduleName, ModuleDependencies dependencies) { auto kind = dependencies.getKind(); auto &map = getDependenciesMap(kind); - assert(map.count(moduleName) == 0 && "Already added to map"); - map.insert({moduleName, std::move(dependencies)}); + // Cache may already have a dependency for this module + if (map.count(moduleName) != 0) { + // Ensure that the existing dependencies objects are at a different path. + // i.e. do not record duplicate dependencies. + auto newModulePath = modulePathForVerification(dependencies); + for (auto &existingDeps : map[moduleName]) { + if (modulePathForVerification(existingDeps) == newModulePath) + return &existingDeps; + } - AllModules.push_back({moduleName.str(), kind}); + map[moduleName].emplace_back(std::move(dependencies)); + return map[moduleName].end()-1; + } else { + map.insert({moduleName, ModuleDependenciesVector{std::move(dependencies)}}); + AllModules.push_back({moduleName.str(), kind}); + return &(map[moduleName].front()); + } } -void ModuleDependenciesCache::updateDependencies( +const ModuleDependencies* GlobalModuleDependenciesCache::updateDependencies( ModuleDependencyID moduleID, ModuleDependencies dependencies) { auto &map = getDependenciesMap(moduleID.second); auto known = map.find(moduleID.first); assert(known != map.end() && "Not yet added to map"); - known->second = std::move(dependencies); + assert(known->second.size() == 1 && + "Cannot update dependency with multiple candidates."); + known->second[0] = std::move(dependencies); + return &(known->second[0]); +} + +llvm::StringMap & +ModuleDependenciesCache::getDependencyReferencesMap(ModuleDependenciesKind kind) { + auto it = ModuleDependenciesKindMap.find(kind); + assert(it != ModuleDependenciesKindMap.end() && "invalid dependency kind"); + return it->second; +} + +const llvm::StringMap & +ModuleDependenciesCache::getDependencyReferencesMap(ModuleDependenciesKind kind) const { + auto it = ModuleDependenciesKindMap.find(kind); + assert(it != ModuleDependenciesKindMap.end() && "invalid dependency kind"); + return it->second; +} + +ModuleDependenciesCache::ModuleDependenciesCache(GlobalModuleDependenciesCache &globalCache) +: globalCache(globalCache) { + for (auto kind = ModuleDependenciesKind::FirstKind; + kind != ModuleDependenciesKind::LastKind; ++kind) { + ModuleDependenciesKindMap.insert( + {kind, llvm::StringMap()}); + } +} + +Optional ModuleDependenciesCache::findDependencies( + StringRef moduleName, Optional kind) const { + if (!kind) { + for (auto kind = ModuleDependenciesKind::FirstKind; + kind != ModuleDependenciesKind::LastKind; ++kind) { + auto dep = findDependencies(moduleName, kind); + if (dep.hasValue()) + return dep.getValue(); + } + return None; + } + + const auto &map = getDependencyReferencesMap(*kind); + auto known = map.find(moduleName); + if (known != map.end()) + return known->second; + + return None; +} + +Optional +ModuleDependenciesCache::findDependencies(StringRef moduleName, + ModuleLookupSpecifics details) const { + // 1. Query the local scan results + // 2. If no module found, query the global cache using the module details + // lookup + auto localResult = findDependencies(moduleName, details.kind); + if (localResult.hasValue()) + return *(localResult.getValue()); + else + return globalCache.findDependencies(moduleName, details); +} + +bool ModuleDependenciesCache::hasDependencies( + StringRef moduleName, ModuleLookupSpecifics details) const { + return findDependencies(moduleName, details).hasValue(); +} + +void ModuleDependenciesCache::recordDependencies( + StringRef moduleName, ModuleDependencies dependencies) { + auto globalDepPtr = globalCache.recordDependencies(moduleName, dependencies); + auto kind = globalDepPtr->getKind(); + auto &map = getDependencyReferencesMap(kind); + assert(map.count(moduleName) == 0 && "Already added to map"); + map.insert({moduleName, globalDepPtr}); +} + +void ModuleDependenciesCache::updateDependencies( + ModuleDependencyID moduleID, ModuleDependencies dependencies) { + auto globalDepRef = globalCache.updateDependencies(moduleID, dependencies); + auto &map = getDependencyReferencesMap(moduleID.second); + auto known = map.find(moduleID.first); + if (known != map.end()) + map.erase(known); + map.insert({moduleID.first, globalDepRef}); } diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 5931f311952f3..b75845342a72a 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -702,13 +702,9 @@ static void recordShadowedDecls(ArrayRef decls, if (decl->isRecursiveValidation()) continue; - CanGenericSignature signature; - - auto *dc = decl->getInnermostDeclContext(); - if (auto genericSig = dc->getGenericSignatureOfContext()) - signature = genericSig->getCanonicalSignature(); - // Record this declaration based on its signature. + auto *dc = decl->getInnermostDeclContext(); + auto signature = dc->getGenericSignatureOfContext().getCanonicalSignature(); auto &known = collisions[signature.getPointer()]; if (known.size() == 1) { collisionSignatures.push_back(signature.getPointer()); @@ -1027,7 +1023,7 @@ void LazyConformanceLoader::anchor() {} /// Lookup table used to store members of a nominal type (and its extensions) /// for fast retrieval. -class swift::MemberLookupTable { +class swift::MemberLookupTable : public ASTAllocated { /// The last extension that was included within the member lookup table's /// results. ExtensionDecl *LastExtensionIncluded = nullptr; @@ -1111,17 +1107,6 @@ class swift::MemberLookupTable { SWIFT_DEBUG_DUMP { dump(llvm::errs()); } - - // Only allow allocation of member lookup tables using the allocator in - // ASTContext or by doing a placement new. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(MemberLookupTable)) { - return C.Allocate(Bytes, Alignment); - } - void *operator new(size_t Bytes, void *Mem) { - assert(Mem); - return Mem; - } }; namespace { @@ -1140,20 +1125,9 @@ namespace { /// table for lookup based on Objective-C selector. class ClassDecl::ObjCMethodLookupTable : public llvm::DenseMap, - StoredObjCMethods> -{ -public: - // Only allow allocation of member lookup tables using the allocator in - // ASTContext or by doing a placement new. - void *operator new(size_t Bytes, ASTContext &C, - unsigned Alignment = alignof(MemberLookupTable)) { - return C.Allocate(Bytes, Alignment); - } - void *operator new(size_t Bytes, void *Mem) { - assert(Mem); - return Mem; - } -}; + StoredObjCMethods>, + public ASTAllocated +{}; MemberLookupTable::MemberLookupTable(ASTContext &ctx) { // Register a cleanup with the ASTContext to call the lookup table @@ -1401,6 +1375,31 @@ NominalTypeDecl::lookupDirect(DeclName name, DirectLookupRequest({this, name, flags}), {}); } +AbstractFunctionDecl* +NominalTypeDecl::lookupDirectRemoteFunc(AbstractFunctionDecl *func) { + auto &C = func->getASTContext(); + auto *selfTyDecl = func->getParent()->getSelfNominalTypeDecl(); + + // _remote functions only exist as counterparts to a distributed function. + if (!func->isDistributed()) + return nullptr; + + auto localFuncName = func->getBaseIdentifier().str().str(); + auto remoteFuncId = C.getIdentifier("_remote_" + localFuncName); + + auto remoteFuncDecls = selfTyDecl->lookupDirect(DeclName(remoteFuncId)); + + if (remoteFuncDecls.empty()) + return nullptr; + + if (auto remoteDecl = dyn_cast(remoteFuncDecls.front())) { + // TODO: implement more checks here, it has to be the exact right signature. + return remoteDecl; + } + + return nullptr; +} + TinyPtrVector DirectLookupRequest::evaluate(Evaluator &evaluator, DirectLookupDescriptor desc) const { @@ -1413,8 +1412,6 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, ASTContext &ctx = decl->getASTContext(); const bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading && decl->hasLazyMembers()); - const bool disableAdditionalExtensionLoading = - flags.contains(NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions); const bool includeAttrImplements = flags.contains(NominalTypeDecl::LookupDirectFlags::IncludeAttrImplements); @@ -1430,8 +1427,7 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, // If we're allowed to load extensions, call prepareExtensions to ensure we // properly invalidate the lazily-complete cache for any extensions brought in // by modules loaded after-the-fact. This can happen with the LLDB REPL. - if (!disableAdditionalExtensionLoading) - decl->prepareExtensions(); + decl->prepareExtensions(); auto &Table = *decl->LookupTable; if (!useNamedLazyMemberLoading) { @@ -1439,12 +1435,10 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, // all extensions). (void)decl->getMembers(); - if (!disableAdditionalExtensionLoading) { - for (auto E : decl->getExtensions()) - (void)E->getMembers(); + for (auto E : decl->getExtensions()) + (void)E->getMembers(); - Table.updateLookupTable(decl); - } + Table.updateLookupTable(decl); } else if (!Table.isLazilyComplete(name.getBaseName())) { // The lookup table believes it doesn't have a complete accounting of this // name - either because we're never seen it before, or another extension @@ -1452,13 +1446,8 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, // us a hand. DeclBaseName baseName(name.getBaseName()); populateLookupTableEntryFromLazyIDCLoader(ctx, Table, baseName, decl); + populateLookupTableEntryFromExtensions(ctx, Table, baseName, decl); - if (!disableAdditionalExtensionLoading) { - populateLookupTableEntryFromExtensions(ctx, Table, baseName, decl); - } - - // FIXME: If disableAdditionalExtensionLoading is true, we should - // not mark the entry as complete. Table.markLazilyComplete(baseName); } @@ -1683,6 +1672,35 @@ static void installPropertyWrapperMembersIfNeeded(NominalTypeDecl *target, } } +static void installDistributedRemoteFunctionsIfNeeded(NominalTypeDecl *target, + DeclNameRef member) { + auto &Context = target->getASTContext(); + auto baseName = member.getBaseName(); + + if (member.isSimpleName() || baseName.isSpecial()) + return; + + // We only need to install _remote functions. + if (!baseName.getIdentifier().str().startswith("_remote_")) + return; + + // _remote_-prefixed functions can be generated by distributed funcs. + auto prefixLen = strlen("_remote_"); + auto originalFuncName = + Context.getIdentifier( + baseName.getIdentifier().str().substr(prefixLen)); + + for (auto member : target->lookupDirect(originalFuncName)) { + auto func = dyn_cast(member); + if (!func) continue; + + auto sourceFile = func->getDeclContext()->getParentSourceFile(); + if (sourceFile && sourceFile->Kind != SourceFileKind::Interface) { + (void)func->getDistributedActorRemoteFuncDecl(); + } + } +} + bool DeclContext::lookupQualified(ArrayRef typeDecls, DeclNameRef member, NLOptions options, @@ -1736,6 +1754,9 @@ QualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, // Make sure we've resolved property wrappers, if we need them. installPropertyWrapperMembersIfNeeded(current, member); + // Make sure we've resolved synthesized _remote funcs for distributed actors. + installDistributedRemoteFunctionsIfNeeded(current, member); + // Look for results within the current nominal type and its extensions. bool currentIsProtocol = isa(current); auto flags = OptionSet(); @@ -2229,6 +2250,7 @@ directReferencesForTypeRepr(Evaluator &evaluator, case TypeReprKind::Error: case TypeReprKind::Function: case TypeReprKind::InOut: + case TypeReprKind::Isolated: case TypeReprKind::Metatype: case TypeReprKind::Owned: case TypeReprKind::Protocol: @@ -2236,8 +2258,9 @@ directReferencesForTypeRepr(Evaluator &evaluator, case TypeReprKind::SILBox: case TypeReprKind::Placeholder: return { }; - + case TypeReprKind::OpaqueReturn: + case TypeReprKind::NamedOpaqueReturn: return { }; case TypeReprKind::Fixed: @@ -2485,7 +2508,8 @@ GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) c // is implemented. if (auto *proto = ext->getExtendedProtocolDecl()) { auto protoType = proto->getDeclaredInterfaceType(); - TypeLoc selfInherited[1] = { TypeLoc::withoutLoc(protoType) }; + InheritedEntry selfInherited[1] = { + InheritedEntry(TypeLoc::withoutLoc(protoType)) }; genericParams->getParams().front()->setInherited( ctx.AllocateCopy(selfInherited)); } @@ -2505,7 +2529,8 @@ GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) c auto selfDecl = new (ctx) GenericTypeParamDecl( proto, selfId, SourceLoc(), /*depth=*/0, /*index=*/0); auto protoType = proto->getDeclaredInterfaceType(); - TypeLoc selfInherited[1] = { TypeLoc::withoutLoc(protoType) }; + InheritedEntry selfInherited[1] = { + InheritedEntry(TypeLoc::withoutLoc(protoType)) }; selfDecl->setInherited(ctx.AllocateCopy(selfInherited)); selfDecl->setImplicit(); @@ -2635,7 +2660,7 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, void swift::getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, - unsigned i, llvm::SmallVectorImpl> &result, + unsigned i, llvm::SmallVectorImpl &result, bool &anyObject) { auto typeDecl = decl.dyn_cast(); auto extDecl = decl.dyn_cast(); @@ -2657,18 +2682,20 @@ void swift::getDirectlyInheritedNominalTypeDecls( // FIXME: This is a hack. We need cooperation from // InheritedDeclsReferencedRequest to make this work. SourceLoc loc; + SourceLoc uncheckedLoc; if (TypeRepr *typeRepr = typeDecl ? typeDecl->getInherited()[i].getTypeRepr() : extDecl->getInherited()[i].getTypeRepr()){ loc = typeRepr->getLoc(); + uncheckedLoc = typeRepr->findUncheckedAttrLoc(); } // Form the result. for (auto nominal : nominalTypes) { - result.push_back({nominal, loc}); + result.push_back({nominal, loc, uncheckedLoc}); } } -SmallVector, 4> +SmallVector swift::getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, bool &anyObject) { @@ -2678,7 +2705,7 @@ swift::getDirectlyInheritedNominalTypeDecls( // Gather results from all of the inherited types. unsigned numInherited = typeDecl ? typeDecl->getInherited().size() : extDecl->getInherited().size(); - SmallVector, 4> result; + SmallVector result; for (unsigned i : range(numInherited)) { getDirectlyInheritedNominalTypeDecls(decl, i, result, anyObject); } @@ -2704,7 +2731,7 @@ swift::getDirectlyInheritedNominalTypeDecls( if (!req.getFirstType()->isEqual(protoSelfTy)) continue; - result.emplace_back(req.getProtocolDecl(), loc); + result.emplace_back(req.getProtocolDecl(), loc, SourceLoc()); } return result; } @@ -2714,7 +2741,7 @@ swift::getDirectlyInheritedNominalTypeDecls( anyObject |= selfBounds.anyObject; for (auto inheritedNominal : selfBounds.decls) - result.emplace_back(inheritedNominal, loc); + result.emplace_back(inheritedNominal, loc, SourceLoc()); return result; } diff --git a/lib/AST/Parameter.cpp b/lib/AST/Parameter.cpp index 8f3ace9139036..32a66bd58ea34 100644 --- a/lib/AST/Parameter.cpp +++ b/lib/AST/Parameter.cpp @@ -60,8 +60,7 @@ ParameterList *ParameterList::clone(const ASTContext &C, // Remap the ParamDecls inside of the ParameterList. for (auto &decl : params) { - bool hadDefaultArgument = - decl->getDefaultArgumentKind() == DefaultArgumentKind::Normal; + auto defaultArgKind = decl->getDefaultArgumentKind(); decl = ParamDecl::cloneWithoutType(C, decl); if (options & Implicit) @@ -74,11 +73,18 @@ ParameterList *ParameterList::clone(const ASTContext &C, // If we're inheriting a default argument, mark it as such. // FIXME: Figure out how to clone default arguments as well. - if (hadDefaultArgument) { - if (options & Inherited) + if (options & Inherited) { + switch (defaultArgKind) { + case DefaultArgumentKind::Normal: + case DefaultArgumentKind::StoredProperty: decl->setDefaultArgumentKind(DefaultArgumentKind::Inherited); - else - decl->setDefaultArgumentKind(DefaultArgumentKind::None); + break; + + default: + break; + } + } else { + decl->setDefaultArgumentKind(DefaultArgumentKind::None); } } diff --git a/lib/AST/Pattern.cpp b/lib/AST/Pattern.cpp index eed9e33c0341c..9ad387167ecc4 100644 --- a/lib/AST/Pattern.cpp +++ b/lib/AST/Pattern.cpp @@ -291,6 +291,15 @@ bool Pattern::hasStorage() const { return HasStorage; } +bool Pattern::hasAnyMutableBindings() const { + auto HasMutable = false; + forEachVariable([&](VarDecl *VD) { + if (!VD->isLet()) + HasMutable = true; + }); + return HasMutable; +} + /// Return true if this is a non-resolved ExprPattern which is syntactically /// irrefutable. static bool isIrrefutableExprPattern(const ExprPattern *EP) { @@ -345,11 +354,6 @@ case PatternKind::ID: foundRefutablePattern = true; break; return foundRefutablePattern; } -/// Standard allocator for Patterns. -void *Pattern::operator new(size_t numBytes, const ASTContext &C) { - return C.Allocate(numBytes, alignof(Pattern)); -} - /// Find the name directly bound by this pattern. When used as a /// tuple element in a function signature, such names become part of /// the type. diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 9dc0c0f8d20dd..343d5c97271bd 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -186,13 +186,6 @@ ProtocolConformanceRef::getWitnessByName(Type type, DeclName name) const { return getConcrete()->getWitnessDeclRef(requirement); } -void *ProtocolConformance::operator new(size_t bytes, ASTContext &context, - AllocationArena arena, - unsigned alignment) { - return context.Allocate(bytes, alignment, arena); - -} - #define CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \ switch (getKind()) { \ case ProtocolConformanceKind::Normal: \ @@ -203,6 +196,11 @@ switch (getKind()) { \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Inherited: \ return cast(this)->Method Args; \ + case ProtocolConformanceKind::Builtin: \ + static_assert(&ProtocolConformance::Method != \ + &BuiltinProtocolConformance::Method, \ + "Must override BuiltinProtocolConformance::" #Method); \ + return cast(this)->Method Args; \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -214,6 +212,7 @@ switch (getKind()) { \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ case ProtocolConformanceKind::Inherited: \ + case ProtocolConformanceKind::Builtin: \ llvm_unreachable("not a root conformance"); \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -276,6 +275,8 @@ ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement) const { case ProtocolConformanceKind::Specialized: return cast(this) ->getGenericConformance()->getWitnessDecl(requirement); + case ProtocolConformanceKind::Builtin: + return requirement; } llvm_unreachable("unhandled kind"); } @@ -297,6 +298,7 @@ GenericEnvironment *ProtocolConformance::getGenericEnvironment() const { return getDeclContext()->getGenericEnvironmentOfContext(); case ProtocolConformanceKind::Specialized: + case ProtocolConformanceKind::Builtin: // If we have a specialized protocol conformance, since we do not support // currently partial specialization, we know that it cannot have any open // type variables. @@ -317,6 +319,9 @@ GenericSignature ProtocolConformance::getGenericSignature() const { // generic signature. return getDeclContext()->getGenericSignatureOfContext(); + case ProtocolConformanceKind::Builtin: + return cast(this)->getGenericSignature(); + case ProtocolConformanceKind::Specialized: // If we have a specialized protocol conformance, since we do not support // currently partial specialization, we know that it cannot have any open @@ -335,6 +340,7 @@ SubstitutionMap ProtocolConformance::getSubstitutions(ModuleDecl *M) const { switch (parent->getKind()) { case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: llvm_unreachable("should have exited the loop?!"); case ProtocolConformanceKind::Inherited: parent = @@ -532,8 +538,8 @@ ConditionalRequirementsRequest::evaluate(Evaluator &evaluator, // something else. The most important bit is having the same type // parameters. (NB. if/when Swift gets parameterized extensions, this needs to // change.) - assert(typeSig->getCanonicalSignature().getGenericParams() == - extensionSig->getCanonicalSignature().getGenericParams()); + assert(typeSig.getCanonicalSignature().getGenericParams() == + extensionSig.getCanonicalSignature().getGenericParams()); // Find the requirements in the extension that aren't proved by the original // type, these are the ones that make the conformance conditional. @@ -950,9 +956,15 @@ void SpecializedProtocolConformance::computeConditionalRequirements() const { // Substitute the conditional requirements so that they're phrased in // terms of the specialized types, not the conformance-declaring decl's // types. - auto nominal = GenericConformance->getType()->getAnyNominal(); - auto module = nominal->getModuleContext(); - auto subMap = getType()->getContextSubstitutionMap(module, nominal); + ModuleDecl *module; + SubstitutionMap subMap; + if (auto nominal = GenericConformance->getType()->getAnyNominal()) { + module = nominal->getModuleContext(); + subMap = getType()->getContextSubstitutionMap(module, nominal); + } else { + module = getProtocol()->getModuleContext(); + subMap = getSubstitutionMap(); + } SmallVector newReqs; for (auto oldReq : *parentCondReqs) { @@ -1101,6 +1113,7 @@ ProtocolConformance::getRootConformance() const { switch (C->getKind()) { case ProtocolConformanceKind::Normal: case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: return cast(C); case ProtocolConformanceKind::Inherited: C = cast(C) @@ -1150,6 +1163,7 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, subMap); } case ProtocolConformanceKind::Self: + case ProtocolConformanceKind::Builtin: return const_cast(this); case ProtocolConformanceKind::Inherited: { // Substitute the base. @@ -1250,17 +1264,23 @@ void NominalTypeDecl::prepareConformanceTable() const { // Actor classes conform to the actor protocol. if (auto classDecl = dyn_cast(mutableThis)) { - if (classDecl->isActor()) + if (classDecl->isDistributedActor()) + addSynthesized(KnownProtocolKind::DistributedActor); + else if (classDecl->isActor()) addSynthesized(KnownProtocolKind::Actor); } + + // Global actors conform to the GlobalActor protocol. + if (mutableThis->getAttrs().hasAttribute()) { + addSynthesized(KnownProtocolKind::GlobalActor); + } } bool NominalTypeDecl::lookupConformance( - ModuleDecl *module, ProtocolDecl *protocol, + ProtocolDecl *protocol, SmallVectorImpl &conformances) const { prepareConformanceTable(); return ConformanceTable->lookupConformance( - module, const_cast(this), protocol, conformances); @@ -1292,9 +1312,9 @@ void NominalTypeDecl::getImplicitProtocols( } void NominalTypeDecl::registerProtocolConformance( - ProtocolConformance *conformance) { + ProtocolConformance *conformance, bool synthesized) { prepareConformanceTable(); - ConformanceTable->registerProtocolConformance(conformance); + ConformanceTable->registerProtocolConformance(conformance, synthesized); } ArrayRef @@ -1345,6 +1365,9 @@ static ProtocolConformance *findSynthesizedSendableConformance( if (concrete->getDeclContext() != dc) return nullptr; + if (isa(concrete)) + return nullptr; + auto normal = concrete->getRootNormalConformance(); if (!normal || normal->getSourceKind() != ConformanceEntryKind::Synthesized) return nullptr; @@ -1484,7 +1507,8 @@ bool ProtocolConformance::isCanonical() const { switch (getKind()) { case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Normal: { + case ProtocolConformanceKind::Normal: + case ProtocolConformanceKind::Builtin: { return true; } case ProtocolConformanceKind::Inherited: { @@ -1513,7 +1537,8 @@ ProtocolConformance *ProtocolConformance::getCanonicalConformance() { switch (getKind()) { case ProtocolConformanceKind::Self: - case ProtocolConformanceKind::Normal: { + case ProtocolConformanceKind::Normal: + case ProtocolConformanceKind::Builtin: { // Root conformances are always canonical by construction. return this; } @@ -1555,6 +1580,21 @@ ProtocolConformanceRef::getCanonicalConformanceRef() const { return ProtocolConformanceRef(getConcrete()->getCanonicalConformance()); } +BuiltinProtocolConformance::BuiltinProtocolConformance( + Type conformingType, ProtocolDecl *protocol, + GenericSignature genericSig, + ArrayRef conditionalRequirements, + BuiltinConformanceKind kind +) : RootProtocolConformance(ProtocolConformanceKind::Builtin, conformingType), + protocol(protocol), genericSig(genericSig), + numConditionalRequirements(conditionalRequirements.size()), + builtinConformanceKind(static_cast(kind)) +{ + std::uninitialized_copy(conditionalRequirements.begin(), + conditionalRequirements.end(), + getTrailingObjects()); +} + // See swift/Basic/Statistic.h for declaration: this enables tracing // ProtocolConformances, is defined here to avoid too much layering violation / // circular linkage dependency. @@ -1616,3 +1656,34 @@ SourceLoc swift::extractNearestSourceLoc(const ProtocolConformanceRef conformanc } return SourceLoc(); } + +bool ProtocolConformanceRef::hasMissingConformance(ModuleDecl *module) const { + return forEachMissingConformance(module, + [](BuiltinProtocolConformance *builtin) { + return true; + }); +} + +bool ProtocolConformanceRef::forEachMissingConformance( + ModuleDecl *module, + llvm::function_ref fn) const { + if (!isConcrete()) + return false; + + // Is this a missing conformance? + ProtocolConformance *concreteConf = getConcrete(); + RootProtocolConformance *rootConf = concreteConf->getRootConformance(); + if (auto builtinConformance = dyn_cast(rootConf)){ + if (builtinConformance->isMissing() && fn(builtinConformance)) + return true; + } + + // Check conformances that are part of this conformance. + auto subMap = concreteConf->getSubstitutions(module); + for (auto conformance : subMap.getConformances()) { + if (conformance.forEachMissingConformance(module, fn)) + return true; + } + + return false; +} diff --git a/lib/AST/RequirementEnvironment.cpp b/lib/AST/RequirementEnvironment.cpp index bd4f710bfeaa8..8ad429e6048c0 100644 --- a/lib/AST/RequirementEnvironment.cpp +++ b/lib/AST/RequirementEnvironment.cpp @@ -76,7 +76,7 @@ RequirementEnvironment::RequirementEnvironment( ++depth; } if (conformanceSig) { - depth += conformanceSig->getGenericParams().back()->getDepth() + 1; + depth += conformanceSig.getGenericParams().back()->getDepth() + 1; } // Build a substitution map to replace the protocol's \c Self and the type @@ -136,14 +136,11 @@ RequirementEnvironment::RequirementEnvironment( // If the requirement itself is non-generic, the synthetic signature // is that of the conformance context. if (!covariantSelf && - reqSig->getGenericParams().size() == 1 && - reqSig->getRequirements().size() == 1) { - syntheticSignature = conformanceDC->getGenericSignatureOfContext(); - if (syntheticSignature) { - syntheticSignature = syntheticSignature.getCanonicalSignature(); - syntheticEnvironment = - syntheticSignature->getGenericEnvironment(); - } + reqSig.getGenericParams().size() == 1 && + reqSig.getRequirements().size() == 1) { + syntheticSignature = conformanceDC->getGenericSignatureOfContext().getCanonicalSignature(); + syntheticEnvironment = + syntheticSignature.getGenericEnvironment(); return; } @@ -162,7 +159,7 @@ RequirementEnvironment::RequirementEnvironment( // Now, add all generic parameters from the conforming type. if (conformanceSig) { - for (auto param : conformanceSig->getGenericParams()) { + for (auto param : conformanceSig.getGenericParams()) { auto substParam = Type(param).subst(conformanceToSyntheticTypeFn, conformanceToSyntheticConformanceFn); genericParamTypes.push_back(substParam->castTo()); @@ -178,7 +175,7 @@ RequirementEnvironment::RequirementEnvironment( } if (conformanceSig) { - for (auto &rawReq : conformanceSig->getRequirements()) { + for (auto &rawReq : conformanceSig.getRequirements()) { if (auto req = rawReq.subst(conformanceToSyntheticTypeFn, conformanceToSyntheticConformanceFn)) requirements.push_back(*req); @@ -186,7 +183,7 @@ RequirementEnvironment::RequirementEnvironment( } // Finally, add the generic parameters from the requirement. - for (auto genericParam : reqSig->getGenericParams().slice(1)) { + for (auto genericParam : reqSig.getGenericParams().slice(1)) { // The only depth that makes sense is depth == 1, the generic parameters // of the requirement itself. Anything else is from invalid code. if (genericParam->getDepth() != 1) { @@ -204,7 +201,7 @@ RequirementEnvironment::RequirementEnvironment( // Next, add each of the requirements (mapped from the requirement's // interface types into the abstract type parameters). - for (auto &rawReq : reqSig->getRequirements()) { + for (auto &rawReq : reqSig.getRequirements()) { if (auto req = rawReq.subst(reqToSyntheticEnvMap)) requirements.push_back(*req); } @@ -217,5 +214,5 @@ RequirementEnvironment::RequirementEnvironment( AbstractGenericSignatureRequest{ nullptr, std::move(genericParamTypes), std::move(requirements)}, GenericSignature()); - syntheticEnvironment = syntheticSignature->getGenericEnvironment(); + syntheticEnvironment = syntheticSignature.getGenericEnvironment(); } diff --git a/lib/AST/RequirementMachine/Debug.h b/lib/AST/RequirementMachine/Debug.h new file mode 100644 index 0000000000000..9dd4e7be4d01a --- /dev/null +++ b/lib/AST/RequirementMachine/Debug.h @@ -0,0 +1,46 @@ +//===--- Debug.h - Requirement machine debugging flags ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RQM_DEBUG_H +#define SWIFT_RQM_DEBUG_H + +namespace swift { + +namespace rewriting { + +enum class DebugFlags : unsigned { + /// Print debug output when simplifying terms. + Simplify = (1<<0), + + /// Print debug output when adding rules. + Add = (1<<1), + + /// Print debug output when merging associated types. + Merge = (1<<2), + + /// Print debug output from the Knuth-Bendix algorithm. + Completion = (1<<3), + + /// Print debug output when unifying concrete types in the property map. + ConcreteUnification = (1<<4), + + /// Print debug output when concretizing nested types in the property map. + ConcretizeNestedTypes = (1<<5) +}; + +using DebugOptions = OptionSet; + +} + +} + +#endif \ No newline at end of file diff --git a/lib/AST/RequirementMachine/GenericSignatureQueries.cpp b/lib/AST/RequirementMachine/GenericSignatureQueries.cpp new file mode 100644 index 0000000000000..c9a4b17780560 --- /dev/null +++ b/lib/AST/RequirementMachine/GenericSignatureQueries.cpp @@ -0,0 +1,670 @@ +//===--- GenericSignatureQueries.cpp --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// +// +// Implements the various operations on interface types in GenericSignature. +// Use those methods instead of calling into the RequirementMachine directly. +// +//===----------------------------------------------------------------------===// +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/GenericSignature.h" +#include "swift/AST/Module.h" +#include "llvm/ADT/TinyPtrVector.h" +#include + +#include "RequirementMachine.h" + +using namespace swift; +using namespace rewriting; + +/// Collects all requirements on a type parameter that are used to construct +/// its ArchetypeType in a GenericEnvironment. +GenericSignature::LocalRequirements +RequirementMachine::getLocalRequirements( + Type depType, + TypeArrayView genericParams) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto &protos = System.getProtocols(); + + GenericSignature::LocalRequirements result; + result.anchor = Context.getTypeForTerm(term, genericParams, protos); + + auto *props = Map.lookUpProperties(term); + if (!props) + return result; + + if (props->isConcreteType()) { + result.concreteType = props->getConcreteType({}, term, protos, Context); + return result; + } + + if (props->hasSuperclassBound()) { + result.superclass = props->getSuperclassBound({}, term, protos, Context); + } + + for (const auto *proto : props->getConformsToExcludingSuperclassConformances()) + result.protos.push_back(const_cast(proto)); + + result.layout = props->getLayoutConstraint(); + + return result; +} + +bool RequirementMachine::requiresClass(Type depType) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return false; + + if (props->isConcreteType()) + return false; + + auto layout = props->getLayoutConstraint(); + return (layout && layout->isClass()); +} + +LayoutConstraint RequirementMachine::getLayoutConstraint(Type depType) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return LayoutConstraint(); + + return props->getLayoutConstraint(); +} + +bool RequirementMachine::requiresProtocol(Type depType, + const ProtocolDecl *proto) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return false; + + if (props->isConcreteType()) + return false; + + for (auto *otherProto : props->getConformsTo()) { + if (otherProto == proto) + return true; + } + + return false; +} + +GenericSignature::RequiredProtocols +RequirementMachine::getRequiredProtocols(Type depType) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return { }; + + if (props->isConcreteType()) + return { }; + + GenericSignature::RequiredProtocols result; + for (auto *otherProto : props->getConformsTo()) { + result.push_back(const_cast(otherProto)); + } + + ProtocolType::canonicalizeProtocols(result); + + return result; +} + +Type RequirementMachine::getSuperclassBound(Type depType) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return Type(); + + if (!props->hasSuperclassBound()) + return Type(); + + auto &protos = System.getProtocols(); + return props->getSuperclassBound({ }, term, protos, Context); +} + +bool RequirementMachine::isConcreteType(Type depType) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return false; + + return props->isConcreteType(); +} + +Type RequirementMachine::getConcreteType(Type depType) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return Type(); + + if (!props->isConcreteType()) + return Type(); + + auto &protos = System.getProtocols(); + return props->getConcreteType({ }, term, protos, Context); +} + +bool RequirementMachine::areSameTypeParameterInContext(Type depType1, + Type depType2) const { + auto term1 = Context.getMutableTermForType(depType1->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term1); + verify(term1); + + auto term2 = Context.getMutableTermForType(depType2->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term2); + verify(term2); + + return (term1 == term2); +} + +MutableTerm +RequirementMachine::getLongestValidPrefix(const MutableTerm &term) const { + MutableTerm prefix; + + for (auto symbol : term) { + switch (symbol.getKind()) { + case Symbol::Kind::Name: + return prefix; + + case Symbol::Kind::Protocol: + assert(prefix.empty() && + "Protocol symbol can only appear at the start of a type term"); + if (!System.getProtocols().isKnownProtocol(symbol.getProtocol())) + return prefix; + + break; + + case Symbol::Kind::GenericParam: + assert(prefix.empty() && + "Generic parameter symbol can only appear at the start of a type term"); + break; + + case Symbol::Kind::AssociatedType: { + const auto *props = Map.lookUpProperties(prefix); + if (!props) + return prefix; + + auto conformsTo = props->getConformsTo(); + + for (const auto *proto : symbol.getProtocols()) { + if (!System.getProtocols().isKnownProtocol(proto)) + return prefix; + + // T.[P:A] is valid iff T conforms to P. + if (std::find(conformsTo.begin(), conformsTo.end(), proto) + == conformsTo.end()) + return prefix; + } + + break; + } + + case Symbol::Kind::Layout: + case Symbol::Kind::Superclass: + case Symbol::Kind::ConcreteType: + llvm_unreachable("Property symbol cannot appear in a type term"); + } + + // This symbol is valid, add it to the longest prefix. + prefix.add(symbol); + } + + return prefix; +} + +/// Unlike most other queries, the input type can be any type, not just a +/// type parameter. +/// +/// Returns true if all structural components that are type parameters are +/// in their canonical form, and are not concrete (in which case they're +/// not considered canonical, since they can be replaced with their +/// concrete type). +bool RequirementMachine::isCanonicalTypeInContext(Type type) const { + auto &protos = System.getProtocols(); + + // Look for non-canonical type parameters. + return !type.findIf([&](Type component) -> bool { + if (!component->isTypeParameter()) + return false; + + auto term = Context.getMutableTermForType(component->getCanonicalType(), + /*proto=*/nullptr); + + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return false; + + if (props->isConcreteType()) + return true; + + auto anchor = Context.getTypeForTerm(term, {}, protos); + return CanType(anchor) != CanType(component); + }); +} + +/// Unlike most other queries, the input type can be any type, not just a +/// type parameter. +/// +/// Replaces all structural components that are type parameters with their +/// most canonical form, which is either a (possibly different) +/// type parameter, or a concrete type, in which case we recursively +/// simplify any type parameters appearing in structural positions of +/// that concrete type as well, and so on. +Type RequirementMachine::getCanonicalTypeInContext( + Type type, + TypeArrayView genericParams) const { + const auto &protos = System.getProtocols(); + + return type.transformRec([&](Type t) -> Optional { + if (!t->isTypeParameter()) + return None; + + // Get a simplified term T. + auto term = Context.getMutableTermForType(t->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + + // We need to handle "purely concrete" member types, eg if I have a + // signature , and we're asked to canonicalize the + // type T.[P:A] where Foo : A. + // + // This comes up because we can derive the signature + // from a generic signature like ; adding the + // concrete requirement 'T == Foo' renders 'T : P' redundant. We then + // want to take interface types written against the original signature + // and canonicalize them with respect to the derived signature. + // + // The problem is that T.[P:A] is not a valid term in the rewrite system + // for , since we do not have the requirement T : P. + // + // A more principled solution would build a substitution map when + // building a derived generic signature that adds new requirements; + // interface types would first be substituted before being canonicalized + // in the new signature. + // + // For now, we handle this with a two-step process; we split a term up + // into a longest valid prefix, which must resolve to a concrete type, + // and the remaining suffix, which we use to perform a concrete + // substitution using subst(). + + // In the below, let T be a type term, with T == UV, where U is the + // longest valid prefix. + // + // Note that V can be empty if T is fully valid; we expect this to be + // true most of the time. + auto prefix = getLongestValidPrefix(term); + + // Get a type (concrete or dependent) for U. + auto prefixType = [&]() -> Type { + verify(prefix); + + auto *props = Map.lookUpProperties(prefix); + if (props) { + if (props->isConcreteType()) { + auto concreteType = props->getConcreteType(genericParams, + prefix, protos, + Context); + if (!concreteType->hasTypeParameter()) + return concreteType; + + // FIXME: Recursion guard is needed here + return getCanonicalTypeInContext(concreteType, genericParams); + } + + // Skip this part if the entire input term is valid, because in that + // case we don't want to replace the term with its superclass bound; + // unlike a fixed concrete type, the superclass bound only comes into + // play when looking up a member type. + if (props->hasSuperclassBound() && + prefix.size() != term.size()) { + auto superclass = props->getSuperclassBound(genericParams, + prefix, protos, + Context); + if (!superclass->hasTypeParameter()) + return superclass; + + // FIXME: Recursion guard is needed here + return getCanonicalTypeInContext(superclass, genericParams); + } + } + + return Context.getTypeForTerm(prefix, genericParams, protos); + }(); + + // If T is already valid, the longest valid prefix U of T is T itself, and + // V is empty. Just return the type we computed above. + // + // This is the only case where U is allowed to be dependent. + if (prefix.size() == term.size()) + return prefixType; + + // If U is not concrete, we have an invalid member type of a dependent + // type, which is not valid in this generic signature. Give up. + if (prefixType->isTypeParameter()) { + llvm::errs() << "Invalid type parameter in getCanonicalTypeInContext()\n"; + llvm::errs() << "Original type: " << type << "\n"; + llvm::errs() << "Simplified term: " << term << "\n"; + llvm::errs() << "Longest valid prefix: " << prefix << "\n"; + llvm::errs() << "Prefix type: " << prefixType << "\n"; + llvm::errs() << "\n"; + dump(llvm::errs()); + abort(); + } + + // Compute the type of the unresolved suffix term V, rooted in the + // generic parameter τ_0_0. + auto origType = Context.getRelativeTypeForTerm( + term, prefix, System.getProtocols()); + + // Substitute τ_0_0 in the above relative type with the concrete type + // for U. + // + // Example: if T == A.B.C and the longest valid prefix is A.B which + // maps to a concrete type Foo, then we have: + // + // U == A.B + // V == C + // + // prefixType == Foo + // origType == τ_0_0.C + // substType == Foo.C + // + auto substType = origType.subst( + [&](SubstitutableType *type) -> Type { + assert(cast(type)->getDepth() == 0); + assert(cast(type)->getIndex() == 0); + + return prefixType; + }, + LookUpConformanceInSignature(Sig.getPointer())); + + // FIXME: Recursion guard is needed here + return getCanonicalTypeInContext(substType, genericParams); + }); +} + +/// Replace 'Self' in the given dependent type (\c depTy) with the given +/// dependent type, producing a type that refers to the nested type. +static Type replaceSelfWithType(Type selfType, Type depTy) { + if (auto depMemTy = depTy->getAs()) { + Type baseType = replaceSelfWithType(selfType, depMemTy->getBase()); + assert(depMemTy->getAssocType() && "Missing associated type"); + return DependentMemberType::get(baseType, depMemTy->getAssocType()); + } + + assert(depTy->is() && "missing Self?"); + return selfType; +} + +/// Retrieve the conformance access path used to extract the conformance of +/// interface \c type to the given \c protocol. +/// +/// \param type The interface type whose conformance access path is to be +/// queried. +/// \param protocol A protocol to which \c type conforms. +/// +/// \returns the conformance access path that starts at a requirement of +/// this generic signature and ends at the conformance that makes \c type +/// conform to \c protocol. +/// +/// \seealso ConformanceAccessPath +ConformanceAccessPath +RequirementMachine::getConformanceAccessPath(Type type, + ProtocolDecl *protocol) { + auto canType = getCanonicalTypeInContext(type, { })->getCanonicalType(); + assert(canType->isTypeParameter()); + + // Check if we've already cached the result before doing anything else. + auto found = ConformanceAccessPaths.find( + std::make_pair(canType, protocol)); + if (found != ConformanceAccessPaths.end()) { + return found->second; + } + + auto &ctx = Context.getASTContext(); + + FrontendStatsTracer tracer(Stats, "get-conformance-access-path"); + + auto recordPath = [&](CanType type, ProtocolDecl *proto, + ConformanceAccessPath path) { + // Add the path to the buffer. + CurrentConformanceAccessPaths.emplace_back(type, path); + + // Add the path to the map. + auto key = std::make_pair(type, proto); + auto inserted = ConformanceAccessPaths.insert( + std::make_pair(key, path)); + assert(inserted.second); + (void) inserted; + + if (Stats) + ++Stats->getFrontendCounters().NumConformanceAccessPathsRecorded; + }; + + // If this is the first time we're asked to look up a conformance access path, + // visit all of the root conformance requirements in our generic signature and + // add them to the buffer. + if (ConformanceAccessPaths.empty()) { + for (const auto &req : Sig.getRequirements()) { + // We only care about conformance requirements. + if (req.getKind() != RequirementKind::Conformance) + continue; + + auto rootType = CanType(req.getFirstType()); + auto *rootProto = req.getProtocolDecl(); + + ConformanceAccessPath::Entry root(rootType, rootProto); + ArrayRef path(root); + ConformanceAccessPath result(ctx.AllocateCopy(path)); + + recordPath(rootType, rootProto, result); + } + } + + // We enumerate conformance access paths in shortlex order until we find the + // path whose corresponding type canonicalizes to the one we are looking for. + while (true) { + auto found = ConformanceAccessPaths.find( + std::make_pair(canType, protocol)); + if (found != ConformanceAccessPaths.end()) { + return found->second; + } + + if (CurrentConformanceAccessPaths.empty()) { + llvm::errs() << "Failed to find conformance access path for "; + llvm::errs() << type << " " << protocol->getName() << "\n:"; + type.dump(llvm::errs()); + llvm::errs() << "\n"; + dump(llvm::errs()); + abort(); + } + + // The buffer consists of all conformance access paths of length N. + // Swap it out with an empty buffer, and fill it with all paths of + // length N+1. + std::vector> oldPaths; + std::swap(CurrentConformanceAccessPaths, oldPaths); + + for (const auto &pair : oldPaths) { + const auto &lastElt = pair.second.back(); + auto *lastProto = lastElt.second; + + // A copy of the current path, populated as needed. + SmallVector entries; + + for (const auto &req : lastProto->getRequirementSignature()) { + // We only care about conformance requirements. + if (req.getKind() != RequirementKind::Conformance) + continue; + + auto nextSubjectType = req.getFirstType()->getCanonicalType(); + auto *nextProto = req.getProtocolDecl(); + + // Compute the canonical anchor for this conformance requirement. + auto nextType = replaceSelfWithType(pair.first, nextSubjectType); + auto nextCanType = getCanonicalTypeInContext(nextType, { }) + ->getCanonicalType(); + + // Skip "derived via concrete" sources. + if (!nextCanType->isTypeParameter()) + continue; + + // If we've already seen a path for this conformance, skip it and + // don't add it to the buffer. Note that because we iterate over + // conformance access paths in shortlex order, the existing + // conformance access path is shorter than the one we found just now. + if (ConformanceAccessPaths.count( + std::make_pair(nextCanType, nextProto))) + continue; + + if (entries.empty()) { + // Fill our temporary vector. + entries.insert(entries.begin(), + pair.second.begin(), + pair.second.end()); + } + + // Add the next entry. + entries.emplace_back(nextSubjectType, nextProto); + ConformanceAccessPath result = ctx.AllocateCopy(entries); + entries.pop_back(); + + recordPath(nextCanType, nextProto, result); + } + } + } +} + +static void lookupConcreteNestedType(NominalTypeDecl *decl, + Identifier name, + SmallVectorImpl &concreteDecls) { + SmallVector foundMembers; + decl->getParentModule()->lookupQualified( + decl, DeclNameRef(name), + NL_QualifiedDefault | NL_OnlyTypes | NL_ProtocolMembers, + foundMembers); + for (auto member : foundMembers) + concreteDecls.push_back(cast(member)); +} + +static TypeDecl * +findBestConcreteNestedType(SmallVectorImpl &concreteDecls) { + return *std::min_element(concreteDecls.begin(), concreteDecls.end(), + [](TypeDecl *type1, TypeDecl *type2) { + return TypeDecl::compare(type1, type2) < 0; + }); +} + +TypeDecl * +RequirementMachine::lookupNestedType(Type depType, Identifier name) const { + auto term = Context.getMutableTermForType(depType->getCanonicalType(), + /*proto=*/nullptr); + System.simplify(term); + verify(term); + + auto *props = Map.lookUpProperties(term); + if (!props) + return nullptr; + + // Look for types with the given name in protocols that we know about. + AssociatedTypeDecl *bestAssocType = nullptr; + SmallVector concreteDecls; + + for (const auto *proto : props->getConformsTo()) { + // Look for an associated type and/or concrete type with this name. + for (auto member : const_cast(proto)->lookupDirect(name)) { + // If this is an associated type, record whether it is the best + // associated type we've seen thus far. + if (auto assocType = dyn_cast(member)) { + // Retrieve the associated type anchor. + assocType = assocType->getAssociatedTypeAnchor(); + + if (!bestAssocType || + compareAssociatedTypes(assocType, bestAssocType) < 0) + bestAssocType = assocType; + + continue; + } + + // If this is another type declaration, record it. + if (auto type = dyn_cast(member)) { + concreteDecls.push_back(type); + continue; + } + } + } + + // If we haven't found anything yet but have a concrete type or a superclass, + // look for a type in that. + // FIXME: Shouldn't we always look here? + if (!bestAssocType && concreteDecls.empty()) { + Type typeToSearch; + if (props->isConcreteType()) + typeToSearch = props->getConcreteType(); + else if (props->hasSuperclassBound()) + typeToSearch = props->getSuperclassBound(); + + if (typeToSearch) + if (auto *decl = typeToSearch->getAnyNominal()) + lookupConcreteNestedType(decl, name, concreteDecls); + } + + if (bestAssocType) { + assert(bestAssocType->getOverriddenDecls().empty() && + "Lookup should never keep a non-anchor associated type"); + return bestAssocType; + + } else if (!concreteDecls.empty()) { + // Find the best concrete type. + return findBestConcreteNestedType(concreteDecls); + } + + return nullptr; +} diff --git a/lib/AST/RequirementMachine/Histogram.h b/lib/AST/RequirementMachine/Histogram.h new file mode 100644 index 0000000000000..6e26e19bb9fd0 --- /dev/null +++ b/lib/AST/RequirementMachine/Histogram.h @@ -0,0 +1,150 @@ +//===--- Histogram.h - Simple histogram for statistics ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" + +#ifndef SWIFT_HISTOGRAM_H +#define SWIFT_HISTOGRAM_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace swift { + +namespace rewriting { + +/// A simple histogram that supports two operations: recording an element, and +/// printing a graphical representation. +/// +/// Used by the rewrite system to record various statistics, which are dumped +/// when the -analyze-requirement-machine frontend flag is used. +class Histogram { + unsigned Size; + unsigned Start; + std::vector Buckets; + unsigned OverflowBucket; + + const unsigned MaxWidth = 40; + + /// This is not really the base-10 logarithm, but rather the number of digits + /// in \p x when printed in base 10. + /// + /// So log10(0) == 1, log10(1) == 1, log10(10) == 2, etc. + static unsigned log10(unsigned x) { + if (x == 0) + return 1; + unsigned result = 0; + while (x != 0) { + result += 1; + x /= 10; + } + return result; + } + +public: + /// Creates a histogram with \p size buckets, where the numbering begins at + /// \p start. + Histogram(unsigned size, unsigned start = 0) + : Size(size), + Start(start), + Buckets(size, 0), + OverflowBucket(0) { + } + + /// Adds an entry to the histogram. Asserts if the \p value is smaller than + /// Start. The value is allowed to be greater than or equal to Start + Size, + /// in which case it's counted in the OverflowBucket. + void add(unsigned value) { + assert(value >= Start); + value -= Start; + if (value >= Size) + ++OverflowBucket; + else + ++Buckets[value]; + } + + /// Print a nice-looking graphical representation of the histogram. + void dump(llvm::raw_ostream &out, const StringRef labels[] = {}) { + unsigned sumValues = 0; + unsigned maxValue = 0; + for (unsigned i = 0; i < Size; ++i) { + sumValues += Buckets[i]; + maxValue = std::max(maxValue, Buckets[i]); + } + sumValues += OverflowBucket; + maxValue = std::max(maxValue, OverflowBucket); + + size_t maxLabelWidth = 3 + log10(Size + Start); + if (labels != nullptr) { + for (unsigned i = 0; i < Size; ++i) + maxLabelWidth = std::max(labels[i].size(), maxLabelWidth); + } + + out << std::string(maxLabelWidth, ' ') << " |"; + out << std::string(std::min(MaxWidth, maxValue), ' '); + out << maxValue << "\n"; + + out << std::string(maxLabelWidth, ' ') << " |"; + out << std::string(std::min(MaxWidth, maxValue), ' '); + out << "*\n"; + + out << std::string(maxLabelWidth, '-') << "-+-"; + out << std::string(std::min(MaxWidth, maxValue), '-') << "\n"; + + auto scaledValue = [&](unsigned value) { + if (maxValue > MaxWidth) { + if (value != 0) { + // If the value is non-zero, print at least one '#'. + return std::max(1U, (value * MaxWidth) / maxValue); + } + } + return value; + }; + + for (unsigned i = 0; i < Size; ++i) { + if (labels) { + out << std::string(maxLabelWidth - labels[i].size(), ' '); + out << labels[i]; + } else { + unsigned key = i + Start; + out << std::string(maxLabelWidth - log10(key), ' '); + out << key; + } + out << " | "; + + unsigned value = scaledValue(Buckets[i]); + out << std::string(value, '#') << "\n"; + } + + if (OverflowBucket > 0) { + out << ">= " << (Size + Start) << " | "; + + unsigned value = scaledValue(OverflowBucket); + out << std::string(value, '#') << "\n"; + } + + out << std::string(maxLabelWidth, '-') << "-+-"; + out << std::string(std::min(MaxWidth, maxValue), '-') << "\n"; + + out << std::string(maxLabelWidth, ' ') << " | "; + out << "Total: " << sumValues << "\n"; + } +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif \ No newline at end of file diff --git a/lib/AST/RequirementMachine/PropertyMap.cpp b/lib/AST/RequirementMachine/PropertyMap.cpp new file mode 100644 index 0000000000000..a0c4877a8588c --- /dev/null +++ b/lib/AST/RequirementMachine/PropertyMap.cpp @@ -0,0 +1,1106 @@ +//===--- PropertyMap.cpp - Collects properties of type parameters ---------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// +// +// In the rewrite system, a conformance requirement 'T : P' is represented as +// rewrite rule of the form: +// +// T.[P] => T +// +// Similarly, layout, superclass, and concrete-type requirements are represented +// by a rewrite rule of the form: +// +// T.[p] => T +// +// Where [p] is a "property symbol": [layout: L], [superclass: Foo], +// [concrete: Bar]. +// +// Given an arbitrary type T and a property [p], we can check if T satisfies the +// property by checking if the two terms T.[p] and T reduce to the same term T'. +// That is, if our rewrite rules allow us to eliminate the [p] suffix, we know +// the type satisfies [p]. +// +// However, the question then becomes, given an arbitrary type T, how do we find +// *all* properties [p] satisfied by T? +// +// The trick is that we can take advantage of confluence here. +// +// If T.[p] => T', and T => T'', then it must follow that T''.[p] => T'. +// Furthermore, since T'' is fully reduced, T'' == T'. So T'' == UV for some +// terms U and V, and there exist be a rewrite rule V.[p] => V' in the system. +// +// Therefore, in order to find all [p] satisfied by T, we start by fully reducing +// T, then we look for rules of the form V.[p] => V' where V is fully reduced, +// and a suffix of T. +// +// This is the idea behind the property map. We collect all rules of the form +// V.[p] => V into a multi-map keyed by V. Then given an arbitrary type T, +// we can reduce it and look up successive suffixes to find all properties [p] +// satisfied by T. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Decl.h" +#include "swift/AST/LayoutConstraint.h" +#include "swift/AST/Module.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/TypeMatcher.h" +#include "swift/AST/Types.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +#include "PropertyMap.h" + +using namespace swift; +using namespace rewriting; + +/// This papers over a behavioral difference between +/// GenericSignature::getRequiredProtocols() and ArchetypeType::getConformsTo(); +/// the latter drops any protocols to which the superclass requirement +/// conforms to concretely. +llvm::TinyPtrVector +PropertyBag::getConformsToExcludingSuperclassConformances() const { + llvm::TinyPtrVector result; + + if (SuperclassConformances.empty()) { + result = ConformsTo; + return result; + } + + // The conformances in SuperclassConformances should appear in the same order + // as the protocols in ConformsTo. + auto conformanceIter = SuperclassConformances.begin(); + + for (const auto *proto : ConformsTo) { + if (conformanceIter == SuperclassConformances.end()) { + result.push_back(proto); + continue; + } + + if (proto == (*conformanceIter)->getProtocol()) { + ++conformanceIter; + continue; + } + + result.push_back(proto); + } + + assert(conformanceIter == SuperclassConformances.end()); + return result; +} + +void PropertyBag::dump(llvm::raw_ostream &out) const { + out << Key << " => {"; + + if (!ConformsTo.empty()) { + out << " conforms_to: ["; + bool first = true; + for (const auto *proto : ConformsTo) { + if (first) + first = false; + else + out << " "; + out << proto->getName(); + } + out << "]"; + } + + if (Layout) { + out << " layout: " << Layout; + } + + if (Superclass) { + out << " superclass: " << *Superclass; + } + + if (ConcreteType) { + out << " concrete_type: " << *ConcreteType; + } + + out << " }"; +} + +/// Concrete type terms are written in terms of generic parameter types that +/// have a depth of 0, and an index into an array of substitution terms. +/// +/// See RewriteSystemBuilder::getConcreteSubstitutionSchema(). +static unsigned getGenericParamIndex(Type type) { + auto *paramTy = type->castTo(); + assert(paramTy->getDepth() == 0); + return paramTy->getIndex(); +} + +/// Reverses the transformation performed by +/// RewriteSystemBuilder::getConcreteSubstitutionSchema(). +static Type getTypeFromSubstitutionSchema(Type schema, + ArrayRef substitutions, + TypeArrayView genericParams, + const MutableTerm &prefix, + const ProtocolGraph &protos, + RewriteContext &ctx) { + assert(!schema->isTypeParameter() && "Must have a concrete type here"); + + if (!schema->hasTypeParameter()) + return schema; + + return schema.transformRec([&](Type t) -> Optional { + if (t->is()) { + auto index = getGenericParamIndex(t); + auto substitution = substitutions[index]; + + // Prepend the prefix of the lookup key to the substitution. + if (prefix.empty()) { + // Skip creation of a new MutableTerm in the case where the + // prefix is empty. + return ctx.getTypeForTerm(substitution, genericParams, protos); + } else if (substitution.size() == 1 && + substitution[0].getKind() == Symbol::Kind::Protocol) { + // If the prefix is non-empty and the substitution is the + // protocol 'Self' type for some protocol, just use the prefix. + return ctx.getTypeForTerm(prefix, genericParams, protos); + } else { + // Otherwise build a new term by appending the substitution + // to the prefix. + MutableTerm result(prefix); + result.append(substitution); + return ctx.getTypeForTerm(result, genericParams, protos); + } + } + + assert(!t->isTypeParameter()); + return None; + }); +} + +/// Given a term \p lookupTerm whose suffix must equal this property bag's +/// key, return a new term with that suffix stripped off. Will be empty if +/// \p lookupTerm exactly equals the key. +MutableTerm +PropertyBag::getPrefixAfterStrippingKey(const MutableTerm &lookupTerm) const { + assert(lookupTerm.size() >= Key.size()); + auto prefixBegin = lookupTerm.begin(); + auto prefixEnd = lookupTerm.end() - Key.size(); + assert(std::equal(prefixEnd, lookupTerm.end(), Key.begin()) && + "This is not the bag you're looking for"); + return MutableTerm(prefixBegin, prefixEnd); +} + +/// Get the superclass bound for \p lookupTerm, whose suffix must be the term +/// represented by this property bag. +/// +/// The original \p lookupTerm is important in case the concrete type has +/// substitutions. For example, if \p lookupTerm is [P:A].[U:B], and this +/// property bag records that the suffix [U:B] has a superclass symbol +/// [superclass: Cache<τ_0_0> with <[U:C]>], then we actually need to +/// apply the substitution τ_0_0 := [P:A].[U:C] to the type 'Cache<τ_0_0>'. +/// +/// Asserts if this property bag does not have a superclass bound. +Type PropertyBag::getSuperclassBound( + TypeArrayView genericParams, + const MutableTerm &lookupTerm, + const ProtocolGraph &protos, + RewriteContext &ctx) const { + MutableTerm prefix = getPrefixAfterStrippingKey(lookupTerm); + return getTypeFromSubstitutionSchema(Superclass->getSuperclass(), + Superclass->getSubstitutions(), + genericParams, prefix, + protos, ctx); +} + +/// Get the concrete type of the term represented by this property bag. +/// +/// The original \p lookupTerm is important in case the concrete type has +/// substitutions. For example, if \p lookupTerm is [P:A].[U:B], and this +/// property bag records that the suffix [U:B] has a concrete type symbol +/// [concrete: Array<τ_0_0> with <[U:C]>], then we actually need to +/// apply the substitution τ_0_0 := [P:A].[U:C] to the type 'Array<τ_0_0>'. +/// +/// Asserts if this property bag is not concrete. +Type PropertyBag::getConcreteType( + TypeArrayView genericParams, + const MutableTerm &lookupTerm, + const ProtocolGraph &protos, + RewriteContext &ctx) const { + MutableTerm prefix = getPrefixAfterStrippingKey(lookupTerm); + return getTypeFromSubstitutionSchema(ConcreteType->getConcreteType(), + ConcreteType->getSubstitutions(), + genericParams, prefix, + protos, ctx); +} + +/// Computes the term corresponding to a member type access on a substitution. +/// +/// The type witness is a type parameter of the form τ_0_n.X.Y.Z, +/// where 'n' is an index into the substitution array. +/// +/// If the nth entry in the array is S, this will produce S.X.Y.Z. +/// +/// There is a special behavior if the substitution is a term consisting of a +/// single protocol symbol [P]. If the innermost associated type in +/// \p typeWitness is [Q:Foo], the result will be [P:Foo], not [P].[Q:Foo] or +/// [Q:Foo]. +static MutableTerm getRelativeTermForType(CanType typeWitness, + ArrayRef substitutions, + RewriteContext &ctx) { + MutableTerm result; + + // Get the substitution S corresponding to τ_0_n. + unsigned index = getGenericParamIndex(typeWitness->getRootGenericParam()); + result = MutableTerm(substitutions[index]); + + // If the substitution is a term consisting of a single protocol symbol + // [P], save P for later. + const ProtocolDecl *proto = nullptr; + if (result.size() == 1 && + result[0].getKind() == Symbol::Kind::Protocol) { + proto = result[0].getProtocol(); + } + + // Collect zero or more member type names in reverse order. + SmallVector symbols; + while (auto memberType = dyn_cast(typeWitness)) { + typeWitness = memberType.getBase(); + + auto *assocType = memberType->getAssocType(); + assert(assocType != nullptr && + "Conformance checking should not produce unresolved member types"); + + // If the substitution is a term consisting of a single protocol symbol [P], + // produce [P:Foo] instead of [P].[Q:Foo] or [Q:Foo]. + const auto *thisProto = assocType->getProtocol(); + if (proto && isa(typeWitness)) { + thisProto = proto; + + assert(result.size() == 1); + assert(result[0].getKind() == Symbol::Kind::Protocol); + assert(result[0].getProtocol() == proto); + result = MutableTerm(); + } + + symbols.push_back(Symbol::forAssociatedType(thisProto, + assocType->getName(), ctx)); + } + + // Add the member type names. + for (auto iter = symbols.rbegin(), end = symbols.rend(); iter != end; ++iter) + result.add(*iter); + + return result; +} + +/// This method takes a concrete type that was derived from a concrete type +/// produced by RewriteSystemBuilder::getConcreteSubstitutionSchema(), +/// either by extracting a structural sub-component or performing a (Swift AST) +/// substitution using subst(). It returns a new concrete substitution schema +/// and a new list of substitution terms. +/// +/// For example, suppose we start with the concrete type +/// +/// Dictionary<τ_0_0, Array<τ_0_1>> with substitutions {X.Y, Z} +/// +/// We can extract out the structural sub-component Array<τ_0_1>. If we wish +/// to build a new concrete substitution schema, we call this method with +/// Array<τ_0_1> and the original substitutions {X.Y, Z}. This will produce +/// the new schema Array<τ_0_0> with substitutions {Z}. +/// +/// As another example, consider we start with the schema Bar<τ_0_0> with +/// original substitutions {X.Y}, and perform a Swift AST subst() to get +/// Foo<τ_0_0.A.B>. We can then call this method with Foo<τ_0_0.A.B> and +/// the original substitutions {X.Y} to produce the new schema Foo<τ_0_0> +/// with substitutions {X.Y.A.B}. +static CanType +remapConcreteSubstitutionSchema(CanType concreteType, + ArrayRef substitutions, + RewriteContext &ctx, + SmallVectorImpl &result) { + assert(!concreteType->isTypeParameter() && "Must have a concrete type here"); + + if (!concreteType->hasTypeParameter()) + return concreteType; + + return CanType(concreteType.transformRec( + [&](Type t) -> Optional { + if (!t->isTypeParameter()) + return None; + + auto term = getRelativeTermForType(CanType(t), substitutions, ctx); + + unsigned newIndex = result.size(); + result.push_back(Term::get(term, ctx)); + + return CanGenericTypeParamType::get(/*depth=*/0, newIndex, + ctx.getASTContext()); + })); +} + +namespace { + /// Utility class used by unifyConcreteTypes() and unifySuperclasses() + /// to walk two concrete types in parallel. Any time there is a mismatch, + /// records a new induced rule. + class ConcreteTypeMatcher : public TypeMatcher { + ArrayRef lhsSubstitutions; + ArrayRef rhsSubstitutions; + RewriteContext &ctx; + SmallVectorImpl> &inducedRules; + bool debug; + + public: + ConcreteTypeMatcher(ArrayRef lhsSubstitutions, + ArrayRef rhsSubstitutions, + RewriteContext &ctx, + SmallVectorImpl> &inducedRules, + bool debug) + : lhsSubstitutions(lhsSubstitutions), + rhsSubstitutions(rhsSubstitutions), + ctx(ctx), inducedRules(inducedRules), debug(debug) {} + + bool alwaysMismatchTypeParameters() const { return true; } + + bool mismatch(TypeBase *firstType, TypeBase *secondType, + Type sugaredFirstType) { + bool firstAbstract = firstType->isTypeParameter(); + bool secondAbstract = secondType->isTypeParameter(); + + if (firstAbstract && secondAbstract) { + // Both sides are type parameters; add a same-type requirement. + auto lhsTerm = getRelativeTermForType(CanType(firstType), + lhsSubstitutions, ctx); + auto rhsTerm = getRelativeTermForType(CanType(secondType), + rhsSubstitutions, ctx); + if (lhsTerm != rhsTerm) { + if (debug) { + llvm::dbgs() << "%% Induced rule " << lhsTerm + << " == " << rhsTerm << "\n"; + } + inducedRules.emplace_back(lhsTerm, rhsTerm); + } + return true; + } + + if (firstAbstract && !secondAbstract) { + // A type parameter is equated with a concrete type; add a concrete + // type requirement. + auto subjectTerm = getRelativeTermForType(CanType(firstType), + lhsSubstitutions, ctx); + + SmallVector result; + auto concreteType = remapConcreteSubstitutionSchema(CanType(secondType), + rhsSubstitutions, + ctx, result); + + MutableTerm constraintTerm(subjectTerm); + constraintTerm.add(Symbol::forConcreteType(concreteType, result, ctx)); + + if (debug) { + llvm::dbgs() << "%% Induced rule " << subjectTerm + << " == " << constraintTerm << "\n"; + } + inducedRules.emplace_back(subjectTerm, constraintTerm); + return true; + } + + if (!firstAbstract && secondAbstract) { + // A concrete type is equated with a type parameter; add a concrete + // type requirement. + auto subjectTerm = getRelativeTermForType(CanType(secondType), + rhsSubstitutions, ctx); + + SmallVector result; + auto concreteType = remapConcreteSubstitutionSchema(CanType(firstType), + lhsSubstitutions, + ctx, result); + + MutableTerm constraintTerm(subjectTerm); + constraintTerm.add(Symbol::forConcreteType(concreteType, result, ctx)); + + if (debug) { + llvm::dbgs() << "%% Induced rule " << subjectTerm + << " == " << constraintTerm << "\n"; + } + inducedRules.emplace_back(subjectTerm, constraintTerm); + return true; + } + + // Any other kind of type mismatch involves conflicting concrete types on + // both sides, which can only happen on invalid input. + return false; + } + }; +} + +/// When a type parameter has two concrete types, we have to unify the +/// type constructor arguments. +/// +/// For example, suppose that we have two concrete same-type requirements: +/// +/// T == Foo +/// T == Foo +/// +/// These lower to the following two rules: +/// +/// T.[concrete: Foo<τ_0_0, τ_0_1, String> with {X.Y, Z}] => T +/// T.[concrete: Foo with {A.B, W}] => T +/// +/// The two concrete type symbols will be added to the property bag of 'T', +/// and we will eventually end up in this method, where we will generate three +/// induced rules: +/// +/// X.Y.[concrete: Int] => X.Y +/// A.B => Z +/// W.[concrete: String] => W +/// +/// Returns the left hand side on success (it could also return the right hand +/// side; since we unified the type constructor arguments, it doesn't matter). +/// +/// Returns true if a conflict was detected. +static bool unifyConcreteTypes( + Symbol lhs, Symbol rhs, RewriteContext &ctx, + SmallVectorImpl> &inducedRules, + bool debug) { + auto lhsType = lhs.getConcreteType(); + auto rhsType = rhs.getConcreteType(); + + if (debug) { + llvm::dbgs() << "% Unifying " << lhs << " with " << rhs << "\n"; + } + + ConcreteTypeMatcher matcher(lhs.getSubstitutions(), + rhs.getSubstitutions(), + ctx, inducedRules, debug); + if (!matcher.match(lhsType, rhsType)) { + // FIXME: Diagnose the conflict + if (debug) { + llvm::dbgs() << "%% Concrete type conflict\n"; + } + return true; + } + + return false; +} + +/// When a type parameter has two superclasses, we have to both unify the +/// type constructor arguments, and record the most derived superclass. +/// +/// +/// For example, if we have this setup: +/// +/// class Base {} +/// class Middle : Base {} +/// class Derived : Middle {} +/// +/// T : Base +/// T : Derived +/// +/// The most derived superclass requirement is 'T : Derived'. +/// +/// The corresponding superclass of 'Derived' is 'Base', so we +/// unify the type constructor arguments of 'Base' and 'Base', +/// which generates two induced rules: +/// +/// U.[concrete: Int] => U +/// V.[concrete: Int] => V +/// +/// Returns the most derived superclass, which becomes the new superclass +/// that gets recorded in the property map. +static Symbol unifySuperclasses( + Symbol lhs, Symbol rhs, RewriteContext &ctx, + SmallVectorImpl> &inducedRules, + bool debug) { + if (debug) { + llvm::dbgs() << "% Unifying " << lhs << " with " << rhs << "\n"; + } + + auto lhsType = lhs.getSuperclass(); + auto rhsType = rhs.getSuperclass(); + + auto *lhsClass = lhsType.getClassOrBoundGenericClass(); + assert(lhsClass != nullptr); + + auto *rhsClass = rhsType.getClassOrBoundGenericClass(); + assert(rhsClass != nullptr); + + // First, establish the invariant that lhsClass is either equal to, or + // is a superclass of rhsClass. + if (lhsClass == rhsClass || + lhsClass->isSuperclassOf(rhsClass)) { + // Keep going. + } else if (rhsClass->isSuperclassOf(lhsClass)) { + std::swap(rhs, lhs); + std::swap(rhsType, lhsType); + std::swap(rhsClass, lhsClass); + } else { + // FIXME: Diagnose the conflict. + if (debug) { + llvm::dbgs() << "%% Unrelated superclass types\n"; + } + + return lhs; + } + + if (lhsClass != rhsClass) { + // Get the corresponding substitutions for the right hand side. + assert(lhsClass->isSuperclassOf(rhsClass)); + rhsType = rhsType->getSuperclassForDecl(lhsClass) + ->getCanonicalType(); + } + + // Unify type contructor arguments. + ConcreteTypeMatcher matcher(lhs.getSubstitutions(), + rhs.getSubstitutions(), + ctx, inducedRules, debug); + if (!matcher.match(lhsType, rhsType)) { + if (debug) { + llvm::dbgs() << "%% Superclass conflict\n"; + } + return rhs; + } + + // Record the more specific class. + return rhs; +} + +void PropertyBag::addProperty( + Symbol property, RewriteContext &ctx, + SmallVectorImpl> &inducedRules, + bool debug) { + + switch (property.getKind()) { + case Symbol::Kind::Protocol: + ConformsTo.push_back(property.getProtocol()); + return; + + case Symbol::Kind::Layout: + if (!Layout) + Layout = property.getLayoutConstraint(); + else + Layout = Layout.merge(property.getLayoutConstraint()); + + return; + + case Symbol::Kind::Superclass: { + // FIXME: Also handle superclass vs concrete + + if (Superclass) { + Superclass = unifySuperclasses(*Superclass, property, + ctx, inducedRules, debug); + } else { + Superclass = property; + } + + return; + } + + case Symbol::Kind::ConcreteType: { + if (ConcreteType) { + (void) unifyConcreteTypes(*ConcreteType, property, + ctx, inducedRules, debug); + } else { + ConcreteType = property; + } + + return; + } + + case Symbol::Kind::Name: + case Symbol::Kind::GenericParam: + case Symbol::Kind::AssociatedType: + break; + } + + llvm_unreachable("Bad symbol kind"); +} + +void PropertyBag::copyPropertiesFrom(const PropertyBag *next, + RewriteContext &ctx) { + // If this is the property bag of T and 'next' is the + // property bag of V, then T := UV for some non-empty U. + int prefixLength = Key.size() - next->Key.size(); + assert(prefixLength > 0); + assert(std::equal(Key.begin() + prefixLength, Key.end(), + next->Key.begin())); + + // Conformances and the layout constraint, if any, can be copied over + // unmodified. + ConformsTo = next->ConformsTo; + Layout = next->Layout; + + // If the property bag of V has superclass or concrete type + // substitutions {X1, ..., Xn}, then the property bag of + // T := UV should have substitutions {UX1, ..., UXn}. + MutableTerm prefix(Key.begin(), Key.begin() + prefixLength); + + if (next->Superclass) { + Superclass = next->Superclass->prependPrefixToConcreteSubstitutions( + prefix, ctx); + } + + if (next->ConcreteType) { + ConcreteType = next->ConcreteType->prependPrefixToConcreteSubstitutions( + prefix, ctx); + } +} + +PropertyMap::~PropertyMap() { + Trie.updateHistograms(Context.PropertyTrieHistogram, + Context.PropertyTrieRootHistogram); + clear(); +} + +/// Look for an property bag corresponding to a suffix of the given key. +/// +/// Returns nullptr if no information is known about this key. +PropertyBag * +PropertyMap::lookUpProperties(const MutableTerm &key) const { + if (auto result = Trie.find(key.rbegin(), key.rend())) + return *result; + + return nullptr; +} + +/// Look for an property bag corresponding to the given key, creating a new +/// property bag if necessary. +/// +/// This must be called in monotonically non-decreasing key order. +PropertyBag * +PropertyMap::getOrCreateProperties(Term key) { + auto next = Trie.find(key.rbegin(), key.rend()); + if (next && (*next)->getKey() == key) + return *next; + + auto *props = new PropertyBag(key); + + // Look for the longest suffix of the key that has an property bag, + // recording it as the next property bag if we find one. + // + // For example, if our rewrite system contains the following three rules: + // + // A.[P] => A + // B.A.[Q] => B.A + // C.A.[R] => C.A + // + // Then we have three property bags: + // + // A => { [P] } + // B.A => { [Q] } + // C.A => { [R] } + // + // The next property bag of both 'B.A' and 'C.A' is 'A'; conceptually, + // the set of properties satisfied by 'B.A' is a superset of the properties + // satisfied by 'A'; analogously for 'C.A'. + // + // Since 'A' has no proper suffix with additional properties, the next + // property bag of 'A' is nullptr. + if (next) + props->copyPropertiesFrom(*next, Context); + + Entries.push_back(props); + auto oldProps = Trie.insert(key.rbegin(), key.rend(), props); + if (oldProps) { + llvm::errs() << "Duplicate property map entry for " << key << "\n"; + llvm::errs() << "Old:\n"; + (*oldProps)->dump(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "New:\n"; + props->dump(llvm::errs()); + llvm::errs() << "\n"; + abort(); + } + + return props; +} + +void PropertyMap::clear() { + for (auto *props : Entries) + delete props; + + Trie.clear(); + Entries.clear(); + ConcreteTypeInDomainMap.clear(); +} + +/// Record a protocol conformance, layout or superclass constraint on the given +/// key. Must be called in monotonically non-decreasing key order. +void PropertyMap::addProperty( + Term key, Symbol property, + SmallVectorImpl> &inducedRules) { + assert(property.isProperty()); + auto *props = getOrCreateProperties(key); + props->addProperty(property, Context, + inducedRules, Debug.contains(DebugFlags::ConcreteUnification)); +} + +/// For each fully-concrete type, find the shortest term having that concrete type. +/// This is later used by computeConstraintTermForTypeWitness(). +void PropertyMap::computeConcreteTypeInDomainMap() { + for (const auto &props : Entries) { + if (!props->isConcreteType()) + continue; + + auto concreteType = props->ConcreteType->getConcreteType(); + if (concreteType->hasTypeParameter()) + continue; + + assert(props->ConcreteType->getSubstitutions().empty()); + + auto domain = props->Key.getRootProtocols(); + auto concreteTypeKey = std::make_pair(concreteType, domain); + + auto found = ConcreteTypeInDomainMap.find(concreteTypeKey); + if (found != ConcreteTypeInDomainMap.end()) + continue; + + auto inserted = ConcreteTypeInDomainMap.insert( + std::make_pair(concreteTypeKey, props->Key)); + assert(inserted.second); + (void) inserted; + } +} + +void PropertyMap::concretizeNestedTypesFromConcreteParents( + SmallVectorImpl> &inducedRules) const { + for (const auto &props : Entries) { + if (props->getConformsTo().empty()) + continue; + + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + if (props->isConcreteType() || + props->hasSuperclassBound()) { + llvm::dbgs() << "^ Concretizing nested types of "; + props->dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + } + + if (props->isConcreteType()) { + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "- via concrete type requirement\n"; + } + + concretizeNestedTypesFromConcreteParent( + props->getKey(), + RequirementKind::SameType, + props->ConcreteType->getConcreteType(), + props->ConcreteType->getSubstitutions(), + props->getConformsTo(), + props->ConcreteConformances, + inducedRules); + } + + if (props->hasSuperclassBound()) { + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "- via superclass requirement\n"; + } + + concretizeNestedTypesFromConcreteParent( + props->getKey(), + RequirementKind::Superclass, + props->Superclass->getSuperclass(), + props->Superclass->getSubstitutions(), + props->getConformsTo(), + props->SuperclassConformances, + inducedRules); + } + } +} + +/// Suppose a same-type requirement merges two property bags, +/// one of which has a conformance requirement to P and the other +/// one has a concrete type or superclass requirement. +/// +/// If the concrete type or superclass conforms to P and P has an +/// associated type A, then we need to infer an equivalence between +/// T.[P:A] and whatever the type witness for 'A' is in the +/// concrete conformance. +/// +/// For example, suppose we have a the following definitions, +/// +/// protocol Q { associatedtype V } +/// protocol P { associatedtype A; associatedtype C } +/// struct Foo : P { +/// typealias C = B.V +/// } +/// +/// together with the following property bag: +/// +/// T => { conforms_to: [ P ], concrete: Foo with } +/// +/// The type witness for A in the conformance Foo : P is +/// the concrete type 'Int', which induces the following rule: +/// +/// T.[P:A].[concrete: Int] => T.[P:A] +/// +/// Whereas the type witness for B in the same conformance is the +/// abstract type 'τ_0_0.V', which via the substitutions corresponds +/// to the term 'U.V', and therefore induces the following rule: +/// +/// T.[P:B] => U.V +/// +void PropertyMap::concretizeNestedTypesFromConcreteParent( + Term key, RequirementKind requirementKind, + CanType concreteType, ArrayRef substitutions, + ArrayRef conformsTo, + llvm::TinyPtrVector &conformances, + SmallVectorImpl> &inducedRules) const { + assert(requirementKind == RequirementKind::SameType || + requirementKind == RequirementKind::Superclass); + + for (auto *proto : conformsTo) { + // FIXME: Either remove the ModuleDecl entirely from conformance lookup, + // or pass the correct one down in here. + auto *module = proto->getParentModule(); + + auto conformance = module->lookupConformance(concreteType, + const_cast(proto)); + if (conformance.isInvalid()) { + // FIXME: Diagnose conflict + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ " << concreteType << " does not conform to " + << proto->getName() << "\n"; + } + + continue; + } + + // FIXME: Maybe this can happen if the concrete type is an + // opaque result type? + assert(!conformance.isAbstract()); + + auto *concrete = conformance.getConcrete(); + + // Record the conformance for use by + // PropertyBag::getConformsToExcludingSuperclassConformances(). + conformances.push_back(concrete); + + auto assocTypes = Protos.getProtocolInfo(proto).AssociatedTypes; + if (assocTypes.empty()) + continue; + + for (auto *assocType : assocTypes) { + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ " << "Looking up type witness for " + << proto->getName() << ":" << assocType->getName() + << " on " << concreteType << "\n"; + } + + auto t = concrete->getTypeWitness(assocType); + if (!t) { + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ " << "Type witness for " << assocType->getName() + << " of " << concreteType << " could not be inferred\n"; + } + + t = ErrorType::get(concreteType); + } + + auto typeWitness = t->getCanonicalType(); + + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ " << "Type witness for " << assocType->getName() + << " of " << concreteType << " is " << typeWitness << "\n"; + } + + MutableTerm subjectType(key); + subjectType.add(Symbol::forAssociatedType(proto, assocType->getName(), + Context)); + + MutableTerm constraintType; + + if (concreteType == typeWitness && + requirementKind == RequirementKind::SameType) { + // FIXME: ConcreteTypeInDomainMap should support substitutions so + // that we can remove this. + + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ Type witness is the same as the concrete type\n"; + } + + // Add a rule T.[P:A] => T. + constraintType = MutableTerm(key); + } else { + constraintType = computeConstraintTermForTypeWitness( + key, concreteType, typeWitness, subjectType, + substitutions); + } + + inducedRules.emplace_back(subjectType, constraintType); + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ Induced rule " << constraintType + << " => " << subjectType << "\n"; + } + } + } +} + +/// Given the key of an property bag known to have \p concreteType, +/// together with a \p typeWitness from a conformance on that concrete +/// type, return the right hand side of a rewrite rule to relate +/// \p subjectType with a term representing the type witness. +/// +/// Suppose the key is T and the subject type is T.[P:A]. +/// +/// If the type witness is an abstract type U, this produces a rewrite +/// rule +/// +/// T.[P:A] => U +/// +/// If the type witness is a concrete type Foo, this produces a rewrite +/// rule +/// +/// T.[P:A].[concrete: Foo] => T.[P:A] +/// +/// However, this also tries to tie off recursion first using a heuristic. +/// +/// If the type witness is fully concrete and we've already seen some +/// term V in the same domain with the same concrete type, we produce a +/// rewrite rule: +/// +/// T.[P:A] => V +MutableTerm PropertyMap::computeConstraintTermForTypeWitness( + Term key, CanType concreteType, CanType typeWitness, + const MutableTerm &subjectType, ArrayRef substitutions) const { + if (!typeWitness->hasTypeParameter()) { + // Check if we have a shorter representative we can use. + auto domain = key.getRootProtocols(); + auto concreteTypeKey = std::make_pair(typeWitness, domain); + + auto found = ConcreteTypeInDomainMap.find(concreteTypeKey); + if (found != ConcreteTypeInDomainMap.end()) { + MutableTerm result(found->second); + if (result != subjectType) { + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ Type witness can re-use property bag of " + << found->second << "\n"; + } + return result; + } + } + } + + if (typeWitness->isTypeParameter()) { + // The type witness is a type parameter of the form τ_0_n.X.Y...Z, + // where 'n' is an index into the substitution array. + // + // Add a rule T => S.X.Y...Z, where S is the nth substitution term. + return getRelativeTermForType(typeWitness, substitutions, Context); + } + + // The type witness is a concrete type. + MutableTerm constraintType = subjectType; + + SmallVector result; + auto typeWitnessSchema = + remapConcreteSubstitutionSchema(typeWitness, substitutions, + Context, result); + + // Add a rule T.[P:A].[concrete: Foo.A] => T.[P:A]. + constraintType.add( + Symbol::forConcreteType( + typeWitnessSchema, result, Context)); + + return constraintType; +} + +void PropertyMap::dump(llvm::raw_ostream &out) const { + out << "Property map: {\n"; + for (const auto &props : Entries) { + out << " "; + props->dump(out); + out << "\n"; + } + out << "}\n"; +} + +/// Build the property map from all rules of the form T.[p] => T, where +/// [p] is a property symbol. +/// +/// Returns a pair consisting of a status and number of iterations executed. +/// +/// The status is CompletionResult::MaxIterations if we exceed \p maxIterations +/// iterations. +/// +/// The status is CompletionResult::MaxDepth if we produce a rewrite rule whose +/// left hand side has a length exceeding \p maxDepth. +/// +/// Otherwise, the status is CompletionResult::Success. +std::pair +RewriteSystem::buildPropertyMap(PropertyMap &map, + unsigned maxIterations, + unsigned maxDepth) { + map.clear(); + + // PropertyMap::addRule() requires that shorter rules are added + // before longer rules, so that it can perform lookups on suffixes and call + // PropertyBag::copyPropertiesFrom(). However, we don't have to perform a + // full sort by term order here; a bucket sort by term length suffices. + SmallVector>, 4> properties; + + for (const auto &rule : Rules) { + if (rule.isDeleted()) + continue; + + auto lhs = rule.getLHS(); + auto rhs = rule.getRHS(); + + // Collect all rules of the form T.[p] => T where T is canonical. + auto property = lhs.back(); + if (!property.isProperty()) + continue; + + if (lhs.size() - 1 != rhs.size()) + continue; + + if (!std::equal(rhs.begin(), rhs.end(), lhs.begin())) + continue; + + if (rhs.size() >= properties.size()) + properties.resize(rhs.size() + 1); + properties[rhs.size()].emplace_back(rhs, property); + } + + // Merging multiple superclass or concrete type rules can induce new rules + // to unify concrete type constructor arguments. + SmallVector, 3> inducedRules; + + for (const auto &bucket : properties) { + for (auto pair : bucket) { + map.addProperty(pair.first, pair.second, inducedRules); + } + } + + // We collect terms with fully concrete types so that we can re-use them + // to tie off recursion in the next step. + map.computeConcreteTypeInDomainMap(); + + // Now, we merge concrete type rules with conformance rules, by adding + // relations between associated type members of type parameters with + // the concrete type witnesses in the concrete type's conformance. + map.concretizeNestedTypesFromConcreteParents(inducedRules); + + // Some of the induced rules might be trivial; only count the induced rules + // where the left hand side is not already equivalent to the right hand side. + unsigned addedNewRules = 0; + for (auto pair : inducedRules) { + if (addRule(pair.first, pair.second)) { + ++addedNewRules; + + const auto &newRule = Rules.back(); + if (newRule.getLHS().size() > maxDepth) + return std::make_pair(CompletionResult::MaxDepth, addedNewRules); + } + } + + if (Rules.size() > maxIterations) + return std::make_pair(CompletionResult::MaxIterations, addedNewRules); + + return std::make_pair(CompletionResult::Success, addedNewRules); +} \ No newline at end of file diff --git a/lib/AST/RequirementMachine/PropertyMap.h b/lib/AST/RequirementMachine/PropertyMap.h new file mode 100644 index 0000000000000..55878363fb5c4 --- /dev/null +++ b/lib/AST/RequirementMachine/PropertyMap.h @@ -0,0 +1,196 @@ +//===--- PropertyMap.h - Properties of type parameter terms 0000-*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// +// +// A description of this data structure and its purpose can be found in +// PropertyMap.cpp. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_PROPERTYMAP_H +#define SWIFT_PROPERTYMAP_H + +#include "swift/AST/LayoutConstraint.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/TinyPtrVector.h" +#include +#include + +#include "Debug.h" +#include "ProtocolGraph.h" +#include "RewriteContext.h" +#include "RewriteSystem.h" + +namespace llvm { + class raw_ostream; +} + +namespace swift { + +class ProtocolDecl; +enum class RequirementKind : unsigned; + +namespace rewriting { + +class MutableTerm; +class RewriteContext; +class Term; + +/// Stores a convenient representation of all "property-like" rewrite rules of +/// the form T.[p] => T, where [p] is a property symbol, for some term 'T'. +class PropertyBag { + friend class PropertyMap; + + /// The fully reduced term whose properties are recorded in this property bag. + Term Key; + + /// All protocols this type conforms to. + llvm::TinyPtrVector ConformsTo; + + /// The most specific layout constraint this type satisfies. + LayoutConstraint Layout; + + /// The most specific superclass constraint this type satisfies. + Optional Superclass; + + /// All concrete conformances of Superclass to the protocols in the + /// ConformsTo list. + llvm::TinyPtrVector SuperclassConformances; + + /// The most specific concrete type constraint this type satisfies. + Optional ConcreteType; + + /// All concrete conformances of ConcreteType to the protocols in the + /// ConformsTo list. + llvm::TinyPtrVector ConcreteConformances; + + explicit PropertyBag(Term key) : Key(key) {} + + void addProperty(Symbol property, + RewriteContext &ctx, + SmallVectorImpl> &inducedRules, + bool debug); + void copyPropertiesFrom(const PropertyBag *next, + RewriteContext &ctx); + + PropertyBag(const PropertyBag &) = delete; + PropertyBag(PropertyBag &&) = delete; + PropertyBag &operator=(const PropertyBag &) = delete; + PropertyBag &operator=(PropertyBag &&) = delete; + +public: + Term getKey() const { return Key; } + void dump(llvm::raw_ostream &out) const; + + bool hasSuperclassBound() const { + return Superclass.hasValue(); + } + + Type getSuperclassBound() const { + return Superclass->getSuperclass(); + } + + Type getSuperclassBound( + TypeArrayView genericParams, + const MutableTerm &lookupTerm, + const ProtocolGraph &protos, + RewriteContext &ctx) const; + + bool isConcreteType() const { + return ConcreteType.hasValue(); + } + + Type getConcreteType() const { + return ConcreteType->getConcreteType(); + } + + Type getConcreteType( + TypeArrayView genericParams, + const MutableTerm &lookupTerm, + const ProtocolGraph &protos, + RewriteContext &ctx) const; + + LayoutConstraint getLayoutConstraint() const { + return Layout; + } + + ArrayRef getConformsTo() const { + return ConformsTo; + } + + llvm::TinyPtrVector + getConformsToExcludingSuperclassConformances() const; + + MutableTerm getPrefixAfterStrippingKey(const MutableTerm &lookupTerm) const; +}; + +/// Stores all rewrite rules of the form T.[p] => T, where [p] is a property +/// symbol, for all terms 'T'. +/// +/// Out-of-line methods are documented in PropertyMap.cpp. +class PropertyMap { + RewriteContext &Context; + std::vector Entries; + Trie Trie; + + using ConcreteTypeInDomain = std::pair>; + llvm::DenseMap ConcreteTypeInDomainMap; + + const ProtocolGraph &Protos; + DebugOptions Debug; + + PropertyBag *getOrCreateProperties(Term key); + + PropertyMap(const PropertyMap &) = delete; + PropertyMap(PropertyMap &&) = delete; + PropertyMap &operator=(const PropertyMap &) = delete; + PropertyMap &operator=(PropertyMap &&) = delete; + +public: + explicit PropertyMap(RewriteContext &ctx, + const ProtocolGraph &protos) + : Context(ctx), Protos(protos) { + Debug = ctx.getDebugOptions(); + } + + ~PropertyMap(); + + PropertyBag *lookUpProperties(const MutableTerm &key) const; + + void dump(llvm::raw_ostream &out) const; + + void clear(); + void addProperty(Term key, Symbol property, + SmallVectorImpl> &inducedRules); + + void computeConcreteTypeInDomainMap(); + void concretizeNestedTypesFromConcreteParents( + SmallVectorImpl> &inducedRules) const; + +private: + void concretizeNestedTypesFromConcreteParent( + Term key, RequirementKind requirementKind, + CanType concreteType, ArrayRef substitutions, + ArrayRef conformsTo, + llvm::TinyPtrVector &conformances, + SmallVectorImpl> &inducedRules) const; + + MutableTerm computeConstraintTermForTypeWitness( + Term key, CanType concreteType, CanType typeWitness, + const MutableTerm &subjectType, ArrayRef substitutions) const; +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif \ No newline at end of file diff --git a/lib/AST/RequirementMachine/ProtocolGraph.cpp b/lib/AST/RequirementMachine/ProtocolGraph.cpp new file mode 100644 index 0000000000000..325f3a06d9447 --- /dev/null +++ b/lib/AST/RequirementMachine/ProtocolGraph.cpp @@ -0,0 +1,212 @@ +//===--- ProtocolGraph.cpp - Collect information about protocols ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "ProtocolGraph.h" + +#include "swift/AST/Decl.h" +#include "swift/AST/Requirement.h" + +using namespace swift; +using namespace rewriting; + +/// Adds information about all protocols transitvely referenced from +/// \p reqs. +void ProtocolGraph::visitRequirements(ArrayRef reqs) { + for (auto req : reqs) { + if (req.getKind() == RequirementKind::Conformance) { + addProtocol(req.getProtocolDecl()); + } + } +} + +/// Return true if we know about this protocol. +bool ProtocolGraph::isKnownProtocol(const ProtocolDecl *proto) const { + return Info.count(proto) > 0; +} + +/// Look up information about a known protocol. +const ProtocolInfo &ProtocolGraph::getProtocolInfo( + const ProtocolDecl *proto) const { + auto found = Info.find(proto); + assert(found != Info.end()); + return found->second; +} + +/// Record information about a protocol if we have no seen it yet. +void ProtocolGraph::addProtocol(const ProtocolDecl *proto) { + if (Info.count(proto) > 0) + return; + + Info[proto] = {proto->getInheritedProtocols(), + proto->getAssociatedTypeMembers(), + proto->getRequirementSignature()}; + Protocols.push_back(proto); +} + +/// Record information about all protocols transtively referenced +/// from protocol requirement signatures. +void ProtocolGraph::computeTransitiveClosure() { + unsigned i = 0; + while (i < Protocols.size()) { + auto *proto = Protocols[i++]; + visitRequirements(getProtocolInfo(proto).Requirements); + } +} + +/// See ProtocolGraph::compareProtocols() for the definition of this linear +/// order. +void ProtocolGraph::computeLinearOrder() { + for (const auto *proto : Protocols) { + (void) computeProtocolDepth(proto); + } + + std::sort( + Protocols.begin(), Protocols.end(), + [&](const ProtocolDecl *lhs, + const ProtocolDecl *rhs) -> bool { + const auto &lhsInfo = getProtocolInfo(lhs); + const auto &rhsInfo = getProtocolInfo(rhs); + + // protocol Base {} // depth 1 + // protocol Derived : Base {} // depth 2 + // + // Derived < Base in the linear order. + if (lhsInfo.Depth != rhsInfo.Depth) + return lhsInfo.Depth > rhsInfo.Depth; + + return TypeDecl::compare(lhs, rhs) < 0; + }); + + for (unsigned i : indices(Protocols)) { + Info[Protocols[i]].Index = i; + } + + if (Debug) { + for (const auto *proto : Protocols) { + const auto &info = getProtocolInfo(proto); + llvm::dbgs() << "@ Protocol " << proto->getName() + << " Depth=" << info.Depth + << " Index=" << info.Index << "\n"; + } + } +} + +/// Update each ProtocolInfo's AssociatedTypes vector to add all associated +/// types from all transitively inherited protocols. +void ProtocolGraph::computeInheritedAssociatedTypes() { + // Visit protocols in reverse order, so that if P inherits from Q and + // Q inherits from R, we first visit R, then Q, then P, ensuring that + // R's associated types are added to P's list, etc. + for (const auto *proto : llvm::reverse(Protocols)) { + auto &info = Info[proto]; + + for (const auto *inherited : info.AllInherited) { + for (auto *inheritedType : getProtocolInfo(inherited).AssociatedTypes) { + info.InheritedAssociatedTypes.push_back(inheritedType); + } + } + } +} + +// Update each protocol's AllInherited vector to add all transitively +// inherited protocols. +void ProtocolGraph::computeInheritedProtocols() { + // Visit protocols in reverse order, so that if P inherits from Q and + // Q inherits from R, we first visit R, then Q, then P, ensuring that + // R's inherited protocols are added to P's list, etc. + for (const auto *proto : llvm::reverse(Protocols)) { + auto &info = Info[proto]; + + // We might inherit the same protocol multiple times due to diamond + // inheritance, so make sure we only add each protocol once. + llvm::SmallDenseSet visited; + visited.insert(proto); + + for (const auto *inherited : info.Inherited) { + // Add directly-inherited protocols. + if (!visited.insert(inherited).second) + continue; + info.AllInherited.push_back(inherited); + + // Add indirectly-inherited protocols. + for (auto *inheritedType : getProtocolInfo(inherited).AllInherited) { + if (!visited.insert(inheritedType).second) + continue; + + info.AllInherited.push_back(inheritedType); + } + } + } +} + +/// Recursively compute the 'depth' of a protocol, which is inductively defined +/// as one greater than the depth of all inherited protocols, with a protocol +/// that does not inherit any other protocol having a depth of one. +unsigned ProtocolGraph::computeProtocolDepth(const ProtocolDecl *proto) { + auto &info = Info[proto]; + + if (info.Mark) { + // Already computed, or we have a cycle. Cycles are diagnosed + // elsewhere in the type checker, so we don't have to do + // anything here. + return info.Depth; + } + + info.Mark = true; + unsigned depth = 0; + + for (auto *inherited : info.Inherited) { + unsigned inheritedDepth = computeProtocolDepth(inherited); + depth = std::max(inheritedDepth, depth); + } + + depth++; + + info.Depth = depth; + return depth; +} + +/// Compute everything in the right order. +void ProtocolGraph::compute() { + computeTransitiveClosure(); + computeLinearOrder(); + computeInheritedProtocols(); + computeInheritedAssociatedTypes(); +} + +/// Defines a linear order with the property that if a protocol P inherits +/// from another protocol Q, then P < Q. (The converse cannot be true, since +/// this is a linear order.) +/// +/// We first compare the 'depth' of a protocol, which is defined in +/// ProtocolGraph::computeProtocolDepth() above. +/// +/// If two protocols have the same depth, the tie is broken by the standard +/// TypeDecl::compare(). +int ProtocolGraph::compareProtocols(const ProtocolDecl *lhs, + const ProtocolDecl *rhs) const { + const auto &infoLHS = getProtocolInfo(lhs); + const auto &infoRHS = getProtocolInfo(rhs); + + return infoLHS.Index - infoRHS.Index; +} + +/// Returns if \p thisProto transitively inherits from \p otherProto. +/// +/// The result is false if the two protocols are equal. +bool ProtocolGraph::inheritsFrom(const ProtocolDecl *thisProto, + const ProtocolDecl *otherProto) const { + const auto &info = getProtocolInfo(thisProto); + return std::find(info.AllInherited.begin(), + info.AllInherited.end(), + otherProto) != info.AllInherited.end(); +} diff --git a/lib/AST/RequirementMachine/ProtocolGraph.h b/lib/AST/RequirementMachine/ProtocolGraph.h new file mode 100644 index 0000000000000..8f6cca19eec94 --- /dev/null +++ b/lib/AST/RequirementMachine/ProtocolGraph.h @@ -0,0 +1,126 @@ +//===--- ProtocolGraph.h - Collects information about protocols -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_PROTOCOLGRAPH_H +#define SWIFT_PROTOCOLGRAPH_H + +#include "swift/AST/Requirement.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/TinyPtrVector.h" + +namespace swift { + +class ProtocolDecl; +class AssociatedTypeDecl; + +namespace rewriting { + +/// Stores cached information about a protocol. +struct ProtocolInfo { + /// All immediately-inherited protocols. + ArrayRef Inherited; + + /// Transitive closure of inherited protocols; does not include the protocol + /// itself. Computed by ProtocolGraph::computeInheritedProtocols(). + llvm::TinyPtrVector AllInherited; + + /// Associated types defined in the protocol itself. + ArrayRef AssociatedTypes; + + /// Associated types from all inherited protocols, not including duplicates or + /// those defined in the protocol itself. Computed by + /// ProtocolGraph::computeInheritedAssociatedTypes(). + llvm::TinyPtrVector InheritedAssociatedTypes; + + /// The protocol's requirement signature. + ArrayRef Requirements; + + /// Used by ProtocolGraph::computeProtocolDepth() to detect circularity. + unsigned Mark : 1; + + /// Longest chain of protocol refinements, including this one. Greater than + /// zero on valid code, might be zero if there's a cycle. Computed by + /// ProtocolGraph::computeLinearOrder(). + unsigned Depth : 31; + + /// Index of the protocol in the linear order. Computed by + /// ProtocolGraph::computeLinearOrder(). + unsigned Index : 32; + + ProtocolInfo() { + Mark = 0; + Depth = 0; + Index = 0; + } + + ProtocolInfo(ArrayRef inherited, + ArrayRef &&types, + ArrayRef reqs) + : Inherited(inherited), + AssociatedTypes(types), + Requirements(reqs) { + Mark = 0; + Depth = 0; + Index = 0; + } +}; + +/// Stores cached information about all protocols transtively +/// referenced from a set of generic requirements. +/// +/// Out-of-line methods are documented in ProtocolGraph.cpp. +class ProtocolGraph { + llvm::DenseMap Info; + std::vector Protocols; + bool Debug = false; + +public: + void visitRequirements(ArrayRef reqs); + + bool isKnownProtocol(const ProtocolDecl *proto) const; + + /// Returns the sorted list of protocols, with the property + /// that (P refines Q) => P < Q. See compareProtocols() + /// for details. + ArrayRef getProtocols() const { + return Protocols; + } + + const ProtocolInfo &getProtocolInfo( + const ProtocolDecl *proto) const; + +private: + void addProtocol(const ProtocolDecl *proto); + void computeTransitiveClosure(); + void computeLinearOrder(); + void computeInheritedAssociatedTypes(); + void computeInheritedProtocols(); + +public: + void compute(); + + int compareProtocols(const ProtocolDecl *lhs, + const ProtocolDecl *rhs) const; + + bool inheritsFrom(const ProtocolDecl *thisProto, + const ProtocolDecl *otherProto) const; + +private: + unsigned computeProtocolDepth(const ProtocolDecl *proto); +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif \ No newline at end of file diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp new file mode 100644 index 0000000000000..62c801e466f65 --- /dev/null +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -0,0 +1,449 @@ +//===--- RequirementMachine.cpp - Generics with term rewriting ------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/GenericSignature.h" +#include "swift/AST/Module.h" +#include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/Requirement.h" +#include + +#include "RequirementMachine.h" + +using namespace swift; +using namespace rewriting; + +namespace { + +/// A utility class for bulding a rewrite system from the top-level requirements +/// of a generic signature, and all protocol requirement signatures from all +/// transitively-referenced protocols. +struct RewriteSystemBuilder { + RewriteContext &Context; + bool Dump; + + ProtocolGraph Protocols; + std::vector> Rules; + + CanType getConcreteSubstitutionSchema(CanType concreteType, + const ProtocolDecl *proto, + SmallVectorImpl &result); + + RewriteSystemBuilder(RewriteContext &ctx, bool dump) + : Context(ctx), Dump(dump) {} + void addGenericSignature(CanGenericSignature sig); + void addAssociatedType(const AssociatedTypeDecl *type, + const ProtocolDecl *proto); + void addRequirement(const Requirement &req, + const ProtocolDecl *proto); +}; + +} // end namespace + +/// Given a concrete type that may contain type parameters in structural positions, +/// collect all the structural type parameter components, and replace them all with +/// fresh generic parameters. The fresh generic parameters all have a depth of 0, +/// and the index is an index into the 'result' array. +/// +/// For example, given the concrete type Foo>, this produces the +/// result type Foo<τ_0_0, Array<τ_0_1>>, with result array {X.Y, Z}. +CanType +RewriteSystemBuilder::getConcreteSubstitutionSchema(CanType concreteType, + const ProtocolDecl *proto, + SmallVectorImpl &result) { + assert(!concreteType->isTypeParameter() && "Must have a concrete type here"); + + if (!concreteType->hasTypeParameter()) + return concreteType; + + return CanType(concreteType.transformRec( + [&](Type t) -> Optional { + if (!t->isTypeParameter()) + return None; + + unsigned index = result.size(); + result.push_back(Context.getTermForType(CanType(t), proto)); + + return CanGenericTypeParamType::get(/*depth=*/0, index, Context.getASTContext()); + })); +} + +void RewriteSystemBuilder::addGenericSignature(CanGenericSignature sig) { + // Collect all protocols transitively referenced from the generic signature's + // requirements. + Protocols.visitRequirements(sig.getRequirements()); + Protocols.compute(); + + // Add rewrite rules for each protocol. + for (auto *proto : Protocols.getProtocols()) { + if (Dump) { + llvm::dbgs() << "protocol " << proto->getName() << " {\n"; + } + + const auto &info = Protocols.getProtocolInfo(proto); + + for (auto *assocType : info.AssociatedTypes) + addAssociatedType(assocType, proto); + + for (auto *assocType : info.InheritedAssociatedTypes) + addAssociatedType(assocType, proto); + + for (auto req : info.Requirements) + addRequirement(req.getCanonical(), proto); + + if (Dump) { + llvm::dbgs() << "}\n"; + } + } + + // Add rewrite rules for all requirements in the top-level signature. + for (const auto &req : sig.getRequirements()) + addRequirement(req, /*proto=*/nullptr); +} + +/// For an associated type T in a protocol P, we add a rewrite rule: +/// +/// [P].T => [P:T] +/// +/// Intuitively, this means "if a type conforms to P, it has a nested type +/// named T". +void RewriteSystemBuilder::addAssociatedType(const AssociatedTypeDecl *type, + const ProtocolDecl *proto) { + MutableTerm lhs; + lhs.add(Symbol::forProtocol(proto, Context)); + lhs.add(Symbol::forName(type->getName(), Context)); + + MutableTerm rhs; + rhs.add(Symbol::forAssociatedType(proto, type->getName(), Context)); + + Rules.emplace_back(lhs, rhs); +} + +/// Lowers a generic requirement to a rewrite rule. +/// +/// If \p proto is null, this is a generic requirement from the top-level +/// generic signature. The added rewrite rule will be rooted in a generic +/// parameter symbol. +/// +/// If \p proto is non-null, this is a generic requirement in the protocol's +/// requirement signature. The added rewrite rule will be rooted in a +/// protocol symbol. +void RewriteSystemBuilder::addRequirement(const Requirement &req, + const ProtocolDecl *proto) { + if (Dump) { + llvm::dbgs() << "+ "; + req.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + + // Compute the left hand side. + auto subjectType = CanType(req.getFirstType()); + auto subjectTerm = Context.getMutableTermForType(subjectType, proto); + + // Compute the right hand side. + MutableTerm constraintTerm; + + switch (req.getKind()) { + case RequirementKind::Conformance: { + // A conformance requirement T : P becomes a rewrite rule + // + // T.[P] == T + // + // Intuitively, this means "any type ending with T conforms to P". + auto *proto = req.getProtocolDecl(); + + constraintTerm = subjectTerm; + constraintTerm.add(Symbol::forProtocol(proto, Context)); + break; + } + + case RequirementKind::Superclass: { + // A superclass requirement T : C becomes a rewrite rule + // + // T.[superclass: C] => T + // + // Together with a rewrite rule + // + // T.[layout: L] => T + // + // Where 'L' is either AnyObject or _NativeObject, depending on the + // ancestry of C. + auto otherType = CanType(req.getSecondType()); + + SmallVector substitutions; + otherType = getConcreteSubstitutionSchema(otherType, proto, + substitutions); + + constraintTerm = subjectTerm; + constraintTerm.add(Symbol::forSuperclass(otherType, substitutions, + Context)); + Rules.emplace_back(subjectTerm, constraintTerm); + + constraintTerm = subjectTerm; + auto layout = + LayoutConstraint::getLayoutConstraint( + otherType->getClassOrBoundGenericClass()->usesObjCObjectModel() + ? LayoutConstraintKind::Class + : LayoutConstraintKind::NativeClass, + Context.getASTContext()); + constraintTerm.add(Symbol::forLayout(layout, Context)); + break; + } + + case RequirementKind::Layout: { + // A layout requirement T : L becomes a rewrite rule + // + // T.[layout: L] == T + constraintTerm = subjectTerm; + constraintTerm.add(Symbol::forLayout(req.getLayoutConstraint(), + Context)); + break; + } + + case RequirementKind::SameType: { + auto otherType = CanType(req.getSecondType()); + + if (!otherType->isTypeParameter()) { + // A concrete same-type requirement T == C becomes a + // rewrite rule + // + // T.[concrete: C] => T + SmallVector substitutions; + otherType = getConcreteSubstitutionSchema(otherType, proto, + substitutions); + + constraintTerm = subjectTerm; + constraintTerm.add(Symbol::forConcreteType(otherType, substitutions, + Context)); + break; + } + + constraintTerm = Context.getMutableTermForType(otherType, proto); + break; + } + } + + Rules.emplace_back(subjectTerm, constraintTerm); +} + +void RequirementMachine::verify(const MutableTerm &term) const { +#ifndef NDEBUG + // If the term is in the generic parameter domain, ensure we have a valid + // generic parameter. + if (term.begin()->getKind() == Symbol::Kind::GenericParam) { + auto *genericParam = term.begin()->getGenericParam(); + auto genericParams = Sig.getGenericParams(); + auto found = std::find(genericParams.begin(), + genericParams.end(), + genericParam); + if (found == genericParams.end()) { + llvm::errs() << "Bad generic parameter in " << term << "\n"; + dump(llvm::errs()); + abort(); + } + } + + MutableTerm erased; + + // First, "erase" resolved associated types from the term, and try + // to simplify it again. + for (auto symbol : term) { + if (erased.empty()) { + switch (symbol.getKind()) { + case Symbol::Kind::Protocol: + case Symbol::Kind::GenericParam: + erased.add(symbol); + continue; + + case Symbol::Kind::AssociatedType: + erased.add(Symbol::forProtocol(symbol.getProtocols()[0], Context)); + break; + + case Symbol::Kind::Name: + case Symbol::Kind::Layout: + case Symbol::Kind::Superclass: + case Symbol::Kind::ConcreteType: + llvm::errs() << "Bad initial symbol in " << term << "\n"; + abort(); + break; + } + } + + switch (symbol.getKind()) { + case Symbol::Kind::Name: + assert(!erased.empty()); + erased.add(symbol); + break; + + case Symbol::Kind::AssociatedType: + erased.add(Symbol::forName(symbol.getName(), Context)); + break; + + case Symbol::Kind::Protocol: + case Symbol::Kind::GenericParam: + case Symbol::Kind::Layout: + case Symbol::Kind::Superclass: + case Symbol::Kind::ConcreteType: + llvm::errs() << "Bad interior symbol " << symbol << " in " << term << "\n"; + abort(); + break; + } + } + + MutableTerm simplified = erased; + System.simplify(simplified); + + // We should end up with the same term. + if (simplified != term) { + llvm::errs() << "Term verification failed\n"; + llvm::errs() << "Initial term: " << term << "\n"; + llvm::errs() << "Erased term: " << erased << "\n"; + llvm::errs() << "Simplified term: " << simplified << "\n"; + llvm::errs() << "\n"; + dump(llvm::errs()); + abort(); + } +#endif +} + +void RequirementMachine::dump(llvm::raw_ostream &out) const { + out << "Requirement machine for " << Sig << "\n"; + System.dump(out); + Map.dump(out); + + out << "\nConformance access paths:\n"; + for (auto pair : ConformanceAccessPaths) { + out << "- " << pair.first.first << " : "; + out << pair.first.second->getName() << " => "; + pair.second.print(out); + out << "\n"; + } +} + +RequirementMachine::RequirementMachine(RewriteContext &ctx) + : Context(ctx), System(ctx), Map(ctx, System.getProtocols()) { + auto &langOpts = ctx.getASTContext().LangOpts; + Dump = langOpts.DumpRequirementMachine; + RequirementMachineStepLimit = langOpts.RequirementMachineStepLimit; + RequirementMachineDepthLimit = langOpts.RequirementMachineDepthLimit; + Stats = ctx.getASTContext().Stats; +} + +RequirementMachine::~RequirementMachine() {} + +void RequirementMachine::addGenericSignature(CanGenericSignature sig) { + Sig = sig; + + PrettyStackTraceGenericSignature debugStack("building rewrite system for", sig); + + auto &ctx = Context.getASTContext(); + auto *Stats = ctx.Stats; + + if (Stats) + ++Stats->getFrontendCounters().NumRequirementMachines; + + FrontendStatsTracer tracer(Stats, "build-rewrite-system"); + + if (Dump) { + llvm::dbgs() << "Adding generic signature " << sig << " {\n"; + } + + + // Collect the top-level requirements, and all transtively-referenced + // protocol requirement signatures. + RewriteSystemBuilder builder(Context, Dump); + builder.addGenericSignature(sig); + + // Add the initial set of rewrite rules to the rewrite system, also + // providing the protocol graph to use for the linear order on terms. + System.initialize(std::move(builder.Rules), + std::move(builder.Protocols)); + + computeCompletion(); + + if (Dump) { + llvm::dbgs() << "}\n"; + } +} + +/// Attempt to obtain a confluent rewrite system using the completion +/// procedure. +void RequirementMachine::computeCompletion() { + while (true) { + // First, run the Knuth-Bendix algorithm to resolve overlapping rules. + auto result = System.computeConfluentCompletion( + RequirementMachineStepLimit, + RequirementMachineDepthLimit); + + if (Stats) { + Stats->getFrontendCounters() + .NumRequirementMachineCompletionSteps += result.second; + } + + // Check for failure. + auto checkCompletionResult = [&]() { + switch (result.first) { + case RewriteSystem::CompletionResult::Success: + break; + + case RewriteSystem::CompletionResult::MaxIterations: + llvm::errs() << "Generic signature " << Sig + << " exceeds maximum completion step count\n"; + System.dump(llvm::errs()); + abort(); + + case RewriteSystem::CompletionResult::MaxDepth: + llvm::errs() << "Generic signature " << Sig + << " exceeds maximum completion depth\n"; + System.dump(llvm::errs()); + abort(); + } + }; + + checkCompletionResult(); + + // Check invariants. + System.verify(); + + // Build the property map, which also performs concrete term + // unification; if this added any new rules, run the completion + // procedure again. + result = System.buildPropertyMap( + Map, + RequirementMachineStepLimit, + RequirementMachineDepthLimit); + + if (Stats) { + Stats->getFrontendCounters() + .NumRequirementMachineUnifiedConcreteTerms += result.second; + } + + checkCompletionResult(); + + // If buildPropertyMap() added new rules, we run another round of + // Knuth-Bendix, and build the property map again. + if (result.second == 0) + break; + } + + if (Dump) { + dump(llvm::dbgs()); + } + + assert(!Complete); + Complete = true; +} + +bool RequirementMachine::isComplete() const { + return Complete; +} diff --git a/lib/AST/RequirementMachine/RequirementMachine.h b/lib/AST/RequirementMachine/RequirementMachine.h new file mode 100644 index 0000000000000..2e0b869339587 --- /dev/null +++ b/lib/AST/RequirementMachine/RequirementMachine.h @@ -0,0 +1,118 @@ +//===--- RequirementMachine.h - Generics with term rewriting ----*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_REQUIREMENTMACHINE_H +#define SWIFT_REQUIREMENTMACHINE_H + +#include "swift/AST/GenericSignature.h" +#include "llvm/ADT/DenseMap.h" +#include + +#include "PropertyMap.h" +#include "ProtocolGraph.h" +#include "RewriteContext.h" +#include "RewriteSystem.h" + +namespace llvm { +class raw_ostream; +} + +namespace swift { + +class ASTContext; +class AssociatedTypeDecl; +class CanType; +class GenericTypeParamType; +class LayoutConstraint; +class ProtocolDecl; +class Requirement; +class Type; +class UnifiedStatsReporter; + +namespace rewriting { +class RewriteContext; + +/// Wraps a rewrite system with higher-level operations in terms of +/// generic signatures and interface types. +class RequirementMachine final { + friend class swift::ASTContext; + + CanGenericSignature Sig; + + RewriteContext &Context; + RewriteSystem System; + PropertyMap Map; + + bool Dump = false; + bool Complete = false; + unsigned RequirementMachineStepLimit; + unsigned RequirementMachineDepthLimit; + + UnifiedStatsReporter *Stats; + + /// All conformance access paths computed so far. + llvm::DenseMap, + ConformanceAccessPath> ConformanceAccessPaths; + + /// Conformance access paths computed during the last round. All elements + /// have the same length. If a conformance access path of greater length + /// is requested, we refill CurrentConformanceAccessPaths with all paths of + /// length N+1, and add them to the ConformanceAccessPaths map. + std::vector> + CurrentConformanceAccessPaths; + + explicit RequirementMachine(RewriteContext &rewriteCtx); + + RequirementMachine(const RequirementMachine &) = delete; + RequirementMachine(RequirementMachine &&) = delete; + RequirementMachine &operator=(const RequirementMachine &) = delete; + RequirementMachine &operator=(RequirementMachine &&) = delete; + + void addGenericSignature(CanGenericSignature sig); + + bool isComplete() const; + void computeCompletion(); + + MutableTerm getLongestValidPrefix(const MutableTerm &term) const; + +public: + ~RequirementMachine(); + + // Generic signature queries. Generally you shouldn't have to construct a + // RequirementMachine instance; instead, call the corresponding methods on + // GenericSignature, which lazily create a RequirementMachine for you. + GenericSignature::LocalRequirements getLocalRequirements(Type depType, + TypeArrayView genericParams) const; + bool requiresClass(Type depType) const; + LayoutConstraint getLayoutConstraint(Type depType) const; + bool requiresProtocol(Type depType, const ProtocolDecl *proto) const; + GenericSignature::RequiredProtocols getRequiredProtocols(Type depType) const; + Type getSuperclassBound(Type depType) const; + bool isConcreteType(Type depType) const; + Type getConcreteType(Type depType) const; + bool areSameTypeParameterInContext(Type depType1, Type depType2) const; + bool isCanonicalTypeInContext(Type type) const; + Type getCanonicalTypeInContext(Type type, + TypeArrayView genericParams) const; + ConformanceAccessPath getConformanceAccessPath(Type type, + ProtocolDecl *protocol); + TypeDecl *lookupNestedType(Type depType, Identifier name) const; + + void verify(const MutableTerm &term) const; + void dump(llvm::raw_ostream &out) const; +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif \ No newline at end of file diff --git a/lib/AST/RequirementMachine/RewriteContext.cpp b/lib/AST/RequirementMachine/RewriteContext.cpp new file mode 100644 index 0000000000000..3d66e32970cef --- /dev/null +++ b/lib/AST/RequirementMachine/RewriteContext.cpp @@ -0,0 +1,349 @@ +//===--- RewriteContext.cpp - Term rewriting allocation arena -------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Decl.h" +#include "swift/AST/Types.h" +#include "ProtocolGraph.h" +#include "RewriteSystem.h" +#include "RewriteContext.h" + +using namespace swift; +using namespace rewriting; + +/// Build a DebugOptions by parsing a comma-separated list of debug flags. +static DebugOptions parseDebugFlags(StringRef debugFlags) { + DebugOptions result; + + SmallVector debug; + debugFlags.split(debug, ','); + for (auto flagStr : debug) { + auto flag = llvm::StringSwitch>(flagStr) + .Case("simplify", DebugFlags::Simplify) + .Case("add", DebugFlags::Add) + .Case("merge", DebugFlags::Merge) + .Case("completion", DebugFlags::Completion) + .Case("concrete-unification", DebugFlags::ConcreteUnification) + .Case("concretize-nested-types", DebugFlags::ConcretizeNestedTypes) + .Default(None); + if (!flag) { + llvm::errs() << "Unknown debug flag in -debug-requirement-machine " + << flagStr << "\n"; + abort(); + } + + result |= *flag; + } + + return result; +} + +RewriteContext::RewriteContext(ASTContext &ctx) + : Context(ctx), + Stats(ctx.Stats), + SymbolHistogram(Symbol::NumKinds), + TermHistogram(4, /*Start=*/1), + RuleTrieHistogram(16, /*Start=*/1), + RuleTrieRootHistogram(16), + PropertyTrieHistogram(16, /*Start=*/1), + PropertyTrieRootHistogram(16) { + auto debugFlags = StringRef(ctx.LangOpts.DebugRequirementMachine); + if (!debugFlags.empty()) + Debug = parseDebugFlags(debugFlags); +} + +Term RewriteContext::getTermForType(CanType paramType, + const ProtocolDecl *proto) { + return Term::get(getMutableTermForType(paramType, proto), *this); +} + +/// Map an interface type to a term. +/// +/// If \p proto is null, this is a term relative to a generic +/// parameter in a top-level signature. The term is rooted in a generic +/// parameter symbol. +/// +/// If \p proto is non-null, this is a term relative to a protocol's +/// 'Self' type. The term is rooted in a protocol symbol for this protocol, +/// or an associated type symbol for some associated type in this protocol. +/// +/// Resolved DependentMemberTypes map to associated type symbols. +/// Unresolved DependentMemberTypes map to name symbols. +/// +/// Note the behavior of the root term is special if it is an associated +/// type symbol. The protocol of the associated type is always mapped to +/// \p proto if it was provided. This ensures we get the correct behavior +/// if a protocol places a constraint on an associated type inherited from +/// another protocol: +/// +/// protocol P { +/// associatedtype Foo +/// } +/// +/// protocol Q : P where Foo : R {} +/// +/// protocol R {} +/// +/// The DependentMemberType in the requirement signature of Q refers to +/// P::Foo. +/// +/// However, we want Q's requirement signature to introduce the rewrite rule +/// +/// [Q:Foo].[R] => [Q:Foo] +/// +/// and not +/// +/// [P:Foo].[R] => [P:Foo] +/// +/// This is because the rule only applies to Q's logical override of Foo, and +/// not P's Foo. +/// +/// To handle this, getMutableTermForType() behaves as follows: +/// +/// Self.P::Foo with proto = P => [P:Foo] +/// Self.P::Foo with proto = Q => [Q:Foo] +/// τ_0_0.P::Foo with proto == nullptr => τ_0_0.[P:Foo] +/// +MutableTerm RewriteContext::getMutableTermForType(CanType paramType, + const ProtocolDecl *proto) { + assert(paramType->isTypeParameter()); + + // Collect zero or more nested type names in reverse order. + bool innermostAssocTypeWasResolved = false; + + SmallVector symbols; + while (auto memberType = dyn_cast(paramType)) { + paramType = memberType.getBase(); + + if (auto *assocType = memberType->getAssocType()) { + const auto *thisProto = assocType->getProtocol(); + if (proto && isa(paramType)) { + thisProto = proto; + innermostAssocTypeWasResolved = true; + } + symbols.push_back(Symbol::forAssociatedType(thisProto, + assocType->getName(), + *this)); + } else { + symbols.push_back(Symbol::forName(memberType->getName(), *this)); + innermostAssocTypeWasResolved = false; + } + } + + // Add the root symbol at the end. + if (proto) { + assert(proto->getSelfInterfaceType()->isEqual(paramType)); + + // Self.Foo becomes [P].Foo + // Self.Q::Foo becomes [P:Foo] (not [Q:Foo] or [P].[Q:Foo]) + if (!innermostAssocTypeWasResolved) + symbols.push_back(Symbol::forProtocol(proto, *this)); + } else { + symbols.push_back(Symbol::forGenericParam( + cast(paramType), *this)); + } + + std::reverse(symbols.begin(), symbols.end()); + + return MutableTerm(symbols); +} + +/// Map an associated type symbol to an associated type declaration. +/// +/// Note that the protocol graph is not part of the caching key; each +/// protocol graph is a subgraph of the global inheritance graph, so +/// the specific choice of subgraph does not change the result. +AssociatedTypeDecl *RewriteContext::getAssociatedTypeForSymbol( + Symbol symbol, const ProtocolGraph &protos) { + auto found = AssocTypes.find(symbol); + if (found != AssocTypes.end()) + return found->second; + + assert(symbol.getKind() == Symbol::Kind::AssociatedType); + auto *proto = symbol.getProtocols()[0]; + auto name = symbol.getName(); + + AssociatedTypeDecl *assocType = nullptr; + + // Special case: handle unknown protocols, since they can appear in the + // invalid types that getCanonicalTypeInContext() must handle via + // concrete substitution; see the definition of getCanonicalTypeInContext() + // below for details. + if (!protos.isKnownProtocol(proto)) { + assert(symbol.getProtocols().size() == 1 && + "Unknown associated type symbol must have a single protocol"); + assocType = proto->getAssociatedType(name)->getAssociatedTypeAnchor(); + } else { + // An associated type symbol [P1&P1&...&Pn:A] has one or more protocols + // P0...Pn and an identifier 'A'. + // + // We map it back to a AssociatedTypeDecl as follows: + // + // - For each protocol Pn, look for associated types A in Pn itself, + // and all protocols that Pn refines. + // + // - For each candidate associated type An in protocol Qn where + // Pn refines Qn, get the associated type anchor An' defined in + // protocol Qn', where Qn refines Qn'. + // + // - Out of all the candidiate pairs (Qn', An'), pick the one where + // the protocol Qn' is the lowest element according to the linear + // order defined by TypeDecl::compare(). + // + // The associated type An' is then the canonical associated type + // representative of the associated type symbol [P0&...&Pn:A]. + // + for (auto *proto : symbol.getProtocols()) { + const auto &info = protos.getProtocolInfo(proto); + auto checkOtherAssocType = [&](AssociatedTypeDecl *otherAssocType) { + otherAssocType = otherAssocType->getAssociatedTypeAnchor(); + + if (otherAssocType->getName() == name && + (assocType == nullptr || + TypeDecl::compare(otherAssocType->getProtocol(), + assocType->getProtocol()) < 0)) { + assocType = otherAssocType; + } + }; + + for (auto *otherAssocType : info.AssociatedTypes) { + checkOtherAssocType(otherAssocType); + } + + for (auto *otherAssocType : info.InheritedAssociatedTypes) { + checkOtherAssocType(otherAssocType); + } + } + } + + assert(assocType && "Need to look harder"); + AssocTypes[symbol] = assocType; + return assocType; +} + +/// Compute the interface type for a range of symbols, with an optional +/// root type. +/// +/// If the root type is specified, we wrap it in a series of +/// DependentMemberTypes. Otherwise, the root is computed from +/// the first symbol of the range. +template +Type getTypeForSymbolRange(Iter begin, Iter end, Type root, + TypeArrayView genericParams, + const ProtocolGraph &protos, + const RewriteContext &ctx) { + Type result = root; + + auto handleRoot = [&](GenericTypeParamType *genericParam) { + assert(genericParam->isCanonical()); + + if (!genericParams.empty()) { + // Return a sugared GenericTypeParamType if we're given an array of + // sugared types to substitute. + unsigned index = GenericParamKey(genericParam).findIndexIn(genericParams); + result = genericParams[index]; + return; + } + + // Otherwise, we're going to return a canonical type. + result = genericParam; + }; + + for (; begin != end; ++begin) { + auto symbol = *begin; + + if (!result) { + // A valid term always begins with a generic parameter, protocol or + // associated type symbol. + switch (symbol.getKind()) { + case Symbol::Kind::GenericParam: + handleRoot(symbol.getGenericParam()); + continue; + + case Symbol::Kind::Protocol: + handleRoot(GenericTypeParamType::get(0, 0, ctx.getASTContext())); + continue; + + case Symbol::Kind::AssociatedType: + handleRoot(GenericTypeParamType::get(0, 0, ctx.getASTContext())); + + // An associated type term at the root means we have a dependent + // member type rooted at Self; handle the associated type below. + break; + + case Symbol::Kind::Name: + case Symbol::Kind::Layout: + case Symbol::Kind::Superclass: + case Symbol::Kind::ConcreteType: + llvm_unreachable("Term has invalid root symbol"); + } + } + + // An unresolved type can appear if we have invalid requirements. + if (symbol.getKind() == Symbol::Kind::Name) { + result = DependentMemberType::get(result, symbol.getName()); + continue; + } + + // We should have a resolved type at this point. + auto *assocType = + const_cast(ctx) + .getAssociatedTypeForSymbol(symbol, protos); + result = DependentMemberType::get(result, assocType); + } + + return result; +} + +Type RewriteContext::getTypeForTerm(Term term, + TypeArrayView genericParams, + const ProtocolGraph &protos) const { + return getTypeForSymbolRange(term.begin(), term.end(), Type(), + genericParams, protos, *this); +} + +Type RewriteContext::getTypeForTerm(const MutableTerm &term, + TypeArrayView genericParams, + const ProtocolGraph &protos) const { + return getTypeForSymbolRange(term.begin(), term.end(), Type(), + genericParams, protos, *this); +} + +Type RewriteContext::getRelativeTypeForTerm( + const MutableTerm &term, const MutableTerm &prefix, + const ProtocolGraph &protos) const { + assert(std::equal(prefix.begin(), prefix.end(), term.begin())); + + auto genericParam = CanGenericTypeParamType::get(0, 0, Context); + return getTypeForSymbolRange( + term.begin() + prefix.size(), term.end(), genericParam, + { }, protos, *this); +} + +/// We print stats in the destructor, which should get executed at the end of +/// a compilation job. +RewriteContext::~RewriteContext() { + if (Context.LangOpts.AnalyzeRequirementMachine) { + llvm::dbgs() << "--- Requirement Machine Statistics ---\n"; + llvm::dbgs() << "\n* Symbol kind:\n"; + SymbolHistogram.dump(llvm::dbgs(), Symbol::Kinds); + llvm::dbgs() << "\n* Term length:\n"; + TermHistogram.dump(llvm::dbgs()); + llvm::dbgs() << "\n* Rule trie fanout:\n"; + RuleTrieHistogram.dump(llvm::dbgs()); + llvm::dbgs() << "\n* Rule trie root fanout:\n"; + RuleTrieRootHistogram.dump(llvm::dbgs()); + llvm::dbgs() << "\n* Property trie fanout:\n"; + PropertyTrieHistogram.dump(llvm::dbgs()); + llvm::dbgs() << "\n* Property trie root fanout:\n"; + PropertyTrieRootHistogram.dump(llvm::dbgs()); + } +} diff --git a/lib/AST/RequirementMachine/RewriteContext.h b/lib/AST/RequirementMachine/RewriteContext.h new file mode 100644 index 0000000000000..c8b3a12384c32 --- /dev/null +++ b/lib/AST/RequirementMachine/RewriteContext.h @@ -0,0 +1,112 @@ +//===--- RewriteContext.h - Term rewriting allocation arena -----*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_REWRITECONTEXT_H +#define SWIFT_REWRITECONTEXT_H + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Types.h" +#include "swift/Basic/Statistic.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/Allocator.h" +#include "Debug.h" +#include "Histogram.h" +#include "Symbol.h" +#include "Term.h" + +namespace swift { + +namespace rewriting { + +/// A global object that can be shared by multiple rewrite systems. +/// +/// It stores uniqued symbols and terms. +/// +/// Out-of-line methods are documented in RewriteContext.cpp. +class RewriteContext final { + friend class Symbol; + friend class Term; + + /// Allocator for uniquing symbols and terms. + llvm::BumpPtrAllocator Allocator; + + /// Folding set for uniquing symbols. + llvm::FoldingSet Symbols; + + /// Folding set for uniquing terms. + llvm::FoldingSet Terms; + + /// Cache for associated type declarations. + llvm::DenseMap AssocTypes; + + /// Cache for merged associated type symbols. + llvm::DenseMap, Symbol> MergedAssocTypes; + + ASTContext &Context; + + DebugOptions Debug; + + RewriteContext(const RewriteContext &) = delete; + RewriteContext(RewriteContext &&) = delete; + RewriteContext &operator=(const RewriteContext &) = delete; + RewriteContext &operator=(RewriteContext &&) = delete; + +public: + /// Statistics. + UnifiedStatsReporter *Stats; + + /// Histograms. + Histogram SymbolHistogram; + Histogram TermHistogram; + Histogram RuleTrieHistogram; + Histogram RuleTrieRootHistogram; + Histogram PropertyTrieHistogram; + Histogram PropertyTrieRootHistogram; + + explicit RewriteContext(ASTContext &ctx); + + DebugOptions getDebugOptions() const { return Debug; } + + Term getTermForType(CanType paramType, const ProtocolDecl *proto); + + MutableTerm getMutableTermForType(CanType paramType, + const ProtocolDecl *proto); + + ASTContext &getASTContext() const { return Context; } + + Type getTypeForTerm(Term term, + TypeArrayView genericParams, + const ProtocolGraph &protos) const; + + Type getTypeForTerm(const MutableTerm &term, + TypeArrayView genericParams, + const ProtocolGraph &protos) const; + + Type getRelativeTypeForTerm( + const MutableTerm &term, const MutableTerm &prefix, + const ProtocolGraph &protos) const; + + AssociatedTypeDecl *getAssociatedTypeForSymbol(Symbol symbol, + const ProtocolGraph &protos); + + Symbol mergeAssociatedTypes(Symbol lhs, Symbol rhs, + const ProtocolGraph &protos); + + ~RewriteContext(); +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp new file mode 100644 index 0000000000000..98351f110fbba --- /dev/null +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -0,0 +1,305 @@ +//===--- RewriteSystem.cpp - Generics with term rewriting -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Decl.h" +#include "swift/AST/Types.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include "ProtocolGraph.h" +#include "RewriteContext.h" +#include "RewriteSystem.h" + +using namespace swift; +using namespace rewriting; + +RewriteSystem::RewriteSystem(RewriteContext &ctx) + : Context(ctx), Debug(ctx.getDebugOptions()) {} + +RewriteSystem::~RewriteSystem() { + Trie.updateHistograms(Context.RuleTrieHistogram, + Context.RuleTrieRootHistogram); +} + +void Rule::dump(llvm::raw_ostream &out) const { + out << LHS << " => " << RHS; + if (deleted) + out << " [deleted]"; +} + +void RewriteSystem::initialize( + std::vector> &&rules, + ProtocolGraph &&graph) { + Protos = graph; + + for (const auto &rule : rules) + addRule(rule.first, rule.second); +} + +Symbol RewriteSystem::simplifySubstitutionsInSuperclassOrConcreteSymbol( + Symbol symbol) const { + return symbol.transformConcreteSubstitutions( + [&](Term term) -> Term { + MutableTerm mutTerm(term); + if (!simplify(mutTerm)) + return term; + + return Term::get(mutTerm, Context); + }, Context); +} + +bool RewriteSystem::addRule(MutableTerm lhs, MutableTerm rhs) { + assert(!lhs.empty()); + assert(!rhs.empty()); + + if (Debug.contains(DebugFlags::Add)) { + llvm::dbgs() << "# Adding rule " << lhs << " == " << rhs << "\n"; + } + + // First, simplify terms appearing inside concrete substitutions before + // doing anything else. + if (lhs.back().isSuperclassOrConcreteType()) + lhs.back() = simplifySubstitutionsInSuperclassOrConcreteSymbol(lhs.back()); + else if (rhs.back().isSuperclassOrConcreteType()) + rhs.back() = simplifySubstitutionsInSuperclassOrConcreteSymbol(rhs.back()); + + // Now simplify both sides as much as possible with the rules we have so far. + // + // This avoids unnecessary work in the completion algorithm. + simplify(lhs); + simplify(rhs); + + // If the left hand side and right hand side are already equivalent, we're + // done. + int result = lhs.compare(rhs, Protos); + if (result == 0) + return false; + + // Orient the two terms so that the left hand side is greater than the + // right hand side. + if (result < 0) + std::swap(lhs, rhs); + + assert(lhs.compare(rhs, Protos) > 0); + + if (Debug.contains(DebugFlags::Add)) { + llvm::dbgs() << "## Simplified and oriented rule " << lhs << " => " << rhs << "\n\n"; + } + + unsigned i = Rules.size(); + Rules.emplace_back(Term::get(lhs, Context), Term::get(rhs, Context)); + auto oldRuleID = Trie.insert(lhs.begin(), lhs.end(), i); + if (oldRuleID) { + llvm::errs() << "Duplicate rewrite rule!\n"; + const auto &oldRule = Rules[*oldRuleID]; + llvm::errs() << "Old rule #" << *oldRuleID << ": "; + oldRule.dump(llvm::errs()); + llvm::errs() << "\nTrying to replay what happened when I simplified this term:\n"; + Debug |= DebugFlags::Simplify; + MutableTerm term = lhs; + simplify(lhs); + + abort(); + } + + // Check if we have a rule of the form + // + // X.[P1:T] => X.[P2:T] + // + // If so, record this rule for later. We'll try to merge the associated + // types in RewriteSystem::processMergedAssociatedTypes(). + if (lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end() - 1, rhs.begin()) && + lhs.back().getKind() == Symbol::Kind::AssociatedType && + rhs.back().getKind() == Symbol::Kind::AssociatedType && + lhs.back().getName() == rhs.back().getName()) { + if (Debug.contains(DebugFlags::Merge)) { + llvm::dbgs() << "## Associated type merge candidate "; + llvm::dbgs() << lhs << " => " << rhs << "\n\n"; + } + MergedAssociatedTypes.emplace_back(lhs, rhs); + } + + // Tell the caller that we added a new rule. + return true; +} + +/// Reduce a term by applying all rewrite rules until fixed point. +bool RewriteSystem::simplify(MutableTerm &term) const { + bool changed = false; + + if (Debug.contains(DebugFlags::Simplify)) { + llvm::dbgs() << "= Term " << term << "\n"; + } + + while (true) { + bool tryAgain = false; + + auto from = term.begin(); + auto end = term.end(); + while (from < end) { + auto ruleID = Trie.find(from, end); + if (ruleID) { + const auto &rule = Rules[*ruleID]; + if (!rule.isDeleted()) { + if (Debug.contains(DebugFlags::Simplify)) { + llvm::dbgs() << "== Rule #" << *ruleID << ": " << rule << "\n"; + } + + auto to = from + rule.getLHS().size(); + assert(std::equal(from, to, rule.getLHS().begin())); + + term.rewriteSubTerm(from, to, rule.getRHS()); + + if (Debug.contains(DebugFlags::Simplify)) { + llvm::dbgs() << "=== Result " << term << "\n"; + } + + changed = true; + tryAgain = true; + break; + } + } + + ++from; + } + + if (!tryAgain) + break; + } + + return changed; +} + +/// Delete any rules whose left hand sides can be reduced by other rules, +/// and reduce the right hand sides of all remaining rules as much as +/// possible. +/// +/// Must be run after the completion procedure, since the deletion of +/// rules is only valid to perform if the rewrite system is confluent. +void RewriteSystem::simplifyRewriteSystem() { + for (auto ruleID : indices(Rules)) { + auto &rule = Rules[ruleID]; + if (rule.isDeleted()) + continue; + + // First, see if the left hand side of this rule can be reduced using + // some other rule. + auto lhs = rule.getLHS(); + auto begin = lhs.begin(); + auto end = lhs.end(); + while (begin < end) { + if (auto otherRuleID = Trie.find(begin++, end)) { + // A rule does not obsolete itself. + if (*otherRuleID == ruleID) + continue; + + // Ignore other deleted rules. + if (Rules[*otherRuleID].isDeleted()) + continue; + + if (Debug.contains(DebugFlags::Completion)) { + const auto &otherRule = Rules[ruleID]; + llvm::dbgs() << "$ Deleting rule " << rule << " because " + << "its left hand side contains " << otherRule + << "\n"; + } + + rule.markDeleted(); + break; + } + } + + // If the rule was deleted above, skip the rest. + if (rule.isDeleted()) + continue; + + // Now, try to reduce the right hand side. + MutableTerm rhs(rule.getRHS()); + if (!simplify(rhs)) + continue; + + // If the right hand side was further reduced, update the rule. + rule = Rule(rule.getLHS(), Term::get(rhs, Context)); + } +} + +void RewriteSystem::verify() const { +#ifndef NDEBUG + +#define ASSERT_RULE(expr) \ + if (!(expr)) { \ + llvm::errs() << "&&& Malformed rewrite rule: " << rule << "\n"; \ + llvm::errs() << "&&& " << #expr << "\n\n"; \ + dump(llvm::errs()); \ + assert(expr); \ + } + + for (const auto &rule : Rules) { + if (rule.isDeleted()) + continue; + + const auto &lhs = rule.getLHS(); + const auto &rhs = rule.getRHS(); + + for (unsigned index : indices(lhs)) { + auto symbol = lhs[index]; + + if (index != lhs.size() - 1) { + ASSERT_RULE(symbol.getKind() != Symbol::Kind::Layout); + ASSERT_RULE(!symbol.isSuperclassOrConcreteType()); + } + + if (index != 0) { + ASSERT_RULE(symbol.getKind() != Symbol::Kind::GenericParam); + } + + if (index != 0 && index != lhs.size() - 1) { + ASSERT_RULE(symbol.getKind() != Symbol::Kind::Protocol); + } + } + + for (unsigned index : indices(rhs)) { + auto symbol = rhs[index]; + + // FIXME: This is only true if the input requirements were valid. + // On invalid code, we'll need to skip this assertion (and instead + // assert that we diagnosed an error!) + ASSERT_RULE(symbol.getKind() != Symbol::Kind::Name); + + ASSERT_RULE(symbol.getKind() != Symbol::Kind::Layout); + ASSERT_RULE(!symbol.isSuperclassOrConcreteType()); + + if (index != 0) { + ASSERT_RULE(symbol.getKind() != Symbol::Kind::GenericParam); + ASSERT_RULE(symbol.getKind() != Symbol::Kind::Protocol); + } + } + + auto lhsDomain = lhs.getRootProtocols(); + auto rhsDomain = rhs.getRootProtocols(); + + ASSERT_RULE(lhsDomain == rhsDomain); + } + +#undef ASSERT_RULE +#endif +} + +void RewriteSystem::dump(llvm::raw_ostream &out) const { + out << "Rewrite system: {\n"; + for (const auto &rule : Rules) { + out << "- " << rule << "\n"; + } + out << "}\n"; +} diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h new file mode 100644 index 0000000000000..8090bea376a67 --- /dev/null +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -0,0 +1,180 @@ +//===--- RewriteSystem.h - Generics with term rewriting ---------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_REWRITESYSTEM_H +#define SWIFT_REWRITESYSTEM_H + +#include "llvm/ADT/DenseSet.h" + +#include "Debug.h" +#include "ProtocolGraph.h" +#include "Symbol.h" +#include "Term.h" +#include "Trie.h" + +namespace llvm { + class raw_ostream; +} + +namespace swift { + +namespace rewriting { + +class PropertyMap; +class RewriteContext; + +/// A rewrite rule that replaces occurrences of LHS with RHS. +/// +/// LHS must be greater than RHS in the linear order over terms. +/// +/// Out-of-line methods are documented in RewriteSystem.cpp. +class Rule final { + Term LHS; + Term RHS; + bool deleted; + +public: + Rule(Term lhs, Term rhs) + : LHS(lhs), RHS(rhs), deleted(false) {} + + const Term &getLHS() const { return LHS; } + const Term &getRHS() const { return RHS; } + + /// Returns if the rule was deleted. + bool isDeleted() const { + return deleted; + } + + /// Deletes the rule, which removes it from consideration in term + /// simplification and completion. Deleted rules are simply marked as + /// such instead of being physically removed from the rules vector + /// in the rewrite system, to ensure that indices remain valid across + /// deletion. + void markDeleted() { + assert(!deleted); + deleted = true; + } + + /// Returns the length of the left hand side. + unsigned getDepth() const { + return LHS.size(); + } + + void dump(llvm::raw_ostream &out) const; + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &out, + const Rule &rule) { + rule.dump(out); + return out; + } +}; + +/// A term rewrite system for working with types in a generic signature. +/// +/// Out-of-line methods are documented in RewriteSystem.cpp. +class RewriteSystem final { + /// Rewrite context for memory allocation. + RewriteContext &Context; + + /// The rules added so far, including rules from our client, as well + /// as rules introduced by the completion procedure. + std::vector Rules; + + /// A prefix trie of rule left hand sides to optimize lookup. The value + /// type is an index into the Rules array defined above. + Trie Trie; + + /// The graph of all protocols transitively referenced via our set of + /// rewrite rules, used for the linear order on symbols. + ProtocolGraph Protos; + + /// A list of pending terms for the associated type merging completion + /// heuristic. + /// + /// The pair (lhs, rhs) satisfies the following conditions: + /// - lhs > rhs + /// - all symbols but the last are pair-wise equal in lhs and rhs + /// - the last symbol in both lhs and rhs is an associated type symbol + /// - the last symbol in both lhs and rhs has the same name + /// + /// See RewriteSystem::processMergedAssociatedTypes() for details. + std::vector> MergedAssociatedTypes; + + /// Pairs of rules which have already been checked for overlap. + llvm::DenseSet> CheckedOverlaps; + + DebugOptions Debug; + +public: + explicit RewriteSystem(RewriteContext &ctx); + ~RewriteSystem(); + + RewriteSystem(const RewriteSystem &) = delete; + RewriteSystem(RewriteSystem &&) = delete; + RewriteSystem &operator=(const RewriteSystem &) = delete; + RewriteSystem &operator=(RewriteSystem &&) = delete; + + /// Return the rewrite context used for allocating memory. + RewriteContext &getRewriteContext() const { return Context; } + + /// Return the object recording information about known protocols. + const ProtocolGraph &getProtocols() const { return Protos; } + + void initialize(std::vector> &&rules, + ProtocolGraph &&protos); + + Symbol simplifySubstitutionsInSuperclassOrConcreteSymbol(Symbol symbol) const; + + bool addRule(MutableTerm lhs, MutableTerm rhs); + + bool simplify(MutableTerm &term) const; + + enum class CompletionResult { + /// Confluent completion was computed successfully. + Success, + + /// Maximum number of iterations reached. + MaxIterations, + + /// Completion produced a rewrite rule whose left hand side has a length + /// exceeding the limit. + MaxDepth + }; + + std::pair + computeConfluentCompletion(unsigned maxIterations, + unsigned maxDepth); + + void simplifyRewriteSystem(); + + void verify() const; + + std::pair + buildPropertyMap(PropertyMap &map, + unsigned maxIterations, + unsigned maxDepth); + + void dump(llvm::raw_ostream &out) const; + +private: + std::pair + computeCriticalPair(ArrayRef::const_iterator from, + const Rule &lhs, const Rule &rhs) const; + + void processMergedAssociatedTypes(); +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif diff --git a/lib/AST/RequirementMachine/RewriteSystemCompletion.cpp b/lib/AST/RequirementMachine/RewriteSystemCompletion.cpp new file mode 100644 index 0000000000000..b0520cf91cd19 --- /dev/null +++ b/lib/AST/RequirementMachine/RewriteSystemCompletion.cpp @@ -0,0 +1,493 @@ +//===--- RewriteSystemCompletion.cpp - Confluent completion procedure -----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 implements completion in the rewriting system sense, not code +// completion. +// +// We use a variation of the Knuth-Bendix algorithm +/// (https://en.wikipedia.org/wiki/Knuth–Bendix_completion_algorithm). +// +// The goal is to find 'overlapping' rules which would allow the same term to +// be rewritten in two different ways. These two different irreducible +// reductions are called a 'critical pair'; the completion procedure introduces +// new rewrite rules to eliminate critical pairs by rewriting one side of the +// pair to the other. This can introduce more overlaps with existing rules, and +// the process iterates until fixed point. +// +//===----------------------------------------------------------------------===// + +#include "swift/Basic/Defer.h" +#include "swift/Basic/Range.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +#include "RewriteContext.h" +#include "RewriteSystem.h" + +using namespace swift; +using namespace rewriting; + +/// For a superclass or concrete type symbol +/// +/// [concrete: Foo] +/// [superclass: Foo] +/// +/// Return a new symbol where the prefix T is prepended to each of the +/// substitutions: +/// +/// [concrete: Foo] +/// [superclass: Foo] +/// +/// Asserts if this is not a superclass or concrete type symbol. +Symbol Symbol::prependPrefixToConcreteSubstitutions( + const MutableTerm &prefix, + RewriteContext &ctx) const { + if (prefix.empty()) + return *this; + + return transformConcreteSubstitutions( + [&](Term term) -> Term { + MutableTerm mutTerm; + mutTerm.append(prefix); + mutTerm.append(term); + + return Term::get(mutTerm, ctx); + }, ctx); +} + +/// If we have two symbols [P:T] and [Q:T], produce a merged symbol: +/// +/// - If P inherits from Q, this is just [P:T]. +/// - If Q inherits from P, this is just [Q:T]. +/// - If P and Q are unrelated, this is [P&Q:T]. +/// +/// Note that the protocol graph is not part of the caching key; each +/// protocol graph is a subgraph of the global inheritance graph, so +/// the specific choice of subgraph does not change the result. +Symbol RewriteContext::mergeAssociatedTypes(Symbol lhs, Symbol rhs, + const ProtocolGraph &graph) { + auto key = std::make_pair(lhs, rhs); + + auto found = MergedAssocTypes.find(key); + if (found != MergedAssocTypes.end()) + return found->second; + + // Check preconditions that were established by RewriteSystem::addRule(). + assert(lhs.getKind() == Symbol::Kind::AssociatedType); + assert(rhs.getKind() == Symbol::Kind::AssociatedType); + assert(lhs.getName() == rhs.getName()); + assert(lhs.compare(rhs, graph) > 0); + + auto protos = lhs.getProtocols(); + auto otherProtos = rhs.getProtocols(); + + // This must follow from lhs > rhs. + assert(protos.size() <= otherProtos.size()); + + // Compute sorted and merged list of protocols, with duplicates. + llvm::TinyPtrVector newProtos; + std::merge(protos.begin(), protos.end(), + otherProtos.begin(), otherProtos.end(), + std::back_inserter(newProtos), + [&](const ProtocolDecl *lhs, + const ProtocolDecl *rhs) -> int { + return graph.compareProtocols(lhs, rhs) < 0; + }); + + // Prune duplicates and protocols that are inherited by other + // protocols. + llvm::TinyPtrVector minimalProtos; + for (const auto *newProto : newProtos) { + auto inheritsFrom = [&](const ProtocolDecl *thisProto) { + return (thisProto == newProto || + graph.inheritsFrom(thisProto, newProto)); + }; + + if (std::find_if(minimalProtos.begin(), minimalProtos.end(), + inheritsFrom) + == minimalProtos.end()) { + minimalProtos.push_back(newProto); + } + } + + // The two input sets are minimal already, so the merged set + // should have at least as many elements as the smallest + // input set. + assert(minimalProtos.size() >= std::min(protos.size(), otherProtos.size())); + + // The merged set cannot contain more elements than the union + // of the two sets. + assert(minimalProtos.size() <= protos.size() + otherProtos.size()); + + auto result = Symbol::forAssociatedType(minimalProtos, lhs.getName(), *this); + auto inserted = MergedAssocTypes.insert(std::make_pair(key, result)); + assert(inserted.second); + (void) inserted; + + return result; +} + +/// Consider the following example: +/// +/// protocol P1 { associatedtype T : P1 } +/// protocol P2 { associatedtype T : P2 } +/// struct G {} +/// +/// We start with these rewrite rules: +/// +/// [P1].T => [P1:T] +/// [P2].T => [P2:T] +/// [P1:T].[P1] => [P1:T] +/// [P2:T].[P1] => [P2:T] +/// τ_0_0.[P1] => τ_0_0 +/// τ_0_0.[P2] => τ_0_0 +/// τ_0_0.T => τ_0_0.[P1:T] +/// τ_0_0.[P2:T] => τ_0_0.[P1:T] +/// +/// The completion procedure ends up adding an infinite series of rules of the +/// form +/// +/// τ_0_0.[P1:T].[P2] => τ_0_0.[P1:T] +/// τ_0_0.[P1:T].[P2:T] => τ_0_0.[P1:T].[P1:T] +/// +/// τ_0_0.[P1:T].[P1:T].[P2] => τ_0_0.[P1:T].[P1:T] +/// τ_0_0.[P1:T].[P1:T].[P2:T] => τ_0_0.[P1:T].[P1:T].[P1:T] +/// +/// τ_0_0.[P1:T].[P1:T].[P1:T].[P2] => τ_0_0.[P1:T].[P1:T].[P1.T] +/// τ_0_0.[P1:T].[P1:T].[P1:T].[P2:T] => τ_0_0.[P1:T].[P1:T].[P1:T].[P1.T] +/// +/// The difficulty here stems from the fact that an arbitrary sequence of +/// [P1:T] following a τ_0_0 is known to conform to P2, but P1:T itself +/// does not conform to P2. +/// +/// We use a heuristic to compute a completion in this case by using +/// merged associated type terms. +/// +/// The key is the following rewrite rule: +/// +/// τ_0_0.[P2:T] => τ_0_0.[P1:T] +/// +/// When we add this rule, we introduce a new merged symbol [P1&P2:T] and +/// a new rule: +/// +/// τ_0_0.[P1:T] => τ_0_0.[P1&P2:T] +/// +/// We also look for any existing rules of the form [P1:T].[Q] => [P1:T] +/// or [P2:T].[Q] => [P2:T], and introduce a new rule: +/// +/// [P1&P2:T].[Q] => [P1&P2:T] +/// +/// In the above example, we have such a rule for Q == P1 and Q == P2, so +/// in total we end up adding the following four rules: +/// +/// τ_0_0.[P1:T] => τ_0_0.[P1&P2:T] +/// [P1&P2:T].[P1] => [P1&P2:T] +/// [P1&P2:T].[P2] => [P1&P2:T] +/// +/// Intuitively, since the conformance requirements on the merged term +/// are not prefixed by the root τ_0_0, they apply at any level; we've +/// "tied off" the recursion, and the rewrite system is now convergent. +void RewriteSystem::processMergedAssociatedTypes() { + if (MergedAssociatedTypes.empty()) + return; + + unsigned i = 0; + + // Chase the end of the vector; calls to RewriteSystem::addRule() + // can theoretically add new elements below. + while (i < MergedAssociatedTypes.size()) { + auto pair = MergedAssociatedTypes[i++]; + const auto &lhs = pair.first; + const auto &rhs = pair.second; + + // If we have X.[P2:T] => Y.[P1:T], add a new pair of rules: + // X.[P1:T] => X.[P1&P2:T] + // X.[P2:T] => X.[P1&P2:T] + if (Debug.contains(DebugFlags::Merge)) { + llvm::dbgs() << "## Processing associated type merge candidate "; + llvm::dbgs() << lhs << " => " << rhs << "\n"; + } + + auto mergedSymbol = Context.mergeAssociatedTypes(lhs.back(), rhs.back(), + Protos); + if (Debug.contains(DebugFlags::Merge)) { + llvm::dbgs() << "### Merged symbol " << mergedSymbol << "\n"; + } + + // We must have mergedSymbol <= rhs < lhs, therefore mergedSymbol != lhs. + assert(lhs.back() != mergedSymbol && + "Left hand side should not already end with merged symbol?"); + assert(mergedSymbol.compare(rhs.back(), Protos) <= 0); + assert(rhs.back().compare(lhs.back(), Protos) < 0); + + // If the merge didn't actually produce a new symbol, there is nothing else + // to do. + if (rhs.back() == mergedSymbol) { + if (Debug.contains(DebugFlags::Merge)) { + llvm::dbgs() << "### Skipping\n"; + } + + continue; + } + + // Build the term X.[P1&P2:T]. + MutableTerm mergedTerm = lhs; + mergedTerm.back() = mergedSymbol; + + // Add the rule X.[P1:T] => X.[P1&P2:T]. + addRule(rhs, mergedTerm); + + // Collect new rules here so that we're not adding rules while traversing + // the trie. + SmallVector, 2> inducedRules; + + // Look for conformance requirements on [P1:T] and [P2:T]. + auto visitRule = [&](unsigned ruleID) { + const auto &otherRule = Rules[ruleID]; + const auto &otherLHS = otherRule.getLHS(); + if (otherLHS.size() == 2 && + otherLHS[1].getKind() == Symbol::Kind::Protocol) { + if (otherLHS[0] == lhs.back() || + otherLHS[0] == rhs.back()) { + // We have a rule of the form + // + // [P1:T].[Q] => [P1:T] + // + // or + // + // [P2:T].[Q] => [P2:T] + if (Debug.contains(DebugFlags::Merge)) { + llvm::dbgs() << "### Lifting conformance rule " << otherRule << "\n"; + } + + // We know that [P1:T] or [P2:T] conforms to Q, therefore the + // merged type [P1&P2:T] must conform to Q as well. Add a new rule + // of the form: + // + // [P1&P2:T].[Q] => [P1&P2:T] + // + MutableTerm newLHS; + newLHS.add(mergedSymbol); + newLHS.add(otherLHS[1]); + + MutableTerm newRHS; + newRHS.add(mergedSymbol); + + inducedRules.emplace_back(newLHS, newRHS); + } + } + }; + + // Visit rhs first to preserve the ordering of protocol requirements in the + // the property map. This is just for aesthetic purposes in the debug dump, + // it doesn't change behavior. + Trie.findAll(rhs.back(), visitRule); + Trie.findAll(lhs.back(), visitRule); + + // Now add the new rules. + for (const auto &pair : inducedRules) + addRule(pair.first, pair.second); + } + + MergedAssociatedTypes.clear(); +} + +/// Compute a critical pair from the left hand sides of two rewrite rules, +/// where \p rhs begins at \p from, which must be an iterator pointing +/// into \p lhs. +/// +/// There are two cases: +/// +/// 1) lhs == TUV -> X, rhs == U -> Y. The overlapped term is TUV; +/// applying lhs and rhs, respectively, yields the critical pair +/// (X, TYV). +/// +/// 2) lhs == TU -> X, rhs == UV -> Y. The overlapped term is once +/// again TUV; applying lhs and rhs, respectively, yields the +/// critical pair (XV, TY). +/// +/// If lhs and rhs have identical left hand sides, either case could +/// apply, but we arbitrarily pick case 1. +/// +/// There is also an additional wrinkle. If we're in case 2, and the +/// last symbol of V is a superclass or concrete type symbol A, we prepend +/// T to each substitution of A. +/// +/// For example, suppose we have the following two rules: +/// +/// A.B -> C +/// B.[concrete: Foo] -> B +/// +/// The overlapped term is A.B.[concrete: Foo], so the critical pair +/// is (C.[concrete: Foo], A.B). We prepended 'A' to the +/// concrete substitution 'X' to get 'A.X'; the new concrete term +/// is now rooted at the same level as A.B in the rewrite system, +/// not just B. +std::pair +RewriteSystem::computeCriticalPair(ArrayRef::const_iterator from, + const Rule &lhs, const Rule &rhs) const { + auto end = lhs.getLHS().end(); + if (from + rhs.getLHS().size() < end) { + // lhs == TUV -> X, rhs == U -> Y. + + // Note: This includes the case where the two rules have exactly + // equal left hand sides; that is, lhs == U -> X, rhs == U -> Y. + // + // In this case, T and V are both empty. + + // Compute the term TYV. + MutableTerm t(lhs.getLHS().begin(), from); + t.append(rhs.getRHS()); + t.append(from + rhs.getLHS().size(), lhs.getLHS().end()); + return std::make_pair(MutableTerm(lhs.getRHS()), t); + } else { + // lhs == TU -> X, rhs == UV -> Y. + + // Compute the term T. + MutableTerm t(lhs.getLHS().begin(), from); + + // Compute the term XV. + MutableTerm xv(lhs.getRHS()); + xv.append(rhs.getLHS().begin() + (lhs.getLHS().end() - from), + rhs.getLHS().end()); + + if (xv.back().isSuperclassOrConcreteType()) { + xv.back() = xv.back().prependPrefixToConcreteSubstitutions( + t, Context); + } + + // Compute the term TY. + t.append(rhs.getRHS()); + + return std::make_pair(xv, t); + } +} + +/// Computes the confluent completion using the Knuth-Bendix algorithm. +/// +/// Returns a pair consisting of a status and number of iterations executed. +/// +/// The status is CompletionResult::MaxIterations if we exceed \p maxIterations +/// iterations. +/// +/// The status is CompletionResult::MaxDepth if we produce a rewrite rule whose +/// left hand side has a length exceeding \p maxDepth. +/// +/// Otherwise, the status is CompletionResult::Success. +std::pair +RewriteSystem::computeConfluentCompletion(unsigned maxIterations, + unsigned maxDepth) { + unsigned steps = 0; + + bool again = false; + + do { + std::vector> resolvedCriticalPairs; + + // For every rule, looking for other rules that overlap with this rule. + for (unsigned i = 0, e = Rules.size(); i < e; ++i) { + const auto &lhs = Rules[i]; + if (lhs.isDeleted()) + continue; + + // Look up every suffix of this rule in the trie using findAll(). This + // will find both kinds of overlap: + // + // 1) rules whose left hand side is fully contained in [from,to) + // 2) rules whose left hand side has a prefix equal to [from,to) + auto from = lhs.getLHS().begin(); + auto to = lhs.getLHS().end(); + while (from < to) { + Trie.findAll(from, to, [&](unsigned j) { + // We don't have to consider the same pair of rules more than once, + // since those critical pairs were already resolved. + if (!CheckedOverlaps.insert(std::make_pair(i, j)).second) + return; + + const auto &rhs = Rules[j]; + if (rhs.isDeleted()) + return; + + if (from == lhs.getLHS().begin()) { + // While every rule will have an overlap of the first kind + // with itself, it's not useful to consider since the + // resulting trivial pair is always trivial. + if (i == j) + return; + + // If the first rule's left hand side is a proper prefix + // of the second rule's left hand side, don't do anything. + // + // We will find the 'opposite' overlap later, where the two + // rules are swapped around. Then it becomes an overlap of + // the first kind, and will be handled as such. + if (rhs.getLHS().size() > lhs.getLHS().size()) + return; + } + + // Try to repair the confluence violation by adding a new rule. + resolvedCriticalPairs.push_back(computeCriticalPair(from, lhs, rhs)); + + if (Debug.contains(DebugFlags::Completion)) { + const auto &pair = resolvedCriticalPairs.back(); + + llvm::dbgs() << "$ Overlapping rules: (#" << i << ") "; + llvm::dbgs() << lhs << "\n"; + llvm::dbgs() << " -vs- (#" << j << ") "; + llvm::dbgs() << rhs << ":\n"; + llvm::dbgs() << "$$ First term of critical pair is " + << pair.first << "\n"; + llvm::dbgs() << "$$ Second term of critical pair is " + << pair.second << "\n\n"; + } + }); + + ++from; + } + } + + simplifyRewriteSystem(); + + again = false; + for (const auto &pair : resolvedCriticalPairs) { + // Check if we've already done too much work. + if (Rules.size() > maxIterations) + return std::make_pair(CompletionResult::MaxIterations, steps); + + if (!addRule(pair.first, pair.second)) + continue; + + // Check if the new rule is too long. + if (Rules.back().getDepth() > maxDepth) + return std::make_pair(CompletionResult::MaxDepth, steps); + + // Only count a 'step' once we add a new rule. + ++steps; + again = true; + } + + // If the added rules merged any associated types, process the merges now + // before we continue with the completion procedure. This is important + // to perform incrementally since merging is required to repair confluence + // violations. + processMergedAssociatedTypes(); + } while (again); + + assert(MergedAssociatedTypes.empty() && + "Should have processed all merge candidates"); + + return std::make_pair(CompletionResult::Success, steps); +} diff --git a/lib/AST/RequirementMachine/Symbol.cpp b/lib/AST/RequirementMachine/Symbol.cpp new file mode 100644 index 0000000000000..ecf189268e085 --- /dev/null +++ b/lib/AST/RequirementMachine/Symbol.cpp @@ -0,0 +1,715 @@ +//===--- Symbol.cpp - The generics rewrite system alphabet ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Decl.h" +#include "swift/AST/Types.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include "ProtocolGraph.h" +#include "RewriteContext.h" +#include "Symbol.h" +#include "Term.h" + +using namespace swift; +using namespace rewriting; + +const StringRef Symbol::Kinds[] = { + "protocol", + "assocty", + "generic", + "name", + "layout", + "super", + "concrete" +}; + +/// Symbols are uniqued and immutable, stored as a single pointer; +/// the Storage type is the allocated backing storage. +struct Symbol::Storage final + : public llvm::FoldingSetNode, + public llvm::TrailingObjects { + friend class Symbol; + + unsigned Kind : 3; + unsigned NumProtocols : 15; + unsigned NumSubstitutions : 14; + + union { + Identifier Name; + CanType ConcreteType; + LayoutConstraint Layout; + const ProtocolDecl *Proto; + GenericTypeParamType *GenericParam; + }; + + explicit Storage(Identifier name) { + Kind = unsigned(Symbol::Kind::Name); + NumProtocols = 0; + NumSubstitutions = 0; + Name = name; + } + + explicit Storage(LayoutConstraint layout) { + Kind = unsigned(Symbol::Kind::Layout); + NumProtocols = 0; + NumSubstitutions = 0; + Layout = layout; + } + + explicit Storage(const ProtocolDecl *proto) { + Kind = unsigned(Symbol::Kind::Protocol); + NumProtocols = 0; + NumSubstitutions = 0; + Proto = proto; + } + + explicit Storage(GenericTypeParamType *param) { + Kind = unsigned(Symbol::Kind::GenericParam); + NumProtocols = 0; + NumSubstitutions = 0; + GenericParam = param; + } + + Storage(ArrayRef protos, Identifier name) { + assert(!protos.empty()); + + Kind = unsigned(Symbol::Kind::AssociatedType); + NumProtocols = protos.size(); + assert(NumProtocols == protos.size() && "Overflow"); + NumSubstitutions = 0; + Name = name; + + for (unsigned i : indices(protos)) + getProtocols()[i] = protos[i]; + } + + Storage(Symbol::Kind kind, CanType type, ArrayRef substitutions) { + assert(kind == Symbol::Kind::Superclass || + kind == Symbol::Kind::ConcreteType); + assert(!type->hasTypeVariable()); + assert(type->hasTypeParameter() != substitutions.empty()); + + Kind = unsigned(kind); + NumProtocols = 0; + NumSubstitutions = substitutions.size(); + ConcreteType = type; + + for (unsigned i : indices(substitutions)) + getSubstitutions()[i] = substitutions[i]; + } + + size_t numTrailingObjects(OverloadToken) const { + return NumProtocols; + } + + size_t numTrailingObjects(OverloadToken) const { + return NumSubstitutions; + } + + MutableArrayRef getProtocols() { + return {getTrailingObjects(), NumProtocols}; + } + + ArrayRef getProtocols() const { + return {getTrailingObjects(), NumProtocols}; + } + + MutableArrayRef getSubstitutions() { + return {getTrailingObjects(), NumSubstitutions}; + } + + ArrayRef getSubstitutions() const { + return {getTrailingObjects(), NumSubstitutions}; + } + + void Profile(llvm::FoldingSetNodeID &id) const; +}; + +Symbol::Kind Symbol::getKind() const { + return Kind(Ptr->Kind); +} + +/// Get the identifier associated with an unbound name symbol or an +/// associated type symbol. +Identifier Symbol::getName() const { + assert(getKind() == Kind::Name || + getKind() == Kind::AssociatedType); + return Ptr->Name; +} + +/// Get the single protocol declaration associated with a protocol symbol. +const ProtocolDecl *Symbol::getProtocol() const { + assert(getKind() == Kind::Protocol); + return Ptr->Proto; +} + +/// Get the list of protocols associated with a protocol or associated type +/// symbol. Note that if this is a protocol symbol, the return value will have +/// exactly one element. +ArrayRef Symbol::getProtocols() const { + auto protos = Ptr->getProtocols(); + if (protos.empty()) { + assert(getKind() == Kind::Protocol); + return {&Ptr->Proto, 1}; + } + assert(getKind() == Kind::AssociatedType); + return protos; +} + +/// Get the generic parameter associated with a generic parameter symbol. +GenericTypeParamType *Symbol::getGenericParam() const { + assert(getKind() == Kind::GenericParam); + return Ptr->GenericParam; +} + +/// Get the layout constraint associated with a layout constraint symbol. +LayoutConstraint Symbol::getLayoutConstraint() const { + assert(getKind() == Kind::Layout); + return Ptr->Layout; +} + +/// Get the superclass type associated with a superclass symbol. +CanType Symbol::getSuperclass() const { + assert(getKind() == Kind::Superclass); + return Ptr->ConcreteType; +} + +/// Get the concrete type associated with a concrete type symbol. +CanType Symbol::getConcreteType() const { + assert(getKind() == Kind::ConcreteType); + return Ptr->ConcreteType; +} + +ArrayRef Symbol::getSubstitutions() const { + assert(getKind() == Kind::Superclass || + getKind() == Kind::ConcreteType); + return Ptr->getSubstitutions(); +} + +/// Creates a new name symbol. +Symbol Symbol::forName(Identifier name, + RewriteContext &ctx) { + llvm::FoldingSetNodeID id; + id.AddInteger(unsigned(Kind::Name)); + id.AddPointer(name.get()); + + void *insertPos = nullptr; + if (auto *symbol = ctx.Symbols.FindNodeOrInsertPos(id, insertPos)) + return symbol; + + unsigned size = Storage::totalSizeToAlloc(0, 0); + void *mem = ctx.Allocator.Allocate(size, alignof(Storage)); + auto *symbol = new (mem) Storage(name); + +#ifndef NDEBUG + llvm::FoldingSetNodeID newID; + symbol->Profile(newID); + assert(id == newID); +#endif + + ctx.Symbols.InsertNode(symbol, insertPos); + ctx.SymbolHistogram.add(unsigned(Kind::Name)); + + return symbol; +} + +/// Creates a new protocol symbol. +Symbol Symbol::forProtocol(const ProtocolDecl *proto, + RewriteContext &ctx) { + assert(proto != nullptr); + + llvm::FoldingSetNodeID id; + id.AddInteger(unsigned(Kind::Protocol)); + id.AddPointer(proto); + + void *insertPos = nullptr; + if (auto *symbol = ctx.Symbols.FindNodeOrInsertPos(id, insertPos)) + return symbol; + + unsigned size = Storage::totalSizeToAlloc(0, 0); + void *mem = ctx.Allocator.Allocate(size, alignof(Storage)); + auto *symbol = new (mem) Storage(proto); + +#ifndef NDEBUG + llvm::FoldingSetNodeID newID; + symbol->Profile(newID); + assert(id == newID); +#endif + + ctx.Symbols.InsertNode(symbol, insertPos); + ctx.SymbolHistogram.add(unsigned(Kind::Protocol)); + + return symbol; +} + +/// Creates a new associated type symbol for a single protocol. +Symbol Symbol::forAssociatedType(const ProtocolDecl *proto, + Identifier name, + RewriteContext &ctx) { + SmallVector protos; + protos.push_back(proto); + + return forAssociatedType(protos, name, ctx); +} + +/// Creates a merged associated type symbol to represent a nested +/// type that conforms to multiple protocols, all of which have +/// an associated type with the same name. +Symbol Symbol::forAssociatedType(ArrayRef protos, + Identifier name, + RewriteContext &ctx) { + llvm::FoldingSetNodeID id; + id.AddInteger(unsigned(Kind::AssociatedType)); + id.AddInteger(protos.size()); + for (const auto *proto : protos) + id.AddPointer(proto); + id.AddPointer(name.get()); + + void *insertPos = nullptr; + if (auto *symbol = ctx.Symbols.FindNodeOrInsertPos(id, insertPos)) + return symbol; + + unsigned size = Storage::totalSizeToAlloc( + protos.size(), 0); + void *mem = ctx.Allocator.Allocate(size, alignof(Storage)); + auto *symbol = new (mem) Storage(protos, name); + +#ifndef NDEBUG + llvm::FoldingSetNodeID newID; + symbol->Profile(newID); + assert(id == newID); +#endif + + ctx.Symbols.InsertNode(symbol, insertPos); + ctx.SymbolHistogram.add(unsigned(Kind::AssociatedType)); + + return symbol; +} + +/// Creates a generic parameter symbol, representing a generic +/// parameter in the top-level generic signature from which the +/// rewrite system is built. +Symbol Symbol::forGenericParam(GenericTypeParamType *param, + RewriteContext &ctx) { + assert(param->isCanonical()); + + llvm::FoldingSetNodeID id; + id.AddInteger(unsigned(Kind::GenericParam)); + id.AddPointer(param); + + void *insertPos = nullptr; + if (auto *symbol = ctx.Symbols.FindNodeOrInsertPos(id, insertPos)) + return symbol; + + unsigned size = Storage::totalSizeToAlloc(0, 0); + void *mem = ctx.Allocator.Allocate(size, alignof(Storage)); + auto *symbol = new (mem) Storage(param); + +#ifndef NDEBUG + llvm::FoldingSetNodeID newID; + symbol->Profile(newID); + assert(id == newID); +#endif + + ctx.Symbols.InsertNode(symbol, insertPos); + ctx.SymbolHistogram.add(unsigned(Kind::GenericParam)); + + return symbol; +} + +/// Creates a layout symbol, representing a layout constraint. +Symbol Symbol::forLayout(LayoutConstraint layout, + RewriteContext &ctx) { + llvm::FoldingSetNodeID id; + id.AddInteger(unsigned(Kind::Layout)); + id.AddPointer(layout.getPointer()); + + void *insertPos = nullptr; + if (auto *symbol = ctx.Symbols.FindNodeOrInsertPos(id, insertPos)) + return symbol; + + unsigned size = Storage::totalSizeToAlloc(0, 0); + void *mem = ctx.Allocator.Allocate(size, alignof(Storage)); + auto *symbol = new (mem) Storage(layout); + +#ifndef NDEBUG + llvm::FoldingSetNodeID newID; + symbol->Profile(newID); + assert(id == newID); +#endif + + ctx.Symbols.InsertNode(symbol, insertPos); + ctx.SymbolHistogram.add(unsigned(Kind::Layout)); + + return symbol; +} + +/// Creates a superclass symbol, representing a superclass constraint. +Symbol Symbol::forSuperclass(CanType type, ArrayRef substitutions, + RewriteContext &ctx) { + llvm::FoldingSetNodeID id; + id.AddInteger(unsigned(Kind::Superclass)); + id.AddPointer(type.getPointer()); + id.AddInteger(unsigned(substitutions.size())); + + for (auto substitution : substitutions) + id.AddPointer(substitution.getOpaquePointer()); + + void *insertPos = nullptr; + if (auto *symbol = ctx.Symbols.FindNodeOrInsertPos(id, insertPos)) + return symbol; + + unsigned size = Storage::totalSizeToAlloc( + 0, substitutions.size()); + void *mem = ctx.Allocator.Allocate(size, alignof(Storage)); + auto *symbol = new (mem) Storage(Kind::Superclass, type, substitutions); + +#ifndef NDEBUG + llvm::FoldingSetNodeID newID; + symbol->Profile(newID); + assert(id == newID); +#endif + + ctx.Symbols.InsertNode(symbol, insertPos); + ctx.SymbolHistogram.add(unsigned(Kind::Superclass)); + + return symbol; +} + +/// Creates a concrete type symbol, representing a superclass constraint. +Symbol Symbol::forConcreteType(CanType type, ArrayRef substitutions, + RewriteContext &ctx) { + llvm::FoldingSetNodeID id; + id.AddInteger(unsigned(Kind::ConcreteType)); + id.AddPointer(type.getPointer()); + id.AddInteger(unsigned(substitutions.size())); + for (auto substitution : substitutions) + id.AddPointer(substitution.getOpaquePointer()); + + void *insertPos = nullptr; + if (auto *symbol = ctx.Symbols.FindNodeOrInsertPos(id, insertPos)) + return symbol; + + unsigned size = Storage::totalSizeToAlloc( + 0, substitutions.size()); + void *mem = ctx.Allocator.Allocate(size, alignof(Storage)); + auto *symbol = new (mem) Storage(Kind::ConcreteType, type, substitutions); + +#ifndef NDEBUG + llvm::FoldingSetNodeID newID; + symbol->Profile(newID); + assert(id == newID); +#endif + + ctx.Symbols.InsertNode(symbol, insertPos); + ctx.SymbolHistogram.add(unsigned(Kind::ConcreteType)); + + return symbol; +} + +/// Given that this symbol is the first symbol of a term, return the +/// "domain" of the term. +/// +/// - If the first symbol is a protocol symbol [P], the domain is P. +/// - If the first symbol is an associated type symbol [P1&...&Pn], +/// the domain is {P1, ..., Pn}. +/// - If the first symbol is a generic parameter symbol, the domain is +/// the empty set {}. +/// - Anything else will assert. +ArrayRef Symbol::getRootProtocols() const { + switch (getKind()) { + case Symbol::Kind::Protocol: + case Symbol::Kind::AssociatedType: + return getProtocols(); + + case Symbol::Kind::GenericParam: + return ArrayRef(); + + case Symbol::Kind::Name: + case Symbol::Kind::Layout: + case Symbol::Kind::Superclass: + case Symbol::Kind::ConcreteType: + break; + } + + llvm_unreachable("Bad root symbol"); +} + +/// Linear order on symbols. +/// +/// First, we order different kinds as follows, from smallest to largest: +/// +/// - Protocol +/// - AssociatedType +/// - GenericParam +/// - Name +/// - Layout +/// - Superclass +/// - ConcreteType +/// +/// Then we break ties when both symbols have the same kind as follows: +/// +/// * For associated type symbols, we first order the number of protocols, +/// with symbols containing more protocols coming first. This ensures +/// that the following holds: +/// +/// [P1&P2:T] < [P1:T] +/// [P1&P2:T] < [P2:T] +/// +/// If both symbols have the same number of protocols, we perform a +/// lexicographic comparison on the protocols pair-wise, using the +/// protocol order defined by \p graph (see +/// ProtocolGraph::compareProtocols()). +/// +/// * For generic parameter symbols, we first order by depth, then index. +/// +/// * For unbound name symbols, we compare identifiers lexicographically. +/// +/// * For protocol symbols, we compare the protocols using the protocol +/// linear order on \p graph. +/// +/// * For layout symbols, we use LayoutConstraint::compare(). +int Symbol::compare(Symbol other, const ProtocolGraph &graph) const { + // Exit early if the symbols are equal. + if (Ptr == other.Ptr) + return 0; + + auto kind = getKind(); + auto otherKind = other.getKind(); + + if (kind != otherKind) + return int(kind) < int(otherKind) ? -1 : 1; + + int result = 0; + + switch (kind) { + case Kind::Name: + result = getName().compare(other.getName()); + break; + + case Kind::Protocol: + result = graph.compareProtocols(getProtocol(), other.getProtocol()); + break; + + case Kind::AssociatedType: { + auto protos = getProtocols(); + auto otherProtos = other.getProtocols(); + + // Symbols with more protocols are 'smaller' than those with fewer. + if (protos.size() != otherProtos.size()) + return protos.size() > otherProtos.size() ? -1 : 1; + + for (unsigned i : indices(protos)) { + int result = graph.compareProtocols(protos[i], otherProtos[i]); + if (result) + return result; + } + + result = getName().compare(other.getName()); + break; + } + + case Kind::GenericParam: { + auto *param = getGenericParam(); + auto *otherParam = other.getGenericParam(); + + if (param->getDepth() != otherParam->getDepth()) + return param->getDepth() < otherParam->getDepth() ? -1 : 1; + + if (param->getIndex() != otherParam->getIndex()) + return param->getIndex() < otherParam->getIndex() ? -1 : 1; + + break; + } + + case Kind::Layout: + result = getLayoutConstraint().compare(other.getLayoutConstraint()); + break; + + case Kind::Superclass: + case Kind::ConcreteType: { + assert(false && "Cannot compare concrete types yet"); + break; + } + } + + assert(result != 0 && "Two distinct symbols should not compare equal"); + return result; +} + +/// For a superclass or concrete type symbol +/// +/// [concrete: Foo] +/// [superclass: Foo] +/// +/// Return a new symbol where the function fn is applied to each of the +/// substitutions: +/// +/// [concrete: Foo] +/// [superclass: Foo] +/// +/// Asserts if this is not a superclass or concrete type symbol. +Symbol Symbol::transformConcreteSubstitutions( + llvm::function_ref fn, + RewriteContext &ctx) const { + assert(isSuperclassOrConcreteType()); + + if (getSubstitutions().empty()) + return *this; + + bool anyChanged = false; + SmallVector substitutions; + for (auto term : getSubstitutions()) { + auto newTerm = fn(term); + if (newTerm != term) + anyChanged = true; + + substitutions.push_back(newTerm); + } + + if (!anyChanged) + return *this; + + switch (getKind()) { + case Kind::Superclass: + return Symbol::forSuperclass(getSuperclass(), substitutions, ctx); + case Kind::ConcreteType: + return Symbol::forConcreteType(getConcreteType(), substitutions, ctx); + + case Kind::GenericParam: + case Kind::Name: + case Kind::Protocol: + case Kind::AssociatedType: + case Kind::Layout: + break; + } + + llvm_unreachable("Bad symbol kind"); +} + +/// Print the symbol using our mnemonic representation. +void Symbol::dump(llvm::raw_ostream &out) const { + auto dumpSubstitutions = [&]() { + if (getSubstitutions().size() > 0) { + out << " with <"; + + bool first = true; + for (auto substitution : getSubstitutions()) { + if (first) { + first = false; + } else { + out << ", "; + } + substitution.dump(out); + } + + out << ">"; + } + }; + + switch (getKind()) { + case Kind::Name: + out << getName(); + return; + + case Kind::Protocol: + out << "[" << getProtocol()->getName() << "]"; + return; + + case Kind::AssociatedType: { + out << "["; + bool first = true; + for (const auto *proto : getProtocols()) { + if (first) { + first = false; + } else { + out << "&"; + } + out << proto->getName(); + } + out << ":" << getName() << "]"; + return; + } + + case Kind::GenericParam: + out << Type(getGenericParam()); + return; + + case Kind::Layout: + out << "[layout: "; + getLayoutConstraint()->print(out); + out << "]"; + return; + + case Kind::Superclass: + out << "[superclass: " << getSuperclass(); + dumpSubstitutions(); + out << "]"; + return; + + case Kind::ConcreteType: + out << "[concrete: " << getConcreteType(); + dumpSubstitutions(); + out << "]"; + return; + } + + llvm_unreachable("Bad symbol kind"); +} + +void Symbol::Storage::Profile(llvm::FoldingSetNodeID &id) const { + id.AddInteger(Kind); + + switch (Symbol::Kind(Kind)) { + case Symbol::Kind::Name: + id.AddPointer(Name.get()); + return; + + case Symbol::Kind::Layout: + id.AddPointer(Layout.getPointer()); + return; + + case Symbol::Kind::Protocol: + id.AddPointer(Proto); + return; + + case Symbol::Kind::GenericParam: + id.AddPointer(GenericParam); + return; + + case Symbol::Kind::AssociatedType: { + auto protos = getProtocols(); + id.AddInteger(protos.size()); + + for (const auto *proto : protos) + id.AddPointer(proto); + + id.AddPointer(Name.get()); + return; + } + + case Symbol::Kind::Superclass: + case Symbol::Kind::ConcreteType: { + id.AddPointer(ConcreteType.getPointer()); + + id.AddInteger(NumSubstitutions); + for (auto term : getSubstitutions()) + id.AddPointer(term.getOpaquePointer()); + + return; + } + } + + llvm_unreachable("Bad symbol kind"); +} \ No newline at end of file diff --git a/lib/AST/RequirementMachine/Symbol.h b/lib/AST/RequirementMachine/Symbol.h new file mode 100644 index 0000000000000..4646c8a9490f3 --- /dev/null +++ b/lib/AST/RequirementMachine/Symbol.h @@ -0,0 +1,251 @@ +//===--- Symbol.h - The generics rewrite system alphabet --------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" + +#ifndef SWIFT_RQM_SYMBOL_H +#define SWIFT_RQM_SYMBOL_H + +namespace llvm { + class raw_ostream; +} + +namespace swift { + +class CanType; +class ProtocolDecl; +class GenericTypeParamType; +class Identifier; +class LayoutConstraint; + +namespace rewriting { + +class MutableTerm; +class ProtocolGraph; +class RewriteContext; +class Term; + +/// The smallest element in the rewrite system. +/// +/// enum Symbol { +/// case name(Identifier) +/// case protocol(Protocol) +/// case type([Protocol], Identifier) +/// case genericParam(index: Int, depth: Int) +/// case layout(LayoutConstraint) +/// case superclass(CanType, substitutions: [Term]) +/// case concrete(CanType, substitutions: [Term]) +/// } +/// +/// For the concrete type symbols (`superclass` and `concrete`), +/// the type's structural components must either be concrete, or +/// generic parameters. All generic parameters must have a depth +/// of 0; the generic parameter index corresponds to an index in +/// the `substitutions` array. +/// +/// For example, the superclass requirement +/// "T : MyClass V.A.B>" is denoted with a symbol +/// structured as follows: +/// +/// - type: MyClass<τ_0_0, (Int) -> τ_0_1> +/// - substitutions: +/// - U.X +/// - V.A.B +/// +/// Out-of-line methods are documented in Symbol.cpp. +class Symbol final { +public: + enum class Kind : uint8_t { + ////// + ////// Special symbol kind that is both type-like and property-like: + ////// + + /// When appearing at the start of a term, denotes a nested + /// type of a protocol 'Self' type. + /// + /// When appearing at the end of a term, denotes that the + /// term's type conforms to the protocol. + Protocol, + + ////// + ////// "Type-like" symbol kinds: + ////// + + /// An associated type [P:T] or [P&Q&...:T]. The parent term + /// must be known to conform to P (or P, Q, ...). + AssociatedType, + + /// A generic parameter, uniquely identified by depth and + /// index. Can only appear at the beginning of a term, where + /// it denotes a generic parameter of the top-level generic + /// signature. + GenericParam, + + /// An unbound identifier name. + Name, + + ////// + ////// "Property-like" symbol kinds: + ////// + + /// When appearing at the end of a term, denotes that the + /// term's type satisfies the layout constraint. + Layout, + + /// When appearing at the end of a term, denotes that the term + /// is a subclass of the superclass constraint. + Superclass, + + /// When appearing at the end of a term, denotes that the term + /// is exactly equal to the concrete type. + ConcreteType, + }; + + static const unsigned NumKinds = 7; + + static const StringRef Kinds[]; + +private: + friend class RewriteContext; + + struct Storage; + +private: + const Storage *Ptr; + + Symbol(const Storage *ptr) : Ptr(ptr) {} + +public: + Kind getKind() const; + + /// A property records something about a type term; either a protocol + /// conformance, a layout constraint, or a superclass or concrete type + /// constraint. + bool isProperty() const { + auto kind = getKind(); + return (kind == Symbol::Kind::Protocol || + kind == Symbol::Kind::Layout || + kind == Symbol::Kind::Superclass || + kind == Symbol::Kind::ConcreteType); + } + + bool isSuperclassOrConcreteType() const { + auto kind = getKind(); + return (kind == Kind::Superclass || kind == Kind::ConcreteType); + } + + Identifier getName() const; + + const ProtocolDecl *getProtocol() const; + + ArrayRef getProtocols() const; + + GenericTypeParamType *getGenericParam() const; + + LayoutConstraint getLayoutConstraint() const; + + CanType getSuperclass() const; + + CanType getConcreteType() const; + + ArrayRef getSubstitutions() const; + + /// Returns an opaque pointer that uniquely identifies this symbol. + const void *getOpaquePointer() const { + return Ptr; + } + + static Symbol fromOpaquePointer(void *ptr) { + return Symbol((Storage *) ptr); + } + + static Symbol forName(Identifier name, + RewriteContext &ctx); + + static Symbol forProtocol(const ProtocolDecl *proto, + RewriteContext &ctx); + + static Symbol forAssociatedType(const ProtocolDecl *proto, + Identifier name, + RewriteContext &ctx); + + static Symbol forAssociatedType(ArrayRef protos, + Identifier name, + RewriteContext &ctx); + + static Symbol forGenericParam(GenericTypeParamType *param, + RewriteContext &ctx); + + static Symbol forLayout(LayoutConstraint layout, + RewriteContext &ctx); + + static Symbol forSuperclass(CanType type, + ArrayRef substitutions, + RewriteContext &ctx); + + static Symbol forConcreteType(CanType type, + ArrayRef substitutions, + RewriteContext &ctx); + + ArrayRef getRootProtocols() const; + + int compare(Symbol other, const ProtocolGraph &protos) const; + + Symbol transformConcreteSubstitutions( + llvm::function_ref fn, + RewriteContext &ctx) const; + + Symbol prependPrefixToConcreteSubstitutions( + const MutableTerm &prefix, + RewriteContext &ctx) const; + + void dump(llvm::raw_ostream &out) const; + + friend bool operator==(Symbol lhs, Symbol rhs) { + return lhs.Ptr == rhs.Ptr; + } + + friend bool operator!=(Symbol lhs, Symbol rhs) { + return !(lhs == rhs); + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &out, Symbol symbol) { + symbol.dump(out); + return out; + } +}; + +} // end namespace rewriting + +} // end namespace swift + +namespace llvm { + template<> struct DenseMapInfo { + static swift::rewriting::Symbol getEmptyKey() { + return swift::rewriting::Symbol::fromOpaquePointer( + llvm::DenseMapInfo::getEmptyKey()); + } + static swift::rewriting::Symbol getTombstoneKey() { + return swift::rewriting::Symbol::fromOpaquePointer( + llvm::DenseMapInfo::getTombstoneKey()); + } + static unsigned getHashValue(swift::rewriting::Symbol Val) { + return DenseMapInfo::getHashValue(Val.getOpaquePointer()); + } + static bool isEqual(swift::rewriting::Symbol LHS, + swift::rewriting::Symbol RHS) { + return LHS == RHS; + } + }; +} // end namespace llvm + +#endif \ No newline at end of file diff --git a/lib/AST/RequirementMachine/Term.cpp b/lib/AST/RequirementMachine/Term.cpp new file mode 100644 index 0000000000000..12ba6f0029847 --- /dev/null +++ b/lib/AST/RequirementMachine/Term.cpp @@ -0,0 +1,190 @@ +//===--- Term.cpp - A term in the generics rewrite system -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Decl.h" +#include "swift/AST/Types.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include "ProtocolGraph.h" +#include "RewriteContext.h" +#include "Symbol.h" +#include "Term.h" + +using namespace swift; +using namespace rewriting; + +/// Terms are uniqued and immutable, stored as a single pointer; +/// the Storage type is the allocated backing storage. +struct Term::Storage final + : public llvm::FoldingSetNode, + public llvm::TrailingObjects { + friend class Symbol; + + unsigned Size; + + explicit Storage(unsigned size) : Size(size) {} + + size_t numTrailingObjects(OverloadToken) const { + return Size; + } + + MutableArrayRef getElements() { + return {getTrailingObjects(), Size}; + } + + ArrayRef getElements() const { + return {getTrailingObjects(), Size}; + } + + void Profile(llvm::FoldingSetNodeID &id) const; +}; + +size_t Term::size() const { return Ptr->Size; } + +ArrayRef::iterator Term::begin() const { + return Ptr->getElements().begin(); +} + +ArrayRef::iterator Term::end() const { + return Ptr->getElements().end(); +} + +ArrayRef::reverse_iterator Term::rbegin() const { + return Ptr->getElements().rbegin(); +} + +ArrayRef::reverse_iterator Term::rend() const { + return Ptr->getElements().rend(); +} + +Symbol Term::back() const { + return Ptr->getElements().back(); +} + +Symbol Term::operator[](size_t index) const { + return Ptr->getElements()[index]; +} + +void Term::dump(llvm::raw_ostream &out) const { + MutableTerm(*this).dump(out); +} + +Term Term::get(const MutableTerm &mutableTerm, RewriteContext &ctx) { + unsigned size = mutableTerm.size(); + assert(size > 0 && "Term must have at least one symbol"); + + llvm::FoldingSetNodeID id; + id.AddInteger(size); + for (auto symbol : mutableTerm) + id.AddPointer(symbol.getOpaquePointer()); + + void *insertPos = nullptr; + if (auto *term = ctx.Terms.FindNodeOrInsertPos(id, insertPos)) + return term; + + void *mem = ctx.Allocator.Allocate( + Storage::totalSizeToAlloc(size), + alignof(Storage)); + auto *term = new (mem) Storage(size); + for (unsigned i = 0; i < size; ++i) + term->getElements()[i] = mutableTerm[i]; + + ctx.Terms.InsertNode(term, insertPos); + ctx.TermHistogram.add(size); + + return term; +} + +void Term::Storage::Profile(llvm::FoldingSetNodeID &id) const { + id.AddInteger(Size); + + for (auto symbol : getElements()) + id.AddPointer(symbol.getOpaquePointer()); +} + +/// Shortlex order on terms. +/// +/// First we compare length, then perform a lexicographic comparison +/// on symbols if the two terms have the same length. +int MutableTerm::compare(const MutableTerm &other, + const ProtocolGraph &graph) const { + if (size() != other.size()) + return size() < other.size() ? -1 : 1; + + for (unsigned i = 0, e = size(); i < e; ++i) { + auto lhs = (*this)[i]; + auto rhs = other[i]; + + int result = lhs.compare(rhs, graph); + if (result != 0) { + assert(lhs != rhs); + return result; + } + + assert(lhs == rhs); + } + + return 0; +} + +/// Replace the subterm in the range [from,to) with \p rhs. +/// +/// Note that \p rhs must precede [from,to) in the linear +/// order on terms. +void MutableTerm::rewriteSubTerm( + decltype(MutableTerm::Symbols)::iterator from, + decltype(MutableTerm::Symbols)::iterator to, + Term rhs) { + auto oldSize = size(); + unsigned lhsLength = (unsigned)(to - from); + assert(rhs.size() <= lhsLength); + + // Overwrite the occurrence of the left hand side with the + // right hand side. + auto newIter = std::copy(rhs.begin(), rhs.end(), from); + + // If the right hand side is shorter than the left hand side, + // then newIter will point to a location before oldIter, eg + // if this term is 'T.A.B.C', lhs is 'A.B' and rhs is 'X', + // then we now have: + // + // T.X .C + // ^--- oldIter + // ^--- newIter + // + // Shift everything over to close the gap (by one location, + // in this case). + if (newIter != to) { + auto newEnd = std::copy(to, end(), newIter); + + // Now, we've moved the gap to the end of the term; close + // it by shortening the term. + Symbols.erase(newEnd, end()); + } + + assert(size() == oldSize - lhsLength + rhs.size()); +} + +void MutableTerm::dump(llvm::raw_ostream &out) const { + bool first = true; + + for (auto symbol : Symbols) { + if (!first) + out << "."; + else + first = false; + + symbol.dump(out); + } +} \ No newline at end of file diff --git a/lib/AST/RequirementMachine/Term.h b/lib/AST/RequirementMachine/Term.h new file mode 100644 index 0000000000000..9801eb5963066 --- /dev/null +++ b/lib/AST/RequirementMachine/Term.h @@ -0,0 +1,206 @@ +//===--- Term.h - A term in the generics rewrite system ---------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "Symbol.h" + +#ifndef SWIFT_RQM_TERM_H +#define SWIFT_RQM_TERM_H + +namespace llvm { + class raw_ostream; +} + +namespace swift { + +namespace rewriting { + +/// A term is a sequence of one or more symbols. +/// +/// The Term type is a uniqued, permanently-allocated representation, +/// used to represent terms in the rewrite rules themselves. See also +/// MutableTerm for the other representation. +/// +/// The first symbol in the term must be a protocol, generic parameter, or +/// associated type symbol. +/// +/// A layout, superclass or concrete type symbol must only appear at the +/// end of a term. +/// +/// Out-of-line methods are documented in Term.cpp. +class Term final { + friend class RewriteContext; + + struct Storage; + + const Storage *Ptr; + + Term(const Storage *ptr) : Ptr(ptr) {} + +public: + size_t size() const; + + ArrayRef::iterator begin() const; + ArrayRef::iterator end() const; + + ArrayRef::reverse_iterator rbegin() const; + ArrayRef::reverse_iterator rend() const; + + Symbol back() const; + + Symbol operator[](size_t index) const; + + /// Returns an opaque pointer that uniquely identifies this term. + const void *getOpaquePointer() const { + return Ptr; + } + + static Term get(const MutableTerm &term, RewriteContext &ctx); + + ArrayRef getRootProtocols() const { + return begin()->getRootProtocols(); + } + + void dump(llvm::raw_ostream &out) const; + + friend bool operator==(Term lhs, Term rhs) { + return lhs.Ptr == rhs.Ptr; + } + + friend bool operator!=(Term lhs, Term rhs) { + return !(lhs == rhs); + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &out, Term term) { + term.dump(out); + return out; + } +}; + +/// A term is a sequence of one or more symbols. +/// +/// The MutableTerm type is a dynamically-allocated representation, +/// used to represent temporary values in simplification and completion. +/// See also Term for the other representation. +/// +/// The first symbol in the term must be a protocol, generic parameter, or +/// associated type symbol. +/// +/// A layout constraint symbol must only appear at the end of a term. +/// +/// Out-of-line methods are documented in RewriteSystem.cpp. +class MutableTerm final { + llvm::SmallVector Symbols; + +public: + /// Creates an empty term. At least one symbol must be added for the term + /// to become valid. + MutableTerm() {} + + explicit MutableTerm(decltype(Symbols)::const_iterator begin, + decltype(Symbols)::const_iterator end) + : Symbols(begin, end) {} + + explicit MutableTerm(llvm::SmallVector &&symbols) + : Symbols(std::move(symbols)) {} + + explicit MutableTerm(ArrayRef symbols) + : Symbols(symbols.begin(), symbols.end()) {} + + explicit MutableTerm(Term term) + : Symbols(term.begin(), term.end()) {} + + void add(Symbol symbol) { + Symbols.push_back(symbol); + } + + void append(Term other) { + Symbols.append(other.begin(), other.end()); + } + + void append(const MutableTerm &other) { + Symbols.append(other.begin(), other.end()); + } + + void append(decltype(Symbols)::const_iterator from, + decltype(Symbols)::const_iterator to) { + Symbols.append(from, to); + } + + int compare(const MutableTerm &other, const ProtocolGraph &protos) const; + + bool empty() const { return Symbols.empty(); } + + size_t size() const { return Symbols.size(); } + + ArrayRef getRootProtocols() const { + return begin()->getRootProtocols(); + } + + decltype(Symbols)::const_iterator begin() const { return Symbols.begin(); } + decltype(Symbols)::const_iterator end() const { return Symbols.end(); } + + decltype(Symbols)::iterator begin() { return Symbols.begin(); } + decltype(Symbols)::iterator end() { return Symbols.end(); } + + decltype(Symbols)::const_reverse_iterator rbegin() const { return Symbols.rbegin(); } + decltype(Symbols)::const_reverse_iterator rend() const { return Symbols.rend(); } + + decltype(Symbols)::reverse_iterator rbegin() { return Symbols.rbegin(); } + decltype(Symbols)::reverse_iterator rend() { return Symbols.rend(); } + + Symbol back() const { + return Symbols.back(); + } + + Symbol &back() { + return Symbols.back(); + } + + Symbol operator[](size_t index) const { + return Symbols[index]; + } + + Symbol &operator[](size_t index) { + return Symbols[index]; + } + + void rewriteSubTerm(decltype(Symbols)::iterator from, + decltype(Symbols)::iterator to, + Term rhs); + + void dump(llvm::raw_ostream &out) const; + + friend bool operator==(const MutableTerm &lhs, const MutableTerm &rhs) { + if (lhs.size() != rhs.size()) + return false; + + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); + } + + friend bool operator!=(const MutableTerm &lhs, const MutableTerm &rhs) { + return !(lhs == rhs); + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &out, + const MutableTerm &term) { + term.dump(out); + return out; + } +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif \ No newline at end of file diff --git a/lib/AST/RequirementMachine/Trie.h b/lib/AST/RequirementMachine/Trie.h new file mode 100644 index 0000000000000..3d22560182a9a --- /dev/null +++ b/lib/AST/RequirementMachine/Trie.h @@ -0,0 +1,210 @@ +//===--- Trie.h - Trie with terms as keys ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RQM_TRIE_H +#define SWIFT_RQM_TRIE_H + +#include "llvm/ADT/MapVector.h" +#include "Histogram.h" + +namespace swift { + +namespace rewriting { + +enum class MatchKind { + Shortest, + Longest +}; + +template +class Trie { +public: + struct Node; + + struct Entry { + Optional Value; + Node *Children = nullptr; + }; + + struct Node { + llvm::SmallMapVector Entries; + }; + +private: + /// We never delete nodes, except for when the entire trie is torn down. + std::vector Nodes; + + /// The root is stored directly. + Node Root; + +public: + void updateHistograms(Histogram &stats, Histogram &rootStats) const { + for (const auto &node : Nodes) + stats.add(node->Entries.size()); + rootStats.add(Root.Entries.size()); + } + + /// Delete all entries from the trie. + void clear() { + Root.Entries.clear(); + + for (auto iter = Nodes.rbegin(); iter != Nodes.rend(); ++iter) { + auto *node = *iter; + delete node; + } + + Nodes.clear(); + } + + ~Trie() { + clear(); + } + + /// Insert an entry with the key given by the range [begin, end). + /// Returns the old value if the trie already had an entry for this key; + /// this is actually an invariant violation, but we can produce a better + /// assertion further up the stack. + template + Optional insert(Iter begin, Iter end, ValueType value) { + assert(begin != end); + auto *node = &Root; + + while (true) { + auto &entry = node->Entries[*begin]; + ++begin; + + if (begin == end) { + if (entry.Value) + return entry.Value; + + entry.Value = value; + return None; + } + + if (entry.Children == nullptr) { + entry.Children = new Node(); + Nodes.push_back(entry.Children); + } + + node = entry.Children; + } + } + + /// Find the shortest or longest prefix of the range given by [begin,end), + /// depending on whether the Kind template parameter was bound to + /// MatchKind::Shortest or MatchKind::Longest. + template + Optional + find(Iter begin, Iter end) const { + assert(begin != end); + auto *node = &Root; + + Optional bestMatch = None; + + while (true) { + auto found = node->Entries.find(*begin); + ++begin; + + if (found == node->Entries.end()) + return bestMatch; + + const auto &entry = found->second; + + if (entry.Value) { + if (Kind == MatchKind::Shortest) + return entry.Value; + + bestMatch = entry.Value; + } + + if (begin == end) + return bestMatch; + + if (entry.Children == nullptr) + return bestMatch; + + node = entry.Children; + } + } + + /// Find all keys that begin with the given symbol. Fn must take a single + /// argument of type ValueType. + template + void findAll(Symbol symbol, Fn fn) { + auto found = Root.Entries.find(symbol); + if (found == Root.Entries.end()) + return; + + const auto &entry = found->second; + + if (entry.Value) + fn(*entry.Value); + + if (entry.Children == nullptr) + return; + + visitChildren(entry.Children, fn); + } + + /// Find all keys that either match a prefix of [begin,end), or where + /// [begin,end) matches a prefix of the key. Fn must take a single + /// argument of type ValueType. + template + void findAll(Iter begin, Iter end, Fn fn) { + assert(begin != end); + auto *node = &Root; + + while (true) { + auto found = node->Entries.find(*begin); + ++begin; + + if (found == node->Entries.end()) + return; + + const auto &entry = found->second; + + if (entry.Value) + fn(*entry.Value); + + if (entry.Children == nullptr) + return; + + node = entry.Children; + + if (begin == end) { + visitChildren(node, fn); + return; + } + } + } + +private: + /// Depth-first traversal of all children of the given node, including + /// the node itself. Fn must take a single argument of type ValueType. + template + void visitChildren(Node *node, Fn fn) { + for (const auto &pair : node->Entries) { + const auto &entry = pair.second; + if (entry.Value) + fn(*entry.Value); + + if (entry.Children) + visitChildren(entry.Children, fn); + } + } +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif \ No newline at end of file diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 53e175698ff57..4749de8c63007 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -33,12 +33,6 @@ using namespace swift; // Stmt methods. //===----------------------------------------------------------------------===// -// Only allow allocation of Stmts using the allocator in ASTContext. -void *Stmt::operator new(size_t Bytes, ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); -} - StringRef Stmt::getKindName(StmtKind K) { switch (K) { #define STMT(Id, Parent) case StmtKind::Id: return #Id; @@ -281,15 +275,15 @@ void LabeledConditionalStmt::setCond(StmtCondition e) { Cond = e; } -PoundAvailableInfo *PoundAvailableInfo::create(ASTContext &ctx, - SourceLoc PoundLoc, - SourceLoc LParenLoc, - ArrayRef queries, - SourceLoc RParenLoc) { +PoundAvailableInfo * +PoundAvailableInfo::create(ASTContext &ctx, SourceLoc PoundLoc, + SourceLoc LParenLoc, + ArrayRef queries, + SourceLoc RParenLoc, bool isUnavailability) { unsigned size = totalSizeToAlloc(queries.size()); void *Buffer = ctx.Allocate(size, alignof(PoundAvailableInfo)); return ::new (Buffer) PoundAvailableInfo(PoundLoc, LParenLoc, queries, - RParenLoc); + RParenLoc, isUnavailability); } SourceLoc PoundAvailableInfo::getEndLoc() const { diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index db4c8ced381cc..badac0c51d868 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -89,7 +89,7 @@ ArrayRef SubstitutionMap::getReplacementTypes() const { // Make sure we've filled in all of the replacement types. if (!storage->populatedAllReplacements) { - for (auto gp : getGenericSignature()->getGenericParams()) { + for (auto gp : getGenericSignature().getGenericParams()) { (void)lookupSubstitution(cast(gp->getCanonicalType())); } @@ -103,7 +103,7 @@ ArrayRef SubstitutionMap::getInnermostReplacementTypes() const { if (empty()) return { }; return getReplacementTypes().take_back( - getGenericSignature()->getInnermostGenericParams().size()); + getGenericSignature().getInnermostGenericParams().size()); } GenericSignature SubstitutionMap::getGenericSignature() const { @@ -213,7 +213,7 @@ SubstitutionMap SubstitutionMap::get(GenericSignature genericSig, // Form the replacement types. SmallVector replacementTypes; - replacementTypes.reserve(genericSig->getGenericParams().size()); + replacementTypes.reserve(genericSig.getGenericParams().size()); genericSig->forEachParam([&](GenericTypeParamType *gp, bool canonical) { // Don't eagerly form replacements for non-canonical generic parameters. @@ -229,7 +229,7 @@ SubstitutionMap SubstitutionMap::get(GenericSignature genericSig, // Form the stored conformances. SmallVector conformances; - for (const auto &req : genericSig->getRequirements()) { + for (const auto &req : genericSig.getRequirements()) { if (req.getKind() != RequirementKind::Conformance) continue; CanType depTy = req.getFirstType()->getCanonicalType(); @@ -263,7 +263,7 @@ Type SubstitutionMap::lookupSubstitution(CanSubstitutableType type) const { auto replacementTypes = mutableThis->getReplacementTypesBuffer(); auto genericSig = getGenericSignature(); assert(genericSig); - auto genericParams = genericSig->getGenericParams(); + auto genericParams = genericSig.getGenericParams(); auto replacementIndex = GenericParamKey(genericParam).findIndexIn(genericParams); @@ -336,7 +336,7 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { auto getSignatureConformance = [&](Type type, ProtocolDecl *proto) -> Optional { unsigned index = 0; - for (auto reqt : genericSig->getRequirements()) { + for (auto reqt : genericSig.getRequirements()) { if (reqt.getKind() == RequirementKind::Conformance) { if (reqt.getFirstType()->isEqual(type) && reqt.getProtocolDecl() == proto) @@ -365,8 +365,10 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // If the type doesn't conform to this protocol, the result isn't formed // from these requirements. - if (!genericSig->requiresProtocol(type, proto)) - return ProtocolConformanceRef::forInvalid(); + if (!genericSig->requiresProtocol(type, proto)) { + Type substType = type.subst(*this); + return ProtocolConformanceRef::forMissingOrInvalid(substType, proto); + } auto accessPath = genericSig->getConformanceAccessPath(type, proto); @@ -461,7 +463,7 @@ SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs, auto oldConformances = getConformances(); auto genericSig = getGenericSignature(); - for (const auto &req : genericSig->getRequirements()) { + for (const auto &req : genericSig.getRequirements()) { if (req.getKind() != RequirementKind::Conformance) continue; auto conformance = oldConformances[0]; @@ -542,7 +544,7 @@ SubstitutionMap::getOverrideSubstitutions(const ClassDecl *baseClass, unsigned baseDepth = 0; SubstitutionMap baseSubMap; if (auto baseClassSig = baseClass->getGenericSignature()) { - baseDepth = baseClassSig->getGenericParams().back()->getDepth() + 1; + baseDepth = baseClassSig.getGenericParams().back()->getDepth() + 1; auto derivedClassTy = derivedClass->getDeclaredInterfaceType(); if (derivedSubs) @@ -556,7 +558,7 @@ SubstitutionMap::getOverrideSubstitutions(const ClassDecl *baseClass, unsigned origDepth = 0; if (auto derivedClassSig = derivedClass->getGenericSignature()) - origDepth = derivedClassSig->getGenericParams().back()->getDepth() + 1; + origDepth = derivedClassSig.getGenericParams().back()->getDepth() + 1; SubstitutionMap origSubMap; if (derivedSubs) @@ -659,7 +661,7 @@ void SubstitutionMap::verify() const { unsigned conformanceIndex = 0; - for (const auto &req : getGenericSignature()->getRequirements()) { + for (const auto &req : getGenericSignature().getRequirements()) { if (req.getKind() != RequirementKind::Conformance) continue; diff --git a/lib/AST/SubstitutionMapStorage.h b/lib/AST/SubstitutionMapStorage.h index 52da2bc4e8203..a365bcef3e993 100644 --- a/lib/AST/SubstitutionMapStorage.h +++ b/lib/AST/SubstitutionMapStorage.h @@ -55,7 +55,7 @@ class SubstitutionMap::Storage final private: unsigned getNumReplacementTypes() const { - return genericSig->getGenericParams().size(); + return genericSig.getGenericParams().size(); } size_t numTrailingObjects(OverloadToken) const { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index da32af4b45498..f20ea1969684b 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -96,12 +96,6 @@ SourceLoc TypeLoc::getLoc() const { return SourceLoc(); } -// Only allow allocation of Types using the allocator in ASTContext. -void *TypeBase::operator new(size_t bytes, const ASTContext &ctx, - AllocationArena arena, unsigned alignment) { - return ctx.Allocate(bytes, alignment, arena); -} - NominalTypeDecl *CanType::getAnyNominal() const { return dyn_cast_or_null(getAnyGeneric()); } @@ -1163,38 +1157,6 @@ static void addProtocols(Type T, Superclass = T; } -/// Add the protocol (or protocols) in the type T to the stack of -/// protocols, checking whether any of the protocols had already been seen and -/// zapping those in the original list that we find again. -static void -addMinimumProtocols(Type T, SmallVectorImpl &Protocols, - llvm::SmallDenseMap &Known, - llvm::SmallPtrSetImpl &Visited, - SmallVector &Stack, bool &ZappedAny) { - if (auto Proto = T->getAs()) { - auto KnownPos = Known.find(Proto->getDecl()); - if (KnownPos != Known.end()) { - // We've come across a protocol that is in our original list. Zap it. - Protocols[KnownPos->second] = nullptr; - ZappedAny = true; - } - - if (Visited.insert(Proto->getDecl()).second) { - Stack.push_back(Proto->getDecl()); - for (auto Inherited : Proto->getDecl()->getInheritedProtocols()) - addMinimumProtocols(Inherited->getDeclaredInterfaceType(), - Protocols, Known, Visited, Stack, ZappedAny); - } - return; - } - - if (auto PC = T->getAs()) { - for (auto C : PC->getMembers()) { - addMinimumProtocols(C, Protocols, Known, Visited, Stack, ZappedAny); - } - } -} - bool ProtocolType::visitAllProtocols( ArrayRef protocols, llvm::function_ref fn) { @@ -1229,44 +1191,48 @@ bool ProtocolType::visitAllProtocols( void ProtocolType::canonicalizeProtocols( SmallVectorImpl &protocols) { llvm::SmallDenseMap known; - llvm::SmallPtrSet visited; - SmallVector stack; bool zappedAny = false; // Seed the stack with the protocol declarations in the original list. // Zap any obvious duplicates along the way. - for (unsigned I = 0, N = protocols.size(); I != N; ++I) { - // Check whether we've seen this protocol before. - auto knownPos = known.find(protocols[I]); - + for (unsigned i : indices(protocols)) { // If we have not seen this protocol before, record its index. - if (knownPos == known.end()) { - known[protocols[I]] = I; - stack.push_back(protocols[I]); + if (known.count(protocols[i]) == 0) { + known[protocols[i]] = i; continue; } - + // We have seen this protocol before; zap this occurrence. - protocols[I] = nullptr; + protocols[i] = nullptr; zappedAny = true; } // Walk the inheritance hierarchies of all of the protocols. If we run into // one of the known protocols, zap it from the original list. - while (!stack.empty()) { - ProtocolDecl *Current = stack.back(); - stack.pop_back(); - + for (unsigned i : indices(protocols)) { + auto *proto = protocols[i]; + if (proto == nullptr) + continue; + // Add the protocols we inherited. - for (auto Inherited : Current->getInheritedProtocols()) { - addMinimumProtocols(Inherited->getDeclaredInterfaceType(), - protocols, known, visited, stack, zappedAny); - } + proto->walkInheritedProtocols([&](ProtocolDecl *inherited) { + if (inherited == proto) + return TypeWalker::Action::Continue; + + auto found = known.find(inherited); + if (found != known.end()) { + protocols[found->second] = nullptr; + zappedAny = true; + } + + return TypeWalker::Action::Continue; + }); } - if (zappedAny) + if (zappedAny) { protocols.erase(std::remove(protocols.begin(), protocols.end(), nullptr), protocols.end()); + } // Sort the set of protocols by module + name, to give a stable // ordering. @@ -1561,6 +1527,7 @@ Type SugarType::getSinglyDesugaredTypeSlow() { case TypeKind::TypeAlias: llvm_unreachable("bound type alias types always have an underlying type"); case TypeKind::ArraySlice: + case TypeKind::VariadicSequence: implDecl = Context->getArrayDecl(); break; case TypeKind::Optional: @@ -2121,11 +2088,11 @@ class IsBindableVisitor bool didChange = newParent != substBGT.getParent(); auto depthStart = - genericSig->getGenericParams().size() - bgt->getGenericArgs().size(); + genericSig.getGenericParams().size() - bgt->getGenericArgs().size(); for (auto i : indices(bgt->getGenericArgs())) { auto orig = bgt->getGenericArgs()[i]->getCanonicalType(); auto subst = substBGT.getGenericArgs()[i]; - auto gp = genericSig->getGenericParams()[depthStart + i]; + auto gp = genericSig.getGenericParams()[depthStart + i]; // The new type is upper-bounded by the constraints the nominal type // requires. The substitution operation may be interested in transforming @@ -2152,7 +2119,7 @@ class IsBindableVisitor didChange |= (newParam != subst); } - for (const auto &req : genericSig->getRequirements()) { + for (const auto &req : genericSig.getRequirements()) { if (req.getKind() != RequirementKind::Conformance) continue; auto canTy = req.getFirstType()->getCanonicalType(); @@ -2638,9 +2605,7 @@ getForeignRepresentable(Type type, ForeignLanguage language, if (auto objcBridgeable = ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable)) { SmallVector conformances; - if (nominal->lookupConformance(dc->getParentModule(), - objcBridgeable, - conformances)) + if (nominal->lookupConformance(objcBridgeable, conformances)) break; } } @@ -3003,7 +2968,8 @@ Type ArchetypeType::getExistentialType() const { constraintTypes.push_back(proto->getDeclaredInterfaceType()); } return ProtocolCompositionType::get( - const_cast(this)->getASTContext(), constraintTypes, false); + const_cast(this)->getASTContext(), constraintTypes, + requiresClass()); } PrimaryArchetypeType::PrimaryArchetypeType(const ASTContext &Ctx, @@ -3472,7 +3438,7 @@ void ArchetypeType::registerNestedType(Identifier name, Type nested) { "Unable to find nested type?"); assert(!found->second || found->second->isEqual(nested) || - (found->second->hasError() && nested->hasError())); + found->second->is()); found->second = nested; } @@ -3636,9 +3602,10 @@ bool SILFunctionType::hasSameExtInfoAs(const SILFunctionType *otherFn) { } FunctionType * -GenericFunctionType::substGenericArgs(SubstitutionMap subs) { +GenericFunctionType::substGenericArgs(SubstitutionMap subs, + SubstOptions options) { return substGenericArgs( - [=](Type t) { return t.subst(subs); }); + [=](Type t) { return t.subst(subs, options); }); } FunctionType *GenericFunctionType::substGenericArgs( @@ -3770,7 +3737,8 @@ operator()(CanType dependentType, Type conformingReplacementType, return ProtocolConformanceRef(conformedProtocol); return M->lookupConformance(conformingReplacementType, - conformedProtocol); + conformedProtocol, + /*allowMissing=*/true); } ProtocolConformanceRef LookUpConformanceInSubstitutionMap:: @@ -4188,7 +4156,7 @@ TypeBase::getContextSubstitutions(const DeclContext *dc, baseTy = baseTy->getSuperclassForDecl(ownerClass); // Gather all of the substitutions for all levels of generic arguments. - auto params = genericSig->getGenericParams(); + auto params = genericSig.getGenericParams(); unsigned n = params.size(); while (baseTy && n > 0) { @@ -4282,7 +4250,7 @@ TypeSubstitutionMap TypeBase::getMemberSubstitutions( auto *innerDC = member->getInnermostDeclContext(); if (innerDC->isInnermostContextGeneric()) { if (auto sig = innerDC->getGenericSignatureOfContext()) { - for (auto param : sig->getInnermostGenericParams()) { + for (auto param : sig.getInnermostGenericParams()) { auto *genericParam = param->getCanonicalType() ->castTo(); substitutions[genericParam] = @@ -4311,8 +4279,16 @@ SubstitutionMap TypeBase::getMemberSubstitutionMap( LookUpConformanceInModule(module)); } +Type TypeBase::getTypeOfMember(ModuleDecl *module, const VarDecl *member) { + return getTypeOfMember(module, member, member->getInterfaceType()); +} + Type TypeBase::getTypeOfMember(ModuleDecl *module, const ValueDecl *member, Type memberType) { + assert(memberType); + assert(!memberType->is() && + "Generic function types are not supported"); + if (is()) return ErrorType::get(getASTContext()); @@ -4321,12 +4297,6 @@ Type TypeBase::getTypeOfMember(ModuleDecl *module, const ValueDecl *member, return objectTy->getTypeOfMember(module, member, memberType); } - // If no member type was provided, use the member's type. - if (!memberType) - memberType = member->getInterfaceType(); - - assert(memberType); - // Perform the substitution. auto substitutions = getMemberSubstitutionMap(module, member); return memberType.subst(substitutions); @@ -4704,7 +4674,7 @@ case TypeKind::Id: return newSubs[index]; }, LookUpConformanceInModule(opaque->getDecl()->getModuleContext())); - return OpaqueTypeArchetypeType::get(opaque->getDecl(), + return OpaqueTypeArchetypeType::get(opaque->getDecl(), opaque->getOrdinal(), newSubMap); } case TypeKind::NestedArchetype: { @@ -4969,6 +4939,18 @@ case TypeKind::Id: return OptionalType::get(baseTy); } + case TypeKind::VariadicSequence: { + auto seq = cast(base); + auto baseTy = seq->getBaseType().transformRec(fn); + if (!baseTy) + return Type(); + + if (baseTy.getPointer() == seq->getBaseType().getPointer()) + return *this; + + return VariadicSequenceType::get(baseTy); + } + case TypeKind::Dictionary: { auto dict = cast(base); auto keyTy = dict->getKeyType().transformRec(fn); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 6c0f6d08f835d..8e21777014742 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -253,31 +253,6 @@ void ExistentialConformsToSelfRequest::cacheResult(bool value) const { decl->setCachedExistentialConformsToSelf(value); } -//----------------------------------------------------------------------------// -// existentialTypeSupported computation. -//----------------------------------------------------------------------------// - -void ExistentialTypeSupportedRequest::diagnoseCycle(DiagnosticEngine &diags) const { - auto decl = std::get<0>(getStorage()); - diags.diagnose(decl, diag::circular_protocol_def, decl->getName()); -} - -void ExistentialTypeSupportedRequest::noteCycleStep(DiagnosticEngine &diags) const { - auto requirement = std::get<0>(getStorage()); - diags.diagnose(requirement, diag::kind_declname_declared_here, - DescriptiveDeclKind::Protocol, requirement->getName()); -} - -Optional ExistentialTypeSupportedRequest::getCachedResult() const { - auto decl = std::get<0>(getStorage()); - return decl->getCachedExistentialTypeSupported(); -} - -void ExistentialTypeSupportedRequest::cacheResult(bool value) const { - auto decl = std::get<0>(getStorage()); - decl->setCachedExistentialTypeSupported(value); -} - //----------------------------------------------------------------------------// // isFinal computation. //----------------------------------------------------------------------------// @@ -468,47 +443,6 @@ void DefaultTypeRequest::cacheResult(Type value) const { cacheEntry = value; } -bool PropertyWrapperTypeInfoRequest::isCached() const { - auto nominal = std::get<0>(getStorage()); - return nominal->getAttrs().hasAttribute();; -} - -bool AttachedPropertyWrappersRequest::isCached() const { - auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty(); -} - -bool AttachedPropertyWrapperTypeRequest::isCached() const { - auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty(); -} - -bool PropertyWrapperBackingPropertyTypeRequest::isCached() const { - auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty() && - !(isa(var) && isa(var->getDeclContext())); -} - -bool PropertyWrapperAuxiliaryVariablesRequest::isCached() const { - auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper(); -} - -bool PropertyWrapperInitializerInfoRequest::isCached() const { - auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper(); -} - -bool PropertyWrapperMutabilityRequest::isCached() const { - auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper(); -} - -bool PropertyWrapperLValuenessRequest::isCached() const { - auto var = std::get<0>(getStorage()); - return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper(); -} - void swift::simple_display( llvm::raw_ostream &out, const PropertyWrapperTypeInfo &propertyWrapper) { out << "{ "; @@ -609,16 +543,6 @@ void swift::simple_display(llvm::raw_ostream &out, << (value.allowUsableFromInline ? "true" : "false"); } -//----------------------------------------------------------------------------// -// ResultBuilder-related requests. -//----------------------------------------------------------------------------// - -bool AttachedResultBuilderRequest::isCached() const { - // Only needs to be cached if there are any custom attributes. - auto var = std::get<0>(getStorage()); - return var->getAttrs().hasAttribute(); -} - //----------------------------------------------------------------------------// // SelfAccessKindRequest computation. //----------------------------------------------------------------------------// @@ -633,20 +557,6 @@ void SelfAccessKindRequest::cacheResult(SelfAccessKind value) const { funcDecl->setSelfAccessKind(value); } -//----------------------------------------------------------------------------// -// IsAsyncHandlerRequest computation. -//----------------------------------------------------------------------------// - -Optional IsAsyncHandlerRequest::getCachedResult() const { - auto *funcDecl = std::get<0>(getStorage()); - return funcDecl->getCachedIsAsyncHandler(); -} - -void IsAsyncHandlerRequest::cacheResult(bool value) const { - auto *funcDecl = std::get<0>(getStorage()); - funcDecl->setIsAsyncHandler(value); -} - //----------------------------------------------------------------------------// // IsGetterMutatingRequest computation. //----------------------------------------------------------------------------// @@ -1022,7 +932,6 @@ void InterfaceTypeRequest::cacheResult(Type type) const { auto *decl = std::get<0>(getStorage()); if (type) { assert(!type->hasTypeVariable() && "Type variable in interface type"); - assert(!type->hasPlaceholder() && "Type placeholder in interface type"); assert(!type->is() && "Interface type must be materializable"); assert(!type->hasArchetype() && "Archetype in interface type"); } @@ -1098,6 +1007,15 @@ void swift::simple_display(llvm::raw_ostream &out, case ImplicitMemberAction::ResolveDecodable: out << "resolve Decodable.init(from:)"; break; + case ImplicitMemberAction::ResolveDistributedActor: + out << "resolve DistributedActor"; + break; + case ImplicitMemberAction::ResolveDistributedActorIdentity: + out << "resolve DistributedActor.id"; + break; + case ImplicitMemberAction::ResolveDistributedActorTransport: + out << "resolve DistributedActor.actorTransport"; + break; } } @@ -1377,7 +1295,7 @@ Optional TypeCheckFunctionBodyRequest::getCachedResult() const { auto *afd = std::get<0>(getStorage()); switch (afd->getBodyKind()) { case BodyKind::Deserialized: - case BodyKind::MemberwiseInitializer: + case BodyKind::SILSynthesize: case BodyKind::None: case BodyKind::Skipped: // These cases don't have any body available. @@ -1525,6 +1443,7 @@ void CustomAttrTypeRequest::cacheResult(Type value) const { bool ActorIsolation::requiresSubstitution() const { switch (kind) { case ActorInstance: + case DistributedActorInstance: case Independent: case Unspecified: return false; @@ -1539,6 +1458,7 @@ bool ActorIsolation::requiresSubstitution() const { ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const { switch (kind) { case ActorInstance: + case DistributedActorInstance: case Independent: case Unspecified: return *this; @@ -1558,6 +1478,10 @@ void swift::simple_display( out << "actor-isolated to instance of " << state.getActor()->getName(); break; + case ActorIsolation::DistributedActorInstance: + out << "distributed-actor-isolated to instance of " << state.getActor()->getName(); + break; + case ActorIsolation::Independent: out << "actor-independent"; break; diff --git a/lib/AST/TypeRefinementContext.cpp b/lib/AST/TypeRefinementContext.cpp index c5bcaeb4e0d54..74099c2740643 100644 --- a/lib/AST/TypeRefinementContext.cpp +++ b/lib/AST/TypeRefinementContext.cpp @@ -28,8 +28,10 @@ using namespace swift; TypeRefinementContext::TypeRefinementContext(ASTContext &Ctx, IntroNode Node, TypeRefinementContext *Parent, SourceRange SrcRange, - const AvailabilityContext &Info) - : Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info) { + const AvailabilityContext &Info, + const AvailabilityContext &ExplicitInfo) + : Node(Node), SrcRange(SrcRange), + AvailabilityInfo(Info), ExplicitAvailabilityInfo(ExplicitInfo) { if (Parent) { assert(SrcRange.isValid()); Parent->addChild(this); @@ -46,18 +48,20 @@ TypeRefinementContext::createRoot(SourceFile *SF, ASTContext &Ctx = SF->getASTContext(); return new (Ctx) TypeRefinementContext(Ctx, SF, - /*Parent=*/nullptr, SourceRange(), Info); + /*Parent=*/nullptr, SourceRange(), + Info, AvailabilityContext::alwaysAvailable()); } TypeRefinementContext * TypeRefinementContext::createForDecl(ASTContext &Ctx, Decl *D, TypeRefinementContext *Parent, const AvailabilityContext &Info, + const AvailabilityContext &ExplicitInfo, SourceRange SrcRange) { assert(D); assert(Parent); return new (Ctx) - TypeRefinementContext(Ctx, D, Parent, SrcRange, Info); + TypeRefinementContext(Ctx, D, Parent, SrcRange, Info, ExplicitInfo); } TypeRefinementContext * @@ -68,7 +72,8 @@ TypeRefinementContext::createForIfStmtThen(ASTContext &Ctx, IfStmt *S, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/true), Parent, - S->getThenStmt()->getSourceRange(), Info); + S->getThenStmt()->getSourceRange(), + Info, /* ExplicitInfo */Info); } TypeRefinementContext * @@ -79,7 +84,8 @@ TypeRefinementContext::createForIfStmtElse(ASTContext &Ctx, IfStmt *S, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/false), Parent, - S->getElseStmt()->getSourceRange(), Info); + S->getElseStmt()->getSourceRange(), + Info, /* ExplicitInfo */Info); } TypeRefinementContext * @@ -92,7 +98,7 @@ TypeRefinementContext::createForConditionFollowingQuery(ASTContext &Ctx, assert(Parent); SourceRange Range(PAI->getEndLoc(), LastElement.getEndLoc()); return new (Ctx) TypeRefinementContext(Ctx, PAI, Parent, Range, - Info); + Info, /* ExplicitInfo */Info); } TypeRefinementContext * @@ -107,7 +113,7 @@ TypeRefinementContext::createForGuardStmtFallthrough(ASTContext &Ctx, SourceRange Range(RS->getEndLoc(), ContainingBraceStmt->getEndLoc()); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/true), - Parent, Range, Info); + Parent, Range, Info, /* ExplicitInfo */Info); } TypeRefinementContext * @@ -118,7 +124,7 @@ TypeRefinementContext::createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS, assert(Parent); return new (Ctx) TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/false), Parent, - RS->getBody()->getSourceRange(), Info); + RS->getBody()->getSourceRange(), Info, /* ExplicitInfo */Info); } TypeRefinementContext * @@ -128,14 +134,7 @@ TypeRefinementContext::createForWhileStmtBody(ASTContext &Ctx, WhileStmt *S, assert(S); assert(Parent); return new (Ctx) TypeRefinementContext( - Ctx, S, Parent, S->getBody()->getSourceRange(), Info); -} - -// Only allow allocation of TypeRefinementContext using the allocator in -// ASTContext. -void *TypeRefinementContext::operator new(size_t Bytes, ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); + Ctx, S, Parent, S->getBody()->getSourceRange(), Info, /* ExplicitInfo */Info); } TypeRefinementContext * diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 0421ce9f6670d..eccb36c12a394 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -14,10 +14,11 @@ // //===----------------------------------------------------------------------===// -#include "swift/AST/ASTPrinter.h" #include "swift/AST/TypeRepr.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/ASTWalker.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" @@ -74,10 +75,45 @@ SourceRange TypeRepr::getSourceRange() const { llvm_unreachable("unknown kind!"); } -/// Standard allocator for TypeReprs. -void *TypeRepr::operator new(size_t Bytes, const ASTContext &C, - unsigned Alignment) { - return C.Allocate(Bytes, Alignment); +bool TypeRepr::findIf(llvm::function_ref pred) { + struct Walker : ASTWalker { + llvm::function_ref Pred; + bool FoundIt; + + explicit Walker(llvm::function_ref pred) + : Pred(pred), FoundIt(false) {} + + bool walkToTypeReprPre(TypeRepr *ty) override { + // Returning false skips any child nodes. If we "found it", we can bail by + // returning false repeatedly back up the type tree. + return !(FoundIt || (FoundIt = Pred(ty))); + } + }; + + Walker walker(pred); + walk(walker); + return walker.FoundIt; +} + +// TODO [OPAQUE SUPPORT]: We should probably use something like `Type`'s +// `RecursiveProperties` to track this instead of computing it. +bool TypeRepr::hasOpaque() { + // TODO [OPAQUE SUPPORT]: In the future we will also need to check if `this` + // is a `NamedOpaqueReturnTypeRepr`. + return findIf([](TypeRepr *ty) { return isa(ty); }); +} + +SourceLoc TypeRepr::findUncheckedAttrLoc() const { + auto typeRepr = this; + while (auto attrTypeRepr = dyn_cast(typeRepr)) { + if (attrTypeRepr->getAttrs().has(TAK_unchecked)) { + return attrTypeRepr->getAttrs().getLoc(TAK_unchecked); + } + + typeRepr = attrTypeRepr->getTypeRepr(); + } + + return SourceLoc(); } DeclNameRef ComponentIdentTypeRepr::getNameRef() const { @@ -441,6 +477,25 @@ void OpaqueReturnTypeRepr::printImpl(ASTPrinter &Printer, printTypeRepr(Constraint, Printer, Opts); } +SourceLoc NamedOpaqueReturnTypeRepr::getStartLocImpl() const { + return GenericParams->getLAngleLoc(); +} + +SourceLoc NamedOpaqueReturnTypeRepr::getEndLocImpl() const { + return Base->getEndLoc(); +} + +SourceLoc NamedOpaqueReturnTypeRepr::getLocImpl() const { + return Base->getLoc(); +} + +void NamedOpaqueReturnTypeRepr::printImpl(ASTPrinter &Printer, + const PrintOptions &Opts) const { + GenericParams->print(Printer, Opts); + Printer << ' '; + printTypeRepr(Base, Printer, Opts); +} + void SpecifierTypeRepr::printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const { switch (getKind()) { diff --git a/lib/AST/USRGeneration.cpp b/lib/AST/USRGeneration.cpp index c3fe2322c21c9..5a02175895e8f 100644 --- a/lib/AST/USRGeneration.cpp +++ b/lib/AST/USRGeneration.cpp @@ -19,6 +19,7 @@ #include "swift/AST/SwiftNameTranslation.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/USRGeneration.h" +#include "swift/Demangling/Demangler.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -255,6 +256,18 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator, return NewMangler.mangleDeclAsUSR(D, getUSRSpacePrefix()); } +std::string ide::demangleUSR(StringRef mangled) { + if (mangled.startswith(getUSRSpacePrefix())) { + mangled = mangled.substr(getUSRSpacePrefix().size()); + } + SmallString<128> buffer; + buffer += "$s"; + buffer += mangled; + mangled = buffer.str(); + Demangler Dem; + return nodeToString(Dem.demangleSymbol(mangled)); +} + std::string swift::MangleLocalTypeDeclRequest::evaluate(Evaluator &evaluator, const TypeDecl *D) const { diff --git a/lib/ASTSectionImporter/CMakeLists.txt b/lib/ASTSectionImporter/CMakeLists.txt index 1a8b262e2a2c0..3fb5a1efc3e45 100644 --- a/lib/ASTSectionImporter/CMakeLists.txt +++ b/lib/ASTSectionImporter/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftASTSectionImporter STATIC ASTSectionImporter.cpp @@ -6,3 +6,4 @@ add_swift_host_library(swiftASTSectionImporter STATIC target_link_libraries(swiftASTSectionImporter PRIVATE swiftBasic) +set_swift_llvm_is_available(swiftASTSectionImporter) diff --git a/lib/Basic/LangOptions.cpp b/lib/Basic/LangOptions.cpp index 041d6a637b160..2af9d4fc39a1a 100644 --- a/lib/Basic/LangOptions.cpp +++ b/lib/Basic/LangOptions.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "swift/Basic/LangOptions.h" +#include "swift/AST/DiagnosticEngine.h" #include "swift/Basic/Feature.h" #include "swift/Basic/Platform.h" #include "swift/Basic/Range.h" @@ -390,4 +391,20 @@ llvm::StringRef swift::getFeatureName(Feature feature) { case Feature::FeatureName: return #FeatureName; #include "swift/Basic/Features.def" } + llvm_unreachable("covered switch"); +} + +DiagnosticBehavior LangOptions::getAccessNoteFailureLimit() const { + switch (AccessNoteBehavior) { + case AccessNoteDiagnosticBehavior::Ignore: + return DiagnosticBehavior::Ignore; + + case AccessNoteDiagnosticBehavior::RemarkOnFailure: + case AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess: + return DiagnosticBehavior::Remark; + + case AccessNoteDiagnosticBehavior::ErrorOnFailureRemarkOnSuccess: + return DiagnosticBehavior::Error; + } + llvm_unreachable("covered switch"); } diff --git a/lib/Basic/Mangler.cpp b/lib/Basic/Mangler.cpp index 4f92c1787cafe..4fbb996f66c71 100644 --- a/lib/Basic/Mangler.cpp +++ b/lib/Basic/Mangler.cpp @@ -223,13 +223,14 @@ void Mangler::mangleSubstitution(unsigned Idx) { return appendOperator("A", Index(Idx - 26)); } - char Subst = Idx + 'A'; + char SubstChar = Idx + 'A'; + StringRef Subst(&SubstChar, 1); if (SubstMerging.tryMergeSubst(*this, Subst, /*isStandardSubst*/ false)) { #ifndef NDEBUG ++mergedSubsts; #endif } else { - appendOperator("A", StringRef(&Subst, 1)); + appendOperator("A", Subst); } } diff --git a/lib/Basic/SourceLoc.cpp b/lib/Basic/SourceLoc.cpp index 9d7ac10034fe9..ccbcc4bdb1663 100644 --- a/lib/Basic/SourceLoc.cpp +++ b/lib/Basic/SourceLoc.cpp @@ -270,6 +270,15 @@ void SourceRange::widen(SourceRange Other) { End = Other.End; } +bool SourceRange::contains(SourceLoc Loc) const { + return Start.Value.getPointer() <= Loc.Value.getPointer() && + Loc.Value.getPointer() <= End.Value.getPointer(); +} + +bool SourceRange::overlaps(SourceRange Other) const { + return contains(Other.Start) || Other.contains(Start); +} + void SourceLoc::printLineAndColumn(raw_ostream &OS, const SourceManager &SM, unsigned BufferID) const { if (isInvalid()) { diff --git a/lib/Basic/Statistic.cpp b/lib/Basic/Statistic.cpp index 979036ce2ab1b..b44fb5ec9d3d2 100644 --- a/lib/Basic/Statistic.cpp +++ b/lib/Basic/Statistic.cpp @@ -10,19 +10,17 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/Decl.h" +#include "swift/Basic/Statistic.h" +#include "swift/Config.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" -#include "swift/Basic/Statistic.h" -#include "swift/AST/Decl.h" -#include "swift/AST/Expr.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Config/config.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/raw_ostream.h" #include #include @@ -59,6 +57,16 @@ bool environmentVariableRequestedMaximumDeterminism() { return false; } +uint64_t getInstructionsExecuted() { +#if defined(HAVE_PROC_PID_RUSAGE) && defined(RUSAGE_INFO_V4) + struct rusage_info_v4 ru; + if (proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&ru) == 0) { + return ru.ri_instructions; + } +#endif + return 0; +} + static std::string makeFileName(StringRef Prefix, StringRef ProgramName, @@ -510,12 +518,9 @@ FrontendStatsTracer::~FrontendStatsTracer() // associated fields in the provided AlwaysOnFrontendCounters. void updateProcessWideFrontendCounters( UnifiedStatsReporter::AlwaysOnFrontendCounters &C) { -#if defined(HAVE_PROC_PID_RUSAGE) && defined(RUSAGE_INFO_V4) - struct rusage_info_v4 ru; - if (0 == proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&ru)) { - C.NumInstructionsExecuted = ru.ri_instructions; + if (auto instrExecuted = getInstructionsExecuted()) { + C.NumInstructionsExecuted = instrExecuted; } -#endif #if defined(HAVE_MALLOC_ZONE_STATISTICS) && defined(HAVE_MALLOC_MALLOC_H) // On Darwin we have a lifetime max that's maintained by malloc we can diff --git a/lib/Basic/Version.cpp b/lib/Basic/Version.cpp index 6ab89f46fd310..65d83778aa64d 100644 --- a/lib/Basic/Version.cpp +++ b/lib/Basic/Version.cpp @@ -326,6 +326,17 @@ Optional Version::getEffectiveLanguageVersion() const { static_assert(SWIFT_VERSION_MAJOR == 5, "getCurrentLanguageVersion is no longer correct here"); return Version::getCurrentLanguageVersion(); + case 6: + // Allow version '6' in asserts compilers *only* so that we can start + // testing changes slated for Swift 6. Note that it's still not listed in + // `Version::getValidEffectiveVersions()`. + // FIXME: When Swift 6 becomes real, remove 'REQUIRES: asserts' from tests + // using '-swift-version 6'. +#ifdef NDEBUG + LLVM_FALLTHROUGH; +#else + return Version{6}; +#endif default: return None; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 69d6d1a3bcd7b..6a3ea1e38ffec 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(ClangImporter) add_subdirectory(Demangling) add_subdirectory(DependencyScan) add_subdirectory(Driver) +add_subdirectory(DriverTool) add_subdirectory(Frontend) add_subdirectory(FrontendTool) add_subdirectory(Index) diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index 75d27036464e4..252cededbd035 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -2,9 +2,8 @@ set(SWIFT_GYB_FLAGS "-DCFDatabaseFile=${SWIFT_SOURCE_DIR}/lib/ClangImporter/CFDatabase.def") add_gyb_target(generated_sorted_cf_database - SortedCFDatabase.def.gyb) - -set_swift_llvm_is_available() + SortedCFDatabase.def.gyb + DEPENDS "${SWIFT_SOURCE_DIR}/lib/ClangImporter/CFDatabase.def") add_swift_host_library(swiftClangImporter STATIC CFTypeInfo.cpp @@ -37,3 +36,5 @@ get_property(CLANG_TABLEGEN_TARGETS GLOBAL PROPERTY CLANG_TABLEGEN_TARGETS) add_dependencies(swiftClangImporter generated_sorted_cf_database ${CLANG_TABLEGEN_TARGETS}) + +set_swift_llvm_is_available(swiftClangImporter) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 2c968fdea1947..1e58c36510ecc 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -63,6 +63,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/FileCollector.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" #include "llvm/Support/YAMLTraits.h" @@ -730,7 +731,11 @@ importer::addCommonInvocationArguments( std::vector &invocationArgStrs, ASTContext &ctx) { using ImporterImpl = ClangImporter::Implementation; - const llvm::Triple &triple = ctx.LangOpts.Target; + llvm::Triple triple = ctx.LangOpts.Target; + // Use clang specific target triple if given. + if (ctx.LangOpts.ClangTarget.hasValue()) { + triple = ctx.LangOpts.ClangTarget.getValue(); + } SearchPathOptions &searchPathOpts = ctx.SearchPathOpts; const ClangImporterOptions &importerOpts = ctx.ClangImporterOpts; @@ -967,11 +972,11 @@ ClangImporter::getClangArguments(ASTContext &ctx) { return invocationArgStrs; } -std::unique_ptr -ClangImporter::createClangInvocation(ClangImporter *importer, - const ClangImporterOptions &importerOpts, - ArrayRef invocationArgStrs, - std::vector *CC1Args) { +std::unique_ptr ClangImporter::createClangInvocation( + ClangImporter *importer, const ClangImporterOptions &importerOpts, + llvm::IntrusiveRefCntPtr VFS, + ArrayRef invocationArgStrs, + std::vector *CC1Args) { std::vector invocationArgs; invocationArgs.reserve(invocationArgStrs.size()); for (auto &argStr : invocationArgStrs) @@ -996,7 +1001,7 @@ ClangImporter::createClangInvocation(ClangImporter *importer, /*owned*/false); auto CI = clang::createInvocationFromCommandLine( - invocationArgs, tempClangDiags, nullptr, false, CC1Args); + invocationArgs, tempClangDiags, VFS, false, CC1Args); if (!CI) { return CI; @@ -1012,12 +1017,12 @@ ClangImporter::createClangInvocation(ClangImporter *importer, // to missing files and report the error that clang would throw manually. // rdar://77516546 is tracking that the clang importer should be more // resilient and provide a module even if there were building it. - auto VFS = clang::createVFSFromCompilerInvocation( + auto TempVFS = clang::createVFSFromCompilerInvocation( *CI, *tempClangDiags, - importer->Impl.SwiftContext.SourceMgr.getFileSystem()); + VFS ? VFS : importer->Impl.SwiftContext.SourceMgr.getFileSystem()); std::vector FilteredModuleMapFiles; for (auto ModuleMapFile : CI->getFrontendOpts().ModuleMapFiles) { - if (VFS->exists(ModuleMapFile)) { + if (TempVFS->exists(ModuleMapFile)) { FilteredModuleMapFiles.push_back(ModuleMapFile); } else { importer->Impl.diagnose(SourceLoc(), diag::module_map_not_found, @@ -1062,11 +1067,15 @@ ClangImporter::create(ASTContext &ctx, } } + // Wrap Swift's FS to allow Clang to override the working directory + llvm::IntrusiveRefCntPtr VFS = + llvm::vfs::RedirectingFileSystem::create({}, true, + *ctx.SourceMgr.getFileSystem()); + // Create a new Clang compiler invocation. { - importer->Impl.Invocation = createClangInvocation(importer.get(), - importerOpts, - invocationArgStrs); + importer->Impl.Invocation = createClangInvocation( + importer.get(), importerOpts, VFS, invocationArgStrs); if (!importer->Impl.Invocation) return nullptr; } @@ -1118,11 +1127,9 @@ ClangImporter::create(ASTContext &ctx, // Set up the file manager. { - llvm::IntrusiveRefCntPtr VFS = - clang::createVFSFromCompilerInvocation(instance.getInvocation(), - instance.getDiagnostics(), - ctx.SourceMgr.getFileSystem()); - instance.createFileManager(std::move(VFS)); + VFS = clang::createVFSFromCompilerInvocation( + instance.getInvocation(), instance.getDiagnostics(), std::move(VFS)); + instance.createFileManager(VFS); } // Don't stop emitting messages if we ever can't load a module. @@ -2863,6 +2870,11 @@ void ClangImporter::lookupTypeDecl( continue; } auto *imported = Impl.importDecl(clangDecl, Impl.CurrentVersion); + + // Namespaces are imported as extensions for enums. + if (auto ext = dyn_cast_or_null(imported)) { + imported = ext->getExtendedNominal(); + } if (auto *importedType = dyn_cast_or_null(imported)) { foundViaClang = true; receiver(importedType); @@ -3829,11 +3841,22 @@ void ClangImporter::Implementation::lookupValue( // If the entry is not visible, skip it. if (!isVisibleClangEntry(entry)) continue; - ValueDecl *decl; + ValueDecl *decl = nullptr; // If it's a Clang declaration, try to import it. if (auto clangDecl = entry.dyn_cast()) { - decl = cast_or_null( - importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion)); + bool isNamespace = isa(clangDecl); + Decl *realDecl = + importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion, + /*useCanonicalDecl*/ !isNamespace); + + if (isNamespace) { + if (auto extension = cast_or_null(realDecl)) + realDecl = extension->getExtendedNominal(); + } + + if (!realDecl) + continue; + decl = cast(realDecl); if (!decl) continue; } else if (!name.isSpecial()) { // Try to import a macro. @@ -4299,3 +4322,8 @@ ClangImporter::instantiateCXXClassTemplate( return dyn_cast_or_null( Impl.importDecl(ctsd, Impl.CurrentVersion)); } + +bool ClangImporter::isCXXMethodMutating(const clang::CXXMethodDecl *method) { + return isa(method) || !method->isConst() || + method->getParent()->hasMutableFields(); +} diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index ae520d3116b32..ee8458fba819e 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -197,11 +197,14 @@ void ClangImporter::recordModuleDependencies( std::string PCMPath; std::string ModuleMapPath; }; + auto &ctx = Impl.SwiftContext; + auto currentSwiftSearchPathSet = ctx.getAllModuleSearchPathsSet(); for (const auto &clangModuleDep : clangDependencies.DiscoveredModules) { // If we've already cached this information, we're done. - if (cache.hasDependencies(clangModuleDep.ModuleName, - ModuleDependenciesKind::Clang)) + if (cache.hasDependencies( + clangModuleDep.ModuleName, + {ModuleDependenciesKind::Clang, currentSwiftSearchPathSet})) continue; // File dependencies for this module. @@ -215,7 +218,7 @@ void ClangImporter::recordModuleDependencies( // Ensure the arguments we collected is sufficient to create a Clang // invocation. - assert(createClangInvocation(this, Opts, allArgs)); + assert(createClangInvocation(this, Opts, nullptr, allArgs)); std::vector swiftArgs; // We are using Swift frontend mode. @@ -232,6 +235,7 @@ void ClangImporter::recordModuleDependencies( while(It != allArgs.end()) { StringRef arg = *It; // Remove the -target arguments because we should use the target triple + // specified with `-clang-target` on the scanner invocation, or // from the depending Swift modules. if (arg == "-target") { It += 2; @@ -254,6 +258,16 @@ void ClangImporter::recordModuleDependencies( swiftArgs.push_back(clangArg); } + // If the scanner is invoked with '-clang-target', ensure this is the target + // used to build this PCM. + if (Impl.SwiftContext.LangOpts.ClangTarget.hasValue()) { + llvm::Triple triple = Impl.SwiftContext.LangOpts.ClangTarget.getValue(); + swiftArgs.push_back("-Xcc"); + swiftArgs.push_back("-target"); + swiftArgs.push_back("-Xcc"); + swiftArgs.push_back(triple.str()); + } + // Swift frontend action: -emit-pcm swiftArgs.push_back("-emit-pcm"); swiftArgs.push_back("-module-name"); @@ -292,9 +306,12 @@ void ClangImporter::recordModuleDependencies( Optional ClangImporter::getModuleDependencies( StringRef moduleName, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate) { + auto &ctx = Impl.SwiftContext; + auto currentSwiftSearchPathSet = ctx.getAllModuleSearchPathsSet(); // Check whether there is already a cached result. if (auto found = cache.findDependencies( - moduleName, ModuleDependenciesKind::Clang)) + moduleName, + {ModuleDependenciesKind::Clang, currentSwiftSearchPathSet})) return found; // Retrieve or create the shared state. @@ -308,7 +325,7 @@ Optional ClangImporter::getModuleDependencies( } // Determine the command-line arguments for dependency scanning. - auto &ctx = Impl.SwiftContext; + std::vector commandLineArgs = getClangDepScanningInvocationArguments(ctx, *importHackFile); @@ -327,14 +344,19 @@ Optional ClangImporter::getModuleDependencies( // Record module dependencies for each module we found. recordModuleDependencies(cache, *clangDependencies); - return cache.findDependencies(moduleName, ModuleDependenciesKind::Clang); + return cache.findDependencies( + moduleName, + {ModuleDependenciesKind::Clang, currentSwiftSearchPathSet}); } bool ClangImporter::addBridgingHeaderDependencies( StringRef moduleName, ModuleDependenciesCache &cache) { + auto &ctx = Impl.SwiftContext; + auto currentSwiftSearchPathSet = ctx.getAllModuleSearchPathsSet(); auto targetModule = *cache.findDependencies( - moduleName, ModuleDependenciesKind::SwiftTextual); + moduleName, + {ModuleDependenciesKind::SwiftTextual,currentSwiftSearchPathSet}); // If we've already recorded bridging header dependencies, we're done. auto swiftDeps = targetModule.getAsSwiftTextualModule(); @@ -349,7 +371,6 @@ bool ClangImporter::addBridgingHeaderDependencies( std::string bridgingHeader = *targetModule.getBridgingHeader(); // Determine the command-line arguments for dependency scanning. - auto &ctx = Impl.SwiftContext; std::vector commandLineArgs = getClangDepScanningInvocationArguments(ctx, bridgingHeader); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 8639e2f342bd5..5b91a36f1a9e9 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -771,9 +771,8 @@ static VarDecl *findAnonymousInnerFieldDecl(VarDecl *importedFieldDecl, auto anonymousFieldTypeDecl = anonymousFieldType->getStructOrBoundGenericStruct(); - const auto flags = NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; for (auto decl : anonymousFieldTypeDecl->lookupDirect( - importedFieldDecl->getName(), flags)) { + importedFieldDecl->getName())) { if (isa(decl)) { return cast(decl); } @@ -1279,7 +1278,7 @@ synthesizeStructDefaultConstructorBody(AbstractFunctionDecl *afd, FunctionType::ExtInfo info; zeroInitializerRef->setType(FunctionType::get({}, selfType, info)); - auto call = CallExpr::createImplicit(ctx, zeroInitializerRef, {}, {}); + auto call = CallExpr::createImplicitEmpty(ctx, zeroInitializerRef); call->setType(selfType); call->setThrows(false); @@ -1344,8 +1343,7 @@ synthesizeValueConstructorBody(AbstractFunctionDecl *afd, void *context) { for (unsigned i = 0, e = members.size(); i < e; ++i) { auto var = members[i]; - if (var->hasClangNode() && - isa(var->getClangDecl())) + if (isa_and_nonnull(var->getClangDecl())) continue; if (var->hasStorage() == (pass != 0)) { @@ -1399,6 +1397,9 @@ createValueConstructor(ClangImporter::Implementation &Impl, // Construct the set of parameters from the list of members. SmallVector valueParameters; for (auto var : members) { + if (var->isStatic()) + continue; + bool generateParamName = wantCtorParamNames; if (var->hasClangNode()) { @@ -1843,6 +1844,7 @@ static void applyAvailableAttribute(Decl *decl, AvailabilityContext &info, targetPlatform(C.LangOpts), /*Message=*/StringRef(), /*Rename=*/StringRef(), + /*RenameDecl=*/nullptr, info.getOSVersion().getLowerEndpoint(), /*IntroducedRange*/SourceRange(), /*Deprecated=*/noVersion, @@ -2303,20 +2305,19 @@ namespace { /// name importing options (e.g., if we're importing the Swift 2 version). /// /// Note: Use this rather than calling Impl.importFullName directly! - ImportedName importFullName(const clang::NamedDecl *D, - Optional &correctSwiftName) { + std::pair> + importFullName(const clang::NamedDecl *D) { ImportNameVersion canonicalVersion = getActiveSwiftVersion(); if (isa(D) || isa(D)) { canonicalVersion = ImportNameVersion::forTypes(); } - correctSwiftName = None; // First, import based on the Swift name of the canonical declaration: // the latest version for types and the current version for non-type // values. If that fails, we won't do anything. auto canonicalName = Impl.importFullName(D, canonicalVersion); if (!canonicalName) - return ImportedName(); + return {ImportedName(), None}; if (getVersion() == canonicalVersion) { // Make sure we don't try to import the same type twice as canonical. @@ -2326,11 +2327,11 @@ namespace { activeName.getDeclName() == canonicalName.getDeclName() && activeName.getEffectiveContext().equalsWithoutResolving( canonicalName.getEffectiveContext())) { - return ImportedName(); + return {ImportedName(), None}; } } - return canonicalName; + return {canonicalName, None}; } // Special handling when we import using the alternate Swift name. @@ -2340,7 +2341,7 @@ namespace { // Swift name stub declaration. auto alternateName = Impl.importFullName(D, getVersion()); if (!alternateName) - return ImportedName(); + return {ImportedName(), None}; // Importing for concurrency is special in that the same declaration // is imported both with a completion handler parameter and as 'async', @@ -2349,10 +2350,10 @@ namespace { // If the resulting name isn't special for concurrency, it's not // different. if (!alternateName.getAsyncInfo()) - return ImportedName(); + return {ImportedName(), None}; // Otherwise, it's a legitimately different import. - return alternateName; + return {alternateName, None}; } if (alternateName.getDeclName() == canonicalName.getDeclName() && @@ -2360,17 +2361,18 @@ namespace { canonicalName.getEffectiveContext())) { if (getVersion() == getActiveSwiftVersion()) { assert(canonicalVersion != getActiveSwiftVersion()); - return alternateName; + return {alternateName, None}; } - return ImportedName(); + return {ImportedName(), None}; } // Always use the active version as the preferred name, even if the // canonical name is a different version. - correctSwiftName = Impl.importFullName(D, getActiveSwiftVersion()); + ImportedName correctSwiftName = + Impl.importFullName(D, getActiveSwiftVersion()); assert(correctSwiftName); - return alternateName; + return {alternateName, correctSwiftName}; } /// Create a declaration name for anonymous enums, unions and @@ -2381,21 +2383,22 @@ namespace { /// is derived from the name of the field in the outer type. Since the /// anonymous type is imported as a nested type of the outer type, this /// generated name will most likely be unique. - ImportedName getClangDeclName(const clang::TagDecl *decl, - Optional &correctSwiftName) { + std::pair> + getClangDeclName(const clang::TagDecl *decl) { // If we have a name for this declaration, use it. - if (auto name = importFullName(decl, correctSwiftName)) - return name; + auto result = importFullName(decl); + if (result.first) + return result; // If that didn't succeed, check whether this is an anonymous tag declaration // with a corresponding typedef-name declaration. if (decl->getDeclName().isEmpty()) { if (auto *typedefForAnon = decl->getTypedefNameForAnonDecl()) - return importFullName(typedefForAnon, correctSwiftName); + return importFullName(typedefForAnon); } if (!decl->isRecord()) - return ImportedName(); + return {ImportedName(), None}; // If the type has no name and no structure name, but is not anonymous, // generate a name for it. Specifically this is for cases like: @@ -2404,7 +2407,6 @@ namespace { // } // Where the member z is an unnamed struct, but does have a member-name // and is accessible as a member of struct a. - correctSwiftName = None; if (auto recordDecl = dyn_cast( decl->getLexicalDeclContext())) { for (auto field : recordDecl->fields()) { @@ -2432,12 +2434,12 @@ namespace { ImportedName Result; Result.setDeclName(Impl.SwiftContext.getIdentifier(IdStream.str())); Result.setEffectiveContext(decl->getDeclContext()); - return Result; + return {Result, None}; } } } - return ImportedName(); + return {ImportedName(), None}; } bool isFactoryInit(ImportedName &name) { @@ -2466,23 +2468,12 @@ namespace { } Decl *VisitNamespaceDecl(const clang::NamespaceDecl *decl) { - // If we have a name for this declaration, use it. - Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); - if (!importedName) return nullptr; - - auto extensionDC = - Impl.importDeclContextOf(decl, importedName.getEffectiveContext()); - if (!extensionDC) - return nullptr; - - SourceLoc loc = Impl.importSourceLoc(decl->getBeginLoc()); DeclContext *dc = nullptr; // If this is a top-level namespace, don't put it in the module we're // importing, put it in the "__ObjC" module that is implicitly imported. // This way, if we have multiple modules that all open the same namespace, // we won't import multiple enums with the same name in swift. - if (extensionDC->getContextKind() == DeclContextKind::FileUnit) + if (!decl->getParent()->isNamespace()) dc = Impl.ImportedHeaderUnit; else { // This is a nested namespace, we need to find its extension decl @@ -2490,49 +2481,85 @@ namespace { // that we add this to the parent enum (in the "__ObjC" module) and not // to the extension. auto parentNS = cast(decl->getParent()); - auto parent = Impl.importDecl(parentNS, getVersion()); + auto parent = + Impl.importDecl(parentNS, getVersion(), /*UseCanonicalDecl*/ false); // Sometimes when the parent namespace is imported, this namespace // also gets imported. If that's the case, then the parent namespace // will be an enum (because it was able to be fully imported) in which // case we need to bail here. - auto cachedResult = - Impl.ImportedDecls.find({decl->getCanonicalDecl(), getVersion()}); + auto cachedResult = Impl.ImportedDecls.find({decl, getVersion()}); if (cachedResult != Impl.ImportedDecls.end()) return cachedResult->second; dc = cast(parent) ->getExtendedType() ->getEnumOrBoundGenericEnum(); } - auto *enumDecl = Impl.createDeclWithClangNode( - decl, AccessLevel::Public, loc, - importedName.getDeclName().getBaseIdentifier(), - Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc); - if (isa(decl->getParent())) - cast(dc)->addMember(enumDecl); - - // We are creating an extension, so put it at the top level. This is done - // after creating the enum, though, because we may need the correctly - // nested decl context above when creating the enum. - while (extensionDC->getParent() && - extensionDC->getContextKind() != DeclContextKind::FileUnit) - extensionDC = extensionDC->getParent(); - - auto *extension = ExtensionDecl::create(Impl.SwiftContext, loc, nullptr, - {}, extensionDC, nullptr, decl); - Impl.SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{extension}, - enumDecl->getDeclaredType()); - Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{extension}, - std::move(enumDecl)); - // Keep track of what members we've already added so we don't add the same - // member twice. Note: we can't use "ImportedDecls" for this because we - // might import a decl that we don't add (for example, if it was a - // parameter to another decl). - SmallPtrSet addedMembers; + + EnumDecl *enumDecl = nullptr; + // Try to find an already created enum for this namespace. + for (auto redecl : decl->redecls()) { + auto extension = Impl.ImportedDecls.find({redecl, getVersion()}); + if (extension != Impl.ImportedDecls.end()) { + enumDecl = cast( + cast(extension->second)->getExtendedNominal()); + break; + } + } + // If we're seeing this namespace for the first time, we need to create a + // new enum in the "__ObjC" module. + if (!enumDecl) { + // If we don't have a name for this declaration, bail. + ImportedName importedName; + std::tie(importedName, std::ignore) = importFullName(decl); + if (!importedName) + return nullptr; + + enumDecl = Impl.createDeclWithClangNode( + decl, AccessLevel::Public, + Impl.importSourceLoc(decl->getBeginLoc()), + importedName.getDeclName().getBaseIdentifier(), + Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc); + if (isa(decl->getParent())) + cast(dc)->addMember(enumDecl); + } + for (auto redecl : decl->redecls()) { - // This will be reset as the EnumDecl after we return from - // VisitNamespaceDecl. - Impl.ImportedDecls[{redecl->getCanonicalDecl(), getVersion()}] = - extension; + if (Impl.ImportedDecls.find({redecl, getVersion()}) != + Impl.ImportedDecls.end()) + continue; + + ImportedName importedName; + std::tie(importedName, std::ignore) = importFullName(redecl); + if (!importedName) + continue; + + auto extensionDC = Impl.importDeclContextOf( + redecl, importedName.getEffectiveContext()); + if (!extensionDC) + continue; + + // We are creating an extension, so put it at the top level. This is + // done after creating the enum, though, because we may need the + // correctly nested decl context above when creating the enum. + while (extensionDC->getParent() && + extensionDC->getContextKind() != DeclContextKind::FileUnit) + extensionDC = extensionDC->getParent(); + + auto *extension = ExtensionDecl::create( + Impl.SwiftContext, Impl.importSourceLoc(decl->getBeginLoc()), + nullptr, {}, extensionDC, nullptr, redecl); + enumDecl->addExtension(extension); + Impl.ImportedDecls[{redecl, getVersion()}] = extension; + + Impl.SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{extension}, + enumDecl->getDeclaredType()); + Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{extension}, + std::move(enumDecl)); + // Keep track of what members we've already added so we don't add the + // same member twice. Note: we can't use "ImportedDecls" for this + // because we might import a decl that we don't add (for example, if it + // was a parameter to another decl). + SmallPtrSet addedMembers; // Insert these backwards into "namespaceDecls" so we can pop them off // the end without loosing order. @@ -2563,7 +2590,8 @@ namespace { continue; } - auto member = Impl.importDecl(nd, getVersion()); + bool useCanonicalDecl = !isa(nd); + auto member = Impl.importDecl(nd, getVersion(), useCanonicalDecl); if (!member || addedMembers.count(member) || isa(nd)) continue; @@ -2578,10 +2606,7 @@ namespace { } } - if (!extension->getMembers().empty()) - enumDecl->addExtension(extension); - - return enumDecl; + return Impl.ImportedDecls[{decl, getVersion()}]; } Decl *VisitUsingDirectiveDecl(const clang::UsingDirectiveDecl *decl) { @@ -2590,8 +2615,43 @@ namespace { } Decl *VisitNamespaceAliasDecl(const clang::NamespaceAliasDecl *decl) { - // FIXME: Implement once Swift has namespaces. - return nullptr; + ImportedName importedName; + Optional correctSwiftName; + std::tie(importedName, correctSwiftName) = importFullName(decl); + auto name = importedName.getDeclName().getBaseIdentifier(); + if (name.empty()) + return nullptr; + + if (correctSwiftName) + return importCompatibilityTypeAlias(decl, importedName, + *correctSwiftName); + + auto dc = + Impl.importDeclContextOf(decl, importedName.getEffectiveContext()); + if (!dc) + return nullptr; + + auto aliasedDecl = + Impl.importDecl(decl->getAliasedNamespace(), getActiveSwiftVersion()); + if (!aliasedDecl) + return nullptr; + + Type aliasedType; + if (auto aliasedTypeDecl = dyn_cast(aliasedDecl)) + aliasedType = aliasedTypeDecl->getDeclaredInterfaceType(); + else if (auto aliasedExtDecl = dyn_cast(aliasedDecl)) + // This happens if the alias points to its parent namespace. + aliasedType = aliasedExtDecl->getExtendedType(); + else + return nullptr; + + auto result = Impl.createDeclWithClangNode( + decl, AccessLevel::Public, Impl.importSourceLoc(decl->getBeginLoc()), + SourceLoc(), name, Impl.importSourceLoc(decl->getLocation()), + /*GenericParams=*/nullptr, dc); + result->setUnderlyingType(aliasedType); + + return result; } Decl *VisitLabelDecl(const clang::LabelDecl *decl) { @@ -2671,6 +2731,7 @@ namespace { attr = new (ctx) AvailableAttr( SourceLoc(), SourceRange(), PlatformKind::none, /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()), + /*RenameDecl=*/nullptr, /*Introduced*/introducedVersion, SourceRange(), /*Deprecated*/llvm::VersionTuple(), SourceRange(), /*Obsoleted*/llvm::VersionTuple(), SourceRange(), @@ -2696,8 +2757,9 @@ namespace { DeclContext *dc, Identifier name); Decl *VisitTypedefNameDecl(const clang::TypedefNameDecl *Decl) { + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(Decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(Decl); auto Name = importedName.getDeclName().getBaseIdentifier(); if (Name.empty()) return nullptr; @@ -2911,8 +2973,9 @@ namespace { if (Impl.isOverAligned(decl)) return nullptr; + ImportedName importedName; Optional correctSwiftName; - auto importedName = getClangDeclName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = getClangDeclName(decl); if (!importedName) return nullptr; @@ -3080,8 +3143,9 @@ namespace { Impl.importIdentifier(decl->getIdentifier())); // Add protocol declarations to the enum declaration. - SmallVector inheritedTypes; - inheritedTypes.push_back(TypeLoc::withoutLoc(underlyingType)); + SmallVector inheritedTypes; + inheritedTypes.push_back( + InheritedEntry(TypeLoc::withoutLoc(underlyingType))); enumDecl->setInherited(C.AllocateCopy(inheritedTypes)); if (errorWrapper) { @@ -3421,8 +3485,9 @@ namespace { } // Import the name. + ImportedName importedName; Optional correctSwiftName; - auto importedName = getClangDeclName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = getClangDeclName(decl); if (!importedName) return nullptr; @@ -3464,6 +3529,10 @@ namespace { SmallVector ctors; SmallVector nestedTypes; + // Store the already imported members in a set to avoid importing the same + // decls multiple times. + SmallPtrSet importedMembers; + // FIXME: Import anonymous union fields and support field access when // it is nested in a struct. @@ -3482,6 +3551,12 @@ namespace { } } + // If we've already imported this decl & added it to the resulting + // struct, skip it so we don't add the same member twice. + if (!importedMembers.insert(m->getCanonicalDecl()).second) { + continue; + } + auto nd = dyn_cast(m); if (!nd) { // We couldn't import the member, so we can't reference it in Swift. @@ -3510,11 +3585,19 @@ namespace { } } - // If we've already imported this decl, skip it so we don't add the same - // member twice. - if (Impl.ImportedDecls.find({nd->getCanonicalDecl(), getVersion()}) != - Impl.ImportedDecls.end()) - continue; + // If we encounter an IndirectFieldDecl, ensure that its parent is + // importable before attempting to import it because they are dependent + // when it comes to getter/setter generation. + if (const auto *ind = dyn_cast(nd)) { + const clang::CXXRecordDecl *parentUnion = + (ind->getChainingSize() >= 2) + ? dyn_cast( + ind->getAnonField()->getParent()) + : nullptr; + if (parentUnion && parentUnion->isAnonymousStructOrUnion() && + parentUnion->isUnion() && !isCxxRecordImportable(parentUnion)) + continue; + } auto member = Impl.importDecl(nd, getActiveSwiftVersion()); if (!member) { @@ -3807,8 +3890,9 @@ namespace { Decl *VisitEnumConstantDecl(const clang::EnumConstantDecl *decl) { auto clangEnum = cast(decl->getDeclContext()); + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); if (!importedName) return nullptr; auto name = importedName.getDeclName().getBaseIdentifier(); @@ -3876,8 +3960,9 @@ namespace { } Decl *VisitIndirectFieldDecl(const clang::IndirectFieldDecl *decl) { + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); if (!importedName) return nullptr; auto name = importedName.getDeclName().getBaseIdentifier(); @@ -3952,8 +4037,9 @@ namespace { Decl *VisitFunctionDecl(const clang::FunctionDecl *decl) { // Import the name of the function. + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); if (!importedName) return nullptr; @@ -4098,10 +4184,10 @@ namespace { selfIdx = None; } else { selfIdx = 0; - // Workaround until proper const support is handled: Force - // everything to be mutating. This implicitly makes the parameter - // indirect. - selfIsInOut = true; + // If the method is imported as mutating, this implicitly makes the + // parameter indirect. + selfIsInOut = Impl.SwiftContext.getClangModuleLoader() + ->isCXXMethodMutating(mdecl); } } } @@ -4242,7 +4328,7 @@ namespace { ImportedName importedName; if (!decl->isAnonymousStructOrUnion() && !decl->getDeclName().isEmpty()) { - importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); if (!importedName) { return nullptr; } @@ -4358,8 +4444,9 @@ namespace { Decl *VisitVarDecl(const clang::VarDecl *decl) { // Variables are imported as... variables. + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); if (!importedName) return nullptr; auto name = importedName.getDeclName().getBaseIdentifier(); @@ -4453,9 +4540,10 @@ namespace { } Decl *VisitFunctionTemplateDecl(const clang::FunctionTemplateDecl *decl) { + ImportedName importedName; Optional correctSwiftName; - auto importedName = - importFullName(decl->getAsFunction(), correctSwiftName); + std::tie(importedName, correctSwiftName) = + importFullName(decl->getAsFunction()); if (!importedName) return nullptr; // All template parameters must be template type parameters. @@ -4478,8 +4566,8 @@ namespace { } } - Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + ImportedName importedName; + std::tie(importedName, std::ignore) = importFullName(decl); auto name = importedName.getDeclName().getBaseIdentifier(); if (name.empty()) return nullptr; @@ -4514,8 +4602,9 @@ namespace { if (!isa(decl->getUnderlyingDecl())) return nullptr; + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); auto Name = importedName.getDeclName().getBaseIdentifier(); if (Name.empty()) return nullptr; @@ -4596,8 +4685,7 @@ namespace { return Known; ImportedName importedName; - Optional correctSwiftName; // TODO: not sure if we need this. - importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, std::ignore) = importFullName(decl); if (!importedName) return nullptr; @@ -4797,7 +4885,7 @@ namespace { ImportedName importedName; Optional correctSwiftName; - importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); if (!importedName) return nullptr; @@ -5140,7 +5228,7 @@ namespace { // declaration. void importObjCProtocols(Decl *decl, const clang::ObjCProtocolList &clangProtocols, - SmallVectorImpl &inheritedTypes); + SmallVectorImpl &inheritedTypes); // Returns None on error. Returns nullptr if there is no type param list to // import or we suppress its import, as in the case of NSArray, NSSet, and @@ -5210,7 +5298,7 @@ namespace { // Create the extension declaration and record it. objcClass->addExtension(result); Impl.ImportedDecls[{decl, getVersion()}] = result; - SmallVector inheritedTypes; + SmallVector inheritedTypes; importObjCProtocols(result, decl->getReferencedProtocols(), inheritedTypes); result->setInherited(Impl.SwiftContext.AllocateCopy(inheritedTypes)); @@ -5362,8 +5450,9 @@ namespace { } Decl *VisitObjCProtocolDecl(const clang::ObjCProtocolDecl *decl) { + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); if (!importedName) return nullptr; // If we've been asked to produce a compatibility stub, handle it via a @@ -5416,7 +5505,7 @@ namespace { Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; // Import protocols this protocol conforms to. - SmallVector inheritedTypes; + SmallVector inheritedTypes; importObjCProtocols(result, decl->getReferencedProtocols(), inheritedTypes); result->setInherited(Impl.SwiftContext.AllocateCopy(inheritedTypes)); @@ -5480,8 +5569,9 @@ namespace { if (auto *definition = decl->getDefinition()) decl = definition; + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(decl); if (!importedName) return nullptr; // If we've been asked to produce a compatibility stub, handle it via a @@ -5566,7 +5656,7 @@ namespace { } // If this Objective-C class has a supertype, import it. - SmallVector inheritedTypes; + SmallVector inheritedTypes; Type superclassType; if (decl->getSuperClass()) { clang::QualType clangSuperclassType = @@ -5680,10 +5770,10 @@ namespace { DeclContext *dc) { assert(dc); + ImportedName importedName; Optional correctSwiftName; - auto name = importFullName(decl, correctSwiftName) - .getDeclName() - .getBaseIdentifier(); + std::tie(importedName, correctSwiftName) = importFullName(decl); + auto name = importedName.getDeclName().getBaseIdentifier(); if (name.empty()) return nullptr; @@ -5817,8 +5907,8 @@ namespace { auto dc = Impl.importDeclContextOf(decl, effectiveContext); if (!dc) return nullptr; - Optional correctSwiftName; - auto importedName = importFullName(decl, correctSwiftName); + ImportedName importedName; + std::tie(importedName, std::ignore) = importFullName(decl); auto name = importedName.getDeclName().getBaseIdentifier(); if (name.empty()) return nullptr; @@ -6007,7 +6097,7 @@ SwiftDeclConverter::importCFClassType(const clang::TypedefNameDecl *decl, addObjCAttribute(theClass, None); if (superclass) { - SmallVector inheritedTypes; + SmallVector inheritedTypes; inheritedTypes.push_back(TypeLoc::withoutLoc(superclass)); theClass->setInherited(Impl.SwiftContext.AllocateCopy(inheritedTypes)); } @@ -6297,9 +6387,10 @@ Decl *SwiftDeclConverter::importEnumCase(const clang::EnumConstantDecl *decl, EnumDecl *theEnum, Decl *correctDecl) { auto &context = Impl.SwiftContext; + ImportedName importedName; Optional correctSwiftName; - auto name = - importFullName(decl, correctSwiftName).getDeclName().getBaseIdentifier(); + std::tie(importedName, correctSwiftName) = importFullName(decl); + auto name = importedName.getDeclName().getBaseIdentifier(); if (name.empty()) return nullptr; @@ -6353,8 +6444,9 @@ Decl * SwiftDeclConverter::importOptionConstant(const clang::EnumConstantDecl *decl, const clang::EnumDecl *clangEnum, NominalTypeDecl *theStruct) { + ImportedName nameInfo; Optional correctSwiftName; - ImportedName nameInfo = importFullName(decl, correctSwiftName); + std::tie(nameInfo, correctSwiftName) = importFullName(decl); Identifier name = nameInfo.getDeclName().getBaseIdentifier(); if (name.empty()) return nullptr; @@ -6412,8 +6504,8 @@ Decl *SwiftDeclConverter::importEnumCaseAlias( /*implicit*/ true); constantRef->setType(enumElt->getInterfaceType()); - auto instantiate = new (Impl.SwiftContext) - DotSyntaxCallExpr(constantRef, SourceLoc(), typeRef); + auto instantiate = DotSyntaxCallExpr::create(Impl.SwiftContext, constantRef, + SourceLoc(), typeRef); instantiate->setType(importedEnumTy); instantiate->setThrows(false); @@ -6597,7 +6689,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName, if (!getter) { // Find the self index for the getter. - getterName = importFullName(function, swift3GetterName); + std::tie(getterName, swift3GetterName) = importFullName(function); if (!getterName) continue; @@ -6607,7 +6699,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName, if (!setter) { // Find the self index for the setter. - setterName = importFullName(function, swift3SetterName); + std::tie(setterName, swift3SetterName) = importFullName(function); if (!setterName) continue; @@ -6741,8 +6833,9 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( if (known != Impl.Constructors.end()) return known->second; + ImportedName importedName; Optional correctSwiftName; - auto importedName = importFullName(objcMethod, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(objcMethod); if (!importedName) return nullptr; @@ -7510,23 +7603,24 @@ SwiftDeclConverter::importAccessor(const clang::ObjCMethodDecl *clangAccessor, return accessor; } -static InOutExpr * -createInOutSelfExpr(AccessorDecl *accessorDecl) { +static Expr *createSelfExpr(AccessorDecl *accessorDecl) { ASTContext &ctx = accessorDecl->getASTContext(); - auto inoutSelfDecl = accessorDecl->getImplicitSelfDecl(); - auto inoutSelfRefExpr = - new (ctx) DeclRefExpr(inoutSelfDecl, DeclNameLoc(), - /*implicit=*/ true); - inoutSelfRefExpr->setType(LValueType::get(inoutSelfDecl->getInterfaceType())); - - auto inoutSelfExpr = - new (ctx) InOutExpr(SourceLoc(), - inoutSelfRefExpr, - accessorDecl->mapTypeIntoContext( - inoutSelfDecl->getValueInterfaceType()), - /*isImplicit=*/ true); - inoutSelfExpr->setType(InOutType::get(inoutSelfDecl->getInterfaceType())); + auto selfDecl = accessorDecl->getImplicitSelfDecl(); + auto selfRefExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), + /*implicit*/ true); + + if (!accessorDecl->isMutating()) { + selfRefExpr->setType(selfDecl->getInterfaceType()); + return selfRefExpr; + } + selfRefExpr->setType(LValueType::get(selfDecl->getInterfaceType())); + + auto inoutSelfExpr = new (ctx) InOutExpr( + SourceLoc(), selfRefExpr, + accessorDecl->mapTypeIntoContext(selfDecl->getValueInterfaceType()), + /*isImplicit*/ true); + inoutSelfExpr->setType(InOutType::get(selfDecl->getInterfaceType())); return inoutSelfExpr; } @@ -7544,7 +7638,7 @@ createParamRefExpr(AccessorDecl *accessorDecl, unsigned index) { static CallExpr * createAccessorImplCallExpr(FuncDecl *accessorImpl, - InOutExpr *inoutSelfExpr, + Expr *selfExpr, DeclRefExpr *keyRefExpr) { ASTContext &ctx = accessorImpl->getASTContext(); @@ -7555,9 +7649,7 @@ createAccessorImplCallExpr(FuncDecl *accessorImpl, accessorImplExpr->setType(accessorImpl->getInterfaceType()); auto accessorImplDotCallExpr = - new (ctx) DotSyntaxCallExpr(accessorImplExpr, - SourceLoc(), - inoutSelfExpr); + DotSyntaxCallExpr::create(ctx, accessorImplExpr, SourceLoc(), selfExpr); accessorImplDotCallExpr->setType(accessorImpl->getMethodInterfaceType()); accessorImplDotCallExpr->setThrows(false); @@ -7577,14 +7669,13 @@ synthesizeSubscriptGetterBody(AbstractFunctionDecl *afd, void *context) { ASTContext &ctx = getterDecl->getASTContext(); - InOutExpr *inoutSelfExpr = createInOutSelfExpr(getterDecl); + Expr *selfExpr = createSelfExpr(getterDecl); DeclRefExpr *keyRefExpr = createParamRefExpr(getterDecl, 0); Type elementTy = getterDecl->getResultInterfaceType(); - auto *getterImplCallExpr = createAccessorImplCallExpr(getterImpl, - inoutSelfExpr, - keyRefExpr); + auto *getterImplCallExpr = + createAccessorImplCallExpr(getterImpl, selfExpr, keyRefExpr); // This default handles C++'s operator[] that returns a value type. Expr *propertyExpr = getterImplCallExpr; @@ -7630,14 +7721,13 @@ synthesizeSubscriptSetterBody(AbstractFunctionDecl *afd, void *context) { ASTContext &ctx = setterDecl->getASTContext(); - InOutExpr *inoutSelfExpr = createInOutSelfExpr(setterDecl); + Expr *selfExpr = createSelfExpr(setterDecl); DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0); DeclRefExpr *keyParamRefExpr = createParamRefExpr(setterDecl, 1); Type elementTy = valueParamRefExpr->getDecl()->getInterfaceType(); - auto *setterImplCallExpr = createAccessorImplCallExpr(setterImpl, - inoutSelfExpr, + auto *setterImplCallExpr = createAccessorImplCallExpr(setterImpl, selfExpr, keyParamRefExpr); VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(PTK_UnsafeMutablePointer); @@ -7777,7 +7867,7 @@ void SwiftDeclConverter::addProtocols( void SwiftDeclConverter::importObjCProtocols( Decl *decl, const clang::ObjCProtocolList &clangProtocols, - SmallVectorImpl &inheritedTypes) { + SmallVectorImpl &inheritedTypes) { SmallVector protocols; llvm::SmallPtrSet knownProtocols; if (auto nominal = dyn_cast(decl)) { @@ -7791,7 +7881,9 @@ void SwiftDeclConverter::importObjCProtocols( Impl.importDecl(*cp, getActiveSwiftVersion()))) { addProtocols(proto, protocols, knownProtocols); inheritedTypes.push_back( - TypeLoc::withoutLoc(proto->getDeclaredInterfaceType())); + InheritedEntry( + TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), + /*isUnchecked=*/false)); } } @@ -7820,7 +7912,7 @@ Optional SwiftDeclConverter::importObjCGenericParams( // nested. // Import parameter constraints. - SmallVector inherited; + SmallVector inherited; if (objcGenericParam->hasExplicitBound()) { assert(!objcGenericParam->getUnderlyingType().isNull()); auto clangBound = objcGenericParam->getUnderlyingType() @@ -8081,27 +8173,21 @@ static bool suppressOverriddenMethods(ClangImporter::Implementation &importer, void addCompletionHandlerAttribute(Decl *asyncImport, ArrayRef members, ASTContext &SwiftContext) { - auto *ayncFunc = dyn_cast_or_null(asyncImport); - if (!ayncFunc) - return; + auto *asyncFunc = dyn_cast_or_null(asyncImport); + // Completion handler functions can be imported as getters, but the decl + // given back from the import is the property. Grab the underlying getter + if (auto *property = dyn_cast_or_null(asyncImport)) + asyncFunc = property->getAccessor(AccessorKind::Get); - auto errorConvention = ayncFunc->getForeignErrorConvention(); - auto asyncConvention = ayncFunc->getForeignAsyncConvention(); - if (!asyncConvention) + if (!asyncFunc) return; - unsigned completionIndex = asyncConvention->completionHandlerParamIndex(); - if (errorConvention && - completionIndex >= errorConvention->getErrorParameterIndex()) { - completionIndex--; - } - for (auto *member : members) { - if (member != asyncImport) { + // Only add the attribute to functions that don't already have availability + if (member != asyncImport && isa(member) && + !member->getAttrs().hasAttribute()) { member->getAttrs().add( - new (SwiftContext) CompletionHandlerAsyncAttr( - cast(*asyncImport), completionIndex, - SourceLoc(), SourceLoc(), SourceRange(), /*implicit*/ true)); + AvailableAttr::createForAlternative(SwiftContext, asyncFunc)); } } } @@ -8248,9 +8334,9 @@ void SwiftDeclConverter::importInheritedConstructors( if (objcMethod->isClassMethod()) { assert(ctor->getInitKind() == CtorInitializerKind::ConvenienceFactory); + ImportedName importedName; Optional correctSwiftName; - ImportedName importedName = - importFullName(objcMethod, correctSwiftName); + std::tie(importedName, correctSwiftName) = importFullName(objcMethod); assert( !correctSwiftName && "Import inherited initializers never references correctSwiftName"); @@ -8458,6 +8544,21 @@ Optional swift::importer::isMainActorAttr( return None; } +static bool isUsingMacroName(clang::SourceManager &SM, + clang::SourceLocation loc, + StringRef MacroName) { + if (!loc.isMacroID()) + return false; + auto Sloc = SM.getExpansionLoc(loc); + if (Sloc.isInvalid()) + return false; + auto Eloc = Sloc.getLocWithOffset(MacroName.size()); + if (Eloc.isInvalid()) + return false; + StringRef content(SM.getCharacterData(Sloc), MacroName.size()); + return content == MacroName; +} + /// Import Clang attributes as Swift attributes. void ClangImporter::Implementation::importAttributes( const clang::NamedDecl *ClangDecl, @@ -8581,6 +8682,17 @@ void ClangImporter::Implementation::importAttributes( AnyUnavailable = true; } + if (isUsingMacroName(getClangASTContext().getSourceManager(), + avail->getLoc(), "SPI_AVAILABLE") || + isUsingMacroName(getClangASTContext().getSourceManager(), + avail->getLoc(), "__SPI_AVAILABLE")) { + // The decl has been marked as SPI in the header by using the SPI macro, + // thus we add the SPI attribute to it with a default group name. + MappedDecl->getAttrs().add(SPIAccessControlAttr::create(SwiftContext, + SourceLoc(), SourceRange(), + SwiftContext.getIdentifier(CLANG_MODULE_DEFUALT_SPI_GROUP_NAME))); + } + StringRef message = avail->getMessage(); llvm::VersionTuple deprecated = avail->getDeprecated(); @@ -8613,6 +8725,7 @@ void ClangImporter::Implementation::importAttributes( auto AvAttr = new (C) AvailableAttr(SourceLoc(), SourceRange(), platformK.getValue(), message, swiftReplacement, + /*RenameDecl=*/nullptr, introduced, /*IntroducedRange=*/SourceRange(), deprecated, @@ -8658,6 +8771,14 @@ void ClangImporter::Implementation::importAttributes( continue; } + // Hard-code @actorIndependent, until Objective-C clients start + // using nonisolated. + if (swiftAttr->getAttribute() == "@actorIndependent") { + auto attr = new (SwiftContext) NonisolatedAttr(/*isImplicit=*/true); + MappedDecl->getAttrs().add(attr); + continue; + } + // Dig out a buffer with the attribute text. unsigned bufferID = getClangSwiftAttrSourceBuffer( swiftAttr->getAttribute()); @@ -8672,19 +8793,28 @@ void ClangImporter::Implementation::importAttributes( // Prime the lexer. parser.consumeTokenWithoutFeedingReceiver(); + bool hadError = false; SourceLoc atLoc; if (parser.consumeIf(tok::at_sign, atLoc)) { - (void)parser.parseDeclAttribute( + hadError = parser.parseDeclAttribute( MappedDecl->getAttrs(), atLoc, initContext, - /*isFromClangAttribute=*/true); + /*isFromClangAttribute=*/true).isError(); } else { - // Complain about the missing '@'. + SourceLoc staticLoc; + StaticSpellingKind staticSpelling; + hadError = parser.parseDeclModifierList( + MappedDecl->getAttrs(), staticLoc, staticSpelling, + /*isFromClangAttribute=*/true); + } + + if (hadError) { + // Complain about the unhandled attribute or modifier. auto &clangSrcMgr = getClangASTContext().getSourceManager(); ClangSourceBufferImporter &bufferImporter = getBufferImporterForDiagnostics(); SourceLoc attrLoc = bufferImporter.resolveSourceLocation( clangSrcMgr, swiftAttr->getLocation()); - diagnose(attrLoc, diag::clang_swift_attr_without_at, + diagnose(attrLoc, diag::clang_swift_attr_unhandled, swiftAttr->getAttribute()); } continue; @@ -9148,7 +9278,7 @@ ClangImporter::Implementation::importMirroredDecl(const clang::NamedDecl *decl, } DeclContext *ClangImporter::Implementation::importDeclContextImpl( - const clang::DeclContext *dc) { + const clang::Decl *ImportingDecl, const clang::DeclContext *dc) { // Every declaration should come from a module, so we should not see the // TranslationUnit DeclContext here. assert(!dc->isTranslationUnit()); @@ -9160,8 +9290,10 @@ DeclContext *ClangImporter::Implementation::importDeclContextImpl( // Category decls with same name can be merged and using canonical decl always // leads to the first category of the given name. We'd like to keep these // categories separated. - auto useCanonical = !isa(decl); - auto swiftDecl = importDecl(decl, CurrentVersion, useCanonical); + auto useCanonical = + !isa(decl) && !isa(decl); + auto swiftDecl = importDeclForDeclContext(ImportingDecl, decl->getName(), + decl, CurrentVersion, useCanonical); if (!swiftDecl) return nullptr; @@ -9214,6 +9346,71 @@ GenericSignature ClangImporter::Implementation::buildGenericSignature( GenericSignature()); } +Decl * +ClangImporter::Implementation::importDeclForDeclContext( + const clang::Decl *importingDecl, + StringRef writtenName, + const clang::NamedDecl *contextDecl, + Version version, + bool useCanonicalDecl) +{ + auto key = std::make_tuple(importingDecl, writtenName, contextDecl, version, + useCanonicalDecl); + auto iter = find(llvm::reverse(contextDeclsBeingImported), key); + + // No cycle? Remember that we're importing this, then import normally. + if (iter == contextDeclsBeingImported.rend()) { + contextDeclsBeingImported.push_back(key); + auto imported = importDecl(contextDecl, version, useCanonicalDecl); + contextDeclsBeingImported.pop_back(); + return imported; + } + + // There's a cycle. Is the declaration imported enough to break the cycle + // gracefully? If so, we'll have it in the decl cache. + if (auto cached = importDeclCached(contextDecl, version, useCanonicalDecl)) + return cached; + + // Can't break it? Warn and return nullptr, which is at least better than + // stack overflow by recursion. + + // Avoid emitting warnings repeatedly. + if (!contextDeclsWarnedAbout.insert(contextDecl).second) + return nullptr; + + auto convertLoc = [&](clang::SourceLocation clangLoc) { + return getBufferImporterForDiagnostics() + .resolveSourceLocation(getClangASTContext().getSourceManager(), + clangLoc); + }; + + auto getDeclName = [](const clang::Decl *D) -> StringRef { + if (auto ND = dyn_cast(D)) + return ND->getName(); + return ""; + }; + + SourceLoc loc = convertLoc(importingDecl->getLocation()); + diagnose(loc, diag::swift_name_circular_context_import, + writtenName, getDeclName(importingDecl)); + + // Diagnose other decls involved in the cycle. + for (auto entry : make_range(contextDeclsBeingImported.rbegin(), iter)) { + auto otherDecl = std::get<0>(entry); + auto otherWrittenName = std::get<1>(entry); + diagnose(convertLoc(otherDecl->getLocation()), + diag::swift_name_circular_context_import_other, + otherWrittenName, getDeclName(otherDecl)); + } + + if (auto *parentModule = contextDecl->getOwningModule()) { + diagnose(loc, diag::unresolvable_clang_decl_is_a_framework_bug, + parentModule->getFullModuleName()); + } + + return nullptr; +} + DeclContext * ClangImporter::Implementation::importDeclContextOf( const clang::Decl *decl, @@ -9231,13 +9428,15 @@ ClangImporter::Implementation::importDeclContextOf( } // Import the DeclContext. - importedDC = importDeclContextImpl(dc); + importedDC = importDeclContextImpl(decl, dc); break; } case EffectiveClangContext::TypedefContext: { // Import the typedef-name as a declaration. - auto importedDecl = importDecl(context.getTypedefName(), CurrentVersion); + auto importedDecl = importDeclForDeclContext( + decl, context.getTypedefName()->getName(), context.getTypedefName(), + CurrentVersion); if (!importedDecl) return nullptr; // Dig out the imported DeclContext. @@ -9255,17 +9454,19 @@ ClangImporter::Implementation::importDeclContextOf( if (auto clangDecl = lookupTable->resolveContext(context.getUnresolvedName())) { // Import the Clang declaration. - auto decl = importDecl(clangDecl, CurrentVersion); - if (!decl) return nullptr; + auto swiftDecl = importDeclForDeclContext(decl, + context.getUnresolvedName(), + clangDecl, CurrentVersion); + if (!swiftDecl) return nullptr; // Look through typealiases. - if (auto typealias = dyn_cast(decl)) + if (auto typealias = dyn_cast(swiftDecl)) importedDC = typealias->getDeclaredInterfaceType()->getAnyNominal(); else // Map to a nominal type declaration. - importedDC = dyn_cast(decl); - break; + importedDC = dyn_cast(swiftDecl); } } + break; } } @@ -9483,8 +9684,7 @@ synthesizeConstantGetterBody(AbstractFunctionDecl *afd, void *voidContext) { DeclName initName = DeclName(ctx, DeclBaseName::createConstructor(), { ctx.Id_rawValue }); auto nominal = type->getAnyNominal(); - const auto flags = NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; - for (auto found : nominal->lookupDirect(initName, flags)) { + for (auto found : nominal->lookupDirect(initName)) { init = dyn_cast(found); if (init && init->getDeclContext() == nominal) break; @@ -9498,8 +9698,8 @@ synthesizeConstantGetterBody(AbstractFunctionDecl *afd, void *voidContext) { // (Self) -> ... initTy = initTy->castTo()->getResult(); - auto initRef = new (ctx) DotSyntaxCallExpr(declRef, SourceLoc(), - typeRef, initTy); + auto initRef = + DotSyntaxCallExpr::create(ctx, declRef, SourceLoc(), typeRef, initTy); initRef->setThrows(false); // (rawValue: T) -> ... @@ -9582,9 +9782,8 @@ ClangImporter::Implementation::createConstant(Identifier name, DeclContext *dc, // Mark the function transparent so that we inline it away completely. func->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true)); - auto actorIndependentAttr = new (C) ActorIndependentAttr( - ActorIndependentKind::Unsafe, /*IsImplicit=*/true); - var->getAttrs().add(actorIndependentAttr); + auto nonisolatedAttr = new (C) NonisolatedAttr(/*IsImplicit=*/true); + var->getAttrs().add(nonisolatedAttr); // Set the function up as the getter. makeComputed(var, func, nullptr); @@ -9906,7 +10105,8 @@ void ClangImporter::Implementation::loadAllConformances( auto conformance = SwiftContext.getConformance( dc->getDeclaredInterfaceType(), protocol, SourceLoc(), dc, - ProtocolConformanceState::Incomplete); + ProtocolConformanceState::Incomplete, + protocol->isSpecificProtocol(KnownProtocolKind::Sendable)); conformance->setLazyLoader(this, /*context*/0); conformance->setState(ProtocolConformanceState::Complete); Conformances.push_back(conformance); diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index aa2e9ac4bccdd..670a292e574b9 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1316,7 +1316,7 @@ NameImporter::considerAsyncImport( // void (^)()), we cannot importer it. auto completionHandlerFunctionType = completionHandlerParam->getType()->castAs() - ->getPointeeType()->getAs(); + ->getPointeeType()->getAs(); if (!completionHandlerFunctionType) return notAsync("block parameter does not have a prototype"); @@ -1328,8 +1328,13 @@ NameImporter::considerAsyncImport( // nullable NSError type, which would indicate that the async method could // throw. Optional completionHandlerErrorParamIndex; - auto completionHandlerParamTypes = - completionHandlerFunctionType->getParamTypes(); + + ArrayRef completionHandlerParamTypes; + if (auto prototype = completionHandlerFunctionType + ->getAs()) { + completionHandlerParamTypes = prototype->getParamTypes(); + } + auto &clangCtx = clangDecl->getASTContext(); for (unsigned paramIdx : indices(completionHandlerParamTypes)) { auto paramType = completionHandlerParamTypes[paramIdx]; diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 1473d8a88cc94..92b3328987195 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -23,8 +23,8 @@ #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/ExistentialLayout.h" -#include "swift/AST/GenericEnvironment.h" -#include "swift/AST/GenericSignatureBuilder.h" +#include "swift/AST/GenericParamList.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" @@ -589,8 +589,7 @@ namespace { auto nominal = element->getAnyNominal(); auto simdscalar = Impl.SwiftContext.getProtocol(KnownProtocolKind::SIMDScalar); SmallVector conformances; - if (simdscalar && nominal->lookupConformance(nominal->getParentModule(), - simdscalar, conformances)) { + if (simdscalar && nominal->lookupConformance(simdscalar, conformances)) { // Element type conforms to SIMDScalar. Get the SIMDn generic type // if it exists. SmallString<8> name("SIMD"); @@ -708,11 +707,11 @@ namespace { // suppressed. Treat it as a typedef. return None; } - if (index > genericSig->getGenericParams().size()) { + if (index > genericSig.getGenericParams().size()) { return ImportResult(); } - return ImportResult(genericSig->getGenericParams()[index], + return ImportResult(genericSig.getGenericParams()[index], ImportHint::ObjCPointer); } @@ -1110,7 +1109,7 @@ namespace { auto unboundDecl = unboundType->getDecl(); auto bridgedSig = unboundDecl->getGenericSignature(); assert(bridgedSig && "Bridged signature"); - unsigned numExpectedTypeArgs = bridgedSig->getGenericParams().size(); + unsigned numExpectedTypeArgs = bridgedSig.getGenericParams().size(); if (importedTypeArgs.size() != numExpectedTypeArgs) return Type(); @@ -1880,9 +1879,12 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( param->setInterfaceType(parentType.getType()); - // Workaround until proper const support is handled: Force everything to - // be mutating. This implicitly makes the parameter indirect. - param->setSpecifier(ParamSpecifier::InOut); + if (SwiftContext.getClangModuleLoader()->isCXXMethodMutating(CMD)) { + // This implicitly makes the parameter indirect. + param->setSpecifier(ParamSpecifier::InOut); + } else { + param->setSpecifier(ParamSpecifier::Default); + } parameters.push_back(param); } @@ -2804,8 +2806,7 @@ bool ClangImporter::Implementation::matchesHashableBound(Type type) { auto hashable = SwiftContext.getProtocol(KnownProtocolKind::Hashable); SmallVector conformances; return hashable && - nominal->lookupConformance(nominal->getParentModule(), hashable, - conformances); + nominal->lookupConformance(hashable, conformances); } return false; diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index ac1a979bb2a86..9ec0f0e8e6108 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -642,6 +642,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// The DWARF importer delegate, if installed. DWARFImporterDelegate *DWARFImporter = nullptr; + public: /// Only used for testing. void setDWARFImporterDelegate(DWARFImporterDelegate &delegate); @@ -881,10 +882,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// /// \returns The imported declaration, or null if this declaration could /// not be represented in Swift. - Decl *importDeclReal(const clang::NamedDecl *ClangDecl, Version version) { + Decl *importDeclReal(const clang::NamedDecl *ClangDecl, Version version, + bool useCanonicalDecl = true) { return importDeclAndCacheImpl(ClangDecl, version, /*SuperfluousTypedefsAreTransparent=*/false, - /*UseCanonicalDecl*/true); + /*UseCanonicalDecl*/ useCanonicalDecl); } /// Import a cloned version of the given declaration, which is part of @@ -913,8 +915,34 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// /// \returns The imported declaration context, or null if it could not /// be converted. - DeclContext *importDeclContextImpl(const clang::DeclContext *dc); + DeclContext *importDeclContextImpl(const clang::Decl *ImportingDecl, + const clang::DeclContext *dc); + +private: + /// Declarations currently being imported by \c importDeclForDeclContext(). + /// Used to break cycles when a swift_name attribute is circular in a way that + /// can't be resolved, or there is some other cycle through + /// \c importDeclContextOf(). + llvm::SmallVector, 8> + contextDeclsBeingImported; + + /// Records which contexts \c importDeclForDeclContext() has already warned + /// were unimportable. + llvm::SmallPtrSet contextDeclsWarnedAbout; + + /// Exactly equivalent to \c importDecl(), except with additional + /// cycle-breaking code. + /// + /// \param writtenName The name that should be used for the declaration + /// in cycle diagnostics. + Decl *importDeclForDeclContext(const clang::Decl *ImportingDecl, + StringRef writtenName, + const clang::NamedDecl *ClangDecl, + Version version, + bool UseCanonicalDecl = true); +public: /// Import the declaration context of a given Clang declaration into /// Swift. /// @@ -1396,6 +1424,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm_unreachable("unimplemented for ClangImporter"); } + void loadAssociatedTypes(const ProtocolDecl *decl, uint64_t contextData, + SmallVectorImpl &assocTypes) override { + llvm_unreachable("unimplemented for ClangImporter"); + } + template DeclTy *createDeclWithClangNode(ClangNode ClangN, AccessLevel access, Targs &&... Args) { diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 3c5c17fc3a77e..36164794bcdc8 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -1854,7 +1854,6 @@ SwiftNameLookupExtension::hashExtension(llvm::hash_code code) const { return llvm::hash_combine(code, StringRef("swift.lookup"), SWIFT_LOOKUP_TABLE_VERSION_MAJOR, SWIFT_LOOKUP_TABLE_VERSION_MINOR, - swiftCtx.LangOpts.EnableExperimentalConcurrency, version::getSwiftFullVersion()); } diff --git a/lib/ClangImporter/SwiftLookupTable.h b/lib/ClangImporter/SwiftLookupTable.h index db60c67a90807..a3d901aded624 100644 --- a/lib/ClangImporter/SwiftLookupTable.h +++ b/lib/ClangImporter/SwiftLookupTable.h @@ -197,11 +197,10 @@ class EffectiveClangContext { DC = omDecl->getCanonicalDecl(); } else if (auto fDecl = dyn_cast(dc)) { DC = fDecl->getCanonicalDecl(); - } else if (auto nsDecl = dyn_cast(dc)) { - DC = nsDecl->getCanonicalDecl(); } else { assert(isa(dc) || isa(dc) || + isa(dc) || isa(dc) && "No other kinds of effective Clang contexts"); DC = dc; diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index f240434c0fc5c..a3e2c8becad79 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -736,6 +736,12 @@ NodePointer Demangler::demangleTypeAnnotation() { return createNode(Node::Kind::AsyncAnnotation); case 'b': return createNode(Node::Kind::ConcurrentFunctionType); + case 'c': + return createWithChild( + Node::Kind::GlobalActorFunctionType, popTypeAndGetChild()); + case 'i': + return createType( + createWithChild(Node::Kind::Isolated, popTypeAndGetChild())); case 'j': return demangleDifferentiableFunctionType(); case 'k': @@ -949,7 +955,9 @@ NodePointer Demangler::demangleStandardSubstitution() { int RepeatCount = demangleNatural(); if (RepeatCount > SubstitutionMerging::MaxRepeatCount) return nullptr; - if (NodePointer Nd = createStandardSubstitution(nextChar())) { + bool secondLevelSubstitution = nextIf('c'); + if (NodePointer Nd = createStandardSubstitution( + nextChar(), secondLevelSubstitution)) { while (RepeatCount-- > 1) { pushNode(Nd); } @@ -960,10 +968,16 @@ NodePointer Demangler::demangleStandardSubstitution() { } } -NodePointer Demangler::createStandardSubstitution(char Subst) { +NodePointer Demangler::createStandardSubstitution( + char Subst, bool SecondLevel) { #define STANDARD_TYPE(KIND, MANGLING, TYPENAME) \ - if (Subst == #MANGLING[0]) { \ - return createSwiftType(Node::Kind::KIND, #TYPENAME); \ + if (!SecondLevel && Subst == #MANGLING[0]) { \ + return createSwiftType(Node::Kind::KIND, #TYPENAME); \ + } + +#define STANDARD_TYPE_2(KIND, MANGLING, TYPENAME) \ + if (SecondLevel && Subst == #MANGLING[0]) { \ + return createSwiftType(Node::Kind::KIND, #TYPENAME); \ } #include "swift/Demangling/StandardTypesMangling.def" @@ -1272,6 +1286,7 @@ NodePointer Demangler::popFunctionType(Node::Kind kind, bool hasClangType) { ClangType = demangleClangType(); } addChild(FuncType, ClangType); + addChild(FuncType, popNode(Node::Kind::GlobalActorFunctionType)); addChild(FuncType, popNode(Node::Kind::DifferentiableFunctionType)); addChild(FuncType, popNode(Node::Kind::ThrowsAnnotation)); addChild(FuncType, popNode(Node::Kind::ConcurrentFunctionType)); @@ -1308,6 +1323,9 @@ NodePointer Demangler::popFunctionParamLabels(NodePointer Type) { return nullptr; unsigned FirstChildIdx = 0; + if (FuncType->getChild(FirstChildIdx)->getKind() + == Node::Kind::GlobalActorFunctionType) + ++FirstChildIdx; if (FuncType->getChild(FirstChildIdx)->getKind() == Node::Kind::DifferentiableFunctionType) ++FirstChildIdx; @@ -2537,6 +2555,20 @@ NodePointer Demangler::demangleThunkOrSpecialization() { return createNode(Node::Kind::OutlinedBridgedMethod, Params); } case 'u': return createNode(Node::Kind::AsyncFunctionPointer); + case 'U': { + auto globalActor = popNode(Node::Kind::Type); + if (!globalActor) + return nullptr; + + auto reabstraction = popNode(); + if (!reabstraction) + return nullptr; + + auto node = createNode(Node::Kind::ReabstractionThunkHelperWithGlobalActor); + node->addChild(reabstraction, *this); + node->addChild(globalActor, *this); + return node; + } case 'J': switch (peekChar()) { case 'S': @@ -2607,7 +2639,8 @@ NodePointer Demangler::demangleAutoDiffSelfReorderingReabstractionThunk() { addChild(result, popNode(Node::Kind::DependentGenericSignature)); result = addChild(result, popNode(Node::Kind::Type)); result = addChild(result, popNode(Node::Kind::Type)); - result->reverseChildren(); + if (result) + result->reverseChildren(); result = addChild(result, demangleAutoDiffFunctionKind()); return result; } @@ -3378,6 +3411,9 @@ NodePointer Demangler::demangleSubscript() { NodePointer LabelList = popFunctionParamLabels(Type); NodePointer Context = popContext(); + if (!Type) + return nullptr; + NodePointer Subscript = createNode(Node::Kind::Subscript); Subscript = addChild(Subscript, Context); addChild(Subscript, LabelList); diff --git a/lib/Demangling/ManglingUtils.cpp b/lib/Demangling/ManglingUtils.cpp index 122436cb1c913..4a42108a4cd79 100644 --- a/lib/Demangling/ManglingUtils.cpp +++ b/lib/Demangling/ManglingUtils.cpp @@ -66,14 +66,19 @@ std::string Mangle::translateOperator(StringRef Op) { return Encoded; } -char Mangle::getStandardTypeSubst(StringRef TypeName) { +llvm::Optional Mangle::getStandardTypeSubst(StringRef TypeName) { #define STANDARD_TYPE(KIND, MANGLING, TYPENAME) \ if (TypeName == #TYPENAME) { \ - return #MANGLING[0]; \ + return StringRef(#MANGLING); \ + } + +#define STANDARD_TYPE_2(KIND, MANGLING, TYPENAME) \ + if (TypeName == #TYPENAME) { \ + return StringRef("c" #MANGLING); \ } #include "swift/Demangling/StandardTypesMangling.def" - return 0; + return llvm::None; } diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 3fc9344f10bdf..19c528ebe42b5 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -173,16 +173,18 @@ class NodePrinter { public: NodePrinter(DemangleOptions options) : Options(options) {} - + std::string printRoot(NodePointer root) { isValid = true; - print(root); + print(root, 0); if (isValid) return std::move(Printer).str(); return ""; } private: + static const unsigned MaxDepth = 1024; + /// Called when the node tree in valid. /// /// The demangler already catches most error cases and mostly produces valid @@ -190,24 +192,24 @@ class NodePrinter { /// instead the NodePrinter bails. void setInvalid() { isValid = false; } - void printChildren(Node::iterator begin, - Node::iterator end, + void printChildren(Node::iterator begin, Node::iterator end, unsigned depth, const char *sep = nullptr) { for (; begin != end;) { - print(*begin); + print(*begin, depth + 1); ++begin; if (sep && begin != end) Printer << sep; } } - - void printChildren(NodePointer Node, const char *sep = nullptr) { + + void printChildren(NodePointer Node, unsigned depth, + const char *sep = nullptr) { if (!Node) return; Node::iterator begin = Node->begin(), end = Node->end(); - printChildren(begin, end, sep); + printChildren(begin, end, depth, sep); } - + NodePointer getFirstChildOfKind(NodePointer Node, Node::Kind kind) { if (!Node) return nullptr; @@ -218,13 +220,13 @@ class NodePrinter { return nullptr; } - void printBoundGenericNoSugar(NodePointer Node) { + void printBoundGenericNoSugar(NodePointer Node, unsigned depth) { if (Node->getNumChildren() < 2) return; NodePointer typelist = Node->getChild(1); - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << "<"; - printChildren(typelist, ", "); + printChildren(typelist, depth, ", "); Printer << ">"; } @@ -310,7 +312,6 @@ class NodePrinter { case Node::Kind::Structure: case Node::Kind::OtherNominalType: case Node::Kind::TupleElementName: - case Node::Kind::Type: case Node::Kind::TypeAlias: case Node::Kind::TypeList: case Node::Kind::LabelList: @@ -321,6 +322,9 @@ class NodePrinter { case Node::Kind::SugaredParen: return true; + case Node::Kind::Type: + return isSimpleType(Node->getChild(0)); + case Node::Kind::ProtocolList: return Node->getChild(0)->getNumChildren() <= 1; @@ -414,6 +418,7 @@ class NodePrinter { case Node::Kind::InOut: case Node::Kind::InfixOperator: case Node::Kind::Initializer: + case Node::Kind::Isolated: case Node::Kind::PropertyWrapperBackingInitializer: case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::KeyPathGetterThunkHelper: @@ -471,6 +476,7 @@ class NodePrinter { case Node::Kind::ReabstractionThunk: case Node::Kind::ReabstractionThunkHelper: case Node::Kind::ReabstractionThunkHelperWithSelf: + case Node::Kind::ReabstractionThunkHelperWithGlobalActor: case Node::Kind::ReadAccessor: case Node::Kind::RelatedEntityDeclName: case Node::Kind::RetroactiveConformance: @@ -516,6 +522,7 @@ class NodePrinter { case Node::Kind::GenericTypeParamDecl: case Node::Kind::ConcurrentFunctionType: case Node::Kind::DifferentiableFunctionType: + case Node::Kind::GlobalActorFunctionType: case Node::Kind::AsyncAnnotation: case Node::Kind::ThrowsAnnotation: case Node::Kind::EmptyList: @@ -578,11 +585,11 @@ class NodePrinter { printer_unreachable("bad node kind"); } - void printWithParens(NodePointer type) { + void printWithParens(NodePointer type, unsigned depth) { bool needs_parens = !isSimpleType(type); if (needs_parens) Printer << "("; - print(type); + print(type, depth + 1); if (needs_parens) Printer << ")"; } @@ -639,12 +646,12 @@ class NodePrinter { return SugarType::None; } - - void printBoundGeneric(NodePointer Node) { + + void printBoundGeneric(NodePointer Node, unsigned depth) { if (Node->getNumChildren() < 2) return; if (Node->getNumChildren() != 2) { - printBoundGenericNoSugar(Node); + printBoundGenericNoSugar(Node, depth); return; } @@ -652,16 +659,16 @@ class NodePrinter { Node->getKind() == Node::Kind::BoundGenericClass) { // no sugar here - printBoundGenericNoSugar(Node); + printBoundGenericNoSugar(Node, depth); return; } // Print the conforming type for a "bound" protocol node "as" the protocol // type. if (Node->getKind() == Node::Kind::BoundGenericProtocol) { - printChildren(Node->getChild(1)); + printChildren(Node->getChild(1), depth); Printer << " as "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return; } @@ -669,19 +676,19 @@ class NodePrinter { switch (sugarType) { case SugarType::None: - printBoundGenericNoSugar(Node); + printBoundGenericNoSugar(Node, depth); break; case SugarType::Optional: case SugarType::ImplicitlyUnwrappedOptional: { NodePointer type = Node->getChild(1)->getChild(0); - printWithParens(type); + printWithParens(type, depth); Printer << (sugarType == SugarType::Optional ? "?" : "!"); break; } case SugarType::Array: { NodePointer type = Node->getChild(1)->getChild(0); Printer << "["; - print(type); + print(type, depth + 1); Printer << "]"; break; } @@ -689,9 +696,9 @@ class NodePrinter { NodePointer keyType = Node->getChild(1)->getChild(0); NodePointer valueType = Node->getChild(1)->getChild(1); Printer << "["; - print(keyType); + print(keyType, depth + 1); Printer << " : "; - print(valueType); + print(valueType, depth + 1); Printer << "]"; break; } @@ -707,7 +714,7 @@ class NodePrinter { } void printFunctionParameters(NodePointer LabelList, NodePointer ParameterType, - bool showTypes) { + unsigned depth, bool showTypes) { if (ParameterType->getKind() != Node::Kind::ArgumentTuple) { setInvalid(); return; @@ -720,7 +727,7 @@ class NodePrinter { // only a single not-named parameter if (showTypes) { Printer << '('; - print(Parameters); + print(Parameters, depth + 1); Printer << ')'; } else { Printer << "(_:)"; @@ -760,24 +767,25 @@ class NodePrinter { ++ParamIndex; if (showTypes) - print(Param); + print(Param, depth + 1); }, [&]() { Printer << (showTypes ? ", " : ""); }); Printer << ')'; } - void printFunctionType(NodePointer LabelList, NodePointer node) { - if (node->getNumChildren() < 2 || node->getNumChildren() > 6) { + void printFunctionType(NodePointer LabelList, NodePointer node, + unsigned depth) { + if (node->getNumChildren() < 2) { setInvalid(); return; } - auto printConventionWithMangledCType = [this, - node](const char *convention) { + auto printConventionWithMangledCType = [this, node, + depth](const char *convention) { Printer << "@convention(" << convention; if (node->getFirstChild()->getKind() == Node::Kind::ClangType) { Printer << ", mangledCType: \""; - print(node->getFirstChild()); + print(node->getFirstChild(), depth + 1); Printer << '"'; } Printer << ") "; @@ -806,19 +814,25 @@ class NodePrinter { assert(false && "Unhandled function type in printFunctionType!"); } + unsigned argIndex = node->getNumChildren() - 2; unsigned startIndex = 0; bool isSendable = false, isAsync = false, isThrows = false; auto diffKind = MangledDifferentiabilityKind::NonDifferentiable; + if (node->getChild(startIndex)->getKind() == Node::Kind::ClangType) { + // handled earlier + ++startIndex; + } + if (node->getChild(startIndex)->getKind() == + Node::Kind::GlobalActorFunctionType) { + print(node->getChild(startIndex), depth + 1); + ++startIndex; + } if (node->getChild(startIndex)->getKind() == Node::Kind::DifferentiableFunctionType) { diffKind = (MangledDifferentiabilityKind)node->getChild(startIndex)->getIndex(); ++startIndex; } - if (node->getChild(startIndex)->getKind() == Node::Kind::ClangType) { - // handled earlier - ++startIndex; - } if (node->getChild(startIndex)->getKind() == Node::Kind::ThrowsAnnotation) { ++startIndex; isThrows = true; @@ -853,7 +867,7 @@ class NodePrinter { if (isSendable) Printer << "@Sendable "; - printFunctionParameters(LabelList, node->getChild(startIndex), + printFunctionParameters(LabelList, node->getChild(argIndex), depth, Options.ShowFunctionArgumentTypes); if (!Options.ShowFunctionArgumentTypes) @@ -865,10 +879,10 @@ class NodePrinter { if (isThrows) Printer << " throws"; - print(node->getChild(startIndex + 1)); + print(node->getChild(argIndex + 1), depth + 1); } - void printImplFunctionType(NodePointer fn) { + void printImplFunctionType(NodePointer fn, unsigned depth) { NodePointer patternSubs = nullptr; NodePointer invocationSubs = nullptr; enum State { Attrs, Inputs, Results } curState = Attrs; @@ -879,7 +893,7 @@ class NodePrinter { case Attrs: if (patternSubs) { Printer << "@substituted "; - print(patternSubs->getChild(0)); + print(patternSubs->getChild(0), depth + 1); Printer << ' '; } Printer << '('; @@ -895,20 +909,20 @@ class NodePrinter { if (child->getKind() == Node::Kind::ImplParameter) { if (curState == Inputs) Printer << ", "; transitionTo(Inputs); - print(child); + print(child, depth + 1); } else if (child->getKind() == Node::Kind::ImplResult || child->getKind() == Node::Kind::ImplYield || child->getKind() == Node::Kind::ImplErrorResult) { if (curState == Results) Printer << ", "; transitionTo(Results); - print(child); + print(child, depth + 1); } else if (child->getKind() == Node::Kind::ImplPatternSubstitutions) { patternSubs = child; } else if (child->getKind() == Node::Kind::ImplInvocationSubstitutions) { invocationSubs = child; } else { assert(curState == Attrs); - print(child); + print(child, depth + 1); Printer << ' '; } } @@ -917,30 +931,33 @@ class NodePrinter { if (patternSubs) { Printer << " for <"; - printChildren(patternSubs->getChild(1)); + printChildren(patternSubs->getChild(1), depth); Printer << '>'; } if (invocationSubs) { Printer << " for <"; - printChildren(invocationSubs->getChild(0)); + printChildren(invocationSubs->getChild(0), depth); Printer << '>'; } } - void printFunctionSigSpecializationParams(NodePointer Node); + void printFunctionSigSpecializationParams(NodePointer Node, unsigned depth); void printSpecializationPrefix(NodePointer node, StringRef Description, + unsigned depth, StringRef ParamPrefix = StringRef()); /// The main big print function. - NodePointer print(NodePointer Node, bool asPrefixContext = false); + NodePointer print(NodePointer Node, unsigned depth, + bool asPrefixContext = false); - NodePointer printAbstractStorage(NodePointer Node, bool asPrefixContent, - StringRef ExtraName); + NodePointer printAbstractStorage(NodePointer Node, unsigned depth, + bool asPrefixContent, StringRef ExtraName); /// Utility function to print entities. /// /// \param Entity The entity node to print + /// \param depth The depth in the print() call tree. /// \param asPrefixContext Should the entity printed as a context which as a /// prefix to another entity, e.g. the Abc in Abc.def() /// \param TypePr How should the type of the entity be printed, if at all. @@ -954,10 +971,10 @@ class NodePrinter { /// provided by the node. Gets printed even if hasName is false. /// \return If a non-null node is returned it's a context which must be /// printed in postfix-form after the entity: " in ". - NodePointer printEntity(NodePointer Entity, bool asPrefixContext, - TypePrinting TypePr, bool hasName, - StringRef ExtraName = "", int ExtraIndex = -1, - StringRef OverwriteName = ""); + NodePointer printEntity(NodePointer Entity, unsigned depth, + bool asPrefixContext, TypePrinting TypePr, + bool hasName, StringRef ExtraName = "", + int ExtraIndex = -1, StringRef OverwriteName = ""); /// Print the type of an entity. /// @@ -965,9 +982,9 @@ class NodePrinter { /// \param type The type of the entity. /// \param genericFunctionTypeList If not null, the generic argument types /// which is printed in the generic signature. + /// \param depth The depth in the print() call tree. void printEntityType(NodePointer Entity, NodePointer type, - NodePointer genericFunctionTypeList); - + NodePointer genericFunctionTypeList, unsigned depth); }; } // end anonymous namespace @@ -979,7 +996,8 @@ static bool isExistentialType(NodePointer node) { } /// Print the relevant parameters and return the new index. -void NodePrinter::printFunctionSigSpecializationParams(NodePointer Node) { +void NodePrinter::printFunctionSigSpecializationParams(NodePointer Node, + unsigned depth) { unsigned Idx = 0; unsigned End = Node->getNumChildren(); while (Idx < End) { @@ -989,12 +1007,12 @@ void NodePrinter::printFunctionSigSpecializationParams(NodePointer Node) { switch (K) { case FunctionSigSpecializationParamKind::BoxToValue: case FunctionSigSpecializationParamKind::BoxToStack: - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); break; case FunctionSigSpecializationParamKind::ConstantPropFunction: case FunctionSigSpecializationParamKind::ConstantPropGlobal: { Printer << "["; - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); Printer << " : "; const auto &text = Node->getChild(Idx++)->getText(); std::string demangledName = demangleSymbolAsString(text); @@ -1009,33 +1027,33 @@ void NodePrinter::printFunctionSigSpecializationParams(NodePointer Node) { case FunctionSigSpecializationParamKind::ConstantPropInteger: case FunctionSigSpecializationParamKind::ConstantPropFloat: Printer << "["; - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); Printer << " : "; - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); Printer << "]"; break; case FunctionSigSpecializationParamKind::ConstantPropString: Printer << "["; - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); Printer << " : "; - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); Printer << "'"; - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); Printer << "'"; Printer << "]"; break; case FunctionSigSpecializationParamKind::ClosureProp: Printer << "["; - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); Printer << " : "; - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); Printer << ", Argument Types : ["; for (unsigned e = Node->getNumChildren(); Idx < e;) { NodePointer child = Node->getChild(Idx); // Until we no longer have a type node, keep demangling. if (child->getKind() != Node::Kind::Type) break; - print(child); + print(child, depth + 1); ++Idx; // If we are not done, print the ", ". @@ -1053,13 +1071,14 @@ void NodePrinter::printFunctionSigSpecializationParams(NodePointer Node) { (V & unsigned( FunctionSigSpecializationParamKind::ExistentialToGeneric))) && "Invalid OptionSet"); - print(Node->getChild(Idx++)); + print(Node->getChild(Idx++), depth + 1); } } } void NodePrinter::printSpecializationPrefix(NodePointer node, StringRef Description, + unsigned depth, StringRef ParamPrefix) { if (!Options.DisplayGenericSpecializations) { if (!SpecializationPrefixPrinted) { @@ -1081,7 +1100,7 @@ void NodePrinter::printSpecializationPrefix(NodePointer node, case Node::Kind::IsSerialized: Printer << Separator; Separator = ", "; - print(child); + print(child, depth + 1); break; default: @@ -1092,14 +1111,14 @@ void NodePrinter::printSpecializationPrefix(NodePointer node, switch (child->getKind()) { case Node::Kind::FunctionSignatureSpecializationParam: Printer << "Arg[" << argNum << "] = "; - printFunctionSigSpecializationParams(child); + printFunctionSigSpecializationParams(child, depth); break; case Node::Kind::FunctionSignatureSpecializationReturn: Printer << "Return = "; - printFunctionSigSpecializationParams(child); + printFunctionSigSpecializationParams(child, depth); break; default: - print(child); + print(child, depth + 1); } } ++argNum; @@ -1127,82 +1146,88 @@ static bool needSpaceBeforeType(NodePointer Type) { } } -NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { +NodePointer NodePrinter::print(NodePointer Node, unsigned depth, + bool asPrefixContext) { + if (depth > NodePrinter::MaxDepth) { + Printer << "<>"; + return nullptr; + } + switch (auto kind = Node->getKind()) { case Node::Kind::Static: Printer << "static "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::CurryThunk: Printer << "curry thunk of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::DispatchThunk: Printer << "dispatch thunk of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::MethodDescriptor: Printer << "method descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::MethodLookupFunction: Printer << "method lookup function for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ObjCMetadataUpdateFunction: Printer << "ObjC metadata update function for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ObjCResilientClassStub: Printer << "ObjC resilient class stub for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::FullObjCResilientClassStub: Printer << "full ObjC resilient class stub for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OutlinedBridgedMethod: Printer << "outlined bridged method (" << Node->getText() << ") of "; return nullptr; case Node::Kind::OutlinedCopy: Printer << "outlined copy of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); if (Node->getNumChildren() > 1) - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::OutlinedConsume: Printer << "outlined consume of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); if (Node->getNumChildren() > 1) - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::OutlinedRetain: Printer << "outlined retain of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OutlinedRelease: Printer << "outlined release of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OutlinedInitializeWithTake: Printer << "outlined init with take of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OutlinedInitializeWithCopy: Printer << "outlined init with copy of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OutlinedAssignWithTake: Printer << "outlined assign with take of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OutlinedAssignWithCopy: Printer << "outlined assign with copy of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OutlinedDestroy: Printer << "outlined destroy of "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OutlinedVariable: Printer << "outlined variable #" << Node->getIndex() << " of "; @@ -1212,14 +1237,14 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; case Node::Kind::AnonymousContext: if (Options.QualifyEntities && Options.DisplayExtensionContexts) { - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << ".(unknown context at "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << ")"; if (Node->getNumChildren() >= 3 && Node->getChild(2)->getNumChildren() > 0) { Printer << '<'; - print(Node->getChild(2)); + print(Node->getChild(2), depth + 1); Printer << '>'; } } @@ -1230,45 +1255,47 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Options.QualifyEntities && Options.DisplayExtensionContexts) { Printer << "(extension in "; // Print the module where extension is defined. - print(Node->getChild(0), true); + print(Node->getChild(0), depth + 1, true); Printer << "):"; } - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); if (Node->getNumChildren() == 3) { // Currently the runtime does not mangle the generic signature. // This is an open to-do in swift::_buildDemanglingForContext(). if (!Options.PrintForTypeName) - print(Node->getChild(2)); + print(Node->getChild(2), depth + 1); } return nullptr; case Node::Kind::Variable: - return printEntity(Node, asPrefixContext, TypePrinting::WithColon, - /*hasName*/true); + return printEntity(Node, depth, asPrefixContext, TypePrinting::WithColon, + /*hasName*/ true); case Node::Kind::Function: case Node::Kind::BoundGenericFunction: - return printEntity(Node, asPrefixContext, TypePrinting::FunctionStyle, - /*hasName*/true); + return printEntity(Node, depth, asPrefixContext, + TypePrinting::FunctionStyle, + /*hasName*/ true); case Node::Kind::Subscript: - return printEntity(Node, asPrefixContext, TypePrinting::FunctionStyle, - /*hasName*/false, /*ExtraName*/"", /*ExtraIndex*/-1, - "subscript"); + return printEntity( + Node, depth, asPrefixContext, TypePrinting::FunctionStyle, + /*hasName*/ false, /*ExtraName*/ "", /*ExtraIndex*/ -1, "subscript"); case Node::Kind::GenericTypeParamDecl: - return printEntity(Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/true); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ true); case Node::Kind::ExplicitClosure: - return printEntity(Node, asPrefixContext, - Options.ShowFunctionArgumentTypes ? - TypePrinting::FunctionStyle : TypePrinting::NoType, - /*hasName*/false, "closure #", - (int)Node->getChild(1)->getIndex() + 1); + return printEntity( + Node, depth, asPrefixContext, + Options.ShowFunctionArgumentTypes ? TypePrinting::FunctionStyle + : TypePrinting::NoType, + /*hasName*/ false, "closure #", (int)Node->getChild(1)->getIndex() + 1); case Node::Kind::ImplicitClosure: - return printEntity(Node, asPrefixContext, - Options.ShowFunctionArgumentTypes ? - TypePrinting::FunctionStyle : TypePrinting::NoType, - /*hasName*/false, "implicit closure #", + return printEntity(Node, depth, asPrefixContext, + Options.ShowFunctionArgumentTypes + ? TypePrinting::FunctionStyle + : TypePrinting::NoType, + /*hasName*/ false, "implicit closure #", (int)Node->getChild(1)->getIndex() + 1); case Node::Kind::Global: - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::Suffix: if (Options.DisplayUnmangledSuffix) { @@ -1277,31 +1304,32 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { } return nullptr; case Node::Kind::Initializer: - return printEntity(Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/false, "variable initialization expression"); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, "variable initialization expression"); case Node::Kind::PropertyWrapperBackingInitializer: - return printEntity( - Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/false, "property wrapper backing initializer"); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, + "property wrapper backing initializer"); case Node::Kind::PropertyWrapperInitFromProjectedValue: - return printEntity( - Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/false, "property wrapper init from projected value"); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, + "property wrapper init from projected value"); case Node::Kind::DefaultArgumentInitializer: - return printEntity(Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/false, "default argument ", + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, "default argument ", (int)Node->getChild(1)->getIndex()); case Node::Kind::DeclContext: - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::Type: - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMangling: if (Node->getChild(0)->getKind() == Node::Kind::LabelList) { - printFunctionType(Node->getChild(0), Node->getChild(1)->getFirstChild()); + printFunctionType(Node->getChild(0), Node->getChild(1)->getFirstChild(), + depth); } else { - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); } return nullptr; case Node::Kind::Class: @@ -1310,10 +1338,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::Protocol: case Node::Kind::TypeAlias: case Node::Kind::OtherNominalType: - return printEntity(Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/true); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ true); case Node::Kind::LocalDeclName: - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); if (Options.DisplayLocalNameContexts) Printer << " #" << (Node->getChild(0)->getIndex() + 1); return nullptr; @@ -1322,7 +1350,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Options.ShowPrivateDiscriminators) Printer << '('; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); if (Options.ShowPrivateDiscriminators) Printer << " in " << Node->getChild(0)->getText() << ')'; @@ -1334,7 +1362,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; case Node::Kind::RelatedEntityDeclName: Printer << "related decl '" << Node->getFirstChild()->getText() << "' for "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::Module: if (Options.DisplayModuleNames) @@ -1358,17 +1386,18 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::CFunctionPointer: case Node::Kind::ObjCBlock: case Node::Kind::EscapingObjCBlock: - printFunctionType(nullptr, Node); + printFunctionType(nullptr, Node, depth); return nullptr; case Node::Kind::ClangType: Printer << Node->getText(); return nullptr; case Node::Kind::ArgumentTuple: - printFunctionParameters(nullptr, Node, Options.ShowFunctionArgumentTypes); + printFunctionParameters(nullptr, Node, depth, + Options.ShowFunctionArgumentTypes); return nullptr; case Node::Kind::Tuple: { Printer << "("; - printChildren(Node, ", "); + printChildren(Node, depth, ", "); Printer << ")"; return nullptr; } @@ -1379,7 +1408,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { auto Type = getChildIf(Node, Node::Kind::Type); assert(Type && "malformed Node::Kind::TupleElement"); - print(Type); + print(Type, depth + 1); if (getChildIf(Node, Node::Kind::VariadicMarker)) Printer << "..."; @@ -1393,7 +1422,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << " -> " << Node->getText(); else { Printer << " -> "; - printChildren(Node); + printChildren(Node, depth); } return nullptr; case Node::Kind::RetroactiveConformance: @@ -1401,30 +1430,34 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; Printer << "retroactive @ "; - print(Node->getChild(0)); - print(Node->getChild(1)); + print(Node->getChild(0), depth + 1); + print(Node->getChild(1), depth + 1); return nullptr; -#define REF_STORAGE(Name, ...) \ - case Node::Kind::Name: \ - Printer << keywordOf(ReferenceOwnership::Name) << " "; \ - print(Node->getChild(0)); \ +#define REF_STORAGE(Name, ...) \ + case Node::Kind::Name: \ + Printer << keywordOf(ReferenceOwnership::Name) << " "; \ + print(Node->getChild(0), depth + 1); \ return nullptr; #include "swift/AST/ReferenceStorage.def" case Node::Kind::InOut: Printer << "inout "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); + return nullptr; + case Node::Kind::Isolated: + Printer << "isolated "; + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::Shared: Printer << "__shared "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::Owned: Printer << "__owned "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::NoDerivative: Printer << "@noDerivative "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::NonObjCAttribute: Printer << "@nonobjc "; @@ -1442,40 +1475,42 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << "override "; return nullptr; case Node::Kind::FunctionSignatureSpecialization: - printSpecializationPrefix(Node, "function signature specialization"); + printSpecializationPrefix(Node, "function signature specialization", depth); return nullptr; case Node::Kind::GenericPartialSpecialization: - printSpecializationPrefix(Node, "generic partial specialization", + printSpecializationPrefix(Node, "generic partial specialization", depth, "Signature = "); return nullptr; case Node::Kind::GenericPartialSpecializationNotReAbstracted: printSpecializationPrefix(Node, - "generic not-reabstracted partial specialization", "Signature = "); + "generic not-reabstracted partial specialization", + depth, "Signature = "); return nullptr; case Node::Kind::GenericSpecialization: case Node::Kind::GenericSpecializationInResilienceDomain: - printSpecializationPrefix(Node, "generic specialization"); + printSpecializationPrefix(Node, "generic specialization", depth); return nullptr; case Node::Kind::GenericSpecializationPrespecialized: - printSpecializationPrefix(Node, "generic pre-specialization"); + printSpecializationPrefix(Node, "generic pre-specialization", depth); return nullptr; case Node::Kind::GenericSpecializationNotReAbstracted: - printSpecializationPrefix(Node, "generic not re-abstracted specialization"); + printSpecializationPrefix(Node, "generic not re-abstracted specialization", + depth); return nullptr; case Node::Kind::InlinedGenericFunction: - printSpecializationPrefix(Node, "inlined generic function"); + printSpecializationPrefix(Node, "inlined generic function", depth); return nullptr; case Node::Kind::IsSerialized: Printer << "serialized"; return nullptr; case Node::Kind::GenericSpecializationParam: - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); for (unsigned i = 1, e = Node->getNumChildren(); i < e; ++i) { if (i == 1) Printer << " with "; else Printer << " and "; - print(Node->getChild(i)); + print(Node->getChild(i), depth + 1); } return nullptr; case Node::Kind::FunctionSignatureSpecializationReturn: @@ -1584,61 +1619,61 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; case Node::Kind::LazyProtocolWitnessTableAccessor: Printer << "lazy protocol witness table accessor for type "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << " and conformance "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::LazyProtocolWitnessTableCacheVariable: Printer << "lazy protocol witness table cache variable for type "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << " and conformance "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::ProtocolSelfConformanceWitnessTable: Printer << "protocol self-conformance witness table for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::ProtocolWitnessTableAccessor: Printer << "protocol witness table accessor for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::ProtocolWitnessTable: Printer << "protocol witness table for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::ProtocolWitnessTablePattern: Printer << "protocol witness table pattern for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::GenericProtocolWitnessTable: Printer << "generic protocol witness table for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::GenericProtocolWitnessTableInstantiationFunction: Printer << "instantiation function for generic protocol witness table for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::ResilientProtocolWitnessTable: Printer << "resilient protocol witness table for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::VTableThunk: { Printer << "vtable thunk for "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << " dispatching to "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; } case Node::Kind::ProtocolSelfConformanceWitness: { Printer << "protocol self-conformance witness for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; } case Node::Kind::ProtocolWitness: { Printer << "protocol witness for "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << " in conformance "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; } case Node::Kind::PartialApplyForwarder: @@ -1649,7 +1684,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Node->hasChildren()) { Printer << " for "; - printChildren(Node); + printChildren(Node, depth); } return nullptr; case Node::Kind::PartialApplyObjCForwarder: @@ -1660,7 +1695,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Node->hasChildren()) { Printer << " for "; - printChildren(Node); + printChildren(Node, depth); } return nullptr; case Node::Kind::KeyPathGetterThunkHelper: @@ -1670,13 +1705,13 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { else Printer << "key path setter for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << " : "; for (unsigned index = 1; index < Node->getNumChildren(); ++index) { auto Child = Node->getChild(index); if (Child->getKind() == Node::Kind::IsSerialized) Printer << ", "; - print(Child); + print(Child, depth + 1); } return nullptr; case Node::Kind::KeyPathEqualsThunkHelper: @@ -1688,15 +1723,13 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { unsigned lastChildIndex = Node->getNumChildren(); auto lastChild = Node->getChild(lastChildIndex - 1); - bool isSerialized = false; if (lastChild->getKind() == Node::Kind::IsSerialized) { - isSerialized = true; --lastChildIndex; lastChild = Node->getChild(lastChildIndex - 1); } if (lastChild->getKind() == Node::Kind::DependentGenericSignature) { - print(lastChild); + print(lastChild, depth + 1); --lastChildIndex; } @@ -1704,29 +1737,29 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { for (unsigned i = 0; i < lastChildIndex; ++i) { if (i != 0) Printer << ", "; - print(Node->getChild(i)); + print(Node->getChild(i), depth + 1); } Printer << ")"; return nullptr; } case Node::Kind::FieldOffset: { - print(Node->getChild(0)); // directness + print(Node->getChild(0), depth + 1); // directness Printer << "field offset for "; auto entity = Node->getChild(1); - print(entity, /*asContext*/ false); + print(entity, depth + 1, /*asContext*/ false); return nullptr; } case Node::Kind::EnumCase: { Printer << "enum case for "; auto entity = Node->getChild(0); - print(entity, /*asContext*/ false); + print(entity, depth + 1, /*asContext*/ false); return nullptr; } case Node::Kind::ReabstractionThunk: case Node::Kind::ReabstractionThunkHelper: { if (Options.ShortenThunk) { Printer << "thunk for "; - print(Node->getChild(Node->getNumChildren() - 1)); + print(Node->getChild(Node->getNumChildren() - 1), depth + 1); return nullptr; } Printer << "reabstraction thunk "; @@ -1736,13 +1769,19 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Node->getNumChildren() == 3) { auto generics = Node->getChild(0); idx = 1; - print(generics); + print(generics, depth + 1); Printer << " "; } Printer << "from "; - print(Node->getChild(idx + 1)); + print(Node->getChild(idx + 1), depth + 1); Printer << " to "; - print(Node->getChild(idx)); + print(Node->getChild(idx), depth + 1); + return nullptr; + } + case Node::Kind::ReabstractionThunkHelperWithGlobalActor: { + print(Node->getChild(0), depth + 1); + Printer << " with global actor constraint "; + print(Node->getChild(1), depth + 1); return nullptr; } case Node::Kind::ReabstractionThunkHelperWithSelf: { @@ -1751,15 +1790,15 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Node->getNumChildren() == 4) { auto generics = Node->getChild(0); idx = 1; - print(generics); + print(generics, depth + 1); Printer << " "; } Printer << "from "; - print(Node->getChild(idx + 2)); + print(Node->getChild(idx + 2), depth + 1); Printer << " to "; - print(Node->getChild(idx + 1)); + print(Node->getChild(idx + 1), depth + 1); Printer << " self "; - print(Node->getChild(idx)); + print(Node->getChild(idx), depth + 1); return nullptr; } case Node::Kind::AutoDiffFunction: @@ -1774,7 +1813,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { auto resultIndices = Node->getChild(prefixEndIndex + 2); if (kind == Node::Kind::AutoDiffDerivativeVTableThunk) Printer << "vtable thunk for "; - print(funcKind); + print(funcKind, depth + 1); Printer << " of "; NodePointer optionalGenSig = nullptr; for (unsigned i = 0; i < prefixEndIndex; ++i) { @@ -1785,17 +1824,17 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { optionalGenSig = Node->getChild(i); break; } - print(Node->getChild(i)); + print(Node->getChild(i), depth + 1); } if (Options.ShortenThunk) return nullptr; Printer << " with respect to parameters "; - print(paramIndices); + print(paramIndices, depth + 1); Printer << " and results "; - print(resultIndices); + print(resultIndices, depth + 1); if (optionalGenSig && Options.DisplayWhereClauses) { Printer << " with "; - print(optionalGenSig); + print(optionalGenSig, depth + 1); } return nullptr; } @@ -1806,22 +1845,22 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { auto toType = *childIt++; if (Options.ShortenThunk) { Printer << "for "; - print(fromType); + print(fromType, depth + 1); return nullptr; } NodePointer optionalGenSig = (*childIt)->getKind() == Node::Kind::DependentGenericSignature ? *childIt++ : nullptr; Printer << "for "; - print(*childIt++); // kind + print(*childIt++, depth + 1); // kind if (optionalGenSig) { - print(optionalGenSig); + print(optionalGenSig, depth + 1); Printer << ' '; } Printer << " from "; - print(fromType); + print(fromType, depth + 1); Printer << " to "; - print(toType); + print(toType, depth + 1); return nullptr; } case Node::Kind::AutoDiffSubsetParametersThunk: { @@ -1831,26 +1870,26 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { auto resultIndices = Node->getChild(currentIndex--); auto paramIndices = Node->getChild(currentIndex--); auto kind = Node->getChild(currentIndex--); - print(kind); + print(kind, depth + 1); Printer << " from "; // Print the "from" thing. if (currentIndex == 0) { - print(Node->getFirstChild()); // the "from" type + print(Node->getFirstChild(), depth + 1); // the "from" type } else { for (unsigned i = 0; i < currentIndex; ++i) // the "from" global - print(Node->getChild(i)); + print(Node->getChild(i), depth + 1); } if (Options.ShortenThunk) return nullptr; Printer << " with respect to parameters "; - print(paramIndices); + print(paramIndices, depth + 1); Printer << " and results "; - print(resultIndices); + print(resultIndices, depth + 1); Printer << " to parameters "; - print(toParamIndices); + print(toParamIndices, depth + 1); if (currentIndex > 0) { Printer << " of type "; - print(Node->getChild(currentIndex)); // "to" type + print(Node->getChild(currentIndex), depth + 1); // "to" type } return nullptr; } @@ -1899,17 +1938,17 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { for (auto numChildren = Node->getNumChildren(); idx < numChildren && Node->getChild(idx)->getKind() != Node::Kind::Index; ++idx) - print(Node->getChild(idx)); + print(Node->getChild(idx), depth + 1); ++idx; // kind (handled earlier) Printer << " with respect to parameters "; - print(Node->getChild(idx++)); // parameter indices + print(Node->getChild(idx++), depth + 1); // parameter indices Printer << " and results "; - print(Node->getChild(idx++)); + print(Node->getChild(idx++), depth + 1); if (idx < Node->getNumChildren()) { auto *genSig = Node->getChild(idx); assert(genSig->getKind() == Node::Kind::DependentGenericSignature); Printer << " with "; - print(genSig); + print(genSig, depth + 1); } return nullptr; } @@ -1964,159 +2003,159 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; case Node::Kind::GenericTypeMetadataPattern: Printer << "generic type metadata pattern for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::Metaclass: Printer << "metaclass for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::ProtocolSelfConformanceDescriptor: Printer << "protocol self-conformance descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ProtocolConformanceDescriptor: Printer << "protocol conformance descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ProtocolDescriptor: Printer << "protocol descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ProtocolRequirementsBaseDescriptor: Printer << "protocol requirements base descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::FullTypeMetadata: Printer << "full type metadata for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMetadata: Printer << "type metadata for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMetadataAccessFunction: Printer << "type metadata accessor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMetadataInstantiationCache: Printer << "type metadata instantiation cache for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMetadataInstantiationFunction: Printer << "type metadata instantiation function for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMetadataSingletonInitializationCache: Printer << "type metadata singleton initialization cache for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMetadataCompletionFunction: Printer << "type metadata completion function for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMetadataDemanglingCache: Printer << "demangling cache variable for type metadata for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::TypeMetadataLazyCache: Printer << "lazy cache variable for type metadata for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::AssociatedConformanceDescriptor: Printer << "associated conformance descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << "."; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << ": "; - print(Node->getChild(2)); + print(Node->getChild(2), depth + 1); return nullptr; case Node::Kind::DefaultAssociatedConformanceAccessor: Printer << "default associated conformance accessor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << "."; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << ": "; - print(Node->getChild(2)); + print(Node->getChild(2), depth + 1); return nullptr; case Node::Kind::AssociatedTypeDescriptor: Printer << "associated type descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::AssociatedTypeMetadataAccessor: Printer << "associated type metadata accessor for "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << " in "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::BaseConformanceDescriptor: Printer << "base conformance descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << ": "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::DefaultAssociatedTypeMetadataAccessor: Printer << "default associated type metadata accessor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::AssociatedTypeWitnessTableAccessor: Printer << "associated type witness table accessor for "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << " : "; - print(Node->getChild(2)); + print(Node->getChild(2), depth + 1); Printer << " in "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::BaseWitnessTableAccessor: Printer << "base witness table accessor for "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << " in "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ClassMetadataBaseOffset: Printer << "class metadata base offset for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::PropertyDescriptor: Printer << "property descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::NominalTypeDescriptor: Printer << "nominal type descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OpaqueTypeDescriptor: Printer << "opaque type descriptor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OpaqueTypeDescriptorAccessor: Printer << "opaque type descriptor accessor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OpaqueTypeDescriptorAccessorImpl: Printer << "opaque type descriptor accessor impl for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OpaqueTypeDescriptorAccessorKey: Printer << "opaque type descriptor accessor key for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::OpaqueTypeDescriptorAccessorVar: Printer << "opaque type descriptor accessor var for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::CoroutineContinuationPrototype: Printer << "coroutine continuation prototype for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ValueWitness: Printer << toString(ValueWitnessKind(Node->getFirstChild()->getIndex())); if (Options.ShortenValueWitness) Printer << " for "; else Printer << " value witness for "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::ValueWitnessTable: Printer << "value witness table for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::BoundGenericClass: case Node::Kind::BoundGenericStructure: @@ -2124,7 +2163,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::BoundGenericProtocol: case Node::Kind::BoundGenericOtherNominalType: case Node::Kind::BoundGenericTypeAlias: - printBoundGeneric(Node); + printBoundGeneric(Node, depth); return nullptr; case Node::Kind::DynamicSelf: Printer << "Self"; @@ -2132,19 +2171,19 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::SILBoxType: { Printer << "@box "; NodePointer type = Node->getChild(0); - print(type); + print(type, depth + 1); return nullptr; } case Node::Kind::Metatype: { unsigned Idx = 0; if (Node->getNumChildren() == 2) { NodePointer repr = Node->getChild(Idx); - print(repr); + print(repr, depth + 1); Printer << " "; ++Idx; } NodePointer type = Node->getChild(Idx)->getChild(0); - printWithParens(type); + printWithParens(type, depth); if (isExistentialType(type)) { Printer << ".Protocol"; } else { @@ -2156,13 +2195,13 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { unsigned Idx = 0; if (Node->getNumChildren() == 2) { NodePointer repr = Node->getChild(Idx); - print(repr); + print(repr, depth + 1); Printer << " "; ++Idx; } NodePointer type = Node->getChild(Idx); - print(type); + print(type, depth + 1); Printer << ".Type"; return nullptr; } @@ -2171,7 +2210,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; } case Node::Kind::AssociatedTypeRef: - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << '.' << Node->getChild(1)->getText(); return nullptr; case Node::Kind::ProtocolList: { @@ -2181,7 +2220,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (type_list->getNumChildren() == 0) Printer << "Any"; else - printChildren(type_list, " & "); + printChildren(type_list, depth, " & "); return nullptr; } case Node::Kind::ProtocolListWithClass: { @@ -2189,12 +2228,12 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; NodePointer protocols = Node->getChild(0); NodePointer superclass = Node->getChild(1); - print(superclass); + print(superclass, depth + 1); Printer << " & "; if (protocols->getNumChildren() < 1) return nullptr; NodePointer type_list = protocols->getChild(0); - printChildren(type_list, " & "); + printChildren(type_list, depth, " & "); return nullptr; } case Node::Kind::ProtocolListWithAnyObject: { @@ -2205,7 +2244,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; NodePointer type_list = protocols->getChild(0); if (type_list->getNumChildren() > 0) { - printChildren(type_list, " & "); + printChildren(type_list, depth, " & "); Printer << " & "; } if (Options.QualifyEntities && Options.DisplayStdlibModule) @@ -2217,73 +2256,76 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { // Don't print for now. return nullptr; case Node::Kind::OwningAddressor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "owningAddressor"); case Node::Kind::OwningMutableAddressor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "owningMutableAddressor"); case Node::Kind::NativeOwningAddressor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "nativeOwningAddressor"); case Node::Kind::NativeOwningMutableAddressor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "nativeOwningMutableAddressor"); case Node::Kind::NativePinningAddressor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "nativePinningAddressor"); case Node::Kind::NativePinningMutableAddressor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "nativePinningMutableAddressor"); case Node::Kind::UnsafeAddressor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "unsafeAddressor"); case Node::Kind::UnsafeMutableAddressor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "unsafeMutableAddressor"); case Node::Kind::GlobalGetter: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "getter"); case Node::Kind::Getter: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "getter"); case Node::Kind::Setter: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "setter"); case Node::Kind::MaterializeForSet: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "materializeForSet"); case Node::Kind::WillSet: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "willset"); case Node::Kind::DidSet: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "didset"); case Node::Kind::ReadAccessor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "read"); case Node::Kind::ModifyAccessor: - return printAbstractStorage(Node->getFirstChild(), asPrefixContext, + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "modify"); case Node::Kind::Allocator: - return printEntity(Node, asPrefixContext, TypePrinting::FunctionStyle, - /*hasName*/false, isClassType(Node->getChild(0)) ? - "__allocating_init" : "init"); + return printEntity( + Node, depth, asPrefixContext, TypePrinting::FunctionStyle, + /*hasName*/ false, + isClassType(Node->getChild(0)) ? "__allocating_init" : "init"); case Node::Kind::Constructor: - return printEntity(Node, asPrefixContext, TypePrinting::FunctionStyle, - /*hasName*/Node->getNumChildren() > 2, "init"); + return printEntity(Node, depth, asPrefixContext, + TypePrinting::FunctionStyle, + /*hasName*/ Node->getNumChildren() > 2, "init"); case Node::Kind::Destructor: - return printEntity(Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/false, "deinit"); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, "deinit"); case Node::Kind::Deallocator: - return printEntity(Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/false, isClassType(Node->getChild(0)) ? - "__deallocating_deinit" : "deinit"); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, + isClassType(Node->getChild(0)) ? "__deallocating_deinit" + : "deinit"); case Node::Kind::IVarInitializer: - return printEntity(Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/false, "__ivar_initializer"); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, "__ivar_initializer"); case Node::Kind::IVarDestroyer: - return printEntity(Node, asPrefixContext, TypePrinting::NoType, - /*hasName*/false, "__ivar_destroyer"); + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, "__ivar_destroyer"); case Node::Kind::ProtocolConformance: { NodePointer child0 = Node->getChild(0); NodePointer child1 = Node->getChild(1); @@ -2291,24 +2333,24 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Node->getNumChildren() == 4) { // TODO: check if this is correct Printer << "property behavior storage of "; - print(child2); + print(child2, depth + 1); Printer << " in "; - print(child0); + print(child0, depth + 1); Printer << " : "; - print(child1); + print(child1, depth + 1); } else { - print(child0); + print(child0, depth + 1); if (Options.DisplayProtocolConformances) { Printer << " : "; - print(child1); + print(child1, depth + 1); Printer << " in "; - print(child2); + print(child2, depth + 1); } } return nullptr; } case Node::Kind::TypeList: - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::LabelList: return nullptr; @@ -2354,7 +2396,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { break; case 2: Printer << Node->getChild(0)->getText() << ", mangledCType: \""; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << '"'; break; default: @@ -2367,37 +2409,37 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; case Node::Kind::ImplErrorResult: Printer << "@error "; - printChildren(Node, " "); + printChildren(Node, depth, " "); return nullptr; case Node::Kind::ImplYield: Printer << "@yields "; - printChildren(Node, " "); + printChildren(Node, depth, " "); return nullptr; case Node::Kind::ImplParameter: case Node::Kind::ImplResult: // Children: `convention, differentiability?, type` // Print convention. - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << " "; // Print differentiability, if it exists. if (Node->getNumChildren() == 3) - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); // Print type. - print(Node->getLastChild()); + print(Node->getLastChild(), depth + 1); return nullptr; case Node::Kind::ImplFunctionType: - printImplFunctionType(Node); + printImplFunctionType(Node, depth); return nullptr; case Node::Kind::ImplInvocationSubstitutions: Printer << "for <"; - printChildren(Node->getChild(0), ", "); + printChildren(Node->getChild(0), depth, ", "); Printer << '>'; return nullptr; case Node::Kind::ImplPatternSubstitutions: Printer << "@substituted "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << " for <"; - printChildren(Node->getChild(1), ", "); + printChildren(Node->getChild(1), depth, ", "); Printer << '>'; return nullptr; case Node::Kind::ErrorType: @@ -2441,7 +2483,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { for (unsigned i = depth; i < numChildren; ++i) { if (i > depth) Printer << ", "; - print(Node->getChild(i)); + print(Node->getChild(i), depth + 1); } } } @@ -2454,15 +2496,15 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::DependentGenericConformanceRequirement: { NodePointer type = Node->getChild(0); NodePointer reqt = Node->getChild(1); - print(type); + print(type, depth + 1); Printer << ": "; - print(reqt); + print(reqt, depth + 1); return nullptr; } case Node::Kind::DependentGenericLayoutRequirement: { NodePointer type = Node->getChild(0); NodePointer layout = Node->getChild(1); - print(type); + print(type, depth + 1); Printer << ": "; assert(layout->getKind() == Node::Kind::Identifier); assert(layout->getText().size() == 1); @@ -2488,10 +2530,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << name; if (Node->getNumChildren() > 2) { Printer << "("; - print(Node->getChild(2)); + print(Node->getChild(2), depth + 1); if (Node->getNumChildren() > 3) { Printer << ", "; - print(Node->getChild(3)); + print(Node->getChild(3), depth + 1); } Printer << ")"; } @@ -2500,10 +2542,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::DependentGenericSameTypeRequirement: { NodePointer fst = Node->getChild(0); NodePointer snd = Node->getChild(1); - - print(fst); + + print(fst, depth + 1); Printer << " == "; - print(snd); + print(snd, depth + 1); return nullptr; } case Node::Kind::DependentGenericParamType: { @@ -2515,43 +2557,43 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::DependentGenericType: { NodePointer sig = Node->getChild(0); NodePointer depTy = Node->getChild(1); - print(sig); + print(sig, depth + 1); if (needSpaceBeforeType(depTy)) Printer << ' '; - print(depTy); + print(depTy, depth + 1); return nullptr; } case Node::Kind::DependentMemberType: { NodePointer base = Node->getChild(0); - print(base); + print(base, depth + 1); Printer << '.'; NodePointer assocTy = Node->getChild(1); - print(assocTy); + print(assocTy, depth + 1); return nullptr; } case Node::Kind::DependentAssociatedTypeRef: { if (Node->getNumChildren() > 1) { - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << '.'; } - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; } case Node::Kind::ReflectionMetadataBuiltinDescriptor: Printer << "reflection metadata builtin descriptor "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ReflectionMetadataFieldDescriptor: Printer << "reflection metadata field descriptor "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ReflectionMetadataAssocTypeDescriptor: Printer << "reflection metadata associated type descriptor "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ReflectionMetadataSuperclassDescriptor: Printer << "reflection metadata superclass descriptor "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ConcurrentFunctionType: @@ -2578,6 +2620,14 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << ' '; return nullptr; } + case Node::Kind::GlobalActorFunctionType: { + if (Node->getNumChildren() > 0) { + Printer << '@'; + print(Node->getChild(0), depth + 1); + Printer << ' '; + } + return nullptr; + } case Node::Kind::AsyncAnnotation: Printer << " async "; return nullptr; @@ -2603,17 +2653,17 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { assert(signature->getKind() == Node::Kind::DependentGenericSignature); genericArgs = Node->getChild(2); assert(genericArgs->getKind() == Node::Kind::TypeList); - - print(signature); + + print(signature, depth + 1); Printer << ' '; } - print(layout); + print(layout, depth + 1); if (genericArgs) { Printer << " <"; for (unsigned i = 0, e = genericArgs->getNumChildren(); i < e; ++i) { if (i > 0) Printer << ", "; - print(genericArgs->getChild(i)); + print(genericArgs->getChild(i), depth + 1); } Printer << '>'; } @@ -2625,7 +2675,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (i > 0) Printer << ','; Printer << ' '; - print(Node->getChild(i)); + print(Node->getChild(i), depth + 1); } Printer << " }"; return nullptr; @@ -2636,89 +2686,89 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { : "var "); assert(Node->getNumChildren() == 1 && Node->getChild(0)->getKind() == Node::Kind::Type); - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::AssocTypePath: - printChildren(Node->begin(), Node->end(), "."); - return nullptr; + printChildren(Node->begin(), Node->end(), depth, "."); + return nullptr; case Node::Kind::ModuleDescriptor: Printer << "module descriptor "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::AnonymousDescriptor: Printer << "anonymous descriptor "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::ExtensionDescriptor: Printer << "extension descriptor "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::AssociatedTypeGenericParamRef: Printer << "generic parameter reference for associated type "; - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::AnyProtocolConformanceList: - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::ConcreteProtocolConformance: Printer << "concrete protocol conformance "; if (Node->hasIndex()) Printer << "#" << Node->getIndex() << " "; - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::DependentAssociatedConformance: Printer << "dependent associated conformance "; - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::DependentProtocolConformanceAssociated: Printer << "dependent associated protocol conformance "; printOptionalIndex(Node->getChild(2)); - print(Node->getChild(0)); - print(Node->getChild(1)); + print(Node->getChild(0), depth + 1); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::DependentProtocolConformanceInherited: Printer << "dependent inherited protocol conformance "; printOptionalIndex(Node->getChild(2)); - print(Node->getChild(0)); - print(Node->getChild(1)); + print(Node->getChild(0), depth + 1); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::DependentProtocolConformanceRoot: Printer << "dependent root protocol conformance "; printOptionalIndex(Node->getChild(2)); - print(Node->getChild(0)); - print(Node->getChild(1)); + print(Node->getChild(0), depth + 1); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::ProtocolConformanceRefInTypeModule: Printer << "protocol conformance ref (type's module) "; - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::ProtocolConformanceRefInProtocolModule: Printer << "protocol conformance ref (protocol's module) "; - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::ProtocolConformanceRefInOtherModule: Printer << "protocol conformance ref (retroactive) "; - printChildren(Node); + printChildren(Node, depth); return nullptr; case Node::Kind::SugaredOptional: - printWithParens(Node->getChild(0)); + printWithParens(Node->getChild(0), depth); Printer << "?"; return nullptr; case Node::Kind::SugaredArray: Printer << "["; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << "]"; return nullptr; case Node::Kind::SugaredDictionary: Printer << "["; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << " : "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); Printer << "]"; return nullptr; case Node::Kind::SugaredParen: Printer << "("; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << ")"; return nullptr; case Node::Kind::OpaqueReturnType: @@ -2726,36 +2776,36 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { return nullptr; case Node::Kind::OpaqueReturnTypeOf: Printer << "<>"; return nullptr; case Node::Kind::OpaqueType: - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << '.'; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::AccessorFunctionReference: Printer << "accessor function at " << Node->getIndex(); return nullptr; case Node::Kind::CanonicalSpecializedGenericMetaclass: Printer << "specialized generic metaclass for "; - print(Node->getFirstChild()); + print(Node->getFirstChild(), depth + 1); return nullptr; case Node::Kind::CanonicalSpecializedGenericTypeMetadataAccessFunction: Printer << "canonical specialized generic type metadata accessor for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::MetadataInstantiationCache: Printer << "metadata instantiation cache for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::NoncanonicalSpecializedGenericTypeMetadata: Printer << "noncanonical specialized generic type metadata for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::NoncanonicalSpecializedGenericTypeMetadataCache: Printer << "cache variable for noncanonical specialized generic type metadata for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::GlobalVariableOnceToken: case Node::Kind::GlobalVariableOnceFunction: @@ -2763,18 +2813,18 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { ? "one-time initialization token for " : "one-time initialization function for "); printContext(Node->getChild(0)); - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); return nullptr; case Node::Kind::GlobalVariableOnceDeclList: if (Node->getNumChildren() == 1) { - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); } else { Printer << '('; for (unsigned i = 0, e = Node->getNumChildren(); i < e; ++i) { if (i != 0) { Printer << ", "; } - print(Node->getChild(i)); + print(Node->getChild(i), depth + 1); } Printer << ')'; } @@ -2785,10 +2835,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::ObjCAsyncCompletionHandlerImpl: Printer << "@objc completion handler block implementation for "; if (Node->getNumChildren() >= 4) - print(Node->getChild(3)); - print(Node->getChild(0)); + print(Node->getChild(3), depth + 1); + print(Node->getChild(0), depth + 1); Printer << " with result type "; - print(Node->getChild(1)); + print(Node->getChild(1), depth + 1); switch (Node->getChild(2)->getIndex()) { case 0: break; @@ -2806,7 +2856,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::CanonicalPrespecializedGenericTypeCachingOnceToken: Printer << "flag for loading of canonical specialized generic type " "metadata for "; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); return nullptr; case Node::Kind::AsyncFunctionPointer: Printer << "async function pointer to "; @@ -2814,7 +2864,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::AsyncAwaitResumePartialFunction: if (Options.ShowAsyncResumePartial) { Printer << "("; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << ")"; Printer << " await resume partial function for "; } @@ -2822,7 +2872,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::AsyncSuspendResumePartialFunction: if (Options.ShowAsyncResumePartial) { Printer << "("; - print(Node->getChild(0)); + print(Node->getChild(0), depth + 1); Printer << ")"; Printer << " suspend resume partial function for "; } @@ -2832,26 +2882,26 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { printer_unreachable("bad node kind!"); } -NodePointer NodePrinter::printAbstractStorage(NodePointer Node, +NodePointer NodePrinter::printAbstractStorage(NodePointer Node, unsigned depth, bool asPrefixContent, StringRef ExtraName) { switch (Node->getKind()) { case Node::Kind::Variable: - return printEntity(Node, asPrefixContent, TypePrinting::WithColon, - /*hasName*/true, ExtraName); + return printEntity(Node, depth, asPrefixContent, TypePrinting::WithColon, + /*hasName*/ true, ExtraName); case Node::Kind::Subscript: - return printEntity(Node, asPrefixContent, TypePrinting::WithColon, - /*hasName*/false, ExtraName, /*ExtraIndex*/-1, + return printEntity(Node, depth, asPrefixContent, TypePrinting::WithColon, + /*hasName*/ false, ExtraName, /*ExtraIndex*/ -1, "subscript"); default: printer_unreachable("Not an abstract storage node"); } } -NodePointer NodePrinter:: -printEntity(NodePointer Entity, bool asPrefixContext, TypePrinting TypePr, - bool hasName, StringRef ExtraName, int ExtraIndex, - StringRef OverwriteName) { +NodePointer NodePrinter::printEntity(NodePointer Entity, unsigned depth, + bool asPrefixContext, TypePrinting TypePr, + bool hasName, StringRef ExtraName, + int ExtraIndex, StringRef OverwriteName) { NodePointer genericFunctionTypeList = nullptr; if (Entity->getKind() == Node::Kind::BoundGenericFunction) { @@ -2883,7 +2933,7 @@ printEntity(NodePointer Entity, bool asPrefixContext, TypePrinting TypePr, PostfixContext = Context; } else { size_t CurrentPos = Printer.getStringRef().size(); - PostfixContext = print(Context, /*asPrefixContext*/true); + PostfixContext = print(Context, depth + 1, /*asPrefixContext*/ true); // Was the context printed as prefix? if (Printer.getStringRef().size() != CurrentPos) @@ -2904,10 +2954,10 @@ printEntity(NodePointer Entity, bool asPrefixContext, TypePrinting TypePr, } else { auto Name = Entity->getChild(1); if (Name->getKind() != Node::Kind::PrivateDeclName) - print(Name); + print(Name, depth + 1); if (auto PrivateName = getChildIf(Entity, Node::Kind::PrivateDeclName)) - print(PrivateName); + print(PrivateName, depth + 1); } if (Printer.getStringRef().size() != CurrentPos && !ExtraName.empty()) Printer << '.'; @@ -2942,13 +2992,13 @@ printEntity(NodePointer Entity, bool asPrefixContext, TypePrinting TypePr, if (TypePr == TypePrinting::WithColon) { if (Options.DisplayEntityTypes) { Printer << " : "; - printEntityType(Entity, type, genericFunctionTypeList); + printEntityType(Entity, type, genericFunctionTypeList, depth); } } else { assert(TypePr == TypePrinting::FunctionStyle); if (MultiWordName || needSpaceBeforeType(type)) Printer << ' '; - printEntityType(Entity, type, genericFunctionTypeList); + printEntityType(Entity, type, genericFunctionTypeList, depth); } } if (!asPrefixContext && PostfixContext && @@ -2962,33 +3012,34 @@ printEntity(NodePointer Entity, bool asPrefixContext, TypePrinting TypePr, } else { Printer << " in "; } - print(PostfixContext); + print(PostfixContext, depth + 1); PostfixContext = nullptr; } return PostfixContext; }; void NodePrinter::printEntityType(NodePointer Entity, NodePointer type, - NodePointer genericFunctionTypeList) { + NodePointer genericFunctionTypeList, + unsigned depth) { NodePointer labelList = getChildIf(Entity, Node::Kind::LabelList); if (labelList || genericFunctionTypeList) { if (genericFunctionTypeList) { Printer << "<"; - printChildren(genericFunctionTypeList, ", "); + printChildren(genericFunctionTypeList, depth, ", "); Printer << ">"; } if (type->getKind() == Node::Kind::DependentGenericType) { if (!genericFunctionTypeList) - print(type->getChild(0)); // generic signature + print(type->getChild(0), depth + 1); // generic signature auto dependentType = type->getChild(1); if (needSpaceBeforeType(dependentType)) Printer << ' '; type = dependentType->getFirstChild(); } - printFunctionType(labelList, type); + printFunctionType(labelList, type, depth); } else { - print(type); + print(type, depth + 1); } } diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index 7e8336f569636..b9acc42f64b7c 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -164,25 +164,32 @@ class OldDemangler { std::vector Substitutions; NameSource Mangled; NodeFactory &Factory; -public: + + static const unsigned MaxDepth = 1024; + +public: OldDemangler(llvm::StringRef mangled, NodeFactory &Factory) : Mangled(mangled), Factory(Factory) {} /// Try to demangle a child node of the given kind. If that fails, /// return; otherwise add it to the parent. -#define DEMANGLE_CHILD_OR_RETURN(PARENT, CHILD_KIND) do { \ - auto _node = demangle##CHILD_KIND(); \ - if (!_node) return nullptr; \ - addChild(PARENT, _node); \ +#define DEMANGLE_CHILD_OR_RETURN(PARENT, CHILD_KIND, DEPTH) \ + do { \ + auto _node = demangle##CHILD_KIND(DEPTH); \ + if (!_node) \ + return nullptr; \ + addChild(PARENT, _node); \ } while (false) /// Try to demangle a child node of the given kind. If that fails, /// return; otherwise add it to the parent. -#define DEMANGLE_CHILD_AS_NODE_OR_RETURN(PARENT, CHILD_KIND) do { \ - auto _kind = demangle##CHILD_KIND(); \ - if (!_kind.hasValue()) return nullptr; \ - addChild(PARENT, Factory.createNode(Node::Kind::CHILD_KIND, \ - unsigned(*_kind))); \ +#define DEMANGLE_CHILD_AS_NODE_OR_RETURN(PARENT, CHILD_KIND, DEPTH) \ + do { \ + auto _kind = demangle##CHILD_KIND(DEPTH); \ + if (!_kind.hasValue()) \ + return nullptr; \ + addChild(PARENT, \ + Factory.createNode(Node::Kind::CHILD_KIND, unsigned(*_kind))); \ } while (false) /// Attempt to demangle the source string. The root node will @@ -200,7 +207,7 @@ class OldDemangler { // First demangle any specialization prefixes. if (Mangled.nextIf("TS")) { do { - DEMANGLE_CHILD_OR_RETURN(topLevel, SpecializedAttribute); + DEMANGLE_CHILD_OR_RETURN(topLevel, SpecializedAttribute, 0); // The Substitution header does not share state with the rest // of the mangling. @@ -224,7 +231,7 @@ class OldDemangler { addChild(topLevel, Factory.createNode(Node::Kind::VTableAttribute)); } - DEMANGLE_CHILD_OR_RETURN(topLevel, Global); + DEMANGLE_CHILD_OR_RETURN(topLevel, Global, 0); // Add a suffix node if there's anything left unmangled. if (!Mangled.isEmpty()) { @@ -235,10 +242,8 @@ class OldDemangler { return topLevel; } - NodePointer demangleTypeName() { - return demangleType(); - } - + NodePointer demangleTypeName(unsigned depth) { return demangleType(depth); } + private: enum class IsVariadic { yes = true, no = false @@ -247,8 +252,8 @@ class OldDemangler { void addChild(NodePointer Parent, NodePointer Child) { Parent->addChild(Child, Factory); } - - llvm::Optional demangleDirectness() { + + llvm::Optional demangleDirectness(unsigned depth) { if (Mangled.nextIf('d')) return Directness::Direct; if (Mangled.nextIf('i')) @@ -256,7 +261,7 @@ class OldDemangler { return llvm::None; } - bool demangleNatural(Node::IndexType &num) { + bool demangleNatural(Node::IndexType &num, unsigned depth) { if (!Mangled) return false; char c = Mangled.next(); @@ -277,15 +282,15 @@ class OldDemangler { } } - bool demangleBuiltinSize(Node::IndexType &num) { - if (!demangleNatural(num)) + bool demangleBuiltinSize(Node::IndexType &num, unsigned depth) { + if (!demangleNatural(num, depth + 1)) return false; if (Mangled.nextIf('_')) return true; return false; } - llvm::Optional demangleValueWitnessKind() { + llvm::Optional demangleValueWitnessKind(unsigned depth) { char Code[2]; if (!Mangled) return llvm::None; @@ -302,8 +307,8 @@ class OldDemangler { return llvm::None; } - NodePointer demangleGlobal() { - if (!Mangled) + NodePointer demangleGlobal(unsigned depth) { + if (depth > OldDemangler::MaxDepth || !Mangled) return nullptr; // Type metadata. @@ -311,43 +316,43 @@ class OldDemangler { if (Mangled.nextIf('P')) { auto pattern = Factory.createNode(Node::Kind::GenericTypeMetadataPattern); - DEMANGLE_CHILD_OR_RETURN(pattern, Type); + DEMANGLE_CHILD_OR_RETURN(pattern, Type, depth + 1); return pattern; } if (Mangled.nextIf('a')) { auto accessor = Factory.createNode(Node::Kind::TypeMetadataAccessFunction); - DEMANGLE_CHILD_OR_RETURN(accessor, Type); + DEMANGLE_CHILD_OR_RETURN(accessor, Type, depth + 1); return accessor; } if (Mangled.nextIf('L')) { auto cache = Factory.createNode(Node::Kind::TypeMetadataLazyCache); - DEMANGLE_CHILD_OR_RETURN(cache, Type); + DEMANGLE_CHILD_OR_RETURN(cache, Type, depth + 1); return cache; } if (Mangled.nextIf('m')) { auto metaclass = Factory.createNode(Node::Kind::Metaclass); - DEMANGLE_CHILD_OR_RETURN(metaclass, Type); + DEMANGLE_CHILD_OR_RETURN(metaclass, Type, depth + 1); return metaclass; } if (Mangled.nextIf('n')) { auto nominalType = Factory.createNode(Node::Kind::NominalTypeDescriptor); - DEMANGLE_CHILD_OR_RETURN(nominalType, Type); + DEMANGLE_CHILD_OR_RETURN(nominalType, Type, depth + 1); return nominalType; } if (Mangled.nextIf('f')) { auto metadata = Factory.createNode(Node::Kind::FullTypeMetadata); - DEMANGLE_CHILD_OR_RETURN(metadata, Type); + DEMANGLE_CHILD_OR_RETURN(metadata, Type, depth + 1); return metadata; } if (Mangled.nextIf('p')) { auto metadata = Factory.createNode(Node::Kind::ProtocolDescriptor); - DEMANGLE_CHILD_OR_RETURN(metadata, ProtocolName); + DEMANGLE_CHILD_OR_RETURN(metadata, ProtocolName, depth + 1); return metadata; } auto metadata = Factory.createNode(Node::Kind::TypeMetadata); - DEMANGLE_CHILD_OR_RETURN(metadata, Type); + DEMANGLE_CHILD_OR_RETURN(metadata, Type, depth + 1); return metadata; } @@ -358,20 +363,20 @@ class OldDemangler { kind = Node::Kind::PartialApplyObjCForwarder; auto forwarder = Factory.createNode(kind); if (Mangled.nextIf("__T")) - DEMANGLE_CHILD_OR_RETURN(forwarder, Global); + DEMANGLE_CHILD_OR_RETURN(forwarder, Global, depth + 1); return forwarder; } // Top-level types, for various consumers. if (Mangled.nextIf('t')) { auto type = Factory.createNode(Node::Kind::TypeMangling); - DEMANGLE_CHILD_OR_RETURN(type, Type); + DEMANGLE_CHILD_OR_RETURN(type, Type, depth + 1); return type; } // Value witnesses. if (Mangled.nextIf('w')) { - llvm::Optional w = demangleValueWitnessKind(); + llvm::Optional w = demangleValueWitnessKind(depth + 1); if (!w.hasValue()) return nullptr; auto witness = @@ -379,7 +384,7 @@ class OldDemangler { NodePointer Idx = Factory.createNode(Node::Kind::Index, unsigned(w.getValue())); witness->addChild(Idx, Factory); - DEMANGLE_CHILD_OR_RETURN(witness, Type); + DEMANGLE_CHILD_OR_RETURN(witness, Type, depth + 1); return witness; } @@ -387,66 +392,66 @@ class OldDemangler { if (Mangled.nextIf('W')) { if (Mangled.nextIf('V')) { auto witnessTable = Factory.createNode(Node::Kind::ValueWitnessTable); - DEMANGLE_CHILD_OR_RETURN(witnessTable, Type); + DEMANGLE_CHILD_OR_RETURN(witnessTable, Type, depth + 1); return witnessTable; } if (Mangled.nextIf('v')) { auto fieldOffset = Factory.createNode(Node::Kind::FieldOffset); - DEMANGLE_CHILD_AS_NODE_OR_RETURN(fieldOffset, Directness); - DEMANGLE_CHILD_OR_RETURN(fieldOffset, Entity); + DEMANGLE_CHILD_AS_NODE_OR_RETURN(fieldOffset, Directness, depth + 1); + DEMANGLE_CHILD_OR_RETURN(fieldOffset, Entity, depth + 1); return fieldOffset; } if (Mangled.nextIf('P')) { auto witnessTable = Factory.createNode(Node::Kind::ProtocolWitnessTable); - DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance); + DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance, depth + 1); return witnessTable; } if (Mangled.nextIf('G')) { auto witnessTable = Factory.createNode(Node::Kind::GenericProtocolWitnessTable); - DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance); + DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance, depth + 1); return witnessTable; } if (Mangled.nextIf('I')) { auto witnessTable = Factory.createNode( Node::Kind::GenericProtocolWitnessTableInstantiationFunction); - DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance); + DEMANGLE_CHILD_OR_RETURN(witnessTable, ProtocolConformance, depth + 1); return witnessTable; } if (Mangled.nextIf('l')) { auto accessor = Factory.createNode(Node::Kind::LazyProtocolWitnessTableAccessor); - DEMANGLE_CHILD_OR_RETURN(accessor, Type); - DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance); + DEMANGLE_CHILD_OR_RETURN(accessor, Type, depth + 1); + DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance, depth + 1); return accessor; } if (Mangled.nextIf('L')) { auto accessor = Factory.createNode(Node::Kind::LazyProtocolWitnessTableCacheVariable); - DEMANGLE_CHILD_OR_RETURN(accessor, Type); - DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance); + DEMANGLE_CHILD_OR_RETURN(accessor, Type, depth + 1); + DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance, depth + 1); return accessor; } if (Mangled.nextIf('a')) { auto tableTemplate = Factory.createNode(Node::Kind::ProtocolWitnessTableAccessor); - DEMANGLE_CHILD_OR_RETURN(tableTemplate, ProtocolConformance); + DEMANGLE_CHILD_OR_RETURN(tableTemplate, ProtocolConformance, depth + 1); return tableTemplate; } if (Mangled.nextIf('t')) { auto accessor = Factory.createNode( Node::Kind::AssociatedTypeMetadataAccessor); - DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance); - DEMANGLE_CHILD_OR_RETURN(accessor, DeclName); + DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance, depth + 1); + DEMANGLE_CHILD_OR_RETURN(accessor, DeclName, depth + 1); return accessor; } if (Mangled.nextIf('T')) { auto accessor = Factory.createNode( Node::Kind::AssociatedTypeWitnessTableAccessor); - DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance); - DEMANGLE_CHILD_OR_RETURN(accessor, DeclName); - DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolName); + DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolConformance, depth + 1); + DEMANGLE_CHILD_OR_RETURN(accessor, DeclName, depth + 1); + DEMANGLE_CHILD_OR_RETURN(accessor, ProtocolName, depth + 1); return accessor; } return nullptr; @@ -456,40 +461,44 @@ class OldDemangler { if (Mangled.nextIf('T')) { if (Mangled.nextIf('R')) { auto thunk = Factory.createNode(Node::Kind::ReabstractionThunkHelper); - if (!demangleReabstractSignature(thunk)) + if (!demangleReabstractSignature(thunk, depth + 1)) return nullptr; return thunk; } if (Mangled.nextIf('r')) { auto thunk = Factory.createNode(Node::Kind::ReabstractionThunk); - if (!demangleReabstractSignature(thunk)) + if (!demangleReabstractSignature(thunk, depth + 1)) return nullptr; return thunk; } if (Mangled.nextIf('W')) { NodePointer thunk = Factory.createNode(Node::Kind::ProtocolWitness); - DEMANGLE_CHILD_OR_RETURN(thunk, ProtocolConformance); + DEMANGLE_CHILD_OR_RETURN(thunk, ProtocolConformance, depth + 1); // The entity is mangled in its own generic context. - DEMANGLE_CHILD_OR_RETURN(thunk, Entity); + DEMANGLE_CHILD_OR_RETURN(thunk, Entity, depth + 1); return thunk; } return nullptr; } // Everything else is just an entity. - return demangleEntity(); + return demangleEntity(depth + 1); } - NodePointer demangleGenericSpecialization(NodePointer specialization) { + NodePointer demangleGenericSpecialization(NodePointer specialization, + unsigned depth) { + if (depth > OldDemangler::MaxDepth) + return nullptr; + while (!Mangled.nextIf('_')) { // Otherwise, we have another parameter. Demangle the type. NodePointer param = Factory.createNode(Node::Kind::GenericSpecializationParam); - DEMANGLE_CHILD_OR_RETURN(param, Type); + DEMANGLE_CHILD_OR_RETURN(param, Type, depth + 1); // Then parse any conformances until we find an underscore. Pop off the // underscore since it serves as the end of our mangling list. while (!Mangled.nextIf('_')) { - DEMANGLE_CHILD_OR_RETURN(param, ProtocolConformance); + DEMANGLE_CHILD_OR_RETURN(param, ProtocolConformance, depth + 1); } // Add the parameter to our specialization list. @@ -509,12 +518,13 @@ class OldDemangler { Factory.createNode(Node::Kind::FunctionSignatureSpecializationParamPayload, \ payload) - bool demangleFuncSigSpecializationConstantProp(NodePointer parent) { + bool demangleFuncSigSpecializationConstantProp(NodePointer parent, + unsigned depth) { // Then figure out what was actually constant propagated. First check if // we have a function. if (Mangled.nextIf("fr")) { // Demangle the identifier - NodePointer name = demangleIdentifier(); + NodePointer name = demangleIdentifier(depth + 1); if (!name || !Mangled.nextIf('_')) return false; parent->addChild(FUNCSIGSPEC_CREATE_PARAM_KIND(ConstantPropFunction), Factory); @@ -523,7 +533,7 @@ class OldDemangler { } if (Mangled.nextIf('g')) { - NodePointer name = demangleIdentifier(); + NodePointer name = demangleIdentifier(depth + 1); if (!name || !Mangled.nextIf('_')) return false; parent->addChild(FUNCSIGSPEC_CREATE_PARAM_KIND(ConstantPropGlobal), Factory); @@ -567,7 +577,7 @@ class OldDemangler { if (!Mangled.nextIf('v')) return false; - NodePointer str = demangleIdentifier(); + NodePointer str = demangleIdentifier(depth + 1); if (!str || !Mangled.nextIf('_')) return false; @@ -581,11 +591,12 @@ class OldDemangler { return false; } - bool demangleFuncSigSpecializationClosureProp(NodePointer parent) { + bool demangleFuncSigSpecializationClosureProp(NodePointer parent, + unsigned depth) { // We don't actually demangle the function or types for now. But we do want // to signal that we specialized a closure. - NodePointer name = demangleIdentifier(); + NodePointer name = demangleIdentifier(depth + 1); if (!name) { return false; } @@ -595,7 +606,7 @@ class OldDemangler { // Then demangle types until we fail. NodePointer type = nullptr; - while (Mangled.peek() != '_' && (type = demangleType())) { + while (Mangled.peek() != '_' && (type = demangleType(depth + 1))) { parent->addChild(type, Factory); } @@ -607,7 +618,8 @@ class OldDemangler { } NodePointer - demangleFunctionSignatureSpecialization(NodePointer specialization) { + demangleFunctionSignatureSpecialization(NodePointer specialization, + unsigned depth) { // Until we hit the last '_' in our specialization info... while (!Mangled.nextIf('_')) { // Create the parameter. @@ -618,10 +630,10 @@ class OldDemangler { if (Mangled.nextIf("n_")) { // Leave the parameter empty. } else if (Mangled.nextIf("cp")) { - if (!demangleFuncSigSpecializationConstantProp(param)) + if (!demangleFuncSigSpecializationConstantProp(param, depth + 1)) return nullptr; } else if (Mangled.nextIf("cl")) { - if (!demangleFuncSigSpecializationClosureProp(param)) + if (!demangleFuncSigSpecializationClosureProp(param, depth + 1)) return nullptr; } else if (Mangled.nextIf("i_")) { auto result = FUNCSIGSPEC_CREATE_PARAM_KIND(BoxToValue); @@ -677,7 +689,7 @@ class OldDemangler { #undef FUNCSIGSPEC_CREATE_PARAM_KIND #undef FUNCSIGSPEC_CREATE_PARAM_PAYLOAD - NodePointer demangleSpecializedAttribute() { + NodePointer demangleSpecializedAttribute(unsigned depth) { bool isNotReAbstracted = false; if (Mangled.nextIf("g") || (isNotReAbstracted = Mangled.nextIf("r"))) { auto spec = Factory.createNode(isNotReAbstracted ? @@ -695,7 +707,7 @@ class OldDemangler { unsigned(Mangled.next() - 48)), Factory); // And then mangle the generic specialization. - return demangleGenericSpecialization(spec); + return demangleGenericSpecialization(spec, depth + 1); } if (Mangled.nextIf("f")) { auto spec = @@ -712,21 +724,21 @@ class OldDemangler { unsigned(Mangled.next() - 48)), Factory); // Then perform the function signature specialization. - return demangleFunctionSignatureSpecialization(spec); + return demangleFunctionSignatureSpecialization(spec, depth + 1); } // We don't know how to handle this specialization. return nullptr; } - - NodePointer demangleDeclName() { + + NodePointer demangleDeclName(unsigned depth) { // decl-name ::= local-decl-name // local-decl-name ::= 'L' index identifier if (Mangled.nextIf('L')) { - NodePointer discriminator = demangleIndexAsNode(); + NodePointer discriminator = demangleIndexAsNode(depth + 1); if (!discriminator) return nullptr; - NodePointer name = demangleIdentifier(); + NodePointer name = demangleIdentifier(depth + 1); if (!name) return nullptr; NodePointer localName = Factory.createNode(Node::Kind::LocalDeclName); @@ -735,10 +747,10 @@ class OldDemangler { return localName; } else if (Mangled.nextIf('P')) { - NodePointer discriminator = demangleIdentifier(); + NodePointer discriminator = demangleIdentifier(depth + 1); if (!discriminator) return nullptr; - NodePointer name = demangleIdentifier(); + NodePointer name = demangleIdentifier(depth + 1); if (!name) return nullptr; auto privateName = Factory.createNode(Node::Kind::PrivateDeclName); @@ -748,10 +760,11 @@ class OldDemangler { } // decl-name ::= identifier - return demangleIdentifier(); + return demangleIdentifier(depth + 1); } - NodePointer demangleIdentifier(llvm::Optional kind = llvm::None) { + NodePointer demangleIdentifier(unsigned depth, + llvm::Optional kind = llvm::None) { if (!Mangled) return nullptr; @@ -792,7 +805,7 @@ class OldDemangler { if (!kind.hasValue()) kind = Node::Kind::Identifier; Node::IndexType length; - if (!demangleNatural(length)) + if (!demangleNatural(length, depth + 1)) return nullptr; if (!Mangled.hasAtLeast(length)) return nullptr; @@ -831,12 +844,12 @@ class OldDemangler { return Factory.createNode(*kind, identifier); } - bool demangleIndex(Node::IndexType &natural) { + bool demangleIndex(Node::IndexType &natural, unsigned depth) { if (Mangled.nextIf('_')) { natural = 0; return true; } - if (demangleNatural(natural)) { + if (demangleNatural(natural, depth + 1)) { if (!Mangled.nextIf('_')) return false; ++natural; @@ -846,9 +859,10 @@ class OldDemangler { } /// Demangle an and package it as a node of some kind. - NodePointer demangleIndexAsNode(Node::Kind kind = Node::Kind::Number) { + NodePointer demangleIndexAsNode(unsigned depth, + Node::Kind kind = Node::Kind::Number) { Node::IndexType index; - if (!demangleIndex(index)) + if (!demangleIndex(index, depth)) return nullptr; return Factory.createNode(kind, index); } @@ -861,7 +875,7 @@ class OldDemangler { } /// Demangle a , given that we've already consumed the 'S'. - NodePointer demangleSubstitutionIndex() { + NodePointer demangleSubstitutionIndex(unsigned depth) { if (!Mangled) return nullptr; if (Mangled.nextIf('o')) @@ -902,19 +916,19 @@ class OldDemangler { if (Mangled.nextIf('u')) return createSwiftType(Node::Kind::Structure, "UInt"); Node::IndexType index_sub; - if (!demangleIndex(index_sub)) + if (!demangleIndex(index_sub, depth)) return nullptr; if (index_sub >= Substitutions.size()) return nullptr; return Substitutions[index_sub]; } - NodePointer demangleModule() { + NodePointer demangleModule(unsigned depth) { if (Mangled.nextIf('s')) { return Factory.createNode(Node::Kind::Module, STDLIB_NAME); } if (Mangled.nextIf('S')) { - NodePointer module = demangleSubstitutionIndex(); + NodePointer module = demangleSubstitutionIndex(depth + 1); if (!module) return nullptr; if (module->getKind() != Node::Kind::Module) @@ -922,17 +936,17 @@ class OldDemangler { return module; } - NodePointer module = demangleIdentifier(Node::Kind::Module); + NodePointer module = demangleIdentifier(depth + 1, Node::Kind::Module); if (!module) return nullptr; Substitutions.push_back(module); return module; } - NodePointer demangleDeclarationName(Node::Kind kind) { - NodePointer context = demangleContext(); + NodePointer demangleDeclarationName(Node::Kind kind, unsigned depth) { + NodePointer context = demangleContext(depth + 1); if (!context) return nullptr; - auto name = demangleDeclName(); + auto name = demangleDeclName(depth + 1); if (!name) return nullptr; auto decl = Factory.createNode(kind); @@ -942,8 +956,8 @@ class OldDemangler { return decl; } - NodePointer demangleProtocolName() { - NodePointer proto = demangleProtocolNameImpl(); + NodePointer demangleProtocolName(unsigned depth) { + NodePointer proto = demangleProtocolNameImpl(depth); if (!proto) return nullptr; NodePointer type = Factory.createNode(Node::Kind::Type); @@ -951,8 +965,9 @@ class OldDemangler { return type; } - NodePointer demangleProtocolNameGivenContext(NodePointer context) { - NodePointer name = demangleDeclName(); + NodePointer demangleProtocolNameGivenContext(NodePointer context, + unsigned depth) { + NodePointer name = demangleDeclName(depth + 1); if (!name) return nullptr; auto proto = Factory.createNode(Node::Kind::Protocol); @@ -962,13 +977,16 @@ class OldDemangler { return proto; } - NodePointer demangleProtocolNameImpl() { + NodePointer demangleProtocolNameImpl(unsigned depth) { + if (depth > OldDemangler::MaxDepth) + return nullptr; + // There's an ambiguity in between a substitution of // the protocol and a substitution of the protocol's context, so // we have to duplicate some of the logic from // demangleDeclarationName. if (Mangled.nextIf('S')) { - NodePointer sub = demangleSubstitutionIndex(); + NodePointer sub = demangleSubstitutionIndex(depth + 1); if (!sub) return nullptr; if (sub->getKind() == Node::Kind::Protocol) return sub; @@ -976,33 +994,34 @@ class OldDemangler { if (sub->getKind() != Node::Kind::Module) return nullptr; - return demangleProtocolNameGivenContext(sub); + return demangleProtocolNameGivenContext(sub, depth + 1); } if (Mangled.nextIf('s')) { NodePointer stdlib = Factory.createNode(Node::Kind::Module, STDLIB_NAME); - return demangleProtocolNameGivenContext(stdlib); + return demangleProtocolNameGivenContext(stdlib, depth + 1); } - return demangleDeclarationName(Node::Kind::Protocol); + return demangleDeclarationName(Node::Kind::Protocol, depth + 1); } - NodePointer demangleNominalType() { + NodePointer demangleNominalType(unsigned depth) { if (Mangled.nextIf('S')) - return demangleSubstitutionIndex(); + return demangleSubstitutionIndex(depth + 1); if (Mangled.nextIf('V')) - return demangleDeclarationName(Node::Kind::Structure); + return demangleDeclarationName(Node::Kind::Structure, depth + 1); if (Mangled.nextIf('O')) - return demangleDeclarationName(Node::Kind::Enum); + return demangleDeclarationName(Node::Kind::Enum, depth + 1); if (Mangled.nextIf('C')) - return demangleDeclarationName(Node::Kind::Class); + return demangleDeclarationName(Node::Kind::Class, depth + 1); if (Mangled.nextIf('P')) - return demangleDeclarationName(Node::Kind::Protocol); + return demangleDeclarationName(Node::Kind::Protocol, depth + 1); return nullptr; } - NodePointer demangleBoundGenericArgs(NodePointer nominalType) { + NodePointer demangleBoundGenericArgs(NodePointer nominalType, + unsigned depth) { if (nominalType->getNumChildren() == 0) return nullptr; @@ -1012,7 +1031,7 @@ class OldDemangler { if (parentOrModule->getKind() != Node::Kind::Module && parentOrModule->getKind() != Node::Kind::Function && parentOrModule->getKind() != Node::Kind::Extension) { - parentOrModule = demangleBoundGenericArgs(parentOrModule); + parentOrModule = demangleBoundGenericArgs(parentOrModule, depth + 1); if (!parentOrModule) return nullptr; @@ -1027,7 +1046,7 @@ class OldDemangler { NodePointer args = Factory.createNode(Node::Kind::TypeList); while (!Mangled.nextIf('_')) { - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; args->addChild(type, Factory); @@ -1065,19 +1084,19 @@ class OldDemangler { return result; } - NodePointer demangleBoundGenericType() { + NodePointer demangleBoundGenericType(unsigned depth) { // bound-generic-type ::= 'G' nominal-type (args+ '_')+ // // Each level of nominal type nesting has its own list of arguments. - NodePointer nominalType = demangleNominalType(); + NodePointer nominalType = demangleNominalType(depth + 1); if (!nominalType) return nullptr; - return demangleBoundGenericArgs(nominalType); + return demangleBoundGenericArgs(nominalType, depth + 1); } - NodePointer demangleContext() { + NodePointer demangleContext(unsigned depth) { // context ::= module // context ::= entity // context ::= 'E' module context (extension defined in a different module) @@ -1085,9 +1104,9 @@ class OldDemangler { if (!Mangled) return nullptr; if (Mangled.nextIf('E')) { NodePointer ext = Factory.createNode(Node::Kind::Extension); - NodePointer def_module = demangleModule(); + NodePointer def_module = demangleModule(depth + 1); if (!def_module) return nullptr; - NodePointer type = demangleContext(); + NodePointer type = demangleContext(depth + 1); if (!type) return nullptr; ext->addChild(def_module, Factory); ext->addChild(type, Factory); @@ -1095,14 +1114,14 @@ class OldDemangler { } if (Mangled.nextIf('e')) { NodePointer ext = Factory.createNode(Node::Kind::Extension); - NodePointer def_module = demangleModule(); + NodePointer def_module = demangleModule(depth + 1); if (!def_module) return nullptr; - NodePointer sig = demangleGenericSignature(); + NodePointer sig = demangleGenericSignature(depth + 1); // The generic context is currently re-specified by the type mangling. // If we ever remove 'self' from manglings, we should stop resetting the // context here. if (!sig) return nullptr; - NodePointer type = demangleContext(); + NodePointer type = demangleContext(depth + 1); if (!type) return nullptr; ext->addChild(def_module, Factory); @@ -1111,22 +1130,22 @@ class OldDemangler { return ext; } if (Mangled.nextIf('S')) - return demangleSubstitutionIndex(); + return demangleSubstitutionIndex(depth + 1); if (Mangled.nextIf('s')) return Factory.createNode(Node::Kind::Module, STDLIB_NAME); if (Mangled.nextIf('G')) - return demangleBoundGenericType(); + return demangleBoundGenericType(depth + 1); if (isStartOfEntity(Mangled.peek())) - return demangleEntity(); - return demangleModule(); + return demangleEntity(depth + 1); + return demangleModule(depth + 1); } - - NodePointer demangleProtocolList() { + + NodePointer demangleProtocolList(unsigned depth) { NodePointer proto_list = Factory.createNode(Node::Kind::ProtocolList); NodePointer type_list = Factory.createNode(Node::Kind::TypeList); proto_list->addChild(type_list, Factory); while (!Mangled.nextIf('_')) { - NodePointer proto = demangleProtocolName(); + NodePointer proto = demangleProtocolName(depth + 1); if (!proto) return nullptr; type_list->addChild(proto, Factory); @@ -1134,14 +1153,14 @@ class OldDemangler { return proto_list; } - NodePointer demangleProtocolConformance() { - NodePointer type = demangleType(); + NodePointer demangleProtocolConformance(unsigned depth) { + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; - NodePointer protocol = demangleProtocolName(); + NodePointer protocol = demangleProtocolName(depth + 1); if (!protocol) return nullptr; - NodePointer context = demangleContext(); + NodePointer context = demangleContext(depth + 1); if (!context) return nullptr; NodePointer proto_conformance = @@ -1154,7 +1173,10 @@ class OldDemangler { // entity ::= entity-kind context entity-name // entity ::= nominal-type - NodePointer demangleEntity() { + NodePointer demangleEntity(unsigned depth) { + if (depth > OldDemangler::MaxDepth) + return nullptr; + // static? bool isStatic = Mangled.nextIf('Z'); @@ -1169,10 +1191,10 @@ class OldDemangler { } else if (Mangled.nextIf('i')) { entityBasicKind = Node::Kind::Subscript; } else { - return demangleNominalType(); + return demangleNominalType(depth + 1); } - NodePointer context = demangleContext(); + NodePointer context = demangleContext(depth + 1); if (!context) return nullptr; // entity-name @@ -1210,7 +1232,7 @@ class OldDemangler { } else { return nullptr; } - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('l')) { wrapEntity = true; @@ -1225,61 +1247,61 @@ class OldDemangler { } else { return nullptr; } - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('g')) { wrapEntity = true; entityKind = Node::Kind::Getter; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('G')) { wrapEntity = true; entityKind = Node::Kind::GlobalGetter; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('s')) { wrapEntity = true; entityKind = Node::Kind::Setter; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('m')) { wrapEntity = true; entityKind = Node::Kind::MaterializeForSet; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('w')) { wrapEntity = true; entityKind = Node::Kind::WillSet; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('W')) { wrapEntity = true; entityKind = Node::Kind::DidSet; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('r')) { wrapEntity = true; entityKind = Node::Kind::ReadAccessor; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('M')) { wrapEntity = true; entityKind = Node::Kind::ModifyAccessor; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('U')) { entityKind = Node::Kind::ExplicitClosure; - name = demangleIndexAsNode(); + name = demangleIndexAsNode(depth + 1); if (!name) return nullptr; } else if (Mangled.nextIf('u')) { entityKind = Node::Kind::ImplicitClosure; - name = demangleIndexAsNode(); + name = demangleIndexAsNode(depth + 1); if (!name) return nullptr; } else if (entityBasicKind == Node::Kind::Initializer) { // entity-name ::= 'A' index if (Mangled.nextIf('A')) { entityKind = Node::Kind::DefaultArgumentInitializer; - name = demangleIndexAsNode(); + name = demangleIndexAsNode(depth + 1); if (!name) return nullptr; // entity-name ::= 'i' } else if (Mangled.nextIf('i')) { @@ -1290,7 +1312,7 @@ class OldDemangler { hasType = false; } else { entityKind = entityBasicKind; - name = demangleDeclName(); + name = demangleDeclName(depth + 1); if (!name) return nullptr; } @@ -1338,7 +1360,7 @@ class OldDemangler { wrappedEntity->addChild(name, Factory); if (hasType) { - auto type = demangleType(); + auto type = demangleType(depth + 1); if (!type) return nullptr; wrappedEntity->addChild(type, Factory); } @@ -1354,7 +1376,7 @@ class OldDemangler { if (name) entity->addChild(name, Factory); if (hasType) { - auto type = demangleType(); + auto type = demangleType(depth + 1); if (!type) return nullptr; entity->addChild(type, Factory); } @@ -1380,34 +1402,35 @@ class OldDemangler { return paramTy; } - NodePointer demangleGenericParamIndex() { - Node::IndexType depth, index; + NodePointer demangleGenericParamIndex(unsigned depth) { + Node::IndexType paramDepth, index; if (Mangled.nextIf('d')) { - if (!demangleIndex(depth)) + if (!demangleIndex(paramDepth, depth + 1)) return nullptr; - depth += 1; - if (!demangleIndex(index)) + paramDepth += 1; + if (!demangleIndex(index, depth + 1)) return nullptr; } else if (Mangled.nextIf('x')) { - depth = 0; + paramDepth = 0; index = 0; } else { - if (!demangleIndex(index)) + if (!demangleIndex(index, depth + 1)) return nullptr; - depth = 0; + paramDepth = 0; index += 1; } - return getDependentGenericParamType(depth, index); + return getDependentGenericParamType(paramDepth, index); } - NodePointer demangleDependentMemberTypeName(NodePointer base) { + NodePointer demangleDependentMemberTypeName(NodePointer base, + unsigned depth) { assert(base->getKind() == Node::Kind::Type && "base should be a type"); NodePointer assocTy = nullptr; if (Mangled.nextIf('S')) { - assocTy = demangleSubstitutionIndex(); + assocTy = demangleSubstitutionIndex(depth + 1); if (!assocTy) return nullptr; if (assocTy->getKind() != Node::Kind::DependentAssociatedTypeRef) @@ -1415,14 +1438,14 @@ class OldDemangler { } else { NodePointer protocol = nullptr; if (Mangled.nextIf('P')) { - protocol = demangleProtocolName(); + protocol = demangleProtocolName(depth + 1); if (!protocol) return nullptr; } // TODO: If the protocol name was elided from the assoc type mangling, // we could try to fish it out of the generic signature constraints on the // base. - NodePointer ID = demangleIdentifier(); + NodePointer ID = demangleIdentifier(depth + 1); if (!ID) return nullptr; assocTy = Factory.createNode(Node::Kind::DependentAssociatedTypeRef); if (!assocTy) return nullptr; @@ -1439,9 +1462,9 @@ class OldDemangler { return depTy; } - NodePointer demangleAssociatedTypeSimple() { + NodePointer demangleAssociatedTypeSimple(unsigned depth) { // Demangle the base type. - auto base = demangleGenericParamIndex(); + auto base = demangleGenericParamIndex(depth + 1); if (!base) return nullptr; @@ -1449,12 +1472,12 @@ class OldDemangler { nodeType->addChild(base, Factory); // Demangle the associated type name. - return demangleDependentMemberTypeName(nodeType); + return demangleDependentMemberTypeName(nodeType, depth + 1); } - - NodePointer demangleAssociatedTypeCompound() { + + NodePointer demangleAssociatedTypeCompound(unsigned depth) { // Demangle the base type. - auto base = demangleGenericParamIndex(); + auto base = demangleGenericParamIndex(depth + 1); if (!base) return nullptr; @@ -1462,45 +1485,45 @@ class OldDemangler { while (!Mangled.nextIf('_')) { NodePointer nodeType = Factory.createNode(Node::Kind::Type); nodeType->addChild(base, Factory); - - base = demangleDependentMemberTypeName(nodeType); + + base = demangleDependentMemberTypeName(nodeType, depth + 1); if (!base) return nullptr; } return base; } - - NodePointer demangleDependentType() { + + NodePointer demangleDependentType(unsigned depth) { if (!Mangled) return nullptr; // A dependent member type begins with a non-index, non-'d' character. auto c = Mangled.peek(); if (c != 'd' && c != '_' && !Mangle::isDigit(c)) { - NodePointer baseType = demangleType(); + NodePointer baseType = demangleType(depth + 1); if (!baseType) return nullptr; - return demangleDependentMemberTypeName(baseType); + return demangleDependentMemberTypeName(baseType, depth + 1); } // Otherwise, we have a generic parameter. - return demangleGenericParamIndex(); + return demangleGenericParamIndex(depth + 1); } - NodePointer demangleConstrainedTypeImpl() { + NodePointer demangleConstrainedTypeImpl(unsigned depth) { // The constrained type can only be a generic parameter or an associated // type thereof. The 'q' introducer is thus left off of generic params. if (Mangled.nextIf('w')) { - return demangleAssociatedTypeSimple(); + return demangleAssociatedTypeSimple(depth + 1); } if (Mangled.nextIf('W')) { - return demangleAssociatedTypeCompound(); + return demangleAssociatedTypeCompound(depth + 1); } - return demangleGenericParamIndex(); + return demangleGenericParamIndex(depth + 1); } - NodePointer demangleConstrainedType() { - auto type = demangleConstrainedTypeImpl(); + NodePointer demangleConstrainedType(unsigned depth) { + auto type = demangleConstrainedTypeImpl(depth); if (!type) return nullptr; @@ -1509,7 +1532,8 @@ class OldDemangler { return nodeType; } - NodePointer demangleGenericSignature(bool isPseudogeneric = false) { + NodePointer demangleGenericSignature(unsigned depth, + bool isPseudogeneric = false) { auto sig = Factory.createNode(isPseudogeneric ? Node::Kind::DependentPseudogenericSignature @@ -1526,7 +1550,7 @@ class OldDemangler { while (Mangled.peek() != 'R' && Mangled.peek() != 'r') { if (Mangled.nextIf('z')) { count = 0; - } else if (demangleIndex(count)) { + } else if (demangleIndex(count, depth + 1)) { count += 1; } else { return nullptr; @@ -1548,7 +1572,7 @@ class OldDemangler { return nullptr; while (!Mangled.nextIf('r')) { - NodePointer reqt = demangleGenericRequirement(); + NodePointer reqt = demangleGenericRequirement(depth + 1); if (!reqt) return nullptr; sig->addChild(reqt, Factory); } @@ -1556,7 +1580,7 @@ class OldDemangler { return sig; } - NodePointer demangleMetatypeRepresentation() { + NodePointer demangleMetatypeRepresentation(unsigned depth) { if (Mangled.nextIf('t')) return Factory.createNode(Node::Kind::MetatypeRepresentation, "@thin"); @@ -1570,13 +1594,13 @@ class OldDemangler { // Unknown metatype representation return nullptr; } - - NodePointer demangleGenericRequirement() { - NodePointer constrainedType = demangleConstrainedType(); + + NodePointer demangleGenericRequirement(unsigned depth) { + NodePointer constrainedType = demangleConstrainedType(depth + 1); if (!constrainedType) return nullptr; if (Mangled.nextIf('z')) { - NodePointer second = demangleType(); + NodePointer second = demangleType(depth + 1); if (!second) return nullptr; auto reqt = Factory.createNode( Node::Kind::DependentGenericSameTypeRequirement); @@ -1604,30 +1628,30 @@ class OldDemangler { name = "T"; } else if (Mangled.nextIf('E')) { kind = Node::Kind::Identifier; - if (!demangleNatural(size)) + if (!demangleNatural(size, depth + 1)) return nullptr; if (!Mangled.nextIf('_')) return nullptr; - if (!demangleNatural(alignment)) + if (!demangleNatural(alignment, depth + 1)) return nullptr; name = "E"; } else if (Mangled.nextIf('e')) { kind = Node::Kind::Identifier; - if (!demangleNatural(size)) + if (!demangleNatural(size, depth + 1)) return nullptr; name = "e"; } else if (Mangled.nextIf('M')) { kind = Node::Kind::Identifier; - if (!demangleNatural(size)) + if (!demangleNatural(size, depth + 1)) return nullptr; if (!Mangled.nextIf('_')) return nullptr; - if (!demangleNatural(alignment)) + if (!demangleNatural(alignment, depth + 1)) return nullptr; name = "M"; } else if (Mangled.nextIf('m')) { kind = Node::Kind::Identifier; - if (!demangleNatural(size)) + if (!demangleNatural(size, depth + 1)) return nullptr; name = "m"; } else { @@ -1657,20 +1681,20 @@ class OldDemangler { auto next = Mangled.peek(); if (next == 'C') { - constraint = demangleType(); + constraint = demangleType(depth + 1); if (!constraint) return nullptr; } else if (next == 'S') { // A substitution may be either the module name of a protocol or a full // type name. NodePointer typeName = nullptr; Mangled.next(); - NodePointer sub = demangleSubstitutionIndex(); + NodePointer sub = demangleSubstitutionIndex(depth + 1); if (!sub) return nullptr; if (sub->getKind() == Node::Kind::Protocol || sub->getKind() == Node::Kind::Class) { typeName = sub; } else if (sub->getKind() == Node::Kind::Module) { - typeName = demangleProtocolNameGivenContext(sub); + typeName = demangleProtocolNameGivenContext(sub, depth + 1); if (!typeName) return nullptr; } else { @@ -1679,7 +1703,7 @@ class OldDemangler { constraint = Factory.createNode(Node::Kind::Type); constraint->addChild(typeName, Factory); } else { - constraint = demangleProtocolName(); + constraint = demangleProtocolName(depth + 1); if (!constraint) return nullptr; } @@ -1689,25 +1713,26 @@ class OldDemangler { reqt->addChild(constraint, Factory); return reqt; } - - NodePointer demangleArchetypeType() { + + NodePointer demangleArchetypeType(unsigned depth) { auto makeAssociatedType = [&](NodePointer root) -> NodePointer { - NodePointer name = demangleIdentifier(); - if (!name) return nullptr; + NodePointer name = demangleIdentifier(depth + 1); + if (!name) + return nullptr; auto assocType = Factory.createNode(Node::Kind::AssociatedTypeRef); assocType->addChild(root, Factory); assocType->addChild(name, Factory); Substitutions.push_back(assocType); return assocType; }; - + if (Mangled.nextIf('Q')) { - NodePointer root = demangleArchetypeType(); + NodePointer root = demangleArchetypeType(depth + 1); if (!root) return nullptr; return makeAssociatedType(root); } if (Mangled.nextIf('S')) { - NodePointer sub = demangleSubstitutionIndex(); + NodePointer sub = demangleSubstitutionIndex(depth + 1); if (!sub) return nullptr; return makeAssociatedType(sub); } @@ -1718,7 +1743,7 @@ class OldDemangler { return nullptr; } - NodePointer demangleTuple(IsVariadic isV) { + NodePointer demangleTuple(IsVariadic isV, unsigned depth) { NodePointer tuple = Factory.createNode(Node::Kind::Tuple); NodePointer elt = nullptr; while (!Mangled.nextIf('_')) { @@ -1727,13 +1752,14 @@ class OldDemangler { elt = Factory.createNode(Node::Kind::TupleElement); if (isStartOfIdentifier(Mangled.peek())) { - NodePointer label = demangleIdentifier(Node::Kind::TupleElementName); + NodePointer label = + demangleIdentifier(depth + 1, Node::Kind::TupleElementName); if (!label) return nullptr; elt->addChild(label, Factory); } - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; elt->addChild(type, Factory); @@ -1748,25 +1774,26 @@ class OldDemangler { } return tuple; } - + NodePointer postProcessReturnTypeNode (NodePointer out_args) { NodePointer out_node = Factory.createNode(Node::Kind::ReturnType); out_node->addChild(out_args, Factory); return out_node; } - NodePointer demangleType() { - NodePointer type = demangleTypeImpl(); + NodePointer demangleType(unsigned depth) { + NodePointer type = demangleTypeImpl(depth); if (!type) return nullptr; NodePointer nodeType = Factory.createNode(Node::Kind::Type); nodeType->addChild(type, Factory); return nodeType; } - - NodePointer demangleFunctionType(Node::Kind kind) { + + NodePointer demangleFunctionType(Node::Kind kind, unsigned depth) { bool throws = false, concurrent = false, async = false; auto diffKind = MangledDifferentiabilityKind::NonDifferentiable; + NodePointer globalActorType = nullptr; if (Mangled) { throws = Mangled.nextIf('z'); concurrent = Mangled.nextIf('y'); @@ -1783,11 +1810,16 @@ class OldDemangler { assert(false && "Impossible case 'NonDifferentiable'"); } } + if (Mangled.nextIf('Y')) { + globalActorType = demangleType(depth + 1); + if (!globalActorType) + return nullptr; + } } - NodePointer in_args = demangleType(); + NodePointer in_args = demangleType(depth + 1); if (!in_args) return nullptr; - NodePointer out_args = demangleType(); + NodePointer out_args = demangleType(depth + 1); if (!out_args) return nullptr; NodePointer block = Factory.createNode(kind); @@ -1807,6 +1839,12 @@ class OldDemangler { Factory.createNode( Node::Kind::DifferentiableFunctionType, (char)diffKind), Factory); } + if (globalActorType) { + auto globalActorNode = + Factory.createNode(Node::Kind::GlobalActorFunctionType); + globalActorNode->addChild(globalActorType, Factory); + block->addChild(globalActorNode, Factory); + } NodePointer in_node = Factory.createNode(Node::Kind::ArgumentTuple); block->addChild(in_node, Factory); @@ -1814,9 +1852,9 @@ class OldDemangler { block->addChild(postProcessReturnTypeNode(out_args), Factory); return block; } - - NodePointer demangleTypeImpl() { - if (!Mangled) + + NodePointer demangleTypeImpl(unsigned depth) { + if (depth > OldDemangler::MaxDepth || !Mangled) return nullptr; char c = Mangled.next(); if (c == 'B') { @@ -1831,7 +1869,7 @@ class OldDemangler { "Builtin.UnsafeValueBuffer"); if (c == 'f') { Node::IndexType size; - if (demangleBuiltinSize(size)) { + if (demangleBuiltinSize(size, depth + 1)) { return Factory.createNode( Node::Kind::BuiltinTypeName, std::move(DemanglerPrinter() << "Builtin.FPIEEE" << size).str()); @@ -1839,7 +1877,7 @@ class OldDemangler { } if (c == 'i') { Node::IndexType size; - if (demangleBuiltinSize(size)) { + if (demangleBuiltinSize(size, depth + 1)) { return Factory.createNode( Node::Kind::BuiltinTypeName, (DemanglerPrinter() << "Builtin.Int" << size).str()); @@ -1847,12 +1885,12 @@ class OldDemangler { } if (c == 'v') { Node::IndexType elts; - if (demangleNatural(elts)) { + if (demangleNatural(elts, depth + 1)) { if (!Mangled.nextIf('B')) return nullptr; if (Mangled.nextIf('i')) { Node::IndexType size; - if (!demangleBuiltinSize(size)) + if (!demangleBuiltinSize(size, depth + 1)) return nullptr; return Factory.createNode( Node::Kind::BuiltinTypeName, @@ -1861,11 +1899,11 @@ class OldDemangler { } if (Mangled.nextIf('f')) { Node::IndexType size; - if (!demangleBuiltinSize(size)) + if (!demangleBuiltinSize(size, depth + 1)) return nullptr; return Factory.createNode( Node::Kind::BuiltinTypeName, - (DemanglerPrinter() << "Builtin.Vec" << elts << "xFloat" + (DemanglerPrinter() << "Builtin.Vec" << elts << "xFPIEEE" << size).str()); } if (Mangled.nextIf('p')) @@ -1893,16 +1931,16 @@ class OldDemangler { return nullptr; } if (c == 'a') - return demangleDeclarationName(Node::Kind::TypeAlias); + return demangleDeclarationName(Node::Kind::TypeAlias, depth + 1); if (c == 'b') { - return demangleFunctionType(Node::Kind::ObjCBlock); + return demangleFunctionType(Node::Kind::ObjCBlock, depth + 1); } if (c == 'c') { - return demangleFunctionType(Node::Kind::CFunctionPointer); + return demangleFunctionType(Node::Kind::CFunctionPointer, depth + 1); } if (c == 'D') { - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; @@ -1918,17 +1956,17 @@ class OldDemangler { return Factory.createNode(Node::Kind::ErrorType, std::string()); } if (c == 'F') { - return demangleFunctionType(Node::Kind::FunctionType); + return demangleFunctionType(Node::Kind::FunctionType, depth + 1); } if (c == 'f') { - return demangleFunctionType(Node::Kind::UncurriedFunctionType); + return demangleFunctionType(Node::Kind::UncurriedFunctionType, depth + 1); } if (c == 'G') { - return demangleBoundGenericType(); + return demangleBoundGenericType(depth + 1); } if (c == 'X') { if (Mangled.nextIf('b')) { - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; NodePointer boxType = Factory.createNode(Node::Kind::SILBoxType); @@ -1938,7 +1976,7 @@ class OldDemangler { if (Mangled.nextIf('B')) { NodePointer signature = nullptr; if (Mangled.nextIf('G')) { - signature = demangleGenericSignature(/*pseudogeneric*/ false); + signature = demangleGenericSignature(depth, /*pseudogeneric*/ false); if (!signature) return nullptr; } @@ -1951,8 +1989,8 @@ class OldDemangler { kind = Node::Kind::SILBoxImmutableField; else return nullptr; - - auto type = demangleType(); + + auto type = demangleType(depth + 1); if (!type) return nullptr; auto field = Factory.createNode(kind); @@ -1963,7 +2001,7 @@ class OldDemangler { if (signature) { genericArgs = Factory.createNode(Node::Kind::TypeList); while (!Mangled.nextIf('_')) { - auto type = demangleType(); + auto type = demangleType(depth + 1); if (!type) return nullptr; genericArgs->addChild(type, Factory); @@ -1981,10 +2019,10 @@ class OldDemangler { } } if (c == 'K') { - return demangleFunctionType(Node::Kind::AutoClosureType); + return demangleFunctionType(Node::Kind::AutoClosureType, depth + 1); } if (c == 'M') { - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; NodePointer metatype = Factory.createNode(Node::Kind::Metatype); @@ -1993,10 +2031,10 @@ class OldDemangler { } if (c == 'X') { if (Mangled.nextIf('M')) { - NodePointer metatypeRepr = demangleMetatypeRepresentation(); + NodePointer metatypeRepr = demangleMetatypeRepresentation(depth + 1); if (!metatypeRepr) return nullptr; - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; NodePointer metatype = Factory.createNode(Node::Kind::Metatype); @@ -2007,23 +2045,23 @@ class OldDemangler { } if (c == 'P') { if (Mangled.nextIf('M')) { - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; auto metatype = Factory.createNode(Node::Kind::ExistentialMetatype); metatype->addChild(type, Factory); return metatype; } - return demangleProtocolList(); + return demangleProtocolList(depth + 1); } if (c == 'X') { if (Mangled.nextIf('P')) { if (Mangled.nextIf('M')) { - NodePointer metatypeRepr = demangleMetatypeRepresentation(); + NodePointer metatypeRepr = demangleMetatypeRepresentation(depth + 1); if (!metatypeRepr) return nullptr; - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; auto metatype = Factory.createNode(Node::Kind::ExistentialMetatype); @@ -2032,7 +2070,7 @@ class OldDemangler { return metatype; } - return demangleProtocolList(); + return demangleProtocolList(depth + 1); } } if (c == 'Q') { @@ -2040,24 +2078,24 @@ class OldDemangler { // Special mangling for opaque return type. return Factory.createNode(Node::Kind::OpaqueReturnType); } - return demangleArchetypeType(); + return demangleArchetypeType(depth + 1); } if (c == 'q') { - return demangleDependentType(); + return demangleDependentType(depth + 1); } if (c == 'x') { // Special mangling for the first generic param. return getDependentGenericParamType(0, 0); } if (c == 'w') { - return demangleAssociatedTypeSimple(); + return demangleAssociatedTypeSimple(depth + 1); } if (c == 'W') { - return demangleAssociatedTypeCompound(); + return demangleAssociatedTypeCompound(depth + 1); } if (c == 'R') { NodePointer inout = Factory.createNode(Node::Kind::InOut); - NodePointer type = demangleTypeImpl(); + NodePointer type = demangleTypeImpl(depth + 1); if (!type) return nullptr; inout->addChild(type, Factory); @@ -2065,25 +2103,25 @@ class OldDemangler { } if (c == 'k') { auto noDerivative = Factory.createNode(Node::Kind::NoDerivative); - auto type = demangleTypeImpl(); + auto type = demangleTypeImpl(depth + 1); if (!type) return nullptr; noDerivative->addChild(type, Factory); return noDerivative; } if (c == 'S') { - return demangleSubstitutionIndex(); + return demangleSubstitutionIndex(depth + 1); } if (c == 'T') { - return demangleTuple(IsVariadic::no); + return demangleTuple(IsVariadic::no, depth + 1); } if (c == 't') { - return demangleTuple(IsVariadic::yes); + return demangleTuple(IsVariadic::yes, depth + 1); } if (c == 'u') { - NodePointer sig = demangleGenericSignature(); + NodePointer sig = demangleGenericSignature(depth + 1); if (!sig) return nullptr; - NodePointer sub = demangleType(); + NodePointer sub = demangleType(depth + 1); if (!sub) return nullptr; NodePointer dependentGenericType = Factory.createNode(Node::Kind::DependentGenericType); @@ -2093,10 +2131,10 @@ class OldDemangler { } if (c == 'X') { if (Mangled.nextIf('f')) { - return demangleFunctionType(Node::Kind::ThinFunctionType); + return demangleFunctionType(Node::Kind::ThinFunctionType, depth + 1); } if (Mangled.nextIf('o')) { - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; NodePointer unowned = Factory.createNode(Node::Kind::Unowned); @@ -2104,7 +2142,7 @@ class OldDemangler { return unowned; } if (Mangled.nextIf('u')) { - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; NodePointer unowned = Factory.createNode(Node::Kind::Unmanaged); @@ -2112,7 +2150,7 @@ class OldDemangler { return unowned; } if (Mangled.nextIf('w')) { - NodePointer type = demangleType(); + NodePointer type = demangleType(depth + 1); if (!type) return nullptr; NodePointer weak = Factory.createNode(Node::Kind::Weak); @@ -2122,28 +2160,28 @@ class OldDemangler { // type ::= 'XF' impl-function-type if (Mangled.nextIf('F')) { - return demangleImplFunctionType(); + return demangleImplFunctionType(depth + 1); } return nullptr; } if (isStartOfNominalType(c)) - return demangleDeclarationName(nominalTypeMarkerToNodeKind(c)); + return demangleDeclarationName(nominalTypeMarkerToNodeKind(c), depth + 1); return nullptr; } - bool demangleReabstractSignature(NodePointer signature) { + bool demangleReabstractSignature(NodePointer signature, unsigned depth) { if (Mangled.nextIf('G')) { - NodePointer generics = demangleGenericSignature(); + NodePointer generics = demangleGenericSignature(depth + 1); if (!generics) return false; signature->addChild(generics, Factory); } - NodePointer srcType = demangleType(); + NodePointer srcType = demangleType(depth + 1); if (!srcType) return false; signature->addChild(srcType, Factory); - NodePointer destType = demangleType(); + NodePointer destType = demangleType(depth + 1); if (!destType) return false; signature->addChild(destType, Factory); @@ -2158,10 +2196,10 @@ class OldDemangler { // impl-function-attribute ::= 'CO' // compatible with ObjC method // impl-function-attribute ::= 'Cw' // compatible with protocol witness // impl-function-attribute ::= 'G' // generic - NodePointer demangleImplFunctionType() { + NodePointer demangleImplFunctionType(unsigned depth) { NodePointer type = Factory.createNode(Node::Kind::ImplFunctionType); - if (!demangleImplCalleeConvention(type)) + if (!demangleImplCalleeConvention(type, depth + 1)) return nullptr; if (Mangled.nextIf('C')) { @@ -2190,7 +2228,8 @@ class OldDemangler { bool isPseudogeneric = false; if (Mangled.nextIf('G') || (isPseudogeneric = Mangled.nextIf('g'))) { - NodePointer generics = demangleGenericSignature(isPseudogeneric); + NodePointer generics = + demangleGenericSignature(depth + 1, isPseudogeneric); if (!generics) return nullptr; type->addChild(generics, Factory); @@ -2201,11 +2240,11 @@ class OldDemangler { return nullptr; // Demangle the parameters. - if (!demangleImplParameters(type)) + if (!demangleImplParameters(type, depth + 1)) return nullptr; // Demangle the result type. - if (!demangleImplResults(type)) + if (!demangleImplResults(type, depth + 1)) return nullptr; return type; @@ -2224,7 +2263,7 @@ class OldDemangler { /// impl-convention ::= 'o' // direct, ownership transfer /// /// Returns an empty string otherwise. - StringRef demangleImplConvention(ImplConventionContext ctxt) { + StringRef demangleImplConvention(ImplConventionContext ctxt, unsigned depth) { #define CASE(CHAR, FOR_CALLEE, FOR_PARAMETER, FOR_RESULT) \ if (Mangled.nextIf(CHAR)) { \ switch (ctxt) { \ @@ -2249,12 +2288,12 @@ class OldDemangler { // impl-callee-convention ::= 't' // impl-callee-convention ::= impl-convention - bool demangleImplCalleeConvention(NodePointer type) { + bool demangleImplCalleeConvention(NodePointer type, unsigned depth) { StringRef attr; if (Mangled.nextIf('t')) { attr = "@convention(thin)"; } else { - attr = demangleImplConvention(ImplConventionContext::Callee); + attr = demangleImplConvention(ImplConventionContext::Callee, depth + 1); } if (attr.empty()) { return false; @@ -2277,9 +2316,10 @@ class OldDemangler { } // impl-parameter ::= impl-convention type - bool demangleImplParameters(NodePointer parent) { + bool demangleImplParameters(NodePointer parent, unsigned depth) { while (!Mangled.nextIf('_')) { - auto input = demangleImplParameterOrResult(Node::Kind::ImplParameter); + auto input = + demangleImplParameterOrResult(Node::Kind::ImplParameter, depth + 1); if (!input) return false; parent->addChild(input, Factory); } @@ -2287,16 +2327,17 @@ class OldDemangler { } // impl-result ::= impl-convention type - bool demangleImplResults(NodePointer parent) { + bool demangleImplResults(NodePointer parent, unsigned depth) { while (!Mangled.nextIf('_')) { - auto res = demangleImplParameterOrResult(Node::Kind::ImplResult); + auto res = + demangleImplParameterOrResult(Node::Kind::ImplResult, depth + 1); if (!res) return false; parent->addChild(res, Factory); } return true; } - NodePointer demangleImplParameterOrResult(Node::Kind kind) { + NodePointer demangleImplParameterOrResult(Node::Kind kind, unsigned depth) { if (Mangled.nextIf('z')) { // Only valid for a result. if (kind != Node::Kind::ImplResult) @@ -2314,16 +2355,16 @@ class OldDemangler { return nullptr; } - auto convention = demangleImplConvention(ConvCtx); + auto convention = demangleImplConvention(ConvCtx, depth + 1); if (convention.empty()) return nullptr; - auto type = demangleType(); + auto type = demangleType(depth + 1); if (!type) return nullptr; NodePointer node = Factory.createNode(kind); node->addChild(Factory.createNode(Node::Kind::ImplConvention, convention), Factory); node->addChild(type, Factory); - + return node; } }; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 5b1bab9f6bd47..01bd4bd209c8c 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -36,6 +36,8 @@ static void unreachable(const char *Message) { namespace { class Remangler : public RemanglerBase { + static const unsigned MaxDepth = 1024; + public: Remangler(NodeFactory &Factory) : RemanglerBase(Factory) { } @@ -72,79 +74,88 @@ namespace { }; }; - void mangle(Node *node) { + void mangle(Node *node, unsigned depth) { + if (depth > Remangler::MaxDepth) { + // FIXME: error handling needs doing properly (rdar://79725187) + unreachable("too complex to remangle"); + } + switch (node->getKind()) { -#define NODE(ID) case Node::Kind::ID: return mangle##ID(node); +#define NODE(ID) \ + case Node::Kind::ID: \ + return mangle##ID(node, depth); #include "swift/Demangling/DemangleNodes.def" } unreachable("bad demangling tree node"); } - void mangleGenericArgs(Node *node, EntityContext &ctx); - void mangleAnyNominalType(Node *node, EntityContext &ctx); + void mangleGenericArgs(Node *node, EntityContext &ctx, unsigned depth); + void mangleAnyNominalType(Node *node, EntityContext &ctx, unsigned depth); -#define NODE(ID) \ - void mangle##ID(Node *node); -#define CONTEXT_NODE(ID) \ - void mangle##ID(Node *node); \ - void mangle##ID(Node *node, EntityContext &ctx); +#define NODE(ID) void mangle##ID(Node *node, unsigned depth); +#define CONTEXT_NODE(ID) \ + void mangle##ID(Node *node, unsigned depth); \ + void mangle##ID(Node *node, EntityContext &ctx, unsigned depth); #include "swift/Demangling/DemangleNodes.def" void mangleIndex(Node::IndexType index); void mangleIdentifier(StringRef name, OperatorKind operatorKind); void mangleAccessor(Node *storageNode, StringRef accessorCode, - EntityContext &ctx); + EntityContext &ctx, unsigned depth); - void mangleChildNodes(Node *node) { mangleNodes(node->begin(), node->end()); } - void mangleNodes(Node::iterator i, Node::iterator e) { + void mangleChildNodes(Node *node, unsigned depth) { + mangleNodes(node->begin(), node->end(), depth); + } + void mangleNodes(Node::iterator i, Node::iterator e, unsigned depth) { for (; i != e; ++i) { - mangle(*i); + mangle(*i, depth); } } - void mangleSingleChildNode(Node *node) { + void mangleSingleChildNode(Node *node, unsigned depth) { assert(node->getNumChildren() == 1); - mangle(*node->begin()); + mangle(*node->begin(), depth); } - void mangleChildNode(Node *node, unsigned index) { + void mangleChildNode(Node *node, unsigned index, unsigned depth) { assert(index < node->getNumChildren()); - mangle(node->begin()[index]); + mangle(node->begin()[index], depth); } void mangleSimpleEntity(Node *node, char basicKind, StringRef entityKind, - EntityContext &ctx); + EntityContext &ctx, unsigned depth); void mangleNamedEntity(Node *node, char basicKind, StringRef entityKind, - EntityContext &ctx, - StringRef ArtificialPrivateDiscriminator = {}); + EntityContext &ctx, unsigned depth, + StringRef ArtificialPrivateDiscriminator = {}); void mangleTypedEntity(Node *node, char basicKind, StringRef entityKind, - EntityContext &ctx); + EntityContext &ctx, unsigned depth); void mangleNamedAndTypedEntity(Node *node, char basicKind, - StringRef entityKind, - EntityContext &ctx); + StringRef entityKind, EntityContext &ctx, + unsigned depth); void mangleNominalType(Node *node, char basicKind, EntityContext &ctx, - StringRef ArtificialPrivateDiscriminator = {}); + unsigned depth, + StringRef ArtificialPrivateDiscriminator = {}); - void mangleProtocolWithoutPrefix(Node *node); - void mangleProtocolListWithoutPrefix(Node *node, + void mangleProtocolWithoutPrefix(Node *node, unsigned depth); + void mangleProtocolListWithoutPrefix(Node *node, unsigned depth, Node *additionalProto = nullptr); - void mangleEntityContext(Node *node, EntityContext &ctx); - void mangleEntityType(Node *node, EntityContext &ctx); + void mangleEntityContext(Node *node, EntityContext &ctx, unsigned depth); + void mangleEntityType(Node *node, EntityContext &ctx, unsigned depth); void mangleEntityGenericType(Node *node, EntityContext &ctx); bool trySubstitution(Node *node, SubstitutionEntry &entry); bool mangleStandardSubstitution(Node *node); - void mangleDependentGenericParamIndex(Node *node); - void mangleConstrainedType(Node *node); + void mangleDependentGenericParamIndex(Node *node, unsigned depth); + void mangleConstrainedType(Node *node, unsigned depth); }; } // end anonymous namespace #define NODE(ID) -#define CONTEXT_NODE(ID) \ -void Remangler::mangle##ID(Node *node) { \ - EntityContext ctx; \ - mangle##ID(node, ctx); \ -} +#define CONTEXT_NODE(ID) \ + void Remangler::mangle##ID(Node *node, unsigned depth) { \ + EntityContext ctx; \ + mangle##ID(node, ctx, depth); \ + } #include "swift/Demangling/DemangleNodes.def" /// Re-apply labels from the function to its parameter type @@ -164,27 +175,31 @@ static NodePointer applyParamLabels(NodePointer LabelList, NodePointer OrigType, auto ParamsType = Factory.createNode(Node::Kind::ArgumentTuple); auto Tuple = Factory.createNode(Node::Kind::Tuple); - auto OrigTuple = ArgTuple->getFirstChild()->getFirstChild(); - assert(OrigTuple->getKind() == Node::Kind::Tuple); - - for (unsigned i = 0, n = OrigTuple->getNumChildren(); i != n; ++i) { - const auto Label = LabelList->getChild(i); + auto processParameter = [&](NodePointer Label, NodePointer Param) { if (Label->getKind() == Node::Kind::FirstElementMarker) { - Tuple->addChild(OrigTuple->getChild(i), Factory); - continue; + Tuple->addChild(Param, Factory); + return; } - auto OrigElt = OrigTuple->getChild(i); - auto NewElt = Factory.createNode(Node::Kind::TupleElement); - - NewElt->addChild(Factory.createNodeWithAllocatedText( + auto NewParam = Factory.createNode(Node::Kind::TupleElement); + NewParam->addChild(Factory.createNodeWithAllocatedText( Node::Kind::TupleElementName, Label->getText()), - Factory); + Factory); + + for (auto &Child : *Param) + NewParam->addChild(Child, Factory); - for (auto &Child : *OrigElt) - NewElt->addChild(Child, Factory); + Tuple->addChild(NewParam, Factory); + }; + + auto OrigTuple = ArgTuple->getFirstChild()->getFirstChild(); - Tuple->addChild(NewElt, Factory); + if (OrigTuple->getKind() != Node::Kind::Tuple) { + processParameter(LabelList->getChild(0), OrigTuple); + } else { + for (unsigned i = 0, n = OrigTuple->getNumChildren(); i != n; ++i) { + processParameter(LabelList->getChild(i), OrigTuple->getChild(i)); + } } auto Type = Factory.createNode(Node::Kind::Type); @@ -294,16 +309,16 @@ bool Remangler::mangleStandardSubstitution(Node *node) { return false; } -void Remangler::mangleIdentifier(Node *node) { +void Remangler::mangleIdentifier(Node *node, unsigned depth) { mangleIdentifier(node->getText(), OperatorKind::NotOperator); } -void Remangler::manglePrefixOperator(Node *node) { +void Remangler::manglePrefixOperator(Node *node, unsigned depth) { mangleIdentifier(node->getText(), OperatorKind::Prefix); } -void Remangler::manglePostfixOperator(Node *node) { +void Remangler::manglePostfixOperator(Node *node, unsigned depth) { mangleIdentifier(node->getText(), OperatorKind::Postfix); } -void Remangler::mangleInfixOperator(Node *node) { +void Remangler::mangleInfixOperator(Node *node, unsigned depth) { mangleIdentifier(node->getText(), OperatorKind::Infix); } void Remangler::mangleIdentifier(StringRef ident, OperatorKind operatorKind) { @@ -338,7 +353,7 @@ void Remangler::mangleIdentifier(StringRef ident, OperatorKind operatorKind) { } } -void Remangler::mangleNumber(Node *node) { +void Remangler::mangleNumber(Node *node, unsigned depth) { mangleIndex(node->getIndex()); } void Remangler::mangleIndex(Node::IndexType value) { @@ -349,271 +364,306 @@ void Remangler::mangleIndex(Node::IndexType value) { } } -void Remangler::mangleGlobal(Node *node) { +void Remangler::mangleGlobal(Node *node, unsigned depth) { Buffer << "_T"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleSuffix(Node *node) { +void Remangler::mangleSuffix(Node *node, unsigned depth) { // Just add the suffix back on. Buffer << node->getText(); } -void Remangler::mangleGenericSpecialization(Node *node) { +void Remangler::mangleGenericSpecialization(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGenericSpecializationPrespecialized(Node *node) { +void Remangler::mangleGenericSpecializationPrespecialized(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGenericSpecializationNotReAbstracted(Node *node) { +void Remangler::mangleGenericSpecializationNotReAbstracted(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGenericSpecializationInResilienceDomain(Node *node) { +void Remangler::mangleGenericSpecializationInResilienceDomain(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleInlinedGenericFunction(Node *node) { +void Remangler::mangleInlinedGenericFunction(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGenericPartialSpecialization(Node *node) { +void Remangler::mangleGenericPartialSpecialization(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGenericPartialSpecializationNotReAbstracted(Node *node) { +void Remangler::mangleGenericPartialSpecializationNotReAbstracted( + Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGenericSpecializationParam(Node *node) { +void Remangler::mangleGenericSpecializationParam(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleFunctionSignatureSpecialization(Node *node) { +void Remangler::mangleFunctionSignatureSpecialization(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleSpecializationPassID(Node *node) { +void Remangler::mangleSpecializationPassID(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleIsSerialized(Node *node) { +void Remangler::mangleIsSerialized(Node *node, unsigned depth) { Buffer << "q"; } -void Remangler::mangleFunctionSignatureSpecializationReturn(Node *node) { +void Remangler::mangleFunctionSignatureSpecializationReturn(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleFunctionSignatureSpecializationParam(Node *node) { +void Remangler::mangleFunctionSignatureSpecializationParam(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleFunctionSignatureSpecializationParamPayload(Node *node) { +void Remangler::mangleFunctionSignatureSpecializationParamPayload( + Node *node, unsigned depth) { // This should never be called since mangling parameter payloads require // knowing what the parameter kind is. unreachable("This should never be called"); } -void Remangler::mangleFunctionSignatureSpecializationParamKind(Node *node) { +void Remangler::mangleFunctionSignatureSpecializationParamKind(Node *node, + unsigned depth) { // This should never be called since mangling parameter kinds have influence // on the payloads. unreachable("This should never be called"); } -void Remangler::mangleRetroactiveConformance(Node *node) { +void Remangler::mangleRetroactiveConformance(Node *node, unsigned depth) { unreachable("Retroactive conformances aren't in the old mangling"); } -void Remangler::mangleProtocolConformanceRefInTypeModule(Node *node) { +void Remangler::mangleProtocolConformanceRefInTypeModule(Node *node, + unsigned depth) { unreachable("Protocol conformance references aren't in the old mangling"); } -void Remangler::mangleProtocolConformanceRefInProtocolModule(Node *node) { +void Remangler::mangleProtocolConformanceRefInProtocolModule(Node *node, + unsigned depth) { unreachable("Protocol conformance references aren't in the old mangling"); } -void Remangler::mangleProtocolConformanceRefInOtherModule(Node *node) { +void Remangler::mangleProtocolConformanceRefInOtherModule(Node *node, + unsigned depth) { unreachable("Protocol conformance references aren't in the old mangling"); } -void Remangler::mangleConcreteProtocolConformance(Node *node) { +void Remangler::mangleConcreteProtocolConformance(Node *node, unsigned depth) { unreachable("Concrete conformances aren't in the old mangling"); } -void Remangler::mangleAnyProtocolConformanceList(Node *node) { +void Remangler::mangleAnyProtocolConformanceList(Node *node, unsigned depth) { unreachable("Conformance lists aren't in the old mangling"); } -void Remangler::mangleDependentAssociatedConformance(Node *node) { +void Remangler::mangleDependentAssociatedConformance(Node *node, + unsigned depth) { unreachable("Dependent associated conformances aren't in the old mangling"); } -void Remangler::mangleDependentProtocolConformanceRoot(Node *node) { +void Remangler::mangleDependentProtocolConformanceRoot(Node *node, + unsigned depth) { unreachable("Dependent conformances aren't in the old mangling"); } -void Remangler::mangleDependentProtocolConformanceInherited(Node *node) { +void Remangler::mangleDependentProtocolConformanceInherited(Node *node, + unsigned depth) { unreachable("Dependent conformances aren't in the old mangling"); } -void Remangler::mangleDependentProtocolConformanceAssociated(Node *node) { +void Remangler::mangleDependentProtocolConformanceAssociated(Node *node, + unsigned depth) { unreachable("Dependent conformances aren't in the old mangling"); } -void Remangler::mangleProtocolConformance(Node *node) { +void Remangler::mangleProtocolConformance(Node *node, unsigned depth) { // type, protocol name, context assert(node->getNumChildren() == 3); - mangleChildNode(node, 0); - mangleProtocolWithoutPrefix(node->begin()[1]); - mangleChildNode(node, 2); + mangleChildNode(node, 0, depth + 1); + mangleProtocolWithoutPrefix(node->begin()[1], depth + 1); + mangleChildNode(node, 2, depth + 1); } -void Remangler::mangleObjCAttribute(Node *node) { +void Remangler::mangleObjCAttribute(Node *node, unsigned depth) { Buffer << "To"; } -void Remangler::mangleNonObjCAttribute(Node *node) { +void Remangler::mangleNonObjCAttribute(Node *node, unsigned depth) { Buffer << "TO"; } -void Remangler::mangleDirectMethodReferenceAttribute(Node *node) { +void Remangler::mangleDirectMethodReferenceAttribute(Node *node, + unsigned depth) { Buffer << "Td"; } -void Remangler::mangleDynamicAttribute(Node *node) { +void Remangler::mangleDynamicAttribute(Node *node, unsigned depth) { Buffer << "TD"; } -void Remangler::mangleVTableAttribute(Node *node) { +void Remangler::mangleVTableAttribute(Node *node, unsigned depth) { Buffer << "TV"; } -void Remangler::mangleGenericTypeMetadataPattern(Node *node) { +void Remangler::mangleGenericTypeMetadataPattern(Node *node, unsigned depth) { Buffer << "MP"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleTypeMetadataAccessFunction(Node *node) { +void Remangler::mangleTypeMetadataAccessFunction(Node *node, unsigned depth) { Buffer << "Ma"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleTypeMetadataInstantiationCache(Node *node) { +void Remangler::mangleTypeMetadataInstantiationCache(Node *node, + unsigned depth) { Buffer << "MI"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleTypeMetadataInstantiationFunction(Node *node) { +void Remangler::mangleTypeMetadataInstantiationFunction(Node *node, + unsigned depth) { Buffer << "Mi"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleTypeMetadataSingletonInitializationCache(Node *node) { +void Remangler::mangleTypeMetadataSingletonInitializationCache(Node *node, + unsigned depth) { Buffer << "Ml"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleTypeMetadataCompletionFunction(Node *node) { +void Remangler::mangleTypeMetadataCompletionFunction(Node *node, + unsigned depth) { Buffer << "Mr"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleTypeMetadataDemanglingCache(Node *node) { +void Remangler::mangleTypeMetadataDemanglingCache(Node *node, unsigned depth) { unreachable("not supported"); } -void Remangler::mangleTypeMetadataLazyCache(Node *node) { +void Remangler::mangleTypeMetadataLazyCache(Node *node, unsigned depth) { Buffer << "ML"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleMetaclass(Node *node) { +void Remangler::mangleMetaclass(Node *node, unsigned depth) { Buffer << "Mm"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleClassMetadataBaseOffset(Node *node) { +void Remangler::mangleClassMetadataBaseOffset(Node *node, unsigned depth) { Buffer << "Mo"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleNominalTypeDescriptor(Node *node) { +void Remangler::mangleNominalTypeDescriptor(Node *node, unsigned depth) { Buffer << "Mn"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::manglePropertyDescriptor(Node *node) { +void Remangler::manglePropertyDescriptor(Node *node, unsigned depth) { unreachable("not supported"); } -void Remangler::mangleTypeMetadata(Node *node) { +void Remangler::mangleTypeMetadata(Node *node, unsigned depth) { Buffer << "M"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleFullTypeMetadata(Node *node) { +void Remangler::mangleFullTypeMetadata(Node *node, unsigned depth) { Buffer << "Mf"; - mangleChildNodes(node); // type + mangleChildNodes(node, depth + 1); // type } -void Remangler::mangleProtocolDescriptor(Node *node) { +void Remangler::mangleProtocolDescriptor(Node *node, unsigned depth) { Buffer << "Mp"; - mangleProtocolWithoutPrefix(node->begin()[0]); + mangleProtocolWithoutPrefix(node->begin()[0], depth + 1); } -void Remangler::mangleProtocolRequirementsBaseDescriptor(Node *node) { +void Remangler::mangleProtocolRequirementsBaseDescriptor(Node *node, + unsigned depth) { Buffer << ""; } -void Remangler::mangleProtocolWitnessTablePattern(Node *node) { +void Remangler::mangleProtocolWitnessTablePattern(Node *node, unsigned depth) { unreachable("todo"); } -void Remangler::mangleProtocolConformanceDescriptor(Node *node) { +void Remangler::mangleProtocolConformanceDescriptor(Node *node, + unsigned depth) { Buffer << "Mc"; - mangleProtocolConformance(node->begin()[0]); + mangleProtocolConformance(node->begin()[0], depth + 1); } -void Remangler::mangleProtocolSelfConformanceDescriptor(Node *node) { +void Remangler::mangleProtocolSelfConformanceDescriptor(Node *node, + unsigned depth) { Buffer << "MS"; - mangleProtocol(node->begin()[0]); + mangleProtocol(node->begin()[0], depth + 1); } -void Remangler::manglePartialApplyForwarder(Node *node) { - Buffer << "PA__T"; - mangleSingleChildNode(node); // global +void Remangler::manglePartialApplyForwarder(Node *node, unsigned depth) { + Buffer << "PA"; + if (node->getNumChildren() == 1) { + Buffer << "__T"; + mangleSingleChildNode(node, depth + 1); // global + } } -void Remangler::manglePartialApplyObjCForwarder(Node *node) { - Buffer << "PAo__T"; - mangleSingleChildNode(node); // global +void Remangler::manglePartialApplyObjCForwarder(Node *node, unsigned depth) { + Buffer << "PAo"; + if (node->getNumChildren() == 1) { + Buffer << "__T"; + mangleSingleChildNode(node, depth + 1); // global + } } -void Remangler::mangleMergedFunction(Node *node) { +void Remangler::mangleMergedFunction(Node *node, unsigned depth) { Buffer << "Tm"; } -void Remangler::mangleDynamicallyReplaceableFunctionImpl(Node *node) { +void Remangler::mangleDynamicallyReplaceableFunctionImpl(Node *node, + unsigned depth) { Buffer << "TI"; } -void Remangler::mangleDynamicallyReplaceableFunctionKey(Node *node) { +void Remangler::mangleDynamicallyReplaceableFunctionKey(Node *node, + unsigned depth) { Buffer << "Tx"; } -void Remangler::mangleDynamicallyReplaceableFunctionVar(Node *node) { +void Remangler::mangleDynamicallyReplaceableFunctionVar(Node *node, + unsigned depth) { Buffer << "TX"; } -void Remangler::mangleAsyncAwaitResumePartialFunction(Node *node) { +void Remangler::mangleAsyncAwaitResumePartialFunction(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleAsyncSuspendResumePartialFunction(Node *node) { +void Remangler::mangleAsyncSuspendResumePartialFunction(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleDirectness(Node *node) { +void Remangler::mangleDirectness(Node *node, unsigned depth) { auto getChar = [](Directness d) -> char { switch (d) { case Directness::Direct: return 'd'; @@ -624,7 +674,7 @@ void Remangler::mangleDirectness(Node *node) { Buffer << getChar(Directness(node->getIndex())); } -void Remangler::mangleValueWitness(Node *node) { +void Remangler::mangleValueWitness(Node *node, unsigned depth) { const char *Code = nullptr; switch (ValueWitnessKind(node->getFirstChild()->getIndex())) { #define VALUE_WITNESS(MANGLING, NAME) \ @@ -632,198 +682,238 @@ void Remangler::mangleValueWitness(Node *node) { #include "swift/Demangling/ValueWitnessMangling.def" } Buffer << 'w' << Code; - mangleChildNode(node, 1); // type + mangleChildNode(node, 1, depth + 1); // type } -void Remangler::mangleValueWitnessTable(Node *node) { +void Remangler::mangleValueWitnessTable(Node *node, unsigned depth) { Buffer << "WV"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleConcurrentFunctionType(Node *node) { +void Remangler::mangleConcurrentFunctionType(Node *node, unsigned depth) { Buffer << "y"; } -void Remangler::mangleAsyncAnnotation(Node *node) { +void Remangler::mangleAsyncAnnotation(Node *node, unsigned depth) { Buffer << "Z"; } -void Remangler::mangleThrowsAnnotation(Node *node) { +void Remangler::mangleThrowsAnnotation(Node *node, unsigned depth) { Buffer << "z"; } -void Remangler::mangleDifferentiableFunctionType(Node *node) { - Buffer << "D" << (char)node->getIndex(); // differentiability kind +void Remangler::mangleDifferentiableFunctionType(Node *node, unsigned depth) { + Buffer << "D"; + mangleChildNodes(node, depth + 1); +} + +void Remangler::mangleGlobalActorFunctionType(Node *node, unsigned depth) { + Buffer << "Y" << (char)node->getIndex(); // differentiability kind } -void Remangler::mangleFieldOffset(Node *node) { +void Remangler::mangleFieldOffset(Node *node, unsigned depth) { Buffer << "Wv"; - mangleChildNodes(node); // directness, entity + mangleChildNodes(node, depth + 1); // directness, entity } -void Remangler::mangleEnumCase(Node *node) { +void Remangler::mangleEnumCase(Node *node, unsigned depth) { Buffer << "WC"; - mangleSingleChildNode(node); // enum case + mangleSingleChildNode(node, depth + 1); // enum case } -void Remangler::mangleProtocolSelfConformanceWitnessTable(Node *node) { +void Remangler::mangleProtocolSelfConformanceWitnessTable(Node *node, + unsigned depth) { Buffer << "WS"; - mangleSingleChildNode(node); // protocol + mangleSingleChildNode(node, depth + 1); // protocol } -void Remangler::mangleProtocolWitnessTable(Node *node) { +void Remangler::mangleProtocolWitnessTable(Node *node, unsigned depth) { Buffer << "WP"; - mangleSingleChildNode(node); // protocol conformance + mangleSingleChildNode(node, depth + 1); // protocol conformance } -void Remangler::mangleGenericProtocolWitnessTable(Node *node) { +void Remangler::mangleGenericProtocolWitnessTable(Node *node, unsigned depth) { Buffer << "WG"; - mangleSingleChildNode(node); // protocol conformance + mangleSingleChildNode(node, depth + 1); // protocol conformance } -void Remangler::mangleResilientProtocolWitnessTable(Node *node) { +void Remangler::mangleResilientProtocolWitnessTable(Node *node, + unsigned depth) { unreachable("todo"); } void Remangler::mangleGenericProtocolWitnessTableInstantiationFunction( - Node *node) { + Node *node, unsigned depth) { Buffer << "WI"; - mangleSingleChildNode(node); // protocol conformance + mangleSingleChildNode(node, depth + 1); // protocol conformance } -void Remangler::mangleProtocolWitnessTableAccessor(Node *node) { +void Remangler::mangleProtocolWitnessTableAccessor(Node *node, unsigned depth) { Buffer << "Wa"; - mangleSingleChildNode(node); // protocol conformance + mangleSingleChildNode(node, depth + 1); // protocol conformance } -void Remangler::mangleLazyProtocolWitnessTableAccessor(Node *node) { +void Remangler::mangleLazyProtocolWitnessTableAccessor(Node *node, + unsigned depth) { Buffer << "Wl"; - mangleChildNodes(node); // type, protocol conformance + mangleChildNodes(node, depth + 1); // type, protocol conformance } -void Remangler::mangleLazyProtocolWitnessTableCacheVariable(Node *node) { +void Remangler::mangleLazyProtocolWitnessTableCacheVariable(Node *node, + unsigned depth) { Buffer << "WL"; - mangleChildNodes(node); // type, protocol conformance + mangleChildNodes(node, depth + 1); // type, protocol conformance } -void Remangler::mangleAssociatedTypeDescriptor(Node *node) { +void Remangler::mangleAssociatedTypeDescriptor(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleAssociatedConformanceDescriptor(Node *node) { +void Remangler::mangleAssociatedConformanceDescriptor(Node *node, + unsigned depth) { Buffer << ""; } -void Remangler::mangleDefaultAssociatedConformanceAccessor(Node *node) { +void Remangler::mangleDefaultAssociatedConformanceAccessor(Node *node, + unsigned depth) { Buffer << ""; } -void Remangler::mangleBaseConformanceDescriptor(Node *node) { +void Remangler::mangleBaseConformanceDescriptor(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node) { +void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node, + unsigned depth) { Buffer << "Wt"; - mangleChildNodes(node); // protocol conformance, identifier + mangleChildNodes(node, depth + 1); // protocol conformance, identifier } -void Remangler::mangleDefaultAssociatedTypeMetadataAccessor(Node *node) { +void Remangler::mangleDefaultAssociatedTypeMetadataAccessor(Node *node, + unsigned depth) { Buffer << ""; } -void Remangler::mangleAssociatedTypeWitnessTableAccessor(Node *node) { +void Remangler::mangleAssociatedTypeWitnessTableAccessor(Node *node, + unsigned depth) { Buffer << "WT"; assert(node->getNumChildren() == 3); - mangleChildNode(node, 0); // protocol conformance - mangleChildNode(node, 1); // type - mangleProtocolWithoutPrefix(node->begin()[2]); // type + mangleChildNode(node, 0, depth + 1); // protocol conformance + mangleChildNode(node, 1, depth + 1); // type + mangleProtocolWithoutPrefix(node->begin()[2], depth + 1); // type } -void Remangler::mangleBaseWitnessTableAccessor(Node *node) { +void Remangler::mangleBaseWitnessTableAccessor(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleReabstractionThunkHelper(Node *node) { +void Remangler::mangleReabstractionThunkHelper(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleReabstractionThunkHelperWithSelf(Node *node) { +void Remangler::mangleReabstractionThunkHelperWithSelf(Node *node, + unsigned depth) { Buffer << ""; } -void Remangler::mangleReabstractionThunk(Node *node) { +void Remangler::mangleReabstractionThunk(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleAutoDiffFunction(Node *node, EntityContext &ctx) { +void Remangler::mangleReabstractionThunkHelperWithGlobalActor(Node *node, + unsigned depth) { + Buffer << ""; +} + +void Remangler::mangleAutoDiffFunction(Node *node, EntityContext &ctx, + unsigned depth) { Buffer << ""; } -void Remangler::mangleAutoDiffDerivativeVTableThunk(Node *node) { +void Remangler::mangleAutoDiffDerivativeVTableThunk(Node *node, + unsigned depth) { Buffer << ""; } -void Remangler::mangleAutoDiffSelfReorderingReabstractionThunk(Node *node) { +void Remangler::mangleAutoDiffSelfReorderingReabstractionThunk(Node *node, + unsigned depth) { Buffer << ""; } -void Remangler::mangleAutoDiffSubsetParametersThunk(Node *node) { +void Remangler::mangleAutoDiffSubsetParametersThunk(Node *node, + unsigned depth) { Buffer << ""; } -void Remangler::mangleAutoDiffFunctionKind(Node *node) { +void Remangler::mangleAutoDiffFunctionKind(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleDifferentiabilityWitness(Node *node) { +void Remangler::mangleDifferentiabilityWitness(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleIndexSubset(Node *node) { +void Remangler::mangleIndexSubset(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleProtocolSelfConformanceWitness(Node *node) { +void Remangler::mangleProtocolSelfConformanceWitness(Node *node, + unsigned depth) { Buffer << "TS"; - mangleSingleChildNode(node); // entity + mangleSingleChildNode(node, depth + 1); // entity } -void Remangler::mangleProtocolWitness(Node *node) { +void Remangler::mangleProtocolWitness(Node *node, unsigned depth) { Buffer << "TW"; - mangleChildNodes(node); // protocol conformance, entity + mangleChildNodes(node, depth + 1); // protocol conformance, entity } -void Remangler::mangleFunction(Node *node, EntityContext &ctx) { - mangleNamedAndTypedEntity(node, 'F', "", ctx); +void Remangler::mangleFunction(Node *node, EntityContext &ctx, unsigned depth) { + mangleNamedAndTypedEntity(node, 'F', "", ctx, depth + 1); } -void Remangler::mangleVariable(Node *node, EntityContext &ctx) { - mangleNamedAndTypedEntity(node, 'v', "", ctx); +void Remangler::mangleVariable(Node *node, EntityContext &ctx, unsigned depth) { + mangleNamedAndTypedEntity(node, 'v', "", ctx, depth + 1); } -void Remangler::mangleSubscript(Node *node, EntityContext &ctx) { - mangleNamedAndTypedEntity(node, 'i', "", ctx); +void Remangler::mangleSubscript(Node *node, EntityContext &ctx, + unsigned depth) { + assert(node->getNumChildren() >= 2); + Buffer << 'i'; + mangleEntityContext(node->begin()[0], ctx, depth + 1); + if (node->getLastChild()->getKind() == Node::Kind::PrivateDeclName) + mangle(node->getLastChild(), depth + 1); + + if (node->getNumChildren() >= 3 + && node->begin()[1]->getKind() == Node::Kind::LabelList) { + auto LabelList = node->begin()[1]; + auto Type = node->begin()[2]; + mangleEntityType(applyParamLabels(LabelList, Type, Factory), ctx, depth + 1); + } else { + mangleEntityType(node->begin()[1], ctx, depth + 1); + } } void Remangler::mangleAccessor(Node *storageNode, StringRef accessorCode, - EntityContext &ctx) { + EntityContext &ctx, unsigned depth) { Buffer << 'F'; - mangleEntityContext(storageNode->getChild(0), ctx); + mangleEntityContext(storageNode->getChild(0), ctx, depth + 1); Buffer << accessorCode; auto mangleAccessorType = [&](unsigned TypeIndex) { auto LabelList = storageNode->getChild(TypeIndex); if (LabelList->getKind() == Node::Kind::LabelList) { auto Type = storageNode->getChild(TypeIndex + 1); - mangleEntityType(applyParamLabels(LabelList, Type, Factory), ctx); + mangleEntityType(applyParamLabels(LabelList, Type, Factory), ctx, + depth + 1); } else { - mangleEntityType(storageNode->getChild(TypeIndex), ctx); + mangleEntityType(storageNode->getChild(TypeIndex), ctx, depth + 1); } }; switch (storageNode->getKind()) { case Demangle::Node::Kind::Variable: { - mangleChildNode(storageNode, 1); + mangleChildNode(storageNode, 1, depth + 1); mangleAccessorType(2); break; } @@ -834,7 +924,7 @@ void Remangler::mangleAccessor(Node *storageNode, StringRef accessorCode, auto PrivateName = storageNode->getChild(NumChildren - 1); if (PrivateName->getKind() == Node::Kind::PrivateDeclName) - mangle(PrivateName); + mangle(PrivateName, depth + 1); mangleIdentifier("subscript", OperatorKind::NotOperator); mangleAccessorType(1); @@ -845,148 +935,171 @@ void Remangler::mangleAccessor(Node *storageNode, StringRef accessorCode, } } -void Remangler::mangleInitializer(Node *node, EntityContext &ctx) { - mangleSimpleEntity(node, 'I', "i", ctx); +void Remangler::mangleInitializer(Node *node, EntityContext &ctx, + unsigned depth) { + mangleSimpleEntity(node, 'I', "i", ctx, depth + 1); } void Remangler::manglePropertyWrapperBackingInitializer(Node *node, - EntityContext &ctx) { - mangleSimpleEntity(node, 'I', "P", ctx); + EntityContext &ctx, + unsigned depth) { + mangleSimpleEntity(node, 'I', "P", ctx, depth + 1); } void Remangler::manglePropertyWrapperInitFromProjectedValue(Node *node, - EntityContext &ctx) { - mangleSimpleEntity(node, 'I', "W", ctx); + EntityContext &ctx, + unsigned depth) { + mangleSimpleEntity(node, 'I', "W", ctx, depth + 1); } -void Remangler::mangleDefaultArgumentInitializer(Node *node, - EntityContext &ctx) { - mangleNamedEntity(node, 'I', "A", ctx); +void Remangler::mangleDefaultArgumentInitializer(Node *node, EntityContext &ctx, + unsigned depth) { + mangleNamedEntity(node, 'I', "A", ctx, depth + 1); } -void Remangler::mangleAsyncFunctionPointer(Node *node) { +void Remangler::mangleAsyncFunctionPointer(Node *node, unsigned depth) { Buffer << "Tu"; } -void Remangler::mangleDeallocator(Node *node, EntityContext &ctx) { - mangleSimpleEntity(node, 'F', "D", ctx); +void Remangler::mangleDeallocator(Node *node, EntityContext &ctx, + unsigned depth) { + mangleSimpleEntity(node, 'F', "D", ctx, depth + 1); } -void Remangler::mangleDestructor(Node *node, EntityContext &ctx) { - mangleSimpleEntity(node, 'F', "d", ctx); +void Remangler::mangleDestructor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleSimpleEntity(node, 'F', "d", ctx, depth + 1); } -void Remangler::mangleAllocator(Node *node, EntityContext &ctx) { - mangleTypedEntity(node, 'F', "C", ctx); +void Remangler::mangleAllocator(Node *node, EntityContext &ctx, + unsigned depth) { + mangleTypedEntity(node, 'F', "C", ctx, depth + 1); } -void Remangler::mangleConstructor(Node *node, EntityContext &ctx) { - mangleTypedEntity(node, 'F', "c", ctx); +void Remangler::mangleConstructor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleTypedEntity(node, 'F', "c", ctx, depth + 1); } -void Remangler::mangleIVarInitializer(Node *node, EntityContext &ctx) { - mangleSimpleEntity(node, 'F', "e", ctx); +void Remangler::mangleIVarInitializer(Node *node, EntityContext &ctx, + unsigned depth) { + mangleSimpleEntity(node, 'F', "e", ctx, depth + 1); } -void Remangler::mangleIVarDestroyer(Node *node, EntityContext &ctx) { - mangleSimpleEntity(node, 'F', "E", ctx); +void Remangler::mangleIVarDestroyer(Node *node, EntityContext &ctx, + unsigned depth) { + mangleSimpleEntity(node, 'F', "E", ctx, depth + 1); } -void Remangler::mangleGetter(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "g", ctx); +void Remangler::mangleGetter(Node *node, EntityContext &ctx, unsigned depth) { + mangleAccessor(node->getFirstChild(), "g", ctx, depth + 1); } -void Remangler::mangleGlobalGetter(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "G", ctx); +void Remangler::mangleGlobalGetter(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "G", ctx, depth + 1); } -void Remangler::mangleSetter(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "s", ctx); +void Remangler::mangleSetter(Node *node, EntityContext &ctx, unsigned depth) { + mangleAccessor(node->getFirstChild(), "s", ctx, depth + 1); } -void Remangler::mangleMaterializeForSet(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "m", ctx); +void Remangler::mangleMaterializeForSet(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "m", ctx, depth + 1); } -void Remangler::mangleWillSet(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "w", ctx); +void Remangler::mangleWillSet(Node *node, EntityContext &ctx, unsigned depth) { + mangleAccessor(node->getFirstChild(), "w", ctx, depth + 1); } -void Remangler::mangleDidSet(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "W", ctx); +void Remangler::mangleDidSet(Node *node, EntityContext &ctx, unsigned depth) { + mangleAccessor(node->getFirstChild(), "W", ctx, depth + 1); } -void Remangler::mangleOwningMutableAddressor(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "aO", ctx); +void Remangler::mangleOwningMutableAddressor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "aO", ctx, depth + 1); } void Remangler::mangleNativeOwningMutableAddressor(Node *node, - EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "ao", ctx); + EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "ao", ctx, depth + 1); } void Remangler::mangleNativePinningMutableAddressor(Node *node, - EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "ap", ctx); + EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "ap", ctx, depth + 1); } -void Remangler::mangleUnsafeMutableAddressor(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "au", ctx); +void Remangler::mangleUnsafeMutableAddressor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "au", ctx, depth + 1); } -void Remangler::mangleOwningAddressor(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "lO", ctx); +void Remangler::mangleOwningAddressor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "lO", ctx, depth + 1); } -void Remangler::mangleNativeOwningAddressor(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "lo", ctx); +void Remangler::mangleNativeOwningAddressor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "lo", ctx, depth + 1); } -void Remangler::mangleNativePinningAddressor(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "lp", ctx); +void Remangler::mangleNativePinningAddressor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "lp", ctx, depth + 1); } -void Remangler::mangleUnsafeAddressor(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "lu", ctx); +void Remangler::mangleUnsafeAddressor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "lu", ctx, depth + 1); } -void Remangler::mangleReadAccessor(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "r", ctx); +void Remangler::mangleReadAccessor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "r", ctx, depth + 1); } -void Remangler::mangleModifyAccessor(Node *node, EntityContext &ctx) { - mangleAccessor(node->getFirstChild(), "M", ctx); +void Remangler::mangleModifyAccessor(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAccessor(node->getFirstChild(), "M", ctx, depth + 1); } -void Remangler::mangleExplicitClosure(Node *node, EntityContext &ctx) { - mangleNamedAndTypedEntity(node, 'F', "U", ctx); // name is index +void Remangler::mangleExplicitClosure(Node *node, EntityContext &ctx, + unsigned depth) { + mangleNamedAndTypedEntity(node, 'F', "U", ctx, depth + 1); // name is index } -void Remangler::mangleImplicitClosure(Node *node, EntityContext &ctx) { - mangleNamedAndTypedEntity(node, 'F', "u", ctx); // name is index +void Remangler::mangleImplicitClosure(Node *node, EntityContext &ctx, + unsigned depth) { + mangleNamedAndTypedEntity(node, 'F', "u", ctx, depth + 1); // name is index } -void Remangler::mangleStatic(Node *node, EntityContext &ctx) { +void Remangler::mangleStatic(Node *node, EntityContext &ctx, unsigned depth) { Buffer << 'Z'; - mangleEntityContext(node->getChild(0), ctx); + mangleEntityContext(node->getChild(0), ctx, depth + 1); } void Remangler::mangleSimpleEntity(Node *node, char basicKind, - StringRef entityKind, - EntityContext &ctx) { + StringRef entityKind, EntityContext &ctx, + unsigned depth) { assert(node->getNumChildren() == 1); Buffer << basicKind; - mangleEntityContext(node->begin()[0], ctx); + mangleEntityContext(node->begin()[0], ctx, depth + 1); Buffer << entityKind; } void Remangler::mangleNamedEntity(Node *node, char basicKind, - StringRef entityKind, - EntityContext &ctx, + StringRef entityKind, EntityContext &ctx, + unsigned depth, StringRef artificialPrivateDiscriminator) { assert(node->getNumChildren() == 2); if (basicKind != '\0') Buffer << basicKind; - mangleEntityContext(node->begin()[0], ctx); + mangleEntityContext(node->begin()[0], ctx, depth + 1); Buffer << entityKind; auto privateDiscriminator = ctx.takeAnonymousContextDiscriminator(); @@ -1003,45 +1116,48 @@ void Remangler::mangleNamedEntity(Node *node, char basicKind, Buffer << 'P'; mangleIdentifier(privateDiscriminator, OperatorKind::NotOperator); } - mangle(name); + mangle(name, depth + 1); } void Remangler::mangleTypedEntity(Node *node, char basicKind, - StringRef entityKind, - EntityContext &ctx) { + StringRef entityKind, EntityContext &ctx, + unsigned depth) { assert(node->getNumChildren() == 2 || node->getNumChildren() == 3); Buffer << basicKind; - mangleEntityContext(node->begin()[0], ctx); + mangleEntityContext(node->begin()[0], ctx, depth + 1); Buffer << entityKind; if (node->begin()[1]->getKind() == Node::Kind::LabelList) { auto LabelList = node->begin()[1]; auto Type = node->begin()[2]; - mangleEntityType(applyParamLabels(LabelList, Type, Factory), ctx); + mangleEntityType(applyParamLabels(LabelList, Type, Factory), ctx, + depth + 1); } else { - mangleEntityType(node->begin()[1], ctx); + mangleEntityType(node->begin()[1], ctx, depth + 1); } } void Remangler::mangleNamedAndTypedEntity(Node *node, char basicKind, StringRef entityKind, - EntityContext &ctx) { + EntityContext &ctx, unsigned depth) { assert(node->getNumChildren() == 3 || node->getNumChildren() == 4); Buffer << basicKind; - mangleEntityContext(node->begin()[0], ctx); + mangleEntityContext(node->begin()[0], ctx, depth + 1); Buffer << entityKind; - mangleChildNode(node, 1); // decl name / index + mangleChildNode(node, 1, depth + 1); // decl name / index if (node->begin()[2]->getKind() == Node::Kind::LabelList) { auto LabelList = node->begin()[2]; auto Type = node->begin()[3]; - mangleEntityType(applyParamLabels(LabelList, Type, Factory), ctx); + mangleEntityType(applyParamLabels(LabelList, Type, Factory), ctx, + depth + 1); } else { - mangleEntityType(node->begin()[2], ctx); + mangleEntityType(node->begin()[2], ctx, depth + 1); } } -void Remangler::mangleEntityContext(Node *node, EntityContext &ctx) { +void Remangler::mangleEntityContext(Node *node, EntityContext &ctx, + unsigned depth) { // Remember that we're mangling a context. EntityContext::ManglingContextRAII raii(ctx); @@ -1052,7 +1168,7 @@ void Remangler::mangleEntityContext(Node *node, EntityContext &ctx) { case Node::Kind::BoundGenericClass: case Node::Kind::BoundGenericOtherNominalType: case Node::Kind::BoundGenericTypeAlias: - mangleAnyNominalType(node, ctx); + mangleAnyNominalType(node, ctx, depth + 1); return; default: @@ -1067,15 +1183,16 @@ void Remangler::mangleEntityContext(Node *node, EntityContext &ctx) { unreachable("not a context node"); #define NODE(ID) -#define CONTEXT_NODE(ID) \ - case Node::Kind::ID: \ - return mangle##ID(node, ctx); +#define CONTEXT_NODE(ID) \ + case Node::Kind::ID: \ + return mangle##ID(node, ctx, depth + 1); #include "swift/Demangling/DemangleNodes.def" } unreachable("bad node kind"); } -void Remangler::mangleEntityType(Node *node, EntityContext &ctx) { +void Remangler::mangleEntityType(Node *node, EntityContext &ctx, + unsigned depth) { assert(node->getKind() == Node::Kind::Type); assert(node->getNumChildren() == 1); node = node->begin()[0]; @@ -1089,47 +1206,47 @@ void Remangler::mangleEntityType(Node *node, EntityContext &ctx) { node->getKind() == Node::Kind::NoEscapeFunctionType) ? 'F' : 'f'); + assert(node->getNumChildren() >= 2); unsigned inputIndex = node->getNumChildren() - 2; - assert(inputIndex <= 1); for (unsigned i = 0; i <= inputIndex; ++i) - mangle(node->begin()[i]); + mangle(node->begin()[i], depth + 1); auto returnType = node->begin()[inputIndex+1]; assert(returnType->getKind() == Node::Kind::ReturnType); assert(returnType->getNumChildren() == 1); - mangleEntityType(returnType->begin()[0], ctx); + mangleEntityType(returnType->begin()[0], ctx, depth + 1); return; } default: - mangle(node); + mangle(node, depth + 1); return; } } -void Remangler::mangleLocalDeclName(Node *node) { +void Remangler::mangleLocalDeclName(Node *node, unsigned depth) { Buffer << 'L'; - mangleChildNodes(node); // index, identifier + mangleChildNodes(node, depth + 1); // index, identifier } -void Remangler::manglePrivateDeclName(Node *node) { +void Remangler::manglePrivateDeclName(Node *node, unsigned depth) { Buffer << 'P'; - mangleChildNodes(node); // identifier, identifier + mangleChildNodes(node, depth + 1); // identifier, identifier } -void Remangler::mangleRelatedEntityDeclName(Node *node) { +void Remangler::mangleRelatedEntityDeclName(Node *node, unsigned depth) { // Non-round-trip mangling: pretend we have a private discriminator "$A" for a // related entity "A". NodePointer kindNode = node->getFirstChild(); Buffer << 'P' << (kindNode->getText().size() + 1) << '$' << kindNode->getText(); - mangleChildNode(node, 1); + mangleChildNode(node, 1, depth + 1); } -void Remangler::mangleTypeMangling(Node *node) { +void Remangler::mangleTypeMangling(Node *node, unsigned depth) { Buffer << 't'; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleType(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleType(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); } template @@ -1141,7 +1258,7 @@ static bool stripPrefix(StringRef &string, const char (&data)[N]) { return true; } -void Remangler::mangleBuiltinTypeName(Node *node) { +void Remangler::mangleBuiltinTypeName(Node *node, unsigned depth) { Buffer << 'B'; StringRef text = node->getText(); @@ -1169,7 +1286,7 @@ void Remangler::mangleBuiltinTypeName(Node *node) { auto element = text.substr(splitIdx).substr(1); if (element == "RawPointer") { Buffer << 'p'; - } else if (stripPrefix(element, "Float")) { + } else if (stripPrefix(element, "FPIEEE")) { Buffer << 'f' << element << '_'; } else if (stripPrefix(element, "Int")) { Buffer << 'i' << element << '_'; @@ -1181,64 +1298,65 @@ void Remangler::mangleBuiltinTypeName(Node *node) { } } -void Remangler::mangleTypeAlias(Node *node, EntityContext &ctx) { - mangleAnyNominalType(node, ctx); +void Remangler::mangleTypeAlias(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleFunctionType(Node *node) { +void Remangler::mangleFunctionType(Node *node, unsigned depth) { Buffer << 'F'; - mangleChildNodes(node); // argument tuple, result type + mangleChildNodes(node, depth + 1); // argument tuple, result type } -void Remangler::mangleUncurriedFunctionType(Node *node) { +void Remangler::mangleUncurriedFunctionType(Node *node, unsigned depth) { Buffer << 'f'; - mangleChildNodes(node); // argument tuple, result type + mangleChildNodes(node, depth + 1); // argument tuple, result type } -void Remangler::mangleObjCBlock(Node *node) { +void Remangler::mangleObjCBlock(Node *node, unsigned depth) { Buffer << 'b'; - mangleChildNodes(node); // argument tuple, result type + mangleChildNodes(node, depth + 1); // argument tuple, result type } -void Remangler::mangleEscapingObjCBlock(Node *node) { +void Remangler::mangleEscapingObjCBlock(Node *node, unsigned depth) { // We shouldn't ever be remangling anything with a DWARF-only mangling. Buffer << ""; } -void Remangler::mangleCFunctionPointer(Node *node) { +void Remangler::mangleCFunctionPointer(Node *node, unsigned depth) { Buffer << 'c'; - mangleChildNodes(node); // argument tuple, result type + mangleChildNodes(node, depth + 1); // argument tuple, result type } -void Remangler::mangleAutoClosureType(Node *node) { +void Remangler::mangleAutoClosureType(Node *node, unsigned depth) { Buffer << 'K'; - mangleChildNodes(node); // argument tuple, result type + mangleChildNodes(node, depth + 1); // argument tuple, result type } -void Remangler::mangleNoEscapeFunctionType(Node *node) { +void Remangler::mangleNoEscapeFunctionType(Node *node, unsigned depth) { Buffer << 'F'; - mangleChildNodes(node); // argument tuple, result type + mangleChildNodes(node, depth + 1); // argument tuple, result type } -void Remangler::mangleEscapingAutoClosureType(Node *node) { +void Remangler::mangleEscapingAutoClosureType(Node *node, unsigned depth) { Buffer << 'K'; - mangleChildNodes(node); // argument tuple, result type + mangleChildNodes(node, depth + 1); // argument tuple, result type } -void Remangler::mangleThinFunctionType(Node *node) { +void Remangler::mangleThinFunctionType(Node *node, unsigned depth) { Buffer << "Xf"; - mangleChildNodes(node); // argument tuple, result type + mangleChildNodes(node, depth + 1); // argument tuple, result type } -void Remangler::mangleArgumentTuple(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleArgumentTuple(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleReturnType(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleReturnType(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleImplFunctionType(Node *node) { +void Remangler::mangleImplFunctionType(Node *node, unsigned depth) { Buffer << "XF"; auto i = node->begin(), e = node->end(); if (i != e && (*i)->getKind() == Node::Kind::ImplConvention) { @@ -1258,26 +1376,26 @@ void Remangler::mangleImplFunctionType(Node *node) { } for (; i != e && (*i)->getKind() == Node::Kind::ImplFunctionAttribute; ++i) { - mangle(*i); // impl function attribute + mangle(*i, depth + 1); // impl function attribute } if (i != e && ((*i)->getKind() == Node::Kind::DependentGenericSignature || (*i)->getKind() == Node::Kind::DependentPseudogenericSignature)) { Buffer << ((*i)->getKind() == Node::Kind::DependentGenericSignature ? 'G' : 'g'); - mangleDependentGenericSignature((*i)); + mangleDependentGenericSignature((*i), depth + 1); ++i; } Buffer << '_'; for (; i != e && (*i)->getKind() == Node::Kind::ImplParameter; ++i) { - mangleImplParameter(*i); + mangleImplParameter(*i, depth + 1); } Buffer << '_'; - mangleNodes(i, e); // impl results + mangleNodes(i, e, depth + 1); // impl results Buffer << '_'; } -void Remangler::mangleImplFunctionAttribute(Node *node) { +void Remangler::mangleImplFunctionAttribute(Node *node, unsigned depth) { StringRef text = node->getText(); if (text == "@yield_once") { Buffer << "A"; @@ -1292,11 +1410,11 @@ void Remangler::mangleImplFunctionAttribute(Node *node) { } } -void Remangler::mangleImplFunctionConvention(Node *node) { - mangle(node->getChild(0)); +void Remangler::mangleImplFunctionConvention(Node *node, unsigned depth) { + mangle(node->getChild(0), depth + 1); } -void Remangler::mangleImplFunctionConventionName(Node *node) { +void Remangler::mangleImplFunctionConventionName(Node *node, unsigned depth) { StringRef text = node->getText(); if (text == "block") { Buffer << "Cb"; @@ -1313,48 +1431,50 @@ void Remangler::mangleImplFunctionConventionName(Node *node) { } } -void Remangler::mangleClangType(Node *node) { unreachable("unsupported"); } +void Remangler::mangleClangType(Node *node, unsigned depth) { + unreachable("unsupported"); +} -void Remangler::mangleImplParameter(Node *node) { +void Remangler::mangleImplParameter(Node *node, unsigned depth) { assert(node->getNumChildren() == 2); - mangleChildNodes(node); // impl convention, type + mangleChildNodes(node, depth + 1); // impl convention, type } -void Remangler::mangleImplErrorResult(Node *node) { +void Remangler::mangleImplErrorResult(Node *node, unsigned depth) { assert(node->getNumChildren() == 2); Buffer << 'z'; - mangleChildNodes(node); // impl convention, type + mangleChildNodes(node, depth + 1); // impl convention, type } -void Remangler::mangleImplResult(Node *node) { +void Remangler::mangleImplResult(Node *node, unsigned depth) { assert(node->getNumChildren() == 2); - mangleChildNodes(node); // impl convention, type + mangleChildNodes(node, depth + 1); // impl convention, type } -void Remangler::mangleImplYield(Node *node) { +void Remangler::mangleImplYield(Node *node, unsigned depth) { assert(node->getNumChildren() == 2); Buffer << 'Y'; - mangleChildNodes(node); // impl convention, type + mangleChildNodes(node, depth + 1); // impl convention, type } -void Remangler::mangleImplDifferentiabilityKind(Node *node) { +void Remangler::mangleImplDifferentiabilityKind(Node *node, unsigned depth) { // TODO(TF-750): Check if this code path actually triggers and add a test. Buffer << (char)node->getIndex(); } -void Remangler::mangleImplEscaping(Node *node) { +void Remangler::mangleImplEscaping(Node *node, unsigned depth) { // The old mangler does not encode escaping. } -void Remangler::mangleImplPatternSubstitutions(Node *node) { +void Remangler::mangleImplPatternSubstitutions(Node *node, unsigned depth) { // The old mangler does not encode substituted function types. } -void Remangler::mangleImplInvocationSubstitutions(Node *node) { +void Remangler::mangleImplInvocationSubstitutions(Node *node, unsigned depth) { // The old mangler does not encode substituted function types. } -void Remangler::mangleImplConvention(Node *node) { +void Remangler::mangleImplConvention(Node *node, unsigned depth) { assert(node->getKind() == Node::Kind::ImplConvention); StringRef text = node->getText(); if (text == "@autoreleased") { @@ -1380,7 +1500,8 @@ void Remangler::mangleImplConvention(Node *node) { } } -void Remangler::mangleImplParameterResultDifferentiability(Node *node) { +void Remangler::mangleImplParameterResultDifferentiability(Node *node, + unsigned depth) { assert(node->getKind() == Node::Kind::ImplDifferentiabilityKind); StringRef text = node->getText(); // Empty string represents default differentiability. @@ -1393,43 +1514,41 @@ void Remangler::mangleImplParameterResultDifferentiability(Node *node) { unreachable("Invalid impl differentiability"); } -void Remangler::mangleDynamicSelf(Node *node) { +void Remangler::mangleDynamicSelf(Node *node, unsigned depth) { Buffer << 'D'; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleErrorType(Node *node) { - Buffer << "ERR"; -} +void Remangler::mangleErrorType(Node *node, unsigned depth) { Buffer << "ERR"; } -void Remangler::mangleSILBoxType(Node *node) { +void Remangler::mangleSILBoxType(Node *node, unsigned depth) { Buffer << 'X' << 'b'; - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleMetatype(Node *node) { +void Remangler::mangleMetatype(Node *node, unsigned depth) { if (node->getNumChildren() == 1) { Buffer << 'M'; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } else { assert(node->getNumChildren() == 2); Buffer << "XM"; - mangleChildNodes(node); // metatype representation, type + mangleChildNodes(node, depth + 1); // metatype representation, type } } -void Remangler::mangleExistentialMetatype(Node *node) { +void Remangler::mangleExistentialMetatype(Node *node, unsigned depth) { if (node->getNumChildren() == 1) { Buffer << "PM"; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } else { assert(node->getNumChildren() == 2); Buffer << "XPM"; - mangleChildNodes(node); // metatype representation, type + mangleChildNodes(node, depth + 1); // metatype representation, type } } -void Remangler::mangleMetatypeRepresentation(Node *node) { +void Remangler::mangleMetatypeRepresentation(Node *node, unsigned depth) { StringRef text = node->getText(); if (text == "@thin") { Buffer << 't'; @@ -1442,55 +1561,60 @@ void Remangler::mangleMetatypeRepresentation(Node *node) { } } -void Remangler::mangleProtocolList(Node *node) { +void Remangler::mangleProtocolList(Node *node, unsigned depth) { // In its usual use as a type, this gets a prefix 'P'. Buffer << 'P'; - mangleProtocolListWithoutPrefix(node); + mangleProtocolListWithoutPrefix(node, depth + 1); } -void Remangler::mangleProtocolListWithoutPrefix(Node *node, +void Remangler::mangleProtocolListWithoutPrefix(Node *node, unsigned depth, Node *additionalProto) { assert(node->getKind() == Node::Kind::ProtocolList); assert(node->getNumChildren() == 1); auto typeList = node->begin()[0]; assert(typeList->getKind() == Node::Kind::TypeList); for (auto &child : *typeList) { - mangleProtocolWithoutPrefix(child); + mangleProtocolWithoutPrefix(child, depth + 1); } if (additionalProto) { - mangleProtocolWithoutPrefix(additionalProto); + mangleProtocolWithoutPrefix(additionalProto, depth + 1); } Buffer << '_'; } -#define REF_STORAGE(Name, ...) \ - void Remangler::mangle##Name(Node *node) { \ - Buffer << manglingOf(ReferenceOwnership::Name); \ - mangleSingleChildNode(node); /* type */ \ +#define REF_STORAGE(Name, ...) \ + void Remangler::mangle##Name(Node *node, unsigned depth) { \ + Buffer << manglingOf(ReferenceOwnership::Name); \ + mangleSingleChildNode(node, depth + 1); /* type */ \ } #include "swift/AST/ReferenceStorage.def" -void Remangler::mangleShared(Node *node) { +void Remangler::mangleShared(Node *node, unsigned depth) { Buffer << 'h'; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleOwned(Node *node) { +void Remangler::mangleOwned(Node *node, unsigned depth) { Buffer << 'n'; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleInOut(Node *node) { +void Remangler::mangleInOut(Node *node, unsigned depth) { Buffer << 'R'; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleNoDerivative(Node *node) { +void Remangler::mangleIsolated(Node *node, unsigned depth) { + Buffer << "Yi"; + mangleSingleChildNode(node, depth + 1); // type +} + +void Remangler::mangleNoDerivative(Node *node, unsigned depth) { Buffer << 'k'; - mangleSingleChildNode(node); // type + mangleSingleChildNode(node, depth + 1); // type } -void Remangler::mangleTuple(Node *node) { +void Remangler::mangleTuple(Node *node, unsigned depth) { size_t NumElems = node->getNumChildren(); if (NumElems > 0 && node->getChild(NumElems - 1)->getFirstChild()->getKind() == @@ -1499,28 +1623,29 @@ void Remangler::mangleTuple(Node *node) { } else { Buffer << 'T'; } - mangleChildNodes(node); // tuple elements + mangleChildNodes(node, depth + 1); // tuple elements Buffer << '_'; } -void Remangler::mangleTupleElement(Node *node) { - mangleChildNodes(node); // tuple element name?, type +void Remangler::mangleTupleElement(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); // tuple element name?, type } -void Remangler::mangleTupleElementName(Node *node) { +void Remangler::mangleTupleElementName(Node *node, unsigned depth) { mangleIdentifier(node->getText(), OperatorKind::NotOperator); } -void Remangler::mangleDependentGenericType(Node *node) { +void Remangler::mangleDependentGenericType(Node *node, unsigned depth) { Buffer << 'u'; - mangleChildNodes(node); // generic signature, type + mangleChildNodes(node, depth + 1); // generic signature, type } -void Remangler::mangleDependentPseudogenericSignature(Node *node) { - mangleDependentGenericSignature(node); +void Remangler::mangleDependentPseudogenericSignature(Node *node, + unsigned depth) { + mangleDependentGenericSignature(node, depth + 1); } -void Remangler::mangleDependentGenericSignature(Node *node) { +void Remangler::mangleDependentGenericSignature(Node *node, unsigned depth) { auto i = node->begin(), e = node->end(); // If there's only one generic param, mangle nothing. @@ -1551,36 +1676,39 @@ void Remangler::mangleDependentGenericSignature(Node *node) { } Buffer << 'R'; - mangleNodes(i, e); // generic requirements + mangleNodes(i, e, depth + 1); // generic requirements Buffer << 'r'; } -void Remangler::mangleDependentGenericParamCount(Node *node) { +void Remangler::mangleDependentGenericParamCount(Node *node, unsigned depth) { unreachable("handled inline in DependentGenericSignature"); } -void Remangler::mangleDependentGenericConformanceRequirement(Node *node) { - mangleConstrainedType(node->getChild(0)); +void Remangler::mangleDependentGenericConformanceRequirement(Node *node, + unsigned depth) { + mangleConstrainedType(node->getChild(0), depth + 1); // If the constraint represents a protocol, use the shorter mangling. if (node->getNumChildren() == 2 && node->getChild(1)->getKind() == Node::Kind::Type && node->getChild(1)->getNumChildren() == 1 && node->getChild(1)->getChild(0)->getKind() == Node::Kind::Protocol) { - mangleProtocolWithoutPrefix(node->getChild(1)->getChild(0)); + mangleProtocolWithoutPrefix(node->getChild(1)->getChild(0), depth + 1); return; } - mangle(node->getChild(1)); + mangle(node->getChild(1), depth + 1); } -void Remangler::mangleDependentGenericSameTypeRequirement(Node *node) { - mangleConstrainedType(node->getChild(0)); +void Remangler::mangleDependentGenericSameTypeRequirement(Node *node, + unsigned depth) { + mangleConstrainedType(node->getChild(0), depth + 1); Buffer << 'z'; - mangle(node->getChild(1)); + mangle(node->getChild(1), depth + 1); } -void Remangler::mangleDependentGenericLayoutRequirement(Node *node) { - mangleConstrainedType(node->getChild(0)); +void Remangler::mangleDependentGenericLayoutRequirement(Node *node, + unsigned depth) { + mangleConstrainedType(node->getChild(0), depth + 1); Buffer << 'l'; auto id = node->getChild(1)->getText(); auto size = -1; @@ -1599,53 +1727,55 @@ void Remangler::mangleDependentGenericLayoutRequirement(Node *node) { } } -void Remangler::mangleConstrainedType(Node *node) { +void Remangler::mangleConstrainedType(Node *node, unsigned depth) { if (node->getFirstChild()->getKind() == Node::Kind::DependentGenericParamType) { // Can be mangled without an introducer. - mangleDependentGenericParamIndex(node->getFirstChild()); + mangleDependentGenericParamIndex(node->getFirstChild(), depth + 1); } else { - mangle(node); + mangle(node, depth + 1); } } -void Remangler::mangleAssociatedType(Node *node) { +void Remangler::mangleAssociatedType(Node *node, unsigned depth) { if (node->hasChildren()) { assert(node->getNumChildren() == 1); - mangleProtocolListWithoutPrefix(*node->begin()); + mangleProtocolListWithoutPrefix(*node->begin(), depth + 1); } else { Buffer << '_'; } } -void Remangler::mangleDeclContext(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleDeclContext(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleExtension(Node *node, EntityContext &ctx) { +void Remangler::mangleExtension(Node *node, EntityContext &ctx, + unsigned depth) { assert(node->getNumChildren() == 2 || node->getNumChildren() == 3); if (node->getNumChildren() == 3) { Buffer << 'e'; } else { Buffer << 'E'; } - mangleEntityContext(node->begin()[0], ctx); // module + mangleEntityContext(node->begin()[0], ctx, depth + 1); // module if (node->getNumChildren() == 3) { - mangleDependentGenericSignature(node->begin()[2]); // generic sig + mangleDependentGenericSignature(node->begin()[2], depth + 1); // generic sig } - mangleEntityContext(node->begin()[1], ctx); // context + mangleEntityContext(node->begin()[1], ctx, depth + 1); // context } -void Remangler::mangleAnonymousContext(Node *node, EntityContext &ctx) { - mangleEntityContext(node->getChild(1), ctx); +void Remangler::mangleAnonymousContext(Node *node, EntityContext &ctx, + unsigned depth) { + mangleEntityContext(node->getChild(1), ctx, depth + 1); // Since we can't change the old mangling, mangle an anonymous context by // introducing a private discriminator onto its child contexts. ctx.setAnonymousContextDiscriminator(node->getChild(0)->getText()); } -void Remangler::mangleModule(Node *node, EntityContext &ctx) { +void Remangler::mangleModule(Node *node, EntityContext &ctx, unsigned depth) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; @@ -1655,15 +1785,15 @@ void Remangler::mangleModule(Node *node, EntityContext &ctx) { addSubstitution(entry); } -void Remangler::mangleAssociatedTypeRef(Node *node) { +void Remangler::mangleAssociatedTypeRef(Node *node, unsigned depth) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; Buffer << "Q"; - mangleChildNodes(node); // type, identifier + mangleChildNodes(node, depth + 1); // type, identifier addSubstitution(entry); } -void Remangler::mangleDependentMemberType(Node *node) { +void Remangler::mangleDependentMemberType(Node *node, unsigned depth) { Vector members; Node *base = node; do { @@ -1677,40 +1807,40 @@ void Remangler::mangleDependentMemberType(Node *node) { assert(members.size() >= 1); if (members.size() == 1) { Buffer << 'w'; - mangleDependentGenericParamIndex(base); - mangle(members[0]->getChild(1)); + mangleDependentGenericParamIndex(base, depth + 1); + mangle(members[0]->getChild(1), depth + 1); } else { Buffer << 'W'; - mangleDependentGenericParamIndex(base); + mangleDependentGenericParamIndex(base, depth + 1); for (unsigned i = 1, n = members.size(); i <= n; ++i) { Node *member = members[n - i]; - mangle(member->getChild(1)); + mangle(member->getChild(1), depth + 1); } Buffer << '_'; } } -void Remangler::mangleDependentAssociatedTypeRef(Node *node) { +void Remangler::mangleDependentAssociatedTypeRef(Node *node, unsigned depth) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; if (node->getNumChildren() > 1) { Buffer << 'P'; - mangleProtocolWithoutPrefix(node->getChild(1)); + mangleProtocolWithoutPrefix(node->getChild(1), depth + 1); } - mangleIdentifier(node->getFirstChild()); + mangleIdentifier(node->getFirstChild(), depth + 1); addSubstitution(entry); } -void Remangler::mangleDependentGenericParamIndex(Node *node) { - auto depth = node->getChild(0)->getIndex(); +void Remangler::mangleDependentGenericParamIndex(Node *node, unsigned depth) { + auto paramDepth = node->getChild(0)->getIndex(); auto index = node->getChild(1)->getIndex(); - if (depth != 0) { + if (paramDepth != 0) { Buffer << 'd'; - mangleIndex(depth - 1); + mangleIndex(paramDepth - 1); mangleIndex(index); return; } @@ -1719,11 +1849,11 @@ void Remangler::mangleDependentGenericParamIndex(Node *node) { return; } - // depth == index == 0 + // paramDepth == index == 0 Buffer << 'x'; } -void Remangler::mangleDependentGenericParamType(Node *node) { +void Remangler::mangleDependentGenericParamType(Node *node, unsigned depth) { if (node->getChild(0)->getIndex() == 0 && node->getChild(1)->getIndex() == 0) { Buffer << 'x'; @@ -1731,22 +1861,22 @@ void Remangler::mangleDependentGenericParamType(Node *node) { } Buffer << 'q'; - mangleDependentGenericParamIndex(node); + mangleDependentGenericParamIndex(node, depth + 1); } -void Remangler::mangleIndex(Node *node) { +void Remangler::mangleIndex(Node *node, unsigned depth) { mangleIndex(node->getIndex()); } -void Remangler::mangleUnknownIndex(Node *node) { +void Remangler::mangleUnknownIndex(Node *node, unsigned depth) { unreachable("should not be reached in an arbitrary context"); } -void Remangler::mangleProtocol(Node *node, EntityContext &ctx) { - mangleNominalType(node, 'P', ctx); +void Remangler::mangleProtocol(Node *node, EntityContext &ctx, unsigned depth) { + mangleNominalType(node, 'P', ctx, depth + 1); } -void Remangler::mangleProtocolWithoutPrefix(Node *node) { +void Remangler::mangleProtocolWithoutPrefix(Node *node, unsigned depth) { if (mangleStandardSubstitution(node)) return; @@ -1757,16 +1887,17 @@ void Remangler::mangleProtocolWithoutPrefix(Node *node) { assert(node->getKind() == Node::Kind::Protocol); EntityContext ctx; - mangleNominalType(node, '\0', ctx); + mangleNominalType(node, '\0', ctx, depth + 1); } -void Remangler::mangleGenericArgs(Node *node, EntityContext &ctx) { +void Remangler::mangleGenericArgs(Node *node, EntityContext &ctx, + unsigned depth) { switch (node->getKind()) { case Node::Kind::Structure: case Node::Kind::Enum: case Node::Kind::Class: { NodePointer parentOrModule = node->getChild(0); - mangleGenericArgs(parentOrModule, ctx); + mangleGenericArgs(parentOrModule, ctx, depth + 1); // No generic arguments at this level Buffer << '_'; @@ -1780,15 +1911,15 @@ void Remangler::mangleGenericArgs(Node *node, EntityContext &ctx) { assert(unboundType->getKind() == Node::Kind::Type); NodePointer nominalType = unboundType->getChild(0); NodePointer parentOrModule = nominalType->getChild(0); - mangleGenericArgs(parentOrModule, ctx); + mangleGenericArgs(parentOrModule, ctx, depth + 1); - mangleTypeList(node->getChild(1)); + mangleTypeList(node->getChild(1), depth + 1); break; } case Node::Kind::AnonymousContext: case Node::Kind::Extension: { - mangleGenericArgs(node->getChild(1), ctx); + mangleGenericArgs(node->getChild(1), ctx, depth + 1); break; } @@ -1797,274 +1928,296 @@ void Remangler::mangleGenericArgs(Node *node, EntityContext &ctx) { } } -void Remangler::mangleAnyNominalType(Node *node, EntityContext &ctx) { +void Remangler::mangleAnyNominalType(Node *node, EntityContext &ctx, + unsigned depth) { + if (depth > Remangler::MaxDepth) { + // FIXME: error handling needs doing properly (rdar://79725187) + unreachable("too complex to remangle"); + } + if (isSpecialized(node)) { Buffer << 'G'; NodePointer unboundType = getUnspecialized(node, Factory); - mangleAnyNominalType(unboundType, ctx); - mangleGenericArgs(node, ctx); + mangleAnyNominalType(unboundType, ctx, depth + 1); + mangleGenericArgs(node, ctx, depth + 1); return; } switch (node->getKind()) { + case Node::Kind::Type: + mangleAnyNominalType(node->getChild(0), ctx, depth + 1); + break; case Node::Kind::OtherNominalType: // Mangle unknown type kinds as structures since we can't change the old // mangling. Give the mangling an artificial "private discriminator" so that // clients who understand the old mangling know this is an unstable // mangled name. - mangleNominalType(node, 'V', ctx, "_UnknownTypeKind"); + mangleNominalType(node, 'V', ctx, depth + 1, "_UnknownTypeKind"); break; case Node::Kind::Structure: - mangleNominalType(node, 'V', ctx); + mangleNominalType(node, 'V', ctx, depth + 1); break; case Node::Kind::Enum: - mangleNominalType(node, 'O', ctx); + mangleNominalType(node, 'O', ctx, depth + 1); break; case Node::Kind::Class: - mangleNominalType(node, 'C', ctx); + mangleNominalType(node, 'C', ctx, depth + 1); break; case Node::Kind::TypeAlias: - mangleNominalType(node, 'a', ctx); + mangleNominalType(node, 'a', ctx, depth + 1); break; default: unreachable("bad nominal type kind"); } } -void Remangler::mangleStructure(Node *node, EntityContext &ctx) { - mangleAnyNominalType(node, ctx); +void Remangler::mangleStructure(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleEnum(Node *node, EntityContext &ctx) { - mangleAnyNominalType(node, ctx); +void Remangler::mangleEnum(Node *node, EntityContext &ctx, unsigned depth) { + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleClass(Node *node, EntityContext &ctx) { - mangleAnyNominalType(node, ctx); +void Remangler::mangleClass(Node *node, EntityContext &ctx, unsigned depth) { + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleOtherNominalType(Node *node, EntityContext &ctx) { - mangleAnyNominalType(node, ctx); +void Remangler::mangleOtherNominalType(Node *node, EntityContext &ctx, + unsigned depth) { + mangleAnyNominalType(node, ctx, depth + 1); } void Remangler::mangleNominalType(Node *node, char kind, EntityContext &ctx, + unsigned depth, StringRef artificialPrivateDiscriminator) { SubstitutionEntry entry; + if (node->getKind() == Node::Kind::Type) { + node = node->getChild(0); + } if (trySubstitution(node, entry)) return; - mangleNamedEntity(node, kind, "", ctx, artificialPrivateDiscriminator); + mangleNamedEntity(node, kind, "", ctx, depth + 1, + artificialPrivateDiscriminator); addSubstitution(entry); } -void Remangler::mangleBoundGenericClass(Node *node) { +void Remangler::mangleBoundGenericClass(Node *node, unsigned depth) { EntityContext ctx; - mangleAnyNominalType(node, ctx); + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleBoundGenericStructure(Node *node) { +void Remangler::mangleBoundGenericStructure(Node *node, unsigned depth) { EntityContext ctx; - mangleAnyNominalType(node, ctx); + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleBoundGenericEnum(Node *node) { +void Remangler::mangleBoundGenericEnum(Node *node, unsigned depth) { EntityContext ctx; - mangleAnyNominalType(node, ctx); + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleBoundGenericOtherNominalType(Node *node) { +void Remangler::mangleBoundGenericOtherNominalType(Node *node, unsigned depth) { EntityContext ctx; - mangleAnyNominalType(node, ctx); + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleBoundGenericProtocol(Node *node) { +void Remangler::mangleBoundGenericProtocol(Node *node, unsigned depth) { EntityContext ctx; - mangleAnyNominalType(node, ctx); + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleBoundGenericTypeAlias(Node *node) { +void Remangler::mangleBoundGenericTypeAlias(Node *node, unsigned depth) { EntityContext ctx; - mangleAnyNominalType(node, ctx); + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleBoundGenericFunction(Node *node) { +void Remangler::mangleBoundGenericFunction(Node *node, unsigned depth) { EntityContext ctx; // Not really a nominal type, but it works for functions, too. - mangleAnyNominalType(node, ctx); + mangleAnyNominalType(node, ctx, depth + 1); } -void Remangler::mangleTypeList(Node *node) { - mangleChildNodes(node); // all types +void Remangler::mangleTypeList(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); // all types Buffer << '_'; } -void Remangler::mangleLabelList(Node *node) { +void Remangler::mangleLabelList(Node *node, unsigned depth) { if (node->getNumChildren() == 0) Buffer << 'y'; else - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleReflectionMetadataBuiltinDescriptor(Node *node) { +void Remangler::mangleReflectionMetadataBuiltinDescriptor(Node *node, + unsigned depth) { Buffer << "MRb"; } -void Remangler::mangleReflectionMetadataFieldDescriptor(Node *node) { +void Remangler::mangleReflectionMetadataFieldDescriptor(Node *node, + unsigned depth) { Buffer << "MRf"; } -void Remangler::mangleReflectionMetadataAssocTypeDescriptor(Node *node) { +void Remangler::mangleReflectionMetadataAssocTypeDescriptor(Node *node, + unsigned depth) { Buffer << "MRa"; } -void Remangler::mangleReflectionMetadataSuperclassDescriptor(Node *node) { +void Remangler::mangleReflectionMetadataSuperclassDescriptor(Node *node, + unsigned depth) { Buffer << "MRc"; } -void Remangler::mangleGenericTypeParamDecl(Node *node) { +void Remangler::mangleGenericTypeParamDecl(Node *node, unsigned depth) { unreachable("todo"); } -void Remangler::mangleCurryThunk(Node *node) { +void Remangler::mangleCurryThunk(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleDispatchThunk(Node *node) { +void Remangler::mangleDispatchThunk(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleMethodDescriptor(Node *node) { +void Remangler::mangleMethodDescriptor(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleMethodLookupFunction(Node *node) { +void Remangler::mangleMethodLookupFunction(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleObjCMetadataUpdateFunction(Node *node) { +void Remangler::mangleObjCMetadataUpdateFunction(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleObjCResilientClassStub(Node *node) { +void Remangler::mangleObjCResilientClassStub(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleFullObjCResilientClassStub(Node *node) { +void Remangler::mangleFullObjCResilientClassStub(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleEmptyList(Node *node) { +void Remangler::mangleEmptyList(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleFirstElementMarker(Node *node) { +void Remangler::mangleFirstElementMarker(Node *node, unsigned depth) { Buffer << ""; } -void Remangler::mangleVariadicMarker(Node *node) { +void Remangler::mangleVariadicMarker(Node *node, unsigned depth) { // Handled in mangleTuple } -void Remangler::mangleOutlinedCopy(Node *node) { +void Remangler::mangleOutlinedCopy(Node *node, unsigned depth) { Buffer << "Wy"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleOutlinedConsume(Node *node) { +void Remangler::mangleOutlinedConsume(Node *node, unsigned depth) { Buffer << "We"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleOutlinedRetain(Node *node) { +void Remangler::mangleOutlinedRetain(Node *node, unsigned depth) { Buffer << "Wr"; - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleOutlinedRelease(Node *node) { +void Remangler::mangleOutlinedRelease(Node *node, unsigned depth) { Buffer << "Ws"; - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleOutlinedInitializeWithTake(Node *node) { +void Remangler::mangleOutlinedInitializeWithTake(Node *node, unsigned depth) { Buffer << "Wb"; - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleOutlinedInitializeWithCopy(Node *node) { +void Remangler::mangleOutlinedInitializeWithCopy(Node *node, unsigned depth) { Buffer << "Wc"; - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleOutlinedAssignWithTake(Node *node) { +void Remangler::mangleOutlinedAssignWithTake(Node *node, unsigned depth) { Buffer << "Wd"; - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleOutlinedAssignWithCopy(Node *node) { +void Remangler::mangleOutlinedAssignWithCopy(Node *node, unsigned depth) { Buffer << "Wf"; - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleOutlinedDestroy(Node *node) { +void Remangler::mangleOutlinedDestroy(Node *node, unsigned depth) { Buffer << "Wh"; - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleOutlinedVariable(Node *node) { +void Remangler::mangleOutlinedVariable(Node *node, unsigned depth) { Buffer << "Tv" << node->getIndex(); - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleOutlinedBridgedMethod(Node *node) { +void Remangler::mangleOutlinedBridgedMethod(Node *node, unsigned depth) { Buffer << "Te" << node->getText(); - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleCoroutineContinuationPrototype(Node *node) { +void Remangler::mangleCoroutineContinuationPrototype(Node *node, + unsigned depth) { Buffer << "TC"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleKeyPathGetterThunkHelper(Node *node) { +void Remangler::mangleKeyPathGetterThunkHelper(Node *node, unsigned depth) { Buffer << "TK"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleKeyPathSetterThunkHelper(Node *node) { +void Remangler::mangleKeyPathSetterThunkHelper(Node *node, unsigned depth) { Buffer << "Tk"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleKeyPathEqualsThunkHelper(Node *node) { +void Remangler::mangleKeyPathEqualsThunkHelper(Node *node, unsigned depth) { Buffer << "TH"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleKeyPathHashThunkHelper(Node *node) { +void Remangler::mangleKeyPathHashThunkHelper(Node *node, unsigned depth) { Buffer << "Th"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleProtocolListWithClass(Node *node) { +void Remangler::mangleProtocolListWithClass(Node *node, unsigned depth) { Buffer << "Xc"; - mangleChildNode(node, 1); - mangleProtocolListWithoutPrefix(node->getChild(0)); + mangleChildNode(node, 1, depth + 1); + mangleProtocolListWithoutPrefix(node->getChild(0), depth + 1); } -void Remangler::mangleProtocolListWithAnyObject(Node *node) { +void Remangler::mangleProtocolListWithAnyObject(Node *node, unsigned depth) { Node *P = Factory.createNode(Node::Kind::Protocol); P->addChild(Factory.createNode(Node::Kind::Module, "Swift"), Factory); P->addChild(Factory.createNode(Node::Kind::Identifier, "AnyObject"), Factory); Buffer << "P"; - mangleProtocolListWithoutPrefix(node->getChild(0), /*additionalProto*/ P); + mangleProtocolListWithoutPrefix(node->getChild(0), depth + 1, + /*additionalProto*/ P); } -void Remangler::mangleVTableThunk(Node *node) { +void Remangler::mangleVTableThunk(Node *node, unsigned depth) { Buffer << "TV"; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleSILBoxTypeWithLayout(Node *node) { +void Remangler::mangleSILBoxTypeWithLayout(Node *node, unsigned depth) { assert(node->getKind() == Node::Kind::SILBoxTypeWithLayout); assert(node->getNumChildren() == 1 || node->getNumChildren() == 3); Buffer << "XB"; @@ -2078,162 +2231,174 @@ void Remangler::mangleSILBoxTypeWithLayout(Node *node) { assert(genericArgs->getKind() == Node::Kind::TypeList); Buffer << 'G'; - mangleDependentGenericSignature(signature); + mangleDependentGenericSignature(signature, depth + 1); } - mangleSILBoxLayout(layout); + mangleSILBoxLayout(layout, depth + 1); if (genericArgs) { for (unsigned i = 0; i < genericArgs->getNumChildren(); ++i) { auto type = genericArgs->getChild(i); assert(genericArgs->getKind() == Node::Kind::Type); - mangleType(type); + mangleType(type, depth + 1); } Buffer << '_'; } } -void Remangler::mangleSILBoxLayout(Node *node) { +void Remangler::mangleSILBoxLayout(Node *node, unsigned depth) { assert(node->getKind() == Node::Kind::SILBoxLayout); for (unsigned i = 0; i < node->getNumChildren(); ++i) { assert(node->getKind() == Node::Kind::SILBoxImmutableField || node->getKind() == Node::Kind::SILBoxMutableField); - mangle(node->getChild(i)); - + mangle(node->getChild(i), depth + 1); } Buffer << '_'; } -void Remangler::mangleSILBoxMutableField(Node *node) { +void Remangler::mangleSILBoxMutableField(Node *node, unsigned depth) { Buffer << 'm'; assert(node->getNumChildren() == 1 && node->getChild(0)->getKind() == Node::Kind::Type); - mangleType(node->getChild(0)); + mangleType(node->getChild(0), depth + 1); } -void Remangler::mangleSILBoxImmutableField(Node *node) { +void Remangler::mangleSILBoxImmutableField(Node *node, unsigned depth) { Buffer << 'i'; assert(node->getNumChildren() == 1 && node->getChild(0)->getKind() == Node::Kind::Type); - mangleType(node->getChild(0)); + mangleType(node->getChild(0), depth + 1); } -void Remangler::mangleAssocTypePath(Node *node) { +void Remangler::mangleAssocTypePath(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleModuleDescriptor(Node *node) { +void Remangler::mangleModuleDescriptor(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleExtensionDescriptor(Node *node) { +void Remangler::mangleExtensionDescriptor(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleAnonymousDescriptor(Node *node) { +void Remangler::mangleAnonymousDescriptor(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleAssociatedTypeGenericParamRef(Node *node) { +void Remangler::mangleAssociatedTypeGenericParamRef(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleTypeSymbolicReference(Node *node, EntityContext&) { +void Remangler::mangleTypeSymbolicReference(Node *node, EntityContext &, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleProtocolSymbolicReference(Node *node, EntityContext&) { +void Remangler::mangleProtocolSymbolicReference(Node *node, EntityContext &, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleOpaqueTypeDescriptorSymbolicReference(Node *node) { +void Remangler::mangleOpaqueTypeDescriptorSymbolicReference(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleSugaredOptional(Node *node) { +void Remangler::mangleSugaredOptional(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleSugaredArray(Node *node) { +void Remangler::mangleSugaredArray(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleSugaredDictionary(Node *node) { +void Remangler::mangleSugaredDictionary(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleSugaredParen(Node *node) { +void Remangler::mangleSugaredParen(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleOpaqueReturnType(Node *node) { +void Remangler::mangleOpaqueReturnType(Node *node, unsigned depth) { Buffer << "Qu"; } -void Remangler::mangleOpaqueReturnTypeOf(Node *node, EntityContext &ctx) { +void Remangler::mangleOpaqueReturnTypeOf(Node *node, EntityContext &ctx, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleOpaqueType(Node *node) { +void Remangler::mangleOpaqueType(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleOpaqueTypeDescriptor(Node *node) { +void Remangler::mangleOpaqueTypeDescriptor(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleOpaqueTypeDescriptorAccessor(Node *node) { +void Remangler::mangleOpaqueTypeDescriptorAccessor(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleOpaqueTypeDescriptorAccessorImpl(Node *node) { +void Remangler::mangleOpaqueTypeDescriptorAccessorImpl(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleOpaqueTypeDescriptorAccessorKey(Node *node) { +void Remangler::mangleOpaqueTypeDescriptorAccessorKey(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleOpaqueTypeDescriptorAccessorVar(Node *node) { +void Remangler::mangleOpaqueTypeDescriptorAccessorVar(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleAccessorFunctionReference(Node *node) { +void Remangler::mangleAccessorFunctionReference(Node *node, unsigned depth) { unreachable("can't remangle"); } -void Remangler::mangleMetadataInstantiationCache(Node *node) { +void Remangler::mangleMetadataInstantiationCache(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGlobalVariableOnceToken(Node *node) { +void Remangler::mangleGlobalVariableOnceToken(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGlobalVariableOnceFunction(Node *node) { +void Remangler::mangleGlobalVariableOnceFunction(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleGlobalVariableOnceDeclList(Node *node) { +void Remangler::mangleGlobalVariableOnceDeclList(Node *node, unsigned depth) { unreachable("unsupported"); } -void Remangler::manglePredefinedObjCAsyncCompletionHandlerImpl(Node *node) { +void Remangler::manglePredefinedObjCAsyncCompletionHandlerImpl(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleObjCAsyncCompletionHandlerImpl(Node *node) { +void Remangler::mangleObjCAsyncCompletionHandlerImpl(Node *node, + unsigned depth) { unreachable("unsupported"); } -void Remangler::mangleCanonicalSpecializedGenericMetaclass(Node *node) { - mangleSingleChildNode(node); // type +void Remangler::mangleCanonicalSpecializedGenericMetaclass(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); // type Buffer << "MM"; } void Remangler::mangleCanonicalSpecializedGenericTypeMetadataAccessFunction( - Node *node) { - mangleSingleChildNode(node); + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mb"; } -void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadata(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadata( + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MN"; } -void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadataCache(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadataCache( + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MJ"; } void Remangler::mangleCanonicalPrespecializedGenericTypeCachingOnceToken( - Node *node) { - mangleSingleChildNode(node); + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mz"; } @@ -2243,7 +2408,7 @@ std::string Demangle::mangleNodeOld(NodePointer node) { NodeFactory Factory; Remangler remangler(Factory); - remangler.mangle(node); + remangler.mangle(node, 0); return remangler.str(); } @@ -2252,7 +2417,7 @@ llvm::StringRef Demangle::mangleNodeOld(NodePointer node, NodeFactory &Factory) if (!node) return ""; Remangler remangler(Factory); - remangler.mangle(node); + remangler.mangle(node, 0); return remangler.getBufferStr(); } @@ -2263,7 +2428,7 @@ const char *Demangle::mangleNodeAsObjcCString(NodePointer node, Remangler remangler(Factory); remangler.append("_Tt"); - remangler.mangle(node); + remangler.mangle(node, 0); remangler.append(StringRef("_", 2)); // Include the trailing 0 char. return remangler.getBufferStr().data(); diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index e97ed4ce87b59..7c49e3841eaab 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -170,6 +170,8 @@ class Remangler : public RemanglerBase { static const size_t MaxNumWords = 26; + static const unsigned MaxDepth = 1024; + SubstitutionMerging SubstMerging; // A callback for resolving symbolic references. @@ -233,14 +235,14 @@ class Remangler : public RemanglerBase { Buffer << (value - 1) << '_'; } } - void mangleDependentConformanceIndex(Node *node); + void mangleDependentConformanceIndex(Node *node, unsigned depth); - void mangleChildNodes(Node *node) { - mangleNodes(node->begin(), node->end()); + void mangleChildNodes(Node *node, unsigned depth) { + mangleNodes(node->begin(), node->end(), depth); } - void mangleChildNodesReversed(Node *node) { + void mangleChildNodesReversed(Node *node, unsigned depth) { for (size_t Idx = 0, Num = node->getNumChildren(); Idx < Num; ++Idx) { - mangleChildNode(node, Num - Idx - 1); + mangleChildNode(node, Num - Idx - 1, depth); } } @@ -256,32 +258,32 @@ class Remangler : public RemanglerBase { Buffer << 'y'; } - void mangleNodes(Node::iterator i, Node::iterator e) { + void mangleNodes(Node::iterator i, Node::iterator e, unsigned depth) { for (; i != e; ++i) { - mangle(*i); + mangle(*i, depth); } } - void mangleSingleChildNode(Node *node) { + void mangleSingleChildNode(Node *node, unsigned depth) { assert(node->getNumChildren() == 1); - mangle(*node->begin()); + mangle(*node->begin(), depth); } - void mangleChildNode(Node *node, unsigned index) { + void mangleChildNode(Node *node, unsigned index, unsigned depth) { if (index < node->getNumChildren()) - mangle(node->begin()[index]); + mangle(node->begin()[index], depth); } - void manglePureProtocol(Node *Proto) { + void manglePureProtocol(Node *Proto, unsigned depth) { Proto = skipType(Proto); if (mangleStandardSubstitution(Proto)) return; - - mangleChildNodes(Proto); + + mangleChildNodes(Proto, depth); } void mangleProtocolList(Node *protocols, Node *superclass, - bool hasExplicitAnyObject); + bool hasExplicitAnyObject, unsigned depth); bool trySubstitution(Node *node, SubstitutionEntry &entry, bool treatAsIdentifier = false); @@ -294,39 +296,48 @@ class Remangler : public RemanglerBase { const char *nonZeroPrefix = "", char zeroOp = 'z'); - std::pair mangleConstrainedType(Node *node); + std::pair mangleConstrainedType(Node *node, unsigned depth); - void mangleFunctionSignature(Node *FuncType) { - mangleChildNodesReversed(FuncType); + void mangleFunctionSignature(Node *FuncType, unsigned depth) { + mangleChildNodesReversed(FuncType, depth); } - void mangleGenericSpecializationNode(Node *node, const char *operatorStr); - void mangleAnyNominalType(Node *node); - void mangleAnyGenericType(Node *node, StringRef TypeOp); - void mangleGenericArgs(Node *node, char &Separator, - bool fullSubstitutionMap=false); - void mangleAnyConstructor(Node *node, char kindOp); - void mangleAbstractStorage(Node *node, StringRef accessorCode); - void mangleAnyProtocolConformance(Node *node); + void mangleGenericSpecializationNode(Node *node, const char *operatorStr, + unsigned depth); + void mangleAnyNominalType(Node *node, unsigned depth); + void mangleAnyGenericType(Node *node, StringRef TypeOp, unsigned depth); + void mangleGenericArgs(Node *node, char &Separator, unsigned depth, + bool fullSubstitutionMap = false); + void mangleAnyConstructor(Node *node, char kindOp, unsigned depth); + void mangleAbstractStorage(Node *node, StringRef accessorCode, + unsigned depth); + void mangleAnyProtocolConformance(Node *node, unsigned depth); - void mangleKeyPathThunkHelper(Node *node, StringRef op); + void mangleKeyPathThunkHelper(Node *node, StringRef op, unsigned depth); - void mangleAutoDiffFunctionOrSimpleThunk(Node *node, StringRef op); + void mangleAutoDiffFunctionOrSimpleThunk(Node *node, StringRef op, + unsigned depth); -#define NODE(ID) \ - void mangle##ID(Node *node); -#define CONTEXT_NODE(ID) \ - void mangle##ID(Node *node); \ -// void mangle##ID(Node *node, EntityContext &ctx); +#define NODE(ID) void mangle##ID(Node *node, unsigned depth); +#define CONTEXT_NODE(ID) \ + void mangle##ID(Node *node, unsigned depth); \ +// void mangle##ID(Node *node, unsigned depth, EntityContext &ctx); #include "swift/Demangling/DemangleNodes.def" public: Remangler(SymbolicResolver Resolver, NodeFactory &Factory) : RemanglerBase(Factory), Resolver(Resolver) { } - void mangle(Node *node) { + void mangle(Node *node, unsigned depth) { + if (depth > Remangler::MaxDepth) { + // FIXME: error handling needs doing properly (rdar://79725187) + unreachable("too complex to remangle"); + } + switch (node->getKind()) { -#define NODE(ID) case Node::Kind::ID: return mangle##ID(node); +#define NODE(ID) \ + case Node::Kind::ID: \ + return mangle##ID(node, depth); #include "swift/Demangling/DemangleNodes.def" } unreachable("bad demangling tree node"); @@ -350,7 +361,8 @@ bool Remangler::trySubstitution(Node *node, SubstitutionEntry &entry, mangleIndex(Idx - 26); return true; } - char Subst = Idx + 'A'; + char SubstChar = Idx + 'A'; + StringRef Subst(&SubstChar, 1); if (!SubstMerging.tryMergeSubst(*this, Subst, /*isStandardSubst*/ false)) { Buffer << 'A' << Subst; } @@ -371,6 +383,7 @@ void Remangler::mangleIdentifierImpl(Node *node, bool isOperator) { bool Remangler::mangleStandardSubstitution(Node *node) { if (node->getKind() != Node::Kind::Structure + && node->getKind() != Node::Kind::Class && node->getKind() != Node::Kind::Enum && node->getKind() != Node::Kind::Protocol) return false; @@ -384,9 +397,9 @@ bool Remangler::mangleStandardSubstitution(Node *node) { if (node->getChild(1)->getKind() != Node::Kind::Identifier) return false; - if (char Subst = getStandardTypeSubst(node->getChild(1)->getText())) { - if (!SubstMerging.tryMergeSubst(*this, Subst, /*isStandardSubst*/ true)) { - Buffer << 'S' << Subst; + if (auto Subst = getStandardTypeSubst(node->getChild(1)->getText())) { + if (!SubstMerging.tryMergeSubst(*this, *Subst, /*isStandardSubst*/ true)) { + Buffer << 'S' << *Subst; } return true; } @@ -394,14 +407,14 @@ bool Remangler::mangleStandardSubstitution(Node *node) { } void Remangler::mangleDependentGenericParamIndex(Node *node, - const char *nonZeroPrefix, - char zeroOp) { - auto depth = node->getChild(0)->getIndex(); + const char *nonZeroPrefix, + char zeroOp) { + auto paramDepth = node->getChild(0)->getIndex(); auto index = node->getChild(1)->getIndex(); - if (depth != 0) { + if (paramDepth != 0) { Buffer << nonZeroPrefix << 'd'; - mangleIndex(depth - 1); + mangleIndex(paramDepth - 1); mangleIndex(index); return; } @@ -414,7 +427,8 @@ void Remangler::mangleDependentGenericParamIndex(Node *node, Buffer << zeroOp; } -std::pair Remangler::mangleConstrainedType(Node *node) { +std::pair Remangler::mangleConstrainedType(Node *node, + unsigned depth) { if (node->getKind() == Node::Kind::Type) node = getChildOfType(node); @@ -427,16 +441,18 @@ std::pair Remangler::mangleConstrainedType(Node *node) { Chain.push_back(node->getChild(1), Factory); node = getChildOfType(node->getFirstChild()); } - + if (node->getKind() != Node::Kind::DependentGenericParamType) { - mangle(node); + mangle(node, depth + 1); + if (!Chain.size()) + return {-1, nullptr}; node = nullptr; } const char *ListSeparator = (Chain.size() > 1 ? "_" : ""); for (unsigned i = 1, n = Chain.size(); i <= n; ++i) { Node *DepAssocTyRef = Chain[n - i]; - mangle(DepAssocTyRef); + mangle(DepAssocTyRef, depth + 1); Buffer << ListSeparator; ListSeparator = ""; } @@ -445,30 +461,36 @@ std::pair Remangler::mangleConstrainedType(Node *node) { return {(int)Chain.size(), node}; } -void Remangler::mangleAnyGenericType(Node *node, StringRef TypeOp) { +void Remangler::mangleAnyGenericType(Node *node, StringRef TypeOp, + unsigned depth) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); Buffer << TypeOp; addSubstitution(entry); } -void Remangler::mangleAnyNominalType(Node *node) { +void Remangler::mangleAnyNominalType(Node *node, unsigned depth) { + if (depth > Remangler::MaxDepth) { + // FIXME: error handling needs doing properly (rdar://79725187) + unreachable("too complex to remangle"); + } + if (isSpecialized(node)) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; NodePointer unboundType = getUnspecialized(node, Factory); - mangleAnyNominalType(unboundType); + mangleAnyNominalType(unboundType, depth + 1); char Separator = 'y'; - mangleGenericArgs(node, Separator); + mangleGenericArgs(node, Separator, depth + 1); if (node->getNumChildren() == 3) { // Retroactive conformances. auto listNode = node->getChild(2); for (size_t Idx = 0, Num = listNode->getNumChildren(); Idx < Num; ++Idx) { - mangle(listNode->getChild(Idx)); + mangle(listNode->getChild(Idx), depth + 1); } } @@ -477,17 +499,22 @@ void Remangler::mangleAnyNominalType(Node *node) { return; } switch (node->getKind()) { - case Node::Kind::Structure: return mangleAnyGenericType(node, "V"); - case Node::Kind::Enum: return mangleAnyGenericType(node, "O"); - case Node::Kind::Class: return mangleAnyGenericType(node, "C"); - case Node::Kind::OtherNominalType: return mangleAnyGenericType(node, "XY"); - case Node::Kind::TypeAlias: return mangleAnyGenericType(node, "a"); - default: - unreachable("bad nominal type kind"); + case Node::Kind::Structure: + return mangleAnyGenericType(node, "V", depth); + case Node::Kind::Enum: + return mangleAnyGenericType(node, "O", depth); + case Node::Kind::Class: + return mangleAnyGenericType(node, "C", depth); + case Node::Kind::OtherNominalType: + return mangleAnyGenericType(node, "XY", depth); + case Node::Kind::TypeAlias: + return mangleAnyGenericType(node, "a", depth); + default: + unreachable("bad nominal type kind"); } } -void Remangler::mangleGenericArgs(Node *node, char &Separator, +void Remangler::mangleGenericArgs(Node *node, char &Separator, unsigned depth, bool fullSubstitutionMap) { switch (node->getKind()) { case Node::Kind::Protocol: @@ -503,7 +530,7 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator, if (node->getKind() == Node::Kind::TypeAlias) fullSubstitutionMap = true; - mangleGenericArgs(node->getChild(0), Separator, + mangleGenericArgs(node->getChild(0), Separator, depth + 1, fullSubstitutionMap); Buffer << Separator; Separator = '_'; @@ -532,7 +559,7 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator, if (!fullSubstitutionMap) break; - mangleGenericArgs(node->getChild(0), Separator, + mangleGenericArgs(node->getChild(0), Separator, depth + 1, fullSubstitutionMap); if (Demangle::nodeConsumesGenericArgs(node)) { Buffer << Separator; @@ -553,11 +580,11 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator, assert(unboundType->getKind() == Node::Kind::Type); NodePointer nominalType = unboundType->getChild(0); NodePointer parentOrModule = nominalType->getChild(0); - mangleGenericArgs(parentOrModule, Separator, + mangleGenericArgs(parentOrModule, Separator, depth + 1, fullSubstitutionMap); Buffer << Separator; Separator = '_'; - mangleChildNodes(node->getChild(1)); + mangleChildNodes(node->getChild(1), depth + 1); break; } @@ -568,16 +595,16 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator, assert(unboundFunction->getKind() == Node::Kind::Function || unboundFunction->getKind() == Node::Kind::Constructor); NodePointer parentOrModule = unboundFunction->getChild(0); - mangleGenericArgs(parentOrModule, Separator, + mangleGenericArgs(parentOrModule, Separator, depth + 1, fullSubstitutionMap); Buffer << Separator; Separator = '_'; - mangleChildNodes(node->getChild(1)); + mangleChildNodes(node->getChild(1), depth + 1); break; } case Node::Kind::Extension: - mangleGenericArgs(node->getChild(1), Separator, + mangleGenericArgs(node->getChild(1), Separator, depth + 1, fullSubstitutionMap); break; @@ -586,8 +613,9 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator, } } -void Remangler::mangleAbstractStorage(Node *node, StringRef accessorCode) { - mangleChildNodes(node); +void Remangler::mangleAbstractStorage(Node *node, StringRef accessorCode, + unsigned depth) { + mangleChildNodes(node, depth + 1); switch (node->getKind()) { case Node::Kind::Subscript: Buffer << "i"; break; case Node::Kind::Variable: Buffer << "v"; break; @@ -596,97 +624,102 @@ void Remangler::mangleAbstractStorage(Node *node, StringRef accessorCode) { Buffer << accessorCode; } -void Remangler::mangleAllocator(Node *node) { - mangleAnyConstructor(node, 'C'); +void Remangler::mangleAllocator(Node *node, unsigned depth) { + mangleAnyConstructor(node, 'C', depth + 1); } -void Remangler::mangleArgumentTuple(Node *node) { +void Remangler::mangleArgumentTuple(Node *node, unsigned depth) { Node *Child = skipType(getSingleChild(node)); if (Child->getKind() == Node::Kind::Tuple && Child->getNumChildren() == 0) { Buffer << 'y'; return; } - mangle(Child); + mangle(Child, depth + 1); } -void Remangler::mangleAssociatedType(Node *node) { +void Remangler::mangleAssociatedType(Node *node, unsigned depth) { unreachable("unsupported node"); } -void Remangler::mangleAssociatedTypeRef(Node *node) { +void Remangler::mangleAssociatedTypeRef(Node *node, unsigned depth) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); Buffer << "Qa"; addSubstitution(entry); } -void Remangler::mangleAssociatedTypeDescriptor(Node *node) { - mangleChildNodes(node); +void Remangler::mangleAssociatedTypeDescriptor(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "Tl"; } -void Remangler::mangleAssociatedConformanceDescriptor(Node *node) { - mangle(node->getChild(0)); - mangle(node->getChild(1)); - manglePureProtocol(node->getChild(2)); +void Remangler::mangleAssociatedConformanceDescriptor(Node *node, + unsigned depth) { + mangle(node->getChild(0), depth + 1); + mangle(node->getChild(1), depth + 1); + manglePureProtocol(node->getChild(2), depth + 1); Buffer << "Tn"; } -void Remangler::mangleDefaultAssociatedConformanceAccessor(Node *node) { - mangle(node->getChild(0)); - mangle(node->getChild(1)); - manglePureProtocol(node->getChild(2)); +void Remangler::mangleDefaultAssociatedConformanceAccessor(Node *node, + unsigned depth) { + mangle(node->getChild(0), depth + 1); + mangle(node->getChild(1), depth + 1); + manglePureProtocol(node->getChild(2), depth + 1); Buffer << "TN"; } -void Remangler::mangleBaseConformanceDescriptor(Node *node) { - mangle(node->getChild(0)); - manglePureProtocol(node->getChild(1)); +void Remangler::mangleBaseConformanceDescriptor(Node *node, unsigned depth) { + mangle(node->getChild(0), depth + 1); + manglePureProtocol(node->getChild(1), depth + 1); Buffer << "Tb"; } -void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node) { - mangleChildNodes(node); // protocol conformance, identifier +void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); // protocol conformance, identifier Buffer << "Wt"; } -void Remangler::mangleDefaultAssociatedTypeMetadataAccessor(Node *node) { - mangleChildNodes(node); // protocol conformance, identifier +void Remangler::mangleDefaultAssociatedTypeMetadataAccessor(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); // protocol conformance, identifier Buffer << "TM"; } -void Remangler::mangleAssociatedTypeWitnessTableAccessor(Node *node) { - mangleChildNodes(node); // protocol conformance, type, protocol +void Remangler::mangleAssociatedTypeWitnessTableAccessor(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); // protocol conformance, type, protocol Buffer << "WT"; } -void Remangler::mangleBaseWitnessTableAccessor(Node *node) { - mangleChildNodes(node); // protocol conformance, protocol +void Remangler::mangleBaseWitnessTableAccessor(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); // protocol conformance, protocol Buffer << "Wb"; } -void Remangler::mangleAutoClosureType(Node *node) { - mangleChildNodesReversed(node); // argument tuple, result type +void Remangler::mangleAutoClosureType(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); // argument tuple, result type Buffer << "XK"; } -void Remangler::mangleEscapingAutoClosureType(Node *node) { - mangleChildNodesReversed(node); // argument tuple, result type +void Remangler::mangleEscapingAutoClosureType(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); // argument tuple, result type Buffer << "XA"; } -void Remangler::mangleNoEscapeFunctionType(Node *node) { - mangleChildNodesReversed(node); // argument tuple, result type +void Remangler::mangleNoEscapeFunctionType(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); // argument tuple, result type Buffer << "XE"; } -void Remangler::mangleBoundGenericClass(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleBoundGenericClass(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleBoundGenericEnum(Node *node) { +void Remangler::mangleBoundGenericEnum(Node *node, unsigned depth) { Node *Enum = node->getChild(0)->getChild(0); assert(Enum->getKind() == Node::Kind::Enum); Node *Mod = Enum->getChild(0); @@ -696,44 +729,44 @@ void Remangler::mangleBoundGenericEnum(Node *node) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; - mangleSingleChildNode(node->getChild(1)); + mangleSingleChildNode(node->getChild(1), depth + 1); Buffer << "Sg"; addSubstitution(entry); return; } - mangleAnyNominalType(node); + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleBoundGenericStructure(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleBoundGenericStructure(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleBoundGenericOtherNominalType(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleBoundGenericOtherNominalType(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleBoundGenericProtocol(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleBoundGenericProtocol(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleBoundGenericTypeAlias(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleBoundGenericTypeAlias(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleBoundGenericFunction(Node *node) { +void Remangler::mangleBoundGenericFunction(Node *node, unsigned depth) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; NodePointer unboundFunction = getUnspecialized(node, Factory); - mangleFunction(unboundFunction); + mangleFunction(unboundFunction, depth + 1); char Separator = 'y'; - mangleGenericArgs(node, Separator); + mangleGenericArgs(node, Separator, depth + 1); Buffer << 'G'; addSubstitution(entry); } -void Remangler::mangleBuiltinTypeName(Node *node) { +void Remangler::mangleBuiltinTypeName(Node *node, unsigned depth) { Buffer << 'B'; StringRef text = node->getText(); @@ -785,82 +818,87 @@ void Remangler::mangleBuiltinTypeName(Node *node) { } } -void Remangler::mangleCFunctionPointer(Node *node) { +void Remangler::mangleCFunctionPointer(Node *node, unsigned depth) { if (node->getNumChildren() > 0 && node->getFirstChild()->getKind() == Node::Kind::ClangType) { for (size_t Idx = node->getNumChildren() - 1; Idx >= 1; --Idx) { - mangleChildNode(node, Idx); + mangleChildNode(node, Idx, depth + 1); } Buffer << "XzC"; - mangleClangType(node->getFirstChild()); + mangleClangType(node->getFirstChild(), depth + 1); return; } - mangleChildNodesReversed(node); // argument tuple, result type + mangleChildNodesReversed(node, depth + 1); // argument tuple, result type Buffer << "XC"; } -void Remangler::mangleClass(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleClass(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleAnyConstructor(Node *node, char kindOp) { - mangleChildNodes(node); +void Remangler::mangleAnyConstructor(Node *node, char kindOp, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "f" << kindOp; } -void Remangler::mangleConstructor(Node *node) { - mangleAnyConstructor(node, 'c'); +void Remangler::mangleConstructor(Node *node, unsigned depth) { + mangleAnyConstructor(node, 'c', depth); } -void Remangler::mangleCoroutineContinuationPrototype(Node *node) { - mangleChildNodes(node); +void Remangler::mangleCoroutineContinuationPrototype(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "TC"; } -void Remangler::manglePredefinedObjCAsyncCompletionHandlerImpl(Node *node) { - mangleChildNodes(node); +void Remangler::manglePredefinedObjCAsyncCompletionHandlerImpl(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "TZ"; } -void Remangler::mangleObjCAsyncCompletionHandlerImpl(Node *node) { - mangleChildNode(node, 0); - mangleChildNode(node, 1); +void Remangler::mangleObjCAsyncCompletionHandlerImpl(Node *node, + unsigned depth) { + mangleChildNode(node, 0, depth + 1); + mangleChildNode(node, 1, depth + 1); if (node->getNumChildren() == 4) - mangleChildNode(node, 3); + mangleChildNode(node, 3, depth + 1); Buffer << "Tz"; - mangleChildNode(node, 2); + mangleChildNode(node, 2, depth + 1); } -void Remangler::mangleDeallocator(Node *node) { - mangleChildNodes(node); +void Remangler::mangleDeallocator(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "fD"; } -void Remangler::mangleDeclContext(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleDeclContext(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleDefaultArgumentInitializer(Node *node) { - mangleChildNode(node, 0); +void Remangler::mangleDefaultArgumentInitializer(Node *node, unsigned depth) { + mangleChildNode(node, 0, depth + 1); Buffer << "fA"; - mangleChildNode(node, 1); + mangleChildNode(node, 1, depth + 1); } -void Remangler::mangleAsyncFunctionPointer(Node *node) { +void Remangler::mangleAsyncFunctionPointer(Node *node, unsigned depth) { Buffer << "Tu"; } -void Remangler::mangleDependentAssociatedTypeRef(Node *node) { - mangleIdentifier(node->getFirstChild()); +void Remangler::mangleDependentAssociatedTypeRef(Node *node, unsigned depth) { + mangleIdentifier(node->getFirstChild(), depth); if (node->getNumChildren() > 1) - mangleChildNode(node, 1); + mangleChildNode(node, 1, depth + 1); } -void Remangler::mangleDependentGenericConformanceRequirement(Node *node) { +void Remangler::mangleDependentGenericConformanceRequirement(Node *node, + unsigned depth) { Node *ProtoOrClass = node->getChild(1); if (ProtoOrClass->getFirstChild()->getKind() == Node::Kind::Protocol) { - manglePureProtocol(ProtoOrClass); - auto NumMembersAndParamIdx = mangleConstrainedType(node->getChild(0)); + manglePureProtocol(ProtoOrClass, depth + 1); + auto NumMembersAndParamIdx = + mangleConstrainedType(node->getChild(0), depth + 1); assert(NumMembersAndParamIdx.first < 0 || NumMembersAndParamIdx.second); switch (NumMembersAndParamIdx.first) { case -1: Buffer << "RQ"; return; // substitution @@ -871,8 +909,9 @@ void Remangler::mangleDependentGenericConformanceRequirement(Node *node) { mangleDependentGenericParamIndex(NumMembersAndParamIdx.second); return; } - mangle(ProtoOrClass); - auto NumMembersAndParamIdx = mangleConstrainedType(node->getChild(0)); + mangle(ProtoOrClass, depth + 1); + auto NumMembersAndParamIdx = + mangleConstrainedType(node->getChild(0), depth + 1); assert(NumMembersAndParamIdx.first < 0 || NumMembersAndParamIdx.second); switch (NumMembersAndParamIdx.first) { case -1: Buffer << "RB"; return; // substitution @@ -884,11 +923,11 @@ void Remangler::mangleDependentGenericConformanceRequirement(Node *node) { return; } -void Remangler::mangleDependentGenericParamCount(Node *node) { +void Remangler::mangleDependentGenericParamCount(Node *node, unsigned depth) { unreachable("handled inline in DependentGenericSignature"); } -void Remangler::mangleDependentGenericParamType(Node *node) { +void Remangler::mangleDependentGenericParamType(Node *node, unsigned depth) { if (node->getChild(0)->getIndex() == 0 && node->getChild(1)->getIndex() == 0) { Buffer << 'x'; @@ -898,9 +937,11 @@ void Remangler::mangleDependentGenericParamType(Node *node) { mangleDependentGenericParamIndex(node); } -void Remangler::mangleDependentGenericSameTypeRequirement(Node *node) { - mangleChildNode(node, 1); - auto NumMembersAndParamIdx = mangleConstrainedType(node->getChild(0)); +void Remangler::mangleDependentGenericSameTypeRequirement(Node *node, + unsigned depth) { + mangleChildNode(node, 1, depth + 1); + auto NumMembersAndParamIdx = + mangleConstrainedType(node->getChild(0), depth + 1); assert(NumMembersAndParamIdx.first < 0 || NumMembersAndParamIdx.second); switch (NumMembersAndParamIdx.first) { case -1: Buffer << "RS"; return; // substitution @@ -911,8 +952,10 @@ void Remangler::mangleDependentGenericSameTypeRequirement(Node *node) { mangleDependentGenericParamIndex(NumMembersAndParamIdx.second); } -void Remangler::mangleDependentGenericLayoutRequirement(Node *node) { - auto NumMembersAndParamIdx = mangleConstrainedType(node->getChild(0)); +void Remangler::mangleDependentGenericLayoutRequirement(Node *node, + unsigned depth) { + auto NumMembersAndParamIdx = + mangleConstrainedType(node->getChild(0), depth + 1); assert(NumMembersAndParamIdx.first < 0 || NumMembersAndParamIdx.second); switch (NumMembersAndParamIdx.first) { case -1: Buffer << "RL"; break; // substitution @@ -927,12 +970,12 @@ void Remangler::mangleDependentGenericLayoutRequirement(Node *node) { assert(node->getChild(1)->getText().size() == 1); Buffer << node->getChild(1)->getText()[0]; if (node->getNumChildren() >=3) - mangleChildNode(node, 2); + mangleChildNode(node, 2, depth + 1); if (node->getNumChildren() >=4) - mangleChildNode(node, 3); + mangleChildNode(node, 3, depth + 1); } -void Remangler::mangleDependentGenericSignature(Node *node) { +void Remangler::mangleDependentGenericSignature(Node *node, unsigned depth) { size_t ParamCountEnd = 0; for (size_t Idx = 0, Num = node->getNumChildren(); Idx < Num; ++Idx) { Node *Child = node->getChild(Idx); @@ -940,7 +983,7 @@ void Remangler::mangleDependentGenericSignature(Node *node) { ParamCountEnd = Idx + 1; } else { // requirement - mangleChildNode(node, Idx); + mangleChildNode(node, Idx, depth + 1); } } // If there's only one generic param, mangle nothing. @@ -962,13 +1005,13 @@ void Remangler::mangleDependentGenericSignature(Node *node) { Buffer << 'l'; } -void Remangler::mangleDependentGenericType(Node *node) { - mangleChildNodesReversed(node); // type, generic signature +void Remangler::mangleDependentGenericType(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); // type, generic signature Buffer << 'u'; } -void Remangler::mangleDependentMemberType(Node *node) { - auto NumMembersAndParamIdx = mangleConstrainedType(node); +void Remangler::mangleDependentMemberType(Node *node, unsigned depth) { + auto NumMembersAndParamIdx = mangleConstrainedType(node, depth + 1); switch (NumMembersAndParamIdx.first) { case -1: break; // substitution @@ -993,20 +1036,21 @@ void Remangler::mangleDependentMemberType(Node *node) { } } -void Remangler::mangleDependentPseudogenericSignature(Node *node) { +void Remangler::mangleDependentPseudogenericSignature(Node *node, + unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleDestructor(Node *node) { - mangleChildNodes(node); +void Remangler::mangleDestructor(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "fd"; } -void Remangler::mangleDidSet(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "W"); +void Remangler::mangleDidSet(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "W", depth + 1); } -void Remangler::mangleDirectness(Node *node) { +void Remangler::mangleDirectness(Node *node, unsigned depth) { if (node->getIndex() == unsigned(Directness::Direct)) { Buffer << 'd'; } else { @@ -1015,98 +1059,100 @@ void Remangler::mangleDirectness(Node *node) { } } -void Remangler::mangleDynamicAttribute(Node *node) { +void Remangler::mangleDynamicAttribute(Node *node, unsigned depth) { Buffer << "TD"; } -void Remangler::mangleDirectMethodReferenceAttribute(Node *node) { +void Remangler::mangleDirectMethodReferenceAttribute(Node *node, + unsigned depth) { Buffer << "Td"; } -void Remangler::mangleDynamicSelf(Node *node) { - mangleSingleChildNode(node); // type +void Remangler::mangleDynamicSelf(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); // type Buffer << "XD"; } -void Remangler::mangleEnum(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleEnum(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleErrorType(Node *node) { - Buffer << "Xe"; -} +void Remangler::mangleErrorType(Node *node, unsigned depth) { Buffer << "Xe"; } -void Remangler::mangleExistentialMetatype(Node *node) { +void Remangler::mangleExistentialMetatype(Node *node, unsigned depth) { if (node->getFirstChild()->getKind() == Node::Kind::MetatypeRepresentation) { - mangleChildNode(node, 1); + mangleChildNode(node, 1, depth + 1); Buffer << "Xm"; - mangleChildNode(node, 0); + mangleChildNode(node, 0, depth + 1); } else { - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); Buffer << "Xp"; } } -void Remangler::mangleExplicitClosure(Node *node) { - mangleChildNode(node, 0); // context - mangleChildNode(node, 2); // type +void Remangler::mangleExplicitClosure(Node *node, unsigned depth) { + mangleChildNode(node, 0, depth + 1); // context + mangleChildNode(node, 2, depth + 1); // type Buffer << "fU"; - mangleChildNode(node, 1); // index + mangleChildNode(node, 1, depth + 1); // index } -void Remangler::mangleExtension(Node *node) { - mangleChildNode(node, 1); - mangleChildNode(node, 0); +void Remangler::mangleExtension(Node *node, unsigned depth) { + mangleChildNode(node, 1, depth + 1); + mangleChildNode(node, 0, depth + 1); if (node->getNumChildren() == 3) - mangleChildNode(node, 2); // generic signature + mangleChildNode(node, 2, depth + 1); // generic signature Buffer << 'E'; } -void Remangler::mangleAnonymousContext(Node *node) { - mangleChildNode(node, 1); - mangleChildNode(node, 0); +void Remangler::mangleAnonymousContext(Node *node, unsigned depth) { + mangleChildNode(node, 1, depth + 1); + mangleChildNode(node, 0, depth + 1); if (node->getNumChildren() >= 3) - mangleTypeList(node->getChild(2)); + mangleTypeList(node->getChild(2), depth + 1); + else + Buffer << 'y'; Buffer << "XZ"; } -void Remangler::mangleFieldOffset(Node *node) { - mangleChildNode(node, 1); // variable +void Remangler::mangleFieldOffset(Node *node, unsigned depth) { + mangleChildNode(node, 1, depth + 1); // variable Buffer << "Wv"; - mangleChildNode(node, 0); // directness + mangleChildNode(node, 0, depth + 1); // directness } -void Remangler::mangleEnumCase(Node *node) { - mangleSingleChildNode(node); // enum case +void Remangler::mangleEnumCase(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); // enum case Buffer << "WC"; } -void Remangler::mangleFullTypeMetadata(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleFullTypeMetadata(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mf"; } -void Remangler::mangleFunction(Node *node) { - mangleChildNode(node, 0); // context - mangleChildNode(node, 1); // name +void Remangler::mangleFunction(Node *node, unsigned depth) { + mangleChildNode(node, 0, depth + 1); // context + mangleChildNode(node, 1, depth + 1); // name bool hasLabels = node->getChild(2)->getKind() == Node::Kind::LabelList; Node *FuncType = getSingleChild(node->getChild(hasLabels ? 3 : 2)); if (hasLabels) - mangleChildNode(node, 2); // parameter labels + mangleChildNode(node, 2, depth + 1); // parameter labels if (FuncType->getKind() == Node::Kind::DependentGenericType) { - mangleFunctionSignature(getSingleChild(FuncType->getChild(1))); - mangleChildNode(FuncType, 0); // generic signature + mangleFunctionSignature(getSingleChild(FuncType->getChild(1)), depth + 1); + mangleChildNode(FuncType, 0, depth + 1); // generic signature } else { - mangleFunctionSignature(FuncType); + mangleFunctionSignature(FuncType, depth + 1); } Buffer << "F"; } -void Remangler::mangleFunctionSignatureSpecialization(Node *node) { +void Remangler::mangleFunctionSignatureSpecialization(Node *node, + unsigned depth) { for (NodePointer Param : *node) { if (Param->getKind() == Node::Kind::FunctionSignatureSpecializationParam && Param->getNumChildren() > 0) { @@ -1114,7 +1160,7 @@ void Remangler::mangleFunctionSignatureSpecialization(Node *node) { switch (FunctionSigSpecializationParamKind(KindNd->getIndex())) { case FunctionSigSpecializationParamKind::ConstantPropFunction: case FunctionSigSpecializationParamKind::ConstantPropGlobal: - mangleIdentifier(Param->getChild(1)); + mangleIdentifier(Param->getChild(1), depth + 1); break; case FunctionSigSpecializationParamKind::ConstantPropString: { NodePointer TextNd = Param->getChild(2); @@ -1124,13 +1170,13 @@ void Remangler::mangleFunctionSignatureSpecialization(Node *node) { Buffer.append(Text.data(), Text.size()); TextNd = Factory.createNode(Node::Kind::Identifier, Buffer); } - mangleIdentifier(TextNd); + mangleIdentifier(TextNd, depth + 1); break; } case FunctionSigSpecializationParamKind::ClosureProp: - mangleIdentifier(Param->getChild(1)); + mangleIdentifier(Param->getChild(1), depth + 1); for (unsigned i = 2, e = Param->getNumChildren(); i != e; ++i) { - mangleType(Param->getChild(i)); + mangleType(Param->getChild(i), depth + 1); } break; default: @@ -1145,7 +1191,7 @@ void Remangler::mangleFunctionSignatureSpecialization(Node *node) { Buffer << '_'; returnValMangled = true; } - mangle(Child); + mangle(Child, depth + 1); if (Child->getKind() == Node::Kind::SpecializationPassID && node->hasIndex()) { @@ -1156,11 +1202,13 @@ void Remangler::mangleFunctionSignatureSpecialization(Node *node) { Buffer << "_n"; } -void Remangler::mangleFunctionSignatureSpecializationReturn(Node *node) { - mangleFunctionSignatureSpecializationParam(node); +void Remangler::mangleFunctionSignatureSpecializationReturn(Node *node, + unsigned depth) { + mangleFunctionSignatureSpecializationParam(node, depth + 1); } -void Remangler::mangleFunctionSignatureSpecializationParam(Node *node) { +void Remangler::mangleFunctionSignatureSpecializationParam(Node *node, + unsigned depth) { if (!node->hasChildren()) { Buffer << 'n'; return; @@ -1247,38 +1295,42 @@ void Remangler::mangleFunctionSignatureSpecializationParam(Node *node) { } } -void Remangler::mangleFunctionSignatureSpecializationParamKind(Node *node) { +void Remangler::mangleFunctionSignatureSpecializationParamKind(Node *node, + unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleFunctionSignatureSpecializationParamPayload(Node *node) { +void Remangler::mangleFunctionSignatureSpecializationParamPayload( + Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleFunctionType(Node *node) { - mangleFunctionSignature(node); +void Remangler::mangleFunctionType(Node *node, unsigned depth) { + mangleFunctionSignature(node, depth + 1); Buffer << 'c'; } -void Remangler::mangleGenericProtocolWitnessTable(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleGenericProtocolWitnessTable(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "WG"; } -void Remangler::mangleGenericProtocolWitnessTableInstantiationFunction(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleGenericProtocolWitnessTableInstantiationFunction( + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "WI"; } -void Remangler::mangleResilientProtocolWitnessTable(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleResilientProtocolWitnessTable(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Wr"; } -void Remangler::mangleGenericPartialSpecialization(Node *node) { +void Remangler::mangleGenericPartialSpecialization(Node *node, unsigned depth) { for (NodePointer Child : *node) { if (Child->getKind() == Node::Kind::GenericSpecializationParam) { - mangleChildNode(Child, 0); + mangleChildNode(Child, 0, depth + 1); break; } } @@ -1286,20 +1338,22 @@ void Remangler::mangleGenericPartialSpecialization(Node *node) { Node::Kind::GenericPartialSpecializationNotReAbstracted ? "TP" : "Tp"); for (NodePointer Child : *node) { if (Child->getKind() != Node::Kind::GenericSpecializationParam) - mangle(Child); + mangle(Child, depth + 1); } } -void Remangler::mangleGenericPartialSpecializationNotReAbstracted(Node *node) { - mangleGenericPartialSpecialization(node); +void Remangler::mangleGenericPartialSpecializationNotReAbstracted( + Node *node, unsigned depth) { + mangleGenericPartialSpecialization(node, depth + 1); } -void Remangler:: -mangleGenericSpecializationNode(Node *node, const char *operatorStr) { +void Remangler::mangleGenericSpecializationNode(Node *node, + const char *operatorStr, + unsigned depth) { bool FirstParam = true; for (NodePointer Child : *node) { if (Child->getKind() == Node::Kind::GenericSpecializationParam) { - mangleChildNode(Child, 0); + mangleChildNode(Child, 0, depth + 1); mangleListSeparator(FirstParam); } } @@ -1309,50 +1363,52 @@ mangleGenericSpecializationNode(Node *node, const char *operatorStr) { for (NodePointer Child : *node) { if (Child->getKind() != Node::Kind::GenericSpecializationParam) - mangle(Child); + mangle(Child, depth + 1); } } -void Remangler::mangleGenericSpecialization(Node *node) { - mangleGenericSpecializationNode(node, "Tg"); +void Remangler::mangleGenericSpecialization(Node *node, unsigned depth) { + mangleGenericSpecializationNode(node, "Tg", depth + 1); } -void Remangler::mangleGenericSpecializationPrespecialized(Node *node) { - mangleGenericSpecializationNode(node, "Ts"); +void Remangler::mangleGenericSpecializationPrespecialized(Node *node, + unsigned depth) { + mangleGenericSpecializationNode(node, "Ts", depth + 1); } -void Remangler::mangleGenericSpecializationNotReAbstracted(Node *node) { - mangleGenericSpecializationNode(node, "TG"); +void Remangler::mangleGenericSpecializationNotReAbstracted(Node *node, + unsigned depth) { + mangleGenericSpecializationNode(node, "TG", depth + 1); } -void Remangler::mangleGenericSpecializationInResilienceDomain(Node *node) { - mangleGenericSpecializationNode(node, "TB"); +void Remangler::mangleGenericSpecializationInResilienceDomain(Node *node, + unsigned depth) { + mangleGenericSpecializationNode(node, "TB", depth + 1); } -void Remangler::mangleInlinedGenericFunction(Node *node) { - mangleGenericSpecializationNode(node, "Ti"); +void Remangler::mangleInlinedGenericFunction(Node *node, unsigned depth) { + mangleGenericSpecializationNode(node, "Ti", depth + 1); } - -void Remangler::mangleGenericSpecializationParam(Node *node) { +void Remangler::mangleGenericSpecializationParam(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleGenericTypeMetadataPattern(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleGenericTypeMetadataPattern(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MP"; } -void Remangler::mangleGenericTypeParamDecl(Node *node) { - mangleChildNodes(node); +void Remangler::mangleGenericTypeParamDecl(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "fp"; } -void Remangler::mangleGetter(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "g"); +void Remangler::mangleGetter(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "g", depth + 1); } -void Remangler::mangleGlobal(Node *node) { +void Remangler::mangleGlobal(Node *node, unsigned depth) { Buffer << MANGLING_PREFIX_STR; bool mangleInReverseOrder = false; for (auto Iter = node->begin(), End = node->end(); Iter != End; ++Iter) { @@ -1383,12 +1439,12 @@ void Remangler::mangleGlobal(Node *node) { mangleInReverseOrder = true; break; default: - mangle(Child); + mangle(Child, depth + 1); if (mangleInReverseOrder) { auto ReverseIter = Iter; while (ReverseIter != node->begin()) { --ReverseIter; - mangle(*ReverseIter); + mangle(*ReverseIter, depth + 1); } mangleInReverseOrder = false; } @@ -1397,41 +1453,41 @@ void Remangler::mangleGlobal(Node *node) { } } -void Remangler::mangleGlobalGetter(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "G"); +void Remangler::mangleGlobalGetter(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "G", depth + 1); } -void Remangler::mangleIdentifier(Node *node) { +void Remangler::mangleIdentifier(Node *node, unsigned depth) { mangleIdentifierImpl(node, /*isOperator*/ false); } -void Remangler::mangleIndex(Node *node) { +void Remangler::mangleIndex(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleUnknownIndex(Node *node) { +void Remangler::mangleUnknownIndex(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleIVarInitializer(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleIVarInitializer(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "fe"; } -void Remangler::mangleIVarDestroyer(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleIVarDestroyer(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "fE"; } -void Remangler::mangleImplDifferentiabilityKind(Node *node) { +void Remangler::mangleImplDifferentiabilityKind(Node *node, unsigned depth) { Buffer << (char)node->getIndex(); } -void Remangler::mangleImplEscaping(Node *node) { +void Remangler::mangleImplEscaping(Node *node, unsigned depth) { Buffer << 'e'; } -void Remangler::mangleImplConvention(Node *node) { +void Remangler::mangleImplConvention(Node *node, unsigned depth) { char ConvCh = llvm::StringSwitch(node->getText()) .Case("@callee_unowned", 'y') .Case("@callee_guaranteed", 'g') @@ -1441,7 +1497,8 @@ void Remangler::mangleImplConvention(Node *node) { Buffer << ConvCh; } -void Remangler::mangleImplParameterResultDifferentiability(Node *node) { +void Remangler::mangleImplParameterResultDifferentiability(Node *node, + unsigned depth) { assert(node->hasText()); // Empty string represents default differentiability. if (node->getText().empty()) @@ -1453,11 +1510,11 @@ void Remangler::mangleImplParameterResultDifferentiability(Node *node) { Buffer << diffChar; } -void Remangler::mangleImplFunctionAttribute(Node *node) { +void Remangler::mangleImplFunctionAttribute(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleImplFunctionConvention(Node *node) { +void Remangler::mangleImplFunctionConvention(Node *node, unsigned depth) { StringRef text = (node->getNumChildren() > 0 && node->getFirstChild()->hasText()) ? node->getFirstChild()->getText() @@ -1474,29 +1531,29 @@ void Remangler::mangleImplFunctionConvention(Node *node) { if ((FuncAttr == 'B' || FuncAttr == 'C') && node->getNumChildren() > 1 && node->getChild(1)->getKind() == Node::Kind::ClangType) { Buffer << 'z' << FuncAttr; - mangleClangType(node->getChild(1)); + mangleClangType(node->getChild(1), depth + 1); return; } Buffer << FuncAttr; } -void Remangler::mangleImplFunctionConventionName(Node *node) { +void Remangler::mangleImplFunctionConventionName(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleClangType(Node *node) { +void Remangler::mangleClangType(Node *node, unsigned depth) { Buffer << node->getText().size() << node->getText(); } -void Remangler::mangleImplInvocationSubstitutions(Node *node) { +void Remangler::mangleImplInvocationSubstitutions(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleImplPatternSubstitutions(Node *node) { +void Remangler::mangleImplPatternSubstitutions(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleImplFunctionType(Node *node) { +void Remangler::mangleImplFunctionType(Node *node, unsigned depth) { const char *PseudoGeneric = ""; Node *GenSig = nullptr; Node *PatternSubs = nullptr; @@ -1509,7 +1566,7 @@ void Remangler::mangleImplFunctionType(Node *node) { case Node::Kind::ImplErrorResult: // Mangle type. Type should be the last child. assert(Child->getNumChildren() == 2 || Child->getNumChildren() == 3); - mangle(Child->getLastChild()); + mangle(Child->getLastChild(), depth + 1); break; case Node::Kind::DependentPseudogenericSignature: PseudoGeneric = "P"; @@ -1528,23 +1585,23 @@ void Remangler::mangleImplFunctionType(Node *node) { } } if (GenSig) - mangle(GenSig); + mangle(GenSig, depth + 1); if (InvocationSubs) { Buffer << 'y'; - mangleChildNodes(InvocationSubs->getChild(0)); + mangleChildNodes(InvocationSubs->getChild(0), depth + 1); if (InvocationSubs->getNumChildren() >= 2) - mangleRetroactiveConformance(InvocationSubs->getChild(1)); + mangleRetroactiveConformance(InvocationSubs->getChild(1), depth + 1); } if (PatternSubs) { - mangle(PatternSubs->getChild(0)); + mangle(PatternSubs->getChild(0), depth + 1); Buffer << 'y'; - mangleChildNodes(PatternSubs->getChild(1)); + mangleChildNodes(PatternSubs->getChild(1), depth + 1); if (PatternSubs->getNumChildren() >= 3) { NodePointer retroactiveConf = PatternSubs->getChild(2); if (retroactiveConf->getKind() == Node::Kind::TypeList) { - mangleChildNodes(retroactiveConf); + mangleChildNodes(retroactiveConf, depth + 1); } else { - mangleRetroactiveConformance(retroactiveConf); + mangleRetroactiveConformance(retroactiveConf, depth + 1); } } } @@ -1577,7 +1634,7 @@ void Remangler::mangleImplFunctionType(Node *node) { break; } case Node::Kind::ImplFunctionConvention: { - mangleImplFunctionConvention(Child); + mangleImplFunctionConvention(Child, depth + 1); break; } case Node::Kind::ImplFunctionAttribute: { @@ -1612,7 +1669,8 @@ void Remangler::mangleImplFunctionType(Node *node) { Buffer << ConvCh; // Mangle parameter differentiability, if it exists. if (Child->getNumChildren() == 3) - mangleImplParameterResultDifferentiability(Child->getChild(1)); + mangleImplParameterResultDifferentiability(Child->getChild(1), + depth + 1); break; } case Node::Kind::ImplErrorResult: @@ -1630,7 +1688,8 @@ void Remangler::mangleImplFunctionType(Node *node) { Buffer << ConvCh; // Mangle result differentiability, if it exists. if (Child->getNumChildren() == 3) - mangleImplParameterResultDifferentiability(Child->getChild(1)); + mangleImplParameterResultDifferentiability(Child->getChild(1), + depth + 1); break; } default: @@ -1640,101 +1699,110 @@ void Remangler::mangleImplFunctionType(Node *node) { Buffer << '_'; } -void Remangler::mangleImplicitClosure(Node *node) { - mangleChildNode(node, 0); // context - mangleChildNode(node, 2); // type +void Remangler::mangleImplicitClosure(Node *node, unsigned depth) { + mangleChildNode(node, 0, depth + 1); // context + mangleChildNode(node, 2, depth + 1); // type Buffer << "fu"; - mangleChildNode(node, 1); // index + mangleChildNode(node, 1, depth + 1); // index } -void Remangler::mangleImplParameter(Node *node) { +void Remangler::mangleImplParameter(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleImplResult(Node *node) { +void Remangler::mangleImplResult(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleImplYield(Node *node) { +void Remangler::mangleImplYield(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleImplErrorResult(Node *node) { +void Remangler::mangleImplErrorResult(Node *node, unsigned depth) { unreachable("handled inline"); } -void Remangler::mangleInOut(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleInOut(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << 'z'; } -void Remangler::mangleShared(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleIsolated(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); + Buffer << "Yi"; +} + +void Remangler::mangleShared(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << 'h'; } -void Remangler::mangleOwned(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleOwned(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << 'n'; } -void Remangler::mangleNoDerivative(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleNoDerivative(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Yk"; } -void Remangler::mangleInfixOperator(Node *node) { +void Remangler::mangleInfixOperator(Node *node, unsigned depth) { mangleIdentifierImpl(node, /*isOperator*/ true); Buffer << "oi"; } -void Remangler::mangleInitializer(Node *node) { - mangleChildNodes(node); +void Remangler::mangleInitializer(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "fi"; } -void Remangler::manglePropertyWrapperBackingInitializer(Node *node) { - mangleChildNodes(node); +void Remangler::manglePropertyWrapperBackingInitializer(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "fP"; } -void Remangler::manglePropertyWrapperInitFromProjectedValue(Node *node) { - mangleChildNodes(node); +void Remangler::manglePropertyWrapperInitFromProjectedValue(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "fW"; } -void Remangler::mangleLazyProtocolWitnessTableAccessor(Node *node) { - mangleChildNodes(node); +void Remangler::mangleLazyProtocolWitnessTableAccessor(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "Wl"; } -void Remangler::mangleLazyProtocolWitnessTableCacheVariable(Node *node) { - mangleChildNodes(node); +void Remangler::mangleLazyProtocolWitnessTableCacheVariable(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WL"; } -void Remangler::mangleLocalDeclName(Node *node) { - mangleChildNode(node, 1); // identifier +void Remangler::mangleLocalDeclName(Node *node, unsigned depth) { + mangleChildNode(node, 1, depth + 1); // identifier Buffer << 'L'; - mangleChildNode(node, 0); // index + mangleChildNode(node, 0, depth + 1); // index } -void Remangler::mangleMaterializeForSet(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "m"); +void Remangler::mangleMaterializeForSet(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "m", depth + 1); } -void Remangler::mangleMetatype(Node *node) { +void Remangler::mangleMetatype(Node *node, unsigned depth) { if (node->getFirstChild()->getKind() == Node::Kind::MetatypeRepresentation) { - mangleChildNode(node, 1); + mangleChildNode(node, 1, depth + 1); Buffer << "XM"; - mangleChildNode(node, 0); + mangleChildNode(node, 0, depth + 1); } else { - mangleSingleChildNode(node); + mangleSingleChildNode(node, depth + 1); Buffer << 'm'; } } -void Remangler::mangleMetatypeRepresentation(Node *node) { +void Remangler::mangleMetatypeRepresentation(Node *node, unsigned depth) { if (node->getText() == "@thin") { Buffer << 't'; } else if (node->getText() == "@thick") { @@ -1746,16 +1814,16 @@ void Remangler::mangleMetatypeRepresentation(Node *node) { } } -void Remangler::mangleMetaclass(Node *node) { - mangleChildNodes(node); +void Remangler::mangleMetaclass(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "Mm"; } -void Remangler::mangleModifyAccessor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "M"); +void Remangler::mangleModifyAccessor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "M", depth + 1); } -void Remangler::mangleModule(Node *node) { +void Remangler::mangleModule(Node *node, unsigned depth) { if (node->getText() == STDLIB_NAME) { Buffer << 's'; } else if (node->getText() == MANGLING_MODULE_OBJC) { @@ -1763,300 +1831,319 @@ void Remangler::mangleModule(Node *node) { } else if (node->getText() == MANGLING_MODULE_CLANG_IMPORTER) { Buffer << "SC"; } else { - mangleIdentifier(node); + mangleIdentifier(node, depth); } } -void Remangler::mangleNativeOwningAddressor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "lo"); +void Remangler::mangleNativeOwningAddressor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "lo", depth + 1); } -void Remangler::mangleNativeOwningMutableAddressor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "ao"); +void Remangler::mangleNativeOwningMutableAddressor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "ao", depth + 1); } -void Remangler::mangleNativePinningAddressor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "lp"); +void Remangler::mangleNativePinningAddressor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "lp", depth + 1); } -void Remangler::mangleNativePinningMutableAddressor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "aP"); +void Remangler::mangleNativePinningMutableAddressor(Node *node, + unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "aP", depth + 1); } -void Remangler::mangleClassMetadataBaseOffset(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleClassMetadataBaseOffset(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mo"; } -void Remangler::mangleNominalTypeDescriptor(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleNominalTypeDescriptor(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mn"; } -void Remangler::mangleOpaqueTypeDescriptor(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleOpaqueTypeDescriptor(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MQ"; } -void Remangler::mangleOpaqueTypeDescriptorAccessor(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleOpaqueTypeDescriptorAccessor(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mg"; } -void Remangler::mangleOpaqueTypeDescriptorAccessorImpl(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleOpaqueTypeDescriptorAccessorImpl(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mh"; } -void Remangler::mangleOpaqueTypeDescriptorAccessorKey(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleOpaqueTypeDescriptorAccessorKey(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mj"; } -void Remangler::mangleOpaqueTypeDescriptorAccessorVar(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleOpaqueTypeDescriptorAccessorVar(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mk"; } -void Remangler::manglePropertyDescriptor(Node *node) { - mangleSingleChildNode(node); +void Remangler::manglePropertyDescriptor(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MV"; } -void Remangler::mangleNonObjCAttribute(Node *node) { +void Remangler::mangleNonObjCAttribute(Node *node, unsigned depth) { Buffer << "TO"; } -void Remangler::mangleTuple(Node *node) { - mangleTypeList(node); +void Remangler::mangleTuple(Node *node, unsigned depth) { + mangleTypeList(node, depth + 1); Buffer << 't'; } -void Remangler::mangleNumber(Node *node) { +void Remangler::mangleNumber(Node *node, unsigned depth) { mangleIndex(node->getIndex()); } -void Remangler::mangleObjCAttribute(Node *node) { +void Remangler::mangleObjCAttribute(Node *node, unsigned depth) { Buffer << "To"; } -void Remangler::mangleObjCBlock(Node *node) { +void Remangler::mangleObjCBlock(Node *node, unsigned depth) { if (node->getNumChildren() > 0 && node->getFirstChild()->getKind() == Node::Kind::ClangType) { for (size_t Idx = node->getNumChildren() - 1; Idx >= 1; --Idx) { - mangleChildNode(node, Idx); + mangleChildNode(node, Idx, depth + 1); } Buffer << "XzB"; - mangleClangType(node->getFirstChild()); + mangleClangType(node->getFirstChild(), depth + 1); return; } - mangleChildNodesReversed(node); + mangleChildNodesReversed(node, depth + 1); Buffer << "XB"; } -void Remangler::mangleEscapingObjCBlock(Node *node) { - mangleChildNodesReversed(node); +void Remangler::mangleEscapingObjCBlock(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); Buffer << "XL"; } -void Remangler::mangleOwningAddressor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "lO"); +void Remangler::mangleOwningAddressor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "lO", depth + 1); } -void Remangler::mangleOwningMutableAddressor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "aO"); +void Remangler::mangleOwningMutableAddressor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "aO", depth + 1); } -void Remangler::manglePartialApplyForwarder(Node *node) { - mangleChildNodesReversed(node); +void Remangler::manglePartialApplyForwarder(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); Buffer << "TA"; } -void Remangler::manglePartialApplyObjCForwarder(Node *node) { - mangleChildNodesReversed(node); +void Remangler::manglePartialApplyObjCForwarder(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); Buffer << "Ta"; } -void Remangler::mangleMergedFunction(Node *node) { +void Remangler::mangleMergedFunction(Node *node, unsigned depth) { Buffer << "Tm"; } -void Remangler::mangleDynamicallyReplaceableFunctionImpl(Node *node) { +void Remangler::mangleDynamicallyReplaceableFunctionImpl(Node *node, + unsigned depth) { Buffer << "TI"; } -void Remangler::mangleDynamicallyReplaceableFunctionKey(Node *node) { +void Remangler::mangleDynamicallyReplaceableFunctionKey(Node *node, + unsigned depth) { Buffer << "Tx"; } -void Remangler::mangleDynamicallyReplaceableFunctionVar(Node *node) { +void Remangler::mangleDynamicallyReplaceableFunctionVar(Node *node, + unsigned depth) { Buffer << "TX"; } -void Remangler::mangleAsyncAwaitResumePartialFunction(Node *node) { +void Remangler::mangleAsyncAwaitResumePartialFunction(Node *node, + unsigned depth) { Buffer << "TQ"; - mangleChildNode(node, 0); + mangleChildNode(node, 0, depth + 1); } -void Remangler::mangleAsyncSuspendResumePartialFunction(Node *node) { +void Remangler::mangleAsyncSuspendResumePartialFunction(Node *node, + unsigned depth) { Buffer << "TY"; - mangleChildNode(node, 0); + mangleChildNode(node, 0, depth + 1); } -void Remangler::manglePostfixOperator(Node *node) { +void Remangler::manglePostfixOperator(Node *node, unsigned depth) { mangleIdentifierImpl(node, /*isOperator*/ true); Buffer << "oP"; } -void Remangler::manglePrefixOperator(Node *node) { +void Remangler::manglePrefixOperator(Node *node, unsigned depth) { mangleIdentifierImpl(node, /*isOperator*/ true); Buffer << "op"; } -void Remangler::manglePrivateDeclName(Node *node) { - mangleChildNodesReversed(node); +void Remangler::manglePrivateDeclName(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); Buffer << (node->getNumChildren() == 1 ? "Ll" : "LL"); } -void Remangler::mangleProtocol(Node *node) { - mangleAnyGenericType(node, "P"); +void Remangler::mangleProtocol(Node *node, unsigned depth) { + mangleAnyGenericType(node, "P", depth + 1); } -void Remangler::mangleRetroactiveConformance(Node *node) { - mangleAnyProtocolConformance(node->getChild(1)); +void Remangler::mangleRetroactiveConformance(Node *node, unsigned depth) { + mangleAnyProtocolConformance(node->getChild(1), depth + 1); Buffer << 'g'; mangleIndex(node->getChild(0)->getIndex()); } -void Remangler::mangleProtocolConformance(Node *node) { +void Remangler::mangleProtocolConformance(Node *node, unsigned depth) { Node *Ty = getChildOfType(node->getChild(0)); Node *GenSig = nullptr; if (Ty->getKind() == Node::Kind::DependentGenericType) { GenSig = Ty->getFirstChild(); Ty = Ty->getChild(1); } - mangle(Ty); + mangle(Ty, depth + 1); if (node->getNumChildren() == 4) - mangleChildNode(node, 3); - manglePureProtocol(node->getChild(1)); - mangleChildNode(node, 2); + mangleChildNode(node, 3, depth + 1); + manglePureProtocol(node->getChild(1), depth + 1); + mangleChildNode(node, 2, depth + 1); if (GenSig) - mangle(GenSig); + mangle(GenSig, depth + 1); } -void Remangler::mangleProtocolConformanceRefInTypeModule(Node *node) { - manglePureProtocol(node->getChild(0)); +void Remangler::mangleProtocolConformanceRefInTypeModule(Node *node, + unsigned depth) { + manglePureProtocol(node->getChild(0), depth + 1); Buffer << "HP"; } -void Remangler::mangleProtocolConformanceRefInProtocolModule(Node *node) { - manglePureProtocol(node->getChild(0)); +void Remangler::mangleProtocolConformanceRefInProtocolModule(Node *node, + unsigned depth) { + manglePureProtocol(node->getChild(0), depth + 1); Buffer << "Hp"; } -void Remangler::mangleProtocolConformanceRefInOtherModule(Node *node) { - manglePureProtocol(node->getChild(0)); - mangleChildNode(node, 1); +void Remangler::mangleProtocolConformanceRefInOtherModule(Node *node, + unsigned depth) { + manglePureProtocol(node->getChild(0), depth + 1); + mangleChildNode(node, 1, depth + 1); } -void Remangler::mangleConcreteProtocolConformance(Node *node) { - mangleType(node->getChild(0)); - mangle(node->getChild(1)); +void Remangler::mangleConcreteProtocolConformance(Node *node, unsigned depth) { + mangleType(node->getChild(0), depth + 1); + mangle(node->getChild(1), depth + 1); if (node->getNumChildren() > 2) - mangleAnyProtocolConformanceList(node->getChild(2)); + mangleAnyProtocolConformanceList(node->getChild(2), depth + 1); else Buffer << "y"; Buffer << "HC"; } -void Remangler::mangleDependentProtocolConformanceRoot(Node *node) { - mangleType(node->getChild(0)); - manglePureProtocol(node->getChild(1)); +void Remangler::mangleDependentProtocolConformanceRoot(Node *node, + unsigned depth) { + mangleType(node->getChild(0), depth + 1); + manglePureProtocol(node->getChild(1), depth + 1); Buffer << "HD"; - mangleDependentConformanceIndex(node->getChild(2)); + mangleDependentConformanceIndex(node->getChild(2), depth + 1); } -void Remangler::mangleDependentProtocolConformanceInherited(Node *node) { - mangleAnyProtocolConformance(node->getChild(0)); - manglePureProtocol(node->getChild(1)); +void Remangler::mangleDependentProtocolConformanceInherited(Node *node, + unsigned depth) { + mangleAnyProtocolConformance(node->getChild(0), depth + 1); + manglePureProtocol(node->getChild(1), depth + 1); Buffer << "HI"; - mangleDependentConformanceIndex(node->getChild(2)); + mangleDependentConformanceIndex(node->getChild(2), depth + 1); } -void Remangler::mangleDependentAssociatedConformance(Node *node) { - mangleType(node->getChild(0)); - manglePureProtocol(node->getChild(1)); +void Remangler::mangleDependentAssociatedConformance(Node *node, + unsigned depth) { + mangleType(node->getChild(0), depth + 1); + manglePureProtocol(node->getChild(1), depth + 1); } -void Remangler::mangleDependentProtocolConformanceAssociated(Node *node) { - mangleAnyProtocolConformance(node->getChild(0)); - mangleDependentAssociatedConformance(node->getChild(1)); +void Remangler::mangleDependentProtocolConformanceAssociated(Node *node, + unsigned depth) { + mangleAnyProtocolConformance(node->getChild(0), depth + 1); + mangleDependentAssociatedConformance(node->getChild(1), depth + 1); Buffer << "HA"; - mangleDependentConformanceIndex(node->getChild(2)); + mangleDependentConformanceIndex(node->getChild(2), depth + 1); } -void Remangler::mangleDependentConformanceIndex(Node *node) { +void Remangler::mangleDependentConformanceIndex(Node *node, unsigned depth) { assert(node->getKind() == Node::Kind::Index || node->getKind() == Node::Kind::UnknownIndex); assert(node->hasIndex() == (node->getKind() == Node::Kind::Index)); mangleIndex(node->hasIndex() ? node->getIndex() + 2 : 1); } -void Remangler::mangleAnyProtocolConformance(Node *node) { +void Remangler::mangleAnyProtocolConformance(Node *node, unsigned depth) { switch (node->getKind()) { case Node::Kind::ConcreteProtocolConformance: - return mangleConcreteProtocolConformance(node); + return mangleConcreteProtocolConformance(node, depth + 1); case Node::Kind::DependentProtocolConformanceRoot: - return mangleDependentProtocolConformanceRoot(node); + return mangleDependentProtocolConformanceRoot(node, depth + 1); case Node::Kind::DependentProtocolConformanceInherited: - return mangleDependentProtocolConformanceInherited(node); + return mangleDependentProtocolConformanceInherited(node, depth + 1); case Node::Kind::DependentProtocolConformanceAssociated: - return mangleDependentProtocolConformanceAssociated(node); + return mangleDependentProtocolConformanceAssociated(node, depth + 1); default: break; } } -void Remangler::mangleAnyProtocolConformanceList(Node *node) { +void Remangler::mangleAnyProtocolConformanceList(Node *node, unsigned depth) { bool firstElem = true; for (NodePointer child : *node) { - mangleAnyProtocolConformance(child); + mangleAnyProtocolConformance(child, depth + 1); mangleListSeparator(firstElem); } mangleEndOfList(firstElem); } -void Remangler::mangleProtocolDescriptor(Node *node) { - manglePureProtocol(getSingleChild(node)); +void Remangler::mangleProtocolDescriptor(Node *node, unsigned depth) { + manglePureProtocol(getSingleChild(node), depth + 1); Buffer << "Mp"; } -void Remangler::mangleProtocolRequirementsBaseDescriptor(Node *node) { - manglePureProtocol(getSingleChild(node)); +void Remangler::mangleProtocolRequirementsBaseDescriptor(Node *node, + unsigned depth) { + manglePureProtocol(getSingleChild(node), depth + 1); Buffer << "TL"; } -void Remangler::mangleProtocolSelfConformanceDescriptor(Node *node) { - manglePureProtocol(node->getChild(0)); +void Remangler::mangleProtocolSelfConformanceDescriptor(Node *node, + unsigned depth) { + manglePureProtocol(node->getChild(0), depth + 1); Buffer << "MS"; } -void Remangler::mangleProtocolConformanceDescriptor(Node *node) { - mangleProtocolConformance(node->getChild(0)); +void Remangler::mangleProtocolConformanceDescriptor(Node *node, + unsigned depth) { + mangleProtocolConformance(node->getChild(0), depth + 1); Buffer << "Mc"; } void Remangler::mangleProtocolList(Node *node, Node *superclass, - bool hasExplicitAnyObject) { + bool hasExplicitAnyObject, unsigned depth) { auto *protocols = getSingleChild(node, Node::Kind::TypeList); bool FirstElem = true; for (NodePointer Child : *protocols) { - manglePureProtocol(Child); + manglePureProtocol(Child, depth + 1); mangleListSeparator(FirstElem); } mangleEndOfList(FirstElem); if (superclass) { - mangleType(superclass); + mangleType(superclass, depth + 1); Buffer << "Xc"; return; } else if (hasExplicitAnyObject) { @@ -2066,313 +2153,329 @@ void Remangler::mangleProtocolList(Node *node, Node *superclass, Buffer << 'p'; } -void Remangler::mangleProtocolList(Node *node) { - mangleProtocolList(node, nullptr, false); +void Remangler::mangleProtocolList(Node *node, unsigned depth) { + mangleProtocolList(node, nullptr, false, depth + 1); } -void Remangler::mangleProtocolListWithClass(Node *node) { - mangleProtocolList(node->getChild(0), - node->getChild(1), - false); +void Remangler::mangleProtocolListWithClass(Node *node, unsigned depth) { + mangleProtocolList(node->getChild(0), node->getChild(1), false, depth + 1); } -void Remangler::mangleProtocolListWithAnyObject(Node *node) { - mangleProtocolList(node->getChild(0), nullptr, true); +void Remangler::mangleProtocolListWithAnyObject(Node *node, unsigned depth) { + mangleProtocolList(node->getChild(0), nullptr, true, depth + 1); } -void Remangler::mangleProtocolSelfConformanceWitness(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleProtocolSelfConformanceWitness(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "TS"; } -void Remangler::mangleProtocolWitness(Node *node) { - mangleChildNodes(node); +void Remangler::mangleProtocolWitness(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "TW"; } -void Remangler::mangleProtocolSelfConformanceWitnessTable(Node *node) { - manglePureProtocol(node->getChild(0)); +void Remangler::mangleProtocolSelfConformanceWitnessTable(Node *node, + unsigned depth) { + manglePureProtocol(node->getChild(0), depth + 1); Buffer << "WS"; } -void Remangler::mangleProtocolWitnessTable(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleProtocolWitnessTable(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "WP"; } -void Remangler::mangleProtocolWitnessTablePattern(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleProtocolWitnessTablePattern(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Wp"; } -void Remangler::mangleProtocolWitnessTableAccessor(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleProtocolWitnessTableAccessor(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Wa"; } -void Remangler::mangleReabstractionThunk(Node *node) { - mangleChildNodesReversed(node); +void Remangler::mangleReabstractionThunk(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); Buffer << "Tr"; } -void Remangler::mangleReabstractionThunkHelper(Node *node) { - mangleChildNodesReversed(node); +void Remangler::mangleReabstractionThunkHelper(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); Buffer << "TR"; } -void Remangler::mangleReabstractionThunkHelperWithSelf(Node *node) { - mangleChildNodesReversed(node); +void Remangler::mangleReabstractionThunkHelperWithSelf(Node *node, + unsigned depth) { + mangleChildNodesReversed(node, depth + 1); Buffer << "Ty"; } -void Remangler::mangleAutoDiffFunctionOrSimpleThunk(Node *node, StringRef op) { +void Remangler::mangleReabstractionThunkHelperWithGlobalActor(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); + Buffer << "TU"; +} + +void Remangler::mangleAutoDiffFunctionOrSimpleThunk(Node *node, StringRef op, + unsigned depth) { auto childIt = node->begin(); while (childIt != node->end() && (*childIt)->getKind() != Node::Kind::AutoDiffFunctionKind) - mangle(*childIt++); + mangle(*childIt++, depth + 1); Buffer << op; - mangle(*childIt++); // kind - mangle(*childIt++); // parameter indices + mangle(*childIt++, depth + 1); // kind + mangle(*childIt++, depth + 1); // parameter indices Buffer << 'p'; - mangle(*childIt++); // result indices + mangle(*childIt++, depth + 1); // result indices Buffer << 'r'; } -void Remangler::mangleAutoDiffFunction(Node *node) { - mangleAutoDiffFunctionOrSimpleThunk(node, "TJ"); +void Remangler::mangleAutoDiffFunction(Node *node, unsigned depth) { + mangleAutoDiffFunctionOrSimpleThunk(node, "TJ", depth + 1); } -void Remangler::mangleAutoDiffDerivativeVTableThunk(Node *node) { - mangleAutoDiffFunctionOrSimpleThunk(node, "TJV"); +void Remangler::mangleAutoDiffDerivativeVTableThunk(Node *node, + unsigned depth) { + mangleAutoDiffFunctionOrSimpleThunk(node, "TJV", depth + 1); } -void Remangler::mangleAutoDiffSelfReorderingReabstractionThunk(Node *node) { +void Remangler::mangleAutoDiffSelfReorderingReabstractionThunk(Node *node, + unsigned depth) { auto childIt = node->begin(); - mangle(*childIt++); // from type - mangle(*childIt++); // to type + mangle(*childIt++, depth + 1); // from type + mangle(*childIt++, depth + 1); // to type if ((*childIt)->getKind() == Node::Kind::DependentGenericSignature) - mangleDependentGenericSignature(*childIt++); + mangleDependentGenericSignature(*childIt++, depth + 1); Buffer << "TJO"; - mangle(*childIt++); // kind + mangle(*childIt++, depth + 1); // kind } -void Remangler::mangleAutoDiffSubsetParametersThunk(Node *node) { +void Remangler::mangleAutoDiffSubsetParametersThunk(Node *node, + unsigned depth) { auto childIt = node->begin(); while (childIt != node->end() && (*childIt)->getKind() != Node::Kind::AutoDiffFunctionKind) - mangle(*childIt++); + mangle(*childIt++, depth + 1); Buffer << "TJS"; - mangle(*childIt++); // kind - mangle(*childIt++); // parameter indices + mangle(*childIt++, depth + 1); // kind + mangle(*childIt++, depth + 1); // parameter indices Buffer << 'p'; - mangle(*childIt++); // result indices + mangle(*childIt++, depth + 1); // result indices Buffer << 'r'; - mangle(*childIt++); // to parameter indices + mangle(*childIt++, depth + 1); // to parameter indices Buffer << 'P'; } -void Remangler::mangleAutoDiffFunctionKind(Node *node) { +void Remangler::mangleAutoDiffFunctionKind(Node *node, unsigned depth) { Buffer << (char)node->getIndex(); } -void Remangler::mangleDifferentiabilityWitness(Node *node) { +void Remangler::mangleDifferentiabilityWitness(Node *node, unsigned depth) { auto childIt = node->begin(); while (childIt != node->end() && (*childIt)->getKind() != Node::Kind::Index) - mangle(*childIt++); + mangle(*childIt++, depth + 1); if (node->getLastChild()->getKind() == Node::Kind::DependentGenericSignature) - mangle(node->getLastChild()); + mangle(node->getLastChild(), depth + 1); Buffer << "WJ" << (char)(*childIt++)->getIndex(); - mangle(*childIt++); // parameter indices + mangle(*childIt++, depth + 1); // parameter indices Buffer << 'p'; - mangle(*childIt++); // result indices + mangle(*childIt++, depth + 1); // result indices Buffer << 'r'; } -void Remangler::mangleIndexSubset(Node *node) { +void Remangler::mangleIndexSubset(Node *node, unsigned depth) { Buffer << node->getText(); } -void Remangler::mangleReadAccessor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "r"); +void Remangler::mangleReadAccessor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "r", depth + 1); } -void Remangler::mangleKeyPathThunkHelper(Node *node, StringRef op) { +void Remangler::mangleKeyPathThunkHelper(Node *node, StringRef op, + unsigned depth) { for (NodePointer Child : *node) if (Child->getKind() != Node::Kind::IsSerialized) - mangle(Child); + mangle(Child, depth + 1); Buffer << op; for (NodePointer Child : *node) if (Child->getKind() == Node::Kind::IsSerialized) - mangle(Child); + mangle(Child, depth + 1); } -void Remangler::mangleKeyPathGetterThunkHelper(Node *node) { - mangleKeyPathThunkHelper(node, "TK"); +void Remangler::mangleKeyPathGetterThunkHelper(Node *node, unsigned depth) { + mangleKeyPathThunkHelper(node, "TK", depth + 1); } -void Remangler::mangleKeyPathSetterThunkHelper(Node *node) { - mangleKeyPathThunkHelper(node, "Tk"); +void Remangler::mangleKeyPathSetterThunkHelper(Node *node, unsigned depth) { + mangleKeyPathThunkHelper(node, "Tk", depth + 1); } -void Remangler::mangleKeyPathEqualsThunkHelper(Node *node) { - mangleKeyPathThunkHelper(node, "TH"); +void Remangler::mangleKeyPathEqualsThunkHelper(Node *node, unsigned depth) { + mangleKeyPathThunkHelper(node, "TH", depth + 1); } -void Remangler::mangleKeyPathHashThunkHelper(Node *node) { - mangleKeyPathThunkHelper(node, "Th"); +void Remangler::mangleKeyPathHashThunkHelper(Node *node, unsigned depth) { + mangleKeyPathThunkHelper(node, "Th", depth + 1); } -void Remangler::mangleReturnType(Node *node) { - mangleArgumentTuple(node); +void Remangler::mangleReturnType(Node *node, unsigned depth) { + mangleArgumentTuple(node, depth + 1); } -void Remangler::mangleRelatedEntityDeclName(Node *node) { - mangleChildNode(node, 1); +void Remangler::mangleRelatedEntityDeclName(Node *node, unsigned depth) { + mangleChildNode(node, 1, depth + 1); NodePointer kindNode = node->getFirstChild(); if (kindNode->getText().size() != 1) unreachable("cannot handle multi-byte related entities"); Buffer << "L" << kindNode->getText(); } -void Remangler::mangleSILBoxType(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleSILBoxType(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Xb"; } -void Remangler::mangleSetter(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "s"); +void Remangler::mangleSetter(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "s", depth + 1); } -void Remangler::mangleSpecializationPassID(Node *node) { +void Remangler::mangleSpecializationPassID(Node *node, unsigned depth) { Buffer << node->getIndex(); } -void Remangler::mangleIsSerialized(Node *node) { +void Remangler::mangleIsSerialized(Node *node, unsigned depth) { Buffer << 'q'; } -void Remangler::mangleStatic(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleStatic(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << 'Z'; } -void Remangler::mangleOtherNominalType(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleOtherNominalType(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleStructure(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleStructure(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleSubscript(Node *node) { - mangleAbstractStorage(node, "p"); +void Remangler::mangleSubscript(Node *node, unsigned depth) { + mangleAbstractStorage(node, "p", depth + 1); } -void Remangler::mangleSuffix(Node *node) { +void Remangler::mangleSuffix(Node *node, unsigned depth) { // Just add the suffix back on. Buffer << node->getText(); } -void Remangler::mangleThinFunctionType(Node *node) { - mangleFunctionSignature(node); +void Remangler::mangleThinFunctionType(Node *node, unsigned depth) { + mangleFunctionSignature(node, depth + 1); Buffer << "Xf"; } -void Remangler::mangleTupleElement(Node *node) { - mangleChildNodesReversed(node); // tuple type, element name? +void Remangler::mangleTupleElement(Node *node, unsigned depth) { + mangleChildNodesReversed(node, depth + 1); // tuple type, element name? } -void Remangler::mangleTupleElementName(Node *node) { - mangleIdentifier(node); +void Remangler::mangleTupleElementName(Node *node, unsigned depth) { + mangleIdentifier(node, depth + 1); } -void Remangler::mangleType(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleType(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); } -void Remangler::mangleTypeAlias(Node *node) { - mangleAnyNominalType(node); +void Remangler::mangleTypeAlias(Node *node, unsigned depth) { + mangleAnyNominalType(node, depth + 1); } -void Remangler::mangleTypeList(Node *node) { +void Remangler::mangleTypeList(Node *node, unsigned depth) { bool FirstElem = true; for (size_t Idx = 0, Num = node->getNumChildren(); Idx < Num; ++Idx) { - mangleChildNode(node, Idx); + mangleChildNode(node, Idx, depth + 1); mangleListSeparator(FirstElem); } mangleEndOfList(FirstElem); } -void Remangler::mangleLabelList(Node *node) { +void Remangler::mangleLabelList(Node *node, unsigned depth) { if (node->getNumChildren() == 0) Buffer << 'y'; else - mangleChildNodes(node); + mangleChildNodes(node, depth + 1); } -void Remangler::mangleTypeMangling(Node *node) { - mangleChildNodes(node); +void Remangler::mangleTypeMangling(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << 'D'; } -void Remangler::mangleTypeMetadata(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleTypeMetadata(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "N"; } -void Remangler::mangleTypeMetadataAccessFunction(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleTypeMetadataAccessFunction(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Ma"; } -void Remangler::mangleTypeMetadataInstantiationCache(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleTypeMetadataInstantiationCache(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MI"; } -void Remangler::mangleTypeMetadataInstantiationFunction(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleTypeMetadataInstantiationFunction(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mi"; } -void Remangler::mangleTypeMetadataSingletonInitializationCache(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleTypeMetadataSingletonInitializationCache(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Ml"; } -void Remangler::mangleTypeMetadataCompletionFunction(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleTypeMetadataCompletionFunction(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mr"; } -void Remangler::mangleTypeMetadataDemanglingCache(Node *node) { - mangleChildNodes(node); +void Remangler::mangleTypeMetadataDemanglingCache(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "MD"; } -void Remangler::mangleTypeMetadataLazyCache(Node *node) { - mangleChildNodes(node); +void Remangler::mangleTypeMetadataLazyCache(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "ML"; } -void Remangler::mangleUncurriedFunctionType(Node *node) { - mangleFunctionSignature(node); +void Remangler::mangleUncurriedFunctionType(Node *node, unsigned depth) { + mangleFunctionSignature(node, depth + 1); // Mangle as regular function type (there is no "uncurried function type" // in the new mangling scheme). Buffer << 'c'; } -void Remangler::mangleUnsafeAddressor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "lu"); +void Remangler::mangleUnsafeAddressor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "lu", depth + 1); } -void Remangler::mangleUnsafeMutableAddressor(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "au"); +void Remangler::mangleUnsafeMutableAddressor(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "au", depth + 1); } -void Remangler::mangleValueWitness(Node *node) { - mangleChildNode(node, 1); // type +void Remangler::mangleValueWitness(Node *node, unsigned depth) { + mangleChildNode(node, 1, depth + 1); // type const char *Code = nullptr; switch (ValueWitnessKind(node->getFirstChild()->getIndex())) { #define VALUE_WITNESS(MANGLING, NAME) \ @@ -2382,175 +2485,182 @@ void Remangler::mangleValueWitness(Node *node) { Buffer << 'w' << Code; } -void Remangler::mangleValueWitnessTable(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleValueWitnessTable(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "WV"; } -void Remangler::mangleVariable(Node *node) { - mangleAbstractStorage(node, "p"); +void Remangler::mangleVariable(Node *node, unsigned depth) { + mangleAbstractStorage(node, "p", depth + 1); } -void Remangler::mangleVTableAttribute(Node *node) { +void Remangler::mangleVTableAttribute(Node *node, unsigned depth) { unreachable("Old-fashioned vtable thunk in new mangling format"); } -void Remangler::mangleVTableThunk(Node *node) { - mangleChildNodes(node); +void Remangler::mangleVTableThunk(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "TV"; } -#define REF_STORAGE(Name, ...) \ - void Remangler::mangle##Name(Node *node) { \ - mangleSingleChildNode(node); \ - Buffer << manglingOf(ReferenceOwnership::Name); \ +#define REF_STORAGE(Name, ...) \ + void Remangler::mangle##Name(Node *node, unsigned depth) { \ + mangleSingleChildNode(node, depth + 1); \ + Buffer << manglingOf(ReferenceOwnership::Name); \ } #include "swift/AST/ReferenceStorage.def" -void Remangler::mangleWillSet(Node *node) { - mangleAbstractStorage(node->getFirstChild(), "w"); +void Remangler::mangleWillSet(Node *node, unsigned depth) { + mangleAbstractStorage(node->getFirstChild(), "w", depth + 1); } -void Remangler::mangleReflectionMetadataBuiltinDescriptor(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleReflectionMetadataBuiltinDescriptor(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MB"; } -void Remangler::mangleReflectionMetadataFieldDescriptor(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleReflectionMetadataFieldDescriptor(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MF"; } -void Remangler::mangleReflectionMetadataAssocTypeDescriptor(Node *node) { - mangleSingleChildNode(node); // protocol-conformance +void Remangler::mangleReflectionMetadataAssocTypeDescriptor(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); // protocol-conformance Buffer << "MA"; } -void Remangler::mangleReflectionMetadataSuperclassDescriptor(Node *node) { - mangleSingleChildNode(node); // protocol-conformance +void Remangler::mangleReflectionMetadataSuperclassDescriptor(Node *node, + unsigned depth) { + mangleSingleChildNode(node, depth + 1); // protocol-conformance Buffer << "MC"; } -void Remangler::mangleCurryThunk(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleCurryThunk(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Tc"; } -void Remangler::mangleDispatchThunk(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleDispatchThunk(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Tj"; } -void Remangler::mangleMethodDescriptor(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleMethodDescriptor(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Tq"; } -void Remangler::mangleMethodLookupFunction(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleMethodLookupFunction(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mu"; } -void Remangler::mangleObjCMetadataUpdateFunction(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleObjCMetadataUpdateFunction(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MU"; } -void Remangler::mangleObjCResilientClassStub(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleObjCResilientClassStub(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Ms"; } -void Remangler::mangleFullObjCResilientClassStub(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleFullObjCResilientClassStub(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mt"; } -void Remangler::mangleConcurrentFunctionType(Node *node) { +void Remangler::mangleConcurrentFunctionType(Node *node, unsigned depth) { Buffer << "Yb"; } -void Remangler::mangleAsyncAnnotation(Node *node) { +void Remangler::mangleAsyncAnnotation(Node *node, unsigned depth) { Buffer << "Ya"; } -void Remangler::mangleDifferentiableFunctionType(Node *node) { +void Remangler::mangleDifferentiableFunctionType(Node *node, unsigned depth) { Buffer << "Yj" << (char)node->getIndex(); // differentiability kind } -void Remangler::mangleThrowsAnnotation(Node *node) { - Buffer << 'K'; +void Remangler::mangleGlobalActorFunctionType(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); + Buffer << "Yc"; } -void Remangler::mangleEmptyList(Node *node) { - Buffer << 'y'; +void Remangler::mangleThrowsAnnotation(Node *node, unsigned depth) { + Buffer << 'K'; } -void Remangler::mangleFirstElementMarker(Node *node) { +void Remangler::mangleEmptyList(Node *node, unsigned depth) { Buffer << 'y'; } + +void Remangler::mangleFirstElementMarker(Node *node, unsigned depth) { Buffer << '_'; } -void Remangler::mangleVariadicMarker(Node *node) { +void Remangler::mangleVariadicMarker(Node *node, unsigned depth) { Buffer << 'd'; } -void Remangler::mangleOutlinedCopy(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedCopy(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOy"; } -void Remangler::mangleOutlinedConsume(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedConsume(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOe"; } -void Remangler::mangleOutlinedRetain(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedRetain(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOr"; } -void Remangler::mangleOutlinedRelease(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedRelease(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOs"; } -void Remangler::mangleOutlinedInitializeWithTake(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedInitializeWithTake(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOb"; } -void Remangler::mangleOutlinedInitializeWithCopy(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedInitializeWithCopy(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOc"; } -void Remangler::mangleOutlinedAssignWithTake(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedAssignWithTake(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOd"; } -void Remangler::mangleOutlinedAssignWithCopy(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedAssignWithCopy(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOf"; } -void Remangler::mangleOutlinedDestroy(Node *node) { - mangleChildNodes(node); +void Remangler::mangleOutlinedDestroy(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WOh"; } -void Remangler::mangleOutlinedVariable(Node *node) { +void Remangler::mangleOutlinedVariable(Node *node, unsigned depth) { Buffer << "Tv"; mangleIndex(node->getIndex()); } -void Remangler::mangleOutlinedBridgedMethod(Node *node) { +void Remangler::mangleOutlinedBridgedMethod(Node *node, unsigned depth) { Buffer << "Te"; Buffer << node->getText(); Buffer << "_"; } -void Remangler::mangleSILBoxTypeWithLayout(Node *node) { +void Remangler::mangleSILBoxTypeWithLayout(Node *node, unsigned depth) { assert(node->getNumChildren() == 1 || node->getNumChildren() == 3); assert(node->getChild(0)->getKind() == Node::Kind::SILBoxLayout); auto layout = node->getChild(0); @@ -2571,124 +2681,129 @@ void Remangler::mangleSILBoxTypeWithLayout(Node *node) { } layoutTypeList->addChild(fieldType, Factory); } - mangleTypeList(layoutTypeList); - + mangleTypeList(layoutTypeList, depth + 1); + if (node->getNumChildren() == 3) { auto signature = node->getChild(1); auto genericArgs = node->getChild(2); assert(signature->getKind() == Node::Kind::DependentGenericSignature); assert(genericArgs->getKind() == Node::Kind::TypeList); - mangleTypeList(genericArgs); - mangleDependentGenericSignature(signature); + mangleTypeList(genericArgs, depth + 1); + mangleDependentGenericSignature(signature, depth + 1); Buffer << "XX"; } else { Buffer << "Xx"; } } -void Remangler::mangleSILBoxLayout(Node *node) { +void Remangler::mangleSILBoxLayout(Node *node, unsigned depth) { unreachable("should be part of SILBoxTypeWithLayout"); } -void Remangler::mangleSILBoxMutableField(Node *node) { +void Remangler::mangleSILBoxMutableField(Node *node, unsigned depth) { unreachable("should be part of SILBoxTypeWithLayout"); } -void Remangler::mangleSILBoxImmutableField(Node *node) { +void Remangler::mangleSILBoxImmutableField(Node *node, unsigned depth) { unreachable("should be part of SILBoxTypeWithLayout"); } -void Remangler::mangleAssocTypePath(Node *node) { +void Remangler::mangleAssocTypePath(Node *node, unsigned depth) { bool FirstElem = true; for (NodePointer Child : *node) { - mangle(Child); + mangle(Child, depth + 1); mangleListSeparator(FirstElem); } } - -void Remangler::mangleModuleDescriptor(Node *node) { - mangle(node->getChild(0)); + +void Remangler::mangleModuleDescriptor(Node *node, unsigned depth) { + mangle(node->getChild(0), depth + 1); Buffer << "MXM"; } -void Remangler::mangleExtensionDescriptor(Node *node) { - mangle(node->getChild(0)); +void Remangler::mangleExtensionDescriptor(Node *node, unsigned depth) { + mangle(node->getChild(0), depth + 1); Buffer << "MXE"; } -void Remangler::mangleAnonymousDescriptor(Node *node) { - mangle(node->getChild(0)); +void Remangler::mangleAnonymousDescriptor(Node *node, unsigned depth) { + mangle(node->getChild(0), depth + 1); if (node->getNumChildren() == 1) { Buffer << "MXX"; } else { - mangleIdentifier(node->getChild(1)); + mangleIdentifier(node->getChild(1), depth + 1); Buffer << "MXY"; } } - -void Remangler::mangleAssociatedTypeGenericParamRef(Node *node) { - mangleType(node->getChild(0)); - mangleAssocTypePath(node->getChild(1)); + +void Remangler::mangleAssociatedTypeGenericParamRef(Node *node, + unsigned depth) { + mangleType(node->getChild(0), depth + 1); + mangleAssocTypePath(node->getChild(1), depth + 1); Buffer << "MXA"; } - -void Remangler::mangleTypeSymbolicReference(Node *node) { - return mangle(Resolver(SymbolicReferenceKind::Context, - (const void *)node->getIndex())); + +void Remangler::mangleTypeSymbolicReference(Node *node, unsigned depth) { + return mangle( + Resolver(SymbolicReferenceKind::Context, (const void *)node->getIndex()), + depth + 1); } -void Remangler::mangleProtocolSymbolicReference(Node *node) { - return mangle(Resolver(SymbolicReferenceKind::Context, - (const void *)node->getIndex())); +void Remangler::mangleProtocolSymbolicReference(Node *node, unsigned depth) { + return mangle( + Resolver(SymbolicReferenceKind::Context, (const void *)node->getIndex()), + depth + 1); } -void Remangler::mangleOpaqueTypeDescriptorSymbolicReference(Node *node) { - return mangle(Resolver(SymbolicReferenceKind::Context, - (const void *)node->getIndex())); +void Remangler::mangleOpaqueTypeDescriptorSymbolicReference(Node *node, + unsigned depth) { + return mangle( + Resolver(SymbolicReferenceKind::Context, (const void *)node->getIndex()), + depth + 1); } -void Remangler::mangleSugaredOptional(Node *node) { - mangleType(node->getChild(0)); +void Remangler::mangleSugaredOptional(Node *node, unsigned depth) { + mangleType(node->getChild(0), depth + 1); Buffer << "XSq"; } -void Remangler::mangleSugaredArray(Node *node) { - mangleType(node->getChild(0)); +void Remangler::mangleSugaredArray(Node *node, unsigned depth) { + mangleType(node->getChild(0), depth + 1); Buffer << "XSa"; } -void Remangler::mangleSugaredDictionary(Node *node) { - mangleType(node->getChild(0)); - mangleType(node->getChild(1)); +void Remangler::mangleSugaredDictionary(Node *node, unsigned depth) { + mangleType(node->getChild(0), depth + 1); + mangleType(node->getChild(1), depth + 1); Buffer << "XSD"; } -void Remangler::mangleSugaredParen(Node *node) { - mangleType(node->getChild(0)); +void Remangler::mangleSugaredParen(Node *node, unsigned depth) { + mangleType(node->getChild(0), depth + 1); Buffer << "XSp"; } -void Remangler::mangleOpaqueReturnType(Node *node) { +void Remangler::mangleOpaqueReturnType(Node *node, unsigned depth) { Buffer << "Qr"; } -void Remangler::mangleOpaqueReturnTypeOf(Node *node) { - mangle(node->getChild(0)); +void Remangler::mangleOpaqueReturnTypeOf(Node *node, unsigned depth) { + mangle(node->getChild(0), depth + 1); Buffer << "QO"; } -void Remangler::mangleOpaqueType(Node *node) { +void Remangler::mangleOpaqueType(Node *node, unsigned depth) { SubstitutionEntry entry; if (trySubstitution(node, entry)) return; - mangle(node->getChild(0)); + mangle(node->getChild(0), depth + 1); auto boundGenerics = node->getChild(2); for (unsigned i = 0; i < boundGenerics->getNumChildren(); ++i) { Buffer << (i == 0 ? 'y' : '_'); - mangleChildNodes(boundGenerics->getChild(i)); + mangleChildNodes(boundGenerics->getChild(i), depth + 1); } if (node->getNumChildren() >= 4) { auto retroactiveConformances = node->getChild(3); for (unsigned i = 0; i < retroactiveConformances->getNumChildren(); ++i) { - mangle(retroactiveConformances->getChild(i)); + mangle(retroactiveConformances->getChild(i), depth + 1); } } Buffer << "Qo"; @@ -2696,55 +2811,58 @@ void Remangler::mangleOpaqueType(Node *node) { addSubstitution(entry); } -void Remangler::mangleAccessorFunctionReference(Node *node) { +void Remangler::mangleAccessorFunctionReference(Node *node, unsigned depth) { unreachable("can't remangle"); } -void Remangler::mangleCanonicalSpecializedGenericMetaclass(Node *node) { - mangleChildNodes(node); +void Remangler::mangleCanonicalSpecializedGenericMetaclass(Node *node, + unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "MM"; } void Remangler::mangleCanonicalSpecializedGenericTypeMetadataAccessFunction( - Node *node) { - mangleSingleChildNode(node); + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mb"; } -void Remangler::mangleMetadataInstantiationCache(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleMetadataInstantiationCache(Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MK"; } -void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadata(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadata( + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MN"; } -void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadataCache(Node *node) { - mangleSingleChildNode(node); +void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadataCache( + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "MJ"; } void Remangler::mangleCanonicalPrespecializedGenericTypeCachingOnceToken( - Node *node) { - mangleSingleChildNode(node); + Node *node, unsigned depth) { + mangleSingleChildNode(node, depth + 1); Buffer << "Mz"; } -void Remangler::mangleGlobalVariableOnceToken(Node *node) { - mangleChildNodes(node); +void Remangler::mangleGlobalVariableOnceToken(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "Wz"; } -void Remangler::mangleGlobalVariableOnceFunction(Node *node) { - mangleChildNodes(node); +void Remangler::mangleGlobalVariableOnceFunction(Node *node, unsigned depth) { + mangleChildNodes(node, depth + 1); Buffer << "WZ"; } -void Remangler::mangleGlobalVariableOnceDeclList(Node *node) { +void Remangler::mangleGlobalVariableOnceDeclList(Node *node, unsigned depth) { for (unsigned i = 0, e = node->getNumChildren(); i < e; ++i) { - mangle(node->getChild(i)); + mangle(node->getChild(i), depth + 1); Buffer << '_'; } } @@ -2765,7 +2883,7 @@ Demangle::mangleNode(NodePointer node, SymbolicResolver resolver) { NodeFactory Factory; Remangler remangler(resolver, Factory); - remangler.mangle(node); + remangler.mangle(node, 0); return remangler.str(); } @@ -2777,12 +2895,19 @@ Demangle::mangleNode(NodePointer node, SymbolicResolver resolver, return StringRef(); Remangler remangler(resolver, Factory); - remangler.mangle(node); + remangler.mangle(node, 0); return remangler.getBufferStr(); } bool Demangle::isSpecialized(Node *node) { + // We shouldn't get here with node being NULL; if we do, assert in debug, + // or return false at runtime (which should at least help diagnose things + // further if it happens). + assert(node); + if (!node) + return false; + switch (node->getKind()) { case Node::Kind::BoundGenericStructure: case Node::Kind::BoundGenericEnum: @@ -2819,10 +2944,12 @@ bool Demangle::isSpecialized(Node *node) { case Node::Kind::ModifyAccessor: case Node::Kind::UnsafeAddressor: case Node::Kind::UnsafeMutableAddressor: - return isSpecialized(node->getChild(0)); + assert(node->getNumChildren() > 0); + return node->getNumChildren() > 0 && isSpecialized(node->getChild(0)); case Node::Kind::Extension: - return isSpecialized(node->getChild(1)); + assert(node->getNumChildren() > 1); + return node->getNumChildren() > 1 && isSpecialized(node->getChild(1)); default: return false; diff --git a/lib/DependencyScan/CMakeLists.txt b/lib/DependencyScan/CMakeLists.txt index e5190d22e7a59..94a1b05df0923 100644 --- a/lib/DependencyScan/CMakeLists.txt +++ b/lib/DependencyScan/CMakeLists.txt @@ -1,7 +1,8 @@ -#set_swift_llvm_is_available() + add_swift_host_library(swiftDependencyScan STATIC DependencyScanningTool.cpp + ModuleDependencyCacheSerialization.cpp ScanDependencies.cpp StringUtils.cpp) diff --git a/lib/DependencyScan/DependencyScanningTool.cpp b/lib/DependencyScan/DependencyScanningTool.cpp index 26c12e5c03205..8c4f70afc7e87 100644 --- a/lib/DependencyScan/DependencyScanningTool.cpp +++ b/lib/DependencyScan/DependencyScanningTool.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/DependencyScan/DependencyScanningTool.h" +#include "swift/DependencyScan/SerializedModuleDependencyCacheFormat.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/Basic/LLVMInitialize.h" @@ -24,7 +25,7 @@ namespace swift { namespace dependencies { DependencyScanningTool::DependencyScanningTool() - : SharedCache(std::make_unique()), + : SharedCache(std::make_unique()), VersionedPCMInstanceCacheCache( std::make_unique()), PDC(), Alloc(), Saver(Alloc) {} @@ -39,8 +40,10 @@ DependencyScanningTool::getDependencies( return EC; auto Instance = std::move(*InstanceOrErr); + // Local scan cache instance, wrapping the shared global cache. + ModuleDependenciesCache cache(*SharedCache); // Execute the scanning action, retreiving the in-memory result - auto DependenciesOrErr = performModuleScan(*Instance.get(), *SharedCache); + auto DependenciesOrErr = performModuleScan(*Instance.get(), cache); if (DependenciesOrErr.getError()) return std::make_error_code(std::errc::not_supported); auto Dependencies = std::move(*DependenciesOrErr); @@ -77,11 +80,39 @@ DependencyScanningTool::getDependencies( BatchInput.size(), std::make_error_code(std::errc::invalid_argument)); auto Instance = std::move(*InstanceOrErr); - auto batchScanResults = performBatchModuleScan( - *Instance.get(), *SharedCache, VersionedPCMInstanceCacheCache.get(), + // Local scan cache instance, wrapping the shared global cache. + ModuleDependenciesCache cache(*SharedCache); + auto BatchScanResults = performBatchModuleScan( + *Instance.get(), cache, VersionedPCMInstanceCacheCache.get(), Saver, BatchInput); - return batchScanResults; + return BatchScanResults; +} + +void DependencyScanningTool::serializeCache(llvm::StringRef path) { + SourceManager SM; + DiagnosticEngine Diags(SM); + Diags.addConsumer(PDC); + module_dependency_cache_serialization::writeInterModuleDependenciesCache( + Diags, path, *SharedCache); +} + +bool DependencyScanningTool::loadCache(llvm::StringRef path) { + SourceManager SM; + DiagnosticEngine Diags(SM); + Diags.addConsumer(PDC); + SharedCache = std::make_unique(); + bool readFailed = + module_dependency_cache_serialization::readInterModuleDependenciesCache( + path, *SharedCache); + if (readFailed) { + Diags.diagnose(SourceLoc(), diag::warn_scaner_deserialize_failed, path); + } + return readFailed; +} + +void DependencyScanningTool::resetCache() { + SharedCache.reset(new GlobalModuleDependenciesCache()); } llvm::ErrorOr> diff --git a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp new file mode 100644 index 0000000000000..6ef405496e390 --- /dev/null +++ b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp @@ -0,0 +1,955 @@ +//=== ModuleDependencyCacheSerialization.cpp - serialized format -*- C++ -*-==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/FileSystem.h" +#include "swift/AST/ModuleDependencies.h" +#include "swift/Basic/PrettyStackTrace.h" +#include "swift/Basic/Version.h" +#include "swift/DependencyScan/SerializedModuleDependencyCacheFormat.h" +#include "llvm/ADT/DenseMap.h" +#include + +using namespace swift; +using namespace dependencies; +using namespace module_dependency_cache_serialization; + +// MARK: Deserialization +namespace { + +class Deserializer { + std::vector Identifiers; + std::vector> ArraysOfIdentifierIDs; + llvm::BitstreamCursor Cursor; + SmallVector Scratch; + StringRef BlobData; + + // These return true if there was an error. + bool readSignature(); + bool enterGraphBlock(); + bool readMetadata(); + bool readGraph(GlobalModuleDependenciesCache &cache); + + llvm::Optional getIdentifier(unsigned n); + llvm::Optional> getArray(unsigned n); + +public: + Deserializer(llvm::MemoryBufferRef Data) : Cursor(Data) {} + bool readInterModuleDependenciesCache(GlobalModuleDependenciesCache &cache); +}; + +} // end namespace + +/// Read in the expected signature: IMDC +bool Deserializer::readSignature() { + for (unsigned char byte : MODULE_DEPENDENCY_CACHE_FORMAT_SIGNATURE) { + if (Cursor.AtEndOfStream()) + return true; + if (auto maybeRead = Cursor.Read(8)) { + if (maybeRead.get() != byte) + return true; + } else { + return true; + } + } + return false; +} + +/// Read in the info block and enter the top-level block which represents the +/// graph +bool Deserializer::enterGraphBlock() { + // Read the BLOCKINFO_BLOCK, which contains metadata used when dumping + // the binary data with llvm-bcanalyzer. + { + auto next = Cursor.advance(); + if (!next) { + consumeError(next.takeError()); + return true; + } + + if (next->Kind != llvm::BitstreamEntry::SubBlock) + return true; + + if (next->ID != llvm::bitc::BLOCKINFO_BLOCK_ID) + return true; + + if (!Cursor.ReadBlockInfoBlock()) + return true; + } + + // Enters our top-level subblock, + // which contains the actual module dependency information. + { + auto next = Cursor.advance(); + if (!next) { + consumeError(next.takeError()); + return true; + } + + if (next->Kind != llvm::BitstreamEntry::SubBlock) + return true; + + if (next->ID != GRAPH_BLOCK_ID) + return true; + + if (auto err = Cursor.EnterSubBlock(GRAPH_BLOCK_ID)) { + consumeError(std::move(err)); + return true; + } + } + return false; +} + +/// Read in the serialized file's format version, error/exit if not matching +/// current version. +bool Deserializer::readMetadata() { + using namespace graph_block; + + auto entry = Cursor.advance(); + if (!entry) { + consumeError(entry.takeError()); + return true; + } + + if (entry->Kind != llvm::BitstreamEntry::Record) + return true; + + auto recordID = Cursor.readRecord(entry->ID, Scratch, &BlobData); + if (!recordID) { + consumeError(recordID.takeError()); + return true; + } + + if (*recordID != METADATA) + return true; + + unsigned majorVersion, minorVersion; + + MetadataLayout::readRecord(Scratch, majorVersion, minorVersion); + if (majorVersion != MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR || + minorVersion != MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR) { + return true; + } + + return false; +} + +/// Read in the top-level block's graph structure by first reading in +/// all of the file's identifiers and arrays of identifiers, followed by +/// consuming individual module info records and registering them into the +/// cache. +bool Deserializer::readGraph(GlobalModuleDependenciesCache &cache) { + using namespace graph_block; + + bool hasCurrentModule = false; + std::string currentModuleName; + llvm::Optional> currentModuleDependencies; + + while (!Cursor.AtEndOfStream()) { + auto entry = cantFail(Cursor.advance(), "Advance bitstream cursor"); + + if (entry.Kind == llvm::BitstreamEntry::EndBlock) { + Cursor.ReadBlockEnd(); + assert(Cursor.GetCurrentBitNo() % CHAR_BIT == 0); + break; + } + + if (entry.Kind != llvm::BitstreamEntry::Record) + llvm::report_fatal_error("Bad bitstream entry kind"); + + Scratch.clear(); + unsigned recordID = + cantFail(Cursor.readRecord(entry.ID, Scratch, &BlobData), + "Read bitstream record"); + + switch (recordID) { + case METADATA: { + // METADATA must appear at the beginning and is read by readMetadata(). + llvm::report_fatal_error("Unexpected METADATA record"); + break; + } + + case IDENTIFIER_NODE: { + // IDENTIFIER_NODE must come before MODULE_NODEs. + if (hasCurrentModule) + llvm::report_fatal_error("Unexpected IDENTIFIER_NODE record"); + IdentifierNodeLayout::readRecord(Scratch); + Identifiers.push_back(BlobData.str()); + break; + } + + case IDENTIFIER_ARRAY_NODE: { + // IDENTIFIER_ARRAY_NODE must come before MODULE_NODEs. + if (hasCurrentModule) + llvm::report_fatal_error("Unexpected IDENTIFIER_NODE record"); + ArrayRef identifierIDs; + IdentifierArrayLayout::readRecord(Scratch, identifierIDs); + ArraysOfIdentifierIDs.push_back(identifierIDs.vec()); + break; + } + + case MODULE_NODE: { + hasCurrentModule = true; + unsigned moduleNameID, moduleDependenciesArrayID; + ModuleInfoLayout::readRecord(Scratch, moduleNameID, + moduleDependenciesArrayID); + auto moduleName = getIdentifier(moduleNameID); + if (!moduleName) + llvm::report_fatal_error("Bad module name"); + currentModuleName = *moduleName; + + currentModuleDependencies = getArray(moduleDependenciesArrayID); + if (!currentModuleDependencies) + llvm::report_fatal_error("Bad direct dependencies"); + break; + } + + case SWIFT_TEXTUAL_MODULE_DETAILS_NODE: { + if (!hasCurrentModule) + llvm::report_fatal_error( + "Unexpected SWIFT_TEXTUAL_MODULE_DETAILS_NODE record"); + unsigned interfaceFileID, compiledModuleCandidatesArrayID, + buildCommandLineArrayID, extraPCMArgsArrayID, contextHashID, + isFramework, bridgingHeaderFileID, sourceFilesArrayID, + bridgingSourceFilesArrayID, bridgingModuleDependenciesArrayID; + SwiftTextualModuleDetailsLayout::readRecord( + Scratch, interfaceFileID, compiledModuleCandidatesArrayID, + buildCommandLineArrayID, extraPCMArgsArrayID, contextHashID, + isFramework, bridgingHeaderFileID, sourceFilesArrayID, + bridgingSourceFilesArrayID, bridgingModuleDependenciesArrayID); + + Optional optionalSwiftInterfaceFile; + if (interfaceFileID != 0) { + auto swiftInterfaceFile = getIdentifier(interfaceFileID); + if (!swiftInterfaceFile) + llvm::report_fatal_error("Bad swift interface file path"); + optionalSwiftInterfaceFile = *swiftInterfaceFile; + } + auto compiledModuleCandidates = getArray(compiledModuleCandidatesArrayID); + if (!compiledModuleCandidates) + llvm::report_fatal_error("Bad compiled module candidates"); + auto commandLine = getArray(buildCommandLineArrayID); + if (!commandLine) + llvm::report_fatal_error("Bad command line"); + auto extraPCMArgs = getArray(extraPCMArgsArrayID); + if (!extraPCMArgs) + llvm::report_fatal_error("Bad PCM Args set"); + auto contextHash = getIdentifier(contextHashID); + if (!contextHash) + llvm::report_fatal_error("Bad context hash"); + + // forSwiftInterface API demands references here. + std::vector buildCommandRefs; + for (auto &arg : *commandLine) + buildCommandRefs.push_back(arg); + std::vector extraPCMRefs; + for (auto &arg : *extraPCMArgs) + extraPCMRefs.push_back(arg); + + // Form the dependencies storage object + auto moduleDep = ModuleDependencies::forSwiftTextualModule( + optionalSwiftInterfaceFile, *compiledModuleCandidates, + buildCommandRefs, extraPCMRefs, *contextHash, isFramework); + + // Add dependencies of this module + for (const auto &moduleName : *currentModuleDependencies) + moduleDep.addModuleDependency(moduleName); + + // Add bridging header file path + if (bridgingHeaderFileID != 0) { + auto bridgingHeaderFile = getIdentifier(bridgingHeaderFileID); + if (!bridgingHeaderFile) + llvm::report_fatal_error("Bad bridging header path"); + + moduleDep.addBridgingHeader(*bridgingHeaderFile); + } + + // Add bridging source files + auto bridgingSourceFiles = getArray(bridgingSourceFilesArrayID); + if (!bridgingSourceFiles) + llvm::report_fatal_error("Bad bridging source files"); + for (const auto &file : *bridgingSourceFiles) + moduleDep.addBridgingSourceFile(file); + + // Add source files + auto sourceFiles = getArray(sourceFilesArrayID); + if (!sourceFiles) + llvm::report_fatal_error("Bad bridging source files"); + for (const auto &file : *sourceFiles) + moduleDep.addSourceFile(file); + + // Add bridging module dependencies + auto bridgingModuleDeps = getArray(bridgingModuleDependenciesArrayID); + if (!bridgingModuleDeps) + llvm::report_fatal_error("Bad bridging module dependencies"); + llvm::StringSet<> alreadyAdded; + for (const auto &mod : *bridgingModuleDeps) + moduleDep.addBridgingModuleDependency(mod, alreadyAdded); + + cache.recordDependencies(currentModuleName, std::move(moduleDep)); + hasCurrentModule = false; + break; + } + + case SWIFT_BINARY_MODULE_DETAILS_NODE: { + if (!hasCurrentModule) + llvm::report_fatal_error( + "Unexpected SWIFT_BINARY_MODULE_DETAILS_NODE record"); + unsigned compiledModulePathID, moduleDocPathID, moduleSourceInfoPathID, + isFramework; + SwiftBinaryModuleDetailsLayout::readRecord( + Scratch, compiledModulePathID, moduleDocPathID, + moduleSourceInfoPathID, isFramework); + + auto compiledModulePath = getIdentifier(compiledModulePathID); + if (!compiledModulePath) + llvm::report_fatal_error("Bad compiled module path"); + auto moduleDocPath = getIdentifier(moduleDocPathID); + if (!moduleDocPath) + llvm::report_fatal_error("Bad module doc path"); + auto moduleSourceInfoPath = getIdentifier(moduleSourceInfoPathID); + if (!moduleSourceInfoPath) + llvm::report_fatal_error("Bad module source info path"); + + // Form the dependencies storage object + auto moduleDep = ModuleDependencies::forSwiftBinaryModule( + *compiledModulePath, *moduleDocPath, *moduleSourceInfoPath, + isFramework); + // Add dependencies of this module + for (const auto &moduleName : *currentModuleDependencies) + moduleDep.addModuleDependency(moduleName); + + cache.recordDependencies(currentModuleName, std::move(moduleDep)); + hasCurrentModule = false; + break; + } + + case SWIFT_PLACEHOLDER_MODULE_DETAILS_NODE: { + if (!hasCurrentModule) + llvm::report_fatal_error( + "Unexpected SWIFT_PLACEHOLDER_MODULE_DETAILS_NODE record"); + unsigned compiledModulePathID, moduleDocPathID, moduleSourceInfoPathID; + SwiftPlaceholderModuleDetailsLayout::readRecord( + Scratch, compiledModulePathID, moduleDocPathID, + moduleSourceInfoPathID); + + auto compiledModulePath = getIdentifier(compiledModulePathID); + if (!compiledModulePath) + llvm::report_fatal_error("Bad compiled module path"); + auto moduleDocPath = getIdentifier(moduleDocPathID); + if (!moduleDocPath) + llvm::report_fatal_error("Bad module doc path"); + auto moduleSourceInfoPath = getIdentifier(moduleSourceInfoPathID); + if (!moduleSourceInfoPath) + llvm::report_fatal_error("Bad module source info path"); + + // Form the dependencies storage object + auto moduleDep = ModuleDependencies::forPlaceholderSwiftModuleStub( + *compiledModulePath, *moduleDocPath, *moduleSourceInfoPath); + // Add dependencies of this module + for (const auto &moduleName : *currentModuleDependencies) + moduleDep.addModuleDependency(moduleName); + + cache.recordDependencies(currentModuleName, std::move(moduleDep)); + hasCurrentModule = false; + break; + } + + case CLANG_MODULE_DETAILS_NODE: { + if (!hasCurrentModule) + llvm::report_fatal_error("Unexpected CLANG_MODULE_DETAILS_NODE record"); + unsigned moduleMapPathID, contextHashID, commandLineArrayID, + fileDependenciesArrayID; + ClangModuleDetailsLayout::readRecord(Scratch, moduleMapPathID, + contextHashID, commandLineArrayID, + fileDependenciesArrayID); + auto moduleMapPath = getIdentifier(moduleMapPathID); + if (!moduleMapPath) + llvm::report_fatal_error("Bad module map path"); + auto contextHash = getIdentifier(contextHashID); + if (!contextHash) + llvm::report_fatal_error("Bad context hash"); + auto commandLineArgs = getArray(commandLineArrayID); + if (!commandLineArgs) + llvm::report_fatal_error("Bad command line"); + auto fileDependencies = getArray(fileDependenciesArrayID); + if (!fileDependencies) + llvm::report_fatal_error("Bad file dependencies"); + + // Form the dependencies storage object + auto moduleDep = ModuleDependencies::forClangModule( + *moduleMapPath, *contextHash, *commandLineArgs, *fileDependencies); + // Add dependencies of this module + for (const auto &moduleName : *currentModuleDependencies) + moduleDep.addModuleDependency(moduleName); + + cache.recordDependencies(currentModuleName, std::move(moduleDep)); + hasCurrentModule = false; + break; + } + + default: { + llvm::report_fatal_error("Unknown record ID"); + } + } + } + + return false; +} + +bool Deserializer::readInterModuleDependenciesCache( + GlobalModuleDependenciesCache &cache) { + using namespace graph_block; + + if (readSignature()) + return true; + + if (enterGraphBlock()) + return true; + + if (readMetadata()) + return true; + + if (readGraph(cache)) + return true; + + return false; +} + +llvm::Optional Deserializer::getIdentifier(unsigned n) { + if (n == 0) + return std::string(); + + --n; + if (n >= Identifiers.size()) + return None; + + return Identifiers[n]; +} + +llvm::Optional> Deserializer::getArray(unsigned n) { + if (n == 0) + return std::vector(); + + --n; + if (n >= ArraysOfIdentifierIDs.size()) + return None; + + auto &identifierIDs = ArraysOfIdentifierIDs[n]; + + auto IDtoStringMap = [this](unsigned id) { + auto identifier = getIdentifier(id); + if (!identifier) + llvm::report_fatal_error("Bad identifier array element"); + return *identifier; + }; + std::vector result; + result.reserve(identifierIDs.size()); + std::transform(identifierIDs.begin(), identifierIDs.end(), + std::back_inserter(result), IDtoStringMap); + return result; +} + +bool swift::dependencies::module_dependency_cache_serialization:: + readInterModuleDependenciesCache(llvm::MemoryBuffer &buffer, + GlobalModuleDependenciesCache &cache) { + Deserializer deserializer(buffer.getMemBufferRef()); + return deserializer.readInterModuleDependenciesCache(cache); +} + +bool swift::dependencies::module_dependency_cache_serialization:: + readInterModuleDependenciesCache(StringRef path, + GlobalModuleDependenciesCache &cache) { + PrettyStackTraceStringAction stackTrace( + "loading inter-module dependency graph", path); + auto buffer = llvm::MemoryBuffer::getFile(path); + if (!buffer) + return true; + + return readInterModuleDependenciesCache(*buffer.get(), cache); +} + +// MARK: Serialization + +/// Kinds of arrays that we track being serialized. Used to query serialized +/// array ID for a given module. +enum ModuleIdentifierArrayKind : uint8_t { + Empty = 0, + DirectDependencies, + CompiledModuleCandidates, + BuildCommandLine, + ExtraPCMArgs, + SourceFiles, + BridgingSourceFiles, + BridgingModuleDependencies, + NonPathCommandLine, + FileDependencies, + LastArrayKind +}; + +using ModuleIdentifierArrayKey = + std::pair; + +// DenseMap Infos for hashing of ModuleIdentifierArrayKind +template <> +struct llvm::DenseMapInfo { + using UnderlyingType = std::underlying_type::type; + using UnerlyingInfo = DenseMapInfo; + + static inline ModuleIdentifierArrayKind getEmptyKey() { + return ModuleIdentifierArrayKind::Empty; + } + static inline ModuleIdentifierArrayKind getTombstoneKey() { + return ModuleIdentifierArrayKind::LastArrayKind; + } + static unsigned getHashValue(const ModuleIdentifierArrayKind &arrKind) { + auto underlyingValue = static_cast(arrKind); + return UnerlyingInfo::getHashValue(underlyingValue); + } + static bool isEqual(const ModuleIdentifierArrayKind &LHS, + const ModuleIdentifierArrayKind &RHS) { + return LHS == RHS; + } +}; + +namespace std { +template <> +struct hash { + using UnderlyingKindType = std::underlying_type::type; + std::size_t operator()(const ModuleDependencyID &id) const { + auto underlyingKindValue = static_cast(id.second); + + return (hash()(id.first) ^ + (hash()(underlyingKindValue))); + } +}; +} // namespace std + +namespace { + +class Serializer { + llvm::StringMap IdentifierIDs; + std::unordered_map> + ArrayIDs; + unsigned LastIdentifierID = 0; + unsigned LastArrayID = 0; + std::vector Identifiers; + std::vector> ArraysOfIdentifiers; + + llvm::BitstreamWriter &Out; + + /// A reusable buffer for emitting records. + SmallVector ScratchRecord; + std::array AbbrCodes; + + // Returns the identifier ID of the added identifier, either + // new or previously-hashed + unsigned addIdentifier(const std::string &str); + unsigned getIdentifier(const std::string &str) const; + + // Returns the array ID of the added array + void addArray(ModuleDependencyID moduleID, + ModuleIdentifierArrayKind arrayKind, + const std::vector &vec); + unsigned getArray(ModuleDependencyID moduleID, + ModuleIdentifierArrayKind arrayKind) const; + + template + void registerRecordAbbr() { + using AbbrArrayTy = decltype(AbbrCodes); + static_assert(Layout::Code <= std::tuple_size::value, + "layout has invalid record code"); + AbbrCodes[Layout::Code] = Layout::emitAbbrev(Out); + } + + void collectStringsAndArrays(const GlobalModuleDependenciesCache &cache); + + void emitBlockID(unsigned ID, StringRef name, + SmallVectorImpl &nameBuffer); + + void emitRecordID(unsigned ID, StringRef name, + SmallVectorImpl &nameBuffer); + + void writeSignature(); + void writeBlockInfoBlock(); + + void writeMetadata(); + void writeIdentifiers(); + void writeArraysOfIdentifiers(); + + void writeModuleInfo(ModuleDependencyID moduleID, + const ModuleDependencies &dependencyInfo); + +public: + Serializer(llvm::BitstreamWriter &ExistingOut) : Out(ExistingOut) {} + +public: + void writeInterModuleDependenciesCache(const GlobalModuleDependenciesCache &cache); +}; + +} // end namespace + +/// Record the name of a block. +void Serializer::emitBlockID(unsigned ID, StringRef name, + SmallVectorImpl &nameBuffer) { + SmallVector idBuffer; + idBuffer.push_back(ID); + Out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, idBuffer); + + // Emit the block name if present. + if (name.empty()) + return; + nameBuffer.resize(name.size()); + memcpy(nameBuffer.data(), name.data(), name.size()); + Out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, nameBuffer); +} + +/// Record the name of a record. +void Serializer::emitRecordID(unsigned ID, StringRef name, + SmallVectorImpl &nameBuffer) { + assert(ID < 256 && "can't fit record ID in next to name"); + nameBuffer.resize(name.size() + 1); + nameBuffer[0] = ID; + memcpy(nameBuffer.data() + 1, name.data(), name.size()); + Out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, nameBuffer); +} + +void Serializer::writeBlockInfoBlock() { + llvm::BCBlockRAII restoreBlock(Out, llvm::bitc::BLOCKINFO_BLOCK_ID, 2); + + SmallVector nameBuffer; +#define BLOCK(X) emitBlockID(X##_ID, #X, nameBuffer) +#define BLOCK_RECORD(K, X) emitRecordID(K::X, #X, nameBuffer) + + BLOCK(GRAPH_BLOCK); + BLOCK_RECORD(graph_block, METADATA); + BLOCK_RECORD(graph_block, IDENTIFIER_NODE); + BLOCK_RECORD(graph_block, IDENTIFIER_ARRAY_NODE); + + BLOCK_RECORD(graph_block, MODULE_NODE); + BLOCK_RECORD(graph_block, SWIFT_TEXTUAL_MODULE_DETAILS_NODE); + BLOCK_RECORD(graph_block, SWIFT_BINARY_MODULE_DETAILS_NODE); + BLOCK_RECORD(graph_block, SWIFT_PLACEHOLDER_MODULE_DETAILS_NODE); + BLOCK_RECORD(graph_block, CLANG_MODULE_DETAILS_NODE); +} + +void Serializer::writeSignature() { + for (auto c : MODULE_DEPENDENCY_CACHE_FORMAT_SIGNATURE) + Out.Emit((unsigned)c, 8); +} + +void Serializer::writeMetadata() { + using namespace graph_block; + + MetadataLayout::emitRecord(Out, ScratchRecord, + AbbrCodes[MetadataLayout::Code], + MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR, + MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR, + version::getSwiftFullVersion()); +} + +void Serializer::writeIdentifiers() { + using namespace graph_block; + for (auto str : Identifiers) { + IdentifierNodeLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[IdentifierNodeLayout::Code], str); + } +} + +void Serializer::writeArraysOfIdentifiers() { + using namespace graph_block; + for (auto vec : ArraysOfIdentifiers) { + IdentifierArrayLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[IdentifierArrayLayout::Code], vec); + } +} + +void Serializer::writeModuleInfo(ModuleDependencyID moduleID, + const ModuleDependencies &dependencyInfo) { + using namespace graph_block; + + ModuleInfoLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[ModuleInfoLayout::Code], + getIdentifier(moduleID.first), + getArray(moduleID, ModuleIdentifierArrayKind::DirectDependencies)); + + switch (dependencyInfo.getKind()) { + case swift::ModuleDependenciesKind::SwiftTextual: { + auto swiftTextDeps = dependencyInfo.getAsSwiftTextualModule(); + assert(swiftTextDeps); + unsigned swiftInterfaceFileId = + swiftTextDeps->swiftInterfaceFile + ? getIdentifier(swiftTextDeps->swiftInterfaceFile.getValue()) + : 0; + unsigned bridgingHeaderFileId = + swiftTextDeps->bridgingHeaderFile + ? getIdentifier(swiftTextDeps->bridgingHeaderFile.getValue()) + : 0; + SwiftTextualModuleDetailsLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[SwiftTextualModuleDetailsLayout::Code], + swiftInterfaceFileId, + getArray(moduleID, ModuleIdentifierArrayKind::CompiledModuleCandidates), + getArray(moduleID, ModuleIdentifierArrayKind::BuildCommandLine), + getArray(moduleID, ModuleIdentifierArrayKind::ExtraPCMArgs), + getIdentifier(swiftTextDeps->contextHash), swiftTextDeps->isFramework, + bridgingHeaderFileId, + getArray(moduleID, ModuleIdentifierArrayKind::SourceFiles), + getArray(moduleID, ModuleIdentifierArrayKind::BridgingSourceFiles), + getArray(moduleID, + ModuleIdentifierArrayKind::BridgingModuleDependencies)); + break; + } + case swift::ModuleDependenciesKind::SwiftBinary: { + auto swiftBinDeps = dependencyInfo.getAsSwiftBinaryModule(); + assert(swiftBinDeps); + SwiftBinaryModuleDetailsLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[SwiftBinaryModuleDetailsLayout::Code], + getIdentifier(swiftBinDeps->compiledModulePath), + getIdentifier(swiftBinDeps->moduleDocPath), + getIdentifier(swiftBinDeps->sourceInfoPath), swiftBinDeps->isFramework); + + break; + } + case swift::ModuleDependenciesKind::SwiftPlaceholder: { + auto swiftPHDeps = dependencyInfo.getAsPlaceholderDependencyModule(); + assert(swiftPHDeps); + SwiftPlaceholderModuleDetailsLayout::emitRecord( + Out, ScratchRecord, + AbbrCodes[SwiftPlaceholderModuleDetailsLayout::Code], + getIdentifier(swiftPHDeps->compiledModulePath), + getIdentifier(swiftPHDeps->moduleDocPath), + getIdentifier(swiftPHDeps->sourceInfoPath)); + break; + } + case swift::ModuleDependenciesKind::Clang: { + auto clangDeps = dependencyInfo.getAsClangModule(); + assert(clangDeps); + ClangModuleDetailsLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[ClangModuleDetailsLayout::Code], + getIdentifier(clangDeps->moduleMapFile), + getIdentifier(clangDeps->contextHash), + getArray(moduleID, ModuleIdentifierArrayKind::NonPathCommandLine), + getArray(moduleID, ModuleIdentifierArrayKind::FileDependencies)); + + break; + } + default: + llvm_unreachable("Unhandled dependency kind."); + } +} + +unsigned Serializer::addIdentifier(const std::string &str) { + if (str.empty()) + return 0; + + decltype(IdentifierIDs)::iterator iter; + bool isNew; + std::tie(iter, isNew) = IdentifierIDs.insert({str, LastIdentifierID + 1}); + + if (!isNew) + return iter->getValue(); + + ++LastIdentifierID; + // Note that we use the string data stored in the StringMap. + Identifiers.push_back(iter->getKey()); + return iter->getValue(); +} + +unsigned Serializer::getIdentifier(const std::string &str) const { + if (str.empty()) + return 0; + + auto iter = IdentifierIDs.find(str); + assert(iter != IdentifierIDs.end()); + assert(iter->second != 0); + return iter->second; +} + +void Serializer::addArray(ModuleDependencyID moduleID, + ModuleIdentifierArrayKind arrayKind, + const std::vector &vec) { + if (ArrayIDs.find(moduleID) != ArrayIDs.end()) { + // Already have arrays for this module + llvm::DenseMap::iterator iter; + bool isNew; + std::tie(iter, isNew) = + ArrayIDs[moduleID].insert({arrayKind, LastArrayID + 1}); + if (!isNew) + return; + } else { + // Do not yet have any arrays for this module + ArrayIDs[moduleID] = llvm::DenseMap(); + ArrayIDs[moduleID].insert({arrayKind, LastArrayID + 1}); + } + + ++LastArrayID; + + // Add in the individual identifiers in the array + std::vector identifierIDs; + identifierIDs.reserve(vec.size()); + for (const auto &id : vec) { + identifierIDs.push_back(addIdentifier(id)); + } + + ArraysOfIdentifiers.push_back(identifierIDs); + return; +} + +unsigned Serializer::getArray(ModuleDependencyID moduleID, + ModuleIdentifierArrayKind arrayKind) const { + auto iter = ArrayIDs.find(moduleID); + assert(iter != ArrayIDs.end()); + auto &innerMap = iter->second; + auto arrayIter = innerMap.find(arrayKind); + assert(arrayIter != innerMap.end()); + return arrayIter->second; +} + +void Serializer::collectStringsAndArrays(const GlobalModuleDependenciesCache &cache) { + for (auto &moduleID : cache.getAllModules()) { + auto dependencyInfos = cache.findAllDependenciesIrrespectiveOfSearchPaths( + moduleID.first, moduleID.second); + assert(dependencyInfos.hasValue() && "Expected dependency info."); + for (auto &dependencyInfo : *dependencyInfos) { + // Add the module's name + addIdentifier(moduleID.first); + // Add the module's dependencies + addArray(moduleID, ModuleIdentifierArrayKind::DirectDependencies, + dependencyInfo.getModuleDependencies()); + + // Add the dependency-kind-specific data + switch (dependencyInfo.getKind()) { + case swift::ModuleDependenciesKind::SwiftTextual: { + auto swiftTextDeps = dependencyInfo.getAsSwiftTextualModule(); + assert(swiftTextDeps); + if (swiftTextDeps->swiftInterfaceFile) + addIdentifier(swiftTextDeps->swiftInterfaceFile.getValue()); + addArray(moduleID, ModuleIdentifierArrayKind::CompiledModuleCandidates, + swiftTextDeps->compiledModuleCandidates); + addArray(moduleID, ModuleIdentifierArrayKind::BuildCommandLine, + swiftTextDeps->buildCommandLine); + addArray(moduleID, ModuleIdentifierArrayKind::ExtraPCMArgs, + swiftTextDeps->extraPCMArgs); + addIdentifier(swiftTextDeps->contextHash); + if (swiftTextDeps->bridgingHeaderFile) + addIdentifier(swiftTextDeps->bridgingHeaderFile.getValue()); + addArray(moduleID, ModuleIdentifierArrayKind::SourceFiles, + swiftTextDeps->sourceFiles); + addArray(moduleID, ModuleIdentifierArrayKind::BridgingSourceFiles, + swiftTextDeps->bridgingSourceFiles); + addArray(moduleID, + ModuleIdentifierArrayKind::BridgingModuleDependencies, + swiftTextDeps->bridgingModuleDependencies); + break; + } + case swift::ModuleDependenciesKind::SwiftBinary: { + auto swiftBinDeps = dependencyInfo.getAsSwiftBinaryModule(); + assert(swiftBinDeps); + addIdentifier(swiftBinDeps->compiledModulePath); + addIdentifier(swiftBinDeps->moduleDocPath); + addIdentifier(swiftBinDeps->sourceInfoPath); + break; + } + case swift::ModuleDependenciesKind::SwiftPlaceholder: { + auto swiftPHDeps = dependencyInfo.getAsPlaceholderDependencyModule(); + assert(swiftPHDeps); + addIdentifier(swiftPHDeps->compiledModulePath); + addIdentifier(swiftPHDeps->moduleDocPath); + addIdentifier(swiftPHDeps->sourceInfoPath); + break; + } + case swift::ModuleDependenciesKind::Clang: { + auto clangDeps = dependencyInfo.getAsClangModule(); + assert(clangDeps); + addIdentifier(clangDeps->moduleMapFile); + addIdentifier(clangDeps->contextHash); + addArray(moduleID, ModuleIdentifierArrayKind::NonPathCommandLine, + clangDeps->nonPathCommandLine); + addArray(moduleID, ModuleIdentifierArrayKind::FileDependencies, + clangDeps->fileDependencies); + break; + } + default: + llvm_unreachable("Unhandled dependency kind."); + } + } + } +} + +void Serializer::writeInterModuleDependenciesCache( + const GlobalModuleDependenciesCache &cache) { + // Write the header + writeSignature(); + writeBlockInfoBlock(); + + // Enter the main graph block + unsigned blockID = GRAPH_BLOCK_ID; + llvm::BCBlockRAII restoreBlock(Out, blockID, 8); + + using namespace graph_block; + + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + + // Make a pass to collect all unique strings and arrays + // of strings + collectStringsAndArrays(cache); + + // Write the version information + writeMetadata(); + + // Write the strings + writeIdentifiers(); + + // Write the arrays + writeArraysOfIdentifiers(); + + // Write the core graph + for (auto &moduleID : cache.getAllModules()) { + auto dependencyInfos = cache.findAllDependenciesIrrespectiveOfSearchPaths( + moduleID.first, moduleID.second); + assert(dependencyInfos.hasValue() && "Expected dependency info."); + for (auto &dependencyInfo : *dependencyInfos) { + writeModuleInfo(moduleID, dependencyInfo); + } + } + + return; +} + +void swift::dependencies::module_dependency_cache_serialization:: + writeInterModuleDependenciesCache(llvm::BitstreamWriter &Out, + const GlobalModuleDependenciesCache &cache) { + Serializer serializer{Out}; + serializer.writeInterModuleDependenciesCache(cache); +} + +bool swift::dependencies::module_dependency_cache_serialization:: + writeInterModuleDependenciesCache(DiagnosticEngine &diags, StringRef path, + const GlobalModuleDependenciesCache &cache) { + PrettyStackTraceStringAction stackTrace( + "saving inter-module dependency graph", path); + return withOutputFile(diags, path, [&](llvm::raw_ostream &out) { + SmallVector Buffer; + llvm::BitstreamWriter Writer{Buffer}; + writeInterModuleDependenciesCache(Writer, cache); + out.write(Buffer.data(), Buffer.size()); + out.flush(); + return false; + }); +} diff --git a/lib/DependencyScan/ScanDependencies.cpp b/lib/DependencyScan/ScanDependencies.cpp index 247802d328f7f..a8365779b2050 100644 --- a/lib/DependencyScan/ScanDependencies.cpp +++ b/lib/DependencyScan/ScanDependencies.cpp @@ -11,10 +11,12 @@ //===----------------------------------------------------------------------===// #include "swift/DependencyScan/ScanDependencies.h" +#include "swift/DependencyScan/SerializedModuleDependencyCacheFormat.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/DiagnosticsDriver.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleDependencies.h" #include "swift/AST/ModuleLoader.h" @@ -141,8 +143,10 @@ static void findAllImportedClangModules(ASTContext &ctx, StringRef moduleName, return; allModules.push_back(moduleName.str()); + auto currentImportPathSet = ctx.getAllModuleSearchPathsSet(); auto dependencies = - cache.findDependencies(moduleName, ModuleDependenciesKind::Clang); + cache.findDependencies(moduleName, + {ModuleDependenciesKind::Clang, currentImportPathSet}); if (!dependencies) return; @@ -158,7 +162,10 @@ resolveDirectDependencies(CompilerInstance &instance, ModuleDependencyID module, InterfaceSubContextDelegate &ASTDelegate, bool cacheOnly = false) { auto &ctx = instance.getASTContext(); - auto knownDependencies = *cache.findDependencies(module.first, module.second); + auto currentImportSet = ctx.getAllModuleSearchPathsSet(); + auto knownDependencies = *cache.findDependencies( + module.first, + {module.second,currentImportSet}); auto isSwiftInterface = knownDependencies.isSwiftTextualModule(); auto isSwift = isSwiftInterface || knownDependencies.isSwiftBinaryModule(); @@ -186,7 +193,8 @@ resolveDirectDependencies(CompilerInstance &instance, ModuleDependencyID module, // Grab the updated module dependencies. // FIXME: This is such a hack. knownDependencies = - *cache.findDependencies(module.first, module.second); + *cache.findDependencies(module.first, + {module.second, currentImportSet}); // Add the Clang modules referenced from the bridging header to the // set of Clang modules we know about. @@ -232,12 +240,15 @@ static void discoverCrosssImportOverlayDependencies( CompilerInstance &instance, StringRef mainModuleName, ArrayRef allDependencies, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &ASTDelegate, - llvm::function_ref action) { + llvm::function_ref action, + llvm::StringSet<> ¤tImportPathSet) { // Modules explicitly imported. Only these can be secondary module. llvm::SetVector newOverlays; for (auto dep : allDependencies) { auto moduleName = dep.first; - auto dependencies = *cache.findDependencies(moduleName, dep.second); + auto dependencies = *cache.findDependencies( + moduleName, + {dep.second, currentImportPathSet}); // Collect a map from secondary module name to cross-import overlay names. auto overlayMap = dependencies.collectCrossImportOverlayNames( instance.getASTContext(), moduleName); @@ -268,8 +279,9 @@ static void discoverCrosssImportOverlayDependencies( auto dummyMainDependencies = ModuleDependencies::forMainSwiftModule({}); // Update main module's dependencies to include these new overlays. - auto mainDep = *cache.findDependencies(mainModuleName, - ModuleDependenciesKind::SwiftTextual); + auto mainDep = *cache.findDependencies( + mainModuleName, + {ModuleDependenciesKind::SwiftTextual, currentImportPathSet}); std::for_each(newOverlays.begin(), newOverlays.end(), [&](Identifier modName) { dummyMainDependencies.addModuleDependency(modName.str()); @@ -280,7 +292,17 @@ static void discoverCrosssImportOverlayDependencies( // Record the dummy main module's direct dependencies. The dummy main module // only directly depend on these newly discovered overlay modules. - cache.recordDependencies(dummyMainName, dummyMainDependencies); + if (cache.findDependencies( + dummyMainName, + {ModuleDependenciesKind::SwiftTextual,currentImportPathSet})) { + cache.updateDependencies( + std::make_pair(dummyMainName.str(), + ModuleDependenciesKind::SwiftTextual), + dummyMainDependencies); + } else { + cache.recordDependencies(dummyMainName, dummyMainDependencies); + } + llvm::SetVector, std::set> allModules; @@ -712,6 +734,8 @@ static std::string createEncodedModuleKindAndName(ModuleDependencyID id) { return "swiftPlaceholder:" + id.first; case ModuleDependenciesKind::Clang: return "clang:" + id.first; + default: + llvm_unreachable("Unhandled dependency kind."); } } @@ -719,7 +743,8 @@ static swiftscan_dependency_graph_t generateFullDependencyGraph(CompilerInstance &instance, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &ASTDelegate, - ArrayRef allModules) { + ArrayRef allModules, + llvm::StringSet<> ¤tImportPathSet) { if (allModules.empty()) { return nullptr; } @@ -733,8 +758,15 @@ generateFullDependencyGraph(CompilerInstance &instance, for (size_t i = 0; i < allModules.size(); ++i) { const auto &module = allModules[i]; // Grab the completed module dependencies. - auto moduleDeps = *cache.findDependencies(module.first, module.second); + auto moduleDepsQuery = cache.findDependencies( + module.first, + {module.second, currentImportPathSet}); + if (!moduleDepsQuery) { + std::string err = "Module Dependency Cache missing module" + module.first; + llvm::report_fatal_error(err); + } + auto moduleDeps = *moduleDepsQuery; // Collect all the required pieces to build a ModuleInfo auto swiftPlaceholderDeps = moduleDeps.getAsPlaceholderDependencyModule(); auto swiftTextualDeps = moduleDeps.getAsSwiftTextualModule(); @@ -841,6 +873,8 @@ generateFullDependencyGraph(CompilerInstance &instance, case ModuleDependenciesKind::Clang: dependencyKindAndName = "clang"; break; + default: + llvm_unreachable("Unhandled dependency kind."); } dependencyKindAndName += ":"; dependencyKindAndName += dep.first; @@ -914,10 +948,6 @@ static bool diagnoseCycle(CompilerInstance &instance, return false; } -using CompilerArgInstanceCacheMap = - llvm::StringMap, - std::unique_ptr>>; - static void updateCachedInstanceOpts(CompilerInstance &cachedInstance, const CompilerInstance &invocationInstance, llvm::StringRef entryArguments) { @@ -976,8 +1006,8 @@ forEachBatchEntry(CompilerInstance &invocationInstance, } else if (subInstanceMap->count(entry.arguments)) { // Use the previously created instance if we've seen the arguments // before. - pInstance = (*subInstanceMap)[entry.arguments].first.get(); - pCache = (*subInstanceMap)[entry.arguments].second.get(); + pInstance = std::get<0>((*subInstanceMap)[entry.arguments]).get(); + pCache = std::get<2>((*subInstanceMap)[entry.arguments]).get(); // We must update the search paths of this instance to instead reflect // those of the current scanner invocation. updateCachedInstanceOpts(*pInstance, invocationInstance, entry.arguments); @@ -988,13 +1018,15 @@ forEachBatchEntry(CompilerInstance &invocationInstance, llvm::cl::ResetAllOptionOccurrences(); // Create a new instance by the arguments and save it in the map. + auto newGlobalCache = std::make_unique(); subInstanceMap->insert( {entry.arguments, - std::make_pair(std::make_unique(), - std::make_unique())}); + std::make_tuple(std::make_unique(), + std::move(newGlobalCache), + std::make_unique(*newGlobalCache))}); - pInstance = (*subInstanceMap)[entry.arguments].first.get(); - pCache = (*subInstanceMap)[entry.arguments].second.get(); + pInstance = std::get<0>((*subInstanceMap)[entry.arguments]).get(); + pCache = std::get<2>((*subInstanceMap)[entry.arguments]).get(); SmallVector args; llvm::cl::TokenizeGNUCommandLine(entry.arguments, saver, args); CompilerInvocation subInvok = invok; @@ -1033,11 +1065,16 @@ identifyMainModuleDependencies(CompilerInstance &instance) { instance.getASTContext() .LangOpts.EffectiveLanguageVersion.asAPINotesVersionString()) .str(); + // Compute the dependencies of the main module. - auto mainDependencies = ModuleDependencies::forMainSwiftModule( - {// ExtraPCMArgs - "-Xcc", "-target", "-Xcc", - instance.getASTContext().LangOpts.Target.str(), "-Xcc", apinotesVer}); + std::vector ExtraPCMArgs = { + "-Xcc", apinotesVer + }; + if (!instance.getASTContext().LangOpts.ClangTarget.hasValue()) + ExtraPCMArgs.insert(ExtraPCMArgs.begin(), + {"-Xcc", "-target", "-Xcc", + instance.getASTContext().LangOpts.Target.str()}); + auto mainDependencies = ModuleDependencies::forMainSwiftModule(ExtraPCMArgs); // Compute Implicit dependencies of the main module { @@ -1093,15 +1130,38 @@ identifyMainModuleDependencies(CompilerInstance &instance) { } // namespace +static void serializeDependencyCache(CompilerInstance &instance, + const GlobalModuleDependenciesCache &cache) { + const FrontendOptions &opts = instance.getInvocation().getFrontendOptions(); + ASTContext &Context = instance.getASTContext(); + auto savePath = opts.SerializedDependencyScannerCachePath; + module_dependency_cache_serialization::writeInterModuleDependenciesCache( + Context.Diags, savePath, cache); + if (opts.EmitDependencyScannerCacheRemarks) { + Context.Diags.diagnose(SourceLoc(), diag::remark_save_cache, savePath); + } +} + +static void deserializeDependencyCache(CompilerInstance &instance, + GlobalModuleDependenciesCache &cache) { + const FrontendOptions &opts = instance.getInvocation().getFrontendOptions(); + ASTContext &Context = instance.getASTContext(); + auto loadPath = opts.SerializedDependencyScannerCachePath; + if (module_dependency_cache_serialization::readInterModuleDependenciesCache( + loadPath, cache)) { + Context.Diags.diagnose(SourceLoc(), diag::warn_scaner_deserialize_failed, + loadPath); + } else if (opts.EmitDependencyScannerCacheRemarks) { + Context.Diags.diagnose(SourceLoc(), diag::remark_reuse_cache, loadPath); + } +} + bool swift::dependencies::scanDependencies(CompilerInstance &instance) { ASTContext &Context = instance.getASTContext(); const FrontendOptions &opts = instance.getInvocation().getFrontendOptions(); std::string path = opts.InputsAndOutputs.getSingleOutputFilename(); std::error_code EC; llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None); - // `-scan-dependencies` invocations use a single new instance - // of a module cache - ModuleDependenciesCache cache; if (out.has_error() || EC) { Context.Diags.diagnose(SourceLoc(), diag::error_opening_output, path, EC.message()); @@ -1109,11 +1169,26 @@ bool swift::dependencies::scanDependencies(CompilerInstance &instance) { return true; } - // Execute scan, and write JSON output to the output stream + // `-scan-dependencies` invocations use a single new instance + // of a module cache + GlobalModuleDependenciesCache globalCache; + if (opts.ReuseDependencyScannerCache) + deserializeDependencyCache(instance, globalCache); + + ModuleDependenciesCache cache(globalCache); + + // Execute scan auto dependenciesOrErr = performModuleScan(instance, cache); + + // Serialize the dependency cache if -serialize-dependency-scan-cache + // is specified + if (opts.SerializeDependencyScannerCache) + serializeDependencyCache(instance, globalCache); + if (dependenciesOrErr.getError()) return true; auto dependencies = std::move(*dependenciesOrErr); + // Write out the JSON description. writeJSON(out, dependencies); // This process succeeds regardless of whether any errors occurred. @@ -1132,7 +1207,8 @@ bool swift::dependencies::prescanDependencies(CompilerInstance &instance) { llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None); // `-scan-dependencies` invocations use a single new instance // of a module cache - ModuleDependenciesCache cache; + GlobalModuleDependenciesCache singleUseGlobalCache; + ModuleDependenciesCache cache(singleUseGlobalCache); if (out.has_error() || EC) { Context.Diags.diagnose(SourceLoc(), diag::error_opening_output, path, EC.message()); @@ -1160,7 +1236,8 @@ bool swift::dependencies::batchScanDependencies( CompilerInstance &instance, llvm::StringRef batchInputFile) { // The primary cache used for scans carried out with the compiler instance // we have created - ModuleDependenciesCache cache; + GlobalModuleDependenciesCache singleUseGlobalCache; + ModuleDependenciesCache cache(singleUseGlobalCache); (void)instance.getMainModule(); llvm::BumpPtrAllocator alloc; llvm::StringSaver saver(alloc); @@ -1192,7 +1269,8 @@ bool swift::dependencies::batchPrescanDependencies( CompilerInstance &instance, llvm::StringRef batchInputFile) { // The primary cache used for scans carried out with the compiler instance // we have created - ModuleDependenciesCache cache; + GlobalModuleDependenciesCache singleUseGlobalCache; + ModuleDependenciesCache cache(singleUseGlobalCache); (void)instance.getMainModule(); llvm::BumpPtrAllocator alloc; llvm::StringSaver saver(alloc); @@ -1226,24 +1304,39 @@ swift::dependencies::performModuleScan(CompilerInstance &instance, ModuleDecl *mainModule = instance.getMainModule(); // First, identify the dependencies of the main module auto mainDependencies = identifyMainModuleDependencies(instance); + auto &ctx = instance.getASTContext(); + auto currentImportPathSet = ctx.getAllModuleSearchPathsSet(); + // Add the main module. StringRef mainModuleName = mainModule->getNameStr(); llvm::SetVector, - std::set> - allModules; + std::set> allModules; allModules.insert({mainModuleName.str(), mainDependencies.getKind()}); - cache.recordDependencies(mainModuleName, std::move(mainDependencies)); - auto &ctx = instance.getASTContext(); + + // We may be re-using an instance of the cache which already contains + // an entry for this module. + if (cache.findDependencies( + mainModuleName, + {ModuleDependenciesKind::SwiftTextual, currentImportPathSet})) { + cache.updateDependencies( + std::make_pair(mainModuleName.str(), + ModuleDependenciesKind::SwiftTextual), + std::move(mainDependencies)); + } else { + cache.recordDependencies(mainModuleName, std::move(mainDependencies)); + } + auto ModuleCachePath = getModuleCachePathFromClang( ctx.getClangModuleLoader()->getClangInstance()); auto &FEOpts = instance.getInvocation().getFrontendOptions(); ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); InterfaceSubContextDelegateImpl ASTDelegate( - ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, + ctx.SourceMgr, &ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, ctx.ClangImporterOpts, LoaderOpts, /*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath, FEOpts.PrebuiltModuleCachePath, + FEOpts.BackupModuleInterfaceDir, FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), RequireOSSAModules_t(instance.getSILOptions())); @@ -1261,18 +1354,22 @@ swift::dependencies::performModuleScan(CompilerInstance &instance, discoverCrosssImportOverlayDependencies( instance, mainModuleName, /*All transitive dependencies*/ allModules.getArrayRef().slice(1), cache, - ASTDelegate, [&](ModuleDependencyID id) { allModules.insert(id); }); + ASTDelegate, [&](ModuleDependencyID id) { allModules.insert(id); }, + currentImportPathSet); // Dignose cycle in dependency graph. if (diagnoseCycle(instance, cache, /*MainModule*/ allModules.front(), ASTDelegate)) return std::make_error_code(std::errc::not_supported); + auto dependencyGraph = generateFullDependencyGraph( - instance, cache, ASTDelegate, allModules.getArrayRef()); + instance, cache, ASTDelegate, + allModules.getArrayRef(), currentImportPathSet); // Update the dependency tracker. if (auto depTracker = instance.getDependencyTracker()) { for (auto module : allModules) { - auto deps = cache.findDependencies(module.first, module.second); + auto deps = cache.findDependencies(module.first, + {module.second, currentImportPathSet}); if (!deps) continue; @@ -1323,6 +1420,7 @@ swift::dependencies::performBatchModuleScan( StringRef moduleName = entry.moduleName; bool isClang = !entry.isSwift; ASTContext &ctx = instance.getASTContext(); + auto currentImportPathSet = ctx.getAllModuleSearchPathsSet(); auto &FEOpts = instance.getInvocation().getFrontendOptions(); ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); auto ModuleCachePath = getModuleCachePathFromClang( @@ -1332,10 +1430,11 @@ swift::dependencies::performBatchModuleScan( std::set> allModules; InterfaceSubContextDelegateImpl ASTDelegate( - ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, + ctx.SourceMgr, &ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, ctx.ClangImporterOpts, LoaderOpts, /*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath, FEOpts.PrebuiltModuleCachePath, + FEOpts.BackupModuleInterfaceDir, FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), RequireOSSAModules_t(instance.getSILOptions())); @@ -1372,7 +1471,8 @@ swift::dependencies::performBatchModuleScan( } batchScanResult.push_back(generateFullDependencyGraph( - instance, cache, ASTDelegate, allModules.getArrayRef())); + instance, cache, ASTDelegate, + allModules.getArrayRef(), currentImportPathSet)); }); return batchScanResult; @@ -1400,10 +1500,11 @@ swift::dependencies::performBatchModulePrescan( std::set> allModules; InterfaceSubContextDelegateImpl ASTDelegate( - ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, + ctx.SourceMgr, &ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, ctx.ClangImporterOpts, LoaderOpts, /*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath, FEOpts.PrebuiltModuleCachePath, + FEOpts.BackupModuleInterfaceDir, FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), RequireOSSAModules_t(instance.getSILOptions())); diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index 504eacfc8add4..8c453ee6e63e1 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + set(swiftDriver_sources Action.cpp @@ -25,3 +25,5 @@ target_link_libraries(swiftDriver PRIVATE swiftAST swiftBasic swiftOption) + +set_swift_llvm_is_available(swiftDriver) diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index a0ee603a9f3ef..8fcef84d3d358 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -254,9 +254,12 @@ toolchains::Darwin::addLinkerInputArgs(InvocationInfo &II, } else { addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, file_types::TY_Object); + addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, + file_types::TY_TBD); addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, file_types::TY_LLVM_BC); addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); + addInputsOfType(Arguments, context.InputActions, file_types::TY_TBD); addInputsOfType(Arguments, context.InputActions, file_types::TY_LLVM_BC); } @@ -318,10 +321,29 @@ toolchains::Darwin::addArgsToLinkARCLite(ArgStringList &Arguments, void toolchains::Darwin::addLTOLibArgs(ArgStringList &Arguments, const JobContext &context) const { - llvm::SmallString<128> LTOLibPath; - if (findXcodeClangLibPath("libLTO.dylib", LTOLibPath)) { + if (!context.OI.LibLTOPath.empty()) { + // Check for user-specified LTO library. Arguments.push_back("-lto_library"); - Arguments.push_back(context.Args.MakeArgString(LTOLibPath)); + Arguments.push_back(context.Args.MakeArgString(context.OI.LibLTOPath)); + } else { + // Check for relative libLTO.dylib. This would be the expected behavior in an + // Xcode toolchain. + StringRef P = llvm::sys::path::parent_path(getDriver().getSwiftProgramPath()); + llvm::SmallString<128> LibLTOPath(P); + llvm::sys::path::remove_filename(LibLTOPath); // Remove '/bin' + llvm::sys::path::append(LibLTOPath, "lib"); + llvm::sys::path::append(LibLTOPath, "libLTO.dylib"); + if (llvm::sys::fs::exists(LibLTOPath)) { + Arguments.push_back("-lto_library"); + Arguments.push_back(context.Args.MakeArgString(LibLTOPath)); + } else { + // Use libLTO.dylib from the default toolchain if a relative one does not exist. + llvm::SmallString<128> LibLTOPath; + if (findXcodeClangLibPath("libLTO.dylib", LibLTOPath)) { + Arguments.push_back("-lto_library"); + Arguments.push_back(context.Args.MakeArgString(LibLTOPath)); + } + } } } @@ -387,6 +409,8 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments, runtimeCompatibilityVersion = llvm::VersionTuple(5, 0); } else if (value.equals("5.1")) { runtimeCompatibilityVersion = llvm::VersionTuple(5, 1); + } else if (value.equals("5.5")) { + runtimeCompatibilityVersion = llvm::VersionTuple(5, 5); } else if (value.equals("none")) { runtimeCompatibilityVersion = None; } else { @@ -774,7 +798,9 @@ toolchains::Darwin::constructInvocation(const DynamicLinkJobAction &job, Arguments.push_back("-no_objc_category_merging"); // These custom arguments should be right before the object file at the end. - context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); + context.Args.AddAllArgsExcept(Arguments, {options::OPT_linker_option_Group}, + {options::OPT_l}); + ToolChain::addLinkedLibArgs(context.Args, Arguments); context.Args.AddAllArgValues(Arguments, options::OPT_Xlinker); // This should be the last option, for convenience in checking output. diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index ffbd4d2743c03..6da917a50c52d 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -167,17 +167,49 @@ static void validateProfilingArgs(DiagnosticEngine &diags, static void validateDependencyScanningArgs(DiagnosticEngine &diags, const ArgList &args) { - const Arg *ExternalDependencyMap = args.getLastArg(options::OPT_placeholder_dependency_module_map); + const Arg *ExternalDependencyMap = + args.getLastArg(options::OPT_placeholder_dependency_module_map); const Arg *ScanDependencies = args.getLastArg(options::OPT_scan_dependencies); const Arg *Prescan = args.getLastArg(options::OPT_import_prescan); + + const Arg *SerializeCache = + args.getLastArg(options::OPT_serialize_dependency_scan_cache); + const Arg *ReuseCache = + args.getLastArg(options::OPT_reuse_dependency_scan_cache); + const Arg *CacheSerializationPath = + args.getLastArg(options::OPT_dependency_scan_cache_path); + if (ExternalDependencyMap && !ScanDependencies) { diags.diagnose(SourceLoc(), diag::error_requirement_not_met, - "-placeholder-dependency-module-map-file", "-scan-dependencies"); + "-placeholder-dependency-module-map-file", + "-scan-dependencies"); } if (Prescan && !ScanDependencies) { diags.diagnose(SourceLoc(), diag::error_requirement_not_met, "-import-prescan", "-scan-dependencies"); } + if (Prescan && !ScanDependencies) { + diags.diagnose(SourceLoc(), diag::error_requirement_not_met, + "-import-prescan", "-scan-dependencies"); + } + if (SerializeCache && !ScanDependencies) { + diags.diagnose(SourceLoc(), diag::error_requirement_not_met, + "-serialize-dependency-scan-cache", "-scan-dependencies"); + } + if (ReuseCache && !ScanDependencies) { + diags.diagnose(SourceLoc(), diag::error_requirement_not_met, + "-load-dependency-scan-cache", "-scan-dependencies"); + } + if (SerializeCache && !CacheSerializationPath) { + diags.diagnose(SourceLoc(), diag::error_requirement_not_met, + "-serialize-dependency-scan-cache", + "-dependency-scan-cache-path"); + } + if (ReuseCache && !CacheSerializationPath) { + diags.diagnose(SourceLoc(), diag::error_requirement_not_met, + "-serialize-dependency-scan-cache", + "-dependency-scan-cache-path"); + } } static void validateDebugInfoArgs(DiagnosticEngine &diags, @@ -251,27 +283,6 @@ static void validateSearchPathArgs(DiagnosticEngine &diags, } } -static void validateAutolinkingArgs(DiagnosticEngine &diags, - const ArgList &args, - const llvm::Triple &T) { - auto *forceLoadArg = args.getLastArg(options::OPT_autolink_force_load); - if (!forceLoadArg) - return; - auto *incrementalArg = args.getLastArg(options::OPT_incremental); - if (!incrementalArg) - return; - - if (T.supportsCOMDAT()) - return; - - // Note: -incremental can itself be overridden by other arguments later - // on, but since -autolink-force-load is a rare and not-really-recommended - // option it's not worth modeling that complexity here (or moving the - // check somewhere else). - diags.diagnose(SourceLoc(), diag::error_option_not_supported, - forceLoadArg->getSpelling(), incrementalArg->getSpelling()); -} - /// Perform miscellaneous early validation of arguments. static void validateArgs(DiagnosticEngine &diags, const ArgList &args, const llvm::Triple &T) { @@ -282,7 +293,6 @@ static void validateArgs(DiagnosticEngine &diags, const ArgList &args, validateDebugInfoArgs(diags, args); validateCompilationConditionArgs(diags, args); validateSearchPathArgs(diags, args); - validateAutolinkingArgs(diags, args, T); validateVerifyIncrementalDependencyArgs(diags, args); } @@ -860,7 +870,8 @@ getDriverBatchSizeLimit(llvm::opt::InputArgList &ArgList, std::unique_ptr Driver::buildCompilation(const ToolChain &TC, - std::unique_ptr ArgList) { + std::unique_ptr ArgList, + bool AllowErrors) { llvm::PrettyStackTraceString CrashInfo("Compilation construction"); llvm::sys::TimePoint<> StartTime = std::chrono::system_clock::now(); @@ -880,7 +891,7 @@ Driver::buildCompilation(const ToolChain &TC, // Perform toolchain specific args validation. TC.validateArguments(Diags, *TranslatedArgList, DefaultTargetTriple); - if (Diags.hadAnyError()) + if (Diags.hadAnyError() && !AllowErrors) return nullptr; if (!handleImmediateArgs(*TranslatedArgList, TC)) { @@ -891,7 +902,7 @@ Driver::buildCompilation(const ToolChain &TC, InputFileList Inputs; buildInputs(TC, *TranslatedArgList, Inputs); - if (Diags.hadAnyError()) + if (Diags.hadAnyError() && !AllowErrors) return nullptr; // Determine the OutputInfo for the driver. @@ -900,14 +911,14 @@ Driver::buildCompilation(const ToolChain &TC, OI.CompilerMode = computeCompilerMode(*TranslatedArgList, Inputs, BatchMode); buildOutputInfo(TC, *TranslatedArgList, BatchMode, Inputs, OI); - if (Diags.hadAnyError()) + if (Diags.hadAnyError() && !AllowErrors) return nullptr; assert(OI.CompilerOutputType != file_types::ID::TY_INVALID && "buildOutputInfo() must set a valid output type!"); TC.validateOutputInfo(Diags, OI); - if (Diags.hadAnyError()) + if (Diags.hadAnyError() && !AllowErrors) return nullptr; validateEmbedBitcode(*TranslatedArgList, OI, Diags); @@ -919,7 +930,7 @@ Driver::buildCompilation(const ToolChain &TC, Optional OFM = buildOutputFileMap( *TranslatedArgList, workingDirectory); - if (Diags.hadAnyError()) + if (Diags.hadAnyError() && !AllowErrors) return nullptr; if (ArgList->hasArg(options::OPT_driver_print_output_file_map)) { @@ -927,7 +938,9 @@ Driver::buildCompilation(const ToolChain &TC, OFM->dump(llvm::errs(), true); else Diags.diagnose(SourceLoc(), diag::error_no_output_file_map_specified); - return nullptr; + if (!AllowErrors) { + return nullptr; + } } const bool ShowIncrementalBuildDecisions = @@ -1044,7 +1057,7 @@ Driver::buildCompilation(const ToolChain &TC, buildActions(TopLevelActions, TC, OI, whyIgnoreIncrementallity.empty() ? &outOfDateMap : nullptr, *C); - if (Diags.hadAnyError()) + if (Diags.hadAnyError() && !AllowErrors) return nullptr; if (DriverPrintActions) { @@ -1076,7 +1089,7 @@ Driver::buildCompilation(const ToolChain &TC, if (!whyIgnoreIncrementallity.empty()) C->disableIncrementalBuild(whyIgnoreIncrementallity); - if (Diags.hadAnyError()) + if (Diags.hadAnyError() && !AllowErrors) return nullptr; if (DriverPrintBindings) @@ -1428,6 +1441,12 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, A->getAsString(Args), A->getValue()); } + + if (const Arg *A = Args.getLastArg(options::OPT_lto_library)) { + OI.LibLTOPath = A->getValue(); + } else { + OI.LibLTOPath = ""; + } auto CompilerOutputType = OI.LTOVariant != OutputInfo::LTOKind::None ? file_types::TY_LLVM_BC @@ -1482,6 +1501,8 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, case options::OPT_emit_object: OI.CompilerOutputType = file_types::TY_Object; + if (OI.LTOVariant != OutputInfo::LTOKind::None) + OI.CompilerOutputType = file_types::TY_LLVM_BC; break; case options::OPT_emit_assembly: @@ -2222,13 +2243,25 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, } TopLevelActions.push_back(LinkAction); - if (TC.getTriple().isOSDarwin() && - OI.DebugInfoLevel > IRGenDebugInfoLevel::None) { - auto *dSYMAction = C.createAction(LinkAction); - TopLevelActions.push_back(dSYMAction); - if (Args.hasArg(options::OPT_verify_debug_info)) { - TopLevelActions.push_back( - C.createAction(dSYMAction)); + if (TC.getTriple().isOSDarwin()) { + switch (OI.LinkAction) { + case LinkKind::Executable: + case LinkKind::DynamicLibrary: + if (OI.DebugInfoLevel > IRGenDebugInfoLevel::None) { + auto *dSYMAction = C.createAction(LinkAction); + TopLevelActions.push_back(dSYMAction); + if (Args.hasArg(options::OPT_verify_debug_info)) { + TopLevelActions.push_back( + C.createAction(dSYMAction)); + } + } + break; + + case LinkKind::None: + LLVM_FALLTHROUGH; + case LinkKind::StaticLibrary: + // Cannot build the DSYM bundle for non-image targets. + break; } } } else { diff --git a/lib/Driver/FrontendUtil.cpp b/lib/Driver/FrontendUtil.cpp index 9be9def90fc2f..df74a502e38d7 100644 --- a/lib/Driver/FrontendUtil.cpp +++ b/lib/Driver/FrontendUtil.cpp @@ -101,7 +101,7 @@ bool swift::driver::getSingleFrontendInvocationFromDriverArguments( return true; std::unique_ptr C = - TheDriver.buildCompilation(*TC, std::move(ArgList)); + TheDriver.buildCompilation(*TC, std::move(ArgList), /*AllowErrors=*/true); if (!C || C->getJobs().empty()) return true; // Don't emit an error; one should already have been emitted @@ -113,7 +113,6 @@ bool swift::driver::getSingleFrontendInvocationFromDriverArguments( if (CompileCommands.size() != 1) { // TODO: include Jobs in the diagnostic. Diags.diagnose(SourceLoc(), diag::error_expected_one_frontend_job); - return true; } const Job *Cmd = *CompileCommands.begin(); diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index 522a9727c454a..525af62dd2b56 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -287,6 +287,15 @@ mergeBatchInputs(ArrayRef jobs, return false; } +void ToolChain::addLinkedLibArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &FrontendArgs) { + Args.getLastArg(options::OPT_l); + for (auto Arg : Args.getAllArgValues(options::OPT_l)) { + const std::string lArg("-l" + Arg); + FrontendArgs.push_back(Args.MakeArgString(Twine(lArg))); + } +} + /// Construct a \c BatchJob by merging the constituent \p jobs' CommandOutput, /// input \c Job and \c Action members. Call through to \c constructInvocation /// on \p BatchJob, to build the \c InvocationInfo. diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 80a81f37617c1..98b6525f9def2 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -142,6 +142,7 @@ static void addLTOArgs(const OutputInfo &OI, ArgStringList &arguments) { } } + void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, const CommandOutput &output, const ArgList &inputArgs, @@ -175,6 +176,10 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, arguments.push_back("-aarch64-use-tbi"); } + if (output.getPrimaryOutputType() == file_types::TY_SwiftModuleFile) { + arguments.push_back("-warn-on-potentially-unavailable-enum-case"); + } + // Enable or disable ObjC interop appropriately for the platform if (Triple.isOSDarwin()) { arguments.push_back("-enable-objc-interop"); @@ -278,13 +283,10 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_diagnostic_style); inputArgs.AddLastArg(arguments, options::OPT_enable_experimental_concise_pound_file); - inputArgs.AddLastArg( - arguments, - options::OPT_enable_fuzzy_forward_scan_trailing_closure_matching, - options::OPT_disable_fuzzy_forward_scan_trailing_closure_matching); inputArgs.AddLastArg(arguments, options::OPT_verify_incremental_dependencies); inputArgs.AddLastArg(arguments, options::OPT_access_notes_path); + inputArgs.AddLastArg(arguments, options::OPT_library_level); // Pass on any build config options inputArgs.AddAllArgs(arguments, options::OPT_D); @@ -556,7 +558,7 @@ ToolChain::constructInvocation(const CompileJobAction &job, options::OPT_disable_autolinking_runtime_compatibility)) { Arguments.push_back("-disable-autolinking-runtime-compatibility"); } - + if (auto arg = context.Args.getLastArg( options::OPT_runtime_compatibility_version)) { Arguments.push_back("-runtime-compatibility-version"); @@ -577,11 +579,15 @@ ToolChain::constructInvocation(const CompileJobAction &job, Arguments, options:: OPT_disable_autolinking_runtime_compatibility_dynamic_replacements); + context.Args.AddLastArg( + Arguments, + options::OPT_disable_autolinking_runtime_compatibility_concurrency); if (context.OI.CompilerMode == OutputInfo::Mode::SingleCompile) { context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph); context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph_dir); } + context.Args.AddLastArg(Arguments, options::OPT_include_spi_symbols); return II; } @@ -837,7 +843,8 @@ ToolChain::constructInvocation(const InterpretJobAction &job, Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); - context.Args.AddAllArgs(Arguments, options::OPT_l, options::OPT_framework); + context.Args.AddAllArgs(Arguments, options::OPT_framework); + ToolChain::addLinkedLibArgs(context.Args, Arguments); // The immediate arguments must be last. context.Args.AddLastArg(Arguments, options::OPT__DASH_DASH); @@ -1071,6 +1078,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph); context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph_dir); + context.Args.AddLastArg(Arguments, options::OPT_include_spi_symbols); context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); @@ -1184,8 +1192,8 @@ ToolChain::constructInvocation(const REPLJobAction &job, addRuntimeLibraryFlags(context.OI, FrontendArgs); context.Args.AddLastArg(FrontendArgs, options::OPT_import_objc_header); - context.Args.AddAllArgs(FrontendArgs, options::OPT_l, options::OPT_framework, - options::OPT_L); + context.Args.AddAllArgs(FrontendArgs, options::OPT_framework, options::OPT_L); + ToolChain::addLinkedLibArgs(context.Args, FrontendArgs); if (!useLLDB) { FrontendArgs.insert(FrontendArgs.begin(), {"-frontend", "-repl"}); diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp index 30905f6b72253..1dc32acfd8357 100644 --- a/lib/Driver/UnixToolChains.cpp +++ b/lib/Driver/UnixToolChains.cpp @@ -358,7 +358,9 @@ toolchains::GenericUnix::constructInvocation(const DynamicLinkJobAction &job, } // These custom arguments should be right before the object file at the end. - context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); + context.Args.AddAllArgsExcept(Arguments, {options::OPT_linker_option_Group}, + {options::OPT_l}); + ToolChain::addLinkedLibArgs(context.Args, Arguments); context.Args.AddAllArgs(Arguments, options::OPT_Xlinker); context.Args.AddAllArgValues(Arguments, options::OPT_Xclang_linker); diff --git a/lib/Driver/WindowsToolChains.cpp b/lib/Driver/WindowsToolChains.cpp index 706d547e8342e..5001fa33fd2a5 100644 --- a/lib/Driver/WindowsToolChains.cpp +++ b/lib/Driver/WindowsToolChains.cpp @@ -172,20 +172,16 @@ toolchains::Windows::constructInvocation(const DynamicLinkJobAction &job, } if (context.Args.hasArg(options::OPT_profile_generate)) { - SmallString<128> LibProfile(SharedResourceDirPath); - llvm::sys::path::remove_filename(LibProfile); // remove platform name - llvm::sys::path::append(LibProfile, "clang", "lib"); - - llvm::sys::path::append(LibProfile, getTriple().getOSName(), - Twine("clang_rt.profile-") + - getTriple().getArchName() + ".lib"); - Arguments.push_back(context.Args.MakeArgString(LibProfile)); + Arguments.push_back(context.Args.MakeArgString("-Xlinker")); Arguments.push_back(context.Args.MakeArgString( - Twine("-u", llvm::getInstrProfRuntimeHookVarName()))); + Twine({"-include:", llvm::getInstrProfRuntimeHookVarName()}))); + Arguments.push_back(context.Args.MakeArgString("-lclang_rt.profile")); } context.Args.AddAllArgs(Arguments, options::OPT_Xlinker); - context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group); + context.Args.AddAllArgsExcept(Arguments, {options::OPT_linker_option_Group}, + {options::OPT_l}); + ToolChain::addLinkedLibArgs(context.Args, Arguments); context.Args.AddAllArgValues(Arguments, options::OPT_Xclang_linker); // Run clang in verbose mode if "-v" is set diff --git a/lib/DriverTool/CMakeLists.txt b/lib/DriverTool/CMakeLists.txt new file mode 100644 index 0000000000000..869c00fece97f --- /dev/null +++ b/lib/DriverTool/CMakeLists.txt @@ -0,0 +1,29 @@ +set(driver_sources_and_options + driver.cpp + autolink_extract_main.cpp + modulewrap_main.cpp + swift_api_digester_main.cpp + swift_indent_main.cpp + swift_symbolgraph_extract_main.cpp + swift_api_extract_main.cpp) + +set(driver_common_libs + swiftAPIDigester + swiftDriver + swiftFrontendTool + swiftSymbolGraphGen + LLVMBitstreamReader) + +add_swift_host_library(swiftDriverTool STATIC + ${driver_sources_and_options} +) +target_link_libraries(swiftDriverTool + PUBLIC + ${driver_common_libs}) + +# If building as part of clang, make sure the headers are installed. +if(NOT SWIFT_BUILT_STANDALONE) + add_dependencies(swiftDriverTool clang-resource-headers) +endif() + +set_swift_llvm_is_available(swiftDriverTool) diff --git a/tools/driver/autolink_extract_main.cpp b/lib/DriverTool/autolink_extract_main.cpp similarity index 100% rename from tools/driver/autolink_extract_main.cpp rename to lib/DriverTool/autolink_extract_main.cpp diff --git a/lib/DriverTool/driver.cpp b/lib/DriverTool/driver.cpp new file mode 100644 index 0000000000000..825946e161d02 --- /dev/null +++ b/lib/DriverTool/driver.cpp @@ -0,0 +1,404 @@ +//===--- driver.cpp - Swift Compiler Driver -------------------------------===// +// +// 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 is the entry point to the swift compiler driver. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsDriver.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/Basic/InitializeLibSwift.h" +#include "swift/Basic/PrettyStackTrace.h" +#include "swift/Basic/Program.h" +#include "swift/Basic/TaskQueue.h" +#include "swift/Basic/SourceManager.h" +#include "swift/Driver/Compilation.h" +#include "swift/Driver/Driver.h" +#include "swift/Driver/FrontendUtil.h" +#include "swift/Driver/Job.h" +#include "swift/Driver/ToolChain.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/FrontendTool/FrontendTool.h" +#include "swift/DriverTool/DriverTool.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +#if defined(_WIN32) +#include +#endif + +using namespace swift; +using namespace swift::driver; + +std::string getExecutablePath(const char *FirstArg) { + void *P = (void *)(intptr_t)getExecutablePath; + return llvm::sys::fs::getMainExecutable(FirstArg, P); +} + +/// Run 'swift-autolink-extract'. +extern int autolink_extract_main(ArrayRef Args, const char *Argv0, + void *MainAddr); + +extern int modulewrap_main(ArrayRef Args, const char *Argv0, + void *MainAddr); + +/// Run 'swift-indent' +extern int swift_indent_main(ArrayRef Args, const char *Argv0, + void *MainAddr); + +/// Run 'swift-symbolgraph-extract' +extern int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, +void *MainAddr); + +/// Run 'swift-api-digester' +extern int swift_api_digester_main(ArrayRef Args, + const char *Argv0, void *MainAddr); + +/// Run 'swift-api-extract' +extern int swift_api_extract_main(ArrayRef Args, + const char *Argv0, void *MainAddr); + +/// Determine if the given invocation should run as a "subcommand". +/// +/// Examples of "subcommands" are 'swift build' or 'swift test', which are +/// usually used to invoke the Swift package manager executables 'swift-build' +/// and 'swift-test', respectively. +/// +/// \param ExecName The name of the argv[0] we were invoked as. +/// \param SubcommandName On success, the full name of the subcommand to invoke. +/// \param Args On return, the adjusted program arguments to use. +/// \returns True if running as a subcommand. +static bool shouldRunAsSubcommand(StringRef ExecName, + SmallString<256> &SubcommandName, + const ArrayRef Args, + bool &isRepl) { + assert(!Args.empty()); + + // If we are not run as 'swift', don't do anything special. This doesn't work + // with symlinks with alternate names, but we can't detect 'swift' vs 'swiftc' + // if we try and resolve using the actual executable path. + if (ExecName != "swift") + return false; + + // If there are no program arguments, always invoke as normal. + if (Args.size() == 1) + return false; + + // Otherwise, we have a program argument. If it looks like an option or a + // path, then invoke in interactive mode with the arguments as given. + StringRef FirstArg(Args[1]); + if (FirstArg.startswith("-") || FirstArg.contains('.') || + FirstArg.contains('/')) + return false; + + // Otherwise, we should have some sort of subcommand. Get the subcommand name + // and remove it from the program arguments. + StringRef Subcommand = Args[1]; + + // If the subcommand is the "built-in" 'repl', then use the + // normal driver. + if (Subcommand == "repl") { + isRepl = true; + return false; + } + + // Form the subcommand name. + SubcommandName.assign("swift-"); + SubcommandName.append(Subcommand); + + return true; +} + +static bool shouldDisallowNewDriver(DiagnosticEngine &diags, + StringRef ExecName, + const ArrayRef argv) { + // We are not invoking the driver, so don't forward. + if (ExecName != "swift" && ExecName != "swiftc") { + return true; + } + StringRef disableArg = "-disallow-use-new-driver"; + StringRef disableEnv = "SWIFT_USE_OLD_DRIVER"; + auto shouldWarn = !llvm::sys::Process:: + GetEnv("SWIFT_AVOID_WARNING_USING_OLD_DRIVER").hasValue(); + // If user specified using the old driver, don't forward. + if (llvm::find_if(argv, [&](const char* arg) { + return StringRef(arg) == disableArg; + }) != argv.end()) { + if (shouldWarn) + diags.diagnose(SourceLoc(), diag::old_driver_deprecated, disableArg); + return true; + } + if (llvm::sys::Process::GetEnv(disableEnv).hasValue()) { + if (shouldWarn) + diags.diagnose(SourceLoc(), diag::old_driver_deprecated, disableEnv); + return true; + } + return false; +} + +static bool appendSwiftDriverName(SmallString<256> &buffer) { + assert(llvm::sys::fs::exists(buffer)); + if (auto driverNameOp = llvm::sys::Process::GetEnv("SWIFT_USE_NEW_DRIVER")) { + llvm::sys::path::append(buffer, *driverNameOp); + return true; + } + + llvm::sys::path::append(buffer, "swift-driver"); + if (llvm::sys::fs::exists(buffer)) { + return true; + } + llvm::sys::path::remove_filename(buffer); + llvm::sys::path::append(buffer, "swift-driver-new"); + if (llvm::sys::fs::exists(buffer)) { + return true; + } + + return false; +} + +static int run_driver(StringRef ExecName, + const ArrayRef argv) { + // This is done here and not done in FrontendTool.cpp, because + // FrontendTool.cpp is linked to tools, which don't use libswift. + initializeLibSwift(); + + // Handle integrated tools. + if (argv.size() > 1) { + StringRef FirstArg(argv[1]); + if (FirstArg == "-frontend") { + return performFrontend(llvm::makeArrayRef(argv.data()+2, + argv.data()+argv.size()), + argv[0], (void *)(intptr_t)getExecutablePath); + } + if (FirstArg == "-modulewrap") { + return modulewrap_main(llvm::makeArrayRef(argv.data()+2, + argv.data()+argv.size()), + argv[0], (void *)(intptr_t)getExecutablePath); + } + + // Run the integrated Swift frontend when called as "swift-frontend" but + // without a leading "-frontend". + if (!FirstArg.startswith("--driver-mode=") + && ExecName == "swift-frontend") { + return performFrontend(llvm::makeArrayRef(argv.data()+1, + argv.data()+argv.size()), + argv[0], (void *)(intptr_t)getExecutablePath); + } + } + + std::string Path = getExecutablePath(argv[0]); + + PrintingDiagnosticConsumer PDC; + + SourceManager SM; + DiagnosticEngine Diags(SM); + Diags.addConsumer(PDC); + + // Forwarding calls to the swift driver if the C++ driver is invoked as `swift` + // or `swiftc`, and an environment variable SWIFT_USE_NEW_DRIVER is defined. + if (!shouldDisallowNewDriver(Diags, ExecName, argv)) { + SmallString<256> NewDriverPath(llvm::sys::path::parent_path(Path)); + if (appendSwiftDriverName(NewDriverPath) && + llvm::sys::fs::exists(NewDriverPath)) { + std::vector subCommandArgs; + // Rewrite the program argument. + subCommandArgs.push_back(NewDriverPath.c_str()); + if (ExecName == "swiftc") { + subCommandArgs.push_back("--driver-mode=swiftc"); + } else { + assert(ExecName == "swift"); + subCommandArgs.push_back("--driver-mode=swift"); + } + // Push these non-op frontend arguments so the build log can indicate + // the new driver is used. + subCommandArgs.push_back("-Xfrontend"); + subCommandArgs.push_back("-new-driver-path"); + subCommandArgs.push_back("-Xfrontend"); + subCommandArgs.push_back(NewDriverPath.c_str()); + + // Push on the source program arguments + subCommandArgs.insert(subCommandArgs.end(), argv.begin() + 1, argv.end()); + + // Execute the subcommand. + subCommandArgs.push_back(nullptr); + ExecuteInPlace(NewDriverPath.c_str(), subCommandArgs.data()); + + // If we reach here then an error occurred (typically a missing path). + std::string ErrorString = llvm::sys::StrError(); + llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0] + << " (" << ErrorString << ")\n"; + return 2; + } + } + + Driver TheDriver(Path, ExecName, argv, Diags); + switch (TheDriver.getDriverKind()) { + case Driver::DriverKind::AutolinkExtract: + return autolink_extract_main( + TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), + argv[0], (void *)(intptr_t)getExecutablePath); + case Driver::DriverKind::SwiftIndent: + return swift_indent_main( + TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), + argv[0], (void *)(intptr_t)getExecutablePath); + case Driver::DriverKind::SymbolGraph: + return swift_symbolgraph_extract_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); + case Driver::DriverKind::APIExtract: + return swift_api_extract_main( + TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], + (void *)(intptr_t)getExecutablePath); + case Driver::DriverKind::APIDigester: + return swift_api_digester_main( + TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], + (void *)(intptr_t)getExecutablePath); + default: + break; + } + + std::unique_ptr ArgList = + TheDriver.parseArgStrings(ArrayRef(argv).slice(1)); + if (Diags.hadAnyError()) + return 1; + + std::unique_ptr TC = TheDriver.buildToolChain(*ArgList); + if (Diags.hadAnyError()) + return 1; + + for (auto arg: ArgList->getArgs()) { + if (arg->getOption().hasFlag(options::NewDriverOnlyOption)) { + Diags.diagnose(SourceLoc(), diag::warning_unsupported_driver_option, + arg->getSpelling()); + } + } + + std::unique_ptr C = + TheDriver.buildCompilation(*TC, std::move(ArgList)); + + if (Diags.hadAnyError()) + return 1; + + if (C) { + std::unique_ptr TQ = TheDriver.buildTaskQueue(*C); + if (!TQ) + return 1; + return C->performJobs(std::move(TQ)).exitCode; + } + + return 0; +} + +int swift::mainEntry(int argc_, const char **argv_) { +#if defined(_WIN32) + LPWSTR *wargv_ = CommandLineToArgvW(GetCommandLineW(), &argc_); + std::vector utf8Args; + // We use UTF-8 as the internal character encoding. On Windows, + // arguments passed to wmain are encoded in UTF-16 + for (int i = 0; i < argc_; i++) { + const wchar_t *wideArg = wargv_[i]; + int wideArgLen = std::wcslen(wideArg); + utf8Args.push_back(""); + llvm::ArrayRef uRef((const char *)wideArg, + (const char *)(wideArg + wideArgLen)); + llvm::convertUTF16ToUTF8String(uRef, utf8Args[i]); + } + + std::vector utf8CStrs; + llvm::transform(utf8Args, std::back_inserter(utf8CStrs), + std::mem_fn(&std::string::c_str)); + argv_ = utf8CStrs.data(); +#endif + // Expand any response files in the command line argument vector - arguments + // may be passed through response files in the event of command line length + // restrictions. + SmallVector ExpandedArgs(&argv_[0], &argv_[argc_]); + llvm::BumpPtrAllocator Allocator; + llvm::StringSaver Saver(Allocator); + swift::driver::ExpandResponseFilesWithRetry(Saver, ExpandedArgs); + + // Initialize the stack trace using the parsed argument vector with expanded + // response files. + + // PROGRAM_START/InitLLVM overwrites the passed in arguments with UTF-8 + // versions of them on Windows. This also has the effect of overwriting the + // response file expansion. Since we handle the UTF-8 conversion above, we + // pass in a copy and throw away the modifications. + int ThrowawayExpandedArgc = ExpandedArgs.size(); + const char **ThrowawayExpandedArgv = ExpandedArgs.data(); + PROGRAM_START(ThrowawayExpandedArgc, ThrowawayExpandedArgv); + ArrayRef argv(ExpandedArgs); + + PrettyStackTraceSwiftVersion versionStackTrace; + + // Check if this invocation should execute a subcommand. + StringRef ExecName = llvm::sys::path::stem(argv[0]); + SmallString<256> SubcommandName; + bool isRepl = false; + if (shouldRunAsSubcommand(ExecName, SubcommandName, argv, isRepl)) { + // Preserve argv for the stack trace. + SmallVector subCommandArgs(argv.begin(), argv.end()); + subCommandArgs.erase(&subCommandArgs[1]); + // We are running as a subcommand, try to find the subcommand adjacent to + // the executable we are running as. + SmallString<256> SubcommandPath( + llvm::sys::path::parent_path(getExecutablePath(argv[0]))); + llvm::sys::path::append(SubcommandPath, SubcommandName); + + // If we didn't find the tool there, let the OS search for it. + if (!llvm::sys::fs::exists(SubcommandPath)) { + // Search for the program and use the path if found. If there was an + // error, ignore it and just let the exec fail. + auto result = llvm::sys::findProgramByName(SubcommandName); + if (!result.getError()) + SubcommandPath = *result; + } + + // Rewrite the program argument. + subCommandArgs[0] = SubcommandPath.c_str(); + + // Execute the subcommand. + subCommandArgs.push_back(nullptr); + ExecuteInPlace(SubcommandPath.c_str(), subCommandArgs.data()); + + // If we reach here then an error occurred (typically a missing path). + std::string ErrorString = llvm::sys::StrError(); + llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0] + << " (" << ErrorString << ")\n"; + return 2; + } + + if (isRepl) { + // Preserve argv for the stack trace. + SmallVector replArgs(argv.begin(), argv.end()); + replArgs.erase(&replArgs[1]); + return run_driver(ExecName, replArgs); + } else { + return run_driver(ExecName, argv); + } +} diff --git a/tools/driver/modulewrap_main.cpp b/lib/DriverTool/modulewrap_main.cpp similarity index 97% rename from tools/driver/modulewrap_main.cpp rename to lib/DriverTool/modulewrap_main.cpp index 3ef145ad07e3a..9166e066974b5 100644 --- a/tools/driver/modulewrap_main.cpp +++ b/lib/DriverTool/modulewrap_main.cpp @@ -26,6 +26,7 @@ #include "swift/SIL/SILModule.h" #include "swift/SIL/TypeLowering.h" #include "swift/Subsystems.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Option/ArgList.h" @@ -177,9 +178,10 @@ int modulewrap_main(ArrayRef Args, const char *Argv0, TypeCheckerOptions TypeCheckOpts; LangOptions LangOpts; ClangImporterOptions ClangImporterOpts; + symbolgraphgen::SymbolGraphOptions SymbolGraphOpts; LangOpts.Target = Invocation.getTargetTriple(); ASTContext &ASTCtx = *ASTContext::get(LangOpts, TypeCheckOpts, SearchPathOpts, - ClangImporterOpts, SrcMgr, + ClangImporterOpts, SymbolGraphOpts, SrcMgr, Instance.getDiags()); registerParseRequestFunctions(ASTCtx.evaluator); registerTypeCheckerRequestFunctions(ASTCtx.evaluator); diff --git a/tools/driver/swift_api_digester_main.cpp b/lib/DriverTool/swift_api_digester_main.cpp similarity index 88% rename from tools/driver/swift_api_digester_main.cpp rename to lib/DriverTool/swift_api_digester_main.cpp index 9567d970cbb0e..60ba731bb5ef9 100644 --- a/tools/driver/swift_api_digester_main.cpp +++ b/lib/DriverTool/swift_api_digester_main.cpp @@ -76,16 +76,6 @@ struct MatchedNodeListener { virtual ~MatchedNodeListener() = default; }; -template -bool contains(std::vector &container, T *instance) { - return std::find(container.begin(), container.end(), instance) != container.end(); -} - -template -bool contains(ArrayRef container, T instance) { - return std::find(container.begin(), container.end(), instance) != container.end(); -} - static void singleMatch(SDKNode* Left, SDKNode *Right, MatchedNodeListener &Listener) { @@ -184,7 +174,7 @@ class RemovedAddedNodeMatcher : public NodeMatcher, public MatchedNodeListener { void handleUnmatch(NodeVector &Matched, NodeVector &All, bool Left) { for (auto A : All) { - if (contains(Matched, A)) + if (llvm::is_contained(Matched, A)) continue; if (Left) Listener.foundMatch(A, nullptr, NodeMatchReason::Removed); @@ -381,12 +371,12 @@ class RemovedAddedNodeMatcher : public NodeMatcher, public MatchedNodeListener { NodeVector RenameRight; for (auto Remain : Removed) { - if (!contains(RemovedMatched, Remain)) + if (!llvm::is_contained(RemovedMatched, Remain)) RenameLeft.push_back(Remain); } for (auto Remain : Added) { - if (!contains(AddedMatched, Remain)) + if (!llvm::is_contained(AddedMatched, Remain)) RenameRight.push_back(Remain); } @@ -488,7 +478,7 @@ void SameNameNodeMatcher::match() { for (auto *RN : Right) { // If RN has matched before, ignore it. - if (contains(MatchedRight, RN)) + if (llvm::is_contained(MatchedRight, RN)) continue; // If LN and RN have the same name for some reason, keep track of RN. @@ -507,7 +497,7 @@ void SameNameNodeMatcher::match() { } } for (auto &R : Right) { - if (!contains(MatchedRight, R)) { + if (!llvm::is_contained(MatchedRight, R)) { Added.push_back(R); } } @@ -550,305 +540,8 @@ class SDKTreeDiffPass { virtual void pass(NodePtr Left, NodePtr Right) = 0; virtual ~SDKTreeDiffPass() {} }; - -static void detectRename(SDKNode *L, SDKNode *R) { - if (L->getKind() == R->getKind() && isa(L) && - L->getPrintedName() != R->getPrintedName()) { - L->annotate(NodeAnnotation::Rename); - L->annotate(NodeAnnotation::RenameOldName, L->getPrintedName()); - L->annotate(NodeAnnotation::RenameNewName, R->getPrintedName()); - } -} - -static bool isOwnershipEquivalent(ReferenceOwnership Left, - ReferenceOwnership Right) { - if (Left == Right) - return true; - if (Left == ReferenceOwnership::Unowned && Right == ReferenceOwnership::Weak) - return true; - if (Left == ReferenceOwnership::Weak && Right == ReferenceOwnership::Unowned) - return true; - return false; -} }// End of anonymous namespace -void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (getDeclKind() != R->getDeclKind()) { - emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind(), - getSDKContext().getOpts().CompilerStyle)); - return; - } - - assert(getDeclKind() == R->getDeclKind()); - auto DKind = getDeclKind(); - switch (DKind) { - case DeclKind::Class: { - auto LSuperClass = getSuperClassName(); - auto RSuperClass = R->getSuperClassName(); - if (!LSuperClass.empty() && LSuperClass != RSuperClass) { - if (RSuperClass.empty()) { - emitDiag(Loc, diag::super_class_removed, LSuperClass); - } else if (!contains(R->getClassInheritanceChain(), LSuperClass)) { - emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); - } - } - - // Check for @_hasMissingDesignatedInitializers and - // @_inheritsConvenienceInitializers changes. - if (isOpen() && R->isOpen()) { - // It's not safe to add new, invisible designated inits to open - // classes. - if (!hasMissingDesignatedInitializers() && - R->hasMissingDesignatedInitializers()) - R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); - } - - // It's not safe to stop inheriting convenience inits, it changes - // the set of initializers that are available. - if (inheritsConvenienceInitializers() && - !R->inheritsConvenienceInitializers()) - R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); - break; - } - default: - break; - } -} - -void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (!isThrowing() && R->isThrowing()) { - emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing")); - } - if (Ctx.checkingABI()) { - if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) { - emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry()); - } - } -} - -void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) { - SDKNodeDeclAbstractFunc::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (getSelfAccessKind() != R->getSelfAccessKind()) { - emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(), - R->getSelfAccessKind()); - } - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) { - emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder()); - } - } -} - -static StringRef getAttrName(DeclAttrKind Kind) { - switch (Kind) { -#define DECL_ATTR(NAME, CLASS, ...) \ - case DAK_##CLASS: \ - return DeclAttribute::isDeclModifier(DAK_##CLASS) ? #NAME : "@"#NAME; -#include "swift/AST/Attr.def" - case DAK_Count: - llvm_unreachable("unrecognized attribute kind."); - } - llvm_unreachable("covered switch"); -} - -static bool shouldDiagnoseAddingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { - return true; -} - -static bool shouldDiagnoseRemovingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { - return true; -} - -void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { - SDKNode::diagnose(Right); - auto *RD = dyn_cast(Right); - if (!RD) - return; - detectRename(this, RD); - auto Loc = RD->getLoc(); - if (isOpen() && !RD->isOpen()) { - emitDiag(Loc, diag::no_longer_open); - } - - // Diagnose static attribute change. - if (isStatic() ^ RD->isStatic()) { - emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" : - "static")); - } - - // Diagnose ownership change. - if (!isOwnershipEquivalent(getReferenceOwnership(), - RD->getReferenceOwnership())) { - auto getOwnershipDescription = [&](swift::ReferenceOwnership O) { - if (O == ReferenceOwnership::Strong) - return Ctx.buffer("strong"); - return keywordOf(O); - }; - emitDiag(Loc, diag::decl_attr_change, - getOwnershipDescription(getReferenceOwnership()), - getOwnershipDescription(RD->getReferenceOwnership())); - } - // Diagnose generic signature change - if (getGenericSignature() != RD->getGenericSignature()) { - // Prefer sugared signature in diagnostics to be more user-friendly. - if (Ctx.commonVersionAtLeast(2) && - getSugaredGenericSignature() != RD->getSugaredGenericSignature()) { - emitDiag(Loc, diag::generic_sig_change, - getSugaredGenericSignature(), RD->getSugaredGenericSignature()); - } else { - emitDiag(Loc, diag::generic_sig_change, - getGenericSignature(), RD->getGenericSignature()); - } - } - - // ObjC name changes are considered breakage - if (getObjCName() != RD->getObjCName()) { - if (Ctx.commonVersionAtLeast(4)) { - emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName()); - } - } - - if (isOptional() != RD->isOptional()) { - if (Ctx.checkingABI()) { - // Both adding/removing optional is ABI-breaking. - emitDiag(Loc, diag::optional_req_changed, isOptional()); - } else if (isOptional()) { - // Removing optional is source-breaking. - emitDiag(Loc, diag::optional_req_changed, isOptional()); - } - } - - // Diagnose removing attributes. - for (auto Kind: getDeclAttributes()) { - if (!RD->hasDeclAttribute(Kind)) { - if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) : - DeclAttribute::isRemovingBreakingAPI(Kind)) && - shouldDiagnoseRemovingAttribute(this, Kind)) { - emitDiag(Loc, diag::decl_new_attr, - Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str())); - } - } - } - - // Diagnose adding attributes. - for (auto Kind: RD->getDeclAttributes()) { - if (!hasDeclAttribute(Kind)) { - if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) : - DeclAttribute::isAddingBreakingAPI(Kind)) && - shouldDiagnoseAddingAttribute(this, Kind)) { - emitDiag(Loc, diag::decl_new_attr, - Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str())); - } - } - } - - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() && - getFixedBinaryOrder() != RD->getFixedBinaryOrder()) { - emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(), - RD->getFixedBinaryOrder()); - } - } -} - -void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *RO = dyn_cast(Right); - if (!RO) - return; - auto Loc = RO->getLoc(); - if (getDeclKind() != RO->getDeclKind()) { - emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind(), - getSDKContext().getOpts().CompilerStyle)); - } -} - -void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *RV = dyn_cast(Right); - if (!RV) - return; - auto Loc = RV->getLoc(); - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) { - emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder()); - } - } -} - -static bool shouldDiagnoseType(SDKNodeType *T) { - return T->isTopLevelType(); -} - -void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) { - SDKNode::diagnose(Right); - auto *RT = dyn_cast(Right); - if (!RT || !shouldDiagnoseType(this)) - return; - assert(isTopLevelType()); - - // Diagnose type witness changes when diagnosing ABI breakages. - if (auto *Wit = dyn_cast(getParent())) { - auto *Conform = Wit->getParent()->getAs(); - if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) { - auto *LD = Conform->getNominalTypeDecl(); - LD->emitDiag(SourceLoc(), diag::type_witness_change, - Wit->getWitnessedTypeName(), - getPrintedName(), RT->getPrintedName()); - } - return; - } - - StringRef Descriptor = getTypeRoleDescription(); - assert(isa(getParent())); - auto LParent = cast(getParent()); - assert(LParent->getKind() == RT->getParent()->getAs()->getKind()); - auto Loc = RT->getParent()->getAs()->getLoc(); - if (getPrintedName() != RT->getPrintedName()) { - LParent->emitDiag(Loc, diag::decl_type_change, - Descriptor, getPrintedName(), RT->getPrintedName()); - } - - if (hasDefaultArgument() && !RT->hasDefaultArgument()) { - LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor); - } - if (getParamValueOwnership() != RT->getParamValueOwnership()) { - LParent->emitDiag(Loc, diag::param_ownership_change, - getTypeRoleDescription(), - getParamValueOwnership(), - RT->getParamValueOwnership()); - } -} - -void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) { - SDKNodeType::diagnose(Right); - auto *RT = dyn_cast(Right); - if (!RT || !shouldDiagnoseType(this)) - return; - assert(isTopLevelType()); - auto Loc = RT->getParent()->getAs()->getLoc(); - if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) { - getParent()->getAs()->emitDiag(Loc, - diag::func_type_escaping_changed, - getTypeRoleDescription(), - isEscaping()); - } -} - namespace { static void diagnoseRemovedDecl(const SDKNodeDecl *D) { if (D->getSDKContext().checkingABI()) { @@ -1104,6 +797,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { case SDKNodeKind::DeclAccessor: case SDKNodeKind::DeclConstructor: case SDKNodeKind::DeclTypeAlias: + case SDKNodeKind::DeclImport: case SDKNodeKind::TypeFunc: case SDKNodeKind::TypeNominal: case SDKNodeKind::TypeAlias: { @@ -2044,7 +1738,7 @@ class RenameDetectorForMemberDiff : public MatchedNodeListener { void foundMatch(NodePtr Left, NodePtr Right, NodeMatchReason Reason) override { if (!Left || !Right) return; - detectRename(Left, Right); + swift::ide::api::detectRename(Left, Right); LeftDetector.detect(Left, Right); RightDetector.detect(Right, Left); } @@ -2107,13 +1801,13 @@ static void findTypeMemberDiffs(NodePtr leftSDKRoot, NodePtr rightSDKRoot, } static std::unique_ptr -createDiagConsumer(llvm::raw_ostream &OS, bool &FailOnError, +createDiagConsumer(llvm::raw_ostream &OS, bool &FailOnError, bool DisableFailOnError, bool CompilerStyleDiags, StringRef SerializedDiagPath) { if (!SerializedDiagPath.empty()) { - FailOnError = true; + FailOnError = !DisableFailOnError; return serialized_diagnostics::createConsumer(SerializedDiagPath); } else if (CompilerStyleDiags) { - FailOnError = true; + FailOnError = !DisableFailOnError; return std::make_unique(); } else { FailOnError = false; @@ -2168,6 +1862,7 @@ static bool readBreakageAllowlist(SDKContext &Ctx, llvm::StringSet<> &lines, static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule, SDKNodeRoot *RightModule, StringRef OutputPath, llvm::StringSet<> ProtocolReqAllowlist, + bool DisableFailOnError, bool CompilerStyleDiags, StringRef SerializedDiagPath, StringRef BreakageAllowlistPath, @@ -2192,7 +1887,7 @@ static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule, BreakageAllowlistPath); } auto pConsumer = std::make_unique( - createDiagConsumer(*OS, FailOnError, CompilerStyleDiags, + createDiagConsumer(*OS, FailOnError, DisableFailOnError, CompilerStyleDiags, SerializedDiagPath), std::move(allowedBreakages)); SWIFT_DEFER { pConsumer->finishProcessing(); }; @@ -2214,6 +1909,7 @@ static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule, static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath, StringRef OutputPath, CheckerOptions Opts, llvm::StringSet<> ProtocolReqAllowlist, + bool DisableFailOnError, bool CompilerStyleDiags, StringRef SerializedDiagPath, StringRef BreakageAllowlistPath, @@ -2233,7 +1929,7 @@ static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath, RightCollector.deSerialize(RightPath); return diagnoseModuleChange( Ctx, LeftCollector.getSDKRoot(), RightCollector.getSDKRoot(), OutputPath, - std::move(ProtocolReqAllowlist), CompilerStyleDiags, SerializedDiagPath, + std::move(ProtocolReqAllowlist), DisableFailOnError, CompilerStyleDiags, SerializedDiagPath, BreakageAllowlistPath, DebugMapping); } @@ -2365,10 +2061,6 @@ static void setSDKPath(CompilerInvocation &InitInvok, bool IsBaseline, InitInvok.setSDKPath(SDK.str()); } else if (const char *SDKROOT = getenv("SDKROOT")) { InitInvok.setSDKPath(SDKROOT); - } else { - llvm::errs() << "Provide '-sdk ' option or run with 'xcrun -sdk <..>\ - swift-api-digester'\n"; - exit(1); } } } @@ -2517,6 +2209,7 @@ class SwiftAPIDigesterInvocation { std::vector PreferInterfaceForModules; std::string ResourceDir; std::string ModuleCachePath; + bool DisableFailOnError; public: SwiftAPIDigesterInvocation(const std::string &ExecPath) @@ -2618,6 +2311,7 @@ class SwiftAPIDigesterInvocation { ResourceDir = ParsedArgs.getLastArgValue(OPT_resource_dir).str(); ModuleCachePath = ParsedArgs.getLastArgValue(OPT_module_cache_path).str(); DebugMapping = ParsedArgs.hasArg(OPT_debug_mapping); + DisableFailOnError = ParsedArgs.hasArg(OPT_disable_fail_on_error); CheckerOpts.AvoidLocation = ParsedArgs.hasArg(OPT_avoid_location); CheckerOpts.AvoidToolArgs = ParsedArgs.hasArg(OPT_avoid_tool_args); @@ -2820,21 +2514,21 @@ class SwiftAPIDigesterInvocation { case ComparisonInputMode::BothJson: { return diagnoseModuleChange( SDKJsonPaths[0], SDKJsonPaths[1], OutputFile, CheckerOpts, - std::move(protocolAllowlist), CompilerStyleDiags, + std::move(protocolAllowlist), DisableFailOnError, CompilerStyleDiags, SerializedDiagPath, BreakageAllowlistPath, DebugMapping); } case ComparisonInputMode::BaselineJson: { SDKContext Ctx(CheckerOpts); return diagnoseModuleChange( Ctx, getBaselineFromJson(Ctx), getSDKRoot(Ctx, false), OutputFile, - std::move(protocolAllowlist), CompilerStyleDiags, + std::move(protocolAllowlist), DisableFailOnError, CompilerStyleDiags, SerializedDiagPath, BreakageAllowlistPath, DebugMapping); } case ComparisonInputMode::BothLoad: { SDKContext Ctx(CheckerOpts); return diagnoseModuleChange( Ctx, getSDKRoot(Ctx, true), getSDKRoot(Ctx, false), OutputFile, - std::move(protocolAllowlist), CompilerStyleDiags, + std::move(protocolAllowlist), DisableFailOnError, CompilerStyleDiags, SerializedDiagPath, BreakageAllowlistPath, DebugMapping); } } diff --git a/tools/driver/swift_api_extract_main.cpp b/lib/DriverTool/swift_api_extract_main.cpp similarity index 100% rename from tools/driver/swift_api_extract_main.cpp rename to lib/DriverTool/swift_api_extract_main.cpp diff --git a/tools/driver/swift_indent_main.cpp b/lib/DriverTool/swift_indent_main.cpp similarity index 100% rename from tools/driver/swift_indent_main.cpp rename to lib/DriverTool/swift_indent_main.cpp diff --git a/tools/driver/swift_symbolgraph_extract_main.cpp b/lib/DriverTool/swift_symbolgraph_extract_main.cpp similarity index 99% rename from tools/driver/swift_symbolgraph_extract_main.cpp rename to lib/DriverTool/swift_symbolgraph_extract_main.cpp index 7131ecfae68be..7297c5ac55aa4 100644 --- a/tools/driver/swift_symbolgraph_extract_main.cpp +++ b/lib/DriverTool/swift_symbolgraph_extract_main.cpp @@ -168,6 +168,7 @@ int swift_symbolgraph_extract_main(ArrayRef Args, !ParsedArgs.hasArg(OPT_skip_synthesized_members), ParsedArgs.hasArg(OPT_v), ParsedArgs.hasArg(OPT_skip_inherited_docs), + ParsedArgs.hasArg(OPT_include_spi_symbols), }; if (auto *A = ParsedArgs.getLastArg(OPT_minimum_access_level)) { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index cdd66021d1f17..d3743c010e6e6 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -63,6 +63,9 @@ bool ArgsToFrontendOptionsConverter::convert( if (const Arg *A = Args.getLastArg(OPT_prebuilt_module_cache_path)) { Opts.PrebuiltModuleCachePath = A->getValue(); } + if (const Arg *A = Args.getLastArg(OPT_backup_module_interface_path)) { + Opts.BackupModuleInterfaceDir = A->getValue(); + } if (const Arg *A = Args.getLastArg(OPT_bridging_header_directory_for_print)) { Opts.BridgingHeaderDirForPrint = A->getValue(); } @@ -100,7 +103,11 @@ bool ArgsToFrontendOptionsConverter::convert( } if (auto A = Args.getLastArg(OPT_user_module_version)) { - if (Opts.UserModuleVersion.tryParse(StringRef(A->getValue()))) { + StringRef raw(A->getValue()); + while(raw.count('.') > 3) { + raw = raw.rsplit('.').first; + } + if (Opts.UserModuleVersion.tryParse(raw)) { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, A->getAsString(Args), A->getValue()); } @@ -110,6 +117,13 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.ImportPrescan |= Args.hasArg(OPT_import_prescan); + Opts.SerializeDependencyScannerCache |= Args.hasArg(OPT_serialize_dependency_scan_cache); + Opts.ReuseDependencyScannerCache |= Args.hasArg(OPT_reuse_dependency_scan_cache); + Opts.EmitDependencyScannerCacheRemarks |= Args.hasArg(OPT_dependency_scan_cache_remarks); + if (const Arg *A = Args.getLastArg(OPT_dependency_scan_cache_path)) { + Opts.SerializedDependencyScannerCachePath = A->getValue(); + } + Opts.DisableCrossModuleIncrementalBuild |= Args.hasArg(OPT_disable_incremental_imports); @@ -257,6 +271,7 @@ bool ArgsToFrontendOptionsConverter::convert( } Opts.SkipInheritedDocs = Args.hasArg(OPT_skip_inherited_docs); + Opts.IncludeSPISymbolsInSymbolGraph = Args.hasArg(OPT_include_spi_symbols); Opts.Static = Args.hasArg(OPT_static); @@ -595,6 +610,11 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module_doc); return true; } + if (!FrontendOptions::canActionEmitABIDescriptor(Opts.RequestedAction) && + Opts.InputsAndOutputs.hasABIDescriptorOutputPath()) { + Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_abi_descriptor); + return true; + } // If we cannot emit module doc, we cannot emit source information file either. if (!FrontendOptions::canActionEmitModuleDoc(Opts.RequestedAction) && Opts.InputsAndOutputs.hasModuleSourceInfoOutputPath()) { diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 0c042e7a12d05..c8ac5777c2859 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -337,15 +337,15 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_private_module_interface_path); auto moduleSourceInfoOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_module_source_info_path); - auto ldAddCFileOutput = getSupplementaryFilenamesFromArguments( - options::OPT_emit_ldadd_cfile_path); auto moduleSummaryOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_module_summary_path); + auto abiDescriptorOutput = getSupplementaryFilenamesFromArguments( + options::OPT_emit_abi_descriptor_path); if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || !moduleInterfaceOutput || !privateModuleInterfaceOutput || - !moduleSourceInfoOutput || !ldAddCFileOutput || !moduleSummaryOutput) { + !moduleSourceInfoOutput || !moduleSummaryOutput || !abiDescriptorOutput) { return None; } std::vector result; @@ -366,8 +366,8 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() sop.ModuleInterfaceOutputPath = (*moduleInterfaceOutput)[i]; sop.PrivateModuleInterfaceOutputPath = (*privateModuleInterfaceOutput)[i]; sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i]; - sop.LdAddCFilePath = (*ldAddCFileOutput)[i]; sop.ModuleSummaryOutputPath = (*moduleSummaryOutput)[i]; + sop.ABIDescriptorOutputPath = (*abiDescriptorOutput)[i]; result.push_back(sop); } return result; @@ -466,6 +466,8 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( auto PrivateModuleInterfaceOutputPath = pathsFromArguments.PrivateModuleInterfaceOutputPath; + // There is no non-path form of -emit-abi-descriptor-path + auto ABIDescriptorOutputPath = pathsFromArguments.ABIDescriptorOutputPath; ID emitModuleOption; std::string moduleExtension; std::string mainOutputIfUsableForModule; @@ -490,8 +492,8 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( sop.ModuleInterfaceOutputPath = ModuleInterfaceOutputPath; sop.PrivateModuleInterfaceOutputPath = PrivateModuleInterfaceOutputPath; sop.ModuleSourceInfoOutputPath = moduleSourceInfoOutputPath; - sop.LdAddCFilePath = pathsFromArguments.LdAddCFilePath; sop.ModuleSummaryOutputPath = moduleSummaryOutputPath; + sop.ABIDescriptorOutputPath = ABIDescriptorOutputPath; return sop; } @@ -596,8 +598,7 @@ SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { options::OPT_emit_module_interface_path, options::OPT_emit_private_module_interface_path, options::OPT_emit_module_source_info_path, - options::OPT_emit_tbd_path, - options::OPT_emit_ldadd_cfile_path)) { + options::OPT_emit_tbd_path)) { Diags.diagnose(SourceLoc(), diag::error_cannot_have_supplementary_outputs, A->getSpelling(), "-supplementary-output-file-map"); diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 2c8c24f9e4dd9..b883676afe17f 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftFrontend STATIC ArgsToFrontendInputsConverter.cpp ArgsToFrontendOptionsConverter.cpp @@ -27,5 +27,7 @@ target_link_libraries(swiftFrontend PRIVATE swiftLocalization swiftSema swiftSerialization - swiftTBDGen) + swiftTBDGen + swiftAPIDigester) +set_swift_llvm_is_available(swiftFrontend) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index dc93cb1063dc6..2eef2b663067c 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -18,6 +18,7 @@ #include "swift/Option/Options.h" #include "swift/Option/SanitizerOptions.h" #include "swift/Strings.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Option/Arg.h" @@ -27,6 +28,7 @@ #include "llvm/Support/LineIterator.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/WithColor.h" using namespace swift; using namespace llvm::opt; @@ -129,6 +131,13 @@ void CompilerInvocation::setDefaultPrebuiltCacheIfNecessary() { FrontendOpts.PrebuiltModuleCachePath = computePrebuiltCachePath( SearchPathOpts.RuntimeResourcePath, LangOpts.Target, LangOpts.SDKVersion); + if (!FrontendOpts.PrebuiltModuleCachePath.empty()) + return; + StringRef anchor = "prebuilt-modules"; + assert(((StringRef)FrontendOpts.PrebuiltModuleCachePath).contains(anchor)); + auto pair = ((StringRef)FrontendOpts.PrebuiltModuleCachePath).split(anchor); + FrontendOpts.BackupModuleInterfaceDir = + (llvm::Twine(pair.first) + "preferred-interfaces" + pair.second).str(); } static void updateRuntimeLibraryPaths(SearchPathOptions &SearchPathOpts, @@ -348,14 +357,26 @@ static void SaveModuleInterfaceArgs(ModuleInterfaceOptions &Opts, if (!FOpts.InputsAndOutputs.hasModuleInterfaceOutputPath()) return; ArgStringList RenderedArgs; + ArgStringList RenderedArgsIgnorable; for (auto A : Args) { - if (A->getOption().hasFlag(options::ModuleInterfaceOption)) + if (A->getOption().hasFlag(options::ModuleInterfaceOptionIgnorable)) { + A->render(Args, RenderedArgsIgnorable); + } else if (A->getOption().hasFlag(options::ModuleInterfaceOption)) { A->render(Args, RenderedArgs); + } + } + { + llvm::raw_string_ostream OS(Opts.Flags); + interleave(RenderedArgs, + [&](const char *Argument) { PrintArg(OS, Argument, StringRef()); }, + [&] { OS << " "; }); + } + { + llvm::raw_string_ostream OS(Opts.IgnorableFlags); + interleave(RenderedArgsIgnorable, + [&](const char *Argument) { PrintArg(OS, Argument, StringRef()); }, + [&] { OS << " "; }); } - llvm::raw_string_ostream OS(Opts.Flags); - interleave(RenderedArgs, - [&](const char *Argument) { PrintArg(OS, Argument, StringRef()); }, - [&] { OS << " "; }); } static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, @@ -400,18 +421,32 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableExperimentalConcurrency |= Args.hasArg(OPT_enable_experimental_concurrency); + + Opts.EnableExperimentalNamedOpaqueTypes |= + Args.hasArg(OPT_enable_experimental_named_opaque_types); + + Opts.EnableExperimentalStructuralOpaqueTypes |= + Args.hasArg(OPT_enable_experimental_structural_opaque_types); + + Opts.EnableExperimentalDistributed |= + Args.hasArg(OPT_enable_experimental_distributed); + Opts.EnableInferPublicSendable |= Args.hasFlag(OPT_enable_infer_public_concurrent_value, OPT_disable_infer_public_concurrent_value, false); - Opts.EnableExperimentalAsyncHandler |= - Args.hasArg(OPT_enable_experimental_async_handler); Opts.EnableExperimentalFlowSensitiveConcurrentCaptures |= Args.hasArg(OPT_enable_experimental_flow_sensitive_concurrent_captures); Opts.DisableImplicitConcurrencyModuleImport |= Args.hasArg(OPT_disable_implicit_concurrency_module_import); + /// experimental distributed also implicitly enables experimental concurrency + Opts.EnableExperimentalDistributed |= + Args.hasArg(OPT_enable_experimental_distributed); + Opts.EnableExperimentalConcurrency |= + Args.hasArg(OPT_enable_experimental_distributed); + Opts.DiagnoseInvalidEphemeralnessAsError |= Args.hasArg(OPT_enable_invalid_ephemeralness_as_error); @@ -430,6 +465,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, = A->getOption().matches(OPT_enable_conformance_availability_errors); } + Opts.WarnOnPotentiallyUnavailableEnumCase |= + Args.hasArg(OPT_warn_on_potentially_unavailable_enum_case); + Opts.WarnOnEditorPlaceholder |= Args.hasArg(OPT_warn_on_editor_placeholder); + if (auto A = Args.getLastArg(OPT_enable_access_control, OPT_disable_access_control)) { Opts.EnableAccessControl @@ -604,12 +643,24 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.OptimizationRemarkMissedPattern = generateOptimizationRemarkRegex(Diags, Args, A); + if (Arg *A = Args.getLastArg(OPT_Raccess_note)) { + auto value = llvm::StringSwitch>(A->getValue()) + .Case("none", AccessNoteDiagnosticBehavior::Ignore) + .Case("failures", AccessNoteDiagnosticBehavior::RemarkOnFailure) + .Case("all", AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess) + .Case("all-validate", AccessNoteDiagnosticBehavior::ErrorOnFailureRemarkOnSuccess) + .Default(None); + + if (value) + Opts.AccessNoteBehavior = *value; + else + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + } + Opts.EnableConcisePoundFile = - Args.hasArg(OPT_enable_experimental_concise_pound_file); - Opts.EnableFuzzyForwardScanTrailingClosureMatching = - Args.hasFlag(OPT_enable_fuzzy_forward_scan_trailing_closure_matching, - OPT_disable_fuzzy_forward_scan_trailing_closure_matching, - true); + Args.hasArg(OPT_enable_experimental_concise_pound_file) || + Opts.EffectiveLanguageVersion.isVersionAtLeast(6); Opts.EnableCrossImportOverlays = Args.hasFlag(OPT_enable_cross_import_overlays, @@ -644,6 +695,20 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.TargetVariant = llvm::Triple(A->getValue()); } + // Collect -clang-target value if specified in the front-end invocation. + // Usually, the driver will pass down a clang target with the + // exactly same value as the main target, so we could dignose the usage of + // unavailable APIs. + // The reason we cannot infer clang target from -target is that not all + // front-end invocation will include a -target to start with. For instance, + // when compiling a Swift module from a textual interface, -target isn't + // necessary because the textual interface hardcoded the proper target triple + // to use. Inferring -clang-target there will always give us the default + // target triple. + if (const Arg *A = Args.getLastArg(OPT_clang_target)) { + Opts.ClangTarget = llvm::Triple(A->getValue()); + } + Opts.EnableCXXInterop |= Args.hasArg(OPT_enable_cxx_interop); Opts.EnableObjCInterop = Args.hasFlag(OPT_enable_objc_interop, OPT_disable_objc_interop, @@ -749,6 +814,51 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, } } + if (auto A = + Args.getLastArg(OPT_requirement_machine_EQ)) { + auto value = llvm::StringSwitch>(A->getValue()) + .Case("off", RequirementMachineMode::Disabled) + .Case("on", RequirementMachineMode::Enabled) + .Case("verify", RequirementMachineMode::Verify) + .Default(None); + + if (value) + Opts.EnableRequirementMachine = *value; + else + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + } + + Opts.DumpRequirementMachine = Args.hasArg( + OPT_dump_requirement_machine); + Opts.AnalyzeRequirementMachine = Args.hasArg( + OPT_analyze_requirement_machine); + + if (const Arg *A = Args.getLastArg(OPT_debug_requirement_machine)) + Opts.DebugRequirementMachine = A->getValue(); + + if (const Arg *A = Args.getLastArg(OPT_requirement_machine_step_limit)) { + unsigned limit; + if (StringRef(A->getValue()).getAsInteger(10, limit)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + Opts.RequirementMachineStepLimit = limit; + } + } + + if (const Arg *A = Args.getLastArg(OPT_requirement_machine_depth_limit)) { + unsigned limit; + if (StringRef(A->getValue()).getAsInteger(10, limit)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + Opts.RequirementMachineDepthLimit = limit; + } + } + return HadError || UnsupportedOS || UnsupportedArch; } @@ -834,7 +944,6 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, Args.hasArg(OPT_experimental_print_full_convention); Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints); - Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); for (const Arg *A : Args.filtered(OPT_debug_constraints_on_line)) { unsigned line; @@ -855,6 +964,8 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, if (Args.getLastArg(OPT_solver_disable_shrink)) Opts.SolverDisableShrink = true; + Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); + return HadError; } @@ -928,6 +1039,28 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, return false; } +static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts, + ArgList &Args, + DiagnosticEngine &Diags, + LangOptions &LangOpts) { + using namespace options; + + if (const Arg *A = Args.getLastArg(OPT_emit_symbol_graph_dir)) { + Opts.OutputDir = A->getValue(); + } + + Opts.Target = LangOpts.Target; + + Opts.SkipInheritedDocs = Args.hasArg(OPT_skip_inherited_docs); + Opts.IncludeSPISymbols = Args.hasArg(OPT_include_spi_symbols); + + // default values for generating symbol graphs during a build + Opts.MinimumAccessLevel = AccessLevel::Public; + Opts.PrettyPrint = false; + Opts.EmitSynthesizedMembers = true; + Opts.PrintMessages = false; +} + static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args, DiagnosticEngine &Diags, @@ -972,9 +1105,6 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, Opts.DisableModulesValidateSystemDependencies |= Args.hasArg(OPT_disable_modules_validate_system_headers); - for (auto A: Args.filtered(OPT_swift_module_file)) { - Opts.ExplicitSwiftModules.push_back(resolveSearchPath(A->getValue())); - } if (const Arg *A = Args.getLastArg(OPT_explict_swift_module_map)) Opts.ExplicitSwiftModuleMap = A->getValue(); for (auto A: Args.filtered(OPT_candidate_module_file)) { @@ -1269,6 +1399,16 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, if (const Arg *A = Args.getLastArg(OPT_save_optimization_record_path)) Opts.OptRecordFile = A->getValue(); + // If any of the '-g', except '-gnone', is given, + // tell the SILPrinter to print debug info as well + if (const Arg *A = Args.getLastArg(OPT_g_Group)) { + if (!A->getOption().matches(options::OPT_gnone)) + Opts.PrintDebugInfo = true; + } + + if (Args.hasArg(OPT_legacy_gsil)) + llvm::WithColor::warning() << "'-gsil' is deprecated, " + << "use '-sil-based-debuginfo' instead\n"; if (Args.hasArg(OPT_debug_on_sil)) { // Derive the name of the SIL file for debugging from // the regular outputfile. @@ -1494,7 +1634,6 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.DisableLLVMOptzns |= Args.hasArg(OPT_disable_llvm_optzns); Opts.DisableSwiftSpecificLLVMOptzns |= Args.hasArg(OPT_disable_swift_specific_llvm_optzns); - Opts.DisableLLVMSLPVectorizer |= Args.hasArg(OPT_disable_llvm_slp_vectorizer); if (Args.hasArg(OPT_disable_llvm_verify)) Opts.Verify = false; @@ -1568,8 +1707,6 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.UseTypeLayoutValueHandling = false; } - Opts.UseSwiftCall = Args.hasArg(OPT_enable_swiftcall); - // This is set to true by default. Opts.UseIncrementalLLVMCodeGen &= !Args.hasArg(OPT_disable_incremental_llvm_codegeneration); @@ -1674,6 +1811,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, for (const auto &Lib : Args.getAllArgValues(options::OPT_autolink_library)) Opts.LinkLibraries.push_back(LinkLibrary(Lib, LibraryKind::Library)); + for (const auto &Lib : Args.getAllArgValues(options::OPT_public_autolink_library)) { + Opts.PublicLinkLibraries.push_back(Lib); + } + if (const Arg *A = Args.getLastArg(OPT_type_info_dump_filter_EQ)) { StringRef mode(A->getValue()); if (mode == "all") @@ -1699,6 +1840,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, runtimeCompatibilityVersion = llvm::VersionTuple(5, 0); } else if (version.equals("5.1")) { runtimeCompatibilityVersion = llvm::VersionTuple(5, 1); + } else if (version.equals("5.5")) { + runtimeCompatibilityVersion = llvm::VersionTuple(5, 5); } else { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, versionArg->getAsString(Args), version); @@ -1721,6 +1864,12 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, getRuntimeCompatVersion(); } + if (!Args.hasArg( + options::OPT_disable_autolinking_runtime_compatibility_concurrency)) { + Opts.AutolinkRuntimeCompatibilityConcurrencyLibraryVersion = + getRuntimeCompatVersion(); + } + if (const Arg *A = Args.getLastArg(OPT_num_threads)) { if (StringRef(A->getValue()).getAsInteger(10, Opts.NumThreads)) { Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, @@ -1734,6 +1883,13 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, } } + if (SWIFT_ENABLE_GLOBAL_ISEL_ARM64 && + (Triple.getArch() == llvm::Triple::aarch64 || + Triple.getArch() == llvm::Triple::aarch64_32) && + Triple.getArchName() != "arm64e") { + Opts.EnableGlobalISel = true; + } + return false; } @@ -1881,6 +2037,8 @@ bool CompilerInvocation::parseArgs( return true; } + ParseSymbolGraphArgs(SymbolGraphOpts, ParsedArgs, Diags, LangOpts); + if (ParseSearchPathArgs(SearchPathOpts, ParsedArgs, Diags, workingDirectory)) { return true; @@ -1979,30 +2137,3 @@ CompilerInvocation::setUpInputForSILTool( } return fileBufOrErr; } - -bool CompilerInvocation::isModuleExternallyConsumed( - const ModuleDecl *mod) const { - // Modules for executables aren't expected to be consumed by other modules. - // This picks up all kinds of entrypoints, including script mode, - // @UIApplicationMain and @NSApplicationMain. - if (mod->hasEntryPoint()) { - return false; - } - - // If an implicit Objective-C header was needed to construct this module, it - // must be the product of a library target. - if (!getFrontendOptions().ImplicitObjCHeaderPath.empty()) { - return false; - } - - // App extensions are special beasts because they build without entrypoints - // like library targets, but they behave like executable targets because - // their associated modules are not suitable for distribution. - if (mod->getASTContext().LangOpts.EnableAppExtensionRestrictions) { - return false; - } - - // FIXME: This is still a lousy approximation of whether the module file will - // be externally consumed. - return true; -} diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 2d6ecc0fe91d5..de292051246f9 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -116,14 +116,6 @@ std::string CompilerInvocation::getTBDPathForWholeModule() const { .SupplementaryOutputs.TBDPath; } -std::string -CompilerInvocation::getLdAddCFileOutputPathForWholeModule() const { - assert(getFrontendOptions().InputsAndOutputs.isWholeModule() && - "LdAdd cfile only makes sense when the whole module can be seen"); - return getPrimarySpecificPathsForAtMostOnePrimary() - .SupplementaryOutputs.LdAddCFilePath; -} - std::string CompilerInvocation::getModuleInterfaceOutputPathForWholeModule() const { assert(getFrontendOptions().InputsAndOutputs.isWholeModule() && @@ -156,7 +148,9 @@ SerializationOptions CompilerInvocation::computeSerializationOptions( serializationOpts.ModuleLinkName = opts.ModuleLinkName; serializationOpts.UserModuleVersion = opts.UserModuleVersion; serializationOpts.ExtraClangOptions = getClangImporterOptions().ExtraArgs; - + serializationOpts.PublicDependentLibraries = + getIRGenOptions().PublicLinkLibraries; + if (opts.EmitSymbolGraph) { if (!opts.SymbolGraphOutputDir.empty()) { serializationOpts.SymbolGraphOutputDir = opts.SymbolGraphOutputDir; @@ -168,6 +162,7 @@ SerializationOptions CompilerInvocation::computeSerializationOptions( serializationOpts.SymbolGraphOutputDir = OutputDir.str().str(); } serializationOpts.SkipSymbolGraphInheritedDocs = opts.SkipInheritedDocs; + serializationOpts.IncludeSPISymbolsInSymbolGraph = opts.IncludeSPISymbolsInSymbolGraph; if (!getIRGenOptions().ForceLoadSymbolName.empty()) serializationOpts.AutolinkForceLoad = true; @@ -177,7 +172,7 @@ SerializationOptions CompilerInvocation::computeSerializationOptions( // the public. serializationOpts.SerializeOptionsForDebugging = opts.SerializeOptionsForDebugging.getValueOr( - !isModuleExternallyConsumed(module)); + !module->isExternallyConsumed()); serializationOpts.DisableCrossModuleIncrementalInfo = opts.DisableCrossModuleIncrementalBuild; @@ -220,6 +215,7 @@ bool CompilerInstance::setUpASTContextIfNeeded() { Invocation.getLangOptions(), Invocation.getTypeCheckerOptions(), Invocation.getSearchPathOptions(), Invocation.getClangImporterOptions(), + Invocation.getSymbolGraphOptions(), SourceMgr, Diagnostics)); registerParseRequestFunctions(Context->evaluator); registerTypeCheckerRequestFunctions(Context->evaluator); @@ -448,6 +444,8 @@ void CompilerInstance::setUpDiagnosticOptions() { } Diagnostics.setDiagnosticDocumentationPath( Invocation.getDiagnosticOptions().DiagnosticDocumentationPath); + Diagnostics.setLanguageVersion( + Invocation.getLangOptions().EffectiveLanguageVersion); if (!Invocation.getDiagnosticOptions().LocalizationCode.empty()) { Diagnostics.setLocalization( Invocation.getDiagnosticOptions().LocalizationCode, @@ -530,7 +528,8 @@ bool CompilerInstance::setUpModuleLoaders() { ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); Context->addModuleInterfaceChecker( std::make_unique( - *Context, ModuleCachePath, FEOpts.PrebuiltModuleCachePath, LoaderOpts, + *Context, ModuleCachePath, FEOpts.PrebuiltModuleCachePath, + FEOpts.BackupModuleInterfaceDir, LoaderOpts, RequireOSSAModules_t(Invocation.getSILOptions()))); // If implicit modules are disabled, we need to install an explicit module // loader. @@ -539,7 +538,6 @@ bool CompilerInstance::setUpModuleLoaders() { auto ESML = ExplicitSwiftModuleLoader::create( *Context, getDependencyTracker(), MLM, - Invocation.getSearchPathOptions().ExplicitSwiftModules, Invocation.getSearchPathOptions().ExplicitSwiftModuleMap, IgnoreSourceInfoFile); this->DefaultSerializedLoader = ESML.get(); @@ -571,10 +569,11 @@ bool CompilerInstance::setUpModuleLoaders() { auto &FEOpts = Invocation.getFrontendOptions(); ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); InterfaceSubContextDelegateImpl ASTDelegate( - Context->SourceMgr, Context->Diags, Context->SearchPathOpts, + Context->SourceMgr, &Context->Diags, Context->SearchPathOpts, Context->LangOpts, Context->ClangImporterOpts, LoaderOpts, /*buildModuleCacheDirIfAbsent*/ false, ModuleCachePath, FEOpts.PrebuiltModuleCachePath, + FEOpts.BackupModuleInterfaceDir, FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), RequireOSSAModules_t(Invocation.getSILOptions())); @@ -764,7 +763,9 @@ static bool shouldImportConcurrencyByDefault(const llvm::Triple &target) { return true; if (target.isOSLinux()) return true; -#if SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY +#if SWIFT_IMPLICIT_CONCURRENCY_IMPORT + if (target.isOSWASI()) + return true; if (target.isOSOpenBSD()) return true; #endif @@ -968,6 +969,9 @@ ModuleDecl *CompilerInstance::getMainModule() const { } if (Invocation.getFrontendOptions().EnableLibraryEvolution) MainModule->setResilienceStrategy(ResilienceStrategy::Resilient); + if (Invocation.getLangOptions().WarnConcurrency || + Invocation.getLangOptions().isSwiftVersionAtLeast(6)) + MainModule->setIsConcurrencyChecked(true); // Register the main module with the AST context. Context->addLoadedModule(MainModule); @@ -1149,10 +1153,10 @@ CompilerInstance::getSourceFileParsingOptions(bool forPrimary) const { opts |= SourceFile::ParsingFlags::DisableDelayedBodies; } + auto typeOpts = getASTContext().TypeCheckerOpts; if (forPrimary || isWholeModuleCompilation()) { // Disable delayed body parsing for primaries and in WMO, unless // forcefully skipping function bodies - auto typeOpts = getASTContext().TypeCheckerOpts; if (typeOpts.SkipFunctionBodies == FunctionBodySkipping::None) opts |= SourceFile::ParsingFlags::DisableDelayedBodies; } else { @@ -1161,9 +1165,10 @@ CompilerInstance::getSourceFileParsingOptions(bool forPrimary) const { opts |= SourceFile::ParsingFlags::SuppressWarnings; } - // Enable interface hash computation for primaries, but not in WMO, as it's - // only currently needed for incremental mode. - if (forPrimary) { + // Enable interface hash computation for primaries or emit-module-separately, + // but not in WMO, as it's only currently needed for incremental mode. + if (forPrimary || + typeOpts.SkipFunctionBodies == FunctionBodySkipping::NonInlinableWithoutTypes) { opts |= SourceFile::ParsingFlags::EnableInterfaceHash; } return opts; diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index c65b43e6543be..d76a2db1b0a6d 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -503,6 +503,12 @@ bool FrontendInputsAndOutputs::hasPrivateModuleInterfaceOutputPath() const { return outs.PrivateModuleInterfaceOutputPath; }); } +bool FrontendInputsAndOutputs::hasABIDescriptorOutputPath() const { + return hasSupplementaryOutputPath( + [](const SupplementaryOutputPaths &outs) -> const std::string & { + return outs.ABIDescriptorOutputPath; + }); +} bool FrontendInputsAndOutputs::hasModuleSummaryOutputPath() const { return hasSupplementaryOutputPath( [](const SupplementaryOutputPaths &outs) -> const std::string & { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 452c1e5b99f87..08e0d14243cf1 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -524,7 +524,47 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) { } llvm_unreachable("unhandled action"); } - +bool FrontendOptions::canActionEmitABIDescriptor(ActionType action) { + switch (action) { + case ActionType::CompileModuleFromInterface: + return true; + case ActionType::NoneAction: + case ActionType::Parse: + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpParse: + case ActionType::DumpInterfaceHash: + case ActionType::DumpAST: + case ActionType::EmitSyntax: + case ActionType::PrintAST: + case ActionType::EmitPCH: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::DumpTypeInfo: + case ActionType::EmitSILGen: + case ActionType::TypecheckModuleFromInterface: + case ActionType::Immediate: + case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::ScanDependencies: + case ActionType::PrintVersion: + case ActionType::PrintFeature: + case ActionType::MergeModules: + case ActionType::EmitModuleOnly: + case ActionType::EmitSIL: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::EmitIRGen: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitAssembly: + case ActionType::EmitObject: + case ActionType::EmitImportedModules: + return false; + } + llvm_unreachable("unhandled action"); +} bool FrontendOptions::canActionEmitModule(ActionType action) { switch (action) { case ActionType::NoneAction: diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index f9348553af86a..4c8a3e18579da 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -24,6 +24,7 @@ #include "swift/Frontend/ModuleInterfaceSupport.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/Serialization/SerializationOptions.h" +#include "swift/APIDigester/ModuleAnalyzerNodes.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/Hashing.h" @@ -103,7 +104,11 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization( // dependency list -- don't serialize that. if (!prebuiltCachePath.empty() && DepName.startswith(prebuiltCachePath)) continue; - + // Don't serialize interface path if it's from the preferred interface dir. + // This ensures the prebuilt module caches generated from these interfaces are + // relocatable. + if (!backupInterfaceDir.empty() && DepName.startswith(backupInterfaceDir)) + continue; if (dependencyTracker) { dependencyTracker->addDependency(DepName, /*isSystem*/IsSDKRelative); } @@ -279,6 +284,9 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( if (SubInstance.getDiags().hadAnyError()) { return std::make_error_code(std::errc::not_supported); } + if (!ABIDescriptorPath.empty()) { + swift::ide::api::dumpModuleContent(Mod, ABIDescriptorPath, true); + } return std::error_code(); }); }, ThreadStackSize); diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index ae90b2ded60f9..fc55c807a4690 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -35,12 +35,14 @@ class DependencyTracker; class ModuleInterfaceBuilder { SourceManager &sourceMgr; - DiagnosticEngine &diags; + DiagnosticEngine *diags; InterfaceSubContextDelegate &subASTDelegate; const StringRef interfacePath; const StringRef moduleName; const StringRef moduleCachePath; const StringRef prebuiltCachePath; + const StringRef backupInterfaceDir; + const StringRef ABIDescriptorPath; const bool disableInterfaceFileLock; const SourceLoc diagnosticLoc; DependencyTracker *const dependencyTracker; @@ -50,7 +52,7 @@ class ModuleInterfaceBuilder { /// Emit a diagnostic tied to this declaration. template static InFlightDiagnostic diagnose( - DiagnosticEngine &Diags, + DiagnosticEngine *Diags, SourceManager &SM, StringRef InterfacePath, SourceLoc Loc, @@ -60,7 +62,7 @@ class ModuleInterfaceBuilder { // Diagnose this inside the interface file, if possible. Loc = SM.getLocFromExternalSource(InterfacePath, 1, 1); } - return Diags.diagnose(Loc, ID, std::move(Args)...); + return Diags->diagnose(Loc, ID, std::move(Args)...); } private: @@ -88,12 +90,14 @@ class ModuleInterfaceBuilder { std::unique_ptr *ModuleBuffer, ArrayRef CandidateModules); public: - ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags, + ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine *diags, InterfaceSubContextDelegate &subASTDelegate, StringRef interfacePath, StringRef moduleName, StringRef moduleCachePath, + StringRef backupInterfaceDir, StringRef prebuiltCachePath, + StringRef ABIDescriptorPath, bool disableInterfaceFileLock = false, SourceLoc diagnosticLoc = SourceLoc(), DependencyTracker *tracker = nullptr) @@ -101,6 +105,8 @@ class ModuleInterfaceBuilder { subASTDelegate(subASTDelegate), interfacePath(interfacePath), moduleName(moduleName), moduleCachePath(moduleCachePath), prebuiltCachePath(prebuiltCachePath), + backupInterfaceDir(backupInterfaceDir), + ABIDescriptorPath(ABIDescriptorPath), disableInterfaceFileLock(disableInterfaceFileLock), diagnosticLoc(diagnosticLoc), dependencyTracker(tracker) {} diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 0946972d80286..380b0a6b2e66d 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -287,44 +287,45 @@ struct ModuleRebuildInfo { /// Emits a diagnostic for all out-of-date compiled or forwarding modules /// encountered while trying to load a module. - void diagnose(ASTContext &ctx, SourceLoc loc, StringRef moduleName, - StringRef interfacePath, StringRef prebuiltCacheDir) { - ctx.Diags.diagnose(loc, diag::rebuilding_module_from_interface, - moduleName, interfacePath); + template + void diagnose(ASTContext &ctx, DiagnosticEngine &diags, + StringRef prebuiltCacheDir, SourceLoc loc, + DiagArgs &&...diagArgs) { + diags.diagnose(loc, std::forward(diagArgs)...); auto SDKVer = getSDKBuildVersion(ctx.SearchPathOpts.SDKPath); llvm::SmallString<64> buffer = prebuiltCacheDir; llvm::sys::path::append(buffer, "SystemVersion.plist"); auto PBMVer = getSDKBuildVersionFromPlist(buffer.str()); if (!SDKVer.empty() && !PBMVer.empty()) { // Remark the potential version difference. - ctx.Diags.diagnose(loc, diag::sdk_version_pbm_version, SDKVer, + diags.diagnose(loc, diag::sdk_version_pbm_version, SDKVer, PBMVer); } // We may have found multiple failing modules, that failed for different // reasons. Emit a note for each of them. for (auto &mod : outOfDateModules) { - ctx.Diags.diagnose(loc, diag::out_of_date_module_here, + diags.diagnose(loc, diag::out_of_date_module_here, (unsigned)mod.kind, mod.path); // Diagnose any out-of-date dependencies in this module. for (auto &dep : mod.outOfDateDependencies) { - ctx.Diags.diagnose(loc, diag::module_interface_dependency_out_of_date, + diags.diagnose(loc, diag::module_interface_dependency_out_of_date, dep); } // Diagnose any missing dependencies in this module. for (auto &dep : mod.missingDependencies) { - ctx.Diags.diagnose(loc, diag::module_interface_dependency_missing, dep); + diags.diagnose(loc, diag::module_interface_dependency_missing, dep); } // If there was a compiled module that wasn't able to be read, diagnose // the reason we couldn't read it. if (auto status = mod.serializationStatus) { if (auto reason = invalidModuleReason(*status)) { - ctx.Diags.diagnose(loc, diag::compiled_module_invalid_reason, + diags.diagnose(loc, diag::compiled_module_invalid_reason, mod.path, reason); } else { - ctx.Diags.diagnose(loc, diag::compiled_module_invalid, mod.path); + diags.diagnose(loc, diag::compiled_module_invalid, mod.path); } } } @@ -346,6 +347,7 @@ class ModuleInterfaceLoaderImpl { const std::string interfacePath; const StringRef moduleName; const StringRef prebuiltCacheDir; + const StringRef backupInterfaceDir; const StringRef cacheDir; const SourceLoc diagnosticLoc; DependencyTracker *const dependencyTracker; @@ -356,13 +358,16 @@ class ModuleInterfaceLoaderImpl { ModuleInterfaceLoaderImpl( ASTContext &ctx, StringRef modulePath, StringRef interfacePath, StringRef moduleName, StringRef cacheDir, StringRef prebuiltCacheDir, + StringRef backupInterfaceDir, SourceLoc diagLoc, ModuleInterfaceLoaderOptions Opts, RequireOSSAModules_t requiresOSSAModules, DependencyTracker *dependencyTracker = nullptr, ModuleLoadingMode loadMode = ModuleLoadingMode::PreferSerialized) : ctx(ctx), fs(*ctx.SourceMgr.getFileSystem()), diags(ctx.Diags), modulePath(modulePath), interfacePath(interfacePath), - moduleName(moduleName), prebuiltCacheDir(prebuiltCacheDir), + moduleName(moduleName), + prebuiltCacheDir(prebuiltCacheDir), + backupInterfaceDir(backupInterfaceDir), cacheDir(cacheDir), diagnosticLoc(diagLoc), dependencyTracker(dependencyTracker), loadMode(loadMode), Opts(Opts), requiresOSSAModules(requiresOSSAModules) {} @@ -420,6 +425,37 @@ class ModuleInterfaceLoaderImpl { DependencyStatus::OutOfDate; } + std::string getBackupPublicModuleInterfacePath() { + return getBackupPublicModuleInterfacePath(ctx.SourceMgr, backupInterfaceDir, + moduleName, interfacePath); + } + + static std::string getBackupPublicModuleInterfacePath(SourceManager &SM, + StringRef backupInterfaceDir, + StringRef moduleName, + StringRef interfacePath) { + if (backupInterfaceDir.empty()) + return std::string(); + auto &fs = *SM.getFileSystem(); + auto fileName = llvm::sys::path::filename(interfacePath); + { + llvm::SmallString<256> path(backupInterfaceDir); + llvm::sys::path::append(path, llvm::Twine(moduleName) + ".swiftmodule"); + llvm::sys::path::append(path, fileName); + if (fs.exists(path.str())) { + return path.str().str(); + } + } + { + llvm::SmallString<256> path(backupInterfaceDir); + llvm::sys::path::append(path, fileName); + if (fs.exists(path.str())) { + return path.str().str(); + } + } + return std::string(); + } + // Check if all the provided file dependencies are up-to-date compared to // what's currently on disk. bool dependenciesAreUpToDate(StringRef modulePath, @@ -521,16 +557,22 @@ class ModuleInterfaceLoaderImpl { return true; } + bool canInterfaceHavePrebuiltModule() { + StringRef sdkPath = ctx.SearchPathOpts.SDKPath; + if (!sdkPath.empty() && + hasPrefix(path::begin(interfacePath), path::end(interfacePath), + path::begin(sdkPath), path::end(sdkPath))) { + return !StringRef(interfacePath).endswith(".private.swiftinterface"); + } + return false; + } + Optional computePrebuiltModulePath(llvm::SmallString<256> &scratch) { namespace path = llvm::sys::path; - StringRef sdkPath = ctx.SearchPathOpts.SDKPath; // Check if this is a public interface file from the SDK. - if (sdkPath.empty() || - !hasPrefix(path::begin(interfacePath), path::end(interfacePath), - path::begin(sdkPath), path::end(sdkPath)) || - StringRef(interfacePath).endswith(".private.swiftinterface")) + if (!canInterfaceHavePrebuiltModule()) return None; // Assemble the expected path: $PREBUILT_CACHE/Foo.swiftmodule or @@ -862,9 +904,10 @@ class ModuleInterfaceLoaderImpl { trackSystemDependencies = ClangDependencyTracker->needSystemDependencies(); } InterfaceSubContextDelegateImpl astDelegate( - ctx.SourceMgr, ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, + ctx.SourceMgr, &ctx.Diags, ctx.SearchPathOpts, ctx.LangOpts, ctx.ClangImporterOpts, Opts, /*buildModuleCacheDirIfAbsent*/ true, cacheDir, prebuiltCacheDir, + backupInterfaceDir, /*serializeDependencyHashes*/ false, trackSystemDependencies, requiresOSSAModules); @@ -914,39 +957,107 @@ class ModuleInterfaceLoaderImpl { return std::make_error_code(std::errc::not_supported); } - // Set up a builder if we need to build the module. It'll also set up - // the genericSubInvocation we'll need to use to compute the cache paths. - ModuleInterfaceBuilder builder( - ctx.SourceMgr, ctx.Diags, astDelegate, interfacePath, moduleName, cacheDir, - prebuiltCacheDir, - Opts.disableInterfaceLock, diagnosticLoc, - dependencyTracker); - std::unique_ptr moduleBuffer; // We didn't discover a module corresponding to this interface. // Diagnose that we didn't find a loadable module, if we were asked to. - auto remarkRebuild = [&]() { - rebuildInfo.diagnose(ctx, diagnosticLoc, moduleName, - interfacePath, prebuiltCacheDir); + // + // Note that we use `diags` so that we emit this remark even when we're + // emitting other messages to `emptyDiags` (see below); these act as status + // messages to explain what's taking so long. + auto remarkRebuildAll = [&]() { + rebuildInfo.diagnose(ctx, diags, prebuiltCacheDir, diagnosticLoc, + diag::rebuilding_module_from_interface, moduleName, + interfacePath); + }; + // Diagnose only for the standard library; it should be prebuilt in typical + // workflows, but if it isn't, building it may take several minutes and a + // lot of memory, so users may think the compiler is busy-hung. + auto remarkRebuildStdlib = [&]() { + if (moduleName != "Swift") + return; + + auto moduleTriple = getTargetSpecificModuleTriple(ctx.LangOpts.Target); + rebuildInfo.diagnose(ctx, diags, prebuiltCacheDir, SourceLoc(), + diag::rebuilding_stdlib_from_interface, + moduleTriple.str()); }; - // If we found an out-of-date .swiftmodule, we still want to add it as - // a dependency of the .swiftinterface. That way if it's updated, but - // the .swiftinterface remains the same, we invalidate the cache and - // check the new .swiftmodule, because it likely has more information - // about the state of the world. - if (rebuildInfo.sawOutOfDateModule(modulePath)) - builder.addExtraDependency(modulePath); - - if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true, - &moduleBuffer, - Opts.remarkOnRebuildFromInterface ? remarkRebuild: - llvm::function_ref())) + auto remarkRebuild = Opts.remarkOnRebuildFromInterface + ? llvm::function_ref(remarkRebuildAll) + : remarkRebuildStdlib; + + bool failed = false; + std::string backupPath = getBackupPublicModuleInterfacePath(); + { + DiagnosticEngine emptyDiags(ctx.SourceMgr); + std::unique_ptr> saver; + DiagnosticEngine *diagsToUse = &ctx.Diags; + // Avoid emitting diagnostics if we have a backup interface to use. + // If we succeed in building this canonical interface, it's not interesting + // to see those diagnostics. + // If we failed in build, we will use the back up interface and it's interesting + // to see diagnostics there. + if (!backupPath.empty()) { + diagsToUse = &emptyDiags; + saver = std::make_unique>( + astDelegate.Diags, diagsToUse); + } + // Set up a builder if we need to build the module. It'll also set up + // the genericSubInvocation we'll need to use to compute the cache paths. + ModuleInterfaceBuilder builder( + ctx.SourceMgr, diagsToUse, + astDelegate, interfacePath, moduleName, cacheDir, + prebuiltCacheDir, backupInterfaceDir, StringRef(), + Opts.disableInterfaceLock, diagnosticLoc, + dependencyTracker); + // If we found an out-of-date .swiftmodule, we still want to add it as + // a dependency of the .swiftinterface. That way if it's updated, but + // the .swiftinterface remains the same, we invalidate the cache and + // check the new .swiftmodule, because it likely has more information + // about the state of the world. + if (rebuildInfo.sawOutOfDateModule(modulePath)) + builder.addExtraDependency(modulePath); + failed = builder.buildSwiftModule(cachedOutputPath, + /*shouldSerializeDeps*/true, + &moduleBuffer, remarkRebuild); + } + if (!failed) { + // If succeeded, we are done. + assert(moduleBuffer && + "failed to write module buffer but returned success?"); + return std::move(moduleBuffer); + } else if (backupPath.empty()) { + // If failed and we don't have a backup interface file, return error code. return std::make_error_code(std::errc::invalid_argument); - - assert(moduleBuffer && - "failed to write module buffer but returned success?"); - return std::move(moduleBuffer); + } + assert(failed); + assert(!backupPath.empty()); + while (1) { + diags.diagnose(diagnosticLoc, diag::interface_file_backup_used, + interfacePath, backupPath); + // Set up a builder if we need to build the module. It'll also set up + // the genericSubInvocation we'll need to use to compute the cache paths. + ModuleInterfaceBuilder fallbackBuilder( + ctx.SourceMgr, &ctx.Diags, astDelegate, backupPath, moduleName, cacheDir, + prebuiltCacheDir, backupInterfaceDir, StringRef(), + Opts.disableInterfaceLock, diagnosticLoc, + dependencyTracker); + if (rebuildInfo.sawOutOfDateModule(modulePath)) + fallbackBuilder.addExtraDependency(modulePath); + // Add the canonical interface path as a dependency of this module. + // This ensures that after the user manually fixed the canonical interface + // file and removed the fallback interface file, we can rebuild the cache. + fallbackBuilder.addExtraDependency(interfacePath); + // Use cachedOutputPath as the output file path. This output path was + // calcualted using the canonical interface file path to make sure we + // can find it from the canonical interface file. + auto failedAgain = fallbackBuilder.buildSwiftModule(cachedOutputPath, + /*shouldSerializeDeps*/true, &moduleBuffer, remarkRebuild); + if (failedAgain) + return std::make_error_code(std::errc::invalid_argument); + assert(moduleBuffer); + return std::move(moduleBuffer); + } } }; @@ -1004,7 +1115,8 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( auto ModuleName = ModuleID.Item.str(); ModuleInterfaceLoaderImpl Impl( Ctx, ModPath, InPath, ModuleName, InterfaceChecker.CacheDir, - InterfaceChecker.PrebuiltCacheDir, ModuleID.Loc, InterfaceChecker.Opts, + InterfaceChecker.PrebuiltCacheDir, InterfaceChecker.BackupInterfaceDir, + ModuleID.Loc, InterfaceChecker.Opts, InterfaceChecker.RequiresOSSAModules, dependencyTracker, llvm::is_contained(PreferInterfaceForModules, ModuleName) ? ModuleLoadingMode::PreferInterface @@ -1044,7 +1156,8 @@ ModuleInterfaceCheckerImpl::getCompiledModuleCandidatesForInterface( llvm::SmallString<32> modulePath = interfacePath; llvm::sys::path::replace_extension(modulePath, newExt); ModuleInterfaceLoaderImpl Impl(Ctx, modulePath, interfacePath, moduleName, - CacheDir, PrebuiltCacheDir, SourceLoc(), Opts, + CacheDir, PrebuiltCacheDir, BackupInterfaceDir, + SourceLoc(), Opts, RequiresOSSAModules, nullptr, ModuleLoadingMode::PreferSerialized); std::vector results; @@ -1065,7 +1178,8 @@ bool ModuleInterfaceCheckerImpl::tryEmitForwardingModule( llvm::SmallString<32> modulePath = interfacePath; llvm::sys::path::replace_extension(modulePath, newExt); ModuleInterfaceLoaderImpl Impl(Ctx, modulePath, interfacePath, moduleName, - CacheDir, PrebuiltCacheDir, SourceLoc(), Opts, + CacheDir, PrebuiltCacheDir, + BackupInterfaceDir, SourceLoc(), Opts, RequiresOSSAModules, nullptr, ModuleLoadingMode::PreferSerialized); SmallVector deps; @@ -1092,22 +1206,46 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( SourceManager &SourceMgr, DiagnosticEngine &Diags, const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts, const ClangImporterOptions &ClangOpts, StringRef CacheDir, - StringRef PrebuiltCacheDir, StringRef ModuleName, StringRef InPath, - StringRef OutPath, bool SerializeDependencyHashes, + StringRef PrebuiltCacheDir, StringRef BackupInterfaceDir, + StringRef ModuleName, StringRef InPath, + StringRef OutPath, StringRef ABIOutputPath, + bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions LoaderOpts, RequireOSSAModules_t RequireOSSAModules) { InterfaceSubContextDelegateImpl astDelegate( - SourceMgr, Diags, SearchPathOpts, LangOpts, ClangOpts, LoaderOpts, + SourceMgr, &Diags, SearchPathOpts, LangOpts, ClangOpts, LoaderOpts, /*CreateCacheDirIfAbsent*/ true, CacheDir, PrebuiltCacheDir, + BackupInterfaceDir, SerializeDependencyHashes, TrackSystemDependencies, RequireOSSAModules); - ModuleInterfaceBuilder builder(SourceMgr, Diags, astDelegate, InPath, + ModuleInterfaceBuilder builder(SourceMgr, &Diags, astDelegate, InPath, ModuleName, CacheDir, PrebuiltCacheDir, + BackupInterfaceDir, ABIOutputPath, LoaderOpts.disableInterfaceLock); // FIXME: We really only want to serialize 'important' dependencies here, if // we want to ship the built swiftmodules to another machine. - return builder.buildSwiftModule(OutPath, /*shouldSerializeDeps*/true, - /*ModuleBuffer*/nullptr, nullptr, - SearchPathOpts.CandidateCompiledModules); + auto failed = builder.buildSwiftModule(OutPath, /*shouldSerializeDeps*/true, + /*ModuleBuffer*/nullptr, nullptr, + SearchPathOpts.CandidateCompiledModules); + if (!failed) + return false; + auto backInPath = + ModuleInterfaceLoaderImpl::getBackupPublicModuleInterfacePath(SourceMgr, + BackupInterfaceDir, ModuleName, InPath); + if (backInPath.empty()) + return true; + assert(failed); + assert(!backInPath.empty()); + ModuleInterfaceBuilder backupBuilder(SourceMgr, &Diags, astDelegate, backInPath, + ModuleName, CacheDir, PrebuiltCacheDir, + BackupInterfaceDir, ABIOutputPath, + LoaderOpts.disableInterfaceLock); + // Ensure we can rebuild module after user changed the original interface file. + backupBuilder.addExtraDependency(InPath); + // FIXME: We really only want to serialize 'important' dependencies here, if + // we want to ship the built swiftmodules to another machine. + return backupBuilder.buildSwiftModule(OutPath, /*shouldSerializeDeps*/true, + /*ModuleBuffer*/nullptr, nullptr, + SearchPathOpts.CandidateCompiledModules); } void ModuleInterfaceLoader::collectVisibleTopLevelModuleNames( @@ -1132,6 +1270,17 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface( GenericArgs.push_back(triple); } + if (LangOpts.ClangTarget.hasValue()) { + genericSubInvocation.getLangOptions().ClangTarget = LangOpts.ClangTarget; + auto triple = ArgSaver.save(genericSubInvocation.getLangOptions() + .ClangTarget->getTriple()); + assert(!triple.empty()); + // In explicit module build, all PCMs will be built using the given clang target. + // So the Swift interface should know that as well to load these PCMs properly. + GenericArgs.push_back("-clang-target"); + GenericArgs.push_back(triple); + } + // Inherit the Swift language version genericSubInvocation.getLangOptions().EffectiveLanguageVersion = LangOpts.EffectiveLanguageVersion; @@ -1142,6 +1291,13 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface( genericSubInvocation.setImportSearchPaths(SearchPathOpts.ImportSearchPaths); genericSubInvocation.setFrameworkSearchPaths(SearchPathOpts.FrameworkSearchPaths); if (!SearchPathOpts.SDKPath.empty()) { + // Add -sdk arguments to the module building commands. + // Module building commands need this because dependencies sometimes use + // sdk-relative paths (prebuilt modules for example). Without -sdk, the command + // will not be able to local these dependencies, leading to unnecessary + // building from textual interfaces. + GenericArgs.push_back("-sdk"); + GenericArgs.push_back(ArgSaver.save(SearchPathOpts.SDKPath)); genericSubInvocation.setSDKPath(SearchPathOpts.SDKPath); } @@ -1171,6 +1327,10 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface( // If we are supposed to use RequireOSSAModules, do so. genericSubInvocation.getSILOptions().EnableOSSAModules = bool(RequireOSSAModules); + if (LangOpts.DisableAvailabilityChecking) { + genericSubInvocation.getLangOptions().DisableAvailabilityChecking = true; + GenericArgs.push_back("-disable-availability-checking"); + } } bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs( @@ -1184,8 +1344,8 @@ bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs( if (!FileOrError) { // Don't use this->diagnose() because it'll just try to re-open // interfacePath. - Diags.diagnose(diagnosticLoc, diag::error_open_input_file, - interfacePath, FileOrError.getError().message()); + Diags->diagnose(diagnosticLoc, diag::error_open_input_file, + interfacePath, FileOrError.getError().message()); return true; } auto SB = FileOrError.get()->getBuffer(); @@ -1198,14 +1358,14 @@ bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs( diag::error_extracting_version_from_module_interface); return true; } - if (extractCompilerFlagsFromInterface(SB, ArgSaver, SubArgs)) { + if (extractCompilerFlagsFromInterface(interfacePath, SB, ArgSaver, SubArgs)) { diagnose(interfacePath, diagnosticLoc, diag::error_extracting_version_from_module_interface); return true; } assert(VersMatches.size() == 2); // FIXME We should diagnose this at a location that makes sense: - auto Vers = swift::version::Version(VersMatches[1], SourceLoc(), &Diags); + auto Vers = swift::version::Version(VersMatches[1], SourceLoc(), Diags); if (CompRe.match(SB, &CompMatches)) { assert(CompMatches.size() == 2); @@ -1226,7 +1386,7 @@ bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs( } SmallString<32> ExpectedModuleName = subInvocation.getModuleName(); - if (subInvocation.parseArgs(SubArgs, Diags)) { + if (subInvocation.parseArgs(SubArgs, *Diags)) { return true; } @@ -1243,11 +1403,12 @@ bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs( } InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( - SourceManager &SM, DiagnosticEngine &Diags, + SourceManager &SM, DiagnosticEngine *Diags, const SearchPathOptions &searchPathOpts, const LangOptions &langOpts, const ClangImporterOptions &clangImporterOpts, ModuleInterfaceLoaderOptions LoaderOpts, bool buildModuleCacheDirIfAbsent, StringRef moduleCachePath, StringRef prebuiltCachePath, + StringRef backupModuleInterfaceDir, bool serializeDependencyHashes, bool trackSystemDependencies, RequireOSSAModules_t requireOSSAModules) : SM(SM), Diags(Diags), ArgSaver(Allocator) { @@ -1264,6 +1425,10 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( genericSubInvocation.getFrontendOptions().PrebuiltModuleCachePath = prebuiltCachePath.str(); } + if (!backupModuleInterfaceDir.empty()) { + genericSubInvocation.getFrontendOptions().BackupModuleInterfaceDir = + backupModuleInterfaceDir.str(); + } if (trackSystemDependencies) { genericSubInvocation.getFrontendOptions().IntermoduleDependencyTracking = IntermoduleDepTrackingMode::IncludeSystem; @@ -1277,8 +1442,6 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( genericSubInvocation.getFrontendOptions().DisableImplicitModules = true; GenericArgs.push_back("-disable-implicit-swift-modules"); } - genericSubInvocation.getSearchPathOptions().ExplicitSwiftModules = - searchPathOpts.ExplicitSwiftModules; // Pass down -explicit-swift-module-map-file // FIXME: we shouldn't need this. Remove it? StringRef explictSwiftModuleMap = searchPathOpts.ExplicitSwiftModuleMap; @@ -1477,7 +1640,7 @@ InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleName, } // Insert arguments collected from the interface file. BuildArgs.insert(BuildArgs.end(), SubArgs.begin(), SubArgs.end()); - if (subInvocation.parseArgs(SubArgs, Diags)) { + if (subInvocation.parseArgs(SubArgs, *Diags)) { return std::make_error_code(std::errc::not_supported); } CompilerInstance subInstance; @@ -1487,22 +1650,27 @@ InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleName, subInstance.getSourceMgr().setFileSystem(SM.getFileSystem()); - ForwardingDiagnosticConsumer FDC(Diags); + ForwardingDiagnosticConsumer FDC(*Diags); subInstance.addDiagnosticConsumer(&FDC); if (subInstance.setup(subInvocation)) { return std::make_error_code(std::errc::not_supported); } info.BuildArguments = BuildArgs; info.Hash = CacheHash; - auto target = *(std::find(BuildArgs.rbegin(), BuildArgs.rend(), "-target") - 1); + auto target = *(std::find(BuildArgs.rbegin(), BuildArgs.rend(), "-target") - 1); auto langVersion = *(std::find(BuildArgs.rbegin(), BuildArgs.rend(), "-swift-version") - 1); - std::array ExtraPCMArgs = { - // PCMs should use the target triple the interface will be using to build - "-Xcc", "-target", "-Xcc", target, + + std::vector ExtraPCMArgs = { // PCMs should use the effective Swift language version for apinotes. - "-Xcc", ArgSaver.save((llvm::Twine("-fapinotes-swift-version=") + langVersion).str()) + "-Xcc", + ArgSaver.save((llvm::Twine("-fapinotes-swift-version=") + langVersion).str()) }; + if (!subInvocation.getLangOptions().ClangTarget.hasValue()) { + ExtraPCMArgs.insert(ExtraPCMArgs.begin(), {"-Xcc", "-target", + "-Xcc", target}); + } + info.ExtraPCMArgs = ExtraPCMArgs; // Run the action under the sub compiler instance. return action(info); @@ -1552,15 +1720,10 @@ bool ExplicitSwiftModuleLoader::findModule(ImportPath::Element ModuleID, return false; } auto &moduleInfo = it->getValue(); - if (moduleInfo.moduleBuffer) { - // We found an explicit module matches the given name, give the buffer - // back to the caller side. - *ModuleBuffer = std::move(moduleInfo.moduleBuffer); - return true; - } // Set IsFramework bit according to the moduleInfo IsFramework = moduleInfo.isFramework; + IsSystemModule = moduleInfo.isSystem; auto &fs = *Ctx.SourceMgr.getFileSystem(); // Open .swiftmodule file @@ -1646,7 +1809,6 @@ void ExplicitSwiftModuleLoader::collectVisibleTopLevelModuleNames( std::unique_ptr ExplicitSwiftModuleLoader::create(ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, - ArrayRef ExplicitModulePaths, StringRef ExplicitSwiftModuleMap, bool IgnoreSwiftSourceInfoFile) { auto result = std::unique_ptr( @@ -1658,24 +1820,6 @@ ExplicitSwiftModuleLoader::create(ASTContext &ctx, // Parse a JSON file to collect explicitly built modules. Impl.parseSwiftExplicitModuleMap(ExplicitSwiftModuleMap); } - // Collect .swiftmodule paths from -swift-module-path - // FIXME: remove these. - for (auto path: ExplicitModulePaths) { - std::string name; - // Load the explicit module into a buffer and get its name. - std::unique_ptr buffer = getModuleName(ctx, path, name); - if (buffer) { - // Register this module for future loading. - auto &entry = Impl.ExplicitModuleMap[name]; - entry.modulePath = path; - entry.moduleBuffer = std::move(buffer); - } else { - // We cannot read the module content, diagnose. - ctx.Diags.diagnose(SourceLoc(), - diag::error_opening_explicit_module_file, - path); - } - } return result; } diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 30fcbeffc2e5a..137ebe0c95351 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -20,6 +20,7 @@ #include "swift/AST/Module.h" #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/TypeRepr.h" #include "swift/Basic/STLExtras.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/ModuleInterfaceSupport.h" @@ -56,6 +57,10 @@ static void printToolVersionAndFlagsComment(raw_ostream &out, << ToolsVersion << "\n"; out << "// " SWIFT_MODULE_FLAGS_KEY ": " << Opts.Flags << "\n"; + if (!Opts.IgnorableFlags.empty()) { + out << "// " SWIFT_MODULE_FLAGS_IGNORABLE_KEY ": " + << Opts.IgnorableFlags << "\n"; + } } std::string @@ -297,7 +302,7 @@ class InheritedProtocolCollector { using AvailableAttrList = TinyPtrVector; using ProtocolAndAvailability = - std::pair; + std::tuple; /// Protocols that will be included by the ASTPrinter without any extra work. SmallVector IncludedProtocols; @@ -334,29 +339,23 @@ class InheritedProtocolCollector { } static bool canPrintProtocolTypeNormally(Type type, const Decl *D) { - if (!isPublicOrUsableFromInline(type)) - return false; - - // Extensions and protocols can print marker protocols. - if (isa(D) || isa(D)) - return true; - - ExistentialLayout layout = type->getExistentialLayout(); - for (ProtocolType *protoTy : layout.getProtocols()) { - if (protoTy->getDecl()->isMarkerProtocol()) - return false; - } + return isPublicOrUsableFromInline(type); + } - return true; + static bool isUncheckedConformance(ProtocolConformance *conformance) { + if (auto normal = conformance->getRootNormalConformance()) + return normal->isUnchecked(); + return false; } - + /// For each type in \p directlyInherited, classify the protocols it refers to /// as included for printing or not, and record them in the appropriate /// vectors. - void recordProtocols(ArrayRef directlyInherited, const Decl *D) { + void recordProtocols(ArrayRef directlyInherited, + const Decl *D, bool skipSynthesized = false) { Optional availableAttrs; - for (TypeLoc inherited : directlyInherited) { + for (InheritedEntry inherited : directlyInherited) { Type inheritedTy = inherited.getType(); if (!inheritedTy || !inheritedTy->isExistentialType()) continue; @@ -367,13 +366,18 @@ class InheritedProtocolCollector { if (canPrintNormally) IncludedProtocols.push_back(protoTy->getDecl()); else - ExtraProtocols.push_back({protoTy->getDecl(), - getAvailabilityAttrs(D, availableAttrs)}); + ExtraProtocols.push_back( + ProtocolAndAvailability(protoTy->getDecl(), + getAvailabilityAttrs(D, availableAttrs), + inherited.isUnchecked)); } // FIXME: This ignores layout constraints, but currently we don't support // any of those besides 'AnyObject'. } + if (skipSynthesized) + return; + // Check for synthesized protocols, like Hashable on enums. if (auto *nominal = dyn_cast(D)) { SmallVector localConformances = @@ -382,8 +386,10 @@ class InheritedProtocolCollector { for (auto *conf : localConformances) { if (conf->getSourceKind() != ConformanceEntryKind::Synthesized) continue; - ExtraProtocols.push_back({conf->getProtocol(), - getAvailabilityAttrs(D, availableAttrs)}); + ExtraProtocols.push_back( + ProtocolAndAvailability(conf->getProtocol(), + getAvailabilityAttrs(D, availableAttrs), + isUncheckedConformance(conf))); } } } @@ -415,7 +421,7 @@ class InheritedProtocolCollector { /// /// \sa recordProtocols static void collectProtocols(PerTypeMap &map, const Decl *D) { - ArrayRef directlyInherited; + ArrayRef directlyInherited; const NominalTypeDecl *nominal; const IterableDeclContext *memberContext; @@ -451,7 +457,8 @@ class InheritedProtocolCollector { if (auto *CD = dyn_cast(D)) { for (auto *SD = CD->getSuperclassDecl(); SD; SD = SD->getSuperclassDecl()) { - map[nominal].recordProtocols(SD->getInherited(), SD); + map[nominal].recordProtocols( + SD->getInherited(), SD, /*skipSynthesized=*/true); for (auto *Ext: SD->getExtensions()) { if (shouldInclude(Ext)) { map[nominal].recordProtocols(Ext->getInherited(), Ext); @@ -495,7 +502,7 @@ class InheritedProtocolCollector { const NominalTypeDecl *nominal, ProtocolDecl *proto) { SmallVector conformances; - nominal->lookupConformance(M, proto, conformances); + nominal->lookupConformance(proto, conformances); return llvm::all_of(conformances, [M](const ProtocolConformance *conformance) -> bool { return M == conformance->getDeclContext()->getParentModule(); @@ -540,7 +547,10 @@ class InheritedProtocolCollector { // of a protocol rather than the maximally available case. SmallVector protocolsToPrint; for (const auto &protoAndAvailability : ExtraProtocols) { - protoAndAvailability.first->walkInheritedProtocols( + auto proto = std::get<0>(protoAndAvailability); + auto availability = std::get<1>(protoAndAvailability); + auto isUnchecked = std::get<2>(protoAndAvailability); + proto->walkInheritedProtocols( [&](ProtocolDecl *inherited) -> TypeWalker::Action { if (!handledProtocols.insert(inherited).second) return TypeWalker::Action::SkipChildren; @@ -558,8 +568,10 @@ class InheritedProtocolCollector { return TypeWalker::Action::Continue; if (isPublicOrUsableFromInline(inherited) && - conformanceDeclaredInModule(M, nominal, inherited)) { - protocolsToPrint.push_back({inherited, protoAndAvailability.second}); + conformanceDeclaredInModule(M, nominal, inherited) && + !M->isImportedImplementationOnly(inherited->getParentModule())) { + protocolsToPrint.push_back( + ProtocolAndAvailability(inherited, availability, isUnchecked)); return TypeWalker::Action::SkipChildren; } @@ -571,26 +583,36 @@ class InheritedProtocolCollector { for (const auto &protoAndAvailability : protocolsToPrint) { StreamPrinter printer(out); - ProtocolDecl *proto = protoAndAvailability.first; + auto proto = std::get<0>(protoAndAvailability); + auto availability = std::get<1>(protoAndAvailability); + auto isUnchecked = std::get<2>(protoAndAvailability); bool haveFeatureChecks = printOptions.PrintCompatibilityFeatureChecks && printCompatibilityFeatureChecksPre(printer, proto); // FIXME: Shouldn't this be an implicit conversion? TinyPtrVector attrs; - attrs.insert(attrs.end(), protoAndAvailability.second.begin(), - protoAndAvailability.second.end()); + attrs.insert(attrs.end(), availability.begin(), availability.end()); auto spiAttributes = proto->getAttrs().getAttributes(); attrs.insert(attrs.end(), spiAttributes.begin(), spiAttributes.end()); DeclAttributes::print(printer, printOptions, attrs); printer << "extension "; - PrintOptions typePrintOptions = printOptions; - typePrintOptions.FullyQualifiedTypes = false; - typePrintOptions.FullyQualifiedTypesIfAmbiguous = false; - nominal->getDeclaredType().print(printer, typePrintOptions); + { + PrintOptions typePrintOptions = printOptions; + bool oldFullyQualifiedTypesIfAmbiguous = + typePrintOptions.FullyQualifiedTypesIfAmbiguous; + typePrintOptions.FullyQualifiedTypesIfAmbiguous = + typePrintOptions.FullyQualifiedExtendedTypesIfAmbiguous; + nominal->getDeclaredType().print(printer, typePrintOptions); + typePrintOptions.FullyQualifiedTypesIfAmbiguous = + oldFullyQualifiedTypesIfAmbiguous; + } printer << " : "; + if (isUnchecked) + printer << "@unchecked "; + proto->getDeclaredInterfaceType()->print(printer, printOptions); printer << " {}"; @@ -624,7 +646,7 @@ class InheritedProtocolCollector { }, [&out] { out << ", "; }); out << " where " - << nominal->getGenericSignature()->getGenericParams().front()->getName() + << nominal->getGenericSignature().getGenericParams().front()->getName() << " : " << DummyProtocolName << " {}\n"; return true; } diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index 46559f1bd91ea..f886dfc317084 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftFrontendTool STATIC FrontendTool.cpp ImportedModules.cpp @@ -27,3 +27,5 @@ target_link_libraries(swiftFrontendTool PRIVATE swiftSILGen swiftSILOptimizer swiftTBDGen) + +set_swift_llvm_is_available(swiftFrontendTool) diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index c72f3ff91d0a1..ed1e921362533 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -48,6 +48,7 @@ #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/UUID.h" +#include "swift/Basic/Version.h" #include "swift/Option/Options.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/AccumulatingDiagnosticConsumer.h" @@ -173,14 +174,12 @@ static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs, /// /// \see swift::printAsObjC static bool printAsObjCIfNeeded(StringRef outputPath, ModuleDecl *M, - StringRef bridgingHeader, bool moduleIsPublic) { + StringRef bridgingHeader) { if (outputPath.empty()) return false; return withOutputFile(M->getDiags(), outputPath, [&](raw_ostream &out) -> bool { - auto requiredAccess = moduleIsPublic ? AccessLevel::Public - : AccessLevel::Internal; - return printAsObjC(out, M, bridgingHeader, requiredAccess); + return printAsObjC(out, M, bridgingHeader); }); } @@ -413,12 +412,15 @@ static bool buildModuleFromInterface(CompilerInstance &Instance) { StringRef InputPath = FEOpts.InputsAndOutputs.getFilenameOfFirstInput(); StringRef PrebuiltCachePath = FEOpts.PrebuiltModuleCachePath; ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + StringRef ABIPath = Instance.getPrimarySpecificPathsForAtMostOnePrimary() + .SupplementaryOutputs.ABIDescriptorOutputPath; return ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( Instance.getSourceMgr(), Instance.getDiags(), Invocation.getSearchPathOptions(), Invocation.getLangOptions(), Invocation.getClangImporterOptions(), Invocation.getClangModuleCachePath(), PrebuiltCachePath, - Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), + FEOpts.BackupModuleInterfaceDir, + Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), ABIPath, FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), LoaderOpts, RequireOSSAModules_t(Invocation.getSILOptions())); @@ -682,63 +684,6 @@ static bool writeTBDIfNeeded(CompilerInstance &Instance) { return writeTBD(Instance.getMainModule(), TBDPath, tbdOpts); } -static std::string changeToLdAdd(StringRef ldHide) { - SmallString<64> SymbolBuffer; - llvm::raw_svector_ostream OS(SymbolBuffer); - auto Parts = ldHide.split("$hide$"); - assert(!Parts.first.empty()); - assert(!Parts.second.empty()); - OS << Parts.first << "$add$" << Parts.second; - return OS.str().str(); -} - -static bool writeLdAddCFileIfNeeded(CompilerInstance &Instance) { - const auto &Invocation = Instance.getInvocation(); - const auto &frontendOpts = Invocation.getFrontendOptions(); - if (!frontendOpts.InputsAndOutputs.isWholeModule()) - return false; - auto Path = Invocation.getLdAddCFileOutputPathForWholeModule(); - if (Path.empty()) - return false; - if (!frontendOpts.InputsAndOutputs.isWholeModule()) { - Instance.getDiags().diagnose(SourceLoc(), - diag::tbd_only_supported_in_whole_module); - return true; - } - if (!Invocation.getTBDGenOptions().ModuleInstallNameMapPath.empty()) { - Instance.getDiags().diagnose(SourceLoc(), - diag::linker_directives_choice_confusion); - return true; - } - auto tbdOpts = Invocation.getTBDGenOptions(); - tbdOpts.LinkerDirectivesOnly = true; - auto *module = Instance.getMainModule(); - auto ldSymbols = - getPublicSymbols(TBDGenDescriptor::forModule(module, tbdOpts)); - std::error_code EC; - llvm::raw_fd_ostream OS(Path, EC, llvm::sys::fs::F_None); - if (EC) { - Instance.getDiags().diagnose(SourceLoc(), diag::error_opening_output, Path, - EC.message()); - return true; - } - OS << "// Automatically generated C source file from the Swift compiler \n" - << "// to add removed symbols back to the high-level framework for deployment\n" - << "// targets prior to the OS version when these symbols were moved to\n" - << "// a low-level framework " << module->getName().str() << ".\n\n"; - unsigned Idx = 0; - for (auto &S: ldSymbols) { - SmallString<32> NameBuffer; - llvm::raw_svector_ostream NameOS(NameBuffer); - NameOS << "ldAdd_" << Idx; - OS << "extern const char " << NameOS.str() << " __asm(\"" << - changeToLdAdd(S) << "\");\n"; - OS << "const char " << NameOS.str() << " = 0;\n"; - ++ Idx; - } - return false; -} - static bool performCompileStepsPostSILGen(CompilerInstance &Instance, std::unique_ptr SM, ModuleOrSourceFile MSF, @@ -815,6 +760,7 @@ static void emitIndexData(const CompilerInstance &Instance) { /// anything past type-checking. static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( CompilerInstance &Instance) { + const auto &Context = Instance.getASTContext(); const auto &Invocation = Instance.getInvocation(); const FrontendOptions &opts = Invocation.getFrontendOptions(); @@ -833,7 +779,8 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( // failure does not mean skipping the rest. bool hadAnyError = false; - if (opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { + if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) && + opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { std::string BridgingHeaderPathForPrint; if (!opts.ImplicitObjCHeaderPath.empty()) { if (opts.BridgingHeaderDirForPrint.hasValue()) { @@ -849,10 +796,14 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( } hadAnyError |= printAsObjCIfNeeded( Invocation.getObjCHeaderOutputPathForAtMostOnePrimary(), - Instance.getMainModule(), BridgingHeaderPathForPrint, - Invocation.isModuleExternallyConsumed(Instance.getMainModule())); + Instance.getMainModule(), BridgingHeaderPathForPrint); } + // Only want the header if there's been any errors, ie. there's not much + // point outputting a swiftinterface for an invalid module + if (Context.hadError()) + return hadAnyError; + if (opts.InputsAndOutputs.hasModuleInterfaceOutputPath()) { hadAnyError |= printModuleInterfaceIfNeeded( Invocation.getModuleInterfaceOutputPathForWholeModule(), @@ -876,9 +827,6 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( { hadAnyError |= writeTBDIfNeeded(Instance); } - { - hadAnyError |= writeLdAddCFileIfNeeded(Instance); - } return hadAnyError; } @@ -995,9 +943,10 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { dumpAPIIfNeeded(Instance); } - if (!ctx.hadError() || opts.AllowModuleWithCompilerErrors) { - emitAnyWholeModulePostTypeCheckSupplementaryOutputs(Instance); - } + + // Contains the hadError checks internally, we still want to output the + // Objective-C header when there's errors and currently allowing them + emitAnyWholeModulePostTypeCheckSupplementaryOutputs(Instance); // Verify reference dependencies of the current compilation job. Note this // must be run *before* verifying diagnostics so that the former can be tested @@ -1038,7 +987,13 @@ static bool printSwiftVersion(const CompilerInvocation &Invocation) { static void printSingleFrontendOpt(llvm::opt::OptTable &table, options::ID id, llvm::raw_ostream &OS) { - if (table.getOption(id).hasFlag(options::FrontendOption)) { + if (table.getOption(id).hasFlag(options::FrontendOption) || + table.getOption(id).hasFlag(options::AutolinkExtractOption) || + table.getOption(id).hasFlag(options::ModuleWrapOption) || + table.getOption(id).hasFlag(options::SwiftIndentOption) || + table.getOption(id).hasFlag(options::SwiftAPIExtractOption) || + table.getOption(id).hasFlag(options::SwiftSymbolGraphExtractOption) || + table.getOption(id).hasFlag(options::SwiftAPIDigesterOption)) { auto name = StringRef(table.getOptionName(id)); if (!name.empty()) { OS << " \"" << name << "\",\n"; @@ -1116,7 +1071,6 @@ withSemanticAnalysis(CompilerInstance &Instance, FrontendObserver *observer, static bool performScanDependencies(CompilerInstance &Instance) { auto batchScanInput = Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; - ModuleDependenciesCache SingleUseCache; if (batchScanInput.empty()) { if (Instance.getInvocation().getFrontendOptions().ImportPrescan) return dependencies::prescanDependencies(Instance); @@ -1542,9 +1496,19 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance, SerializationOptions serializationOpts = Invocation.computeSerializationOptions(outs, Instance.getMainModule()); + // Infer if this is an emit-module job part of an incremental build, + // vs a partial emit-module job (with primary files) or other kinds. + // We may want to rely on a flag instead to differentiate them. + const bool isEmitModuleSeparately = + Action == FrontendOptions::ActionType::EmitModuleOnly && + MSF.is() && + Instance.getInvocation() + .getTypeCheckerOptions() + .SkipFunctionBodies == FunctionBodySkipping::NonInlinableWithoutTypes; const bool canEmitIncrementalInfoIntoModule = !serializationOpts.DisableCrossModuleIncrementalInfo && - (Action == FrontendOptions::ActionType::MergeModules); + (Action == FrontendOptions::ActionType::MergeModules || + isEmitModuleSeparately); if (canEmitIncrementalInfoIntoModule) { const auto alsoEmitDotFile = Instance.getInvocation() @@ -1944,6 +1908,25 @@ static void printTargetInfo(const CompilerInvocation &invocation, out << "}\n"; } +/// A PrettyStackTraceEntry to print frontend information useful for debugging. +class PrettyStackTraceFrontend : public llvm::PrettyStackTraceEntry { + const LangOptions &LangOpts; + +public: + PrettyStackTraceFrontend(const LangOptions &langOpts) + : LangOpts(langOpts) {} + + void print(llvm::raw_ostream &os) const override { + auto effective = LangOpts.EffectiveLanguageVersion; + if (effective != version::Version::getCurrentLanguageVersion()) { + os << "Compiling with effective version " << effective; + } else { + os << "Compiling with the current language version"; + } + os << "\n"; + }; +}; + int swift::performFrontend(ArrayRef Args, const char *Argv0, void *MainAddr, FrontendObserver *observer) { @@ -2037,6 +2020,8 @@ int swift::performFrontend(ArrayRef Args, return finishDiagProcessing(1, /*verifierEnabled*/ false); } + PrettyStackTraceFrontend frontendTrace(Invocation.getLangOptions()); + // Make an array of PrettyStackTrace objects to dump the configuration files // we used to parse the arguments. These are RAII objects, so they and the // buffers they refer to must be kept alive in order to be useful. (That is, diff --git a/lib/FrontendTool/TBD.cpp b/lib/FrontendTool/TBD.cpp index 8641a335c692f..7f92dfb938e77 100644 --- a/lib/FrontendTool/TBD.cpp +++ b/lib/FrontendTool/TBD.cpp @@ -84,7 +84,8 @@ static bool validateSymbols(DiagnosticEngine &diags, if (auto GV = dyn_cast(value)) { // Is this a symbol that should be listed? auto externallyVisible = - GV->hasExternalLinkage() && !GV->hasHiddenVisibility(); + (GV->hasExternalLinkage() || GV->hasCommonLinkage()) + && !GV->hasHiddenVisibility(); if (!GV->isDeclaration() && externallyVisible) { // Is it listed? if (!symbolSet.erase(name)) diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index 48ebc21981d30..d1955ec412590 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -1,7 +1,8 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftIDE STATIC CodeCompletion.cpp CodeCompletionCache.cpp + CodeCompletionDiagnostics.cpp CodeCompletionResultPrinter.cpp CommentConversion.cpp CompletionInstance.cpp @@ -11,6 +12,7 @@ add_swift_host_library(swiftIDE STATIC FuzzyStringMatcher.cpp Refactoring.cpp ModuleInterfacePrinting.cpp + ModuleSourceFileInfo.cpp REPLCodeCompletion.cpp SwiftSourceDocInfo.cpp SyntaxModel.cpp @@ -29,3 +31,4 @@ target_link_libraries(swiftIDE PRIVATE swiftParse swiftSema) +set_swift_llvm_is_available(swiftIDE) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index e03145948e0ee..922e87aee5015 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/IDE/CodeCompletion.h" +#include "CodeCompletionDiagnostics.h" #include "CodeCompletionResultBuilder.h" #include "ExprContextAnalysis.h" #include "swift/AST/ASTPrinter.h" @@ -33,6 +34,7 @@ #include "swift/Frontend/FrontendOptions.h" #include "swift/IDE/CodeCompletionCache.h" #include "swift/IDE/CodeCompletionResultPrinter.h" +#include "swift/IDE/ModuleSourceFileInfo.h" #include "swift/IDE/Utils.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Sema/IDETypeChecking.h" @@ -58,240 +60,8 @@ using namespace swift; using namespace ide; -using CommandWordsPairs = std::vector>; using NotRecommendedReason = CodeCompletionResult::NotRecommendedReason; -enum CodeCompletionCommandKind { - none, - keyword, - recommended, - recommendedover, - mutatingvariant, - nonmutatingvariant, -}; - -CodeCompletionCommandKind getCommandKind(StringRef Command) { -#define CHECK_CASE(KIND) \ - if (Command == #KIND) \ - return CodeCompletionCommandKind::KIND; - CHECK_CASE(keyword); - CHECK_CASE(recommended); - CHECK_CASE(recommendedover); - CHECK_CASE(mutatingvariant); - CHECK_CASE(nonmutatingvariant); -#undef CHECK_CASE - return CodeCompletionCommandKind::none; -} - -StringRef getCommandName(CodeCompletionCommandKind Kind) { -#define CHECK_CASE(KIND) \ - if (CodeCompletionCommandKind::KIND == Kind) { \ - static std::string Name(#KIND); \ - return Name; \ - } - CHECK_CASE(keyword) - CHECK_CASE(recommended) - CHECK_CASE(recommendedover) - CHECK_CASE(mutatingvariant); - CHECK_CASE(nonmutatingvariant); -#undef CHECK_CASE - llvm_unreachable("Cannot handle this Kind."); -} - -bool containsInterestedWords(StringRef Content, StringRef Splitter, - bool AllowWhitespace) { - do { - Content = Content.split(Splitter).second; - Content = AllowWhitespace ? Content.trim() : Content; -#define CHECK_CASE(KIND) \ -if (Content.startswith(#KIND)) \ -return true; - CHECK_CASE(keyword) - CHECK_CASE(recommended) - CHECK_CASE(recommendedover) - CHECK_CASE(mutatingvariant); - CHECK_CASE(nonmutatingvariant); -#undef CHECK_CASE - } while (!Content.empty()); - return false; -} - -void splitTextByComma(StringRef Text, std::vector& Subs) { - do { - auto Pair = Text.split(','); - auto Key = Pair.first.trim(); - if (!Key.empty()) - Subs.push_back(Key); - Text = Pair.second; - } while (!Text.empty()); -} - -namespace clang { -namespace comments { -class WordPairsArrangedViewer { - ArrayRef> Content; - std::vector ViewedText; - std::vector Words; - StringRef Key; - - bool isKeyViewed(StringRef K) { - return std::find(ViewedText.begin(), ViewedText.end(), K) != ViewedText.end(); - } - -public: - WordPairsArrangedViewer(ArrayRef> Content): - Content(Content) {} - - bool hasNext() { - Words.clear(); - bool Found = false; - for (auto P : Content) { - if (!Found && !isKeyViewed(P.first)) { - Key = P.first; - Found = true; - } - if (Found && P.first == Key) - Words.push_back(P.second); - } - return Found; - } - - std::pair> next() { - bool HasNext = hasNext(); - (void) HasNext; - assert(HasNext && "Have no more data."); - ViewedText.push_back(Key); - return std::make_pair(Key, llvm::makeArrayRef(Words)); - } -}; - -class ClangCommentExtractor : public ConstCommentVisitor { - CommandWordsPairs &Words; - const CommandTraits &Traits; - std::vector Parents; - - void visitChildren(const Comment* C) { - Parents.push_back(C); - for (auto It = C->child_begin(); It != C->child_end(); ++ It) - visit(*It); - Parents.pop_back(); - } - -public: - ClangCommentExtractor(CommandWordsPairs &Words, - const CommandTraits &Traits) : Words(Words), - Traits(Traits) {} -#define CHILD_VISIT(NAME) \ - void visit##NAME(const NAME *C) {\ - visitChildren(C);\ - } - CHILD_VISIT(FullComment) - CHILD_VISIT(ParagraphComment) -#undef CHILD_VISIT - - void visitInlineCommandComment(const InlineCommandComment *C) { - auto Command = C->getCommandName(Traits); - auto CommandKind = getCommandKind(Command); - if (CommandKind == CodeCompletionCommandKind::none) - return; - auto &Parent = Parents.back(); - for (auto CIT = std::find(Parent->child_begin(), Parent->child_end(), C) + 1; - CIT != Parent->child_end(); ++CIT) { - if (auto TC = dyn_cast(*CIT)) { - auto Text = TC->getText(); - std::vector Subs; - splitTextByComma(Text, Subs); - auto Kind = getCommandName(CommandKind); - for (auto S : Subs) - Words.push_back(std::make_pair(Kind, S)); - } else - break; - } - } -}; - -void getClangDocKeyword(ClangImporter &Importer, const Decl *D, - CommandWordsPairs &Words) { - ClangCommentExtractor Extractor(Words, Importer.getClangASTContext(). - getCommentCommandTraits()); - if (auto RC = Importer.getClangASTContext().getRawCommentForAnyRedecl(D)) { - auto RT = RC->getRawText(Importer.getClangASTContext().getSourceManager()); - if (containsInterestedWords(RT, "@", /*AllowWhitespace*/false)) { - FullComment* Comment = Importer.getClangASTContext(). - getLocalCommentForDeclUncached(D); - Extractor.visit(Comment); - } - } -} -} // end namespace comments -} // end namespace clang - -namespace swift { -namespace markup { -class SwiftDocWordExtractor : public MarkupASTWalker { - CommandWordsPairs &Pairs; - CodeCompletionCommandKind Kind; -public: - SwiftDocWordExtractor(CommandWordsPairs &Pairs) : - Pairs(Pairs), Kind(CodeCompletionCommandKind::none) {} - void visitKeywordField(const KeywordField *Field) override { - Kind = CodeCompletionCommandKind::keyword; - } - void visitRecommendedField(const RecommendedField *Field) override { - Kind = CodeCompletionCommandKind::recommended; - } - void visitRecommendedoverField(const RecommendedoverField *Field) override { - Kind = CodeCompletionCommandKind::recommendedover; - } - void visitMutatingvariantField(const MutatingvariantField *Field) override { - Kind = CodeCompletionCommandKind::mutatingvariant; - } - void visitNonmutatingvariantField(const NonmutatingvariantField *Field) override { - Kind = CodeCompletionCommandKind::nonmutatingvariant; - } - void visitText(const Text *Text) override { - if (Kind == CodeCompletionCommandKind::none) - return; - StringRef CommandName = getCommandName(Kind); - std::vector Subs; - splitTextByComma(Text->str(), Subs); - for (auto S : Subs) - Pairs.push_back(std::make_pair(CommandName, S)); - } -}; - -void getSwiftDocKeyword(const Decl* D, CommandWordsPairs &Words) { - auto Interested = false; - for (auto C : D->getRawComment(/*SerializedOK=*/false).Comments) { - if (containsInterestedWords(C.RawText, "-", /*AllowWhitespace*/true)) { - Interested = true; - break; - } - } - if (!Interested) - return; - static swift::markup::MarkupContext MC; - auto DC = getSingleDocComment(MC, D); - if (!DC) - return; - SwiftDocWordExtractor Extractor(Words); - for (auto Part : DC->getBodyNodes()) { - switch (Part->getKind()) { - case ASTNodeKind::KeywordField: - case ASTNodeKind::RecommendedField: - case ASTNodeKind::RecommendedoverField: - case ASTNodeKind::MutatingvariantField: - case ASTNodeKind::NonmutatingvariantField: - Extractor.walk(Part); - break; - default: - break; - } - } -} -} // end namespace markup -} // end namespace swift - using DeclFilter = std::function; static bool DefaultFilter(ValueDecl* VD, DeclVisibilityKind Kind) { return true; @@ -411,18 +181,18 @@ void CodeCompletionString::print(raw_ostream &OS) const { case ChunkKind::TypeIdUser: AnnotatedTextChunk = I->isAnnotation(); LLVM_FALLTHROUGH; - case ChunkKind::CallParameterName: - case ChunkKind::CallParameterInternalName: - case ChunkKind::CallParameterColon: + case ChunkKind::CallArgumentName: + case ChunkKind::CallArgumentInternalName: + case ChunkKind::CallArgumentColon: case ChunkKind::DeclAttrParamColon: - case ChunkKind::CallParameterType: - case ChunkKind::CallParameterClosureType: + case ChunkKind::CallArgumentType: + case ChunkKind::CallArgumentClosureType: case ChunkKind::GenericParameterName: if (AnnotatedTextChunk) OS << "['"; - else if (I->getKind() == ChunkKind::CallParameterInternalName) + else if (I->getKind() == ChunkKind::CallArgumentInternalName) OS << "("; - else if (I->getKind() == ChunkKind::CallParameterClosureType) + else if (I->getKind() == ChunkKind::CallArgumentClosureType) OS << "##"; for (char Ch : I->getText()) { if (Ch == '\n') @@ -432,12 +202,12 @@ void CodeCompletionString::print(raw_ostream &OS) const { } if (AnnotatedTextChunk) OS << "']"; - else if (I->getKind() == ChunkKind::CallParameterInternalName) + else if (I->getKind() == ChunkKind::CallArgumentInternalName) OS << ")"; break; case ChunkKind::OptionalBegin: - case ChunkKind::CallParameterBegin: - case ChunkKind::CallParameterTypeBegin: + case ChunkKind::CallArgumentBegin: + case ChunkKind::CallArgumentTypeBegin: case ChunkKind::GenericParameterBegin: OS << "{#"; break; @@ -461,7 +231,7 @@ void CodeCompletionString::print(raw_ostream &OS) const { OS << I->getText(); OS << "#]"; break; - case ChunkKind::CallParameterClosureExpr: + case ChunkKind::CallArgumentClosureExpr: OS << " {" << I->getText() << "|}"; break; case ChunkKind::BraceStmtWithCursor: @@ -708,9 +478,6 @@ void CodeCompletionResult::printPrefix(raw_ostream &OS) const { case SemanticContextKind::None: Prefix.append("None"); break; - case SemanticContextKind::ExpressionSpecific: - Prefix.append("ExprSpecific"); - break; case SemanticContextKind::Local: Prefix.append("Local"); break; @@ -732,6 +499,24 @@ void CodeCompletionResult::printPrefix(raw_ostream &OS) const { Prefix.append((Twine("[") + ModuleName + "]").str()); break; } + if (getFlair().toRaw()) { + Prefix.append("/Flair["); + bool isFirstFlair = true; +#define PRINT_FLAIR(KIND, NAME) \ + if (getFlair().contains(CodeCompletionFlairBit::KIND)) { \ + if (isFirstFlair) { isFirstFlair = false; } \ + else { Prefix.append(","); } \ + Prefix.append(NAME); \ + } + PRINT_FLAIR(ExpressionSpecific, "ExprSpecific"); + PRINT_FLAIR(SuperChain, "SuperChain"); + PRINT_FLAIR(ArgumentLabels, "ArgLabels"); + PRINT_FLAIR(CommonKeywordAtCurrentPosition, "CommonKeyword") + PRINT_FLAIR(RareKeywordAtCurrentPosition, "RareKeyword") + PRINT_FLAIR(RareTypeAtCurrentPosition, "RareType") + PRINT_FLAIR(ExpressionAtNonScriptOrMainFileScope, "ExprAtFileScope") + Prefix.append("]"); + } if (NotRecommended) Prefix.append("/NotRecommended"); if (IsSystem) @@ -757,22 +542,6 @@ void CodeCompletionResult::printPrefix(raw_ostream &OS) const { break; } - for (clang::comments::WordPairsArrangedViewer Viewer(DocWords); - Viewer.hasNext();) { - auto Pair = Viewer.next(); - Prefix.append("/"); - Prefix.append(Pair.first); - Prefix.append("["); - StringRef Sep = ", "; - for (auto KW : Pair.second) { - Prefix.append(KW); - Prefix.append(Sep); - } - for (unsigned I = 0, N = Sep.size(); I < N; ++I) - Prefix.pop_back(); - Prefix.append("]"); - } - Prefix.append(": "); while (Prefix.size() < 36) { Prefix.append(" "); @@ -786,6 +555,25 @@ void CodeCompletionResult::dump() const { llvm::errs() << "\n"; } +CodeCompletionResult * +CodeCompletionResult::withFlair(CodeCompletionFlair newFlair, + CodeCompletionResultSink &Sink) { + if (Kind == ResultKind::Declaration) { + return new (*Sink.Allocator) CodeCompletionResult( + getSemanticContext(), newFlair, getNumBytesToErase(), + getCompletionString(), getAssociatedDeclKind(), isSystem(), + getModuleName(), getSourceFilePath(), getNotRecommendedReason(), + getDiagnosticSeverity(), getDiagnosticMessage(), + getBriefDocComment(), getAssociatedUSRs(), getExpectedTypeRelation(), + isOperator() ? getOperatorKind() : CodeCompletionOperatorKind::None); + } else { + return new (*Sink.Allocator) CodeCompletionResult( + getKind(), getSemanticContext(), newFlair, getNumBytesToErase(), + getCompletionString(), getExpectedTypeRelation(), + isOperator() ? getOperatorKind() : CodeCompletionOperatorKind::None); + } +} + void CodeCompletionResultBuilder::withNestedGroup( CodeCompletionString::Chunk::ChunkKind Kind, llvm::function_ref body) { @@ -802,7 +590,9 @@ void CodeCompletionResultBuilder::addChunkWithText( void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) { assert(Kind == CodeCompletionResult::ResultKind::Declaration); + AssociatedDecl = D; + if (auto *ClangD = D->getClangDecl()) CurrentModule = ClangD->getImportedOwningModule(); // FIXME: macros @@ -821,6 +611,20 @@ void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) { if (D->getAttrs().getDeprecated(D->getASTContext())) setNotRecommended(NotRecommendedReason::Deprecated); + else if (D->getAttrs().getSoftDeprecated(D->getASTContext())) + setNotRecommended(NotRecommendedReason::SoftDeprecated); + + if (D->getClangNode()) { + if (auto *ClangD = D->getClangDecl()) { + const auto &ClangContext = ClangD->getASTContext(); + if (const clang::RawComment *RC = + ClangContext.getRawCommentForAnyRedecl(ClangD)) { + setBriefDocComment(RC->getBriefText(ClangContext)); + } + } + } else { + setBriefDocComment(AssociatedDecl->getBriefComment()); + } } namespace { @@ -891,20 +695,14 @@ class AnnotatedTypePrinter : public ASTPrinter { }; } // namespcae -void CodeCompletionResultBuilder::addCallParameter(Identifier Name, - Identifier LocalName, - Type Ty, - Type ContextTy, - bool IsVarArg, - bool IsInOut, - bool IsIUO, - bool isAutoClosure, - bool useUnderscoreLabel, - bool isLabeledTrailingClosure) { +void CodeCompletionResultBuilder::addCallArgument( + Identifier Name, Identifier LocalName, Type Ty, Type ContextTy, + bool IsVarArg, bool IsInOut, bool IsIUO, bool isAutoClosure, + bool useUnderscoreLabel, bool isLabeledTrailingClosure) { ++CurrentNestingLevel; using ChunkKind = CodeCompletionString::Chunk::ChunkKind; - addSimpleChunk(ChunkKind::CallParameterBegin); + addSimpleChunk(ChunkKind::CallArgumentBegin); if (shouldAnnotateResults()) { if (!Name.empty() || !LocalName.empty()) { @@ -912,48 +710,48 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, if (!Name.empty()) { addChunkWithText( - CodeCompletionString::Chunk::ChunkKind::CallParameterName, + CodeCompletionString::Chunk::ChunkKind::CallArgumentName, escapeKeyword(Name.str(), false, EscapedKeyword)); if (!LocalName.empty() && Name != LocalName) { addChunkWithTextNoCopy(ChunkKind::Text, " "); getLastChunk().setIsAnnotation(); - addChunkWithText(ChunkKind::CallParameterInternalName, + addChunkWithText(ChunkKind::CallArgumentInternalName, escapeKeyword(LocalName.str(), false, EscapedKeyword)); getLastChunk().setIsAnnotation(); } } else { assert(!LocalName.empty()); - addChunkWithTextNoCopy(ChunkKind::CallParameterName, "_"); + addChunkWithTextNoCopy(ChunkKind::CallArgumentName, "_"); getLastChunk().setIsAnnotation(); addChunkWithTextNoCopy(ChunkKind::Text, " "); getLastChunk().setIsAnnotation(); - addChunkWithText(ChunkKind::CallParameterInternalName, + addChunkWithText(ChunkKind::CallArgumentInternalName, escapeKeyword(LocalName.str(), false, EscapedKeyword)); } - addChunkWithTextNoCopy(ChunkKind::CallParameterColon, ": "); + addChunkWithTextNoCopy(ChunkKind::CallArgumentColon, ": "); } } else { if (!Name.empty()) { llvm::SmallString<16> EscapedKeyword; addChunkWithText( - CodeCompletionString::Chunk::ChunkKind::CallParameterName, + CodeCompletionString::Chunk::ChunkKind::CallArgumentName, escapeKeyword(Name.str(), false, EscapedKeyword)); addChunkWithTextNoCopy( - CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": "); + CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": "); } else if (useUnderscoreLabel) { addChunkWithTextNoCopy( - CodeCompletionString::Chunk::ChunkKind::CallParameterName, "_"); + CodeCompletionString::Chunk::ChunkKind::CallArgumentName, "_"); addChunkWithTextNoCopy( - CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": "); + CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": "); } else if (!LocalName.empty()) { // Use local (non-API) parameter name if we have nothing else. llvm::SmallString<16> EscapedKeyword; addChunkWithText( - CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName, + CodeCompletionString::Chunk::ChunkKind::CallArgumentInternalName, escapeKeyword(LocalName.str(), false, EscapedKeyword)); addChunkWithTextNoCopy( - CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": "); + CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": "); } } @@ -981,13 +779,13 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, if (ContextTy) PO.setBaseType(ContextTy); if (shouldAnnotateResults()) { - withNestedGroup(ChunkKind::CallParameterTypeBegin, [&]() { + withNestedGroup(ChunkKind::CallArgumentTypeBegin, [&]() { AnnotatedTypePrinter printer(*this); Ty->print(printer, PO); }); } else { std::string TypeName = Ty->getString(PO); - addChunkWithText(ChunkKind::CallParameterType, TypeName); + addChunkWithText(ChunkKind::CallArgumentType, TypeName); } // Look through optional types and type aliases to find out if we have @@ -1036,12 +834,12 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, OS << " in"; addChunkWithText( - CodeCompletionString::Chunk::ChunkKind::CallParameterClosureExpr, + CodeCompletionString::Chunk::ChunkKind::CallArgumentClosureExpr, OS.str()); } else { // Add the closure type. addChunkWithText( - CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType, + CodeCompletionString::Chunk::ChunkKind::CallArgumentClosureType, AFT->getString(PO)); } } @@ -1263,19 +1061,6 @@ CodeCompletionResult *CodeCompletionResultBuilder::takeResult() { switch (Kind) { case CodeCompletionResult::ResultKind::Declaration: { - StringRef BriefComment; - auto MaybeClangNode = AssociatedDecl->getClangNode(); - if (MaybeClangNode) { - if (auto *D = MaybeClangNode.getAsDecl()) { - const auto &ClangContext = D->getASTContext(); - if (const clang::RawComment *RC = - ClangContext.getRawCommentForAnyRedecl(D)) - BriefComment = RC->getBriefText(ClangContext); - } - } else { - BriefComment = AssociatedDecl->getBriefComment(); - } - StringRef ModuleName; if (CurrentModule) { if (Sink.LastModule.first == CurrentModule.getOpaqueValue()) { @@ -1293,31 +1078,45 @@ CodeCompletionResult *CodeCompletionResultBuilder::takeResult() { } } - return new (*Sink.Allocator) CodeCompletionResult( - SemanticContext, IsArgumentLabels, NumBytesToErase, CCS, AssociatedDecl, - ModuleName, NotRecReason, copyString(*Sink.Allocator, BriefComment), + CodeCompletionResult *result = new (*Sink.Allocator) CodeCompletionResult( + SemanticContext, Flair, NumBytesToErase, CCS, AssociatedDecl, + ModuleName, NotRecReason, copyString(*Sink.Allocator, BriefDocComment), copyAssociatedUSRs(*Sink.Allocator, AssociatedDecl), - copyArray(*Sink.Allocator, CommentWords), ExpectedTypeRelation); + ExpectedTypeRelation); + if (!result->isSystem()) + result->setSourceFilePath(getSourceFilePathForDecl(AssociatedDecl)); + if (NotRecReason != NotRecommendedReason::None) { + // FIXME: We should generate the message lazily. + if (const auto *VD = dyn_cast(AssociatedDecl)) { + CodeCompletionDiagnosticSeverity severity; + SmallString<256> message; + llvm::raw_svector_ostream messageOS(message); + if (!getCompletionDiagnostics(NotRecReason, VD, severity, messageOS)) + result->setDiagnostics(severity, + copyString(*Sink.Allocator, message)); + } + } + return result; } case CodeCompletionResult::ResultKind::Keyword: return new (*Sink.Allocator) CodeCompletionResult( - KeywordKind, SemanticContext, IsArgumentLabels, NumBytesToErase, + KeywordKind, SemanticContext, Flair, NumBytesToErase, CCS, ExpectedTypeRelation, copyString(*Sink.Allocator, BriefDocComment)); case CodeCompletionResult::ResultKind::BuiltinOperator: case CodeCompletionResult::ResultKind::Pattern: return new (*Sink.Allocator) CodeCompletionResult( - Kind, SemanticContext, IsArgumentLabels, NumBytesToErase, CCS, + Kind, SemanticContext, Flair, NumBytesToErase, CCS, ExpectedTypeRelation, CodeCompletionOperatorKind::None, copyString(*Sink.Allocator, BriefDocComment)); case CodeCompletionResult::ResultKind::Literal: assert(LiteralKind.hasValue()); return new (*Sink.Allocator) - CodeCompletionResult(*LiteralKind, SemanticContext, IsArgumentLabels, + CodeCompletionResult(*LiteralKind, SemanticContext, Flair, NumBytesToErase, CCS, ExpectedTypeRelation); } @@ -1348,8 +1147,8 @@ Optional CodeCompletionString::getFirstTextChunkIndex( switch (C.getKind()) { using ChunkKind = Chunk::ChunkKind; case ChunkKind::Text: - case ChunkKind::CallParameterName: - case ChunkKind::CallParameterInternalName: + case ChunkKind::CallArgumentName: + case ChunkKind::CallArgumentInternalName: case ChunkKind::GenericParameterName: case ChunkKind::LeftParen: case ChunkKind::LeftBracket: @@ -1361,7 +1160,7 @@ Optional CodeCompletionString::getFirstTextChunkIndex( case ChunkKind::BaseName: case ChunkKind::TypeIdSystem: case ChunkKind::TypeIdUser: - case ChunkKind::CallParameterBegin: + case ChunkKind::CallArgumentBegin: return i; case ChunkKind::Dot: case ChunkKind::ExclamationMark: @@ -1381,12 +1180,12 @@ Optional CodeCompletionString::getFirstTextChunkIndex( case ChunkKind::OverrideKeyword: case ChunkKind::EffectsSpecifierKeyword: case ChunkKind::DeclIntroducer: - case ChunkKind::CallParameterColon: - case ChunkKind::CallParameterTypeBegin: + case ChunkKind::CallArgumentColon: + case ChunkKind::CallArgumentTypeBegin: case ChunkKind::DeclAttrParamColon: - case ChunkKind::CallParameterType: - case ChunkKind::CallParameterClosureType: - case ChunkKind::CallParameterClosureExpr: + case ChunkKind::CallArgumentType: + case ChunkKind::CallArgumentClosureType: + case ChunkKind::CallArgumentClosureExpr: case ChunkKind::OptionalBegin: case ChunkKind::GenericParameterBegin: case ChunkKind::DynamicLookupMethodCallTail: @@ -1410,44 +1209,6 @@ CodeCompletionString::getFirstTextChunk(bool includeLeadingPunctuation) const { return StringRef(); } -void CodeCompletionString::getName(raw_ostream &OS) const { - auto FirstTextChunk = getFirstTextChunkIndex(); - int TextSize = 0; - if (FirstTextChunk.hasValue()) { - auto chunks = getChunks().slice(*FirstTextChunk); - - for (auto i = chunks.begin(), e = chunks.end(); i != e; ++i) { - using ChunkKind = Chunk::ChunkKind; - - bool shouldPrint = !i->isAnnotation(); - switch (i->getKind()) { - case ChunkKind::TypeAnnotation: - case ChunkKind::CallParameterClosureType: - case ChunkKind::CallParameterClosureExpr: - case ChunkKind::DeclAttrParamColon: - case ChunkKind::OptionalMethodCallTail: - continue; - case ChunkKind::TypeAnnotationBegin: { - auto level = i->getNestingLevel(); - do { ++i; } while (i != e && !i->endsPreviousNestedGroup(level)); - --i; - continue; - } - case ChunkKind::EffectsSpecifierKeyword: - shouldPrint = true; // Even when they're annotations. - break; - default: - break; - } - - if (i->hasText() && shouldPrint) { - TextSize += i->getText().size(); - OS << i->getText(); - } - } - } -} - void CodeCompletionContext::sortCompletionResults( MutableArrayRef Results) { struct ResultAndName { @@ -1462,7 +1223,7 @@ void CodeCompletionContext::sortCompletionResults( auto *result = Results[i]; nameCache[i].result = result; llvm::raw_string_ostream OS(nameCache[i].name); - result->getCompletionString()->getName(OS); + printCodeCompletionResultFilterName(*result, OS); OS.flush(); } @@ -1529,6 +1290,12 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { CodeCompletionResult::ResultKind::Keyword, SemanticContextKind::CurrentNominal, {}); + if (auto *AFD = dyn_cast(CurDeclContext)) { + if (AFD->getOverriddenDecl() != nullptr) { + Builder.addFlair(CodeCompletionFlairBit::CommonKeywordAtCurrentPosition); + } + } + Builder.setKeywordKind(CodeCompletionKeywordKind::kw_super); Builder.addKeyword("super"); Builder.addTypeAnnotation(ST, PrintOptions()); @@ -1669,6 +1436,8 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { void completeGenericRequirement() override; void completeAfterIfStmt(bool hasElse) override; void completeStmtLabel(StmtKind ParentKind) override; + void completeForEachPatternBeginning(bool hasTry, bool hasAwait) override; + void completeTypeAttrBeginning() override; void doneParsing() override; @@ -1809,6 +1578,78 @@ static bool canDeclContextHandleAsync(const DeclContext *DC) { return false; } +/// Returns \c true only if the completion is happening for top-level +/// declrarations. i.e.: +/// +/// if condition { +/// #false# +/// } +/// expr.#false# +/// +/// #true# +/// +/// struct S { +/// #false# +/// func foo() { +/// #false# +/// } +/// } +static bool isCodeCompletionAtTopLevel(const DeclContext *DC) { + if (DC->isModuleScopeContext()) + return true; + + // CC token at top-level is parsed as an expression. If the only element + // body of the TopLevelCodeDecl is a CodeCompletionExpr without a base + // expression, the user might be writing a top-level declaration. + if (const TopLevelCodeDecl *TLCD = dyn_cast(DC)) { + auto body = TLCD->getBody(); + if (!body || body->empty()) + return true; + if (body->getElements().size() > 1) + return false; + auto expr = body->getFirstElement().dyn_cast(); + if (!expr) + return false; + if (CodeCompletionExpr *CCExpr = dyn_cast(expr)) { + if (CCExpr->getBase() == nullptr) + return true; + } + } + + return false; +} + +/// Returns \c true if the completion is happening in local context such as +/// inside function bodies. i.e.: +/// +/// if condition { +/// #true# +/// } +/// expr.#true# +/// +/// #false# +/// +/// struct S { +/// #false# +/// func foo() { +/// #true# +/// } +/// } +static bool isCompletionDeclContextLocalContext(DeclContext *DC) { + if (!DC->isLocalContext()) + return false; + if (isCodeCompletionAtTopLevel(DC)) + return false; + return true; +} + +/// Return \c true if the completion happens at top-level of a library file. +static bool isCodeCompletionAtTopLevelOfLibraryFile(const DeclContext *DC) { + if (DC->getParentSourceFile()->isScriptMode()) + return false; + return isCodeCompletionAtTopLevel(DC); +} + /// Build completions by doing visible decl lookup from a context. class CompletionLookup final : public swift::VisibleDeclConsumer { CodeCompletionResultSink &Sink; @@ -1882,6 +1723,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { bool FoundFunctionsWithoutFirstKeyword = false; private: + bool isForCaching() const { + return Kind == LookupKind::ImportFromModule; + } + void foundFunction(const AbstractFunctionDecl *AFD) { FoundFunctionCalls = true; const DeclName Name = AFD->getName(); @@ -1905,16 +1750,6 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { FoundFunctionsWithoutFirstKeyword = true; } - void setClangDeclKeywords(const ValueDecl *VD, CommandWordsPairs &Pairs, - CodeCompletionResultBuilder &Builder) { - if (auto *CD = VD->getClangDecl()) { - clang::comments::getClangDocKeyword(*Importer, CD, Pairs); - } else { - swift::markup::getSwiftDocKeyword(VD, Pairs); - } - Builder.addDeclDocCommentWords(llvm::makeArrayRef(Pairs)); - } - /// Returns \c true if \p TAD is usable as a first type of a requirement in /// \c where clause for a context. /// \p selfTy must be a \c Self type of the context. @@ -2077,6 +1912,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { IncludeInstanceMembers = true; } + bool isHiddenModuleName(Identifier Name) { + return (Name.str().startswith("_") || + Name == Ctx.SwiftShimsModuleName || + Name.str() == SWIFT_ONONE_SUPPORT); + } + void addSubModuleNames(std::vector> &SubModuleNameVisibilityPairs) { for (auto &Pair : SubModuleNameVisibilityPairs) { @@ -2094,7 +1935,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } } - void collectImportedModules(llvm::StringSet<> &ImportedModules) { + void collectImportedModules(llvm::StringSet<> &directImportedModules, + llvm::StringSet<> &allImportedModules) { SmallVector Imported; SmallVector FurtherImported; CurrDeclContext->getParentSourceFile()->getImportedModules( @@ -2102,10 +1944,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly}); + + for (ImportedModule &imp : Imported) + directImportedModules.insert(imp.importedModule->getNameStr()); + while (!Imported.empty()) { ModuleDecl *MD = Imported.back().importedModule; Imported.pop_back(); - if (!ImportedModules.insert(MD->getNameStr()).second) + if (!allImportedModules.insert(MD->getNameStr()).second) continue; FurtherImported.clear(); MD->getImportedModules(FurtherImported, @@ -2136,23 +1982,24 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { SmallVector ModuleNames; Ctx.getVisibleTopLevelModuleNames(ModuleNames); - llvm::StringSet<> ImportedModules; - collectImportedModules(ImportedModules); + llvm::StringSet<> directImportedModules; + llvm::StringSet<> allImportedModules; + collectImportedModules(directImportedModules, allImportedModules); auto mainModuleName = CurrModule->getName(); for (auto ModuleName : ModuleNames) { - if (ModuleName.str().startswith("_") || - ModuleName == mainModuleName || - ModuleName == Ctx.SwiftShimsModuleName || - ModuleName.str() == SWIFT_ONONE_SUPPORT) + if (ModuleName == mainModuleName || isHiddenModuleName(ModuleName)) continue; auto MD = ModuleDecl::create(ModuleName, Ctx); Optional Reason = None; // Imported modules are not recommended. - if (ImportedModules.count(MD->getNameStr()) != 0) + if (directImportedModules.contains(MD->getNameStr())) { Reason = NotRecommendedReason::RedundantImport; + } else if (allImportedModules.contains(MD->getNameStr())) { + Reason = NotRecommendedReason::RedundantImportIndirect; + } addModuleName(MD, Reason); } @@ -2171,9 +2018,6 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return SemanticContextKind::Local; case DeclVisibilityKind::MemberOfCurrentNominal: - if (IsSuperRefExpr && - CurrentMethod && CurrentMethod->getOverriddenDecl() == D) - return SemanticContextKind::ExpressionSpecific; return SemanticContextKind::CurrentNominal; case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal: @@ -2484,9 +2328,16 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { // For everything else, substitute in the base type. auto Subs = MaybeNominalType->getMemberSubstitutionMap(CurrModule, VD); - // Pass in DesugarMemberTypes so that we see the actual - // concrete type witnesses instead of type alias types. - T = T.subst(Subs, SubstFlags::DesugarMemberTypes); + // For a GenericFunctionType, we only want to substitute the + // param/result types, as otherwise we might end up with a bad generic + // signature if there are UnresolvedTypes present in the base type. Note + // we pass in DesugarMemberTypes so that we see the actual concrete type + // witnesses instead of type alias types. + if (auto *GFT = T->getAs()) { + T = GFT->substGenericArgs(Subs, SubstFlags::DesugarMemberTypes); + } else { + T = T.subst(Subs, SubstFlags::DesugarMemberTypes); + } } } @@ -2520,6 +2371,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { auto isolation = getActorIsolation(const_cast(VD)); switch (isolation.getKind()) { + case ActorIsolation::DistributedActorInstance: { + // TODO: implicitlyThrowing here for distributed + LLVM_FALLTHROUGH; // continue the ActorInstance checks + } case ActorIsolation::ActorInstance: { if (IsCrossActorReference) { implicitlyAsync = true; @@ -2527,31 +2382,46 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } break; } - case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: - // TODO: Implement. + // For "unsafe" global actor isolation, automatic 'async' only happens + // if the context has adopted concurrency. + if (!CanCurrDeclContextHandleAsync && + !completionContextUsesConcurrencyFeatures(CurrDeclContext) && + !CurrDeclContext->getParentModule()->isConcurrencyChecked()) { + return; + } + LLVM_FALLTHROUGH; + case ActorIsolation::GlobalActor: { + auto contextIsolation = getActorIsolationOfContext( + const_cast(CurrDeclContext)); + if (contextIsolation != isolation) { + implicitlyAsync = true; + } break; + } case ActorIsolation::Unspecified: case ActorIsolation::Independent: return; } // If the reference is 'async', all types must be 'Sendable'. - if (implicitlyAsync && T) { + if (CurrDeclContext->getParentModule()->isConcurrencyChecked() && + implicitlyAsync && T) { + auto *M = CurrDeclContext->getParentModule(); if (isa(VD)) { - if (!isSendableType(CurrDeclContext, T)) { + if (!isSendableType(M, T)) { NotRecommended = NotRecommendedReason::CrossActorReference; } } else { assert(isa(VD) || isa(VD)); // Check if the result and the param types are all 'Sendable'. auto *AFT = T->castTo(); - if (!isSendableType(CurrDeclContext, AFT->getResult())) { + if (!isSendableType(M, AFT->getResult())) { NotRecommended = NotRecommendedReason::CrossActorReference; } else { for (auto ¶m : AFT->getParams()) { Type paramType = param.getPlainType(); - if (!isSendableType(CurrDeclContext, paramType)) { + if (!isSendableType(M, paramType)) { NotRecommended = NotRecommendedReason::CrossActorReference; break; } @@ -2583,18 +2453,17 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } bool implicitlyAsync = false; analyzeActorIsolation(VD, VarType, implicitlyAsync, NotRecommended); - if (!NotRecommended && implicitlyAsync && !CanCurrDeclContextHandleAsync) { + if (!isForCaching() && !NotRecommended && implicitlyAsync && + !CanCurrDeclContextHandleAsync) { NotRecommended = NotRecommendedReason::InvalidAsyncContext; } - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(VD, Reason, dynamicLookupInfo), expectedTypeContext); Builder.setAssociatedDecl(VD); addLeadingDot(Builder); addValueBaseName(Builder, Name); - setClangDeclKeywords(VD, Pairs, Builder); if (NotRecommended) Builder.setNotRecommended(*NotRecommended); @@ -2629,7 +2498,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Builder.addAnnotatedAsync(); if (isUnresolvedMemberIdealType(VarType)) - Builder.setSemanticContext(SemanticContextKind::ExpressionSpecific); + Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); } static bool hasInterestingDefaultValues(const AbstractFunctionDecl *func) { @@ -2720,11 +2589,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) contextTy = typeContext->getDeclaredTypeInContext(); - Builder.addCallParameter(argName, bodyName, - eraseArchetypes(paramTy, genericSig), contextTy, - isVariadic, isInOut, isIUO, isAutoclosure, - /*useUnderscoreLabel=*/false, - /*isLabeledTrailingClosure=*/false); + Builder.addCallArgument(argName, bodyName, + eraseArchetypes(paramTy, genericSig), contextTy, + isVariadic, isInOut, isIUO, isAutoclosure, + /*useUnderscoreLabel=*/false, + /*isLabeledTrailingClosure=*/false); modifiedBuilder = true; NeedComma = true; @@ -2767,7 +2636,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (ParentKind != StmtKind::If && ParentKind != StmtKind::Guard) return; CodeCompletionResultBuilder Builder(Sink, CodeCompletionResult::ResultKind::Keyword, - SemanticContextKind::ExpressionSpecific, expectedTypeContext); + // FIXME: SemanticContextKind::Local is not correct. + // Use 'None' (and fix prioritization) or introduce a new context. + SemanticContextKind::Local, expectedTypeContext); + Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); Builder.addBaseName("available"); Builder.addLeftParen(); Builder.addSimpleTypedParameter("Platform", /*IsVarArg=*/true); @@ -2847,21 +2719,22 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (SD) genericSig = SD->getGenericSignatureOfContext(); - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, SD ? CodeCompletionResult::ResultKind::Declaration : CodeCompletionResult::ResultKind::Pattern, SemanticContext ? *SemanticContext : getSemanticContextKind(SD), expectedTypeContext); - if (SD) { + if (SD) Builder.setAssociatedDecl(SD); - setClangDeclKeywords(SD, Pairs, Builder); - } - if (!HaveLParen) + if (!HaveLParen) { Builder.addLeftBracket(); - else + } else { + // Add 'ArgumentLabels' only if it has '['. Without existing '[', + // consider it suggesting 'subscript' itself, not call arguments for it. + Builder.addFlair(CodeCompletionFlairBit::ArgumentLabels); Builder.addAnnotatedLeftBracket(); + } ArrayRef declParams; if (SD) declParams = SD->getIndices()->getArray(); @@ -2888,18 +2761,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { // Add the pattern, possibly including any default arguments. auto addPattern = [&](ArrayRef declParams = {}, bool includeDefaultArgs = true) { - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, AFD ? CodeCompletionResult::ResultKind::Declaration : CodeCompletionResult::ResultKind::Pattern, SemanticContext ? *SemanticContext : getSemanticContextKind(AFD), expectedTypeContext); - Builder.setIsArgumentLabels(); - if (AFD) { + Builder.addFlair(CodeCompletionFlairBit::ArgumentLabels); + if (AFD) Builder.setAssociatedDecl(AFD); - setClangDeclKeywords(AFD, Pairs, Builder); - } if (!HaveLParen) Builder.addLeftParen(); @@ -2925,7 +2795,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { else addTypeAnnotation(Builder, AFT->getResult(), genericSig); - if (AFT->isAsync() && !CanCurrDeclContextHandleAsync) { + if (!isForCaching() && AFT->isAsync() && !CanCurrDeclContextHandleAsync) { Builder.setNotRecommended(NotRecommendedReason::InvalidAsyncContext); } }; @@ -3039,7 +2909,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { bool implictlyAsync = false; analyzeActorIsolation(FD, AFT, implictlyAsync, NotRecommended); - if (!NotRecommended && !IsImplicitlyCurriedInstanceMethod && + if (!isForCaching() && !NotRecommended && + !IsImplicitlyCurriedInstanceMethod && ((AFT && AFT->isAsync()) || implictlyAsync) && !CanCurrDeclContextHandleAsync) { NotRecommended = NotRecommendedReason::InvalidAsyncContext; @@ -3048,14 +2919,16 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { // Add the method, possibly including any default arguments. auto addMethodImpl = [&](bool includeDefaultArgs = true, bool trivialTrailingClosure = false) { - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(FD, Reason, dynamicLookupInfo), expectedTypeContext); - setClangDeclKeywords(FD, Pairs, Builder); Builder.setAssociatedDecl(FD); + if (IsSuperRefExpr && CurrentMethod && + CurrentMethod->getOverriddenDecl() == FD) + Builder.addFlair(CodeCompletionFlairBit::SuperChain); + if (NotRecommended) Builder.setNotRecommended(*NotRecommended); @@ -3145,7 +3018,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { ResultType, expectedTypeContext, CurrDeclContext)); if (isUnresolvedMemberIdealType(ResultType)) - Builder.setSemanticContext(SemanticContextKind::ExpressionSpecific); + Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); if (!IsImplicitlyCurriedInstanceMethod && expectedTypeContext.requiresNonVoid() && @@ -3192,19 +3065,24 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { // Add the constructor, possibly including any default arguments. auto addConstructorImpl = [&](bool includeDefaultArgs = true) { - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(CD, Reason, dynamicLookupInfo), expectedTypeContext); - setClangDeclKeywords(CD, Pairs, Builder); Builder.setAssociatedDecl(CD); + + if (IsSuperRefExpr && CurrentMethod && + CurrentMethod->getOverriddenDecl() == CD) + Builder.addFlair(CodeCompletionFlairBit::SuperChain); + if (needInit) { assert(addName.empty()); addLeadingDot(Builder); Builder.addBaseName("init"); } else if (!addName.empty()) { Builder.addBaseName(addName.str()); + } else { + Builder.addFlair(CodeCompletionFlairBit::ArgumentLabels); } if (!ConstructorType) { @@ -3239,7 +3117,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { addTypeAnnotation(Builder, *Result, CD->getGenericSignatureOfContext()); } - if (ConstructorType->isAsync() && !CanCurrDeclContextHandleAsync) { + if (!isForCaching() && ConstructorType->isAsync() && + !CanCurrDeclContextHandleAsync) { Builder.setNotRecommended(NotRecommendedReason::InvalidAsyncContext); } }; @@ -3290,16 +3169,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { bool implictlyAsync = false; analyzeActorIsolation(SD, subscriptType, implictlyAsync, NotRecommended); - if (!NotRecommended && implictlyAsync && !CanCurrDeclContextHandleAsync) { + if (!isForCaching() && !NotRecommended && implictlyAsync && + !CanCurrDeclContextHandleAsync) { NotRecommended = NotRecommendedReason::InvalidAsyncContext; } - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(SD, Reason, dynamicLookupInfo), expectedTypeContext); Builder.setAssociatedDecl(SD); - setClangDeclKeywords(SD, Pairs, Builder); if (NotRecommended) Builder.setNotRecommended(*NotRecommended); @@ -3334,13 +3212,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addNominalTypeRef(const NominalTypeDecl *NTD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(NTD, Reason, dynamicLookupInfo), expectedTypeContext); Builder.setAssociatedDecl(NTD); - setClangDeclKeywords(NTD, Pairs, Builder); addLeadingDot(Builder); Builder.addBaseName(NTD->getName().str()); @@ -3362,13 +3238,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addTypeAliasRef(const TypeAliasDecl *TAD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(TAD, Reason, dynamicLookupInfo), expectedTypeContext); Builder.setAssociatedDecl(TAD); - setClangDeclKeywords(TAD, Pairs, Builder); addLeadingDot(Builder); Builder.addBaseName(TAD->getName().str()); if (auto underlyingType = TAD->getUnderlyingType()) { @@ -3392,11 +3266,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addGenericTypeParamRef(const GenericTypeParamDecl *GP, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(GP, Reason, dynamicLookupInfo), expectedTypeContext); - setClangDeclKeywords(GP, Pairs, Builder); Builder.setAssociatedDecl(GP); addLeadingDot(Builder); Builder.addBaseName(GP->getName().str()); @@ -3406,11 +3278,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addAssociatedTypeRef(const AssociatedTypeDecl *AT, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(AT, Reason, dynamicLookupInfo), expectedTypeContext); - setClangDeclKeywords(AT, Pairs, Builder); Builder.setAssociatedDecl(AT); addLeadingDot(Builder); Builder.addBaseName(AT->getName().str()); @@ -3437,14 +3307,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { EED->shouldHideFromEditor()) return; - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, - HasTypeContext ? SemanticContextKind::ExpressionSpecific - : getSemanticContext(EED, Reason, dynamicLookupInfo), + getSemanticContext(EED, Reason, dynamicLookupInfo), expectedTypeContext); Builder.setAssociatedDecl(EED); - setClangDeclKeywords(EED, Pairs, Builder); + addLeadingDot(Builder); addValueBaseName(Builder, EED->getBaseIdentifier()); @@ -3468,7 +3336,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { addTypeAnnotation(Builder, EnumType, EED->getGenericSignatureOfContext()); if (isUnresolvedMemberIdealType(EnumType)) - Builder.setSemanticContext(SemanticContextKind::ExpressionSpecific); + Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); } void addKeyword(StringRef Name, Type TypeAnnotation = Type(), @@ -3490,11 +3358,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addKeyword(StringRef Name, StringRef TypeAnnotation, CodeCompletionKeywordKind KeyKind - = CodeCompletionKeywordKind::None) { + = CodeCompletionKeywordKind::None, + CodeCompletionFlair flair = {}) { CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Keyword, SemanticContextKind::None, expectedTypeContext); + Builder.addFlair(flair); addLeadingDot(Builder); Builder.addKeyword(Name); Builder.setKeywordKind(KeyKind); @@ -3545,12 +3415,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (dropCurryLevel && isDuplicate(AFD, funcTy)) return true; - CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Declaration, getSemanticContext(AFD, Reason, dynamicLookupInfo), expectedTypeContext); - setClangDeclKeywords(AFD, Pairs, Builder); Builder.setAssociatedDecl(AFD); // Base name @@ -4077,7 +3945,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Type contextTy; if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) contextTy = typeContext->getDeclaredTypeInContext(); - builder.addCallParameter(Identifier(), RHSType, contextTy); + builder.addCallArgument(Identifier(), RHSType, contextTy); addTypeAnnotation(builder, resultType); } @@ -4102,7 +3970,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Type contextTy; if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) contextTy = typeContext->getDeclaredTypeInContext(); - builder.addCallParameter(Identifier(), RHSType, contextTy); + builder.addCallArgument(Identifier(), RHSType, contextTy); } if (resultType) addTypeAnnotation(builder, resultType); @@ -4224,7 +4092,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { // Check for conformance to the literal protocol. if (auto *NTD = T->getAnyNominal()) { SmallVector conformances; - if (NTD->lookupConformance(CurrModule, P, conformances)) { + if (NTD->lookupConformance(P, conformances)) { addTypeAnnotation(builder, T); builder.setExpectedTypeRelation(typeRelation); return; @@ -4244,6 +4112,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { /// Add '#file', '#line', et at. void addPoundLiteralCompletions(bool needPound) { + CodeCompletionFlair flair; + if (isCodeCompletionAtTopLevelOfLibraryFile(CurrDeclContext)) + flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope; + auto addFromProto = [&](MagicIdentifierLiteralExpr::Kind magicKind, Optional literalKind) { CodeCompletionKeywordKind kwKind; @@ -4268,13 +4140,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (!literalKind) { // Pointer type - addKeyword(name, "UnsafeRawPointer", kwKind); + addKeyword(name, "UnsafeRawPointer", kwKind, flair); return; } CodeCompletionResultBuilder builder( Sink, CodeCompletionResult::ResultKind::Keyword, SemanticContextKind::None, {}); + builder.addFlair(flair); builder.setLiteralKind(literalKind.getValue()); builder.setKeywordKind(kwKind); builder.addBaseName(name); @@ -4296,6 +4169,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addValueLiteralCompletions() { auto &context = CurrDeclContext->getASTContext(); + CodeCompletionFlair flair; + if (isCodeCompletionAtTopLevelOfLibraryFile(CurrDeclContext)) + flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope; + auto addFromProto = [&]( CodeCompletionLiteralKind kind, llvm::function_ref consumer, @@ -4304,6 +4181,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { CodeCompletionResultBuilder builder(Sink, CodeCompletionResult::Literal, SemanticContextKind::None, {}); builder.setLiteralKind(kind); + builder.addFlair(flair); consumer(builder); addTypeRelationFromProtocol(builder, kind); @@ -4345,34 +4223,38 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addRightBracket(); }); - auto floatType = context.getFloatType(); - addFromProto(LK::ColorLiteral, [&](Builder &builder) { - builder.addBaseName("#colorLiteral"); - builder.addLeftParen(); - builder.addCallParameter(context.getIdentifier("red"), floatType); - builder.addComma(); - builder.addCallParameter(context.getIdentifier("green"), floatType); - builder.addComma(); - builder.addCallParameter(context.getIdentifier("blue"), floatType); - builder.addComma(); - builder.addCallParameter(context.getIdentifier("alpha"), floatType); - builder.addRightParen(); - }); - - auto stringType = context.getStringType(); - addFromProto(LK::ImageLiteral, [&](Builder &builder) { - builder.addBaseName("#imageLiteral"); - builder.addLeftParen(); - builder.addCallParameter(context.getIdentifier("resourceName"), - stringType); - builder.addRightParen(); - }); + // Optionally add object literals. + if (CompletionContext->includeObjectLiterals()) { + auto floatType = context.getFloatType(); + addFromProto(LK::ColorLiteral, [&](Builder &builder) { + builder.addBaseName("#colorLiteral"); + builder.addLeftParen(); + builder.addCallArgument(context.getIdentifier("red"), floatType); + builder.addComma(); + builder.addCallArgument(context.getIdentifier("green"), floatType); + builder.addComma(); + builder.addCallArgument(context.getIdentifier("blue"), floatType); + builder.addComma(); + builder.addCallArgument(context.getIdentifier("alpha"), floatType); + builder.addRightParen(); + }); + + auto stringType = context.getStringType(); + addFromProto(LK::ImageLiteral, [&](Builder &builder) { + builder.addBaseName("#imageLiteral"); + builder.addLeftParen(); + builder.addCallArgument(context.getIdentifier("resourceName"), + stringType); + builder.addRightParen(); + }); + } // Add tuple completion (item, item). { CodeCompletionResultBuilder builder(Sink, CodeCompletionResult::Literal, SemanticContextKind::None, {}); builder.setLiteralKind(LK::Tuple); + builder.addFlair(flair); builder.addLeftParen(); builder.addSimpleNamedParameter("values"); @@ -4488,6 +4370,23 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { addObjCPoundKeywordCompletions(/*needPound=*/true); } + /// Returns \c true if \p VD is an initializer on the \c Optional or \c + /// Id_OptionalNilComparisonType type from the Swift stdlib. + static bool isInitializerOnOptional(Type T, ValueDecl *VD) { + bool IsOptionalType = false; + IsOptionalType |= static_cast(T->getOptionalObjectType()); + if (auto *NTD = T->getAnyNominal()) { + IsOptionalType |= NTD->getBaseIdentifier() == + VD->getASTContext().Id_OptionalNilComparisonType; + } + if (IsOptionalType && VD->getModuleContext()->isStdlibModule() && + isa(VD)) { + return true; + } else { + return false; + } + } + void getUnresolvedMemberCompletions(Type T) { if (!T->mayHaveMembers()) return; @@ -4505,16 +4404,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { // We can only say .foo where foo is a static member of the contextual // type and has the same type (or if the member is a function, then the // same result type) as the contextual type. - FilteredDeclConsumer consumer(*this, [=](ValueDecl *VD, - DeclVisibilityKind Reason) { - if (T->getOptionalObjectType() && - VD->getModuleContext()->isStdlibModule()) { - // In optional context, ignore '.init()', 'init(nilLiteral:)', - if (isa(VD)) - return false; - } - return true; - }); + FilteredDeclConsumer consumer( + *this, [=](ValueDecl *VD, DeclVisibilityKind Reason) { + // In optional context, ignore '.init()', 'init(nilLiteral:)', + return !isInitializerOnOptional(T, VD); + }); auto baseType = MetatypeType::get(T); llvm::SaveAndRestore SaveLook(Kind, LookupKind::ValueExpr); @@ -4526,6 +4420,21 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { /*includeProtocolExtensionMembers*/true); } + /// Complete all enum members declared on \p T. + void getEnumElementPatternCompletions(Type T) { + if (!isa_and_nonnull(T->getAnyNominal())) + return; + + auto baseType = MetatypeType::get(T); + llvm::SaveAndRestore SaveLook(Kind, LookupKind::EnumElement); + llvm::SaveAndRestore SaveType(ExprType, baseType); + llvm::SaveAndRestore SaveUnresolved(IsUnresolvedMember, true); + lookupVisibleMemberDecls(*this, baseType, CurrDeclContext, + /*includeInstanceMembers=*/false, + /*includeDerivedRequirements=*/false, + /*includeProtocolExtensionMembers=*/true); + } + void getUnresolvedMemberCompletions(ArrayRef Types) { NeedLeadingDot = !HaveDot; @@ -4558,13 +4467,16 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { continue; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Pattern, - SemanticContextKind::ExpressionSpecific, {}); - Builder.addCallParameter(Arg->getLabel(), Identifier(), - Arg->getPlainType(), ContextType, - Arg->isVariadic(), Arg->isInOut(), - /*isIUO=*/false, Arg->isAutoClosure(), - /*useUnderscoreLabel=*/true, - isLabeledTrailingClosure); + // FIXME: SemanticContextKind::Local is not correct. + // Use 'None' (and fix prioritization) or introduce a new context. + SemanticContextKind::Local, {}); + Builder.addCallArgument(Arg->getLabel(), Identifier(), + Arg->getPlainType(), ContextType, + Arg->isVariadic(), Arg->isInOut(), + /*isIUO=*/false, Arg->isAutoClosure(), + /*useUnderscoreLabel=*/true, + isLabeledTrailingClosure); + Builder.addFlair(CodeCompletionFlairBit::ArgumentLabels); auto Ty = Arg->getPlainType(); if (Arg->isInOut()) { Ty = InOutType::get(Ty); @@ -4604,7 +4516,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (!genericSig) return; - for (auto GPT : genericSig->getGenericParams()) { + for (auto GPT : genericSig.getGenericParams()) { addGenericTypeParamRef(GPT->getDecl(), DeclVisibilityKind::GenericParameter, {}); } @@ -4642,6 +4554,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { static bool canUseAttributeOnDecl(DeclAttrKind DAK, bool IsInSil, bool IsConcurrencyEnabled, + bool IsDistributedEnabled, Optional DK) { if (DeclAttribute::isUserInaccessible(DAK)) return false; @@ -4653,6 +4566,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return false; if (!IsConcurrencyEnabled && DeclAttribute::isConcurrencyOnly(DAK)) return false; + if (!IsDistributedEnabled && DeclAttribute::isDistributedOnly(DAK)) + return false; if (!DK.hasValue()) return true; return DeclAttribute::canAttributeAppearOnDeclKind(DAK, DK.getValue()); @@ -4671,9 +4586,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } } bool IsConcurrencyEnabled = Ctx.LangOpts.EnableExperimentalConcurrency; + bool IsDistributedEnabled = Ctx.LangOpts.EnableExperimentalDistributed; std::string Description = TargetName.str() + " Attribute"; #define DECL_ATTR(KEYWORD, NAME, ...) \ - if (canUseAttributeOnDecl(DAK_##NAME, IsInSil, IsConcurrencyEnabled, DK)) \ + if (canUseAttributeOnDecl(DAK_##NAME, IsInSil, \ + IsConcurrencyEnabled, \ + IsDistributedEnabled, \ + DK)) \ addDeclAttrKeyword(#KEYWORD, Description); #include "swift/AST/Attr.def" } @@ -4698,6 +4617,22 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } } + void getTypeAttributeKeywordCompletions() { + auto addTypeAttr = [&](StringRef Name) { + CodeCompletionResultBuilder Builder( + Sink, + CodeCompletionResult::ResultKind::Keyword, + SemanticContextKind::None, expectedTypeContext); + Builder.addAttributeKeyword(Name, "Type Attribute"); + }; + addTypeAttr("autoclosure"); + addTypeAttr("convention(swift)"); + addTypeAttr("convention(block)"); + addTypeAttr("convention(c)"); + addTypeAttr("convention(thin)"); + addTypeAttr("escaping"); + } + void collectPrecedenceGroups() { assert(CurrDeclContext); @@ -5229,7 +5164,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { switch (Reason) { case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal: case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal: - if (!C->isFinal()) + if (!C->isSemanticallyFinal()) needRequired = true; break; case DeclVisibilityKind::MemberOfSuper: @@ -5265,7 +5200,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { if (D->shouldHideFromEditor()) return; - if (D->isFinal()) + if (D->isSemanticallyFinal()) return; bool hasIntroducer = hasFuncIntroducer || @@ -5820,6 +5755,22 @@ void CodeCompletionCallbacksImpl::completeStmtLabel(StmtKind ParentKind) { ParentStmtKind = ParentKind; } +void CodeCompletionCallbacksImpl::completeForEachPatternBeginning( + bool hasTry, bool hasAwait) { + CurDeclContext = P.CurDeclContext; + Kind = CompletionKind::ForEachPatternBeginning; + ParsedKeywords.clear(); + if (hasTry) + ParsedKeywords.emplace_back("try"); + if (hasAwait) + ParsedKeywords.emplace_back("await"); +} + +void CodeCompletionCallbacksImpl::completeTypeAttrBeginning() { + CurDeclContext = P.CurDeclContext; + Kind = CompletionKind::TypeAttrBeginning; +} + static bool isDynamicLookup(Type T) { return T->getRValueType()->isAnyObject(); } @@ -5834,21 +5785,126 @@ static void addKeyword(CodeCompletionResultSink &Sink, StringRef Name, CodeCompletionKeywordKind Kind, StringRef TypeAnnotation = "", CodeCompletionResult::ExpectedTypeRelation TypeRelation = - CodeCompletionResult::ExpectedTypeRelation::NotApplicable) { + CodeCompletionResult::ExpectedTypeRelation::NotApplicable, + CodeCompletionFlair Flair = {}) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResult::ResultKind::Keyword, SemanticContextKind::None, {}); Builder.setKeywordKind(Kind); Builder.addKeyword(Name); + Builder.addFlair(Flair); if (!TypeAnnotation.empty()) Builder.addTypeAnnotation(TypeAnnotation); Builder.setExpectedTypeRelation(TypeRelation); } -static void addDeclKeywords(CodeCompletionResultSink &Sink, - bool IsConcurrencyEnabled) { +static void addDeclKeywords(CodeCompletionResultSink &Sink, DeclContext *DC, + bool IsConcurrencyEnabled, + bool IsDistributedEnabled) { + auto isTypeDeclIntroducer = [](CodeCompletionKeywordKind Kind, + Optional DAK) -> bool { + switch (Kind) { + case CodeCompletionKeywordKind::kw_protocol: + case CodeCompletionKeywordKind::kw_class: + case CodeCompletionKeywordKind::kw_struct: + case CodeCompletionKeywordKind::kw_enum: + case CodeCompletionKeywordKind::kw_extension: + return true; + case CodeCompletionKeywordKind::None: + if (DAK && *DAK == DeclAttrKind::DAK_Actor) { + return true; + } + break; + default: + break; + } + return false; + }; + auto isTopLevelOnlyDeclIntroducer = [](CodeCompletionKeywordKind Kind, + Optional DAK) -> bool { + switch (Kind) { + case CodeCompletionKeywordKind::kw_operator: + case CodeCompletionKeywordKind::kw_precedencegroup: + case CodeCompletionKeywordKind::kw_import: + case CodeCompletionKeywordKind::kw_protocol: + case CodeCompletionKeywordKind::kw_extension: + return true; + default: + return false; + } + }; + + auto getFlair = [&](CodeCompletionKeywordKind Kind, + Optional DAK) -> CodeCompletionFlair { + if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) { + // Type decls are common in library file top-level. + if (isTypeDeclIntroducer(Kind, DAK)) + return CodeCompletionFlairBit::CommonKeywordAtCurrentPosition; + } + if (isa(DC)) { + // Protocols cannot have nested type decls (other than 'typealias'). + if (isTypeDeclIntroducer(Kind, DAK)) + return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; + } + if (DC->isTypeContext()) { + // Top-level only decls are invalid in type context. + if (isTopLevelOnlyDeclIntroducer(Kind, DAK)) + return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; + } + if (isCompletionDeclContextLocalContext(DC)) { + // Local type decl are valid, but not common. + if (isTypeDeclIntroducer(Kind, DAK)) + return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; + + // Top-level only decls are invalid in function body. + if (isTopLevelOnlyDeclIntroducer(Kind, DAK)) + return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; + + // 'init', 'deinit' and 'subscript' are invalid in function body. + // Access control modifiers are invalid in function body. + switch (Kind) { + case CodeCompletionKeywordKind::kw_init: + case CodeCompletionKeywordKind::kw_deinit: + case CodeCompletionKeywordKind::kw_subscript: + case CodeCompletionKeywordKind::kw_private: + case CodeCompletionKeywordKind::kw_fileprivate: + case CodeCompletionKeywordKind::kw_internal: + case CodeCompletionKeywordKind::kw_public: + case CodeCompletionKeywordKind::kw_static: + return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; + + default: + break; + } + + // These modifiers are invalid for decls in function body. + if (DAK) { + switch (*DAK) { + case DeclAttrKind::DAK_Lazy: + case DeclAttrKind::DAK_Final: + case DeclAttrKind::DAK_Infix: + case DeclAttrKind::DAK_Frozen: + case DeclAttrKind::DAK_Prefix: + case DeclAttrKind::DAK_Postfix: + case DeclAttrKind::DAK_Dynamic: + case DeclAttrKind::DAK_Override: + case DeclAttrKind::DAK_Optional: + case DeclAttrKind::DAK_Required: + case DeclAttrKind::DAK_Convenience: + case DeclAttrKind::DAK_AccessControl: + case DeclAttrKind::DAK_Nonisolated: + return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; + + default: + break; + } + } + } + return None; + }; + auto AddDeclKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind, - Optional DAK) { + Optional DAK) { if (Name == "let" || Name == "var") { // Treat keywords that could be the start of a pattern specially. return; @@ -5857,17 +5913,27 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink, // FIXME: This should use canUseAttributeOnDecl. // Remove user inaccessible keywords. - if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*DAK)) return; + if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*DAK)) + return; // Remove keywords only available when concurrency is enabled. if (DAK.hasValue() && !IsConcurrencyEnabled && DeclAttribute::isConcurrencyOnly(*DAK)) return; - addKeyword(Sink, Name, Kind); + // Remove keywords only available when distributed is enabled. + if (DAK.hasValue() && !IsDistributedEnabled && + DeclAttribute::isDistributedOnly(*DAK)) + return; + + addKeyword( + Sink, Name, Kind, /*TypeAnnotation=*/"", + /*TypeRelation=*/CodeCompletionResult::ExpectedTypeRelation::NotApplicable, + getFlair(Kind, DAK)); }; -#define DECL_KEYWORD(kw) AddDeclKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None); +#define DECL_KEYWORD(kw) \ + AddDeclKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None); #include "swift/Syntax/TokenKinds.def" // Context-sensitive keywords. @@ -5881,14 +5947,22 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink, #define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS) #include #undef CONTEXTUAL_CASE - } -static void addStmtKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody) { +static void addStmtKeywords(CodeCompletionResultSink &Sink, DeclContext *DC, + bool MaybeFuncBody) { + CodeCompletionFlair flair; + // Starting a statement at top-level in non-script files is invalid. + if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) { + flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope; + } + auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) { if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return) return; - addKeyword(Sink, Name, Kind); + addKeyword(Sink, Name, Kind, "", + CodeCompletionResult::ExpectedTypeRelation::NotApplicable, + flair); }; #define STMT_KEYWORD(kw) AddStmtKeyword(#kw, CodeCompletionKeywordKind::kw_##kw); #include "swift/Syntax/TokenKinds.def" @@ -5914,12 +5988,22 @@ static void addObserverKeywords(CodeCompletionResultSink &Sink) { addKeyword(Sink, "didSet", CodeCompletionKeywordKind::None); } -static void addExprKeywords(CodeCompletionResultSink &Sink) { +static void addExprKeywords(CodeCompletionResultSink &Sink, DeclContext *DC) { + // Expression is invalid at top-level of non-script files. + CodeCompletionFlair flair; + if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) { + flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope; + } + // Expr keywords. - addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try); - addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try); - addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try); - addKeyword(Sink, "await", CodeCompletionKeywordKind::None); + addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try, "", + CodeCompletionResult::ExpectedTypeRelation::NotApplicable, flair); + addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try, "", + CodeCompletionResult::ExpectedTypeRelation::NotApplicable, flair); + addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try, "", + CodeCompletionResult::ExpectedTypeRelation::NotApplicable, flair); + addKeyword(Sink, "await", CodeCompletionKeywordKind::None, "", + CodeCompletionResult::ExpectedTypeRelation::NotApplicable, flair); } static void addOpaqueTypeKeyword(CodeCompletionResultSink &Sink) { @@ -5945,7 +6029,6 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, case CompletionKind::PoundAvailablePlatform: case CompletionKind::Import: case CompletionKind::UnresolvedMember: - case CompletionKind::CallArg: case CompletionKind::LabeledTrailingClosure: case CompletionKind::AfterPoundExpr: case CompletionKind::AfterPoundDirective: @@ -5955,6 +6038,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, case CompletionKind::KeyPathExprSwift: case CompletionKind::PrecedenceGroup: case CompletionKind::StmtLabel: + case CompletionKind::TypeAttrBeginning: break; case CompletionKind::EffectsSpecifier: { @@ -5985,8 +6069,10 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, LLVM_FALLTHROUGH; } case CompletionKind::StmtOrExpr: - addDeclKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); - addStmtKeywords(Sink, MaybeFuncBody); + addDeclKeywords(Sink, CurDeclContext, + Context.LangOpts.EnableExperimentalConcurrency, + Context.LangOpts.EnableExperimentalDistributed); + addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody); LLVM_FALLTHROUGH; case CompletionKind::ReturnStmtExpr: case CompletionKind::YieldStmtExpr: @@ -5994,16 +6080,23 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, case CompletionKind::ForEachSequence: addSuperKeyword(Sink); addLetVarKeywords(Sink); - addExprKeywords(Sink); + addExprKeywords(Sink, CurDeclContext); addAnyTypeKeyword(Sink, CurDeclContext->getASTContext().TheAnyType); break; + case CompletionKind::CallArg: + case CompletionKind::PostfixExprParen: + // Note that we don't add keywords here as the completion might be for + // an argument list pattern. We instead add keywords later in + // CodeCompletionCallbacksImpl::doneParsing when we know we're not + // completing for a argument list pattern. + break; + case CompletionKind::CaseStmtKeyword: addCaseStmtKeywords(Sink); break; case CompletionKind::PostfixExpr: - case CompletionKind::PostfixExprParen: case CompletionKind::CaseStmtBeginning: case CompletionKind::TypeIdentifierWithDot: case CompletionKind::TypeIdentifierWithoutDot: @@ -6014,7 +6107,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, if (ParsedDecl && ParsedDecl == CurDeclContext->getAsDecl()) DC = ParsedDecl->getDeclContext(); if (!isa(DC)) - if (DC->isTypeContext() || (ParsedDecl && isa(ParsedDecl))) + if (DC->isTypeContext() || isa_and_nonnull(ParsedDecl)) addOpaqueTypeKeyword(Sink); LLVM_FALLTHROUGH; @@ -6046,7 +6139,9 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, .Default(false); }) != ParsedKeywords.end(); if (!HasDeclIntroducer) { - addDeclKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); + addDeclKeywords(Sink, CurDeclContext, + Context.LangOpts.EnableExperimentalConcurrency, + Context.LangOpts.EnableExperimentalDistributed); addLetVarKeywords(Sink); } break; @@ -6055,6 +6150,13 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, case CompletionKind::AfterIfStmtElse: addKeyword(Sink, "if", CodeCompletionKeywordKind::kw_if); break; + case CompletionKind::ForEachPatternBeginning: + if (!llvm::is_contained(ParsedKeywords, "try")) + addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try); + if (!llvm::is_contained(ParsedKeywords, "await")) + addKeyword(Sink, "await", CodeCompletionKeywordKind::None); + addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var); + addKeyword(Sink, "case", CodeCompletionKeywordKind::kw_case); } } @@ -6122,12 +6224,16 @@ static void addPlatformConditions(CodeCompletionResultSink &Sink) { consumer) { CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Pattern, - SemanticContextKind::ExpressionSpecific, {}); + // FIXME: SemanticContextKind::CurrentModule is not correct. + // Use 'None' (and fix prioritization) or introduce a new context. + SemanticContextKind::CurrentModule, {}); + Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); Builder.addBaseName(Name); Builder.addLeftParen(); consumer(Builder); Builder.addRightParen(); }; + addWithName("os", [](CodeCompletionResultBuilder &Builder) { Builder.addSimpleNamedParameter("name"); }); @@ -6168,18 +6274,118 @@ static void addConditionalCompilationFlags(ASTContext &Ctx, // TODO: Should we filter out some flags? CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Keyword, - SemanticContextKind::ExpressionSpecific, {}); + // FIXME: SemanticContextKind::CurrentModule is not correct. + // Use 'None' (and fix prioritization) or introduce a new context. + SemanticContextKind::CurrentModule, {}); + Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); Builder.addTextChunk(Flag); } } +/// Add flairs to the each item in \p results . +/// +/// If \p Sink is passed, the pointer of the each result may be replaced with a +/// pointer to the new item allocated in \p Sink. +/// If \p Sink is nullptr, the pointee of each result may be modified in place. +static void postProcessResults(MutableArrayRef results, + CompletionKind Kind, DeclContext *DC, + CodeCompletionResultSink *Sink) { + for (CodeCompletionResult *&result : results) { + bool modified = false; + auto flair = result->getFlair(); + + // Starting a statement with a protocol name is not common. So protocol + // names at non-type name position are "rare". + if (result->getKind() == CodeCompletionResult::ResultKind::Declaration && + result->getAssociatedDeclKind() == CodeCompletionDeclKind::Protocol && + Kind != CompletionKind::TypeSimpleBeginning && + Kind != CompletionKind::TypeIdentifierWithoutDot && + Kind != CompletionKind::TypeIdentifierWithDot && + Kind != CompletionKind::TypeDeclResultBeginning && + Kind != CompletionKind::GenericRequirement) { + flair |= CodeCompletionFlairBit::RareTypeAtCurrentPosition; + modified = true; + } + + // Starting a statement at top-level in non-script files is invalid. + if (Kind == CompletionKind::StmtOrExpr && + result->getKind() == CodeCompletionResult::ResultKind::Declaration && + isCodeCompletionAtTopLevelOfLibraryFile(DC)) { + flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope; + modified = true; + } + + if (!modified) + continue; + + if (Sink) { + // Replace the result with a new result with the flair. + result = result->withFlair(flair, *Sink); + } else { + // 'Sink' == nullptr means the result is modifiable in place. + result->setFlair(flair); + } + } +} + +static void copyAllKnownSourceFileInfo( + ASTContext &Ctx, CodeCompletionResultSink &Sink) { + assert(Sink.SourceFiles.empty()); + + SmallVector loadedModules; + loadedModules.reserve(Ctx.getNumLoadedModules()); + for (auto &entry : Ctx.getLoadedModules()) + loadedModules.push_back(entry.second); + + auto &result = Sink.SourceFiles; + for (auto *M : loadedModules) { + // We don't need to check system modules. + if (M->isSystemModule()) + continue; + + M->collectBasicSourceFileInfo([&](const BasicSourceFileInfo &info) { + if (info.getFilePath().empty()) + return; + + bool isUpToDate = false; + if (info.isFromSourceFile()) { + // 'SourceFile' is always "up-to-date" because we've just loaded. + isUpToDate = true; + } else { + isUpToDate = isSourceFileUpToDate(info, Ctx); + } + result.emplace_back(copyString(*Sink.Allocator, info.getFilePath()), isUpToDate); + }); + } +} + static void deliverCompletionResults(CodeCompletionContext &CompletionContext, CompletionLookup &Lookup, - SourceFile &SF, + DeclContext *DC, CodeCompletionConsumer &Consumer) { + auto &SF = *DC->getParentSourceFile(); llvm::SmallPtrSet seenModuleNames; std::vector RequestedModules; + SmallPtrSet explictlyImportedModules; + { + // Collect modules directly imported in this SourceFile. + SmallVector directImport; + SF.getImportedModules(directImport, + {ModuleDecl::ImportFilterKind::Default, + ModuleDecl::ImportFilterKind::ImplementationOnly}); + for (auto import : directImport) + explictlyImportedModules.insert(import.importedModule); + + // Exclude modules implicitly imported in the current module. + auto implicitImports = SF.getParentModule()->getImplicitImports(); + for (auto import : implicitImports.imports) + explictlyImportedModules.erase(import.module.importedModule); + + // Consider the current module "explicit". + explictlyImportedModules.insert(SF.getParentModule()); + } + for (auto &Request: Lookup.RequestedCachedResults) { llvm::DenseSet ImportsSeen; auto handleImport = [&](ImportedModule Import) { @@ -6228,8 +6434,11 @@ static void deliverCompletionResults(CodeCompletionContext &CompletionContext, RequestedModules.push_back({std::move(K), TheModule, Request.OnlyTypes, Request.OnlyPrecedenceGroups}); + auto TheModuleName = TheModule->getName(); if (Request.IncludeModuleQualifier && - seenModuleNames.insert(TheModule->getName()).second) + (!Lookup.isHiddenModuleName(TheModuleName) || + explictlyImportedModules.contains(TheModule)) && + seenModuleNames.insert(TheModuleName).second) Lookup.addModuleName(TheModule); } }; @@ -6265,17 +6474,20 @@ static void deliverCompletionResults(CodeCompletionContext &CompletionContext, Lookup.RequestedCachedResults.clear(); CompletionContext.typeContextKind = Lookup.typeContextKind(); - // Use the current SourceFile as the DeclContext so that we can use it to - // perform qualified lookup, and to get the correct visibility for - // @testable imports. - DeclContext *DCForModules = &SF; - Consumer.handleResultsAndModules(CompletionContext, RequestedModules, - DCForModules); + postProcessResults(CompletionContext.getResultSink().Results, + CompletionContext.CodeCompletionKind, DC, + /*Sink=*/nullptr); + + if (CompletionContext.requiresSourceFileInfo()) + copyAllKnownSourceFileInfo(SF.getASTContext(), + CompletionContext.getResultSink()); + + Consumer.handleResultsAndModules(CompletionContext, RequestedModules, DC); } void deliverUnresolvedMemberResults( - ArrayRef Results, - DeclContext *DC, SourceLoc DotLoc, + ArrayRef Results, + ArrayRef EnumPatternTypes, DeclContext *DC, SourceLoc DotLoc, ide::CodeCompletionContext &CompletionCtx, CodeCompletionConsumer &Consumer) { ASTContext &Ctx = DC->getASTContext(); @@ -6284,7 +6496,7 @@ void deliverUnresolvedMemberResults( assert(DotLoc.isValid()); Lookup.setHaveDot(DotLoc); - Lookup.shouldCheckForDuplicates(Results.size() > 1); + Lookup.shouldCheckForDuplicates(Results.size() + EnumPatternTypes.size() > 1); // Get the canonical versions of the top-level types SmallPtrSet originalTypes; @@ -6308,8 +6520,46 @@ void deliverUnresolvedMemberResults( } Lookup.getUnresolvedMemberCompletions(Result.ExpectedTy); } - SourceFile *SF = DC->getParentSourceFile(); - deliverCompletionResults(CompletionCtx, Lookup, *SF, Consumer); + + // Offer completions when interpreting the pattern match as an + // EnumElementPattern. + for (auto &Ty : EnumPatternTypes) { + Lookup.setExpectedTypes({Ty}, /*IsImplicitSingleExpressionReturn=*/false, + /*expectsNonVoid=*/true); + Lookup.setIdealExpectedType(Ty); + + // We can pattern match MyEnum against Optional + if (Ty->getOptionalObjectType()) { + Type Unwrapped = Ty->lookThroughAllOptionalTypes(); + Lookup.getEnumElementPatternCompletions(Unwrapped); + } + + Lookup.getEnumElementPatternCompletions(Ty); + } + + deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); +} + +void deliverKeyPathResults( + ArrayRef Results, + DeclContext *DC, SourceLoc DotLoc, + ide::CodeCompletionContext &CompletionCtx, + CodeCompletionConsumer &Consumer) { + ASTContext &Ctx = DC->getASTContext(); + CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, + &CompletionCtx); + + if (DotLoc.isValid()) { + Lookup.setHaveDot(DotLoc); + } + Lookup.shouldCheckForDuplicates(Results.size() > 1); + + for (auto &Result : Results) { + Lookup.setIsSwiftKeyPathExpr(Result.OnRoot); + Lookup.getValueExprCompletions(Result.BaseType); + } + + deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); } void deliverDotExprResults( @@ -6349,8 +6599,7 @@ void deliverDotExprResults( Lookup.getValueExprCompletions(Result.BaseTy, Result.BaseDecl); } - SourceFile *SF = DC->getParentSourceFile(); - deliverCompletionResults(CompletionCtx, Lookup, *SF, Consumer); + deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); } bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { @@ -6400,8 +6649,24 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { Lookup.fallbackTypeCheck(CurDeclContext); addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); - deliverUnresolvedMemberResults(Lookup.getResults(), CurDeclContext, DotLoc, - CompletionContext, Consumer); + deliverUnresolvedMemberResults(Lookup.getExprResults(), + Lookup.getEnumPatternTypes(), CurDeclContext, + DotLoc, CompletionContext, Consumer); + return true; + } + case CompletionKind::KeyPathExprSwift: { + assert(CurDeclContext); + + // CodeCompletionCallbacks::completeExprKeyPath takes a \c KeyPathExpr, + // so we can safely cast the \c ParsedExpr back to a \c KeyPathExpr. + auto KeyPath = cast(ParsedExpr); + KeyPathTypeCheckCompletionCallback Lookup(KeyPath); + llvm::SaveAndRestore CompletionCollector( + Context.CompletionCallback, &Lookup); + typeCheckContextAt(CurDeclContext, CompletionLoc); + + deliverKeyPathResults(Lookup.getResults(), CurDeclContext, DotLoc, + CompletionContext, Consumer); return true; } default: @@ -6505,7 +6770,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { } if (auto *DRE = dyn_cast_or_null(ParsedExpr)) { Lookup.setIsSelfRefExpr(DRE->getDecl()->getName() == Context.Id_self); - } else if (ParsedExpr && isa(ParsedExpr)) { + } else if (isa_and_nonnull(ParsedExpr)) { Lookup.setIsSuperRefExpr(); } @@ -6524,60 +6789,10 @@ void CodeCompletionCallbacksImpl::doneParsing() { case CompletionKind::None: case CompletionKind::DotExpr: case CompletionKind::UnresolvedMember: + case CompletionKind::KeyPathExprSwift: llvm_unreachable("should be already handled"); return; - case CompletionKind::KeyPathExprSwift: { - auto KPE = dyn_cast(ParsedExpr); - auto BGT = (*ExprType)->getAs(); - if (!KPE || !BGT || BGT->getGenericArgs().size() != 2) - break; - assert(!KPE->isObjC()); - - if (DotLoc.isValid()) - Lookup.setHaveDot(DotLoc); - - bool OnRoot = !KPE->getComponents().front().isValid(); - Lookup.setIsSwiftKeyPathExpr(OnRoot); - - Type baseType = BGT->getGenericArgs()[OnRoot ? 0 : 1]; - if (OnRoot && baseType->is()) { - // Infer the root type of the keypath from the context type. - ExprContextInfo ContextInfo(CurDeclContext, ParsedExpr); - for (auto T : ContextInfo.getPossibleTypes()) { - if (auto unwrapped = T->getOptionalObjectType()) - T = unwrapped; - - // If the context type is any of the KeyPath types, use it. - if (T->getAnyNominal() && T->getAnyNominal()->getKeyPathTypeKind() && - !T->hasUnresolvedType() && T->is()) { - baseType = T->castTo()->getGenericArgs()[0]; - break; - } - - // KeyPath can be used as a function that receives its root type. - if (T->is()) { - auto *fnType = T->castTo(); - if (fnType->getNumParams() == 1) { - const AnyFunctionType::Param ¶m = fnType->getParams()[0]; - baseType = param.getParameterType(); - break; - } - } - } - } - if (!OnRoot && KPE->getComponents().back().getKind() == - KeyPathExpr::Component::Kind::OptionalWrap) { - // KeyPath expr with '?' (e.g. '\Ty.[0].prop?.another'). - // Although expected type is optional, we should unwrap it because it's - // unwrapped. - baseType = baseType->getOptionalObjectType(); - } - - Lookup.getValueExprCompletions(baseType); - break; - } - case CompletionKind::StmtOrExpr: case CompletionKind::ForEachSequence: case CompletionKind::PostfixExprBeginning: { @@ -6593,6 +6808,13 @@ void CodeCompletionCallbacksImpl::doneParsing() { if (isDynamicLookup(*ExprType)) Lookup.setIsDynamicLookup(); Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); + /// We set the type of ParsedExpr explicitly above. But we don't want an + /// unresolved type in our AST when we type check again for operator + /// completions. Remove the type of the ParsedExpr and see if we can come up + /// with something more useful based on the the full sequence expression. + if (ParsedExpr->getType()->is()) { + ParsedExpr->setType(nullptr); + } Lookup.getOperatorCompletions(ParsedExpr, leadingSequenceExprs); Lookup.getPostfixKeywordCompletions(*ExprType, ParsedExpr); break; @@ -6627,6 +6849,11 @@ void CodeCompletionCallbacksImpl::doneParsing() { Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), ContextInfo.isImplicitSingleExpressionReturn()); Lookup.setHaveLParen(false); + + // Add any keywords that can be used in an argument expr position. + addSuperKeyword(CompletionContext.getResultSink()); + addExprKeywords(CompletionContext.getResultSink(), CurDeclContext); + DoPostfixExprBeginning(); } break; @@ -6736,7 +6963,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { Lookup.setHaveLParen(true); for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) { auto apply = ContextInfo.getAnalyzedExpr(); - if (apply && isa(apply)) { + if (isa_and_nonnull(apply)) { Lookup.addSubscriptCallPattern( typeAndDecl.Type, dyn_cast_or_null(typeAndDecl.Decl), @@ -6768,6 +6995,11 @@ void CodeCompletionCallbacksImpl::doneParsing() { if (shouldPerformGlobalCompletion) { Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), ContextInfo.isImplicitSingleExpressionReturn()); + + // Add any keywords that can be used in an argument expr position. + addSuperKeyword(CompletionContext.getResultSink()); + addExprKeywords(CompletionContext.getResultSink(), CurDeclContext); + DoPostfixExprBeginning(); } break; @@ -6809,7 +7041,9 @@ void CodeCompletionCallbacksImpl::doneParsing() { if (CurDeclContext->isTypeContext()) { // Override completion (CompletionKind::NominalMemberBeginning). - addDeclKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); + addDeclKeywords(Sink, CurDeclContext, + Context.LangOpts.EnableExperimentalConcurrency, + Context.LangOpts.EnableExperimentalDistributed); addLetVarKeywords(Sink); SmallVector ParsedKeywords; CompletionOverrideLookup OverrideLookup(Sink, Context, CurDeclContext, @@ -6817,11 +7051,13 @@ void CodeCompletionCallbacksImpl::doneParsing() { OverrideLookup.getOverrideCompletions(SourceLoc()); } else { // Global completion (CompletionKind::PostfixExprBeginning). - addDeclKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); - addStmtKeywords(Sink, MaybeFuncBody); + addDeclKeywords(Sink, CurDeclContext, + Context.LangOpts.EnableExperimentalConcurrency, + Context.LangOpts.EnableExperimentalDistributed); + addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody); addSuperKeyword(Sink); addLetVarKeywords(Sink); - addExprKeywords(Sink); + addExprKeywords(Sink, CurDeclContext); addAnyTypeKeyword(Sink, Context.TheAnyType); DoPostfixExprBeginning(); } @@ -6942,14 +7178,40 @@ void CodeCompletionCallbacksImpl::doneParsing() { Lookup.getStmtLabelCompletions(Loc, ParentStmtKind == StmtKind::Continue); break; } + case CompletionKind::TypeAttrBeginning: { + Lookup.getTypeAttributeKeywordCompletions(); + + // Type names at attribute position after '@'. + Lookup.getTypeCompletionsInDeclContext( + P.Context.SourceMgr.getCodeCompletionLoc()); + break; + + } case CompletionKind::AfterIfStmtElse: case CompletionKind::CaseStmtKeyword: case CompletionKind::EffectsSpecifier: + case CompletionKind::ForEachPatternBeginning: // Handled earlier by keyword completions. break; } - deliverCompletionResults(CompletionContext, Lookup, P.SF, Consumer); + deliverCompletionResults(CompletionContext, Lookup, CurDeclContext, Consumer); +} + +void PrintingCodeCompletionConsumer::handleResults( + CodeCompletionContext &context) { + if (context.requiresSourceFileInfo() && + !context.getResultSink().SourceFiles.empty()) { + OS << "Known module source files\n"; + for (auto &entry : context.getResultSink().SourceFiles) { + OS << (entry.IsUpToDate ? " + " : " - "); + OS << entry.FilePath; + OS << "\n"; + } + this->RequiresSourceFileInfo = true; + } + auto results = context.takeResults(); + handleResults(results); } void PrintingCodeCompletionConsumer::handleResults( @@ -6976,16 +7238,43 @@ void PrintingCodeCompletionConsumer::handleResults( Result->getCompletionString()->print(OS); } - llvm::SmallString<64> Name; - llvm::raw_svector_ostream NameOs(Name); - Result->getCompletionString()->getName(NameOs); - OS << "; name=" << Name; + OS << "; name="; + printCodeCompletionResultFilterName(*Result, OS); StringRef comment = Result->getBriefDocComment(); if (IncludeComments && !comment.empty()) { OS << "; comment=" << comment; } + if (RequiresSourceFileInfo) { + StringRef sourceFilePath = Result->getSourceFilePath(); + if (!sourceFilePath.empty()) { + OS << "; source=" << sourceFilePath; + } + } + + if (Result->getDiagnosticSeverity() != + CodeCompletionDiagnosticSeverity::None) { + OS << "; diagnostics=" << comment; + switch (Result->getDiagnosticSeverity()) { + case CodeCompletionDiagnosticSeverity::Error: + OS << "error"; + break; + case CodeCompletionDiagnosticSeverity::Warning: + OS << "warning"; + break; + case CodeCompletionDiagnosticSeverity::Remark: + OS << "remark"; + break; + case CodeCompletionDiagnosticSeverity::Note: + OS << "note"; + break; + case CodeCompletionDiagnosticSeverity::None: + llvm_unreachable("none"); + } + OS << ":" << Result->getDiagnosticMessage(); + } + OS << "\n"; } OS << "End completions\n"; @@ -7018,20 +7307,24 @@ swift::ide::makeCodeCompletionCallbacksFactory( void swift::ide::lookupCodeCompletionResultsFromModule( CodeCompletionResultSink &targetSink, const ModuleDecl *module, ArrayRef accessPath, bool needLeadingDot, - const DeclContext *currDeclContext) { - CompletionLookup Lookup(targetSink, module->getASTContext(), currDeclContext); + const SourceFile *SF) { + // Use the SourceFile as the decl context, to avoid decl context specific + // behaviors. + CompletionLookup Lookup(targetSink, module->getASTContext(), SF); Lookup.lookupExternalModuleDecls(module, accessPath, needLeadingDot); } -void swift::ide::copyCodeCompletionResults(CodeCompletionResultSink &targetSink, - CodeCompletionResultSink &sourceSink, - bool onlyTypes, - bool onlyPrecedenceGroups) { +MutableArrayRef +swift::ide::copyCodeCompletionResults(CodeCompletionResultSink &targetSink, + CodeCompletionResultSink &sourceSink, + bool onlyTypes, + bool onlyPrecedenceGroups) { // We will be adding foreign results (from another sink) into TargetSink. // TargetSink should have an owning pointer to the allocator that keeps the // results alive. targetSink.ForeignAllocators.push_back(sourceSink.Allocator); + auto startSize = targetSink.Results.size(); if (onlyTypes) { std::copy_if(sourceSink.Results.begin(), sourceSink.Results.end(), @@ -7081,12 +7374,22 @@ void swift::ide::copyCodeCompletionResults(CodeCompletionResultSink &targetSink, sourceSink.Results.begin(), sourceSink.Results.end()); } + + return llvm::makeMutableArrayRef(targetSink.Results.data() + startSize, + targetSink.Results.size() - startSize); } void SimpleCachingCodeCompletionConsumer::handleResultsAndModules( CodeCompletionContext &context, ArrayRef requestedModules, - DeclContext *DCForModules) { + DeclContext *DC) { + + // Use the current SourceFile as the DeclContext so that we can use it to + // perform qualified lookup, and to get the correct visibility for + // @testable imports. Also it cannot use 'DC' since it would apply decl + // context changes to cached results. + const SourceFile *SF = DC->getParentSourceFile(); + for (auto &R : requestedModules) { // FIXME(thread-safety): lock the whole AST context. We might load a // module. @@ -7098,15 +7401,18 @@ void SimpleCachingCodeCompletionConsumer::handleResultsAndModules( (*V)->Sink.annotateResult = context.getAnnotateResult(); lookupCodeCompletionResultsFromModule( (*V)->Sink, R.TheModule, R.Key.AccessPath, - R.Key.ResultsHaveLeadingDot, DCForModules); + R.Key.ResultsHaveLeadingDot, SF); context.Cache.set(R.Key, *V); } assert(V.hasValue()); - copyCodeCompletionResults(context.getResultSink(), (*V)->Sink, - R.OnlyTypes, R.OnlyPrecedenceGroups); + auto newItems = + copyCodeCompletionResults(context.getResultSink(), (*V)->Sink, + R.OnlyTypes, R.OnlyPrecedenceGroups); + postProcessResults(newItems, context.CodeCompletionKind, DC, + &context.getResultSink()); } - handleResults(context.takeResults()); + handleResults(context); } //===----------------------------------------------------------------------===// diff --git a/lib/IDE/CodeCompletionCache.cpp b/lib/IDE/CodeCompletionCache.cpp index 834eca5b3f36b..832ac78a41c58 100644 --- a/lib/IDE/CodeCompletionCache.cpp +++ b/lib/IDE/CodeCompletionCache.cpp @@ -13,7 +13,9 @@ #include "swift/IDE/CodeCompletionCache.h" #include "swift/Basic/Cache.h" #include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -100,7 +102,7 @@ CodeCompletionCache::~CodeCompletionCache() {} /// /// This should be incremented any time we commit a change to the format of the /// cached results. This isn't expected to change very often. -static constexpr uint32_t onDiskCompletionCacheVersion = 1; +static constexpr uint32_t onDiskCompletionCacheVersion = 2; /// Deserializes CodeCompletionResults from \p in and stores them in \p V. /// \see writeCacheModule. @@ -151,15 +153,22 @@ static bool readCachedModule(llvm::MemoryBuffer *in, auto stringCount = read32le(strings); assert(strings + stringCount == end && "incorrect file size"); (void)stringCount; // so it is not seen as "unused" in release builds. + llvm::DenseMap knownStrings; // STRINGS auto getString = [&](uint32_t index) -> StringRef { if (index == ~0u) return ""; + auto found = knownStrings.find(index); + if (found != knownStrings.end()) { + return found->second; + } const char *p = strings + index; auto size = read32le(p); - return copyString(*V.Sink.Allocator, StringRef(p, size)); + auto str = copyString(*V.Sink.Allocator, StringRef(p, size)); + knownStrings[index] = str; + return str; }; // CHUNKS @@ -194,49 +203,39 @@ static bool readCachedModule(llvm::MemoryBuffer *in, auto context = static_cast(*cursor++); auto notRecommended = static_cast(*cursor++); + auto diagSeverity = + static_cast(*cursor++); auto isSystem = static_cast(*cursor++); auto numBytesToErase = static_cast(*cursor++); - auto oldCursor = cursor; auto chunkIndex = read32le(cursor); - auto IntLength = cursor - oldCursor; auto moduleIndex = read32le(cursor); + auto sourceFilePathIndex = read32le(cursor); auto briefDocIndex = read32le(cursor); - auto assocUSRCount = read32le(cursor); - auto assocUSRsIndex = read32le(cursor); - auto declKeywordCount = read32le(cursor); - auto declKeywordIndex = read32le(cursor); + auto diagMessageIndex = read32le(cursor); - CodeCompletionString *string = getCompletionString(chunkIndex); - auto moduleName = getString(moduleIndex); - auto briefDocComment = getString(briefDocIndex); + auto assocUSRCount = read32le(cursor); SmallVector assocUSRs; for (unsigned i = 0; i < assocUSRCount; ++i) { - auto usr = getString(assocUSRsIndex); - assocUSRs.push_back(usr); - assocUSRsIndex += usr.size() + IntLength; + assocUSRs.push_back(getString(read32le(cursor))); } - SmallVector, 4> declKeywords; - for (unsigned i = 0; i < declKeywordCount; ++i) { - auto first = getString(declKeywordIndex); - declKeywordIndex += first.size() + IntLength; - auto second = getString(declKeywordIndex); - declKeywordIndex += second.size() + IntLength; - declKeywords.push_back(std::make_pair(first, second)); - } + CodeCompletionString *string = getCompletionString(chunkIndex); + auto moduleName = getString(moduleIndex); + auto sourceFilePath = getString(sourceFilePathIndex); + auto briefDocComment = getString(briefDocIndex); + auto diagMessage = getString(diagMessageIndex); CodeCompletionResult *result = nullptr; if (kind == CodeCompletionResult::Declaration) { result = new (*V.Sink.Allocator) CodeCompletionResult( - context, /*IsArgumentLabels=*/false, numBytesToErase, string, - declKind, isSystem, moduleName, notRecommended, briefDocComment, + context, CodeCompletionFlair(), numBytesToErase, string, + declKind, isSystem, moduleName, sourceFilePath, notRecommended, + diagSeverity, diagMessage, briefDocComment, copyArray(*V.Sink.Allocator, ArrayRef(assocUSRs)), - copyArray(*V.Sink.Allocator, - ArrayRef>(declKeywords)), CodeCompletionResult::Unknown, opKind); } else { result = new (*V.Sink.Allocator) - CodeCompletionResult(kind, context, /*IsArgumentLabels=*/false, + CodeCompletionResult(kind, context, CodeCompletionFlair(), numBytesToErase, string, CodeCompletionResult::NotApplicable, opKind); } @@ -310,14 +309,20 @@ static void writeCachedModule(llvm::raw_ostream &out, endian::Writer chunksLE(chunks, little); std::string strings_; llvm::raw_string_ostream strings(strings_); + llvm::StringMap knownStrings; - auto addString = [&strings](StringRef str) { + auto addString = [&strings, &knownStrings](StringRef str) { if (str.empty()) return ~0u; + auto found = knownStrings.find(str); + if (found != knownStrings.end()) { + return found->second; + } auto size = strings.tell(); endian::Writer LE(strings, little); LE.write(static_cast(str.size())); strings << str; + knownStrings[str] = size; return static_cast(size); }; @@ -341,7 +346,11 @@ static void writeCachedModule(llvm::raw_ostream &out, { endian::Writer LE(results, little); for (CodeCompletionResult *R : V.Sink.Results) { - assert(!R->isArgumentLabels() && "Argument labels should not be cached"); + assert(!R->getFlair().toRaw() && "Any flairs should not be cached"); + assert(R->getNotRecommendedReason() != + CodeCompletionResult::NotRecommendedReason::InvalidAsyncContext && + "InvalidAsyncContext is decl context specific, cannot be cached"); + // FIXME: compress bitfield LE.write(static_cast(R->getKind())); if (R->getKind() == CodeCompletionResult::Declaration) @@ -354,32 +363,19 @@ static void writeCachedModule(llvm::raw_ostream &out, LE.write(static_cast(CodeCompletionOperatorKind::None)); LE.write(static_cast(R->getSemanticContext())); LE.write(static_cast(R->getNotRecommendedReason())); + LE.write(static_cast(R->getDiagnosticSeverity())); LE.write(static_cast(R->isSystem())); LE.write(static_cast(R->getNumBytesToErase())); LE.write( static_cast(addCompletionString(R->getCompletionString()))); LE.write(addString(R->getModuleName())); // index into strings + LE.write(addString(R->getSourceFilePath())); // index into strings LE.write(addString(R->getBriefDocComment())); // index into strings + LE.write(addString(R->getDiagnosticMessage())); // index into strings + LE.write(static_cast(R->getAssociatedUSRs().size())); - if (R->getAssociatedUSRs().empty()) { - LE.write(static_cast(~0u)); - } else { - LE.write(addString(R->getAssociatedUSRs()[0])); - for (unsigned i = 1; i < R->getAssociatedUSRs().size(); ++i) { - addString(R->getAssociatedUSRs()[i]); // ignore result - } - } - auto AllKeywords = R->getDeclKeywords(); - LE.write(static_cast(AllKeywords.size())); - if (AllKeywords.empty()) { - LE.write(static_cast(~0u)); - } else { - LE.write(addString(AllKeywords[0].first)); - addString(AllKeywords[0].second); - for (unsigned i = 1; i < AllKeywords.size(); ++i) { - addString(AllKeywords[i].first); - addString(AllKeywords[i].second); - } + for (unsigned i = 0; i < R->getAssociatedUSRs().size(); ++i) { + LE.write(addString(R->getAssociatedUSRs()[i])); } } } diff --git a/lib/IDE/CodeCompletionDiagnostics.cpp b/lib/IDE/CodeCompletionDiagnostics.cpp new file mode 100644 index 0000000000000..8dde71a7d3319 --- /dev/null +++ b/lib/IDE/CodeCompletionDiagnostics.cpp @@ -0,0 +1,180 @@ +//===--- CodeCompletionDiagnostics.cpp ------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "CodeCompletionDiagnostics.h" + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsIDE.h" +#include "swift/AST/DiagnosticsSema.h" + +using namespace swift; +using namespace ide; + +namespace { + +CodeCompletionDiagnosticSeverity getSeverity(DiagnosticKind DiagKind) { + switch (DiagKind) { + case DiagnosticKind::Error: + return CodeCompletionDiagnosticSeverity::Error; + break; + case DiagnosticKind::Warning: + return CodeCompletionDiagnosticSeverity::Warning; + break; + case DiagnosticKind::Remark: + return CodeCompletionDiagnosticSeverity::Remark; + break; + case DiagnosticKind::Note: + return CodeCompletionDiagnosticSeverity::Note; + break; + } + llvm_unreachable("unhandled DiagnosticKind"); +} + +class CodeCompletionDiagnostics { + ASTContext &Ctx; + DiagnosticEngine &Engine; + +public: + CodeCompletionDiagnostics(ASTContext &Ctx) : Ctx(Ctx), Engine(Ctx.Diags) {} + + template + bool + getDiagnostics(CodeCompletionDiagnosticSeverity &severity, + llvm::raw_ostream &Out, Diag ID, + typename swift::detail::PassArgument::type... VArgs); + + bool getDiagnosticForDeprecated(const ValueDecl *D, + CodeCompletionDiagnosticSeverity &severity, + llvm::raw_ostream &Out); +}; + +template +bool CodeCompletionDiagnostics::getDiagnostics( + CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out, + Diag ID, + typename swift::detail::PassArgument::type... VArgs) { + DiagID id = ID.ID; + std::vector DiagArgs{std::move(VArgs)...}; + auto format = Engine.diagnosticStringFor(id, /*printDiagnosticNames=*/false); + DiagnosticEngine::formatDiagnosticText(Out, format, DiagArgs); + severity = getSeverity(Engine.declaredDiagnosticKindFor(id)); + + return false; +} + +bool CodeCompletionDiagnostics::getDiagnosticForDeprecated( + const ValueDecl *D, CodeCompletionDiagnosticSeverity &severity, + llvm::raw_ostream &Out) { + bool isSoftDeprecated = false; + const AvailableAttr *Attr = D->getAttrs().getDeprecated(Ctx); + if (!Attr) { + Attr = D->getAttrs().getSoftDeprecated(Ctx); + isSoftDeprecated = true; + } + if (!Attr) + return true; + + DeclName Name; + unsigned RawAccessorKind; + std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D); + // FIXME: 'RawAccessorKind' is always 2 (NOT_ACCESSOR_INDEX). + // Code completion doesn't offer accessors. It only emits 'VarDecl's. + // So getter/setter specific availability doesn't work in code completion. + + StringRef Platform = Attr->prettyPlatformString(); + llvm::VersionTuple DeprecatedVersion; + if (Attr->Deprecated) + DeprecatedVersion = Attr->Deprecated.getValue(); + + if (!isSoftDeprecated) { + if (Attr->Message.empty() && Attr->Rename.empty()) { + getDiagnostics(severity, Out, diag::availability_deprecated, + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + Attr->Deprecated.hasValue(), DeprecatedVersion, + /*message*/ StringRef()); + } else if (!Attr->Message.empty()) { + EncodedDiagnosticMessage EncodedMessage(Attr->Message); + getDiagnostics(severity, Out, diag::availability_deprecated, + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + Attr->Deprecated.hasValue(), DeprecatedVersion, + EncodedMessage.Message); + } else { + getDiagnostics(severity, Out, diag::availability_deprecated_rename, + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + Attr->Deprecated.hasValue(), DeprecatedVersion, false, + /*ReplaceKind*/ 0, Attr->Rename); + } + } else { + // '100000' is used as a version number in API that will be deprecated in an + // upcoming release. This number is to match the 'API_TO_BE_DEPRECATED' + // macro defined in Darwin platforms. + static llvm::VersionTuple DISTANT_FUTURE_VESION(100000); + bool isDistantFuture = DeprecatedVersion >= DISTANT_FUTURE_VESION; + + if (Attr->Message.empty() && Attr->Rename.empty()) { + getDiagnostics(severity, Out, diag::ide_availability_softdeprecated, + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + !isDistantFuture, DeprecatedVersion, + /*message*/ StringRef()); + } else if (!Attr->Message.empty()) { + EncodedDiagnosticMessage EncodedMessage(Attr->Message); + getDiagnostics(severity, Out, diag::ide_availability_softdeprecated, + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + !isDistantFuture, DeprecatedVersion, + EncodedMessage.Message); + } else { + getDiagnostics(severity, Out, diag::ide_availability_softdeprecated_rename, + RawAccessorKind, Name, Attr->hasPlatform(), Platform, + !isDistantFuture, DeprecatedVersion, Attr->Rename); + } + } + return false;; +} + +} // namespace + +bool swift::ide::getCompletionDiagnostics( + CodeCompletionResult::NotRecommendedReason reason, const ValueDecl *D, + CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out) { + using NotRecommendedReason = CodeCompletionResult::NotRecommendedReason; + + ASTContext &ctx = D->getASTContext(); + + CodeCompletionDiagnostics Diag(ctx); + switch (reason) { + case NotRecommendedReason::Deprecated: + case NotRecommendedReason::SoftDeprecated: + return Diag.getDiagnosticForDeprecated(D, severity, Out); + case NotRecommendedReason::InvalidAsyncContext: + // FIXME: Could we use 'diag::async_in_nonasync_function'? + return Diag.getDiagnostics(severity, Out, diag::ide_async_in_nonasync_context, + D->getName()); + case NotRecommendedReason::CrossActorReference: + return Diag.getDiagnostics(severity, Out, diag::ide_cross_actor_reference_swift5, + D->getName()); + case NotRecommendedReason::RedundantImport: + return Diag.getDiagnostics(severity, Out, diag::ide_redundant_import, + D->getName()); + case NotRecommendedReason::RedundantImportIndirect: + return Diag.getDiagnostics(severity, Out, diag::ide_redundant_import_indirect, + D->getName()); + case NotRecommendedReason::VariableUsedInOwnDefinition: + return Diag.getDiagnostics(severity, Out, diag::recursive_accessor_reference, + D->getName().getBaseIdentifier(), /*"getter"*/ 0); + case NotRecommendedReason::None: + llvm_unreachable("invalid not recommended reason"); + } + return true; +} + diff --git a/lib/IDE/CodeCompletionDiagnostics.h b/lib/IDE/CodeCompletionDiagnostics.h new file mode 100644 index 0000000000000..46cc1ebe61294 --- /dev/null +++ b/lib/IDE/CodeCompletionDiagnostics.h @@ -0,0 +1,34 @@ +//===--- CodeCompletionDiagnostics.h --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_CODECOMPLETIONDIAGNOSTICS_H +#define SWIFT_IDE_CODECOMPLETIONDIAGNOSTICS_H + +#include "swift/IDE/CodeCompletion.h" + +namespace swift { + +class ValueDecl; + +namespace ide { + +/// Populate \p severity and \p Out with the diagnostics for \p D. +/// Returns \c true if it fails to generate the diagnostics. +bool getCompletionDiagnostics(CodeCompletionResult::NotRecommendedReason reason, + const ValueDecl *D, + CodeCompletionDiagnosticSeverity &severity, + llvm::raw_ostream &Out); + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_CODECOMPLETIONDIAGNOSTICS_H diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index 79f869be2f352..2452b35aa75b8 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -76,6 +76,7 @@ class CodeCompletionResultBuilder { CodeCompletionResultSink &Sink; CodeCompletionResult::ResultKind Kind; SemanticContextKind SemanticContext; + CodeCompletionFlair Flair; unsigned NumBytesToErase = 0; const Decl *AssociatedDecl = nullptr; Optional LiteralKind; @@ -87,12 +88,10 @@ class CodeCompletionResultBuilder { ExpectedTypeContext declTypeContext; CodeCompletionResult::ExpectedTypeRelation ExpectedTypeRelation = CodeCompletionResult::Unknown; - bool IsArgumentLabels = false; bool Cancelled = false; - ArrayRef> CommentWords; CodeCompletionResult::NotRecommendedReason NotRecReason = CodeCompletionResult::NotRecommendedReason::None; - StringRef BriefDocComment = StringRef(); + StringRef BriefDocComment; void addChunkWithText(CodeCompletionString::Chunk::ChunkKind Kind, StringRef Text); @@ -155,8 +154,8 @@ class CodeCompletionResultBuilder { SemanticContext = Kind; } - void setIsArgumentLabels(bool Flag = true) { - IsArgumentLabels = Flag; + void addFlair(CodeCompletionFlair Options) { + Flair |= Options; } void @@ -244,11 +243,6 @@ class CodeCompletionResultBuilder { " async"); } - void addDeclDocCommentWords(ArrayRef> Pairs) { - assert(Kind == CodeCompletionResult::ResultKind::Declaration); - CommentWords = Pairs; - } - void addAnnotatedRethrows() { addRethrows(); getLastChunk().setIsAnnotation(); @@ -360,6 +354,12 @@ class CodeCompletionResultBuilder { addTypeAnnotation(Annotation); } + void addAttributeKeyword(StringRef Name, StringRef Annotation) { + addChunkWithText(CodeCompletionString::Chunk::ChunkKind::Attribute, Name); + if (!Annotation.empty()) + addTypeAnnotation(Annotation); + } + StringRef escapeKeyword(StringRef Word, bool escapeAllKeywords, llvm::SmallString<16> &EscapedKeyword) { EscapedKeyword.clear(); @@ -384,39 +384,39 @@ class CodeCompletionResultBuilder { void addCallParameterColon() { addChunkWithText(CodeCompletionString::Chunk::ChunkKind:: - CallParameterColon, ": "); + CallArgumentColon, ": "); } void addSimpleNamedParameter(StringRef name) { - withNestedGroup(CodeCompletionString::Chunk::ChunkKind::CallParameterBegin, [&] { + withNestedGroup(CodeCompletionString::Chunk::ChunkKind::CallArgumentBegin, [&] { // Use internal, since we don't want the name to be outside the // placeholder. addChunkWithText( - CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName, + CodeCompletionString::Chunk::ChunkKind::CallArgumentInternalName, name); }); } void addSimpleTypedParameter(StringRef Annotation, bool IsVarArg = false) { - withNestedGroup(CodeCompletionString::Chunk::ChunkKind::CallParameterBegin, [&] { + withNestedGroup(CodeCompletionString::Chunk::ChunkKind::CallArgumentBegin, [&] { addChunkWithText( - CodeCompletionString::Chunk::ChunkKind::CallParameterType, + CodeCompletionString::Chunk::ChunkKind::CallArgumentType, Annotation); if (IsVarArg) addEllipsis(); }); } - void addCallParameter(Identifier Name, Identifier LocalName, Type Ty, - Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO, - bool isAutoClosure, bool useUnderscoreLabel, - bool isLabeledTrailingClosure); + void addCallArgument(Identifier Name, Identifier LocalName, Type Ty, + Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO, + bool isAutoClosure, bool useUnderscoreLabel, + bool isLabeledTrailingClosure); - void addCallParameter(Identifier Name, Type Ty, Type ContextTy = Type()) { - addCallParameter(Name, Identifier(), Ty, ContextTy, - /*IsVarArg=*/false, /*IsInOut=*/false, /*isIUO=*/false, - /*isAutoClosure=*/false, /*useUnderscoreLabel=*/false, - /*isLabeledTrailingClosure=*/false); + void addCallArgument(Identifier Name, Type Ty, Type ContextTy = Type()) { + addCallArgument(Name, Identifier(), Ty, ContextTy, + /*IsVarArg=*/false, /*IsInOut=*/false, /*isIUO=*/false, + /*isAutoClosure=*/false, /*useUnderscoreLabel=*/false, + /*isLabeledTrailingClosure=*/false); } void addGenericParameter(StringRef Name) { diff --git a/lib/IDE/CodeCompletionResultPrinter.cpp b/lib/IDE/CodeCompletionResultPrinter.cpp index e86d8cb422d64..0e6910b1e84de 100644 --- a/lib/IDE/CodeCompletionResultPrinter.cpp +++ b/lib/IDE/CodeCompletionResultPrinter.cpp @@ -39,8 +39,8 @@ void swift::ide::printCodeCompletionResultDescription( using ChunkKind = CodeCompletionString::Chunk::ChunkKind; if (C.is(ChunkKind::TypeAnnotation) || - C.is(ChunkKind::CallParameterClosureType) || - C.is(ChunkKind::CallParameterClosureExpr) || + C.is(ChunkKind::CallArgumentClosureType) || + C.is(ChunkKind::CallArgumentClosureExpr) || C.is(ChunkKind::Whitespace)) continue; @@ -52,9 +52,9 @@ void swift::ide::printCodeCompletionResultDescription( continue; } - if (isOperator && C.is(ChunkKind::CallParameterType)) + if (isOperator && C.is(ChunkKind::CallArgumentType)) continue; - if (isOperator && C.is(ChunkKind::CallParameterTypeBegin)) { + if (isOperator && C.is(ChunkKind::CallArgumentTypeBegin)) { auto level = I->getNestingLevel(); do { ++I; } while (I != E && !I->endsPreviousNestedGroup(level)); --I; @@ -117,15 +117,15 @@ class AnnotatingResultPrinter { case ChunkKind::TypeIdUser: printWithTag("typeid.user", C.getText()); break; - case ChunkKind::CallParameterName: + case ChunkKind::CallArgumentName: printWithTag("callarg.label", C.getText()); break; - case ChunkKind::CallParameterInternalName: + case ChunkKind::CallArgumentInternalName: printWithTag("callarg.param", C.getText()); break; case ChunkKind::TypeAnnotation: - case ChunkKind::CallParameterClosureType: - case ChunkKind::CallParameterClosureExpr: + case ChunkKind::CallArgumentClosureType: + case ChunkKind::CallArgumentClosureExpr: case ChunkKind::Whitespace: // ignore; break; @@ -140,7 +140,7 @@ class AnnotatingResultPrinter { for (auto i = chunks.begin(), e = chunks.end(); i != e; ++i) { using ChunkKind = CodeCompletionString::Chunk::ChunkKind; - if (i->is(ChunkKind::CallParameterTypeBegin)) { + if (i->is(ChunkKind::CallArgumentTypeBegin)) { OS << ""; auto nestingLevel = i->getNestingLevel(); ++i; @@ -182,7 +182,7 @@ class AnnotatingResultPrinter { } // Print call argument group. - if (i->is(ChunkKind::CallParameterBegin)) { + if (i->is(ChunkKind::CallArgumentBegin)) { auto start = i; auto level = i->getNestingLevel(); do { ++i; } while (i != e && !i->endsPreviousNestedGroup(level)); @@ -192,7 +192,7 @@ class AnnotatingResultPrinter { continue; } - if (isOperator && i->is(ChunkKind::CallParameterType)) + if (isOperator && i->is(ChunkKind::CallArgumentType)) continue; printTextChunk(*i); } @@ -261,16 +261,16 @@ void swift::ide::printCodeCompletionResultTypeNameAnnotated(const CodeCompletion static void constructTextForCallParam(ArrayRef ParamGroup, raw_ostream &OS) { - assert(ParamGroup.front().is(ChunkKind::CallParameterBegin)); + assert(ParamGroup.front().is(ChunkKind::CallArgumentBegin)); for (; !ParamGroup.empty(); ParamGroup = ParamGroup.slice(1)) { auto &C = ParamGroup.front(); if (C.isAnnotation()) continue; - if (C.is(ChunkKind::CallParameterInternalName) || - C.is(ChunkKind::CallParameterType) || - C.is(ChunkKind::CallParameterTypeBegin) || - C.is(ChunkKind::CallParameterClosureExpr)) { + if (C.is(ChunkKind::CallArgumentInternalName) || + C.is(ChunkKind::CallArgumentType) || + C.is(ChunkKind::CallArgumentTypeBegin) || + C.is(ChunkKind::CallArgumentClosureExpr)) { break; } if (!C.hasText()) @@ -284,7 +284,7 @@ constructTextForCallParam(ArrayRef ParamGroup, for (auto i = ParamGroup.begin(), e = ParamGroup.end(); i != e; ++i) { auto &C = *i; - if (C.is(ChunkKind::CallParameterTypeBegin)) { + if (C.is(ChunkKind::CallArgumentTypeBegin)) { assert(TypeString.empty()); auto nestingLevel = C.getNestingLevel(); ++i; @@ -299,16 +299,16 @@ constructTextForCallParam(ArrayRef ParamGroup, --i; continue; } - if (C.is(ChunkKind::CallParameterClosureType)) { + if (C.is(ChunkKind::CallArgumentClosureType)) { assert(ExpansionTypeString.empty()); ExpansionTypeString = C.getText(); continue; } - if (C.is(ChunkKind::CallParameterType)) { + if (C.is(ChunkKind::CallArgumentType)) { assert(TypeString.empty()); TypeString = C.getText(); } - if (C.is(ChunkKind::CallParameterClosureExpr)) { + if (C.is(ChunkKind::CallArgumentClosureExpr)) { // We have a closure expression, so provide it directly instead of in // a placeholder. OS << "{"; @@ -348,7 +348,7 @@ void swift::ide::printCodeCompletionResultSourceText( OS << " {\n" << getCodePlaceholder() << "\n}"; continue; } - if (C.is(ChunkKind::CallParameterBegin)) { + if (C.is(ChunkKind::CallArgumentBegin)) { size_t Start = i++; for (; i < Chunks.size(); ++i) { if (Chunks[i].endsPreviousNestedGroup(C.getNestingLevel())) @@ -403,10 +403,10 @@ void swift::ide::printCodeCompletionResultFilterName( bool shouldPrint = !C.isAnnotation(); switch (C.getKind()) { case ChunkKind::TypeAnnotation: - case ChunkKind::CallParameterInternalName: - case ChunkKind::CallParameterClosureType: - case ChunkKind::CallParameterClosureExpr: - case ChunkKind::CallParameterType: + case ChunkKind::CallArgumentInternalName: + case ChunkKind::CallArgumentClosureType: + case ChunkKind::CallArgumentClosureExpr: + case ChunkKind::CallArgumentType: case ChunkKind::DeclAttrParamColon: case ChunkKind::Comma: case ChunkKind::Whitespace: @@ -414,7 +414,7 @@ void swift::ide::printCodeCompletionResultFilterName( case ChunkKind::Ampersand: case ChunkKind::OptionalMethodCallTail: continue; - case ChunkKind::CallParameterTypeBegin: + case ChunkKind::CallArgumentTypeBegin: case ChunkKind::TypeAnnotationBegin: { // Skip call parameter type or type annotation structure. auto nestingLevel = C.getNestingLevel(); @@ -424,7 +424,7 @@ void swift::ide::printCodeCompletionResultFilterName( --i; continue; } - case ChunkKind::CallParameterColon: + case ChunkKind::CallArgumentColon: // Since we don't add the type, also don't add the space after ':'. if (shouldPrint) OS << ":"; diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 9c91fe0b36c5e..259b21fa7e0ca 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -29,6 +29,7 @@ #include "swift/Parse/PersistentParserState.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Subsystems.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "clang/AST/ASTContext.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/MemoryBuffer.h" @@ -330,9 +331,10 @@ bool CompletionInstance::performCachedOperationIfPossible( SearchPathOptions searchPathOpts = CI.getASTContext().SearchPathOpts; DiagnosticEngine tmpDiags(tmpSM); ClangImporterOptions clangOpts; + symbolgraphgen::SymbolGraphOptions symbolOpts; std::unique_ptr tmpCtx( - ASTContext::get(langOpts, typeckOpts, searchPathOpts, clangOpts, tmpSM, - tmpDiags)); + ASTContext::get(langOpts, typeckOpts, searchPathOpts, clangOpts, + symbolOpts, tmpSM, tmpDiags)); registerParseRequestFunctions(tmpCtx->evaluator); registerIDERequestFunctions(tmpCtx->evaluator); registerTypeCheckerRequestFunctions(tmpCtx->evaluator); @@ -611,10 +613,6 @@ bool swift::ide::CompletionInstance::performOperation( std::string &Error, DiagnosticConsumer *DiagC, llvm::function_ref Callback) { - // Always disable source location resolutions from .swiftsourceinfo file - // because they're somewhat heavy operations and aren't needed for completion. - Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; - // Disable to build syntax tree because code-completion skips some portion of // source text. That breaks an invariant of syntax tree building. Invocation.getLangOptions().BuildSyntaxTree = false; diff --git a/lib/IDE/ConformingMethodList.cpp b/lib/IDE/ConformingMethodList.cpp index e0e6c627a6d63..36d1e8f706cfc 100644 --- a/lib/IDE/ConformingMethodList.cpp +++ b/lib/IDE/ConformingMethodList.cpp @@ -129,22 +129,20 @@ void ConformingMethodListCallbacks::getMatchingMethods( /// Returns true if \p VD is a instance method whose return type conforms /// to the requested protocols. bool isMatchingMethod(ValueDecl *VD) { - if (!isa(VD)) + auto *FD = dyn_cast(VD); + if (!FD) return false; - if (VD->isStatic() || VD->isOperator()) + if (FD->isStatic() || FD->isOperator()) return false; - auto declTy = T->getTypeOfMember(CurModule, VD); - if (declTy->is()) + auto resultTy = T->getTypeOfMember(CurModule, FD, + FD->getResultInterfaceType()); + if (resultTy->is()) return false; - // Strip '(Self.Type) ->' and parameters. - declTy = declTy->castTo()->getResult(); - declTy = declTy->castTo()->getResult(); - // The return type conforms to any of the requested protocols. for (auto Proto : ExpectedTypes) { - if (CurModule->conformsToProtocol(declTy, Proto)) + if (CurModule->conformsToProtocol(resultTy, Proto)) return true; } diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 75f24d9b4d4b6..35cbc3efce163 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -456,7 +456,8 @@ static void collectPossibleCalleesByQualifiedLookup( llvm::DenseMap, size_t> known; auto *baseNominal = baseInstanceTy->getAnyNominal(); for (auto *VD : decls) { - if ((!isa(VD) && !isa(VD)) || + if ((!isa(VD) && !isa(VD) && + !isa(VD)) || VD->shouldHideFromEditor()) continue; if (!isMemberDeclApplied(&DC, baseInstanceTy, VD)) @@ -479,6 +480,11 @@ static void collectPossibleCalleesByQualifiedLookup( } else if (isa(VD)) { if (isOnMetaType != VD->isStatic()) continue; + } else if (isa(VD)) { + if (!isOnMetaType) + continue; + declaredMemberType = + declaredMemberType->castTo()->getResult(); } } @@ -682,7 +688,7 @@ static bool collectPossibleCalleesForApply( fnType = *fnTypeOpt; } - if (!fnType || fnType->hasUnresolvedType() || fnType->hasError()) + if (!fnType || fnType->hasError()) return false; fnType = fnType->getWithoutSpecifierType(); @@ -771,22 +777,22 @@ static Expr *getArgAtPosition(Expr *Args, unsigned Position) { /// be different than the position in \p Args if there are defaulted arguments /// in \p Params which don't occur in \p Args. /// -/// \returns \c true if success, \c false if \p CCExpr is not a part of \p Args. -static bool getPositionInParams(DeclContext &DC, Expr *Args, Expr *CCExpr, - ArrayRef Params, - unsigned &PosInParams) { +/// \returns the position index number on success, \c None if \p CCExpr is not +/// a part of \p Args. +static Optional +getPositionInParams(DeclContext &DC, Expr *Args, Expr *CCExpr, + ArrayRef Params, bool Lenient) { if (isa(Args)) { - PosInParams = 0; - return true; + return 0; } auto *tuple = dyn_cast(Args); if (!tuple) { - return false; + return None; } auto &SM = DC.getASTContext().SourceMgr; - PosInParams = 0; + unsigned PosInParams = 0; unsigned PosInArgs = 0; bool LastParamWasVariadic = false; // We advance PosInArgs until we find argument that is after the code @@ -847,16 +853,22 @@ static bool getPositionInParams(DeclContext &DC, Expr *Args, Expr *CCExpr, } if (!AdvancedPosInParams) { - // We haven't performed any special advance logic. Assume the argument - // and parameter match, so advance PosInParams by 1. - ++PosInParams; + if (Lenient) { + // We haven't performed any special advance logic. Assume the argument + // and parameter match, so advance PosInParams by 1. + PosInParams += 1; + } else { + // If there is no matching argument label. These arguments can't be + // applied to the params. + return None; + } } } if (PosInArgs < tuple->getNumElements() && PosInParams < Params.size()) { // We didn't search until the end, so we found a position in Params. Success - return true; + return PosInParams; } else { - return false; + return None; } } @@ -947,21 +959,45 @@ class ExprContextAnalyzer { } SmallPtrSet seenTypes; llvm::SmallSet, 4> seenArgs; - for (auto &typeAndDecl : Candidates) { - DeclContext *memberDC = nullptr; - if (typeAndDecl.Decl) - memberDC = typeAndDecl.Decl->getInnermostDeclContext(); + llvm::SmallVector, 2> posInParams; + { + bool found = false; + for (auto &typeAndDecl : Candidates) { + Optional pos = getPositionInParams( + *DC, Args, ParsedExpr, typeAndDecl.Type->getParams(), + /*lenient=*/false); + posInParams.push_back(pos); + found |= pos.hasValue(); + } + if (!found) { + // If applicable overload is not found, retry with considering + // non-matching argument labels mis-typed. + for (auto i : indices(Candidates)) { + posInParams[i] = getPositionInParams( + *DC, Args, ParsedExpr, Candidates[i].Type->getParams(), + /*lenient=*/true); + } + } + } + assert(posInParams.size() == Candidates.size()); - auto Params = typeAndDecl.Type->getParams(); - unsigned PositionInParams; - if (!getPositionInParams(*DC, Args, ParsedExpr, Params, - PositionInParams)) { + for (auto i : indices(Candidates)) { + if (!posInParams[i].hasValue()) { // If the argument doesn't have a matching position in the parameters, // indicate that with optional nullptr param. if (seenArgs.insert({Identifier(), CanType()}).second) recordPossibleParam(nullptr, /*isRequired=*/false); continue; } + + auto &typeAndDecl = Candidates[i]; + DeclContext *memberDC = nullptr; + if (typeAndDecl.Decl) + memberDC = typeAndDecl.Decl->getInnermostDeclContext(); + + auto Params = typeAndDecl.Type->getParams(); + auto PositionInParams = *posInParams[i]; + ParameterList *paramList = nullptr; if (auto VD = typeAndDecl.Decl) { paramList = getParameterList(VD); @@ -1260,8 +1296,8 @@ class ExprContextAnalyzer { auto AFD = dyn_cast(initDC->getParent()); if (!AFD) return; - auto *param = initDC->getParam(); - recordPossibleType(AFD->mapTypeIntoContext(param->getInterfaceType())); + auto *var = initDC->getWrappedVar(); + recordPossibleType(AFD->mapTypeIntoContext(var->getInterfaceType())); break; } } diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 21813616f17c0..5ac16c39173a4 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -2048,7 +2048,7 @@ class FormatWalker : public ASTWalker { } Optional - getIndentContextFromInherits(ArrayRef Inherits, + getIndentContextFromInherits(ArrayRef Inherits, SourceLoc ContextLoc) { if (Inherits.empty()) return None; @@ -2586,18 +2586,17 @@ class FormatWalker : public ASTWalker { ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R); for (auto &Entry: ParentCapture->getCaptureList()) { - if (auto *PBD = Entry.Init) { - NodesToSkip.insert(PBD); - SourceRange Range = PBD->getSourceRangeIncludingAttrs(); - Aligner.updateAlignment(Range, PBD); + auto *PBD = Entry.PBD; + NodesToSkip.insert(PBD); + SourceRange Range = PBD->getSourceRangeIncludingAttrs(); + Aligner.updateAlignment(Range, PBD); - if (isTargetContext(Range)) { - Aligner.setAlignmentIfNeeded(CtxOverride); - return IndentContext { - Range.Start, - !OutdentChecker::hasOutdent(SM, Range, PBD) - }; - } + if (isTargetContext(Range)) { + Aligner.setAlignmentIfNeeded(CtxOverride); + return IndentContext { + Range.Start, + !OutdentChecker::hasOutdent(SM, Range, PBD) + }; } } return Aligner.getContextAndSetAlignment(CtxOverride); diff --git a/lib/IDE/IDERequests.cpp b/lib/IDE/IDERequests.cpp index 041094c7307b9..5530b30f221c4 100644 --- a/lib/IDE/IDERequests.cpp +++ b/lib/IDE/IDERequests.cpp @@ -12,6 +12,7 @@ #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" +#include "swift/AST/Effects.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ASTDemangler.h" #include "swift/Basic/SourceManager.h" @@ -377,41 +378,45 @@ class RangeResolver : public SourceEntityWalker { ResolvedRangeInfo resolve(); }; -static bool hasUnhandledError(ArrayRef Nodes) { - class ThrowingEntityAnalyzer : public SourceEntityWalker { - bool Throwing; +static PossibleEffects getUnhandledEffects(ArrayRef Nodes) { + class EffectsAnalyzer : public SourceEntityWalker { + PossibleEffects Effects; public: - ThrowingEntityAnalyzer(): Throwing(false) {} bool walkToStmtPre(Stmt *S) override { if (auto DCS = dyn_cast(S)) { if (DCS->isSyntacticallyExhaustive()) return false; - Throwing = true; + Effects |= EffectKind::Throws; } else if (isa(S)) { - Throwing = true; + Effects |= EffectKind::Throws; } - return !Throwing; + return true; } bool walkToExprPre(Expr *E) override { - if (isa(E)) { - Throwing = true; - } - return !Throwing; + // Don't walk into closures, they only produce effects when called. + if (isa(E)) + return false; + + if (isa(E)) + Effects |= EffectKind::Throws; + if (isa(E)) + Effects |= EffectKind::Async; + + return true; } bool walkToDeclPre(Decl *D, CharSourceRange Range) override { return false; } - bool walkToDeclPost(Decl *D) override { return !Throwing; } - bool walkToStmtPost(Stmt *S) override { return !Throwing; } - bool walkToExprPost(Expr *E) override { return !Throwing; } - bool isThrowing() { return Throwing; } + PossibleEffects getEffects() const { return Effects; } }; - return Nodes.end() != std::find_if(Nodes.begin(), Nodes.end(), [](ASTNode N) { - ThrowingEntityAnalyzer Analyzer; + PossibleEffects Effects; + for (auto N : Nodes) { + EffectsAnalyzer Analyzer; Analyzer.walk(N); - return Analyzer.isThrowing(); - }); + Effects |= Analyzer.getEffects(); + } + return Effects; } struct RangeResolver::Implementation { @@ -549,7 +554,7 @@ struct RangeResolver::Implementation { assert(ContainedASTNodes.size() == 1); // Single node implies single entry point, or is it? bool SingleEntry = true; - bool UnhandledError = hasUnhandledError({Node}); + auto UnhandledEffects = getUnhandledEffects({Node}); OrphanKind Kind = getOrphanKind(ContainedASTNodes); if (Node.is()) return ResolvedRangeInfo(RangeKind::SingleExpression, @@ -558,7 +563,7 @@ struct RangeResolver::Implementation { getImmediateContext(), /*Common Parent Expr*/nullptr, SingleEntry, - UnhandledError, Kind, + UnhandledEffects, Kind, llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), llvm::makeArrayRef(ReferencedDecls)); @@ -569,7 +574,7 @@ struct RangeResolver::Implementation { getImmediateContext(), /*Common Parent Expr*/nullptr, SingleEntry, - UnhandledError, Kind, + UnhandledEffects, Kind, llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), llvm::makeArrayRef(ReferencedDecls)); @@ -581,7 +586,7 @@ struct RangeResolver::Implementation { getImmediateContext(), /*Common Parent Expr*/nullptr, SingleEntry, - UnhandledError, Kind, + UnhandledEffects, Kind, llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), llvm::makeArrayRef(ReferencedDecls)); @@ -642,7 +647,7 @@ struct RangeResolver::Implementation { getImmediateContext(), Parent, hasSingleEntryPoint(ContainedASTNodes), - hasUnhandledError(ContainedASTNodes), + getUnhandledEffects(ContainedASTNodes), getOrphanKind(ContainedASTNodes), llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), @@ -889,7 +894,7 @@ struct RangeResolver::Implementation { TokensInRange, getImmediateContext(), nullptr, hasSingleEntryPoint(ContainedASTNodes), - hasUnhandledError(ContainedASTNodes), + getUnhandledEffects(ContainedASTNodes), getOrphanKind(ContainedASTNodes), llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), @@ -904,7 +909,7 @@ struct RangeResolver::Implementation { getImmediateContext(), /*Common Parent Expr*/ nullptr, /*SinleEntry*/ true, - hasUnhandledError(ContainedASTNodes), + getUnhandledEffects(ContainedASTNodes), getOrphanKind(ContainedASTNodes), llvm::makeArrayRef(ContainedASTNodes), llvm::makeArrayRef(DeclaredDecls), diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index 8b8a80befaa68..580f10598f93e 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/Requirement.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Types.h" #include "swift/Sema/IDETypeChecking.h" @@ -169,13 +170,13 @@ struct SynthesizedExtensionAnalyzer::Implementation { bool Unmergable; unsigned InheritsCount; std::set Requirements; - void addRequirement(GenericSignature GenericSig, - Type First, Type Second, RequirementKind Kind) { - CanType CanFirst = GenericSig->getCanonicalTypeInContext(First); - CanType CanSecond; - if (Second) CanSecond = GenericSig->getCanonicalTypeInContext(Second); + void addRequirement(swift::Requirement Req) { + auto First = Req.getFirstType(); + auto CanFirst = First->getCanonicalType(); + auto Second = Req.getSecondType(); + auto CanSecond = Second->getCanonicalType(); - Requirements.insert({First, Second, Kind, CanFirst, CanSecond}); + Requirements.insert({First, Second, Req.getKind(), CanFirst, CanSecond}); } bool operator== (const ExtensionMergeInfo& Another) const { // Trivially unmergeable. @@ -254,7 +255,7 @@ struct SynthesizedExtensionAnalyzer::Implementation { InfoMap(collectSynthesizedExtensionInfo(AllGroups)) {} unsigned countInherits(ExtensionDecl *ED) { - SmallVector Results; + SmallVector Results; getInheritedForPrinting(ED, Options, Results); return Results.size(); } @@ -283,90 +284,58 @@ struct SynthesizedExtensionAnalyzer::Implementation { } auto handleRequirements = [&](SubstitutionMap subMap, - GenericSignature GenericSig, ExtensionDecl *OwningExt, ArrayRef Reqs) { ProtocolDecl *BaseProto = OwningExt->getInnermostDeclContext() ->getSelfProtocolDecl(); for (auto Req : Reqs) { - auto Kind = Req.getKind(); - - // FIXME: Could do something here - if (Kind == RequirementKind::Layout) + // FIXME: Don't skip layout requirements. + if (Req.getKind() == RequirementKind::Layout) continue; - Type First = Req.getFirstType(); - Type Second = Req.getSecondType(); - // Skip protocol's Self : requirement. if (BaseProto && Req.getKind() == RequirementKind::Conformance && - First->isEqual(BaseProto->getSelfInterfaceType()) && - Second->getAnyNominal() == BaseProto) + Req.getFirstType()->isEqual(BaseProto->getSelfInterfaceType()) && + Req.getProtocolDecl() == BaseProto) continue; if (!BaseType->isExistentialType()) { // Apply any substitutions we need to map the requirements from a // a protocol extension to an extension on the conforming type. - First = First.subst(subMap); - Second = Second.subst(subMap); - - if (First->hasError() || Second->hasError()) { + auto SubstReq = Req.subst(subMap); + if (!SubstReq) { // Substitution with interface type bases can only fail // if a concrete type fails to conform to a protocol. // In this case, just give up on the extension altogether. return true; } - } - assert(!First->hasArchetype() && !Second->hasArchetype()); - switch (Kind) { - case RequirementKind::Conformance: { - auto *M = DC->getParentModule(); - auto *Proto = Second->castTo()->getDecl(); - if (!First->isTypeParameter() && - M->conformsToProtocol(First, Proto).isInvalid()) - return true; - if (M->conformsToProtocol(First, Proto).isInvalid()) - MergeInfo.addRequirement(GenericSig, First, Second, Kind); - break; + Req = *SubstReq; } - case RequirementKind::Superclass: - // If the subject type of the requirement is still a type parameter, - // we need to check if the contextual type could possibly be bound to - // the superclass. If not, this extension isn't applicable. - if (First->isTypeParameter()) { - if (!Target->mapTypeIntoContext(First)->isBindableTo( - Target->mapTypeIntoContext(Second))) { - return true; - } - MergeInfo.addRequirement(GenericSig, First, Second, Kind); - break; - } - - // If we've substituted in a concrete type for the subject, we can - // check for an exact superclass match, and disregard the extension if - // it missed. - // FIXME: What if it ends being something like `C : C`? - // Arguably we should allow that to be mirrored with a U == Int - // constraint. - if (!Second->isExactSuperclassOf(First)) - return true; - - break; - - case RequirementKind::SameType: - if (!First->isBindableTo(Second) && - !Second->isBindableTo(First)) { + assert(!Req.getFirstType()->hasArchetype()); + assert(!Req.getSecondType()->hasArchetype()); + + auto *M = DC->getParentModule(); + auto SubstReq = Req.subst( + [&](Type type) -> Type { + if (type->isTypeParameter()) + return Target->mapTypeIntoContext(type); + + return type; + }, + LookUpConformanceInModule(M)); + if (!SubstReq) + return true; + + // FIXME: Need to handle conditional requirements here! + ArrayRef conditionalRequirements; + if (!SubstReq->isSatisfied(conditionalRequirements)) { + if (!SubstReq->canBeSatisfied()) return true; - } else if (!First->isEqual(Second)) { - MergeInfo.addRequirement(GenericSig, First, Second, Kind); - } - break; - case RequirementKind::Layout: - llvm_unreachable("Handled above"); + MergeInfo.addRequirement(Req); } } return false; @@ -385,7 +354,7 @@ struct SynthesizedExtensionAnalyzer::Implementation { assert(Ext->getGenericSignature() && "No generic signature."); auto GenericSig = Ext->getGenericSignature(); - if (handleRequirements(subMap, GenericSig, Ext, GenericSig->getRequirements())) + if (handleRequirements(subMap, Ext, GenericSig.getRequirements())) return {Result, MergeInfo}; } @@ -396,7 +365,6 @@ struct SynthesizedExtensionAnalyzer::Implementation { subMap = BaseType->getContextSubstitutionMap(M, NTD); } if (handleRequirements(subMap, - Conf->getGenericSignature(), EnablingExt, Conf->getConditionalRequirements())) return {Result, MergeInfo}; @@ -761,6 +729,117 @@ swift::collectExpressionType(SourceFile &SF, return Scratch; } +/// This walker will traverse the AST and report types for every variable +/// declaration. +class VariableTypeCollector : public SourceEntityWalker { +private: + const SourceManager &SM; + unsigned int BufferId; + + /// The range in which variable types are to be collected. + SourceRange TotalRange; + + /// The output vector for VariableTypeInfos emitted during traversal. + std::vector &Results; + + /// We print all types into a single output stream (e.g. into a string buffer) + /// and provide offsets into this string buffer to describe individual types, + /// i.e. \c OS builds a string that contains all null-terminated printed type + /// strings. When referring to one of these types, we can use the offsets at + /// which it starts in the \c OS. + llvm::raw_ostream &OS; + + /// Map from a printed type to the offset in \c OS where the type starts. + llvm::StringMap TypeOffsets; + + /// Returns the start offset of this string in \c OS. If \c PrintedType + /// hasn't been printed to \c OS yet, this function will do so. + uint32_t getTypeOffset(StringRef PrintedType) { + auto It = TypeOffsets.find(PrintedType); + if (It == TypeOffsets.end()) { + TypeOffsets[PrintedType] = OS.tell(); + OS << PrintedType << '\0'; + } + return TypeOffsets[PrintedType]; + } + + /// Checks whether the given range overlaps the total range in which we + /// collect variable types. + bool overlapsTotalRange(SourceRange Range) { + return TotalRange.isInvalid() || Range.overlaps(TotalRange); + } + +public: + VariableTypeCollector(const SourceFile &SF, SourceRange Range, + std::vector &Results, + llvm::raw_ostream &OS) + : SM(SF.getASTContext().SourceMgr), BufferId(*SF.getBufferID()), + TotalRange(Range), Results(Results), OS(OS) {} + + bool walkToDeclPre(Decl *D, CharSourceRange DeclNameRange) override { + if (DeclNameRange.isInvalid()) { + return true; + } + // Skip this declaration and its subtree if outside the range + if (!overlapsTotalRange(D->getSourceRange())) { + return false; + } + if (auto VD = dyn_cast(D)) { + unsigned VarOffset = + SM.getLocOffsetInBuffer(DeclNameRange.getStart(), BufferId); + unsigned VarLength = DeclNameRange.getByteLength(); + // Print the type to a temporary buffer + SmallString<64> Buffer; + { + llvm::raw_svector_ostream OS(Buffer); + PrintOptions Options; + Options.SynthesizeSugarOnTypes = true; + auto Ty = VD->getType(); + // Skip this declaration and its children if the type is an error type. + if (Ty->is()) { + return false; + } + Ty->print(OS, Options); + } + // Transfer the type to `OS` if needed and get the offset of this string + // in `OS`. + auto TyOffset = getTypeOffset(Buffer.str()); + bool HasExplicitType = + VD->getTypeReprOrParentPatternTypeRepr() != nullptr; + // Add the type information to the result list. + Results.emplace_back(VarOffset, VarLength, HasExplicitType, TyOffset); + } + return true; + } + + bool walkToStmtPre(Stmt *S) override { + // Skip this statement and its subtree if outside the range + return overlapsTotalRange(S->getSourceRange()); + } + + bool walkToExprPre(Expr *E) override { + // Skip this expression and its subtree if outside the range + return overlapsTotalRange(E->getSourceRange()); + } + + bool walkToPatternPre(Pattern *P) override { + // Skip this pattern and its subtree if outside the range + return overlapsTotalRange(P->getSourceRange()); + } +}; + +VariableTypeInfo::VariableTypeInfo(uint32_t Offset, uint32_t Length, + bool HasExplicitType, uint32_t TypeOffset) + : Offset(Offset), Length(Length), HasExplicitType(HasExplicitType), + TypeOffset(TypeOffset) {} + +void swift::collectVariableType( + SourceFile &SF, SourceRange Range, + std::vector &VariableTypeInfos, llvm::raw_ostream &OS) { + VariableTypeCollector Walker(SF, Range, VariableTypeInfos, OS); + Walker.walk(SF); +} + ArrayRef swift:: canDeclProvideDefaultImplementationFor(ValueDecl* VD) { return evaluateOrDefault(VD->getASTContext().evaluator, diff --git a/lib/IDE/ModuleSourceFileInfo.cpp b/lib/IDE/ModuleSourceFileInfo.cpp new file mode 100644 index 0000000000000..61406d2eed3eb --- /dev/null +++ b/lib/IDE/ModuleSourceFileInfo.cpp @@ -0,0 +1,90 @@ +//===--- ModuleSourceFileInfo.cpp -------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/IDE/ModuleSourceFileInfo.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/Module.h" +#include "swift/AST/SourceFile.h" +#include "swift/Basic/SourceManager.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" +#include "swift/Subsystems.h" + +using namespace swift; +using namespace swift::ide; + +StringRef swift::ide::getSourceFilePathForDecl(const Decl *D) { + auto *DC = D->getDeclContext(); + FileUnit *fileUnit = dyn_cast(DC->getModuleScopeContext()); + if (!fileUnit) + return ""; + + if (auto *SF = dyn_cast(fileUnit)) + return SF->getFilename(); + if (auto loc = fileUnit->getExternalRawLocsForDecl(D)) + return loc->SourceFilePath; + + return ""; +} + +bool swift::ide::isSourceFileUpToDate(const BasicSourceFileInfo &info, + ASTContext &Ctx) { + auto &SM = Ctx.SourceMgr; + auto stat = SM.getFileSystem()->status(info.getFilePath()); + // If missing, it's not up-to-date. + if (!stat) + return false; + + // Assume up-to-date if the modification time and the size are the same. + if (stat->getLastModificationTime() == info.getLastModified() && + stat->getSize() == info.getFileSize()) + return true; + + // If the interface hash is unknown, we can't compare it. + if (info.getInterfaceHashIncludingTypeMembers() == Fingerprint::ZERO()) + return false; + + // Check if the interface hash has changed. + + auto buffer = SM.getFileSystem()->getBufferForFile(info.getFilePath()); + // If failed to open, it's not up-to-date. + if (!buffer) + return false; + + SourceManager tmpSM; + auto tmpBufferID = tmpSM.addNewSourceBuffer(std::move(*buffer)); + + // FIXME: Using the current options is not correct because the module might be + // compiled with different options. We could use serialized search paths etc. + LangOptions langOpts = Ctx.LangOpts; + TypeCheckerOptions typechkOpts = Ctx.TypeCheckerOpts; + SearchPathOptions searchPathOpts = Ctx.SearchPathOpts; + + DiagnosticEngine tmpDiags(tmpSM); + ClangImporterOptions clangOpts; + symbolgraphgen::SymbolGraphOptions symbolOpts; + std::unique_ptr tmpCtx( + ASTContext::get(langOpts, typechkOpts, searchPathOpts, clangOpts, + symbolOpts, tmpSM, tmpDiags)); + registerParseRequestFunctions(tmpCtx->evaluator); + registerIDERequestFunctions(tmpCtx->evaluator); + registerTypeCheckerRequestFunctions(tmpCtx->evaluator); + registerSILGenRequestFunctions(tmpCtx->evaluator); + ModuleDecl *tmpM = ModuleDecl::create(Identifier(), *tmpCtx); + SourceFile::ParsingOptions parseOpts; + parseOpts |= SourceFile::ParsingFlags::EnableInterfaceHash; + SourceFile *tmpSF = new (*tmpCtx) + SourceFile(*tmpM, SourceFileKind::Library, tmpBufferID, parseOpts); + auto fingerprint = tmpSF->getInterfaceHashIncludingTypeMembers(); + return fingerprint == info.getInterfaceHashIncludingTypeMembers(); +} diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index 6d69350632f19..ec50091fd2f22 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -67,23 +67,23 @@ static std::string toInsertableString(CodeCompletionResult *Result) { Str += C.getText(); break; - case CodeCompletionString::Chunk::ChunkKind::CallParameterName: - case CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName: - case CodeCompletionString::Chunk::ChunkKind::CallParameterColon: + case CodeCompletionString::Chunk::ChunkKind::CallArgumentName: + case CodeCompletionString::Chunk::ChunkKind::CallArgumentInternalName: + case CodeCompletionString::Chunk::ChunkKind::CallArgumentColon: case CodeCompletionString::Chunk::ChunkKind::DeclAttrParamKeyword: case CodeCompletionString::Chunk::ChunkKind::DeclAttrParamColon: - case CodeCompletionString::Chunk::ChunkKind::CallParameterType: - case CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType: + case CodeCompletionString::Chunk::ChunkKind::CallArgumentType: + case CodeCompletionString::Chunk::ChunkKind::CallArgumentClosureType: case CodeCompletionString::Chunk::ChunkKind::OptionalBegin: - case CodeCompletionString::Chunk::ChunkKind::CallParameterBegin: - case CodeCompletionString::Chunk::ChunkKind::CallParameterTypeBegin: + case CodeCompletionString::Chunk::ChunkKind::CallArgumentBegin: + case CodeCompletionString::Chunk::ChunkKind::CallArgumentTypeBegin: case CodeCompletionString::Chunk::ChunkKind::GenericParameterBegin: case CodeCompletionString::Chunk::ChunkKind::GenericParameterName: case CodeCompletionString::Chunk::ChunkKind::TypeAnnotation: case CodeCompletionString::Chunk::ChunkKind::TypeAnnotationBegin: return Str; - case CodeCompletionString::Chunk::ChunkKind::CallParameterClosureExpr: + case CodeCompletionString::Chunk::ChunkKind::CallArgumentClosureExpr: Str += " {"; Str += C.getText(); break; @@ -165,7 +165,8 @@ class REPLCodeCompletionConsumer : public SimpleCachingCodeCompletionConsumer { REPLCodeCompletionConsumer(REPLCompletions &Completions) : Completions(Completions) {} - void handleResults(MutableArrayRef Results) override { + void handleResults(CodeCompletionContext &context) override { + MutableArrayRef Results = context.takeResults(); CodeCompletionContext::sortCompletionResults(Results); for (auto Result : Results) { std::string InsertableString = toInsertableString(Result); diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 01f9016f13615..c2681580488c1 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -16,6 +16,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsRefactoring.h" #include "swift/AST/Expr.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/GenericParamList.h" #include "swift/AST/NameLookup.h" #include "swift/AST/Pattern.h" @@ -25,14 +26,15 @@ #include "swift/AST/USRGeneration.h" #include "swift/Basic/Edit.h" #include "swift/Basic/StringExtras.h" +#include "swift/ClangImporter/ClangImporter.h" #include "swift/Frontend/Frontend.h" #include "swift/IDE/IDERequests.h" #include "swift/Index/Index.h" -#include "swift/ClangImporter/ClangImporter.h" #include "swift/Parse/Lexer.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Subsystems.h" #include "clang/Rewrite/Core/RewriteBuffer.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringSet.h" @@ -652,7 +654,22 @@ class RenameRangeCollector : public IndexDataConsumer { Action startSourceEntity(const IndexSymbol &symbol) override { if (symbol.USR == USR) { if (auto loc = indexSymbolToRenameLoc(symbol, newName)) { - locations.push_back(std::move(*loc)); + // Inside capture lists like `{ [test] in }`, 'test' refers to both the + // newly declared, captured variable and the referenced variable it is + // initialized from. Make sure to only rename it once. + auto existingLoc = llvm::find_if(locations, [&](RenameLoc searchLoc) { + return searchLoc.Line == loc->Line && searchLoc.Column == loc->Column; + }); + if (existingLoc == locations.end()) { + locations.push_back(std::move(*loc)); + } else { + assert(existingLoc->OldName == loc->OldName && + existingLoc->NewName == loc->NewName && + existingLoc->IsFunctionLike == loc->IsFunctionLike && + existingLoc->IsNonProtocolType == + existingLoc->IsNonProtocolType && + "Asked to do a different rename for the same location?"); + } } } return IndexDataConsumer::Continue; @@ -1303,7 +1320,9 @@ bool RefactoringActionExtractFunction::performChange() { } OS << ")"; - if (RangeInfo.ThrowingUnhandledError) + if (RangeInfo.UnhandledEffects.contains(EffectKind::Async)) + OS << " async"; + if (RangeInfo.UnhandledEffects.contains(EffectKind::Throws)) OS << " " << tok::kw_throws; bool InsertedReturnType = false; @@ -1331,6 +1350,12 @@ bool RefactoringActionExtractFunction::performChange() { llvm::raw_svector_ostream OS(Buffer); if (RangeInfo.exit() == ExitState::Positive) OS << tok::kw_return <<" "; + + if (RangeInfo.UnhandledEffects.contains(EffectKind::Throws)) + OS << tok::kw_try << " "; + if (RangeInfo.UnhandledEffects.contains(EffectKind::Async)) + OS << "await "; + CallNameOffset = Buffer.size() - ReplaceBegin; OS << PreferredName << "("; for (auto &RD : Parameters) { @@ -1824,7 +1849,7 @@ bool RefactoringActionCollapseNestedIfStmt::performChange() { EditConsumer, SM, Lexer::getCharSourceRangeFromSourceRange(SM, OuterIf->getSourceRange())); - OS << tok::kw_if << " "; + OS << tok::kw_if << " "; // Emit conditions. bool first = true; @@ -2468,11 +2493,11 @@ bool RefactoringActionConvertToSwitchStmt::performChange() { SmallString<64> ConditionalPattern = SmallString<64>(); Expr *walkToExprPost(Expr *E) override { - if (E->getKind() != ExprKind::Binary) + auto *BE = dyn_cast(E); + if (!BE) return E; - auto BE = dyn_cast(E); if (isFunctionNameAllowed(BE)) - appendPattern(dyn_cast(E)->getArg()); + appendPattern(BE->getLHS(), BE->getRHS()); return E; } @@ -2498,10 +2523,10 @@ bool RefactoringActionConvertToSwitchStmt::performChange() { || FunctionName == "__derived_struct_equals"; } - void appendPattern(TupleExpr *Tuple) { - auto PatternArgument = Tuple->getElements().back(); + void appendPattern(Expr *LHS, Expr *RHS) { + auto *PatternArgument = RHS; if (PatternArgument->getKind() == ExprKind::DeclRef) - PatternArgument = Tuple->getElements().front(); + PatternArgument = LHS; if (ConditionalPattern.size() > 0) ConditionalPattern.append(", "); ConditionalPattern.append(Lexer::getCharSourceRangeFromSourceRange(SM, PatternArgument->getSourceRange()).str()); @@ -3266,7 +3291,7 @@ class AddEquatableContext { SourceLoc StartLoc; /// Array of all inherited protocols' locations - ArrayRef ProtocolsLocations; + ArrayRef ProtocolsLocations; /// Array of all conformed protocols SmallVector Protocols; @@ -3684,7 +3709,7 @@ static CallExpr *findTrailingClosureTarget( return nullptr; CallExpr *CE = cast(contexts.back().get()); - if (CE->hasTrailingClosure()) + if (CE->getUnlabeledTrailingClosureIndex().hasValue()) // Call expression already has a trailing closure. return nullptr; @@ -3763,43 +3788,18 @@ bool RefactoringActionTrailingClosure::performChange() { return false; } -static bool rangeStartMayNeedRename(const ResolvedRangeInfo &Info) { - switch(Info.Kind) { - case RangeKind::SingleExpression: { - Expr *E = Info.ContainedNodes[0].get(); - // We should show rename for the selection of "foo()" - if (auto *CE = dyn_cast(E)) { - if (CE->getFn()->getKind() == ExprKind::DeclRef) - return true; - - // When callling an instance method inside another instance method, - // we have a dot syntax call whose dot and base are both implicit. We - // need to explicitly allow the specific case here. - if (auto *DSC = dyn_cast(CE->getFn())) { - if (DSC->getBase()->isImplicit() && - DSC->getFn()->getStartLoc() == Info.TokensInRange.front().getLoc()) - return true; - } - } - return false; - } - case RangeKind::PartOfExpression: { - if (auto *CE = dyn_cast(Info.CommonExprParent)) { - if (auto *DSC = dyn_cast(CE->getFn())) { - if (DSC->getFn()->getStartLoc() == Info.TokensInRange.front().getLoc()) - return true; - } - } - return false; - } - case RangeKind::SingleDecl: - case RangeKind::MultiTypeMemberDecl: - case RangeKind::SingleStatement: - case RangeKind::MultiStatement: - case RangeKind::Invalid: - return false; +static bool collectRangeStartRefactorings(const ResolvedRangeInfo &Info) { + switch (Info.Kind) { + case RangeKind::SingleExpression: + case RangeKind::SingleStatement: + case RangeKind::SingleDecl: + case RangeKind::PartOfExpression: + return true; + case RangeKind::MultiStatement: + case RangeKind::MultiTypeMemberDecl: + case RangeKind::Invalid: + return false; } - llvm_unreachable("unhandled kind"); } bool RefactoringActionConvertToComputedProperty:: @@ -3946,19 +3946,25 @@ PtrArrayRef callArgs(const ApplyExpr *AE) { return PtrArrayRef(); } -FuncDecl *getUnderlyingFunc(const Expr *Fn) { +/// A more aggressive variant of \c Expr::getReferencedDecl that also looks +/// through autoclosures created to pass the \c self parameter to a member funcs +ValueDecl *getReferencedDecl(const Expr *Fn) { Fn = Fn->getSemanticsProvidingExpr(); if (auto *DRE = dyn_cast(Fn)) - return dyn_cast_or_null(DRE->getDecl()); + return DRE->getDecl(); if (auto ApplyE = dyn_cast(Fn)) - return getUnderlyingFunc(ApplyE->getFn()); + return getReferencedDecl(ApplyE->getFn()); if (auto *ACE = dyn_cast(Fn)) { if (auto *Unwrapped = ACE->getUnwrappedCurryThunkExpr()) - return getUnderlyingFunc(Unwrapped); + return getReferencedDecl(Unwrapped); } return nullptr; } +FuncDecl *getUnderlyingFunc(const Expr *Fn) { + return dyn_cast_or_null(getReferencedDecl(Fn)); +} + /// Find the outermost call of the given location CallExpr *findOuterCall(const ResolvedCursorInfo &CursorInfo) { auto IncludeInContext = [](ASTNode N) { @@ -4063,41 +4069,43 @@ class HandlerResult { /// single parameter of `Result` type. enum class HandlerType { INVALID, PARAMS, RESULT }; +/// A single return type of a refactored async function. If the async function +/// returns a tuple, each element of the tuple (represented by a \c +/// LabeledReturnType) might have a label, otherwise the \p Label is empty. +struct LabeledReturnType { + Identifier Label; + swift::Type Ty; + + LabeledReturnType(Identifier Label, swift::Type Ty) : Label(Label), Ty(Ty) {} +}; + /// Given a function with an async alternative (or one that *could* have an -/// async alternative), stores information about the handler parameter. +/// async alternative), stores information about the completion handler. +/// The completion handler can be either a variable (which includes a parameter) +/// or a function struct AsyncHandlerDesc { - const ParamDecl *Handler = nullptr; - int Index = -1; + PointerUnion Handler = nullptr; HandlerType Type = HandlerType::INVALID; bool HasError = false; - static AsyncHandlerDesc find(const FuncDecl *FD, bool ignoreName) { - if (!FD || FD->hasAsync() || FD->hasThrows()) - return AsyncHandlerDesc(); - - // Require at least one parameter and void return type - auto *Params = FD->getParameters(); - if (Params->size() == 0 || !FD->getResultInterfaceType()->isVoid()) - return AsyncHandlerDesc(); - + static AsyncHandlerDesc get(const ValueDecl *Handler, bool RequireName) { AsyncHandlerDesc HandlerDesc; - - // Assume the handler is the last parameter for now - HandlerDesc.Index = Params->size() - 1; - HandlerDesc.Handler = Params->get(HandlerDesc.Index); - - // Callback must not be attributed with @autoclosure - if (HandlerDesc.Handler->isAutoClosure()) + if (auto Var = dyn_cast(Handler)) { + HandlerDesc.Handler = Var; + } else if (auto Func = dyn_cast(Handler)) { + HandlerDesc.Handler = Func; + } else { + // The handler must be a variable or function return AsyncHandlerDesc(); + } - // Callback must have a completion-like name (if we're not ignoring it) - if (!ignoreName && - !isCompletionHandlerParamName(HandlerDesc.Handler->getNameStr())) + // Callback must have a completion-like name + if (RequireName && !isCompletionHandlerParamName(HandlerDesc.getNameStr())) return AsyncHandlerDesc(); // Callback must be a function type and return void. Doesn't need to have // any parameters - may just be a "I'm done" callback - auto *HandlerTy = HandlerDesc.Handler->getType()->getAs(); + auto *HandlerTy = HandlerDesc.getType()->getAs(); if (!HandlerTy || !HandlerTy->getResult()->isVoid()) return AsyncHandlerDesc(); @@ -4126,7 +4134,7 @@ struct AsyncHandlerDesc { if (!HandlerParams.empty()) { auto LastParamTy = HandlerParams.back().getParameterType(); HandlerDesc.HasError = isErrorType(LastParamTy->getOptionalObjectType(), - FD->getModuleContext()); + Handler->getModuleContext()); } } @@ -4135,8 +4143,57 @@ struct AsyncHandlerDesc { bool isValid() const { return Type != HandlerType::INVALID; } + /// Return the declaration of the completion handler as a \c ValueDecl. + /// In practice, the handler will always be a \c VarDecl or \c + /// AbstractFunctionDecl. + /// \c getNameStr and \c getType provide access functions that are available + /// for both variables and functions, but not on \c ValueDecls. + const ValueDecl *getHandler() const { + if (!Handler) { + return nullptr; + } + if (auto Var = Handler.dyn_cast()) { + return Var; + } else if (auto Func = Handler.dyn_cast()) { + return Func; + } else { + llvm_unreachable("Unknown handler type"); + } + } + + /// Return the name of the completion handler. If it is a variable, the + /// variable name, if it's a function, the function base name. + StringRef getNameStr() const { + if (auto Var = Handler.dyn_cast()) { + return Var->getNameStr(); + } else if (auto Func = Handler.dyn_cast()) { + return Func->getNameStr(); + } else { + llvm_unreachable("Unknown handler type"); + } + } + + HandlerType getHandlerType() const { return Type; } + + /// Get the type of the completion handler. + swift::Type getType() const { + if (auto Var = Handler.dyn_cast()) { + return Var->getType(); + } else if (auto Func = Handler.dyn_cast()) { + auto Type = Func->getInterfaceType(); + // Undo the self curry thunk if we are referencing a member function. + if (Func->hasImplicitSelfDecl()) { + assert(Type->is()); + Type = Type->getAs()->getResult(); + } + return Type; + } else { + llvm_unreachable("Unknown handler type"); + } + } + ArrayRef params() const { - auto Ty = Handler->getType()->getAs(); + auto Ty = getType()->getAs(); assert(Ty && "Type must be a function type"); return Ty->getParams(); } @@ -4149,6 +4206,13 @@ struct AsyncHandlerDesc { return params(); } + /// If the completion handler has an Error parameter, return it. + Optional getErrorParam() const { + if (HasError && Type == HandlerType::PARAMS) + return params().back(); + return None; + } + /// Get the type of the error that will be thrown by the \c async method or \c /// None if the completion handler doesn't accept an error parameter. /// This may be more specialized than the generic 'Error' type if the @@ -4184,25 +4248,61 @@ struct AsyncHandlerDesc { if (!isValid()) return nullptr; - if (Node.isExpr(swift::ExprKind::Call)) { - CallExpr *CE = cast(Node.dyn_cast()); - if (CE->getFn()->getReferencedDecl().getDecl() == Handler) - return CE; + if (auto E = Node.dyn_cast()) { + if (auto *CE = dyn_cast(E->getSemanticsProvidingExpr())) { + if (CE->getFn()->getReferencedDecl().getDecl() == getHandler()) { + return CE; + } + } } return nullptr; } + /// Returns \c true if the call to the completion handler contains possibly + /// non-nil values for both the success and error parameters, e.g. + /// \code + /// completion(result, error) + /// \endcode + /// This can only happen if the completion handler is a params handler. + bool isAmbiguousCallToParamHandler(const CallExpr *CE) const { + if (!HasError || Type != HandlerType::PARAMS) { + // Only param handlers with an error can pass both an error AND a result. + return false; + } + auto Args = callArgs(CE).ref(); + if (!isa(Args.back())) { + // We've got an error parameter. If any of the success params is not nil, + // the call is ambiguous. + for (auto &Arg : Args.drop_back()) { + if (!isa(Arg)) { + return true; + } + } + } + return false; + } + /// Given a call to the `Handler`, extract the expressions to be returned or /// thrown, taking care to remove the `.success`/`.failure` if it's a /// `RESULT` handler type. - HandlerResult extractResultArgs(const CallExpr *CE) const { + /// If the call is ambiguous (contains potentially non-nil arguments to both + /// the result and the error parameters), the \p ReturnErrorArgsIfAmbiguous + /// determines whether the success or error parameters are passed. + HandlerResult extractResultArgs(const CallExpr *CE, + bool ReturnErrorArgsIfAmbiguous) const { auto ArgList = callArgs(CE); auto Args = ArgList.ref(); if (Type == HandlerType::PARAMS) { - // If there's an error parameter and the user isn't passing nil to it, - // assume this is the error path. - if (HasError && !isa(Args.back())) + bool IsErrorResult; + if (isAmbiguousCallToParamHandler(CE)) { + IsErrorResult = ReturnErrorArgsIfAmbiguous; + } else { + // If there's an error parameter and the user isn't passing nil to it, + // assume this is the error path. + IsErrorResult = (HasError && !isa(Args.back())); + } + if (IsErrorResult) return HandlerResult(Args.back(), true); // We can drop the args altogether if they're just Void. @@ -4265,12 +4365,26 @@ struct AsyncHandlerDesc { } } + /// If the async function returns a tuple, the label of the \p Index -th + /// element in the returned tuple. If the function doesn't return a tuple or + /// the element is unlabeled, an empty identifier is returned. + Identifier getAsyncReturnTypeLabel(size_t Index) const { + assert(Index < getSuccessParams().size()); + if (getSuccessParams().size() <= 1) { + // There can't be any labels if the async function doesn't return a tuple. + return Identifier(); + } else { + return getSuccessParams()[Index].getInternalLabel(); + } + } + /// Gets the return value types for the async equivalent of this handler. - ArrayRef - getAsyncReturnTypes(SmallVectorImpl &Scratch) const { - for (auto &Param : getSuccessParams()) { - auto Ty = Param.getParameterType(); - Scratch.push_back(getSuccessParamAsyncReturnType(Ty)); + ArrayRef + getAsyncReturnTypes(SmallVectorImpl &Scratch) const { + for (size_t I = 0; I < getSuccessParams().size(); ++I) { + auto Ty = getSuccessParams()[I].getParameterType(); + Scratch.emplace_back(getAsyncReturnTypeLabel(I), + getSuccessParamAsyncReturnType(Ty)); } return Scratch; } @@ -4285,47 +4399,223 @@ struct AsyncHandlerDesc { }); } + // TODO: If we have an async alternative we should check its result types + // for whether to unwrap or not bool shouldUnwrap(swift::Type Ty) const { return HasError && Ty->isOptional(); } }; -enum class ConditionType { INVALID, NIL, NOT_NIL }; +/// Given a completion handler that is part of a function signature, stores +/// information about that completion handler and its index within the function +/// declaration. +struct AsyncHandlerParamDesc : public AsyncHandlerDesc { + /// Enum to represent the position of the completion handler param within + /// the parameter list. Given `(A, B, C, D)`: + /// - A is `First` + /// - B and C are `Middle` + /// - D is `Last` + /// The position is `Only` if there's a single parameter that is the + /// completion handler and `None` if there is no handler. + enum class Position { + First, Middle, Last, Only, None + }; + + /// The function the completion handler is a parameter of. + const FuncDecl *Func = nullptr; + /// The index of the completion handler in the function that declares it. + unsigned Index = 0; + /// The async alternative, if one is found. + const AbstractFunctionDecl *Alternative = nullptr; + + AsyncHandlerParamDesc() : AsyncHandlerDesc() {} + AsyncHandlerParamDesc(const AsyncHandlerDesc &Handler, const FuncDecl *Func, + unsigned Index, + const AbstractFunctionDecl *Alternative) + : AsyncHandlerDesc(Handler), Func(Func), Index(Index), + Alternative(Alternative) {} + + static AsyncHandlerParamDesc find(const FuncDecl *FD, + bool RequireAttributeOrName) { + if (!FD || FD->hasAsync() || FD->hasThrows() || + !FD->getResultInterfaceType()->isVoid()) + return AsyncHandlerParamDesc(); + + const auto *Alternative = FD->getAsyncAlternative(); + Optional Index = + FD->findPotentialCompletionHandlerParam(Alternative); + if (!Index) + return AsyncHandlerParamDesc(); + + bool RequireName = RequireAttributeOrName && !Alternative; + return AsyncHandlerParamDesc( + AsyncHandlerDesc::get(FD->getParameters()->get(*Index), RequireName), + FD, *Index, Alternative); + } + + /// Build an @available attribute with the name of the async alternative as + /// the \c renamed argument, followed by a newline. + SmallString<128> buildRenamedAttribute() const { + SmallString<128> AvailabilityAttr; + llvm::raw_svector_ostream OS(AvailabilityAttr); + + // If there's an alternative then there must already be an attribute, + // don't add another. + if (!isValid() || Alternative) + return AvailabilityAttr; + + DeclName Name = Func->getName(); + OS << "@available(*, renamed: \"" << Name.getBaseName() << "("; + ArrayRef ArgNames = Name.getArgumentNames(); + for (size_t I = 0; I < ArgNames.size(); ++I) { + if (I != Index) { + OS << ArgNames[I] << tok::colon; + } + } + OS << ")\")\n"; + + return AvailabilityAttr; + } + + /// Retrieves the parameter decl for the completion handler parameter, or + /// \c nullptr if no valid completion parameter is present. + const ParamDecl *getHandlerParam() const { + if (!isValid()) + return nullptr; + return cast(getHandler()); + } + + /// See \c Position + Position handlerParamPosition() const { + if (!isValid()) + return Position::None; + const auto *Params = Func->getParameters(); + if (Params->size() == 1) + return Position::Only; + if (Index == 0) + return Position::First; + if (Index == Params->size() - 1) + return Position::Last; + return Position::Middle; + } + + bool operator==(const AsyncHandlerParamDesc &Other) const { + return Handler == Other.Handler && Type == Other.Type && + HasError == Other.HasError && Index == Other.Index; + } + + bool alternativeIsAccessor() const { + return isa_and_nonnull(Alternative); + } +}; + +/// The type of a condition in a conditional statement. +enum class ConditionType { + NIL, // == nil + NOT_NIL, // != nil + IS_TRUE, // if b + IS_FALSE, // if !b + SUCCESS_PATTERN, // case .success + FAILURE_PATTEN // case .failure +}; + +/// Indicates whether a condition describes a success or failure path. For +/// example, a check for whether an error parameter is present is a failure +/// path. A check for a nil error parameter is a success path. This is distinct +/// from ConditionType, as it relies on contextual information about what values +/// need to be checked for success or failure. +enum class ConditionPath { SUCCESS, FAILURE }; + +static ConditionPath flippedConditionPath(ConditionPath Path) { + switch (Path) { + case ConditionPath::SUCCESS: + return ConditionPath::FAILURE; + case ConditionPath::FAILURE: + return ConditionPath::SUCCESS; + } + llvm_unreachable("Unhandled case in switch!"); +} /// Finds the `Subject` being compared to in various conditions. Also finds any /// pattern that may have a bound name. struct CallbackCondition { - ConditionType Type = ConditionType::INVALID; + Optional Type; const Decl *Subject = nullptr; const Pattern *BindPattern = nullptr; - // Bit of a hack. When the `Subject` is a `Result` type we use this to - // distinguish between the `.success` and `.failure` case (as opposed to just - // checking whether `Subject` == `TheErrDecl`) - bool ErrorCase = false; - - CallbackCondition() = default; /// Initializes a `CallbackCondition` with a `!=` or `==` comparison of - /// an `Optional` typed `Subject` to `nil`, ie. + /// an `Optional` typed `Subject` to `nil`, or a `Bool` typed `Subject` to a + /// boolean literal, ie. /// - ` != nil` /// - ` == nil` + /// - ` != true` + /// - ` == false` CallbackCondition(const BinaryExpr *BE, const FuncDecl *Operator) { bool FoundNil = false; - for (auto *Operand : BE->getArg()->getElements()) { + BooleanLiteralExpr *FoundBool = nullptr; + bool DidUnwrapOptional = false; + + for (auto *Operand : {BE->getLHS(), BE->getRHS()}) { + Operand = Operand->getSemanticsProvidingExpr(); + if (auto *IIOE = dyn_cast(Operand)) { + Operand = IIOE->getSubExpr()->getSemanticsProvidingExpr(); + DidUnwrapOptional = true; + } if (isa(Operand)) { FoundNil = true; + } else if (auto *BLE = dyn_cast(Operand)) { + FoundBool = BLE; } else if (auto *DRE = dyn_cast(Operand)) { Subject = DRE->getDecl(); } } - if (Subject && FoundNil) { + if (!Subject) + return; + + if (FoundNil) { if (Operator->getBaseName() == "==") { Type = ConditionType::NIL; } else if (Operator->getBaseName() == "!=") { Type = ConditionType::NOT_NIL; } + } else if (FoundBool) { + if (Operator->getBaseName() == "==") { + Type = FoundBool->getValue() ? ConditionType::IS_TRUE + : ConditionType::IS_FALSE; + } else if (Operator->getBaseName() == "!=" && !DidUnwrapOptional) { + // Note that we don't consider this case if we unwrapped an optional, + // as e.g optBool != false is a check for true *or* nil. + Type = FoundBool->getValue() ? ConditionType::IS_FALSE + : ConditionType::IS_TRUE; + } + } + } + + /// A bool condition expression. + explicit CallbackCondition(const Expr *E) { + // FIXME: Sema should produce ErrorType. + if (!E->getType() || !E->getType()->isBool()) + return; + + auto CondType = ConditionType::IS_TRUE; + E = E->getSemanticsProvidingExpr(); + + // If we have a prefix negation operator, this is a check for false. + if (auto *PrefixOp = dyn_cast(E)) { + auto *Callee = PrefixOp->getCalledValue(); + if (Callee && Callee->isOperator() && Callee->getBaseName() == "!") { + CondType = ConditionType::IS_FALSE; + E = PrefixOp->getArg()->getSemanticsProvidingExpr(); + } } + + auto *DRE = dyn_cast(E); + if (!DRE) + return; + + Subject = DRE->getDecl(); + Type = CondType; } /// Initializes a `CallbackCondition` with binding of an `Optional` or @@ -4335,6 +4625,9 @@ struct CallbackCondition { /// - `case .failure(let bind) = ` /// - `let bind = try? .get()` CallbackCondition(const Pattern *P, const Expr *Init) { + Init = Init->getSemanticsProvidingExpr(); + P = P->getSemanticsProvidingPattern(); + if (auto *DRE = dyn_cast(Init)) { if (auto *OSP = dyn_cast(P)) { // `let bind = ` @@ -4361,73 +4654,26 @@ struct CallbackCondition { /// } /// ``` CallbackCondition(const Decl *Subject, const CaseLabelItem *CaseItem) { - if (auto *EEP = dyn_cast(CaseItem->getPattern())) { + if (auto *EEP = dyn_cast( + CaseItem->getPattern()->getSemanticsProvidingPattern())) { // `case .(let )` initFromEnumPattern(Subject, EEP); } } - bool isValid() const { return Type != ConditionType::INVALID; } - - /// Given an `if` condition `Cond` and a set of `Decls`, find any - /// `CallbackCondition`s in `Cond` that use one of those `Decls` and add them - /// to the map `AddTo`. Return `true` if all elements in the condition are - /// "handled", ie. every condition can be mapped to a single `Decl` in - /// `Decls`. - static bool all(StmtCondition Cond, llvm::DenseSet Decls, - llvm::DenseMap &AddTo) { - bool Handled = true; - for (auto &CondElement : Cond) { - if (auto *BoolExpr = CondElement.getBooleanOrNull()) { - SmallVector Exprs; - Exprs.push_back(BoolExpr); - - while (!Exprs.empty()) { - auto *Next = Exprs.pop_back_val(); - if (auto *ACE = dyn_cast(Next)) - Next = ACE->getSingleExpressionBody(); - - if (auto *BE = dyn_cast_or_null(Next)) { - auto *Operator = isOperator(BE); - if (Operator) { - if (Operator->getBaseName() == "&&") { - auto Args = BE->getArg()->getElements(); - Exprs.insert(Exprs.end(), Args.begin(), Args.end()); - } else { - addCond(CallbackCondition(BE, Operator), Decls, AddTo, Handled); - } - continue; - } - } - - Handled = false; - } - } else if (auto *P = CondElement.getPatternOrNull()) { - addCond(CallbackCondition(P, CondElement.getInitializer()), Decls, - AddTo, Handled); - } - } - return Handled && !AddTo.empty(); - } + bool isValid() const { return Type.hasValue(); } private: - static void addCond(const CallbackCondition &CC, - llvm::DenseSet Decls, - llvm::DenseMap &AddTo, - bool &Handled) { - if (!CC.isValid() || !Decls.count(CC.Subject) || - !AddTo.try_emplace(CC.Subject, CC).second) - Handled = false; - } - void initFromEnumPattern(const Decl *D, const EnumElementPattern *EEP) { if (auto *EED = EEP->getElementDecl()) { auto eedTy = EED->getParentEnum()->getDeclaredType(); if (!eedTy || !eedTy->isResult()) return; - if (EED->getNameStr() == StringRef("failure")) - ErrorCase = true; - Type = ConditionType::NOT_NIL; + if (EED->getNameStr() == StringRef("failure")) { + Type = ConditionType::FAILURE_PATTEN; + } else { + Type = ConditionType::SUCCESS_PATTERN; + } Subject = D; BindPattern = EEP->getSubPattern(); } @@ -4461,75 +4707,279 @@ struct CallbackCondition { } }; +/// A CallbackCondition with additional semantic information about whether it +/// is for a success path or failure path. +struct ClassifiedCondition : public CallbackCondition { + ConditionPath Path; + + /// Whether this represents an Obj-C style boolean flag check for success. + bool IsObjCStyleFlagCheck; + + explicit ClassifiedCondition(CallbackCondition Cond, ConditionPath Path, + bool IsObjCStyleFlagCheck) + : CallbackCondition(Cond), Path(Path), + IsObjCStyleFlagCheck(IsObjCStyleFlagCheck) {} +}; + +/// A wrapper for a map of parameter decls to their classified conditions, or +/// \c None if they are not present in any conditions. +struct ClassifiedCallbackConditions final + : llvm::MapVector { + Optional lookup(const Decl *D) const { + auto Res = find(D); + if (Res == end()) + return None; + return Res->second; + } +}; + +/// A list of nodes to print, along with a list of locations that may have +/// preceding comments attached, which also need printing. For example: +/// +/// \code +/// if .random() { +/// // a +/// print("hello") +/// // b +/// } +/// \endcode +/// +/// To print out the contents of the if statement body, we'll include the AST +/// node for the \c print call. This will also include the preceding comment +/// \c a, but won't include the comment \c b. To ensure the comment \c b gets +/// printed, the SourceLoc for the closing brace \c } is added as a possible +/// comment loc. +class NodesToPrint { + SmallVector Nodes; + SmallVector PossibleCommentLocs; + +public: + NodesToPrint() {} + NodesToPrint(ArrayRef Nodes, ArrayRef PossibleCommentLocs) + : Nodes(Nodes.begin(), Nodes.end()), + PossibleCommentLocs(PossibleCommentLocs.begin(), + PossibleCommentLocs.end()) {} + + ArrayRef getNodes() const { return Nodes; } + ArrayRef getPossibleCommentLocs() const { + return PossibleCommentLocs; + } + + /// Add an AST node to print. + void addNode(ASTNode Node) { + // Note we skip vars as they'll be printed as a part of their + // PatternBindingDecl. + if (!Node.isDecl(DeclKind::Var)) + Nodes.push_back(Node); + } + + /// Add a SourceLoc which may have a preceding comment attached. If so, the + /// comment will be printed out at the appropriate location. + void addPossibleCommentLoc(SourceLoc Loc) { + if (Loc.isValid()) + PossibleCommentLocs.push_back(Loc); + } + + /// Add all the nodes in the brace statement to the list of nodes to print. + /// This should be preferred over adding the nodes manually as it picks up the + /// end location of the brace statement as a possible comment loc, ensuring + /// that we print any trailing comments in the brace statement. + void addNodesInBraceStmt(BraceStmt *Brace) { + for (auto Node : Brace->getElements()) + addNode(Node); + + // Ignore the end locations of implicit braces, as they're likely bogus. + // e.g for a case statement, the r-brace loc points to the last token of the + // last node in the body. + if (!Brace->isImplicit()) + addPossibleCommentLoc(Brace->getRBraceLoc()); + } + + /// Add the nodes and comment locs from another NodesToPrint. + void addNodes(NodesToPrint OtherNodes) { + Nodes.append(OtherNodes.Nodes.begin(), OtherNodes.Nodes.end()); + PossibleCommentLocs.append(OtherNodes.PossibleCommentLocs.begin(), + OtherNodes.PossibleCommentLocs.end()); + } + + /// Whether the last recorded node is an explicit return or break statement. + bool hasTrailingReturnOrBreak() const { + if (Nodes.empty()) + return false; + return (Nodes.back().isStmt(StmtKind::Return) || + Nodes.back().isStmt(StmtKind::Break)) && + !Nodes.back().isImplicit(); + } + + /// If the last recorded node is an explicit return or break statement that + /// can be safely dropped, drop it from the list. + void dropTrailingReturnOrBreakIfPossible() { + if (!hasTrailingReturnOrBreak()) + return; + + auto *Node = Nodes.back().get(); + + // If this is a return statement with return expression, let's preserve it. + if (auto *RS = dyn_cast(Node)) { + if (RS->hasResult()) + return; + } + + // Remove the node from the list, but make sure to add it as a possible + // comment loc to preserve any of its attached comments. + Nodes.pop_back(); + addPossibleCommentLoc(Node->getStartLoc()); + } + + /// Returns a list of nodes to print in a brace statement. This picks up the + /// end location of the brace statement as a possible comment loc, ensuring + /// that we print any trailing comments in the brace statement. + static NodesToPrint inBraceStmt(BraceStmt *stmt) { + NodesToPrint Nodes; + Nodes.addNodesInBraceStmt(stmt); + return Nodes; + } +}; + /// The statements within the closure of call to a function taking a callback /// are split into a `SuccessBlock` and `ErrorBlock` (`ClassifiedBlocks`). /// This class stores the nodes for each block, as well as a mapping of /// decls to any patterns they are used in. class ClassifiedBlock { - SmallVector Nodes; - // closure param -> name - llvm::DenseMap BoundNames; - bool AllLet = true; + NodesToPrint Nodes; + + // A mapping of closure params to a list of patterns that bind them. + using ParamPatternBindingsMap = + llvm::MapVector>; + ParamPatternBindingsMap ParamPatternBindings; public: - ArrayRef nodes() const { return llvm::makeArrayRef(Nodes); } + const NodesToPrint &nodesToPrint() const { return Nodes; } + + /// Attempt to retrieve an existing bound name for a closure parameter, or + /// an empty string if there's no suitable existing binding. + StringRef boundName(const Decl *D) const { + // Adopt the same name as the representative single pattern, if it only + // binds a single var. + if (auto *P = getSinglePatternFor(D)) { + if (P->getSingleVar()) + return P->getBoundName().str(); + } + return StringRef(); + } + + /// Checks whether a closure parameter can be represented by a single pattern + /// that binds it. If the param is only bound by a single pattern, that will + /// be returned. If there's a pattern with a single var that binds it, that + /// will be returned, preferring a 'let' pattern to prefer out of line + /// printing of 'var' patterns. + const Pattern *getSinglePatternFor(const Decl *D) const { + auto Iter = ParamPatternBindings.find(D); + if (Iter == ParamPatternBindings.end()) + return nullptr; - StringRef boundName(const Decl *D) const { return BoundNames.lookup(D); } + const auto &Patterns = Iter->second; + if (Patterns.empty()) + return nullptr; + if (Patterns.size() == 1) + return Patterns[0]; + + // If we have multiple patterns, search for the best single var pattern to + // use, preferring a 'let' binding. + const Pattern *FirstSingleVar = nullptr; + for (auto *P : Patterns) { + if (!P->getSingleVar()) + continue; - bool allLet() const { return AllLet; } + if (!P->hasAnyMutableBindings()) + return P; - void addAllNodes(ArrayRef Nodes) { - for (auto Node : Nodes) { - addNode(Node); + if (!FirstSingleVar) + FirstSingleVar = P; } + return FirstSingleVar; } - void addNode(const ASTNode Node) { - if (!Node.isDecl(DeclKind::Var)) - Nodes.push_back(Node); - } + /// Retrieve any bound vars that are effectively aliases of a given closure + /// parameter. + llvm::SmallDenseSet getAliasesFor(const Decl *D) const { + auto Iter = ParamPatternBindings.find(D); + if (Iter == ParamPatternBindings.end()) + return {}; - void addBinding(const CallbackCondition &FromCondition, - DiagnosticEngine &DiagEngine) { - if (!FromCondition.BindPattern) - return; + llvm::SmallDenseSet Aliases; + + // The single pattern that we replace the decl with is always an alias. + if (auto *P = getSinglePatternFor(D)) { + if (auto *SingleVar = P->getSingleVar()) + Aliases.insert(SingleVar); + } - if (auto *BP = - dyn_cast_or_null(FromCondition.BindPattern)) { - if (!BP->isLet()) - AllLet = false; + // Any other let bindings we have are also aliases. + for (auto *P : Iter->second) { + if (auto *SingleVar = P->getSingleVar()) { + if (!P->hasAnyMutableBindings()) + Aliases.insert(SingleVar); + } } + return Aliases; + } + + const ParamPatternBindingsMap ¶mPatternBindings() const { + return ParamPatternBindings; + } + + void addNodesInBraceStmt(BraceStmt *Brace) { + Nodes.addNodesInBraceStmt(Brace); + } + void addPossibleCommentLoc(SourceLoc Loc) { + Nodes.addPossibleCommentLoc(Loc); + } + void addAllNodes(NodesToPrint OtherNodes) { + Nodes.addNodes(std::move(OtherNodes)); + } + + void addNode(ASTNode Node) { + Nodes.addNode(Node); + } - StringRef Name = FromCondition.BindPattern->getBoundName().str(); - if (Name.empty()) + void addBinding(const ClassifiedCondition &FromCondition) { + auto *P = FromCondition.BindPattern; + if (!P) return; - auto Res = BoundNames.try_emplace(FromCondition.Subject, Name); - if (Res.second) + // Patterns that don't bind anything aren't interesting. + SmallVector Vars; + P->collectVariables(Vars); + if (Vars.empty()) return; - // Already inserted, only handle cases where the name is the same - // TODO: This wouldn't be that hard to handle, just need to keep track - // of the decl and replace its name with the same as the original - StringRef OldName = Res.first->second; - if (OldName != Name) { - DiagEngine.diagnose(FromCondition.BindPattern->getLoc(), - diag::callback_multiple_bound_names, - StringRef(OldName), Name); - } + ParamPatternBindings[FromCondition.Subject].push_back(P); } - void addAllBindings( - const llvm::DenseMap &FromConditions, - DiagnosticEngine &DiagEngine) { - for (auto &Entry : FromConditions) { - addBinding(Entry.second, DiagEngine); - if (DiagEngine.hadAnyError()) - return; - } + void addAllBindings(const ClassifiedCallbackConditions &FromConditions) { + for (auto &Entry : FromConditions) + addBinding(Entry.second); } }; +/// Whether or not the given statement starts a new scope. Note that most +/// statements are handled by the \c BraceStmt check. The others listed are +/// a somewhat special case since they can also declare variables in their +/// condition. +static bool startsNewScope(Stmt *S) { + switch (S->getKind()) { + case StmtKind::Brace: + case StmtKind::If: + case StmtKind::While: + case StmtKind::ForEach: + case StmtKind::Case: + return true; + default: + return false; + } +} + struct ClassifiedBlocks { ClassifiedBlock SuccessBlock; ClassifiedBlock ErrorBlock; @@ -4550,21 +5000,24 @@ struct CallbackClassifier { /// Updates the success and error block of `Blocks` with nodes and bound /// names from `Body`. Errors are added through `DiagEngine`, possibly /// resulting in partially filled out blocks. - static void classifyInto(ClassifiedBlocks &Blocks, + static void classifyInto(ClassifiedBlocks &Blocks, const FuncDecl *Callee, + ArrayRef SuccessParams, llvm::DenseSet &HandledSwitches, DiagnosticEngine &DiagEngine, llvm::DenseSet UnwrapParams, const ParamDecl *ErrParam, HandlerType ResultType, - ArrayRef Body) { - assert(!Body.empty() && "Cannot classify empty body"); - CallbackClassifier Classifier(Blocks, HandledSwitches, DiagEngine, - UnwrapParams, ErrParam, - ResultType == HandlerType::RESULT); - Classifier.classifyNodes(Body); + BraceStmt *Body) { + assert(!Body->getElements().empty() && "Cannot classify empty body"); + CallbackClassifier Classifier(Blocks, Callee, SuccessParams, + HandledSwitches, DiagEngine, UnwrapParams, + ErrParam, ResultType == HandlerType::RESULT); + Classifier.classifyNodes(Body->getElements(), Body->getRBraceLoc()); } private: ClassifiedBlocks &Blocks; + const FuncDecl *Callee; + ArrayRef SuccessParams; llvm::DenseSet &HandledSwitches; DiagnosticEngine &DiagEngine; ClassifiedBlock *CurrentBlock; @@ -4572,31 +5025,38 @@ struct CallbackClassifier { const ParamDecl *ErrParam; bool IsResultParam; - CallbackClassifier(ClassifiedBlocks &Blocks, + /// This is set to \c true if we're currently classifying on a known condition + /// path, where \c CurrentBlock is set to the appropriate block. This lets us + /// be more lenient with unhandled conditions as we already know the block + /// we're supposed to be in. + bool IsKnownConditionPath = false; + + CallbackClassifier(ClassifiedBlocks &Blocks, const FuncDecl *Callee, + ArrayRef SuccessParams, llvm::DenseSet &HandledSwitches, DiagnosticEngine &DiagEngine, llvm::DenseSet UnwrapParams, const ParamDecl *ErrParam, bool IsResultParam) - : Blocks(Blocks), HandledSwitches(HandledSwitches), - DiagEngine(DiagEngine), CurrentBlock(&Blocks.SuccessBlock), - UnwrapParams(UnwrapParams), ErrParam(ErrParam), - IsResultParam(IsResultParam) {} + : Blocks(Blocks), Callee(Callee), SuccessParams(SuccessParams), + HandledSwitches(HandledSwitches), DiagEngine(DiagEngine), + CurrentBlock(&Blocks.SuccessBlock), UnwrapParams(UnwrapParams), + ErrParam(ErrParam), IsResultParam(IsResultParam) {} - void classifyNodes(ArrayRef Nodes) { + void classifyNodes(ArrayRef Nodes, SourceLoc endCommentLoc) { for (auto I = Nodes.begin(), E = Nodes.end(); I < E; ++I) { auto *Statement = I->dyn_cast(); if (auto *IS = dyn_cast_or_null(Statement)) { - ArrayRef TempNodes; + NodesToPrint TempNodes; if (auto *BS = dyn_cast(IS->getThenStmt())) { - TempNodes = BS->getElements(); + TempNodes = NodesToPrint::inBraceStmt(BS); } else { - TempNodes = ArrayRef(IS->getThenStmt()); + TempNodes = NodesToPrint({IS->getThenStmt()}, /*commentLocs*/ {}); } - classifyConditional(IS, IS->getCond(), TempNodes, IS->getElseStmt()); + classifyConditional(IS, IS->getCond(), std::move(TempNodes), + IS->getElseStmt()); } else if (auto *GS = dyn_cast_or_null(Statement)) { - classifyConditional(GS, GS->getCond(), ArrayRef(), - GS->getBody()); + classifyConditional(GS, GS->getCond(), NodesToPrint(), GS->getBody()); } else if (auto *SS = dyn_cast_or_null(Statement)) { classifySwitch(SS); } else { @@ -4606,108 +5066,396 @@ struct CallbackClassifier { if (DiagEngine.hadAnyError()) return; } + // Make sure to pick up any trailing comments. + CurrentBlock->addPossibleCommentLoc(endCommentLoc); } - void classifyConditional(Stmt *Statement, StmtCondition Condition, - ArrayRef ThenNodes, Stmt *ElseStmt) { - llvm::DenseMap CallbackConditions; - bool UnhandledConditions = - !CallbackCondition::all(Condition, UnwrapParams, CallbackConditions); - CallbackCondition ErrCondition = CallbackConditions.lookup(ErrParam); + /// Whether any of the provided ASTNodes have a child expression that force + /// unwraps the error parameter. Note that this doesn't walk into new scopes. + bool hasForceUnwrappedErrorParam(ArrayRef Nodes) { + if (IsResultParam || !ErrParam) + return false; - if (UnhandledConditions) { - // Some unknown conditions. If there's an else, assume we can't handle - // and use the fallback case. Otherwise add to either the success or - // error block depending on some heuristics, known conditions will have - // placeholders added (ideally we'd remove them) - // TODO: Remove known conditions and split the `if` statement + class ErrUnwrapFinder : public ASTWalker { + const ParamDecl *ErrParam; + bool FoundUnwrap = false; - if (CallbackConditions.empty()) { - // Technically this has a similar problem, ie. the else could have - // conditions that should be in either success/error - CurrentBlock->addNode(Statement); - } else if (ElseStmt) { - DiagEngine.diagnose(Statement->getStartLoc(), - diag::unknown_callback_conditions); - } else if (ErrCondition.isValid() && - ErrCondition.Type == ConditionType::NOT_NIL) { - Blocks.ErrorBlock.addNode(Statement); - } else { - for (auto &Entry : CallbackConditions) { - if (Entry.second.Type == ConditionType::NIL) { - Blocks.ErrorBlock.addNode(Statement); - return; - } + public: + explicit ErrUnwrapFinder(const ParamDecl *ErrParam) + : ErrParam(ErrParam) {} + bool foundUnwrap() const { return FoundUnwrap; } + + std::pair walkToExprPre(Expr *E) override { + // Don't walk into ternary conditionals as they may have additional + // conditions such as err != nil that make a force unwrap now valid. + if (isa(E)) + return {false, E}; + + auto *FVE = dyn_cast(E); + if (!FVE) + return {true, E}; + + auto *DRE = dyn_cast(FVE->getSubExpr()); + if (!DRE) + return {true, E}; + + if (DRE->getDecl() != ErrParam) + return {true, E}; + + // If we find the node we're looking for, make a note of it, and abort + // the walk. + FoundUnwrap = true; + return {false, nullptr}; + } + + std::pair walkToStmtPre(Stmt *S) override { + // Don't walk into new explicit scopes, we only want to consider force + // unwraps in the immediate conditional body. + if (!S->isImplicit() && startsNewScope(S)) + return {false, S}; + return {true, S}; + } + + bool walkToDeclPre(Decl *D) override { + // Don't walk into new explicit DeclContexts. + return D->isImplicit() || !isa(D); + } + }; + for (auto Node : Nodes) { + ErrUnwrapFinder walker(ErrParam); + Node.walk(walker); + if (walker.foundUnwrap()) + return true; + } + return false; + } + + /// Given a callback condition, classify it as a success or failure path. + Optional + classifyCallbackCondition(const CallbackCondition &Cond, + const NodesToPrint &SuccessNodes, Stmt *ElseStmt) { + if (!Cond.isValid()) + return None; + + // If the condition involves a refutable pattern, we can't currently handle + // it. + if (Cond.BindPattern && Cond.BindPattern->isRefutablePattern()) + return None; + + // For certain types of condition, they need to appear in certain lists. + auto CondType = *Cond.Type; + switch (CondType) { + case ConditionType::NOT_NIL: + case ConditionType::NIL: + if (!UnwrapParams.count(Cond.Subject)) + return None; + break; + case ConditionType::IS_TRUE: + case ConditionType::IS_FALSE: + if (!llvm::is_contained(SuccessParams, Cond.Subject)) + return None; + break; + case ConditionType::SUCCESS_PATTERN: + case ConditionType::FAILURE_PATTEN: + if (!IsResultParam || Cond.Subject != ErrParam) + return None; + break; + } + + // Let's start with a success path, and flip any negative conditions. + auto Path = ConditionPath::SUCCESS; + + // If it's an error param, that's a flip. + if (Cond.Subject == ErrParam && !IsResultParam) + Path = flippedConditionPath(Path); + + // If we have a nil, false, or failure condition, that's a flip. + switch (CondType) { + case ConditionType::NIL: + case ConditionType::IS_FALSE: + case ConditionType::FAILURE_PATTEN: + Path = flippedConditionPath(Path); + break; + case ConditionType::IS_TRUE: + case ConditionType::NOT_NIL: + case ConditionType::SUCCESS_PATTERN: + break; + } + + // If we have a bool condition, it could be an Obj-C style flag check, which + // we do some extra checking for. Otherwise, we're done. + if (CondType != ConditionType::IS_TRUE && + CondType != ConditionType::IS_FALSE) { + return ClassifiedCondition(Cond, Path, /*ObjCFlagCheck*/ false); + } + + // Check to see if the async alternative function has a convention that + // specifies where the flag is and what it indicates. + Optional> CustomFlag; + if (auto *AsyncAlt = Callee->getAsyncAlternative()) { + if (auto Conv = AsyncAlt->getForeignAsyncConvention()) { + if (auto Idx = Conv->completionHandlerFlagParamIndex()) { + auto IsSuccessFlag = Conv->completionHandlerFlagIsErrorOnZero(); + CustomFlag = std::make_pair(*Idx, IsSuccessFlag); } - Blocks.SuccessBlock.addNode(Statement); } - return; } + if (CustomFlag) { + auto Idx = CustomFlag->first; + if (Idx < 0 || Idx >= SuccessParams.size()) + return None; - ClassifiedBlock *ThenBlock = &Blocks.SuccessBlock; - ClassifiedBlock *ElseBlock = &Blocks.ErrorBlock; + if (SuccessParams[Idx] != Cond.Subject) + return None; - if (ErrCondition.isValid() && (!IsResultParam || ErrCondition.ErrorCase) && - ErrCondition.Type == ConditionType::NOT_NIL) { - ClassifiedBlock *TempBlock = ThenBlock; - ThenBlock = ElseBlock; - ElseBlock = TempBlock; - } else { - ConditionType CondType = ConditionType::INVALID; - for (auto &Entry : CallbackConditions) { - if (IsResultParam || Entry.second.Subject != ErrParam) { - if (CondType == ConditionType::INVALID) { - CondType = Entry.second.Type; - } else if (CondType != Entry.second.Type) { - // Similar to the unknown conditions case. Add the whole if unless - // there's an else, in which case use the fallback instead. - // TODO: Split the `if` statement - - if (ElseStmt) { - DiagEngine.diagnose(Statement->getStartLoc(), - diag::mixed_callback_conditions); - } else { - CurrentBlock->addNode(Statement); + // The path may need to be flipped depending on whether the flag indicates + // success. + auto IsSuccessFlag = CustomFlag->second; + if (!IsSuccessFlag) + Path = flippedConditionPath(Path); + + return ClassifiedCondition(Cond, Path, /*ObjCStyleFlagCheck*/ true); + } + + // If we've reached here, we have a bool flag check that isn't specified in + // the async convention. We apply a heuristic to see if the error param is + // force unwrapped in the conditional body. In that case, the user is + // expecting it to be the error path, and it's more likely than not that the + // flag value conveys no more useful information in the error block. + + // First check the success block. + auto FoundInSuccessBlock = + hasForceUnwrappedErrorParam(SuccessNodes.getNodes()); + + // Then check the else block if we have it. + if (ASTNode ElseNode = ElseStmt) { + // Unwrap the BraceStmt of the else clause if needed. This is needed as + // we won't walk into BraceStmts by default as they introduce new + // scopes. + ArrayRef Nodes; + if (auto *BS = dyn_cast(ElseStmt)) { + Nodes = BS->getElements(); + } else { + Nodes = llvm::makeArrayRef(ElseNode); + } + if (hasForceUnwrappedErrorParam(Nodes)) { + // If we also found an unwrap in the success block, we don't know what's + // happening here. + if (FoundInSuccessBlock) + return None; + + // Otherwise we can determine this as a success condition. Note this is + // flipped as if the error is present in the else block, this condition + // is for success. + return ClassifiedCondition(Cond, ConditionPath::SUCCESS, + /*ObjCStyleFlagCheck*/ true); + } + } + + if (FoundInSuccessBlock) { + // Note that the path is flipped as if the error is present in the success + // block, this condition is for failure. + return ClassifiedCondition(Cond, ConditionPath::FAILURE, + /*ObjCStyleFlagCheck*/ true); + } + + // Otherwise we can't classify this. + return None; + } + + /// Classifies all the conditions present in a given StmtCondition, taking + /// into account its success body and failure body. Returns \c true if there + /// were any conditions that couldn't be classified, \c false otherwise. + bool classifyConditionsOf(StmtCondition Cond, + const NodesToPrint &ThenNodesToPrint, + Stmt *ElseStmt, + ClassifiedCallbackConditions &Conditions) { + bool UnhandledConditions = false; + Optional ObjCFlagCheck; + auto TryAddCond = [&](CallbackCondition CC) { + auto Classified = + classifyCallbackCondition(CC, ThenNodesToPrint, ElseStmt); + + // If we couldn't classify this, or if there are multiple Obj-C style flag + // checks, this is unhandled. + if (!Classified || (ObjCFlagCheck && Classified->IsObjCStyleFlagCheck)) { + UnhandledConditions = true; + return; + } + + // If we've seen multiple conditions for the same subject, don't handle + // this. + if (!Conditions.insert({CC.Subject, *Classified}).second) { + UnhandledConditions = true; + return; + } + + if (Classified->IsObjCStyleFlagCheck) + ObjCFlagCheck = Classified; + }; + + for (auto &CondElement : Cond) { + if (auto *BoolExpr = CondElement.getBooleanOrNull()) { + SmallVector Exprs; + Exprs.push_back(BoolExpr); + + while (!Exprs.empty()) { + auto *Next = Exprs.pop_back_val()->getSemanticsProvidingExpr(); + if (auto *ACE = dyn_cast(Next)) + Next = ACE->getSingleExpressionBody()->getSemanticsProvidingExpr(); + + if (auto *BE = dyn_cast_or_null(Next)) { + auto *Operator = isOperator(BE); + if (Operator) { + // If we have an && operator, decompose its arguments. + if (Operator->getBaseName() == "&&") { + Exprs.push_back(BE->getLHS()); + Exprs.push_back(BE->getRHS()); + } else { + // Otherwise check to see if we have an == nil or != nil + // condition. + TryAddCond(CallbackCondition(BE, Operator)); + } + continue; } + } + + // Check to see if we have a lone bool condition. + TryAddCond(CallbackCondition(Next)); + } + } else if (auto *P = CondElement.getPatternOrNull()) { + TryAddCond(CallbackCondition(P, CondElement.getInitializer())); + } + } + return UnhandledConditions || Conditions.empty(); + } + + /// Classifies the conditions of a conditional statement, and adds the + /// necessary nodes to either the success or failure block. + void classifyConditional(Stmt *Statement, StmtCondition Condition, + NodesToPrint ThenNodesToPrint, Stmt *ElseStmt) { + ClassifiedCallbackConditions CallbackConditions; + bool UnhandledConditions = classifyConditionsOf( + Condition, ThenNodesToPrint, ElseStmt, CallbackConditions); + auto ErrCondition = CallbackConditions.lookup(ErrParam); + + if (UnhandledConditions) { + // Some unknown conditions. If there's an else, assume we can't handle + // and use the fallback case. Otherwise add to either the success or + // error block depending on some heuristics, known conditions will have + // placeholders added (ideally we'd remove them) + // TODO: Remove known conditions and split the `if` statement + + if (IsKnownConditionPath) { + // If we're on a known condition path, we can be lenient as we already + // know what block we're in and can therefore just add the conditional + // straight to it. + CurrentBlock->addNode(Statement); + } else if (CallbackConditions.empty()) { + // Technically this has a similar problem, ie. the else could have + // conditions that should be in either success/error + CurrentBlock->addNode(Statement); + } else if (ElseStmt) { + DiagEngine.diagnose(Statement->getStartLoc(), + diag::unknown_callback_conditions); + } else if (ErrCondition && ErrCondition->Path == ConditionPath::FAILURE) { + Blocks.ErrorBlock.addNode(Statement); + } else { + for (auto &Entry : CallbackConditions) { + if (Entry.second.Path == ConditionPath::FAILURE) { + Blocks.ErrorBlock.addNode(Statement); return; } } + Blocks.SuccessBlock.addNode(Statement); } + return; + } - if (CondType == ConditionType::NIL) { - ClassifiedBlock *TempBlock = ThenBlock; - ThenBlock = ElseBlock; - ElseBlock = TempBlock; + // If all the conditions were classified, make sure they're all consistently + // on the success or failure path. + Optional Path; + for (auto &Entry : CallbackConditions) { + auto &Cond = Entry.second; + if (!Path) { + Path = Cond.Path; + } else if (*Path != Cond.Path) { + // Similar to the unknown conditions case. Add the whole if unless + // there's an else, in which case use the fallback instead. + // TODO: Split the `if` statement + + if (ElseStmt) { + DiagEngine.diagnose(Statement->getStartLoc(), + diag::mixed_callback_conditions); + } else { + CurrentBlock->addNode(Statement); + } + return; } } + assert(Path && "Didn't classify a path?"); - ThenBlock->addAllBindings(CallbackConditions, DiagEngine); - if (DiagEngine.hadAnyError()) - return; + auto *ThenBlock = &Blocks.SuccessBlock; + auto *ElseBlock = &Blocks.ErrorBlock; + + // If the condition is for a failure path, the error block is ThenBlock, and + // the success block is ElseBlock. + if (*Path == ConditionPath::FAILURE) + std::swap(ThenBlock, ElseBlock); + + // We'll be dropping the statement, but make sure to keep any attached + // comments. + CurrentBlock->addPossibleCommentLoc(Statement->getStartLoc()); + + ThenBlock->addAllBindings(CallbackConditions); // TODO: Handle nested ifs - setNodes(ThenBlock, ElseBlock, ThenNodes); + setNodes(ThenBlock, ElseBlock, std::move(ThenNodesToPrint)); if (ElseStmt) { if (auto *BS = dyn_cast(ElseStmt)) { - setNodes(ElseBlock, ThenBlock, BS->getElements()); + // If this is a guard statement, we know that we'll always exit, + // allowing us to classify any additional nodes into the opposite block. + auto AlwaysExits = isa(Statement); + setNodes(ElseBlock, ThenBlock, NodesToPrint::inBraceStmt(BS), + AlwaysExits); } else { - classifyNodes(ArrayRef(ElseStmt)); + // If we reached here, we should have an else if statement. Given we + // know we're in the else of a known condition, temporarily flip the + // current block, and set that we know what path we're on. + llvm::SaveAndRestore CondScope(IsKnownConditionPath, true); + llvm::SaveAndRestore BlockScope(CurrentBlock, + ElseBlock); + classifyNodes(ArrayRef(ElseStmt), + /*endCommentLoc*/ SourceLoc()); } } } + /// Adds \p Nodes to \p Block, potentially flipping the current block if we + /// can determine that the nodes being added will cause control flow to leave + /// the scope. + /// + /// \param Block The block to add the nodes to. + /// \param OtherBlock The block for the opposing condition path. + /// \param Nodes The nodes to add. + /// \param AlwaysExitsScope Whether the nodes being added always exit the + /// scope, and therefore whether the current block should be flipped. void setNodes(ClassifiedBlock *Block, ClassifiedBlock *OtherBlock, - ArrayRef Nodes) { - if (Nodes.empty()) - return; - if ((Nodes.back().isStmt(StmtKind::Return) || - Nodes.back().isStmt(StmtKind::Break)) && - !Nodes.back().isImplicit()) { + NodesToPrint Nodes, bool AlwaysExitsScope = false) { + // Drop an explicit trailing 'return' or 'break' if we can. + bool HasTrailingReturnOrBreak = Nodes.hasTrailingReturnOrBreak(); + if (HasTrailingReturnOrBreak) + Nodes.dropTrailingReturnOrBreakIfPossible(); + + // If we know we're exiting the scope, we can set IsKnownConditionPath, as + // we know any future nodes should be classified into the other block. + if (HasTrailingReturnOrBreak || AlwaysExitsScope) { CurrentBlock = OtherBlock; - Block->addAllNodes(Nodes.drop_back()); + IsKnownConditionPath = true; + Block->addAllNodes(std::move(Nodes)); } else { - Block->addAllNodes(Nodes); + Block->addAllNodes(std::move(Nodes)); } } @@ -4717,7 +5465,16 @@ struct CallbackClassifier { return; } - for (auto *CS : SS->getCases()) { + // We'll be dropping the switch, but make sure to keep any attached + // comments. + CurrentBlock->addPossibleCommentLoc(SS->getStartLoc()); + + // Push the cases into a vector. This is only done to eagerly evaluate the + // AsCaseStmtRange sequence so we can know what the last case is. + SmallVector Cases; + Cases.append(SS->getCases().begin(), SS->getCases().end()); + + for (auto *CS : Cases) { if (CS->hasFallthroughDest()) { DiagEngine.diagnose(CS->getLoc(), diag::callback_with_fallthrough); return; @@ -4739,24 +5496,308 @@ struct CallbackClassifier { return; } - CallbackCondition CC(ErrParam, &Items[0]); - ClassifiedBlock *Block = &Blocks.SuccessBlock; - ClassifiedBlock *OtherBlock = &Blocks.ErrorBlock; - if (CC.ErrorCase) { - Block = &Blocks.ErrorBlock; - OtherBlock = &Blocks.SuccessBlock; - } + auto *Block = &Blocks.SuccessBlock; + auto *OtherBlock = &Blocks.ErrorBlock; + auto SuccessNodes = NodesToPrint::inBraceStmt(CS->getBody()); - setNodes(Block, OtherBlock, CS->getBody()->getElements()); - Block->addBinding(CC, DiagEngine); - if (DiagEngine.hadAnyError()) + // Classify the case pattern. + auto CC = classifyCallbackCondition( + CallbackCondition(ErrParam, &Items[0]), SuccessNodes, + /*elseStmt*/ nullptr); + if (!CC) { + DiagEngine.diagnose(CS->getLoc(), diag::unknown_callback_case_item); return; + } + + if (CC->Path == ConditionPath::FAILURE) + std::swap(Block, OtherBlock); + + // We'll be dropping the case, but make sure to keep any attached + // comments. Because these comments will effectively be part of the + // previous case, add them to CurrentBlock. + CurrentBlock->addPossibleCommentLoc(CS->getStartLoc()); + + // Make sure to grab trailing comments in the last case stmt. + if (CS == Cases.back()) + Block->addPossibleCommentLoc(SS->getRBraceLoc()); + + setNodes(Block, OtherBlock, std::move(SuccessNodes)); + Block->addBinding(*CC); } // Mark this switch statement as having been transformed. HandledSwitches.insert(SS); } }; +/// Base name of a decl if it has one, an empty \c DeclBaseName otherwise. +static DeclBaseName getDeclName(const Decl *D) { + if (auto *VD = dyn_cast(D)) { + if (VD->hasName()) + return VD->getBaseName(); + } + return DeclBaseName(); +} + +class DeclCollector : private SourceEntityWalker { + llvm::DenseSet &Decls; + +public: + /// Collect all explicit declarations declared in \p Scope (or \p SF if + /// \p Scope is a nullptr) that are not within their own scope. + static void collect(BraceStmt *Scope, SourceFile &SF, + llvm::DenseSet &Decls) { + DeclCollector Collector(Decls); + if (Scope) { + for (auto Node : Scope->getElements()) { + Collector.walk(Node); + } + } else { + Collector.walk(SF); + } + } + +private: + DeclCollector(llvm::DenseSet &Decls) + : Decls(Decls) {} + + bool walkToDeclPre(Decl *D, CharSourceRange Range) override { + // Want to walk through top level code decls (which are implicitly added + // for top level non-decl code) and pattern binding decls (which contain + // the var decls that we care about). + if (isa(D) || isa(D)) + return true; + + if (!D->isImplicit()) + Decls.insert(D); + return false; + } + + bool walkToExprPre(Expr *E) override { + return !isa(E); + } + + bool walkToStmtPre(Stmt *S) override { + return S->isImplicit() || !startsNewScope(S); + } +}; + +class ReferenceCollector : private SourceEntityWalker { + SourceManager *SM; + llvm::DenseSet DeclaredDecls; + llvm::DenseSet &ReferencedDecls; + + ASTNode Target; + bool AfterTarget; + +public: + /// Collect all explicit references in \p Scope (or \p SF if \p Scope is + /// a nullptr) that are after \p Target and not first declared. That is, + /// references that we don't want to shadow with hoisted declarations. + /// + /// Also collect all declarations that are \c DeclContexts, which is an + /// over-appoximation but let's us ignore them elsewhere. + static void collect(ASTNode Target, BraceStmt *Scope, SourceFile &SF, + llvm::DenseSet &Decls) { + ReferenceCollector Collector(Target, &SF.getASTContext().SourceMgr, + Decls); + if (Scope) + Collector.walk(Scope); + else + Collector.walk(SF); + } + +private: + ReferenceCollector(ASTNode Target, SourceManager *SM, + llvm::DenseSet &Decls) + : SM(SM), DeclaredDecls(), ReferencedDecls(Decls), Target(Target), + AfterTarget(false) {} + + bool walkToDeclPre(Decl *D, CharSourceRange Range) override { + // Bit of a hack, include all contexts so they're never renamed (seems worse + // to rename a class/function than it does a variable). Again, an + // over-approximation, but hopefully doesn't come up too often. + if (isa(D) && !D->isImplicit()) { + ReferencedDecls.insert(D); + } + + if (AfterTarget && !D->isImplicit()) { + DeclaredDecls.insert(D); + } else if (D == Target.dyn_cast()) { + AfterTarget = true; + } + return shouldWalkInto(D->getSourceRange()); + } + + bool walkToExprPre(Expr *E) override { + if (AfterTarget && !E->isImplicit()) { + if (auto *DRE = dyn_cast(E)) { + if (auto *D = DRE->getDecl()) { + // Only care about references that aren't declared, as seen decls will + // be renamed (if necessary) during the refactoring. + if (!D->isImplicit() && !DeclaredDecls.count(D)) { + ReferencedDecls.insert(D); + + // Also add the async alternative of a function to prevent + // collisions if a call is replaced with the alternative. + if (auto *AFD = dyn_cast(D)) { + if (auto *Alternative = AFD->getAsyncAlternative()) + ReferencedDecls.insert(Alternative); + } + } + } + } + } else if (E == Target.dyn_cast()) { + AfterTarget = true; + } + return shouldWalkInto(E->getSourceRange()); + } + + bool walkToStmtPre(Stmt *S) override { + if (S == Target.dyn_cast()) + AfterTarget = true; + return shouldWalkInto(S->getSourceRange()); + } + + bool walkToPatternPre(Pattern *P) override { + if (P == Target.dyn_cast()) + AfterTarget = true; + return shouldWalkInto(P->getSourceRange()); + } + + bool shouldWalkInto(SourceRange Range) { + return AfterTarget || (SM && + SM->rangeContainsTokenLoc(Range, Target.getStartLoc())); + } +}; + +/// Similar to the \c ReferenceCollector but collects references in all scopes +/// without any starting point in each scope. In addition, it tracks the number +/// of references to a decl in a given scope. +class ScopedDeclCollector : private SourceEntityWalker { +public: + using DeclsTy = llvm::DenseSet; + using RefDeclsTy = llvm::DenseMap; + +private: + using ScopedDeclsTy = llvm::DenseMap; + + struct Scope { + DeclsTy DeclaredDecls; + RefDeclsTy *ReferencedDecls; + Scope(RefDeclsTy *ReferencedDecls) : DeclaredDecls(), + ReferencedDecls(ReferencedDecls) {} + }; + + ScopedDeclsTy ReferencedDecls; + llvm::SmallVector ScopeStack; + +public: + /// Starting at \c Scope, collect all explicit references in every scope + /// within (including the initial) that are not first declared, ie. those that + /// could end up shadowed. Also include all \c DeclContext declarations as + /// we'd like to avoid renaming functions and types completely. + void collect(ASTNode Node) { + walk(Node); + } + + const RefDeclsTy *getReferencedDecls(Stmt *Scope) const { + auto Res = ReferencedDecls.find(Scope); + if (Res == ReferencedDecls.end()) + return nullptr; + return &Res->second; + } + +private: + bool walkToDeclPre(Decl *D, CharSourceRange Range) override { + if (ScopeStack.empty() || D->isImplicit()) + return true; + + ScopeStack.back().DeclaredDecls.insert(D); + if (isa(D)) + (*ScopeStack.back().ReferencedDecls)[D] += 1; + return true; + } + + bool walkToExprPre(Expr *E) override { + if (ScopeStack.empty()) + return true; + + if (!E->isImplicit()) { + if (auto *DRE = dyn_cast(E)) { + if (auto *D = DRE->getDecl()) { + // If we have a reference that isn't declared in the same scope, + // increment the number of references to that decl. + if (!D->isImplicit() && !ScopeStack.back().DeclaredDecls.count(D)) { + (*ScopeStack.back().ReferencedDecls)[D] += 1; + + // Also add the async alternative of a function to prevent + // collisions if a call is replaced with the alternative. + if (auto *AFD = dyn_cast(D)) { + if (auto *Alternative = AFD->getAsyncAlternative()) + (*ScopeStack.back().ReferencedDecls)[Alternative] += 1; + } + } + } + } + } + return true; + } + + bool walkToStmtPre(Stmt *S) override { + // Purposely check \c BraceStmt here rather than \c startsNewScope. + // References in the condition should be applied to the previous scope, not + // the scope of that statement. + if (isa(S)) + ScopeStack.emplace_back(&ReferencedDecls[S]); + return true; + } + + bool walkToStmtPost(Stmt *S) override { + if (isa(S)) { + size_t NumScopes = ScopeStack.size(); + if (NumScopes >= 2) { + // Add any referenced decls to the parent scope that weren't declared + // there. + auto &ParentStack = ScopeStack[NumScopes - 2]; + for (auto DeclAndNumRefs : *ScopeStack.back().ReferencedDecls) { + auto *D = DeclAndNumRefs.first; + if (!ParentStack.DeclaredDecls.count(D)) + (*ParentStack.ReferencedDecls)[D] += DeclAndNumRefs.second; + } + } + ScopeStack.pop_back(); + } + return true; + } +}; + +/// Checks whether an ASTNode contains a reference to a given declaration. +class DeclReferenceFinder : private SourceEntityWalker { + bool HasFoundReference = false; + const Decl *Search; + + bool walkToExprPre(Expr *E) override { + if (auto DRE = dyn_cast(E)) { + if (DRE->getDecl() == Search) { + HasFoundReference = true; + return false; + } + } + return true; + } + + DeclReferenceFinder(const Decl *Search) : Search(Search) {} + +public: + /// Returns \c true if \p node contains a reference to \p Search, \c false + /// otherwise. + static bool containsReference(ASTNode Node, const ValueDecl *Search) { + DeclReferenceFinder Checker(Search); + Checker.walk(Node); + return Checker.HasFoundReference; + } +}; + /// Builds up async-converted code for an AST node. /// /// If it is a function, its declaration will have `async` added. If a @@ -4787,6 +5828,22 @@ struct CallbackClassifier { /// the code the user intended. In most cases the refactoring will continue, /// with any unhandled decls wrapped in placeholders instead. class AsyncConverter : private SourceEntityWalker { + struct Scope { + llvm::DenseSet Names; + + /// If this scope is wrapped in a \c withChecked(Throwing)Continuation, the + /// name of the continuation that must be resumed where there previously was + /// a call to the function's completion handler. + /// Otherwise an empty identifier. + Identifier ContinuationName; + + Scope(Identifier ContinuationName) + : Names(), ContinuationName(ContinuationName) {} + + /// Whether this scope is wrapped in a \c withChecked(Throwing)Continuation. + bool isWrappedInContination() const { return !ContinuationName.empty(); } + }; + SourceFile *SF; SourceManager &SM; DiagnosticEngine &DiagEngine; @@ -4795,7 +5852,8 @@ class AsyncConverter : private SourceEntityWalker { // Completion handler of `StartNode` (if it's a function with an async // alternative) - const AsyncHandlerDesc &TopHandler; + AsyncHandlerParamDesc TopHandler; + SmallString<0> Buffer; llvm::raw_svector_ostream OS; @@ -4803,33 +5861,81 @@ class AsyncConverter : private SourceEntityWalker { // elided, e.g for a previously optional closure parameter that has become a // non-optional local. llvm::DenseSet Unwraps; + // Decls whose references should be replaced with, either because they no // longer exist or are a different type. Any replaced code should ideally be // handled by the refactoring properly, but that's not possible in all cases llvm::DenseSet Placeholders; - // Mapping from decl -> name, used as both the name of possibly new local - // declarations of old parameters, as well as the replacement for any - // references to it - llvm::DenseMap Names; + + // Mapping from decl -> name, used as the name of possible new local + // declarations of old completion handler parametes, as well as the + // replacement for other hoisted declarations and their references + llvm::DenseMap Names; + + /// The scopes (containing all name decls and whether the scope is wrapped in + /// a continuation) as the AST is being walked. The first element is the + /// initial scope and the last is the current scope. + llvm::SmallVector Scopes; + + // Mapping of \c BraceStmt -> declarations referenced in that statement + // without first being declared. These are used to fill the \c ScopeNames + // map on entering that scope. + ScopedDeclCollector ScopedDecls; /// The switch statements that have been re-written by this transform. llvm::DenseSet HandledSwitches; - // These are per-node (ie. are saved and restored on each convertNode call) + // The last source location that has been output. Used to output the source + // between handled nodes SourceLoc LastAddedLoc; + + // Number of expressions (or pattern binding decl) currently nested in, taking + // into account hoisting and the possible removal of ifs/switches int NestedExprCount = 0; + // Whether a completion handler body is currently being hoisted out of its + // call + bool Hoisting = false; + + /// Whether a pattern is currently being converted. + bool ConvertingPattern = false; + + /// A mapping of inline patterns to print for closure parameters. + using InlinePatternsToPrint = llvm::DenseMap; + public: - AsyncConverter(SourceManager &SM, DiagnosticEngine &DiagEngine, - ASTNode StartNode, const AsyncHandlerDesc &TopHandler) - : SM(SM), DiagEngine(DiagEngine), StartNode(StartNode), - TopHandler(TopHandler), Buffer(), OS(Buffer) { - Placeholders.insert(TopHandler.Handler); + /// Convert a function + AsyncConverter(SourceFile *SF, SourceManager &SM, + DiagnosticEngine &DiagEngine, AbstractFunctionDecl *FD, + const AsyncHandlerParamDesc &TopHandler) + : SF(SF), SM(SM), DiagEngine(DiagEngine), StartNode(FD), + TopHandler(TopHandler), OS(Buffer) { + Placeholders.insert(TopHandler.getHandler()); + ScopedDecls.collect(FD); + + // Shouldn't strictly be necessary, but prefer possible shadowing over + // crashes caused by a missing scope + addNewScope({}); } + /// Convert a call + AsyncConverter(SourceFile *SF, SourceManager &SM, + DiagnosticEngine &DiagEngine, CallExpr *CE, BraceStmt *Scope) + : SF(SF), SM(SM), DiagEngine(DiagEngine), StartNode(CE), OS(Buffer) { + ScopedDecls.collect(CE); + + // Create the initial scope, can be more accurate than the general + // \c ScopedDeclCollector as there is a starting point. + llvm::DenseSet UsedDecls; + DeclCollector::collect(Scope, *SF, UsedDecls); + ReferenceCollector::collect(StartNode, Scope, *SF, UsedDecls); + addNewScope(UsedDecls); + } + + ASTContext &getASTContext() const { return SF->getASTContext(); } + bool convert() { - if (!Buffer.empty()) - return !DiagEngine.hadAnyError(); + assert(Buffer.empty() && "AsyncConverter can only be used once"); if (auto *FD = dyn_cast_or_null(StartNode.dyn_cast())) { addFuncDecl(FD); @@ -4837,11 +5943,84 @@ class AsyncConverter : private SourceEntityWalker { convertNode(FD->getBody()); } } else { - convertNode(StartNode); + convertNode(StartNode, /*StartOverride=*/{}, /*ConvertCalls=*/true, + /*IncludeComments=*/false); } return !DiagEngine.hadAnyError(); } + /// When adding an async alternative method for the function declaration \c + /// FD, this function tries to create a function body for the legacy function + /// (the one with a completion handler), which calls the newly converted async + /// function. There are certain situations in which we fail to create such a + /// body, e.g. if the completion handler has the signature `(String, Error?) + /// -> Void` in which case we can't synthesize the result of type \c String in + /// the error case. + bool createLegacyBody() { + assert(Buffer.empty() && + "AsyncConverter can only be used once"); + + if (!canCreateLegacyBody()) + return false; + + FuncDecl *FD = cast(StartNode.get()); + OS << tok::l_brace << "\n"; // start function body + OS << "Task " << tok::l_brace << "\n"; + addHoistedNamedCallback(FD, TopHandler, TopHandler.getNameStr(), [&]() { + if (TopHandler.HasError) { + OS << tok::kw_try << " "; + } + OS << "await "; + + // Since we're *creating* the async alternative here, there shouldn't + // already be one. Thus, just assume that the call to the alternative is + // the same as the call to the old completion handler function, minus the + // completion handler arg. + addForwardingCallTo(FD, /*HandlerReplacement=*/""); + }); + OS << "\n"; + OS << tok::r_brace << "\n"; // end 'Task' + OS << tok::r_brace << "\n"; // end function body + return true; + } + + /// Creates an async alternative function that forwards onto the completion + /// handler function through + /// withCheckedContinuation/withCheckedThrowingContinuation. + bool createAsyncWrapper() { + assert(Buffer.empty() && "AsyncConverter can only be used once"); + auto *FD = cast(StartNode.get()); + + // First add the new async function declaration. + addFuncDecl(FD); + OS << tok::l_brace << "\n"; + + // Then add the body. + OS << tok::kw_return << " "; + if (TopHandler.HasError) + OS << tok::kw_try << " "; + + OS << "await "; + + // withChecked[Throwing]Continuation { continuation in + if (TopHandler.HasError) { + OS << "withCheckedThrowingContinuation"; + } else { + OS << "withCheckedContinuation"; + } + OS << " " << tok::l_brace << " continuation " << tok::kw_in << "\n"; + + // fnWithHandler(args...) { ... } + auto ClosureStr = + getAsyncWrapperCompletionClosure("continuation", TopHandler); + addForwardingCallTo(FD, /*HandlerReplacement=*/ClosureStr); + + OS << "\n"; + OS << tok::r_brace << "\n"; // end continuation closure + OS << tok::r_brace << "\n"; // end function body + return true; + } + void replace(ASTNode Node, SourceEditConsumer &EditConsumer, SourceLoc StartOverride = SourceLoc()) { SourceRange Range = Node.getSourceRange(); @@ -4861,30 +6040,336 @@ class AsyncConverter : private SourceEntityWalker { } private: - void convertNodes(ArrayRef Nodes) { - for (auto Node : Nodes) { + bool canCreateLegacyBody() { + FuncDecl *FD = dyn_cast(StartNode.dyn_cast()); + if (!FD) { + return false; + } + if (FD == nullptr || FD->getBody() == nullptr) { + return false; + } + if (FD->hasThrows()) { + assert(!TopHandler.isValid() && "We shouldn't have found a handler desc " + "if the original function throws"); + return false; + } + return TopHandler.isValid(); + } + + /// Prints a tuple of elements, or a lone single element if only one is + /// present, using the provided printing function. + template + void addTupleOf(const Container &Elements, llvm::raw_ostream &OS, + PrintFn PrintElt) { + if (Elements.size() == 1) { + PrintElt(Elements[0]); + return; + } + OS << tok::l_paren; + llvm::interleave(Elements, PrintElt, [&]() { OS << tok::comma << " "; }); + OS << tok::r_paren; + } + + /// Retrieve the completion handler closure argument for an async wrapper + /// function. + std::string + getAsyncWrapperCompletionClosure(StringRef ContName, + const AsyncHandlerParamDesc &HandlerDesc) { + std::string OutputStr; + llvm::raw_string_ostream OS(OutputStr); + + OS << tok::l_brace; // start closure + + // Prepare parameter names for the closure. + auto SuccessParams = HandlerDesc.getSuccessParams(); + SmallVector, 2> SuccessParamNames; + for (auto idx : indices(SuccessParams)) { + SuccessParamNames.emplace_back("result"); + + // If we have multiple success params, number them e.g res1, res2... + if (SuccessParams.size() > 1) + SuccessParamNames.back().append(std::to_string(idx + 1)); + } + Optional> ErrName; + if (HandlerDesc.getErrorParam()) + ErrName.emplace("error"); + + auto HasAnyParams = !SuccessParamNames.empty() || ErrName; + if (HasAnyParams) + OS << " "; + + // res1, res2 + llvm::interleave( + SuccessParamNames, [&](auto Name) { OS << Name; }, + [&]() { OS << tok::comma << " "; }); + + // , err + if (ErrName) { + if (!SuccessParamNames.empty()) + OS << tok::comma << " "; + + OS << *ErrName; + } + if (HasAnyParams) + OS << " " << tok::kw_in; + + OS << "\n"; + + // The closure body. + switch (HandlerDesc.Type) { + case HandlerType::PARAMS: { + // For a (Success?, Error?) -> Void handler, we do an if let on the error. + if (ErrName) { + // if let err = err { + OS << tok::kw_if << " " << tok::kw_let << " "; + OS << *ErrName << " " << tok::equal << " " << *ErrName << " "; + OS << tok::l_brace << "\n"; + for (auto Idx : indices(SuccessParamNames)) { + auto ParamTy = SuccessParams[Idx].getParameterType(); + if (!HandlerDesc.shouldUnwrap(ParamTy)) + continue; + } + + // continuation.resume(throwing: err) + OS << ContName << tok::period << "resume" << tok::l_paren; + OS << "throwing" << tok::colon << " " << *ErrName; + OS << tok::r_paren << "\n"; + + // return } + OS << tok::kw_return << "\n"; + OS << tok::r_brace << "\n"; + } + + // If we have any success params that we need to unwrap, insert a guard. + for (auto Idx : indices(SuccessParamNames)) { + auto &Name = SuccessParamNames[Idx]; + auto ParamTy = SuccessParams[Idx].getParameterType(); + if (!HandlerDesc.shouldUnwrap(ParamTy)) + continue; + + // guard let res = res else { + OS << tok::kw_guard << " " << tok::kw_let << " "; + OS << Name << " " << tok::equal << " " << Name << " " << tok::kw_else; + OS << " " << tok::l_brace << "\n"; + + // fatalError(...) + OS << "fatalError" << tok::l_paren; + OS << "\"Expected non-nil result '" << Name << "' for nil error\""; + OS << tok::r_paren << "\n"; + + // End guard. + OS << tok::r_brace << "\n"; + } + + // continuation.resume(returning: (res1, res2, ...)) + OS << ContName << tok::period << "resume" << tok::l_paren; + OS << "returning" << tok::colon << " "; + addTupleOf(SuccessParamNames, OS, [&](auto Ref) { OS << Ref; }); + OS << tok::r_paren << "\n"; + break; + } + case HandlerType::RESULT: { + // continuation.resume(with: res) + assert(SuccessParamNames.size() == 1); + OS << ContName << tok::period << "resume" << tok::l_paren; + OS << "with" << tok::colon << " " << SuccessParamNames[0]; + OS << tok::r_paren << "\n"; + break; + } + case HandlerType::INVALID: + llvm_unreachable("Should not have an invalid handler here"); + } + + OS << tok::r_brace; // end closure + return OutputStr; + } + + /// Retrieves the location for the start of a comment attached to the token + /// at the provided location, or the location itself if there is no comment. + SourceLoc getLocIncludingPrecedingComment(SourceLoc Loc) { + auto Tokens = SF->getAllTokens(); + auto TokenIter = token_lower_bound(Tokens, Loc); + if (TokenIter != Tokens.end() && TokenIter->hasComment()) + return TokenIter->getCommentStart(); + return Loc; + } + + /// If the provided SourceLoc has a preceding comment, print it out. Returns + /// true if a comment was printed, false otherwise. + bool printCommentIfNeeded(SourceLoc Loc, bool AddNewline = false) { + auto PrecedingLoc = getLocIncludingPrecedingComment(Loc); + if (Loc == PrecedingLoc) + return false; + if (AddNewline) + OS << "\n"; + OS << CharSourceRange(SM, PrecedingLoc, Loc).str(); + return true; + } + + void convertNodes(const NodesToPrint &ToPrint) { + // Sort the possible comment locs in reverse order so we can pop them as we + // go. + SmallVector CommentLocs; + CommentLocs.append(ToPrint.getPossibleCommentLocs().begin(), + ToPrint.getPossibleCommentLocs().end()); + llvm::sort(CommentLocs.begin(), CommentLocs.end(), [](auto lhs, auto rhs) { + return lhs.getOpaquePointerValue() > rhs.getOpaquePointerValue(); + }); + + // First print the nodes we've been asked to print. + for (auto Node : ToPrint.getNodes()) { OS << "\n"; + + // If we need to print comments, do so now. + while (!CommentLocs.empty()) { + auto CommentLoc = CommentLocs.back().getOpaquePointerValue(); + auto NodeLoc = Node.getStartLoc().getOpaquePointerValue(); + assert(CommentLoc != NodeLoc && + "Added node to both comment locs and nodes to print?"); + + // If the comment occurs after the node, don't print now. Wait until + // the right node comes along. + if (CommentLoc > NodeLoc) + break; + + printCommentIfNeeded(CommentLocs.pop_back_val()); + } convertNode(Node); } + + // We're done printing nodes. Make sure to output the remaining comments. + bool HasPrintedComment = false; + while (!CommentLocs.empty()) { + HasPrintedComment |= + printCommentIfNeeded(CommentLocs.pop_back_val(), + /*AddNewline*/ !HasPrintedComment); + } } void convertNode(ASTNode Node, SourceLoc StartOverride = {}, - bool ConvertCalls = true) { + bool ConvertCalls = true, + bool IncludePrecedingComment = true) { if (!StartOverride.isValid()) StartOverride = Node.getStartLoc(); + // Make sure to include any preceding comments attached to the loc + if (IncludePrecedingComment) + StartOverride = getLocIncludingPrecedingComment(StartOverride); + llvm::SaveAndRestore RestoreLoc(LastAddedLoc, StartOverride); llvm::SaveAndRestore RestoreCount(NestedExprCount, ConvertCalls ? 0 : 1); + walk(Node); addRange(LastAddedLoc, Node.getEndLoc(), /*ToEndOfToken=*/true); } + void convertPattern(const Pattern *P) { + // Only print semantic patterns. This cleans up the output of the transform + // and works around some bogus source locs that can appear with typed + // patterns in if let statements. + P = P->getSemanticsProvidingPattern(); + + // Set up the start of the pattern as the last loc printed to make sure we + // accurately fill in the gaps as we customize the printing of sub-patterns. + llvm::SaveAndRestore RestoreLoc(LastAddedLoc, P->getStartLoc()); + llvm::SaveAndRestore RestoreFlag(ConvertingPattern, true); + + walk(const_cast(P)); + addRange(LastAddedLoc, P->getEndLoc(), /*ToEndOfToken*/ true); + } + + /// Check whether \p Node requires the remainder of this scope to be wrapped + /// in a \c withChecked(Throwing)Continuation. If it is necessary, add + /// a call to \c withChecked(Throwing)Continuation and modify the current + /// scope (\c Scopes.back() ) so that it knows it's wrapped in a continuation. + /// + /// Wrapping a node in a continuation is necessary if the following conditions + /// are satisfied: + /// - It contains a reference to the \c TopHandler's completion hander, + /// because these completion handler calls need to be promoted to \c return + /// statements in the refactored method, but + /// - We cannot hoist the completion handler of \p Node, because it doesn't + /// have an async alternative by our heuristics (e.g. because of a + /// completion handler name mismatch or because it also returns a value + /// synchronously). + void wrapScopeInContinationIfNecessary(ASTNode Node) { + if (NestedExprCount != 0) { + // We can't start a continuation in the middle of an expression + return; + } + if (Scopes.back().isWrappedInContination()) { + // We are already in a continuation. No need to add another one. + return; + } + if (!DeclReferenceFinder::containsReference(Node, + TopHandler.getHandler())) { + // The node doesn't have a reference to the function's completion handler. + // It can stay a call with a completion handler, because we don't need to + // promote a completion handler call to a 'return'. + return; + } + + // Wrap the current call in a continuation + + Identifier contName = createUniqueName("continuation"); + Scopes.back().Names.insert(contName); + Scopes.back().ContinuationName = contName; + + insertCustom(Node.getStartLoc(), [&]() { + OS << tok::kw_return << ' '; + if (TopHandler.HasError) { + OS << tok::kw_try << ' '; + } + OS << "await "; + if (TopHandler.HasError) { + OS << "withCheckedThrowingContinuation "; + } else { + OS << "withCheckedContinuation "; + } + OS << tok::l_brace << ' ' << contName << ' ' << tok::kw_in << '\n'; + }); + } + + bool walkToPatternPre(Pattern *P) override { + // If we're not converting a pattern, there's nothing extra to do. + if (!ConvertingPattern) + return true; + + // When converting a pattern, don't print the 'let' or 'var' of binding + // subpatterns, as they're illegal when nested in PBDs, and we print a + // top-level one. + if (auto *BP = dyn_cast(P)) { + return addCustom(BP->getSourceRange(), [&]() { + convertPattern(BP->getSubPattern()); + }); + } + return true; + } + bool walkToDeclPre(Decl *D, CharSourceRange Range) override { if (isa(D)) { + // We can't hoist a closure inside a PatternBindingDecl. If it contains + // a call to the completion handler, wrap it in a continuation. + wrapScopeInContinationIfNecessary(D); NestedExprCount++; return true; } + + // Functions and types already have their names in \c Scopes.Names, only + // variables should need to be renamed. + if (isa(D)) { + // If we don't already have a name for the var, assign it one. Note that + // vars in binding patterns may already have assigned names here. + if (Names.find(D) == Names.end()) { + auto Ident = assignUniqueName(D, StringRef()); + Scopes.back().Names.insert(Ident); + } + addCustom(D->getSourceRange(), [&]() { + OS << newNameFor(D); + }); + } + // Note we don't walk into any nested local function decls. If we start // doing so in the future, be sure to update the logic that deals with // converting unhandled returns into placeholders in walkToStmtPre. @@ -4902,6 +6387,14 @@ class AsyncConverter : private SourceEntityWalker { // TODO: Handle Result.get as well if (auto *DRE = dyn_cast(E)) { if (auto *D = DRE->getDecl()) { + // Look through to the parent var decl if we have one. This ensures we + // look at the var in a case stmt's pattern rather than the var that's + // implicitly declared in the body. + if (auto *VD = dyn_cast(D)) { + if (auto *Parent = VD->getParentVarDecl()) + D = Parent; + } + bool AddPlaceholder = Placeholders.count(D); StringRef Name = newNameFor(D, false); if (AddPlaceholder || !Name.empty()) @@ -4931,19 +6424,37 @@ class AsyncConverter : private SourceEntityWalker { return addCustom(E->getSourceRange(), [&]() { OS << newNameFor(D, true); }); } - } else if (NestedExprCount == 0) { - if (CallExpr *CE = TopHandler.getAsHandlerCall(E)) - return addCustom(CE->getSourceRange(), [&]() { addHandlerCall(CE); }); - - if (auto *CE = dyn_cast(E)) { - auto HandlerDesc = AsyncHandlerDesc::find( - getUnderlyingFunc(CE->getFn()), StartNode.dyn_cast() == CE); - if (HandlerDesc.isValid()) + } else if (CallExpr *CE = TopHandler.getAsHandlerCall(E)) { + if (Scopes.back().isWrappedInContination()) { + return addCustom(E->getSourceRange(), + [&]() { convertHandlerToContinuationResume(CE); }); + } else if (NestedExprCount == 0) { + return addCustom(E->getSourceRange(), + [&]() { convertHandlerToReturnOrThrows(CE); }); + } + } else if (auto *CE = dyn_cast(E)) { + // Try and hoist a call's completion handler. Don't do so if + // - the current expression is nested (we can't start hoisting in the + // middle of an expression) + // - the current scope is wrapped in a continuation (we can't have await + // calls in the continuation block) + if (NestedExprCount == 0 && !Scopes.back().isWrappedInContination()) { + // If the refactoring is on the call itself, do not require the callee + // to have the @available attribute or a completion-like name. + auto HandlerDesc = AsyncHandlerParamDesc::find( + getUnderlyingFunc(CE->getFn()), + /*RequireAttributeOrName=*/StartNode.dyn_cast() != CE); + if (HandlerDesc.isValid()) { return addCustom(CE->getSourceRange(), [&]() { addHoistedCallback(CE, HandlerDesc); }); + } } } + // We didn't do any special conversion for this expression. If needed, wrap + // it in a continuation. + wrapScopeInContinationIfNecessary(E); + NestedExprCount++; return true; } @@ -4956,44 +6467,88 @@ class AsyncConverter : private SourceEntityWalker { }); } + bool walkToExprPost(Expr *E) override { + NestedExprCount--; + return true; + } + +#undef PLACEHOLDER_START +#undef PLACEHOLDER_END + bool walkToStmtPre(Stmt *S) override { - // Some break and return statements need to be turned into placeholders, - // as they may no longer perform the control flow that the user is - // expecting. - if (!S->isImplicit()) { - // For a break, if it's jumping out of a switch statement that we've - // re-written as a part of the transform, turn it into a placeholder, as - // it would have been lifted out of the switch statement. + // CaseStmt has an implicit BraceStmt inside it, which *should* start a new + // scope, so don't check isImplicit here. + if (startsNewScope(S)) { + // Add all names of decls referenced within this statement that aren't + // also declared first, plus any contexts. Note that \c getReferencedDecl + // will only return a value for a \c BraceStmt. This means that \c IfStmt + // (and other statements with conditions) will have their own empty scope, + // which is fine for our purposes - their existing names are always valid. + // The body of those statements will include the decls if they've been + // referenced, so shadowing is still avoided there. + if (auto *ReferencedDecls = ScopedDecls.getReferencedDecls(S)) { + llvm::DenseSet Decls; + for (auto DeclAndNumRefs : *ReferencedDecls) + Decls.insert(DeclAndNumRefs.first); + addNewScope(Decls); + } else { + addNewScope({}); + } + } else if (Hoisting && !S->isImplicit()) { + // Some break and return statements need to be turned into placeholders, + // as they may no longer perform the control flow that the user is + // expecting. if (auto *BS = dyn_cast(S)) { + // For a break, if it's jumping out of a switch statement that we've + // re-written as a part of the transform, turn it into a placeholder, as + // it would have been lifted out of the switch statement. if (auto *SS = dyn_cast(BS->getTarget())) { if (HandledSwitches.contains(SS)) - replaceRangeWithPlaceholder(S->getSourceRange()); + return replaceRangeWithPlaceholder(S->getSourceRange()); } + } else if (isa(S) && NestedExprCount == 0) { + // For a return, if it's not nested inside another closure or function, + // turn it into a placeholder, as it will be lifted out of the callback. + // Note that we only turn the 'return' token into a placeholder as we + // still want to be able to apply transforms to the argument. + replaceRangeWithPlaceholder(S->getStartLoc()); } - - // For a return, if it's not nested inside another closure or function, - // turn it into a placeholder, as it will be lifted out of the callback. - if (isa(S) && NestedExprCount == 0) - replaceRangeWithPlaceholder(S->getSourceRange()); } return true; } -#undef PLACEHOLDER_START -#undef PLACEHOLDER_END - - bool walkToExprPost(Expr *E) override { - NestedExprCount--; + bool walkToStmtPost(Stmt *S) override { + if (startsNewScope(S)) { + bool ClosedScopeWasWrappedInContinuation = + Scopes.back().isWrappedInContination(); + Scopes.pop_back(); + if (ClosedScopeWasWrappedInContinuation && + !Scopes.back().isWrappedInContination()) { + // The nested scope was wrapped in a continuation but the current one + // isn't anymore. Add the '}' that corresponds to the the call to + // withChecked(Throwing)Continuation. + insertCustom(S->getEndLoc(), [&]() { OS << tok::r_brace << '\n'; }); + } + } return true; } - bool addCustom(SourceRange Range, std::function Custom = {}) { + bool addCustom(SourceRange Range, llvm::function_ref Custom = {}) { addRange(LastAddedLoc, Range.Start); Custom(); LastAddedLoc = Lexer::getLocForEndOfToken(SM, Range.End); return false; } + /// Insert custom text at the given \p Loc that shouldn't replace any existing + /// source code. + bool insertCustom(SourceLoc Loc, llvm::function_ref Custom = {}) { + addRange(LastAddedLoc, Loc); + Custom(); + LastAddedLoc = Loc; + return false; + } + void addRange(SourceLoc Start, SourceLoc End, bool ToEndOfToken = false) { if (ToEndOfToken) { OS << Lexer::getCharSourceRangeFromSourceRange(SM, @@ -5010,29 +6565,73 @@ class AsyncConverter : private SourceEntityWalker { void addFuncDecl(const FuncDecl *FD) { auto *Params = FD->getParameters(); + auto *HandlerParam = TopHandler.getHandlerParam(); + auto ParamPos = TopHandler.handlerParamPosition(); + + // If the completion handler parameter has a default argument, the async + // version is effectively @discardableResult, as not all the callers care + // about receiving the completion call. + if (HandlerParam && HandlerParam->isDefaultArgument()) + OS << tok::at_sign << "discardableResult" << "\n"; // First chunk: start -> the parameter to remove (if any) - SourceLoc LeftEndLoc = Params->getLParenLoc().getAdvancedLoc(1); - if (TopHandler.Index - 1 >= 0) { + SourceLoc LeftEndLoc; + switch (ParamPos) { + case AsyncHandlerParamDesc::Position::None: + case AsyncHandlerParamDesc::Position::Only: + case AsyncHandlerParamDesc::Position::First: + // Handler is the first param (or there is none), so only include the ( + LeftEndLoc = Params->getLParenLoc().getAdvancedLoc(1); + break; + case AsyncHandlerParamDesc::Position::Middle: + // Handler is somewhere in the middle of the params, so we need to + // include any comments and comma up until the handler + LeftEndLoc = Params->get(TopHandler.Index)->getStartLoc(); + LeftEndLoc = getLocIncludingPrecedingComment(LeftEndLoc); + break; + case AsyncHandlerParamDesc::Position::Last: + // Handler is the last param, which means we don't want the comma. This + // is a little annoying since we *do* want the comments past for the + // last parameter LeftEndLoc = Lexer::getLocForEndOfToken( SM, Params->get(TopHandler.Index - 1)->getEndLoc()); + // Skip to the end of any comments + Token Next = Lexer::getTokenAtLocation(SM, LeftEndLoc, + CommentRetentionMode::None); + if (Next.getKind() != tok::NUM_TOKENS) + LeftEndLoc = Next.getLoc(); + break; } addRange(FD->getSourceRangeIncludingAttrs().Start, LeftEndLoc); // Second chunk: end of the parameter to remove -> right parenthesis - SourceLoc MidStartLoc = LeftEndLoc; + SourceLoc MidStartLoc; SourceLoc MidEndLoc = Params->getRParenLoc().getAdvancedLoc(1); - if (TopHandler.isValid()) { - if ((size_t)(TopHandler.Index + 1) < Params->size()) { - MidStartLoc = Params->get(TopHandler.Index + 1)->getStartLoc(); - } else { - MidStartLoc = Params->getRParenLoc(); - } + switch (ParamPos) { + case AsyncHandlerParamDesc::Position::None: + // No handler param, so make sure to include them all + MidStartLoc = LeftEndLoc; + break; + case AsyncHandlerParamDesc::Position::First: + case AsyncHandlerParamDesc::Position::Middle: + // Handler param is either the first or one of the middle params. Skip + // past it but make sure to include comments preceding the param after + // the handler + MidStartLoc = Params->get(TopHandler.Index + 1)->getStartLoc(); + MidStartLoc = getLocIncludingPrecedingComment(MidStartLoc); + break; + case AsyncHandlerParamDesc::Position::Only: + case AsyncHandlerParamDesc::Position::Last: + // Handler param is last, this is easy since there's no other params + // to copy over + MidStartLoc = Params->getRParenLoc(); + break; } addRange(MidStartLoc, MidEndLoc); // Third chunk: add in async and throws if necessary - OS << " async"; + if (!FD->hasAsync()) + OS << " async"; if (FD->hasThrows() || TopHandler.HasError) // TODO: Add throws if converting a function and it has a converted call // without a do/catch @@ -5052,7 +6651,7 @@ class AsyncConverter : private SourceEntityWalker { return; } - SmallVector Scratch; + SmallVector Scratch; auto ReturnTypes = TopHandler.getAsyncReturnTypes(Scratch); if (ReturnTypes.empty()) { OS << " "; @@ -5062,14 +6661,7 @@ class AsyncConverter : private SourceEntityWalker { // Print the function result type, making sure to omit a '-> Void' return. if (!TopHandler.willAsyncReturnVoid()) { OS << " -> "; - if (ReturnTypes.size() > 1) - OS << "("; - - llvm::interleave( - ReturnTypes, [&](Type Ty) { Ty->print(OS); }, [&]() { OS << ", "; }); - - if (ReturnTypes.size() > 1) - OS << ")"; + addAsyncFuncReturnType(TopHandler); } if (FD->hasBody()) @@ -5099,67 +6691,427 @@ class AsyncConverter : private SourceEntityWalker { } } - void addDo() { OS << tok::kw_do << " " << tok::l_brace << "\n"; } + void addDo() { OS << tok::kw_do << " " << tok::l_brace << "\n"; } + + /// Assuming that \p Result represents an error result to completion handler, + /// returns \c true if the error has already been handled through a + /// 'try await'. + bool isErrorAlreadyHandled(HandlerResult Result) { + assert(Result.isError()); + assert(Result.args().size() == 1 && + "There should only be one error parameter"); + // We assume that the error has already been handled if its variable + // declaration doesn't exist anymore, which is the case if it's in + // Placeholders but not in Unwraps (if it's in Placeholders and Unwraps + // an optional Error has simply been promoted to a non-optional Error). + if (auto DRE = dyn_cast(Result.args().back())) { + if (Placeholders.count(DRE->getDecl()) && + !Unwraps.count(DRE->getDecl())) { + return true; + } + } + return false; + } + + /// Returns \c true if the source representation of \p E can be interpreted + /// as an expression returning an Optional value. + bool isExpressionOptional(Expr *E) { + if (isa(E)) { + // E is downgrading a non-Optional result to an Optional. Its source + // representation isn't Optional. + return false; + } + if (auto DRE = dyn_cast(E)) { + if (Unwraps.count(DRE->getDecl())) { + // E has been promoted to a non-Optional value. It can't be used as an + // Optional anymore. + return false; + } + } + if (!E->getType().isNull() && E->getType()->isOptional()) { + return true; + } + // We couldn't determine the type. Assume non-Optional. + return false; + } + + /// Converts a call \p CE to a completion handler. Depending on the call it + /// will be interpreted as a call that's returning a success result, an error + /// or, if the call is completely ambiguous, adds an if-let that checks if the + /// error is \c nil at runtime and dispatches to the success or error case + /// depending on it. + /// \p AddConvertedHandlerCall needs to add the converted version of the + /// completion handler. Depending on the given \c HandlerResult, it must be + /// intepreted as a success or error call. + /// \p AddConvertedErrorCall must add the converted equivalent of returning an + /// error. The passed \c StringRef contains the name of a variable that is of + /// type 'Error'. + void convertHandlerCall( + const CallExpr *CE, + llvm::function_ref AddConvertedHandlerCall, + llvm::function_ref AddConvertedErrorCall) { + auto Result = + TopHandler.extractResultArgs(CE, /*ReturnErrorArgsIfAmbiguous=*/true); + if (!TopHandler.isAmbiguousCallToParamHandler(CE)) { + if (Result.isError()) { + if (!isErrorAlreadyHandled(Result)) { + // If the error has already been handled, we don't need to add another + // throwing call. + AddConvertedHandlerCall(Result); + } + } else { + AddConvertedHandlerCall(Result); + } + } else { + assert(Result.isError() && "If the call was ambiguous, we should have " + "retrieved its error representation"); + assert(Result.args().size() == 1 && + "There should only be one error parameter"); + Expr *ErrorExpr = Result.args().back(); + if (isErrorAlreadyHandled(Result)) { + // The error has already been handled, interpret the call as a success + // call. + auto SuccessExprs = TopHandler.extractResultArgs( + CE, /*ReturnErrorArgsIfAmbiguous=*/false); + AddConvertedHandlerCall(SuccessExprs); + } else if (!isExpressionOptional(ErrorExpr)) { + // The error is never nil. No matter what the success param is, we + // interpret it as an error call. + AddConvertedHandlerCall(Result); + } else { + // The call was truly ambiguous. Add an + // if let error = { + // throw error // or equivalent + // } else { + // + // } + auto SuccessExprs = TopHandler.extractResultArgs( + CE, /*ReturnErrorArgsIfAmbiguous=*/false); + + // The variable 'error' is only available in the 'if let' scope, so we + // don't need to create a new unique one. + StringRef ErrorName = "error"; + OS << tok::kw_if << ' ' << tok::kw_let << ' ' << ErrorName << ' ' + << tok::equal << ' '; + convertNode(ErrorExpr, /*StartOverride=*/{}, /*ConvertCalls=*/false); + OS << ' ' << tok::l_brace << '\n'; + AddConvertedErrorCall(ErrorName); + OS << tok::r_brace << ' ' << tok::kw_else << ' ' << tok::l_brace + << '\n'; + AddConvertedHandlerCall(SuccessExprs); + OS << '\n' << tok::r_brace; + } + } + } + + /// Convert a call \p CE to a completion handler to its 'return' or 'throws' + /// equivalent. + void convertHandlerToReturnOrThrows(const CallExpr *CE) { + return convertHandlerCall( + CE, + [&](HandlerResult Exprs) { + convertHandlerToReturnOrThrowsImpl(CE, Exprs); + }, + [&](StringRef ErrorName) { + OS << tok::kw_throw << ' ' << ErrorName << '\n'; + }); + } + + /// Convert the call \p CE to a completion handler to its 'return' or 'throws' + /// equivalent, where \p Result determines whether the call should be + /// interpreted as an error or success call. + void convertHandlerToReturnOrThrowsImpl(const CallExpr *CE, + HandlerResult Result) { + bool AddedReturnOrThrow = true; + if (!Result.isError()) { + // It's possible the user has already written an explicit return statement + // for the completion handler call, e.g 'return completion(args...)'. In + // that case, be sure not to add another return. + auto *parent = getWalker().Parent.getAsStmt(); + if (isa_and_nonnull(parent) && + !cast(parent)->isImplicit()) { + // The statement already has a return keyword. Don't add another one. + AddedReturnOrThrow = false; + } else { + OS << tok::kw_return; + } + } else { + OS << tok::kw_throw; + } + + ArrayRef Args = Result.args(); + if (!Args.empty()) { + if (AddedReturnOrThrow) + OS << ' '; + unsigned I = 0; + addTupleOf(Args, OS, [&](Expr *Elt) { + // Special case: If the completion handler is a params handler that + // takes an error, we could pass arguments to it without unwrapping + // them. E.g. + // simpleWithError { (res: String?, error: Error?) in + // completion(res, nil) + // } + // But after refactoring `simpleWithError` to an async function we have + // let res: String = await simple() + // and `res` is no longer an `Optional`. Thus it's in `Placeholders` and + // `Unwraps` and any reference to it will be replaced by a placeholder + // unless it is wrapped in an unwrapping expression. This would cause us + // to create `return <#res# >`. + // Under our assumption that either the error or the result parameter + // are non-nil, the above call to the completion handler is equivalent + // to + // completion(res!, nil) + // which correctly yields + // return res + // Synthesize the force unwrap so that we get the expected results. + if (TopHandler.getHandlerType() == HandlerType::PARAMS && + TopHandler.HasError) { + if (auto DRE = + dyn_cast(Elt->getSemanticsProvidingExpr())) { + auto D = DRE->getDecl(); + if (Unwraps.count(D)) { + Elt = new (getASTContext()) ForceValueExpr(Elt, SourceLoc()); + } + } + } + // Can't just add the range as we need to perform replacements + convertNode(Elt, /*StartOverride=*/CE->getArgumentLabelLoc(I), + /*ConvertCalls=*/false); + I++; + }); + } + } + + /// Convert a call \p CE to a completion handler to resumes of the + /// continuation that's currently on top of the stack. + void convertHandlerToContinuationResume(const CallExpr *CE) { + return convertHandlerCall( + CE, + [&](HandlerResult Exprs) { + convertHandlerToContinuationResumeImpl(CE, Exprs); + }, + [&](StringRef ErrorName) { + Identifier ContinuationName = Scopes.back().ContinuationName; + OS << ContinuationName << tok::period << "resume" << tok::l_paren + << "throwing" << tok::colon << ' ' << ErrorName; + OS << tok::r_paren << '\n'; + }); + } - void addHandlerCall(const CallExpr *CE) { - auto Exprs = TopHandler.extractResultArgs(CE); + /// Convert a call \p CE to a completion handler to resumes of the + /// continuation that's currently on top of the stack. + /// \p Result determines whether the call should be interpreted as a success + /// or error call. + void convertHandlerToContinuationResumeImpl(const CallExpr *CE, + HandlerResult Result) { + assert(Scopes.back().isWrappedInContination()); - if (!Exprs.isError()) { - OS << tok::kw_return; - } else { - OS << tok::kw_throw; + std::vector Args; + StringRef ResumeArgumentLabel; + switch (TopHandler.getHandlerType()) { + case HandlerType::PARAMS: { + Args = Result.args(); + if (!Result.isError()) { + ResumeArgumentLabel = "returning"; + } else { + ResumeArgumentLabel = "throwing"; + } + break; + } + case HandlerType::RESULT: { + Args = callArgs(CE).ref(); + ResumeArgumentLabel = "with"; + break; } + case HandlerType::INVALID: + llvm_unreachable("Invalid top handler"); + } + + // A vector in which each argument of Result has an entry. If the entry is + // not empty then that argument has been unwrapped using 'guard let' into + // a variable with that name. + SmallVector ArgNames; + ArgNames.reserve(Args.size()); + + /// When unwrapping a result argument \p Arg into a variable using + /// 'guard let' return a suitable name for the unwrapped variable. + /// \p ArgIndex is the index of \p Arg in the results passed to the + /// completion handler. + auto GetSuitableNameForGuardUnwrap = [&](Expr *Arg, + unsigned ArgIndex) -> Identifier { + // If Arg is a DeclRef, use its name for the guard unwrap. + // guard let myVar1 = myVar. + if (auto DRE = dyn_cast(Arg)) { + return createUniqueName(DRE->getDecl()->getBaseIdentifier().str()); + } else if (auto IIOE = dyn_cast(Arg)) { + if (auto DRE = dyn_cast(IIOE->getSubExpr())) { + return createUniqueName(DRE->getDecl()->getBaseIdentifier().str()); + } + } + if (Args.size() == 1) { + // We only have a single result. 'result' seems a resonable name. + return createUniqueName("result"); + } else { + // We are returning a tuple. Name the result elements 'result' + + // index in tuple. + return createUniqueName("result" + std::to_string(ArgIndex)); + } + }; + + unsigned ArgIndex = 0; + for (auto Arg : Args) { + Identifier ArgName; + if (isExpressionOptional(Arg) && TopHandler.HasError) { + ArgName = GetSuitableNameForGuardUnwrap(Arg, ArgIndex); + Scopes.back().Names.insert(ArgName); + OS << tok::kw_guard << ' ' << tok::kw_let << ' ' << ArgName << ' ' + << tok::equal << ' '; + + // If the argument is a call with a trailing closure, the generated + // guard statement will not compile. + // e.g. 'guard let result1 = value.map { $0 + 1 } else { ... }' + // doesn't compile. Adding parentheses makes the code compile. + auto HasTrailingClosure = false; + if (auto *CE = dyn_cast(Arg)) { + if (CE->getUnlabeledTrailingClosureIndex().hasValue()) + HasTrailingClosure = true; + } - ArrayRef Args = Exprs.args(); - if (!Args.empty()) { - OS << " "; - if (Args.size() > 1) - OS << tok::l_paren; - for (size_t I = 0, E = Args.size(); I < E; ++I) { - if (I > 0) - OS << tok::comma << " "; - // Can't just add the range as we need to perform replacements - convertNode(Args[I], /*StartOverride=*/CE->getArgumentLabelLoc(I), + if (HasTrailingClosure) + OS << tok::l_paren; + + convertNode(Arg, /*StartOverride=*/CE->getArgumentLabelLoc(ArgIndex), /*ConvertCalls=*/false); + + if (HasTrailingClosure) + OS << tok::r_paren; + + OS << ' ' << tok::kw_else << ' ' << tok::l_brace << '\n'; + OS << "fatalError" << tok::l_paren; + OS << "\"Expected non-nil result "; + if (ArgName.str() != "result") { + OS << "'" << ArgName << "' "; + } + OS << "in the non-error case\""; + OS << tok::r_paren << '\n'; + OS << tok::r_brace << '\n'; } - if (Args.size() > 1) - OS << tok::r_paren; + ArgNames.push_back(ArgName); + ArgIndex++; } + + Identifier ContName = Scopes.back().ContinuationName; + OS << ContName << tok::period << "resume" << tok::l_paren + << ResumeArgumentLabel << tok::colon << ' '; + + ArgIndex = 0; + addTupleOf(Args, OS, [&](Expr *Elt) { + Identifier ArgName = ArgNames[ArgIndex]; + if (!ArgName.empty()) { + OS << ArgName; + } else { + // Can't just add the range as we need to perform replacements + convertNode(Elt, /*StartOverride=*/CE->getArgumentLabelLoc(ArgIndex), + /*ConvertCalls=*/false); + } + ArgIndex++; + }); + OS << tok::r_paren; } /// From the given expression \p E, which is an argument to a function call, /// extract the passed closure if there is one. Otherwise return \c nullptr. ClosureExpr *extractCallback(Expr *E) { + E = lookThroughFunctionConversionExpr(E); if (auto Closure = dyn_cast(E)) { return Closure; } else if (auto CaptureList = dyn_cast(E)) { return CaptureList->getClosureBody(); - } else if (auto FunctionConversion = dyn_cast(E)) { - // Closure arguments marked as e.g. `@convention(block)` produce arguments - // that are `FunctionConversionExpr`. - return extractCallback(FunctionConversion->getSubExpr()); } else { return nullptr; } } + /// Callback arguments marked as e.g. `@convention(block)` produce arguments + /// that are `FunctionConversionExpr`. + /// We don't care about the conversions and want to shave them off. + Expr *lookThroughFunctionConversionExpr(Expr *E) { + if (auto FunctionConversion = dyn_cast(E)) { + return lookThroughFunctionConversionExpr( + FunctionConversion->getSubExpr()); + } else { + return E; + } + } + void addHoistedCallback(const CallExpr *CE, - const AsyncHandlerDesc &HandlerDesc) { + const AsyncHandlerParamDesc &HandlerDesc) { + llvm::SaveAndRestore RestoreHoisting(Hoisting, true); + auto ArgList = callArgs(CE); - if ((size_t)HandlerDesc.Index >= ArgList.ref().size()) { + if (HandlerDesc.Index >= ArgList.ref().size()) { DiagEngine.diagnose(CE->getStartLoc(), diag::missing_callback_arg); return; } - ClosureExpr *Callback = extractCallback(ArgList.ref()[HandlerDesc.Index]); - if (!Callback) { - DiagEngine.diagnose(CE->getStartLoc(), diag::missing_callback_arg); + Expr *CallbackArg = + lookThroughFunctionConversionExpr(ArgList.ref()[HandlerDesc.Index]); + if (ClosureExpr *Callback = extractCallback(CallbackArg)) { + // The user is using a closure for the completion handler + addHoistedClosureCallback(CE, HandlerDesc, Callback, ArgList); return; } + if (auto CallbackDecl = getReferencedDecl(CallbackArg)) { + if (CallbackDecl == TopHandler.getHandler()) { + // We are refactoring the function that declared the completion handler + // that would be called here. We can't call the completion handler + // anymore because it will be removed. But since the function that + // declared it is being refactored to async, we can just return the + // values. + if (!HandlerDesc.willAsyncReturnVoid()) { + OS << tok::kw_return << " "; + } + InlinePatternsToPrint InlinePatterns; + addAwaitCall(CE, ArgList.ref(), ClassifiedBlock(), {}, InlinePatterns, + HandlerDesc, /*AddDeclarations*/ false); + return; + } + // We are not removing the completion handler, so we can call it once the + // async function returns. + + // The completion handler that is called as part of the \p CE call. + // This will be called once the async function returns. + auto CompletionHandler = + AsyncHandlerDesc::get(CallbackDecl, /*RequireAttributeOrName=*/false); + if (CompletionHandler.isValid()) { + if (auto CalledFunc = getUnderlyingFunc(CE->getFn())) { + StringRef HandlerName = Lexer::getCharSourceRangeFromSourceRange( + SM, CallbackArg->getSourceRange()).str(); + addHoistedNamedCallback( + CalledFunc, CompletionHandler, HandlerName, [&] { + InlinePatternsToPrint InlinePatterns; + addAwaitCall(CE, ArgList.ref(), ClassifiedBlock(), {}, + InlinePatterns, HandlerDesc, + /*AddDeclarations*/ false); + }); + return; + } + } + } + DiagEngine.diagnose(CE->getStartLoc(), diag::missing_callback_arg); + } + /// Add a call to the async alternative of \p CE and convert the \p Callback + /// to be executed after the async call. \p HandlerDesc describes the + /// completion handler in the function that's called by \p CE and \p ArgList + /// are the arguments being passed in \p CE. + void addHoistedClosureCallback(const CallExpr *CE, + const AsyncHandlerParamDesc &HandlerDesc, + const ClosureExpr *Callback, + PtrArrayRef ArgList) { ArrayRef CallbackParams = Callback->getParameters()->getArray(); - ArrayRef CallbackBody = Callback->getBody()->getElements(); + auto CallbackBody = Callback->getBody(); if (HandlerDesc.params().size() != CallbackParams.size()) { DiagEngine.diagnose(CE->getStartLoc(), diag::mismatched_callback_args); return; @@ -5179,8 +7131,8 @@ class AsyncConverter : private SourceEntityWalker { ClassifiedBlocks Blocks; if (!HandlerDesc.HasError) { - Blocks.SuccessBlock.addAllNodes(CallbackBody); - } else if (!CallbackBody.empty()) { + Blocks.SuccessBlock.addNodesInBraceStmt(CallbackBody); + } else if (!CallbackBody->getElements().empty()) { llvm::DenseSet UnwrapParams; for (auto *Param : SuccessParams) { if (HandlerDesc.shouldUnwrap(Param->getType())) @@ -5188,40 +7140,47 @@ class AsyncConverter : private SourceEntityWalker { } if (ErrParam) UnwrapParams.insert(ErrParam); - CallbackClassifier::classifyInto(Blocks, HandledSwitches, DiagEngine, - UnwrapParams, ErrParam, HandlerDesc.Type, - CallbackBody); + CallbackClassifier::classifyInto( + Blocks, HandlerDesc.Func, SuccessParams, HandledSwitches, DiagEngine, + UnwrapParams, ErrParam, HandlerDesc.Type, CallbackBody); } if (DiagEngine.hadAnyError()) { - // Can only fallback when the results are params, in which case only - // the names are used (defaulted to the names of the params if none) - if (HandlerDesc.Type != HandlerType::PARAMS) + // For now, only fallback when the results are params with an error param, + // in which case only the names are used (defaulted to the names of the + // params if none). + if (HandlerDesc.Type != HandlerType::PARAMS || !HandlerDesc.HasError) return; DiagEngine.resetHadAnyError(); + // Note that we don't print any inline patterns here as we just want + // assignments to the names in the outer scope. + InlinePatternsToPrint InlinePatterns; + // Don't do any unwrapping or placeholder replacement since all params // are still valid in the fallback case - prepareNames(ClassifiedBlock(), CallbackParams); + prepareNames(ClassifiedBlock(), CallbackParams, InlinePatterns); addFallbackVars(CallbackParams, Blocks); addDo(); addAwaitCall(CE, ArgList.ref(), Blocks.SuccessBlock, SuccessParams, - HandlerDesc, /*AddDeclarations=*/!HandlerDesc.HasError); + InlinePatterns, HandlerDesc, /*AddDeclarations*/ false); addFallbackCatch(ErrParam); OS << "\n"; - convertNodes(CallbackBody); + convertNodes(NodesToPrint::inBraceStmt(CallbackBody)); clearNames(CallbackParams); return; } - bool RequireDo = !Blocks.ErrorBlock.nodes().empty(); + auto ErrorNodes = Blocks.ErrorBlock.nodesToPrint().getNodes(); + bool RequireDo = !ErrorNodes.empty(); // Check if we *actually* need a do/catch (see class comment) - if (Blocks.ErrorBlock.nodes().size() == 1) { - auto Node = Blocks.ErrorBlock.nodes()[0]; + if (ErrorNodes.size() == 1) { + auto Node = ErrorNodes[0]; if (auto *HandlerCall = TopHandler.getAsHandlerCall(Node)) { - auto Res = TopHandler.extractResultArgs(HandlerCall); + auto Res = TopHandler.extractResultArgs( + HandlerCall, /*ReturnErrorArgsIfAmbiguous=*/true); if (Res.args().size() == 1) { // Skip if we have the param itself or the name it's bound to auto *SingleDecl = Res.args()[0]->getReferencedDecl().getDecl(); @@ -5233,58 +7192,233 @@ class AsyncConverter : private SourceEntityWalker { } } + // If we're not requiring a 'do', we'll be dropping the error block. But + // let's make sure we at least preserve the comments in the error block by + // transplanting them into the success block. This should make sure they + // maintain a sensible ordering. + if (!RequireDo) { + auto ErrorNodes = Blocks.ErrorBlock.nodesToPrint(); + for (auto CommentLoc : ErrorNodes.getPossibleCommentLocs()) + Blocks.SuccessBlock.addPossibleCommentLoc(CommentLoc); + } + if (RequireDo) { addDo(); } - prepareNames(Blocks.SuccessBlock, SuccessParams); + auto InlinePatterns = + getInlinePatternsToPrint(Blocks.SuccessBlock, SuccessParams, Callback); + + prepareNames(Blocks.SuccessBlock, SuccessParams, InlinePatterns); preparePlaceholdersAndUnwraps(HandlerDesc, SuccessParams, ErrParam, /*Success=*/true); addAwaitCall(CE, ArgList.ref(), Blocks.SuccessBlock, SuccessParams, - HandlerDesc, /*AddDeclarations=*/true); - convertNodes(Blocks.SuccessBlock.nodes()); + InlinePatterns, HandlerDesc, /*AddDeclarations=*/true); + printOutOfLineBindingPatterns(Blocks.SuccessBlock, InlinePatterns); + convertNodes(Blocks.SuccessBlock.nodesToPrint()); clearNames(SuccessParams); if (RequireDo) { - // Always use the ErrParam name if none is bound + // We don't use inline patterns for the error path. + InlinePatternsToPrint ErrInlinePatterns; + + // Always use the ErrParam name if none is bound. prepareNames(Blocks.ErrorBlock, llvm::makeArrayRef(ErrParam), - HandlerDesc.Type != HandlerType::RESULT); + ErrInlinePatterns, + /*AddIfMissing=*/HandlerDesc.Type != HandlerType::RESULT); preparePlaceholdersAndUnwraps(HandlerDesc, SuccessParams, ErrParam, /*Success=*/false); addCatch(ErrParam); - convertNodes(Blocks.ErrorBlock.nodes()); + convertNodes(Blocks.ErrorBlock.nodesToPrint()); OS << "\n" << tok::r_brace; clearNames(llvm::makeArrayRef(ErrParam)); } } + /// Add a call to the async alternative of \p FD. Afterwards, pass the results + /// of the async call to the completion handler, named \p HandlerName and + /// described by \p HandlerDesc. + /// \p AddAwaitCall adds the call to the refactored async method to the output + /// stream without storing the result to any variables. + /// This is used when the user didn't use a closure for the callback, but + /// passed in a variable or function name for the completion handler. + void addHoistedNamedCallback(const FuncDecl *FD, + const AsyncHandlerDesc &HandlerDesc, + StringRef HandlerName, + std::function AddAwaitCall) { + if (HandlerDesc.HasError) { + // "result" and "error" always okay to use here since they're added + // in their own scope, which only contains new code. + addDo(); + if (!HandlerDesc.willAsyncReturnVoid()) { + OS << tok::kw_let << " result"; + addResultTypeAnnotationIfNecessary(FD, HandlerDesc); + OS << " " << tok::equal << " "; + } + AddAwaitCall(); + OS << "\n"; + addCallToCompletionHandler("result", HandlerDesc, HandlerName); + OS << "\n"; + OS << tok::r_brace << " " << tok::kw_catch << " " << tok::l_brace << "\n"; + addCallToCompletionHandler(StringRef(), HandlerDesc, HandlerName); + OS << "\n" << tok::r_brace; // end catch + } else { + // This code may be placed into an existing scope, in that case create + // a unique "result" name so that it doesn't cause shadowing or redecls. + StringRef ResultName; + if (!HandlerDesc.willAsyncReturnVoid()) { + Identifier Unique = createUniqueName("result"); + Scopes.back().Names.insert(Unique); + ResultName = Unique.str(); + + OS << tok::kw_let << " " << ResultName; + addResultTypeAnnotationIfNecessary(FD, HandlerDesc); + OS << " " << tok::equal << " "; + } else { + // The name won't end up being used, just give it a bogus one so that + // the result path is taken (versus the error path). + ResultName = "result"; + } + AddAwaitCall(); + OS << "\n"; + addCallToCompletionHandler(ResultName, HandlerDesc, HandlerName); + } + } + + /// Checks whether a binding pattern for a given decl can be printed inline in + /// an await call, e.g 'let ((x, y), z) = await foo()', where '(x, y)' is the + /// inline pattern. + const Pattern * + bindingPatternToPrintInline(const Decl *D, const ClassifiedBlock &Block, + const ClosureExpr *CallbackClosure) { + // Only currently done for callback closures. + if (!CallbackClosure) + return nullptr; + + // If we can reduce the pattern bindings down to a single pattern, we may + // be able to print it inline. + auto *P = Block.getSinglePatternFor(D); + if (!P) + return nullptr; + + // Patterns that bind a single var are always printed inline. + if (P->getSingleVar()) + return P; + + // If we have a multi-var binding, and the decl being bound is referenced + // elsewhere in the block, we cannot print the pattern immediately in the + // await call. Instead, we'll print it out of line. + auto *Decls = ScopedDecls.getReferencedDecls(CallbackClosure->getBody()); + assert(Decls); + auto NumRefs = Decls->lookup(D); + return NumRefs == 1 ? P : nullptr; + } + + /// Retrieve a map of patterns to print inline for an array of param decls. + InlinePatternsToPrint + getInlinePatternsToPrint(const ClassifiedBlock &Block, + ArrayRef Params, + const ClosureExpr *CallbackClosure) { + InlinePatternsToPrint Patterns; + for (auto *Param : Params) { + if (auto *P = bindingPatternToPrintInline(Param, Block, CallbackClosure)) + Patterns[Param] = P; + } + return Patterns; + } + + /// Print any out of line binding patterns that could not be printed as inline + /// patterns. These typically appear directly after an await call, e.g: + /// \code + /// let x = await foo() + /// let (y, z) = x + /// \endcode + void + printOutOfLineBindingPatterns(const ClassifiedBlock &Block, + const InlinePatternsToPrint &InlinePatterns) { + for (auto &Entry : Block.paramPatternBindings()) { + auto *D = Entry.first; + auto Aliases = Block.getAliasesFor(D); + + for (auto *P : Entry.second) { + // If we already printed this as an inline pattern, there's nothing else + // to do. + if (InlinePatterns.lookup(D) == P) + continue; + + // If this is an alias binding, it can be elided. + if (auto *SingleVar = P->getSingleVar()) { + if (Aliases.contains(SingleVar)) + continue; + } + + auto HasMutable = P->hasAnyMutableBindings(); + OS << "\n" << (HasMutable ? tok::kw_var : tok::kw_let) << " "; + convertPattern(P); + OS << " = "; + OS << newNameFor(D); + } + } + } + + /// Prints an \c await call to an \c async function, binding any return values + /// into variables. + /// + /// \param CE The call expr to convert. + /// \param Args The arguments of the call expr. + /// \param SuccessBlock The nodes present in the success block following the + /// call. + /// \param SuccessParams The success parameters, which will be printed as + /// return values. + /// \param InlinePatterns A map of patterns that can be printed inline for + /// a given param. + /// \param HandlerDesc A description of the completion handler. + /// \param AddDeclarations Whether or not to add \c let or \c var keywords to + /// the return value bindings. void addAwaitCall(const CallExpr *CE, ArrayRef Args, const ClassifiedBlock &SuccessBlock, ArrayRef SuccessParams, - const AsyncHandlerDesc &HandlerDesc, bool AddDeclarations) { + const InlinePatternsToPrint &InlinePatterns, + const AsyncHandlerParamDesc &HandlerDesc, + bool AddDeclarations) { // Print the bindings to match the completion handler success parameters, // making sure to omit in the case of a Void return. if (!SuccessParams.empty() && !HandlerDesc.willAsyncReturnVoid()) { + auto AllLet = true; + + // Gather the items to print for the variable bindings. This can either be + // a param decl, or a pattern that binds it. + using DeclOrPattern = llvm::PointerUnion; + SmallVector ToPrint; + for (auto *Param : SuccessParams) { + // Check if we have an inline pattern to print. + if (auto *P = InlinePatterns.lookup(Param)) { + if (P->hasAnyMutableBindings()) + AllLet = false; + ToPrint.push_back(P); + continue; + } + ToPrint.push_back(Param); + } + if (AddDeclarations) { - if (SuccessBlock.allLet()) { + if (AllLet) { OS << tok::kw_let; } else { OS << tok::kw_var; } OS << " "; } - if (SuccessParams.size() > 1) - OS << tok::l_paren; - OS << newNameFor(SuccessParams.front()); - for (const auto Param : SuccessParams.drop_front()) { - OS << tok::comma << " "; - OS << newNameFor(Param); - } - if (SuccessParams.size() > 1) { - OS << tok::r_paren; - } + // 'res =' or '(res1, res2, ...) =' + addTupleOf(ToPrint, OS, [&](DeclOrPattern Elt) { + if (auto *P = Elt.dyn_cast()) { + convertPattern(P); + return; + } + OS << newNameFor(Elt.get()); + }); OS << " " << tok::equal << " "; } @@ -5292,23 +7426,74 @@ class AsyncConverter : private SourceEntityWalker { OS << tok::kw_try << " "; } OS << "await "; - addRange(CE->getStartLoc(), CE->getFn()->getEndLoc(), - /*ToEndOfToken=*/true); - OS << tok::l_paren; - size_t realArgCount = 0; - for (size_t I = 0, E = Args.size() - 1; I < E; ++I) { - if (isa(Args[I])) + // Try to replace the name with that of the alternative. Use the existing + // name if for some reason that's not possible. + bool NameAdded = false; + if (HandlerDesc.Alternative) { + const ValueDecl *Named = HandlerDesc.Alternative; + if (auto *Accessor = dyn_cast(HandlerDesc.Alternative)) + Named = Accessor->getStorage(); + if (!Named->getBaseName().isSpecial()) { + Names.try_emplace(HandlerDesc.Func, + Named->getBaseName().getIdentifier()); + convertNode(CE->getFn(), /*StartOverride=*/{}, /*ConvertCalls=*/false, + /*IncludeComments=*/false); + NameAdded = true; + } + } + if (!NameAdded) { + addRange(CE->getStartLoc(), CE->getFn()->getEndLoc(), + /*ToEndOfToken=*/true); + } + + if (!HandlerDesc.alternativeIsAccessor()) + OS << tok::l_paren; + + size_t ConvertedArgIndex = 0; + ArrayRef AlternativeParams; + if (HandlerDesc.Alternative) + AlternativeParams = HandlerDesc.Alternative->getParameters()->getArray(); + ArrayRef ArgLabels = CE->getArgumentLabels(); + for (size_t I = 0, E = Args.size(); I < E; ++I) { + if (I == HandlerDesc.Index || isa(Args[I])) continue; - if (realArgCount > 0) + if (ConvertedArgIndex > 0) OS << tok::comma << " "; - // Can't just add the range as we need to perform replacements + + if (HandlerDesc.Alternative) { + // Skip argument if it's defaulted and has a different name + while (ConvertedArgIndex < AlternativeParams.size() && + AlternativeParams[ConvertedArgIndex]->isDefaultArgument() && + AlternativeParams[ConvertedArgIndex]->getArgumentName() != + ArgLabels[I]) { + ConvertedArgIndex++; + } + + if (ConvertedArgIndex < AlternativeParams.size()) { + // Could have a different argument label (or none), so add it instead + auto Name = AlternativeParams[ConvertedArgIndex]->getArgumentName(); + if (!Name.empty()) + OS << Name << ": "; + convertNode(Args[I], /*StartOverride=*/{}, /*ConvertCalls=*/false); + + ConvertedArgIndex++; + continue; + } + + // Fallthrough if arguments don't match up for some reason + } + + // Can't just add the range as we need to perform replacements. Also + // make sure to include the argument label (if any) convertNode(Args[I], /*StartOverride=*/CE->getArgumentLabelLoc(I), /*ConvertCalls=*/false); - realArgCount++; + ConvertedArgIndex++; } - OS << tok::r_paren; + + if (!HandlerDesc.alternativeIsAccessor()) + OS << tok::r_paren; } void addFallbackCatch(const ParamDecl *ErrParam) { @@ -5322,7 +7507,7 @@ class AsyncConverter : private SourceEntityWalker { void addCatch(const ParamDecl *ErrParam) { OS << "\n" << tok::r_brace << " " << tok::kw_catch << " "; auto ErrName = newNameFor(ErrParam, false); - if (!ErrName.empty()) { + if (!ErrName.empty() && ErrName != "_") { OS << tok::kw_let << " " << ErrName << " "; } OS << tok::l_brace; @@ -5373,28 +7558,82 @@ class AsyncConverter : private SourceEntityWalker { } } - // TODO: Check for clashes with existing names and add all decls, not just - // params + /// Add a mapping from each passed parameter to a new name, possibly + /// synthesizing a new one if hoisting it would cause a redeclaration or + /// shadowing. If there's no bound name and \c AddIfMissing is false, no + /// name will be added. void prepareNames(const ClassifiedBlock &Block, ArrayRef Params, + const InlinePatternsToPrint &InlinePatterns, bool AddIfMissing = true) { - for (auto *Param : Params) { - StringRef Name = Block.boundName(Param); - if (!Name.empty()) { - Names[Param] = Name.str(); - continue; + for (auto *PD : Params) { + // If this param is to be replaced by a pattern that binds multiple + // separate vars, it's not actually going to be added to the scope, and + // therefore doesn't need naming. This avoids needing to rename a var with + // the same name later on in the scope, as it's not actually clashing. + if (auto *P = InlinePatterns.lookup(PD)) { + if (!P->getSingleVar()) + continue; } - - if (!AddIfMissing) + auto Name = Block.boundName(PD); + if (Name.empty() && !AddIfMissing) continue; - auto ParamName = Param->getNameStr(); - if (ParamName.startswith("$")) { - Names[Param] = "val" + ParamName.drop_front().str(); - } else { - Names[Param] = ParamName.str(); - } + auto Ident = assignUniqueName(PD, Name); + + // Also propagate the name to any aliases. + for (auto *Alias : Block.getAliasesFor(PD)) + Names[Alias] = Ident; + } + } + + /// Returns a unique name using \c Name as base that doesn't clash with any + /// other names in the current scope. + Identifier createUniqueName(StringRef Name) { + Identifier Ident = getASTContext().getIdentifier(Name); + if (Name == "_") + return Ident; + + auto &CurrentNames = Scopes.back().Names; + if (CurrentNames.count(Ident)) { + // Add a number to the end of the name until it's unique given the current + // names in scope. + llvm::SmallString<32> UniquedName; + unsigned UniqueId = 1; + do { + UniquedName = Name; + UniquedName.append(std::to_string(UniqueId)); + Ident = getASTContext().getIdentifier(UniquedName); + UniqueId++; + } while (CurrentNames.count(Ident)); + } + return Ident; + } + + /// Create a unique name for the variable declared by \p D that doesn't + /// clash with any other names in scope, using \p BoundName as the base name + /// if not empty and the name of \p D otherwise. Adds this name to both + /// \c Names and the current scope's names (\c Scopes.Names). + Identifier assignUniqueName(const Decl *D, StringRef BoundName) { + Identifier Ident; + if (BoundName.empty()) { + BoundName = getDeclName(D).userFacingName(); + if (BoundName.empty()) + return Ident; + } + + if (BoundName.startswith("$")) { + llvm::SmallString<8> NewName; + NewName.append("val"); + NewName.append(BoundName.drop_front()); + Ident = createUniqueName(NewName); + } else { + Ident = createUniqueName(BoundName); } + + Names.try_emplace(D, Ident); + Scopes.back().Names.insert(Ident); + return Ident; } StringRef newNameFor(const Decl *D, bool Required = true) { @@ -5403,7 +7642,23 @@ class AsyncConverter : private SourceEntityWalker { assert(!Required && "Missing name for decl when one was required"); return StringRef(); } - return StringRef(Res->second); + return Res->second.str(); + } + + void addNewScope(const llvm::DenseSet &Decls) { + if (Scopes.empty()) { + Scopes.emplace_back(/*ContinuationName=*/Identifier()); + } else { + // If the parent scope is nested in a continuation, the new one is also. + // Carry over the continuation name. + Identifier PreviousContinuationName = Scopes.back().ContinuationName; + Scopes.emplace_back(PreviousContinuationName); + } + for (auto D : Decls) { + auto Name = getDeclName(D); + if (!Name.empty()) + Scopes.back().Names.insert(Name); + } } void clearNames(ArrayRef Params) { @@ -5413,89 +7668,119 @@ class AsyncConverter : private SourceEntityWalker { Names.erase(Param); } } -}; -/// When adding an async alternative method for the function declaration \c FD, -/// this class tries to create a function body for the legacy function (the one -/// with a completion handler), which calls the newly converted async function. -/// There are certain situations in which we fail to create such a body, e.g. -/// if the completion handler has the signature `(String, Error?) -> Void` in -/// which case we can't synthesize the result of type \c String in the error -/// case. -class LegacyAlternativeBodyCreator { - /// The old function declaration for which an async alternative has been added - /// and whose body shall be rewritten to call the newly added async - /// alternative. - FuncDecl *FD; - - /// The description of the completion handler in the old function declaration. - AsyncHandlerDesc HandlerDesc; + /// Adds a forwarding call to the old completion handler function, with + /// \p HandlerReplacement that allows for a custom replacement or, if empty, + /// removal of the completion handler closure. + void addForwardingCallTo(const FuncDecl *FD, StringRef HandlerReplacement) { + OS << FD->getBaseName() << tok::l_paren; - std::string Buffer; - llvm::raw_string_ostream OS; - - /// Adds the call to the refactored 'async' method without the 'await' - /// keyword to the output stream. - void addCallToAsyncMethod() { - OS << FD->getBaseName() << "("; - bool FirstParam = true; - for (auto Param : *FD->getParameters()) { - if (Param == HandlerDesc.Handler) { - /// We don't need to pass the completion handler to the async method. - continue; + auto *Params = FD->getParameters(); + size_t ConvertedArgsIndex = 0; + for (size_t I = 0, E = Params->size(); I < E; ++I) { + if (I == TopHandler.Index) { + /// If we're not replacing the handler with anything, drop it. + if (HandlerReplacement.empty()) + continue; + + // Use a trailing closure if the handler is the last param + if (I == E - 1) { + OS << tok::r_paren << " "; + OS << HandlerReplacement; + return; + } + + // Otherwise fall through to do the replacement. } - if (!FirstParam) { - OS << ", "; + + if (ConvertedArgsIndex > 0) + OS << tok::comma << " "; + + const auto *Param = Params->get(I); + if (!Param->getArgumentName().empty()) + OS << Param->getArgumentName() << tok::colon << " "; + + if (I == TopHandler.Index) { + OS << HandlerReplacement; } else { - FirstParam = false; - } - if (!Param->getArgumentName().empty()) { - OS << Param->getArgumentName() << ": "; + OS << Param->getParameterName(); } - OS << Param->getParameterName(); + + ConvertedArgsIndex++; } - OS << ")"; + OS << tok::r_paren; } - /// If the returned error type is more specialized than \c Error, adds an - /// 'as! CustomError' cast to the more specialized error type to the output - /// stream. - void addCastToCustomErrorTypeIfNecessary() { + /// Adds a forwarded error argument to a completion handler call. If the error + /// type of \p HandlerDesc is more specialized than \c Error, an + /// 'as! CustomError' cast to the more specialized error type will be added to + /// the output stream. + void addForwardedErrorArgument(StringRef ErrorName, + const AsyncHandlerDesc &HandlerDesc) { + // If the error type is already Error, we can pass it as-is. auto ErrorType = *HandlerDesc.getErrorType(); - if (ErrorType->getCanonicalType() != - FD->getASTContext().getExceptionType()) { - OS << " as! "; - ErrorType->lookThroughSingleOptionalType()->print(OS); - } - } - - /// Adds the \c Index -th parameter to the completion handler. - /// If \p HasResult is \c true, it is assumed that a variable named 'result' - /// contains the result returned from the async alternative. If the callback - /// also takes an error parameter, \c nil passed to the completion handler for - /// the error. - /// If \p HasResult is \c false, it is a assumed that a variable named 'error' - /// contains the error thrown from the async method and 'nil' will be passed - /// to the completion handler for all result parameters. - void addCompletionHandlerArgument(size_t Index, bool HasResult) { + if (ErrorType->getCanonicalType() == getASTContext().getExceptionType()) { + OS << ErrorName; + return; + } + + // Otherwise we need to add a force cast to the destination custom error + // type. If this is for an Error? parameter, we'll need to add parens around + // the cast to silence a compiler warning about force casting never + // producing nil. + auto RequiresParens = HandlerDesc.getErrorParam().hasValue(); + if (RequiresParens) + OS << tok::l_paren; + + OS << ErrorName << " " << tok::kw_as << tok::exclaim_postfix << " "; + ErrorType->lookThroughSingleOptionalType()->print(OS); + + if (RequiresParens) + OS << tok::r_paren; + } + + /// If \p T has a natural default value like \c nil for \c Optional or \c () + /// for \c Void, add that default value to the output. Otherwise, add a + /// placeholder that contains \p T's name as the hint. + void addDefaultValueOrPlaceholder(Type T) { + if (T->isOptional()) { + OS << tok::kw_nil; + } else if (T->isVoid()) { + OS << "()"; + } else { + OS << "<#"; + T.print(OS); + OS << "#>"; + } + } + + /// Adds the \c Index -th parameter to the completion handler described by \p + /// HanderDesc. + /// If \p ResultName is not empty, it is assumed that a variable with that + /// name contains the result returned from the async alternative. If the + /// callback also takes an error parameter, \c nil passed to the completion + /// handler for the error. If \p ResultName is empty, it is a assumed that a + /// variable named 'error' contains the error thrown from the async method and + /// 'nil' will be passed to the completion handler for all result parameters. + void addCompletionHandlerArgument(size_t Index, StringRef ResultName, + const AsyncHandlerDesc &HandlerDesc) { if (HandlerDesc.HasError && Index == HandlerDesc.params().size() - 1) { // The error parameter is the last argument of the completion handler. - if (!HasResult) { - OS << "error"; - addCastToCustomErrorTypeIfNecessary(); + if (ResultName.empty()) { + addForwardedErrorArgument("error", HandlerDesc); } else { - OS << "nil"; + addDefaultValueOrPlaceholder(HandlerDesc.params()[Index].getPlainType()); } } else { - if (!HasResult) { - OS << "nil"; + if (ResultName.empty()) { + addDefaultValueOrPlaceholder(HandlerDesc.params()[Index].getPlainType()); } else if (HandlerDesc .getSuccessParamAsyncReturnType( HandlerDesc.params()[Index].getPlainType()) ->isVoid()) { // Void return types are not returned by the async function, synthesize // a Void instance. - OS << "()"; + OS << tok::l_paren << tok::r_paren; } else if (HandlerDesc.getSuccessParams().size() > 1) { // If the async method returns a tuple, we need to pass its elements to // the completion handler separately. For example: @@ -5505,23 +7790,32 @@ class LegacyAlternativeBodyCreator { // causes the following legacy body to be created: // // func foo(completion: (String, Int) -> Void) { - // async { + // Task { // let result = await foo() // completion(result.0, result.1) // } // } - OS << "result." << Index; + OS << ResultName << tok::period; + + auto Label = HandlerDesc.getAsyncReturnTypeLabel(Index); + if (!Label.empty()) { + OS << Label; + } else { + OS << Index; + } } else { - OS << "result"; + OS << ResultName; } } } - /// Adds the call to the completion handler. See \c - /// getCompletionHandlerArgument for how the arguments are synthesized if the - /// completion handler takes arguments, not a \c Result type. - void addCallToCompletionHandler(bool HasResult) { - OS << HandlerDesc.Handler->getParameterName() << "("; + /// Add a call to the completion handler named \p HandlerName and described by + /// \p HandlerDesc, passing all the required arguments. See \c + /// getCompletionHandlerArgument for how the arguments are synthesized. + void addCallToCompletionHandler(StringRef ResultName, + const AsyncHandlerDesc &HandlerDesc, + StringRef HandlerName) { + OS << HandlerName << tok::l_paren; // Construct arguments to pass to the completion handler switch (HandlerDesc.Type) { @@ -5531,53 +7825,61 @@ class LegacyAlternativeBodyCreator { case HandlerType::PARAMS: { for (size_t I = 0; I < HandlerDesc.params().size(); ++I) { if (I > 0) { - OS << ", "; + OS << tok::comma << " "; } - addCompletionHandlerArgument(I, HasResult); + addCompletionHandlerArgument(I, ResultName, HandlerDesc); } break; } case HandlerType::RESULT: { - if (HasResult) { - OS << ".success(result)"; + if (!ResultName.empty()) { + OS << tok::period_prefix << "success" << tok::l_paren; + if (!HandlerDesc.willAsyncReturnVoid()) { + OS << ResultName; + } else { + OS << tok::l_paren << tok::r_paren; + } + OS << tok::r_paren; } else { - OS << ".failure(error"; - addCastToCustomErrorTypeIfNecessary(); - OS << ")"; + OS << tok::period_prefix << "failure" << tok::l_paren; + addForwardedErrorArgument("error", HandlerDesc); + OS << tok::r_paren; } break; } } - OS << ")"; // Close the call to the completion handler + OS << tok::r_paren; // Close the call to the completion handler } - /// Adds the result type of the converted async function. - void addAsyncFuncReturnType() { - SmallVector Scratch; + /// Adds the result type of a refactored async function that previously + /// returned results via a completion handler described by \p HandlerDesc. + void addAsyncFuncReturnType(const AsyncHandlerDesc &HandlerDesc) { + // Type or (Type1, Type2, ...) + SmallVector Scratch; auto ReturnTypes = HandlerDesc.getAsyncReturnTypes(Scratch); - if (ReturnTypes.size() > 1) { - OS << "("; - } - - llvm::interleave( - ReturnTypes, [&](Type Ty) { Ty->print(OS); }, [&]() { OS << ", "; }); - - if (ReturnTypes.size() > 1) { - OS << ")"; + if (ReturnTypes.empty()) { + OS << "Void"; + } else { + addTupleOf(ReturnTypes, OS, [&](LabeledReturnType LabelAndType) { + if (!LabelAndType.Label.empty()) { + OS << LabelAndType.Label << tok::colon << " "; + } + LabelAndType.Ty->print(OS); + }); } } - /// If the async alternative function is generic, adds the type annotation - /// to the 'return' variable in the legacy function so that the generic - /// parameters of the legacy function are passed to the generic function. - /// For example for + /// If \p FD is generic, adds a type annotation with the return type of the + /// converted async function. This is used when creating a legacy function, + /// calling the converted 'async' function so that the generic parameters of + /// the legacy function are passed to the generic function. For example for /// \code /// func foo() async -> GenericParam {} /// \endcode /// we generate /// \code - /// func foo(completion: (T) -> Void) { - /// async { + /// func foo(completion: (GenericParam) -> Void) { + /// Task { /// let result: GenericParam = await foo() /// <------------> /// completion(result) @@ -5585,88 +7887,12 @@ class LegacyAlternativeBodyCreator { /// } /// \endcode /// This function adds the range marked by \c <-----> - void addResultTypeAnnotationIfNecessary() { + void addResultTypeAnnotationIfNecessary(const FuncDecl *FD, + const AsyncHandlerDesc &HandlerDesc) { if (FD->isGeneric()) { - OS << ": "; - addAsyncFuncReturnType(); - } - } - -public: - LegacyAlternativeBodyCreator(FuncDecl *FD, AsyncHandlerDesc HandlerDesc) - : FD(FD), HandlerDesc(HandlerDesc), OS(Buffer) {} - - bool canRewriteLegacyBody() { - if (FD == nullptr || FD->getBody() == nullptr) { - return false; - } - if (FD->hasThrows()) { - assert(!HandlerDesc.isValid() && "We shouldn't have found a handler desc " - "if the original function throws"); - return false; - } - switch (HandlerDesc.Type) { - case HandlerType::INVALID: - return false; - case HandlerType::PARAMS: { - if (HandlerDesc.HasError) { - // The non-error parameters must be optional so that we can set them to - // nil in the error case. - // The error parameter must be optional so we can set it to nil in the - // success case. - // Otherwise we can't synthesize the values to return for these - // parameters. - return llvm::all_of(HandlerDesc.params(), - [](AnyFunctionType::Param Param) -> bool { - return Param.getPlainType()->isOptional(); - }); - } else { - return true; - } - } - case HandlerType::RESULT: - return true; - } - } - - std::string create() { - assert(Buffer.empty() && - "LegacyAlternativeBodyCreator can only be used once"); - assert(canRewriteLegacyBody() && - "Cannot create a legacy body if the body can't be rewritten"); - OS << "{\n"; // start function body - OS << "async {\n"; - if (HandlerDesc.HasError) { - OS << "do {\n"; - if (!HandlerDesc.willAsyncReturnVoid()) { - OS << "let result"; - addResultTypeAnnotationIfNecessary(); - OS << " = "; - } - OS << "try await "; - addCallToAsyncMethod(); - OS << "\n"; - addCallToCompletionHandler(/*HasResult=*/true); - OS << "\n" - << "} catch {\n"; - addCallToCompletionHandler(/*HasResult=*/false); - OS << "\n" - << "}\n"; // end catch - } else { - if (!HandlerDesc.willAsyncReturnVoid()) { - OS << "let result"; - addResultTypeAnnotationIfNecessary(); - OS << " = "; - } - OS << "await "; - addCallToAsyncMethod(); - OS << "\n"; - addCallToCompletionHandler(/*HasResult=*/true); - OS << "\n"; + OS << tok::colon << " "; + addAsyncFuncReturnType(HandlerDesc); } - OS << "}\n"; // end 'async' - OS << "}\n"; // end function body - return Buffer; } }; @@ -5683,8 +7909,8 @@ bool RefactoringActionConvertCallToAsyncAlternative::isApplicable( if (!CE) return false; - auto HandlerDesc = AsyncHandlerDesc::find(getUnderlyingFunc(CE->getFn()), - /*ignoreName=*/true); + auto HandlerDesc = AsyncHandlerParamDesc::find( + getUnderlyingFunc(CE->getFn()), /*RequireAttributeOrName=*/false); return HandlerDesc.isValid(); } @@ -5703,8 +7929,18 @@ bool RefactoringActionConvertCallToAsyncAlternative::performChange() { assert(CE && "Should not run performChange when refactoring is not applicable"); - AsyncHandlerDesc TempDesc; - AsyncConverter Converter(SM, DiagEngine, CE, TempDesc); + // Find the scope this call is in + ContextFinder Finder(*CursorInfo.SF, CursorInfo.Loc, + [](ASTNode N) { + return N.isStmt(StmtKind::Brace) && !N.isImplicit(); + }); + Finder.resolve(); + auto Scopes = Finder.getContexts(); + BraceStmt *Scope = nullptr; + if (!Scopes.empty()) + Scope = cast(Scopes.back().get()); + + AsyncConverter Converter(TheFile, SM, DiagEngine, CE, Scope); if (!Converter.convert()) return true; @@ -5731,8 +7967,9 @@ bool RefactoringActionConvertToAsync::performChange() { assert(FD && "Should not run performChange when refactoring is not applicable"); - auto HandlerDesc = AsyncHandlerDesc::find(FD, /*ignoreName=*/true); - AsyncConverter Converter(SM, DiagEngine, FD, HandlerDesc); + auto HandlerDesc = + AsyncHandlerParamDesc::find(FD, /*RequireAttributeOrName=*/false); + AsyncConverter Converter(TheFile, SM, DiagEngine, FD, HandlerDesc); if (!Converter.convert()) return true; @@ -5748,7 +7985,8 @@ bool RefactoringActionAddAsyncAlternative::isApplicable( if (!FD) return false; - auto HandlerDesc = AsyncHandlerDesc::find(FD, /*ignoreName=*/true); + auto HandlerDesc = + AsyncHandlerParamDesc::find(FD, /*RequireAttributeOrName=*/false); return HandlerDesc.isValid(); } @@ -5765,28 +8003,73 @@ bool RefactoringActionAddAsyncAlternative::performChange() { assert(FD && "Should not run performChange when refactoring is not applicable"); - auto HandlerDesc = AsyncHandlerDesc::find(FD, /*ignoreName=*/true); + auto HandlerDesc = + AsyncHandlerParamDesc::find(FD, /*RequireAttributeOrName=*/false); assert(HandlerDesc.isValid() && "Should not run performChange when refactoring is not applicable"); - AsyncConverter Converter(SM, DiagEngine, FD, HandlerDesc); + AsyncConverter Converter(TheFile, SM, DiagEngine, FD, HandlerDesc); if (!Converter.convert()) return true; + // Add a reference to the async function so that warnings appear when the + // synchronous function is used in an async context + SmallString<128> AvailabilityAttr = HandlerDesc.buildRenamedAttribute(); EditConsumer.accept(SM, FD->getAttributeInsertionLoc(false), - "@available(*, deprecated, message: \"Prefer async " - "alternative instead\")\n"); - LegacyAlternativeBodyCreator LegacyBody(FD, HandlerDesc); - if (LegacyBody.canRewriteLegacyBody()) { - EditConsumer.accept(SM, - Lexer::getCharSourceRangeFromSourceRange( - SM, FD->getBody()->getSourceRange()), - LegacyBody.create()); + AvailabilityAttr); + + AsyncConverter LegacyBodyCreator(TheFile, SM, DiagEngine, FD, HandlerDesc); + if (LegacyBodyCreator.createLegacyBody()) { + LegacyBodyCreator.replace(FD->getBody(), EditConsumer); } + + // Add the async alternative + Converter.insertAfter(FD, EditConsumer); + + return false; +} + +bool RefactoringActionAddAsyncWrapper::isApplicable( + const ResolvedCursorInfo &CursorInfo, DiagnosticEngine &Diag) { + using namespace asyncrefactorings; + + auto *FD = findFunction(CursorInfo); + if (!FD) + return false; + + auto HandlerDesc = + AsyncHandlerParamDesc::find(FD, /*RequireAttributeOrName=*/false); + return HandlerDesc.isValid(); +} + +bool RefactoringActionAddAsyncWrapper::performChange() { + using namespace asyncrefactorings; + + auto *FD = findFunction(CursorInfo); + assert(FD && + "Should not run performChange when refactoring is not applicable"); + + auto HandlerDesc = + AsyncHandlerParamDesc::find(FD, /*RequireAttributeOrName=*/false); + assert(HandlerDesc.isValid() && + "Should not run performChange when refactoring is not applicable"); + + AsyncConverter Converter(TheFile, SM, DiagEngine, FD, HandlerDesc); + if (!Converter.createAsyncWrapper()) + return true; + + // Add a reference to the async function so that warnings appear when the + // synchronous function is used in an async context + SmallString<128> AvailabilityAttr = HandlerDesc.buildRenamedAttribute(); + EditConsumer.accept(SM, FD->getAttributeInsertionLoc(false), + AvailabilityAttr); + + // Add the async wrapper. Converter.insertAfter(FD, EditConsumer); return false; } + } // end of anonymous namespace StringRef swift::ide:: @@ -5988,7 +8271,7 @@ void swift::ide::collectAvailableRefactorings( } void swift::ide::collectAvailableRefactorings( - SourceFile *SF, RangeConfig Range, bool &RangeStartMayNeedRename, + SourceFile *SF, RangeConfig Range, bool &CollectRangeStartRefactorings, SmallVectorImpl &Kinds, ArrayRef DiagConsumers) { if (Range.Length == 0) { @@ -6017,7 +8300,7 @@ void swift::ide::collectAvailableRefactorings( RANGE_REFACTORING(KIND, NAME, ID) #include "swift/IDE/RefactoringKinds.def" - RangeStartMayNeedRename = rangeStartMayNeedRename(Result); + CollectRangeStartRefactorings = collectRangeStartRefactorings(Result); } bool swift::ide:: diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index e644fed26b3c7..56a1781b72650 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -66,6 +66,7 @@ class SemaAnnotator : public ASTWalker { Stmt *walkToStmtPost(Stmt *S) override; std::pair walkToPatternPre(Pattern *P) override; + Pattern *walkToPatternPost(Pattern *P) override; bool handleImports(ImportDecl *Import); bool handleCustomAttributes(Decl *D); @@ -124,11 +125,15 @@ bool SemaAnnotator::walkToDeclPre(Decl *D) { bool IsExtension = false; if (auto *VD = dyn_cast(D)) { - if (VD->hasName() && !VD->isImplicit()) { + if (!VD->isImplicit()) { SourceManager &SM = VD->getASTContext().SourceMgr; - NameLen = VD->getBaseName().userFacingName().size(); - if (Loc.isValid() && SM.extractText({Loc, 1}) == "`") - NameLen += 2; + if (VD->hasName()) { + NameLen = VD->getBaseName().userFacingName().size(); + if (Loc.isValid() && SM.extractText({Loc, 1}) == "`") + NameLen += 2; + } else if (Loc.isValid() && SM.extractText({Loc, 1}) == "_") { + NameLen = 1; + } } auto ReportParamList = [&](ParameterList *PL) { @@ -270,6 +275,21 @@ std::pair SemaAnnotator::walkToExprPre(Expr *E) { return { false, E }; } + auto doStopTraversal = [&]() -> std::pair { + Cancelled = true; + return { false, nullptr }; + }; + + // Skip the synthesized curry thunks and just walk over the unwrapped + // expression + if (auto *ACE = dyn_cast(E)) { + if (auto *SubExpr = ACE->getUnwrappedCurryThunkExpr()) { + if (!SubExpr->walk(*this)) + return doStopTraversal(); + return { false, E }; + } + } + if (!SEWalker.walkToExprPre(E)) { return { false, E }; } @@ -286,30 +306,9 @@ std::pair SemaAnnotator::walkToExprPre(Expr *E) { return { false, E }; }; - auto doStopTraversal = [&]() -> std::pair { - Cancelled = true; - return { false, nullptr }; - }; - if (auto *CtorRefE = dyn_cast(E)) CtorRefs.push_back(CtorRefE); - if (auto *ACE = dyn_cast(E)) { - if (auto *SubExpr = ACE->getUnwrappedCurryThunkExpr()) { - if (auto *DRE = dyn_cast(SubExpr)) { - if (!passReference(DRE->getDecl(), DRE->getType(), - DRE->getNameLoc(), - ReferenceMetaData(getReferenceKind(Parent.getAsExpr(), DRE), - OpAccess))) - return doStopTraversal(); - - return doSkipChildren(); - } - } - - return { true, E }; - } - if (auto *DRE = dyn_cast(E)) { auto *FD = dyn_cast(DRE->getDecl()); // Handle implicit callAsFunction reference. An explicit reference will be @@ -434,16 +433,17 @@ std::pair SemaAnnotator::walkToExprPre(Expr *E) { case KeyPathExpr::Component::Kind::OptionalForce: case KeyPathExpr::Component::Kind::Identity: case KeyPathExpr::Component::Kind::DictionaryKey: + case KeyPathExpr::Component::Kind::CodeCompletion: break; } } } else if (auto *BinE = dyn_cast(E)) { // Visit in source order. - if (!BinE->getArg()->getElement(0)->walk(*this)) + if (!BinE->getLHS()->walk(*this)) return doStopTraversal(); if (!BinE->getFn()->walk(*this)) return doStopTraversal(); - if (!BinE->getArg()->getElement(1)->walk(*this)) + if (!BinE->getRHS()->walk(*this)) return doStopTraversal(); // We already visited the children. @@ -587,6 +587,9 @@ std::pair SemaAnnotator::walkToPatternPre(Pattern *P) { return { false, nullptr }; } + if (!SEWalker.walkToPatternPre(P)) + return { false, P }; + if (P->isImplicit()) return { true, P }; @@ -609,6 +612,16 @@ std::pair SemaAnnotator::walkToPatternPre(Pattern *P) { return { false, P }; } +Pattern *SemaAnnotator::walkToPatternPost(Pattern *P) { + if (isDone()) + return nullptr; + + bool Continue = SEWalker.walkToPatternPost(P); + if (!Continue) + Cancelled = true; + return Continue ? P : nullptr; +} + bool SemaAnnotator::handleCustomAttributes(Decl *D) { // CustomAttrs of non-param VarDecls are handled when this method is called // on their containing PatternBindingDecls (see below). @@ -806,32 +819,37 @@ bool SemaAnnotator::shouldIgnore(Decl *D) { bool SourceEntityWalker::walk(SourceFile &SrcFile) { SemaAnnotator Annotator(*this); - return SrcFile.walk(Annotator); + return performWalk(Annotator, [&]() { return SrcFile.walk(Annotator); }); } bool SourceEntityWalker::walk(ModuleDecl &Mod) { SemaAnnotator Annotator(*this); - return Mod.walk(Annotator); + return performWalk(Annotator, [&]() { return Mod.walk(Annotator); }); } bool SourceEntityWalker::walk(Stmt *S) { SemaAnnotator Annotator(*this); - return S->walk(Annotator); + return performWalk(Annotator, [&]() { return S->walk(Annotator); }); } bool SourceEntityWalker::walk(Expr *E) { SemaAnnotator Annotator(*this); - return E->walk(Annotator); + return performWalk(Annotator, [&]() { return E->walk(Annotator); }); +} + +bool SourceEntityWalker::walk(Pattern *P) { + SemaAnnotator Annotator(*this); + return performWalk(Annotator, [&]() { return P->walk(Annotator); }); } bool SourceEntityWalker::walk(Decl *D) { SemaAnnotator Annotator(*this); - return D->walk(Annotator); + return performWalk(Annotator, [&]() { return D->walk(Annotator); }); } bool SourceEntityWalker::walk(DeclContext *DC) { SemaAnnotator Annotator(*this); - return DC->walkContext(Annotator); + return performWalk(Annotator, [&]() { return DC->walkContext(Annotator); }); } bool SourceEntityWalker::walk(ASTNode N) { diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index 06376e8c585ed..b0637d40e8985 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -412,11 +412,11 @@ std::pair NameMatcher::walkToExprPre(Expr *E) { case ExprKind::Binary: { BinaryExpr *BinE = cast(E); // Visit in source order. - if (!BinE->getArg()->getElement(0)->walk(*this)) + if (!BinE->getLHS()->walk(*this)) return {false, nullptr}; if (!BinE->getFn()->walk(*this)) return {false, nullptr}; - if (!BinE->getArg()->getElement(1)->walk(*this)) + if (!BinE->getRHS()->walk(*this)) return {false, nullptr}; // We already visited the children. @@ -440,6 +440,7 @@ std::pair NameMatcher::walkToExprPre(Expr *E) { break; case KeyPathExpr::Component::Kind::DictionaryKey: case KeyPathExpr::Component::Kind::Invalid: + case KeyPathExpr::Component::Kind::CodeCompletion: break; case KeyPathExpr::Component::Kind::OptionalForce: case KeyPathExpr::Component::Kind::OptionalChain: @@ -724,9 +725,12 @@ void ResolvedRangeInfo::print(llvm::raw_ostream &OS) const { OS << "Multi\n"; } - if (ThrowingUnhandledError) { + if (UnhandledEffects.contains(EffectKind::Throws)) { OS << "Throwing\n"; } + if (UnhandledEffects.contains(EffectKind::Async)) { + OS << "Async\n"; + } if (Orphan != OrphanKind::None) { OS << ""; diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index 126cfa7f7b68f..3c76dd622792c 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -305,7 +305,7 @@ static const std::vector URLProtocols = { // Use RegexStrURL: "acap", "afp", "afs", "cid", "data", "fax", "feed", "file", "ftp", "go", "gopher", "http", "https", "imap", "ldap", "mailserver", "mid", "modem", - "news", "nntp", "opaquelocktoken", "pop", "prospero", "rdar", "rtsp", "service" + "news", "nntp", "opaquelocktoken", "pop", "prospero", "rdar", "rtsp", "service", "sip", "soap.beep", "soap.beeps", "tel", "telnet", "tip", "tn3270", "urn", "vemmi", "wais", "xcdoc", "z39.50r","z39.50s", @@ -484,8 +484,12 @@ CharSourceRange innerCharSourceRangeFromSourceRange(const SourceManager &SM, return CharSourceRange(SM, SRS, (SR.End != SR.Start) ? SR.End : SRS); } -CharSourceRange parameterNameRangeOfCallArg(const TupleExpr *TE, +CharSourceRange parameterNameRangeOfCallArg(const Expr *ArgListExpr, const Expr *Arg) { + if (isa(ArgListExpr)) + return CharSourceRange(); + + auto *TE = cast(ArgListExpr); if (!TE->hasElementNameLocs() || !TE->hasElementNames()) return CharSourceRange(); @@ -547,12 +551,11 @@ std::pair ModelASTWalker::walkToExprPre(Expr *E) { if (isVisitedBefore(E)) return {false, E}; - auto addCallArgExpr = [&](Expr *Elem, TupleExpr *ParentTupleExpr) { - if (isa(Elem) || - !isCurrentCallArgExpr(ParentTupleExpr)) + auto addCallArgExpr = [&](Expr *Elem, Expr *ArgListExpr) { + if (isa(Elem) || !isCurrentCallArgExpr(ArgListExpr)) return; - CharSourceRange NR = parameterNameRangeOfCallArg(ParentTupleExpr, Elem); + CharSourceRange NR = parameterNameRangeOfCallArg(ArgListExpr, Elem); SyntaxStructureNode SN; SN.Kind = SyntaxStructureKind::Argument; SN.NameRange = NR; @@ -568,15 +571,9 @@ std::pair ModelASTWalker::walkToExprPre(Expr *E) { pushStructureNode(SN, Elem); }; - if (auto *ParentTupleExpr = dyn_cast_or_null(Parent.getAsExpr())) { - // the argument value is a tuple expression already, we can just extract it - addCallArgExpr(E, ParentTupleExpr); - } else if (auto *ParentOptionalExpr = dyn_cast_or_null(Parent.getAsExpr())) { - // if an argument value is an optional expression, we should extract the - // argument from the subexpression - if (auto *ParentTupleExpr = dyn_cast_or_null(ParentOptionalExpr->getSubExpr())) { - addCallArgExpr(E, ParentTupleExpr); - } + if (auto *ParentExpr = Parent.getAsExpr()) { + if (isa(ParentExpr) || isa(ParentExpr)) + addCallArgExpr(E, ParentExpr); } if (E->isImplicit()) @@ -668,23 +665,6 @@ std::pair ModelASTWalker::walkToExprPre(Expr *E) { pushStructureNode(SN, Closure); - } else if (auto *CLE = dyn_cast(E)) { - // The ASTWalker visits captured variables twice, from a `CaptureListEntry` they are visited - // from the `VarDecl` and the `PatternBindingDecl` entries. - // We take over visitation here to avoid walking the `PatternBindingDecl` ones. - for (auto c : CLE->getCaptureList()) { - if (auto *VD = c.Var) { - // We're skipping over the PatternBindingDecl so we need to handle the - // the VarDecl's attributes that we'd normally process visiting the PBD. - if (!handleAttrs(VD->getAttrs())) - return { false, nullptr }; - VD->walk(*this); - } - } - if (auto *CE = CLE->getClosureBody()) - CE->walk(*this); - return { false, walkToExprPost(E) }; - } else if (auto SE = dyn_cast(E)) { // In SequenceExpr, explicit cast expressions (e.g. 'as', 'is') appear // twice. Skip pointers we've already seen. diff --git a/lib/IDE/TypeContextInfo.cpp b/lib/IDE/TypeContextInfo.cpp index 0ab8242b7aa34..23c3476c8d94b 100644 --- a/lib/IDE/TypeContextInfo.cpp +++ b/lib/IDE/TypeContextInfo.cpp @@ -143,11 +143,13 @@ void ContextInfoCallbacks::getImplicitMembers( return true; // Static properties which is convertible to 'Self'. - if (isa(VD) && VD->isStatic()) { - auto declTy = T->getTypeOfMember(CurModule, VD); - if (declTy->isEqual(T) || - swift::isConvertibleTo(declTy, T, /*openArchetypes=*/true, *DC)) - return true; + if (auto *Var = dyn_cast(VD)) { + if (Var->isStatic()) { + auto declTy = T->getTypeOfMember(CurModule, Var); + if (declTy->isEqual(T) || + swift::isConvertibleTo(declTy, T, /*openArchetypes=*/true, *DC)) + return true; + } } return false; diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index 01e905dbee4a4..8878e613709f2 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -294,30 +294,38 @@ bool ide::initCompilerInvocation( StreamDiagConsumer DiagConsumer(ErrOS); Diags.addConsumer(DiagConsumer); - bool HadError = driver::getSingleFrontendInvocationFromDriverArguments( - Args, Diags, [&](ArrayRef FrontendArgs) { - return Invocation.parseArgs(FrontendArgs, Diags); - }, /*ForceNoOutputs=*/true); + bool InvocationCreationFailed = + driver::getSingleFrontendInvocationFromDriverArguments( + Args, Diags, + [&](ArrayRef FrontendArgs) { + return Invocation.parseArgs(FrontendArgs, Diags); + }, + /*ForceNoOutputs=*/true); // Remove the StreamDiagConsumer as it's no longer needed. Diags.removeConsumer(DiagConsumer); - if (HadError) { - Error = std::string(ErrOS.str()); + Error = std::string(ErrOS.str()); + if (InvocationCreationFailed) { return true; } + std::string SymlinkResolveError; Invocation.getFrontendOptions().InputsAndOutputs = resolveSymbolicLinksInInputs( Invocation.getFrontendOptions().InputsAndOutputs, - UnresolvedPrimaryFile, FileSystem, Error); + UnresolvedPrimaryFile, FileSystem, SymlinkResolveError); // SourceKit functionalities want to proceed even if there are missing inputs. Invocation.getFrontendOptions().InputsAndOutputs .setShouldRecoverMissingInputs(); - if (!Error.empty()) + if (!SymlinkResolveError.empty()) { + // resolveSymbolicLinksInInputs fails if the unresolved primary file is not + // in the input files. We can't recover from that. + Error += SymlinkResolveError; return true; + } ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions(); ImporterOpts.DetailedPreprocessingRecord = true; @@ -1009,9 +1017,6 @@ accept(SourceManager &SM, RegionType Type, ArrayRef Replacements) { } } -swift::ide::SourceEditTextConsumer:: -SourceEditTextConsumer(llvm::raw_ostream &OS) : OS(OS) { } - void swift::ide::SourceEditTextConsumer:: accept(SourceManager &SM, RegionType Type, ArrayRef Replacements) { for (const auto &Replacement: Replacements) { @@ -1108,6 +1113,14 @@ accept(SourceManager &SM, RegionType RegionType, } } +void swift::ide::BroadcastingSourceEditConsumer::accept( + SourceManager &SM, RegionType RegionType, + ArrayRef Replacements) { + for (auto &Consumer : Consumers) { + Consumer->accept(SM, RegionType, Replacements); + } +} + bool swift::ide::isFromClang(const Decl *D) { if (getEffectiveClangNode(D)) return true; @@ -1140,7 +1153,11 @@ ClangNode swift::ide::extensionGetClangNode(const ExtensionDecl *ext) { return ClangNode(); } -std::pair swift::ide::getReferencedDecl(Expr *expr) { +std::pair swift::ide::getReferencedDecl(Expr *expr, + bool semantic) { + if (semantic) + expr = expr->getSemanticsProvidingExpr(); + auto exprTy = expr->getType(); // Look through unbound instance member accesses. @@ -1206,6 +1223,7 @@ Expr *swift::ide::getBase(ArrayRef ExprStack) { Expr *CurrentE = ExprStack.back(); Expr *ParentE = getContainingExpr(ExprStack, 1); Expr *Base = nullptr; + if (auto DSE = dyn_cast_or_null(ParentE)) Base = DSE->getBase(); else if (auto MRE = dyn_cast(CurrentE)) @@ -1213,6 +1231,11 @@ Expr *swift::ide::getBase(ArrayRef ExprStack) { else if (auto SE = dyn_cast(CurrentE)) Base = SE->getBase(); + // Look through curry thunks + if (auto ACE = dyn_cast_or_null(Base)) + if (auto *Unwrapped = ACE->getUnwrappedCurryThunkExpr()) + Base = Unwrapped; + if (Base) { while (auto ICE = dyn_cast(Base)) Base = ICE->getSubExpr(); diff --git a/lib/IRGen/CMakeLists.txt b/lib/IRGen/CMakeLists.txt index 168172073a10b..8077b37f77b79 100644 --- a/lib/IRGen/CMakeLists.txt +++ b/lib/IRGen/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftIRGen STATIC AllocStackHoisting.cpp ClassLayout.cpp @@ -17,6 +17,7 @@ add_swift_host_library(swiftIRGen STATIC GenControl.cpp GenCoverage.cpp GenConcurrency.cpp + GenDistributed.cpp GenDecl.cpp GenDiffFunc.cpp GenDiffWitness.cpp @@ -73,3 +74,5 @@ target_link_libraries(swiftIRGen PRIVATE swiftSILGen swiftSILOptimizer swiftTBDGen) + +set_swift_llvm_is_available(swiftIRGen) diff --git a/lib/IRGen/CallEmission.h b/lib/IRGen/CallEmission.h index d845656cae338..25072be085369 100644 --- a/lib/IRGen/CallEmission.h +++ b/lib/IRGen/CallEmission.h @@ -119,6 +119,9 @@ class CallEmission { return result; } + + virtual llvm::Value *getResumeFunctionPointer() = 0; + virtual llvm::Value *getAsyncContext() = 0; }; std::unique_ptr diff --git a/lib/IRGen/Callee.h b/lib/IRGen/Callee.h index f5d9b44377c4d..61f9564e07ff8 100644 --- a/lib/IRGen/Callee.h +++ b/lib/IRGen/Callee.h @@ -172,6 +172,9 @@ namespace irgen { TaskFutureWaitThrowing, AsyncLetWait, AsyncLetWaitThrowing, + AsyncLetGet, + AsyncLetGetThrowing, + AsyncLetFinish, TaskGroupWaitNext, }; @@ -204,30 +207,45 @@ namespace irgen { assert(isSpecial()); return SpecialKind(value - SpecialOffset); } + + bool isSpecialAsyncLet() const { + if (!isSpecial()) return false; + switch (getSpecialKind()) { + case SpecialKind::AsyncLetGet: + case SpecialKind::AsyncLetGetThrowing: + case SpecialKind::AsyncLetFinish: + return true; + + case SpecialKind::TaskFutureWaitThrowing: + case SpecialKind::TaskFutureWait: + case SpecialKind::AsyncLetWait: + case SpecialKind::AsyncLetWaitThrowing: + case SpecialKind::TaskGroupWaitNext: + return false; + } + + return false; + } /// Should we suppress the generic signature from the given function? /// /// This is a micro-optimization we apply to certain special functions /// that we know don't need generics. - bool suppressGenerics() const { + bool useSpecialConvention() const { if (!isSpecial()) return false; switch (getSpecialKind()) { - case SpecialKind::TaskFutureWait: case SpecialKind::TaskFutureWaitThrowing: + case SpecialKind::TaskFutureWait: case SpecialKind::AsyncLetWait: case SpecialKind::AsyncLetWaitThrowing: + case SpecialKind::AsyncLetGet: + case SpecialKind::AsyncLetGetThrowing: + case SpecialKind::AsyncLetFinish: case SpecialKind::TaskGroupWaitNext: - // FIXME: I have disabled this optimization, if we bring it back we - // need to debug why it currently does not work (call emission - // computes an undef return pointer) and change the runtime entries to - // remove the extra type parameter. - // - // We suppress generics from these as a code-size optimization - // because the runtime can recover the success type from the - // future. - return false; + return true; } + llvm_unreachable("covered switch"); } friend bool operator==(Kind lhs, Kind rhs) { @@ -371,9 +389,7 @@ namespace irgen { return !kind.isAsyncFunctionPointer(); } - bool suppressGenerics() const { - return kind.suppressGenerics(); - } + bool useSpecialConvention() const { return kind.useSpecialConvention(); } }; class Callee { @@ -437,9 +453,7 @@ namespace irgen { return Fn.getSignature(); } - bool suppressGenerics() const { - return Fn.suppressGenerics(); - } + bool useSpecialConvention() const { return Fn.useSpecialConvention(); } /// If this callee has a value for the Swift context slot, return /// it; otherwise return non-null. @@ -457,6 +471,7 @@ namespace irgen { llvm::Value *getObjCMethodSelector() const; }; + FunctionPointer::Kind classifyFunctionPointerKind(SILFunction *fn); } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/DebugTypeInfo.cpp b/lib/IRGen/DebugTypeInfo.cpp index f1c2dfd1f25d6..9f183d306870f 100644 --- a/lib/IRGen/DebugTypeInfo.cpp +++ b/lib/IRGen/DebugTypeInfo.cpp @@ -24,9 +24,9 @@ using namespace swift; using namespace irgen; -DebugTypeInfo::DebugTypeInfo(swift::Type Ty, llvm::Type *StorageTy, Size size, - Alignment align, bool HasDefaultAlignment, - bool IsMetadata) +DebugTypeInfo::DebugTypeInfo(swift::Type Ty, llvm::Type *StorageTy, + Optional size, Alignment align, + bool HasDefaultAlignment, bool IsMetadata) : Type(Ty.getPointer()), StorageType(StorageTy), size(size), align(align), DefaultAlignment(HasDefaultAlignment), IsMetadataType(IsMetadata) { assert(align.getValue() != 0); @@ -43,14 +43,10 @@ static bool hasDefaultAlignment(swift::Type Ty) { DebugTypeInfo DebugTypeInfo::getFromTypeInfo(swift::Type Ty, const TypeInfo &Info) { - Size size; + Optional size; if (Info.isFixedSize()) { const FixedTypeInfo &FixTy = *cast(&Info); size = FixTy.getFixedSize(); - } else { - // FIXME: Handle NonFixedTypeInfo here or assert that we won't - // encounter one. - size = Size(0); } assert(Info.getStorageType() && "StorageType is a nullptr"); return DebugTypeInfo(Ty.getPointer(), Info.getStorageType(), size, @@ -160,10 +156,12 @@ TypeDecl *DebugTypeInfo::getDecl() const { #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void DebugTypeInfo::dump() const { - llvm::errs() << "[Size " << size.getValue() << " Alignment " - << align.getValue() << "] "; - + llvm::errs() << "["; + if (size) + llvm::errs() << "Size " << size->getValue() << " "; + llvm::errs() << "Alignment " << align.getValue() << "] "; getType()->dump(llvm::errs()); + if (StorageType) { llvm::errs() << "StorageType="; StorageType->dump(); diff --git a/lib/IRGen/DebugTypeInfo.h b/lib/IRGen/DebugTypeInfo.h index 14e26e9360ec6..aaa5c7e0e4678 100644 --- a/lib/IRGen/DebugTypeInfo.h +++ b/lib/IRGen/DebugTypeInfo.h @@ -36,22 +36,23 @@ class TypeInfo; /// This data structure holds everything needed to emit debug info /// for a type. class DebugTypeInfo { +protected: /// The type we need to emit may be different from the type /// mentioned in the Decl, for example, stripped of qualifiers. TypeBase *Type = nullptr; /// Needed to determine the size of basic types and to determine /// the storage type for undefined variables. llvm::Type *StorageType = nullptr; - Size size = Size(0); - Alignment align = Alignment(); + Optional size; + Alignment align; bool DefaultAlignment = true; bool IsMetadataType = false; public: DebugTypeInfo() = default; - DebugTypeInfo(swift::Type Ty, llvm::Type *StorageTy, Size SizeInBytes, - Alignment AlignInBytes, bool HasDefaultAlignment, - bool IsMetadataType); + DebugTypeInfo(swift::Type Ty, llvm::Type *StorageTy, + Optional SizeInBytes, Alignment AlignInBytes, + bool HasDefaultAlignment, bool IsMetadataType); /// Create type for a local variable. static DebugTypeInfo getLocalVariable(VarDecl *Decl, @@ -92,24 +93,44 @@ class DebugTypeInfo { } llvm::Type *getStorageType() const { - assert((StorageType || size.isZero()) && - "only defined types may have a size"); + if (size && size->isZero()) + assert(StorageType && "only defined types may have a size"); return StorageType; } - Size getSize() const { return size; } + Optional getSize() const { return size; } void setSize(Size NewSize) { size = NewSize; } Alignment getAlignment() const { return align; } bool isNull() const { return Type == nullptr; } bool isForwardDecl() const { return StorageType == nullptr; } bool isMetadataType() const { return IsMetadataType; } bool hasDefaultAlignment() const { return DefaultAlignment; } - + bool operator==(DebugTypeInfo T) const; bool operator!=(DebugTypeInfo T) const; #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void dump() const; #endif }; + +/// A DebugTypeInfo with a defined size (that may be 0). +class CompletedDebugTypeInfo : public DebugTypeInfo { + CompletedDebugTypeInfo(DebugTypeInfo DbgTy) : DebugTypeInfo(DbgTy) {} +public: + static Optional get(DebugTypeInfo DbgTy) { + if (!DbgTy.getSize()) + return {}; + return CompletedDebugTypeInfo(DbgTy); + } + + static Optional + getFromTypeInfo(swift::Type Ty, const TypeInfo &Info) { + return CompletedDebugTypeInfo::get( + DebugTypeInfo::getFromTypeInfo(Ty, Info)); + } + + Size::int_type getSizeValue() const { return size->getValue(); } +}; + } } diff --git a/lib/IRGen/GenArchetype.cpp b/lib/IRGen/GenArchetype.cpp index c81255520af49..30871955b094b 100644 --- a/lib/IRGen/GenArchetype.cpp +++ b/lib/IRGen/GenArchetype.cpp @@ -95,12 +95,13 @@ namespace { class OpaqueArchetypeTypeInfo : public ResilientTypeInfo { - OpaqueArchetypeTypeInfo(llvm::Type *type) - : ResilientTypeInfo(type, IsABIAccessible) {} + OpaqueArchetypeTypeInfo(llvm::Type *type, IsABIAccessible_t abiAccessible) + : ResilientTypeInfo(type, abiAccessible) {} public: - static const OpaqueArchetypeTypeInfo *create(llvm::Type *type) { - return new OpaqueArchetypeTypeInfo(type); + static const OpaqueArchetypeTypeInfo * + create(llvm::Type *type, IsABIAccessible_t abiAccessible) { + return new OpaqueArchetypeTypeInfo(type, abiAccessible); } void collectMetadataForOutlining(OutliningMetadataCollector &collector, @@ -342,7 +343,18 @@ const TypeInfo *TypeConverter::convertArchetypeType(ArchetypeType *archetype) { // Otherwise, for now, always use an opaque indirect type. llvm::Type *storageType = IGM.OpaquePtrTy->getElementType(); - return OpaqueArchetypeTypeInfo::create(storageType); + + // Opaque result types can be private and from a different module. In this + // case we can't access their type metadata from another module. + IsABIAccessible_t abiAccessible = IsABIAccessible; + if (auto opaqueArchetype = dyn_cast(archetype)) { + auto ¤tSILModule = IGM.getSILModule(); + abiAccessible = + currentSILModule.isTypeMetadataAccessible(archetype->getCanonicalType()) + ? IsABIAccessible + : IsNotABIAccessible; + } + return OpaqueArchetypeTypeInfo::create(storageType, abiAccessible); } static void setMetadataRef(IRGenFunction &IGF, diff --git a/lib/IRGen/GenBuiltin.cpp b/lib/IRGen/GenBuiltin.cpp index 559aad247fd6c..b72174f0ce638 100644 --- a/lib/IRGen/GenBuiltin.cpp +++ b/lib/IRGen/GenBuiltin.cpp @@ -29,6 +29,7 @@ #include "GenCall.h" #include "GenCast.h" #include "GenConcurrency.h" +#include "GenDistributed.h" #include "GenPointerAuth.h" #include "GenIntegerLiteral.h" #include "IRGenFunction.h" @@ -227,13 +228,35 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, } if (Builtin.ID == BuiltinValueKind::StartAsyncLet) { + auto taskOptions = args.claimNext(); auto taskFunction = args.claimNext(); auto taskContext = args.claimNext(); auto asyncLet = emitBuiltinStartAsyncLet( IGF, + taskOptions, taskFunction, taskContext, + nullptr, + substitutions + ); + + out.add(asyncLet); + return; + } + + if (Builtin.ID == BuiltinValueKind::StartAsyncLetWithLocalBuffer) { + auto taskOptions = args.claimNext(); + auto taskFunction = args.claimNext(); + auto taskContext = args.claimNext(); + auto localBuffer = args.claimNext(); + + auto asyncLet = emitBuiltinStartAsyncLet( + IGF, + taskOptions, + taskFunction, + taskContext, + localBuffer, substitutions ); @@ -249,8 +272,18 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, return; } + if (Builtin.ID == BuiltinValueKind::EndAsyncLetLifetime) { + IGF.Builder.CreateLifetimeEnd(args.claimNext()); + // Ignore a second operand which is inserted by ClosureLifetimeFixup and + // only used for dependency tracking. + (void)args.claimAll(); + return; + } + if (Builtin.ID == BuiltinValueKind::CreateTaskGroup) { - out.add(emitCreateTaskGroup(IGF)); + // Claim metadata pointer. + (void)args.claimAll(); + out.add(emitCreateTaskGroup(IGF, substitutions)); return; } @@ -266,24 +299,24 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, return; } - if (Builtin.ID == BuiltinValueKind::CreateAsyncTaskFuture || - Builtin.ID == BuiltinValueKind::CreateAsyncTaskGroupFuture) { + if (Builtin.ID == BuiltinValueKind::CreateAsyncTask || + Builtin.ID == BuiltinValueKind::CreateAsyncTaskInGroup) { auto flags = args.claimNext(); auto taskGroup = - (Builtin.ID == BuiltinValueKind::CreateAsyncTaskGroupFuture) + (Builtin.ID == BuiltinValueKind::CreateAsyncTaskInGroup) ? args.claimNext() : nullptr; - auto futureResultType = - ((Builtin.ID == BuiltinValueKind::CreateAsyncTaskFuture) || - (Builtin.ID == BuiltinValueKind::CreateAsyncTaskGroupFuture)) - ? args.claimNext() - : nullptr; + auto futureResultType = args.claimNext(); auto taskFunction = args.claimNext(); auto taskContext = args.claimNext(); auto newTaskAndContext = emitTaskCreate( - IGF, flags, taskGroup, futureResultType, taskFunction, taskContext, + IGF, + flags, + taskGroup, + futureResultType, + taskFunction, taskContext, substitutions); // Cast back to NativeObject/RawPointer. @@ -355,6 +388,18 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, return; } + if (Builtin.ID == BuiltinValueKind::InitializeDistributedRemoteActor) { + auto actorMetatype = args.claimNext(); + emitDistributedActorInitializeRemote(IGF, resultType, actorMetatype, out); + return; + } + + if (Builtin.ID == BuiltinValueKind::DestroyDistributedActor) { + auto actor = args.claimNext(); + emitDistributedActorDestroy(IGF, actor); + return; + } + // If this is an LLVM IR intrinsic, lower it to an intrinsic call. const IntrinsicInfo &IInfo = IGF.getSILModule().getIntrinsicInfo(FnId); llvm::Intrinsic::ID IID = IInfo.ID; diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index fa55bb5168b37..5d8269477b096 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -89,17 +89,15 @@ AsyncContextLayout irgen::getAsyncContextLayout(IRGenModule &IGM, IGM.getMaximalTypeExpansionContext()); auto layout = getAsyncContextLayout( IGM, originalType, substitutedType, forwardingSubstitutionMap, - /*suppressGenerics*/ false, + /*useSpecialConvention*/ false, FunctionPointer::Kind(FunctionPointer::BasicKind::AsyncFunctionPointer)); return layout; } -AsyncContextLayout -irgen::getAsyncContextLayout(IRGenModule &IGM, CanSILFunctionType originalType, - CanSILFunctionType substitutedType, - SubstitutionMap substitutionMap, - bool suppressGenerics, - FunctionPointer::Kind kind) { +AsyncContextLayout irgen::getAsyncContextLayout( + IRGenModule &IGM, CanSILFunctionType originalType, + CanSILFunctionType substitutedType, SubstitutionMap substitutionMap, + bool useSpecialConvention, FunctionPointer::Kind kind) { SmallVector typeInfos; SmallVector valTypes; SmallVector paramInfos; @@ -137,11 +135,8 @@ irgen::getAsyncContextLayout(IRGenModule &IGM, CanSILFunctionType originalType, } // Add storage for data used by runtime entry points. - // See TaskFutureWaitAsyncContext and TaskGroupNextAsyncContext. + // See TaskFutureWaitAsyncContext. if (kind.isSpecial()) { - switch (kind.getSpecialKind()) { - case FunctionPointer::SpecialKind::TaskFutureWait: - case FunctionPointer::SpecialKind::TaskFutureWaitThrowing: { // This needs to match the layout of TaskFutureWaitAsyncContext. // Add storage for the waiting future's result pointer (OpaqueValue *). auto ty = SILType(); @@ -152,51 +147,6 @@ irgen::getAsyncContextLayout(IRGenModule &IGM, CanSILFunctionType originalType, // OpaqueValue *successResultPointer valTypes.push_back(ty); typeInfos.push_back(&ti); - // void (*, *) async *asyncResumeEntryPoint; - valTypes.push_back(ty); - typeInfos.push_back(&ti); - break; - } - case FunctionPointer::SpecialKind::AsyncLetWait: - case FunctionPointer::SpecialKind::AsyncLetWaitThrowing: { - // This needs to match the layout of TaskFutureWaitAsyncContext. - // Add storage for the waiting future's result pointer (OpaqueValue *). - auto ty = SILType(); - auto &ti = IGM.getSwiftContextPtrTypeInfo(); - // SwiftError * - valTypes.push_back(ty); - typeInfos.push_back(&ti); - // OpaqueValue *successResultPointer - valTypes.push_back(ty); - typeInfos.push_back(&ti); - // void (*, *) async *asyncResumeEntryPoint; - valTypes.push_back(ty); - typeInfos.push_back(&ti); - break; - } - case FunctionPointer::SpecialKind::TaskGroupWaitNext: { - // This needs to match the layout of TaskGroupNextAsyncContext. - // Add storage for the waiting future's result pointer (OpaqueValue *). - auto ty = SILType(); - auto &ti = IGM.getSwiftContextPtrTypeInfo(); - // SwiftError * errorResult; - valTypes.push_back(ty); - typeInfos.push_back(&ti); - // OpaqueValue *successResultPointer; - valTypes.push_back(ty); - typeInfos.push_back(&ti); - // void (*, *) async *asyncResumeEntryPoint; - valTypes.push_back(ty); - typeInfos.push_back(&ti); - // TaskGroup *group; - valTypes.push_back(ty); - typeInfos.push_back(&ti); - // Metadata *successType; - valTypes.push_back(ty); - typeInfos.push_back(&ti); - break; - } - } } return AsyncContextLayout(IGM, LayoutStrategy::Optimal, valTypes, typeInfos, originalType, substitutedType, substitutionMap); @@ -224,7 +174,12 @@ Alignment IRGenModule::getAsyncContextAlignment() const { void IRGenFunction::setupAsync(unsigned asyncContextIndex) { llvm::Value *c = CurFn->getArg(asyncContextIndex); asyncContextLocation = createAlloca(c->getType(), IGM.getPointerAlignment()); - Builder.CreateStore(c, asyncContextLocation); + + IRBuilder builder(IGM.getLLVMContext(), IGM.DebugInfo != nullptr); + // Insert the stores after the coro.begin. + builder.SetInsertPoint(getEarliestInsertionPoint()->getParent(), + getEarliestInsertionPoint()->getIterator()); + builder.CreateStore(c, asyncContextLocation); } llvm::Value *IRGenFunction::getAsyncTask() { @@ -442,13 +397,14 @@ namespace { bool CanUseSRet = true; bool CanUseError = true; bool CanUseSelf = true; - bool SuppressGenerics; + bool useSpecialConvention; unsigned AsyncContextIdx; unsigned AsyncResumeFunctionSwiftSelfIdx = 0; SignatureExpansion(IRGenModule &IGM, CanSILFunctionType fnType, - bool suppressGenerics) - : IGM(IGM), FnType(fnType), SuppressGenerics(suppressGenerics) {} + bool useSpecialConvention) + : IGM(IGM), FnType(fnType), useSpecialConvention(useSpecialConvention) { + } /// Expand the components of the primary entrypoint of the function type. void expandFunctionType(); @@ -1653,8 +1609,15 @@ void SignatureExpansion::expandParameters() { } // Next, the generic signature. - if (hasPolymorphicParameters(FnType) && !SuppressGenerics) + if (hasPolymorphicParameters(FnType) && !useSpecialConvention) expandPolymorphicSignature(IGM, FnType, ParamIRTypes); + if (useSpecialConvention) { + // Async waiting functions add the resume function pointer, and the context + // for the call. + // (But skip passing the metadata.) + ParamIRTypes.push_back(IGM.Int8PtrTy); + ParamIRTypes.push_back(IGM.SwiftContextPtrTy); + } // Context is next. if (hasSelfContext) { @@ -1812,8 +1775,14 @@ void SignatureExpansion::expandAsyncEntryType() { } // Next, the generic signature. - if (hasPolymorphicParameters(FnType) && !SuppressGenerics) + if (hasPolymorphicParameters(FnType) && !useSpecialConvention) expandPolymorphicSignature(IGM, FnType, ParamIRTypes); + if (useSpecialConvention) { + // Async waiting functions add the resume function pointer. + // (But skip passing the metadata.) + ParamIRTypes.push_back(IGM.Int8PtrTy); + ParamIRTypes.push_back(IGM.SwiftContextPtrTy); + } // Context is next. if (hasSelfContext) { @@ -1941,9 +1910,9 @@ Signature SignatureExpansion::getSignature() { Signature Signature::getUncached(IRGenModule &IGM, CanSILFunctionType formalType, - bool suppressGenerics) { + bool useSpecialConvention) { GenericContextScope scope(IGM, formalType->getInvocationGenericSignature()); - SignatureExpansion expansion(IGM, formalType, suppressGenerics); + SignatureExpansion expansion(IGM, formalType, useSpecialConvention); expansion.expandFunctionType(); return expansion.getSignature(); } @@ -1960,25 +1929,28 @@ Signature Signature::forAsyncReturn(IRGenModule &IGM, CanSILFunctionType fnType) { assert(fnType->isAsync()); GenericContextScope scope(IGM, fnType->getInvocationGenericSignature()); - SignatureExpansion expansion(IGM, fnType, /*suppress generics*/ false); + SignatureExpansion expansion(IGM, fnType, + /*suppress generics*/ false); expansion.expandAsyncReturnType(); return expansion.getSignature(); } -Signature Signature::forAsyncAwait(IRGenModule &IGM, - CanSILFunctionType fnType) { +Signature Signature::forAsyncAwait(IRGenModule &IGM, CanSILFunctionType fnType, + bool useSpecialConvention) { assert(fnType->isAsync()); GenericContextScope scope(IGM, fnType->getInvocationGenericSignature()); - SignatureExpansion expansion(IGM, fnType, /*suppress generics*/ false); + SignatureExpansion expansion(IGM, fnType, + /*suppress generics*/ useSpecialConvention); expansion.expandAsyncAwaitType(); return expansion.getSignature(); } -Signature Signature::forAsyncEntry(IRGenModule &IGM, - CanSILFunctionType fnType) { +Signature Signature::forAsyncEntry(IRGenModule &IGM, CanSILFunctionType fnType, + bool useSpecialConvention) { assert(fnType->isAsync()); GenericContextScope scope(IGM, fnType->getInvocationGenericSignature()); - SignatureExpansion expansion(IGM, fnType, /*suppress generics*/ false); + SignatureExpansion expansion(IGM, fnType, + /*suppress generics*/ useSpecialConvention); expansion.expandAsyncEntryType(); return expansion.getSignature(); } @@ -2284,6 +2256,14 @@ class SyncCallEmission final : public CallEmission { Address getCalleeErrorSlot(SILType errorType, bool isCalleeAsync) override { return IGF.getCalleeErrorResultSlot(errorType); }; + + llvm::Value *getResumeFunctionPointer() override { + llvm_unreachable("Should not call getResumeFunctionPointer on a sync call"); + } + + llvm::Value *getAsyncContext() override { + llvm_unreachable("Should not call getAsyncContext on a sync call"); + } }; class AsyncCallEmission final : public CallEmission { @@ -2294,15 +2274,15 @@ class AsyncCallEmission final : public CallEmission { llvm::Value *calleeFunction = nullptr; llvm::Value *currentResumeFn = nullptr; llvm::Value *thickContext = nullptr; + Size initialContextSize = Size(0); Optional asyncContextLayout; AsyncContextLayout getAsyncContextLayout() { if (!asyncContextLayout) { asyncContextLayout.emplace(::getAsyncContextLayout( IGF.IGM, getCallee().getOrigFunctionType(), - getCallee().getSubstFunctionType(), - getCallee().getSubstitutions(), - getCallee().suppressGenerics(), + getCallee().getSubstFunctionType(), getCallee().getSubstitutions(), + getCallee().useSpecialConvention(), getCallee().getFunctionPointer().getKind())); } return *asyncContextLayout; @@ -2332,7 +2312,7 @@ class AsyncCallEmission final : public CallEmission { auto layout = getAsyncContextLayout(); // Allocate space for the async context. - auto initialContextSize = Size(0); + initialContextSize = Size(0); // Only c++ runtime functions should use the initial context size. if (CurCallee.getFunctionPointer().getKind().isSpecial()) { initialContextSize = layout.getSize(); @@ -2344,13 +2324,18 @@ class AsyncCallEmission final : public CallEmission { std::make_pair(true, true), initialContextSize); auto *dynamicContextSize = IGF.Builder.CreateZExt(dynamicContextSize32, IGF.IGM.SizeTy); - contextBuffer = emitAllocAsyncContext(IGF, dynamicContextSize); + contextBuffer = getCallee().useSpecialConvention() + ? emitStaticAllocAsyncContext(IGF, initialContextSize) + : emitAllocAsyncContext(IGF, dynamicContextSize); context = layout.emitCastTo(IGF, contextBuffer.getAddress()); } void end() override { assert(contextBuffer.isValid()); assert(context.isValid()); - emitDeallocAsyncContext(IGF, contextBuffer); + if (getCallee().useSpecialConvention()) + emitStaticDeallocAsyncContext(IGF, contextBuffer, initialContextSize); + else + emitDeallocAsyncContext(IGF, contextBuffer); super::end(); } void setFromCallee() override { @@ -2385,7 +2370,8 @@ class AsyncCallEmission final : public CallEmission { .getCorrespondingCodeAuthInfo(); return FunctionPointer( FunctionPointer::Kind::Function, calleeFunction, codeAuthInfo, - Signature::forAsyncAwait(IGF.IGM, getCallee().getOrigFunctionType())); + Signature::forAsyncAwait(IGF.IGM, getCallee().getOrigFunctionType(), + getCallee().useSpecialConvention())); } SILType getParameterType(unsigned index) override { @@ -2407,7 +2393,11 @@ class AsyncCallEmission final : public CallEmission { original.transferInto(asyncExplosion, fnConv.getNumIndirectSILResults()); // Pass the async context. - asyncExplosion.add(contextBuffer.getAddress()); + if (getCallee().useSpecialConvention()) { + // Pass the caller context. + asyncExplosion.add(IGF.getAsyncContext()); + } else + asyncExplosion.add(contextBuffer.getAddress()); // Pass along the coroutine buffer. switch (origCalleeType->getCoroutineKind()) { @@ -2471,7 +2461,7 @@ class AsyncCallEmission final : public CallEmission { auto layout = getAsyncContextLayout(); // Set caller info into the context. - { // caller context + if (!getCallee().useSpecialConvention()) { // caller context Explosion explosion; auto fieldLayout = layout.getParentLayout(); auto *context = IGF.getAsyncContext(); @@ -2485,7 +2475,8 @@ class AsyncCallEmission final : public CallEmission { explosion.add(context); saveValue(fieldLayout, explosion, isOutlined); } - { // Return to caller function. + if (!getCallee().useSpecialConvention()) { // Return to caller function. + assert(currentResumeFn == nullptr); auto fieldLayout = layout.getResumeParentLayout(); currentResumeFn = IGF.Builder.CreateIntrinsicCall( llvm::Intrinsic::coro_async_resume, {}); @@ -2613,7 +2604,9 @@ class AsyncCallEmission final : public CallEmission { arguments.push_back( IGM.getInt32(paramAttributeFlags)); arguments.push_back(currentResumeFn); - auto resumeProjFn = IGF.getOrCreateResumePrjFn(); + auto resumeProjFn = getCallee().useSpecialConvention() + ? IGF.getOrCreateResumeFromSuspensionFn() + : IGF.getOrCreateResumePrjFn(); arguments.push_back( Builder.CreateBitOrPointerCast(resumeProjFn, IGM.Int8PtrTy)); auto dispatchFn = IGF.createAsyncDispatchFn( @@ -2631,6 +2624,27 @@ class AsyncCallEmission final : public CallEmission { cast(signature.getType()->getReturnType()); return IGF.emitSuspendAsyncCall(asyncContextIndex, resultTy, arguments); } + llvm::Value *getResumeFunctionPointer() override { + assert(getCallee().useSpecialConvention()); + assert(currentResumeFn == nullptr); + currentResumeFn = + IGF.Builder.CreateIntrinsicCall(llvm::Intrinsic::coro_async_resume, {}); + auto signedResumeFn = currentResumeFn; + // Sign the task resume function with the C function pointer schema. + if (auto schema = IGF.IGM.getOptions().PointerAuth.FunctionPointers) { + // TODO: use the Clang type for TaskContinuationFunction* + // to make this work with type diversity. + auto authInfo = + PointerAuthInfo::emit(IGF, schema, nullptr, PointerAuthEntity()); + signedResumeFn = emitPointerAuthSign(IGF, signedResumeFn, authInfo); + } + return signedResumeFn; + } + + + llvm::Value *getAsyncContext() override { + return contextBuffer.getAddress(); + } }; } // end anonymous namespace @@ -3762,6 +3776,11 @@ emitRetconCoroutineEntry(IRGenFunction &IGF, CanSILFunctionType fnType, // Set the coroutine handle; this also flags that is a coroutine so that // e.g. dynamic allocas use the right code generation. IGF.setCoroutineHandle(hdl); + + auto *pt = IGF.Builder.IRBuilderBase::CreateAlloca(IGF.IGM.Int1Ty, + /*array size*/ nullptr, + "earliest insert point"); + IGF.setEarliestInsertionPoint(pt); } void irgen::emitAsyncFunctionEntry(IRGenFunction &IGF, @@ -3787,6 +3806,11 @@ void irgen::emitAsyncFunctionEntry(IRGenFunction &IGF, // Set the coroutine handle; this also flags that is a coroutine so that // e.g. dynamic allocas use the right code generation. IGF.setCoroutineHandle(hdl); + auto *pt = IGF.Builder.IRBuilderBase::CreateAlloca(IGF.IGM.Int1Ty, + /*array size*/ nullptr, + "earliest insert point"); + IGF.setEarliestInsertionPoint(pt); + IGF.setupAsync(asyncContextIndex); } void irgen::emitYieldOnceCoroutineEntry( @@ -3849,26 +3873,46 @@ void irgen::emitTaskCancel(IRGenFunction &IGF, llvm::Value *task) { } llvm::Value *irgen::emitTaskCreate( - IRGenFunction &IGF, llvm::Value *flags, + IRGenFunction &IGF, + llvm::Value *flags, llvm::Value *taskGroup, llvm::Value *futureResultType, - llvm::Value *taskFunction, llvm::Value *localContextInfo, + llvm::Value *taskFunction, + llvm::Value *localContextInfo, SubstitutionMap subs) { - llvm::CallInst *result; - if (taskGroup && futureResultType) { - taskGroup = IGF.Builder.CreateBitOrPointerCast( - taskGroup, IGF.IGM.SwiftTaskGroupPtrTy); - result = IGF.Builder.CreateCall( - IGF.IGM.getTaskCreateGroupFutureFn(), - {flags, taskGroup, futureResultType, - taskFunction, localContextInfo}); - } else if (futureResultType) { - result = IGF.Builder.CreateCall( - IGF.IGM.getTaskCreateFutureFn(), - {flags, futureResultType, taskFunction, localContextInfo}); - } else { - llvm_unreachable("no future?!"); - } + // If there is a task group, emit a task group option structure to contain + // it. + llvm::Value *taskOptions = llvm::ConstantInt::get( + IGF.IGM.SwiftTaskOptionRecordPtrTy, 0); + if (taskGroup) { + TaskOptionRecordFlags optionsFlags(TaskOptionRecordKind::TaskGroup); + llvm::Value *optionsFlagsVal = llvm::ConstantInt::get( + IGF.IGM.SizeTy, optionsFlags.getOpaqueValue()); + + auto optionsRecord = IGF.createAlloca( + IGF.IGM.SwiftTaskGroupTaskOptionRecordTy, Alignment(), + "task_group_options"); + auto optionsBaseRecord = IGF.Builder.CreateStructGEP( + optionsRecord, 0, Size()); + IGF.Builder.CreateStore( + optionsFlagsVal, + IGF.Builder.CreateStructGEP(optionsBaseRecord, 0, Size())); + IGF.Builder.CreateStore( + taskOptions, IGF.Builder.CreateStructGEP(optionsBaseRecord, 1, Size())); + + IGF.Builder.CreateStore( + taskGroup, IGF.Builder.CreateStructGEP(optionsRecord, 1, Size())); + taskOptions = IGF.Builder.CreateBitOrPointerCast( + optionsRecord.getAddress(), IGF.IGM.SwiftTaskOptionRecordPtrTy); + } + + assert(futureResultType && "no future?!"); + llvm::CallInst *result = IGF.Builder.CreateCall( + IGF.IGM.getTaskCreateFn(), + {flags, + taskOptions, + futureResultType, + taskFunction, localContextInfo}); result->setDoesNotThrow(); result->setCallingConv(IGF.IGM.SwiftCC); @@ -3888,6 +3932,20 @@ void irgen::emitDeallocAsyncContext(IRGenFunction &IGF, Address context) { IGF.Builder.CreateLifetimeEnd(context, Size(-1) /*dynamic size*/); } +Address irgen::emitStaticAllocAsyncContext(IRGenFunction &IGF, + Size size) { + auto alignment = IGF.IGM.getAsyncContextAlignment(); + auto &IGM = IGF.IGM; + auto address = IGF.createAlloca(IGM.Int8Ty, IGM.getSize(size), alignment); + IGF.Builder.CreateLifetimeStart(address, size); + return address; +} + +void irgen::emitStaticDeallocAsyncContext(IRGenFunction &IGF, Address context, + Size size) { + IGF.Builder.CreateLifetimeEnd(context, size); +} + llvm::Value *irgen::emitYield(IRGenFunction &IGF, CanSILFunctionType coroutineType, Explosion &substValues) { @@ -4011,6 +4069,11 @@ Address IRGenFunction::createErrorResultSlot(SILType errorType, bool isAsync) { auto addr = createAlloca(errorTI.getStorageType(), errorTI.getFixedAlignment(), "swifterror"); + if (!isAsync) { + builder.SetInsertPoint(getEarliestInsertionPoint()->getParent(), + getEarliestInsertionPoint()->getIterator()); + } + // Only add the swifterror attribute on ABIs that pass it in a register. // We create a shadow stack location of the swifterror parameter for the // debugger on platforms that pass swifterror by reference and so we can't @@ -4088,6 +4151,7 @@ void IRGenFunction::emitPrologue() { AllocaIP = Builder.IRBuilderBase::CreateAlloca(IGM.Int1Ty, /*array size*/ nullptr, "alloca point"); + EarliestIP = AllocaIP; } /// Emit a branch to the return block and set the insert point there. @@ -4127,6 +4191,8 @@ bool IRGenFunction::emitBranchToReturnBB() { /// Emit the epilogue for the function. void IRGenFunction::emitEpilogue() { + if (EarliestIP != AllocaIP) + EarliestIP->eraseFromParent(); // Destroy the alloca insertion point. AllocaIP->eraseFromParent(); } @@ -4915,8 +4981,11 @@ IRGenFunction::getFunctionPointerForResumeIntrinsic(llvm::Value *resume) { auto *fnTy = llvm::FunctionType::get( IGM.VoidTy, {IGM.Int8PtrTy}, false /*vaargs*/); + auto attrs = IGM.constructInitialAttributes(); + attrs = attrs.addParamAttribute(IGM.getLLVMContext(), 0, + llvm::Attribute::SwiftAsync); auto signature = - Signature(fnTy, IGM.constructInitialAttributes(), IGM.SwiftAsyncCC); + Signature(fnTy, attrs, IGM.SwiftAsyncCC); auto fnPtr = FunctionPointer( FunctionPointer::Kind::Function, Builder.CreateBitOrPointerCast(resume, fnTy->getPointerTo()), diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index bb82f7ce9969b..0172fdf0ff630 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -120,7 +120,7 @@ namespace irgen { CanSILFunctionType originalType, CanSILFunctionType substitutedType, SubstitutionMap substitutionMap, - bool suppressGenerics, + bool useSpecialConvention, FunctionPointer::Kind kind); /// Given an async function, get the pointer to the function to be called and @@ -224,21 +224,23 @@ namespace irgen { void emitTaskCancel(IRGenFunction &IGF, llvm::Value *task); - /// Emit a class to swift_task_create[_f] or swift_task_create_future[__f] - /// with the given flags, parent task, and task function. - /// - /// When \c futureResultType is non-null, calls the future variant to create - /// a future. + /// Emit a call to swift_task_create[_f] with the given flags, options, and + /// task function. llvm::Value *emitTaskCreate( - IRGenFunction &IGF, llvm::Value *flags, + IRGenFunction &IGF, + llvm::Value *flags, llvm::Value *taskGroup, llvm::Value *futureResultType, - llvm::Value *taskFunction, llvm::Value *localContextInfo, + llvm::Value *taskFunction, + llvm::Value *localContextInfo, SubstitutionMap subs); /// Allocate task local storage for the provided dynamic size. Address emitAllocAsyncContext(IRGenFunction &IGF, llvm::Value *sizeValue); void emitDeallocAsyncContext(IRGenFunction &IGF, Address context); + Address emitStaticAllocAsyncContext(IRGenFunction &IGF, Size size); + void emitStaticDeallocAsyncContext(IRGenFunction &IGF, Address context, + Size size); void emitAsyncFunctionEntry(IRGenFunction &IGF, const AsyncContextLayout &layout, diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 852cb7c518c0f..9e5979d6abcbf 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -2461,7 +2461,7 @@ ClassDecl *IRGenModule::getObjCRuntimeBaseClass(Identifier name, // Make a really fake-looking class. auto SwiftRootClass = new (Context) ClassDecl(SourceLoc(), name, SourceLoc(), - ArrayRef(), + ArrayRef(), /*generics*/ nullptr, Context.TheBuiltinModule, /*isActor*/false); @@ -2712,7 +2712,7 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, signature); } } - + llvm_unreachable("covered switch"); } FunctionPointer diff --git a/lib/IRGen/GenConcurrency.cpp b/lib/IRGen/GenConcurrency.cpp index a93b4afd98d64..1437e7277297f 100644 --- a/lib/IRGen/GenConcurrency.cpp +++ b/lib/IRGen/GenConcurrency.cpp @@ -21,6 +21,7 @@ #include "ExtraInhabitants.h" #include "GenProto.h" #include "GenType.h" +#include "IRGenDebugInfo.h" #include "IRGenFunction.h" #include "IRGenModule.h" #include "LoadableTypeInfo.h" @@ -127,7 +128,12 @@ const LoadableTypeInfo &TypeConverter::getExecutorTypeInfo() { void irgen::emitBuildMainActorExecutorRef(IRGenFunction &IGF, Explosion &out) { - // FIXME + auto call = IGF.Builder.CreateCall(IGF.IGM.getTaskGetMainExecutorFn(), + {}); + call->setDoesNotThrow(); + call->setCallingConv(IGF.IGM.SwiftCC); + + IGF.emitAllExtractValues(call, IGF.IGM.SwiftExecutorTy, out); } void irgen::emitBuildDefaultActorExecutorRef(IRGenFunction &IGF, @@ -169,8 +175,10 @@ void irgen::emitGetCurrentExecutor(IRGenFunction &IGF, Explosion &out) { } llvm::Value *irgen::emitBuiltinStartAsyncLet(IRGenFunction &IGF, + llvm::Value *taskOptions, llvm::Value *taskFunction, llvm::Value *localContextInfo, + llvm::Value *localResultBuffer, SubstitutionMap subs) { // stack allocate AsyncLet, and begin lifetime for it (until EndAsyncLet) auto ty = llvm::ArrayType::get(IGF.IGM.Int8PtrTy, NumWords_AsyncLet); @@ -184,13 +192,27 @@ llvm::Value *irgen::emitBuiltinStartAsyncLet(IRGenFunction &IGF, auto futureResultType = subs.getReplacementTypes()[0]->getCanonicalType(); auto futureResultTypeMetadata = IGF.emitAbstractTypeMetadataRef(futureResultType); - // This is @_silgen_name("swift_asyncLet_start") - auto *call = IGF.Builder.CreateCall(IGF.IGM.getAsyncLetStartFn(), + llvm::CallInst *call; + if (localResultBuffer) { + // This is @_silgen_name("swift_asyncLet_begin") + call = IGF.Builder.CreateCall(IGF.IGM.getAsyncLetBeginFn(), + {alet, + taskOptions, + futureResultTypeMetadata, + taskFunction, + localContextInfo, + localResultBuffer + }); + } else { + // This is @_silgen_name("swift_asyncLet_start") + call = IGF.Builder.CreateCall(IGF.IGM.getAsyncLetStartFn(), {alet, + taskOptions, futureResultTypeMetadata, taskFunction, localContextInfo }); + } call->setDoesNotThrow(); call->setCallingConv(IGF.IGM.SwiftCC); @@ -206,15 +228,20 @@ void irgen::emitEndAsyncLet(IRGenFunction &IGF, llvm::Value *alet) { IGF.Builder.CreateLifetimeEnd(alet); } -llvm::Value *irgen::emitCreateTaskGroup(IRGenFunction &IGF) { +llvm::Value *irgen::emitCreateTaskGroup(IRGenFunction &IGF, + SubstitutionMap subs) { auto ty = llvm::ArrayType::get(IGF.IGM.Int8PtrTy, NumWords_TaskGroup); auto address = IGF.createAlloca(ty, Alignment(Alignment_TaskGroup)); auto group = IGF.Builder.CreateBitCast(address.getAddress(), IGF.IGM.Int8PtrTy); IGF.Builder.CreateLifetimeStart(group); + assert(subs.getReplacementTypes().size() == 1 && + "createTaskGroup should have a type substitution"); + auto resultType = subs.getReplacementTypes()[0]->getCanonicalType(); + auto resultTypeMetadata = IGF.emitAbstractTypeMetadataRef(resultType); auto *call = IGF.Builder.CreateCall(IGF.IGM.getTaskGroupInitializeFn(), - {group}); + {group, resultTypeMetadata}); call->setDoesNotThrow(); call->setCallingConv(IGF.IGM.SwiftCC); @@ -229,3 +256,34 @@ void irgen::emitDestroyTaskGroup(IRGenFunction &IGF, llvm::Value *group) { IGF.Builder.CreateLifetimeEnd(group); } + +llvm::Function *IRGenModule::getAwaitAsyncContinuationFn() { + StringRef name = "__swift_continuation_await_point"; + if (llvm::GlobalValue *F = Module.getNamedValue(name)) + return cast(F); + + // The parameters here match the extra arguments passed to + // @llvm.coro.suspend.async by emitAwaitAsyncContinuation. + llvm::Type *argTys[] = { ContinuationAsyncContextPtrTy }; + auto *suspendFnTy = + llvm::FunctionType::get(VoidTy, argTys, false /*vaargs*/); + + llvm::Function *suspendFn = + llvm::Function::Create(suspendFnTy, llvm::Function::InternalLinkage, + name, &Module); + suspendFn->setCallingConv(SwiftAsyncCC); + suspendFn->setDoesNotThrow(); + IRGenFunction suspendIGF(*this, suspendFn); + if (DebugInfo) + DebugInfo->emitArtificialFunction(suspendIGF, suspendFn); + auto &Builder = suspendIGF.Builder; + + llvm::Value *context = suspendFn->getArg(0); + auto *call = Builder.CreateCall(getContinuationAwaitFn(), { context }); + call->setDoesNotThrow(); + call->setCallingConv(SwiftAsyncCC); + call->setTailCallKind(AsyncTailCallKind); + + Builder.CreateRetVoid(); + return suspendFn; +} diff --git a/lib/IRGen/GenConcurrency.h b/lib/IRGen/GenConcurrency.h index 3ea9fa5e84bd0..e1cb41bc4d8c1 100644 --- a/lib/IRGen/GenConcurrency.h +++ b/lib/IRGen/GenConcurrency.h @@ -59,15 +59,17 @@ void emitGetCurrentExecutor(IRGenFunction &IGF, Explosion &out); /// Emit the createAsyncLet builtin. llvm::Value *emitBuiltinStartAsyncLet(IRGenFunction &IGF, + llvm::Value *taskOptions, llvm::Value *taskFunction, llvm::Value *localContextInfo, + llvm::Value *resultBuffer, SubstitutionMap subs); /// Emit the endAsyncLet builtin. void emitEndAsyncLet(IRGenFunction &IGF, llvm::Value *alet); /// Emit the createTaskGroup builtin. -llvm::Value *emitCreateTaskGroup(IRGenFunction &IGF); +llvm::Value *emitCreateTaskGroup(IRGenFunction &IGF, SubstitutionMap subs); /// Emit the destroyTaskGroup builtin. void emitDestroyTaskGroup(IRGenFunction &IGF, llvm::Value *group); diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index b1ad5fb31534b..676799f3ec487 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -85,7 +85,7 @@ llvm::cl::opt UseBasicDynamicReplacement( llvm::cl::desc("Basic implementation of dynamic replacement")); namespace { - + /// Add methods, properties, and protocol conformances from a JITed extension /// to an ObjC class using the ObjC runtime. /// @@ -119,8 +119,8 @@ class CategoryInitializerVisitor /*allow uninitialized*/ false); classMetadata = Builder.CreateBitCast(classMetadata, IGM.ObjCClassPtrTy); metaclassMetadata = IGM.getAddrOfMetaclassObject( - origTy.getClassOrBoundGenericClass(), - NotForDefinition); + dyn_cast_or_null(origTy->getAnyNominal()), + NotForDefinition); metaclassMetadata = llvm::ConstantExpr::getBitCast(metaclassMetadata, IGM.ObjCClassPtrTy); @@ -485,6 +485,9 @@ void IRGenModule::emitSourceFile(SourceFile &SF) { if (libraryName == "swiftCompatibilityDynamicReplacements") { compatibilityVersion = IRGen.Opts. AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion; + } else if (libraryName == "swiftCompatibilityConcurrency") { + compatibilityVersion = + IRGen.Opts.AutolinkRuntimeCompatibilityConcurrencyLibraryVersion; } else { compatibilityVersion = IRGen.Opts. AutolinkRuntimeCompatibilityLibraryVersion; @@ -570,8 +573,14 @@ emitGlobalList(IRGenModule &IGM, ArrayRef handles, auto var = new llvm::GlobalVariable(IGM.Module, varTy, isConstant, linkage, init, name); var->setSection(section); - var->setAlignment(llvm::MaybeAlign(alignment.getValue())); - disableAddressSanitizer(IGM, var); + + // Do not set alignment and don't set disableAddressSanitizer on @llvm.used + // and @llvm.compiler.used. Doing so confuses LTO (merging) and they're not + // going to end up as real global symbols in the binary anyways. + if (name != "llvm.used" && name != "llvm.compiler.used") { + var->setAlignment(llvm::MaybeAlign(alignment.getValue())); + disableAddressSanitizer(IGM, var); + } // Mark the variable as used if doesn't have external linkage. // (Note that we'd specifically like to not put @llvm.used in itself.) @@ -585,8 +594,7 @@ void IRGenModule::emitRuntimeRegistration() { if (SwiftProtocols.empty() && ProtocolConformances.empty() && RuntimeResolvableTypes.empty() && (!ObjCInterop || (ObjCProtocols.empty() && ObjCClasses.empty() && - ObjCCategoryDecls.empty())) && - FieldDescriptors.empty()) + ObjCCategoryDecls.empty()))) return; // Find the entry point. @@ -748,10 +756,6 @@ void IRGenModule::emitRuntimeRegistration() { } } - if (!FieldDescriptors.empty()) { - emitFieldDescriptors(); - } - RegIGF.Builder.CreateRetVoid(); } @@ -2055,13 +2059,20 @@ LinkInfo LinkInfo::get(const UniversalLinkageInfo &linkInfo, const LinkEntity &entity, ForDefinition_t isDefinition) { LinkInfo result; + entity.mangle(result.Name); bool isKnownLocal = entity.isAlwaysSharedLinkage(); - if (const auto *DC = entity.getDeclContextForEmission()) + if (const auto *DC = entity.getDeclContextForEmission()) { if (const auto *MD = DC->getParentModule()) isKnownLocal = MD == swiftModule || MD->isStaticLibrary(); + } else if (entity.hasSILFunction()) { + // SIL serialized entitites (functions, witness tables, vtables) do not have + // an associated DeclContext and are serialized into the current module. As + // a result, we explicitly handle SIL Functions here. We do not expect other + // types to be referenced directly. + isKnownLocal = entity.getSILFunction()->isStaticallyLinked(); + } - entity.mangle(result.Name); bool weakImported = entity.isWeakImported(swiftModule); result.IRL = getIRLinkage(linkInfo, entity.getLinkage(isDefinition), isDefinition, weakImported, isKnownLocal); @@ -2648,7 +2659,9 @@ void IRGenModule::createReplaceableProlog(IRGenFunction &IGF, SILFunction *f) { asyncFnPtr.getAuthInfo().getCorrespondingCodeAuthInfo(); auto newFnPtr = FunctionPointer( FunctionPointer::Kind::Function, asyncFnPtr.getPointer(IGF), - codeAuthInfo, Signature::forAsyncAwait(IGM, silFunctionType)); + codeAuthInfo, + Signature::forAsyncAwait(IGM, silFunctionType, + /*useSpecialConvention*/ false)); SmallVector forwardedArgs; for (auto &arg : IGF.CurFn->args()) forwardedArgs.push_back(&arg); @@ -2955,6 +2968,10 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) { FunctionPointer(fnType, typeFnPtr, authInfo, signature) .getAsFunction(IGF), forwardedArgs); + Res->setTailCall(); + if (f->isAsync()) { + Res->setTailCallKind(IGF.IGM.AsyncTailCallKind); + } if (implFn->getReturnType()->isVoidTy()) IGF.Builder.CreateRetVoid(); @@ -3103,8 +3120,9 @@ llvm::Function *IRGenModule::getAddrOfSILFunction( isLazilyEmittedFunction(*f, getSILModule())) { IRGen.addLazyFunction(f); } - - Signature signature = getSignature(f->getLoweredFunctionType()); + auto fpKind = irgen::classifyFunctionPointerKind(f); + Signature signature = + getSignature(f->getLoweredFunctionType(), fpKind.useSpecialConvention()); addLLVMFunctionAttributes(f, signature); LinkInfo link = LinkInfo::get(*this, entity, forDefinition); @@ -3817,58 +3835,6 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords() { return var; } -llvm::Constant *IRGenModule::emitFieldDescriptors() { - std::string sectionName; - switch (TargetInfo.OutputObjectFormat) { - case llvm::Triple::MachO: - sectionName = "__TEXT, __swift5_fieldmd, regular, no_dead_strip"; - break; - case llvm::Triple::ELF: - case llvm::Triple::Wasm: - sectionName = "swift5_fieldmd"; - break; - case llvm::Triple::XCOFF: - case llvm::Triple::COFF: - sectionName = ".sw5flmd$B"; - break; - case llvm::Triple::GOFF: - case llvm::Triple::UnknownObjectFormat: - llvm_unreachable("Don't know how to emit field records table for " - "the selected object format."); - } - - // Do nothing if the list is empty. - if (FieldDescriptors.empty()) - return nullptr; - - // Define the global variable for the field record list. - // We have to do this before defining the initializer since the entries will - // contain offsets relative to themselves. - auto arrayTy = - llvm::ArrayType::get(FieldDescriptorPtrTy, FieldDescriptors.size()); - - // FIXME: This needs to be a linker-local symbol in order for Darwin ld to - // resolve relocations relative to it. - auto var = new llvm::GlobalVariable( - Module, arrayTy, - /*isConstant*/ true, llvm::GlobalValue::PrivateLinkage, - /*initializer*/ nullptr, "\x01l_type_metadata_table"); - - SmallVector elts; - for (auto *descriptor : FieldDescriptors) - elts.push_back( - llvm::ConstantExpr::getBitCast(descriptor, FieldDescriptorPtrTy)); - - var->setInitializer(llvm::ConstantArray::get(arrayTy, elts)); - var->setSection(sectionName); - var->setAlignment(llvm::MaybeAlign(4)); - - disableAddressSanitizer(*this, var); - - addUsedGlobal(var); - return var; -} - /// Fetch a global reference to a reference to the given Objective-C class. /// The result is of type ObjCClassPtrTy->getPointerTo(). Address IRGenModule::getAddrOfObjCClassRef(ClassDecl *theClass) { diff --git a/lib/IRGen/GenDistributed.cpp b/lib/IRGen/GenDistributed.cpp new file mode 100644 index 0000000000000..ecb7b57bba1c0 --- /dev/null +++ b/lib/IRGen/GenDistributed.cpp @@ -0,0 +1,65 @@ +//===--- GenDistributed.cpp - IRGen for distributed features --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2021 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 implements IR generation for distributed features. +// +//===----------------------------------------------------------------------===// + +#include "GenDistributed.h" + +#include "BitPatternBuilder.h" +#include "ClassTypeInfo.h" +#include "ExtraInhabitants.h" +#include "GenProto.h" +#include "GenType.h" +#include "IRGenDebugInfo.h" +#include "IRGenFunction.h" +#include "IRGenModule.h" +#include "LoadableTypeInfo.h" +#include "ScalarPairTypeInfo.h" +#include "swift/AST/ProtocolConformanceRef.h" +#include "swift/ABI/MetadataValues.h" + +using namespace swift; +using namespace irgen; + +llvm::Value *irgen::emitDistributedActorInitializeRemote( + IRGenFunction &IGF, SILType selfType, llvm::Value *actorMetatype, Explosion &out) { + auto &classTI = IGF.getTypeInfo(selfType).as(); + auto &classLayout = classTI.getClassLayout(IGF.IGM, selfType, + /*forBackwardDeployment=*/false); + llvm::Type *destType = classLayout.getType()->getPointerTo(); + + auto fn = IGF.IGM.getDistributedActorInitializeRemoteFn(); + actorMetatype = + IGF.Builder.CreateBitCast(actorMetatype, IGF.IGM.TypeMetadataPtrTy); + + auto call = IGF.Builder.CreateCall(fn, {actorMetatype}); + call->setCallingConv(IGF.IGM.SwiftCC); + call->setDoesNotThrow(); + + auto result = IGF.Builder.CreateBitCast(call, destType); + + out.add(result); + + return result; +} + +void irgen::emitDistributedActorDestroy(IRGenFunction &IGF, + llvm::Value *actor) { + auto fn = IGF.IGM.getDistributedActorDestroyFn(); + actor = IGF.Builder.CreateBitCast(actor, IGF.IGM.RefCountedPtrTy); + + auto call = IGF.Builder.CreateCall(fn, {actor}); + call->setCallingConv(IGF.IGM.SwiftCC); + call->setDoesNotThrow(); +} \ No newline at end of file diff --git a/lib/IRGen/GenDistributed.h b/lib/IRGen/GenDistributed.h new file mode 100644 index 0000000000000..97df8f259335e --- /dev/null +++ b/lib/IRGen/GenDistributed.h @@ -0,0 +1,59 @@ +//===--- GenDistributed.h - IRGen for distributed features ------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2021 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 defines interfaces for emitting code for various distributed +// features. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IRGEN_GENDISTRIBUTED_H +#define SWIFT_IRGEN_GENDISTRIBUTED_H + +#include "swift/AST/Types.h" +#include "swift/Basic/LLVM.h" +#include "swift/SIL/ApplySite.h" +#include "llvm/IR/CallingConv.h" + +#include "Callee.h" +#include "GenHeap.h" +#include "IRGenModule.h" + +namespace llvm { +class Value; +} + +namespace swift { +class CanType; +class ProtocolConformanceRef; +class SILType; + +namespace irgen { +class Explosion; +class IRGenFunction; + +/// Emit the '_distributedActorRemoteInitialize' call. +llvm::Value *emitDistributedActorInitializeRemote( + IRGenFunction &IGF, + SILType selfType, + llvm::Value *actorMetatype, + Explosion &out); + +/// Destroy the distributed actor. +/// E.g. a remote actor has to be destroyed differently from a local one. +void emitDistributedActorDestroy( + IRGenFunction &IGF, + llvm::Value *actor); + +} // end namespace irgen +} // end namespace swift + +#endif diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index b97b3f5a530be..1b57f9368611f 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -593,12 +593,11 @@ getFuncSignatureInfoForLowered(IRGenModule &IGM, CanSILFunctionType type) { llvm_unreachable("bad function type representation"); } -Signature -IRGenModule::getSignature(CanSILFunctionType type, - bool suppressGenerics) { +Signature IRGenModule::getSignature(CanSILFunctionType type, + bool useSpecialConvention) { // Don't bother caching if we've been asked to suppress generics. - if (suppressGenerics) - return Signature::getUncached(*this, type, suppressGenerics); + if (useSpecialConvention) + return Signature::getUncached(*this, type, useSpecialConvention); auto &sigInfo = getFuncSignatureInfoForLowered(*this, type); return sigInfo.getSignature(*this); @@ -1071,7 +1070,7 @@ class AsyncPartialApplicationForwarderEmission substType, outType, subs, layout, conventions), layout(getAsyncContextLayout( subIGF.IGM, origType, substType, subs, - staticFnPtr ? staticFnPtr->suppressGenerics() : false, + staticFnPtr ? staticFnPtr->useSpecialConvention() : false, FunctionPointer::Kind( FunctionPointer::BasicKind::AsyncFunctionPointer))), currentArgumentIndex(outType->getNumParameters()) {} @@ -1160,7 +1159,8 @@ class AsyncPartialApplicationForwarderEmission fnPtr.getAuthInfo().getCorrespondingCodeAuthInfo(); auto newFnPtr = FunctionPointer( FunctionPointer::Kind::Function, fnPtr.getPointer(subIGF), newAuthInfo, - Signature::forAsyncAwait(subIGF.IGM, origType)); + Signature::forAsyncAwait(subIGF.IGM, origType, + /*useSpecialConvention*/ false)); auto &Builder = subIGF.Builder; auto argValues = args.claimAll(); @@ -1268,14 +1268,13 @@ static llvm::Value *emitPartialApplicationForwarder(IRGenModule &IGM, IRGenFunction subIGF(IGM, fwd); if (origType->isAsync()) { auto asyncContextIdx = - Signature::forAsyncEntry(IGM, outType).getAsyncContextIndex(); + Signature::forAsyncEntry(IGM, outType, /*useSpecialConvention*/ false) + .getAsyncContextIndex(); asyncLayout.emplace(irgen::getAsyncContextLayout( IGM, origType, substType, subs, /*suppress generics*/ false, FunctionPointer::Kind( FunctionPointer::BasicKind::AsyncFunctionPointer))); - subIGF.setupAsync(asyncContextIdx); - //auto *calleeAFP = staticFnPtr->getDirectPointer(); LinkEntity entity = LinkEntity::forPartialApplyForwarder(fwd); assert(!asyncFunctionPtr && @@ -1372,8 +1371,9 @@ static llvm::Value *emitPartialApplicationForwarder(IRGenModule &IGM, // captured arguments which we will do later. Otherwise, we have to // potentially bind polymorphic arguments from the context if it was a // partially applied argument. - bool hasPolymorphicParams = hasPolymorphicParameters(origType) && - (!staticFnPtr || !staticFnPtr->suppressGenerics()); + bool hasPolymorphicParams = + hasPolymorphicParameters(origType) && + (!staticFnPtr || !staticFnPtr->useSpecialConvention()); if (!layout && hasPolymorphicParams) { assert(conventions.size() == 1); // We could have either partially applied an argument from the function diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index 1a5f18da2e2f7..f8f992452b9d6 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -1343,21 +1343,34 @@ llvm::Value *IRGenFunction::emitLoadRefcountedPtr(Address addr, llvm::Value *IRGenFunction:: emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull) { llvm::Constant *fn; + bool nonObjC = !IGM.getAvailabilityContext().isContainedIn( + IGM.Context.getObjCIsUniquelyReferencedAvailability()); + if (value->getType() == IGM.RefCountedPtrTy) { if (isNonNull) fn = IGM.getIsUniquelyReferenced_nonNull_nativeFn(); else fn = IGM.getIsUniquelyReferenced_nativeFn(); } else if (value->getType() == IGM.UnknownRefCountedPtrTy) { - if (isNonNull) - fn = IGM.getIsUniquelyReferencedNonObjC_nonNullFn(); - else - fn = IGM.getIsUniquelyReferencedNonObjCFn(); + if (nonObjC) { + if (isNonNull) + fn = IGM.getIsUniquelyReferencedNonObjC_nonNullFn(); + else + fn = IGM.getIsUniquelyReferencedNonObjCFn(); + } else { + if (isNonNull) + fn = IGM.getIsUniquelyReferenced_nonNullFn(); + else + fn = IGM.getIsUniquelyReferencedFn(); + } } else if (value->getType() == IGM.BridgeObjectPtrTy) { if (!isNonNull) unimplemented(loc, "optional bridge ref"); - fn = IGM.getIsUniquelyReferencedNonObjC_nonNull_bridgeObjectFn(); + if (nonObjC) + fn = IGM.getIsUniquelyReferencedNonObjC_nonNull_bridgeObjectFn(); + else + fn = IGM.getIsUniquelyReferenced_nonNull_bridgeObjectFn(); } else { llvm_unreachable("Unexpected LLVM type for a refcounted pointer."); } diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 01d0973d1bbd1..47eac770c18ba 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -1185,12 +1185,9 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern, // Collect the required parameters for the keypath's generic environment. SmallVector requirements; - GenericEnvironment *genericEnv = nullptr; - if (auto sig = pattern->getGenericSignature()) { - genericEnv = sig->getGenericEnvironment(); - enumerateGenericSignatureRequirements(pattern->getGenericSignature(), - [&](GenericRequirement reqt) { requirements.push_back(reqt); }); - } + auto *genericEnv = pattern->getGenericSignature().getGenericEnvironment(); + enumerateGenericSignatureRequirements(pattern->getGenericSignature(), + [&](GenericRequirement reqt) { requirements.push_back(reqt); }); // Start building the key path pattern. ConstantInitBuilder builder(*this); diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 16f04db649835..a8c664898c4a0 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -415,15 +415,15 @@ namespace { }); // Pad the structure up to four bytes for the following requirements. - unsigned padding = (unsigned) -canSig->getGenericParams().size() & 3; + unsigned padding = (unsigned) -canSig.getGenericParams().size() & 3; for (unsigned i = 0; i < padding; ++i) B.addInt(IGM.Int8Ty, 0); // Fill in the parameter count. - assert(canSig->getGenericParams().size() <= UINT16_MAX + assert(canSig.getGenericParams().size() <= UINT16_MAX && "way too generic"); B.fillPlaceholderWithInt(*GenericParamCount, IGM.Int16Ty, - canSig->getGenericParams().size()); + canSig.getGenericParams().size()); } void addGenericParameter(GenericParamKind kind, @@ -442,7 +442,7 @@ namespace { auto metadata = irgen::addGenericRequirements(IGM, B, asImpl().getGenericSignature(), - asImpl().getGenericSignature()->getRequirements()); + asImpl().getGenericSignature().getRequirements()); // Fill in the final requirement count. assert(metadata.NumRequirements <= UINT16_MAX @@ -916,6 +916,10 @@ namespace { if (!entry.isValid() || entry.getKind() != SILWitnessTable::Method || entry.getMethodWitness().Requirement != func) continue; + auto silFunc = entry.getMethodWitness().Witness; + if (silFunc->isAsync()) { + return IGM.getAddrOfAsyncFunctionPointer(silFunc); + } return IGM.getAddrOfSILFunction(entry.getMethodWitness().Witness, NotForDefinition); } @@ -2946,6 +2950,7 @@ namespace { swift::irgen::ConstantReference::Direct); } } + llvm_unreachable("covered switch"); } void addValueWitnessTable() { @@ -3004,6 +3009,7 @@ namespace { case ClassMetadataStrategy::Fixed: return false; } + llvm_unreachable("covered switch"); } void addSuperclass() { @@ -5159,10 +5165,14 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::Differentiable: case KnownProtocolKind::FloatingPoint: case KnownProtocolKind::Actor: + case KnownProtocolKind::ActorTransport: + case KnownProtocolKind::DistributedActor: + case KnownProtocolKind::ActorIdentity: case KnownProtocolKind::SerialExecutor: case KnownProtocolKind::Sendable: case KnownProtocolKind::UnsafeSendable: case KnownProtocolKind::RangeReplaceableCollection: + case KnownProtocolKind::GlobalActor: return SpecialProtocol::None; } diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index 010a9e7f6aeba..74b9c638fd34d 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -473,6 +473,7 @@ namespace { case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: + case SILDeclRef::Kind::EntryPoint: llvm_unreachable("Method does not have a selector"); case SILDeclRef::Kind::Destroyer: diff --git a/lib/IRGen/GenOpaque.cpp b/lib/IRGen/GenOpaque.cpp index b99a38c41134e..d70eff9ab24fd 100644 --- a/lib/IRGen/GenOpaque.cpp +++ b/lib/IRGen/GenOpaque.cpp @@ -321,7 +321,7 @@ llvm::Value *irgen::emitInvariantLoadOfOpaqueWitness(IRGenFunction &IGF, llvm::Value *slot = table; if (index.getValue() != 0) slot = IGF.Builder.CreateConstInBoundsGEP1_32( - /*Ty=*/nullptr, table, index.getValue()); + table->getType()->getPointerElementType(), table, index.getValue()); if (slotPtr) *slotPtr = slot; diff --git a/lib/IRGen/GenPointerAuth.cpp b/lib/IRGen/GenPointerAuth.cpp index 8222676ddeecb..df719af21a86d 100644 --- a/lib/IRGen/GenPointerAuth.cpp +++ b/lib/IRGen/GenPointerAuth.cpp @@ -523,8 +523,7 @@ static uint64_t getTypeHash(IRGenModule &IGM, CanSILFunctionType type) { auto genericSig = type->getInvocationGenericSignature(); hashStringForFunctionType( IGM, type, Out, - genericSig ? genericSig->getCanonicalSignature()->getGenericEnvironment() - : nullptr); + genericSig.getCanonicalSignature().getGenericEnvironment()); return clang::CodeGen::computeStableStringHash(Out.str()); } @@ -532,9 +531,7 @@ static uint64_t getYieldTypesHash(IRGenModule &IGM, CanSILFunctionType type) { SmallString<32> buffer; llvm::raw_svector_ostream out(buffer); auto genericSig = type->getInvocationGenericSignature(); - GenericEnvironment *genericEnv = - genericSig ? genericSig->getCanonicalSignature()->getGenericEnvironment() - : nullptr; + auto *genericEnv = genericSig.getCanonicalSignature().getGenericEnvironment(); out << [&]() -> StringRef { switch (type->getCoroutineKind()) { diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 99aca5aa2157a..21efbaec2d307 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -134,8 +134,10 @@ class PolymorphicConvention { private: void initGenerics(); - void considerNewTypeSource(MetadataSource::Kind kind, unsigned paramIndex, - CanType type, IsExact_t isExact); + + template + void considerNewTypeSource(IsExact_t isExact, MetadataSource::Kind kind, + CanType type, Args... args); bool considerType(CanType type, IsExact_t isExact, unsigned sourceIndex, MetadataPath &&path); @@ -234,12 +236,17 @@ PolymorphicConvention::PolymorphicConvention(IRGenModule &IGM, void PolymorphicConvention::addPseudogenericFulfillments() { enumerateRequirements([&](GenericRequirement reqt) { - MetadataPath path; - path.addImpossibleComponent(); + auto archetype = Generics.getGenericEnvironment() + ->mapTypeIntoContext(reqt.TypeParameter) + ->getAs(); + assert(archetype && "did not get an archetype by mapping param?"); + auto erasedTypeParam = archetype->getExistentialType()->getCanonicalType(); + Sources.emplace_back(MetadataSource::Kind::ErasedTypeMetadata, + reqt.TypeParameter, erasedTypeParam); - unsigned sourceIndex = 0; // unimportant, since impossible + MetadataPath path; Fulfillments.addFulfillment({reqt.TypeParameter, reqt.Protocol}, - sourceIndex, std::move(path), + Sources.size() - 1, std::move(path), MetadataState::Complete); }); } @@ -256,7 +263,7 @@ irgen::enumerateGenericSignatureRequirements(CanGenericSignature signature, }); // Get the protocol conformances. - for (auto &reqt : signature->getRequirements()) { + for (auto &reqt : signature.getRequirements()) { switch (reqt.getKind()) { // Ignore these; they don't introduce extra requirements. case RequirementKind::Superclass: @@ -302,14 +309,15 @@ void PolymorphicConvention::initGenerics() { Generics = FnType->getInvocationGenericSignature(); } -void PolymorphicConvention::considerNewTypeSource(MetadataSource::Kind kind, - unsigned paramIndex, +template +void PolymorphicConvention::considerNewTypeSource(IsExact_t isExact, + MetadataSource::Kind kind, CanType type, - IsExact_t isExact) { + Args... args) { if (!Fulfillments.isInterestingTypeForFulfillments(type)) return; // Prospectively add a source. - Sources.emplace_back(kind, paramIndex, type); + Sources.emplace_back(kind, type, std::forward(args)...); // Consider the source. if (!considerType(type, isExact, Sources.size() - 1, MetadataPath())) { @@ -333,9 +341,7 @@ void PolymorphicConvention::considerWitnessSelf(CanSILFunctionType fnType) { auto conformance = fnType->getWitnessMethodConformanceOrInvalid(); // First, bind type metadata for Self. - Sources.emplace_back(MetadataSource::Kind::SelfMetadata, - MetadataSource::InvalidSourceIndex, - selfTy); + Sources.emplace_back(MetadataSource::Kind::SelfMetadata, selfTy); if (selfTy->is()) { // The Self type is abstract, so we can fulfill its metadata from @@ -347,8 +353,7 @@ void PolymorphicConvention::considerWitnessSelf(CanSILFunctionType fnType) { // The witness table for the Self : P conformance can be // fulfilled from the Self witness table parameter. - Sources.emplace_back(MetadataSource::Kind::SelfWitnessTable, - MetadataSource::InvalidSourceIndex, selfTy); + Sources.emplace_back(MetadataSource::Kind::SelfWitnessTable, selfTy); addSelfWitnessTableFulfillment(selfTy, conformance); } @@ -359,8 +364,7 @@ void PolymorphicConvention::considerObjCGenericSelf(CanSILFunctionType fnType) { unsigned paramIndex = fnType->getParameters().size() - 1; // Bind type metadata for Self. - Sources.emplace_back(MetadataSource::Kind::ClassPointer, paramIndex, - selfTy); + Sources.emplace_back(MetadataSource::Kind::ClassPointer, selfTy, paramIndex); if (isa(selfTy)) addSelfMetadataFulfillment(selfTy); @@ -385,8 +389,9 @@ void PolymorphicConvention::considerParameter(SILParameterInfo param, case ParameterConvention::Indirect_InoutAliasable: if (!isSelfParameter) return; if (type->getNominalOrBoundGenericNominal()) { - considerNewTypeSource(MetadataSource::Kind::GenericLValueMetadata, - paramIndex, type, IsExact); + considerNewTypeSource(IsExact, + MetadataSource::Kind::GenericLValueMetadata, + type, paramIndex); } return; @@ -395,15 +400,15 @@ void PolymorphicConvention::considerParameter(SILParameterInfo param, case ParameterConvention::Direct_Guaranteed: // Classes are sources of metadata. if (type->getClassOrBoundGenericClass()) { - considerNewTypeSource(MetadataSource::Kind::ClassPointer, - paramIndex, type, IsInexact); + considerNewTypeSource(IsInexact, MetadataSource::Kind::ClassPointer, + type, paramIndex); return; } if (isa(type)) { if (auto superclassTy = getSuperclassBound(type)) { - considerNewTypeSource(MetadataSource::Kind::ClassPointer, - paramIndex, superclassTy, IsInexact); + considerNewTypeSource(IsInexact, MetadataSource::Kind::ClassPointer, + superclassTy, paramIndex); return; } @@ -418,11 +423,11 @@ void PolymorphicConvention::considerParameter(SILParameterInfo param, // sources of metadata. CanType objTy = metatypeTy.getInstanceType(); if (auto classDecl = objTy->getClassOrBoundGenericClass()) - if (classDecl->usesObjCGenericsModel()) + if (classDecl->isTypeErasedGenericClass()) return; - considerNewTypeSource(MetadataSource::Kind::Metadata, - paramIndex, objTy, IsInexact); + considerNewTypeSource(IsInexact, MetadataSource::Kind::Metadata, objTy, + paramIndex); return; } @@ -465,7 +470,7 @@ void irgen::enumerateGenericParamFulfillments(IRGenModule &IGM, // captured value. auto generics = fnType->getInvocationGenericSignature(); - for (auto genericParam : generics->getGenericParams()) { + for (auto genericParam : generics.getGenericParams()) { auto genericParamType = genericParam->getCanonicalType(); auto fulfillment @@ -599,6 +604,17 @@ void EmitPolymorphicParameters::bindExtraSource( } return; } + + case MetadataSource::Kind::ErasedTypeMetadata: { + ArtificialLocation Loc(IGF.getDebugScope(), IGF.IGM.DebugInfo.get(), + IGF.Builder); + CanType argTy = getTypeInContext(source.Type); + llvm::Value *metadata = IGF.emitTypeMetadataRef(source.getFixedType()); + setTypeMetadataName(IGF.IGM, metadata, argTy); + IGF.bindLocalTypeDataFromTypeMetadata(argTy, IsExact, metadata, + MetadataState::Complete); + return; + } } llvm_unreachable("bad source kind!"); } @@ -2986,6 +3002,10 @@ namespace { case MetadataSource::Kind::SelfMetadata: case MetadataSource::Kind::SelfWitnessTable: continue; + + // No influence on the arguments. + case MetadataSource::Kind::ErasedTypeMetadata: + continue; } llvm_unreachable("bad source kind!"); } @@ -3040,6 +3060,10 @@ void EmitPolymorphicArguments::emit(SubstitutionMap subs, // Added later. continue; } + + case MetadataSource::Kind::ErasedTypeMetadata: + // No influence on the arguments. + continue; } llvm_unreachable("bad source kind"); } @@ -3098,6 +3122,10 @@ NecessaryBindings NecessaryBindings::computeBindings( case MetadataSource::Kind::SelfWitnessTable: // We'll just pass undef in cases like this. continue; + + case MetadataSource::Kind::ErasedTypeMetadata: + // Fixed in the body. + continue; } llvm_unreachable("bad source kind"); } @@ -3320,6 +3348,8 @@ namespace { case MetadataSource::Kind::SelfMetadata: case MetadataSource::Kind::SelfWitnessTable: return; // handled as a special case in expand() + case MetadataSource::Kind::ErasedTypeMetadata: + return; // fixed in the body } llvm_unreachable("bad source kind"); } @@ -3508,7 +3538,7 @@ llvm::Constant *IRGenModule::getAddrOfGenericEnvironment( llvm::SmallVector genericParamCounts; unsigned curDepth = 0; unsigned genericParamCount = 0; - for (const auto gp : signature->getGenericParams()) { + for (const auto &gp : signature.getGenericParams()) { if (curDepth != gp->getDepth()) { genericParamCounts.push_back(genericParamCount); curDepth = gp->getDepth(); @@ -3520,7 +3550,7 @@ llvm::Constant *IRGenModule::getAddrOfGenericEnvironment( auto flags = GenericEnvironmentFlags() .withNumGenericParameterLevels(genericParamCounts.size()) - .withNumGenericRequirements(signature->getRequirements().size()); + .withNumGenericRequirements(signature.getRequirements().size()); ConstantStructBuilder fields = builder.beginStruct(); fields.setPacked(true); @@ -3545,7 +3575,7 @@ llvm::Constant *IRGenModule::getAddrOfGenericEnvironment( // Generic requirements irgen::addGenericRequirements(*this, fields, signature, - signature->getRequirements()); + signature.getRequirements()); return fields.finishAndCreateFuture(); }); } diff --git a/lib/IRGen/GenProto.h b/lib/IRGen/GenProto.h index dada1eee4310e..79b6e0c7964ff 100644 --- a/lib/IRGen/GenProto.h +++ b/lib/IRGen/GenProto.h @@ -197,6 +197,11 @@ namespace irgen { /// Metadata is derived from the Self witness table parameter /// passed via the WitnessMethod convention. SelfWitnessTable, + + /// Metadata is obtained directly from the FixedType indicated. Used with + /// Objective-C generics, where the actual argument is erased at runtime + /// and its existential bound is used instead. + ErasedTypeMetadata, }; static bool requiresSourceIndex(Kind kind) { @@ -205,28 +210,56 @@ namespace irgen { kind == Kind::GenericLValueMetadata); } + static bool requiresFixedType(Kind kind) { + return (kind == Kind::ErasedTypeMetadata); + } + enum : unsigned { InvalidSourceIndex = ~0U }; private: /// The kind of source this is. Kind TheKind; - /// The parameter index, for ClassPointer and Metadata sources. - unsigned Index; + /// For ClassPointer, Metadata, and GenericLValueMetadata, the source index; + /// for ErasedTypeMetadata, the type; for others, Index should be set to + /// InvalidSourceIndex. + union { + unsigned Index; + CanType FixedType; + }; public: CanType Type; - MetadataSource(Kind kind, unsigned index, CanType type) + MetadataSource(Kind kind, CanType type) + : TheKind(kind), Index(InvalidSourceIndex), Type(type) + { + assert(!requiresSourceIndex(kind) && !requiresFixedType(kind)); + } + + + MetadataSource(Kind kind, CanType type, unsigned index) : TheKind(kind), Index(index), Type(type) { - assert(index != InvalidSourceIndex || !requiresSourceIndex(kind)); + assert(requiresSourceIndex(kind)); + assert(index != InvalidSourceIndex); + } + + MetadataSource(Kind kind, CanType type, CanType fixedType) + : TheKind(kind), FixedType(fixedType), Type(type) { + assert(requiresFixedType(kind)); } Kind getKind() const { return TheKind; } + unsigned getParamIndex() const { assert(requiresSourceIndex(getKind())); return Index; } + + CanType getFixedType() const { + assert(requiresFixedType(getKind())); + return FixedType; + } }; using GenericParamFulfillmentCallback = diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index b5d3daed05192..4c6ad47b23cdd 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -172,11 +172,8 @@ class PrintMetadataSource } }; -// Return the minimum Swift runtime version that supports demangling a given -// type. -static llvm::VersionTuple -getRuntimeVersionThatSupportsDemanglingType(IRGenModule &IGM, - CanType type) { +Optional +swift::irgen::getRuntimeVersionThatSupportsDemanglingType(CanType type) { // Associated types of opaque types weren't mangled in a usable form by the // Swift 5.1 runtime, so we needed to add a new mangling in 5.2. if (type->hasOpaqueArchetype()) { @@ -194,8 +191,18 @@ getRuntimeVersionThatSupportsDemanglingType(IRGenModule &IGM, // guards, so we don't need to limit availability of mangled names // involving them. } - - return llvm::VersionTuple(5, 0); + + bool needsConcurrency = type.findIf([](CanType t) -> bool { + if (auto fn = dyn_cast(t)) { + return fn->isAsync() || fn->isSendable() || fn->hasGlobalActor(); + } + return false; + }); + if (needsConcurrency) { + return llvm::VersionTuple(5, 5); + } + + return None; } // Produce a fallback mangled type name that uses an open-coded callback @@ -229,13 +236,10 @@ getTypeRefByFunction(IRGenModule &IGM, accessor->setAttributes(IGM.constructInitialAttributes()); SmallVector requirements; - GenericEnvironment *genericEnv = nullptr; - if (sig) { - enumerateGenericSignatureRequirements(sig, - [&](GenericRequirement reqt) { requirements.push_back(reqt); }); - genericEnv = sig->getGenericEnvironment(); - } - + auto *genericEnv = sig.getGenericEnvironment(); + enumerateGenericSignatureRequirements(sig, + [&](GenericRequirement reqt) { requirements.push_back(reqt); }); + { IRGenFunction IGF(IGM, accessor); if (IGM.DebugInfo) @@ -292,9 +296,11 @@ getTypeRefImpl(IRGenModule &IGM, // symbolic reference with a callback function. if (auto runtimeCompatVersion = getSwiftRuntimeCompatibilityVersionForTarget (IGM.Context.LangOpts.Target)) { - if (*runtimeCompatVersion < - getRuntimeVersionThatSupportsDemanglingType(IGM, type)) { - return getTypeRefByFunction(IGM, sig, type); + if (auto minimumSupportedRuntimeVersion = + getRuntimeVersionThatSupportsDemanglingType(type)) { + if (*runtimeCompatVersion < *minimumSupportedRuntimeVersion) { + return getTypeRefByFunction(IGM, sig, type); + } } } @@ -355,16 +361,12 @@ IRGenModule::emitWitnessTableRefString(CanType type, = substOpaqueTypesWithUnderlyingTypes(type, conformance); auto origType = type; - CanGenericSignature genericSig; - SmallVector requirements; - GenericEnvironment *genericEnv = nullptr; + auto genericSig = origGenericSig.getCanonicalSignature(); - if (origGenericSig) { - genericSig = origGenericSig.getCanonicalSignature(); - enumerateGenericSignatureRequirements(genericSig, - [&](GenericRequirement reqt) { requirements.push_back(reqt); }); - genericEnv = genericSig->getGenericEnvironment(); - } + SmallVector requirements; + enumerateGenericSignatureRequirements(genericSig, + [&](GenericRequirement reqt) { requirements.push_back(reqt); }); + auto *genericEnv = genericSig.getGenericEnvironment(); IRGenMangler mangler; std::string symbolName = @@ -1118,6 +1120,10 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { case irgen::MetadataSource::Kind::Metadata: Root = SourceBuilder.createMetadataCapture(Source.getParamIndex()); break; + + case irgen::MetadataSource::Kind::ErasedTypeMetadata: + // Fixed in the function body + break; } // The metadata might be reached via a non-trivial path (eg, @@ -1437,7 +1443,7 @@ void IRGenModule::emitFieldDescriptor(const NominalTypeDecl *D) { if (needsFieldDescriptor) { FieldTypeMetadataBuilder builder(*this, D); - FieldDescriptors.push_back(builder.emit()); + builder.emit(); } } diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index 64c324d7f4a56..70b2fb0330d6b 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -216,6 +216,13 @@ namespace { return nullptr; } + const TypeInfo *getFieldTypeInfo(IRGenModule &IGM, VarDecl *field) const { + auto &fieldInfo = getFieldInfo(field); + if (fieldInfo.isEmpty()) + return nullptr; + return &fieldInfo.getTypeInfo(); + } + MemberAccessStrategy getFieldAccessStrategy(IRGenModule &IGM, SILType T, VarDecl *field) const { auto &fieldInfo = getFieldInfo(field); @@ -1137,6 +1144,12 @@ Optional irgen::getPhysicalStructFieldIndex(IRGenModule &IGM, FOR_STRUCT_IMPL(IGM, baseType, getFieldIndexIfNotEmpty, field); } +const TypeInfo *irgen::getPhysicalStructFieldTypeInfo(IRGenModule &IGM, + SILType baseType, + VarDecl *field) { + FOR_STRUCT_IMPL(IGM, baseType, getFieldTypeInfo, field); +} + void IRGenModule::emitStructDecl(StructDecl *st) { if (!IRGen.hasLazyMetadata(st)) { emitStructMetadata(*this, st); diff --git a/lib/IRGen/GenStruct.h b/lib/IRGen/GenStruct.h index c3212a32c08fc..4e72a1b9e7d4a 100644 --- a/lib/IRGen/GenStruct.h +++ b/lib/IRGen/GenStruct.h @@ -34,7 +34,8 @@ namespace irgen { class IRGenFunction; class IRGenModule; class MemberAccessStrategy; - + class TypeInfo; + Address projectPhysicalStructMemberAddress(IRGenFunction &IGF, Address base, SILType baseType, @@ -58,6 +59,10 @@ namespace irgen { getPhysicalStructMemberAccessStrategy(IRGenModule &IGM, SILType baseType, VarDecl *field); + const TypeInfo *getPhysicalStructFieldTypeInfo(IRGenModule &IGM, + SILType baseType, + VarDecl *field); + /// Returns the index of the element in the llvm struct type which represents /// \p field in \p baseType. /// @@ -66,7 +71,6 @@ namespace irgen { llvm::Optional getPhysicalStructFieldIndex(IRGenModule &IGM, SILType baseType, VarDecl *field); - } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/GenThunk.cpp b/lib/IRGen/GenThunk.cpp index 34763af99da8d..75ceb0cbffede 100644 --- a/lib/IRGen/GenThunk.cpp +++ b/lib/IRGen/GenThunk.cpp @@ -252,9 +252,9 @@ void IRGenThunk::emit() { GenericContextScope scope(IGF.IGM, origTy->getInvocationGenericSignature()); if (isAsync) { - auto asyncContextIdx = - Signature::forAsyncEntry(IGF.IGM, origTy).getAsyncContextIndex(); - IGF.setupAsync(asyncContextIdx); + auto asyncContextIdx = Signature::forAsyncEntry( + IGF.IGM, origTy, /*useSpecialConvention*/ false) + .getAsyncContextIndex(); auto entity = LinkEntity::forDispatchThunk(declRef); emitAsyncFunctionEntry(IGF, *asyncLayout, entity, asyncContextIdx); diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index fd646b51c7715..eba984b8e6123 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -1441,9 +1441,6 @@ TypeConverter::~TypeConverter() { } void TypeConverter::setGenericContext(CanGenericSignature signature) { - if (!signature) - return; - CurGenericSignature = signature; // Clear the dependent type info cache since we have a new active signature @@ -1463,7 +1460,7 @@ CanGenericSignature IRGenModule::getCurGenericContext() { } GenericEnvironment *TypeConverter::getGenericEnvironment() { - return CurGenericSignature->getGenericEnvironment(); + return CurGenericSignature.getGenericEnvironment(); } GenericEnvironment *IRGenModule::getGenericEnvironment() { @@ -1816,7 +1813,7 @@ ArchetypeType *TypeConverter::getExemplarArchetype(ArchetypeType *t) { // Dig out the canonical generic environment. auto genericSig = genericEnv->getGenericSignature(); auto canGenericSig = genericSig.getCanonicalSignature(); - auto canGenericEnv = canGenericSig->getGenericEnvironment(); + auto canGenericEnv = canGenericSig.getGenericEnvironment(); if (canGenericEnv == genericEnv) return t; // Map the archetype out of its own generic environment and into the @@ -2567,7 +2564,7 @@ bool TypeConverter::isExemplarArchetype(ArchetypeType *arch) const { // Dig out the canonical generic environment. auto genericSig = genericEnv->getGenericSignature(); auto canGenericSig = genericSig.getCanonicalSignature(); - auto canGenericEnv = canGenericSig->getGenericEnvironment(); + auto canGenericEnv = canGenericSig.getGenericEnvironment(); // If this archetype is in the canonical generic environment, it's an // exemplar archetype. diff --git a/lib/IRGen/GenType.h b/lib/IRGen/GenType.h index 2325df64b50b9..1b485ba477d3d 100644 --- a/lib/IRGen/GenType.h +++ b/lib/IRGen/GenType.h @@ -277,8 +277,6 @@ class GenericContextScope { {} ~GenericContextScope() { - if (!newSig) - return; assert(TC.CurGenericSignature == newSig); TC.setGenericContext(oldSig); } diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index c2cef046b7567..043dc7ae42983 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -41,6 +41,7 @@ #include "IRGenDebugInfo.h" #include "IRGenFunction.h" #include "IRGenModule.h" +#include "MetadataLayout.h" #include "StructLayout.h" #include "TypeInfo.h" @@ -815,15 +816,72 @@ static llvm::Constant *getMemCpyFunction(IRGenModule &IGM, }); } +namespace { +struct BoundGenericTypeCharacteristics { + SILType concreteType; + const TypeInfo *TI; + FixedPacking packing; +}; + +ValueWitnessFlags getValueWitnessFlags(const TypeInfo *TI, SILType concreteType, + FixedPacking packing) { + ValueWitnessFlags flags; + + // If we locally know that the type has fixed layout, we can emit + // meaningful flags for it. + if (auto *fixedTI = dyn_cast(TI)) { + assert(packing == FixedPacking::OffsetZero || + packing == FixedPacking::Allocate); + bool isInline = packing == FixedPacking::OffsetZero; + bool isBitwiseTakable = + fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal); + assert(isBitwiseTakable || !isInline); + flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue()) + .withPOD(fixedTI->isPOD(ResilienceExpansion::Maximal)) + .withInlineStorage(isInline) + .withBitwiseTakable(isBitwiseTakable); + } else { + flags = flags.withIncomplete(true); + } + + if (concreteType.getEnumOrBoundGenericEnum()) + flags = flags.withEnumWitnesses(true); + + return flags; +} + +unsigned getExtraInhabitantCount(const TypeInfo *TI, IRGenModule &IGM) { + unsigned value = 0; + if (auto *fixedTI = dyn_cast(TI)) { + value = fixedTI->getFixedExtraInhabitantCount(IGM); + } + return value; +} + +void addSize(ConstantStructBuilder &B, const TypeInfo *TI, IRGenModule &IGM) { + if (auto staticSize = TI->getStaticSize(IGM)) + return B.add(staticSize); + // Just fill in 0 here if the type can't be statically laid out. + return B.addSize(Size(0)); +} + +void addStride(ConstantStructBuilder &B, const TypeInfo *TI, IRGenModule &IGM) { + if (auto value = TI->getStaticStride(IGM)) + return B.add(value); + + // Just fill in null here if the type can't be statically laid out. + return B.addSize(Size(0)); +} +} // end anonymous namespace + /// Find a witness to the fact that a type is a value type. /// Always adds an i8*. -static void addValueWitness(IRGenModule &IGM, - ConstantStructBuilder &B, - ValueWitness index, - FixedPacking packing, - CanType abstractType, - SILType concreteType, - const TypeInfo &concreteTI) { +static void addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B, + ValueWitness index, FixedPacking packing, + CanType abstractType, SILType concreteType, + const TypeInfo &concreteTI, + const Optional + boundGenericCharacteristics = llvm::None) { auto addFunction = [&](llvm::Constant *fn) { fn = llvm::ConstantExpr::getBitCast(fn, IGM.Int8PtrTy); B.addSignedPointer(fn, IGM.getOptions().PointerAuth.ValueWitnesses, index); @@ -880,57 +938,51 @@ static void addValueWitness(IRGenModule &IGM, goto standard; case ValueWitness::Size: { - if (auto value = concreteTI.getStaticSize(IGM)) - return B.add(value); - - // Just fill in 0 here if the type can't be statically laid out. - return B.addSize(Size(0)); + if (boundGenericCharacteristics) + return addSize(B, boundGenericCharacteristics->TI, IGM); + return addSize(B, &concreteTI, IGM); } case ValueWitness::Flags: { - ValueWitnessFlags flags; - - // If we locally know that the type has fixed layout, we can emit - // meaningful flags for it. - if (auto *fixedTI = dyn_cast(&concreteTI)) { - assert(packing == FixedPacking::OffsetZero || - packing == FixedPacking::Allocate); - bool isInline = packing == FixedPacking::OffsetZero; - bool isBitwiseTakable = - fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal); - assert(isBitwiseTakable || !isInline); - flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue()) - .withPOD(fixedTI->isPOD(ResilienceExpansion::Maximal)) - .withInlineStorage(isInline) - .withBitwiseTakable(isBitwiseTakable); - } else { - flags = flags.withIncomplete(true); - } - - if (concreteType.getEnumOrBoundGenericEnum()) - flags = flags.withEnumWitnesses(true); - - return B.addInt32(flags.getOpaqueValue()); + if (boundGenericCharacteristics) + return B.addInt32( + getValueWitnessFlags(boundGenericCharacteristics->TI, + boundGenericCharacteristics->concreteType, + boundGenericCharacteristics->packing) + .getOpaqueValue()); + return B.addInt32(getValueWitnessFlags(&concreteTI, concreteType, packing) + .getOpaqueValue()); } case ValueWitness::ExtraInhabitantCount: { - unsigned value = 0; - if (auto *fixedTI = dyn_cast(&concreteTI)) { - value = fixedTI->getFixedExtraInhabitantCount(IGM); - } - return B.addInt32(value); + if (boundGenericCharacteristics) + return B.addInt32( + getExtraInhabitantCount(boundGenericCharacteristics->TI, IGM)); + return B.addInt32(getExtraInhabitantCount(&concreteTI, IGM)); } case ValueWitness::Stride: { - if (auto value = concreteTI.getStaticStride(IGM)) - return B.add(value); - - // Just fill in null here if the type can't be statically laid out. - return B.addSize(Size(0)); + if (boundGenericCharacteristics) + return addStride(B, boundGenericCharacteristics->TI, IGM); + return addStride(B, &concreteTI, IGM); } - case ValueWitness::GetEnumTagSinglePayload: + case ValueWitness::GetEnumTagSinglePayload: { + if (boundGenericCharacteristics) + if (auto *enumDecl = boundGenericCharacteristics->concreteType + .getEnumOrBoundGenericEnum()) + if (IGM.getMetadataLayout(enumDecl).hasPayloadSizeOffset()) + return B.add(llvm::ConstantExpr::getBitCast( + IGM.getGetMultiPayloadEnumTagSinglePayloadFn(), IGM.Int8PtrTy)); + goto standard; + } case ValueWitness::StoreEnumTagSinglePayload: { + if (boundGenericCharacteristics) + if (auto *enumDecl = boundGenericCharacteristics->concreteType + .getEnumOrBoundGenericEnum()) + if (IGM.getMetadataLayout(enumDecl).hasPayloadSizeOffset()) + return B.add(llvm::ConstantExpr::getBitCast( + IGM.getStoreMultiPayloadEnumTagSinglePayloadFn(), IGM.Int8PtrTy)); goto standard; } @@ -949,7 +1001,7 @@ static void addValueWitness(IRGenModule &IGM, buildValueWitnessFunction(IGM, fn, index, packing, abstractType, concreteType, concreteTI); addFunction(fn); -} + } static bool shouldAddEnumWitnesses(CanType abstractType) { // Needs to handle UnboundGenericType. @@ -964,22 +1016,21 @@ static llvm::StructType *getValueWitnessTableType(IRGenModule &IGM, } /// Collect the value witnesses for a particular type. -static void addValueWitnesses(IRGenModule &IGM, - ConstantStructBuilder &B, - FixedPacking packing, - CanType abstractType, - SILType concreteType, - const TypeInfo &concreteTI) { +static void addValueWitnesses(IRGenModule &IGM, ConstantStructBuilder &B, + FixedPacking packing, CanType abstractType, + SILType concreteType, const TypeInfo &concreteTI, + const Optional + boundGenericCharacteristics = llvm::None) { for (unsigned i = 0; i != NumRequiredValueWitnesses; ++i) { - addValueWitness(IGM, B, ValueWitness(i), packing, - abstractType, concreteType, concreteTI); + addValueWitness(IGM, B, ValueWitness(i), packing, abstractType, + concreteType, concreteTI, boundGenericCharacteristics); } if (shouldAddEnumWitnesses(abstractType)) { for (auto i = unsigned(ValueWitness::First_EnumValueWitness); i <= unsigned(ValueWitness::Last_EnumValueWitness); ++i) { - addValueWitness(IGM, B, ValueWitness(i), packing, - abstractType, concreteType, concreteTI); + addValueWitness(IGM, B, ValueWitness(i), packing, abstractType, + concreteType, concreteTI, boundGenericCharacteristics); } } } @@ -995,6 +1046,19 @@ static void addValueWitnessesForAbstractType(IRGenModule &IGM, ConstantStructBuilder &B, CanType abstractType, bool &canBeConstant) { + Optional boundGenericCharacteristics; + if (auto boundGenericType = dyn_cast(abstractType)) { + CanType concreteFormalType = getFormalTypeInContext(abstractType); + + auto concreteLoweredType = IGM.getLoweredType(concreteFormalType); + const auto *boundConcreteTI = &IGM.getTypeInfo(concreteLoweredType); + auto packing = boundConcreteTI->getFixedPacking(IGM); + boundGenericCharacteristics = {concreteLoweredType, boundConcreteTI, + packing}; + + abstractType = + boundGenericType->getDecl()->getDeclaredType()->getCanonicalType(); + } CanType concreteFormalType = getFormalTypeInContext(abstractType); auto concreteLoweredType = IGM.getLoweredType(concreteFormalType); @@ -1003,10 +1067,12 @@ static void addValueWitnessesForAbstractType(IRGenModule &IGM, // For now, assume that we never have any interest in dynamically // changing the value witnesses for something that's fixed-layout. - canBeConstant = concreteTI.isFixedSize(); + canBeConstant = boundGenericCharacteristics + ? boundGenericCharacteristics->TI->isFixedSize() + : concreteTI.isFixedSize(); - addValueWitnesses(IGM, B, packing, abstractType, - concreteLoweredType, concreteTI); + addValueWitnesses(IGM, B, packing, abstractType, concreteLoweredType, + concreteTI, boundGenericCharacteristics); } static constexpr uint64_t sizeAndAlignment(Size size, Alignment alignment) { diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 26ee4aefe55ef..5d7ad24e11e00 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -175,6 +175,11 @@ swift::getIRTargetOptions(const IRGenOptions &Opts, ASTContext &Ctx) { if (Clang->getTargetInfo().getTriple().isOSBinFormatWasm()) TargetOpts.ThreadModel = llvm::ThreadModel::Single; + if (Opts.EnableGlobalISel) { + TargetOpts.EnableGlobalISel = true; + TargetOpts.GlobalISelAbort = GlobalISelAbortMode::DisableWithDiag; + } + clang::TargetOptions &ClangOpts = Clang->getTargetInfo().getTargetOpts(); return std::make_tuple(TargetOpts, ClangOpts.CPU, ClangOpts.Features, ClangOpts.Triple); } diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 7c66f161c79fb..e7e6157a286d9 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -17,9 +17,11 @@ #define DEBUG_TYPE "debug-info" #include "IRGenDebugInfo.h" #include "GenOpaque.h" +#include "GenStruct.h" #include "GenType.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" @@ -189,16 +191,25 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { llvm::DISubprogram *emitFunction(SILFunction &SILFn, llvm::Function *Fn); void emitArtificialFunction(IRBuilder &Builder, llvm::Function *Fn, SILType SILTy); + + /// Return false if we fail to create the right DW_OP_LLVM_fragment operand. + bool handleFragmentDIExpr(const SILDIExprOperand &CurDIExprOp, + SmallVectorImpl &Operands); + /// Return false if we fail to create the desired !DIExpression. + bool buildDebugInfoExpression(const SILDebugVariable &VarInfo, + SmallVectorImpl &Operands); + void emitVariableDeclaration(IRBuilder &Builder, ArrayRef Storage, DebugTypeInfo Ty, const SILDebugScope *DS, - ValueDecl *VarDecl, SILDebugVariable VarInfo, + Optional VarLoc, + SILDebugVariable VarInfo, IndirectionKind = DirectValue, ArtificialKind = RealValue); void emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage, llvm::DILocalVariable *Var, llvm::DIExpression *Expr, unsigned Line, unsigned Col, llvm::DILocalScope *Scope, - const SILDebugScope *DS, bool InCoroContext = false); + const SILDebugScope *DS, bool InCoroContext); void emitGlobalVariableDeclaration(llvm::GlobalVariable *Storage, StringRef Name, StringRef LinkageName, @@ -288,6 +299,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { FilenameAndLocation getStartLocation(Optional OptLoc) { if (!OptLoc) return {}; + if (OptLoc->isFilenameAndLocation()) + return sanitizeCodeViewFilenameAndLocation(*OptLoc->getFilenameAndLocation()); return decodeSourceLoc(OptLoc->getStartSourceLoc()); } @@ -813,9 +826,29 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { return MetadataTypeDeclCache.find(DbgTy.getDecl()->getName().str()) ->getKey(); + // This is a bit of a hack. We need a generic signature to use for mangling. + // If we started with an interface type, just use IGM.getCurGenericContext(), + // since callers that use interface types typically push a signature that way. + // + // Otherwise, if we have a contextual type, find an archetype and ask it for + // it's generic signature. The context generic signature from the IRGenModule + // is unlikely to be useful here. + GenericSignature Sig; Type Ty = DbgTy.getType(); - if (Ty->hasArchetype()) + if (Ty->hasArchetype()) { + Ty.findIf([&](Type t) -> bool { + if (auto *archetypeTy = t->getAs()) { + Sig = archetypeTy->getGenericEnvironment()->getGenericSignature(); + return true; + } + + return false; + }); + Ty = Ty->mapTypeOutOfContext(); + } else { + Sig = IGM.getCurGenericContext(); + } // Strip off top level of type sugar (except for type aliases). // We don't want Optional and T? to get different debug types. @@ -840,7 +873,6 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { IGM.getSILModule()); Mangle::ASTMangler Mangler; - GenericSignature Sig = IGM.getCurGenericContext(); std::string Result = Mangler.mangleTypeForDebugger(Ty, Sig); if (!Opts.DisableRoundTripDebugTypes) { @@ -869,62 +901,23 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { return BumpAllocatedString(Result); } - llvm::DIDerivedType *createMemberType(DebugTypeInfo DbgTy, StringRef Name, - unsigned &OffsetInBits, + llvm::DIDerivedType *createMemberType(CompletedDebugTypeInfo DbgTy, + StringRef Name, unsigned &OffsetInBits, llvm::DIScope *Scope, llvm::DIFile *File, llvm::DINode::DIFlags Flags) { unsigned SizeOfByte = CI.getTargetInfo().getCharWidth(); auto *Ty = getOrCreateType(DbgTy); auto *DITy = DBuilder.createMemberType( - Scope, Name, File, 0, SizeOfByte * DbgTy.getSize().getValue(), 0, - OffsetInBits, Flags, Ty); + Scope, Name, File, 0, + SizeOfByte * DbgTy.getSizeValue(), 0, OffsetInBits, + Flags, Ty); OffsetInBits += getSizeInBits(Ty); OffsetInBits = llvm::alignTo(OffsetInBits, SizeOfByte * DbgTy.getAlignment().getValue()); return DITy; } - llvm::DINodeArray getTupleElements(TupleType *TupleTy, llvm::DIScope *Scope, - llvm::DIFile *File, - llvm::DINode::DIFlags Flags, - unsigned &SizeInBits) { - SmallVector Elements; - unsigned OffsetInBits = 0; - auto genericSig = IGM.getCurGenericContext(); - for (auto ElemTy : TupleTy->getElementTypes()) { - auto &elemTI = IGM.getTypeInfoForUnlowered( - AbstractionPattern(genericSig, ElemTy->getCanonicalType()), ElemTy); - auto DbgTy = DebugTypeInfo::getFromTypeInfo(ElemTy, elemTI); - Elements.push_back(createMemberType(DbgTy, StringRef(), OffsetInBits, - Scope, File, Flags)); - } - SizeInBits = OffsetInBits; - return DBuilder.getOrCreateArray(Elements); - } - - llvm::DINodeArray getStructMembers(NominalTypeDecl *D, Type BaseTy, - llvm::DIScope *Scope, llvm::DIFile *File, - llvm::DINode::DIFlags Flags, - unsigned &SizeInBits) { - SmallVector Elements; - unsigned OffsetInBits = 0; - for (VarDecl *VD : D->getStoredProperties()) { - auto memberTy = - BaseTy->getTypeOfMember(IGM.getSwiftModule(), VD, nullptr); - - auto DbgTy = DebugTypeInfo::getFromTypeInfo( - VD->getInterfaceType(), - IGM.getTypeInfoForUnlowered( - IGM.getSILTypes().getAbstractionPattern(VD), memberTy)); - Elements.push_back(createMemberType(DbgTy, VD->getName().str(), - OffsetInBits, Scope, File, Flags)); - } - if (OffsetInBits > SizeInBits) - SizeInBits = OffsetInBits; - return DBuilder.getOrCreateArray(Elements); - } - llvm::DICompositeType * createStructType(DebugTypeInfo DbgTy, NominalTypeDecl *Decl, Type BaseTy, llvm::DIScope *Scope, llvm::DIFile *File, unsigned Line, @@ -950,25 +943,64 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto TH = llvm::TrackingMDNodeRef(FwdDecl.get()); DITypeCache[DbgTy.getType()] = TH; - auto Members = - getStructMembers(Decl, BaseTy, Scope, File, Flags, SizeInBits); + // Collect the members. + SmallVector Elements; + unsigned OffsetInBits = 0; + for (VarDecl *VD : Decl->getStoredProperties()) { + auto memberTy = BaseTy->getTypeOfMember(IGM.getSwiftModule(), VD); + + if (auto DbgTy = CompletedDebugTypeInfo::getFromTypeInfo( + VD->getInterfaceType(), + IGM.getTypeInfoForUnlowered( + IGM.getSILTypes().getAbstractionPattern(VD), memberTy))) + Elements.push_back(createMemberType(*DbgTy, VD->getName().str(), + OffsetInBits, Scope, File, Flags)); + else + // Without complete type info we can only create a forward decl. + return DBuilder.createForwardDecl( + llvm::dwarf::DW_TAG_structure_type, Name, Scope, File, Line, + llvm::dwarf::DW_LANG_Swift, SizeInBits, 0, UniqueID); + } + if (OffsetInBits > SizeInBits) + SizeInBits = OffsetInBits; + auto DITy = DBuilder.createStructType( Scope, Name, File, Line, SizeInBits, AlignInBits, Flags, DerivedFrom, - Members, RuntimeLang, nullptr, UniqueID); + DBuilder.getOrCreateArray(Elements), RuntimeLang, nullptr, UniqueID); DBuilder.replaceTemporary(std::move(FwdDecl), DITy); return DITy; } - llvm::DINodeArray getEnumElements(DebugTypeInfo DbgTy, EnumDecl *ED, - llvm::DIScope *Scope, llvm::DIFile *File, - llvm::DINode::DIFlags Flags) { + llvm::DICompositeType *createEnumType(CompletedDebugTypeInfo DbgTy, + EnumDecl *Decl, StringRef MangledName, + llvm::DIScope *Scope, + llvm::DIFile *File, unsigned Line, + llvm::DINode::DIFlags Flags) { + StringRef Name = Decl->getName().str(); + unsigned SizeOfByte = CI.getTargetInfo().getCharWidth(); + unsigned SizeInBits = DbgTy.getSize()->getValue() * SizeOfByte; + // Default, since Swift doesn't allow specifying a custom alignment. + unsigned AlignInBits = 0; + + // FIXME: Is DW_TAG_union_type the right thing here? + // Consider using a DW_TAG_variant_type instead. + auto FwdDecl = llvm::TempDIType(DBuilder.createReplaceableCompositeType( + llvm::dwarf::DW_TAG_union_type, MangledName, Scope, File, Line, + llvm::dwarf::DW_LANG_Swift, SizeInBits, AlignInBits, Flags, + MangledName)); + + auto TH = llvm::TrackingMDNodeRef(FwdDecl.get()); + DITypeCache[DbgTy.getType()] = TH; + SmallVector Elements; - for (auto *ElemDecl : ED->getAllElements()) { + for (auto *ElemDecl : Decl->getAllElements()) { // FIXME Support enums. // Swift Enums can be both like DWARF enums and discriminated unions. - DebugTypeInfo ElemDbgTy; - if (ED->hasRawType()) + // LLVM now supports variant types in debug metadata, which may be a + // better fit. + Optional ElemDbgTy; + if (Decl->hasRawType()) // An enum with a raw type (enum E : Int {}), similar to a // DWARF enum. // @@ -976,54 +1008,37 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // one of the raw type as long as it is large enough to hold // all enum values. Use the raw type for the debug type, but // the storage size from the enum. - ElemDbgTy = - DebugTypeInfo(ED->getRawType(), DbgTy.getStorageType(), - DbgTy.getSize(), DbgTy.getAlignment(), true, false); + ElemDbgTy = CompletedDebugTypeInfo::get( + DebugTypeInfo(Decl->getRawType(), DbgTy.getStorageType(), + DbgTy.getSize(), DbgTy.getAlignment(), true, false)); else if (auto ArgTy = ElemDecl->getArgumentInterfaceType()) { // A discriminated union. This should really be described as a // DW_TAG_variant_type. For now only describing the data. ArgTy = ElemDecl->getParentEnum()->mapTypeIntoContext(ArgTy); auto &TI = IGM.getTypeInfoForUnlowered(ArgTy); - ElemDbgTy = DebugTypeInfo::getFromTypeInfo(ArgTy, TI); + ElemDbgTy = CompletedDebugTypeInfo::getFromTypeInfo(ArgTy, TI); } else { // Discriminated union case without argument. Fallback to Int // as the element type; there is no storage here. Type IntTy = IGM.Context.getIntType(); - ElemDbgTy = DebugTypeInfo(IntTy, DbgTy.getStorageType(), Size(0), - Alignment(1), true, false); + ElemDbgTy = CompletedDebugTypeInfo::get(DebugTypeInfo( + IntTy, DbgTy.getStorageType(), Size(0), Alignment(1), true, false)); + } + if (!ElemDbgTy) { + // Without complete type info we can only create a forward decl. + return DBuilder.createForwardDecl( + llvm::dwarf::DW_TAG_union_type, Name, Scope, File, Line, + llvm::dwarf::DW_LANG_Swift, SizeInBits, 0, MangledName); } unsigned Offset = 0; auto MTy = - createMemberType(ElemDbgTy, ElemDecl->getBaseIdentifier().str(), + createMemberType(*ElemDbgTy, ElemDecl->getBaseIdentifier().str(), Offset, Scope, File, Flags); Elements.push_back(MTy); } - return DBuilder.getOrCreateArray(Elements); - } - - llvm::DICompositeType *createEnumType(DebugTypeInfo DbgTy, EnumDecl *Decl, - StringRef MangledName, - llvm::DIScope *Scope, - llvm::DIFile *File, unsigned Line, - llvm::DINode::DIFlags Flags) { - unsigned SizeOfByte = CI.getTargetInfo().getCharWidth(); - unsigned SizeInBits = DbgTy.getSize().getValue() * SizeOfByte; - // Default, since Swift doesn't allow specifying a custom alignment. - unsigned AlignInBits = 0; - - // FIXME: Is DW_TAG_union_type the right thing here? - // Consider using a DW_TAG_variant_type instead. - auto FwdDecl = llvm::TempDIType(DBuilder.createReplaceableCompositeType( - llvm::dwarf::DW_TAG_union_type, MangledName, Scope, File, Line, - llvm::dwarf::DW_LANG_Swift, SizeInBits, AlignInBits, Flags, - MangledName)); - - auto TH = llvm::TrackingMDNodeRef(FwdDecl.get()); - DITypeCache[DbgTy.getType()] = TH; - auto DITy = DBuilder.createUnionType( - Scope, Decl->getName().str(), File, Line, SizeInBits, AlignInBits, - Flags, getEnumElements(DbgTy, Decl, Scope, File, Flags), + Scope, Name, File, Line, SizeInBits, AlignInBits, + Flags, DBuilder.getOrCreateArray(Elements), llvm::dwarf::DW_LANG_Swift, MangledName); DBuilder.replaceTemporary(std::move(FwdDecl), DITy); @@ -1037,9 +1052,11 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { return getOrCreateType(BlandDbgTy); } - uint64_t getSizeOfBasicType(DebugTypeInfo DbgTy) { + uint64_t getSizeOfBasicType(CompletedDebugTypeInfo DbgTy) { uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth(); - uint64_t BitWidth = DbgTy.getSize().getValue() * SizeOfByte; + uint64_t BitWidth = 0; + if (DbgTy.getSize()) + BitWidth = DbgTy.getSizeValue() * SizeOfByte; llvm::Type *StorageType = DbgTy.getStorageType() ? DbgTy.getStorageType() : IGM.DataLayout.getSmallestLegalIntType( @@ -1081,9 +1098,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { if (llvm::Metadata *V = InnerTypeCache.lookup(UID)) UniqueType = cast(V); else { - UniqueType = DBuilder.createStructType( - Scope, Name, File, Line, 0, 0, Flags, nullptr, nullptr, - llvm::dwarf::DW_LANG_Swift, nullptr, MangledName); + UniqueType = DBuilder.createForwardDecl( + llvm::dwarf::DW_TAG_structure_type, Name, Scope, File, Line, + llvm::dwarf::DW_LANG_Swift, 0, 0, MangledName); if (BoundParams) DBuilder.replaceArrays(UniqueType, nullptr, BoundParams); InnerTypeCache[UID] = llvm::TrackingMDNodeRef(UniqueType); @@ -1217,6 +1234,25 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { StringRef MangledName) { TypeBase *BaseTy = DbgTy.getType(); auto *TupleTy = BaseTy->castTo(); + + SmallVector Elements; + unsigned OffsetInBits = 0; + auto genericSig = IGM.getCurGenericContext(); + for (auto ElemTy : TupleTy->getElementTypes()) { + auto &elemTI = IGM.getTypeInfoForUnlowered( + AbstractionPattern(genericSig, ElemTy->getCanonicalType()), ElemTy); + if (auto DbgTy = CompletedDebugTypeInfo::getFromTypeInfo(ElemTy, elemTI)) + Elements.push_back( + createMemberType(*DbgTy, "", OffsetInBits, Scope, MainFile, Flags)); + else + // We can only create a forward declaration without complete size info. + return DBuilder.createReplaceableCompositeType( + llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, MainFile, 0, + llvm::dwarf::DW_LANG_Swift, 0, AlignInBits, Flags, MangledName); + } + // FIXME: assert that SizeInBits == OffsetInBits. + SizeInBits = OffsetInBits; + auto FwdDecl = llvm::TempDINode(DBuilder.createReplaceableCompositeType( llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, MainFile, 0, llvm::dwarf::DW_LANG_Swift, SizeInBits, AlignInBits, Flags, @@ -1224,26 +1260,20 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { DITypeCache[DbgTy.getType()] = llvm::TrackingMDNodeRef(FwdDecl.get()); - unsigned RealSize; - auto Elements = getTupleElements(TupleTy, Scope, MainFile, Flags, RealSize); - // FIXME: Handle %swift.opaque members and make this into an assertion. - if (!RealSize) - RealSize = SizeInBits; - auto DITy = DBuilder.createStructType( - Scope, MangledName, MainFile, 0, RealSize, AlignInBits, Flags, + Scope, MangledName, MainFile, 0, SizeInBits, AlignInBits, Flags, nullptr, // DerivedFrom - Elements, llvm::dwarf::DW_LANG_Swift, nullptr, MangledName); + DBuilder.getOrCreateArray(Elements), llvm::dwarf::DW_LANG_Swift, + nullptr, MangledName); DBuilder.replaceTemporary(std::move(FwdDecl), DITy); return DITy; } - llvm::DIType *createOpaqueStruct(llvm::DIScope *Scope, StringRef Name, - llvm::DIFile *File, unsigned Line, - unsigned SizeInBits, unsigned AlignInBits, - llvm::DINode::DIFlags Flags, - StringRef MangledName) { + llvm::DICompositeType * + createOpaqueStruct(llvm::DIScope *Scope, StringRef Name, llvm::DIFile *File, + unsigned Line, unsigned SizeInBits, unsigned AlignInBits, + llvm::DINode::DIFlags Flags, StringRef MangledName) { return DBuilder.createStructType( Scope, Name, File, Line, SizeInBits, AlignInBits, Flags, /* DerivedFrom */ nullptr, @@ -1259,7 +1289,10 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // emitting the storage size of the struct, but it may be necessary // to emit the (target!) size of the underlying basic type. uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth(); - uint64_t SizeInBits = DbgTy.getSize().getValue() * SizeOfByte; + // FIXME: SizeInBits is redundant with DbgTy, remove it. + uint64_t SizeInBits = 0; + if (DbgTy.getSize()) + SizeInBits = DbgTy.getSize()->getValue() * SizeOfByte; unsigned AlignInBits = DbgTy.hasDefaultAlignment() ? 0 : DbgTy.getAlignment().getValue() * SizeOfByte; @@ -1275,7 +1308,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { StringRef Name = ""; InternalType = DBuilder.createForwardDecl( llvm::dwarf::DW_TAG_structure_type, Name, Scope, File, - /*Line*/ 0, llvm::dwarf::DW_LANG_Swift, SizeInBits, AlignInBits); + /*Line*/ 0, llvm::dwarf::DW_LANG_Swift, SizeInBits, AlignInBits, + MangledName); } return InternalType; } @@ -1284,13 +1318,15 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { switch (BaseTy->getKind()) { case TypeKind::BuiltinInteger: { Encoding = llvm::dwarf::DW_ATE_unsigned; - SizeInBits = getSizeOfBasicType(DbgTy); + if (auto CompletedDbgTy = CompletedDebugTypeInfo::get(DbgTy)) + SizeInBits = getSizeOfBasicType(*CompletedDbgTy); break; } case TypeKind::BuiltinIntegerLiteral: { Encoding = llvm::dwarf::DW_ATE_unsigned; // ? - SizeInBits = getSizeOfBasicType(DbgTy); + if (auto CompletedDbgTy = CompletedDebugTypeInfo::get(DbgTy)) + SizeInBits = getSizeOfBasicType(*CompletedDbgTy); break; } @@ -1361,19 +1397,22 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto *Decl = StructTy->getDecl(); auto L = getFilenameAndLocation(*this, Decl); auto *File = getOrCreateFile(L.filename); + // No line numbers are attached to type forward declarations. This is + // intentional: It interfers with the efficacy of incremental builds. We + // don't want a whitespace change to an secondary file trigger a + // recompilation of the debug info of a primary source file. unsigned FwdDeclLine = 0; if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) return createStructType(DbgTy, Decl, StructTy, Scope, File, L.line, SizeInBits, AlignInBits, Flags, nullptr, llvm::dwarf::DW_LANG_Swift, MangledName); - else - // No line numbers are attached to type forward declarations. This is - // intentional: It interfers with the efficacy of incremental builds. We - // don't want a whitespace change to an secondary file trigger a - // recompilation of the debug info of a primary source file. - return createOpaqueStruct(Scope, Decl->getName().str(), File, - FwdDeclLine, SizeInBits, AlignInBits, Flags, - MangledName); + StringRef Name = Decl->getName().str(); + if (DbgTy.getSize()) + return createOpaqueStruct(Scope, Name, File, FwdDeclLine, SizeInBits, + AlignInBits, Flags, MangledName); + return DBuilder.createForwardDecl( + llvm::dwarf::DW_TAG_structure_type, Name, Scope, File, FwdDeclLine, + llvm::dwarf::DW_LANG_Swift, 0, AlignInBits, MangledName); } case TypeKind::Class: { @@ -1535,14 +1574,12 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto L = getFilenameAndLocation(*this, Decl); auto *File = getOrCreateFile(L.filename); unsigned FwdDeclLine = 0; - if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) - return createEnumType(DbgTy, Decl, MangledName, Scope, File, L.line, - Flags); - else - return createOpaqueStruct(Scope, Decl->getName().str(), File, - FwdDeclLine, SizeInBits, AlignInBits, Flags, - MangledName); + if (auto CompletedDbgTy = CompletedDebugTypeInfo::get(DbgTy)) + return createEnumType(*CompletedDbgTy, Decl, MangledName, Scope, File, + L.line, Flags); + return createOpaqueStruct(Scope, Decl->getName().str(), File, FwdDeclLine, + SizeInBits, AlignInBits, Flags, MangledName); } case TypeKind::BoundGenericEnum: { @@ -1612,7 +1649,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // SyntaxSugarType derivations. case TypeKind::Dictionary: case TypeKind::ArraySlice: - case TypeKind::Optional: { + case TypeKind::Optional: + case TypeKind::VariadicSequence: { auto *SyntaxSugarTy = cast(BaseTy); auto *CanTy = SyntaxSugarTy->getSinglyDesugaredType(); return getOrCreateDesugaredType(CanTy, DbgTy); @@ -1652,8 +1690,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { static bool canMangle(TypeBase *Ty) { // TODO: C++ types are not yet supported (SR-13223). if (Ty->getStructOrBoundGenericStruct() && - Ty->getStructOrBoundGenericStruct()->getClangDecl() && - isa( + isa_and_nonnull( Ty->getStructOrBoundGenericStruct()->getClangDecl())) return false; @@ -2321,10 +2358,73 @@ void IRGenDebugInfoImpl::emitArtificialFunction(IRBuilder &Builder, setCurrentLoc(Builder, Scope, ALoc); } +bool IRGenDebugInfoImpl::handleFragmentDIExpr( + const SILDIExprOperand &CurDIExprOp, SmallVectorImpl &Operands) { + assert(CurDIExprOp.getOperator() == SILDIExprOperator::Fragment); + // Expecting a VarDecl that points to a field in an struct + auto DIExprArgs = CurDIExprOp.args(); + auto *VD = dyn_cast_or_null(DIExprArgs.size()? + DIExprArgs[0].getAsDecl() : nullptr); + assert(VD && "Expecting a VarDecl as the operand for " + "DIExprOperator::Fragment"); + // Translate the based type + DeclContext *ParentDecl = VD->getDeclContext(); + assert(ParentDecl && "VarDecl has no parent context?"); + SILType ParentSILType = + IGM.getLoweredType(ParentDecl->getDeclaredTypeInContext()); + // Retrieve the offset & size of the field + llvm::Constant *Offset = + emitPhysicalStructMemberFixedOffset(IGM, ParentSILType, VD); + auto *FieldTypeInfo = getPhysicalStructFieldTypeInfo(IGM, ParentSILType, VD); + // FIXME: This will only happen if IRGen hasn't processed ParentSILType + // (into its own representation) but we probably should ask IRGen to process + // it right now. + if (!FieldTypeInfo) + return false; + llvm::Type *FieldTy = FieldTypeInfo->getStorageType(); + // Doesn't support non-fixed type right now + if (!Offset || !FieldTy) + return false; + + uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth(); + uint64_t SizeInBits = IGM.DataLayout.getTypeSizeInBits(FieldTy); + uint64_t OffsetInBits = + Offset->getUniqueInteger().getLimitedValue() * SizeOfByte; + + // Translate to LLVM dbg intrinsic operands + Operands.push_back(llvm::dwarf::DW_OP_LLVM_fragment); + Operands.push_back(OffsetInBits); + Operands.push_back(SizeInBits); + + return true; +} + +bool IRGenDebugInfoImpl::buildDebugInfoExpression( + const SILDebugVariable &VarInfo, SmallVectorImpl &Operands) { + assert(VarInfo.DIExpr && "SIL debug info expression not found"); + + const auto &DIExpr = VarInfo.DIExpr; + for (const SILDIExprOperand &ExprOperand : DIExpr.operands()) { + switch (ExprOperand.getOperator()) { + case SILDIExprOperator::Fragment: + if (!handleFragmentDIExpr(ExprOperand, Operands)) + return false; + break; + case SILDIExprOperator::Dereference: + Operands.push_back(llvm::dwarf::DW_OP_deref); + break; + default: + llvm_unreachable("Unrecognized operator"); + } + } + return true; +} + void IRGenDebugInfoImpl::emitVariableDeclaration( IRBuilder &Builder, ArrayRef Storage, DebugTypeInfo DbgTy, - const SILDebugScope *DS, ValueDecl *VarDecl, SILDebugVariable VarInfo, - IndirectionKind Indirection, ArtificialKind Artificial) { + const SILDebugScope *DS, Optional DbgInstLoc, + SILDebugVariable VarInfo, IndirectionKind Indirection, + ArtificialKind Artificial) { assert(DS && "variable has no scope"); if (Opts.DebugInfoLevel <= IRGenDebugInfoLevel::LineTables) @@ -2339,7 +2439,7 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( auto *Scope = dyn_cast_or_null(getOrCreateScope(DS)); assert(Scope && "variable has no local scope"); - auto Loc = getFilenameAndLocation(*this, VarDecl); + auto DInstLoc = getStartLocation(DbgInstLoc); // FIXME: this should be the scope of the type's declaration. // If this is an argument, attach it to the current function scope. @@ -2347,17 +2447,18 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( while (isa(Scope)) Scope = cast(Scope)->getScope(); } - assert(Scope && isa(Scope) && "variable has no scope"); + assert(isa_and_nonnull(Scope) && "variable has no scope"); llvm::DIFile *Unit = getFile(Scope); llvm::DIType *DITy = getOrCreateType(DbgTy); assert(DITy && "could not determine debug type of variable"); if (VarInfo.Constant) DITy = DBuilder.createQualifiedType(llvm::dwarf::DW_TAG_const_type, DITy); - unsigned Line = Loc.line; + unsigned DInstLine = DInstLoc.line; // Self is always an artificial argument, so are variables without location. - if (!Line || (VarInfo.ArgNo > 0 && VarInfo.Name == IGM.Context.Id_self.str())) + if (!DInstLine || + (VarInfo.ArgNo > 0 && VarInfo.Name == IGM.Context.Id_self.str())) Artificial = ArtificialValue; llvm::DINode::DIFlags Flags = llvm::DINode::FlagZero; @@ -2367,12 +2468,36 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( // This could be Opts.Optimize if we would also unique DIVariables here. bool Optimized = false; // Create the descriptor for the variable. + unsigned DVarLine = DInstLine; + if (VarInfo.Loc) { + auto DVarLoc = getStartLocation(VarInfo.Loc); + DVarLine = DVarLoc.line; + } + llvm::DIScope *VarScope = Scope; + if (VarInfo.Scope) { + if (auto *VS = dyn_cast_or_null( + getOrCreateScope(VarInfo.Scope))) + VarScope = VS; + } llvm::DILocalVariable *Var = (VarInfo.ArgNo > 0) - ? DBuilder.createParameterVariable(Scope, VarInfo.Name, VarInfo.ArgNo, - Unit, Line, DITy, Optimized, Flags) - : DBuilder.createAutoVariable(Scope, VarInfo.Name, Unit, Line, DITy, - Optimized, Flags); + ? DBuilder.createParameterVariable(VarScope, VarInfo.Name, + VarInfo.ArgNo, Unit, DVarLine, + DITy, Optimized, Flags) + : DBuilder.createAutoVariable(VarScope, VarInfo.Name, Unit, DVarLine, + DITy, Optimized, Flags); + + auto appendDIExpression = + [&VarInfo, this](llvm::DIExpression *DIExpr) -> llvm::DIExpression * { + if (VarInfo.DIExpr) { + llvm::SmallVector Operands; + if (!buildDebugInfoExpression(VarInfo, Operands)) + return nullptr; + if (Operands.size()) + return llvm::DIExpression::append(DIExpr, Operands); + } + return DIExpr; + }; // Running variables for the current/previous piece. bool IsPiece = Storage.size() > 1; @@ -2404,30 +2529,25 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( Operands.push_back(OffsetInBits); Operands.push_back(SizeInBits); } - emitDbgIntrinsic(Builder, Piece, Var, DBuilder.createExpression(Operands), - Line, Loc.column, Scope, DS, - Indirection == CoroDirectValue || - Indirection == CoroIndirectValue); + llvm::DIExpression *DIExpr = DBuilder.createExpression(Operands); + // DW_OP_LLVM_fragment must be the last part of an DIExpr + // so we can't append more if IsPiece is true. + if (!IsPiece) + DIExpr = appendDIExpression(DIExpr); + if (DIExpr) + emitDbgIntrinsic( + Builder, Piece, Var, DIExpr, DInstLine, DInstLoc.column, Scope, DS, + Indirection == CoroDirectValue || Indirection == CoroIndirectValue); } // Emit locationless intrinsic for variables that were optimized away. - if (Storage.empty()) - emitDbgIntrinsic(Builder, llvm::ConstantInt::get(IGM.Int64Ty, 0), Var, - DBuilder.createExpression(), Line, Loc.column, Scope, DS); -} - -static bool pointsIntoAlloca(llvm::Value *Storage) { - while (Storage) { - if (auto *LdInst = dyn_cast(Storage)) - Storage = LdInst->getOperand(0); - else if (auto *GEPInst = dyn_cast(Storage)) - Storage = GEPInst->getOperand(0); - else if (auto *BCInst = dyn_cast(Storage)) - Storage = BCInst->getOperand(0); - else - return isa(Storage); + if (Storage.empty()) { + if (auto *DIExpr = appendDIExpression(DBuilder.createExpression())) + emitDbgIntrinsic(Builder, llvm::ConstantInt::get(IGM.Int64Ty, 0), Var, + DIExpr, DInstLine, DInstLoc.column, Scope, DS, + Indirection == CoroDirectValue || + Indirection == CoroIndirectValue); } - return false; } void IRGenDebugInfoImpl::emitDbgIntrinsic( @@ -2441,9 +2561,27 @@ void IRGenDebugInfoImpl::emitDbgIntrinsic( auto *BB = Builder.GetInsertBlock(); // An alloca may only be described by exactly one dbg.declare. - if (isa(Storage) && !llvm::FindDbgAddrUses(Storage).empty()) + if (isa(Storage) && + !llvm::FindDbgDeclareUses(Storage).empty()) return; + // Fragment DIExpression cannot cover the whole variable + // or going out-of-bound. + if (auto Fragment = Expr->getFragmentInfo()) + if (auto VarSize = Var->getSizeInBits()) { + unsigned FragSize = Fragment->SizeInBits; + unsigned FragOffset = Fragment->OffsetInBits; + if (FragOffset + FragSize > *VarSize || + FragSize == *VarSize) { + // Drop the fragment part + assert(Expr->isValid()); + // Since this expression is valid, DW_OP_LLVM_fragment + // and its arguments must be the last 3 elements. + auto OrigElements = Expr->getElements(); + Expr = DBuilder.createExpression(OrigElements.drop_back(3)); + } + } + // A dbg.declare is only meaningful if there is a single alloca for // the variable that is live throughout the function. if (auto *Alloca = dyn_cast(Storage)) { @@ -2471,10 +2609,19 @@ void IRGenDebugInfoImpl::emitDbgIntrinsic( else DBuilder.insertDeclare(Storage, Var, Expr, DL, &EntryBlock); } else { - if (pointsIntoAlloca(Storage)) - DBuilder.insertDeclare(Storage, Var, Expr, DL, BB); + // Insert a dbg.value at the current insertion point. + if (isa(Storage) && !Var->getArg() && + BB->getFirstNonPHIOrDbg()) + // SelectionDAGISel only generates debug info for a dbg.value + // that is associated with a llvm::Argument if either its !DIVariable + // is marked as argument or there is no non-debug intrinsic instruction + // before it. So In the case of associating a llvm::Argument with a + // non-argument debug variable -- usually via a !DIExpression -- we + // need to make sure that dbg.value is before any non-phi / no-dbg + // instruction. + DBuilder.insertDbgValueIntrinsic(Storage, Var, Expr, DL, + BB->getFirstNonPHIOrDbg()); else - // Insert a dbg.value at the current insertion point. DBuilder.insertDbgValueIntrinsic(Storage, Var, Expr, DL, BB); } } @@ -2491,8 +2638,7 @@ void IRGenDebugInfoImpl::emitGlobalVariableDeclaration( if (MetatypeType *metaTy = dyn_cast(ty)) ty = metaTy->getInstanceType().getPointer(); if (ty->getStructOrBoundGenericStruct() && - ty->getStructOrBoundGenericStruct()->getClangDecl() && - isa( + isa_and_nonnull( ty->getStructOrBoundGenericStruct()->getClangDecl())) return; } @@ -2548,7 +2694,7 @@ void IRGenDebugInfoImpl::emitTypeMetadata(IRGenFunction &IGF, Metadata->getType(), Size(CI.getTargetInfo().getPointerWidth(0)), Alignment(CI.getTargetInfo().getPointerAlign(0))); emitVariableDeclaration(IGF.Builder, Metadata, DbgTy, IGF.getDebugScope(), - nullptr, {OS.str().str(), 0, false}, + {}, {OS.str().str(), 0, false}, // swift.type is already a pointer type, // having a shadow copy doesn't add another // layer of indirection. @@ -2646,10 +2792,10 @@ void IRGenDebugInfo::emitArtificialFunction(IRBuilder &Builder, void IRGenDebugInfo::emitVariableDeclaration( IRBuilder &Builder, ArrayRef Storage, DebugTypeInfo Ty, - const SILDebugScope *DS, ValueDecl *VarDecl, SILDebugVariable VarInfo, + const SILDebugScope *DS, Optional VarLoc, SILDebugVariable VarInfo, IndirectionKind Indirection, ArtificialKind Artificial) { static_cast(this)->emitVariableDeclaration( - Builder, Storage, Ty, DS, VarDecl, VarInfo, Indirection, Artificial); + Builder, Storage, Ty, DS, VarLoc, VarInfo, Indirection, Artificial); } void IRGenDebugInfo::emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage, diff --git a/lib/IRGen/IRGenDebugInfo.h b/lib/IRGen/IRGenDebugInfo.h index 9de1d06aaea70..dd81f6cb3ceed 100644 --- a/lib/IRGen/IRGenDebugInfo.h +++ b/lib/IRGen/IRGenDebugInfo.h @@ -141,7 +141,8 @@ class IRGenDebugInfo { void emitVariableDeclaration(IRBuilder &Builder, ArrayRef Storage, DebugTypeInfo Ty, const SILDebugScope *DS, - ValueDecl *VarDecl, SILDebugVariable VarInfo, + Optional VarLoc, + SILDebugVariable VarInfo, IndirectionKind Indirection = DirectValue, ArtificialKind Artificial = RealValue); diff --git a/lib/IRGen/IRGenFunction.cpp b/lib/IRGen/IRGenFunction.cpp index 85dfbeec53bc3..321a2b02b83c1 100644 --- a/lib/IRGen/IRGenFunction.cpp +++ b/lib/IRGen/IRGenFunction.cpp @@ -526,9 +526,15 @@ static llvm::Value *unsafeContinuationFromTask(IRGenFunction &IGF, static llvm::Value *emitLoadOfResumeContextFromTask(IRGenFunction &IGF, llvm::Value *task) { + // Task.ResumeContext is at field index 8 within SwiftTaskTy. The offset comes + // from 7 pointers (two within the single RefCountedStructTy) and 2 Int32 + // fields. + const unsigned taskResumeContextIndex = 8; + const Size taskResumeContextOffset = (7 * IGF.IGM.getPointerSize()) + Size(8); + auto addr = Address(task, IGF.IGM.getPointerAlignment()); - auto resumeContextAddr = - IGF.Builder.CreateStructGEP(addr, 5, 6 * IGF.IGM.getPointerSize()); + auto resumeContextAddr = IGF.Builder.CreateStructGEP( + addr, taskResumeContextIndex, taskResumeContextOffset); llvm::Value *resumeContext = IGF.Builder.CreateLoad(resumeContextAddr); if (auto &schema = IGF.getOptions().PointerAuth.TaskResumeContext) { auto info = PointerAuthInfo::emit(IGF, schema, @@ -652,6 +658,17 @@ void IRGenFunction::emitGetAsyncContinuation(SILType resumeTy, out.add(unsafeContinuation); } +static bool shouldUseContinuationAwait(IRGenModule &IGM) { + auto &ctx = IGM.Context; + auto module = ctx.getLoadedModule(ctx.Id_Concurrency); + assert(module && "building async code without concurrency library"); + SmallVector results; + module->lookupValue(ctx.getIdentifier("_abiEnableAwaitContinuation"), + NLKind::UnqualifiedLookup, results); + assert(results.size() <= 1); + return !results.empty(); +} + void IRGenFunction::emitAwaitAsyncContinuation( SILType resumeTy, bool isIndirectResult, Explosion &outDirectResult, llvm::BasicBlock *&normalBB, @@ -659,50 +676,45 @@ void IRGenFunction::emitAwaitAsyncContinuation( assert(AsyncCoroutineCurrentContinuationContext && "no active continuation"); auto pointerAlignment = IGM.getPointerAlignment(); - // Check whether the continuation has already been resumed. - // If so, we can just immediately continue with the control flow. - // Otherwise, we need to suspend, and resuming the continuation will - // trigger the function to resume. - // - // We do this by atomically trying to change the synchronization field - // in the continuation context from 0 (the state it was initialized - // with) to 1. If this fails, the continuation must already have been - // resumed, so we can bypass the suspension point and immediately - // start interpreting the result stored in the continuation. - // Note that we use a strong compare-exchange (the default for the LLVM - // cmpxchg instruction), so spurious failures are disallowed; we can - // therefore trust that a failure means that the continuation has - // already been resumed. - - auto contAwaitSyncAddr = - Builder.CreateStructGEP(AsyncCoroutineCurrentContinuationContext, 1); - - auto pendingV = llvm::ConstantInt::get( - contAwaitSyncAddr->getType()->getPointerElementType(), - unsigned(ContinuationStatus::Pending)); - auto awaitedV = llvm::ConstantInt::get( - contAwaitSyncAddr->getType()->getPointerElementType(), - unsigned(ContinuationStatus::Awaited)); - auto results = Builder.CreateAtomicCmpXchg( - contAwaitSyncAddr, pendingV, awaitedV, - llvm::AtomicOrdering::Release /*success ordering*/, - llvm::AtomicOrdering::Acquire /* failure ordering */, - llvm::SyncScope::System); - auto firstAtAwait = Builder.CreateExtractValue(results, 1); - auto contBB = createBasicBlock("await.async.resume"); - auto abortBB = createBasicBlock("await.async.abort"); - Builder.CreateCondBr(firstAtAwait, abortBB, contBB); - Builder.emitBlock(abortBB); - { - // We were the first to the sync point. "Abort" (return from the - // coroutine partial function, without making a tail call to anything) - // because the continuation result is not available yet. When the - // continuation is later resumed, the task will get scheduled - // starting from the suspension point. - emitCoroutineOrAsyncExit(); + // Call swift_continuation_await to check whether the continuation + // has already been resumed. + bool useContinuationAwait = shouldUseContinuationAwait(IGM); + + // As a temporary hack for compatibility with SDKs that don't provide + // swift_continuation_await, emit the old inline sequence. This can + // be removed as soon as we're sure that such SDKs don't exist. + if (!useContinuationAwait) { + auto contAwaitSyncAddr = + Builder.CreateStructGEP(AsyncCoroutineCurrentContinuationContext, 1); + + auto pendingV = llvm::ConstantInt::get( + contAwaitSyncAddr->getType()->getPointerElementType(), + unsigned(ContinuationStatus::Pending)); + auto awaitedV = llvm::ConstantInt::get( + contAwaitSyncAddr->getType()->getPointerElementType(), + unsigned(ContinuationStatus::Awaited)); + auto results = Builder.CreateAtomicCmpXchg( + contAwaitSyncAddr, pendingV, awaitedV, + llvm::AtomicOrdering::Release /*success ordering*/, + llvm::AtomicOrdering::Acquire /* failure ordering */, + llvm::SyncScope::System); + auto firstAtAwait = Builder.CreateExtractValue(results, 1); + auto contBB = createBasicBlock("await.async.resume"); + auto abortBB = createBasicBlock("await.async.abort"); + Builder.CreateCondBr(firstAtAwait, abortBB, contBB); + Builder.emitBlock(abortBB); + { + // We were the first to the sync point. "Abort" (return from the + // coroutine partial function, without making a tail call to anything) + // because the continuation result is not available yet. When the + // continuation is later resumed, the task will get scheduled + // starting from the suspension point. + emitCoroutineOrAsyncExit(); + } + + Builder.emitBlock(contBB); } - Builder.emitBlock(contBB); { // Set up the suspend point. SmallVector arguments; @@ -712,15 +724,26 @@ void IRGenFunction::emitAwaitAsyncContinuation( auto resumeProjFn = getOrCreateResumePrjFn(); arguments.push_back( Builder.CreateBitOrPointerCast(resumeProjFn, IGM.Int8PtrTy)); - // The dispatch function just calls the resume point. - auto resumeFnPtr = + + llvm::Constant *awaitFnPtr; + if (useContinuationAwait) { + awaitFnPtr = IGM.getAwaitAsyncContinuationFn(); + } else { + auto resumeFnPtr = getFunctionPointerForResumeIntrinsic(AsyncCoroutineCurrentResume); - arguments.push_back(Builder.CreateBitOrPointerCast( - createAsyncDispatchFn(resumeFnPtr, {IGM.Int8PtrTy}), - IGM.Int8PtrTy)); - arguments.push_back(AsyncCoroutineCurrentResume); - arguments.push_back(Builder.CreateBitOrPointerCast( + awaitFnPtr = createAsyncDispatchFn(resumeFnPtr, {IGM.Int8PtrTy}); + } + arguments.push_back( + Builder.CreateBitOrPointerCast(awaitFnPtr, IGM.Int8PtrTy)); + + if (useContinuationAwait) { + arguments.push_back(AsyncCoroutineCurrentContinuationContext); + } else { + arguments.push_back(AsyncCoroutineCurrentResume); + arguments.push_back(Builder.CreateBitOrPointerCast( AsyncCoroutineCurrentContinuationContext, IGM.Int8PtrTy)); + } + auto resultTy = llvm::StructType::get(IGM.getLLVMContext(), {IGM.Int8PtrTy}, false /*packed*/); emitSuspendAsyncCall(swiftAsyncContextIndex, resultTy, arguments); diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index b19515ee7ece0..a7a45224b6483 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -364,8 +364,20 @@ class IRGenFunction { private: llvm::Instruction *AllocaIP; const SILDebugScope *DbgScope; + /// The insertion point where we should but instructions we would normally put + /// at the beginning of the function. LLVM's coroutine lowering really does + /// not like it if we put instructions with side-effectrs before the + /// coro.begin. + llvm::Instruction *EarliestIP; -//--- Reference-counting methods ----------------------------------------------- +public: + void setEarliestInsertionPoint(llvm::Instruction *inst) { EarliestIP = inst; } + /// Returns the first insertion point before which we should insert + /// instructions which have side-effects. + llvm::Instruction *getEarliestInsertionPoint() const { return EarliestIP; } + + //--- Reference-counting methods + //----------------------------------------------- public: // Returns the default atomicity of the module. Atomicity getDefaultAtomicity(); diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index f83b44a5f4238..6129df0ed3484 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -36,7 +36,7 @@ const char *getManglingForWitness(swift::Demangle::ValueWitnessKind kind) { std::string IRGenMangler::mangleValueWitness(Type type, ValueWitness witness) { beginMangling(); - appendType(type); + appendType(type, nullptr); const char *Code = nullptr; switch (witness) { @@ -97,7 +97,7 @@ IRGenMangler::withSymbolicReferences(IRGenModule &IGM, // The short-substitution types in the standard library have compact // manglings already, and the runtime ought to have a lookup table for // them. Symbolic referencing would be wasteful. - if (type->getModuleContext()->isStdlibModule() + if (type->getModuleContext()->hasStandardSubstitutions() && Mangle::getStandardTypeSubst(type->getName().str())) { return false; } @@ -147,8 +147,7 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM, CanGenericSignature Sig, CanType Ty) { return withSymbolicReferences(IGM, [&]{ - bindGenericParameters(Sig); - appendType(Ty); + appendType(Ty, Sig); }); } @@ -191,7 +190,7 @@ std::string IRGenMangler::mangleTypeForLLVMTypeName(CanType Ty) { appendProtocolName(P->getDecl(), /*allowStandardSubstitution=*/false); appendOperator("P"); } else { - appendType(Ty); + appendType(Ty, nullptr); } return finalize(); } @@ -224,7 +223,7 @@ mangleProtocolForLLVMTypeName(ProtocolCompositionType *type) { ->getDeclaredType(); } - appendType(CanType(superclass)); + appendType(CanType(superclass), nullptr); appendOperator("Xc"); } else if (layout.getLayoutConstraint()) { appendOperator("Xl"); @@ -305,7 +304,7 @@ std::string IRGenMangler::mangleSymbolNameForMangledMetadataAccessorString( appendGenericSignature(genericSig); if (type) - appendType(type); + appendType(type, genericSig); return finalize(); } diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index e4d05de2f0d25..e3e0d4174b201 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -311,6 +311,7 @@ class IRGenMangler : public Mangle::ASTMangler { bool isAssocTypeAtDepth = false; (void)appendAssocType( assocType->getDeclaredInterfaceType()->castTo(), + nullptr, isAssocTypeAtDepth); appendOperator("Tl"); return finalize(); @@ -323,7 +324,7 @@ class IRGenMangler : public Mangle::ASTMangler { beginMangling(); appendAnyGenericType(proto); if (isa(subject)) { - appendType(subject); + appendType(subject, nullptr); } else { bool isFirstAssociatedTypeIdentifier = true; appendAssociatedTypePath(subject, isFirstAssociatedTypeIdentifier); @@ -426,6 +427,7 @@ class IRGenMangler : public Mangle::ASTMangler { const ProtocolDecl *Proto) { beginMangling(); appendProtocolConformance(Conformance); + bool isFirstAssociatedTypeIdentifier = true; appendAssociatedTypePath(AssociatedType, isFirstAssociatedTypeIdentifier); appendAnyGenericType(Proto); @@ -446,7 +448,7 @@ class IRGenMangler : public Mangle::ASTMangler { void appendAssociatedTypePath(CanType associatedType, bool &isFirst) { if (auto memberType = dyn_cast(associatedType)) { appendAssociatedTypePath(memberType.getBase(), isFirst); - appendAssociatedTypeName(memberType); + appendAssociatedTypeName(memberType, nullptr); appendListSeparator(isFirst); } else { assert(isa(associatedType)); @@ -483,8 +485,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedCopyFunction(CanType ty, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(ty); + appendType(ty, sig); if (sig) appendGenericSignature(sig); appendOperator("WOy"); @@ -493,8 +494,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedConsumeFunction(CanType ty, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(ty); + appendType(ty, sig); if (sig) appendGenericSignature(sig); appendOperator("WOe"); @@ -504,8 +504,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedRetainFunction(CanType t, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(t); + appendType(t, sig); if (sig) appendGenericSignature(sig); appendOperator("WOr"); @@ -514,8 +513,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedReleaseFunction(CanType t, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(t); + appendType(t, sig); if (sig) appendGenericSignature(sig); appendOperator("WOs"); @@ -525,8 +523,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedInitializeWithTakeFunction(CanType t, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(t); + appendType(t, sig); if (sig) appendGenericSignature(sig); appendOperator("WOb"); @@ -535,8 +532,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedInitializeWithCopyFunction(CanType t, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(t); + appendType(t, sig); if (sig) appendGenericSignature(sig); appendOperator("WOc"); @@ -545,8 +541,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedAssignWithTakeFunction(CanType t, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(t); + appendType(t, sig); if (sig) appendGenericSignature(sig); appendOperator("WOd"); @@ -555,8 +550,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedAssignWithCopyFunction(CanType t, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(t); + appendType(t, sig); if (sig) appendGenericSignature(sig); appendOperator("WOf"); @@ -565,8 +559,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleOutlinedDestroyFunction(CanType t, CanGenericSignature sig) { beginMangling(); - bindGenericParameters(sig); - appendType(t); + appendType(t, sig); if (sig) appendGenericSignature(sig); appendOperator("WOh"); @@ -616,7 +609,7 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleTypeSymbol(Type type, const char *Op) { beginMangling(); - appendType(type); + appendType(type, nullptr); appendOperator(Op); return finalize(); } @@ -634,13 +627,18 @@ class IRGenMangler : public Mangle::ASTMangler { const char *Op) { beginMangling(); if (type) - appendType(type); + appendType(type, nullptr); appendProtocolConformance(Conformance); appendOperator(Op); return finalize(); } }; +/// Does this type require a special minimum Swift runtime version which +/// supports demangling it? +Optional +getRuntimeVersionThatSupportsDemanglingType(CanType type); + } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index c513cfebe7f56..6f2ac630d2f15 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -113,7 +113,7 @@ static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context, break; case IRGenDebugInfoLevel::ASTTypes: case IRGenDebugInfoLevel::DwarfTypes: - CGO.DebugTypeExtRefs = true; + CGO.DebugTypeExtRefs = !Opts.DisableClangModuleSkeletonCUs; CGO.setDebugInfo(clang::codegenoptions::DebugInfoKind::FullDebugInfo); break; } @@ -610,7 +610,9 @@ IRGenModule::IRGenModule(IRGenerator &irgen, SwiftTaskTy = createStructType(*this, "swift.task", { RefCountedStructTy, // object header Int8PtrTy, Int8PtrTy, // Job.SchedulerPrivate - SizeTy, // Job.Flags + Int32Ty, // Job.Flags + Int32Ty, // Job.ID + Int8PtrTy, Int8PtrTy, // Reserved FunctionPtrTy, // Job.RunJob/Job.ResumeTask SwiftContextPtrTy, // Task.ResumeContext IntPtrTy // Task.Status @@ -619,7 +621,17 @@ IRGenModule::IRGenModule(IRGenerator &irgen, AsyncFunctionPointerPtrTy = AsyncFunctionPointerTy->getPointerTo(DefaultAS); SwiftTaskPtrTy = SwiftTaskTy->getPointerTo(DefaultAS); SwiftAsyncLetPtrTy = Int8PtrTy; // we pass it opaquely (AsyncLet*) + SwiftTaskOptionRecordPtrTy = SizeTy; // Builtin.RawPointer? that we get as (TaskOptionRecord*) SwiftTaskGroupPtrTy = Int8PtrTy; // we pass it opaquely (TaskGroup*) + SwiftTaskOptionRecordTy = createStructType(*this, "swift.task_option", { + SizeTy, // Flags + SwiftTaskOptionRecordPtrTy, // Parent + }); + SwiftTaskGroupTaskOptionRecordTy = createStructType( + *this, "swift.task_group_task_option", { + SwiftTaskOptionRecordTy, // Base option record + SwiftTaskGroupPtrTy, // Task group + }); ExecutorFirstTy = SizeTy; ExecutorSecondTy = SizeTy; SwiftExecutorTy = createStructType(*this, "swift.executor", { @@ -629,7 +641,9 @@ IRGenModule::IRGenModule(IRGenerator &irgen, SwiftJobTy = createStructType(*this, "swift.job", { RefCountedStructTy, // object header Int8PtrTy, Int8PtrTy, // SchedulerPrivate - SizeTy, // flags + Int32Ty, // flags + Int32Ty, // ID + Int8PtrTy, Int8PtrTy, // Reserved FunctionPtrTy, // RunJob/ResumeTask }); SwiftJobPtrTy = SwiftJobTy->getPointerTo(DefaultAS); @@ -779,6 +793,26 @@ namespace RuntimeConstants { } return RuntimeAvailability::AlwaysAvailable; } + + RuntimeAvailability + MultiPayloadEnumTagSinglePayloadAvailability(ASTContext &context) { + auto featureAvailability = context.getMultiPayloadEnumTagSinglePayload(); + if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } + + RuntimeAvailability + ObjCIsUniquelyReferencedAvailability(ASTContext &context) { + auto featureAvailability = + context.getObjCIsUniquelyReferencedAvailability(); + if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } + } // namespace RuntimeConstants // We don't use enough attributes to justify generalizing the @@ -1298,18 +1332,6 @@ static bool replaceModuleFlagsEntry(llvm::LLVMContext &Ctx, llvm_unreachable("Could not replace old linker options entry?"); } -/// Returns true if the object file generated by \p IGM will be the "first" -/// object file in the module. This lets us determine where to put a symbol -/// that must be unique. -static bool isFirstObjectFileInModule(IRGenModule &IGM) { - if (IGM.getSILModule().isWholeModule()) - return IGM.IRGen.getPrimaryIGM() == &IGM; - - auto *file = cast(IGM.getSILModule().getAssociatedContext()); - auto *containingModule = file->getParentModule(); - return containingModule->getFiles().front() == file; -} - static bool doesTargetAutolinkUsingAutolinkExtract(const SwiftTargetInfo &TargetInfo, const llvm::Triple &Triple) { @@ -1484,6 +1506,47 @@ AutolinkKind AutolinkKind::create(const SwiftTargetInfo &TargetInfo, return AutolinkKind::LLVMLinkerOptions; } +static llvm::GlobalObject *createForceImportThunk(IRGenModule &IGM) { + llvm::SmallString<64> buf; + encodeForceLoadSymbolName(buf, IGM.IRGen.Opts.ForceLoadSymbolName); + if (IGM.Triple.isOSBinFormatMachO()) { + // On Mach-O targets, emit a common force-load symbol that resolves to + // a global variable so it will be coalesced at static link time into a + // single external symbol. + // + // Looks like C's tentative definitions are good for something after all. + auto ForceImportThunk = + new llvm::GlobalVariable(IGM.Module, + IGM.Int1Ty, + /*isConstant=*/false, + llvm::GlobalValue::CommonLinkage, + llvm::Constant::getNullValue(IGM.Int1Ty), + buf.str()); + ApplyIRLinkage(IRLinkage::ExternalCommon).to(ForceImportThunk); + IGM.addUsedGlobal(ForceImportThunk); + return ForceImportThunk; + } else { + // On all other targets, emit an external symbol that resolves to a + // function definition. On Windows, linkonce_odr is basically a no-op and + // the COMDAT we set by applying linkage gives us the desired coalescing + // behavior. + auto ForceImportThunk = + llvm::Function::Create(llvm::FunctionType::get(IGM.VoidTy, false), + llvm::GlobalValue::LinkOnceODRLinkage, buf, + &IGM.Module); + ForceImportThunk->setAttributes(IGM.constructInitialAttributes()); + ApplyIRLinkage(IRLinkage::ExternalExport).to(ForceImportThunk); + if (IGM.Triple.supportsCOMDAT()) + if (auto *GO = cast(ForceImportThunk)) + GO->setComdat(IGM.Module.getOrInsertComdat(ForceImportThunk->getName())); + + auto BB = llvm::BasicBlock::Create(IGM.getLLVMContext(), "", ForceImportThunk); + llvm::IRBuilder<> IRB(BB); + IRB.CreateRetVoid(); + return ForceImportThunk; + } +} + void IRGenModule::emitAutolinkInfo() { auto Autolink = AutolinkKind::create(TargetInfo, Triple, IRGen.Opts.LLVMLTOKind); @@ -1502,23 +1565,8 @@ void IRGenModule::emitAutolinkInfo() { Autolink.writeEntries(Entries, Metadata, *this); - if (!IRGen.Opts.ForceLoadSymbolName.empty() && - (Triple.supportsCOMDAT() || isFirstObjectFileInModule(*this))) { - llvm::SmallString<64> buf; - encodeForceLoadSymbolName(buf, IRGen.Opts.ForceLoadSymbolName); - auto ForceImportThunk = - llvm::Function::Create(llvm::FunctionType::get(VoidTy, false), - llvm::GlobalValue::ExternalLinkage, buf, - &Module); - ForceImportThunk->setAttributes(constructInitialAttributes()); - ApplyIRLinkage(IRLinkage::ExternalExport).to(ForceImportThunk); - if (Triple.supportsCOMDAT()) - if (auto *GO = cast(ForceImportThunk)) - GO->setComdat(Module.getOrInsertComdat(ForceImportThunk->getName())); - - auto BB = llvm::BasicBlock::Create(getLLVMContext(), "", ForceImportThunk); - llvm::IRBuilder<> IRB(BB); - IRB.CreateRetVoid(); + if (!IRGen.Opts.ForceLoadSymbolName.empty()) { + (void) createForceImportThunk(*this); } } @@ -1651,10 +1699,10 @@ bool IRGenModule::useDllStorage() { return ::useDllStorage(Triple); } bool IRGenModule::shouldPrespecializeGenericMetadata() { auto canPrespecializeTarget = - (Triple.isOSDarwin() || Triple.isTvOS() || + (Triple.isOSDarwin() || (Triple.isOSLinux() && !(Triple.isARM() && Triple.isArch32Bit()))); if (canPrespecializeTarget && isStandardLibrary()) { - return true; + return IRGen.Opts.PrespecializeGenericMetadata; } auto &context = getSwiftModule()->getASTContext(); auto deploymentAvailability = diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 07aae5e6c366e..7f8ce0a1b9240 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -733,7 +733,10 @@ class IRGenModule { llvm::PointerType *SwiftContextPtrTy; llvm::PointerType *SwiftTaskPtrTy; llvm::PointerType *SwiftAsyncLetPtrTy; + llvm::IntegerType *SwiftTaskOptionRecordPtrTy; llvm::PointerType *SwiftTaskGroupPtrTy; + llvm::StructType *SwiftTaskOptionRecordTy; + llvm::StructType *SwiftTaskGroupTaskOptionRecordTy; llvm::PointerType *SwiftJobPtrTy; llvm::IntegerType *ExecutorFirstTy; llvm::IntegerType *ExecutorSecondTy; @@ -849,7 +852,7 @@ class IRGenModule { llvm::PointerType *getEnumValueWitnessTablePtrTy(); void unimplemented(SourceLoc, StringRef Message); - LLVM_ATTRIBUTE_NORETURN + [[noreturn]] void fatal_unimplemented(SourceLoc, StringRef Message); void error(SourceLoc loc, const Twine &message); @@ -1152,9 +1155,6 @@ class IRGenModule { /// categories. SmallVector ObjCCategoryDecls; - /// List of fields descriptors to register in runtime. - SmallVector FieldDescriptors; - /// Map of Objective-C protocols and protocol references, bitcast to i8*. /// The interesting global variables relating to an ObjC protocol. struct ObjCProtocolPair { @@ -1330,6 +1330,7 @@ private: \ llvm::Constant *getFixLifetimeFn(); llvm::Constant *getFixedClassInitializationFn(); + llvm::Function *getAwaitAsyncContinuationFn(); /// The constructor used when generating code. /// @@ -1393,7 +1394,7 @@ private: \ void finishEmitAfterTopLevel(); Signature getSignature(CanSILFunctionType fnType, - bool suppressGenerics = false); + bool useSpecialConvention = false); llvm::FunctionType *getFunctionType(CanSILFunctionType type, llvm::AttributeList &attrs, ForeignFunctionInfo *foreignInfo=nullptr); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 8b21ac315a61d..925e4d95b5422 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -687,7 +687,8 @@ class IRGenSILFunction : return; llvm::IRBuilder<> ZeroInitBuilder(AI->getNextNode()); - + ZeroInitBuilder.SetInsertPoint(getEarliestInsertionPoint()->getParent(), + getEarliestInsertionPoint()->getIterator()); // No debug location is how LLVM marks prologue instructions. ZeroInitBuilder.SetCurrentDebugLocation(nullptr); ZeroInitBuilder.CreateMemSet( @@ -1056,15 +1057,15 @@ class IRGenSILFunction : template void emitDebugVariableDeclaration(StorageType Storage, DebugTypeInfo Ty, SILType SILTy, const SILDebugScope *DS, - VarDecl *VarDecl, SILDebugVariable VarInfo, - IndirectionKind Indirection = DirectValue) { + SILLocation VarLoc, + SILDebugVariable VarInfo, + IndirectionKind Indirection) { // TODO: fix demangling for C++ types (SR-13223). if (swift::TypeBase *ty = SILTy.getASTType().getPointer()) { if (MetatypeType *metaTy = dyn_cast(ty)) ty = metaTy->getRootClass().getPointer(); if (ty->getStructOrBoundGenericStruct() && - ty->getStructOrBoundGenericStruct()->getClangDecl() && - isa( + isa_and_nonnull( ty->getStructOrBoundGenericStruct()->getClangDecl())) return; } @@ -1072,10 +1073,10 @@ class IRGenSILFunction : assert(IGM.DebugInfo && "debug info not enabled"); if (VarInfo.ArgNo) { PrologueLocation AutoRestore(IGM.DebugInfo.get(), Builder); - IGM.DebugInfo->emitVariableDeclaration(Builder, Storage, Ty, DS, VarDecl, + IGM.DebugInfo->emitVariableDeclaration(Builder, Storage, Ty, DS, VarLoc, VarInfo, Indirection); } else - IGM.DebugInfo->emitVariableDeclaration(Builder, Storage, Ty, DS, VarDecl, + IGM.DebugInfo->emitVariableDeclaration(Builder, Storage, Ty, DS, VarLoc, VarInfo, Indirection); } @@ -1145,7 +1146,6 @@ class IRGenSILFunction : llvm_unreachable("unimplemented"); } void visitDebugValueInst(DebugValueInst *i); - void visitDebugValueAddrInst(DebugValueAddrInst *i); void visitRetainValueInst(RetainValueInst *i); void visitRetainValueAddrInst(RetainValueAddrInst *i); void visitCopyValueInst(CopyValueInst *i); @@ -1636,7 +1636,7 @@ void LoweredValue::getExplosion(IRGenFunction &IGF, SILType type, case Kind::DynamicallyEnforcedAddress: case Kind::CoroutineState: llvm_unreachable("not a value"); - + case Kind::ExplosionVector: ex.add(Storage.get(kind)); return; @@ -1707,7 +1707,7 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f) CurFn->addFnAttr(llvm::Attribute::SanitizeAddress); if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread) { auto declContext = f->getDeclContext(); - if (declContext && isa(declContext)) { + if (isa_and_nonnull(declContext)) { // Do not report races in deinit and anything called from it // because TSan does not observe synchronization between retain // count dropping to '0' and the object deinitialization. @@ -1730,11 +1730,6 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f) if (f->isDynamicallyReplaceable() && !f->isAsync()) { IGM.createReplaceableProlog(*this, f); } - - if (f->getLoweredFunctionType()->isAsync()) { - setupAsync(Signature::forAsyncEntry(IGM, f->getLoweredFunctionType()) - .getAsyncContextIndex()); - } } IRGenSILFunction::~IRGenSILFunction() { @@ -1933,11 +1928,6 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, witnessMetadata); } - // Bind the error result by popping it off the parameter list. - if (funcTy->hasErrorResult() && !funcTy->isAsync()) { - IGF.setCallerErrorResultSlot(emission->getCallerErrorResultArgument()); - } - // The coroutine context should be the first parameter. switch (funcTy->getCoroutineKind()) { case SILCoroutineKind::None: @@ -1951,10 +1941,11 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, } if (funcTy->isAsync()) { - emitAsyncFunctionEntry( - IGF, getAsyncContextLayout(IGF.IGM, IGF.CurSILFn), - LinkEntity::forSILFunction(IGF.CurSILFn), - Signature::forAsyncEntry(IGF.IGM, funcTy).getAsyncContextIndex()); + emitAsyncFunctionEntry(IGF, getAsyncContextLayout(IGF.IGM, IGF.CurSILFn), + LinkEntity::forSILFunction(IGF.CurSILFn), + Signature::forAsyncEntry( + IGF.IGM, funcTy, /*useSpecialConvention*/ false) + .getAsyncContextIndex()); if (IGF.CurSILFn->isDynamicallyReplaceable()) { IGF.IGM.createReplaceableProlog(IGF, IGF.CurSILFn); // Remap the entry block. @@ -1962,6 +1953,11 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, } } + // Bind the error result by popping it off the parameter list. + if (funcTy->hasErrorResult() && !funcTy->isAsync()) { + IGF.setCallerErrorResultSlot(emission->getCallerErrorResultArgument()); + } + SILFunctionConventions conv(funcTy, IGF.getSILModule()); // The 'self' argument might be in the context position, which is @@ -2503,7 +2499,7 @@ void IRGenSILFunction::visitDifferentiabilityWitnessFunctionInst( setLoweredFunctionPointer(i, FunctionPointer(fnType, diffWitness, signature)); } -static FunctionPointer::Kind classifyFunctionPointerKind(SILFunction *fn) { +FunctionPointer::Kind irgen::classifyFunctionPointerKind(SILFunction *fn) { using SpecialKind = FunctionPointer::SpecialKind; // Check for some special cases, which are currently all async: @@ -2519,6 +2515,14 @@ static FunctionPointer::Kind classifyFunctionPointerKind(SILFunction *fn) { if (name.equals("swift_asyncLet_wait_throwing")) return SpecialKind::AsyncLetWaitThrowing; + if (name.equals("swift_asyncLet_get")) + return SpecialKind::AsyncLetGet; + if (name.equals("swift_asyncLet_get_throwing")) + return SpecialKind::AsyncLetGetThrowing; + + if (name.equals("swift_asyncLet_finish")) + return SpecialKind::AsyncLetFinish; + if (name.equals("swift_taskGroup_wait_next_throwing")) return SpecialKind::TaskGroupWaitNext; } @@ -2530,9 +2534,9 @@ void IRGenSILFunction::visitFunctionRefBaseInst(FunctionRefBaseInst *i) { auto fn = i->getInitiallyReferencedFunction(); auto fnType = fn->getLoweredFunctionType(); - auto fpKind = classifyFunctionPointerKind(fn); + auto fpKind = irgen::classifyFunctionPointerKind(fn); - auto sig = IGM.getSignature(fnType, fpKind.suppressGenerics()); + auto sig = IGM.getSignature(fnType, fpKind.useSpecialConvention()); // Note that the pointer value returned by getAddrOfSILFunction doesn't // necessarily have element type sig.getType(), e.g. if it's imported. @@ -3072,11 +3076,15 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { } // Pass the generic arguments. - if (hasPolymorphicParameters(origCalleeType) && - !emission->getCallee().getFunctionPointer().suppressGenerics()) { + auto useSpecialConvention = + emission->getCallee().getFunctionPointer().useSpecialConvention(); + if (hasPolymorphicParameters(origCalleeType) && !useSpecialConvention) { SubstitutionMap subMap = site.getSubstitutionMap(); emitPolymorphicArguments(*this, origCalleeType, subMap, &witnessMetadata, llArgs); + } else if (useSpecialConvention) { + llArgs.add(emission->getResumeFunctionPointer()); + llArgs.add(emission->getAsyncContext()); } // Add all those arguments. @@ -4590,7 +4598,7 @@ void IRGenSILFunction::emitErrorResultVar(CanSILFunctionType FnTy, ErrorResultSlot->getType(), IGM.getPointerSize(), IGM.getPointerAlignment()); IGM.DebugInfo->emitVariableDeclaration(Builder, Storage, DbgTy, - getDebugScope(), nullptr, *Var, + getDebugScope(), {}, *Var, IndirectValue, ArtificialValue); } @@ -4695,8 +4703,20 @@ void IRGenSILFunction::emitPoisonDebugValueInst(DebugValueInst *i) { Builder.CreateStore(newShadowVal, shadowAddress, ptrAlign); } +/// Determine whether the debug-info-carrying instruction \c i belongs to an +/// async function and thus may get allocated in the coroutine context. These +/// variables need to be marked with the Coro flag, so LLVM's CoroSplit pass can +/// recognize them. +static bool InCoroContext(SILFunction &f, SILInstruction &i) { + return f.isAsync() && !i.getDebugScope()->InlinedCallSite; +} + void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { + auto SILVal = i->getOperand(); + bool IsAddrVal = SILVal->getType().isAddress(); if (i->poisonRefs()) { + assert(!IsAddrVal && + "SIL values with address type should not have poison"); emitPoisonDebugValueInst(i); return; } @@ -4705,96 +4725,70 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { auto VarInfo = i->getVarInfo(); assert(VarInfo && "debug_value without debug info"); - auto SILVal = i->getOperand(); if (isa(SILVal)) { // We cannot track the location of inlined error arguments because it has no // representation in SIL. - if (!i->getDebugScope()->InlinedCallSite && VarInfo->Name == "$error") { + if (!IsAddrVal && + !i->getDebugScope()->InlinedCallSite && VarInfo->Name == "$error") { auto funcTy = CurSILFn->getLoweredFunctionType(); emitErrorResultVar(funcTy, funcTy->getErrorResult(), i); } return; } + bool IsInCoro = InCoroContext(*CurSILFn, *i); bool IsAnonymous = false; VarInfo->Name = getVarName(i, IsAnonymous); DebugTypeInfo DbgTy; - SILType SILTy = SILVal->getType(); - auto RealTy = SILVal->getType().getASTType(); - if (VarDecl *Decl = i->getDecl()) { - DbgTy = DebugTypeInfo::getLocalVariable( - Decl, RealTy, getTypeInfo(SILVal->getType())); - } else if (i->getFunction()->isBare() && !SILTy.hasArchetype() && - !VarInfo->Name.empty()) { - // Preliminary support for .sil debug information. - DbgTy = DebugTypeInfo::getFromTypeInfo(RealTy, getTypeInfo(SILTy)); - } else - return; - - // Put the value into a stack slot at -Onone. - llvm::SmallVector Copy; - emitShadowCopyIfNeeded(SILVal, i->getDebugScope(), *VarInfo, IsAnonymous, - Copy); - bindArchetypes(DbgTy.getType()); - if (!IGM.DebugInfo) - return; - - IndirectionKind Indirection = DirectValue; - if (CurSILFn->isAsync() && !i->getDebugScope()->InlinedCallSite && - (Copy.empty() || !isa(Copy[0]))) { - Indirection = CoroDirectValue; - } - - emitDebugVariableDeclaration(Copy, DbgTy, SILTy, i->getDebugScope(), - i->getDecl(), *VarInfo, Indirection); -} - -void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) { - if (i->getDebugScope()->getInlinedFunction()->isTransparent()) - return; - - VarDecl *Decl = i->getDecl(); - if (!Decl) - return; - - auto SILVal = i->getOperand(); - if (isa(SILVal)) - return; + SILType SILTy; + if (auto MaybeSILTy = VarInfo->Type) + // If there is auxiliary type info, use it + SILTy = *MaybeSILTy; + else + SILTy = SILVal->getType(); - auto VarInfo = i->getVarInfo(); - assert(VarInfo && "debug_value_addr without debug info"); - bool IsAnonymous = false; - bool IsLoadablyByAddress = isa(SILVal); - IndirectionKind Indirection = - (IsLoadablyByAddress) ? DirectValue : IndirectValue; - VarInfo->Name = getVarName(i, IsAnonymous); - auto *Addr = getLoweredAddress(SILVal).getAddress(); - SILType SILTy = SILVal->getType(); - auto RealType = SILTy.getASTType(); - if (CurSILFn->isAsync() && !i->getDebugScope()->InlinedCallSite) { - Indirection = CoroIndirectValue; + auto RealTy = SILTy.getASTType(); + if (IsAddrVal && IsInCoro) if (auto *PBI = dyn_cast(i->getOperand())) { // Usually debug info only ever describes the *result* of a projectBox // call. To allow the debugger to display a boxed parameter of an async // continuation object, however, the debug info can only describe the box // itself and thus also needs to emit a box type for it so the debugger // knows to call into Remote Mirrors to unbox the value. - RealType = PBI->getOperand()->getType().getASTType(); - assert(isa(RealType)); + RealTy = PBI->getOperand()->getType().getASTType(); + assert(isa(RealTy)); } - } - auto DbgTy = DebugTypeInfo::getLocalVariable( - Decl, RealType, getTypeInfo(SILVal->getType())); + // Figure out the debug variable type + if (VarDecl *Decl = i->getDecl()) { + DbgTy = DebugTypeInfo::getLocalVariable( + Decl, RealTy, getTypeInfo(SILVal->getType())); + } else if (!SILTy.hasArchetype() && !VarInfo->Name.empty()) { + // Handle the cases that read from a SIL file + DbgTy = DebugTypeInfo::getFromTypeInfo(RealTy, getTypeInfo(SILTy)); + } else + return; + + // Since debug_value is expressing indirection explicitly via op_deref, + // we're not using either IndirectValue or CoroIndirectValue here. + IndirectionKind Indirection = IsInCoro? CoroDirectValue : DirectValue; + + // Put the value into a shadow-copy stack slot at -Onone. + llvm::SmallVector Copy; + if (IsAddrVal) + Copy.emplace_back( + emitShadowCopyIfNeeded(getLoweredAddress(SILVal).getAddress(), + i->getDebugScope(), *VarInfo, IsAnonymous)); + else + emitShadowCopyIfNeeded(SILVal, i->getDebugScope(), *VarInfo, IsAnonymous, + Copy); + bindArchetypes(DbgTy.getType()); if (!IGM.DebugInfo) return; - // Put the value's address into a stack slot at -Onone and emit a debug - // intrinsic. - emitDebugVariableDeclaration( - emitShadowCopyIfNeeded(Addr, i->getDebugScope(), *VarInfo, IsAnonymous), - DbgTy, SILType(), i->getDebugScope(), Decl, *VarInfo, Indirection); + emitDebugVariableDeclaration(Copy, DbgTy, SILTy, i->getDebugScope(), + i->getLoc(), *VarInfo, Indirection); } void IRGenSILFunction::visitFixLifetimeInst(swift::FixLifetimeInst *i) { @@ -5103,7 +5097,8 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, assert(isa(addr) || isa(addr) || isa(addr) || isCallToSwiftTaskAlloc(addr)); - auto Indirection = DirectValue; + auto Indirection = + InCoroContext(*CurSILFn, *i) ? CoroDirectValue : DirectValue; if (!IGM.IRGen.Opts.DisableDebuggerShadowCopies && !IGM.IRGen.Opts.shouldOptimize()) if (auto *Alloca = dyn_cast(addr)) @@ -5111,21 +5106,35 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, // Store the address of the dynamic alloca on the stack. addr = emitShadowCopy(addr, DS, *VarInfo, IGM.getPointerAlignment(), /*init*/ true); - Indirection = IndirectValue; + Indirection = + InCoroContext(*CurSILFn, *i) ? CoroIndirectValue : IndirectValue; } - if (!Decl) - return; - // Ignore compiler-generated patterns but not optional bindings. - if (auto *Pattern = Decl->getParentPattern()) - if (Pattern->isImplicit() && - Pattern->getKind() != PatternKind::OptionalSome) - return; + if (Decl) { + if (auto *Pattern = Decl->getParentPattern()) { + if (Pattern->isImplicit() && + Pattern->getKind() != PatternKind::OptionalSome) + return; + } + } - SILType SILTy = i->getType(); + SILType SILTy; + if (auto MaybeSILTy = VarInfo->Type) + // If there is auxiliary type info, use it + SILTy = *MaybeSILTy; + else + SILTy = i->getType(); auto RealType = SILTy.getASTType(); - auto DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type); + DebugTypeInfo DbgTy; + if (Decl) { + DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type); + } else if (i->getFunction()->isBare() && !SILTy.hasArchetype() && + !VarInfo->Name.empty()) { + DbgTy = DebugTypeInfo::getFromTypeInfo(RealType, getTypeInfo(SILTy)); + } else + return; + // Async functions use the value of the artificial address. auto shadow = addr; if (CurSILFn->isAsync() && emitLifetimeExtendingUse(shadow) && @@ -5139,7 +5148,8 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, bindArchetypes(DbgTy.getType()); if (IGM.DebugInfo) - emitDebugVariableDeclaration(addr, DbgTy, SILTy, DS, Decl, *VarInfo, + emitDebugVariableDeclaration(addr, DbgTy, SILTy, DS, + i->getLoc(), *VarInfo, Indirection); } @@ -5159,15 +5169,15 @@ void IRGenSILFunction::visitAllocStackInst(swift::AllocStackInst *i) { Address addr = stackAddr.getAddress(); // Generate Debug Info. - if (!Decl) + if (!i->getVarInfo()) return; - assert(i->getVarInfo() && "alloc_stack without debug info"); - - Type Desugared = Decl->getType()->getDesugaredType(); - if (Desugared->getClassOrBoundGenericClass() || - Desugared->getStructOrBoundGenericStruct()) - zeroInit(dyn_cast(addr.getAddress())); + if (Decl) { + Type Desugared = Decl->getType()->getDesugaredType(); + if (Desugared->getClassOrBoundGenericClass() || + Desugared->getStructOrBoundGenericStruct()) + zeroInit(dyn_cast(addr.getAddress())); + } emitDebugInfoForAllocStack(i, type, addr.getAddress()); } @@ -5352,9 +5362,9 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) { if (!IGM.DebugInfo) return; - IGM.DebugInfo->emitVariableDeclaration(Builder, Storage, DbgTy, - i->getDebugScope(), Decl, *VarInfo, - IndirectValue); + IGM.DebugInfo->emitVariableDeclaration( + Builder, Storage, DbgTy, i->getDebugScope(), i->getLoc(), *VarInfo, + InCoroContext(*CurSILFn, *i) ? CoroIndirectValue : IndirectValue); } void IRGenSILFunction::visitProjectBoxInst(swift::ProjectBoxInst *i) { diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 18adfc70fa4f2..67f79aee220ee 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -50,6 +50,12 @@ const IRLinkage IRLinkage::Internal = { llvm::GlobalValue::DefaultStorageClass, }; +const IRLinkage IRLinkage::ExternalCommon = { + llvm::GlobalValue::CommonLinkage, + llvm::GlobalValue::DefaultVisibility, + llvm::GlobalValue::DLLExportStorageClass, +}; + const IRLinkage IRLinkage::ExternalImport = { llvm::GlobalValue::ExternalLinkage, llvm::GlobalValue::DefaultVisibility, @@ -462,9 +468,10 @@ std::string LinkEntity::mangleAsString() const { case Kind::AsyncFunctionPointerAST: { std::string Result; - Result = - SILDeclRef(const_cast(getDecl()), SILDeclRef::Kind::Func) - .mangle(); + Result = SILDeclRef(const_cast(getDecl()), + static_cast( + reinterpret_cast(SecondaryPointer))) + .mangle(); Result.append("Tu"); return Result; } @@ -752,7 +759,7 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { case Kind::DispatchThunkAllocatorAsyncFunctionPointer: case Kind::PartialApplyForwarderAsyncFunctionPointer: return getUnderlyingEntityForAsyncFunctionPointer() - .getLinkage(ForDefinition); + .getLinkage(forDefinition); case Kind::KnownAsyncFunctionPointer: return SILLinkage::PublicExternal; case Kind::PartialApplyForwarder: diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 6b33836b2ba5f..010dfd4cda81b 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -38,8 +38,7 @@ using namespace swift; using namespace swift::irgen; static GenericEnvironment *getSubstGenericEnvironment(CanSILFunctionType fnTy) { - auto sig = fnTy->getSubstGenericSignature(); - return sig ? sig->getGenericEnvironment() : nullptr; + return fnTy->getSubstGenericSignature().getGenericEnvironment(); } static GenericEnvironment *getSubstGenericEnvironment(SILFunction *F) { @@ -1079,9 +1078,9 @@ static AllocStackInst *allocate(StructLoweringState &pass, SILType type) { // Insert an alloc_stack at the beginning of the function. SILBuilderWithScope allocBuilder(&*pass.F->begin()); // Don't put any variable debug info into the alloc_stack, there will be a - // debug_value_addr insterted later. TODO: It may be more elegant to insert + // debug_value insterted later. TODO: It may be more elegant to insert // the variable info into the alloc_stack instead of additionally generating a - // debug_value_addr. + // debug_value. AllocStackInst *alloc = allocBuilder.createAllocStack( RegularLocation::getAutoGeneratedLocation(), type); @@ -2069,6 +2068,11 @@ static void rewriteFunction(StructLoweringState &pass, pass.applies.append(currentModApplies.begin(), currentModApplies.end()); } while (repeat); + while (!pass.modYieldInsts.empty()) { + YieldInst *inst = pass.modYieldInsts.pop_back_val(); + allocateAndSetAll(pass, allocator, inst, inst->getAllOperands()); + } + for (SILInstruction *instr : pass.instsToMod) { for (Operand &operand : instr->getAllOperands()) { auto currOperand = operand.get(); @@ -2324,11 +2328,6 @@ static void rewriteFunction(StructLoweringState &pass, retBuilder.createReturn(newRetTuple->getLoc(), newRetTuple); instr->eraseFromParent(); } - - while (!pass.modYieldInsts.empty()) { - YieldInst *inst = pass.modYieldInsts.pop_back_val(); - allocateAndSetAll(pass, allocator, inst, inst->getAllOperands()); - } } // Rewrite function return argument if it is a "function pointer" @@ -2729,9 +2728,7 @@ bool LoadableByAddress::recreateDifferentiabilityWitnessFunction( auto *currIRMod = getIRGenModule()->IRGen.getGenModule(instr->getFunction()); auto resultFnTy = instr->getType().castTo(); auto genSig = resultFnTy->getSubstGenericSignature(); - GenericEnvironment *genEnv = nullptr; - if (genSig) - genEnv = genSig->getGenericEnvironment(); + auto *genEnv = genSig.getGenericEnvironment(); auto newResultFnTy = MapperCache.getNewSILFunctionType(genEnv, resultFnTy, *currIRMod); if (resultFnTy == newResultFnTy) @@ -2799,9 +2796,9 @@ bool LoadableByAddress::recreateConvInstr(SILInstruction &I, if (convInstr->getKind() == SILInstructionKind::DifferentiableFunctionInst || convInstr->getKind() == SILInstructionKind::DifferentiableFunctionExtractInst || convInstr->getKind() == SILInstructionKind::LinearFunctionInst || - convInstr->getKind() == SILInstructionKind::LinearFunctionExtractInst) - if (auto genSig = currSILFunctionType->getSubstGenericSignature()) - genEnv = genSig->getGenericEnvironment(); + convInstr->getKind() == SILInstructionKind::LinearFunctionExtractInst) { + genEnv = currSILFunctionType->getSubstGenericSignature().getGenericEnvironment(); + } CanSILFunctionType newFnType = MapperCache.getNewSILFunctionType( genEnv, currSILFunctionType, *currIRMod); SILType newType = SILType::getPrimitiveObjectType(newFnType); @@ -2972,10 +2969,8 @@ void LoadableByAddress::run() { builtinInstrs.insert(instr); break; } - case SILInstructionKind::DebugValueAddrInst: - case SILInstructionKind::DebugValueInst: { + case SILInstructionKind::DebugValueInst: break; - } default: llvm_unreachable("Unhandled use of FunctionRefInst"); } diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 63c74bfc83ee3..c9fcad58fe488 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1383,7 +1383,8 @@ namespace { .withValueOwnership(flags.getValueOwnership()) .withVariadic(flags.isVariadic()) .withAutoClosure(flags.isAutoClosure()) - .withNoDerivative(flags.isNoDerivative()); + .withNoDerivative(flags.isNoDerivative()) + .withIsolated(flags.isIsolated()); }; bool hasParameterFlags = false; @@ -1445,15 +1446,20 @@ namespace { .withThrows(type->isThrowing()) .withParameterFlags(hasParameterFlags) .withEscaping(isEscaping) - .withDifferentiable(type->isDifferentiable()); + .withDifferentiable(type->isDifferentiable()) + .withGlobalActor(!type->getGlobalActor().isNull()); auto flagsVal = llvm::ConstantInt::get(IGF.IGM.SizeTy, flags.getIntValue()); llvm::Value *diffKindVal = nullptr; if (type->isDifferentiable()) { assert(metadataDifferentiabilityKind.isDifferentiable()); - diffKindVal = llvm::ConstantInt::get(IGF.IGM.SizeTy, - flags.getIntValue()); + diffKindVal = llvm::ConstantInt::get( + IGF.IGM.SizeTy, metadataDifferentiabilityKind.getIntValue()); + } else if (type->getGlobalActor()) { + diffKindVal = llvm::ConstantInt::get( + IGF.IGM.SizeTy, + FunctionMetadataDifferentiabilityKind::NonDifferentiable); } auto collectParameters = @@ -1507,7 +1513,8 @@ namespace { case 1: case 2: case 3: { - if (!hasParameterFlags && !type->isDifferentiable()) { + if (!hasParameterFlags && !type->isDifferentiable() && + !type->getGlobalActor()) { llvm::SmallVector arguments; auto *metadataFn = constructSimpleCall(arguments); auto *call = IGF.Builder.CreateCall(metadataFn, arguments); @@ -1515,48 +1522,56 @@ namespace { return setLocal(CanType(type), MetadataResponse::forComplete(call)); } - // If function type has parameter flags or is differentiable, let's emit - // the most general function to retrieve them. + // If function type has parameter flags or is differentiable or has a + // global actor, emit the most general function to retrieve them. LLVM_FALLTHROUGH; } default: - assert(!params.empty() || type->isDifferentiable() && + assert((!params.empty() || type->isDifferentiable() || + type->getGlobalActor()) && "0 parameter case should be specialized unless it is a " - "differentiable function"); + "differentiable function or has a global actor"); auto *const Int32Ptr = IGF.IGM.Int32Ty->getPointerTo(); llvm::SmallVector arguments; arguments.push_back(flagsVal); - if (type->isDifferentiable()) { - assert(diffKindVal); + if (diffKindVal) { arguments.push_back(diffKindVal); } ConstantInitBuilder paramFlags(IGF.IGM); auto flagsArr = paramFlags.beginArray(); - auto arrayTy = - llvm::ArrayType::get(IGF.IGM.TypeMetadataPtrTy, numParams); - Address parameters = IGF.createAlloca( - arrayTy, IGF.IGM.getTypeMetadataAlignment(), "function-parameters"); - - IGF.Builder.CreateLifetimeStart(parameters, - IGF.IGM.getPointerSize() * numParams); - - collectParameters([&](unsigned i, llvm::Value *typeRef, - ParameterFlags flags) { - auto argPtr = IGF.Builder.CreateStructGEP(parameters, i, - IGF.IGM.getPointerSize()); - IGF.Builder.CreateStore(typeRef, argPtr); - if (i == 0) - arguments.push_back(argPtr.getAddress()); - - if (hasParameterFlags) - flagsArr.addInt32(flags.getIntValue()); - }); + Address parameters; + if (!params.empty()) { + auto arrayTy = + llvm::ArrayType::get(IGF.IGM.TypeMetadataPtrTy, numParams); + parameters = IGF.createAlloca( + arrayTy, IGF.IGM.getTypeMetadataAlignment(), "function-parameters"); + + IGF.Builder.CreateLifetimeStart(parameters, + IGF.IGM.getPointerSize() * numParams); + + collectParameters([&](unsigned i, llvm::Value *typeRef, + ParameterFlags flags) { + auto argPtr = IGF.Builder.CreateStructGEP(parameters, i, + IGF.IGM.getPointerSize()); + IGF.Builder.CreateStore(typeRef, argPtr); + if (i == 0) + arguments.push_back(argPtr.getAddress()); + + if (hasParameterFlags) + flagsArr.addInt32(flags.getIntValue()); + }); + } else { + auto parametersPtr = + llvm::ConstantPointerNull::get( + IGF.IGM.TypeMetadataPtrTy->getPointerTo()); + arguments.push_back(parametersPtr); + } if (hasParameterFlags) { auto *flagsVar = flagsArr.finishAndCreateGlobal( @@ -1570,9 +1585,16 @@ namespace { arguments.push_back(result); - auto *getMetadataFn = type->isDifferentiable() - ? IGF.IGM.getGetFunctionMetadataDifferentiableFn() - : IGF.IGM.getGetFunctionMetadataFn(); + if (Type globalActor = type->getGlobalActor()) { + arguments.push_back( + IGF.emitAbstractTypeMetadataRef(globalActor->getCanonicalType())); + } + + auto *getMetadataFn = type->getGlobalActor() + ? IGF.IGM.getGetFunctionMetadataGlobalActorFn() + : type->isDifferentiable() + ? IGF.IGM.getGetFunctionMetadataDifferentiableFn() + : IGF.IGM.getGetFunctionMetadataFn(); auto call = IGF.Builder.CreateCall(getMetadataFn, arguments); call->setDoesNotThrow(); @@ -2465,18 +2487,10 @@ static bool shouldAccessByMangledName(IRGenModule &IGM, CanType type) { } // The Swift 5.1 runtime fails to demangle associated types of opaque types. - if (!IGM.getAvailabilityContext().isContainedIn(IGM.Context.getSwift52Availability())) { - auto hasNestedOpaqueArchetype = type.findIf([](CanType sub) -> bool { - if (auto archetype = dyn_cast(sub)) { - if (isa(archetype->getRoot())) { - return true; - } - } - return false; - }); - - if (hasNestedOpaqueArchetype) - return false; + if (auto minimumSwiftVersion = + getRuntimeVersionThatSupportsDemanglingType(type)) { + return IGM.getAvailabilityContext().isContainedIn( + IGM.Context.getSwift5PlusAvailability(*minimumSwiftVersion)); } return true; @@ -3389,7 +3403,7 @@ llvm::Value *irgen::emitClassHeapMetadataRef(IRGenFunction &IGF, CanType type, bool allowUninitialized) { assert(request.canResponseStatusBeIgnored() && "emitClassHeapMetadataRef only supports satisfied requests"); - assert(type->mayHaveSuperclass()); + assert(type->mayHaveSuperclass() || type->isTypeErasedGenericClassType()); // Archetypes may or may not be ObjC classes and need unwrapping to get at // the class object. @@ -3404,7 +3418,7 @@ llvm::Value *irgen::emitClassHeapMetadataRef(IRGenFunction &IGF, CanType type, return classPtr; } - if (ClassDecl *theClass = type->getClassOrBoundGenericClass()) { + if (ClassDecl *theClass = dyn_cast_or_null(type->getAnyNominal())) { if (!hasKnownSwiftMetadata(IGF.IGM, theClass)) { llvm::Value *result = emitObjCHeapMetadataRef(IGF, theClass, allowUninitialized); diff --git a/lib/IRGen/NecessaryBindings.h b/lib/IRGen/NecessaryBindings.h index 765866bf2ce6c..89bdb26993f83 100644 --- a/lib/IRGen/NecessaryBindings.h +++ b/lib/IRGen/NecessaryBindings.h @@ -94,6 +94,7 @@ class NecessaryBindings { case Kind::AsyncFunction: return RequirementsVector[i]; } + llvm_unreachable("covered switch"); } ProtocolConformanceRef diff --git a/lib/IRGen/Signature.h b/lib/IRGen/Signature.h index cbefcf4c8c7ca..f99ed1764bf64 100644 --- a/lib/IRGen/Signature.h +++ b/lib/IRGen/Signature.h @@ -124,9 +124,8 @@ class Signature { /// This is a private detail of the implementation of /// IRGenModule::getSignature(CanSILFunctionType), which is what /// clients should generally be using. - static Signature getUncached(IRGenModule &IGM, - CanSILFunctionType formalType, - bool suppressGenerics); + static Signature getUncached(IRGenModule &IGM, CanSILFunctionType formalType, + bool useSpecialConvention); /// Compute the signature of a coroutine's continuation function. static Signature forCoroutineContinuation(IRGenModule &IGM, @@ -134,10 +133,10 @@ class Signature { static Signature forAsyncReturn(IRGenModule &IGM, CanSILFunctionType asyncType); - static Signature forAsyncAwait(IRGenModule &IGM, - CanSILFunctionType asyncType); - static Signature forAsyncEntry(IRGenModule &IGM, - CanSILFunctionType asyncType); + static Signature forAsyncAwait(IRGenModule &IGM, CanSILFunctionType asyncType, + bool useSpecialConvention); + static Signature forAsyncEntry(IRGenModule &IGM, CanSILFunctionType asyncType, + bool useSpecialConvention); llvm::FunctionType *getType() const { assert(isValid()); diff --git a/lib/Immediate/Immediate.cpp b/lib/Immediate/Immediate.cpp index 93720e4df4139..1e49aca2e46ba 100644 --- a/lib/Immediate/Immediate.cpp +++ b/lib/Immediate/Immediate.cpp @@ -237,26 +237,42 @@ int swift::RunImmediately(CompilerInstance &CI, // // This must be done here, before any library loading has been done, to avoid // racing with the static initializers in user code. + // Setup interpreted process arguments. + using ArgOverride = void (*)(const char **, int); +#if defined(_WIN32) auto stdlib = loadSwiftRuntime(Context.SearchPathOpts.RuntimeLibraryPaths); if (!stdlib) { CI.getDiags().diagnose(SourceLoc(), diag::error_immediate_mode_missing_stdlib); return -1; } - - // Setup interpreted process arguments. - using ArgOverride = void (*)(const char **, int); -#if defined(_WIN32) auto module = static_cast(stdlib); auto emplaceProcessArgs = reinterpret_cast( GetProcAddress(module, "_swift_stdlib_overrideUnsafeArgvArgc")); if (emplaceProcessArgs == nullptr) return -1; #else + // In case the compiler is built with libswift, it already has the stdlib + // linked to. First try to lookup the symbol with the standard library + // resolving. auto emplaceProcessArgs - = (ArgOverride)dlsym(stdlib, "_swift_stdlib_overrideUnsafeArgvArgc"); - if (dlerror()) - return -1; + = (ArgOverride)dlsym(RTLD_DEFAULT, "_swift_stdlib_overrideUnsafeArgvArgc"); + + if (dlerror()) { + // If this does not work (= not build with libswift), we have to explicitly + // load the stdlib. + auto stdlib = loadSwiftRuntime(Context.SearchPathOpts.RuntimeLibraryPaths); + if (!stdlib) { + CI.getDiags().diagnose(SourceLoc(), + diag::error_immediate_mode_missing_stdlib); + return -1; + } + dlerror(); + emplaceProcessArgs + = (ArgOverride)dlsym(stdlib, "_swift_stdlib_overrideUnsafeArgvArgc"); + if (dlerror()) + return -1; + } #endif SmallVector argBuf; diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index f8cbd9cb94d56..5ddb875ef3bb5 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -79,7 +79,7 @@ static bool printDisplayName(const swift::ValueDecl *D, llvm::raw_ostream &OS) { static bool isMemberwiseInit(swift::ValueDecl *D) { if (auto AFD = dyn_cast(D)) - return AFD->getBodyKind() == AbstractFunctionDecl::BodyKind::MemberwiseInitializer; + return AFD->isMemberwiseInitializer(); return false; } @@ -479,7 +479,7 @@ class IndexSwiftASTWalker : public SourceEntityWalker { // The generic parameter in the extension has the same depths and index // as the one in the extended type. - for (auto ExtendedTypeGenParam : ExtendedTypeGenSig->getGenericParams()) { + for (auto ExtendedTypeGenParam : ExtendedTypeGenSig.getGenericParams()) { if (ExtendedTypeGenParam->getIndex() == GenParam->getIndex() && ExtendedTypeGenParam->getDepth() == GenParam->getDepth()) { assert(ExtendedTypeGenParam->getDecl() && @@ -617,7 +617,8 @@ class IndexSwiftASTWalker : public SourceEntityWalker { bool reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit, SymbolRoleSet Relations, Decl *Related); bool reportRelatedTypeRef(const TypeLoc &Ty, SymbolRoleSet Relations, Decl *Related); - bool reportInheritedTypeRefs(ArrayRef Inherited, Decl *Inheritee); + bool reportInheritedTypeRefs( + ArrayRef Inherited, Decl *Inheritee); NominalTypeDecl *getTypeLocAsNominalTypeDecl(const TypeLoc &Ty); bool reportPseudoGetterDecl(VarDecl *D) { @@ -998,7 +999,7 @@ bool IndexSwiftASTWalker::reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isI return !Cancelled; } -bool IndexSwiftASTWalker::reportInheritedTypeRefs(ArrayRef Inherited, Decl *Inheritee) { +bool IndexSwiftASTWalker::reportInheritedTypeRefs(ArrayRef Inherited, Decl *Inheritee) { for (auto Base : Inherited) { if (!reportRelatedTypeRef(Base, (SymbolRoleSet) SymbolRole::RelationBaseOf, Inheritee)) return false; diff --git a/lib/LLVMPasses/CMakeLists.txt b/lib/LLVMPasses/CMakeLists.txt index f5a3ee28b7562..085e95427c818 100644 --- a/lib/LLVMPasses/CMakeLists.txt +++ b/lib/LLVMPasses/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftLLVMPasses STATIC LLVMSwiftAA.cpp LLVMSwiftRCIdentity.cpp @@ -12,3 +12,5 @@ add_swift_host_library(swiftLLVMPasses STATIC ) target_link_libraries(swiftLLVMPasses PRIVATE swiftDemangling) + +set_swift_llvm_is_available(swiftLLVMPasses) diff --git a/lib/LLVMPasses/LLVMARCOpts.cpp b/lib/LLVMPasses/LLVMARCOpts.cpp index f279dd2dbf3d5..25a3d1aa35f92 100644 --- a/lib/LLVMPasses/LLVMARCOpts.cpp +++ b/lib/LLVMPasses/LLVMARCOpts.cpp @@ -116,6 +116,12 @@ static bool canonicalizeInputFunction(Function &F, ARCEntryPointBuilder &B, ++NumNoopDeleted; continue; } + if (!CI.use_empty()) { + // Do not get RC identical value here, could end up with a + // crash in replaceAllUsesWith as the type maybe different. + CI.replaceAllUsesWith(CI.getArgOperand(0)); + Changed = true; + } // Rewrite unknown retains into swift_retains. NativeRefs.insert(ArgVal); for (auto &X : UnknownObjectRetains[ArgVal]) { @@ -138,7 +144,12 @@ static bool canonicalizeInputFunction(Function &F, ARCEntryPointBuilder &B, ++NumNoopDeleted; continue; } - + if (!CI.use_empty()) { + // Do not get RC identical value here, could end up with a + // crash in replaceAllUsesWith as the type maybe different. + CI.replaceAllUsesWith(CI.getArgOperand(0)); + Changed = true; + } // Have not encountered a strong retain/release. keep it in the // unknown retain/release list for now. It might get replaced // later. diff --git a/lib/Localization/LocalizationFormat.cpp b/lib/Localization/LocalizationFormat.cpp index fd788712771f8..ee830337a35e1 100644 --- a/lib/Localization/LocalizationFormat.cpp +++ b/lib/Localization/LocalizationFormat.cpp @@ -93,9 +93,24 @@ bool SerializedLocalizationWriter::emit(llvm::StringRef filePath) { return OS.has_error(); } +void LocalizationProducer::initializeIfNeeded() { + if (state != NotInitialized) + return; + + if (initializeImpl()) + state = Initialized; + else + state = FailedInitialization; +} + llvm::StringRef LocalizationProducer::getMessageOr(swift::DiagID id, llvm::StringRef defaultMessage) { + initializeIfNeeded(); + if (getState() == FailedInitialization) { + return defaultMessage; + } + auto localizedMessage = getMessage(id); if (localizedMessage.empty()) return defaultMessage; @@ -108,14 +123,22 @@ LocalizationProducer::getMessageOr(swift::DiagID id, return localizedMessage; } +LocalizationProducerState LocalizationProducer::getState() const { + return state; +} + SerializedLocalizationProducer::SerializedLocalizationProducer( std::unique_ptr buffer, bool printDiagnosticNames) : LocalizationProducer(printDiagnosticNames), Buffer(std::move(buffer)) { +} + +bool SerializedLocalizationProducer::initializeImpl() { auto base = reinterpret_cast(Buffer.get()->getBufferStart()); auto tableOffset = endian::read(base, little); SerializedTable.reset(SerializedLocalizationTable::Create( base + tableOffset, base + sizeof(offset_type), base)); + return true; } llvm::StringRef @@ -128,12 +151,16 @@ SerializedLocalizationProducer::getMessage(swift::DiagID id) const { YAMLLocalizationProducer::YAMLLocalizationProducer(llvm::StringRef filePath, bool printDiagnosticNames) - : LocalizationProducer(printDiagnosticNames) { + : LocalizationProducer(printDiagnosticNames), filePath(filePath) { +} + +bool YAMLLocalizationProducer::initializeImpl() { auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(filePath); llvm::MemoryBuffer *document = FileBufOrErr->get(); diag::LocalizationInput yin(document->getBuffer()); yin >> diagnostics; unknownIDs = std::move(yin.unknownIDs); + return true; } llvm::StringRef YAMLLocalizationProducer::getMessage(swift::DiagID id) const { @@ -141,7 +168,12 @@ llvm::StringRef YAMLLocalizationProducer::getMessage(swift::DiagID id) const { } void YAMLLocalizationProducer::forEachAvailable( - llvm::function_ref callback) const { + llvm::function_ref callback) { + initializeIfNeeded(); + if (getState() == FailedInitialization) { + return; + } + for (uint32_t i = 0, n = diagnostics.size(); i != n; ++i) { auto translation = diagnostics[i]; if (!translation.empty()) @@ -149,6 +181,34 @@ void YAMLLocalizationProducer::forEachAvailable( } } +std::unique_ptr +LocalizationProducer::producerFor(llvm::StringRef locale, llvm::StringRef path, + bool printDiagnosticNames) { + std::unique_ptr producer; + llvm::SmallString<128> filePath(path); + llvm::sys::path::append(filePath, locale); + llvm::sys::path::replace_extension(filePath, ".db"); + + // If the serialized diagnostics file not available, + // fallback to the `YAML` file. + if (llvm::sys::fs::exists(filePath)) { + if (auto file = llvm::MemoryBuffer::getFile(filePath)) { + producer = std::make_unique( + std::move(file.get()), printDiagnosticNames); + } + } else { + llvm::sys::path::replace_extension(filePath, ".yaml"); + // In case of missing localization files, we should fallback to messages + // from `.def` files. + if (llvm::sys::fs::exists(filePath)) { + producer = std::make_unique( + filePath.str(), printDiagnosticNames); + } + } + + return producer; +} + llvm::Optional LocalizationInput::readID(llvm::yaml::IO &io) { LocalDiagID diagID; io.mapRequired("id", diagID); diff --git a/lib/Migrator/APIDiffMigratorPass.cpp b/lib/Migrator/APIDiffMigratorPass.cpp index 08057358d05ed..62a29ce4ca943 100644 --- a/lib/Migrator/APIDiffMigratorPass.cpp +++ b/lib/Migrator/APIDiffMigratorPass.cpp @@ -161,6 +161,10 @@ class ChildIndexFinder : public TypeReprVisitor { return visit(T->getBase()); } + FoundResult visitIsolatedTypeRepr(IsolatedTypeRepr *T) { + return visit(T->getBase()); + } + FoundResult visitArrayTypeRepr(ArrayTypeRepr *T) { return handleParent(T, T->getBase()); } @@ -568,7 +572,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } return false; }; - if (auto *VD = getReferencedDecl(Call).second.getDecl()) + if (auto *VD = getReferencedDecl(Call, /*semantic=*/false).second.getDecl()) if (handleDecl(VD, Call->getSourceRange())) return true; diff --git a/lib/Migrator/CMakeLists.txt b/lib/Migrator/CMakeLists.txt index 473ad151f0640..993933bf090af 100644 --- a/lib/Migrator/CMakeLists.txt +++ b/lib/Migrator/CMakeLists.txt @@ -47,8 +47,6 @@ swift_install_in_component(FILES ${datafiles} DESTINATION "lib/swift/migrator" COMPONENT compiler) -set_swift_llvm_is_available() - add_swift_host_library(swiftMigrator STATIC APIDiffMigratorPass.cpp EditorAdapter.cpp @@ -63,3 +61,5 @@ target_link_libraries(swiftMigrator PRIVATE add_dependencies(swiftMigrator "symlink_migrator_data") + +set_swift_llvm_is_available(swiftMigrator) diff --git a/lib/Migrator/Migrator.cpp b/lib/Migrator/Migrator.cpp index 08dbb26c31c3f..baf7ae64c2870 100644 --- a/lib/Migrator/Migrator.cpp +++ b/lib/Migrator/Migrator.cpp @@ -21,6 +21,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Edit/EditedSource.h" #include "clang/Rewrite/Core/RewriteBuffer.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" using namespace swift; @@ -148,6 +149,9 @@ Migrator::performAFixItMigration(version::Version SwiftLanguageVersion) { } auto Instance = std::make_unique(); + // rdar://78576743 - Reset LLVM global state for command-line arguments set + // by prior calls to setup. + llvm::cl::ResetAllOptionOccurrences(); if (Instance->setup(Invocation)) { return nullptr; } diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index c9759508cabc0..de414004993a4 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) set(SWIFT_GYB_FLAGS --line-directive "^\"#line %(line)d \\\"%(file)s\\\"^\"") @@ -34,3 +34,5 @@ target_link_libraries(swiftParse PRIVATE swiftSyntaxParse) add_dependencies(swiftParse swift-parse-syntax-generated-headers) + +set_swift_llvm_is_available(swiftParse) diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index fc7da734576f5..0a346eabd8170 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -2145,9 +2145,10 @@ void Lexer::tryLexEditorPlaceholder() { if (Ptr[0] == '<' && Ptr[1] == '#') break; if (Ptr[0] == '#' && Ptr[1] == '>') { - // Found it. Flag it as error (or warning, if in playground mode) for the - // rest of the compiler pipeline and lex it as an identifier. - if (LangOpts.Playground) { + // Found it. Flag it as error (or warning, if in playground mode or we've + // been asked to warn) for the rest of the compiler pipeline and lex it + // as an identifier. + if (LangOpts.Playground || LangOpts.WarnOnEditorPlaceholder) { diagnose(TokStart, diag::lex_editor_placeholder_in_playground); } else { diagnose(TokStart, diag::lex_editor_placeholder); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 3021970961862..a552cef9991c8 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -568,7 +568,7 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( auto Attr = new (Context) AvailableAttr(AtLoc, SourceRange(AttrLoc, Tok.getLoc()), PlatformKind.getValue(), - Message, Renamed, + Message, Renamed, /*RenameDecl=*/nullptr, Introduced.Version, Introduced.Range, Deprecated.Version, Deprecated.Range, Obsoleted.Version, Obsoleted.Range, @@ -1575,70 +1575,6 @@ void Parser::parseAllAvailabilityMacroArguments() { AvailabilityMacrosComputed = true; } -static CompletionHandlerAsyncAttr * -parseCompletionHandlerAsyncAttribute(Parser &P, StringRef AttrName, - SourceLoc AtLoc, DeclAttrKind DK) { - SourceLoc Loc = P.PreviousLoc; - - if (!P.consumeIf(tok::l_paren)) { - P.diagnose(P.getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName, - DeclAttribute::isDeclModifier(DK)); - return nullptr; - } - - if (!P.Tok.is(tok::string_literal)) { - P.diagnose(Loc, diag::attr_expected_string_literal, AttrName); - return nullptr; - } - - SourceLoc nameLoc = P.Tok.getLoc(); - Optional asyncFunctionName = P.getStringLiteralIfNotInterpolated( - nameLoc, ("argument of '" + AttrName + "'").str()); - P.consumeToken(tok::string_literal); - - if (!asyncFunctionName) - return nullptr; - - ParsedDeclName parsedAsyncName = parseDeclName(*asyncFunctionName); - if (!parsedAsyncName || !parsedAsyncName.ContextName.empty()) { - P.diagnose(nameLoc, diag::attr_completion_handler_async_invalid_name, - AttrName); - return nullptr; - } - - size_t handlerIndex = 0; - SourceLoc handlerIndexLoc = SourceLoc(); - if (P.consumeIf(tok::comma)) { - // The completion handler is explicitly specified, parse it - if (P.parseSpecificIdentifier("completionHandlerIndex", - diag::attr_missing_label, - "completionHandlerIndex", AttrName) || - P.parseToken(tok::colon, diag::expected_colon_after_label, - "completionHandlerIndex")) { - return nullptr; - } - - if (P.Tok.getText().getAsInteger(0, handlerIndex)) { - P.diagnose(P.Tok.getLoc(), diag::attr_expected_integer_literal, AttrName); - return nullptr; - } - - handlerIndexLoc = P.consumeToken(tok::integer_literal); - } - - SourceRange AttrRange = SourceRange(Loc, P.Tok.getRange().getStart()); - if (!P.consumeIf(tok::r_paren)) { - P.diagnose(P.getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, - DeclAttribute::isDeclModifier(DK)); - return nullptr; - } - - return new (P.Context) CompletionHandlerAsyncAttr( - parsedAsyncName.formDeclNameRef(P.Context), nameLoc, - handlerIndex, handlerIndexLoc, AtLoc, - AttrRange); -} - bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, DeclAttrKind DK, bool isFromClangAttribute) { // Ok, it is a valid attribute, eat it, and then process it. @@ -1675,6 +1611,14 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, DiscardAttribute = true; } + // If this attribute is only permitted when distributed is enabled, reject it. + if (DeclAttribute::isDistributedOnly(DK) && + !shouldParseExperimentalDistributed()) { + diagnose(Loc, diag::attr_requires_distributed, AttrName, + DeclAttribute::isDeclModifier(DK)); + DiscardAttribute = true; + } + if (Context.LangOpts.Target.isOSBinFormatCOFF()) { if (DK == DAK_WeakLinked) { diagnose(Loc, diag::attr_unsupported_on_target, AttrName, @@ -1784,45 +1728,6 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } - case DAK_ActorIndependent: { - // if no option is provided, then it's the 'safe' version. - if (!consumeIf(tok::l_paren)) { - if (!DiscardAttribute) { - AttrRange = SourceRange(Loc, Tok.getRange().getStart()); - Attributes.add(new (Context) ActorIndependentAttr(AtLoc, AttrRange, - ActorIndependentKind::Safe)); - } - break; - } - - // otherwise, make sure it looks like an identifier. - if (Tok.isNot(tok::identifier)) { - diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "unsafe"); - return false; - } - - // make sure the identifier is 'unsafe' - if (Tok.getText() != "unsafe") { - diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName); - return false; - } - - consumeToken(tok::identifier); - AttrRange = SourceRange(Loc, Tok.getRange().getStart()); - - if (!consumeIf(tok::r_paren)) { - diagnose(Loc, diag::attr_expected_rparen, AttrName, - DeclAttribute::isDeclModifier(DK)); - return false; - } - - if (!DiscardAttribute) - Attributes.add(new (Context) ActorIndependentAttr(AtLoc, AttrRange, - ActorIndependentKind::Unsafe)); - - break; - } - case DAK_Optimize: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -2333,7 +2238,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, peekAvailabilityMacroName())) { // We have the short form of available: @available(iOS 8.0.1, *) SmallVector Specs; - ParserStatus Status = parseAvailabilitySpecList(Specs); + ParserStatus Status = parseAvailabilitySpecList(Specs, + AvailabilitySpecSource::Available); if (Status.isErrorOrHasCompletion()) return false; @@ -2398,6 +2304,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, Platform, /*Message=*/StringRef(), /*Rename=*/StringRef(), + /*RenameDecl=*/nullptr, /*Introduced=*/Version, /*IntroducedRange=*/VersionRange, /*Deprecated=*/llvm::VersionTuple(), @@ -2721,19 +2628,6 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, name, AtLoc, range, /*implicit*/ false)); break; } - case DAK_CompletionHandlerAsync: { - auto *attr = - parseCompletionHandlerAsyncAttribute(*this, AttrName, AtLoc, DK); - if (!attr) { - skipUntilDeclStmtRBrace(tok::r_paren); - consumeIf(tok::r_paren); - return false; - } - - if (!DiscardAttribute) - Attributes.add(attr); - break; - } } if (DuplicateAttribute) { @@ -2745,7 +2639,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, .highlight(DuplicateAttribute->getRange()); } - // If this is a decl modifier spelled with an @, emit an error and remove it + // If this is a decl modifier spelled with an @, emit an error and remove it // with a fixit. if (AtLoc.isValid() && DeclAttribute::isDeclModifier(DK)) diagnose(AtLoc, diag::cskeyword_not_attribute, AttrName).fixItRemove(AtLoc); @@ -2856,10 +2750,11 @@ bool Parser::canParseCustomAttribute() { ParserResult Parser::parseCustomAttribute( SourceLoc atLoc, PatternBindingInitializer *&initContext) { + assert(Tok.is(tok::identifier)); SyntaxContext->setCreateSyntax(SyntaxKind::CustomAttribute); // Parse a custom attribute. - auto type = parseType(diag::expected_type); + auto type = parseType(diag::expected_type, ParseTypeReason::CustomAttribute); if (type.hasCodeCompletion() || type.isNull()) { if (Tok.is(tok::l_paren) && isCustomAttributeArgument()) skipSingle(); @@ -3027,6 +2922,16 @@ ParserStatus Parser::parseDeclAttribute( checkInvalidAttrName( "concurrent", "Sendable", DAK_Sendable, diag::attr_renamed_warning); + // Historical name for 'nonisolated'. + if (DK == DAK_Count && Tok.getText() == "actorIndependent") { + diagnose( + Tok, diag::attr_renamed_to_modifier_warning, "actorIndependent", + "nonisolated") + .fixItReplace(SourceRange(AtLoc, Tok.getLoc()), "nonisolated"); + DK = DAK_Nonisolated; + AtLoc = SourceLoc(); + } + if (DK == DAK_Count && Tok.getText() == "warn_unused_result") { // The behavior created by @warn_unused_result is now the default. Emit a // Fix-It to remove. @@ -3093,7 +2998,7 @@ bool Parser::canParseTypeAttribute() { TypeAttributes attrs; // ignored PatternBindingInitializer *initContext = nullptr; return !parseTypeAttribute(attrs, /*atLoc=*/SourceLoc(), initContext, - /*justChecking*/ true); + /*justChecking*/ true).isError(); } /// Parses the '@differentiable' type attribute argument (no argument list, @@ -3252,16 +3157,28 @@ bool Parser::parseConventionAttributeInternal( /// \param justChecking - if true, we're just checking whether we /// canParseTypeAttribute; don't emit any diagnostics, and there's /// no need to actually record the attribute -bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, - PatternBindingInitializer *&initContext, - bool justChecking) { +ParserStatus Parser::parseTypeAttribute(TypeAttributes &Attributes, + SourceLoc AtLoc, + PatternBindingInitializer *&initContext, + bool justChecking) { // If this not an identifier, the attribute is malformed. if (Tok.isNot(tok::identifier) && // These are keywords that we accept as attribute names. Tok.isNot(tok::kw_in) && Tok.isNot(tok::kw_inout)) { + + if (Tok.is(tok::code_complete)) { + if (!justChecking) { + if (CodeCompletion) { + CodeCompletion->completeTypeAttrBeginning(); + } + } + consumeToken(tok::code_complete); + return makeParserCodeCompletionStatus(); + } + if (!justChecking) diagnose(Tok, diag::expected_attribute_name); - return true; + return makeParserError(); } // Determine which attribute it is, and diagnose it if unknown. @@ -3289,7 +3206,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, if (declAttrID != DAK_Count) { // This is a valid decl attribute so they should have put it on the decl // instead of the type. - if (justChecking) return true; + if (justChecking) return makeParserError(); // If this is the first attribute, and if we are on a simple decl, emit a // fixit to move the attribute. Otherwise, we don't have the location of @@ -3324,21 +3241,22 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, backtrack.cancelBacktrack(); } - return true; + return makeParserError(); } // If we're just checking, try to parse now. if (justChecking) - return !canParseCustomAttribute(); + return canParseCustomAttribute() ? makeParserSuccess() + : makeParserError(); // Parse as a custom attribute. auto customAttrResult = parseCustomAttribute(AtLoc, initContext); if (customAttrResult.isParseErrorOrHasCompletion()) - return true; + return customAttrResult; if (auto attr = customAttrResult.get()) Attributes.addCustomAttr(attr); - return false; + return makeParserSuccess(); } // Ok, it is a valid attribute, eat it, and then process it. @@ -3352,19 +3270,19 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, if (failedToParse) { if (Tok.is(tok::r_paren)) consumeToken(); - return true; + return makeParserError(); } } // In just-checking mode, we only need to consume the tokens, and we don't // want to do any other analysis. if (justChecking) - return false; + return makeParserSuccess(); // Diagnose duplicated attributes. if (Attributes.has(attr)) { diagnose(AtLoc, diag::duplicate_attribute, /*isModifier=*/false); - return false; + return makeParserSuccess(); } // Handle any attribute-specific processing logic. @@ -3386,7 +3304,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, case TAK_objc_metatype: if (!isInSILMode()) { diagnose(AtLoc, diag::only_allowed_in_sil, Text); - return false; + return makeParserSuccess(); } break; @@ -3395,12 +3313,12 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, case TAK_sil_unowned: if (!isInSILMode()) { diagnose(AtLoc, diag::only_allowed_in_sil, Text); - return false; + return makeParserSuccess(); } if (Attributes.hasOwnership()) { diagnose(AtLoc, diag::duplicate_attribute, /*isModifier*/false); - return false; + return makeParserSuccess(); } break; @@ -3408,14 +3326,14 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, case TAK_inout: if (!isInSILMode()) { diagnose(AtLoc, diag::inout_not_attribute); - return false; + return makeParserSuccess(); } break; case TAK_opened: { if (!isInSILMode()) { diagnose(AtLoc, diag::only_allowed_in_sil, "opened"); - return false; + return makeParserSuccess(); } // Parse the opened existential ID string in parens @@ -3449,7 +3367,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, Attributes.differentiabilityKind = DifferentiabilityKind::Normal; if (parseDifferentiableTypeAttributeArgument( *this, Attributes, /*emitDiagnostics=*/!justChecking)) - return true; + return makeParserError(); // Only 'reverse' is supported today. // TODO: Change this to an error once clients have migrated to 'reverse'. if (Attributes.differentiabilityKind == DifferentiabilityKind::Normal) { @@ -3471,31 +3389,31 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, auto beginLoc = Tok.getLoc(); if (!consumeIfNotAtStartOfLine(tok::l_paren)) { diagnose(Tok, diag::attr_expected_lparen, "_opaqueReturnTypeOf", false); - return true; + return makeParserError(); } if (!Tok.is(tok::string_literal)) { diagnose(Tok, diag::opened_attribute_id_value); - return true; + return makeParserError(); } auto mangling = Tok.getText().slice(1, Tok.getText().size() - 1); consumeToken(tok::string_literal); if (!Tok.is(tok::comma)) { diagnose(Tok, diag::attr_expected_comma, "_opaqueReturnTypeOf", false); - return true; + return makeParserError(); } consumeToken(tok::comma); if (!Tok.is(tok::integer_literal)) { diagnose(Tok, diag::attr_expected_string_literal, "_opaqueReturnTypeOf"); - return true; + return makeParserError(); } unsigned index; if (Tok.getText().getAsInteger(10, index)) { diagnose(Tok, diag::attr_expected_string_literal, "_opaqueReturnTypeOf"); - return true; + return makeParserError(); } consumeToken(tok::integer_literal); @@ -3510,7 +3428,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, } Attributes.setAttr(attr, AtLoc); - return false; + return makeParserSuccess(); } /// \verbatim @@ -3566,9 +3484,11 @@ ParserStatus Parser::parseDeclAttributeList(DeclAttributes &Attributes) { // '__consuming' // 'convenience' // 'actor' +// 'distributed' bool Parser::parseDeclModifierList(DeclAttributes &Attributes, SourceLoc &StaticLoc, - StaticSpellingKind &StaticSpelling) { + StaticSpellingKind &StaticSpelling, + bool isFromClangAttribute) { SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::ModifierList); bool isError = false; bool hasModifier = false; @@ -3612,47 +3532,32 @@ bool Parser::parseDeclModifierList(DeclAttributes &Attributes, // that scope, so we have to store enough state to emit the diagnostics // outside of the scope. bool isActorModifier = false; - bool isClassNext = false; SourceLoc actorLoc = Tok.getLoc(); - SourceLoc classLoc; + { BacktrackingScope Scope(*this); - // Is this the class token before the identifier? - auto atClassDecl = [this]() -> bool { - return peekToken().is(tok::identifier) || - Tok.is(tok::kw_class) || - Tok.is(tok::kw_enum) || - Tok.is(tok::kw_struct); - }; consumeToken(); // consume actor isActorModifier = isStartOfSwiftDecl(); - if (isActorModifier) { - isClassNext = atClassDecl(); - while (!atClassDecl()) - consumeToken(); - classLoc = Tok.getLoc(); - } } if (!isActorModifier) break; - auto diag = diagnose(actorLoc, - diag::renamed_platform_condition_argument, "actor class", "actor"); - if (isClassNext) - diag.fixItRemove(classLoc); - else - diag.fixItReplace(classLoc, "actor") - .fixItRemove(actorLoc); - Attributes.add(new (Context) ActorAttr({}, Tok.getLoc())); - consumeToken(); + // Actor is a standalone keyword now, so it can't be used + // as a modifier. Let's diagnose and recover. + isError = true; + + consumeToken(); // consume 'actor' + + diagnose(actorLoc, diag::keyword_cant_be_identifier, Tok.getText()); continue; } SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier); - isError |= parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, Kind); + isError |= parseNewDeclAttribute( + Attributes, /*AtLoc=*/{}, Kind, isFromClangAttribute); hasModifier = true; continue; } @@ -3744,15 +3649,27 @@ bool Parser::parseDeclModifierList(DeclAttributes &Attributes, /// '@' attribute /// '@' attribute attribute-list-clause /// \endverbatim -bool Parser::parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, - SourceLoc &SpecifierLoc, - TypeAttributes &Attributes) { +ParserStatus +Parser::parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, + SourceLoc &SpecifierLoc, + SourceLoc &IsolatedLoc, + TypeAttributes &Attributes) { PatternBindingInitializer *initContext = nullptr; Specifier = ParamDecl::Specifier::Default; while (Tok.is(tok::kw_inout) || - (Tok.is(tok::identifier) && - (Tok.getRawText().equals("__shared") || - Tok.getRawText().equals("__owned")))) { + Tok.isContextualKeyword("__shared") || + Tok.isContextualKeyword("__owned") || + Tok.isContextualKeyword("isolated")) { + + if (Tok.isContextualKeyword("isolated")) { + if (IsolatedLoc.isValid()) { + diagnose(Tok, diag::parameter_specifier_repeated) + .fixItRemove(SpecifierLoc); + } + IsolatedLoc = consumeToken(); + continue; + } + if (SpecifierLoc.isValid()) { diagnose(Tok, diag::parameter_specifier_repeated) .fixItRemove(SpecifierLoc); @@ -3770,21 +3687,23 @@ bool Parser::parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, SpecifierLoc = consumeToken(); } + ParserStatus status; SyntaxParsingContext AttrListCtx(SyntaxContext, SyntaxKind::AttributeList); while (Tok.is(tok::at_sign)) { // Ignore @substituted in SIL mode and leave it for the type parser. if (isInSILMode() && peekToken().getText() == "substituted") - return false; + return status; if (Attributes.AtLoc.isInvalid()) Attributes.AtLoc = Tok.getLoc(); SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute); SourceLoc AtLoc = consumeToken(); - if (parseTypeAttribute(Attributes, AtLoc, initContext)) - return true; + status |= parseTypeAttribute(Attributes, AtLoc, initContext); + if (status.isError()) + return status; } - return false; + return status; } static bool isStartOfOperatorDecl(const Token &Tok, const Token &Tok2) { @@ -4812,9 +4731,9 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, /// 'class' /// type-identifier /// \endverbatim -ParserStatus Parser::parseInheritance(SmallVectorImpl &Inherited, - bool allowClassRequirement, - bool allowAnyObject) { +ParserStatus Parser::parseInheritance( + SmallVectorImpl &Inherited, + bool allowClassRequirement, bool allowAnyObject) { SyntaxParsingContext InheritanceContext(SyntaxContext, SyntaxKind::TypeInheritanceClause); @@ -4874,8 +4793,10 @@ ParserStatus Parser::parseInheritance(SmallVectorImpl &Inherited, // Add 'AnyObject' to the inherited list. Inherited.push_back( - new (Context) SimpleIdentTypeRepr(DeclNameLoc(classLoc), DeclNameRef( - Context.getIdentifier("AnyObject")))); + InheritedEntry( + new (Context) SimpleIdentTypeRepr( + DeclNameLoc(classLoc), + DeclNameRef(Context.getIdentifier("AnyObject"))))); continue; } @@ -4884,7 +4805,7 @@ ParserStatus Parser::parseInheritance(SmallVectorImpl &Inherited, // Record the type if its a single type. if (ParsedTypeResult.isNonNull()) - Inherited.push_back(ParsedTypeResult.get()); + Inherited.push_back(InheritedEntry(ParsedTypeResult.get())); } while (HasNextType); return Status; @@ -5172,7 +5093,9 @@ bool Parser::delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, if (Tok.is(tok::r_brace)) { RBLoc = consumeToken(); } else { - RBLoc = Tok.getLoc(); + // Non-delayed parsing would set the RB location to the LB if it is missing, + // match that behaviour here + RBLoc = LBLoc; error = true; } return error; @@ -5197,7 +5120,7 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { status |= extendedType; // Parse optional inheritance clause. - SmallVector Inherited; + SmallVector Inherited; if (Tok.is(tok::colon)) status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, @@ -5635,7 +5558,7 @@ ParserResult Parser::parseDeclAssociatedType(Parser::ParseDeclOptions // Parse optional inheritance clause. // FIXME: Allow class requirements here. - SmallVector Inherited; + SmallVector Inherited; if (Tok.is(tok::colon)) Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, @@ -6983,7 +6906,7 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, CurDeclContext); // Let the source file track the opaque return type mapping, if any. - if (FuncRetTy && isa(FuncRetTy) && + if (isa_and_nonnull(FuncRetTy) && !InInactiveClauseEnvironment) { if (auto sf = CurDeclContext->getParentSourceFile()) { sf->addUnvalidatedDeclWithOpaqueResultType(FD); @@ -7075,8 +6998,10 @@ BraceStmt *Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { return nullptr; BraceStmt *BS = Body.get(); + // Reset the single expression body status. + AFD->setHasSingleExpressionBody(false); AFD->setBodyParsed(BS); - + if (Parser::shouldReturnSingleExpressionElement(BS->getElements())) { auto Element = BS->getLastElement(); if (auto *stmt = Element.dyn_cast()) { @@ -7219,7 +7144,7 @@ ParserResult Parser::parseDeclEnum(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the enum. if (Tok.is(tok::colon)) { - SmallVector Inherited; + SmallVector Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); @@ -7494,7 +7419,7 @@ ParserResult Parser::parseDeclStruct(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the struct. if (Tok.is(tok::colon)) { - SmallVector Inherited; + SmallVector Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); @@ -7588,7 +7513,7 @@ ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the class. if (Tok.is(tok::colon)) { - SmallVector Inherited; + SmallVector Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); @@ -7685,7 +7610,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) { DebuggerContextChange DCC (*this); // Parse optional inheritance clause. - SmallVector InheritedProtocols; + SmallVector InheritedProtocols; SourceLoc colonLoc; if (Tok.is(tok::colon)) { colonLoc = Tok.getLoc(); @@ -7848,7 +7773,7 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, Subscript->getAttrs() = Attributes; // Let the source file track the opaque return type mapping, if any. - if (ElementTy.get() && isa(ElementTy.get()) && + if (isa_and_nonnull(ElementTy.get()) && !InInactiveClauseEnvironment) { if (auto sf = CurDeclContext->getParentSourceFile()) { sf->addUnvalidatedDeclWithOpaqueResultType(Subscript); @@ -8198,8 +8123,8 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, // designated protocol. These both look like identifiers, so we // parse them both as identifiers here and sort it out in type // checking. - SourceLoc colonLoc; - SmallVector, 4> identifiers; + SourceLoc colonLoc, groupLoc; + Identifier groupName; if (Tok.is(tok::colon)) { SyntaxParsingContext GroupCtxt(SyntaxContext, SyntaxKind::OperatorPrecedenceAndTypes); @@ -8214,43 +8139,40 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, return makeParserCodeCompletionResult(); } - if (Context.TypeCheckerOpts.EnableOperatorDesignatedTypes) { - if (Tok.is(tok::identifier)) { - SyntaxParsingContext GroupCtxt(SyntaxContext, - SyntaxKind::IdentifierList); + SyntaxParsingContext ListCtxt(SyntaxContext, SyntaxKind::IdentifierList); - Identifier name; - auto loc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/false); - identifiers.emplace_back(name, loc); + (void)parseIdentifier(groupName, groupLoc, + diag::operator_decl_expected_precedencegroup, + /*diagnoseDollarPrefix=*/false); + + if (Context.TypeCheckerOpts.EnableOperatorDesignatedTypes) { + // Designated types have been removed; consume the list (mainly for source + // compatibility with old swiftinterfaces) and emit a warning. - while (Tok.is(tok::comma)) { - auto comma = consumeToken(); + // These SourceLocs point to the ends of the designated type list. If + // `typesEndLoc` never becomes valid, we didn't find any designated types. + SourceLoc typesStartLoc = Tok.getLoc(); + SourceLoc typesEndLoc; - if (Tok.is(tok::identifier)) { - Identifier name; - auto loc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/false); - identifiers.emplace_back(name, loc); - } else { - if (Tok.isNot(tok::eof)) { - auto otherTokLoc = consumeToken(); - diagnose(otherTokLoc, diag::operator_decl_expected_type); - } else { - diagnose(comma, diag::operator_decl_trailing_comma); - } - } - } + if (isPrefix || isPostfix) { + // These have no precedence group, so we already parsed the first entry + // in the designated types list. Retroactively include it in the range. + typesStartLoc = colonLoc; + typesEndLoc = groupLoc; } - } else if (Tok.is(tok::identifier)) { - SyntaxParsingContext GroupCtxt(SyntaxContext, - SyntaxKind::IdentifierList); - Identifier name; - auto nameLoc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/false); - identifiers.emplace_back(name, nameLoc); + while (consumeIf(tok::comma, typesEndLoc)) { + if (Tok.isNot(tok::eof)) + typesEndLoc = consumeToken(); + } + if (typesEndLoc.isValid()) + diagnose(typesStartLoc, diag::operator_decl_remove_designated_types) + .fixItRemove({typesStartLoc, typesEndLoc}); + } else { if (isPrefix || isPostfix) { diagnose(colonLoc, diag::precedencegroup_not_infix) - .fixItRemove({colonLoc, nameLoc}); + .fixItRemove({colonLoc, groupLoc}); } // Nothing to complete here, simply consume the token. if (Tok.is(tok::code_complete)) @@ -8266,10 +8188,7 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, } else { auto Diag = diagnose(lBraceLoc, diag::deprecated_operator_body); if (Tok.is(tok::r_brace)) { - SourceLoc lastGoodLoc = - !identifiers.empty() ? identifiers.back().Loc : SourceLoc(); - if (lastGoodLoc.isInvalid()) - lastGoodLoc = NameLoc; + SourceLoc lastGoodLoc = groupLoc.isValid() ? groupLoc : NameLoc; SourceLoc lastGoodLocEnd = Lexer::getLocForEndOfToken(SourceMgr, lastGoodLoc); SourceLoc rBraceEnd = Lexer::getLocForEndOfToken(SourceMgr, Tok.getLoc()); @@ -8282,18 +8201,16 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, } OperatorDecl *res; - if (Attributes.hasAttribute()) + if (isPrefix) res = new (Context) - PrefixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc, - Context.AllocateCopy(identifiers)); - else if (Attributes.hasAttribute()) + PrefixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc); + else if (isPostfix) res = new (Context) - PostfixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc, - Context.AllocateCopy(identifiers)); + PostfixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc); else res = new (Context) InfixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc, colonLoc, - Context.AllocateCopy(identifiers)); + groupName, groupLoc); diagnoseOperatorFixityAttributes(*this, Attributes, res); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 74626ed0fbfb1..bf6a6c5438627 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -229,7 +229,7 @@ ParserResult Parser::parseExprSequence(Diag<> Message, // this, because then we can produce a fixit to rewrite the && into a , // if we're in a stmt-condition. if (Tok.getText() == "&&" && - peekToken().isAny(tok::pound_available, + peekToken().isAny(tok::pound_available, tok::pound_unavailable, tok::kw_let, tok::kw_var, tok::kw_case)) goto done; @@ -586,9 +586,9 @@ ParserResult Parser::parseExprUnary(Diag<> Message, bool isExprBasic) { } } - return makeParserResult( - Status, new (Context) PrefixUnaryExpr( - Operator, formUnaryArgument(Context, SubExpr.get()))); + auto *opCall = PrefixUnaryExpr::create( + Context, Operator, formUnaryArgument(Context, SubExpr.get())); + return makeParserResult(Status, opCall); } /// expr-keypath-swift: @@ -659,21 +659,26 @@ ParserResult Parser::parseExprKeyPath() { if (rootResult.isNull() && pathResult.isNull()) return nullptr; - auto keypath = - new (Context) KeyPathExpr(backslashLoc, rootResult.getPtrOrNull(), - pathResult.getPtrOrNull(), hasLeadingDot); - // Handle code completion. if ((Tok.is(tok::code_complete) && !Tok.isAtStartOfLine()) || (Tok.is(tok::period) && peekToken().isAny(tok::code_complete))) { SourceLoc DotLoc; consumeIf(tok::period, DotLoc); + + // Add the code completion expression to the path result. + CodeCompletionExpr *CC = new (Context) + CodeCompletionExpr(pathResult.getPtrOrNull(), Tok.getLoc()); + auto keypath = new (Context) + KeyPathExpr(backslashLoc, rootResult.getPtrOrNull(), CC, hasLeadingDot); if (CodeCompletion) CodeCompletion->completeExprKeyPath(keypath, DotLoc); consumeToken(tok::code_complete); return makeParserCodeCompletionResult(keypath); } + auto keypath = + new (Context) KeyPathExpr(backslashLoc, rootResult.getPtrOrNull(), + pathResult.getPtrOrNull(), hasLeadingDot); return makeParserResult(parseStatus, keypath); } @@ -1170,6 +1175,19 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, CodeCompletion->completeDotExpr(CCExpr, /*DotLoc=*/TokLoc); } consumeToken(tok::code_complete); + + // Parse and discard remaining suffixes. + // e.g. + // closureReceiver { + // baseExpr. { $0 } + // } + // In this case, we want to consume the trailing closure because + // otherwise it will get parsed as a get-set clause on a variable + // declared by `baseExpr.` which is complete garbage. + bool hasBindOptional = false; + parseExprPostfixSuffix(makeParserResult(CCExpr), isExprBasic, + periodHasKeyPathBehavior, hasBindOptional); + return makeParserCodeCompletionResult(CCExpr); } @@ -1193,13 +1211,17 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, SmallVector args; SourceLoc LAngleLoc, RAngleLoc; auto argStat = parseGenericArguments(args, LAngleLoc, RAngleLoc); + Result = makeParserResult(Result | argStat, Result.getPtrOrNull()); if (argStat.isErrorOrHasCompletion()) diagnose(LAngleLoc, diag::while_parsing_as_left_angle_bracket); SyntaxContext->createNodeInPlace(SyntaxKind::SpecializeExpr); - Result = makeParserResult( - Result, UnresolvedSpecializeExpr::create( - Context, Result.get(), LAngleLoc, args, RAngleLoc)); + // The result can be empty in error cases. + if (!args.empty()) { + Result = makeParserResult( + Result, UnresolvedSpecializeExpr::create( + Context, Result.get(), LAngleLoc, args, RAngleLoc)); + } } continue; @@ -1301,9 +1323,9 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, Expr *oper = parseExprOperator(); - Result = makeParserResult( - Result, new (Context) PostfixUnaryExpr( - oper, formUnaryArgument(Context, Result.get()))); + auto *opCall = PostfixUnaryExpr::create( + Context, oper, formUnaryArgument(Context, Result.get())); + Result = makeParserResult(Result, opCall); SyntaxContext->createNodeInPlace(SyntaxKind::PostfixUnaryExpr); continue; } @@ -1727,10 +1749,12 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { case tok::l_square: return parseExprCollection(); - case tok::pound_available: { - // For better error recovery, parse but reject #available in an expr + case tok::pound_available: + case tok::pound_unavailable: { + // For better error recovery, parse but reject availability in an expr // context. - diagnose(Tok.getLoc(), diag::availability_query_outside_if_stmt_guard); + diagnose(Tok.getLoc(), diag::availability_query_outside_if_stmt_guard, + Tok.getText()); auto res = parseStmtConditionPoundAvailable(); if (res.hasCodeCompletion()) return makeParserCodeCompletionStatus(); @@ -2460,7 +2484,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( // speculative parse to validate it and look for 'in'. if (Tok.isAny( tok::at_sign, tok::l_paren, tok::l_square, tok::identifier, - tok::kw__)) { + tok::kw__, tok::code_complete)) { BacktrackingScope backtrack(*this); // Consume attributes. @@ -2496,11 +2520,11 @@ ParserStatus Parser::parseClosureSignatureIfPresent( } // Okay, we have a closure signature. - } else if (Tok.isIdentifierOrUnderscore()) { + } else if (Tok.isIdentifierOrUnderscore() || Tok.is(tok::code_complete)) { // Parse identifier (',' identifier)* consumeToken(); while (consumeIf(tok::comma)) { - if (Tok.isIdentifierOrUnderscore()) { + if (Tok.isIdentifierOrUnderscore() || Tok.is(tok::code_complete)) { consumeToken(); continue; } @@ -2575,7 +2599,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( if (!consumeIf(tok::r_paren, ownershipLocEnd)) diagnose(Tok, diag::attr_unowned_expected_rparen); } - } else if (Tok.isAny(tok::identifier, tok::kw_self) && + } else if (Tok.isAny(tok::identifier, tok::kw_self, tok::code_complete) && peekToken().isAny(tok::equal, tok::comma, tok::r_square)) { // "x = 42", "x," and "x]" are all strong captures of x. } else { @@ -2584,7 +2608,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( continue; } - if (Tok.isNot(tok::identifier, tok::kw_self)) { + if (Tok.isNot(tok::identifier, tok::kw_self, tok::code_complete)) { diagnose(Tok, diag::expected_capture_specifier_name); skipUntil(tok::comma, tok::r_square); continue; @@ -2602,10 +2626,20 @@ ParserStatus Parser::parseClosureSignatureIfPresent( if (peekToken().isNot(tok::equal)) { // If this is the simple case, then the identifier is both the name and // the expression to capture. - name = Context.getIdentifier(Tok.getText()); - auto initializerResult = parseExprIdentifier(); - status |= initializerResult; - initializer = initializerResult.get(); + if (!Tok.is(tok::code_complete)) { + name = Context.getIdentifier(Tok.getText()); + auto initializerResult = parseExprIdentifier(); + status |= initializerResult; + initializer = initializerResult.get(); + } else { + auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); + if (CodeCompletion) + CodeCompletion->completePostfixExprBeginning(CCE); + name = Identifier(); + initializer = CCE; + consumeToken(); + status.setHasCodeCompletion(); + } // It is a common error to try to capture a nested field instead of just // a local name, reject it with a specific error message. @@ -2617,7 +2651,13 @@ ParserStatus Parser::parseClosureSignatureIfPresent( } else { // Otherwise, the name is a new declaration. - consumeIdentifier(name, /*diagnoseDollarPrefix=*/true); + if (!Tok.is(tok::code_complete)) { + consumeIdentifier(name, /*diagnoseDollarPrefix=*/true); + } else { + // Ignore completion token because it's a new declaration. + name = Identifier(); + consumeToken(tok::code_complete); + } equalLoc = consumeToken(tok::equal); auto ExprResult = parseExpr(diag::expected_init_capture_specifier); @@ -2651,7 +2691,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( /*VarLoc*/ nameLoc, pattern, /*EqualLoc*/ equalLoc, initializer, CurDeclContext); - auto CLE = CaptureListEntry(VD, PBD); + auto CLE = CaptureListEntry(PBD); if (CLE.isSimpleSelfCapture()) VD->setIsSelfParamCapture(); @@ -2687,7 +2727,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent( bool HasNext; do { SyntaxParsingContext ClParamCtx(SyntaxContext, SyntaxKind::ClosureParam); - if (Tok.isNot(tok::identifier, tok::kw__)) { + if (Tok.isNot(tok::identifier, tok::kw__, tok::code_complete)) { diagnose(Tok, diag::expected_closure_parameter_name); status.setIsParseError(); break; @@ -2698,7 +2738,10 @@ ParserStatus Parser::parseClosureSignatureIfPresent( if (Tok.is(tok::identifier)) { nameLoc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/false); } else { - nameLoc = consumeToken(tok::kw__); + assert(Tok.isAny(tok::kw__ , tok::code_complete)); + // Consume and ignore code_completion token so that completion don't + // suggest anything for the parameter name declaration. + nameLoc = consumeToken(); } auto var = new (Context) ParamDecl(SourceLoc(), SourceLoc(), diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index 64ba485f837db..3f742781c2cd8 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -78,7 +78,7 @@ Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc, } // Parse the ':' followed by a type. - SmallVector Inherited; + SmallVector Inherited; if (Tok.is(tok::colon)) { (void)consumeToken(); ParserResult Ty; @@ -101,7 +101,7 @@ Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc, return makeParserCodeCompletionStatus(); if (Ty.isNonNull()) - Inherited.push_back(Ty.get()); + Inherited.push_back({Ty.get()}); } // We always create generic type parameters with an invalid depth. diff --git a/lib/Parse/ParseIfConfig.cpp b/lib/Parse/ParseIfConfig.cpp index 0a2ee5dcf60f4..ead8f66571705 100644 --- a/lib/Parse/ParseIfConfig.cpp +++ b/lib/Parse/ParseIfConfig.cpp @@ -98,12 +98,17 @@ static llvm::VersionTuple getCanImportVersion(TupleExpr *te, } return result; } + StringRef verText; if (auto *nle = dyn_cast(subE)) { - auto digits = nle->getDigitsText(); - if (result.tryParse(digits)) { - if (D) { - D->diagnose(nle->getLoc(), diag::canimport_version, digits); - } + verText = nle->getDigitsText(); + } else if (auto *sle= dyn_cast(subE)) { + verText = sle->getValue(); + } + if (verText.empty()) + return result; + if (result.tryParse(verText)) { + if (D) { + D->diagnose(subE->getLoc(), diag::canimport_version, verText); } } return result; @@ -199,11 +204,7 @@ class ValidateIfConfigCondition : // Apply the operator with left-associativity by folding the first two // operands. - TupleExpr *Arg = TupleExpr::create(Ctx, SourceLoc(), { LHS, RHS }, - { }, { }, SourceLoc(), - /*HasTrailingClosure=*/false, - /*Implicit=*/true); - LHS = new (Ctx) BinaryExpr(Op, Arg, /*implicit*/false); + LHS = BinaryExpr::create(Ctx, LHS, Op, RHS, /*implicit*/ false); // If we don't have the next operator, we're done. if (IsEnd) @@ -522,9 +523,8 @@ class EvaluateIfConfigCondition : bool visitBinaryExpr(BinaryExpr *E) { auto OpName = getDeclRefStr(E->getFn()); - auto Args = E->getArg()->getElements(); - if (OpName == "||") return visit(Args[0]) || visit(Args[1]); - if (OpName == "&&") return visit(Args[0]) && visit(Args[1]); + if (OpName == "||") return visit(E->getLHS()) || visit(E->getRHS()); + if (OpName == "&&") return visit(E->getLHS()) && visit(E->getRHS()); llvm_unreachable("unsupported binary operator"); } @@ -552,9 +552,8 @@ class IsVersionIfConfigCondition : bool visitBinaryExpr(BinaryExpr *E) { auto OpName = getDeclRefStr(E->getFn()); - auto Args = E->getArg()->getElements(); - if (OpName == "||") return visit(Args[0]) && visit(Args[1]); - if (OpName == "&&") return visit(Args[0]) || visit(Args[1]); + if (OpName == "||") return visit(E->getLHS()) && visit(E->getRHS()); + if (OpName == "&&") return visit(E->getLHS()) || visit(E->getRHS()); llvm_unreachable("unsupported binary operator"); } @@ -587,9 +586,8 @@ static bool isPlatformConditionDisjunction(Expr *E, PlatformConditionKind Kind, ArrayRef Vals) { if (auto *Or = dyn_cast(E)) { if (getDeclRefStr(Or->getFn()) == "||") { - auto Args = Or->getArg()->getElements(); - return (isPlatformConditionDisjunction(Args[0], Kind, Vals) && - isPlatformConditionDisjunction(Args[1], Kind, Vals)); + return (isPlatformConditionDisjunction(Or->getLHS(), Kind, Vals) && + isPlatformConditionDisjunction(Or->getRHS(), Kind, Vals)); } } else if (auto *P = dyn_cast(E)) { return isPlatformConditionDisjunction(P->getSubExpr(), Kind, Vals); @@ -650,11 +648,10 @@ static Expr *findAnyLikelySimulatorEnvironmentTest(Expr *Condition) { if (auto *And = dyn_cast(Condition)) { if (getDeclRefStr(And->getFn()) == "&&") { - auto Args = And->getArg()->getElements(); - if ((isSimulatorPlatformOSTest(Args[0]) && - isSimulatorPlatformArchTest(Args[1])) || - (isSimulatorPlatformOSTest(Args[1]) && - isSimulatorPlatformArchTest(Args[0]))) { + if ((isSimulatorPlatformOSTest(And->getLHS()) && + isSimulatorPlatformArchTest(And->getRHS())) || + (isSimulatorPlatformOSTest(And->getRHS()) && + isSimulatorPlatformArchTest(And->getLHS()))) { return And; } } diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 9a4328d05a4b2..7b6ab7d613187 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -17,6 +17,7 @@ #include "swift/Parse/Parser.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/Module.h" #include "swift/AST/SourceFile.h" @@ -150,22 +151,37 @@ static ParserStatus parseDefaultArgument( /// Determine whether we are at the start of a parameter name when /// parsing a parameter. -static bool startsParameterName(Parser &parser, bool isClosure) { - // '_' cannot be a type, so it must be a parameter name. - if (parser.Tok.is(tok::kw__)) - return true; - +bool Parser::startsParameterName(bool isClosure) { // To have a parameter name here, we need a name. - if (!parser.Tok.canBeArgumentLabel()) + if (!Tok.canBeArgumentLabel()) return false; - // If the next token can be an argument label or is ':', this is a name. - const auto &nextTok = parser.peekToken(); - if (nextTok.is(tok::colon) || nextTok.canBeArgumentLabel()) + // If the next token is ':', this is a name. + const auto &nextTok = peekToken(); + if (nextTok.is(tok::colon)) return true; - if (parser.isOptionalToken(nextTok) - || parser.isImplicitlyUnwrappedOptionalToken(nextTok)) + // If the next token can be an argument label, we might have a name. + if (nextTok.canBeArgumentLabel()) { + // If the first name wasn't "isolated", we're done. + if (!Tok.isContextualKeyword("isolated") && + !Tok.isContextualKeyword("some")) + return true; + + // "isolated" can be an argument label, but it's also a contextual keyword, + // so look ahead one more token (two total) see if we have a ':' that would + // indicate that this is an argument label. + return lookahead(2, [&](CancellableBacktrackingScope &) { + if (Tok.is(tok::colon)) + return true; // isolated : + + // isolated x : + return Tok.canBeArgumentLabel() && nextTok.is(tok::colon); + }); + } + + if (isOptionalToken(nextTok) + || isImplicitlyUnwrappedOptionalToken(nextTok)) return false; // The identifier could be a name or it could be a type. In a closure, we @@ -238,26 +254,51 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, } } - // ('inout' | '__shared' | '__owned')? + // ('inout' | '__shared' | '__owned' | isolated)? bool hasSpecifier = false; while (Tok.is(tok::kw_inout) || - (Tok.is(tok::identifier) && - (Tok.getRawText().equals("__shared") || - Tok.getRawText().equals("__owned")))) { + Tok.isContextualKeyword("__shared") || + Tok.isContextualKeyword("__owned") || + Tok.isContextualKeyword("isolated")) { + + if (Tok.isContextualKeyword("isolated")) { + // did we already find an 'isolated' type modifier? + if (param.IsolatedLoc.isValid()) { + diagnose(Tok, diag::parameter_specifier_repeated) + .fixItRemove(Tok.getLoc()); + consumeToken(); + continue; + } + + // is this 'isolated' token the identifier of an argument label? + bool partOfArgumentLabel = lookahead(1, [&](CancellableBacktrackingScope &) { + if (Tok.is(tok::colon)) + return true; // isolated : + + // isolated x : + return Tok.canBeArgumentLabel() && peekToken().is(tok::colon); + }); + + if (partOfArgumentLabel) + break; + + // consume 'isolated' as type modifier + param.IsolatedLoc = consumeToken(); + continue; + } + if (!hasSpecifier) { if (Tok.is(tok::kw_inout)) { // This case is handled later when mapping to ParamDecls for // better fixits. param.SpecifierKind = ParamDecl::Specifier::InOut; param.SpecifierLoc = consumeToken(); - } else if (Tok.is(tok::identifier) && - Tok.getRawText().equals("__shared")) { + } else if (Tok.isContextualKeyword("__shared")) { // This case is handled later when mapping to ParamDecls for // better fixits. param.SpecifierKind = ParamDecl::Specifier::Shared; param.SpecifierLoc = consumeToken(); - } else if (Tok.is(tok::identifier) && - Tok.getRawText().equals("__owned")) { + } else if (Tok.isContextualKeyword("__owned")) { // This case is handled later when mapping to ParamDecls for // better fixits. param.SpecifierKind = ParamDecl::Specifier::Owned; @@ -279,8 +320,8 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText()) .fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`"); } - - if (startsParameterName(*this, isClosure)) { + + if (startsParameterName(isClosure)) { // identifier-or-none for the first name param.FirstNameLoc = consumeArgumentLabel(param.FirstName, /*diagnoseDollarPrefix=*/!isClosure); @@ -350,7 +391,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, } if (isBareType && paramContext == ParameterContextKind::EnumElement) { - auto type = parseType(diag::expected_parameter_type, false); + auto type = parseType(diag::expected_parameter_type); status |= type; param.Type = type.getPtrOrNull(); param.FirstName = Identifier(); @@ -365,7 +406,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, // the user is about to type the parameter label and we shouldn't // suggest types. SourceLoc typeStartLoc = Tok.getLoc(); - auto type = parseType(diag::expected_parameter_type, false); + auto type = parseType(diag::expected_parameter_type); status |= type; param.Type = type.getPtrOrNull(); @@ -408,7 +449,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, status.setIsParseError(); } } - + // '...'? if (Tok.isEllipsis()) { Tok.setKind(tok::ellipsis); @@ -541,18 +582,42 @@ mapParsedParameters(Parser &parser, "__owned", parsingEnumElt); } + + if (paramInfo.IsolatedLoc.isValid()) { + type = new (parser.Context) IsolatedTypeRepr( + type, paramInfo.IsolatedLoc); + param->setIsolated(); + } + param->setTypeRepr(type); - // If there is `@autoclosure` attribute associated with the type - // let's mark that in the declaration as well, because it - // belongs to both type flags and declaration. - if (auto *ATR = dyn_cast(type)) { - auto &attrs = ATR->getAttrs(); - // At this point we actually don't know if that's valid to mark - // this parameter declaration as `autoclosure` because type has - // not been resolved yet - it should either be a function type - // or typealias with underlying function type. - param->setAutoClosure(attrs.has(TypeAttrKind::TAK_autoclosure)); + // Dig through the type to find any attributes or modifiers that are + // associated with the type but should also be reflected on the + // declaration. + { + auto unwrappedType = type; + while (true) { + if (auto *ATR = dyn_cast(unwrappedType)) { + auto &attrs = ATR->getAttrs(); + // At this point we actually don't know if that's valid to mark + // this parameter declaration as `autoclosure` because type has + // not been resolved yet - it should either be a function type + // or typealias with underlying function type. + param->setAutoClosure(attrs.has(TypeAttrKind::TAK_autoclosure)); + + unwrappedType = ATR->getTypeRepr(); + continue; + } + + if (auto *STR = dyn_cast(unwrappedType)) { + if (isa(STR)) + param->setIsolated(true); + unwrappedType = STR->getBase(); + continue;; + } + + break; + } } } else if (paramInfo.SpecifierLoc.isValid()) { StringRef specifier; @@ -816,8 +881,8 @@ Parser::parseFunctionSignature(Identifier SimpleName, arrowLoc = consumeToken(tok::colon); } - // Check for effect specifiers after the arrow, but before the type, and - // correct it. + // Check for effect specifiers after the arrow, but before the return type, + // and correct it. parseEffectsSpecifiers(arrowLoc, asyncLoc, &reasync, throwsLoc, &rethrows); ParserResult ResultType = @@ -1070,7 +1135,7 @@ ParserResult Parser::parsePattern() { // code complete anything here -- we expect an identifier. consumeToken(tok::code_complete); } - return nullptr; + return makeParserCodeCompletionStatus(); case tok::kw_var: case tok::kw_let: { diff --git a/lib/Parse/ParseRequests.cpp b/lib/Parse/ParseRequests.cpp index 5341d138c17eb..8fbc368e579ac 100644 --- a/lib/Parse/ParseRequests.cpp +++ b/lib/Parse/ParseRequests.cpp @@ -93,7 +93,7 @@ BraceStmt *ParseAbstractFunctionBodyRequest::evaluate( switch (afd->getBodyKind()) { case BodyKind::Deserialized: - case BodyKind::MemberwiseInitializer: + case BodyKind::SILSynthesize: case BodyKind::None: case BodyKind::Skipped: return nullptr; diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index d8aca843f45f0..751d21fa6b82a 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -512,12 +512,12 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, } /// Recover from a 'case' or 'default' outside of a 'switch' by consuming up to -/// the next ':'. +/// the next ':' or '}'. static ParserResult recoverFromInvalidCase(Parser &P) { assert(P.Tok.is(tok::kw_case) || P.Tok.is(tok::kw_default) && "not case or default?!"); P.diagnose(P.Tok, diag::case_outside_of_switch, P.Tok.getText()); - P.skipUntil(tok::colon); + P.skipUntil(tok::colon, tok::r_brace); // FIXME: Return an ErrorStmt? return nullptr; } @@ -968,7 +968,7 @@ ParserResult Parser::parseStmtDefer() { auto DRE = new (Context) DeclRefExpr(tempDecl, DeclNameLoc(loc), /*Implicit*/true, AccessSemantics::DirectToStorage); - auto call = CallExpr::createImplicit(Context, DRE, { }, { }); + auto call = CallExpr::createImplicitEmpty(Context, DRE); auto DS = new (Context) DeferStmt(DeferLoc, tempDecl, call); return makeParserResult(Status, DS); @@ -1169,18 +1169,18 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result, } } -/// Validate availability spec list, emitting diagnostics if necessary and removing -/// specs for unrecognized platforms. +/// Validate availability spec list, emitting diagnostics if necessary and +/// removing specs for unrecognized platforms. static void validateAvailabilitySpecList(Parser &P, SmallVectorImpl &Specs, - bool ParsingMacroDefinition) { + Parser::AvailabilitySpecSource Source) { llvm::SmallSet Platforms; - bool HasOtherPlatformSpec = false; + Optional OtherPlatformSpecLoc = None; if (Specs.size() == 1 && isa(Specs[0])) { - // @available(swift N) and @available(_PackageDescription N) are allowed + // @available(swift N) and @available(_PackageDescription N) are allowed // only in isolation; they cannot be combined with other availability specs // in a single list. return; @@ -1189,16 +1189,18 @@ validateAvailabilitySpecList(Parser &P, SmallVector RecognizedSpecs; for (auto *Spec : Specs) { RecognizedSpecs.push_back(Spec); - if (isa(Spec)) { - HasOtherPlatformSpec = true; + if (auto *OtherPlatSpec = dyn_cast(Spec)) { + OtherPlatformSpecLoc = OtherPlatSpec->getStarLoc(); continue; } if (auto *PlatformAgnosticSpec = - dyn_cast(Spec)) { + dyn_cast(Spec)) { P.diagnose(PlatformAgnosticSpec->getPlatformAgnosticNameLoc(), diag::availability_must_occur_alone, - PlatformAgnosticSpec->isLanguageVersionSpecific() ? "swift" : "_PackageDescription"); + PlatformAgnosticSpec->isLanguageVersionSpecific() + ? "swift" + : "_PackageDescription"); continue; } @@ -1222,25 +1224,58 @@ validateAvailabilitySpecList(Parser &P, } } - if (ParsingMacroDefinition) { - if (HasOtherPlatformSpec) { + switch (Source) { + case Parser::AvailabilitySpecSource::Available: { + if (OtherPlatformSpecLoc == None) { SourceLoc InsertWildcardLoc = P.PreviousLoc; - P.diagnose(InsertWildcardLoc, diag::attr_availability_wildcard_in_macro); - } - } else if (!HasOtherPlatformSpec) { - SourceLoc InsertWildcardLoc = P.PreviousLoc; - P.diagnose(InsertWildcardLoc, diag::availability_query_wildcard_required) + P.diagnose(InsertWildcardLoc, diag::availability_query_wildcard_required) .fixItInsertAfter(InsertWildcardLoc, ", *"); + } + break; + } + case Parser::AvailabilitySpecSource::Unavailable: { + if (OtherPlatformSpecLoc != None) { + SourceLoc Loc = OtherPlatformSpecLoc.getValue(); + P.diagnose(Loc, diag::unavailability_query_wildcard_not_required) + .fixItRemove(Loc); + } + break; + } + case Parser::AvailabilitySpecSource::Macro: { + if (OtherPlatformSpecLoc != None) { + SourceLoc Loc = OtherPlatformSpecLoc.getValue(); + P.diagnose(Loc, diag::attr_availability_wildcard_in_macro); + } + break; + } } Specs = RecognizedSpecs; } // #available(...) +// #unavailable(...) ParserResult Parser::parseStmtConditionPoundAvailable() { - SyntaxParsingContext ConditonCtxt(SyntaxContext, - SyntaxKind::AvailabilityCondition); - SourceLoc PoundLoc = consumeToken(tok::pound_available); + tok MainToken; + SyntaxKind Kind; + AvailabilitySpecSource Source; + bool isUnavailability; + if (Tok.is(tok::pound_available)) { + MainToken = tok::pound_available; + Kind = SyntaxKind::AvailabilityCondition; + Source = AvailabilitySpecSource::Available; + isUnavailability = false; + } else { + MainToken = tok::pound_unavailable; + Kind = SyntaxKind::UnavailabilityCondition; + Source = AvailabilitySpecSource::Unavailable; + isUnavailability = true; + } + + SyntaxParsingContext ConditonCtxt(SyntaxContext, Kind); + SourceLoc PoundLoc; + + PoundLoc = consumeToken(MainToken); if (!Tok.isFollowingLParen()) { diagnose(Tok, diag::avail_query_expected_condition); @@ -1252,19 +1287,20 @@ ParserResult Parser::parseStmtConditionPoundAvailable() { if (ParsingAvailabilitySpecList.isFailed()) { return makeParserError(); } - + SourceLoc LParenLoc = consumeToken(tok::l_paren); SmallVector Specs; - ParserStatus Status = parseAvailabilitySpecList(Specs); + ParserStatus Status = parseAvailabilitySpecList(Specs, Source); for (auto *Spec : Specs) { if (auto *PlatformAgnostic = - dyn_cast(Spec)) { - diagnose(PlatformAgnostic->getPlatformAgnosticNameLoc(), - PlatformAgnostic->isLanguageVersionSpecific() ? - diag::pound_available_swift_not_allowed : - diag::pound_available_package_description_not_allowed); + dyn_cast(Spec)) { + diagnose(PlatformAgnostic->getPlatformAgnosticNameLoc(), + PlatformAgnostic->isLanguageVersionSpecific() + ? diag::pound_available_swift_not_allowed + : diag::pound_available_package_description_not_allowed, + getTokenText(MainToken)); Status.setIsParseError(); } } @@ -1274,8 +1310,20 @@ ParserResult Parser::parseStmtConditionPoundAvailable() { diag::avail_query_expected_rparen, LParenLoc)) Status.setIsParseError(); + // Diagnose #available == false as being a wrong spelling of #unavailable. + if (!isUnavailability && Tok.isAnyOperator() && Tok.getText() == "==" && + peekToken().is(tok::kw_false)) { + diagnose(Tok, diag::false_available_is_called_unavailable) + .highlight(SourceRange(PoundLoc, peekToken().getLoc())) + .fixItReplace(PoundLoc, getTokenText(tok::pound_unavailable)) + .fixItRemove(SourceRange(Tok.getLoc(), peekToken().getLoc())); + consumeToken(); + consumeToken(); + Status.setIsParseError(); + } + auto *result = PoundAvailableInfo::create(Context, PoundLoc, LParenLoc, Specs, - RParenLoc); + RParenLoc, isUnavailability); return makeParserResult(Status, result); } @@ -1307,13 +1355,12 @@ Parser::parseAvailabilityMacroDefinition(AvailabilityMacroDefinition &Result) { return makeParserError(); } - return parseAvailabilitySpecList(Result.Specs, - /*ParsingMacroDefinition=*/true); + return parseAvailabilitySpecList(Result.Specs, AvailabilitySpecSource::Macro); } ParserStatus Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs, - bool ParsingMacroDefinition) { + AvailabilitySpecSource Source) { SyntaxParsingContext AvailabilitySpecContext( SyntaxContext, SyntaxKind::AvailabilitySpecList); ParserStatus Status = makeParserSuccess(); @@ -1326,19 +1373,26 @@ Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs, // First look for a macro as we need Specs for the expansion. bool MatchedAMacro = false; - if (!ParsingMacroDefinition && Tok.is(tok::identifier)) { - SmallVector MacroSpecs; - ParserStatus MacroStatus = parseAvailabilityMacro(MacroSpecs); - - if (MacroStatus.isError()) { - // There's a parsing error if the platform name matches a macro - // but something goes wrong after. - Status.setIsParseError(); - MatchedAMacro = true; - } else { - MatchedAMacro = !MacroSpecs.empty(); - Specs.append(MacroSpecs.begin(), MacroSpecs.end()); + switch (Source) { + case AvailabilitySpecSource::Available: + case AvailabilitySpecSource::Unavailable: + if (Tok.is(tok::identifier)) { + SmallVector MacroSpecs; + ParserStatus MacroStatus = parseAvailabilityMacro(MacroSpecs); + + if (MacroStatus.isError()) { + // There's a parsing error if the platform name matches a macro + // but something goes wrong after. + Status.setIsParseError(); + MatchedAMacro = true; + } else { + MatchedAMacro = !MacroSpecs.empty(); + Specs.append(MacroSpecs.begin(), MacroSpecs.end()); + } } + break; + case AvailabilitySpecSource::Macro: + break; } if (!MatchedAMacro) { @@ -1410,7 +1464,7 @@ Parser::parseAvailabilitySpecList(SmallVectorImpl &Specs, } if (Status.isSuccess() && !Status.hasCodeCompletion()) - validateAvailabilitySpecList(*this, Specs, ParsingMacroDefinition); + validateAvailabilitySpecList(*this, Specs, Source); return Status; } @@ -1421,8 +1475,17 @@ Parser::parseStmtConditionElement(SmallVectorImpl &result, StringRef &BindingKindStr) { ParserStatus Status; - // Parse a leading #available condition if present. - if (Tok.is(tok::pound_available)) { + // Diagnose !#available as being a wrong spelling of #unavailable. + if (Tok.getText() == "!" && peekToken().is(tok::pound_available)) { + SourceRange Range = SourceRange(Tok.getLoc(), peekToken().getLoc()); + diagnose(Tok, diag::false_available_is_called_unavailable) + .fixItReplace(Range, getTokenText(tok::pound_unavailable)); + // For better error recovery, reject but allow parsing to continue. + consumeToken(); + } + + // Parse a leading #available/#unavailable condition if present. + if (Tok.isAny(tok::pound_available, tok::pound_unavailable)) { auto res = parseStmtConditionPoundAvailable(); if (res.isNull() || res.hasCodeCompletion()) { Status |= res; @@ -2136,6 +2199,17 @@ ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { } } + if (Tok.is(tok::code_complete)) { + if (CodeCompletion) { + CodeCompletion->completeForEachPatternBeginning(TryLoc.isValid(), + AwaitLoc.isValid()); + } + consumeToken(tok::code_complete); + // Since 'completeForeachPatternBeginning' is a keyword only completion, + // we don't need to parse the rest of 'for' statement. + return makeParserCodeCompletionStatus(); + } + // Parse the pattern. This is either 'case ' or just a // normal pattern. if (consumeIf(tok::kw_case)) { diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index e010b2b90130f..d7fdbaa9bfc92 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -36,7 +36,8 @@ using namespace swift::syntax; TypeRepr *Parser::applyAttributeToType(TypeRepr *ty, const TypeAttributes &attrs, ParamDecl::Specifier specifier, - SourceLoc specifierLoc) { + SourceLoc specifierLoc, + SourceLoc isolatedLoc) { // Apply those attributes that do apply. if (!attrs.empty()) { ty = new (Context) AttributedTypeRepr(attrs, ty); @@ -59,6 +60,11 @@ TypeRepr *Parser::applyAttributeToType(TypeRepr *ty, } } + // Apply 'isolated'. + if (isolatedLoc.isValid()) { + ty = new (Context) IsolatedTypeRepr(ty, isolatedLoc); + } + return ty; } @@ -151,7 +157,9 @@ LayoutConstraint Parser::parseLayoutConstraint(Identifier LayoutConstraintID) { /// type-simple '!' /// type-collection /// type-array -ParserResult Parser::parseTypeSimple(Diag<> MessageID) { +/// '_' +ParserResult Parser::parseTypeSimple( + Diag<> MessageID, ParseTypeReason reason) { ParserResult ty; if (Tok.is(tok::kw_inout) || @@ -182,6 +190,9 @@ ParserResult Parser::parseTypeSimple(Diag<> MessageID) { ty = parseTypeCollection(); break; } + case tok::kw__: + ty = makeParserResult(new (Context) PlaceholderTypeRepr(consumeToken())); + break; case tok::kw_protocol: if (startsWithLess(peekToken())) { ty = parseOldStyleProtocolComposition(); @@ -228,16 +239,16 @@ ParserResult Parser::parseTypeSimple(Diag<> MessageID) { if (!Tok.isAtStartOfLine()) { if (isOptionalToken(Tok)) { - ty = parseTypeOptional(ty.get()); + ty = parseTypeOptional(ty); continue; } if (isImplicitlyUnwrappedOptionalToken(Tok)) { - ty = parseTypeImplicitlyUnwrappedOptional(ty.get()); + ty = parseTypeImplicitlyUnwrappedOptional(ty); continue; } // Parse legacy array types for migration. - if (Tok.is(tok::l_square)) { - ty = parseTypeArray(ty.get()); + if (Tok.is(tok::l_square) && reason != ParseTypeReason::CustomAttribute) { + ty = parseTypeArray(ty); continue; } } @@ -311,7 +322,7 @@ ParserResult Parser::parseSILBoxType(GenericParamList *generics, LAngleLoc, Args, RAngleLoc); return makeParserResult(applyAttributeToType(repr, attrs, ParamDecl::Specifier::Owned, - SourceLoc())); + SourceLoc(), SourceLoc())); } @@ -323,16 +334,20 @@ ParserResult Parser::parseSILBoxType(GenericParamList *generics, /// type-function: /// type-composition 'async'? 'throws'? '->' type /// -ParserResult Parser::parseType(Diag<> MessageID, - bool IsSILFuncDecl) { +ParserResult Parser::parseType( + Diag<> MessageID, ParseTypeReason reason) { // Start a context for creating type syntax. SyntaxParsingContext TypeParsingContext(SyntaxContext, SyntaxContextKind::Type); + + ParserStatus status; + // Parse attributes. ParamDecl::Specifier specifier; SourceLoc specifierLoc; + SourceLoc isolatedLoc; TypeAttributes attrs; - parseTypeAttributeList(specifier, specifierLoc, attrs); + status |= parseTypeAttributeList(specifier, specifierLoc, isolatedLoc, attrs); // Parse generic parameters in SIL mode. GenericParamList *generics = nullptr; @@ -359,11 +374,11 @@ ParserResult Parser::parseType(Diag<> MessageID, return parseSILBoxType(generics, attrs); } - ParserResult ty = parseTypeSimpleOrComposition(MessageID); + ParserResult ty = parseTypeSimpleOrComposition(MessageID, reason); + status |= ParserStatus(ty); if (ty.isNull()) - return ty; + return status; auto tyR = ty.get(); - auto status = ParserStatus(ty); // Parse effects specifiers. // Don't consume them, if there's no following '->', so we can emit a more @@ -387,11 +402,11 @@ ParserResult Parser::parseType(Diag<> MessageID, ParserResult SecondHalf = parseType(diag::expected_type_function_result); + status |= SecondHalf; if (SecondHalf.isNull()) { status.setIsParseError(); return status; } - status |= SecondHalf; if (SyntaxContext->isEnabled()) { ParsedFunctionTypeSyntaxBuilder Builder(*SyntaxContext); @@ -529,11 +544,33 @@ ParserResult Parser::parseType(Diag<> MessageID, if (tyR) tyR->walk(walker); } - if (specifierLoc.isValid() || !attrs.empty()) + if (specifierLoc.isValid() || isolatedLoc.isValid() || !attrs.empty()) SyntaxContext->setCreateSyntax(SyntaxKind::AttributedType); - return makeParserResult(status, applyAttributeToType(tyR, attrs, specifier, - specifierLoc)); + return makeParserResult( + status, + applyAttributeToType(tyR, attrs, specifier, specifierLoc, isolatedLoc)); +} + +ParserResult Parser::parseTypeWithOpaqueParams(Diag<> MessageID) { + GenericParamList *genericParams = nullptr; + if (Context.LangOpts.EnableExperimentalNamedOpaqueTypes) { + auto result = maybeParseGenericParams(); + genericParams = result.getPtrOrNull(); + if (result.hasCodeCompletion()) + return makeParserCodeCompletionStatus(); + } + + auto typeResult = parseType(MessageID); + if (auto type = typeResult.getPtrOrNull()) { + return makeParserResult( + ParserStatus(typeResult), + genericParams ? new (Context) + NamedOpaqueReturnTypeRepr(type, genericParams) + : type); + } else { + return typeResult; + } } ParserResult Parser::parseDeclResultType(Diag<> MessageID) { @@ -544,7 +581,7 @@ ParserResult Parser::parseDeclResultType(Diag<> MessageID) { return makeParserCodeCompletionStatus(); } - auto result = parseType(MessageID); + auto result = parseTypeWithOpaqueParams(MessageID); if (!result.isParseErrorOrHasCompletion()) { if (Tok.is(tok::r_square)) { @@ -751,7 +788,7 @@ Parser::parseTypeIdentifier(bool isParsingQualifiedDeclBaseType) { /// 'some'? type-simple /// type-composition '&' type-simple ParserResult -Parser::parseTypeSimpleOrComposition(Diag<> MessageID) { +Parser::parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason) { SyntaxParsingContext SomeTypeContext(SyntaxContext, SyntaxKind::SomeType); // Check for the opaque modifier. // This is only semantically allowed in certain contexts, but we parse it @@ -775,7 +812,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID) { SyntaxParsingContext CompositionContext(SyntaxContext, SyntaxContextKind::Type); // Parse the first type - ParserResult FirstType = parseTypeSimple(MessageID); + ParserResult FirstType = parseTypeSimple(MessageID, reason); if (FirstType.isNull()) return FirstType; if (!Tok.isContextualPunctuator("&")) { @@ -833,7 +870,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID) { // Parse next type. ParserResult ty = - parseTypeSimple(diag::expected_identifier_for_type); + parseTypeSimple(diag::expected_identifier_for_type, reason); if (ty.hasCodeCompletion()) return makeParserCodeCompletionResult(); Status |= ty; @@ -1005,20 +1042,11 @@ ParserResult Parser::parseTypeTupleBody() { Backtracking.emplace(*this); ObsoletedInOutLoc = consumeToken(tok::kw_inout); } - - // If the label is "some", this could end up being an opaque type - // description if there's `some ` without a following colon, - // so we may need to backtrack as well. - if (Tok.isContextualKeyword("some")) { - Backtracking.emplace(*this); - } // If the tuple element starts with a potential argument label followed by a // ':' or another potential argument label, then the identifier is an // element tag, and it is followed by a type annotation. - if (Tok.canBeArgumentLabel() - && (peekToken().is(tok::colon) - || peekToken().canBeArgumentLabel())) { + if (startsParameterName(false)) { // Consume a name. element.NameLoc = consumeArgumentLabel(element.Name, /*diagnoseDollarPrefix=*/true); @@ -1174,12 +1202,11 @@ ParserResult Parser::parseTypeTupleBody() { /// type-array '[' ']' /// type-array '[' expr ']' /// -ParserResult Parser::parseTypeArray(TypeRepr *Base) { +ParserResult Parser::parseTypeArray(ParserResult Base) { assert(Tok.isFollowingLSquare()); Parser::StructureMarkerRAII ParsingArrayBound(*this, Tok); SourceLoc lsquareLoc = consumeToken(); - ArrayTypeRepr *ATR = nullptr; - + // Handle a postfix [] production, a common typo for a C-like array. // If we have something that might be an array size expression, parse it as @@ -1192,19 +1219,23 @@ ParserResult Parser::parseTypeArray(TypeRepr *Base) { SourceLoc rsquareLoc; if (parseMatchingToken(tok::r_square, rsquareLoc, - diag::expected_rbracket_array_type, lsquareLoc)) - return makeParserErrorResult(Base); + diag::expected_rbracket_array_type, lsquareLoc)) { + Base.setIsParseError(); + return Base; + } + + auto baseTyR = Base.get(); // If we parsed something valid, diagnose it with a fixit to rewrite it to // Swift syntax. diagnose(lsquareLoc, diag::new_array_syntax) - .fixItInsert(Base->getStartLoc(), "[") + .fixItInsert(baseTyR->getStartLoc(), "[") .fixItRemove(lsquareLoc); // Build a normal array slice type for recovery. - ATR = new (Context) ArrayTypeRepr(Base, - SourceRange(Base->getStartLoc(), rsquareLoc)); - return makeParserResult(ATR); + ArrayTypeRepr *ATR = new (Context) ArrayTypeRepr( + baseTyR, SourceRange(baseTyR->getStartLoc(), rsquareLoc)); + return makeParserResult(ParserStatus(Base), ATR); } ParserResult Parser::parseTypeCollection() { @@ -1309,22 +1340,22 @@ SourceLoc Parser::consumeImplicitlyUnwrappedOptionalToken() { /// Parse a single optional suffix, given that we are looking at the /// question mark. ParserResult -Parser::parseTypeOptional(TypeRepr *base) { +Parser::parseTypeOptional(ParserResult base) { SourceLoc questionLoc = consumeOptionalToken(); - auto TyR = new (Context) OptionalTypeRepr(base, questionLoc); + auto TyR = new (Context) OptionalTypeRepr(base.get(), questionLoc); SyntaxContext->createNodeInPlace(SyntaxKind::OptionalType); - return makeParserResult(TyR); + return makeParserResult(ParserStatus(base), TyR); } /// Parse a single implicitly unwrapped optional suffix, given that we /// are looking at the exclamation mark. ParserResult -Parser::parseTypeImplicitlyUnwrappedOptional(TypeRepr *base) { +Parser::parseTypeImplicitlyUnwrappedOptional(ParserResult base) { SourceLoc exclamationLoc = consumeImplicitlyUnwrappedOptionalToken(); auto TyR = - new (Context) ImplicitlyUnwrappedOptionalTypeRepr(base, exclamationLoc); + new (Context) ImplicitlyUnwrappedOptionalTypeRepr(base.get(), exclamationLoc); SyntaxContext->createNodeInPlace(SyntaxKind::ImplicitlyUnwrappedOptionalType); - return makeParserResult(TyR); + return makeParserResult(ParserStatus(base), TyR); } //===----------------------------------------------------------------------===// @@ -1442,6 +1473,9 @@ bool Parser::canParseType() { if (!consumeIf(tok::r_square)) return false; break; + case tok::kw__: + consumeToken(); + break; default: diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 5d54a6dff4721..ea7634279353c 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -29,6 +29,7 @@ #include "swift/Parse/ParseSILSupport.h" #include "swift/Parse/SyntaxParseActions.h" #include "swift/Parse/SyntaxParsingContext.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "swift/Syntax/RawSyntax.h" #include "swift/Syntax/TokenSyntax.h" #include "swift/SyntaxParse/SyntaxTreeCreator.h" @@ -1219,6 +1220,7 @@ struct ParserUnit::Implementation { TypeCheckerOptions TypeCheckerOpts; SearchPathOptions SearchPathOpts; ClangImporterOptions clangImporterOpts; + symbolgraphgen::SymbolGraphOptions symbolGraphOpts; DiagnosticEngine Diags; ASTContext &Ctx; SourceFile *SF; @@ -1231,7 +1233,7 @@ struct ParserUnit::Implementation { : SPActions(std::move(spActions)), LangOpts(Opts), TypeCheckerOpts(TyOpts), Diags(SM), Ctx(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, - clangImporterOpts, SM, Diags)) { + clangImporterOpts, symbolGraphOpts, SM, Diags)) { auto parsingOpts = SourceFile::getDefaultParsingOptions(LangOpts); parsingOpts |= ParsingFlags::DisableDelayedBodies; parsingOpts |= ParsingFlags::DisablePoundIfEvaluation; @@ -1403,6 +1405,11 @@ ParsedDeclName swift::parseDeclName(StringRef name) { baseName = baseName.substr(7); } + // If the base name is prefixed by "subscript", it's an subscript. + if (baseName == "subscript") { + result.IsSubscript = true; + } + // Parse the base name. if (parseBaseName(baseName)) return ParsedDeclName(); @@ -1496,10 +1503,6 @@ DeclNameRef swift::formDeclNameRef(ASTContext &ctx, return DeclNameRef({ ctx, baseNameId, argumentLabelIds }); } -DeclName swift::parseDeclName(ASTContext &ctx, StringRef name) { - return parseDeclName(name).formDeclName(ctx); -} - void PrettyStackTraceParser::print(llvm::raw_ostream &out) const { out << "With parser at source location: "; P.Tok.getLoc().print(out, P.Context.SourceMgr); diff --git a/lib/PrintAsObjC/CMakeLists.txt b/lib/PrintAsObjC/CMakeLists.txt index 66044fa3fc7ff..e02db162fe497 100644 --- a/lib/PrintAsObjC/CMakeLists.txt +++ b/lib/PrintAsObjC/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftPrintAsObjC STATIC DeclAndTypePrinter.cpp ModuleContentsWriter.cpp @@ -9,3 +9,4 @@ target_link_libraries(swiftPrintAsObjC PRIVATE swiftFrontend swiftIDE) +set_swift_llvm_is_available(swiftPrintAsObjC) diff --git a/lib/PrintAsObjC/DeclAndTypePrinter.cpp b/lib/PrintAsObjC/DeclAndTypePrinter.cpp index c2476236ade8f..04a1769de9703 100644 --- a/lib/PrintAsObjC/DeclAndTypePrinter.cpp +++ b/lib/PrintAsObjC/DeclAndTypePrinter.cpp @@ -23,6 +23,7 @@ #include "swift/AST/GenericEnvironment.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/SwiftNameTranslation.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeVisitor.h" #include "swift/IDE/CommentConversion.h" #include "swift/Parse/Lexer.h" @@ -591,7 +592,7 @@ class DeclAndTypePrinter::Implementation if (asyncConvention && i == asyncConvention->completionHandlerParamIndex()) { os << piece << ":("; - print(asyncConvention->completionHandlerType(), None); + print(asyncConvention->completionHandlerType(), OTK_None); os << ")completionHandler"; continue; } @@ -927,96 +928,15 @@ class DeclAndTypePrinter::Implementation return hasPrintedAnything; } - const ValueDecl *getRenameDecl(const ValueDecl *D, - const ParsedDeclName renamedParsedDeclName) { - auto declContext = D->getDeclContext(); - ASTContext &astContext = D->getASTContext(); - auto renamedDeclName = renamedParsedDeclName.formDeclNameRef(astContext); - - if (isa(D) || isa(D)) { - if (!renamedParsedDeclName.ContextName.empty()) { - return nullptr; - } - SmallVector decls; - declContext->lookupQualified(declContext->getParentModule(), - renamedDeclName.withoutArgumentLabels(), - NL_OnlyTypes, - decls); - if (decls.size() == 1) - return decls[0]; - return nullptr; - } - - TypeDecl *typeDecl = declContext->getSelfNominalTypeDecl(); - - const ValueDecl *renamedDecl = nullptr; - SmallVector lookupResults; - declContext->lookupQualified(typeDecl->getDeclaredInterfaceType(), - renamedDeclName, NL_QualifiedDefault, - lookupResults); - - if (lookupResults.size() == 1) { - auto candidate = lookupResults[0]; - if (!shouldInclude(candidate)) - return nullptr; - if (candidate->getKind() != D->getKind() || - (candidate->isInstanceMember() != - cast(D)->isInstanceMember())) - return nullptr; - - renamedDecl = candidate; - } else { - for (auto candidate : lookupResults) { - if (!shouldInclude(candidate)) - continue; - - if (candidate->getKind() != D->getKind() || - (candidate->isInstanceMember() != - cast(D)->isInstanceMember())) - continue; - - if (isa(candidate)) { - auto cParams = cast(candidate)->getParameters(); - auto dParams = cast(D)->getParameters(); - - if (cParams->size() != dParams->size()) - continue; - - bool hasSameParameterTypes = true; - for (auto index : indices(*cParams)) { - auto cParamsType = cParams->get(index)->getType(); - auto dParamsType = dParams->get(index)->getType(); - if (!cParamsType->matchesParameter(dParamsType, - TypeMatchOptions())) { - hasSameParameterTypes = false; - break; - } - } - - if (!hasSameParameterTypes) { - continue; - } - } - - if (renamedDecl) { - // If we found a duplicated candidate then we would silently fail. - renamedDecl = nullptr; - break; - } - renamedDecl = candidate; - } - } - return renamedDecl; - } - void printRenameForDecl(const AvailableAttr *AvAttr, const ValueDecl *D, bool includeQuotes) { assert(!AvAttr->Rename.empty()); - const ValueDecl *renamedDecl = - getRenameDecl(D, parseDeclName(AvAttr->Rename)); - + auto *renamedDecl = evaluateOrDefault( + getASTContext().evaluator, RenamedDeclRequest{D, AvAttr}, nullptr); if (renamedDecl) { + assert(shouldInclude(renamedDecl) && + "ObjC printer logic mismatch with renamed decl"); SmallString<128> scratch; auto renamedObjCRuntimeName = renamedDecl->getObjCRuntimeName()->getString(scratch); @@ -1183,7 +1103,7 @@ class DeclAndTypePrinter::Implementation copyTy->isDictionary() || copyTy->isSet() || copyTy->isString() || - (!getKnownTypeInfo(nominal) && getObjCBridgedClass(nominal))) { + getObjCBridgedClass(nominal)) { // We fast-path the most common cases in the condition above. os << ", copy"; } else if (copyTy->isUnmanaged()) { @@ -1371,10 +1291,15 @@ class DeclAndTypePrinter::Implementation public: /// If \p nominal is bridged to an Objective-C class (via a conformance to - /// _ObjectiveCBridgeable), return that class. + /// _ObjectiveCBridgeable) and is not an imported Clang type or a known type, + /// return that class. /// /// Otherwise returns null. const ClassDecl *getObjCBridgedClass(const NominalTypeDecl *nominal) { + // Print known types as their unbridged type. + if (getKnownTypeInfo(nominal)) + return nullptr; + // Print imported bridgeable decls as their unbridged type. if (nominal->hasClangNode()) return nullptr; @@ -1387,7 +1312,7 @@ class DeclAndTypePrinter::Implementation // Determine whether this nominal type is _ObjectiveCBridgeable. SmallVector conformances; - if (!nominal->lookupConformance(&owningPrinter.M, proto, conformances)) + if (!nominal->lookupConformance(proto, conformances)) return nullptr; // Dig out the Objective-C type. @@ -2092,7 +2017,7 @@ auto DeclAndTypePrinter::getImpl() -> Implementation { } bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) { - return isVisibleToObjC(VD, minRequiredAccess) && + return !VD->isInvalid() && isVisibleToObjC(VD, minRequiredAccess) && !VD->getAttrs().hasAttribute(); } diff --git a/lib/PrintAsObjC/ModuleContentsWriter.cpp b/lib/PrintAsObjC/ModuleContentsWriter.cpp index 6d4d53b966ff8..62b966e1f26d9 100644 --- a/lib/PrintAsObjC/ModuleContentsWriter.cpp +++ b/lib/PrintAsObjC/ModuleContentsWriter.cpp @@ -86,7 +86,7 @@ class ReferencedTypeFinder : public TypeDeclFinder { auto sig = decl->getGenericSignature(); for_each(boundGeneric->getGenericArgs(), - sig->getInnermostGenericParams(), + sig.getInnermostGenericParams(), [&](Type argTy, GenericTypeParamType *paramTy) { // FIXME: I think there's a bug here with recursive generic types. if (isObjCGeneric && isConstrained(sig, paramTy)) @@ -449,7 +449,7 @@ class ModuleWriter { SmallVector conformances; auto errorTypeProto = ctx.getProtocol(KnownProtocolKind::Error); - if (ED->lookupConformance(&M, errorTypeProto, conformances)) { + if (ED->lookupConformance(errorTypeProto, conformances)) { bool hasDomainCase = std::any_of(ED->getAllElements().begin(), ED->getAllElements().end(), [](const EnumElementDecl *elem) { @@ -598,6 +598,8 @@ class ModuleWriter { void swift::printModuleContentsAsObjC(raw_ostream &os, llvm::SmallPtrSetImpl &imports, - ModuleDecl &M, AccessLevel minRequiredAccess) { - ModuleWriter(os, imports, M, minRequiredAccess).write(); + ModuleDecl &M) { + auto requiredAccess = M.isExternallyConsumed() ? AccessLevel::Public + : AccessLevel::Internal; + ModuleWriter(os, imports, M, requiredAccess).write(); } diff --git a/lib/PrintAsObjC/ModuleContentsWriter.h b/lib/PrintAsObjC/ModuleContentsWriter.h index 0336b6e7da95b..33532bb9e2949 100644 --- a/lib/PrintAsObjC/ModuleContentsWriter.h +++ b/lib/PrintAsObjC/ModuleContentsWriter.h @@ -27,11 +27,11 @@ class ModuleDecl; using ImportModuleTy = PointerUnion; -/// Prints the declarations of \p M to \p os, filtering by \p minRequiredAccess -/// and collecting imports in \p imports along the way. +/// Prints the declarations of \p M to \p os and collecting imports in +/// \p imports along the way. void printModuleContentsAsObjC(raw_ostream &os, llvm::SmallPtrSetImpl &imports, - ModuleDecl &M, AccessLevel minRequiredAccess); + ModuleDecl &M); } // end namespace swift diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index 31d8f30d91709..d83c80ad8a3a8 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -383,14 +383,13 @@ static void writeEpilogue(raw_ostream &os) { } bool swift::printAsObjC(raw_ostream &os, ModuleDecl *M, - StringRef bridgingHeader, - AccessLevel minRequiredAccess) { + StringRef bridgingHeader) { llvm::PrettyStackTraceString trace("While generating Objective-C header"); SmallPtrSet imports; std::string moduleContentsBuf; llvm::raw_string_ostream moduleContents{moduleContentsBuf}; - printModuleContentsAsObjC(moduleContents, imports, *M, minRequiredAccess); + printModuleContentsAsObjC(moduleContents, imports, *M); std::string macroGuard = (llvm::Twine(M->getNameStr().upper()) + "_SWIFT_H").str(); writePrologue(os, M->getASTContext(), macroGuard); writeImports(os, imports, *M, bridgingHeader); diff --git a/lib/SIL/CMakeLists.txt b/lib/SIL/CMakeLists.txt index 96a6b852ad8b0..26bce079deb77 100644 --- a/lib/SIL/CMakeLists.txt +++ b/lib/SIL/CMakeLists.txt @@ -1,4 +1,3 @@ -set_swift_llvm_is_available() add_swift_host_library(swiftSIL STATIC SIL.cpp) target_link_libraries(swiftSIL PUBLIC @@ -17,3 +16,5 @@ add_subdirectory(Parser) # headers. # For more information see the comment at the top of lib/CMakeLists.txt. add_dependencies(swiftSIL intrinsics_gen clang-tablegen-targets) + +set_swift_llvm_is_available(swiftSIL) diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index a7ca113e9bfab..3e07a4e60ad35 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -322,8 +322,6 @@ bool AbstractionPattern::matchesTuple(CanTupleType substType) { auto type = getType(); if (auto tuple = dyn_cast(type)) return (tuple->getNumElements() == substType->getNumElements()); - if (isa(type)) - return true; return false; } } @@ -576,12 +574,14 @@ AbstractionPattern AbstractionPattern::getFunctionResultType() const { // If there's a single argument, abstract it according to its formal type // in the ObjC signature. unsigned callbackResultIndex = 0; - if (callbackErrorIndex && callbackResultIndex >= *callbackErrorIndex) - ++callbackResultIndex; - if (callbackErrorFlagIndex - && callbackResultIndex >= *callbackErrorFlagIndex) - ++callbackResultIndex; - + for (auto index : indices(callbackParamTy->getParamTypes())) { + if (callbackErrorIndex && index == *callbackErrorIndex) + continue; + if (callbackErrorFlagIndex && index == *callbackErrorFlagIndex) + continue; + callbackResultIndex = index; + break; + } auto clangResultType = callbackParamTy ->getParamType(callbackResultIndex) .getTypePtr(); @@ -632,7 +632,7 @@ AbstractionPattern::getObjCMethodAsyncCompletionHandlerType( if (auto origSig = getGenericSignature()) { patternSig = origSig; } else if (auto genFnTy = dyn_cast(getType())) { - patternSig = genFnTy->getGenericSignature()->getCanonicalSignature(); + patternSig = genFnTy->getGenericSignature().getCanonicalSignature(); } return AbstractionPattern(patternSig, @@ -661,6 +661,31 @@ AbstractionPattern::getObjCMethodAsyncCompletionHandlerType( case Kind::ObjCCompletionHandlerArgumentsType: swift_unreachable("not appropriate for this kind"); } + llvm_unreachable("covered switch"); +} + + +CanType AbstractionPattern::getObjCMethodAsyncCompletionHandlerForeignType( + ForeignAsyncConvention convention, + Lowering::TypeConverter &TC +) const { + auto nativeCHTy = convention.completionHandlerType(); + + // Use the abstraction pattern we're lowering against in order to lower + // the completion handler type, so we can preserve C/ObjC distinctions that + // normally get abstracted away by the importer. + auto completionHandlerNativeOrigTy = getObjCMethodAsyncCompletionHandlerType(nativeCHTy); + + // Bridge the Swift completion handler type back to its + // foreign representation. + auto foreignCHTy = TC.getLoweredBridgedType(completionHandlerNativeOrigTy, + nativeCHTy, + Bridgeability::Full, + SILFunctionTypeRepresentation::ObjCMethod, + TypeConverter::ForArgument) + ->getCanonicalType(); + + return foreignCHTy; } AbstractionPattern @@ -872,9 +897,7 @@ AbstractionPattern AbstractionPattern::getOptionalObjectType() const { return *this; case Kind::Type: - if (isTypeParameter()) - return AbstractionPattern::getOpaque(); - if (isa(getType())) + if (isTypeParameterOrOpaqueArchetype()) return AbstractionPattern::getOpaque(); return AbstractionPattern(getGenericSignature(), ::getOptionalObjectType(getType())); @@ -1138,13 +1161,12 @@ AbstractionPattern AbstractionPattern::unsafeGetSubstFieldType(ValueDecl *member, CanType origMemberInterfaceType) const { + assert(origMemberInterfaceType); if (isTypeParameterOrOpaqueArchetype()) { // Fall back to the generic abstraction pattern for the member. auto sig = member->getDeclContext()->getGenericSignatureOfContext(); - CanType memberTy = origMemberInterfaceType - ? origMemberInterfaceType - : member->getInterfaceType()->getCanonicalType(sig); - return AbstractionPattern(sig.getCanonicalSignature(), memberTy); + return AbstractionPattern(sig.getCanonicalSignature(), + origMemberInterfaceType); } switch (getKind()) { diff --git a/lib/SIL/IR/CMakeLists.txt b/lib/SIL/IR/CMakeLists.txt index 394ceb88544bf..5351b43c6f096 100644 --- a/lib/SIL/IR/CMakeLists.txt +++ b/lib/SIL/IR/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources(swiftSIL PRIVATE SILBuilder.cpp SILConstants.cpp SILCoverageMap.cpp + SILDebugInfoExpression.cpp SILDebugScope.cpp SILDeclRef.cpp SILDefaultWitnessTable.cpp diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index b05300cf5e643..aaa7de9e07d1e 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -19,6 +19,16 @@ using namespace swift; +/// Return true if all OperandOwnership invariants hold. +bool swift::checkOperandOwnershipInvariants(const Operand *operand) { + OperandOwnership opOwnership = operand->getOperandOwnership(); + if (opOwnership == OperandOwnership::Borrow) { + // Must be a valid BorrowingOperand. + return bool(BorrowingOperand(const_cast(operand))); + } + return true; +} + //===----------------------------------------------------------------------===// // OperandOwnershipClassifier //===----------------------------------------------------------------------===// @@ -133,7 +143,6 @@ OPERAND_OWNERSHIP(TrivialUse, CondBranch) OPERAND_OWNERSHIP(TrivialUse, CondFail) OPERAND_OWNERSHIP(TrivialUse, CopyAddr) OPERAND_OWNERSHIP(TrivialUse, DeallocStack) -OPERAND_OWNERSHIP(TrivialUse, DebugValueAddr) OPERAND_OWNERSHIP(TrivialUse, DeinitExistentialAddr) OPERAND_OWNERSHIP(TrivialUse, DestroyAddr) OPERAND_OWNERSHIP(TrivialUse, EndAccess) @@ -431,6 +440,7 @@ static OperandOwnership getFunctionArgOwnership(SILArgumentConvention argConv, case SILArgumentConvention::Indirect_InoutAliasable: llvm_unreachable("Illegal convention for non-address types"); } + llvm_unreachable("covered switch"); } OperandOwnership @@ -511,6 +521,7 @@ OperandOwnership OperandOwnershipClassifier::visitReturnInst(ReturnInst *i) { case OwnershipKind::Owned: return OperandOwnership::ForwardingConsume; } + llvm_unreachable("covered switch"); } OperandOwnership OperandOwnershipClassifier::visitAssignInst(AssignInst *i) { @@ -747,17 +758,22 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IntInstrprofIncrement) BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLet) BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, EndAsyncLet) +BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLetWithLocalBuffer) +BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, EndAsyncLetLifetime) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroup) BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DestroyTaskGroup) BUILTIN_OPERAND_OWNERSHIP(ForwardingConsume, COWBufferForReading) BUILTIN_OPERAND_OWNERSHIP(ForwardingConsume, UnsafeGuaranteed) +const int PARAMETER_INDEX_CREATE_ASYNC_TASK_FUTURE_FUNCTION = 2; +const int PARAMETER_INDEX_CREATE_ASYNC_TASK_GROUP_FUTURE_FUNCTION = 3; + OperandOwnership -OperandOwnershipBuiltinClassifier::visitCreateAsyncTaskFuture(BuiltinInst *bi, - StringRef attr) { +OperandOwnershipBuiltinClassifier::visitCreateAsyncTask(BuiltinInst *bi, + StringRef attr) { // The function operand is consumed by the new task. - if (&op == &bi->getOperandRef(2)) + if (&op == &bi->getOperandRef(PARAMETER_INDEX_CREATE_ASYNC_TASK_FUTURE_FUNCTION)) return OperandOwnership::DestroyingConsume; // FIXME: These are considered InteriorPointer because they may propagate a @@ -768,10 +784,10 @@ OperandOwnershipBuiltinClassifier::visitCreateAsyncTaskFuture(BuiltinInst *bi, } OperandOwnership -OperandOwnershipBuiltinClassifier::visitCreateAsyncTaskGroupFuture(BuiltinInst *bi, - StringRef attr) { +OperandOwnershipBuiltinClassifier::visitCreateAsyncTaskInGroup(BuiltinInst *bi, + StringRef attr) { // The function operand is consumed by the new task. - if (&op == &bi->getOperandRef(3)) + if (&op == &bi->getOperandRef(PARAMETER_INDEX_CREATE_ASYNC_TASK_GROUP_FUTURE_FUNCTION)) return OperandOwnership::DestroyingConsume; // FIXME: These are considered InteriorPointer because they may propagate a @@ -812,6 +828,9 @@ BUILTIN_OPERAND_OWNERSHIP(InteriorPointer, CancelAsyncTask) BUILTIN_OPERAND_OWNERSHIP(InteriorPointer, InitializeDefaultActor) BUILTIN_OPERAND_OWNERSHIP(InteriorPointer, DestroyDefaultActor) +BUILTIN_OPERAND_OWNERSHIP(InteriorPointer, InitializeDistributedRemoteActor) +BUILTIN_OPERAND_OWNERSHIP(InteriorPointer, DestroyDistributedActor) + // FIXME: Why do these reqiuire a borrowed value at all? BUILTIN_OPERAND_OWNERSHIP(ForwardingBorrow, AutoDiffAllocateSubcontext) BUILTIN_OPERAND_OWNERSHIP(ForwardingBorrow, AutoDiffProjectTopLevelSubcontext) diff --git a/lib/SIL/IR/SIL.cpp b/lib/SIL/IR/SIL.cpp index e9c739af20805..0383a1461cc30 100644 --- a/lib/SIL/IR/SIL.cpp +++ b/lib/SIL/IR/SIL.cpp @@ -107,8 +107,14 @@ bool SILModule::isTypeMetadataAccessible(CanType type) { return !type.findIf([&](CanType type) { // Note that this function returns true if the type is *illegal* to use. - // Ignore non-nominal types. - auto decl = type.getNominalOrBoundGenericNominal(); + // Ignore non-nominal types -- except for opaque result types which can be + // private and in a different translation unit in which case they can't be + // accessed. + ValueDecl *decl = type.getNominalOrBoundGenericNominal(); + if (!decl) + decl = isa(type) + ? cast(type)->getDecl() + : nullptr; if (!decl) return false; @@ -141,6 +147,31 @@ bool SILModule::isTypeMetadataAccessible(CanType type) { }); } +/// Return the minimum linkage structurally required to reference the given formal type. +FormalLinkage swift::getTypeLinkage(CanType t) { + assert(t->isLegalFormalType()); + + class Walker : public TypeWalker { + public: + FormalLinkage Linkage; + Walker() : Linkage(FormalLinkage::PublicUnique) {} + + Action walkToTypePre(Type ty) override { + // Non-nominal types are always available. + auto decl = ty->getNominalOrBoundGenericNominal(); + if (!decl) + return Action::Continue; + + Linkage = std::min(Linkage, getDeclLinkage(decl)); + return Action::Continue; + } + }; + + Walker w; + t.walk(w); + return w.Linkage; +} + /// Answer whether IRGen's emitTypeMetadataForLayout can fetch metadata for /// a type, which is the necessary condition for being able to do value /// operations on the type using dynamic metadata. diff --git a/lib/SIL/IR/SILArgument.cpp b/lib/SIL/IR/SILArgument.cpp index 40f716b671921..9c42f0b2162c8 100644 --- a/lib/SIL/IR/SILArgument.cpp +++ b/lib/SIL/IR/SILArgument.cpp @@ -316,7 +316,7 @@ TermInst *SILPhiArgument::getSingleTerminator() const { return const_cast(predBlock)->getTerminator(); } -TermInst *SILPhiArgument::getTerminatorForResultArg() const { +TermInst *SILPhiArgument::getTerminatorForResult() const { if (auto *termInst = getSingleTerminator()) { if (!isa(termInst) && !isa(termInst)) return termInst; diff --git a/lib/SIL/IR/SILBasicBlock.cpp b/lib/SIL/IR/SILBasicBlock.cpp index 49de6f0604a84..a3ae02f01686d 100644 --- a/lib/SIL/IR/SILBasicBlock.cpp +++ b/lib/SIL/IR/SILBasicBlock.cpp @@ -19,6 +19,7 @@ #include "swift/SIL/DebugUtils.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILBuilder.h" +#include "swift/SIL/SILBridging.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILFunction.h" @@ -26,12 +27,21 @@ #include "swift/SIL/SILModule.h" #include "swift/SIL/SILUndef.h" #include "swift/Strings.h" + using namespace swift; //===----------------------------------------------------------------------===// // SILBasicBlock Implementation //===----------------------------------------------------------------------===// +SwiftMetatype SILBasicBlock::registeredMetatype; + +SILBasicBlock::SILBasicBlock() : + SwiftObjectHeader(registeredMetatype), Parent(nullptr) {} + +SILBasicBlock::SILBasicBlock(SILFunction *parent) : + SwiftObjectHeader(registeredMetatype), Parent(parent) {} + SILBasicBlock::~SILBasicBlock() { if (!getParent()) { assert(ArgumentList.empty() && @@ -41,35 +51,8 @@ SILBasicBlock::~SILBasicBlock() { return; } - SILModule &M = getModule(); - - // Invalidate all of the basic block arguments. - for (auto *Arg : ArgumentList) { - M.notifyDeleteHandlers(Arg); - } - dropAllReferences(); - - for (auto I = begin(), E = end(); I != E;) { - auto Inst = &*I; - ++I; - erase(Inst); - } - assert(InstList.empty()); -} - -void SILBasicBlock::clearStaticInitializerBlock(SILModule &module) { - assert(!getParent() && "not a global variable's static initializer block"); - assert(ArgumentList.empty() && - "a static initializer block must not have arguments"); - - for (auto I = begin(), E = end(); I != E;) { - SILInstruction *Inst = &*I; - ++I; - InstList.erase(Inst); - module.deallocateInst(Inst); - } - assert(InstList.empty()); + eraseAllInstructions(getModule()); } int SILBasicBlock::getDebugID() const { @@ -104,22 +87,21 @@ void SILBasicBlock::remove(SILInstruction *I) { InstList.remove(I); } -void SILBasicBlock::eraseInstructions() { - for (auto It = begin(); It != end();) { - auto *Inst = &*It++; - Inst->replaceAllUsesOfAllResultsWithUndef(); - Inst->eraseFromParent(); +void SILBasicBlock::eraseAllInstructions(SILModule &module) { + while (!empty()) { + erase(&*begin(), module); } } /// Returns the iterator following the erased instruction. -SILBasicBlock::iterator SILBasicBlock::erase(SILInstruction *I) { - // Notify the delete handlers that this instruction is going away. - SILModule &module = getModule(); - module.notifyDeleteHandlers(I->asSILNode()); - auto nextIter = InstList.erase(I); - module.deallocateInst(I); - return nextIter; +void SILBasicBlock::erase(SILInstruction *I) { + erase(I, getModule()); +} + +void SILBasicBlock::erase(SILInstruction *I, SILModule &module) { + module.willDeleteInstruction(I); + InstList.remove(I); + module.scheduleForDeletion(I); } /// This method unlinks 'self' from the containing SILFunction and deletes it. @@ -186,9 +168,6 @@ SILFunctionArgument *SILBasicBlock::replaceFunctionArgument( assert(ArgumentList[i]->use_empty() && "Expected no uses of the old arg!"); - // Notify the delete handlers that this argument is being deleted. - M.notifyDeleteHandlers(ArgumentList[i]); - SILFunctionArgument *NewArg = new (M) SILFunctionArgument(Ty, Kind, D); NewArg->setParent(this); @@ -212,9 +191,6 @@ SILPhiArgument *SILBasicBlock::replacePhiArgument(unsigned i, SILType Ty, assert(ArgumentList[i]->use_empty() && "Expected no uses of the old BB arg!"); - // Notify the delete handlers that this argument is being deleted. - M.notifyDeleteHandlers(ArgumentList[i]); - SILPhiArgument *NewArg = new (M) SILPhiArgument(Ty, Kind, D); NewArg->setParent(this); @@ -274,8 +250,6 @@ SILPhiArgument *SILBasicBlock::insertPhiArgument(unsigned AtArgPos, SILType Ty, void SILBasicBlock::eraseArgument(int Index) { assert(getArgument(Index)->use_empty() && "Erasing block argument that has uses!"); - // Notify the delete handlers that this BB argument is going away. - getModule().notifyDeleteHandlers(getArgument(Index)); ArgumentList.erase(ArgumentList.begin() + Index); } @@ -318,8 +292,15 @@ transferNodesFromList(llvm::ilist_traits &SrcTraits, First->Parent = Parent; First->index = -1; First->lastInitializedBitfieldID = 0; - for (auto &II : *First) + for (auto &II : *First) { II.setDebugScope(ScopeCloner.getOrCreateClonedScope(II.getDebugScope())); + // Special handling for SILDebugVariable. + if (auto DVI = DebugVarCarryingInst(&II)) + if (auto VarInfo = DVI.getVarInfo()) + if (VarInfo->Scope) + DVI.setDebugVarScope( + ScopeCloner.getOrCreateClonedScope(VarInfo->Scope)); + } } } diff --git a/lib/SIL/IR/SILBuilder.cpp b/lib/SIL/IR/SILBuilder.cpp index 66cb8701d546f..5d8b5071b01b1 100644 --- a/lib/SIL/IR/SILBuilder.cpp +++ b/lib/SIL/IR/SILBuilder.cpp @@ -569,7 +569,6 @@ void SILBuilder::emitDestructureValueOperation( DebugValueInst *SILBuilder::createDebugValue(SILLocation Loc, SILValue src, SILDebugVariable Var, bool poisonRefs) { - assert(isLoadableOrOpaque(src->getType())); // Debug location overrides cannot apply to debug value instructions. DebugLocOverrideRAII LocOverride{*this, None}; return insert( @@ -577,12 +576,12 @@ DebugValueInst *SILBuilder::createDebugValue(SILLocation Loc, SILValue src, poisonRefs)); } -DebugValueAddrInst *SILBuilder::createDebugValueAddr(SILLocation Loc, - SILValue src, - SILDebugVariable Var) { +DebugValueInst *SILBuilder::createDebugValueAddr(SILLocation Loc, + SILValue src, + SILDebugVariable Var) { // Debug location overrides cannot apply to debug addr instructions. DebugLocOverrideRAII LocOverride{*this, None}; - return insert(DebugValueAddrInst::create(getSILDebugLocation(Loc), src, + return insert(DebugValueInst::createAddr(getSILDebugLocation(Loc), src, getModule(), Var)); } diff --git a/lib/SIL/IR/SILDebugInfoExpression.cpp b/lib/SIL/IR/SILDebugInfoExpression.cpp new file mode 100644 index 0000000000000..25cd17737d09e --- /dev/null +++ b/lib/SIL/IR/SILDebugInfoExpression.cpp @@ -0,0 +1,67 @@ +//===-------- SILDebugInfoExpression.cpp - DIExpression for SIL -----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the table used by SILDIExprInfo to map from +/// SILDIExprOperator to suppliment information like the operator string. +/// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/SILDebugInfoExpression.h" +#include + +using namespace swift; + +namespace std { +// This is, unfortunately, a workaround for an ancient bug in libstdc++: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 +// Which prevented std::hash users from having drop-in support for +// enum class types. +template <> struct hash { + size_t operator()(SILDIExprOperator V) const noexcept { + return std::hash{}(static_cast(V)); + } +}; +} // end namespace std + +const SILDIExprInfo *SILDIExprInfo::get(SILDIExprOperator Op) { + static const std::unordered_map Infos = { + {SILDIExprOperator::Fragment, + {"op_fragment", {SILDIExprElement::DeclKind}}}, + {SILDIExprOperator::Dereference, {"op_deref", {}}}}; + + return Infos.count(Op) ? &Infos.at(Op) : nullptr; +} + +void SILDebugInfoExpression::op_iterator::increment() { + if (Remain.empty()) { + // Effectively making this an end iterator + Current = {}; + return; + } + + const SILDIExprElement &NextHead = Remain[0]; + SILDIExprOperator Op = NextHead.getAsOperator(); + if (const auto *ExprInfo = SILDIExprInfo::get(Op)) { + auto NewSlice = Remain.take_front(ExprInfo->OperandKinds.size() + 1); + Current = SILDIExprOperand(NewSlice.data(), NewSlice.size()); + if (Remain.size() >= Current.size()) + Remain = Remain.drop_front(Current.size()); + } +} + +SILDebugInfoExpression SILDebugInfoExpression::createFragment(VarDecl *Field) { + assert(Field); + return SILDebugInfoExpression( + {SILDIExprElement::createOperator(SILDIExprOperator::Fragment), + SILDIExprElement::createDecl(Field)}); +} diff --git a/lib/SIL/IR/SILDebugScope.cpp b/lib/SIL/IR/SILDebugScope.cpp index 2c937812a48fc..9212d4991ccd2 100644 --- a/lib/SIL/IR/SILDebugScope.cpp +++ b/lib/SIL/IR/SILDebugScope.cpp @@ -57,8 +57,8 @@ SILFunction *SILDebugScope::getParentFunction() const { } /// Determine whether an instruction may not have a SILDebugScope. -bool swift::maybeScopeless(SILInstruction &I) { - if (I.getFunction()->isBare()) +bool swift::maybeScopeless(const SILInstruction &inst) { + if (inst.getFunction()->isBare()) return true; - return !isa(I) && !isa(I); + return !isa(inst); } diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 744c14ad83206..ba68115e8e92c 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -16,6 +16,7 @@ #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/SourceFile.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/SILLinkage.h" @@ -38,6 +39,9 @@ swift::getMethodDispatch(AbstractFunctionDecl *method) { if (method->hasForcedStaticDispatch()) return MethodDispatch::Static; + if (method->getAttrs().hasAttribute()) + return MethodDispatch::Static; + // Import-as-member declarations are always statically referenced. if (method->isImportAsMember()) return MethodDispatch::Static; @@ -117,12 +121,13 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) { return false; } -SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign, +SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, + bool isForeign, bool isDistributed, AutoDiffDerivativeFunctionIdentifier *derivativeId) - : loc(vd), kind(kind), isForeign(isForeign), defaultArgIndex(0), - pointer(derivativeId) {} + : loc(vd), kind(kind), isForeign(isForeign), isDistributed(isDistributed), + defaultArgIndex(0), pointer(derivativeId) {} -SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign) +SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign, bool asDistributed) : defaultArgIndex(0), pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr) { if (auto *vd = baseLoc.dyn_cast()) { @@ -161,27 +166,43 @@ SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign) } isForeign = asForeign; + isDistributed = asDistributed; } SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, GenericSignature prespecializedSig) - : SILDeclRef(baseLoc, false) { + : SILDeclRef(baseLoc, false, false) { pointer = prespecializedSig.getPointer(); } Optional SILDeclRef::getAnyFunctionRef() const { - if (auto vd = loc.dyn_cast()) { - if (auto afd = dyn_cast(vd)) { + switch (getLocKind()) { + case LocKind::Decl: + if (auto *afd = getAbstractFunctionDecl()) return AnyFunctionRef(afd); - } else { - return None; - } + return None; + case LocKind::Closure: + return AnyFunctionRef(getAbstractClosureExpr()); + case LocKind::File: + return None; + } + llvm_unreachable("Unhandled case in switch"); +} + +ASTContext &SILDeclRef::getASTContext() const { + switch (getLocKind()) { + case LocKind::Decl: + return getDecl()->getASTContext(); + case LocKind::Closure: + return getAbstractClosureExpr()->getASTContext(); + case LocKind::File: + return getFileUnit()->getASTContext(); } - return AnyFunctionRef(loc.get()); + llvm_unreachable("Unhandled case in switch"); } bool SILDeclRef::isThunk() const { - return isForeignToNativeThunk() || isNativeToForeignThunk(); + return isForeignToNativeThunk() || isNativeToForeignThunk() || isDistributedThunk(); } bool SILDeclRef::isClangImported() const { @@ -226,9 +247,16 @@ bool SILDeclRef::isClangGenerated(ClangNode node) { } bool SILDeclRef::isImplicit() const { - if (hasDecl()) + switch (getLocKind()) { + case LocKind::Decl: return getDecl()->isImplicit(); - return getAbstractClosureExpr()->isImplicit(); + case LocKind::Closure: + return getAbstractClosureExpr()->isImplicit(); + case LocKind::File: + // Files are currently never considered implicit. + return false; + } + llvm_unreachable("Unhandled case in switch"); } SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { @@ -242,6 +270,10 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { return isSerialized() ? SILLinkage::Shared : SILLinkage::Private; } + // The main entry-point is public. + if (kind == Kind::EntryPoint) + return SILLinkage::Public; + // Add External to the linkage (e.g. Public -> PublicExternal) if this is a // declaration not a definition. auto maybeAddExternal = [&](SILLinkage linkage) { @@ -367,12 +399,12 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { auto effectiveAccess = d->getEffectiveAccess(); // Private setter implementations for an internal storage declaration should - // be internal as well, so that a dynamically-writable - // keypath can be formed from other files. + // be at least internal as well, so that a dynamically-writable + // keypath can be formed from other files in the same module. if (auto accessor = dyn_cast(d)) { if (accessor->isSetter() - && accessor->getStorage()->getEffectiveAccess() == AccessLevel::Internal) - effectiveAccess = AccessLevel::Internal; + && accessor->getStorage()->getEffectiveAccess() >= AccessLevel::Internal) + effectiveAccess = std::max(effectiveAccess, AccessLevel::Internal); } switch (effectiveAccess) { @@ -407,6 +439,23 @@ SILDeclRef SILDeclRef::getDefaultArgGenerator(Loc loc, return result; } +SILDeclRef SILDeclRef::getMainDeclEntryPoint(ValueDecl *decl) { + auto *file = cast(decl->getDeclContext()->getModuleScopeContext()); + assert(file->getMainDecl() == decl); + SILDeclRef result; + result.loc = decl; + result.kind = Kind::EntryPoint; + return result; +} + +SILDeclRef SILDeclRef::getMainFileEntryPoint(FileUnit *file) { + assert(file->hasEntryPoint() && !file->getMainDecl()); + SILDeclRef result; + result.loc = file; + result.kind = Kind::EntryPoint; + return result; +} + bool SILDeclRef::hasClosureExpr() const { return loc.is() && isa(getAbstractClosureExpr()); @@ -490,6 +539,9 @@ IsSerialized_t SILDeclRef::isSerialized() const { return IsNotSerialized; } + if (kind == Kind::EntryPoint) + return IsNotSerialized; + if (isIVarInitializerOrDestroyer()) return IsNotSerialized; @@ -655,6 +707,12 @@ EffectsKind SILDeclRef::getEffectsAttribute() const { return MA->getKind(); } +bool SILDeclRef::isAnyThunk() const { + return isForeignToNativeThunk() || + isNativeToForeignThunk() || + isDistributedThunk(); +} + bool SILDeclRef::isForeignToNativeThunk() const { // If this isn't a native entry-point, it's not a foreign-to-native thunk. if (isForeign) @@ -681,17 +739,29 @@ bool SILDeclRef::isNativeToForeignThunk() const { if (!isForeign) return false; - // We can have native-to-foreign thunks over closures. - if (!hasDecl()) - return true; + switch (getLocKind()) { + case LocKind::Decl: + // A decl with a clang node doesn't have a native entry-point to forward + // onto. + if (getDecl()->hasClangNode()) + return false; - // A decl with a clang node doesn't have a native entry-point to forward onto. - if (getDecl()->hasClangNode()) + // Only certain kinds of SILDeclRef can expose native-to-foreign thunks. + return kind == Kind::Func || kind == Kind::Initializer || + kind == Kind::Deallocator; + case LocKind::Closure: + // We can have native-to-foreign thunks over closures. + return true; + case LocKind::File: return false; + } + llvm_unreachable("Unhandled case in switch"); +} - // Only certain kinds of SILDeclRef can expose native-to-foreign thunks. - return kind == Kind::Func || kind == Kind::Initializer || - kind == Kind::Deallocator; +bool SILDeclRef::isDistributedThunk() const { + if (!isDistributed) + return false; + return kind == Kind::Func; } /// Use the Clang importer to mangle a Clang declaration. @@ -772,27 +842,25 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { SKind = ASTMangler::SymbolKind::SwiftAsObjCThunk; } else if (isForeignToNativeThunk()) { SKind = ASTMangler::SymbolKind::ObjCAsSwiftThunk; + } else if (isDistributedThunk()) { + SKind = ASTMangler::SymbolKind::DistributedThunk; } break; case SILDeclRef::ManglingKind::DynamicThunk: SKind = ASTMangler::SymbolKind::DynamicThunk; break; - case SILDeclRef::ManglingKind::AsyncHandlerBody: - SKind = ASTMangler::SymbolKind::AsyncHandlerBody; - break; } switch (kind) { case SILDeclRef::Kind::Func: - if (!hasDecl()) - return mangler.mangleClosureEntity(getAbstractClosureExpr(), SKind); + if (auto *ACE = getAbstractClosureExpr()) + return mangler.mangleClosureEntity(ACE, SKind); // As a special case, functions can have manually mangled names. // Use the SILGen name only for the original non-thunked, non-curried entry // point. if (auto NameA = getDecl()->getAttrs().getAttribute()) - if (!NameA->Name.empty() && - !isForeignToNativeThunk() && !isNativeToForeignThunk()) { + if (!NameA->Name.empty() && !isAnyThunk()) { return NameA->Name.str(); } @@ -856,6 +924,10 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return mangler.mangleInitFromProjectedValueEntity(cast(getDecl()), SKind); + + case SILDeclRef::Kind::EntryPoint: { + return getASTContext().getEntryPointFunctionName(); + } } llvm_unreachable("bad entity kind!"); @@ -898,6 +970,8 @@ bool SILDeclRef::requiresNewVTableEntry() const { return true; if (!hasDecl()) return false; + if (isDistributedThunk()) + return false; auto fnDecl = dyn_cast(getDecl()); if (!fnDecl) return false; @@ -930,6 +1004,10 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const { // @NSManaged property, then it won't be in the vtable. if (overridden.getDecl()->hasClangNode()) return SILDeclRef(); + + // Distributed thunks are not in the vtable. + if (isDistributedThunk()) + return SILDeclRef(); // An @objc convenience initializer can be "overridden" in the sense that // its selector is reclaimed by a subclass's convenience init with the @@ -1059,9 +1137,15 @@ SILDeclRef SILDeclRef::getOverriddenVTableEntry() const { } SILLocation SILDeclRef::getAsRegularLocation() const { - if (hasDecl()) + switch (getLocKind()) { + case LocKind::Decl: return RegularLocation(getDecl()); - return RegularLocation(getAbstractClosureExpr()); + case LocKind::Closure: + return RegularLocation(getAbstractClosureExpr()); + case LocKind::File: + return RegularLocation::getModuleLocation(); + } + llvm_unreachable("Unhandled case in switch"); } SubclassScope SILDeclRef::getSubclassScope() const { @@ -1168,7 +1252,12 @@ SubclassScope SILDeclRef::getSubclassScope() const { } unsigned SILDeclRef::getParameterListCount() const { - if (!hasDecl() || kind == Kind::DefaultArgGenerator) + // Only decls can introduce currying. + if (!hasDecl()) + return 1; + + // Always uncurried even if the underlying function is curried. + if (kind == Kind::DefaultArgGenerator || kind == Kind::EntryPoint) return 1; auto *vd = getDecl(); @@ -1198,6 +1287,8 @@ bool SILDeclRef::canBeDynamicReplacement() const { // generic class can't be a dynamic replacement. if (isForeign && hasDecl() && getDecl()->isNativeMethodReplacement()) return false; + if (isDistributedThunk()) + return false; if (kind == SILDeclRef::Kind::Destroyer || kind == SILDeclRef::Kind::DefaultArgGenerator) return false; @@ -1214,6 +1305,9 @@ bool SILDeclRef::isDynamicallyReplaceable() const { if (!isForeign && hasDecl() && getDecl()->isNativeMethodReplacement()) return false; + if (isDistributedThunk()) + return false; + if (kind == SILDeclRef::Kind::DefaultArgGenerator) return false; if (isStoredPropertyInitializer() || isPropertyWrapperBackingInitializer()) @@ -1249,6 +1343,9 @@ bool SILDeclRef::isDynamicallyReplaceable() const { } bool SILDeclRef::hasAsync() const { + if (isDistributedThunk()) + return true; + if (hasDecl()) { if (auto afd = dyn_cast(getDecl())) { return afd->hasAsync(); diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index a87ea09451cf6..d273cf3feac3d 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -14,6 +14,7 @@ #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILBridging.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" @@ -124,6 +125,8 @@ SILFunction::create(SILModule &M, SILLinkage linkage, StringRef name, return fn; } +SwiftMetatype SILFunction::registeredMetatype; + SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name, CanSILFunctionType LoweredType, GenericEnvironment *genericEnv, @@ -135,7 +138,8 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name, const SILDebugScope *DebugScope, IsDynamicallyReplaceable_t isDynamic, IsExactSelfClass_t isExactSelfClass) - : Module(Module), Availability(AvailabilityContext::alwaysAvailable()) { + : SwiftObjectHeader(registeredMetatype), + Module(Module), Availability(AvailabilityContext::alwaysAvailable()) { init(Linkage, Name, LoweredType, genericEnv, Loc, isBareSILFunction, isTrans, isSerialized, entryCount, isThunk, classSubclassScope, inlineStrategy, E, DebugScope, isDynamic, isExactSelfClass); @@ -178,6 +182,7 @@ void SILFunction::init(SILLinkage Linkage, StringRef Name, this->Zombie = false; this->HasOwnership = true, this->WasDeserializedCanonical = false; + this->IsStaticallyLinked = false; this->IsWithoutActuallyEscapingThunk = false; this->OptMode = unsigned(OptimizationMode::NotSet); this->EffectsKindAttr = unsigned(E); @@ -201,16 +206,7 @@ SILFunction::~SILFunction() { auto &M = getModule(); for (auto &BB : *this) { - for (auto I = BB.begin(), E = BB.end(); I != E;) { - auto Inst = &*I; - ++I; - SILInstruction::destroy(Inst); - // TODO: It is only safe to directly deallocate an - // instruction if this BB is being removed in scope - // of destructing a SILFunction. - M.deallocateInst(Inst); - } - BB.InstList.clearAndLeakNodesUnsafely(); + BB.eraseAllInstructions(M); } assert(RefCount == 0 && @@ -689,6 +685,10 @@ bool SILFunction::isExternallyUsedSymbol() const { void SILFunction::clear() { dropAllReferences(); + eraseAllBlocks(); +} + +void SILFunction::eraseAllBlocks() { BlockList.clear(); } diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index cf011b75abcbe..192f6a62be8be 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -11,8 +11,11 @@ //===----------------------------------------------------------------------===// #include "swift/SIL/SILFunctionBuilder.h" +#include "swift/AST/AttrKind.h" #include "swift/AST/Availability.h" #include "swift/AST/Decl.h" +#include "swift/AST/SemanticAttrs.h" + using namespace swift; SILFunction *SILFunctionBuilder::getOrCreateFunction( @@ -45,6 +48,13 @@ void SILFunctionBuilder::addFunctionAttributes( for (auto *A : Attrs.getAttributes()) F->addSemanticsAttr(cast(A)->Value); + // If we are asked to emit assembly vision remarks for this function, mark the + // function as force emitting all optremarks including assembly vision + // remarks. This allows us to emit the assembly vision remarks without needing + // to change any of the underlying optremark mechanisms. + if (auto *A = Attrs.getAttribute(DAK_EmitAssemblyVisionRemarks)) + F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX); + // Propagate @_specialize. for (auto *A : Attrs.getAttributes()) { auto *SA = cast(A); diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 68e720b306581..4d83f23e63e04 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -23,7 +23,6 @@ #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/ForeignInfo.h" #include "swift/AST/GenericEnvironment.h" -#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/ProtocolConformance.h" @@ -1300,7 +1299,7 @@ class SubstFunctionTypeCollector { // Extract structural substitutions. if (origContextType->hasTypeParameter()) { - origContextType = origSig->getGenericEnvironment() + origContextType = origSig.getGenericEnvironment() ->mapTypeIntoContext(origContextType) ->getCanonicalType(); } @@ -1558,11 +1557,11 @@ class DestructureInputs { unsigned numEltTypes = params.size(); bool hasSelf = - (extInfoBuilder.hasSelfParam() || Foreign.Self.isImportAsMember()); + (extInfoBuilder.hasSelfParam() || Foreign.self.isImportAsMember()); unsigned numNonSelfParams = (hasSelf ? numEltTypes - 1 : numEltTypes); TopLevelOrigType = origType; // If we have a foreign-self, install handleSelf as the handler. - if (Foreign.Self.isInstance()) { + if (Foreign.self.isInstance()) { assert(hasSelf && numEltTypes > 0); ForeignSelf = ForeignSelfInfo{origType.getFunctionParamType(numNonSelfParams), params[numNonSelfParams]}; @@ -1583,7 +1582,7 @@ class DestructureInputs { // Process the self parameter. Note that we implicitly drop self // if this is a static foreign-self import. - if (hasSelf && !Foreign.Self.isImportAsMember()) { + if (hasSelf && !Foreign.self.isImportAsMember()) { auto selfParam = params[numNonSelfParams]; auto ty = selfParam.getParameterType(); auto eltPattern = origType.getFunctionParamType(numNonSelfParams); @@ -1671,29 +1670,14 @@ class DestructureInputs { } bool maybeAddForeignAsyncParameter() { - if (!Foreign.Async - || NextOrigParamIndex != Foreign.Async->completionHandlerParamIndex()) + if (!Foreign.async || + NextOrigParamIndex != Foreign.async->completionHandlerParamIndex()) return false; - - auto nativeCHTy = Foreign.Async->completionHandlerType(); - // Use the abstraction pattern we're lowering against in order to lower - // the completion handler type, so we can preserve C/ObjC distinctions that - // normally get abstracted away by the importer. - auto completionHandlerNativeOrigTy = TopLevelOrigType - .getObjCMethodAsyncCompletionHandlerType(nativeCHTy); - - // Bridge the Swift completion handler type back to its - // foreign representation. - auto foreignCHTy = TC.getLoweredBridgedType(completionHandlerNativeOrigTy, - nativeCHTy, - Bridgeability::Full, - SILFunctionTypeRepresentation::ObjCMethod, - TypeConverter::ForArgument) - ->getCanonicalType(); - - auto completionHandlerOrigTy = TopLevelOrigType - .getObjCMethodAsyncCompletionHandlerType(foreignCHTy); + CanType foreignCHTy = + TopLevelOrigType.getObjCMethodAsyncCompletionHandlerForeignType( + Foreign.async.getValue(), TC); + auto completionHandlerOrigTy = TopLevelOrigType.getObjCMethodAsyncCompletionHandlerType(foreignCHTy); auto completionHandlerTy = TC.getLoweredType(completionHandlerOrigTy, foreignCHTy, expansion) .getASTType(); @@ -1704,17 +1688,12 @@ class DestructureInputs { } bool maybeAddForeignErrorParameter() { - // A foreign async convention absorbs any error parameter, making it into - // an argument to the callback. - if (Foreign.Async) - return false; - - if (!Foreign.Error || - NextOrigParamIndex != Foreign.Error->getErrorParameterIndex()) + if (!Foreign.error || + NextOrigParamIndex != Foreign.error->getErrorParameterIndex()) return false; auto foreignErrorTy = TC.getLoweredRValueType( - expansion, Foreign.Error->getErrorParameterType()); + expansion, Foreign.error->getErrorParameterType()); // Assume the error parameter doesn't have interesting lowering. Inputs.push_back(SILParameterInfo(foreignErrorTy, @@ -1724,8 +1703,8 @@ class DestructureInputs { } bool maybeAddForeignSelfParameter() { - if (!Foreign.Self.isInstance() || - NextOrigParamIndex != Foreign.Self.getSelfIndex()) + if (!Foreign.self.isInstance() || + NextOrigParamIndex != Foreign.self.getSelfIndex()) return false; if (ForeignSelf) { @@ -1767,7 +1746,7 @@ static bool isPseudogeneric(SILDeclRef c) { if (!dc) return false; auto classDecl = dc->getSelfClassDecl(); - return (classDecl && classDecl->usesObjCGenericsModel()); + return (classDecl && classDecl->isTypeErasedGenericClass()); } /// Update the result type given the foreign error convention that we will be @@ -1776,27 +1755,27 @@ void updateResultTypeForForeignInfo( const ForeignInfo &foreignInfo, CanGenericSignature genericSig, AbstractionPattern &origResultType, CanType &substFormalResultType) { // If there's no error or async convention, the return type is unchanged. - if (!foreignInfo.Async && !foreignInfo.Error) { + if (!foreignInfo.async && !foreignInfo.error) { return; } - - // A foreign async convention means our lowered return type is Void, since - // the imported semantic return and/or error type map to the completion - // callback's argument(s). - auto &C = substFormalResultType->getASTContext(); - if (auto async = foreignInfo.Async) { + + // A foreign async convention without an error convention means our lowered + // return type is Void, since the imported semantic return map to the + // completion callback's argument(s). + if (!foreignInfo.error) { + auto &C = substFormalResultType->getASTContext(); substFormalResultType = TupleType::getEmpty(C); origResultType = AbstractionPattern(genericSig, substFormalResultType); return; } - + // Otherwise, adjust the return type to match the foreign error convention. - auto convention = *foreignInfo.Error; + auto convention = *foreignInfo.error; switch (convention.getKind()) { // These conventions replace the result type. case ForeignErrorConvention::ZeroResult: case ForeignErrorConvention::NonZeroResult: - assert(substFormalResultType->isVoid()); + assert(substFormalResultType->isVoid() || foreignInfo.async); substFormalResultType = convention.getResultType(); origResultType = AbstractionPattern(genericSig, substFormalResultType); return; @@ -1988,6 +1967,7 @@ static void destructureYieldsForCoroutine(TypeConverter &TC, Optional origConstant, Optional constant, Optional reqtSubs, + Optional genericSig, SmallVectorImpl &yields, SILCoroutineKind &coroutineKind, SubstFunctionTypeCollector &subst) { @@ -2012,12 +1992,14 @@ static void destructureYieldsForCoroutine(TypeConverter &TC, auto storage = accessor->getStorage(); auto valueType = storage->getValueInterfaceType(); + if (reqtSubs) { valueType = valueType.subst(*reqtSubs); } - auto canValueType = valueType->getCanonicalType( - accessor->getGenericSignature()); + auto canValueType = (genericSig + ? valueType->getCanonicalType(*genericSig) + : valueType->getCanonicalType()); // 'modify' yields an inout of the target type. if (accessor->getAccessorKind() == AccessorKind::Modify) { @@ -2100,27 +2082,25 @@ static CanSILFunctionType getSILFunctionType( Optional errorResult; assert( - (!foreignInfo.Error || substFnInterfaceType->getExtInfo().isThrowing()) - && "foreignError was set but function type does not throw?"); - assert( - (!foreignInfo.Async || substFnInterfaceType->getExtInfo().isAsync()) - && "foreignAsync was set but function type is not async?"); + (!foreignInfo.error || substFnInterfaceType->getExtInfo().isThrowing()) && + "foreignError was set but function type does not throw?"); + assert((!foreignInfo.async || substFnInterfaceType->getExtInfo().isAsync()) && + "foreignAsync was set but function type is not async?"); // Map '@Sendable' to the appropriate `@Sendable` modifier. bool isSendable = substFnInterfaceType->getExtInfo().isSendable(); // Map 'async' to the appropriate `@async` modifier. bool isAsync = false; - if (substFnInterfaceType->getExtInfo().isAsync() && !foreignInfo.Async) { + if (substFnInterfaceType->getExtInfo().isAsync() && !foreignInfo.async) { assert(!origType.isForeign() && "using native Swift async for foreign type!"); isAsync = true; } - + // Map 'throws' to the appropriate error convention. - if (substFnInterfaceType->getExtInfo().isThrowing() - && !foreignInfo.Error - && !foreignInfo.Async) { + if (substFnInterfaceType->getExtInfo().isThrowing() && !foreignInfo.error && + !foreignInfo.async) { assert(!origType.isForeign() && "using native Swift error convention for foreign type!"); SILType exnType = SILType::getExceptionType(TC.Context); @@ -2178,7 +2158,8 @@ static CanSILFunctionType getSILFunctionType( SILCoroutineKind coroutineKind = SILCoroutineKind::None; SmallVector yields; destructureYieldsForCoroutine(TC, expansionContext, origConstant, constant, - reqtSubs, yields, coroutineKind, subst); + reqtSubs, genericSig, yields, coroutineKind, + subst); // Destructure the result tuple type. SmallVector results; @@ -2512,6 +2493,9 @@ static CanSILFunctionType getNativeSILFunctionType( DefaultConventions(NormalParameterConvention::Guaranteed)); case SILDeclRef::Kind::Deallocator: return getSILFunctionTypeForConventions(DeallocatorConventions()); + + case SILDeclRef::Kind::EntryPoint: + llvm_unreachable("Handled by getSILFunctionTypeForAbstractCFunction"); } } } @@ -2840,18 +2824,31 @@ class CFunctionConventions : public CFunctionTypeConventions { class CXXMethodConventions : public CFunctionTypeConventions { using super = CFunctionTypeConventions; const clang::CXXMethodDecl *TheDecl; + bool isMutating; public: - CXXMethodConventions(const clang::CXXMethodDecl *decl) + CXXMethodConventions(const clang::CXXMethodDecl *decl, bool isMutating) : CFunctionTypeConventions( ConventionsKind::CXXMethod, decl->getType()->castAs()), - TheDecl(decl) {} + TheDecl(decl), isMutating(isMutating) {} ParameterConvention getIndirectSelfParameter(const AbstractionPattern &type) const override { - if (TheDecl->isConst()) + llvm_unreachable( + "cxx functions do not have a Swift self parameter; " + "foreign self parameter is handled in getIndirectParameter"); + } + + ParameterConvention + getIndirectParameter(unsigned int index, const AbstractionPattern &type, + const TypeLowering &substTL) const override { + // `self` is the last parameter. + if (index == TheDecl->getNumParams()) { + if (isMutating) + return ParameterConvention::Indirect_Inout; return ParameterConvention::Indirect_In_Guaranteed; - return ParameterConvention::Indirect_Inout; + } + return super::getIndirectParameter(index, type, substTL); } ResultConvention getResult(const TypeLowering &resultTL) const override { if (isa(TheDecl)) { @@ -2889,10 +2886,8 @@ static CanSILFunctionType getSILFunctionTypeForClangDecl( SILExtInfoBuilder extInfoBuilder, const ForeignInfo &foreignInfo, Optional constant) { if (auto method = dyn_cast(clangDecl)) { - auto origPattern = - AbstractionPattern::getObjCMethod(origType, method, - foreignInfo.Error, - foreignInfo.Async); + auto origPattern = AbstractionPattern::getObjCMethod( + origType, method, foreignInfo.error, foreignInfo.async); return getSILFunctionType( TC, TypeExpansionContext::minimal(), origPattern, substInterfaceType, extInfoBuilder, ObjCMethodConventions(method), foreignInfo, constant, @@ -2900,10 +2895,15 @@ static CanSILFunctionType getSILFunctionTypeForClangDecl( } if (auto method = dyn_cast(clangDecl)) { - AbstractionPattern origPattern = method->isOverloadedOperator() ? - AbstractionPattern::getCXXOperatorMethod(origType, method, foreignInfo.Self): - AbstractionPattern::getCXXMethod(origType, method, foreignInfo.Self); - auto conventions = CXXMethodConventions(method); + AbstractionPattern origPattern = + method->isOverloadedOperator() + ? AbstractionPattern::getCXXOperatorMethod(origType, method, + foreignInfo.self) + : AbstractionPattern::getCXXMethod(origType, method, + foreignInfo.self); + bool isMutating = + TC.Context.getClangModuleLoader()->isCXXMethodMutating(method); + auto conventions = CXXMethodConventions(method, isMutating); return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, substInterfaceType, extInfoBuilder, conventions, foreignInfo, constant, constant, None, @@ -2913,10 +2913,10 @@ static CanSILFunctionType getSILFunctionTypeForClangDecl( if (auto func = dyn_cast(clangDecl)) { auto clangType = func->getType().getTypePtr(); AbstractionPattern origPattern = - foreignInfo.Self.isImportAsMember() - ? AbstractionPattern::getCFunctionAsMethod(origType, clangType, - foreignInfo.Self) - : AbstractionPattern(origType, clangType); + foreignInfo.self.isImportAsMember() + ? AbstractionPattern::getCFunctionAsMethod(origType, clangType, + foreignInfo.self) + : AbstractionPattern(origType, clangType); return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, substInterfaceType, extInfoBuilder, CFunctionConventions(func), foreignInfo, constant, @@ -3038,6 +3038,7 @@ static ObjCSelectorFamily getObjCSelectorFamily(SILDeclRef c) { case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: + case SILDeclRef::Kind::EntryPoint: llvm_unreachable("Unexpected Kind of foreign SILDeclRef"); } @@ -3208,10 +3209,10 @@ static CanSILFunctionType getUncachedSILFunctionTypeForConstant( // import-as-member but do involve the same gymnastics with the // formal type. That's all that SILFunctionType cares about, so // pretend that it's import-as-member. - if (!foreignInfo.Self.isImportAsMember() && + if (!foreignInfo.self.isImportAsMember() && isImporterGeneratedAccessor(clangDecl, constant)) { unsigned selfIndex = cast(decl)->isSetter() ? 1 : 0; - foreignInfo.Self.setSelfIndex(selfIndex); + foreignInfo.self.setSelfIndex(selfIndex); } return getSILFunctionTypeForClangDecl( @@ -3262,19 +3263,21 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) { } // Anonymous functions currently always have Freestanding CC. - if (!c.hasDecl()) + if (c.getAbstractClosureExpr()) return SILFunctionTypeRepresentation::Thin; // FIXME: Assert that there is a native entry point // available. There's no great way to do this. // Protocol witnesses are called using the witness calling convention. - if (auto proto = dyn_cast(c.getDecl()->getDeclContext())) { - // Use the regular method convention for foreign-to-native thunks. - if (c.isForeignToNativeThunk()) - return SILFunctionTypeRepresentation::Method; - assert(!c.isNativeToForeignThunk() && "shouldn't be possible"); - return getProtocolWitnessRepresentation(proto); + if (c.hasDecl()) { + if (auto proto = dyn_cast(c.getDecl()->getDeclContext())) { + // Use the regular method convention for foreign-to-native thunks. + if (c.isForeignToNativeThunk()) + return SILFunctionTypeRepresentation::Method; + assert(!c.isNativeToForeignThunk() && "shouldn't be possible"); + return getProtocolWitnessRepresentation(proto); + } } switch (c.kind) { @@ -3298,6 +3301,9 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) { case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: return SILFunctionTypeRepresentation::Method; + + case SILDeclRef::Kind::EntryPoint: + return SILFunctionTypeRepresentation::CFunctionPointer; } llvm_unreachable("Unhandled SILDeclRefKind in switch."); @@ -4125,6 +4131,7 @@ static AbstractFunctionDecl *getBridgedFunction(SILDeclRef declRef) { case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: + case SILDeclRef::Kind::EntryPoint: return nullptr; } llvm_unreachable("bad SILDeclRef kind"); @@ -4295,10 +4302,15 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, if (innerExtInfo.isAsync()) extInfo = extInfo.withAsync(true); + // Distributed thunks are always `async throws` + if (constant.isDistributedThunk()) { + extInfo = extInfo.withAsync(true).withThrows(true); + } + // If this is a C++ constructor, don't add the metatype "self" parameter // because we'll never use it and it will cause problems in IRGen. - if (constant.getDecl()->getClangDecl() && - isa(constant.getDecl()->getClangDecl())) { + if (isa_and_nonnull( + constant.getDecl()->getClangDecl())) { // But, make sure it is actually a metatype that we're not adding. If // changes to the self parameter are made in the future, this logic may // need to be updated. diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index d9519576fac20..0b9936dbe98f8 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "swift/SIL/SILBridging.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILGlobalVariable.h" #include "swift/SIL/SILInstruction.h" @@ -18,6 +19,8 @@ using namespace swift; +SwiftMetatype SILGlobalVariable::registeredMetatype; + SILGlobalVariable *SILGlobalVariable::create(SILModule &M, SILLinkage linkage, IsSerialized_t isSerialized, StringRef name, @@ -44,7 +47,8 @@ SILGlobalVariable::SILGlobalVariable(SILModule &Module, SILLinkage Linkage, IsSerialized_t isSerialized, StringRef Name, SILType LoweredType, Optional Loc, VarDecl *Decl) - : Module(Module), + : SwiftObjectHeader(registeredMetatype), + Module(Module), Name(Name), LoweredType(LoweredType), Location(Loc.getValueOr(SILLocation::invalid())), @@ -58,8 +62,7 @@ SILGlobalVariable::SILGlobalVariable(SILModule &Module, SILLinkage Linkage, } SILGlobalVariable::~SILGlobalVariable() { - StaticInitializerBlock.dropAllReferences(); - StaticInitializerBlock.clearStaticInitializerBlock(Module); + clear(); } bool SILGlobalVariable::isPossiblyUsedExternally() const { diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index 2af1c5c78067b..47ce3171996e2 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -68,7 +68,6 @@ void llvm::ilist_traits::addNodeToList(SILInstruction *I) { void llvm::ilist_traits::removeNodeFromList(SILInstruction *I) { // When an instruction is removed from a BB, clear the parent pointer. - assert(I->ParentBB && "Not in a list!"); I->ParentBB = nullptr; } @@ -104,6 +103,16 @@ SILModule &SILInstruction::getModule() const { return getFunction()->getModule(); } +void SILInstruction::removeFromParent() { +#ifndef NDEBUG + for (auto result : getResults()) { + assert(result->use_empty() && "Uses of SILInstruction remain at deletion."); + } +#endif + getParent()->remove(this); + ParentBB = nullptr; +} + /// eraseFromParent - This method unlinks 'self' from the containing basic /// block and deletes it. /// @@ -146,6 +155,15 @@ void SILInstruction::dropAllReferences() { OpE = PossiblyDeadOps.end(); OpI != OpE; ++OpI) { OpI->drop(); } + dropNonOperandReferences(); +} + +void SILInstruction::dropNonOperandReferences() { + if (auto *termInst = dyn_cast(this)) { + for (SILSuccessor &succ : termInst->getSuccessors()) { + succ = nullptr; + } + } // If we have a function ref inst, we need to especially drop its function // argument so that it gets a proper ref decrement. @@ -1111,6 +1129,7 @@ bool SILInstruction::mayRelease() const { default: llvm_unreachable("Unhandled releasing instruction!"); + case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::GetAsyncContinuationInst: case SILInstructionKind::GetAsyncContinuationAddrInst: case SILInstructionKind::AwaitAsyncContinuationInst: @@ -1348,7 +1367,6 @@ bool SILInstruction::isMetaInstruction() const { case SILInstructionKind::AllocBoxInst: case SILInstructionKind::AllocStackInst: case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: return true; default: return false; diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 45146aea74ab2..b0a656f5db9d0 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -116,13 +116,20 @@ template static void *allocateDebugVarCarryingInst(SILModule &M, Optional Var, ArrayRef Operands = {}) { - return M.allocateInst(sizeof(INST) + (Var ? Var->Name.size() : 0) + - sizeof(Operand) * Operands.size(), - alignof(INST)); + return M.allocateInst( + sizeof(INST) + (Var ? Var->Name.size() : 0) + + (Var && Var->Type ? sizeof(SILType) : 0) + + (Var && Var->Loc ? sizeof(SILLocation) : 0) + + (Var && Var->Scope ? sizeof(const SILDebugScope *) : 0) + + sizeof(SILDIExprElement) * (Var ? Var->DIExpr.getNumElements() : 0) + + sizeof(Operand) * Operands.size(), + alignof(INST)); } TailAllocatedDebugVariable::TailAllocatedDebugVariable( - Optional Var, char *buf) { + Optional Var, char *buf, SILType *AuxVarType, + SILLocation *DeclLoc, const SILDebugScope **DeclScope, + SILDIExprElement *DIExprOps) { if (!Var) { Bits.RawValue = 0; return; @@ -131,10 +138,21 @@ TailAllocatedDebugVariable::TailAllocatedDebugVariable( Bits.Data.HasValue = true; Bits.Data.Constant = Var->Constant; Bits.Data.ArgNo = Var->ArgNo; + Bits.Data.Implicit = Var->Implicit; Bits.Data.NameLength = Var->Name.size(); assert(Bits.Data.ArgNo == Var->ArgNo && "Truncation"); assert(Bits.Data.NameLength == Var->Name.size() && "Truncation"); memcpy(buf, Var->Name.data(), Bits.Data.NameLength); + if (AuxVarType && Var->Type) + *AuxVarType = *Var->Type; + if (DeclLoc && Var->Loc) + *DeclLoc = *Var->Loc; + if (DeclScope && Var->Scope) + *DeclScope = Var->Scope; + if (DIExprOps) { + llvm::ArrayRef Ops(Var->DIExpr.Elements); + memcpy(DIExprOps, Ops.data(), sizeof(SILDIExprElement) * Ops.size()); + } } StringRef TailAllocatedDebugVariable::getName(const char *buf) const { @@ -143,19 +161,59 @@ StringRef TailAllocatedDebugVariable::getName(const char *buf) const { return StringRef(); } +Optional +SILDebugVariable::createFromAllocation(const AllocationInst *AI) { + Optional VarInfo; + if (const auto *ASI = dyn_cast_or_null(AI)) + VarInfo = ASI->getVarInfo(); + // TODO: Support AllocBoxInst + + if (!VarInfo) + return VarInfo; + + // Copy everything but the DIExpr + VarInfo->DIExpr.clear(); + + // Coalesce the debug loc attached on AI into VarInfo + SILType Type = AI->getType(); + SILLocation InstLoc = AI->getLoc(); + const SILDebugScope *InstDS = AI->getDebugScope(); + if (!VarInfo->Type) + VarInfo->Type = Type; + if (!VarInfo->Loc) + VarInfo->Loc = InstLoc; + if (!VarInfo->Scope) + VarInfo->Scope = InstDS; + + return VarInfo; +} + AllocStackInst::AllocStackInst(SILDebugLocation Loc, SILType elementType, ArrayRef TypeDependentOperands, - SILFunction &F, - Optional Var, + SILFunction &F, Optional Var, bool hasDynamicLifetime) : InstructionBase(Loc, elementType.getAddressType()), - dynamicLifetime(hasDynamicLifetime) { + SILDebugVariableSupplement(Var ? Var->DIExpr.getNumElements() : 0, + Var ? Var->Type.hasValue() : false, + Var ? Var->Loc.hasValue() : false, + Var ? Var->Scope != nullptr : false), + dynamicLifetime(hasDynamicLifetime) { SILNode::Bits.AllocStackInst.NumOperands = TypeDependentOperands.size(); assert(SILNode::Bits.AllocStackInst.NumOperands == TypeDependentOperands.size() && "Truncation"); SILNode::Bits.AllocStackInst.VarInfo = - TailAllocatedDebugVariable(Var, getTrailingObjects()).getRawValue(); + TailAllocatedDebugVariable(Var, getTrailingObjects(), + getTrailingObjects(), + getTrailingObjects(), + getTrailingObjects(), + getTrailingObjects()) + .getRawValue(); + if (auto *VD = Loc.getLocation().getAsASTNode()) { + TailAllocatedDebugVariable DbgVar(SILNode::Bits.AllocStackInst.VarInfo); + DbgVar.setImplicit(VD->isImplicit() || DbgVar.isImplicit()); + SILNode::Bits.AllocStackInst.VarInfo = DbgVar.getRawValue(); + } TrailingOperandsList::InitOperandsList(getAllOperands().begin(), this, TypeDependentOperands); } @@ -280,9 +338,17 @@ SILType AllocBoxInst::getAddressType() const { DebugValueInst::DebugValueInst(SILDebugLocation DebugLoc, SILValue Operand, SILDebugVariable Var, bool poisonRefs) : UnaryInstructionBase(DebugLoc, Operand), - VarInfo(Var, getTrailingObjects()) { - setPoisonRefs(poisonRefs); - } + SILDebugVariableSupplement(Var.DIExpr.getNumElements(), + Var.Type.hasValue(), Var.Loc.hasValue(), + Var.Scope), + VarInfo(Var, getTrailingObjects(), getTrailingObjects(), + getTrailingObjects(), + getTrailingObjects(), + getTrailingObjects()) { + if (auto *VD = DebugLoc.getLocation().getAsASTNode()) + VarInfo.setImplicit(VD->isImplicit() || VarInfo.isImplicit()); + setPoisonRefs(poisonRefs); +} DebugValueInst *DebugValueInst::create(SILDebugLocation DebugLoc, SILValue Operand, SILModule &M, @@ -291,25 +357,32 @@ DebugValueInst *DebugValueInst::create(SILDebugLocation DebugLoc, return ::new (buf) DebugValueInst(DebugLoc, Operand, Var, poisonRefs); } -DebugValueAddrInst::DebugValueAddrInst(SILDebugLocation DebugLoc, - SILValue Operand, - SILDebugVariable Var) - : UnaryInstructionBase(DebugLoc, Operand), - VarInfo(Var, getTrailingObjects()) {} +DebugValueInst *DebugValueInst::createAddr(SILDebugLocation DebugLoc, + SILValue Operand, SILModule &M, + SILDebugVariable Var) { + // For alloc_stack, debug_value is used to annotate the associated + // memory location, so we shouldn't attach op_deref. + if (!isa(Operand)) + Var.DIExpr.prependElements( + {SILDIExprElement::createOperator(SILDIExprOperator::Dereference)}); + void *buf = allocateDebugVarCarryingInst(M, Var); + return ::new (buf) DebugValueInst(DebugLoc, Operand, Var, + /*poisonRefs=*/false); +} + +bool DebugValueInst::exprStartsWithDeref() const { + if (!NumDIExprOperands) + return false; -DebugValueAddrInst *DebugValueAddrInst::create(SILDebugLocation DebugLoc, - SILValue Operand, SILModule &M, - SILDebugVariable Var) { - void *buf = allocateDebugVarCarryingInst(M, Var); - return ::new (buf) DebugValueAddrInst(DebugLoc, Operand, Var); + llvm::ArrayRef DIExprElements( + getTrailingObjects(), NumDIExprOperands); + return DIExprElements.front().getAsOperator() + == SILDIExprOperator::Dereference; } VarDecl *DebugValueInst::getDecl() const { return getLoc().getAsASTNode(); } -VarDecl *DebugValueAddrInst::getDecl() const { - return getLoc().getAsASTNode(); -} AllocExistentialBoxInst *AllocExistentialBoxInst::create( SILDebugLocation Loc, SILType ExistentialType, CanType ConcreteType, @@ -751,9 +824,7 @@ SILType DifferentiabilityWitnessFunctionInst::getDifferentiabilityWitnessType( SILModule &module, DifferentiabilityWitnessFunctionKind witnessKind, SILDifferentiabilityWitness *witness) { auto fnTy = witness->getOriginalFunction()->getLoweredFunctionType(); - CanGenericSignature witnessCanGenSig; - if (auto witnessGenSig = witness->getDerivativeGenericSignature()) - witnessCanGenSig = witnessGenSig->getCanonicalSignature(); + auto witnessCanGenSig = witness->getDerivativeGenericSignature().getCanonicalSignature(); auto *parameterIndices = witness->getParameterIndices(); auto *resultIndices = witness->getResultIndices(); if (auto derivativeKind = witnessKind.getAsDerivativeFunctionKind()) { @@ -1311,6 +1382,16 @@ unsigned swift::getFieldIndex(NominalTypeDecl *decl, VarDecl *field) { "property of the operand type"); } +unsigned swift::getCaseIndex(EnumElementDecl *enumElement) { + unsigned idx = 0; + for (EnumElementDecl *e : enumElement->getParentEnum()->getAllElements()) { + if (e == enumElement) + return idx; + ++idx; + } + llvm_unreachable("enum element not found in enum decl"); +} + /// Get the property for a struct or class by its unique index. VarDecl *swift::getIndexedField(NominalTypeDecl *decl, unsigned index) { if (auto *structDecl = dyn_cast(decl)) { @@ -2784,7 +2865,7 @@ SILType GetAsyncContinuationInstBase::getLoweredResumeType() const { auto formalType = getFormalResumeType(); auto &M = getFunction()->getModule(); auto c = getFunction()->getTypeExpansionContext(); - return M.Types.getLoweredType(AbstractionPattern::getOpaque(), formalType, c); + return M.Types.getLoweredType(AbstractionPattern(formalType), formalType, c); } ReturnInst::ReturnInst(SILFunction &func, SILDebugLocation debugLoc, diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 1096e93a77f33..dc486f95260fe 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -126,8 +126,9 @@ SILModule::~SILModule() { assert(!hasUnresolvedOpenedArchetypeDefinitions()); // Decrement ref count for each SILGlobalVariable with static initializers. - for (SILGlobalVariable &v : silGlobals) - v.dropAllReferences(); + for (SILGlobalVariable &v : silGlobals) { + v.clear(); + } for (auto vt : vtables) vt->~SILVTable(); @@ -143,10 +144,16 @@ SILModule::~SILModule() { F.dropDynamicallyReplacedFunction(); F.clearSpecializeAttrs(); } + + for (SILFunction &F : *this) { + F.eraseAllBlocks(); + } + flushDeletedInsts(); } void SILModule::checkForLeaks() const { - int instsInModule = 0; + int instsInModule = std::distance(scheduledForDeletion.begin(), + scheduledForDeletion.end()); for (const SILFunction &F : *this) { for (const SILBasicBlock &block : F) { instsInModule += std::distance(block.begin(), block.end()); @@ -231,8 +238,30 @@ void *SILModule::allocateInst(unsigned Size, unsigned Align) const { return AlignedAlloc(Size, Align); } -void SILModule::deallocateInst(SILInstruction *I) { - AlignedFree(I); +void SILModule::willDeleteInstruction(SILInstruction *I) { + // Update openedArchetypeDefs. + if (auto *svi = dyn_cast(I)) { + if (CanArchetypeType archeTy = svi->getOpenedArchetype()) { + OpenedArchetypeKey key = {archeTy, svi->getFunction()}; + assert(openedArchetypeDefs.lookup(key) == svi && + "archetype def was not registered"); + openedArchetypeDefs.erase(key); + } + } +} + +void SILModule::scheduleForDeletion(SILInstruction *I) { + I->dropAllReferences(); + scheduledForDeletion.push_back(I); + I->ParentBB = nullptr; +} + +void SILModule::flushDeletedInsts() { + while (!scheduledForDeletion.empty()) { + SILInstruction *inst = &*scheduledForDeletion.begin(); + scheduledForDeletion.erase(inst); + AlignedFree(inst); + } } SILWitnessTable * @@ -779,36 +808,6 @@ void SILModule::notifyMovedInstruction(SILInstruction *inst, } } -void SILModule::registerDeleteNotificationHandler( - DeleteNotificationHandler *handler) { - // Ask the handler (that can be an analysis, a pass, or some other data - // structure) if it wants to receive delete notifications. - if (handler->needsNotifications()) { - NotificationHandlers.insert(handler); - } -} - -void SILModule:: -removeDeleteNotificationHandler(DeleteNotificationHandler* Handler) { - NotificationHandlers.remove(Handler); -} - -void SILModule::notifyDeleteHandlers(SILNode *node) { - // Update openedArchetypeDefs. - if (auto *svi = dyn_cast(node)) { - if (CanArchetypeType archeTy = svi->getOpenedArchetype()) { - OpenedArchetypeKey key = {archeTy, svi->getFunction()}; - assert(openedArchetypeDefs.lookup(key) == svi && - "archetype def was not registered"); - openedArchetypeDefs.erase(key); - } - } - - for (auto *Handler : NotificationHandlers) { - Handler->handleDeleteNotification(node); - } -} - // TODO: We should have an "isNoReturn" bit on Swift's BuiltinInfo, but for // now, let's recognize noreturn intrinsics and builtins specially here. bool SILModule::isNoReturnBuiltinOrIntrinsic(Identifier Name) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 60e8d87491299..b01d382c057fe 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -262,55 +262,60 @@ void SILDeclRef::print(raw_ostream &OS) const { } bool isDot = true; - if (!hasDecl()) { + switch (getLocKind()) { + case LocKind::Closure: OS << ""; - } else if (kind == SILDeclRef::Kind::Func) { - auto *FD = cast(getDecl()); - auto accessor = dyn_cast(FD); + break; + case LocKind::File: + OS << ""; + break; + case LocKind::Decl: { + if (kind != Kind::Func) { + printValueDecl(getDecl(), OS); + break; + } + + auto *accessor = dyn_cast(getDecl()); if (!accessor) { - printValueDecl(FD, OS); + printValueDecl(getDecl(), OS); isDot = false; - } else { - switch (accessor->getAccessorKind()) { - case AccessorKind::WillSet: - printValueDecl(accessor->getStorage(), OS); - OS << "!willSet"; - break; - case AccessorKind::DidSet: - printValueDecl(accessor->getStorage(), OS); - OS << "!didSet"; - break; - case AccessorKind::Get: - printValueDecl(accessor->getStorage(), OS); - OS << "!getter"; - break; - case AccessorKind::Set: - printValueDecl(accessor->getStorage(), OS); - OS << "!setter"; - break; - case AccessorKind::Address: - printValueDecl(accessor->getStorage(), OS); - OS << "!addressor"; - break; - case AccessorKind::MutableAddress: - printValueDecl(accessor->getStorage(), OS); - OS << "!mutableAddressor"; - break; - case AccessorKind::Read: - printValueDecl(accessor->getStorage(), OS); - OS << "!read"; - break; - case AccessorKind::Modify: - printValueDecl(accessor->getStorage(), OS); - OS << "!modify"; - break; - } + break; } - } else { - printValueDecl(getDecl(), OS); + + printValueDecl(accessor->getStorage(), OS); + switch (accessor->getAccessorKind()) { + case AccessorKind::WillSet: + OS << "!willSet"; + break; + case AccessorKind::DidSet: + OS << "!didSet"; + break; + case AccessorKind::Get: + OS << "!getter"; + break; + case AccessorKind::Set: + OS << "!setter"; + break; + case AccessorKind::Address: + OS << "!addressor"; + break; + case AccessorKind::MutableAddress: + OS << "!mutableAddressor"; + break; + case AccessorKind::Read: + OS << "!read"; + break; + case AccessorKind::Modify: + OS << "!modify"; + break; + } + break; } + } + switch (kind) { case SILDeclRef::Kind::Func: + case SILDeclRef::Kind::EntryPoint: break; case SILDeclRef::Kind::Allocator: OS << "!allocator"; @@ -467,17 +472,17 @@ static void printSILFunctionNameAndType( function->printName(OS); OS << " : $"; auto *genEnv = function->getGenericEnvironment(); - const GenericSignatureImpl *genSig = nullptr; + GenericSignature genSig; // If `genEnv` is defined, get sugared names of generic // parameter types for printing. if (genEnv) { - genSig = genEnv->getGenericSignature().getPointer(); + genSig = genEnv->getGenericSignature(); llvm::DenseSet usedNames; llvm::SmallString<16> disambiguatedNameBuf; unsigned disambiguatedNameCounter = 1; - for (auto *paramTy : genSig->getGenericParams()) { + for (auto *paramTy : genSig.getGenericParams()) { // Get a uniqued sugared name for the generic parameter type. auto sugaredTy = genEnv->getGenericSignature()->getSugaredType(paramTy); Identifier name = sugaredTy->getName(); @@ -501,7 +506,7 @@ static void printSILFunctionNameAndType( } } auto printOptions = PrintOptions::printSIL(silPrintContext); - printOptions.GenericSig = genSig; + printOptions.GenericSig = genSig.getPointer(); printOptions.AlternativeTypeNames = sugaredTypeNames.empty() ? nullptr : &sugaredTypeNames; function->getLoweredFunctionType()->print(OS, printOptions); @@ -976,9 +981,8 @@ class SILPrinter : public SILInstructionVisitor { // Print results. auto results = I->getResults(); - if (results.size() == 1 && - I->isStaticInitializerInst() && - I == &I->getParent()->back()) { + if (results.size() == 1 && !I->isDeleted() && I->isStaticInitializerInst() + && I == &I->getParent()->back()) { *this << "%initval = "; } else if (results.size() == 1) { ID Name = Ctx.getID(results[0]); @@ -1006,7 +1010,8 @@ class SILPrinter : public SILInstructionVisitor { // Maybe print debugging information. bool printedSlashes = false; - if (Ctx.printDebugInfo() && !I->isStaticInitializerInst()) { + if (Ctx.printDebugInfo() && !I->isDeleted() + && !I->isStaticInitializerInst()) { auto &SM = I->getModule().getASTContext().SourceMgr; printDebugLocRef(I->getLoc(), SM); printDebugScopeRef(I->getDebugScope(), SM); @@ -1138,23 +1143,83 @@ class SILPrinter : public SILInstructionVisitor { } } - void printDebugVar(Optional Var) { - if (!Var || Var->Name.empty()) + void printDebugInfoExpression(const SILDebugInfoExpression &DIExpr) { + assert(DIExpr && "DIExpression empty?"); + *this << ", expr "; + bool IsFirst = true; + for (const auto &E : DIExpr.elements()) { + if (IsFirst) + IsFirst = false; + else + *this << ":"; + + switch (E.getKind()) { + case SILDIExprElement::OperatorKind: { + SILDIExprOperator Op = E.getAsOperator(); + assert(Op != SILDIExprOperator::INVALID && + "Invalid SILDIExprOperator kind"); + *this << SILDIExprInfo::get(Op)->OpText; + break; + } + case SILDIExprElement::DeclKind: { + const Decl *D = E.getAsDecl(); + // FIXME: Can we generalize this special handling for VarDecl + // to other kinds of Decl? + if (const auto *VD = dyn_cast(D)) { + *this << "#"; + printFullContext(VD->getDeclContext(), PrintState.OS); + *this << VD->getName().get(); + } else + D->print(PrintState.OS, PrintState.ASTOptions); + break; + } + } + } + } + + void printDebugVar(Optional Var, + const SourceManager *SM = nullptr) { + if (!Var) return; - if (Var->Constant) - *this << ", let"; - else - *this << ", var"; - *this << ", name \"" << Var->Name << '"'; - if (Var->ArgNo) - *this << ", argno " << Var->ArgNo; + + if (!Var->Name.empty()) { + if (Var->Constant) + *this << ", let"; + else + *this << ", var"; + + if ((Var->Loc || Var->Scope) && SM) { + *this << ", (name \"" << Var->Name << '"'; + if (Var->Loc) + printDebugLocRef(*Var->Loc, *SM); + if (Var->Scope) + printDebugScopeRef(Var->Scope, *SM); + *this << ")"; + } else + *this << ", name \"" << Var->Name << '"'; + + if (Var->ArgNo) + *this << ", argno " << Var->ArgNo; + if (Var->Implicit) + *this << ", implicit"; + if (Var->Type) { + *this << ", type "; + Var->Type->print(PrintState.OS, PrintState.ASTOptions); + } + } + // Although it's rare in real-world use cases, but during testing, + // sometimes we want to print out di-expression, even the debug + // variable name is empty. + if (Var->DIExpr) + printDebugInfoExpression(Var->DIExpr); } void visitAllocStackInst(AllocStackInst *AVI) { if (AVI->hasDynamicLifetime()) *this << "[dynamic_lifetime] "; *this << AVI->getElementType(); - printDebugVar(AVI->getVarInfo()); + printDebugVar(AVI->getVarInfo(), + &AVI->getModule().getASTContext().SourceMgr); } void printAllocRefInstBase(AllocRefInstBase *ARI) { @@ -1189,7 +1254,8 @@ class SILPrinter : public SILInstructionVisitor { if (ABI->hasDynamicLifetime()) *this << "[dynamic_lifetime] "; *this << ABI->getType(); - printDebugVar(ABI->getVarInfo()); + printDebugVar(ABI->getVarInfo(), + &ABI->getModule().getASTContext().SourceMgr); } void printSubstitutions(SubstitutionMap Subs, @@ -1202,7 +1268,7 @@ class SILPrinter : public SILInstructionVisitor { *this << '<'; bool first = true; - for (auto gp : genericSig->getGenericParams()) { + for (auto gp : genericSig.getGenericParams()) { if (first) first = false; else *this << ", "; @@ -1397,6 +1463,9 @@ class SILPrinter : public SILInstructionVisitor { } void visitBeginBorrowInst(BeginBorrowInst *LBI) { + if (LBI->isDefined()) { + *this << "[defined] "; + } *this << getIDAndType(LBI->getOperand()); } @@ -1434,6 +1503,9 @@ class SILPrinter : public SILInstructionVisitor { void printForwardingOwnershipKind(OwnershipForwardingMixin *inst, SILValue op) { + if (!op) + return; + if (inst->getForwardingOwnershipKind() != op.getOwnershipKind()) { *this << ", forwarding: @" << inst->getForwardingOwnershipKind(); } @@ -1510,12 +1582,8 @@ class SILPrinter : public SILInstructionVisitor { if (DVI->poisonRefs()) *this << "[poison] "; *this << getIDAndType(DVI->getOperand()); - printDebugVar(DVI->getVarInfo()); - } - - void visitDebugValueAddrInst(DebugValueAddrInst *DVAI) { - *this << getIDAndType(DVAI->getOperand()); - printDebugVar(DVAI->getVarInfo()); + printDebugVar(DVI->getVarInfo(), + &DVI->getModule().getASTContext().SourceMgr); } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ @@ -2149,6 +2217,8 @@ class SILPrinter : public SILInstructionVisitor { } void visitHopToExecutorInst(HopToExecutorInst *HTEI) { + if (HTEI->isMandatory()) + *this << "[mandatory] "; *this << getIDAndType(HTEI->getTargetExecutor()); } @@ -3585,7 +3655,7 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { genericSig); requirements = requirementsScratch; } else { - requirements = specializedSig->getRequirements(); + requirements = specializedSig.getRequirements(); } } if (targetFunction) { @@ -3630,7 +3700,8 @@ SILPrintContext::SILPrintContext(llvm::raw_ostream &OS, bool Verbose, SILPrintContext::SILPrintContext(llvm::raw_ostream &OS, const SILOptions &Opts) : OutStream(OS), Verbose(Opts.EmitVerboseSIL), - SortedSIL(Opts.EmitSortedSIL), DebugInfo(SILPrintDebugInfo), + SortedSIL(Opts.EmitSortedSIL), + DebugInfo(Opts.PrintDebugInfo || SILPrintDebugInfo), PrintFullConvention(Opts.PrintFullConvention) {} SILPrintContext::SILPrintContext(llvm::raw_ostream &OS, bool Verbose, diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index 00040669d0640..05fd1d8694c7b 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -165,7 +165,7 @@ SILType SILType::getFieldType(VarDecl *field, TypeConverter &TC, substFieldTy = origFieldTy.getType(); } else { substFieldTy = - getASTType()->getTypeOfMember(&TC.M, field, nullptr)->getCanonicalType(); + getASTType()->getTypeOfMember(&TC.M, field)->getCanonicalType(); } auto loweredTy = diff --git a/lib/SIL/IR/SILValue.cpp b/lib/SIL/IR/SILValue.cpp index 9045a61f3e922..af63cec64059a 100644 --- a/lib/SIL/IR/SILValue.cpp +++ b/lib/SIL/IR/SILValue.cpp @@ -156,6 +156,7 @@ StringRef OwnershipKind::asString() const { case OwnershipKind::None: return "none"; } + llvm_unreachable("covered switch"); } //===----------------------------------------------------------------------===// @@ -339,6 +340,23 @@ bool Operand::isLifetimeEnding() const { return get().getOwnershipKind() != OwnershipKind::None; } +bool Operand::isConsuming() const { + if (!getOwnershipConstraint().isConsuming()) + return false; + + return get().getOwnershipKind() != OwnershipKind::None; +} + +void Operand::dump() const { print(llvm::dbgs()); } + +void Operand::print(llvm::raw_ostream &os) const { + os << "Operand.\n" + "Owner: " + << *Owner << "Value: " << get() << "Operand Number: " << getOperandNumber() + << '\n' + << "Is Type Dependent: " << (isTypeDependent() ? "yes" : "no") << '\n'; +} + //===----------------------------------------------------------------------===// // OperandConstraint //===----------------------------------------------------------------------===// @@ -382,6 +400,7 @@ StringRef OperandOwnership::asString() const { case OperandOwnership::Reborrow: return "reborrow"; } + llvm_unreachable("covered switch"); } llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os, diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 55d1aae95cb61..a9ef32670b0d6 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -1768,7 +1768,10 @@ namespace { // field type against the declaration's interface type as we normally // would, we use the substituted field type in order to accurately // preserve the properties of the aggregate. - auto origFieldType = origType.unsafeGetSubstFieldType(field); + auto sig = field->getDeclContext()->getGenericSignatureOfContext(); + auto interfaceTy = field->getInterfaceType()->getCanonicalType(sig); + auto origFieldType = origType.unsafeGetSubstFieldType(field, + interfaceTy); properties.addSubobject(classifyType(origFieldType, substFieldType, TC, Expansion)); @@ -2383,6 +2386,7 @@ static CanType removeNoEscape(CanType resultType) { /// Get the type of a default argument generator, () -> T. static CanAnyFunctionType getDefaultArgGeneratorInterfaceType( + TypeConverter &TC, SILDeclRef c) { auto *vd = c.getDecl(); auto resultTy = getParameterAt(vd, @@ -2400,14 +2404,7 @@ static CanAnyFunctionType getDefaultArgGeneratorInterfaceType( canResultTy = removeNoEscape(canResultTy); // Get the generic signature from the surrounding context. - auto sig = vd->getInnermostDeclContext()->getGenericSignatureOfContext(); - if (auto *afd = dyn_cast(vd)) { - auto *param = getParameterAt(afd, c.defaultArgIndex); - if (param->hasDefaultExpr()) { - auto captureInfo = param->getDefaultArgumentCaptureInfo(); - sig = getEffectiveGenericSignature(afd, captureInfo); - } - } + auto sig = TC.getConstantGenericSignature(c); // FIXME: Verify ExtInfo state is correct, not working by accident. CanAnyFunctionType::ExtInfo info; @@ -2447,38 +2444,20 @@ static CanAnyFunctionType getStoredPropertyInitializerInterfaceType( /// (property-type) -> backing-type. static CanAnyFunctionType getPropertyWrapperBackingInitializerInterfaceType( TypeConverter &TC, - VarDecl *VD) { - CanType resultType = - VD->getPropertyWrapperBackingPropertyType()->getCanonicalType(); - - auto *DC = VD->getInnermostDeclContext(); - CanType inputType = - VD->getPropertyWrapperInitValueInterfaceType()->getCanonicalType(); - - auto sig = DC->getGenericSignatureOfContext(); - - AnyFunctionType::Param param( - inputType, Identifier(), - ParameterTypeFlags().withValueOwnership(ValueOwnership::Owned)); - // FIXME: Verify ExtInfo state is correct, not working by accident. - CanAnyFunctionType::ExtInfo info; - return CanAnyFunctionType::get(getCanonicalSignatureOrNull(sig), {param}, - resultType, info); -} - -static CanAnyFunctionType getPropertyWrapperInitFromProjectedValueInterfaceType(TypeConverter &TC, - VarDecl *VD) { + SILDeclRef c) { + auto *VD = cast(c.getDecl()); CanType resultType = VD->getPropertyWrapperBackingPropertyType()->getCanonicalType(); - Type interfaceType = VD->getPropertyWrapperProjectionVar()->getInterfaceType(); - if (interfaceType->hasArchetype()) - interfaceType = interfaceType->mapTypeOutOfContext(); - - CanType inputType = interfaceType->getCanonicalType(); + CanType inputType; + if (c.kind == SILDeclRef::Kind::PropertyWrapperBackingInitializer) { + inputType = VD->getPropertyWrapperInitValueInterfaceType()->getCanonicalType(); + } else { + Type interfaceType = VD->getPropertyWrapperProjectionVar()->getInterfaceType(); + inputType = interfaceType->getCanonicalType(); + } - auto *DC = VD->getInnermostDeclContext(); - auto sig = DC->getGenericSignatureOfContext(); + GenericSignature sig = TC.getConstantGenericSignature(c); AnyFunctionType::Param param( inputType, Identifier(), @@ -2577,6 +2556,45 @@ getFunctionInterfaceTypeWithCaptures(TypeConverter &TC, innerExtInfo); } +static CanAnyFunctionType getEntryPointInterfaceType(ASTContext &C) { + // Use standard library types if we have them; otherwise, fall back to + // builtins. + CanType Int32Ty; + if (auto Int32Decl = C.getInt32Decl()) { + Int32Ty = Int32Decl->getDeclaredInterfaceType()->getCanonicalType(); + } else { + Int32Ty = CanType(BuiltinIntegerType::get(32, C)); + } + + CanType PtrPtrInt8Ty = C.TheRawPointerType; + if (auto PointerDecl = C.getUnsafeMutablePointerDecl()) { + if (auto Int8Decl = C.getInt8Decl()) { + Type Int8Ty = Int8Decl->getDeclaredInterfaceType(); + Type PointerInt8Ty = BoundGenericType::get(PointerDecl, + nullptr, + Int8Ty); + Type OptPointerInt8Ty = OptionalType::get(PointerInt8Ty); + PtrPtrInt8Ty = BoundGenericType::get(PointerDecl, + nullptr, + OptPointerInt8Ty) + ->getCanonicalType(); + } + } + + using Param = FunctionType::Param; + Param params[] = {Param(Int32Ty), Param(PtrPtrInt8Ty)}; + + auto rep = FunctionTypeRepresentation::CFunctionPointer; + auto *clangTy = C.getClangFunctionType(params, Int32Ty, rep); + auto extInfo = FunctionType::ExtInfoBuilder() + .withRepresentation(rep) + .withClangFunctionType(clangTy) + .build(); + + return CanAnyFunctionType::get(/*genericSig*/ nullptr, + llvm::makeArrayRef(params), Int32Ty, extInfo); +} + CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { if (auto *derivativeId = c.getDerivativeFunctionIdentifier()) { auto originalFnTy = @@ -2639,21 +2657,20 @@ CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { return getGlobalAccessorType(var->getInterfaceType()->getCanonicalType()); } case SILDeclRef::Kind::DefaultArgGenerator: - return getDefaultArgGeneratorInterfaceType(c); + return getDefaultArgGeneratorInterfaceType(*this, c); case SILDeclRef::Kind::StoredPropertyInitializer: return getStoredPropertyInitializerInterfaceType(cast(vd)); case SILDeclRef::Kind::PropertyWrapperBackingInitializer: - return getPropertyWrapperBackingInitializerInterfaceType(*this, - cast(vd)); case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: - return getPropertyWrapperInitFromProjectedValueInterfaceType(*this, - cast(vd)); + return getPropertyWrapperBackingInitializerInterfaceType(*this, c); case SILDeclRef::Kind::IVarInitializer: return getIVarInitDestroyerInterfaceType(cast(vd), c.isForeign, false); case SILDeclRef::Kind::IVarDestroyer: return getIVarInitDestroyerInterfaceType(cast(vd), c.isForeign, true); + case SILDeclRef::Kind::EntryPoint: + return getEntryPointInterfaceType(Context); } llvm_unreachable("Unhandled SILDeclRefKind in switch."); @@ -2683,12 +2700,30 @@ TypeConverter::getConstantGenericSignature(SILDeclRef c) { return getEffectiveGenericSignature( vd->getInnermostDeclContext(), captureInfo); } + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: { + // FIXME: It might be better to compute lowered local captures of + // the property wrapper generator directly and collapse this into the + // above case. For now, take the generic signature of the enclosing + // context. + auto *dc = vd->getDeclContext(); + if (dc->isLocalContext()) { + SILDeclRef enclosingDecl; + if (auto *closure = dyn_cast(dc)) { + enclosingDecl = SILDeclRef(closure); + } else { + enclosingDecl = SILDeclRef(cast(dc)); + } + return getConstantGenericSignature(enclosingDecl); + } + return dc->getGenericSignatureOfContext(); + } case SILDeclRef::Kind::EnumElement: case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::StoredPropertyInitializer: - case SILDeclRef::Kind::PropertyWrapperBackingInitializer: - case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return vd->getDeclContext()->getGenericSignatureOfContext(); + case SILDeclRef::Kind::EntryPoint: + llvm_unreachable("Doesn't have generic signature"); } llvm_unreachable("Unhandled SILDeclRefKind in switch."); @@ -2696,9 +2731,7 @@ TypeConverter::getConstantGenericSignature(SILDeclRef c) { GenericEnvironment * TypeConverter::getConstantGenericEnvironment(SILDeclRef c) { - if (auto sig = getConstantGenericSignature(c)) - return sig->getGenericEnvironment(); - return nullptr; + return getConstantGenericSignature(c).getGenericEnvironment(); } SILType TypeConverter::getSubstitutedStorageType(TypeExpansionContext context, @@ -2829,8 +2862,15 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { // of its accessors. if (auto capturedVar = dyn_cast(capture.getDecl())) { auto collectAccessorCaptures = [&](AccessorKind kind) { - if (auto *accessor = capturedVar->getParsedAccessor(kind)) + if (auto *accessor = capturedVar->getParsedAccessor(kind)) { collectFunctionCaptures(accessor); + } else if (capturedVar->hasAttachedPropertyWrapper() || + capturedVar->getOriginalWrappedProperty( + PropertyWrapperSynthesizedPropertyKind::Projection)) { + // Wrapped properties have synthesized accessors. + if (auto *accessor = capturedVar->getSynthesizedAccessor(kind)) + collectFunctionCaptures(accessor); + } }; // 'Lazy' properties don't fit into the below categorization, @@ -3373,7 +3413,7 @@ TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured, auto loweredContextType = loweredInterfaceType; auto contextBoxTy = boxTy; if (signature) { - auto env = signature->getGenericEnvironment(); + auto env = signature.getGenericEnvironment(); loweredContextType = env->mapTypeIntoContext(loweredContextType) ->getCanonicalType(); contextBoxTy = cast( diff --git a/lib/SIL/IR/ValueOwnership.cpp b/lib/SIL/IR/ValueOwnership.cpp index 678be4f096b45..a682b541840ed 100644 --- a/lib/SIL/IR/ValueOwnership.cpp +++ b/lib/SIL/IR/ValueOwnership.cpp @@ -532,11 +532,13 @@ CONSTANT_OWNERSHIP_BUILTIN(None, IntInstrprofIncrement) CONSTANT_OWNERSHIP_BUILTIN(None, GlobalStringTablePointer) CONSTANT_OWNERSHIP_BUILTIN(None, GetCurrentAsyncTask) CONSTANT_OWNERSHIP_BUILTIN(None, CancelAsyncTask) -CONSTANT_OWNERSHIP_BUILTIN(Owned, CreateAsyncTaskFuture) -CONSTANT_OWNERSHIP_BUILTIN(Owned, CreateAsyncTaskGroupFuture) +CONSTANT_OWNERSHIP_BUILTIN(Owned, CreateAsyncTask) +CONSTANT_OWNERSHIP_BUILTIN(Owned, CreateAsyncTaskInGroup) CONSTANT_OWNERSHIP_BUILTIN(None, ConvertTaskToJob) CONSTANT_OWNERSHIP_BUILTIN(None, InitializeDefaultActor) CONSTANT_OWNERSHIP_BUILTIN(None, DestroyDefaultActor) +CONSTANT_OWNERSHIP_BUILTIN(None, InitializeDistributedRemoteActor) +CONSTANT_OWNERSHIP_BUILTIN(None, DestroyDistributedActor) CONSTANT_OWNERSHIP_BUILTIN(Owned, AutoDiffCreateLinearMapContext) CONSTANT_OWNERSHIP_BUILTIN(None, AutoDiffProjectTopLevelSubcontext) CONSTANT_OWNERSHIP_BUILTIN(None, AutoDiffAllocateSubcontext) @@ -549,6 +551,8 @@ CONSTANT_OWNERSHIP_BUILTIN(None, BuildDefaultActorExecutorRef) CONSTANT_OWNERSHIP_BUILTIN(None, BuildMainActorExecutorRef) CONSTANT_OWNERSHIP_BUILTIN(None, StartAsyncLet) CONSTANT_OWNERSHIP_BUILTIN(None, EndAsyncLet) +CONSTANT_OWNERSHIP_BUILTIN(None, StartAsyncLetWithLocalBuffer) +CONSTANT_OWNERSHIP_BUILTIN(None, EndAsyncLetLifetime) CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroup) CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup) diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 3c4a4f552511c..90403c09805c1 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -243,6 +243,11 @@ namespace { return parseSILIdentifier(Result, L, Diagnostic(ID, Args...)); } + template + bool + parseSILQualifier(Optional &result, + llvm::function_ref(StringRef)> parseName); + bool parseVerbatim(StringRef identifier); template @@ -360,6 +365,8 @@ namespace { StringRef &OpcodeName); bool parseSILDebugVar(SILDebugVariable &Var); + bool parseSILDebugInfoExpression(SILDebugInfoExpression &DIExpr); + /// Parses the basic block arguments as part of branch instruction. bool parseSILBBArgsAtBranch(SmallVector &Args, SILBuilder &B); @@ -817,6 +824,38 @@ static bool parseSILOptional(bool &Result, SILParser &SP, StringRef Expected) { return false; } +// If the qualifier string is unrecognized, then diagnose and fail. +// +// If the qualifier is absent, then succeed and set the result to None. +// The caller can decide how to proceed with an absent qualifier. +// +// Usage: +// auto parseQualifierName = [](StringRef Str) { +// return llvm::StringSwitch>(Str) +// .Case("one", SomeQualifier::One) +// .Case("two", SomeQualifier::Two) +// .Default(None); +// }; +// if (parseSILQualifier(Qualifier, parseQualifierName)) +// return true; +template +bool SILParser::parseSILQualifier( + Optional &result, llvm::function_ref(StringRef)> parseName) { + auto loc = P.Tok.getLoc(); + StringRef Str; + // If we do not parse '[' ... ']', + if (!parseSILOptional(Str, *this)) { + result = None; + return false; + } + result = parseName(Str); + if (!result) { + P.diagnose(loc, Diagnostic(diag::unrecognized_sil_qualifier)); + return true; + } + return false; +} + /// Remap RequirementReps to Requirements. void SILParser::convertRequirements(ArrayRef From, SmallVectorImpl &To) { @@ -1247,8 +1286,9 @@ bool SILParser::parseSILType(SILType &Result, // Parse attributes. ParamDecl::Specifier specifier; SourceLoc specifierLoc; + SourceLoc isolatedLoc; TypeAttributes attrs; - P.parseTypeAttributeList(specifier, specifierLoc, attrs); + P.parseTypeAttributeList(specifier, specifierLoc, isolatedLoc, attrs); // Global functions are implicitly @convention(thin) if not specified otherwise. if (IsFuncDecl && !attrs.has(TAK_convention)) { @@ -1258,8 +1298,7 @@ bool SILParser::parseSILType(SILType &Result, TypeAttributes::Convention::makeSwiftConvention("thin"); } - ParserResult TyR = P.parseType(diag::expected_sil_type, - /*isSILFuncDecl*/ IsFuncDecl); + ParserResult TyR = P.parseType(diag::expected_sil_type); if (TyR.isNull()) return true; @@ -1268,7 +1307,8 @@ bool SILParser::parseSILType(SILType &Result, // Apply attributes to the type. auto *attrRepr = - P.applyAttributeToType(TyR.get(), attrs, specifier, specifierLoc); + P.applyAttributeToType( + TyR.get(), attrs, specifier, specifierLoc, isolatedLoc); const auto Ty = performTypeResolution(attrRepr, /*IsSILType=*/true, OuterGenericEnv, OuterGenericParams); @@ -1397,7 +1437,7 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result, if (!P.consumeIf(tok::sil_exclamation)) { // Construct SILDeclRef. - Result = SILDeclRef(VD, Kind, IsObjC, DerivativeId); + Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false, DerivativeId); return false; } @@ -1517,7 +1557,7 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result, } while (P.consumeIf(tok::period)); // Construct SILDeclRef. - Result = SILDeclRef(VD, Kind, IsObjC, DerivativeId); + Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false, DerivativeId); return false; } @@ -1604,24 +1644,126 @@ bool SILParser::parseSILOpcode(SILInstructionKind &Opcode, SourceLoc &OpcodeLoc, return false; } +bool SILParser::parseSILDebugInfoExpression(SILDebugInfoExpression &DIExpr) { + if (P.Tok.getText() != "expr") + return true; + + // All operators that we currently support + static const SILDIExprOperator AllOps[] = { + SILDIExprOperator::Dereference, + SILDIExprOperator::Fragment + }; + + do { + P.consumeToken(); + bool FoundOp = false; + auto OpLoc = P.Tok.getLoc(); + for (const auto &Op : AllOps) { + const auto *ExprInfo = SILDIExprInfo::get(Op); + auto OpText = ExprInfo->OpText; + if (OpText != P.Tok.getText()) + continue; + auto NewOperator = SILDIExprElement::createOperator(Op); + DIExpr.push_back(NewOperator); + P.consumeToken(); + + // Ready to parse the operands + for (const auto &OpKind : ExprInfo->OperandKinds) { + if (P.parseToken(tok::colon, diag::expected_sil_colon, + "debug info expression operand")) + return true; + + switch (OpKind) { + case SILDIExprElement::DeclKind: { + SILDeclRef Result; + if (parseSILDeclRef(Result) || !Result.hasDecl()) { + P.diagnose(P.Tok.getLoc(), diag::sil_dbg_expr_expect_operand_kind, + OpText, "declaration"); + return true; + } + auto NewOperand = SILDIExprElement::createDecl(Result.getDecl()); + DIExpr.push_back(NewOperand); + break; + } + default: + P.diagnose(P.Tok.getLoc(), diag::sil_dbg_unknown_expr_part, + "operand kind"); + return true; + } + } + FoundOp = true; + break; + } + + if (!FoundOp) { + P.diagnose(OpLoc, diag::sil_dbg_unknown_expr_part, "operator"); + return true; + } + } while (P.Tok.is(tok::colon)); + + return false; +} + static bool peekSILDebugLocation(Parser &P) { auto T = P.peekToken().getText(); return P.Tok.is(tok::comma) && (T == "loc" || T == "scope"); } bool SILParser::parseSILDebugVar(SILDebugVariable &Var) { + auto parseVariableName = [&, this](bool Consume) -> bool { + if (Consume) + P.consumeToken(); + if (P.Tok.getKind() != tok::string_literal) { + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "string"); + return true; + } + // Drop the double quotes. + StringRef Val = P.Tok.getText().drop_front().drop_back(); + Var.Name = Val; + return false; + }; + while (P.Tok.is(tok::comma) && !peekSILDebugLocation(P)) { P.consumeToken(); StringRef Key = P.Tok.getText(); - if (Key == "name") { - P.consumeToken(); - if (P.Tok.getKind() != tok::string_literal) { - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "string"); + bool NoConsume = false; + if (P.consumeIf(tok::l_paren)) { + if (parseVerbatim("name")) + return true; + if (parseVariableName(/*Consume=*/false)) return true; + P.consumeToken(); + + // Optional operands + if (peekSILDebugLocation(P)) { + P.consumeToken(tok::comma); + + bool requireScope = false; + if (P.Tok.getText() == "loc") { + SILLocation VarLoc = RegularLocation::getAutoGeneratedLocation(); + if (parseSILLocation(VarLoc)) + return true; + Var.Loc = VarLoc; + requireScope = P.consumeIf(tok::comma); + } + + if (P.Tok.getText() == "scope" || requireScope) { + parseVerbatim("scope"); + SILDebugScope *DS = nullptr; + if (parseScopeRef(DS)) + return true; + if (DS) + Var.Scope = DS; + } } - // Drop the double quotes. - StringRef Val = P.Tok.getText().drop_front().drop_back(); - Var.Name = Val; + + if (P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")")) + return true; + + NoConsume = true; + } else if (Key == "name") { + if (parseVariableName(/*Consume=*/true)) + return true; } else if (Key == "argno") { P.consumeToken(); if (P.Tok.getKind() != tok::integer_literal) { @@ -1632,17 +1774,33 @@ bool SILParser::parseSILDebugVar(SILDebugVariable &Var) { if (parseIntegerLiteral(P.Tok.getText(), 0, ArgNo)) return true; Var.ArgNo = ArgNo; + } else if (Key == "expr") { + if (parseSILDebugInfoExpression(Var.DIExpr)) + return true; + NoConsume = true; + } else if (Key == "type") { + // Auxiliary type information + P.consumeToken(); + SILType Ty; + if (parseSILType(Ty)) + return true; + Var.Type = Ty; + NoConsume = true; } else if (Key == "let") { Var.Constant = true; } else if (Key == "var") { - Var.Constant = false; + Var.Constant = false; } else if (Key == "loc") { - Var.Constant = false; + Var.Constant = false; + } else if (Key == "implicit") { + Var.Implicit = true; } else { P.diagnose(P.Tok, diag::sil_dbg_unknown_key, Key); return true; } - P.consumeToken(); + + if (!NoConsume) + P.consumeToken(); } return false; } @@ -1754,8 +1912,8 @@ SubstitutionMap getApplySubstitutionsFromParsed( // Ensure that we have the right number of type arguments. auto genericSig = env->getGenericSignature(); - if (parses.size() != genericSig->getGenericParams().size()) { - bool hasTooFew = parses.size() < genericSig->getGenericParams().size(); + if (parses.size() != genericSig.getGenericParams().size()) { + bool hasTooFew = parses.size() < genericSig.getGenericParams().size(); SP.P.diagnose(loc, hasTooFew ? diag::sil_missing_substitutions : diag::sil_too_many_substitutions); @@ -1771,7 +1929,7 @@ SubstitutionMap getApplySubstitutionsFromParsed( return nullptr; auto index = genericSig->getGenericParamOrdinal(genericParam); - assert(index < genericSig->getGenericParams().size()); + assert(index < genericSig.getGenericParams().size()); assert(index < parses.size()); // Provide the replacement type. @@ -1892,84 +2050,6 @@ bool SILParser::parseSILDebugLocation(SILLocation &L, SILBuilder &B, return false; } -static bool parseLoadOwnershipQualifier(LoadOwnershipQualifier &Result, - SILParser &P) { - StringRef Str; - // If we do not parse '[' ... ']', we have unqualified. Set value and return. - if (!parseSILOptional(Str, P)) { - Result = LoadOwnershipQualifier::Unqualified; - return false; - } - - // Then try to parse one of our other qualifiers. We do not support parsing - // unqualified here so we use that as our fail value. - auto Tmp = llvm::StringSwitch(Str) - .Case("take", LoadOwnershipQualifier::Take) - .Case("copy", LoadOwnershipQualifier::Copy) - .Case("trivial", LoadOwnershipQualifier::Trivial) - .Default(LoadOwnershipQualifier::Unqualified); - - // Thus return true (following the conventions in this file) if we fail. - if (Tmp == LoadOwnershipQualifier::Unqualified) - return true; - - // Otherwise, assign Result and return false. - Result = Tmp; - return false; -} - -static bool parseStoreOwnershipQualifier(StoreOwnershipQualifier &Result, - SILParser &P) { - StringRef Str; - // If we do not parse '[' ... ']', we have unqualified. Set value and return. - if (!parseSILOptional(Str, P)) { - Result = StoreOwnershipQualifier::Unqualified; - return false; - } - - // Then try to parse one of our other qualifiers. We do not support parsing - // unqualified here so we use that as our fail value. - auto Tmp = llvm::StringSwitch(Str) - .Case("init", StoreOwnershipQualifier::Init) - .Case("assign", StoreOwnershipQualifier::Assign) - .Case("trivial", StoreOwnershipQualifier::Trivial) - .Default(StoreOwnershipQualifier::Unqualified); - - // Thus return true (following the conventions in this file) if we fail. - if (Tmp == StoreOwnershipQualifier::Unqualified) - return true; - - // Otherwise, assign Result and return false. - Result = Tmp; - return false; -} - -static bool parseAssignOwnershipQualifier(AssignOwnershipQualifier &Result, - SILParser &P) { - StringRef Str; - // If we do not parse '[' ... ']', we have unknown. Set value and return. - if (!parseSILOptional(Str, P)) { - Result = AssignOwnershipQualifier::Unknown; - return false; - } - - // Then try to parse one of our other initialization kinds. We do not support - // parsing unknown here so we use that as our fail value. - auto Tmp = llvm::StringSwitch(Str) - .Case("reassign", AssignOwnershipQualifier::Reassign) - .Case("reinit", AssignOwnershipQualifier::Reinit) - .Case("init", AssignOwnershipQualifier::Init) - .Default(AssignOwnershipQualifier::Unknown); - - // Thus return true (following the conventions in this file) if we fail. - if (Tmp == AssignOwnershipQualifier::Unknown) - return true; - - // Otherwise, assign Result and return false. - Result = Tmp; - return false; -} - static bool parseAssignByWrapperMode(AssignByWrapperInst::Mode &Result, SILParser &P) { StringRef Str; @@ -2084,9 +2164,9 @@ static bool parseSILDifferentiabilityWitnessConfigAndFunction( // generic have the same generic parameters. auto areGenericParametersConsistent = [&]() { llvm::SmallDenseSet genericParamKeys; - for (auto *origGP : origGenSig->getGenericParams()) - genericParamKeys.insert(GenericParamKey(origGP)); - for (auto *witnessGP : witnessGenSig->getGenericParams()) + for (auto origGP : origGenSig.getGenericParams()) + genericParamKeys.insert(GenericParamKey(origGP.getPointer())); + for (auto *witnessGP : witnessGenSig.getGenericParams()) if (!genericParamKeys.erase(GenericParamKey(witnessGP))) return false; return genericParamKeys.empty(); @@ -2100,8 +2180,8 @@ static bool parseSILDifferentiabilityWitnessConfigAndFunction( // Combine parsed witness requirements with original function generic // signature requirements to form full witness generic signature. SmallVector witnessRequirements( - witnessGenSig->getRequirements().begin(), - witnessGenSig->getRequirements().end()); + witnessGenSig.getRequirements().begin(), + witnessGenSig.getRequirements().end()); witnessGenSig = evaluateOrDefault( P.Context.evaluator, AbstractGenericSignatureRequest{origGenSig.getPointer(), @@ -2518,9 +2598,10 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, auto parseForwardingOwnershipKind = [&](ValueOwnershipKind &forwardingKind) -> bool { - if (P.consumeIf(tok::comma)) { - return parseVerbatim("forwarding") || - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || + if (P.consumeIf(tok::comma) && + P.Tok.is(tok::identifier) && P.Tok.getText() == "forwarding") { + P.consumeToken(); + return P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || parseSILOwnership(forwardingKind); } return false; @@ -2942,7 +3023,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = Val.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = B.createOpenExistentialBoxValue(InstLoc, Val, Ty, forwardingOwnership); @@ -2962,7 +3043,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = Val.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = @@ -2976,7 +3057,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = Val.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = @@ -3022,7 +3103,6 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, UNARY_INSTRUCTION(EndBorrow) UNARY_INSTRUCTION(DestructureStruct) UNARY_INSTRUCTION(DestructureTuple) - UNARY_INSTRUCTION(HopToExecutor) UNARY_INSTRUCTION(ExtractExecutor) REFCOUNTING_INSTRUCTION(UnmanagedReleaseValue) REFCOUNTING_INSTRUCTION(UnmanagedRetainValue) @@ -3046,6 +3126,14 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, #undef UNARY_INSTRUCTION #undef REFCOUNTING_INSTRUCTION + case SILInstructionKind::HopToExecutorInst: { + bool mandatory = false; + if (parseSILOptional(mandatory, *this, "mandatory") + || parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createHopToExecutor(InstLoc, Val, mandatory); + break; + } case SILInstructionKind::DestroyValueInst: { bool poisonRefs = false; if (parseSILOptional(poisonRefs, *this, "poison") @@ -3085,20 +3173,16 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, break; } - case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: { + case SILInstructionKind::DebugValueInst: { bool poisonRefs = false; SILDebugVariable VarInfo; if (parseSILOptional(poisonRefs, *this, "poison") || parseTypedValueRef(Val, B) || parseSILDebugVar(VarInfo) || parseSILDebugLocation(InstLoc, B)) return true; - if (Opcode == SILInstructionKind::DebugValueInst) - ResultVal = B.createDebugValue(InstLoc, Val, VarInfo, poisonRefs); - else { - assert(!poisonRefs && "debug_value_addr does not support poison"); - ResultVal = B.createDebugValueAddr(InstLoc, Val, VarInfo); - } + if (Val->getType().isAddress()) + assert(!poisonRefs && "debug_value w/ address value does not support poison"); + ResultVal = B.createDebugValue(InstLoc, Val, VarInfo, poisonRefs); break; } @@ -3126,15 +3210,23 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, } case SILInstructionKind::LoadInst: { - LoadOwnershipQualifier Qualifier; + Optional Qualifier; SourceLoc AddrLoc; - - if (parseLoadOwnershipQualifier(Qualifier, *this) || - parseTypedValueRef(Val, AddrLoc, B) || - parseSILDebugLocation(InstLoc, B)) + auto parseLoadOwnership = [](StringRef Str) { + return llvm::StringSwitch>(Str) + .Case("take", LoadOwnershipQualifier::Take) + .Case("copy", LoadOwnershipQualifier::Copy) + .Case("trivial", LoadOwnershipQualifier::Trivial) + .Default(None); + }; + if (parseSILQualifier(Qualifier, parseLoadOwnership) + || parseTypedValueRef(Val, AddrLoc, B) + || parseSILDebugLocation(InstLoc, B)) { return true; - - ResultVal = B.createLoad(InstLoc, Val, Qualifier); + } + if (!Qualifier) + Qualifier = LoadOwnershipQualifier::Unqualified; + ResultVal = B.createLoad(InstLoc, Val, Qualifier.getValue()); break; } @@ -3152,11 +3244,21 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, case SILInstructionKind::BeginBorrowInst: { SourceLoc AddrLoc; + bool defined = false; + StringRef attributeName; + + if (parseSILOptional(attributeName, *this)) { + if (attributeName.equals("defined")) + defined = true; + else + return true; + } + if (parseTypedValueRef(Val, AddrLoc, B) || parseSILDebugLocation(InstLoc, B)) return true; - ResultVal = B.createBeginBorrow(InstLoc, Val); + ResultVal = B.createBeginBorrow(InstLoc, Val, defined); break; } @@ -3195,7 +3297,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = Val.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = B.createMarkDependence(InstLoc, Val, Base, forwardingOwnership); @@ -3394,7 +3496,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, return true; } - if (parseSILDebugLocation(InstLoc, B)) { + if (parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) { return true; } @@ -3513,7 +3615,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = Val.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = @@ -3591,7 +3693,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = Val.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; auto opaque = Lowering::AbstractionPattern::getOpaque(); @@ -3674,7 +3776,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = Val.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = @@ -3702,8 +3804,8 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, SourceLoc ToLoc, AddrLoc; Identifier ToToken; SILValue AddrVal; - StoreOwnershipQualifier StoreQualifier; - AssignOwnershipQualifier AssignQualifier; + Optional StoreQualifier; + Optional AssignQualifier; bool IsStore = Opcode == SILInstructionKind::StoreInst; bool IsAssign = Opcode == SILInstructionKind::AssignInst; if (parseValueName(From) || @@ -3711,10 +3813,28 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, "to")) return true; - if (IsStore && parseStoreOwnershipQualifier(StoreQualifier, *this)) + auto parseStoreOwnership = [](StringRef Str) { + return llvm::StringSwitch>(Str) + .Case("init", StoreOwnershipQualifier::Init) + .Case("assign", StoreOwnershipQualifier::Assign) + .Case("trivial", StoreOwnershipQualifier::Trivial) + .Default(None); + }; + if (IsStore + && parseSILQualifier(StoreQualifier, + parseStoreOwnership)) return true; - if (IsAssign && parseAssignOwnershipQualifier(AssignQualifier, *this)) + auto parseAssignOwnership = [](StringRef Str) { + return llvm::StringSwitch>(Str) + .Case("reassign", AssignOwnershipQualifier::Reassign) + .Case("reinit", AssignOwnershipQualifier::Reinit) + .Case("init", AssignOwnershipQualifier::Init) + .Default(None); + }; + if (IsAssign + && parseSILQualifier(AssignQualifier, + parseAssignOwnership)) return true; if (parseTypedValueRef(AddrVal, AddrLoc, B) || @@ -3735,15 +3855,19 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, SILType ValType = AddrVal->getType().getObjectType(); if (IsStore) { + if (!StoreQualifier) + StoreQualifier = StoreOwnershipQualifier::Unqualified; ResultVal = B.createStore(InstLoc, getLocalValue(From, ValType, InstLoc, B), - AddrVal, StoreQualifier); + AddrVal, StoreQualifier.getValue()); } else { assert(IsAssign); + if (!AssignQualifier) + AssignQualifier = AssignOwnershipQualifier::Unknown; ResultVal = B.createAssign(InstLoc, getLocalValue(From, ValType, InstLoc, B), - AddrVal, AssignQualifier); + AddrVal, AssignQualifier.getValue()); } break; @@ -3979,7 +4103,12 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, SILDebugVariable VarInfo; if (parseSILDebugVar(VarInfo) || parseSILDebugLocation(InstLoc, B)) return true; - ResultVal = B.createAllocStack(InstLoc, Ty, VarInfo, hasDynamicLifetime); + // It doesn't make sense to attach a debug var info if the name is empty + if (VarInfo.Name.size()) + ResultVal = + B.createAllocStack(InstLoc, Ty, VarInfo, hasDynamicLifetime); + else + ResultVal = B.createAllocStack(InstLoc, Ty, {}, hasDynamicLifetime); } else { assert(Opcode == SILInstructionKind::MetatypeInst); if (parseSILDebugLocation(InstLoc, B)) @@ -4148,7 +4277,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, : ValueOwnershipKind(OwnershipKind::None); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = B.createTuple(InstLoc, Ty2, OpList, forwardingOwnership); @@ -4222,7 +4351,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, : ValueOwnershipKind(OwnershipKind::None); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = @@ -4244,7 +4373,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, if (Opcode == SILInstructionKind::UncheckedEnumDataInst) parseForwardingOwnershipKind(forwardingOwnership); - if (parseSILDebugLocation(InstLoc, B)) + if (parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; EnumElementDecl *Elt = cast(EltRef.getDecl()); auto ResultTy = Operand->getType().getEnumElementType( @@ -4303,7 +4432,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, return true; } - if (parseSILDebugLocation(InstLoc, B)) + if (parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; auto ResultTy = TT->getElement(Field).getType()->getCanonicalType(); if (Opcode == SILInstructionKind::TupleElementAddrInst) @@ -4579,7 +4708,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, F && F->hasOwnership() ? mergeSILValueOwnership(OpList) : ValueOwnershipKind(OwnershipKind::None); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) { + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) { return true; } if (Opcode == SILInstructionKind::StructInst) { @@ -4605,7 +4734,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, return true; } - if (parseSILDebugLocation(InstLoc, B)) + if (parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; if (!FieldV || !isa(FieldV)) { P.diagnose(NameLoc, diag::sil_struct_inst_wrong_field); @@ -4821,7 +4950,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, if (parseForwardingOwnershipKind(forwardingOwnership)) return true; } - if (parseSILDebugLocation(InstLoc, B)) + if (parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; // Resolve the results. @@ -4884,7 +5013,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, return true; } - if (parseSILDebugLocation(InstLoc, B)) + if (parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; if (Opcode == SILInstructionKind::SwitchEnumInst) { ResultVal = B.createSwitchEnum(InstLoc, Val, DefaultBB, CaseBBs, None, @@ -5134,7 +5263,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = Val.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ArrayRef conformances = @@ -5302,7 +5431,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership(OwnershipKind::None); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; auto *parameterIndices = IndexSubset::get( P.Context, fnType->getNumParameters(), rawParameterIndices); @@ -5351,7 +5480,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership(OwnershipKind::None); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; auto *parameterIndicesSubset = IndexSubset::get( @@ -5397,7 +5526,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = functionOperand.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = B.createDifferentiableFunctionExtract( @@ -5425,7 +5554,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ValueOwnershipKind forwardingOwnership = functionOperand.getOwnershipKind(); if (parseForwardingOwnershipKind(forwardingOwnership) || - parseSILDebugLocation(InstLoc, B)) + parseSILDebugLocation(InstLoc, B, /*parsedComma=*/ true)) return true; ResultVal = B.createLinearFunctionExtract( InstLoc, extractee, functionOperand, forwardingOwnership); diff --git a/lib/SIL/Utils/BasicBlockUtils.cpp b/lib/SIL/Utils/BasicBlockUtils.cpp index beb81421d4429..0b172c9bb5d27 100644 --- a/lib/SIL/Utils/BasicBlockUtils.cpp +++ b/lib/SIL/Utils/BasicBlockUtils.cpp @@ -174,7 +174,8 @@ void swift::getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB, if (!succBB->getNumArguments()) return; args.push_back(newEdgeBB->createPhiArgument( - succBB->getArgument(0)->getType(), OwnershipKind::Owned)); + succBB->getArgument(0)->getType(), + succBB->getArgument(0)->getOwnershipKind())); return; } @@ -186,7 +187,8 @@ void swift::getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB, if (!succBB->getNumArguments()) return; args.push_back(newEdgeBB->createPhiArgument( - succBB->getArgument(0)->getType(), OwnershipKind::Owned)); + succBB->getArgument(0)->getType(), + succBB->getArgument(0)->getOwnershipKind())); return; } @@ -366,6 +368,15 @@ void swift::mergeBasicBlockWithSingleSuccessor(SILBasicBlock *BB, // DeadEndBlocks //===----------------------------------------------------------------------===// +// Propagate the reachability up the control flow graph. +void DeadEndBlocks::propagateNewlyReachableBlocks(unsigned startIdx) { + for (unsigned idx = startIdx; idx < reachableBlocks.size(); ++idx) { + const SILBasicBlock *bb = reachableBlocks[idx]; + for (SILBasicBlock *predBB : bb->getPredecessorBlocks()) + reachableBlocks.insert(predBB); + } +} + void DeadEndBlocks::compute() { assert(reachableBlocks.empty() && "Computed twice"); @@ -377,13 +388,19 @@ void DeadEndBlocks::compute() { if (TI->isFunctionExiting()) reachableBlocks.insert(&BB); } - // Propagate the reachability up the control flow graph. - unsigned Idx = 0; - while (Idx < reachableBlocks.size()) { - const SILBasicBlock *BB = reachableBlocks[Idx++]; - for (SILBasicBlock *Pred : BB->getPredecessorBlocks()) - reachableBlocks.insert(Pred); + propagateNewlyReachableBlocks(0); +} + +void DeadEndBlocks::updateForReachableBlock(SILBasicBlock *reachableBB) { + if (!didComputeValue) + return; + + assert(reachableBlocks.count(reachableBB)); + unsigned numReachable = reachableBlocks.size(); + for (SILBasicBlock *predBB : reachableBB->getPredecessorBlocks()) { + reachableBlocks.insert(predBB); } + propagateNewlyReachableBlocks(numReachable); } bool DeadEndBlocks::triviallyEndsInUnreachable(SILBasicBlock *block) { diff --git a/lib/SIL/Utils/CMakeLists.txt b/lib/SIL/Utils/CMakeLists.txt index de70d19e19e14..35efd6d4d4ee7 100644 --- a/lib/SIL/Utils/CMakeLists.txt +++ b/lib/SIL/Utils/CMakeLists.txt @@ -13,6 +13,7 @@ target_sources(swiftSIL PRIVATE OwnershipUtils.cpp PrettyStackTrace.cpp Projection.cpp + SILBridging.cpp SILInstructionWorklist.cpp SILRemarkStreamer.cpp ValueUtils.cpp) diff --git a/lib/SIL/Utils/DynamicCasts.cpp b/lib/SIL/Utils/DynamicCasts.cpp index aaceaac6f51e7..f0950b9821822 100644 --- a/lib/SIL/Utils/DynamicCasts.cpp +++ b/lib/SIL/Utils/DynamicCasts.cpp @@ -603,12 +603,12 @@ swift::classifyDynamicCast(ModuleDecl *M, if (targetClass) { // Imported Objective-C generics don't check the generic parameters, which // are lost at runtime. - if (sourceClass->usesObjCGenericsModel()) { + if (sourceClass->isTypeErasedGenericClass()) { if (sourceClass == targetClass) return DynamicCastFeasibility::WillSucceed; - if (targetClass->usesObjCGenericsModel()) { + if (targetClass->isTypeErasedGenericClass()) { // If both classes are ObjC generics, the cast may succeed if the // classes are related, irrespective of their generic parameters. diff --git a/lib/SIL/Utils/GenericSpecializationMangler.cpp b/lib/SIL/Utils/GenericSpecializationMangler.cpp index beae0d6481b22..eaf2d7fd91f04 100644 --- a/lib/SIL/Utils/GenericSpecializationMangler.cpp +++ b/lib/SIL/Utils/GenericSpecializationMangler.cpp @@ -79,7 +79,7 @@ appendSubstitutions(GenericSignature sig, SubstitutionMap subs) { bool First = true; sig->forEachParam([&](GenericTypeParamType *ParamType, bool Canonical) { if (Canonical) { - appendType(Type(ParamType).subst(subs)->getCanonicalType()); + appendType(Type(ParamType).subst(subs)->getCanonicalType(), nullptr); appendListSeparator(First); } }); @@ -127,7 +127,7 @@ getSubstitutionMapForPrespecialization(GenericSignature genericSig, GenericSignature specSig) { auto CalleeGenericSig = genericSig; auto SpecializedGenericSig = specSig; - auto SpecializedGenericEnv = specSig->getGenericEnvironment(); + auto SpecializedGenericEnv = specSig.getGenericEnvironment(); auto CalleeInterfaceToSpecializedInterfaceMap = SubstitutionMap::get( CalleeGenericSig, diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 43c463889dc56..cc7dcc2b5c453 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -62,20 +62,6 @@ SILValue swift::getUnderlyingObject(SILValue v) { } } -SILValue -swift::getUnderlyingObjectStoppingAtObjectToAddrProjections(SILValue v) { - if (!v->getType().isAddress()) - return SILValue(); - - while (true) { - auto v2 = lookThroughAddressToAddressProjections(v); - v2 = stripIndexingInsts(v2); - if (v2 == v) - return v2; - v = v2; - } -} - SILValue swift::getUnderlyingObjectStopAtMarkDependence(SILValue v) { while (true) { SILValue v2 = stripCastsWithoutMarkDependence(v); @@ -135,6 +121,9 @@ SILValue swift::stripSinglePredecessorArgs(SILValue V) { SILValue swift::stripCastsWithoutMarkDependence(SILValue v) { while (true) { v = stripSinglePredecessorArgs(v); + if (isa(v)) + return v; + if (auto *svi = dyn_cast(v)) { if (isRCIdentityPreservingCast(svi) || isa(v) @@ -291,14 +280,13 @@ bool swift::isEndOfScopeMarker(SILInstruction *user) { return false; case SILInstructionKind::EndAccessInst: case SILInstructionKind::EndBorrowInst: - case SILInstructionKind::EndLifetimeInst: return true; } } bool swift::isIncidentalUse(SILInstruction *user) { return isEndOfScopeMarker(user) || user->isDebugInstruction() || - isa(user); + isa(user) || isa(user); } bool swift::onlyAffectsRefCount(SILInstruction *user) { diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index cf7c335093d4d..9e8bd64c7ad7a 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -390,6 +390,10 @@ bool swift::isRCIdentityPreservingCast(SingleValueInstruction *svi) { case SILInstructionKind::UnconditionalCheckedCastInst: case SILInstructionKind::UnconditionalCheckedCastValueInst: return SILDynamicCastInst(svi).isRCIdentityPreserving(); + // Ignore markers + case SILInstructionKind::MarkUninitializedInst: + case SILInstructionKind::MarkDependenceInst: + return true; } } @@ -446,7 +450,7 @@ class FindReferenceRoot { } // end anonymous namespace -static SILValue findReferenceRoot(SILValue ref) { +SILValue swift::findReferenceRoot(SILValue ref) { return FindReferenceRoot().findRoot(ref); } @@ -471,12 +475,13 @@ constexpr unsigned AccessedStorage::TailIndex; AccessedStorage::AccessedStorage(SILValue base, Kind kind) { // For kind==Unidentified, base may be an invalid empty or tombstone value. assert(base && "invalid storage base"); - initKind(kind); + initKind(kind, InvalidElementIndex); switch (kind) { - case Box: - assert(isa(base)); - value = base; + case Box: { + auto *projectBox = cast(base); + value = findReferenceRoot(projectBox->getOperand()); break; + } case Stack: assert(isa(base)); value = base; @@ -533,9 +538,6 @@ void AccessedStorage::visitRoots( visitor(root); return; } - if (getKind() == Unidentified) { - return; - } assert(getKind() == Global && function); SILGlobalVariable *global = getGlobal(); for (auto &block : *function) { @@ -567,7 +569,10 @@ void AccessedStorage::setLetAccess(SILValue base) { const ValueDecl *AccessedStorage::getDecl(SILValue base) const { switch (getKind()) { case Box: - return cast(value)->getLoc().getAsASTNode(); + if (auto *allocBox = dyn_cast(value)) + return allocBox->getLoc().getAsASTNode(); + + return nullptr; case Stack: return cast(value)->getDecl(); @@ -689,7 +694,7 @@ class FindAccessedStorageVisitor // `storage` may still be invalid. If both `storage` and `foundStorage` // are invalid, this check passes, but we return an invalid storage // below. - if (!result.storage->hasIdenticalBase(foundStorage)) + if (!result.storage->hasIdenticalStorage(foundStorage)) result.storage = AccessedStorage(); if (result.base != foundBase) result.base = SILValue(); @@ -768,7 +773,7 @@ AccessedStorage AccessedStorage::computeInScope(SILValue sourceAddress) { AccessPath AccessPath::forTailStorage(SILValue rootReference) { return AccessPath( - AccessedStorage::forClass(rootReference, AccessedStorage::TailIndex), + AccessedStorage::forObjectTail(rootReference), PathNode(rootReference->getModule()->getIndexTrieRoot()), /*offset*/ 0); } @@ -777,7 +782,7 @@ bool AccessPath::contains(AccessPath subPath) const { if (!isValid() || !subPath.isValid()) { return false; } - if (!storage.hasIdenticalBase(subPath.storage)) { + if (!storage.hasIdenticalStorage(subPath.storage)) { return false; } // Does the offset index match? @@ -937,6 +942,8 @@ class AccessPathVisitor : public FindAccessVisitorImpl { assert(isa(projectedAddr) || isa(projectedAddr) || isa(projectedAddr) + // project_box is not normally an access projection but we treat it + // as such when it operates on unchecked_take_enum_data_addr. || isa(projectedAddr)); } return sourceAddr->get(); @@ -1015,7 +1022,8 @@ LLVM_ATTRIBUTE_USED void AccessPath::dump() const { print(llvm::dbgs()); } void AccessPathWithBase::print(raw_ostream &os) const { if (base) os << "Base: " << base; - + else + os << "Base: unidentified\n"; accessPath.print(os); } @@ -1227,7 +1235,7 @@ bool AccessPathDefUseTraversal::pushPhiUses(const SILPhiArgument *phi, // looking through it. If the phi input differ the its storage is invalid. auto phiPath = AccessPath::compute(phi); if (phiPath.isValid()) { - assert(phiPath.getStorage().hasIdenticalBase(storage) + assert(phiPath.getStorage().hasIdenticalStorage(storage) && "inconsistent phi storage"); // If the phi paths have different offsets, its path has unknown offset. if (phiPath.getOffset() == AccessPath::UnknownOffset) { @@ -1256,7 +1264,7 @@ int AccessPathDefUseTraversal::getPathOffset(const DFSEntry &dfs) const { bool AccessPathDefUseTraversal::checkAndUpdateOffset(DFSEntry &dfs) { int pathOffset = getPathOffset(dfs); if (dfs.offset == AccessPath::UnknownOffset) { - if (pathOffset > 0) { + if (pathOffset != 0) { // Pop the offset from the expected path; there should only be // one. Continue matching subobject indices even after seeing an unknown // offset. A subsequent mismatching subobject index is still considered @@ -1353,8 +1361,8 @@ AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi, pushUsers(svi, dfs); return IgnoredUse; - // Handle ref_element_addr since we start at the object root instead of - // the access base. + // Handle ref_element_addr, ref_tail_addr, and project_box since we start at + // the object root instead of the access base. case SILInstructionKind::RefElementAddrInst: assert(dfs.isRef()); assert(dfs.pathCursor > 0 && "ref_element_addr cannot occur within access"); @@ -1375,6 +1383,13 @@ AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi, return IgnoredUse; } + case SILInstructionKind::ProjectBoxInst: { + assert(dfs.isRef()); + dfs.useAndIsRef.setInt(false); + pushUsers(svi, dfs); + return IgnoredUse; + } + // MARK: Access projections case SILInstructionKind::StructElementAddrInst: @@ -1433,8 +1448,10 @@ AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi, assert(isa(addrOper->get())); // Push the project_box uses for (auto *use : svi->getUses()) { - if (isa(use->getUser())) - pushUser(DFSEntry(use, dfs.isRef(), dfs.pathCursor, dfs.offset)); + if (auto *projectBox = dyn_cast(use->getUser())) { + assert(!dfs.isRef() && "originates from an enum address"); + pushUsers(projectBox, dfs); + } } } return LeafUse; @@ -1612,9 +1629,12 @@ swift::getSingleInitAllocStackUse(AllocStackInst *asi, if (destroyingUses) destroyingUses->push_back(use); continue; + case SILInstructionKind::DebugValueInst: + if (cast(user)->hasAddrVal()) + continue; + break; case SILInstructionKind::DeallocStackInst: case SILInstructionKind::LoadBorrowInst: - case SILInstructionKind::DebugValueAddrInst: continue; } @@ -1698,8 +1718,8 @@ void swift::checkSwitchEnumBlockArg(SILPhiArgument *arg) { } } -bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage, - SILFunction *F) { +bool swift::isPossibleFormalAccessStorage(const AccessedStorage &storage, + SILFunction *F) { switch (storage.getKind()) { case AccessedStorage::Nested: assert(false && "don't pass nested storage to this helper"); @@ -1767,7 +1787,9 @@ SILBasicBlock::iterator swift::removeBeginAccess(BeginAccessInst *beginAccess) { op->set(beginAccess->getSource()); } } - return beginAccess->getParent()->erase(beginAccess); + auto nextIter = std::next(beginAccess->getIterator()); + beginAccess->getParent()->erase(beginAccess); + return nextIter; } //===----------------------------------------------------------------------===// @@ -1852,15 +1874,18 @@ static void visitBuiltinAddress(BuiltinInst *builtin, case BuiltinValueKind::IntInstrprofIncrement: case BuiltinValueKind::TSanInoutAccess: case BuiltinValueKind::CancelAsyncTask: - case BuiltinValueKind::CreateAsyncTaskFuture: - case BuiltinValueKind::CreateAsyncTaskGroupFuture: + case BuiltinValueKind::CreateAsyncTask: + case BuiltinValueKind::CreateAsyncTaskInGroup: case BuiltinValueKind::AutoDiffCreateLinearMapContext: case BuiltinValueKind::AutoDiffAllocateSubcontext: case BuiltinValueKind::InitializeDefaultActor: + case BuiltinValueKind::InitializeDistributedRemoteActor: case BuiltinValueKind::DestroyDefaultActor: case BuiltinValueKind::GetCurrentExecutor: case BuiltinValueKind::StartAsyncLet: + case BuiltinValueKind::StartAsyncLetWithLocalBuffer: case BuiltinValueKind::EndAsyncLet: + case BuiltinValueKind::EndAsyncLetLifetime: case BuiltinValueKind::CreateTaskGroup: case BuiltinValueKind::DestroyTaskGroup: return; diff --git a/lib/SIL/Utils/MemoryLocations.cpp b/lib/SIL/Utils/MemoryLocations.cpp index bc9618a048b8c..1b064a8cdf632 100644 --- a/lib/SIL/Utils/MemoryLocations.cpp +++ b/lib/SIL/Utils/MemoryLocations.cpp @@ -357,6 +357,15 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx if (!handleNonTrivialProjections && hasInoutArgument(ApplySite(user))) return false; break; + case SILInstructionKind::LoadBorrowInst: + // Reborrows are not handled + if (!cast(user)->getUsersOfType().empty()) + return false; + break; + case SILInstructionKind::DebugValueInst: + if (cast(user)->hasAddrVal()) + break; + return false; case SILInstructionKind::InjectEnumAddrInst: case SILInstructionKind::SelectEnumAddrInst: case SILInstructionKind::ExistentialMetatypeInst: @@ -367,7 +376,6 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx case SILInstructionKind::StoreInst: case SILInstructionKind::StoreBorrowInst: case SILInstructionKind::EndAccessInst: - case SILInstructionKind::LoadBorrowInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::CheckedCastAddrBranchInst: case SILInstructionKind::UncheckedRefCastAddrInst: @@ -375,7 +383,6 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::CopyAddrInst: case SILInstructionKind::YieldInst: case SILInstructionKind::DeallocStackInst: diff --git a/lib/SIL/Utils/OptimizationRemark.cpp b/lib/SIL/Utils/OptimizationRemark.cpp index 82f590c925045..a134371374474 100644 --- a/lib/SIL/Utils/OptimizationRemark.cpp +++ b/lib/SIL/Utils/OptimizationRemark.cpp @@ -135,15 +135,28 @@ static bool hasForceEmitSemanticAttr(SILFunction &fn, StringRef passName) { }); } +static bool isMethodWithForceEmitSemanticAttrNominalType(SILFunction &fn) { + if (!fn.hasSelfParam()) + return false; + + auto selfType = fn.getSelfArgument()->getType(); + auto *nomType = selfType.getNominalOrBoundGenericNominal(); + if (!nomType) + return false; + return nomType->shouldEmitAssemblyVisionRemarksOnMethods(); +} + Emitter::Emitter(StringRef passName, SILFunction &fn) : fn(fn), passName(passName), passedEnabled( hasForceEmitSemanticAttr(fn, passName) || + isMethodWithForceEmitSemanticAttrNominalType(fn) || (fn.getASTContext().LangOpts.OptimizationRemarkPassedPattern && fn.getASTContext().LangOpts.OptimizationRemarkPassedPattern->match( passName))), missedEnabled( hasForceEmitSemanticAttr(fn, passName) || + isMethodWithForceEmitSemanticAttrNominalType(fn) || (fn.getASTContext().LangOpts.OptimizationRemarkMissedPattern && fn.getASTContext().LangOpts.OptimizationRemarkMissedPattern->match( passName))) {} @@ -158,6 +171,20 @@ static SourceLoc getLocForPresentation(SILLocation loc, case SourceLocPresentationKind::EndRange: return loc.getEndSourceLoc(); } + llvm_unreachable("covered switch"); +} + +static bool instHasInferrableLoc(SILInstruction &inst) { + if (isa(inst) || isa(inst) || + isa(inst) || isa(inst) || + isa(inst) || isa(inst)) + return false; + + // Ignore empty tuples + if (auto *tup = dyn_cast(&inst)) + return tup->getNumOperands() != 0; + + return true; } /// The user has passed us an instruction that for some reason has a source loc @@ -168,11 +195,16 @@ inferOptRemarkSearchForwards(SILInstruction &i, SourceLocPresentationKind presentationKind) { for (auto &inst : llvm::make_range(std::next(i.getIterator()), i.getParent()->end())) { + if (!instHasInferrableLoc(inst)) + continue; + // Skip instructions without a loc we care about since we move it around. auto newLoc = getLocForPresentation(inst.getLoc(), presentationKind); if (auto inlinedLoc = inst.getDebugScope()->getOutermostInlineLocation()) newLoc = getLocForPresentation(inlinedLoc, presentationKind); - if (newLoc.isValid()) + if (newLoc.isValid()) { + LLVM_DEBUG(llvm::dbgs() << "Inferring loc from " << inst); return newLoc; + } } return SourceLoc(); @@ -187,11 +219,15 @@ inferOptRemarkSearchBackwards(SILInstruction &i, SourceLocPresentationKind presentationKind) { for (auto &inst : llvm::make_range(std::next(i.getReverseIterator()), i.getParent()->rend())) { + if (!instHasInferrableLoc(inst)) + continue; auto loc = inst.getLoc(); if (auto inlinedLoc = inst.getDebugScope()->getOutermostInlineLocation()) loc = inlinedLoc; - if (auto result = getLocForPresentation(loc, presentationKind)) + if (auto result = getLocForPresentation(loc, presentationKind)) { + LLVM_DEBUG(llvm::dbgs() << "Inferring loc from " << inst); return result; + } } return SourceLoc(); diff --git a/lib/SIL/Utils/OwnershipUtils.cpp b/lib/SIL/Utils/OwnershipUtils.cpp index f1c86c45fc855..9178350263a62 100644 --- a/lib/SIL/Utils/OwnershipUtils.cpp +++ b/lib/SIL/Utils/OwnershipUtils.cpp @@ -16,6 +16,7 @@ #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/LinearLifetimeChecker.h" #include "swift/SIL/Projection.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILInstruction.h" @@ -447,6 +448,7 @@ BorrowedValue BorrowingOperand::getBorrowIntroducingUserResult() { return BorrowedValue(bi->getDestBB()->getArgument(op->getOperandNumber())); } } + llvm_unreachable("covered switch"); } void BorrowingOperand::getImplicitUses( @@ -716,7 +718,8 @@ bool InteriorPointerOperand::findTransitiveUsesForAddress( isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || - isa(user) || isa(user)) { + isa(user) || isa(user) || + isa(user)) { continue; } @@ -728,7 +731,8 @@ bool InteriorPointerOperand::findTransitiveUsesForAddress( isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || - isa(user)) { + isa(user) + || isa(user)) { for (SILValue r : user->getResults()) { llvm::copy(r->getUses(), std::back_inserter(worklist)); } @@ -785,6 +789,46 @@ bool InteriorPointerOperand::findTransitiveUsesForAddress( return foundError; } +// Determine whether \p address may be in a borrow scope and if so record the +// InteriorPointerOperand that produces the address from a guaranteed value. +BorrowedAddress::BorrowedAddress(SILValue address) { + assert(address->getType().isAddress()); + + auto storageWithBase = AccessedStorageWithBase::compute(address); + switch (storageWithBase.storage.getKind()) { + case AccessedStorage::Argument: + case AccessedStorage::Stack: + case AccessedStorage::Global: + // Unidentified storage is from an "escaped pointer", so it need not be + // restricted to a borrow scope. + case AccessedStorage::Unidentified: + this->mayBeBorrowed = false; + return; + case AccessedStorage::Box: + case AccessedStorage::Yield: + case AccessedStorage::Class: + case AccessedStorage::Tail: + // The base object might be within a borrow scope. + break; + case AccessedStorage::Nested: + llvm_unreachable("unexpected storage"); + }; + auto base = storageWithBase.base; + if (!base) + return; // bail on unexpected patterns + + auto intPtrOp = InteriorPointerOperand::inferFromResult(base); + if (!intPtrOp) + return; + + SILValue nonAddressValue = intPtrOp.operand->get(); + if (nonAddressValue->getOwnershipKind() != OwnershipKind::Guaranteed) { + this->mayBeBorrowed = false; + return; + } + this->interiorPointerOp = intPtrOp; +} + //===----------------------------------------------------------------------===// // Owned Value Introducers //===----------------------------------------------------------------------===// @@ -1327,10 +1371,10 @@ void swift::findTransitiveReborrowBaseValuePairs( } void swift::visitTransitiveEndBorrows( - BeginBorrowInst *borrowInst, + BorrowedValue beginBorrow, function_ref visitEndBorrow) { SmallSetVector worklist; - worklist.insert(borrowInst); + worklist.insert(beginBorrow.value); while (!worklist.empty()) { auto val = worklist.pop_back_val(); diff --git a/lib/SIL/Utils/SILBridging.cpp b/lib/SIL/Utils/SILBridging.cpp new file mode 100644 index 0000000000000..42261c507b2e6 --- /dev/null +++ b/lib/SIL/Utils/SILBridging.cpp @@ -0,0 +1,480 @@ +//===--- SILBridgingUtils.cpp - Utilities for swift bridging --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/SILNode.h" +#include "swift/SIL/SILBridgingUtils.h" +#include "swift/SIL/SILGlobalVariable.h" +#include "swift/SIL/SILBuilder.h" + +using namespace swift; + +namespace { + +bool nodeMetatypesInitialized = false; + +// Filled in by class registration in initializeLibSwift(). +SwiftMetatype nodeMetatypes[(unsigned)SILNodeKind::Last_SILNode + 1]; + +} + +// Does return null, if libswift is not used, i.e. initializeLibSwift() is +// never called. +SwiftMetatype SILNode::getSILNodeMetatype(SILNodeKind kind) { + SwiftMetatype metatype = nodeMetatypes[(unsigned)kind]; + assert((!nodeMetatypesInitialized || metatype) && + "no metatype for bridged SIL node"); + return metatype; +} + +static_assert(sizeof(BridgedLocation) == sizeof(SILDebugLocation), + "BridgedLocation has wrong size"); + +/// Fills \p storage with all Values from the bridged \p values array. +ArrayRef swift::getSILValues(BridgedValueArray values, + SmallVectorImpl &storage) { + auto *base = reinterpret_cast(values.data); + + // The bridged array contains class existentials, which have a layout of two + // words. The first word is the actual object. Pick the objects and store them + // into storage. + for (unsigned idx = 0; idx < values.count; ++idx) { + storage.push_back(castToSILValue({base[idx * 2]})); + } + return storage; +} + +//===----------------------------------------------------------------------===// +// Class registration +//===----------------------------------------------------------------------===// + +static llvm::StringMap valueNamesToKind; +static llvm::SmallPtrSet unimplementedTypes; + +// Utility to fill in a metatype of an "unimplemented" class for a whole range +// of class types. +static void setUnimplementedRange(SwiftMetatype metatype, + SILNodeKind from, SILNodeKind to) { + unimplementedTypes.insert(metatype); + for (unsigned kind = (unsigned)from; kind <= (unsigned)to; ++kind) { + assert((!nodeMetatypes[kind] || unimplementedTypes.count(metatype)) && + "unimplemented nodes must be registered first"); + nodeMetatypes[kind] = metatype; + } +} + +/// Registers the metatype of a libswift class. +/// Called by initializeLibSwift(). +void registerBridgedClass(BridgedStringRef className, SwiftMetatype metatype) { + nodeMetatypesInitialized = true; + + // Handle the important non Node classes. + StringRef clName = getStringRef(className); + if (clName == "Function") + return SILFunction::registerBridgedMetatype(metatype); + if (clName == "BasicBlock") + return SILBasicBlock::registerBridgedMetatype(metatype); + if (clName == "GlobalVariable") + return SILGlobalVariable::registerBridgedMetatype(metatype); + if (clName == "BlockArgument") { + nodeMetatypes[(unsigned)SILNodeKind::SILPhiArgument] = metatype; + return; + } + if (clName == "FunctionArgument") { + nodeMetatypes[(unsigned)SILNodeKind::SILFunctionArgument] = metatype; + return; + } + + // Pre-populate the "unimplemented" ranges of metatypes. + // If a specifc class is not implemented yet in libswift, it bridges to an + // "unimplemented" class. This ensures that optimizations handle _all_ kind of + // instructions gracefully, without the need to define the not-yet-used + // classes in libswift. +#define VALUE_RANGE(ID) SILNodeKind::First_##ID, SILNodeKind::Last_##ID + if (clName == "UnimplementedRefCountingInst") + return setUnimplementedRange(metatype, VALUE_RANGE(RefCountingInst)); + if (clName == "UnimplementedSingleValueInst") + return setUnimplementedRange(metatype, VALUE_RANGE(SingleValueInstruction)); + if (clName == "UnimplementedInstruction") + return setUnimplementedRange(metatype, VALUE_RANGE(SILInstruction)); +#undef VALUE_RANGE + + if (valueNamesToKind.empty()) { +#define VALUE(ID, PARENT) \ + valueNamesToKind[#ID] = SILNodeKind::ID; +#define BRIDGED_NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ + VALUE(ID, NAME) +#define ARGUMENT(ID, PARENT) \ + VALUE(ID, NAME) +#define BRIDGED_SINGLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ + VALUE(ID, NAME) +#define MULTIPLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \ + VALUE(ID, NAME) +#include "swift/SIL/SILNodes.def" + } + + std::string prefixedName; + auto iter = valueNamesToKind.find(clName); + if (iter == valueNamesToKind.end()) { + // Try again with a "SIL" prefix. For example Argument -> SILArgument. + prefixedName = std::string("SIL") + std::string(clName); + iter = valueNamesToKind.find(prefixedName); + if (iter == valueNamesToKind.end()) { + llvm::errs() << "Unknown bridged node class " << clName << '\n'; + abort(); + } + clName = prefixedName; + } + SILNodeKind kind = iter->second; + SwiftMetatype existingTy = nodeMetatypes[(unsigned)kind]; + if (existingTy && !unimplementedTypes.count(existingTy)) { + llvm::errs() << "Double registration of class " << clName << '\n'; + abort(); + } + nodeMetatypes[(unsigned)kind] = metatype; +} + +//===----------------------------------------------------------------------===// +// Bridging C functions +//===----------------------------------------------------------------------===// + +/// Frees a string which was allocated by getCopiedBridgedStringRef. +void freeBridgedStringRef(BridgedStringRef str) { + llvm::MallocAllocator().Deallocate(str.data, str.length); +} + +//===----------------------------------------------------------------------===// +// SILFunction +//===----------------------------------------------------------------------===// + +BridgedStringRef SILFunction_getName(BridgedFunction function) { + return getBridgedStringRef(castToFunction(function)->getName()); +} + +BridgedStringRef SILFunction_debugDescription(BridgedFunction function) { + std::string str; + llvm::raw_string_ostream os(str); + castToFunction(function)->print(os); + return getCopiedBridgedStringRef(str, /*removeTrailingNewline*/ true); +} + +OptionalBridgedBasicBlock SILFunction_firstBlock(BridgedFunction function) { + SILFunction *f = castToFunction(function); + if (f->empty()) + return {nullptr}; + return {f->getEntryBlock()}; +} + +OptionalBridgedBasicBlock SILFunction_lastBlock(BridgedFunction function) { + SILFunction *f = castToFunction(function); + if (f->empty()) + return {nullptr}; + return {&*f->rbegin()}; +} + +//===----------------------------------------------------------------------===// +// SILBasicBlock +//===----------------------------------------------------------------------===// + +static_assert(BridgedSuccessorSize == sizeof(SILSuccessor), + "wrong bridged SILSuccessor size"); + +OptionalBridgedBasicBlock SILBasicBlock_next(BridgedBasicBlock block) { + SILBasicBlock *b = castToBasicBlock(block); + auto iter = std::next(b->getIterator()); + if (iter == b->getParent()->end()) + return {nullptr}; + return {&*iter}; +} + +OptionalBridgedBasicBlock SILBasicBlock_previous(BridgedBasicBlock block) { + SILBasicBlock *b = castToBasicBlock(block); + auto iter = std::next(b->getReverseIterator()); + if (iter == b->getParent()->rend()) + return {nullptr}; + return {&*iter}; +} + +BridgedFunction SILBasicBlock_getFunction(BridgedBasicBlock block) { + return {castToBasicBlock(block)->getParent()}; +} + +BridgedStringRef SILBasicBlock_debugDescription(BridgedBasicBlock block) { + std::string str; + llvm::raw_string_ostream os(str); + castToBasicBlock(block)->print(os); + return getCopiedBridgedStringRef(str, /*removeTrailingNewline*/ true); +} + +OptionalBridgedInstruction SILBasicBlock_firstInst(BridgedBasicBlock block) { + SILBasicBlock *b = castToBasicBlock(block); + if (b->empty()) + return {nullptr}; + return {b->front().asSILNode()}; +} + +OptionalBridgedInstruction SILBasicBlock_lastInst(BridgedBasicBlock block) { + SILBasicBlock *b = castToBasicBlock(block); + if (b->empty()) + return {nullptr}; + return {b->back().asSILNode()}; +} + +SwiftInt SILBasicBlock_getNumArguments(BridgedBasicBlock block) { + return castToBasicBlock(block)->getNumArguments(); +} + +BridgedArgument SILBasicBlock_getArgument(BridgedBasicBlock block, SwiftInt index) { + return {castToBasicBlock(block)->getArgument(index)}; +} + +OptionalBridgedSuccessor SILBasicBlock_getFirstPred(BridgedBasicBlock block) { + return {castToBasicBlock(block)->pred_begin().getSuccessorRef()}; +} + +static SILSuccessor *castToSuccessor(BridgedSuccessor succ) { + return const_cast(static_cast(succ.succ)); +} + +OptionalBridgedSuccessor SILSuccessor_getNext(BridgedSuccessor succ) { + return {castToSuccessor(succ)->getNext()}; +} + +BridgedBasicBlock SILSuccessor_getTargetBlock(BridgedSuccessor succ) { + return {castToSuccessor(succ)->getBB()}; +} + +BridgedInstruction SILSuccessor_getContainingInst(BridgedSuccessor succ) { + return {castToSuccessor(succ)->getContainingInst()}; +} + +//===----------------------------------------------------------------------===// +// SILArgument +//===----------------------------------------------------------------------===// + +BridgedBasicBlock SILArgument_getParent(BridgedArgument argument) { + return {static_cast(argument.obj)->getParent()}; +} + +//===----------------------------------------------------------------------===// +// SILValue +//===----------------------------------------------------------------------===// + +static_assert(BridgedOperandSize == sizeof(Operand), + "wrong bridged Operand size"); + +BridgedStringRef SILNode_debugDescription(BridgedNode node) { + std::string str; + llvm::raw_string_ostream os(str); + castToSILNode(node)->print(os); + return getCopiedBridgedStringRef(str, /*removeTrailingNewline*/ true); +} + +static Operand *castToOperand(BridgedOperand operand) { + return const_cast(static_cast(operand.op)); +} + +BridgedValue Operand_getValue(BridgedOperand operand) { + return {castToOperand(operand)->get()}; +} + +OptionalBridgedOperand Operand_nextUse(BridgedOperand operand) { + return {castToOperand(operand)->getNextUse()}; +} + +BridgedInstruction Operand_getUser(BridgedOperand operand) { + return {castToOperand(operand)->getUser()->asSILNode()}; +} + +OptionalBridgedOperand SILValue_firstUse(BridgedValue value) { + return {*castToSILValue(value)->use_begin()}; +} + +BridgedType SILValue_getType(BridgedValue value) { + return { castToSILValue(value)->getType().getOpaqueValue() }; +} + +//===----------------------------------------------------------------------===// +// SILType +//===----------------------------------------------------------------------===// + +SwiftInt SILType_isAddress(BridgedType type) { + return castToSILType(type).isAddress(); +} + +//===----------------------------------------------------------------------===// +// SILGlobalVariable +//===----------------------------------------------------------------------===// + +BridgedStringRef SILGlobalVariable_getName(BridgedGlobalVar global) { + return getBridgedStringRef(castToGlobal(global)->getName()); +} + +BridgedStringRef SILGlobalVariable_debugDescription(BridgedGlobalVar global) { + std::string str; + llvm::raw_string_ostream os(str); + castToGlobal(global)->print(os); + return getCopiedBridgedStringRef(str, /*removeTrailingNewline*/ true); +} + +//===----------------------------------------------------------------------===// +// SILInstruction +//===----------------------------------------------------------------------===// + +OptionalBridgedInstruction SILInstruction_next(BridgedInstruction inst) { + SILInstruction *i = castToInst(inst); + auto iter = std::next(i->getIterator()); + if (iter == i->getParent()->end()) + return {nullptr}; + return {iter->asSILNode()}; +} + +OptionalBridgedInstruction SILInstruction_previous(BridgedInstruction inst) { + SILInstruction *i = castToInst(inst); + auto iter = std::next(i->getReverseIterator()); + if (iter == i->getParent()->rend()) + return {nullptr}; + return {iter->asSILNode()}; +} + +BridgedBasicBlock SILInstruction_getParent(BridgedInstruction inst) { + SILInstruction *i = castToInst(inst); + assert(!i->isStaticInitializerInst() && + "cannot get the parent of a static initializer instruction"); + return {i->getParent()}; +} + +BridgedArrayRef SILInstruction_getOperands(BridgedInstruction inst) { + auto operands = castToInst(inst)->getAllOperands(); + return {(const unsigned char *)operands.data(), operands.size()}; +} + +void SILInstruction_setOperand(BridgedInstruction inst, SwiftInt index, + BridgedValue value) { + castToInst(inst)->setOperand((unsigned)index, castToSILValue(value)); +} + +BridgedLocation SILInstruction_getLocation(BridgedInstruction inst) { + SILDebugLocation loc = castToInst(inst)->getDebugLocation(); + return *reinterpret_cast(&loc); +} + +BridgedMemoryBehavior SILInstruction_getMemBehavior(BridgedInstruction inst) { + return (BridgedMemoryBehavior)castToInst(inst)->getMemoryBehavior(); +} + +BridgedInstruction MultiValueInstResult_getParent(BridgedMultiValueResult result) { + return {static_cast(result.obj)->getParent()}; +} + +SwiftInt MultipleValueInstruction_getNumResults(BridgedInstruction inst) { + return castToInst(inst)->getNumResults(); +} +BridgedMultiValueResult +MultipleValueInstruction_getResult(BridgedInstruction inst, SwiftInt index) { + return {castToInst(inst)->getResult(index)}; +} + +BridgedArrayRef TermInst_getSuccessors(BridgedInstruction term) { + auto successors = castToInst(term)->getSuccessors(); + return {(const unsigned char *)successors.data(), successors.size()}; +} + +//===----------------------------------------------------------------------===// +// Instruction classes +//===----------------------------------------------------------------------===// + +BridgedStringRef CondFailInst_getMessage(BridgedInstruction cfi) { + return getBridgedStringRef(castToInst(cfi)->getMessage()); +} + +BridgedGlobalVar GlobalAccessInst_getGlobal(BridgedInstruction globalInst) { + return {castToInst(globalInst)->getReferencedGlobal()}; +} + +SwiftInt TupleExtractInst_fieldIndex(BridgedInstruction tei) { + return castToInst(tei)->getFieldIndex(); +} + +SwiftInt TupleElementAddrInst_fieldIndex(BridgedInstruction teai) { + return castToInst(teai)->getFieldIndex(); +} + +SwiftInt StructExtractInst_fieldIndex(BridgedInstruction sei) { + return castToInst(sei)->getFieldIndex(); +} + +SwiftInt StructElementAddrInst_fieldIndex(BridgedInstruction seai) { + return castToInst(seai)->getFieldIndex(); +} + +SwiftInt EnumInst_caseIndex(BridgedInstruction ei) { + return getCaseIndex(castToInst(ei)->getElement()); +} + +SwiftInt UncheckedEnumDataInst_caseIndex(BridgedInstruction uedi) { + return getCaseIndex(castToInst(uedi)->getElement()); +} + +SwiftInt RefElementAddrInst_fieldIndex(BridgedInstruction reai) { + return castToInst(reai)->getFieldIndex(); +} + +SwiftInt PartialApplyInst_numArguments(BridgedInstruction pai) { + return castToInst(pai)->getNumArguments(); +} + +SwiftInt ApplyInst_numArguments(BridgedInstruction ai) { + return castToInst(ai)->getNumArguments(); +} + +SwiftInt BeginApplyInst_numArguments(BridgedInstruction tai) { + return castToInst(tai)->getNumArguments(); +} + +SwiftInt TryApplyInst_numArguments(BridgedInstruction tai) { + return castToInst(tai)->getNumArguments(); +} + +BridgedBasicBlock BranchInst_getTargetBlock(BridgedInstruction bi) { + return {castToInst(bi)->getDestBB()}; +} + +SwiftInt SwitchEnumInst_getNumCases(BridgedInstruction se) { + return castToInst(se)->getNumCases(); +} + +SwiftInt SwitchEnumInst_getCaseIndex(BridgedInstruction se, SwiftInt idx) { + return getCaseIndex(castToInst(se)->getCase(idx).first); +} + +//===----------------------------------------------------------------------===// +// SILBuilder +//===----------------------------------------------------------------------===// + +BridgedInstruction SILBuilder_createBuiltinBinaryFunction( + BridgedInstruction insertionPoint, + BridgedLocation loc, BridgedStringRef name, + BridgedType operandType, BridgedType resultType, + BridgedValueArray arguments) { + SILBuilder builder(castToInst(insertionPoint), getSILDebugScope(loc)); + SmallVector argValues; + return {builder.createBuiltinBinaryFunction(getRegularLocation(loc), + getStringRef(name), getSILType(operandType), getSILType(resultType), + getSILValues(arguments, argValues))}; +} + +BridgedInstruction SILBuilder_createCondFail(BridgedInstruction insertionPoint, + BridgedLocation loc, BridgedValue condition, BridgedStringRef messge) { + SILBuilder builder(castToInst(insertionPoint), getSILDebugScope(loc)); + return {builder.createCondFail(getRegularLocation(loc), + castToSILValue(condition), getStringRef(messge))}; +} diff --git a/lib/SIL/Verifier/CMakeLists.txt b/lib/SIL/Verifier/CMakeLists.txt index ab292545cae83..d1c47adc03e7b 100644 --- a/lib/SIL/Verifier/CMakeLists.txt +++ b/lib/SIL/Verifier/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(swiftSIL PRIVATE + DebugInfoVerifier.cpp LoadBorrowImmutabilityChecker.cpp LinearLifetimeChecker.cpp MemoryLifetimeVerifier.cpp diff --git a/lib/SIL/Verifier/DebugInfoVerifier.cpp b/lib/SIL/Verifier/DebugInfoVerifier.cpp new file mode 100644 index 0000000000000..7857ffc7bd13a --- /dev/null +++ b/lib/SIL/Verifier/DebugInfoVerifier.cpp @@ -0,0 +1,57 @@ +//===--- DebugInfoVerifier.cpp --------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Utility verifier code for validating debug info. +/// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/SILDebugScope.h" +#include "swift/SIL/SILInstruction.h" + +using namespace swift; + +//===----------------------------------------------------------------------===// +// MARK: Verify SILInstruction Debug Info +//===----------------------------------------------------------------------===// + +void SILInstruction::verifyDebugInfo() const { + auto require = [&](bool reqt, StringRef message) { + if (!reqt) { + llvm::errs() << message << "\n"; + assert(false && "invoking standard assertion failure"); + } + }; + + // Check the location kind. + SILLocation loc = getLoc(); + SILLocation::LocationKind locKind = loc.getKind(); + SILInstructionKind instKind = getKind(); + + // Regular locations are allowed on all instructions. + if (locKind == SILLocation::RegularKind) + return; + + if (locKind == SILLocation::ReturnKind || + locKind == SILLocation::ImplicitReturnKind) + require( + instKind == SILInstructionKind::BranchInst || + instKind == SILInstructionKind::ReturnInst || + instKind == SILInstructionKind::UnreachableInst, + "return locations are only allowed on branch and return instructions"); + + if (locKind == SILLocation::ArtificialUnreachableKind) + require( + instKind == SILInstructionKind::UnreachableInst, + "artificial locations are only allowed on Unreachable instructions"); +} diff --git a/lib/SIL/Verifier/LinearLifetimeChecker.cpp b/lib/SIL/Verifier/LinearLifetimeChecker.cpp index 8a1ed426f0c33..5cf3724462567 100644 --- a/lib/SIL/Verifier/LinearLifetimeChecker.cpp +++ b/lib/SIL/Verifier/LinearLifetimeChecker.cpp @@ -314,8 +314,9 @@ void State::checkForSameBlockUseAfterFree(Operand *consumingUse, }) == userBlock->end()) { continue; } - } else if (auto borrowingOperand = BorrowingOperand::get(consumingUse)) { + } else if (auto borrowingOperand = BorrowingOperand(nonConsumingUse)) { assert(borrowingOperand.isReborrow()); + // a reborrow is expected to be consumed by the same phi. continue; } diff --git a/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp index 1b0dc0401a388..8824ff5cb3e93 100644 --- a/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp +++ b/lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp @@ -88,7 +88,6 @@ bool GatherWritesVisitor::visitUse(Operand *op, AccessUseType useTy) { return true; } switch (user->getKind()) { - // Known reads... case SILInstructionKind::LoadBorrowInst: case SILInstructionKind::SelectEnumAddrInst: @@ -100,6 +99,7 @@ bool GatherWritesVisitor::visitUse(Operand *op, AccessUseType useTy) { case SILInstructionKind::IsUniqueInst: case SILInstructionKind::HopToExecutorInst: case SILInstructionKind::ExtractExecutorInst: + case SILInstructionKind::ValueMetatypeInst: return true; // Known writes... diff --git a/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp b/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp index aef6480226072..4d88d8c1acf25 100644 --- a/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp +++ b/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp @@ -178,6 +178,12 @@ static bool isTrivialEnumElem(EnumElementDecl *elem, SILType enumType, enumType.getEnumElementType(elem, function).isTrivial(*function); } +static bool isOrHasEnum(SILType type) { + return type.getASTType().findIf([](Type ty) { + return ty->getEnumOrBoundGenericEnum() != nullptr; + }); +} + bool MemoryLifetimeVerifier::storesTrivialEnum(int locIdx, SILBasicBlock::reverse_iterator start, SILBasicBlock::reverse_iterator end) { @@ -191,7 +197,7 @@ bool MemoryLifetimeVerifier::storesTrivialEnum(int locIdx, if (auto *SI = dyn_cast(&inst)) { const Location *loc = locations.getLocation(SI->getDest()); if (loc && loc->isSubLocation(locIdx) && - SI->getSrc()->getType().getEnumOrBoundGenericEnum()) { + isOrHasEnum(SI->getSrc()->getType())) { return SI->getOwnershipQualifier() == StoreOwnershipQualifier::Trivial; } } @@ -604,9 +610,18 @@ void MemoryLifetimeVerifier::checkBlock(SILBasicBlock *block, Bits &bits) { case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::IsUniqueInst: case SILInstructionKind::FixLifetimeInst: - case SILInstructionKind::DebugValueAddrInst: requireBitsSet(bits, I.getOperand(0), &I); break; + case SILInstructionKind::DebugValueInst: + // We don't want to check `debug_value` instructions that + // are used to mark variable declarations (e.g. its SSA value is + // an alloc_stack), which don't have any `op_deref` in its + // di-expression, because that memory does't need to be initialized + // when `debug_value` is referencing it. + if (cast(&I)->hasAddrVal() && + cast(&I)->exprStartsWithDeref()) + requireBitsSet(bits, I.getOperand(0), &I); + break; case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { // Note that despite the name, unchecked_take_enum_data_addr does _not_ // "take" the payload of the Swift.Optional enum. This is a terrible diff --git a/lib/SIL/Verifier/SILOwnershipVerifier.cpp b/lib/SIL/Verifier/SILOwnershipVerifier.cpp index 79e2784ea028e..a032034420de1 100644 --- a/lib/SIL/Verifier/SILOwnershipVerifier.cpp +++ b/lib/SIL/Verifier/SILOwnershipVerifier.cpp @@ -244,7 +244,7 @@ bool SILValueOwnershipChecker::gatherNonGuaranteedUsers( // regular users so we can ensure that the borrow scope operand's scope is // completely within the owned value's scope. If we do not have a borrow // scope operand, just continue, we are done. - auto initialScopedOperand = BorrowingOperand::get(op); + auto initialScopedOperand = BorrowingOperand(op); if (!initialScopedOperand) { continue; } @@ -348,7 +348,7 @@ bool SILValueOwnershipChecker::gatherUsers( // Ok, our operand does not consume guaranteed values. Check if it is a // BorrowScopeOperand and if so, add its end scope instructions as // implicit regular users of our value. - if (auto scopedOperand = BorrowingOperand::get(op)) { + if (auto scopedOperand = BorrowingOperand(op)) { assert(!scopedOperand.isReborrow()); std::function onError = [&](Operand *op) { @@ -735,21 +735,31 @@ void SILInstruction::verifyOperandOwnership() const { if (isTypeDependentOperand(op)) continue; - if (op.satisfiesConstraints()) - continue; + if (!checkOperandOwnershipInvariants(&op)) { + errorBuilder->handleMalformedSIL([&] { + llvm::errs() << "Found an operand with invalid invariants.\n"; + llvm::errs() << "Value: " << op.get(); + llvm::errs() << "Instruction:\n"; + printInContext(llvm::errs()); + llvm::errs() << "OperandOwnership: " << op.getOperandOwnership() + << "\n"; + }); + } - auto constraint = op.getOwnershipConstraint(); - SILValue opValue = op.get(); - auto valueOwnershipKind = opValue.getOwnershipKind(); - errorBuilder->handleMalformedSIL([&] { - llvm::errs() << "Found an operand with a value that is not compatible " - "with the operand's operand ownership kind map.\n"; - llvm::errs() << "Value: " << opValue; - llvm::errs() << "Value Ownership Kind: " << valueOwnershipKind << "\n"; - llvm::errs() << "Instruction:\n"; - printInContext(llvm::errs()); - llvm::errs() << "Constraint: " << constraint << "\n"; - }); + if (!op.satisfiesConstraints()) { + auto constraint = op.getOwnershipConstraint(); + SILValue opValue = op.get(); + auto valueOwnershipKind = opValue.getOwnershipKind(); + errorBuilder->handleMalformedSIL([&] { + llvm::errs() << "Found an operand with a value that is not compatible " + "with the operand's operand ownership kind map.\n"; + llvm::errs() << "Value: " << opValue; + llvm::errs() << "Value Ownership Kind: " << valueOwnershipKind << "\n"; + llvm::errs() << "Instruction:\n"; + printInContext(llvm::errs()); + llvm::errs() << "Constraint: " << constraint << "\n"; + }); + } } } diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 44bc7d30beeb1..b6ce63c36c3d4 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -537,7 +537,6 @@ struct ImmutableAddressUseVerifier { } case SILInstructionKind::MarkDependenceInst: case SILInstructionKind::LoadBorrowInst: - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::ExistentialMetatypeInst: case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::FixLifetimeInst: @@ -545,6 +544,13 @@ struct ImmutableAddressUseVerifier { case SILInstructionKind::SwitchEnumAddrInst: case SILInstructionKind::SelectEnumAddrInst: break; + case SILInstructionKind::DebugValueInst: + if (cast(inst)->hasAddrVal()) + break; + else { + llvm::errs() << "Unhandled, unexpected instruction: " << *inst; + llvm_unreachable("invoking standard assertion failure"); + } case SILInstructionKind::AddressToPointerInst: // We assume that the user is attempting to do something unsafe since we // are converting to a raw pointer. So just ignore this use. @@ -1039,8 +1045,9 @@ class SILVerifier : public SILVerifierBase { CurInstruction = I; checkSILInstruction(I); - // Check the SILLLocation attached to the instruction. - checkInstructionsSILLocation(I); + // Check the SILLLocation attached to the instruction, + // as well as debug-variable-carrying instructions. + checkInstructionsDebugInfo(I); // Check ownership and types. SILFunction *F = I->getFunction(); @@ -1204,67 +1211,84 @@ class SILVerifier : public SILVerifierBase { } } - void checkInstructionsSILLocation(SILInstruction *I) { - // Check the debug scope. - auto *DS = I->getDebugScope(); - if (DS && !maybeScopeless(*I)) - require(DS, "instruction has a location, but no scope"); + void checkDebugVariable(SILInstruction *inst) { + Optional varInfo; + if (auto *di = dyn_cast(inst)) + varInfo = di->getVarInfo(); + else if (auto *di = dyn_cast(inst)) + varInfo = di->getVarInfo(); + else if (auto *di = dyn_cast(inst)) + varInfo = di->getVarInfo(); - require(!DS || DS->getParentFunction() == I->getFunction(), - "debug scope of instruction belongs to a different function"); + if (!varInfo) + return; - // Check the location kind. - SILLocation L = I->getLoc(); - SILLocation::LocationKind LocKind = L.getKind(); - SILInstructionKind InstKind = I->getKind(); - - // Check that there is at most one debug variable defined - // for each argument slot. This catches SIL transformations - // that accidentally remove inline information (stored in the SILDebugScope) - // from debug-variable-carrying instructions. - if (!DS->InlinedCallSite) { - Optional VarInfo; - if (auto *DI = dyn_cast(I)) - VarInfo = DI->getVarInfo(); - else if (auto *DI = dyn_cast(I)) - VarInfo = DI->getVarInfo(); - else if (auto *DI = dyn_cast(I)) - VarInfo = DI->getVarInfo(); - else if (auto *DI = dyn_cast(I)) - VarInfo = DI->getVarInfo(); - - if (VarInfo) - if (unsigned ArgNo = VarInfo->ArgNo) { - // It is a function argument. - if (ArgNo < DebugVars.size() && !DebugVars[ArgNo].empty() && !VarInfo->Name.empty()) { - require( - DebugVars[ArgNo] == VarInfo->Name, - "Scope contains conflicting debug variables for one function " - "argument"); - } else { - // Reserve enough space. - while (DebugVars.size() <= ArgNo) { - DebugVars.push_back(StringRef()); - } + auto *debugScope = inst->getDebugScope(); + + // Check that there is at most one debug variable defined for each argument + // slot if our debug scope is not an inlined call site. + // + // This catches SIL transformations that accidentally remove inline + // information (stored in the SILDebugScope) from debug-variable-carrying + // instructions. + if (debugScope && !debugScope->InlinedCallSite) + if (unsigned argNum = varInfo->ArgNo) { + // It is a function argument. + if (argNum < DebugVars.size() && !DebugVars[argNum].empty() && + !varInfo->Name.empty()) { + require(DebugVars[argNum] == varInfo->Name, + "Scope contains conflicting debug variables for one function " + "argument"); + } else { + // Reserve enough space. + while (DebugVars.size() <= argNum) { + DebugVars.push_back(StringRef()); } - DebugVars[ArgNo] = VarInfo->Name; + } + DebugVars[argNum] = varInfo->Name; + } + + // Check the (auxiliary) debug variable scope + if (const SILDebugScope *VarDS = varInfo->Scope) + require(VarDS->getInlinedFunction() == debugScope->getInlinedFunction(), + "Scope of the debug variable should have the same parent function" + " as that of instruction."); + + // Check debug info expression + if (const auto &DIExpr = varInfo->DIExpr) { + for (auto It = DIExpr.element_begin(), ItEnd = DIExpr.element_end(); + It != ItEnd;) { + require(It->getKind() == SILDIExprElement::OperatorKind, + "dangling di-expression operand"); + auto Op = It->getAsOperator(); + const auto *DIExprInfo = SILDIExprInfo::get(Op); + require(DIExprInfo, "unrecognized di-expression operator"); + ++It; + // Check operand kinds + for (auto OpK : DIExprInfo->OperandKinds) + require(It != ItEnd && (It++)->getKind() == OpK, + "di-expression operand kind mismatch"); + + if (Op == SILDIExprOperator::Fragment) + require(It == ItEnd, "op_fragment directive needs to be at the end " + "of a di-expression"); } } + } - // Regular locations are allowed on all instructions. - if (LocKind == SILLocation::RegularKind) - return; + void checkInstructionsDebugInfo(SILInstruction *inst) { + // First verify structural debug info information. + inst->verifyDebugInfo(); - if (LocKind == SILLocation::ReturnKind || - LocKind == SILLocation::ImplicitReturnKind) - require(InstKind == SILInstructionKind::BranchInst || - InstKind == SILInstructionKind::ReturnInst || - InstKind == SILInstructionKind::UnreachableInst, - "return locations are only allowed on branch and return instructions"); + // Check the debug scope. + auto *debugScope = inst->getDebugScope(); + if (debugScope && !maybeScopeless(*inst)) + require(debugScope, "instruction has a location, but no scope"); + require(!debugScope || + debugScope->getParentFunction() == inst->getFunction(), + "debug scope of instruction belongs to a different function"); - if (LocKind == SILLocation::ArtificialUnreachableKind) - require(InstKind == SILInstructionKind::UnreachableInst, - "artificial locations are only allowed on Unreachable instructions"); + checkDebugVariable(inst); } /// Check that the types of this value producer are all legal in the function @@ -1431,8 +1455,8 @@ class SILVerifier : public SILVerifierBase { }); } - if (subs.getGenericSignature()->getCanonicalSignature() != - fnTy->getInvocationGenericSignature()->getCanonicalSignature()) { + if (subs.getGenericSignature().getCanonicalSignature() != + fnTy->getInvocationGenericSignature().getCanonicalSignature()) { llvm::dbgs() << "substitution map's generic signature: "; subs.getGenericSignature()->print(llvm::dbgs()); llvm::dbgs() << "\n"; @@ -3155,12 +3179,12 @@ class SILVerifier : public SILVerifierBase { auto genericSig = methodType->getInvocationGenericSignature(); - auto selfGenericParam = genericSig->getGenericParams()[0]; + auto selfGenericParam = genericSig.getGenericParams()[0]; require(selfGenericParam->getDepth() == 0 && selfGenericParam->getIndex() == 0, "method should be polymorphic on Self parameter at depth 0 index 0"); Optional selfRequirement; - for (auto req : genericSig->getRequirements()) { + for (auto req : genericSig.getRequirements()) { if (req.getKind() != RequirementKind::SameType) { selfRequirement = req; break; @@ -3175,7 +3199,7 @@ class SILVerifier : public SILVerifierBase { "requirement Self parameter must conform to called protocol"); auto lookupType = AMI->getLookupType(); - if (getOpenedArchetypeOf(lookupType) || isa(lookupType)) { + if (getOpenedArchetypeOf(lookupType) || lookupType->hasDynamicSelfType()) { require(AMI->getTypeDependentOperands().size() == 1, "Must have a type dependent operand for the opened archetype"); verifyOpenedArchetype(AMI, lookupType); @@ -4042,7 +4066,7 @@ class SILVerifier : public SILVerifierBase { require(instClass, "upcast must convert a class metatype to a class metatype"); - if (instClass->usesObjCGenericsModel()) { + if (instClass->isTypeErasedGenericClass()) { require(instClass->getDeclaredTypeInContext() ->isBindableToSuperclassOf(opInstTy), "upcast must cast to a superclass or an existential metatype"); @@ -4075,7 +4099,7 @@ class SILVerifier : public SILVerifierBase { auto ToClass = ToTy.getClassOrBoundGenericClass(); require(ToClass, "upcast must convert a class instance to a class type"); - if (ToClass->usesObjCGenericsModel()) { + if (ToClass->isTypeErasedGenericClass()) { require(ToClass->getDeclaredTypeInContext() ->isBindableToSuperclassOf(FromTy.getASTType()), "upcast must cast to a superclass or an existential metatype"); diff --git a/lib/SILGen/CMakeLists.txt b/lib/SILGen/CMakeLists.txt index c4daab77fd3e6..94090fa995aff 100644 --- a/lib/SILGen/CMakeLists.txt +++ b/lib/SILGen/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftSILGen STATIC ArgumentSource.cpp Cleanup.cpp @@ -18,6 +18,7 @@ add_swift_host_library(swiftSILGen STATIC SILGenConvert.cpp SILGenDecl.cpp SILGenDestructor.cpp + SILGenDistributed.cpp SILGenDynamicCast.cpp SILGenEpilog.cpp SILGenExpr.cpp @@ -35,3 +36,5 @@ add_swift_host_library(swiftSILGen STATIC SILGenType.cpp) target_link_libraries(swiftSILGen PRIVATE swiftSIL) + +set_swift_llvm_is_available(swiftSILGen) diff --git a/lib/SILGen/Callee.h b/lib/SILGen/Callee.h index 06fc3f4561c6d..b3eadd2983257 100644 --- a/lib/SILGen/Callee.h +++ b/lib/SILGen/Callee.h @@ -15,6 +15,7 @@ #include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" +#include "swift/AST/ForeignInfo.h" #include "swift/AST/Types.h" #include "swift/SIL/AbstractionPattern.h" @@ -23,14 +24,10 @@ namespace Lowering { class CalleeTypeInfo { public: + Optional origFormalType; CanSILFunctionType substFnType; Optional origResultType; CanType substResultType; - struct ForeignInfo { - Optional error; - Optional async; - ImportAsMemberStatus self; - }; ForeignInfo foreign; private: @@ -45,17 +42,18 @@ class CalleeTypeInfo { const Optional &foreignAsync, ImportAsMemberStatus foreignSelf, Optional overrideRep = None) - : substFnType(substFnType), origResultType(origResultType), - substResultType(substResultType), - foreign{foreignError, foreignAsync, foreignSelf}, + : origFormalType(llvm::None), substFnType(substFnType), + origResultType(origResultType), + substResultType(substResultType), foreign{foreignSelf, foreignError, + foreignAsync}, overrideRep(overrideRep) {} CalleeTypeInfo(CanSILFunctionType substFnType, AbstractionPattern origResultType, CanType substResultType, Optional overrideRep = None) - : substFnType(substFnType), origResultType(origResultType), - substResultType(substResultType), foreign(), - overrideRep(overrideRep) {} + : origFormalType(llvm::None), substFnType(substFnType), + origResultType(origResultType), substResultType(substResultType), + foreign(), overrideRep(overrideRep) {} SILFunctionTypeRepresentation getOverrideRep() const { return overrideRep.getValueOr(substFnType->getRepresentation()); diff --git a/lib/SILGen/Cleanup.h b/lib/SILGen/Cleanup.h index e2361797d79d6..6c21245fb8e6a 100644 --- a/lib/SILGen/Cleanup.h +++ b/lib/SILGen/Cleanup.h @@ -272,7 +272,8 @@ class LLVM_LIBRARY_VISIBILITY CleanupManager { bool hasAnyActiveCleanups(CleanupsDepth from); /// Dump the output of each cleanup on this stack. - void dump() const; + LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, + "Only for use in the debugger"); /// Dump the given cleanup handle if it is on the current stack. void dump(CleanupHandle handle) const; diff --git a/lib/SILGen/ExecutorBreadcrumb.h b/lib/SILGen/ExecutorBreadcrumb.h index 1f544e53447fd..efc1441f4cb8c 100644 --- a/lib/SILGen/ExecutorBreadcrumb.h +++ b/lib/SILGen/ExecutorBreadcrumb.h @@ -10,9 +10,16 @@ // //===----------------------------------------------------------------------===// +#include "swift/SIL/SILValue.h" + namespace swift { + +class SILLocation; + namespace Lowering { +class SILGenFunction; + /// Represents the information necessary to return to a caller's own /// active executor after making a hop to an actor for actor-isolated calls. class ExecutorBreadcrumb { diff --git a/lib/SILGen/LValue.h b/lib/SILGen/LValue.h index a8700dca07968..64bd57d7744bc 100644 --- a/lib/SILGen/LValue.h +++ b/lib/SILGen/LValue.h @@ -207,9 +207,9 @@ class PathComponent { /// The only operation on this component is `project`. class PhysicalPathComponent : public PathComponent { virtual void _anchor() override; + Optional ActorIso; protected: - Optional ActorIso; PhysicalPathComponent(LValueTypeData typeData, KindTy Kind, Optional actorIso = None) : PathComponent(typeData, Kind), ActorIso(actorIso) { @@ -217,8 +217,16 @@ class PhysicalPathComponent : public PathComponent { } public: - // Obtains the actor-isolation required for any loads of this component. - Optional getActorIsolation() const { return ActorIso; } + /// Obtains and consumes the actor-isolation required for any loads of + /// this component. + Optional takeActorIsolation() { + Optional current = ActorIso; + ActorIso = None; + return current; + } + + /// Determines whether this component has any actor-isolation. + bool hasActorIsolation() const { return ActorIso.hasValue(); } }; inline PhysicalPathComponent &PathComponent::asPhysical() { diff --git a/lib/SILGen/ManagedValue.cpp b/lib/SILGen/ManagedValue.cpp index 922e950f11237..677d24b08f257 100644 --- a/lib/SILGen/ManagedValue.cpp +++ b/lib/SILGen/ManagedValue.cpp @@ -37,6 +37,24 @@ ManagedValue ManagedValue::copy(SILGenFunction &SGF, SILLocation loc) const { return SGF.emitManagedRValueWithCleanup(buf, lowering); } +// Emit an unmanaged copy of this value +// WARNING: Callers of this API should manage the cleanup of this value! +ManagedValue ManagedValue::unmanagedCopy(SILGenFunction &SGF, + SILLocation loc) const { + auto &lowering = SGF.getTypeLowering(getType()); + if (lowering.isTrivial()) + return *this; + + if (getType().isObject()) { + auto copy = SGF.B.emitCopyValueOperation(loc, getValue()); + return ManagedValue::forUnmanaged(copy); + } + + SILValue buf = SGF.emitTemporaryAllocation(loc, getType()); + SGF.B.createCopyAddr(loc, getValue(), buf, IsNotTake, IsInitialization); + return ManagedValue::forUnmanaged(buf); +} + /// Emit a copy of this value with independent ownership. ManagedValue ManagedValue::formalAccessCopy(SILGenFunction &SGF, SILLocation loc) { diff --git a/lib/SILGen/ManagedValue.h b/lib/SILGen/ManagedValue.h index 981a848794510..28f346ba17b61 100644 --- a/lib/SILGen/ManagedValue.h +++ b/lib/SILGen/ManagedValue.h @@ -270,6 +270,10 @@ class ManagedValue { /// Emit a copy of this value with independent ownership. ManagedValue copy(SILGenFunction &SGF, SILLocation loc) const; + /// Returns an unmanaged copy of this value. + /// WARNING: Callers of this API should manage the cleanup of this value! + ManagedValue unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const; + /// Emit a copy of this value with independent ownership into the current /// formal evaluation scope. ManagedValue formalAccessCopy(SILGenFunction &SGF, SILLocation loc); diff --git a/lib/SILGen/ResultPlan.cpp b/lib/SILGen/ResultPlan.cpp index 0ef570fa510c2..23a79c0136dc5 100644 --- a/lib/SILGen/ResultPlan.cpp +++ b/lib/SILGen/ResultPlan.cpp @@ -37,7 +37,8 @@ class InPlaceInitializationResultPlan final : public ResultPlan { InPlaceInitializationResultPlan(Initialization *init) : init(init) {} RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { + ArrayRef &directResults, + SILValue bridgedForeignError) override { init->finishInitialization(SGF); return RValue::forInContext(); } @@ -169,7 +170,8 @@ class IndirectOpenedSelfResultPlan final : public ResultPlan { } RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { + ArrayRef &directResults, + SILValue bridgedForeignError) override { assert(resultBox && "never emitted temporary?!"); // Lower the unabstracted result type. @@ -209,7 +211,8 @@ class ScalarResultPlan final : public ResultPlan { rep(rep) {} RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { + ArrayRef &directResults, + SILValue bridgedForeignError) override { // Lower the unabstracted result type. auto &substTL = SGF.getTypeLowering(substType); @@ -309,8 +312,10 @@ class InitValueFromTemporaryResultPlan final : public ResultPlan { temporary(std::move(temporary)) {} RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { - RValue subResult = subPlan->finish(SGF, loc, substType, directResults); + ArrayRef &directResults, + SILValue bridgedForeignError) override { + RValue subResult = subPlan->finish(SGF, loc, substType, directResults, + bridgedForeignError); assert(subResult.isInContext() && "sub-plan didn't emit into context?"); (void)subResult; @@ -339,8 +344,10 @@ class InitValueFromRValueResultPlan final : public ResultPlan { : init(init), subPlan(std::move(subPlan)) {} RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { - RValue subResult = subPlan->finish(SGF, loc, substType, directResults); + ArrayRef &directResults, + SILValue bridgedForeignError) override { + RValue subResult = subPlan->finish(SGF, loc, substType, directResults, + bridgedForeignError); ManagedValue value = std::move(subResult).getAsSingleValue(SGF, loc); init->copyOrInitValueInto(SGF, loc, value, /*init*/ true); @@ -374,15 +381,17 @@ class TupleRValueResultPlan final : public ResultPlan { } RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { + ArrayRef &directResults, + SILValue bridgedForeignError) override { RValue tupleRV(substType); // Finish all the component tuples. auto substTupleType = cast(substType); assert(substTupleType.getElementTypes().size() == eltPlans.size()); for (auto i : indices(substTupleType.getElementTypes())) { - RValue eltRV = eltPlans[i]->finish( - SGF, loc, substTupleType.getElementType(i), directResults); + RValue eltRV = + eltPlans[i]->finish(SGF, loc, substTupleType.getElementType(i), + directResults, bridgedForeignError); tupleRV.addElement(std::move(eltRV)); } @@ -428,12 +437,14 @@ class TupleInitializationResultPlan final : public ResultPlan { } RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { + ArrayRef &directResults, + SILValue bridgedForeignError) override { auto substTupleType = cast(substType); assert(substTupleType.getElementTypes().size() == eltPlans.size()); for (auto i : indices(substTupleType.getElementTypes())) { auto eltType = substTupleType.getElementType(i); - RValue eltRV = eltPlans[i]->finish(SGF, loc, eltType, directResults); + RValue eltRV = eltPlans[i]->finish(SGF, loc, eltType, directResults, + bridgedForeignError); assert(eltRV.isInContext()); (void)eltRV; } @@ -465,8 +476,9 @@ class ForeignAsyncInitializationPlan final : public ResultPlan { { // Allocate space to receive the resume value when the continuation is // resumed. - opaqueResumeType = SGF.getLoweredType(AbstractionPattern::getOpaque(), - calleeTypeInfo.substResultType); + opaqueResumeType = + SGF.getLoweredType(AbstractionPattern(calleeTypeInfo.substResultType), + calleeTypeInfo.substResultType); resumeBuf = SGF.emitTemporaryAllocation(loc, opaqueResumeType); } @@ -475,14 +487,17 @@ class ForeignAsyncInitializationPlan final : public ResultPlan { SmallVectorImpl &outList) const override { // A foreign async function shouldn't have any indirect results. } - + ManagedValue - emitForeignAsyncCompletionHandler(SILGenFunction &SGF, SILLocation loc) - override { + emitForeignAsyncCompletionHandler(SILGenFunction &SGF, + AbstractionPattern origFormalType, + SILLocation loc) override { // Get the current continuation for the task. - bool throws = calleeTypeInfo.foreign.async - ->completionHandlerErrorParamIndex().hasValue(); - + bool throws = + calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex() + .hasValue() || + calleeTypeInfo.foreign.error.hasValue(); + continuation = SGF.B.createGetAsyncContinuationAddr(loc, resumeBuf, calleeTypeInfo.substResultType, throws); @@ -525,15 +540,15 @@ class ForeignAsyncInitializationPlan final : public ResultPlan { impFnTy = cast(impTy.getASTType()); } auto env = SGF.F.getGenericEnvironment(); - auto sig = env ? env->getGenericSignature()->getCanonicalSignature() + auto sig = env ? env->getGenericSignature().getCanonicalSignature() : CanGenericSignature(); - SILFunction *impl = SGF.SGM - .getOrCreateForeignAsyncCompletionHandlerImplFunction( - cast(impFnTy->mapTypeOutOfContext() - ->getCanonicalType(sig)), - continuationTy->mapTypeOutOfContext()->getCanonicalType(sig), - sig, - *calleeTypeInfo.foreign.async); + SILFunction *impl = + SGF.SGM.getOrCreateForeignAsyncCompletionHandlerImplFunction( + cast( + impFnTy->mapTypeOutOfContext()->getCanonicalType(sig)), + continuationTy->mapTypeOutOfContext()->getCanonicalType(sig), + origFormalType, sig, *calleeTypeInfo.foreign.async, + calleeTypeInfo.foreign.error); auto impRef = SGF.B.createFunctionRef(loc, impl); // Initialize the block object for the completion handler. @@ -551,20 +566,97 @@ class ForeignAsyncInitializationPlan final : public ResultPlan { // _Block_copy-ing it. return ManagedValue::forUnmanaged(block); } - + RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { + ArrayRef &directResults, + SILValue bridgedForeignError) override { // There should be no direct results from the call. assert(directResults.empty()); // Await the continuation we handed off to the completion handler. SILBasicBlock *resumeBlock = SGF.createBasicBlock(); SILBasicBlock *errorBlock = nullptr; - auto errorParamIndex = calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex(); - if (errorParamIndex) { + bool throws = + calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex() + .hasValue() || + calleeTypeInfo.foreign.error.hasValue(); + if (throws) { errorBlock = SGF.createBasicBlock(FunctionSection::Postmatter); } - + + auto *awaitBB = SGF.B.getInsertionBB(); + if (bridgedForeignError) { + // Avoid a critical edge from the block which branches to the await and + // foreign error blocks to the await block (to which the error block will + // be made to branch in a moment) by introducing a trampoline which will + // branch to the await block. + awaitBB = SGF.createBasicBlock(); + SGF.B.createBranch(loc, awaitBB); + + // Finish emitting the foreign error block: + // (1) fulfill the unsafe continuation with the foreign error + // (2) branch to the await block + { + // First, fulfill the unsafe continuation with the foreign error. + // Currently, that block's code looks something like + // %foreignError = ... : $*Optional + // %converter = function_ref _convertNSErrorToError(_:) + // %error = apply %converter(%foreignError) + // [... insert here ...] + // destroy_value %error + // destroy_value %foreignError + // Insert code to fulfill it after the native %error is defined. That + // code should structure the RawUnsafeContinuation (continuation) into + // an appropriately typed UnsafeContinuation and then pass that together + // with (a copy of) the error to + // _resumeUnsafeThrowingContinuationWithError. + // [foreign_error_block_with_foreign_async_convention] + SGF.B.setInsertionPoint( + ++bridgedForeignError->getDefiningInstruction()->getIterator()); + + auto continuationDecl = SGF.getASTContext().getUnsafeContinuationDecl(); + + auto errorTy = SGF.getASTContext().getExceptionType(); + auto continuationBGT = + BoundGenericType::get(continuationDecl, Type(), + {calleeTypeInfo.substResultType, errorTy}); + auto env = SGF.F.getGenericEnvironment(); + auto sig = env ? env->getGenericSignature().getCanonicalSignature() + : CanGenericSignature(); + auto mappedContinuationTy = + continuationBGT->mapTypeOutOfContext()->getCanonicalType(sig); + auto resumeType = + cast(mappedContinuationTy).getGenericArgs()[0]; + auto continuationTy = continuationBGT->getCanonicalType(); + + auto errorIntrinsic = + SGF.SGM.getResumeUnsafeThrowingContinuationWithError(); + Type replacementTypes[] = { + SGF.F.mapTypeIntoContext(resumeType)->getCanonicalType()}; + auto subs = SubstitutionMap::get(errorIntrinsic->getGenericSignature(), + replacementTypes, + ArrayRef{}); + auto wrappedContinuation = SGF.B.createStruct( + loc, SILType::getPrimitiveObjectType(continuationTy), + {continuation}); + + auto continuationMV = + ManagedValue::forUnmanaged(SILValue(wrappedContinuation)); + SGF.emitApplyOfLibraryIntrinsic( + loc, errorIntrinsic, subs, + {continuationMV, + ManagedValue::forUnmanaged(bridgedForeignError).copy(SGF, loc)}, + SGFContext()); + + // Second, emit a branch from the end of the foreign error block to the + // await block, to await the continuation which was just fulfilled. + SGF.B.setInsertionPoint( + bridgedForeignError->getDefiningInstruction()->getParent()); + SGF.B.createBranch(loc, awaitBB); + } + + SGF.B.emitBlock(awaitBB); + } SGF.B.createAwaitAsyncContinuation(loc, continuation, resumeBlock, errorBlock); // Propagate an error if we have one. @@ -654,8 +746,10 @@ class ForeignErrorInitializationPlan final : public ResultPlan { } RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) override { - return subPlan->finish(SGF, loc, substType, directResults); + ArrayRef &directResults, + SILValue bridgedForeignError) override { + return subPlan->finish(SGF, loc, substType, directResults, + bridgedForeignError); } void @@ -664,6 +758,13 @@ class ForeignErrorInitializationPlan final : public ResultPlan { subPlan->gatherIndirectResultAddrs(SGF, loc, outList); } + ManagedValue + emitForeignAsyncCompletionHandler(SILGenFunction &SGF, + AbstractionPattern origFormalType, + SILLocation loc) override { + return subPlan->emitForeignAsyncCompletionHandler(SGF, origFormalType, loc); + } + Optional> emitForeignErrorArgument(SILGenFunction &SGF, SILLocation loc) override { SILGenFunction::PointerAccessInfo pointerInfo = { @@ -694,12 +795,7 @@ class ForeignErrorInitializationPlan final : public ResultPlan { ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init, SILLocation loc) { // First check if we have a foreign error and/or async convention. - if (auto foreignAsync = calleeTypeInfo.foreign.async) { - // Create a result plan that gets the result schema from the completion - // handler callback's arguments. - // completion handler. - return ResultPlanPtr(new ForeignAsyncInitializationPlan(SGF, loc, calleeTypeInfo)); - } else if (auto foreignError = calleeTypeInfo.foreign.error) { + if (auto foreignError = calleeTypeInfo.foreign.error) { // Handle the foreign error first. // // The plan needs to be built using the formal result type after foreign-error @@ -708,7 +804,8 @@ ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init, // These conventions make the formal result type (). case ForeignErrorConvention::ZeroResult: case ForeignErrorConvention::NonZeroResult: - assert(calleeTypeInfo.substResultType->isVoid()); + assert(calleeTypeInfo.substResultType->isVoid() || + calleeTypeInfo.foreign.async); allResults.clear(); break; @@ -733,10 +830,21 @@ ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init, } } - ResultPlanPtr subPlan = build(init, calleeTypeInfo.origResultType.getValue(), - calleeTypeInfo.substResultType); + ResultPlanPtr subPlan; + if (auto foreignAsync = calleeTypeInfo.foreign.async) { + subPlan = ResultPlanPtr( + new ForeignAsyncInitializationPlan(SGF, loc, calleeTypeInfo)); + } else { + subPlan = build(init, calleeTypeInfo.origResultType.getValue(), + calleeTypeInfo.substResultType); + } return ResultPlanPtr(new ForeignErrorInitializationPlan( SGF, loc, calleeTypeInfo, std::move(subPlan))); + } else if (auto foreignAsync = calleeTypeInfo.foreign.async) { + // Create a result plan that gets the result schema from the completion + // handler callback's arguments. + return ResultPlanPtr( + new ForeignAsyncInitializationPlan(SGF, loc, calleeTypeInfo)); } else { // Otherwise, we can just call build. return build(init, calleeTypeInfo.origResultType.getValue(), diff --git a/lib/SILGen/ResultPlan.h b/lib/SILGen/ResultPlan.h index 39a62477dbfe7..503b71cd73de8 100644 --- a/lib/SILGen/ResultPlan.h +++ b/lib/SILGen/ResultPlan.h @@ -38,7 +38,8 @@ class CalleeTypeInfo; class ResultPlan { public: virtual RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, - ArrayRef &directResults) = 0; + ArrayRef &directResults, + SILValue bridgedForeignError) = 0; virtual ~ResultPlan() = default; virtual void @@ -49,9 +50,9 @@ class ResultPlan { emitForeignErrorArgument(SILGenFunction &SGF, SILLocation loc) { return None; } - - virtual ManagedValue - emitForeignAsyncCompletionHandler(SILGenFunction &SGF, SILLocation loc) { + + virtual ManagedValue emitForeignAsyncCompletionHandler( + SILGenFunction &SGF, AbstractionPattern origFormalType, SILLocation loc) { return {}; } }; diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 1a5c7546f9956..79bc1621ab7d6 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -181,12 +181,23 @@ static FuncDecl *diagnoseMissingIntrinsic(SILGenModule &sgm, #define FUNC_DECL(NAME, ID) \ FuncDecl *SILGenModule::get##NAME(SILLocation loc) { \ - if (auto fn = getASTContext().get##NAME()) \ + if (auto fn = getASTContext().get##NAME()) \ return fn; \ return diagnoseMissingIntrinsic(*this, loc, ID); \ } #include "swift/AST/KnownDecls.def" +#define KNOWN_SDK_FUNC_DECL(MODULE, NAME, ID) \ + FuncDecl *SILGenModule::get##NAME(SILLocation loc) { \ + if (ModuleDecl *M = getASTContext().getLoadedModule( \ + getASTContext().Id_##MODULE)) { \ + if (auto fn = getASTContext().get##NAME()) \ + return fn; \ + } \ + return diagnoseMissingIntrinsic(*this, loc, ID); \ + } +#include "swift/AST/KnownSDKDecls.def" + ProtocolDecl *SILGenModule::getObjectiveCBridgeable(SILLocation loc) { if (ObjectiveCBridgeable) return *ObjectiveCBridgeable; @@ -365,21 +376,21 @@ FuncDecl * SILGenModule::getAsyncLetGet() { return lookupConcurrencyIntrinsic(getASTContext(), AsyncLetGet, - "_asyncLetGet"); + "_asyncLet_get"); } FuncDecl * SILGenModule::getAsyncLetGetThrowing() { return lookupConcurrencyIntrinsic(getASTContext(), AsyncLetGetThrowing, - "_asyncLetGetThrowing"); + "_asyncLet_get_throwing"); } FuncDecl * -SILGenModule::getEndAsyncLet() { +SILGenModule::getFinishAsyncLet() { return lookupConcurrencyIntrinsic(getASTContext(), EndAsyncLet, - "_asyncLetEnd"); + "_asyncLet_finish"); } FuncDecl * @@ -421,11 +432,6 @@ SILGenModule::getRunTaskForBridgedAsyncMethod() { "_runTaskForBridgedAsyncMethod"); } FuncDecl * -SILGenModule::getRunAsyncHandler() { - return lookupConcurrencyIntrinsic(getASTContext(), RunAsyncHandler, - "_runAsyncHandler"); -} -FuncDecl * SILGenModule::getCheckExpectedExecutor() { return lookupConcurrencyIntrinsic(getASTContext(), CheckExpectedExecutor, "_checkExpectedExecutor"); @@ -528,7 +534,7 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, SubstitutionMap(), getASTContext()); - auto env = sig->getGenericEnvironment(); + auto env = sig.getGenericEnvironment(); SILGenFunctionBuilder builder(*this); fn = builder.createFunction(SILLinkage::PublicExternal, @@ -544,65 +550,6 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, return fn; } - -SILFunction *SILGenModule::emitTopLevelFunction(SILLocation Loc) { - ASTContext &C = getASTContext(); - - // Use standard library types if we have them; otherwise, fall back to - // builtins. - CanType Int32Ty; - if (auto Int32Decl = C.getInt32Decl()) { - Int32Ty = Int32Decl->getDeclaredInterfaceType()->getCanonicalType(); - } else { - Int32Ty = CanType(BuiltinIntegerType::get(32, C)); - } - - CanType PtrPtrInt8Ty = C.TheRawPointerType; - if (auto PointerDecl = C.getUnsafeMutablePointerDecl()) { - if (auto Int8Decl = C.getInt8Decl()) { - Type Int8Ty = Int8Decl->getDeclaredInterfaceType(); - Type PointerInt8Ty = BoundGenericType::get(PointerDecl, - nullptr, - Int8Ty); - Type OptPointerInt8Ty = OptionalType::get(PointerInt8Ty); - PtrPtrInt8Ty = BoundGenericType::get(PointerDecl, - nullptr, - OptPointerInt8Ty) - ->getCanonicalType(); - } - } - - SILParameterInfo params[] = { - SILParameterInfo(Int32Ty, ParameterConvention::Direct_Unowned), - SILParameterInfo(PtrPtrInt8Ty, ParameterConvention::Direct_Unowned), - }; - SILResultInfo results[] = {SILResultInfo(Int32Ty, ResultConvention::Unowned)}; - - auto rep = SILFunctionType::Representation::CFunctionPointer; - auto *clangTy = C.getCanonicalClangFunctionType(params, results[0], rep); - auto extInfo = SILFunctionType::ExtInfoBuilder() - .withRepresentation(rep) - .withClangFunctionType(clangTy) - .build(); - - CanSILFunctionType topLevelType = SILFunctionType::get(nullptr, extInfo, - SILCoroutineKind::None, - ParameterConvention::Direct_Unowned, - params, /*yields*/ {}, - SILResultInfo(Int32Ty, - ResultConvention::Unowned), - None, - SubstitutionMap(), SubstitutionMap(), - C); - - auto name = getASTContext().getEntryPointFunctionName(); - SILGenFunctionBuilder builder(*this); - return builder.createFunction( - SILLinkage::Public, name, topLevelType, nullptr, - Loc, IsBare, IsNotTransparent, IsNotSerialized, IsNotDynamic, - ProfileCounter(), IsNotThunk, SubclassScope::NotApplicable); -} - SILFunction *SILGenModule::getEmittedFunction(SILDeclRef constant, ForDefinition_t forDefinition) { auto found = emittedFunctions.find(constant); @@ -829,6 +776,23 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { return; } + if (constant.isDistributedThunk()) { + auto loc = constant.getAsRegularLocation(); + loc.markAutoGenerated(); + auto *dc = loc.getAsDeclContext(); + assert(dc); + + preEmitFunction(constant, f, loc); + PrettyStackTraceSILFunction X("silgen emitDistributedThunk", f); + f->setBare(IsBare); + f->setThunk(IsThunk); + + SILGenFunction(*this, *f, dc).emitDistributedThunk(constant); + + postEmitFunction(constant, f); + return; + } + switch (constant.kind) { case SILDeclRef::Kind::Func: { if (auto *ce = constant.getAbstractClosureExpr()) { @@ -1072,6 +1036,20 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { postEmitFunction(constant, f); return; } + case SILDeclRef::Kind::EntryPoint: { + f->setBare(IsBare); + + // TODO: Handle main SourceFile emission (currently done by + // SourceFileScope). + auto loc = RegularLocation::getModuleLocation(); + preEmitFunction(constant, f, loc); + auto *decl = constant.getDecl(); + auto *dc = decl->getDeclContext(); + PrettyStackTraceSILFunction X("silgen emitArtificialTopLevel", f); + SILGenFunction(*this, *f, dc).emitArtificialTopLevel(decl); + postEmitFunction(constant, f); + return; + } } } @@ -1299,8 +1277,8 @@ emitMarkFunctionEscapeForTopLevelCodeGlobals(SILLocation loc, } void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) { - // Emit any default argument generators. - emitDefaultArgGenerators(AFD, AFD->getParameters()); + // Emit default arguments and property wrapper initializers. + emitArgumentGenerators(AFD, AFD->getParameters()); // If this is a function at global scope, it may close over a global variable. // If we're emitting top-level code, then emit a "mark_function_escape" that @@ -1318,6 +1296,11 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) { if (!hasFunction(thunk)) emitNativeToForeignThunk(thunk); } + + if (AFD->isDistributed()) { + auto thunk = SILDeclRef(AFD).asDistributed(); + emitDistributedThunk(thunk); + } } void SILGenModule::emitFunction(FuncDecl *fd) { @@ -1389,6 +1372,9 @@ SILFunction *SILGenModule::emitClosure(AbstractClosureExpr *ce) { if (!f->isExternalDeclaration()) return f; + // Emit property wrapper argument generators. + emitArgumentGenerators(ce, ce->getParameters()); + emitFunctionDefinition(constant, f); return f; } @@ -1575,13 +1561,17 @@ void SILGenModule::emitGlobalAccessor(VarDecl *global, emitOrDelayFunction(*this, accessor); } -void SILGenModule::emitDefaultArgGenerators(SILDeclRef::Loc decl, - ParameterList *paramList) { +void SILGenModule::emitArgumentGenerators(SILDeclRef::Loc decl, + ParameterList *paramList) { unsigned index = 0; for (auto param : *paramList) { if (param->isDefaultArgument()) emitDefaultArgGenerator(SILDeclRef::getDefaultArgGenerator(decl, index), param); + + if (param->hasExternalPropertyWrapper()) + emitPropertyWrapperBackingInitializer(param); + ++index; } } @@ -1873,10 +1863,9 @@ namespace { /// An RAII class to scope source file codegen. class SourceFileScope { SILGenModule &sgm; - SourceFile *sf; Optional scope; public: - SourceFileScope(SILGenModule &sgm, SourceFile *sf) : sgm(sgm), sf(sf) { + SourceFileScope(SILGenModule &sgm, SourceFile *sf) : sgm(sgm) { // If this is the script-mode file for the module, create a toplevel. if (sf->isScriptMode()) { assert(!sgm.TopLevelSGF && "already emitted toplevel?!"); @@ -1885,7 +1874,9 @@ class SourceFileScope { "already emitted toplevel?!"); RegularLocation TopLevelLoc = RegularLocation::getModuleLocation(); - SILFunction *toplevel = sgm.emitTopLevelFunction(TopLevelLoc); + auto ref = SILDeclRef::getMainFileEntryPoint(sf); + auto *toplevel = sgm.getFunction(ref, ForDefinition); + toplevel->setBare(IsBare); // Assign a debug scope pointing into the void to the top level function. toplevel->setDebugScope(new (sgm.M) SILDebugScope(TopLevelLoc, toplevel)); @@ -2000,31 +1991,6 @@ class SourceFileScope { toplevel->verify(); sgm.emitLazyConformancesForFunction(toplevel); } - - // If the source file contains an artificial main, emit the implicit - // toplevel code. - if (auto mainDecl = sf->getMainDecl()) { - assert(!sgm.M.lookUpFunction( - sgm.getASTContext().getEntryPointFunctionName()) && - "already emitted toplevel before main class?!"); - - RegularLocation TopLevelLoc = RegularLocation::getModuleLocation(); - SILFunction *toplevel = sgm.emitTopLevelFunction(TopLevelLoc); - - // Assign a debug scope pointing into the void to the top level function. - toplevel->setDebugScope(new (sgm.M) SILDebugScope(TopLevelLoc, toplevel)); - - // Create the argc and argv arguments. - SILGenFunction SGF(sgm, *toplevel, sf); - auto entry = SGF.B.getInsertionBB(); - auto paramTypeIter = - SGF.F.getConventions() - .getParameterSILTypes(SGF.getTypeExpansionContext()) - .begin(); - entry->createFunctionArgument(*paramTypeIter); - entry->createFunctionArgument(*std::next(paramTypeIter)); - SGF.emitArtificialTopLevel(mainDecl); - } } }; @@ -2039,7 +2005,7 @@ class SILGenModuleRAII { performTypeChecking(*sf); SourceFileScope scope(SGM, sf); - for (Decl *D : sf->getTopLevelDecls()) { + for (auto *D : sf->getTopLevelDecls()) { FrontendStatsTracer StatsTracer(SGM.getASTContext().Stats, "SILgen-decl", D); SGM.visit(D); @@ -2060,7 +2026,13 @@ class SILGenModuleRAII { continue; SGM.visit(TD); } + + // If the source file contains an artificial main, emit the implicit + // top-level code. + if (auto *mainDecl = sf->getMainDecl()) + emitSILFunctionDefinition(SILDeclRef::getMainDeclEntryPoint(mainDecl)); } + void emitSILFunctionDefinition(SILDeclRef ref) { SGM.emitFunctionDefinition(ref, SGM.getFunction(ref, ForDefinition)); } diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 4196194ce7b63..53a823a1b39b2 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -86,8 +86,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Set of delayed conformances that have already been forced. llvm::DenseSet forcedConformances; - SILFunction *emitTopLevelFunction(SILLocation Loc); - size_t anonymousSymbolCounter = 0; Optional StringToNSStringFn; @@ -131,7 +129,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { Optional ResumeUnsafeContinuation; Optional ResumeUnsafeThrowingContinuation; Optional ResumeUnsafeThrowingContinuationWithError; - Optional RunAsyncHandler; Optional CheckExpectedExecutor; public: @@ -178,16 +175,17 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { CanSILFunctionType thunkType, CanSILFunctionType fromType, CanSILFunctionType toType, - CanType dynamicSelfType); + CanType dynamicSelfType, + CanType fromGlobalActor); /// Get or create the declaration of a completion handler block /// implementation function for an ObjC API that was imported /// as `async` in Swift. SILFunction *getOrCreateForeignAsyncCompletionHandlerImplFunction( - CanSILFunctionType blockType, - CanType continuationTy, - CanGenericSignature sig, - ForeignAsyncConvention convention); + CanSILFunctionType blockType, CanType continuationTy, + AbstractionPattern origFormalType, CanGenericSignature sig, + ForeignAsyncConvention convention, + Optional foreignError); /// Determine whether the given class has any instance variables that /// need to be destroyed. @@ -320,15 +318,18 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Emits the backing initializer for a property with an attached wrapper. void emitPropertyWrapperBackingInitializer(VarDecl *var); - /// Emits default argument generators for the given parameter list. - void emitDefaultArgGenerators(SILDeclRef::Loc decl, - ParameterList *paramList); + /// Emits argument generators, including default argument generators and + /// property wrapper argument generators, for the given parameter list. + void emitArgumentGenerators(SILDeclRef::Loc decl, ParameterList *paramList); /// Emits a thunk from a foreign function to the native Swift convention. void emitForeignToNativeThunk(SILDeclRef thunk); /// Emits a thunk from a Swift function to the native Swift convention. void emitNativeToForeignThunk(SILDeclRef thunk); + + /// Emits a thunk from an actor function to a potentially distributed call. + void emitDistributedThunk(SILDeclRef thunk); void preEmitFunction(SILDeclRef constant, SILFunction *F, SILLocation L); void postEmitFunction(SILDeclRef constant, SILFunction *F); @@ -452,6 +453,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { #define FUNC_DECL(NAME, ID) \ FuncDecl *get##NAME(SILLocation loc); #include "swift/AST/KnownDecls.def" + +#define KNOWN_SDK_FUNC_DECL(MODULE, NAME, ID) \ + FuncDecl *get##NAME(SILLocation loc); +#include "swift/AST/KnownSDKDecls.def" /// Retrieve the _ObjectiveCBridgeable protocol definition. ProtocolDecl *getObjectiveCBridgeable(SILLocation loc); @@ -492,8 +497,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { FuncDecl *getAsyncLetGet(); /// Retrieve the _Concurrency._asyncLetGetThrowing intrinsic. FuncDecl *getAsyncLetGetThrowing(); - /// Retrieve the _Concurrency._asyncLetEnd intrinsic. - FuncDecl *getEndAsyncLet(); + /// Retrieve the _Concurrency._asyncLetFinish intrinsic. + FuncDecl *getFinishAsyncLet(); /// Retrieve the _Concurrency._taskFutureGet intrinsic. FuncDecl *getTaskFutureGet(); @@ -507,8 +512,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { FuncDecl *getResumeUnsafeThrowingContinuation(); /// Retrieve the _Concurrency._resumeUnsafeThrowingContinuationWithError intrinsic. FuncDecl *getResumeUnsafeThrowingContinuationWithError(); - /// Retrieve the _Concurrency._runAsyncHandler intrinsic. - FuncDecl *getRunAsyncHandler(); /// Retrieve the _Concurrency._runTaskForBridgedAsyncMethod intrinsic. FuncDecl *getRunTaskForBridgedAsyncMethod(); /// Retrieve the _Concurrency._checkExpectedExecutor intrinsic. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index f4803d6b45a1a..f1f00be426a2a 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -548,15 +548,15 @@ class Callee { return result; auto func = cast(constant->getDecl()); - result.foreign = CalleeTypeInfo::ForeignInfo{ - func->getForeignErrorConvention(), - func->getForeignAsyncConvention(), - func->getImportAsMemberStatus(), + result.foreign = ForeignInfo{ + func->getImportAsMemberStatus(), + func->getForeignErrorConvention(), + func->getForeignAsyncConvention(), }; // Remove the metatype "self" parameter by making this a static member. - if (constant->getDecl()->getClangDecl() && - isa(constant->getDecl()->getClangDecl())) + if (isa_and_nonnull( + constant->getDecl()->getClangDecl())) result.foreign.self.setStatic(); return result; @@ -1051,7 +1051,12 @@ class SILGenApply : public Lowering::ExprVisitor { return; } - auto constant = SILDeclRef(afd).asForeign(requiresForeignEntryPoint(afd)); + SILDeclRef constant = SILDeclRef(afd); + if (afd->isDistributed()) { + constant = constant.asDistributed(true); + } else { + constant = constant.asForeign(requiresForeignEntryPoint(afd)); + } auto subs = e->getDeclRef().getSubstitutions(); @@ -1108,9 +1113,14 @@ class SILGenApply : public Lowering::ExprVisitor { } // Otherwise, we have a statically-dispatched call. - auto constant = SILDeclRef(e->getDecl()) - .asForeign(!isConstructorWithGeneratedAllocatorThunk(e->getDecl()) - && requiresForeignEntryPoint(e->getDecl())); + auto constant = SILDeclRef(e->getDecl()); + if (e->getDecl()->getAttrs().hasAttribute()) { + constant = constant.asDistributed(true); + } else { + constant = constant.asForeign( + !isConstructorWithGeneratedAllocatorThunk(e->getDecl()) + && requiresForeignEntryPoint(e->getDecl())); + } auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(constant); if (afd->getDeclContext()->isLocalContext() && @@ -1156,6 +1166,27 @@ class SILGenApply : public Lowering::ExprVisitor { } } + void visitMemberRefExpr(MemberRefExpr *e) { + // If we're loading a closure-type property out of a generic aggregate, + // we might reabstract it under normal circumstances, but since we're + // going to apply it immediately here, there's no reason to. We can + // invoke the function value at whatever abstraction level we get. + assert(isa(e->getMember().getDecl())); + + // Any writebacks for this access are tightly scoped. + FormalEvaluationScope scope(SGF); + + LValue lv = SGF.emitLValue(e, SGFAccessKind::OwnedObjectRead); + if (lv.isLastComponentTranslation()) + lv.dropLastTranslationComponent(); + + ManagedValue fn = SGF.emitLoadOfLValue(e, std::move(lv), SGFContext()) + .getAsSingleValue(SGF, e); + + setCallee(Callee::forIndirect(fn, lv.getOrigFormalType(), + cast(lv.getSubstFormalType()), e)); + } + void visitAbstractClosureExpr(AbstractClosureExpr *e) { // Emit the closure body. SGF.SGM.emitClosure(e); @@ -1917,6 +1948,7 @@ buildBuiltinLiteralArgs(SILGenFunction &SGF, SGFContext C, case MagicIdentifierLiteralExpr::DSOHandle: llvm_unreachable("handled elsewhere"); } + llvm_unreachable("covered switch"); } static inline PreparedArguments buildBuiltinLiteralArgs(SILGenFunction &SGF, @@ -2732,7 +2764,7 @@ class ArgEmitter { SILFunctionTypeRepresentation Rep; bool IsYield; bool IsForCoroutine; - CalleeTypeInfo::ForeignInfo Foreign; + ForeignInfo Foreign; ClaimedParamsRef ParamInfos; SmallVectorImpl &Args; @@ -2745,10 +2777,10 @@ class ArgEmitter { bool isYield, bool isForCoroutine, ClaimedParamsRef paramInfos, SmallVectorImpl &args, SmallVectorImpl &delayedArgs, - const CalleeTypeInfo::ForeignInfo &foreign) + const ForeignInfo &foreign) : SGF(SGF), Rep(Rep), IsYield(isYield), IsForCoroutine(isForCoroutine), - Foreign(foreign), - ParamInfos(paramInfos), Args(args), DelayedArguments(delayedArgs) {} + Foreign(foreign), ParamInfos(paramInfos), Args(args), + DelayedArguments(delayedArgs) {} // origParamType is a parameter type. void emitSingleArg(ArgumentSource &&arg, AbstractionPattern origParamType) { @@ -3330,11 +3362,9 @@ void DelayedArgument::emitDefaultArgument(SILGenFunction &SGF, SmallVector loweredArgs; SmallVector delayedArgs; - auto emitter = - ArgEmitter(SGF, info.functionRepresentation, /*yield*/ false, - /*coroutine*/ false, info.paramsToEmit, loweredArgs, - delayedArgs, - CalleeTypeInfo::ForeignInfo{}); + auto emitter = ArgEmitter(SGF, info.functionRepresentation, /*yield*/ false, + /*coroutine*/ false, info.paramsToEmit, loweredArgs, + delayedArgs, ForeignInfo{}); emitter.emitSingleArg(ArgumentSource(info.loc, std::move(value)), info.origResultType); @@ -3496,10 +3526,9 @@ struct ParamLowering { Rep(fnType->getRepresentation()), fnConv(fnType, SGF.SGM.M), typeExpansionContext(SGF.getTypeExpansionContext()) {} - ClaimedParamsRef - claimParams(AbstractionPattern origFormalType, - ArrayRef substParams, - const CalleeTypeInfo::ForeignInfo &foreign) { + ClaimedParamsRef claimParams(AbstractionPattern origFormalType, + ArrayRef substParams, + const ForeignInfo &foreign) { unsigned count = 0; if (!foreign.self.isStatic()) { for (auto i : indices(substParams)) { @@ -3608,7 +3637,7 @@ class CallSite { CanSILFunctionType substFnType, ParamLowering &lowering, SmallVectorImpl &args, SmallVectorImpl &delayedArgs, - const CalleeTypeInfo::ForeignInfo &foreign) && { + const ForeignInfo &foreign) && { auto params = lowering.claimParams(origFormalType, getParams(), foreign); ArgEmitter emitter(SGF, lowering.Rep, /*yield*/ false, @@ -3651,7 +3680,8 @@ class CallEmission { Callee callee; FormalEvaluationScope initialWritebackScope; - Optional implicitAsyncIsolation; + Optional implicitActorHopTarget; + bool implicitlyThrows; public: /// Create an emission for a call of the given callee. @@ -3659,7 +3689,8 @@ class CallEmission { FormalEvaluationScope &&writebackScope) : SGF(SGF), callee(std::move(callee)), initialWritebackScope(std::move(writebackScope)), - implicitAsyncIsolation(None) {} + implicitActorHopTarget(None), + implicitlyThrows(false) {} /// A factory method for decomposing the apply expr \p e into a call /// emission. @@ -3699,10 +3730,17 @@ class CallEmission { /// Sets a flag that indicates whether this call be treated as being /// implicitly async, i.e., it requires a hop_to_executor prior to /// invoking the sync callee, etc. - void setImplicitlyAsync(Optional implicitAsyncIsolation) { - this->implicitAsyncIsolation = implicitAsyncIsolation; + void setImplicitlyAsync( + Optional implicitActorHopTarget) { + this->implicitActorHopTarget = implicitActorHopTarget; } + /// Sets a flag that indicates whether this call be treated as being + /// implicitly throws, i.e., the call may be delegating to a proxy function + /// which actually is throwing, regardless whether or not the actual target + /// function can throw or not. + void setImplicitlyThrows(bool flag) { implicitlyThrows = flag; } + CleanupHandle applyCoroutine(SmallVectorImpl &yields); RValue apply(SGFContext C = SGFContext()) { @@ -3740,8 +3778,7 @@ class CallEmission { /// ApplyOptions. ApplyOptions emitArgumentsForNormalApply( AbstractionPattern origFormalType, CanSILFunctionType substFnType, - const CalleeTypeInfo::ForeignInfo &foreign, - SmallVectorImpl &uncurriedArgs, + const ForeignInfo &foreign, SmallVectorImpl &uncurriedArgs, Optional &uncurriedLoc); RValue @@ -3874,6 +3911,8 @@ RValue CallEmission::applyNormalCall(SGFContext C) { // Get the callee type information. auto calleeTypeInfo = callee.getTypeInfo(SGF); + calleeTypeInfo.origFormalType = origFormalType; + // In C language modes, substitute the type of the AbstractionPattern // so that we won't see type parameters down when we try to form bridging // conversions. @@ -3891,6 +3930,8 @@ RValue CallEmission::applyNormalCall(SGFContext C) { calleeTypeInfo.substResultType = formalType.getResult(); if (selfArg.hasValue() && callSite.hasValue()) { + calleeTypeInfo.origFormalType = + calleeTypeInfo.origFormalType->getFunctionResultType(); calleeTypeInfo.origResultType = calleeTypeInfo.origResultType->getFunctionResultType(); calleeTypeInfo.substResultType = @@ -3926,7 +3967,7 @@ RValue CallEmission::applyNormalCall(SGFContext C) { return SGF.emitApply( std::move(resultPlan), std::move(argScope), uncurriedLoc.getValue(), mv, callee.getSubstitutions(), uncurriedArgs, calleeTypeInfo, options, - uncurriedContext, implicitAsyncIsolation); + uncurriedContext, implicitActorHopTarget); } static void emitPseudoFunctionArguments(SILGenFunction &SGF, @@ -3977,7 +4018,7 @@ RValue CallEmission::applyEnumElementConstructor(SGFContext C) { resultFnType, argVals, std::move(*callSite).forward()); - auto payloadTy = AnyFunctionType::composeInput(SGF.getASTContext(), + auto payloadTy = AnyFunctionType::composeTuple(SGF.getASTContext(), resultFnType.getParams(), /*canonicalVararg*/ true); auto arg = RValue(SGF, argVals, payloadTy->getCanonicalType()); @@ -4056,8 +4097,7 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, SmallVector uncurriedArgs; Optional uncurriedLoc; CanFunctionType formalApplyType; - emitArgumentsForNormalApply(origFormalType, substFnType, - CalleeTypeInfo::ForeignInfo{}, + emitArgumentsForNormalApply(origFormalType, substFnType, ForeignInfo{}, uncurriedArgs, uncurriedLoc); // If we have a late emitter, now that we have emitted our arguments, call the @@ -4112,7 +4152,7 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, // Then finish our value. if (resultPlan.hasValue()) { return std::move(*resultPlan) - ->finish(SGF, loc, formalResultType, directResultsFinal); + ->finish(SGF, loc, formalResultType, directResultsFinal, SILValue()); } else { return RValue( SGF, *uncurriedLoc, formalResultType, directResultsFinal[0]); @@ -4121,8 +4161,7 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, ApplyOptions CallEmission::emitArgumentsForNormalApply( AbstractionPattern origFormalType, CanSILFunctionType substFnType, - const CalleeTypeInfo::ForeignInfo &foreign, - SmallVectorImpl &uncurriedArgs, + const ForeignInfo &foreign, SmallVectorImpl &uncurriedArgs, Optional &uncurriedLoc) { ApplyOptions options; @@ -4157,7 +4196,7 @@ ApplyOptions CallEmission::emitArgumentsForNormalApply( args.push_back({}); // Claim the foreign "self" with the self param. - auto siteForeign = CalleeTypeInfo::ForeignInfo{{}, {}, foreign.self}; + auto siteForeign = ForeignInfo{foreign.self, {}, {}}; std::move(*selfArg).emit(SGF, origFormalType, substFnType, paramLowering, args.back(), delayedArgs, siteForeign); @@ -4167,13 +4206,18 @@ ApplyOptions CallEmission::emitArgumentsForNormalApply( args.push_back({}); - // Claim the foreign error and/or async argument(s) with the method - // formal params. - auto siteForeign = - CalleeTypeInfo::ForeignInfo{foreign.error, foreign.async, {}}; - std::move(*callSite).emit(SGF, origFormalType, substFnType, paramLowering, - args.back(), delayedArgs, - siteForeign); + if (!foreign.async || (foreign.async && foreign.error)) { + // Claim the foreign error argument(s) with the method formal params. + auto siteForeignError = ForeignInfo{{}, foreign.error, {}}; + std::move(*callSite).emit(SGF, origFormalType, substFnType, paramLowering, + args.back(), delayedArgs, siteForeignError); + } + if (foreign.async) { + // Claim the foreign async argument(s) with the method formal params. + auto siteForeignAsync = ForeignInfo{{}, {}, foreign.async}; + std::move(*callSite).emit(SGF, origFormalType, substFnType, paramLowering, + args.back(), delayedArgs, siteForeignAsync); + } } uncurriedLoc = callSite->Loc; @@ -4233,7 +4277,7 @@ CallEmission CallEmission::forApplyExpr(SILGenFunction &SGF, ApplyExpr *e) { Expr *arg = apply.callSite->getArg(); SmallVector params; - AnyFunctionType::decomposeInput(arg->getType(), params); + AnyFunctionType::decomposeTuple(arg->getType(), params); PreparedArguments preparedArgs(params, arg); @@ -4241,28 +4285,9 @@ CallEmission CallEmission::forApplyExpr(SILGenFunction &SGF, ApplyExpr *e) { apply.callSite->isNoThrows(), apply.callSite->isNoAsync()); - // For an implicitly-async call, determine the actor isolation. - if (apply.callSite->implicitlyAsync()) { - Optional isolation; - - // Check for global-actor isolation on the function type. - if (auto fnType = apply.callSite->getFn()->getType() - ->castTo()) { - if (Type globalActor = fnType->getGlobalActor()) { - isolation = ActorIsolation::forGlobalActor(globalActor, false); - } - } - - // If there was no global-actor isolation on the function type, find - // the callee declaration and retrieve the isolation from it. - if (!isolation) { - if (auto decl = emission.callee.getDecl()) - isolation = getActorIsolation(decl); - } - - assert(isolation && "Implicitly asynchronous call without isolation"); - emission.setImplicitlyAsync(isolation); - } + // For an implicitly-async call, record the target of the actor hop. + if (auto target = apply.callSite->isImplicitlyAsync()) + emission.setImplicitlyAsync(target); } return emission; @@ -4306,6 +4331,29 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) { return self.isFormalIndirect(); } +namespace { +/// Cleanup to insert fix_lifetime and destroy +class FixLifetimeDestroyCleanup : public Cleanup { + SILValue val; + +public: + FixLifetimeDestroyCleanup(SILValue val) : val(val) {} + + void emit(SILGenFunction &SGF, CleanupLocation l, + ForUnwind_t forUnwind) override { + SGF.B.emitFixLifetime(l, val); + SGF.B.emitDestroyOperation(l, val); + } + + void dump(SILGenFunction &SGF) const override { +#ifndef NDEBUG + llvm::errs() << "FixLifetimeDestroyCleanup " + << "State:" << getState() << " " + << "Value: " << val << "\n"; +#endif + } +}; +} // end anonymous namespace //===----------------------------------------------------------------------===// // Top Level Entrypoints @@ -4315,13 +4363,14 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) { /// lowered appropriately for the abstraction level but that the /// result does need to be turned back into something matching a /// formal type. -RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, - ArgumentScope &&argScope, SILLocation loc, - ManagedValue fn, SubstitutionMap subs, - ArrayRef args, - const CalleeTypeInfo &calleeTypeInfo, - ApplyOptions options, SGFContext evalContext, - Optional implicitAsyncIsolation) { +RValue SILGenFunction::emitApply( + ResultPlanPtr &&resultPlan, + ArgumentScope &&argScope, SILLocation loc, + ManagedValue fn, SubstitutionMap subs, + ArrayRef args, + const CalleeTypeInfo &calleeTypeInfo, + ApplyOptions options, SGFContext evalContext, + Optional implicitActorHopTarget) { auto substFnType = calleeTypeInfo.substFnType; auto substResultType = calleeTypeInfo.substResultType; @@ -4372,13 +4421,15 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, if (auto foreignAsync = calleeTypeInfo.foreign.async) { unsigned completionIndex = foreignAsync->completionHandlerParamIndex(); - // Ram the emitted error into the argument list, over the placeholder + // Ram the emitted completion into the argument list, over the placeholder // we left during the first pass. auto &completionArgSlot = const_cast(args[completionIndex]); - completionArgSlot = resultPlan->emitForeignAsyncCompletionHandler(*this, loc); - - } else if (auto foreignError = calleeTypeInfo.foreign.error) { + auto origFormalType = *calleeTypeInfo.origFormalType; + completionArgSlot = resultPlan->emitForeignAsyncCompletionHandler( + *this, origFormalType, loc); + } + if (auto foreignError = calleeTypeInfo.foreign.error) { unsigned errorParamIndex = foreignError->getErrorParameterIndex(); @@ -4408,30 +4459,34 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, } ExecutorBreadcrumb breadcrumb; - - // The presence of `implicitAsyncIsolation` indicates that the callee is a + + // The presence of `implicitActorHopTarget` indicates that the callee is a // synchronous function isolated to an actor other than our own. // Such functions require the caller to hop to the callee's executor // prior to invoking the callee. - if (implicitAsyncIsolation) { + if (implicitActorHopTarget) { assert(F.isAsync() && "cannot hop_to_executor in a non-async func!"); - switch (*implicitAsyncIsolation) { - case ActorIsolation::ActorInstance: - breadcrumb = emitHopToTargetActor(loc, *implicitAsyncIsolation, - args.back()); + SILValue executor; + switch (*implicitActorHopTarget) { + case ImplicitActorHopTarget::InstanceSelf: + executor = emitLoadActorExecutor(loc, args.back()); break; - case ActorIsolation::GlobalActor: - case ActorIsolation::GlobalActorUnsafe: - breadcrumb = emitHopToTargetActor(loc, *implicitAsyncIsolation, None); + case ImplicitActorHopTarget::GlobalActor: + executor = emitLoadGlobalActorExecutor( + implicitActorHopTarget->getGlobalActor()); break; - case ActorIsolation::Independent: - case ActorIsolation::Unspecified: - llvm_unreachable("Not actor-isolated"); + case ImplicitActorHopTarget::IsolatedParameter: + executor = emitLoadActorExecutor( + loc, args[implicitActorHopTarget->getIsolatedParameterIndex()]); + break; } - } else if (ExpectedExecutor && substFnType->isAsync()) { + + breadcrumb = emitHopToTargetExecutor(loc, executor); + } else if (ExpectedExecutor && + (substFnType->isAsync() || calleeTypeInfo.foreign.async)) { // Otherwise, if we're in an actor method ourselves, and we're calling into // any sort of async function, we'll want to make sure to hop back to our // own executor afterward, since the callee could have made arbitrary hops @@ -4448,8 +4503,23 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, rawDirectResult = rawDirectResults[0]; } - // hop back to the current executor - breadcrumb.emit(*this, loc); + if (!calleeTypeInfo.foreign.async) { + // hop back to the current executor + breadcrumb.emit(*this, loc); + } + + // For objc async calls, lifetime extend the args until the result plan which + // generates `await_async_continuation`. + // Lifetime is extended by creating unmanaged copies here and by pushing the + // cleanups required just before the result plan is generated. + SmallVector unmanagedCopies; + if (calleeTypeInfo.foreign.async) { + for (auto arg : args) { + if (arg.hasCleanup()) { + unmanagedCopies.push_back(arg.unmanagedCopy(*this, loc)); + } + } + } // Pop the argument scope. argScope.pop(); @@ -4518,20 +4588,43 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, }); } + SILValue bridgedForeignError; // If there was a foreign error convention, consider it. // TODO: maybe this should happen after managing the result if it's // not a result-checking convention? if (auto foreignError = calleeTypeInfo.foreign.error) { bool doesNotThrow = options.contains(ApplyFlags::DoesNotThrow); - emitForeignErrorCheck(loc, directResults, errorTemp, doesNotThrow, - *foreignError); + bridgedForeignError = + emitForeignErrorCheck(loc, directResults, errorTemp, doesNotThrow, + *foreignError, calleeTypeInfo.foreign.async); } - + + // For objc async calls, push cleanup to be used on throw paths in the result + // planner. + for (unsigned i : indices(unmanagedCopies)) { + SILValue value = unmanagedCopies[i].getValue(); + Cleanups.pushCleanup(value); + unmanagedCopies[i] = ManagedValue(value, Cleanups.getTopCleanup()); + } + auto directResultsArray = makeArrayRef(directResults); - RValue result = - resultPlan->finish(*this, loc, substResultType, directResultsArray); + RValue result = resultPlan->finish(*this, loc, substResultType, + directResultsArray, bridgedForeignError); assert(directResultsArray.empty() && "didn't claim all direct results"); + // For objc async calls, generate cleanup on the resume path here and forward + // the previously pushed cleanups. + if (calleeTypeInfo.foreign.async) { + for (auto unmanagedCopy : unmanagedCopies) { + auto value = unmanagedCopy.forward(*this); + B.emitFixLifetime(loc, value); + B.emitDestroyOperation(loc, value); + } + + // hop back to the current executor + breadcrumb.emit(*this, loc); + } + return result; } @@ -4543,10 +4636,8 @@ RValue SILGenFunction::emitMonomorphicApply( SGFContext evalContext) { auto fnType = fn.getType().castTo(); assert(!fnType->isPolymorphic()); - CalleeTypeInfo::ForeignInfo foreign{ - foreignError, - {}, // TODO: take a foreign async convention? - {}, + ForeignInfo foreign{ + {}, foreignError, {}, // TODO: take a foreign async convention? }; CalleeTypeInfo calleeTypeInfo(fnType, AbstractionPattern(foreignResultType), nativeResultType, @@ -4647,10 +4738,8 @@ void SILGenFunction::emitYield(SILLocation loc, } ArgEmitter emitter(*this, fnType->getRepresentation(), /*yield*/ true, - /*isForCoroutine*/ false, - ClaimedParamsRef(substYieldTys), - yieldArgs, delayedArgs, - CalleeTypeInfo::ForeignInfo{}); + /*isForCoroutine*/ false, ClaimedParamsRef(substYieldTys), + yieldArgs, delayedArgs, ForeignInfo{}); for (auto i : indices(valueSources)) { emitter.emitSingleArg(std::move(valueSources[i]), origTypes[i]); @@ -4983,6 +5072,11 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, // Form the reference to the method. auto callRef = SILDeclRef(call, SILDeclRef::Kind::Func) .asForeign(requiresForeignEntryPoint(declRef.getDecl())); + + if (call->isDistributed()) { + callRef = callRef.asDistributed(true); + } + auto declRefConstant = getConstantInfo(getTypeExpansionContext(), callRef); auto subs = declRef.getSubstitutions(); bool throws = false; @@ -5518,11 +5612,9 @@ static void emitPseudoFunctionArguments(SILGenFunction &SGF, SmallVector delayedArgs; ArgEmitter emitter(SGF, SILFunctionTypeRepresentation::Thin, - /*yield*/ false, - /*isForCoroutine*/ false, - ClaimedParamsRef(substParamTys), - argValues, delayedArgs, - CalleeTypeInfo::ForeignInfo{}); + /*yield*/ false, + /*isForCoroutine*/ false, ClaimedParamsRef(substParamTys), + argValues, delayedArgs, ForeignInfo{}); emitter.emitPreparedArgs(std::move(args), origFnType); @@ -5763,7 +5855,10 @@ SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor, } ManagedValue SILGenFunction::emitAsyncLetStart( - SILLocation loc, Type functionType, ManagedValue taskFunction) { + SILLocation loc, + SILValue taskOptions, + Type functionType, ManagedValue taskFunction, + SILValue resultBuf) { ASTContext &ctx = getASTContext(); Type resultType = functionType->castTo()->getResult(); Type replacementTypes[] = {resultType}; @@ -5772,8 +5867,7 @@ ManagedValue SILGenFunction::emitAsyncLetStart( auto subs = SubstitutionMap::get(startBuiltin->getGenericSignature(), replacementTypes, ArrayRef{}); - - CanType origParamType = startBuiltin->getParameters()->get(1) + CanType origParamType = startBuiltin->getParameters()->get(2) ->getInterfaceType()->getCanonicalType(); CanType substParamType = origParamType.subst(subs)->getCanonicalType(); @@ -5786,9 +5880,9 @@ ManagedValue SILGenFunction::emitAsyncLetStart( auto apply = B.createBuiltin( loc, - ctx.getIdentifier(getBuiltinName(BuiltinValueKind::StartAsyncLet)), + ctx.getIdentifier(getBuiltinName(BuiltinValueKind::StartAsyncLetWithLocalBuffer)), getLoweredType(ctx.TheRawPointerType), subs, - { taskFunction.forward(*this) }); + {taskOptions, taskFunction.forward(*this), resultBuf}); return ManagedValue::forUnmanaged(apply); } @@ -5804,43 +5898,121 @@ ManagedValue SILGenFunction::emitCancelAsyncTask( return ManagedValue::forUnmanaged(apply); } -void SILGenFunction::completeAsyncLetChildTask( - PatternBindingDecl *patternBinding, unsigned index) { - SILValue asyncLet; - bool isThrowing; - std::tie(asyncLet, isThrowing)= AsyncLetChildTasks[{patternBinding, index}]; +ManagedValue SILGenFunction::emitReadAsyncLetBinding(SILLocation loc, + VarDecl *var) { + auto patternBinding = var->getParentPatternBinding(); + auto index = patternBinding->getPatternEntryIndexForVarDecl(var); + auto childTask = AsyncLetChildTasks[{patternBinding, index}]; - Type childResultType = patternBinding->getPattern(index)->getType(); + auto pattern = patternBinding->getPattern(index); + Type formalPatternType = pattern->getType(); + // async let context stores the maximally-abstracted representation. + SILType loweredOpaquePatternType = getLoweredType(AbstractionPattern::getOpaque(), + formalPatternType); - auto asyncLetGet = isThrowing + auto asyncLetGet = childTask.isThrowing ? SGM.getAsyncLetGetThrowing() : SGM.getAsyncLetGet(); - // Get the result from the async-let future. - Type replacementTypes[] = {childResultType}; - auto subs = SubstitutionMap::get(asyncLetGet->getGenericSignature(), - replacementTypes, - ArrayRef{}); - RValue childResult = emitApplyOfLibraryIntrinsic( - SILLocation(patternBinding), asyncLetGet, subs, - { ManagedValue::forTrivialObjectRValue(asyncLet) }, - SGFContext()); - - // Write the child result into the pattern variables. - emitAssignToPatternVars( - SILLocation(patternBinding), patternBinding->getPattern(index), - std::move(childResult)); + // The intrinsic returns a pointer to the address of the result value inside + // the async let task context. + emitApplyOfLibraryIntrinsic(loc, asyncLetGet, {}, + {ManagedValue::forTrivialObjectRValue(childTask.asyncLet), + ManagedValue::forTrivialObjectRValue(childTask.resultBuf)}, + SGFContext()); + + auto resultAddr = B.createPointerToAddress(loc, childTask.resultBuf, + loweredOpaquePatternType.getAddressType(), + /*strict*/ true, + /*invariant*/ true); + + // Project the address of the variable within the pattern binding result. + struct ProjectResultVisitor: public PatternVisitor + { + SILGenFunction &SGF; + SILLocation loc; + VarDecl *var; + SILValue resultAddr, varAddr; + SmallVector path; + + ProjectResultVisitor(SILGenFunction &SGF, + SILLocation loc, + VarDecl *var, + SILValue resultAddr) + : SGF(SGF), loc(loc), var(var), resultAddr(resultAddr), varAddr() {} + + // Walk through non-binding patterns. + void visitParenPattern(ParenPattern *P) { + return visit(P->getSubPattern()); + } + void visitTypedPattern(TypedPattern *P) { + return visit(P->getSubPattern()); + } + void visitBindingPattern(BindingPattern *P) { + return visit(P->getSubPattern()); + } + void visitTuplePattern(TuplePattern *P) { + path.push_back(0); + for (unsigned i : indices(P->getElements())) { + path.back() = i; + visit(P->getElement(i).getPattern()); + // If we found the variable of interest, we're done. + if (varAddr) + return; + } + path.pop_back(); + } + void visitAnyPattern(AnyPattern *P) {} + + // When we see the variable binding, project it out of the aggregate. + void visitNamedPattern(NamedPattern *P) { + if (P->getDecl() != var) + return; + + assert(!varAddr && "var appears in pattern more than once?"); + varAddr = resultAddr; + for (unsigned component : path) { + varAddr = SGF.B.createTupleElementAddr(loc, varAddr, component); + } + } + + #define INVALID_PATTERN(Id, Parent) \ + void visit##Id##Pattern(Id##Pattern *) { \ + llvm_unreachable("pattern not valid in var binding"); \ + } + #define PATTERN(Id, Parent) + #define REFUTABLE_PATTERN(Id, Parent) INVALID_PATTERN(Id, Parent) + #include "swift/AST/PatternNodes.def" + #undef INVALID_PATTERN + }; + + ProjectResultVisitor visitor(*this, loc, var, resultAddr); + visitor.visit(pattern); + assert(visitor.varAddr && "didn't find var in pattern?"); + + // Load and reabstract the value if needed. + auto genericSig = F.getLoweredFunctionType()->getInvocationGenericSignature(); + auto substVarTy = var->getType()->getCanonicalType(genericSig); + auto substAbstraction = AbstractionPattern(genericSig, substVarTy); + return emitLoad(loc, visitor.varAddr, substAbstraction, substVarTy, + getTypeLowering(substAbstraction, substVarTy), + SGFContext(), IsNotTake); } -ManagedValue SILGenFunction::emitEndAsyncLet( - SILLocation loc, SILValue asyncLet) { - ASTContext &ctx = getASTContext(); - auto apply = B.createBuiltin( - loc, - ctx.getIdentifier(getBuiltinName(BuiltinValueKind::EndAsyncLet)), - getLoweredType(ctx.TheEmptyTupleType), SubstitutionMap(), - { asyncLet }); - return ManagedValue::forUnmanaged(apply); +void SILGenFunction::emitFinishAsyncLet( + SILLocation loc, SILValue asyncLet, SILValue resultPtr) { + // This runtime function cancels the task, awaits its completion, and + // destroys the value in the result buffer if necessary. + emitApplyOfLibraryIntrinsic(loc, SGM.getFinishAsyncLet(), {}, + {ManagedValue::forTrivialObjectRValue(asyncLet), + ManagedValue::forTrivialObjectRValue(resultPtr)}, + SGFContext()); + // This builtin ends the lifetime of the allocation for the async let. + auto &ctx = getASTContext(); + B.createBuiltin(loc, + ctx.getIdentifier(getBuiltinName(BuiltinValueKind::EndAsyncLetLifetime)), + getLoweredType(ctx.TheEmptyTupleType), {}, + {asyncLet}); } // Create a partial application of a dynamic method, applying bridging thunks @@ -6113,9 +6285,8 @@ SmallVector SILGenFunction::emitKeyPathSubscriptOperands( ArgEmitter emitter(*this, fnType->getRepresentation(), /*yield*/ false, /*isForCoroutine*/ false, - ClaimedParamsRef(fnType->getParameters()), - argValues, delayedArgs, - CalleeTypeInfo::ForeignInfo{}); + ClaimedParamsRef(fnType->getParameters()), argValues, + delayedArgs, ForeignInfo{}); auto prepared = prepareSubscriptIndices(subscript, subs, diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 1655162c7d73c..a5cec1772cfb8 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -12,6 +12,7 @@ #include "ArgumentScope.h" #include "Callee.h" +#include "ExecutorBreadcrumb.h" #include "RValue.h" #include "ResultPlan.h" #include "SILGenFunction.h" @@ -22,6 +23,7 @@ #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ModuleLoader.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/SIL/SILArgument.h" @@ -606,7 +608,8 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, auto thunk = SGM.getOrCreateReabstractionThunk(invokeTy, loweredFuncUnsubstTy, loweredBlockTy, - /*dynamicSelfType=*/CanType()); + /*dynamicSelfType=*/CanType(), + /*global actor=*/CanType()); // Build it if necessary. if (thunk->empty()) { @@ -975,7 +978,8 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, auto thunk = SGM.getOrCreateReabstractionThunk(thunkTy, loweredBlockTy, loweredFuncUnsubstTy, - /*dynamicSelfType=*/CanType()); + /*dynamicSelfType=*/CanType(), + /*global actor=*/CanType()); // Build it if necessary. if (thunk->empty()) { @@ -1016,16 +1020,11 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, loc, thunkedFn, SILType::getPrimitiveObjectType(loweredFuncTy)); } -static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, - SILLocation loc, - ManagedValue v, - CanType bridgedType, - CanType nativeType, - SILType loweredNativeTy, - bool isCallResult, - SGFContext C) { +static ManagedValue emitCBridgedToNativeValue( + SILGenFunction &SGF, SILLocation loc, ManagedValue v, CanType bridgedType, + SILType loweredBridgedTy, CanType nativeType, SILType loweredNativeTy, + int bridgedOptionalsToUnwrap, bool isCallResult, SGFContext C) { assert(loweredNativeTy.isObject()); - SILType loweredBridgedTy = v.getType(); if (loweredNativeTy == loweredBridgedTy.getObjectType()) return v; @@ -1036,37 +1035,50 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, if (!bridgedObjectType) { auto helper = [&](SILGenFunction &SGF, SILLocation loc, SGFContext C) { auto loweredNativeObjectTy = loweredNativeTy.getOptionalObjectType(); - return emitCBridgedToNativeValue(SGF, loc, v, bridgedType, - nativeObjectType, - loweredNativeObjectTy, - isCallResult, C); + return emitCBridgedToNativeValue( + SGF, loc, v, bridgedType, loweredBridgedTy, nativeObjectType, + loweredNativeObjectTy, bridgedOptionalsToUnwrap, isCallResult, C); }; return SGF.emitOptionalSome(loc, loweredNativeTy, helper, C); } // Optional-to-optional. - auto helper = - [=](SILGenFunction &SGF, SILLocation loc, ManagedValue v, - SILType loweredNativeObjectTy, SGFContext C) { - return emitCBridgedToNativeValue(SGF, loc, v, bridgedObjectType, - nativeObjectType, loweredNativeObjectTy, - isCallResult, C); + auto helper = [=](SILGenFunction &SGF, SILLocation loc, ManagedValue v, + SILType loweredNativeObjectTy, SGFContext C) { + return emitCBridgedToNativeValue( + SGF, loc, v, bridgedObjectType, + loweredBridgedTy.getOptionalObjectType(), nativeObjectType, + loweredNativeObjectTy, bridgedOptionalsToUnwrap, isCallResult, C); }; return SGF.emitOptionalToOptional(loc, v, loweredNativeTy, helper, C); } + if (auto bridgedObjectType = bridgedType.getOptionalObjectType()) { + return emitCBridgedToNativeValue( + SGF, loc, v, bridgedObjectType, + loweredBridgedTy.getOptionalObjectType(), nativeType, loweredNativeTy, + bridgedOptionalsToUnwrap + 1, isCallResult, C); + } + + auto unwrapBridgedOptionals = [&](ManagedValue v) { + for (int i = 0; i < bridgedOptionalsToUnwrap; ++i) { + v = SGF.emitPreconditionOptionalHasValue(loc, v, + /*implicit*/ true); + }; + return v; + }; // Bridge ObjCBool, DarwinBoolean, WindowsBool to Bool when requested. if (nativeType == SGF.SGM.Types.getBoolType()) { if (bridgedType == SGF.SGM.Types.getObjCBoolType()) { - return emitBridgeForeignBoolToBool(SGF, loc, v, + return emitBridgeForeignBoolToBool(SGF, loc, unwrapBridgedOptionals(v), SGF.SGM.getObjCBoolToBoolFn()); } if (bridgedType == SGF.SGM.Types.getDarwinBooleanType()) { - return emitBridgeForeignBoolToBool(SGF, loc, v, + return emitBridgeForeignBoolToBool(SGF, loc, unwrapBridgedOptionals(v), SGF.SGM.getDarwinBooleanToBoolFn()); } if (bridgedType == SGF.SGM.Types.getWindowsBoolType()) { - return emitBridgeForeignBoolToBool(SGF, loc, v, + return emitBridgeForeignBoolToBool(SGF, loc, unwrapBridgedOptionals(v), SGF.SGM.getWindowsBoolToBoolFn()); } } @@ -1076,8 +1088,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, auto bridgedMetaTy = cast(bridgedType); if (bridgedMetaTy->hasRepresentation() && bridgedMetaTy->getRepresentation() == MetatypeRepresentation::ObjC) { - SILValue native = - SGF.B.emitObjCToThickMetatype(loc, v.getValue(), loweredNativeTy); + SILValue native = SGF.B.emitObjCToThickMetatype( + loc, unwrapBridgedOptionals(v).getValue(), loweredNativeTy); // *NOTE*: ObjCMetatypes are trivial types. They only gain ARC semantics // when they are converted to an object via objc_metatype_to_object. assert(!v.hasCleanup() && "Metatypes are trivial and should not have " @@ -1093,7 +1105,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, == AnyFunctionType::Representation::Block && nativeFTy->getRepresentation() != AnyFunctionType::Representation::Block) { - return SGF.emitBlockToFunc(loc, v, bridgedFTy, nativeFTy, + return SGF.emitBlockToFunc(loc, unwrapBridgedOptionals(v), bridgedFTy, + nativeFTy, loweredNativeTy.castTo()); } } @@ -1102,8 +1115,10 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, if (auto conformance = SGF.SGM.getConformanceToObjectiveCBridgeable(loc, nativeType)) { if (auto result = emitBridgeObjectiveCToNative(SGF, loc, v, bridgedType, - conformance)) - return *result; + conformance)) { + --bridgedOptionalsToUnwrap; + return unwrapBridgedOptionals(*result); + } assert(SGF.SGM.getASTContext().Diags.hadAnyError() && "Bridging code should have complained"); @@ -1114,7 +1129,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, if (nativeType->isAny()) { // If this is not a call result, use the normal erasure logic. if (!isCallResult) { - return SGF.emitTransformedValue(loc, v, bridgedType, nativeType, C); + return SGF.emitTransformedValue(loc, unwrapBridgedOptionals(v), + bridgedType, nativeType, C); } // Otherwise, we use more complicated logic that handles results that @@ -1126,7 +1142,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, CanType anyObjectTy = SGF.getASTContext().getAnyObjectType()->getCanonicalType(); if (bridgedType != anyObjectTy) { - v = SGF.emitTransformedValue(loc, v, bridgedType, anyObjectTy); + v = SGF.emitTransformedValue(loc, unwrapBridgedOptionals(v), bridgedType, + anyObjectTy); } // TODO: Ever need to handle +0 values here? @@ -1139,8 +1156,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, // Bitcast to Optional. This provides a barrier to the optimizer to prevent // it from attempting to eliminate null checks. auto optionalBridgedTy = SILType::getOptionalType(loweredBridgedTy); - auto optionalMV = - SGF.B.createUncheckedBitCast(loc, v, optionalBridgedTy); + auto optionalMV = SGF.B.createUncheckedBitCast( + loc, unwrapBridgedOptionals(v), optionalBridgedTy); return SGF.emitApplyOfLibraryIntrinsic(loc, SGF.getASTContext().getBridgeAnyObjectToAny(), SubstitutionMap(), optionalMV, C) @@ -1149,9 +1166,9 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, // Bridge NSError to Error. if (bridgedType == SGF.SGM.Types.getNSErrorType()) - return SGF.emitBridgedToNativeError(loc, v); + return SGF.emitBridgedToNativeError(loc, unwrapBridgedOptionals(v)); - return v; + return unwrapBridgedOptionals(v); } ManagedValue SILGenFunction::emitBridgedToNativeValue(SILLocation loc, @@ -1162,8 +1179,10 @@ ManagedValue SILGenFunction::emitBridgedToNativeValue(SILLocation loc, SGFContext C, bool isCallResult) { loweredNativeTy = loweredNativeTy.getObjectType(); - return emitCBridgedToNativeValue(*this, loc, v, bridgedType, nativeType, - loweredNativeTy, isCallResult, C); + SILType loweredBridgedTy = v.getType(); + return emitCBridgedToNativeValue( + *this, loc, v, bridgedType, loweredBridgedTy, nativeType, loweredNativeTy, + /*bridgedOptionalsToUnwrap=*/0, isCallResult, C); } /// Bridge a possibly-optional foreign error type to Error. @@ -1476,10 +1495,11 @@ SILFunction *SILGenFunction::emitNativeAsyncToForeignThunk(SILDeclRef thunk) { } // Create the closure implementation function. It has the same signature, - // but is just swiftcc and async. + // but is swiftcc and async. auto closureExtInfo = objcFnTy->getExtInfo().intoBuilder() .withRepresentation(SILFunctionTypeRepresentation::Thin) .withAsync() + .withConcurrent() .build(); auto closureTy = objcFnTy->getWithExtInfo(closureExtInfo); @@ -1553,6 +1573,24 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) { auto loc = thunk.getAsRegularLocation(); loc.markAutoGenerated(); Scope scope(Cleanups, CleanupLocation(loc)); + + // Hop to the actor for the method's actor constraint, if any. + // Note that, since an async native-to-foreign thunk only ever runs in a + // task purpose-built for running the Swift async code triggering the + // completion handler, there is no need for us to hop back to the existing + // executor, since the task will end after we invoke the completion handler. + if (F.isAsync()) { + Optional isolation; + if (thunk.hasDecl()) { + isolation = getActorIsolation(thunk.getDecl()); + } + + // A hop is only needed in the thunk if it is global-actor isolated. + // Native, instance-isolated async methods will hop in the prologue. + if (isolation && isolation->isGlobalActor()) { + emitHopToTargetActor(loc, *isolation, None); + } + } // If we are bridging a Swift method with an Any return value, create a // stack allocation to hold the result, since Any is address-only. @@ -2193,6 +2231,8 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { foreignError, foreignAsync, ImportAsMemberStatus()); + calleeTypeInfo.origFormalType = + foreignCI.FormalPattern.getFunctionResultType(); auto init = indirectResult ? useBufferAsTemporary(indirectResult, diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index b23fc06766d25..d2076edfa8aff 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -1401,8 +1401,8 @@ emitFunctionArgumentForAsyncTaskEntryPoint(SILGenFunction &SGF, return function.ensurePlusOne(SGF, loc); } -// Emit SIL for the named builtin: createAsyncTaskFuture. -static ManagedValue emitBuiltinCreateAsyncTaskFuture( +// Emit SIL for the named builtin: createAsyncTask. +static ManagedValue emitBuiltinCreateAsyncTask( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { ASTContext &ctx = SGF.getASTContext(); @@ -1449,15 +1449,14 @@ static ManagedValue emitBuiltinCreateAsyncTaskFuture( auto apply = SGF.B.createBuiltin( loc, - ctx.getIdentifier( - getBuiltinName(BuiltinValueKind::CreateAsyncTaskFuture)), + ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CreateAsyncTask)), SGF.getLoweredType(getAsyncTaskAndContextType(ctx)), subs, { flags, futureResultMetadata, function.forward(SGF) }); return SGF.emitManagedRValueWithCleanup(apply); } -// Emit SIL for the named builtin: createAsyncTaskGroupFuture. -static ManagedValue emitBuiltinCreateAsyncTaskGroupFuture( +// Emit SIL for the named builtin: createAsyncTaskInGroup. +static ManagedValue emitBuiltinCreateAsyncTaskInGroup( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { ASTContext &ctx = SGF.getASTContext(); @@ -1485,7 +1484,7 @@ static ManagedValue emitBuiltinCreateAsyncTaskGroupFuture( auto apply = SGF.B.createBuiltin( loc, ctx.getIdentifier( - getBuiltinName(BuiltinValueKind::CreateAsyncTaskGroupFuture)), + getBuiltinName(BuiltinValueKind::CreateAsyncTaskInGroup)), SGF.getLoweredType(getAsyncTaskAndContextType(ctx)), subs, { flags, group, futureResultMetadata, function.forward(SGF) }); return SGF.emitManagedRValueWithCleanup(apply); @@ -1570,6 +1569,14 @@ static ManagedValue emitBuiltinWithUnsafeThrowingContinuation( /*throws=*/true); } +static ManagedValue emitBuiltinHopToActor(SILGenFunction &SGF, SILLocation loc, + SubstitutionMap subs, + ArrayRef args, + SGFContext C) { + SGF.emitHopToActorValue(loc, args[0]); + return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); +} + static ManagedValue emitBuiltinAutoDiffCreateLinearMapContext( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 439ec8306026c..4857b60edd0d9 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -657,9 +657,8 @@ void SILGenFunction::emitClassConstructorAllocator(ConstructorDecl *ctor) { B.createReturn(ImplicitReturnLocation(Loc), initedSelfValue); } -static void emitDefaultActorInitialization(SILGenFunction &SGF, - SILLocation loc, - ManagedValue self) { +static void emitDefaultActorInitialization( + SILGenFunction &SGF, SILLocation loc, ManagedValue self) { auto &ctx = SGF.getASTContext(); auto builtinName = ctx.getIdentifier( getBuiltinName(BuiltinValueKind::InitializeDefaultActor)); @@ -678,7 +677,7 @@ void SILGenFunction::emitConstructorPrologActorHop( if (auto executor = emitExecutor(loc, *maybeIso, None)) { ExpectedExecutor = *executor; - B.createHopToExecutor(loc, *executor); + B.createHopToExecutor(loc.asAutoGenerated(), *executor, /*mandatory*/ false); } } @@ -780,6 +779,11 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { } } + // Distributed actor initializers implicitly initialize their transport and id + if (selfClassDecl->isDistributedActor() && !isDelegating) { + initializeDistributedActorImplicitStorageInit(ctor, selfArg); + } + // Prepare the end of initializer location. SILLocation endOfInitLoc = RegularLocation(ctor); endOfInitLoc.pointToEnd(); @@ -920,13 +924,19 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { // problem. ReturnDest = std::move(ReturnDest).translate(getTopCleanup()); } - + // Emit the epilog and post-matter. auto returnLoc = emitEpilog(ctor, /*UsesCustomEpilog*/true); // Unpop our selfArg cleanup, so we can forward. std::move(SelfCleanupSave).pop(); + // TODO(distributed): rdar://81783599 if this is a distributed actor, jump to the distributedReady block? + // // For distributed actors, emit "actor ready" since we successfully initialized + // if (selfClassDecl->isDistributedActor() && !isDelegating) { + // emitDistributedActorReady(ctor, selfArg); + // } + // Finish off the epilog by returning. If this is a failable ctor, then we // actually jump to the failure epilog to keep the invariant that there is // only one SIL return instruction per SIL function. @@ -1012,6 +1022,7 @@ emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, Pattern *pattern) { #include "swift/AST/PatternNodes.def" llvm_unreachable("Refutable pattern in stored property pattern binding"); } + llvm_unreachable("covered switch"); } static std::pair @@ -1117,10 +1128,22 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, // abstraction level. To undo this, we use a converting // initialization and rely on the peephole that optimizes // out the redundant conversion. - auto loweredResultTy = getLoweredType(origType, substType); - auto loweredSubstTy = getLoweredType(substType); + SILType loweredResultTy; + SILType loweredSubstTy; + + // A converting initialization isn't necessary if the member is + // a property wrapper. Though the initial value can have a + // reabstractable type, the result of the initialization is + // always the property wrapper type, which is never reabstractable. + bool needsConvertingInit = false; + auto *singleVar = varPattern->getSingleVar(); + if (!(singleVar && singleVar->getOriginalWrappedProperty())) { + loweredResultTy = getLoweredType(origType, substType); + loweredSubstTy = getLoweredType(substType); + needsConvertingInit = loweredResultTy != loweredSubstTy; + } - if (loweredResultTy != loweredSubstTy) { + if (needsConvertingInit) { Conversion conversion = Conversion::getSubstToOrig( origType, substType, loweredResultTy); @@ -1174,3 +1197,4 @@ void SILGenFunction::emitIVarInitializer(SILDeclRef ivarInitializer) { emitEpilog(loc); } + diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index c9173874c508e..774221b0f5fad 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -594,7 +594,7 @@ class ExistentialInitialization final : public SingleBufferInitialization { } bool isInPlaceInitializationOfGlobal() const override { - return existential && isa(existential); + return isa_and_nonnull(existential); } void finishInitialization(SILGenFunction &SGF) override { diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index e95e56b7c5267..c0be640f610e8 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -69,9 +69,16 @@ void TupleInitialization::copyOrInitValueInto(SILGenFunction &SGF, // In the address case, we forward the underlying value and store it // into memory and then create a +1 cleanup. since we assume here // that we have a +1 value since we are forwarding into memory. + // + // In order to ensure that we properly clean up along any failure paths, we + // need to mark value as being persistently active. We then unforward it once + // we are done. assert(value.isPlusOne(SGF) && "Can not store a +0 value into memory?!"); - value = ManagedValue::forUnmanaged(value.forward(SGF)); - return copyOrInitValueIntoHelper( + CleanupStateRestorationScope valueScope(SGF.Cleanups); + if (value.hasCleanup()) + valueScope.pushCleanupState(value.getCleanup(), + CleanupState::PersistentlyActive); + copyOrInitValueIntoHelper( SGF, loc, value, isInit, SubInitializations, [&](ManagedValue aggregate, unsigned i, SILType fieldType) -> ManagedValue { @@ -83,6 +90,8 @@ void TupleInitialization::copyOrInitValueInto(SILGenFunction &SGF, return SGF.emitManagedRValueWithCleanup(elt.getValue()); }); + std::move(valueScope).pop(); + value.forward(SGF); } void TupleInitialization::finishUninitialized(SILGenFunction &SGF) { @@ -290,13 +299,13 @@ class DestroyLocalVariable : public Cleanup { namespace { /// Cleanup to destroy an uninitialized local variable. class DeallocateUninitializedLocalVariable : public Cleanup { - VarDecl *Var; + SILValue Box; public: - DeallocateUninitializedLocalVariable(VarDecl *var) : Var(var) {} + DeallocateUninitializedLocalVariable(SILValue box) : Box(box) {} void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override { - SGF.deallocateUninitializedLocalVariable(l, Var); + SGF.B.createDeallocBox(l, Box); } void dump(SILGenFunction &) const override { @@ -315,7 +324,12 @@ namespace { class LocalVariableInitialization : public SingleBufferInitialization { /// The local variable decl being initialized. VarDecl *decl; - SILGenFunction &SGF; + + /// The alloc_box instruction. + SILValue Box; + + /// The projected address. + SILValue Addr; /// The cleanup we pushed to deallocate the local variable before it /// gets initialized. @@ -332,7 +346,7 @@ class LocalVariableInitialization : public SingleBufferInitialization { LocalVariableInitialization(VarDecl *decl, Optional kind, uint16_t ArgNo, SILGenFunction &SGF) - : decl(decl), SGF(SGF) { + : decl(decl) { assert(decl->getDeclContext()->isLocalContext() && "can't emit a local var for a non-local var decl"); assert(decl->hasStorage() && "can't emit storage for a computed variable"); @@ -348,17 +362,13 @@ class LocalVariableInitialization : public SingleBufferInitialization { // The variable may have its lifetime extended by a closure, heap-allocate // it using a box. SILDebugVariable DbgVar(decl->isLet(), ArgNo); - SILValue allocBox = SGF.B.createAllocBox(decl, boxType, DbgVar); + Box = SGF.B.createAllocBox(decl, boxType, DbgVar); // Mark the memory as uninitialized, so DI will track it for us. if (kind) - allocBox = SGF.B.createMarkUninitialized(decl, allocBox, kind.getValue()); - - SILValue addr = SGF.B.createProjectBox(decl, allocBox, 0); + Box = SGF.B.createMarkUninitialized(decl, Box, kind.getValue()); - /// Remember that this is the memory location that we're emitting the - /// decl to. - SGF.VarLocs[decl] = SILGenFunction::VarLoc::get(addr, allocBox); + Addr = SGF.B.createProjectBox(decl, Box, 0); // Push a cleanup to destroy the local variable. This has to be // inactive until the variable is initialized. @@ -366,8 +376,10 @@ class LocalVariableInitialization : public SingleBufferInitialization { decl); ReleaseCleanup = SGF.Cleanups.getTopCleanup(); - // Push a cleanup to deallocate the local variable. - SGF.Cleanups.pushCleanup(decl); + // Push a cleanup to deallocate the local variable. This references the + // box directly since it might be activated before we update + // SGF.VarLocs. + SGF.Cleanups.pushCleanup(Box); DeallocCleanup = SGF.Cleanups.getTopCleanup(); } @@ -376,8 +388,7 @@ class LocalVariableInitialization : public SingleBufferInitialization { } SILValue getAddress() const { - assert(SGF.VarLocs.count(decl) && "did not emit var?!"); - return SGF.VarLocs[decl].value; + return Addr; } SILValue getAddressForInPlaceInitialization(SILGenFunction &SGF, @@ -394,6 +405,11 @@ class LocalVariableInitialization : public SingleBufferInitialization { } void finishInitialization(SILGenFunction &SGF) override { + /// Remember that this is the memory location that we've emitted the + /// decl to. + assert(SGF.VarLocs.count(decl) == 0 && "Already emitted the local?"); + SGF.VarLocs[decl] = SILGenFunction::VarLoc::get(Addr, Box); + SingleBufferInitialization::finishInitialization(SGF); assert(!DidFinish && "called LocalVariableInitialization::finishInitialization twice!"); @@ -440,7 +456,7 @@ class LetValueInitialization : public Initialization { // buffer. DI will make sure it is only assigned to once. needsTemporaryBuffer = true; isUninitialized = true; - } else if (vd->isSpawnLet()) { + } else if (vd->isAsyncLet()) { // If this is an async let, treat it like a let-value without an // initializer. The initializer runs concurrently in a child task, // and value will be initialized at the point the variable in the @@ -1137,14 +1153,11 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool forceImmutable) { void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, unsigned idx) { - - - auto initialization = emitPatternBindingInitialization(PBD->getPattern(idx), - JumpDest::invalid()); + auto &C = PBD->getASTContext(); // If this is an async let, create a child task to compute the initializer // value. - if (PBD->isSpawnLet()) { + if (PBD->isAsyncLet()) { // Look through the implicit await (if present), try (if present), and // call to reach the autoclosure that computes the value. auto *init = PBD->getExecutableInit(idx); @@ -1157,28 +1170,48 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, "Could not find async let autoclosure"); bool isThrowing = init->getType()->castTo()->isThrowing(); + // Allocate space to receive the child task's result. + auto initLoweredTy = getLoweredType(AbstractionPattern::getOpaque(), + PBD->getPattern(idx)->getType()); + SILLocation loc(PBD); + SILValue resultBuf = emitTemporaryAllocation(loc, initLoweredTy); + SILValue resultBufPtr = B.createAddressToPointer(loc, resultBuf, + SILType::getPrimitiveObjectType(C.TheRawPointerType)); + // Emit the closure for the child task. // Prepare the opaque `AsyncLet` representation. SILValue alet; { - SILLocation loc(PBD); + + // Currently we don't pass any task options here, so just grab a 'nil'. + + // If we can statically detect some option needs to be passed, e.g. + // an executor preference, we'd construct the appropriate option here and + // pass it to the async let start. + auto options = B.createManagedOptionalNone( + loc, SILType::getOptionalType(SILType::getRawPointerType(C))); + alet = emitAsyncLetStart( loc, + options.forward(*this), // options is B.createManagedOptionalNone init->getType(), - emitRValue(init).getScalarValue() + emitRValue(init).getScalarValue(), + resultBufPtr ).forward(*this); } - + // Push a cleanup to destroy the AsyncLet along with the task and child record. - enterAsyncLetCleanup(alet); + enterAsyncLetCleanup(alet, resultBufPtr); // Save the child task so we can await it as needed. - AsyncLetChildTasks[{PBD, idx}] = { alet, isThrowing }; + AsyncLetChildTasks[{PBD, idx}] = {alet, resultBufPtr, isThrowing}; + return; + } - // Mark as uninitialized; actual initialization will occur when the - // variables are referenced. - initialization->finishUninitialized(*this); - } else if (auto *Init = PBD->getExecutableInit(idx)) { + auto initialization = emitPatternBindingInitialization(PBD->getPattern(idx), + JumpDest::invalid()); + + if (auto *Init = PBD->getExecutableInit(idx)) { // If an initial value expression was specified by the decl, emit it into // the initialization. FullExpr Scope(Cleanups, CleanupLocation(Init)); @@ -1329,7 +1362,8 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest, case StmtConditionElement::CK_Availability: // Check the running OS version to determine whether it is in the range // specified by elt. - VersionRange OSVersion = elt.getAvailability()->getAvailableRange(); + PoundAvailableInfo *availability = elt.getAvailability(); + VersionRange OSVersion = availability->getAvailableRange(); // The OS version might be left empty if availability checking was // disabled. Treat it as always-true in that case. @@ -1338,11 +1372,21 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest, if (OSVersion.isEmpty() || OSVersion.isAll()) { // If there's no check for the current platform, this condition is - // trivially true. + // trivially true (or false, for unavailability). SILType i1 = SILType::getBuiltinIntegerType(1, getASTContext()); - booleanTestValue = B.createIntegerLiteral(loc, i1, true); + bool value = !availability->isUnavailability(); + booleanTestValue = B.createIntegerLiteral(loc, i1, value); } else { booleanTestValue = emitOSVersionRangeCheck(loc, OSVersion); + if (availability->isUnavailability()) { + // If this is an unavailability check, invert the result + // by emitting a call to Builtin.xor_Int1(lhs, -1). + SILType i1 = SILType::getBuiltinIntegerType(1, getASTContext()); + SILValue minusOne = B.createIntegerLiteral(loc, i1, -1); + booleanTestValue = + B.createBuiltinBinaryFunction(loc, "xor", i1, i1, + {booleanTestValue, minusOne}); + } } break; } @@ -1503,26 +1547,30 @@ namespace { /// A cleanup that destroys the AsyncLet along with the child task and record. class AsyncLetCleanup: public Cleanup { SILValue alet; + SILValue resultBuf; public: - AsyncLetCleanup(SILValue alet) : alet(alet) { } + AsyncLetCleanup(SILValue alet, SILValue resultBuf) + : alet(alet), resultBuf(resultBuf) { } void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override { - SGF.emitEndAsyncLet(l, alet); + SGF.emitFinishAsyncLet(l, alet, resultBuf); } void dump(SILGenFunction &) const override { #ifndef NDEBUG llvm::errs() << "AsyncLetCleanup\n" - << "AsyncLet:" << alet << "\n"; + << "AsyncLet:" << alet << "\n" + << "result buffer:" << resultBuf << "\n"; #endif } }; } // end anonymous namespace -CleanupHandle SILGenFunction::enterAsyncLetCleanup(SILValue alet) { +CleanupHandle SILGenFunction::enterAsyncLetCleanup(SILValue alet, + SILValue resultBuf) { Cleanups.pushCleanupInState( - CleanupState::Active, alet); + CleanupState::Active, alet, resultBuf); return Cleanups.getTopCleanup(); } @@ -1680,20 +1728,3 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) { else B.createDestroyAddr(silLoc, Val); } - -void SILGenFunction::deallocateUninitializedLocalVariable(SILLocation silLoc, - VarDecl *vd) { - assert(vd->getDeclContext()->isLocalContext() && - "can't emit a local var for a non-local var decl"); - assert(vd->hasStorage() && "can't emit storage for a computed variable"); - - assert(VarLocs.count(vd) && "var decl wasn't emitted?!"); - - auto loc = VarLocs[vd]; - - // Ignore let values captured without a memory location. - if (!loc.value->getType().isAddress()) return; - - assert(loc.box && "captured var should have been given a box"); - B.createDeallocBox(silLoc, loc.box); -} diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 42c2f259b6f87..589b3e883dac6 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "SILGenFunction.h" +#include "SILGenFunctionBuilder.h" #include "RValue.h" #include "ArgumentScope.h" #include "swift/AST/GenericSignature.h" @@ -75,6 +76,15 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) { resultSelfValue = selfValue; } + /// A distributed actor resigns its identity as it is deallocated. + /// This way the transport knows it must not deliver any more messages to it, + /// and can remove it from its (weak) lookup tables. + if (cd->isDistributedActor()) { + SILBasicBlock *continueBB = createBasicBlock(); + emitDistributedActor_resignAddress(dd, selfValue, continueBB); + B.emitBlock(continueBB); + } + ArgumentScope S(*this, Loc); ManagedValue borrowedValue = ManagedValue::forUnmanaged(resultSelfValue).borrow(*this, cleanupLoc); @@ -187,35 +197,77 @@ void SILGenFunction::emitIVarDestroyer(SILDeclRef ivarDestroyer) { emitEpilog(loc); } +void SILGenFunction::destroyClassMember(SILLocation cleanupLoc, + ManagedValue selfValue, VarDecl *D) { + const TypeLowering &ti = getTypeLowering(D->getType()); + if (!ti.isTrivial()) { + SILValue addr = + B.createRefElementAddr(cleanupLoc, selfValue.getValue(), D, + ti.getLoweredType().getAddressType()); + addr = B.createBeginAccess( + cleanupLoc, addr, SILAccessKind::Deinit, SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); + B.createDestroyAddr(cleanupLoc, addr); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); + } +} + void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, ClassDecl *cd, CleanupLocation cleanupLoc) { assert(selfValue.getOwnershipKind() == OwnershipKind::Guaranteed); - for (VarDecl *vd : cd->getStoredProperties()) { - const TypeLowering &ti = getTypeLowering(vd->getType()); - if (!ti.isTrivial()) { - SILValue addr = - B.createRefElementAddr(cleanupLoc, selfValue.getValue(), vd, - ti.getLoweredType().getAddressType()); - addr = B.createBeginAccess( - cleanupLoc, addr, SILAccessKind::Deinit, SILAccessEnforcement::Static, - false /*noNestedConflict*/, false /*fromBuiltin*/); - B.createDestroyAddr(cleanupLoc, addr); - B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); - } + + /// If this ClassDecl is a distributed actor, we must synthesise another code + /// path for deallocating a 'remote' actor. In that case, these basic blocks + /// are used to return to the "normal" (i.e. 'local' instance) destruction. + /// + /// For other cases, the basic blocks are not necessary and the destructor + /// can just emit all the normal destruction code right into the current block. + // If set, used as the basic block for the destroying of all members. + SILBasicBlock* normalMemberDestroyBB = nullptr; + // If set, used as the basic block after members have been destroyed, + // and we're ready to perform final cleanups before returning. + SILBasicBlock* finishBB = nullptr; + + /// A distributed actor may be 'remote' in which case there is no need to + /// destroy "all" members, because they never had storage to begin with. + if (cd->isDistributedActor()) { + finishBB = createBasicBlock(); + normalMemberDestroyBB = createBasicBlock(); + + emitDistributedActorClassMemberDestruction(cleanupLoc, selfValue, cd, + normalMemberDestroyBB, + finishBB); + } + + /// Destroy all members. + { + if (normalMemberDestroyBB) + B.emitBlock(normalMemberDestroyBB); + + for (VarDecl *vd : cd->getStoredProperties()) + destroyClassMember(cleanupLoc, selfValue, vd); + + if (finishBB) + B.createBranch(cleanupLoc, finishBB); } - if (cd->isRootDefaultActor()) { - auto builtinName = getASTContext().getIdentifier( - getBuiltinName(BuiltinValueKind::DestroyDefaultActor)); - auto resultTy = SGM.Types.getEmptyTupleType(); + { + if (finishBB) + B.emitBlock(finishBB); + + if (cd->isRootDefaultActor()) { + // TODO(distributed): we may need to call the distributed destroy here instead? + auto builtinName = getASTContext().getIdentifier( + getBuiltinName(BuiltinValueKind::DestroyDefaultActor)); + auto resultTy = SGM.Types.getEmptyTupleType(); - B.createBuiltin(cleanupLoc, builtinName, resultTy, /*subs*/{}, - { selfValue.getValue() }); + B.createBuiltin(cleanupLoc, builtinName, resultTy, /*subs*/{}, + { selfValue.getValue() }); + } } } - void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) { auto dd = cast(dtor.getDecl()); auto cd = cast(dd->getDeclContext()); diff --git a/lib/SILGen/SILGenDistributed.cpp b/lib/SILGen/SILGenDistributed.cpp new file mode 100644 index 0000000000000..2da2a4c340d91 --- /dev/null +++ b/lib/SILGen/SILGenDistributed.cpp @@ -0,0 +1,1019 @@ +//===--- SILGenDistributed.cpp - SILGen for distributed -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "ArgumentSource.h" +#include "Conversion.h" +#include "ExecutorBreadcrumb.h" +#include "Initialization.h" +#include "LValue.h" +#include "RValue.h" +#include "SILGenFunction.h" +#include "SILGenFunctionBuilder.h" +#include "Scope.h" +#include "swift/AST/ASTMangler.h" +#include "swift/AST/ForeignErrorConvention.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/PropertyWrappers.h" +#include "swift/Basic/Defer.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILUndef.h" +#include "swift/SIL/TypeLowering.h" + +using namespace swift; +using namespace Lowering; + +/******************************************************************************/ +/******************* COMMON (DISTRIBUTED) SIL PATTERNS ************************/ +/******************************************************************************/ + +/// Emit the following branch SIL instruction: +/// \verbatim +/// if __isRemoteActor(self) { +/// +/// } else { +/// +/// } +/// \endverbatim +static void emitDistributedIfRemoteBranch(SILGenFunction &SGF, + SILLocation Loc, + ManagedValue selfValue, Type selfTy, + SILBasicBlock *isRemoteBB, + SILBasicBlock *isLocalBB) { + ASTContext &ctx = SGF.getASTContext(); + auto &B = SGF.B; + + FuncDecl *isRemoteFn = ctx.getIsRemoteDistributedActor(); + assert(isRemoteFn && "Could not find 'is remote' function, is the " + "'_Distributed' module available?"); + + ManagedValue selfAnyObject = + B.createInitExistentialRef(Loc, SGF.getLoweredType(ctx.getAnyObjectType()), + CanType(selfTy), selfValue, {}); + auto result = SGF.emitApplyOfLibraryIntrinsic( + Loc, isRemoteFn, SubstitutionMap(), {selfAnyObject}, SGFContext()); + + SILValue isRemoteResult = + std::move(result).forwardAsSingleValue(SGF, Loc); + SILValue isRemoteResultUnwrapped = + SGF.emitUnwrapIntegerResult(Loc, isRemoteResult); + + B.createCondBranch(Loc, isRemoteResultUnwrapped, isRemoteBB, isLocalBB); +} + +static VarDecl *lookupActorTransportProperty(ASTContext &C, ClassDecl *cd, + SILValue selfValue) { + auto transportVarDeclRefs = cd->lookupDirect(C.Id_actorTransport); + assert(transportVarDeclRefs.size() == 1); + return dyn_cast(transportVarDeclRefs.front()); +} + +/******************************************************************************/ +/****************** DISTRIBUTED ACTOR STORAGE INITIALIZATION ******************/ +/******************************************************************************/ + +/// Get the `ActorTransport` parameter of the constructor. +/// Sema should have guaranteed that there is exactly one of them for any +/// designated initializer of a distributed actor. +static SILArgument* +getActorTransportArgument(ASTContext& C, SILFunction& F, ConstructorDecl *ctor) { + auto *DC = cast(ctor); + auto module = DC->getParentModule(); + + auto *transportProto = + C.getProtocol(KnownProtocolKind::ActorTransport); + Type transportTy = transportProto->getDeclaredInterfaceType(); + + for (auto arg : F.getArguments()) { + // TODO(distributed): also be able to locate a generic transport + Type argTy = arg->getType().getASTType(); + auto argDecl = arg->getDecl(); + + auto conformsToTransport = module->lookupConformance( + argDecl->getInterfaceType(), transportProto); + + // Is it a protocol that conforms to ActorTransport? + if (argTy->isEqual(transportTy) || conformsToTransport) { + return arg; + } + + // Is it some specific ActorTransport? + auto result = module->lookupConformance(argTy, transportProto); + if (!result.isInvalid()) { + return arg; + } + } + + // did not find argument of ActorTransport type! + llvm_unreachable("Missing required ActorTransport argument!"); +} + +/// Synthesize the actorTransport initialization: +/// +/// \verbatim +/// self.actorTransport = <> +/// \endverbatim +static void emitDistributedActorStore_transport( + ASTContext& C, SILGenFunction &SGF, + SILValue actorSelf, AbstractFunctionDecl *func, + SILArgument *transportArg) { + auto &B = SGF.B; + + auto &SGM = SGF.SGM; + SILGenFunctionBuilder builder(SGM); + + auto *dc = func->getDeclContext(); + auto classDecl = dc->getSelfClassDecl(); + + auto loc = SILLocation(func); + loc.markAutoGenerated(); + + // ==== Prepare the property reference: self.id + auto vars = classDecl->lookupDirect(C.Id_actorTransport); + assert(vars.size() == 1); + auto *var = dyn_cast(vars.front()); + + // ---- + + auto fieldAddr = B.createRefElementAddr( + loc, actorSelf, var, + SGF.getLoweredType(var->getInterfaceType())); + + // ==== Store the transport + B.createCopyAddr(loc, + /*src*/transportArg, + /*dest*/fieldAddr, + IsNotTake, IsInitialization); +} + +// TODO(distributed): remove this store impl and reuse Store_transport +static void +emitDistributedActor_init_transportStore( + SILGenFunction &SGF, + ManagedValue borrowedSelfArg, VarDecl *selfDecl, + ConstructorDecl *ctor, + Pattern *pattern, VarDecl *var) { + auto &C = selfDecl->getASTContext(); + auto &B = SGF.B; + auto &F = SGF.F; + auto &SGM = SGF.SGM; + SILGenFunctionBuilder builder(SGM); + + auto loc = SILLocation(ctor); + loc.markAutoGenerated(); + + // ==== Prepare assignment: get the self.transport address + SILValue transportArgValue = getActorTransportArgument(C, F, ctor); + + // ---- + + auto transportFieldAddr = B.createRefElementAddr( + loc, borrowedSelfArg.getValue(), var, + SGF.getLoweredType(var->getInterfaceType())); + + // ==== Store the transport + B.createCopyAddr(loc, + /*src*/transportArgValue, + /*dest*/transportFieldAddr, + IsNotTake, IsInitialization); +} + +/// Synthesize storing the passed in managed identity to the `id` property: +/// +/// \verbatim +/// self.id = <> +/// \endverbatim +static void emitDistributedActorStore_id( + ASTContext& C, SILGenFunction &SGF, + SILValue actorSelf, AbstractFunctionDecl *func, + SILArgument *identityArg) { + auto &B = SGF.B; + + auto &SGM = SGF.SGM; + SILGenFunctionBuilder builder(SGM); + + auto *dc = func->getDeclContext(); + auto classDecl = dc->getSelfClassDecl(); + + auto loc = SILLocation(func); + loc.markAutoGenerated(); + + // ==== Prepare the property reference: self.id + auto vars = classDecl->lookupDirect(C.Id_id); + assert(vars.size() == 1); + auto *var = dyn_cast(vars.front()); + + // ==== Prepare assignment + auto fieldAddr = B.createRefElementAddr( + loc, actorSelf, var, + SGF.getLoweredType(var->getInterfaceType())); + + // ==== Store the transport + B.createCopyAddr(loc, + /*src*/identityArg, + /*dest*/fieldAddr, + IsNotTake, IsInitialization); +} + +/// Synthesize the distributed actor's identity (`id`) initialization: +/// +/// \verbatim +/// self.id = transport.assignIdentity(Self.self) +/// \endverbatim +static void emitDistributedActorStore_init_assignIdentity( + SILGenFunction &SGF, + ManagedValue borrowedSelfArg, VarDecl *selfVarDecl, + ConstructorDecl *ctor, + Pattern *pattern, VarDecl *var) { + auto &C = selfVarDecl->getASTContext(); + auto &B = SGF.B; + auto &F = SGF.F; + auto &SGM = SGF.SGM; + SILGenFunctionBuilder builder(SGM); + + auto loc = SILLocation(ctor); + loc.markAutoGenerated(); + + // ==== prepare the transport.assignIdentity(_:) function + ProtocolDecl *transportProto = C.getProtocol(KnownProtocolKind::ActorTransport); + + // --- Prepare the arguments + SILValue transportArgValue = getActorTransportArgument(C, F, ctor); + + ProtocolDecl *distributedActorProto = C.getProtocol(KnownProtocolKind::DistributedActor); + + assert(distributedActorProto); + assert(transportProto); + + // --- Open the transport existential + OpenedArchetypeType *Opened; + auto transportASTType = transportArgValue->getType().getASTType(); + auto openedTransportType = + transportASTType->openAnyExistentialType(Opened)->getCanonicalType(); + auto openedTransportSILType = F.getLoweredType(openedTransportType); + auto transportArchetypeValue = B.createOpenExistentialAddr( + loc, transportArgValue, openedTransportSILType, OpenedExistentialAccess::Immutable); + + // --- prepare `Self.self` metatype + auto *selfTyDecl = ctor->getParent()->getSelfNominalTypeDecl(); + // This would be bad: since not ok for generic + // auto selfMetatype = SGF.getLoweredType(selfTyDecl->getInterfaceType()); + auto selfMetatype = + SGF.getLoweredType(F.mapTypeIntoContext(selfTyDecl->getInterfaceType())); + // selfVarDecl.getType() // TODO: do this; then dont need the self type decl + SILValue selfMetatypeValue = B.createMetatype(loc, selfMetatype); + + // === Make the transport.assignIdentity call + // --- prepare the witness_method + // Note: it does not matter on what module we perform the lookup, + // it is currently ignored. So the Stdlib module is good enough. + auto *module = SGF.getModule().getSwiftModule(); + + // the conformance here is just an abstract thing so we can simplify + auto transportConfRef = ProtocolConformanceRef(transportProto); + assert(!transportConfRef.isInvalid() && "Missing conformance to `ActorTransport`"); + + auto selfTy = F.mapTypeIntoContext(selfTyDecl->getDeclaredInterfaceType()); // TODO: thats just self var devl getType + + auto distributedActorConfRef = module->lookupConformance( + selfTy, + distributedActorProto); + assert(!distributedActorConfRef.isInvalid() && "Missing conformance to `DistributedActor`"); + + auto assignIdentityMethod = + cast(transportProto->getSingleRequirement(C.Id_assignIdentity)); + auto assignIdentityRef = SILDeclRef(assignIdentityMethod, SILDeclRef::Kind::Func); + auto assignIdentitySILTy = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), assignIdentityRef) + .getSILType(); + + auto assignWitnessMethod = B.createWitnessMethod( + loc, + /*lookupTy*/openedTransportType, + /*Conformance*/transportConfRef, + /*member*/assignIdentityRef, + /*methodTy*/assignIdentitySILTy); + + // --- prepare conformance subs + auto genericSig = assignIdentityMethod->getGenericSignature(); + + SubstitutionMap subs = + SubstitutionMap::get(genericSig, + {openedTransportType, selfTy}, + {transportConfRef, distributedActorConfRef}); + + // --- create a temporary storage for the result of the call + // it will be deallocated automatically as we exit this scope + auto resultTy = SGF.getLoweredType(var->getInterfaceType()); + auto temp = SGF.emitTemporaryAllocation(loc, resultTy); + + // ---- actually call transport.assignIdentity(Self.self) + B.createApply( + loc, assignWitnessMethod, subs, + { temp, selfMetatypeValue, transportArchetypeValue}); + + // ==== Assign the identity to stored property + // TODO(distributed): reuse emitDistributedActorStore_id here, pass the SILValue + // --- Prepare address of self.id + auto idFieldAddr = B.createRefElementAddr( + loc, borrowedSelfArg.getValue(), var, + SGF.getLoweredType(var->getInterfaceType())); + + // --- assign to the property + B.createCopyAddr(loc, /*src*/temp, /*dest*/idFieldAddr, + IsTake, IsInitialization); +} + +/******************************************************************************/ +/******************* DISTRIBUTED ACTOR LOCAL INIT *****************************/ +/******************************************************************************/ + +void SILGenFunction::initializeDistributedActorImplicitStorageInit( + ConstructorDecl *ctor, ManagedValue selfArg) { + VarDecl *selfVarDecl = ctor->getImplicitSelfDecl(); + auto *dc = ctor->getDeclContext(); + auto classDecl = dc->getSelfClassDecl(); + auto &C = classDecl->getASTContext(); + + // Only designated initializers get the lifecycle handling injected + if (!ctor->isDesignatedInit()) + return; + + SILLocation prologueLoc = RegularLocation(ctor); + prologueLoc.markAsPrologue(); // TODO: no idea if this is necessary or makes sense + + auto transportTy = C.getActorTransportType(); + auto identityProtoTy = C.getActorIdentityType(); + auto anyIdentityTy = C.getAnyActorIdentityType(); + + // ==== Find the stored properties we will initialize + VarDecl *transportMember = nullptr; + VarDecl *idMember = nullptr; + + auto borrowedSelfArg = selfArg.borrow(*this, prologueLoc); + + // TODO(distributed): getStoredProperties might be better here, avoid the `break;` + for (auto member : classDecl->getMembers()) { + PatternBindingDecl *pbd = dyn_cast(member); + if (!pbd) continue; + if (pbd->isStatic()) continue; + + Pattern *pattern = pbd->getPattern(0); + VarDecl *var = pbd->getSingleVar(); + if (!var) continue; + + if (var->getName() == C.Id_actorTransport && + var->getInterfaceType()->isEqual(transportTy)) { + transportMember = var; + // TODO(distributed): reuse emitDistributedActorStore_transport + emitDistributedActor_init_transportStore( + *this, borrowedSelfArg, selfVarDecl, ctor, pattern, var); + } else if (var->getName() == C.Id_id && + (var->getInterfaceType()->isEqual(identityProtoTy) || + var->getInterfaceType()->isEqual(anyIdentityTy))) { // TODO(distributed): stick one way to store, but today we can't yet store the existential + idMember = var; + emitDistributedActorStore_init_assignIdentity( + *this, borrowedSelfArg, selfVarDecl, ctor, pattern, var); + } + if (transportMember && idMember) { + break; // we found all properties we care about, break out of the loop early + } + } + + assert(transportMember && "Missing DistributedActor.actorTransport member"); + assert(idMember && "Missing DistributedActor.id member"); +} + +void SILGenFunction::emitDistributedActorReady( + ConstructorDecl *ctor, ManagedValue selfArg) { + + auto *dc = ctor->getDeclContext(); + auto classDecl = dc->getSelfClassDecl(); + auto &C = classDecl->getASTContext(); + + // Only designated initializers get the lifecycle handling injected + if (!ctor->isDesignatedInit()) + return; + + SILLocation loc = RegularLocation(ctor); + loc.markAutoGenerated(); + + // === Prepare the arguments + SILValue transportArgValue = getActorTransportArgument(C, F, ctor); + SILValue selfArgValue = F.getSelfArgument(); + + ProtocolDecl *distributedActorProto = C.getProtocol(KnownProtocolKind::DistributedActor); + ProtocolDecl *transportProto = C.getProtocol(KnownProtocolKind::ActorTransport); + assert(distributedActorProto); + assert(transportProto); + + // --- Open the transport existential + OpenedArchetypeType *Opened; + auto transportASTType = transportArgValue->getType().getASTType(); + auto openedTransportType = + transportASTType->openAnyExistentialType(Opened)->getCanonicalType(); + auto openedTransportSILType = F.getLoweredType(openedTransportType); + auto transportArchetypeValue = B.createOpenExistentialAddr( + loc, transportArgValue, openedTransportSILType, OpenedExistentialAccess::Immutable); + + // === Make the transport.actorReady call + // --- prepare the witness_method + // Note: it does not matter on what module we perform the lookup, + // it is currently ignored. So the Stdlib module is good enough. + auto *module = getModule().getSwiftModule(); + + // the conformance here is just an abstract thing so we can simplify + auto transportConfRef = ProtocolConformanceRef(transportProto); + assert(!transportConfRef.isInvalid() && "Missing conformance to `ActorTransport`"); + + auto *selfTyDecl = ctor->getParent()->getSelfNominalTypeDecl(); + auto selfTy = F.mapTypeIntoContext(selfTyDecl->getDeclaredInterfaceType()); // TODO: thats just self var devl getType + + auto distributedActorConfRef = module->lookupConformance( + selfTy, + distributedActorProto); + assert(!distributedActorConfRef.isInvalid() && "Missing conformance to `DistributedActor`"); + + // === Prepare the actorReady function + auto actorReadyMethod = + cast(transportProto->getSingleRequirement(C.Id_actorReady)); + auto actorReadyRef = SILDeclRef(actorReadyMethod, SILDeclRef::Kind::Func); + auto actorReadySILTy = + getConstantInfo(getTypeExpansionContext(), actorReadyRef) + .getSILType(); + + auto readyWitnessMethod = B.createWitnessMethod( + loc, + /*lookupTy*/openedTransportType, + /*Conformance*/transportConfRef, + /*member*/actorReadyRef, + /*methodTy*/actorReadySILTy); + + // --- prepare conformance subs + auto genericSig = actorReadyMethod->getGenericSignature(); + + SubstitutionMap subs = + SubstitutionMap::get(genericSig, + {openedTransportType, selfTy}, + {transportConfRef, distributedActorConfRef}); + + // ---- actually call transport.actorReady(self) + B.createApply( + loc, readyWitnessMethod, subs, + { selfArgValue, transportArchetypeValue}); +} + +/******************************************************************************/ +/******************* DISTRIBUTED ACTOR RESOLVE FUNCTION ***********************/ +/******************************************************************************/ + +/// Synthesize the distributed actor's identity (`id`) initialization: +/// +/// \verbatim +/// transport.resolve(_, as:) +/// \endverbatim +static void createDistributedActorFactory_resolve( + SILGenFunction &SGF, ASTContext &C, FuncDecl *fd, SILValue identityValue, + SILValue transportValue, Type selfTy, SILValue selfMetatypeValue, + SILType resultTy, SILBasicBlock *normalBB, SILBasicBlock *errorBB) { + auto &B = SGF.B; + auto &SGM = SGF.SGM; + auto &F = SGF.F; + SILGenFunctionBuilder builder(SGM); + + auto loc = SILLocation(fd); + loc.markAutoGenerated(); + + ProtocolDecl *distributedActorProto = + C.getProtocol(KnownProtocolKind::DistributedActor); + ProtocolDecl *transportProto = + C.getProtocol(KnownProtocolKind::ActorTransport); + assert(distributedActorProto); + assert(transportProto); + + // // --- Open the transport existential + OpenedArchetypeType *Opened; + auto transportASTType = transportValue->getType().getASTType(); + auto openedTransportType = + transportASTType->openAnyExistentialType(Opened)->getCanonicalType(); + auto openedTransportSILType = F.getLoweredType(openedTransportType); + auto transportArchetypeValue = + B.createOpenExistentialAddr(loc, transportValue, openedTransportSILType, + OpenedExistentialAccess::Immutable); + + // --- prepare the witness_method + // Note: it does not matter on what module we perform the lookup, + // it is currently ignored. So the Stdlib module is good enough. + auto *module = SGF.getModule().getSwiftModule(); + + // the conformance here is just an abstract thing so we can simplify + auto transportConfRef = ProtocolConformanceRef(transportProto); + assert(!transportConfRef.isInvalid() && + "Missing conformance to `ActorTransport`"); + + auto distributedActorConfRef = + module->lookupConformance(selfTy, distributedActorProto); + assert(!distributedActorConfRef.isInvalid() && + "Missing conformance to `DistributedActor`"); + + auto resolveMethod = + cast(transportProto->getSingleRequirement(C.Id_resolve)); + auto resolveRef = SILDeclRef(resolveMethod, SILDeclRef::Kind::Func); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), resolveRef); + auto resolveSILTy = constantInfo.getSILType(); + + auto resolveWitnessMethod = + B.createWitnessMethod(loc, + /*lookupTy*/ openedTransportType, + /*Conformance*/ transportConfRef, + /*member*/ resolveRef, + /*methodTy*/ resolveSILTy); + + // // --- prepare conformance subs + auto genericSig = resolveMethod->getGenericSignature(); + + SubstitutionMap subs = + SubstitutionMap::get(genericSig, {openedTransportType, selfTy}, + {transportConfRef, distributedActorConfRef}); + + // // ---- actually call transport.resolve(id, as: Self.self) + + SmallVector params; + params.push_back(identityValue); + params.push_back(selfMetatypeValue); + params.push_back(transportArchetypeValue); // self for the call, as last param + + B.createTryApply(loc, resolveWitnessMethod, subs, params, normalBB, errorBB); +} + +/// Function body of: +/// \verbatim +/// DistributedActor.resolve( +/// _ identity: Identity, +/// using transport: ActorTransport +/// ) throws -> Self where Identity: ActorIdentity +/// \endverbatim +void SILGenFunction::emitDistributedActorFactory(FuncDecl *fd) { + /// NOTE: this will only be reached if the resolve function is actually + /// demanded. For example, by declaring the actor as `public` or + /// having at least one call to the resolve function. + + auto &C = getASTContext(); + SILLocation loc = fd; + + // ==== Prepare argument references + // --- Parameter: identity + SILArgument *identityArg = F.getArgument(0); + assert(identityArg->getType().getASTType()->isEqual(C.getAnyActorIdentityType())); + + // --- Parameter: transport + SILArgument *transportArg = F.getArgument(1); + assert( + transportArg->getType().getASTType()->isEqual(C.getActorTransportType())); + + SILValue selfArgValue = F.getSelfArgument(); + ManagedValue selfArg = ManagedValue::forUnmanaged(selfArgValue); + + // type: SpecificDistributedActor.Type + auto selfArgType = F.mapTypeIntoContext(selfArg.getType().getASTType()); + auto selfMetatype = getLoweredType(selfArgType); + SILValue selfMetatypeValue = B.createMetatype(loc, selfMetatype); + + // type: SpecificDistributedActor + auto *selfTyDecl = fd->getParent()->getSelfNominalTypeDecl(); + assert(selfTyDecl->isDistributedActor()); + auto selfTy = F.mapTypeIntoContext(selfTyDecl->getDeclaredInterfaceType()); + auto returnTy = getLoweredType(selfTy); + + // ==== Prepare all the basic blocks + auto returnBB = createBasicBlock(); + auto resolvedBB = createBasicBlock(); + auto makeProxyBB = createBasicBlock(); + auto switchBB = createBasicBlock(); + auto errorBB = createBasicBlock(); + + SILFunctionConventions fnConv = F.getConventions(); // TODO: no idea? + + // --- get the uninitialized allocation from the runtime system. + FullExpr scope(Cleanups, CleanupLocation(fd)); + + auto optionalReturnTy = SILType::getOptionalType(returnTy); + + // ==== Call `try transport.resolve(id, as: Self.self)` + { + createDistributedActorFactory_resolve( + *this, C, fd, identityArg, transportArg, selfTy, selfMetatypeValue, + optionalReturnTy, switchBB, errorBB); + } + + // ==== switch resolved { ... } + { + B.emitBlock(switchBB); + auto resolve = + switchBB->createPhiArgument(optionalReturnTy, OwnershipKind::Owned); + + B.createSwitchEnum( + loc, resolve, nullptr, + {{C.getOptionalSomeDecl(), resolvedBB}, + {std::make_pair(C.getOptionalNoneDecl(), makeProxyBB)}}); + } + + // ==== Case 'some') return the resolved instance + { + B.emitBlock(resolvedBB); + + auto local = resolvedBB->createPhiArgument(returnTy, OwnershipKind::Owned); + + B.createBranch(loc, returnBB, {local}); + } + + // ==== Case 'none') Create the remote instance + { + B.emitBlock(makeProxyBB); + // ==== Create 'remote' distributed actor instance + + // --- Call: _distributedActorRemoteInitialize(Self.self) + auto builtinName = C.getIdentifier( + getBuiltinName(BuiltinValueKind::InitializeDistributedRemoteActor)); + auto *remote = B.createBuiltin( + loc, builtinName, + /*returnTy*/returnTy, + /*subs*/ {}, + {selfMetatypeValue}); + + // ==== Initialize distributed actor properties + // --- Store the identity: self.id = identity + emitDistributedActorStore_id( + C, *this, /*actorSelf*/remote, fd, identityArg); + + // --- Store the transport: self.actorTransport = transport + emitDistributedActorStore_transport( + C, *this, /*actorSelf*/remote, fd, transportArg); + + // ==== Branch to return the fully initialized remote instance + B.createBranch(loc, returnBB, {remote}); + } + + // --- Emit return logic + // return + { + B.emitBlock(returnBB); + + auto local = returnBB->createPhiArgument(returnTy, OwnershipKind::Owned); + + Cleanups.emitCleanupsForReturn(CleanupLocation(loc), NotForUnwind); + B.createReturn(loc, local); + } + + // --- Emit rethrow logic + // throw error + { + B.emitBlock(errorBB); + + auto error = errorBB->createPhiArgument( + fnConv.getSILErrorType(F.getTypeExpansionContext()), + OwnershipKind::Owned); + + Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind); + B.createThrow(loc, error); + } +} + +/******************************************************************************/ +/******************* DISTRIBUTED DEINIT: resignAddress ************************/ +/******************************************************************************/ + +void SILGenFunction::emitDistributedActor_resignAddress( + DestructorDecl *dd, SILValue selfValue, SILBasicBlock *continueBB) { + ASTContext &ctx = getASTContext(); + + auto cd = cast(dd->getDeclContext()); + assert(cd->isDistributedActor() && + "only distributed actors have transport lifecycle hooks in deinit"); + + RegularLocation Loc(dd); + if (dd->isImplicit()) + Loc.markAutoGenerated(); + + auto selfDecl = dd->getImplicitSelfDecl(); + auto selfManagedValue = ManagedValue::forUnmanaged(selfValue); + auto selfTy = selfDecl->getType(); + + // ==== locate: self.id + auto idVarDeclRefs = cd->lookupDirect(ctx.Id_id); + assert(idVarDeclRefs.size() == 1); + auto *idVarDeclRef = dyn_cast(idVarDeclRefs.front()); + assert(idVarDeclRef); + auto idRef = + B.createRefElementAddr(Loc, selfValue, idVarDeclRef, + getLoweredType(idVarDeclRef->getType())); + + // ==== locate: self.actorTransport + auto transportVarDeclRef = lookupActorTransportProperty(ctx, cd, selfValue); + + auto transportRef = + B.createRefElementAddr(Loc, selfValue, transportVarDeclRef, + getLoweredType(transportVarDeclRef->getType())); + + // locate: self.transport.resignIdentity(...) + auto *transportDecl = ctx.getActorTransportDecl(); + auto resignFnDecls = transportDecl->lookupDirect(ctx.Id_resignIdentity); + assert(resignFnDecls.size() == 1); + auto *resignFnDecl = resignFnDecls.front(); + auto resignFnRef = SILDeclRef(resignFnDecl); + + // we only transport.resignIdentity if we are a local actor, + // and thus the address was created by transport.assignIdentity. + auto isRemoteBB = createBasicBlock(); + auto isLocalBB = createBasicBlock(); + + // if __isRemoteActor(self) { + // ... + // } else { + // ... + // } + emitDistributedIfRemoteBranch(*this, Loc, + selfManagedValue, selfTy, + /*if remote*/isRemoteBB, + /*if local*/isLocalBB); + + // if remote + // === + { + B.emitBlock(isRemoteBB); + // noop, remote actors don't do anything in their deinit + B.createBranch(Loc, continueBB); + } + + // if local + // === self.transport.resignIdentity(self.address) + { + B.emitBlock(isLocalBB); + + auto openedTransport = + OpenedArchetypeType::get(transportVarDeclRef->getType()); + auto transportAddr = B.createOpenExistentialAddr( + Loc, /*operand=*/transportRef, getLoweredType(openedTransport), + OpenedExistentialAccess::Immutable); + + auto resignFnType = SGM.M.Types.getConstantFunctionType( + TypeExpansionContext::minimal(), resignFnRef); + + auto witness = B.createWitnessMethod( + Loc, openedTransport, ProtocolConformanceRef(transportDecl), + resignFnRef, SILType::getPrimitiveObjectType(resignFnType)); + + auto subs = SubstitutionMap::getProtocolSubstitutions( + transportDecl, openedTransport, ProtocolConformanceRef(transportDecl)); + + SmallVector params; + params.push_back(idRef); + params.push_back(transportAddr); // self for the call, as last param + + B.createApply(Loc, witness, subs, params); + B.createBranch(Loc, continueBB); + } +} + + +/******************************************************************************/ +/******************* DISTRIBUTED DEINIT: class memberwise destruction *********/ +/******************************************************************************/ + +void SILGenFunction::emitDistributedActorClassMemberDestruction( + SILLocation cleanupLoc, ManagedValue selfValue, ClassDecl *cd, + SILBasicBlock *normalMemberDestroyBB, SILBasicBlock *finishBB) { + auto selfTy = cd->getDeclaredInterfaceType(); + + Scope scope(Cleanups, CleanupLocation(cleanupLoc)); + + auto isLocalBB = createBasicBlock(); + auto remoteMemberDestroyBB = createBasicBlock(); + + // if __isRemoteActor(self) { + // ... + // } else { + // ... + // } + emitDistributedIfRemoteBranch(*this, cleanupLoc, + selfValue, selfTy, + /*if remote*/remoteMemberDestroyBB, + /*if local*/isLocalBB); + + // // if __isRemoteActor(self) + // { + // // destroy only self.id and self.actorTransport + // } + { + B.emitBlock(remoteMemberDestroyBB); + + for (VarDecl *vd : cd->getStoredProperties()) { + if (!vd->getAttrs().hasAttribute()) + continue; + + destroyClassMember(cleanupLoc, selfValue, vd); + } + + B.createBranch(cleanupLoc, finishBB); + } + + // // else (local distributed actor) + // { + // + // } + { + B.emitBlock(isLocalBB); + + B.createBranch(cleanupLoc, normalMemberDestroyBB); + } +} + +/******************************************************************************/ +/***************************** DISTRIBUTED THUNKS *****************************/ +/******************************************************************************/ + +void SILGenFunction::emitDistributedThunk(SILDeclRef thunk) { + // Check if actor is local or remote and call respective function + // + // func X_distributedThunk(...) async throws -> T { + // if __isRemoteActor(self) { + // return try await self._remote_X(...) + // } else { + // return try await self.X(...) + // } + // } + // + + assert(thunk.isDistributed); + SILDeclRef native = thunk.asDistributed(false); + auto fd = cast(thunk.getDecl()); + + ASTContext &ctx = getASTContext(); + + // Use the same generic environment as the native entry point. + F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(native)); + + auto loc = thunk.getAsRegularLocation(); + loc.markAutoGenerated(); + Scope scope(Cleanups, CleanupLocation(loc)); + + auto isRemoteBB = createBasicBlock(); + auto isLocalBB = createBasicBlock(); + auto localErrorBB = createBasicBlock(); + auto remoteErrorBB = createBasicBlock(); + auto localReturnBB = createBasicBlock(); + auto remoteReturnBB = createBasicBlock(); + auto errorBB = createBasicBlock(); + auto returnBB = createBasicBlock(); + + auto methodTy = SGM.Types.getConstantOverrideType(getTypeExpansionContext(), + thunk); + auto derivativeFnSILTy = SILType::getPrimitiveObjectType(methodTy); + auto silFnType = derivativeFnSILTy.castTo(); + SILFunctionConventions fnConv(silFnType, SGM.M); + auto resultType = fnConv.getSILResultType(getTypeExpansionContext()); + + auto *selfVarDecl = fd->getImplicitSelfDecl(); + + SmallVector params; + + bindParametersForForwarding(fd->getParameters(), params); + bindParameterForForwarding(selfVarDecl, params); + auto selfValue = ManagedValue::forUnmanaged(params[params.size() - 1]); // TODO(distributed): getSelfArgument instead + auto selfTy = selfVarDecl->getType(); + + // if __isRemoteActor(self) { + // ... + // } else { + // ... + // } + { + FuncDecl* isRemoteFn = ctx.getIsRemoteDistributedActor(); + assert(isRemoteFn && + "Could not find 'is remote' function, is the '_Distributed' module available?"); + + ManagedValue selfAnyObject = B.createInitExistentialRef( + loc, getLoweredType(ctx.getAnyObjectType()), + CanType(selfTy), selfValue, {}); + auto result = emitApplyOfLibraryIntrinsic( + loc, isRemoteFn, SubstitutionMap(), + {selfAnyObject}, SGFContext()); + + SILValue isRemoteResult = std::move(result).forwardAsSingleValue(*this, loc); + SILValue isRemoteResultUnwrapped = emitUnwrapIntegerResult(loc, isRemoteResult); + + B.createCondBranch(loc, isRemoteResultUnwrapped, isRemoteBB, isLocalBB); + } + + // // if __isRemoteActor(self) + // { + // return try await self._remote_X(...) + // } + { + B.emitBlock(isRemoteBB); + + auto *selfTyDecl = FunctionDC->getParent()->getSelfNominalTypeDecl(); + assert(selfTyDecl && "distributed function declared outside of actor"); + + auto remoteFnDecl = selfTyDecl->lookupDirectRemoteFunc(fd); + assert(remoteFnDecl && "Could not find _remote_ function"); + auto remoteFnRef = SILDeclRef(remoteFnDecl); + + SILGenFunctionBuilder builder(SGM); + auto remoteFnSIL = builder.getOrCreateFunction(loc, remoteFnRef, ForDefinition); + SILValue remoteFn = B.createFunctionRefFor(loc, remoteFnSIL); + + auto subs = F.getForwardingSubstitutionMap(); + + SmallVector remoteParams(params); + + B.createTryApply(loc, remoteFn, subs, remoteParams, remoteReturnBB, remoteErrorBB); + } + + // // else + // { + // return (try)? (await)? self.X(...) + // } + { + B.emitBlock(isLocalBB); + + auto nativeMethodTy = SGM.Types.getConstantOverrideType(getTypeExpansionContext(), + native); + auto nativeFnSILTy = SILType::getPrimitiveObjectType(nativeMethodTy); + auto nativeSilFnType = nativeFnSILTy.castTo(); + + SILValue nativeFn = emitClassMethodRef( + loc, params[params.size() - 1], native, nativeMethodTy); + auto subs = F.getForwardingSubstitutionMap(); + + if (nativeSilFnType->hasErrorResult()) { + B.createTryApply(loc, nativeFn, subs, params, localReturnBB, localErrorBB); + } else { + auto result = B.createApply(loc, nativeFn, subs, params); + B.createBranch(loc, returnBB, {result}); + } + } + + // TODO(distributed): to use a emitAndBranch local function, since these four blocks are so similar + + { + B.emitBlock(remoteErrorBB); + SILValue error = remoteErrorBB->createPhiArgument( + fnConv.getSILErrorType(getTypeExpansionContext()), + OwnershipKind::Owned); + + B.createBranch(loc, errorBB, {error}); + } + + { + B.emitBlock(localErrorBB); + SILValue error = localErrorBB->createPhiArgument( + fnConv.getSILErrorType(getTypeExpansionContext()), + OwnershipKind::Owned); + + B.createBranch(loc, errorBB, {error}); + } + + { + B.emitBlock(remoteReturnBB); + SILValue result = remoteReturnBB->createPhiArgument( + resultType, OwnershipKind::Owned); + B.createBranch(loc, returnBB, {result}); + } + + { + B.emitBlock(localReturnBB); + SILValue result = localReturnBB->createPhiArgument( + resultType, OwnershipKind::Owned); + B.createBranch(loc, returnBB, {result}); + } + + // Emit return logic + { + B.emitBlock(returnBB); + SILValue resArg = returnBB->createPhiArgument( + resultType, OwnershipKind::Owned); + B.createReturn(loc, resArg); + } + + // Emit the rethrow logic. + { + B.emitBlock(errorBB); + SILValue error = errorBB->createPhiArgument( + fnConv.getSILErrorType(getTypeExpansionContext()), + OwnershipKind::Owned); + + Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind); + B.createThrow(loc, error); + } +} \ No newline at end of file diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 4ca3f4117490b..8d55775669f55 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -1363,7 +1363,7 @@ RValue SILGenFunction::emitCollectionConversion(SILLocation loc, // Form type parameter substitutions. auto genericSig = fn->getGenericSignature(); unsigned fromParamCount = fromDecl->getGenericSignature() - ->getGenericParams().size(); + .getGenericParams().size(); auto subMap = SubstitutionMap::combineSubstitutionMaps(fromSubMap, @@ -1568,10 +1568,8 @@ ManagedValue emitCFunctionPointer(SILGenFunction &SGF, // Ensure that weak captures are in a separate scope. DebugScope scope(SGF, CleanupLocation(captureList)); // CaptureListExprs evaluate their bound variables. - for (auto capture : captureList->getCaptureList()) { - SGF.visit(capture.Var); - SGF.visit(capture.Init); - } + for (auto capture : captureList->getCaptureList()) + SGF.visit(capture.PBD); // Emit the closure body. auto *closure = captureList->getClosureBody(); @@ -2205,6 +2203,7 @@ RValue RValueEmitter::visitMemberRefExpr(MemberRefExpr *e, RValue RValueEmitter::visitDynamicMemberRefExpr(DynamicMemberRefExpr *E, SGFContext C) { assert(!E->isImplicitlyAsync() && "an actor-isolated @objc member?"); + assert(!E->isImplicitlyThrows() && "an distributed-actor-isolated @objc member?"); return SGF.emitDynamicMemberRefExpr(E, C); } @@ -2227,6 +2226,7 @@ RValue RValueEmitter::visitSubscriptExpr(SubscriptExpr *E, SGFContext C) { RValue RValueEmitter::visitDynamicSubscriptExpr( DynamicSubscriptExpr *E, SGFContext C) { assert(!E->isImplicitlyAsync() && "an actor-isolated @objc member?"); + assert(!E->isImplicitlyThrows() && "an distributed-actor-isolated @objc member?"); return SGF.emitDynamicSubscriptExpr(E, C); } @@ -2416,10 +2416,8 @@ RValue RValueEmitter::visitCaptureListExpr(CaptureListExpr *E, SGFContext C) { // Ensure that weak captures are in a separate scope. DebugScope scope(SGF, CleanupLocation(E)); // CaptureListExprs evaluate their bound variables. - for (auto capture : E->getCaptureList()) { - SGF.visit(capture.Var); - SGF.visit(capture.Init); - } + for (auto capture : E->getCaptureList()) + SGF.visit(capture.PBD); // Then they evaluate to their body. return visit(E->getClosureBody(), C); @@ -2660,7 +2658,7 @@ loadIndexValuesForKeyPathComponent(SILGenFunction &SGF, SILLocation loc, auto indexLoweredTy = SGF.getLoweredType( - AnyFunctionType::composeInput(SGF.getASTContext(), indexParams, + AnyFunctionType::composeTuple(SGF.getASTContext(), indexParams, /*canonicalVararg=*/false)); auto addr = SGF.B.createPointerToAddress(loc, pointer, @@ -2694,7 +2692,6 @@ getRepresentativeAccessorForKeyPath(AbstractStorageDecl *storage) { } static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, - SILLocation loc, AbstractStorageDecl *property, SubstitutionMap subs, GenericEnvironment *genericEnv, @@ -2761,6 +2758,8 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, auto name = Mangle::ASTMangler() .mangleKeyPathGetterThunkHelper(property, genericSig, baseType, subs, expansion); + auto loc = RegularLocation::getAutoGeneratedLocation(); + SILGenFunctionBuilder builder(SGM); auto thunk = builder.getOrCreateSharedFunction( loc, name, signature, IsBare, IsNotTransparent, @@ -2808,7 +2807,6 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, auto baseSubstValue = emitKeyPathRValueBase(subSGF, property, loc, baseArg, baseType, subs); - auto subscriptIndices = loadIndexValuesForKeyPathComponent(subSGF, loc, property, indexes, indexPtrArg); @@ -2834,7 +2832,6 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, } static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, - SILLocation loc, AbstractStorageDecl *property, SubstitutionMap subs, GenericEnvironment *genericEnv, @@ -2909,6 +2906,7 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, auto name = Mangle::ASTMangler() .mangleKeyPathSetterThunkHelper(property, genericSig, baseType, subs, expansion); + auto loc = RegularLocation::getAutoGeneratedLocation(); SILGenFunctionBuilder builder(SGM); auto thunk = builder.getOrCreateSharedFunction( @@ -3516,14 +3514,6 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, return storage->isSettable(storage->getDeclContext()); }; - // We cannot use the same opened archetype in the getter and setter. Therefore - // we create a new one for both the getter and the setter. - auto renewOpenedArchetypes = [](SubstitutableType *type) -> Type { - if (auto *openedTy = dyn_cast(type)) - return OpenedArchetypeType::get(openedTy->getOpenedExistentialType()); - return type; - }; - if (auto var = dyn_cast(storage)) { CanType componentTy; if (!var->getDeclContext()->isTypeContext()) { @@ -3546,16 +3536,14 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, // expected by the key path runtime. auto id = getIdForKeyPathComponentComputedProperty(*this, var, strategy); - auto getter = getOrCreateKeyPathGetter(*this, loc, - var, subs.subst(renewOpenedArchetypes, - MakeAbstractConformanceForGenericType()), + auto getter = getOrCreateKeyPathGetter(*this, + var, subs, needsGenericContext ? genericEnv : nullptr, expansion, {}, baseTy, componentTy); if (isSettableInComponent()) { - auto setter = getOrCreateKeyPathSetter(*this, loc, - var, subs.subst(renewOpenedArchetypes, - MakeAbstractConformanceForGenericType()), + auto setter = getOrCreateKeyPathSetter(*this, + var, subs, needsGenericContext ? genericEnv : nullptr, expansion, {}, baseTy, componentTy); return KeyPathPatternComponent::forComputedSettableProperty(id, @@ -3599,9 +3587,8 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, } auto id = getIdForKeyPathComponentComputedProperty(*this, decl, strategy); - auto getter = getOrCreateKeyPathGetter(*this, loc, - decl, subs.subst(renewOpenedArchetypes, - MakeAbstractConformanceForGenericType()), + auto getter = getOrCreateKeyPathGetter(*this, + decl, subs, needsGenericContext ? genericEnv : nullptr, expansion, indexTypes, @@ -3609,9 +3596,8 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, auto indexPatternsCopy = getASTContext().AllocateCopy(indexPatterns); if (isSettableInComponent()) { - auto setter = getOrCreateKeyPathSetter(*this, loc, - decl, subs.subst(renewOpenedArchetypes, - MakeAbstractConformanceForGenericType()), + auto setter = getOrCreateKeyPathSetter(*this, + decl, subs, needsGenericContext ? genericEnv : nullptr, expansion, indexTypes, @@ -3743,6 +3729,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { case KeyPathExpr::Component::Kind::Invalid: case KeyPathExpr::Component::Kind::UnresolvedProperty: case KeyPathExpr::Component::Kind::UnresolvedSubscript: + case KeyPathExpr::Component::Kind::CodeCompletion: llvm_unreachable("not resolved"); break; @@ -5253,7 +5240,7 @@ RValue RValueEmitter::visitPropertyWrapperValuePlaceholderExpr( RValue RValueEmitter::visitAppliedPropertyWrapperExpr( AppliedPropertyWrapperExpr *E, SGFContext C) { auto *param = const_cast(E->getParamDecl()); - auto argument = visit(E->getValue(), C); + auto argument = visit(E->getValue()); SILDeclRef::Kind initKind; switch (E->getValueKind()) { case swift::AppliedPropertyWrapperExpr::ValueKind::WrappedValue: @@ -5264,9 +5251,15 @@ RValue RValueEmitter::visitAppliedPropertyWrapperExpr( break; } + // The property wrapper generator function needs the same substitutions as the + // enclosing function or closure. If the parameter is declared in a function, take + // the substitutions from the concrete callee. Otherwise, forward the archetypes + // from the closure. SubstitutionMap subs; if (param->getDeclContext()->getAsDecl()) { subs = E->getCallee().getSubstitutions(); + } else { + subs = SGF.getForwardingSubstitutionMap(); } return SGF.emitApplyOfPropertyWrapperBackingInitializer( diff --git a/lib/SILGen/SILGenForeignError.cpp b/lib/SILGen/SILGenForeignError.cpp index 59e966bcc2112..938a7be8054b6 100644 --- a/lib/SILGen/SILGenForeignError.cpp +++ b/lib/SILGen/SILGenForeignError.cpp @@ -10,16 +10,17 @@ // //===----------------------------------------------------------------------===// -#include "SILGen.h" -#include "SILGenFunction.h" #include "ASTVisitor.h" #include "LValue.h" #include "RValue.h" +#include "SILGen.h" +#include "SILGenFunction.h" #include "Scope.h" +#include "swift/AST/DiagnosticsSIL.h" +#include "swift/AST/ForeignAsyncConvention.h" +#include "swift/AST/ForeignErrorConvention.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILUndef.h" -#include "swift/AST/ForeignErrorConvention.h" -#include "swift/AST/DiagnosticsSIL.h" using namespace swift; using namespace Lowering; @@ -247,13 +248,20 @@ emitBridgeReturnValueForForeignError(SILLocation loc, llvm_unreachable("bad foreign error convention kind"); } +static FunctionSection +functionSectionForConvention(Optional foreignAsync) { + // If there is a foreign async convention too, the error block branches to + // the block awaiting the conteinuation. + return foreignAsync ? FunctionSection::Ordinary : FunctionSection::Postmatter; +} + /// Step out of the current control flow to emit a foreign error block, /// which loads from the error slot and jumps to the error slot. -void SILGenFunction::emitForeignErrorBlock(SILLocation loc, - SILBasicBlock *errorBB, - Optional errorSlot) { +SILValue SILGenFunction::emitForeignErrorBlock( + SILLocation loc, SILBasicBlock *errorBB, Optional errorSlot, + Optional foreignAsync) { SILGenSavedInsertionPoint savedIP(*this, errorBB, - FunctionSection::Postmatter); + functionSectionForConvention(foreignAsync)); Scope scope(Cleanups, CleanupLocation(loc)); // Load the error (taking responsibility for it). In theory, this @@ -275,22 +283,35 @@ void SILGenFunction::emitForeignErrorBlock(SILLocation loc, ManagedValue error = emitManagedRValueWithCleanup(errorV); // Turn the error into an Error value. - error = scope.popPreservingValue(emitBridgedToNativeError(loc, error)); - - // Propagate. - FullExpr throwScope(Cleanups, CleanupLocation(loc)); - emitThrow(loc, error, true); + error = emitBridgedToNativeError(loc, error); + + // If there's no foreign async convention, then the error needs to be thrown. + // + // If there _is_ a foreign async convention, the error needs to be used to + // fulfill the unsafe continuation, and then destroyed. Afterwards, a branch + // to the block containing the await_async_continuation must be emitted. That + // work is done in ForeignAsyncInitializationPlan::finish using the error + // that is returned from here. + // [foreign_error_block_with_foreign_async_convention] + if (!foreignAsync) { + error = scope.popPreservingValue(error); + // Propagate. + FullExpr throwScope(Cleanups, CleanupLocation(loc)); + emitThrow(loc, error, true); + } + return error.getValue(); } /// Perform a foreign error check by testing whether the call result is zero. /// The call result is otherwise ignored. -static void +static SILValue emitResultIsZeroErrorCheck(SILGenFunction &SGF, SILLocation loc, ManagedValue result, ManagedValue errorSlot, - bool suppressErrorCheck, bool zeroIsError) { + bool suppressErrorCheck, bool zeroIsError, + Optional foreignAsync) { // Just ignore the call result if we're suppressing the error check. if (suppressErrorCheck) { - return; + return SILValue(); } SILValue resultValue = @@ -310,7 +331,8 @@ emitResultIsZeroErrorCheck(SILGenFunction &SGF, SILLocation loc, {resultValue, zero}); } - SILBasicBlock *errorBB = SGF.createBasicBlock(FunctionSection::Postmatter); + SILBasicBlock *errorBB = + SGF.createBasicBlock(functionSectionForConvention(foreignAsync)); SILBasicBlock *contBB = SGF.createBasicBlock(); if (zeroIsError) @@ -318,16 +340,21 @@ emitResultIsZeroErrorCheck(SILGenFunction &SGF, SILLocation loc, else SGF.B.createCondBranch(loc, resultValue, errorBB, contBB); - SGF.emitForeignErrorBlock(loc, errorBB, errorSlot); + SILValue error = + SGF.emitForeignErrorBlock(loc, errorBB, errorSlot, foreignAsync); SGF.B.emitBlock(contBB); + + return error; } /// Perform a foreign error check by testing whether the call result is nil. -static ManagedValue +static std::pair emitResultIsNilErrorCheck(SILGenFunction &SGF, SILLocation loc, ManagedValue origResult, ManagedValue errorSlot, - bool suppressErrorCheck) { + bool suppressErrorCheck, + Optional foreignAsync) { + assert(!foreignAsync); // Take local ownership of the optional result value. SILValue optionalResult = origResult.forward(SGF); @@ -340,33 +367,37 @@ emitResultIsNilErrorCheck(SILGenFunction &SGF, SILLocation loc, SILValue objectResult = SGF.B.createUncheckedEnumData(loc, optionalResult, ctx.getOptionalSomeDecl()); - return SGF.emitManagedRValueWithCleanup(objectResult); + return {SGF.emitManagedRValueWithCleanup(objectResult), SILValue()}; } // Switch on the optional result. - SILBasicBlock *errorBB = SGF.createBasicBlock(FunctionSection::Postmatter); + SILBasicBlock *errorBB = + SGF.createBasicBlock(functionSectionForConvention(foreignAsync)); SILBasicBlock *contBB = SGF.createBasicBlock(); SGF.B.createSwitchEnum(loc, optionalResult, /*default*/ nullptr, { { ctx.getOptionalSomeDecl(), contBB }, { ctx.getOptionalNoneDecl(), errorBB } }); // Emit the error block. - SGF.emitForeignErrorBlock(loc, errorBB, errorSlot); + SILValue error = + SGF.emitForeignErrorBlock(loc, errorBB, errorSlot, foreignAsync); // In the continuation block, take ownership of the now non-optional // result value. SGF.B.emitBlock(contBB); SILValue objectResult = contBB->createPhiArgument(resultObjectType, OwnershipKind::Owned); - return SGF.emitManagedRValueWithCleanup(objectResult); + return {SGF.emitManagedRValueWithCleanup(objectResult), error}; } /// Perform a foreign error check by testing whether the error was nil. -static void +static SILValue emitErrorIsNonNilErrorCheck(SILGenFunction &SGF, SILLocation loc, - ManagedValue errorSlot, bool suppressErrorCheck) { + ManagedValue errorSlot, bool suppressErrorCheck, + Optional foreignAsync) { // If we're suppressing the check, just don't check. - if (suppressErrorCheck) return; + if (suppressErrorCheck) + return SILValue(); SILValue optionalError = SGF.B.emitLoadValueOperation( loc, errorSlot.forward(SGF), LoadOwnershipQualifier::Take); @@ -374,7 +405,8 @@ emitErrorIsNonNilErrorCheck(SILGenFunction &SGF, SILLocation loc, ASTContext &ctx = SGF.getASTContext(); // Switch on the optional error. - SILBasicBlock *errorBB = SGF.createBasicBlock(FunctionSection::Postmatter); + SILBasicBlock *errorBB = + SGF.createBasicBlock(functionSectionForConvention(foreignAsync)); errorBB->createPhiArgument(optionalError->getType().unwrapOptionalType(), OwnershipKind::Owned); SILBasicBlock *contBB = SGF.createBasicBlock(); @@ -386,11 +418,11 @@ emitErrorIsNonNilErrorCheck(SILGenFunction &SGF, SILLocation loc, // in the errorSlot as our BB argument so we can pass ownership correctly. In // emitForeignErrorBlock, we will create the appropriate cleanup for the // argument. - SGF.emitForeignErrorBlock(loc, errorBB, None); + SILValue error = SGF.emitForeignErrorBlock(loc, errorBB, None, foreignAsync); // Return the result. SGF.B.emitBlock(contBB); - return; + return error; } /// Emit a check for whether a non-native function call produced an @@ -398,43 +430,41 @@ emitErrorIsNonNilErrorCheck(SILGenFunction &SGF, SILLocation loc, /// /// \c results should be left with only values that match the formal /// direct results of the function. -void -SILGenFunction::emitForeignErrorCheck(SILLocation loc, - SmallVectorImpl &results, - ManagedValue errorSlot, - bool suppressErrorCheck, - const ForeignErrorConvention &foreignError) { +SILValue SILGenFunction::emitForeignErrorCheck( + SILLocation loc, SmallVectorImpl &results, + ManagedValue errorSlot, bool suppressErrorCheck, + const ForeignErrorConvention &foreignError, + Optional foreignAsync) { // All of this is autogenerated. loc.markAutoGenerated(); switch (foreignError.getKind()) { case ForeignErrorConvention::ZeroPreservedResult: assert(results.size() == 1); - emitResultIsZeroErrorCheck(*this, loc, results[0], errorSlot, - suppressErrorCheck, - /*zeroIsError*/ true); - return; + return emitResultIsZeroErrorCheck(*this, loc, results[0], errorSlot, + suppressErrorCheck, + /*zeroIsError*/ true, foreignAsync); case ForeignErrorConvention::ZeroResult: assert(results.size() == 1); - emitResultIsZeroErrorCheck(*this, loc, results.pop_back_val(), - errorSlot, suppressErrorCheck, - /*zeroIsError*/ true); - return; + return emitResultIsZeroErrorCheck(*this, loc, results.pop_back_val(), + errorSlot, suppressErrorCheck, + /*zeroIsError*/ true, foreignAsync); case ForeignErrorConvention::NonZeroResult: assert(results.size() == 1); - emitResultIsZeroErrorCheck(*this, loc, results.pop_back_val(), - errorSlot, suppressErrorCheck, - /*zeroIsError*/ false); - return; - case ForeignErrorConvention::NilResult: + return emitResultIsZeroErrorCheck(*this, loc, results.pop_back_val(), + errorSlot, suppressErrorCheck, + /*zeroIsError*/ false, foreignAsync); + case ForeignErrorConvention::NilResult: { assert(results.size() == 1); - results[0] = emitResultIsNilErrorCheck(*this, loc, results[0], errorSlot, - suppressErrorCheck); - return; + SILValue error; + std::tie(results[0], error) = emitResultIsNilErrorCheck( + *this, loc, results[0], errorSlot, suppressErrorCheck, foreignAsync); + return error; + } case ForeignErrorConvention::NonNilError: // Leave the direct results alone. - emitErrorIsNonNilErrorCheck(*this, loc, errorSlot, suppressErrorCheck); - return; + return emitErrorIsNonNilErrorCheck(*this, loc, errorSlot, + suppressErrorCheck, foreignAsync); } llvm_unreachable("bad foreign error convention kind"); } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 72e756abd493f..dc70697ac85f3 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -26,6 +26,7 @@ #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" +#include "swift/AST/SourceFile.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILProfiler.h" #include "swift/SIL/SILUndef.h" @@ -140,6 +141,9 @@ DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) { case SILDeclRef::Kind::EnumElement: return getMagicFunctionName(cast(ref.getDecl()) ->getDeclContext()); + case SILDeclRef::Kind::EntryPoint: + auto *file = ref.getDecl()->getDeclContext()->getParentSourceFile(); + return getMagicFunctionName(file); } llvm_unreachable("Unhandled SILDeclRefKind in switch."); @@ -510,92 +514,22 @@ void SILGenFunction::emitFunction(FuncDecl *fd) { emitProfilerIncrement(fd->getTypecheckedBody()); emitProlog(captureInfo, fd->getParameters(), fd->getImplicitSelfDecl(), fd, fd->getResultInterfaceType(), fd->hasThrows(), fd->getThrowsLoc()); - prepareEpilog(true, fd->hasThrows(), CleanupLocation(fd)); - - if (fd->isAsyncHandler() && - // If F.isAsync() we are emitting the asyncHandler body and not the - // original asyncHandler. - !F.isAsync()) { - emitAsyncHandler(fd); - } else if (llvm::any_of(*fd->getParameters(), - [](ParamDecl *p){ return p->hasAttachedPropertyWrapper(); })) { - // If any parameters have property wrappers, emit the local auxiliary - // variables before emitting the function body. - LexicalScope BraceScope(*this, CleanupLocation(fd)); - for (auto *param : *fd->getParameters()) { - param->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { - SILLocation WrapperLoc(auxiliaryVar); - WrapperLoc.markAsPrologue(); - if (auto *patternBinding = auxiliaryVar->getParentPatternBinding()) - visitPatternBindingDecl(patternBinding); - - visit(auxiliaryVar); - }); - } - emitStmt(fd->getTypecheckedBody()); + if (fd->isDistributedActorFactory()) { + // Synthesize the factory function body + emitDistributedActorFactory(fd); } else { + prepareEpilog(true, fd->hasThrows(), CleanupLocation(fd)); + + // Emit the actual function body as usual emitStmt(fd->getTypecheckedBody()); - } - emitEpilog(fd); + emitEpilog(fd); + } mergeCleanupBlocks(); } -/// An asyncHandler function is split into two functions: -/// 1. The asyncHandler body function: it contains the body of the function, but -/// is emitted as an async function. -/// 2. The original function: it just contains -/// _runAsyncHandler(operation: asyncHandlerBodyFunction) -void SILGenFunction::emitAsyncHandler(FuncDecl *fd) { - - // 1. step: create the asyncHandler body function - // - auto origFnTy = F.getLoweredFunctionType(); - assert(!F.isAsync() && "an asyncHandler function cannot be async"); - - // The body function type is the same as the original type, just with "async". - auto bodyFnTy = origFnTy->getWithExtInfo(origFnTy->getExtInfo().withAsync()); - - SILDeclRef constant(fd, SILDeclRef::Kind::Func); - std::string name = constant.mangle(SILDeclRef::ManglingKind::AsyncHandlerBody); - SILLocation loc = F.getLocation(); - SILGenFunctionBuilder builder(*this); - - SILFunction *bodyFn = builder.createFunction( - SILLinkage::Hidden, name, bodyFnTy, F.getGenericEnvironment(), - loc, F.isBare(), F.isTransparent(), - F.isSerialized(), IsNotDynamic, ProfileCounter(), IsNotThunk, - SubclassScope::NotApplicable, F.getInlineStrategy(), F.getEffectsKind()); - bodyFn->setDebugScope(new (getModule()) SILDebugScope(loc, bodyFn)); - - SILGenFunction(SGM, *bodyFn, fd).emitFunction(fd); - SGM.emitLazyConformancesForFunction(bodyFn); - - // 2. step: emit the original asyncHandler function - // - Scope scope(*this, loc); - - // %bodyFnRef = partial_apply %bodyFn(%originalArg0, %originalArg1, ...) - // - SmallVector managedArgs; - for (SILValue arg : F.getArguments()) { - ManagedValue argVal = ManagedValue(arg, CleanupHandle::invalid()); - managedArgs.push_back(argVal.copy(*this, loc)); - } - auto *bodyFnRef = B.createFunctionRef(loc, bodyFn); - ManagedValue bodyFnValue = - B.createPartialApply(loc, bodyFnRef, F.getForwardingSubstitutionMap(), - managedArgs, ParameterConvention::Direct_Guaranteed); - - // apply %_runAsyncHandler(%bodyFnValue) - // - FuncDecl *asyncHandlerDecl = SGM.getRunAsyncHandler(); - emitApplyOfLibraryIntrinsic(loc, asyncHandlerDecl, SubstitutionMap(), - { bodyFnValue }, SGFContext()); -} - void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { MagicFunctionName = SILGenModule::getMagicFunctionName(ace); @@ -606,11 +540,6 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { emitProlog(captureInfo, ace->getParameters(), /*selfParam=*/nullptr, ace, resultIfaceTy, ace->isBodyThrowing(), ace->getLoc()); prepareEpilog(true, ace->isBodyThrowing(), CleanupLocation(ace)); - for (auto *param : *ace->getParameters()) { - param->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { - visit(auxiliaryVar); - }); - } if (auto *ce = dyn_cast(ace)) { emitStmt(ce->getBody()); @@ -627,9 +556,13 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { } void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { - // Load argc and argv from the entry point arguments. - SILValue argc = F.begin()->getArgument(0); - SILValue argv = F.begin()->getArgument(1); + // Create the argc and argv arguments. + auto entry = B.getInsertionBB(); + auto paramTypeIter = F.getConventions() + .getParameterSILTypes(getTypeExpansionContext()) + .begin(); + SILValue argc = entry->createFunctionArgument(*paramTypeIter); + SILValue argv = entry->createFunctionArgument(*std::next(paramTypeIter)); switch (mainDecl->getArtificialMainKind()) { case ArtificialMainKind::UIApplicationMain: { diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 7f37c51bc39d7..792fea6f59a5b 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -376,11 +376,22 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// a local variable. llvm::DenseMap VarLocs; + /// The local auxiliary declarations for the parameters of this function that + /// need to be emitted inside the next brace statement. + llvm::SmallVector LocalAuxiliaryDecls; + + // Context information for tracking an `async let` child task. + struct AsyncLetChildTask { + SILValue asyncLet; // RawPointer to the async let state + SILValue resultBuf; // RawPointer to the result buffer + bool isThrowing; // true if task can throw + }; + /// Mapping from each async let clause to the AsyncLet repr that contains the /// AsyncTask that will produce the initializer value for that clause and a /// Boolean value indicating whether the task can throw. llvm::SmallDenseMap, - std::pair > + AsyncLetChildTask> AsyncLetChildTasks; /// When rebinding 'self' during an initializer delegation, we have to be @@ -611,12 +622,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Generates code for a FuncDecl. void emitFunction(FuncDecl *fd); - /// Generate code for @asyncHandler functions. - void emitAsyncHandler(FuncDecl *fd); /// Emits code for a ClosureExpr. void emitClosure(AbstractClosureExpr *ce); /// Generates code for a class destroying destructor. This - /// emits the body code from the DestructorDecl, calls the base class + /// emits the body code from the DestructorDecl, calls the base class /// destructor, then implicitly releases the elements of the class. void emitDestroyingDestructor(DestructorDecl *dd); @@ -839,6 +848,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction Optional actorIso, Optional actorSelf); + /// Emit a hop to the target executor, returning a breadcrumb with enough + /// enough information to hop back. + ExecutorBreadcrumb emitHopToTargetExecutor(SILLocation loc, + SILValue executor); + + /// Generate a hop directly to a dynamic actor instance. This can only be done + /// inside an async actor-independent function. No hop-back is expected. + void emitHopToActorValue(SILLocation loc, ManagedValue actor); + /// A version of `emitHopToTargetActor` that is specialized to the needs /// of various types of ConstructorDecls, like class or value initializers, /// because their prolog emission is not the same as for regular functions. @@ -1369,18 +1387,17 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction ArgumentSource &&value, bool isOnSelfParameter); - ManagedValue emitAsyncLetStart( - SILLocation loc, Type functionType, ManagedValue taskFunction); + ManagedValue emitAsyncLetStart(SILLocation loc, + SILValue taskOptions, + Type functionType, ManagedValue taskFunction, + SILValue resultBuf); - ManagedValue emitAsyncLetGet(SILLocation loc, SILValue asyncLet); - - ManagedValue emitEndAsyncLet(SILLocation loc, SILValue asyncLet); + void emitFinishAsyncLet(SILLocation loc, SILValue asyncLet, SILValue resultBuf); + ManagedValue emitReadAsyncLetBinding(SILLocation loc, VarDecl *var); + ManagedValue emitCancelAsyncTask(SILLocation loc, SILValue task); - void completeAsyncLetChildTask( - PatternBindingDecl *patternBinding, unsigned index); - bool maybeEmitMaterializeForSetThunk(ProtocolConformanceRef conformance, SILLinkage linkage, Type selfInterfaceType, Type selfType, @@ -1588,7 +1605,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction ArrayRef args, const CalleeTypeInfo &calleeTypeInfo, ApplyOptions options, SGFContext evalContext, - Optional implicitAsyncIsolation); + Optional implicitActorHopTarget); RValue emitApplyOfDefaultArgGenerator(SILLocation loc, ConcreteDeclRef defaultArgsOwner, @@ -1856,14 +1873,16 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SILValue foreignErrorSlot, const ForeignErrorConvention &foreignError); - void emitForeignErrorBlock(SILLocation loc, SILBasicBlock *errorBB, - Optional errorSlot); + SILValue emitForeignErrorBlock(SILLocation loc, SILBasicBlock *errorBB, + Optional errorSlot, + Optional foreignAsync); - void emitForeignErrorCheck(SILLocation loc, - SmallVectorImpl &directResults, - ManagedValue errorSlot, - bool suppressErrorCheck, - const ForeignErrorConvention &foreignError); + SILValue emitForeignErrorCheck(SILLocation loc, + SmallVectorImpl &directResults, + ManagedValue errorSlot, + bool suppressErrorCheck, + const ForeignErrorConvention &foreignError, + Optional foreignAsync); //===--------------------------------------------------------------------===// // Re-abstraction thunks @@ -1974,6 +1993,34 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CanSILFunctionType toType, bool reorderSelf); + //===---------------------------------------------------------------------===// + // Distributed Actors + //===---------------------------------------------------------------------===// + + /// Initialize the distributed actors transport and id. + void initializeDistributedActorImplicitStorageInit( + ConstructorDecl *ctor, ManagedValue selfArg); + + /// Given a function representing a distributed actor factory, emits the + /// corresponding SIL function for it. + void emitDistributedActorFactory(FuncDecl *fd); + + /// Generates a thunk from an actor function + void emitDistributedThunk(SILDeclRef thunk); + + /// Notify transport that actor has initialized successfully, + /// and is ready to receive messages. + void emitDistributedActorReady( + ConstructorDecl *ctor, ManagedValue selfArg); + + /// Inject distributed actor and transport interaction code into the destructor. + void emitDistributedActor_resignAddress( + DestructorDecl *dd, SILValue selfValue, SILBasicBlock *continueBB); + + void emitDistributedActorClassMemberDestruction( + SILLocation cleanupLoc, ManagedValue selfValue, ClassDecl *cd, + SILBasicBlock *normalMemberDestroyBB, SILBasicBlock *finishBB); + //===--------------------------------------------------------------------===// // Declarations //===--------------------------------------------------------------------===// @@ -2066,9 +2113,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Destroy and deallocate an initialized local variable. void destroyLocalVariable(SILLocation L, VarDecl *D); - - /// Deallocate an uninitialized local variable. - void deallocateUninitializedLocalVariable(SILLocation L, VarDecl *D); + + /// Destroy the class member. + void destroyClassMember(SILLocation L, ManagedValue selfValue, VarDecl *D); /// Enter a cleanup to deallocate a stack variable. CleanupHandle enterDeallocStackCleanup(SILValue address); @@ -2101,7 +2148,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CleanupHandle enterCancelAsyncTaskCleanup(SILValue task); // Enter a cleanup to cancel and destroy an AsyncLet as it leaves the scope. - CleanupHandle enterAsyncLetCleanup(SILValue alet); + CleanupHandle enterAsyncLetCleanup(SILValue alet, SILValue resultBuf); /// Evaluate an Expr as an lvalue. LValue emitLValue(Expr *E, SGFAccessKind accessKind, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 6c454ac54cb34..5794db6982823 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -537,9 +537,13 @@ namespace { }; class EndAccessPseudoComponent : public WritebackPseudoComponent { + private: + ExecutorBreadcrumb ExecutorHop; public: - EndAccessPseudoComponent(const LValueTypeData &typeData) - : WritebackPseudoComponent(typeData) {} + EndAccessPseudoComponent(const LValueTypeData &typeData, + ExecutorBreadcrumb &&executorHop) + : WritebackPseudoComponent(typeData), + ExecutorHop(std::move(executorHop)) {} private: void writeback(SILGenFunction &SGF, SILLocation loc, @@ -550,6 +554,7 @@ namespace { assert(base.isLValue()); SGF.B.createEndAccess(loc, base.getValue(), /*abort*/ false); + ExecutorHop.emit(SGF, loc); } void dump(raw_ostream &OS, unsigned indent) const override { @@ -559,15 +564,20 @@ namespace { } // end anonymous namespace static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, - SILValue addr, LValueTypeData typeData, + ManagedValue base, SILValue addr, + LValueTypeData typeData, SGFAccessKind accessKind, - SILAccessEnforcement enforcement) { + SILAccessEnforcement enforcement, + Optional actorIso) { auto silAccessKind = isReadAccess(accessKind) ? SILAccessKind::Read : SILAccessKind::Modify; assert(SGF.isInFormalEvaluationScope() && "tried to enter access scope without a writeback scope!"); + ExecutorBreadcrumb prevExecutor = + SGF.emitHopToTargetActor(loc, actorIso, base); + // Enter the access. addr = SGF.B.createBeginAccess(loc, addr, silAccessKind, enforcement, /*hasNoNestedConflict=*/false, @@ -576,7 +586,8 @@ static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, // Push a writeback to end it. auto accessedMV = ManagedValue::forLValue(addr); std::unique_ptr - component(new EndAccessPseudoComponent(typeData)); + component(new EndAccessPseudoComponent(typeData, + std::move(prevExecutor))); pushWriteback(SGF, loc, std::move(component), accessedMV, MaterializedLValue()); @@ -584,12 +595,14 @@ static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, } static ManagedValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, - ManagedValue addr, LValueTypeData typeData, + ManagedValue base, ManagedValue addr, + LValueTypeData typeData, SGFAccessKind accessKind, - SILAccessEnforcement enforcement) { + SILAccessEnforcement enforcement, + Optional actorIso) { return ManagedValue::forLValue( - enterAccessScope(SGF, loc, addr.getLValueAddress(), typeData, - accessKind, enforcement)); + enterAccessScope(SGF, loc, base, addr.getLValueAddress(), typeData, + accessKind, enforcement, actorIso)); } // Find the base of the formal access at `address`. If the base requires an @@ -606,7 +619,7 @@ SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc, auto storage = AccessedStorage::compute(address); // Unsafe access may have invalid storage (e.g. a RawPointer). - if (storage && !isPossibleFormalAccessBase(storage, &SGF.F)) + if (storage && !isPossibleFormalAccessStorage(storage, &SGF.F)) return address; auto BAI = @@ -717,8 +730,9 @@ namespace { bool IsNonAccessing; public: RefElementComponent(VarDecl *field, LValueOptions options, - SILType substFieldType, LValueTypeData typeData) - : PhysicalPathComponent(typeData, RefElementKind), + SILType substFieldType, LValueTypeData typeData, + Optional actorIso) + : PhysicalPathComponent(typeData, RefElementKind, actorIso), Field(field), SubstFieldType(substFieldType), IsNonAccessing(options.IsNonAccessing) {} @@ -744,8 +758,9 @@ namespace { // declarations. Access marker verification is aware of these cases. if (!IsNonAccessing && !Field->isLet()) { if (auto enforcement = SGF.getDynamicEnforcement(Field)) { - result = enterAccessScope(SGF, loc, result, getTypeData(), - getAccessKind(), *enforcement); + result = enterAccessScope(SGF, loc, base, result, getTypeData(), + getAccessKind(), *enforcement, + takeActorIsolation()); } } @@ -761,7 +776,8 @@ namespace { unsigned ElementIndex; public: TupleElementComponent(unsigned elementIndex, LValueTypeData typeData) - : PhysicalPathComponent(typeData, TupleElementKind), + : PhysicalPathComponent(typeData, TupleElementKind, + /*actorIsolation=*/None), ElementIndex(elementIndex) {} virtual bool isLoadingPure() const override { return true; } @@ -790,8 +806,9 @@ namespace { SILType SubstFieldType; public: StructElementComponent(VarDecl *field, SILType substFieldType, - LValueTypeData typeData) - : PhysicalPathComponent(typeData, StructElementKind), + LValueTypeData typeData, + Optional actorIso) + : PhysicalPathComponent(typeData, StructElementKind, actorIso), Field(field), SubstFieldType(substFieldType) {} virtual bool isLoadingPure() const override { return true; } @@ -819,9 +836,9 @@ namespace { class ForceOptionalObjectComponent : public PhysicalPathComponent { bool isImplicitUnwrap; public: - ForceOptionalObjectComponent(LValueTypeData typeData, - bool isImplicitUnwrap) - : PhysicalPathComponent(typeData, OptionalObjectKind), + ForceOptionalObjectComponent(LValueTypeData typeData, bool isImplicitUnwrap) + : PhysicalPathComponent(typeData, OptionalObjectKind, + /*actorIsolation=*/None), isImplicitUnwrap(isImplicitUnwrap) {} ManagedValue project(SILGenFunction &SGF, SILLocation loc, @@ -842,7 +859,8 @@ namespace { public: OpenOpaqueExistentialComponent(CanArchetypeType openedArchetype, LValueTypeData typeData) - : PhysicalPathComponent(typeData, OpenOpaqueExistentialKind) { + : PhysicalPathComponent(typeData, OpenOpaqueExistentialKind, + /*actorIsolation=*/None) { assert(getSubstFormalType() == openedArchetype); } @@ -1016,8 +1034,9 @@ namespace { return Value; SILValue addr = Value.getLValueAddress(); - addr = enterAccessScope(SGF, loc, addr, getTypeData(), - getAccessKind(), *Enforcement); + addr = + enterAccessScope(SGF, loc, base, addr, getTypeData(), getAccessKind(), + *Enforcement, takeActorIsolation()); return ManagedValue::forLValue(addr); } @@ -1034,10 +1053,7 @@ namespace { } else { OS << "unenforced"; } - if (ActorIso) { - OS << "requires actor-hop because "; - simple_display(OS, *ActorIso); - } + if (hasActorIsolation()) OS << " requires actor-hop"; OS << "):\n"; Value.dump(OS, indent + 2); } @@ -1324,9 +1340,12 @@ namespace { if (!initInfo.hasInitFromWrappedValue()) return false; + auto *fnDecl = SGF.FunctionDC->getAsDecl(); bool isAssignmentToSelfParamInInit = - IsOnSelfParameter && - isa(SGF.FunctionDC->getAsDecl()); + IsOnSelfParameter && isa(fnDecl) && + // Convenience initializers only contain assignments and not + // initializations. + !(cast(fnDecl)->isConvenienceInit()); // Assignment to a wrapped property can only be re-written to initialization for // members of `self` in an initializer, and for local variables. @@ -1448,11 +1467,12 @@ namespace { backingVar, AccessKind::Write); } else if (BaseFormalType->mayHaveSuperclass()) { RefElementComponent REC(backingVar, LValueOptions(), varStorageType, - typeData); + typeData, /*actorIsolation=*/None); proj = std::move(REC).project(SGF, loc, base); } else { assert(BaseFormalType->getStructOrBoundGenericStruct()); - StructElementComponent SEC(backingVar, varStorageType, typeData); + StructElementComponent SEC(backingVar, varStorageType, + typeData, /*actorIsolation=*/None); proj = std::move(SEC).project(SGF, loc, base); } @@ -1778,8 +1798,9 @@ namespace { } // Enter an unsafe access scope for the access. - addr = enterAccessScope(SGF, loc, addr, getTypeData(), getAccessKind(), - SILAccessEnforcement::Unsafe); + addr = + enterAccessScope(SGF, loc, base, addr, getTypeData(), getAccessKind(), + SILAccessEnforcement::Unsafe, ActorIso); return addr; } @@ -2084,7 +2105,8 @@ namespace { PhysicalKeyPathApplicationComponent(LValueTypeData typeData, KeyPathTypeKind typeKind, ManagedValue keyPath) - : PhysicalPathComponent(typeData, PhysicalKeyPathApplicationKind), + : PhysicalPathComponent(typeData, PhysicalKeyPathApplicationKind, + /*actorIsolation=*/None), TypeKind(typeKind), KeyPath(keyPath) { assert(typeKind == KPTK_KeyPath || typeKind == KPTK_WritableKeyPath || @@ -2695,6 +2717,7 @@ static AccessKind mapAccessKind(SGFAccessKind accessKind) { case SGFAccessKind::ReadWrite: return AccessKind::ReadWrite; } + llvm_unreachable("covered switch"); } void LValue::addNonMemberVarComponent(SILGenFunction &SGF, SILLocation loc, @@ -2814,14 +2837,6 @@ SILGenFunction::maybeEmitValueOfLocalVarDecl( // For local decls, use the address we allocated or the value if we have it. auto It = VarLocs.find(var); if (It != VarLocs.end()) { - // If the variable is part of an async let, ensure that the child task - // has completed first. - if (var->isSpawnLet() && accessKind != AccessKind::Write) { - auto patternBinding = var->getParentPatternBinding(); - unsigned index = patternBinding->getPatternEntryIndexForVarDecl(var); - completeAsyncLetChildTask(patternBinding, index); - } - // If this has an address, return it. By-value let's have no address. SILValue ptr = It->second.value; if (ptr->getType().isAddress()) @@ -2845,6 +2860,9 @@ SILGenFunction::emitAddressOfLocalVarDecl(SILLocation loc, VarDecl *var, assert(var->getDeclContext()->isLocalContext()); assert(var->getImplInfo().isSimpleStored()); AccessKind astAccessKind = mapAccessKind(accessKind); + + assert(!var->isAsyncLet() && "async let does not have an address"); + auto address = maybeEmitValueOfLocalVarDecl(var, astAccessKind); assert(address); assert(address.isLValue()); @@ -2860,6 +2878,12 @@ RValue SILGenFunction::emitRValueForNonMemberVarDecl(SILLocation loc, FormalEvaluationScope scope(*this); auto *var = cast(declRef.getDecl()); + // If the variable is part of an async let, get the result from the child + // task. + if (var->isAsyncLet()) { + return RValue(*this, loc, formalRValueType, + emitReadAsyncLetBinding(loc, var)); + } auto localValue = maybeEmitValueOfLocalVarDecl(var, AccessKind::Read); // If this VarDecl is represented as an address, emit it as an lvalue, then @@ -3114,8 +3138,7 @@ bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF, bool &isObjCReplacementSelfCall); static bool isCallToSelfOfCurrentFunction(SILGenFunction &SGF, LookupExpr *e) { - return SGF.FunctionDC->getAsDecl() && - isa(SGF.FunctionDC->getAsDecl()) && + return isa_and_nonnull(SGF.FunctionDC->getAsDecl()) && e->getBase()->isSelfExprOf( cast(SGF.FunctionDC->getAsDecl()), false); } @@ -3271,8 +3294,6 @@ void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, using MemberStorageAccessEmitter::MemberStorageAccessEmitter; void emitUsingStorage(LValueTypeData typeData) { - assert(!ActorIso); - // For static variables, emit a reference to the global variable backing // them. // FIXME: This has to be dynamically looked up for classes, and @@ -3286,7 +3307,7 @@ void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, typeData.getAccessKind(), AccessStrategy::getStorage(), FormalRValueType, - /*actorIsolation=*/None); + ActorIso); return; } @@ -3295,10 +3316,12 @@ void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, SGF.getTypeExpansionContext(), Storage, FormalRValueType); if (BaseFormalType->mayHaveSuperclass()) { - LV.add(Storage, Options, varStorageType, typeData); + LV.add(Storage, Options, varStorageType, + typeData, ActorIso); } else { assert(BaseFormalType->getStructOrBoundGenericStruct()); - LV.add(Storage, varStorageType, typeData); + LV.add(Storage, varStorageType, typeData, + ActorIso); } // If the member has weak or unowned storage, convert it away. @@ -4171,18 +4194,17 @@ RValue SILGenFunction::emitLoadOfLValue(SILLocation loc, LValue &&src, // If the last component is physical, drill down and load from it. if (component.isPhysical()) { - auto actorIso = component.asPhysical().getActorIsolation(); - - // If the load must happen in the context of an actor, do a hop first. - prevExecutor = emitHopToTargetActor(loc, actorIso, /*actorSelf=*/None); - auto projection = std::move(component).project(*this, loc, addr); if (projection.getType().isAddress()) { + auto actorIso = component.asPhysical().takeActorIsolation(); + + // If the load must happen in the context of an actor, do a hop first. + prevExecutor = emitHopToTargetActor(loc, actorIso, /*actorSelf=*/None); projection = emitLoad(loc, projection.getValue(), origFormalType, substFormalType, rvalueTL, C, IsNotTake, isBaseGuaranteed); } else if (isReadAccessResultOwned(src.getAccessKind()) && - !projection.isPlusOne(*this)) { + !projection.isPlusOne(*this)) { projection = projection.copy(*this, loc); } @@ -4195,7 +4217,6 @@ RValue SILGenFunction::emitLoadOfLValue(SILLocation loc, LValue &&src, // If we hopped to the target's executor, then we need to hop back. prevExecutor.emit(*this, loc); - return result; } diff --git a/lib/SILGen/SILGenLazyConformance.cpp b/lib/SILGen/SILGenLazyConformance.cpp index 791ae643c7f4c..904ece1a42b6d 100644 --- a/lib/SILGen/SILGenLazyConformance.cpp +++ b/lib/SILGen/SILGenLazyConformance.cpp @@ -324,11 +324,9 @@ void SILGenModule::emitLazyConformancesForFunction(SILFunction *F) { void SILGenModule::emitLazyConformancesForType(NominalTypeDecl *NTD) { auto genericSig = NTD->getGenericSignature(); - if (genericSig) { - for (auto reqt : genericSig->getRequirements()) { - if (reqt.getKind() != RequirementKind::Layout) - useConformancesFromType(reqt.getSecondType()->getCanonicalType()); - } + for (auto reqt : genericSig.getRequirements()) { + if (reqt.getKind() != RequirementKind::Layout) + useConformancesFromType(reqt.getSecondType()->getCanonicalType()); } if (auto *ED = dyn_cast(NTD)) { diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 242ad44b55fa4..051cde6f54d3e 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -82,6 +82,7 @@ // //===----------------------------------------------------------------------===// +#include "ExecutorBreadcrumb.h" #include "Initialization.h" #include "LValue.h" #include "RValue.h" @@ -922,7 +923,7 @@ namespace { auto outputOrigType = AbstractionPattern::getTuple(outputOrigTypes); // Build the substituted output tuple type. Note that we deliberately - // don't use composeInput() because we want to drop ownership + // don't use composeTuple() because we want to drop ownership // qualifiers. SmallVector elts; for (auto param : outputSubstTypes) { @@ -2941,6 +2942,16 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc, assert(!fnType->isPolymorphic()); auto argTypes = fnType->getParameters(); + // If the input is synchronous and global-actor-qualified, and the + // output is asynchronous, hop to the executor expected by the input. + ExecutorBreadcrumb prevExecutor; + if (outputSubstType->isAsync() && !inputSubstType->isAsync()) { + if (Type globalActor = inputSubstType->getGlobalActor()) { + prevExecutor = SGF.emitHopToTargetActor( + loc, ActorIsolation::forGlobalActor(globalActor, false), None); + } + } + // Translate the argument values. Function parameters are // contravariant: we want to switch the direction of transformation // on them by flipping inputOrigType and outputOrigType. @@ -2984,6 +2995,9 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc, // Reabstract the result. SILValue outerResult = resultPlanner.execute(innerResult); + // If we hopped to the target's executor, then we need to hop back. + prevExecutor.emit(SGF, loc); + scope.pop(); SGF.B.createReturn(loc, outerResult); } @@ -3031,7 +3045,7 @@ buildThunkSignature(SILGenFunction &SGF, if (auto genericSig = SGF.F.getLoweredFunctionType()->getInvocationGenericSignature()) { baseGenericSig = genericSig; - depth = genericSig->getGenericParams().back()->getDepth() + 1; + depth = genericSig.getGenericParams().back()->getDepth() + 1; } } @@ -3045,7 +3059,7 @@ buildThunkSignature(SILGenFunction &SGF, AbstractGenericSignatureRequest{ baseGenericSig.getPointer(), { newGenericParam }, { newRequirement }}, GenericSignature()); - genericEnv = genericSig->getGenericEnvironment(); + genericEnv = genericSig.getGenericEnvironment(); newArchetype = genericEnv->mapTypeIntoContext(newGenericParam) ->castTo(); @@ -3329,11 +3343,20 @@ static ManagedValue createThunk(SILGenFunction &SGF, genericEnv, interfaceSubs, dynamicSelfType); + // An actor-isolated non-async function can be converted to an async function + // by inserting a hop to the global actor. + CanType globalActorForThunk; + if (outputSubstType->isAsync() + && !inputSubstType->isAsync()) { + globalActorForThunk = CanType(inputSubstType->getGlobalActor()); + } + auto thunk = SGF.SGM.getOrCreateReabstractionThunk( thunkType, sourceType, toType, - dynamicSelfType); + dynamicSelfType, + globalActorForThunk); // Build it if necessary. if (thunk->empty()) { @@ -3557,7 +3580,7 @@ SILGenFunction::createWithoutActuallyEscapingClosure( dynamicSelfType); auto *thunk = SGM.getOrCreateReabstractionThunk( - thunkType, noEscapingFnTy, escapingFnTy, dynamicSelfType); + thunkType, noEscapingFnTy, escapingFnTy, dynamicSelfType, CanType()); if (thunk->empty()) { thunk->setWithoutActuallyEscapingThunk(); @@ -3661,7 +3684,7 @@ ManagedValue SILGenFunction::getThunkedAutoDiffLinearMap( // Otherwise, it is just a normal reabstraction thunk. else { name = mangler.mangleReabstractionThunkHelper( - thunkType, fromInterfaceType, toInterfaceType, Type(), + thunkType, fromInterfaceType, toInterfaceType, Type(), Type(), getModule().getSwiftModule()); } @@ -3915,15 +3938,10 @@ SILFunction *SILGenModule::getOrCreateCustomDerivativeThunk( SILFunction *customDerivativeFn, const AutoDiffConfig &config, AutoDiffDerivativeFunctionKind kind) { auto customDerivativeFnTy = customDerivativeFn->getLoweredFunctionType(); - auto *thunkGenericEnv = customDerivativeFnTy->getSubstGenericSignature() - ? customDerivativeFnTy->getSubstGenericSignature() - ->getGenericEnvironment() - : nullptr; + auto *thunkGenericEnv = customDerivativeFnTy->getSubstGenericSignature().getGenericEnvironment(); auto origFnTy = originalFn->getLoweredFunctionType(); - CanGenericSignature derivativeCanGenSig; - if (auto derivativeGenSig = config.derivativeGenericSignature) - derivativeCanGenSig = derivativeGenSig->getCanonicalSignature(); + auto derivativeCanGenSig = config.derivativeGenericSignature.getCanonicalSignature(); auto thunkFnTy = origFnTy->getAutoDiffDerivativeFunctionType( config.parameterIndices, config.resultIndices, kind, Types, LookUpConformanceInModule(M.getSwiftModule()), derivativeCanGenSig); diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 29d8a81934765..5203a03ffea0d 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -244,12 +244,11 @@ struct ArgumentInitHelper { } void emitParam(ParamDecl *PD) { - if (PD->hasExternalPropertyWrapper()) { - auto initInfo = PD->getPropertyWrapperInitializerInfo(); - if (initInfo.hasSynthesizedInitializers()) { - SGF.SGM.emitPropertyWrapperBackingInitializer(PD); - } + PD->visitAuxiliaryDecls([&](VarDecl *localVar) { + SGF.LocalAuxiliaryDecls.push_back(localVar); + }); + if (PD->hasExternalPropertyWrapper()) { PD = cast(PD->getPropertyWrapperBackingProperty()); } @@ -307,6 +306,10 @@ static void makeArgument(Type ty, ParamDecl *decl, void SILGenFunction::bindParameterForForwarding(ParamDecl *param, SmallVectorImpl ¶meters) { + if (param->hasExternalPropertyWrapper()) { + param = cast(param->getPropertyWrapperBackingProperty()); + } + makeArgument(param->getType(), param, parameters, *this); } @@ -469,6 +472,7 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo, if (auto destructor = dyn_cast(dc)) { switch (getActorIsolation(destructor)) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: return true; case ActorIsolation::GlobalActor: @@ -500,10 +504,34 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo, dyn_cast_or_null(FunctionDC->getAsDecl())) { auto actorIsolation = getActorIsolation(funcDecl); switch (actorIsolation.getKind()) { - case ActorIsolation::Unspecified: - case ActorIsolation::Independent: - case ActorIsolation::GlobalActorUnsafe: - break; + case ActorIsolation::Unspecified: + case ActorIsolation::Independent: + // If this is an async function that has an isolated parameter, hop + // to it. + if (F.isAsync()) { + for (auto param : *funcDecl->getParameters()) { + if (param->isIsolated()) { + auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation()); + Type actorType = param->getType(); + RValue actorInstanceRV = emitRValueForDecl( + loc, param, actorType, AccessSemantics::Ordinary); + ManagedValue actorInstance = + std::move(actorInstanceRV).getScalarValue(); + ExpectedExecutor = emitLoadActorExecutor(loc, actorInstance); + break; + } + } + } + + break; + + case ActorIsolation::GlobalActorUnsafe: + break; + + case ActorIsolation::DistributedActorInstance: { + // TODO: perhaps here we can emit our special handling to make a message? + LLVM_FALLTHROUGH; + } case ActorIsolation::ActorInstance: { assert(selfParam && "no self parameter for ActorInstance isolation"); @@ -566,7 +594,7 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo, // For an async function, hop to the executor. B.createHopToExecutor( RegularLocation::getAutoGeneratedLocation(F.getLocation()), - ExpectedExecutor); + ExpectedExecutor, /*mandatory*/ false); } else { // For a synchronous function, check that we're on the same executor. // Note: if we "know" that the code is completely Sendable-safe, this @@ -626,20 +654,24 @@ ExecutorBreadcrumb SILGenFunction::emitHopToTargetActor(SILLocation loc, return ExecutorBreadcrumb(); if (auto executor = emitExecutor(loc, *maybeIso, maybeSelf)) { - // Record the previous executor to hop back to when we no longer need to - // be isolated to the target actor. - // - // If we're calling from an actor method ourselves, then we'll want to hop - // back to our own actor. - auto breadcrumb = ExecutorBreadcrumb(emitGetCurrentExecutor(loc)); - B.createHopToExecutor(loc, executor.getValue()); - - return breadcrumb; + return emitHopToTargetExecutor(loc, *executor); } else { return ExecutorBreadcrumb(); } } +ExecutorBreadcrumb SILGenFunction::emitHopToTargetExecutor( + SILLocation loc, SILValue executor) { + // Record the previous executor to hop back to when we no longer need to + // be isolated to the target actor. + // + // If we're calling from an actor method ourselves, then we'll want to hop + // back to our own actor. + auto breadcrumb = ExecutorBreadcrumb(emitGetCurrentExecutor(loc)); + B.createHopToExecutor(loc.asAutoGenerated(), executor, /*mandatory*/ false); + return breadcrumb; +} + Optional SILGenFunction::emitExecutor( SILLocation loc, ActorIsolation isolation, Optional maybeSelf) { @@ -648,7 +680,8 @@ Optional SILGenFunction::emitExecutor( case ActorIsolation::Independent: return None; - case ActorIsolation::ActorInstance: { + case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: { // "self" here means the actor instance's "self" value. assert(maybeSelf.hasValue() && "actor-instance but no self provided?"); auto self = maybeSelf.getValue(); @@ -659,6 +692,25 @@ Optional SILGenFunction::emitExecutor( case ActorIsolation::GlobalActorUnsafe: return emitLoadGlobalActorExecutor(isolation.getGlobalActor()); } + llvm_unreachable("covered switch"); +} + +void SILGenFunction::emitHopToActorValue(SILLocation loc, ManagedValue actor) { + // TODO: can the type system enforce this async requirement? + if (!F.isAsync()) { + llvm::report_fatal_error("Builtin.hopToActor must be in an async function"); + } + auto isolation = getActorIsolationOfContext(FunctionDC); + if (isolation != ActorIsolation::Independent + && isolation != ActorIsolation::Unspecified) { + // TODO: Explicit hop with no hop-back should only be allowed in independent + // async functions. But it needs work for any closure passed to + // Task.detached, which currently has unspecified isolation. + llvm::report_fatal_error( + "Builtin.hopToActor must be in an actor-independent function"); + } + SILValue executor = emitLoadActorExecutor(loc, actor); + B.createHopToExecutor(loc.asAutoGenerated(), executor, /*mandatory*/ true); } void SILGenFunction::emitPreconditionCheckExpectedExecutor( @@ -689,7 +741,7 @@ void SILGenFunction::emitPreconditionCheckExpectedExecutor( void ExecutorBreadcrumb::emit(SILGenFunction &SGF, SILLocation loc) { if (Executor) - SGF.B.createHopToExecutor(loc, Executor); + SGF.B.createHopToExecutor(loc.asAutoGenerated(), Executor, /*mandatory*/ false); } SILValue SILGenFunction::emitGetCurrentExecutor(SILLocation loc) { diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 4c0e532f3c045..cae6e98291a37 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -282,7 +282,19 @@ void StmtEmitter::visitBraceStmt(BraceStmt *S) { const unsigned ThrowStmtType = 3; const unsigned UnknownStmtType = 4; unsigned StmtType = UnknownStmtType; - + + // Emit local auxiliary declarations. + if (!SGF.LocalAuxiliaryDecls.empty()) { + for (auto *var : SGF.LocalAuxiliaryDecls) { + if (auto *patternBinding = var->getParentPatternBinding()) + SGF.visit(patternBinding); + + SGF.visit(var); + } + + SGF.LocalAuxiliaryDecls.clear(); + } + for (auto &ESD : S->getElements()) { if (auto D = ESD.dyn_cast()) @@ -330,9 +342,10 @@ void StmtEmitter::visitBraceStmt(BraceStmt *S) { continue; } } else if (auto D = ESD.dyn_cast()) { - // Local type declarations are not unreachable because they can appear - // after the declared type has already been used. - if (isa(D)) + // Local declarations aren't unreachable - only their usages can be. To + // that end, we only care about pattern bindings since their + // initializer expressions can be unreachable. + if (!isa(D)) continue; } diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index 07474b0b69781..950c70165fa4b 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -28,7 +28,9 @@ #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/FileUnit.h" #include "swift/AST/ForeignAsyncConvention.h" +#include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/SIL/FormalLinkage.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/TypeLowering.h" @@ -103,6 +105,12 @@ void SILGenModule::emitNativeToForeignThunk(SILDeclRef thunk) { emitFunctionDefinition(thunk, getFunction(thunk, ForDefinition)); } +void SILGenModule::emitDistributedThunk(SILDeclRef thunk) { + // Thunks are always emitted by need, so don't need delayed emission. + assert(thunk.isDistributedThunk() && "distributed thunks only"); + emitFunctionDefinition(thunk, getFunction(thunk, ForDefinition)); +} + SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, SILConstantInfo constantInfo, @@ -181,15 +189,29 @@ static const clang::Type *prependParameterType( return clangCtx.getPointerType(newFnTy).getTypePtr(); } -SILFunction * -SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( - CanSILFunctionType blockType, - CanType continuationTy, - CanGenericSignature sig, - ForeignAsyncConvention convention) { +SILFunction *SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( + CanSILFunctionType blockType, CanType continuationTy, + AbstractionPattern origFormalType, CanGenericSignature sig, + ForeignAsyncConvention convention, + Optional foreignError) { // Extract the result and error types from the continuation type. auto resumeType = cast(continuationTy).getGenericArgs()[0]; + CanAnyFunctionType completionHandlerOrigTy = [&]() { + auto completionHandlerOrigTy = + origFormalType.getObjCMethodAsyncCompletionHandlerForeignType(convention, Types); + Optional maybeCompletionHandlerOrigTy; + if (auto fnTy = + dyn_cast(completionHandlerOrigTy)) { + maybeCompletionHandlerOrigTy = fnTy; + } else { + maybeCompletionHandlerOrigTy = cast( + completionHandlerOrigTy.getOptionalObjectType()); + } + return maybeCompletionHandlerOrigTy.getValue(); + }(); + auto blockParams = completionHandlerOrigTy.getParams(); + // Build up the implementation function type, which matches the // block signature with an added block storage argument that points at the // block buffer. The block storage holds the continuation we feed the @@ -207,7 +229,7 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( getASTContext(), blockType->getClangTypeInfo().getType(), getASTContext().getClangTypeForIRGen(blockStorageTy)); - + auto implTy = SILFunctionType::get(sig, blockType->getExtInfo().intoBuilder() .withRepresentation(SILFunctionTypeRepresentation::CFunctionPointer) @@ -239,8 +261,7 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( if (F->empty()) { // Emit the implementation. - if (sig) - F->setGenericEnvironment(sig->getGenericEnvironment()); + F->setGenericEnvironment(sig.getGenericEnvironment()); SILGenFunction SGF(*this, *F, SwiftModule); { @@ -254,10 +275,14 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( auto continuationVal = SGF.B.createLoad(loc, continuationAddr, LoadOwnershipQualifier::Trivial); auto continuation = ManagedValue::forUnmanaged(continuationVal); - + // Check for an error if the convention includes one. - auto errorIndex = convention.completionHandlerErrorParamIndex(); - auto flagIndex = convention.completionHandlerFlagParamIndex(); + // Increment the error and flag indices if present. They do not account + // for the fact that they are preceded by the block_storage arguments. + auto errorIndex = convention.completionHandlerErrorParamIndex().map( + [](auto original) { return original + 1; }); + auto flagIndex = convention.completionHandlerFlagParamIndex().map( + [](auto original) { return original + 1; }); FuncDecl *resumeIntrinsic; @@ -265,8 +290,8 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( if (errorIndex) { resumeIntrinsic = getResumeUnsafeThrowingContinuation(); auto errorIntrinsic = getResumeUnsafeThrowingContinuationWithError(); - - auto errorArgument = params[*errorIndex + 1]; + + auto errorArgument = params[*errorIndex]; auto someErrorBB = SGF.createBasicBlock(FunctionSection::Postmatter); auto noneErrorBB = SGF.createBasicBlock(); returnBB = SGF.createBasicBlockAfter(noneErrorBB); @@ -275,8 +300,8 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( // Check whether there's an error, based on the presence of a flag // parameter. If there is a flag parameter, test it against zero. if (flagIndex) { - auto flagArgument = params[*flagIndex + 1]; - + auto flagArgument = params[*flagIndex]; + // The flag must be an integer type. Get the underlying builtin // integer field from it. auto builtinFlagArg = SGF.emitUnwrapIntegerResult(loc, flagArgument.getValue()); @@ -337,10 +362,12 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( errorScope.pop(); SGF.B.createBranch(loc, returnBB); SGF.B.emitBlock(noneErrorBB); + } else if (foreignError) { + resumeIntrinsic = getResumeUnsafeThrowingContinuation(); } else { resumeIntrinsic = getResumeUnsafeContinuation(); } - + auto loweredResumeTy = SGF.getLoweredType(AbstractionPattern::getOpaque(), F->mapTypeIntoContext(resumeType)); @@ -351,15 +378,13 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( auto resumeArgBuf = SGF.emitTemporaryAllocation(loc, loweredResumeTy.getAddressType()); - auto prepareArgument = [&](SILValue destBuf, ManagedValue arg) { + auto prepareArgument = [&](SILValue destBuf, CanType destFormalType, + ManagedValue arg, CanType argFormalType) { // Convert the ObjC argument to the bridged Swift representation we // want. - ManagedValue bridgedArg = SGF.emitBridgedToNativeValue(loc, - arg.copy(SGF, loc), - arg.getType().getASTType(), - // FIXME: pass down formal type - destBuf->getType().getASTType(), - destBuf->getType().getObjectType()); + ManagedValue bridgedArg = SGF.emitBridgedToNativeValue( + loc, arg.copy(SGF, loc), argFormalType, destFormalType, + destBuf->getType().getObjectType()); // Force-unwrap an argument that comes to us as Optional if it's // formally non-optional in the return. if (bridgedArg.getType().getOptionalObjectType() @@ -371,31 +396,52 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( bridgedArg.forwardInto(SGF, loc, destBuf); }; + // Collect the indices which correspond to the values to be returned. + SmallVector paramIndices; + for (auto index : indices(params)) { + // The first index is the block_storage parameter. + if (index == 0) + continue; + if (errorIndex && index == *errorIndex) + continue; + if (flagIndex && index == *flagIndex) + continue; + paramIndices.push_back(index); + } + auto blockParamIndex = [paramIndices](unsigned long i) { + // The non-error, non-flag block parameter (formal types of the + // completion handler's arguments) indices are the same as the the + // parameter (lowered types of the completion handler's arguments) + // indices but shifted by 1 corresponding to the fact that the lowered + // completion handler has a block_storage argument but the formal type + // does not. + return paramIndices[i] - 1; + }; if (auto resumeTuple = dyn_cast(resumeType)) { + assert(paramIndices.size() == resumeTuple->getNumElements()); assert(params.size() == resumeTuple->getNumElements() + 1 + (bool)errorIndex + (bool)flagIndex); for (unsigned i : indices(resumeTuple.getElementTypes())) { - unsigned paramI = i; - if (errorIndex && paramI >= *errorIndex) { - ++paramI; - } - if (flagIndex && paramI >= *flagIndex) { - ++paramI; - } auto resumeEltBuf = SGF.B.createTupleElementAddr(loc, resumeArgBuf, i); - prepareArgument(resumeEltBuf, params[paramI + 1]); + prepareArgument( + /*destBuf*/ resumeEltBuf, + /*destFormalType*/ + F->mapTypeIntoContext(resumeTuple.getElementTypes()[i]) + ->getCanonicalType(), + /*arg*/ params[paramIndices[i]], + /*argFormalType*/ + blockParams[blockParamIndex(i)].getParameterType()); } } else { + assert(paramIndices.size() == 1); assert(params.size() == 2 + (bool)errorIndex + (bool)flagIndex); - unsigned paramI = 0; - if (errorIndex && paramI >= *errorIndex) { - ++paramI; - } - if (flagIndex && paramI >= *flagIndex) { - ++paramI; - } - prepareArgument(resumeArgBuf, params[paramI + 1]); + prepareArgument(/*destBuf*/ resumeArgBuf, + /*destFormalType*/ + F->mapTypeIntoContext(resumeType)->getCanonicalType(), + /*arg*/ params[paramIndices[0]], + /*argFormalType*/ + blockParams[blockParamIndex(0)].getParameterType()); } // Resume the continuation with the composed bridged result. @@ -429,7 +475,8 @@ SILFunction *SILGenModule:: getOrCreateReabstractionThunk(CanSILFunctionType thunkType, CanSILFunctionType fromType, CanSILFunctionType toType, - CanType dynamicSelfType) { + CanType dynamicSelfType, + CanType fromGlobalActorBound) { // The reference to the thunk is likely @noescape, but declarations are always // escaping. auto thunkDeclType = @@ -445,18 +492,31 @@ getOrCreateReabstractionThunk(CanSILFunctionType thunkType, if (dynamicSelfType) dynamicSelfInterfaceType = dynamicSelfType->mapTypeOutOfContext() ->getCanonicalType(); + if (fromGlobalActorBound) + fromGlobalActorBound = fromGlobalActorBound->mapTypeOutOfContext() + ->getCanonicalType(); Mangle::ASTMangler NewMangler; std::string name = NewMangler.mangleReabstractionThunkHelper(thunkType, fromInterfaceType, toInterfaceType, dynamicSelfInterfaceType, + fromGlobalActorBound, M.getSwiftModule()); auto loc = RegularLocation::getAutoGeneratedLocation(); + + // The thunk that converts an actor-constrained, non-async function to an + // async function is not serializable if the actor's visibility precludes it. + auto serializable = IsSerializable; + if (fromGlobalActorBound) { + auto globalActorLinkage = getTypeLinkage(fromGlobalActorBound); + serializable = globalActorLinkage >= FormalLinkage::PublicNonUnique + ? IsSerializable : IsNotSerialized; + } SILGenFunctionBuilder builder(*this); return builder.getOrCreateSharedFunction( - loc, name, thunkDeclType, IsBare, IsTransparent, IsSerializable, + loc, name, thunkDeclType, IsBare, IsTransparent, serializable, ProfileCounter(), IsReabstractionThunk, IsNotDynamic); } @@ -483,8 +543,7 @@ SILFunction *SILGenModule::getOrCreateDerivativeVTableThunk( if (!thunk->empty()) return thunk; - if (auto genSig = constantTy->getSubstGenericSignature()) - thunk->setGenericEnvironment(genSig->getGenericEnvironment()); + thunk->setGenericEnvironment(constantTy->getSubstGenericSignature().getGenericEnvironment()); SILGenFunction SGF(*this, *thunk, SwiftModule); SmallVector params; auto loc = derivativeFnDeclRef.getAsRegularLocation(); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index d2059c7f6819a..4d37eaedea818 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -184,9 +184,7 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, if (auto existingThunk = M.lookUpFunction(name)) return SILVTable::Entry(base, existingThunk, implKind, false); - GenericEnvironment *genericEnv = nullptr; - if (auto genericSig = overrideInfo.FormalType.getOptGenericSignature()) - genericEnv = genericSig->getGenericEnvironment(); + auto *genericEnv = overrideInfo.FormalType.getOptGenericSignature().getGenericEnvironment(); // Emit the thunk. SILLocation loc(derivedDecl); @@ -1102,7 +1100,7 @@ class SILGenType : public TypeMemberVisitor { return; // Emit any default argument generators. - SGM.emitDefaultArgGenerators(EED, EED->getParameterList()); + SGM.emitArgumentGenerators(EED, EED->getParameterList()); } void visitPatternBindingDecl(PatternBindingDecl *pd) { @@ -1137,7 +1135,7 @@ class SILGenType : public TypeMemberVisitor { } void visitSubscriptDecl(SubscriptDecl *sd) { - SGM.emitDefaultArgGenerators(sd, sd->getIndices()); + SGM.emitArgumentGenerators(sd, sd->getIndices()); visitAbstractStorageDecl(sd); } @@ -1263,7 +1261,7 @@ class SILGenExtension : public TypeMemberVisitor { } void visitSubscriptDecl(SubscriptDecl *sd) { - SGM.emitDefaultArgGenerators(sd, sd->getIndices()); + SGM.emitArgumentGenerators(sd, sd->getIndices()); visitAbstractStorageDecl(sd); } diff --git a/lib/SILOptimizer/ARC/ARCLoopOpts.cpp b/lib/SILOptimizer/ARC/ARCLoopOpts.cpp index aa54592497e51..24f0ace63479b 100644 --- a/lib/SILOptimizer/ARC/ARCLoopOpts.cpp +++ b/lib/SILOptimizer/ARC/ARCLoopOpts.cpp @@ -66,7 +66,7 @@ class ARCLoopOpts : public SILFunctionTransform { } // Get all of the analyses that we need. - auto *AA = getAnalysis(); + auto *AA = getAnalysis(F); auto *RCFI = getAnalysis()->get(F); auto *EAFI = getAnalysis()->get(F); auto *LRFI = getAnalysis()->get(F); diff --git a/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp b/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp index a3a0cb3598097..4b932aa15d5ba 100644 --- a/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp +++ b/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp @@ -260,7 +260,7 @@ class ARCSequenceOpts : public SILFunctionTransform { return; if (!EnableLoopARC) { - auto *AA = getAnalysis(); + auto *AA = getAnalysis(F); auto *POTA = getAnalysis(); auto *RCFI = getAnalysis()->get(F); auto *EAFI = getAnalysis()->get(F); @@ -288,7 +288,7 @@ class ARCSequenceOpts : public SILFunctionTransform { LA->unlockInvalidation(); } - auto *AA = getAnalysis(); + auto *AA = getAnalysis(F); auto *POTA = getAnalysis(); auto *RCFI = getAnalysis()->get(F); auto *EAFI = getAnalysis()->get(F); diff --git a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp index 0a804ca876be5..40a8ccbee2b48 100644 --- a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp @@ -157,7 +157,6 @@ bool swift::canUseObject(SILInstruction *Inst) { // Debug values do not use referenced counted values in a manner we care // about. case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: return false; // Casts do not use pointers in a manner that we care about since we strip @@ -1159,8 +1158,7 @@ SILInstruction *swift::findReleaseToMatchUnsafeGuaranteedValue( continue; } - if (CurInst.mayHaveSideEffects() && !isa(CurInst) && - !isa(CurInst)) + if (CurInst.mayHaveSideEffects() && !DebugValueInst::hasAddrVal(&CurInst)) break; } @@ -1178,8 +1176,7 @@ SILInstruction *swift::findReleaseToMatchUnsafeGuaranteedValue( continue; } - if (CurInst.mayHaveSideEffects() && !isa(CurInst) && - !isa(CurInst)) + if (CurInst.mayHaveSideEffects() && !DebugValueInst::hasAddrVal(&CurInst)) break; } diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index 0d6eba8442681..c642e76384a39 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -116,7 +116,6 @@ void AccessSummaryAnalysis::processArgument(FunctionInfo *info, worklist.append(inst->use_begin(), inst->use_end()); break; } - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::AddressToPointerInst: // Ignore these uses, they don't affect formal accesses. break; @@ -124,6 +123,10 @@ void AccessSummaryAnalysis::processArgument(FunctionInfo *info, processPartialApply(info, argumentIndex, cast(user), operand, order); break; + case SILInstructionKind::DebugValueInst: + if (DebugValueInst::hasAddrVal(user)) + break; + LLVM_FALLTHROUGH; default: // FIXME: These likely represent scenarios in which we're not generating // begin access markers. Ignore these for now. But we really should @@ -143,10 +146,22 @@ void AccessSummaryAnalysis::processArgument(FunctionInfo *info, /// used by directly calling it or passing it as argument, but not using it as a /// partial_apply callee. /// -/// FIXME: This needs to be checked in the SILVerifier. +/// An error found in DiagnoseInvalidEscapingCaptures can indicate invalid SIL +/// that is detected here but not in normal SIL verification. When the +/// source-level closure captures an inout argument, it appears in SIL to be a +/// non-escaping closure. The following verification then fails because the +/// "nonescaping" closure actually escapes. +/// +/// FIXME: This should be checked in the SILVerifier, with consideration for the +/// caveat above where an inout has been captured be an escaping closure. static bool hasExpectedUsesOfNoEscapePartialApply(Operand *partialApplyUse) { SILInstruction *user = partialApplyUse->getUser(); + // Bypass this verification when a diagnostic error is present. See comments + // on DiagnoseInvalidEscapingCaptures above. + if (user->getModule().getASTContext().hadError()) + return true; + // It is fine to call the partial apply switch (user->getKind()) { case SILInstructionKind::ApplyInst: diff --git a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp index 64a80796a3d19..f498cb706bedd 100644 --- a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp @@ -222,39 +222,47 @@ static SILValue getCallerArg(FullApplySite fullApply, unsigned paramIndex) { static StorageAccessInfo transformCalleeStorage(const StorageAccessInfo &storage, FullApplySite fullApply) { - switch (storage.getKind()) { - case AccessedStorage::Box: - case AccessedStorage::Stack: + if (storage.isLocal()) { // Do not merge local storage. return StorageAccessInfo(AccessedStorage(), storage); - case AccessedStorage::Global: - // Global accesses is universal. - return storage; - case AccessedStorage::Class: - case AccessedStorage::Tail: { - // If the object's value is an argument, translate it into a value on the - // caller side. - SILValue obj = storage.getObject(); - if (auto *arg = dyn_cast(obj)) { - SILValue argVal = getCallerArg(fullApply, arg->getIndex()); - if (argVal) { - unsigned idx = (storage.getKind() == AccessedStorage::Class) - ? storage.getPropertyIndex() - : AccessedStorage::TailIndex; + } + // Remap reference storage. The caller's argument becomes the new object. The + // old storage info is inherited. + if (storage.isReference()) { + auto object = storage.getObject(); + if (auto *arg = dyn_cast(object)) { + if (SILValue argVal = getCallerArg(fullApply, arg->getIndex())) { // Remap this storage info. The argument source value is now the new // object. The old storage info is inherited. - return StorageAccessInfo(AccessedStorage::forClass(argVal, idx), - storage); + auto callerStorage = storage.transformReference(argVal); + return StorageAccessInfo(callerStorage, storage); } } - // Otherwise, continue to reference the value in the callee because we don't - // have any better placeholder for a callee-defined object. + // Continue using the callee value as the storage object. return storage; } + switch (storage.getKind()) { + case AccessedStorage::Box: + case AccessedStorage::Class: + case AccessedStorage::Tail: + case AccessedStorage::Stack: + llvm_unreachable("Handled immediately above"); + case AccessedStorage::Nested: + llvm_unreachable("Nested storage should not be used here"); + case AccessedStorage::Global: + // Global accesses is universal. + return storage; + case AccessedStorage::Yield: + // Continue to hold on to yields from the callee because we don't have + // any better placeholder in the callee. + return storage; + case AccessedStorage::Unidentified: + // For unidentified storage, continue to reference the value in the callee + // because we don't have any better placeholder for a callee-defined object. + return storage; case AccessedStorage::Argument: { // Transitively search for the storage base in the caller. - SILValue argVal = getCallerArg(fullApply, storage.getParamIndex()); - if (argVal) { + if (SILValue argVal = getCallerArg(fullApply, storage.getParamIndex())) { // Remap the argument source value and inherit the old storage info. if (auto calleeStorage = AccessedStorage::compute(argVal)) return StorageAccessInfo(calleeStorage, storage); @@ -267,21 +275,12 @@ transformCalleeStorage(const StorageAccessInfo &storage, // AccessedStorage::compute returns an invalid SILValue, which won't // pass SIL verification. // - // FIXME: In case argVal is invalid, support Unidentified access for invalid - // values. This would also be useful for partially invalidating results. + // TODO: To handle invalid argVal, consider allowing Unidentified access for + // invalid values. This may be useful for partially invalidating results. return StorageAccessInfo( - AccessedStorage(argVal, AccessedStorage::Unidentified), storage); + AccessedStorage(storage.getValue(), AccessedStorage::Unidentified), + storage); } - case AccessedStorage::Nested: - llvm_unreachable("Unexpected nested access"); - case AccessedStorage::Yield: - // Continue to hold on to yields from the callee because we don't have - // any better placeholder in the callee. - return storage; - case AccessedStorage::Unidentified: - // For unidentified storage, continue to reference the value in the callee - // because we don't have any better placeholder for a callee-defined object. - return storage; } llvm_unreachable("unhandled kind"); } diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index 64ec6dba82368..2fe0c7ad2316d 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -12,6 +12,7 @@ #define DEBUG_TYPE "sil-aa" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" +#include "swift/SIL/SILBridgingUtils.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" @@ -27,16 +28,11 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" +#include "swift/SILOptimizer/OptimizerBridging.h" using namespace swift; -// The AliasAnalysis Cache must not grow beyond this size. -// We limit the size of the AA cache to 2**14 because we want to limit the -// memory usage of this cache. -static const int AliasAnalysisMaxCacheSize = 16384; - - //===----------------------------------------------------------------------===// // AA Debugging //===----------------------------------------------------------------------===// @@ -538,27 +534,6 @@ bool AliasAnalysis::typesMayAlias(SILType T1, SILType T2, return MA; } -void AliasAnalysis::handleDeleteNotification(SILNode *node) { - // The pointer 'node' is going away. We can't scan the whole cache - // and remove all of the occurrences of the pointer. Instead we remove - // the pointer from the index caches. - - if (auto *value = dyn_cast(node)) - ValueToIndex.invalidateValue(value); - - if (auto *inst = dyn_cast(node)) { - InstructionToIndex.invalidateValue(inst); - - // When a MultipleValueInstruction is deleted, we have to invalidate all - // the instruction results. - if (auto *mvi = dyn_cast(inst)) { - for (unsigned idx = 0, end = mvi->getNumResults(); idx < end; ++idx) { - ValueToIndex.invalidateValue(mvi->getResult(idx)); - } - } - } -} - //===----------------------------------------------------------------------===// // Entry Points //===----------------------------------------------------------------------===// @@ -567,7 +542,8 @@ void AliasAnalysis::handleDeleteNotification(SILNode *node) { /// to disambiguate the two values. AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2, SILType TBAAType1, SILType TBAAType2) { - AliasKeyTy Key = toAliasKey(V1, V2, TBAAType1, TBAAType2); + AliasCacheKey Key = {V1, V2, TBAAType1.getOpaqueValue(), + TBAAType2.getOpaqueValue()}; // Check if we've already computed this result. auto It = AliasCache.find(Key); @@ -575,11 +551,6 @@ AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2, return It->second; } - // Flush the cache if the size of the cache is too large. - if (AliasCache.size() > AliasAnalysisMaxCacheSize) { - AliasCache.clear(); - } - // Calculate the aliasing result and store it in the cache. auto Result = aliasInner(V1, V2, TBAAType1, TBAAType2); AliasCache[Key] = Result; @@ -807,24 +778,51 @@ bool AliasAnalysis::mayValueReleaseInterfereWithInstruction( return EA->mayReleaseReferenceContent(releasedReference, accessedPointer); } -void AliasAnalysis::initialize(SILPassManager *PM) { - SEA = PM->getAnalysis(); - EA = PM->getAnalysis(); -} +namespace { + +class AliasAnalysisContainer : public FunctionAnalysisBase { + SideEffectAnalysis *SEA = nullptr; + EscapeAnalysis *EA = nullptr; + +public: + AliasAnalysisContainer() : FunctionAnalysisBase(SILAnalysisKind::Alias) {} + + virtual bool shouldInvalidate(SILAnalysis::InvalidationKind K) override { + return K & InvalidationKind::Instructions; + } + + // Computes loop information for the given function using dominance + // information. + virtual std::unique_ptr + newFunctionAnalysis(SILFunction *F) override { + assert(EA && SEA && "dependent analysis not initialized"); + return std::make_unique(SEA, EA); + } + + virtual void initialize(SILPassManager *PM) override { + SEA = PM->getAnalysis(); + EA = PM->getAnalysis(); + } +}; + +} // end anonymous namespace SILAnalysis *swift::createAliasAnalysis(SILModule *M) { - return new AliasAnalysis(M); + return new AliasAnalysisContainer(); +} + +//===----------------------------------------------------------------------===// +// Swift Bridging +//===----------------------------------------------------------------------===// + +inline AliasAnalysis *castToAliasAnalysis(BridgedAliasAnalysis aa) { + return const_cast( + static_cast(aa.aliasAnalysis)); } -AliasKeyTy AliasAnalysis::toAliasKey(SILValue V1, SILValue V2, - SILType Type1, SILType Type2) { - size_t idx1 = ValueToIndex.getIndex(V1); - assert(idx1 != std::numeric_limits::max() && - "~0 index reserved for empty/tombstone keys"); - size_t idx2 = ValueToIndex.getIndex(V2); - assert(idx2 != std::numeric_limits::max() && - "~0 index reserved for empty/tombstone keys"); - void *t1 = Type1.getOpaqueValue(); - void *t2 = Type2.getOpaqueValue(); - return {idx1, idx2, t1, t2}; +BridgedMemoryBehavior AliasAnalysis_getMemBehavior(BridgedAliasAnalysis aa, + BridgedInstruction inst, + BridgedValue addr) { + return (BridgedMemoryBehavior)castToAliasAnalysis(aa)-> + computeMemoryBehavior(castToInst(inst), castToSILValue(addr)); } diff --git a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp index 6d92d617ccebe..023237e4d7dd4 100644 --- a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp @@ -16,6 +16,8 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/Basic/Statistic.h" #include "swift/SIL/SILModule.h" +#include "swift/SIL/SILBridgingUtils.h" +#include "swift/SILOptimizer/OptimizerBridging.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "llvm/Support/Compiler.h" @@ -85,7 +87,7 @@ CalleeCache::getOrCreateCalleesForMethod(SILDeclRef Decl) { if (Found != TheCache.end()) return Found->second; - auto *TheCallees = new (Allocator.Allocate()) Callees; + auto *TheCallees = new (Allocator.Allocate()) CalleeList::Callees; bool canCallUnknown = !calleesAreStaticallyKnowable(M, Decl); CalleesAndCanCallUnknown Entry(TheCallees, canCallUnknown); @@ -225,7 +227,7 @@ CalleeList CalleeCache::getCalleeList(SILDeclRef Decl) const { return CalleeList(); auto &Pair = Found->second; - return CalleeList(*Pair.getPointer(), Pair.getInt()); + return CalleeList(Pair.getPointer(), Pair.getInt()); } // Return a callee list for the given witness method. @@ -318,3 +320,29 @@ void BasicCalleeAnalysis::print(llvm::raw_ostream &os) const { } } } + +//===----------------------------------------------------------------------===// +// Swift Bridging +//===----------------------------------------------------------------------===// + +BridgedCalleeList CalleeAnalysis_getCallees(BridgedCalleeAnalysis calleeAnalysis, + BridgedValue callee) { + BasicCalleeAnalysis *bca = static_cast(calleeAnalysis.bca); + CalleeList cl = bca->getCalleeListOfValue(castToSILValue(callee)); + return {cl.getOpaquePtr(), cl.getOpaqueKind(), cl.isIncomplete()}; +} + +SwiftInt BridgedFunctionArray_size(BridgedCalleeList callees) { + CalleeList cl = CalleeList::fromOpaque(callees.opaquePtr, callees.kind, + callees.incomplete); + return cl.end() - cl.begin(); +} + +BridgedFunction BridgedFunctionArray_get(BridgedCalleeList callees, + SwiftInt index) { + CalleeList cl = CalleeList::fromOpaque(callees.opaquePtr, callees.kind, + callees.incomplete); + auto iter = cl.begin() + index; + assert(index >= 0 && iter < cl.end()); + return {*iter}; +} diff --git a/lib/SILOptimizer/Analysis/EpilogueARCAnalysis.cpp b/lib/SILOptimizer/Analysis/EpilogueARCAnalysis.cpp index cd057ace2ec83..325895dbc4f4a 100644 --- a/lib/SILOptimizer/Analysis/EpilogueARCAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EpilogueARCAnalysis.cpp @@ -172,11 +172,17 @@ bool EpilogueARCContext::computeEpilogueARC() { //===----------------------------------------------------------------------===// void EpilogueARCAnalysis::initialize(SILPassManager *PM) { - AA = PM->getAnalysis(); + passManager = PM; PO = PM->getAnalysis(); RC = PM->getAnalysis(); } +std::unique_ptr +EpilogueARCAnalysis::newFunctionAnalysis(SILFunction *F) { + return std::make_unique(F, PO, + passManager->getAnalysis(F), RC); +} + SILAnalysis *swift::createEpilogueARCAnalysis(SILModule *M) { return new EpilogueARCAnalysis(M); } diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 10f397eb59aba..f4de88c10b9bb 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -1271,15 +1271,6 @@ bool EscapeAnalysis::ConnectionGraph::forwardTraverseDefer( return true; } -void EscapeAnalysis::ConnectionGraph::removeFromGraph(ValueBase *V) { - CGNode *node = Values2Nodes.lookup(V); - if (!node) - return; - Values2Nodes.erase(V); - if (node->mappedValue == V) - node->mappedValue = nullptr; -} - //===----------------------------------------------------------------------===// // Dumping, Viewing and Verification //===----------------------------------------------------------------------===// @@ -2152,7 +2143,6 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::CondBranchInst: case SILInstructionKind::SwitchEnumInst: case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::InitExistentialMetatypeInst: case SILInstructionKind::OpenExistentialMetatypeInst: @@ -2922,21 +2912,6 @@ void EscapeAnalysis::invalidate(SILFunction *F, InvalidationKind K) { } } -void EscapeAnalysis::handleDeleteNotification(SILNode *node) { - auto value = dyn_cast(node); - if (!value) return; - - if (SILBasicBlock *Parent = node->getParentBlock()) { - SILFunction *F = Parent->getParent(); - if (FunctionInfo *FInfo = Function2Info.lookup(F)) { - if (FInfo->isValid()) { - FInfo->Graph.removeFromGraph(value); - FInfo->SummaryGraph.removeFromGraph(value); - } - } - } -} - void EscapeAnalysis::verify() const { #ifndef NDEBUG for (auto Iter : Function2Info) { diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index c6b28cd057f51..cb333baa62ee6 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -15,6 +15,8 @@ #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/SILVisitor.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/BasicBlockBits.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/EscapeAnalysis.h" #include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" @@ -23,11 +25,6 @@ using namespace swift; -// The MemoryBehavior Cache must not grow beyond this size. -// We limit the size of the MB cache to 2**14 because we want to limit the -// memory usage of this cache. -static const int MemoryBehaviorAnalysisMaxCacheSize = 16384; - //===----------------------------------------------------------------------===// // Memory Behavior Implementation //===----------------------------------------------------------------------===// @@ -367,7 +364,6 @@ static bool hasEscapingUses(SILValue address, int &numChecks) { return true; switch (user->getKind()) { - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::FixLifetimeInst: case SILInstructionKind::LoadInst: case SILInstructionKind::StoreInst: @@ -377,6 +373,10 @@ static bool hasEscapingUses(SILValue address, int &numChecks) { case SILInstructionKind::EndAccessInst: // Those instructions have no result and cannot escape the address. break; + case SILInstructionKind::DebugValueInst: + if (DebugValueInst::hasAddrVal(user)) + break; + return true; case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: @@ -518,39 +518,162 @@ visitBeginCOWMutationInst(BeginCOWMutationInst *BCMI) { MemBehavior AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V) { - MemBehaviorKeyTy Key = toMemoryBehaviorKey(Inst, V); + MemBehaviorCacheKey Key = {V, Inst}; // Check if we've already computed this result. auto It = MemoryBehaviorCache.find(Key); if (It != MemoryBehaviorCache.end()) { return It->second; } - // Flush the cache if the size of the cache is too large. - if (MemoryBehaviorCache.size() > MemoryBehaviorAnalysisMaxCacheSize) { - MemoryBehaviorCache.clear(); - } - // Calculate the aliasing result and store it in the cache. auto Result = computeMemoryBehaviorInner(Inst, V); MemoryBehaviorCache[Key] = Result; return Result; } +/// If \p V is an address of an immutable memory, return the begin of the +/// scope where the memory can be considered to be immutable. +/// +/// This is either a ``begin_access [read]`` in case V is the result of the +/// begin_access or a projection of it. +/// Or it is the begin of a borrow scope (begin_borrow, load_borrow, a +/// guaranteed function argument) of an immutable copy-on-write buffer. +/// For example: +/// %b = begin_borrow %array_buffer +/// %V = ref_element_addr [immutable] %b : $BufferType, #BufferType.someField +/// +static SILValue getBeginScopeInst(SILValue V) { + SILValue accessScope = getAccessScope(V); + if (auto *access = dyn_cast(accessScope)) { + if (access->getAccessKind() == SILAccessKind::Read && + access->getEnforcement() != SILAccessEnforcement::Unsafe) + return access; + return SILValue(); + } + SILValue accessBase = getAccessBase(V); + SILValue object; + if (auto *elementAddr = dyn_cast(accessBase)) { + if (!elementAddr->isImmutable()) + return SILValue(); + object = elementAddr->getOperand(); + } else if (auto *tailAddr = dyn_cast(accessBase)) { + if (!tailAddr->isImmutable()) + return SILValue(); + object = tailAddr->getOperand(); + } else { + return SILValue(); + } + if (BorrowedValue borrowedObj = getSingleBorrowIntroducingValue(object)) { + return borrowedObj.value; + } + return SILValue(); +} + +/// Collect all instructions which are inside an immutable scope. +/// +/// The \p beginScopeInst is either a ``begin_access [read]`` or the begin of a +/// borrow scope (begin_borrow, load_borrow) of an immutable copy-on-write +/// buffer. +void AliasAnalysis::computeImmutableScope(SingleValueInstruction *beginScopeInst) { + BasicBlockSet visitedBlocks(beginScopeInst->getFunction()); + llvm::SmallVector, 16> workList; + + auto addEndScopeInst = [&](SILInstruction *endScope) { + workList.push_back({endScope, endScope->getParent()}); + bool isNew = visitedBlocks.insert(endScope->getParent()); + (void)isNew; + assert(isNew); + }; + + // First step: add all scope-ending instructions to the worklist. + if (auto *beginAccess = dyn_cast(beginScopeInst)) { + for (EndAccessInst *endAccess : beginAccess->getEndAccesses()) { + addEndScopeInst(endAccess); + } + } else { + visitTransitiveEndBorrows(BorrowedValue(beginScopeInst), addEndScopeInst); + } + + // Second step: walk up the control flow until the beginScopeInst and add + // all (potentially) memory writing instructions to instsInImmutableScopes. + while (!workList.empty()) { + auto instAndBlock = workList.pop_back_val(); + SILBasicBlock *block = instAndBlock.second; + // If the worklist entry doesn't have an instruction, start at the end of + // the block. + auto iter = instAndBlock.first ? instAndBlock.first->getIterator() + : block->end(); + // Walk up the instruction list - either to the begin of the block or until + // we hit the beginScopeInst. + while (true) { + if (iter == block->begin()) { + assert(block != block->getParent()->getEntryBlock() && + "didn't find the beginScopeInst when walking up the CFG"); + // Add all predecessor blocks to the worklist. + for (SILBasicBlock *pred : block->getPredecessorBlocks()) { + if (visitedBlocks.insert(pred)) + workList.push_back({nullptr, pred}); + } + break; + } + --iter; + SILInstruction *inst = &*iter; + if (inst == beginScopeInst) { + // When we are at the beginScopeInst we terminate the CFG walk. + break; + } + if (inst->mayWriteToMemory()) { + instsInImmutableScopes.insert({beginScopeInst, inst}); + } + } + } +} + +/// Returns true if \p inst is in an immutable scope of V. +/// +/// That means that even if we don't know anything about inst, we can be sure +/// that inst cannot write to V. +/// An immutable scope is for example a read-only begin_access/end_access scope. +/// Another example is a borrow scope of an immutable copy-on-write buffer. +bool AliasAnalysis::isInImmutableScope(SILInstruction *inst, SILValue V) { + if (!V->getType().isAddress()) + return false; + + SILValue beginScope = getBeginScopeInst(V); + if (!beginScope) + return false; + + if (auto *funcArg = dyn_cast(beginScope)) { + // The immutable scope (= an guaranteed argument) spans over the whole + // function. We don't need to do any scope computation in this case. + assert(funcArg->getArgumentConvention().isGuaranteedConvention()); + return true; + } + + auto *beginScopeInst = dyn_cast(beginScope); + if (!beginScopeInst) + return false; + + // Recompute the scope if not done yet. + if (immutableScopeComputed.insert(beginScopeInst).second) { + computeImmutableScope(beginScopeInst); + } + return instsInImmutableScopes.contains({beginScopeInst, inst}); +} + MemBehavior AliasAnalysis::computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V) { LLVM_DEBUG(llvm::dbgs() << "GET MEMORY BEHAVIOR FOR:\n " << *Inst << " " << *V); assert(SEA && "SideEffectsAnalysis must be initialized!"); - return MemoryBehaviorVisitor(this, SEA, EA, V).visit(Inst); -} - -MemBehaviorKeyTy AliasAnalysis::toMemoryBehaviorKey(SILInstruction *V1, - SILValue V2) { - size_t idx1 = InstructionToIndex.getIndex(V1); - assert(idx1 != std::numeric_limits::max() && - "~0 index reserved for empty/tombstone keys"); - size_t idx2 = ValueToIndex.getIndex(V2); - assert(idx2 != std::numeric_limits::max() && - "~0 index reserved for empty/tombstone keys"); - return {idx1, idx2}; + + MemBehavior result = MemoryBehaviorVisitor(this, SEA, EA, V).visit(Inst); + + // If the "regular" alias analysis thinks that Inst may modify V, check if + // Inst is in an immutable scope of V. + if (result > MemBehavior::MayRead && isInImmutableScope(Inst, V)) { + return (result == MemBehavior::MayWrite) ? MemBehavior::None + : MemBehavior::MayRead; + } + return result; } diff --git a/lib/SILOptimizer/CMakeLists.txt b/lib/SILOptimizer/CMakeLists.txt index c9ddd901ab5dc..1f34ce6e44d36 100644 --- a/lib/SILOptimizer/CMakeLists.txt +++ b/lib/SILOptimizer/CMakeLists.txt @@ -1,8 +1,8 @@ -set_swift_llvm_is_available() add_swift_host_library(swiftSILOptimizer STATIC SILOptimizer.cpp) target_link_libraries(swiftSILOptimizer PRIVATE swiftSIL) +set_swift_llvm_is_available(swiftSILOptimizer) add_subdirectory(ARC) add_subdirectory(Analysis) diff --git a/lib/SILOptimizer/Differentiation/Common.cpp b/lib/SILOptimizer/Differentiation/Common.cpp index 7f016db2d6dcb..7190475bd0acb 100644 --- a/lib/SILOptimizer/Differentiation/Common.cpp +++ b/lib/SILOptimizer/Differentiation/Common.cpp @@ -259,10 +259,6 @@ findDebugLocationAndVariable(SILValue originalValue) { return dvi->getVarInfo().map([&](SILDebugVariable var) { return std::make_pair(dvi->getDebugLocation(), var); }); - if (auto *dvai = dyn_cast(use->getUser())) - return dvai->getVarInfo().map([&](SILDebugVariable var) { - return std::make_pair(dvai->getDebugLocation(), var); - }); } return None; } diff --git a/lib/SILOptimizer/Differentiation/JVPCloner.cpp b/lib/SILOptimizer/Differentiation/JVPCloner.cpp index a3adb47ee4e23..d221c72905d92 100644 --- a/lib/SILOptimizer/Differentiation/JVPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/JVPCloner.cpp @@ -1615,9 +1615,7 @@ void JVPCloner::Implementation::prepareForDifferentialGeneration() { // signature: when witness generic signature has same-type requirements // binding all generic parameters to concrete types, JVP function type uses // all the concrete types and JVP generic signature is null. - CanGenericSignature witnessCanGenSig; - if (auto witnessGenSig = witness->getDerivativeGenericSignature()) - witnessCanGenSig = witnessGenSig->getCanonicalSignature(); + auto witnessCanGenSig = witness->getDerivativeGenericSignature().getCanonicalSignature(); auto lookupConformance = LookUpConformanceInModule(module.getSwiftModule()); // Parameters of the differential are: @@ -1693,8 +1691,7 @@ void JVPCloner::Implementation::prepareForDifferentialGeneration() { // binding all generic parameters to concrete types. auto diffGenericSig = jvp->getLoweredFunctionType()->getSubstGenericSignature(); - auto *diffGenericEnv = - diffGenericSig ? diffGenericSig->getGenericEnvironment() : nullptr; + auto *diffGenericEnv = diffGenericSig.getGenericEnvironment(); auto diffType = SILFunctionType::get( diffGenericSig, SILExtInfo::getThin(), origTy->getCoroutineKind(), origTy->getCalleeConvention(), dfParams, {}, dfResults, None, diff --git a/lib/SILOptimizer/Differentiation/LinearMapInfo.cpp b/lib/SILOptimizer/Differentiation/LinearMapInfo.cpp index 2db62373d2b7c..d042cfc87da79 100644 --- a/lib/SILOptimizer/Differentiation/LinearMapInfo.cpp +++ b/lib/SILOptimizer/Differentiation/LinearMapInfo.cpp @@ -37,7 +37,7 @@ static GenericParamList *cloneGenericParameters(ASTContext &ctx, DeclContext *dc, CanGenericSignature sig) { SmallVector clonedParams; - for (auto paramType : sig->getGenericParams()) { + for (auto paramType : sig.getGenericParams()) { auto clonedParam = new (ctx) GenericTypeParamDecl(dc, paramType->getName(), SourceLoc(), paramType->getDepth(), paramType->getIndex()); @@ -374,7 +374,7 @@ void LinearMapInfo::generateDifferentiationDataStructures( CanGenericSignature derivativeFnGenSig = nullptr; if (auto *derivativeFnGenEnv = derivativeFn->getGenericEnvironment()) derivativeFnGenSig = - derivativeFnGenEnv->getGenericSignature()->getCanonicalSignature(); + derivativeFnGenEnv->getGenericSignature().getCanonicalSignature(); // Create linear map struct for each original block. for (auto &origBB : *original) { diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index a2da23f921c3a..e4ab8f3947af2 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -1676,7 +1676,6 @@ class PullbackCloner::Implementation final // Debugging/reference counting instructions. NO_ADJOINT(DebugValue) - NO_ADJOINT(DebugValueAddr) NO_ADJOINT(RetainValue) NO_ADJOINT(RetainValueAddr) NO_ADJOINT(ReleaseValue) @@ -2052,6 +2051,8 @@ bool PullbackCloner::Implementation::run() { SmallVector retElts; // This vector will contain all indirect parameter adjoint buffers. SmallVector indParamAdjoints; + // This vector will identify the locations where initialization is needed. + SmallBitVector outputsToInitialize; auto conv = getOriginal().getConventions(); auto origParams = getOriginal().getArgumentsWithoutIndirectResults(); @@ -2071,25 +2072,62 @@ bool PullbackCloner::Implementation::run() { case SILValueCategory::Address: { auto adjBuf = getAdjointBuffer(origEntry, origParam); indParamAdjoints.push_back(adjBuf); + outputsToInitialize.push_back( + !conv.getParameters()[parameterIndex].isIndirectMutating()); break; } } }; + SmallVector pullbackIndirectResults( + getPullback().getIndirectResults().begin(), + getPullback().getIndirectResults().end()); + // Collect differentiation parameter adjoints. + // Do a first pass to collect non-inout values. + unsigned pullbackInoutArgumentIndex = 0; + for (auto i : getConfig().parameterIndices->getIndices()) { + auto isParameterInout = conv.getParameters()[i].isIndirectMutating(); + if (!isParameterInout) { + addRetElt(i); + } + } + + // Do a second pass for all inout parameters. for (auto i : getConfig().parameterIndices->getIndices()) { - // Skip `inout` parameters. - if (conv.getParameters()[i].isIndirectMutating()) + // Skip non-inout parameters. + auto isParameterInout = conv.getParameters()[i].isIndirectMutating(); + if (!isParameterInout) continue; + + // Skip `inout` parameters for functions with a single basic block: + // adjoint accumulation for those parameters is already done by + // per-instruction visitors. + if (getOriginal().size() == 1) + continue; + + // For functions with multiple basic blocks, accumulation is needed + // for `inout` parameters because pullback basic blocks have different + // adjoint buffers. + auto pullbackInoutArgument = + getPullback() + .getArgumentsWithoutIndirectResults()[pullbackInoutArgumentIndex++]; + pullbackIndirectResults.push_back(pullbackInoutArgument); addRetElt(i); } // Copy them to adjoint indirect results. - assert(indParamAdjoints.size() == getPullback().getIndirectResults().size() && + assert(indParamAdjoints.size() == pullbackIndirectResults.size() && "Indirect parameter adjoint count mismatch"); - for (auto pair : zip(indParamAdjoints, getPullback().getIndirectResults())) { + unsigned currentIndex = 0; + for (auto pair : zip(indParamAdjoints, pullbackIndirectResults)) { auto source = std::get<0>(pair); auto *dest = std::get<1>(pair); - builder.createCopyAddr(pbLoc, source, dest, IsTake, IsInitialization); + if (outputsToInitialize[currentIndex]) { + builder.createCopyAddr(pbLoc, source, dest, IsTake, IsInitialization); + } else { + builder.createCopyAddr(pbLoc, source, dest, IsTake, IsNotInitialization); + } + currentIndex++; // Prevent source buffer from being deallocated, since the underlying // value is moved. destroyedLocalAllocations.insert(source); diff --git a/lib/SILOptimizer/Differentiation/Thunk.cpp b/lib/SILOptimizer/Differentiation/Thunk.cpp index b829502deae8a..41b6d4bbb61aa 100644 --- a/lib/SILOptimizer/Differentiation/Thunk.cpp +++ b/lib/SILOptimizer/Differentiation/Thunk.cpp @@ -61,7 +61,7 @@ CanGenericSignature buildThunkSignature(SILFunction *fn, bool inheritGenericSig, if (auto genericSig = fn->getLoweredFunctionType()->getSubstGenericSignature()) { builder.addGenericSignature(genericSig); - depth = genericSig->getGenericParams().back()->getDepth() + 1; + depth = genericSig.getGenericParams().back()->getDepth() + 1; } } @@ -77,7 +77,7 @@ CanGenericSignature buildThunkSignature(SILFunction *fn, bool inheritGenericSig, auto genericSig = std::move(builder).computeGenericSignature( /*allowConcreteGenericParams=*/true); - genericEnv = genericSig->getGenericEnvironment(); + genericEnv = genericSig.getGenericEnvironment(); newArchetype = genericEnv->mapTypeIntoContext(newGenericParam)->castTo(); @@ -104,7 +104,7 @@ CanGenericSignature buildThunkSignature(SILFunction *fn, bool inheritGenericSig, }, MakeAbstractConformanceForGenericType()); - return genericSig->getCanonicalSignature(); + return genericSig.getCanonicalSignature(); } CanSILFunctionType buildThunkType(SILFunction *fn, @@ -324,7 +324,7 @@ SILFunction *getOrCreateReabstractionThunk(SILOptFunctionBuilder &fb, Mangle::ASTMangler mangler; std::string name = mangler.mangleReabstractionThunkHelper( - thunkType, fromInterfaceType, toInterfaceType, Type(), + thunkType, fromInterfaceType, toInterfaceType, Type(), Type(), module.getSwiftModule()); auto *thunk = fb.getOrCreateSharedFunction( @@ -610,7 +610,8 @@ getOrCreateSubsetParametersThunkForLinearMap( }; // Build a `.zero` argument for the given `Differentiable`-conforming type. - auto buildZeroArgument = [&](SILType zeroSILType) { + auto buildZeroArgument = [&](SILParameterInfo zeroSILParameter) { + auto zeroSILType = zeroSILParameter.getSILStorageInterfaceType(); auto zeroSILObjType = zeroSILType.getObjectType(); auto zeroType = zeroSILType.getASTType(); auto *swiftMod = parentThunk->getModule().getSwiftModule(); @@ -623,13 +624,17 @@ getOrCreateSubsetParametersThunkForLinearMap( localAllocations.push_back(buf); builder.emitZeroIntoBuffer(loc, buf, IsInitialization); if (zeroSILType.isAddress()) { - valuesToCleanup.push_back(buf); arguments.push_back(buf); + if (zeroSILParameter.isGuaranteed()) { + valuesToCleanup.push_back(buf); + } } else { auto arg = builder.emitLoadValueOperation(loc, buf, LoadOwnershipQualifier::Take); - valuesToCleanup.push_back(arg); arguments.push_back(arg); + if (zeroSILParameter.isGuaranteed()) { + valuesToCleanup.push_back(arg); + } } break; } @@ -687,10 +692,9 @@ getOrCreateSubsetParametersThunkForLinearMap( } // Otherwise, construct and use a zero argument. else { - auto zeroSILType = - linearMapType->getParameters()[mapOriginalParameterIndex(i)] - .getSILStorageInterfaceType(); - buildZeroArgument(zeroSILType); + auto zeroSILParameter = + linearMapType->getParameters()[mapOriginalParameterIndex(i)]; + buildZeroArgument(zeroSILParameter); } } break; diff --git a/lib/SILOptimizer/Differentiation/VJPCloner.cpp b/lib/SILOptimizer/Differentiation/VJPCloner.cpp index b1d926dbf32de..840330d7230fb 100644 --- a/lib/SILOptimizer/Differentiation/VJPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/VJPCloner.cpp @@ -823,9 +823,7 @@ SILFunction *VJPCloner::Implementation::createEmptyPullback() { // signature: when witness generic signature has same-type requirements // binding all generic parameters to concrete types, VJP function type uses // all the concrete types and VJP generic signature is null. - CanGenericSignature witnessCanGenSig; - if (auto witnessGenSig = witness->getDerivativeGenericSignature()) - witnessCanGenSig = witnessGenSig->getCanonicalSignature(); + auto witnessCanGenSig = witness->getDerivativeGenericSignature().getCanonicalSignature(); auto lookupConformance = LookUpConformanceInModule(module.getSwiftModule()); // Given a type, returns its formal SIL parameter info. @@ -985,8 +983,7 @@ SILFunction *VJPCloner::Implementation::createEmptyPullback() { // Do not use witness generic signature, which may have same-type requirements // binding all generic parameters to concrete types. auto pbGenericSig = vjp->getLoweredFunctionType()->getSubstGenericSignature(); - auto *pbGenericEnv = - pbGenericSig ? pbGenericSig->getGenericEnvironment() : nullptr; + auto *pbGenericEnv = pbGenericSig.getGenericEnvironment(); auto pbType = SILFunctionType::get( pbGenericSig, SILExtInfo::getThin(), origTy->getCoroutineKind(), origTy->getCalleeConvention(), pbParams, {}, adjResults, None, diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index 020cc58fe8083..eba35c8816a04 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -292,7 +292,7 @@ void ExistentialTransform::convertExistentialArgTypesToGenericArgTypes( /// Determine the existing generic parameter depth. int Depth = 0; if (OrigGenericSig != nullptr) { - Depth = OrigGenericSig->getGenericParams().back()->getDepth() + 1; + Depth = OrigGenericSig.getGenericParams().back()->getDepth() + 1; } /// Index of the Generic Parameter. @@ -526,7 +526,7 @@ void ExistentialTransform::populateThunkBody() { unsigned int OrigDepth = 0; if (F->getLoweredFunctionType()->isPolymorphic()) { - OrigDepth = OrigCalleeGenericSig->getGenericParams().back()->getDepth() + 1; + OrigDepth = OrigCalleeGenericSig.getGenericParams().back()->getDepth() + 1; } SubstitutionMap OrigSubMap = F->getForwardingSubstitutionMap(); @@ -628,7 +628,7 @@ void ExistentialTransform::createExistentialSpecializedFunction() { NewF = CachedFn; } else { auto NewFGenericSig = NewFTy->getInvocationGenericSignature(); - auto NewFGenericEnv = NewFGenericSig->getGenericEnvironment(); + auto NewFGenericEnv = NewFGenericSig.getGenericEnvironment(); SILLinkage linkage = getSpecializedLinkage(F, F->getLinkage()); NewF = FunctionBuilder.createFunction( diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index 46a569095c580..f270b84c4aaa5 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -57,6 +57,7 @@ #define DEBUG_TYPE "closure-specialization" #include "swift/Basic/Range.h" +#include "swift/Demangling/Demangler.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILFunction.h" @@ -162,6 +163,14 @@ class ClosureSpecCloner : public SILClonerWithScopes { namespace { struct ClosureInfo; +static SILFunction *getClosureCallee(SILInstruction *inst) { + if (auto *PAI = dyn_cast(inst)) + return cast(PAI->getCallee())->getReferencedFunction(); + + auto *TTTFI = cast(inst); + return cast(TTTFI->getCallee())->getReferencedFunction(); +} + class CallSiteDescriptor { ClosureInfo *CInfo; FullApplySite AI; @@ -188,11 +197,7 @@ class CallSiteDescriptor { } SILFunction *getClosureCallee() const { - if (auto *PAI = dyn_cast(getClosure())) - return cast(PAI->getCallee())->getReferencedFunction(); - - auto *TTTFI = cast(getClosure()); - return cast(TTTFI->getCallee())->getReferencedFunction(); + return ::getClosureCallee(getClosure()); } bool closureHasRefSemanticContext() const { @@ -1065,6 +1070,59 @@ static bool canSpecializeFullApplySite(FullApplySiteKind kind) { llvm_unreachable("covered switch"); } +static int getSpecializationLevelRecursive(StringRef funcName, Demangler &parent) { + using namespace Demangle; + + Demangler demangler; + demangler.providePreallocatedMemory(parent); + + // Check for this kind of node tree: + // + // kind=Global + // kind=FunctionSignatureSpecialization + // kind=SpecializationPassID, index=1 + // kind=FunctionSignatureSpecializationParam + // kind=FunctionSignatureSpecializationParamKind, index=5 + // kind=FunctionSignatureSpecializationParamPayload, text="..." + // + Node *root = demangler.demangleSymbol(funcName); + if (!root) + return 0; + if (root->getKind() != Node::Kind::Global) + return 0; + Node *funcSpec = root->getFirstChild(); + if (!funcSpec || funcSpec->getNumChildren() < 2) + return 0; + if (funcSpec->getKind() != Node::Kind::FunctionSignatureSpecialization) + return 0; + Node *param = funcSpec->getChild(1); + if (param->getKind() != Node::Kind::FunctionSignatureSpecializationParam) + return 0; + if (param->getNumChildren() < 2) + return 0; + Node *kindNd = param->getChild(0); + if (kindNd->getKind() != Node::Kind::FunctionSignatureSpecializationParamKind) + return 0; + auto kind = FunctionSigSpecializationParamKind(kindNd->getIndex()); + if (kind != FunctionSigSpecializationParamKind::ConstantPropFunction) + return 0; + + Node *payload = param->getChild(1); + if (payload->getKind() != Node::Kind::FunctionSignatureSpecializationParamPayload) + return 1; + // Check if the specialized function is a specialization itself. + return 1 + getSpecializationLevelRecursive(payload->getText(), demangler); +} + +/// If \p function is a function-signature specialization for a constant- +/// propagated function argument, returns 1. +/// If \p function is a specialization of such a specialization, returns 2. +/// And so on. +static int getSpecializationLevel(SILFunction *f) { + Demangle::StackAllocatedDemangler<1024> demangler; + return getSpecializationLevelRecursive(f->getName(), demangler); +} + bool SILClosureSpecializerTransform::gatherCallSites( SILFunction *Caller, llvm::SmallVectorImpl> &ClosureCandidates, @@ -1252,6 +1310,24 @@ bool SILClosureSpecializerTransform::gatherCallSites( continue; } + // Avoid an infinite specialization loop caused by repeated runs of + // ClosureSpecializer and CapturePropagation. + // CapturePropagation propagates constant function-literals. Such + // function specializations can then be optimized again by the + // ClosureSpecializer and so on. + // This happens if a closure argument is called _and_ referenced in + // another closure, which is passed to a recursive call. E.g. + // + // func foo(_ c: @escaping () -> ()) { + // c() + // foo({ c() }) + // } + // + // A limit of 2 is good enough and will not be exceed in "regular" + // optimization scenarios. + if (getSpecializationLevel(getClosureCallee(ClosureInst)) > 2) + continue; + // Compute the final release points of the closure. We will insert // release of the captured arguments here. if (!CInfo) diff --git a/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp b/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp index 90e6895fba47e..fde588c301a3f 100644 --- a/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp +++ b/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp @@ -386,6 +386,11 @@ bool CrossModuleSerializationSetup::canUseFromInline(SILFunction *func, if (!func) return false; + if (DeclContext *funcCtxt = func->getDeclContext()) { + if (!M.getSwiftModule()->canBeUsedForCrossModuleOptimization(funcCtxt)) + return false; + } + switch (func->getLinkage()) { case SILLinkage::PublicNonABI: return func->isSerialized() != IsNotSerialized; diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index f9eadde1c203c..5ccf621c52f1d 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -457,8 +457,9 @@ bool SILGlobalOpt::optimizeInitializer(SILFunction *AddrF, // Remove "once" call from the addressor. removeToken(CallToOnce->getOperand(0)); - eraseUsesOfInstruction(CallToOnce); - recursivelyDeleteTriviallyDeadInstructions(CallToOnce, true); + InstructionDeleter deleter; + deleter.forceDeleteWithUsers(CallToOnce); + deleter.cleanupDeadInstructions(); // Create the constant initializer of the global variable. StaticInitCloner::appendToInitializer(SILG, InitVal); @@ -818,11 +819,15 @@ bool SILGlobalOpt::run() { for (auto &allocPair : globalAllocPairs) { HasChanged |= tryRemoveGlobalAlloc(allocPair.first, allocPair.second); } - - // Erase the instructions that we have marked for deletion. - for (auto *inst : InstToRemove) { - eraseUsesOfInstruction(inst); - inst->eraseFromParent(); + if (HasChanged) { + // Erase the instructions that we have marked for deletion. + InstructionDeleter deleter; + for (auto *inst : InstToRemove) { + deleter.forceDeleteWithUsers(inst); + } + deleter.cleanupDeadInstructions(); + } else { + assert(InstToRemove.empty()); } for (auto &global : Module->getSILGlobals()) { diff --git a/lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp b/lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp index 62857c4c6905c..d6a1930ac3798 100644 --- a/lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp @@ -219,7 +219,6 @@ bool GlobalPropertyOpt::canAddressEscape(SILValue V, bool acceptStore) { // These instructions do not cause the address to escape. if (isa(User) || isa(User) || - isa(User) || isa(User) || isa(User) || isa(User) || diff --git a/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp b/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp index 8ff3d211c6b39..95731db808bf2 100644 --- a/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp +++ b/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp @@ -273,6 +273,8 @@ void LetPropertiesOpt::optimizeLetPropertyAccess(VarDecl *Property, return; } + InstructionDeleter deleter; + auto &Loads = AccessMap[Property]; unsigned NumReplaced = 0; @@ -302,12 +304,12 @@ void LetPropertiesOpt::optimizeLetPropertyAccess(VarDecl *Property, continue; replaceLoadSequence(User, clonedInit); - eraseUsesOfInstruction(User); - User->eraseFromParent(); + deleter.forceDeleteWithUsers(User); ++NumReplaced; } ChangedFunctions.insert(F); } + deleter.cleanupDeadInstructions(); LLVM_DEBUG(llvm::dbgs() << "Access to " << *Property << " was replaced " << NumReplaced << " time(s)\n"); diff --git a/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp b/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp index 03e211d45ec7a..480ce70d6855b 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp @@ -864,6 +864,10 @@ static void reportBoundsChecks(SILFunction *F) { #endif namespace { + +// Should be more than enough to cover "usual" functions. +static constexpr int maxRecursionDepth = 500; + /// Remove redundant checks in basic blocks and hoist redundant checks out of /// loops. class ABCOpt : public SILFunctionTransform { @@ -885,13 +889,15 @@ class ABCOpt : public SILFunctionTransform { /// Walk down the dominator tree inside the loop, removing redundant checks. bool removeRedundantChecksInLoop(DominanceInfoNode *CurBB, ABCAnalysis &ABC, IndexedArraySet &DominatingSafeChecks, - SILLoop *Loop); + SILLoop *Loop, + int recursionDepth); /// Analyze the loop for arrays that are not modified and perform dominator /// tree based redundant bounds check removal. bool hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC, InductionAnalysis &IndVars, SILBasicBlock *Preheader, SILBasicBlock *Header, - SILBasicBlock *SingleExitingBlk); + SILBasicBlock *SingleExitingBlk, + int recursionDepth); public: void run() override { @@ -1049,10 +1055,16 @@ bool ABCOpt::removeRedundantChecksInBlock(SILBasicBlock &BB) { bool ABCOpt::removeRedundantChecksInLoop(DominanceInfoNode *CurBB, ABCAnalysis &ABC, IndexedArraySet &DominatingSafeChecks, - SILLoop *Loop) { + SILLoop *Loop, + int recursionDepth) { auto *BB = CurBB->getBlock(); if (!Loop->contains(BB)) return false; + + // Avoid a stack overflow for very deep dominator trees. + if (recursionDepth >= maxRecursionDepth) + return false; + bool Changed = false; // When we come back from the dominator tree recursion we need to remove @@ -1106,7 +1118,8 @@ bool ABCOpt::removeRedundantChecksInLoop(DominanceInfoNode *CurBB, // Traverse the children in the dominator tree inside the loop. for (auto Child : *CurBB) Changed |= - removeRedundantChecksInLoop(Child, ABC, DominatingSafeChecks, Loop); + removeRedundantChecksInLoop(Child, ABC, DominatingSafeChecks, Loop, + recursionDepth + 1); // Remove checks we have seen for the first time. std::for_each(SafeChecksToPop.begin(), SafeChecksToPop.end(), @@ -1149,7 +1162,8 @@ bool ABCOpt::processLoop(SILLoop *Loop) { // check for safety outside the loop (with ABCAnalysis). IndexedArraySet DominatingSafeChecks; bool Changed = removeRedundantChecksInLoop(DT->getNode(Header), ABC, - DominatingSafeChecks, Loop); + DominatingSafeChecks, Loop, + /*recursionDepth*/ 0); if (!EnableABCHoisting) return Changed; @@ -1255,7 +1269,7 @@ bool ABCOpt::processLoop(SILLoop *Loop) { // Hoist bounds checks. Changed |= hoistChecksInLoop(DT->getNode(Header), ABC, IndVars, Preheader, - Header, SingleExitingBlk); + Header, SingleExitingBlk, /*recursionDepth*/ 0); if (Changed) { Preheader->getParent()->verify(); } @@ -1265,7 +1279,12 @@ bool ABCOpt::processLoop(SILLoop *Loop) { bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC, InductionAnalysis &IndVars, SILBasicBlock *Preheader, SILBasicBlock *Header, - SILBasicBlock *SingleExitingBlk) { + SILBasicBlock *SingleExitingBlk, + int recursionDepth) { + // Avoid a stack overflow for very deep dominator trees. + if (recursionDepth >= maxRecursionDepth) + return false; + bool Changed = false; auto *CurBB = DTNode->getBlock(); bool blockAlwaysExecutes = @@ -1364,7 +1383,7 @@ bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC, // Traverse the children in the dominator tree. for (auto Child : *DTNode) Changed |= hoistChecksInLoop(Child, ABC, IndVars, Preheader, Header, - SingleExitingBlk); + SingleExitingBlk, recursionDepth + 1); return Changed; } diff --git a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp index 2971a255c3904..70e4ae33723da 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp @@ -53,6 +53,7 @@ #define DEBUG_TYPE "array-property-opt" #include "ArrayOpt.h" +#include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/Analysis/LoopAnalysis.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" diff --git a/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp b/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp index 55afe6222f876..16eb4c7d07ec9 100644 --- a/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp +++ b/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp @@ -635,7 +635,7 @@ class ForEachLoopUnroller : public SILFunctionTransform { } if (changed) { - deleter.cleanUpDeadInstructions(); + deleter.cleanupDeadInstructions(); PM->invalidateAnalysis(&fun, SILAnalysis::InvalidationKind::FunctionBody); } diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index b73da1f4b685b..a7b101e3872d6 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -939,7 +939,8 @@ void LoopTreeOptimization::analyzeCurrentLoop( // how to rematerialize global_addr, then we don't need this base. auto access = AccessPathWithBase::compute(SI->getDest()); auto accessPath = access.accessPath; - if (accessPath.isValid() && isLoopInvariant(access.base, Loop)) { + if (accessPath.isValid() && + (access.base && isLoopInvariant(access.base, Loop))) { if (isOnlyLoadedAndStored(AA, sideEffects, Loads, Stores, SI->getDest(), accessPath)) { if (!LoadAndStoreAddrs.count(accessPath)) { @@ -1467,7 +1468,7 @@ class LICM : public SILFunctionTransform { DominanceAnalysis *DA = PM->getAnalysis(); PostDominanceAnalysis *PDA = PM->getAnalysis(); - AliasAnalysis *AA = PM->getAnalysis(); + AliasAnalysis *AA = PM->getAnalysis(F); SideEffectAnalysis *SEA = PM->getAnalysis(); AccessedStorageAnalysis *ASA = getAnalysis(); DominanceInfo *DomTree = nullptr; diff --git a/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp b/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp index ab574e1f689e2..1b37cb62b7f61 100644 --- a/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp +++ b/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp @@ -53,8 +53,9 @@ struct AccessMarkerElimination { } SILBasicBlock::iterator eraseInst(SILInstruction *inst) { + auto nextIter = std::next(inst->getIterator()); notifyErased(inst); - return inst->getParent()->erase(inst); + return nextIter; }; bool shouldPreserveAccess(SILAccessEnforcement enforcement); diff --git a/lib/SILOptimizer/Mandatory/CapturePromotion.cpp b/lib/SILOptimizer/Mandatory/CapturePromotion.cpp index 479ce6c8c739c..67686f606930e 100644 --- a/lib/SILOptimizer/Mandatory/CapturePromotion.cpp +++ b/lib/SILOptimizer/Mandatory/CapturePromotion.cpp @@ -313,7 +313,7 @@ class ClosureCloner : public SILClonerWithScopes { SILValue getProjectBoxMappedVal(SILValue operandValue); - void visitDebugValueAddrInst(DebugValueAddrInst *inst); + void visitDebugValueInst(DebugValueInst *inst); void visitDestroyValueInst(DestroyValueInst *inst); void visitStructElementAddrInst(StructElementAddrInst *inst); void visitLoadInst(LoadInst *inst); @@ -570,16 +570,17 @@ SILValue ClosureCloner::getProjectBoxMappedVal(SILValue operandValue) { return SILValue(); } -/// Handle a debug_value_addr instruction during cloning of a closure; -/// if its operand is the promoted address argument then lower it to a -/// debug_value, otherwise it is handled normally. -void ClosureCloner::visitDebugValueAddrInst(DebugValueAddrInst *inst) { - if (SILValue value = getProjectBoxMappedVal(inst->getOperand())) { - getBuilder().setCurrentDebugScope(getOpScope(inst->getDebugScope())); - getBuilder().createDebugValue(inst->getLoc(), value, *inst->getVarInfo()); - return; - } - SILCloner::visitDebugValueAddrInst(inst); +/// Handle a debug_value instruction during cloning of a closure; +/// if its operand is the promoted address argument then lower it to +/// another debug_value, otherwise it is handled normally. +void ClosureCloner::visitDebugValueInst(DebugValueInst *inst) { + if (inst->hasAddrVal()) + if (SILValue value = getProjectBoxMappedVal(inst->getOperand())) { + getBuilder().setCurrentDebugScope(getOpScope(inst->getDebugScope())); + getBuilder().createDebugValue(inst->getLoc(), value, *inst->getVarInfo()); + return; + } + SILCloner::visitDebugValueInst(inst); } /// Handle a destroy_value instruction during cloning of a closure; if it is a @@ -854,7 +855,7 @@ getPartialApplyArgMutationsAndEscapes(PartialApplyInst *pai, return false; } - if (isa(addrUser) || + if (DebugValueInst::hasAddrVal(addrUser) || isa(addrUser) || isa(addrUser)) { return false; } diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index ca1e873cb8448..3b752fd6c84b1 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -208,7 +208,8 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue, /// that case where we have to consider that destroy_value, we have a simpler /// time here. static void extendLifetimeToEndOfFunction(SILFunction &fn, - ConvertEscapeToNoEscapeInst *cvt) { + ConvertEscapeToNoEscapeInst *cvt, + SILSSAUpdater &updater) { auto escapingClosure = cvt->getOperand(); auto escapingClosureTy = escapingClosure->getType(); auto optionalEscapingClosureTy = SILType::getOptionalType(escapingClosureTy); @@ -239,7 +240,7 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn, // use SILSSAUpdater::GetValueInMiddleOfBlock() to extend the object's // lifetime respecting loops. SmallVector insertedPhis; - SILSSAUpdater updater(&insertedPhis); + updater.setInsertedPhis(&insertedPhis); updater.initialize(optionalEscapingClosureTy, fn.hasOwnership() ? OwnershipKind::Owned : OwnershipKind::None); @@ -363,16 +364,16 @@ static SILValue insertMarkDependenceForCapturedArguments(PartialApplyInst *pai, return curr; } -/// Returns the (single) "endAsyncLet" builtin if \p startAsyncLet is a -/// "startAsyncLet" builtin. +/// Returns the (single) "endAsyncLetLifetime" builtin if \p startAsyncLet is a +/// "startAsyncLetWithLocalBuffer" builtin. static BuiltinInst *getEndAsyncLet(BuiltinInst *startAsyncLet) { - if (startAsyncLet->getBuiltinKind() != BuiltinValueKind::StartAsyncLet) + if (startAsyncLet->getBuiltinKind() != BuiltinValueKind::StartAsyncLetWithLocalBuffer) return nullptr; BuiltinInst *endAsyncLet = nullptr; for (Operand *op : startAsyncLet->getUses()) { auto *endBI = dyn_cast(op->getUser()); - if (endBI && endBI->getBuiltinKind() == BuiltinValueKind::EndAsyncLet) { + if (endBI && endBI->getBuiltinKind() == BuiltinValueKind::EndAsyncLetLifetime) { // At this stage of the pipeline, it's always the case that a // startAsyncLet has an endAsyncLet: that's how SILGen generates it. // Just to be on the safe side, do this check. @@ -398,7 +399,8 @@ static void insertAfterClosureUser(SILInstruction *closureUser, if (auto *startAsyncLet = dyn_cast(closureUser)) { BuiltinInst *endAsyncLet = getEndAsyncLet(startAsyncLet); - assert(endAsyncLet); + if (!endAsyncLet) + return; SILBuilderWithScope builder(std::next(endAsyncLet->getIterator())); insertAtNonUnreachable(builder); return; @@ -443,7 +445,7 @@ static SILValue skipConvert(SILValue v) { /// nesting. static SILValue tryRewriteToPartialApplyStack( ConvertEscapeToNoEscapeInst *cvt, - SILInstruction *closureUser, SILBasicBlock::iterator &advanceIfDelete, + SILInstruction *closureUser, InstructionDeleter &deleter, llvm::DenseMap &memoized) { auto *origPA = dyn_cast(skipConvert(cvt->getOperand())); @@ -457,10 +459,8 @@ static SILValue tryRewriteToPartialApplyStack( // Whenever we delete an instruction advance the iterator and remove the // instruction from the memoized map. auto saveDeleteInst = [&](SILInstruction *i) { - if (&*advanceIfDelete == i) - ++advanceIfDelete; memoized.erase(i); - i->eraseFromParent(); + deleter.forceDelete(i); }; // Look for a single non ref count user of the partial_apply. @@ -534,7 +534,7 @@ static SILValue tryRewriteToPartialApplyStack( SmallVector Uses(arg.get()->getUses()); for (auto use : Uses) if (auto *deallocInst = dyn_cast(use->getUser())) - deallocInst->eraseFromParent(); + deleter.forceDelete(deallocInst); } } @@ -552,7 +552,7 @@ static SILValue tryRewriteToPartialApplyStack( static bool tryExtendLifetimeToLastUse( ConvertEscapeToNoEscapeInst *cvt, llvm::DenseMap &memoized, - SILBasicBlock::iterator &advanceIfDelete) { + InstructionDeleter &deleter) { // If there is a single user that is an apply this is simple: extend the // lifetime of the operand until after the apply. auto *singleUser = lookThroughRebastractionUsers(cvt, memoized); @@ -574,7 +574,7 @@ static bool tryExtendLifetimeToLastUse( } if (SILValue closure = tryRewriteToPartialApplyStack(cvt, singleUser, - advanceIfDelete, memoized)) { + deleter, memoized)) { if (auto *cfi = dyn_cast(closure)) closure = cfi->getOperand(); if (endAsyncLet && isa(closure)) { @@ -587,7 +587,7 @@ static bool tryExtendLifetimeToLastUse( builder.createBuiltin(endAsyncLet->getLoc(), endAsyncLet->getName(), endAsyncLet->getType(), endAsyncLet->getSubstitutions(), {endAsyncLet->getOperand(0), closure}); - endAsyncLet->eraseFromParent(); + deleter.forceDelete(endAsyncLet); } return true; } @@ -797,6 +797,7 @@ static SILInstruction *getOnlyDestroy(CopyBlockWithoutEscapingInst *cb) { /// cond_fail %e /// destroy_value %closure static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb, + InstructionDeleter &deleter, bool &modifiedCFG) { // Find the end of the lifetime of the copy_block_without_escaping // instruction. @@ -837,7 +838,7 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb, SILBuilderWithScope b(cb); auto *newCB = b.createCopyBlock(loc, cb->getBlock()); cb->replaceAllUsesWith(newCB); - cb->eraseFromParent(); + deleter.forceDelete(cb); auto autoGenLoc = RegularLocation::getAutoGeneratedLocation(); @@ -977,14 +978,12 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting, llvm::DenseMap memoizedQueries; for (auto &block : fn) { - auto i = block.begin(); - while (i != block.end()) { - SILInstruction *inst = &*i; - ++i; + SILSSAUpdater updater; + for (SILInstruction *inst : updater.getDeleter().updatingRange(&block)) { // Handle, copy_block_without_escaping instructions. if (auto *cb = dyn_cast(inst)) { - if (fixupCopyBlockWithoutEscaping(cb, modifiedCFG)) { + if (fixupCopyBlockWithoutEscaping(cb, updater.getDeleter(), modifiedCFG)) { changed = true; } continue; @@ -1004,7 +1003,7 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting, } } - if (tryExtendLifetimeToLastUse(cvt, memoizedQueries, i)) { + if (tryExtendLifetimeToLastUse(cvt, memoizedQueries, updater.getDeleter())) { changed = true; checkStackNesting = true; continue; @@ -1012,7 +1011,7 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting, // Otherwise, extend the lifetime of the operand to the end of the // function. - extendLifetimeToEndOfFunction(fn, cvt); + extendLifetimeToEndOfFunction(fn, cvt, updater); changed = true; } } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 3e113b40d1b04..721f504d52cfe 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -93,13 +93,8 @@ static unsigned getElementCountRec(TypeExpansionContext context, } static std::pair -computeMemorySILType(MarkUninitializedInst *MemoryInst) { +computeMemorySILType(MarkUninitializedInst *MUI, SILValue Address) { // Compute the type of the memory object. - auto *MUI = MemoryInst; - SILValue Address = MUI; - if (auto *PBI = Address->getSingleUserOfType()) { - Address = PBI; - } SILType MemorySILType = Address->getType().getObjectType(); // If this is a let variable we're initializing, remember this so we don't @@ -118,7 +113,13 @@ DIMemoryObjectInfo::DIMemoryObjectInfo(MarkUninitializedInst *MI) : MemoryInst(MI) { auto &Module = MI->getModule(); - std::tie(MemorySILType, IsLet) = computeMemorySILType(MemoryInst); + SILValue Address = MemoryInst; + if (auto PBI = MemoryInst->getSingleUserOfType()) { + IsBox = true; + Address = PBI; + } + + std::tie(MemorySILType, IsLet) = computeMemorySILType(MI, Address); // Compute the number of elements to track in this memory object. // If this is a 'self' in a delegating initializer, we only track one bit: @@ -431,6 +432,23 @@ bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const { return false; } +ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const { + // is it 'self'? + if (!MemoryInst->isVar()) + if (auto decl = + dyn_cast_or_null(getASTType()->getAnyNominal())) + // is it for an actor? + if (decl->isActor() && !decl->isDistributedActor()) // FIXME(78484431) skip distributed actors for now, until their initializers are fixed! + if (auto *silFn = MemoryInst->getFunction()) + // is it a designated initializer? + if (auto *ctor = dyn_cast_or_null( + silFn->getDeclContext()->getAsDecl())) + if (ctor->isDesignatedInit()) + return ctor; + + return nullptr; +} + //===----------------------------------------------------------------------===// // DIMemoryUse Implementation //===----------------------------------------------------------------------===// @@ -1047,7 +1065,11 @@ void ElementUseCollector::collectClassSelfUses(SILValue ClassPointer) { // If we are looking at the init method for a root class, just walk the // MUI use-def chain directly to find our uses. - if (TheMemory.isRootSelf()) { + if (TheMemory.isRootSelf() || + + // Also, just visit all users if ClassPointer is a closure argument, + // i.e. collectClassSelfUses is called from addClosureElementUses. + isa(ClassPointer)) { collectClassSelfUses(ClassPointer, TheMemory.getType(), EltNumbering); return; } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index 88ebcd5f0b537..60e9292eae229 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -72,6 +72,9 @@ class DIMemoryObjectInfo { /// non-empty. bool HasDummyElement = false; + /// True if this object has a single user of type ProjectBoxInst. + bool IsBox = false; + public: DIMemoryObjectInfo(MarkUninitializedInst *MemoryInst); @@ -98,8 +101,12 @@ class DIMemoryObjectInfo { /// instruction. For alloc_box though it returns the project_box associated /// with the memory info. SingleValueInstruction *getUninitializedValue() const { - if (auto *pbi = MemoryInst->getSingleUserOfType()) + if (IsBox) { + // TODO: consider just storing the ProjectBoxInst in this case. + auto *pbi = MemoryInst->getSingleUserOfType(); + assert(pbi); return pbi; + } return MemoryInst; } @@ -157,6 +164,10 @@ class DIMemoryObjectInfo { return false; } + /// Returns the initializer if the memory use is 'self' and appears in an + /// actor's designated initializer. Otherwise, returns nullptr. + ConstructorDecl *getActorInitSelf() const; + /// True if this memory object is the 'self' of a derived class initializer. bool isDerivedClassSelf() const { return MemoryInst->isDerivedClassSelf(); } diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 8c68a235f96dd..491fae8e3edd7 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -17,12 +17,13 @@ #include "swift/AST/Expr.h" #include "swift/AST/Stmt.h" #include "swift/ClangImporter/ClangModule.h" -#include "swift/SIL/BasicBlockData.h" #include "swift/SIL/BasicBlockBits.h" -#include "swift/SIL/SILValue.h" +#include "swift/SIL/BasicBlockData.h" #include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" +#include "swift/SIL/SILValue.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" @@ -384,6 +385,13 @@ namespace { using BlockStates = BasicBlockData; + enum class ActorInitKind { + None, // not an actor init + Plain, // synchronous, not isolated to global-actor + PlainAsync, // asynchronous, not isolated to global-actor + GlobalActorIsolated // isolated to global-actor (sync or async). + }; + /// LifetimeChecker - This is the main heavy lifting for definite /// initialization checking of a memory object. class LifetimeChecker { @@ -444,6 +452,21 @@ namespace { void doIt(); private: + /// Injects `hop_to_executor` instructions into the function after + /// `self` becomes fully initialized, only if the current function + /// is an actor initializer that requires this, and if TheMemory + /// corresponds to `self`. + void injectActorHops(); + + /// Given an initializing block and the live-in availability of TheMemory, + /// this function injects a `hop_to_executor` instruction soon after the + /// first non-load use of TheMemory that fully-initializes it. + /// An "initializing block" is one that definitely contains a such a + /// non-load use, e.g., because its live-in set is *not* all-Yes, but its + /// live-out set is all-Yes. + void injectActorHopForBlock(SILLocation loc, + SILBasicBlock *initializingBlock, + AvailabilitySet const &liveInAvailability); void emitSelfConsumedDiagnostic(SILInstruction *Inst); @@ -469,7 +492,6 @@ namespace { bool *FailedSelfUse = nullptr, bool *FullyUninitialized = nullptr); - void handleStoreUse(unsigned UseID); void handleLoadUse(const DIMemoryUse &Use); void handleLoadForTypeOfSelfUse(DIMemoryUse &Use); @@ -480,6 +502,20 @@ namespace { bool diagnoseReturnWithoutInitializingStoredProperties( const SILInstruction *Inst, SILLocation loc, const DIMemoryUse &Use); + /// Returns true iff TheMemory involves 'self' in a restricted kind of + /// actor initializer. If a non-null Kind pointer was passed in, + /// then the specific kind of restricted actor initializer will be + /// written out. Otherwise, the None initializer kind will be written out. + bool isRestrictedActorInitSelf(ActorInitKind *kind = nullptr) const; + + void reportIllegalUseForActorInit(const DIMemoryUse &Use, + ActorInitKind ActorKind, + StringRef ProblemDesc, + bool suggestConvenienceInit) const; + + void handleLoadUseFailureForActorInit(const DIMemoryUse &Use, + ActorInitKind ActorKind) const; + void handleLoadUseFailure(const DIMemoryUse &Use, bool SuperInitDone, bool FailedSelfUse); @@ -761,6 +797,196 @@ void LifetimeChecker::diagnoseInitError(const DIMemoryUse &Use, diagnose(Module, TheMemory.getLoc(), diag::variable_defined_here, isLet); } +/// Injects a hop_to_executor instruction after the specified insertion point. +static void injectHopToExecutorAfter(SILLocation loc, + SILBasicBlock::iterator insertPt, + SILValue actor, bool needsBorrow = true) { + + LLVM_DEBUG(llvm::dbgs() << "hop-injector: requested insertion after " + << *insertPt); + + // While insertAfter can handle terminators, it cannot handle ones that lead + // to a block with multiple predecessors. I don't expect that a terminator + // could initialize a stored property at all: a try_apply passed the property + // as an inout would not be a valid use until _after_ the property has been + // initialized. + assert(!isa(*insertPt) && "unexpected hop-inject after terminator"); + + auto injectAfter = [&](SILInstruction *insertPt) -> void { + LLVM_DEBUG(llvm::dbgs() << "hop-injector: injecting after " << *insertPt); + SILBuilderWithScope::insertAfter(insertPt, [&](SILBuilder &b) { + if (needsBorrow) + actor = b.createBeginBorrow(loc, actor); + + b.createHopToExecutor(loc.asAutoGenerated(), actor, /*mandatory=*/false); + + if (needsBorrow) + b.createEndBorrow(loc, actor); + }); + }; + + ////// + // NOTE: We prefer to inject a hop outside of any access regions, so that + // the dynamic access-set is empty. This is a best-effort to avoid injecting + // it inside of a region, but does not account for overlapping accesses, etc. + // But, I cannot think of a way to create an overlapping access with a stored + // property when it is first initialized, because it's not valid to pass those + // inout or capture them in a closure. - kavon + + SILInstruction *cur = &*insertPt; + BeginAccessInst *access = nullptr; + + // Finds begin_access instructions that need hops placed after its end_access. + auto getBeginAccess = [](SILValue v) -> BeginAccessInst * { + return dyn_cast(getAccessScope(v)); + }; + + // If this insertion-point is after a store-like instruction, look for a + // begin_access corresponding to the destination. + if (auto *store = dyn_cast(cur)) { + access = getBeginAccess(store->getDest()); + } else if (auto *assign = dyn_cast(cur)) { + access = getBeginAccess(assign->getDest()); + } + + // If we found a begin_access, then we need to inject the hop after + // all of the corresponding end_accesses. + if (access) { + for (auto *endAccess : access->getEndAccesses()) + injectAfter(endAccess); + + return; + } + + ////// + // Otherwise, we just put the hop after the original insertion point. + return injectAfter(cur); +} + +void LifetimeChecker::injectActorHopForBlock( + SILLocation loc, SILBasicBlock *block, + AvailabilitySet const &liveInAvailability) { + // Tracks status of each element of TheMemory as we scan through the block, + // starting with the initial availability at the block's entry-point. + AvailabilitySet localAvail = liveInAvailability; + + auto bbi = block->begin(); // our cursor and eventual insertion-point. + const auto bbe = block->end(); + for (; bbi != bbe; ++bbi) { + auto *inst = &*bbi; + + auto result = NonLoadUses.find(inst); + if (result == NonLoadUses.end()) + continue; // not a possible store + + // Mark the tuple elements involved in this use as defined. + for (unsigned use : result->second) { + auto const &instUse = Uses[use]; + for (unsigned i = instUse.FirstElement; + i < instUse.FirstElement + instUse.NumElements; ++i) + localAvail.set(i, DIKind::Yes); + } + + // Stop if we found the instruction that initializes TheMemory. + if (localAvail.isAllYes()) + break; + } + + // Make sure we found the initializing use of TheMemory. + assert(bbi != bbe && "this block is not initializing?"); + + injectHopToExecutorAfter(loc, bbi, TheMemory.getUninitializedValue()); +} + +void LifetimeChecker::injectActorHops() { + auto ctor = TheMemory.getActorInitSelf(); + + // Must be `self` within an actor's designated initializer. + if (!ctor) + return; + + // If the initializer has restricted use of `self`, then no hops are needed. + if (isRestrictedActorInitSelf()) + return; + + // Even if there are no stored properties to initialize, we still need a hop. + // We insert this directly after the mark_uninitialized instruction, so + // that it happens as early as `self` is available.` + if (TheMemory.getNumElements() == 0) { + auto *selfDef = TheMemory.getUninitializedValue(); + return injectHopToExecutorAfter(ctor, selfDef->getIterator(), selfDef); + } + + // Returns true iff a block returns normally from the initializer, + // which means that it returns `self` in some way (perhaps optional-wrapped). + auto returnsSelf = [](SILBasicBlock &block) -> bool { + // This check relies on the fact that failable initializers are emitted by + // SILGen to perform their return in a fresh block with either: + // 1. No non-load uses of `self` (e.g., failing case) + // 2. An all-Yes in-availability. (e.g., success case) + return block.getTerminator()->getTermKind() == TermKind::ReturnInst; + }; + + ///// + // Step 1: Find initializing blocks, which are blocks that contain a store + // to TheMemory that fully-initializes it, and build the Map. + + // We determine whether a block is "initializing" by inspecting the "in" and + // "out" availability sets of the block. If the block goes from No / Partial + // "in" to Yes "out", then some instruction in the block caused TheMemory to + // become fully-initialized, so we record that block and its in-availability + // to scan the block more precisely later in the next Step. + for (auto &block : F) { + auto &info = getBlockInfo(&block); + + if (!info.HasNonLoadUse) { + LLVM_DEBUG(llvm::dbgs() + << "hop-injector: rejecting bb" << block.getDebugID() + << " b/c no non-load uses.\n"); + continue; // could not be an initializing block. + } + + // Determine if this `block` is initializing, that is: + // + // InAvailability ≡ merge(OutAvailability(predecessors(block))) + // ≠ Yes + // AND + // OutAvailability(block) = Yes OR returnsSelf(block) + // + // A block with no predecessors has in-avail of non-Yes. + // A block with no successors has an out-avail of non-Yes, since + // availability is not computed for it. + + auto outSet = info.OutAvailability; + if (!outSet.isAllYes() && !returnsSelf(block)) { + LLVM_DEBUG(llvm::dbgs() + << "hop-injector: rejecting bb" << block.getDebugID() + << " b/c non-Yes OUT avail\n"); + continue; // then this block never sees TheMemory initialized. + } + + AvailabilitySet inSet(outSet.size()); + auto const &predecessors = block.getPredecessorBlocks(); + for (auto *pred : predecessors) + inSet.mergeIn(getBlockInfo(pred).OutAvailability); + + if (inSet.isAllYes()) { + LLVM_DEBUG(llvm::dbgs() + << "hop-injector: rejecting bb" << block.getDebugID() + << " b/c all-Yes IN avail\n"); + continue; // then this block always sees TheMemory initialized. + } + + LLVM_DEBUG(llvm::dbgs() << "hop-injector: bb" << block.getDebugID() + << " is initializing block with in-availability: " + << inSet << "\n"); + + // Step 2: Scan the initializing block to find the first non-load use that + // fully-initializes TheMemory, and insert the hop there. + injectActorHopForBlock(ctor, &block, inSet); + } +} + void LifetimeChecker::doIt() { // With any escapes tallied up, we can work through all the uses, checking // for definitive initialization, promoting loads, rewriting assigns, and @@ -829,6 +1055,9 @@ void LifetimeChecker::doIt() { // If we emitted an error, there is no reason to proceed with load promotion. if (!EmittedErrorLocs.empty()) return; + // Insert hop_to_executor instructions for actor initializers, if needed. + injectActorHops(); + // If the memory object has nontrivial type, then any destroy/release of the // memory object will destruct the memory. If the memory (or some element // thereof) is not initialized on some path, the bad things happen. Process @@ -866,9 +1095,13 @@ void LifetimeChecker::doIt() { void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) { bool IsSuperInitComplete, FailedSelfUse; + ActorInitKind ActorKind = ActorInitKind::None; // If the value is not definitively initialized, emit an error. if (!isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse)) return handleLoadUseFailure(Use, IsSuperInitComplete, FailedSelfUse); + // Check if it involves 'self' in a restricted actor init. + if (isRestrictedActorInitSelf(&ActorKind)) + return handleLoadUseFailureForActorInit(Use, ActorKind); } static void replaceValueMetatypeInstWithMetatypeArgument( @@ -1177,6 +1410,7 @@ static FullApplySite findApply(SILInstruction *I, bool &isSelfParameter) { void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) { bool IsSuperInitDone, FailedSelfUse; + ActorInitKind ActorKind = ActorInitKind::None; // inout uses are generally straight-forward: the memory must be initialized // before the "address" is passed as an l-value. @@ -1195,6 +1429,11 @@ void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) { return; } + // 'self' cannot be passed 'inout' from some kinds of actor initializers. + if (isRestrictedActorInitSelf(&ActorKind)) + reportIllegalUseForActorInit(Use, ActorKind, "be passed 'inout'", + /*suggestConvenienceInit=*/false); + // One additional check: 'let' properties may never be passed inout, because // they are only allowed to have their initial value set, not a subsequent // overwrite. @@ -1357,12 +1596,20 @@ static bool isLoadForReturn(SingleValueInstruction *loadInst) { } void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) { + // The value must be fully initialized at all escape points. If not, diagnose // the error. bool SuperInitDone, FailedSelfUse, FullyUninitialized; + ActorInitKind ActorKind = ActorInitKind::None; if (isInitializedAtUse(Use, &SuperInitDone, &FailedSelfUse, &FullyUninitialized)) { + + // no escaping uses of 'self' are allowed in restricted actor inits. + if (isRestrictedActorInitSelf(&ActorKind)) + reportIllegalUseForActorInit(Use, ActorKind, "be captured by a closure", + /*suggestConvenienceInit=*/true); + return; } @@ -1746,6 +1993,103 @@ bool LifetimeChecker::diagnoseReturnWithoutInitializingStoredProperties( return true; } +bool LifetimeChecker::isRestrictedActorInitSelf(ActorInitKind *kind) const { + + auto result = [&](ActorInitKind k, bool isRestricted) -> bool { + if (kind) + *kind = k; + return isRestricted; + }; + + // Currently: being synchronous, or global-actor isolated, means the actor's + // self is restricted within the init. + if (auto *ctor = TheMemory.getActorInitSelf()) { + if (getActorIsolation(ctor).isGlobalActor()) // global-actor isolated? + return result(ActorInitKind::GlobalActorIsolated, true); + else if (!ctor->hasAsync()) // synchronous? + return result(ActorInitKind::Plain, true); + else + return result(ActorInitKind::PlainAsync, false); + } + + return result(ActorInitKind::None, false); +} + +void LifetimeChecker::reportIllegalUseForActorInit( + const DIMemoryUse &Use, + ActorInitKind ActorKind, + StringRef ProblemDesc, + bool suggestConvenienceInit) const { + switch(ActorKind) { + case ActorInitKind::None: + case ActorInitKind::PlainAsync: + llvm::report_fatal_error("this actor init is never problematic!"); + + case ActorInitKind::Plain: + diagnose(Module, Use.Inst->getLoc(), diag::self_disallowed_actor_init, + false, ProblemDesc) + .warnUntilSwiftVersion(6); + break; + + case ActorInitKind::GlobalActorIsolated: + diagnose(Module, Use.Inst->getLoc(), diag::self_disallowed_actor_init, + true, ProblemDesc) + .warnUntilSwiftVersion(6); + break; + } + + if (suggestConvenienceInit) + diagnose(Module, Use.Inst->getLoc(), diag::actor_convenience_init); +} + +void LifetimeChecker::handleLoadUseFailureForActorInit( + const DIMemoryUse &Use, + ActorInitKind ActorKind) const { + assert(TheMemory.isAnyInitSelf()); + SILInstruction *Inst = Use.Inst; + + // While enum instructions have no side-effects, they do wrap-up `self` + // such that it can escape our analysis. So, enum instruction uses are only + // acceptable if the enum is part of a specific pattern of usage that is + // generated for a failable initializer. + if (auto *enumInst = dyn_cast(Inst)) { + if (isFailableInitReturnUseOfEnum(enumInst)) + return; + + // Otherwise, if the use has no possibility of side-effects, then its OK. + } else if (!Inst->mayHaveSideEffects()) { + return; + + // While loads can have side-effects, we are not concerned by them here. + } else if (isa(Inst)) { + return; + } + + // Everything else is disallowed! + switch(ActorKind) { + case ActorInitKind::None: + case ActorInitKind::PlainAsync: + llvm::report_fatal_error("this actor init is never problematic!"); + + case ActorInitKind::Plain: + diagnose(Module, Use.Inst->getLoc(), diag::self_use_actor_init, false) + .warnUntilSwiftVersion(6); + break; + + case ActorInitKind::GlobalActorIsolated: + diagnose(Module, Use.Inst->getLoc(), diag::self_use_actor_init, true) + .warnUntilSwiftVersion(6); + break; + } + + // We cannot easily determine which argument in the call the use of 'self' + // appears in. If we could, then we could determine whether the callee + // is 'isolated' to that parameter, in order to avoid suggesting a convenience + // init in those cases. Thus, the phrasing of the note should be informative. + if (isa(Inst)) + diagnose(Module, Use.Inst->getLoc(), diag::actor_convenience_init); +} + /// Check and diagnose various failures when a load use is not fully /// initialized. /// @@ -2053,6 +2397,18 @@ static void emitDefaultActorDestroy(SILBuilder &B, SILLocation loc, B.createEndBorrow(loc, self); } +static void emitDistributedActorDestroy(SILBuilder &B, SILLocation loc, + SILValue self) { + auto builtinName = B.getASTContext().getIdentifier( + getBuiltinName(BuiltinValueKind::DestroyDistributedActor)); + auto resultTy = B.getModule().Types.getEmptyTupleType(); + + self = B.createBeginBorrow(loc, self); + B.createBuiltin(loc, builtinName, resultTy, /*subs*/{}, + { self }); + B.createEndBorrow(loc, self); +} + void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt) { @@ -2108,8 +2464,12 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, // don't need to track it specially. if (!TheMemory.isDelegatingInit()) { auto classDecl = TheMemory.getASTType().getClassOrBoundGenericClass(); - if (classDecl && classDecl->isRootDefaultActor()) - emitDefaultActorDestroy(B, Loc, Pointer); + if (classDecl && classDecl->isRootDefaultActor()) { + if (classDecl->isDistributedActor()) + emitDistributedActorDestroy(B, Loc, Pointer); // FIXME(distributed): this will be different only for if it is 'remote' + else + emitDefaultActorDestroy(B, Loc, Pointer); + } } // We've already destroyed any instance variables initialized by this diff --git a/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp b/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp index 3a6f7ad4b8d38..f6c77ff41f2d7 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp @@ -44,6 +44,7 @@ #include "swift/SIL/ApplySite.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/BasicBlockData.h" +#include "swift/SIL/BasicBlockDatastructures.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/Devirtualize.h" @@ -275,8 +276,10 @@ struct BlockInfo { /// non-null if this block contains a recursive call. SILInstruction *recursiveCall; - /// The number of successors which reach a `return`. - unsigned numSuccsNotReachingReturn; + /// The number of successors which reach a recursive call, but not the + /// function exit, i.e. successors for which + /// reachesRecursiveCall && !reachesFunctionExit + unsigned numSuccsReachingRecursiveCall; /// True if the block has a terminator with an invariant condition. /// @@ -284,29 +287,22 @@ struct BlockInfo { /// which are passed to the constructor. bool hasInvariantCondition; - /// Is there any path from the this block to a function return, without going + /// Is there any path from the this block to a function exit, without going /// through a recursive call? /// - /// This flag is propagated up the control flow, starting at returns. - /// /// Note that if memory is expected to be invariant, all memory-writing - /// instructions are also considered as a "return". - bool reachesReturn; - - /// Is there any path from the entry to this block without going through a - /// `reachesReturn` block. - /// - /// This flag is propagated down the control flow, starting at entry. If this - /// flag reaches a block with a recursiveCall, it means that it's an infinite - /// recursive call. - bool reachableFromEntry; + /// instructions are also considered as a "function exit". + bool reachesFunctionExit; + /// Is there any path from the this block to a recursive call? + bool reachesRecursiveCall; + /// Get block information with expected \p invariants. BlockInfo(SILBasicBlock *block, Invariants invariants) : recursiveCall(nullptr), - numSuccsNotReachingReturn(block->getNumSuccessors()), + numSuccsReachingRecursiveCall(0), hasInvariantCondition(invariants.isInvariant(block->getTerminator())), - reachesReturn(false), reachableFromEntry(false) { + reachesFunctionExit(false), reachesRecursiveCall(false) { for (SILInstruction &inst : *block) { if (auto applySite = FullApplySite::isa(&inst)) { // Ignore blocks which call a @_semantics("programtermination_point"). @@ -319,14 +315,15 @@ struct BlockInfo { if (isRecursiveCall(applySite) && invariants.hasInvariantArguments(applySite)) { recursiveCall = &inst; + reachesRecursiveCall = true; return; } } if (invariants.isMemoryInvariant() && mayWriteToMemory(&inst)) { // If we are assuming that all memory is invariant, a memory-writing // instruction potentially breaks the infinite recursion loop. For the - // sake of the anlaysis, it's like a function return. - reachesReturn = true; + // sake of the anlaysis, it's like a function exit. + reachesFunctionExit = true; return; } } @@ -334,7 +331,7 @@ struct BlockInfo { if (term->isFunctionExiting() || // Also treat non-assert-like unreachables as returns, like "exit()". term->isProgramTerminating()) { - reachesReturn = true; + reachesFunctionExit = true; } } }; @@ -383,76 +380,102 @@ class InfiniteRecursionAnalysis { return BlockInfo(block, invariants); }) { } - /// Propagates the `reachesReturn` flags up the control flow and returns true - /// if the flag reaches the entry block. - bool isEntryReachableFromReturn() { - // Contains blocks for which the `reachesReturn` flag is set. - SmallVector workList; + /// Propagates the `reachesRecursiveCall` flags up the control flow. + void propagateRecursiveCalls() { + StackList workList(blockInfos.getFunction()); + + // Initialize the workList with all blocks which contain recursive calls. + for (auto bd : blockInfos) { + if (bd.data.reachesRecursiveCall) + workList.push_back(&bd.block); + } + + while (!workList.empty()) { + SILBasicBlock *block = workList.pop_back_val(); + assert(blockInfos[block].reachesRecursiveCall); + for (auto *pred : block->getPredecessorBlocks()) { + BlockInfo &predInfo = blockInfos[pred]; + predInfo.numSuccsReachingRecursiveCall += 1; + if (!predInfo.reachesRecursiveCall) { + predInfo.reachesRecursiveCall = true; + workList.push_back(pred); + } + } + } + } + + /// Propagates the `reachesFunctionExit` flags up the control flow. + void propagateFunctionExits() { + StackList workList(blockInfos.getFunction()); - // Initialize the workList with all function-return blocks. + // Initialize the workList with all function-exiting blocks. for (auto bd : blockInfos) { - if (bd.data.reachesReturn) + if (bd.data.reachesFunctionExit) workList.push_back(&bd.block); } while (!workList.empty()) { SILBasicBlock *block = workList.pop_back_val(); + BlockInfo &info = blockInfos[block]; + assert(info.reachesFunctionExit); for (auto *pred : block->getPredecessorBlocks()) { BlockInfo &predInfo = blockInfos[pred]; - if (predInfo.reachesReturn || + + if (info.reachesRecursiveCall) { + // Update `numSuccsReachingRecursiveCall`, because this counter + // excludes successors which reach a function exit. + assert(predInfo.numSuccsReachingRecursiveCall > 0); + predInfo.numSuccsReachingRecursiveCall -= 1; + } + + if (predInfo.reachesFunctionExit || // Recursive calls block the flag propagation. predInfo.recursiveCall != nullptr) continue; - assert(predInfo.numSuccsNotReachingReturn > 0); - predInfo.numSuccsNotReachingReturn -= 1; - // This is the trick for handling invariant conditions: usually the - // `reachesReturn` flag is propagated if _any_ of the successors has it - // set. + // `reachesFunctionExit` flag is propagated if _any_ of the successors + // has it set. // For invariant conditions, it's only propagated if _all_ successors - // have it set. If at least one of the successors reaches a recursive - // call and this successor is taken once, it will be taken forever - // (because the condition is invariant). + // which reach recursive calls also reach a function exit. + // If at least one of the successors reaches a recursive call (but not + // a function exit) and this successor is taken once, it will be taken + // forever (because the condition is invariant). if (predInfo.hasInvariantCondition && - predInfo.numSuccsNotReachingReturn > 0) + predInfo.numSuccsReachingRecursiveCall > 0) continue; - predInfo.reachesReturn = true; + predInfo.reachesFunctionExit = true; workList.push_back(pred); } } - return blockInfos.entry().data.reachesReturn; } + + /// Finds all infinite recursive calls reachable from the entry and issues + /// warnings. + /// Returns true if the function contains infinite recursive calls. + bool issueWarningsForInfiniteRecursiveCalls() { + const BlockInfo &entryInfo = blockInfos.entry().data; + if (!entryInfo.reachesRecursiveCall || entryInfo.reachesFunctionExit) + return false; - /// Propagates the `reachableFromEntry` flags down the control flow and - /// issues a warning if it reaches a recursive call. - /// Returns true, if at least one recursive call is found. - bool findRecursiveCallsAndDiagnose() { - SmallVector workList; - auto entry = blockInfos.entry(); - entry.data.reachableFromEntry = true; - workList.push_back(&entry.block); + BasicBlockWorklist workList(blockInfos.getFunction()); + workList.push(&blockInfos.entry().block); - bool foundInfiniteRecursion = false; - while (!workList.empty()) { - SILBasicBlock *block = workList.pop_back_val(); + while (SILBasicBlock *block = workList.pop()) { if (auto *recursiveCall = blockInfos[block].recursiveCall) { blockInfos.getFunction()->getModule().getASTContext().Diags.diagnose( recursiveCall->getLoc().getSourceLoc(), diag::warn_infinite_recursive_call); - foundInfiniteRecursion = true; continue; } for (auto *succ : block->getSuccessorBlocks()) { BlockInfo &succInfo = blockInfos[succ]; - if (!succInfo.reachesReturn && !succInfo.reachableFromEntry) { - succInfo.reachableFromEntry = true; - workList.push_back(succ); - } + if (succInfo.reachesRecursiveCall && !succInfo.reachesFunctionExit) + workList.pushIfNotVisited(succ); } } - return foundInfiniteRecursion; + return true; } public: @@ -460,14 +483,14 @@ class InfiniteRecursionAnalysis { LLVM_ATTRIBUTE_USED void dump() { for (auto bd : blockInfos) { llvm::dbgs() << "bb" << bd.block.getDebugID() - << ": numSuccs= " << bd.data.numSuccsNotReachingReturn; + << ": numSuccs= " << bd.data.numSuccsReachingRecursiveCall; if (bd.data.recursiveCall) llvm::dbgs() << " hasRecursiveCall"; if (bd.data.hasInvariantCondition) llvm::dbgs() << " hasInvariantCondition"; - if (bd.data.reachesReturn) - llvm::dbgs() << " reachesReturn"; - if (bd.data.reachableFromEntry) + if (bd.data.reachesFunctionExit) + llvm::dbgs() << " reachesFunctionExit"; + if (bd.data.reachesRecursiveCall) llvm::dbgs() << " reachesRecursiveCall"; llvm::dbgs() << '\n'; } @@ -477,20 +500,9 @@ class InfiniteRecursionAnalysis { /// Returns true, if at least one recursive call is found. static bool analyzeAndDiagnose(SILFunction *function, Invariants invariants) { InfiniteRecursionAnalysis analysis(function, invariants); - if (analysis.isEntryReachableFromReturn()) - return false; - - // Now we know that the function never returns. - // There can be three cases: - // 1. All paths end up in an abnormal program termination, like fatalError(). - // We don't want to warn about this. It's probably intention. - // 2. There is an infinite loop. - // We don't want to warn about this either. Maybe it's intention. Anyway, - // this case is handled by the DiagnoseUnreachable pass. - // 3. There is an infinite recursion. - // That's what we are interested in. We do a forward propagation to find - // the actual infinite recursive call(s) - if any. - return analysis.findRecursiveCallsAndDiagnose(); + analysis.propagateRecursiveCalls(); + analysis.propagateFunctionExits(); + return analysis.issueWarningsForInfiniteRecursiveCalls(); } }; diff --git a/lib/SILOptimizer/Mandatory/DiagnoseLifetimeIssues.cpp b/lib/SILOptimizer/Mandatory/DiagnoseLifetimeIssues.cpp index a6faf6061f6bc..2cb24b3b74b0c 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseLifetimeIssues.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseLifetimeIssues.cpp @@ -31,14 +31,15 @@ #define DEBUG_TYPE "diagnose-lifetime-issues" #include "swift/AST/DiagnosticsSIL.h" +#include "swift/Demangling/Demangler.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/BasicBlockBits.h" +#include "swift/SIL/OwnershipUtils.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/PrunedLiveness.h" -#include "swift/Demangling/Demangler.h" +#include "clang/AST/DeclObjC.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Debug.h" -#include "clang/AST/DeclObjC.h" using namespace swift; @@ -156,19 +157,24 @@ static bool isStoreObjcWeak(SILInstruction *inst, Operand *op) { /// Returns the state of \p def. See DiagnoseLifetimeIssues::State. DiagnoseLifetimeIssues::State DiagnoseLifetimeIssues:: visitUses(SILValue def, bool updateLivenessAndWeakStores, int callDepth) { - SmallSetVector defUseWorklist; - defUseWorklist.insert(def); + SmallPtrSet defUseVisited; + SmallVector defUseVector; + auto pushDef = [&](SILValue value) { + if (defUseVisited.insert(value).second) + defUseVector.push_back(value); + }; + + pushDef(def); bool foundWeakStore = false; - while (!defUseWorklist.empty()) { - SILValue value = defUseWorklist.pop_back_val(); + while (!defUseVector.empty()) { + SILValue value = defUseVector.pop_back_val(); for (Operand *use : value->getUses()) { auto *user = use->getUser(); // Recurse through copies and enums. Enums are important because the // operand of a store_weak is always an Optional. - if (isa(user) || isa(user) || - isa(user)) { - defUseWorklist.insert(cast(user)); + if (isa(user)) { + pushDef(cast(user)); continue; } if (isa(user) || isStoreObjcWeak(user, use)) { @@ -208,22 +214,53 @@ visitUses(SILValue def, bool updateLivenessAndWeakStores, int callDepth) { if (updateLivenessAndWeakStores) liveness.updateForUse(user, /*lifetimeEnding*/ false); break; + case OperandOwnership::ForwardingBorrow: case OperandOwnership::ForwardingConsume: - // TODO: handle forwarding instructions, e.g. casts. - return CanEscape; + // TermInst includes ReturnInst, which is generally an escape. + // If this is called as part of getArgumentState, then it is not really + // an escape, but we don't currently follow returned values. + if (isa(user)) + return CanEscape; + + for (SILValue result : user->getResults()) { + // This assumes that forwarding to a trivial value cannot extend the + // lifetime. This way, simply projecting a trivial value out of an + // aggregate isn't considered an escape. + if (result.getOwnershipKind() == OwnershipKind::None) + continue; + + pushDef(result); + } + continue; case OperandOwnership::DestroyingConsume: // destroy_value does not force pruned liveness (but store etc. does). if (!isa(user)) return CanEscape; break; - case OperandOwnership::Borrow: + case OperandOwnership::Borrow: { if (updateLivenessAndWeakStores && !liveness.updateForBorrowingOperand(use)) return CanEscape; + BorrowingOperand borrowOper(use); + if (borrowOper.hasBorrowIntroducingUser()) { + if (auto *beginBorrow = dyn_cast(user)) + pushDef(beginBorrow); + else + return CanEscape; + } break; - case OperandOwnership::InteriorPointer: - case OperandOwnership::ForwardingBorrow: + } case OperandOwnership::EndBorrow: + continue; + + case OperandOwnership::InteriorPointer: + // Treat most interior pointers as escapes until they can be audited. + // But if the interior pointer cannot be used to copy the parent + // reference, then it does not need to be considered an escape. + if (isa(user)) { + continue; + } + return CanEscape; case OperandOwnership::Reborrow: return CanEscape; } diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index f9e68af362c47..7cb1be53757ce 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -1034,7 +1034,7 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { // Some identifiable addresses can also be recognized as local initialization // or other patterns that don't qualify as formal access. - if (!isPossibleFormalAccessBase(storage, memInst->getFunction())) + if (!isPossibleFormalAccessStorage(storage, memInst->getFunction())) return; // A box or stack variable may represent lvalues, but they can only conflict @@ -1054,25 +1054,39 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) { namespace { -class DiagnoseStaticExclusivity : public SILFunctionTransform { +/// TODO: This is currently a module transform to ensure that source-level +/// diagnostics, like DiagnoseInvalidCaptures run on closures (in addition to +/// other callees) before this pass processes their parent functions. Otherwise, +/// AccessSummaryAnalysis may crash on invalid SIL. Fix the pass manager to +/// ensure that closures are always diagnosed before their parent. Then add an +/// SCC transform to ensure that the previous diagnostic pass runs on all +/// functions in the SCC before the next diagnostic pass. This will handle +/// closures that call back into their parent. Then this can be converted to an +/// SCC transform. +class DiagnoseStaticExclusivity : public SILModuleTransform { public: DiagnoseStaticExclusivity() {} private: void run() override { - // Don't rerun diagnostics on deserialized functions. - if (getFunction()->wasDeserializedCanonical()) - return; + for (auto &function : *getModule()) { + if (!function.isDefinition()) + continue; - SILFunction *Fn = getFunction(); - // This is a staging flag. Eventually the ability to turn off static - // enforcement will be removed. - if (!Fn->getModule().getOptions().EnforceExclusivityStatic) - return; + // Don't rerun diagnostics on deserialized functions. + if (function.wasDeserializedCanonical()) + continue; - PostOrderFunctionInfo *PO = getAnalysis()->get(Fn); - auto *ASA = getAnalysis(); - checkStaticExclusivity(*Fn, PO, ASA); + // This is a staging flag. Eventually the ability to turn off static + // enforcement will be removed. + if (!function.getModule().getOptions().EnforceExclusivityStatic) + continue; + + PostOrderFunctionInfo *PO = + getAnalysis()->get(&function); + auto *ASA = getAnalysis(); + checkStaticExclusivity(function, PO, ASA); + } } }; diff --git a/lib/SILOptimizer/Mandatory/Differentiation.cpp b/lib/SILOptimizer/Mandatory/Differentiation.cpp index 2a7f9f7fdf86a..23a57616a87d0 100644 --- a/lib/SILOptimizer/Mandatory/Differentiation.cpp +++ b/lib/SILOptimizer/Mandatory/Differentiation.cpp @@ -25,7 +25,6 @@ #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" -#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/ParameterList.h" #include "swift/AST/SourceFile.h" @@ -204,9 +203,7 @@ static bool diagnoseUnsatisfiedRequirements(ADContext &context, return false; // If there are no derivative requirements, return false. - if (!derivativeGenSig) - return false; - auto requirements = derivativeGenSig->getRequirements(); + auto requirements = derivativeGenSig.getRequirements(); if (requirements.empty()) return false; // Iterate through all requirements and check whether they are satisfied. @@ -619,7 +616,7 @@ emitDerivativeFunctionReference( context.emitNondifferentiabilityError( original, invoker, diag::autodiff_private_derivative_from_fragile, fragileKind, - llvm::isa_and_nonnull( + isa_and_nonnull( originalFRI->getLoc().getAsASTNode())); return None; } @@ -782,12 +779,10 @@ static SILFunction *createEmptyVJP(ADContext &context, Mangle::DifferentiationMangler mangler; auto vjpName = mangler.mangleDerivativeFunction( original->getName(), AutoDiffDerivativeFunctionKind::VJP, config); - CanGenericSignature vjpCanGenSig; - if (auto vjpGenSig = witness->getDerivativeGenericSignature()) - vjpCanGenSig = vjpGenSig->getCanonicalSignature(); + auto vjpCanGenSig = witness->getDerivativeGenericSignature().getCanonicalSignature(); GenericEnvironment *vjpGenericEnv = nullptr; if (vjpCanGenSig && !vjpCanGenSig->areAllParamsConcrete()) - vjpGenericEnv = vjpCanGenSig->getGenericEnvironment(); + vjpGenericEnv = vjpCanGenSig.getGenericEnvironment(); auto vjpType = originalTy->getAutoDiffDerivativeFunctionType( config.parameterIndices, config.resultIndices, AutoDiffDerivativeFunctionKind::VJP, @@ -825,12 +820,10 @@ static SILFunction *createEmptyJVP(ADContext &context, Mangle::DifferentiationMangler mangler; auto jvpName = mangler.mangleDerivativeFunction( original->getName(), AutoDiffDerivativeFunctionKind::JVP, config); - CanGenericSignature jvpCanGenSig; - if (auto jvpGenSig = witness->getDerivativeGenericSignature()) - jvpCanGenSig = jvpGenSig->getCanonicalSignature(); + auto jvpCanGenSig = witness->getDerivativeGenericSignature().getCanonicalSignature(); GenericEnvironment *jvpGenericEnv = nullptr; if (jvpCanGenSig && !jvpCanGenSig->areAllParamsConcrete()) - jvpGenericEnv = jvpCanGenSig->getGenericEnvironment(); + jvpGenericEnv = jvpCanGenSig.getGenericEnvironment(); auto jvpType = originalTy->getAutoDiffDerivativeFunctionType( config.parameterIndices, config.resultIndices, AutoDiffDerivativeFunctionKind::JVP, @@ -1041,8 +1034,7 @@ static SILValue promoteCurryThunkApplicationToDifferentiableFunction( // returned function value with an `differentiable_function` // instruction, and process the `differentiable_function` instruction. if (newThunk->empty()) { - if (auto newThunkGenSig = thunkType->getSubstGenericSignature()) - newThunk->setGenericEnvironment(newThunkGenSig->getGenericEnvironment()); + newThunk->setGenericEnvironment(thunkType->getSubstGenericSignature().getGenericEnvironment()); BasicTypeSubstCloner cloner(thunk, newThunk); cloner.cloneFunction(); diff --git a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp index c1c98ee765284..7b112ca195bc7 100644 --- a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp +++ b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp @@ -63,7 +63,7 @@ static bool cleanFunction(SILFunction &fn) { deleter.forceDelete(bi); // StaticReport only takes trivial operands, and therefore doesn't // require fixing the lifetime of its operands. - deleter.cleanUpDeadInstructions(); + deleter.cleanupDeadInstructions(); madeChange = true; break; } diff --git a/lib/SILOptimizer/Mandatory/LowerHopToActor.cpp b/lib/SILOptimizer/Mandatory/LowerHopToActor.cpp index ce13da0904fbd..15fd531d01946 100644 --- a/lib/SILOptimizer/Mandatory/LowerHopToActor.cpp +++ b/lib/SILOptimizer/Mandatory/LowerHopToActor.cpp @@ -100,12 +100,13 @@ bool LowerHopToActor::processHop(HopToExecutorInst *hop) { return false; B.setInsertionPoint(hop); + B.setCurrentDebugScope(hop->getDebugScope()); // Get the dominating executor value for this actor, if available, // or else emit code to derive it. SILValue executor = emitGetExecutor(hop->getLoc(), actor, /*optional*/true); - B.createHopToExecutor(hop->getLoc(), executor); + B.createHopToExecutor(hop->getLoc(), executor, /*mandatory*/ false); hop->eraseFromParent(); @@ -117,6 +118,7 @@ bool LowerHopToActor::processExtract(ExtractExecutorInst *extract) { auto executor = extract->getExpectedExecutor(); if (!isOptionalBuiltinExecutor(executor->getType())) { B.setInsertionPoint(extract); + B.setCurrentDebugScope(extract->getDebugScope()); executor = emitGetExecutor(extract->getLoc(), executor, /*optional*/false); } diff --git a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp index 8f11ab051fb7d..d7530d9f57b89 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp @@ -296,6 +296,9 @@ bool MandatoryCombiner::doOneIteration(SILFunction &function, // its contents to the worklist and then clear said list in preparation // for the next iteration. for (SILInstruction *instruction : createdInstructions) { + if (instruction->isDeleted()) + continue; + LLVM_DEBUG(llvm::dbgs() << "MC: add " << *instruction << " from tracking list to worklist\n"); worklist.add(instruction); @@ -400,26 +403,6 @@ class MandatoryCombine final : public SILFunctionTransform { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } } - -protected: - void handleDeleteNotification(SILNode *node) override { - // Remove instructions that were both created and deleted from the list of - // created instructions which will eventually be added to the worklist. - - auto *instruction = dyn_cast(node); - if (instruction == nullptr) { - return; - } - - // Linear searching the tracking list doesn't hurt because usually it only - // contains a few elements. - auto iterator = find(createdInstructions, instruction); - if (createdInstructions.end() != iterator) { - createdInstructions.erase(iterator); - } - } - - bool needsNotifications() override { return true; } }; } // end anonymous namespace diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp index 08551142e1ce4..b031d3eb6476f 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp @@ -264,8 +264,16 @@ static bool fixupReferenceCounts( return invalidatedStackNesting; } -static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) { - auto *pbi = dyn_cast(li->getOperand()); +// Handle the case where the callee of the apply is either a load or a +// project_box that was used by a deleted load. If we fail to optimize, +// return an invalid SILValue. +static SILValue cleanupLoadedCalleeValue(SILValue calleeValue) { + auto calleeSource = calleeValue; + auto *li = dyn_cast(calleeValue); + if (li) { + calleeSource = li->getOperand(); + } + auto *pbi = dyn_cast(calleeSource); if (!pbi) return SILValue(); auto *abi = dyn_cast(pbi->getOperand()); @@ -274,17 +282,18 @@ static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) { // The load instruction must have no more uses or a single destroy left to // erase it. - if (li->getFunction()->hasOwnership()) { - // TODO: What if we have multiple destroy_value? That should be ok as well. - auto *dvi = li->getSingleUserOfType(); - if (!dvi) + if (li) { + if (li->getFunction()->hasOwnership()) { + // TODO: What if we have multiple destroy_value? That should be ok. + auto *dvi = li->getSingleUserOfType(); + if (!dvi) + return SILValue(); + dvi->eraseFromParent(); + } else if (!li->use_empty()) { return SILValue(); - dvi->eraseFromParent(); - } else if (!li->use_empty()) { - return SILValue(); + } + li->eraseFromParent(); } - li->eraseFromParent(); - // Look through uses of the alloc box the load is loading from to find up to // one store and up to one strong release. PointerUnion destroy; @@ -356,15 +365,8 @@ static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) { /// longer necessary after inlining. static void cleanupCalleeValue(SILValue calleeValue, bool &invalidatedStackNesting) { - // Handle the case where the callee of the apply is a load instruction. If we - // fail to optimize, return. Otherwise, see if we can look through other - // abstractions on our callee. - if (auto *li = dyn_cast(calleeValue)) { - calleeValue = cleanupLoadedCalleeValue(calleeValue, li); - if (!calleeValue) { - return; - } - } + if (auto loadedValue = cleanupLoadedCalleeValue(calleeValue)) + calleeValue = loadedValue; calleeValue = stripCopiesAndBorrows(calleeValue); @@ -417,46 +419,6 @@ static void cleanupCalleeValue(SILValue calleeValue, namespace { /// Cleanup dead closures after inlining. class ClosureCleanup { - using DeadInstSet = SmallBlotSetVector; - - /// A helper class to update the set of dead instructions. - /// - /// Since this is called by the SILModule callback, the instruction may longer - /// be well-formed. Do not visit its operands. However, it's position in the - /// basic block is still valid. - /// - /// FIXME: Using the Module's callback mechanism for this is terrible. - /// Instead, cleanupCalleeValue could be easily rewritten to use its own - /// instruction deletion helper and pass a callback to tryDeleteDeadClosure - /// and recursivelyDeleteTriviallyDeadInstructions. - class DeleteUpdateHandler : public DeleteNotificationHandler { - SILModule &Module; - DeadInstSet &DeadInsts; - - public: - DeleteUpdateHandler(SILModule &M, DeadInstSet &DeadInsts) - : Module(M), DeadInsts(DeadInsts) { - Module.registerDeleteNotificationHandler(this); - } - - ~DeleteUpdateHandler() override { - // Unregister the handler. - Module.removeDeleteNotificationHandler(this); - } - - // Handling of instruction removal notifications. - bool needsNotifications() override { return true; } - - // Handle notifications about removals of instructions. - void handleDeleteNotification(SILNode *node) override { - auto deletedI = dyn_cast(node); - if (!deletedI) - return; - - DeadInsts.erase(deletedI); - } - }; - SmallBlotSetVector deadFunctionVals; public: @@ -500,9 +462,8 @@ class ClosureCleanup { // set needs to continue to be updated (by this handler) when deleting // instructions. This assumes that DeadFunctionValSet::erase() is stable. void cleanupDeadClosures(SILFunction *F) { - DeleteUpdateHandler deleteUpdate(F->getModule(), deadFunctionVals); for (Optional I : deadFunctionVals) { - if (!I.hasValue()) + if (!I.hasValue() || I.getValue()->isDeleted()) continue; if (auto *SVI = dyn_cast(I.getValue())) diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 34972896b093b..40322ac0cfffd 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -1200,6 +1200,8 @@ static void forceDeleteAllocStack(SingleValueInstruction *inst, forceDeleteAllocStack(cast(user), deleter); continue; } + // Notify the deletion worklist in case user's other operands become dead. + deleter.getCallbacks().notifyWillBeDeleted(user); deleter.forceDeleteAndFixLifetimes(user); } deleter.forceDelete(inst); @@ -1252,8 +1254,11 @@ static bool tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) { if (deletedInstructions.count(inst)) continue; deleteInstructionWithUsersAndFixLifetimes(inst, deleter); + // Call cleanupDeadInstructions incrementally because it may expose a dead + // alloc_stack, which will only be deleted by this pass via + // deleteInstructionWithUsersAndFixLifetimes(). + deleter.cleanupDeadInstructions(); } - deleter.cleanUpDeadInstructions(); // If the OSLogMessage instance is not deleted, either we couldn't see the // body of the log call or there is a bug in the library implementation. @@ -1478,7 +1483,7 @@ suppressGlobalStringTablePointerError(SingleValueInstruction *oslogMessage) { // many instructions, do the cleanup at the end. deleter.trackIfDead(bi); } - deleter.cleanUpDeadInstructions(); + deleter.cleanupDeadInstructions(); } /// If the SILInstruction is an initialization of OSLogMessage, return the diff --git a/lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp b/lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp index 28931162e5cf8..2f945a02c5db6 100644 --- a/lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp +++ b/lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "insert-hop-to-executor" +#define DEBUG_TYPE "optimize-hop-to-executor" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/ApplySite.h" @@ -233,17 +233,22 @@ bool OptimizeHopToExecutor::removeRedundantHopToExecutors(const Actors &actors) actorIdx = BlockState::Unknown; continue; } - if (auto *hop = dyn_cast(inst)) { - int newActorIdx = actors.lookup(hop->getOperand()); - if (newActorIdx == actorIdx) { - // There is a dominating hop_to_executor with the same operand. - hop->eraseFromParent(); - changed = true; - continue; - } + auto *hop = dyn_cast(inst); + if (!hop) + continue; + + int newActorIdx = actors.lookup(hop->getOperand()); + if (newActorIdx != actorIdx) { actorIdx = newActorIdx; continue; } + if (hop->isMandatory()) + continue; + + // There is a dominating hop_to_executor with the same operand. + LLVM_DEBUG(llvm::dbgs() << "Redundant executor " << *hop); + hop->eraseFromParent(); + changed = true; } assert(actorIdx == state.exit); } @@ -279,8 +284,10 @@ bool OptimizeHopToExecutor::removeDeadHopToExecutors() { for (auto iter = state.block->rbegin(); iter != state.block->rend();) { SILInstruction *inst = &*iter++; auto *hop = dyn_cast(inst); - if (hop && needActor == BlockState::NoExecutorNeeded) { + if (hop && !hop->isMandatory() + && needActor == BlockState::NoExecutorNeeded) { // Remove the dead hop_to_executor. + LLVM_DEBUG(llvm::dbgs() << "Dead executor " << *hop); hop->eraseFromParent(); changed = true; continue; diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index ce93377a34a97..2624b591357b6 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -527,9 +527,10 @@ static bool stripOwnership(SILFunction &func) { auto value = visitor.instructionsToSimplify.pop_back_val(); if (!value.hasValue()) continue; - auto callbacks = InstModCallbacks().onDelete([&](SILInstruction *instToErase) { - visitor.eraseInstruction(instToErase); - }); + auto callbacks = + InstModCallbacks().onDelete([&](SILInstruction *instToErase) { + visitor.eraseInstruction(instToErase); + }); // We are no longer in OSSA, so we don't need to pass in a deBlocks. simplifyAndReplaceAllSimplifiedUsesAndErase(*value, callbacks); madeChange |= callbacks.hadCallbackInvocation(); diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 76bd889059f09..38177ce20ebf5 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -1336,6 +1336,8 @@ class AvailableValueDataflowContext { /// this. SmallVectorImpl &Uses; + InstructionDeleter &deleter; + /// The set of blocks with local definitions. /// /// We use this to determine if we should visit a block or look at a block's @@ -1365,7 +1367,8 @@ class AvailableValueDataflowContext { public: AvailableValueDataflowContext(AllocationInst *TheMemory, unsigned NumMemorySubElements, - SmallVectorImpl &Uses); + SmallVectorImpl &Uses, + InstructionDeleter &deleter); /// Try to compute available values for "TheMemory" at the instruction \p /// StartingFrom. We only compute the values for set bits in \p @@ -1405,9 +1408,10 @@ class AvailableValueDataflowContext { AvailableValueDataflowContext::AvailableValueDataflowContext( AllocationInst *InputTheMemory, unsigned NumMemorySubElements, - SmallVectorImpl &InputUses) + SmallVectorImpl &InputUses, InstructionDeleter &deleter) : TheMemory(InputTheMemory), NumMemorySubElements(NumMemorySubElements), - Uses(InputUses), HasLocalDefinition(InputTheMemory->getFunction()), + Uses(InputUses), deleter(deleter), + HasLocalDefinition(InputTheMemory->getFunction()), HasLocalKill(InputTheMemory->getFunction()) { // The first step of processing an element is to collect information about the // element into data structures we use later. @@ -1878,7 +1882,7 @@ void AvailableValueDataflowContext::explodeCopyAddr(CopyAddrInst *CAI) { } // Next, remove the copy_addr itself. - CAI->eraseFromParent(); + deleter.forceDelete(CAI); } bool AvailableValueDataflowContext::hasEscapedAt(SILInstruction *I) { @@ -1934,19 +1938,22 @@ class AllocOptimize { DeadEndBlocks &deadEndBlocks; + InstructionDeleter &deleter; + /// A structure that we use to compute our available values. AvailableValueDataflowContext DataflowContext; public: AllocOptimize(AllocationInst *memory, SmallVectorImpl &uses, SmallVectorImpl &releases, - DeadEndBlocks &deadEndBlocks) + DeadEndBlocks &deadEndBlocks, InstructionDeleter &deleter) : Module(memory->getModule()), TheMemory(memory), MemoryType(getMemoryType(memory)), NumMemorySubElements(getNumSubElements( MemoryType, Module, TypeExpansionContext(*memory->getFunction()))), Uses(uses), Releases(releases), deadEndBlocks(deadEndBlocks), - DataflowContext(TheMemory, NumMemorySubElements, uses) {} + deleter(deleter), + DataflowContext(TheMemory, NumMemorySubElements, uses, deleter) {} bool optimizeMemoryAccesses(); @@ -2085,9 +2092,9 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { if (!li->getFunction()->hasOwnership()) { li->replaceAllUsesWith(newVal); SILValue addr = li->getOperand(); - li->eraseFromParent(); + deleter.forceDelete(li); if (auto *addrI = addr->getDefiningInstruction()) - eliminateDeadInstruction(addrI); + deleter.deleteIfDead(addrI); return true; } @@ -2106,9 +2113,9 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) { li->replaceAllUsesWith(newVal); SILValue addr = li->getOperand(); - li->eraseFromParent(); + deleter.forceDelete(li); if (auto *addrI = addr->getDefiningInstruction()) - eliminateDeadInstruction(addrI); + deleter.deleteIfDead(addrI); return true; } @@ -2209,9 +2216,9 @@ bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *lbi) { lbi->replaceAllUsesWith(newVal); SILValue addr = lbi->getOperand(); - lbi->eraseFromParent(); + deleter.forceDelete(lbi); if (auto *addrI = addr->getDefiningInstruction()) - eliminateDeadInstruction(addrI); + deleter.deleteIfDead(addrI); return true; } @@ -2308,7 +2315,7 @@ void AllocOptimize::promoteDestroyAddr( LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); SILBuilderWithScope(dai).emitDestroyValueOperation(dai->getLoc(), newVal); - dai->eraseFromParent(); + deleter.forceDelete(dai); } SILValue AllocOptimize::promoteLoadTake( @@ -2496,8 +2503,7 @@ bool AllocOptimize::tryToRemoveDeadAllocation() { // If it is safe to remove, do it. Recursively remove all instructions // hanging off the allocation instruction, then return success. Let the // caller remove the allocation itself to avoid iterator invalidation. - eraseUsesOfInstruction(TheMemory); - + deleter.forceDeleteWithUsers(TheMemory); return true; } @@ -2623,16 +2629,15 @@ bool AllocOptimize::tryToRemoveDeadAllocation() { LoadInst *li = p.first; SILValue newValue = p.second; li->replaceAllUsesWith(newValue); - li->eraseFromParent(); + deleter.forceDelete(li); } LLVM_DEBUG(llvm::dbgs() << "*** Removing autogenerated non-trivial alloc: " << *TheMemory); // If it is safe to remove, do it. Recursively remove all instructions - // hanging off the allocation instruction, then return success. Let the - // caller remove the allocation itself to avoid iterator invalidation. - eraseUsesOfInstruction(TheMemory); + // hanging off the allocation instruction, then return success. + deleter.forceDeleteWithUsers(TheMemory); // Now look at all of our available values and complete any of their // post-dominating consuming use sets. This can happen if we have an enum that @@ -2647,6 +2652,13 @@ bool AllocOptimize::tryToRemoveDeadAllocation() { if (v.getOwnershipKind() != OwnershipKind::Owned) continue; + // FIXME: This doesn't handle situations where non-consuming uses are in + // blocks in which the consuming use is already deleted. Replace the + // following code with an general OSSA utility for owned lifetime + // fixup. ValueLifetimeAnalysis does this easily, but we also need to handle + // reborrows by adding copies. An OwnershipOptUtils utility will handle + // that soon. + // First see if our value doesn't have any uses. In such a case, just // insert a destroy_value at the next instruction and return. if (v->use_empty()) { @@ -2838,13 +2850,12 @@ static bool optimizeMemoryAccesses(SILFunction &fn) { bool changed = false; DeadEndBlocks deadEndBlocks(&fn); + InstructionDeleter deleter; for (auto &bb : fn) { - auto i = bb.begin(), e = bb.end(); - while (i != e) { + for (SILInstruction *inst : deleter.updatingRange(&bb)) { // First see if i is an allocation that we can optimize. If not, skip it. - AllocationInst *alloc = getOptimizableAllocation(&*i); + AllocationInst *alloc = getOptimizableAllocation(inst); if (!alloc) { - ++i; continue; } @@ -2860,16 +2871,16 @@ static bool optimizeMemoryAccesses(SILFunction &fn) { // to optimize, skip this value. *NOTE* We may still scalarize values // inside the value. if (!collectPMOElementUsesFrom(memInfo, uses, destroys)) { - ++i; + // Avoid advancing this iterator until after collectPMOElementUsesFrom() + // runs. It creates and deletes instructions other than alloc. continue; } - - AllocOptimize allocOptimize(alloc, uses, destroys, deadEndBlocks); + AllocOptimize allocOptimize(alloc, uses, destroys, deadEndBlocks, + deleter); changed |= allocOptimize.optimizeMemoryAccesses(); // Move onto the next instruction. We know this is safe since we do not // eliminate allocations here. - ++i; } } @@ -2881,12 +2892,11 @@ static bool eliminateDeadAllocations(SILFunction &fn) { DeadEndBlocks deadEndBlocks(&fn); for (auto &bb : fn) { - auto i = bb.begin(), e = bb.end(); - while (i != e) { + InstructionDeleter deleter; + for (SILInstruction *inst : deleter.updatingRange(&bb)) { // First see if i is an allocation that we can optimize. If not, skip it. - AllocationInst *alloc = getOptimizableAllocation(&*i); + AllocationInst *alloc = getOptimizableAllocation(inst); if (!alloc) { - ++i; continue; } @@ -2903,24 +2913,17 @@ static bool eliminateDeadAllocations(SILFunction &fn) { // to optimize, skip this value. *NOTE* We may still scalarize values // inside the value. if (!collectPMOElementUsesFrom(memInfo, uses, destroys)) { - ++i; continue; } - - AllocOptimize allocOptimize(alloc, uses, destroys, deadEndBlocks); - changed |= allocOptimize.tryToRemoveDeadAllocation(); - - // Move onto the next instruction. We know this is safe since we do not - // eliminate allocations here. - ++i; - if (alloc->use_empty()) { - alloc->eraseFromParent(); + AllocOptimize allocOptimize(alloc, uses, destroys, deadEndBlocks, + deleter); + if (allocOptimize.tryToRemoveDeadAllocation()) { + deleter.cleanupDeadInstructions(); ++NumAllocRemoved; changed = true; } } } - return changed; } diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index 31829e7bdd4ec..f05dbddc2f66f 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -16,10 +16,13 @@ #include "swift/AST/SILOptimizerRequests.h" #include "swift/Demangling/Demangle.h" #include "swift/SIL/ApplySite.h" +#include "swift/SIL/SILBridgingUtils.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILModule.h" +#include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h" #include "swift/SILOptimizer/Analysis/FunctionOrder.h" +#include "swift/SILOptimizer/OptimizerBridging.h" #include "swift/SILOptimizer/PassManager/PrettyStackTrace.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/OptimizerStatsUtils.h" @@ -318,15 +321,15 @@ void swift::executePassPipelinePlan(SILModule *SM, SILPassManager::SILPassManager(SILModule *M, bool isMandatory, irgen::IRGenModule *IRMod) - : Mod(M), IRMod(IRMod), isMandatory(isMandatory), - deserializationNotificationHandler(nullptr) { + : Mod(M), IRMod(IRMod), + libswiftPassInvocation(this), + isMandatory(isMandatory), deserializationNotificationHandler(nullptr) { #define ANALYSIS(NAME) \ Analyses.push_back(create##NAME##Analysis(Mod)); #include "swift/SILOptimizer/Analysis/Analysis.def" for (SILAnalysis *A : Analyses) { A->initialize(this); - M->registerDeleteNotificationHandler(A); } std::unique_ptr handler( @@ -455,7 +458,6 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { } llvm::sys::TimePoint<> StartTime = std::chrono::system_clock::now(); - Mod->registerDeleteNotificationHandler(SFT); if (breakBeforeRunning(F->getName(), SFT)) LLVM_BUILTIN_DEBUGTRAP; if (SILForceVerifyAll || @@ -464,7 +466,21 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { SILForceVerifyAroundPass.end(), MatchFun)) { forcePrecomputeAnalyses(F); } + + assert(changeNotifications == SILAnalysis::InvalidationKind::Nothing + && "change notifications not cleared"); + + libswiftPassInvocation.startPassRun(F); + + // Run it! SFT->run(); + + if (changeNotifications != SILAnalysis::InvalidationKind::Nothing) { + invalidateAnalysis(F, changeNotifications); + changeNotifications = SILAnalysis::InvalidationKind::Nothing; + } + libswiftPassInvocation.finishedPassRun(); + if (SILForceVerifyAll || SILForceVerifyAroundPass.end() != std::find_if(SILForceVerifyAroundPass.begin(), @@ -472,7 +488,7 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { verifyAnalyses(F); } assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); - Mod->removeDeleteNotificationHandler(SFT); + Mod->flushDeletedInsts(); auto Delta = (std::chrono::system_clock::now() - StartTime).count(); if (SILPrintPassTime) { @@ -615,10 +631,9 @@ void SILPassManager::runModulePass(unsigned TransIdx) { llvm::sys::TimePoint<> StartTime = std::chrono::system_clock::now(); assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); - Mod->registerDeleteNotificationHandler(SMT); SMT->run(); - Mod->removeDeleteNotificationHandler(SMT); assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); + Mod->flushDeletedInsts(); auto Delta = (std::chrono::system_clock::now() - StartTime).count(); if (SILPrintPassTime) { @@ -731,7 +746,6 @@ SILPassManager::~SILPassManager() { // delete the analysis. for (auto *A : Analyses) { - Mod->removeDeleteNotificationHandler(A); assert(!A->isLocked() && "Deleting a locked analysis. Did we forget to unlock ?"); delete A; @@ -1063,3 +1077,117 @@ void SILPassManager::viewCallGraph() { llvm::ViewGraph(&OCG, "callgraph"); #endif } + +//===----------------------------------------------------------------------===// +// LibswiftPassInvocation +//===----------------------------------------------------------------------===// + +static_assert(BridgedSlabCapacity == FixedSizeSlab::capacity, + "wrong bridged slab capacity"); + +FixedSizeSlab *LibswiftPassInvocation::allocSlab(FixedSizeSlab *afterSlab) { + FixedSizeSlab *slab = passManager->getModule()->allocSlab(); + if (afterSlab) { + allocatedSlabs.insert(std::next(afterSlab->getIterator()), *slab); + } else { + allocatedSlabs.push_back(*slab); + } + return slab; +} + +FixedSizeSlab *LibswiftPassInvocation::freeSlab(FixedSizeSlab *slab) { + FixedSizeSlab *prev = std::prev(&*slab->getIterator()); + allocatedSlabs.remove(*slab); + passManager->getModule()->freeSlab(slab); + return prev; +} + +void LibswiftPassInvocation::startPassRun(SILFunction *function) { + assert(!this->function && "a pass is already running"); + this->function = function; +} + +void LibswiftPassInvocation::finishedPassRun() { + assert(allocatedSlabs.empty() && "StackList is leaking slabs"); + function = nullptr; +} + +//===----------------------------------------------------------------------===// +// Swift Bridging +//===----------------------------------------------------------------------===// + +inline LibswiftPassInvocation *castToPassInvocation(BridgedPassContext ctxt) { + return const_cast( + static_cast(ctxt.opaqueCtxt)); +} + +inline FixedSizeSlab *castToSlab(BridgedSlab slab) { + if (slab.data) + return static_cast((FixedSizeSlabPayload *)slab.data); + return nullptr; +} + +inline BridgedSlab toBridgedSlab(FixedSizeSlab *slab) { + if (slab) { + FixedSizeSlabPayload *payload = slab; + assert((void *)payload == slab->dataFor()); + return {payload}; + } + return {nullptr}; +} + + +BridgedSlab PassContext_getNextSlab(BridgedSlab slab) { + return toBridgedSlab(&*std::next(castToSlab(slab)->getIterator())); +} + +BridgedSlab PassContext_allocSlab(BridgedPassContext passContext, + BridgedSlab afterSlab) { + auto *inv = castToPassInvocation(passContext); + return toBridgedSlab(inv->allocSlab(castToSlab(afterSlab))); +} + +BridgedSlab PassContext_freeSlab(BridgedPassContext passContext, + BridgedSlab slab) { + auto *inv = castToPassInvocation(passContext); + return toBridgedSlab(inv->freeSlab(castToSlab(slab))); +} + +void PassContext_notifyChanges(BridgedPassContext passContext, + enum ChangeNotificationKind changeKind) { + LibswiftPassInvocation *inv = castToPassInvocation(passContext); + switch (changeKind) { + case instructionsChanged: + inv->notifyChanges(SILAnalysis::InvalidationKind::Instructions); + break; + case callsChanged: + inv->notifyChanges(SILAnalysis::InvalidationKind::CallsAndInstructions); + break; + case branchesChanged: + inv->notifyChanges(SILAnalysis::InvalidationKind::BranchesAndInstructions); + break; + } +} + +void PassContext_eraseInstruction(BridgedPassContext passContext, + BridgedInstruction inst) { + castToPassInvocation(passContext)->eraseInstruction(castToInst(inst)); +} + +SwiftInt PassContext_isSwift51RuntimeAvailable(BridgedPassContext context) { + SILPassManager *pm = castToPassInvocation(context)->getPassManager(); + ASTContext &ctxt = pm->getModule()->getASTContext(); + return AvailabilityContext::forDeploymentTarget(ctxt). + isContainedIn(ctxt.getSwift51Availability()); +} + +BridgedAliasAnalysis PassContext_getAliasAnalysis(BridgedPassContext context) { + LibswiftPassInvocation *invocation = castToPassInvocation(context); + SILPassManager *pm = invocation->getPassManager(); + return {pm->getAnalysis(invocation->getFunction())}; +} + +BridgedCalleeAnalysis PassContext_getCalleeAnalysis(BridgedPassContext context) { + SILPassManager *pm = castToPassInvocation(context)->getPassManager(); + return {pm->getAnalysis()}; +} diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 67eb902abde76..d79e4addb2b9c 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -154,8 +154,12 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) { P.addDiagnoseInfiniteRecursion(); P.addYieldOnceCheck(); P.addEmitDFDiagnostics(); - P.addDiagnoseLifetimeIssues(); + // Only issue weak lifetime warnings for users who select object lifetime + // optimization. The risk of spurious warnings outweighs the benefits. + if (P.getOptions().EnableCopyPropagation) { + P.addDiagnoseLifetimeIssues(); + } // Canonical swift requires all non cond_br critical edges to be split. P.addSplitNonCondBrCriticalEdges(); } @@ -343,6 +347,12 @@ void addFunctionPasses(SILPassPipelinePlan &P, P.addCOWArrayOpts(); P.addDCE(); P.addSwiftArrayPropertyOpt(); + + // This string optimization can catch additional opportunities, which are + // exposed once optimized String interpolations (from the high-level string + // optimization) are cleaned up. But before the mid-level inliner inlines + // semantic calls. + P.addStringOptimization(); } // Run the devirtualizer, specializer, and inliner. If any of these @@ -728,8 +738,8 @@ static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) { // Only has an effect if the -assume-single-thread option is specified. P.addAssumeSingleThreaded(); - // Only has an effect if opt-remark is enabled. - P.addOptRemarkGenerator(); + // Emits remarks on all functions with @_assemblyVision attribute. + P.addAssemblyVisionRemarkGenerator(); // FIXME: rdar://72935649 (Miscompile on combining PruneVTables with WMO) // P.addPruneVTables(); @@ -828,7 +838,7 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) { addLastChanceOptPassPipeline(P); - // Has only an effect if the -gsil option is specified. + // Has only an effect if the -sil-based-debuginfo option is specified. addSILDebugInfoGeneratorPipeline(P); // Call the CFG viewer. @@ -879,7 +889,7 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { // Create pre-specializations. P.addOnonePrespecializations(); - // Has only an effect if the -gsil option is specified. + // Has only an effect if the -sil-based-debuginfo option is specified. P.addSILDebugInfoGenerator(); return P; diff --git a/lib/SILOptimizer/PassManager/Passes.cpp b/lib/SILOptimizer/PassManager/Passes.cpp index 2dba5d09dea12..338690f4536ca 100644 --- a/lib/SILOptimizer/PassManager/Passes.cpp +++ b/lib/SILOptimizer/PassManager/Passes.cpp @@ -25,7 +25,9 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Module.h" #include "swift/SIL/SILModule.h" +#include "swift/SIL/SILBridgingUtils.h" #include "swift/SILOptimizer/Analysis/Analysis.h" +#include "swift/SILOptimizer/OptimizerBridging.h" #include "swift/SILOptimizer/PassManager/PassManager.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" @@ -212,3 +214,66 @@ void swift::runSILLoweringPasses(SILModule &Module) { assert(Module.getStage() == SILStage::Lowered); } + +/// Registered briged pass run functions. +static llvm::StringMap bridgedPassRunFunctions; +static bool passesRegistered = false; + +/// Runs a bridged function pass. +/// +/// \p runFunction is a cache for the run function, so that it has to be looked +/// up only once in bridgedPassRunFunctions. +static void runBridgedFunctionPass(BridgedFunctionPassRunFn &runFunction, + SILPassManager *passManager, + SILFunction *f, StringRef passName) { + if (!runFunction) { + runFunction = bridgedPassRunFunctions[passName]; + if (!runFunction) { + if (passesRegistered) { + llvm::errs() << "Swift pass " << passName << " is not registered\n"; + abort(); + } + return; + } + } + if (!f->isBridged()) { + llvm::errs() << "SILFunction metatype is not registered\n"; + abort(); + } + runFunction({{f}, {passManager->getLibswiftPassInvocation()}}); +} + +// Called from libswift's initializeLibSwift(). +void SILPassManager_registerFunctionPass(BridgedStringRef name, + BridgedFunctionPassRunFn runFn) { + bridgedPassRunFunctions[getStringRef(name)] = runFn; + passesRegistered = true; +} + +#define SWIFT_FUNCTION_PASS_COMMON(ID, TAG) \ +class ID##Pass : public SILFunctionTransform { \ + static BridgedFunctionPassRunFn runFunction; \ + void run() override { \ + runBridgedFunctionPass(runFunction, PM, getFunction(), TAG); \ + } \ +}; \ +BridgedFunctionPassRunFn ID##Pass::runFunction = nullptr; \ + +#define PASS(ID, TAG, DESCRIPTION) +#define SWIFT_INSTRUCTION_PASS(INST, TAG) + +#define SWIFT_FUNCTION_PASS(ID, TAG, DESCRIPTION) \ +SWIFT_FUNCTION_PASS_COMMON(ID, TAG) \ +SILTransform *swift::create##ID() { return new ID##Pass(); } \ + +#define SWIFT_FUNCTION_PASS_WITH_LEGACY(ID, TAG, DESCRIPTION) \ +SWIFT_FUNCTION_PASS_COMMON(ID, TAG) \ +SILTransform *swift::create##ID() { \ + if (passesRegistered) \ + return new ID##Pass(); \ + return createLegacy##ID(); \ +} \ + +#include "swift/SILOptimizer/PassManager/Passes.def" + +#undef SWIFT_FUNCTION_PASS_COMMON diff --git a/lib/SILOptimizer/SILCombiner/SILCombine.cpp b/lib/SILOptimizer/SILCombiner/SILCombine.cpp index f254a2a80d919..eddc0e615cd1b 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombine.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombine.cpp @@ -23,15 +23,17 @@ #include "SILCombiner.h" #include "swift/SIL/BasicBlockDatastructures.h" #include "swift/SIL/DebugUtils.h" +#include "swift/SIL/SILBridgingUtils.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILVisitor.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h" #include "swift/SILOptimizer/Analysis/SimplifyInstruction.h" -#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/PassManager.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CanonicalOSSALifetime.h" +#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h" #include "swift/SILOptimizer/Utils/CanonicalizeInstruction.h" #include "swift/SILOptimizer/Utils/DebugOptUtils.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" @@ -175,7 +177,7 @@ bool SILCombiner::trySinkOwnedForwardingInst(SingleValueInstruction *svi) { // Otherwise, delete all of the debug uses so we don't have to sink them as // well and then return true so we process svi in its new position. - deleteAllDebugUses(svi, instModCallbacks); + deleteAllDebugUses(svi, getInstModCallbacks()); svi->moveBefore(consumingUser); MadeChange = true; @@ -216,6 +218,88 @@ bool SILCombiner::trySinkOwnedForwardingInst(SingleValueInstruction *svi) { return true; } +/// Canonicalize each extended OSSA lifetime that contains an instruction newly +/// created during this SILCombine iteration. +void SILCombiner::canonicalizeOSSALifetimes() { + if (!enableCopyPropagation || !Builder.hasOwnership()) + return; + + SmallSetVector defsToCanonicalize; + for (auto *trackedInst : *Builder.getTrackingList()) { + if (trackedInst->isDeleted()) + continue; + + if (auto *cvi = dyn_cast(trackedInst)) { + SILValue def = CanonicalizeOSSALifetime::getCanonicalCopiedDef(cvi); + + // getCanonicalCopiedDef returns a copy whenever that the copy's source is + // guaranteed. In that case, find the root of the borrowed lifetime. If it + // is a function argument, then a simple guaranteed canonicalization can + // be performed. Canonicalizing other borrow scopes is not handled by + // SILCombine because it's not a single-lifetime canonicalization. + // Instead, SILCombine treats a copy that uses a borrowed value as a + // separate owned live range. Handling the compensation code across the + // borrow scope boundary requires post processing in a particular order. + // The copy propagation pass knows how to handle that. To avoid complexity + // and ensure fast convergence, rewriting borrow scopes should not be + // combined with other unrelated transformations. + if (auto *copy = dyn_cast(def)) { + if (SILValue borrowDef = + CanonicalizeBorrowScope::getCanonicalBorrowedDef( + copy->getOperand())) { + if (isa(borrowDef)) { + def = borrowDef; + } + } + } + defsToCanonicalize.insert(def); + } + } + if (defsToCanonicalize.empty()) + return; + + // Remove instructions deleted during canonicalization from SILCombine's + // worklist. CanonicalizeOSSALifetime invalidates operands before invoking + // the deletion callback. + // + // Note: a simpler approach would be to drain the Worklist before + // canonicalizing OSSA, then callbacks could be completely removed from + // CanonicalizeOSSALifetime. + auto canonicalizeCallbacks = + InstModCallbacks().onDelete([this](SILInstruction *instToDelete) { + eraseInstFromFunction(*instToDelete, + false /*do not add operands to the worklist*/); + }); + InstructionDeleter deleter(std::move(canonicalizeCallbacks)); + + DominanceInfo *domTree = DA->get(&Builder.getFunction()); + CanonicalizeOSSALifetime canonicalizer( + false /*prune debug*/, false /*poison refs*/, NLABA, domTree, deleter); + CanonicalizeBorrowScope borrowCanonicalizer(deleter); + + while (!defsToCanonicalize.empty()) { + SILValue def = defsToCanonicalize.pop_back_val(); + if (auto functionArg = dyn_cast(def)) { + if (!borrowCanonicalizer.canonicalizeFunctionArgument(functionArg)) + continue; + } else if (!canonicalizer.canonicalizeValueLifetime(def)) { + continue; + } + MadeChange = true; + + // Canonicalization may rewrite many copies and destroys within a single + // extended lifetime, but the extended lifetime of each canonical def is + // non-overlapping. If def was successfully canonicalized, simply add it + // and its users to the SILCombine worklist. + if (auto *inst = def->getDefiningInstruction()) { + Worklist.add(inst); + } + for (auto *use : def->getUses()) { + Worklist.add(use->getUser()); + } + } +} + bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) { MadeChange = false; @@ -301,46 +385,22 @@ bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) { MadeChange = true; } - // Our tracking list has been accumulating instructions created by the - // SILBuilder during this iteration. In order to finish this round of - // SILCombine, go through the tracking list and add its contents to the - // worklist and then clear said list in preparation for the next - // iteration. We canonicalize any copies that we created in order to - // eliminate unnecessary copies introduced by RAUWing when ownership is - // enabled. - // - // NOTE: It is ok if copy propagation results in MadeChanges being set to - // true. This is because we only add elements to the tracking list if we - // actually made a change to the IR, so MadeChanges should already be true - // at this point. - auto &TrackingList = *Builder.getTrackingList(); - if (TrackingList.size() && Builder.hasOwnership()) { - SmallSetVector defsToCanonicalize; - for (auto *trackedInst : TrackingList) { - if (auto *cvi = dyn_cast(trackedInst)) { - defsToCanonicalize.insert( - CanonicalizeOSSALifetime::getCanonicalCopiedDef(cvi)); - } + // Eliminate copies created that this SILCombine iteration may have + // introduced during OSSA-RAUW. + canonicalizeOSSALifetimes(); + + // Builder's tracking list has been accumulating instructions created by the + // during this SILCombine iteration. To finish this iteration, go through + // the tracking list and add its contents to the worklist and then clear + // said list in preparation for the next iteration. + for (SILInstruction *I : *Builder.getTrackingList()) { + if (!I->isDeleted()) { + LLVM_DEBUG(llvm::dbgs() + << "SC: add " << *I << " from tracking list to worklist\n"); + Worklist.add(I); } - if (defsToCanonicalize.size()) { - CanonicalizeOSSALifetime canonicalizer( - false /*prune debug*/, false /*canonicalize borrows*/, - false /*poison refs*/, NLABA, DA, instModCallbacks); - auto analysisInvalidation = canonicalizeOSSALifetimes( - canonicalizer, defsToCanonicalize.getArrayRef()); - if (bool(analysisInvalidation)) { - NLABA->lockInvalidation(); - parentTransform->invalidateAnalysis(analysisInvalidation); - NLABA->unlockInvalidation(); - } - } - } - for (SILInstruction *I : TrackingList) { - LLVM_DEBUG(llvm::dbgs() << "SC: add " << *I - << " from tracking list to worklist\n"); - Worklist.add(I); } - TrackingList.clear(); + Builder.getTrackingList()->clear(); } Worklist.resetChecked(); @@ -374,6 +434,56 @@ void SILCombiner::eraseInstIncludingUsers(SILInstruction *inst) { eraseInstFromFunction(*inst); } +/// Runs an instruction pass in libswift. +void SILCombiner::runSwiftInstructionPass(SILInstruction *inst, + void (*runFunction)(BridgedInstructionPassCtxt)) { + Worklist.setLibswiftPassInvocation(&libswiftPassInvocation); + runFunction({ {inst->asSILNode()}, {&libswiftPassInvocation} }); + Worklist.setLibswiftPassInvocation(nullptr); + libswiftPassInvocation.finishedPassRun(); +} + +/// Registered briged instruction pass run functions. +static llvm::StringMap libswiftInstPasses; +static bool passesRegistered = false; + +// Called from libswift's initializeLibSwift(). +void SILCombine_registerInstructionPass(BridgedStringRef name, + BridgedInstructionPassRunFn runFn) { + libswiftInstPasses[getStringRef(name)] = runFn; + passesRegistered = true; +} + +#define SWIFT_INSTRUCTION_PASS_COMMON(INST, TAG, LEGACY_RUN) \ +SILInstruction *SILCombiner::visit##INST(INST *inst) { \ + static BridgedInstructionPassRunFn runFunction = nullptr; \ + static bool runFunctionSet = false; \ + if (!runFunctionSet) { \ + runFunction = libswiftInstPasses[TAG]; \ + if (!runFunction && passesRegistered) { \ + llvm::errs() << "Swift pass " << TAG << " is not registered\n"; \ + abort(); \ + } \ + runFunctionSet = true; \ + } \ + if (!runFunction) { \ + LEGACY_RUN; \ + } \ + runSwiftInstructionPass(inst, runFunction); \ + return nullptr; \ +} \ + +#define PASS(ID, TAG, DESCRIPTION) + +#define SWIFT_INSTRUCTION_PASS(INST, TAG) \ + SWIFT_INSTRUCTION_PASS_COMMON(INST, TAG, { return nullptr; }) + +#define SWIFT_INSTRUCTION_PASS_WITH_LEGACY(INST, TAG) \ + SWIFT_INSTRUCTION_PASS_COMMON(INST, TAG, { return legacyVisit##INST(inst); }) + +#include "swift/SILOptimizer/PassManager/Passes.def" + +#undef SWIFT_INSTRUCTION_PASS_COMMON //===----------------------------------------------------------------------===// // Entry Points @@ -387,18 +497,24 @@ class SILCombine : public SILFunctionTransform { /// The entry point to the transformation. void run() override { - auto *AA = PM->getAnalysis(); + auto *AA = PM->getAnalysis(getFunction()); auto *DA = PM->getAnalysis(); auto *PCA = PM->getAnalysis(); auto *CHA = PM->getAnalysis(); auto *NLABA = PM->getAnalysis(); + bool enableCopyPropagation = getOptions().EnableCopyPropagation; + if (getOptions().EnableOSSAModules) { + enableCopyPropagation = !getOptions().DisableCopyPropagation; + } + SILOptFunctionBuilder FuncBuilder(*this); // Create a SILBuilder with a tracking list for newly added // instructions, which we will periodically move to our worklist. SILBuilder B(*getFunction(), &TrackingList); SILCombiner Combiner(this, FuncBuilder, B, AA, DA, PCA, CHA, NLABA, - getOptions().RemoveRuntimeAsserts); + getOptions().RemoveRuntimeAsserts, + enableCopyPropagation); bool Changed = Combiner.runOnFunction(*getFunction()); assert(TrackingList.empty() && "TrackingList should be fully processed by SILCombiner"); @@ -408,20 +524,6 @@ class SILCombine : public SILFunctionTransform { invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); } } - - void handleDeleteNotification(SILNode *node) override { - auto I = dyn_cast(node); - if (!I) return; - - // Linear searching the tracking list doesn't hurt because usually it only - // contains a few elements. - auto Iter = std::find(TrackingList.begin(), TrackingList.end(), I); - if (Iter != TrackingList.end()) - TrackingList.erase(Iter); - } - - bool needsNotifications() override { return true; } - }; } // end anonymous namespace @@ -429,3 +531,15 @@ class SILCombine : public SILFunctionTransform { SILTransform *swift::createSILCombine() { return new SILCombine(); } + +//===----------------------------------------------------------------------===// +// SwiftFunctionPassContext +//===----------------------------------------------------------------------===// + +void LibswiftPassInvocation::eraseInstruction(SILInstruction *inst) { + if (silCombiner) { + silCombiner->eraseInstFromFunction(*inst); + } else { + inst->eraseFromParent(); + } +} diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index 5caa4f15af056..f563aab945744 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -28,13 +28,17 @@ #include "swift/SIL/SILInstructionWorklist.h" #include "swift/SIL/SILValue.h" #include "swift/SIL/SILVisitor.h" +#include "swift/SIL/InstructionUtils.h" #include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h" #include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h" #include "swift/SILOptimizer/Analysis/ProtocolConformanceAnalysis.h" +#include "swift/SILOptimizer/OptimizerBridging.h" #include "swift/SILOptimizer/Utils/CastOptimizer.h" #include "swift/SILOptimizer/Utils/Existential.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/OwnershipOptUtils.h" +#include "swift/SILOptimizer/PassManager/PassManager.h" + #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -68,6 +72,9 @@ class SILCombiner : /// Worklist containing all of the instructions primed for simplification. SmallSILInstructionWorklist<256> Worklist; + /// Utility for dead code removal. + InstructionDeleter deleter; + /// A cache of "dead end blocks" through which all paths it is known that the /// program will terminate. This means that we are allowed to leak /// objects. @@ -79,6 +86,9 @@ class SILCombiner : /// If set to true then the optimizer is free to erase cond_fail instructions. bool RemoveCondFails; + /// If set to true then copies are canonicalized in OSSA mode. + bool enableCopyPropagation; + /// Set to true if some alloc/dealloc_stack instruction are inserted and at /// the end of the run stack nesting needs to be corrected. bool invalidatedStackNesting = false; @@ -92,59 +102,64 @@ class SILCombiner : /// Cast optimizer CastOptimizer CastOpt; - /// Centralized InstModCallback that we use for certain utility methods. - InstModCallbacks instModCallbacks; - /// Dead end blocks cache. SILCombine is already not allowed to mess with CFG /// edges so it is safe to use this here. DeadEndBlocks deBlocks; /// External context struct used by \see ownershipRAUWHelper. OwnershipFixupContext ownershipFixupContext; + + /// For invoking Swift instruction passes in libswift. + LibswiftPassInvocation libswiftPassInvocation; public: SILCombiner(SILFunctionTransform *parentTransform, SILOptFunctionBuilder &FuncBuilder, SILBuilder &B, AliasAnalysis *AA, DominanceAnalysis *DA, ProtocolConformanceAnalysis *PCA, ClassHierarchyAnalysis *CHA, - NonLocalAccessBlockAnalysis *NLABA, bool removeCondFails) + NonLocalAccessBlockAnalysis *NLABA, bool removeCondFails, + bool enableCopyPropagation) : parentTransform(parentTransform), AA(AA), DA(DA), PCA(PCA), CHA(CHA), - NLABA(NLABA), Worklist("SC"), deadEndBlocks(&B.getFunction()), - MadeChange(false), RemoveCondFails(removeCondFails), Iteration(0), - Builder(B), CastOpt( - FuncBuilder, nullptr /*SILBuilderContext*/, - /* ReplaceValueUsesAction */ - [&](SILValue Original, SILValue Replacement) { - replaceValueUsesWith(Original, Replacement); - }, - /* ReplaceInstUsesAction */ - [&](SingleValueInstruction *I, ValueBase *V) { - replaceInstUsesWith(*I, V); - }, - /* EraseAction */ - [&](SILInstruction *I) { eraseInstFromFunction(*I); }), - instModCallbacks(), deBlocks(&B.getFunction()), - ownershipFixupContext(instModCallbacks, deBlocks) { - instModCallbacks = - InstModCallbacks() - .onDelete([&](SILInstruction *instToDelete) { - // We allow for users in SILCombine to perform 2 stage deletion, - // so we need to split the erasing of instructions from adding - // operands to the worklist. - eraseInstFromFunction( - *instToDelete, false /*do not add operands to the worklist*/); - }) - .onNotifyWillBeDeleted([&](SILInstruction *instThatWillBeDeleted) { - Worklist.addOperandsToWorklist(*instThatWillBeDeleted); - }) - .onCreateNewInst([&](SILInstruction *newlyCreatedInst) { - Worklist.add(newlyCreatedInst); - }) - .onSetUseValue([&](Operand *use, SILValue newValue) { - use->set(newValue); - Worklist.add(use->getUser()); - }); - } + NLABA(NLABA), Worklist("SC"), + deleter(InstModCallbacks() + .onDelete([&](SILInstruction *instToDelete) { + // We allow for users in SILCombine to perform 2 stage + // deletion, so we need to split the erasing of + // instructions from adding operands to the worklist. + eraseInstFromFunction(*instToDelete, + false /* don't add operands */); + }) + .onNotifyWillBeDeleted( + [&](SILInstruction *instThatWillBeDeleted) { + Worklist.addOperandsToWorklist( + *instThatWillBeDeleted); + }) + .onCreateNewInst([&](SILInstruction *newlyCreatedInst) { + Worklist.add(newlyCreatedInst); + }) + .onSetUseValue([&](Operand *use, SILValue newValue) { + use->set(newValue); + Worklist.add(use->getUser()); + })), + deadEndBlocks(&B.getFunction()), MadeChange(false), + RemoveCondFails(removeCondFails), + enableCopyPropagation(enableCopyPropagation), Iteration(0), Builder(B), + CastOpt( + FuncBuilder, nullptr /*SILBuilderContext*/, + /* ReplaceValueUsesAction */ + [&](SILValue Original, SILValue Replacement) { + replaceValueUsesWith(Original, Replacement); + }, + /* ReplaceInstUsesAction */ + [&](SingleValueInstruction *I, ValueBase *V) { + replaceInstUsesWith(*I, V); + }, + /* EraseAction */ + [&](SILInstruction *I) { eraseInstFromFunction(*I); }), + deBlocks(&B.getFunction()), + ownershipFixupContext(getInstModCallbacks(), deBlocks), + libswiftPassInvocation(parentTransform->getPassManager(), + parentTransform->getFunction(), this) {} bool runOnFunction(SILFunction &F); @@ -264,7 +279,7 @@ class SILCombiner : SILInstruction *optimizeStringObject(BuiltinInst *BI); SILInstruction *visitBuiltinInst(BuiltinInst *BI); SILInstruction *visitCondFailInst(CondFailInst *CFI); - SILInstruction *visitStrongRetainInst(StrongRetainInst *SRI); + SILInstruction *legacyVisitStrongRetainInst(StrongRetainInst *SRI); SILInstruction *visitCopyValueInst(CopyValueInst *cvi); SILInstruction *visitDestroyValueInst(DestroyValueInst *dvi); SILInstruction *visitRefToRawPointerInst(RefToRawPointerInst *RRPI); @@ -294,7 +309,7 @@ class SILCombiner : SILInstruction *visitRawPointerToRefInst(RawPointerToRefInst *RPTR); SILInstruction * visitUncheckedTakeEnumDataAddrInst(UncheckedTakeEnumDataAddrInst *TEDAI); - SILInstruction *visitStrongReleaseInst(StrongReleaseInst *SRI); + SILInstruction *legacyVisitStrongReleaseInst(StrongReleaseInst *SRI); SILInstruction *visitCondBranchInst(CondBranchInst *CBI); SILInstruction * visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI); @@ -317,11 +332,18 @@ class SILCombiner : SILInstruction *visitMarkDependenceInst(MarkDependenceInst *MDI); SILInstruction *visitClassifyBridgeObjectInst(ClassifyBridgeObjectInst *CBOI); - SILInstruction *visitGlobalValueInst(GlobalValueInst *globalValue); SILInstruction *visitConvertFunctionInst(ConvertFunctionInst *CFI); SILInstruction * visitConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *Cvt); + SILInstruction *legacyVisitGlobalValueInst(GlobalValueInst *globalValue); + +#define PASS(ID, TAG, DESCRIPTION) +#define SWIFT_FUNCTION_PASS(ID, TAG, DESCRIPTION) +#define SWIFT_INSTRUCTION_PASS(INST, TAG) \ + SILInstruction *visit##INST(INST *); +#include "swift/SILOptimizer/PassManager/Passes.def" + /// Instruction visitor helpers. SILInstruction *optimizeBuiltinCanBeObjCClass(BuiltinInst *AI); @@ -360,6 +382,10 @@ class SILCombiner : /// try to visit it. bool trySinkOwnedForwardingInst(SingleValueInstruction *svi); + /// Apply CanonicalizeOSSALifetime to the extended lifetime of any copy + /// introduced during SILCombine for an owned value. + void canonicalizeOSSALifetimes(); + // Optimize concatenation of string literals. // Constant-fold concatenation of string literals known at compile-time. SILInstruction *optimizeConcatenationOfStringLiterals(ApplyInst *AI); @@ -381,7 +407,7 @@ class SILCombiner : SingleValueInstruction *user, SingleValueInstruction *value, function_ref newValueGenerator); - InstModCallbacks &getInstModCallbacks() { return instModCallbacks; } + InstModCallbacks &getInstModCallbacks() { return deleter.getCallbacks(); } private: // Build concrete existential information using findInitExistential. @@ -452,6 +478,10 @@ class SILCombiner : bool hasOwnership() const { return Builder.hasOwnership(); } + + void runSwiftInstructionPass(SILInstruction *inst, + void (*runFunction)(BridgedInstructionPassCtxt)); + }; } // end namespace swift diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index e93cb8083ae89..5db74a3e9f1b2 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -1199,11 +1199,16 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // apply. Since the apply was never rewritten, if they aren't removed here, // they will be removed later as dead when visited by SILCombine, causing // SILCombine to loop infinitely, creating and destroying the casts. + // + // Use a new deleter with no callbacks so we can pretend this never + // happened. Otherwise SILCombine will infinitely iterate. This works as + // long as the instructions in this tracking list were never added to the + // SILCombine Worklist. InstructionDeleter deleter; for (SILInstruction *inst : *Builder.getTrackingList()) { deleter.trackIfDead(inst); } - deleter.cleanUpDeadInstructions(); + deleter.cleanupDeadInstructions(); Builder.getTrackingList()->clear(); return nullptr; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp index e383ebe3bf46a..fd57c2b0bf4d3 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp @@ -185,6 +185,7 @@ SILCombiner::optimizeBuiltinCOWBufferForReadingOSSA(BuiltinInst *bi) { cast(intPtrOperand->getUser())->setImmutable(); return; case InteriorPointerOperandKind::OpenExistentialBox: + case InteriorPointerOperandKind::ProjectBox: case InteriorPointerOperandKind::StoreBorrow: // Can not mark this immutable. return; diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index 9aff21b4ea9d7..41a7f377785b1 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -1024,7 +1024,7 @@ SILCombiner::visitConvertFunctionInst(ConvertFunctionInst *cfi) { SILValue newValue = cfi->getConverted(); if (newValue.getOwnershipKind() != OwnershipKind::Owned && newValue.getOwnershipKind() != OwnershipKind::Guaranteed) { - instModCallbacks.setUseValue(use, newValue); + getInstModCallbacks().setUseValue(use, newValue); fas.setSubstCalleeType(newValue->getType().castTo()); continue; } @@ -1112,6 +1112,6 @@ SILCombiner::visitConvertFunctionInst(ConvertFunctionInst *cfi) { // Replace a convert_function that only has refcounting uses with its // operand. - tryEliminateOnlyOwnershipUsedForwardingInst(cfi, instModCallbacks); + tryEliminateOnlyOwnershipUsedForwardingInst(cfi, getInstModCallbacks()); return nullptr; } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 3fc1c420846e0..b078389b754f9 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -508,12 +508,15 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { for (auto *use : AS->getUses()) { SILInstruction *user = use->getUser(); switch (user->getKind()) { - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::DeallocStackInst: case SILInstructionKind::InjectEnumAddrInst: // We'll check init_enum_addr below. break; + case SILInstructionKind::DebugValueInst: + if (DebugValueInst::hasAddrVal(user)) + break; + return false; case SILInstructionKind::InitEnumDataAddrInst: { auto *ieda = cast(user); auto *el = ieda->getElement(); @@ -569,7 +572,6 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { SILInstruction *user = use->getUser(); switch (user->getKind()) { case SILInstructionKind::InjectEnumAddrInst: - case SILInstructionKind::DebugValueAddrInst: eraseInstFromFunction(*user); break; case SILInstructionKind::DestroyAddrInst: @@ -599,6 +601,12 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { eraseInstFromFunction(*svi); break; } + case SILInstructionKind::DebugValueInst: + if (DebugValueInst::hasAddrVal(user)) { + eraseInstFromFunction(*user); + break; + } + LLVM_FALLTHROUGH; default: llvm_unreachable("unexpected alloc_stack user"); } @@ -1213,7 +1221,7 @@ SILInstruction *SILCombiner::visitDestroyValueInst(DestroyValueInst *dvi) { return nullptr; } -SILInstruction *SILCombiner::visitStrongRetainInst(StrongRetainInst *SRI) { +SILInstruction *SILCombiner::legacyVisitStrongRetainInst(StrongRetainInst *SRI) { assert(!SRI->getFunction()->hasOwnership()); // Retain of ThinToThickFunction is a no-op. @@ -1599,9 +1607,11 @@ SILCombiner::visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) { Builder.createStore(DataAddrInst->getLoc(), E, DataAddrInst->getOperand(), StoreOwnershipQualifier::Unqualified); // Cleanup. - eraseUsesOfInstruction(DataAddrInst); - recursivelyDeleteTriviallyDeadInstructions(DataAddrInst, true); - return eraseInstFromFunction(*IEAI); + getInstModCallbacks().notifyWillBeDeleted(DataAddrInst); + deleter.forceDeleteWithUsers(DataAddrInst); + deleter.forceDelete(IEAI); + deleter.cleanupDeadInstructions(); + return nullptr; } // Check whether we have an apply initializing the enum. @@ -1819,7 +1829,7 @@ SILInstruction *SILCombiner::visitUncheckedTakeEnumDataAddrInst( return eraseInstFromFunction(*tedai); } -SILInstruction *SILCombiner::visitStrongReleaseInst(StrongReleaseInst *SRI) { +SILInstruction *SILCombiner::legacyVisitStrongReleaseInst(StrongReleaseInst *SRI) { assert(!SRI->getFunction()->hasOwnership()); // Release of ThinToThickFunction is a no-op. @@ -2208,7 +2218,8 @@ SILInstruction *SILCombiner::visitMarkDependenceInst(MarkDependenceInst *mdi) { use, eiBase->getOperand()); if (helper) { helper.perform(); - tryEliminateOnlyOwnershipUsedForwardingInst(eiBase, instModCallbacks); + tryEliminateOnlyOwnershipUsedForwardingInst(eiBase, + getInstModCallbacks()); return mdi; } } @@ -2222,7 +2233,7 @@ SILInstruction *SILCombiner::visitMarkDependenceInst(MarkDependenceInst *mdi) { use, ier->getOperand()); if (helper) { helper.perform(); - tryEliminateOnlyOwnershipUsedForwardingInst(ier, instModCallbacks); + tryEliminateOnlyOwnershipUsedForwardingInst(ier, getInstModCallbacks()); return mdi; } } @@ -2235,7 +2246,7 @@ SILInstruction *SILCombiner::visitMarkDependenceInst(MarkDependenceInst *mdi) { use, oeri->getOperand()); if (helper) { helper.perform(); - tryEliminateOnlyOwnershipUsedForwardingInst(oeri, instModCallbacks); + tryEliminateOnlyOwnershipUsedForwardingInst(oeri, getInstModCallbacks()); return mdi; } } @@ -2309,7 +2320,7 @@ static bool checkGlobalValueUsers(SILValue val, } SILInstruction * -SILCombiner::visitGlobalValueInst(GlobalValueInst *globalValue) { +SILCombiner::legacyVisitGlobalValueInst(GlobalValueInst *globalValue) { // Delete all reference count instructions on a global_value if the only other // users are projections (ref_element_addr and ref_tail_addr). SmallVector toDelete; diff --git a/lib/SILOptimizer/SemanticARC/Context.cpp b/lib/SILOptimizer/SemanticARC/Context.cpp index d0811fd244dba..fb92dcf41391f 100644 --- a/lib/SILOptimizer/SemanticARC/Context.cpp +++ b/lib/SILOptimizer/SemanticARC/Context.cpp @@ -38,16 +38,20 @@ void Context::verify() const { bool Context::constructCacheValue( SILValue initialValue, SmallVectorImpl &wellBehavedWriteAccumulator) { - SmallVector worklist(initialValue->getUses()); + SmallVector worklist(initialValue->getNonTypeDependentUses()); while (!worklist.empty()) { auto *op = worklist.pop_back_val(); + assert(!op->isTypeDependent() && + "Uses that are type dependent should have been filtered before " + "being inserted into the worklist"); SILInstruction *user = op->getUser(); if (Projection::isAddressProjection(user) || isa(user)) { for (SILValue r : user->getResults()) { - llvm::copy(r->getUses(), std::back_inserter(worklist)); + llvm::copy(r->getNonTypeDependentUses(), + std::back_inserter(worklist)); } continue; } @@ -59,7 +63,8 @@ bool Context::constructCacheValue( } // Otherwise, look through it and continue. - llvm::copy(oeai->getUses(), std::back_inserter(worklist)); + llvm::copy(oeai->getNonTypeDependentUses(), + std::back_inserter(worklist)); continue; } @@ -93,7 +98,8 @@ bool Context::constructCacheValue( } // And then add the users to the worklist and continue. - llvm::copy(bai->getUses(), std::back_inserter(worklist)); + llvm::copy(bai->getNonTypeDependentUses(), + std::back_inserter(worklist)); continue; } diff --git a/lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp b/lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp index 9fa3c0fcfa3ac..8788be34ac113 100644 --- a/lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp @@ -473,7 +473,7 @@ static bool tryJoinIfDestroyConsumingUseInSameBlock( // we need to only find scopes that end within the region in between the // singleConsumingUse (the original forwarded use) and the destroy_value. In // such a case, we must bail! - if (auto operand = BorrowingOperand::get(use)) + if (auto operand = BorrowingOperand(use)) if (!operand.visitScopeEndingUses([&](Operand *endScopeUse) { // Return false if we did see the relevant end scope instruction // in the block. That means that we are going to exit early and diff --git a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp index f2d71a40c4d64..bf13d0976321e 100644 --- a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp +++ b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp @@ -283,6 +283,7 @@ static SILValue convertIntroducerToGuaranteed(OwnedValueIntroducer introducer) { case OwnedValueIntroducerKind::AllocRefInit: return SILValue(); } + llvm_unreachable("covered switch"); } void OwnershipLiveRange::convertJoinedLiveRangePhiToGuaranteed( diff --git a/lib/SILOptimizer/Transforms/ARCCodeMotion.cpp b/lib/SILOptimizer/Transforms/ARCCodeMotion.cpp index 9baaf56dea80c..a2528514e90db 100644 --- a/lib/SILOptimizer/Transforms/ARCCodeMotion.cpp +++ b/lib/SILOptimizer/Transforms/ARCCodeMotion.cpp @@ -76,7 +76,6 @@ #include "swift/SIL/BasicBlockData.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" -#include "swift/SILOptimizer/Analysis/EscapeAnalysis.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" #include "swift/SILOptimizer/Analysis/ProgramTerminationAnalysis.h" #include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" @@ -1189,7 +1188,7 @@ class ARCCodeMotion : public SILFunctionTransform { POA->invalidateFunction(F); auto *PO = POA->get(F); - auto *AA = PM->getAnalysis(); + auto *AA = PM->getAnalysis(F); auto *RCFI = PM->getAnalysis()->get(F); llvm::SpecificBumpPtrAllocator BPA; diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp index 810c45aa11769..368e87e844082 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp @@ -428,7 +428,7 @@ BeginAccessInst *AccessConflictAndMergeAnalysis::findMergeableOutOfScopeAccess( auto identicalStorageIter = llvm::find_if( state.outOfScopeConflictFreeAccesses, [&](BeginAccessInst *bai) { auto storageInfo = result.getAccessInfo(bai); - return storageInfo.hasIdenticalBase(currStorageInfo); + return storageInfo.hasIdenticalStorage(currStorageInfo); }); if (identicalStorageIter == state.outOfScopeConflictFreeAccesses.end()) return nullptr; @@ -484,7 +484,7 @@ void AccessConflictAndMergeAnalysis::insertOutOfScopeAccess( auto identicalStorageIter = llvm::find_if( state.outOfScopeConflictFreeAccesses, [&](BeginAccessInst *bai) { auto storageInfo = result.getAccessInfo(bai); - return storageInfo.hasIdenticalBase(currStorageInfo); + return storageInfo.hasIdenticalStorage(currStorageInfo); }); if (identicalStorageIter == state.outOfScopeConflictFreeAccesses.end()) state.outOfScopeConflictFreeAccesses.insert(beginAccess); diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp index 44374ff3f366a..371a702885f44 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp @@ -145,6 +145,7 @@ static bool isBarrier(SILInstruction *inst) { case BuiltinValueKind::GetCurrentExecutor: case BuiltinValueKind::AutoDiffCreateLinearMapContext: case BuiltinValueKind::EndAsyncLet: + case BuiltinValueKind::EndAsyncLetLifetime: case BuiltinValueKind::CreateTaskGroup: case BuiltinValueKind::DestroyTaskGroup: return false; @@ -173,11 +174,14 @@ static bool isBarrier(SILInstruction *inst) { case BuiltinValueKind::UnsafeGuaranteedEnd: case BuiltinValueKind::CancelAsyncTask: case BuiltinValueKind::StartAsyncLet: - case BuiltinValueKind::CreateAsyncTaskFuture: - case BuiltinValueKind::CreateAsyncTaskGroupFuture: + case BuiltinValueKind::CreateAsyncTask: + case BuiltinValueKind::CreateAsyncTaskInGroup: + case BuiltinValueKind::StartAsyncLetWithLocalBuffer: case BuiltinValueKind::ConvertTaskToJob: case BuiltinValueKind::InitializeDefaultActor: case BuiltinValueKind::DestroyDefaultActor: + case BuiltinValueKind::InitializeDistributedRemoteActor: + case BuiltinValueKind::DestroyDistributedActor: case BuiltinValueKind::BuildOrdinarySerialExecutorRef: case BuiltinValueKind::BuildDefaultActorExecutorRef: case BuiltinValueKind::BuildMainActorExecutorRef: diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 5cfb5ba1c3f79..ea75ac05f07b5 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -65,7 +65,7 @@ static bool useCaptured(Operand *UI) { auto *User = UI->getUser(); // These instructions do not cause the address to escape. - if (isa(User) || isa(User) + if (isa(User) || isa(User) || isa(User) || isa(User)) return false; diff --git a/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp b/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp index 75d5dc94a5c93..8f84bde149b50 100644 --- a/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp +++ b/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp @@ -214,7 +214,10 @@ bool ArrayAllocation::replaceGetElements() { if (ConstantIndex == None) continue; - assert(*ConstantIndex >= 0 && "Must have a positive index"); + // ElementValueMap keys are unsigned. Avoid implicit signed-unsigned + // conversion from an invalid index to a valid index. + if (*ConstantIndex < 0) + continue; auto EltValueIt = ElementValueMap.find(*ConstantIndex); if (EltValueIt == ElementValueMap.end()) diff --git a/lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp b/lib/SILOptimizer/Transforms/AssemblyVisionRemarkGenerator.cpp similarity index 83% rename from lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp rename to lib/SILOptimizer/Transforms/AssemblyVisionRemarkGenerator.cpp index 7febe0c8dc229..2d3dc2c7623fa 100644 --- a/lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp +++ b/lib/SILOptimizer/Transforms/AssemblyVisionRemarkGenerator.cpp @@ -1,8 +1,8 @@ -//===--- OptRemarkGenerator.cpp -------------------------------------------===// +//===--- AssemblyVisionRemarkGenerator.cpp --------------------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2021 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 @@ -12,12 +12,12 @@ /// /// \file /// -/// In this pass, we define the opt-remark-generator, a simple SILVisitor that -/// attempts to infer opt-remarks for the user using heuristics. +/// In this pass, we define the assembly-vision-remark-generator, a simple +/// SILVisitor that attempts to infer remarks for the user using heuristics. /// //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "sil-opt-remark-gen" +#define DEBUG_TYPE "sil-assembly-vision-remark-gen" #include "swift/AST/SemanticAttrs.h" #include "swift/Basic/Defer.h" @@ -25,6 +25,7 @@ #include "swift/SIL/DynamicCasts.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/OptimizationRemark.h" +#include "swift/SIL/PatternMatch.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" @@ -37,15 +38,17 @@ #include "llvm/Support/raw_ostream.h" using namespace swift; +using namespace swift::PatternMatch; static llvm::cl::opt ForceVisitImplicitAutogeneratedFunctions( - "optremarkgen-visit-implicit-autogen-funcs", llvm::cl::Hidden, + "assemblyvisionremarkgen-visit-implicit-autogen-funcs", llvm::cl::Hidden, llvm::cl::desc( "Emit opt remarks even on implicit and autogenerated functions"), llvm::cl::init(false)); static llvm::cl::opt DecllessDebugValueUseSILDebugInfo( - "optremarkgen-declless-debugvalue-use-sildebugvar-info", llvm::cl::Hidden, + "assemblyvisionremarkgen-declless-debugvalue-use-sildebugvar-info", + llvm::cl::Hidden, llvm::cl::desc( "If a debug_value does not have a decl, infer a value with a name from " "that info that has a loc set to the loc of the debug_value " @@ -96,7 +99,8 @@ struct ValueToDeclInferrer { /// /// sil @theFunction : $@convention(thin) () -> () { /// bb0: - /// %0 = apply %getKlassPair() : $@convention(thin) () -> @owned KlassPair + /// %0 = apply %getKlassPair() : $@convention(thin) () -> @owned + /// KlassPair /// // This debug_value's name can be combined... /// debug_value %0 : $KlassPair, name "myPair" /// // ... with the access path from the struct_extract here... @@ -117,8 +121,9 @@ struct ValueToDeclInferrer { /// // /// // The reason why we must do this is due to the behavior of the late /// // optimizer and how it forms these patterns in the code. - /// %0a = apply %getStateWithOwningPointer() : $@convention(thin) () -> @owned StateWithOwningPointer - /// %1 = struct_extract %0a : $StateWithOwningPointer, #StateWithOwningPointer.owningPtr + /// %0a = apply %getStateWithOwningPointer() : $@convention(thin) () -> + /// @owned StateWithOwningPointer %1 = struct_extract %0a : + /// $StateWithOwningPointer, #StateWithOwningPointer.owningPtr /// strong_retain %1 : $Klass /// %2 = struct $Array(%0 : $Builtin.NativeObject, ...) /// debug_value %2 : $Array, ... @@ -128,7 +133,8 @@ struct ValueToDeclInferrer { /// Convenience overload that calls: /// - /// printNote(stream, decl->getBaseName().userFacingName(), shouldPrintAccessPath). + /// printNote(stream, decl->getBaseName().userFacingName(), + /// shouldPrintAccessPath). void printNote(llvm::raw_string_ostream &stream, const ValueDecl *decl, bool shouldPrintAccessPath = true) { printNote(stream, decl->getBaseName().userFacingName(), @@ -223,7 +229,7 @@ namespace { /// A helper struct that attempts to infer the decl associated with a value from /// one of its uses. It does this by searching the def-use graph locally for -/// debug_value and debug_value_addr instructions. +/// debug_value instructions. struct ValueUseToDeclInferrer { using Argument = ValueToDeclInferrer::Argument; using ArgumentKeyKind = ValueToDeclInferrer::ArgumentKeyKind; @@ -306,9 +312,7 @@ bool ValueToDeclInferrer::infer( SmallVectorImpl &resultingInferredDecls, bool allowSingleRefEltAddrPeek) { // Clear the stored access path at end of scope. - SWIFT_DEFER { - accessPath.clear(); - }; + SWIFT_DEFER { accessPath.clear(); }; ValueUseToDeclInferrer valueUseInferrer{ {}, *this, keyKind, resultingInferredDecls}; bool foundSingleRefElementAddr = false; @@ -374,6 +378,31 @@ bool ValueToDeclInferrer::infer( } } + // A pattern that we see around empty array storage is: + // + // %0 = global_addr @_swiftEmptyArrayStorage : $*_SwiftEmptyArrayStorage + // %1 = address_to_pointer %0 : $*_SwiftEmptyArrayStorage to + // $Builtin.RawPointer %2 = raw_pointer_to_ref %1 : $Builtin.RawPointer to + // $__EmptyArrayStorage + // + // Recognize this case. + { + GlobalAddrInst *gai; + if (match(value, m_RawPointerToRefInst( + m_AddressToPointerInst(m_GlobalAddrInst(gai))))) { + if (auto *decl = gai->getReferencedGlobal()->getDecl()) { + std::string msg; + { + llvm::raw_string_ostream stream(msg); + printNote(stream, decl); + } + resultingInferredDecls.push_back( + Argument({keyKind, "InferredValue"}, std::move(msg), decl)); + return true; + } + } + } + // We prefer decls not from uses since these are inherently noisier. Still, // it is better than nothing. bool foundDeclFromUse = false; @@ -391,7 +420,7 @@ bool ValueToDeclInferrer::infer( } // See if we have a single init alloc_stack and can infer a - // debug_value/debug_value_addr from that. + // debug_value from that. LLVM_DEBUG(llvm::dbgs() << "Checking for single init use!\n"); if (auto *initUse = getSingleInitAllocStackUse(asi)) { LLVM_DEBUG(llvm::dbgs() << "Found one: " << *initUse->getUser()); @@ -464,8 +493,9 @@ bool ValueToDeclInferrer::infer( namespace { -struct OptRemarkGeneratorInstructionVisitor - : public SILInstructionVisitor { +struct AssemblyVisionRemarkGeneratorInstructionVisitor + : public SILInstructionVisitor< + AssemblyVisionRemarkGeneratorInstructionVisitor> { SILModule &mod; OptRemark::Emitter ORE; @@ -473,8 +503,8 @@ struct OptRemarkGeneratorInstructionVisitor /// miscellaneous SIL value. This is just a heuristic that is to taste. ValueToDeclInferrer valueToDeclInferrer; - OptRemarkGeneratorInstructionVisitor(SILFunction &fn, - RCIdentityFunctionInfo &rcfi) + AssemblyVisionRemarkGeneratorInstructionVisitor(SILFunction &fn, + RCIdentityFunctionInfo &rcfi) : mod(fn.getModule()), ORE(DEBUG_TYPE, fn), valueToDeclInferrer(rcfi) {} void visitStrongRetainInst(StrongRetainInst *sri); @@ -493,7 +523,7 @@ struct OptRemarkGeneratorInstructionVisitor } // anonymous namespace -void OptRemarkGeneratorInstructionVisitor:: +void AssemblyVisionRemarkGeneratorInstructionVisitor:: visitUnconditionalCheckedCastAddrInst( UnconditionalCheckedCastAddrInst *uccai) { ORE.emit([&]() { @@ -516,8 +546,8 @@ void OptRemarkGeneratorInstructionVisitor:: }); } -void OptRemarkGeneratorInstructionVisitor::visitCheckedCastAddrBranchInst( - CheckedCastAddrBranchInst *ccabi) { +void AssemblyVisionRemarkGeneratorInstructionVisitor:: + visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *ccabi) { ORE.emit([&]() { using namespace OptRemark; SmallVector inferredArgs; @@ -538,7 +568,7 @@ void OptRemarkGeneratorInstructionVisitor::visitCheckedCastAddrBranchInst( }); } -void OptRemarkGeneratorInstructionVisitor::visitBeginAccessInst( +void AssemblyVisionRemarkGeneratorInstructionVisitor::visitBeginAccessInst( BeginAccessInst *bai) { ORE.emit([&]() { using namespace OptRemark; @@ -560,7 +590,7 @@ void OptRemarkGeneratorInstructionVisitor::visitBeginAccessInst( }); } -void OptRemarkGeneratorInstructionVisitor::visitEndAccessInst( +void AssemblyVisionRemarkGeneratorInstructionVisitor::visitEndAccessInst( EndAccessInst *eai) { ORE.emit([&]() { using namespace OptRemark; @@ -574,7 +604,8 @@ void OptRemarkGeneratorInstructionVisitor::visitEndAccessInst( // Use the actual source loc of the begin_access if it works. Otherwise, // scan backwards. auto remark = - RemarkMissed("memory", *eai, SourceLocInferenceBehavior::BackwardScan, + RemarkMissed("memory", *eai, + SourceLocInferenceBehavior::BackwardThenForwardAlwaysInfer, SourceLocPresentationKind::EndRange) << "end exclusive access to value of type '" << NV("ValueType", eai->getOperand()->getType()) << "'"; @@ -585,7 +616,7 @@ void OptRemarkGeneratorInstructionVisitor::visitEndAccessInst( }); } -void OptRemarkGeneratorInstructionVisitor::visitStrongRetainInst( +void AssemblyVisionRemarkGeneratorInstructionVisitor::visitStrongRetainInst( StrongRetainInst *sri) { ORE.emit([&]() { using namespace OptRemark; @@ -607,7 +638,7 @@ void OptRemarkGeneratorInstructionVisitor::visitStrongRetainInst( }); } -void OptRemarkGeneratorInstructionVisitor::visitStrongReleaseInst( +void AssemblyVisionRemarkGeneratorInstructionVisitor::visitStrongReleaseInst( StrongReleaseInst *sri) { ORE.emit([&]() { using namespace OptRemark; @@ -619,7 +650,7 @@ void OptRemarkGeneratorInstructionVisitor::visitStrongReleaseInst( auto remark = RemarkMissed("memory", *sri, - SourceLocInferenceBehavior::BackwardScanAlwaysInfer, + SourceLocInferenceBehavior::BackwardThenForwardAlwaysInfer, SourceLocPresentationKind::EndRange) << "release of type '" << NV("ValueType", sri->getOperand()->getType()) << "'"; @@ -630,7 +661,7 @@ void OptRemarkGeneratorInstructionVisitor::visitStrongReleaseInst( }); } -void OptRemarkGeneratorInstructionVisitor::visitRetainValueInst( +void AssemblyVisionRemarkGeneratorInstructionVisitor::visitRetainValueInst( RetainValueInst *rvi) { ORE.emit([&]() { using namespace OptRemark; @@ -651,7 +682,7 @@ void OptRemarkGeneratorInstructionVisitor::visitRetainValueInst( }); } -void OptRemarkGeneratorInstructionVisitor::visitReleaseValueInst( +void AssemblyVisionRemarkGeneratorInstructionVisitor::visitReleaseValueInst( ReleaseValueInst *rvi) { ORE.emit([&]() { using namespace OptRemark; @@ -663,7 +694,7 @@ void OptRemarkGeneratorInstructionVisitor::visitReleaseValueInst( // Releases end a lifetime scope so we infer scan backward. auto remark = RemarkMissed("memory", *rvi, - SourceLocInferenceBehavior::BackwardScanAlwaysInfer) + SourceLocInferenceBehavior::BackwardThenForwardAlwaysInfer) << "release of type '" << NV("ValueType", rvi->getOperand()->getType()) << "'"; for (auto arg : inferredArgs) { @@ -673,7 +704,7 @@ void OptRemarkGeneratorInstructionVisitor::visitReleaseValueInst( }); } -void OptRemarkGeneratorInstructionVisitor::visitAllocRefInst( +void AssemblyVisionRemarkGeneratorInstructionVisitor::visitAllocRefInst( AllocRefInst *ari) { if (ari->canAllocOnStack()) { return ORE.emit([&]() { @@ -709,7 +740,7 @@ void OptRemarkGeneratorInstructionVisitor::visitAllocRefInst( }); } -void OptRemarkGeneratorInstructionVisitor::visitAllocBoxInst( +void AssemblyVisionRemarkGeneratorInstructionVisitor::visitAllocBoxInst( AllocBoxInst *abi) { return ORE.emit([&]() { using namespace OptRemark; @@ -734,19 +765,43 @@ void OptRemarkGeneratorInstructionVisitor::visitAllocBoxInst( namespace { -class OptRemarkGenerator : public SILFunctionTransform { - ~OptRemarkGenerator() override {} +class AssemblyVisionRemarkGenerator : public SILFunctionTransform { + ~AssemblyVisionRemarkGenerator() override {} bool isOptRemarksEnabled() { auto *fn = getFunction(); // TODO: Put this on LangOpts as a helper. auto &langOpts = fn->getASTContext().LangOpts; - return bool(langOpts.OptimizationRemarkMissedPattern) || - bool(langOpts.OptimizationRemarkPassedPattern) || - fn->getModule().getSILRemarkStreamer() || - fn->hasSemanticsAttrThatStartsWith( - semantics::FORCE_EMIT_OPT_REMARK_PREFIX); + // If we are supposed to emit remarks, always emit. + if (bool(langOpts.OptimizationRemarkMissedPattern) || + bool(langOpts.OptimizationRemarkPassedPattern) || + fn->getModule().getSILRemarkStreamer()) + return true; + + // Otherwise, first check if our function has a force emit opt remark prefix + // semantics tag. + if (fn->hasSemanticsAttrThatStartsWith( + semantics::FORCE_EMIT_OPT_REMARK_PREFIX)) + return true; + + // Otherwise, check if we have a self parameter that is a nominal type that + // is marked with the @_assemblyVision attribute. + if (fn->hasSelfParam()) { + if (auto *nomType = fn->getSelfArgument() + ->getType() + .getNominalOrBoundGenericNominal()) { + LLVM_DEBUG(llvm::dbgs() << "Checking for remark on: " + << nomType->getName().get() << "\n"); + if (nomType->shouldEmitAssemblyVisionRemarksOnMethods()) { + LLVM_DEBUG(llvm::dbgs() << "Success! Will emit remarks!!\n"); + return true; + } + LLVM_DEBUG(llvm::dbgs() << "Fail! No remarks will be emitted!!\n"); + } + } + + return false; } /// The entry point to the transformation. @@ -760,19 +815,29 @@ class OptRemarkGenerator : public SILFunctionTransform { // unless we were asked by the user to emit them. if (!ForceVisitImplicitAutogeneratedFunctions) { // Skip implicit functions generated by Sema. - if (auto *ctx = fn->getDeclContext()) - if (auto *decl = ctx->getAsDecl()) - if (decl->isImplicit()) + if (auto *ctx = fn->getDeclContext()) { + if (auto *decl = ctx->getAsDecl()) { + if (decl->isImplicit()) { + LLVM_DEBUG(llvm::dbgs() << "Skipping implicit decl function: " + << fn->getName() << "\n"); return; + } + } + } + // Skip autogenerated functions generated by SILGen. - if (auto loc = fn->getDebugScope()->getLoc()) - if (loc.isAutoGenerated()) + if (auto loc = fn->getDebugScope()->getLoc()) { + if (loc.isAutoGenerated()) { + LLVM_DEBUG(llvm::dbgs() << "Skipping autogenerated function: " + << fn->getName() << "\n"); return; + } + } } LLVM_DEBUG(llvm::dbgs() << "Visiting: " << fn->getName() << "\n"); auto &rcfi = *getAnalysis()->get(fn); - OptRemarkGeneratorInstructionVisitor visitor(*fn, rcfi); + AssemblyVisionRemarkGeneratorInstructionVisitor visitor(*fn, rcfi); for (auto &block : *fn) { for (auto &inst : block) { visitor.visit(&inst); @@ -783,6 +848,6 @@ class OptRemarkGenerator : public SILFunctionTransform { } // end anonymous namespace -SILTransform *swift::createOptRemarkGenerator() { - return new OptRemarkGenerator(); +SILTransform *swift::createAssemblyVisionRemarkGenerator() { + return new AssemblyVisionRemarkGenerator(); } diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index ac3718b6cf20f..e31f0d2733ee8 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -24,7 +24,7 @@ target_sources(swiftSILOptimizer PRIVATE MergeCondFail.cpp Outliner.cpp ObjectOutliner.cpp - OptRemarkGenerator.cpp + AssemblyVisionRemarkGenerator.cpp PerformanceInliner.cpp PhiArgumentOptimizations.cpp PruneVTables.cpp diff --git a/lib/SILOptimizer/Transforms/COWOpts.cpp b/lib/SILOptimizer/Transforms/COWOpts.cpp index de13369819e7f..007d4bb5c09e2 100644 --- a/lib/SILOptimizer/Transforms/COWOpts.cpp +++ b/lib/SILOptimizer/Transforms/COWOpts.cpp @@ -89,7 +89,7 @@ void COWOptsPass::run() { LLVM_DEBUG(llvm::dbgs() << "*** RedundantPhiElimination on function: " << F->getName() << " ***\n"); - AA = PM->getAnalysis(); + AA = PM->getAnalysis(F); bool changed = false; for (SILBasicBlock &block : *F) { @@ -247,6 +247,7 @@ void COWOptsPass::collectEscapePoints(SILValue v, case SILInstructionKind::BeginCOWMutationInst: case SILInstructionKind::RefElementAddrInst: case SILInstructionKind::RefTailAddrInst: + case SILInstructionKind::DebugValueInst: break; case SILInstructionKind::BranchInst: collectEscapePoints(cast(user)->getArgForOperand(use), diff --git a/lib/SILOptimizer/Transforms/CSE.cpp b/lib/SILOptimizer/Transforms/CSE.cpp index df9ac3e9fe91a..f623d3daaba0d 100644 --- a/lib/SILOptimizer/Transforms/CSE.cpp +++ b/lib/SILOptimizer/Transforms/CSE.cpp @@ -915,6 +915,10 @@ static bool isLazyPropertyGetter(ApplyInst *ai) { !callee->isLazyPropertyGetter()) return false; + // We cannot inline a non-ossa function into an ossa function + if (ai->getFunction()->hasOwnership() && !callee->hasOwnership()) + return false; + // Only handle classes, but not structs. // Lazy property getters of structs have an indirect inout self parameter. // We don't know if the whole struct is overwritten between two getter calls. diff --git a/lib/SILOptimizer/Transforms/CopyForwarding.cpp b/lib/SILOptimizer/Transforms/CopyForwarding.cpp index e41e0c3266298..2400ae8d3ded5 100644 --- a/lib/SILOptimizer/Transforms/CopyForwarding.cpp +++ b/lib/SILOptimizer/Transforms/CopyForwarding.cpp @@ -194,7 +194,7 @@ class AddressUserVisitor { virtual bool visitNormalUse(SILInstruction *user) = 0; virtual bool visitTake(CopyAddrInst *copy) = 0; virtual bool visitDestroy(DestroyAddrInst *destroy) = 0; - virtual bool visitDebugValue(DebugValueAddrInst *debugValue) = 0; + virtual bool visitDebugValue(DebugValueInst *debugValue) = 0; }; } // namespace @@ -265,12 +265,16 @@ static bool visitAddressUsers(SILValue address, SILInstruction *ignoredUser, case SILInstructionKind::StoreInst: if (!visitor.visitNormalUse(UserInst)) return false; - break; - case SILInstructionKind::DebugValueAddrInst: - if (!visitor.visitDebugValue(cast(UserInst))) + case SILInstructionKind::DebugValueInst: + if (auto *DV = DebugValueInst::hasAddrVal(UserInst)) { + if (!visitor.visitDebugValue(DV)) + return false; + } else { + LLVM_DEBUG(llvm::dbgs() << " Skipping copy: use exposes def" + << *UserInst); return false; - + } break; case SILInstructionKind::DeallocStackInst: break; @@ -385,9 +389,12 @@ class AnalyzeForwardUse Oper = &UserInst->getOperandRef(); return false; } - bool visitDebugValueAddrInst(DebugValueAddrInst *UserInst) { - Oper = &UserInst->getOperandRef(); - return false; + bool visitDebugValueInst(DebugValueInst *UserInst) { + if (UserInst->hasAddrVal()) { + Oper = &UserInst->getOperandRef(); + return false; + } + return true; } bool visitInitEnumDataAddrInst(InitEnumDataAddrInst *UserInst) { llvm_unreachable("illegal reinitialization"); @@ -489,9 +496,12 @@ class AnalyzeBackwardUse } return true; } - bool visitDebugValueAddrInst(DebugValueAddrInst *UserInst) { - Oper = &UserInst->getOperandRef(); - return false; + bool visitDebugValueInst(DebugValueInst *UserInst) { + if (UserInst->hasAddrVal()) { + Oper = &UserInst->getOperandRef(); + return false; + } + return true; } bool visitSILInstruction(SILInstruction *UserInst) { return false; @@ -523,7 +533,7 @@ class CopyForwarding { bool HasForwardedToCopy; SmallPtrSet SrcUserInsts; - SmallPtrSet SrcDebugValueInsts; + SmallPtrSet SrcDebugValueInsts; SmallVector TakePoints; SmallPtrSet StoredValueUserInsts; SmallVector DestroyPoints; @@ -560,7 +570,7 @@ class CopyForwarding { CPF.DestroyPoints.push_back(destroy); return true; } - virtual bool visitDebugValue(DebugValueAddrInst *debugValue) override { + virtual bool visitDebugValue(DebugValueInst *debugValue) override { return CPF.SrcDebugValueInsts.insert(debugValue).second; } }; @@ -614,11 +624,12 @@ class CopyForwarding { bool propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy); CopyAddrInst *findCopyIntoDeadTemp( CopyAddrInst *destCopy, - SmallVectorImpl &debugInstsToDelete); + SmallVectorImpl &debugInstsToDelete); bool forwardDeadTempCopy(CopyAddrInst *destCopy); bool forwardPropagateCopy(); bool backwardPropagateCopy(); - bool hoistDestroy(SILInstruction *DestroyPoint, SILLocation DestroyLoc); + bool hoistDestroy(SILInstruction *DestroyPoint, SILLocation DestroyLoc, + SmallVectorImpl &debugInstsToDelete); bool isSourceDeadAtCopy(); @@ -645,7 +656,7 @@ class CopyDestUserVisitor : public AddressUserVisitor { virtual bool visitDestroy(DestroyAddrInst *destroy) override { return DestUsers.insert(destroy).second; } - virtual bool visitDebugValue(DebugValueAddrInst *debugValue) override { + virtual bool visitDebugValue(DebugValueInst *debugValue) override { return DestUsers.insert(debugValue).second; } }; @@ -739,7 +750,7 @@ propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) { /// intervening instructions, it avoids the need to analyze projection paths. CopyAddrInst *CopyForwarding::findCopyIntoDeadTemp( CopyAddrInst *destCopy, - SmallVectorImpl &debugInstsToDelete) { + SmallVectorImpl &debugInstsToDelete) { auto tmpVal = destCopy->getSrc(); assert(tmpVal == CurrentDef); assert(isIdentifiedSourceValue(tmpVal)); @@ -756,13 +767,13 @@ CopyAddrInst *CopyForwarding::findCopyIntoDeadTemp( if (SrcUserInsts.count(UserInst)) return nullptr; - // Collect all debug_value_addr instructions between temp to dest copy and - // src to temp copy. On success, these debug_value_addr instructions should - // be deleted. - if (auto *debugUser = dyn_cast(UserInst)) { + // Collect all debug_value w/ address value instructions between temp to + // dest copy and src to temp copy. + // On success, these debug_value instructions should be deleted. + if (isa(UserInst)) { // 'SrcDebugValueInsts' consists of all the debug users of 'temp' - if (SrcDebugValueInsts.count(debugUser)) - debugInstsToDelete.push_back(debugUser); + if (SrcDebugValueInsts.count(UserInst)) + debugInstsToDelete.push_back(UserInst); continue; } @@ -795,7 +806,7 @@ CopyAddrInst *CopyForwarding::findCopyIntoDeadTemp( /// attempts to destroy this uninitialized value. bool CopyForwarding:: forwardDeadTempCopy(CopyAddrInst *destCopy) { - SmallVector debugInstsToDelete; + SmallVector debugInstsToDelete; auto *srcCopy = findCopyIntoDeadTemp(CurrentCopy, debugInstsToDelete); if (!srcCopy) return false; @@ -816,7 +827,7 @@ forwardDeadTempCopy(CopyAddrInst *destCopy) { .createDestroyAddr(srcCopy->getLoc(), srcCopy->getDest()); } - // Delete all dead debug_value_addr instructions + // Delete all dead debug_value instructions for (auto *deadDebugUser : debugInstsToDelete) { deadDebugUser->eraseFromParent(); } @@ -1142,7 +1153,7 @@ bool CopyForwarding::backwardPropagateCopy() { bool seenCopyDestDef = false; // ValueUses records the uses of CopySrc in reverse order. SmallVector ValueUses; - SmallVector DebugValueInstsToDelete; + SmallVector DebugValueInstsToDelete; auto SI = CurrentCopy->getIterator(), SE = CurrentCopy->getParent()->begin(); while (SI != SE) { --SI; @@ -1155,8 +1166,8 @@ bool CopyForwarding::backwardPropagateCopy() { if (UserInst == CopyDestRoot->getDefiningInstruction() || DestUserInsts.count(UserInst) || RootUserInsts.count(UserInst)) { - if (auto *DVAI = dyn_cast(UserInst)) { - DebugValueInstsToDelete.push_back(DVAI); + if (DebugValueInst::hasAddrVal(UserInst)) { + DebugValueInstsToDelete.push_back(UserInst); continue; } LLVM_DEBUG(llvm::dbgs() << " Skipping copy" << *CurrentCopy @@ -1165,8 +1176,8 @@ bool CopyForwarding::backwardPropagateCopy() { } // Early check to avoid scanning unrelated instructions. if (!SrcUserInsts.count(UserInst) - && !(isa(UserInst) - && SrcDebugValueInsts.count(cast(UserInst)))) + && !(isa(UserInst) + && SrcDebugValueInsts.count(UserInst))) continue; AnalyzeBackwardUse AnalyzeUse(CopySrc); @@ -1224,8 +1235,11 @@ bool CopyForwarding::backwardPropagateCopy() { /// /// Return true if a destroy was inserted, forwarded from a copy, or the /// block was marked dead-in. -bool CopyForwarding::hoistDestroy(SILInstruction *DestroyPoint, - SILLocation DestroyLoc) { +/// \p debugInstsToDelete will contain the debug_value users of copy src +/// that need to be deleted if the hoist is successful +bool CopyForwarding::hoistDestroy( + SILInstruction *DestroyPoint, SILLocation DestroyLoc, + SmallVectorImpl &debugInstsToDelete) { if (!EnableDestroyHoisting) return false; @@ -1266,6 +1280,14 @@ bool CopyForwarding::hoistDestroy(SILInstruction *DestroyPoint, << *Inst); return tryToInsertHoistedDestroyAfter(Inst); } + if (isa(Inst)) { + // Collect debug_value uses of copy src. If the hoist is + // successful, these instructions will have consumed operand and need to + // be erased. + if (SrcDebugValueInsts.contains(Inst)) { + debugInstsToDelete.push_back(Inst); + } + } if (!ShouldHoist && isa(Inst)) ShouldHoist = true; continue; @@ -1316,10 +1338,11 @@ void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) { bool HoistedDestroyFound = false; SILLocation HoistedDestroyLoc = F->getLocation(); const SILDebugScope *HoistedDebugScope = nullptr; + SmallVector debugInstsToDelete; for (auto *Destroy : DestroyPoints) { // If hoistDestroy returns false, it was not worth hoisting. - if (hoistDestroy(Destroy, Destroy->getLoc())) { + if (hoistDestroy(Destroy, Destroy->getLoc(), debugInstsToDelete)) { // Propagate DestroyLoc for any destroy hoisted above a block. if (DeadInBlocks.count(Destroy->getParent())) { HoistedDestroyLoc = Destroy->getLoc(); @@ -1331,7 +1354,12 @@ void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) { // original Destroy. Destroy->eraseFromParent(); assert(HasChanged || !DeadInBlocks.empty() && "HasChanged should be set"); + // Since the hoist was successful, delete all dead debug_value + for (auto *deadDebugUser : debugInstsToDelete) { + deadDebugUser->eraseFromParent(); + } } + debugInstsToDelete.clear(); } // Any blocks containing a DestroyPoints where hoistDestroy did not find a use // are now marked in DeadInBlocks. @@ -1358,9 +1386,14 @@ void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) { if (DeadInSuccs.size() == Succs.size() && !SrcUserInsts.count(BB->getTerminator())) { // All successors are dead, so continue hoisting. - bool WasHoisted = hoistDestroy(BB->getTerminator(), HoistedDestroyLoc); + bool WasHoisted = hoistDestroy(BB->getTerminator(), HoistedDestroyLoc, + debugInstsToDelete); (void)WasHoisted; assert(WasHoisted && "should always hoist above a terminator"); + for (auto *deadDebugUser : debugInstsToDelete) { + deadDebugUser->eraseFromParent(); + } + debugInstsToDelete.clear(); continue; } // Emit a destroy on each CFG edge leading to a dead-in block. This requires diff --git a/lib/SILOptimizer/Transforms/CopyPropagation.cpp b/lib/SILOptimizer/Transforms/CopyPropagation.cpp index 01520e47645ad..86f99496e634d 100644 --- a/lib/SILOptimizer/Transforms/CopyPropagation.cpp +++ b/lib/SILOptimizer/Transforms/CopyPropagation.cpp @@ -15,8 +15,22 @@ /// /// Because this algorithm rewrites copies and destroys without attempting to /// balance the retain count, it is only sound when SIL is in ownership-SSA -/// form. The pass itself is mostly for testing the underlying functionality -/// which can also be invoked as a utility for any owned value. +/// form. +/// +/// This pass operates independently on each extended lifetime--the lifetime of +/// an OSSA reference after propagating that reference through all copies. For +/// owned references, this is a simple process of canonicalization that can be +/// invoked separately via the CanonicalizeOSSALifetime utility. The +/// CanonicalizeBorrowScope utility handles borrowed references, but this is +/// much more involved. It requires coordination to cleanup owned lifetimes +/// outside the borrow scope after canonicalizing the scope itself. +/// +/// This pass also coordinates other transformations that affect lifetime +/// canonicalization: +/// +/// - converting extract to destructure +/// +/// - sinking owned forwarding operations /// /// TODO: Cleanup the resulting SIL by deleting instructions that produce dead /// values (after removing its copies). @@ -25,27 +39,333 @@ #define DEBUG_TYPE "copy-propagation" +#include "swift/SIL/BasicBlockDatastructures.h" #include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/DebugUtils.h" #include "swift/SIL/SILUndef.h" #include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CanonicalOSSALifetime.h" +#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "llvm/ADT/SetVector.h" using namespace swift; +// Canonicalize borrow scopes. // This only applies to -O copy-propagation. llvm::cl::opt EnableRewriteBorrows("canonical-ossa-rewrite-borrows", llvm::cl::init(false), llvm::cl::desc("Enable rewriting borrow scopes")); +namespace { + +/// Worklist of defs to be canonicalized. Defs may be revisited after changing +/// their uses. +struct CanonicalDefWorklist { + bool canonicalizeBorrows; + + llvm::SmallSetVector ownedValues; + llvm::SmallSetVector borrowedValues; + // Ideally, ownedForwards is in def-use order. + llvm::SmallSetVector ownedForwards; + + CanonicalDefWorklist(bool canonicalizeBorrows) + : canonicalizeBorrows(canonicalizeBorrows) {} + + // Update the worklist for the def corresponding to \p copy, which is usually + // a CopyValue, but may be any owned value such as the operand of a + // DestroyValue (to force lifetime shortening). + void updateForCopy(SILValue copy) { + SILValue def = copy; + // sometimes a destroy_value's operand is not owned. + if (def->getOwnershipKind() != OwnershipKind::Owned) + return; + + while (true) { + def = CanonicalizeOSSALifetime::getCanonicalCopiedDef(def); + if (!canonicalizeBorrows) { + ownedValues.insert(def); + return; + } + // If the copy's source is guaranteed, find the root of a borrowed + // extended lifetime. + if (auto *copy = dyn_cast(def)) { + if (SILValue borrowDef = + CanonicalizeBorrowScope::getCanonicalBorrowedDef( + copy->getOperand())) { + borrowedValues.insert(borrowDef); + return; + } + } + // Look through hoistable owned forwarding instructions on the + // use-def chain. + if (SILInstruction *defInst = def->getDefiningInstruction()) { + if (CanonicalizeBorrowScope::isRewritableOSSAForward(defInst)) { + SILValue forwardedDef = defInst->getOperand(0); + if (forwardedDef->getOwnershipKind() == OwnershipKind::Owned) { + def = forwardedDef; + continue; + } + } + } + assert(def->getOwnershipKind() == OwnershipKind::Owned + && "getCanonicalCopiedDef returns owned values"); + // Add any forwarding uses of this owned def. This may include uses that + // we looked through above, but may also include other uses. + addForwardingUses(def); + ownedValues.insert(def); + return; + } + } + + // Add forwarding uses of \p def in def-use order. They will be popped from + // the list for sinking in use-def order. + void addForwardingUses(SILValue def) { + SmallVector useWorklist(def->getUses()); + while (!useWorklist.empty()) { + auto *user = useWorklist.pop_back_val()->getUser(); + if (auto *copy = dyn_cast(user)) { + useWorklist.append(copy->getUses().begin(), copy->getUses().end()); + continue; + } + if (!CanonicalizeBorrowScope::isRewritableOSSAForward(user)) + continue; + + if (!ownedForwards.insert(user)) + continue; + + for (auto result : user->getResults()) { + useWorklist.append(result->getUses().begin(), result->getUses().end()); + } + } + } + + void erase(SILInstruction *i) { + for (auto result : i->getResults()) { + ownedValues.remove(result); + borrowedValues.remove(result); + } + ownedForwards.remove(i); + } +}; + +} // namespace + +//===----------------------------------------------------------------------===// +// MARK: Convert struct_extract to destructure +//===----------------------------------------------------------------------===// + +/// Convert a struct_extract into a copy + destructure. Return the destructured +/// result or invalid SILValue. The caller must delete the extract and its +/// now-dead copy use. +/// +/// Converts: +/// %extract = struct_extract %src : $TypeWithSingleOwnershipValue +/// %copy = copy_value %extract : $OwnershipValue +/// To: +/// %copy = copy_value %src : $TypeWithSingleOwnershipValue +/// (%extracted,...) = destructure %copy : $OwnershipValue +/// +/// This allows the ownership of '%src' to be forwarded to its member. +/// +/// This utility runs during copy propagation as a prerequisite to +/// CanonicalizeBorrowScopes. +/// +/// TODO: generalize this to handle multiple nondebug uses of the +/// struct_extract. +/// +/// TODO: generalize this to handle multiple reference member. At that point, it +/// may need to have its own analysis. +static SILValue convertExtractToDestructure(StructExtractInst *extract) { + if (!hasOneNonDebugUse(extract)) + return nullptr; + + if (!extract->isFieldOnlyNonTrivialField()) + return nullptr; + + auto *extractCopy = + dyn_cast(getNonDebugUses(extract).begin()->getUser()); + if (!extractCopy) + return nullptr; + + SILBuilderWithScope builder(extract); + auto loc = extract->getLoc(); + auto *copy = builder.createCopyValue(loc, extract->getOperand()); + auto *destructure = builder.createDestructureStruct(loc, copy); + + SILValue nonTrivialResult = destructure->getResult(extract->getFieldIndex()); + assert(!nonTrivialResult->getType().isTrivial(*destructure->getFunction()) + && "field idx mismatch"); + + extractCopy->replaceAllUsesWith(nonTrivialResult); + return nonTrivialResult; +} + +/// Push copy_value instructions above their struct_extract operands by +/// inserting destructures. +/// +/// For types with a single reference member, converts +/// src -> struct_extract -> copy +/// into +/// src -> copy -> destructure +/// +/// Returns true if any changes were made. Uses \p deleter for deletion but does +/// not use callbacks for other modifications. +static bool convertExtractsToDestructures(CanonicalDefWorklist &copiedDefs, + InstructionDeleter &deleter) { + SmallVector extracts; + auto pushExtract = [&extracts](CopyValueInst *copy) { + if (auto *extract = dyn_cast(copy->getOperand())) { + if (SILValue(extract).getOwnershipKind() == OwnershipKind::Guaranteed) { + extracts.push_back(extract); + } + } + }; + for (SILValue v : copiedDefs.ownedValues) { + auto *copy = dyn_cast(v); + if (!copy) + continue; + + pushExtract(copy); + } + bool changed = false; + // extracts may grow as copies are added + for (unsigned idx = 0; idx < extracts.size(); ++idx) { + auto *extract = extracts[idx]; + SILValue destructuredResult = convertExtractToDestructure(extract); + if (!destructuredResult) + continue; + + changed = true; + + auto *destructure = cast( + destructuredResult.getDefiningInstruction()); + auto *newCopy = cast(destructure->getOperand()); + copiedDefs.updateForCopy(newCopy); + pushExtract(newCopy); + + LLVM_DEBUG(llvm::dbgs() << "Destructure Conversion:\n" + << *extract << " to " << *destructure); + + // Delete both the copy and the extract. + deleter.recursivelyDeleteUsersIfDead(extract); + } + return changed; +} + +//===----------------------------------------------------------------------===// +// MARK: Sink owned forwarding operations +//===----------------------------------------------------------------------===// + +/// Find loop preheaders on the reverse path from useBlock to defBlock. Here, a +/// preheader is a block on this path with a CFG successor that is the target of +/// a DFS back edge. +/// +/// Uses PostOrderAnalysis to identify back edges. +/// +/// Precondition: defBlock and useBlock are weakly control equivalent +/// - defBlock dominates useBlock +/// - useBlock postdominates defBlock +/// +/// defBlock maybe within an inner loop relative to useBlock. +static void findPreheadersOnControlEquivalentPath( + SILBasicBlock *defBlock, SILBasicBlock *useBlock, + PostOrderFunctionInfo *postorder, + SmallVectorImpl &preheaders) { + + assert(useBlock != defBlock); + BasicBlockWorklist worklist(useBlock); + while (auto *bb = worklist.pop()) { + unsigned rpo = *postorder->getRPONumber(bb); + bool hasBackedge = + llvm::any_of(bb->getPredecessorBlocks(), [&](SILBasicBlock *pred) { + return postorder->getRPONumber(pred) > rpo; + }); + for (auto *pred : bb->getPredecessorBlocks()) { + if (hasBackedge && postorder->getRPONumber(pred) < rpo) { + preheaders.push_back(pred); + } + + // Follow predecessor even if it's a preheader in case of irreducibility. + if (pred != defBlock) { + worklist.pushIfNotVisited(pred); + } + } + } +} + +/// Sink \p ownedForward to its uses. +/// +/// Owned forwarding instructions are identified by +/// CanonicalOSSALifetime::isRewritableOSSAForward(). +/// +/// Assumes that the uses of ownedForward jointly postdominate it (valid OSSA). +/// +/// TODO: consider cloning into each use block (or loop preheader for blocks +/// inside loops). +static bool sinkOwnedForward(SILInstruction *ownedForward, + PostOrderAnalysis *postOrderAnalysis, + DominanceInfo *domTree) { + // First find the LCA block to sink this forward without cloning it. + SILBasicBlock *lca = nullptr; + for (auto result : ownedForward->getResults()) { + for (auto *use : result->getUses()) { + auto *bb = use->getParentBlock(); + lca = lca ? domTree->findNearestCommonDominator(lca, bb) : bb; + } + } + // Find any preheader on the path from ownedForward to lca. Consider them uses + // and recompute lca. + auto *defBB = ownedForward->getParent(); + if (lca != defBB) { + auto *f = defBB->getParent(); + SmallVector preheaders; + findPreheadersOnControlEquivalentPath(defBB, lca, postOrderAnalysis->get(f), + preheaders); + for (SILBasicBlock *preheader : preheaders) { + lca = domTree->findNearestCommonDominator(lca, preheader); + } + } + // Mark all uses in this LCA block. + SmallPtrSet lcaUses; + for (auto result : ownedForward->getResults()) { + for (auto *use : result->getUses()) { + if (use->getParentBlock() != lca) + continue; + + lcaUses.insert(use->getUser()); + } + } + // Find the position in the LCA before the first use. + SILBasicBlock::iterator forwardPos; + if (lcaUses.empty()) { + forwardPos = lca->getTerminator()->getIterator(); + } else { + // Start at the def or begining of the block and search forward. + if (ownedForward->getParent() == lca) + forwardPos = std::next(ownedForward->getIterator()); + else + forwardPos = lca->begin(); + while (!lcaUses.count(&*forwardPos)) { + ++forwardPos; + } + } + if (forwardPos == std::next(ownedForward->getIterator())) + return false; + + ownedForward->moveBefore(&*forwardPos); + return true; +} + //===----------------------------------------------------------------------===// // CopyPropagation: Top-Level Function Transform. //===----------------------------------------------------------------------===// namespace { + class CopyPropagation : public SILFunctionTransform { /// True if debug_value instructions should be pruned. bool pruneDebug; @@ -66,138 +386,125 @@ class CopyPropagation : public SILFunctionTransform { /// The entry point to this function transformation. void run() override; }; -} // end anonymous namespace - -static bool isCopyDead(CopyValueInst *copy, bool pruneDebug) { - for (Operand *use : copy->getUses()) { - auto *user = use->getUser(); - if (isa(user)) { - continue; - } - if (pruneDebug && isa(user)) { - continue; - } - return false; - } - return true; -} -static bool isBorrowDead(BeginBorrowInst *borrow) { - return llvm::all_of(borrow->getUses(), [](Operand *use) { - SILInstruction *user = use->getUser(); - return isa(user) || user->isDebugInstruction(); - }); -} +} // end anonymous namespace /// Top-level pass driver. void CopyPropagation::run() { auto *f = getFunction(); + auto *postOrderAnalysis = getAnalysis(); auto *accessBlockAnalysis = getAnalysis(); auto *dominanceAnalysis = getAnalysis(); + DominanceInfo *domTree = dominanceAnalysis->get(f); - // Debug label for unit testing. + // Label for unit testing with debug output. LLVM_DEBUG(llvm::dbgs() << "*** CopyPropagation: " << f->getName() << "\n"); // This algorithm fundamentally assumes ownership. if (!f->hasOwnership()) return; + CanonicalDefWorklist defWorklist(canonicalizeBorrows); + auto callbacks = + InstModCallbacks().onDelete([&](SILInstruction *instToDelete) { + defWorklist.erase(instToDelete); + instToDelete->eraseFromParent(); + }); + + InstructionDeleter deleter(std::move(callbacks)); + bool changed = false; + // Driver: Find all copied defs. - llvm::SmallSetVector copiedDefs; for (auto &bb : *f) { for (auto &i : bb) { if (auto *copy = dyn_cast(&i)) { - copiedDefs.insert( - CanonicalizeOSSALifetime::getCanonicalCopiedDef(copy)); + defWorklist.updateForCopy(copy); } else if (canonicalizeAll) { if (auto *destroy = dyn_cast(&i)) { - copiedDefs.insert(CanonicalizeOSSALifetime::getCanonicalCopiedDef( - destroy->getOperand())); + defWorklist.updateForCopy(destroy->getOperand()); } } } } - // Push copy_value instructions above their struct_extract operands by - // inserting destructures. - // - // copiedDefs be be modified, but it never shrinks - for (unsigned idx = 0; idx < copiedDefs.size(); ++idx) { - SILValue def = copiedDefs[idx]; - auto *copy = dyn_cast(def); - if (!copy) - continue; - - auto *extract = dyn_cast(copy->getOperand()); - if (!extract - || SILValue(extract).getOwnershipKind() != OwnershipKind::Guaranteed) - continue; + // canonicalizer performs all modifications through deleter's callbacks, so we + // don't need to explicitly check for changes. + CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs, + accessBlockAnalysis, domTree, deleter); + // For now, only modify forwarding instructions + // At -Onone, we do nothing but rewrite copies of owned values. + if (canonicalizeBorrows) { + // Canonicalize extracts to destructures. struct_extracts are initially part + // of the copiedDefs. If the are converted, they are removed from copiedDefs + // and the source of the new destructure is added. + changed |= convertExtractsToDestructures(defWorklist, deleter); - // Bail-out if we won't rewrite borrows because that currently regresses - // Breadcrumbs.MutatedUTF16ToIdx.Mixed/Breadcrumbs.MutatedIdxToUTF16.Mixed. - // Also, mandatory copyprop does not need to rewrite destructures. - if (!canonicalizeBorrows) - continue; - - if (SILValue destructuredResult = convertExtractToDestructure(extract)) { - // Remove to-be-deleted instructions from copiedDeds. The extract cannot - // be in the copiedDefs set since getCanonicalCopiedDef does not allow a - // guaranteed projection to be a canonical def. - copiedDefs.remove(copy); - --idx; // point back to the current element, which was erased. - - // TODO: unfortunately SetVector has no element replacement. - copiedDefs.insert(destructuredResult); - - auto *destructure = cast( - destructuredResult.getDefiningInstruction()); - auto *newCopy = cast(destructure->getOperand()); - copiedDefs.insert( - CanonicalizeOSSALifetime::getCanonicalCopiedDef(newCopy)); - - LLVM_DEBUG(llvm::dbgs() << "Destructure Conversion:\n" - << *extract << " to " << *destructure); - // Delete both the copy and the extract. - InstructionDeleter().recursivelyDeleteUsersIfDead(extract); - } - } - // Perform copy propgation for each copied value. - CanonicalizeOSSALifetime canonicalizer(pruneDebug, canonicalizeBorrows, - poisonRefs, accessBlockAnalysis, - dominanceAnalysis); - // Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the - // copy's source operand is unrecgonized), then the copy is itself treated - // like a def and may be dead after canonicalization. - llvm::SmallVector deadCopies; - for (auto &def : copiedDefs) { - // Canonicalized this def. - canonicalizer.canonicalizeValueLifetime(def); + // borrowCanonicalizer performs all modifications through deleter's + // callbacks, so we don't need to explicitly check for changes. + CanonicalizeBorrowScope borrowCanonicalizer(deleter); + // The utilities in this loop cannot delete borrows before they are popped + // from the worklist. + while (true) { + while (!defWorklist.ownedForwards.empty()) { + SILInstruction *ownedForward = defWorklist.ownedForwards.pop_back_val(); + // Delete a dead forwarded value before sinking to avoid this pattern: + // %outerVal = destructure_struct %def + // destroy %outerVal <= delete this destroy now + // destroy %def <= so we don't delete this one later + if (deleter.deleteIfDead(ownedForward)) { + LLVM_DEBUG(llvm::dbgs() << " Deleted " << *ownedForward); + continue; + } + // Canonicalize a forwarded owned value before sinking the forwarding + // instruction, and sink the instruction before canonicalizing the owned + // value being forwarded. Process 'ownedForwards' in reverse since + // they may be chained, and CanonicalizeBorrowScopes pushes them + // top-down. + for (auto result : ownedForward->getResults()) { + canonicalizer.canonicalizeValueLifetime(result); + } + if (sinkOwnedForward(ownedForward, postOrderAnalysis, domTree)) { + changed = true; + // Sinking 'ownedForward' may create an opportunity to sink its + // operand. This handles chained forwarding instructions that were + // pushed onto the list out-of-order. + if (SILInstruction *forwardDef = + CanonicalizeOSSALifetime::getCanonicalCopiedDef( + ownedForward->getOperand(0)) + ->getDefiningInstruction()) { + if (CanonicalizeBorrowScope::isRewritableOSSAForward(forwardDef)) { + defWorklist.ownedForwards.insert(forwardDef); + } + } + } + } + if (defWorklist.borrowedValues.empty()) + break; - if (auto *copy = dyn_cast(def)) { - if (isCopyDead(copy, pruneDebug)) { - deadCopies.push_back(copy); + BorrowedValue borrow(defWorklist.borrowedValues.pop_back_val()); + borrowCanonicalizer.canonicalizeBorrowScope(borrow); + for (CopyValueInst *copy : borrowCanonicalizer.getUpdatedCopies()) { + defWorklist.updateForCopy(copy); } - } - // Dead borrow scopes must be removed as uses before canonicalizing the - // outer copy. - if (auto *borrow = dyn_cast(def)) { - if (isBorrowDead(borrow)) { - borrow->setOperand(SILUndef::get(borrow->getType(), *f)); - deadCopies.push_back(borrow); + // Dead borrow scopes must be removed as uses before canonicalizing the + // outer copy. + if (auto *beginBorrow = dyn_cast(borrow.value)) { + if (hasOnlyEndOfScopeOrEndOfLifetimeUses(beginBorrow)) { + deleter.recursivelyDeleteUsersIfDead(beginBorrow); + } } } - // Canonicalize any new outer copy. - if (SILValue outerCopy = canonicalizer.createdOuterCopy()) { - SILValue outerDef = canonicalizer.getCanonicalCopiedDef(outerCopy); - canonicalizer.canonicalizeValueLifetime(outerDef); - } - // TODO: also canonicalize any lifetime.persistentCopies like separate owned - // live ranges. + deleter.cleanupDeadInstructions(); } - if (canonicalizer.hasChanged() || !deadCopies.empty()) { - InstructionDeleter deleter; - for (auto *copyOrBorrow : deadCopies) { - deleter.recursivelyDeleteUsersIfDead(copyOrBorrow); - } + // Canonicalize all owned defs. + while (!defWorklist.ownedValues.empty()) { + SILValue def = defWorklist.ownedValues.pop_back_val(); + canonicalizer.canonicalizeValueLifetime(def); + } + // Recursively cleanup dead defs after removing uses. + deleter.cleanupDeadInstructions(); + + // Invalidate analyses. + if (changed || deleter.hadCallbackInvocation()) { // Preserves NonLocalAccessBlockAnalysis. accessBlockAnalysis->lockInvalidation(); invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); diff --git a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp index 72a49e7b7c717..0a6a3e4f2553f 100644 --- a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp @@ -45,10 +45,11 @@ namespace { // FIXME: Reconcile the similarities between this and // isInstructionTriviallyDead. static bool seemsUseful(SILInstruction *I) { - // Even though begin_access/destroy_value/copy_value have side-effects, they - // can be DCE'ed if they do not have useful dependencies/reverse dependencies + // Even though begin_access/destroy_value/copy_value/end_lifetime have + // side-effects, they can be DCE'ed if they do not have useful + // dependencies/reverse dependencies if (isa(I) || isa(I) || - isa(I)) + isa(I) || isa(I)) return false; // A load [copy] is okay to be DCE'ed if there are no useful dependencies @@ -73,6 +74,10 @@ static bool seemsUseful(SILInstruction *I) { return true; } + // Is useful if it's associating with a function argument + if (isa(I)) + return isa(I->getOperand(0)); + return false; } @@ -268,7 +273,8 @@ void DCE::markLive() { break; } case SILInstructionKind::DestroyValueInst: - case SILInstructionKind::EndBorrowInst: { + case SILInstructionKind::EndBorrowInst: + case SILInstructionKind::EndLifetimeInst: { // The instruction is live only if it's operand value is also live addReverseDependency(I.getOperand(0), &I); break; @@ -282,10 +288,10 @@ void DCE::markLive() { OwnershipKind::Owned) { markInstructionLive(borrowInst); // Visit all end_borrows and mark them live - auto visitEndBorrow = [&](EndBorrowInst *endBorrow) { - markInstructionLive(endBorrow); - }; - visitTransitiveEndBorrows(borrowInst, visitEndBorrow); + visitTransitiveEndBorrows(BorrowedValue(borrowInst), + [&](EndBorrowInst *endBorrow) { + markInstructionLive(endBorrow); + }); continue; } // If not populate reborrowDependencies for this borrow diff --git a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp index 8d01e26413bb8..612ef4242bcdd 100644 --- a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp @@ -62,6 +62,19 @@ STATISTIC(DeadAllocApplyEliminated, using UserList = llvm::SmallSetVector; +namespace { + +/// Side effects of a destructor. +enum class DestructorEffects { + None, + + /// The destructor contains a "destroyArray" builtin which destroys the tail + /// elements of the object - like in Array. + DestroysTailElems, + + Unknown +}; + // Analyzing the body of this class destructor is valid because the object is // dead. This means that the object is never passed to objc_setAssociatedObject, // so its destructor cannot be extended at runtime. @@ -98,14 +111,21 @@ static SILFunction *getDestructor(AllocRefInst *ARI) { return Fn; } +static bool isDestroyArray(SILInstruction *inst) { + BuiltinInst *bi = dyn_cast(inst); + return bi && bi->getBuiltinInfo().ID == BuiltinValueKind::DestroyArray; +} + /// Analyze the destructor for the class of ARI to see if any instructions in it /// could have side effects on the program outside the destructor. If it does /// not, then we can eliminate the destructor. -static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) { +static DestructorEffects doesDestructorHaveSideEffects(AllocRefInst *ARI) { SILFunction *Fn = getDestructor(ARI); // If we can't find a constructor then assume it has side effects. if (!Fn) - return true; + return DestructorEffects::Unknown; + + DestructorEffects effects = DestructorEffects::None; // A destructor only has one argument, self. assert(Fn->begin()->getNumArguments() == 1 && @@ -115,7 +135,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) { LLVM_DEBUG(llvm::dbgs() << " Analyzing destructor.\n"); // For each BB in the destructor... - for (auto &BB : *Fn) + for (auto &BB : *Fn) { // For each instruction I in BB... for (auto &I : BB) { LLVM_DEBUG(llvm::dbgs() << " Visiting: " << I); @@ -127,6 +147,14 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) { continue; } + if (auto *fl = dyn_cast(&I)) { + // A fix_lifetime of self does cannot have a side effect, because in the + // destructor, Self is deleted. + if (stripCasts(fl->getOperand()) == Self) + continue; + return DestructorEffects::Unknown; + } + // RefCounting operations on Self are ok since we are already in the // destructor. RefCountingOperations on other instructions could have side // effects though. @@ -142,7 +170,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) { } else { LLVM_DEBUG(llvm::dbgs() << " UNSAFE! Ref count operation " "not on self.\n"); - return true; + return DestructorEffects::Unknown; } } @@ -162,7 +190,7 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) { } else { LLVM_DEBUG(llvm::dbgs() << " UNSAFE! dealloc_ref on value " "besides self.\n"); - return true; + return DestructorEffects::Unknown; } } @@ -174,22 +202,28 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) { continue; } + if (isDestroyArray(&I)) { + // Check if the "destroyArray" destroys the tail elements of the object, + // like in Array. + SILValue addr = I.getOperand(1); + auto *atp = dyn_cast(addr); + if (!atp) + return DestructorEffects::Unknown; + auto *rta = dyn_cast(atp->getOperand()); + if (!rta) + return DestructorEffects::Unknown; + effects = DestructorEffects::DestroysTailElems; + if (rta->getOperand() == Self) + continue; + } + LLVM_DEBUG(llvm::dbgs() << " UNSAFE! Unknown instruction.\n"); // Otherwise, we can't remove the deallocation completely. - return true; + return DestructorEffects::Unknown; } - - // We didn't find any side effects. - return false; -} - -void static -removeInstructions(ArrayRef UsersToRemove) { - for (auto *I : UsersToRemove) { - I->replaceAllUsesOfAllResultsWithUndef(); - // Now we know that I should not have any uses... erase it from its parent. - I->eraseFromParent(); } + // We didn't find any side effects. + return effects; } //===----------------------------------------------------------------------===// @@ -326,11 +360,6 @@ static bool onlyStoresToTailObjects(BuiltinInst *destroyArray, return true; } -static bool isDestroyArray(SILInstruction *inst) { - BuiltinInst *bi = dyn_cast(inst); - return bi && bi->getBuiltinInfo().ID == BuiltinValueKind::DestroyArray; -} - /// Inserts releases of all stores in \p users. static void insertCompensatingReleases(SILInstruction *before, const UserList &users) { @@ -433,7 +462,6 @@ hasUnremovableUsers(SILInstruction *allocation, UserList *Users, // NonTrivial DeadObject Elimination //===----------------------------------------------------------------------===// -namespace { /// Determine if an object is dead. Compute its original lifetime. Find the /// lifetime endpoints reached by each store of a refcounted object into the /// object. @@ -682,93 +710,6 @@ static void insertReleases(ArrayRef Stores, } } -// Attempt to remove the array allocated at NewAddrValue and release its -// refcounted elements. -// -// This is tightly coupled with the implementation of array.uninitialized. -// The call to allocate an uninitialized array returns two values: -// (Array ArrayBase, UnsafeMutable ArrayElementStorage) -// -// TODO: This relies on the lowest level array.uninitialized not being -// inlined. To do better we could either run this pass before semantic inlining, -// or we could also handle calls to array.init. -static bool removeAndReleaseArray(SingleValueInstruction *NewArrayValue, - DeadEndBlocks &DEBlocks) { - TupleExtractInst *ArrayDef = nullptr; - TupleExtractInst *StorageAddress = nullptr; - for (auto *Op : NewArrayValue->getUses()) { - auto *TupleElt = dyn_cast(Op->getUser()); - if (!TupleElt) - return false; - if (TupleElt->getFieldIndex() == 0 && !ArrayDef) { - ArrayDef = TupleElt; - } else if (TupleElt->getFieldIndex() == 1 && !StorageAddress) { - StorageAddress = TupleElt; - } else { - return false; - } - } - if (!ArrayDef) - return false; // No Array object to delete. - - assert(!ArrayDef->getType().isTrivial(*ArrayDef->getFunction()) && - "Array initialization should produce the proper tuple type."); - - // Analyze the array object uses. - DeadObjectAnalysis DeadArray(ArrayDef); - if (!DeadArray.analyze()) - return false; - - // Require all stores to be into the array storage not the array object, - // otherwise bail. - bool HasStores = false; - DeadArray.visitStoreLocations([&](ArrayRef){ HasStores = true; }); - if (HasStores) - return false; - - // Remove references to empty arrays. - if (!StorageAddress) { - removeInstructions(DeadArray.getAllUsers()); - return true; - } - assert(StorageAddress->getType().isTrivial(*ArrayDef->getFunction()) && - "Array initialization should produce the proper tuple type."); - - // Analyze the array storage uses. - DeadObjectAnalysis DeadStorage(StorageAddress); - if (!DeadStorage.analyze()) - return false; - - // Find array object lifetime. - ValueLifetimeAnalysis VLA(NewArrayValue, DeadArray.getAllUsers()); - - // Check that all storage users are in the Array's live blocks. - for (auto *User : DeadStorage.getAllUsers()) { - if (!VLA.isWithinLifetime(User)) - return false; - } - // For each store location, insert releases. - SILSSAUpdater SSAUp; - ValueLifetimeAnalysis::Frontier ArrayFrontier; - if (!VLA.computeFrontier(ArrayFrontier, - ValueLifetimeAnalysis::UsersMustPostDomDef, - &DEBlocks)) { - // In theory the allocated object must be released on all paths in which - // some object initialization occurs. If not (for some reason) we bail. - return false; - } - - DeadStorage.visitStoreLocations([&] (ArrayRef Stores) { - insertReleases(Stores, ArrayFrontier, SSAUp); - }); - - // Delete all uses of the dead array and its storage address. - removeInstructions(DeadArray.getAllUsers()); - removeInstructions(DeadStorage.getAllUsers()); - - return true; -} - //===----------------------------------------------------------------------===// // Function Processing //===----------------------------------------------------------------------===// @@ -783,35 +724,12 @@ static bool isAllocatingApply(SILInstruction *Inst) { namespace { class DeadObjectElimination : public SILFunctionTransform { - llvm::DenseMap DestructorAnalysisCache; - // This is not a SILBasicBlock::iterator because we need a null value and we - // don't have a specific block whose end() we could use. - SILInstruction *nextInstToProcess = nullptr; + llvm::DenseMap DestructorAnalysisCache; - /// Advance nextInstToProcess to the next instruction in its block. - /// If nextInstToProcess is the last instruction in its block, it is set to - /// null. - void advance() { - if (!nextInstToProcess) - return; - SILBasicBlock *block = nextInstToProcess->getParent(); - auto nextIter = std::next(nextInstToProcess->getIterator()); - if (nextIter == block->end()) { - nextInstToProcess = nullptr; - } else { - nextInstToProcess = &*nextIter; - } - } - - void handleDeleteNotification(SILNode *node) override { - if (auto *inst = dyn_cast(node)) { - if (inst == nextInstToProcess) - advance(); - } - } + InstructionDeleter deleter; - bool needsNotifications() override { return true; } + void removeInstructions(ArrayRef toRemove); bool processAllocRef(AllocRefInst *ARI); bool processAllocStack(AllocStackInst *ASI); @@ -819,6 +737,11 @@ class DeadObjectElimination : public SILFunctionTransform { bool processAllocBox(AllocBoxInst *ABI){ return false;} bool processAllocApply(ApplyInst *AI, DeadEndBlocks &DEBlocks); + bool getDeadInstsAfterInitializerRemoved( + ApplyInst *AI, llvm::SmallVectorImpl &ToDestroy); + bool removeAndReleaseArray( + SingleValueInstruction *NewArrayValue, DeadEndBlocks &DEBlocks); + bool processFunction(SILFunction &Fn) { DeadEndBlocks DEBlocks(&Fn); DestructorAnalysisCache.clear(); @@ -826,14 +749,8 @@ class DeadObjectElimination : public SILFunctionTransform { bool Changed = false; for (auto &BB : Fn) { - nextInstToProcess = &BB.front(); - - // We cannot just iterate over the instructions, because the processing - // functions might deleted instruction before or after the current - // instruction - and also inst itself. - while (SILInstruction *inst = nextInstToProcess) { - advance(); + for (SILInstruction *inst : deleter.updatingRange(&BB)) { if (auto *A = dyn_cast(inst)) Changed |= processAllocRef(A); else if (auto *A = dyn_cast(inst)) @@ -845,6 +762,7 @@ class DeadObjectElimination : public SILFunctionTransform { else if (auto *A = dyn_cast(inst)) Changed |= processAllocApply(A, DEBlocks); } + deleter.cleanupDeadInstructions(); } return Changed; } @@ -862,15 +780,24 @@ class DeadObjectElimination : public SILFunctionTransform { }; } // end anonymous namespace +void +DeadObjectElimination::removeInstructions(ArrayRef toRemove) { + for (auto *I : toRemove) { + I->replaceAllUsesOfAllResultsWithUndef(); + // Now we know that I should not have any uses... erase it from its parent. + deleter.forceDelete(I); + } +} + bool DeadObjectElimination::processAllocRef(AllocRefInst *ARI) { // Ok, we have an alloc_ref. Check the cache to see if we have already // computed the destructor behavior for its SILType. - bool HasSideEffects; + DestructorEffects destructorEffects; SILType Type = ARI->getType(); auto CacheSearchResult = DestructorAnalysisCache.find(Type); if (CacheSearchResult != DestructorAnalysisCache.end()) { // Ok we found a value in the cache. - HasSideEffects = CacheSearchResult->second; + destructorEffects = CacheSearchResult->second; } else { // We did not find a value in the cache for our destructor. Analyze the // destructor to make sure it has no side effects. For now this only @@ -880,24 +807,34 @@ bool DeadObjectElimination::processAllocRef(AllocRefInst *ARI) { // // TODO: We should be able to handle destructors that do nothing but release // members of the object. - HasSideEffects = doesDestructorHaveSideEffects(ARI); - DestructorAnalysisCache[Type] = HasSideEffects; + destructorEffects = doesDestructorHaveSideEffects(ARI); + DestructorAnalysisCache[Type] = destructorEffects; } // Our destructor has no side effects, so if we can prove that no loads // escape, then we can completely remove the use graph of this alloc_ref. UserList UsersToRemove; if (hasUnremovableUsers(ARI, &UsersToRemove, - /*acceptRefCountInsts=*/ !HasSideEffects, - /*onlyAcceptTrivialStores*/false)) { + /*acceptRefCountInsts=*/ destructorEffects != DestructorEffects::Unknown, + /*onlyAcceptTrivialStores*/false)) { LLVM_DEBUG(llvm::dbgs() << " Found a use that cannot be zapped...\n"); return false; } - auto iter = std::find_if(UsersToRemove.begin(), UsersToRemove.end(), - isDestroyArray); - if (iter != UsersToRemove.end()) - insertCompensatingReleases(*iter, UsersToRemove); + // Find the instruction which releases the object's tail elements. + SILInstruction *releaseOfTailElems = nullptr; + for (SILInstruction *user : UsersToRemove) { + if (isDestroyArray(user) || + (destructorEffects == DestructorEffects::DestroysTailElems && + isa(user) && user->mayRelease())) { + // Bail if we find multiple such instructions. + if (releaseOfTailElems) + return false; + releaseOfTailElems = user; + } + } + if (releaseOfTailElems) + insertCompensatingReleases(releaseOfTailElems, UsersToRemove); // Remove the AllocRef and all of its users. removeInstructions( @@ -954,7 +891,7 @@ bool DeadObjectElimination::processKeyPath(KeyPathInst *KPI) { /// If AI is the version of an initializer where we pass in either an apply or /// an alloc_ref to initialize in place, validate that we are able to continue /// optimizing and return To -static bool getDeadInstsAfterInitializerRemoved( +bool DeadObjectElimination::getDeadInstsAfterInitializerRemoved( ApplyInst *AI, llvm::SmallVectorImpl &ToDestroy) { assert(ToDestroy.empty() && "We assume that ToDestroy is empty, so on " "failure we can clear without worrying about the " @@ -995,6 +932,93 @@ static bool getDeadInstsAfterInitializerRemoved( return true; } +// Attempt to remove the array allocated at NewAddrValue and release its +// refcounted elements. +// +// This is tightly coupled with the implementation of array.uninitialized. +// The call to allocate an uninitialized array returns two values: +// (Array ArrayBase, UnsafeMutable ArrayElementStorage) +// +// TODO: This relies on the lowest level array.uninitialized not being +// inlined. To do better we could either run this pass before semantic inlining, +// or we could also handle calls to array.init. +bool DeadObjectElimination::removeAndReleaseArray( + SingleValueInstruction *NewArrayValue, DeadEndBlocks &DEBlocks) { + TupleExtractInst *ArrayDef = nullptr; + TupleExtractInst *StorageAddress = nullptr; + for (auto *Op : NewArrayValue->getUses()) { + auto *TupleElt = dyn_cast(Op->getUser()); + if (!TupleElt) + return false; + if (TupleElt->getFieldIndex() == 0 && !ArrayDef) { + ArrayDef = TupleElt; + } else if (TupleElt->getFieldIndex() == 1 && !StorageAddress) { + StorageAddress = TupleElt; + } else { + return false; + } + } + if (!ArrayDef) + return false; // No Array object to delete. + + assert(!ArrayDef->getType().isTrivial(*ArrayDef->getFunction()) && + "Array initialization should produce the proper tuple type."); + + // Analyze the array object uses. + DeadObjectAnalysis DeadArray(ArrayDef); + if (!DeadArray.analyze()) + return false; + + // Require all stores to be into the array storage not the array object, + // otherwise bail. + bool HasStores = false; + DeadArray.visitStoreLocations([&](ArrayRef){ HasStores = true; }); + if (HasStores) + return false; + + // Remove references to empty arrays. + if (!StorageAddress) { + removeInstructions(DeadArray.getAllUsers()); + return true; + } + assert(StorageAddress->getType().isTrivial(*ArrayDef->getFunction()) && + "Array initialization should produce the proper tuple type."); + + // Analyze the array storage uses. + DeadObjectAnalysis DeadStorage(StorageAddress); + if (!DeadStorage.analyze()) + return false; + + // Find array object lifetime. + ValueLifetimeAnalysis VLA(NewArrayValue, DeadArray.getAllUsers()); + + // Check that all storage users are in the Array's live blocks. + for (auto *User : DeadStorage.getAllUsers()) { + if (!VLA.isWithinLifetime(User)) + return false; + } + // For each store location, insert releases. + SILSSAUpdater SSAUp; + ValueLifetimeAnalysis::Frontier ArrayFrontier; + if (!VLA.computeFrontier(ArrayFrontier, + ValueLifetimeAnalysis::UsersMustPostDomDef, + &DEBlocks)) { + // In theory the allocated object must be released on all paths in which + // some object initialization occurs. If not (for some reason) we bail. + return false; + } + + DeadStorage.visitStoreLocations([&] (ArrayRef Stores) { + insertReleases(Stores, ArrayFrontier, SSAUp); + }); + + // Delete all uses of the dead array and its storage address. + removeInstructions(DeadArray.getAllUsers()); + removeInstructions(DeadStorage.getAllUsers()); + + return true; +} + bool DeadObjectElimination::processAllocApply(ApplyInst *AI, DeadEndBlocks &DEBlocks) { // Currently only handle array.uninitialized @@ -1011,13 +1035,11 @@ bool DeadObjectElimination::processAllocApply(ApplyInst *AI, LLVM_DEBUG(llvm::dbgs() << " Success! Eliminating apply allocate(...).\n"); - eraseUsesOfInstruction(AI); - assert(AI->use_empty() && "All users should have been removed."); - recursivelyDeleteTriviallyDeadInstructions(AI, true); - if (instsDeadAfterInitializerRemoved.size()) { - recursivelyDeleteTriviallyDeadInstructions(instsDeadAfterInitializerRemoved, - true); + deleter.forceDeleteWithUsers(AI); + for (auto *toDelete : instsDeadAfterInitializerRemoved) { + deleter.trackIfDead(toDelete); } + ++DeadAllocApplyEliminated; return true; } diff --git a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp index 41a146cf09e66..fca73d20286b8 100644 --- a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp @@ -400,9 +400,10 @@ enum class ProcessKind { /// Process instructions. Extract locations from SIL StoreInst. void processStoreInst(SILInstruction *Inst, DSEKind Kind); - /// Process instructions. Extract locations from SIL DebugValueAddrInst. - /// DebugValueAddrInst maybe promoted to DebugValue, when this is done, - /// DebugValueAddrInst is effectively a read on the location. + /// Process instructions. Extract locations from SIL DebugValueInst. + /// DebugValueInst w/ address value maybe promoted to DebugValueInst w/ + /// scalar value when this is done, + /// DebugValueInst is effectively a read on the location. void processDebugValueAddrInst(SILInstruction *I, DSEKind Kind); void processDebugValueAddrInstForGenKillSet(SILInstruction *I); void processDebugValueAddrInstForDSE(SILInstruction *I); @@ -1045,7 +1046,7 @@ void DSEContext::processStoreInst(SILInstruction *I, DSEKind Kind) { void DSEContext::processDebugValueAddrInstForGenKillSet(SILInstruction *I) { BlockState *S = getBlockState(I); - SILValue Mem = cast(I)->getOperand(); + SILValue Mem = cast(I)->getOperand(); for (unsigned i = 0; i < S->LocationNum; ++i) { if (!S->BBMaxStoreSet.test(i)) continue; @@ -1058,7 +1059,7 @@ void DSEContext::processDebugValueAddrInstForGenKillSet(SILInstruction *I) { void DSEContext::processDebugValueAddrInstForDSE(SILInstruction *I) { BlockState *S = getBlockState(I); - SILValue Mem = cast(I)->getOperand(); + SILValue Mem = cast(I)->getOperand(); for (unsigned i = 0; i < S->LocationNum; ++i) { if (!S->isTrackingLocation(S->BBWriteSetMid, i)) continue; @@ -1140,11 +1141,11 @@ void DSEContext::processInstruction(SILInstruction *I, DSEKind Kind) { processLoadInst(I, Kind); } else if (isa(I)) { processStoreInst(I, Kind); - } else if (isa(I)) { + } else if (DebugValueInst::hasAddrVal(I)) { processDebugValueAddrInst(I, Kind); } else if (I->mayReadFromMemory()) { processUnknownReadInst(I, Kind); - } + } // Check whether this instruction will invalidate any other locations. for (auto result : I->getResults()) @@ -1275,7 +1276,7 @@ class DeadStoreElimination : public SILFunctionTransform { LLVM_DEBUG(llvm::dbgs() << "*** DSE on function: " << F->getName() << " ***\n"); - auto *AA = PM->getAnalysis(); + auto *AA = PM->getAnalysis(F); auto *TE = PM->getAnalysis(); auto *EAFI = PM->getAnalysis()->get(F); diff --git a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp index 5492e0ef06adb..6d0e7c647040c 100644 --- a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp @@ -333,16 +333,21 @@ void DestroyHoisting::getUsedLocationsOfOperands(Bits &bits, SILInstruction *I) } } -// Set all bits of locations which instruction \p I is using. It's including -// parent and sub-locations (see comment in getUsedLocationsOfAddr). +// NOTE: All instructions handled in +// MemoryLocations::analyzeLocationUsesRecursively should also be handled +// explicitly here. +// Set all bits of locations which instruction \p I is using. +// It's including parent and sub-locations (see comment in +// getUsedLocationsOfAddr). void DestroyHoisting::getUsedLocationsOfInst(Bits &bits, SILInstruction *I) { switch (I->getKind()) { - case SILInstructionKind::EndBorrowInst: - if (auto *LBI = dyn_cast( - cast(I)->getOperand())) { + case SILInstructionKind::EndBorrowInst: { + auto op = cast(I)->getOperand(); + if (auto *LBI = dyn_cast(op)) { getUsedLocationsOfAddr(bits, LBI->getOperand()); } break; + } case SILInstructionKind::EndApplyInst: // Operands passed to begin_apply are alive throughout an end_apply ... getUsedLocationsOfOperands(bits, cast(I)->getBeginApply()); @@ -351,6 +356,19 @@ void DestroyHoisting::getUsedLocationsOfInst(Bits &bits, SILInstruction *I) { // ... or abort_apply. getUsedLocationsOfOperands(bits, cast(I)->getBeginApply()); break; + case SILInstructionKind::EndAccessInst: + getUsedLocationsOfOperands(bits, + cast(I)->getBeginAccess()); + break; + // These instructions do not access the memory location for read/write + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::WitnessMethodInst: + case SILInstructionKind::OpenExistentialAddrInst: + break; + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::InitEnumDataAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: case SILInstructionKind::SelectEnumAddrInst: case SILInstructionKind::ExistentialMetatypeInst: case SILInstructionKind::ValueMetatypeInst: @@ -368,11 +386,12 @@ void DestroyHoisting::getUsedLocationsOfInst(Bits &bits, SILInstruction *I) { case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::YieldInst: + case SILInstructionKind::SwitchEnumAddrInst: getUsedLocationsOfOperands(bits, I); break; - case SILInstructionKind::DebugValueAddrInst: + case SILInstructionKind::DebugValueInst: case SILInstructionKind::DestroyAddrInst: - // destroy_addr and debug_value_addr are handled specially. + // destroy_addr and debug_value are handled specially. break; default: break; @@ -427,7 +446,7 @@ void DestroyHoisting::moveDestroys(BitDataflow &dataFlow) { return activeDestroys == dataFlow[P].exitSet; })); - // Delete all destroy_addr and debug_value_addr which are scheduled for + // Delete all destroy_addr and debug_value which are scheduled for // removal. processRemoveList(toRemove); } @@ -462,15 +481,15 @@ void DestroyHoisting::moveDestroysInBlock( if (destroyedLoc >= 0) { activeDestroys.set(destroyedLoc); toRemove.push_back(&I); - } else if (auto *DVA = dyn_cast(&I)) { - // debug_value_addr does not count as real use of a location. If we are - // moving a destroy_addr above a debug_value_addr, just delete that - // debug_value_addr. - auto *dvaLoc = locations.getLocation(DVA->getOperand()); + } else if (auto *DV = DebugValueInst::hasAddrVal(&I)) { + // debug_value w/ address value does not count as real use of a location. + // If we are moving a destroy_addr above a debug_value, just delete that + // debug_value. + auto *dvaLoc = locations.getLocation(DV->getOperand()); if (dvaLoc && locationOverlaps(dvaLoc, activeDestroys)) - toRemove.push_back(DVA); + toRemove.push_back(DV); } else if (I.mayHaveSideEffects()) { - // Delete all destroy_addr and debug_value_addr which are scheduled for + // Delete all destroy_addr and debug_value which are scheduled for // removal. processRemoveList(toRemove); } @@ -486,7 +505,7 @@ void DestroyHoisting::insertDestroys(Bits &toInsert, Bits &activeDestroys, if (toInsert.none()) return; - // The removeList contains destroy_addr (and debug_value_addr) instructions + // The removeList contains destroy_addr (and debug_value) instructions // which we want to delete, but we didn't see any side-effect instructions // since then. There is no value in moving a destroy_addr over side-effect- // free instructions (it could even trigger creating redundant address @@ -503,10 +522,10 @@ void DestroyHoisting::insertDestroys(Bits &toInsert, Bits &activeDestroys, activeDestroys.reset(destroyedLoc); return true; } - if (auto *DVA = dyn_cast(I)) { - // Also keep debug_value_addr instructions, located before a + if (auto *DV = DebugValueInst::hasAddrVal(I)) { + // Also keep debug_value instructions, located before a // destroy_addr which we won't move. - auto *dvaLoc = locations.getLocation(DVA->getOperand()); + auto *dvaLoc = locations.getLocation(DV->getOperand()); if (dvaLoc && dvaLoc->selfAndParents.anyCommon(keepDestroyedLocs)) return true; } @@ -765,18 +784,11 @@ class DestroyHoistingPass : public SILFunctionTransform { LLVM_DEBUG(llvm::dbgs() << "*** DestroyHoisting on function: " << F->getName() << " ***\n"); - bool EdgeChanged = splitAllCriticalEdges(*F, nullptr, nullptr); - DominanceAnalysis *DA = PM->getAnalysis(); DestroyHoisting CM(F, DA); bool InstChanged = CM.hoistDestroys(); - if (EdgeChanged) { - // We split critical edges. - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - return; - } if (InstChanged) { // We moved instructions. invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); diff --git a/lib/SILOptimizer/Transforms/MergeCondFail.cpp b/lib/SILOptimizer/Transforms/MergeCondFail.cpp index 0b293850eb784..cd89c118191c7 100644 --- a/lib/SILOptimizer/Transforms/MergeCondFail.cpp +++ b/lib/SILOptimizer/Transforms/MergeCondFail.cpp @@ -125,6 +125,6 @@ class MergeCondFailInsts : public SILFunctionTransform { }; } // end anonymous namespace -SILTransform *swift::createMergeCondFails() { +SILTransform *swift::createLegacyMergeCondFails() { return new MergeCondFailInsts(); } diff --git a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp index ac886521ec55c..9f93353ec9c2c 100644 --- a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp +++ b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp @@ -123,7 +123,6 @@ bool ObjectOutliner::isValidUseOfObject(SILInstruction *I, return true; switch (I->getKind()) { - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::DebugValueInst: case SILInstructionKind::LoadInst: case SILInstructionKind::DeallocRefInst: diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 8b30d36e311f1..14e527762418c 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -306,6 +306,13 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { return FunctionType; } +static void eraseBlock(SILBasicBlock *block) { + for (SILInstruction &inst : *block) { + inst.replaceAllUsesOfAllResultsWithUndef(); + } + block->eraseFromParent(); +} + std::pair BridgedProperty::outline(SILModule &M) { // Get the function type. @@ -362,14 +369,10 @@ BridgedProperty::outline(SILModule &M) { // Delete the outlined instructions/blocks. if (Release) Release->eraseFromParent(); - OutlinedEntryBB->eraseInstructions(); - OutlinedEntryBB->eraseFromParent(); - switchInfo.NoneBB->eraseInstructions(); - switchInfo.NoneBB->eraseFromParent(); - switchInfo.SomeBB->eraseInstructions(); - switchInfo.SomeBB->eraseFromParent(); - OldMergeBB->eraseInstructions(); - OldMergeBB->eraseFromParent(); + eraseBlock(OutlinedEntryBB); + eraseBlock(switchInfo.NoneBB); + eraseBlock(switchInfo.SomeBB); + eraseBlock(OldMergeBB); return std::make_pair(nullptr, std::prev(StartBB->end())); } @@ -904,14 +907,10 @@ void BridgedReturn::outline(SILFunction *Fun, ApplyInst *NewOutlinedCall) { // Outlined function already existed. Just delete instructions and wire up // blocks. if (!Fun) { - OutlinedEntryBB->eraseInstructions(); - OutlinedEntryBB->eraseFromParent(); - switchInfo.NoneBB->eraseInstructions(); - switchInfo.NoneBB->eraseFromParent(); - switchInfo.SomeBB->eraseInstructions(); - switchInfo.SomeBB->eraseFromParent(); - OldMergeBB->eraseInstructions(); - OldMergeBB->eraseFromParent(); + eraseBlock(OutlinedEntryBB); + eraseBlock(switchInfo.NoneBB); + eraseBlock(switchInfo.SomeBB); + eraseBlock(OldMergeBB); return; } diff --git a/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp b/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp index 5a71965f8be34..86d8a1d9e8603 100644 --- a/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp +++ b/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp @@ -1363,7 +1363,7 @@ SILValue RLEContext::computePredecessorLocationValue(SILBasicBlock *BB, } endLifetimeAtLeakingBlocks(phi, userBBs); } - return makeNewValueAvailable(Val, BB); + return makeValueAvailable(Val, BB); } bool RLEContext::collectLocationValues(SILBasicBlock *BB, LSLocation &L, @@ -1681,7 +1681,7 @@ class RedundantLoadElimination : public SILFunctionTransform { LLVM_DEBUG(llvm::dbgs() << "*** RLE on function: " << F->getName() << " ***\n"); - auto *AA = PM->getAnalysis(); + auto *AA = PM->getAnalysis(F); auto *TE = PM->getAnalysis(); auto *PO = PM->getAnalysis()->get(F); auto *EAFI = PM->getAnalysis()->get(F); diff --git a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp index b0a1c444f8899..5f6cd3a25a23f 100644 --- a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp +++ b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp @@ -1778,7 +1778,7 @@ class SILCodeMotion : public SILFunctionTransform { if (F->hasOwnership()) return; - auto *AA = getAnalysis(); + auto *AA = getAnalysis(F); auto *PO = getAnalysis()->get(F); auto *RCIA = getAnalysis()->get(getFunction()); diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index 907d22ae6d286..aff1f1a85e650 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -63,7 +63,8 @@ using DomTreeLevelMap = llvm::DenseMap; //===----------------------------------------------------------------------===// static void replaceDestroy(DestroyAddrInst *dai, SILValue newValue, - SILBuilderContext &ctx) { + SILBuilderContext &ctx, + InstructionDeleter &deleter) { SILFunction *f = dai->getFunction(); auto ty = dai->getOperand()->getType(); @@ -83,28 +84,41 @@ static void replaceDestroy(DestroyAddrInst *dai, SILValue newValue, : TypeExpansionKind::None; typeLowering.emitLoweredDestroyValue(builder, dai->getLoc(), newValue, expansionKind); - dai->eraseFromParent(); + deleter.forceDelete(dai); } -/// Promote a DebugValueAddr to a DebugValue of the given value. -static void promoteDebugValueAddr(DebugValueAddrInst *dvai, SILValue value, - SILBuilderContext &ctx) { +/// Promote a DebugValue w/ address value to a DebugValue of non-address value. +static void promoteDebugValueAddr(DebugValueInst *dvai, SILValue value, + SILBuilderContext &ctx, + InstructionDeleter &deleter) { assert(dvai->getOperand()->getType().isLoadable(*dvai->getFunction()) && "Unexpected promotion of address-only type!"); assert(value && "Expected valid value"); // Avoid inserting the same debug_value twice. for (auto *use : value->getUses()) { if (auto *dvi = dyn_cast(use->getUser())) { - if (*dvi->getVarInfo() == *dvai->getVarInfo()) { - dvai->eraseFromParent(); + // Since we're not comparing di-expression in + // SILDebugVariable::operator==(), it's necessary to distinguish + // debug_value w/ normal values from that with address-type values. + if (!dvi->hasAddrVal() && + *dvi->getVarInfo() == *dvai->getVarInfo()) { + deleter.forceDelete(dvai); return; } } } + auto VarInfo = *dvai->getVarInfo(); + // Drop op_deref if dvai is actually a debug_value instruction + if (isa(dvai)) { + auto &DIExpr = VarInfo.DIExpr; + if (DIExpr) + DIExpr.eraseElement(DIExpr.element_begin()); + } + SILBuilderWithScope b(dvai, ctx); - b.createDebugValue(dvai->getLoc(), value, *dvai->getVarInfo()); - dvai->eraseFromParent(); + b.createDebugValue(dvai->getLoc(), value, std::move(VarInfo)); + deleter.forceDelete(dvai); } /// Returns true if \p I is a load which loads from \p ASI. @@ -141,7 +155,7 @@ static void collectLoads(SILInstruction *i, } static void replaceLoad(LoadInst *li, SILValue newValue, AllocStackInst *asi, - SILBuilderContext &ctx) { + SILBuilderContext &ctx, InstructionDeleter &deleter) { ProjectionPath projections(newValue->getType()); SILValue op = li->getOperand(); SILBuilderWithScope builder(li, ctx); @@ -191,14 +205,14 @@ static void replaceLoad(LoadInst *li, SILValue newValue, AllocStackInst *asi, std::move(scope).popAtEndOfScope(&*builder.getInsertionPoint()); // Delete the load - li->eraseFromParent(); + deleter.forceDelete(li); while (op != asi && op->use_empty()) { assert(isa(op) || isa(op) || isa(op)); auto *inst = cast(op); SILValue next = inst->getOperand(0); - inst->eraseFromParent(); + deleter.forceDelete(inst); op = next; } } @@ -226,7 +240,7 @@ namespace { /// Promotes a single AllocStackInst into registers.. class StackAllocationPromoter { - using BlockSet = BasicBlockSetVector; + using BlockSetVector = BasicBlockSetVector; using BlockToInstMap = llvm::DenseMap; // Use a priority queue keyed on dominator tree level so that inserted nodes @@ -253,6 +267,8 @@ class StackAllocationPromoter { /// promotion. SILBuilderContext &ctx; + InstructionDeleter &deleter; + /// Records the last store instruction in each block for a specific /// AllocStackInst. BlockToInstMap lastStoreInBlock; @@ -261,9 +277,10 @@ class StackAllocationPromoter { /// C'tor. StackAllocationPromoter(AllocStackInst *inputASI, DominanceInfo *inputDomInfo, DomTreeLevelMap &inputDomTreeLevels, - SILBuilderContext &inputCtx) + SILBuilderContext &inputCtx, + InstructionDeleter &deleter) : asi(inputASI), dsi(nullptr), domInfo(inputDomInfo), - domTreeLevels(inputDomTreeLevels), ctx(inputCtx) { + domTreeLevels(inputDomTreeLevels), ctx(inputCtx), deleter(deleter) { // Scan the users in search of a deallocation instruction. for (auto *use : asi->getUses()) { if (auto *foundDealloc = dyn_cast(use->getUser())) { @@ -286,26 +303,40 @@ class StackAllocationPromoter { void promoteAllocationToPhi(); /// Replace the dummy nodes with new block arguments. - void addBlockArguments(BlockSet &phiBlocks); + void addBlockArguments(BlockSetVector &phiBlocks); + + /// Check if \p phi is a proactively added phi by SILMem2Reg + bool isProactivePhi(SILPhiArgument *phi, const BlockSetVector &phiBlocks); + + /// Check if \p proactivePhi is live. + bool isNecessaryProactivePhi(SILPhiArgument *proactivePhi, + const BlockSetVector &phiBlocks); + + /// Given a \p proactivePhi that is live, backward propagate liveness to + /// other proactivePhis. + void propagateLiveness(SILPhiArgument *proactivePhi, + const BlockSetVector &phiBlocks, + SmallPtrSetImpl &livePhis); /// Fix all of the branch instructions and the uses to use /// the AllocStack definitions (which include stores and Phis). - void fixBranchesAndUses(BlockSet &blocks); + void fixBranchesAndUses(BlockSetVector &blocks); /// update the branch instructions with the new Phi argument. /// The blocks in \p PhiBlocks are blocks that define a value, \p Dest is /// the branch destination, and \p Pred is the predecessors who's branch we /// modify. - void fixPhiPredBlock(BlockSet &phiBlocks, SILBasicBlock *dest, + void fixPhiPredBlock(BlockSetVector &phiBlocks, SILBasicBlock *dest, SILBasicBlock *pred); /// Get the value for this AllocStack variable that is /// flowing out of StartBB. - SILValue getLiveOutValue(BlockSet &phiBlocks, SILBasicBlock *startBlock); + SILValue getLiveOutValue(BlockSetVector &phiBlocks, + SILBasicBlock *startBlock); /// Get the value for this AllocStack variable that is /// flowing into BB. - SILValue getLiveInValue(BlockSet &phiBlocks, SILBasicBlock *block); + SILValue getLiveInValue(BlockSetVector &phiBlocks, SILBasicBlock *block); /// Prune AllocStacks usage in the function. Scan the function /// and remove in-block usage of the AllocStack. Leave only the first @@ -344,7 +375,7 @@ StoreInst *StackAllocationPromoter::promoteAllocationInBlock( // If we are loading from the AllocStackInst and we already know the // content of the Alloca then use it. LLVM_DEBUG(llvm::dbgs() << "*** Promoting load: " << *li); - replaceLoad(li, runningVal, asi, ctx); + replaceLoad(li, runningVal, asi, ctx, deleter); ++NumInstRemoved; } else if (li->getOperand() == asi && li->getOwnershipQualifier() != LoadOwnershipQualifier::Copy) { @@ -389,7 +420,7 @@ StoreInst *StackAllocationPromoter::promoteAllocationInBlock( LLVM_DEBUG(llvm::dbgs() << "*** Removing redundant store: " << *lastStore); ++NumInstRemoved; - lastStore->eraseFromParent(); + deleter.forceDelete(lastStore); } // The stored value is the new running value. @@ -399,19 +430,20 @@ StoreInst *StackAllocationPromoter::promoteAllocationInBlock( continue; } - // Replace debug_value_addr with debug_value of the promoted value + // Replace debug_value w/ address value with debug_value of + // the promoted value. // if we have a valid value to use at this point. Otherwise we'll // promote this when we deal with hooking up phis. - if (auto *dvai = dyn_cast(inst)) { - if (dvai->getOperand() == asi && runningVal) - promoteDebugValueAddr(dvai, runningVal, ctx); + if (auto *dvi = DebugValueInst::hasAddrVal(inst)) { + if (dvi->getOperand() == asi && runningVal) + promoteDebugValueAddr(dvi, runningVal, ctx, deleter); continue; } // Replace destroys with a release of the value. if (auto *dai = dyn_cast(inst)) { if (dai->getOperand() == asi && runningVal) { - replaceDestroy(dai, runningVal, ctx); + replaceDestroy(dai, runningVal, ctx, deleter); } continue; } @@ -445,14 +477,14 @@ StoreInst *StackAllocationPromoter::promoteAllocationInBlock( return lastStore; } -void StackAllocationPromoter::addBlockArguments(BlockSet &phiBlocks) { +void StackAllocationPromoter::addBlockArguments(BlockSetVector &phiBlocks) { LLVM_DEBUG(llvm::dbgs() << "*** Adding new block arguments.\n"); for (auto *block : phiBlocks) block->createPhiArgument(asi->getElementType(), OwnershipKind::Owned); } -SILValue StackAllocationPromoter::getLiveOutValue(BlockSet &phiBlocks, +SILValue StackAllocationPromoter::getLiveOutValue(BlockSetVector &phiBlocks, SILBasicBlock *startBlock) { LLVM_DEBUG(llvm::dbgs() << "*** Searching for a value definition.\n"); // Walk the Dom tree in search of a defining value: @@ -484,7 +516,7 @@ SILValue StackAllocationPromoter::getLiveOutValue(BlockSet &phiBlocks, return SILUndef::get(asi->getElementType(), *asi->getFunction()); } -SILValue StackAllocationPromoter::getLiveInValue(BlockSet &phiBlocks, +SILValue StackAllocationPromoter::getLiveInValue(BlockSetVector &phiBlocks, SILBasicBlock *block) { // First, check if there is a Phi value in the current block. We know that // our loads happen before stores, so we need to first check for Phi nodes @@ -507,7 +539,7 @@ SILValue StackAllocationPromoter::getLiveInValue(BlockSet &phiBlocks, return getLiveOutValue(phiBlocks, iDom->getBlock()); } -void StackAllocationPromoter::fixPhiPredBlock(BlockSet &phiBlocks, +void StackAllocationPromoter::fixPhiPredBlock(BlockSetVector &phiBlocks, SILBasicBlock *destBlock, SILBasicBlock *predBlock) { TermInst *ti = predBlock->getTerminator(); @@ -518,10 +550,56 @@ void StackAllocationPromoter::fixPhiPredBlock(BlockSet &phiBlocks, LLVM_DEBUG(llvm::dbgs() << "*** Found the definition: " << *def); addArgumentToBranch(def, destBlock, ti); - ti->eraseFromParent(); + deleter.forceDelete(ti); +} + +bool StackAllocationPromoter::isProactivePhi(SILPhiArgument *phi, + const BlockSetVector &phiBlocks) { + auto *phiBlock = phi->getParentBlock(); + return phiBlocks.contains(phiBlock) && + phi == phiBlock->getArgument(phiBlock->getNumArguments() - 1); +} + +bool StackAllocationPromoter::isNecessaryProactivePhi( + SILPhiArgument *proactivePhi, const BlockSetVector &phiBlocks) { + assert(isProactivePhi(proactivePhi, phiBlocks)); + for (auto *use : proactivePhi->getUses()) { + auto *branch = dyn_cast(use->getUser()); + // A non-branch use is a necessary use + if (!branch) + return true; + auto *destBB = branch->getDestBB(); + auto opNum = use->getOperandNumber(); + // A phi has a necessary use if it is used as a branch op for a + // non-proactive phi + if (!phiBlocks.contains(destBB) || (opNum != branch->getNumArgs() - 1)) + return true; + } + return false; +} + +void StackAllocationPromoter::propagateLiveness( + SILPhiArgument *proactivePhi, const BlockSetVector &phiBlocks, + SmallPtrSetImpl &livePhis) { + assert(isProactivePhi(proactivePhi, phiBlocks)); + if (livePhis.contains(proactivePhi)) + return; + // If liveness has not been propagated, go over the incoming operands and mark + // any operand values that are proactivePhis as live + livePhis.insert(proactivePhi); + SmallVector incomingPhiVals; + proactivePhi->getIncomingPhiValues(incomingPhiVals); + for (auto &inVal : incomingPhiVals) { + auto *inPhi = dyn_cast(inVal); + if (!inPhi) + continue; + if (!isProactivePhi(inPhi, phiBlocks)) + continue; + propagateLiveness(inPhi, phiBlocks, livePhis); + } } -void StackAllocationPromoter::fixBranchesAndUses(BlockSet &phiBlocks) { +void StackAllocationPromoter::fixBranchesAndUses(BlockSetVector &phiBlocks) { // First update uses of the value. SmallVector collectedLoads; @@ -545,7 +623,7 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &phiBlocks) { << "*** Replacing " << *li << " with Def " << *def); // Replace the load with the definition that we found. - replaceLoad(li, def, asi, ctx); + replaceLoad(li, def, asi, ctx, deleter); removedUser = true; ++NumInstRemoved; } @@ -558,10 +636,11 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &phiBlocks) { // on. SILBasicBlock *userBlock = user->getParent(); - if (auto *dvai = dyn_cast(user)) { - // Replace DebugValueAddr with DebugValue. + if (auto *dvi = DebugValueInst::hasAddrVal(user)) { + // Replace debug_value w/ address-type value with + // a new debug_value w/ promoted value. SILValue def = getLiveInValue(phiBlocks, userBlock); - promoteDebugValueAddr(dvai, def, ctx); + promoteDebugValueAddr(dvi, def, ctx, deleter); ++NumInstRemoved; continue; } @@ -569,14 +648,13 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &phiBlocks) { // Replace destroys with a release of the value. if (auto *dai = dyn_cast(user)) { SILValue def = getLiveInValue(phiBlocks, userBlock); - replaceDestroy(dai, def, ctx); + replaceDestroy(dai, def, ctx, deleter); continue; } } // Now that all of the uses are fixed we can fix the branches that point // to the blocks with the added arguments. - // For each Block with a new Phi argument: for (auto *block : phiBlocks) { // Fix all predecessors. @@ -590,21 +668,37 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &phiBlocks) { } } - // If the owned phi arg we added did not have any uses, create end_lifetime to - // end its lifetime. In asserts mode, make sure we have only undef incoming - // values for such phi args. - for (auto *block : phiBlocks) { - auto *phiArg = - cast(block->getArgument(block->getNumArguments() - 1)); - if (phiArg->use_empty()) { - erasePhiArgument(block, block->getNumArguments() - 1); + // Fix ownership of proactively created non-trivial phis + if (asi->getFunction()->hasOwnership() && + !asi->getType().isTrivial(*asi->getFunction())) { + SmallPtrSet livePhis; + + for (auto *block : phiBlocks) { + auto *proactivePhi = cast( + block->getArgument(block->getNumArguments() - 1)); + // First, check if the proactively added phi is necessary by looking at + // it's immediate uses. + if (isNecessaryProactivePhi(proactivePhi, phiBlocks)) { + // Backward propagate liveness to other dependent proactively added phis + propagateLiveness(proactivePhi, phiBlocks, livePhis); + } + } + // Go over all proactively added phis, and delete those that were not marked + // live above. + for (auto *block : phiBlocks) { + auto *proactivePhi = cast( + block->getArgument(block->getNumArguments() - 1)); + if (!livePhis.contains(proactivePhi)) { + proactivePhi->replaceAllUsesWithUndef(); + erasePhiArgument(block, block->getNumArguments() - 1); + } } } } void StackAllocationPromoter::pruneAllocStackUsage() { LLVM_DEBUG(llvm::dbgs() << "*** Pruning : " << *asi); - BlockSet functionBlocks(asi->getFunction()); + BlockSetVector functionBlocks(asi->getFunction()); // Insert all of the blocks that asi is live in. for (auto *use : asi->getUses()) @@ -625,7 +719,7 @@ void StackAllocationPromoter::promoteAllocationToPhi() { LLVM_DEBUG(llvm::dbgs() << "*** Placing Phis for : " << *asi); // A list of blocks that will require new Phi values. - BlockSet phiBlocks(asi->getFunction()); + BlockSetVector phiBlocks(asi->getFunction()); // The "piggy-bank" data-structure that we use for processing the dom-tree // bottom-up. @@ -758,6 +852,8 @@ class MemoryToRegisters { /// promotion. SILBuilderContext ctx; + InstructionDeleter deleter; + /// Returns the dom tree levels for the current function. Computes these /// lazily. DomTreeLevelMap &getDomTreeLevels() { @@ -891,9 +987,9 @@ static bool isCaptured(AllocStackInst *asi, bool &inSingleBlock) { if (si->getDest() == asi) continue; - // Deallocation is also okay, as are DebugValueAddr. We will turn - // the latter into DebugValue. - if (isa(user) || isa(user)) + // Deallocation is also okay, as are DebugValue w/ address value. We will + // promote the latter into normal DebugValue. + if (isa(user) || DebugValueInst::hasAddrVal(user)) continue; // Destroys of loadable types can be rewritten as releases, so @@ -928,8 +1024,8 @@ bool MemoryToRegisters::isWriteOnlyAllocation(AllocStackInst *asi) { continue; // If we haven't already promoted the AllocStack, we may see - // DebugValueAddr uses. - if (isa(user)) + // DebugValue uses. + if (DebugValueInst::hasAddrVal(user)) continue; if (isDeadAddrProjection(user)) @@ -965,7 +1061,7 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) { // Void (= empty tuple) or a tuple of Voids. runningVal = createValueForEmptyTuple(asi->getElementType(), inst, ctx); } - replaceLoad(cast(inst), runningVal, asi, ctx); + replaceLoad(cast(inst), runningVal, asi, ctx, deleter); ++NumInstRemoved; continue; } @@ -980,22 +1076,23 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) { runningVal); } runningVal = si->getSrc(); - inst->eraseFromParent(); + deleter.forceDelete(inst); ++NumInstRemoved; continue; } } - // Replace debug_value_addr with debug_value of the promoted value. - if (auto *dvai = dyn_cast(inst)) { - if (dvai->getOperand() == asi) { + // Replace debug_value w/ address value with debug_value of + // the promoted value. + if (auto *dvi = DebugValueInst::hasAddrVal(inst)) { + if (dvi->getOperand() == asi) { if (runningVal) { - promoteDebugValueAddr(dvai, runningVal, ctx); + promoteDebugValueAddr(dvi, runningVal, ctx, deleter); } else { - // Drop debug_value_addr of uninitialized void values. + // Drop debug_value of uninitialized void values. assert(asi->getElementType().isVoid() && "Expected initialization of non-void type!"); - dvai->eraseFromParent(); + deleter.forceDelete(dvi); } } continue; @@ -1004,7 +1101,7 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) { // Replace destroys with a release of the value. if (auto *dai = dyn_cast(inst)) { if (dai->getOperand() == asi) { - replaceDestroy(dai, runningVal, ctx); + replaceDestroy(dai, runningVal, ctx, deleter); } continue; } @@ -1012,7 +1109,7 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) { // Remove deallocation. if (auto *dsi = dyn_cast(inst)) { if (dsi->getOperand() == asi) { - inst->eraseFromParent(); + deleter.forceDelete(inst); NumInstRemoved++; // No need to continue scanning after deallocation. break; @@ -1026,7 +1123,7 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) { isa(addrInst) || isa(addrInst))) { SILValue op = addrInst->getOperand(0); - addrInst->eraseFromParent(); + deleter.forceDelete(addrInst); ++NumInstRemoved; addrInst = dyn_cast(op); } @@ -1041,6 +1138,12 @@ bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc) { LLVM_DEBUG(llvm::dbgs() << "*** Memory to register looking at: " << *alloc); ++NumAllocStackFound; + // In OSSA, don't do Mem2Reg on non-trivial alloc_stack with dynamic_lifetime. + if (alloc->hasDynamicLifetime() && f.hasOwnership() && + !alloc->getType().isTrivial(f)) { + return false; + } + // Don't handle captured AllocStacks. bool inSingleBlock = false; if (isCaptured(alloc, inSingleBlock)) { @@ -1050,7 +1153,7 @@ bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc) { // Remove write-only AllocStacks. if (isWriteOnlyAllocation(alloc)) { - eraseUsesOfInstruction(alloc); + deleter.forceDeleteWithUsers(alloc); LLVM_DEBUG(llvm::dbgs() << "*** Deleting store-only AllocStack: "<< *alloc); return true; @@ -1063,7 +1166,9 @@ bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc) { LLVM_DEBUG(llvm::dbgs() << "*** Deleting single block AllocStackInst: " << *alloc); - if (!alloc->use_empty()) { + if (alloc->use_empty()) { + deleter.forceDelete(alloc); + } else { // Handle a corner case where the ASI still has uses: // This can come up if the source contains a withUnsafePointer where // the pointer escapes. It's illegal code but we should not crash. @@ -1080,12 +1185,12 @@ bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc) { // Promote this allocation, lazily computing dom tree levels for this function // if we have not done so yet. auto &domTreeLevels = getDomTreeLevels(); - StackAllocationPromoter(alloc, domInfo, domTreeLevels, ctx).run(); + StackAllocationPromoter(alloc, domInfo, domTreeLevels, ctx, deleter).run(); // Make sure that all of the allocations were promoted into registers. assert(isWriteOnlyAllocation(alloc) && "Non-write uses left behind"); // ... and erase the allocation. - eraseUsesOfInstruction(alloc); + deleter.forceDeleteWithUsers(alloc); return true; } @@ -1096,20 +1201,12 @@ bool MemoryToRegisters::run() { f.verifyCriticalEdges(); for (auto &block : f) { - auto ii = block.begin(), ie = block.end(); - while (ii != ie) { - SILInstruction *inst = &*ii; + for (SILInstruction *inst : deleter.updatingReverseRange(&block)) { auto *asi = dyn_cast(inst); - if (!asi) { - ++ii; + if (!asi) continue; - } - bool promoted = promoteSingleAllocation(asi); - ++ii; - if (promoted) { - if (asi->use_empty()) - asi->eraseFromParent(); + if (promoteSingleAllocation(asi)) { ++NumInstRemoved; madeChange = true; } diff --git a/lib/SILOptimizer/Transforms/SILSROA.cpp b/lib/SILOptimizer/Transforms/SILSROA.cpp index 62221114446b8..3e02ed6f1ec8a 100644 --- a/lib/SILOptimizer/Transforms/SILSROA.cpp +++ b/lib/SILOptimizer/Transforms/SILSROA.cpp @@ -195,22 +195,31 @@ SROAMemoryUseAnalyzer:: createAllocas(llvm::SmallVector &NewAllocations) { SILBuilderWithScope B(AI); SILType Type = AI->getType().getObjectType(); + // Intentionally dropping the debug info. - // FIXME: VarInfo needs to be extended with fragment info to support this. SILLocation Loc = RegularLocation::getAutoGeneratedLocation(); if (TT) { + // TODO: Add op_fragment support for tuple type for (unsigned EltNo : indices(TT->getElementTypes())) { SILType EltTy = Type.getTupleElementType(EltNo); - NewAllocations.push_back(B.createAllocStack(Loc, EltTy, {})); + NewAllocations.push_back( + B.createAllocStack(Loc, EltTy, {}, AI->hasDynamicLifetime())); } } else { assert(SD && "SD should not be null since either it or TT must be set at " "this point."); SILModule &M = AI->getModule(); - for (auto *D : SD->getStoredProperties()) + for (VarDecl *VD : SD->getStoredProperties()) { + Optional NewDebugVarInfo = + SILDebugVariable::createFromAllocation(AI); + if (NewDebugVarInfo) + // TODO: Handle DIExpr that is already attached + NewDebugVarInfo->DIExpr = SILDebugInfoExpression::createFragment(VD); + NewAllocations.push_back(B.createAllocStack( - Loc, Type.getFieldType(D, M, TypeExpansionContext(B.getFunction())), - {})); + Loc, Type.getFieldType(VD, M, TypeExpansionContext(B.getFunction())), + NewDebugVarInfo, AI->hasDynamicLifetime())); + } } } diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index e05e49af7d61f..3e70a2bc8e255 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -22,6 +22,7 @@ #include "swift/SIL/SILUndef.h" #include "swift/SIL/TerminatorUtils.h" #include "swift/SIL/BasicBlockDatastructures.h" +#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Analysis/ProgramTerminationAnalysis.h" #include "swift/SILOptimizer/Analysis/SimplifyInstruction.h" @@ -43,6 +44,25 @@ #include "llvm/Support/Debug.h" using namespace swift; +// This is temporarily used for testing until Swift 5.5 branches to reduce risk. +llvm::cl::opt EnableOSSASimplifyCFG( + "enable-ossa-simplify-cfg", + llvm::cl::desc( + "Enable non-trivial OSSA simplify-cfg and simple jump threading " + "(staging).")); + +// This requires new OwnershipOptUtilities which aren't well tested yet. +llvm::cl::opt EnableOSSARewriteTerminator( + "enable-ossa-rewriteterminator", + llvm::cl::desc( + "Enable OSSA simplify-cfg with non-trivial terminator rewriting " + "(staging).")); + +llvm::cl::opt IsInfiniteJumpThreadingBudget( + "sil-infinite-jump-threading-budget", + llvm::cl::desc( + "Use infinite budget for jump threading. Useful for testing purposes")); + STATISTIC(NumBlocksDeleted, "Number of unreachable blocks removed"); STATISTIC(NumBlocksMerged, "Number of blocks merged together"); STATISTIC(NumJumpThreads, "Number of jumps threaded"); @@ -57,7 +77,7 @@ STATISTIC(NumSROAArguments, "Number of aggregate argument levels split by " //===----------------------------------------------------------------------===// /// dominatorBasedSimplify iterates between dominator based simplification of -/// terminator branch condition values and cfg simplification. This is the +/// terminator branch condition values and CFG simplification. This is the /// maximum number of iterations we run. The number is the maximum number of /// iterations encountered when compiling the stdlib on April 2 2015. /// @@ -72,6 +92,12 @@ class SimplifyCFG { SILFunction &Fn; SILPassManager *PM; + // DeadEndBlocks remains conservatively valid across updates that rewrite + // branches and remove edges. Any transformation that adds a block must call + // updateForReachableBlock(). Removing a block causes a dangling pointer + // within DeadEndBlocks, but this pointer can't be accessed by queries. + DeadEndBlocks *deBlocks = nullptr; + // WorklistList is the actual list that we iterate over (for determinism). // Slots may be null, which should be ignored. SmallVector WorklistList; @@ -386,8 +412,8 @@ bool SimplifyCFG::threadEdge(const ThreadInfo &ti) { return true; } -/// Give a cond_br or switch_enum instruction and one successor block return -/// true if we can infer the value of the condition/enum along the edge to this +/// Give a cond_br or switch_enum instruction and one successor block returns +/// true if we can infer the value of the condition/enum along the edge to these /// successor blocks. static bool isKnownEdgeValue(TermInst *Term, SILBasicBlock *SuccBB, EnumElementDecl *&EnumCase) { @@ -571,6 +597,8 @@ static bool tryDominatorBasedSimplifications( if (stripExpectIntrinsic(Op.get()) == DominatingCondition) { if (!EdgeValue) EdgeValue = createValueForEdge(UserInst, DominatingTerminator, Idx); + LLVM_DEBUG(llvm::dbgs() << "replace dominated operand\n in " + << *UserInst << " with " << EdgeValue); Op.set(EdgeValue); Changed = true; } @@ -661,6 +689,9 @@ bool SimplifyCFG::dominatorBasedSimplify(DominanceAnalysis *DA) { // Get the dominator tree. DT = DA->get(&Fn); + if (!EnableOSSASimplifyCFG && Fn.hasOwnership()) + return false; + // Split all critical edges such that we can move code onto edges. This is // also required for SSA construction in dominatorBasedSimplifications' jump // threading. It only splits new critical edges it creates by jump threading. @@ -668,16 +699,6 @@ bool SimplifyCFG::dominatorBasedSimplify(DominanceAnalysis *DA) { if (!Fn.hasOwnership() && EnableJumpThread) { Changed = splitAllCriticalEdges(Fn, DT, nullptr); } - - // TODO: OSSA phi support. Even if all block arguments are trivial, - // jump-threading may require creation of guaranteed phis, which may require - // creation of nested borrow scopes. - if (Fn.hasOwnership()) { - for (auto &BB : Fn) - Changed |= simplifyArgs(&BB); - return Changed; - } - unsigned MaxIter = MaxIterationsOfDominatorBasedSimplify; SmallVector BlocksForWorklist; @@ -688,7 +709,8 @@ bool SimplifyCFG::dominatorBasedSimplify(DominanceAnalysis *DA) { // Do dominator based simplification of terminator condition. This does not // and MUST NOT change the CFG without updating the dominator tree to // reflect such change. - if (tryCheckedCastBrJumpThreading(&Fn, DT, BlocksForWorklist)) { + if (tryCheckedCastBrJumpThreading(&Fn, DT, deBlocks, BlocksForWorklist, + EnableOSSARewriteTerminator)) { for (auto BB: BlocksForWorklist) addToWorklist(BB); @@ -816,7 +838,7 @@ getEnumCaseRecursive(SILValue Val, SILBasicBlock *UsedInBB, int RecursionDepth, Pred = UsedInBB->getSinglePredecessorBlock(); } - // In case of a block argument, recursively check the enum cases of all + // In case of a phi, recursively check the enum cases of all // incoming predecessors. if (auto *Arg = dyn_cast(Val)) { HandledArgs.insert(Arg); @@ -1029,15 +1051,19 @@ static bool hasInjectedEnumAtEndOfBlock(SILBasicBlock *block, SILValue enumAddr) /// tryJumpThreading - Check to see if it looks profitable to duplicate the /// destination of an unconditional jump into the bottom of this block. bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { - // TODO: OSSA phi support. Even if all block arguments are trivial, - // jump-threading may require creation of guaranteed phis, which may require - // creation of nested borrow scopes. - if (Fn.hasOwnership()) + if (!EnableOSSASimplifyCFG && Fn.hasOwnership()) return false; auto *DestBB = BI->getDestBB(); auto *SrcBB = BI->getParent(); TermInst *destTerminator = DestBB->getTerminator(); + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { + if (llvm::any_of(DestBB->getArguments(), [this](SILValue op) { + return !op->getType().isTrivial(Fn); + })) { + return false; + } + } // If the destination block ends with a return, we don't want to duplicate it. // We want to maintain the canonical form of a single return where possible. if (destTerminator->isFunctionExiting()) @@ -1045,7 +1071,7 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { // Jump threading only makes sense if there is an argument on the branch // (which is reacted on in the DestBB), or if this goes through a memory - // location (switch_enum_addr is the only adress-instruction which we + // location (switch_enum_addr is the only address-instruction which we // currently handle). if (BI->getArgs().empty() && !isa(destTerminator)) return false; @@ -1056,7 +1082,7 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { // major second order simplifications. Here we only do it if there are // "constant" arguments to the branch or if we know how to fold something // given the duplication. - int ThreadingBudget = 0; + int ThreadingBudget = IsInfiniteJumpThreadingBudget ? INT_MAX : 0; for (unsigned i : indices(BI->getArgs())) { SILValue Arg = BI->getArg(i); @@ -1183,11 +1209,19 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { /// result in exposing opportunities for CFG simplification. bool SimplifyCFG::simplifyBranchOperands(OperandValueArrayRef Operands) { InstModCallbacks callbacks; +#ifndef NDEBUG + callbacks = callbacks.onDelete( + [](SILInstruction *instToKill) { + LLVM_DEBUG(llvm::dbgs() << "simplify and erase " << *instToKill); + instToKill->eraseFromParent(); + }); +#endif + for (auto O = Operands.begin(), E = Operands.end(); O != E; ++O) { // All of our interesting simplifications are on single-value instructions // for now. if (auto *I = dyn_cast(*O)) { - simplifyAndReplaceAllSimplifiedUsesAndErase(I, callbacks); + simplifyAndReplaceAllSimplifiedUsesAndErase(I, callbacks, deBlocks); } } return callbacks.hadCallbackInvocation(); @@ -1820,8 +1854,7 @@ static bool isOnlyUnreachable(SILBasicBlock *BB) { /// switch_enum where all but one block consists of just an /// "unreachable" with an unchecked_enum_data and branch. bool SimplifyCFG::simplifySwitchEnumUnreachableBlocks(SwitchEnumInst *SEI) { - if (Fn.hasOwnership()) { - // TODO: OSSA; cleanup terminator results. + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { if (!SEI->getOperand()->getType().isTrivial(Fn)) return false; } @@ -1976,7 +2009,7 @@ static bool containsOnlyObjMethodCallOnOptional(SILValue optionalValue, } /// Check that all that noneBB does is forwarding none. -/// The only other allowed operation are ref count operations. +/// The only other allowed operations are ref count operations. static bool onlyForwardsNone(SILBasicBlock *noneBB, SILBasicBlock *someBB, SwitchEnumInst *SEI) { // It all the basic blocks leading up to the ultimate block we only expect @@ -2054,7 +2087,7 @@ static bool hasSameUltimateSuccessor(SILBasicBlock *noneBB, SILBasicBlock *someB // Otherwise, lets begin a traversal along the successors of noneSuccessorBB, // searching for someSuccessorBB, being careful to only allow for blocks to be - // visited once. This enables us to guarantee that there are not any loops or + // visited once. This enables us to guarantee that there no loops or // any sub-diamonds in the part of the CFG we are traversing. This /does/ // allow for side-entrances to the region from blocks not reachable from // noneSuccessorBB. See function level comment above. @@ -2118,7 +2151,7 @@ static bool hasSameUltimateSuccessor(SILBasicBlock *noneBB, SILBasicBlock *someB bool SimplifyCFG::simplifySwitchEnumOnObjcClassOptional(SwitchEnumInst *SEI) { // TODO: OSSA; handle non-trivial enum case cleanup // (simplify_switch_enum_objc.sil). - if (Fn.hasOwnership()) { + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { return false; } @@ -2182,7 +2215,7 @@ bool SimplifyCFG::simplifySwitchEnumBlock(SwitchEnumInst *SEI) { auto *LiveBlock = SEI->getCaseDestination(EnumCase.get()); auto *ThisBB = SEI->getParent(); - if (Fn.hasOwnership()) { + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { // TODO: OSSA; cleanup terminator results. if (!SEI->getOperand()->getType().isTrivial(Fn)) return false; @@ -2422,7 +2455,7 @@ bool SimplifyCFG::simplifyCheckedCastBranchBlock(CheckedCastBranchInst *CCBI) { bool SimplifyCFG::simplifyCheckedCastValueBranchBlock( CheckedCastValueBranchInst *CCBI) { // TODO: OSSA; handle cleanups for opaque cases (simplify_cfg_opaque.sil). - if (Fn.hasOwnership()) { + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { return false; } @@ -2639,7 +2672,7 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) { if (requiresOSSACleanup(newCallee)) { newCallee = SILBuilderWithScope(newCallee->getNextInstruction()) .createCopyValue(calleeLoc, newCallee); - newCallee = makeNewValueAvailable(newCallee, TAI->getParent()); + newCallee = makeValueAvailable(newCallee, TAI->getParent()); } ApplyOptions Options = TAI->getApplyOptions(); @@ -2681,8 +2714,6 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) { bool SimplifyCFG::simplifyTermWithIdenticalDestBlocks(SILBasicBlock *BB) { TrampolineDest commonDest; for (auto *SuccBlock : BB->getSuccessorBlocks()) { - if (SuccBlock->getNumArguments() != 0) - return false; auto trampolineDest = TrampolineDest(BB, SuccBlock); if (!trampolineDest) { return false; @@ -2700,7 +2731,7 @@ bool SimplifyCFG::simplifyTermWithIdenticalDestBlocks(SILBasicBlock *BB) { TermInst *Term = BB->getTerminator(); // TODO: OSSA; cleanup nontrivial terminator operands (if this ever actually // happens) - if (Fn.hasOwnership()) { + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { if (llvm::any_of(Term->getOperandValues(), [this](SILValue op) { return !op->getType().isTrivial(Fn); })) { @@ -2895,11 +2926,7 @@ bool SimplifyCFG::simplifyBlocks() { /// Canonicalize all switch_enum and switch_enum_addr instructions. /// If possible, replace the default with the corresponding unique case. bool SimplifyCFG::canonicalizeSwitchEnums() { - if (Fn.hasOwnership()) { - // TODO: OSSA. In OSSA, the default switch_enum case passes the - // original enum as a block argument. This needs to check that the block - // argument is dead, then replace it with the a new argument for the default - // payload. + if (!EnableOSSASimplifyCFG && Fn.hasOwnership()) { return false; } bool Changed = false; @@ -2917,6 +2944,15 @@ bool SimplifyCFG::canonicalizeSwitchEnums() { if (!elementDecl) continue; + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { + if (!SWI.getOperand()->getType().isTrivial(Fn)) { + // TODO: OSSA. In OSSA, the default switch_enum case passes the original + // enum as a block argument. This needs to check that the block argument + // is dead, then replace it with the a new argument for the default + // payload. + continue; + } + } LLVM_DEBUG(llvm::dbgs() << "simplify canonical switch_enum\n"); // Construct a new instruction by copying all the case entries. @@ -2925,9 +2961,25 @@ bool SimplifyCFG::canonicalizeSwitchEnums() { CaseBBs.push_back(SWI.getCase(idx)); } // Add the default-entry of the original instruction as case-entry. - CaseBBs.push_back(std::make_pair(elementDecl.get(), SWI.getDefaultBB())); + auto *defaultBB = SWI.getDefaultBB(); + CaseBBs.push_back(std::make_pair(elementDecl.get(), defaultBB)); if (isa(*SWI)) { + if (Fn.hasOwnership()) { + assert(defaultBB->getNumArguments() == 1); + defaultBB->getArgument(0)->replaceAllUsesWith(SWI.getOperand()); + defaultBB->eraseArgument(0); + // TODO: handle non-trivial payloads. The new block argument must be + // destroyed. The old default argument may need to be copied. But it may + // also be possible to optimize the common case on-the-fly without the + // extra copy/destroy. + if (elementDecl.get()->hasAssociatedValues()) { + // Note: this is not really a phi. + auto elementTy = SWI.getOperand()->getType().getEnumElementType( + elementDecl.get(), Fn.getModule(), Fn.getTypeExpansionContext()); + defaultBB->createPhiArgument(elementTy, OwnershipKind::None); + } + } SILBuilderWithScope(SWI).createSwitchEnum(SWI->getLoc(), SWI.getOperand(), nullptr, CaseBBs); } else { @@ -3003,7 +3055,7 @@ bool SimplifyCFG::tailDuplicateObjCMethodCallSuccessorBlocks() { // TODO: OSSA phi support. Even if all block arguments are trivial, // jump-threading may require creation of guaranteed phis, which may require // creation of nested borrow scopes. - if (Fn.hasOwnership()) { + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { return false; } // Collect blocks to tail duplicate. @@ -3212,7 +3264,7 @@ RemoveDeadArgsWhenSplitting("sroa-args-remove-dead-args-after", llvm::cl::init(true)); bool ArgumentSplitter::split() { - if (Arg->getFunction()->hasOwnership()) { + if (!EnableOSSARewriteTerminator && Arg->getFunction()->hasOwnership()) { // TODO: OSSA phi support if (!Arg->getType().isTrivial(*Arg->getFunction())) return false; @@ -3338,6 +3390,16 @@ bool SimplifyCFG::run() { // First remove any block not reachable from the entry. bool Changed = removeUnreachableBlocks(Fn); + DeadEndBlocksAnalysis *deBlocksAnalysis = + PM->getAnalysis(); + if (Changed) { + // Eliminate unreachable blocks from deBlocks. This isn't strictly necessary + // but avoids excess dangling pointers in deBlocks. + deBlocksAnalysis->invalidate(&Fn, + SILAnalysis::InvalidationKind::Everything); + } + deBlocks = deBlocksAnalysis->get(&Fn); + // Find the set of loop headers. We don't want to jump-thread through headers. findLoopHeaders(); @@ -3356,11 +3418,15 @@ bool SimplifyCFG::run() { // Do simplifications that require the dominator tree to be accurate. DominanceAnalysis *DA = PM->getAnalysis(); - if (Changed) { // Force dominator recomputation since we modified the cfg. DA->invalidate(&Fn, SILAnalysis::InvalidationKind::Everything); + // Eliminate unreachable blocks from deBlocks. This isn't strictly necessary + // but avoids excess dangling pointers in deBlocks. + deBlocksAnalysis->invalidate(&Fn, + SILAnalysis::InvalidationKind::Everything); } + deBlocks = deBlocksAnalysis->get(&Fn); Changed |= dominatorBasedSimplify(DA); @@ -4004,7 +4070,7 @@ bool SimplifyCFG::simplifyArgs(SILBasicBlock *BB) { if (BB->pred_empty()) return false; - if (Fn.hasOwnership()) { + if (!EnableOSSARewriteTerminator && Fn.hasOwnership()) { // TODO: OSSA phi support if (llvm::any_of(BB->getArguments(), [this](SILArgument *arg) { return !arg->getType().isTrivial(Fn); @@ -4048,13 +4114,13 @@ bool SimplifyCFG::simplifyProgramTerminationBlock(SILBasicBlock *BB) { // // TODO: should we use ProgramTerminationAnalysis ?. The reason we do not // use the analysis is because the CFG is likely to be invalidated right - // after this pass, o we do not really get the benefit of reusing the + // after this pass, that's why we do not really get the benefit of reusing the // computation for the next iteration of the pass. if (!isARCInertTrapBB(BB)) return false; // This is going to be the last basic block this program is going to execute - // and this block is inert from the ARC's prospective, no point to do any + // and this block is inert from the ARC's prospective,so there's no point to do any // releases at this point. bool Changed = false; llvm::SmallPtrSet InstsToRemove; diff --git a/lib/SILOptimizer/Transforms/StringOptimization.cpp b/lib/SILOptimizer/Transforms/StringOptimization.cpp index 6acf5fb810eba..bfc2fdc8611f5 100644 --- a/lib/SILOptimizer/Transforms/StringOptimization.cpp +++ b/lib/SILOptimizer/Transforms/StringOptimization.cpp @@ -437,10 +437,13 @@ isStringStoreToIdentifyableObject(SILInstruction *inst) { switch (user->getKind()) { // Those instructions do not write to destAddr nor let they destAddr // escape. - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::DeallocStackInst: case SILInstructionKind::LoadInst: break; + case SILInstructionKind::DebugValueInst: + if (DebugValueInst::hasAddrVal(user)) + break; + LLVM_FALLTHROUGH; default: if (!mayWriteToIdentifyableObject(user)) { // We don't handle user. It is some instruction which may write to @@ -471,11 +474,60 @@ void StringOptimization::invalidateModifiedObjects(SILInstruction *inst, } } +/// If \p value is a struct_extract, return its operand and field. +static std::pair skipStructExtract(SILValue value) { + if (auto *sei = dyn_cast(value)) + return {sei->getOperand(), sei->getField()}; + + // Look through function calls, which do the struct_extract in the callee. + // This specifically targets + // String(stringInterpolation: DefaultStringInterpolation) + // which is not inlined in the high level pipeline (due to the specified + // effects). + auto *apply = dyn_cast(value); + if (!apply) + return {value, nullptr}; + + SILFunction *callee = apply->getReferencedFunctionOrNull(); + if (!callee || !callee->isDefinition()) + return {value, nullptr}; + + // `String(stringInterpolation: DefaultStringInterpolation)` has only a single + // basic block. Avoid the effort of searching all blocks for a `return`. + auto *ret = dyn_cast(callee->getEntryBlock()->getTerminator()); + if (!ret) + return {value, nullptr}; + + auto *sei = dyn_cast(ret->getOperand()); + if (!sei) + return {value, nullptr}; + + auto *arg = dyn_cast(sei->getOperand()); + if (!arg) + return {value, nullptr}; + + value = apply->getArgument(arg->getIndex()); + return {value, sei->getField()}; +} + /// Returns information about value if it's a constant string. StringOptimization::StringInfo StringOptimization::getStringInfo(SILValue value) { if (!value) return StringInfo::unknown(); + // Look through struct_extract(struct(value)). + // This specifically targets calls to + // String(stringInterpolation: DefaultStringInterpolation) + // which are not inlined in the high level pipeline. + VarDecl *field = nullptr; + std::tie(value, field) = skipStructExtract(value); + if (field) { + auto *si = dyn_cast(value); + if (!si) + return StringInfo::unknown(); + value = si->getFieldValue(field); + } + auto *apply = dyn_cast(value); if (!apply) { return getStringFromStaticLet(value); diff --git a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp index 9679c17609cba..59a358dcfb983 100644 --- a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp +++ b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp @@ -78,10 +78,8 @@ class TempLValueOptPass : public SILFunctionTransform { void run() override; private: - AliasAnalysis *AA = nullptr; - - bool tempLValueOpt(CopyAddrInst *copyInst); - bool combineCopyAndDestroy(CopyAddrInst *copyInst); + void tempLValueOpt(CopyAddrInst *copyInst); + void combineCopyAndDestroy(CopyAddrInst *copyInst); }; void TempLValueOptPass::run() { @@ -92,9 +90,6 @@ void TempLValueOptPass::run() { LLVM_DEBUG(llvm::dbgs() << "*** TempLValueOptPass on function: " << F->getName() << " ***\n"); - AA = PM->getAnalysis(); - - bool changed = false; for (SILBasicBlock &block : *F) { // First collect all copy_addr instructions upfront to avoid iterator // invalidation problems (the optimizations might delete the copy_addr @@ -106,14 +101,10 @@ void TempLValueOptPass::run() { } // Do the optimizations. for (CopyAddrInst *copyInst : copyInsts) { - changed |= combineCopyAndDestroy(copyInst); - changed |= tempLValueOpt(copyInst); + combineCopyAndDestroy(copyInst); + tempLValueOpt(copyInst); } } - - if (changed) { - invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); - } } static SingleValueInstruction *isMovableProjection(SILValue addr) { @@ -129,7 +120,7 @@ static SingleValueInstruction *isMovableProjection(SILValue addr) { return nullptr; } -bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { +void TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { // An overview of the algorithm: // @@ -147,11 +138,11 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { // 'destroy_addr %destination' is inserted before the first use of %temp. if (!copyInst->isTakeOfSrc()) - return false; + return; auto *temporary = dyn_cast(copyInst->getSrc()); if (!temporary) - return false; + return; // Collect all users of the temporary into a set. Also, for simplicity, // require that all users are within a single basic block. @@ -160,7 +151,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { for (Operand *use : temporary->getUses()) { SILInstruction *user = use->getUser(); if (user->getParent() != block) - return false; + return; users.insert(user); } @@ -176,7 +167,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { // Just to be on the safe side, bail if it's not the case (instead of an // assert). if (users.count(projInst)) - return false; + return; projections.insert(projInst); destRootAddr = projInst->getOperand(0); } @@ -185,6 +176,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { SILInstruction *destRootInst = destRootAddr->getDefiningInstruction(); // Iterate over the liferange of the temporary and make some validity checks. + AliasAnalysis *AA = nullptr; SILInstruction *beginOfLiferange = nullptr; bool endOfLiferangeReached = false; for (auto iter = temporary->getIterator(); iter != block->end(); ++iter) { @@ -197,7 +189,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { // Check if the copyInst is the last user of the temporary (beside the // dealloc_stack). if (endOfLiferangeReached) - return false; + return; // Find the first user of the temporary to get a more precise liferange. // It would be too conservative to treat the alloc_stack itself as the @@ -213,8 +205,11 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { // temporary, we cannot replace all uses of the temporary with the // destination (it would break the def-use dominance rule). if (inst == destRootInst) - return false; + return; + if (!AA) + AA = PM->getAnalysis(getFunction()); + // Check if the destination is not accessed within the liferange of // the temporary. // This is unlikely, because the destination is initialized at the @@ -223,7 +218,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { if (AA->mayReadOrWriteMemory(inst, destination) && // Needed to treat init_existential_addr as not-writing projection. projections.count(inst) == 0) - return false; + return; } } assert(endOfLiferangeReached); @@ -253,18 +248,17 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) { user->eraseFromParent(); break; default: - AA->invalidateInstruction(user); use->set(destination); } } temporary->eraseFromParent(); copyInst->eraseFromParent(); - return true; + invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } -bool TempLValueOptPass::combineCopyAndDestroy(CopyAddrInst *copyInst) { +void TempLValueOptPass::combineCopyAndDestroy(CopyAddrInst *copyInst) { if (copyInst->isTakeOfSrc()) - return false; + return; // Find a destroy_addr of the copy's source address. DestroyAddrInst *destroy = nullptr; @@ -274,32 +268,32 @@ bool TempLValueOptPass::combineCopyAndDestroy(CopyAddrInst *copyInst) { } SILBasicBlock *block = copyInst->getParent(); if (!destroy || destroy->getParent() != block) - return false; + return; assert(destroy->getOperand() == copyInst->getSrc()); // Check if the destroy_addr is after the copy_addr and if there are no // memory accesses between them. - SmallVector debugInsts; + SmallVector debugInsts; for (auto iter = std::next(copyInst->getIterator()); iter != block->end(); ++iter) { SILInstruction *inst = &*iter; if (inst == destroy) { copyInst->setIsTakeOfSrc(IsTake); destroy->eraseFromParent(); - // Cleanup all debug_value_addr of the copy src btw the copy and destroy + // Cleanup all debug_value of the copy src btw the copy and destroy for (auto debugInst : debugInsts) { debugInst->eraseFromParent(); } - return true; + invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); + return; } - if (auto *debugInst = dyn_cast(inst)) { + if(auto *debugInst = DebugValueInst::hasAddrVal(inst)) { if (debugInst->getOperand() == copyInst->getSrc()) debugInsts.push_back(debugInst); } if (inst->mayReadOrWriteMemory()) - return false; + return; } - return false; } } // end anonymous namespace diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 993486709d9ed..57190b2a7f03d 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -75,8 +75,6 @@ namespace { /// /// TODO: Check if we still need to handle stores when RLE supports OSSA. class TempRValueOptPass : public SILFunctionTransform { - AliasAnalysis *aa = nullptr; - bool collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, SmallPtrSetImpl &loadInsts); bool collectLoadsFromProjection(SingleValueInstruction *projection, @@ -84,16 +82,17 @@ class TempRValueOptPass : public SILFunctionTransform { SmallPtrSetImpl &loadInsts); SILInstruction *getLastUseWhileSourceIsNotModified( - CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts); + CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts, + AliasAnalysis *aa); bool checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst); - bool extendAccessScopes(CopyAddrInst *copyInst, SILInstruction *lastUseInst); + bool extendAccessScopes(CopyAddrInst *copyInst, SILInstruction *lastUseInst, + AliasAnalysis *aa); - bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst); - std::pair - tryOptimizeStoreIntoTemp(StoreInst *si); + void tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst); + SILBasicBlock::iterator tryOptimizeStoreIntoTemp(StoreInst *si); void run() override; }; @@ -216,6 +215,15 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, } return true; } + case SILInstructionKind::YieldInst: { + auto *yield = cast(user); + auto convention = yield->getArgumentConventionForOperand(*addressUse); + if (!convention.isGuaranteedConvention()) + return false; + + loadInsts.insert(user); + return true; + } case SILInstructionKind::OpenExistentialAddrInst: { // We only support open existential addr if the access is immutable. auto *oeai = cast(user); @@ -306,7 +314,8 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy, /// of the temporary and look for the last use, which effectively ends the /// lifetime. SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified( - CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts) { + CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts, + AliasAnalysis *aa) { if (useInsts.empty()) return copyInst; unsigned numLoadsFound = 0; @@ -331,8 +340,10 @@ SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified( if (numLoadsFound == useInsts.size()) { // Function calls are an exception: in a called function a potential // modification of copySrc could occur _before_ the read of the temporary. - if (FullApplySite::isa(inst) && aa->mayWriteToMemory(inst, copySrc)) + if ((FullApplySite::isa(inst) || isa(inst)) && + aa->mayWriteToMemory(inst, copySrc)) { return nullptr; + } return inst; } @@ -358,7 +369,7 @@ SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified( /// We must not replace %temp with %a after the end_access. Instead we try to /// move the end_access after "use %temp". bool TempRValueOptPass::extendAccessScopes( - CopyAddrInst *copyInst, SILInstruction *lastUseInst) { + CopyAddrInst *copyInst, SILInstruction *lastUseInst, AliasAnalysis *aa) { SILValue copySrc = copyInst->getSrc(); EndAccessInst *endAccessToMove = nullptr; @@ -460,13 +471,13 @@ bool TempRValueOptPass::checkTempObjectDestroy( } /// Tries to perform the temporary rvalue copy elimination for \p copyInst -bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { +void TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (!copyInst->isInitializationOfDest()) - return false; + return; auto *tempObj = dyn_cast(copyInst->getDest()); if (!tempObj) - return false; + return; bool isOSSA = copyInst->getFunction()->hasOwnership(); @@ -502,21 +513,23 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { // Otherwise we would risk of inserting the destroy too early. // So we just treat the destroy_addr as any other use of tempObj. if (user->getParent() != copyInst->getParent()) - return false; + return; loadInsts.insert(user); } continue; } if (!collectLoads(useOper, copyInst, loadInsts)) - return false; + return; } + AliasAnalysis *aa = getPassManager()->getAnalysis(getFunction()); + // Check if the source is modified within the lifetime of the temporary. - SILInstruction *lastLoadInst = getLastUseWhileSourceIsNotModified(copyInst, - loadInsts); + SILInstruction *lastLoadInst = + getLastUseWhileSourceIsNotModified(copyInst, loadInsts, aa); if (!lastLoadInst) - return false; + return; // We cannot insert the destroy of copySrc after lastLoadInst if copySrc is // re-initialized by exactly this instruction. @@ -527,13 +540,13 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (needToInsertDestroy && lastLoadInst != copyInst && !isa(lastLoadInst) && aa->mayWriteToMemory(lastLoadInst, copySrc)) - return false; + return; if (!isOSSA && !checkTempObjectDestroy(tempObj, copyInst)) - return false; + return; - if (!extendAccessScopes(copyInst, lastLoadInst)) - return false; + if (!extendAccessScopes(copyInst, lastLoadInst, aa)) + return; LLVM_DEBUG(llvm::dbgs() << " Success: replace temp" << *tempObj); @@ -556,7 +569,6 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { while (!tempObj->use_empty()) { Operand *use = *tempObj->use_begin(); SILInstruction *user = use->getUser(); - aa->invalidateInstruction(user); switch (user->getKind()) { case SILInstructionKind::DestroyAddrInst: @@ -591,25 +603,25 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { } tempObj->eraseFromParent(); - return true; + invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } -std::pair +SILBasicBlock::iterator TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) { // If our store is an assign, bail. if (si->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) - return {std::next(si->getIterator()), false}; + return std::next(si->getIterator()); auto *tempObj = dyn_cast(si->getDest()); if (!tempObj) { - return {std::next(si->getIterator()), false}; + return std::next(si->getIterator()); } // If our tempObj has a dynamic lifetime (meaning it is conditionally // initialized, conditionally taken, etc), we can not convert its uses to SSA // while eliminating it simply. So bail. if (tempObj->hasDynamicLifetime()) { - return {std::next(si->getIterator()), false}; + return std::next(si->getIterator()); } // Scan all uses of the temporary storage (tempObj) to verify they all refer @@ -632,14 +644,14 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) { break; case SILInstructionKind::CopyAddrInst: if (cast(user)->getDest() == tempObj) - return {std::next(si->getIterator()), false}; + return std::next(si->getIterator()); break; case SILInstructionKind::MarkDependenceInst: if (cast(user)->getValue() == tempObj) - return {std::next(si->getIterator()), false}; + return std::next(si->getIterator()); break; default: - return {std::next(si->getIterator()), false}; + return std::next(si->getIterator()); } } @@ -734,7 +746,8 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) { auto nextIter = std::next(si->getIterator()); si->eraseFromParent(); tempObj->eraseFromParent(); - return {nextIter, true}; + invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); + return nextIter; } //===----------------------------------------------------------------------===// @@ -746,9 +759,6 @@ void TempRValueOptPass::run() { LLVM_DEBUG(llvm::dbgs() << "Copy Peephole in Func " << getFunction()->getName() << "\n"); - aa = getPassManager()->getAnalysis(); - bool changed = false; - // Find all copy_addr instructions. llvm::SmallSetVector deadCopies; for (auto &block : *getFunction()) { @@ -759,13 +769,12 @@ void TempRValueOptPass::run() { if (auto *copyInst = dyn_cast(&*ii)) { // In case of success, this may delete instructions, but not the // CopyInst itself. - changed |= tryOptimizeCopyIntoTemp(copyInst); + tryOptimizeCopyIntoTemp(copyInst); // Remove identity copies which either directly result from successfully // calling tryOptimizeCopyIntoTemp or was created by an earlier // iteration, where another copy_addr copied the temporary back to the // source location. if (copyInst->getSrc() == copyInst->getDest()) { - changed = true; deadCopies.insert(copyInst); } ++ii; @@ -773,9 +782,7 @@ void TempRValueOptPass::run() { } if (auto *si = dyn_cast(&*ii)) { - bool madeSingleChange; - std::tie(ii, madeSingleChange) = tryOptimizeStoreIntoTemp(si); - changed |= madeSingleChange; + ii = tryOptimizeStoreIntoTemp(si); continue; } @@ -794,7 +801,6 @@ void TempRValueOptPass::run() { DeadEndBlocks deBlocks(getFunction()); for (auto *deadCopy : deadCopies) { - assert(changed); auto *srcInst = deadCopy->getSrc()->getDefiningInstruction(); deadCopy->eraseFromParent(); // Simplify any access scope markers that were only used by the dead @@ -803,7 +809,7 @@ void TempRValueOptPass::run() { simplifyAndReplaceAllSimplifiedUsesAndErase(srcInst, callbacks, &deBlocks); } } - if (changed) { + if (!deadCopies.empty()) { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } } diff --git a/lib/SILOptimizer/Transforms/UnsafeGuaranteedPeephole.cpp b/lib/SILOptimizer/Transforms/UnsafeGuaranteedPeephole.cpp index aa2de64bc4c8b..25a3e25af7d3e 100644 --- a/lib/SILOptimizer/Transforms/UnsafeGuaranteedPeephole.cpp +++ b/lib/SILOptimizer/Transforms/UnsafeGuaranteedPeephole.cpp @@ -72,7 +72,7 @@ static void tryRemoveRetainReleasePairsBetween( if (!CurInst->mayHaveSideEffects()) continue; - if (isa(CurInst) || isa(CurInst)) + if (isa(CurInst)) continue; if (isa(CurInst) || isa(CurInst)) @@ -140,8 +140,7 @@ static bool removeGuaranteedRetainReleasePairs(SILFunction &F, (isa(*NextInstIter) || isa(*NextInstIter) || !NextInstIter->mayHaveSideEffects() || - isa(*NextInstIter) || - isa(*NextInstIter))) + isa(*NextInstIter))) ++NextInstIter; if (&*NextInstIter != CurInst) { LLVM_DEBUG(llvm::dbgs() << "Last retain right before match failed\n"); diff --git a/lib/SILOptimizer/UtilityPasses/AADumper.cpp b/lib/SILOptimizer/UtilityPasses/AADumper.cpp index 6da3331d83768..6fd874947401a 100644 --- a/lib/SILOptimizer/UtilityPasses/AADumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/AADumper.cpp @@ -64,7 +64,7 @@ class SILAADumper : public SILModuleTransform { if (!gatherValues(Fn, Values)) continue; - AliasAnalysis *AA = PM->getAnalysis(); + AliasAnalysis *AA = PM->getAnalysis(&Fn); // A cache llvm::DenseMap Results; diff --git a/lib/SILOptimizer/UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp b/lib/SILOptimizer/UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp index c5bba2cd4a39b..9abaf9b53a382 100644 --- a/lib/SILOptimizer/UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp @@ -39,13 +39,14 @@ namespace { class SILEpilogueRetainReleaseMatcherDumper : public SILModuleTransform { void run() override { - auto *AA = PM->getAnalysis(); auto *RCIA = getAnalysis(); for (auto &Fn: *getModule()) { // Function is not definition. if (!Fn.isDefinition()) continue; + auto *AA = PM->getAnalysis(&Fn); + llvm::outs() << "START: sil @" << Fn.getName() << "\n"; // Handle @owned return value. diff --git a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp index 7750ccd5403a9..6bd67c6f88ce9 100644 --- a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp @@ -83,7 +83,7 @@ class MemBehaviorDumper : public SILModuleTransform { if (!gatherValues(Fn, Values)) continue; - AliasAnalysis *AA = PM->getAnalysis(); + AliasAnalysis *AA = PM->getAnalysis(&Fn); unsigned PairCount = 0; for (auto &BB : Fn) { diff --git a/lib/SILOptimizer/UtilityPasses/SILDebugInfoGenerator.cpp b/lib/SILOptimizer/UtilityPasses/SILDebugInfoGenerator.cpp index 37b8e19c73c7e..cb3a4d861903f 100644 --- a/lib/SILOptimizer/UtilityPasses/SILDebugInfoGenerator.cpp +++ b/lib/SILOptimizer/UtilityPasses/SILDebugInfoGenerator.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "gsil-gen" +#define DEBUG_TYPE "sil-based-debuginfo-gen" #include "swift/AST/SILOptions.h" #include "swift/SIL/SILPrintContext.h" #include "swift/SIL/SILModule.h" @@ -25,12 +25,12 @@ namespace { /// A pass for generating debug info on SIL level. /// /// This pass is only enabled if SILOptions::SILOutputFileNameForDebugging is -/// set (i.e. if the -gsil command line option is specified). +/// set (i.e. if the -sil-based-debuginfo frontend option is specified). /// The pass writes all SIL functions into one or multiple output files, /// depending on the size of the SIL. The names of the output files are derived /// from the main output file. /// -/// output file name = .gsil_.sil +/// output file name = .sil_dbg_.sil /// /// Where is a consecutive number. The files are stored in the same /// same directory as the main output file. @@ -104,7 +104,7 @@ class SILDebugInfoGenerator : public SILModuleTransform { std::string FileName; llvm::raw_string_ostream NameOS(FileName); - NameOS << FileBaseName << ".gsil_" << FileIdx++ << ".sil"; + NameOS << FileBaseName << ".sil_dbg_" << FileIdx++ << ".sil"; NameOS.flush(); char *FileNameBuf = (char *)M->allocate(FileName.size() + 1, 1); @@ -144,12 +144,18 @@ class SILDebugInfoGenerator : public SILModuleTransform { for (auto iter = BB.begin(), end = BB.end(); iter != end;) { SILInstruction *I = &*iter; ++iter; - if (isa(I) || isa(I)) { - // debug_value and debug_value_addr are not needed anymore. + if (isa(I)) { + // debug_value instructions are not needed anymore. // Also, keeping them might trigger a verifier error. I->eraseFromParent(); continue; } + if (auto *ASI = dyn_cast(I)) + // Remove the debug variable scope enclosed + // within the SILDebugVariable such that we won't + // trigger a verification error. + ASI->setDebugVarScope(nullptr); + SILLocation Loc = I->getLoc(); auto *filePos = SILLocation::FilenameAndLocation::alloc( Ctx.LineNums[I], 1, FileNameBuf, *M); diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 1eb2ac4f5f93e..e9a41c5041815 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -310,7 +310,6 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::AssignByWrapperInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Store##Name##Inst: #include "swift/AST/ReferenceStorage.def" diff --git a/lib/SILOptimizer/UtilityPasses/StripDebugInfo.cpp b/lib/SILOptimizer/UtilityPasses/StripDebugInfo.cpp index f8d45fda8c30b..b5ee98e2c8713 100644 --- a/lib/SILOptimizer/UtilityPasses/StripDebugInfo.cpp +++ b/lib/SILOptimizer/UtilityPasses/StripDebugInfo.cpp @@ -25,8 +25,7 @@ static void stripFunction(SILFunction *F) { SILInstruction *Inst = &*II; ++II; - if (!isa(Inst) && - !isa(Inst)) + if (!isa(Inst)) continue; Inst->eraseFromParent(); diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index 5f67ddcb3e91b..69963b3e04274 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -244,7 +244,7 @@ void BasicBlockCloner::sinkAddressProjections() { deleter.trackIfDead(&*ii); ii = nextII; } - deleter.cleanUpDeadInstructions(); + deleter.cleanupDeadInstructions(); } // Populate 'projections' with the chain of address projections leading diff --git a/lib/SILOptimizer/Utils/CFGOptUtils.cpp b/lib/SILOptimizer/Utils/CFGOptUtils.cpp index dc844792ba215..28878cdebf15c 100644 --- a/lib/SILOptimizer/Utils/CFGOptUtils.cpp +++ b/lib/SILOptimizer/Utils/CFGOptUtils.cpp @@ -26,7 +26,7 @@ using namespace swift; TermInst *swift::addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest, SILValue val, - InstModCallbacks &callbacks) { + InstructionDeleter &deleter) { SILBuilderWithScope builder(branch); TermInst *newBr = nullptr; @@ -53,7 +53,7 @@ TermInst *swift::addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest, cbi->getLoc(), cbi->getCondition(), cbi->getTrueBB(), trueArgs, cbi->getFalseBB(), falseArgs, cbi->getTrueBBCount(), cbi->getFalseBBCount()); - callbacks.createdNewInst(newBr); + deleter.getCallbacks().createdNewInst(newBr); } else if (auto *bi = dyn_cast(branch)) { SmallVector args; @@ -63,13 +63,13 @@ TermInst *swift::addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest, args.push_back(val); assert(args.size() == dest->getNumArguments()); newBr = builder.createBranch(bi->getLoc(), bi->getDestBB(), args); - callbacks.createdNewInst(newBr); + deleter.getCallbacks().createdNewInst(newBr); } else { // At the moment we can only add arguments to br and cond_br. llvm_unreachable("Can't add argument to terminator"); } - callbacks.deleteInst(branch); + deleter.forceDelete(branch); return newBr; } diff --git a/lib/SILOptimizer/Utils/CMakeLists.txt b/lib/SILOptimizer/Utils/CMakeLists.txt index 31b391cc1d1f5..4fbd0f71ef455 100644 --- a/lib/SILOptimizer/Utils/CMakeLists.txt +++ b/lib/SILOptimizer/Utils/CMakeLists.txt @@ -3,6 +3,7 @@ target_sources(swiftSILOptimizer PRIVATE CFGOptUtils.cpp CanonicalizeInstruction.cpp CanonicalOSSALifetime.cpp + CanonicalizeBorrowScope.cpp CastOptimizer.cpp CheckedCastBrJumpThreading.cpp ConstantFolding.cpp diff --git a/lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp b/lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp index 87443de1b636e..7a1cf0b3292b6 100644 --- a/lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp +++ b/lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2021 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 @@ -29,22 +29,39 @@ /// /// See CanonicalOSSALifetime.h for examples. /// -/// TODO: Enable canonical-ossa-rewrite-borrows to rewrite single-block borrows. -/// Add support for multi-block borrows, load_borrow, and phi by using -/// persistentCopies. +/// TODO: Canonicalization currently bails out if any uses of the def has +/// OperandOwnership::PointerEscape. Once project_box is protected by a borrow +/// scope and mark_dependence is associated with an end_dependence, those will +/// no longer be represented as PointerEscapes, and canonicalization will +/// naturally work everywhere as intended. The intention is to keep the +/// canonicalization algorithm as simple and robust, leaving the remaining +/// performance opportunities contingent on fixing the SIL representation. /// -/// TODO: If all client passes can maintain block numbers, then the -/// SmallDenseMaps/SetVectors can be replaced with bitsets/sparsesets. +/// TODO: Replace BasicBlock SmallDenseMaps/SetVectors with inlined bits; +/// see BasicBlockDataStructures.h. +/// +/// TODO: This algorithm would be extraordinarily simple and cheap except for +/// the following issues: +/// +/// 1. Liveness is extended by any overlapping begin/end_access scopes. This +/// avoids calling a destructor within an exclusive access. A simpler +/// alternative would be to model all end_access instructions as deinit +/// barriers, but that may significantly limit optimization. +/// +/// 2. A poison mode supports debugging by setting the poisonRefs flag on any +/// destroys that have been hoisted. This forces the shadow variables to be +/// overwritten with a sentinel, preventing the debugger from seeing +/// deinitialized objects. /// //===----------------------------------------------------------------------===// #define DEBUG_TYPE "copy-propagation" #include "swift/SILOptimizer/Utils/CanonicalOSSALifetime.h" -#include "swift/SIL/DebugUtils.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/OwnershipUtils.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" +#include "swift/SILOptimizer/Utils/DebugOptUtils.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/ValueLifetime.h" #include "llvm/ADT/Statistic.h" @@ -52,471 +69,39 @@ using namespace swift; using llvm::SmallSetVector; -STATISTIC(NumCopiesEliminated, "number of copy_value instructions removed"); +llvm::Statistic swift::NumCopiesEliminated = { + DEBUG_TYPE, "NumCopiesEliminated", + "number of copy_value instructions removed"}; + +llvm::Statistic swift::NumCopiesGenerated = { + DEBUG_TYPE, "NumCopiesGenerated", + "number of copy_value instructions created"}; + STATISTIC(NumDestroysEliminated, "number of destroy_value instructions removed"); -STATISTIC(NumCopiesGenerated, "number of copy_value instructions created"); STATISTIC(NumDestroysGenerated, "number of destroy_value instructions created"); STATISTIC(NumUnknownUsers, "number of functions with unknown users"); -/// This use-def walk must be consistent with the def-use walks performed -/// within the canonicalizeValueLifetime() implementation. -SILValue CanonicalizeOSSALifetime::getCanonicalCopiedDef(SILValue v) { - while (auto *copy = dyn_cast(v)) { - auto def = copy->getOperand(); - if (def.getOwnershipKind() == OwnershipKind::Owned) { - v = def; - continue; - } - if (auto borrowedVal = BorrowedValue(def)) { - // Any def's that aren't filtered out here must be handled by - // computeBorrowLiveness. - switch (borrowedVal.kind) { - case BorrowedValueKind::Invalid: - llvm_unreachable("Using invalid case?!"); - case BorrowedValueKind::SILFunctionArgument: - return def; - case BorrowedValueKind::BeginBorrow: { - return def; - } - case BorrowedValueKind::LoadBorrow: - case BorrowedValueKind::Phi: - break; - } - } - // This guaranteed value cannot be handled, treat the copy as an owned - // live range def instead. - return copy; - } - return v; -} +//===----------------------------------------------------------------------===// +// MARK: General utilities +//===----------------------------------------------------------------------===// /// The lifetime extends beyond given consuming use. Copy the value. /// /// This can set the operand value, but cannot invalidate the use iterator. -static void copyLiveUse(Operand *use, InstModCallbacks &instModCallbacks) { +void swift::copyLiveUse(Operand *use, InstModCallbacks &instModCallbacks) { SILInstruction *user = use->getUser(); SILBuilderWithScope builder(user->getIterator()); auto loc = RegularLocation::getAutoGeneratedLocation(user->getLoc()); auto *copy = builder.createCopyValue(loc, use->get()); instModCallbacks.createdNewInst(copy); - instModCallbacks.setUseValue(use, copy); + use->set(copy); ++NumCopiesGenerated; LLVM_DEBUG(llvm::dbgs() << " Copying at last use " << *copy); } -// TODO: generalize this to handle multiple nondebug uses of the struct_extract. -SILValue swift::convertExtractToDestructure(StructExtractInst *extract, - InstModCallbacks *callbacks) { - if (!hasOneNonDebugUse(extract)) - return nullptr; - - if (!extract->isFieldOnlyNonTrivialField()) - return nullptr; - - auto *extractCopy = - dyn_cast(getNonDebugUses(extract).begin()->getUser()); - if (!extractCopy) - return nullptr; - - SILBuilderWithScope builder(extract); - auto loc = extract->getLoc(); - auto *copy = builder.createCopyValue(loc, extract->getOperand()); - auto *destructure = builder.createDestructureStruct(loc, copy); - if (callbacks) { - callbacks->createdNewInst(copy); - callbacks->createdNewInst(destructure); - } - - SILValue nonTrivialResult = destructure->getResult(extract->getFieldIndex()); - assert(!nonTrivialResult->getType().isTrivial(*destructure->getFunction()) - && "field idx mismatch"); - - if (callbacks) { - callbacks->replaceValueUsesWith(extractCopy, nonTrivialResult); - } else { - extractCopy->replaceAllUsesWith(nonTrivialResult); - } - return nonTrivialResult; -} - -//===----------------------------------------------------------------------===// -// MARK: Rewrite borrow scopes -//===----------------------------------------------------------------------===// - -bool CanonicalizeOSSALifetime::computeBorrowLiveness() { - auto borrowedVal = BorrowedValue(currentDef); - if (!borrowedVal) { - return false; - } - switch (borrowedVal.kind) { - case BorrowedValueKind::Invalid: - llvm_unreachable("Used invalid"); - case BorrowedValueKind::SILFunctionArgument: - // For efficiency, function arguments skip liveness. - return true; - case BorrowedValueKind::LoadBorrow: - case BorrowedValueKind::Phi: - // TODO: Canonicalize load_borrow scope and phi once consolidateBorrowScope - // can handle persistentCopies. - return false; - case BorrowedValueKind::BeginBorrow: - break; - } - if (!canonicalizeBorrowMode) - return false; - - // Note that there is no need to look through any reborrows. The reborrowed - // value is considered a separate lifetime for canonicalization. Any copies of - // the reborrowed value will not be rewritten when canonicalizing the current - // borrow scope because they are "hidden" behind the reborrow. - borrowedVal.visitLocalScopeEndingUses([this](Operand *use) { - liveness.updateForUse(use->getUser(), /*lifetimeEnding*/ true); - return true; - }); - return true; -} - -// Create a copy for outer uses of the borrow scope introduced by -// currentDef. This copy should only be used by outer uses in the same block -// as the borrow scope. -// -// To use an existing outer copy, we could find its earliest consume. But the -// new copy will immediately canonicalized and a canonical begin_borrow scope -// have no outer uses of its first block. -static CopyValueInst *createOuterCopy(BeginBorrowInst *beginBorrow, - InstModCallbacks &instModCallbacks) { - SILBuilderWithScope builder(beginBorrow); - - auto loc = RegularLocation::getAutoGeneratedLocation(beginBorrow->getLoc()); - auto *copy = builder.createCopyValue(loc, beginBorrow->getOperand()); - instModCallbacks.createdNewInst(copy); - - ++NumCopiesGenerated; - LLVM_DEBUG(llvm::dbgs() << " Outer copy " << *copy); - - return copy; -} - -// This def-use traversal is similar to findExtendedTransitiveGuaranteedUses(), -// however, to cover the canonical lifetime, it looks through copies. It also -// considers uses within the introduced borrow scope itself (instead of simply -// visiting the scope-ending uses). It does not, however, look into nested -// borrow scopes uses, since nested scopes are canonicalized independently. -// -// \p useInsts are the potentially outer use instructions. This set will -// be pared down to only the outer uses in the next step. -bool CanonicalizeOSSALifetime::findBorrowScopeUses( - llvm::SmallPtrSetImpl &useInsts) { - auto isUserInLiveOutBlock = [&](SILInstruction *user) { - return (liveness.getBlockLiveness(user->getParent()) - == PrunedLiveBlocks::LiveOut); - return false; - }; - auto recordOuterUse = [&](Operand *use) { - // If the user's block is LiveOut, then it is definitely within the borrow - // scope, so there's no need to record it. - if (isUserInLiveOutBlock(use->getUser())) { - return; - } - useInsts.insert(use->getUser()); - }; - defUseWorklist.initialize(currentDef); - // Avoid revisiting uses because we recurse through - // struct/destructure. Otherwise the order does not matter. - while (SILValue value = defUseWorklist.pop()) { - for (Operand *use : value->getUses()) { - auto *user = use->getUser(); - // Recurse through copies. - if (auto *copy = dyn_cast(user)) { - defUseWorklist.insert(copy); - continue; - } - // Note: debug_value uses are handled like normal uses here. They should - // be stripped later if required when handling outerCopy or - // persistentCopies. - - switch (use->getOperandOwnership()) { - case OperandOwnership::NonUse: - break; - case OperandOwnership::TrivialUse: - llvm_unreachable("this operand cannot handle ownership"); - - case OperandOwnership::InteriorPointer: - case OperandOwnership::ForwardingBorrow: - case OperandOwnership::EndBorrow: - case OperandOwnership::Reborrow: - // Ignore uses that must be within the borrow scope. - // Rewriting does not look through reborrowed values--it considers them - // part of a separate lifetime. - break; - - case OperandOwnership::ForwardingUnowned: - case OperandOwnership::PointerEscape: - // Pointer escapes are only allowed if they use the guaranteed value, - // which means that the escaped value must be confied to the current - // borrow scope. - if (use->get().getOwnershipKind() != OwnershipKind::Guaranteed) - return false; - break; - - case OperandOwnership::ForwardingConsume: - // Recurse through destructure, but also record them as an outer - // use. Note that they will consider to be outer uses even if they are - // within this scope as long as any of their transitively uses our - // outside the scope. - // - // FIXME: handle all ForwardingOperands. - recordOuterUse(use); - for (auto result : user->getResults()) { - if (result.getOwnershipKind() == OwnershipKind::Owned) { - defUseWorklist.insert(result); - } - } - break; - - case OperandOwnership::InstantaneousUse: - case OperandOwnership::UnownedInstantaneousUse: - case OperandOwnership::BitwiseEscape: - case OperandOwnership::DestroyingConsume: - recordOuterUse(use); - break; - case OperandOwnership::Borrow: - BorrowingOperand borrowOper(use); - assert(borrowOper && "BorrowingOperand must handle OperandOwnership"); - recordOuterUse(use); - // For borrows, record the scope-ending instructions in addition to the - // borrow instruction outer use points. The logic below to check whether - // a borrow scope is an outer use must visit the same set of uses. - borrowOper.visitExtendedScopeEndingUses([&](Operand *endBorrow) { - if (!isUserInLiveOutBlock(endBorrow->getUser())) { - useInsts.insert(endBorrow->getUser()); - } - return true; - }); - break; - } - } // end switch OperandOwnership - } // end def-use traversal - - return true; -} - -void CanonicalizeOSSALifetime::filterOuterBorrowUseInsts( - llvm::SmallPtrSetImpl &outerUseInsts) { - auto *beginBorrow = cast(currentDef); - SmallVector scopeEndingInsts; - BorrowedValue(beginBorrow).getLocalScopeEndingInstructions(scopeEndingInsts); - blockWorklist.clear(); - // Remove outer uses that occur before the end of the borrow scope by - // reverse iterating from the end_borrow. - auto scanBlock = [&](SILBasicBlock *bb, SILBasicBlock::iterator endIter) { - auto beginIter = bb->begin(); - if (bb == beginBorrow->getParent()) { - beginIter = std::next(beginBorrow->getIterator()); - } else { - blockWorklist.insert(bb); - } - for (auto instIter = endIter; instIter != beginIter;) { - --instIter; - outerUseInsts.erase(&*instIter); - } - }; - for (auto *scopeEnd : scopeEndingInsts) { - scanBlock(scopeEnd->getParent(), std::next(scopeEnd->getIterator())); - } - // This worklist is also a visited set, so we never pop the entries. - while (auto *bb = blockWorklist.pop()) { - for (auto *predBB : bb->getPredecessorBlocks()) { - scanBlock(predBB, predBB->end()); - } - } -} - -// Repeat the same def-use traversal as findBorrowScopeUses(). This time, -// instead of recording all the uses, rewrite the operands of outer uses, -// record consumingUses, and add forwarding operations to the outerUseInsts if -// they have transitive outer uses. -// -// Recurse through forwarded consumes, but don't revisit uses. Once an outer -// use it visited, it marks its incoming operand as an outer use. -// -// Return true if any outer uses were found and rewritten. -void CanonicalizeOSSALifetime::rewriteOuterBorrowUsesAndFindConsumes( - SILValue incomingValue, - llvm::SmallPtrSetImpl &outerUseInsts) { - - SILValue newIncomingValue = - (incomingValue == currentDef) ? outerCopy : incomingValue; - - // Outer uses specific to the current incomingValue - SmallVector currentOuterUseInsts; - SmallVector consumingUses; - SmallPtrSet unclaimedConsumingUsers; - - auto rewriteOuterUse = [&](Operand *use) { - LLVM_DEBUG(llvm::dbgs() << " Use of outer copy " << *use->getUser()); - instModCallbacks.setUseValue(use, newIncomingValue); - currentOuterUseInsts.push_back(use->getUser()); - outerUseInsts.insert(incomingValue->getDefiningInstruction()); - if (use->isLifetimeEnding()) { - consumingUses.push_back(use); - unclaimedConsumingUsers.insert(use->getUser()); - } - }; - // defUseWorklist is used recursively here - unsigned defUseStart = defUseWorklist.size(); - defUseWorklist.insert(incomingValue); - while (defUseStart < defUseWorklist.size()) { - SILValue value = defUseWorklist.pop(); - // Gather the uses before updating any of them. - SmallVector uses(value->getUses()); - for (Operand *use : uses) { - auto *user = use->getUser(); - // Transitively poke through copies. - if (auto *copy = dyn_cast(user)) { - defUseWorklist.insert(copy); - continue; - } - // Note: debug_value uses are handled like normal uses here. They should - // be stripped later if required when handling outerCopy or - // persistentCopies. - - switch (use->getOperandOwnership()) { - case OperandOwnership::NonUse: - case OperandOwnership::TrivialUse: - case OperandOwnership::InteriorPointer: - case OperandOwnership::ForwardingBorrow: - case OperandOwnership::EndBorrow: - case OperandOwnership::Reborrow: - case OperandOwnership::ForwardingUnowned: - case OperandOwnership::PointerEscape: - break; - - case OperandOwnership::ForwardingConsume: - // FIXME: Need unit tests for various forwarding operations. - // - // Handles: - // Enum, SelectValue, InitExistential, MarkDependence - // Struct, Tuple - // DestructureStruct, DestructureTuple - // Various reference casts, - // SelectEnum, SwitchEnum, CheckCastBranch - if (OwnershipForwardingMixin::isa(user->getKind())) { - for (auto result : user->getResults()) { - if (result.getOwnershipKind() != OwnershipKind::Owned) - continue; - - // Process transitive users and add this destructure to - // outerUseInsts if any outer uses were found. - rewriteOuterBorrowUsesAndFindConsumes(result, outerUseInsts); - if (outerUseInsts.count(user)) { - rewriteOuterUse(use); - } - } - continue; - } - // Calls and unrecognized forwarding consumes are end points. - LLVM_FALLTHROUGH; - - case OperandOwnership::InstantaneousUse: - case OperandOwnership::UnownedInstantaneousUse: - case OperandOwnership::BitwiseEscape: - case OperandOwnership::DestroyingConsume: - if (outerUseInsts.count(use->getUser())) { - rewriteOuterUse(use); - } - break; - case OperandOwnership::Borrow: - BorrowingOperand borrowOper(use); - assert(borrowOper && "BorrowingOperand must handle OperandOwnership"); - - // For borrows, record the scope-ending instructions in addition to the - // borrow instruction outer use points. - if (outerUseInsts.count(use->getUser()) - || !borrowOper.visitExtendedScopeEndingUses([&](Operand *endScope) { - return !outerUseInsts.count(endScope->getUser()); - })) { - rewriteOuterUse(use); - } - break; - } - } // end switch OperandOwnership - } // end def-use traversal - - // Insert a destroy on the outer copy's or forwarding consume's lifetime - // frontier, or claim an existing consume. - ValueLifetimeAnalysis lifetimeAnalysis( - newIncomingValue.getDefiningInstruction(), currentOuterUseInsts); - ValueLifetimeBoundary boundary; - lifetimeAnalysis.computeLifetimeBoundary(boundary); - - for (auto *boundaryEdge : boundary.boundaryEdges) { - if (DeadEndBlocks::triviallyEndsInUnreachable(boundaryEdge)) - continue; - auto insertPt = boundaryEdge->begin(); - auto *dvi = SILBuilderWithScope(insertPt).createDestroyValue( - insertPt->getLoc(), newIncomingValue); - instModCallbacks.createdNewInst(dvi); - } - - for (SILInstruction *lastUser : boundary.lastUsers) { - if (unclaimedConsumingUsers.erase(lastUser)) - continue; - - SILBuilderWithScope::insertAfter(lastUser, [&](SILBuilder &b) { - auto *dvi = b.createDestroyValue(lastUser->getLoc(), newIncomingValue); - instModCallbacks.createdNewInst(dvi); - }); - } - // Add copies for consuming users of newIncomingValue. - for (auto *use : consumingUses) { - // If the user is still in the unclaimedConsumingUsers set, then it does not - // end the outer copy's lifetime and therefore requires a copy. Only one - // operand can be claimed as ending the lifetime, so return its user to the - // unclaimedConsumingUsers set after skipping the first copy. - auto iterAndInserted = unclaimedConsumingUsers.insert(use->getUser()); - if (!iterAndInserted.second) { - copyLiveUse(use, instModCallbacks); - } - } -} - -// If this succeeds, then all uses of the borrowed value outside the borrow -// scope will be rewritten to use an outer copy, and all remaining uses of the -// borrowed value will be confined to the borrow scope. -// -// TODO: Canonicalize multi-block borrow scopes, load_borrow scope, and phi -// borrow scopes by adding one copy per block to persistentCopies for -// each block that dominates an outer use. -bool CanonicalizeOSSALifetime::consolidateBorrowScope() { - if (isa(currentDef)) { - return true; - } - // getCanonicalCopiedDef ensures that if currentDef is a guaranteed value, - // then it is a borrow scope introducer. - assert(BorrowedValue(currentDef).isLocalScope()); - - // Gather all potential outer uses before rewriting any to avoid scanning any - // basic block more than once. - llvm::SmallPtrSet outerUseInsts; - if (!findBorrowScopeUses(outerUseInsts)) - return false; - - filterOuterBorrowUseInsts(outerUseInsts); - if (outerUseInsts.empty()) { - return true; - } - this->outerCopy = - createOuterCopy(cast(currentDef), instModCallbacks); - - defUseWorklist.clear(); - rewriteOuterBorrowUsesAndFindConsumes(currentDef, outerUseInsts); - return true; -} - //===----------------------------------------------------------------------===// // MARK: Step 1. Compute pruned liveness //===----------------------------------------------------------------------===// @@ -662,10 +247,10 @@ endsAccessOverlappingPrunedBoundary(SILInstruction *inst) { // that does not pass through endAccess. endAccess is dominated by // both currentDef and begin_access. Therefore, such a path only exists if // beginAccess dominates currentDef. - DominanceInfo *domInfo = dominanceAnalysis->get(inst->getFunction()); - return domInfo->properlyDominates(beginAccess->getParent(), + return domTree->properlyDominates(beginAccess->getParent(), getCurrentDef()->getParentBlock()); } + llvm_unreachable("covered switch"); } // Find all overlapping access scopes and extend pruned liveness to cover them: @@ -775,7 +360,7 @@ void CanonicalizeOSSALifetime::insertDestroyOnCFGEdge( SILBuilderWithScope builder(pos); auto loc = RegularLocation::getAutoGeneratedLocation(pos->getLoc()); auto *di = builder.createDestroyValue(loc, currentDef, needsPoison); - instModCallbacks.createdNewInst(di); + getCallbacks().createdNewInst(di); consumes.recordFinalConsume(di); @@ -847,15 +432,13 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) { if (isa(inst)) { for (auto &succ : bb->getSuccessors()) { insertDestroyOnCFGEdge(bb, succ, poisonRefsMode); - setChanged(); } } else { if (existingDestroy) { needsPoison = existingDestroyNeedsPoison; } insertDestroyAtInst(std::next(instIter), existingDestroy, currentDef, - needsPoison, consumes, instModCallbacks); - setChanged(); + needsPoison, consumes, getCallbacks()); } return; case PrunedLiveness::LifetimeEndingUse: @@ -906,8 +489,7 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) { needsPoison = existingDestroyNeedsPoison; } insertDestroyAtInst(instIter, existingDestroy, currentDef, needsPoison, - consumes, instModCallbacks); - setChanged(); + consumes, getCallbacks()); return; } --instIter; @@ -918,8 +500,7 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) { needsPoison = existingDestroyNeedsPoison; } insertDestroyAtInst(std::next(instIter), existingDestroy, currentDef, - needsPoison, consumes, instModCallbacks); - setChanged(); + needsPoison, consumes, getCallbacks()); return; } } @@ -957,7 +538,6 @@ void CanonicalizeOSSALifetime::findOrInsertDestroys() { for (auto *predBB : bb->getPredecessorBlocks()) { if (liveness.getBlockLiveness(predBB) == PrunedLiveBlocks::LiveOut) { insertDestroyOnCFGEdge(predBB, bb, poisonRefsMode); - setChanged(); } else { if (poisonRefsMode) { remnantLiveOutBlocks.insert(predBB); @@ -978,9 +558,7 @@ void CanonicalizeOSSALifetime::findOrInsertDestroys() { /// copies and destroys for deletion. Insert new copies for interior uses that /// require ownership of the used operand. void CanonicalizeOSSALifetime::rewriteCopies() { - bool isOwned = currentDef.getOwnershipKind() == OwnershipKind::Owned; - assert((!isOwned || !consumes.hasPersistentCopies()) - && "persistent copies use borrowed values"); + assert(currentDef.getOwnershipKind() == OwnershipKind::Owned); SmallSetVector instsToDelete; defUseWorklist.clear(); @@ -993,10 +571,8 @@ void CanonicalizeOSSALifetime::rewriteCopies() { auto *user = use->getUser(); // Recurse through copies. if (auto *copy = dyn_cast(user)) { - if (!consumes.isPersistentCopy(copy)) { - defUseWorklist.insert(copy); - return true; - } + defUseWorklist.insert(copy); + return true; } if (auto *destroy = dyn_cast(user)) { // If this destroy was marked as a final destroy, ignore it; otherwise, @@ -1044,14 +620,8 @@ void CanonicalizeOSSALifetime::rewriteCopies() { for (auto useIter = currentDef->use_begin(), endIter = currentDef->use_end(); useIter != endIter;) { Operand *use = *useIter++; - // A direct lifetime-ending use of a guaranteed value (EndBorrow or - // Reborrow), never needs a copy. - if (!isOwned && use->isLifetimeEnding()) { - continue; - } if (!visitUse(use)) { - copyLiveUse(use, instModCallbacks); - setChanged(); + copyLiveUse(use, getCallbacks()); } } while (SILValue value = defUseWorklist.pop()) { @@ -1064,16 +634,14 @@ void CanonicalizeOSSALifetime::rewriteCopies() { if (!reusedCopyOp && srcCopy->getParent() == use->getParentBlock()) { reusedCopyOp = use; } else { - copyLiveUse(use, instModCallbacks); - setChanged(); + copyLiveUse(use, getCallbacks()); } } } if (!(reusedCopyOp && srcCopy->hasOneUse())) { - setChanged(); - instModCallbacks.replaceValueUsesWith(srcCopy, srcCopy->getOperand()); + getCallbacks().replaceValueUsesWith(srcCopy, srcCopy->getOperand()); if (reusedCopyOp) { - instModCallbacks.setUseValue(reusedCopyOp, srcCopy); + reusedCopyOp->set(srcCopy); } else { if (instsToDelete.insert(srcCopy)) { LLVM_DEBUG(llvm::dbgs() << " Removing " << *srcCopy); @@ -1098,15 +666,12 @@ void CanonicalizeOSSALifetime::rewriteCopies() { // Remove any dead, non-recovered debug_values. for (auto *dvi : consumes.getDebugInstsAfterConsume()) { LLVM_DEBUG(llvm::dbgs() << " Removing debug_value: " << *dvi); - instModCallbacks.deleteInst(dvi); + deleter.forceDelete(dvi); } // Remove the leftover copy_value and destroy_value instructions. - if (!instsToDelete.empty()) { - recursivelyDeleteTriviallyDeadInstructions(instsToDelete.takeVector(), - /*force=*/true, - instModCallbacks); - setChanged(); + for (unsigned idx = 0, eidx = instsToDelete.size(); idx != eidx; ++idx) { + deleter.forceDelete(instsToDelete[idx]); } } @@ -1118,7 +683,7 @@ void CanonicalizeOSSALifetime::injectPoison() { builder.getInsertionPoint()->getLoc()); auto *di = builder.createDestroyValue(loc, currentDef, /*needsPoison*/ true); - instModCallbacks.createdNewInst(di); + getCallbacks().createdNewInst(di); ++NumDestroysGenerated; LLVM_DEBUG(llvm::dbgs() << " Destroy at last use " << *di); }; @@ -1147,7 +712,7 @@ void CanonicalizeOSSALifetime::injectPoison() { /// shorten lifetimes to mimic -O behavior, not to remove copies. /// /// IRGen must also have support for injecting poison into values of this type. -bool shouldCanonicalizeWithPoison(SILValue def) { +static bool shouldCanonicalizeWithPoison(SILValue def) { assert(def->getType().isObject() && "only SIL values are canonicalized"); auto objectTy = def->getType().unwrapOptionalType(); @@ -1158,56 +723,45 @@ bool shouldCanonicalizeWithPoison(SILValue def) { return objectTy.isAnyClassReferenceType(); } +/// Canonicalize a single extended owned lifetime. bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) { - LLVM_DEBUG(llvm::dbgs() << " Canonicalizing: " << def); - - switch (def.getOwnershipKind()) { - case OwnershipKind::None: - case OwnershipKind::Unowned: - case OwnershipKind::Any: + if (def.getOwnershipKind() != OwnershipKind::Owned) return false; - case OwnershipKind::Guaranteed: - // The goal of poisonRefsMode is only to hoist destroys, not remove copies. - if (poisonRefsMode) - return false; - initDef(def); - if (!computeBorrowLiveness()) { - clearLiveness(); - return false; - } - // Set outerCopy and persistentCopies and rewrite uses - // outside the scope. - if (!consolidateBorrowScope()) { - clearLiveness(); - return false; - } - // Invalidate book-keeping before deleting instructions. - clearLiveness(); - // Rewrite copies and delete extra destroys within the scope. - assert(!consumes.hasFinalConsumes()); - rewriteCopies(); - return true; - case OwnershipKind::Owned: - if (poisonRefsMode && !shouldCanonicalizeWithPoison(def)) - return false; + if (poisonRefsMode && !shouldCanonicalizeWithPoison(def)) + return false; - initDef(def); - // Step 1: compute liveness - if (!computeCanonicalLiveness()) { - clearLiveness(); - return false; - } - extendLivenessThroughOverlappingAccess(); - // Step 2: record final destroys - findOrInsertDestroys(); - // Step 3: rewrite copies and delete extra destroys - rewriteCopies(); + LLVM_DEBUG(llvm::dbgs() << " Canonicalizing: " << def); + // Note: There is no need to register callbacks with this utility. 'onDelete' + // is the only one in use to handle dangling pointers, which could be done + // instead be registering a temporary handler with the pass. Canonicalization + // is only allowed to create and delete instructions that are associated with + // this canonical def (copies and destroys). Each canonical def has a disjoint + // extended lifetime. Any pass calling this utility should work at the level + // canonical defs, not individual instructions. + // + // NotifyWillBeDeleted will not work because copy rewriting removes operands + // before deleting instructions. Also prohibit setUse callbacks just because + // that would simply be insane. + assert(!getCallbacks().notifyWillBeDeletedFunc + && !getCallbacks().setUseValueFunc && "unsupported"); + + initDef(def); + // Step 1: compute liveness + if (!computeCanonicalLiveness()) { clearLiveness(); - consumes.clear(); - return true; + return false; } + extendLivenessThroughOverlappingAccess(); + // Step 2: record final destroys + findOrInsertDestroys(); + // Step 3: rewrite copies and delete extra destroys + rewriteCopies(); + + clearLiveness(); + consumes.clear(); + return true; } //===----------------------------------------------------------------------===// @@ -1221,64 +775,3 @@ SWIFT_ASSERT_ONLY_DECL( llvm::dbgs() << " " << *blockAndInst.getSecond(); } }) - -//===----------------------------------------------------------------------===// -// MARK: Canonicalize Lifetimes Utility -//===----------------------------------------------------------------------===// - -SILAnalysis::InvalidationKind -swift::canonicalizeOSSALifetimes(CanonicalizeOSSALifetime &canonicalizer, - ArrayRef copiedDefs) { - auto isCopyDead = [](CopyValueInst *copy, bool pruneDebug) -> bool { - for (Operand *use : copy->getUses()) { - auto *user = use->getUser(); - if (isa(user)) { - continue; - } - if (pruneDebug && isa(user)) { - continue; - } - return false; - } - return true; - }; - - // Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the - // copy's source operand is unrecgonized), then the copy is itself treated - // like a def and may be dead after canonicalization. - SmallVector deadCopies; - for (auto def : copiedDefs) { - // Canonicalized this def. If we did not perform any canonicalization, just - // continue. - if (!canonicalizer.canonicalizeValueLifetime(def)) - continue; - - // Otherwise, see if we have a dead copy. - if (auto *copy = dyn_cast(def)) { - if (isCopyDead(copy, false /*prune debug*/)) { - deadCopies.push_back(copy); - } - } - - // Canonicalize any new outer copy. - if (SILValue outerCopy = canonicalizer.createdOuterCopy()) { - SILValue outerDef = canonicalizer.getCanonicalCopiedDef(outerCopy); - canonicalizer.canonicalizeValueLifetime(outerDef); - } - - // TODO: also canonicalize any lifetime.persistentCopies like separate owned - // live ranges. - } - - if (!canonicalizer.hasChanged() && deadCopies.empty()) { - return SILAnalysis::InvalidationKind::Nothing; - } - - // We use our canonicalizers inst mod callbacks. - InstructionDeleter deleter(canonicalizer.getInstModCallbacks()); - for (auto *copy : deadCopies) { - deleter.recursivelyDeleteUsersIfDead(copy); - } - - return SILAnalysis::InvalidationKind::Instructions; -} diff --git a/lib/SILOptimizer/Utils/CanonicalizeBorrowScope.cpp b/lib/SILOptimizer/Utils/CanonicalizeBorrowScope.cpp new file mode 100644 index 0000000000000..fcf9b2f04edad --- /dev/null +++ b/lib/SILOptimizer/Utils/CanonicalizeBorrowScope.cpp @@ -0,0 +1,818 @@ +//===--- CanonicalizeBorrowScope.cpp - Canonicalize OSSA borrow scopes ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +/// +/// TODO: Enable -canonical-ossa-rewrite-borrows by default (see +/// CopyPropagation). +/// +/// TODO: Add support for load_borrows, and reborrows by using persistentCopies. +/// +/// TODO: Hoist struct/tuple with multiple operands out of borrow scopes if that +/// ever happens. Consider modeling them as reborrows instead. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "copy-propagation" + +#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h" +#include "swift/Basic/Defer.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" +#include "swift/SILOptimizer/Utils/CanonicalOSSALifetime.h" +#include "swift/SILOptimizer/Utils/DebugOptUtils.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" +#include "llvm/ADT/Statistic.h" + +using namespace swift; + +STATISTIC(NumOuterCopies, "number of copy_value instructions added for a " + "borrow scope"); + +//===----------------------------------------------------------------------===// +// MARK: Local utilities +//===----------------------------------------------------------------------===// + +static bool hasValueOwnership(SILValue value) { + return value.getOwnershipKind() == OwnershipKind::Guaranteed + || value.getOwnershipKind() == OwnershipKind::Owned; +} + +/// Delete a chain of unused copies leading to \p v. +static void deleteCopyChain(SILValue v, InstructionDeleter &deleter) { + while (auto *copy = dyn_cast(v)) { + if (!onlyHaveDebugUses(copy)) + break; + + v = copy->getOperand(); + LLVM_DEBUG(llvm::dbgs() << " Deleting " << *copy); + ++NumCopiesEliminated; + deleter.forceDelete(copy); + } +} + +//===----------------------------------------------------------------------===// +// MARK: Rewrite borrow scopes +//===----------------------------------------------------------------------===// + +/// Does this instruction forward ownership, and can it be hoisted out of a +/// borrow scope or sunk to its uses? +/// +/// Currently requires the first operand to forward ownership. +/// +/// Handles: +/// OwnershipForwardingConversionInst (all kinds of ref casts) +/// OwnershipForwardingMultipleValueInstruction +/// (DestructureStruct, DestructureTuple) +/// AllArgOwnershipForwardingSingleValueInst +/// (Struct, Tuple) +/// FirstArgOwnershipForwardingSingleValueInst +/// (Object, Enum, UncheckedEnumData, SelectValue, Open/InitExistentialRef, +/// MarkDependence) +/// +/// TODO: +/// Enum, SelectValue, InitExistential, MarkDependence +/// Struct, Tuple +/// SelectEnum, SwitchEnum, CheckCastBranch +bool CanonicalizeBorrowScope::isRewritableOSSAForward(SILInstruction *inst) { + if (!inst->isTriviallyDuplicatable()) + return false; + + // TODO: check other operands for dominance and even attempt to hoist them. + if (inst->getNumOperands() != 1) + return false; + + if (isa(inst) + || isa(inst) + || isa(inst) + || isa(inst)) { + Operand *forwardedOper = &inst->getOperandRef(0); + // Trivial conversions do not need to be hoisted out of a borrow scope. + auto operOwnership = forwardedOper->getOperandOwnership(); + if (operOwnership == OperandOwnership::TrivialUse) + return false; + // Don't mess with unowned conversions. They need to be copied immeidately. + if (operOwnership != OperandOwnership::ForwardingBorrow + && operOwnership != OperandOwnership::ForwardingConsume) { + return false; + } + assert(operOwnership == OperandOwnership::ForwardingBorrow + || operOwnership == OperandOwnership::ForwardingConsume); + + // Filter instructions that belong to a Forwarding*ValueInst mixin but + // cannot be converted to forward owned value (struct_extract). + if (!canOpcodeForwardOwnedValues(forwardedOper)) + return false; + + return true; + } + return false; +} + +/// Return the root of a borrowed extended lifetime for \p def or invalid. +/// +/// \p def may be any guaranteed value. +SILValue CanonicalizeBorrowScope::getCanonicalBorrowedDef(SILValue def) { + while (true) { + if (def->getOwnershipKind() != OwnershipKind::Guaranteed) + break; + + if (auto borrowedVal = BorrowedValue(def)) { + // Any def's that aren't filtered out here must be handled by + // computeBorrowLiveness. + switch (borrowedVal.kind) { + case BorrowedValueKind::Invalid: + llvm_unreachable("Using invalid case?!"); + case BorrowedValueKind::SILFunctionArgument: + case BorrowedValueKind::BeginBorrow: + return def; + + case BorrowedValueKind::LoadBorrow: + case BorrowedValueKind::Phi: + break; + } + } + // Look through hoistable guaranteed forwarding instructions on the + // use-def chain. + SILInstruction *defInst = def->getDefiningInstruction(); + if (!defInst) + break; + + if (!CanonicalizeBorrowScope::isRewritableOSSAForward(defInst)) + break; + + def = defInst->getOperand(0); + } + return SILValue(); +} + +bool CanonicalizeBorrowScope::computeBorrowLiveness() { + switch (borrowedValue.kind) { + case BorrowedValueKind::Invalid: + llvm_unreachable("Used invalid"); + case BorrowedValueKind::SILFunctionArgument: + // For efficiency, function arguments skip liveness. + return true; + case BorrowedValueKind::LoadBorrow: + case BorrowedValueKind::Phi: + // TODO: Canonicalize load_borrow scope and phi once consolidateBorrowScope + // can handle persistentCopies. + return false; + case BorrowedValueKind::BeginBorrow: + break; + } + // Note that there is no need to look through any reborrows. The reborrowed + // value is considered a separate lifetime for canonicalization. Any copies of + // the reborrowed value will not be rewritten when canonicalizing the current + // borrow scope because they are "hidden" behind the reborrow. + borrowedValue.visitLocalScopeEndingUses([this](Operand *use) { + liveness.updateForUse(use->getUser(), /*lifetimeEnding*/ true); + return true; + }); + return true; +} + +/// Return the source of a use within a borrow scope. This is the use-def +/// equivalent to the logic in visitBorrowScopeUses that recurses through +/// copies. The use-def and def-use logic must be consistent. +SILValue CanonicalizeBorrowScope::findDefInBorrowScope(SILValue value) { + while (auto *copy = dyn_cast(value)) { + if (isPersistentCopy(copy)) + return copy; + + value = copy->getOperand(); + } + return value; +} + +/// Visit all extended uses within the borrow scope, looking through copies. +/// Call visitUse for uses which could potentially be outside the borrow scope. +/// Call visitForwardingUse for hoistable forwarding operations which could +/// potentially be inside the borrow scope. +/// +/// The visitor may or may not be able to determine which uses are outside the +/// scope, but it can filter uses that are definitely within the scope. For +/// example, guaranteed uses and uses in live-out blocks must be both be within +/// the scope. +/// +/// This def-use traversal is similar to findExtendedTransitiveGuaranteedUses(), +/// however, to cover the canonical lifetime, it looks through copies. It also +/// considers uses within the introduced borrow scope itself (instead of simply +/// visiting the scope-ending uses). It does not, however, look into nested +/// borrow scopes uses, since nested scopes are canonicalized independently. +/// +/// \p innerValue is either the initial begin_borrow, or a forwarding operation +/// within the borrow scope. +template +bool CanonicalizeBorrowScope::visitBorrowScopeUses(SILValue innerValue, + Visitor &visitor) { + // defUseWorklist is used recursively here. + // This avoids revisiting uses in case we ever recurse through + // both structs and destructures. + unsigned defUseStart = defUseWorklist.size(); + defUseWorklist.insert(innerValue); + while (defUseStart < defUseWorklist.size()) { + SILValue value = defUseWorklist.pop(); + // Gather the uses before updating any of them. + // 'value' may be deleted in this loop after rewriting its last use. + // 'use' may become invalid after processing its user. + SmallVector uses(value->getUses()); + for (Operand *use : uses) { + auto *user = use->getUser(); + // Incidental uses, such as debug_value may be deleted before they can be + // processed. Their user will now be nullptr. This means that value + // is dead, so just bail. + if (!user) + break; + + // Recurse through copies. + if (auto *copy = dyn_cast(user)) { + if (!isPersistentCopy(copy)) { + defUseWorklist.insert(copy); + continue; + } + } + // Note: debug_value uses are handled like normal uses here. If they are + // inner uses, they remain. If they are outer uses, then they will be + // stripped if needed when the outer copy or persistent copy is + // canonicalized. + switch (use->getOperandOwnership()) { + case OperandOwnership::NonUse: + break; + case OperandOwnership::TrivialUse: + llvm_unreachable("this operand cannot handle ownership"); + + case OperandOwnership::InteriorPointer: + case OperandOwnership::EndBorrow: + case OperandOwnership::Reborrow: + // Ignore uses that must be within the borrow scope. + // Rewriting does not look through reborrowed values--it considers them + // part of a separate lifetime. + break; + + case OperandOwnership::ForwardingUnowned: + case OperandOwnership::PointerEscape: + // Pointer escapes are only allowed if they use the guaranteed value, + // which means that the escaped value must be confied to the current + // borrow scope. + if (use->get().getOwnershipKind() != OwnershipKind::Guaranteed) + return false; + break; + + case OperandOwnership::ForwardingBorrow: + case OperandOwnership::ForwardingConsume: + if (CanonicalizeBorrowScope::isRewritableOSSAForward(user)) { + if (!visitor.visitForwardingUse(use)) + return false; + break; + } + LLVM_FALLTHROUGH; + + case OperandOwnership::Borrow: + case OperandOwnership::InstantaneousUse: + case OperandOwnership::UnownedInstantaneousUse: + case OperandOwnership::BitwiseEscape: + case OperandOwnership::DestroyingConsume: + if (!visitor.visitUse(use)) + return false; + break; + } // end switch OperandOwnership + } + } // end def-use traversal + return true; +} + +namespace { + +using OuterUsers = CanonicalizeBorrowScope::OuterUsers; + +/// Find all potential outer uses within a borrow scope. +/// +/// Implements visitBorrowScopeUses +class FindBorrowScopeUses { + CanonicalizeBorrowScope &scope; + + /// useInsts are the potentially outer use instructions. This set is built up + /// recursively. It is pared down to only the outer uses outside this class by + /// filterOuterBorrowUseInsts. + OuterUsers useInsts; + +public: + FindBorrowScopeUses(CanonicalizeBorrowScope &scope) : scope(scope) {} + + Optional findUses() && { + scope.beginVisitBorrowScopeUses(); + if (!scope.visitBorrowScopeUses(scope.getBorrowedValue().value, *this)) + return None; + + return std::move(useInsts); + } + + bool visitUse(Operand *use) { + // A guaranteed use can never be outside this borrow scope + if (use->get().getOwnershipKind() == OwnershipKind::Guaranteed) + return true; + + auto *user = use->getUser(); + if (!isUserInLiveOutBlock(user)) { + useInsts.insert(user); + } + if (auto borrowingOper = BorrowingOperand(use)) { + // For borrows, record the scope-ending instructions to outer use + // points. Note: The logic in filterOuterBorrowUseInsts that checks + // whether a borrow scope is an outer use must visit the same set of uses. + borrowingOper.visitExtendedScopeEndingUses([&](Operand *endBorrow) { + auto *endInst = endBorrow->getUser(); + if (!isUserInLiveOutBlock(endInst)) { + useInsts.insert(endInst); + } + return true; + }); + } + return true; + } + + // Recurse through forwards. + bool visitForwardingUse(Operand *use) { + auto *user = use->getUser(); + if (!isUserInLiveOutBlock(user)) { + useInsts.insert(user); + } + for (auto result : user->getResults()) { + if (hasValueOwnership(result)) { + if (!scope.visitBorrowScopeUses(result, *this)) + return false; + } + } + return true; + } + +protected: + bool isUserInLiveOutBlock(SILInstruction *user) { + return (scope.getLiveness().getBlockLiveness(user->getParent()) + == PrunedLiveBlocks::LiveOut); + return false; + }; +}; + +} // namespace + +/// Erase users from \p outerUseInsts that are not within the borrow scope. +void CanonicalizeBorrowScope::filterOuterBorrowUseInsts( + OuterUsers &outerUseInsts) { + auto *beginBorrow = cast(borrowedValue.value); + SmallVector scopeEndingInsts; + BorrowedValue(beginBorrow).getLocalScopeEndingInstructions(scopeEndingInsts); + blockWorklist.clear(); + // Remove outer uses that occur before the end of the borrow scope by + // reverse iterating from the end_borrow. + auto scanBlock = [&](SILBasicBlock *bb, SILBasicBlock::iterator endIter) { + auto beginIter = bb->begin(); + if (bb == beginBorrow->getParent()) { + beginIter = std::next(beginBorrow->getIterator()); + } else { + blockWorklist.insert(bb); + } + for (auto instIter = endIter; instIter != beginIter;) { + --instIter; + outerUseInsts.erase(&*instIter); + } + }; + for (auto *scopeEnd : scopeEndingInsts) { + scanBlock(scopeEnd->getParent(), std::next(scopeEnd->getIterator())); + } + // This worklist is also a visited set, so we never pop the entries. + while (auto *bb = blockWorklist.pop()) { + for (auto *predBB : bb->getPredecessorBlocks()) { + scanBlock(predBB, predBB->end()); + } + } +} + +namespace { + +/// Remove redundant copies/destroys within a borrow scope. +class RewriteInnerBorrowUses { + CanonicalizeBorrowScope &scope; + +public: + RewriteInnerBorrowUses(CanonicalizeBorrowScope &scope): scope(scope) {} + + CanonicalizeBorrowScope &getScope() const { return scope; } + + // Implements visitBorrowScopeUses + bool visitUse(Operand *use) { + auto *user = use->getUser(); + SILValue value = use->get(); + // destroys are never needed within a borrow scope. + if (isa(user)) { + scope.getDeleter().forceDelete(user); + deleteCopyChain(value, scope.getDeleter()); + return true; + } + SILValue def = scope.findDefInBorrowScope(value); + if (use->isConsuming()) { + // All in-scope consuming uses need a unique copy in the same block. + auto *copy = dyn_cast(use->get()); + if (copy && copy->hasOneUse() && copy->getParent() == user->getParent()) { + value = copy->getOperand(); + copy->setOperand(def); + } else { + use->set(def); + copyLiveUse(use, scope.getCallbacks()); + } + deleteCopyChain(value, scope.getDeleter()); + return true; + } + // Non-consuming use. + use->set(def); + deleteCopyChain(value, scope.getDeleter()); + return true; + } + + // Recurse through forwards. + // + // Implements visitBorrowScopeUses + bool visitForwardingUse(Operand *use) { + auto *user = use->getUser(); + assert(CanonicalizeBorrowScope::isRewritableOSSAForward(user)); + + for (auto result : user->getResults()) { + if (!hasValueOwnership(result)) { + continue; + } + scope.visitBorrowScopeUses(result, *this); + } + // Update this operand bypassing any copies. + SILValue value = use->get(); + use->set(scope.findDefInBorrowScope(value)); + ForwardingOperand(use).setOwnershipKind(OwnershipKind::Guaranteed); + deleteCopyChain(value, scope.getDeleter()); + return true; + } +}; + +/// Generate copies outside this borrow scope, hoist forwarding operations, +/// rewrite the operands of uses outside the scope, and record the outer uses +/// that are consumes. +/// +/// Each hoisted forwarding operation instantiates a new instance of this +/// visitor. They are rewritten separately. +/// +/// Implements visitBorrowScopeUses +class RewriteOuterBorrowUses { + CanonicalizeBorrowScope &scope; + + RewriteInnerBorrowUses &innerRewriter; + + const OuterUsers &outerUseInsts; + + /// Map values inside the borrow scope to cloned-and-copied values outside the + /// borrow scope. + using InnerToOuterMap = llvm::SmallDenseMap; + InnerToOuterMap &innerToOuterMap; + + // Outer uses specific to the current incomingValue, including hoisted + // forwarding instructions, which are not part of outerUseInsts. + SmallVector consumingUses; + SmallPtrSet unclaimedConsumingUsers; + + RewriteOuterBorrowUses(RewriteInnerBorrowUses &innerRewriter, + const OuterUsers &outerUseInsts, + InnerToOuterMap &innerToOuterMap) + : scope(innerRewriter.getScope()), innerRewriter(innerRewriter), + outerUseInsts(outerUseInsts), innerToOuterMap(innerToOuterMap) {} + +public: + static void rewrite(BeginBorrowInst *beginBorrow, + CanonicalizeBorrowScope &scope, + const OuterUsers &outerUseInsts) { + + SILBuilderWithScope builder(beginBorrow); + auto loc = RegularLocation::getAutoGeneratedLocation(beginBorrow->getLoc()); + auto *outerCopy = + createOuterCopy(beginBorrow->getOperand(), builder, loc, scope); + + llvm::SmallDenseMap innerToOuterMap; + innerToOuterMap[beginBorrow] = outerCopy; + + RewriteInnerBorrowUses innerRewriter(scope); + RewriteOuterBorrowUses rewriter(innerRewriter, outerUseInsts, + innerToOuterMap); + scope.beginVisitBorrowScopeUses(); + auto outerVal = rewriter.recursivelyRewriteOuterUses(beginBorrow); + assert(outerVal == outerCopy); + } + + // Implements visitBorrowScopeUses + bool visitUse(Operand *use) { + auto *user = use->getUser(); + if (outerUseInsts.count(user)) { + rewriteOuterUse(use); + return true; + } + // If this use begins a borrow scope, check if any of the scope ending + // instructions are outside the current scope (this can happen if any copy + // has occured on the def-use chain within the current scope). + if (auto borrowingOper = BorrowingOperand(use)) { + if (!borrowingOper.visitExtendedScopeEndingUses( + [&](Operand *endBorrow) { + return !outerUseInsts.count(endBorrow->getUser()); + })) { + rewriteOuterUse(use); + return true; + } + } + innerRewriter.visitUse(use); + return true; + } + + // Recurse through forwards. + // + // Implements visitBorrowScopeUses + bool visitForwardingUse(Operand *use) { + auto *user = use->getUser(); + assert(CanonicalizeBorrowScope::isRewritableOSSAForward(user)); + if (outerUseInsts.count(user)) { + rewriteOuterUse(use); + return true; + } + + // Process transitive users and add this forwarding operation to + // outerUseInsts if any outer uses were found. + SILInstruction *outerClone = nullptr; + for (auto result : user->getResults()) { + if (!hasValueOwnership(result)) { + continue; + } + RewriteOuterBorrowUses rewriter(innerRewriter, outerUseInsts, + innerToOuterMap); + auto outerVal = rewriter.recursivelyRewriteOuterUses(result); + if (outerVal) + outerClone = outerVal->getDefiningInstruction(); + } + // Record the forwarded operand of this hoisted instruction as an outer use. + if (outerClone) { + recordOuterUse(&outerClone->getOperandRef(0)); + } + // If it's not already dead, update this operand bypassing any copies. + SILValue innerValue = use->get(); + if (scope.getDeleter().deleteIfDead(user)) { + LLVM_DEBUG(llvm::dbgs() << " Deleted " << *user); + } else { + use->set(scope.findDefInBorrowScope(use->get())); + ForwardingOperand(use).setOwnershipKind(OwnershipKind::Guaranteed); + } + deleteCopyChain(innerValue, scope.getDeleter()); + return true; + } + +protected: + // Create a copy for outer uses of the borrow scope introduced by + // currentDef. This copy should only be used by outer uses in the same block + // as the borrow scope. + // + // To use an existing outer copy, we could find its earliest consume. But the + // new copy will immediately canonicalized and a canonical begin_borrow scope + // have no outer uses of its first block. + static CopyValueInst *createOuterCopy(SILValue incomingValue, + SILBuilder &builder, SILLocation loc, + CanonicalizeBorrowScope &scope) { + auto *copy = builder.createCopyValue(loc, incomingValue); + scope.getCallbacks().createdNewInst(copy); + scope.recordOuterCopy(copy); + + ++NumCopiesGenerated; + LLVM_DEBUG(llvm::dbgs() << " Outer copy " << *copy); + + return copy; + } + + SILValue recursivelyRewriteOuterUses(SILValue innerValue) { + bool succeed = scope.visitBorrowScopeUses(innerValue, *this); + assert(succeed && "should be filtered by FindBorrowScopeUses"); + + auto iter = innerToOuterMap.find(innerValue); + assert(iter != innerToOuterMap.end()); + SILValue outerValue = iter->second; + cleanupOuterValue(outerValue); + return outerValue; + } + + void recordOuterUse(Operand *use) { + if (use->isLifetimeEnding()) { + consumingUses.push_back(use); + unclaimedConsumingUsers.insert(use->getUser()); + } + }; + + void rewriteOuterUse(Operand *use) { + LLVM_DEBUG(llvm::dbgs() << " Use of outer copy " << *use->getUser()); + + SILValue innerValue = use->get(); + SILValue outerValue = createOuterValues(innerValue); + + use->set(outerValue); + + deleteCopyChain(innerValue, scope.getDeleter()); + + recordOuterUse(use); + }; + + SILValue createOuterValues(SILValue innerValue); + + void cleanupOuterValue(SILValue outerValue); +}; + +} // namespace + +// Incrementally create a def-use chain outside the current borrow scope for +// all instructions between currentDef and \p innerValue, where \p innerValue +// has at least one use outside the borrow scope. +SILValue RewriteOuterBorrowUses::createOuterValues(SILValue innerValue) { + // Poke through the copies just like rewriteOuterBorrowUsesAndFindConsumes + // does on the def-use side. + while (auto *copy = dyn_cast(innerValue)) { + innerValue = copy->getOperand(); + } + auto iter = innerToOuterMap.find(innerValue); + if (iter != innerToOuterMap.end()) { + return iter->second; + } + auto *innerInst = innerValue->getDefiningInstruction(); + assert(CanonicalizeBorrowScope::isRewritableOSSAForward(innerInst)); + SILValue incomingInnerVal = innerInst->getOperand(0); + + auto incomingOuterVal = createOuterValues(incomingInnerVal); + + auto *insertPt = incomingOuterVal->getNextInstruction(); + auto *clone = innerInst->clone(insertPt); + scope.getCallbacks().createdNewInst(clone); + Operand *use = &clone->getOperandRef(0); + use->set(incomingOuterVal); + ForwardingOperand(use).setOwnershipKind(OwnershipKind::Owned); + + LLVM_DEBUG(llvm::dbgs() << " Hoisted forward " << *clone); + + for (unsigned idx = 0, endIdx = clone->getNumResults(); idx < endIdx; ++idx) { + innerToOuterMap[innerInst->getResult(idx)] = clone->getResult(idx); + } + return innerToOuterMap[innerValue]; +} + +// Insert destroys on the outer copy's or forwarding consume's lifetime +// frontier, or claim a existing consumes. Insert copies for unclaimed consumes. +void RewriteOuterBorrowUses::cleanupOuterValue(SILValue outerValue) { + // Gather outerValue's transitive ownership uses for ValueLifetimeAnalysis. + SmallVector outerUses; + for (Operand *use : outerValue->getUses()) { + outerUses.push_back(use->getUser()); + if (auto borrowingOper = BorrowingOperand(use)) { + borrowingOper.visitExtendedScopeEndingUses([&](Operand *endBorrow) { + outerUses.push_back(endBorrow->getUser()); + return true; + }); + } + } + ValueLifetimeAnalysis lifetimeAnalysis(outerValue.getDefiningInstruction(), + outerUses); + + auto createDestroy = [&](SILBuilder &b, SILLocation loc) { + auto *dvi = b.createDestroyValue(loc, outerValue); + scope.getCallbacks().createdNewInst(dvi); + }; + if (outerUses.empty()) { + SILBuilder b(outerValue->getNextInstruction()); + createDestroy(b, outerValue.getLoc()); + return; + } + + ValueLifetimeBoundary boundary; + lifetimeAnalysis.computeLifetimeBoundary(boundary); + + for (auto *boundaryEdge : boundary.boundaryEdges) { + if (DeadEndBlocks::triviallyEndsInUnreachable(boundaryEdge)) + continue; + auto insertPt = boundaryEdge->begin(); + auto *dvi = SILBuilderWithScope(insertPt).createDestroyValue( + insertPt->getLoc(), outerValue); + scope.getCallbacks().createdNewInst(dvi); + } + + for (SILInstruction *lastUser : boundary.lastUsers) { + if (unclaimedConsumingUsers.erase(lastUser)) + continue; + + SILBuilderWithScope::insertAfter(lastUser, [&](SILBuilder &b) { + createDestroy(b, lastUser->getLoc()); + }); + } + // Add copies for consuming users of outerValue. + for (auto *use : consumingUses) { + // If the user is still in the unclaimedConsumingUsers set, then it does not + // end the outer copy's lifetime and therefore requires a copy. Only one + // operand can be claimed as ending the lifetime, so return its user to the + // unclaimedConsumingUsers set after skipping the first copy. + auto iterAndInserted = unclaimedConsumingUsers.insert(use->getUser()); + if (!iterAndInserted.second) { + copyLiveUse(use, scope.getCallbacks()); + scope.recordOuterCopy(cast(use->get())); + } + } +} + +// If this succeeds, then all uses of the borrowed value outside the borrow +// scope will be rewritten to use an outer copy, and all remaining uses of the +// borrowed value will be confined to the borrow scope. +// +// TODO: Canonicalize multi-block borrow scopes, load_borrow scope, and phi +// borrow scopes by adding one copy per block to persistentCopies for +// each block that dominates an outer use. +bool CanonicalizeBorrowScope::consolidateBorrowScope() { + OuterUsers outerUseInsts; + if (!isa(borrowedValue.value)) { + // getCanonicalCopiedDef ensures that if currentDef is a guaranteed value, + // then it is a borrow scope introducer. + assert(borrowedValue.isLocalScope()); + + // Gather all potential outer uses before rewriting any to avoid scanning + // any basic block more than once. + Optional outerUsers = FindBorrowScopeUses(*this).findUses(); + if (!outerUsers) + return false; + + outerUseInsts = std::move(outerUsers).getValue(); + + filterOuterBorrowUseInsts(outerUseInsts); + } + // If there are no outer uses, then canonicalization is a simple matter of + // removing all the destroys and rewriting the copies. + if (outerUseInsts.empty()) { + RewriteInnerBorrowUses innerRewriter(*this); + beginVisitBorrowScopeUses(); // reset the def/use worklist + bool succeed = visitBorrowScopeUses(borrowedValue.value, innerRewriter); + assert(succeed && "should be filtered by FindBorrowScopeUses"); + return true; + } + LLVM_DEBUG(llvm::dbgs() << " Outer uses:\n"; + for (SILInstruction *inst + : outerUseInsts) { llvm::dbgs() << " " << *inst; }); + + RewriteOuterBorrowUses::rewrite(cast(borrowedValue.value), + *this, outerUseInsts); + return true; +} + +//===----------------------------------------------------------------------===// +// MARK: Top-Level CanonicalizeBorrowScope +//===----------------------------------------------------------------------===// + +bool CanonicalizeBorrowScope::canonicalizeFunctionArgument( + SILFunctionArgument *arg) { + BorrowedValue borrow(arg); + if (!borrow) + return false; + + initBorrow(borrow); + + LLVM_DEBUG(llvm::dbgs() << "*** Canonicalize Borrow: " << borrowedValue); + + SWIFT_DEFER { liveness.clear(); }; + + RewriteInnerBorrowUses innerRewriter(*this); + beginVisitBorrowScopeUses(); // reset the def/use worklist + bool succeed = visitBorrowScopeUses(borrowedValue.value, innerRewriter); + assert(succeed && "should be filtered by FindBorrowScopeUses"); + return true; +} + +/// Canonicalize a worklist of extended lifetimes. This iterates after rewriting +/// borrow scopes to handle new outer copies and new owned lifetimes from +/// forwarding operations. +bool CanonicalizeBorrowScope:: +canonicalizeBorrowScope(BorrowedValue borrowedValue) { + LLVM_DEBUG(llvm::dbgs() << "*** Canonicalize Borrow: " << borrowedValue); + + initBorrow(borrowedValue); + + SWIFT_DEFER { liveness.clear(); }; + + if (!computeBorrowLiveness()) + return false; + + // Set outerCopy and persistentCopies and rewrite uses + // outside the scope. + if (!consolidateBorrowScope()) + return false; + + return true; +} diff --git a/lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp b/lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp index d2c29e6046fb9..281159f408d69 100644 --- a/lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp +++ b/lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp @@ -410,6 +410,18 @@ broadenSingleElementStores(StoreInst *storeInst, // Simple ARC Peepholes //===----------------------------------------------------------------------===// +/// "dead" copies are removed in OSSA, but this may shorten object lifetimes, +/// changing program semantics in unexpected ways by releasing weak references +/// and running deinitializers early. This copy may be the only thing keeping a +/// variable's reference alive. But just because the copy's current SSA value +/// contains no other uses does not mean that there aren't other uses that still +/// correspond to the original variable whose lifetime is protected by this +/// copy. The only way to guarantee the lifetime of a variable is to use a +/// borrow scope--copy/destroy is insufficient by itself. +/// +/// FIXME: Technically this should be guarded by a compiler flag like +/// -enable-copy-propagation until SILGen protects scoped variables by borrow +/// scopes. static SILBasicBlock::iterator eliminateSimpleCopies(CopyValueInst *cvi, CanonicalizeInstruction &pass) { auto next = std::next(cvi->getIterator()); @@ -432,6 +444,8 @@ eliminateSimpleCopies(CopyValueInst *cvi, CanonicalizeInstruction &pass) { return next; } +/// Unlike dead copy elimination, dead borrows can be safely removed because the +/// semantics of a borrow scope static SILBasicBlock::iterator eliminateSimpleBorrows(BeginBorrowInst *bbi, CanonicalizeInstruction &pass) { auto next = std::next(bbi->getIterator()); diff --git a/lib/SILOptimizer/Utils/CastOptimizer.cpp b/lib/SILOptimizer/Utils/CastOptimizer.cpp index e1acc2bd97e38..21c59151f1c61 100644 --- a/lib/SILOptimizer/Utils/CastOptimizer.cpp +++ b/lib/SILOptimizer/Utils/CastOptimizer.cpp @@ -1004,6 +1004,7 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { auto TargetFormalType = dynamicCast.getTargetFormalType(); auto Loc = dynamicCast.getLocation(); auto *SuccessBB = dynamicCast.getSuccessBlock(); + auto *FailureBB = dynamicCast.getFailureBlock(); auto Op = dynamicCast.getSource(); auto *F = dynamicCast.getFunction(); @@ -1016,10 +1017,13 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { SILBuilderWithScope Builder(Inst, builderContext); if (Feasibility == DynamicCastFeasibility::WillFail) { - TinyPtrVector Args; - if (Builder.hasOwnership()) - Args.push_back(Inst->getOperand()); - auto *NewI = Builder.createBranch(Loc, dynamicCast.getFailureBlock(), Args); + auto *NewI = Builder.createBranch(Loc, FailureBB); + if (Builder.hasOwnership()) { + FailureBB->getArgument(0)->replaceAllUsesWith(Op); + FailureBB->eraseArgument(0); + SuccessBB->getArgument(0)->replaceAllUsesWithUndef(); + SuccessBB->eraseArgument(0); + } eraseInstAction(Inst); willFailAction(); return NewI; @@ -1063,7 +1067,19 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { CastedValue = Op; } - auto *NewI = Builder.createBranch(Loc, SuccessBB, CastedValue); + BranchInst *NewI = nullptr; + + if (Builder.hasOwnership()) { + NewI = Builder.createBranch(Loc, SuccessBB); + SuccessBB->getArgument(0)->replaceAllUsesWith(CastedValue); + SuccessBB->eraseArgument(0); + FailureBB->getArgument(0)->replaceAllUsesWithUndef(); + FailureBB->eraseArgument(0); + } + else { + NewI = Builder.createBranch(Loc, SuccessBB, CastedValue); + } + eraseInstAction(Inst); willSucceedAction(); return NewI; diff --git a/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp b/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp index 09f032bb1d02e..875dd080de07f 100644 --- a/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp +++ b/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp @@ -42,6 +42,16 @@ class CheckedCastBrJumpThreading { // Dominator information to be used. DominanceInfo *DT; + // DeadEndBlocks is used by OwnershipRAUW and incrementally updated within + // CheckedCastBrJumpThreading. + // + // TODO: incrementally update dead-end blocks during SimplifyCFG so it doesn't + // need to be recomputed each time tryCheckedCastBrJumpThreading is called. + DeadEndBlocks *deBlocks; + + // Enable non-trivial terminator rewriting in OSSA. + bool EnableOSSARewriteTerminator; + // List of predecessors. typedef SmallVector PredList; @@ -119,10 +129,14 @@ class CheckedCastBrJumpThreading { bool trySimplify(CheckedCastBranchInst *CCBI); public: - CheckedCastBrJumpThreading(SILFunction *Fn, DominanceInfo *DT, - SmallVectorImpl &BlocksForWorklist) - : Fn(Fn), DT(DT), BlocksForWorklist(BlocksForWorklist), - BlocksToEdit(Fn), BlocksToClone(Fn) { } + CheckedCastBrJumpThreading( + SILFunction *Fn, DominanceInfo *DT, DeadEndBlocks *deBlocks, + SmallVectorImpl &BlocksForWorklist, + bool EnableOSSARewriteTerminator) + : Fn(Fn), DT(DT), deBlocks(deBlocks), + EnableOSSARewriteTerminator(EnableOSSARewriteTerminator), + BlocksForWorklist(BlocksForWorklist), BlocksToEdit(Fn), + BlocksToClone(Fn) {} void optimizeFunction(); }; @@ -486,6 +500,11 @@ areEquivalentConditionsAlongPaths(CheckedCastBranchInst *DomCCBI) { /// Try performing a dominator-based jump-threading for /// checked_cast_br instructions. bool CheckedCastBrJumpThreading::trySimplify(CheckedCastBranchInst *CCBI) { + if (!EnableOSSARewriteTerminator && Fn->hasOwnership() + && !CCBI->getOperand()->getType().isTrivial(*Fn)) { + return false; + } + // Init information about the checked_cast_br we try to // jump-thread. BB = CCBI->getParent(); @@ -661,7 +680,7 @@ void CheckedCastBrJumpThreading::optimizeFunction() { Fn->verifyCriticalEdges(); for (Edit *edit : Edits) { - BasicBlockCloner Cloner(edit->CCBBlock); + BasicBlockCloner Cloner(edit->CCBBlock, deBlocks); if (!Cloner.canCloneBlock()) continue; @@ -685,9 +704,14 @@ void CheckedCastBrJumpThreading::optimizeFunction() { namespace swift { -bool tryCheckedCastBrJumpThreading(SILFunction *Fn, DominanceInfo *DT, - SmallVectorImpl &BlocksForWorklist) { - CheckedCastBrJumpThreading CCBJumpThreading(Fn, DT, BlocksForWorklist); +bool tryCheckedCastBrJumpThreading( + SILFunction *Fn, DominanceInfo *DT, DeadEndBlocks *deBlocks, + SmallVectorImpl &BlocksForWorklist, + bool EnableOSSARewriteTerminator) { + + CheckedCastBrJumpThreading CCBJumpThreading(Fn, DT, deBlocks, + BlocksForWorklist, + EnableOSSARewriteTerminator); CCBJumpThreading.optimizeFunction(); return !BlocksForWorklist.empty(); } diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index d76242be0deae..f1af21bf1ed5c 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -1422,7 +1422,7 @@ ConstExprFunctionState::initializeAddressFromSingleWriter(SILValue addr) { // Ignore markers, loads, and other things that aren't stores to this stack // value. if (isa(user) || isa(user) || - isa(user) || isa(user)) + isa(user) || DebugValueInst::hasAddrVal(user)) continue; // TODO: Allow BeginAccess/EndAccess users. @@ -1737,8 +1737,7 @@ llvm::Optional ConstExprFunctionState::evaluateClosureCreation( llvm::Optional ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) { // These are just markers. - if (isa(inst) || isa(inst) || - isa(inst) || + if (isa(inst) || isa(inst) || // The interpreter doesn't model these memory management instructions, so // skip them. isa(inst) || isa(inst) || diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index 26f5c0be8664a..65ca711697c8a 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -875,28 +875,7 @@ constantFoldAndCheckIntegerConversions(BuiltinInst *BI, template static bool tryExtractLiteralText(FloatLiteralInst *flitInst, SmallString &fpStr) { - - Expr *expr = flitInst->getLoc().getAsASTNode(); - if (!expr) - return false; - - // 'expr' may not be a FloatLiteralExpr since 'flitInst' could have been - // created by the ConstantFolder by folding floating-point constructor calls. - // So we iterate through the sequence of folded constructors if any, and - // try to extract the FloatLiteralExpr. - while (auto *callExpr = dyn_cast(expr)) { - if (callExpr->getNumArguments() != 1 || - !dyn_cast(callExpr->getFn())) - break; - - auto *tupleExpr = dyn_cast(callExpr->getArg()); - if (!tupleExpr) - break; - - expr = tupleExpr->getElement(0); - } - - auto *flitExpr = dyn_cast(expr); + auto *flitExpr = flitInst->getLoc().getAsASTNode(); if (!flitExpr) return false; @@ -1074,27 +1053,8 @@ bool isLossyUnderflow(APFloat srcVal, BuiltinFloatType *srcType, /// This function determines whether the float literal in the given /// SIL instruction is specified using hex-float notation in the Swift source. bool isHexLiteralInSource(FloatLiteralInst *flitInst) { - Expr *expr = flitInst->getLoc().getAsASTNode(); - if (!expr) - return false; - - // Iterate through a sequence of folded implicit constructors if any, and - // try to extract the FloatLiteralExpr. - while (auto *callExpr = dyn_cast(expr)) { - if (!callExpr->isImplicit() || callExpr->getNumArguments() != 1 || - !dyn_cast(callExpr->getFn())) - break; - - auto *tupleExpr = dyn_cast(callExpr->getArg()); - if (!tupleExpr) - break; - - expr = tupleExpr->getElement(0); - } - auto *flitExpr = dyn_cast(expr); - if (!flitExpr) - return false; - return flitExpr->getDigitsText().startswith("0x"); + auto *flitExpr = flitInst->getLoc().getAsASTNode(); + return flitExpr && flitExpr->getDigitsText().startswith("0x"); } bool maybeExplicitFPCons(BuiltinInst *BI, const BuiltinInfo &Builtin) { @@ -2023,7 +1983,7 @@ ConstantFolder::processWorkList() { // Eagerly DCE. We do this after visiting all users to ensure we don't // invalidate the uses iterator. - deleter.cleanUpDeadInstructions(); + deleter.cleanupDeadInstructions(); } // TODO: refactor this code outside of the method. Passes should not merge diff --git a/lib/SILOptimizer/Utils/Devirtualize.cpp b/lib/SILOptimizer/Utils/Devirtualize.cpp index a4edd7ee4fa5d..5f785fb2c069b 100644 --- a/lib/SILOptimizer/Utils/Devirtualize.cpp +++ b/lib/SILOptimizer/Utils/Devirtualize.cpp @@ -407,7 +407,7 @@ getSubstitutionsForCallee(SILModule &module, CanSILFunctionType baseCalleeType, unsigned baseDepth = 0; SubstitutionMap baseSubMap; if (auto baseClassSig = baseClassDecl->getGenericSignatureOfContext()) { - baseDepth = baseClassSig->getGenericParams().back()->getDepth() + 1; + baseDepth = baseClassSig.getGenericParams().back()->getDepth() + 1; // Compute the type of the base class, starting from the // derived class type and the type of the method's self @@ -434,7 +434,7 @@ getSubstitutionsForCallee(SILModule &module, CanSILFunctionType baseCalleeType, // parameters from the derived class. unsigned origDepth = 0; if (auto calleeClassSig = calleeClassDecl->getGenericSignatureOfContext()) - origDepth = calleeClassSig->getGenericParams().back()->getDepth() + 1; + origDepth = calleeClassSig.getGenericParams().back()->getDepth() + 1; auto baseCalleeSig = baseCalleeType->getInvocationGenericSignature(); @@ -916,7 +916,7 @@ getWitnessMethodSubstitutions( unsigned baseDepth = 0; auto *rootConformance = conformance->getRootConformance(); if (auto witnessSig = rootConformance->getGenericSignature()) - baseDepth = witnessSig->getGenericParams().back()->getDepth() + 1; + baseDepth = witnessSig.getGenericParams().back()->getDepth() + 1; // If the witness has a class-constrained 'Self' generic parameter, // we have to build a new substitution map that shifts all generic diff --git a/lib/SILOptimizer/Utils/DifferentiationMangler.cpp b/lib/SILOptimizer/Utils/DifferentiationMangler.cpp index 31b77e67aefea..4973128a28b54 100644 --- a/lib/SILOptimizer/Utils/DifferentiationMangler.cpp +++ b/lib/SILOptimizer/Utils/DifferentiationMangler.cpp @@ -168,7 +168,7 @@ DifferentiationMangler::mangleDerivativeFunctionSubsetParametersThunk( // mangle the other parts. beginManglingWithoutPrefix(); appendOperator(originalName); - appendType(toType); + appendType(toType, nullptr); auto kindCode = (char)kind; appendOperator("TJS", StringRef(&kindCode, 1)); appendIndexSubset(fromParamIndices); @@ -185,7 +185,7 @@ std::string DifferentiationMangler::mangleLinearMapSubsetParametersThunk( IndexSubset *fromParamIndices, IndexSubset *fromResultIndices, IndexSubset *toParamIndices) { beginMangling(); - appendType(fromType); + appendType(fromType, nullptr); auto functionKindCode = (char)getAutoDiffFunctionKind(linearMapKind); appendOperator("TJS", StringRef(&functionKindCode, 1)); appendIndexSubset(fromParamIndices); diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 7906c793ff657..32e8de0cd63e1 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -86,7 +86,8 @@ static SILInstruction *getStackInitInst(SILValue allocStackAddr, // Ignore instructions which don't write to the stack location. // Also ignore ASIUser (only kicks in if ASIUser is the original apply). - if (isa(User) || isa(User) || + if (isa(User) || + DebugValueInst::hasAddrVal(User) || isa(User) || isa(User) || isa(User) || isa(User) || User == ASIUser) { diff --git a/lib/SILOptimizer/Utils/GenericCloner.cpp b/lib/SILOptimizer/Utils/GenericCloner.cpp index 57dc9cc1f1743..b625ddf2de2e7 100644 --- a/lib/SILOptimizer/Utils/GenericCloner.cpp +++ b/lib/SILOptimizer/Utils/GenericCloner.cpp @@ -111,17 +111,21 @@ void GenericCloner::populateCloned() { auto *NewArg = ClonedEntryBB->createFunctionArgument( mappedType, OrigArg->getDecl()); - // Try to create a new debug_value from an existing debug_value_addr - // for the argument. We do this before storing to ensure that when we - // are cloning code in ossa the argument has not been consumed by the - // store below. + // Try to create a new debug_value from an existing debug_value w/ + // address value for the argument. We do this before storing to + // ensure that when we are cloning code in ossa the argument has + // not been consumed by the store below. for (Operand *ArgUse : OrigArg->getUses()) { - if (auto *DVAI = dyn_cast(ArgUse->getUser())) { + if (auto *DVI = DebugValueInst::hasAddrVal(ArgUse->getUser())) { auto *oldScope = getBuilder().getCurrentDebugScope(); getBuilder().setCurrentDebugScope( - remapScope(DVAI->getDebugScope())); - getBuilder().createDebugValue(DVAI->getLoc(), NewArg, - *DVAI->getVarInfo()); + remapScope(DVI->getDebugScope())); + auto VarInfo = DVI->getVarInfo(); + assert(VarInfo && VarInfo->DIExpr && + "No DebugVarInfo or no DIExpr operand?"); + // Drop the op_deref + VarInfo->DIExpr.eraseElement(VarInfo->DIExpr.element_begin()); + getBuilder().createDebugValue(DVI->getLoc(), NewArg, *VarInfo); getBuilder().setCurrentDebugScope(oldScope); break; } diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index de958d68f2415..8c93eebf8bf39 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -915,7 +915,7 @@ getGenericEnvironmentAndSignatureWithRequirements( OrigGenSig.getPointer(), { }, std::move(RequirementsCopy)}, GenericSignature()); - auto NewGenEnv = NewGenSig->getGenericEnvironment(); + auto NewGenEnv = NewGenSig.getGenericEnvironment(); return { NewGenEnv, NewGenSig }; } @@ -946,7 +946,7 @@ void ReabstractionInfo::performFullSpecializationPreparation( static bool hasNonSelfContainedRequirements(ArchetypeType *Archetype, GenericSignature Sig, GenericEnvironment *Env) { - auto Reqs = Sig->getRequirements(); + auto Reqs = Sig.getRequirements(); auto CurrentGP = Archetype->getInterfaceType() ->getCanonicalType() ->getRootGenericParam(); @@ -989,7 +989,7 @@ static bool hasNonSelfContainedRequirements(ArchetypeType *Archetype, static void collectRequirements(ArchetypeType *Archetype, GenericSignature Sig, GenericEnvironment *Env, SmallVectorImpl &CollectedReqs) { - auto Reqs = Sig->getRequirements(); + auto Reqs = Sig.getRequirements(); auto CurrentGP = Archetype->getInterfaceType() ->getCanonicalType() ->getRootGenericParam(); @@ -1261,14 +1261,14 @@ class FunctionSignaturePartialSpecializer { M(M), SM(M.getSwiftModule()), Ctx(M.getASTContext()) { // Create the new generic signature using provided requirements. - SpecializedGenericEnv = SpecializedGenericSig->getGenericEnvironment(); + SpecializedGenericEnv = SpecializedGenericSig.getGenericEnvironment(); // Compute SubstitutionMaps required for re-mapping. // Callee's generic signature and specialized generic signature // use the same set of generic parameters, i.e. each generic // parameter should be mapped to itself. - for (auto GP : CalleeGenericSig->getGenericParams()) { + for (auto GP : CalleeGenericSig.getGenericParams()) { CalleeInterfaceToSpecializedInterfaceMapping[GP] = Type(GP); } computeCalleeInterfaceToSpecializedInterfaceMap(); @@ -1437,7 +1437,7 @@ void FunctionSignaturePartialSpecializer:: /// which requires a substitution. void FunctionSignaturePartialSpecializer:: createGenericParamsForCalleeGenericParams() { - for (auto GP : CalleeGenericSig->getGenericParams()) { + for (auto GP : CalleeGenericSig.getGenericParams()) { auto CanTy = GP->getCanonicalType(); auto CanTyInContext = CalleeGenericSig->getCanonicalTypeInContext(CanTy); @@ -1570,9 +1570,8 @@ void FunctionSignaturePartialSpecializer::addCallerRequirements() { /// Add requirements from the callee's signature. void FunctionSignaturePartialSpecializer::addCalleeRequirements() { - if (CalleeGenericSig) - addRequirements(CalleeGenericSig->getRequirements(), - CalleeInterfaceToSpecializedInterfaceMap); + addRequirements(CalleeGenericSig.getRequirements(), + CalleeInterfaceToSpecializedInterfaceMap); } std::pair @@ -1586,7 +1585,7 @@ FunctionSignaturePartialSpecializer:: Ctx.evaluator, AbstractGenericSignatureRequest{nullptr, AllGenericParams, AllRequirements}, GenericSignature()); - auto GenEnv = GenSig ? GenSig->getGenericEnvironment() : nullptr; + auto *GenEnv = GenSig.getGenericEnvironment(); return { GenEnv, GenSig }; } @@ -1657,7 +1656,7 @@ void FunctionSignaturePartialSpecializer:: SpecializedGenericEnv = GenPair.first; } - for (auto GP : CalleeGenericSig->getGenericParams()) { + for (auto GP : CalleeGenericSig.getGenericParams()) { CalleeInterfaceToSpecializedInterfaceMapping[GP] = Type(GP); } computeCalleeInterfaceToSpecializedInterfaceMap(); diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 70f3b16e77035..6716fa4bdf98b 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -27,6 +27,7 @@ #include "swift/SIL/TypeLowering.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/Analysis.h" +#include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/ConstExpr.h" @@ -157,7 +158,7 @@ bool swift::isInstructionTriviallyDead(SILInstruction *inst) { if (isa(inst)) return false; - if (isa(inst) || isa(inst)) + if (isa(inst)) return false; // These invalidate enums so "write" memory, but that is not an essential @@ -210,18 +211,22 @@ bool swift::isIntermediateRelease(SILInstruction *inst, return false; } -static bool hasOnlyEndOfScopeOrDestroyUses(SILInstruction *inst) { +bool swift::hasOnlyEndOfScopeOrEndOfLifetimeUses(SILInstruction *inst) { for (SILValue result : inst->getResults()) { for (Operand *use : result->getUses()) { SILInstruction *user = use->getUser(); bool isDebugUser = user->isDebugInstruction(); - if (!isa(user) && !isEndOfScopeMarker(user) && - !isDebugUser) + if (!isa(user) && !isa(user) && + !isEndOfScopeMarker(user) && !isDebugUser) return false; // Include debug uses only in Onone mode. if (isDebugUser && inst->getFunction()->getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization) - return false; + if (auto DbgVarInst = DebugVarCarryingInst(user)) { + auto VarInfo = DbgVarInst.getVarInfo(); + if (VarInfo && !VarInfo->Implicit) + return false; + } } } return true; @@ -289,7 +294,10 @@ static bool isNonGenericReadOnlyConstantEvaluableCall(FullApplySite applySite) { /// of non-trivial results. /// /// \param inst instruction that checked for liveness. -static bool isScopeAffectingInstructionDead(SILInstruction *inst) { +/// +/// TODO: Handle partial_apply [stack] which has a dealloc_stack user. +static bool isScopeAffectingInstructionDead(SILInstruction *inst, + bool fixLifetime) { SILFunction *fun = inst->getFunction(); assert(fun && "Instruction has no function."); // Only support ownership SIL for scoped instructions. @@ -298,7 +306,7 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst) { } // If the instruction has any use other than end of scope use or destroy_value // use, bail out. - if (!hasOnlyEndOfScopeOrDestroyUses(inst)) { + if (!hasOnlyEndOfScopeOrEndOfLifetimeUses(inst)) { return false; } // If inst is a copy or beginning of scope, inst is dead, since we know that @@ -323,10 +331,31 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst) { // say this for load. } case SILInstructionKind::PartialApplyInst: { + bool onlyTrivialArgs = true; + for (auto &arg : cast(inst)->getArgumentOperands()) { + auto argTy = arg.get()->getType(); + // Non-stack partial apply captures that are passed by address are always + // captured at +1 by the closure contrext, regardless of the calling + // convention. + // + // TODO: When on-stack partial applies are also handled, then their +0 + // address arguments can be ignored. + // + // FIXME: Even with fixLifetimes enabled, InstructionDeleter does not know + // how to cleanup arguments captured by address. This should be as simple + // as inserting a destroy_addr. But the analagous code in + // tryDeleteDeadClosure() and keepArgsOfPartialApplyAlive() mysteriously + // creates new alloc_stack's and invalidates stack nesting. So we + // conservatively bail-out until we understand why that hack exists. + if (argTy.isAddress()) + return false; + + onlyTrivialArgs &= argTy.isTrivial(*fun); + } // Partial applies that are only used in destroys cannot have any effect on // the program state, provided the values they capture are explicitly - // destroyed. - return true; + // destroyed, which only happens when fixLifetime is true. + return onlyTrivialArgs || fixLifetime; } case SILInstructionKind::StructInst: case SILInstructionKind::EnumInst: @@ -366,111 +395,116 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst) { } } -void InstructionDeleter::trackIfDead(SILInstruction *inst) { - if (isInstructionTriviallyDead(inst) || - isScopeAffectingInstructionDead(inst)) { +static bool hasOnlyIncidentalUses(SILInstruction *inst, + bool disallowDebugUses = false) { + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + if (!isIncidentalUse(user)) + return false; + if (disallowDebugUses && user->isDebugInstruction()) + return false; + } + } + return true; +} + +bool InstructionDeleter::trackIfDead(SILInstruction *inst) { + bool fixLifetime = inst->getFunction()->hasOwnership(); + if (isInstructionTriviallyDead(inst) + || isScopeAffectingInstructionDead(inst, fixLifetime)) { assert(!isIncidentalUse(inst) && !isa(inst) && "Incidental uses cannot be removed in isolation. " "They would be removed iff the operand is dead"); + getCallbacks().notifyWillBeDeleted(inst); deadInstructions.insert(inst); + return true; } + return false; } -/// Given an \p operand that belongs to an instruction that will be removed, -/// destroy the operand just before the instruction, if the instruction consumes -/// \p operand. This function will result in a double consume, which is expected -/// to be resolved when the caller deletes the original instruction. This -/// function works only on ownership SIL. -static void destroyConsumedOperandOfDeadInst(Operand &operand, - InstModCallbacks callbacks) { - assert(operand.get() && operand.getUser()); - SILInstruction *deadInst = operand.getUser(); - SILFunction *fun = deadInst->getFunction(); - assert(fun->hasOwnership()); - - SILValue operandValue = operand.get(); - if (operandValue->getType().isTrivial(*fun)) - return; - // Ignore type-dependent operands which are not real operands but are just - // there to create use-def dependencies. - if (deadInst->isTypeDependentOperand(operand)) - return; - // A scope ending instruction cannot be deleted in isolation without removing - // the instruction defining its operand as well. - assert(!isEndOfScopeMarker(deadInst) && !isa(deadInst) && - !isa(deadInst) && - "lifetime ending instruction is deleted without its operand"); - if (operand.isLifetimeEnding()) { - // Since deadInst cannot be an end-of-scope instruction (asserted above), - // this must be a consuming use of an owned value. - assert(operandValue.getOwnershipKind() == OwnershipKind::Owned); - SILBuilderWithScope builder(deadInst); - auto *dvi = builder.createDestroyValue(deadInst->getLoc(), operandValue); - callbacks.createdNewInst(dvi); - } +void InstructionDeleter::forceTrackAsDead(SILInstruction *inst) { + bool disallowDebugUses = inst->getFunction()->getEffectiveOptimizationMode() + <= OptimizationMode::NoOptimization; + assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); + getCallbacks().notifyWillBeDeleted(inst); + deadInstructions.insert(inst); } -void InstructionDeleter::deleteInstruction(SILInstruction *inst, - bool fixOperandLifetimes) { - // We cannot fix operand lifetimes in non-ownership SIL. - assert(!fixOperandLifetimes || inst->getFunction()->hasOwnership()); - // Collect instruction and its immediate uses and check if they are all - // incidental uses. Also, invoke the callback on the instruction and its uses. - // Note that the Callback is invoked before deleting anything to ensure that - // the SIL is valid at the time of the callback. +/// Force-delete \p inst and all its uses. +/// +/// \p fixLifetimes causes new destroys to be inserted after dropping +/// operands. +/// +/// \p forceDeleteUsers allows deleting an instruction with non-incidental, +/// non-destoy uses, such as a store. +/// +/// Does not call callbacks.notifyWillBeDeleted for \p inst. But does +/// for any other instructions that become dead as a result. +/// +/// Carefully orchestrated steps for deleting an instruction with its uses: +/// +/// Recursively gather the instruction's uses into the toDeleteInsts set and +/// dropping the operand for each use traversed. +/// +/// For the remaining operands, insert destroys for consuming operands and track +/// newly dead operand definitions. +/// +/// Finally, erase the instruction. +void InstructionDeleter::deleteWithUses(SILInstruction *inst, bool fixLifetimes, + bool forceDeleteUsers) { + // Cannot fix operand lifetimes in non-ownership SIL. + assert(!fixLifetimes || inst->getFunction()->hasOwnership()); + + // Recursively visit all uses while growing toDeleteInsts in def-use order and + // dropping dead operands. SmallVector toDeleteInsts; + toDeleteInsts.push_back(inst); - instModCallbacks.notifyWillBeDeleted(inst); - for (SILValue result : inst->getResults()) { - for (Operand *use : result->getUses()) { - SILInstruction *user = use->getUser(); - assert(isIncidentalUse(user) || isa(user)); - instModCallbacks.notifyWillBeDeleted(user); - toDeleteInsts.push_back(user); + swift::salvageDebugInfo(inst); + for (unsigned idx = 0; idx < toDeleteInsts.size(); ++idx) { + for (SILValue result : toDeleteInsts[idx]->getResults()) { + // Temporary use vector to avoid iterator invalidation. + auto uses = llvm::to_vector<4>(result->getUses()); + for (Operand *use : uses) { + SILInstruction *user = use->getUser(); + assert(forceDeleteUsers || isIncidentalUse(user) || + isa(user)); + assert(!isa(user) && "can't delete phis"); + + toDeleteInsts.push_back(user); + swift::salvageDebugInfo(user); + use->drop(); + } } } - // Record definitions of instruction's operands. Also, in case an operand is - // consumed by inst, emit necessary compensation code. - SmallVector operandDefinitions; - for (Operand &operand : inst->getAllOperands()) { - SILValue operandValue = operand.get(); - assert(operandValue && - "Instruction's operand are deleted before the instruction"); - SILInstruction *defInst = operandValue->getDefiningInstruction(); - // If the operand has a defining instruction, it could be potentially - // dead. Therefore, record the definition. - if (defInst) - operandDefinitions.push_back(defInst); - // The scope of the operand could be ended by inst. Therefore, emit - // any compensating code needed to end the scope of the operand value - // once inst is deleted. - if (fixOperandLifetimes) - destroyConsumedOperandOfDeadInst(operand, instModCallbacks); - } - // First drop all references from all instructions to be deleted and then - // erase the instruction. Note that this is done in this order so that when an - // instruction is deleted, its uses would have dropped their references. - // Note that the toDeleteInsts must also be removed from the tracked - // deadInstructions. - for (SILInstruction *inst : toDeleteInsts) { + // Process the remaining operands. Insert destroys for consuming + // operands. Track newly dead operand values. + for (auto *inst : toDeleteInsts) { + for (Operand &operand : inst->getAllOperands()) { + SILValue operandValue = operand.get(); + // Check for dead operands, which are dropped above. + if (!operandValue) + continue; + + if (fixLifetimes && operand.isConsuming()) { + SILBuilderWithScope builder(inst); + auto *dvi = builder.createDestroyValue(inst->getLoc(), operandValue); + getCallbacks().createdNewInst(dvi); + } + auto *operDef = operandValue->getDefiningInstruction(); + operand.drop(); + if (operDef) { + trackIfDead(operDef); + } + } + inst->dropNonOperandReferences(); deadInstructions.remove(inst); - inst->dropAllReferences(); - } - for (SILInstruction *inst : toDeleteInsts) { - // We do not notify when deleting since we already called the notify - // callback earlier for all toDeleteInsts. - instModCallbacks.deleteInst(inst, false /*notify when deleting*/); - } - // Record operand definitions that become dead now. - for (SILInstruction *operandValInst : operandDefinitions) { - trackIfDead(operandValInst); + getCallbacks().deleteInst(inst, false /*notify when deleting*/); } } -void InstructionDeleter::cleanUpDeadInstructions() { - SILFunction *fun = nullptr; - if (!deadInstructions.empty()) - fun = deadInstructions.front()->getFunction(); +void InstructionDeleter::cleanupDeadInstructions() { while (!deadInstructions.empty()) { SmallVector currentDeadInsts(deadInstructions.begin(), deadInstructions.end()); @@ -484,33 +518,21 @@ void InstructionDeleter::cleanUpDeadInstructions() { // deadInst as deadInstructions is a set vector, and the latter cannot be // in deadInstructions as they are incidental uses which are never added // to deadInstructions. - deleteInstruction(deadInst, /*Fix lifetime of operands*/ - fun->hasOwnership()); + deleteWithUses(deadInst, + /*fixLifetimes*/ deadInst->getFunction()->hasOwnership()); } } } -static bool hasOnlyIncidentalUses(SILInstruction *inst, - bool disallowDebugUses = false) { - for (SILValue result : inst->getResults()) { - for (Operand *use : result->getUses()) { - SILInstruction *user = use->getUser(); - if (!isIncidentalUse(user)) - return false; - if (disallowDebugUses && user->isDebugInstruction()) - return false; - } - } - return true; -} - -void InstructionDeleter::deleteIfDead(SILInstruction *inst) { - if (isInstructionTriviallyDead(inst) || - isScopeAffectingInstructionDead(inst)) { - deleteInstruction( - inst, - /*Fix lifetime of operands*/ inst->getFunction()->hasOwnership()); +bool InstructionDeleter::deleteIfDead(SILInstruction *inst) { + bool fixLifetime = inst->getFunction()->hasOwnership(); + if (isInstructionTriviallyDead(inst) + || isScopeAffectingInstructionDead(inst, fixLifetime)) { + getCallbacks().notifyWillBeDeleted(inst); + deleteWithUses(inst, fixLifetime); + return true; } + return false; } void InstructionDeleter::forceDeleteAndFixLifetimes(SILInstruction *inst) { @@ -519,7 +541,7 @@ void InstructionDeleter::forceDeleteAndFixLifetimes(SILInstruction *inst) { bool disallowDebugUses = fun->getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization; assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); - deleteInstruction(inst, /*Fix lifetime of operands*/ true); + deleteWithUses(inst, /*fixLifetimes*/ true); } void InstructionDeleter::forceDelete(SILInstruction *inst) { @@ -527,15 +549,7 @@ void InstructionDeleter::forceDelete(SILInstruction *inst) { inst->getFunction()->getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization; assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); - deleteInstruction(inst, /*Fix lifetime of operands*/ false); -} - -void InstructionDeleter::forceTrackAsDead(SILInstruction *inst) { - bool disallowDebugUses = - inst->getFunction()->getEffectiveOptimizationMode() <= - OptimizationMode::NoOptimization; - assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); - deadInstructions.insert(inst); + deleteWithUses(inst, /*fixLifetimes*/ false); } void InstructionDeleter::recursivelyDeleteUsersIfDead(SILInstruction *inst) { @@ -560,7 +574,7 @@ void InstructionDeleter::recursivelyForceDeleteUsersAndFixLifetimes( if (isIncidentalUse(inst) || isa(inst)) { forceDelete(inst); return; - } + } forceDeleteAndFixLifetimes(inst); } @@ -568,7 +582,7 @@ void swift::eliminateDeadInstruction(SILInstruction *inst, InstModCallbacks callbacks) { InstructionDeleter deleter(callbacks); deleter.trackIfDead(inst); - deleter.cleanUpDeadInstructions(); + deleter.cleanupDeadInstructions(); } void swift::recursivelyDeleteTriviallyDeadInstructions( @@ -634,37 +648,6 @@ void swift::recursivelyDeleteTriviallyDeadInstructions( recursivelyDeleteTriviallyDeadInstructions(ai, force, callbacks); } -void swift::eraseUsesOfInstruction(SILInstruction *inst, - InstModCallbacks callbacks) { - for (auto result : inst->getResults()) { - while (!result->use_empty()) { - auto ui = result->use_begin(); - auto *user = ui->getUser(); - assert(user && "User should never be NULL!"); - - // If the instruction itself has any uses, recursively zap them so that - // nothing uses this instruction. - eraseUsesOfInstruction(user, callbacks); - - // Walk through the operand list and delete any random instructions that - // will become trivially dead when this instruction is removed. - - for (auto &operand : user->getAllOperands()) { - if (auto *operandI = operand.get()->getDefiningInstruction()) { - // Don't recursively delete the instruction we're working on. - // FIXME: what if we're being recursively invoked? - if (operandI != inst) { - operand.drop(); - recursivelyDeleteTriviallyDeadInstructions(operandI, false, - callbacks); - } - } - } - callbacks.deleteInst(user); - } - } -} - void swift::collectUsesOfValue(SILValue v, llvm::SmallPtrSetImpl &insts) { for (auto ui = v->use_begin(), E = v->use_end(); ui != E; ++ui) { @@ -773,9 +756,14 @@ getConcreteValueOfExistentialBoxAddr(SILValue addr, SILInstruction *ignoreUser) break; } case SILInstructionKind::DeallocStackInst: - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::LoadInst: break; + case SILInstructionKind::DebugValueInst: + if (!DebugValueInst::hasAddrVal(stackUser)) { + if (stackUser != ignoreUser) + return SILValue(); + } + break; case SILInstructionKind::StoreInst: { auto *store = cast(stackUser); assert(store->getSrc() != stackLoc && "cannot store an address"); @@ -2029,25 +2017,30 @@ SILValue swift::makeCopiedValueAvailable(SILValue value, SILBasicBlock *inBlock) auto *copy = builder.createCopyValue( RegularLocation::getAutoGeneratedLocation(), value); - return makeNewValueAvailable(copy, inBlock); + return makeValueAvailable(copy, inBlock); } -SILValue swift::makeNewValueAvailable(SILValue value, SILBasicBlock *inBlock) { +SILValue swift::makeValueAvailable(SILValue value, SILBasicBlock *inBlock) { if (!value->getFunction()->hasOwnership()) return value; if (value.getOwnershipKind() == OwnershipKind::None) return value; - assert(value->getUses().empty() && - value.getOwnershipKind() == OwnershipKind::Owned); + assert(value.getOwnershipKind() == OwnershipKind::Owned); + + SmallVector userBBs; + for (auto use : value->getUses()) { + userBBs.push_back(use->getParentBlock()); + } + userBBs.push_back(inBlock); // Use \p jointPostDomComputer to: // 1. Create a control equivalent copy at \p inBlock if needed // 2. Insert destroy_value at leaking blocks SILValue controlEqCopy; findJointPostDominatingSet( - value->getParentBlock(), inBlock, + value->getParentBlock(), userBBs, [&](SILBasicBlock *loopBlock) { assert(loopBlock == inBlock); auto front = loopBlock->begin(); @@ -2123,3 +2116,20 @@ void swift::endLifetimeAtLeakingBlocks(SILValue value, RegularLocation::getAutoGeneratedLocation(), value); }); } + +void swift::salvageDebugInfo(SILInstruction *I) { + if (!I) + return; + + if (auto *SI = dyn_cast(I)) { + if (SILValue DestVal = SI->getDest()) + // TODO: Generalize this into "get the attached debug info + // on `DestVal`". + if (auto *ASI = dyn_cast_or_null( + DestVal.getDefiningInstruction())) { + if (auto VarInfo = ASI->getVarInfo()) + SILBuilder(SI, ASI->getDebugScope()) + .createDebugValue(SI->getLoc(), SI->getSrc(), *VarInfo); + } + } +} diff --git a/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp b/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp index f91fa2dbf4323..2a9b2a0529778 100644 --- a/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp +++ b/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp @@ -119,7 +119,7 @@ void LSValue::reduceInner(LSLocation &Base, SILModule *M, Builder, RegularLocation::getAutoGeneratedLocation(), Base.getType(M, context).getObjectType(), Vals); - auto AvailVal = makeNewValueAvailable(AI.get(), InsertPt->getParent()); + auto AvailVal = makeValueAvailable(AI.get(), InsertPt->getParent()); // This is the Value for the current base. ProjectionPath P(Base.getType(M, context)); diff --git a/lib/SILOptimizer/Utils/OwnershipOptUtils.cpp b/lib/SILOptimizer/Utils/OwnershipOptUtils.cpp index 9ca096042bb3f..48488d37405f0 100644 --- a/lib/SILOptimizer/Utils/OwnershipOptUtils.cpp +++ b/lib/SILOptimizer/Utils/OwnershipOptUtils.cpp @@ -78,7 +78,8 @@ insertOwnedBaseValueAlongBranchEdge(BranchInst *bi, SILValue innerCopy, // argument. auto *phiArg = destBB->createPhiArgument(innerCopy->getType(), OwnershipKind::Owned); - addNewEdgeValueToBranch(bi, destBB, innerCopy, callbacks); + InstructionDeleter deleter(callbacks); + addNewEdgeValueToBranch(bi, destBB, innerCopy, deleter); // Grab our predecessor blocks, ignoring us, add to the branch edge an // undef corresponding to our value. @@ -93,7 +94,7 @@ insertOwnedBaseValueAlongBranchEdge(BranchInst *bi, SILValue innerCopy, continue; addNewEdgeValueToBranch( predBlock->getTerminator(), destBB, - SILUndef::get(innerCopy->getType(), *destBB->getParent()), callbacks); + SILUndef::get(innerCopy->getType(), *destBB->getParent()), deleter); } return phiArg; @@ -508,7 +509,7 @@ static void eliminateReborrowsOfRecursiveBorrows( // Otherwise, we have a reborrow. For now our reborrows must be // phis. Add our owned value as a new argument of that phi along our // edge and undef along all other edges. - auto borrowingOp = BorrowingOperand::get(use); + auto borrowingOp = BorrowingOperand(use); auto *brInst = cast(borrowingOp.op->getUser()); auto *newBorrowedPhi = brInst->getArgForOperand(*borrowingOp); auto *newBasePhi = @@ -572,7 +573,7 @@ rewriteReborrows(SILValue newBorrowedValue, // Otherwise, we have a reborrow. For now our reborrows must be // phis. Add our owned value as a new argument of that phi along our // edge and undef along all other edges. - auto borrowingOp = BorrowingOperand::get(use); + auto borrowingOp = BorrowingOperand(use); auto *brInst = cast(borrowingOp.op->getUser()); auto *newBorrowedPhi = brInst->getArgForOperand(*borrowingOp); auto *newBasePhi = @@ -858,7 +859,7 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx, // Otherwise, lets check if we can perform this RAUW operation. If we can't, // set ctx to nullptr to invalidate the helper and return. if (!canFixUpOwnershipForRAUW(oldValue, newValue, inputCtx)) { - ctx = nullptr; + invalidate(); return; } @@ -902,43 +903,31 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx, // // NOTE: We also need to handle this here since a pointer_to_address is not a // valid base value for an access path since it doesn't refer to any storage. - { - auto baseProj = - getUnderlyingObjectStoppingAtObjectToAddrProjections(newValue); - if (isa(baseProj)) { - return; - } - } - - auto accessPathWithBase = AccessPathWithBase::compute(newValue); - if (!accessPathWithBase.base) { - // Invalidate! - ctx = nullptr; + BorrowedAddress borrowedAddress(newValue); + if (!borrowedAddress.mayBeBorrowed) return; - } - auto &intPtr = ctx->extraAddressFixupInfo.intPtrOp; - intPtr = InteriorPointerOperand::inferFromResult(accessPathWithBase.base); - if (!intPtr) { - // We can optimize! Do not invalidate! + if (!borrowedAddress.interiorPointerOp) { + invalidate(); return; } - auto borrowedValue = intPtr.getSingleBaseValue(); + ctx->extraAddressFixupInfo.intPtrOp = borrowedAddress.interiorPointerOp; + auto borrowedValue = borrowedAddress.interiorPointerOp.getSingleBaseValue(); if (!borrowedValue) { - // Invalidate! - ctx = nullptr; + invalidate(); return; } - // This cloner check must match the later cloner invocation in - // replaceAddressUses() - auto *intPtrUser = cast(intPtr->getUser()); + auto *intPtrInst = + cast(borrowedAddress.interiorPointerOp.getUser()); auto checkBase = [&](SILValue srcAddr) { - return (srcAddr == intPtrUser) ? SILValue(intPtrUser) : SILValue(); + return (srcAddr == intPtrInst) ? SILValue(intPtrInst) : SILValue(); }; + // This cloner check must match the later cloner invocation in + // replaceAddressUses() if (!canCloneUseDefChain(newValue, checkBase)) { - ctx = nullptr; + invalidate(); return; } @@ -946,8 +935,7 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx, auto &oldValueUses = ctx->extraAddressFixupInfo.allAddressUsesFromOldValue; if (InteriorPointerOperand::findTransitiveUsesForAddress(oldValue, oldValueUses)) { - // If we found an error, invalidate and return! - ctx = nullptr; + invalidate(); return; } @@ -1192,14 +1180,14 @@ OwnershipReplaceSingleUseHelper::OwnershipReplaceSingleUseHelper( // If we have an address, bail. We don't support this. if (newValue->getType().isAddress()) { - ctx = nullptr; + invalidate(); return; } // Otherwise, lets check if we can perform this RAUW operation. If we can't, // set ctx to nullptr to invalidate the helper and return. if (!hasValidRAUWOwnership(use->get(), newValue)) { - ctx = nullptr; + invalidate(); return; } diff --git a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp index f0bba8d993a44..cb09de5975d43 100644 --- a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp +++ b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "swift/SILOptimizer/Analysis/ArraySemantic.h" +#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" #include "swift/SILOptimizer/Utils/PerformanceInlinerUtils.h" #include "swift/AST/Module.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" @@ -614,6 +616,7 @@ SemanticFunctionLevel swift::getSemanticFunctionLevel(SILFunction *function) { return SemanticFunctionLevel::Transient; } // end switch + llvm_unreachable("covered switch"); } /// Return true if \p apply calls into an optimizable semantic function from diff --git a/lib/SILOptimizer/Utils/PrunedLiveness.cpp b/lib/SILOptimizer/Utils/PrunedLiveness.cpp index 62b0a967feda7..393ad7f680364 100644 --- a/lib/SILOptimizer/Utils/PrunedLiveness.cpp +++ b/lib/SILOptimizer/Utils/PrunedLiveness.cpp @@ -66,6 +66,7 @@ PrunedLiveBlocks::IsLive PrunedLiveBlocks::updateForUse(SILInstruction *user) { return getBlockLiveness(bb); } } + llvm_unreachable("covered switch"); } //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index 8aeb69d5d9a38..db61a48e40034 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -286,7 +286,6 @@ class SILInlineCloner SILValue borrowFunctionArgument(SILValue callArg, FullApplySite AI); void visitDebugValueInst(DebugValueInst *Inst); - void visitDebugValueAddrInst(DebugValueAddrInst *Inst); void visitHopToExecutorInst(HopToExecutorInst *Inst); void visitTerminator(SILBasicBlock *BB); @@ -348,6 +347,9 @@ SILInliner::inlineFullApply(FullApplySite apply, SILInliner::InlineKind inlineKind, SILOptFunctionBuilder &funcBuilder) { assert(apply.canOptimize()); + assert(!apply.getFunction()->hasOwnership() || + apply.getReferencedFunctionOrNull()->hasOwnership()); + SmallVector appliedArgs; for (const auto arg : apply.getArguments()) appliedArgs.push_back(arg); @@ -575,15 +577,21 @@ void SILInlineCloner::fixUp(SILFunction *calleeFunction) { assert(!Apply.getInstruction()->hasUsesOfAnyResult()); - auto callbacks = InstModCallbacks().onNotifyWillBeDeleted( - [this](SILInstruction *deletedI) { - if (NextIter == deletedI->getIterator()) - ++NextIter; - if (DeletionCallback) - DeletionCallback(deletedI); - }); - recursivelyDeleteTriviallyDeadInstructions(Apply.getInstruction(), true, - callbacks); + auto callbacks = InstModCallbacks().onDelete([&](SILInstruction *deadInst) { + if (NextIter == deadInst->getIterator()) + ++NextIter; + deadInst->eraseFromParent(); + }); + if (DeletionCallback) { + callbacks = + callbacks.onNotifyWillBeDeleted([this](SILInstruction *toDeleteInst) { + DeletionCallback(toDeleteInst); + }); + } + InstructionDeleter deleter(callbacks); + callbacks.notifyWillBeDeleted(Apply.getInstruction()); + deleter.forceDelete(Apply.getInstruction()); + deleter.cleanupDeadInstructions(); } SILValue SILInlineCloner::borrowFunctionArgument(SILValue callArg, @@ -604,13 +612,6 @@ void SILInlineCloner::visitDebugValueInst(DebugValueInst *Inst) { return SILCloner::visitDebugValueInst(Inst); } -void SILInlineCloner::visitDebugValueAddrInst(DebugValueAddrInst *Inst) { - // The mandatory inliner drops debug_value_addr instructions when inlining, as - // if it were a "nodebug" function in C. - if (IKind == InlineKind::MandatoryInline) return; - - return SILCloner::visitDebugValueAddrInst(Inst); -} void SILInlineCloner::visitHopToExecutorInst(HopToExecutorInst *Inst) { // Drop hop_to_executor in non async functions. if (!Apply.getFunction()->isAsync()) { @@ -672,7 +673,6 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::IntegerLiteralInst: case SILInstructionKind::FloatLiteralInst: case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::StringLiteralInst: case SILInstructionKind::FixLifetimeInst: case SILInstructionKind::EndBorrowInst: diff --git a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp index a118e9c16bb07..cbeefe65b8ca6 100644 --- a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp +++ b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp @@ -230,9 +230,10 @@ SILValue SILSSAUpdater::getValueInMiddleOfBlock(SILBasicBlock *block) { // Create a new phi node. SILPhiArgument *phiArg = block->createPhiArgument(type, OwnershipKind::Owned); - for (auto &pair : predVals) - addNewEdgeValueToBranch(pair.first->getTerminator(), block, pair.second); - + for (auto &pair : predVals) { + addNewEdgeValueToBranch(pair.first->getTerminator(), block, pair.second, + deleter); + } if (insertedPhis) insertedPhis->push_back(phiArg); @@ -326,7 +327,8 @@ class SSAUpdaterTraits { for (auto *predBlock : predBlockList) { TermInst *ti = predBlock->getTerminator(); - addNewEdgeValueToBranch(ti, block, ssaUpdater->phiSentinel.get()); + addNewEdgeValueToBranch(ti, block, ssaUpdater->phiSentinel.get(), + ssaUpdater->deleter); } return phi; diff --git a/lib/SILOptimizer/Utils/SpecializationMangler.cpp b/lib/SILOptimizer/Utils/SpecializationMangler.cpp index 2d67dbdd49a70..0517e2299107f 100644 --- a/lib/SILOptimizer/Utils/SpecializationMangler.cpp +++ b/lib/SILOptimizer/Utils/SpecializationMangler.cpp @@ -26,7 +26,7 @@ using namespace Mangle; std::string PartialSpecializationMangler::mangle() { beginMangling(); - appendType(SpecializedFnTy); + appendType(SpecializedFnTy, nullptr); appendSpecializationOperator(isReAbstracted ? "Tp" : "TP"); return finalize(); } @@ -193,7 +193,7 @@ FunctionSignatureSpecializationMangler::mangleClosureProp(SILInstruction *Inst) // specializing. for (auto &Op : PAI->getArgumentOperands()) { SILType Ty = Op.get()->getType(); - appendType(Ty.getASTType()); + appendType(Ty.getASTType(), nullptr); } } diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index d0e2956337f7c..8660d22bd17a2 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -205,9 +205,8 @@ class BuilderClosureVisitor } public: - BuilderClosureVisitor(ASTContext &ctx, ConstraintSystem *cs, - DeclContext *dc, Type builderType, - Type bodyResultType) + BuilderClosureVisitor(ASTContext &ctx, ConstraintSystem *cs, DeclContext *dc, + Type builderType, Type bodyResultType) : cs(cs), dc(dc), ctx(ctx), builderType(builderType) { builder = builderType->getAnyNominal(); applied.builderType = builderType; @@ -238,9 +237,8 @@ class BuilderClosureVisitor { applied.returnExpr }, { Identifier() }); } - applied.returnExpr = cs->buildTypeErasedExpr(applied.returnExpr, - dc, applied.bodyResultType, - CTP_ReturnStmt); + applied.returnExpr = cs->buildTypeErasedExpr( + applied.returnExpr, dc, applied.bodyResultType, CTP_ReturnStmt); applied.returnExpr = cs->generateConstraints(applied.returnExpr, dc); if (!applied.returnExpr) { @@ -502,15 +500,19 @@ class BuilderClosureVisitor if (!cs || !thenVar || (elseChainVar && !*elseChainVar)) return nullptr; - // If there is a #available in the condition, the 'then' will need to - // be wrapped in a call to buildLimitedAvailability(_:), if available. Expr *thenVarRefExpr = buildVarRef( thenVar, ifStmt->getThenStmt()->getEndLoc()); - if (findAvailabilityCondition(ifStmt->getCond()) && - builderSupports(ctx.Id_buildLimitedAvailability)) { - thenVarRefExpr = buildCallIfWanted( - ifStmt->getThenStmt()->getEndLoc(), ctx.Id_buildLimitedAvailability, - { thenVarRefExpr }, { Identifier() }); + + // If there is a #available in the condition, wrap the 'then' in a call to + // buildLimitedAvailability(_:). + auto availabilityCond = findAvailabilityCondition(ifStmt->getCond()); + bool supportsAvailability = + availabilityCond && builderSupports(ctx.Id_buildLimitedAvailability); + if (supportsAvailability && + !availabilityCond->getAvailability()->isUnavailability()) { + thenVarRefExpr = buildCallIfWanted(ifStmt->getThenStmt()->getEndLoc(), + ctx.Id_buildLimitedAvailability, + {thenVarRefExpr}, {Identifier()}); } // Prepare the `then` operand by wrapping it to produce a chain result. @@ -533,12 +535,22 @@ class BuilderClosureVisitor elseExpr = buildVarRef(*elseChainVar, ifStmt->getEndLoc()); elseLoc = ifStmt->getElseLoc(); - // - Otherwise, wrap it to produce a chain result. + // - Otherwise, wrap it to produce a chain result. } else { + Expr *elseVarRefExpr = buildVarRef(*elseChainVar, ifStmt->getEndLoc()); + + // If there is a #unavailable in the condition, wrap the 'else' in a call + // to buildLimitedAvailability(_:). + if (supportsAvailability && + availabilityCond->getAvailability()->isUnavailability()) { + elseVarRefExpr = buildCallIfWanted(ifStmt->getEndLoc(), + ctx.Id_buildLimitedAvailability, + {elseVarRefExpr}, {Identifier()}); + } + + elseExpr = buildWrappedChainPayload(elseVarRefExpr, payloadIndex + 1, + numPayloads, isOptional); elseLoc = ifStmt->getElseLoc(); - elseExpr = buildWrappedChainPayload( - buildVarRef(*elseChainVar, ifStmt->getEndLoc()), - payloadIndex + 1, numPayloads, isOptional); } // The operand should have optional type if we had optional results, @@ -963,7 +975,8 @@ struct ResultBuilderTarget { /// Handles the rewrite of the body of a closure to which a result builder /// has been applied. class BuilderClosureRewriter - : public StmtVisitor { + : public StmtVisitor, + ResultBuilderTarget> { ASTContext &ctx; const Solution &solution; DeclContext *dc; @@ -1030,12 +1043,12 @@ class BuilderClosureRewriter switch (target.kind) { case ResultBuilderTarget::ReturnValue: { // Return the expression. - Type bodyResultType = + Type bodyResultInterfaceType = solution.simplifyType(builderTransform.bodyResultType); - SolutionApplicationTarget returnTarget( - capturedExpr, dc, CTP_ReturnStmt, bodyResultType, - /*isDiscarded=*/false); + SolutionApplicationTarget returnTarget(capturedExpr, dc, CTP_ReturnStmt, + bodyResultInterfaceType, + /*isDiscarded=*/false); Expr *resultExpr = nullptr; if (auto resultTarget = rewriteTarget(returnTarget)) resultExpr = resultTarget->getAsExpr(); @@ -1124,8 +1137,9 @@ class BuilderClosureRewriter solution(solution), dc(dc), builderTransform(builderTransform), rewriteTarget(rewriteTarget) { } - Stmt *visitBraceStmt(BraceStmt *braceStmt, ResultBuilderTarget target, - Optional innerTarget = None) { + NullablePtr + visitBraceStmt(BraceStmt *braceStmt, ResultBuilderTarget target, + Optional innerTarget = None) { std::vector newElements; // If there is an "inner" target corresponding to this brace, declare @@ -1165,7 +1179,7 @@ class BuilderClosureRewriter // "throw" statements produce no value. Transform them directly. if (auto throwStmt = dyn_cast(stmt)) { if (auto newStmt = visitThrowStmt(throwStmt)) { - newElements.push_back(stmt); + newElements.push_back(newStmt.get()); } continue; } @@ -1176,7 +1190,7 @@ class BuilderClosureRewriter declareTemporaryVariable(captured.first, newElements); - Stmt *finalStmt = visit( + auto finalStmt = visit( stmt, ResultBuilderTarget{ResultBuilderTarget::TemporaryVar, std::move(captured)}); @@ -1186,7 +1200,7 @@ class BuilderClosureRewriter if (!finalStmt) return nullptr; - newElements.push_back(finalStmt); + newElements.push_back(finalStmt.get()); continue; } @@ -1236,7 +1250,7 @@ class BuilderClosureRewriter braceStmt->getRBraceLoc()); } - Stmt *visitIfStmt(IfStmt *ifStmt, ResultBuilderTarget target) { + NullablePtr visitIfStmt(IfStmt *ifStmt, ResultBuilderTarget target) { // Rewrite the condition. if (auto condition = rewriteTarget( SolutionApplicationTarget(ifStmt->getCond(), dc))) @@ -1252,7 +1266,10 @@ class BuilderClosureRewriter temporaryVar, {target.captured.second[0]}), ResultBuilderTarget::forAssign( capturedThen.first, {capturedThen.second.front()})); - ifStmt->setThenStmt(newThen); + if (!newThen) + return nullptr; + + ifStmt->setThenStmt(newThen.get()); // Look for a #available condition. If there is one, we need to check // that the resulting type of the "then" doesn't refer to any types that @@ -1265,9 +1282,18 @@ class BuilderClosureRewriter // this warning to an error. if (auto availabilityCond = findAvailabilityCondition(ifStmt->getCond())) { SourceLoc loc = availabilityCond->getStartLoc(); - Type thenBodyType = solution.simplifyType( + Type bodyType; + if (availabilityCond->getAvailability()->isUnavailability()) { + // For #unavailable, we need to check the "else". + Type elseBodyType = solution.simplifyType( + solution.getType(target.captured.second[1])); + bodyType = elseBodyType; + } else { + Type thenBodyType = solution.simplifyType( solution.getType(target.captured.second[0])); - thenBodyType.findIf([&](Type type) { + bodyType = thenBodyType; + } + bodyType.findIf([&](Type type) { auto nominal = type->getAnyNominal(); if (!nominal) return false; @@ -1315,23 +1341,28 @@ class BuilderClosureRewriter dyn_cast_or_null(ifStmt->getElseStmt())) { // Translate the "else" branch when it's a stmt-brace. auto capturedElse = takeCapturedStmt(elseBraceStmt); - Stmt *newElse = visitBraceStmt( + auto newElse = visitBraceStmt( elseBraceStmt, ResultBuilderTarget::forAssign( temporaryVar, {target.captured.second[1]}), ResultBuilderTarget::forAssign( capturedElse.first, {capturedElse.second.front()})); - ifStmt->setElseStmt(newElse); + if (!newElse) + return nullptr; + + ifStmt->setElseStmt(newElse.get()); } else if (auto elseIfStmt = cast_or_null(ifStmt->getElseStmt())){ // Translate the "else" branch when it's an else-if. auto capturedElse = takeCapturedStmt(elseIfStmt); std::vector newElseElements; declareTemporaryVariable(capturedElse.first, newElseElements); - newElseElements.push_back( - visitIfStmt( - elseIfStmt, - ResultBuilderTarget::forAssign( - capturedElse.first, capturedElse.second))); + auto newElseElt = + visitIfStmt(elseIfStmt, ResultBuilderTarget::forAssign( + capturedElse.first, capturedElse.second)); + if (!newElseElt) + return nullptr; + + newElseElements.push_back(newElseElt.get()); newElseElements.push_back( initializeTarget( ResultBuilderTarget::forAssign( @@ -1355,23 +1386,25 @@ class BuilderClosureRewriter return ifStmt; } - Stmt *visitDoStmt(DoStmt *doStmt, ResultBuilderTarget target) { + NullablePtr visitDoStmt(DoStmt *doStmt, ResultBuilderTarget target) { // Each statement turns into a (potential) temporary variable // binding followed by the statement itself. auto body = cast(doStmt->getBody()); auto captured = takeCapturedStmt(body); - auto newInnerBody = cast( - visitBraceStmt( - body, - target, - ResultBuilderTarget::forAssign( - captured.first, {captured.second.front()}))); - doStmt->setBody(newInnerBody); + auto newInnerBody = + visitBraceStmt(body, target, + ResultBuilderTarget::forAssign( + captured.first, {captured.second.front()})); + if (!newInnerBody) + return nullptr; + + doStmt->setBody(cast(newInnerBody.get())); return doStmt; } - Stmt *visitSwitchStmt(SwitchStmt *switchStmt, ResultBuilderTarget target) { + NullablePtr visitSwitchStmt(SwitchStmt *switchStmt, + ResultBuilderTarget target) { // Translate the subject expression. ConstraintSystem &cs = solution.getConstraintSystem(); auto subjectTarget = @@ -1416,7 +1449,8 @@ class BuilderClosureRewriter return switchStmt; } - Stmt *visitCaseStmt(CaseStmt *caseStmt, ResultBuilderTarget target) { + NullablePtr visitCaseStmt(CaseStmt *caseStmt, + ResultBuilderTarget target) { // Translate the patterns and guard expressions for each case label item. for (auto &caseLabelItem : caseStmt->getMutableCaseLabelItems()) { SolutionApplicationTarget caseLabelTarget(&caseLabelItem, dc); @@ -1427,19 +1461,19 @@ class BuilderClosureRewriter // Transform the body of the case. auto body = cast(caseStmt->getBody()); auto captured = takeCapturedStmt(body); - auto newInnerBody = cast( - visitBraceStmt( - body, - target, - ResultBuilderTarget::forAssign( - captured.first, {captured.second.front()}))); - caseStmt->setBody(newInnerBody); + auto newInnerBody = + visitBraceStmt(body, target, + ResultBuilderTarget::forAssign( + captured.first, {captured.second.front()})); + if (!newInnerBody) + return nullptr; + caseStmt->setBody(cast(newInnerBody.get())); return caseStmt; } - Stmt *visitForEachStmt( - ForEachStmt *forEachStmt, ResultBuilderTarget target) { + NullablePtr visitForEachStmt(ForEachStmt *forEachStmt, + ResultBuilderTarget target) { // Translate the for-each loop header. ConstraintSystem &cs = solution.getConstraintSystem(); auto forEachTarget = @@ -1469,13 +1503,14 @@ class BuilderClosureRewriter // will append the result of executing the loop body to the array. auto body = forEachStmt->getBody(); auto capturedBody = takeCapturedStmt(body); - auto newBody = cast( - visitBraceStmt( - body, - ResultBuilderTarget::forExpression(arrayAppendCall), - ResultBuilderTarget::forAssign( - capturedBody.first, {capturedBody.second.front()}))); - forEachStmt->setBody(newBody); + auto newBody = visitBraceStmt( + body, ResultBuilderTarget::forExpression(arrayAppendCall), + ResultBuilderTarget::forAssign(capturedBody.first, + {capturedBody.second.front()})); + if (!newBody) + return nullptr; + + forEachStmt->setBody(cast(newBody.get())); outerBodySteps.push_back(forEachStmt); // Step 3. Perform the buildArray() call to turn the array of results @@ -1487,11 +1522,11 @@ class BuilderClosureRewriter // Form a brace statement to put together the three main steps for the // for-each loop translation outlined above. - return BraceStmt::create( - ctx, forEachStmt->getStartLoc(), outerBodySteps, newBody->getEndLoc()); + return BraceStmt::create(ctx, forEachStmt->getStartLoc(), outerBodySteps, + newBody.get()->getEndLoc()); } - Stmt *visitThrowStmt(ThrowStmt *throwStmt) { + NullablePtr visitThrowStmt(ThrowStmt *throwStmt) { // Rewrite the error. auto target = *solution.getConstraintSystem() .getSolutionApplicationTarget(throwStmt); @@ -1503,12 +1538,14 @@ class BuilderClosureRewriter return throwStmt; } - Stmt *visitThrowStmt(ThrowStmt *throwStmt, ResultBuilderTarget target) { + NullablePtr visitThrowStmt(ThrowStmt *throwStmt, + ResultBuilderTarget target) { llvm_unreachable("Throw statements produce no value"); } #define UNHANDLED_RESULT_BUILDER_STMT(STMT) \ - Stmt *visit##STMT##Stmt(STMT##Stmt *stmt, ResultBuilderTarget target) { \ + NullablePtr \ + visit##STMT##Stmt(STMT##Stmt *stmt, ResultBuilderTarget target) { \ llvm_unreachable("Function builders do not allow statement of kind " \ #STMT); \ } @@ -1540,12 +1577,12 @@ BraceStmt *swift::applyResultBuilderTransform( rewriteTarget) { BuilderClosureRewriter rewriter(solution, dc, applied, rewriteTarget); auto captured = rewriter.takeCapturedStmt(body); - return cast_or_null( - rewriter.visitBraceStmt( - body, - ResultBuilderTarget::forReturn(applied.returnExpr), - ResultBuilderTarget::forAssign( - captured.first, captured.second))); + auto result = rewriter.visitBraceStmt( + body, ResultBuilderTarget::forReturn(applied.returnExpr), + ResultBuilderTarget::forAssign(captured.first, captured.second)); + if (!result) + return nullptr; + return cast(result.get()); } Optional TypeChecker::applyResultBuilderBodyTransform( @@ -1587,10 +1624,7 @@ Optional TypeChecker::applyResultBuilderBodyTransform( } if (attr) { - ctx.Diags.diagnose( - attr->getLocation(), diag::result_builder_remove_attr) - .fixItRemove(attr->getRangeWithAt()); - attr->setInvalid(); + diagnoseAndRemoveAttr(func, attr, diag::result_builder_remove_attr); } // Note that one can remove all of the return statements. @@ -1616,7 +1650,7 @@ Optional TypeChecker::applyResultBuilderBodyTransform( ConstraintKind resultConstraintKind = ConstraintKind::Conversion; if (auto opaque = resultContextType->getAs()) { if (opaque->getDecl()->isOpaqueReturnTypeOfFunction(func)) { - resultConstraintKind = ConstraintKind::OpaqueUnderlyingType; + resultConstraintKind = ConstraintKind::Equal; } } @@ -1682,25 +1716,24 @@ Optional TypeChecker::applyResultBuilderBodyTransform( } Optional -ConstraintSystem::matchResultBuilder( - AnyFunctionRef fn, Type builderType, Type bodyResultType, - ConstraintKind bodyResultConstraintKind, - ConstraintLocatorBuilder locator) { +ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType, + Type bodyResultType, + ConstraintKind bodyResultConstraintKind, + ConstraintLocatorBuilder locator) { auto builder = builderType->getAnyNominal(); assert(builder && "Bad result builder type"); assert(builder->getAttrs().hasAttribute()); if (InvalidResultBuilderBodies.count(fn)) { - (void)recordFix( - IgnoreInvalidResultBuilderBody::duringConstraintGeneration( - *this, getConstraintLocator(fn.getAbstractClosureExpr()))); + (void)recordFix(IgnoreInvalidResultBuilderBody::create( + *this, getConstraintLocator(fn.getAbstractClosureExpr()))); return getTypeMatchSuccess(); } // Pre-check the body: pre-check any expressions in it and look // for return statements. auto request = - PreCheckResultBuilderRequest{{fn, /*SuppressDiagnostics=*/true}}; + PreCheckResultBuilderRequest{{fn, /*SuppressDiagnostics=*/false}}; switch (evaluateOrDefault(getASTContext().evaluator, request, ResultBuilderBodyPreCheck::Error)) { case ResultBuilderBodyPreCheck::Okay: @@ -1708,10 +1741,12 @@ ConstraintSystem::matchResultBuilder( break; case ResultBuilderBodyPreCheck::Error: { + InvalidResultBuilderBodies.insert(fn); + if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); - if (recordFix(IgnoreInvalidResultBuilderBody::duringPreCheck( + if (recordFix(IgnoreInvalidResultBuilderBody::create( *this, getConstraintLocator(fn.getAbstractClosureExpr())))) return getTypeMatchFailure(locator); @@ -1776,9 +1811,8 @@ ConstraintSystem::matchResultBuilder( if (transaction.hasErrors()) { InvalidResultBuilderBodies.insert(fn); - if (recordFix( - IgnoreInvalidResultBuilderBody::duringConstraintGeneration( - *this, getConstraintLocator(fn.getAbstractClosureExpr())))) + if (recordFix(IgnoreInvalidResultBuilderBody::create( + *this, getConstraintLocator(fn.getAbstractClosureExpr())))) return getTypeMatchFailure(locator); return getTypeMatchSuccess(); @@ -1810,7 +1844,8 @@ ConstraintSystem::matchResultBuilder( } // Bind the body result type to the type of the transformed expression. - addConstraint(bodyResultConstraintKind, transformedType, bodyResultType, + addConstraint(bodyResultConstraintKind, transformedType, + openOpaqueType(bodyResultType, CTP_ReturnStmt, locator), locator); return getTypeMatchSuccess(); } @@ -1869,15 +1904,11 @@ class PreCheckResultBuilderApplication : public ASTWalker { DiagnosticTransaction transaction(diagEngine); HasError |= ConstraintSystem::preCheckExpression( - E, DC, /*replaceInvalidRefsWithErrors=*/false); + E, DC, /*replaceInvalidRefsWithErrors=*/true); HasError |= transaction.hasErrors(); - if (!HasError) { - E->forEachChildExpr([&](Expr *expr) { - HasError |= isa(expr); - return HasError ? nullptr : expr; - }); - } + if (!HasError) + HasError |= containsErrorExpr(E); if (SuppressDiagnostics) transaction.abort(); @@ -1899,6 +1930,29 @@ class PreCheckResultBuilderApplication : public ASTWalker { return std::make_pair(true, S); } + /// Check whether given expression (including single-statement + /// closures) contains `ErrorExpr` as one of its sub-expressions. + bool containsErrorExpr(Expr *expr) { + bool hasError = false; + + expr->forEachChildExpr([&](Expr *expr) -> Expr * { + hasError |= isa(expr); + if (hasError) + return nullptr; + + if (auto *closure = dyn_cast(expr)) { + if (shouldTypeCheckInEnclosingExpression(closure)) { + hasError |= containsErrorExpr(closure->getSingleExpressionBody()); + return hasError ? nullptr : expr; + } + } + + return expr; + }); + + return hasError; + } + /// Ignore patterns. std::pair walkToPatternPre(Pattern *pat) override { return { false, pat }; @@ -1909,13 +1963,6 @@ class PreCheckResultBuilderApplication : public ASTWalker { ResultBuilderBodyPreCheck PreCheckResultBuilderRequest::evaluate( Evaluator &evaluator, PreCheckResultBuilderDescriptor owner) const { - // We don't want to do the precheck if it will already have happened in - // the enclosing expression. - bool skipPrecheck = false; - if (auto closure = dyn_cast_or_null( - owner.Fn.getAbstractClosureExpr())) - skipPrecheck = shouldTypeCheckInEnclosingExpression(closure); - return PreCheckResultBuilderApplication( owner.Fn, /*skipPrecheck=*/false, /*suppressDiagnostics=*/owner.SuppressDiagnostics) diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index e71cb7ff910bc..3eb6f1e1bbeb2 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftSema STATIC BuilderTransform.cpp CSApply.cpp @@ -12,6 +12,7 @@ add_swift_host_library(swiftSema STATIC CSFix.cpp CSDiagnostics.cpp CodeSynthesis.cpp + CodeSynthesisDistributedActor.cpp ConstantnessSemaDiagnostics.cpp Constraint.cpp ConstraintGraph.cpp @@ -19,6 +20,7 @@ add_swift_host_library(swiftSema STATIC ConstraintSystem.cpp DebuggerTestingTransform.cpp DerivedConformanceActor.cpp + DerivedConformanceDistributedActor.cpp DerivedConformanceAdditiveArithmetic.cpp DerivedConformanceCaseIterable.cpp DerivedConformanceCodable.cpp @@ -45,6 +47,7 @@ add_swift_host_library(swiftSema STATIC TypeCheckCircularity.cpp TypeCheckCodeCompletion.cpp TypeCheckConcurrency.cpp + TypeCheckDistributed.cpp TypeCheckConstraints.cpp TypeCheckDecl.cpp TypeCheckDeclObjC.cpp @@ -77,3 +80,5 @@ target_link_libraries(swiftSema PRIVATE swiftAST swiftParse swiftSerialization) + +set_swift_llvm_is_available(swiftSema) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 05e4a2a9e4065..ba8d844b062e7 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -101,7 +101,7 @@ Solution::computeSubstitutions(GenericSignature sig, // FIXME: Retrieve the conformance from the solution itself. return TypeChecker::conformsToProtocol(replacement, protoType, - getConstraintSystem().DC); + getConstraintSystem().DC->getParentModule()); }; return SubstitutionMap::get(sig, @@ -202,8 +202,7 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl, // If this is a C++ function template, get it's specialization for the given // substitution map and update the decl accordingly. - if (decl->getClangDecl() && - isa(decl->getClangDecl())) { + if (isa_and_nonnull(decl->getClangDecl())) { auto *newFn = decl->getASTContext() .getClangModuleLoader() @@ -377,6 +376,7 @@ static bool buildObjCKeyPathString(KeyPathExpr *E, case KeyPathExpr::Component::Kind::Invalid: case KeyPathExpr::Component::Kind::UnresolvedProperty: case KeyPathExpr::Component::Kind::UnresolvedSubscript: + case KeyPathExpr::Component::Kind::CodeCompletion: // Don't bother building the key path string if the key path didn't even // resolve. return false; @@ -546,7 +546,7 @@ namespace { } auto sig = gft->getGenericSignature(); - auto *env = sig->getGenericEnvironment(); + auto *env = sig.getGenericEnvironment(); witnessType = FunctionType::get(gft->getParams(), gft->getResult(), @@ -620,7 +620,7 @@ namespace { // the protocol requirement with Self == the concrete type, and SILGen // (or later) can devirtualize as appropriate. auto conformance = - TypeChecker::conformsToProtocol(baseTy, proto, cs.DC); + TypeChecker::conformsToProtocol(baseTy, proto, cs.DC->getParentModule()); if (conformance.isConcrete()) { if (auto witness = conformance.getConcrete()->getWitnessDecl(decl)) { bool isMemberOperator = witness->getDeclContext()->isTypeContext(); @@ -654,8 +654,8 @@ namespace { ctx); cs.setType(base, MetatypeType::get(baseTy)); - refExpr = new (ctx) DotSyntaxCallExpr(declRefExpr, - SourceLoc(), base); + refExpr = DotSyntaxCallExpr::create(ctx, declRefExpr, + SourceLoc(), base); auto refType = fullType->castTo()->getResult(); cs.setType(refExpr, refType); } else { @@ -1066,7 +1066,7 @@ namespace { outerParamTypes.push_back(AnyFunctionType::Param(outerParamType, Identifier(), paramInfo[i].getParameterFlags())); - outerParam->setInterfaceType(outerParamType); + outerParam->setInterfaceType(outerParamType->mapTypeOutOfContext()); if (fnDecl.getAbstractFunctionDecl()) argLabels.push_back(innerParam->getArgumentName()); @@ -1091,8 +1091,8 @@ namespace { FunctionType::ExtInfo closureInfo; auto *autoClosureType = FunctionType::get(outerParamTypes, fnType->getResult(), closureInfo); - auto *autoClosure = new (context) AutoClosureExpr(fnCall, autoClosureType, - discriminator, cs.DC); + auto *autoClosure = new (context) + AutoClosureExpr(fnCall, autoClosureType, discriminator, dc); autoClosure->setParameterList(outerParams); autoClosure->setThunkKind(AutoClosureExpr::Kind::SingleCurryThunk); cs.cacheType(autoClosure); @@ -1125,9 +1125,8 @@ namespace { auto resultTy = selfFnTy->getResult(); auto discriminator = AutoClosureExpr::InvalidDiscriminator; - auto closure = - new (context) AutoClosureExpr(/*set body later*/nullptr, resultTy, - discriminator, cs.DC); + auto closure = new (context) AutoClosureExpr(/*set body later*/ nullptr, + resultTy, discriminator, dc); closure->setParameterList(params); closure->setType(selfFnTy); closure->setThunkKind(AutoClosureExpr::Kind::SingleCurryThunk); @@ -1175,8 +1174,8 @@ namespace { } // (Self) -> ... - ApplyExpr *selfCall = new (context) DotSyntaxCallExpr( - ref, SourceLoc(), selfOpenedRef); + ApplyExpr *selfCall = + DotSyntaxCallExpr::create(context, ref, SourceLoc(), selfOpenedRef); selfCall->setType(refTy->getResult()); cs.cacheType(selfCall); @@ -1254,7 +1253,7 @@ namespace { /*implicit=*/true); } - auto argTy = AnyFunctionType::composeInput(context, calleeParams, + auto argTy = AnyFunctionType::composeTuple(context, calleeParams, /*canonical*/false); closureArg->setType(argTy); cs.cacheType(closureArg); @@ -1692,8 +1691,7 @@ namespace { memberLocator); auto outerClosure = - new (context) AutoClosureExpr(closure, selfFnTy, - discriminator, cs.DC); + new (context) AutoClosureExpr(closure, selfFnTy, discriminator, dc); outerClosure->setThunkKind(AutoClosureExpr::Kind::DoubleCurryThunk); outerClosure->setParameterList(outerParams); @@ -1744,7 +1742,7 @@ namespace { if (isa(member)) { // FIXME: Provide type annotation. ref = forceUnwrapIfExpected(ref, choice, memberLocator); - apply = new (context) ConstructorRefCallExpr(ref, base); + apply = ConstructorRefCallExpr::create(context, ref, base); } else if (isUnboundInstanceMember) { auto refType = cs.simplifyType(openedType); if (!cs.getType(ref)->isEqual(refType)) { @@ -1763,7 +1761,7 @@ namespace { assert((!baseIsInstance || member->isInstanceMember()) && "can't call a static method on an instance"); ref = forceUnwrapIfExpected(ref, choice, memberLocator); - apply = new (context) DotSyntaxCallExpr(ref, dotLoc, base); + apply = DotSyntaxCallExpr::create(context, ref, dotLoc, base); if (Implicit) { apply->setImplicit(); } @@ -2418,7 +2416,7 @@ namespace { auto bridgedToObjectiveCConformance = TypeChecker::conformsToProtocol(valueType, bridgedProto, - cs.DC); + cs.DC->getParentModule()); FuncDecl *fn = nullptr; @@ -2451,7 +2449,7 @@ namespace { auto subMap = SubstitutionMap::get( genericSig, [&](SubstitutableType *type) -> Type { - assert(type->isEqual(genericSig->getGenericParams()[0])); + assert(type->isEqual(genericSig.getGenericParams()[0])); return valueType; }, [&](CanType origType, Type replacementType, @@ -2678,7 +2676,7 @@ namespace { ProtocolDecl *protocol = TypeChecker::getProtocol( ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByStringLiteral); - if (!TypeChecker::conformsToProtocol(type, protocol, cs.DC)) { + if (!TypeChecker::conformsToProtocol(type, protocol, cs.DC->getParentModule())) { // If the type does not conform to ExpressibleByStringLiteral, it should // be ExpressibleByExtendedGraphemeClusterLiteral. protocol = TypeChecker::getProtocol( @@ -2687,7 +2685,7 @@ namespace { isStringLiteral = false; isGraphemeClusterLiteral = true; } - if (!TypeChecker::conformsToProtocol(type, protocol, cs.DC)) { + if (!TypeChecker::conformsToProtocol(type, protocol, cs.DC->getParentModule())) { // ... or it should be ExpressibleByUnicodeScalarLiteral. protocol = TypeChecker::getProtocol( cs.getASTContext(), expr->getLoc(), @@ -2802,7 +2800,7 @@ namespace { assert(proto && "Missing string interpolation protocol?"); auto conformance = - TypeChecker::conformsToProtocol(type, proto, cs.DC); + TypeChecker::conformsToProtocol(type, proto, cs.DC->getParentModule()); assert(conformance && "string interpolation type conforms to protocol"); DeclName constrName(ctx, DeclBaseName::createConstructor(), argLabels); @@ -2908,7 +2906,8 @@ namespace { auto proto = TypeChecker::getLiteralProtocol(ctx, expr); assert(proto && "Missing object literal protocol?"); auto conformance = - TypeChecker::conformsToProtocol(conformingType, proto, cs.DC); + TypeChecker::conformsToProtocol(conformingType, proto, + cs.DC->getParentModule()); assert(conformance && "object literal type conforms to protocol"); auto constrName = TypeChecker::getObjectLiteralConstructorName(ctx, expr); @@ -3152,8 +3151,8 @@ namespace { Expr *ctorRef = buildOtherConstructorRef(overload.openedFullType, callee, base, nameLoc, ctorLocator, implicit); - auto *call = new (cs.getASTContext()) DotSyntaxCallExpr(ctorRef, dotLoc, - base); + auto *call = + DotSyntaxCallExpr::create(cs.getASTContext(), ctorRef, dotLoc, base); return finishApply(call, cs.getType(expr), locator, ctorLocator); } @@ -3411,7 +3410,21 @@ namespace { } Expr *visitAnyTryExpr(AnyTryExpr *expr) { - cs.setType(expr, cs.getType(expr->getSubExpr())); + auto *subExpr = expr->getSubExpr(); + auto type = simplifyType(cs.getType(subExpr)); + + // Let's load the value associated with this try. + if (type->hasLValueType()) { + subExpr = coerceToType(subExpr, type->getRValueType(), + cs.getConstraintLocator(subExpr)); + + if (!subExpr) + return nullptr; + } + + cs.setType(expr, cs.getType(subExpr)); + expr->setSubExpr(subExpr); + return expr; } @@ -3511,7 +3524,8 @@ namespace { assert(arrayProto && "type-checked array literal w/o protocol?!"); auto conformance = - TypeChecker::conformsToProtocol(arrayTy, arrayProto, cs.DC); + TypeChecker::conformsToProtocol(arrayTy, arrayProto, + cs.DC->getParentModule()); assert(conformance && "Type does not conform to protocol?"); DeclName name(ctx, DeclBaseName::createConstructor(), @@ -3555,7 +3569,8 @@ namespace { KnownProtocolKind::ExpressibleByDictionaryLiteral); auto conformance = - TypeChecker::conformsToProtocol(dictionaryTy, dictionaryProto, cs.DC); + TypeChecker::conformsToProtocol(dictionaryTy, dictionaryProto, + cs.DC->getParentModule()); if (conformance.isInvalid()) return nullptr; @@ -4298,7 +4313,7 @@ namespace { // Special handle for literals conditional checked cast when they can // be statically coerced to the cast type. if (protocol && TypeChecker::conformsToProtocol( - toType, protocol, cs.DC)) { + toType, protocol, cs.DC->getParentModule())) { ctx.Diags .diagnose(expr->getLoc(), diag::literal_conditional_downcast_to_coercion, @@ -4915,7 +4930,8 @@ namespace { } break; } - case KeyPathExpr::Component::Kind::Invalid: { + case KeyPathExpr::Component::Kind::Invalid: + case KeyPathExpr::Component::Kind::CodeCompletion: { auto component = origComponent; component.setComponentType(leafTy); resolvedComponents.push_back(component); @@ -5193,7 +5209,8 @@ namespace { // verified by the solver, we just need to get it again // with all of the generic parameters resolved. auto hashableConformance = - TypeChecker::conformsToProtocol(indexType, hashable, cs.DC); + TypeChecker::conformsToProtocol(indexType, hashable, + cs.DC->getParentModule()); assert(hashableConformance); conformances.push_back(hashableConformance); @@ -5503,13 +5520,13 @@ Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType) { /// conformances. static ArrayRef collectExistentialConformances(Type fromType, Type toType, - DeclContext *DC) { + ModuleDecl *module) { auto layout = toType->getExistentialLayout(); SmallVector conformances; for (auto proto : layout.getProtocols()) { conformances.push_back(TypeChecker::containsProtocol( - fromType, proto->getDecl(), DC)); + fromType, proto->getDecl(), module)); } return toType->getASTContext().AllocateCopy(conformances); @@ -5532,7 +5549,8 @@ Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType) { ASTContext &ctx = cs.getASTContext(); auto conformances = - collectExistentialConformances(fromInstanceType, toInstanceType, cs.DC); + collectExistentialConformances(fromInstanceType, toInstanceType, + cs.DC->getParentModule()); // For existential-to-existential coercions, open the source existential. if (fromType->isAnyExistentialType()) { @@ -5789,7 +5807,7 @@ Expr *ExprRewriter::coerceCallArguments( ParameterListInfo paramInfo(params, callee.getDecl(), skipCurriedSelf); SmallVector args; - AnyFunctionType::decomposeInput(cs.getType(arg), args); + AnyFunctionType::decomposeTuple(cs.getType(arg), args); // If this application is an init(wrappedValue:) call that needs an injected // wrapped value placeholder, the first non-defaulted argument must be @@ -5818,8 +5836,9 @@ Expr *ExprRewriter::coerceCallArguments( // Apply labels to arguments. AnyFunctionType::relabelParams(args, argLabels); - auto unlabeledTrailingClosureIndex = + auto oldTrailingClosureIndex = arg->getUnlabeledTrailingClosureIndexOfPackedArgument(); + Optional newTrailingClosureIndex; // Determine the parameter bindings that were applied. auto *locatorPtr = cs.getConstraintLocator(locator); @@ -5893,6 +5912,12 @@ Expr *ExprRewriter::coerceCallArguments( auto arg = getArg(argIdx); auto argType = cs.getType(arg); + // Update the trailing closure index if needed. + if (oldTrailingClosureIndex && *oldTrailingClosureIndex == argIdx) { + assert(!newTrailingClosureIndex); + newTrailingClosureIndex = newArgs.size(); + } + // If the argument type exactly matches, this just works. if (argType->isEqual(param.getPlainType())) { variadicArgs.push_back(arg); @@ -5955,6 +5980,12 @@ Expr *ExprRewriter::coerceCallArguments( auto arg = getArg(argIdx); auto argType = cs.getType(arg); + // Update the trailing closure index if needed. + if (oldTrailingClosureIndex && *oldTrailingClosureIndex == argIdx) { + assert(!newTrailingClosureIndex); + newTrailingClosureIndex = newArgs.size(); + } + // Save the original label location. newLabelLocs.push_back(getLabelLoc(argIdx)); @@ -5998,16 +6029,44 @@ Expr *ExprRewriter::coerceCallArguments( }; if (paramInfo.hasExternalPropertyWrapper(paramIdx)) { - auto *param = getParameterAt(callee.getDecl(), paramIdx); + auto *paramDecl = getParameterAt(callee.getDecl(), paramIdx); auto appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++]; auto wrapperType = solution.simplifyType(appliedWrapper.wrapperType); auto initKind = appliedWrapper.initKind; - using ValueKind = AppliedPropertyWrapperExpr::ValueKind; - ValueKind valueKind = (initKind == PropertyWrapperInitKind::ProjectedValue ? - ValueKind::ProjectedValue : ValueKind::WrappedValue); + AppliedPropertyWrapperExpr::ValueKind valueKind; + PropertyWrapperValuePlaceholderExpr *generatorArg; + auto initInfo = paramDecl->getPropertyWrapperInitializerInfo(); + if (initKind == PropertyWrapperInitKind::ProjectedValue) { + valueKind = AppliedPropertyWrapperExpr::ValueKind::ProjectedValue; + generatorArg = initInfo.getProjectedValuePlaceholder(); + } else { + valueKind = AppliedPropertyWrapperExpr::ValueKind::WrappedValue; + generatorArg = initInfo.getWrappedValuePlaceholder(); + } + + // Coerce the property wrapper argument type to the input type of + // the property wrapper generator function. The wrapper generator + // has the same generic signature as the enclosing function, so we + // can use substitutions from the callee. + Type generatorInputType = + generatorArg->getType().subst(callee.getSubstitutions()); + auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags()); + + if (generatorArg->isAutoClosure()) { + auto *closureType = generatorInputType->castTo(); + arg = coerceToType( + arg, closureType->getResult(), + argLoc.withPathElement(ConstraintLocator::AutoclosureResult)); + arg = cs.buildAutoClosureExpr(arg, closureType, dc); + } + + arg = coerceToType(arg, generatorInputType, argLoc); - arg = AppliedPropertyWrapperExpr::create(ctx, callee, param, arg->getStartLoc(), + // Wrap the argument in an applied property wrapper expr, which will + // later turn into a call to the property wrapper generator function. + arg = AppliedPropertyWrapperExpr::create(ctx, callee, paramDecl, + arg->getStartLoc(), wrapperType, arg, valueKind); cs.cacheExprTypes(arg); } @@ -6035,14 +6094,15 @@ Expr *ExprRewriter::coerceCallArguments( bool isDefaultWrappedValue = target->propertyWrapperHasInitialWrappedValue(); auto *placeholder = injectWrappedValuePlaceholder( - cs.buildAutoClosureExpr(arg, closureType, isDefaultWrappedValue), + cs.buildAutoClosureExpr(arg, closureType, dc, + isDefaultWrappedValue), /*isAutoClosure=*/true); - arg = CallExpr::createImplicit(ctx, placeholder, {}, {}); + arg = CallExpr::createImplicitEmpty(ctx, placeholder); arg->setType(closureType->getResult()); cs.cacheType(arg); } - convertedArg = cs.buildAutoClosureExpr(arg, closureType); + convertedArg = cs.buildAutoClosureExpr(arg, closureType, dc); } else { convertedArg = coerceToType( arg, paramType, @@ -6069,6 +6129,9 @@ Expr *ExprRewriter::coerceCallArguments( assert(newArgs.size() == newParams.size()); assert(newArgs.size() == newLabels.size()); assert(newArgs.size() == newLabelLocs.size()); + assert(oldTrailingClosureIndex.hasValue() == + newTrailingClosureIndex.hasValue()); + assert(!newTrailingClosureIndex || *newTrailingClosureIndex < newArgs.size()); // This is silly. SILGen gets confused if a 'self' parameter is wrapped // in a ParenExpr sometimes. @@ -6076,12 +6139,12 @@ Expr *ExprRewriter::coerceCallArguments( (params[0].getValueOwnership() == ValueOwnership::Default || params[0].getValueOwnership() == ValueOwnership::InOut)) { assert(newArgs.size() == 1); - assert(!unlabeledTrailingClosureIndex); + assert(!newTrailingClosureIndex); return newArgs[0]; } // Rebuild the argument list, sharing as much structure as possible. - auto paramType = AnyFunctionType::composeInput(ctx, newParams, + auto paramType = AnyFunctionType::composeTuple(ctx, newParams, /*canonicalVararg=*/false); if (isa(paramType.getPointer())) { if (argParen) { @@ -6091,7 +6154,7 @@ Expr *ExprRewriter::coerceCallArguments( bool isImplicit = arg->isImplicit(); arg = new (ctx) ParenExpr( lParenLoc, newArgs[0], rParenLoc, - static_cast(unlabeledTrailingClosureIndex)); + static_cast(newTrailingClosureIndex)); arg->setImplicit(isImplicit); } } else { @@ -6106,7 +6169,7 @@ Expr *ExprRewriter::coerceCallArguments( } else { // Build a new TupleExpr, re-using source location information. arg = TupleExpr::create(ctx, lParenLoc, rParenLoc, newArgs, newLabels, - newLabelLocs, unlabeledTrailingClosureIndex, + newLabelLocs, newTrailingClosureIndex, /*implicit=*/arg->isImplicit()); } } @@ -6120,13 +6183,14 @@ static bool isClosureLiteralExpr(Expr *expr) { return (isa(expr) || isa(expr)); } -/// Whether we should propagate async down to a closure. -static bool shouldPropagateAsyncToClosure(Expr *expr) { +/// Whether the given expression is a closure that should inherit +/// the actor context from where it was formed. +static bool closureInheritsActorContext(Expr *expr) { if (auto IE = dyn_cast(expr)) - return shouldPropagateAsyncToClosure(IE->getSubExpr()); + return closureInheritsActorContext(IE->getSubExpr()); if (auto CLE = dyn_cast(expr)) - return shouldPropagateAsyncToClosure(CLE->getClosureBody()); + return closureInheritsActorContext(CLE->getClosureBody()); if (auto CE = dyn_cast(expr)) return CE->inheritsActorContext(); @@ -6141,8 +6205,15 @@ static bool applyTypeToClosureExpr(ConstraintSystem &cs, Expr *expr, Type toType) { // Look through identity expressions, like parens. if (auto IE = dyn_cast(expr)) { - if (!applyTypeToClosureExpr(cs, IE->getSubExpr(), toType)) return false; - cs.setType(IE, toType); + if (!applyTypeToClosureExpr(cs, IE->getSubExpr(), toType)) + return false; + + auto subExprTy = cs.getType(IE->getSubExpr()); + if (isa(IE)) { + cs.setType(IE, ParenType::get(cs.getASTContext(), subExprTy)); + } else { + cs.setType(IE, subExprTy); + } return true; } @@ -6572,6 +6643,19 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, Optional typeFromPattern) { auto &ctx = cs.getASTContext(); + // Diagnose conversions to invalid function types that couldn't be performed + // beforehand because of placeholders. + if (auto *fnTy = toType->getAs()) { + auto contextTy = cs.getContextualType(expr); + if (cs.getConstraintLocator(locator)->isForContextualType() && contextTy && + contextTy->hasPlaceholder()) { + bool hadError = TypeChecker::diagnoseInvalidFunctionType( + fnTy, expr->getLoc(), None, dc, None); + if (hadError) + return nullptr; + } + } + // The type we're converting from. Type fromType = cs.getType(expr); @@ -6688,7 +6772,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); auto conformance = TypeChecker::conformsToProtocol( - cs.getType(expr), hashable, cs.DC); + cs.getType(expr), hashable, cs.DC->getParentModule()); assert(conformance && "must conform to Hashable"); return cs.cacheType( @@ -7012,22 +7096,6 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, } } - // If we have a ClosureExpr, then we can safely propagate the 'async' - // bit to the closure without invalidating prior analysis. - fromEI = fromFunc->getExtInfo(); - if (toEI.isAsync() && !fromEI.isAsync() && - shouldPropagateAsyncToClosure(expr)) { - auto newFromFuncType = fromFunc->withExtInfo(fromEI.withAsync()); - if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) { - fromFunc = newFromFuncType->castTo(); - - // Propagating the 'concurrent' bit might have satisfied the entire - // conversion. If so, we're done, otherwise keep converting. - if (fromFunc->isEqual(toType)) - return expr; - } - } - // If we have a ClosureExpr, then we can safely propagate a global actor // to the closure without invalidating prior analysis. fromEI = fromFunc->getExtInfo(); @@ -7044,27 +7112,28 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, } } - /// Whether the given effect should be propagated to a closure expression. - auto shouldApplyEffect = [&](EffectKind kind) -> bool { + /// Whether the given effect is polymorphic at this location. + auto isEffectPolymorphic = [&](EffectKind kind) -> bool { auto last = locator.last(); if (!(last && last->is())) - return true; + return false; - // The effect should not be applied if the closure is an argument - // to a function where that effect is polymorphic. if (auto *call = getAsExpr(locator.getAnchor())) { if (auto *declRef = dyn_cast(call->getFn())) { if (auto *fn = dyn_cast(declRef->getDecl())) - return !fn->hasPolymorphicEffect(kind); + return fn->hasPolymorphicEffect(kind); } } - return true; + return false; }; - // If we have a ClosureExpr, we can safely propagate 'async' to the closure. + // If we have a ClosureExpr, and we can safely propagate 'async' to the + // closure, do that here. fromEI = fromFunc->getExtInfo(); - if (toEI.isAsync() && !fromEI.isAsync() && shouldApplyEffect(EffectKind::Async)) { + bool shouldPropagateAsync = + !isEffectPolymorphic(EffectKind::Async) || closureInheritsActorContext(expr); + if (toEI.isAsync() && !fromEI.isAsync() && shouldPropagateAsync) { auto newFromFuncType = fromFunc->withExtInfo(fromEI.withAsync()); if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) { fromFunc = newFromFuncType->castTo(); @@ -7252,10 +7321,14 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, return cs.cacheType(new (ctx) UnresolvedTypeConversionExpr(expr, toType)); // Use an opaque type to abstract a value of the underlying concrete type. - if (toType->getAs()) { + // The full check here would be that `toType` and `fromType` are structually + // equal except in any position where `toType` has an opaque archetype. The + // below is just an approximate check since the above would be expensive to + // verify and still relies on the type checker ensuing `fromType` is + // compatible with any opaque archetypes. + if (toType->hasOpaqueArchetype()) return cs.cacheType(new (ctx) UnderlyingToOpaqueExpr(expr, toType)); - } - + llvm_unreachable("Unhandled coercion"); } @@ -7373,7 +7446,7 @@ Expr *ExprRewriter::convertLiteralInPlace( // initialize via the builtin protocol. if (builtinProtocol) { auto builtinConformance = TypeChecker::conformsToProtocol( - type, builtinProtocol, cs.DC); + type, builtinProtocol, cs.DC->getParentModule()); if (builtinConformance) { // Find the witness that we'll use to initialize the type via a builtin // literal. @@ -7396,7 +7469,8 @@ Expr *ExprRewriter::convertLiteralInPlace( // This literal type must conform to the (non-builtin) protocol. assert(protocol && "requirements should have stopped recursion"); - auto conformance = TypeChecker::conformsToProtocol(type, protocol, cs.DC); + auto conformance = TypeChecker::conformsToProtocol(type, protocol, + cs.DC->getParentModule()); assert(conformance && "must conform to literal protocol"); // Dig out the literal type and perform a builtin literal conversion to it. @@ -7524,7 +7598,8 @@ ExprRewriter::buildDynamicCallable(ApplyExpr *apply, SelectedOverload selected, auto dictLitProto = ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral); auto conformance = - TypeChecker::conformsToProtocol(argumentType, dictLitProto, cs.DC); + TypeChecker::conformsToProtocol(argumentType, dictLitProto, + cs.DC->getParentModule()); auto keyType = conformance.getTypeWitnessByName(argumentType, ctx.Id_Key); auto valueType = conformance.getTypeWitnessByName(argumentType, ctx.Id_Value); @@ -7631,7 +7706,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, {escapable}, {}, getType); cs.cacheSubExprTypes(callSubExpr); cs.setType(callSubExpr->getArg(), - AnyFunctionType::composeInput(ctx, + AnyFunctionType::composeTuple(ctx, escapableParams, false)); cs.setType(callSubExpr, resultType); @@ -7873,15 +7948,22 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, // nearest ancestor of 'expr' which imposes a minimum precedence on 'expr'. // Right now that just means skipping over TupleExpr instances that only exist // to hold arguments to binary operators. -static std::pair getPrecedenceParentAndIndex(Expr *expr, - Expr *rootExpr) -{ - auto parentMap = rootExpr->getParentMap(); - auto it = parentMap.find(expr); - if (it == parentMap.end()) { +static std::pair getPrecedenceParentAndIndex( + Expr *expr, llvm::function_ref getParent) { + auto *parent = getParent(expr); + if (!parent) return { nullptr, 0 }; + + // Look through an unresolved chain wrappers, try, and await exprs, as they + // have no effect on precedence; they will associate the same with any parent + // operator as their sub-expression would. + while (isa(parent) || + isa(parent) || isa(parent)) { + expr = parent; + parent = getParent(parent); + if (!parent) + return { nullptr, 0 }; } - Expr *parent = it->second; // Handle all cases where the answer isn't just going to be { parent, 0 }. if (auto tuple = dyn_cast(parent)) { @@ -7891,18 +7973,14 @@ static std::pair getPrecedenceParentAndIndex(Expr *expr, assert(elemIt != tupleElems.end() && "expr not found in parent TupleExpr"); unsigned index = elemIt - tupleElems.begin(); - it = parentMap.find(parent); - if (it != parentMap.end()) { - Expr *gparent = it->second; - - // Was this tuple just constructed for a binop? - if (isa(gparent)) { + // Was this tuple just constructed for a binop? + if (auto *gparent = getParent(tuple)) { + if (isa(gparent)) return { gparent, index }; - } } // Must be a tuple literal, function arg list, collection, etc. - return { parent, index }; + return { tuple, index }; } else if (auto ifExpr = dyn_cast(parent)) { unsigned index; if (expr == ifExpr->getCondExpr()) { @@ -7957,18 +8035,21 @@ bool swift::exprNeedsParensInsideFollowingOperator( /// the new operator to prevent it from binding incorrectly in the /// surrounding context. bool swift::exprNeedsParensOutsideFollowingOperator( - DeclContext *DC, Expr *expr, Expr *rootExpr, - PrecedenceGroupDecl *followingPG) { + DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG, + llvm::function_ref getParent) { Expr *parent; unsigned index; - std::tie(parent, index) = getPrecedenceParentAndIndex(expr, rootExpr); - if (!parent || isa(parent)) { + std::tie(parent, index) = getPrecedenceParentAndIndex(expr, getParent); + if (!parent) return false; - } - if (auto parenExp = dyn_cast(parent)) - if (!parenExp->isImplicit()) + if (isa(parent) || isa(parent)) { + if (!parent->isImplicit()) return false; + } + + if (isa(parent) || isa(parent)) + return false; if (parent->isInfixOperator()) { auto parentPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, @@ -8000,16 +8081,16 @@ bool swift::exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC, return exprNeedsParensInsideFollowingOperator(DC, expr, asPG); } -bool swift::exprNeedsParensAfterAddingNilCoalescing(DeclContext *DC, - Expr *expr, - Expr *rootExpr) { +bool swift::exprNeedsParensAfterAddingNilCoalescing( + DeclContext *DC, Expr *expr, + llvm::function_ref getParent) { auto &ctx = DC->getASTContext(); auto asPG = TypeChecker::lookupPrecedenceGroup( DC, ctx.Id_NilCoalescingPrecedence, SourceLoc()) .getSingle(); if (!asPG) return true; - return exprNeedsParensOutsideFollowingOperator(DC, expr, rootExpr, asPG); + return exprNeedsParensOutsideFollowingOperator(DC, expr, asPG, getParent); } namespace { @@ -8101,7 +8182,7 @@ namespace { if (auto captureList = dyn_cast(expr)) { // Rewrite captures. for (const auto &capture : captureList->getCaptureList()) { - (void)rewriteTarget(SolutionApplicationTarget(capture.Init)); + (void)rewriteTarget(SolutionApplicationTarget(capture.PBD)); } } @@ -8143,11 +8224,12 @@ namespace { if (auto *projectionVar = param->getPropertyWrapperProjectionVar()) { projectionVar->setInterfaceType( - solution.simplifyType(solution.getType(projectionVar))); + solution.simplifyType(solution.getType(projectionVar))->mapTypeOutOfContext()); } auto *wrappedValueVar = param->getPropertyWrapperWrappedValueVar(); - auto wrappedValueType = solution.simplifyType(solution.getType(wrappedValueVar)); + auto wrappedValueType = + solution.simplifyType(solution.getType(wrappedValueVar))->mapTypeOutOfContext(); wrappedValueVar->setInterfaceType(wrappedValueType->getWithoutSpecifierType()); if (param->hasImplicitPropertyWrapper()) { @@ -8315,13 +8397,12 @@ static Expr *wrapAsyncLetInitializer( auto closureType = FunctionType::get({ }, initializerType, extInfo); ASTContext &ctx = dc->getASTContext(); Expr *autoclosureExpr = cs.buildAutoClosureExpr( - initializer, closureType, /*isDefaultWrappedValue=*/false, - /*isSpawnLetWrapper=*/true); + initializer, closureType, dc, /*isDefaultWrappedValue=*/false, + /*isAsyncLetWrapper=*/true); // Call the autoclosure so that the AST types line up. SILGen will ignore the // actual calls and translate them into a different mechanism. - auto autoclosureCall = CallExpr::createImplicit( - ctx, autoclosureExpr, { }, { }); + auto autoclosureCall = CallExpr::createImplicitEmpty(ctx, autoclosureExpr); autoclosureCall->setType(initializerType); autoclosureCall->setThrows(throws); @@ -8375,13 +8456,18 @@ static Optional applySolutionToInitialization( // been subsumed by the backing property. if (wrappedVar) { ASTContext &ctx = cs.getASTContext(); - wrappedVar->getParentPatternBinding()->setInitializerSubsumed(0); ctx.setSideCachedPropertyWrapperBackingPropertyType( wrappedVar, initType->mapTypeOutOfContext()); // Record the semantic initializer on the outermost property wrapper. wrappedVar->getAttachedPropertyWrappers().front() ->setSemanticInit(initializer); + + // If this is a wrapped parameter, we're done. + if (isa(wrappedVar)) + return resultTarget; + + wrappedVar->getParentPatternBinding()->setInitializerSubsumed(0); } // Coerce the pattern to the type of the initializer. @@ -8417,7 +8503,7 @@ static Optional applySolutionToInitialization( // For an async let, wrap the initializer appropriately to make it a child // task. if (auto patternBinding = target.getInitializationPatternBindingDecl()) { - if (patternBinding->isSpawnLet()) { + if (patternBinding->isAsyncLet()) { resultTarget.setExpr( wrapAsyncLetInitializer( cs, resultTarget.getAsExpr(), resultTarget.getDeclContext())); @@ -8462,7 +8548,7 @@ static Optional applySolutionToForEachStmt( stmt->getAwaitLoc().isValid() ? KnownProtocolKind::AsyncSequence : KnownProtocolKind::Sequence); auto sequenceConformance = TypeChecker::conformsToProtocol( - forEachStmtInfo.sequenceType, sequenceProto, cs.DC); + forEachStmtInfo.sequenceType, sequenceProto, cs.DC->getParentModule()); assert(!sequenceConformance.isInvalid() && "Couldn't find sequence conformance"); @@ -8721,7 +8807,11 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) { patternBinding->setPattern( index, resultTarget->getInitializationPattern(), resultTarget->getDeclContext()); - patternBinding->setInit(index, resultTarget->getAsExpr()); + + if (patternBinding->getInit(index)) { + patternBinding->setInit(index, resultTarget->getAsExpr()); + patternBinding->setInitializerChecked(index); + } } return target; @@ -8736,6 +8826,19 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) { wrappedVar, backingType->mapTypeOutOfContext()); return target; + } else if (auto *pattern = target.getAsUninitializedVar()) { + auto contextualPattern = target.getContextualPattern(); + auto patternType = target.getTypeOfUninitializedVar(); + + if (auto coercedPattern = TypeChecker::coercePatternToType( + contextualPattern, patternType, + TypeResolverContext::PatternBindingDecl)) { + auto resultTarget = target; + resultTarget.setPattern(coercedPattern); + return resultTarget; + } + + return None; } else { auto fn = *target.getAsFunction(); if (rewriteFunction(fn)) @@ -8783,7 +8886,8 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) { // conversion. if (FunctionType *autoclosureParamType = target.getAsAutoclosureParamType()) { - resultExpr = cs.buildAutoClosureExpr(resultExpr, autoclosureParamType); + resultExpr = cs.buildAutoClosureExpr(resultExpr, autoclosureParamType, + target.getDeclContext()); } solution.setExprTypes(resultExpr); @@ -8890,6 +8994,12 @@ bool Solution::hasType(ASTNode node) const { return cs.hasType(node); } +bool Solution::hasType(const KeyPathExpr *KP, unsigned ComponentIndex) const { + assert(KP && "Expected non-null key path parameter!"); + return keyPathComponentTypes.find(std::make_pair(KP, ComponentIndex)) + != keyPathComponentTypes.end(); +} + Type Solution::getType(ASTNode node) const { auto result = nodeTypes.find(node); if (result != nodeTypes.end()) @@ -8899,6 +9009,11 @@ Type Solution::getType(ASTNode node) const { return cs.getType(node); } +Type Solution::getType(const KeyPathExpr *KP, unsigned I) const { + assert(hasType(KP, I) && "Expected type to have been set!"); + return keyPathComponentTypes.find(std::make_pair(KP, I))->second; +} + Type Solution::getResolvedType(ASTNode node) const { return simplifyType(getType(node)); } @@ -9000,7 +9115,7 @@ SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { case Kind::patternBinding: return *this; - case Kind::uninitializedWrappedVar: + case Kind::uninitializedVar: return *this; } diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 1276de22da4cd..bcfbe17bb0712 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -859,7 +859,8 @@ bool LiteralRequirement::isCoveredBy(Type type, DeclContext *useDC) const { if (hasDefaultType() && coversDefaultType(type, getDefaultType())) return true; - return bool(TypeChecker::conformsToProtocol(type, getProtocol(), useDC)); + return (bool)TypeChecker::conformsToProtocol(type, getProtocol(), + useDC->getParentModule()); } std::pair @@ -1110,14 +1111,35 @@ PotentialBindings::inferFromRelational(Constraint *constraint) { if (type->hasError()) return None; - if (auto *locator = TypeVar->getImpl().getLocator()) { - if (locator->isKeyPathType()) { - auto *BGT = - type->lookThroughAllOptionalTypes()->getAs(); - if (!BGT || !isKnownKeyPathType(BGT)) - return None; + if (TypeVar->getImpl().isKeyPathType()) { + auto *BGT = type->lookThroughAllOptionalTypes()->getAs(); + if (!BGT || !isKnownKeyPathType(BGT)) + return None; + + // `PartialKeyPath` represents a type-erased version of `KeyPath`. + // + // In situations where partial key path cannot be used directly i.e. + // passing an argument to a parameter represented by a partial key path, + // let's attempt a `KeyPath` binding which would then be converted to a + // partial key path since there is a subtype relationship between them. + if (BGT->isPartialKeyPath() && kind == AllowedBindingKind::Subtypes) { + auto &ctx = CS.getASTContext(); + auto *keyPathLoc = TypeVar->getImpl().getLocator(); + + auto rootTy = BGT->getGenericArgs()[0]; + // Since partial key path is an erased version of `KeyPath`, the value + // type would never be used, which means that binding can use + // type variable generated for a result of key path expression. + auto valueTy = + keyPathLoc->castLastElementTo() + .getValueType(); + + type = BoundGenericType::get(ctx.getKeyPathDecl(), Type(), + {rootTy, valueTy}); } + } + if (auto *locator = TypeVar->getImpl().getLocator()) { // Don't allow a protocol type to get propagated from the base to the result // type of a chain, Result should always be a concrete type which conforms // to the protocol inferred for the base. @@ -1192,8 +1214,6 @@ PotentialBindings::inferFromRelational(Constraint *constraint) { if (!bindingTypeVar) return None; - AdjacentVars.insert({bindingTypeVar, constraint}); - // If current type variable is associated with a code completion token // it's possible that it doesn't have enough contextual information // to be resolved to anything, so let's note that fact in the potential @@ -1215,14 +1235,24 @@ PotentialBindings::inferFromRelational(Constraint *constraint) { assert(kind == AllowedBindingKind::Supertypes); SupertypeOf.insert({bindingTypeVar, constraint}); } + + AdjacentVars.insert({bindingTypeVar, constraint}); break; } case ConstraintKind::Bind: case ConstraintKind::BindParam: - case ConstraintKind::Equal: + case ConstraintKind::Equal: { + EquivalentTo.insert({bindingTypeVar, constraint}); + AdjacentVars.insert({bindingTypeVar, constraint}); + break; + } + case ConstraintKind::UnresolvedMemberChainBase: { EquivalentTo.insert({bindingTypeVar, constraint}); + + // Don't record adjacency between base and result types, + // this is just an auxiliary contraint to enforce ordering. break; } @@ -1308,7 +1338,6 @@ void PotentialBindings::infer(Constraint *constraint) { case ConstraintKind::KeyPath: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: - case ConstraintKind::OpaqueUnderlyingType: // Constraints from which we can't do anything. break; @@ -1369,7 +1398,18 @@ void PotentialBindings::infer(Constraint *constraint) { } case ConstraintKind::ApplicableFunction: - case ConstraintKind::DynamicCallableApplicableFunction: + case ConstraintKind::DynamicCallableApplicableFunction: { + auto overloadTy = constraint->getSecondType(); + // If current type variable represents an overload set + // being applied to the arguments, it can't be delayed + // by application constraints, because it doesn't + // depend on argument/result types being resolved first. + if (overloadTy->isEqual(TypeVar)) + break; + + LLVM_FALLTHROUGH; + } + case ConstraintKind::BindOverload: { DelayedBy.push_back(constraint); break; @@ -1527,6 +1567,9 @@ void BindingSet::dump(TypeVariableType *typeVar, llvm::raw_ostream &out, } void BindingSet::dump(llvm::raw_ostream &out, unsigned indent) const { + PrintOptions PO; + PO.PrintTypesForDebugging = true; + out.indent(indent); if (isDirectHole()) out << "hole "; @@ -1539,16 +1582,18 @@ void BindingSet::dump(llvm::raw_ostream &out, unsigned indent) const { auto literalKind = getLiteralKind(); if (literalKind != LiteralBindingKind::None) out << "literal=" << static_cast(literalKind) << " "; - if (involvesTypeVariables()) - out << "involves_type_vars "; + if (involvesTypeVariables()) { + out << "involves_type_vars=["; + interleave(AdjacentVars, + [&](const auto *typeVar) { out << typeVar->getString(PO); }, + [&out]() { out << " "; }); + out << "] "; + } auto numDefaultable = getNumViableDefaultableBindings(); if (numDefaultable > 0) out << "#defaultable_bindings=" << numDefaultable << " "; - PrintOptions PO; - PO.PrintTypesForDebugging = true; - auto printBinding = [&](const PotentialBinding &binding) { auto type = binding.BindingType; switch (binding.Kind) { @@ -1646,14 +1691,19 @@ bool TypeVarBindingProducer::computeNext() { newBindings.push_back(std::move(binding)); }; + // Let's attempt only directly inferrable bindings for + // a type variable representing a closure type because + // such type variables are handled specially and only + // bound to a type inferred from their expression, having + // contextual bindings is just a trigger for that to + // happen. + if (TypeVar->getImpl().isClosureType()) + return false; + for (auto &binding : Bindings) { const auto type = binding.BindingType; assert(!type->hasError()); - // After our first pass, note that we've explored these types. - if (NumTries == 0) - ExploredTypes.insert(type->getCanonicalType()); - // If we have a protocol with a default type, look for alternative // types to the default. if (NumTries == 0 && binding.hasDefaultedLiteralProtocol()) { @@ -1841,10 +1891,24 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const { if (cs.hasFixFor(kpLocator, FixKind::AllowKeyPathWithoutComponents)) return None; + // If key path has any invalid component, let's just skip fix because the + // invalid component would be already diagnosed. + auto keyPath = castToExpr(srcLocator->getAnchor()); + if (llvm::any_of(keyPath->getComponents(), + [](KeyPathExpr::Component component) { + return !component.isValid(); + })) + return None; + ConstraintFix *fix = SpecifyKeyPathRootType::create(cs, dstLocator); return std::make_pair(fix, defaultImpact); } + if (srcLocator->isLastElement()) { + ConstraintFix *fix = SpecifyTypeForPlaceholder::create(cs, srcLocator); + return std::make_pair(fix, defaultImpact); + } + if (dstLocator->directlyAt()) { // This is a dramatic event, it means that there is absolutely // no contextual information to resolve type of `nil`. diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index 365f372fcb9e4..41cdf16b5be0d 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -33,15 +33,13 @@ class ClosureConstraintGenerator ConstraintSystem &cs; ClosureExpr *closure; - Type closureResultType; public: /// Whether an error was encountered while generating constraints. bool hadError = false; - ClosureConstraintGenerator(ConstraintSystem &cs, ClosureExpr *closure, - Type closureResultType) - : cs(cs), closure(closure), closureResultType(closureResultType) { } + ClosureConstraintGenerator(ConstraintSystem &cs, ClosureExpr *closure) + : cs(cs), closure(closure) {} private: void visitDecl(Decl *decl) { @@ -95,11 +93,10 @@ class ClosureConstraintGenerator // FIXME: Locator should point at the return statement? bool hasReturn = hasExplicitResult(closure); - cs.addConstraint( - ConstraintKind::Conversion, cs.getType(expr), - closureResultType, - cs.getConstraintLocator( - closure, LocatorPathElt::ClosureBody(hasReturn))); + cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), + cs.getClosureType(closure)->getResult(), + cs.getConstraintLocator( + closure, LocatorPathElt::ClosureBody(hasReturn))); } #define UNSUPPORTED_STMT(STMT) void visit##STMT##Stmt(STMT##Stmt *) { \ @@ -127,9 +124,8 @@ class ClosureConstraintGenerator } -bool ConstraintSystem::generateConstraints( - ClosureExpr *closure, Type resultType) { - ClosureConstraintGenerator generator(*this, closure, resultType); +bool ConstraintSystem::generateConstraints(ClosureExpr *closure) { + ClosureConstraintGenerator generator(*this, closure); generator.visit(closure->getBody()); return generator.hadError; } @@ -185,7 +181,7 @@ class ClosureConstraintApplication if (auto expr = node.dyn_cast()) { // Rewrite the expression. if (auto rewrittenExpr = rewriteExpr(expr)) - node = expr; + node = rewrittenExpr; else hadError = true; } else if (auto stmt = node.dyn_cast()) { diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 3228708a7b256..88454bedbcbb3 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// // -// This file implements diagnostics for constraint system. +// This file implements diagnostics for the constraint system. // //===----------------------------------------------------------------------===// @@ -165,7 +165,8 @@ Type FailureDiagnostic::restoreGenericParameters( bool FailureDiagnostic::conformsToKnownProtocol( Type type, KnownProtocolKind protocol) const { auto &cs = getConstraintSystem(); - return constraints::conformsToKnownProtocol(cs.DC, type, protocol); + return TypeChecker::conformsToKnownProtocol(type, protocol, + cs.DC->getParentModule()); } Type RequirementFailure::getOwnerType() const { @@ -199,7 +200,7 @@ const Requirement &RequirementFailure::getRequirement() const { // type requirement this conditional conformance belongs to. auto requirements = isConditional() ? Conformance->getConditionalRequirements() - : Signature->getRequirements(); + : Signature.getRequirements(); return requirements[getRequirementIndex()]; } @@ -226,13 +227,17 @@ ValueDecl *RequirementFailure::getDeclRef() const { // diagnostic directly to its declaration without desugaring. if (auto *alias = dyn_cast(type.getPointer())) return alias->getDecl(); - - if (auto *opaque = type->getAs()) - return opaque->getDecl(); - return type->getAnyGeneric(); }; + // TODO: potentially we are tracking more information than we need to here + // because the decl might also availiable via the contextual type. In the long + // run we probably want to refactor to get rid of get/set contextual. + if (auto opaqueLocator = + getLocator()->findFirst()) { + return opaqueLocator->getDecl(); + } + // If the locator is for a result builder body result type, the requirement // came from the function's return type. if (getLocator()->isForResultBuilderBodyResult()) { @@ -251,12 +256,6 @@ ValueDecl *RequirementFailure::getDeclRef() const { // type, local function etc. if (contextualPurpose == CTP_ReturnStmt || contextualPurpose == CTP_ReturnSingleExpr) { - // In case of opaque result type, let's point to the declaration - // associated with the type itself (since it has one) instead of - // declarer. - if (auto *opaque = contextualTy->getAs()) - return opaque->getDecl(); - return cast(getDC()->getAsDecl()); } @@ -398,8 +397,14 @@ bool RequirementFailure::diagnoseAsNote() { const auto &req = getRequirement(); const auto *reqDC = getRequirementDC(); + // Layout requirement doesn't have a second type, let's always + // `AnyObject`. + auto requirementTy = req.getKind() == RequirementKind::Layout + ? getASTContext().getAnyObjectType() + : req.getSecondType(); + emitDiagnosticAt(reqDC->getAsDecl(), getDiagnosticAsNote(), getLHS(), - getRHS(), req.getFirstType(), req.getSecondType(), ""); + getRHS(), req.getFirstType(), requirementTy); return true; } @@ -456,7 +461,7 @@ bool MissingConformanceFailure::diagnoseAsError() { if (isPatternMatchingOperator(anchor)) { auto *expr = castToExpr(anchor); if (auto *binaryOp = dyn_cast_or_null(findParentExpr(expr))) { - auto *caseExpr = binaryOp->getArg()->getElement(0); + auto *caseExpr = binaryOp->getLHS(); llvm::SmallPtrSet anchors; for (const auto *fix : getSolution().Fixes) { @@ -537,7 +542,21 @@ bool MissingConformanceFailure::diagnoseTypeCannotConform( nonConformingType->isEqual(protocolType), protocolType); - emitDiagnostic(diag::only_concrete_types_conform_to_protocols); + bool emittedSpecializedNote = false; + if (auto protoType = protocolType->getAs()) { + if (protoType->getDecl()->isSpecificProtocol(KnownProtocolKind::Sendable)) { + if (nonConformingType->is()) { + emitDiagnostic(diag::nonsendable_function_type); + emittedSpecializedNote = true; + } else if (nonConformingType->is()) { + emitDiagnostic(diag::nonsendable_tuple_type); + emittedSpecializedNote = true; + } + } + } + + if (!emittedSpecializedNote) + emitDiagnostic(diag::only_concrete_types_conform_to_protocols); if (auto *OTD = dyn_cast(AffectedDecl)) { auto *namingDecl = OTD->getNamingDecl(); @@ -898,6 +917,94 @@ bool AttributedFuncToTypeConversionFailure::diagnoseAsError() { return true; } +static VarDecl *getDestinationVarDecl(AssignExpr *AE, + const Solution &solution) { + ConstraintLocator *locator = nullptr; + if (auto *URDE = dyn_cast(AE->getDest())) { + locator = solution.getConstraintLocator(URDE, {ConstraintLocator::Member}); + } else if (auto *declRef = dyn_cast(AE->getDest())) { + locator = solution.getConstraintLocator(declRef); + } + if (!locator) + return nullptr; + + auto overload = solution.getOverloadChoiceIfAvailable(locator); + if (!overload) + return nullptr; + + return dyn_cast_or_null(overload->choice.getDecl()); +} + +bool AttributedFuncToTypeConversionFailure:: + diagnoseFunctionParameterEscapenessMismatch(AssignExpr *AE) const { + auto loc = getLocator(); + if (attributeKind != Escaping) + return false; + + if (!loc->findLast()) + return false; + + auto destType = getType(AE->getDest())->lookThroughAllOptionalTypes(); + auto destFnType = destType->castTo(); + auto sourceType = getType(AE->getSrc())->lookThroughAllOptionalTypes(); + + // The tuple locator element will give us the exact parameter mismatch + // position. + auto tupleElt = loc->getLastElementAs(); + auto mismatchPosition = tupleElt ? tupleElt->getIndex() : 0; + auto param = destFnType->getParams()[mismatchPosition]; + + emitDiagnostic(diag::cannot_convert_assign, sourceType, destType); + emitDiagnosticAt(AE->getDest()->getLoc(), + diag::escape_expected_at_parameter_position, + mismatchPosition, param.getParameterType()); + + auto &solution = getSolution(); + auto decl = getDestinationVarDecl(AE, solution); + // We couldn't find a declaration to add an extra note with a fix-it but + // the main diagnostic was already covered. + if (!decl) + return true; + + auto declRepr = decl->getTypeReprOrParentPatternTypeRepr(); + class TopLevelFuncReprFinder : public ASTWalker { + bool walkToTypeReprPre(TypeRepr *TR) override { + FnRepr = dyn_cast(TR); + return FnRepr == nullptr; + } + + public: + FunctionTypeRepr *FnRepr; + TopLevelFuncReprFinder() : FnRepr(nullptr) {} + }; + + // Look to find top-level function repr that maybe inside optional + // representations. + TopLevelFuncReprFinder fnFinder; + declRepr->walk(fnFinder); + + auto declFnRepr = fnFinder.FnRepr; + if (!declFnRepr) + return true; + + auto note = emitDiagnosticAt(decl->getLoc(), diag::add_explicit_escaping, + mismatchPosition); + auto argsRepr = declFnRepr->getArgsTypeRepr(); + auto argRepr = argsRepr->getElement(mismatchPosition).Type; + if (!param.isAutoClosure()) { + note.fixItInsert(argRepr->getStartLoc(), "@escaping "); + } else { + auto attrRepr = dyn_cast(argRepr); + if (attrRepr) { + auto autoclosureEndLoc = Lexer::getLocForEndOfToken( + getASTContext().SourceMgr, + attrRepr->getAttrs().getLoc(TAK_autoclosure)); + note.fixItInsertAfter(autoclosureEndLoc, " @escaping"); + } + } + return true; +} + bool AttributedFuncToTypeConversionFailure::diagnoseParameterUse() const { auto convertTo = getToType(); // If the other side is not a function, we have common case diagnostics @@ -954,6 +1061,11 @@ bool AttributedFuncToTypeConversionFailure::diagnoseParameterUse() const { diagnostic = diag::passing_noattrfunc_to_attrfunc; } } else if (auto *AE = getAsExpr(getRawAnchor())) { + // Attempt to diagnose escape/non-escape mismatch in function + // parameter position. + if (diagnoseFunctionParameterEscapenessMismatch(AE)) + return true; + if (auto *DRE = dyn_cast(AE->getSrc())) { PD = dyn_cast(DRE->getDecl()); diagnostic = diag::assigning_noattrfunc_to_attrfunc; @@ -1071,7 +1183,7 @@ bool MissingExplicitConversionFailure::diagnoseAsError() { } bool needsParensInside = exprNeedsParensBeforeAddingAs(anchor); - bool needsParensOutside = exprNeedsParensAfterAddingAs(anchor, expr); + bool needsParensOutside = exprNeedsParensAfterAddingAs(anchor); llvm::SmallString<2> insertBefore; llvm::SmallString<32> insertAfter; @@ -1210,11 +1322,8 @@ void MissingOptionalUnwrapFailure::offerDefaultValueUnwrapFixIt( // Figure out what we need to parenthesize. bool needsParensInside = exprNeedsParensBeforeAddingNilCoalescing(DC, const_cast(expr)); - auto parentExpr = findParentExpr(anchor); - if (parentExpr && isa(parentExpr)) - parentExpr = findParentExpr(parentExpr); bool needsParensOutside = exprNeedsParensAfterAddingNilCoalescing( - DC, const_cast(expr), parentExpr); + DC, const_cast(expr), [&](auto *E) { return findParentExpr(E); }); llvm::SmallString<2> insertBefore; llvm::SmallString<32> insertAfter; @@ -1879,19 +1988,6 @@ bool AssignmentFailure::diagnoseAsError() { // If the expression is the result of a call, it is an rvalue, not a mutable // lvalue. if (auto *AE = dyn_cast(immutableExpr)) { - // Handle literals, which are a call to the conversion function. - auto argsTuple = - dyn_cast(AE->getArg()->getSemanticsProvidingExpr()); - if (isa(AE) && AE->isImplicit() && argsTuple && - argsTuple->getNumElements() == 1) { - if (auto LE = dyn_cast( - argsTuple->getElement(0)->getSemanticsProvidingExpr())) { - emitDiagnosticAt(Loc, DeclDiagnostic, "literals are not mutable") - .highlight(LE->getSourceRange()); - return true; - } - } - std::string name = "call"; if (isa(AE) || isa(AE)) name = "unary operator"; @@ -2073,10 +2169,10 @@ AssignmentFailure::getMemberRef(ConstraintLocator *locator) const { if (!member->choice.isDecl()) return member->choice; - auto *DC = getDC(); auto *decl = member->choice.getDecl(); if (isa(decl) && - isValidDynamicMemberLookupSubscript(cast(decl), DC)) { + isValidDynamicMemberLookupSubscript(cast(decl), + getParentModule())) { auto *subscript = cast(decl); // If this is a keypath dynamic member lookup, we have to // adjust the locator to find member referred by it. @@ -2690,7 +2786,7 @@ bool ContextualFailure::diagnoseConversionToBool() const { // Check if we need the inner parentheses. // Technically we only need them if there's something in 'expr' with // lower precedence than '!=', but the code actually comes out nicer - // in most cases with parens on anything non-trivial. + // in most cases with parens on anything that is non-trivial. if (anchor->canAppendPostfixExpression()) { prefix = prefix.drop_back(); suffix = suffix.drop_front(); @@ -2754,7 +2850,7 @@ bool ContextualFailure::diagnoseThrowsTypeMismatch() const { Ctx.getProtocol(KnownProtocolKind::ErrorCodeProtocol)) { Type errorCodeType = getFromType(); auto conformance = TypeChecker::conformsToProtocol( - errorCodeType, errorCodeProtocol, getDC()); + errorCodeType, errorCodeProtocol, getParentModule()); if (conformance) { Type errorType = conformance @@ -2962,7 +3058,8 @@ bool ContextualFailure::tryProtocolConformanceFixIt( SmallVector missingProtoTypeStrings; SmallVector missingProtocols; for (auto protocol : layout.getProtocols()) { - if (!TypeChecker::conformsToProtocol(fromType, protocol->getDecl(), getDC())) { + if (!TypeChecker::conformsToProtocol(fromType, protocol->getDecl(), + getParentModule())) { missingProtoTypeStrings.push_back(protocol->getString()); missingProtocols.push_back(protocol->getDecl()); } @@ -3012,7 +3109,7 @@ bool ContextualFailure::tryProtocolConformanceFixIt( for (auto protocol : missingProtocols) { auto conformance = NormalProtocolConformance( nominal->getDeclaredType(), protocol, SourceLoc(), nominal, - ProtocolConformanceState::Incomplete); + ProtocolConformanceState::Incomplete, /*isUnchecked=*/false); ConformanceChecker checker(getASTContext(), &conformance, missingWitnesses); checker.resolveValueWitnesses(); @@ -3065,7 +3162,7 @@ void ContextualFailure::tryComputedPropertyFixIts() const { auto *initExpr = PBD->getInit(i); if (!VD->isStatic() && !VD->getAttrs().getAttribute() && - initExpr && isa(initExpr)) { + isa_and_nonnull(initExpr)) { auto diag = emitDiagnostic(diag::extension_stored_property_fixit, VD->getName()); diag.fixItRemove(PBD->getEqualLoc(i)); @@ -3175,7 +3272,7 @@ bool TupleContextualFailure::diagnoseAsError() { auto purpose = getContextualTypePurpose(); if (isNumElementsMismatch()) diagnostic = diag::tuple_types_not_convertible_nelts; - else if ((purpose == CTP_Initialization) && !getContextualType(getAnchor())) + else if (purpose == CTP_Unused) diagnostic = diag::tuple_types_not_convertible; else if (auto diag = getDiagnosticFor(purpose, getToType())) diagnostic = *diag; @@ -3250,7 +3347,8 @@ bool MissingCallFailure::diagnoseAsError() { switch (last.getKind()) { case ConstraintLocator::ContextualType: case ConstraintLocator::ApplyArgToParam: { - auto fnType = getType(anchor)->castTo(); + auto type = getType(anchor)->lookThroughAllOptionalTypes(); + auto fnType = type->castTo(); emitDiagnostic(diag::missing_nullary_call, fnType->getResult()) .fixItInsertAfter(insertLoc, "()"); return true; @@ -3858,7 +3956,7 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { if (!argExpr) return false; auto possibleApplyExpr = findParentExpr(expr); - return possibleApplyExpr && isa(possibleApplyExpr); + return isa_and_nonnull(possibleApplyExpr); }; auto *initCall = findParentExpr(findParentExpr(ctorRef)); @@ -4078,11 +4176,9 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { ValueDecl *decl0 = overloadedFn->getDecls()[0]; if (decl0->getBaseName() == decl0->getASTContext().Id_MatchOperator) { - assert(binaryExpr->getArg()->getElements().size() == 2); - // If the rhs of '~=' is the enum type, a single dot suffixes // since the type can be inferred - Type secondArgType = getType(binaryExpr->getArg()->getElement(1)); + Type secondArgType = getType(binaryExpr->getRHS()); if (secondArgType->isEqual(baseTy)) { Diag->fixItInsert(loc, "."); return true; @@ -4435,7 +4531,7 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { // corresponding to the missing argument doesn't support a trailing closure, // don't provide a Fix-It. // FIXME: It's possible to parenthesize and relabel the argument list to - // accomodate this, but it's tricky. + // accommodate this, but it's tricky. bool shouldEmitFixIt = !(insertingTrailingClosure && !paramAcceptsTrailingClosure); @@ -4984,20 +5080,69 @@ bool ExtraneousArgumentsFailure::diagnoseAsError() { params->getStartLoc(), diag::closure_argument_list_tuple, fnType, fnType->getNumParams(), params->size(), (params->size() == 1)); - bool onlyAnonymousParams = + // Unsed parameter is represented by `_` before `in`. + bool onlyUnusedParams = std::all_of(params->begin(), params->end(), [](ParamDecl *param) { return !param->hasName(); }); // If closure expects no parameters but N was given, - // and all of them are anonymous let's suggest removing them. - if (fnType->getNumParams() == 0 && onlyAnonymousParams) { + // and all of them are unused, let's suggest removing them. + if (fnType->getNumParams() == 0 && onlyUnusedParams) { auto inLoc = closure->getInLoc(); auto &sourceMgr = getASTContext().SourceMgr; - if (inLoc.isValid()) + if (inLoc.isValid()) { diag.fixItRemoveChars(params->getStartLoc(), Lexer::getLocForEndOfToken(sourceMgr, inLoc)); + return true; + } + } + + diag.flush(); + + // If all of the parameters are anonymous, let's point out references + // to make it explicit where parameters are used in complex closure body, + // which helps in situations where braces are missing for potential inner + // closures e.g. + // + // func a(_: () -> Void) {} + // func b(_: (Int) -> Void) {} + // + // a { + // ... + // b($0.member) + // } + // + // Here `$0` is associated with `a` since braces around `member` reference + // are missing. + if (!closure->hasSingleExpressionBody() && + llvm::all_of(params->getArray(), + [](ParamDecl *P) { return P->isAnonClosureParam(); })) { + if (auto *body = closure->getBody()) { + struct ParamRefFinder : public ASTWalker { + DiagnosticEngine &D; + ParameterList *Params; + + ParamRefFinder(DiagnosticEngine &diags, ParameterList *params) + : D(diags), Params(params) {} + + std::pair walkToExprPre(Expr *E) override { + if (auto *DRE = dyn_cast(E)) { + if (llvm::is_contained(Params->getArray(), DRE->getDecl())) { + auto *P = cast(DRE->getDecl()); + D.diagnose(DRE->getLoc(), diag::use_of_anon_closure_param, + P->getName()); + } + } + return {true, E}; + } + }; + + ParamRefFinder finder(getASTContext().Diags, params); + body->walk(finder); + } } + return true; } @@ -5036,7 +5181,16 @@ bool ExtraneousArgumentsFailure::diagnoseAsError() { }, [&] { OS << ", "; }); - emitDiagnostic(diag::extra_arguments_in_call, OS.str()); + bool areTrailingClosures = false; + if (auto *argExpr = getArgumentListExprFor(getLocator())) { + if (auto i = argExpr->getUnlabeledTrailingClosureIndexOfPackedArgument()) { + areTrailingClosures = llvm::all_of(ExtraArgs, [&](auto &pair) { + return pair.first >= i; + }); + } + } + + emitDiagnostic(diag::extra_arguments_in_call, areTrailingClosures, OS.str()); if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) { if (auto *decl = overload->choice.getDeclOrNull()) { @@ -5087,9 +5241,12 @@ bool ExtraneousArgumentsFailure::diagnoseSingleExtraArgument() const { auto argExpr = tuple ? tuple->getElement(index) : cast(arguments)->getSubExpr(); + auto trailingClosureIdx = + arguments->getUnlabeledTrailingClosureIndexOfPackedArgument(); + auto isTrailingClosure = trailingClosureIdx && index >= *trailingClosureIdx; + auto loc = argExpr->getLoc(); - if (tuple && index == tuple->getNumElements() - 1 && - tuple->hasTrailingClosure()) { + if (isTrailingClosure) { emitDiagnosticAt(loc, diag::extra_trailing_closure_in_call) .highlight(argExpr->getSourceRange()); } else if (ContextualType->getNumParams() == 0) { @@ -5287,8 +5444,17 @@ bool ExtraneousReturnFailure::diagnoseAsError() { if (FD->getResultTypeRepr() == nullptr && FD->getParameters()->getStartLoc().isValid() && !FD->getBaseIdentifier().empty()) { - auto fixItLoc = Lexer::getLocForEndOfToken( - getASTContext().SourceMgr, FD->getParameters()->getEndLoc()); + // Insert the fix-it after the parameter list, and after any + // effects specifiers. + SourceLoc loc = FD->getParameters()->getEndLoc(); + if (auto asyncLoc = FD->getAsyncLoc()) + loc = asyncLoc; + + if (auto throwsLoc = FD->getThrowsLoc()) + if (throwsLoc.getOpaquePointerValue() > loc.getOpaquePointerValue()) + loc = throwsLoc; + + auto fixItLoc = Lexer::getLocForEndOfToken(getASTContext().SourceMgr, loc); emitDiagnostic(diag::add_return_type_note) .fixItInsert(fixItLoc, " -> <#Return Type#>"); } @@ -5458,7 +5624,6 @@ bool MissingGenericArgumentsFailure::diagnoseAsError() { if (!isScoped) { auto anchor = getAnchor(); - assert(anchor.is() || anchor.is()); return diagnoseForAnchor(anchor, Parameters); } @@ -5705,8 +5870,84 @@ static bool hasMissingElseInChain(IfStmt *ifStmt) { return false; } +bool SkipUnhandledConstructInResultBuilderFailure::diagnosePatternBinding( + PatternBindingDecl *PB) const { + bool diagnosed = false; + + for (unsigned i : range(PB->getNumPatternEntries())) { + auto *pattern = PB->getPattern(i); + + // Each variable bound by the pattern must be stored and cannot have + // observers. + { + SmallVector variables; + pattern->collectVariables(variables); + + bool diagnosedStorage = false; + for (auto *var : variables) + diagnosedStorage |= diagnoseStorage(var); + + // if storage has been diagnosed, let's move to the next entry. + if (diagnosedStorage) { + diagnosed = true; + continue; + } + } + + // Diagnose all of the patterns without explicit initializers. + if (PB->isExplicitlyInitialized(i)) + continue; + + StringRef name; + + if (auto *TP = dyn_cast(pattern)) { + if (auto *NP = dyn_cast(TP->getSubPattern())) + name = NP->getNameStr(); + } + + emitDiagnosticAt(pattern->getLoc(), + diag::result_builder_requires_explicit_var_initialization, + !name.empty(), name, builder->getName()) + .fixItInsertAfter(pattern->getEndLoc(), " = <#value#>"); + + diagnosed = true; + } + + return diagnosed; +} + +bool SkipUnhandledConstructInResultBuilderFailure::diagnoseStorage( + VarDecl *var) const { + enum class PropertyKind : unsigned { lazy, wrapped, computed, observed }; + + if (var->getImplInfo().isSimpleStored()) + return false; + + PropertyKind kind; + if (var->getAttrs().hasAttribute()) { + kind = PropertyKind::lazy; + } else if (var->hasAttachedPropertyWrapper()) { + kind = PropertyKind::wrapped; + } else if (var->hasObservers()) { + kind = PropertyKind::observed; + } else { + kind = PropertyKind::computed; + } + + emitDiagnosticAt(var, diag::cannot_declare_computed_var_in_result_builder, + static_cast(kind)); + return true; +} + void SkipUnhandledConstructInResultBuilderFailure::diagnosePrimary( bool asNote) { + + if (auto *decl = unhandled.dyn_cast()) { + auto *PB = dyn_cast(decl); + if (PB && diagnosePatternBinding(PB)) + return; + } + if (auto stmt = unhandled.dyn_cast()) { emitDiagnostic(asNote ? diag::note_result_builder_control_flow : diag::result_builder_control_flow, @@ -6086,10 +6327,10 @@ bool ArgumentMismatchFailure::diagnoseAsError() { bool ArgumentMismatchFailure::diagnoseAsNote() { auto *locator = getLocator(); if (auto *callee = getCallee()) { - emitDiagnosticAt( - callee, diag::candidate_has_invalid_argument_at_position, getToType(), - getParamPosition(), - locator->isLastElement()); + emitDiagnosticAt(callee, diag::candidate_has_invalid_argument_at_position, + getToType(), getParamPosition(), + locator->isLastElement(), + getFromType()); return true; } @@ -6103,8 +6344,8 @@ bool ArgumentMismatchFailure::diagnoseUseOfReferenceEqualityOperator() const { return false; auto *binaryOp = castToExpr(getRawAnchor()); - auto *lhs = binaryOp->getArg()->getElement(0); - auto *rhs = binaryOp->getArg()->getElement(1); + auto *lhs = binaryOp->getLHS(); + auto *rhs = binaryOp->getRHS(); auto name = *getOperatorName(binaryOp->getFn()); @@ -6170,8 +6411,8 @@ bool ArgumentMismatchFailure::diagnosePatternMatchingMismatch() const { return false; auto *op = castToExpr(getRawAnchor()); - auto *lhsExpr = op->getArg()->getElement(0); - auto *rhsExpr = op->getArg()->getElement(1); + auto *lhsExpr = op->getLHS(); + auto *rhsExpr = op->getRHS(); auto lhsType = getType(lhsExpr); auto rhsType = getType(rhsExpr); @@ -6683,14 +6924,14 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() { auto *parentExpr = findParentExpr(anchor); // Look through immediate call of unresolved member (e.g., `.foo(0)`). - if (parentExpr && isa(parentExpr)) + if (isa_and_nonnull(parentExpr)) parentExpr = findParentExpr(parentExpr); // FIXME: We should probably look through the entire member chain so that // something like `let _ = .foo().bar` gets the "no contextual type" error // rather than the "Cannot infer contextual base" error. UnresolvedMemberChainResultExpr *resultExpr = nullptr; - if (parentExpr && isa(parentExpr)) { + if (isa_and_nonnull(parentExpr)) { resultExpr = cast(parentExpr); parentExpr = findParentExpr(parentExpr); } @@ -6770,13 +7011,9 @@ bool UnableToInferClosureParameterType::diagnoseAsError() { llvm::SmallString<16> id; llvm::raw_svector_ostream OS(id); - if (PD->isAnonClosureParam()) { - OS << "$" << paramIdx; - } else { - OS << "'" << PD->getParameterName() << "'"; - } + OS << "'" << PD->getParameterName() << "'"; - auto loc = PD->isAnonClosureParam() ? getLoc() : PD->getLoc(); + auto loc = PD->isImplicit() ? getLoc() : PD->getLoc(); emitDiagnosticAt(loc, diag::cannot_infer_closure_parameter_type, OS.str()); return true; } @@ -6784,8 +7021,7 @@ bool UnableToInferClosureParameterType::diagnoseAsError() { bool UnableToInferClosureReturnType::diagnoseAsError() { auto *closure = castToExpr(getRawAnchor()); - auto diagnostic = emitDiagnostic(diag::cannot_infer_closure_result_type, - closure->hasSingleExpressionBody()); + auto diagnostic = emitDiagnostic(diag::cannot_infer_closure_result_type); // If there is a location for an 'in' token, then the argument list was // specified somehow but no return type was. Insert a "-> ReturnType " @@ -6989,9 +7225,9 @@ bool AbstractRawRepresentableFailure::diagnoseAsNote() { } } else if (auto argConv = locator->getLastElementAs()) { - diagnostic.emplace( - emitDiagnostic(diag::candidate_has_invalid_argument_at_position, - RawReprType, argConv->getParamIdx(), /*inOut=*/false)); + diagnostic.emplace(emitDiagnostic( + diag::candidate_has_invalid_argument_at_position, RawReprType, + argConv->getParamIdx(), /*inOut=*/false, ExpectedType)); } if (diagnostic) { @@ -7233,12 +7469,12 @@ bool MissingContextualTypeForNil::diagnoseAsError() { // attempt any types for it. auto *parentExpr = findParentExpr(expr); - while (parentExpr && isa(parentExpr)) + while (isa_and_nonnull(parentExpr)) parentExpr = findParentExpr(parentExpr); // In cases like `_ = nil?` AST would have `nil` // wrapped in `BindOptionalExpr`. - if (parentExpr && isa(parentExpr)) + if (isa_and_nonnull(parentExpr)) parentExpr = findParentExpr(parentExpr); if (parentExpr) { @@ -7265,6 +7501,19 @@ bool MissingContextualTypeForNil::diagnoseAsError() { return true; } +bool CouldNotInferPlaceholderType::diagnoseAsError() { + // If this placeholder was explicitly written out by the user, they can maybe + // fix things by specifying an actual type. + if (auto *typeExpr = getAsExpr(getAnchor())) { + if (typeExpr->getLoc().isValid()) { + emitDiagnostic(diag::could_not_infer_placeholder); + return true; + } + } + + return false; +} + bool ReferenceToInvalidDeclaration::diagnoseAsError() { auto &DE = getASTContext().Diags; @@ -7387,9 +7636,15 @@ bool InvalidMemberRefOnProtocolMetatype::diagnoseAsError() { if (auto *whereClause = extension->getTrailingWhereClause()) { auto sourceRange = whereClause->getSourceRange(); note.fixItInsertAfter(sourceRange.End, ", Self == <#Type#> "); - } else { - auto nameRepr = extension->getExtendedTypeRepr(); - note.fixItInsertAfter(nameRepr->getEndLoc(), " where Self == <#Type#>"); + } else if (auto nameRepr = extension->getExtendedTypeRepr()) { + // Type repr is not always available so we need to be defensive + // about its presence and validity. + if (nameRepr->isInvalid()) + return true; + + if (auto noteLoc = nameRepr->getEndLoc()) { + note.fixItInsertAfter(noteLoc, " where Self == <#Type#>"); + } } return true; @@ -7414,8 +7669,8 @@ SourceRange CheckedCastBaseFailure::getCastRange() const { llvm_unreachable("There is no other kind of checked cast!"); } -std::tuple -CoercibleOptionalCheckedCastFailure::unwrapedTypes() const { +std::tuple +CoercibleOptionalCheckedCastFailure::unwrappedTypes() const { SmallVector fromOptionals; SmallVector toOptionals; Type unwrappedFromType = @@ -7431,8 +7686,7 @@ bool CoercibleOptionalCheckedCastFailure::diagnoseIfExpr() const { return false; Type unwrappedFrom, unwrappedTo; - unsigned extraFromOptionals; - std::tie(unwrappedFrom, unwrappedTo, extraFromOptionals) = unwrapedTypes(); + std::tie(unwrappedFrom, unwrappedTo, std::ignore) = unwrappedTypes(); SourceRange diagFromRange = getFromRange(); SourceRange diagToRange = getToRange(); @@ -7463,8 +7717,8 @@ bool CoercibleOptionalCheckedCastFailure::diagnoseForcedCastExpr() const { auto fromType = getFromType(); auto toType = getToType(); Type unwrappedFrom, unwrappedTo; - unsigned extraFromOptionals; - std::tie(unwrappedFrom, unwrappedTo, extraFromOptionals) = unwrapedTypes(); + int extraFromOptionals; + std::tie(unwrappedFrom, unwrappedTo, extraFromOptionals) = unwrappedTypes(); SourceRange diagFromRange = getFromRange(); SourceRange diagToRange = getToRange(); @@ -7506,8 +7760,7 @@ bool CoercibleOptionalCheckedCastFailure::diagnoseConditionalCastExpr() const { auto fromType = getFromType(); auto toType = getToType(); Type unwrappedFrom, unwrappedTo; - unsigned extraFromOptionals; - std::tie(unwrappedFrom, unwrappedTo, extraFromOptionals) = unwrapedTypes(); + std::tie(unwrappedFrom, unwrappedTo, std::ignore) = unwrappedTypes(); SourceRange diagFromRange = getFromRange(); SourceRange diagToRange = getToRange(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index b6b0548426401..8c5b8e121fc97 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -150,6 +150,10 @@ class FailureDiagnostic { return cs.DC; } + ModuleDecl *getParentModule() const { + return getDC()->getParentModule(); + } + ASTContext &getASTContext() const { auto &cs = getConstraintSystem(); return cs.getASTContext(); @@ -231,7 +235,7 @@ class RequirementFailure : public FailureDiagnostic { using PathEltKind = ConstraintLocator::PathElementKind; using DiagOnDecl = Diag; using DiagInReference = Diag; - using DiagAsNote = Diag; + using DiagAsNote = Diag; /// If this failure associated with one of the conditional requirements, /// this field would represent conformance where requirement comes from. @@ -604,7 +608,10 @@ class ContextualFailure : public FailureDiagnostic { ContextualFailure(const Solution &solution, ContextualTypePurpose purpose, Type lhs, Type rhs, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), CTP(purpose), RawFromType(lhs), - RawToType(rhs) {} + RawToType(rhs) { + assert(lhs && "Expected a valid 'from' type"); + assert(rhs && "Expected a valid 'to' type"); + } SourceLoc getLoc() const override; @@ -733,6 +740,11 @@ class AttributedFuncToTypeConversionFailure final : public ContextualFailure { /// argument, or trying to assign it to a variable which expects @escaping /// or @Sendable function. bool diagnoseParameterUse() const; + + /// Emit a tailored diagnostic for a no-escape/espace mismatch for function + /// arguments where the mismatch has to take into account that a + /// function type subtype relation in the parameter position is contravariant. + bool diagnoseFunctionParameterEscapenessMismatch(AssignExpr *) const; }; /// Diagnose failure where a global actor attribute is dropped when @@ -891,7 +903,7 @@ class MissingExplicitConversionFailure final : public ContextualFailure { asPG); } - bool exprNeedsParensAfterAddingAs(const Expr *expr, const Expr *rootExpr) { + bool exprNeedsParensAfterAddingAs(const Expr *expr) { auto *DC = getDC(); auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()).getSingle(); @@ -899,7 +911,8 @@ class MissingExplicitConversionFailure final : public ContextualFailure { return true; return exprNeedsParensOutsideFollowingOperator( - DC, const_cast(expr), const_cast(rootExpr), asPG); + DC, const_cast(expr), asPG, + [&](auto *E) { return findParentExpr(E); }); } }; @@ -1849,6 +1862,13 @@ class SkipUnhandledConstructInResultBuilderFailure final bool diagnoseAsError() override; bool diagnoseAsNote() override; + +private: + /// Tailored diagnostics for an unsupported variable declaration. + bool diagnosePatternBinding(PatternBindingDecl *PB) const; + + /// Tailored diagnostics for lazy/wrapped/computed variable declarations. + bool diagnoseStorage(VarDecl *var) const; }; /// Diagnose situation when a single "tuple" parameter is given N arguments e.g. @@ -2386,6 +2406,21 @@ class MissingContextualTypeForNil final : public FailureDiagnostic { bool diagnoseAsError() override; }; +/// Diagnose situations where there is no context to determine the type of a +/// placeholder, e.g., +/// +/// \code +/// let _ = _.foo +/// \endcode +class CouldNotInferPlaceholderType final : public FailureDiagnostic { +public: + CouldNotInferPlaceholderType(const Solution &solution, + ConstraintLocator *locator) + : FailureDiagnostic(solution, locator) {} + + bool diagnoseAsError() override; +}; + /// Diagnostic situations where AST node references an invalid declaration. /// /// \code @@ -2489,7 +2524,7 @@ class CoercibleOptionalCheckedCastFailure final bool diagnoseAsError() override; private: - std::tuple unwrapedTypes() const; + std::tuple unwrappedTypes() const; bool diagnoseIfExpr() const; diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index d18c545787984..9cb05be1f70c3 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -403,9 +403,21 @@ getStructuralTypeContext(const Solution &solution, ConstraintLocator *locator) { return std::make_tuple(contextualTypeElt->getPurpose(), exprType, contextualType); } else if (auto argApplyInfo = solution.getFunctionArgApplyInfo(locator)) { - return std::make_tuple(CTP_CallArgument, - argApplyInfo->getArgType(), - argApplyInfo->getParamType()); + Type fromType = argApplyInfo->getArgType(); + Type toType = argApplyInfo->getParamType(); + // In case locator points to the function result we want the + // argument and param function types result. + if (locator->isLastElement()) { + auto fromFnType = fromType->getAs(); + auto toFnType = toType->getAs(); + if (fromFnType && toFnType) { + auto &cs = solution.getConstraintSystem(); + return std::make_tuple( + cs.getContextualTypePurpose(locator->getAnchor()), + fromFnType->getResult(), toFnType->getResult()); + } + } + return std::make_tuple(CTP_CallArgument, fromType, toType); } else if (auto *coerceExpr = getAsExpr(locator->getAnchor())) { return std::make_tuple(CTP_CoerceOperand, solution.getType(coerceExpr->getSubExpr()), @@ -416,14 +428,7 @@ getStructuralTypeContext(const Solution &solution, ConstraintLocator *locator) { return std::make_tuple(CTP, solution.getType(assignExpr->getSrc()), solution.getType(assignExpr->getDest())->getRValueType()); - } else if (auto *call = getAsExpr(locator->getAnchor())) { - assert(isa(call->getFn())); - return std::make_tuple( - CTP_Initialization, - solution.getType(call->getFn())->getMetatypeInstanceType(), - solution.getType(call->getArg())); } - return None; } @@ -441,26 +446,19 @@ bool AllowTupleTypeMismatch::coalesceAndDiagnose( indices.push_back(*tupleFix->Index); } - auto &cs = getConstraintSystem(); auto *locator = getLocator(); ContextualTypePurpose purpose; - Type fromType; - Type toType; - - if (getFromType()->is() && getToType()->is()) { - purpose = cs.getContextualTypePurpose(locator->getAnchor()); - fromType = getFromType(); - toType = getToType(); - } else if (auto contextualTypeInfo = - getStructuralTypeContext(solution, locator)) { - std::tie(purpose, fromType, toType) = *contextualTypeInfo; + if (isExpr(locator->getAnchor())) { + purpose = CTP_CoerceOperand; + } else if (auto *assignExpr = getAsExpr(locator->getAnchor())) { + purpose = isa(assignExpr->getDest()) ? CTP_SubscriptAssignSource + : CTP_AssignSource; } else { - return false; + auto &cs = getConstraintSystem(); + purpose = cs.getContextualTypePurpose(locator->getAnchor()); } - TupleContextualFailure failure(solution, purpose, - fromType->lookThroughAllOptionalTypes(), - toType->lookThroughAllOptionalTypes(), + TupleContextualFailure failure(solution, purpose, getFromType(), getToType(), indices, locator); return failure.diagnose(asNote); } @@ -1545,11 +1543,7 @@ std::string SpecifyClosureParameterType::getName() const { auto *PD = closure->getParameters()->get(paramLoc.getIndex()); OS << "specify type for parameter "; - if (PD->isAnonClosureParam()) { - OS << "$" << paramLoc.getIndex(); - } else { - OS << "'" << PD->getParameterName() << "'"; - } + OS << "'" << PD->getParameterName() << "'"; return OS.str(); } @@ -1734,78 +1728,14 @@ AllowKeyPathWithoutComponents::create(ConstraintSystem &cs, } bool IgnoreInvalidResultBuilderBody::diagnose(const Solution &solution, - bool asNote) const { - switch (Phase) { - // Handled below - case ErrorInPhase::PreCheck: - break; - case ErrorInPhase::ConstraintGeneration: - return true; // Already diagnosed by `matchResultBuilder`. - } - - auto *S = castToExpr(getAnchor())->getBody(); - - class PreCheckWalker : public ASTWalker { - DeclContext *DC; - DiagnosticTransaction Transaction; - // Check whether expression(s) in the body of the - // result builder had any `ErrorExpr`s before pre-check. - bool FoundErrorExpr = false; - - public: - PreCheckWalker(DeclContext *dc) - : DC(dc), Transaction(dc->getASTContext().Diags) {} - - std::pair walkToExprPre(Expr *E) override { - // This has to be checked before `preCheckExpression` - // because pre-check would convert invalid references - // into `ErrorExpr`s. - E->forEachChildExpr([&](Expr *expr) { - FoundErrorExpr |= isa(expr); - return FoundErrorExpr ? nullptr : expr; - }); - - auto hasError = ConstraintSystem::preCheckExpression( - E, DC, /*replaceInvalidRefsWithErrors=*/true); - - return std::make_pair(false, hasError ? nullptr : E); - } - - std::pair walkToStmtPre(Stmt *S) override { - return std::make_pair(true, S); - } - - // Ignore patterns because result builder pre-check does so as well. - std::pair walkToPatternPre(Pattern *P) override { - return std::make_pair(false, P); - } - - bool diagnosed() const { - // pre-check produced an error. - if (Transaction.hasErrors()) - return true; - - // If there were `ErrorExpr`s before pre-check - // they should have been diagnosed already by parser. - if (FoundErrorExpr) { - auto &DE = DC->getASTContext().Diags; - return DE.hadAnyError(); - } - - return false; - } - }; - - PreCheckWalker walker(solution.getDC()); - S->walk(walker); - - return walker.diagnosed(); + bool asNote) const { + return true; // Already diagnosed by `matchResultBuilder`. } -IgnoreInvalidResultBuilderBody *IgnoreInvalidResultBuilderBody::create( - ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator) { - return new (cs.getAllocator()) - IgnoreInvalidResultBuilderBody(cs, phase, locator); +IgnoreInvalidResultBuilderBody * +IgnoreInvalidResultBuilderBody::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) IgnoreInvalidResultBuilderBody(cs, locator); } bool SpecifyContextualTypeForNil::diagnose(const Solution &solution, @@ -1820,6 +1750,18 @@ SpecifyContextualTypeForNil::create(ConstraintSystem &cs, return new (cs.getAllocator()) SpecifyContextualTypeForNil(cs, locator); } +bool SpecifyTypeForPlaceholder::diagnose(const Solution &solution, + bool asNote) const { + CouldNotInferPlaceholderType failure(solution, getLocator()); + return failure.diagnose(asNote); +} + +SpecifyTypeForPlaceholder * +SpecifyTypeForPlaceholder::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) SpecifyTypeForPlaceholder(cs, locator); +} + bool AllowRefToInvalidDecl::diagnose(const Solution &solution, bool asNote) const { ReferenceToInvalidDeclaration failure(solution, getLocator()); @@ -1896,6 +1838,26 @@ SpecifyBaseTypeForOptionalUnresolvedMember::attempt( if (memberDecl->isInstanceMember()) continue; + // Disable this warning for ambiguities related to a + // static member lookup in generic context because it's + // possible to declare a member with the same name on + // a concrete type and in an extension of a protocol + // that type conforms to e.g.: + // + // struct S : P { static var test: S { ... } + // + // extension P where Self == S { static var test: { ... } } + // + // And use that in an optional context e.g. passing `.test` + // to a parameter of expecting `S?`. + if (auto *extension = + dyn_cast(memberDecl->getDeclContext())) { + if (extension->getSelfProtocolDecl()) { + allOptionalBase = false; + break; + } + } + allOptionalBase &= bool(choice.getBaseType() ->getMetatypeInstanceType() ->getOptionalObjectType()); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 47d70b1137cc9..52a9ed8645c35 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -411,7 +411,7 @@ namespace { if (otherArgTy && otherArgTy->getAnyNominal()) { if (otherArgTy->isEqual(paramTy) && TypeChecker::conformsToProtocol( - otherArgTy, literalProto, CS.DC)) { + otherArgTy, literalProto, CS.DC->getParentModule())) { return true; } } else if (Type defaultType = @@ -559,7 +559,7 @@ namespace { if (!fnTy) return false; - Type paramTy = FunctionType::composeInput(CS.getASTContext(), + Type paramTy = FunctionType::composeTuple(CS.getASTContext(), fnTy->getParams(), false); auto argTy = CS.getType(expr->getArg()) @@ -637,7 +637,7 @@ namespace { return false; auto paramTy = - AnyFunctionType::composeInput(CS.getASTContext(), fnTy->getParams(), + AnyFunctionType::composeTuple(CS.getASTContext(), fnTy->getParams(), /*canonicalVararg*/ false); return favoredTy->isEqual(paramTy); }; @@ -990,7 +990,7 @@ namespace { // arguments together with their inout-ness, instead of a single // ParenExpr or TupleExpr. SmallVector params; - AnyFunctionType::decomposeInput(CS.getType(index), params); + AnyFunctionType::decomposeTuple(CS.getType(index), params); // Add the constraint that the index expression's type be convertible // to the input type of the subscript operator. @@ -1211,7 +1211,7 @@ namespace { FunctionRefKind::DoubleApply, {}, memberLoc); SmallVector args; - AnyFunctionType::decomposeInput(CS.getType(expr->getArg()), args); + AnyFunctionType::decomposeTuple(CS.getType(expr->getArg()), args); auto resultType = CS.createTypeVariable( CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult), @@ -1706,17 +1706,24 @@ namespace { auto locator = CS.getConstraintLocator(expr); auto contextualType = CS.getContextualType(expr); + auto contextualPurpose = CS.getContextualTypePurpose(expr); auto joinElementTypes = [&](Optional elementType) { + auto openedElementType = elementType.map([&](Type type) { + return CS.openOpaqueType(type, contextualPurpose, locator); + }); + const auto elements = expr->getElements(); unsigned index = 0; using Iterator = decltype(elements)::iterator; - CS.addJoinConstraint(locator, elements.begin(), elements.end(), - elementType, [&](const auto it) { - auto *locator = CS.getConstraintLocator(expr, LocatorPathElt::TupleElement(index++)); - return std::make_pair(CS.getType(*it), locator); - }); + CS.addJoinConstraint( + locator, elements.begin(), elements.end(), openedElementType, + [&](const auto it) { + auto *locator = CS.getConstraintLocator( + expr, LocatorPathElt::TupleElement(index++)); + return std::make_pair(CS.getType(*it), locator); + }); }; // If a contextual type exists for this expression, apply it directly. @@ -1737,13 +1744,15 @@ namespace { if (!contextualType) return false; + auto *M = CS.DC->getParentModule(); + auto type = contextualType->lookThroughAllOptionalTypes(); - if (conformsToKnownProtocol( - CS.DC, type, KnownProtocolKind::ExpressibleByArrayLiteral)) + if (TypeChecker::conformsToKnownProtocol( + type, KnownProtocolKind::ExpressibleByArrayLiteral, M)) return false; - return conformsToKnownProtocol( - CS.DC, type, KnownProtocolKind::ExpressibleByDictionaryLiteral); + return TypeChecker::conformsToKnownProtocol( + type, KnownProtocolKind::ExpressibleByDictionaryLiteral, M); }; if (isDictionaryContextualType(contextualType)) { @@ -1822,16 +1831,17 @@ namespace { auto locator = CS.getConstraintLocator(expr); auto contextualType = CS.getContextualType(expr); - Type contextualDictionaryType = nullptr; - Type contextualDictionaryKeyType = nullptr; - Type contextualDictionaryValueType = nullptr; - - // If a contextual type exists for this expression, apply it directly. + auto contextualPurpose = CS.getContextualTypePurpose(expr); + auto openedType = + CS.openOpaqueType(contextualType, contextualPurpose, locator); + + // If a contextual type exists for this expression and is a dictionary + // type, apply it directly. Optional> dictionaryKeyValue; - if (contextualType && - (dictionaryKeyValue = ConstraintSystem::isDictionaryType(contextualType))) { - // Is the contextual type a dictionary type? - contextualDictionaryType = contextualType; + if (openedType && (dictionaryKeyValue = + ConstraintSystem::isDictionaryType(openedType))) { + Type contextualDictionaryKeyType; + Type contextualDictionaryValueType; std::tie(contextualDictionaryKeyType, contextualDictionaryValueType) = *dictionaryKeyValue; @@ -1839,11 +1849,10 @@ namespace { TupleTypeElt tupleElts[2] = { TupleTypeElt(contextualDictionaryKeyType), TupleTypeElt(contextualDictionaryValueType) }; Type contextualDictionaryElementType = TupleType::get(tupleElts, C); - - CS.addConstraint(ConstraintKind::LiteralConformsTo, contextualType, - dictionaryProto->getDeclaredInterfaceType(), - locator); - + + CS.addConstraint(ConstraintKind::LiteralConformsTo, openedType, + dictionaryProto->getDeclaredInterfaceType(), locator); + unsigned index = 0; for (auto element : expr->getElements()) { CS.addConstraint(ConstraintKind::Conversion, @@ -1852,10 +1861,10 @@ namespace { CS.getConstraintLocator( expr, LocatorPathElt::TupleElement(index++))); } - - return contextualDictionaryType; + + return openedType; } - + auto dictionaryTy = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding | TVO_CanBindToNoEscape); @@ -2029,6 +2038,8 @@ namespace { } auto extInfo = CS.closureEffects(closure); + auto resultLocator = + CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult); // Closure expressions always have function type. In cases where a // parameter or return type is omitted, a fresh type variable is used to @@ -2042,9 +2053,7 @@ namespace { const auto resolvedTy = resolveTypeReferenceInExpression( closure->getExplicitResultTypeRepr(), - TypeResolverContext::InExpression, - CS.getConstraintLocator(closure, - ConstraintLocator::ClosureResult)); + TypeResolverContext::InExpression, resultLocator); if (resolvedTy) return resolvedTy; } @@ -2060,9 +2069,9 @@ namespace { // If this is a multi-statement closure, let's mark result // as potential hole right away. return Type(CS.createTypeVariable( - CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult), - shouldTypeCheckInEnclosingExpression(closure) ? 0 - : TVO_CanBindToHole)); + resultLocator, shouldTypeCheckInEnclosingExpression(closure) + ? 0 + : TVO_CanBindToHole)); }(); // For a non-async function type, add the global actor if present. @@ -2070,7 +2079,9 @@ namespace { extInfo = extInfo.withGlobalActor(getExplicitGlobalActor(closure)); } - return FunctionType::get(closureParams, resultTy, extInfo); + auto *fnTy = FunctionType::get(closureParams, resultTy, extInfo); + return CS.replaceInferableTypesWithTypeVars( + fnTy, CS.getConstraintLocator(closure))->castTo(); } /// Produces a type for the given pattern, filling in any missing @@ -2235,7 +2246,9 @@ namespace { // Look through reference storage types. type = type->getReferenceStorageReferent(); - Type openedType = CS.replaceInferableTypesWithTypeVars(type, locator); + Type replacedType = CS.replaceInferableTypesWithTypeVars(type, locator); + Type openedType = + CS.openOpaqueType(replacedType, CTP_Initialization, locator); assert(openedType); auto *subPattern = cast(pattern)->getSubPattern(); @@ -2252,7 +2265,13 @@ namespace { CS.addConstraint( ConstraintKind::Conversion, subPatternType, openedType, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - return setType(openedType); + + // FIXME [OPAQUE SUPPORT]: the distinction between where we want opaque + // types in opened vs. un-unopened form is *very* tricky. The pattern + // ultimately needs the un-opened type and it gets this from the set + // type of the expression. + CS.setType(pattern, replacedType); + return openedType; } case PatternKind::Tuple: { @@ -2262,7 +2281,7 @@ namespace { // types so long as we have the right number of such types. SmallVector externalEltTypes; if (externalPatternType) { - AnyFunctionType::decomposeInput(externalPatternType, + AnyFunctionType::decomposeTuple(externalPatternType, externalEltTypes); // If we have the wrong number of elements, we may not be able to @@ -2447,7 +2466,7 @@ namespace { return Type(); SmallVector params; - AnyFunctionType::decomposeInput(subPatternType, params); + AnyFunctionType::decomposeTuple(subPatternType, params); // Remove parameter labels; they aren't used when matching cases, // but outright conflicts will be checked during coercion. @@ -2517,10 +2536,8 @@ namespace { std::pair walkToExprPre(Expr *expr) override { // If there are any error expressions in this closure // it wouldn't be possible to infer its type. - if (isa(expr)) { + if (isa(expr)) hasErrorExprs = true; - return {false, nullptr}; - } // Retrieve type variables from references to var decls. if (auto *declRef = dyn_cast(expr)) { @@ -2608,14 +2625,13 @@ namespace { // Try to build the appropriate type for a variadic argument list of // the fresh element type. If that failed, just bail out. - auto array = TypeChecker::getArraySliceType(expr->getLoc(), element); - if (array->hasError()) return element; + auto variadicSeq = VariadicSequenceType::get(element); // Require the operand to be convertible to the array type. CS.addConstraint(ConstraintKind::Conversion, - CS.getType(expr->getSubExpr()), array, + CS.getType(expr->getSubExpr()), variadicSeq, CS.getConstraintLocator(expr)); - return array; + return variadicSeq; } Type visitDynamicTypeExpr(DynamicTypeExpr *expr) { @@ -2681,7 +2697,7 @@ namespace { // arguments together with their inout-ness, instead of a single // ParenExpr or TupleExpr. SmallVector params; - AnyFunctionType::decomposeInput(CS.getType(expr->getArg()), params); + AnyFunctionType::decomposeTuple(CS.getType(expr->getArg()), params); CS.addConstraint(ConstraintKind::ApplicableFunction, FunctionType::get(params, resultType, extInfo), @@ -3121,6 +3137,7 @@ namespace { if (!rootObjectTy || rootObjectTy->hasError()) return Type(); + CS.setType(rootRepr, rootObjectTy); // Allow \Derived.property to be inferred as \Base.property to // simulate a sort of covariant conversion from // KeyPath to KeyPath. @@ -3142,7 +3159,13 @@ namespace { switch (auto kind = component.getKind()) { case KeyPathExpr::Component::Kind::Invalid: break; - + case KeyPathExpr::Component::Kind::CodeCompletion: + // We don't know what the code completion might resolve to, so we are + // creating a new type variable for its result, which might be a hole. + base = CS.createTypeVariable( + resultLocator, + TVO_CanBindToLValue | TVO_CanBindToNoEscape | TVO_CanBindToHole); + break; case KeyPathExpr::Component::Kind::UnresolvedProperty: // This should only appear in resolved ASTs, but we may need to // re-type-check the constraints during failure diagnosis. @@ -3224,7 +3247,7 @@ namespace { break; } case KeyPathExpr::Component::Kind::Identity: - continue; + break; case KeyPathExpr::Component::Kind::DictionaryKey: llvm_unreachable("DictionaryKey only valid in #keyPath"); break; @@ -3238,7 +3261,8 @@ namespace { // If there was an optional chaining component, the end result must be // optional. if (didOptionalChain) { - auto objTy = CS.createTypeVariable(locator, TVO_CanBindToNoEscape); + auto objTy = CS.createTypeVariable(locator, TVO_CanBindToNoEscape | + TVO_CanBindToHole); componentTypeVars.push_back(objTy); auto optTy = OptionalType::get(objTy); @@ -3249,17 +3273,18 @@ namespace { auto baseLocator = CS.getConstraintLocator(E, ConstraintLocator::KeyPathValue); - auto rvalueBase = CS.createTypeVariable(baseLocator, - TVO_CanBindToNoEscape); + auto rvalueBase = CS.createTypeVariable( + baseLocator, TVO_CanBindToNoEscape | TVO_CanBindToHole); CS.addConstraint(ConstraintKind::Equal, base, rvalueBase, locator); // The result is a KeyPath from the root to the end component. // The type of key path depends on the overloads chosen for the key // path components. - auto typeLoc = - CS.getConstraintLocator(locator, ConstraintLocator::KeyPathType); + auto typeLoc = CS.getConstraintLocator( + locator, LocatorPathElt::KeyPathType(rvalueBase)); + Type kpTy = CS.createTypeVariable(typeLoc, TVO_CanBindToNoEscape | - TVO_CanBindToHole); + TVO_CanBindToHole); CS.addKeyPathConstraint(kpTy, root, rvalueBase, componentTypeVars, locator); return kpTy; @@ -3466,7 +3491,7 @@ namespace { auto &CS = CG.getConstraintSystem(); for (const auto &capture : captureList->getCaptureList()) { - SolutionApplicationTarget target(capture.Init); + SolutionApplicationTarget target(capture.PBD); if (CS.generateConstraints(target, FreeTypeVariableBinding::Disallow)) return {false, nullptr}; } @@ -3809,9 +3834,7 @@ bool ConstraintSystem::generateConstraints( // If there is a type that we're expected to convert to, add the conversion // constraint. if (Type convertType = target.getExprConversionType()) { - // Determine whether we know more about the contextual type. ContextualTypePurpose ctp = target.getExprContextualTypePurpose(); - bool isOpaqueReturnType = target.infersOpaqueReturnType(); auto *convertTypeLocator = getConstraintLocator(expr, LocatorPathElt::ContextualType(ctp)); @@ -3851,8 +3874,7 @@ bool ConstraintSystem::generateConstraints( }); } - addContextualConversionConstraint(expr, convertType, ctp, - isOpaqueReturnType); + addContextualConversionConstraint(expr, convertType, ctp); } // For an initialization target, generate constraints for the pattern. @@ -3898,20 +3920,35 @@ bool ConstraintSystem::generateConstraints( /// Generate constraints for each pattern binding entry for (unsigned index : range(patternBinding->getNumPatternEntries())) { - // Type check the pattern. - auto pattern = patternBinding->getPattern(index); + auto *pattern = TypeChecker::resolvePattern( + patternBinding->getPattern(index), dc, /*isStmtCondition=*/true); + + if (!pattern) + return true; + + // Type check the pattern. Note use of `forRawPattern` here instead + // of `forPatternBindingDecl` because resolved `pattern` is not + // associated with `patternBinding`. auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc); Type patternType = TypeChecker::typeCheckPattern(contextualPattern); - auto init = patternBinding->getInit(index); - if (!init) { - llvm_unreachable("Unsupported pattern binding entry"); + // Fail early if pattern couldn't be type-checked. + if (!patternType || patternType->hasError()) + return true; + + if (!patternBinding->isInitialized(index) && + patternBinding->isDefaultInitializable(index) && + pattern->hasStorage()) { + llvm_unreachable("default initialization is unsupported"); } - // Generate constraints for the initialization. - auto target = SolutionApplicationTarget::forInitialization( - init, dc, patternType, pattern, - /*bindPatternVarsOneWay=*/true); + auto init = patternBinding->getInit(index); + auto target = init ? SolutionApplicationTarget::forInitialization( + init, dc, patternType, pattern, + /*bindPatternVarsOneWay=*/true) + : SolutionApplicationTarget::forUninitializedVar( + patternBinding, index, pattern, patternType); + if (generateConstraints(target, FreeTypeVariableBinding::Disallow)) { hadError = true; continue; @@ -3924,14 +3961,28 @@ bool ConstraintSystem::generateConstraints( return hadError; } - case SolutionApplicationTarget::Kind::uninitializedWrappedVar: { - auto *wrappedVar = target.getAsUninitializedWrappedVar(); - auto propertyType = getVarType(wrappedVar); - if (propertyType->hasError()) - return true; + case SolutionApplicationTarget::Kind::uninitializedVar: { + if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) { + auto propertyType = getVarType(wrappedVar); + if (propertyType->hasError()) + return true; - return generateWrappedPropertyTypeConstraints( + return generateWrappedPropertyTypeConstraints( *this, /*initializerType=*/Type(), wrappedVar, propertyType); + } else { + auto pattern = target.getAsUninitializedVar(); + auto locator = getConstraintLocator( + pattern, LocatorPathElt::ContextualType(CTP_Initialization)); + + // Generate constraints to bind all of the internal declarations + // and verify the pattern. + Type patternType = generateConstraints( + pattern, locator, /*shouldBindPatternVarsOneWay*/ true, + target.getPatternBindingOfUninitializedVar(), + target.getIndexOfUninitializedVar()); + + return !patternType; + } } } } @@ -4135,31 +4186,6 @@ void ConstraintSystem::optimizeConstraints(Expr *e) { e->walk(optimizer); } -bool swift::areGenericRequirementsSatisfied( - const DeclContext *DC, GenericSignature sig, - SubstitutionMap Substitutions, bool isExtension) { - - ConstraintSystemOptions Options; - ConstraintSystem CS(const_cast(DC), Options); - auto Loc = CS.getConstraintLocator({}); - - // For every requirement, add a constraint. - for (auto Req : sig->getRequirements()) { - if (auto resolved = Req.subst( - QuerySubstitutionMap{Substitutions}, - LookUpConformanceInModule(DC->getParentModule()))) { - CS.addConstraint(*resolved, Loc); - } else if (isExtension) { - return false; - } - // Unresolved requirements are requirements of the function itself. This - // does not prevent it from being applied. E.g. func foo(x: T). - } - - // Having a solution implies the requirements have been fulfilled. - return CS.solveSingle().hasValue(); -} - struct ResolvedMemberResult::Implementation { llvm::SmallVector AllDecls; unsigned ViableStartIdx; @@ -4248,9 +4274,17 @@ OriginalArgumentList swift::getOriginalArgumentList(Expr *expr) { OriginalArgumentList result; - auto add = [&](Expr *arg, Identifier label, SourceLoc labelLoc) { - if (isa(arg)) { + auto oldTrailingClosureIdx = + expr->getUnlabeledTrailingClosureIndexOfPackedArgument(); + Optional newTrailingClosureIdx; + + auto add = [&](unsigned i, Expr *arg, Identifier label, SourceLoc labelLoc) { + if (isa(arg)) return; + + if (oldTrailingClosureIdx && *oldTrailingClosureIdx == i) { + assert(!newTrailingClosureIdx); + newTrailingClosureIdx = result.args.size(); } if (auto *varargExpr = dyn_cast(arg)) { @@ -4276,24 +4310,25 @@ swift::getOriginalArgumentList(Expr *expr) { if (auto *parenExpr = dyn_cast(expr)) { result.lParenLoc = parenExpr->getLParenLoc(); result.rParenLoc = parenExpr->getRParenLoc(); - result.hasTrailingClosure = parenExpr->hasTrailingClosure(); - add(parenExpr->getSubExpr(), Identifier(), SourceLoc()); + add(0, parenExpr->getSubExpr(), Identifier(), SourceLoc()); } else if (auto *tupleExpr = dyn_cast(expr)) { result.lParenLoc = tupleExpr->getLParenLoc(); result.rParenLoc = tupleExpr->getRParenLoc(); - result.hasTrailingClosure = tupleExpr->hasTrailingClosure(); auto args = tupleExpr->getElements(); auto labels = tupleExpr->getElementNames(); auto labelLocs = tupleExpr->getElementNameLocs(); for (unsigned i = 0, e = args.size(); i != e; ++i) { // Implicit TupleExprs don't always store label locations. - add(args[i], labels[i], + add(i, args[i], labels[i], labelLocs.empty() ? SourceLoc() : labelLocs[i]); } } else { - add(expr, Identifier(), SourceLoc()); + add(0, expr, Identifier(), SourceLoc()); } + assert(oldTrailingClosureIdx.hasValue() == newTrailingClosureIdx.hasValue()); + assert(!newTrailingClosureIdx || *newTrailingClosureIdx < result.args.size()); + result.unlabeledTrailingClosureIdx = newTrailingClosureIdx; return result; } diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index eedffe3faecc6..0ebadffebb342 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -370,8 +370,8 @@ static bool isProtocolExtensionAsSpecializedAs(DeclContext *dc1, // Bind the 'Self' type from the first extension to the type parameter from // opening 'Self' of the second extension. - Type selfType1 = sig1->getGenericParams()[0]; - Type selfType2 = sig2->getGenericParams()[0]; + Type selfType1 = sig1.getGenericParams()[0]; + Type selfType2 = sig2.getGenericParams()[0]; cs.addConstraint(ConstraintKind::Bind, replacements[cast(selfType2->getCanonicalType())], dc1->mapTypeIntoContext(selfType1), diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 1b4eb4f9ea4bd..9e11298478f3d 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" @@ -28,8 +29,8 @@ #include "swift/AST/SourceFile.h" #include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" -#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/CSFix.h" +#include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeChecking.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Compiler.h" @@ -445,11 +446,11 @@ static bool matchCallArgumentsImpl( } // If this parameter does not require an argument, consider applying a - // "fuzzy" match rule that skips this parameter if doing so is the only + // backward-match rule that skips this parameter if doing so is the only // way to successfully match arguments to parameters. if (!parameterRequiresArgument(params, paramInfo, paramIdx) && - param.getPlainType()->getASTContext().LangOpts - .EnableFuzzyForwardScanTrailingClosureMatching && + !param.getPlainType()->getASTContext().LangOpts + .isSwiftVersionAtLeast(6) && anyParameterRequiresArgument( params, paramInfo, paramIdx + 1, nextArgIdx + 1 < numArgs @@ -786,7 +787,9 @@ static bool matchCallArgumentsImpl( unsigned argIdx = 0; for (const auto &binding : parameterBindings) { paramToArgMap.push_back(argIdx); - argIdx += binding.size(); + // Ignore argument bindings that were synthesized due to missing args. + argIdx += llvm::count_if( + binding, [numArgs](unsigned argIdx) { return argIdx < numArgs; }); } } @@ -803,6 +806,10 @@ static bool matchCallArgumentsImpl( // it should move to (toArgIdx). const auto fromArgIdx = binding[paramBindIdx]; + // Ignore argument bindings that were synthesized due to missing args. + if (fromArgIdx >= numArgs) + continue; + // Does nothing for variadic tail. if (params[paramIdx].isVariadic() && paramBindIdx > 0) { assert(args[fromArgIdx].getLabel().empty()); @@ -893,9 +900,9 @@ static bool requiresBothTrailingClosureDirections( if (params.empty()) return false; - // If "fuzzy" matching is disabled, only scan forward. + // If backward matching is disabled, only scan forward. ASTContext &ctx = params.front().getPlainType()->getASTContext(); - if (!ctx.LangOpts.EnableFuzzyForwardScanTrailingClosureMatching) + if (ctx.LangOpts.isSwiftVersionAtLeast(6)) return false; // If there are at least two parameters that meet the backward scan's @@ -1488,6 +1495,31 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( } } + // Detect that there is sync -> async mismatch early on for + // closure argument to avoid re-checking calls if there was + // an overload choice with synchronous parameter of the same + // shape e.g. + // + // func test(_: () -> Void) -> MyStruct {} + // func test(_: () async -> Void) -> MyStruct {} + // + // test({ ... }).... + // + // Synchronous overload is always better in this case so there + // is no need to re-check follow-up ``s and better + // to short-circuit this path early. + if (auto *fnType = paramTy->getAs()) { + if (fnType->isAsync()) { + auto *typeVar = argTy->getAs(); + if (typeVar && typeVar->getImpl().isClosureType()) { + auto *locator = typeVar->getImpl().getLocator(); + auto *closure = castToExpr(locator->getAnchor()); + if (!cs.getClosureType(closure)->isAsync()) + cs.increaseScore(SK_SyncInAsync); + } + } + } + cs.addConstraint( subKind, argTy, paramTy, matchingAutoClosureResult @@ -1522,6 +1554,9 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, SmallVector path; (void)locator.getLocatorParts(path); + while (!path.empty() && path.back().is()) + path.pop_back(); + if (!path.empty()) { // Direct pattern matching between tuple pattern and tuple type. if (path.back().is()) { @@ -1586,7 +1621,6 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, subKind = ConstraintKind::Conversion; break; - case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::Bind: case ConstraintKind::BindParam: case ConstraintKind::BindToPointerType: @@ -1731,7 +1765,6 @@ static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1, } return true; - case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::BridgingConversion: case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: @@ -1978,7 +2011,7 @@ static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor, // synthesized arguments to it. if (argumentTuple) { cs.addConstraint(ConstraintKind::Bind, *argumentTuple, - FunctionType::composeInput(ctx, args, + FunctionType::composeTuple(ctx, args, /*canonicalVararg=*/false), cs.getConstraintLocator(anchor)); } @@ -2037,7 +2070,20 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, return getTypeMatchFailure(locator); } - increaseScore(SK_SyncInAsync); + bool forClosureInArgumentPosition = false; + if (auto last = locator.last()) { + forClosureInArgumentPosition = + last->is() && + isa(locator.trySimplifyToExpr()); + } + + // Since it's possible to infer `async` from the body of a + // closure, score for sync -> async mismatch is increased + // while solver is matching arguments to parameters to + // indicate than solution with such a mismatch is always + // worse than one with synchronous functions on both sides. + if (!forClosureInArgumentPosition) + increaseScore(SK_SyncInAsync); } // A @Sendable function can be a subtype of a non-@Sendable function. @@ -2076,8 +2122,8 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, auto result = matchTypes(func1->getGlobalActor(), func2->getGlobalActor(), ConstraintKind::Equal, subflags, locator); if (result == SolutionKind::Error) return getTypeMatchFailure(locator); - } else if (func1->getGlobalActor()) { - // Cannot remove a global actor. + } else if (func1->getGlobalActor() && !func2->isAsync()) { + // Cannot remove a global actor from a synchronous function. if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); @@ -2120,7 +2166,6 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: case ConstraintKind::OperatorArgumentConversion: - case ConstraintKind::OpaqueUnderlyingType: subKind = ConstraintKind::Subtype; break; @@ -2182,7 +2227,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, }; auto implodeParams = [&](SmallVectorImpl ¶ms) { - auto input = AnyFunctionType::composeInput(getASTContext(), params, + auto input = AnyFunctionType::composeTuple(getASTContext(), params, /*canonicalVararg=*/false); params.clear(); @@ -2273,7 +2318,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, if (shouldAttemptFixes()) { auto *anchor = locator.trySimplifyToExpr(); - if (anchor && isa(anchor) && + if (isa_and_nonnull(anchor) && isSingleTupleParam(ctx, func2Params) && canImplodeParams(func1Params)) { auto *fix = AllowClosureParamDestructuring::create( @@ -2423,6 +2468,12 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, hasLabelingFailures = true; } + // "isolated" can be added as a subtype relation, but otherwise must match. + if (func1Param.isIsolated() != func2Param.isIsolated() && + !(func2Param.isIsolated() && subKind >= ConstraintKind::Subtype)) { + return getTypeMatchFailure(argumentLocator); + } + // FIXME: We should check value ownership too, but it's not completely // trivial because of inout-to-pointer conversions. @@ -2663,14 +2714,25 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2, } } + unsigned impact = 1; + + if (type1->getAnyPointerElementType() && + type2->getAnyPointerElementType()) { + // If this is a pointer <-> pointer conversion of different kind, + // there is a dedicated restriction/fix for that in some cases. + // To accommodate that, let's increase the impact of this fix. + impact += 2; + } else { + // Increase the solution's score for each mismtach this fixes. + impact += mismatches.size() - 1; + } + auto *fix = GenericArgumentsMismatch::create( *this, type1, type2, mismatches, getConstraintLocator(locator)); - if (!recordFix(fix)) { - // Increase the solution's score for each mismtach this fixes. - increaseScore(SK_Fix, mismatches.size() - 1); + if (!recordFix(fix, impact)) return getTypeMatchSuccess(); - } + return result; } return matchDeepTypeArguments(*this, subflags, args1, args2, locator); @@ -3469,9 +3531,10 @@ static bool repairArrayLiteralUsedAsDictionary( if (unwrappedDict->isTypeVariableOrMember()) return false; - if (!conformsToKnownProtocol( - cs.DC, unwrappedDict, - KnownProtocolKind::ExpressibleByDictionaryLiteral)) + if (!TypeChecker::conformsToKnownProtocol( + unwrappedDict, + KnownProtocolKind::ExpressibleByDictionaryLiteral, + cs.DC->getParentModule())) return false; // Ignore any attempts at promoting the value to an optional as even after @@ -4044,11 +4107,23 @@ bool ConstraintSystem::repairFailures( path.pop_back(); // If this is a contextual mismatch between l-value types e.g. // `@lvalue String vs. @lvalue Int`, let's pretend that it's okay. - if (!path.empty() && path.back().is()) { - auto *locator = getConstraintLocator(anchor, path.back()); - conversionsOrFixes.push_back( - IgnoreContextualType::create(*this, lhs, rhs, locator)); - break; + if (!path.empty()) { + if (path.back().is()) { + auto *locator = getConstraintLocator(anchor, path.back()); + conversionsOrFixes.push_back( + IgnoreContextualType::create(*this, lhs, rhs, locator)); + break; + } + + // If this is a function type param type mismatch in any position, + // the mismatch we want to report is for the whole structural type. + auto last = std::find_if( + path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool { + return elt.is(); + }); + + if (last != path.rend()) + break; } LLVM_FALLTHROUGH; @@ -4226,8 +4301,7 @@ bool ConstraintSystem::repairFailures( // it's going to be diagnosed by specialized fix which deals // with generic argument mismatches. if (matchKind == ConstraintKind::BindToPointerType) { - auto *member = rhs->getAs(); - if (!(member && member->getBase()->hasPlaceholder())) + if (!rhs->isPlaceholder()) break; } @@ -4421,6 +4495,10 @@ bool ConstraintSystem::repairFailures( // a conversion to another function type, see `matchFunctionTypes`. if (parentLoc->isForContextualType() || parentLoc->isLastElement()) { + // If either type has a placeholder, consider this fixed. + if (lhs->hasPlaceholder() || rhs->hasPlaceholder()) + return true; + // If there is a fix associated with contextual conversion or // a function type itself, let's ignore argument failure but // increase a score. @@ -4570,6 +4648,12 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::FunctionResult: { + if (lhs->isPlaceholder() || rhs->isPlaceholder()) { + recordAnyTypeVarAsPotentialHole(lhs); + recordAnyTypeVarAsPotentialHole(rhs); + return true; + } + auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1}); // If this is a mismatch between contextual type and (trailing) // closure with explicitly specified result type let's record it @@ -4590,6 +4674,7 @@ bool ConstraintSystem::repairFailures( break; } } + // Handle function result coerce expression wrong type conversion. if (isExpr(anchor)) { auto *fix = @@ -4674,6 +4759,15 @@ bool ConstraintSystem::repairFailures( // mismatches within the same tuple type can be coalesced later. auto index = elt.getAs()->getIndex(); path.pop_back(); + + // Drop the tuple type path elements too, but extract each tuple type first. + if (!path.empty() && path.back().is()) { + rhs = path.back().getAs()->getType(); + path.pop_back(); + lhs = path.back().getAs()->getType(); + path.pop_back(); + } + auto *tupleLocator = getConstraintLocator(locator.getAnchor(), path); // Let this fail if it's a contextual mismatch with sequence element types, @@ -4826,14 +4920,24 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::PatternMatch: { + auto *pattern = elt.castTo().getPattern(); + bool isMemberMatch = + lhs->is() && isa(pattern); + + // If member reference couldn't be resolved, let's allow pattern + // to have holes. + if (rhs->isPlaceholder() && isMemberMatch) { + recordAnyTypeVarAsPotentialHole(lhs); + return true; + } + // If either type is a placeholder, consider this fixed. if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; - // If the left-hand side is a function type and the pattern is an enum - // element pattern, call it a contextual mismatch. - auto pattern = elt.castTo().getPattern(); - if (lhs->is() && isa(pattern)) { + // If member reference didn't match expected pattern, + // let's consider that a contextual mismatch. + if (isMemberMatch) { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); @@ -5061,7 +5165,6 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, return formUnsolvedResult(); } - case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::BindOverload: @@ -5143,6 +5246,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, llvm_unreachable("type variables should have already been handled by now"); case TypeKind::DependentMember: { + // If types are identical, let's consider this constraint solved + // even though they are dependent members, they would be resolved + // to the same concrete type. + if (desugar1->isEqual(desugar2)) + return getTypeMatchSuccess(); + // If one of the dependent member types has no type variables, // this comparison is effectively illformed, because dependent // member couldn't be simplified down to the actual type, and @@ -5170,9 +5279,15 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, } case TypeKind::Tuple: { + // Add each tuple type to the locator before matching the element types. + // This is useful for diagnostics, because the error message can use the + // full tuple type for several element mismatches. Use the original types + // to preserve sugar such as typealiases. + auto tmpTupleLoc = locator.withPathElement(LocatorPathElt::TupleType(type1)); + auto tupleLoc = tmpTupleLoc.withPathElement(LocatorPathElt::TupleType(type2)); auto result = matchTupleTypes(cast(desugar1), cast(desugar2), - kind, subflags, locator); + kind, subflags, tupleLoc); if (result != SolutionKind::Error) return result; @@ -5927,7 +6042,7 @@ ConstraintSystem::simplifyConstructionConstraint( } // Tuple construction is simply tuple conversion. - Type argType = AnyFunctionType::composeInput(getASTContext(), + Type argType = AnyFunctionType::composeTuple(getASTContext(), fnType->getParams(), /*canonicalVararg=*/false); Type resultType = fnType->getResult(); @@ -6108,7 +6223,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( switch (kind) { case ConstraintKind::SelfObjectOfProtocol: { auto conformance = TypeChecker::containsProtocol( - type, protocol, DC, /*skipConditionalRequirements=*/true); + type, protocol, DC->getParentModule(), + /*skipConditionalRequirements=*/true); if (conformance) { return recordConformance(conformance); } @@ -6117,7 +6233,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( case ConstraintKind::LiteralConformsTo: { // Check whether this type conforms to the protocol. auto conformance = DC->getParentModule()->lookupConformance( - type, protocol); + type, protocol, /*allowMissing=*/true); if (conformance) { return recordConformance(conformance); } @@ -6237,7 +6353,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( if (auto rawValue = isRawRepresentable(*this, type)) { if (!rawValue->isTypeVariableOrMember() && - TypeChecker::conformsToProtocol(rawValue, protocol, DC)) { + TypeChecker::conformsToProtocol(rawValue, protocol, + DC->getParentModule())) { auto *fix = UseRawValue::create(*this, type, protocolTy, loc); return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } @@ -6250,15 +6367,26 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( auto signature = path[path.size() - 2] .castTo() .getSignature(); - auto requirement = signature->getRequirements()[req->getIndex()]; + auto requirement = signature.getRequirements()[req->getIndex()]; auto *memberLoc = getConstraintLocator(anchor, path.front()); - auto *memberRef = findResolvedMemberRef(memberLoc); + auto overload = findSelectedOverloadFor(memberLoc); // To figure out what is going on here we need to wait until // member overload is set in the constraint system. - if (!memberRef) + if (!overload) { + // If it's not allowed to generate new constraints + // there is no way to control re-activation, so this + // check has to fail. + if (!flags.contains(TMF_GenerateConstraints)) + return SolutionKind::Error; + return formUnsolved(/*activate=*/true); + } + + auto *memberRef = overload->choice.getDeclOrNull(); + if (!memberRef) + return SolutionKind::Error; // If this is a `Self` conformance requirement from a static member // reference on a protocol metatype, let's produce a tailored diagnostic. @@ -6365,6 +6493,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo( // If the type doesn't conform, let's check whether // an Optional or Unsafe{Mutable}Pointer from it would. + // If the current score is equal to the best score, fail without checking + // implicit conversions, because an implicit conversion would lead to a + // worse score anyway. + if (solverState && solverState->BestScore && CurrentScore == *solverState->BestScore) + return SolutionKind::Error; + SmallVector typesToCheck; // T -> Optional @@ -6477,7 +6611,8 @@ static ConstraintFix *maybeWarnAboutExtraneousCast( SmallVector toOptionals, ConstraintSystem::TypeMatchOptions flags, ConstraintLocatorBuilder locator) { - if (flags.contains(ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix)) + auto last = locator.last(); + if (last && last->is()) return nullptr; // Both types have to be fixed. @@ -6492,7 +6627,9 @@ static ConstraintFix *maybeWarnAboutExtraneousCast( if (!castExpr) return nullptr; - unsigned extraOptionals = fromOptionals.size() - toOptionals.size(); + // "from" could be less optional than "to" e.g. `0 as Any?`, so + // we need to store the difference as a signed integer. + int extraOptionals = fromOptionals.size() - toOptionals.size(); // Removing the optionality from to type when the force cast expr is an IUO. const auto *const TR = castExpr->getCastTypeRepr(); if (isExpr(anchor) && TR && @@ -6511,8 +6648,12 @@ static ConstraintFix *maybeWarnAboutExtraneousCast( } // Except for forced cast expressions, if optionals are more than a single - // level difference, we don't need to record any fix. - if (!isExpr(anchor) && extraOptionals > 1) + // level difference or there is a single level between the types but an extra + // level of optional is added to subexpr via OptionalEvaluationExpr, we don't + // need to record any fix. + if (!isExpr(anchor) && + (extraOptionals > 1 || + isExpr(castExpr->getSubExpr()))) return nullptr; // Always succeed @@ -6648,9 +6789,11 @@ ConstraintSystem::simplifyCheckedCastConstraint( case CheckedCastKind::ArrayDowncast: { auto fromBaseType = *isArrayType(fromType); auto toBaseType = *isArrayType(toType); - - auto result = simplifyCheckedCastConstraint( - fromBaseType, toBaseType, subflags | TMF_ApplyingFix, locator); + + auto elementLocator = + locator.withPathElement(LocatorPathElt::GenericArgument(0)); + auto result = simplifyCheckedCastConstraint(fromBaseType, toBaseType, + subflags, elementLocator); attemptRecordCastFixIfSolved(result); return result; } @@ -6662,13 +6805,16 @@ ConstraintSystem::simplifyCheckedCastConstraint( Type toKeyType, toValueType; std::tie(toKeyType, toValueType) = *isDictionaryType(toType); - if (simplifyCheckedCastConstraint(fromKeyType, toKeyType, - subflags | TMF_ApplyingFix, - locator) == SolutionKind::Error) + auto keyLocator = + locator.withPathElement(LocatorPathElt::GenericArgument(0)); + if (simplifyCheckedCastConstraint(fromKeyType, toKeyType, subflags, + keyLocator) == SolutionKind::Error) return SolutionKind::Error; - auto result = simplifyCheckedCastConstraint( - fromValueType, toValueType, subflags | TMF_ApplyingFix, locator); + auto valueLocator = + locator.withPathElement(LocatorPathElt::GenericArgument(1)); + auto result = simplifyCheckedCastConstraint(fromValueType, toValueType, + subflags, valueLocator); attemptRecordCastFixIfSolved(result); return result; } @@ -6676,8 +6822,11 @@ ConstraintSystem::simplifyCheckedCastConstraint( case CheckedCastKind::SetDowncast: { auto fromBaseType = *isSetType(fromType); auto toBaseType = *isSetType(toType); - auto result = simplifyCheckedCastConstraint( - fromBaseType, toBaseType, subflags | TMF_ApplyingFix, locator); + + auto elementLocator = + locator.withPathElement(LocatorPathElt::GenericArgument(0)); + auto result = simplifyCheckedCastConstraint(fromBaseType, toBaseType, + subflags, elementLocator); attemptRecordCastFixIfSolved(result); return result; } @@ -6829,7 +6978,7 @@ ConstraintSystem::simplifyFunctionComponentConstraint( ConstraintLocator::PathElementKind locKind; if (kind == ConstraintKind::FunctionInput) { - type = AnyFunctionType::composeInput(getASTContext(), + type = AnyFunctionType::composeTuple(getASTContext(), funcTy->getParams(), /*canonicalVararg=*/false); locKind = ConstraintLocator::FunctionArgument; @@ -6907,8 +7056,7 @@ allFromConditionalConformances(DeclContext *DC, Type baseTy, if (auto *protocol = candidateDC->getSelfProtocolDecl()) { SmallVector conformances; - if (!NTD->lookupConformance(DC->getParentModule(), protocol, - conformances)) + if (!NTD->lookupConformance(protocol, conformances)) return false; // This is opportunistic, there should be a way to narrow the @@ -7174,7 +7322,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, // Dig out the instance type and figure out what members of the instance type // we are going to see. auto baseTy = candidate.getBaseType(); - auto baseObjTy = baseTy->getRValueType(); + const auto baseObjTy = baseTy->getRValueType(); bool hasInstanceMembers = false; bool hasInstanceMethods = false; @@ -7221,18 +7369,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, hasInstanceMethods = true; } - // If our base is an existential type, we can't make use of any - // member whose signature involves associated types. - if (instanceTy->isExistentialType()) { - if (auto *proto = decl->getDeclContext()->getSelfProtocolDecl()) { - if (!proto->isAvailableInExistential(decl)) { - result.addUnviable(candidate, - MemberLookupResult::UR_UnavailableInExistential); - return; - } - } - } - // If the invocation's argument expression has a favored type, // use that information to determine whether a specific overload for // the candidate should be favored. @@ -7244,7 +7380,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, if (!ctor->isGenericContext()) { auto args = ctor->getMethodInterfaceType() ->castTo()->getParams(); - auto argType = AnyFunctionType::composeInput(getASTContext(), args, + auto argType = AnyFunctionType::composeTuple(getASTContext(), args, /*canonicalVarargs=*/false); if (argType->isEqual(favoredType)) if (!isDeclUnavailable(decl, memberLocator)) @@ -7252,6 +7388,20 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, } } + const auto isUnsupportedExistentialMemberAccess = [&] { + // If our base is an existential type, we can't make use of any + // member whose signature involves associated types. + if (instanceTy->isExistentialType()) { + if (auto *proto = decl->getDeclContext()->getSelfProtocolDecl()) { + if (!proto->isAvailableInExistential(decl)) { + return true; + } + } + } + + return false; + }; + // See if we have an instance method, instance member or static method, // and check if it can be accessed on our base type. @@ -7265,20 +7415,35 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, ? candidate : OverloadChoice(instanceTy, decl, FunctionRefKind::SingleApply); - // If this is an instance member referenced from metatype - // let's add unviable result to the set because it could be - // either curried reference or an invalid call. - // - // New candidate shouldn't affect performance because such - // choice would only be attempted when solver is in diagnostic mode. - result.addUnviable(choice, MemberLookupResult::UR_InstanceMemberOnType); - - bool invalidMethodRef = isa(decl) && !hasInstanceMethods; - bool invalidMemberRef = !isa(decl) && !hasInstanceMembers; - // If this is definitely an invalid way to reference a method or member - // on the metatype, let's stop here. - if (invalidMethodRef || invalidMemberRef) + + const bool invalidMethodRef = isa(decl) && !hasInstanceMethods; + const bool invalidMemberRef = !isa(decl) && !hasInstanceMembers; + + if (invalidMethodRef || invalidMemberRef) { + // If this is definitely an invalid way to reference a method or member + // on the metatype, let's stop here. + result.addUnviable(choice, + MemberLookupResult::UR_InstanceMemberOnType); return; + } else if (isUnsupportedExistentialMemberAccess()) { + // If the member reference itself is legal, but it turns out to be an + // unsupported existential member access, do not make further + // assumptions about the correctness of a potential call -- let + // the unsupported member access error prevail. + result.addUnviable(candidate, + MemberLookupResult::UR_UnavailableInExistential); + return; + } else { + // Otherwise, still add an unviable result to the set, because it + // could be an invalid call that was supposed to be performed on an + // instance of the type. + // + // New candidate shouldn't affect performance because such + // choice would only be attempted when solver is in diagnostic mode. + result.addUnviable(choice, + MemberLookupResult::UR_InstanceMemberOnType); + + } } // If the underlying type of a typealias is fully concrete, it is legal @@ -7329,6 +7494,12 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, } } + if (isUnsupportedExistentialMemberAccess()) { + result.addUnviable(candidate, + MemberLookupResult::UR_UnavailableInExistential); + return; + } + // If we have an rvalue base, make sure that the result isn't 'mutating' // (only valid on lvalues). if (!baseTy->is() && @@ -7575,7 +7746,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, auto *SD = cast(candidate.getDecl()); bool isKeyPathBased = isValidKeyPathDynamicMemberLookup(SD); - if (isValidStringDynamicMemberLookup(SD, DC) || isKeyPathBased) + if (isValidStringDynamicMemberLookup(SD, DC->getParentModule()) || + isKeyPathBased) result.addViable(OverloadChoice::getDynamicMemberLookup( baseTy, SD, name, isKeyPathBased)); } @@ -7631,7 +7803,7 @@ static bool isNonFinalClass(Type type) { type = dynamicSelf->getSelfType(); if (auto classDecl = type->getClassOrBoundGenericClass()) - return !classDecl->isFinal(); + return !classDecl->isSemanticallyFinal(); if (auto archetype = type->getAs()) if (auto super = archetype->getSuperclass()) @@ -8102,6 +8274,13 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (instanceTy->isAny() || instanceTy->isAnyObject()) impact += 5; + // Increasing the impact for missing member in any argument position so it + // doesn't affect situations where there are another fixes involved. + auto *anchorExpr = getAsExpr(locator->getAnchor()); + if (anchorExpr && isArgumentExpr(anchorExpr)) { + impact += 5; + } + if (recordFix(fix, impact)) return SolutionKind::Error; @@ -8118,6 +8297,13 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( // `key path` constraint can't be retired until all components // are simplified. addTypeVariableConstraintsToWorkList(memberTypeVar); + } else if (locator->isLastElement()) { + // Let's handle member patterns specifically because they use + // equality instead of argument application constraint, so allowing + // them to bind member could mean missing valid hole positions in + // the pattern. + assignFixedType(memberTypeVar, + PlaceholderType::get(getASTContext(), memberTypeVar)); } else { recordPotentialHole(memberTypeVar); } @@ -8145,7 +8331,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( functionRefKind, locator, /*includeInaccessibleMembers*/ true); - // If uwrapped type still couldn't find anything for a given name, + // If unwrapped type still couldn't find anything for a given name, // let's fallback to a "not such member" fix. if (result.ViableCandidates.empty() && result.UnviableCandidates.empty()) return fixMissingMember(origBaseTy, memberTy, locator); @@ -8610,6 +8796,22 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, auto *closure = castToExpr(closureLocator->getAnchor()); auto *inferredClosureType = getClosureType(closure); + // Let's look through all optionals associated with contextual + // type to make it possible to infer parameter/result type of + // the closure faster e.g.: + // + // func test(_: ((Int) -> Void)?) { + // ... + // } + // + // test { $0 + ... } + // + // In this case dropping optionality from contextual type + // `((Int) -> Void)?` allows `resolveClosure` to infer type + // of `$0` directly (via `getContextualParamAt`) instead of + // having to use type variable inference mechanism. + contextualType = contextualType->lookThroughAllOptionalTypes(); + auto getContextualParamAt = [&contextualType, &inferredClosureType]( unsigned index) -> Optional { @@ -8670,11 +8872,16 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, auto param = inferredClosureType->getParams()[i]; auto *paramDecl = paramList->get(i); - // In case of anonymous parameters let's infer flags from context - // that helps to infer variadic and inout earlier. - if (closure->hasAnonymousClosureVars()) { - if (auto contextualParam = getContextualParamAt(i)) - param = param.withFlags(contextualParam->getParameterFlags()); + // In case of anonymous or name-only parameters, let's infer inout/variadic + // flags from context, that helps to propagate type information into the + // internal type of the parameter and reduces inference solver has to make. + if (!paramDecl->getTypeRepr()) { + if (auto contextualParam = getContextualParamAt(i)) { + auto flags = param.getParameterFlags(); + param = + param.withFlags(flags.withInOut(contextualParam->isInOut()) + .withVariadic(contextualParam->isVariadic())); + } } if (paramDecl->hasAttachedPropertyWrapper()) { @@ -8682,8 +8889,18 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, Type wrappedValueType; if (paramDecl->hasImplicitPropertyWrapper()) { - backingType = getContextualParamAt(i)->getPlainType(); - wrappedValueType = createTypeVariable(getConstraintLocator(locator), + if (auto contextualType = getContextualParamAt(i)) { + backingType = contextualType->getPlainType(); + } else { + // There may not be a contextual parameter type if the contextual + // type is not a function type or if closure body declares too many + // parameters. + auto *paramLoc = + getConstraintLocator(closure, LocatorPathElt::TupleElement(i)); + backingType = createTypeVariable(paramLoc, TVO_CanBindToHole); + } + + wrappedValueType = createTypeVariable(getConstraintLocator(paramDecl), TVO_CanBindToHole | TVO_CanBindToLValue); } else { auto *wrapperAttr = paramDecl->getAttachedPropertyWrappers().front(); @@ -8741,7 +8958,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, // If external parameter is variadic it translates into an array in // the body of the closure. internalType = - param.isVariadic() ? ArraySliceType::get(typeVar) : Type(typeVar); + param.isVariadic() ? VariadicSequenceType::get(typeVar) : Type(typeVar); auto externalType = param.getOldType(); @@ -8800,7 +9017,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, // generate constraints for it now. auto &ctx = getASTContext(); if (shouldTypeCheckInEnclosingExpression(closure)) { - if (generateConstraints(closure, closureType->getResult())) + if (generateConstraints(closure)) return false; } else if (!hasExplicitResult(closure)) { // If this closure has an empty body and no explicit result type @@ -8879,38 +9096,6 @@ ConstraintSystem::simplifyDynamicTypeOfConstraint( return SolutionKind::Error; } -ConstraintSystem::SolutionKind -ConstraintSystem::simplifyOpaqueUnderlyingTypeConstraint(Type type1, Type type2, - TypeMatchOptions flags, - ConstraintLocatorBuilder locator) { - // Open the second type, which must be an opaque archetype, to try to - // infer the first type using its constraints. - auto opaque2 = type2->castTo(); - - // Open the generic signature of the opaque decl, and bind the "outer" generic - // params to our context. The remaining axes of freedom on the type variable - // corresponding to the underlying type should be the constraints on the - // underlying return type. - OpenedTypeMap replacements; - openGeneric(DC, opaque2->getBoundSignature(), locator, replacements); - - auto underlyingTyVar = openType(opaque2->getInterfaceType(), - replacements); - assert(underlyingTyVar); - - if (auto dcSig = DC->getGenericSignatureOfContext()) { - for (auto param : dcSig->getGenericParams()) { - addConstraint(ConstraintKind::Bind, - openType(param, replacements), - DC->mapTypeIntoContext(param), - locator); - } - } - - addConstraint(ConstraintKind::Equal, type1, underlyingTyVar, locator); - return getTypeMatchSuccess(); -} - ConstraintSystem::SolutionKind ConstraintSystem::simplifyBridgingConstraint(Type type1, Type type2, @@ -9400,7 +9585,12 @@ ConstraintSystem::simplifyKeyPathConstraint( case KeyPathExpr::Component::Kind::Invalid: case KeyPathExpr::Component::Kind::Identity: break; - + + case KeyPathExpr::Component::Kind::CodeCompletion: { + anyComponentsUnresolved = true; + capability = ReadOnly; + break; + } case KeyPathExpr::Component::Kind::Property: case KeyPathExpr::Component::Kind::Subscript: case KeyPathExpr::Component::Kind::UnresolvedProperty: @@ -10202,7 +10392,6 @@ lookupDynamicCallableMethods(Type type, ConstraintSystem &CS, const ConstraintLocatorBuilder &locator, Identifier argumentName, bool hasKeywordArgs) { auto &ctx = CS.getASTContext(); - auto decl = type->getAnyNominal(); DeclNameRef methodName({ ctx, ctx.Id_dynamicallyCall, { argumentName } }); auto matches = CS.performMemberLookup( ConstraintKind::ValueMember, methodName, type, @@ -10212,7 +10401,8 @@ lookupDynamicCallableMethods(Type type, ConstraintSystem &CS, auto candidates = matches.ViableCandidates; auto filter = [&](OverloadChoice choice) { auto cand = cast(choice.getDecl()); - return !isValidDynamicCallableMethod(cand, decl, hasKeywordArgs); + return !isValidDynamicCallableMethod(cand, CS.DC->getParentModule(), + hasKeywordArgs); }; candidates.erase( std::remove_if(candidates.begin(), candidates.end(), filter), @@ -10595,12 +10785,13 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( auto *fix = GenericArgumentsMismatch::create(*this, ptr1, ptr2, {0}, getConstraintLocator(locator)); + // Treat this as a contextual type mismatch. + unsigned baseImpact = 2; // It's possible to implicitly promote pointer into an optional // before matching base types if other side is an optional, so // score needs to account for number of such promotions. int optionalWraps = baseType2.getInt() - baseType1.getInt(); - return recordFix(fix, - /*impact=*/1 + abs(optionalWraps)) + return recordFix(fix, baseImpact + abs(optionalWraps)) ? SolutionKind::Error : SolutionKind::Solved; }; @@ -11340,7 +11531,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowAlwaysSucceedCheckedCast: case FixKind::AllowInvalidStaticMemberRefOnProtocolMetatype: case FixKind::AllowWrappedValueMismatch: - case FixKind::RemoveExtraneousArguments: { + case FixKind::RemoveExtraneousArguments: + case FixKind::SpecifyTypeForPlaceholder: { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } @@ -11393,6 +11585,33 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( })) impact += 3; + // Passing a closure to a parameter that doesn't expect one should + // be scored lower because there might be an overload that expects + // a closure but has other issues e.g. wrong number of parameters. + if (!type2->lookThroughAllOptionalTypes()->is()) { + auto argument = simplifyLocatorToAnchor(fix->getLocator()); + if (isExpr(argument)) { + impact += 2; + } + } + + // De-prioritize `Builtin.RawPointer` and `OpaquePointer` parameters + // because they usually clash with generic parameter mismatches e.g. + // + // let ptr: UnsafePointer = ... + // _ = UnsafePointer(ups) + // + // Here initializer overloads have both `Builtin.RawPointer` and + // `OpaquePointer` variants, but the actual issue is that generic argument + // `String` doesn't match `Int`. + { + if (type2->is()) + impact += 1; + + if (type2->getAnyNominal() == getASTContext().getOpaquePointerDecl()) + impact += 1; + } + return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; } @@ -11531,10 +11750,6 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, case ConstraintKind::OperatorArgumentConversion: return addArgumentConversionConstraintImpl(kind, first, second, locator); - case ConstraintKind::OpaqueUnderlyingType: - return simplifyOpaqueUnderlyingTypeConstraint(first, second, - subflags, locator); - case ConstraintKind::BridgingConversion: return simplifyBridgingConstraint(first, second, subflags, locator); @@ -11817,8 +12032,7 @@ void ConstraintSystem::addConstraint(ConstraintKind kind, Type first, } void ConstraintSystem::addContextualConversionConstraint( - Expr *expr, Type conversionType, ContextualTypePurpose purpose, - bool isOpaqueReturnType) { + Expr *expr, Type conversionType, ContextualTypePurpose purpose) { if (conversionType.isNull()) return; @@ -11827,11 +12041,13 @@ void ConstraintSystem::addContextualConversionConstraint( switch (purpose) { case CTP_ReturnStmt: case CTP_ReturnSingleExpr: - case CTP_Initialization: - if (isOpaqueReturnType) - constraintKind = ConstraintKind::OpaqueUnderlyingType; + case CTP_Initialization: { + if (conversionType->is()) + constraintKind = ConstraintKind::Equal; + // Alternatively, we might have a nested opaque archetype, e.g. `(some P)?`. + // In that case, we want `ConstraintKind::Conversion`. break; - + } case CTP_CallArgument: constraintKind = ConstraintKind::ArgumentConversion; break; @@ -11867,8 +12083,9 @@ void ConstraintSystem::addContextualConversionConstraint( // Add the constraint. auto *convertTypeLocator = getConstraintLocator(expr, LocatorPathElt::ContextualType(purpose)); - addConstraint(constraintKind, getType(expr), conversionType, - convertTypeLocator, /*isFavored*/ true); + auto openedType = openOpaqueType(conversionType, purpose, convertTypeLocator); + addConstraint(constraintKind, getType(expr), openedType, convertTypeLocator, + /*isFavored*/ true); } void ConstraintSystem::addFixConstraint(ConstraintFix *fix, ConstraintKind kind, @@ -11937,8 +12154,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { case ConstraintKind::Subtype: case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: - case ConstraintKind::OperatorArgumentConversion: - case ConstraintKind::OpaqueUnderlyingType: { + case ConstraintKind::OperatorArgumentConversion: { // Relational constraints. // If there is a fix associated with this constraint, apply it. diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index e1961547f5216..490bc1cf0ac6f 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -175,6 +175,9 @@ Solution ConstraintSystem::finalize() { for (auto &nodeType : NodeTypes) { solution.nodeTypes.insert(nodeType); } + for (auto &keyPathComponentType : KeyPathComponentTypes) { + solution.keyPathComponentTypes.insert(keyPathComponentType); + } // Remember contextual types. solution.contextualTypes.assign( @@ -257,6 +260,11 @@ void ConstraintSystem::applySolution(const Solution &solution) { setType(nodeType.first, nodeType.second); } + for (auto &nodeType : solution.keyPathComponentTypes) { + setType(nodeType.getFirst().first, nodeType.getFirst().second, + nodeType.getSecond()); + } + // Add the contextual types. for (const auto &contextualType : solution.contextualTypes) { if (!getContextualTypeInfo(contextualType.first)) { @@ -414,6 +422,11 @@ ConstraintSystem::SolverState::~SolverState() { "Expected constraint system to have this solver state!"); CS.solverState = nullptr; + // If constraint system ended up being in an invalid state + // let's just drop the state without attempting to rollback. + if (CS.inInvalidState()) + return; + // Make sure that all of the retired constraints have been returned // to constraint system. assert(!hasRetiredConstraints()); @@ -486,6 +499,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numOpenedExistentialTypes = cs.OpenedExistentialTypes.size(); numDefaultedConstraints = cs.DefaultedConstraints.size(); numAddedNodeTypes = cs.addedNodeTypes.size(); + numAddedKeyPathComponentTypes = cs.addedKeyPathComponentTypes.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); numResultBuilderTransformed = cs.resultBuilderTransformed.size(); @@ -504,6 +518,10 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) } ConstraintSystem::SolverScope::~SolverScope() { + // Don't attempt to rollback from an incorrect state. + if (cs.inInvalidState()) + return; + // Erase the end of various lists. while (cs.TypeVariables.size() > numTypeVariables) cs.TypeVariables.pop_back(); @@ -567,6 +585,19 @@ ConstraintSystem::SolverScope::~SolverScope() { } truncate(cs.addedNodeTypes, numAddedNodeTypes); + // Remove any node types we registered. + for (unsigned i : reverse(range(numAddedKeyPathComponentTypes, + cs.addedKeyPathComponentTypes.size()))) { + auto KeyPath = std::get<0>(cs.addedKeyPathComponentTypes[i]); + auto KeyPathIndex = std::get<1>(cs.addedKeyPathComponentTypes[i]); + if (Type oldType = std::get<2>(cs.addedKeyPathComponentTypes[i])) { + cs.KeyPathComponentTypes[{KeyPath, KeyPathIndex}] = oldType; + } else { + cs.KeyPathComponentTypes.erase({KeyPath, KeyPathIndex}); + } + } + truncate(cs.addedKeyPathComponentTypes, numAddedKeyPathComponentTypes); + /// Remove any builder transformed closures. truncate(cs.resultBuilderTransformed, numResultBuilderTransformed); @@ -884,8 +915,8 @@ void ConstraintSystem::shrink(Expr *expr) { return isArithmeticExprOfLiterals(postfix->getArg()); if (auto binary = dyn_cast(expr)) - return isArithmeticExprOfLiterals(binary->getArg()->getElement(0)) && - isArithmeticExprOfLiterals(binary->getArg()->getElement(1)); + return isArithmeticExprOfLiterals(binary->getLHS()) && + isArithmeticExprOfLiterals(binary->getRHS()); return isa(expr) || isa(expr); } @@ -1362,6 +1393,13 @@ void ConstraintSystem::solveImpl(SmallVectorImpl &solutions) { if (failedConstraint) return; + // Attempt to solve a constraint system already in an invalid + // state should be immediately aborted. + if (inInvalidState()) { + solutions.clear(); + return; + } + // Allocate new solver scope, so constraint system // could be restored to its original state afterwards. // Otherwise there is a risk that some of the constraints @@ -1404,6 +1442,14 @@ void ConstraintSystem::solveImpl(SmallVectorImpl &solutions) { // or error, which means that current path is inconsistent. { auto result = advance(step.get(), prevFailed); + + // If execution of this step let constraint system in an + // invalid state, let's drop all of the solutions and abort. + if (inInvalidState()) { + solutions.clear(); + return; + } + switch (result.getKind()) { // It was impossible to solve this step, let's note that // for followup steps, to propogate the error. @@ -1890,7 +1936,7 @@ void DisjunctionChoiceProducer::partitionGenericOperators( return refined->inheritsFrom(protocol); return (bool)TypeChecker::conformsToProtocol(nominal->getDeclaredType(), protocol, - nominal->getDeclContext()); + CS.DC->getParentModule()); }; // Gather Numeric and Sequence overloads into separate buckets. @@ -1938,15 +1984,18 @@ void DisjunctionChoiceProducer::partitionGenericOperators( if (argType->isTypeVariableOrMember()) continue; - if (conformsToKnownProtocol(CS.DC, argType, - KnownProtocolKind::AdditiveArithmetic)) { + if (TypeChecker::conformsToKnownProtocol( + argType, KnownProtocolKind::AdditiveArithmetic, + CS.DC->getParentModule())) { first = std::copy(numericOverloads.begin(), numericOverloads.end(), first); numericOverloads.clear(); break; } - if (conformsToKnownProtocol(CS.DC, argType, KnownProtocolKind::Sequence)) { + if (TypeChecker::conformsToKnownProtocol( + argType, KnownProtocolKind::Sequence, + CS.DC->getParentModule())) { first = std::copy(sequenceOverloads.begin(), sequenceOverloads.end(), first); sequenceOverloads.clear(); diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 88ce76e44ff81..b0a75d00a0f37 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -282,11 +282,11 @@ StepResult DependentComponentSplitterStep::take(bool prevFailed) { for (auto index : swift::indices(indices)) { dependsOnSolutions.push_back(&(*dependsOnSets[index])[indices[index]]); } + ContextualSolutions.push_back(std::make_unique>()); - followup.push_back( - std::make_unique(CS, Index, Constraints, Component, - std::move(dependsOnSolutions), - Solutions)); + followup.push_back(std::make_unique( + CS, Index, Constraints, Component, std::move(dependsOnSolutions), + *ContextualSolutions.back())); } while (nextCombination(dependsOnSetsRef, indices)); /// Wait until all of the component steps are done. @@ -294,6 +294,10 @@ StepResult DependentComponentSplitterStep::take(bool prevFailed) { } StepResult DependentComponentSplitterStep::resume(bool prevFailed) { + for (auto &ComponentStepSolutions : ContextualSolutions) { + Solutions.append(std::make_move_iterator(ComponentStepSolutions->begin()), + std::make_move_iterator(ComponentStepSolutions->end())); + } return done(/*isSuccess=*/!Solutions.empty()); } @@ -358,22 +362,36 @@ StepResult ComponentStep::take(bool prevFailed) { return finalize(/*isSuccess=*/false); } + auto printConstraints = [&](const ConstraintList &constraints) { + for (auto &constraint : constraints) + constraint.print(getDebugLogger(), &CS.getASTContext().SourceMgr); + }; + // If we don't have any disjunction or type variable choices left, we're done // solving. Make sure we don't have any unsolved constraints left over, using - // report_fatal_error to make sure we trap in release builds instead of - // potentially miscompiling. + // report_fatal_error to make sure we trap in debug builds and fail the step + // in release builds. if (!CS.ActiveConstraints.empty()) { - CS.print(llvm::errs()); - llvm::report_fatal_error("Active constraints left over?"); + if (CS.isDebugMode()) { + getDebugLogger() << "(failed due to remaining active constraints:\n"; + printConstraints(CS.ActiveConstraints); + getDebugLogger() << ")\n"; + } + + CS.InvalidState = true; + return finalize(/*isSuccess=*/false); } + if (!CS.solverState->allowsFreeTypeVariables()) { if (!CS.InactiveConstraints.empty()) { - CS.print(llvm::errs()); - llvm::report_fatal_error("Inactive constraints left over?"); - } - if (CS.hasFreeTypeVariables()) { - CS.print(llvm::errs()); - llvm::report_fatal_error("Free type variables left over?"); + if (CS.isDebugMode()) { + getDebugLogger() << "(failed due to remaining inactive constraints:\n"; + printConstraints(CS.InactiveConstraints); + getDebugLogger() << ")\n"; + } + + CS.InvalidState = true; + return finalize(/*isSuccess=*/false); } } @@ -538,8 +556,8 @@ StepResult DisjunctionStep::resume(bool prevFailed) { } bool IsDeclRefinementOfRequest::evaluate(Evaluator &evaluator, - ValueDecl *declA, - ValueDecl *declB) const { + ValueDecl *declA, + ValueDecl *declB) const { auto *typeA = declA->getInterfaceType()->getAs(); auto *typeB = declB->getInterfaceType()->getAs(); @@ -551,9 +569,9 @@ bool IsDeclRefinementOfRequest::evaluate(Evaluator &evaluator, // Substitute generic parameters with their archetypes in each generic function. Type substTypeA = typeA->substGenericArgs( - genericSignatureA->getGenericEnvironment()->getForwardingSubstitutionMap()); + genericSignatureA.getGenericEnvironment()->getForwardingSubstitutionMap()); Type substTypeB = typeB->substGenericArgs( - genericSignatureB->getGenericEnvironment()->getForwardingSubstitutionMap()); + genericSignatureB.getGenericEnvironment()->getForwardingSubstitutionMap()); // Attempt to substitute archetypes from the second type with archetypes in the // same structural position in the first type. @@ -578,9 +596,8 @@ bool IsDeclRefinementOfRequest::evaluate(Evaluator &evaluator, return false; auto result = TypeChecker::checkGenericArguments( - declA->getDeclContext(), SourceLoc(), SourceLoc(), typeB, - genericSignatureB->getGenericParams(), - genericSignatureB->getRequirements(), + declA->getDeclContext()->getParentModule(), + genericSignatureB.getRequirements(), QueryTypeSubstitutionMap{ substMap }); if (result != RequirementCheckResult::Success) @@ -636,58 +653,6 @@ bool DisjunctionStep::shouldSkip(const DisjunctionChoice &choice) const { } } - // If the solver already found a solution with a choice that did not - // introduce any conversions (i.e., the score is not worse than the - // current score), we can skip any generic operators with conformance - // requirements that are not satisfied by any known argument types. - auto argFnType = CS.getAppliedDisjunctionArgumentFunction(Disjunction); - auto checkRequirementsEarly = [&]() -> bool { - auto bestScore = getBestScore(Solutions); - if (!(bestScore && choice.isGenericOperator() && argFnType)) - return false; - - auto currentScore = getCurrentScore(); - for (unsigned i = 0; i < NumScoreKinds; ++i) { - if (i == SK_NonDefaultLiteral) - continue; - - if (bestScore->Data[i] > currentScore.Data[i]) - return false; - } - - return true; - }; - if (checkRequirementsEarly()) { - Constraint *constraint = choice; - auto *decl = constraint->getOverloadChoice().getDecl(); - if (decl->getBaseIdentifier().isArithmeticOperator()) { - auto *useDC = constraint->getOverloadUseDC(); - auto choiceType = CS.getEffectiveOverloadType( - constraint->getLocator(), constraint->getOverloadChoice(), - /*allowMembers=*/true, useDC); - auto choiceFnType = choiceType->getAs(); - auto genericFnType = decl->getInterfaceType()->getAs(); - auto signature = genericFnType->getGenericSignature(); - - for (auto argParamPair : llvm::zip(argFnType->getParams(), - choiceFnType->getParams())) { - auto argType = std::get<0>(argParamPair).getPlainType(); - auto paramType = std::get<1>(argParamPair).getPlainType(); - - // Only check argument types with no type variables that will be matched - // against a plain type parameter. - argType = argType->getCanonicalType()->getWithoutSpecifierType(); - if (argType->hasTypeVariable() || !paramType->isTypeParameter()) - continue; - - for (auto *protocol : signature->getRequiredProtocols(paramType)) { - if (!TypeChecker::conformsToProtocol(argType, protocol, useDC)) - return skip("unsatisfied"); - } - } - } - } - // Don't attempt to solve for generic operators if we already have // a non-generic solution. diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 962faa2e11a83..2a16d3dc7dc5a 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -294,6 +294,11 @@ class DependentComponentSplitterStep final : public SolverStep { /// Array containing all of the partial solutions for the parent split. MutableArrayRef> AllPartialSolutions; + /// The solutions computed the \c ComponentSteps created for each partial + /// solution combinations. Will be merged into the final \c Solutions vector + /// in \c resume. + std::vector>> ContextualSolutions; + /// Take all of the constraints in this component and put them into /// \c Constraints. void injectConstraints() { diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 0064d48d75b48..851e7482eb25e 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -20,6 +20,7 @@ #include "TypeCheckDecl.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" +#include "TypeCheckDistributed.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Availability.h" #include "swift/AST/Expr.h" @@ -135,7 +136,7 @@ Expr *swift::buildArgumentForwardingExpr(ArrayRef params, SourceLoc(), false, IsImplicit); } - auto argTy = AnyFunctionType::composeInput(ctx, elts, /*canonical*/false); + auto argTy = AnyFunctionType::composeTuple(ctx, elts, /*canonical*/false); argExpr->setType(argTy); return argExpr; @@ -208,10 +209,14 @@ enum class ImplicitConstructorKind { /// The default constructor, which default-initializes each /// of the instance variables. Default, + /// The default constructor of a distributed actor. + /// Similarly to a Default one it initializes each of the instance variables, + /// however it also implicitly gains an ActorTransport parameter. + DefaultDistributedActor, /// The memberwise constructor, which initializes each of /// the instance variables from a parameter of the same type and /// name. - Memberwise + Memberwise, }; /// Create an implicit struct or class constructor. @@ -308,6 +313,26 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, maybeAddMemberwiseDefaultArg(arg, var, params.size(), ctx); + params.push_back(arg); + } + } else if (ICK == ImplicitConstructorKind::DefaultDistributedActor) { + assert(isa(decl)); + assert(decl->isDistributedActor() && + "Only 'distributed actor' type can gain implicit distributed actor init"); + + if (swift::ensureDistributedModuleLoaded(decl)) { + // copy access level of distributed actor init from the nominal decl + accessLevel = decl->getEffectiveAccess(); + + auto transportDecl = ctx.getActorTransportDecl(); + + // Create the parameter. + auto *arg = new (ctx) ParamDecl(SourceLoc(), Loc, ctx.Id_transport, Loc, + ctx.Id_transport, transportDecl); + arg->setSpecifier(ParamSpecifier::Default); + arg->setInterfaceType(transportDecl->getDeclaredInterfaceType()); + arg->setImplicit(); + params.push_back(arg); } } @@ -418,35 +443,54 @@ synthesizeStubBody(AbstractFunctionDecl *fn, void *) { /*isTypeChecked=*/true }; } -static std::tuple -configureGenericDesignatedInitOverride(ASTContext &ctx, +namespace { + struct DesignatedInitOverrideInfo { + GenericSignature GenericSig; + GenericParamList *GenericParams; + SubstitutionMap OverrideSubMap; + }; +} + +static DesignatedInitOverrideInfo +computeDesignatedInitOverrideSignature(ASTContext &ctx, ClassDecl *classDecl, Type superclassTy, ConstructorDecl *superclassCtor) { - auto *superclassDecl = superclassTy->getAnyNominal(); - auto *moduleDecl = classDecl->getParentModule(); - auto subMap = superclassTy->getContextSubstitutionMap( - moduleDecl, superclassDecl); - GenericSignature genericSig; - auto *genericParams = superclassCtor->getGenericParams(); + auto *superclassDecl = superclassTy->getAnyNominal(); + auto classSig = classDecl->getGenericSignature(); auto superclassCtorSig = superclassCtor->getGenericSignature(); auto superclassSig = superclassDecl->getGenericSignature(); + // These are our outputs. + GenericSignature genericSig = classSig; + auto *genericParams = superclassCtor->getGenericParams(); + auto subMap = superclassTy->getContextSubstitutionMap( + moduleDecl, superclassDecl); + if (superclassCtorSig.getPointer() != superclassSig.getPointer()) { + // If the base initiliazer's generic signature is different + // from that of the base class, the base class initializer either + // has generic parameters or a 'where' clause. + // + // We need to "rebase" the requirements on top of the derived class's + // generic signature. + SmallVector newParams; SmallVector newParamTypes; - // Inheriting initializers that have their own generic parameters + // If genericParams is non-null, the base class initializer has its own + // generic parameters. Otherwise, it is non-generic with a contextual + // 'where' clause. if (genericParams) { - // First, clone the superclass constructor's generic parameter list, + // First, clone the base class initializer's generic parameter list, // but change the depth of the generic parameters to be one greater // than the depth of the subclass. unsigned depth = 0; if (auto genericSig = classDecl->getGenericSignature()) - depth = genericSig->getGenericParams().back()->getDepth() + 1; + depth = genericSig.getGenericParams().back()->getDepth() + 1; for (auto *param : genericParams->getParams()) { auto *newParam = new (ctx) GenericTypeParamDecl(classDecl, @@ -457,8 +501,8 @@ configureGenericDesignatedInitOverride(ASTContext &ctx, newParams.push_back(newParam); } - // We don't have to clone the requirements, because they're not - // used for anything. + // We don't have to clone the RequirementReprs, because they're not + // used for anything other than SIL mode. genericParams = GenericParamList::create(ctx, SourceLoc(), newParams, @@ -473,53 +517,62 @@ configureGenericDesignatedInitOverride(ASTContext &ctx, } } - // Build a generic signature for the derived class initializer. + // The depth at which the initializer's own generic parameters start, if any. unsigned superclassDepth = 0; if (superclassSig) - superclassDepth = superclassSig->getGenericParams().back()->getDepth() + 1; + superclassDepth = superclassSig.getGenericParams().back()->getDepth() + 1; // We're going to be substituting the requirements of the base class // initializer to form the requirements of the derived class initializer. auto substFn = [&](SubstitutableType *type) -> Type { auto *gp = cast(type); + // Generic parameters of the base class itself are mapped via the + // substitution map of the superclass type. if (gp->getDepth() < superclassDepth) return Type(gp).subst(subMap); + + // Generic parameters added by the base class initializer map to the new + // generic parameters of the derived initializer. return genericParams->getParams()[gp->getIndex()] ->getDeclaredInterfaceType(); }; - auto lookupConformanceFn = - [&](CanType depTy, Type substTy, - ProtocolDecl *proto) -> ProtocolConformanceRef { - if (auto conf = subMap.lookupConformance(depTy, proto)) - return conf; - - return ProtocolConformanceRef(proto); - }; - - SmallVector requirements; - for (auto reqt : superclassCtorSig->getRequirements()) - if (auto substReqt = reqt.subst(substFn, lookupConformanceFn)) - requirements.push_back(*substReqt); - - // Now form the substitution map that will be used to remap parameter - // types. - subMap = SubstitutionMap::get(superclassCtorSig, - substFn, lookupConformanceFn); - - genericSig = evaluateOrDefault( - ctx.evaluator, - AbstractGenericSignatureRequest{ - classDecl->getGenericSignature().getPointer(), - std::move(newParamTypes), - std::move(requirements) - }, - GenericSignature()); - } else { - genericSig = classDecl->getGenericSignature(); + // If we don't have any new generic parameters and the derived class is + // not generic, the base class initializer's 'where' clause should already + // be fully satisfied, and we can just drop it. + if (genericParams != nullptr || classSig) { + auto lookupConformanceFn = + [&](CanType depTy, Type substTy, + ProtocolDecl *proto) -> ProtocolConformanceRef { + if (depTy->getRootGenericParam()->getDepth() < superclassDepth) + if (auto conf = subMap.lookupConformance(depTy, proto)) + return conf; + + return ProtocolConformanceRef(proto); + }; + + SmallVector requirements; + for (auto reqt : superclassCtorSig.getRequirements()) + if (auto substReqt = reqt.subst(substFn, lookupConformanceFn)) + requirements.push_back(*substReqt); + + // Now form the substitution map that will be used to remap parameter + // types. + subMap = SubstitutionMap::get(superclassCtorSig, + substFn, lookupConformanceFn); + + genericSig = evaluateOrDefault( + ctx.evaluator, + AbstractGenericSignatureRequest{ + classSig.getPointer(), + std::move(newParamTypes), + std::move(requirements) + }, + GenericSignature()); + } } - return std::make_tuple(genericSig, genericParams, subMap); + return DesignatedInitOverrideInfo{genericSig, genericParams, subMap}; } static void @@ -625,7 +678,7 @@ synthesizeDesignatedInitOverride(AbstractFunctionDecl *fn, void *context) { if (auto *funcTy = type->getAs()) type = funcTy->getResult(); auto *superclassCtorRefExpr = - new (ctx) DotSyntaxCallExpr(ctorRefExpr, SourceLoc(), superRef, type); + DotSyntaxCallExpr::create(ctx, ctorRefExpr, SourceLoc(), superRef, type); superclassCtorRefExpr->setThrows(false); auto *bodyParams = ctor->getParameters(); @@ -702,17 +755,30 @@ createDesignatedInitOverride(ClassDecl *classDecl, return nullptr; } - GenericSignature genericSig; - GenericParamList *genericParams; - SubstitutionMap subMap; - - std::tie(genericSig, genericParams, subMap) = - configureGenericDesignatedInitOverride(ctx, + auto overrideInfo = + computeDesignatedInitOverrideSignature(ctx, classDecl, superclassTy, superclassCtor); - // Determine the initializer parameters. + if (auto superclassCtorSig = superclassCtor->getGenericSignature()) { + auto *genericEnv = overrideInfo.GenericSig.getGenericEnvironment(); + + // If the base class initializer has a 'where' clause, it might impose + // requirements on the base class's own generic parameters that are not + // satisfied by the derived class. In this case, we don't want to inherit + // this initializer; there's no way to call it on the derived class. + auto checkResult = TypeChecker::checkGenericArguments( + classDecl->getParentModule(), + superclassCtorSig.getRequirements(), + [&](Type type) -> Type { + auto substType = type.subst(overrideInfo.OverrideSubMap); + return GenericEnvironment::mapTypeIntoContext( + genericEnv, substType); + }); + if (checkResult != RequirementCheckResult::Success) + return nullptr; + } // Create the initializer parameter patterns. OptionSet options @@ -732,7 +798,7 @@ createDesignatedInitOverride(ClassDecl *classDecl, auto *bodyParam = bodyParams->get(idx); auto paramTy = superclassParam->getInterfaceType(); - auto substTy = paramTy.subst(subMap); + auto substTy = paramTy.subst(overrideInfo.OverrideSubMap); bodyParam->setInterfaceType(substTy); } @@ -747,12 +813,13 @@ createDesignatedInitOverride(ClassDecl *classDecl, /*Async=*/false, /*AsyncLoc=*/SourceLoc(), /*Throws=*/superclassCtor->hasThrows(), /*ThrowsLoc=*/SourceLoc(), - bodyParams, genericParams, classDecl); + bodyParams, overrideInfo.GenericParams, + classDecl); ctor->setImplicit(); // Set the interface type of the initializer. - ctor->setGenericSignature(genericSig); + ctor->setGenericSignature(overrideInfo.GenericSig); ctor->setImplicitlyUnwrappedOptional( superclassCtor->isImplicitlyUnwrappedOptional()); @@ -926,10 +993,16 @@ HasUserDefinedDesignatedInitRequest::evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const { assert(!decl->hasClangNode()); - for (auto *member : decl->getMembers()) - if (auto *ctor = dyn_cast(member)) - if (ctor->isDesignatedInit() && !ctor->isSynthesized()) - return true; + auto results = decl->lookupDirect(DeclBaseName::createConstructor()); + for (auto *member : results) { + if (isa(member->getDeclContext())) + continue; + + auto *ctor = cast(member); + if (ctor->isDesignatedInit() && !ctor->isSynthesized()) + return true; + } + return false; } @@ -960,11 +1033,17 @@ static void collectNonOveriddenSuperclassInits( // overrides, which we don't want to consider as viable delegates for // convenience inits. llvm::SmallPtrSet overriddenInits; - for (auto member : subclass->getMembers()) - if (auto ctor = dyn_cast(member)) - if (!ctor->hasStubImplementation()) - if (auto overridden = ctor->getOverriddenDecl()) - overriddenInits.insert(overridden); + + auto ctors = subclass->lookupDirect(DeclBaseName::createConstructor()); + for (auto *member : ctors) { + if (isa(member->getDeclContext())) + continue; + + auto *ctor = cast(member); + if (!ctor->hasStubImplementation()) + if (auto overridden = ctor->getOverriddenDecl()) + overriddenInits.insert(overridden); + } superclassDecl->synthesizeSemanticMembersIfNeeded( DeclBaseName::createConstructor()); @@ -996,11 +1075,13 @@ static void collectNonOveriddenSuperclassInits( static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) { // Bail out if we're validating one of our constructors already; // we'll revisit the issue later. - for (auto member : decl->getMembers()) { - if (auto ctor = dyn_cast(member)) { - if (ctor->isRecursiveValidation()) - return; - } + auto results = decl->lookupDirect(DeclBaseName::createConstructor()); + for (auto *member : results) { + if (isa(member->getDeclContext())) + continue; + + if (member->isRecursiveValidation()) + return; } decl->setAddedImplicitInitializers(); @@ -1055,19 +1136,22 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) { // A designated or required initializer has not been overridden. bool alreadyDeclared = false; - for (auto *member : decl->getMembers()) { - if (auto ctor = dyn_cast(member)) { - // Skip any invalid constructors. - if (ctor->isInvalid()) - continue; - - auto type = swift::getMemberTypeForComparison(ctor, nullptr); - auto parentType = swift::getMemberTypeForComparison(superclassCtor, ctor); - - if (isOverrideBasedOnType(ctor, type, superclassCtor, parentType)) { - alreadyDeclared = true; - break; - } + + auto results = decl->lookupDirect(DeclBaseName::createConstructor()); + for (auto *member : results) { + if (isa(member->getDeclContext())) + continue; + + auto *ctor = cast(member); + + // Skip any invalid constructors. + if (ctor->isInvalid()) + continue; + + auto type = swift::getMemberTypeForComparison(ctor, nullptr); + if (isOverrideBasedOnType(ctor, type, superclassCtor)) { + alreadyDeclared = true; + break; } } @@ -1142,14 +1226,15 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { // If we already added implicit initializers, we're done. if (decl->addedImplicitInitializers()) return; - + if (!shouldAttemptInitializerSynthesis(decl)) { decl->setAddedImplicitInitializers(); return; } - if (auto *classDecl = dyn_cast(decl)) + if (auto *classDecl = dyn_cast(decl)) { addImplicitInheritedConstructorsToClass(classDecl); + } // Force the memberwise and default initializers if the type has them. // FIXME: We need to be more lazy about synthesizing constructors. @@ -1231,6 +1316,20 @@ ResolveImplicitMemberRequest::evaluate(Evaluator &evaluator, (void)evaluateTargetConformanceTo(decodableProto); } break; + case ImplicitMemberAction::ResolveDistributedActor: + case ImplicitMemberAction::ResolveDistributedActorTransport: + case ImplicitMemberAction::ResolveDistributedActorIdentity: { + // init(transport:) and init(resolve:using:) may be synthesized as part of + // derived conformance to the DistributedActor protocol. + // If the target should conform to the DistributedActor protocol, check the + // conformance here to attempt synthesis. + // FIXME(distributed): invoke the requirement adding explicitly here + TypeChecker::addImplicitConstructors(target); + auto *distributedActorProto = + Context.getProtocol(KnownProtocolKind::DistributedActor); + (void)evaluateTargetConformanceTo(distributedActorProto); + break; + } } return std::make_tuple<>(); } @@ -1405,16 +1504,20 @@ SynthesizeDefaultInitRequest::evaluate(Evaluator &evaluator, decl); // Create the default constructor. - auto ctor = createImplicitConstructor(decl, - ImplicitConstructorKind::Default, - ctx); - - // Add the constructor. - decl->addMember(ctor); + auto ctorKind = decl->isDistributedActor() ? + ImplicitConstructorKind::DefaultDistributedActor : + ImplicitConstructorKind::Default; + if (auto ctor = createImplicitConstructor(decl, ctorKind, ctx)) { + // Add the constructor. + decl->addMember(ctor); + + // Lazily synthesize an empty body for the default constructor. + ctor->setBodySynthesizer(synthesizeSingleReturnFunctionBody); + return ctor; + } - // Lazily synthesize an empty body for the default constructor. - ctor->setBodySynthesizer(synthesizeSingleReturnFunctionBody); - return ctor; + // no default init was synthesized + return nullptr; } ValueDecl *swift::getProtocolRequirement(ProtocolDecl *protocol, diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp new file mode 100644 index 0000000000000..bae5d592f2090 --- /dev/null +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -0,0 +1,191 @@ +//===--- CodeSynthesisDistributedActor.cpp --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "TypeCheckDistributed.h" + + +#include "TypeChecker.h" +#include "TypeCheckType.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/AST/Availability.h" +#include "swift/AST/Expr.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/Initializer.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/TypeCheckRequests.h" +#include "swift/Basic/Defer.h" +#include "swift/ClangImporter/ClangModule.h" +#include "swift/Sema/ConstraintSystem.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "DerivedConformances.h" +using namespace swift; + +/******************************************************************************/ +/*************************** _REMOTE_ FUNCTIONS *******************************/ +/******************************************************************************/ + +/// Synthesizes the for `_remote_xxx` functions. +/// +/// Create a stub body that emits a fatal error message. +static std::pair +synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *context) { + auto distributedFunc = static_cast(context); + auto classDecl = func->getDeclContext()->getSelfClassDecl(); + auto &ctx = func->getASTContext(); + auto &SM = ctx.SourceMgr; + + auto *staticStringDecl = ctx.getStaticStringDecl(); + auto staticStringType = staticStringDecl->getDeclaredInterfaceType(); + auto staticStringInit = ctx.getStringBuiltinInitDecl(staticStringDecl); + + auto *uintDecl = ctx.getUIntDecl(); + auto uintType = uintDecl->getDeclaredInterfaceType(); + auto uintInit = ctx.getIntBuiltinInitDecl(uintDecl); + + auto missingTransportDecl = ctx.getMissingDistributedActorTransport(); + assert(missingTransportDecl && "Could not locate '_missingDistributedActorTransport' function"); + + // Create a call to _Distributed._missingDistributedActorTransport + auto loc = func->getLoc(); + Expr *ref = new (ctx) DeclRefExpr(missingTransportDecl, + DeclNameLoc(loc), /*Implicit=*/true); + ref->setType(missingTransportDecl->getInterfaceType() + ->removeArgumentLabels(1)); + + llvm::SmallString<64> buffer; + StringRef fullClassName = ctx.AllocateCopy( + (classDecl->getModuleContext()->getName().str() + + "." + + classDecl->getName().str()).toStringRef(buffer)); + + auto *className = new (ctx) StringLiteralExpr(fullClassName, loc, + /*Implicit=*/true); + className->setBuiltinInitializer(staticStringInit); + assert(isa(className->getBuiltinInitializer().getDecl())); + className->setType(staticStringType); + + auto *funcName = new (ctx) StringLiteralExpr( + ctx.AllocateCopy(func->getName().getBaseName().getIdentifier().str()), loc, + /*Implicit=*/true); + funcName->setType(staticStringType); + funcName->setBuiltinInitializer(staticStringInit); + + // Note: Sadly we cannot just rely on #function, #file, #line for the location + // (MagicIdentifierLiteralExpr), of the call because the call is made from a thunk. + // That thunk does not carry those info today although it could. + // + // Instead, we offer the location where the distributed func was declared. + auto fileString = SM.getDisplayNameForLoc(distributedFunc->getStartLoc()); + auto *file = new (ctx) StringLiteralExpr(fileString, loc, /*Implicit=*/true); + file->setType(staticStringType); + file->setBuiltinInitializer(staticStringInit); + + auto startLineAndCol = SM.getPresumedLineAndColumnForLoc(distributedFunc->getStartLoc()); + auto *line = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.first); + line->setType(uintType); + line->setBuiltinInitializer(uintInit); + + auto *column = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.second); + column->setType(uintType); + column->setBuiltinInitializer(uintInit); + + auto *call = CallExpr::createImplicit( + ctx, ref, { className, funcName, file, line, column }, {}); + call->setType(ctx.getNeverType()); + call->setThrows(false); + + SmallVector stmts; + stmts.push_back(call); + auto body = BraceStmt::create(ctx, SourceLoc(), stmts, SourceLoc(), + /*implicit=*/true); + return { body, /*isTypeChecked=*/true }; +} + +static Identifier makeRemoteFuncIdentifier(FuncDecl* distributedFunc) { + auto &C = distributedFunc->getASTContext(); + assert(distributedFunc->isDistributed()); + auto localFuncName = distributedFunc->getBaseIdentifier().str().str(); + auto remoteFuncIdent = C.getIdentifier("_remote_" + localFuncName); + return remoteFuncIdent; +} + +/******************************************************************************/ +/************************ SYNTHESIS ENTRY POINT *******************************/ +/******************************************************************************/ + +/// Create a remote stub for the passed in \c func. +/// The remote stub function is not user accessible and mirrors the API of +/// the local function. It is always throwing, async, and user-inaccessible. +/// +/// ``` +/// // func greet(name: String) { ... } +/// dynamic func _remote_greet(name: String) async throws { +/// fatalError(...) +/// } +/// ``` +/// +/// and is intended to be replaced by a transport library by providing an +/// appropriate @_dynamicReplacement function. +AbstractFunctionDecl *TypeChecker::addImplicitDistributedActorRemoteFunction( + ClassDecl *decl, AbstractFunctionDecl *AFD) { + if (!decl->isDistributedActor()) + return nullptr; + + auto func = dyn_cast(AFD); + if (!func || !func->isDistributed()) + return nullptr; + + // ==== if the remote func already exists, return it + if (auto existing = decl->lookupDirectRemoteFunc(func)) + return existing; + + // ==== Synthesize and add 'remote' func to the actor decl + + auto &C = decl->getASTContext(); + auto parentDC = decl; + + auto remoteFuncIdent = makeRemoteFuncIdentifier(func); + + auto params = ParameterList::clone(C, func->getParameters()); + auto genericParams = func->getGenericParams(); // TODO: also clone those + Type resultTy = func->getResultInterfaceType(); + + DeclName name(C, remoteFuncIdent, params); + auto *const remoteFuncDecl = FuncDecl::createImplicit( + C, StaticSpellingKind::None, name, /*NameLoc=*/SourceLoc(), + /*Async=*/true, /*Throws=*/true, + /*GenericParams=*/genericParams, params, + resultTy, parentDC); + + // *dynamic* because we'll be replacing it with specific transports + remoteFuncDecl->getAttrs().add( + new (C) DynamicAttr(/*implicit=*/true)); + + // @_distributedActorIndependent + remoteFuncDecl->getAttrs().add( + new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true)); + + // users should never have to access this function directly; + // it is only invoked from our distributed function thunk if the actor is remote. + remoteFuncDecl->setUserAccessible(false); + remoteFuncDecl->setSynthesized(); + + remoteFuncDecl->setBodySynthesizer(&synthesizeRemoteFuncStubBody, func); + + // same access control as the original function is fine + remoteFuncDecl->copyFormalAccessFrom(func, /*sourceIsParentContext=*/false); + + decl->addMember(remoteFuncDecl); + + return remoteFuncDecl; +} diff --git a/lib/Sema/ConstantnessSemaDiagnostics.cpp b/lib/Sema/ConstantnessSemaDiagnostics.cpp index 476022b05058a..256abfd75c9db 100644 --- a/lib/Sema/ConstantnessSemaDiagnostics.cpp +++ b/lib/Sema/ConstantnessSemaDiagnostics.cpp @@ -335,25 +335,56 @@ static void diagnoseConstantArgumentRequirementOfCall(const CallExpr *callExpr, void swift::diagnoseConstantArgumentRequirement( const Expr *expr, const DeclContext *declContext) { class ConstantReqCallWalker : public ASTWalker { - const ASTContext &astContext; + DeclContext *DC; public: - ConstantReqCallWalker(ASTContext &ctx) : astContext(ctx) {} + ConstantReqCallWalker(DeclContext *DC) : DC(DC) {} // Descend until we find a call expressions. Note that the input expression // could be an assign expression or another expression that contains the // call. std::pair walkToExprPre(Expr *expr) override { - if (!expr || isa(expr) || !expr->getType()) + // Handle closure expressions separately as we may need to + // manually descend into the body. + if (auto *closureExpr = dyn_cast(expr)) { + return walkToClosureExprPre(closureExpr); + } + // Interpolated expressions' bodies will be type checked + // separately so exit early to avoid duplicate diagnostics. + if (!expr || isa(expr) || !expr->getType() || + isa(expr)) return {false, expr}; if (auto *callExpr = dyn_cast(expr)) { - diagnoseConstantArgumentRequirementOfCall(callExpr, astContext); - return {false, expr}; + diagnoseConstantArgumentRequirementOfCall(callExpr, DC->getASTContext()); } return {true, expr}; } + + std::pair walkToClosureExprPre(ClosureExpr *closure) { + if (closure->hasSingleExpressionBody()) { + // Single expression closure bodies are not visited directly + // by the ASTVisitor, so we must descend into the body manually + // and set the DeclContext to that of the closure. + DC = closure; + return {true, closure}; + } + return {false, closure}; + } + + Expr *walkToExprPost(Expr *expr) override { + if (auto *closureExpr = dyn_cast(expr)) { + // Reset the DeclContext to the outer scope if we descended + // into a closure expr. + DC = closureExpr->getParent(); + } + return expr; + } + + std::pair walkToStmtPre(Stmt *stmt) override { + return {true, stmt}; + } }; - ConstantReqCallWalker walker(declContext->getASTContext()); + ConstantReqCallWalker walker(const_cast(declContext)); const_cast(expr)->walk(walker); } diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 57c85e1290bd7..551fcc5f84281 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -65,7 +65,6 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, case ConstraintKind::OptionalObject: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: - case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::UnresolvedMemberChainBase: @@ -142,7 +141,6 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, case ConstraintKind::Disjunction: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: - case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: @@ -274,7 +272,6 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { case ConstraintKind::Defaultable: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: - case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: @@ -303,7 +300,9 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { getMemberUseDC(), getFunctionRefKind(), getLocator()); case ConstraintKind::Disjunction: - return createDisjunction(cs, getNestedConstraints(), getLocator()); + return createDisjunction( + cs, getNestedConstraints(), getLocator(), + static_cast(shouldRememberChoice())); case ConstraintKind::KeyPath: case ConstraintKind::KeyPathApplication: @@ -355,7 +354,6 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { case ConstraintKind::BindToPointerType: Out << " bind to pointer "; break; case ConstraintKind::Subtype: Out << " subtype "; break; case ConstraintKind::Conversion: Out << " conv "; break; - case ConstraintKind::OpaqueUnderlyingType: Out << " underlying type of opaque "; break; case ConstraintKind::BridgingConversion: Out << " bridging conv "; break; case ConstraintKind::ArgumentConversion: Out << " arg conv "; break; case ConstraintKind::OperatorArgumentConversion: @@ -609,7 +607,6 @@ gatherReferencedTypeVars(Constraint *constraint, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: - case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 53792783c33f7..b0d52f24e600b 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -37,7 +37,14 @@ using namespace constraints; ConstraintGraph::ConstraintGraph(ConstraintSystem &cs) : CS(cs) { } ConstraintGraph::~ConstraintGraph() { - assert(Changes.empty() && "Scope stack corrupted"); +#ifndef NDEBUG + // If constraint system is in an invalid state, it's + // possible that constraint graph is corrupted as well + // so let's not attempt to check change log. + if (!CS.inInvalidState()) + assert(Changes.empty() && "Scope stack corrupted"); +#endif + for (unsigned i = 0, n = TypeVariables.size(); i != n; ++i) { auto &impl = TypeVariables[i]->getImpl(); delete impl.getGraphNode(); @@ -103,11 +110,37 @@ ConstraintGraphNode::getEquivalenceClassUnsafe() const{ } #pragma mark Node mutation + +static bool isUsefulForReferencedVars(Constraint *constraint) { + switch (constraint->getKind()) { + // Don't attempt to propagate information about `Bind`s and + // `BindOverload`s to referenced variables since they are + // adjacent through that binding already, and there is no + // useful information in trying to process that kind of + // constraint. + case ConstraintKind::Bind: + case ConstraintKind::BindOverload: + return false; + + default: + return true; + } +} + void ConstraintGraphNode::addConstraint(Constraint *constraint) { assert(ConstraintIndex.count(constraint) == 0 && "Constraint re-insertion"); ConstraintIndex[constraint] = Constraints.size(); Constraints.push_back(constraint); - introduceToInference(constraint, /*notifyFixedBindings=*/true); + + { + introduceToInference(constraint); + + if (isUsefulForReferencedVars(constraint)) { + notifyReferencedVars([&](ConstraintGraphNode &referencedVar) { + referencedVar.introduceToInference(constraint); + }); + } + } } void ConstraintGraphNode::removeConstraint(Constraint *constraint) { @@ -119,8 +152,15 @@ void ConstraintGraphNode::removeConstraint(Constraint *constraint) { ConstraintIndex.erase(pos); assert(Constraints[index] == constraint && "Mismatched constraint"); - retractFromInference(constraint, - /*notifyFixedBindings=*/true); + { + retractFromInference(constraint); + + if (isUsefulForReferencedVars(constraint)) { + notifyReferencedVars([&](ConstraintGraphNode &referencedVar) { + referencedVar.retractFromInference(constraint); + }); + } + } // If this is the last constraint, just pop it off the list and we're done. unsigned lastIndex = Constraints.size()-1; @@ -160,8 +200,7 @@ void ConstraintGraphNode::notifyReferencingVars() const { affectedVar->getImpl().getRepresentative(/*record=*/nullptr); if (!repr->getImpl().getFixedType(/*record=*/nullptr)) - CG[repr].reintroduceToInference(constraint, - /*notifyReferencedVars=*/false); + CG[repr].reintroduceToInference(constraint); } } }; @@ -197,6 +236,13 @@ void ConstraintGraphNode::notifyReferencingVars() const { } } +void ConstraintGraphNode::notifyReferencedVars( + llvm::function_ref notification) { + for (auto *fixedBinding : getReferencedVars()) { + notification(CG[fixedBinding]); + } +} + void ConstraintGraphNode::addToEquivalenceClass( ArrayRef typeVars) { assert(forRepresentativeVar() && @@ -210,7 +256,14 @@ void ConstraintGraphNode::addToEquivalenceClass( auto &node = CG[newMember]; for (auto *constraint : node.getConstraints()) { - introduceToInference(constraint, /*notifyReferencedVars=*/true); + introduceToInference(constraint); + + if (!isUsefulForReferencedVars(constraint)) + continue; + + notifyReferencedVars([&](ConstraintGraphNode &referencedVar) { + referencedVar.introduceToInference(constraint); + }); } node.notifyReferencingVars(); @@ -273,24 +326,7 @@ inference::PotentialBindings &ConstraintGraphNode::getCurrentBindings() { return *Bindings; } -static bool isUsefulForReferencedVars(Constraint *constraint) { - switch (constraint->getKind()) { - // Don't attempt to propagate information about `Bind`s and - // `BindOverload`s to referenced variables since they are - // adjacent through that binding already, and there is no - // useful information in trying to process that kind of - // constraint. - case ConstraintKind::Bind: - case ConstraintKind::BindOverload: - return false; - - default: - return true; - } -} - -void ConstraintGraphNode::introduceToInference(Constraint *constraint, - bool notifyReferencedVars) { +void ConstraintGraphNode::introduceToInference(Constraint *constraint) { if (forRepresentativeVar()) { auto fixedType = TypeVar->getImpl().getFixedType(/*record=*/nullptr); if (!fixedType) @@ -298,20 +334,11 @@ void ConstraintGraphNode::introduceToInference(Constraint *constraint, } else { auto *repr = getTypeVariable()->getImpl().getRepresentative(/*record=*/nullptr); - CG[repr].introduceToInference(constraint, /*notifyReferencedVars=*/false); - } - - if (!notifyReferencedVars || !isUsefulForReferencedVars(constraint)) - return; - - for (auto *fixedBinding : getReferencedVars()) { - CG[fixedBinding].introduceToInference(constraint, - /*notifyReferencedVars=*/false); + CG[repr].introduceToInference(constraint); } } -void ConstraintGraphNode::retractFromInference(Constraint *constraint, - bool notifyReferencedVars) { +void ConstraintGraphNode::retractFromInference(Constraint *constraint) { if (forRepresentativeVar()) { auto fixedType = TypeVar->getImpl().getFixedType(/*record=*/nullptr); if (!fixedType) @@ -319,22 +346,13 @@ void ConstraintGraphNode::retractFromInference(Constraint *constraint, } else { auto *repr = getTypeVariable()->getImpl().getRepresentative(/*record=*/nullptr); - CG[repr].retractFromInference(constraint, /*notifyReferencedVars=*/false); - } - - if (!notifyReferencedVars || !isUsefulForReferencedVars(constraint)) - return; - - for (auto *fixedBinding : getReferencedVars()) { - CG[fixedBinding].retractFromInference(constraint, - /*notifyReferencedVars=*/false); + CG[repr].retractFromInference(constraint); } } -void ConstraintGraphNode::reintroduceToInference(Constraint *constraint, - bool notifyReferencedVars) { - retractFromInference(constraint, notifyReferencedVars); - introduceToInference(constraint, notifyReferencedVars); +void ConstraintGraphNode::reintroduceToInference(Constraint *constraint) { + retractFromInference(constraint); + introduceToInference(constraint); } void ConstraintGraphNode::introduceToInference(Type fixedType) { @@ -360,8 +378,7 @@ void ConstraintGraphNode::introduceToInference(Type fixedType) { // all of the constraints that reference bound type variable. for (auto *constraint : getConstraints()) { if (isUsefulForReferencedVars(constraint)) - node.reintroduceToInference(constraint, - /*notifyReferencedVars=*/false); + node.reintroduceToInference(constraint); } } } @@ -402,6 +419,11 @@ ConstraintGraphScope::ConstraintGraphScope(ConstraintGraph &CG) } ConstraintGraphScope::~ConstraintGraphScope() { + // Don't attempt to rollback if constraint system ended up + // in an invalid state. + if (CG.CS.inInvalidState()) + return; + // Pop changes off the stack until we hit the change could we had prior to // introducing this scope. assert(CG.Changes.size() >= NumChanges && "Scope stack corrupted"); diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index d28be9325029e..7d671c35acf91 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -60,9 +60,11 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::DynamicType: case ConstraintLocator::SubscriptMember: case ConstraintLocator::OpenedGeneric: + case ConstraintLocator::OpenedOpaqueArchetype: case ConstraintLocator::WrappedValue: case ConstraintLocator::GenericParameter: case ConstraintLocator::GenericArgument: + case ConstraintLocator::TupleType: case ConstraintLocator::NamedTupleElement: case ConstraintLocator::TupleElement: case ConstraintLocator::ProtocolRequirement: @@ -122,7 +124,7 @@ bool ConstraintLocator::isKeyPathType() const { // The format of locator should be ` -> key path type` if (!anchor || !isExpr(anchor) || path.size() != 1) return false; - return path.back().getKind() == ConstraintLocator::KeyPathType; + return path.back().is(); } bool ConstraintLocator::isKeyPathRoot() const { @@ -356,6 +358,12 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { out << "member reference base"; break; + case TupleType: { + auto tupleElt = elt.castTo(); + out << "tuple type '" << tupleElt.getType()->getString(PO) << "'"; + break; + } + case NamedTupleElement: { auto tupleElt = elt.castTo(); out << "named tuple element #" << llvm::utostr(tupleElt.getIndex()); @@ -411,6 +419,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { out << "opened generic"; break; + case OpenedOpaqueArchetype: + out << "opened opaque archetype"; + break; + case ConditionalRequirement: { auto reqElt = elt.castTo(); out << "conditional requirement #" << llvm::utostr(reqElt.getIndex()); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index f53461b223c1d..f5e5389d76eab 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -509,6 +509,7 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( case ComponentKind::OptionalWrap: case ComponentKind::Identity: case ComponentKind::DictionaryKey: + case ComponentKind::CodeCompletion: // These components don't have any callee associated, so just continue. break; } @@ -843,6 +844,63 @@ Type ConstraintSystem::openType(Type type, OpenedTypeMap &replacements) { }); } +Type ConstraintSystem::openOpaqueType(OpaqueTypeArchetypeType *opaque, + ConstraintLocatorBuilder locator) { + auto opaqueLocator = locator.withPathElement( + LocatorPathElt::OpenedOpaqueArchetype(opaque->getDecl())); + + // Open the generic signature of the opaque decl, and bind the "outer" generic + // params to our context. The remaining axes of freedom on the type variable + // corresponding to the underlying type should be the constraints on the + // underlying return type. + OpenedTypeMap replacements; + openGeneric(DC, opaque->getBoundSignature(), opaqueLocator, replacements); + + auto underlyingTyVar = openType(opaque->getInterfaceType(), replacements); + assert(underlyingTyVar); + + for (auto param : DC->getGenericSignatureOfContext().getGenericParams()) { + addConstraint(ConstraintKind::Bind, openType(param, replacements), + DC->mapTypeIntoContext(param), opaqueLocator); + } + + return underlyingTyVar; +} + +Type ConstraintSystem::openOpaqueType(Type type, ContextualTypePurpose context, + ConstraintLocatorBuilder locator) { + // Early return if `type` is `NULL` or if there are no opaque archetypes (in + // which case there is certainly nothing for us to do). + if (!type || !type->hasOpaqueArchetype()) + return type; + + auto inReturnContext = [](ContextualTypePurpose context) { + return context == CTP_ReturnStmt || context == CTP_ReturnSingleExpr; + }; + + if (!(context == CTP_Initialization || inReturnContext(context))) + return type; + + auto shouldOpen = [&](OpaqueTypeArchetypeType *opaqueType) { + if (!inReturnContext(context)) + return true; + + if (auto *func = dyn_cast(DC)) + return opaqueType->getDecl()->isOpaqueReturnTypeOfFunction(func); + + return true; + }; + + return type.transform([&](Type type) -> Type { + auto *opaqueType = type->getAs(); + + if (opaqueType && shouldOpen(opaqueType)) + return openOpaqueType(opaqueType, locator); + + return type; + }); +} + FunctionType *ConstraintSystem::openFunctionType( AnyFunctionType *funcType, ConstraintLocatorBuilder locator, @@ -997,8 +1055,11 @@ ConstraintSystem::getPropertyWrapperInformation( if (!decl->hasAttachedPropertyWrapper()) return None; - return std::make_pair(decl, - decl->getPropertyWrapperBackingPropertyType()); + auto backingTy = decl->getPropertyWrapperBackingPropertyType(); + if (!backingTy) + return None; + + return std::make_pair(decl, backingTy); }); } @@ -1398,12 +1459,8 @@ static void bindArchetypesFromContext( continue; } - // If it's not generic, there's nothing to do. auto genericSig = parentDC->getGenericSignatureOfContext(); - if (!genericSig) - break; - - for (auto *paramTy : genericSig->getGenericParams()) { + for (auto *paramTy : genericSig.getGenericParams()) { Type contextTy = cs.DC->mapTypeIntoContext(paramTy); bindPrimaryArchetype(paramTy, contextTy); } @@ -1435,7 +1492,7 @@ void ConstraintSystem::openGenericParameters(DeclContext *outerDC, assert(sig); // Create the type variables for the generic parameters. - for (auto gp : sig->getGenericParams()) { + for (auto gp : sig.getGenericParams()) { auto *paramLocator = getConstraintLocator( locator.withPathElement(LocatorPathElt::GenericParameter(gp))); @@ -1458,7 +1515,7 @@ void ConstraintSystem::openGenericRequirements( DeclContext *outerDC, GenericSignature signature, bool skipProtocolSelfConstraint, ConstraintLocatorBuilder locator, llvm::function_ref substFn) { - auto requirements = signature->getRequirements(); + auto requirements = signature.getRequirements(); for (unsigned pos = 0, n = requirements.size(); pos != n; ++pos) { const auto &req = requirements[pos]; @@ -2957,6 +3014,10 @@ Type ConstraintSystem::simplifyTypeImpl(Type type, // Simplify the base. Type newBase = simplifyTypeImpl(depMemTy->getBase(), getFixedTypeFn); + if (newBase->isPlaceholder()) { + return PlaceholderType::get(getASTContext(), depMemTy); + } + // If nothing changed, we're done. if (newBase.getPointer() == depMemTy->getBase().getPointer()) return type; @@ -3200,8 +3261,8 @@ static void diagnoseOperatorAmbiguity(ConstraintSystem &cs, const auto &solution = solutions.front(); if (auto *binaryOp = dyn_cast(applyExpr)) { - auto *lhs = binaryOp->getArg()->getElement(0); - auto *rhs = binaryOp->getArg()->getElement(1); + auto *lhs = binaryOp->getLHS(); + auto *rhs = binaryOp->getRHS(); auto lhsType = solution.simplifyType(solution.getType(lhs))->getRValueType(); @@ -3676,10 +3737,16 @@ static bool diagnoseAmbiguity( assert(fn); if (fn->getNumParams() == 1) { + auto argExpr = + simplifyLocatorToAnchor(solution.Fixes.front()->getLocator()); + assert(argExpr); + const auto ¶m = fn->getParams()[0]; + auto argType = solution.simplifyType(cs.getType(argExpr)); + DE.diagnose(noteLoc, diag::candidate_has_invalid_argument_at_position, solution.simplifyType(param.getPlainType()), - /*position=*/1, param.isInOut()); + /*position=*/1, param.isInOut(), argType); } else { DE.diagnose(noteLoc, diag::candidate_partial_match, fn->getParamListAsString(fn->getParams())); @@ -3703,6 +3770,151 @@ static bool diagnoseAmbiguity( return diagnosed; } +using FixInContext = std::pair; + +// Attempts to diagnose function call ambiguities of types inferred for a result +// generic parameter from contextual type and a closure argument that +// conflicting infer a different type for the same argument. Example: +// func callit(_ f: () -> T) -> T { +// f() +// } +// +// func context() -> Int { +// callit { +// print("hello") +// } +// } +// Where generic argument `T` can be inferred both as `Int` from contextual +// result and `Void` from the closure argument result. +static bool diagnoseContextualFunctionCallGenericAmbiguity( + ConstraintSystem &cs, ArrayRef contextualFixes, + ArrayRef allFixes) { + + if (contextualFixes.empty()) + return false; + + auto contextualFix = contextualFixes.front(); + if (!std::all_of(contextualFixes.begin() + 1, contextualFixes.end(), + [&contextualFix](FixInContext fix) { + return fix.second->getLocator() == + contextualFix.second->getLocator(); + })) + return false; + + auto fixLocator = contextualFix.second->getLocator(); + auto contextualAnchor = fixLocator->getAnchor(); + auto *AE = getAsExpr(contextualAnchor); + // All contextual failures anchored on the same function call. + if (!AE) + return false; + + auto fnLocator = cs.getConstraintLocator(AE->getSemanticFn()); + auto overload = contextualFix.first->getOverloadChoiceIfAvailable(fnLocator); + if (!overload) + return false; + + auto applyFnType = overload->openedType->castTo(); + auto resultTypeVar = applyFnType->getResult()->getAs(); + if (!resultTypeVar) + return false; + + auto *GP = resultTypeVar->getImpl().getGenericParameter(); + if (!GP) + return false; + + auto applyLoc = + cs.getConstraintLocator(AE, {LocatorPathElt::ApplyArgument()}); + auto argMatching = + contextualFix.first->argumentMatchingChoices.find(applyLoc); + if (argMatching == contextualFix.first->argumentMatchingChoices.end()) { + return false; + } + + auto typeParamResultInvolvesTypeVar = [&cs, &applyFnType, &argMatching]( + unsigned argIdx, + TypeVariableType *typeVar) { + auto argParamMatch = argMatching->second.parameterBindings[argIdx]; + auto param = applyFnType->getParams()[argParamMatch.front()]; + auto paramFnType = param.getPlainType()->castTo(); + return cs.typeVarOccursInType(typeVar, paramFnType->getResult()); + }; + + llvm::SmallVector closureArguments; + // A single closure argument. + if (auto *closure = + getAsExpr(AE->getArg()->getSemanticsProvidingExpr())) { + if (typeParamResultInvolvesTypeVar(/*paramIdx=*/0, resultTypeVar)) + closureArguments.push_back(closure); + } else if (auto *argTuple = getAsExpr(AE->getArg())) { + for (auto i : indices(argTuple->getElements())) { + auto arg = argTuple->getElements()[i]; + auto *closure = getAsExpr(arg); + if (closure && + typeParamResultInvolvesTypeVar(/*paramIdx=*/i, resultTypeVar)) { + closureArguments.push_back(closure); + } + } + } + + // If no closure result's involves the generic parameter, just bail because we + // won't find a conflict. + if (closureArguments.empty()) + return false; + + // At least one closure where result type involves the generic parameter. + // So let's try to collect the set of fixed types for the generic parameter + // from all the closure contextual fix/solutions and if there are more than + // one fixed type diagnose it. + llvm::SmallSetVector genericParamInferredTypes; + for (auto &fix : contextualFixes) + genericParamInferredTypes.insert(fix.first->getFixedType(resultTypeVar)); + + if (llvm::all_of(allFixes, [&](FixInContext fix) { + auto fixLocator = fix.second->getLocator(); + if (fixLocator->isForContextualType()) + return true; + + if (!(fix.second->getKind() == FixKind::ContextualMismatch || + fix.second->getKind() == FixKind::AllowTupleTypeMismatch)) + return false; + + auto anchor = fixLocator->getAnchor(); + if (!(anchor == contextualAnchor || + fixLocator->isLastElement() || + fixLocator->isLastElement())) + return false; + + genericParamInferredTypes.insert( + fix.first->getFixedType(resultTypeVar)); + return true; + })) { + + if (genericParamInferredTypes.size() != 2) + return false; + + auto &DE = cs.getASTContext().Diags; + llvm::SmallString<64> arguments; + llvm::raw_svector_ostream OS(arguments); + interleave( + genericParamInferredTypes, + [&](Type argType) { OS << "'" << argType << "'"; }, + [&OS] { OS << " vs. "; }); + + DE.diagnose(AE->getLoc(), diag::conflicting_arguments_for_generic_parameter, + GP, OS.str()); + + DE.diagnose(AE->getLoc(), + diag::generic_parameter_inferred_from_result_context, GP, + genericParamInferredTypes.back()); + DE.diagnose(closureArguments.front()->getStartLoc(), + diag::generic_parameter_inferred_from_closure, GP, + genericParamInferredTypes.front()); + + return true; + } + return false; +} + bool ConstraintSystem::diagnoseAmbiguityWithFixes( SmallVectorImpl &solutions) { if (solutions.empty()) @@ -3746,6 +3958,16 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( } } + // If there either no fixes at all or all of the are warnings, + // let's diagnose this as regular ambiguity. + if (llvm::all_of(solutions, [](const Solution &solution) { + return llvm::all_of(solution.Fixes, [](const ConstraintFix *fix) { + return fix->isWarning(); + }); + })) { + return diagnoseAmbiguity(solutions); + } + // Algorithm is as follows: // // a. Aggregate all of the available fixes based on callee locator; @@ -3755,16 +3977,15 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // d. Diagnose remaining (uniqued based on kind + locator) fixes // iff they appear in all of the solutions. - using Fix = std::pair; - - llvm::SmallSetVector fixes; + llvm::SmallSetVector fixes; for (auto &solution : solutions) { for (auto *fix : solution.Fixes) fixes.insert({&solution, fix}); } - llvm::MapVector> fixesByCallee; - llvm::SmallVector contextualFixes; + llvm::MapVector> + fixesByCallee; + llvm::SmallVector contextualFixes; for (const auto &entry : fixes) { const auto &solution = *entry.first; @@ -3784,7 +4005,7 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( bool diagnosed = false; // All of the fixes which have been considered already. - llvm::SmallSetVector consideredFixes; + llvm::SmallSetVector consideredFixes; for (const auto &ambiguity : solutionDiff.overloads) { auto fixes = fixesByCallee.find(ambiguity.locator); @@ -3807,7 +4028,8 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // overload choices. fixes.set_subtract(consideredFixes); - llvm::MapVector, SmallVector> + llvm::MapVector, + SmallVector> fixesByKind; for (const auto &entry : fixes) { @@ -3831,6 +4053,10 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( } } + if (!diagnosed && diagnoseContextualFunctionCallGenericAmbiguity( + *this, contextualFixes, fixes.getArrayRef())) + return true; + return diagnosed; } @@ -4109,6 +4335,10 @@ void constraints::simplifyLocator(ASTNode &anchor, path = path.slice(1); continue; + case ConstraintLocator::TupleType: + path = path.slice(1); + continue; + case ConstraintLocator::NamedTupleElement: case ConstraintLocator::TupleElement: { // Extract tuple element. @@ -4347,14 +4577,6 @@ bool constraints::hasAppliedSelf(const OverloadChoice &choice, doesMemberRefApplyCurriedSelf(baseType, decl); } -bool constraints::conformsToKnownProtocol(DeclContext *dc, Type type, - KnownProtocolKind protocol) { - if (auto *proto = - TypeChecker::getProtocol(dc->getASTContext(), SourceLoc(), protocol)) - return (bool)TypeChecker::conformsToProtocol(type, proto, dc); - return false; -} - /// Check whether given type conforms to `RawPepresentable` protocol /// and return the witness type. Type constraints::isRawRepresentable(ConstraintSystem &cs, Type type) { @@ -4365,24 +4587,14 @@ Type constraints::isRawRepresentable(ConstraintSystem &cs, Type type) { if (!rawReprType) return Type(); - auto conformance = TypeChecker::conformsToProtocol(type, rawReprType, DC); + auto conformance = TypeChecker::conformsToProtocol(type, rawReprType, + DC->getParentModule()); if (conformance.isInvalid()) return Type(); return conformance.getTypeWitnessByName(type, cs.getASTContext().Id_RawValue); } -Type constraints::isRawRepresentable( - ConstraintSystem &cs, Type type, - KnownProtocolKind rawRepresentableProtocol) { - Type rawTy = isRawRepresentable(cs, type); - if (!rawTy || - !conformsToKnownProtocol(cs.DC, rawTy, rawRepresentableProtocol)) - return Type(); - - return rawTy; -} - void ConstraintSystem::generateConstraints( SmallVectorImpl &constraints, Type type, ArrayRef choices, DeclContext *useDC, @@ -4718,6 +4930,18 @@ bool constraints::isStandardComparisonOperator(ASTNode node) { Optional> ConstraintSystem::isArgumentExpr(Expr *expr) { auto *argList = getParentExpr(expr); + if (!argList) { + return None; + } + + // Specifically for unresolved member expr getParentExpr returns a chain + // result expr as its immediate parent, so let's look one level up on AST. + if (auto *URMCR = getAsExpr(argList)) { + argList = getParentExpr(URMCR); + if (!argList) { + return None; + } + } if (isa(argList)) { for (;;) { @@ -4872,7 +5096,7 @@ ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion, // For an instance member, the base must be an @lvalue struct type. if (auto *lvt = simplifyType(getType(base))->getAs()) { auto *nominal = lvt->getObjectType()->getAnyNominal(); - if (nominal && isa(nominal)) { + if (isa_and_nonnull(nominal)) { subExpr = base; continue; } @@ -4944,8 +5168,9 @@ ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion, Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr, FunctionType *closureType, + DeclContext *ClosureContext, bool isDefaultWrappedValue, - bool isSpawnLetWrapper) { + bool isAsyncLetWrapper) { auto &Context = DC->getASTContext(); bool isInDefaultArgumentContext = false; if (auto *init = dyn_cast(DC)) { @@ -4962,12 +5187,13 @@ Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr, newClosureType = closureType->withExtInfo(info.withNoEscape(false)) ->castTo(); - auto *closure = new (Context) AutoClosureExpr( - expr, newClosureType, AutoClosureExpr::InvalidDiscriminator, DC); + auto *closure = new (Context) + AutoClosureExpr(expr, newClosureType, + AutoClosureExpr::InvalidDiscriminator, ClosureContext); closure->setParameterList(ParameterList::createEmpty(Context)); - if (isSpawnLetWrapper) + if (isAsyncLetWrapper) closure->setThunkKind(AutoClosureExpr::Kind::AsyncLet); Expr *result = closure; @@ -5070,7 +5296,14 @@ SolutionApplicationTarget::SolutionApplicationTarget( void SolutionApplicationTarget::maybeApplyPropertyWrapper() { assert(kind == Kind::expression); assert(expression.contextualPurpose == CTP_Initialization); - auto singleVar = expression.pattern->getSingleVar(); + + VarDecl *singleVar; + if (auto *pattern = expression.pattern) { + singleVar = pattern->getSingleVar(); + } else { + singleVar = expression.propertyWrapper.wrappedVar; + } + if (!singleVar) return; @@ -5196,12 +5429,30 @@ SolutionApplicationTarget SolutionApplicationTarget::forForEachStmt( } SolutionApplicationTarget -SolutionApplicationTarget::forUninitializedWrappedVar(VarDecl *wrappedVar) { - return SolutionApplicationTarget(wrappedVar); +SolutionApplicationTarget::forPropertyWrapperInitializer( + VarDecl *wrappedVar, DeclContext *dc, Expr *initializer) { + SolutionApplicationTarget target( + initializer, dc, CTP_Initialization, wrappedVar->getType(), + /*isDiscarded=*/false); + target.expression.propertyWrapper.wrappedVar = wrappedVar; + if (auto *patternBinding = wrappedVar->getParentPatternBinding()) { + auto index = patternBinding->getPatternEntryIndexForVarDecl(wrappedVar); + target.expression.initialization.patternBinding = patternBinding; + target.expression.initialization.patternBindingIndex = index; + target.expression.pattern = patternBinding->getPattern(index); + } + target.maybeApplyPropertyWrapper(); + return target; } ContextualPattern SolutionApplicationTarget::getContextualPattern() const { + if (kind == Kind::uninitializedVar) { + assert(patternBinding); + return ContextualPattern::forPatternBindingDecl(patternBinding, + uninitializedVar.index); + } + assert(kind == Kind::expression); assert(expression.contextualPurpose == CTP_Initialization || expression.contextualPurpose == CTP_ForEachStmt); @@ -5219,23 +5470,11 @@ bool SolutionApplicationTarget::infersOpaqueReturnType() const { assert(kind == Kind::expression); switch (expression.contextualPurpose) { case CTP_Initialization: - if (Type convertType = expression.convertType.getType()) { - return convertType->is(); - } - return false; - case CTP_ReturnStmt: case CTP_ReturnSingleExpr: - if (Type convertType = expression.convertType.getType()) { - if (auto opaqueType = convertType->getAs()) { - auto dc = getDeclContext(); - if (auto func = dyn_cast(dc)) { - return opaqueType->getDecl()->isOpaqueReturnTypeOfFunction(func); - } - } - } + if (Type convertType = expression.convertType.getType()) + return convertType->hasOpaqueArchetype(); return false; - default: return false; } @@ -5286,6 +5525,14 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) { SWIFT_DEFER { setPhase(ConstraintSystemPhase::Finalization); }; auto &DE = getASTContext().Diags; + + // If constraint system is in invalid state always produce + // a fallback diagnostic that asks to file a bug. + if (inInvalidState()) { + DE.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic); + return; + } + if (auto expr = target.getAsExpr()) { if (auto *assignment = dyn_cast(expr)) { if (isa(assignment->getDest())) @@ -5321,6 +5568,8 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) { nominal->diagnose(diag::property_wrapper_declared_here, nominal->getName()); } + } else if (auto *var = target.getAsUninitializedVar()) { + DE.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic); } else { // Emit a poor fallback message. DE.diagnose(target.getAsFunction()->getLoc(), @@ -5415,7 +5664,7 @@ static Optional getRequirement(ConstraintSystem &cs, if (auto openedGeneric = reqLocator->findLast()) { auto signature = openedGeneric->getSignature(); - return signature->getRequirements()[reqLoc->getIndex()]; + return signature.getRequirements()[reqLoc->getIndex()]; } return None; @@ -5641,7 +5890,6 @@ bool TypeVarBindingProducer::requiresOptionalAdjustment( if (auto *nominalBindingDecl = type->getAnyNominal()) { SmallVector conformances; conformsToExprByNilLiteral = nominalBindingDecl->lookupConformance( - CS.DC->getParentModule(), CS.getASTContext().getProtocol( KnownProtocolKind::ExpressibleByNilLiteral), conformances); @@ -5709,7 +5957,7 @@ ASTNode constraints::findAsyncNode(ClosureExpr *closure) { bool walkToDeclPre(Decl *decl) override { // Do not walk into function or type declarations. if (auto *patternBinding = dyn_cast(decl)) { - if (patternBinding->isSpawnLet()) + if (patternBinding->isAsyncLet()) AsyncNode = patternBinding; return true; diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index 654b89409d974..0a75cbc4af1af 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -239,7 +239,7 @@ class DebuggerTestingTransform : public ASTWalker { Closure->setBody(ClosureBody, /*isSingleExpression=*/false); // Call the closure. - auto *ClosureCall = CallExpr::createImplicit(Ctx, Closure, {}, {}); + auto *ClosureCall = CallExpr::createImplicitEmpty(Ctx, Closure); ClosureCall->setThrows(false); // TODO: typeCheckExpression() seems to assign types to everything here, diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index e6be800376468..9fbdce4587ee1 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -70,8 +70,8 @@ static Expr *constructUnownedSerialExecutor(ASTContext &ctx, // (Builtin.Executor) -> UnownedSerialExecutor auto metatypeRef = TypeExpr::createImplicit(executorType, ctx); Type ctorAppliedType = ctorType->getAs()->getResult(); - auto selfApply = new (ctx) ConstructorRefCallExpr(initRef, metatypeRef, - ctorAppliedType); + auto selfApply = ConstructorRefCallExpr::create(ctx, initRef, metatypeRef, + ctorAppliedType); selfApply->setImplicit(true); selfApply->setThrows(false); diff --git a/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp b/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp index f530c47b4ead1..c1f5a1c9ba865 100644 --- a/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp +++ b/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp @@ -77,7 +77,8 @@ bool DerivedConformance::canDeriveAdditiveArithmetic(NominalTypeDecl *nominal, if (v->getInterfaceType()->hasError()) return false; auto varType = DC->mapTypeIntoContext(v->getValueInterfaceType()); - return (bool)TypeChecker::conformsToProtocol(varType, proto, DC); + return (bool)TypeChecker::conformsToProtocol(varType, proto, + DC->getParentModule()); }); } @@ -97,7 +98,7 @@ deriveBodyMathOperator(AbstractFunctionDecl *funcDecl, MathOperator op) { auto *nominalTypeExpr = TypeExpr::createImplicitForDecl( DeclNameLoc(), nominal, funcDecl, funcDecl->mapTypeIntoContext(nominal->getInterfaceType())); - auto *initExpr = new (C) ConstructorRefCallExpr(initDRE, nominalTypeExpr); + auto *initExpr = ConstructorRefCallExpr::create(C, initDRE, nominalTypeExpr); // Get operator protocol requirement. auto *proto = C.getProtocol(KnownProtocolKind::AdditiveArithmetic); @@ -142,13 +143,8 @@ deriveBodyMathOperator(AbstractFunctionDecl *funcDecl, MathOperator op) { DeclNameLoc(), /*Implicit*/ true); auto *rhsArg = new (C) MemberRefExpr(rhsDRE, SourceLoc(), member, DeclNameLoc(), /*Implicit*/ true); - auto *memberOpArgs = - TupleExpr::create(C, SourceLoc(), {lhsArg, rhsArg}, {}, {}, SourceLoc(), - /*HasTrailingClosure*/ false, - /*Implicit*/ true); - auto *memberOpCallExpr = - new (C) BinaryExpr(memberOpExpr, memberOpArgs, /*Implicit*/ true); - return memberOpCallExpr; + return BinaryExpr::create(C, lhsArg, memberOpExpr, rhsArg, + /*implicit*/ true); }; // Create array of member operator call expressions. @@ -233,7 +229,7 @@ deriveBodyPropertyGetter(AbstractFunctionDecl *funcDecl, ProtocolDecl *proto, auto *nominalTypeExpr = TypeExpr::createImplicitForDecl( DeclNameLoc(), nominal, funcDecl, funcDecl->mapTypeIntoContext(nominal->getInterfaceType())); - auto *initExpr = new (C) ConstructorRefCallExpr(initDRE, nominalTypeExpr); + auto *initExpr = ConstructorRefCallExpr::create(C, initDRE, nominalTypeExpr); auto createMemberPropertyExpr = [&](VarDecl *member) -> Expr * { auto memberType = diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index dc3565d7c979b..0280f09ccdc5b 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -48,11 +48,20 @@ static bool superclassConformsTo(ClassDecl *target, KnownProtocolKind kpk) { } /// Retrieve the variable name for the purposes of encoding/decoding. -static Identifier getVarNameForCoding(VarDecl *var) { +/// +/// \param paramIndex if set will be used to generate name in the form of +/// '_$paramIndex' when VarDecl has no name. +static Identifier getVarNameForCoding(VarDecl *var, + Optional paramIndex = None) { + auto &C = var->getASTContext(); + Identifier identifier = var->getName(); if (auto originalVar = var->getOriginalWrappedProperty()) - return originalVar->getName(); + identifier = originalVar->getName(); + + if (identifier.empty() && paramIndex.hasValue()) + return C.getIdentifier("_" + std::to_string(paramIndex.getValue())); - return var->getName(); + return identifier; } /// Compute the Identifier for the CodingKey of an enum case @@ -60,9 +69,8 @@ static Identifier caseCodingKeysIdentifier(const ASTContext &C, EnumElementDecl *elt) { llvm::SmallString<16> scratch; camel_case::appendSentenceCase(scratch, elt->getBaseIdentifier().str()); - llvm::StringRef result = - camel_case::appendSentenceCase(scratch, C.Id_CodingKeys.str()); - return C.getIdentifier(result); + scratch += C.Id_CodingKeys.str(); + return C.getIdentifier(scratch.str()); } /// Fetches the \c CodingKeys enum nested in \c target, potentially reaching @@ -118,86 +126,84 @@ static NominalTypeDecl *lookupErrorContext(ASTContext &C, return dyn_cast(decl); } -static EnumDecl *addImplicitCodingKeys_enum(EnumDecl *target) { +static EnumDecl * +addImplicitCodingKeys(NominalTypeDecl *target, + llvm::SmallVectorImpl &caseIdentifiers, + Identifier codingKeysEnumIdentifier) { auto &C = target->getASTContext(); + assert(target->lookupDirect(DeclName(codingKeysEnumIdentifier)).empty()); - // We want to look through all the case declarations of this enum to create - // enum cases based on those case names. + // We want to look through all the var declarations of this type to create + // enum cases based on those var names. auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); auto codingKeyType = codingKeyProto->getDeclaredInterfaceType(); - TypeLoc protoTypeLoc[1] = {TypeLoc::withoutLoc(codingKeyType)}; - ArrayRef inherited = C.AllocateCopy(protoTypeLoc); - - llvm::SmallVector codingKeys; + InheritedEntry protoTypeLoc[1] = { + InheritedEntry(TypeLoc::withoutLoc(codingKeyType))}; + ArrayRef inherited = C.AllocateCopy(protoTypeLoc); - auto *enumDecl = new (C) EnumDecl(SourceLoc(), C.Id_CodingKeys, SourceLoc(), - inherited, nullptr, target); + auto *enumDecl = new (C) EnumDecl(SourceLoc(), codingKeysEnumIdentifier, + SourceLoc(), inherited, nullptr, target); enumDecl->setImplicit(); + enumDecl->setSynthesized(); enumDecl->setAccess(AccessLevel::Private); - for (auto *elementDecl : target->getAllElements()) { - auto *elt = - new (C) EnumElementDecl(SourceLoc(), elementDecl->getBaseName(), - nullptr, SourceLoc(), nullptr, enumDecl); + // For classes which inherit from something Encodable or Decodable, we + // provide case `super` as the first key (to be used in encoding super). + auto *classDecl = dyn_cast(target); + if (superclassConformsTo(classDecl, KnownProtocolKind::Encodable) || + superclassConformsTo(classDecl, KnownProtocolKind::Decodable)) { + // TODO: Ensure the class doesn't already have or inherit a variable named + // "`super`"; otherwise we will generate an invalid enum. In that case, + // diagnose and bail. + auto *super = new (C) EnumElementDecl(SourceLoc(), C.Id_super, nullptr, + SourceLoc(), nullptr, enumDecl); + super->setImplicit(); + enumDecl->addMember(super); + } + + for (auto caseIdentifier : caseIdentifiers) { + auto *elt = new (C) EnumElementDecl(SourceLoc(), caseIdentifier, nullptr, + SourceLoc(), nullptr, enumDecl); elt->setImplicit(); enumDecl->addMember(elt); } + // Forcibly derive conformance to CodingKey. TypeChecker::checkConformancesInContext(enumDecl); + // Add to the type. target->addMember(enumDecl); return enumDecl; } static EnumDecl *addImplicitCaseCodingKeys(EnumDecl *target, - EnumElementDecl *elementDecl, - EnumDecl *codingKeysEnum) { + EnumElementDecl *elementDecl, + EnumDecl *codingKeysEnum) { auto &C = target->getASTContext(); - auto enumIdentifier = caseCodingKeysIdentifier(C, elementDecl); - - auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); - auto codingKeyType = codingKeyProto->getDeclaredInterfaceType(); - TypeLoc protoTypeLoc[1] = {TypeLoc::withoutLoc(codingKeyType)}; - ArrayRef inherited = C.AllocateCopy(protoTypeLoc); - // Only derive if this case exist in the CodingKeys enum auto *codingKeyCase = lookupEnumCase(C, codingKeysEnum, elementDecl->getBaseIdentifier()); if (!codingKeyCase) return nullptr; - auto *caseEnum = new (C) EnumDecl(SourceLoc(), enumIdentifier, SourceLoc(), - inherited, nullptr, target); - caseEnum->setImplicit(); - caseEnum->setAccess(AccessLevel::Private); + auto enumIdentifier = caseCodingKeysIdentifier(C, elementDecl); + llvm::SmallVector caseIdentifiers; if (elementDecl->hasAssociatedValues()) { for (auto entry : llvm::enumerate(*elementDecl->getParameterList())) { auto *paramDecl = entry.value(); // if the type conforms to {En,De}codable, add it to the enum. - Identifier paramIdentifier = getVarNameForCoding(paramDecl); - bool generatedName = false; - if (paramIdentifier.empty()) { - paramIdentifier = C.getIdentifier("_" + std::to_string(entry.index())); - generatedName = true; - } - - auto *elt = - new (C) EnumElementDecl(SourceLoc(), paramIdentifier, nullptr, - SourceLoc(), nullptr, caseEnum); - elt->setImplicit(); - caseEnum->addMember(elt); + Identifier paramIdentifier = + getVarNameForCoding(paramDecl, entry.index()); + + caseIdentifiers.push_back(paramIdentifier); } } - // Forcibly derive conformance to CodingKey. - TypeChecker::checkConformancesInContext(caseEnum); - target->addMember(caseEnum); - - return caseEnum; + return addImplicitCodingKeys(target, caseIdentifiers, enumIdentifier); } // Create CodingKeys in the parent type always, because both @@ -210,67 +216,46 @@ static EnumDecl *addImplicitCaseCodingKeys(EnumDecl *target, // CodingKeys. It will also help in our quest to separate semantic and parsed // members. static EnumDecl *addImplicitCodingKeys(NominalTypeDecl *target) { - if (auto *enumDecl = dyn_cast(target)) { - return addImplicitCodingKeys_enum(enumDecl); - } - auto &C = target->getASTContext(); - assert(target->lookupDirect(DeclName(C.Id_CodingKeys)).empty()); - - // We want to look through all the var declarations of this type to create - // enum cases based on those var names. - auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); - auto codingKeyType = codingKeyProto->getDeclaredInterfaceType(); - TypeLoc protoTypeLoc[1] = {TypeLoc::withoutLoc(codingKeyType)}; - ArrayRef inherited = C.AllocateCopy(protoTypeLoc); - - auto *enumDecl = new (C) EnumDecl(SourceLoc(), C.Id_CodingKeys, SourceLoc(), - inherited, nullptr, target); - enumDecl->setImplicit(); - enumDecl->setSynthesized(); - enumDecl->setAccess(AccessLevel::Private); - // For classes which inherit from something Encodable or Decodable, we - // provide case `super` as the first key (to be used in encoding super). - auto *classDecl = dyn_cast(target); - if (superclassConformsTo(classDecl, KnownProtocolKind::Encodable) || - superclassConformsTo(classDecl, KnownProtocolKind::Decodable)) { - // TODO: Ensure the class doesn't already have or inherit a variable named - // "`super`"; otherwise we will generate an invalid enum. In that case, - // diagnose and bail. - auto *super = new (C) EnumElementDecl(SourceLoc(), C.Id_super, nullptr, - SourceLoc(), nullptr, enumDecl); - super->setImplicit(); - enumDecl->addMember(super); - } - - for (auto *varDecl : target->getStoredProperties()) { - if (!varDecl->isUserAccessible()) { - continue; + llvm::SmallVector caseIdentifiers; + if (auto *enumDecl = dyn_cast(target)) { + for (auto *elementDecl : enumDecl->getAllElements()) { + caseIdentifiers.push_back(elementDecl->getBaseIdentifier()); } + } else { + for (auto *varDecl : target->getStoredProperties()) { + if (!varDecl->isUserAccessible()) { + continue; + } - auto *elt = - new (C) EnumElementDecl(SourceLoc(), getVarNameForCoding(varDecl), - nullptr, SourceLoc(), nullptr, enumDecl); - elt->setImplicit(); - enumDecl->addMember(elt); + caseIdentifiers.push_back(getVarNameForCoding(varDecl)); + } } - // Forcibly derive conformance to CodingKey. - TypeChecker::checkConformancesInContext(enumDecl); - - // Add to the type. - target->addMember(enumDecl); + return addImplicitCodingKeys(target, caseIdentifiers, C.Id_CodingKeys); +} - return enumDecl; +namespace { + /// Container for a set of functions that produces notes used when a + /// synthesized conformance fails. + struct DelayedNotes : public std::vector> { + ~DelayedNotes() { + for (const auto &fn : *this) { + fn(); + } + } + }; } static EnumDecl *validateCodingKeysType(const DerivedConformance &derived, - TypeDecl *_codingKeysTypeDecl) { + TypeDecl *_codingKeysTypeDecl, + DelayedNotes &delayedNotes) { auto &C = derived.Context; - auto *codingKeysTypeDecl = _codingKeysTypeDecl; // CodingKeys may be a typealias. If so, follow the alias to its canonical - // type. + // type. We are creating a copy here, so we can hold on to the original + // `TypeDecl` in case we need to produce a diagnostic. + auto *codingKeysTypeDecl = _codingKeysTypeDecl; auto codingKeysType = codingKeysTypeDecl->getDeclaredInterfaceType(); if (isa(codingKeysTypeDecl)) codingKeysTypeDecl = codingKeysType->getAnyNominal(); @@ -278,7 +263,7 @@ static EnumDecl *validateCodingKeysType(const DerivedConformance &derived, // Ensure that the type we found conforms to the CodingKey protocol. auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); if (!TypeChecker::conformsToProtocol(codingKeysType, codingKeyProto, - derived.getConformanceContext())) { + derived.getParentModule())) { // If CodingKeys is a typealias which doesn't point to a valid nominal type, // codingKeysTypeDecl will be nullptr here. In that case, we need to warn on // the location of the usage, since there isn't an underlying type to @@ -286,17 +271,22 @@ static EnumDecl *validateCodingKeysType(const DerivedConformance &derived, SourceLoc loc = codingKeysTypeDecl ? codingKeysTypeDecl->getLoc() : cast(_codingKeysTypeDecl)->getLoc(); - C.Diags.diagnose(loc, diag::codable_codingkeys_type_does_not_conform_here, - derived.getProtocolType()); + delayedNotes.push_back([=] { + ASTContext &C = derived.getProtocolType()->getASTContext(); + C.Diags.diagnose(loc, diag::codable_codingkeys_type_does_not_conform_here, + derived.getProtocolType()); + }); return nullptr; } auto *codingKeysDecl = dyn_cast_or_null(codingKeysType->getAnyNominal()); if (!codingKeysDecl) { - codingKeysTypeDecl->diagnose( - diag::codable_codingkeys_type_is_not_an_enum_here, - derived.getProtocolType()); + delayedNotes.push_back([=] { + codingKeysTypeDecl->diagnose( + diag::codable_codingkeys_type_is_not_an_enum_here, + derived.getProtocolType()); + }); return nullptr; } @@ -310,8 +300,10 @@ static EnumDecl *validateCodingKeysType(const DerivedConformance &derived, /// \param codingKeysTypeDecl The \c CodingKeys enum decl to validate. static bool validateCodingKeysEnum(const DerivedConformance &derived, llvm::SmallMapVector varDecls, - TypeDecl *codingKeysTypeDecl) { - auto *codingKeysDecl = validateCodingKeysType(derived, codingKeysTypeDecl); + TypeDecl *codingKeysTypeDecl, + DelayedNotes &delayedNotes) { + auto *codingKeysDecl = validateCodingKeysType( + derived, codingKeysTypeDecl, delayedNotes); if (!codingKeysDecl) return false; @@ -325,8 +317,10 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived, for (auto elt : codingKeysDecl->getAllElements()) { auto it = varDecls.find(elt->getBaseIdentifier()); if (it == varDecls.end()) { - elt->diagnose(diag::codable_extraneous_codingkey_case_here, - elt->getBaseIdentifier()); + delayedNotes.push_back([=] { + elt->diagnose(diag::codable_extraneous_codingkey_case_here, + elt->getBaseIdentifier()); + }); // TODO: Investigate typo-correction here; perhaps the case name was // misspelled and we can provide a fix-it. varDeclsAreValid = false; @@ -337,14 +331,19 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived, auto target = derived.getConformanceContext()->mapTypeIntoContext( it->second->getValueInterfaceType()); if (TypeChecker::conformsToProtocol(target, derived.Protocol, - derived.getConformanceContext()) + derived.getParentModule()) .isInvalid()) { TypeLoc typeLoc = { it->second->getTypeReprOrParentPatternTypeRepr(), it->second->getType(), }; - it->second->diagnose(diag::codable_non_conforming_property_here, - derived.getProtocolType(), typeLoc); + + auto var = it->second; + auto proto = derived.getProtocolType(); + delayedNotes.push_back([=] { + var->diagnose(diag::codable_non_conforming_property_here, + proto, typeLoc); + }); varDeclsAreValid = false; } else { // The property was valid. Remove it from the list. @@ -378,8 +377,10 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived, // The var was not default initializable, and did not have an explicit // initial value. varDeclsAreValid = false; - entry.second->diagnose(diag::codable_non_decoded_property_here, - derived.getProtocolType(), entry.first); + delayedNotes.push_back([=] { + entry.second->diagnose(diag::codable_non_decoded_property_here, + derived.getProtocolType(), entry.first); + }); } } @@ -387,7 +388,8 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived, } static bool validateCodingKeysEnum_enum(const DerivedConformance &derived, - TypeDecl *codingKeysTypeDecl) { + TypeDecl *codingKeysTypeDecl, + DelayedNotes &delayedNotes) { auto *enumDecl = dyn_cast(derived.Nominal); if (!enumDecl) { return false; @@ -397,16 +399,18 @@ static bool validateCodingKeysEnum_enum(const DerivedConformance &derived, caseNames.insert(elt->getBaseIdentifier()); } - auto *codingKeysDecl = validateCodingKeysType(derived, - codingKeysTypeDecl); + auto *codingKeysDecl = validateCodingKeysType( + derived, codingKeysTypeDecl, delayedNotes); if (!codingKeysDecl) return false; bool casesAreValid = true; for (auto *elt : codingKeysDecl->getAllElements()) { if (!caseNames.contains(elt->getBaseIdentifier())) { - elt->diagnose(diag::codable_extraneous_codingkey_case_here, - elt->getBaseIdentifier()); + delayedNotes.push_back([=] { + elt->diagnose(diag::codable_extraneous_codingkey_case_here, + elt->getBaseIdentifier()); + }); casesAreValid = false; } } @@ -416,7 +420,8 @@ static bool validateCodingKeysEnum_enum(const DerivedConformance &derived, /// Looks up and validates a CodingKeys enum for the given DerivedConformance. /// If a CodingKeys enum does not exist, one will be derived. -static bool validateCodingKeysEnum(const DerivedConformance &derived) { +static bool validateCodingKeysEnum(const DerivedConformance &derived, + DelayedNotes &delayedNotes) { auto &C = derived.Context; auto codingKeysDecls = @@ -431,13 +436,16 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived) { : codingKeysDecls.front(); auto *codingKeysTypeDecl = dyn_cast(result); if (!codingKeysTypeDecl) { - result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here, - derived.getProtocolType()); + delayedNotes.push_back([=] { + result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here, + derived.getProtocolType()); + }); return false; } if (dyn_cast(derived.Nominal)) { - return validateCodingKeysEnum_enum(derived, codingKeysTypeDecl); + return validateCodingKeysEnum_enum( + derived, codingKeysTypeDecl, delayedNotes); } else { // Look through all var decls in the given type. @@ -454,7 +462,8 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived) { properties[getVarNameForCoding(varDecl)] = varDecl; } - return validateCodingKeysEnum(derived, properties, codingKeysTypeDecl); + return validateCodingKeysEnum( + derived, properties, codingKeysTypeDecl, delayedNotes); } } @@ -463,7 +472,8 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived) { /// /// \param elementDecl The \c EnumElementDecl to validate against. static bool validateCaseCodingKeysEnum(const DerivedConformance &derived, - EnumElementDecl *elementDecl) { + EnumElementDecl *elementDecl, + DelayedNotes &delayedNotes) { auto &C = derived.Context; auto *enumDecl = dyn_cast(derived.Nominal); if (!enumDecl) { @@ -488,10 +498,21 @@ static bool validateCaseCodingKeysEnum(const DerivedConformance &derived, ? addImplicitCaseCodingKeys( enumDecl, elementDecl, codingKeysEnum) : caseCodingKeysDecls.front(); + + if (!result) { + // There is no coding key defined for this element, + // which is okay, because not all elements have to + // be considered for serialization. Attempts to + // en-/decode them will be handled at runtime. + return true; + } + auto *codingKeysTypeDecl = dyn_cast(result); if (!codingKeysTypeDecl) { - result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here, - derived.getProtocolType()); + delayedNotes.push_back([=] { + result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here, + derived.getProtocolType()); + }); return false; } @@ -504,16 +525,13 @@ static bool validateCaseCodingKeysEnum(const DerivedConformance &derived, if (!paramDecl->isUserAccessible()) continue; - auto identifier = getVarNameForCoding(paramDecl); - if (identifier.empty()) { - identifier = C.getIdentifier("_" + std::to_string(entry.index())); - } - + auto identifier = getVarNameForCoding(paramDecl, entry.index()); properties[identifier] = paramDecl; } } - return validateCodingKeysEnum(derived, properties, codingKeysTypeDecl); + return validateCodingKeysEnum( + derived, properties, codingKeysTypeDecl, delayedNotes); } /// Creates a new var decl representing @@ -649,13 +667,18 @@ static CallExpr *createNestedContainerKeyedByForKeyCall( argNames); } -static ThrowStmt *createThrowDecodingErrorTypeMismatchStmt( - ASTContext &C, DeclContext *DC, NominalTypeDecl *targetDecl, - Expr *containerExpr, Expr *debugMessage) { - auto *errorDecl = C.getDecodingErrorDecl(); +static ThrowStmt *createThrowCodingErrorStmt(ASTContext &C, Expr *containerExpr, + NominalTypeDecl *errorDecl, + Identifier errorId, + Optional argument, + StringRef debugMessage) { auto *contextDecl = lookupErrorContext(C, errorDecl); assert(contextDecl && "Missing Context decl."); + auto *debugMessageExpr = new (C) StringLiteralExpr( + C.AllocateCopy(debugMessage), SourceRange(), + /* Implicit */ true); + auto *contextTypeExpr = TypeExpr::createImplicit(contextDecl->getDeclaredType(), C); @@ -669,61 +692,29 @@ static ThrowStmt *createThrowDecodingErrorTypeMismatchStmt( auto *contextInitCallExpr = CallExpr::createImplicit( C, contextInitCall, - {codingPathExpr, debugMessage, + {codingPathExpr, debugMessageExpr, new (C) NilLiteralExpr(SourceLoc(), /* Implicit */ true)}, {C.Id_codingPath, C.Id_debugDescription, C.Id_underlyingError}); - auto *decodingErrorTypeExpr = - TypeExpr::createImplicit(errorDecl->getDeclaredType(), C); - auto *decodingErrorCall = UnresolvedDotExpr::createImplicit( - C, decodingErrorTypeExpr, C.Id_typeMismatch, - {Identifier(), Identifier()}); - auto *targetType = TypeExpr::createImplicit( - DC->mapTypeIntoContext(targetDecl->getDeclaredInterfaceType()), C); - auto *targetTypeExpr = - new (C) DotSelfExpr(targetType, SourceLoc(), SourceLoc()); - - auto *decodingErrorCallExpr = CallExpr::createImplicit( - C, decodingErrorCall, {targetTypeExpr, contextInitCallExpr}, - {Identifier(), Identifier()}); - return new (C) ThrowStmt(SourceLoc(), decodingErrorCallExpr); -} - -static ThrowStmt *createThrowEncodingErrorInvalidValueStmt(ASTContext &C, - DeclContext *DC, - Expr *valueExpr, - Expr *containerExpr, - Expr *debugMessage) { - auto *errorDecl = C.getEncodingErrorDecl(); - auto *contextDecl = lookupErrorContext(C, errorDecl); - assert(contextDecl && "Missing Context decl."); - - auto *contextTypeExpr = - TypeExpr::createImplicit(contextDecl->getDeclaredType(), C); - - // Context.init(codingPath:, debugDescription:) - auto *contextInitCall = UnresolvedDotExpr::createImplicit( - C, contextTypeExpr, DeclBaseName::createConstructor(), - {C.Id_codingPath, C.Id_debugDescription, C.Id_underlyingError}); + llvm::SmallVector arguments; + llvm::SmallVector argumentLabels; - auto *codingPathExpr = - UnresolvedDotExpr::createImplicit(C, containerExpr, C.Id_codingPath); + if (argument.hasValue()) { + arguments.push_back(argument.getValue()); + argumentLabels.push_back(Identifier()); + } - auto *contextInitCallExpr = CallExpr::createImplicit( - C, contextInitCall, - {codingPathExpr, debugMessage, - new (C) NilLiteralExpr(SourceLoc(), /* Implicit */ true)}, - {C.Id_codingPath, C.Id_debugDescription, C.Id_underlyingError}); + arguments.push_back(contextInitCallExpr); + argumentLabels.push_back(Identifier()); auto *decodingErrorTypeExpr = TypeExpr::createImplicit(errorDecl->getDeclaredType(), C); auto *decodingErrorCall = UnresolvedDotExpr::createImplicit( - C, decodingErrorTypeExpr, C.Id_invalidValue, - {Identifier(), Identifier()}); + C, decodingErrorTypeExpr, errorId, C.AllocateCopy(argumentLabels)); - auto *decodingErrorCallExpr = CallExpr::createImplicit( - C, decodingErrorCall, {valueExpr, contextInitCallExpr}, - {Identifier(), Identifier()}); + auto *decodingErrorCallExpr = + CallExpr::createImplicit(C, decodingErrorCall, C.AllocateCopy(arguments), + C.AllocateCopy(argumentLabels)); return new (C) ThrowStmt(SourceLoc(), decodingErrorCallExpr); } @@ -769,6 +760,33 @@ lookupVarDeclForCodingKeysCase(DeclContext *conformanceDC, llvm_unreachable("Should have found at least 1 var decl"); } +static TryExpr *createEncodeCall(ASTContext &C, Type codingKeysType, + EnumElementDecl *codingKey, + Expr *containerExpr, Expr *varExpr, + bool useIfPresentVariant) { + // CodingKeys.x + auto *metaTyRef = TypeExpr::createImplicit(codingKeysType, C); + auto *keyExpr = new (C) MemberRefExpr(metaTyRef, SourceLoc(), codingKey, + DeclNameLoc(), /*Implicit=*/true); + + // encode(_:forKey:)/encodeIfPresent(_:forKey:) + auto methodName = useIfPresentVariant ? C.Id_encodeIfPresent : C.Id_encode; + SmallVector argNames{Identifier(), C.Id_forKey}; + + auto *encodeCall = + UnresolvedDotExpr::createImplicit(C, containerExpr, methodName, argNames); + + // container.encode(x, forKey: CodingKeys.x) + Expr *args[2] = {varExpr, keyExpr}; + auto *callExpr = CallExpr::createImplicit(C, encodeCall, C.AllocateCopy(args), + C.AllocateCopy(argNames)); + + // try container.encode(x, forKey: CodingKeys.x) + auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); + return tryExpr; +} + /// Synthesizes the body for `func encode(to encoder: Encoder) throws`. /// /// \param encodeDecl The function decl whose body to synthesize. @@ -864,28 +882,10 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) { ConcreteDeclRef(varDecl), DeclNameLoc(), /*Implicit=*/true); - // CodingKeys.x - auto *metaTyRef = TypeExpr::createImplicit(codingKeysType, C); - auto *keyExpr = new (C) MemberRefExpr(metaTyRef, SourceLoc(), elt, - DeclNameLoc(), /*Implicit=*/true); - - // encode(_:forKey:)/encodeIfPresent(_:forKey:) - auto methodName = useIfPresentVariant ? C.Id_encodeIfPresent : C.Id_encode; - SmallVector argNames{Identifier(), C.Id_forKey}; - - auto *encodeCall = UnresolvedDotExpr::createImplicit(C, containerExpr, - methodName, argNames); - - // container.encode(self.x, forKey: CodingKeys.x) - Expr *args[2] = {varExpr, keyExpr}; - auto *callExpr = CallExpr::createImplicit(C, encodeCall, - C.AllocateCopy(args), - C.AllocateCopy(argNames)); + auto *encodeCallExpr = createEncodeCall( + C, codingKeysType, elt, containerExpr, varExpr, useIfPresentVariant); - // try container.encode(self.x, forKey: CodingKeys.x) - auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*Implicit=*/true); - statements.push_back(tryExpr); + statements.push_back(encodeCallExpr); } // Classes which inherit from something Codable should encode super as well. @@ -897,8 +897,8 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) { auto *method = UnresolvedDeclRefExpr::createImplicit(C, C.Id_superEncoder); // container.superEncoder() - auto *superEncoderRef = new (C) DotSyntaxCallExpr(containerExpr, - SourceLoc(), method); + auto *superEncoderRef = + DotSyntaxCallExpr::create(C, containerExpr, SourceLoc(), method); // encode(to:) expr auto *encodeDeclRef = new (C) DeclRefExpr(ConcreteDeclRef(encodeDecl), @@ -909,8 +909,8 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) { SourceLoc(), /*Implicit=*/true); // super.encode(to:) - auto *encodeCall = new (C) DotSyntaxCallExpr(superRef, SourceLoc(), - encodeDeclRef); + auto *encodeCall = + DotSyntaxCallExpr::create(C, superRef, SourceLoc(), encodeDeclRef); // super.encode(to: container.superEncoder()) Expr *args[1] = {superEncoderRef}; @@ -930,6 +930,112 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) { return { body, /*isTypeChecked=*/false }; } +static SwitchStmt * +createEnumSwitch(ASTContext &C, DeclContext *DC, Expr *expr, EnumDecl *enumDecl, + EnumDecl *codingKeysEnum, bool createSubpattern, + std::function( + EnumElementDecl *, EnumElementDecl *, ArrayRef)> + createCase) { + SmallVector cases; + for (auto elt : enumDecl->getAllElements()) { + // .(let a0, let a1, ...) + SmallVector payloadVars; + Pattern *subpattern = nullptr; + Optional> caseBodyVarDecls; + + if (createSubpattern) { + subpattern = DerivedConformance::enumElementPayloadSubpattern( + elt, 'a', DC, payloadVars, /* useLabels */ true); + + auto hasBoundDecls = !payloadVars.empty(); + if (hasBoundDecls) { + // We allocated a direct copy of our var decls for the case + // body. + auto copy = C.Allocate(payloadVars.size()); + for (unsigned i : indices(payloadVars)) { + auto *vOld = payloadVars[i]; + auto *vNew = new (C) VarDecl( + /*IsStatic*/ false, vOld->getIntroducer(), vOld->getNameLoc(), + vOld->getName(), vOld->getDeclContext()); + vNew->setImplicit(); + copy[i] = vNew; + } + caseBodyVarDecls.emplace(copy); + } + } + + // CodingKeys.x + auto *codingKeyCase = + lookupEnumCase(C, codingKeysEnum, elt->getName().getBaseIdentifier()); + + EnumElementDecl *targetElt; + BraceStmt *caseBody; + std::tie(targetElt, caseBody) = createCase(elt, codingKeyCase, payloadVars); + + if (caseBody) { + // generate: case .: + auto pat = new (C) EnumElementPattern( + TypeExpr::createImplicit( + DC->mapTypeIntoContext( + targetElt->getParentEnum()->getDeclaredInterfaceType()), + C), + SourceLoc(), DeclNameLoc(), DeclNameRef(), targetElt, subpattern); + pat->setImplicit(); + + auto labelItem = CaseLabelItem(pat); + auto stmt = CaseStmt::create( + C, CaseParentKind::Switch, SourceLoc(), labelItem, SourceLoc(), + SourceLoc(), caseBody, + /*case body vardecls*/ createSubpattern ? caseBodyVarDecls : None); + cases.push_back(stmt); + } + } + + // generate: switch $expr { } + return SwitchStmt::createImplicit(LabeledStmtInfo(), expr, cases, C); +} + +static DeclRefExpr *createContainer(ASTContext &C, DeclContext *DC, + VarDecl::Introducer introducer, + NominalTypeDecl *containerTypeDecl, + VarDecl *target, EnumDecl *codingKeysEnum, + llvm::SmallVectorImpl &statements, + bool throws) { + // let/var container : KeyedDecodingContainer + auto *containerDecl = createKeyedContainer( + C, DC, containerTypeDecl, codingKeysEnum->getDeclaredInterfaceType(), + introducer); + + auto *containerExpr = + new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), DeclNameLoc(), + /*Implicit=*/true, AccessSemantics::DirectToStorage); + + // de/encoder + auto *decoderExpr = new (C) + DeclRefExpr(ConcreteDeclRef(target), DeclNameLoc(), /*Implicit=*/true); + + // Bound de/encoder.container(keyedBy: CodingKeys.self) call + auto containerType = containerDecl->getInterfaceType(); + Expr *callExpr = createContainerKeyedByCall(C, DC, decoderExpr, containerType, + codingKeysEnum); + + if (throws) { + // try decoder.container(keyedBy: CodingKeys.self) + callExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*implicit=*/true); + } + + // Full `let container = decoder.container(keyedBy: CodingKeys.self)` + // binding. + auto *containerPattern = NamedPattern::createImplicit(C, containerDecl); + auto *bindingDecl = PatternBindingDecl::createImplicit( + C, StaticSpellingKind::None, containerPattern, callExpr, DC); + statements.push_back(bindingDecl); + statements.push_back(containerDecl); + + return containerExpr; +} + static std::pair deriveBodyEncodable_enum_encode(AbstractFunctionDecl *encodeDecl, void *) { // enum Foo : Codable { @@ -986,185 +1092,105 @@ deriveBodyEncodable_enum_encode(AbstractFunctionDecl *encodeDecl, void *) { // properties to encode or decode, but the type is a class which inherits from // something Codable and needs to encode super. - // let container : KeyedEncodingContainer - auto *containerDecl = - createKeyedContainer(C, funcDC, C.getKeyedEncodingContainerDecl(), - codingKeysEnum->getDeclaredInterfaceType(), - VarDecl::Introducer::Var); - - auto *containerExpr = - new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), DeclNameLoc(), - /*Implicit=*/true, AccessSemantics::DirectToStorage); - // Need to generate // `let container = encoder.container(keyedBy: CodingKeys.self)` // This is unconditional because a type with no properties should encode as an // empty container. - // - // `let container` (containerExpr) is generated above. - - // encoder - auto encoderParam = encodeDecl->getParameters()->get(0); - auto *encoderExpr = new (C) DeclRefExpr(ConcreteDeclRef(encoderParam), - DeclNameLoc(), /*Implicit=*/true); - - // Bound encoder.container(keyedBy: CodingKeys.self) call - auto containerType = containerDecl->getInterfaceType(); - auto *callExpr = createContainerKeyedByCall(C, funcDC, encoderExpr, - containerType, codingKeysEnum); - // Full `let container = encoder.container(keyedBy: CodingKeys.self)` - // binding. - auto *containerPattern = NamedPattern::createImplicit(C, containerDecl); - auto *bindingDecl = PatternBindingDecl::createImplicit( - C, StaticSpellingKind::None, containerPattern, callExpr, funcDC); - statements.push_back(bindingDecl); - statements.push_back(containerDecl); + // let container : KeyedEncodingContainer + auto *containerExpr = createContainer( + C, funcDC, VarDecl::Introducer::Var, C.getKeyedEncodingContainerDecl(), + encodeDecl->getParameters()->get(0), codingKeysEnum, statements, + /*throws*/ false); auto *selfRef = encodeDecl->getImplicitSelfDecl(); - SmallVector cases; - for (auto elt : enumDecl->getAllElements()) { - // CodingKeys.x - auto *codingKeyCase = - lookupEnumCase(C, codingKeysEnum, elt->getName().getBaseIdentifier()); - - SmallVector caseStatements; - - // .(let a0, let a1, ...) - SmallVector payloadVars; - auto subpattern = DerivedConformance::enumElementPayloadSubpattern( - elt, 'a', encodeDecl, payloadVars, /* useLabels */ true); - - auto hasBoundDecls = !payloadVars.empty(); - Optional> caseBodyVarDecls; - if (hasBoundDecls) { - // We allocated a direct copy of our var decls for the case - // body. - auto copy = C.Allocate(payloadVars.size()); - for (unsigned i : indices(payloadVars)) { - auto *vOld = payloadVars[i]; - auto *vNew = new (C) VarDecl( - /*IsStatic*/ false, vOld->getIntroducer(), vOld->getNameLoc(), - vOld->getName(), vOld->getDeclContext()); - vNew->setImplicit(); - copy[i] = vNew; - } - caseBodyVarDecls.emplace(copy); - } - - if (!codingKeyCase) { - // This case should not be encodable, so throw an error if an attempt is - // made to encode it - llvm::SmallString<128> buffer; - buffer.append("Case '"); - buffer.append(elt->getBaseIdentifier().str()); - buffer.append( - "' cannot be decoded because it is not defined in CodingKeys."); - auto *debugMessage = new (C) StringLiteralExpr( - C.AllocateCopy(buffer.str()), SourceRange(), /* Implicit */ true); - auto *selfRefExpr = new (C) DeclRefExpr( - ConcreteDeclRef(selfRef), DeclNameLoc(), /* Implicit */ true); - auto *throwStmt = createThrowEncodingErrorInvalidValueStmt( - C, funcDC, selfRefExpr, containerExpr, debugMessage); - caseStatements.push_back(throwStmt); - } else { - auto caseIdentifier = caseCodingKeysIdentifier(C, elt); - auto *caseCodingKeys = - lookupEvaluatedCodingKeysEnum(C, enumDecl, caseIdentifier); - - auto *nestedContainerDecl = createKeyedContainer( - C, funcDC, C.getKeyedEncodingContainerDecl(), - caseCodingKeys->getDeclaredInterfaceType(), VarDecl::Introducer::Var, - C.Id_nestedContainer); - - auto *nestedContainerCall = createNestedContainerKeyedByForKeyCall( - C, funcDC, containerExpr, caseCodingKeys, codingKeyCase); - - auto *containerPattern = - NamedPattern::createImplicit(C, nestedContainerDecl); - auto *bindingDecl = PatternBindingDecl::createImplicit( - C, StaticSpellingKind::None, containerPattern, nestedContainerCall, - funcDC); - caseStatements.push_back(bindingDecl); - caseStatements.push_back(nestedContainerDecl); - - // TODO: use param decls to get names - for (auto entry : llvm::enumerate(payloadVars)) { - auto *payloadVar = entry.value(); - auto *nestedContainerExpr = new (C) - DeclRefExpr(ConcreteDeclRef(nestedContainerDecl), DeclNameLoc(), - /*Implicit=*/true, AccessSemantics::DirectToStorage); - auto payloadVarRef = new (C) DeclRefExpr(payloadVar, DeclNameLoc(), - /*implicit*/ true); - auto *paramDecl = elt->getParameterList()->get(entry.index()); - auto caseCodingKeysIdentifier = getVarNameForCoding(paramDecl); - if (caseCodingKeysIdentifier.empty()) { - caseCodingKeysIdentifier = C.getIdentifier("_" + std::to_string(entry.index())); - } - auto *caseCodingKey = - lookupEnumCase(C, caseCodingKeys, caseCodingKeysIdentifier); - - // If there is no key defined for this parameter, skip it. - if (!caseCodingKey) - continue; + // generate: switch self { } + auto enumRef = + new (C) DeclRefExpr(ConcreteDeclRef(selfRef), DeclNameLoc(), + /*implicit*/ true, AccessSemantics::Ordinary); + auto switchStmt = createEnumSwitch( + C, funcDC, enumRef, enumDecl, codingKeysEnum, + /*createSubpattern*/ true, + [&](auto *elt, auto *codingKeyCase, + auto payloadVars) -> std::tuple { + SmallVector caseStatements; + + if (!codingKeyCase) { + // This case should not be encodable, so throw an error if an attempt + // is made to encode it + auto debugMessage = + "Case '" + elt->getBaseIdentifier().str().str() + + "' cannot be encoded because it is not defined in CodingKeys."; + auto *selfRefExpr = new (C) DeclRefExpr( + ConcreteDeclRef(selfRef), DeclNameLoc(), /* Implicit */ true); + + auto *throwStmt = createThrowCodingErrorStmt( + C, containerExpr, C.getEncodingErrorDecl(), C.Id_invalidValue, + selfRefExpr, debugMessage); + + caseStatements.push_back(throwStmt); + } else { + auto caseIdentifier = caseCodingKeysIdentifier(C, elt); + auto *caseCodingKeys = + lookupEvaluatedCodingKeysEnum(C, enumDecl, caseIdentifier); + + auto *nestedContainerDecl = createKeyedContainer( + C, funcDC, C.getKeyedEncodingContainerDecl(), + caseCodingKeys->getDeclaredInterfaceType(), + VarDecl::Introducer::Var, C.Id_nestedContainer); + + auto *nestedContainerCall = createNestedContainerKeyedByForKeyCall( + C, funcDC, containerExpr, caseCodingKeys, codingKeyCase); + + auto *containerPattern = + NamedPattern::createImplicit(C, nestedContainerDecl); + auto *bindingDecl = PatternBindingDecl::createImplicit( + C, StaticSpellingKind::None, containerPattern, + nestedContainerCall, funcDC); + caseStatements.push_back(bindingDecl); + caseStatements.push_back(nestedContainerDecl); + + for (auto entry : llvm::enumerate(payloadVars)) { + auto *payloadVar = entry.value(); + auto *nestedContainerExpr = new (C) DeclRefExpr( + ConcreteDeclRef(nestedContainerDecl), DeclNameLoc(), + /*Implicit=*/true, AccessSemantics::DirectToStorage); + auto payloadVarRef = new (C) DeclRefExpr(payloadVar, DeclNameLoc(), + /*implicit*/ true); + auto *paramDecl = elt->getParameterList()->get(entry.index()); + auto caseCodingKeysIdentifier = + getVarNameForCoding(paramDecl, entry.index()); + auto *caseCodingKey = + lookupEnumCase(C, caseCodingKeys, caseCodingKeysIdentifier); + + // If there is no key defined for this parameter, skip it. + if (!caseCodingKey) + continue; + + auto varType = conformanceDC->mapTypeIntoContext( + payloadVar->getValueInterfaceType()); + + bool useIfPresentVariant = false; + if (auto objType = varType->getOptionalObjectType()) { + varType = objType; + useIfPresentVariant = true; + } - auto varType = conformanceDC->mapTypeIntoContext( - payloadVar->getValueInterfaceType()); + auto *encodeCallExpr = createEncodeCall( + C, caseCodingKeys->getDeclaredType(), caseCodingKey, + nestedContainerExpr, payloadVarRef, useIfPresentVariant); - bool useIfPresentVariant = false; - if (auto objType = varType->getOptionalObjectType()) { - varType = objType; - useIfPresentVariant = true; + caseStatements.push_back(encodeCallExpr); + } } - // BarCodingKeys.x - auto *metaTyRef = - TypeExpr::createImplicit(caseCodingKeys->getDeclaredType(), C); - auto *keyExpr = - new (C) MemberRefExpr(metaTyRef, SourceLoc(), caseCodingKey, - DeclNameLoc(), /*Implicit=*/true); - - // encode(_:forKey:)/encodeIfPresent(_:forKey:) - auto methodName = - useIfPresentVariant ? C.Id_encodeIfPresent : C.Id_encode; - SmallVector argNames{Identifier(), C.Id_forKey}; - - auto *encodeCall = UnresolvedDotExpr::createImplicit( - C, nestedContainerExpr, methodName, argNames); - - // nestedContainer.encode(x, forKey: CodingKeys.x) - Expr *args[2] = {payloadVarRef, keyExpr}; - auto *callExpr = CallExpr::createImplicit( - C, encodeCall, C.AllocateCopy(args), C.AllocateCopy(argNames)); - - // try nestedContainer.encode(x, forKey: CodingKeys.x) - auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*Implicit=*/true); - caseStatements.push_back(tryExpr); - } - } - - // generate: case .: - auto pat = new (C) EnumElementPattern( - TypeExpr::createImplicit(enumDecl->getDeclaredType(), C), SourceLoc(), - DeclNameLoc(), DeclNameRef(), elt, subpattern); - pat->setImplicit(); - - auto labelItem = CaseLabelItem(pat); - auto body = BraceStmt::create(C, SourceLoc(), caseStatements, SourceLoc()); - cases.push_back(CaseStmt::create(C, CaseParentKind::Switch, SourceLoc(), - labelItem, SourceLoc(), SourceLoc(), body, - /*case body vardecls*/ caseBodyVarDecls)); - } + auto body = + BraceStmt::create(C, SourceLoc(), caseStatements, SourceLoc()); - // generate: switch self { } - auto enumRef = - new (C) DeclRefExpr(ConcreteDeclRef(selfRef), DeclNameLoc(), - /*implicit*/ true, AccessSemantics::Ordinary); + return std::make_tuple(elt, body); + }); - auto switchStmt = - SwitchStmt::createImplicit(LabeledStmtInfo(), enumRef, cases, C); statements.push_back(switchStmt); auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(), @@ -1233,6 +1259,40 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) { return encodeDecl; } +static TryExpr *createDecodeCall(ASTContext &C, Type resultType, + Type codingKeysType, + EnumElementDecl *codingKey, + Expr *containerExpr, + bool useIfPresentVariant) { + auto methodName = useIfPresentVariant ? C.Id_decodeIfPresent : C.Id_decode; + + // Type.self + auto *metaTyRef = TypeExpr::createImplicit(resultType, C); + auto *targetExpr = + new (C) DotSelfExpr(metaTyRef, SourceLoc(), SourceLoc(), resultType); + + // CodingKeys.x + metaTyRef = TypeExpr::createImplicit(codingKeysType, C); + auto *keyExpr = + new (C) MemberRefExpr(metaTyRef, SourceLoc(), codingKey, DeclNameLoc(), + /*Implicit=*/true); + + // decode(_:forKey:)/decodeIfPresent(_:forKey:) + SmallVector argNames{Identifier(), C.Id_forKey}; + auto *decodeCall = + UnresolvedDotExpr::createImplicit(C, containerExpr, methodName, argNames); + + // container.decode(Type.self, forKey: CodingKeys.x) + Expr *args[2] = {targetExpr, keyExpr}; + auto *callExpr = CallExpr::createImplicit(C, decodeCall, C.AllocateCopy(args), + C.AllocateCopy(argNames)); + + // try container.decode(Type.self, forKey: CodingKeys.x) + auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); + return tryExpr; +} + /// Synthesizes the body for `init(from decoder: Decoder) throws`. /// /// \param initDecl The function decl whose body to synthesize. @@ -1386,35 +1446,8 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { continue; } - auto methodName = - useIfPresentVariant ? C.Id_decodeIfPresent : C.Id_decode; - - // Type.self (where Type === type(of: x)) - // Calculating the metatype needs to happen after potential Optional - // unwrapping in lookupVarDeclForCodingKeysCase(). - auto *metaTyRef = TypeExpr::createImplicit(varType, C); - auto *targetExpr = new (C) DotSelfExpr(metaTyRef, SourceLoc(), - SourceLoc(), varType); - - // CodingKeys.x - metaTyRef = TypeExpr::createImplicit(codingKeysType, C); - auto *keyExpr = new (C) MemberRefExpr(metaTyRef, SourceLoc(), - elt, DeclNameLoc(), /*Implicit=*/true); - - // decode(_:forKey:)/decodeIfPresent(_:forKey:) - SmallVector argNames{Identifier(), C.Id_forKey}; - auto *decodeCall = UnresolvedDotExpr::createImplicit( - C, containerExpr, methodName, argNames); - - // container.decode(Type.self, forKey: CodingKeys.x) - Expr *args[2] = {targetExpr, keyExpr}; - auto *callExpr = CallExpr::createImplicit(C, decodeCall, - C.AllocateCopy(args), - C.AllocateCopy(argNames)); - - // try container.decode(Type.self, forKey: CodingKeys.x) - auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*Implicit=*/true); + auto *tryExpr = createDecodeCall(C, varType, codingKeysType, elt, + containerExpr, useIfPresentVariant); auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); auto *varExpr = UnresolvedDotExpr::createImplicit(C, selfRef, @@ -1439,8 +1472,7 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { // container.superDecoder() auto *superDecoderCall = - CallExpr::createImplicit(C, superDecoderRef, ArrayRef(), - ArrayRef()); + CallExpr::createImplicitEmpty(C, superDecoderRef); // super auto *superRef = new (C) SuperRefExpr(initDecl->getImplicitSelfDecl(), @@ -1485,9 +1517,7 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { auto *superInitRef = UnresolvedDotExpr::createImplicit(C, superRef, initName); // super.init() call - Expr *callExpr = CallExpr::createImplicit(C, superInitRef, - ArrayRef(), - ArrayRef()); + Expr *callExpr = CallExpr::createImplicitEmpty(C, superInitRef); // If super.init throws, try super.init() if (superInitDecl->hasThrows()) @@ -1537,13 +1567,15 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) { // } // switch container.allKeys.first { // case .bar: - // let nestedContainer = try container.nestedContainer(keyedBy: - // BarCodingKeys.self, forKey: .bar) let x = try - // nestedContainer.decode(Int.self, forKey: .x) self = .bar(x: x) + // let nestedContainer = try container.nestedContainer( + // keyedBy: BarCodingKeys.self, forKey: .bar) + // let x = try nestedContainer.decode(Int.self, forKey: .x) + // self = .bar(x: x) // case .baz: - // let nestedContainer = try container.nestedContainer(keyedBy: - // BarCodingKeys.self, forKey: .baz) let y = try - // nestedContainer.decode(String.self, forKey: .y) self = .baz(y: y) + // let nestedContainer = try container.nestedContainer( + // keyedBy: BarCodingKeys.self, forKey: .baz) + // let y = try nestedContainer.decode(String.self, forKey: .y) + // self = .baz(y: y) // } // } @@ -1562,192 +1594,15 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) { // b) The type is not an enum assert(codingKeysEnum && "Missing CodingKeys decl."); - // Generate a reference to containerExpr ahead of time in case there are no - // properties to encode or decode, but the type is a class which inherits from - // something Codable and needs to decode super. - - // let container : KeyedDecodingContainer - auto codingKeysType = codingKeysEnum->getDeclaredInterfaceType(); - auto *containerDecl = - createKeyedContainer(C, funcDC, C.getKeyedDecodingContainerDecl(), - codingKeysEnum->getDeclaredInterfaceType(), - VarDecl::Introducer::Let); - - auto *containerExpr = - new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), DeclNameLoc(), - /*Implicit=*/true, AccessSemantics::DirectToStorage); - SmallVector statements; if (codingKeysEnum->hasCases()) { // Need to generate // `let container = try decoder.container(keyedBy: CodingKeys.self)` - // `let container` (containerExpr) is generated above. - - // decoder - auto decoderParam = initDecl->getParameters()->get(0); - auto *decoderExpr = new (C) DeclRefExpr(ConcreteDeclRef(decoderParam), - DeclNameLoc(), /*Implicit=*/true); - - // Bound decoder.container(keyedBy: CodingKeys.self) call - auto containerType = containerDecl->getInterfaceType(); - auto *callExpr = createContainerKeyedByCall(C, funcDC, decoderExpr, - containerType, codingKeysEnum); - - // try decoder.container(keyedBy: CodingKeys.self) - auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*implicit=*/true); - - // Full `let container = decoder.container(keyedBy: CodingKeys.self)` - // binding. - auto *containerPattern = NamedPattern::createImplicit(C, containerDecl); - auto *bindingDecl = PatternBindingDecl::createImplicit( - C, StaticSpellingKind::None, containerPattern, tryExpr, funcDC); - statements.push_back(bindingDecl); - statements.push_back(containerDecl); - - SmallVector cases; - - for (auto *elt : targetEnum->getAllElements()) { - auto *codingKeyCase = - lookupEnumCase(C, codingKeysEnum, elt->getName().getBaseIdentifier()); - - // Skip this case if it's not defined in the CodingKeys - if (!codingKeyCase) - continue; - // generate: case .: - auto pat = new (C) EnumElementPattern( - TypeExpr::createImplicit(funcDC->mapTypeIntoContext(codingKeysType), - C), - SourceLoc(), DeclNameLoc(), DeclNameRef(), codingKeyCase, nullptr); - pat->setImplicit(); - pat->setType(codingKeysType); - - auto labelItem = - CaseLabelItem(new (C) OptionalSomePattern(pat, SourceLoc())); - - llvm::SmallVector caseStatements; - - auto caseIdentifier = caseCodingKeysIdentifier(C, elt); - auto *caseCodingKeys = - lookupEvaluatedCodingKeysEnum(C, targetEnum, caseIdentifier); - - auto *nestedContainerDecl = createKeyedContainer( - C, funcDC, C.getKeyedDecodingContainerDecl(), - caseCodingKeys->getDeclaredInterfaceType(), VarDecl::Introducer::Var, - C.Id_nestedContainer); - - auto *nestedContainerCall = createNestedContainerKeyedByForKeyCall( - C, funcDC, containerExpr, caseCodingKeys, codingKeyCase); - - auto *tryNestedContainerCall = new (C) TryExpr( - SourceLoc(), nestedContainerCall, Type(), /* Implicit */ true); - - auto *containerPattern = - NamedPattern::createImplicit(C, nestedContainerDecl); - auto *bindingDecl = PatternBindingDecl::createImplicit( - C, StaticSpellingKind::None, containerPattern, tryNestedContainerCall, - funcDC); - caseStatements.push_back(bindingDecl); - caseStatements.push_back(nestedContainerDecl); - - llvm::SmallVector decodeCalls; - llvm::SmallVector params; - if (elt->hasAssociatedValues()) { - for (auto entry : llvm::enumerate(*elt->getParameterList())) { - auto *paramDecl = entry.value(); - Identifier identifier = getVarNameForCoding(paramDecl); - if (identifier.empty()) { - identifier = C.getIdentifier("_" + std::to_string(entry.index())); - } - auto *caseCodingKey = lookupEnumCase(C, caseCodingKeys, identifier); - - params.push_back(getVarNameForCoding(paramDecl)); - - // If no key is defined for this parameter, use the default value - if (!caseCodingKey) { - // This should have been verified to have a default expr in the - // CodingKey synthesis - assert(paramDecl->hasDefaultExpr()); - decodeCalls.push_back(paramDecl->getTypeCheckedDefaultExpr()); - continue; - } - - // Type.self - auto *parameterTypeExpr = TypeExpr::createImplicit( - funcDC->mapTypeIntoContext(paramDecl->getInterfaceType()), C); - auto *parameterMetaTypeExpr = - new (C) DotSelfExpr(parameterTypeExpr, SourceLoc(), SourceLoc()); - // BarCodingKeys.x - auto *metaTyRef = - TypeExpr::createImplicit(caseCodingKeys->getDeclaredType(), C); - auto *keyExpr = - new (C) MemberRefExpr(metaTyRef, SourceLoc(), caseCodingKey, - DeclNameLoc(), /*Implicit=*/true); - - auto *nestedContainerExpr = new (C) - DeclRefExpr(ConcreteDeclRef(nestedContainerDecl), DeclNameLoc(), - /*Implicit=*/true, AccessSemantics::DirectToStorage); - // decode(_:, forKey:) - auto *decodeCall = UnresolvedDotExpr::createImplicit( - C, nestedContainerExpr, C.Id_decode, {Identifier(), C.Id_forKey}); - - // nestedContainer.decode(Type.self, forKey: BarCodingKeys.x) - auto *callExpr = CallExpr::createImplicit( - C, decodeCall, {parameterMetaTypeExpr, keyExpr}, - {Identifier(), C.Id_forKey}); - - // try nestedContainer.decode(Type.self, forKey: BarCodingKeys.x) - auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*Implicit=*/true); - - decodeCalls.push_back(tryExpr); - } - } - - auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); - - // Foo.bar - auto *selfTypeExpr = - TypeExpr::createImplicit(targetEnum->getDeclaredType(), C); - - if (params.empty()) { - auto *selfCaseExpr = new (C) MemberRefExpr( - selfTypeExpr, SourceLoc(), elt, DeclNameLoc(), /*Implicit=*/true); - - auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); - - auto *assignExpr = - new (C) AssignExpr(selfRef, SourceLoc(), selfCaseExpr, - /*Implicit=*/true); - - caseStatements.push_back(assignExpr); - } else { - // Foo.bar(x:) - auto *selfCaseExpr = UnresolvedDotExpr::createImplicit( - C, selfTypeExpr, elt->getBaseIdentifier(), C.AllocateCopy(params)); - - // Foo.bar(x: try nestedContainer.decode(Int.self, forKey: .x)) - auto *caseCallExpr = CallExpr::createImplicit( - C, selfCaseExpr, C.AllocateCopy(decodeCalls), - C.AllocateCopy(params)); - - // self = Foo.bar(x: try nestedContainer.decode(Int.self)) - auto *assignExpr = - new (C) AssignExpr(selfRef, SourceLoc(), caseCallExpr, - /*Implicit=*/true); - - caseStatements.push_back(assignExpr); - } - - auto body = - BraceStmt::create(C, SourceLoc(), caseStatements, SourceLoc()); - - cases.push_back(CaseStmt::create(C, CaseParentKind::Switch, SourceLoc(), - labelItem, SourceLoc(), SourceLoc(), - body, - /*case body vardecls*/ None)); - } + auto *containerExpr = createContainer( + C, funcDC, VarDecl::Introducer::Let, C.getKeyedDecodingContainerDecl(), + initDecl->getParameters()->get(0), codingKeysEnum, statements, + /*throws*/ true); // generate: // @@ -1758,11 +1613,14 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) { // one.") // throw DecodingError.typeMismatch(Foo.self, context) // } - auto *debugMessage = new (C) StringLiteralExpr( - StringRef("Invalid number of keys found, expected one."), SourceRange(), - /* Implicit */ true); - auto *throwStmt = createThrowDecodingErrorTypeMismatchStmt( - C, funcDC, targetEnum, containerExpr, debugMessage); + auto *targetType = TypeExpr::createImplicit( + funcDC->mapTypeIntoContext(targetEnum->getDeclaredInterfaceType()), C); + auto *targetTypeExpr = + new (C) DotSelfExpr(targetType, SourceLoc(), SourceLoc()); + + auto *throwStmt = createThrowCodingErrorStmt( + C, containerExpr, C.getDecodingErrorDecl(), C.Id_typeMismatch, + targetTypeExpr, "Invalid number of keys found, expected one."); // container.allKeys auto *allKeysExpr = @@ -1780,11 +1638,8 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) { /*implicit*/ true, AccessSemantics::Ordinary, fnType); auto *oneExpr = IntegerLiteralExpr::createFromUnsigned(C, 1); - auto *tupleExpr = TupleExpr::createImplicit(C, {keysCountExpr, oneExpr}, - {Identifier(), Identifier()}); - - auto *cmpExpr = - new (C) BinaryExpr(cmpFuncExpr, tupleExpr, /*implicit*/ true); + auto *cmpExpr = BinaryExpr::create(C, keysCountExpr, cmpFuncExpr, oneExpr, + /*implicit*/ true); cmpExpr->setThrows(false); auto *guardBody = BraceStmt::create(C, SourceLoc(), {throwStmt}, @@ -1799,8 +1654,131 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) { auto *firstExpr = UnresolvedDotExpr::createImplicit(C, allKeysExpr, C.Id_first); - auto switchStmt = - SwitchStmt::createImplicit(LabeledStmtInfo(), firstExpr, cases, C); + // generate: switch container.allKeys.first.unsafelyUnwrapped { } + auto *unwrapped = + UnresolvedDotExpr::createImplicit(C, firstExpr, C.Id_unsafelyUnwrapped); + + auto switchStmt = createEnumSwitch( + C, funcDC, unwrapped, targetEnum, codingKeysEnum, + /*createSubpattern*/ false, + [&](auto *elt, auto *codingKeyCase, + auto payloadVars) -> std::tuple { + // Skip this case if it's not defined in the CodingKeys + if (!codingKeyCase) + return std::make_tuple(nullptr, nullptr); + + llvm::SmallVector caseStatements; + + auto caseIdentifier = caseCodingKeysIdentifier(C, elt); + auto *caseCodingKeys = + lookupEvaluatedCodingKeysEnum(C, targetEnum, caseIdentifier); + + auto *nestedContainerDecl = createKeyedContainer( + C, funcDC, C.getKeyedDecodingContainerDecl(), + caseCodingKeys->getDeclaredInterfaceType(), + VarDecl::Introducer::Var, C.Id_nestedContainer); + + auto *nestedContainerCall = createNestedContainerKeyedByForKeyCall( + C, funcDC, containerExpr, caseCodingKeys, codingKeyCase); + + auto *tryNestedContainerCall = new (C) TryExpr( + SourceLoc(), nestedContainerCall, Type(), /* Implicit */ true); + + auto *containerPattern = + NamedPattern::createImplicit(C, nestedContainerDecl); + auto *bindingDecl = PatternBindingDecl::createImplicit( + C, StaticSpellingKind::None, containerPattern, + tryNestedContainerCall, funcDC); + caseStatements.push_back(bindingDecl); + caseStatements.push_back(nestedContainerDecl); + + llvm::SmallVector decodeCalls; + llvm::SmallVector params; + if (elt->hasAssociatedValues()) { + for (auto entry : llvm::enumerate(*elt->getParameterList())) { + auto *paramDecl = entry.value(); + Identifier identifier = getVarNameForCoding(paramDecl); + if (identifier.empty()) { + identifier = + C.getIdentifier("_" + std::to_string(entry.index())); + } + auto *caseCodingKey = + lookupEnumCase(C, caseCodingKeys, identifier); + + params.push_back(getVarNameForCoding(paramDecl)); + + // If no key is defined for this parameter, use the default value + if (!caseCodingKey) { + // This should have been verified to have a default expr in the + // CodingKey synthesis + assert(paramDecl->hasDefaultExpr()); + decodeCalls.push_back(paramDecl->getTypeCheckedDefaultExpr()); + continue; + } + + auto varType = conformanceDC->mapTypeIntoContext( + paramDecl->getValueInterfaceType()); + + bool useIfPresentVariant = false; + if (auto objType = varType->getOptionalObjectType()) { + varType = objType; + useIfPresentVariant = true; + } + + auto *nestedContainerExpr = new (C) DeclRefExpr( + ConcreteDeclRef(nestedContainerDecl), DeclNameLoc(), + /*Implicit=*/true, AccessSemantics::DirectToStorage); + + auto *tryExpr = createDecodeCall( + C, varType, caseCodingKeys->getDeclaredType(), caseCodingKey, + nestedContainerExpr, useIfPresentVariant); + + decodeCalls.push_back(tryExpr); + } + } + + auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); + + // Foo.bar + auto *selfTypeExpr = + TypeExpr::createImplicit(targetEnum->getDeclaredType(), C); + + if (params.empty()) { + auto *selfCaseExpr = + new (C) MemberRefExpr(selfTypeExpr, SourceLoc(), elt, + DeclNameLoc(), /*Implicit=*/true); + + auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); + + auto *assignExpr = + new (C) AssignExpr(selfRef, SourceLoc(), selfCaseExpr, + /*Implicit=*/true); + + caseStatements.push_back(assignExpr); + } else { + // Foo.bar(x:) + auto *selfCaseExpr = UnresolvedDotExpr::createImplicit( + C, selfTypeExpr, elt->getBaseIdentifier(), + C.AllocateCopy(params)); + + // Foo.bar(x: try nestedContainer.decode(Int.self, forKey: .x)) + auto *caseCallExpr = CallExpr::createImplicit( + C, selfCaseExpr, C.AllocateCopy(decodeCalls), + C.AllocateCopy(params)); + + // self = Foo.bar(x: try nestedContainer.decode(Int.self)) + auto *assignExpr = + new (C) AssignExpr(selfRef, SourceLoc(), caseCallExpr, + /*Implicit=*/true); + + caseStatements.push_back(assignExpr); + } + + auto body = + BraceStmt::create(C, SourceLoc(), caseStatements, SourceLoc()); + + return std::make_tuple(codingKeyCase, body); + }); statements.push_back(switchStmt); } @@ -1859,7 +1837,7 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { } // This constructor should be marked as `required` for non-final classes. - if (classDecl && !classDecl->isFinal()) { + if (classDecl && !classDecl->isSemanticallyFinal()) { auto *reqAttr = new (C) RequiredAttr(/*IsImplicit=*/true); initDecl->getAttrs().add(reqAttr); } @@ -1878,7 +1856,8 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { /// not, attempts to synthesize one for it. /// /// \param requirement The requirement we want to synthesize. -static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { +static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement, + DelayedNotes &delayedNotes) { // Before we attempt to look up (or more importantly, synthesize) a CodingKeys // entity on target, we need to make sure the type is otherwise valid. // @@ -1895,7 +1874,8 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { if (auto *superclassDecl = classDecl->getSuperclassDecl()) { DeclName memberName; auto superType = superclassDecl->getDeclaredInterfaceType(); - if (TypeChecker::conformsToProtocol(superType, proto, superclassDecl)) { + if (TypeChecker::conformsToProtocol(superType, proto, + derived.getParentModule())) { // super.init(from:) must be accessible. memberName = cast(requirement)->getName(); } else { @@ -1913,8 +1893,11 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { if (result.empty()) { // No super initializer for us to call. - superclassDecl->diagnose(diag::decodable_no_super_init_here, - requirement->getName(), memberName); + delayedNotes.push_back([=] { + superclassDecl->diagnose(diag::decodable_no_super_init_here, + requirement->getName(), memberName); + }); + return false; } else if (result.size() > 1) { // There are multiple results for this lookup. We'll end up producing a @@ -1927,28 +1910,35 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { auto conformanceDC = derived.getConformanceContext(); if (!initializer->isDesignatedInit()) { // We must call a superclass's designated initializer. - initializer->diagnose(diag::decodable_super_init_not_designated_here, - requirement->getName(), memberName); + delayedNotes.push_back([=] { + initializer->diagnose( + diag::decodable_super_init_not_designated_here, + requirement->getName(), memberName); + }); return false; } else if (!initializer->isAccessibleFrom(conformanceDC)) { // Cannot call an inaccessible method. - auto accessScope = initializer->getFormalAccessScope(conformanceDC); - initializer->diagnose(diag::decodable_inaccessible_super_init_here, - requirement->getName(), memberName, - accessScope.accessLevelForDiagnostics()); + delayedNotes.push_back([=] { + auto accessScope = initializer->getFormalAccessScope(conformanceDC); + initializer->diagnose(diag::decodable_inaccessible_super_init_here, + requirement->getName(), memberName, + accessScope.accessLevelForDiagnostics()); + }); return false; } else if (initializer->isFailable()) { // We can't call super.init() if it's failable, since init(from:) // isn't failable. - initializer->diagnose(diag::decodable_super_init_is_failable_here, - requirement->getName(), memberName); + delayedNotes.push_back([=] { + initializer->diagnose(diag::decodable_super_init_is_failable_here, + requirement->getName(), memberName); + }); return false; } } } } - if (!validateCodingKeysEnum(derived)) { + if (!validateCodingKeysEnum(derived, delayedNotes)) { return false; } @@ -1958,10 +1948,12 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { for (auto *elementDecl : enumDecl->getAllElements()) { bool duplicate = false; if (!caseNames.insert(elementDecl->getBaseIdentifier())) { - elementDecl->diagnose(diag::codable_enum_duplicate_case_name_here, - derived.getProtocolType(), - derived.Nominal->getDeclaredType(), - elementDecl->getBaseIdentifier()); + delayedNotes.push_back([=] { + elementDecl->diagnose(diag::codable_enum_duplicate_case_name_here, + derived.getProtocolType(), + derived.Nominal->getDeclaredType(), + elementDecl->getBaseIdentifier()); + }); allValid = false; duplicate = true; } @@ -1987,17 +1979,20 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { userDefinedParam = inserted.first->second; } - userDefinedParam->diagnose(diag::codable_enum_duplicate_parameter_name_here, - derived.getProtocolType(), - derived.Nominal->getDeclaredType(), - paramIdentifier, - elementDecl->getBaseIdentifier()); + delayedNotes.push_back([=] { + userDefinedParam->diagnose(diag::codable_enum_duplicate_parameter_name_here, + derived.getProtocolType(), + derived.Nominal->getDeclaredType(), + paramIdentifier, + elementDecl->getBaseIdentifier()); + }); allValid = false; } } } - if (!duplicate && !validateCaseCodingKeysEnum(derived, elementDecl)) { + if (!duplicate && + !validateCaseCodingKeysEnum(derived, elementDecl, delayedNotes)) { allValid = false; } } @@ -2051,7 +2046,8 @@ ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) { // Check other preconditions for synthesized conformance. // This synthesizes a CodingKeys enum if possible. - if (!canSynthesize(*this, requirement)) { + DelayedNotes delayedNotes; + if (!canSynthesize(*this, requirement, delayedNotes)) { ConformanceDecl->diagnose(diag::type_does_not_conform, Nominal->getDeclaredType(), getProtocolType()); requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Func, @@ -2060,6 +2056,7 @@ ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) { return nullptr; } + assert(delayedNotes.empty()); return deriveEncodable_encode(*this); } @@ -2081,7 +2078,8 @@ ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) { // Check other preconditions for synthesized conformance. // This synthesizes a CodingKeys enum if possible. - if (!canSynthesize(*this, requirement)) { + DelayedNotes delayedNotes; + if (!canSynthesize(*this, requirement, delayedNotes)) { ConformanceDecl->diagnose(diag::type_does_not_conform, Nominal->getDeclaredType(), getProtocolType()); requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Constructor, @@ -2090,6 +2088,7 @@ ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) { return nullptr; } + assert(delayedNotes.empty()); return deriveDecodable_init(*this); } diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index 2f39a75b81db6..4cf9827722767 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -78,13 +78,8 @@ deriveBodyComparable_enum_noAssociatedValues_lt(AbstractFunctionDecl *ltDecl, /*implicit*/ true, AccessSemantics::Ordinary); - TupleExpr *abTuple = TupleExpr::create(C, SourceLoc(), { aIndex, bIndex }, - { }, { }, SourceLoc(), - /*HasTrailingClosure*/ false, - /*Implicit*/ true); - - auto *cmpExpr = new (C) BinaryExpr( - cmpFuncExpr, abTuple, /*implicit*/ true); + auto *cmpExpr = + BinaryExpr::create(C, aIndex, cmpFuncExpr, bIndex, /*implicit*/ true); statements.push_back(new (C) ReturnStmt(SourceLoc(), cmpExpr)); BraceStmt *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); diff --git a/lib/Sema/DerivedConformanceDifferentiable.cpp b/lib/Sema/DerivedConformanceDifferentiable.cpp index e9e1aa8dd042b..63abf882100d2 100644 --- a/lib/Sema/DerivedConformanceDifferentiable.cpp +++ b/lib/Sema/DerivedConformanceDifferentiable.cpp @@ -84,7 +84,7 @@ getStoredPropertiesForDifferentiation( continue; auto varType = DC->mapTypeIntoContext(vd->getValueInterfaceType()); auto conformance = TypeChecker::conformsToProtocol( - varType, diffableProto, nominal); + varType, diffableProto, DC->getParentModule()); if (!conformance) continue; // Skip `let` stored properties with a mutating `move(by:)` if requested. @@ -113,11 +113,12 @@ static StructDecl *convertToStructDecl(ValueDecl *v) { /// for the given interface type and declaration context. static Type getTangentVectorInterfaceType(Type contextualType, DeclContext *DC) { - auto &C = contextualType->getASTContext(); + auto &C = DC->getASTContext(); auto *diffableProto = C.getProtocol(KnownProtocolKind::Differentiable); assert(diffableProto && "`Differentiable` protocol not found"); auto conf = - TypeChecker::conformsToProtocol(contextualType, diffableProto, DC); + TypeChecker::conformsToProtocol(contextualType, diffableProto, + DC->getParentModule()); assert(conf && "Contextual type must conform to `Differentiable`"); if (!conf) return nullptr; @@ -139,7 +140,8 @@ static bool canDeriveTangentVectorAsSelf(NominalTypeDecl *nominal, auto *diffableProto = C.getProtocol(KnownProtocolKind::Differentiable); auto *addArithProto = C.getProtocol(KnownProtocolKind::AdditiveArithmetic); // `Self` must conform to `AdditiveArithmetic`. - if (!TypeChecker::conformsToProtocol(nominalTypeInContext, addArithProto, DC)) + if (!TypeChecker::conformsToProtocol(nominalTypeInContext, addArithProto, + DC->getParentModule())) return false; for (auto *field : nominal->getStoredProperties()) { // `Self` must not have any `@noDerivative` stored properties. @@ -147,7 +149,8 @@ static bool canDeriveTangentVectorAsSelf(NominalTypeDecl *nominal, return false; // `Self` must have all stored properties satisfy `Self == TangentVector`. auto fieldType = DC->mapTypeIntoContext(field->getValueInterfaceType()); - auto conf = TypeChecker::conformsToProtocol(fieldType, diffableProto, DC); + auto conf = TypeChecker::conformsToProtocol(fieldType, diffableProto, + DC->getParentModule()); if (!conf) return false; auto tangentType = conf.getTypeWitnessByName(fieldType, C.Id_TangentVector); @@ -210,7 +213,8 @@ bool DerivedConformance::canDeriveDifferentiable(NominalTypeDecl *nominal, if (v->getInterfaceType()->hasError()) return false; auto varType = DC->mapTypeIntoContext(v->getValueInterfaceType()); - return (bool)TypeChecker::conformsToProtocol(varType, diffableProto, DC); + return (bool)TypeChecker::conformsToProtocol(varType, diffableProto, + DC->getParentModule()); }); } @@ -381,10 +385,10 @@ getOrSynthesizeTangentVectorStruct(DerivedConformance &derived, Identifier id) { tvDesiredProtos.insert(req.getProtocolDecl()); } } - SmallVector tvDesiredProtoTypeLocs; + SmallVector tvDesiredProtoInherited; for (auto *p : tvDesiredProtos) - tvDesiredProtoTypeLocs.push_back( - TypeLoc::withoutLoc(p->getDeclaredInterfaceType())); + tvDesiredProtoInherited.push_back( + InheritedEntry(TypeLoc::withoutLoc(p->getDeclaredInterfaceType()))); // Cache original members and their associated types for later use. SmallVector diffProperties; @@ -393,7 +397,7 @@ getOrSynthesizeTangentVectorStruct(DerivedConformance &derived, Identifier id) { auto synthesizedLoc = derived.ConformanceDecl->getEndLoc(); auto *structDecl = new (C) StructDecl(synthesizedLoc, C.Id_TangentVector, synthesizedLoc, - /*Inherited*/ C.AllocateCopy(tvDesiredProtoTypeLocs), + /*Inherited*/ C.AllocateCopy(tvDesiredProtoInherited), /*GenericParams*/ {}, parentDC); structDecl->setBraces({synthesizedLoc, synthesizedLoc}); structDecl->setImplicit(); @@ -551,7 +555,8 @@ static void checkAndDiagnoseImplicitNoDerivative(ASTContext &Context, // Check whether to diagnose stored property. auto varType = DC->mapTypeIntoContext(vd->getValueInterfaceType()); auto diffableConformance = - TypeChecker::conformsToProtocol(varType, diffableProto, nominal); + TypeChecker::conformsToProtocol(varType, diffableProto, + DC->getParentModule()); // If stored property should not be diagnosed, continue. if (diffableConformance && canInvokeMoveByOnProperty(vd, diffableConformance)) diff --git a/lib/Sema/DerivedConformanceDistributedActor.cpp b/lib/Sema/DerivedConformanceDistributedActor.cpp new file mode 100644 index 0000000000000..3ebfb1dcc071b --- /dev/null +++ b/lib/Sema/DerivedConformanceDistributedActor.cpp @@ -0,0 +1,178 @@ +//===--- DerivedConformanceActor.cpp - Derived Actor Conformance ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 implements implicit derivation of the Actor protocol. +// +//===----------------------------------------------------------------------===// + +#include "CodeSynthesis.h" +#include "DerivedConformances.h" +#include "TypeChecker.h" +#include "TypeCheckConcurrency.h" +#include "swift/AST/NameLookupRequests.h" +#include "swift/AST/ParameterList.h" + +using namespace swift; + +bool DerivedConformance::canDeriveDistributedActor( + NominalTypeDecl *nominal, DeclContext *dc) { + auto classDecl = dyn_cast(nominal); + return classDecl && classDecl->isDistributedActor() && dc == nominal; +} + +// ==== ------------------------------------------------------------------------ + +/******************************************************************************/ +/******************************* RESOLVE FUNCTION *****************************/ +/******************************************************************************/ + +/// Synthesizes the +/// +/// \verbatim +/// static resolve(_ address: ActorAddress, +/// using transport: ActorTransport) throws -> Self { +/// +/// } +/// \endverbatim +/// +/// factory function in the AST, with an empty body. Its body is +/// expected to be filled-in during SILGen. +// TODO(distributed): move this synthesis to DerivedConformance style +static FuncDecl *deriveDistributedActor_resolve(DerivedConformance &derived) { + auto decl = dyn_cast(derived.Nominal); + assert(decl->isDistributedActor()); + auto &C = decl->getASTContext(); + + auto mkParam = [&](Identifier argName, Identifier paramName, Type ty) -> ParamDecl* { + auto *param = new (C) ParamDecl(SourceLoc(), + SourceLoc(), argName, + SourceLoc(), paramName, decl); + param->setImplicit(); + param->setSpecifier(ParamSpecifier::Default); + param->setInterfaceType(ty); + return param; + }; + + auto addressType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType(); + auto transportType = C.getActorTransportDecl()->getDeclaredInterfaceType(); + + // (_ identity: AnyActorIdentity, using transport: ActorTransport) + auto *params = ParameterList::create( + C, + /*LParenLoc=*/SourceLoc(), + /*params=*/{ mkParam(Identifier(), C.Id_identity, addressType), + mkParam(C.Id_using, C.Id_transport, transportType) + }, + /*RParenLoc=*/SourceLoc() + ); + + // Func name: resolve(_:using:) + DeclName name(C, C.Id_resolve, params); + + // Expected type: (Self) -> (AnyActorIdentity, ActorTransport) throws -> (Self) + auto *factoryDecl = + FuncDecl::createImplicit(C, StaticSpellingKind::KeywordStatic, + name, SourceLoc(), + /*async=*/false, + /*throws=*/true, + /*genericParams=*/nullptr, + params, + /*returnType*/decl->getDeclaredInterfaceType(), + decl); + + factoryDecl->setDistributedActorFactory(); // TODO(distributed): should we mark this specifically as the resolve factory? + factoryDecl->copyFormalAccessFrom(decl, /*sourceIsParentContext=*/true); + + derived.addMembersToConformanceContext({factoryDecl}); + return factoryDecl; +} + +/******************************************************************************/ +/******************************* PROPERTIES ***********************************/ +/******************************************************************************/ + +static ValueDecl *deriveDistributedActor_id(DerivedConformance &derived) { + assert(derived.Nominal->isDistributedActor()); + auto &C = derived.Context; + + // ``` + // @_distributedActorIndependent + // let id: AnyActorIdentity + // ``` + auto propertyType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType(); + + VarDecl *propDecl; + PatternBindingDecl *pbDecl; + std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( + C.Id_id, + propertyType, propertyType, + /*isStatic=*/false, /*isFinal=*/true); + + propDecl->setIntroducer(VarDecl::Introducer::Let); + + // mark as @_distributedActorIndependent, allowing access to it from everywhere + propDecl->getAttrs().add( + new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true)); + + derived.addMembersToConformanceContext({ propDecl, pbDecl }); + return propDecl; +} + +static ValueDecl *deriveDistributedActor_actorTransport( + DerivedConformance &derived) { + assert(derived.Nominal->isDistributedActor()); + auto &C = derived.Context; + + // ``` + // @_distributedActorIndependent + // let actorTransport: ActorTransport + // ``` + // (no need for @actorIndependent because it is an immutable let) + auto propertyType = C.getActorTransportDecl()->getDeclaredInterfaceType(); + + VarDecl *propDecl; + PatternBindingDecl *pbDecl; + std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( + C.Id_actorTransport, + propertyType, propertyType, + /*isStatic=*/false, /*isFinal=*/true); + + propDecl->setIntroducer(VarDecl::Introducer::Let); + + // mark as @_distributedActorIndependent, allowing access to it from everywhere + propDecl->getAttrs().add( + new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true)); + + derived.addMembersToConformanceContext({ propDecl, pbDecl }); + return propDecl; +} + +// ==== ------------------------------------------------------------------------ + +ValueDecl *DerivedConformance::deriveDistributedActor(ValueDecl *requirement) { + if (auto var = dyn_cast(requirement)) { + if (var->getName() == Context.Id_id) + return deriveDistributedActor_id(*this); + + if (var->getName() == Context.Id_actorTransport) + return deriveDistributedActor_actorTransport(*this); + } + + if (auto func = dyn_cast(requirement)) { + // just a simple name check is enough here, + // if we are invoked here we know for sure it is for the "right" function + if (func->getName().getBaseName() == Context.Id_resolve) + return deriveDistributedActor_resolve(*this); + } + + return nullptr; +} diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index ad1929ac3b58d..b9cbb4e1438ea 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -123,7 +123,8 @@ deriveBodyEquatable_enum_noAssociatedValues_eq(AbstractFunctionDecl *eqDecl, AccessSemantics::Ordinary, fnType); fnType = fnType->getResult()->castTo(); - auto *callExpr = new (C) DotSyntaxCallExpr(ref, SourceLoc(), base, fnType); + auto *callExpr = + DotSyntaxCallExpr::create(C, ref, SourceLoc(), base, fnType); callExpr->setImplicit(); callExpr->setThrows(false); cmpFuncExpr = callExpr; @@ -134,16 +135,9 @@ deriveBodyEquatable_enum_noAssociatedValues_eq(AbstractFunctionDecl *eqDecl, fnType); } - TupleTypeElt abTupleElts[2] = { aIndex->getType(), bIndex->getType() }; - TupleExpr *abTuple = TupleExpr::create(C, SourceLoc(), { aIndex, bIndex }, - { }, { }, SourceLoc(), - /*HasTrailingClosure*/ false, - /*Implicit*/ true, - TupleType::get(abTupleElts, C)); - - auto *cmpExpr = new (C) BinaryExpr( - cmpFuncExpr, abTuple, /*implicit*/ true, - fnType->castTo()->getResult()); + auto *cmpExpr = + BinaryExpr::create(C, aIndex, cmpFuncExpr, bIndex, /*implicit*/ true, + fnType->castTo()->getResult()); cmpExpr->setThrows(false); statements.push_back(new (C) ReturnStmt(SourceLoc(), cmpExpr)); @@ -557,6 +551,12 @@ deriveHashable_hashInto( hashDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext=*/true); + // The derived hash(into:) for an actor must be non-isolated. + if (derived.Nominal->isActor() || + getActorIsolation(derived.Nominal) == ActorIsolation::GlobalActor) { + hashDecl->getAttrs().add(new (C) NonisolatedAttr(/*IsImplicit*/true)); + } + derived.addMembersToConformanceContext({hashDecl}); return hashDecl; @@ -864,17 +864,16 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { // We can't form a Hashable conformance if Int isn't Hashable or // ExpressibleByIntegerLiteral. - if (TypeChecker::conformsToProtocol( - intType, C.getProtocol(KnownProtocolKind::Hashable), parentDC) - .isInvalid()) { + if (!TypeChecker::conformsToKnownProtocol( + intType, KnownProtocolKind::Hashable, + derived.getParentModule())) { derived.ConformanceDecl->diagnose(diag::broken_int_hashable_conformance); return nullptr; } - ProtocolDecl *intLiteralProto = - C.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); - if (TypeChecker::conformsToProtocol(intType, intLiteralProto, parentDC) - .isInvalid()) { + if (!TypeChecker::conformsToKnownProtocol( + intType, KnownProtocolKind::ExpressibleByIntegerLiteral, + derived.getParentModule())) { derived.ConformanceDecl->diagnose( diag::broken_int_integer_literal_convertible_conformance); return nullptr; @@ -912,6 +911,12 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { hashValueDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); + // The derived hashValue of an actor must be nonisolated. + if (derived.Nominal->isActor() || + getActorIsolation(derived.Nominal) == ActorIsolation::GlobalActor) { + hashValueDecl->getAttrs().add(new (C) NonisolatedAttr(/*IsImplicit*/true)); + } + Pattern *hashValuePat = NamedPattern::createImplicit(C, hashValueDecl); hashValuePat->setType(intType); hashValuePat = TypedPattern::createImplicit(C, hashValuePat, intType); diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index b9d4282709b4c..98960da7aa9b8 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -207,7 +207,8 @@ struct RuntimeVersionCheck { // availableInfo = "#available(\(platformSpec), \(otherSpec))" auto availableInfo = PoundAvailableInfo::create( - C, SourceLoc(), SourceLoc(), { platformSpec, otherSpec }, SourceLoc()); + C, SourceLoc(), SourceLoc(), { platformSpec, otherSpec }, SourceLoc(), + false); // This won't be filled in by TypeCheckAvailability because we have // invalid SourceLocs in this area of the AST. @@ -403,12 +404,9 @@ deriveRawRepresentable_init(DerivedConformance &derived) { assert([&]() -> bool { - auto equatableProto = TypeChecker::getProtocol(C, enumDecl->getLoc(), - KnownProtocolKind::Equatable); - if (!equatableProto) { - return false; - } - return !TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl).isInvalid(); + return TypeChecker::conformsToKnownProtocol( + rawType, KnownProtocolKind::Equatable, + derived.getParentModule()); }()); auto *rawDecl = new (C) @@ -464,14 +462,8 @@ bool DerivedConformance::canDeriveRawRepresentable(DeclContext *DC, // The raw type must be Equatable, so that we have a suitable ~= for // synthesized switch statements. - auto equatableProto = - TypeChecker::getProtocol(enumDecl->getASTContext(), enumDecl->getLoc(), - KnownProtocolKind::Equatable); - if (!equatableProto) - return false; - - if (TypeChecker::conformsToProtocol(rawType, equatableProto, DC) - .isInvalid()) + if (!TypeChecker::conformsToKnownProtocol(rawType, KnownProtocolKind::Equatable, + DC->getParentModule())) return false; auto &C = type->getASTContext(); diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 371ad8640d2d0..19d80d9b9181b 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -40,6 +40,10 @@ DeclContext *DerivedConformance::getConformanceContext() const { return cast(ConformanceDecl); } +ModuleDecl *DerivedConformance::getParentModule() const { + return cast(ConformanceDecl)->getParentModule(); +} + void DerivedConformance::addMembersToConformanceContext( ArrayRef children) { auto IDC = cast(ConformanceDecl); @@ -73,6 +77,8 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, if (*derivableKind == KnownDerivableProtocolKind::Actor) return canDeriveActor(DC, Nominal); + if (*derivableKind == KnownDerivableProtocolKind::DistributedActor) + return canDeriveDistributedActor(Nominal, DC); if (*derivableKind == KnownDerivableProtocolKind::AdditiveArithmetic) return canDeriveAdditiveArithmetic(Nominal, DC); @@ -162,7 +168,7 @@ DerivedConformance::storedPropertiesNotConformingToProtocol( nonconformingProperties.push_back(propertyDecl); if (!TypeChecker::conformsToProtocol(DC->mapTypeIntoContext(type), protocol, - DC)) { + DC->getParentModule())) { nonconformingProperties.push_back(propertyDecl); } } @@ -306,6 +312,14 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, if (name.isSimpleName(ctx.Id_unownedExecutor)) return getRequirement(KnownProtocolKind::Actor); + // DistributedActor.id + if(name.isSimpleName(ctx.Id_id)) + return getRequirement(KnownProtocolKind::DistributedActor); + + // DistributedActor.actorTransport + if(name.isSimpleName(ctx.Id_actorTransport)) + return getRequirement(KnownProtocolKind::DistributedActor); + return nullptr; } @@ -345,6 +359,17 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, return getRequirement(KnownProtocolKind::Hashable); } + // static DistributedActor.resolve(_:using:) + if (name.isCompoundName() && name.getBaseName() == ctx.Id_resolve && + func->isStatic()) { + auto argumentNames = name.getArgumentNames(); + if (argumentNames.size() == 2 && + argumentNames[0] == Identifier() && + argumentNames[1] == ctx.Id_using) { + return getRequirement(KnownProtocolKind::DistributedActor); + } + } + return nullptr; } @@ -539,7 +564,7 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext( // A non-final class can't have an protocol-witnesss initializer in an // extension. if (auto CD = dyn_cast(Nominal)) { - if (!CD->isFinal() && isa(synthesizing) && + if (!CD->isSemanticallyFinal() && isa(synthesizing) && isa(ConformanceDecl)) { ConformanceDecl->diagnose( diag::cannot_synthesize_init_in_extension_of_nonfinal, @@ -573,13 +598,8 @@ GuardStmt *DerivedConformance::returnIfNotEqualGuard(ASTContext &C, auto cmpFuncExpr = new (C) UnresolvedDeclRefExpr( DeclNameRef(C.Id_EqualsOperator), DeclRefKind::BinaryOperator, DeclNameLoc()); - auto cmpArgsTuple = TupleExpr::create(C, SourceLoc(), - { lhsExpr, rhsExpr }, - { }, { }, SourceLoc(), - /*HasTrailingClosure*/false, - /*Implicit*/true); - auto cmpExpr = new (C) BinaryExpr(cmpFuncExpr, cmpArgsTuple, - /*Implicit*/true); + auto *cmpExpr = BinaryExpr::create(C, lhsExpr, cmpFuncExpr, rhsExpr, + /*implicit*/ true); conditions.emplace_back(cmpExpr); // Build and return the complete guard statement. @@ -613,12 +633,8 @@ GuardStmt *DerivedConformance::returnComparisonIfNotEqualGuard(ASTContext &C, auto ltFuncExpr = new (C) UnresolvedDeclRefExpr( DeclNameRef(C.Id_LessThanOperator), DeclRefKind::BinaryOperator, DeclNameLoc()); - auto ltArgsTuple = TupleExpr::create(C, SourceLoc(), - { lhsExpr, rhsExpr }, - { }, { }, SourceLoc(), - /*HasTrailingClosure*/false, - /*Implicit*/true); - auto ltExpr = new (C) BinaryExpr(ltFuncExpr, ltArgsTuple, /*Implicit*/true); + auto *ltExpr = BinaryExpr::create(C, lhsExpr, ltFuncExpr, rhsExpr, + /*implicit*/ true); return returnIfNotEqualGuard(C, lhsExpr, rhsExpr, ltExpr); } @@ -715,8 +731,8 @@ DeclRefExpr *DerivedConformance::convertEnumToIndex(SmallVectorImpl &st /// \p protocol The protocol being requested. /// \return The ParamDecl of each associated value whose type does not conform. SmallVector -DerivedConformance::associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, - ProtocolDecl *protocol) { +DerivedConformance::associatedValuesNotConformingToProtocol( + DeclContext *DC, EnumDecl *theEnum, ProtocolDecl *protocol) { SmallVector nonconformingAssociatedValues; for (auto elt : theEnum->getAllElements()) { auto PL = elt->getParameterList(); @@ -726,7 +742,7 @@ DerivedConformance::associatedValuesNotConformingToProtocol(DeclContext *DC, Enu for (auto param : *PL) { auto type = param->getInterfaceType(); if (TypeChecker::conformsToProtocol(DC->mapTypeIntoContext(type), - protocol, DC) + protocol, DC->getParentModule()) .isInvalid()) { nonconformingAssociatedValues.push_back(param); } diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 4c962925ed931..c2789ecd45a68 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -62,6 +62,9 @@ class DerivedConformance { /// nominal type, or an extension of it) as a \c DeclContext. DeclContext *getConformanceContext() const; + /// Retrieve the module in which the conformance is declared. + ModuleDecl *getParentModule() const; + /// Add \c children as members of the context that declares the conformance. void addMembersToConformanceContext(ArrayRef children); @@ -298,7 +301,15 @@ class DerivedConformance { /// Derive a Decodable requirement for a struct type. /// /// \returns the derived member, which will also be added to the type. - ValueDecl *deriveDecodable(ValueDecl *requirement); + ValueDecl *deriveDecodable(ValueDecl *requirement); + + /// Whether we can derive the given DistributedActor requirement in the given context. + static bool canDeriveDistributedActor(NominalTypeDecl *nominal, DeclContext *dc); + + /// Derive a DistributedActor requirement for an distributed actor. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveDistributedActor(ValueDecl *requirement); /// Determine if \c Actor can be derived for the given type. static bool canDeriveActor(DeclContext *DC, NominalTypeDecl *NTD); diff --git a/lib/Sema/IDETypeCheckingRequests.cpp b/lib/Sema/IDETypeCheckingRequests.cpp index e83d35ba85fe9..f02d0bb87182d 100644 --- a/lib/Sema/IDETypeCheckingRequests.cpp +++ b/lib/Sema/IDETypeCheckingRequests.cpp @@ -131,10 +131,12 @@ static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy, return true; GenericSignature genericSig = ED->getGenericSignature(); + auto *module = DC->getParentModule(); SubstitutionMap substMap = BaseTy->getContextSubstitutionMap( - DC->getParentModule(), ED->getExtendedNominal()); - return areGenericRequirementsSatisfied(DC, genericSig, substMap, - /*isExtension=*/true); + module, ED->getExtendedNominal()); + return TypeChecker::checkGenericArguments( + module, genericSig.getRequirements(), + QuerySubstitutionMap{substMap}) == RequirementCheckResult::Success; } static bool isMemberDeclAppliedInternal(const DeclContext *DC, Type BaseTy, @@ -148,6 +150,13 @@ static bool isMemberDeclAppliedInternal(const DeclContext *DC, Type BaseTy, BaseTy->hasUnresolvedType() || BaseTy->hasError()) return true; + if (isa(VD) && BaseTy->is()) { + // The protocol doesn't satisfy its own generic signature (static members + // of the protocol are not visible on the protocol itself) but we can still + // access typealias declarations on it. + return true; + } + const GenericContext *genericDecl = VD->getAsGenericContext(); if (!genericDecl) return true; @@ -155,10 +164,15 @@ static bool isMemberDeclAppliedInternal(const DeclContext *DC, Type BaseTy, if (!genericSig) return true; + auto *module = DC->getParentModule(); SubstitutionMap substMap = BaseTy->getContextSubstitutionMap( - DC->getParentModule(), VD->getDeclContext()); - return areGenericRequirementsSatisfied(DC, genericSig, substMap, - /*isExtension=*/false); + module, VD->getDeclContext()); + + // Note: we treat substitution failure as success, to avoid tripping + // up over generic parameters introduced by the declaration itself. + return TypeChecker::checkGenericArguments( + module, genericSig.getRequirements(), + QuerySubstitutionMap{substMap}) != RequirementCheckResult::Failure; } bool diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 6a017f687b8ac..5971e04719d2c 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -28,6 +28,7 @@ #include "swift/ClangImporter/ClangModule.h" #include "swift/Parse/Parser.h" #include "swift/Subsystems.h" +#include "swift/Strings.h" #include "clang/Basic/Module.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/TinyPtrVector.h" @@ -317,6 +318,23 @@ void ImportResolver::bindImport(UnboundImport &&I) { ID.get()->setModule(nullptr); return; } + // If the imported module is a clang module, add an implicit import statement + // to request the SPIs from the module. + if (M->isNonSwiftModule() && ID && + !ID.get()->getAttrs().hasAttribute()) { + ImportDecl *id = ID.get(); + auto *newId = ImportDecl::create(id->getASTContext(), id->getDeclContext(), + SourceLoc(), id->getImportKind(), SourceLoc(), id->getImportPath()); + // Copy all the existing attribute from the actual import statement. + llvm::for_each(id->getAttrs(), + [&](DeclAttribute *attr) {newId->getAttrs().add(attr);}); + // Add SPI attribute with the default group name. + newId->getAttrs().add(SPIAccessControlAttr::create(id->getASTContext(), + SourceLoc(), SourceRange(), + { ctx.getIdentifier(CLANG_MODULE_DEFUALT_SPI_GROUP_NAME) })); + // So we'll resolve the new import. + unboundImports.push_back(UnboundImport(newId)); + } auto topLevelModule = I.getTopLevelModule(M, SF); diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index b9ddb8043447c..f4a91fd6f20b7 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -19,7 +19,6 @@ #include "clang/AST/DeclObjC.h" #include "swift/AST/ASTContext.h" #include "swift/AST/GenericSignature.h" -#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/ImportCache.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" @@ -413,7 +412,7 @@ static void doDynamicLookup(VisibleDeclConsumer &Consumer, } namespace { - typedef llvm::SmallPtrSet VisitedSet; + typedef llvm::SmallPtrSet VisitedSet; } // end anonymous namespace static DeclVisibilityKind getReasonForSuper(DeclVisibilityKind Reason) { @@ -478,6 +477,12 @@ static void lookupDeclsFromProtocolsBeingConformedTo( if (!isDeclVisibleInLookupMode(VD, LS, FromContext)) continue; + if (isa(VD)) { + // Typealias declarations of the protocol are always visible in + // types that inherits from it. + Consumer.foundDecl(VD, ReasonForThisProtocol); + continue; + } if (!VD->isProtocolRequirement()) continue; @@ -528,15 +533,15 @@ static void lookupVisibleMemberDeclsImpl(Type BaseTy, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC, LookupState LS, DeclVisibilityKind Reason, - GenericSignatureBuilder *GSB, + GenericSignature Sig, VisitedSet &Visited); static void - lookupVisibleProtocolMemberDecls(Type BaseTy, ProtocolDecl *PD, + lookupVisibleProtocolMemberDecls(Type BaseTy, const ProtocolDecl *PD, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC, LookupState LS, DeclVisibilityKind Reason, - GenericSignatureBuilder *GSB, + GenericSignature Sig, VisitedSet &Visited) { if (!Visited.insert(PD).second) return; @@ -545,7 +550,7 @@ static void lookupVisibleProtocolMemberDecls(BaseTy, Proto, Consumer, CurrDC, LS, getReasonForSuper(Reason), - GSB, Visited); + Sig, Visited); lookupTypeMembers(BaseTy, PD->getDeclaredInterfaceType(), Consumer, CurrDC, LS, Reason); } @@ -597,7 +602,7 @@ static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD, static void lookupVisibleMemberDeclsImpl( Type BaseTy, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC, - LookupState LS, DeclVisibilityKind Reason, GenericSignatureBuilder *GSB, + LookupState LS, DeclVisibilityKind Reason, GenericSignature Sig, VisitedSet &Visited) { // Just look through l-valueness. It doesn't affect name lookup. assert(BaseTy && "lookup into null type"); @@ -628,7 +633,7 @@ static void lookupVisibleMemberDeclsImpl( // functions, and can even look up non-static functions as well (thus // getting the address of the member). lookupVisibleMemberDeclsImpl(Ty, Consumer, CurrDC, subLS, Reason, - GSB, Visited); + Sig, Visited); return; } @@ -652,7 +657,7 @@ static void lookupVisibleMemberDeclsImpl( if (ProtocolType *PT = BaseTy->getAs()) { lookupVisibleProtocolMemberDecls(BaseTy, PT->getDecl(), Consumer, CurrDC, LS, Reason, - GSB, Visited); + Sig, Visited); return; } @@ -660,7 +665,7 @@ static void lookupVisibleMemberDeclsImpl( if (auto PC = BaseTy->getAs()) { for (auto Member : PC->getMembers()) lookupVisibleMemberDeclsImpl(Member, Consumer, CurrDC, LS, Reason, - GSB, Visited); + Sig, Visited); return; } @@ -669,38 +674,35 @@ static void lookupVisibleMemberDeclsImpl( for (auto Proto : Archetype->getConformsTo()) lookupVisibleProtocolMemberDecls( BaseTy, Proto, Consumer, CurrDC, LS, - Reason, GSB, Visited); + Reason, Sig, Visited); if (auto superclass = Archetype->getSuperclass()) lookupVisibleMemberDeclsImpl(superclass, Consumer, CurrDC, LS, - Reason, GSB, Visited); + Reason, Sig, Visited); return; } - // If we're looking into a type parameter and we have a generic signature - // builder, use the GSB to resolve where we should look. - if (BaseTy->isTypeParameter() && GSB) { - auto EquivClass = - GSB->resolveEquivalenceClass(BaseTy, - ArchetypeResolutionKind::CompleteWellFormed); - if (!EquivClass) return; - - if (EquivClass->concreteType) { - BaseTy = EquivClass->concreteType; + // If we're looking into a type parameter and we have a GenericSignature, + // query the signature to resolve where we should look. + if (BaseTy->isTypeParameter() && Sig) { + // The type might be fully concrete via a same-type requirement. + if (auto ConcreteTy = Sig->getConcreteType(BaseTy)) { + BaseTy = ConcreteTy; } else { - // Conformances - for (const auto &Conforms : EquivClass->conformsTo) { + // Look into protocols of conformance requirements + for (const auto *Proto : Sig->getRequiredProtocols(BaseTy)) { lookupVisibleProtocolMemberDecls( - BaseTy, Conforms.first, Consumer, CurrDC, - LS, getReasonForSuper(Reason), GSB, Visited); + BaseTy, Proto, Consumer, CurrDC, + LS, getReasonForSuper(Reason), Sig, Visited); } - // Superclass. - if (EquivClass->superclass) { - lookupVisibleMemberDeclsImpl(EquivClass->superclass, Consumer, CurrDC, + // Look into the superclass requirement type, if there is one. + if (auto SuperclassTy = Sig->getSuperclassBound(BaseTy)) { + lookupVisibleMemberDeclsImpl(SuperclassTy, Consumer, CurrDC, LS, getReasonForSuper(Reason), - GSB, Visited); + Sig, Visited); } + return; } } @@ -1073,7 +1075,7 @@ struct KeyPathDynamicMemberConsumer : public VisibleDeclConsumer { static void lookupVisibleDynamicMemberLookupDecls( Type baseType, KeyPathDynamicMemberConsumer &consumer, const DeclContext *dc, LookupState LS, DeclVisibilityKind reason, - GenericSignatureBuilder *GSB, VisitedSet &visited, + GenericSignature Sig, VisitedSet &visited, llvm::DenseSet &seenDynamicLookup); /// Enumerates all members of \c baseType, including both directly visible and @@ -1084,11 +1086,11 @@ static void lookupVisibleDynamicMemberLookupDecls( static void lookupVisibleMemberAndDynamicMemberDecls( Type baseType, VisibleDeclConsumer &consumer, KeyPathDynamicMemberConsumer &dynamicMemberConsumer, const DeclContext *DC, - LookupState LS, DeclVisibilityKind reason, GenericSignatureBuilder *GSB, + LookupState LS, DeclVisibilityKind reason, GenericSignature Sig, VisitedSet &visited, llvm::DenseSet &seenDynamicLookup) { - lookupVisibleMemberDeclsImpl(baseType, consumer, DC, LS, reason, GSB, visited); + lookupVisibleMemberDeclsImpl(baseType, consumer, DC, LS, reason, Sig, visited); lookupVisibleDynamicMemberLookupDecls(baseType, dynamicMemberConsumer, DC, LS, - reason, GSB, visited, seenDynamicLookup); + reason, Sig, visited, seenDynamicLookup); } /// Enumerates all keypath dynamic members of \c baseType, as seen from the @@ -1100,7 +1102,7 @@ static void lookupVisibleMemberAndDynamicMemberDecls( static void lookupVisibleDynamicMemberLookupDecls( Type baseType, KeyPathDynamicMemberConsumer &consumer, const DeclContext *dc, LookupState LS, DeclVisibilityKind reason, - GenericSignatureBuilder *GSB, VisitedSet &visited, + GenericSignature Sig, VisitedSet &visited, llvm::DenseSet &seenDynamicLookup) { if (!seenDynamicLookup.insert(baseType.getPointer()).second) return; @@ -1138,7 +1140,7 @@ static void lookupVisibleDynamicMemberLookupDecls( baseType); lookupVisibleMemberAndDynamicMemberDecls(memberType, consumer, consumer, dc, - LS, reason, GSB, visited, + LS, reason, Sig, visited, seenDynamicLookup); } } @@ -1151,7 +1153,7 @@ static void lookupVisibleDynamicMemberLookupDecls( /// binding. static void lookupVisibleMemberDecls( Type BaseTy, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC, - LookupState LS, DeclVisibilityKind Reason, GenericSignatureBuilder *GSB) { + LookupState LS, DeclVisibilityKind Reason, GenericSignature Sig) { OverrideFilteringConsumer overrideConsumer(BaseTy, CurrDC); KeyPathDynamicMemberConsumer dynamicConsumer( Consumer, @@ -1161,7 +1163,7 @@ static void lookupVisibleMemberDecls( llvm::DenseSet seenDynamicLookup; lookupVisibleMemberAndDynamicMemberDecls( BaseTy, overrideConsumer, dynamicConsumer, CurrDC, LS, Reason, - GSB, Visited, seenDynamicLookup); + Sig, Visited, seenDynamicLookup); // Report the declarations we found to the real consumer. overrideConsumer.filterDecls(Consumer); @@ -1332,7 +1334,7 @@ void swift::lookupVisibleMemberDecls(VisibleDeclConsumer &Consumer, Type BaseTy, bool includeInstanceMembers, bool includeDerivedRequirements, bool includeProtocolExtensionMembers, - GenericSignatureBuilder *GSB) { + GenericSignature Sig) { assert(CurrDC); LookupState ls = LookupState::makeQualified(); if (includeInstanceMembers) { @@ -1347,5 +1349,5 @@ void swift::lookupVisibleMemberDecls(VisibleDeclConsumer &Consumer, Type BaseTy, ::lookupVisibleMemberDecls(BaseTy, Consumer, CurrDC, ls, DeclVisibilityKind::MemberOfCurrentNominal, - GSB); + Sig); } diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index e223a4802757f..3e41a4162cbaa 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -77,7 +77,9 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, SmallPtrSet AlreadyDiagnosedBitCasts; /// Keep track of the arguments to CallExprs. - SmallPtrSet CallArgs; + /// Key -> an argument expression, + /// Value -> a call argument is associated with. + llvm::SmallDenseMap CallArgs; bool IsExprStmt; @@ -141,14 +143,14 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, checkUseOfMetaTypeName(Base); if (auto *OLE = dyn_cast(E)) { - CallArgs.insert(OLE->getArg()); + CallArgs.insert({OLE->getArg(), E}); } if (auto *SE = dyn_cast(E)) - CallArgs.insert(SE->getIndex()); + CallArgs.insert({SE->getIndex(), E}); if (auto *DSE = dyn_cast(E)) - CallArgs.insert(DSE->getIndex()); + CallArgs.insert({DSE->getIndex(), E}); if (auto *KPE = dyn_cast(E)) { // raise an error if this KeyPath contains an effectful member. @@ -156,7 +158,7 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, for (auto Comp : KPE->getComponents()) { if (auto *Arg = Comp.getIndexExpr()) - CallArgs.insert(Arg); + CallArgs.insert({Arg, E}); } } @@ -164,7 +166,7 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, // function and inspecting the arguments directly. if (auto *Call = dyn_cast(E)) { // Record call arguments. - CallArgs.insert(Call->getArg()); + CallArgs.insert({Call->getArg(), E}); // Warn about surprising implicit optional promotions. checkOptionalPromotions(Call); @@ -246,7 +248,7 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, // Void to _ then warn, because that is redundant. if (auto DAE = dyn_cast(destExpr)) { if (auto CE = dyn_cast(AE->getSrc())) { - if (CE->getCalledValue() && isa(CE->getCalledValue()) && + if (isa_and_nonnull(CE->getCalledValue()) && CE->getType()->isVoid()) { Ctx.Diags .diagnose(DAE->getLoc(), @@ -613,30 +615,30 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, if (!AlreadyDiagnosedMetatypes.insert(E).second) return; - // Allow references to types as a part of: - // - member references T.foo, T.Type, T.self, etc. - // - constructor calls T() - // - Subscripts T[] + // In Swift < 6 warn about plain type name passed as an + // argument to a subscript, dynamic subscript, or ObjC + // literal since it used to be accepted. + DiagnosticBehavior behavior = DiagnosticBehavior::Error; + if (auto *ParentExpr = Parent.getAsExpr()) { - // This is an exhaustive list of the accepted syntactic forms. - if (isa(ParentExpr) || - isa(ParentExpr) || // T.self - isa(ParentExpr) || // T() - isa(ParentExpr) || // T.foo - isa(ParentExpr) || - isa(ParentExpr) || // T.foo() T() - isa(ParentExpr) || - isa(ParentExpr) || - isa(ParentExpr) || - isa(ParentExpr) || - isa(ParentExpr)) { + if (ParentExpr->isValidParentOfTypeExpr(E)) return; + + if (!Ctx.LangOpts.isSwiftVersionAtLeast(6)) { + auto argument = CallArgs.find(ParentExpr); + if (argument != CallArgs.end()) { + auto *callExpr = argument->second; + if (isa(callExpr) || + isa(callExpr) || + isa(callExpr)) + behavior = DiagnosticBehavior::Warning; + } } } // Is this a protocol metatype? - - Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_metatype_type); + Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_metatype_type) + .limitBehavior(behavior); // Add fix-it to insert '()', only if this is a metatype of // non-existential type and has any initializers. @@ -1425,7 +1427,7 @@ static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) { // But silence the warning if the base was explicitly qualified. auto parentAsExpr = Parent.getAsExpr(); - if (parentAsExpr && isa(parentAsExpr)) + if (isa_and_nonnull(parentAsExpr)) shouldDiagnose = false; if (shouldDiagnose) { @@ -1621,7 +1623,8 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, memberLoc = MRE->getLoc(); Diags.diagnose(memberLoc, diag::property_use_in_closure_without_explicit_self, - baseName.getIdentifier()); + baseName.getIdentifier()) + .warnUntilSwiftVersionIf(Closures.size() > 1, 6); } // Handle method calls with a specific diagnostic + fixit. @@ -1632,7 +1635,8 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, memberLoc = DSCE->getLoc(); Diags.diagnose(DSCE->getLoc(), diag::method_call_in_closure_without_explicit_self, - MethodExpr->getDecl()->getBaseIdentifier()); + MethodExpr->getDecl()->getBaseIdentifier()) + .warnUntilSwiftVersionIf(Closures.size() > 1, 6); } if (memberLoc.isValid()) { @@ -1642,7 +1646,8 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, // Catch any other implicit uses of self with a generic diagnostic. if (isImplicitSelfParamUseLikelyToCauseCycle(E, ACE)) - Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure); + Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure) + .warnUntilSwiftVersionIf(Closures.size() > 1, 6); return { true, E }; } @@ -1840,13 +1845,9 @@ bool TypeChecker::getDefaultGenericArgumentsString( genericParamText << contextTy; }; - // FIXME: We can potentially be in the middle of creating a generic signature - // if we get here. Break this cycle. - if (typeDecl->hasComputedGenericSignature()) { - llvm::interleave(typeDecl->getInnermostGenericParamTypes(), - printGenericParamSummary, - [&] { genericParamText << ", "; }); - } + llvm::interleave(typeDecl->getInnermostGenericParamTypes(), + printGenericParamSummary, + [&] { genericParamText << ", "; }); genericParamText << ">"; return true; @@ -1892,9 +1893,7 @@ bool swift::diagnoseArgumentLabelError(ASTContext &ctx, assert(oldName || newName && "We can't have oldName and newName out of " "bounds, otherwise n would be smaller"); - if (oldName == newName || - (argList.hasTrailingClosure && i == argList.args.size() - 1 && - (numMissing > 0 || numExtra > 0 || numWrong > 0))) + if (oldName == newName || argList.isUnlabeledTrailingClosureIdx(i)) continue; if (!oldName.hasValue() && newName.hasValue()) { @@ -1974,11 +1973,17 @@ bool swift::diagnoseArgumentLabelError(ASTContext &ctx, if (i < newNames.size()) newName = newNames[i]; - if (oldName == newName || (i == n-1 && argList.hasTrailingClosure)) + if (oldName == newName || argList.isUnlabeledTrailingClosureIdx(i)) continue; if (newName.empty()) { - // Delete the old name. + // If this is a labeled trailing closure, we need to replace with '_'. + if (argList.isLabeledTrailingClosureIdx(i)) { + diag.fixItReplace(argList.labelLocs[i], "_"); + continue; + } + + // Otherwise, delete the old name. diag.fixItRemoveChars(argList.labelLocs[i], argList.args[i]->getStartLoc()); continue; @@ -1992,7 +1997,10 @@ bool swift::diagnoseArgumentLabelError(ASTContext &ctx, if (newNameIsReserved) newStr += "`"; - if (oldName.empty()) { + // If the argument was previously unlabeled, insert the new label. Note that + // we don't do this for labeled trailing closures as they write unlabeled + // args as '_:', and therefore need replacement. + if (oldName.empty() && !argList.isLabeledTrailingClosureIdx(i)) { // Insert the name. newStr += ": "; diag.fixItInsert(argList.args[i]->getStartLoc(), newStr); @@ -2496,26 +2504,16 @@ class VarDeclUsageChecker : public ASTWalker { // If this is a VarDecl, then add it to our list of things to track. if (auto *vd = dyn_cast(D)) { if (shouldTrackVarDecl(vd)) { - // Inline constructor. - auto defaultFlags = [&]() -> unsigned { - // If this VarDecl is nested inside of a CaptureListExpr, remember - // that fact for better diagnostics. - auto parentAsExpr = Parent.getAsExpr(); - if (parentAsExpr && isa(parentAsExpr)) - return RK_CaptureList | RK_Defined; - // Otherwise, return none. - return RK_Defined; - }(); - - if (!vd->isImplicit()) { - if (auto *childVd = - vd->getCorrespondingCaseBodyVariable().getPtrOrNull()) { - // Child vars are never in capture lists. - assert(defaultFlags == RK_Defined); - VarDecls[childVd] |= RK_Defined; - } + unsigned flags = RK_Defined; + if (vd->isCaptureList()) + flags |= RK_CaptureList; + + if (auto childVd = vd->getCorrespondingCaseBodyVariable()) { + // Child vars are never in capture lists. + assert(flags == RK_Defined); + addMark(childVd.get(), flags); } - VarDecls[vd] |= defaultFlags; + addMark(vd, flags); } } @@ -2639,11 +2637,13 @@ class VarDeclUsageChecker : public ASTWalker { /// An AST walker that determines the underlying type of an opaque return decl /// from its associated function body. class OpaqueUnderlyingTypeChecker : public ASTWalker { + using Candidate = std::pair; + ASTContext &Ctx; AbstractFunctionDecl *Implementation; OpaqueTypeDecl *OpaqueDecl; BraceStmt *Body; - SmallVector, 4> Candidates; + SmallVector Candidates; bool HasInvalidReturn = false; @@ -2674,24 +2674,15 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker { Implementation->diagnose(diag::opaque_type_no_underlying_type_candidates); return; } - + // Check whether all of the underlying type candidates match up. - auto opaqueTypeInContext = - Implementation->mapTypeIntoContext(OpaqueDecl->getDeclaredInterfaceType()); + // TODO [OPAQUE SUPPORT]: multiple opaque types Type underlyingType = Candidates.front().second; - - bool mismatch = false; - for (auto otherCandidate : llvm::makeArrayRef(Candidates).slice(1)) { - // Disregard tautological candidates. - if (otherCandidate.second->isEqual(opaqueTypeInContext)) - continue; - - if (!underlyingType->isEqual(otherCandidate.second)) { - mismatch = true; - break; - } - } - + bool mismatch = + std::any_of(Candidates.begin() + 1, Candidates.end(), + [&](Candidate &otherCandidate) { + return !underlyingType->isEqual(otherCandidate.second); + }); if (mismatch) { Implementation->diagnose( diag::opaque_type_mismatched_underlying_type_candidates); @@ -2705,6 +2696,8 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker { // The underlying type can't be defined recursively // in terms of the opaque type itself. + auto opaqueTypeInContext = Implementation->mapTypeIntoContext( + OpaqueDecl->getDeclaredInterfaceType()); auto isSelfReferencing = underlyingType.findIf([&](Type t) -> bool { return t->isEqual(opaqueTypeInContext); }); @@ -2736,14 +2729,11 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker { std::pair walkToExprPre(Expr *E) override { if (auto underlyingToOpaque = dyn_cast(E)) { - assert(E->getType()->isEqual( - Implementation->mapTypeIntoContext(OpaqueDecl->getDeclaredInterfaceType())) - && "unexpected opaque type in function body"); - Candidates.push_back(std::make_pair(underlyingToOpaque->getSubExpr(), underlyingToOpaque->getSubExpr()->getType())); + return {false, E}; } - return std::make_pair(false, E); + return {true, E}; } std::pair walkToStmtPre(Stmt *S) override { @@ -2892,12 +2882,31 @@ VarDeclUsageChecker::~VarDeclUsageChecker() { // If the subexpr is an "as?" cast, we can rewrite it to // be an "is" test. - bool isIsTest = false; - if (isa(initExpr) && - !initExpr->isImplicit()) { - noParens = isIsTest = true; + ConditionalCheckedCastExpr *CCE = nullptr; + + // initExpr can be wrapped inside parens or try expressions. + if (auto ccExpr = dyn_cast( + initExpr->getValueProvidingExpr())) { + if (!ccExpr->isImplicit()) { + CCE = ccExpr; + noParens = true; + } } - + + // In cases where the value is optional, the cast expr is + // wrapped inside OptionalEvaluationExpr. Unwrap it to get + // ConditionalCheckedCastExpr. + if (auto oeExpr = dyn_cast( + initExpr->getValueProvidingExpr())) { + if (auto ccExpr = dyn_cast( + oeExpr->getSubExpr()->getValueProvidingExpr())) { + if (!ccExpr->isImplicit()) { + CCE = ccExpr; + noParens = true; + } + } + } + auto diagIF = Diags.diagnose(var->getLoc(), diag::pbd_never_used_stmtcond, var->getName()); @@ -2905,10 +2914,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() { diagIF.fixItReplaceChars(introducerLoc, initExpr->getStartLoc(), &"("[noParens]); - - if (isIsTest) { + + if (CCE) { // If this was an "x as? T" check, rewrite it to "x is T". - auto CCE = cast(initExpr); diagIF.fixItReplace(SourceRange(CCE->getLoc(), CCE->getQuestionLoc()), "is"); @@ -3321,8 +3329,6 @@ static void checkSwitch(ASTContext &ctx, const SwitchStmt *stmt) { // We want to warn about "case .Foo, .Bar where 1 != 100:" since the where // clause only applies to the second case, and this is surprising. for (auto cs : stmt->getCases()) { - TypeChecker::checkUnsupportedProtocolType(ctx, cs); - // The case statement can have multiple case items, each can have a where. // If we find a "where", and there is a preceding item without a where, and // if they are on the same source line, then warn. @@ -4070,7 +4076,7 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E, if (auto *apply = dyn_cast(E)) { auto *decl = apply->getCalledValue(); - if (decl && isa(decl)) + if (isa_and_nonnull(decl)) return decl; } return nullptr; @@ -4532,11 +4538,6 @@ static void diagnoseComparisonWithNaN(const Expr *E, const DeclContext *DC) { void tryDiagnoseComparisonWithNaN(BinaryExpr *BE) { ValueDecl *comparisonDecl = nullptr; - // Comparison functions like == or <= take two arguments. - if (BE->getArg()->getNumElements() != 2) { - return; - } - // Dig out the function declaration. if (auto Fn = BE->getFn()) { if (auto DSCE = dyn_cast(Fn)) { @@ -4557,16 +4558,25 @@ static void diagnoseComparisonWithNaN(const Expr *E, const DeclContext *DC) { return; } - auto firstArg = BE->getArg()->getElement(0); - auto secondArg = BE->getArg()->getElement(1); + auto *firstArg = BE->getLHS(); + auto *secondArg = BE->getRHS(); + + // Make sure that both arguments are valid before doing anything else, + // this helps us to debug reports of crashes in `conformsToKnownProtocol` + // referencing arguments (rdar://78920375). + // + // Since this diagnostic should only be run on type-checked AST, + // it's unclear what caused one of the arguments to have null type. + assert(firstArg->getType() && "Expected valid type for first argument"); + assert(secondArg->getType() && "Expected valid type for second argument"); // Both arguments must conform to FloatingPoint protocol. - if (!conformsToKnownProtocol(const_cast(DC), - firstArg->getType(), - KnownProtocolKind::FloatingPoint) || - !conformsToKnownProtocol(const_cast(DC), - secondArg->getType(), - KnownProtocolKind::FloatingPoint)) { + if (!TypeChecker::conformsToKnownProtocol(firstArg->getType(), + KnownProtocolKind::FloatingPoint, + DC->getParentModule()) || + !TypeChecker::conformsToKnownProtocol(secondArg->getType(), + KnownProtocolKind::FloatingPoint, + DC->getParentModule())) { return; } @@ -4669,8 +4679,17 @@ class CompletionHandlerUsageChecker final : public ASTWalker { if (!asyncFunc) return {false, call}; ctx.Diags.diagnose(call->getLoc(), diag::warn_use_async_alternative); - ctx.Diags.diagnose(asyncFunc->getLoc(), diag::decl_declared_here, - asyncFunc->getName()); + + if (auto *accessor = dyn_cast(asyncFunc)) { + SmallString<32> name; + llvm::raw_svector_ostream os(name); + accessor->printUserFacingName(os); + ctx.Diags.diagnose(asyncFunc->getLoc(), + diag::descriptive_decl_declared_here, name); + } else { + ctx.Diags.diagnose(asyncFunc->getLoc(), diag::decl_declared_here, + asyncFunc->getName()); + } } } } @@ -4726,8 +4745,6 @@ void swift::performSyntacticExprDiagnostics(const Expr *E, void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) { auto &ctx = DC->getASTContext(); - TypeChecker::checkUnsupportedProtocolType(ctx, const_cast(S)); - if (auto switchStmt = dyn_cast(S)) checkSwitch(ctx, switchStmt); diff --git a/lib/Sema/MiscDiagnostics.h b/lib/Sema/MiscDiagnostics.h index 29f47c5b9302a..928cafcbf7542 100644 --- a/lib/Sema/MiscDiagnostics.h +++ b/lib/Sema/MiscDiagnostics.h @@ -111,8 +111,8 @@ void fixItEncloseTrailingClosure(ASTContext &ctx, /// Check that we use the async version of a function where available /// /// If a completion-handler function is called from an async context and it has -/// a '@completionHandlerAsync' attribute, we emit a diagnostic suggesting the -/// async call. +/// a '@available' attribute with renamed field pointing to an async function, +/// we emit a diagnostic suggesting the async call. void checkFunctionAsyncUsage(AbstractFunctionDecl *decl); void checkPatternBindingDeclAsyncUsage(PatternBindingDecl *decl); } // namespace swift diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index 683c19f22cc32..42aa53e1a4406 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -380,6 +380,44 @@ static bool isValidForwardReference(ValueDecl *D, DeclContext *DC, return true; } +/// Checks whether this is a BinaryExpr with operator `&` and returns the +/// BinaryExpr, if so. +static BinaryExpr *getCompositionExpr(Expr *expr) { + if (auto *binaryExpr = dyn_cast(expr)) { + // look at the name of the operator, if it is a '&' we can create the + // composition TypeExpr + auto fn = binaryExpr->getFn(); + if (auto Overload = dyn_cast(fn)) { + if (llvm::any_of(Overload->getDecls(), [](auto *decl) -> bool { + return decl->getBaseName() == "&"; + })) + return binaryExpr; + } else if (auto *Decl = dyn_cast(fn)) { + if (Decl->getName().isSimpleName() && + Decl->getName().getBaseName() == "&") + return binaryExpr; + } + } + + return nullptr; +} + +/// Whether the given expression "looks like" a (possibly sugared) type. For +/// example, `(foo, bar)` "looks like" a type, but `foo + bar` does not. +static bool exprLooksLikeAType(Expr *expr) { + return isa(expr) || + isa(expr) || + isa(expr) || + isa(expr) || + isa(expr) || + isa(expr) || + (isa(expr) && + cast(expr)->getElements().size() == 1) || + (isa(expr) && + cast(expr)->getElements().size() == 1) || + getCompositionExpr(expr); +} + /// Bind an UnresolvedDeclRefExpr by performing name lookup and /// returning the resultant expression. Context is the DeclContext used /// for the lookup. @@ -897,6 +935,18 @@ namespace { /// the type conforms to the expected literal protocol. Expr *simplifyTypeConstructionWithLiteralArg(Expr *E); + /// Whether the current expression \p E is in a context that might turn out + /// to be a \c TypeExpr after \c simplifyTypeExpr is called up the tree. + /// This function allows us to make better guesses about whether invalid + /// uses of '_' were "supposed" to be \c DiscardAssignmentExprs or patterns, + /// which results in better diagnostics after type checking. + bool possiblyInTypeContext(Expr *E); + + /// Whether we can simplify the given discard assignment expr. Not possible + /// if it's been marked "valid" or if the current state of the AST disallows + /// such simplification (see \c canSimplifyPlaceholderTypes above). + bool canSimplifyDiscardAssignmentExpr(DiscardAssignmentExpr *DAE); + /// In Swift < 5, diagnose and correct invalid multi-argument or /// argument-labeled interpolations. void correctInterpolationIfStrange(InterpolatedStringLiteralExpr *ISLE) { @@ -1110,8 +1160,11 @@ namespace { // return site should call through here. auto finish = [&](bool recursive, Expr *expr) { // If we're going to recurse, record this expression on the stack. - if (recursive) + if (recursive) { + if (isa(expr)) + SequenceExprDepth++; ExprStack.push_back(expr); + } return std::make_pair(recursive, expr); }; @@ -1225,9 +1278,6 @@ namespace { if (auto *assignment = dyn_cast(expr)) markAcceptableDiscardExprs(assignment->getDest()); - if (isa(expr)) - SequenceExprDepth++; - return finish(true, expr); } @@ -1483,7 +1533,9 @@ TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { }, // FIXME: Don't let placeholder types escape type resolution. // For now, just return the placeholder type. - PlaceholderType::get); + [](auto &ctx, auto *originator) { + return Type(); + }); const auto BaseTy = resolution.resolveType(InnerTypeRepr); if (BaseTy->mayHaveMembers()) { @@ -1521,6 +1573,32 @@ TypeExpr *PreCheckExpression::simplifyUnresolvedSpecializeExpr( return nullptr; } +bool PreCheckExpression::possiblyInTypeContext(Expr *E) { + // Walk back up the stack of parents looking for a valid type context. + for (auto *ParentExpr : llvm::reverse(ExprStack)) { + // We're considered to be in a type context if either: + // - We have a valid parent for a TypeExpr, or + // - The parent "looks like" a type (and is not a call arg), and we can + // reach a valid parent for a TypeExpr if we continue walking. + if (ParentExpr->isValidParentOfTypeExpr(E)) + return true; + + if (!exprLooksLikeAType(ParentExpr) || CallArgs.count(ParentExpr)) + return false; + + E = ParentExpr; + } + return false; +} + +/// Only allow simplification of a DiscardAssignmentExpr if it hasn't already +/// been explicitly marked as correct, and the current AST state allows it. +bool PreCheckExpression::canSimplifyDiscardAssignmentExpr( + DiscardAssignmentExpr *DAE) { + return !CorrectDiscardAssignmentExprs.count(DAE) && SequenceExprDepth == 0 && + possiblyInTypeContext(DAE); +} + /// Simplify expressions which are type sugar productions that got parsed /// as expressions due to the parser not knowing which identifiers are /// type names. @@ -1534,8 +1612,14 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { return simplifyNestedTypeExpr(UDE); } - // TODO: Fold DiscardAssignmentExpr into a placeholder type here once parsing - // them is supported. + // Fold '_' into a placeholder type, if we're allowed. + if (auto *DAE = dyn_cast(E)) { + if (canSimplifyDiscardAssignmentExpr(DAE)) { + auto *placeholderRepr = + new (getASTContext()) PlaceholderTypeRepr(DAE->getLoc()); + return new (getASTContext()) TypeExpr(placeholderRepr); + } + } // Fold T? into an optional type when T is a TypeExpr. if (isa(E) || isa(E)) { @@ -1778,56 +1862,38 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { } // Fold 'P & Q' into a composition type - if (auto *binaryExpr = dyn_cast(E)) { - bool isComposition = false; - // look at the name of the operator, if it is a '&' we can create the - // composition TypeExpr - auto fn = binaryExpr->getFn(); - if (auto Overload = dyn_cast(fn)) { - for (auto Decl : Overload->getDecls()) - if (Decl->getBaseName() == "&") { - isComposition = true; - break; - } - } else if (auto *Decl = dyn_cast(fn)) { - if (Decl->getName().isSimpleName() && - Decl->getName().getBaseName() == "&") - isComposition = true; - } - - if (isComposition) { - // The protocols we are composing - SmallVector Types; - - auto lhsExpr = binaryExpr->getArg()->getElement(0); - if (auto *lhs = dyn_cast(lhsExpr)) { - Types.push_back(lhs->getTypeRepr()); - } else if (isa(lhsExpr)) { - // If the lhs is another binary expression, we have a multi element - // composition: 'A & B & C' is parsed as ((A & B) & C); we get - // the protocols from the lhs here - if (auto expr = simplifyTypeExpr(lhsExpr)) - if (auto *repr = dyn_cast(expr->getTypeRepr())) - // add the protocols to our list - for (auto proto : repr->getTypes()) - Types.push_back(proto); - else - return nullptr; + if (auto *binaryExpr = getCompositionExpr(E)) { + // The protocols we are composing + SmallVector Types; + + auto lhsExpr = binaryExpr->getLHS(); + if (auto *lhs = dyn_cast(lhsExpr)) { + Types.push_back(lhs->getTypeRepr()); + } else if (isa(lhsExpr)) { + // If the lhs is another binary expression, we have a multi element + // composition: 'A & B & C' is parsed as ((A & B) & C); we get + // the protocols from the lhs here + if (auto expr = simplifyTypeExpr(lhsExpr)) + if (auto *repr = dyn_cast(expr->getTypeRepr())) + // add the protocols to our list + for (auto proto : repr->getTypes()) + Types.push_back(proto); else return nullptr; - } else + else return nullptr; + } else + return nullptr; - // Add the rhs which is just a TypeExpr - auto *rhs = dyn_cast(binaryExpr->getArg()->getElement(1)); - if (!rhs) return nullptr; - Types.push_back(rhs->getTypeRepr()); + // Add the rhs which is just a TypeExpr + auto *rhs = dyn_cast(binaryExpr->getRHS()); + if (!rhs) return nullptr; + Types.push_back(rhs->getTypeRepr()); - auto CompRepr = CompositionTypeRepr::create(getASTContext(), Types, - lhsExpr->getStartLoc(), - binaryExpr->getSourceRange()); - return new (getASTContext()) TypeExpr(CompRepr); - } + auto CompRepr = CompositionTypeRepr::create(getASTContext(), Types, + lhsExpr->getStartLoc(), + binaryExpr->getSourceRange()); + return new (getASTContext()) TypeExpr(CompRepr); } return nullptr; @@ -1888,6 +1954,15 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { UDE->getName(), UDE->getLoc())); expr = UDE->getBase(); + } else if (auto CCE = dyn_cast(expr)) { + components.push_back( + KeyPathExpr::Component::forCodeCompletion(CCE->getLoc())); + + expr = CCE->getBase(); + if (!expr) { + // We are completing on the key path's base. Stop iterating. + return; + } } else if (auto SE = dyn_cast(expr)) { // .[0] or just plain [0] components.push_back( @@ -2017,7 +2092,9 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { }, // FIXME: Don't let placeholder types escape type resolution. // For now, just return the placeholder type. - PlaceholderType::get); + [](auto &ctx, auto *originator) { + return Type(); + }); const auto result = resolution.resolveType(typeExpr->getTypeRepr()); if (result->hasError()) return nullptr; @@ -2034,8 +2111,7 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { } SmallVector conformances; - return castTy->getAnyNominal()->lookupConformance(DC->getParentModule(), - protocol, conformances) + return castTy->getAnyNominal()->lookupConformance(protocol, conformances) ? CoerceExpr::forLiteralInit(getASTContext(), argExpr, call->getSourceRange(), typeExpr->getTypeRepr()) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 7969cff02e0eb..ba09960520acf 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1874,12 +1874,10 @@ class DeclAvailabilityChecker : public DeclVisitor { } void visitInfixOperatorDecl(InfixOperatorDecl *IOD) { - // FIXME: Handle operator designated types (which also applies to prefix - // and postfix operators). if (auto *precedenceGroup = IOD->getPrecedenceGroup()) { - if (!IOD->getIdentifiers().empty()) { + if (!IOD->getPrecedenceGroupName().empty()) { checkPrecedenceGroup(precedenceGroup, IOD, IOD->getLoc(), - IOD->getIdentifiers().front().Loc); + IOD->getPrecedenceGroupLoc()); } } } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index a4a55fbc2bd28..32babe8ab06c0 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -34,9 +34,11 @@ #include "swift/AST/PropertyWrappers.h" #include "swift/AST/SourceFile.h" #include "swift/AST/StorageImpl.h" +#include "swift/AST/SwiftNameTranslation.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "swift/Parse/Lexer.h" +#include "swift/Parse/Parser.h" #include "swift/Sema/IDETypeChecking.h" #include "clang/Basic/CharInfo.h" #include "llvm/ADT/STLExtras.h" @@ -45,35 +47,6 @@ using namespace swift; namespace { - /// This emits a diagnostic with a fixit to remove the attribute. - template - InFlightDiagnostic - diagnoseAndRemoveAttr(DiagnosticEngine &Diags, Decl *D, DeclAttribute *attr, - ArgTypes &&...Args) { - attr->setInvalid(); - - assert(!D->hasClangNode() && "Clang importer propagated a bogus attribute"); - if (!D->hasClangNode()) { - SourceLoc loc = attr->getLocation(); -#ifndef NDEBUG - if (!loc.isValid() && !attr->getAddedByAccessNote()) { - llvm::errs() << "Attribute '"; - attr->print(llvm::errs()); - llvm::errs() << "' has invalid location, failed to diagnose!\n"; - assert(false && "Diagnosing attribute with invalid location"); - } -#endif - if (loc.isInvalid()) { - loc = D->getLoc(); - } - if (loc.isValid()) { - return std::move(Diags.diagnose(loc, std::forward(Args)...) - .fixItRemove(attr->getRangeWithAt())); - } - } - return InFlightDiagnostic(); - } - /// This visits each attribute on a decl. The visitor should return true if /// the attribute is invalid and should be marked as such. class AttributeChecker : public AttributeVisitor { @@ -87,8 +60,8 @@ class AttributeChecker : public AttributeVisitor { template InFlightDiagnostic diagnoseAndRemoveAttr(DeclAttribute *attr, ArgTypes &&...Args) { - return ::diagnoseAndRemoveAttr(Ctx.Diags, D, attr, - std::forward(Args)...); + return swift::diagnoseAndRemoveAttr(D, attr, + std::forward(Args)...); } template @@ -119,6 +92,7 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(RequiresStoredPropertyInits) IGNORED_ATTR(RestatedObjCConformance) IGNORED_ATTR(Semantics) + IGNORED_ATTR(EmitAssemblyVisionRemarks) IGNORED_ATTR(ShowInInterface) IGNORED_ATTR(SILGenName) IGNORED_ATTR(StaticInitializeObjCMetadata) @@ -139,6 +113,7 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(UnsafeMainActor) IGNORED_ATTR(ImplicitSelfCapture) IGNORED_ATTR(InheritActorContext) + IGNORED_ATTR(Isolated) #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { @@ -279,19 +254,17 @@ class AttributeChecker : public AttributeVisitor { void visitDerivativeAttr(DerivativeAttr *attr); void visitTransposeAttr(TransposeAttr *attr); - void visitAsyncHandlerAttr(AsyncHandlerAttr *attr); void visitActorAttr(ActorAttr *attr); - void visitActorIndependentAttr(ActorIndependentAttr *attr); + void visitDistributedActorAttr(DistributedActorAttr *attr); + void visitDistributedActorIndependentAttr(DistributedActorIndependentAttr *attr); void visitGlobalActorAttr(GlobalActorAttr *attr); void visitAsyncAttr(AsyncAttr *attr); - void visitSpawnAttr(SpawnAttr *attr); - void visitAsyncOrSpawnAttr(DeclAttribute *attr); void visitMarkerAttr(MarkerAttr *attr); void visitReasyncAttr(ReasyncAttr *attr); void visitNonisolatedAttr(NonisolatedAttr *attr); - void visitCompletionHandlerAsyncAttr(CompletionHandlerAsyncAttr *attr); }; + } // end anonymous namespace void AttributeChecker::visitTransparentAttr(TransparentAttr *attr) { @@ -825,7 +798,9 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) { } if (attr->getAccess() == AccessLevel::Open) { - if (!isa(D) && !D->isPotentiallyOverridable() && + auto classDecl = dyn_cast(D); + if (!(classDecl && !classDecl->isActor()) && + !D->isPotentiallyOverridable() && !attr->isInvalid()) { diagnose(attr->getLocation(), diag::access_control_open_bad_decl) .fixItReplace(attr->getRange(), "public"); @@ -946,6 +921,7 @@ static bool checkObjCDeclContext(Decl *D) { } static void diagnoseObjCAttrWithoutFoundation(ObjCAttr *attr, Decl *decl, + ObjCReason reason, DiagnosticBehavior behavior) { auto *SF = decl->getDeclContext()->getParentSourceFile(); assert(SF); @@ -954,11 +930,15 @@ static void diagnoseObjCAttrWithoutFoundation(ObjCAttr *attr, Decl *decl, if (attr->isImplicit()) return; + // @objc enums do not require -enable-objc-interop or Foundation be have been + // imported. + if (isa(decl)) + return; + auto &ctx = SF->getASTContext(); if (!ctx.LangOpts.EnableObjCInterop) { - ctx.Diags.diagnose(attr->getLocation(), diag::objc_interop_disabled) - .fixItRemove(attr->getRangeWithAt()) + diagnoseAndRemoveAttr(decl, attr, diag::objc_interop_disabled) .limitBehavior(behavior); return; } @@ -981,6 +961,7 @@ static void diagnoseObjCAttrWithoutFoundation(ObjCAttr *attr, Decl *decl, ctx.Id_Foundation) .highlight(attr->getRangeWithAt()) .limitBehavior(behavior); + reason.describe(decl); } void AttributeChecker::visitObjCAttr(ObjCAttr *attr) { @@ -1023,9 +1004,21 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) { if (error) { diagnoseAndRemoveAttr(attr, *error).limitBehavior(behavior); + reason.describe(D); return; } + auto correctNameUsingNewAttr = [&](ObjCAttr *newAttr) { + if (attr->isInvalid()) newAttr->setInvalid(); + newAttr->setImplicit(attr->isImplicit()); + newAttr->setNameImplicit(attr->isNameImplicit()); + newAttr->setAddedByAccessNote(attr->getAddedByAccessNote()); + D->getAttrs().add(newAttr); + + D->getAttrs().removeAttribute(attr); + attr->setInvalid(); + }; + // If there is a name, check whether the kind of name is // appropriate. if (auto objcName = attr->getName()) { @@ -1045,24 +1038,31 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) { else { firstNameLoc = D->getLoc(); } - diagnose(firstNameLoc, diag::objc_name_req_nullary, - D->getDescriptiveKind()) - .fixItRemoveChars(afterFirstNameLoc, attr->getRParenLoc()) - .limitBehavior(behavior); - const_cast(attr)->setName( - ObjCSelector(Ctx, 0, objcName->getSelectorPieces()[0]), - /*implicit=*/false); + softenIfAccessNote(D, attr, + diagnose(firstNameLoc, diag::objc_name_req_nullary, + D->getDescriptiveKind()) + .fixItRemoveChars(afterFirstNameLoc, attr->getRParenLoc()) + .limitBehavior(behavior)); + + correctNameUsingNewAttr( + ObjCAttr::createNullary(Ctx, attr->AtLoc, attr->getLocation(), + attr->getLParenLoc(), firstNameLoc, + objcName->getSelectorPieces()[0], + attr->getRParenLoc())); } } else if (isa(D) || isa(D)) { SourceLoc diagLoc = attr->getLParenLoc(); if (diagLoc.isInvalid()) diagLoc = D->getLoc(); - diagnose(diagLoc, - isa(D) - ? diag::objc_name_subscript - : diag::objc_name_deinit) - .limitBehavior(behavior); - const_cast(attr)->clearName(); + softenIfAccessNote(D, attr, + diagnose(diagLoc, + isa(D) + ? diag::objc_name_subscript + : diag::objc_name_deinit) + .limitBehavior(behavior)); + + correctNameUsingNewAttr( + ObjCAttr::createUnnamed(Ctx, attr->AtLoc, attr->getLocation())); } else { auto func = cast(D); @@ -1092,28 +1092,30 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) { SourceLoc firstNameLoc = func->getLoc(); if (!attr->getNameLocs().empty()) firstNameLoc = attr->getNameLocs().front(); - diagnose(firstNameLoc, - diag::objc_name_func_mismatch, - isa(func), - numArgumentNames, - numArgumentNames != 1, - numParameters, - numParameters != 1, - func->hasThrows()) - .limitBehavior(behavior); - D->getAttrs().add( - ObjCAttr::createUnnamed(Ctx, attr->AtLoc, attr->Range.Start)); - D->getAttrs().removeAttribute(attr); + softenIfAccessNote(D, attr, + diagnose(firstNameLoc, + diag::objc_name_func_mismatch, + isa(func), + numArgumentNames, + numArgumentNames != 1, + numParameters, + numParameters != 1, + func->hasThrows()) + .limitBehavior(behavior)); + + correctNameUsingNewAttr( + ObjCAttr::createUnnamed(Ctx, attr->AtLoc, attr->Range.Start)); } } } else if (isa(D)) { // Enum elements require names. diagnoseAndRemoveAttr(attr, diag::objc_enum_case_req_name) .limitBehavior(behavior); + reason.describe(D); } // Diagnose an @objc attribute used without importing Foundation. - diagnoseObjCAttrWithoutFoundation(attr, D, behavior); + diagnoseObjCAttrWithoutFoundation(attr, D, reason, behavior); } void AttributeChecker::visitNonObjCAttr(NonObjCAttr *attr) { @@ -1208,20 +1210,13 @@ void TypeChecker::checkDeclAttributes(Decl *D) { default: break; } - DiagnosticBehavior behavior = attr->getAddedByAccessNote() - ? DiagnosticBehavior::Remark - : DiagnosticBehavior::Unspecified; - if (!OnlyKind.empty()) Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind, - attr, OnlyKind) - .limitBehavior(behavior); + attr, OnlyKind); else if (attr->isDeclModifier()) - Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr) - .limitBehavior(behavior); + Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr); else - Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr) - .limitBehavior(behavior); + Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr); } Checker.checkOriginalDefinedInAttrs(D, ODIAttrs); } @@ -1230,9 +1225,9 @@ void TypeChecker::checkDeclAttributes(Decl *D) { /// @dynamicCallable attribute requirement. The method is given to be defined /// as one of the following: `dynamicallyCall(withArguments:)` or /// `dynamicallyCall(withKeywordArguments:)`. -bool swift::isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC, +bool swift::isValidDynamicCallableMethod(FuncDecl *decl, ModuleDecl *module, bool hasKeywordArguments) { - auto &ctx = decl->getASTContext(); + auto &ctx = module->getASTContext(); // There are two cases to check. // 1. `dynamicallyCall(withArguments:)`. // In this case, the method is valid if the argument has type `A` where @@ -1253,7 +1248,7 @@ bool swift::isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC, if (!hasKeywordArguments) { auto arrayLitProto = ctx.getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral); - return (bool)TypeChecker::conformsToProtocol(argType, arrayLitProto, DC); + return (bool)TypeChecker::conformsToProtocol(argType, arrayLitProto, module); } // If keyword arguments, check that argument type conforms to // `ExpressibleByDictionaryLiteral` and that the `Key` associated type @@ -1262,11 +1257,11 @@ bool swift::isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC, ctx.getProtocol(KnownProtocolKind::ExpressibleByStringLiteral); auto dictLitProto = ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral); - auto dictConf = TypeChecker::conformsToProtocol(argType, dictLitProto, DC); + auto dictConf = TypeChecker::conformsToProtocol(argType, dictLitProto, module); if (dictConf.isInvalid()) return false; auto keyType = dictConf.getTypeWitnessByName(argType, ctx.Id_Key); - return (bool)TypeChecker::conformsToProtocol(keyType, stringLitProtocol, DC); + return (bool)TypeChecker::conformsToProtocol(keyType, stringLitProtocol, module); } /// Returns true if the given nominal type has a valid implementation of a @@ -1281,9 +1276,10 @@ static bool hasValidDynamicCallableMethod(NominalTypeDecl *decl, if (candidates.empty()) return false; // Filter valid candidates. + auto *module = decl->getParentModule(); candidates.filter([&](LookupResultEntry entry, bool isOuter) { auto candidate = cast(entry.getValueDecl()); - return isValidDynamicCallableMethod(candidate, decl, hasKeywordArgs); + return isValidDynamicCallableMethod(candidate, module, hasKeywordArgs); }); // If there are no valid candidates, return false. @@ -1332,17 +1328,17 @@ static bool hasSingleNonVariadicParam(SubscriptDecl *decl, /// the `subscript(dynamicMember:)` requirement for @dynamicMemberLookup. /// The method is given to be defined as `subscript(dynamicMember:)`. bool swift::isValidDynamicMemberLookupSubscript(SubscriptDecl *decl, - DeclContext *DC, + ModuleDecl *module, bool ignoreLabel) { // It could be // - `subscript(dynamicMember: {Writable}KeyPath<...>)`; or // - `subscript(dynamicMember: String*)` return isValidKeyPathDynamicMemberLookup(decl, ignoreLabel) || - isValidStringDynamicMemberLookup(decl, DC, ignoreLabel); + isValidStringDynamicMemberLookup(decl, module, ignoreLabel); } bool swift::isValidStringDynamicMemberLookup(SubscriptDecl *decl, - DeclContext *DC, + ModuleDecl *module, bool ignoreLabel) { auto &ctx = decl->getASTContext(); // There are two requirements: @@ -1355,11 +1351,9 @@ bool swift::isValidStringDynamicMemberLookup(SubscriptDecl *decl, const auto *param = decl->getIndices()->get(0); auto paramType = param->getType(); - auto stringLitProto = - ctx.getProtocol(KnownProtocolKind::ExpressibleByStringLiteral); - // If this is `subscript(dynamicMember: String*)` - return (bool)TypeChecker::conformsToProtocol(paramType, stringLitProto, DC); + return TypeChecker::conformsToKnownProtocol( + paramType, KnownProtocolKind::ExpressibleByStringLiteral, module); } bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, @@ -1390,6 +1384,8 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) { auto type = decl->getDeclaredType(); auto &ctx = decl->getASTContext(); + auto *module = decl->getParentModule(); + auto emitInvalidTypeDiagnostic = [&](const SourceLoc loc) { diagnose(loc, diag::invalid_dynamic_member_lookup_type, type); attr->setInvalid(); @@ -1405,7 +1401,7 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) { auto oneCandidate = candidates.front().getValueDecl(); candidates.filter([&](LookupResultEntry entry, bool isOuter) -> bool { auto cand = cast(entry.getValueDecl()); - return isValidDynamicMemberLookupSubscript(cand, decl); + return isValidDynamicMemberLookupSubscript(cand, module); }); if (candidates.empty()) { @@ -1427,7 +1423,7 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) { // Validate the candidates while ignoring the label. newCandidates.filter([&](const LookupResultEntry entry, bool isOuter) { auto cand = cast(entry.getValueDecl()); - return isValidDynamicMemberLookupSubscript(cand, decl, + return isValidDynamicMemberLookupSubscript(cand, module, /*ignoreLabel*/ true); }); @@ -1801,8 +1797,9 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, } if (!ApplicationDelegateProto || - !TypeChecker::conformsToProtocol(CD->getDeclaredType(), - ApplicationDelegateProto, CD)) { + !TypeChecker::conformsToProtocol(CD->getDeclaredInterfaceType(), + ApplicationDelegateProto, + CD->getParentModule())) { diagnose(attr->getLocation(), diag::attr_ApplicationMain_not_ApplicationDelegate, applicationMainKind); @@ -1868,7 +1865,7 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) { /*Implicit*/ true); memberRefExpr->setImplicit(true); - auto *callExpr = CallExpr::createImplicit(context, memberRefExpr, {}, {}); + auto *callExpr = CallExpr::createImplicitEmpty(context, memberRefExpr); callExpr->setImplicit(true); callExpr->setType(context.TheEmptyTupleType); @@ -2089,7 +2086,8 @@ void AttributeChecker::visitRequiredAttr(RequiredAttr *attr) { return; } // Only classes can have required constructors. - if (parentTy->getClassOrBoundGenericClass()) { + if (parentTy->getClassOrBoundGenericClass() && + !parentTy->getClassOrBoundGenericClass()->isActor()) { // The constructor must be declared within the class itself. // FIXME: Allow an SDK overlay to add a required initializer to a class // defined in Objective-C @@ -2172,7 +2170,7 @@ static void checkSpecializeAttrRequirements(SpecializeAttr *attr, SmallVector unspecializedParams; - for (auto *paramTy : specializedSig->getGenericParams()) { + for (auto *paramTy : specializedSig.getGenericParams()) { auto canTy = paramTy->getCanonicalType(); if (specializedSig->isCanonicalTypeInContext(canTy) && (!specializedSig->getLayoutConstraint(canTy) || @@ -2181,7 +2179,7 @@ static void checkSpecializeAttrRequirements(SpecializeAttr *attr, } } - unsigned expectedCount = specializedSig->getGenericParams().size(); + unsigned expectedCount = specializedSig.getGenericParams().size(); unsigned gotCount = expectedCount - unspecializedParams.size(); if (expectedCount == gotCount) @@ -2239,7 +2237,8 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { using FloatingRequirementSource = GenericSignatureBuilder::FloatingRequirementSource; Builder.addRequirement(req, reqRepr, - FloatingRequirementSource::forExplicit(reqRepr), + FloatingRequirementSource::forExplicit( + reqRepr->getSeparatorLoc()), nullptr, DC->getParentModule()); return false; }); @@ -2722,9 +2721,10 @@ bool TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator, TypeEraserAttr *attr, ProtocolDecl *protocol) const { - auto &ctx = protocol->getASTContext(); - auto &diags = ctx.Diags; DeclContext *dc = protocol->getDeclContext(); + ModuleDecl *module = dc->getParentModule(); + auto &ctx = module->getASTContext(); + auto &diags = ctx.Diags; Type protocolType = protocol->getDeclaredInterfaceType(); // Get the NominalTypeDecl for the type eraser. @@ -2752,7 +2752,7 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator, } // The type eraser must conform to the annotated protocol - if (!TypeChecker::conformsToProtocol(typeEraser, protocol, dc)) { + if (!TypeChecker::conformsToProtocol(typeEraser, protocol, module)) { diags.diagnose(attr->getLoc(), diag::type_eraser_does_not_conform, typeEraser, protocolType); diags.diagnose(nominalTypeDecl->getLoc(), diag::type_eraser_declared_here); @@ -2777,7 +2777,7 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator, return false; auto genericSignature = init->getGenericSignature(); - auto genericParamType = genericSignature->getInnermostGenericParams().front(); + auto genericParamType = genericSignature.getInnermostGenericParams().front(); // Fow now, only allow one parameter. auto params = init->getParameters(); @@ -2795,26 +2795,21 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator, // type conforming to the annotated protocol. We will check this by // substituting the protocol's Self type for the generic arg and check that // the requirements in the generic signature are satisfied. + auto *module = nominalTypeDecl->getParentModule(); auto baseMap = - typeEraser->getContextSubstitutionMap(nominalTypeDecl->getParentModule(), + typeEraser->getContextSubstitutionMap(module, nominalTypeDecl); QuerySubstitutionMap getSubstitution{baseMap}; - auto subMap = SubstitutionMap::get( - genericSignature, - [&](SubstitutableType *type) -> Type { - if (type->isEqual(genericParamType)) - return protocol->getSelfTypeInContext(); - - return getSubstitution(type); - }, - LookUpConformanceInModule(dc->getParentModule())); // Use invalid 'SourceLoc's to suppress diagnostics. auto result = TypeChecker::checkGenericArguments( - dc, SourceLoc(), SourceLoc(), typeEraser, - genericSignature->getGenericParams(), - genericSignature->getRequirements(), - QuerySubstitutionMap{subMap}); + module, genericSignature.getRequirements(), + [&](SubstitutableType *type) -> Type { + if (type->isEqual(genericParamType)) + return protocol->getSelfTypeInContext(); + + return getSubstitution(type); + }); if (result != RequirementCheckResult::Success) { unviable.push_back( @@ -2915,7 +2910,7 @@ void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { // conforms to the specified protocol. NominalTypeDecl *NTD = DC->getSelfNominalTypeDecl(); SmallVector conformances; - if (!NTD->lookupConformance(DC->getParentModule(), PD, conformances)) { + if (!NTD->lookupConformance(PD, conformances)) { diagnose(attr->getLocation(), diag::implements_attr_protocol_not_conformed_to, NTD->getName(), PD->getName()) @@ -2978,7 +2973,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { if (isa(D)) { // Check for unsupported declarations. auto *context = D->getDeclContext()->getAsDecl(); - if (context && isa(context)) { + if (isa_and_nonnull(context)) { diagnose(attr->getLocation(), diag::property_wrapper_param_not_supported, context->getDescriptiveKind()); @@ -3358,10 +3353,9 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, if (PDC && !PDC->isObjC()) { // Ownership does not make sense in protocols, except for "weak" on // properties of Objective-C protocols. - auto D = var->getASTContext().isSwiftVersionAtLeast(5) - ? diag::ownership_invalid_in_protocols - : diag::ownership_invalid_in_protocols_compat_warning; + auto D = diag::ownership_invalid_in_protocols; Diags.diagnose(attr->getLocation(), D, ownershipKind) + .warnUntilSwiftVersion(5) .fixItRemove(attr->getRange()); attr->setInvalid(); } @@ -3375,6 +3369,8 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, Optional> TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D) { + auto *DC = D->getDeclContext(); + if (auto *VD = dyn_cast(D)) { if (!VD->hasStorage()) return None; @@ -3387,14 +3383,23 @@ TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D) { // Globals and statics are lazily initialized, so they are safe // for potential unavailability. - if (!VD->isStatic() && !VD->getDeclContext()->isModuleScopeContext()) + if (!VD->isStatic() && !DC->isModuleScopeContext()) return diag::availability_stored_property_no_potential; } else if (auto *EED = dyn_cast(D)) { // An enum element with an associated value cannot be potentially // unavailable. - if (EED->hasAssociatedValues()) - return diag::availability_enum_element_no_potential; + if (EED->hasAssociatedValues()) { + auto &ctx = DC->getASTContext(); + auto *SF = DC->getParentSourceFile(); + + if (SF->Kind == SourceFileKind::Interface || + ctx.LangOpts.WarnOnPotentiallyUnavailableEnumCase) { + return diag::availability_enum_element_no_potential_warn; + } else { + return diag::availability_enum_element_no_potential; + } + } } return None; @@ -3549,12 +3554,12 @@ SpecializeAttrTargetDeclRequest::evaluate(Evaluator &evaluator, /// Returns true if the given type conforms to `Differentiable` in the given /// context. If `tangentVectorEqualsSelf` is true, also check whether the given /// type satisfies `TangentVector == Self`. -static bool conformsToDifferentiable(Type type, DeclContext *DC, +static bool conformsToDifferentiable(Type type, ModuleDecl *module, bool tangentVectorEqualsSelf = false) { - auto &ctx = type->getASTContext(); + auto &ctx = module->getASTContext(); auto *differentiableProto = ctx.getProtocol(KnownProtocolKind::Differentiable); - auto conf = TypeChecker::conformsToProtocol(type, differentiableProto, DC); + auto conf = TypeChecker::conformsToProtocol(type, differentiableProto, module); if (conf.isInvalid()) return false; if (!tangentVectorEqualsSelf) @@ -3565,7 +3570,8 @@ static bool conformsToDifferentiable(Type type, DeclContext *DC, IndexSubset *TypeChecker::inferDifferentiabilityParameters( AbstractFunctionDecl *AFD, GenericEnvironment *derivativeGenEnv) { - auto &ctx = AFD->getASTContext(); + auto *module = AFD->getParentModule(); + auto &ctx = module->getASTContext(); auto *functionType = AFD->getInterfaceType()->castTo(); auto numUncurriedParams = functionType->getNumParams(); if (auto *resultFnType = @@ -3588,7 +3594,7 @@ IndexSubset *TypeChecker::inferDifferentiabilityParameters( if (paramType->isExistentialType()) return false; // Return true if the type conforms to `Differentiable`. - return conformsToDifferentiable(paramType, AFD); + return conformsToDifferentiable(paramType, module); }; // Get all parameter types. @@ -3621,7 +3627,8 @@ static IndexSubset *computeDifferentiabilityParameters( ArrayRef parsedDiffParams, AbstractFunctionDecl *function, GenericEnvironment *derivativeGenEnv, StringRef attrName, SourceLoc attrLoc) { - auto &ctx = function->getASTContext(); + auto *module = function->getParentModule(); + auto &ctx = module->getASTContext(); auto &diags = ctx.Diags; // Get function type and parameters. @@ -3648,7 +3655,7 @@ static IndexSubset *computeDifferentiabilityParameters( selfType = derivativeGenEnv->mapTypeIntoContext(selfType); else selfType = function->mapTypeIntoContext(selfType); - if (!conformsToDifferentiable(selfType, function)) { + if (!conformsToDifferentiable(selfType, module)) { diags .diagnose(attrLoc, diag::diff_function_no_parameters, function->getName()) @@ -4188,8 +4195,6 @@ resolveDifferentiableAttrOriginalFunction(DifferentiableAttr *attr) { auto *D = attr->getOriginalDeclaration(); assert(D && "Original declaration should be resolved by parsing/deserialization"); - auto &ctx = D->getASTContext(); - auto &diags = ctx.Diags; auto *original = dyn_cast(D); if (auto *asd = dyn_cast(D)) { // If `@differentiable` attribute is declared directly on a @@ -4213,7 +4218,7 @@ resolveDifferentiableAttrOriginalFunction(DifferentiableAttr *attr) { original = nullptr; // Diagnose if original `AbstractFunctionDecl` could not be resolved. if (!original) { - diagnoseAndRemoveAttr(diags, D, attr, diag::invalid_decl_attribute, attr); + diagnoseAndRemoveAttr(D, attr, diag::invalid_decl_attribute, attr); attr->setInvalid(); return nullptr; } @@ -4314,7 +4319,8 @@ bool resolveDifferentiableAttrDerivativeGenericSignature( // Add requirement to generic signature builder. builder.addRequirement( - req, reqRepr, FloatingRequirementSource::forExplicit(reqRepr), + req, reqRepr, FloatingRequirementSource::forExplicit( + reqRepr->getSeparatorLoc()), nullptr, original->getModuleContext()); return false; }); @@ -4484,7 +4490,7 @@ IndexSubset *DifferentiableAttributeTypeCheckRequest::evaluate( if (diagnoseDynamicSelfResult) { // Diagnose class initializers in non-final classes. if (isa(original)) { - if (!classDecl->isFinal()) { + if (!classDecl->isSemanticallyFinal()) { diags.diagnose( attr->getLocation(), diag::differentiable_attr_nonfinal_class_init_unsupported, @@ -4510,9 +4516,7 @@ IndexSubset *DifferentiableAttributeTypeCheckRequest::evaluate( if (resolveDifferentiableAttrDerivativeGenericSignature(attr, original, derivativeGenSig)) return nullptr; - GenericEnvironment *derivativeGenEnv = nullptr; - if (derivativeGenSig) - derivativeGenEnv = derivativeGenSig->getGenericEnvironment(); + auto *derivativeGenEnv = derivativeGenSig.getGenericEnvironment(); // Compute the derivative function type. auto originalFnRemappedTy = originalFnTy; @@ -4543,8 +4547,7 @@ IndexSubset *DifferentiableAttributeTypeCheckRequest::evaluate( {getterDecl, resolvedDiffParamIndices}, newAttr); // Reject duplicate `@differentiable` attributes. if (!insertion.second) { - diagnoseAndRemoveAttr(diags, D, attr, - diag::differentiable_attr_duplicate); + diagnoseAndRemoveAttr(D, attr, diag::differentiable_attr_duplicate); diags.diagnose(insertion.first->getSecond()->getLocation(), diag::differentiable_attr_duplicate_note); return nullptr; @@ -4560,7 +4563,7 @@ IndexSubset *DifferentiableAttributeTypeCheckRequest::evaluate( auto insertion = ctx.DifferentiableAttrs.try_emplace({D, resolvedDiffParamIndices}, attr); if (!insertion.second && insertion.first->getSecond() != attr) { - diagnoseAndRemoveAttr(diags, D, attr, diag::differentiable_attr_duplicate); + diagnoseAndRemoveAttr(D, attr, diag::differentiable_attr_duplicate); diags.diagnose(insertion.first->getSecond()->getLocation(), diag::differentiable_attr_duplicate_note); return nullptr; @@ -4786,7 +4789,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, if (diagnoseDynamicSelfResult) { // Diagnose class initializers in non-final classes. if (isa(originalAFD)) { - if (!classDecl->isFinal()) { + if (!classDecl->isSemanticallyFinal()) { diags.diagnose(attr->getLocation(), diag::derivative_attr_nonfinal_class_init_unsupported, classDecl->getDeclaredInterfaceType()); @@ -5141,7 +5144,7 @@ static bool checkLinearityParameters( SmallVector linearParams, GenericEnvironment *derivativeGenEnv, ModuleDecl *module, ArrayRef parsedLinearParams, SourceLoc attrLoc) { - auto &ctx = originalAFD->getASTContext(); + auto &ctx = module->getASTContext(); auto &diags = ctx.Diags; // Check that linearity parameters have allowed types. @@ -5157,7 +5160,7 @@ static bool checkLinearityParameters( parsedLinearParams.empty() ? attrLoc : parsedLinearParams[i].getLoc(); // Parameter must conform to `Differentiable` and satisfy // `Self == Self.TangentVector`. - if (!conformsToDifferentiable(linearParamType, originalAFD, + if (!conformsToDifferentiable(linearParamType, module, /*tangentVectorEqualsSelf*/ true)) { diags.diagnose(loc, diag::transpose_attr_invalid_linearity_parameter_or_result, @@ -5204,6 +5207,7 @@ doTransposeStaticAndInstanceSelfTypesMatch(AnyFunctionType *transposeType, void AttributeChecker::visitTransposeAttr(TransposeAttr *attr) { auto *transpose = cast(D); + auto *module = transpose->getParentModule(); auto originalName = attr->getOriginalFunctionName(); auto *transposeInterfaceType = transpose->getInterfaceType()->castTo(); @@ -5269,7 +5273,7 @@ void AttributeChecker::visitTransposeAttr(TransposeAttr *attr) { if (expectedOriginalResultType->hasTypeParameter()) expectedOriginalResultType = transpose->mapTypeIntoContext( expectedOriginalResultType); - if (!conformsToDifferentiable(expectedOriginalResultType, transpose, + if (!conformsToDifferentiable(expectedOriginalResultType, module, /*tangentVectorEqualsSelf*/ true)) { diagnoseAndRemoveAttr( attr, diag::transpose_attr_invalid_linearity_parameter_or_result, @@ -5391,22 +5395,6 @@ void AttributeChecker::visitTransposeAttr(TransposeAttr *attr) { attr->setParameterIndices(linearParamIndices); } -void AttributeChecker::visitAsyncHandlerAttr(AsyncHandlerAttr *attr) { - if (!Ctx.LangOpts.EnableExperimentalAsyncHandler) { - diagnoseAndRemoveAttr(attr, diag::asynchandler_removed); - return; - } - - auto func = dyn_cast(D); - if (!func) { - diagnoseAndRemoveAttr(attr, diag::asynchandler_non_func); - return; - } - - // Trigger the request to check for @asyncHandler. - (void)func->isAsyncHandler(); -} - void AttributeChecker::visitActorAttr(ActorAttr *attr) { auto classDecl = dyn_cast(D); if (!classDecl) @@ -5415,42 +5403,65 @@ void AttributeChecker::visitActorAttr(ActorAttr *attr) { (void)classDecl->isActor(); } -void AttributeChecker::visitActorIndependentAttr(ActorIndependentAttr *attr) { - // @actorIndependent can be applied to global and static/class variables - // that do not have storage. +void AttributeChecker::visitDistributedActorAttr(DistributedActorAttr *attr) { auto dc = D->getDeclContext(); - if (auto var = dyn_cast(D)) { - // @actorIndependent can not be applied to mutable stored properties, unless if - // the 'unsafe' option was specified - if (var->hasStorage()) { - switch (attr->getKind()) { - case ActorIndependentKind::Safe: - if (var->isLet()) - break; - diagnoseAndRemoveAttr(attr, diag::actorindependent_mutable_storage); - return; + // distributed can be applied to actor class definitions and async functions + if (auto varDecl = dyn_cast(D)) { + // distributed can not be applied to stored properties + diagnoseAndRemoveAttr(attr, diag::distributed_actor_property); + return; + } - case ActorIndependentKind::Unsafe: - break; - } + // distributed can only be declared on an `actor` + if (auto classDecl = dyn_cast(D)) { + if (!classDecl->isActor()) { + diagnoseAndRemoveAttr(attr, diag::distributed_actor_not_actor); + return; + } else { + // good: `distributed actor` + return; } + } else if (dyn_cast(D) || dyn_cast(D)) { + diagnoseAndRemoveAttr( + attr, diag::distributed_actor_func_not_in_distributed_actor); + return; + } - // @actorIndependent can not be applied to local properties. - if (dc->isLocalContext()) { - diagnoseAndRemoveAttr(attr, diag::actorindependent_local_var); + if (auto funcDecl = dyn_cast(D)) { + // distributed functions must not be static + if (funcDecl->isStatic()) { + diagnoseAndRemoveAttr(attr, diag::distributed_actor_func_static); return; } - // If this is a static or global variable, we're all set. - if (dc->isModuleScopeContext() || - (dc->isTypeContext() && var->isStatic())) { + // distributed func cannot be simultaneously nonisolated + if (auto nonisolated = + funcDecl->getAttrs().getAttribute()) { + diagnoseAndRemoveAttr(nonisolated, + diag::distributed_actor_func_nonisolated, + funcDecl->getName()); return; } - } - if (auto VD = dyn_cast(D)) { - (void)getActorIsolation(VD); + // distributed func must be declared inside an distributed actor + if (dc->getSelfClassDecl() && + !dc->getSelfClassDecl()->isDistributedActor()) { + diagnoseAndRemoveAttr( + attr, diag::distributed_actor_func_not_in_distributed_actor); + return; + } else if (auto protoDecl = dc->getSelfProtocolDecl()){ + if (!protoDecl->inheritsFromDistributedActor()) { + // TODO: could suggest adding `: DistributedActor` to the protocol as well + diagnoseAndRemoveAttr( + attr, diag::distributed_actor_func_not_in_distributed_actor); + return; + } + } else if (dc->getSelfStructDecl() || dc->getSelfEnumDecl()) { + diagnoseAndRemoveAttr( + attr, diag::distributed_actor_func_not_in_distributed_actor); + return; + } } } @@ -5458,24 +5469,30 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) { // 'nonisolated' can be applied to global and static/class variables // that do not have storage. auto dc = D->getDeclContext(); + if (auto var = dyn_cast(D)) { - // 'nonisolated' can only be applied to 'let' stored properties. - // Those must be Sendable. + // stored properties have limitations as to when they can be nonisolated. if (var->hasStorage()) { - if (!var->isLet()) { - diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage); + auto nominal = dyn_cast(dc); + + // 'nonisolated' can not be applied to stored properties inside + // distributed actors. Attempts of nonisolated access would be + // cross-actor, and that means they might be accessing on a remote actor, + // in which case the stored property storage does not exist. + if (nominal && nominal->isDistributedActor()) { + diagnoseAndRemoveAttr(attr, + diag::nonisolated_distributed_actor_storage); return; } - // nonisolated lets must have Sendable type. - if (shouldDiagnoseNonSendableViolations(dc->getASTContext().LangOpts) && - !isSendableType(dc, var->getType())) { - var->diagnose( - diag::non_sendable_nonisolated_let, var->getName(), var->getType()); + // 'nonisolated' can not be applied to mutable stored properties. + if (var->supportsMutation()) { + diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage); + return; } } - // @actorIndependent can not be applied to local properties. + // nonisolated can not be applied to local properties. if (dc->isLocalContext()) { diagnoseAndRemoveAttr(attr, diag::nonisolated_local_var); return; @@ -5493,6 +5510,16 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) { } } +void AttributeChecker::visitDistributedActorIndependentAttr(DistributedActorIndependentAttr *attr) { + /// user-inaccessible _distributedActorIndependent can only be applied to let properties + if (auto var = dyn_cast(D)) { + if (!var->isLet()) { + diagnoseAndRemoveAttr(attr, diag::distributed_actor_independent_property_must_be_let); + return; + } + } +} + void AttributeChecker::visitGlobalActorAttr(GlobalActorAttr *attr) { auto nominal = dyn_cast(D); if (!nominal) @@ -5502,20 +5529,6 @@ void AttributeChecker::visitGlobalActorAttr(GlobalActorAttr *attr) { } void AttributeChecker::visitAsyncAttr(AsyncAttr *attr) { - if (isa(D)) { - D->getASTContext().Diags.diagnose( - attr->getLocation(), diag::async_let_is_spawn_let) - .fixItReplace(attr->getRange(), "spawn"); - - visitAsyncOrSpawnAttr(attr); - } -} - -void AttributeChecker::visitSpawnAttr(SpawnAttr *attr) { - visitAsyncOrSpawnAttr(attr); -} - -void AttributeChecker::visitAsyncOrSpawnAttr(DeclAttribute *attr) { auto var = dyn_cast(D); if (!var) return; @@ -5526,7 +5539,7 @@ void AttributeChecker::visitAsyncOrSpawnAttr(DeclAttribute *attr) { // "Async" modifier can only be applied to local declarations. if (!patternBinding->getDeclContext()->isLocalContext()) { - diagnoseAndRemoveAttr(attr, diag::spawn_let_not_local); + diagnoseAndRemoveAttr(attr, diag::async_let_not_local); return; } @@ -5547,21 +5560,21 @@ void AttributeChecker::visitAsyncOrSpawnAttr(DeclAttribute *attr) { // Each entry must bind at least one named variable, so that there is // something to "await". if (!foundAnyVariable) { - diagnose(pattern->getLoc(), diag::spawn_let_no_variables); + diagnose(pattern->getLoc(), diag::async_let_no_variables); attr->setInvalid(); return; } // Async can only be used on an "async let". if (!isLet && !diagnosedVar) { - diagnose(patternBinding->getLoc(), diag::spawn_not_let) + diagnose(patternBinding->getLoc(), diag::async_not_let) .fixItReplace(patternBinding->getLoc(), "let"); diagnosedVar = true; } // Each pattern entry must have an initializer expression. if (patternBinding->getEqualLoc(index).isInvalid()) { - diagnose(pattern->getLoc(), diag::spawn_let_not_initialized); + diagnose(pattern->getLoc(), diag::async_let_not_initialized); attr->setInvalid(); return; } @@ -5632,10 +5645,6 @@ class ClosureAttributeChecker // Nothing else to check. } - void visitActorIndependentAttr(ActorIndependentAttr *attr) { - // Nothing else to check. - } - void visitCustomAttr(CustomAttr *attr) { // Check whether this custom attribute is the global actor attribute. auto globalActorAttr = evaluateOrDefault( @@ -5669,182 +5678,157 @@ void TypeChecker::checkClosureAttributes(ClosureExpr *closure) { } } -void AttributeChecker::visitCompletionHandlerAsyncAttr( - CompletionHandlerAsyncAttr *attr) { - if (AbstractFunctionDecl *AFD = dyn_cast(D)) - AFD->getAsyncAlternative(); +static bool renameCouldMatch(const ValueDecl *original, + const ValueDecl *candidate, + bool originalIsObjCVisible, + AccessLevel minAccess) { + // Can't match itself + if (original == candidate) + return false; + + // Kinds have to match, but we want to allow eg. an accessor to match + // a function + if (candidate->getKind() != original->getKind() && + !(isa(candidate) && isa(original))) + return false; + + // Instance can't match static/class function + if (candidate->isInstanceMember() != original->isInstanceMember()) + return false; + + // If the original is ObjC visible then the rename must be as well + if (originalIsObjCVisible && + !objc_translation::isVisibleToObjC(candidate, minAccess)) + return false; + + // @available is intended for public interfaces, so an implementation-only + // decl shouldn't match + if (candidate->getAttrs().hasAttribute()) + return false; + + return true; } -AbstractFunctionDecl *AsyncAlternativeRequest::evaluate( - Evaluator &evaluator, AbstractFunctionDecl *attachedFunctionDecl) const { - auto attr = attachedFunctionDecl->getAttrs() - .getAttribute(); - if (!attr) - return nullptr; +static bool parametersMatch(const AbstractFunctionDecl *a, + const AbstractFunctionDecl *b) { + auto aParams = a->getParameters(); + auto bParams = b->getParameters(); - if (attr->AsyncFunctionDecl) - return attr->AsyncFunctionDecl; - - auto &Diags = attachedFunctionDecl->getASTContext().Diags; - // Check phases: - // 1. Attached function shouldn't be async and should have enough args - // to have a completion handler - // 2. Completion handler should be a function type that returns void. - // Completion handler type should be escaping and not autoclosure - // 3. Find functionDecl that the attachedFunction is being mapped to - // - Find all with the same name - // - Keep any that are async - // - Do some sanity checking on types - - // Phase 1: Typecheck the function the attribute is attached to - if (attachedFunctionDecl->hasAsync()) { - Diags.diagnose(attr->getLocation(), - diag::attr_completion_handler_async_handler_not_func, attr); - Diags.diagnose(attachedFunctionDecl->getAsyncLoc(), - diag::note_attr_function_declared_async); - return nullptr; + if (aParams->size() != bParams->size()) + return false; + + for (auto index : indices(*aParams)) { + auto aParamType = aParams->get(index)->getType(); + auto bParamType = bParams->get(index)->getType(); + if (!aParamType->matchesParameter(bParamType, TypeMatchOptions())) + return false; } + return true; +} - const ParameterList *attachedFunctionParams = - attachedFunctionDecl->getParameters(); - assert(attachedFunctionParams && "Attached function has no parameter list"); - if (attachedFunctionParams->size() == 0) { - Diags.diagnose(attr->getLocation(), - diag::attr_completion_handler_async_handler_not_func, attr); +ValueDecl *RenamedDeclRequest::evaluate(Evaluator &evaluator, + const ValueDecl *attached, + const AvailableAttr *attr) const { + if (!attached || !attr) return nullptr; - } - size_t completionHandlerIndex = attr->CompletionHandlerIndexLoc.isValid() - ? attr->CompletionHandlerIndex - : attachedFunctionParams->size() - 1; - if (attachedFunctionParams->size() < completionHandlerIndex) { - Diags.diagnose(attr->CompletionHandlerIndexLoc, - diag::attr_completion_handler_async_handler_out_of_range); + + if (attr->RenameDecl) + return attr->RenameDecl; + + if (attr->Rename.empty()) return nullptr; - } - // Phase 2: Typecheck the completion handler - const ParamDecl *completionHandlerParamDecl = - attachedFunctionParams->get(completionHandlerIndex); - { - AnyFunctionType *handlerType = - completionHandlerParamDecl->getType()->getAs(); - if (!handlerType) { - Diags.diagnose(attr->getLocation(), - diag::attr_completion_handler_async_handler_not_func, - attr); - Diags - .diagnose( - completionHandlerParamDecl->getTypeRepr()->getLoc(), - diag::note_attr_completion_handler_async_type_is_not_function, - completionHandlerParamDecl->getType()) - .highlight( - completionHandlerParamDecl->getTypeRepr()->getSourceRange()); - return nullptr; - } + auto attachedContext = attached->getDeclContext(); + auto parsedName = parseDeclName(attr->Rename); + auto nameRef = parsedName.formDeclNameRef(attached->getASTContext()); - auto handlerTypeRepr = - dyn_cast(completionHandlerParamDecl->getTypeRepr()); - const TypeAttributes *handlerTypeAttrs = nullptr; - if (handlerTypeRepr) - handlerTypeAttrs = &handlerTypeRepr->getAttrs(); - - const bool missingVoid = !handlerType->getResult()->isVoid(); - const bool hasAutoclosure = - handlerTypeAttrs ? handlerTypeAttrs->has(TAK_autoclosure) : false; - const bool hasEscaping = - handlerTypeAttrs ? handlerTypeAttrs->has(TAK_escaping) : false; - const bool hasError = missingVoid | hasAutoclosure | !hasEscaping; - - if (hasError) { - Diags.diagnose(attr->getLocation(), - diag::attr_completion_handler_async_handler_not_func, - attr); - - if (missingVoid) - Diags - .diagnose(completionHandlerParamDecl->getLoc(), - diag::note_attr_completion_function_must_return_void) - .highlight( - completionHandlerParamDecl->getTypeRepr()->getSourceRange()); - - if (!hasEscaping) - Diags - .diagnose(completionHandlerParamDecl->getLoc(), - diag::note_attr_completion_handler_async_handler_attr_req, - true, "escaping") - .highlight( - completionHandlerParamDecl->getTypeRepr()->getSourceRange()); - - if (hasAutoclosure) - Diags.diagnose( - handlerTypeAttrs->getLoc(TAK_autoclosure), - diag::note_attr_completion_handler_async_handler_attr_req, false, - "autoclosure"); + // Handle types separately + if (isa(attached)) { + if (!parsedName.ContextName.empty()) return nullptr; - } + + SmallVector lookupResults; + attachedContext->lookupQualified(attachedContext->getParentModule(), + nameRef.withoutArgumentLabels(), + NL_OnlyTypes, lookupResults); + if (lookupResults.size() == 1) + return lookupResults[0]; + return nullptr; } - // Phase 3: Find mapped async function - { - // Get the list of candidates based on the name - // Grab all functions that are async - // TODO: Sanity check types -- we just use the DeclName for now - // - Need a throwing decl if the completion handler takes a Result type - // containing an error, or if it takes a tuple containing an optional - // error. - // Find a declref that works. - // Get list of candidates based on the name. - // The correct candidate will need to be async. - // - // TODO: Implement the type matching stuff eventually - // If the completion handler takes a single type, then we find the async - // function that returns just that type. (easy case) - // - // If the completion handler takes a result type consisting of a type and - // an error, the async function should be throwing and return that type. - // (easy-ish case) - // - // If the completion handler takes an optional type and an optional Error - // type, this could map to either of these two. The intent isn't clear. - // - func foo() async throws -> Int - // - func foo() async throws -> Int? - // This case is ambiguous, so we will report an error. - // - // If the completion handler takes multiple types, the async function - // should return all of those types in a tuple - - SmallVector allCandidates; - lookupReplacedDecl(attr->AsyncFunctionName, attr, attachedFunctionDecl, - allCandidates); - SmallVector candidates; - candidates.reserve(allCandidates.size()); - for (ValueDecl *candidate : allCandidates) { - AbstractFunctionDecl *funcDecl = - dyn_cast(candidate); - if (!funcDecl) // Only consider functions + auto minAccess = AccessLevel::Private; + if (attached->getModuleContext()->isExternallyConsumed()) + minAccess = AccessLevel::Public; + bool attachedIsObjcVisible = + objc_translation::isVisibleToObjC(attached, minAccess); + + SmallVector lookupResults; + SmallVector asyncResults; + lookupReplacedDecl(nameRef, attr, attached, lookupResults); + + ValueDecl *renamedDecl = nullptr; + auto attachedFunc = dyn_cast(attached); + for (auto candidate : lookupResults) { + // If the name is a getter or setter, grab the underlying accessor (if any) + if (parsedName.IsGetter || parsedName.IsSetter) { + auto *VD = dyn_cast(candidate); + if (!VD) continue; - if (!funcDecl->hasAsync()) // only consider async functions + + candidate = VD->getAccessor(parsedName.IsGetter ? AccessorKind::Get : + AccessorKind::Set); + if (!candidate) continue; - candidates.push_back(funcDecl); } - if (candidates.empty()) { - Diags.diagnose(attr->AsyncFunctionNameLoc, - diag::attr_completion_handler_async_no_suitable_function, - attr->AsyncFunctionName); - return nullptr; - } else if (candidates.size() > 1) { - Diags.diagnose(attr->AsyncFunctionNameLoc, - diag::attr_completion_handler_async_ambiguous_function, - attr, attr->AsyncFunctionName); - - for (AbstractFunctionDecl *candidate : candidates) { - Diags.diagnose(candidate->getLoc(), diag::decl_declared_here, - candidate->getName()); + if (!renameCouldMatch(attached, candidate, attachedIsObjcVisible, + minAccess)) + continue; + + if (auto *candidateFunc = dyn_cast(candidate)) { + // Require both functions to be async/not. Async alternatives are handled + // below if there's no other matches + if (attachedFunc->hasAsync() != candidateFunc->hasAsync()) { + if (candidateFunc->hasAsync()) + asyncResults.push_back(candidateFunc); + continue; } - return nullptr; + + // Require matching parameters for functions, unless there's only a single + // match + if (lookupResults.size() > 1 && + !parametersMatch(attachedFunc, candidateFunc)) + continue; } - return candidates.front(); + // Do not match if there are any duplicates + if (renamedDecl) { + renamedDecl = nullptr; + break; + } + renamedDecl = candidate; } + + // Try to match up an async alternative instead (ie. one where the + // completion handler has been removed). + if (!renamedDecl && !asyncResults.empty()) { + for (AbstractFunctionDecl *candidate : asyncResults) { + Optional completionHandler = + attachedFunc->findPotentialCompletionHandlerParam(candidate); + if (!completionHandler) + continue; + + // TODO: Check the result of the async function matches the parameters + // of the completion handler? + + // Do not match if there are any duplicates + if (renamedDecl) { + renamedDecl = nullptr; + break; + } + renamedDecl = candidate; + } + } + + return renamedDecl; } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 126e5b7e3ca33..ee1b3a1c4e807 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "TypeCheckAvailability.h" +#include "TypeCheckConcurrency.h" #include "TypeChecker.h" #include "TypeCheckObjC.h" #include "MiscDiagnostics.h" @@ -113,6 +114,8 @@ static void forEachOuterDecl(DeclContext *DC, Fn fn) { case DeclContextKind::Initializer: if (auto *PBI = dyn_cast(DC)) fn(PBI->getBinding()); + else if (auto *I = dyn_cast(DC)) + fn(I->getWrappedVar()); break; case DeclContextKind::SubscriptDecl: @@ -400,13 +403,15 @@ class TypeRefinementContextBuilder : private ASTWalker { // The potential versions in the declaration are constrained by both // the declared availability of the declaration and the potential versions // of its lexical context. - AvailabilityContext DeclInfo = + AvailabilityContext ExplicitDeclInfo = swift::AvailabilityInference::availableRange(D, Context); + AvailabilityContext DeclInfo = ExplicitDeclInfo; DeclInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo()); TypeRefinementContext *NewTRC = TypeRefinementContext::createForDecl(Context, D, getCurrentTRC(), DeclInfo, + ExplicitDeclInfo, refinementSourceRangeForDecl(D)); // Record the TRC for this storage declaration so that @@ -643,9 +648,14 @@ class TypeRefinementContextBuilder : private ASTWalker { TypeRefinementContext *StartingTRC = getCurrentTRC(); + // Tracks if we're refining for availability or unavailability. + Optional isUnavailability = None; + for (StmtConditionElement Element : Cond) { TypeRefinementContext *CurrentTRC = getCurrentTRC(); AvailabilityContext CurrentInfo = CurrentTRC->getAvailabilityInfo(); + AvailabilityContext CurrentExplicitInfo = + CurrentTRC->getExplicitAvailabilityInfo(); // If the element is not a condition, walk it in the current TRC. if (Element.getKind() != StmtConditionElement::CK_Availability) { @@ -664,10 +674,25 @@ class TypeRefinementContextBuilder : private ASTWalker { // condition elements following it. auto *Query = Element.getAvailability(); + if (isUnavailability == None) { + isUnavailability = Query->isUnavailability(); + } else if (isUnavailability != Query->isUnavailability()) { + // Mixing availability with unavailability in the same statement will + // cause the false flow's version range to be ambiguous. Report it. + // + // Technically we can support this by not refining ambiguous flows, + // but there are currently no legitimate cases where one would have + // to mix availability with unavailability. + Context.Diags.diagnose(Query->getLoc(), + diag::availability_cannot_be_mixed); + break; + } + // If this query expression has no queries, we will not introduce a new // refinement context. We do not diagnose here: a diagnostic will already // have been emitted by the parser. - if (Query->getQueries().empty()) + // For #unavailable, empty queries are valid as wildcards are implied. + if (!Query->isUnavailability() && Query->getQueries().empty()) continue; AvailabilitySpec *Spec = bestActiveSpecForQuery(Query); @@ -676,10 +701,9 @@ class TypeRefinementContextBuilder : private ASTWalker { // so rather than refining, emit a diagnostic and just use the current // TRC. Context.Diags.diagnose( - Query->getLoc(), - diag::availability_query_required_for_platform, + Query->getLoc(), diag::availability_query_required_for_platform, platformString(targetPlatform(Context.LangOpts))); - + continue; } @@ -708,24 +732,23 @@ class TypeRefinementContextBuilder : private ASTWalker { continue; } - - // If the version range for the current TRC is completely contained in - // the range for the spec, then a version query can never be false, so the - // spec is useless. If so, report this. - if (CurrentInfo.isContainedIn(NewConstraint)) { + // If the explicitly-specified (via #availability) version range for the + // current TRC is completely contained in the range for the spec, then + // a version query can never be false, so the spec is useless. + // If so, report this. + if (CurrentExplicitInfo.isContainedIn(NewConstraint)) { + // Unavailability refinements are always "useless" from a symbol + // availability point of view, so only useless availability specs are + // reported. + if (isUnavailability.getValue()) { + continue; + } DiagnosticEngine &Diags = Context.Diags; - // Some availability checks will always pass because the minimum - // deployment target guarantees they will never be false. We don't - // diagnose these checks as useless because the source file may - // be shared with other projects/targets having older deployment - // targets. We don't currently have a mechanism for the user to - // suppress these warnings (for example, by indicating when the - // required compatibility version is different than the deployment - // target). if (CurrentTRC->getReason() != TypeRefinementContext::Reason::Root) { PlatformKind BestPlatform = targetPlatform(Context.LangOpts); auto *PlatformSpec = dyn_cast(Spec); + // If possible, try to report the diagnostic in terms for the // platform the user uttered in the '#available()'. For a platform // that inherits availability from another platform it may be @@ -738,7 +761,9 @@ class TypeRefinementContextBuilder : private ASTWalker { Diags.diagnose(CurrentTRC->getIntroductionLoc(), diag::availability_query_useless_enclosing_scope_here); } + } + if (CurrentInfo.isContainedIn(NewConstraint)) { // No need to actually create the refinement context if we know it is // useless. continue; @@ -773,8 +798,18 @@ class TypeRefinementContextBuilder : private ASTWalker { FalseRefinement = FalseFlow; } + auto makeResult = + [isUnavailability](Optional TrueRefinement, + Optional FalseRefinement) { + if (isUnavailability.hasValue() && isUnavailability.getValue()) { + // If this is an unavailability check, invert the result. + return std::make_pair(FalseRefinement, TrueRefinement); + } + return std::make_pair(TrueRefinement, FalseRefinement); + }; + if (NestedCount == 0) - return std::make_pair(None, FalseRefinement); + return makeResult(None, FalseRefinement); TypeRefinementContext *NestedTRC = getCurrentTRC(); while (NestedCount-- > 0) @@ -782,7 +817,7 @@ class TypeRefinementContextBuilder : private ASTWalker { assert(getCurrentTRC() == StartingTRC); - return std::make_pair(NestedTRC->getAvailabilityInfo(), FalseRefinement); + return makeResult(NestedTRC->getAvailabilityInfo(), FalseRefinement); } /// Return the best active spec for the target platform or nullptr if no @@ -798,7 +833,8 @@ class TypeRefinementContextBuilder : private ASTWalker { continue; } - auto *VersionSpec = dyn_cast(Spec); + auto *VersionSpec = + dyn_cast(Spec); if (!VersionSpec) continue; @@ -821,7 +857,15 @@ class TypeRefinementContextBuilder : private ASTWalker { // If we have reached this point, we found no spec for our target, so // we return the other spec ('*'), if we found it, or nullptr, if not. - return FoundOtherSpec; + if (FoundOtherSpec) { + return FoundOtherSpec; + } else if (available->isUnavailability()) { + // For #unavailable, imply the presence of a wildcard. + SourceLoc Loc = available->getRParenLoc(); + return new (Context) OtherPlatformAvailabilitySpec(Loc); + } else { + return nullptr; + } } /// Return the availability context for the given spec. @@ -872,6 +916,25 @@ void TypeChecker::buildTypeRefinementContextHierarchy(SourceFile &SF) { } } +void TypeChecker::buildTypeRefinementContextHierarchyDelayed(SourceFile &SF, AbstractFunctionDecl *AFD) { + // If there's no TRC for the file, we likely don't want this one either. + // RootTRC is not set when availability checking is disabled. + TypeRefinementContext *RootTRC = SF.getTypeRefinementContext(); + if(!RootTRC) + return; + + if (AFD->getBodyKind() != AbstractFunctionDecl::BodyKind::Unparsed) + return; + + // Parse the function body. + AFD->getBody(/*canSynthesize=*/true); + + // Build the refinement context for the function body. + ASTContext &Context = SF.getASTContext(); + TypeRefinementContextBuilder Builder(RootTRC, Context); + Builder.build(AFD); +} + TypeRefinementContext * TypeChecker::getOrBuildTypeRefinementContext(SourceFile *SF) { TypeRefinementContext *TRC = SF->getTypeRefinementContext(); @@ -1617,6 +1680,47 @@ void TypeChecker::diagnosePotentialOpaqueTypeUnavailability( fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context); } +static void diagnosePotentialConcurrencyUnavailability( + SourceRange ReferenceRange, const DeclContext *ReferenceDC, + const UnavailabilityReason &Reason) { + ASTContext &Context = ReferenceDC->getASTContext(); + + auto RequiredRange = Reason.getRequiredOSVersionRange(); + { + auto Err = + Context.Diags.diagnose( + ReferenceRange.Start, + diag::availability_concurrency_only_version_newer, + prettyPlatformString(targetPlatform(Context.LangOpts)), + Reason.getRequiredOSVersionRange().getLowerEndpoint()); + + // Direct a fixit to the error if an existing guard is nearly-correct + if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange, + ReferenceDC, + RequiredRange, Context, Err)) + return; + } + fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context); +} + +void TypeChecker::checkConcurrencyAvailability(SourceRange ReferenceRange, + const DeclContext *ReferenceDC) { + // Check the availability of concurrency runtime support. + ASTContext &ctx = ReferenceDC->getASTContext(); + if (ctx.LangOpts.DisableAvailabilityChecking) + return; + + auto runningOS = + TypeChecker::overApproximateAvailabilityAtLocation( + ReferenceRange.Start, ReferenceDC); + auto availability = ctx.getBackDeployedConcurrencyAvailability(); + if (!runningOS.isContainedIn(availability)) { + diagnosePotentialConcurrencyUnavailability( + ReferenceRange, ReferenceDC, + UnavailabilityReason::requiresVersionRange(availability.getOSVersion())); + } +} + void TypeChecker::diagnosePotentialUnavailability( const ValueDecl *D, SourceRange ReferenceRange, const DeclContext *ReferenceDC, @@ -1751,7 +1855,7 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag, SourceRange referenceRange, const ValueDecl *renamedDecl, const AvailableAttr *attr, - const ApplyExpr *call) { + const Expr *call) { if (isa(renamedDecl)) return; @@ -1771,25 +1875,25 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag, auto &ctx = renamedDecl->getASTContext(); SourceManager &sourceMgr = ctx.SourceMgr; - if (parsed.isInstanceMember()) { + auto *CE = dyn_cast_or_null(call); + if (!CE) + return; + // Replace the base of the call with the "self argument". // We can only do a good job with the fix-it if we have the whole call // expression. // FIXME: Should we be validating the ContextName in some way? - if (!call || !isa(call)) - return; - unsigned selfIndex = parsed.SelfIndex.getValue(); const Expr *selfExpr = nullptr; SourceLoc removeRangeStart; SourceLoc removeRangeEnd; - auto *argExpr = call->getArg(); + auto *argExpr = CE->getArg(); auto argList = getOriginalArgumentList(argExpr); size_t numElementsWithinParens = argList.args.size(); - numElementsWithinParens -= argList.hasTrailingClosure; + numElementsWithinParens -= argList.getNumTrailingClosures(); if (selfIndex >= numElementsWithinParens) return; @@ -1882,55 +1986,91 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag, selfReplace += base; if (needsParens) selfReplace.push_back(')'); + selfReplace.push_back('.'); selfReplace += parsed.BaseName; - diag.fixItReplace(call->getFn()->getSourceRange(), selfReplace); + + diag.fixItReplace(CE->getFn()->getSourceRange(), selfReplace); if (!parsed.isPropertyAccessor()) diag.fixItRemoveChars(removeRangeStart, removeRangeEnd); // Continue on to diagnose any argument label renames. - } else if (parsed.BaseName == "init" && - call && isa(call)) { + } else if (parsed.BaseName == "init" && isa_and_nonnull(call)) { + auto *CE = cast(call); + // For initializers, replace with a "call" of the context type...but only // if we know we're doing a call (rather than a first-class reference). if (parsed.isMember()) { - diag.fixItReplace(call->getFn()->getSourceRange(), parsed.ContextName); - - } else if (auto *dotCall = dyn_cast(call->getFn())) { + diag.fixItReplace(CE->getFn()->getSourceRange(), parsed.ContextName); + } else if (auto *dotCall = dyn_cast(CE->getFn())) { SourceLoc removeLoc = dotCall->getDotLoc(); if (removeLoc.isInvalid()) return; diag.fixItRemove(SourceRange(removeLoc, dotCall->getFn()->getEndLoc())); - } else if (!isa(call->getFn())) { + } else if (!isa(CE->getFn())) { return; } // Continue on to diagnose any constructor argument label renames. - + + } else if (parsed.IsSubscript) { + if (auto *CE = dyn_cast_or_null(call)) { + // Renaming from CallExpr to SubscriptExpr. Remove function name and + // replace parens with square brackets. + + if (auto *DSCE = dyn_cast(CE->getFn())) { + if (DSCE->getBase()->isImplicit()) { + // If self is implicit, self must be inserted before subscript syntax. + diag.fixItInsert(CE->getStartLoc(), "self"); + } + } + + diag.fixItReplace(CE->getFn()->getEndLoc(), "["); + diag.fixItReplace(CE->getEndLoc(), "]"); + } } else { // Just replace the base name. SmallString<64> baseReplace; + if (!parsed.ContextName.empty()) { baseReplace += parsed.ContextName; baseReplace += '.'; } baseReplace += parsed.BaseName; - if (parsed.IsFunctionName && parsed.ArgumentLabels.empty() && - isa(renamedDecl)) { - // If we're going from a var to a function with no arguments, emit an - // empty parameter list. - baseReplace += "()"; + + if (parsed.IsFunctionName && isa_and_nonnull(call)) { + auto *SE = cast(call); + + // Renaming from SubscriptExpr to CallExpr. Insert function name and + // replace square brackets with parens. + diag.fixItReplace(SE->getIndex()->getStartLoc(), + ("." + baseReplace.str() + "(").str()); + diag.fixItReplace(SE->getEndLoc(), ")"); + } else { + if (parsed.IsFunctionName && parsed.ArgumentLabels.empty() && + isa(renamedDecl)) { + // If we're going from a var to a function with no arguments, emit an + // empty parameter list. + baseReplace += "()"; + } + diag.fixItReplace(referenceRange, baseReplace); } - diag.fixItReplace(referenceRange, baseReplace); } - if (!call || !isa(call)) + if (!call) + return; + + Expr *argExpr; + if (auto *CE = dyn_cast(call)) + argExpr = CE->getArg(); + else if (auto *SE = dyn_cast(call)) + argExpr = SE->getIndex(); + else return; - auto *argExpr = call->getArg(); auto argList = getOriginalArgumentList(argExpr); if (parsed.IsGetter) { @@ -2037,18 +2177,17 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag, return; } - auto argumentLabelsToCheck = llvm::makeArrayRef(argumentLabelIDs); - // The argument label for a trailing closure is ignored. - if (argList.hasTrailingClosure) - argumentLabelsToCheck = argumentLabelsToCheck.drop_back(); - - if (std::equal(argumentLabelsToCheck.begin(), argumentLabelsToCheck.end(), - argList.labels.begin())) { - // Already matching. - return; + // If any of the argument labels are mismatched, perform label correction. + for (auto i : indices(argList.args)) { + // The argument label of an unlabeled trailing closure is ignored. + if (argList.isUnlabeledTrailingClosureIdx(i)) + continue; + if (argumentLabelIDs[i] != argList.labels[i]) { + diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs, + parsed.IsSubscript, &diag); + return; + } } - - diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs, false, &diag); } // Must be kept in sync with diag::availability_decl_unavailable_rename and @@ -2088,7 +2227,7 @@ describeRename(ASTContext &ctx, const AvailableAttr *attr, const ValueDecl *D, name << parsed.ContextName << '.'; if (parsed.IsFunctionName) { - name << parsed.formDeclName(ctx); + name << parsed.formDeclName(ctx, (D && isa(D))); } else { name << parsed.BaseName; } @@ -2102,30 +2241,10 @@ describeRename(ASTContext &ctx, const AvailableAttr *attr, const ValueDecl *D, return ReplacementDeclKind::None; } -/// Returns a value that can be used to select between accessor kinds in -/// diagnostics. -/// -/// This is correlated with diag::availability_deprecated and others. -static std::pair -getAccessorKindAndNameForDiagnostics(const ValueDecl *D) { - // This should always be one more than the last AccessorKind supported in - // the diagnostics. If you need to change it, change the assertion below as - // well. - static const unsigned NOT_ACCESSOR_INDEX = 2; - - if (auto *accessor = dyn_cast(D)) { - DeclName Name = accessor->getStorage()->getName(); - assert(accessor->isGetterOrSetter()); - return {static_cast(accessor->getAccessorKind()), Name}; - } - - return {NOT_ACCESSOR_INDEX, D->getName()}; -} - void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange, const ExportContext &Where, const ValueDecl *DeprecatedDecl, - const ApplyExpr *Call) { + const Expr *Call) { const AvailableAttr *Attr = TypeChecker::getDeprecated(DeprecatedDecl); if (!Attr) return; @@ -2309,10 +2428,9 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override, /// Emit a diagnostic for references to declarations that have been /// marked as unavailable, either through "unavailable" or "obsoleted:". -bool swift::diagnoseExplicitUnavailability(const ValueDecl *D, - SourceRange R, +bool swift::diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R, const ExportContext &Where, - const ApplyExpr *call, + const Expr *call, DeclAvailabilityFlags Flags) { return diagnoseExplicitUnavailability(D, R, Where, Flags, [=](InFlightDiagnostic &diag) { @@ -2679,7 +2797,7 @@ class ExprAvailabilityWalker : public ASTWalker { diagnoseDeclRefAvailability(DS->getMember(), DS->getSourceRange()); if (auto S = dyn_cast(E)) { if (S->hasDecl()) { - diagnoseDeclRefAvailability(S->getDecl(), S->getSourceRange()); + diagnoseDeclRefAvailability(S->getDecl(), S->getSourceRange(), S); maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC); } } @@ -2737,7 +2855,7 @@ class ExprAvailabilityWalker : public ASTWalker { } bool diagnoseDeclRefAvailability(ConcreteDeclRef declRef, SourceRange R, - const ApplyExpr *call = nullptr, + const Expr *call = nullptr, DeclAvailabilityFlags flags = None) const; private: @@ -2840,6 +2958,7 @@ class ExprAvailabilityWalker : public ASTWalker { case KeyPathExpr::Component::Kind::OptionalForce: case KeyPathExpr::Component::Kind::Identity: case KeyPathExpr::Component::Kind::DictionaryKey: + case KeyPathExpr::Component::Kind::CodeCompletion: break; } } @@ -2919,9 +3038,8 @@ class ExprAvailabilityWalker : public ASTWalker { /// Diagnose uses of unavailable declarations. Returns true if a diagnostic /// was emitted. -bool -ExprAvailabilityWalker::diagnoseDeclRefAvailability( - ConcreteDeclRef declRef, SourceRange R, const ApplyExpr *call, +bool ExprAvailabilityWalker::diagnoseDeclRefAvailability( + ConcreteDeclRef declRef, SourceRange R, const Expr *call, DeclAvailabilityFlags Flags) const { if (!declRef) return false; @@ -2930,7 +3048,8 @@ ExprAvailabilityWalker::diagnoseDeclRefAvailability( if (auto *attr = AvailableAttr::isUnavailable(D)) { if (diagnoseIncDecRemoval(D, R, attr)) return true; - if (call && diagnoseMemoryLayoutMigration(D, R, attr, call)) + if (isa_and_nonnull(call) && + diagnoseMemoryLayoutMigration(D, R, attr, cast(call))) return true; } @@ -2948,12 +3067,10 @@ ExprAvailabilityWalker::diagnoseDeclRefAvailability( /// Diagnose uses of unavailable declarations. Returns true if a diagnostic /// was emitted. -bool -swift::diagnoseDeclAvailability(const ValueDecl *D, - SourceRange R, - const ApplyExpr *call, - const ExportContext &Where, - DeclAvailabilityFlags Flags) { +bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R, + const Expr *call, + const ExportContext &Where, + DeclAvailabilityFlags Flags) { assert(!Where.isImplicit()); // Generic parameters are always available. @@ -3013,19 +3130,13 @@ swift::diagnoseDeclAvailability(const ValueDecl *D, return false; } - /// Return true if the specified type looks like an integer of floating point /// type. -static bool isIntegerOrFloatingPointType(Type ty, DeclContext *DC, - ASTContext &Context) { - auto integerType = - Context.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); - auto floatingType = - Context.getProtocol(KnownProtocolKind::ExpressibleByFloatLiteral); - if (!integerType || !floatingType) return false; - - return TypeChecker::conformsToProtocol(ty, integerType, DC) || - TypeChecker::conformsToProtocol(ty, floatingType, DC); +static bool isIntegerOrFloatingPointType(Type ty, ModuleDecl *M) { + return (TypeChecker::conformsToKnownProtocol( + ty, KnownProtocolKind::ExpressibleByIntegerLiteral, M) || + TypeChecker::conformsToKnownProtocol( + ty, KnownProtocolKind::ExpressibleByFloatLiteral, M)); } @@ -3055,7 +3166,7 @@ ExprAvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R, // to "lvalue += 1". auto *DC = Where.getDeclContext(); std::string replacement; - if (isIntegerOrFloatingPointType(call->getType(), DC, Context)) + if (isIntegerOrFloatingPointType(call->getType(), DC->getParentModule())) replacement = isInc ? " += 1" : " -= 1"; else { // Otherwise, it must be an index type. Rewrite to: @@ -3401,6 +3512,12 @@ void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc, diagnoseTypeAvailability(T, loc, where, flags); } +static void diagnoseMissingConformance( + SourceLoc loc, Type type, ProtocolDecl *proto, ModuleDecl *module) { + assert(proto->isSpecificProtocol(KnownProtocolKind::Sendable)); + diagnoseMissingSendableConformance(loc, type, module); +} + bool swift::diagnoseConformanceAvailability(SourceLoc loc, ProtocolConformanceRef conformance, @@ -3414,6 +3531,16 @@ swift::diagnoseConformanceAvailability(SourceLoc loc, const ProtocolConformance *concreteConf = conformance.getConcrete(); const RootProtocolConformance *rootConf = concreteConf->getRootConformance(); + // Diagnose "missing" conformances where we needed a conformance but + // didn't have one. + if (auto builtinConformance = dyn_cast(rootConf)){ + if (builtinConformance->isMissing()) { + diagnoseMissingConformance(loc, builtinConformance->getType(), + builtinConformance->getProtocol(), + where.getDeclContext()->getParentModule()); + } + } + auto *DC = where.getDeclContext(); auto maybeEmitAssociatedTypeNote = [&]() { diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 70b75b531e638..ac9b57d93e45d 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -230,10 +230,8 @@ diagnoseSubstitutionMapAvailability(SourceLoc loc, /// Diagnose uses of unavailable declarations. Returns true if a diagnostic /// was emitted. -bool diagnoseDeclAvailability(const ValueDecl *D, - SourceRange R, - const ApplyExpr *call, - const ExportContext &where, +bool diagnoseDeclAvailability(const ValueDecl *D, SourceRange R, + const Expr *call, const ExportContext &where, DeclAvailabilityFlags flags = None); void diagnoseUnavailableOverride(ValueDecl *override, @@ -242,10 +240,9 @@ void diagnoseUnavailableOverride(ValueDecl *override, /// Emit a diagnostic for references to declarations that have been /// marked as unavailable, either through "unavailable" or "obsoleted:". -bool diagnoseExplicitUnavailability(const ValueDecl *D, - SourceRange R, +bool diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R, const ExportContext &Where, - const ApplyExpr *call, + const Expr *call, DeclAvailabilityFlags Flags = None); /// Emit a diagnostic for references to declarations that have been diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index bb817f58b88fc..fb60113b4848b 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -108,7 +108,7 @@ class FindCapturedVars : public ASTWalker { // don't need to visit them. if (ObjC) { if (auto clas = dyn_cast_or_null(ty->getAnyNominal())) { - if (clas->usesObjCGenericsModel()) { + if (clas->isTypeErasedGenericClass()) { return Action::SkipChildren; } } @@ -177,6 +177,14 @@ class FindCapturedVars : public ASTWalker { /// if invalid. void addCapture(CapturedValue capture) { auto VD = capture.getDecl(); + + if (auto var = dyn_cast(VD)) { + // `async let` variables cannot currently be captured. + if (var->isAsyncLet()) { + Context.Diags.diagnose(capture.getLoc(), diag::capture_async_let_not_supported); + return; + } + } // Check to see if we already have an entry for this decl. unsigned &entryNumber = captureEntryNumber[VD]; @@ -530,7 +538,7 @@ class FindCapturedVars : public ASTWalker { return false; if (auto clas = dyn_cast_or_null(toTy->getAnyNominal())) { - if (clas->usesObjCGenericsModel()) { + if (clas->isTypeErasedGenericClass()) { return false; } } @@ -658,7 +666,7 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) { // their context. if (AFD && finder.hasGenericParamCaptures()) { if (auto Clas = AFD->getParent()->getSelfClassDecl()) { - if (Clas->usesObjCGenericsModel()) { + if (Clas->isTypeErasedGenericClass()) { AFD->diagnose(diag::objc_generic_extension_using_type_parameter); // If it's possible, suggest adding @objc. diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 2e6efe8464b8a..ea70d8365f454 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -242,6 +242,9 @@ class SanitizeExpr : public ASTWalker { if (auto closure = dyn_cast(expr)) { if (!shouldTypeCheckInEnclosingExpression(closure)) return { false, expr }; + for (auto &Param : *closure->getParameters()) { + Param->setSpecifier(swift::ParamSpecifier::Default); + } } // Now, we're ready to walk into sub expressions. @@ -285,18 +288,18 @@ class SanitizeExpr : public ASTWalker { new (C) ParenExpr(argList.lParenLoc, argList.args[0], argList.rParenLoc, - argList.hasTrailingClosure); + argList.hasAnyTrailingClosures()); result->setImplicit(); return result; } return TupleExpr::create(C, argList.lParenLoc, + argList.rParenLoc, argList.args, argList.labels, argList.labelLocs, - argList.rParenLoc, - argList.hasTrailingClosure, + argList.unlabeledTrailingClosureIdx, /*implicit=*/true); } @@ -563,6 +566,7 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, auto *opExpr = TypeChecker::resolveDeclRefExpr( &UDRE, DC, /*replaceInvalidRefsWithErrors=*/true); + auto &ctx = DC->getASTContext(); switch (refKind) { case DeclRefKind::PostfixOperator: { // (postfix_unary_expr @@ -571,9 +575,8 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, // ())) ParenExpr Args(SourceLoc(), LHS, SourceLoc(), /*hasTrailingClosure=*/false); - PostfixUnaryExpr postfixExpr(opExpr, &Args); - return getTypeOfCompletionOperatorImpl(DC, &postfixExpr, - referencedDecl); + auto *postfixExpr = PostfixUnaryExpr::create(ctx, opExpr, &Args); + return getTypeOfCompletionOperatorImpl(DC, postfixExpr, referencedDecl); } case DeclRefKind::BinaryOperator: { @@ -583,13 +586,9 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, // () // (code_completion_expr))) CodeCompletionExpr dummyRHS(Loc); - auto Args = TupleExpr::create( - DC->getASTContext(), SourceLoc(), {LHS, &dummyRHS}, {}, {}, SourceLoc(), - /*hasTrailingClosure=*/false, /*isImplicit=*/true); - BinaryExpr binaryExpr(opExpr, Args, /*isImplicit=*/true); - - return getTypeOfCompletionOperatorImpl(DC, &binaryExpr, - referencedDecl); + auto *binaryExpr = BinaryExpr::create(ctx, LHS, opExpr, &dummyRHS, + /*implicit*/ true); + return getTypeOfCompletionOperatorImpl(DC, binaryExpr, referencedDecl); } default: @@ -615,7 +614,15 @@ class CompletionContextFinder : public ASTWalker { /// Stack of all "interesting" contexts up to code completion expression. llvm::SmallVector Contexts; - CodeCompletionExpr *CompletionExpr = nullptr; + + /// If we are completing inside an expression, the \c CodeCompletionExpr that + /// represents the code completion token. + + /// The AST node that represents the code completion token, either as a + /// \c CodeCompletionExpr or a \c KeyPathExpr which contains a code completion + /// component. + llvm::PointerUnion CompletionNode; + Expr *InitialExpr = nullptr; DeclContext *InitialDC; @@ -661,9 +668,21 @@ class CompletionContextFinder : public ASTWalker { } if (auto *CCE = dyn_cast(E)) { - CompletionExpr = CCE; + CompletionNode = CCE; return std::make_pair(false, nullptr); } + if (auto *KeyPath = dyn_cast(E)) { + for (auto &component : KeyPath->getComponents()) { + if (component.getKind() == + KeyPathExpr::Component::Kind::CodeCompletion) { + CompletionNode = KeyPath; + return std::make_pair(false, nullptr); + } + } + // Code completion in key paths is modelled by a code completion component + // Don't walk the key path's parsed expressions. + return std::make_pair(false, E); + } return std::make_pair(true, E); } @@ -688,12 +707,42 @@ class CompletionContextFinder : public ASTWalker { } bool hasCompletionExpr() const { - return CompletionExpr; + return CompletionNode.dyn_cast() != nullptr; } CodeCompletionExpr *getCompletionExpr() const { - assert(CompletionExpr); - return CompletionExpr; + assert(hasCompletionExpr()); + return CompletionNode.get(); + } + + bool hasCompletionKeyPathComponent() const { + return CompletionNode.dyn_cast() != nullptr; + } + + /// If we are completing in a key path, returns the \c KeyPath that contains + /// the code completion component. + const KeyPathExpr *getKeyPathContainingCompletionComponent() const { + assert(hasCompletionKeyPathComponent()); + return CompletionNode.get(); + } + + /// If we are completing in a key path, returns the index at which the key + /// path has the code completion component. + size_t getKeyPathCompletionComponentIndex() const { + assert(hasCompletionKeyPathComponent()); + size_t ComponentIndex = 0; + auto Components = + getKeyPathContainingCompletionComponent()->getComponents(); + for (auto &Component : Components) { + if (Component.getKind() == KeyPathExpr::Component::Kind::CodeCompletion) { + break; + } else { + ComponentIndex++; + } + } + assert(ComponentIndex < Components.size() && + "No completion component in the key path?"); + return ComponentIndex; } struct Fallback { @@ -707,7 +756,11 @@ class CompletionContextFinder : public ASTWalker { /// of the enclosing context e.g. when completion is an argument /// to a call. Optional getFallbackCompletionExpr() const { - assert(CompletionExpr); + if (!hasCompletionExpr()) { + // Creating a fallback expression only makes sense if we are completing in + // an expression, not when we're completing in a key path. + return None; + } Optional fallback; bool separatePrecheck = false; @@ -743,8 +796,8 @@ class CompletionContextFinder : public ASTWalker { if (fallback) return fallback; - if (CompletionExpr->getBase() && CompletionExpr != InitialExpr) - return Fallback{CompletionExpr, fallbackDC, separatePrecheck}; + if (getCompletionExpr()->getBase() && getCompletionExpr() != InitialExpr) + return Fallback{getCompletionExpr(), fallbackDC, separatePrecheck}; return None; } @@ -758,59 +811,11 @@ class CompletionContextFinder : public ASTWalker { } // end namespace -// Determine if the target expression is the implicit BinaryExpr generated for -// pattern-matching in a switch/if/guard case ( ~= matchValue). -static bool isForPatternMatch(SolutionApplicationTarget &target) { - if (target.getExprContextualTypePurpose() != CTP_Condition) - return false; - Expr *condition = target.getAsExpr(); - if (!condition->isImplicit()) - return false; - if (auto *BE = dyn_cast(condition)) { - Identifier id; - if (auto *ODRE = dyn_cast(BE->getFn())) { - id = ODRE->getDecls().front()->getBaseIdentifier(); - } else if (auto *DRE = dyn_cast(BE->getFn())) { - id = DRE->getDecl()->getBaseIdentifier(); - } - if (id != target.getDeclContext()->getASTContext().Id_MatchOperator) - return false; - return isa(BE->getArg()->getElement(0)); - } - return false; -} - -/// Remove any solutions from the provided vector that both require fixes and have a -/// score worse than the best. +/// Remove any solutions from the provided vector that both require fixes and +/// have a score worse than the best. static void filterSolutions(SolutionApplicationTarget &target, SmallVectorImpl &solutions, CodeCompletionExpr *completionExpr) { - // FIXME: this is only needed because in pattern matching position, the - // code completion expression always becomes an expression pattern, which - // requires the ~= operator to be defined on the type being matched against. - // Pattern matching against an enum doesn't require that however, so valid - // solutions always end up having fixes. This is a problem because there will - // always be a valid solution as well. Optional defines ~= between Optional - // and _OptionalNilComparisonType (which defines a nilLiteral initializer), - // and the matched-against value can implicitly be made Optional if it isn't - // already, so _OptionalNilComparisonType is always a valid solution for the - // completion. That only generates the 'nil' completion, which is rarely what - // the user intends to write in this position and shouldn't be preferred over - // the other formed solutions (which require fixes). We should generate enum - // pattern completions separately, but for now ignore the - // _OptionalNilComparisonType solution. - if (isForPatternMatch(target)) { - solutions.erase(llvm::remove_if(solutions, [&](const Solution &S) { - ASTContext &ctx = S.getConstraintSystem().getASTContext(); - if (!S.hasType(completionExpr)) - return false; - if (auto ty = S.getResolvedType(completionExpr)) - if (auto *NTD = ty->getAnyNominal()) - return NTD->getBaseIdentifier() == ctx.Id_OptionalNilComparisonType; - return false; - }), solutions.end()); - } - if (solutions.size() <= 1) return; @@ -856,7 +861,8 @@ bool TypeChecker::typeCheckForCodeCompletion( // If there was no completion expr (e.g. if the code completion location was // among tokens that were skipped over during parser error recovery) bail. - if (!contextAnalyzer.hasCompletionExpr()) + if (!contextAnalyzer.hasCompletionExpr() && + !contextAnalyzer.hasCompletionKeyPathComponent()) return false; // Interpolation components are type-checked separately. @@ -902,7 +908,11 @@ bool TypeChecker::typeCheckForCodeCompletion( // FIXME: instead of filtering, expose the score and viability to clients. // Remove any solutions that both require fixes and have a score that is // worse than the best. - filterSolutions(target, solutions, contextAnalyzer.getCompletionExpr()); + CodeCompletionExpr *completionExpr = nullptr; + if (contextAnalyzer.hasCompletionExpr()) { + completionExpr = contextAnalyzer.getCompletionExpr(); + } + filterSolutions(target, solutions, completionExpr); // Similarly, if the type-check didn't produce any solutions, fall back // to type-checking a sub-expression in isolation. @@ -915,20 +925,27 @@ bool TypeChecker::typeCheckForCodeCompletion( if (contextAnalyzer.locatedInMultiStmtClosure()) { auto &solution = solutions.front(); - if (solution.hasType(contextAnalyzer.getCompletionExpr())) { - llvm::for_each(solutions, callback); - return CompletionResult::Ok; + bool HasTypeForCompletionNode = false; + if (completionExpr) { + HasTypeForCompletionNode = solution.hasType(completionExpr); + } else { + assert(contextAnalyzer.hasCompletionKeyPathComponent()); + HasTypeForCompletionNode = solution.hasType( + contextAnalyzer.getKeyPathContainingCompletionComponent(), + contextAnalyzer.getKeyPathCompletionComponentIndex()); } - // At this point we know the code completion expression wasn't checked - // with the closure's surrounding context, so can defer to regular type- - // checking for the current call to typeCheckExpression. If that succeeds - // we will get a second call to typeCheckExpression for the body of the - // closure later and can gather completions then. If it doesn't we rely - // on the fallback typechecking in the subclasses of - // TypeCheckCompletionCallback that considers in isolation a - // sub-expression of the closure that contains the completion location. - return CompletionResult::NotApplicable; + if (!HasTypeForCompletionNode) { + // At this point we know the code completion node wasn't checked with + // the closure's surrounding context, so can defer to regular + // type-checking for the current call to typeCheckExpression. If that + // succeeds we will get a second call to typeCheckExpression for the + // body of the closure later and can gather completions then. If it + // doesn't we rely on the fallback typechecking in the subclasses of + // TypeCheckCompletionCallback that considers in isolation a + // sub-expression of the closure that contains the completion location. + return CompletionResult::NotApplicable; + } } llvm::for_each(solutions, callback); @@ -1221,6 +1238,69 @@ sawSolution(const constraints::Solution &S) { } } +/// If the code completion variable occurs in a pattern matching position, we +/// have an AST that looks like this. +/// \code +/// (binary_expr implicit type='$T3' +/// (overloaded_decl_ref_expr function_ref=compound decls=[ +/// Swift.(file).~=, +/// Swift.(file).Optional extension.~=]) +/// (tuple_expr implicit type='($T1, (OtherEnum))' +/// (code_completion_expr implicit type='$T1') +/// (declref_expr implicit decl=swift_ide_test.(file).foo(x:).$match))) +/// \endcode +/// If the code completion expression occurs in such an AST, return the +/// declaration of the \c $match variable, otherwise return \c nullptr. +VarDecl *getMatchVarIfInPatternMatch(CodeCompletionExpr *CompletionExpr, + ConstraintSystem &CS) { + auto &Context = CS.getASTContext(); + + TupleExpr *ArgTuple = + dyn_cast_or_null(CS.getParentExpr(CompletionExpr)); + if (!ArgTuple || !ArgTuple->isImplicit() || ArgTuple->getNumElements() != 2) { + return nullptr; + } + + auto Binary = dyn_cast_or_null(CS.getParentExpr(ArgTuple)); + if (!Binary || !Binary->isImplicit()) { + return nullptr; + } + + auto CalledOperator = Binary->getFn(); + if (!CalledOperator || !CalledOperator->isImplicit()) { + return nullptr; + } + // The reference to the ~= operator might be an OverloadedDeclRefExpr or a + // DeclRefExpr, depending on how many ~= operators are viable. + if (auto Overloaded = + dyn_cast_or_null(CalledOperator)) { + if (!llvm::all_of(Overloaded->getDecls(), [&Context](ValueDecl *D) { + return D->getBaseName() == Context.Id_MatchOperator; + })) { + return nullptr; + } + } else if (auto Ref = dyn_cast_or_null(CalledOperator)) { + if (Ref->getDecl()->getBaseName() != Context.Id_MatchOperator) { + return nullptr; + } + } else { + return nullptr; + } + + auto MatchArg = dyn_cast_or_null(ArgTuple->getElement(1)); + if (!MatchArg || !MatchArg->isImplicit()) { + return nullptr; + } + + auto MatchVar = MatchArg->getDecl(); + if (MatchVar && MatchVar->isImplicit() && + MatchVar->getBaseName() == Context.Id_PatternMatchVar) { + return dyn_cast(MatchVar); + } else { + return nullptr; + } +} + void UnresolvedMemberTypeCheckCompletionCallback:: sawSolution(const constraints::Solution &S) { GotCallback = true; @@ -1230,16 +1310,83 @@ sawSolution(const constraints::Solution &S) { // If the type couldn't be determined (e.g. because there isn't any context // to derive it from), let's not attempt to do a lookup since it wouldn't // produce any useful results anyway. - if (!ExpectedTy || ExpectedTy->is()) - return; + if (ExpectedTy && !ExpectedTy->is()) { + // If ExpectedTy is a duplicate of any other result, ignore this solution. + if (!llvm::any_of(ExprResults, [&](const ExprResult &R) { + return R.ExpectedTy->isEqual(ExpectedTy); + })) { + bool SingleExprBody = + isImplicitSingleExpressionReturn(CS, CompletionExpr); + ExprResults.push_back({ExpectedTy, SingleExprBody}); + } + } + + if (auto MatchVar = getMatchVarIfInPatternMatch(CompletionExpr, CS)) { + Type MatchVarType; + // If the MatchVar has an explicit type, it's not part of the solution. But + // we can look it up in the constraint system directly. + if (auto T = S.getConstraintSystem().getVarType(MatchVar)) { + MatchVarType = T; + } else { + MatchVarType = S.getResolvedType(MatchVar); + } + if (MatchVarType && !MatchVarType->is()) { + if (!llvm::any_of(EnumPatternTypes, [&](const Type &R) { + return R->isEqual(MatchVarType); + })) { + EnumPatternTypes.push_back(MatchVarType); + } + } + } +} + +void KeyPathTypeCheckCompletionCallback::sawSolution( + const constraints::Solution &S) { + // Determine the code completion. + size_t ComponentIndex = 0; + for (auto &Component : KeyPath->getComponents()) { + if (Component.getKind() == KeyPathExpr::Component::Kind::CodeCompletion) { + break; + } else { + ComponentIndex++; + } + } + assert(ComponentIndex < KeyPath->getComponents().size() && + "Didn't find a code compleiton component?"); + + Type BaseType; + if (ComponentIndex == 0) { + // We are completing on the root and need to extract the key path's root + // type. + if (KeyPath->getRootType()) { + BaseType = S.getResolvedType(KeyPath->getRootType()); + } else { + // The key path doesn't have a root TypeRepr set, so we can't look the key + // path's root up through it. Build a constraint locator and look the + // root type up through it. + // FIXME: Improve the linear search over S.typeBindings when it's possible + // to look up type variables by their locators. + auto RootLocator = + S.getConstraintLocator(KeyPath, {ConstraintLocator::KeyPathRoot}); + auto BaseVariableType = + llvm::find_if(S.typeBindings, [&RootLocator](const auto &Entry) { + return Entry.first->getImpl().getLocator() == RootLocator; + })->getSecond(); + BaseType = S.simplifyType(BaseVariableType); + } + } else { + // We are completing after a component. Get the previous component's result + // type. + BaseType = S.simplifyType(S.getType(KeyPath, ComponentIndex - 1)); + } // If ExpectedTy is a duplicate of any other result, ignore this solution. if (llvm::any_of(Results, [&](const Result &R) { - return R.ExpectedTy->isEqual(ExpectedTy); + return R.BaseType->isEqual(BaseType); })) { return; } - - bool SingleExprBody = isImplicitSingleExpressionReturn(CS, CompletionExpr); - Results.push_back({ExpectedTy, SingleExprBody}); + if (BaseType) { + Results.push_back({BaseType, /*OnRoot=*/(ComponentIndex == 0)}); + } } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 0c8caf22baf14..9c87b32c31430 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -14,16 +14,19 @@ // //===----------------------------------------------------------------------===// #include "TypeCheckConcurrency.h" +#include "TypeCheckDistributed.h" #include "TypeChecker.h" #include "TypeCheckType.h" #include "swift/Strings.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/TypeCheckRequests.h" -#include "swift/AST/TypeVisitor.h" +#include "swift/AST/ExistentialLayout.h" +#include "swift/Sema/IDETypeChecking.h" using namespace swift; @@ -57,74 +60,6 @@ static bool shouldInferAttributeInContext(const DeclContext *dc) { case FileUnitKind::DWARFModule: return true; } - } - - return false; -} - -/// Check whether the @asyncHandler attribute can be applied to the given -/// function declaration. -/// -/// \param diagnose Whether to emit a diagnostic when a problem is encountered. -/// -/// \returns \c true if there was a problem with adding the attribute, \c false -/// otherwise. -static bool checkAsyncHandler(FuncDecl *func, bool diagnose) { - if (!func->getResultInterfaceType()->isVoid()) { - if (diagnose) { - func->diagnose(diag::asynchandler_returns_value) - .highlight(func->getResultTypeSourceRange()); - } - - return true; - } - - if (func->hasThrows()) { - if (diagnose) { - func->diagnose(diag::asynchandler_throws) - .fixItRemove(func->getThrowsLoc()); - } - - return true; - } - - if (func->hasAsync()) { - if (diagnose) { - func->diagnose(diag::asynchandler_async) - .fixItRemove(func->getAsyncLoc()); - } - - return true; - } - - for (auto param : *func->getParameters()) { - if (param->isInOut()) { - if (diagnose) { - param->diagnose(diag::asynchandler_inout_parameter) - .fixItRemove(param->getSpecifierLoc()); - } - - return true; - } - - if (auto fnType = param->getInterfaceType()->getAs()) { - if (fnType->isNoEscape()) { - if (diagnose) { - param->diagnose(diag::asynchandler_noescape_closure_parameter); - } - - return true; - } - } - } - - if (func->isMutating()) { - if (diagnose) { - auto diag = func->diagnose(diag::asynchandler_mutating); - if (auto mutatingAttr = func->getAttrs().getAttribute()) { - diag.fixItRemove(mutatingAttr->getRange()); - } - } return true; } @@ -149,121 +84,16 @@ void swift::addAsyncNotes(AbstractFunctionDecl const* func) { " async"); } } - - if (func->canBeAsyncHandler()) { - func->diagnose( - diag::note_add_asynchandler_to_function, func->getName()) - .fixItInsert(func->getAttributeInsertionLoc(false), "@asyncHandler "); - } -} - -bool IsAsyncHandlerRequest::evaluate( - Evaluator &evaluator, FuncDecl *func) const { - // Turn off @asyncHandler when not specifically enabled. - if (!func->getASTContext().LangOpts.EnableExperimentalAsyncHandler) { - return false; - } - - // Check whether the attribute was explicitly specified. - if (auto attr = func->getAttrs().getAttribute()) { - // Check for well-formedness. - if (checkAsyncHandler(func, /*diagnose=*/true)) { - attr->setInvalid(); - return false; - } - - return true; - } - - if (!shouldInferAttributeInContext(func->getDeclContext())) - return false; - - // Are we in a context where inference is possible? - auto dc = func->getDeclContext(); - if (!dc->getSelfClassDecl() || !dc->getParentSourceFile() || !func->hasBody()) - return false; - - // Is it possible to infer @asyncHandler for this function at all? - if (!func->canBeAsyncHandler()) - return false; - - if (!dc->getSelfClassDecl()->isActor()) - return false; - - // Add an implicit @asyncHandler attribute and return true. We're done. - auto addImplicitAsyncHandlerAttr = [&] { - func->getAttrs().add(new (func->getASTContext()) AsyncHandlerAttr(true)); - return true; - }; - - // Check whether any of the conformances in the context of the function - // implies @asyncHandler. - { - auto idc = cast(dc->getAsDecl()); - auto conformances = idc->getLocalConformances( - ConformanceLookupKind::NonStructural); - - for (auto conformance : conformances) { - auto protocol = conformance->getProtocol(); - for (auto found : protocol->lookupDirect(func->getName())) { - if (!isa(found->getDeclContext())) - continue; - - auto requirement = dyn_cast(found); - if (!requirement) - continue; - - if (!requirement->isAsyncHandler()) - continue; - - auto witness = conformance->getWitnessDecl(requirement); - if (witness != func) - continue; - - return addImplicitAsyncHandlerAttr(); - } - } - } - - // Look through dynamic replacements. - if (auto replaced = func->getDynamicallyReplacedDecl()) { - if (auto replacedFunc = dyn_cast(replaced)) - if (replacedFunc->isAsyncHandler()) - return addImplicitAsyncHandlerAttr(); - } - - return false; -} - -bool CanBeAsyncHandlerRequest::evaluate( - Evaluator &evaluator, FuncDecl *func) const { - // Turn off @asyncHandler when not specifically enabled. - if (!func->getASTContext().LangOpts.EnableExperimentalAsyncHandler) { - return false; - } - - return !checkAsyncHandler(func, /*diagnose=*/false); } bool IsActorRequest::evaluate( Evaluator &evaluator, NominalTypeDecl *nominal) const { - // Protocols are actors if their `Self` type conforms to `Actor`. + // Protocols are actors if they inherit from `Actor`. if (auto protocol = dyn_cast(nominal)) { - // Simple case: we have the Actor protocol itself. - if (protocol->isSpecificProtocol(KnownProtocolKind::Actor)) - return true; - - auto actorProto = nominal->getASTContext().getProtocol( - KnownProtocolKind::Actor); - if (!actorProto) - return false; - - auto selfType = Type(protocol->getProtocolSelfType()); - auto genericSig = protocol->getGenericSignature(); - if (!genericSig) - return false; - - return genericSig->requiresProtocol(selfType, actorProto); + auto &ctx = protocol->getASTContext(); + auto *actorProtocol = ctx.getProtocol(KnownProtocolKind::Actor); + return (protocol == actorProtocol || + protocol->inheritsFrom(actorProtocol)); } // Class declarations are actors if they were declared with "actor". @@ -271,8 +101,7 @@ bool IsActorRequest::evaluate( if (!classDecl) return false; - return classDecl->isExplicitActor() || - classDecl->getAttrs().getAttribute(); + return classDecl->isExplicitActor(); } bool IsDefaultActorRequest::evaluate( @@ -299,12 +128,6 @@ bool IsDefaultActorRequest::evaluate( return true; } -static bool isDeclNotAsAccessibleAsParent(ValueDecl *decl, - NominalTypeDecl *parent) { - return decl->getFormalAccess() < - std::min(parent->getFormalAccess(), AccessLevel::Public); -} - VarDecl *GlobalActorInstanceRequest::evaluate( Evaluator &evaluator, NominalTypeDecl *nominal) const { auto globalActorAttr = nominal->getAttrs().getAttribute(); @@ -319,104 +142,27 @@ VarDecl *GlobalActorInstanceRequest::evaluate( return nullptr; } + // Non-final classes cannot be global actors. + if (auto classDecl = dyn_cast(nominal)) { + if (!classDecl->isSemanticallyFinal()) { + nominal->diagnose(diag::global_actor_non_final_class, nominal->getName()) + .highlight(globalActorAttr->getRangeWithAt()); + } + } + // Global actors have a static property "shared" that provides an actor - // instance. The value must + // instance. The value must be of Actor type, which is validated by + // conformance to the 'GlobalActor' protocol. SmallVector decls; nominal->lookupQualified( nominal, DeclNameRef(ctx.Id_shared), NL_QualifiedDefault, decls); - VarDecl *sharedVar = nullptr; - llvm::TinyPtrVector candidates; for (auto decl : decls) { auto var = dyn_cast(decl); if (!var) continue; - auto varDC = var->getDeclContext(); - if (var->isStatic() && - !isDeclNotAsAccessibleAsParent(var, nominal) && - !(isa(varDC) && - cast(varDC)->isConstrainedExtension()) && - TypeChecker::conformsToProtocol( - varDC->mapTypeIntoContext(var->getValueInterfaceType()), - actorProto, nominal)) { - sharedVar = var; - break; - } - - candidates.push_back(var); - } - - // If we found a suitable candidate, we're done. - if (sharedVar) - return sharedVar; - - // Complain about the lack of a suitable 'shared' property. - { - auto primaryDiag = nominal->diagnose( - diag::global_actor_missing_shared, nominal->getName()); - - // If there were no candidates, provide a Fix-It with a prototype. - if (candidates.empty() && nominal->getBraces().Start.isValid()) { - // Figure out the indentation we need. - SourceLoc sharedInsertionLoc = Lexer::getLocForEndOfToken( - ctx.SourceMgr, nominal->getBraces().Start); - - StringRef extraIndent; - StringRef currentIndent = Lexer::getIndentationForLine( - ctx.SourceMgr, sharedInsertionLoc, &extraIndent); - std::string stubIndent = (currentIndent + extraIndent).str(); - - // From the string to add the declaration. - std::string sharedDeclString = "\n" + stubIndent; - if (nominal->getFormalAccess() >= AccessLevel::Public) - sharedDeclString += "public "; - - sharedDeclString += "static let shared = <#actor instance#>"; - - primaryDiag.fixItInsert(sharedInsertionLoc, sharedDeclString); - } - } - - // Remark about all of the candidates that failed (and why). - for (auto candidate : candidates) { - if (!candidate->isStatic()) { - candidate->diagnose(diag::global_actor_shared_not_static) - .fixItInsert(candidate->getAttributeInsertionLoc(true), "static "); - continue; - } - - if (isDeclNotAsAccessibleAsParent(candidate, nominal)) { - AccessLevel needAccessLevel = std::min( - nominal->getFormalAccess(), AccessLevel::Public); - auto diag = candidate->diagnose( - diag::global_actor_shared_inaccessible, - getAccessLevelSpelling(candidate->getFormalAccess()), - getAccessLevelSpelling(needAccessLevel)); - if (auto attr = candidate->getAttrs().getAttribute()) { - if (needAccessLevel == AccessLevel::Internal) { - diag.fixItRemove(attr->getRange()); - } else { - diag.fixItReplace( - attr->getRange(), getAccessLevelSpelling(needAccessLevel)); - } - } else { - diag.fixItInsert( - candidate->getAttributeInsertionLoc(true), - getAccessLevelSpelling(needAccessLevel)); - } - continue; - } - - if (auto ext = dyn_cast(candidate->getDeclContext())) { - if (ext->isConstrainedExtension()) { - candidate->diagnose(diag::global_actor_shared_constrained_extension); - continue; - } - } - - Type varType = candidate->getDeclContext()->mapTypeIntoContext( - candidate->getValueInterfaceType()); - candidate->diagnose(diag::global_actor_shared_non_actor_type, varType); + if (var->getDeclContext() == nominal && var->isStatic()) + return var; } return nullptr; @@ -471,7 +217,18 @@ GlobalActorAttributeRequest::evaluate( if (auto decl = subject.dyn_cast()) { dc = decl->getDeclContext(); declAttrs = &decl->getAttrs(); - loc = decl->getLoc(); + // HACK: `getLoc`, when querying the attr from a serialized decl, + // dependning on deserialization order, may launch into arbitrary + // type-checking when querying interface types of such decls. Which, + // in turn, may do things like query (to print) USRs. This ends up being + // prone to request evaluator cycles. + // + // Because this only applies to serialized decls, we can be confident + // that they already went through this type-checking as primaries, so, + // for now, to avoid cycles, we simply ignore the locs on serialized decls + // only. + // This is a workaround for rdar://79563942 + loc = decl->getLoc(/* SerializedOK */ false); } else { auto closure = subject.get(); dc = closure; @@ -553,7 +310,7 @@ Type swift::getExplicitGlobalActor(ClosureExpr *closure) { /// Determine the isolation rules for a given declaration. ActorIsolationRestriction ActorIsolationRestriction::forDeclaration( - ConcreteDeclRef declRef, bool fromExpression) { + ConcreteDeclRef declRef, const DeclContext *fromDC, bool fromExpression) { auto decl = declRef.getDecl(); switch (decl->getKind()) { @@ -602,6 +359,21 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration( if (cast(decl)->isLocalCapture()) return forUnrestricted(); + auto isolation = getActorIsolation(cast(decl)); + + // 'let' declarations are immutable, so they can be accessed across + // actors. + bool isAccessibleAcrossActors = false; + if (auto var = dyn_cast(decl)) { + // A 'let' declaration is accessible across actors if it is either + // nonisolated or it is accessed from within the same module. + if (var->isLet() && + (isolation == ActorIsolation::Independent || + var->getDeclContext()->getParentModule() == + fromDC->getParentModule())) + isAccessibleAcrossActors = true; + } + // A function that provides an asynchronous context has no restrictions // on its access. // @@ -609,10 +381,24 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration( // The call-sites are just conditionally async based on where they appear // (outside or inside the actor). This suggests that the implicitly-async // concept could be merged into the CrossActorSelf concept. - bool isAccessibleAcrossActors = false; if (auto func = dyn_cast(decl)) { if (func->isAsyncContext()) isAccessibleAcrossActors = true; + + // FIXME: move diagnosis out of this function entirely (!) + if (func->isDistributed()) { + if (auto classDecl = dyn_cast(decl->getDeclContext())) { + if (!classDecl->isDistributedActor()) { + // `distributed func` must only be defined in `distributed actor` + func->diagnose( + diag::distributed_actor_func_defined_outside_of_distributed_actor, + func->getName()); + } + } // TODO: need to handle protocol case here too? + + return forDistributedActorSelf(isolation.getActor(), + /*isCrossActor*/ isAccessibleAcrossActors); + } } // Similarly, a computed property or subscript that has an 'async' getter @@ -624,12 +410,17 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration( } // Determine the actor isolation of the given declaration. - switch (auto isolation = getActorIsolation(cast(decl))) { + switch (isolation) { case ActorIsolation::ActorInstance: // Protected actor instance members can only be accessed on 'self'. return forActorSelf(isolation.getActor(), isAccessibleAcrossActors || isa(decl)); + case ActorIsolation::DistributedActorInstance: + // Only distributed functions can be called externally on a distributed actor. + return forDistributedActorSelf(isolation.getActor(), + /*isCrossActor*/ isAccessibleAcrossActors || isa(decl)); + case ActorIsolation::GlobalActorUnsafe: case ActorIsolation::GlobalActor: { // A global-actor-isolated function referenced within an expression @@ -648,7 +439,6 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration( } case ActorIsolation::Independent: - // Actor-independent have no restrictions on their access. return forUnrestricted(); case ActorIsolation::Unspecified: @@ -711,7 +501,7 @@ findMemberReference(Expr *expr) { /// Note that this must be called after the implicitlyAsync flag has been set, /// or implicitly async calls will not return the correct value. static bool isAsyncCall(const ApplyExpr *call) { - if (call->implicitlyAsync()) + if (call->isImplicitlyAsync()) return true; // Effectively the same as doing a @@ -739,8 +529,9 @@ static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc); static bool isSendableClosure( const AbstractClosureExpr *closure, bool forActorIsolation) { if (auto explicitClosure = dyn_cast(closure)) { - if (forActorIsolation && explicitClosure->inheritsActorContext()) + if (forActorIsolation && explicitClosure->inheritsActorContext()) { return false; + } if (explicitClosure->isUnsafeSendable()) return true; @@ -756,201 +547,137 @@ static bool isSendableClosure( } /// Determine whether the given type is suitable as a concurrent value type. -bool swift::isSendableType(const DeclContext *dc, Type type) { - class IsSendable : public TypeVisitor { - DeclContext *dc; - ProtocolDecl *SendableProto; - - public: - IsSendable(const DeclContext *dc) - : dc(const_cast(dc)) { - SendableProto = dc->getASTContext().getProtocol( - KnownProtocolKind::Sendable); - } - -#define ALWAYS_CONCURRENT_VALUE(Id) \ - bool visit##Id##Type(Id##Type *) { return true; } - -#define UNEXPECTED_TYPE(Id) ALWAYS_CONCURRENT_VALUE(Id) - - ALWAYS_CONCURRENT_VALUE(Error) - ALWAYS_CONCURRENT_VALUE(Builtin) - ALWAYS_CONCURRENT_VALUE(AnyMetatype) - ALWAYS_CONCURRENT_VALUE(Module) - - UNEXPECTED_TYPE(GenericTypeParam) - UNEXPECTED_TYPE(DependentMember) - UNEXPECTED_TYPE(GenericFunction) - -#define TYPE(Id, Parent) - - // Look through type sugar. -#define SUGARED_TYPE(Id, Parent) \ - bool visit##Id##Type(Id##Type *type) { \ - return visit(type->getSinglyDesugaredType()); \ - } - -// Unchecked and artificial types won't show up in well-formed code, -// but don't trip over them. -#define UNCHECKED_TYPE(Id, Parent) UNEXPECTED_TYPE(Id) -#define ARTIFICIAL_TYPE(Id, Parent) UNEXPECTED_TYPE(Id) - -#include "swift/AST/TypeNodes.def" - -#undef UNEXPECTED_TYPE -#undef ALWAYS_CONCURRENT_VALUE - - bool visitForConformanceCheck(TypeBase *type) { - if (!SendableProto) - return true; - - return !TypeChecker::conformsToProtocol( - Type(type), SendableProto, dc).isInvalid(); - } - - bool visitTupleType(TupleType *type) { - for (Type elementType : type->getElementTypes()) { - if (!visit(elementType)) - return false; - } - - return true; - } - - bool visitReferenceStorageType(ReferenceStorageType *type) { - return visit(type->getReferentType()); - } - - bool visitEnumType(EnumType *type) { - return visitForConformanceCheck(type); - } - - bool visitStructType(StructType *type) { - return visitForConformanceCheck(type); - } - - bool visitClassType(ClassType *type) { - return visitForConformanceCheck(type); - } +bool swift::isSendableType(ModuleDecl *module, Type type) { + auto proto = module->getASTContext().getProtocol(KnownProtocolKind::Sendable); + if (!proto) + return true; - bool visitProtocolType(ProtocolType *type) { - if (!SendableProto) - return true; + auto conformance = TypeChecker::conformsToProtocol(type, proto, module); + if (conformance.isInvalid()) + return false; - return !TypeChecker::containsProtocol( - Type(type), SendableProto, dc).isInvalid(); - } + // Look for missing Sendable conformances. + return !conformance.forEachMissingConformance(module, + [](BuiltinProtocolConformance *missing) { + return missing->getProtocol()->isSpecificProtocol( + KnownProtocolKind::Sendable); + }); +} - bool visitBoundGenericType(BoundGenericType *type) { - return visitForConformanceCheck(type); - } +/// Produce a diagnostic for a single instance of a non-Sendable type where +/// a Sendable type is required. +static bool diagnoseSingleNonSendableType( + Type type, ModuleDecl *module, SourceLoc loc, + llvm::function_ref diagnose) { + + auto behavior = DiagnosticBehavior::Unspecified; + + ASTContext &ctx = module->getASTContext(); + auto nominal = type->getAnyNominal(); + const LangOptions &langOpts = ctx.LangOpts; + if (nominal) { + // A nominal type that has not provided conformance to Sendable will be + // diagnosed based on whether its defining module was consistently + // checked for concurrency. + auto nominalModule = nominal->getParentModule(); + + if (langOpts.isSwiftVersionAtLeast(6)) { + // In Swift 6, error when the nominal type comes from a module that + // had the concurrency checks consistently applied or from this module. + // Otherwise, warn. + if (nominalModule->isConcurrencyChecked() || nominalModule == module) + behavior = DiagnosticBehavior::Unspecified; + else + behavior = DiagnosticBehavior::Warning; + } else { + // In Swift 5, warn if either the imported or importing model is + // checking for concurrency, or if the nominal type comes from this + // module. Otherwise, leave a safety hole. + if (nominalModule->isConcurrencyChecked() || + nominalModule == module || + langOpts.WarnConcurrency) + behavior = DiagnosticBehavior::Warning; + else + behavior = DiagnosticBehavior::Ignore; + } + } else if (!langOpts.isSwiftVersionAtLeast(6)) { + // Always warn in Swift 5. + behavior = DiagnosticBehavior::Warning; + } + + bool wasError = diagnose(type, behavior); + + if (type->is()) { + ctx.Diags.diagnose(loc, diag::nonsendable_function_type); + } else if (nominal && nominal->getParentModule() == module && + (isa(nominal) || isa(nominal))) { + auto note = nominal->diagnose( + diag::add_nominal_sendable_conformance, + nominal->getDescriptiveKind(), nominal->getName()); + if (nominal->getInherited().empty()) { + SourceLoc fixItLoc = nominal->getBraces().Start; + note.fixItInsert(fixItLoc, ": Sendable "); + } else { + SourceLoc fixItLoc = nominal->getInherited().back().getSourceRange().End; + fixItLoc = Lexer::getLocForEndOfToken(ctx.SourceMgr, fixItLoc); + note.fixItInsert(fixItLoc, ", Sendable"); + } + } else if (nominal) { + nominal->diagnose( + diag::non_sendable_nominal, nominal->getDescriptiveKind(), + nominal->getName()); + } + + return wasError; +} - bool visitDynamicSelfType(DynamicSelfType *type) { - return visit(type->getSelfType()); - } +bool swift::diagnoseNonSendableTypes( + Type type, ModuleDecl *module, SourceLoc loc, + llvm::function_ref diagnose) { + // If the Sendable protocol is missing, do nothing. + auto proto = module->getASTContext().getProtocol(KnownProtocolKind::Sendable); + if (!proto) + return false; - bool visitArchetypeType(ArchetypeType *type) { - return visitForConformanceCheck(type); - } + auto conformance = TypeChecker::conformsToProtocol(type, proto, module); + if (conformance.isInvalid()) { + return diagnoseSingleNonSendableType(type, module, loc, diagnose); + } - bool visitFunctionType(FunctionType *type) { - // Concurrent function types meet the requirements. - if (type->isSendable()) - return true; + // Walk the conformance, diagnosing any missing Sendable conformances. + bool anyMissing = false; + conformance.forEachMissingConformance(module, + [&](BuiltinProtocolConformance *missing) { + if (diagnoseSingleNonSendableType( + missing->getType(), module, loc, diagnose)) { + anyMissing = true; + } - // C and thin function types meeting the requirements because they - // cannot have captures. - switch (type->getExtInfo().getRepresentation()) { - case FunctionTypeRepresentation::Block: - case FunctionTypeRepresentation::Swift: return false; + }); - case FunctionTypeRepresentation::CFunctionPointer: - case FunctionTypeRepresentation::Thin: - return true; - } - } - - bool visitProtocolCompositionType(ProtocolCompositionType *type) { - if (!SendableProto) - return true; - - return !TypeChecker::containsProtocol(type, SendableProto, dc) - .isInvalid(); - } - - bool visitLValueType(LValueType *type) { - return visit(type->getObjectType()); - } - - bool visitInOutType(InOutType *type) { - return visit(type->getObjectType()); - } - } checker(dc); - - return checker.visit(type); + return anyMissing; } -static bool diagnoseNonConcurrentParameter( - SourceLoc loc, ConcurrentReferenceKind refKind, ConcreteDeclRef declRef, - ParamDecl *param, Type paramType, DiagnosticBehavior behavior) { - ASTContext &ctx = declRef.getDecl()->getASTContext(); - ctx.Diags.diagnose(loc, diag::non_concurrent_param_type, paramType) - .limitBehavior(behavior); - return false; -} - -static bool diagnoseNonConcurrentResult( - SourceLoc loc, ConcurrentReferenceKind refKind, ConcreteDeclRef declRef, - Type resultType, DiagnosticBehavior behavior) { - ASTContext &ctx = declRef.getDecl()->getASTContext(); - ctx.Diags.diagnose(loc, diag::non_concurrent_result_type, resultType) - .limitBehavior(behavior); - return false; -} - -static bool diagnoseNonConcurrentProperty( - SourceLoc loc, ConcurrentReferenceKind refKind, VarDecl *var, - Type propertyType, DiagnosticBehavior behavior) { - ASTContext &ctx = var->getASTContext(); - ctx.Diags.diagnose(loc, diag::non_concurrent_property_type, - var->getDescriptiveKind(), var->getName(), - propertyType, var->isLocalCapture()) - .limitBehavior(behavior); - return false; -} - -/// Whether we should diagnose cases where Sendable conformances are -/// missing. -bool swift::shouldDiagnoseNonSendableViolations(const LangOptions &langOpts) { - return langOpts.WarnConcurrency; -} - -bool swift::diagnoseNonConcurrentTypesInReference( - ConcreteDeclRef declRef, const DeclContext *dc, SourceLoc loc, - ConcurrentReferenceKind refKind, DiagnosticBehavior behavior) { - // Bail out immediately if we aren't supposed to do this checking. - if (!shouldDiagnoseNonSendableViolations(dc->getASTContext().LangOpts)) - return false; - +bool swift::diagnoseNonSendableTypesInReference( + ConcreteDeclRef declRef, ModuleDecl *module, SourceLoc loc, + ConcurrentReferenceKind refKind) { // For functions, check the parameter and result types. SubstitutionMap subs = declRef.getSubstitutions(); if (auto function = dyn_cast(declRef.getDecl())) { for (auto param : *function->getParameters()) { Type paramType = param->getInterfaceType().subst(subs); - if (!isSendableType(dc, paramType)) { - return diagnoseNonConcurrentParameter( - loc, refKind, declRef, param, paramType, behavior); - } + if (diagnoseNonSendableTypes( + paramType, module, loc, diag::non_sendable_param_type)) + return true; } // Check the result type of a function. if (auto func = dyn_cast(function)) { Type resultType = func->getResultInterfaceType().subst(subs); - if (!isSendableType(dc, resultType)) { - return diagnoseNonConcurrentResult(loc, refKind, declRef, resultType, - behavior); - } + if (diagnoseNonSendableTypes( + resultType, module, loc, diag::non_sendable_result_type)) + return true; } return false; @@ -960,27 +687,27 @@ bool swift::diagnoseNonConcurrentTypesInReference( Type propertyType = var->isLocalCapture() ? var->getType() : var->getValueInterfaceType().subst(subs); - if (!isSendableType(dc, propertyType)) { - return diagnoseNonConcurrentProperty(loc, refKind, var, propertyType, - behavior); - } + if (diagnoseNonSendableTypes( + propertyType, module, loc, + diag::non_sendable_property_type, + var->getDescriptiveKind(), var->getName(), + var->isLocalCapture())) + return true; } if (auto subscript = dyn_cast(declRef.getDecl())) { for (auto param : *subscript->getIndices()) { Type paramType = param->getInterfaceType().subst(subs); - if (!isSendableType(dc, paramType)) { - return diagnoseNonConcurrentParameter( - loc, refKind, declRef, param, paramType, behavior); - } + if (diagnoseNonSendableTypes( + paramType, module, loc, diag::non_sendable_param_type)) + return true; } // Check the element type of a subscript. Type resultType = subscript->getElementInterfaceType().subst(subs); - if (!isSendableType(dc, resultType)) { - return diagnoseNonConcurrentResult(loc, refKind, declRef, resultType, - behavior); - } + if (diagnoseNonSendableTypes( + resultType, module, loc, diag::non_sendable_result_type)) + return true; return false; } @@ -988,7 +715,94 @@ bool swift::diagnoseNonConcurrentTypesInReference( return false; } +void swift::diagnoseMissingSendableConformance( + SourceLoc loc, Type type, ModuleDecl *module) { + diagnoseNonSendableTypes( + type, module, loc, diag::non_sendable_type); +} + +/// Determine whether this is the main actor type. +/// FIXME: the diagnostics engine has a copy of this. +static bool isMainActor(Type type) { + if (auto nominal = type->getAnyNominal()) { + if (nominal->getName().is("MainActor") && + nominal->getParentModule()->getName() == + nominal->getASTContext().Id_Concurrency) + return true; + } + + return false; +} + +/// If this DeclContext is an actor, or an extension on an actor, return the +/// NominalTypeDecl, otherwise return null. +static NominalTypeDecl *getSelfActorDecl(const DeclContext *dc) { + auto nominal = dc->getSelfNominalTypeDecl(); + return nominal && nominal->isActor() ? nominal : nullptr; +} + namespace { + /// Describes a referenced actor variable and whether it is isolated. + struct ReferencedActor { + /// Describes whether the actor variable is isolated or, if it is not + /// isolated, why it is not isolated. + enum Kind { + /// It is isolated. + Isolated = 0, + + /// It is not an isolated parameter at all. + NonIsolatedParameter, + + // It is within a Sendable function. + SendableFunction, + + // It is within a Sendable closure. + SendableClosure, + + // It is within an 'async let' initializer. + AsyncLet, + + // It is within a global actor. + GlobalActor, + + // It is within the main actor. + MainActor, + + // It is within a nonisolated context. + NonIsolatedContext, + }; + + VarDecl * const actor; + const Kind kind; + const Type globalActor; + + ReferencedActor(VarDecl *actor, Kind kind, Type globalActor = Type()) + : actor(actor), kind(kind), globalActor(globalActor) { } + + static ReferencedActor forGlobalActor(VarDecl *actor, Type globalActor) { + Kind kind = isMainActor(globalActor) ? MainActor : GlobalActor; + return ReferencedActor(actor, kind, globalActor); + } + + bool isIsolated() const { return kind == Isolated; } + + /// Whether the variable is the "self" of an actor method. + bool isActorSelf() const { + if (!actor) + return false; + + if (!actor->isSelfParameter() && !actor->isSelfParamCapture()) + return false; + + auto dc = actor->getDeclContext(); + while (!dc->isTypeContext() && !dc->isModuleScopeContext()) + dc = dc->getParent(); + return getSelfActorDecl(dc); + } + + explicit operator bool() const { return isIsolated(); } + }; + /// Check for adherence to the actor isolation rules, emitting errors /// when actor-isolated declarations are used in an unsafe manner. class ActorIsolationChecker : public ASTWalker { @@ -1074,6 +888,10 @@ namespace { return contextStack.back(); } + ModuleDecl *getParentModule() const { + return getDeclContext()->getParentModule(); + } + /// Determine whether code in the given use context might execute /// concurrently with code in the definition context. bool mayExecuteConcurrentlyWith( @@ -1153,32 +971,34 @@ namespace { } /// Searches the applyStack from back to front for the inner-most CallExpr - /// and marks that CallExpr as implicitly async. + /// and marks that CallExpr as implicitly async. /// /// NOTE: Crashes if no CallExpr was found. - /// + /// /// For example, for global actor function `curryAdd`, if we have: /// ((curryAdd 1) 2) /// then we want to mark the inner-most CallExpr, `(curryAdd 1)`. /// /// The same goes for calls to member functions, such as calc.add(1, 2), /// aka ((add calc) 1 2), looks like this: - /// + /// /// (call_expr /// (dot_syntax_call_expr /// (declref_expr add) /// (declref_expr calc)) - /// (tuple_expr + /// (tuple_expr /// ...)) /// /// and we reach up to mark the CallExpr. - void markNearestCallAsImplicitlyAsync() { + void markNearestCallAsImplicitly( + Optional setAsync, bool setThrows = false) { assert(applyStack.size() > 0 && "not contained within an Apply?"); const auto End = applyStack.rend(); for (auto I = applyStack.rbegin(); I != End; ++I) if (auto call = dyn_cast(*I)) { - call->setImplicitlyAsync(true); + if (setAsync) call->setImplicitlyAsync(*setAsync); + if (setThrows) call->setImplicitlyThrows(true); return; } llvm_unreachable("expected a CallExpr in applyStack!"); @@ -1234,7 +1054,7 @@ namespace { if (auto lookup = dyn_cast(expr)) { checkMemberReference(lookup->getBase(), lookup->getMember(), lookup->getLoc(), - /*isEscapingPartialApply*/false, + /*partialApply*/None, lookup); return { true, expr }; } @@ -1256,11 +1076,11 @@ namespace { if (auto partialApply = decomposePartialApplyThunk( apply, Parent.getAsExpr())) { if (auto memberRef = findMemberReference(partialApply->fn)) { - // NOTE: partially-applied thunks are never annotated as + // NOTE: partially-applied thunks are never annotated as // implicitly async, regardless of whether they are escaping. checkMemberReference( partialApply->base, memberRef->first, memberRef->second, - partialApply->isEscaping); + partialApply); partialApply->base->walk(*this); @@ -1279,7 +1099,7 @@ namespace { if (auto memberRef = findMemberReference(fn)) { checkMemberReference( call->getArg(), memberRef->first, memberRef->second, - /*isEscapingPartialApply=*/false, call); + /*partialApply=*/None, call); call->getArg()->walk(*this); @@ -1315,8 +1135,9 @@ namespace { // Track the capture contexts for variables. if (auto captureList = dyn_cast(expr)) { + auto *closure = captureList->getClosureBody(); for (const auto &entry : captureList->getCaptureList()) { - captureContexts[entry.Var].push_back(captureList->getClosureBody()); + captureContexts[entry.getVar()].push_back(closure); } } @@ -1345,11 +1166,11 @@ namespace { // Remove the tracked capture contexts. if (auto captureList = dyn_cast(expr)) { for (const auto &entry : captureList->getCaptureList()) { - auto &contexts = captureContexts[entry.Var]; + auto &contexts = captureContexts[entry.getVar()]; assert(contexts.back() == captureList->getClosureBody()); contexts.pop_back(); if (contexts.empty()) - captureContexts.erase(entry.Var); + captureContexts.erase(entry.getVar()); } } @@ -1357,8 +1178,9 @@ namespace { } private: - /// If the expression is a reference to `self`, the `self` declaration. - static VarDecl *getReferencedSelf(Expr *expr) { + /// Find the directly-referenced parameter or capture of a parameter for + /// for the given expression. + static VarDecl *getReferencedParamOrCapture(Expr *expr) { // Look through identity expressions and implicit conversions. Expr *prior; do { @@ -1370,18 +1192,143 @@ namespace { expr = conversion->getSubExpr(); } while (prior != expr); - // 'super' references always act on self. + // 'super' references always act on a 'self' variable. if (auto super = dyn_cast(expr)) return super->getSelf(); - // Declaration references to 'self'. - if (auto declRef = dyn_cast(expr)) { - if (auto var = dyn_cast(declRef->getDecl())) { - if (var->isSelfParameter() || var->isSelfParamCapture()) - return var; + // Declaration references to a variable. + if (auto declRef = dyn_cast(expr)) + return dyn_cast(declRef->getDecl()); + + return nullptr; + } + + /// Find the isolated actor instance to which the given expression refers. + ReferencedActor getIsolatedActor(Expr *expr) { + // Check whether this expression is an isolated parameter or a reference + // to a capture thereof. + auto var = getReferencedParamOrCapture(expr); + bool isPotentiallyIsolated = false; + if (!var) { + isPotentiallyIsolated = false; + } else if (auto param = dyn_cast(var)) { + isPotentiallyIsolated = param->isIsolated(); + } else if (var->isSelfParamCapture()) { + // Find the "self" parameter that we captured and determine whether + // it is potentially isolated. + for (auto dc = var->getDeclContext(); dc; dc = dc->getParent()) { + if (auto func = dyn_cast(dc)) { + if (auto selfDecl = func->getImplicitSelfDecl()) { + isPotentiallyIsolated = selfDecl->isIsolated(); + break; + } + } + + if (dc->isModuleScopeContext() || dc->isTypeContext()) + break; } } + // Walk the scopes between the variable reference and the variable + // declaration to determine whether it is still isolated. + auto dc = const_cast(getDeclContext()); + for (; dc; dc = dc->getParent()) { + // If we hit the context in which the parameter is declared, we're done. + if (var && dc == var->getDeclContext()) { + if (isPotentiallyIsolated) + return ReferencedActor(var, ReferencedActor::Isolated); + } + + // If we've hit a module or type boundary, we're done. + if (dc->isModuleScopeContext() || dc->isTypeContext()) + break; + + if (auto closure = dyn_cast(dc)) { + switch (auto isolation = closure->getActorIsolation()) { + case ClosureActorIsolation::Independent: + if (isSendableClosure(closure, /*forActorIsolation=*/true)) + return ReferencedActor(var, ReferencedActor::SendableClosure); + + return ReferencedActor(var, ReferencedActor::NonIsolatedContext); + + case ClosureActorIsolation::ActorInstance: + // If the closure is isolated to the same variable, we're + // all set. + if (isPotentiallyIsolated && + (var == isolation.getActorInstance() || + (var->isSelfParamCapture() && + (isolation.getActorInstance()->isSelfParameter() || + isolation.getActorInstance()->isSelfParamCapture())))) + return ReferencedActor(var, ReferencedActor::Isolated); + + return ReferencedActor(var, ReferencedActor::NonIsolatedContext); + + case ClosureActorIsolation::GlobalActor: + return ReferencedActor::forGlobalActor( + var, isolation.getGlobalActor()); + } + } + + // Check for an 'async let' autoclosure. + if (auto autoclosure = dyn_cast(dc)) { + switch (autoclosure->getThunkKind()) { + case AutoClosureExpr::Kind::AsyncLet: + return ReferencedActor(var, ReferencedActor::AsyncLet); + + case AutoClosureExpr::Kind::DoubleCurryThunk: + case AutoClosureExpr::Kind::SingleCurryThunk: + case AutoClosureExpr::Kind::None: + break; + } + } + + if (auto func = dyn_cast(dc)) { + // @Sendable functions are nonisolated. + if (func->isSendable()) + return ReferencedActor(var, ReferencedActor::SendableFunction); + } + + // Check isolation of the context itself. We do this separately + // from the closure check because closures capture specific variables + // while general isolation is declaration-based. + switch (auto isolation = getActorIsolationOfContext(dc)) { + case ActorIsolation::Independent: + case ActorIsolation::Unspecified: + // Local functions can capture an isolated parameter. + // FIXME: This really should be modeled by getActorIsolationOfContext. + if (isa(dc) && cast(dc)->isLocalCapture()) { + // FIXME: Local functions could presumably capture an isolated + // parameter that isn't 'self'. + if (isPotentiallyIsolated && + (var->isSelfParameter() || var->isSelfParamCapture())) + continue; + } + + return ReferencedActor(var, ReferencedActor::NonIsolatedContext); + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + return ReferencedActor::forGlobalActor( + var, isolation.getGlobalActor()); + + case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: + break; + } + } + + if (isPotentiallyIsolated) + return ReferencedActor(var, ReferencedActor::NonIsolatedContext); + + return ReferencedActor(var, ReferencedActor::NonIsolatedParameter); + } + + /// If the expression is a reference to `self`, the `self` declaration. + static VarDecl *getReferencedSelf(Expr *expr) { + if (auto selfVar = getReferencedParamOrCapture(expr)) + if (selfVar->isSelfParameter() || selfVar->isSelfParamCapture()) + return selfVar; + // Not a self reference. return nullptr; } @@ -1394,6 +1341,7 @@ namespace { if (!isa(fn) && !fn->isAsyncContext()) { switch (getActorIsolation(fn)) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: case ActorIsolation::Independent: @@ -1416,27 +1364,35 @@ namespace { /// Note that the given actor member is isolated. /// @param context is allowed to be null if no context is appropriate. void noteIsolatedActorMember(ValueDecl *decl, Expr *context) { - // FIXME: Make this diagnostic more sensitive to the isolation context - // of the declaration. - if (auto func = dyn_cast(decl)) { - func->diagnose(diag::actor_isolated_sync_func, + // detect if it is a distributed actor, to provide better isolation notes + + auto isDistributedActor = false; + if (auto dc = dyn_cast(decl->getDeclContext())) + isDistributedActor = dc->isDistributedActor(); + + // FIXME: Make this diagnostic more sensitive to the isolation context of + // the declaration. + if (isDistributedActor) { + if (dyn_cast(decl)) { + // Distributed actor properties are never accessible externally. + decl->diagnose(diag::distributed_actor_isolated_property); + } else { + // it's a function or subscript + decl->diagnose(diag::distributed_actor_isolated_method_note); + } + } else if (auto func = dyn_cast(decl)) { + func->diagnose(diag::actor_isolated_sync_func, // FIXME: this is emitted wrongly for self.hello() decl->getDescriptiveKind(), decl->getName()); // was it an attempt to mutate an actor instance's isolated state? } else if (auto environment = kindOfUsage(decl, context)) { - if (isa(decl) && cast(decl)->isLet()) { - auto diag = decl->diagnose(diag::actor_isolated_let); - SourceLoc fixItLoc = - decl->getAttributeInsertionLoc(/*forModifier=*/true); - if (fixItLoc.isValid()) - diag.fixItInsert(fixItLoc, "nonisolated "); - } else if (environment.getValue() == VarRefUseEnv::Read) { + if (environment.getValue() == VarRefUseEnv::Read) decl->diagnose(diag::kind_declared_here, decl->getDescriptiveKind()); - } else { + else decl->diagnose(diag::actor_mutable_state, decl->getDescriptiveKind()); - } + } else { decl->diagnose(diag::kind_declared_here, decl->getDescriptiveKind()); } @@ -1464,7 +1420,7 @@ namespace { /// /// \returns true if we diagnosed the entity, \c false otherwise. bool diagnoseReferenceToUnsafeGlobal(ValueDecl *value, SourceLoc loc) { - if (!ctx.LangOpts.WarnConcurrency) + if (!getDeclContext()->getParentModule()->isConcurrencyChecked()) return false; // Only diagnose direct references to mutable global state. @@ -1494,7 +1450,8 @@ namespace { bool result = false; auto checkDiagnostic = [this, call, isPartialApply, &result](ValueDecl *decl, SourceLoc argLoc) { - auto isolation = ActorIsolationRestriction::forDeclaration(decl); + auto isolation = ActorIsolationRestriction::forDeclaration( + decl, getDeclContext()); switch (isolation) { case ActorIsolationRestriction::Unrestricted: case ActorIsolationRestriction::Unsafe: @@ -1510,13 +1467,14 @@ namespace { case ActorIsolationRestriction::GlobalActor: { ctx.Diags.diagnose(argLoc, diag::actor_isolated_inout_state, decl->getDescriptiveKind(), decl->getName(), - call->implicitlyAsync()); + call->isImplicitlyAsync().hasValue()); decl->diagnose(diag::kind_declared_here, decl->getDescriptiveKind()); result = true; break; } case ActorIsolationRestriction::CrossActorSelf: - case ActorIsolationRestriction::ActorSelf: { + case ActorIsolationRestriction::ActorSelf: + case ActorIsolationRestriction::DistributedActorSelf: { if (isPartialApply) { // The partially applied InoutArg is a property of actor. This // can really only happen when the property is a struct with a @@ -1533,7 +1491,7 @@ namespace { } else { ctx.Diags.diagnose(argLoc, diag::actor_isolated_inout_state, decl->getDescriptiveKind(), decl->getName(), - call->implicitlyAsync()); + call->isImplicitlyAsync().hasValue()); result = true; } break; @@ -1565,6 +1523,7 @@ namespace { // Retrieve the actor isolation of the context. switch (auto isolation = getActorIsolationOfContext(dc)) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: case ActorIsolation::Independent: case ActorIsolation::Unspecified: return isolation; @@ -1579,13 +1538,15 @@ namespace { bool isInAsynchronousContext() const { auto dc = getDeclContext(); - if (auto func = dyn_cast(dc)) + if (auto func = dyn_cast(dc)) { return func->isAsyncContext(); + } if (auto closure = dyn_cast(dc)) { if (auto type = closure->getType()) { - if (auto fnType = type->getAs()) + if (auto fnType = type->getAs()) { return fnType->isAsync(); + } } } @@ -1603,7 +1564,8 @@ namespace { /// actor-isolated member (e.g., sync function application, property access) AsyncMarkingResult tryMarkImplicitlyAsync(SourceLoc declLoc, ConcreteDeclRef concDeclRef, - Expr* context) { + Expr* context, + ImplicitActorHopTarget target) { ValueDecl *decl = concDeclRef.getDecl(); AsyncMarkingResult result = AsyncMarkingResult::NotFound; @@ -1615,7 +1577,7 @@ namespace { if (!isInAsynchronousContext()) return AsyncMarkingResult::SyncContext; - declRef->setImplicitlyAsync(true); + declRef->setImplicitlyAsync(target); result = AsyncMarkingResult::FoundAsync; } } else if (auto lookupExpr = dyn_cast_or_null(context)) { @@ -1624,12 +1586,12 @@ namespace { if (!isInAsynchronousContext()) return AsyncMarkingResult::SyncContext; - lookupExpr->setImplicitlyAsync(true); + lookupExpr->setImplicitlyAsync(target); result = AsyncMarkingResult::FoundAsync; } } - } else if (llvm::isa_and_nonnull(context) && + } else if (isa_and_nonnull(context) && isa(decl)) { // actor-isolated non-isolated-self calls are implicitly async // and thus OK. @@ -1637,7 +1599,7 @@ namespace { if (!isInAsynchronousContext()) return AsyncMarkingResult::SyncContext; - markNearestCallAsImplicitlyAsync(); + markNearestCallAsImplicitly(/*setAsync=*/target); result = AsyncMarkingResult::FoundAsync; } else if (!applyStack.empty()) { @@ -1650,13 +1612,13 @@ namespace { Expr *fn = apply->getFn()->getValueProvidingExpr(); if (auto memberRef = findMemberReference(fn)) { auto concDecl = memberRef->first; - if (decl == concDecl.getDecl() && !apply->implicitlyAsync()) { + if (decl == concDecl.getDecl() && !apply->isImplicitlyAsync()) { if (!isInAsynchronousContext()) return AsyncMarkingResult::SyncContext; // then this ValueDecl appears as the called value of the ApplyExpr. - markNearestCallAsImplicitlyAsync(); + markNearestCallAsImplicitly(/*setAsync=*/target); result = AsyncMarkingResult::FoundAsync; } } @@ -1665,8 +1627,8 @@ namespace { if (result == AsyncMarkingResult::FoundAsync) { // Check for non-concurrent types. bool problemFound = - diagnoseNonConcurrentTypesInReference( - concDeclRef, getDeclContext(), declLoc, + diagnoseNonSendableTypesInReference( + concDeclRef, getDeclContext()->getParentModule(), declLoc, ConcurrentReferenceKind::SynchronousAsAsyncCall); if (problemFound) result = AsyncMarkingResult::NotSendable; @@ -1675,6 +1637,78 @@ namespace { return result; } + enum ThrowsMarkingResult { + FoundThrows, + NotFound + }; + + ThrowsMarkingResult tryMarkImplicitlyThrows(SourceLoc declLoc, + ConcreteDeclRef concDeclRef, + Expr* context) { + + ValueDecl *decl = concDeclRef.getDecl(); + ThrowsMarkingResult result = ThrowsMarkingResult::NotFound; + + if (isa_and_nonnull(context)) { + if (auto func = dyn_cast(decl)) { + if (func->isDistributed() && !func->hasThrows()) { + // A distributed function is implicitly throwing if called from + // outside of the actor. + // + // If it already is throwing, no need to mark it implicitly so. + markNearestCallAsImplicitly( + /*setAsync=*/None, /*setThrows=*/true); + result = ThrowsMarkingResult::FoundThrows; + } + } + } else if (!applyStack.empty()) { + // Check our applyStack metadata from the traversal. + // Our goal is to identify whether the actor reference appears + // as the called value of the enclosing ApplyExpr. We cannot simply + // inspect Parent here because of expressions like (callee)() + // and the fact that the reference may be just an argument to an apply + ApplyExpr *apply = applyStack.back(); + Expr *fn = apply->getFn()->getValueProvidingExpr(); + if (auto memberRef = findMemberReference(fn)) { + auto concDecl = memberRef->first; + if (decl == concDecl.getDecl() && !apply->implicitlyThrows()) { + + if (auto func = dyn_cast(decl)) { + if (func->isDistributed() && !func->hasThrows()) { + // then this ValueDecl appears as the called value of the ApplyExpr. + markNearestCallAsImplicitly( + /*setAsync=*/None, /*setThrows=*/true); + result = ThrowsMarkingResult::FoundThrows; + } + } + } + } + } + + return result; + } + + /// Retrieve the call argument at the given index from the overall + /// call. + static Expr *getCallArgument(ApplyExpr *apply, unsigned index) { + Expr *arg = apply->getArg(); + + if (auto tuple = dyn_cast(arg)) { + if (index < tuple->getNumElements()) + return tuple->getElement(index); + + return nullptr; + } + + if (index != 0) + return nullptr; + + if (auto paren = dyn_cast(arg)) + return paren->getSubExpr(); + + return arg; + } + /// Check actor isolation for a particular application. bool checkApply(ApplyExpr *apply) { auto fnExprType = apply->getFn()->getType(); @@ -1685,73 +1719,126 @@ namespace { if (!fnType) return false; - // Handle calls to global-actor-qualified functions. - Type globalActor = fnType->getGlobalActor(); - if (!globalActor) - return false; + // The isolation of the context we're in. + Optional contextIsolation; + auto getContextIsolation = [&]() -> ActorIsolation { + if (contextIsolation) + return *contextIsolation; - auto declContext = const_cast(getDeclContext()); + auto declContext = const_cast(getDeclContext()); + contextIsolation = getInnermostIsolatedContext(declContext); + return *contextIsolation; + }; - // Check whether we are within the same isolation context, in which - // case there is nothing further to check, - auto contextIsolation = getInnermostIsolatedContext(declContext); - if (contextIsolation.isGlobalActor() && - contextIsolation.getGlobalActor()->isEqual(globalActor)) { + // If the function type is global-actor-qualified, determine whether + // we are within that global actor already. + Optional unsatisfiedIsolation; + if (Type globalActor = fnType->getGlobalActor()) { + if (!getContextIsolation().isGlobalActor() || + !getContextIsolation().getGlobalActor()->isEqual(globalActor)) { + unsatisfiedIsolation = ActorIsolation::forGlobalActor( + globalActor, /*unsafe=*/false); + } + } + + if (isa(apply) && !unsatisfiedIsolation) return false; + + // Check for isolated parameters. + Optional isolatedParamIdx; + for (unsigned paramIdx : range(fnType->getNumParams())) { + // We only care about isolated parameters. + if (!fnType->getParams()[paramIdx].isIsolated()) + continue; + + Expr *arg = getCallArgument(apply, paramIdx); + if (!arg) + continue; + + if (getIsolatedActor(arg)) + continue; + + // An isolated parameter was provided with a non-isolated argument. + // FIXME: The modeling of unsatisfiedIsolation is not great here. + // We'd be better off using something more like closure isolation + // that can talk about specific parameters. + auto nominal = arg->getType()->getAnyNominal(); + if (!nominal) { + nominal = arg->getType()->getASTContext().getProtocol( + KnownProtocolKind::Actor); + } + + unsatisfiedIsolation = ActorIsolation::forActorInstance(nominal); + isolatedParamIdx = paramIdx; + break; } - // From this point on, the only possibility is that we have an implicitly - // aynchronous call. + // If there was no unsatisfied actor isolation, we're done. + if (!unsatisfiedIsolation) + return false; // If we are not in an asynchronous context, complain. if (!isInAsynchronousContext()) { - auto isolation = ActorIsolation::forGlobalActor( - globalActor, /*unsafe=*/false); if (auto calleeDecl = apply->getCalledValue()) { ctx.Diags.diagnose( - apply->getLoc(), diag::actor_isolated_call_decl, isolation, + apply->getLoc(), diag::actor_isolated_call_decl, + *unsatisfiedIsolation, calleeDecl->getDescriptiveKind(), calleeDecl->getName(), - contextIsolation); + getContextIsolation()); calleeDecl->diagnose( diag::actor_isolated_sync_func, calleeDecl->getDescriptiveKind(), calleeDecl->getName()); } else { ctx.Diags.diagnose( - apply->getLoc(), diag::actor_isolated_call, isolation, - contextIsolation); + apply->getLoc(), diag::actor_isolated_call, *unsatisfiedIsolation, + getContextIsolation()); + } + + if (unsatisfiedIsolation->isGlobalActor()) { + noteGlobalActorOnContext( + const_cast(getDeclContext()), + unsatisfiedIsolation->getGlobalActor()); } - noteGlobalActorOnContext( - const_cast(getDeclContext()), globalActor); return true; } // Mark as implicitly async. - if (!fnType->getExtInfo().isAsync()) - apply->setImplicitlyAsync(true); + if (!fnType->getExtInfo().isAsync()) { + switch (*unsatisfiedIsolation) { + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + apply->setImplicitlyAsync( + ImplicitActorHopTarget::forGlobalActor( + unsatisfiedIsolation->getGlobalActor())); + break; - // If we don't need to check for sendability, we're done. - if (!shouldDiagnoseNonSendableViolations(ctx.LangOpts)) - return false; + case ActorIsolation::DistributedActorInstance: + case ActorIsolation::ActorInstance: + apply->setImplicitlyAsync( + ImplicitActorHopTarget::forIsolatedParameter(*isolatedParamIdx)); + break; + + case ActorIsolation::Unspecified: + case ActorIsolation::Independent: + llvm_unreachable("Not actor-isolated"); + } + } // Check for sendability of the parameter types. for (const auto ¶m : fnType->getParams()) { // FIXME: Dig out the locations of the corresponding arguments. - if (!isSendableType(getDeclContext(), param.getParameterType())) { - ctx.Diags.diagnose( - apply->getLoc(), diag::non_concurrent_param_type, - param.getParameterType()); + if (diagnoseNonSendableTypes( + param.getParameterType(), getParentModule(), apply->getLoc(), + diag::non_sendable_param_type)) return true; - } } // Check for sendability of the result type. - if (!isSendableType(getDeclContext(), fnType->getResult())) { - ctx.Diags.diagnose( - apply->getLoc(), diag::non_concurrent_result_type, - fnType->getResult()); + if (diagnoseNonSendableTypes( + fnType->getResult(), getParentModule(), apply->getLoc(), + diag::non_sendable_result_type)) return true; - } return false; } @@ -1774,14 +1861,17 @@ namespace { // A cross-actor access requires types to be concurrent-safe. if (isCrossActor) { - return diagnoseNonConcurrentTypesInReference( - valueRef, getDeclContext(), loc, + return diagnoseNonSendableTypesInReference( + valueRef, getParentModule(), loc, ConcurrentReferenceKind::CrossActor); } switch (contextIsolation) { - case ActorIsolation::ActorInstance: { - auto result = tryMarkImplicitlyAsync(loc, valueRef, context); + case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: { + auto result = tryMarkImplicitlyAsync( + loc, valueRef, context, + ImplicitActorHopTarget::forGlobalActor(globalActor)); if (result == AsyncMarkingResult::FoundAsync) return false; @@ -1800,7 +1890,9 @@ namespace { case ActorIsolation::GlobalActorUnsafe: { // Check if this decl reference is the callee of the enclosing Apply, // making it OK as an implicitly async call. - auto result = tryMarkImplicitlyAsync(loc, valueRef, context); + auto result = tryMarkImplicitlyAsync( + loc, valueRef, context, + ImplicitActorHopTarget::forGlobalActor(globalActor)); if (result == AsyncMarkingResult::FoundAsync) return false; @@ -1818,7 +1910,9 @@ namespace { } case ActorIsolation::Independent: { - auto result = tryMarkImplicitlyAsync(loc, valueRef, context); + auto result = tryMarkImplicitlyAsync( + loc, valueRef, context, + ImplicitActorHopTarget::forGlobalActor(globalActor)); if (result == AsyncMarkingResult::FoundAsync) return false; @@ -1835,7 +1929,9 @@ namespace { } case ActorIsolation::Unspecified: { - auto result = tryMarkImplicitlyAsync(loc, valueRef, context); + auto result = tryMarkImplicitlyAsync( + loc, valueRef, context, + ImplicitActorHopTarget::forGlobalActor(globalActor)); if (result == AsyncMarkingResult::FoundAsync) return false; @@ -1900,8 +1996,8 @@ namespace { if (!var->supportsMutation() || (ctx.LangOpts.EnableExperimentalFlowSensitiveConcurrentCaptures && parent.dyn_cast())) { - return diagnoseNonConcurrentTypesInReference( - valueRef, getDeclContext(), loc, + return diagnoseNonSendableTypesInReference( + valueRef, getParentModule(), loc, ConcurrentReferenceKind::LocalCapture); } @@ -1943,12 +2039,33 @@ namespace { bool checkKeyPathExpr(KeyPathExpr *keyPath) { bool diagnosed = false; + // returns None if it is not a 'let'-bound var decl. Otherwise, + // the bool indicates whether a diagnostic was emitted. + auto checkLetBoundVarDecl = [&](KeyPathExpr::Component const& component) + -> Optional { + auto decl = component.getDeclRef().getDecl(); + if (auto varDecl = dyn_cast(decl)) { + if (varDecl->isLet()) { + auto type = component.getComponentType(); + if (shouldDiagnoseExistingDataRaces(getDeclContext()) && + diagnoseNonSendableTypes( + type, getParentModule(), component.getLoc(), + diag::non_sendable_keypath_access)) + return true; + + return false; + } + } + return None; + }; + // check the components of the keypath. for (const auto &component : keyPath->getComponents()) { // The decl referred to by the path component cannot be within an actor. if (component.hasDeclRef()) { auto concDecl = component.getDeclRef(); - auto isolation = ActorIsolationRestriction::forDeclaration(concDecl); + auto isolation = ActorIsolationRestriction::forDeclaration( + concDecl, getDeclContext()); switch (isolation.getKind()) { case ActorIsolationRestriction::Unsafe: @@ -1970,10 +2087,20 @@ namespace { LLVM_FALLTHROUGH; // otherwise, it's invalid so diagnose it. case ActorIsolationRestriction::CrossActorSelf: - case ActorIsolationRestriction::ActorSelf: { + // 'let'-bound decls with this isolation are OK, just check them. + if (auto wasLetBound = checkLetBoundVarDecl(component)) { + diagnosed = wasLetBound.getValue(); + break; + } + LLVM_FALLTHROUGH; // otherwise, it's invalid so diagnose it. + + case ActorIsolationRestriction::ActorSelf: + case ActorIsolationRestriction::DistributedActorSelf: { auto decl = concDecl.getDecl(); ctx.Diags.diagnose(component.getLoc(), diag::actor_isolated_keypath_component, + /*isDistributed=*/isolation.getKind() == + ActorIsolationRestriction::DistributedActorSelf, decl->getDescriptiveKind(), decl->getName()); diagnosed = true; break; @@ -1986,19 +2113,63 @@ namespace { // such as \Type.dict[k] where k is a captured dictionary key. if (auto indexExpr = component.getIndexExpr()) { auto type = indexExpr->getType(); - if (type && shouldDiagnoseNonSendableViolations(ctx.LangOpts) - && !isSendableType(getDeclContext(), type)) { - ctx.Diags.diagnose( - component.getLoc(), diag::non_concurrent_keypath_capture, - indexExpr->getType()); + if (type && + shouldDiagnoseExistingDataRaces(getDeclContext()) && + diagnoseNonSendableTypes( + type, getParentModule(), component.getLoc(), + diag::non_sendable_keypath_capture)) diagnosed = true; - } } } return diagnosed; } + /// Check whether we are in an actor's initializer or deinitializer. + /// \returns nullptr iff we are not in such a declaration. Otherwise, + /// returns a pointer to the declaration. + static AbstractFunctionDecl const* isActorInitOrDeInitContext(const DeclContext *dc) { + while (true) { + // Non-Sendable closures are considered part of the enclosing context. + if (auto closure = dyn_cast(dc)) { + if (isSendableClosure(closure, /*forActorIsolation=*/false)) + return nullptr; + + dc = dc->getParent(); + continue; + } + + if (auto func = dyn_cast(dc)) { + // If this is an initializer or deinitializer of an actor, we're done. + if ((isa(func) || isa(func)) && + getSelfActorDecl(dc->getParent())) + return func; + + // Non-Sendable local functions are considered part of the enclosing + // context. + if (func->getDeclContext()->isLocalContext()) { + if (auto fnType = + func->getInterfaceType()->getAs()) { + if (fnType->isSendable()) + return nullptr; + + dc = dc->getParent(); + continue; + } + } + } + + return nullptr; + } + } + + static bool isConvenienceInit(AbstractFunctionDecl const* fn) { + if (auto ctor = dyn_cast_or_null(fn)) + return ctor->isConvenienceInit(); + + return false; + } + /// Check a reference to a local or global. bool checkNonMemberReference( ConcreteDeclRef valueRef, SourceLoc loc, DeclRefExpr *declRefExpr) { @@ -2011,12 +2182,14 @@ namespace { return checkLocalCapture(valueRef, loc, declRefExpr); switch (auto isolation = - ActorIsolationRestriction::forDeclaration(valueRef)) { + ActorIsolationRestriction::forDeclaration( + valueRef, getDeclContext())) { case ActorIsolationRestriction::Unrestricted: return false; case ActorIsolationRestriction::CrossActorSelf: case ActorIsolationRestriction::ActorSelf: + case ActorIsolationRestriction::DistributedActorSelf: llvm_unreachable("non-member reference into an actor"); case ActorIsolationRestriction::GlobalActorUnsafe: @@ -2037,158 +2210,178 @@ namespace { llvm_unreachable("unhandled actor isolation kind!"); } - /// Determine the reason for the given declaration context to be - /// actor-independent. - static Diag - findActorIndependentReason(DeclContext *dc) { - if (auto autoclosure = dyn_cast(dc)) { - switch (autoclosure->getThunkKind()) { - case AutoClosureExpr::Kind::AsyncLet: - return diag::actor_isolated_from_spawn_let; - - case AutoClosureExpr::Kind::DoubleCurryThunk: - case AutoClosureExpr::Kind::SingleCurryThunk: - return findActorIndependentReason(dc->getParent()); - - case AutoClosureExpr::Kind::None: - break; - } - } - - if (auto closure = dyn_cast(dc)) { - if (isSendableClosure(closure, /*forActorIsolation=*/true)) { - return diag::actor_isolated_from_concurrent_closure; - } - - return findActorIndependentReason(dc->getParent()); - } - - if (auto func = dyn_cast(dc)) { - if (func->isSendable()) - return diag::actor_isolated_from_concurrent_function; - } - - return diag::actor_isolated_self_independent_context; - } - /// Check a reference with the given base expression to the given member. /// Returns true iff the member reference refers to actor-isolated state /// in an invalid or unsafe way such that a diagnostic was emitted. bool checkMemberReference( Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc, - bool isEscapingPartialApply = false, + Optional partialApply = None, Expr *context = nullptr) { if (!base || !memberRef) return false; auto member = memberRef.getDecl(); switch (auto isolation = - ActorIsolationRestriction::forDeclaration(memberRef)) { - case ActorIsolationRestriction::Unrestricted: + ActorIsolationRestriction::forDeclaration( + memberRef, getDeclContext())) { + case ActorIsolationRestriction::Unrestricted: { + // If a cross-actor reference is to an isolated actor, it's not + // crossing actors. + if (getIsolatedActor(base)) + return false; + + // Always fine to invoke constructors from outside of actors. + if (dyn_cast(member)) + return false; + + // While the member may be unrestricted, perhaps it is in a + // distributed actor, in which case we need to diagnose it. + if (auto classDecl = dyn_cast(member->getDeclContext())) { + if (classDecl->isDistributedActor()) { + ctx.Diags.diagnose(memberLoc, diag::distributed_actor_isolated_method); + noteIsolatedActorMember(member, context); + return true; + } + } + return false; + } case ActorIsolationRestriction::CrossActorSelf: { - // If a cross-actor reference is on "self", it's not crossing actors. - auto *selfVar = getReferencedSelf(base); - auto curDC = const_cast(getDeclContext()); - if (selfVar && - getActorIsolationOfContext(curDC) == - ActorIsolation::forActorInstance( - getNearestEnclosingActorContext(getDeclContext()))) + // If a cross-actor reference is to an isolated actor, it's not + // crossing actors. + if (getIsolatedActor(base)) return false; - return diagnoseNonConcurrentTypesInReference( - memberRef, getDeclContext(), memberLoc, + return diagnoseNonSendableTypesInReference( + memberRef, getDeclContext()->getParentModule(), memberLoc, ConcurrentReferenceKind::CrossActor); } - case ActorIsolationRestriction::ActorSelf: { - // Must reference actor-isolated state on 'self'. - auto *selfVar = getReferencedSelf(base); - if (!selfVar) { - // Check for implicit async. - auto result = tryMarkImplicitlyAsync(memberLoc, memberRef, context); - if (result == AsyncMarkingResult::FoundAsync) - return false; // no problems - else if (result == AsyncMarkingResult::NotSendable) - return true; - - auto useKind = static_cast( - kindOfUsage(member, context).getValueOr(VarRefUseEnv::Read)); - - ctx.Diags.diagnose( - memberLoc, diag::actor_isolated_non_self_reference, - member->getDescriptiveKind(), - member->getName(), - isolation.getActorType() == - getNearestEnclosingActorContext(getDeclContext()), - useKind - ); - noteIsolatedActorMember(member, context); - return true; - } - - // Check whether the current context is differently-isolated. - auto curDC = const_cast(getDeclContext()); - switch (auto contextIsolation = getActorIsolationOfContext(curDC)) { - case ActorIsolation::ActorInstance: - // An escaping partial application of something that is part of - // the actor's isolated state is never permitted. - if (isEscapingPartialApply) { - ctx.Diags.diagnose( - memberLoc, diag::actor_isolated_partial_apply, - member->getDescriptiveKind(), - member->getName()); + case ActorIsolationRestriction::DistributedActorSelf: { + // distributed actor isolation is more strict; + // we do not allow any property access, or synchronous access at all. + // FIXME: We can collapse a much of this with the ActorSelf case. + bool continueToCheckingLocalIsolation = false; + // Must reference distributed actor-isolated state on 'self'. + // + // FIXME(78484431): For now, be loose about access to "self" in actor + // initializers/deinitializers for distributed actors. + // We'll want to tighten this up once we decide exactly + // how the model should go. + auto isolatedActor = getIsolatedActor(base); + if (!isolatedActor && + !(isolatedActor.isActorSelf() && + member->isInstanceMember() && + isActorInitOrDeInitContext(getDeclContext()))) { + // cross actor invocation is only ok for a distributed or static func + if (auto func = dyn_cast(member)) { + if (func->isStatic()) { + // FIXME: rather, never end up as distributed actor self isolated + // at all for static funcs + continueToCheckingLocalIsolation = true; + } else if (func->isDistributed()) { + tryMarkImplicitlyAsync( + memberLoc, memberRef, context, + ImplicitActorHopTarget::forInstanceSelf()); + tryMarkImplicitlyThrows(memberLoc, memberRef, context); + + // distributed func reference, that passes all checks, great! + continueToCheckingLocalIsolation = true; + } else { + // the func is neither static or distributed + ctx.Diags.diagnose(memberLoc, diag::distributed_actor_isolated_method); + // TODO: offer a fixit to add 'distributed' on the member; how to test fixits? See also https://github.com/apple/swift/pull/35930/files noteIsolatedActorMember(member, context); return true; } + } // end FuncDecl + + if (!continueToCheckingLocalIsolation) { + // it wasn't a function (including a distributed function), + // so we need to perform some more checks + if (auto var = dyn_cast(member)) { + // TODO: we want to remove _distributedActorIndependent in favor of nonisolated + // + // @_distributedActorIndependent decls are accessible always, + // regardless of distributed actor-isolation; e.g. actorAddress + if (member->getAttrs().hasAttribute()) + return false; + + // nonisolated decls are accessible always + if (member->getAttrs().hasAttribute()) + return false; + + // otherwise, no other properties are accessible on a distributed actor + if (!continueToCheckingLocalIsolation) { + ctx.Diags.diagnose( + memberLoc, diag::distributed_actor_isolated_non_self_reference, + member->getDescriptiveKind(), + member->getName()); + noteIsolatedActorMember(member, context); + return true; + } + } // end VarDecl + + // TODO: would have to also consider subscripts and other things + } + } // end !isolatedActor - return false; + if (!continueToCheckingLocalIsolation) + return false; + + LLVM_FALLTHROUGH; + } + + case ActorIsolationRestriction::ActorSelf: { + // Check whether the base is a reference to an isolated actor instance. + // If so, there's nothing more to check. + auto isolatedActor = getIsolatedActor(base); + if (isolatedActor) + return false; - case ActorIsolation::Unspecified: - return false; + // An instance member of an actor can be referenced from an actor's + // designated initializer or deinitializer. + if (isolatedActor.isActorSelf() && member->isInstanceMember()) + if (auto fn = isActorInitOrDeInitContext(getDeclContext())) + if (!isConvenienceInit(fn)) + return false; - case ActorIsolation::Independent: { - auto result = tryMarkImplicitlyAsync(memberLoc, memberRef, context); - if (result == AsyncMarkingResult::FoundAsync) - return false; // no problems - else if (result == AsyncMarkingResult::NotSendable) - return true; + // An escaping partial application of something that is part of + // the actor's isolated state is never permitted. + if (partialApply && partialApply->isEscaping) { + ctx.Diags.diagnose( + memberLoc, diag::actor_isolated_partial_apply, + member->getDescriptiveKind(), + member->getName()); + return true; + } - // The 'self' is for an actor-independent member, which means - // we cannot refer to actor-isolated state. - auto useKind = static_cast( - kindOfUsage(member, context).getValueOr(VarRefUseEnv::Read)); - auto diag = findActorIndependentReason(curDC); - ctx.Diags.diagnose(memberLoc, diag, member->getDescriptiveKind(), - member->getName(), useKind); - noteIsolatedActorMember(member, context); - return true; - } + // Try implicit asynchronous access. + auto implicitAsyncResult = tryMarkImplicitlyAsync( + memberLoc, memberRef, context, + ImplicitActorHopTarget::forInstanceSelf()); + if (implicitAsyncResult == AsyncMarkingResult::FoundAsync) + return false; // no problems + else if (implicitAsyncResult == AsyncMarkingResult::NotSendable) + return true; - case ActorIsolation::GlobalActor: - case ActorIsolation::GlobalActorUnsafe: { - auto result = tryMarkImplicitlyAsync(memberLoc, memberRef, context); - if (result == AsyncMarkingResult::FoundAsync) - return false; // no problems - else if (result == AsyncMarkingResult::NotSendable) - return true; + // Complain about access outside of the isolation domain. + auto useKind = static_cast( + kindOfUsage(member, context).getValueOr(VarRefUseEnv::Read)); - // The 'self' is for a member that's part of a global actor, which - // means we cannot refer to actor-isolated state. - auto useKind = static_cast( - kindOfUsage(member, context).getValueOr(VarRefUseEnv::Read)); - ctx.Diags.diagnose(memberLoc, - diag::actor_isolated_global_actor_context, - member->getDescriptiveKind(), member->getName(), - contextIsolation.getGlobalActor(), useKind, - result == AsyncMarkingResult::SyncContext - ); - noteIsolatedActorMember(member, context); - return true; - } - } - llvm_unreachable("Unhandled actor isolation"); + ctx.Diags.diagnose( + memberLoc, diag::actor_isolated_non_self_reference, + member->getDescriptiveKind(), + member->getName(), + useKind, + isolatedActor.kind - 1, + isolatedActor.globalActor); + + noteIsolatedActorMember(member, context); + // FIXME: If isolatedActor has a variable in it, refer to that with + // more detail? + return true; } case ActorIsolationRestriction::GlobalActorUnsafe: @@ -2213,6 +2406,15 @@ namespace { case ActorIsolationRestriction::Unsafe: // This case is hit when passing actor state inout to functions in some // cases. The error is emitted by diagnoseInOutArg. + auto classDecl = dyn_cast(member->getDeclContext()); + if (classDecl && classDecl->isDistributedActor()) { + auto funcDecl = dyn_cast(member); + if (funcDecl && !funcDecl->isStatic()) { + member->diagnose(diag::distributed_actor_isolated_method); + return true; + } + } + return false; } llvm_unreachable("unhandled actor isolation kind!"); @@ -2232,6 +2434,7 @@ namespace { return getExplicitGlobalActor(closure); } + public: /// Determine the isolation of a particular closure. /// /// This function assumes that enclosing closures have already had their @@ -2240,9 +2443,6 @@ namespace { AbstractClosureExpr *closure) { // If the closure specifies a global actor, use it. if (auto explicitClosure = dyn_cast(closure)) { - if (explicitClosure->getAttrs().hasAttribute()) - return ClosureActorIsolation::forIndependent(); - if (Type globalActorType = resolveGlobalActorType(explicitClosure)) return ClosureActorIsolation::forGlobalActor(globalActorType); @@ -2274,25 +2474,27 @@ namespace { return ClosureActorIsolation::forGlobalActor(globalActorType); } - case ActorIsolation::ActorInstance: { + case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: { SmallVector localCaptures; closure->getCaptureInfo().getLocalCaptures(localCaptures); for (const auto &localCapture : localCaptures) { if (localCapture.isDynamicSelfMetadata()) continue; - auto var = dyn_cast_or_null(localCapture.getDecl()); - if (!var) + auto param = dyn_cast_or_null(localCapture.getDecl()); + if (!param) continue; - // If we have captured the 'self' parameter, the closure is isolated + // If we have captured an isolated parameter, the closure is isolated // to that actor instance. - if (var->isSelfParameter()) { - return ClosureActorIsolation::forActorInstance(var); + if (param->isIsolated()) { + return ClosureActorIsolation::forActorInstance(param); } } - // When 'self' is not captured, this closure is actor-independent. + // When no actor instance is not captured, this closure is + // actor-independent. return ClosureActorIsolation::forIndependent(); } } @@ -2343,9 +2545,15 @@ void swift::checkFunctionActorIsolation(AbstractFunctionDecl *decl) { if (auto body = decl->getBody()) { body->walk(checker); } - if (auto ctor = dyn_cast(decl)) + if (auto ctor = dyn_cast(decl)) { if (auto superInit = ctor->getSuperInitCall()) superInit->walk(checker); + } + if (auto attr = decl->getAttrs().getAttribute()) { + if (auto func = dyn_cast(decl)) { + checkDistributedFunction(func, /*diagnose=*/true); + } + } } void swift::checkInitializerActorIsolation(Initializer *init, Expr *expr) { @@ -2360,11 +2568,17 @@ void swift::checkEnumElementActorIsolation( } void swift::checkPropertyWrapperActorIsolation( - PatternBindingDecl *binding, Expr *expr) { - ActorIsolationChecker checker(binding->getDeclContext()); + VarDecl *wrappedVar, Expr *expr) { + ActorIsolationChecker checker(wrappedVar->getDeclContext()); expr->walk(checker); } +ClosureActorIsolation +swift::determineClosureActorIsolation(AbstractClosureExpr *closure) { + ActorIsolationChecker checker(closure->getParent()); + return checker.determineClosureIsolation(closure); +} + /// Determine actor isolation solely from attributes. /// /// \returns the actor isolation determined from attributes alone (with no @@ -2374,14 +2588,11 @@ static Optional getIsolationFromAttributes( const Decl *decl, bool shouldDiagnose = true, bool onlyExplicit = false) { // Look up attributes on the declaration that can affect its actor isolation. // If any of them are present, use that attribute. - auto independentAttr = decl->getAttrs().getAttribute(); auto nonisolatedAttr = decl->getAttrs().getAttribute(); auto globalActorAttr = decl->getGlobalActorAttr(); // Remove implicit attributes if we only care about explicit ones. if (onlyExplicit) { - if (independentAttr && independentAttr->isImplicit()) - independentAttr = nullptr; if (nonisolatedAttr && nonisolatedAttr->isImplicit()) nonisolatedAttr = nullptr; if (globalActorAttr && globalActorAttr->first->isImplicit()) @@ -2389,8 +2600,7 @@ static Optional getIsolationFromAttributes( } unsigned numIsolationAttrs = - (nonisolatedAttr ? 1 : 0) + (independentAttr ? 1 : 0) + - (globalActorAttr ? 1 : 0); + (nonisolatedAttr ? 1 : 0) + (globalActorAttr ? 1 : 0); if (numIsolationAttrs == 0) return None; @@ -2406,22 +2616,12 @@ static Optional getIsolationFromAttributes( } if (globalActorAttr) { - StringRef nonisolatedAttrName; - SourceRange nonisolatedRange; - if (independentAttr) { - nonisolatedAttrName = independentAttr->getAttrName(); - nonisolatedRange = independentAttr->getRangeWithAt(); - } else { - nonisolatedAttrName = nonisolatedAttr->getAttrName(); - nonisolatedRange = nonisolatedAttr->getRangeWithAt(); - } - if (shouldDiagnose) { decl->diagnose( diag::actor_isolation_multiple_attr, decl->getDescriptiveKind(), - name, nonisolatedAttrName, + name, nonisolatedAttr->getAttrName(), globalActorAttr->second->getName().str()) - .highlight(nonisolatedRange) + .highlight(nonisolatedAttr->getRangeWithAt()) .highlight(globalActorAttr->first->getRangeWithAt()); } } @@ -2433,12 +2633,6 @@ static Optional getIsolationFromAttributes( return ActorIsolation::forIndependent(); } - // If the declaration is explicitly marked @actorIndependent, report it as - // independent. - if (independentAttr) { - return ActorIsolation::forIndependent(); - } - // If the declaration is marked with a global actor, report it as being // part of that global actor. if (globalActorAttr) { @@ -2498,6 +2692,7 @@ static Optional getIsolationFromWitnessedRequirements( auto requirementIsolation = getActorIsolation(requirement); switch (requirementIsolation) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: case ActorIsolation::Unspecified: continue; @@ -2525,10 +2720,11 @@ static Optional getIsolationFromWitnessedRequirements( auto isolation = std::get<1>(isolated); switch (isolation) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: llvm_unreachable("protocol requirements cannot be actor instances"); case ActorIsolation::Independent: - // We only need one @actorIndependent. + // We only need one nonisolated. if (sawActorIndependent) return true; @@ -2576,6 +2772,7 @@ static Optional getIsolationFromConformances( nominal->getLocalProtocols(ConformanceLookupKind::NonStructural)) { switch (auto protoIsolation = getActorIsolation(proto)) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: case ActorIsolation::Unspecified: case ActorIsolation::Independent: break; @@ -2626,6 +2823,7 @@ static Optional getIsolationFromWrappers( switch (isolation) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: case ActorIsolation::Unspecified: case ActorIsolation::Independent: break; @@ -2647,16 +2845,6 @@ static Optional getIsolationFromWrappers( return foundIsolation; } -// Check whether a declaration is an asynchronous handler. -static bool isAsyncHandler(ValueDecl *value) { - if (auto func = dyn_cast(value)) { - if (func->isAsyncHandler()) - return true; - } - - return false; -} - namespace { /// Describes how actor isolation is propagated to a member, if at all. @@ -2700,9 +2888,11 @@ static Optional getMemberIsolationPropagation( case DeclKind::PatternBinding: case DeclKind::EnumCase: case DeclKind::EnumElement: - case DeclKind::Constructor: return MemberIsolationPropagation::GlobalActor; + case DeclKind::Constructor: + return MemberIsolationPropagation::AnyIsolation; + case DeclKind::Func: case DeclKind::Accessor: case DeclKind::Subscript: @@ -2712,11 +2902,141 @@ static Optional getMemberIsolationPropagation( } } +/// Given a property, determine the isolation when it part of a wrapped +/// property. +static ActorIsolation getActorIsolationFromWrappedProperty(VarDecl *var) { + // If this is a variable with a property wrapper, infer from the property + // wrapper's wrappedValue. + if (auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0)) { + if (auto wrappedValue = wrapperInfo.valueVar) { + if (auto isolation = getActorIsolation(wrappedValue)) + return isolation; + } + } + + // If this is the backing storage for a property wrapper, infer from the + // type of the outermost property wrapper. + if (auto originalVar = var->getOriginalWrappedProperty( + PropertyWrapperSynthesizedPropertyKind::Backing)) { + if (auto backingType = + originalVar->getPropertyWrapperBackingPropertyType()) { + if (auto backingNominal = backingType->getAnyNominal()) { + if (!isa(backingNominal) || + !cast(backingNominal)->isActor()) { + if (auto isolation = getActorIsolation(backingNominal)) + return isolation; + } + } + } + } + + // If this is the projected property for a property wrapper, infer from + // the property wrapper's projectedValue. + if (auto originalVar = var->getOriginalWrappedProperty( + PropertyWrapperSynthesizedPropertyKind::Projection)) { + if (auto wrapperInfo = + originalVar->getAttachedPropertyWrapperTypeInfo(0)) { + if (auto projectedValue = wrapperInfo.projectedValueVar) { + if (auto isolation = getActorIsolation(projectedValue)) + return isolation; + } + } + } + + return ActorIsolation::forUnspecified(); +} + +/// Check rules related to global actor attributes on a class declaration. +/// +/// \returns true if an error occurred. +static bool checkClassGlobalActorIsolation( + ClassDecl *classDecl, ActorIsolation isolation) { + assert(isolation.isGlobalActor()); + + // A class can only be annotated with a global actor if it has no + // superclass, the superclass is annotated with the same global actor, or + // the superclass is NSObject. A subclass of a global-actor-annotated class + // must be isolated to the same global actor. + auto superclassDecl = classDecl->getSuperclassDecl(); + if (!superclassDecl) + return false; + + if (superclassDecl->isNSObject()) + return false; + + // Ignore actors outright. They'll be diagnosed later. + if (classDecl->isActor() || superclassDecl->isActor()) + return false; + + // Check the superclass's isolation. + auto superIsolation = getActorIsolation(superclassDecl); + switch (superIsolation) { + case ActorIsolation::Unspecified: + case ActorIsolation::Independent: + return false; + + case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: + // This is an error that will be diagnosed later. Ignore it here. + return false; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: { + // If the the global actors match, we're fine. + Type superclassGlobalActor = superIsolation.getGlobalActor(); + auto module = classDecl->getParentModule(); + SubstitutionMap subsMap = classDecl->getDeclaredInterfaceType() + ->getSuperclassForDecl(superclassDecl) + ->getContextSubstitutionMap(module, superclassDecl); + Type superclassGlobalActorInSub = superclassGlobalActor.subst(subsMap); + if (isolation.getGlobalActor()->isEqual(superclassGlobalActorInSub)) + return false; + + break; + } + } + + // Complain about the mismatch. + classDecl->diagnose( + diag::actor_isolation_superclass_mismatch, isolation, + classDecl->getName(), superIsolation, superclassDecl->getName()); + return true; +} + ActorIsolation ActorIsolationRequest::evaluate( Evaluator &evaluator, ValueDecl *value) const { + // If this declaration has actor-isolated "self", it's isolated to that + // actor. + if (evaluateOrDefault(evaluator, HasIsolatedSelfRequest{value}, false)) { + auto actor = value->getDeclContext()->getSelfNominalTypeDecl(); + assert(actor && "could not find the actor that 'self' is isolated to"); + return actor->isDistributedActor() + ? ActorIsolation::forDistributedActorInstance(actor) + : ActorIsolation::forActorInstance(actor); + } + // If this declaration has one of the actor isolation attributes, report // that. if (auto isolationFromAttr = getIsolationFromAttributes(value)) { + // Nonisolated declarations must involve Sendable types. + if (*isolationFromAttr == ActorIsolation::Independent) { + SubstitutionMap subs; + if (auto genericEnv = value->getInnermostDeclContext() + ->getGenericEnvironmentOfContext()) { + subs = genericEnv->getForwardingSubstitutionMap(); + } + diagnoseNonSendableTypesInReference( + ConcreteDeclRef(value, subs), + value->getDeclContext()->getParentModule(), value->getLoc(), + ConcurrentReferenceKind::Nonisolated); + } + + // Classes with global actors have additional rules regarding inheritance. + if (isolationFromAttr->isGlobalActor()) { + if (auto classDecl = dyn_cast(value)) + checkClassGlobalActorIsolation(classDecl, *isolationFromAttr); + } + return *isolationFromAttr; } @@ -2724,22 +3044,27 @@ ActorIsolation ActorIsolationRequest::evaluate( // overridden by other inference rules. ActorIsolation defaultIsolation = ActorIsolation::forUnspecified(); - // A @Sendable function is assumed to be actor-independent. if (auto func = dyn_cast(value)) { + // A @Sendable function is assumed to be actor-independent. if (func->isSendable()) { defaultIsolation = ActorIsolation::forIndependent(); } - } - // Check for instance members and initializers of actor types, - // which are part of actor-isolated state. - if (auto nominal = value->getDeclContext()->getSelfNominalTypeDecl()) { - if (nominal->isActor() && - (value->isInstanceMember() || isa(value))) { - defaultIsolation = ActorIsolation::forActorInstance(nominal); + if (auto nominal = value->getDeclContext()->getSelfNominalTypeDecl()) { + /// Unless the function is static, it is isolated to the dist actor + if (nominal->isDistributedActor() && !func->isStatic()) { + defaultIsolation = ActorIsolation::forDistributedActorInstance(nominal); + } } } + // An actor's convenience init is assumed to be actor-independent. + if (auto nominal = value->getDeclContext()->getSelfNominalTypeDecl()) + if (nominal->isActor()) + if (auto ctor = dyn_cast(value)) + if (ctor->isConvenienceInit()) + defaultIsolation = ActorIsolation::forIndependent(); + // Function used when returning an inferred isolation. auto inferredIsolation = [&]( ActorIsolation inferred, bool onlyGlobal = false) { @@ -2751,8 +3076,7 @@ ActorIsolation ActorIsolationRequest::evaluate( if (onlyGlobal) return ActorIsolation::forUnspecified(); - value->getAttrs().add(new (ctx) ActorIndependentAttr( - ActorIndependentKind::Safe, /*IsImplicit=*/true)); + value->getAttrs().add(new (ctx) NonisolatedAttr(/*IsImplicit=*/true)); break; case ActorIsolation::GlobalActorUnsafe: @@ -2766,6 +3090,17 @@ ActorIsolation ActorIsolationRequest::evaluate( break; } + case ActorIsolation::DistributedActorInstance: { + /// 'distributed actor independent' implies 'nonisolated' + if (value->isDistributedActorIndependent()) { + // TODO: rename 'distributed actor independent' to 'distributed(nonisolated)' + value->getAttrs().add( + new (ctx) DistributedActorIndependentAttr(/*IsImplicit=*/true)); + value->getAttrs().add( + new (ctx) NonisolatedAttr(/*IsImplicit=*/true)); + } + break; + } case ActorIsolation::ActorInstance: case ActorIsolation::Unspecified: if (onlyGlobal) @@ -2774,25 +3109,42 @@ ActorIsolation ActorIsolationRequest::evaluate( // Nothing to do. break; } + return inferred; }; + // If this is a "defer" function body, inherit the global actor isolation + // from its context. + if (auto func = dyn_cast(value)) { + if (func->isDeferBody()) { + switch (auto enclosingIsolation = + getActorIsolationOfContext(func->getDeclContext())) { + case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: + case ActorIsolation::Independent: + case ActorIsolation::Unspecified: + // Do nothing. + break; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + return inferredIsolation(enclosingIsolation); + } + } + } + // If the declaration overrides another declaration, it must have the same // actor isolation. if (auto overriddenValue = value->getOverriddenDecl()) { - // Ignore the overridden declaration's isolation for an async handler, - // because async handlers dispatch to wherever they need to be. - if (!isAsyncHandler(value)) { - auto isolation = getActorIsolation(overriddenValue); - SubstitutionMap subs; - - if (Type selfType = value->getDeclContext()->getSelfInterfaceType()) { - subs = selfType->getMemberSubstitutionMap( - value->getModuleContext(), overriddenValue); - } + auto isolation = getActorIsolation(overriddenValue); + SubstitutionMap subs; - return inferredIsolation(isolation.subst(subs)); + if (Type selfType = value->getDeclContext()->getSelfInterfaceType()) { + subs = selfType->getMemberSubstitutionMap( + value->getModuleContext(), overriddenValue); } + + return inferredIsolation(isolation.subst(subs)); } // If this is an accessor, use the actor isolation of its storage @@ -2802,43 +3154,8 @@ ActorIsolation ActorIsolationRequest::evaluate( } if (auto var = dyn_cast(value)) { - // If this is a variable with a property wrapper, infer from the property - // wrapper's wrappedValue. - if (auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0)) { - if (auto wrappedValue = wrapperInfo.valueVar) { - if (auto isolation = getActorIsolation(wrappedValue)) - return inferredIsolation(isolation); - } - } - - // If this is the backing storage for a property wrapper, infer from the - // type of the outermost property wrapper. - if (auto originalVar = var->getOriginalWrappedProperty( - PropertyWrapperSynthesizedPropertyKind::Backing)) { - if (auto backingType = - originalVar->getPropertyWrapperBackingPropertyType()) { - if (auto backingNominal = backingType->getAnyNominal()) { - if (!isa(backingNominal) || - !cast(backingNominal)->isActor()) { - if (auto isolation = getActorIsolation(backingNominal)) - return inferredIsolation(isolation); - } - } - } - } - - // If this is the projected property for a property wrapper, infer from - // the property wrapper's projectedValue. - if (auto originalVar = var->getOriginalWrappedProperty( - PropertyWrapperSynthesizedPropertyKind::Projection)) { - if (auto wrapperInfo = - originalVar->getAttachedPropertyWrapperTypeInfo(0)) { - if (auto projectedValue = wrapperInfo.projectedValueVar) { - if (auto isolation = getActorIsolation(projectedValue)) - return inferredIsolation(isolation); - } - } - } + if (auto isolation = getActorIsolationFromWrappedProperty(var)) + return inferredIsolation(isolation); } if (shouldInferAttributeInContext(value->getDeclContext())) { @@ -2913,6 +3230,72 @@ ActorIsolation ActorIsolationRequest::evaluate( return defaultIsolation; } +bool HasIsolatedSelfRequest::evaluate( + Evaluator &evaluator, ValueDecl *value) const { + // Only ever applies to members of actors. + auto dc = value->getDeclContext(); + auto selfTypeDecl = dc->getSelfNominalTypeDecl(); + if (!selfTypeDecl || !selfTypeDecl->isAnyActor()) + return false; + + // For accessors, consider the storage declaration. + if (auto accessor = dyn_cast(value)) + value = accessor->getStorage(); + + // Check whether this member can be isolated to an actor at all. + auto memberIsolation = getMemberIsolationPropagation(value); + if (!memberIsolation) + return false; + + switch (*memberIsolation) { + case MemberIsolationPropagation::GlobalActor: + return false; + + case MemberIsolationPropagation::AnyIsolation: + break; + } + + // Check whether the default isolation was overridden by any attributes on + // this declaration. + if (getIsolationFromAttributes(value)) + return false; + + // ... or its extension context. + if (auto ext = dyn_cast(dc)) { + if (getIsolationFromAttributes(ext)) + return false; + } + + // If this is a variable, check for a property wrapper that alters its + // isolation. + if (auto var = dyn_cast(value)) { + switch (auto isolation = getActorIsolationFromWrappedProperty(var)) { + case ActorIsolation::Independent: + case ActorIsolation::Unspecified: + break; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + return false; + + case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: + if (isolation.getActor() != selfTypeDecl) + return false; + break; + } + } + + // In an actor's convenience init, self is not isolated. + if (auto ctor = dyn_cast(value)) { + if (ctor->isConvenienceInit()) { + return false; + } + } + + return true; +} + void swift::checkOverrideActorIsolation(ValueDecl *value) { if (isa(value)) return; @@ -2921,10 +3304,6 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) { if (!overridden) return; - // Actor isolation doesn't matter for async handlers. - if (isAsyncHandler(value)) - return; - // Determine the actor isolation of this declaration. auto isolation = getActorIsolation(value); @@ -2945,10 +3324,15 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) { if (isolation == overriddenIsolation) return; + // If both are actor-instance isolated, we're done. + if (isolation.getKind() == overriddenIsolation.getKind() && + (isolation.getKind() == ActorIsolation::ActorInstance || + isolation.getKind() == ActorIsolation::DistributedActorInstance)) + return; + // If the overridden declaration is from Objective-C with no actor annotation, - // and the overriding declaration has been placed in a global actor, allow it. - if (overridden->hasClangNode() && !overriddenIsolation && - isolation.isGlobalActor()) + // allow it. + if (overridden->hasClangNode() && !overriddenIsolation) return; // If the overridden declaration uses an unsafe global actor, we can do @@ -2960,6 +3344,7 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) { return; case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: // Diagnose below. break; @@ -2983,6 +3368,7 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) { return; case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: case ActorIsolation::Independent: // Diagnose below. break; @@ -3009,12 +3395,9 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) { bool swift::contextUsesConcurrencyFeatures(const DeclContext *dc) { while (!dc->isModuleScopeContext()) { if (auto closure = dyn_cast(dc)) { - // A closure with an explicit global actor or @actorIndependent + // A closure with an explicit global actor or nonindependent // uses concurrency features. if (auto explicitClosure = dyn_cast(closure)) { - if (explicitClosure->getAttrs().hasAttribute()) - return true; - if (getExplicitGlobalActor(const_cast(explicitClosure))) return true; } @@ -3037,11 +3420,6 @@ bool swift::contextUsesConcurrencyFeatures(const DeclContext *dc) { if (func->hasAsync() || func->isSendable()) return true; - // If there is an explicit @asyncHandler, we're using concurrency - // features. - if (func->getAttrs().hasAttribute()) - return true; - // If we're in an accessor declaration, also check the storage // declaration. if (auto accessor = dyn_cast(decl)) { @@ -3067,27 +3445,47 @@ bool swift::contextUsesConcurrencyFeatures(const DeclContext *dc) { } static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) { - if (dc->getASTContext().LangOpts.WarnConcurrency) + if (dc->getParentModule()->isConcurrencyChecked()) return true; return contextUsesConcurrencyFeatures(dc); } -static DiagnosticBehavior toDiagnosticBehavior(const LangOptions &langOpts, - SendableCheck check, - bool diagnoseImplicit = false) { +/// Limit the diagnostic behavior used when performing checks for the Sendable +/// instance storage of Sendable types. +/// +/// \returns a pair containing the diagnostic behavior that should be used +/// for this diagnostic, as well as a Boolean value indicating whether to +/// treat this as an error. +static std::pair limitSendableInstanceBehavior( + const LangOptions &langOpts, SendableCheck check, + DiagnosticBehavior suggestedBehavior) { + // Is an error suggested? + bool suggestedError = suggestedBehavior == DiagnosticBehavior::Unspecified || + suggestedBehavior == DiagnosticBehavior::Error; switch (check) { - case SendableCheck::ImpliedByStandardProtocol: - return shouldDiagnoseNonSendableViolations(langOpts) - ? DiagnosticBehavior::Warning - : DiagnosticBehavior::Ignore; - case SendableCheck::Explicit: - return DiagnosticBehavior::Unspecified; case SendableCheck::Implicit: - return (diagnoseImplicit && - shouldDiagnoseNonSendableViolations(langOpts)) - ? DiagnosticBehavior::Unspecified - : DiagnosticBehavior::Ignore; + // For implicit checks, we always ignore the diagnostic and fail. + return std::make_pair(DiagnosticBehavior::Ignore, true); + + case SendableCheck::Explicit: + // Bump warnings up to errors due to explicit Sendable conformance. + if (suggestedBehavior == DiagnosticBehavior::Warning) + return std::make_pair(DiagnosticBehavior::Unspecified, true); + + return std::make_pair(suggestedBehavior, suggestedError); + + case SendableCheck::ImpliedByStandardProtocol: + // If we aren't in Swift 6, downgrade diagnostics. + if (!langOpts.isSwiftVersionAtLeast(6)) { + if (langOpts.WarnConcurrency && + suggestedBehavior != DiagnosticBehavior::Ignore) + return std::make_pair(DiagnosticBehavior::Warning, false); + + return std::make_pair(DiagnosticBehavior::Ignore, false); + } + + return std::make_pair(suggestedBehavior, suggestedError); } } @@ -3097,34 +3495,49 @@ static bool checkSendableInstanceStorage( NominalTypeDecl *nominal, DeclContext *dc, SendableCheck check) { // Stored properties of structs and classes must have // Sendable-conforming types. - const auto &langOpts = dc->getASTContext().LangOpts; - auto behavior = toDiagnosticBehavior(langOpts, check); + const LangOptions &langOpts = dc->getASTContext().LangOpts; bool invalid = false; if (isa(nominal) || isa(nominal)) { auto classDecl = dyn_cast(nominal); for (auto property : nominal->getStoredProperties()) { if (classDecl && property->supportsMutation()) { - if (behavior == DiagnosticBehavior::Ignore) + if (check == SendableCheck::Implicit) return true; + + auto action = limitSendableInstanceBehavior( + langOpts, check, DiagnosticBehavior::Unspecified); + property->diagnose(diag::concurrent_value_class_mutable_property, property->getName(), nominal->getDescriptiveKind(), nominal->getName()) - .limitBehavior(behavior); - invalid = true; + .limitBehavior(action.first); + invalid = invalid || action.second; continue; } - auto propertyType = dc->mapTypeIntoContext(property->getInterfaceType()); - if (!isSendableType(dc, propertyType)) { - if (behavior == DiagnosticBehavior::Ignore) - return true; - property->diagnose(diag::non_concurrent_type_member, - false, property->getName(), - nominal->getDescriptiveKind(), nominal->getName(), - propertyType) - .limitBehavior(behavior); + // Check that the property is Sendable. + auto propertyType = dc->mapTypeIntoContext(property->getInterfaceType()) + ->getRValueType()->getReferenceStorageReferent(); + bool diagnosedProperty = diagnoseNonSendableTypes( + propertyType, dc->getParentModule(), property->getLoc(), + [&](Type type, DiagnosticBehavior suggestedBehavior) { + auto action = limitSendableInstanceBehavior( + langOpts, check, suggestedBehavior); + property->diagnose(diag::non_concurrent_type_member, + false, property->getName(), + nominal->getDescriptiveKind(), + nominal->getName(), + propertyType) + .limitBehavior(action.first); + return action.second; + }); + + if (diagnosedProperty) { invalid = true; - continue; + + // For implicit checks, bail out early if anything failed. + if (check == SendableCheck::Implicit) + return true; } } @@ -3139,18 +3552,29 @@ static bool checkSendableInstanceStorage( if (!element->hasAssociatedValues()) continue; + // Check that the associated value type is Sendable. auto elementType = dc->mapTypeIntoContext( element->getArgumentInterfaceType()); - if (!isSendableType(dc, elementType)) { - if (behavior == DiagnosticBehavior::Ignore) - return true; - element->diagnose(diag::non_concurrent_type_member, - true, element->getName(), - nominal->getDescriptiveKind(), nominal->getName(), - elementType) - .limitBehavior(behavior); + bool diagnosedElement = diagnoseNonSendableTypes( + elementType, dc->getParentModule(), element->getLoc(), + [&](Type type, DiagnosticBehavior suggestedBehavior) { + auto action = limitSendableInstanceBehavior( + langOpts, check, suggestedBehavior); + element->diagnose(diag::non_concurrent_type_member, + true, element->getName(), + nominal->getDescriptiveKind(), + nominal->getName(), + type) + .limitBehavior(action.first); + return action.second; + }); + + if (diagnosedElement) { invalid = true; - continue; + + // For implicit checks, bail out early if anything failed. + if (check == SendableCheck::Implicit) + return true; } } } @@ -3173,10 +3597,27 @@ bool swift::checkSendableConformance( return false; } + // Global-actor-isolated types can be Sendable. We do not check the + // instance data because it's all isolated to the global actor. + switch (getActorIsolation(nominal)) { + case ActorIsolation::Unspecified: + case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: + case ActorIsolation::Independent: + break; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + return false; + } + // Sendable can only be used in the same source file. auto conformanceDecl = conformanceDC->getAsDecl(); - auto behavior = toDiagnosticBehavior( - nominal->getASTContext().LangOpts, check, /*diagnoseImplicit=*/true); + const LangOptions &langOpts = conformanceDC->getASTContext().LangOpts; + DiagnosticBehavior behavior; + bool diagnosticCausesFailure; + std::tie(behavior, diagnosticCausesFailure) = limitSendableInstanceBehavior( + langOpts, check, DiagnosticBehavior::Unspecified); if (!conformanceDC->getParentSourceFile() || conformanceDC->getParentSourceFile() != nominal->getParentSourceFile()) { conformanceDecl->diagnose(diag::concurrent_value_outside_source_file, @@ -3184,18 +3625,18 @@ bool swift::checkSendableConformance( nominal->getName()) .limitBehavior(behavior); - if (behavior != DiagnosticBehavior::Warning) + if (diagnosticCausesFailure) return true; } if (classDecl) { // An non-final class cannot conform to `Sendable`. - if (!classDecl->isFinal()) { + if (!classDecl->isSemanticallyFinal()) { classDecl->diagnose(diag::concurrent_value_nonfinal_class, classDecl->getName()) .limitBehavior(behavior); - if (behavior != DiagnosticBehavior::Warning) + if (diagnosticCausesFailure) return true; } @@ -3210,7 +3651,7 @@ bool swift::checkSendableConformance( classDecl->getName()) .limitBehavior(behavior); - if (behavior != DiagnosticBehavior::Warning) + if (diagnosticCausesFailure) return true; } } @@ -3222,23 +3663,16 @@ bool swift::checkSendableConformance( NormalProtocolConformance *GetImplicitSendableRequest::evaluate( Evaluator &evaluator, NominalTypeDecl *nominal) const { - // Only structs and enums can get implicit Sendable conformances. - if (!isa(nominal) && !isa(nominal)) + // Protocols never get implicit Sendable conformances. + if (isa(nominal)) return nullptr; - // Public, non-frozen structs and enums defined in Swift don't get implicit - // Sendable conformances. - if (!nominal->getASTContext().LangOpts.EnableInferPublicSendable && - nominal->getFormalAccessScope( - /*useDC=*/nullptr, - /*treatUsableFromInlineAsPublic=*/true).isPublic() && - !(nominal->hasClangNode() || - nominal->getAttrs().hasAttribute() || - nominal->getAttrs().hasAttribute())) { + // Actor types are always Sendable; they don't get it via this path. + auto classDecl = dyn_cast(nominal); + if (classDecl && classDecl->isActor()) return nullptr; - } - // Check the context in which the conformance occurs. + // Check whether we can infer conformance at all. if (auto *file = dyn_cast(nominal->getModuleScopeContext())) { switch (file->getKind()) { case FileUnitKind::Source: @@ -3272,23 +3706,66 @@ NormalProtocolConformance *GetImplicitSendableRequest::evaluate( return nullptr; } - // Check the instance storage for Sendable conformance. - if (checkSendableInstanceStorage( - nominal, nominal, SendableCheck::Implicit)) + // Local function to form the implicit conformance. + auto formConformance = [&]() -> NormalProtocolConformance * { + ASTContext &ctx = nominal->getASTContext(); + auto proto = ctx.getProtocol(KnownProtocolKind::Sendable); + if (!proto) + return nullptr; + + auto conformance = ctx.getConformance( + nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(), + nominal, ProtocolConformanceState::Complete, false); + conformance->setSourceKindAndImplyingConformance( + ConformanceEntryKind::Synthesized, nullptr); + + nominal->registerProtocolConformance(conformance, /*synthesized=*/true); + return conformance; + }; + + // A non-protocol type with a global actor is implicitly Sendable. + if (nominal->getGlobalActorAttr()) { + // If this is a class, check the superclass. We won't infer Sendable + // if the superclass is already Sendable, to avoid introducing redundant + // conformances. + if (classDecl) { + if (Type superclass = classDecl->getSuperclass()) { + auto classModule = classDecl->getParentModule(); + if (TypeChecker::conformsToKnownProtocol( + classDecl->mapTypeIntoContext(superclass), + KnownProtocolKind::Sendable, + classModule)) + return nullptr; + } + } + + // Form the implicit conformance to Sendable. + return formConformance(); + } + + // Only structs and enums can get implicit Sendable conformances by + // considering their instance data. + if (!isa(nominal) && !isa(nominal)) return nullptr; - ASTContext &ctx = nominal->getASTContext(); - auto proto = ctx.getProtocol(KnownProtocolKind::Sendable); - if (!proto) + // Public, non-frozen structs and enums defined in Swift don't get implicit + // Sendable conformances. + if (!nominal->getASTContext().LangOpts.EnableInferPublicSendable && + nominal->getFormalAccessScope( + /*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true).isPublic() && + !(nominal->hasClangNode() || + nominal->getAttrs().hasAttribute() || + nominal->getAttrs().hasAttribute())) { return nullptr; + } - auto conformance = ctx.getConformance( - nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(), - nominal, ProtocolConformanceState::Complete); - conformance->setSourceKindAndImplyingConformance( - ConformanceEntryKind::Synthesized, nullptr); + // Check the instance storage for Sendable conformance. + if (checkSendableInstanceStorage( + nominal, nominal, SendableCheck::Implicit)) + return nullptr; - return conformance; + return formConformance(); } AnyFunctionType *swift::applyGlobalActorType( @@ -3296,6 +3773,7 @@ AnyFunctionType *swift::applyGlobalActorType( Type globalActorType; switch (auto isolation = getActorIsolation(funcOrEnum)) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: case ActorIsolation::Independent: case ActorIsolation::Unspecified: return fnType; @@ -3342,3 +3820,7 @@ AnyFunctionType *swift::applyGlobalActorType( return FunctionType::get( fnType->getParams(), Type(innerFnType), fnType->getExtInfo()); } + +bool swift::completionContextUsesConcurrencyFeatures(const DeclContext *dc) { + return contextUsesConcurrencyFeatures(dc); +} diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 25bc62191458d..292bb5847c6b8 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -17,18 +17,22 @@ #ifndef SWIFT_SEMA_TYPECHECKCONCURRENCY_H #define SWIFT_SEMA_TYPECHECKCONCURRENCY_H +#include "swift/AST/ASTContext.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/Module.h" #include "swift/AST/Type.h" #include namespace swift { class AbstractFunctionDecl; +class ConstructorDecl; class ActorIsolation; class AnyFunctionType; class ASTContext; class ClassDecl; +class ClosureActorIsolation; class ClosureExpr; class ConcreteDeclRef; class CustomAttr; @@ -38,15 +42,15 @@ class EnumElementDecl; class Expr; class FuncDecl; class Initializer; -class LangOptions; class PatternBindingDecl; class ProtocolConformance; class TopLevelCodeDecl; class TypeBase; class ValueDecl; +class VarDecl; -/// Add notes suggesting the addition of 'async' or '@asyncHandler', as -/// appropriate, to a diagnostic for a function that isn't an async context. +/// Add notes suggesting the addition of 'async', as appropriate, +/// to a diagnostic for a function that isn't an async context. void addAsyncNotes(AbstractFunctionDecl const* func); /// Check actor isolation rules. @@ -54,8 +58,17 @@ void checkTopLevelActorIsolation(TopLevelCodeDecl *decl); void checkFunctionActorIsolation(AbstractFunctionDecl *decl); void checkInitializerActorIsolation(Initializer *init, Expr *expr); void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr); -void checkPropertyWrapperActorIsolation( - PatternBindingDecl *binding, Expr *expr); +void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr); + +/// Determine the isolation of a particular closure. +/// +/// This forwards to \c ActorIsolationChecker::determineClosureActorIsolation +/// and thus assumes that enclosing closures have already had their isolation +/// checked. +/// +/// This does not set the closure's actor isolation +ClosureActorIsolation +determineClosureActorIsolation(AbstractClosureExpr *closure); /// Describes the kind of operation that introduced the concurrent refernece. enum class ConcurrentReferenceKind { @@ -68,6 +81,8 @@ enum class ConcurrentReferenceKind { LocalCapture, /// Concurrent function ConcurrentFunction, + /// Nonisolated declaration. + Nonisolated, }; /// The isolation restriction in effect for a given declaration that is @@ -100,6 +115,10 @@ class ActorIsolationRestriction { /// are permitted from elsewhere as a cross-actor reference, but /// contexts with unspecified isolation won't diagnose anything. GlobalActorUnsafe, + + /// References to declarations that are part of a distributed actor are + /// only permitted if they are async. + DistributedActorSelf, }; private: @@ -129,7 +148,9 @@ class ActorIsolationRestriction { /// Retrieve the actor type that the declaration is within. NominalTypeDecl *getActorType() const { - assert(kind == ActorSelf || kind == CrossActorSelf); + assert(kind == ActorSelf || + kind == CrossActorSelf || + kind == DistributedActorSelf); return data.actorType; } @@ -159,6 +180,15 @@ class ActorIsolationRestriction { return result; } + /// Accesses to the given declaration can only be made via the 'self' of + /// the current actor. + static ActorIsolationRestriction forDistributedActorSelf( + NominalTypeDecl *actor, bool isCrossActor) { + ActorIsolationRestriction result(DistributedActorSelf, isCrossActor); + result.data.actorType = actor; + return result; + } + /// Accesses to the given declaration can only be made via this particular /// global actor or is a cross-actor access. static ActorIsolationRestriction forGlobalActor( @@ -174,7 +204,8 @@ class ActorIsolationRestriction { /// \param fromExpression Indicates that the reference is coming from an /// expression. static ActorIsolationRestriction forDeclaration( - ConcreteDeclRef declRef, bool fromExpression = true); + ConcreteDeclRef declRef, const DeclContext *fromDC, + bool fromExpression = true); operator Kind() const { return kind; }; }; @@ -199,7 +230,7 @@ bool contextUsesConcurrencyFeatures(const DeclContext *dc); /// domain, including the substitutions so that (e.g.) we can consider the /// specific types at the use site. /// -/// \param dc The declaration context from which the reference occurs. This is +/// \param module The module from which the reference occurs. This is /// used to perform lookup of conformances to the \c Sendable protocol. /// /// \param loc The location at which the reference occurs, which will be @@ -209,14 +240,48 @@ bool contextUsesConcurrencyFeatures(const DeclContext *dc); /// used to tailor the diagnostic. /// /// \returns true if an problem was detected, false otherwise. -bool diagnoseNonConcurrentTypesInReference( - ConcreteDeclRef declRef, const DeclContext *dc, SourceLoc loc, - ConcurrentReferenceKind refKind, - DiagnosticBehavior behavior = DiagnosticBehavior::Unspecified); +bool diagnoseNonSendableTypesInReference( + ConcreteDeclRef declRef, ModuleDecl *module, SourceLoc loc, + ConcurrentReferenceKind refKind); + +/// Produce a diagnostic for a missing conformance to Sendable. +void diagnoseMissingSendableConformance( + SourceLoc loc, Type type, ModuleDecl *module); -/// Whether we should diagnose cases where Sendable conformances are -/// missing. -bool shouldDiagnoseNonSendableViolations(const LangOptions &langOpts); +/// Diagnose any non-Sendable types that occur within the given type, using +/// the given diagnostic. +/// +/// \param diagnose Emit a diagnostic indicating that the current type +/// is non-Sendable, with the suggested behavior limitation. Returns \c true +/// if an error was produced. +/// +/// \returns \c true if any diagnostics were produced, \c false otherwise. +bool diagnoseNonSendableTypes( + Type type, ModuleDecl *module, SourceLoc loc, + llvm::function_ref diagnose); + +namespace detail { + template + struct Identity { + typedef T type; + }; +} +/// Diagnose any non-Sendable types that occur within the given type, using +/// the given diagnostic. +/// +/// \returns \c true if any errors were produced, \c false otherwise. +template +bool diagnoseNonSendableTypes( + Type type, ModuleDecl *module, SourceLoc loc, Diag diag, + typename detail::Identity::type ...diagArgs) { + ASTContext &ctx = module->getASTContext(); + return diagnoseNonSendableTypes( + type, module, loc, [&](Type specificType, DiagnosticBehavior behavior) { + ctx.Diags.diagnose(loc, diag, type, diagArgs...) + .limitBehavior(behavior); + return behavior == DiagnosticBehavior::Unspecified; + }); +} /// How the concurrent value check should be performed. enum class SendableCheck { @@ -228,7 +293,7 @@ enum class SendableCheck { /// protocols that added Sendable after-the-fact. ImpliedByStandardProtocol, - /// Implicit conformance to Sendable for structs and enums. + /// Implicit conformance to Sendable. Implicit, }; @@ -238,8 +303,8 @@ enum class SendableCheck { Optional> checkGlobalActorAttributes( SourceLoc loc, DeclContext *dc, ArrayRef attrs); - /// Get the explicit global actor specified for a closure. + Type getExplicitGlobalActor(ClosureExpr *closure); /// Check the correctness of the given Sendable conformance. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index b540b5494772a..c3305b6e68fb9 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -111,6 +111,10 @@ bool TypeVariableType::Implementation::isClosureResultType() const { locator->isLastElement(); } +bool TypeVariableType::Implementation::isKeyPathType() const { + return locator && locator->isKeyPathType(); +} + void *operator new(size_t bytes, ConstraintSystem& cs, size_t alignment) { return cs.getAllocator().Allocate(bytes, alignment); @@ -216,7 +220,7 @@ void ParentConditionalConformance::diagnoseConformanceStack( ArrayRef conformances) { for (auto history : llvm::reverse(conformances)) { diags.diagnose(loc, diag::requirement_implied_by_conditional_conformance, - history.ConformingType, history.Protocol); + history.ConformingType, history.Protocol->getDeclaredInterfaceType()); } } @@ -291,7 +295,7 @@ void constraints::performSyntacticDiagnosticsForTarget( case SolutionApplicationTarget::Kind::stmtCondition: case SolutionApplicationTarget::Kind::caseLabelItem: case SolutionApplicationTarget::Kind::patternBinding: - case SolutionApplicationTarget::Kind::uninitializedWrappedVar: + case SolutionApplicationTarget::Kind::uninitializedVar: // Nothing to do for these. return; } @@ -350,9 +354,6 @@ TypeChecker::typeCheckExpression( if (DiagnosticSuppression::isEnabled(Context.Diags)) csOptions |= ConstraintSystemFlags::SuppressDiagnostics; - if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables)) - csOptions |= ConstraintSystemFlags::AllowUnresolvedTypeVariables; - if (options.contains(TypeCheckExprFlags::LeaveClosureBodyUnchecked)) csOptions |= ConstraintSystemFlags::LeaveClosureBodyUnchecked; @@ -374,8 +375,6 @@ TypeChecker::typeCheckExpression( // If the client can handle unresolved type variables, leave them in the // system. auto allowFreeTypeVariables = FreeTypeVariableBinding::Disallow; - if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables)) - allowFreeTypeVariables = FreeTypeVariableBinding::UnresolvedType; // Attempt to solve the constraint system. auto viable = cs.solve(target, allowFreeTypeVariables); @@ -384,16 +383,6 @@ TypeChecker::typeCheckExpression( return None; } - // If the client allows the solution to have unresolved type expressions, - // check for them now. We cannot apply the solution with unresolved TypeVars, - // because they will leak out into arbitrary places in the resultant AST. - if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && - (viable->size() != 1 || - (target.getExprConversionType() && - target.getExprConversionType()->hasUnresolvedType()))) { - return target; - } - // Apply this solution to the constraint system. // FIXME: This shouldn't be necessary. auto &solution = (*viable)[0]; @@ -457,6 +446,7 @@ bool TypeChecker::typeCheckBinding( // Assign error types to the pattern and its variables, to prevent it from // being referenced by the constraint system. if (patternType->hasUnresolvedType() || + patternType->hasPlaceholder() || patternType->hasUnboundGenericType()) { pattern->setType(ErrorType::get(Context)); } @@ -519,23 +509,24 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, // given by the initializer. if (auto var = pattern->getSingleVar()) { if (auto opaque = var->getOpaqueResultTypeDecl()) { - if (auto convertedInit = dyn_cast(init)) { - auto underlyingType = convertedInit->getSubExpr()->getType() - ->mapTypeOutOfContext(); - auto underlyingSubs = SubstitutionMap::get( - opaque->getOpaqueInterfaceGenericSignature(), - [&](SubstitutableType *t) -> Type { - if (t->isEqual(opaque->getUnderlyingInterfaceType())) { - return underlyingType; - } - return Type(t); - }, - LookUpConformanceInModule(opaque->getModuleContext())); - - opaque->setUnderlyingTypeSubstitutions(underlyingSubs); - } else { - var->diagnose(diag::opaque_type_var_no_underlying_type); - } + init->forEachChildExpr([&](Expr *expr) -> Expr * { + if (auto coercionExpr = dyn_cast(expr)) { + auto underlyingType = + coercionExpr->getSubExpr()->getType()->mapTypeOutOfContext(); + auto underlyingSubs = SubstitutionMap::get( + opaque->getOpaqueInterfaceGenericSignature(), + [&](SubstitutableType *t) -> Type { + if (t->isEqual(opaque->getUnderlyingInterfaceType())) { + return underlyingType; + } + return Type(t); + }, + LookUpConformanceInModule(opaque->getModuleContext())); + + opaque->setUnderlyingTypeSubstitutions(underlyingSubs); + } + return expr; + }); } } @@ -642,11 +633,9 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, PrettyStackTracePattern stackTrace(Context, "type-checking", EP); // Create a 'let' binding to stand in for the RHS value. - auto *matchVar = new (Context) VarDecl(/*IsStatic*/false, - VarDecl::Introducer::Let, - EP->getLoc(), - Context.getIdentifier("$match"), - DC); + auto *matchVar = + new (Context) VarDecl(/*IsStatic*/ false, VarDecl::Introducer::Let, + EP->getLoc(), Context.Id_PatternMatchVar, DC); matchVar->setInterfaceType(rhsType->mapTypeOutOfContext()); matchVar->setImplicit(); @@ -678,20 +667,14 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, auto *matchOp = TypeChecker::buildRefExpr(choices, DC, DeclNameLoc(EP->getLoc()), /*Implicit=*/true, FunctionRefKind::Compound); + + // Note we use getEndLoc here to have the BinaryExpr source range be the same + // as the expr pattern source range. auto *matchVarRef = new (Context) DeclRefExpr(matchVar, - DeclNameLoc(EP->getLoc()), + DeclNameLoc(EP->getEndLoc()), /*Implicit=*/true); - - Expr *matchArgElts[] = {EP->getSubExpr(), matchVarRef}; - auto *matchArgs - = TupleExpr::create(Context, EP->getSubExpr()->getSourceRange().Start, - matchArgElts, { }, { }, - EP->getSubExpr()->getSourceRange().End, - /*HasTrailingClosure=*/false, /*Implicit=*/true); - - Expr *matchCall = new (Context) BinaryExpr(matchOp, matchArgs, - /*Implicit=*/true); - + Expr *matchCall = BinaryExpr::create(Context, EP->getSubExpr(), matchOp, + matchVarRef, /*implicit*/ true); // Check the expression as a condition. bool hadError = typeCheckCondition(matchCall, DC); // Save the type-checked expression in the pattern. @@ -1340,6 +1323,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, Type origFromType = fromType; Type origToType = toType; + auto *module = dc->getParentModule(); + auto &diags = dc->getASTContext().Diags; bool optionalToOptionalCast = false; @@ -1728,7 +1713,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // if (auto *protocolDecl = dyn_cast_or_null(fromType->getAnyNominal())) { - if (!couldDynamicallyConformToProtocol(toType, protocolDecl, dc)) { + if (!couldDynamicallyConformToProtocol(toType, protocolDecl, module)) { return failed(); } } else if (auto protocolComposition = @@ -1738,7 +1723,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, if (auto protocolDecl = dyn_cast_or_null( protocolType->getAnyNominal())) { return !couldDynamicallyConformToProtocol( - toType, protocolDecl, dc); + toType, protocolDecl, module); } return false; })) { @@ -1834,7 +1819,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, auto nsErrorTy = Context.getNSErrorType(); if (auto errorTypeProto = Context.getProtocol(KnownProtocolKind::Error)) { - if (!conformsToProtocol(toType, errorTypeProto, dc).isInvalid()) { + if (conformsToProtocol(toType, errorTypeProto, module)) { if (nsErrorTy) { if (isSubtypeOf(fromType, nsErrorTy, dc) // Don't mask "always true" warnings if NSError is cast to @@ -1844,7 +1829,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, } } - if (!conformsToProtocol(fromType, errorTypeProto, dc).isInvalid()) { + if (conformsToProtocol(fromType, errorTypeProto, module)) { // Cast of an error-conforming type to NSError or NSObject. if ((nsObject && toType->isEqual(nsObject)) || (nsErrorTy && toType->isEqual(nsErrorTy))) @@ -1872,7 +1857,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // classes. This may be necessary to force-fit ObjC APIs that depend on // covariance, or for APIs where the generic parameter annotations in the // ObjC headers are inaccurate. - if (clas && clas->usesObjCGenericsModel()) { + if (clas && clas->isTypeErasedGenericClass()) { if (fromType->getClassOrBoundGenericClass() == clas) return CheckedCastKind::ValueCast; } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index a254fb9326b34..eaa8649af8ba6 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -672,34 +672,6 @@ ExistentialConformsToSelfRequest::evaluate(Evaluator &evaluator, return true; } -bool -ExistentialTypeSupportedRequest::evaluate(Evaluator &evaluator, - ProtocolDecl *decl) const { - // ObjC protocols can always be existential. - if (decl->isObjC()) - return true; - - for (auto member : decl->getMembers()) { - // Existential types cannot be used if the protocol has an associated type. - if (isa(member)) - return false; - - // For value members, look at their type signatures. - if (auto valueMember = dyn_cast(member)) { - if (!decl->isAvailableInExistential(valueMember)) - return false; - } - } - - // Check whether all of the inherited protocols support existential types. - for (auto proto : decl->getInheritedProtocols()) { - if (!proto->existentialTypeSupported()) - return false; - } - - return true; -} - bool IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { if (isa(decl)) @@ -863,7 +835,7 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { return true; // The presence of 'final' blocks the inference of 'dynamic'. - if (decl->isFinal()) + if (decl->isSemanticallyFinal()) return false; // Types are never 'dynamic'. @@ -940,7 +912,7 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator, auto reqSignature = std::move(builder).computeGenericSignature( /*allowConcreteGenericParams=*/false, /*requirementSignatureSelfProto=*/proto); - return reqSignature->getRequirements(); + return reqSignature.getRequirements(); } Type @@ -1102,13 +1074,13 @@ swift::computeAutomaticEnumValueKind(EnumDecl *ED) { if (ED->getGenericEnvironmentOfContext() != nullptr) rawTy = ED->mapTypeIntoContext(rawTy); - + + auto *module = ED->getParentModule(); + // Swift enums require that the raw type is convertible from one of the // primitive literal protocols. auto conformsToProtocol = [&](KnownProtocolKind protoKind) { - ProtocolDecl *proto = ED->getASTContext().getProtocol(protoKind); - return proto && - TypeChecker::conformsToProtocol(rawTy, proto, ED->getDeclContext()); + return TypeChecker::conformsToKnownProtocol(rawTy, protoKind, module); }; static auto otherLiteralProtocolKinds = { @@ -1516,46 +1488,6 @@ TypeChecker::lookupPrecedenceGroup(DeclContext *dc, Identifier name, return PrecedenceGroupLookupResult(dc, name, std::move(groups)); } -static NominalTypeDecl *resolveSingleNominalTypeDecl( - DeclContext *DC, SourceLoc loc, Identifier ident, ASTContext &Ctx, - TypeResolutionFlags flags = TypeResolutionFlags(0)) { - auto *TyR = new (Ctx) SimpleIdentTypeRepr(DeclNameLoc(loc), - DeclNameRef(ident)); - - const auto options = - TypeResolutionOptions(TypeResolverContext::TypeAliasDecl) | flags; - - const auto result = - TypeResolution::forInterface(DC, options, - // FIXME: Should unbound generics be allowed - // to appear amongst designated types? - /*unboundTyOpener*/ nullptr, - /*placeholderHandler*/ nullptr) - .resolveType(TyR); - - if (result->hasError()) - return nullptr; - return result->getAnyNominal(); -} - -bool swift::checkDesignatedTypes(OperatorDecl *OD, - ArrayRef> identifiers) { - auto &ctx = OD->getASTContext(); - auto *DC = OD->getDeclContext(); - - SmallVector designatedNominalTypes; - for (auto ident : identifiers) { - auto *decl = resolveSingleNominalTypeDecl(DC, ident.Loc, ident.Item, ctx); - if (!decl) - return true; - - designatedNominalTypes.push_back(decl); - } - - OD->setDesignatedNominalTypes(ctx.AllocateCopy(designatedNominalTypes)); - return false; -} - /// Validate the given operator declaration. /// /// This establishes key invariants, such as an InfixOperatorDecl's @@ -1567,49 +1499,26 @@ OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator, auto &ctx = IOD->getASTContext(); auto *dc = IOD->getDeclContext(); - auto enableOperatorDesignatedTypes = - ctx.TypeCheckerOpts.EnableOperatorDesignatedTypes; - - PrecedenceGroupDecl *group = nullptr; - - auto identifiers = IOD->getIdentifiers(); - if (!identifiers.empty()) { - auto name = identifiers[0].Item; - auto loc = identifiers[0].Loc; - - auto canResolveType = [&]() -> bool { - return enableOperatorDesignatedTypes && - resolveSingleNominalTypeDecl(dc, loc, name, ctx, - TypeResolutionFlags::SilenceErrors); - }; - - // Make sure not to diagnose in the case where we failed to find a - // precedencegroup, but we could resolve a type instead. + auto name = IOD->getPrecedenceGroupName(); + if (!name.empty()) { + auto loc = IOD->getPrecedenceGroupLoc(); auto groups = TypeChecker::lookupPrecedenceGroup(dc, name, loc); - if (groups.hasResults() || !canResolveType()) { - group = groups.getSingleOrDiagnose(loc); - identifiers = identifiers.slice(1); - } - } - // Unless operator designed types are enabled, the parser will ensure that - // only one identifier is allowed in the clause, which we should have just - // handled. - assert(identifiers.empty() || enableOperatorDesignatedTypes); + if (groups.hasResults() || + !ctx.TypeCheckerOpts.EnableOperatorDesignatedTypes) + return groups.getSingleOrDiagnose(loc); - if (!group) { - auto groups = TypeChecker::lookupPrecedenceGroup( - dc, ctx.Id_DefaultPrecedence, SourceLoc()); - group = groups.getSingleOrDiagnose(IOD->getLoc(), /*forBuiltin*/ true); + // We didn't find the named precedence group and designated types are + // enabled, so we will assume that it was actually a designated type. Warn + // and fall through as though `PrecedenceGroupName` had never been set. + ctx.Diags.diagnose(IOD->getColonLoc(), + diag::operator_decl_remove_designated_types) + .fixItRemove({IOD->getColonLoc(), loc}); } - auto nominalTypes = IOD->getDesignatedNominalTypes(); - if (nominalTypes.empty() && enableOperatorDesignatedTypes) { - if (checkDesignatedTypes(IOD, identifiers)) { - IOD->setInvalid(); - } - } - return group; + auto groups = TypeChecker::lookupPrecedenceGroup( + dc, ctx.Id_DefaultPrecedence, SourceLoc()); + return groups.getSingleOrDiagnose(IOD->getLoc(), /*forBuiltin*/ true); } SelfAccessKind @@ -1860,7 +1769,7 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const { if (dc->isTypeContext()) { if (auto classDecl = dc->getSelfClassDecl()) { // For a class, we also need the function or class to be 'final'. - if (!classDecl->isFinal() && !FD->isFinal() && + if (!classDecl->isSemanticallyFinal() && !FD->isFinal() && FD->getStaticLoc().isValid() && FD->getStaticSpelling() != StaticSpellingKind::KeywordStatic) { FD->diagnose(diag::nonfinal_operator_in_class, @@ -1998,7 +1907,7 @@ bool swift::isMemberOperator(FuncDecl *decl, Type type) { auto selfNominal = DC->getSelfNominalTypeDecl(); // Check the parameters for a reference to 'Self'. - bool isProtocol = selfNominal && isa(selfNominal); + bool isProtocol = isa_and_nonnull(selfNominal); for (auto param : *decl->getParameters()) { // Look through a metatype reference, if there is one. auto paramType = param->getInterfaceType()->getMetatypeInstanceType(); @@ -2149,6 +2058,9 @@ ParamSpecifierRequest::evaluate(Evaluator &evaluator, nestedRepr = tupleRepr->getElementType(0); } + if (auto isolated = dyn_cast(nestedRepr)) + nestedRepr = isolated->getBase(); + if (isa(nestedRepr) && param->isDefaultArgument()) { auto &ctx = param->getASTContext(); @@ -2186,7 +2098,9 @@ static Type validateParameterType(ParamDecl *decl) { }; // FIXME: Don't let placeholder types escape type resolution. // For now, just return the placeholder type. - placeholderHandler = PlaceholderType::get; + placeholderHandler = [](auto &ctx, auto *originator) { + return Type(); + }; } else if (isa(dc)) { options = TypeResolutionOptions(TypeResolverContext::AbstractFunctionDecl); } else if (isa(dc)) { @@ -2218,9 +2132,10 @@ static Type validateParameterType(ParamDecl *decl) { } if (decl->isVariadic()) { - Ty = TypeChecker::getArraySliceType(decl->getStartLoc(), Ty); - if (Ty->hasError()) { - decl->setInvalid(); + Ty = VariadicSequenceType::get(Ty); + if (!ctx.getArrayDecl()) { + ctx.Diags.diagnose(decl->getTypeRepr()->getLoc(), + diag::sugar_type_not_found, 0); return ErrorType::get(ctx); } @@ -2299,6 +2214,7 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { auto selfParam = computeSelfParam(AFD, /*isInitializingCtor*/true, /*wantDynamicSelf*/true); + PD->setIsolated(selfParam.isIsolated()); return selfParam.getPlainType(); } @@ -2693,6 +2609,10 @@ static ArrayRef evaluateMembersRequest( (void) var->getPropertyWrapperAuxiliaryVariables(); (void) var->getPropertyWrapperInitializerInfo(); } + + if (auto *func = dyn_cast(member)) { + (void) func->getDistributedActorRemoteFuncDecl(); + } } } @@ -2754,8 +2674,8 @@ bool TypeChecker::isPassThroughTypealias(TypeAliasDecl *typealias, if (!nominalSig) return true; // Check that the type parameters are the same the whole way through. - auto nominalGenericParams = nominalSig->getGenericParams(); - auto typealiasGenericParams = typealiasSig->getGenericParams(); + auto nominalGenericParams = nominalSig.getGenericParams(); + auto typealiasGenericParams = typealiasSig.getGenericParams(); if (nominalGenericParams.size() != typealiasGenericParams.size()) return false; if (!std::equal(nominalGenericParams.begin(), nominalGenericParams.end(), @@ -2773,7 +2693,7 @@ bool TypeChecker::isPassThroughTypealias(TypeAliasDecl *typealias, // If our arguments line up with our innermost generic parameters, it's // a passthrough typealias. - auto innermostGenericParams = typealiasSig->getInnermostGenericParams(); + auto innermostGenericParams = typealiasSig.getInnermostGenericParams(); auto boundArgs = boundGenericType->getGenericArgs(); if (boundArgs.size() != innermostGenericParams.size()) return false; @@ -2818,7 +2738,9 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { }, // FIXME: Don't let placeholder types escape type resolution. // For now, just return the placeholder type. - PlaceholderType::get); + [](auto &ctx, auto *originator) { + return Type(); + }); const auto extendedType = resolution.resolveType(extendedRepr); diff --git a/lib/Sema/TypeCheckDecl.h b/lib/Sema/TypeCheckDecl.h index 5bb835b9e3585..bd8d71d440762 100644 --- a/lib/Sema/TypeCheckDecl.h +++ b/lib/Sema/TypeCheckDecl.h @@ -56,8 +56,7 @@ Optional computeAutomaticEnumValueKind(EnumDecl *ED); void validatePrecedenceGroup(PrecedenceGroupDecl *PGD); -bool checkDesignatedTypes(OperatorDecl *OD, - ArrayRef> identifiers); +void diagnoseAttrsAddedByAccessNote(SourceFile &SF); } diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 42ad5f6af6ddb..057758e8bce94 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -55,7 +55,7 @@ swift::behaviorLimitForObjCReason(ObjCReason reason, ASTContext &ctx) { return DiagnosticBehavior::Ignore; case ObjCReason::ExplicitlyObjCByAccessNote: - return DiagnosticBehavior::Remark; + return ctx.LangOpts.getAccessNoteFailureLimit(); case ObjCReason::MemberOfObjCSubclass: case ObjCReason::MemberOfObjCMembersClass: @@ -97,33 +97,59 @@ unsigned swift::getObjCDiagnosticAttrKind(ObjCReason reason) { llvm_unreachable("unhandled reason"); } -/// Emit an additional diagnostic describing why we are applying @objc to the -/// decl, if this is not obvious from the decl itself. -static void describeObjCReason(const ValueDecl *VD, ObjCReason Reason) { - if (Reason == ObjCReason::MemberOfObjCProtocol) { - VD->diagnose(diag::objc_inferring_on_objc_protocol_member); - } else if (Reason == ObjCReason::OverridesObjC) { - unsigned kind = isa(VD) ? 0 - : isa(VD) ? 1 - : isa(VD) ? 2 +void ObjCReason::describe(const Decl *D) const { + switch (kind) { + case ObjCReason::MemberOfObjCProtocol: + D->diagnose(diag::objc_inferring_on_objc_protocol_member); + break; + + case ObjCReason::OverridesObjC: { + unsigned kind = isa(D) ? 0 + : isa(D) ? 1 + : isa(D) ? 2 : 3; - auto overridden = VD->getOverriddenDecl(); + auto overridden = cast(D)->getOverriddenDecl(); overridden->diagnose(diag::objc_overriding_objc_decl, - kind, VD->getOverriddenDecl()->getName()); - } else if (Reason == ObjCReason::WitnessToObjC) { - auto requirement = Reason.getObjCRequirement(); + kind, overridden->getName()); + break; + } + + case ObjCReason::WitnessToObjC: { + auto requirement = getObjCRequirement(); requirement->diagnose(diag::objc_witness_objc_requirement, - VD->getDescriptiveKind(), requirement->getName(), + D->getDescriptiveKind(), requirement->getName(), cast(requirement->getDeclContext()) ->getName()); + break; } - else if (Reason == ObjCReason::ExplicitlyObjCByAccessNote) { - // FIXME: Look up the Reason string in the access note and emit a note that - // includes it + + case ObjCReason::ExplicitlyObjCByAccessNote: + case ObjCReason::ExplicitlyCDecl: + case ObjCReason::ExplicitlyDynamic: + case ObjCReason::ExplicitlyObjC: + case ObjCReason::ExplicitlyIBOutlet: + case ObjCReason::ExplicitlyIBAction: + case ObjCReason::ExplicitlyIBSegueAction: + case ObjCReason::ExplicitlyNSManaged: + case ObjCReason::ImplicitlyObjC: + case ObjCReason::ExplicitlyIBInspectable: + case ObjCReason::ExplicitlyGKInspectable: + case ObjCReason::MemberOfObjCExtension: + case ObjCReason::MemberOfObjCMembersClass: + case ObjCReason::MemberOfObjCSubclass: + case ObjCReason::ElementOfObjCEnum: + case ObjCReason::Accessor: + // No additional note required. + break; } } +void ObjCReason::setAttrInvalid() const { + if (requiresAttr(kind)) + getAttr()->setInvalid(); +} + static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC, Type T, SourceRange TypeRange, @@ -253,19 +279,21 @@ static void diagnoseFunctionParamNotRepresentable( auto behavior = behaviorLimitForObjCReason(Reason, AFD->getASTContext()); if (NumParams == 1) { - AFD->diagnose(diag::objc_invalid_on_func_single_param_type, - getObjCDiagnosticAttrKind(Reason)) - .limitBehavior(behavior); + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::objc_invalid_on_func_single_param_type, + getObjCDiagnosticAttrKind(Reason)) + .limitBehavior(behavior)); } else { - AFD->diagnose(diag::objc_invalid_on_func_param_type, - ParamIndex + 1, getObjCDiagnosticAttrKind(Reason)) - .limitBehavior(behavior); + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::objc_invalid_on_func_param_type, + ParamIndex + 1, getObjCDiagnosticAttrKind(Reason)) + .limitBehavior(behavior)); } SourceRange SR; if (auto typeRepr = P->getTypeRepr()) SR = typeRepr->getSourceRange(); diagnoseTypeNotRepresentableInObjC(AFD, P->getType(), SR, behavior); - describeObjCReason(AFD, Reason); + Reason.describe(AFD); } static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD, @@ -282,22 +310,24 @@ static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD, // Swift Varargs are not representable in Objective-C. if (param->isVariadic()) { - diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_variadic, - getObjCDiagnosticAttrKind(Reason)) - .highlight(param->getSourceRange()) - .limitBehavior(behavior); - describeObjCReason(AFD, Reason); + softenIfAccessNote(AFD, Reason.getAttr(), + diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_variadic, + getObjCDiagnosticAttrKind(Reason)) + .highlight(param->getSourceRange()) + .limitBehavior(behavior)); + Reason.describe(AFD); return false; } // Swift inout parameters are not representable in Objective-C. if (param->isInOut()) { - diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_inout, - getObjCDiagnosticAttrKind(Reason)) - .highlight(param->getSourceRange()) - .limitBehavior(behavior); - describeObjCReason(AFD, Reason); + softenIfAccessNote(AFD, Reason.getAttr(), + diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_inout, + getObjCDiagnosticAttrKind(Reason)) + .highlight(param->getSourceRange()) + .limitBehavior(behavior)); + Reason.describe(AFD); return false; } @@ -339,19 +369,21 @@ static bool checkObjCWithGenericParams(const ValueDecl *VD, ObjCReason Reason) { auto *GC = VD->getAsGenericContext(); assert(GC); if (GC->getGenericParams()) { - VD->diagnose(diag::objc_invalid_with_generic_params, - VD->getDescriptiveKind(), getObjCDiagnosticAttrKind(Reason)) - .limitBehavior(behavior); - describeObjCReason(VD, Reason); + softenIfAccessNote(VD, Reason.getAttr(), + VD->diagnose(diag::objc_invalid_with_generic_params, + VD->getDescriptiveKind(), getObjCDiagnosticAttrKind(Reason)) + .limitBehavior(behavior)); + Reason.describe(VD); return true; } if (GC->getTrailingWhereClause()) { - VD->diagnose(diag::objc_invalid_with_generic_requirements, - VD->getDescriptiveKind(), getObjCDiagnosticAttrKind(Reason)) - .limitBehavior(behavior); - describeObjCReason(VD, Reason); + softenIfAccessNote(VD, Reason.getAttr(), + VD->diagnose(diag::objc_invalid_with_generic_requirements, + VD->getDescriptiveKind(), getObjCDiagnosticAttrKind(Reason)) + .limitBehavior(behavior)); + Reason.describe(VD); return true; } @@ -381,7 +413,7 @@ static bool checkObjCInForeignClassContext(const ValueDecl *VD, VD->diagnose(diag::objc_invalid_on_foreign_class, getObjCDiagnosticAttrKind(Reason)) .limitBehavior(behavior); - describeObjCReason(VD, Reason); + Reason.describe(VD); break; case ClassDecl::ForeignKind::RuntimeOnly: @@ -389,7 +421,7 @@ static bool checkObjCInForeignClassContext(const ValueDecl *VD, VD->getDescriptiveKind(), getObjCDiagnosticAttrKind(Reason), clas->getName()) .limitBehavior(behavior); - describeObjCReason(VD, Reason); + Reason.describe(VD); break; } @@ -400,22 +432,20 @@ static bool checkObjCInForeignClassContext(const ValueDecl *VD, static bool checkObjCActorIsolation(const ValueDecl *VD, ObjCReason Reason) { // Check actor isolation. - auto behavior = behaviorLimitForObjCReason(Reason, VD->getASTContext()); - switch (auto restriction = ActorIsolationRestriction::forDeclaration( - const_cast(VD), /*fromExpression=*/false)) { + const_cast(VD), VD->getDeclContext(), + /*fromExpression=*/false)) { case ActorIsolationRestriction::CrossActorSelf: // FIXME: Substitution map? - diagnoseNonConcurrentTypesInReference( - const_cast(VD), VD->getDeclContext(), VD->getLoc(), - ConcurrentReferenceKind::CrossActor, behavior); + diagnoseNonSendableTypesInReference( + const_cast(VD), VD->getDeclContext()->getParentModule(), + VD->getLoc(), ConcurrentReferenceKind::CrossActor); return false; case ActorIsolationRestriction::ActorSelf: // Actor-isolated functions cannot be @objc. VD->diagnose(diag::actor_isolated_objc, VD->getDescriptiveKind(), - VD->getName()) - .limitBehavior(behavior); - describeObjCReason(VD, Reason); + VD->getName()); + Reason.describe(VD); if (auto FD = dyn_cast(VD)) { addAsyncNotes(const_cast(FD)); } @@ -425,6 +455,9 @@ static bool checkObjCActorIsolation(const ValueDecl *VD, case ActorIsolationRestriction::GlobalActor: // FIXME: Consider whether to limit @objc on global-actor-qualified // declarations. + case ActorIsolationRestriction::DistributedActorSelf: + // we do not allow distributed + objc actors. + return false; case ActorIsolationRestriction::Unrestricted: case ActorIsolationRestriction::Unsafe: return false; @@ -470,13 +503,16 @@ static const ClassDecl *getResilientAncestor(ModuleDecl *mod, /// extension of an Objective-C runtime visible class, and /// therefore is not representable in Objective-C. static bool checkObjCInExtensionContext(const ValueDecl *value, - DiagnosticBehavior behavior) { + ObjCReason reason) { + auto behavior = behaviorLimitForObjCReason(reason, value->getASTContext()); auto DC = value->getDeclContext(); if (auto ED = dyn_cast(DC)) { if (ED->getTrailingWhereClause()) { - value->diagnose(diag::objc_in_extension_context) - .limitBehavior(behavior); + softenIfAccessNote(value, reason.getAttr(), + value->diagnose(diag::objc_in_extension_context) + .limitBehavior(behavior)); + reason.describe(value); return true; } @@ -493,12 +529,14 @@ static bool checkObjCInExtensionContext(const ValueDecl *value, auto platform = prettyPlatformString(targetPlatform(ctx.LangOpts)); auto range = getMinOSVersionForClassStubs(target); auto *ancestor = getResilientAncestor(mod, classDecl); - value->diagnose(diag::objc_in_resilient_extension, - value->getDescriptiveKind(), - ancestor->getName(), - platform, - range.getLowerEndpoint()) - .limitBehavior(behavior); + softenIfAccessNote(value, reason.getAttr(), + value->diagnose(diag::objc_in_resilient_extension, + value->getDescriptiveKind(), + ancestor->getName(), + platform, + range.getLowerEndpoint()) + .limitBehavior(behavior)); + reason.describe(value); return true; } } @@ -512,10 +550,12 @@ static bool checkObjCInExtensionContext(const ValueDecl *value, ->getModuleContext() ->isImplicitDynamicEnabled()) return false; - if (!classDecl->usesObjCGenericsModel()) { - value->diagnose(diag::objc_in_generic_extension, - classDecl->isGeneric()) - .limitBehavior(behavior); + if (!classDecl->isTypeErasedGenericClass()) { + softenIfAccessNote(value, reason.getAttr(), + value->diagnose(diag::objc_in_generic_extension, + classDecl->isGeneric()) + .limitBehavior(behavior)); + reason.describe(value); return true; } } @@ -565,17 +605,17 @@ bool swift::isRepresentableInObjC( ASTContext &ctx = AFD->getASTContext(); DiagnosticStateRAII diagState(ctx.Diags); - auto behavior = behaviorLimitForObjCReason(Reason, ctx); if (checkObjCInForeignClassContext(AFD, Reason)) return false; if (checkObjCWithGenericParams(AFD, Reason)) return false; - if (checkObjCInExtensionContext(AFD, behavior)) + if (checkObjCInExtensionContext(AFD, Reason)) return false; if (checkObjCActorIsolation(AFD, Reason)) return false; + auto behavior = behaviorLimitForObjCReason(Reason, ctx); if (AFD->isOperator()) { AFD->diagnose((isa(AFD->getDeclContext()) ? diag::objc_operator_proto @@ -588,50 +628,62 @@ bool swift::isRepresentableInObjC( // Accessors can only be @objc if the storage declaration is. // Global computed properties may however @_cdecl their accessors. auto storage = accessor->getStorage(); - if (!storage->isObjC() && Reason != ObjCReason::ExplicitlyCDecl && - Reason != ObjCReason::WitnessToObjC && - Reason != ObjCReason::MemberOfObjCProtocol) { - if (accessor->isGetter()) { - auto error = isa(storage) - ? diag::objc_getter_for_nonobjc_property - : diag::objc_getter_for_nonobjc_subscript; - - accessor->diagnose(error).limitBehavior(behavior); - describeObjCReason(accessor, Reason); - } else if (accessor->isSetter()) { - auto error = isa(storage) - ? diag::objc_setter_for_nonobjc_property - : diag::objc_setter_for_nonobjc_subscript; - - accessor->diagnose(error).limitBehavior(behavior); - describeObjCReason(accessor, Reason); - } - return false; - } + bool storageIsObjC = storage->isObjC() + || Reason == ObjCReason::ExplicitlyCDecl + || Reason == ObjCReason::WitnessToObjC + || Reason == ObjCReason::MemberOfObjCProtocol; switch (accessor->getAccessorKind()) { case AccessorKind::DidSet: - case AccessorKind::WillSet: + case AccessorKind::WillSet: { // willSet/didSet implementations are never exposed to objc, they are // always directly dispatched from the synthesized setter. - accessor->diagnose(diag::objc_observing_accessor).limitBehavior(behavior); - describeObjCReason(accessor, Reason); + diagnoseAndRemoveAttr(accessor, Reason.getAttr(), + diag::objc_observing_accessor) + .limitBehavior(behavior); + Reason.describe(accessor); return false; + } case AccessorKind::Get: + if (!storageIsObjC) { + auto error = isa(storage) + ? diag::objc_getter_for_nonobjc_property + : diag::objc_getter_for_nonobjc_subscript; + + diagnoseAndRemoveAttr(accessor, Reason.getAttr(), error) + .limitBehavior(behavior); + Reason.describe(accessor); + return false; + } + return true; + case AccessorKind::Set: + if (!storageIsObjC) { + auto error = isa(storage) + ? diag::objc_setter_for_nonobjc_property + : diag::objc_setter_for_nonobjc_subscript; + + diagnoseAndRemoveAttr(accessor, Reason.getAttr(), error) + .limitBehavior(behavior); + Reason.describe(accessor); + return false; + } return true; case AccessorKind::Address: case AccessorKind::MutableAddress: - accessor->diagnose(diag::objc_addressor).limitBehavior(behavior); - describeObjCReason(accessor, Reason); + diagnoseAndRemoveAttr(accessor, Reason.getAttr(), diag::objc_addressor) + .limitBehavior(behavior); + Reason.describe(accessor); return false; case AccessorKind::Read: case AccessorKind::Modify: - accessor->diagnose(diag::objc_coroutine_accessor).limitBehavior(behavior); - describeObjCReason(accessor, Reason); + diagnoseAndRemoveAttr(accessor, Reason.getAttr(), + diag::objc_coroutine_accessor) + .limitBehavior(behavior); + Reason.describe(accessor); return false; } llvm_unreachable("bad kind"); @@ -666,13 +718,14 @@ bool swift::isRepresentableInObjC( !ResultType->isUninhabited() && !ResultType->isRepresentableIn(ForeignLanguage::ObjectiveC, const_cast(FD))) { - AFD->diagnose(diag::objc_invalid_on_func_result_type, - getObjCDiagnosticAttrKind(Reason)) - .limitBehavior(behavior); + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::objc_invalid_on_func_result_type, + getObjCDiagnosticAttrKind(Reason)) + .limitBehavior(behavior)); diagnoseTypeNotRepresentableInObjC(FD, ResultType, FD->getResultTypeSourceRange(), behavior); - describeObjCReason(FD, Reason); + Reason.describe(FD); return false; } } @@ -682,10 +735,11 @@ bool swift::isRepresentableInObjC( // information into a completion handler. auto FD = dyn_cast(AFD); if (!FD) { - AFD->diagnose(diag::not_objc_function_async, AFD->getDescriptiveKind()) - .highlight(AFD->getAsyncLoc()) - .limitBehavior(behavior); - describeObjCReason(AFD, Reason); + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::not_objc_function_async, AFD->getDescriptiveKind()) + .highlight(AFD->getAsyncLoc()) + .limitBehavior(behavior)); + Reason.describe(AFD); return false; } @@ -696,7 +750,7 @@ bool swift::isRepresentableInObjC( AFD->diagnose(diag::async_objc_dynamic_self) .highlight(AFD->getAsyncLoc()) .limitBehavior(behavior); - describeObjCReason(AFD, Reason); + Reason.describe(AFD); return false; } @@ -721,13 +775,14 @@ bool swift::isRepresentableInObjC( // Make sure that the paraneter type is representable in Objective-C. if (!type->isRepresentableIn( ForeignLanguage::ObjectiveC, const_cast(FD))) { - AFD->diagnose(diag::objc_invalid_on_func_result_type, - getObjCDiagnosticAttrKind(Reason)) - .limitBehavior(behavior); + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::objc_invalid_on_func_result_type, + getObjCDiagnosticAttrKind(Reason)) + .limitBehavior(behavior)); diagnoseTypeNotRepresentableInObjC(FD, type, FD->getResultTypeSourceRange(), behavior); - describeObjCReason(FD, Reason); + Reason.describe(FD); return true; } @@ -789,11 +844,12 @@ bool swift::isRepresentableInObjC( // Only non-failing initializers can throw. if (ctor->isFailable()) { - AFD->diagnose(diag::objc_invalid_on_failing_init, - getObjCDiagnosticAttrKind(Reason)) - .highlight(throwsLoc) - .limitBehavior(behavior); - describeObjCReason(AFD, Reason); + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::objc_invalid_on_failing_init, + getObjCDiagnosticAttrKind(Reason)) + .highlight(throwsLoc) + .limitBehavior(behavior)); + Reason.describe(AFD); return false; } @@ -823,20 +879,22 @@ bool swift::isRepresentableInObjC( isValidObjectiveCErrorResultType(dc, optOptionalType)) { // Cannot return an optional bridged type, because 'nil' is reserved // to indicate failure. Call this out in a separate diagnostic. - AFD->diagnose(diag::objc_invalid_on_throwing_optional_result, - getObjCDiagnosticAttrKind(Reason), - resultType) - .highlight(throwsLoc) - .limitBehavior(behavior); - describeObjCReason(AFD, Reason); + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::objc_invalid_on_throwing_optional_result, + getObjCDiagnosticAttrKind(Reason), + resultType) + .highlight(throwsLoc) + .limitBehavior(behavior)); + Reason.describe(AFD); return false; } else { // Other result types are not permitted. - AFD->diagnose(diag::objc_invalid_on_throwing_result, - getObjCDiagnosticAttrKind(Reason), resultType) - .highlight(throwsLoc) - .limitBehavior(behavior); - describeObjCReason(AFD, Reason); + softenIfAccessNote(AFD, Reason.getAttr(), + AFD->diagnose(diag::objc_invalid_on_throwing_result, + getObjCDiagnosticAttrKind(Reason), resultType) + .highlight(throwsLoc) + .limitBehavior(behavior)); + Reason.describe(AFD); return false; } @@ -987,9 +1045,8 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { bool Result = T->isRepresentableIn(ForeignLanguage::ObjectiveC, VD->getDeclContext()); - auto behavior = behaviorLimitForObjCReason(Reason, ctx); - if (Result && checkObjCInExtensionContext(VD, behavior)) + if (Result && checkObjCInExtensionContext(VD, Reason)) return false; if (checkObjCInForeignClassContext(VD, Reason)) @@ -997,11 +1054,16 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { if (checkObjCActorIsolation(VD, Reason)) return false; + auto behavior = behaviorLimitForObjCReason(Reason, ctx); + // effectful computed properties cannot be represented, and its an error // to mark them as @objc if (VD->getEffectfulGetAccessor()) { - VD->diagnose(diag::effectful_not_representable_objc, - VD->getDescriptiveKind()); + softenIfAccessNote(VD, Reason.getAttr(), + VD->diagnose(diag::effectful_not_representable_objc, + VD->getDescriptiveKind()) + .limitBehavior(behavior)); + Reason.describe(VD); return false; } @@ -1011,13 +1073,14 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { if (TypeRange.isInvalid()) TypeRange = VD->getNameLoc(); - VD->diagnose(diag::objc_invalid_on_var, getObjCDiagnosticAttrKind(Reason)) - .highlight(TypeRange) - .limitBehavior(behavior); + softenIfAccessNote(VD, Reason.getAttr(), + VD->diagnose(diag::objc_invalid_on_var, getObjCDiagnosticAttrKind(Reason)) + .highlight(TypeRange) + .limitBehavior(behavior)); diagnoseTypeNotRepresentableInObjC(VD->getDeclContext(), VD->getInterfaceType(), TypeRange, behavior); - describeObjCReason(VD, Reason); + Reason.describe(VD); } return Result; @@ -1039,17 +1102,22 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) { // effectful subscripts cannot be represented, and its an error // to mark them as @objc if (SD->getEffectfulGetAccessor()) { - SD->diagnose(diag::effectful_not_representable_objc, - SD->getDescriptiveKind()); + softenIfAccessNote(SD, Reason.getAttr(), + SD->diagnose(diag::effectful_not_representable_objc, + SD->getDescriptiveKind()) + .limitBehavior(behavior)); + Reason.describe(SD); return false; } // ObjC doesn't support class subscripts. if (!SD->isInstanceMember()) { - SD->diagnose(diag::objc_invalid_on_static_subscript, - SD->getDescriptiveKind(), Reason) - .limitBehavior(behavior); - describeObjCReason(SD, Reason); + softenIfAccessNote(SD, Reason.getAttr(), + SD->diagnose(diag::objc_invalid_on_static_subscript, + SD->getDescriptiveKind(), Reason) + .limitBehavior(behavior)); + Reason.describe(SD); + Reason.setAttrInvalid(); return true; } @@ -1078,7 +1146,7 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) { ForeignLanguage::ObjectiveC, SD->getDeclContext()); bool Result = IndexResult && ElementResult; - if (Result && checkObjCInExtensionContext(SD, behavior)) + if (Result && checkObjCInExtensionContext(SD, Reason)) return false; if (!Result) { @@ -1087,16 +1155,17 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) { TypeRange = SD->getIndices()->getSourceRange(); else TypeRange = SD->getElementTypeSourceRange(); - SD->diagnose(diag::objc_invalid_on_subscript, - getObjCDiagnosticAttrKind(Reason)) - .highlight(TypeRange) - .limitBehavior(behavior); + softenIfAccessNote(SD, Reason.getAttr(), + SD->diagnose(diag::objc_invalid_on_subscript, + getObjCDiagnosticAttrKind(Reason)) + .highlight(TypeRange) + .limitBehavior(behavior)); diagnoseTypeNotRepresentableInObjC(SD->getDeclContext(), !IndexResult ? IndexType : ElementType, TypeRange, behavior); - describeObjCReason(SD, Reason); + Reason.describe(SD); } return Result; @@ -1145,9 +1214,9 @@ static bool isMemberOfObjCMembersClass(const ValueDecl *VD) { ObjCReason swift::objCReasonForObjCAttr(const ObjCAttr *attr) { if (attr->getAddedByAccessNote()) - return ObjCReason::ExplicitlyObjCByAccessNote; + return ObjCReason(ObjCReason::ExplicitlyObjCByAccessNote, attr); - return ObjCReason::ExplicitlyObjC; + return ObjCReason(ObjCReason::ExplicitlyObjC, attr); } // A class is @objc if it does not have generic ancestry, and it either has @@ -1160,10 +1229,6 @@ static Optional shouldMarkClassAsObjC(const ClassDecl *CD) { auto reason = objCReasonForObjCAttr(attr); auto behavior = behaviorLimitForObjCReason(reason, ctx); - SourceLoc attrLoc = attr->getLocation(); - if (attrLoc.isInvalid()) - attrLoc = CD->getLoc(); - if (ancestry.contains(AncestryFlags::Generic)) { if (attr->hasName() && !CD->isGenericContext()) { // @objc with a name on a non-generic subclass of a generic class is @@ -1173,9 +1238,9 @@ static Optional shouldMarkClassAsObjC(const ClassDecl *CD) { return None; } - ctx.Diags.diagnose(attrLoc, diag::objc_for_generic_class) - .fixItRemove(attr->getRangeWithAt()) + swift::diagnoseAndRemoveAttr(CD, attr, diag::objc_for_generic_class) .limitBehavior(behavior); + reason.describe(CD); } // If the class has resilient ancestry, @objc just controls the runtime @@ -1194,24 +1259,25 @@ static Optional shouldMarkClassAsObjC(const ClassDecl *CD) { auto platform = prettyPlatformString(targetPlatform(ctx.LangOpts)); auto range = getMinOSVersionForClassStubs(target); auto *ancestor = getResilientAncestor(CD->getParentModule(), CD); - ctx.Diags.diagnose(attrLoc, - diag::objc_for_resilient_class, - ancestor->getName(), - platform, - range.getLowerEndpoint()) - .fixItRemove(attr->getRangeWithAt()) + swift::diagnoseAndRemoveAttr(CD, attr, + diag::objc_for_resilient_class, + ancestor->getName(), + platform, + range.getLowerEndpoint()) .limitBehavior(behavior); + reason.describe(CD); } - // Only allow ObjC-rooted classes to be @objc. + // Only allow actors and ObjC-rooted classes to be @objc. // (Leave a hole for test cases.) if (ancestry.contains(AncestryFlags::ObjC) && - !ancestry.contains(AncestryFlags::ClangImported)) { + !ancestry.contains(AncestryFlags::ClangImported) && + !CD->isActor()) { if (ctx.LangOpts.EnableObjCAttrRequiresFoundation) { - ctx.Diags.diagnose(attrLoc, - diag::invalid_objc_swift_rooted_class) - .fixItRemove(attr->getRangeWithAt()) + swift::diagnoseAndRemoveAttr(CD, attr, + diag::invalid_objc_swift_rooted_class) .limitBehavior(behavior); + reason.describe(CD); // If the user has not spelled out a superclass, offer to insert // 'NSObject'. We could also offer to replace the existing superclass, // but that's a touch aggressive. @@ -1229,9 +1295,9 @@ static Optional shouldMarkClassAsObjC(const ClassDecl *CD) { } if (!ctx.LangOpts.EnableObjCInterop) { - ctx.Diags.diagnose(attrLoc, diag::objc_interop_disabled) - .fixItRemove(attr->getRangeWithAt()) + swift::diagnoseAndRemoveAttr(CD, attr, diag::objc_interop_disabled) .limitBehavior(behavior); + reason.describe(CD); } } @@ -1333,18 +1399,22 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { // // @IBInspectable and @GKInspectable imply @objc quietly in Swift 3 // (where they warn on failure) and loudly in Swift 4 (error on failure). - if (VD->getAttrs().hasAttribute()) - return ObjCReason(ObjCReason::ExplicitlyIBOutlet); - if (VD->getAttrs().hasAttribute()) - return ObjCReason(ObjCReason::ExplicitlyIBAction); - if (VD->getAttrs().hasAttribute()) - return ObjCReason(ObjCReason::ExplicitlyIBSegueAction); - if (VD->getAttrs().hasAttribute()) - return ObjCReason(ObjCReason::ExplicitlyIBInspectable); - if (VD->getAttrs().hasAttribute()) - return ObjCReason(ObjCReason::ExplicitlyGKInspectable); - if (VD->getAttrs().hasAttribute()) - return ObjCReason(ObjCReason::ExplicitlyNSManaged); + if (auto attr = VD->getAttrs().getAttribute()) + return ObjCReason(ObjCReason::ExplicitlyIBOutlet, attr); + if (auto attr = VD->getAttrs().getAttribute()) + return ObjCReason(ObjCReason::ExplicitlyIBAction, attr); + if (auto attr = VD->getAttrs().getAttribute()) + return ObjCReason(ObjCReason::ExplicitlyIBSegueAction, attr); + if (auto attr = VD->getAttrs().getAttribute()) + return ObjCReason(ObjCReason::ExplicitlyIBInspectable, attr); + if (auto attr = VD->getAttrs().getAttribute()) + return ObjCReason(ObjCReason::ExplicitlyGKInspectable, attr); + if (auto attr = VD->getAttrs().getAttribute()) { + // Make sure the implicit DynamicAttr gets added before we potentially + // disable this attribute. + (void) VD->isDynamic(); + return ObjCReason(ObjCReason::ExplicitlyNSManaged, attr); + } // A member of an @objc protocol is implicitly @objc. if (isMemberOfObjCProtocol) { if (!VD->isProtocolRequirement()) @@ -1397,7 +1467,7 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { "@objc "); } - return ObjCReason(ObjCReason::ExplicitlyDynamic); + return ObjCReason(ObjCReason::ExplicitlyDynamic, attr); } } @@ -1520,9 +1590,11 @@ bool IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const { // Members of classes can be @objc. isObjC = shouldMarkAsObjC(VD, isa(VD)); } - else if (isa(VD)) { + else if (auto classDecl = dyn_cast(VD)) { // Classes can be @objc. + + // Protocols and enums can also be @objc, but this is covered by the // isObjC() check at the beginning. isObjC = shouldMarkAsObjC(VD, /*allowImplicit=*/false); @@ -1592,17 +1664,23 @@ bool IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const { Optional asyncConvention; Optional errorConvention; if (auto var = dyn_cast(VD)) { - if (!isRepresentableInObjC(var, *isObjC)) + if (!isRepresentableInObjC(var, *isObjC)) { + isObjC->setAttrInvalid(); return false; + } } else if (auto subscript = dyn_cast(VD)) { - if (!isRepresentableInObjC(subscript, *isObjC)) + if (!isRepresentableInObjC(subscript, *isObjC)) { + isObjC->setAttrInvalid(); return false; + } } else if (isa(VD)) { // Destructors need no additional checking. } else if (auto func = dyn_cast(VD)) { if (!isRepresentableInObjC( - func, *isObjC, asyncConvention, errorConvention)) + func, *isObjC, asyncConvention, errorConvention)) { + isObjC->setAttrInvalid(); return false; + } } // Note that this declaration is exposed to Objective-C. @@ -1622,13 +1700,14 @@ static ObjCSelector inferObjCName(ValueDecl *decl) { /// Set the @objc name. auto setObjCName = [&](ObjCSelector selector) { - // If there already is an @objc attribute, update its name. + // If there already is an @objc attribute, invalidate and remove it. (This + // helps with access notes.) if (attr) { - const_cast(attr)->setName(selector, /*implicit=*/true); - return; + attr->setInvalid(); + decl->getAttrs().removeAttribute(attr); } - // Otherwise, create an @objc attribute with the implicit name. + // Create an @objc attribute with the implicit name. attr = ObjCAttr::create(ctx, selector, /*implicitName=*/true); decl->getAttrs().add(attr); }; @@ -1693,14 +1772,15 @@ static ObjCSelector inferObjCName(ValueDecl *decl) { diagLoc = decl->getLoc(); if (!attr->getNameLocs().empty()) firstNameLoc = attr->getNameLocs().front(); - ctx.Diags.diagnose(diagLoc, - diag::objc_override_property_name_mismatch, - attr->getName()->getSelectorPieces()[0], - overriddenName) - .fixItReplaceChars(firstNameLoc, - attr->getRParenLoc(), - overriddenName.str()) - .limitBehavior(behavior); + softenIfAccessNote(decl, attr, + ctx.Diags.diagnose(diagLoc, + diag::objc_override_property_name_mismatch, + attr->getName()->getSelectorPieces()[0], + overriddenName) + .fixItReplaceChars(firstNameLoc, + attr->getRParenLoc(), + overriddenName.str()) + .limitBehavior(behavior)); overridden->diagnose(diag::overridden_here); } @@ -2103,7 +2183,7 @@ bool swift::fixDeclarationObjCName(InFlightDiagnostic &diag, const ValueDecl *de // about implicit ones because they don't have useful source location // information. auto attr = decl->getAttrs().getAttribute(); - if (attr && attr->isImplicit()) + if (attr && (attr->isImplicit() || attr->getLocation().isInvalid())) attr = nullptr; // If there is an @objc attribute with an explicit, incorrect witness @@ -2335,6 +2415,17 @@ getObjCMethodConflictDecls(const SourceFile::ObjCMethodConflict &conflict) { return classDecl->lookupDirect(selector, isInstanceMethod); } +static ObjCAttr *getObjCAttrIfFromAccessNote(ValueDecl *VD) { + if (auto objc = VD->getAttrs().getAttribute()) + if (objc->getAddedByAccessNote()) + return objc; + + if (auto accessor = dyn_cast(VD)) + return getObjCAttrIfFromAccessNote(accessor->getStorage()); + + return nullptr; +} + bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) { // If there were no conflicts, we're done. if (sf.ObjCMethodConflicts.empty()) @@ -2385,22 +2476,41 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) { // Diagnose the conflict. anyConflicts = true; - // If the first method is in an extension and the second is not, swap them - // so the primary diagnostic is on the extension method. - MutableArrayRef methodsRef(methods); - if (isa(methods[0]->getDeclContext()) && - !isa(methods[1]->getDeclContext())) { - std::swap(methodsRef[0], methodsRef[1]); + /// If true, \p a has a "weaker" claim on the selector than \p b, and the + /// conflict diagnostic should appear on \p a instead of \p b. + auto areBackwards = + [&](AbstractFunctionDecl *a, AbstractFunctionDecl *b) -> bool { + #define RULE(aCriterion, bCriterion) do { \ + bool _aCriterion = (aCriterion), _bCriterion = (bCriterion); \ + if (!_aCriterion && _bCriterion) \ + return true; \ + if (_aCriterion && !_bCriterion) \ + return false; \ + } while (0) + + // Is one of these from an access note? + RULE(getObjCAttrIfFromAccessNote(a), + getObjCAttrIfFromAccessNote(b)); + + // Is one of these from the main declaration? + RULE(!isa(a->getDeclContext()), + !isa(b->getDeclContext())); + + // Are these from different source files? If so, fall back to the order in + // which the declarations were type checked. + // FIXME: This is gross and nondeterministic. + if (a->getParentSourceFile() != b->getParentSourceFile()) + return false; - // Within a source file, use our canonical ordering. - } else if (methods[0]->getParentSourceFile() == - methods[1]->getParentSourceFile() && - !ordering(methods[0], methods[1])) { - std::swap(methodsRef[0], methodsRef[1]); - } + // Handle them in source order. + return !ordering(a, b); - // Otherwise, fall back to the order in which the declarations were type - // checked. + #undef RULE + }; + + MutableArrayRef methodsRef(methods); + if (areBackwards(methods[0], methods[1])) + std::swap(methodsRef[0], methodsRef[1]); auto originalMethod = methods.front(); auto conflictingMethods = methodsRef.slice(1); @@ -2414,18 +2524,25 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) { if (auto accessor = dyn_cast(originalMethod)) originalDecl = accessor->getStorage(); - if (diagInfo == origDiagInfo) { - Ctx.Diags.diagnose(conflictingDecl, diag::objc_redecl_same, - diagInfo.first, diagInfo.second, selector); + bool redeclSame = (diagInfo == origDiagInfo); + auto diag = Ctx.Diags.diagnose(conflictingDecl, + redeclSame ? diag::objc_redecl_same + : diag::objc_redecl, + diagInfo.first, diagInfo.second, + origDiagInfo.first, origDiagInfo.second, + selector); + + auto objcAttr = getObjCAttrIfFromAccessNote(conflictingDecl); + swift::softenIfAccessNote(conflictingDecl, objcAttr, diag); + if (objcAttr) + objcAttr->setInvalid(); + + if (redeclSame) Ctx.Diags.diagnose(originalDecl, diag::invalid_redecl_prev, originalDecl->getBaseName()); - } else { - Ctx.Diags.diagnose(conflictingDecl, diag::objc_redecl, diagInfo.first, - diagInfo.second, origDiagInfo.first, - origDiagInfo.second, selector); + else Ctx.Diags.diagnose(originalDecl, diag::objc_declared_here, origDiagInfo.first, origDiagInfo.second); - } } } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 29073210980e0..947bd6182b8cc 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -146,13 +146,11 @@ areAccessorsOverrideCompatible(const AbstractStorageDecl *storage, } bool swift::isOverrideBasedOnType(const ValueDecl *decl, Type declTy, - const ValueDecl *parentDecl, - Type parentDeclTy) { + const ValueDecl *parentDecl) { auto genericSig = decl->getInnermostDeclContext()->getGenericSignatureOfContext(); auto canDeclTy = declTy->getCanonicalType(genericSig); - auto canParentDeclTy = parentDeclTy->getCanonicalType(genericSig); auto declIUOAttr = decl->isImplicitlyUnwrappedOptional(); auto parentDeclIUOAttr = parentDecl->isImplicitlyUnwrappedOptional(); @@ -167,18 +165,33 @@ bool swift::isOverrideBasedOnType(const ValueDecl *decl, Type declTy, // We can still succeed with a subtype match later in // OverrideMatcher::match(). if (decl->getDeclContext()->getSelfClassDecl()) { - if (auto declGenericCtx = decl->getAsGenericContext()) { + if (auto declCtx = decl->getAsGenericContext()) { + auto *parentCtx = parentDecl->getAsGenericContext(); + + if (declCtx->isGeneric() != parentCtx->isGeneric()) + return false; + + if (declCtx->isGeneric() && + (declCtx->getGenericParams()->size() != + parentCtx->getGenericParams()->size())) + return false; + auto &ctx = decl->getASTContext(); auto sig = ctx.getOverrideGenericSignature(parentDecl, decl); - if (sig && - declGenericCtx->getGenericSignature().getCanonicalSignature() != + declCtx->getGenericSignature().getCanonicalSignature() != sig.getCanonicalSignature()) { return false; } } } + auto parentDeclTy = getMemberTypeForComparison(parentDecl, decl); + if (parentDeclTy->hasError()) + return false; + + auto canParentDeclTy = parentDeclTy->getCanonicalType(genericSig); + // If this is a constructor, let's compare only parameter types. if (isa(decl)) { // Within a protocol context, check for a failability mismatch. @@ -285,6 +298,11 @@ static bool areOverrideCompatibleSimple(ValueDecl *decl, auto genParentDecl = parentDecl->getAsGenericContext(); if (genDecl->isGeneric() != genParentDecl->isGeneric()) return false; + + if (genDecl->isGeneric() && + (genDecl->getGenericParams()->size() != + genParentDecl->getGenericParams()->size())) + return false; } // Factory initializers cannot be overridden. @@ -463,7 +481,7 @@ static bool noteFixableMismatchedTypes(ValueDecl *decl, const ValueDecl *base) { // input arguments. auto *fnType = baseTy->getAs(); baseTy = fnType->getResult(); - Type argTy = FunctionType::composeInput( + Type argTy = FunctionType::composeTuple( ctx, baseTy->getAs()->getParams(), false); auto diagKind = diag::override_type_mismatch_with_fixits_init; unsigned numArgs = baseInit->getParameters()->size(); @@ -908,13 +926,22 @@ SmallVector OverrideMatcher::match( (void)parentMethod; (void)parentStorage; - // Check whether the types are identical. - auto parentDeclTy = getMemberTypeForComparison(parentDecl, decl); - if (parentDeclTy->hasError()) - continue; + // If the generic requirements don't match, don't try anything else below, + // because it will compute an invalid interface type by applying malformed + // substitutions. + if (isClassOverride()) { + using Direction = ASTContext::OverrideGenericSignatureReqCheck; + if (decl->getAsGenericContext()) { + if (!ctx.overrideGenericSignatureReqsSatisfied( + parentDecl, decl, Direction::DerivedReqSatisfiedByBase)) { + continue; + } + } + } + // Check whether the types are identical. Type declTy = getDeclComparisonType(); - if (isOverrideBasedOnType(decl, declTy, parentDecl, parentDeclTy)) { + if (isOverrideBasedOnType(decl, declTy, parentDecl)) { matches.push_back({parentDecl, true}); continue; } @@ -944,6 +971,7 @@ SmallVector OverrideMatcher::match( } auto declFnTy = getDeclComparisonType()->getAs(); + auto parentDeclTy = getMemberTypeForComparison(parentDecl, decl); auto parentDeclFnTy = parentDeclTy->getAs(); if (declFnTy && parentDeclFnTy) { auto paramsAndResultMatch = [=]() -> bool { @@ -1028,7 +1056,7 @@ static void checkOverrideAccessControl(ValueDecl *baseDecl, ValueDecl *decl, } else if (baseHasOpenAccess && classDecl->hasOpenAccess(dc) && decl->getFormalAccess() < AccessLevel::Public && - !decl->isFinal()) { + !decl->isSemanticallyFinal()) { { auto diag = diags.diagnose(decl, diag::override_not_accessible, /*setter*/false, @@ -1102,26 +1130,6 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl, emittedMatchError = true; } - if (isClassOverride()) { - auto baseGenericCtx = baseDecl->getAsGenericContext(); - auto derivedGenericCtx = decl->getAsGenericContext(); - - using Direction = ASTContext::OverrideGenericSignatureReqCheck; - if (baseGenericCtx && derivedGenericCtx) { - if (!ctx.overrideGenericSignatureReqsSatisfied( - baseDecl, decl, Direction::DerivedReqSatisfiedByBase)) { - auto newSig = ctx.getOverrideGenericSignature(baseDecl, decl); - diags.diagnose(decl, diag::override_method_different_generic_sig, - decl->getBaseName(), - derivedGenericCtx->getGenericSignature()->getAsString(), - baseGenericCtx->getGenericSignature()->getAsString(), - newSig->getAsString()); - diags.diagnose(baseDecl, diag::overridden_here); - emittedMatchError = true; - } - } - } - // If we have an explicit ownership modifier and our parent doesn't, // complain. auto parentAttr = @@ -1150,7 +1158,7 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl, if (decl->getASTContext().isSwiftVersionAtLeast(5) && baseDecl->getInterfaceType()->hasDynamicSelfType() && !decl->getInterfaceType()->hasDynamicSelfType() && - !classDecl->isFinal()) { + !classDecl->isSemanticallyFinal()) { diags.diagnose(decl, diag::override_dynamic_self_mismatch); diags.diagnose(baseDecl, diag::overridden_here); } @@ -1438,7 +1446,6 @@ namespace { UNINTERESTING_ATTR(AccessControl) UNINTERESTING_ATTR(Alignment) UNINTERESTING_ATTR(AlwaysEmitIntoClient) - UNINTERESTING_ATTR(AsyncHandler) UNINTERESTING_ATTR(Borrowed) UNINTERESTING_ATTR(CDecl) UNINTERESTING_ATTR(Consuming) @@ -1449,7 +1456,6 @@ namespace { UNINTERESTING_ATTR(Exported) UNINTERESTING_ATTR(ForbidSerializingReference) UNINTERESTING_ATTR(GKInspectable) - UNINTERESTING_ATTR(CompletionHandlerAsync) UNINTERESTING_ATTR(HasMissingDesignatedInitializers) UNINTERESTING_ATTR(IBAction) UNINTERESTING_ATTR(IBDesignable) @@ -1481,6 +1487,7 @@ namespace { UNINTERESTING_ATTR(Required) UNINTERESTING_ATTR(Convenience) UNINTERESTING_ATTR(Semantics) + UNINTERESTING_ATTR(EmitAssemblyVisionRemarks) UNINTERESTING_ATTR(SetterAccess) UNINTERESTING_ATTR(TypeEraser) UNINTERESTING_ATTR(SPIAccessControl) @@ -1534,10 +1541,10 @@ namespace { UNINTERESTING_ATTR(ProjectedValueProperty) UNINTERESTING_ATTR(OriginallyDefinedIn) UNINTERESTING_ATTR(Actor) - UNINTERESTING_ATTR(ActorIndependent) + UNINTERESTING_ATTR(DistributedActor) + UNINTERESTING_ATTR(DistributedActorIndependent) UNINTERESTING_ATTR(GlobalActor) UNINTERESTING_ATTR(Async) - UNINTERESTING_ATTR(Spawn) UNINTERESTING_ATTR(Sendable) UNINTERESTING_ATTR(AtRethrows) @@ -1549,6 +1556,7 @@ namespace { UNINTERESTING_ATTR(UnsafeMainActor) UNINTERESTING_ATTR(ImplicitSelfCapture) UNINTERESTING_ATTR(InheritActorContext) + UNINTERESTING_ATTR(Isolated) #undef UNINTERESTING_ATTR void visitAvailableAttr(AvailableAttr *attr) { @@ -1940,7 +1948,7 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) { } // The overridden declaration cannot be 'final'. - if (base->isFinal() && !isAccessor) { + if (base->isSemanticallyFinal() && !isAccessor) { // Use a special diagnostic for overriding an actor's unownedExecutor // method. TODO: only if it's implicit? But then we need to // propagate implicitness in module interfaces. diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 6484a4156935c..e2e3eb1fcc982 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -35,7 +35,6 @@ #include "swift/AST/Expr.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" -#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" @@ -73,7 +72,7 @@ using namespace swift; static void checkInheritanceClause( llvm::PointerUnion declUnion) { const DeclContext *DC; - ArrayRef inheritedClause; + ArrayRef inheritedClause; const ExtensionDecl *ext = nullptr; const TypeDecl *typeDecl = nullptr; const Decl *decl; @@ -354,6 +353,13 @@ static void installCodingKeysIfNecessary(NominalTypeDecl *NTD) { (void)evaluateOrDefault(NTD->getASTContext().evaluator, req, {}); } +// TODO: same ugly hack as Codable does... +static void installDistributedActorIfNecessary(NominalTypeDecl *NTD) { + auto req = + ResolveImplicitMemberRequest{NTD, ImplicitMemberAction::ResolveDistributedActor}; + (void)evaluateOrDefault(NTD->getASTContext().evaluator, req, {}); +} + // Check for static properties that produce empty option sets // using a rawValue initializer with a value of '0' static void checkForEmptyOptionSet(const VarDecl *VD) { @@ -368,10 +374,10 @@ static void checkForEmptyOptionSet(const VarDecl *VD) { return; // Make sure this type conforms to OptionSet - auto *optionSetProto = VD->getASTContext().getProtocol(KnownProtocolKind::OptionSet); - bool conformsToOptionSet = (bool)TypeChecker::conformsToProtocol( - DC->getSelfTypeInContext(), - optionSetProto, DC); + bool conformsToOptionSet = + (bool)TypeChecker::conformsToKnownProtocol(DC->getSelfTypeInContext(), + KnownProtocolKind::OptionSet, + DC->getParentModule()); if (!conformsToOptionSet) return; @@ -620,7 +626,9 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const { // Thwart attempts to override the same declaration more than once. const auto *currentOverride = current->getOverriddenDecl(); const auto *otherOverride = other->getOverriddenDecl(); - if (currentOverride && currentOverride == otherOverride) { + const auto *otherInit = dyn_cast(other); + if (currentOverride && currentOverride == otherOverride && + !(otherInit && otherInit->isImplicit())) { current->diagnose(diag::multiple_override, current->getName()); other->diagnose(diag::multiple_override_prev, other->getName()); current->setInvalid(); @@ -1111,13 +1119,13 @@ static Optional buildDefaultInitializerString(DeclContext *dc, // Special-case the various types we might see here. auto type = pattern->getType(); + auto *module = dc->getParentModule(); + // For literal-convertible types, form the corresponding literal. -#define CHECK_LITERAL_PROTOCOL(Kind, String) \ - if (auto proto = TypeChecker::getProtocol( \ - type->getASTContext(), SourceLoc(), KnownProtocolKind::Kind)) { \ - if (TypeChecker::conformsToProtocol(type, proto, dc)) \ - return std::string(String); \ - } +#define CHECK_LITERAL_PROTOCOL(Kind, String) \ + if (TypeChecker::conformsToKnownProtocol(type, KnownProtocolKind::Kind, module)) \ + return std::string(String); + CHECK_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "[]") CHECK_LITERAL_PROTOCOL(ExpressibleByDictionaryLiteral, "[:]") CHECK_LITERAL_PROTOCOL(ExpressibleByUnicodeScalarLiteral, "\"\"") @@ -1218,6 +1226,7 @@ static std::string getFixItStringForDecodable(ClassDecl *CD, static void diagnoseClassWithoutInitializers(ClassDecl *classDecl) { ASTContext &C = classDecl->getASTContext(); C.Diags.diagnose(classDecl, diag::class_without_init, + classDecl->isExplicitActor(), classDecl->getDeclaredType()); // HACK: We've got a special case to look out for and diagnose specifically to @@ -1236,7 +1245,7 @@ static void diagnoseClassWithoutInitializers(ClassDecl *classDecl) { auto *decodableProto = C.getProtocol(KnownProtocolKind::Decodable); auto superclassType = superclassDecl->getDeclaredInterfaceType(); auto ref = TypeChecker::conformsToProtocol( - superclassType, decodableProto, superclassDecl); + superclassType, decodableProto, classDecl->getParentModule()); if (ref) { // super conforms to Decodable, so we've failed to inherit init(from:). // Let's suggest overriding it here. @@ -1264,7 +1273,7 @@ static void diagnoseClassWithoutInitializers(ClassDecl *classDecl) { // we can produce a slightly different diagnostic to suggest doing so. auto *encodableProto = C.getProtocol(KnownProtocolKind::Encodable); auto ref = TypeChecker::conformsToProtocol( - superclassType, encodableProto, superclassDecl); + superclassType, encodableProto, classDecl->getParentModule()); if (ref) { // We only want to produce this version of the diagnostic if the // subclass doesn't directly implement encode(to:). @@ -1411,104 +1420,147 @@ void TypeChecker::diagnoseDuplicateBoundVars(Pattern *pattern) { void TypeChecker::diagnoseDuplicateCaptureVars(CaptureListExpr *expr) { SmallVector captureListVars; for (auto &capture : expr->getCaptureList()) - captureListVars.push_back(capture.Var); + captureListVars.push_back(capture.getVar()); diagnoseDuplicateDecls(captureListVars); } -template InFlightDiagnostic -diagnoseAtAttrOrDecl(DeclAttribute *attr, ValueDecl *VD, - DiagIDAndArgs... idAndArgs) { - ASTContext &ctx = VD->getASTContext(); - if (attr->getLocation().isValid()) - return ctx.Diags.diagnose(attr->getLocation(), idAndArgs...); - else - return ctx.Diags.diagnose(VD, idAndArgs...); +static StringRef prettyPrintAttrs(const ValueDecl *VD, + ArrayRef attrs, + SmallVectorImpl &out) { + llvm::raw_svector_ostream os(out); + StreamPrinter printer(os); + + PrintOptions opts = PrintOptions::printEverything(); + VD->getAttrs().print(printer, opts, attrs, VD); + return StringRef(out.begin(), out.size()).drop_back(); +} + +static void diagnoseChangesByAccessNote( + ValueDecl *VD, + ArrayRef attrs, + Diag diagID, + Diag fixItID, + llvm::function_ref addFixIts) { + if (!VD->getASTContext().LangOpts.shouldRemarkOnAccessNoteSuccess() || + attrs.empty()) + return; + + // Generate string containing all attributes. + SmallString<64> attrString; + auto attrText = prettyPrintAttrs(VD, attrs, attrString); + + SourceLoc fixItLoc; + + auto reason = VD->getModuleContext()->getAccessNotes().Reason; + auto diag = VD->diagnose(diagID, reason, attrText, VD->getDescriptiveKind()); + for (auto attr : attrs) { + diag.highlight(attr->getRangeWithAt()); + if (fixItLoc.isInvalid()) + fixItLoc = attr->getRangeWithAt().Start; + } + diag.flush(); + + if (!fixItLoc) + fixItLoc = VD->getAttributeInsertionLoc(true); + + addFixIts(VD->getASTContext().Diags.diagnose(fixItLoc, fixItID, attrText), + attrString); } template static void addOrRemoveAttr(ValueDecl *VD, const AccessNotesFile ¬es, Optional expected, + SmallVectorImpl &removedAttrs, llvm::function_ref willCreate) { if (!expected) return; auto attr = VD->getAttrs().getAttribute(); if (*expected == (attr != nullptr)) return; - auto diagnoseChangeByAccessNote = - [&](Diag diagID, - Diag fixitID) -> InFlightDiagnostic { - bool isModifier = attr->isDeclModifier(); - - diagnoseAtAttrOrDecl(attr, VD, diagID, notes.Reason, isModifier, - attr->getAttrName(), VD->getDescriptiveKind()); - return diagnoseAtAttrOrDecl(attr, VD, fixitID, isModifier); - }; - if (*expected) { attr = willCreate(); attr->setAddedByAccessNote(); VD->getAttrs().add(attr); - SmallString<64> attrString; - llvm::raw_svector_ostream os(attrString); - attr->print(os, VD); - - diagnoseChangeByAccessNote(diag::attr_added_by_access_note, - diag::fixit_attr_added_by_access_note) - .fixItInsert(VD->getAttributeInsertionLoc(attr->isDeclModifier()), - attrString); + // Arrange for us to emit a remark about this attribute after type checking + // has ensured it's valid. + if (auto SF = VD->getDeclContext()->getParentSourceFile()) + SF->AttrsAddedByAccessNotes[VD].push_back(attr); } else { + removedAttrs.push_back(attr); VD->getAttrs().removeAttribute(attr); - diagnoseChangeByAccessNote(diag::attr_removed_by_access_note, - diag::fixit_attr_removed_by_access_note) - .fixItRemove(attr->getRangeWithAt()); } } +InFlightDiagnostic +swift::softenIfAccessNote(const Decl *D, const DeclAttribute *attr, + InFlightDiagnostic &diag) { + const ValueDecl *VD = dyn_cast(D); + if (!VD || !attr || !attr->getAddedByAccessNote()) + return std::move(diag); + + SmallString<32> attrString; + auto attrText = prettyPrintAttrs(VD, makeArrayRef(attr), attrString); + + ASTContext &ctx = D->getASTContext(); + auto behavior = ctx.LangOpts.getAccessNoteFailureLimit(); + return std::move(diag.wrapIn(diag::wrap_invalid_attr_added_by_access_note, + D->getModuleContext()->getAccessNotes().Reason, + ctx.AllocateCopy(attrText), D->getDescriptiveKind()) + .limitBehavior(behavior)); +} + static void applyAccessNote(ValueDecl *VD, const AccessNote ¬e, const AccessNotesFile ¬es) { ASTContext &ctx = VD->getASTContext(); + SmallVector removedAttrs; - addOrRemoveAttr(VD, notes, note.ObjC, [&]{ + addOrRemoveAttr(VD, notes, note.ObjC, removedAttrs, [&]{ return ObjCAttr::create(ctx, note.ObjCName, false); }); - addOrRemoveAttr(VD, notes, note.Dynamic, [&]{ + addOrRemoveAttr(VD, notes, note.Dynamic, removedAttrs, [&]{ return new (ctx) DynamicAttr(true); }); + // FIXME: If we ever have more attributes, we'll need to sort removedAttrs by + // SourceLoc. As it is, attrs are always before modifiers, so we're okay now. + + diagnoseChangesByAccessNote(VD, removedAttrs, + diag::attr_removed_by_access_note, + diag::fixit_attr_removed_by_access_note, + [&](InFlightDiagnostic diag, StringRef code) { + for (auto attr : llvm::reverse(removedAttrs)) + diag.fixItRemove(attr->getRangeWithAt()); + }); + if (note.ObjCName) { auto attr = VD->getAttrs().getAttribute(); assert(attr && "ObjCName set, but ObjCAttr not true or did not apply???"); if (!attr->hasName()) { + auto oldName = attr->getName(); attr->setName(*note.ObjCName, true); - diagnoseAtAttrOrDecl(attr, VD, - diag::attr_objc_name_changed_by_access_note, - notes.Reason, VD->getDescriptiveKind(), - *note.ObjCName); - - SmallString<64> newNameString; - llvm::raw_svector_ostream os(newNameString); - os << "("; - os << *note.ObjCName; - os << ")"; - - auto note = diagnoseAtAttrOrDecl(attr, VD, - diag::fixit_attr_objc_name_changed_by_access_note); - - if (attr->getLParenLoc().isValid()) - note.fixItReplace({ attr->getLParenLoc(), attr->getRParenLoc() }, - newNameString); - else - note.fixItInsertAfter(attr->getLocation(), newNameString); + + if (!ctx.LangOpts.shouldRemarkOnAccessNoteSuccess()) + return; + + VD->diagnose(diag::attr_objc_name_changed_by_access_note, + notes.Reason, VD->getDescriptiveKind(), *note.ObjCName); + + auto fixIt = + VD->diagnose(diag::fixit_attr_objc_name_changed_by_access_note); + fixDeclarationObjCName(fixIt, VD, oldName, *note.ObjCName); } else if (attr->getName() != *note.ObjCName) { - diagnoseAtAttrOrDecl(attr, VD, - diag::attr_objc_name_conflicts_with_access_note, - notes.Reason, VD->getDescriptiveKind(), - *note.ObjCName, *attr->getName()); + auto behavior = ctx.LangOpts.getAccessNoteFailureLimit(); + + VD->diagnose(diag::attr_objc_name_conflicts_with_access_note, + notes.Reason, VD->getDescriptiveKind(), *attr->getName(), + *note.ObjCName) + .highlight(attr->getRangeWithAt()) + .limitBehavior(behavior); } } } @@ -1518,6 +1570,38 @@ void TypeChecker::applyAccessNote(ValueDecl *VD) { ApplyAccessNoteRequest{VD}, {}); } +void swift::diagnoseAttrsAddedByAccessNote(SourceFile &SF) { + if (!SF.getASTContext().LangOpts.shouldRemarkOnAccessNoteSuccess()) + return; + + for (auto declAndAttrs : SF.AttrsAddedByAccessNotes) { + auto D = declAndAttrs.getFirst(); + SmallVector sortedAttrs; + llvm::append_range(sortedAttrs, declAndAttrs.getSecond()); + + // Filter out invalid attributes. + sortedAttrs.erase( + llvm::remove_if(sortedAttrs, [](DeclAttribute *attr) { + assert(attr->getAddedByAccessNote()); + return attr->isInvalid(); + }), sortedAttrs.end()); + if (sortedAttrs.empty()) continue; + + // Sort attributes by name. + llvm::sort(sortedAttrs, [](DeclAttribute * first, DeclAttribute * second) { + return first->getAttrName() < second->getAttrName(); + }); + sortedAttrs.erase(std::unique(sortedAttrs.begin(), sortedAttrs.end()), + sortedAttrs.end()); + + diagnoseChangesByAccessNote(D, sortedAttrs, diag::attr_added_by_access_note, + diag::fixit_attr_added_by_access_note, + [=](InFlightDiagnostic diag, StringRef code) { + diag.fixItInsert(D->getAttributeInsertionLoc(/*isModifier=*/true), code); + }); + } +} + evaluator::SideEffect ApplyAccessNoteRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const { AccessNotesFile ¬es = VD->getModuleContext()->getAccessNotes(); @@ -1553,8 +1637,6 @@ class DeclChecker : public DeclVisitor { DeclVisitor::visit(decl); - TypeChecker::checkUnsupportedProtocolType(decl); - if (auto VD = dyn_cast(decl)) { auto &Context = getASTContext(); TypeChecker::checkForForbiddenPrefix(Context, VD->getBaseName()); @@ -1562,7 +1644,7 @@ class DeclChecker : public DeclVisitor { // Force some requests, which can produce diagnostics. // Check redeclaration. - (void) evaluateOrDefault(decl->getASTContext().evaluator, + (void) evaluateOrDefault(Context.evaluator, CheckRedeclarationRequest{VD}, {}); // Compute access level. @@ -1576,6 +1658,12 @@ class DeclChecker : public DeclVisitor { (void) VD->isObjC(); (void) VD->isDynamic(); + // Check for actor isolation of top-level and local declarations. + // Declarations inside types are handled in checkConformancesInContext() + // to avoid cycles involving associated type inference. + if (!VD->getDeclContext()->isTypeContext()) + (void) getActorIsolation(VD); + // If this is a member of a nominal type, don't allow it to have a name of // "Type" or "Protocol" since we reserve the X.Type and X.Protocol // expressions to mean something builtin to the language. We *do* allow @@ -1585,7 +1673,7 @@ class DeclChecker : public DeclVisitor { VD->getName().isSimpleName(Context.Id_Protocol)) && VD->getNameLoc().isValid() && Context.SourceMgr.extractText({VD->getNameLoc(), 1}) != "`") { - auto &DE = getASTContext().Diags; + auto &DE = Context.Diags; DE.diagnose(VD->getNameLoc(), diag::reserved_member_name, VD->getName(), VD->getBaseIdentifier().str()); DE.diagnose(VD->getNameLoc(), diag::backticks_to_escape) @@ -1634,20 +1722,8 @@ class DeclChecker : public DeclVisitor { void visitOperatorDecl(OperatorDecl *OD) { TypeChecker::checkDeclAttributes(OD); checkRedeclaration(OD); - auto &Ctx = OD->getASTContext(); - if (auto *IOD = dyn_cast(OD)) { + if (auto *IOD = dyn_cast(OD)) (void)IOD->getPrecedenceGroup(); - } else { - auto nominalTypes = OD->getDesignatedNominalTypes(); - const auto wantsDesignatedTypes = - Ctx.TypeCheckerOpts.EnableOperatorDesignatedTypes; - if (nominalTypes.empty() && wantsDesignatedTypes) { - auto identifiers = OD->getIdentifiers(); - if (checkDesignatedTypes(OD, identifiers)) - OD->setInvalid(); - } - return; - } checkAccessControl(OD); } @@ -1792,31 +1868,6 @@ class DeclChecker : public DeclVisitor { }); } - /// If the given pattern binding has a property wrapper, check the - /// isolation and effects of the backing storage initializer. - void checkPropertyWrapperBackingInitializer(PatternBindingDecl *PBD) { - auto singleVar = PBD->getSingleVar(); - if (!singleVar) - return; - - if (!singleVar->hasAttachedPropertyWrapper()) - return; - - auto *backingVar = singleVar->getPropertyWrapperBackingProperty(); - if (!backingVar) - return; - - auto backingPBD = backingVar->getParentPatternBinding(); - if (!backingPBD) - return; - - auto initInfo = singleVar->getPropertyWrapperInitializerInfo(); - if (auto initializer = initInfo.getInitFromWrappedValue()) { - checkPropertyWrapperActorIsolation(backingPBD, initializer); - TypeChecker::checkPropertyWrapperEffects(backingPBD, initializer); - } - } - void visitPatternBindingDecl(PatternBindingDecl *PBD) { DeclContext *DC = PBD->getDeclContext(); @@ -1963,8 +2014,6 @@ class DeclChecker : public DeclVisitor { } } } - - checkPropertyWrapperBackingInitializer(PBD); } void visitSubscriptDecl(SubscriptDecl *SD) { @@ -2026,6 +2075,14 @@ class DeclChecker : public DeclVisitor { } } + // Reject "class" methods on actors. + if (SD->getStaticSpelling() == StaticSpellingKind::KeywordClass && + SD->getDeclContext()->getSelfClassDecl() && + SD->getDeclContext()->getSelfClassDecl()->isActor()) { + SD->diagnose(diag::class_subscript_not_in_class, false) + .fixItReplace(SD->getStaticLoc(), "static"); + } + // Now check all the accessors. SD->visitEmittedAccessors([&](AccessorDecl *accessor) { visit(accessor); @@ -2190,6 +2247,7 @@ class DeclChecker : public DeclVisitor { TypeChecker::addImplicitConstructors(SD); installCodingKeysIfNecessary(SD); + installDistributedActorIfNecessary(SD); TypeChecker::checkDeclAttributes(SD); @@ -2317,9 +2375,15 @@ class DeclChecker : public DeclVisitor { if (auto superclass = CD->getSuperclassDecl()) { // Actors cannot have superclasses, nor can they be superclasses. if (CD->isActor() && !superclass->isNSObject()) - CD->diagnose(diag::actor_inheritance); + CD->diagnose(diag::actor_inheritance, + /*distributed=*/CD->isDistributedActor()); else if (superclass->isActor()) - CD->diagnose(diag::actor_inheritance); + CD->diagnose(diag::actor_inheritance, + /*distributed=*/CD->isDistributedActor()); + } + + if (CD->isDistributedActor()) { + TypeChecker::checkDistributedActor(CD); } // Force lowering of stored properties. @@ -2330,6 +2394,9 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkDeclAttributes(CD); + if (CD->isActor()) + TypeChecker::checkConcurrencyAvailability(CD->getLoc(), CD); + for (Decl *Member : CD->getABIMembers()) visit(Member); @@ -2489,8 +2556,8 @@ class DeclChecker : public DeclVisitor { llvm::errs() << "Canonical requirement signature: "; auto canRequirementSig = - CanGenericSignature::getCanonical(requirementsSig->getGenericParams(), - requirementsSig->getRequirements()); + CanGenericSignature::getCanonical(requirementsSig.getGenericParams(), + requirementsSig.getRequirements()); canRequirementSig->print(llvm::errs()); llvm::errs() << "\n"; } @@ -2580,6 +2647,13 @@ class DeclChecker : public DeclVisitor { return AFD->getResilienceExpansion() != ResilienceExpansion::Minimal; } + /// FIXME: This is an egregious hack to turn off availability checking + /// for specific functions that were missing availability in older versions + /// of existing libraries that we must nonethess still support. + static bool hasHistoricallyWrongAvailability(FuncDecl *func) { + return func->getName().isCompoundName("swift_deletedAsyncMethodError", { }); + } + void visitFuncDecl(FuncDecl *FD) { // Force these requests in case they emit diagnostics. (void) FD->getInterfaceType(); @@ -2624,6 +2698,10 @@ class DeclChecker : public DeclVisitor { checkImplementationOnlyOverride(FD); + if (FD->getAsyncLoc().isValid() && + !hasHistoricallyWrongAvailability(FD)) + TypeChecker::checkConcurrencyAvailability(FD->getAsyncLoc(), FD); + if (requiresDefinition(FD) && !FD->hasBody()) { // Complain if we should have a body. FD->diagnose(diag::func_decl_without_brace); @@ -2662,11 +2740,19 @@ class DeclChecker : public DeclVisitor { } } + // Reject "class" methods on actors. + if (StaticSpelling == StaticSpellingKind::KeywordClass && + FD->getDeclContext()->getSelfClassDecl() && + FD->getDeclContext()->getSelfClassDecl()->isActor()) { + FD->diagnose(diag::class_func_not_in_class, false) + .fixItReplace(FD->getStaticLoc(), "static"); + } + // Member functions need some special validation logic. if (FD->getDeclContext()->isTypeContext()) { if (FD->isOperator() && !isMemberOperator(FD, nullptr)) { auto selfNominal = FD->getDeclContext()->getSelfNominalTypeDecl(); - auto isProtocol = selfNominal && isa(selfNominal); + auto isProtocol = isa_and_nonnull(selfNominal); // We did not find 'Self'. Complain. FD->diagnose(diag::operator_in_unrelated_type, FD->getDeclContext()->getDeclaredInterfaceType(), isProtocol, @@ -2680,8 +2766,8 @@ class DeclChecker : public DeclVisitor { if (auto CDeclAttr = FD->getAttrs().getAttribute()) { Optional asyncConvention; Optional errorConvention; - if (isRepresentableInObjC(FD, ObjCReason::ExplicitlyCDecl, - asyncConvention, errorConvention)) { + ObjCReason reason(ObjCReason::ExplicitlyCDecl, CDeclAttr); + if (isRepresentableInObjC(FD, reason, asyncConvention, errorConvention)) { if (FD->hasAsync()) { FD->setForeignAsyncConvention(*asyncConvention); getASTContext().Diags.diagnose(CDeclAttr->getLocation(), @@ -2691,6 +2777,8 @@ class DeclChecker : public DeclVisitor { getASTContext().Diags.diagnose(CDeclAttr->getLocation(), diag::cdecl_throws); } + } else { + reason.setAttrInvalid(); } } } @@ -2827,6 +2915,9 @@ class DeclChecker : public DeclVisitor { checkAccessControl(ED); checkExplicitAvailability(ED); + + if (nominal->isDistributedActor()) + TypeChecker::checkDistributedActor(dyn_cast(nominal)); } void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD) { @@ -2866,6 +2957,9 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkDeclAttributes(CD); TypeChecker::checkParameterList(CD->getParameters(), CD); + if (CD->getAsyncLoc().isValid()) + TypeChecker::checkConcurrencyAvailability(CD->getAsyncLoc(), CD); + // Check whether this initializer overrides an initializer in its // superclass. if (!checkOverrides(CD)) { diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp new file mode 100644 index 0000000000000..3257f07049110 --- /dev/null +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -0,0 +1,265 @@ +//===--- TypeCheckDistributed.cpp - Distributed ---------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 implements type checking support for Swift's concurrency model. +// +//===----------------------------------------------------------------------===// +#include "TypeCheckConcurrency.h" +#include "TypeCheckDistributed.h" +#include "TypeChecker.h" +#include "TypeCheckType.h" +#include "swift/Strings.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/Initializer.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/NameLookupRequests.h" +#include "swift/AST/TypeCheckRequests.h" +#include "swift/AST/TypeVisitor.h" +#include "swift/AST/ExistentialLayout.h" + +using namespace swift; + +// ==== ------------------------------------------------------------------------ + +bool swift::ensureDistributedModuleLoaded(Decl *decl) { + auto &C = decl->getASTContext(); + auto moduleAvailable = evaluateOrDefault( + C.evaluator, DistributedModuleIsAvailableRequest{decl}, false); + return moduleAvailable; +} + +bool +DistributedModuleIsAvailableRequest::evaluate(Evaluator &evaluator, + Decl *decl) const { + auto &C = decl->getASTContext(); + + if (C.getLoadedModule(C.Id_Distributed)) + return true; + + // seems we're missing the _Distributed module, ask to import it explicitly + decl->diagnose(diag::distributed_actor_needs_explicit_distributed_import); + return false; +} + +// ==== ------------------------------------------------------------------------ + +bool IsDistributedActorRequest::evaluate( + Evaluator &evaluator, NominalTypeDecl *nominal) const { + // Protocols are actors if they inherit from `DistributedActor`. + if (auto protocol = dyn_cast(nominal)) { + auto &ctx = protocol->getASTContext(); + auto *distributedActorProtocol = ctx.getDistributedActorDecl(); + return (protocol == distributedActorProtocol || + protocol->inheritsFrom(distributedActorProtocol)); + } + + // Class declarations are 'distributed actors' if they are declared with 'distributed actor' + auto classDecl = dyn_cast(nominal); + if(!classDecl) + return false; + + return classDecl->isExplicitDistributedActor(); +} + +AbstractFunctionDecl *GetDistributedRemoteFuncRequest::evaluate( + Evaluator &evaluator, AbstractFunctionDecl *func) const { + + if (!func->isDistributed()) + return nullptr; + + auto &C = func->getASTContext(); + DeclContext *DC = func->getDeclContext(); + + // not via `ensureDistributedModuleLoaded` to avoid generating a warning, + // we won't be emitting the offending decl after all. + if (!C.getLoadedModule(C.Id_Distributed)) + return nullptr; + + // Locate the actor decl that the member must be synthesized to. + // TODO(distributed): should this just be added to the extension instead when we're in one? + ClassDecl *decl = dyn_cast(DC); + if (!decl) { + if (auto ED = dyn_cast(DC)) { + decl = dyn_cast(ED->getExtendedNominal()); + } + } + + /// A distributed func cannot be added to a non-distributed actor; + /// If the 'decl' was not a distributed actor we must have declared and + /// requested it from a illegal context, let's just ignore the synthesis. + assert(decl && "Can't find actor detect to add implicit _remote function to"); + return TypeChecker::addImplicitDistributedActorRemoteFunction(decl, func); +} + +// ==== ------------------------------------------------------------------------ + +/// Check whether the function is a proper distributed function +/// +/// \param diagnose Whether to emit a diagnostic when a problem is encountered. +/// +/// \returns \c true if there was a problem with adding the attribute, \c false +/// otherwise. +bool swift::checkDistributedFunction(FuncDecl *func, bool diagnose) { + // === All parameters and the result type must be Codable + + auto &C = func->getASTContext(); + auto encodableType = C.getProtocol(KnownProtocolKind::Encodable); + auto decodableType = C.getProtocol(KnownProtocolKind::Decodable); + + auto module = func->getParentModule(); + + // --- Check parameters for 'Codable' conformance + for (auto param : *func->getParameters()) { + auto paramTy = func->mapTypeIntoContext(param->getInterfaceType()); + if (TypeChecker::conformsToProtocol(paramTy, encodableType, module).isInvalid() || + TypeChecker::conformsToProtocol(paramTy, decodableType, module).isInvalid()) { + if (diagnose) + func->diagnose( + diag::distributed_actor_func_param_not_codable, + param->getArgumentName().str(), + param->getInterfaceType() + ); + // TODO: suggest a fixit to add Codable to the type? + return true; + } + } + + // --- Result type must be either void or a codable type + auto resultType = func->mapTypeIntoContext(func->getResultInterfaceType()); + if (!resultType->isVoid()) { + if (TypeChecker::conformsToProtocol(resultType, decodableType, module).isInvalid() || + TypeChecker::conformsToProtocol(resultType, encodableType, module).isInvalid()) { + if (diagnose) + func->diagnose( + diag::distributed_actor_func_result_not_codable, + func->getResultInterfaceType() + ); + // TODO: suggest a fixit to add Codable to the type? + return true; + } + } + + // === Check _remote functions + ClassDecl *actorDecl = dyn_cast(func->getParent()); + assert(actorDecl && actorDecl->isDistributedActor()); + + // _remote function for a distributed function must not be implemented by end-users, + // it must be the specific implementation synthesized by the compiler. + auto remoteFuncDecl = actorDecl->lookupDirectRemoteFunc(func); + if (remoteFuncDecl && !remoteFuncDecl->isSynthesized()) { + if (diagnose) { + func->diagnose(diag::distributed_actor_remote_func_implemented_manually, + func->getBaseIdentifier(), + // TODO: make general function to get the _remote identifier + C.getIdentifier("_remote_" + func->getBaseIdentifier().str().str())); + } + return true; + } + + return false; +} + +void swift::checkDistributedActorProperties(const ClassDecl *decl) { + auto &C = decl->getASTContext(); + + for (auto member : decl->getMembers()) { + if (auto prop = dyn_cast(member)) { + if (prop->isSynthesized()) + continue; + + auto id = prop->getName(); + if (id == C.Id_actorTransport || id == C.Id_id) { + prop->diagnose(diag::distributed_actor_user_defined_special_property, + id); + } + } + } +} + +void swift::checkDistributedActorConstructor(const ClassDecl *decl, ConstructorDecl *ctor) { + // bail out unless distributed actor, only those have special rules to check here + if (!decl->isDistributedActor()) + return; + + // Only designated initializers need extra checks + if (!ctor->isDesignatedInit()) + return; + + // === Designated initializers must accept exactly one ActorTransport + auto &C = ctor->getASTContext(); + auto module = ctor->getParentModule(); + + SmallVector transportParams; + int transportParamsCount = 0; + auto protocolDecl = C.getProtocol(KnownProtocolKind::ActorTransport); + auto protocolTy = protocolDecl->getDeclaredInterfaceType(); + + for (auto param : *ctor->getParameters()) { + auto paramTy = ctor->mapTypeIntoContext(param->getInterfaceType()); + auto conformance = TypeChecker::conformsToProtocol(paramTy, protocolDecl, module); + + if (paramTy->isEqual(protocolTy) || !conformance.isInvalid()) { + transportParamsCount += 1; + transportParams.push_back(param); + } + } + + // missing transport parameter + if (transportParamsCount == 0) { + ctor->diagnose(diag::distributed_actor_designated_ctor_missing_transport_param, + ctor->getName()); + // TODO(distributed): offer fixit to insert 'transport: ActorTransport' + return; + } + + // ok! We found exactly one transport parameter + if (transportParamsCount == 1) + return; + + // TODO(distributed): rdar://81824959 report the error on the offending (2nd) matching parameter + // Or maybe we can issue a note about the other offending params? + ctor->diagnose(diag::distributed_actor_designated_ctor_must_have_one_transport_param, + ctor->getName(), transportParamsCount); +} + +// ==== ------------------------------------------------------------------------ + +void TypeChecker::checkDistributedActor(ClassDecl *decl) { + if (!decl) + return; + + // ==== Ensure the _Distributed module is available, + // without it there's no reason to check the decl in more detail anyway. + if (!swift::ensureDistributedModuleLoaded(decl)) + return; + + // ==== Constructors + // --- Get the default initializer + // If applicable, this will create the default 'init(transport:)' initializer + (void)decl->getDefaultInitializer(); + + for (auto member : decl->getMembers()) { + // --- Check all constructors + if (auto ctor = dyn_cast(member)) + checkDistributedActorConstructor(decl, ctor); + + // --- synthesize _remote functions for distributed functions + if (auto func = dyn_cast(member)) + (void)addImplicitDistributedActorRemoteFunction(decl, func); + } + + // ==== Properties + // --- Check for any illegal re-definitions + checkDistributedActorProperties(decl); +} + diff --git a/lib/Sema/TypeCheckDistributed.h b/lib/Sema/TypeCheckDistributed.h new file mode 100644 index 0000000000000..c4647f605e15b --- /dev/null +++ b/lib/Sema/TypeCheckDistributed.h @@ -0,0 +1,49 @@ +//===-- TypeCheckDistributed.h - Distributed actor typechecking -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 type checking support for Swift's distributed actor model. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SEMA_TYPECHECKDISTRIBUTED_H +#define SWIFT_SEMA_TYPECHECKDISTRIBUTED_H + +#include "swift/AST/ConcreteDeclRef.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/Type.h" + +namespace swift { + +class ClassDecl; +class ConstructorDecl; +class Decl; +class FuncDecl; + +/******************************************************************************/ +/********************* Distributed Actor Type Checking ************************/ +/******************************************************************************/ + +// Diagnose an error if the _Distributed module is not loaded. +bool ensureDistributedModuleLoaded(Decl *decl); + +/// Check for illegal property declarations (e.g. re-declaring transport or id) +void checkDistributedActorProperties(const ClassDecl *decl); + +/// The local and resolve distributed actor constructors have special rules to check. +void checkDistributedActorConstructor(const ClassDecl *decl, ConstructorDecl *ctor); + +bool checkDistributedFunction(FuncDecl *decl, bool diagnose); + +} + + +#endif /* SWIFT_SEMA_TYPECHECKDISTRIBUTED_H */ diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index c43b524157140..db5e787503477 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -112,12 +112,10 @@ PolymorphicEffectKindRequest::evaluate(Evaluator &evaluator, return PolymorphicEffectKind::Always; } - if (auto genericSig = decl->getGenericSignature()) { - for (auto req : genericSig->getRequirements()) { - if (req.getKind() == RequirementKind::Conformance) { - if (req.getProtocolDecl()->hasPolymorphicEffect(kind)) { - return PolymorphicEffectKind::ByConformance; - } + for (auto req : decl->getGenericSignature().getRequirements()) { + if (req.getKind() == RequirementKind::Conformance) { + if (req.getProtocolDecl()->hasPolymorphicEffect(kind)) { + return PolymorphicEffectKind::ByConformance; } } } @@ -418,7 +416,7 @@ class EffectsHandlingWalker : public ASTWalker { if (auto ic = dyn_cast(D)) { recurse = asImpl().checkIfConfig(ic); } else if (auto patternBinding = dyn_cast(D)) { - if (patternBinding->isSpawnLet()) + if (patternBinding->isAsyncLet()) recurse = asImpl().checkAsyncLet(patternBinding); } else { recurse = ShouldNotRecurse; @@ -648,8 +646,8 @@ class Classification { ConditionalEffectKind conditionalKind, PotentialEffectReason reason) { Classification result; - for (auto k : kinds) + result.merge(forEffect(k, conditionalKind, reason)); return result; @@ -758,7 +756,7 @@ class ApplyClassifier { /// Check to see if the given function application throws or is async. Classification classifyApply(ApplyExpr *E) { if (isa(E)) { - assert(!E->implicitlyAsync()); + assert(!E->isImplicitlyAsync()); return Classification(); } @@ -774,8 +772,9 @@ class ApplyClassifier { // If the function doesn't have any effects, we're done here. if (!fnType->isThrowing() && + !E->implicitlyThrows() && !fnType->isAsync() && - !E->implicitlyAsync()) { + !E->isImplicitlyAsync()) { return Classification(); } @@ -793,8 +792,8 @@ class ApplyClassifier { auto classifyApplyEffect = [&](EffectKind kind) { if (!fnType->hasEffect(kind) && - !(kind == EffectKind::Async && - E->implicitlyAsync())) { + !(kind == EffectKind::Async && E->isImplicitlyAsync()) && + !(kind == EffectKind::Throws && E->implicitlyThrows())) { return; } @@ -1689,7 +1688,7 @@ class Context { bool suggestTryFixIt = reasonKind == PotentialEffectReason::Kind::Apply; if (reasonKind == PotentialEffectReason::Kind::AsyncLet) { - message = diag::throwing_spawn_let_without_try; + message = diag::throwing_async_let_without_try; } else if (reasonKind == PotentialEffectReason::Kind::PropertyAccess) { message = diag::throwing_prop_access_without_try; @@ -1922,9 +1921,9 @@ class Context { if (auto declRef = dyn_cast(e)) { if (auto var = dyn_cast(declRef->getDecl())) { - if (var->isSpawnLet()) { + if (var->isAsyncLet()) { Diags.diagnose( - e->getLoc(), diag::spawn_let_in_illegal_context, + e->getLoc(), diag::async_let_in_illegal_context, var->getName(), static_cast(getKind())); return; } @@ -1932,10 +1931,10 @@ class Context { } } else if (auto patternBinding = dyn_cast_or_null( node.dyn_cast())) { - if (patternBinding->isSpawnLet()) { + if (patternBinding->isAsyncLet()) { auto var = patternBinding->getAnchoringVarDecl(0); Diags.diagnose( - e->getLoc(), diag::spawn_let_in_illegal_context, + e->getLoc(), diag::async_let_in_illegal_context, var->getName(), static_cast(getKind())); return; } @@ -2520,7 +2519,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker PotentialEffectReason::forPropertyAccess())); } else if (E->isImplicitlyAsync()) { - checkThrowAsyncSite(E, /*requiresTry=*/false, + checkThrowAsyncSite(E, /*requiresTry=*/E->isImplicitlyThrows(), Classification::forUnconditional(EffectKind::Async, PotentialEffectReason::forPropertyAccess())); @@ -2529,7 +2528,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker // "Async let" declarations are treated as an asynchronous call // (to the underlying task's "get"). If the initializer was throwing, // then the access is also treated as throwing. - if (var->isSpawnLet()) { + if (var->isAsyncLet()) { // If the initializer could throw, we will have a 'try' in the // application of its autoclosure. bool throws = false; @@ -2651,15 +2650,10 @@ class CheckEffectsCoverage : public EffectsHandlingWalker Expr *expr = E.dyn_cast(); Expr *anchor = walkToAnchor(expr, parentMap, CurContext.isWithinInterpolatedString()); - - auto key = uncoveredAsync.find(anchor); - if (key == uncoveredAsync.end()) { - uncoveredAsync.insert({anchor, {}}); + if (uncoveredAsync.find(anchor) == uncoveredAsync.end()) errorOrder.push_back(anchor); - } - uncoveredAsync[anchor].emplace_back( - *expr, - classification.getAsyncReason()); + uncoveredAsync[anchor].emplace_back(*expr, + classification.getAsyncReason()); } } @@ -2692,7 +2686,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker CurContext.diagnoseUnhandledThrowSite(Ctx.Diags, E, isTryCovered, classification.getThrowReason()); } else if (!isTryCovered) { - CurContext.diagnoseUncoveredThrowSite(Ctx, E, + CurContext.diagnoseUncoveredThrowSite(Ctx, E, // we want this one to trigger classification.getThrowReason()); } break; @@ -2831,9 +2825,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker case PotentialEffectReason::Kind::AsyncLet: if (auto declR = dyn_cast(&diag.expr)) { if (auto var = dyn_cast(declR->getDecl())) { - if (var->isSpawnLet()) { + if (var->isAsyncLet()) { Ctx.Diags.diagnose(declR->getLoc(), - diag::spawn_let_without_await, + diag::async_let_without_await, var->getName()); continue; } @@ -2862,7 +2856,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker break; case AutoClosureExpr::Kind::AsyncLet: Ctx.Diags.diagnose(diag.expr.getStartLoc(), - diag::async_call_without_await_in_spawn_let); + diag::async_call_without_await_in_async_let); break; case AutoClosureExpr::Kind::SingleCurryThunk: case AutoClosureExpr::Kind::DoubleCurryThunk: @@ -2874,7 +2868,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker } auto *call = dyn_cast(&diag.expr); - if (call && call->implicitlyAsync()) { + if (call && call->isImplicitlyAsync()) { // Emit a tailored note if the call is implicitly async, meaning the // callee is isolated to an actor. auto callee = call->getCalledValue(); diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index 8eb3a80c34bca..1d9dd655789bf 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -355,7 +355,12 @@ static Expr *makeBinOp(ASTContext &Ctx, Expr *Op, Expr *LHS, Expr *RHS, if (auto *ifExpr = dyn_cast(Op)) { // Resolve the ternary expression. - assert(!ifExpr->isFolded() && "already folded if expr in sequence?!"); + if (!Ctx.CompletionCallback) { + // In code completion we might call preCheckExpression twice - once for + // the first pass and once for the second pass. This is fine since + // preCheckExpression idempotent. + assert(!ifExpr->isFolded() && "already folded if expr in sequence?!"); + } ifExpr->setCondExpr(LHS); ifExpr->setElseExpr(RHS); return ifExpr; @@ -363,7 +368,12 @@ static Expr *makeBinOp(ASTContext &Ctx, Expr *Op, Expr *LHS, Expr *RHS, if (auto *assign = dyn_cast(Op)) { // Resolve the assignment expression. - assert(!assign->isFolded() && "already folded assign expr in sequence?!"); + if (!Ctx.CompletionCallback) { + // In code completion we might call preCheckExpression twice - once for + // the first pass and once for the second pass. This is fine since + // preCheckExpression idempotent. + assert(!assign->isFolded() && "already folded assign expr in sequence?!"); + } assign->setDest(LHS); assign->setSrc(RHS); return assign; @@ -371,7 +381,12 @@ static Expr *makeBinOp(ASTContext &Ctx, Expr *Op, Expr *LHS, Expr *RHS, if (auto *as = dyn_cast(Op)) { // Resolve the 'as' or 'is' expression. - assert(!as->isFolded() && "already folded 'as' expr in sequence?!"); + if (!Ctx.CompletionCallback) { + // In code completion we might call preCheckExpression twice - once for + // the first pass and once for the second pass. This is fine since + // preCheckExpression idempotent. + assert(!as->isFolded() && "already folded 'as' expr in sequence?!"); + } assert(RHS == as && "'as' with non-type RHS?!"); as->setSubExpr(LHS); return as; @@ -379,23 +394,19 @@ static Expr *makeBinOp(ASTContext &Ctx, Expr *Op, Expr *LHS, Expr *RHS, if (auto *arrow = dyn_cast(Op)) { // Resolve the '->' expression. - assert(!arrow->isFolded() && "already folded '->' expr in sequence?!"); + if (!Ctx.CompletionCallback) { + // In code completion we might call preCheckExpression twice - once for + // the first pass and once for the second pass. This is fine since + // preCheckExpression idempotent. + assert(!arrow->isFolded() && "already folded '->' expr in sequence?!"); + } arrow->setArgsExpr(LHS); arrow->setResultExpr(RHS); return arrow; } - // Build the argument to the operation. - Expr *ArgElts[] = { LHS, RHS }; - auto ArgElts2 = Ctx.AllocateCopy(MutableArrayRef(ArgElts)); - TupleExpr *Arg = TupleExpr::create(Ctx, - SourceLoc(), - ArgElts2, { }, { }, SourceLoc(), - /*HasTrailingClosure=*/false, - /*Implicit=*/true); - // Build the operation. - return new (Ctx) BinaryExpr(Op, Arg, Op->isImplicit()); + return BinaryExpr::create(Ctx, LHS, Op, RHS, Op->isImplicit()); } namespace { diff --git a/lib/Sema/TypeCheckExprObjC.cpp b/lib/Sema/TypeCheckExprObjC.cpp index 11f9e507f49ce..8ca41bce02588 100644 --- a/lib/Sema/TypeCheckExprObjC.cpp +++ b/lib/Sema/TypeCheckExprObjC.cpp @@ -204,6 +204,7 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, switch (auto kind = component.getKind()) { case KeyPathExpr::Component::Kind::Invalid: case KeyPathExpr::Component::Kind::Identity: + case KeyPathExpr::Component::Kind::CodeCompletion: continue; case KeyPathExpr::Component::Kind::UnresolvedProperty: diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index e7e78e7e15c14..c1dcdafef2777 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -13,15 +13,16 @@ // This file implements support for generics. // //===----------------------------------------------------------------------===// -#include "TypeChecker.h" #include "TypeCheckProtocol.h" #include "TypeCheckType.h" +#include "TypeChecker.h" +#include "swift/AST/ASTWalker.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignatureBuilder.h" -#include "swift/AST/ProtocolConformance.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeResolutionStage.h" #include "swift/AST/Types.h" @@ -78,6 +79,29 @@ TypeChecker::gatherGenericParamBindingsText( return result.str().str(); } +// An alias to avoid repeating the `SmallVector`'s size parameter. +using CollectedOpaqueReprs = SmallVector; + +/// Walk `repr` recursively, collecting any `OpaqueReturnTypeRepr`s. +static CollectedOpaqueReprs collectOpaqueReturnTypeReprs(TypeRepr *repr) { + class Walker : public ASTWalker { + CollectedOpaqueReprs &Reprs; + + public: + explicit Walker(CollectedOpaqueReprs &reprs) : Reprs(reprs) {} + + bool walkToTypeReprPre(TypeRepr *repr) override { + if (auto opaqueRepr = dyn_cast(repr)) + Reprs.push_back(opaqueRepr); + return true; + } + }; + + CollectedOpaqueReprs reprs; + repr->walk(Walker(reprs)); + return reprs; +} + // // Generic functions // @@ -92,6 +116,14 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator, auto *dc = originatingDecl->getInnermostDeclContext(); auto &ctx = dc->getASTContext(); + // Support for structural opaque result types is hidden behind a compiler flag + // until the proposal gets approved. + if (!ctx.LangOpts.EnableExperimentalStructuralOpaqueTypes && + !isa(repr)) { + ctx.Diags.diagnose(repr->getLoc(), diag::structural_opaque_types_are_experimental); + return nullptr; + } + // Protocol requirements can't have opaque return types. // // TODO: Maybe one day we could treat this as sugar for an associated type. @@ -105,14 +137,31 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator, fixitLoc = originatingDecl->getStartLoc(); } - ctx.Diags.diagnose(repr->getLoc(), - diag::opaque_type_in_protocol_requirement) - .fixItInsert(fixitLoc, "associatedtype <#AssocType#>\n") - .fixItReplace(repr->getSourceRange(), "<#AssocType#>"); - + std::string result; + const char *const placeholder = "<#AssocType#>"; + { + llvm::raw_string_ostream out(result); + out << "associatedtype " << placeholder << ": "; + // FIXME [OPAQUE SUPPORT]: to produce the right associate type for the + // replacement in general, we would need to recurse into the type repr and + // replace every `OpaqueReturnType` with its 'constraint'. Things get + // trickier when we allow named opaque return types. + if (isa(repr)) { + cast(repr)->getConstraint()->print(out); + } else { + out << "<#type#>"; + } + out << "\n"; + } + + ctx.Diags + .diagnose(repr->getLoc(), diag::opaque_type_in_protocol_requirement) + .fixItInsert(fixitLoc, result) + .fixItReplace(repr->getSourceRange(), placeholder); + return nullptr; } - + // Check the availability of the opaque type runtime support. if (!ctx.LangOpts.DisableAvailabilityChecking) { auto runningOS = @@ -128,67 +177,86 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator, } } - // Try to resolve the constraint repr. It should be some kind of existential - // type. Pass along the error type if resolving the repr failed. - auto constraintType = TypeResolution::forInterface( - dc, TypeResolverContext::GenericRequirement, - // Unbound generics and placeholders are meaningless - // in opaque types. - /*unboundTyOpener*/ nullptr, - /*placeholderHandler*/ nullptr) - .resolveType(repr->getConstraint()); - - if (constraintType->hasError()) - return nullptr; - - // Error out if the constraint type isn't a class or existential type. - if (!constraintType->getClassOrBoundGenericClass() - && !constraintType->isExistentialType()) { - ctx.Diags.diagnose(repr->getConstraint()->getLoc(), - diag::opaque_type_invalid_constraint); - return nullptr; - } - - if (constraintType->hasArchetype()) - constraintType = constraintType->mapTypeOutOfContext(); - // Create a generic signature for the opaque environment. This is the outer - // generic signature with an added generic parameter representing the opaque - // type and its interface constraints. + // generic signature with an added generic parameters representing the opaque + // types and their interface constraints. auto originatingDC = originatingDecl->getInnermostDeclContext(); - unsigned returnTypeDepth = 0; auto outerGenericSignature = originatingDC->getGenericSignatureOfContext(); - - if (outerGenericSignature) { - returnTypeDepth = - outerGenericSignature->getGenericParams().back()->getDepth() + 1; - } - - auto returnTypeParam = GenericTypeParamType::get(returnTypeDepth, 0, ctx); + unsigned opaqueSignatureDepth = + outerGenericSignature + ? outerGenericSignature.getGenericParams().back()->getDepth() + 1 + : 0; SmallVector genericParamTypes; - genericParamTypes.push_back(returnTypeParam); - SmallVector requirements; - if (constraintType->getClassOrBoundGenericClass()) { - requirements.push_back(Requirement(RequirementKind::Superclass, - returnTypeParam, constraintType)); - } else { - auto constraints = constraintType->getExistentialLayout(); - if (auto superclass = constraints.getSuperclass()) { - requirements.push_back(Requirement(RequirementKind::Superclass, - returnTypeParam, superclass)); + + auto opaqueReprs = collectOpaqueReturnTypeReprs(repr); + if (opaqueReprs.size() > 1) { + ctx.Diags.diagnose(repr->getLoc(), diag::more_than_one_opaque_type, repr); + return nullptr; + } + + // TODO [OPAQUE SUPPORT]: right now we only allow one structural 'some' type, + // but *very* soon we will allow more than one such type. + for (unsigned i = 0; i < opaqueReprs.size(); ++i) { + auto *currentRepr = opaqueReprs[i]; + + // Usually, we resolve the opaque constraint and bail if it isn't a class or + // existential type (see below). However, in this case we know we will fail, + // so we can bail early and provide a better diagnostic. + if (auto *optionalRepr = + dyn_cast(currentRepr->getConstraint())) { + std::string buf; + llvm::raw_string_ostream stream(buf); + stream << "(some " << optionalRepr->getBase() << ")?"; + + ctx.Diags.diagnose(currentRepr->getLoc(), + diag::opaque_type_invalid_constraint); + ctx.Diags + .diagnose(currentRepr->getLoc(), diag::opaque_of_optional_rewrite) + .fixItReplaceChars(currentRepr->getStartLoc(), + currentRepr->getEndLoc(), stream.str()); + return nullptr; } - for (auto protocol : constraints.getProtocols()) { - requirements.push_back(Requirement(RequirementKind::Conformance, - returnTypeParam, protocol)); + + auto *paramType = GenericTypeParamType::get(opaqueSignatureDepth, i, ctx); + genericParamTypes.push_back(paramType); + + // Try to resolve the constraint repr in the parent decl context. It should + // be some kind of existential type. Pass along the error type if resolving + // the repr failed. + auto constraintType = TypeResolution::forInterface( + dc, TypeResolverContext::GenericRequirement, + // Unbound generics and placeholders are + // meaningless in opaque types. + /*unboundTyOpener*/ nullptr, + /*placeholderHandler*/ nullptr) + .resolveType(currentRepr->getConstraint()); + + if (constraintType->hasError()) + return nullptr; + + // Error out if the constraint type isn't a class or existential type. + if (!constraintType->getClassOrBoundGenericClass() && + !constraintType->isExistentialType()) { + ctx.Diags.diagnose(currentRepr->getLoc(), + diag::opaque_type_invalid_constraint); + return nullptr; } - if (auto layout = constraints.getLayoutConstraint()) { - requirements.push_back(Requirement(RequirementKind::Layout, - returnTypeParam, layout)); + + if (constraintType->hasArchetype()) + constraintType = constraintType->mapTypeOutOfContext(); + + if (constraintType->getClassOrBoundGenericClass()) { + requirements.push_back( + Requirement(RequirementKind::Superclass, paramType, constraintType)); + } else { + // In this case, the constraint type is an existential + requirements.push_back( + Requirement(RequirementKind::Conformance, paramType, constraintType)); } } - + auto interfaceSignature = evaluateOrDefault( ctx.evaluator, AbstractGenericSignatureRequest{ @@ -206,23 +274,22 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator, ? originatingGenericContext->getGenericParams() : nullptr; - auto opaqueDecl = new (ctx) OpaqueTypeDecl(originatingDecl, - genericParams, - parentDC, - interfaceSignature, - returnTypeParam); + auto opaqueDecl = new (ctx) + OpaqueTypeDecl(originatingDecl, genericParams, parentDC, + interfaceSignature, opaqueReprs[0], genericParamTypes[0]); opaqueDecl->copyFormalAccessFrom(originatingDecl); if (auto originatingSig = originatingDC->getGenericSignatureOfContext()) { opaqueDecl->setGenericSignature(originatingSig); } - - // The declared interface type is an opaque ArchetypeType. - SubstitutionMap subs; - if (outerGenericSignature) { - subs = outerGenericSignature->getIdentitySubstitutionMap(); - } - auto opaqueTy = OpaqueTypeArchetypeType::get(opaqueDecl, subs); - auto metatype = MetatypeType::get(opaqueTy); + + // Resolving in the context of `opaqueDecl` allows type resolution to create + // opaque archetypes where needed + auto interfaceType = + TypeResolution::forInterface(opaqueDecl, TypeResolverContext::None, + /*unboundTyOpener*/ nullptr, + /*placeholderHandler*/ nullptr) + .resolveType(repr); + auto metatype = MetatypeType::get(interfaceType); opaqueDecl->setInterfaceType(metatype); return opaqueDecl; } @@ -250,7 +317,7 @@ void TypeChecker::checkProtocolSelfRequirements(ValueDecl *decl) { auto &ctx = proto->getASTContext(); auto protoSelf = proto->getSelfInterfaceType(); auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext(); - for (auto req : sig->getRequirements()) { + for (auto req : sig.getRequirements()) { // If one of the types in the requirement is dependent on a non-Self // type parameter, this requirement is okay. if (!isSelfDerivedOrConcrete(protoSelf, req.getFirstType()) || @@ -395,7 +462,7 @@ void TypeChecker::checkReferencedGenericParams(GenericContext *dc) { auto FindReferencedGenericParamsInRequirements = [&requirements, genericSig, &reqTypesVisitor] { - requirements = genericSig->getRequirements(); + requirements = genericSig.getRequirements(); // Try to find new referenced generic parameter types in requirements until // we reach a fix point. We need to iterate until a fix point, because we // may have e.g. chains of same-type requirements like: @@ -422,7 +489,7 @@ void TypeChecker::checkReferencedGenericParams(GenericContext *dc) { // Check that every generic parameter type from the signature is // among referencedGenericParams. - for (auto *genParam : genericSig->getGenericParams()) { + for (auto *genParam : genericSig.getGenericParams()) { auto *paramDecl = genParam->getDecl(); if (paramDecl->getDepth() != fnGenericParamsDepth) continue; @@ -576,7 +643,7 @@ static unsigned getExtendedTypeGenericDepth(ExtensionDecl *ext) { auto sig = nominal->getGenericSignatureOfContext(); if (!sig) return static_cast(-1); - return sig->getGenericParams().back()->getDepth(); + return sig.getGenericParams().back()->getDepth(); } GenericSignature @@ -774,7 +841,12 @@ RequirementCheckResult TypeChecker::checkGenericArguments( auto req = rawReq; if (current.Parents.empty()) { auto substed = rawReq.subst( - substitutions, + [&](SubstitutableType *type) -> Type { + auto substType = substitutions(type); + if (substType->hasTypeParameter()) + return dc->mapTypeIntoContext(substType); + return substType; + }, LookUpConformanceInModule(module), options); if (!substed) { @@ -786,105 +858,70 @@ RequirementCheckResult TypeChecker::checkGenericArguments( req = *substed; } - auto kind = req.getKind(); - Type rawFirstType = rawReq.getFirstType(); - Type firstType = req.getFirstType(); - if (firstType->hasTypeParameter()) - firstType = dc->mapTypeIntoContext(firstType); - - Type rawSecondType, secondType; - if (kind != RequirementKind::Layout) { - rawSecondType = rawReq.getSecondType(); - secondType = req.getSecondType(); - if (secondType->hasTypeParameter()) - secondType = dc->mapTypeIntoContext(secondType); - } + ArrayRef conditionalRequirements; + if (req.isSatisfied(conditionalRequirements, /*allowMissing=*/true)) { + if (!conditionalRequirements.empty()) { + assert(req.getKind() == RequirementKind::Conformance); - // Don't do further checking on error types. - if (firstType->hasError() || (secondType && secondType->hasError())) { - // Another requirement will fail later; just continue. - valid = false; + auto history = current.Parents; + history.push_back({req.getFirstType(), req.getProtocolDecl()}); + pendingReqs.push_back({conditionalRequirements, std::move(history)}); + } continue; } - bool requirementFailure = false; + if (loc.isValid()) { + Diag diagnostic; + Diag diagnosticNote; - Diag diagnostic; - Diag diagnosticNote; + switch (req.getKind()) { + case RequirementKind::Conformance: { + diagnoseConformanceFailure(req.getFirstType(), req.getProtocolDecl(), + module, loc); - switch (kind) { - case RequirementKind::Conformance: { - // Protocol conformance requirements. - auto proto = secondType->castTo(); - auto conformance = module->lookupConformance(firstType, proto->getDecl()); + if (current.Parents.empty()) + return RequirementCheckResult::Failure; - if (conformance) { - auto conditionalReqs = conformance.getConditionalRequirements(); - if (!conditionalReqs.empty()) { - auto history = current.Parents; - history.push_back({firstType, proto}); - pendingReqs.push_back({conditionalReqs, std::move(history)}); - } - continue; + diagnostic = diag::type_does_not_conform_owner; + diagnosticNote = diag::type_does_not_inherit_or_conform_requirement; + break; } - if (loc.isValid()) - diagnoseConformanceFailure(firstType, proto->getDecl(), module, loc); - - if (current.Parents.empty()) - return RequirementCheckResult::Failure; - - // Failure needs to emit a diagnostic. - diagnostic = diag::type_does_not_conform_owner; - diagnosticNote = diag::type_does_not_inherit_or_conform_requirement; - requirementFailure = true; - break; - } - - case RequirementKind::Layout: - // TODO: Statically check other layout constraints, once they can - // be spelled in Swift. - if (req.getLayoutConstraint()->isClass() && - !firstType->satisfiesClassConstraint()) { + case RequirementKind::Layout: diagnostic = diag::type_is_not_a_class; diagnosticNote = diag::anyobject_requirement; - requirementFailure = true; - } - break; + break; - case RequirementKind::Superclass: { - // Superclass requirements. - if (!secondType->isExactSuperclassOf(firstType)) { + case RequirementKind::Superclass: diagnostic = diag::type_does_not_inherit; diagnosticNote = diag::type_does_not_inherit_or_conform_requirement; - requirementFailure = true; - } - break; - } + break; - case RequirementKind::SameType: - if (!firstType->isEqual(secondType)) { + case RequirementKind::SameType: diagnostic = diag::types_not_equal; diagnosticNote = diag::types_not_equal_requirement; - requirementFailure = true; + break; } - break; - } - if (!requirementFailure) - continue; + Type rawSecondType, secondType; + if (req.getKind() != RequirementKind::Layout) { + rawSecondType = rawReq.getSecondType(); + secondType = req.getSecondType(); + } - if (loc.isValid()) { // FIXME: Poor source-location information. - ctx.Diags.diagnose(loc, diagnostic, owner, firstType, secondType); + ctx.Diags.diagnose(loc, diagnostic, owner, + req.getFirstType(), secondType); std::string genericParamBindingsText; if (!genericParams.empty()) { genericParamBindingsText = gatherGenericParamBindingsText( - {rawFirstType, rawSecondType}, genericParams, substitutions); + {rawReq.getFirstType(), rawSecondType}, + genericParams, substitutions); } - ctx.Diags.diagnose(noteLoc, diagnosticNote, rawFirstType, rawSecondType, + ctx.Diags.diagnose(noteLoc, diagnosticNote, + rawReq.getFirstType(), rawSecondType, genericParamBindingsText); ParentConditionalConformance::diagnoseConformanceStack( @@ -900,6 +937,37 @@ RequirementCheckResult TypeChecker::checkGenericArguments( return RequirementCheckResult::SubstitutionFailure; } +RequirementCheckResult +TypeChecker::checkGenericArguments(ModuleDecl *module, + ArrayRef requirements, + TypeSubstitutionFn substitutions) { + SmallVector worklist; + bool valid = true; + + for (auto req : requirements) { + if (auto resolved = req.subst(substitutions, + LookUpConformanceInModule(module))) { + worklist.push_back(*resolved); + } else { + valid = false; + } + } + + while (!worklist.empty()) { + auto req = worklist.pop_back_val(); + ArrayRef conditionalRequirements; + if (!req.isSatisfied(conditionalRequirements, /*allowMissing=*/true)) + return RequirementCheckResult::Failure; + + worklist.append(conditionalRequirements.begin(), + conditionalRequirements.end()); + } + + if (valid) + return RequirementCheckResult::Success; + return RequirementCheckResult::SubstitutionFailure; +} + Requirement RequirementRequest::evaluate(Evaluator &evaluator, WhereClauseOwner owner, diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 8c0bc28b15c8d..26d0e8ec85a8a 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -358,8 +358,25 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, return result; } +static bool doesTypeAliasFullyConstrainAllOuterGenericParams( + TypeAliasDecl *aliasDecl) { + auto parentSig = aliasDecl->getDeclContext()->getGenericSignatureOfContext(); + auto genericSig = aliasDecl->getGenericSignature(); + + if (!parentSig || !genericSig) + return false; + + for (auto *paramType : parentSig.getGenericParams()) { + if (!genericSig->isConcreteType(paramType)) + return false; + } + + return true; +} + TypeChecker::UnsupportedMemberTypeAccessKind -TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl) { +TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl, + bool hasUnboundOpener) { // We don't allow lookups of a non-generic typealias of an unbound // generic type, because we have no way to model such a type in the // AST. @@ -376,13 +393,18 @@ TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl) { // underlying type is not dependent. if (auto *aliasDecl = dyn_cast(typeDecl)) { if (!aliasDecl->isGeneric() && - aliasDecl->getUnderlyingType()->hasTypeParameter()) { + aliasDecl->getUnderlyingType()->hasTypeParameter() && + !doesTypeAliasFullyConstrainAllOuterGenericParams(aliasDecl)) { return UnsupportedMemberTypeAccessKind::TypeAliasOfUnboundGeneric; } } if (isa(typeDecl)) return UnsupportedMemberTypeAccessKind::AssociatedTypeOfUnboundGeneric; + + if (isa(typeDecl)) + if (!hasUnboundOpener) + return UnsupportedMemberTypeAccessKind::NominalTypeOfUnboundGeneric; } if (type->isExistentialType() && @@ -433,7 +455,7 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, continue; } - if (isUnsupportedMemberTypeAccess(type, typeDecl) + if (isUnsupportedMemberTypeAccess(type, typeDecl, true) != TypeChecker::UnsupportedMemberTypeAccessKind::None) { auto memberType = typeDecl->getDeclaredInterfaceType(); @@ -573,7 +595,7 @@ void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind, Type baseTypeOrNull, NameLookupOptions lookupOptions, TypoCorrectionResults &corrections, - GenericSignatureBuilder *gsb, + GenericSignature genericSig, unsigned maxResults) { // Disable typo-correction if we won't show the diagnostic anyway or if // we've hit our typo correction limit. @@ -614,7 +636,8 @@ void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind, lookupVisibleMemberDecls(consumer, baseTypeOrNull, DC, /*includeInstanceMembers*/true, /*includeDerivedRequirements*/false, - /*includeProtocolExtensionMembers*/true, gsb); + /*includeProtocolExtensionMembers*/true, + genericSig); } else { lookupVisibleDecls(consumer, DC, /*top level*/ true, corrections.Loc.getBaseNameLoc()); diff --git a/lib/Sema/TypeCheckObjC.h b/lib/Sema/TypeCheckObjC.h index a7ea96e4ff77e..7f8bc171dd2d9 100644 --- a/lib/Sema/TypeCheckObjC.h +++ b/lib/Sema/TypeCheckObjC.h @@ -21,6 +21,7 @@ #include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" namespace swift { @@ -90,14 +91,49 @@ class ObjCReason { Kind kind; /// When the kind is \c WitnessToObjC, the requirement being witnessed. - ValueDecl * decl = nullptr; - - ObjCReason(Kind kind, ValueDecl *decl) : kind(kind), decl(decl) { } + llvm::PointerUnion declOrAttr = + static_cast(nullptr); + + ObjCReason(Kind kind, ValueDecl *decl) : kind(kind), declOrAttr(decl) { } + + static bool requiresAttr(Kind kind) { + switch (kind) { + case ExplicitlyCDecl: + case ExplicitlyDynamic: + case ExplicitlyObjC: + case ExplicitlyIBOutlet: + case ExplicitlyIBAction: + case ExplicitlyIBSegueAction: + case ExplicitlyNSManaged: + case ExplicitlyIBInspectable: + case ExplicitlyGKInspectable: + case ExplicitlyObjCByAccessNote: + return true; + + case MemberOfObjCProtocol: + case ImplicitlyObjC: + case OverridesObjC: + case WitnessToObjC: + case MemberOfObjCExtension: + case MemberOfObjCMembersClass: + case MemberOfObjCSubclass: + case ElementOfObjCEnum: + case Accessor: + return false; + } + } public: /// Implicit conversion from the trivial reason kinds. ObjCReason(Kind kind) : kind(kind) { assert(kind != WitnessToObjC && "Use ObjCReason::witnessToObjC()"); + assert(!requiresAttr(kind) && "Use ObjCReason(Kind, DeclAttribute*)"); + } + + ObjCReason(Kind kind, const DeclAttribute *attr) + : kind(kind), declOrAttr(const_cast(attr)) { + // const_cast above because it's difficult to get a non-const DeclAttribute. + assert(requiresAttr(kind) && "Use ObjCReason(Kind)"); } /// Retrieve the kind of requirement. @@ -113,8 +149,20 @@ class ObjCReason { /// requirement, retrieve the requirement. ValueDecl *getObjCRequirement() const { assert(kind == WitnessToObjC); - return decl; + return declOrAttr.get(); } + + DeclAttribute *getAttr() const { + if (!requiresAttr(kind)) + return nullptr; + return declOrAttr.get(); + } + + void setAttrInvalid() const; + + /// Emit an additional diagnostic describing why we are applying @objc to the + /// decl, if this is not obvious from the decl itself. + void describe(const Decl *VD) const; }; /// Determine how to diagnose conflicts due to inferring @objc with this diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index e251912578217..292222bf6655b 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -477,7 +477,9 @@ class ResolvePattern : public ASTVisitor(ty->getAnyNominal()); if (!enumDecl) @@ -599,7 +601,9 @@ class ResolvePattern : public ASTVisitor(enumTy->getAnyNominal()); if (!enumDecl) @@ -707,7 +711,7 @@ static Type validateTypedPattern(TypedPattern *TP, TypeResolution resolution) { // property definition. auto &Context = resolution.getASTContext(); auto *Repr = TP->getTypeRepr(); - if (Repr && isa(Repr)) { + if (Repr && Repr->hasOpaque()) { auto named = dyn_cast( TP->getSubPattern()->getSemanticsProvidingPattern()); if (!named) { @@ -809,7 +813,9 @@ Type PatternTypeRequest::evaluate(Evaluator &evaluator, }; // FIXME: Don't let placeholder types escape type resolution. // For now, just return the placeholder type. - placeholderHandler = PlaceholderType::get; + placeholderHandler = [](auto &ctx, auto *originator) { + return Type(); + }; } return validateTypedPattern( cast(P), @@ -877,7 +883,9 @@ Type PatternTypeRequest::evaluate(Evaluator &evaluator, }; // FIXME: Don't let placeholder types escape type resolution. // For now, just return the placeholder type. - placeholderHandler = PlaceholderType::get; + placeholderHandler = [](auto &ctx, auto *originator) { + return Type(); + }; } TypedPattern *TP = cast(somePat->getSubPattern()); const auto type = validateTypedPattern( diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index f2423c0aee30d..bc27e30067d98 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -54,9 +54,13 @@ static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, switch (vars.size()) { case 0: if (!allowMissing) { + std::string fixIt = "var wrappedValue: <#Value#>"; + auto fixitLocation = nominal->getBraces().Start; nominal->diagnose(diag::property_wrapper_no_value_property, - nominal->getDeclaredType(), name); + nominal->getDeclaredType(), name) + .fixItInsertAfter(fixitLocation, fixIt); } + return nullptr; case 1: @@ -85,6 +89,7 @@ static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, // The property must not be isolated to an actor instance. switch (auto isolation = getActorIsolation(var)) { case ActorIsolation::ActorInstance: + case ActorIsolation::DistributedActorInstance: var->diagnose( diag::actor_instance_property_wrapper, var->getName(), nominal->getName()); @@ -622,6 +627,11 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( if (!type) return Type(); + // If the declaration came from a module file, there's no need to + // compute the auxiliary variables. + if (!var->getDeclContext()->getParentSourceFile()) + return type; + // Set the interface type of each synthesized declaration. auto auxiliaryVars = var->getPropertyWrapperAuxiliaryVariables(); auxiliaryVars.backingVar->setInterfaceType(type); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 4f96f6936f0ef..67c8b7181255d 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -357,7 +357,7 @@ matchWitnessDifferentiableAttr(DeclContext *dc, ValueDecl *req, auto reqDiffGenSig = reqDiffAttr->getDerivativeGenericSignature(); auto conformanceGenSig = dc->getGenericSignatureOfContext(); for (const auto &req : - witnessConfig.derivativeGenericSignature->getRequirements()) { + witnessConfig.derivativeGenericSignature.getRequirements()) { auto substReq = req.subst(result.WitnessSubstitutions); bool reqDiffGenSigSatisfies = reqDiffGenSig && substReq && @@ -874,11 +874,16 @@ static Optional findMissingGenericRequirementForSolutionFix( type = solution.simplifyType(type); missingType = solution.simplifyType(missingType); - missingType = missingType->mapTypeOutOfContext(); - if (missingType->hasTypeParameter()) - if (auto env = conformance->getGenericEnvironment()) - if (auto assocType = env->mapTypeIntoContext(missingType)) - missingType = assocType; + if (auto *env = conformance->getGenericEnvironment()) { + // We use subst() with LookUpConformanceInModule here, because + // associated type inference failures mean that we can end up + // here with a DependentMemberType with an ArchetypeType base. + missingType = missingType.subst( + [&](SubstitutableType *type) -> Type { + return env->mapTypeIntoContext(type->mapTypeOutOfContext()); + }, + LookUpConformanceInModule(conformance->getDeclContext()->getParentModule())); + } auto missingRequirementMatch = [&](Type type) -> RequirementMatch { Requirement requirement(requirementKind, type, missingType); @@ -942,7 +947,7 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache, ClassDecl *covariantSelf = nullptr; if (witness->getDeclContext()->getExtendedProtocolDecl()) { if (auto *classDecl = dc->getSelfClassDecl()) { - if (!classDecl->isFinal()) { + if (!classDecl->isSemanticallyFinal()) { // If the requirement's type does not involve any associated types, // we use a class-constrained generic parameter as the 'Self' type // in the witness thunk. @@ -1351,6 +1356,27 @@ bool WitnessChecker::findBestWitness( } } + // If there are multiple viable matches, drop any that are less available than the + // requirement. + if (numViable > 1) { + SmallVector checkedMatches; + bool foundCheckedMatch = false; + + for (auto match : matches) { + if (!match.isViable()) { + checkedMatches.push_back(match); + } else if (checkWitness(requirement, match).Kind != CheckKind::Availability) { + foundCheckedMatch = true; + checkedMatches.push_back(match); + } + } + + // If none of the matches were at least as available as the requirement, don't + // drop any of them; this will produce better diagnostics. + if (foundCheckedMatch) + std::swap(checkedMatches, matches); + } + if (numViable == 0) { // Assume any missing value witnesses for a conformance in a module // interface can be treated as opaque. @@ -1832,6 +1858,13 @@ static void diagnoseConformanceImpliedByConditionalConformance( .fixItInsert(loc, differentFixit); } +/// Determine whether there are additional semantic checks for conformance +/// to the given protocol. This should return true when @unchecked can be +/// used to disable those semantic checks. +static bool hasAdditionalSemanticChecks(ProtocolDecl *proto) { + return proto->isSpecificProtocol(KnownProtocolKind::Sendable); +} + /// Determine whether the type \c T conforms to the protocol \c Proto, /// recording the complete witness table if it does. ProtocolConformance *MultiConformanceChecker:: @@ -1899,6 +1932,26 @@ checkIndividualConformance(NormalProtocolConformance *conformance, return conformance; } + if (T->isActorType()) { + if (auto globalActor = Proto->getGlobalActorAttr()) { + C.Diags.diagnose(ComplainLoc, + diag::actor_cannot_conform_to_global_actor_protocol, T, + ProtoType); + + CustomAttr *attr; + NominalTypeDecl *actor; + + std::tie(attr, actor) = *globalActor; + + C.Diags.diagnose(attr->getLocation(), + diag::protocol_isolated_to_global_actor_here, ProtoType, + actor->getDeclaredInterfaceType()); + + conformance->setInvalid(); + return conformance; + } + } + if (Proto->isObjC()) { // Foreign classes cannot conform to objc protocols. if (auto clas = canT->getClassOrBoundGenericClass()) { @@ -1940,7 +1993,7 @@ checkIndividualConformance(NormalProtocolConformance *conformance, if (auto ext = dyn_cast(DC)) { if (auto classDecl = ext->getSelfClassDecl()) { if (classDecl->isGenericContext()) { - if (!classDecl->usesObjCGenericsModel()) { + if (!classDecl->isTypeErasedGenericClass()) { C.Diags.diagnose(ComplainLoc, diag::objc_protocol_in_generic_extension, classDecl->isGeneric(), T, ProtoType); @@ -1961,7 +2014,7 @@ checkIndividualConformance(NormalProtocolConformance *conformance, // types for any obj-c ones. while (nestedType) { if (auto clas = nestedType->getClassOrBoundGenericClass()) { - if (clas->usesObjCGenericsModel()) { + if (clas->isTypeErasedGenericClass()) { C.Diags.diagnose(ComplainLoc, diag::objc_generics_cannot_conditionally_conform, T, ProtoType); @@ -2014,6 +2067,13 @@ checkIndividualConformance(NormalProtocolConformance *conformance, return conformance; } + // Complain about the use of @unchecked for protocols that don't have + // additional semantic checks. + if (conformance->isUnchecked() && !hasAdditionalSemanticChecks(Proto)) { + C.Diags.diagnose( + ComplainLoc, diag::unchecked_conformance_not_special, ProtoType); + } + bool impliedDisablesMissingWitnessFixits = false; if (conformance->getSourceKind() == ConformanceEntryKind::Implied && !Proto->isMarkerProtocol()) { @@ -2101,7 +2161,7 @@ static void addAssocTypeDeductionString(llvm::SmallString<128> &str, static Type getTypeForDisplay(ModuleDecl *module, ValueDecl *decl) { // For a constructor, we only care about the parameter types. if (auto ctor = dyn_cast(decl)) { - return AnyFunctionType::composeInput(module->getASTContext(), + return AnyFunctionType::composeTuple(module->getASTContext(), ctor->getMethodInterfaceType() ->castTo() ->getParams(), @@ -2185,10 +2245,10 @@ static Type getRequirementTypeForDisplay(ModuleDecl *module, auto genericSig = fnTy->getOptGenericSignature(); if (genericSig) { - if (genericSig->getGenericParams().size() > 1) { + if (genericSig.getGenericParams().size() > 1) { genericSig = GenericSignature::get( - genericSig->getGenericParams().slice(1), - genericSig->getRequirements()); + genericSig.getGenericParams().slice(1), + genericSig.getRequirements()); } else { genericSig = nullptr; } @@ -2664,7 +2724,18 @@ ConformanceChecker::getReferencedAssociatedTypes(ValueDecl *req) { }; Walker walker(Proto, assocTypes); - req->getInterfaceType()->getCanonicalType().walk(walker); + + // This dance below is to avoid calling getCanonicalType() on a + // GenericFunctionType, which creates a GenericSignatureBuilder, which + // can in turn trigger associated type inference and cause a cycle. + auto reqTy = req->getInterfaceType(); + if (auto *funcTy = reqTy->getAs()) { + for (auto param : funcTy->getParams()) + param.getPlainType()->getCanonicalType().walk(walker); + funcTy->getResult()->getCanonicalType().walk(walker); + } else { + reqTy->getCanonicalType().walk(walker); + } return assocTypes; } @@ -2752,27 +2823,37 @@ bool ConformanceChecker::checkActorIsolation( Type witnessGlobalActor; switch (auto witnessRestriction = ActorIsolationRestriction::forDeclaration( - witness, /*fromExpression=*/false)) { + witness, witness->getDeclContext(), + /*fromExpression=*/false)) { + case ActorIsolationRestriction::DistributedActorSelf: { + if (witness->isSynthesized()) { + // we only have two 'distributed-actor-nonisolated' properties, + // the address and transport; if we see any such marked property, + // we're free to automatically assume those are fine and accessible always. + if (witness->isDistributedActorIndependent()) + return false; + } + + if (auto funcDecl = dyn_cast(witness)) { + // A 'distributed func' may witness a distributed isolated function requirement. + if (funcDecl->isDistributed()) + return false; + } + + // continue checking ActorSelf rules + LLVM_FALLTHROUGH; + } case ActorIsolationRestriction::ActorSelf: { + auto requirementIsolation = getActorIsolation(requirement); + // An actor-isolated witness can only conform to an actor-isolated // requirement. - if (getActorIsolation(requirement) == ActorIsolation::ActorInstance) + if (requirementIsolation == ActorIsolation::ActorInstance) return false; witness->diagnose(diag::actor_isolated_witness, witness->getDescriptiveKind(), witness->getName()); - if (auto witnessFunc = dyn_cast(witness)) { - witnessFunc->canBeAsyncHandler(); - if (!witnessFunc->isAsyncHandler()) { - auto handlerNote = witness->diagnose( - diag::note_add_asynchandler_to_function, - witness->getName()); - handlerNote.fixItInsert(witness->getAttributeInsertionLoc(false), - "@asyncHandler "); - } - } - { auto witnessVar = dyn_cast(witness); if ((witnessVar && !witnessVar->hasStorage()) || !witnessVar) { @@ -2788,8 +2869,9 @@ bool ConformanceChecker::checkActorIsolation( } case ActorIsolationRestriction::CrossActorSelf: - return diagnoseNonConcurrentTypesInReference( - witness, DC, witness->getLoc(), ConcurrentReferenceKind::CrossActor); + return diagnoseNonSendableTypesInReference( + witness, DC->getParentModule(), witness->getLoc(), + ConcurrentReferenceKind::CrossActor); case ActorIsolationRestriction::GlobalActorUnsafe: witnessIsUnsafe = true; @@ -2819,6 +2901,11 @@ bool ConformanceChecker::checkActorIsolation( switch (auto requirementIsolation = getActorIsolation(requirement)) { case ActorIsolation::ActorInstance: llvm_unreachable("There are not actor protocols"); + case ActorIsolation::DistributedActorInstance: + // A requirement inside a distributed actor, where it has a protocol that was + // bound requiring a `DistributedActor` conformance (`protocol D: DistributedActor`), + // results in the requirement being isolated to given distributed actor. + break; case ActorIsolation::GlobalActorUnsafe: requirementIsUnsafe = true; @@ -2853,8 +2940,9 @@ bool ConformanceChecker::checkActorIsolation( if (requirement->hasClangNode()) return false; - return diagnoseNonConcurrentTypesInReference( - witness, DC, witness->getLoc(), ConcurrentReferenceKind::CrossActor); + return diagnoseNonSendableTypesInReference( + witness, DC->getParentModule(), witness->getLoc(), + ConcurrentReferenceKind::CrossActor); } // If the witness has a global actor but the requirement does not, we have @@ -2887,8 +2975,9 @@ bool ConformanceChecker::checkActorIsolation( return false; if (isCrossActor) { - return diagnoseNonConcurrentTypesInReference( - witness, DC, witness->getLoc(), ConcurrentReferenceKind::CrossActor); + return diagnoseNonSendableTypesInReference( + witness, DC->getParentModule(), witness->getLoc(), + ConcurrentReferenceKind::CrossActor); } witness->diagnose( @@ -2925,7 +3014,7 @@ bool ConformanceChecker::checkObjCTypeErasedGenerics( auto classDecl = Adoptee->getClassOrBoundGenericClass(); if (!classDecl) return false; - if (!classDecl->usesObjCGenericsModel()) return false; + if (!classDecl->isTypeErasedGenericClass()) return false; // Concrete types are okay. if (!type->getCanonicalType()->hasTypeParameter()) return false; @@ -3159,7 +3248,8 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, // Find the conformance for this overridden protocol. auto overriddenConformance = DC->getParentModule()->lookupConformance(Adoptee, - overridden->getProtocol()); + overridden->getProtocol(), + /*allowMissing=*/true); if (overriddenConformance.isInvalid() || !overriddenConformance.isConcrete()) continue; @@ -3185,7 +3275,7 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS) { if (isa(Requirement)) { if (auto CD = Adopter->getSelfClassDecl()) { - if (!CD->isFinal() && isa(Adopter)) { + if (!CD->isSemanticallyFinal() && isa(Adopter)) { // In this case, user should mark class as 'final' or define // 'required' initializer directly in the class definition. return false; @@ -3614,11 +3704,8 @@ static bool hasSelfSameTypeConstraint(const ValueDecl *req) { return false; const auto genericSig = genCtx->getGenericSignature(); - if (!genericSig) - return false; - const auto selfTy = proto->getSelfInterfaceType(); - for (const auto &constr : genericSig->getRequirements()) { + for (const auto &constr : genericSig.getRequirements()) { if (constr.getKind() != RequirementKind::SameType) continue; @@ -3639,10 +3726,9 @@ static Optional> getAdopteeSelfSameTypeConstraint(ClassDecl *selfClass, ValueDecl *witness) { auto genericSig = witness->getInnermostDeclContext()->getGenericSignatureOfContext(); - if (!genericSig) return None; // First, search for any bogus requirements. - auto it = llvm::find_if(genericSig->getRequirements(), + auto it = llvm::find_if(genericSig.getRequirements(), [&selfClass](const auto &req) { if (req.getKind() != RequirementKind::SameType) return false; @@ -3650,7 +3736,7 @@ getAdopteeSelfSameTypeConstraint(ClassDecl *selfClass, ValueDecl *witness) { return req.getFirstType()->getAnyNominal() == selfClass || req.getSecondType()->getAnyNominal() == selfClass; }); - if (it == genericSig->getRequirements().end()) { + if (it == genericSig.getRequirements().end()) { return None; } @@ -3877,21 +3963,25 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { // Determine whether we can derive a witness for this requirement. bool canDerive = false; - // Can a witness for this requirement be derived for this nominal type? - if (auto derivable = DerivedConformance::getDerivableRequirement( - nominal, - requirement)) { - if (derivable == requirement) { - // If it's the same requirement, we can derive it here. - canDerive = true; - } else { - // Otherwise, go satisfy the derivable requirement, which can introduce - // a member that could in turn satisfy *this* requirement. - auto derivableProto = cast(derivable->getDeclContext()); - auto conformance = - TypeChecker::conformsToProtocol(Adoptee, derivableProto, DC); - if (conformance.isConcrete()) { - (void)conformance.getConcrete()->getWitnessDecl(derivable); + auto *SF = DC->getParentSourceFile(); + if (!(SF == nullptr || SF->Kind == SourceFileKind::Interface)) { + // Can a witness for this requirement be derived for this nominal type? + if (auto derivable = DerivedConformance::getDerivableRequirement( + nominal, + requirement)) { + if (derivable == requirement) { + // If it's the same requirement, we can derive it here. + canDerive = true; + } else { + // Otherwise, go satisfy the derivable requirement, which can introduce + // a member that could in turn satisfy *this* requirement. + auto derivableProto = cast(derivable->getDeclContext()); + auto conformance = + TypeChecker::conformsToProtocol(Adoptee, derivableProto, + DC->getParentModule()); + if (conformance.isConcrete()) { + (void)conformance.getConcrete()->getWitnessDecl(derivable); + } } } } @@ -4010,14 +4100,11 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { witness->getName(), isSetter, requiredAccess, protoAccessScope.accessLevelForDiagnostics(), proto->getName()); - if (auto *decl = dyn_cast(witness)) { - auto isMemberwiseInitializer = - decl->getBodyKind() == - AbstractFunctionDecl::BodyKind::MemberwiseInitializer; - if (isMemberwiseInitializer) { + + if (auto *decl = dyn_cast(witness)) + if (decl->isMemberwiseInitializer()) return; - } - } + diagnoseWitnessFixAccessLevel(diags, witness, requiredAccess, isSetter); }); @@ -4112,11 +4199,12 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { NormalProtocolConformance *conformance) { auto &diags = witness->getASTContext().Diags; SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness); - diags.diagnose(diagLoc, - diag::witness_unavailable, - witness->getDescriptiveKind(), - witness->getName(), - conformance->getProtocol()->getName()); + auto *attr = AvailableAttr::isUnavailable(witness); + EncodedDiagnosticMessage EncodedMessage(attr->Message); + diags.diagnose(diagLoc, diag::witness_unavailable, + witness->getDescriptiveKind(), witness->getName(), + conformance->getProtocol()->getName(), + EncodedMessage.Message); emitDeclaredHereIfNeeded(diags, diagLoc, witness); diags.diagnose(requirement, diag::kind_declname_declared_here, DescriptiveDeclKind::Requirement, @@ -4126,7 +4214,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { } if (auto *classDecl = Adoptee->getClassOrBoundGenericClass()) { - if (!classDecl->isFinal()) { + if (!classDecl->isSemanticallyFinal()) { checkNonFinalClassWitness(requirement, witness); } } @@ -4203,6 +4291,10 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDerivation( ValueDecl *requirement) { assert(!isa(requirement) && "Use resolveTypeWitnessVia*"); + auto *SF = DC->getParentSourceFile(); + if (SF != nullptr && SF->Kind == SourceFileKind::Interface) + return ResolveWitnessResult::Missing; + // Find the declaration that derives the protocol conformance. NominalTypeDecl *derivingTypeDecl = nullptr; auto *nominal = Adoptee->getAnyNominal(); @@ -4594,10 +4686,12 @@ void ConformanceChecker::resolveValueWitnesses() { return; // Ensure that Actor.unownedExecutor is implemented within the - // actor class itself. + // actor class itself. But if this somehow resolves to the + // requirement, ignore it. if (requirement->getName().isSimpleName(C.Id_unownedExecutor) && Proto->isSpecificProtocol(KnownProtocolKind::Actor) && DC != witness->getDeclContext() && + !isa(witness->getDeclContext()) && Adoptee->getClassOrBoundGenericClass() && Adoptee->getClassOrBoundGenericClass()->isActor()) { witness->diagnose(diag::unowned_executor_outside_actor); @@ -4909,7 +5003,7 @@ void swift::diagnoseConformanceFailure(Type T, // If we're checking conformance of an existential type to a protocol, // do a little bit of extra work to produce a better diagnostic. if (T->isExistentialType() && - TypeChecker::containsProtocol(T, Proto, DC)) { + TypeChecker::containsProtocol(T, Proto, DC->getParentModule())) { if (!T->isObjCExistentialType()) { diags.diagnose(ComplainLoc, diag::type_cannot_conform, true, @@ -4947,12 +5041,8 @@ void swift::diagnoseConformanceFailure(Type T, // If the reason is that the raw type does not conform to // Equatable, say so. - auto equatableProto = ctx.getProtocol(KnownProtocolKind::Equatable); - if (!equatableProto) - return; - - if (TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl) - .isInvalid()) { + if (!TypeChecker::conformsToKnownProtocol( + rawType, KnownProtocolKind::Equatable, DC->getParentModule())) { SourceLoc loc = enumDecl->getInherited()[0].getSourceRange().Start; diags.diagnose(loc, diag::enum_raw_type_not_equatable, rawType); return; @@ -5042,7 +5132,7 @@ void ConformanceChecker::emitDelayedDiags() { } ProtocolConformanceRef -TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, +TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M, bool skipConditionalRequirements) { // Existential types don't need to conform, i.e., they only need to // contain the protocol. @@ -5051,7 +5141,7 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, // *and* has a witness table. if (T->isEqual(Proto->getDeclaredInterfaceType()) && Proto->requiresSelfConformanceWitnessTable()) { - auto &ctx = DC->getASTContext(); + auto &ctx = M->getASTContext(); return ProtocolConformanceRef(ctx.getSelfConformance(Proto)); } @@ -5062,8 +5152,8 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, if (auto superclass = layout.getSuperclass()) { auto result = (skipConditionalRequirements - ? DC->getParentModule()->lookupConformance(superclass, Proto) - : TypeChecker::conformsToProtocol(superclass, Proto, DC)); + ? M->lookupConformance(superclass, Proto) + : TypeChecker::conformsToProtocol(superclass, Proto, M)); if (result) { return result; } @@ -5088,15 +5178,14 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, // For non-existential types, this is equivalent to checking conformance. return (skipConditionalRequirements - ? DC->getParentModule()->lookupConformance(T, Proto) - : TypeChecker::conformsToProtocol(T, Proto, DC)); + ? M->lookupConformance(T, Proto) + : TypeChecker::conformsToProtocol(T, Proto, M)); } ProtocolConformanceRef -TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC) { +TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M) { // Look up conformance in the module. - ModuleDecl *M = DC->getParentModule(); - auto lookupResult = M->lookupConformance(T, Proto); + auto lookupResult = M->lookupConformance(T, Proto, /*alllowMissing=*/true); if (lookupResult.isInvalid()) { return ProtocolConformanceRef::forInvalid(); } @@ -5106,12 +5195,10 @@ TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC) { "unhandled recursion: missing conditional requirements when they're " "required"); - // If we have a conditional requirements that - // we need to check, do so now. + // If we have a conditional requirements that we need to check, do so now. if (!condReqs->empty()) { auto conditionalCheckResult = checkGenericArguments( - DC, SourceLoc(), SourceLoc(), T, - {lookupResult.getRequirement()->getSelfInterfaceType()}, *condReqs, + M, *condReqs, [](SubstitutableType *dependentType) { return Type(dependentType); }); switch (conditionalCheckResult) { case RequirementCheckResult::Success: @@ -5126,9 +5213,17 @@ TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC) { return lookupResult; } +bool TypeChecker::conformsToKnownProtocol(Type type, KnownProtocolKind protocol, + ModuleDecl *module) { + if (auto *proto = + TypeChecker::getProtocol(module->getASTContext(), SourceLoc(), protocol)) + return (bool)TypeChecker::conformsToProtocol(type, proto, module); + return false; +} + bool TypeChecker::couldDynamicallyConformToProtocol(Type type, ProtocolDecl *Proto, - DeclContext *DC) { + ModuleDecl *M) { // An existential may have a concrete underlying type with protocol conformances // we cannot know statically. if (type->isExistentialType()) @@ -5141,11 +5236,10 @@ TypeChecker::couldDynamicallyConformToProtocol(Type type, ProtocolDecl *Proto, // A non-final class might have a subclass that conforms to the protocol. if (auto *classDecl = type->getClassOrBoundGenericClass()) { - if (!classDecl->isFinal()) + if (!classDecl->isSemanticallyFinal()) return true; } - ModuleDecl *M = DC->getParentModule(); // For standard library collection types such as Array, Set or Dictionary // which have custom casting machinery implemented for situations like: // @@ -5157,7 +5251,7 @@ TypeChecker::couldDynamicallyConformToProtocol(Type type, ProtocolDecl *Proto, // are met or not. if (type->isKnownStdlibCollectionType()) return !M->lookupConformance(type, Proto).isInvalid(); - return !conformsToProtocol(type, Proto, DC).isInvalid(); + return !conformsToProtocol(type, Proto, M).isInvalid(); } /// Exposes TypeChecker functionality for querying protocol conformance. @@ -5742,11 +5836,13 @@ diagnoseMissingAppendInterpolationMethod(NominalTypeDecl *typeDecl) { void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { auto *const dc = idc->getAsGenericContext(); + auto *sf = dc->getParentSourceFile(); - // For anything imported from Clang, lazily check conformances. - if (isa(dc->getModuleScopeContext())) - return; + assert(sf != nullptr && + "checkConformancesInContext() should not be called on imported " + "or deserialized DeclContexts"); + // Catch invalid extensions. const auto *const nominal = dc->getSelfNominalTypeDecl(); if (!nominal) return; @@ -5762,7 +5858,7 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { MultiConformanceChecker groupChecker(Context); ProtocolConformance *SendableConformance = nullptr; - ProtocolConformance *unsafeSendableConformance = nullptr; + bool sendableConformanceIsUnchecked = false; ProtocolConformance *errorConformance = nullptr; ProtocolConformance *codingKeyConformance = nullptr; bool anyInvalid = false; @@ -5793,9 +5889,39 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { } } else if (proto->isSpecificProtocol(KnownProtocolKind::Sendable)) { SendableConformance = conformance; + + if (auto normal = conformance->getRootNormalConformance()) { + if (normal->isUnchecked()) + sendableConformanceIsUnchecked = true; + } + } else if (proto->isSpecificProtocol(KnownProtocolKind::DistributedActor)) { + if (auto classDecl = dyn_cast(nominal)) { + if (!classDecl->isDistributedActor()) { + if (classDecl->isActor()) { + dc->getSelfNominalTypeDecl() + ->diagnose(diag::distributed_actor_protocol_illegal_inheritance, + dc->getSelfNominalTypeDecl()->getName()) + .fixItInsert(classDecl->getStartLoc(), "distributed "); + } else { + dc->getSelfNominalTypeDecl() + ->diagnose(diag::actor_protocol_illegal_inheritance, + dc->getSelfNominalTypeDecl()->getName()) + .fixItReplace(nominal->getStartLoc(), "distributed actor"); + } + } + } + } else if (proto->isSpecificProtocol(KnownProtocolKind::Actor)) { + if (auto classDecl = dyn_cast(nominal)) { + if (!classDecl->isExplicitActor()) { + dc->getSelfNominalTypeDecl() + ->diagnose(diag::actor_protocol_illegal_inheritance, + dc->getSelfNominalTypeDecl()->getName()) + .fixItReplace(nominal->getStartLoc(), "actor"); + } + } } else if (proto->isSpecificProtocol( KnownProtocolKind::UnsafeSendable)) { - unsafeSendableConformance = conformance; + sendableConformanceIsUnchecked = true; } else if (proto->isSpecificProtocol(KnownProtocolKind::Error)) { errorConformance = conformance; } else if (proto->isSpecificProtocol(KnownProtocolKind::CodingKey)) { @@ -5804,16 +5930,26 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { } // Check constraints of Sendable. - if (SendableConformance && !unsafeSendableConformance) { + if (SendableConformance && !sendableConformanceIsUnchecked) { SendableCheck check = SendableCheck::Explicit; if (errorConformance || codingKeyConformance) check = SendableCheck::ImpliedByStandardProtocol; + else if (SendableConformance->getSourceKind() == + ConformanceEntryKind::Synthesized) + check = SendableCheck::Implicit; checkSendableConformance(SendableConformance, check); } // Check all conformances. groupChecker.checkAllConformances(); + // Check actor isolation. + for (auto *member : idc->getMembers()) { + if (auto *valueDecl = dyn_cast(member)) { + (void)getActorIsolation(valueDecl); + } + } + if (Context.TypeCheckerOpts.DebugGenericSignatures && !conformances.empty()) { // Now that they're filled out, print out information about the conformances @@ -5880,10 +6016,8 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { continue; bool valueIsType = isa(value); - const auto flags = - NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; for (auto requirement - : diag.Protocol->lookupDirect(value->getName(), flags)) { + : diag.Protocol->lookupDirect(value->getName())) { if (requirement->getDeclContext() != diag.Protocol) continue; @@ -5932,8 +6066,7 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { // If there were any unsatisfied requirements, check whether there // are any near-matches we should diagnose. if (!unsatisfiedReqs.empty() && !anyInvalid) { - SourceFile *SF = dc->getParentSourceFile(); - if (SF && SF->Kind != SourceFileKind::Interface) { + if (sf->Kind != SourceFileKind::Interface) { // Find all of the members that aren't used to satisfy // requirements, and check whether they are close to an // unsatisfied or defaulted requirement. @@ -6017,27 +6150,25 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { } } - if (auto *sf = dc->getParentSourceFile()) { - // For any unsatisfied optional @objc requirements that remain - // unsatisfied, note them in the AST for @objc selector collision - // checking. - for (auto req : unsatisfiedReqs) { - // Skip non-@objc requirements. - if (!req->isObjC()) continue; + // For any unsatisfied optional @objc requirements that remain + // unsatisfied, note them in the AST for @objc selector collision + // checking. + for (auto req : unsatisfiedReqs) { + // Skip non-@objc requirements. + if (!req->isObjC()) continue; - // Skip unavailable requirements. - if (req->getAttrs().isUnavailable(Context)) continue; + // Skip unavailable requirements. + if (req->getAttrs().isUnavailable(Context)) continue; - // Record this requirement. - if (auto funcReq = dyn_cast(req)) { - sf->ObjCUnsatisfiedOptReqs.emplace_back(dc, funcReq); - } else { - auto storageReq = cast(req); - if (auto getter = storageReq->getParsedAccessor(AccessorKind::Get)) - sf->ObjCUnsatisfiedOptReqs.emplace_back(dc, getter); - if (auto setter = storageReq->getParsedAccessor(AccessorKind::Set)) - sf->ObjCUnsatisfiedOptReqs.emplace_back(dc, setter); - } + // Record this requirement. + if (auto funcReq = dyn_cast(req)) { + sf->ObjCUnsatisfiedOptReqs.emplace_back(dc, funcReq); + } else { + auto storageReq = cast(req); + if (auto getter = storageReq->getParsedAccessor(AccessorKind::Get)) + sf->ObjCUnsatisfiedOptReqs.emplace_back(dc, getter); + if (auto setter = storageReq->getParsedAccessor(AccessorKind::Set)) + sf->ObjCUnsatisfiedOptReqs.emplace_back(dc, setter); } } } @@ -6085,8 +6216,7 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness, if (!proto->isObjC()) continue; Optional conformance; - const auto flags = NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; - for (auto req : proto->lookupDirect(name, flags)) { + for (auto req : proto->lookupDirect(name)) { // Skip anything in a protocol extension. if (req->getDeclContext() != proto) continue; @@ -6099,8 +6229,7 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness, // Dig out the conformance. if (!conformance.hasValue()) { SmallVector conformances; - nominal->lookupConformance(dc->getParentModule(), proto, - conformances); + nominal->lookupConformance(proto, conformances); if (conformances.size() == 1) conformance = conformances.front(); else @@ -6282,6 +6411,9 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, case KnownDerivableProtocolKind::Differentiable: return derived.deriveDifferentiable(Requirement); + case KnownDerivableProtocolKind::DistributedActor: + return derived.deriveDistributedActor(Requirement); + case KnownDerivableProtocolKind::OptionSet: llvm_unreachable( "When possible, OptionSet is derived via memberwise init synthesis"); @@ -6444,12 +6576,14 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { if (!assocType) continue; + auto *module = proto->getParentModule(); + // Find the associated type nearest our own protocol, which might have // a default not available in the associated type referenced by the // (canonicalized) requirement. if (assocType->getProtocol() != proto) { SmallVector found; - proto->getModuleContext()->lookupQualified( + module->lookupQualified( proto, DeclNameRef(assocType->getName()), NL_QualifiedDefault|NL_ProtocolMembers|NL_OnlyTypes, found); @@ -6469,7 +6603,7 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { proto->mapTypeIntoContext(defaultAssocType); auto requirementProto = req.getProtocolDecl(); auto conformance = conformsToProtocol(defaultAssocTypeInContext, - requirementProto, proto); + requirementProto, module); if (conformance.isInvalid()) { // Diagnose the lack of a conformance. This is potentially an ABI // incompatibility. diff --git a/lib/Sema/TypeCheckProtocolInference.cpp b/lib/Sema/TypeCheckProtocolInference.cpp index 6f861534c111f..58bb855b7fdbb 100644 --- a/lib/Sema/TypeCheckProtocolInference.cpp +++ b/lib/Sema/TypeCheckProtocolInference.cpp @@ -19,6 +19,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/GenericSignature.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeMatcher.h" @@ -206,20 +207,15 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses( if (extendedNominal == nullptr) return true; - // FIXME: The extension may not have a generic signature set up yet as - // resolving signatures may trigger associated type inference. This cycle - // is now detectable and we should look into untangling it - // - see rdar://55263708 - if (!extension->hasComputedGenericSignature()) - return true; - - // Retrieve the generic signature of the extension. - const auto extensionSig = extension->getGenericSignature(); + auto *proto = dyn_cast(extendedNominal); // If the extension is bound to the nominal the conformance is // declared on, it is viable for inference when its conditional // requirements are satisfied by those of the conformance context. - if (!isa(extendedNominal)) { + if (!proto) { + // Retrieve the generic signature of the extension. + const auto extensionSig = extension->getGenericSignature(); + // Extensions of non-generic nominals are always viable for inference. if (!extensionSig) return true; @@ -235,30 +231,29 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses( // in the first place. Only check conformances on the `Self` type, // because those have to be explicitly declared on the type somewhere // so won't be affected by whatever answer inference comes up with. - auto selfTy = extension->getSelfInterfaceType(); - for (const Requirement &reqt : extensionSig->getRequirements()) { - switch (reqt.getKind()) { - case RequirementKind::Conformance: - case RequirementKind::Superclass: - // FIXME: This is the wrong check - if (selfTy->isEqual(reqt.getFirstType()) && - !TypeChecker::isSubtypeOf(conformance->getType(), - reqt.getSecondType(), dc)) - return false; - break; + auto *module = dc->getParentModule(); + auto checkConformance = [&](ProtocolDecl *proto) { + auto otherConf = module->lookupConformance(conformance->getType(), + proto); + return (otherConf && otherConf.getConditionalRequirements().empty()); + }; - case RequirementKind::Layout: - case RequirementKind::SameType: - break; + // First check the extended protocol itself. + if (!checkConformance(proto)) + return false; + + // Now check any additional bounds on 'Self' from the where clause. + auto bounds = getSelfBoundsFromWhereClause(extension); + for (auto *decl : bounds.decls) { + if (auto *proto = dyn_cast(decl)) { + if (!checkConformance(proto)) + return false; } } return true; }; - auto typeInContext = - conformance->getDeclContext()->mapTypeIntoContext(conformance->getType()); - for (auto witness : checker.lookupValueWitnesses(req, /*ignoringNames=*/nullptr)) { LLVM_DEBUG(llvm::dbgs() << "Inferring associated types from decl:\n"; @@ -314,6 +309,10 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses( if (!associatedTypesAreSameEquivalenceClass(dmt->getAssocType(), result.first)) return false; + + auto typeInContext = + conformance->getDeclContext()->mapTypeIntoContext(conformance->getType()); + if (!dmt->getBase()->isEqual(typeInContext)) return false; @@ -327,7 +326,7 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses( auto selfAssocTy = DependentMemberType::get(selfTy, dmt->getAssocType()); for (auto &reqt : witnessContext->getGenericSignatureOfContext() - ->getRequirements()) { + .getRequirements()) { switch (reqt.getKind()) { case RequirementKind::Conformance: case RequirementKind::Superclass: @@ -820,9 +819,14 @@ Type AssociatedTypeInference::computeFixedTypeWitness( !conformedProto->inheritsFrom(assocType->getProtocol())) continue; - const auto ty = - conformedProto->getGenericSignature()->getCanonicalTypeInContext( - structuralTy); + auto sig = conformedProto->getGenericSignature(); + + // FIXME: The RequirementMachine will assert on re-entrant construction. + // We should find a more principled way of breaking this cycle. + if (ctx.isRecursivelyConstructingRequirementMachine(sig.getCanonicalSignature())) + continue; + + const auto ty = sig->getCanonicalTypeInContext(structuralTy); // A dependent member type with an identical base and name indicates that // the protocol does not same-type constrain it in any way; move on to @@ -908,7 +912,7 @@ AssociatedTypeInference::computeAbstractTypeWitness( // If there is a generic parameter of the named type, use that. if (auto genericSig = dc->getGenericSignatureOfContext()) { - for (auto gp : genericSig->getInnermostGenericParams()) { + for (auto gp : genericSig.getInnermostGenericParams()) { if (gp->getName() == assocType->getName()) return AbstractTypeWitness::forGenericParam(assocType, gp); } @@ -1130,8 +1134,8 @@ bool AssociatedTypeInference::checkConstrainedExtension(ExtensionDecl *ext) { SubstOptions options = getSubstOptionsWithCurrentTypeWitnesses(); switch (TypeChecker::checkGenericArguments( dc, SourceLoc(), SourceLoc(), adoptee, - ext->getGenericSignature()->getGenericParams(), - ext->getGenericSignature()->getRequirements(), + ext->getGenericSignature().getGenericParams(), + ext->getGenericSignature().getRequirements(), QueryTypeSubstitutionMap{subs}, options)) { case RequirementCheckResult::Success: @@ -1505,7 +1509,7 @@ static Comparison compareDeclsForInference(DeclContext *DC, ValueDecl *decl1, insertProtocol(parent); }; - for (auto &reqt : sig1->getRequirements()) { + for (auto &reqt : sig1.getRequirements()) { if (!reqt.getFirstType()->isEqual(selfParam)) continue; switch (reqt.getKind()) { @@ -1537,7 +1541,7 @@ static Comparison compareDeclsForInference(DeclContext *DC, ValueDecl *decl1, removeProtocol(parent); }; - for (auto &reqt : sig2->getRequirements()) { + for (auto &reqt : sig2.getRequirements()) { if (!reqt.getFirstType()->isEqual(selfParam)) continue; switch (reqt.getKind()) { diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index 2985a4714ae4c..41b210577e5b0 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -45,13 +45,15 @@ Type InheritedTypeRequest::evaluate( switch (stage) { case TypeResolutionStage::Structural: resolution = - TypeResolution::forStructural(dc, None, /*unboundTyOpener*/ nullptr, + TypeResolution::forStructural(dc, TypeResolverContext::Inherited, + /*unboundTyOpener*/ nullptr, /*placeholderHandler*/ nullptr); break; case TypeResolutionStage::Interface: resolution = - TypeResolution::forInterface(dc, None, /*unboundTyOpener*/ nullptr, + TypeResolution::forInterface(dc, TypeResolverContext::Inherited, + /*unboundTyOpener*/ nullptr, /*placeholderHandler*/ nullptr); break; diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index aa0807af8d3c9..647a3ec163492 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -17,6 +17,7 @@ #include "TypeChecker.h" #include "TypeCheckAvailability.h" #include "TypeCheckConcurrency.h" +#include "TypeCheckDistributed.h" #include "TypeCheckType.h" #include "MiscDiagnostics.h" #include "swift/Subsystems.h" @@ -101,8 +102,8 @@ namespace { if (auto CapE = dyn_cast(E)) { if (isa(ParentDC)) { for (auto &Cap : CapE->getCaptureList()) { - Cap.Init->setDeclContext(ParentDC); - Cap.Var->setDeclContext(ParentDC); + Cap.PBD->setDeclContext(ParentDC); + Cap.getVar()->setDeclContext(ParentDC); } } } @@ -229,8 +230,7 @@ static void tryDiagnoseUnnecessaryCastOverOptionSet(ASTContext &Ctx, if (!optionSetType) return; SmallVector conformances; - if (!(optionSetType && - NTD->lookupConformance(module, optionSetType, conformances))) + if (!(optionSetType && NTD->lookupConformance(optionSetType, conformances))) return; auto *CE = dyn_cast(E); @@ -764,7 +764,6 @@ class StmtChecker : public StmtVisitor { assert(DiagnosticSuppression::isEnabled(getASTContext().Diags) && "Diagnosing and AllowUnresolvedTypeVariables don't seem to mix"); options |= TypeCheckExprFlags::LeaveClosureBodyUnchecked; - options |= TypeCheckExprFlags::AllowUnresolvedTypeVariables; } ContextualTypePurpose ctp = CTP_ReturnStmt; @@ -1258,16 +1257,6 @@ static void diagnoseIgnoredLiteral(ASTContext &Ctx, LiteralExpr *LE) { } void TypeChecker::checkIgnoredExpr(Expr *E) { - // For parity with C, several places in the grammar accept multiple - // comma-separated expressions and then bind them together as an implicit - // tuple. Break these apart and check them separately. - if (E->isImplicit() && isa(E)) { - for (auto Elt : cast(E)->getElements()) { - checkIgnoredExpr(Elt); - } - return; - } - // Skip checking if there is no type, which presumably means there was a // type error. if (!E->getType()) { @@ -1432,22 +1421,8 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { // Otherwise, complain. Start with more specific diagnostics. - // Diagnose unused literals that were translated to implicit - // constructor calls during CSApply / ExprRewriter::convertLiteral. - if (call->isImplicit()) { - Expr *arg = call->getArg(); - if (auto *TE = dyn_cast(arg)) - if (TE->getNumElements() == 1) - arg = TE->getElement(0); - - if (auto *LE = dyn_cast(arg)) { - diagnoseIgnoredLiteral(Context, LE); - return; - } - } - - // Other unused constructor calls. - if (callee && isa(callee) && !call->isImplicit()) { + // Diagnose unused constructor calls. + if (isa_and_nonnull(callee) && !call->isImplicit()) { DE.diagnose(fn->getLoc(), diag::expression_unused_init_result, callee->getDeclContext()->getDeclaredInterfaceType()) .highlight(call->getArg()->getSourceRange()); @@ -1456,8 +1431,8 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { SourceRange SR1 = call->getArg()->getSourceRange(), SR2; if (auto *BO = dyn_cast(call)) { - SR1 = BO->getArg()->getElement(0)->getSourceRange(); - SR2 = BO->getArg()->getElement(1)->getSourceRange(); + SR1 = BO->getLHS()->getSourceRange(); + SR2 = BO->getRHS()->getSourceRange(); } // Otherwise, produce a generic diagnostic. @@ -1506,7 +1481,6 @@ void StmtChecker::typeCheckASTNode(ASTNode &node) { options |= TypeCheckExprFlags::IsDiscarded; if (LeaveBraceStmtBodyUnchecked) { options |= TypeCheckExprFlags::LeaveClosureBodyUnchecked; - options |= TypeCheckExprFlags::AllowUnresolvedTypeVariables; } auto resultTy = @@ -1607,7 +1581,7 @@ static Expr* constructCallToSuperInit(ConstructorDecl *ctor, SourceLoc(), /*Implicit=*/true); Expr *r = UnresolvedDotExpr::createImplicit( Context, superRef, DeclBaseName::createConstructor()); - r = CallExpr::createImplicit(Context, r, { }, { }); + r = CallExpr::createImplicitEmpty(Context, r); if (ctor->hasThrows()) r = new (Context) TryExpr(SourceLoc(), r, Type(), /*implicit=*/true); @@ -1921,11 +1895,18 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator, if (Type builderType = getResultBuilderType(func)) { auto optBody = TypeChecker::applyResultBuilderBodyTransform(func, builderType); - if (!optBody || !*optBody) - return true; - // Wire up the function body now. - func->setBody(*optBody, AbstractFunctionDecl::BodyKind::TypeChecked); - return false; + if (optBody && *optBody) { + // Wire up the function body now. + func->setBody(*optBody, AbstractFunctionDecl::BodyKind::TypeChecked); + return false; + } + // FIXME: We failed to apply the result builder transform. Fall back to + // just type checking the node that contains the code completion token. + // This may be missing some context from the result builder but in + // practice it often contains sufficient information to provide a decent + // level of code completion that's better than providing nothing at all. + // The proper solution would be to only partially type check the result + // builder so that this fall back would not be necessary. } else if (func->hasSingleExpressionBody() && func->getResultInterfaceType()->isVoid()) { // The function returns void. We don't need an explicit return, no matter @@ -1941,6 +1922,19 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator, if (auto CE = dyn_cast(DC)) { if (CE->getBodyState() == ClosureExpr::BodyState::Parsed) { swift::typeCheckASTNodeAtLoc(CE->getParent(), CE->getLoc()); + // We need the actor isolation of the closure to be set so that we can + // annotate results that are on the same global actor. + // Since we are evaluating TypeCheckASTNodeAtLocRequest for every closure + // from outermost to innermost, we don't want to call checkActorIsolation, + // because that would cause actor isolation to be checked multiple times + // for nested closures. Instead, call determineClosureActorIsolation + // directly and set the closure's actor isolation manually. We can + // guarantee of that the actor isolation of enclosing closures have their + // isolation checked before nested ones are being checked by the way + // TypeCheckASTNodeAtLocRequest is called multiple times, as described + // above. + auto ActorIsolation = determineClosureActorIsolation(CE); + CE->setActorIsolation(ActorIsolation); if (CE->getBodyState() != ClosureExpr::BodyState::ReadyForTypeChecking) return false; } @@ -1964,7 +1958,7 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, BraceStmt *body = AFD->getBody(); assert(body && "Expected body to type-check"); - // It's possible we sythesized an already type-checked body, in which case + // It's possible we synthesized an already type-checked body, in which case // we're done. if (AFD->isBodyTypeChecked()) return body; diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 7332c84ac16b4..a417063ecd590 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -121,15 +121,27 @@ static void computeLoweredStoredProperties(NominalTypeDecl *decl) { // If this is an actor, check conformance to the Actor protocol to // ensure that the actor storage will get created (if needed). if (auto classDecl = dyn_cast(decl)) { + // If this is an actor class, check conformance to the Actor protocol to + // ensure that the actor storage will get created (if needed). if (classDecl->isActor()) { ASTContext &ctx = decl->getASTContext(); + if (auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor)) { SmallVector conformances; - classDecl->lookupConformance( - decl->getModuleContext(), actorProto, conformances); + classDecl->lookupConformance(actorProto, conformances); for (auto conformance : conformances) TypeChecker::checkConformance(conformance->getRootNormalConformance()); } + + // If this is a distributed actor, synthesize its special stored properties. + if (classDecl->isDistributedActor()) { + if (auto actorProto = ctx.getProtocol(KnownProtocolKind::DistributedActor)) { + SmallVector conformances; + classDecl->lookupConformance(actorProto, conformances); + for (auto conformance : conformances) + TypeChecker::checkConformance(conformance->getRootNormalConformance()); + } + } } } } @@ -219,6 +231,14 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, } } + // Reject "class" methods on actors. + if (StaticSpelling == StaticSpellingKind::KeywordClass && + binding->getDeclContext()->getSelfClassDecl() && + binding->getDeclContext()->getSelfClassDecl()->isActor()) { + binding->diagnose(diag::class_var_not_in_class, false) + .fixItReplace(binding->getStaticLoc(), "static"); + } + // Check the pattern. auto contextualPattern = ContextualPattern::forPatternBindingDecl(binding, entryNumber); @@ -245,6 +265,7 @@ PatternBindingEntryRequest::evaluate(Evaluator &eval, // If the pattern contains some form of unresolved type, we'll need to // check the initializer. if (patternType->hasUnresolvedType() || + patternType->hasPlaceholder() || patternType->hasUnboundGenericType()) { if (TypeChecker::typeCheckPatternBinding(binding, entryNumber, patternType)) { @@ -978,7 +999,8 @@ static ProtocolConformanceRef checkConformanceToNSCopying(VarDecl *var, auto proto = ctx.getNSCopyingDecl(); if (proto) { - if (auto result = TypeChecker::conformsToProtocol(type, proto, dc)) + if (auto result = TypeChecker::conformsToProtocol(type, proto, + dc->getParentModule())) return result; } @@ -1053,7 +1075,7 @@ static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD, // Drop the self type copyMethodType = copyMethodType->getResult()->castTo(); - auto DSCE = new (Ctx) DotSyntaxCallExpr(DRE, SourceLoc(), Val); + auto DSCE = DotSyntaxCallExpr::create(Ctx, DRE, SourceLoc(), Val); DSCE->setImplicit(); DSCE->setType(copyMethodType); DSCE->setThrows(false); @@ -1228,8 +1250,8 @@ namespace { if (auto CLE = dyn_cast(E)) { // Make sure to recontextualize any decls in the capture list as well. for (auto &CLE : CLE->getCaptureList()) { - CLE.Var->setDeclContext(NewDC); - CLE.Init->setDeclContext(NewDC); + CLE.getVar()->setDeclContext(NewDC); + CLE.PBD->setDeclContext(NewDC); } } @@ -1538,7 +1560,7 @@ synthesizeObservedSetterBody(AccessorDecl *Set, TargetImpl target, auto *SelfDRE = buildSelfReference(SelfDecl, SelfAccessorKind::Peer, IsSelfLValue); SelfDRE = maybeWrapInOutExpr(SelfDRE, Ctx); - auto *DSCE = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE); + auto *DSCE = DotSyntaxCallExpr::create(Ctx, Callee, SourceLoc(), SelfDRE); if (auto funcType = type->getAs()) type = funcType->getResult(); @@ -1551,7 +1573,7 @@ synthesizeObservedSetterBody(AccessorDecl *Set, TargetImpl target, if (arg) { Call = CallExpr::createImplicit(Ctx, Callee, {ValueDRE}, {Identifier()}); } else { - Call = CallExpr::createImplicit(Ctx, Callee, {}, {}); + Call = CallExpr::createImplicitEmpty(Ctx, Callee); } if (auto funcType = type->getAs()) @@ -1719,7 +1741,7 @@ synthesizeModifyCoroutineBodyWithSimpleDidSet(AccessorDecl *accessor, auto *SelfDRE = buildSelfReference(SelfDecl, SelfAccessorKind::Peer, storage->isSetterMutating()); SelfDRE = maybeWrapInOutExpr(SelfDRE, ctx); - auto *DSCE = new (ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE); + auto *DSCE = DotSyntaxCallExpr::create(ctx, Callee, SourceLoc(), SelfDRE); if (auto funcType = type->getAs()) type = funcType->getResult(); @@ -1728,7 +1750,7 @@ synthesizeModifyCoroutineBodyWithSimpleDidSet(AccessorDecl *accessor, Callee = DSCE; } - auto *Call = CallExpr::createImplicit(ctx, Callee, {}, {}); + auto *Call = CallExpr::createImplicitEmpty(ctx, Callee); if (auto funcType = type->getAs()) type = funcType->getResult(); Call->setType(type); @@ -2563,26 +2585,24 @@ static VarDecl *synthesizePropertyWrapperProjectionVar( static void typeCheckSynthesizedWrapperInitializer(VarDecl *wrappedVar, Expr *&initializer) { - // Figure out the context in which the initializer was written. - auto *parentPBD = wrappedVar->getParentPatternBinding(); - auto i = parentPBD->getPatternEntryIndexForVarDecl(wrappedVar); - DeclContext *originalDC = parentPBD->getDeclContext(); - if (!originalDC->isLocalContext()) { - auto initContext = - cast_or_null(parentPBD->getInitContext(i)); - if (initContext) - originalDC = initContext; - } + auto *dc = wrappedVar->getInnermostDeclContext(); + auto &ctx = wrappedVar->getASTContext(); + auto *initContext = new (ctx) PropertyWrapperInitializer( + dc, wrappedVar, PropertyWrapperInitializer::Kind::WrappedValue); // Type-check the initialization. - auto *pattern = parentPBD->getPattern(i); - TypeChecker::typeCheckBinding(pattern, initializer, originalDC, - wrappedVar->getType(), parentPBD, i); + using namespace constraints; + auto target = SolutionApplicationTarget::forPropertyWrapperInitializer( + wrappedVar, initContext, initializer); + auto result = TypeChecker::typeCheckExpression(target); + if (!result) + return; - if (auto initializerContext = - dyn_cast_or_null(parentPBD->getInitContext(i))) { - TypeChecker::contextualizeInitializer(initializerContext, initializer); - } + initializer = result->getAsExpr(); + + TypeChecker::contextualizeInitializer(initContext, initializer); + checkPropertyWrapperActorIsolation(wrappedVar, initializer); + TypeChecker::checkInitializerEffects(initContext, initializer); } static PropertyWrapperMutability::Value @@ -2853,16 +2873,17 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator, && parentPBD->isDefaultInitializable(patternNumber) && !wrapperInfo.defaultInit) { auto ty = parentPBD->getPattern(patternNumber)->getType(); - if (auto defaultInit = TypeChecker::buildDefaultInitializer(ty)) - parentPBD->setInit(patternNumber, defaultInit); - } - - if (parentPBD->isInitialized(patternNumber) && - !parentPBD->isInitializerChecked(patternNumber)) { - TypeChecker::typeCheckPatternBinding(parentPBD, patternNumber); + if (auto defaultInit = TypeChecker::buildDefaultInitializer(ty)) { + typeCheckSynthesizedWrapperInitializer(var, defaultInit); + parentPBD->setInit(0, defaultInit); + parentPBD->setInitializerChecked(0); + } } if ((initializer = parentPBD->getInit(patternNumber))) { + assert(parentPBD->isInitializerChecked(0) && + "Initializer should to be type-checked"); + pbd->setInit(0, initializer); pbd->setInitializerChecked(0); wrappedValue = findWrappedValuePlaceholder(initializer); @@ -2870,10 +2891,16 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator, if (!parentPBD->isInitialized(patternNumber) && wrapperInfo.defaultInit) { // FIXME: Record this expression somewhere so that DI can perform the // initialization itself. - Expr *initializer = nullptr; - typeCheckSynthesizedWrapperInitializer(var, initializer); - pbd->setInit(0, initializer); + Expr *defaultInit = nullptr; + typeCheckSynthesizedWrapperInitializer(var, defaultInit); + pbd->setInit(0, defaultInit); pbd->setInitializerChecked(0); + + // If a static, global, or local wrapped property has a default + // initializer, this is the only initializer that will be used. + if (var->isStatic() || !dc->isTypeContext()) { + initializer = defaultInit; + } } else if (var->hasObservers() && !dc->isTypeContext()) { var->diagnose(diag::observingprop_requires_initializer); } @@ -2935,21 +2962,7 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator, !var->getName().hasDollarPrefix()) { wrappedValueInit = PropertyWrapperValuePlaceholderExpr::create( ctx, var->getSourceRange(), var->getType(), /*wrappedValue=*/nullptr); - - if (auto *param = dyn_cast(var)) { - wrappedValueInit = buildPropertyWrapperInitCall( - var, storageType, wrappedValueInit, PropertyWrapperInitKind::WrappedValue); - TypeChecker::typeCheckExpression(wrappedValueInit, dc); - - // Check initializer effects. - auto *initContext = new (ctx) PropertyWrapperInitializer( - dc, param, PropertyWrapperInitializer::Kind::WrappedValue); - TypeChecker::contextualizeInitializer(initContext, wrappedValueInit); - checkInitializerActorIsolation(initContext, wrappedValueInit); - TypeChecker::checkInitializerEffects(initContext, wrappedValueInit); - } else { - typeCheckSynthesizedWrapperInitializer(var, wrappedValueInit); - } + typeCheckSynthesizedWrapperInitializer(var, wrappedValueInit); } return PropertyWrapperInitializerInfo(wrappedValueInit, projectedValueInit); @@ -2991,15 +3004,6 @@ static void finishProtocolStorageImplInfo(AbstractStorageDecl *storage, } } -/// This emits a diagnostic with a fixit to remove the attribute. -template -void diagnoseAndRemoveAttr(Decl *D, DeclAttribute *attr, - ArgTypes &&...Args) { - auto &ctx = D->getASTContext(); - ctx.Diags.diagnose(attr->getLocation(), std::forward(Args)...) - .fixItRemove(attr->getRangeWithAt()); -} - static void finishLazyVariableImplInfo(VarDecl *var, StorageImplInfo &info) { auto *attr = var->getAttrs().getAttribute(); @@ -3007,16 +3011,16 @@ static void finishLazyVariableImplInfo(VarDecl *var, // It cannot currently be used on let's since we don't have a mutability model // that supports it. if (var->isLet()) - diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_let); + diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_not_on_let); // lazy must have an initializer. if (!var->getParentInitializer()) - diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_initializer); + diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_requires_initializer); bool invalid = false; if (isa(var->getDeclContext())) { - diagnoseAndRemoveAttr(var, attr, diag::lazy_not_in_protocol); + diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_not_in_protocol); invalid = true; } @@ -3024,13 +3028,13 @@ static void finishLazyVariableImplInfo(VarDecl *var, if (info.getReadImpl() != ReadImplKind::Stored && (info.getWriteImpl() != WriteImplKind::Stored && info.getWriteImpl() != WriteImplKind::StoredWithObservers)) { - diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_computed); + diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_not_on_computed); invalid = true; } // The pattern binding must only bind a single variable. if (!var->getParentPatternBinding()->getSingleVar()) - diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_single_var); + diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_requires_single_var); if (!invalid) info = StorageImplInfo::getMutableComputed(); @@ -3082,7 +3086,7 @@ static void finishNSManagedImplInfo(VarDecl *var, auto *attr = var->getAttrs().getAttribute(); if (var->isLet()) - diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_let_property); + diagnoseAttrWithRemovalFixIt(var, attr, diag::attr_NSManaged_let_property); SourceFile *parentFile = var->getDeclContext()->getParentSourceFile(); @@ -3094,7 +3098,7 @@ static void finishNSManagedImplInfo(VarDecl *var, if (parentFile && parentFile->Kind == SourceFileKind::Interface) return; - diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_not_stored, kind); + diagnoseAttrWithRemovalFixIt(var, attr, diag::attr_NSManaged_not_stored, kind); }; // @NSManaged properties must be written as stored. diff --git a/lib/Sema/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp index 4ab47dba16b47..b43ff6696ee5f 100644 --- a/lib/Sema/TypeCheckSwitchStmt.cpp +++ b/lib/Sema/TypeCheckSwitchStmt.cpp @@ -137,7 +137,7 @@ namespace { cache.insert(getType().getPointer()); SmallVector spaces; - decompose(DC, getType(), spaces); + decomposeDisjuncts(DC, getType(), {}, spaces); size_t acc = 0; for (auto &sp : spaces) { // Decomposed pattern spaces grow with the sum of the subspaces. @@ -317,14 +317,14 @@ namespace { // (_ : Ty1) <= (_ : Ty2) iff D(Ty1) == D(Ty2) if (canDecompose(this->getType())) { - Space or1Space = decompose(DC, this->getType()); + Space or1Space = decompose(DC, this->getType(), {}); if (or1Space.isSubspace(other, DC)) { return true; } } if (canDecompose(other.getType())) { - Space or2Space = decompose(DC, other.getType()); + Space or2Space = decompose(DC, other.getType(), {}); return this->isSubspace(or2Space, DC); } @@ -342,13 +342,13 @@ namespace { if (!canDecompose(this->getType())) { return false; } - Space or1Space = decompose(DC, this->getType()); + Space or1Space = decompose(DC, this->getType(), {}); return or1Space.isSubspace(other, DC); } PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): { // (_ : Ty1) <= H(p1 | ... | pn) iff D(Ty1) <= H(p1 | ... | pn) if (canDecompose(this->getType())) { - Space or1Space = decompose(DC, this->getType()); + Space or1Space = decompose(DC, this->getType(), {}); return or1Space.isSubspace(other, DC); } // An undecomposable type is always larger than its constructor space. @@ -466,7 +466,7 @@ namespace { } PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): { if (canDecompose(this->getType())) { - auto decomposition = decompose(DC, this->getType()); + auto decomposition = decompose(DC, this->getType(), {}); return decomposition.minus(other, DC, minusCount); } else { return *this; @@ -480,8 +480,38 @@ namespace { // decomposable. return *this; + PAIRCASE (SpaceKind::Type, SpaceKind::Disjunct): { + // Optimize for the common case of a type minus a disjunct of + // constructor subspaces. This form of subtraction is guaranteed to + // happen very early on, and we can eliminate a huge part of the + // pattern space by only decomposing the parts of the type space that + // aren't actually covered by the disjunction. + if (canDecompose(this->getType())) { + llvm::StringSet<> otherConstructors; + for (auto s : other.getSpaces()) { + // Filter for constructor spaces with no payloads. + if (s.getKind() != SpaceKind::Constructor) { + continue; + } + + if (!s.getSpaces().empty()) { + continue; + } + + otherConstructors.insert(s.Head.getBaseIdentifier().str()); + } + + auto decomposition = decompose(DC, this->getType(), + otherConstructors); + return decomposition.minus(other, DC, minusCount); + } else { + // If the type isn't decomposable then there's no way we can + // subtract from it. Report the total space as uncovered. + return *this; + } + } + PAIRCASE (SpaceKind::Empty, SpaceKind::Disjunct): - PAIRCASE (SpaceKind::Type, SpaceKind::Disjunct): PAIRCASE (SpaceKind::Constructor, SpaceKind::Disjunct): PAIRCASE (SpaceKind::Disjunct, SpaceKind::Disjunct): PAIRCASE (SpaceKind::BooleanConstant, SpaceKind::Disjunct): @@ -628,7 +658,7 @@ namespace { } if (canDecompose(other.getType())) { - auto decomposition = decompose(DC, other.getType()); + auto decomposition = decompose(DC, other.getType(), {}); return this->minus(decomposition, DC, minusCount); } return *this; @@ -640,7 +670,7 @@ namespace { PAIRCASE (SpaceKind::Type, SpaceKind::BooleanConstant): { if (canDecompose(this->getType())) { - auto orSpace = decompose(DC, this->getType()); + auto orSpace = decompose(DC, this->getType(), {}); return orSpace.minus(other, DC, minusCount); } else { return *this; @@ -778,9 +808,16 @@ namespace { } }; - // Decompose a type into its component spaces. - static void decompose(const DeclContext *DC, Type tp, - SmallVectorImpl &arr) { + // Decompose a type into its component spaces, ignoring any enum + // cases that have no payloads and are also in the `voidList`. Membership + // there means the space is guaranteed by the subtraction procedure to be + // covered, so there's no reason to include it. Note that this *only* + // works for constructor spaces with no payloads as these cannot be + // overloaded and there is no further recursive structure to subtract + // into. + static void decomposeDisjuncts(const DeclContext *DC, Type tp, + const llvm::StringSet<> &voidList, + SmallVectorImpl &arr) { assert(canDecompose(tp) && "Non-decomposable type?"); if (tp->isBool()) { @@ -797,13 +834,20 @@ namespace { return Space(); } + // If we're guaranteed a match from a subtraction, don't include + // the space at all. See the `Type - Disjunct` case of + // subtraction for when this optimization applies. + if (!eed->hasAssociatedValues() && + voidList.contains(eed->getBaseIdentifier().str())) { + return Space(); + } + // .e(a: X, b: X) -> (a: X, b: X) // .f((a: X, b: X)) -> ((a: X, b: X) - auto eedTy = tp->getCanonicalType()->getTypeOfMember( - E->getModuleContext(), eed, - eed->getArgumentInterfaceType()); SmallVector constElemSpaces; - if (eedTy) { + if (auto payloadTy = eed->getArgumentInterfaceType()) { + auto eedTy = tp->getCanonicalType()->getTypeOfMember( + E->getModuleContext(), eed, payloadTy); if (auto *TTy = eedTy->getAs()) { Space::getTupleTypeSpaces(eedTy, TTy, constElemSpaces); } else if (auto *TTy = @@ -838,9 +882,11 @@ namespace { } } - static Space decompose(const DeclContext *DC, Type type) { + static Space decompose(const DeclContext *DC, + Type type, + const llvm::StringSet<> &voidList) { SmallVector spaces; - decompose(DC, type, spaces); + decomposeDisjuncts(DC, type, voidList, spaces); return Space::forDisjunct(spaces); } @@ -1034,7 +1080,7 @@ namespace { if (uncovered.getKind() == SpaceKind::Type) { if (Space::canDecompose(uncovered.getType())) { SmallVector spaces; - Space::decompose(DC, uncovered.getType(), spaces); + Space::decomposeDisjuncts(DC, uncovered.getType(), {}, spaces); diagnoseMissingCases(RequiresDefault::No, Space::forDisjunct(spaces), unknownCase); } else { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 162c2caf951e1..b85debf53c85a 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -29,7 +29,6 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" -#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" @@ -75,8 +74,7 @@ TypeResolution::forInterface(DeclContext *dc, TypeResolutionOptions options, HandlePlaceholderTypeReprFn placeholderHandler) { TypeResolution result(dc, TypeResolutionStage::Interface, options, unboundTyOpener, placeholderHandler); - result.complete.genericSig = dc->getGenericSignatureOfContext(); - result.complete.builder = nullptr; + result.genericSig = dc->getGenericSignatureOfContext(); return result; } @@ -102,7 +100,7 @@ TypeResolution::forContextual(DeclContext *dc, GenericEnvironment *genericEnv, TypeResolution TypeResolution::withOptions(TypeResolutionOptions opts) const { TypeResolution result(dc, stage, opts, unboundTyOpener, placeholderHandler); result.genericEnv = genericEnv; - result.complete = complete; + result.genericSig = genericSig; return result; } @@ -110,24 +108,14 @@ ASTContext &TypeResolution::getASTContext() const { return dc->getASTContext(); } -GenericSignatureBuilder *TypeResolution::getGenericSignatureBuilder() const { - assert(stage == TypeResolutionStage::Interface); - if (!complete.builder) { - auto genericSig = getGenericSignature(); - complete.builder = genericSig->getGenericSignatureBuilder(); - } - - return complete.builder; -} - GenericSignature TypeResolution::getGenericSignature() const { switch (stage) { case TypeResolutionStage::Contextual: return dc->getGenericSignatureOfContext(); case TypeResolutionStage::Interface: - if (complete.genericSig) - return complete.genericSig; + if (genericSig) + return genericSig; return dc->getGenericSignatureOfContext(); @@ -193,32 +181,24 @@ Type TypeResolution::resolveDependentMemberType( } assert(stage == TypeResolutionStage::Interface); - if (!getGenericSignature()) - return ErrorType::get(baseTy); - - auto builder = getGenericSignatureBuilder(); - auto baseEquivClass = - builder->resolveEquivalenceClass( - baseTy, - ArchetypeResolutionKind::CompleteWellFormed); - if (!baseEquivClass) + auto genericSig = getGenericSignature(); + if (!genericSig) return ErrorType::get(baseTy); - ASTContext &ctx = baseTy->getASTContext(); - // Look for a nested type with the given name. - if (auto nestedType = - baseEquivClass->lookupNestedType(*builder, refIdentifier)) { + if (auto nestedType = genericSig->lookupNestedType(baseTy, refIdentifier)) { // Record the type we found. ref->setValue(nestedType, nullptr); } else { + ASTContext &ctx = DC->getASTContext(); + // Resolve the base to a potential archetype. // Perform typo correction. TypoCorrectionResults corrections(ref->getNameRef(), ref->getNameLoc()); TypeChecker::performTypoCorrection(DC, DeclRefKind::Ordinary, MetatypeType::get(baseTy), defaultMemberLookupOptions, - corrections, builder); + corrections, genericSig); // Check whether we have a single type result. auto singleType = cast_or_null( @@ -257,13 +237,6 @@ Type TypeResolution::resolveDependentMemberType( return DependentMemberType::get(baseTy, assocType); } - // Otherwise, the nested type comes from a concrete type, - // or it's a typealias declared in protocol or protocol extension. - // Substitute the base type into it. - - // Make sure that base type didn't get replaced along the way. - assert(baseTy->isTypeParameter()); - // There are two situations possible here: // // 1. Member comes from the protocol, which means that it has been @@ -279,9 +252,12 @@ Type TypeResolution::resolveDependentMemberType( // end up using incorrect generic signature while attempting to form // a substituted type for the member we found. if (!concrete->getDeclContext()->getSelfProtocolDecl()) { - baseTy = baseEquivClass->concreteType ? baseEquivClass->concreteType - : baseEquivClass->superclass; - assert(baseTy); + if (auto concreteTy = genericSig->getConcreteType(baseTy)) + baseTy = concreteTy; + else { + baseTy = genericSig->getSuperclassBound(baseTy); + assert(baseTy); + } } return TypeChecker::substMemberTypeWithBase(DC->getParentModule(), concrete, @@ -304,16 +280,12 @@ Type TypeResolution::resolveSelfAssociatedType(Type baseTy, } assert(stage == TypeResolutionStage::Interface); - auto builder = getGenericSignatureBuilder(); - auto baseEquivClass = - builder->resolveEquivalenceClass( - baseTy, - ArchetypeResolutionKind::CompleteWellFormed); - if (!baseEquivClass) + auto genericSig = getGenericSignature(); + if (!genericSig) return ErrorType::get(baseTy); // Look for a nested type with the given name. - auto nestedType = baseEquivClass->lookupNestedType(*builder, name); + auto nestedType = genericSig->lookupNestedType(baseTy, name); assert(nestedType); // If the nested type has been resolved to an associated type, use it. @@ -326,9 +298,12 @@ Type TypeResolution::resolveSelfAssociatedType(Type baseTy, // extension. // // Get the superclass of the 'Self' type parameter. - baseTy = (baseEquivClass->concreteType - ? baseEquivClass->concreteType - : baseEquivClass->superclass); + if (auto concreteTy = genericSig->getConcreteType(baseTy)) + baseTy = concreteTy; + else { + baseTy = genericSig->getSuperclassBound(baseTy); + assert(baseTy); + } assert(baseTy); } @@ -384,16 +359,6 @@ bool TypeResolution::areSameType(Type type1, Type type2) const { return areSameType(depMem1->getBase(), depMem2->getBase()); } -Type TypeChecker::getArraySliceType(SourceLoc loc, Type elementType) { - ASTContext &ctx = elementType->getASTContext(); - if (!ctx.getArrayDecl()) { - ctx.Diags.diagnose(loc, diag::sugar_type_not_found, 0); - return ErrorType::get(ctx); - } - - return ArraySliceType::get(elementType); -} - Type TypeChecker::getOptionalType(SourceLoc loc, Type elementType) { ASTContext &ctx = elementType->getASTContext(); if (!ctx.getOptionalDecl()) { @@ -658,8 +623,8 @@ bool TypeChecker::checkContextualRequirements(GenericTypeDecl *decl, TypeChecker::checkGenericArguments( dc, loc, noteLoc, decl->getDeclaredInterfaceType(), - genericSig->getGenericParams(), - genericSig->getRequirements(), + genericSig.getGenericParams(), + genericSig.getRequirements(), QueryTypeSubstitutionMap{subMap}); switch (result) { @@ -712,11 +677,7 @@ static Type applyGenericArguments(Type type, TypeResolution resolution, if (const auto boundTy = openerFn(unboundTy)) return boundTy; - // Complain if we're allowed to and bail out with an error. - if (!options.contains(TypeResolutionFlags::SilenceErrors)) - diagnoseUnboundGenericType(type, loc); - - return ErrorType::get(resolution.getASTContext()); + return type; } } @@ -914,18 +875,25 @@ Type TypeResolution::applyUnboundGenericArguments( return BoundGenericType::get(nominalDecl, parentTy, genericArgs); } - assert(!resultType->hasTypeParameter()); - return resultType; + if (!resultType->hasTypeParameter()) + return resultType; + + auto parentSig = decl->getDeclContext()->getGenericSignatureOfContext(); + for (auto gp : parentSig.getGenericParams()) + subs[gp->getCanonicalType()->castTo()] = + genericSig->getConcreteType(gp); + } else { + subs = parentTy->getContextSubstitutions(decl->getDeclContext()); } - subs = parentTy->getContextSubstitutions(decl->getDeclContext()); skipRequirementsCheck |= parentTy->hasTypeVariable(); - } else if (auto genericEnv = - decl->getDeclContext()->getGenericEnvironmentOfContext()) { - auto genericSig = genericEnv->getGenericSignature(); - for (auto gp : genericSig->getGenericParams()) { + } else if (auto genericSig = + decl->getDeclContext()->getGenericSignatureOfContext()) { + for (auto gp : genericSig.getGenericParams()) { subs[gp->getCanonicalType()->castTo()] = - (usesArchetypes() ? genericEnv->mapTypeIntoContext(gp) : gp); + (usesArchetypes() + ? genericSig.getGenericEnvironment()->mapTypeIntoContext(gp) + : gp); } } @@ -936,7 +904,7 @@ Type TypeResolution::applyUnboundGenericArguments( // Realize the types of the generic arguments and add them to the // substitution map. for (unsigned i = 0, e = genericArgs.size(); i < e; ++i) { - auto origTy = genericSig->getInnermostGenericParams()[i]; + auto origTy = genericSig.getInnermostGenericParams()[i]; auto substTy = genericArgs[i]; // Enter a substitution. @@ -955,7 +923,7 @@ Type TypeResolution::applyUnboundGenericArguments( auto result = TypeChecker::checkGenericArguments( getDeclContext(), loc, noteLoc, UnboundGenericType::get(decl, parentTy, getASTContext()), - genericSig->getGenericParams(), genericSig->getRequirements(), + genericSig.getGenericParams(), genericSig.getRequirements(), QueryTypeSubstitutionMap{subs}); switch (result) { @@ -996,11 +964,17 @@ static void diagnoseUnboundGenericType(Type ty, SourceLoc loc) { if (auto unbound = ty->getAs()) { auto *decl = unbound->getDecl(); { - InFlightDiagnostic diag = ctx.Diags.diagnose(loc, - diag::generic_type_requires_arguments, ty); + // Compute the string before creating a new diagnostic, since + // getDefaultGenericArgumentsString() might emit its own + // diagnostics. SmallString<64> genericArgsToAdd; - if (TypeChecker::getDefaultGenericArgumentsString(genericArgsToAdd, - decl)) + bool hasGenericArgsToAdd = + TypeChecker::getDefaultGenericArgumentsString(genericArgsToAdd, + decl); + + auto diag = ctx.Diags.diagnose(loc, + diag::generic_type_requires_arguments, ty); + if (hasGenericArgsToAdd) diag.fixItInsertAfter(loc, genericArgsToAdd); } @@ -1255,18 +1229,17 @@ static Type diagnoseUnknownType(TypeResolution resolution, comp->getNameRef(), moduleType->getModule()->getName()); } else { LookupResult memberLookup; - // Let's try to lookup given identifier as a member of the parent type, - // this allows for more precise diagnostic, which distinguishes between - // identifier not found as a member type vs. not found at all. - NameLookupOptions memberLookupOptions = lookupOptions; - memberLookupOptions |= NameLookupFlags::IgnoreAccessControl; - memberLookup = TypeChecker::lookupMember(dc, parentType, - comp->getNameRef(), - memberLookupOptions); + // Let's try to look any member of the parent type with the given name, + // even if it is not a type, allowing for a more precise diagnostic. + NLOptions memberLookupOptions = (NL_QualifiedDefault | + NL_IgnoreAccessControl); + SmallVector results; + dc->lookupQualified(parentType, comp->getNameRef(), memberLookupOptions, + results); // Looks like this is not a member type, but simply a member of parent type. - if (!memberLookup.empty()) { - auto member = memberLookup[0].getValueDecl(); + if (!results.empty()) { + auto member = results[0]; diags.diagnose(comp->getNameLoc(), diag::invalid_member_reference, member->getDescriptiveKind(), member->getName(), parentType) @@ -1333,6 +1306,25 @@ static SelfTypeKind getSelfTypeKind(DeclContext *dc, } } +static void diagnoseGenericArgumentsOnSelf(TypeResolution resolution, + ComponentIdentTypeRepr *comp, + DeclContext *typeDC) { + ASTContext &ctx = resolution.getASTContext(); + auto &diags = ctx.Diags; + + auto *selfNominal = typeDC->getSelfNominalTypeDecl(); + auto declaredType = selfNominal->getDeclaredType(); + + diags.diagnose(comp->getNameLoc(), diag::cannot_specialize_self); + + if (selfNominal->isGeneric() && !isa(selfNominal)) { + diags.diagnose(comp->getNameLoc(), diag::specialize_explicit_type_instead, + declaredType) + .fixItReplace(comp->getNameLoc().getSourceRange(), + declaredType.getString()); + } +} + /// Resolve the given identifier type representation as an unqualified type, /// returning the type it references. /// @@ -1426,40 +1418,48 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution, return ErrorType::get(ctx); } - // If we found nothing, complain and give ourselves a chance to recover. - if (current.isNull()) { - // Dynamic 'Self' in the result type of a function body. - if (id.isSimpleName(ctx.Id_Self)) { - if (auto *typeDC = DC->getInnermostTypeContext()) { - // FIXME: The passed-in TypeRepr should get 'typechecked' as well. - // The issue is though that ComponentIdentTypeRepr only accepts a ValueDecl - // while the 'Self' type is more than just a reference to a TypeDecl. - auto selfType = resolution.mapTypeIntoContext( - typeDC->getSelfInterfaceType()); - - // Check if we can reference Self here, and if so, what kind of Self it is. - switch (getSelfTypeKind(DC, options)) { - case SelfTypeKind::StaticSelf: - return selfType; - case SelfTypeKind::DynamicSelf: - return DynamicSelfType::get(selfType, ctx); - case SelfTypeKind::InvalidSelf: - break; - } - } - } + // If we found a type declaration with the given name, return it now. + if (current) { + comp->setValue(currentDecl, currentDC); + return current; + } - // If we're not allowed to complain or we couldn't fix the - // source, bail out. - if (options.contains(TypeResolutionFlags::SilenceErrors)) - return ErrorType::get(ctx); + // 'Self' inside of a nominal type refers to that type. + if (id.isSimpleName(ctx.Id_Self)) { + if (auto *typeDC = DC->getInnermostTypeContext()) { + // FIXME: The passed-in TypeRepr should get 'typechecked' as well. + // The issue is though that ComponentIdentTypeRepr only accepts a ValueDecl + // while the 'Self' type is more than just a reference to a TypeDecl. + auto selfType = resolution.mapTypeIntoContext( + typeDC->getSelfInterfaceType()); + + // Check if we can reference 'Self' here, and if so, what kind of Self it is. + auto selfTypeKind = getSelfTypeKind(DC, options); + + // We don't allow generic arguments on 'Self'. + if (selfTypeKind != SelfTypeKind::InvalidSelf && + isa(comp)) { + diagnoseGenericArgumentsOnSelf(resolution, comp, typeDC); + } - return diagnoseUnknownType(resolution, nullptr, SourceRange(), comp, - lookupOptions); + switch (selfTypeKind) { + case SelfTypeKind::StaticSelf: + return selfType; + case SelfTypeKind::DynamicSelf: + return DynamicSelfType::get(selfType, ctx); + case SelfTypeKind::InvalidSelf: + break; + } + } } - comp->setValue(currentDecl, currentDC); - return current; + // If we're not allowed to complain, bail out. + if (options.contains(TypeResolutionFlags::SilenceErrors)) + return ErrorType::get(ctx); + + // Complain and give ourselves a chance to recover. + return diagnoseUnknownType(resolution, nullptr, SourceRange(), comp, + lookupOptions); } static void diagnoseAmbiguousMemberType(Type baseTy, SourceRange baseRange, @@ -1496,16 +1496,21 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution, auto maybeDiagnoseBadMemberType = [&](TypeDecl *member, Type memberType, AssociatedTypeDecl *inferredAssocType) { + bool hasUnboundOpener = !!resolution.getUnboundTypeOpener(); + if (options.contains(TypeResolutionFlags::SilenceErrors)) { - if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member) + if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member, + hasUnboundOpener) != TypeChecker::UnsupportedMemberTypeAccessKind::None) return ErrorType::get(ctx); } - switch (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member)) { + switch (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member, + hasUnboundOpener)) { case TypeChecker::UnsupportedMemberTypeAccessKind::None: break; + case TypeChecker::UnsupportedMemberTypeAccessKind::NominalTypeOfUnboundGeneric: case TypeChecker::UnsupportedMemberTypeAccessKind::TypeAliasOfUnboundGeneric: case TypeChecker::UnsupportedMemberTypeAccessKind::AssociatedTypeOfUnboundGeneric: diagnoseUnboundGenericType(parentTy, parentRange.End); @@ -1625,29 +1630,44 @@ static Type resolveIdentTypeComponent(TypeResolution resolution, GenericParamList *silParams, ArrayRef components) { - auto comp = components.back(); - // The first component uses unqualified lookup. - const auto parentComps = components.drop_back(); - if (parentComps.empty()) { - return resolveTopLevelIdentTypeComponent(resolution, silParams, - comp); - } + auto topLevelComp = components.front(); + auto result = resolveTopLevelIdentTypeComponent(resolution, silParams, + topLevelComp); + if (result->hasError()) + return ErrorType::get(result->getASTContext()); + + // Remaining components are resolved via iterated qualified lookups. + SourceRange parentRange(topLevelComp->getStartLoc(), + topLevelComp->getEndLoc()); + for (auto nestedComp : components.drop_front()) { + result = resolveNestedIdentTypeComponent(resolution, silParams, + result, parentRange, + nestedComp); + if (result->hasError()) + return ErrorType::get(result->getASTContext()); + + parentRange.End = nestedComp->getEndLoc(); + } + + // Diagnose an error if the last component's generic arguments are missing. + auto lastComp = components.back(); + auto options = resolution.getOptions(); + + if (result->is() && + !isa(lastComp) && + !resolution.getUnboundTypeOpener() && + !options.is(TypeResolverContext::TypeAliasDecl)) { - // All remaining components use qualified lookup. + if (!options.contains(TypeResolutionFlags::SilenceErrors)) { + diagnoseUnboundGenericType(result, + lastComp->getNameLoc().getBaseNameLoc()); + } - // Resolve the parent type. - Type parentTy = resolveIdentTypeComponent(resolution, silParams, - parentComps); - if (!parentTy || parentTy->hasError()) return parentTy; - - SourceRange parentRange(parentComps.front()->getStartLoc(), - parentComps.back()->getEndLoc()); + return ErrorType::get(result->getASTContext()); + } - // Resolve the nested type. - return resolveNestedIdentTypeComponent(resolution, silParams, - parentTy, parentRange, - comp); + return result; } // Hack to apply context-specific @escaping to an AST function type. @@ -1844,6 +1864,8 @@ namespace { TypeResolutionOptions options); NeverNullType resolveSpecifierTypeRepr(SpecifierTypeRepr *repr, TypeResolutionOptions options); + NeverNullType resolveIsolatedTypeRepr(IsolatedTypeRepr *repr, + TypeResolutionOptions options); NeverNullType resolveArrayType(ArrayTypeRepr *repr, TypeResolutionOptions options); NeverNullType resolveDictionaryType(DictionaryTypeRepr *repr, @@ -1874,11 +1896,6 @@ namespace { NeverNullType resolveOpaqueReturnType(TypeRepr *repr, StringRef mangledName, unsigned ordinal, TypeResolutionOptions options); - - /// Returns true if the given type conforms to `Differentiable` in the - /// module of `DC`. If `tangentVectorEqualsSelf` is true, returns true iff - /// the given type additionally satisfies `Self == Self.TangentVector`. - bool isDifferentiable(Type type, bool tangentVectorEqualsSelf = false); }; } // end anonymous namespace @@ -1921,6 +1938,31 @@ Type ResolveTypeRequest::evaluate(Evaluator &evaluator, if (validateAutoClosureAttributeUse(ctx.Diags, TyR, result, options)) return ErrorType::get(ctx); + // Diagnose an attempt to use a placeholder at the top level. + if (result->getCanonicalType()->is() && + resolution->getOptions().contains(TypeResolutionFlags::Direct)) { + if (!resolution->getOptions().contains(TypeResolutionFlags::SilenceErrors)) + ctx.Diags.diagnose(loc, diag::top_level_placeholder_type); + + TyR->setInvalid(); + return ErrorType::get(ctx); + } + + // Now that top-level placeholders have been diagnosed, replace them according + // to the user-specified handler (if it exists). + if (const auto handlerFn = resolution->getPlaceholderHandler()) { + result = result.get().transform([&](Type ty) { + if (auto *oldTy = ty->getAs()) { + auto originator = oldTy->getOriginator(); + if (auto *repr = originator.dyn_cast()) + if (auto newTy = handlerFn(ctx, repr)) + return newTy; + } + + return ty; + }); + } + return result; } @@ -1959,6 +2001,9 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, case TypeReprKind::Owned: return resolveSpecifierTypeRepr(cast(repr), options); + case TypeReprKind::Isolated: + return resolveIsolatedTypeRepr(cast(repr), options); + case TypeReprKind::SimpleIdent: case TypeReprKind::GenericIdent: case TypeReprKind::CompoundIdent: @@ -2004,11 +2049,29 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, case TypeReprKind::Protocol: return resolveProtocolType(cast(repr), options); - + case TypeReprKind::OpaqueReturn: { - // Only valid as the return type of a function, which should be handled - // during function decl type checking. + // If the opaque type is in a valid position, e.g. part of a function return + // type, resolution should happen in the context of an `OpaqueTypeDecl`. + // This decl is implicit in the source and is created in such contexts by + // evaluation of an `OpaqueResultTypeRequest`. auto opaqueRepr = cast(repr); + auto *DC = getDeclContext(); + if (isa(DC)) { + auto opaqueDecl = cast(DC); + auto outerGenericSignature = opaqueDecl->getNamingDecl() + ->getInnermostDeclContext() + ->getGenericSignatureOfContext(); + + SubstitutionMap subs; + if (outerGenericSignature) + subs = outerGenericSignature->getIdentitySubstitutionMap(); + + unsigned ordinal = opaqueDecl->getAnonymousOpaqueParamOrdinal(opaqueRepr); + return OpaqueTypeArchetypeType::get(opaqueDecl, ordinal, subs); + } + + // We are not inside an `OpaqueTypeDecl`, so diagnose an error. if (!(options & TypeResolutionFlags::SilenceErrors)) { diagnose(opaqueRepr->getOpaqueLoc(), diag::unsupported_opaque_type); @@ -2023,19 +2086,26 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, : ErrorType::get(getASTContext()); } - case TypeReprKind::Placeholder: { - auto &ctx = getASTContext(); - // Fill in the placeholder if there's an appropriate handler. - if (const auto handlerFn = resolution.getPlaceholderHandler()) - if (const auto ty = handlerFn(ctx, cast(repr))) - return ty; + case TypeReprKind::NamedOpaqueReturn: + return resolveType(cast(repr)->getBase(), + options); - // Complain if we're allowed to and bail out with an error. + case TypeReprKind::Placeholder: { + if (resolution.getPlaceholderHandler()) + // For now, just form a `PlaceholderType` so that we can properly diagnose + // invalid top-level placeholders. `ResolveTypeRequest::evaluate` will + // take care of substituting the placeholder based on the caller-specified + // handler. + return PlaceholderType::get(getASTContext(), + cast(repr)); + + // If there's no handler, complain if we're allowed to and bail out with an + // error. if (!options.contains(TypeResolutionFlags::SilenceErrors)) - ctx.Diags.diagnose(repr->getLoc(), + getASTContext().Diags.diagnose(repr->getLoc(), diag::placeholder_type_not_allowed); - return ErrorType::get(resolution.getASTContext()); + return ErrorType::get(getASTContext()); } case TypeReprKind::Fixed: @@ -2603,6 +2673,20 @@ TypeResolver::resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr, } } + if (attrs.has(TAK_unchecked)) { + if (!options.is(TypeResolverContext::Inherited) || + getDeclContext()->getSelfProtocolDecl()) { + diagnoseInvalid(repr, attrs.getLoc(TAK_unchecked), + diag::unchecked_not_inheritance_clause); + } else if (!ty->isExistentialType()) { + diagnoseInvalid(repr, attrs.getLoc(TAK_unchecked), + diag::unchecked_not_existential, ty); + } + + // Nothing to record in the type. Just clear the attribute. + attrs.clearAttribute(TAK_unchecked); + } + for (unsigned i = 0; i != TypeAttrKind::TAK_Count; ++i) if (attrs.has((TypeAttrKind)i)) { diagnoseInvalid(repr, attrs.getLoc((TypeAttrKind)i), @@ -2664,23 +2748,31 @@ TypeResolver::resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr, nestedRepr = tupleRepr->getElementType(0); } - if (auto *specifierRepr = dyn_cast(nestedRepr)) { - switch (specifierRepr->getKind()) { - case TypeReprKind::Shared: - ownership = ValueOwnership::Shared; - nestedRepr = specifierRepr->getBase(); - break; - case TypeReprKind::InOut: - ownership = ValueOwnership::InOut; - nestedRepr = specifierRepr->getBase(); - break; - case TypeReprKind::Owned: - ownership = ValueOwnership::Owned; - nestedRepr = specifierRepr->getBase(); - break; - default: - break; + bool isolated = false; + while (true) { + if (auto *specifierRepr = dyn_cast(nestedRepr)) { + switch (specifierRepr->getKind()) { + case TypeReprKind::Shared: + ownership = ValueOwnership::Shared; + nestedRepr = specifierRepr->getBase(); + continue; + case TypeReprKind::InOut: + ownership = ValueOwnership::InOut; + nestedRepr = specifierRepr->getBase(); + continue; + case TypeReprKind::Owned: + ownership = ValueOwnership::Owned; + nestedRepr = specifierRepr->getBase(); + continue; + case TypeReprKind::Isolated: + isolated = true; + nestedRepr = specifierRepr->getBase(); + continue; + default: + break; + } } + break; } bool noDerivative = false; @@ -2700,55 +2792,11 @@ TypeResolver::resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr, auto paramFlags = ParameterTypeFlags::fromParameterType( ty, variadic, autoclosure, /*isNonEphemeral*/ false, ownership, - noDerivative); + isolated, noDerivative); elements.emplace_back(ty, Identifier(), paramFlags, inputRepr->getElementName(i)); } - // All non-`@noDerivative` parameters of `@differentiable` function types must - // be differentiable. - if (diffKind != DifferentiabilityKind::NonDifferentiable && - resolution.getStage() != TypeResolutionStage::Structural) { - bool isLinear = diffKind == DifferentiabilityKind::Linear; - // Emit `@noDerivative` fixit only if there is at least one valid - // differentiability parameter. Otherwise, adding `@noDerivative` produces - // an ill-formed function type. - auto hasValidDifferentiabilityParam = - llvm::find_if(elements, [&](AnyFunctionType::Param param) { - if (param.isNoDerivative()) - return false; - return isDifferentiable(param.getPlainType(), - /*tangentVectorEqualsSelf*/ isLinear); - }) != elements.end(); - bool alreadyDiagnosedOneParam = false; - for (unsigned i = 0, end = inputRepr->getNumElements(); i != end; ++i) { - auto *eltTypeRepr = inputRepr->getElementType(i); - auto param = elements[i]; - if (param.isNoDerivative()) - continue; - auto paramType = param.getPlainType(); - if (isDifferentiable(paramType, isLinear)) - continue; - auto paramTypeString = paramType->getString(); - auto diagnostic = - diagnose(eltTypeRepr->getLoc(), - diag::differentiable_function_type_invalid_parameter, - paramTypeString, isLinear, hasValidDifferentiabilityParam); - alreadyDiagnosedOneParam = true; - if (hasValidDifferentiabilityParam) - diagnostic.fixItInsert(eltTypeRepr->getLoc(), "@noDerivative "); - } - // Reject the case where all parameters have '@noDerivative'. - if (!alreadyDiagnosedOneParam && !hasValidDifferentiabilityParam) { - diagnose( - inputRepr->getLoc(), - diag:: - differentiable_function_type_no_differentiability_parameters, - isLinear) - .highlight(inputRepr->getSourceRange()); - } - } - return elements; } @@ -2877,59 +2925,14 @@ NeverNullType TypeResolver::resolveASTFunctionType( if (fnTy->hasError()) return fnTy; - // If the type is a block or C function pointer, it must be representable in - // ObjC. - switch (representation) { - case AnyFunctionType::Representation::Block: - case AnyFunctionType::Representation::CFunctionPointer: - if (!fnTy->isRepresentableIn(ForeignLanguage::ObjectiveC, - getDeclContext())) { - StringRef strName = - (representation == AnyFunctionType::Representation::Block) - ? "block" - : "c"; - auto extInfo2 = - extInfo.withRepresentation(AnyFunctionType::Representation::Swift); - auto simpleFnTy = FunctionType::get(params, outputTy, extInfo2); - diagnose(repr->getStartLoc(), diag::objc_convention_invalid, - simpleFnTy, strName); - } - break; - - case AnyFunctionType::Representation::Thin: - case AnyFunctionType::Representation::Swift: - break; - } - - // `@differentiable` function types must return a differentiable type. - if (extInfo.isDifferentiable() && - resolution.getStage() != TypeResolutionStage::Structural) { - bool isLinear = diffKind == DifferentiabilityKind::Linear; - if (!isDifferentiable(outputTy, /*tangentVectorEqualsSelf*/ isLinear)) { - diagnose(repr->getResultTypeRepr()->getLoc(), - diag::differentiable_function_type_invalid_result, - outputTy->getString(), isLinear) - .highlight(repr->getResultTypeRepr()->getSourceRange()); - } - } + if (TypeChecker::diagnoseInvalidFunctionType(fnTy, repr->getLoc(), repr, + getDeclContext(), + resolution.getStage())) + return ErrorType::get(fnTy); return fnTy; } -bool TypeResolver::isDifferentiable(Type type, bool tangentVectorEqualsSelf) { - if (resolution.getStage() != TypeResolutionStage::Contextual) - type = getDeclContext()->mapTypeIntoContext(type); - auto tanSpace = type->getAutoDiffTangentSpace( - LookUpConformanceInModule(getDeclContext()->getParentModule())); - if (!tanSpace) - return false; - // If no `Self == Self.TangentVector` requirement, return true. - if (!tangentVectorEqualsSelf) - return true; - // Otherwise, return true if `Self == Self.TangentVector`. - return type->getCanonicalType() == tanSpace->getCanonicalType(); -} - NeverNullType TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, TypeResolutionOptions options) { // Resolve the field types. @@ -2975,9 +2978,9 @@ NeverNullType TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, if (genericSig) { TypeSubstitutionMap genericArgMap; - auto params = genericSig->getGenericParams(); + auto params = genericSig.getGenericParams(); if (repr->getGenericArguments().size() - != genericSig->getGenericParams().size()) { + != genericSig.getGenericParams().size()) { diagnose(repr->getLoc(), diag::sil_box_arg_mismatch); return ErrorType::get(getASTContext()); } @@ -3079,7 +3082,7 @@ NeverNullType TypeResolver::resolveSILFunctionType( TypeResolver &¶meterResolver) { auto sig = env->getGenericSignature().getCanonicalSignature(); TypeSubstitutionMap subsMap; - auto params = sig->getGenericParams(); + auto params = sig.getGenericParams(); for (unsigned i : indices(args)) { auto resolved = parameterResolver.resolveType(args[i], options); subsMap.insert({params[i], resolved->getCanonicalType()}); @@ -3197,7 +3200,8 @@ NeverNullType TypeResolver::resolveSILFunctionType( } witnessMethodConformance = TypeChecker::conformsToProtocol( - selfType, protocolType->getDecl(), getDeclContext()); + selfType, protocolType->getDecl(), + getDeclContext()->getParentModule()); assert(witnessMethodConformance && "found witness_method without matching conformance"); } @@ -3493,6 +3497,30 @@ TypeResolver::resolveSpecifierTypeRepr(SpecifierTypeRepr *repr, return resolveType(repr->getBase(), options); } +NeverNullType +TypeResolver::resolveIsolatedTypeRepr(IsolatedTypeRepr *repr, + TypeResolutionOptions options) { + // isolated is only value for non-EnumCaseDecl parameters. + if (!options.is(TypeResolverContext::FunctionInput) || + options.hasBase(TypeResolverContext::EnumElementDecl)) { + diagnoseInvalid( + repr, repr->getSpecifierLoc(), diag::attr_only_on_parameters, + "isolated"); + return ErrorType::get(getASTContext()); + } + + Type type = resolveType(repr->getBase(), options); + + // isolated parameters must be of actor type + if (!type->hasTypeParameter() && !type->isActorType() && !type->hasError()) { + diagnoseInvalid( + repr, repr->getSpecifierLoc(), diag::isolated_parameter_not_actor, type); + return ErrorType::get(type); + } + + return type; +} + NeverNullType TypeResolver::resolveArrayType(ArrayTypeRepr *repr, TypeResolutionOptions options) { auto baseTy = resolveType(repr->getBase(), options.withoutContext()); @@ -3500,12 +3528,17 @@ NeverNullType TypeResolver::resolveArrayType(ArrayTypeRepr *repr, return ErrorType::get(getASTContext()); } - auto sliceTy = - TypeChecker::getArraySliceType(repr->getBrackets().Start, baseTy); - if (sliceTy->hasError()) - return ErrorType::get(getASTContext()); + ASTContext &ctx = baseTy->getASTContext(); + // If the standard library isn't loaded, we ought to let the user know + // something has gone terribly wrong, since the rest of the compiler is going + // to assume it can canonicalize [T] to Array. + if (!ctx.getArrayDecl()) { + ctx.Diags.diagnose(repr->getBrackets().Start, + diag::sugar_type_not_found, 0); + return ErrorType::get(ctx); + } - return sliceTy; + return ArraySliceType::get(baseTy); } NeverNullType @@ -3589,6 +3622,7 @@ NeverNullType TypeResolver::resolveImplicitlyUnwrappedOptionalType( case TypeResolverContext::EditorPlaceholderExpr: case TypeResolverContext::AbstractFunctionDecl: case TypeResolverContext::ClosureExpr: + case TypeResolverContext::Inherited: doDiag = true; break; } @@ -3728,7 +3762,7 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr, if (ty->hasError()) return ty; auto nominalDecl = ty->getAnyNominal(); - if (nominalDecl && isa(nominalDecl)) { + if (isa_and_nonnull(nominalDecl)) { if (checkSuperclass(tyR->getStartLoc(), ty)) continue; @@ -3915,159 +3949,6 @@ Type TypeChecker::substMemberTypeWithBase(ModuleDecl *module, return resultType; } -namespace { - -class UnsupportedProtocolVisitor - : public TypeReprVisitor, public ASTWalker -{ - ASTContext &Ctx; - bool checkStatements; - bool hitTopStmt; - -public: - UnsupportedProtocolVisitor(ASTContext &ctx, bool checkStatements) - : Ctx(ctx), checkStatements(checkStatements), hitTopStmt(false) { } - - bool walkToTypeReprPre(TypeRepr *T) override { - if (T->isInvalid()) - return false; - if (auto compound = dyn_cast(T)) { - // Only visit the last component to check, because nested typealiases in - // existentials are okay. - visit(compound->getComponentRange().back()); - return false; - } - // Arbitrary protocol constraints are OK on opaque types. - if (isa(T)) - return false; - - visit(T); - return true; - } - - std::pair walkToStmtPre(Stmt *S) override { - if (checkStatements && !hitTopStmt) { - hitTopStmt = true; - return { true, S }; - } - - return { false, S }; - } - - bool walkToDeclPre(Decl *D) override { - return !checkStatements; - } - - void visitTypeRepr(TypeRepr *T) { - // Do nothing for all TypeReprs except the ones listed below. - } - - void visitIdentTypeRepr(IdentTypeRepr *T) { - if (T->isInvalid()) - return; - - auto comp = T->getComponentRange().back(); - if (auto *proto = dyn_cast_or_null(comp->getBoundDecl())) { - if (!proto->existentialTypeSupported()) { - Ctx.Diags.diagnose(comp->getNameLoc(), - diag::unsupported_existential_type, - proto->getName()); - T->setInvalid(); - } - } else if (auto *alias = dyn_cast_or_null(comp->getBoundDecl())) { - auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType()); - type.findIf([&](Type type) -> bool { - if (T->isInvalid()) - return false; - if (type->isExistentialType()) { - auto layout = type->getExistentialLayout(); - for (auto *proto : layout.getProtocols()) { - auto *protoDecl = proto->getDecl(); - if (protoDecl->existentialTypeSupported()) - continue; - - Ctx.Diags.diagnose(comp->getNameLoc(), - diag::unsupported_existential_type, - protoDecl->getName()); - T->setInvalid(); - } - } - return false; - }); - } - } - - void visitRequirements(ArrayRef reqts) { - for (auto reqt : reqts) { - if (reqt.getKind() == RequirementReprKind::SameType) { - if (auto *repr = reqt.getFirstTypeRepr()) - repr->walk(*this); - if (auto *repr = reqt.getSecondTypeRepr()) - repr->walk(*this); - } - } - } -}; - -} // end anonymous namespace - -void TypeChecker::checkUnsupportedProtocolType(Decl *decl) { - if (!decl || decl->isInvalid()) - return; - - auto &ctx = decl->getASTContext(); - if (auto *protocolDecl = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, protocolDecl->getTrailingWhereClause()); - } else if (auto *genericDecl = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, genericDecl->getGenericParams()); - checkUnsupportedProtocolType(ctx, genericDecl->getTrailingWhereClause()); - } else if (auto *assocType = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, assocType->getTrailingWhereClause()); - } else if (auto *extDecl = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, extDecl->getTrailingWhereClause()); - } else if (auto *subscriptDecl = dyn_cast(decl)) { - checkUnsupportedProtocolType(ctx, subscriptDecl->getGenericParams()); - checkUnsupportedProtocolType(ctx, subscriptDecl->getTrailingWhereClause()); - } else if (auto *funcDecl = dyn_cast(decl)) { - if (!isa(funcDecl)) { - checkUnsupportedProtocolType(ctx, funcDecl->getGenericParams()); - checkUnsupportedProtocolType(ctx, funcDecl->getTrailingWhereClause()); - } - } - - if (isa(decl) || isa(decl)) - return; - - UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); - decl->walk(visitor); -} - -void TypeChecker::checkUnsupportedProtocolType(ASTContext &ctx, Stmt *stmt) { - if (!stmt) - return; - - UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/true); - stmt->walk(visitor); -} - -void TypeChecker::checkUnsupportedProtocolType( - ASTContext &ctx, TrailingWhereClause *whereClause) { - if (whereClause == nullptr) - return; - - UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); - visitor.visitRequirements(whereClause->getRequirements()); -} - -void TypeChecker::checkUnsupportedProtocolType( - ASTContext &ctx, GenericParamList *genericParams) { - if (genericParams == nullptr) - return; - - UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); - visitor.visitRequirements(genericParams->getRequirements()); -} - Type CustomAttrTypeRequest::evaluate(Evaluator &eval, CustomAttr *attr, DeclContext *dc, CustomAttrTypeKind typeKind) const { diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index 8d7d403fb72a7..9c6624dfbce55 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -28,7 +28,6 @@ class TypeRepr; class ComponentIdentTypeRepr; class GenericEnvironment; class GenericSignature; -class GenericSignatureBuilder; /// Flags that describe the context of type checking a pattern or /// type. @@ -131,6 +130,9 @@ enum class TypeResolverContext : uint8_t { /// Whether this is the type of an editor placeholder. EditorPlaceholderExpr, + + /// Whether this is an "inherited" type. + Inherited, }; /// Options that determine how type resolution should work. @@ -212,6 +214,7 @@ class TypeResolutionOptions { case Context::GenericRequirement: case Context::ImmediateOptionalTypeArgument: case Context::AbstractFunctionDecl: + case Context::Inherited: return false; } llvm_unreachable("unhandled kind"); @@ -278,7 +281,9 @@ class TypeResolutionOptions { /// \returns the \c null type on failure. using OpenUnboundGenericTypeFn = llvm::function_ref; -/// A function reference used to handle a PlaceholderTypeRepr. +/// A function reference used to handle a \c PlaceholderTypeRepr. If the +/// function returns a null type, then the unmodified \c PlaceholderType will be +/// used. using HandlePlaceholderTypeReprFn = llvm::function_ref; @@ -293,20 +298,11 @@ class TypeResolution { HandlePlaceholderTypeReprFn placeholderHandler; private: - union { - /// The generic environment used to map to archetypes. - GenericEnvironment *genericEnv; - - /// The generic signature - struct { - /// The generic signature to use for type resolution. - GenericSignature genericSig; + /// The generic environment used to map to archetypes. + GenericEnvironment *genericEnv; - /// The generic signature builder that will answer queries about - /// generic types. - mutable GenericSignatureBuilder *builder; - } complete; - }; + /// The generic signature to use for type resolution. + GenericSignature genericSig; TypeResolution(DeclContext *dc, TypeResolutionStage stage, TypeResolutionOptions options, @@ -314,9 +310,8 @@ class TypeResolution { HandlePlaceholderTypeReprFn placeholderHandler) : dc(dc), stage(stage), options(options), unboundTyOpener(unboundTyOpener), - placeholderHandler(placeholderHandler) {} - - GenericSignatureBuilder *getGenericSignatureBuilder() const; + placeholderHandler(placeholderHandler), + genericEnv(nullptr) {} /// Retrieves the generic signature for the context, or NULL if there is /// no generic signature to resolve types. diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index c87ebf57b0d36..abcebd8441082 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -17,6 +17,7 @@ #include "swift/Subsystems.h" #include "TypeChecker.h" +#include "TypeCheckDecl.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" #include "CodeSynthesis.h" @@ -251,6 +252,9 @@ static void typeCheckDelayedFunctions(SourceFile &SF) { while (currentFunctionIdx < SF.DelayedFunctions.size()) { auto *AFD = SF.DelayedFunctions[currentFunctionIdx]; assert(!AFD->getDeclContext()->isLocalContext()); + + TypeChecker::buildTypeRefinementContextHierarchyDelayed(SF, AFD); + (void) AFD->getTypecheckedBody(); ++currentFunctionIdx; } @@ -335,6 +339,7 @@ void swift::performWholeModuleTypeChecking(SourceFile &SF) { diagnoseObjCMethodConflicts(SF); diagnoseObjCUnsatisfiedOptReqConflicts(SF); diagnoseUnintendedObjCMethodOverrides(SF); + diagnoseAttrsAddedByAccessNote(SF); return; case SourceFileKind::SIL: case SourceFileKind::Interface: @@ -376,7 +381,9 @@ Type swift::performTypeResolution(TypeRepr *TyR, ASTContext &Ctx, }, // FIXME: Don't let placeholder types escape type resolution. // For now, just return the placeholder type. - PlaceholderType::get); + [](auto &ctx, auto *originator) { + return Type(); + }); Optional suppression; if (!ProduceDiagnostics) @@ -433,11 +440,10 @@ swift::handleSILGenericParams(GenericParamList *genericParams, genericParams->walk(walker); } - auto sig = - TypeChecker::checkGenericSignature(nestedList.back(), DC, + return TypeChecker::checkGenericSignature(nestedList.back(), DC, /*parentSig=*/nullptr, - /*allowConcreteGenericParams=*/true); - return (sig ? sig->getGenericEnvironment() : nullptr); + /*allowConcreteGenericParams=*/true) + .getGenericEnvironment(); } void swift::typeCheckPatternBinding(PatternBindingDecl *PBD, @@ -489,3 +495,136 @@ TypeChecker::getDeclTypeCheckingSemantics(ValueDecl *decl) { } return DeclTypeCheckingSemantics::Normal; } + +bool TypeChecker::isDifferentiable(Type type, bool tangentVectorEqualsSelf, + DeclContext *dc, + Optional stage) { + if (stage && stage != TypeResolutionStage::Contextual) + type = dc->mapTypeIntoContext(type); + auto tanSpace = type->getAutoDiffTangentSpace( + LookUpConformanceInModule(dc->getParentModule())); + if (!tanSpace) + return false; + // If no `Self == Self.TangentVector` requirement, return true. + if (!tangentVectorEqualsSelf) + return true; + // Otherwise, return true if `Self == Self.TangentVector`. + return type->getCanonicalType() == tanSpace->getCanonicalType(); +} + +bool TypeChecker::diagnoseInvalidFunctionType(FunctionType *fnTy, SourceLoc loc, + Optionalrepr, + DeclContext *dc, + Optional stage) { + // If the type has a placeholder, don't try to diagnose anything now since + // we'll produce a better diagnostic when (if) the expression successfully + // typechecks. + if (fnTy->hasPlaceholder()) + return false; + + // If the type is a block or C function pointer, it must be representable in + // ObjC. + auto representation = fnTy->getRepresentation(); + auto extInfo = fnTy->getExtInfo(); + auto &ctx = dc->getASTContext(); + + bool hadAnyError = false; + + switch (representation) { + case AnyFunctionType::Representation::Block: + case AnyFunctionType::Representation::CFunctionPointer: + if (!fnTy->isRepresentableIn(ForeignLanguage::ObjectiveC, dc)) { + StringRef strName = + (representation == AnyFunctionType::Representation::Block) + ? "block" + : "c"; + auto extInfo2 = + extInfo.withRepresentation(AnyFunctionType::Representation::Swift); + auto simpleFnTy = FunctionType::get(fnTy->getParams(), fnTy->getResult(), + extInfo2); + ctx.Diags.diagnose(loc, diag::objc_convention_invalid, + simpleFnTy, strName); + hadAnyError = true; + } + break; + + case AnyFunctionType::Representation::Thin: + case AnyFunctionType::Representation::Swift: + break; + } + + // `@differentiable` function types must return a differentiable type and have + // differentiable (or `@noDerivative`) parameters. + if (extInfo.isDifferentiable() && + stage != TypeResolutionStage::Structural) { + auto result = fnTy->getResult(); + auto params = fnTy->getParams(); + auto diffKind = extInfo.getDifferentiabilityKind(); + bool isLinear = diffKind == DifferentiabilityKind::Linear; + + // Check the params. + + // Emit `@noDerivative` fixit only if there is at least one valid + // differentiability parameter. Otherwise, adding `@noDerivative` produces + // an ill-formed function type. + auto hasValidDifferentiabilityParam = + llvm::find_if(params, [&](AnyFunctionType::Param param) { + if (param.isNoDerivative()) + return false; + return TypeChecker::isDifferentiable(param.getPlainType(), + /*tangentVectorEqualsSelf*/ isLinear, + dc, stage); + }) != params.end(); + bool alreadyDiagnosedOneParam = false; + for (unsigned i = 0, end = fnTy->getNumParams(); i != end; ++i) { + auto param = params[i]; + if (param.isNoDerivative()) + continue; + auto paramType = param.getPlainType(); + if (TypeChecker::isDifferentiable(paramType, isLinear, dc, stage)) + continue; + auto diagLoc = + repr ? (*repr)->getArgsTypeRepr()->getElement(i).Type->getLoc() : loc; + auto paramTypeString = paramType->getString(); + auto diagnostic = ctx.Diags.diagnose( + diagLoc, diag::differentiable_function_type_invalid_parameter, + paramTypeString, isLinear, hasValidDifferentiabilityParam); + alreadyDiagnosedOneParam = true; + hadAnyError = true; + if (hasValidDifferentiabilityParam) + diagnostic.fixItInsert(diagLoc, "@noDerivative "); + } + // Reject the case where all parameters have '@noDerivative'. + if (!alreadyDiagnosedOneParam && !hasValidDifferentiabilityParam) { + auto diagLoc = repr ? (*repr)->getArgsTypeRepr()->getLoc() : loc; + auto diag = ctx.Diags.diagnose( + diagLoc, + diag::differentiable_function_type_no_differentiability_parameters, + isLinear); + hadAnyError = true; + + if (repr) { + diag.highlight((*repr)->getSourceRange()); + } + } + + // Check the result + bool differentiable = isDifferentiable(result, + /*tangentVectorEqualsSelf*/ isLinear, + dc, stage); + if (!differentiable) { + auto diagLoc = repr ? (*repr)->getResultTypeRepr()->getLoc() : loc; + auto resultStr = fnTy->getResult()->getString(); + auto diag = ctx.Diags.diagnose( + diagLoc, diag::differentiable_function_type_invalid_result, resultStr, + isLinear); + hadAnyError = true; + + if (repr) { + diag.highlight((*repr)->getResultTypeRepr()->getSourceRange()); + } + } + } + + return hadAnyError; +} diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c1443e0c309c3..619305d6828fd 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -23,6 +23,7 @@ #include "swift/AST/Availability.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/GenericParamList.h" +#include "swift/AST/GenericSignature.h" #include "swift/AST/KnownProtocols.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/NameLookup.h" @@ -38,8 +39,10 @@ namespace swift { +class Decl; +class DeclAttribute; +class DiagnosticEngine; class ExportContext; -class GenericSignatureBuilder; class NominalTypeDecl; class NormalProtocolConformance; class RootProtocolConformance; @@ -122,22 +125,15 @@ enum class TypeCheckExprFlags { /// disables constraints forcing an lvalue result to be loadable. IsDiscarded = 0x01, - /// If set, the client wants a best-effort solution to the constraint system, - /// but can tolerate a solution where all of the constraints are solved, but - /// not all type variables have been determined. In this case, the constraint - /// system is not applied to the expression AST, but the ConstraintSystem is - /// left in-tact. - AllowUnresolvedTypeVariables = 0x02, - /// If set, this expression isn't embedded in a larger expression or /// statement. This should only be used for syntactic restrictions, and should /// not affect type checking itself. - IsExprStmt = 0x04, + IsExprStmt = 0x02, /// Don't try to type check closure expression bodies, and leave them /// unchecked. This is used by source tooling functionalities such as code /// completion. - LeaveClosureBodyUnchecked = 0x08, + LeaveClosureBodyUnchecked = 0x04, }; using TypeCheckExprOptions = OptionSet; @@ -195,7 +191,7 @@ enum class Comparison { /// formatted with \c diagnoseConformanceStack. struct ParentConditionalConformance { Type ConformingType; - ProtocolType *Protocol; + ProtocolDecl *Protocol; /// Format the stack \c conformances as a series of notes that trace a path of /// conditional conformances that lead to some other failing requirement (that @@ -235,7 +231,6 @@ enum class CheckedCastContextKind { }; namespace TypeChecker { -Type getArraySliceType(SourceLoc loc, Type elementType); Type getOptionalType(SourceLoc loc, Type elementType); /// Bind an UnresolvedDeclRefExpr by performing name lookup and @@ -247,22 +242,6 @@ Type getOptionalType(SourceLoc loc, Type elementType); Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *Context, bool replaceInvalidRefsWithErrors); -/// Check for unsupported protocol types in the given declaration. -void checkUnsupportedProtocolType(Decl *decl); - -/// Check for unsupported protocol types in the given statement. -void checkUnsupportedProtocolType(ASTContext &ctx, Stmt *stmt); - -/// Check for unsupported protocol types in the given generic requirement -/// list. -void checkUnsupportedProtocolType(ASTContext &ctx, - TrailingWhereClause *whereClause); - -/// Check for unsupported protocol types in the given generic requirement -/// list. -void checkUnsupportedProtocolType(ASTContext &ctx, - GenericParamList *genericParams); - /// Substitute the given base type into the type of the given nested type, /// producing the effective type that the nested type will have. /// @@ -505,15 +484,29 @@ RequirementCheckResult checkGenericArguments( ArrayRef requirements, TypeSubstitutionFn substitutions, SubstOptions options = None); +/// A lower-level version of the above without diagnostic emission. +RequirementCheckResult checkGenericArguments( + ModuleDecl *module, + ArrayRef requirements, + TypeSubstitutionFn substitutions); + bool checkContextualRequirements(GenericTypeDecl *decl, Type parentTy, SourceLoc loc, DeclContext *dc); /// Add any implicitly-defined constructors required for the given -/// struct or class. +/// struct, class or actor. void addImplicitConstructors(NominalTypeDecl *typeDecl); +/// Synthesize and add a '_remote' counterpart of the passed in `func` to `decl`. +/// +/// \param decl the actor type to add the '_remote' definition to +/// \param func the 'distributed func' that the '_remote' func should mirror +/// \return the synthesized function +AbstractFunctionDecl *addImplicitDistributedActorRemoteFunction( + ClassDecl* decl, AbstractFunctionDecl *func); + /// Fold the given sequence expression into an (unchecked) expression /// tree. Expr *foldSequence(SequenceExpr *expr, DeclContext *dc); @@ -717,13 +710,10 @@ Expr *addImplicitLoadExpr( /// Determine whether the given type contains the given protocol. /// -/// \param DC The context in which to check conformance. This affects, for -/// example, extension visibility. -/// /// \returns the conformance, if \c T conforms to the protocol \c Proto, or /// an empty optional. ProtocolConformanceRef containsProtocol(Type T, ProtocolDecl *Proto, - DeclContext *DC, + ModuleDecl *M, bool skipConditionalRequirements=false); /// Determine whether the given type conforms to the given protocol. @@ -731,25 +721,22 @@ ProtocolConformanceRef containsProtocol(Type T, ProtocolDecl *Proto, /// Unlike subTypeOfProtocol(), this will return false for existentials of /// non-self conforming protocols. /// -/// \param DC The context in which to check conformance. This affects, for -/// example, extension visibility. -/// /// \returns The protocol conformance, if \c T conforms to the /// protocol \c Proto, or \c None. ProtocolConformanceRef conformsToProtocol(Type T, ProtocolDecl *Proto, - DeclContext *DC); + ModuleDecl *M); + +/// Check whether the type conforms to a given known protocol. +bool conformsToKnownProtocol(Type type, KnownProtocolKind protocol, + ModuleDecl *module); /// This is similar to \c conformsToProtocol, but returns \c true for cases where /// the type \p T could be dynamically cast to \p Proto protocol, such as a non-final /// class where a subclass conforms to \p Proto. /// -/// \param DC The context in which to check conformance. This affects, for -/// example, extension visibility. -/// -/// /// \returns True if \p T conforms to the protocol \p Proto, false otherwise. bool couldDynamicallyConformToProtocol(Type T, ProtocolDecl *Proto, - DeclContext *DC); + ModuleDecl *M); /// Completely check the given conformance. void checkConformance(NormalProtocolConformance *conformance); @@ -849,13 +836,15 @@ enum class UnsupportedMemberTypeAccessKind : uint8_t { TypeAliasOfUnboundGeneric, TypeAliasOfExistential, AssociatedTypeOfUnboundGeneric, - AssociatedTypeOfExistential + AssociatedTypeOfExistential, + NominalTypeOfUnboundGeneric }; /// Check whether the given declaration can be written as a /// member of the given base type. UnsupportedMemberTypeAccessKind -isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl); +isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl, + bool hasUnboundOpener); /// @} @@ -963,6 +952,10 @@ AvailabilityContext overApproximateAvailabilityAtLocation( /// Walk the AST to build the hierarchy of TypeRefinementContexts void buildTypeRefinementContextHierarchy(SourceFile &SF); +/// Walk the AST to complete the hierarchy of TypeRefinementContexts for +/// the delayed function body of \p AFD. +void buildTypeRefinementContextHierarchyDelayed(SourceFile &SF, AbstractFunctionDecl *AFD); + /// Build the hierarchy of TypeRefinementContexts for the entire /// source file, if it has not already been built. Returns the root /// TypeRefinementContext for the source file. @@ -1022,6 +1015,12 @@ diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange, const DeclContext *ReferenceDC, const UnavailabilityReason &Reason); +/// Type check a 'distributed actor' declaration. +void checkDistributedActor(ClassDecl *decl); + +void checkConcurrencyAvailability(SourceRange ReferenceRange, + const DeclContext *ReferenceDC); + /// Emits a diagnostic for a reference to a storage accessor that is /// potentially unavailable. void diagnosePotentialAccessorUnavailability( @@ -1034,10 +1033,8 @@ void diagnosePotentialAccessorUnavailability( const AvailableAttr *getDeprecated(const Decl *D); /// Emits a diagnostic for a reference to a declaration that is deprecated. -void diagnoseIfDeprecated(SourceRange SourceRange, - const ExportContext &Where, - const ValueDecl *DeprecatedDecl, - const ApplyExpr *Call); +void diagnoseIfDeprecated(SourceRange SourceRange, const ExportContext &Where, + const ValueDecl *DeprecatedDecl, const Expr *Call); /// Emits a diagnostic for a reference to a conformnace that is deprecated. bool diagnoseIfDeprecated(SourceLoc loc, @@ -1110,7 +1107,7 @@ void performTypoCorrection(DeclContext *DC, DeclRefKind refKind, Type baseTypeOrNull, NameLookupOptions lookupOptions, TypoCorrectionResults &corrections, - GenericSignatureBuilder *gsb = nullptr, + GenericSignature genericSig = GenericSignature(), unsigned maxResults = 4); /// Check if the given decl has a @_semantics attribute that gives it @@ -1160,23 +1157,20 @@ bool typeSupportsBuilderOp(Type builderType, DeclContext *dc, Identifier fnName, /// once. void applyAccessNote(ValueDecl *VD); -}; // namespace TypeChecker +/// Returns true if the given type conforms to `Differentiable` in the +/// module of `dc`. If `tangentVectorEqualsSelf` is true, returns true iff +/// the given type additionally satisfies `Self == Self.TangentVector`. +bool isDifferentiable(Type type, bool tangentVectorEqualsSelf, DeclContext *dc, + Optional stage); -/// Temporary on-stack storage and unescaping for encoded diagnostic -/// messages. -/// -/// -class EncodedDiagnosticMessage { - llvm::SmallString<128> Buf; +/// Emits diagnostics if the given function type's parameter/result types are +/// not compatible with the ext info. Returns whether an error was diagnosed. +bool diagnoseInvalidFunctionType(FunctionType *fnTy, SourceLoc loc, + Optionalrepr, + DeclContext *dc, + Optional stage); -public: - /// \param S A string with an encoded message - EncodedDiagnosticMessage(StringRef S) - : Message(Lexer::getEncodedStringSegment(S, Buf, true, true, ~0U)) {} - - /// The unescaped message to display to the user. - const StringRef Message; -}; +}; // namespace TypeChecker /// Returns the protocol requirement kind of the given declaration. /// Used in diagnostics. @@ -1188,13 +1182,13 @@ diag::RequirementKind getProtocolRequirementKind(ValueDecl *Requirement); /// @dynamicCallable attribute requirement. The method is given to be defined /// as one of the following: `dynamicallyCall(withArguments:)` or /// `dynamicallyCall(withKeywordArguments:)`. -bool isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC, +bool isValidDynamicCallableMethod(FuncDecl *decl, ModuleDecl *module, bool hasKeywordArguments); /// Returns true if the given subscript method is an valid implementation of /// the `subscript(dynamicMember:)` requirement for @dynamicMemberLookup. /// The method is given to be defined as `subscript(dynamicMember:)`. -bool isValidDynamicMemberLookupSubscript(SubscriptDecl *decl, DeclContext *DC, +bool isValidDynamicMemberLookupSubscript(SubscriptDecl *decl, ModuleDecl *module, bool ignoreLabel = false); /// Returns true if the given subscript method is an valid implementation of @@ -1202,7 +1196,7 @@ bool isValidDynamicMemberLookupSubscript(SubscriptDecl *decl, DeclContext *DC, /// The method is given to be defined as `subscript(dynamicMember:)` which /// takes a single non-variadic parameter that conforms to /// `ExpressibleByStringLiteral` protocol. -bool isValidStringDynamicMemberLookup(SubscriptDecl *decl, DeclContext *DC, +bool isValidStringDynamicMemberLookup(SubscriptDecl *decl, ModuleDecl *module, bool ignoreLabel = false); /// Returns true if the given subscript method is an valid implementation of @@ -1260,7 +1254,7 @@ Type getMemberTypeForComparison(const ValueDecl *member, /// Determine whether the given declaration is an override by comparing type /// information. bool isOverrideBasedOnType(const ValueDecl *decl, Type declTy, - const ValueDecl *parentDecl, Type parentDeclTy); + const ValueDecl *parentDecl); /// Determine whether the given declaration is an operator defined in a /// protocol. If \p type is not null, check specifically whether \p decl @@ -1292,11 +1286,6 @@ bool diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf); std::pair getObjCMethodDiagInfo( AbstractFunctionDecl *method); -bool areGenericRequirementsSatisfied(const DeclContext *DC, - GenericSignature sig, - SubstitutionMap Substitutions, - bool isExtension); - /// Check for restrictions on the use of the @unknown attribute on a /// case statement. void checkUnknownAttrRestrictions( @@ -1329,6 +1318,54 @@ void bindSwitchCasePatternVars(DeclContext *dc, CaseStmt *stmt); AnyFunctionType *applyGlobalActorType( AnyFunctionType *fnType, ValueDecl *funcOrEnum, DeclContext *dc); +/// If \p attr was added by an access note, wraps the error in +/// \c diag::wrap_invalid_attr_added_by_access_note and limits it as an access +/// note-related diagnostic should be. +InFlightDiagnostic softenIfAccessNote(const Decl *D, const DeclAttribute *attr, + InFlightDiagnostic &diag); + +/// Diagnose an error concerning an incorrect attribute (softening it if it's +/// caused by an access note) and emit a fix-it offering to remove it. +template +InFlightDiagnostic +diagnoseAttrWithRemovalFixIt(const Decl *D, const DeclAttribute *attr, + ArgTypes &&...Args) { + assert(D); + + if (D->hasClangNode() && (!attr || !attr->getAddedByAccessNote())) { + assert(false && "Clang importer propagated a bogus attribute"); + return InFlightDiagnostic(); + } + + auto &Diags = D->getASTContext().Diags; + + Optional diag; + if (!attr || !attr->getLocation().isValid()) + diag.emplace(Diags.diagnose(D, std::forward(Args)...)); + else + diag.emplace(std::move(Diags.diagnose(attr->getLocation(), + std::forward(Args)...) + .fixItRemove(attr->getRangeWithAt()))); + + return softenIfAccessNote(D, attr, *diag); +} + +/// Diagnose an error concerning an incorrect attribute (softening it if it's +/// caused by an access note), emit a fix-it offering to remove it, and mark the +/// attribute invalid so that it will be ignored by other parts of the compiler. +template +InFlightDiagnostic +diagnoseAndRemoveAttr(const Decl *D, const DeclAttribute *attr, + ArgTypes &&...Args) { + if (attr) + // FIXME: Due to problems with the design of DeclAttributes::getAttribute(), + // many callers try to pass us const DeclAttributes. This is a hacky + // workaround. + const_cast(attr)->setInvalid(); + + return diagnoseAttrWithRemovalFixIt(D, attr, std::forward(Args)...); +} + } // end namespace swift #endif diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index f0b211ecf4f8e..75b185c949bcd 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftSerialization STATIC Deserialization.cpp DeserializeSIL.cpp @@ -19,3 +19,4 @@ target_link_libraries(swiftSerialization PRIVATE swiftSIL swiftSymbolGraphGen) +set_swift_llvm_is_available(swiftSerialization) diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index ea7cdb0f08bb5..a4043fdbbd1e3 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -101,6 +101,7 @@ TYPE(DICTIONARY) TYPE(REFERENCE_STORAGE) TYPE(UNBOUND_GENERIC) TYPE(OPTIONAL) +TYPE(VARIADIC_SEQUENCE) TYPE(SIL_FUNCTION) TYPE(DYNAMIC_SELF) TYPE(OPENED_EXISTENTIAL) @@ -123,6 +124,7 @@ DECL(OPAQUE_TYPE) DECL(PATTERN_BINDING) DECL(PROTOCOL) TRAILING_INFO(DEFAULT_WITNESS_TABLE) +TRAILING_INFO(ASSOCIATED_TYPE) DECL(PREFIX_OPERATOR) DECL(POSTFIX_OPERATOR) DECL(INFIX_OPERATOR) @@ -168,7 +170,7 @@ OTHER(GENERIC_PARAM_LIST, 230) OTHER(GENERIC_SIGNATURE, 231) TRAILING_INFO(GENERIC_REQUIREMENT) TRAILING_INFO(LAYOUT_REQUIREMENT) -// 234 is unused +OTHER(BUILTIN_PROTOCOL_CONFORMANCE, 234) OTHER(SIL_GENERIC_SIGNATURE, 235) OTHER(SUBSTITUTION_MAP, 236) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 7df6d5a72ec73..996f6929e7dab 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -57,10 +57,6 @@ using namespace swift; using namespace swift::serialization; using llvm::Expected; -StringRef swift::getNameOfModule(const ModuleFile *MF) { - return MF->getName(); -} - namespace { struct DeclAndOffset { const Decl *D; @@ -111,7 +107,7 @@ namespace { os << DeclAndOffset{DeclOrOffset.get(), offset}; } } - os << " in '" << getNameOfModule(MF) << "'\n"; + os << " in '" << MF->getName() << "'\n"; } }; @@ -166,32 +162,15 @@ static void skipRecord(llvm::BitstreamCursor &cursor, unsigned recordKind) { (void)kind; } -void ModuleFile::fatal(llvm::Error error) { - if (FileContext) { - getContext().Diags.diagnose(SourceLoc(), diag::serialization_fatal, Core->Name); - getContext().Diags.diagnose(SourceLoc(), diag::serialization_misc_version, - Core->Name, Core->MiscVersion); - - if (!Core->CompatibilityVersion.empty()) { - if (getContext().LangOpts.EffectiveLanguageVersion - != Core->CompatibilityVersion) { - SmallString<16> effectiveVersionBuffer, compatVersionBuffer; - { - llvm::raw_svector_ostream out(effectiveVersionBuffer); - out << getContext().LangOpts.EffectiveLanguageVersion; - } - { - llvm::raw_svector_ostream out(compatVersionBuffer); - out << Core->CompatibilityVersion; - } - getContext().Diags.diagnose( - SourceLoc(), diag::serialization_compatibility_version_mismatch, - effectiveVersionBuffer, Core->Name, compatVersionBuffer); - } - } - } +void ModuleFile::fatal(llvm::Error error) const { + if (FileContext) + getContext().Diags.diagnose(SourceLoc(), diag::serialization_fatal, + Core->Name); + Core->fatal(std::move(error)); +} - ModuleFileSharedCore::fatal(std::move(error)); +void ModuleFile::outputDiagnosticInfo(llvm::raw_ostream &os) const { + Core->outputDiagnosticInfo(os); } static Optional @@ -564,6 +543,39 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor, return ProtocolConformanceRef(conformance); } + case BUILTIN_PROTOCOL_CONFORMANCE: { + TypeID conformingTypeID; + DeclID protoID; + GenericSignatureID genericSigID; + unsigned builtinConformanceKind; + BuiltinProtocolConformanceLayout::readRecord(scratch, conformingTypeID, + protoID, genericSigID, + builtinConformanceKind); + + Type conformingType = getType(conformingTypeID); + + auto decl = getDeclChecked(protoID); + if (!decl) + return decl.takeError(); + + auto proto = cast(decl.get()); + auto genericSig = getGenericSignatureChecked(genericSigID); + if (!genericSig) + return genericSig.takeError(); + + // Read the conditional requirements. + SmallVector conditionalRequirements; + auto error = readGenericRequirementsChecked( + conditionalRequirements, Cursor); + if (error) + return std::move(error); + + auto conformance = getContext().getBuiltinConformance( + conformingType, proto, *genericSig, conditionalRequirements, + static_cast(builtinConformanceKind)); + return ProtocolConformanceRef(conformance); + } + case NORMAL_PROTOCOL_CONFORMANCE_ID: { NormalConformanceID conformanceID; NormalProtocolConformanceIdLayout::readRecord(scratch, conformanceID); @@ -597,7 +609,7 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor, module = getAssociatedModule(); SmallVector conformances; - nominal->lookupConformance(module, proto, conformances); + nominal->lookupConformance(proto, conformances); PrettyStackTraceModuleFile traceMsg( "If you're seeing a crash here, check that your SDK and dependencies " "are at least as new as the versions used to build", *this); @@ -632,7 +644,7 @@ Expected ModuleFile::readNormalConformanceChecked( DeclID protoID; DeclContextID contextID; - unsigned valueCount, typeCount, conformanceCount; + unsigned valueCount, typeCount, conformanceCount, isUnchecked; ArrayRef rawIDs; SmallVector scratch; @@ -644,6 +656,7 @@ Expected ModuleFile::readNormalConformanceChecked( NormalProtocolConformanceLayout::readRecord(scratch, protoID, contextID, typeCount, valueCount, conformanceCount, + isUnchecked, rawIDs); ASTContext &ctx = getContext(); @@ -666,8 +679,8 @@ Expected ModuleFile::readNormalConformanceChecked( ++NumNormalProtocolConformancesLoaded; auto conformance = ctx.getConformance(conformingType, proto, SourceLoc(), dc, - ProtocolConformanceState::Incomplete); - + ProtocolConformanceState::Incomplete, + isUnchecked); // Record this conformance. if (conformanceEntry.isComplete()) return conformance; @@ -876,6 +889,36 @@ llvm::Error ModuleFile::readGenericRequirementsChecked( return llvm::Error::success(); } +void ModuleFile::readAssociatedTypes( + SmallVectorImpl &assocTypes, + llvm::BitstreamCursor &Cursor) { + using namespace decls_block; + + BCOffsetRAII lastRecordOffset(Cursor); + SmallVector scratch; + StringRef blobData; + + while (true) { + lastRecordOffset.reset(); + + llvm::BitstreamEntry entry = + fatalIfUnexpected(Cursor.advance(AF_DontPopBlockAtEnd)); + if (entry.Kind != llvm::BitstreamEntry::Record) + break; + + scratch.clear(); + unsigned recordID = fatalIfUnexpected( + Cursor.readRecord(entry.ID, scratch, &blobData)); + if (recordID != ASSOCIATED_TYPE) + break; + + DeclID declID; + AssociatedTypeLayout::readRecord(scratch, declID); + + assocTypes.push_back(cast(getDecl(declID))); + } +} + /// Advances past any records that might be part of a requirement signature. static llvm::Error skipGenericRequirements(llvm::BitstreamCursor &Cursor) { using namespace decls_block; @@ -909,6 +952,38 @@ static llvm::Error skipGenericRequirements(llvm::BitstreamCursor &Cursor) { return llvm::Error::success(); } +/// Advances past any lazy associated type member records. +static llvm::Error skipAssociatedTypeMembers(llvm::BitstreamCursor &Cursor) { + using namespace decls_block; + + BCOffsetRAII lastRecordOffset(Cursor); + + while (true) { + Expected maybeEntry = + Cursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + llvm::BitstreamEntry entry = maybeEntry.get(); + if (entry.Kind != llvm::BitstreamEntry::Record) + break; + + Expected maybeRecordID = Cursor.skipRecord(entry.ID); + if (!maybeRecordID) + return maybeRecordID.takeError(); + switch (maybeRecordID.get()) { + case ASSOCIATED_TYPE: + break; + + default: + // This record is not an associated type. + return llvm::Error::success(); + } + + lastRecordOffset.reset(); + } + return llvm::Error::success(); +} + GenericSignature ModuleFile::getGenericSignature( serialization::GenericSignatureID ID) { auto signature = getGenericSignatureChecked(ID); @@ -1836,7 +1911,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { } bool found = false; - for (auto paramTy : currentSig->getGenericParams()) { + for (auto paramTy : currentSig.getGenericParams()) { if (paramTy->getIndex() == paramIndex && paramTy->getDepth() == depth) { values.clear(); @@ -2473,14 +2548,19 @@ class DeclDeserializer { void handleInherited(llvm::PointerUnion decl, ArrayRef rawInheritedIDs) { - SmallVector inheritedTypes; + SmallVector inheritedTypes; for (auto rawID : rawInheritedIDs) { - auto maybeType = MF.getTypeChecked(rawID); + // The low bit indicates "@unchecked". + bool isUnchecked = rawID & 0x01; + TypeID typeID = rawID >> 1; + + auto maybeType = MF.getTypeChecked(typeID); if (!maybeType) { llvm::consumeError(maybeType.takeError()); continue; } - inheritedTypes.push_back(TypeLoc::withoutLoc(MF.getType(rawID))); + inheritedTypes.push_back( + InheritedEntry(TypeLoc::withoutLoc(maybeType.get()), isUnchecked)); } auto inherited = ctx.AllocateCopy(inheritedTypes); @@ -2512,7 +2592,7 @@ class DeclDeserializer { name = VD->getName(); } - auto diagId = ctx.LangOpts.AllowModuleWithCompilerErrors + auto diagId = MF.allowCompilerErrors() ? diag::serialization_allowing_invalid_decl : diag::serialization_invalid_decl; ctx.Diags.diagnose(SourceLoc(), diagId, name, @@ -3092,7 +3172,7 @@ class DeclDeserializer { declOrOffset = param; auto paramTy = MF.getType(interfaceTypeID); - if (paramTy->hasError() && !MF.isAllowModuleWithCompilerErrorsEnabled()) { + if (paramTy->hasError() && !MF.allowCompilerErrors()) { // FIXME: This should never happen, because we don't serialize // error types. DC->printContext(llvm::errs()); @@ -3381,9 +3461,10 @@ class DeclDeserializer { return cast(declOrOffset.get()); // Create the decl. - auto opaqueDecl = - new (ctx) OpaqueTypeDecl(nullptr, nullptr, declContext, - interfaceSig, interfaceType); + auto opaqueDecl = new (ctx) + OpaqueTypeDecl(/*NamingDecl*/ nullptr, + /*GenericParams*/ nullptr, declContext, interfaceSig, + /*UnderlyingInterfaceTypeRepr*/ nullptr, interfaceType); declOrOffset = opaqueDecl; auto namingDecl = cast(MF.getDecl(namingDeclID)); @@ -3412,7 +3493,8 @@ class DeclDeserializer { if (genericSig) { subs = genericSig->getIdentitySubstitutionMap(); } - auto opaqueTy = OpaqueTypeArchetypeType::get(opaqueDecl, subs); + // TODO [OPAQUE SUPPORT]: multiple opaque types + auto opaqueTy = OpaqueTypeArchetypeType::get(opaqueDecl, 0, subs); auto metatype = MetatypeType::get(opaqueTy); opaqueDecl->setInterfaceType(metatype); return opaqueDecl; @@ -3479,14 +3561,13 @@ class DeclDeserializer { StringRef blobData) { IdentifierID nameID; DeclContextID contextID; - bool isImplicit, isClassBounded, isObjC, existentialTypeSupported; + bool isImplicit, isClassBounded, isObjC; uint8_t rawAccessLevel; unsigned numInheritedTypes; ArrayRef rawInheritedAndDependencyIDs; decls_block::ProtocolLayout::readRecord(scratch, nameID, contextID, isImplicit, isClassBounded, isObjC, - existentialTypeSupported, rawAccessLevel, numInheritedTypes, rawInheritedAndDependencyIDs); @@ -3512,8 +3593,6 @@ class DeclDeserializer { ctx.evaluator.cacheOutput(ProtocolRequiresClassRequest{proto}, std::move(isClassBounded)); - ctx.evaluator.cacheOutput(ExistentialTypeSupportedRequest{proto}, - std::move(existentialTypeSupported)); if (auto accessLevel = getActualAccessLevel(rawAccessLevel)) proto->setAccess(*accessLevel); @@ -3537,6 +3616,11 @@ class DeclDeserializer { if (llvm::Error Err = skipGenericRequirements(MF.DeclTypeCursor)) MF.fatal(std::move(Err)); + proto->setLazyAssociatedTypeMembers(&MF, + MF.DeclTypeCursor.GetCurrentBitNo()); + if (llvm::Error Err = skipAssociatedTypeMembers(MF.DeclTypeCursor)) + MF.fatal(std::move(Err)); + proto->setMemberLoader(&MF, MF.DeclTypeCursor.GetCurrentBitNo()); return proto; @@ -3547,27 +3631,16 @@ class DeclDeserializer { StringRef blobData) { IdentifierID nameID; DeclContextID contextID; - ArrayRef designatedNominalTypeDeclIDs; - OperatorLayout::readRecord(scratch, nameID, contextID, - designatedNominalTypeDeclIDs); + OperatorLayout::readRecord(scratch, nameID, contextID); Identifier name = MF.getIdentifier(nameID); PrettySupplementalDeclNameTrace trace(name); auto DC = MF.getDeclContext(contextID); - SmallVector designatedNominalTypes; - for (auto id : designatedNominalTypeDeclIDs) { - Expected nominal = MF.getDeclChecked(id); - if (!nominal) - return nominal.takeError(); - designatedNominalTypes.push_back(cast(nominal.get())); - } - auto result = MF.createDecl( - DC, SourceLoc(), name, SourceLoc(), - ctx.AllocateCopy(designatedNominalTypes)); + DC, SourceLoc(), name, SourceLoc()); declOrOffset = result; return result; @@ -3590,11 +3663,9 @@ class DeclDeserializer { IdentifierID nameID; DeclContextID contextID; DeclID precedenceGroupID; - ArrayRef designatedNominalTypeDeclIDs; decls_block::InfixOperatorLayout::readRecord(scratch, nameID, contextID, - precedenceGroupID, - designatedNominalTypeDeclIDs); + precedenceGroupID); Identifier name = MF.getIdentifier(nameID); PrettySupplementalDeclNameTrace trace(name); @@ -3604,18 +3675,9 @@ class DeclDeserializer { auto DC = MF.getDeclContext(contextID); - SmallVector designatedNominalTypes; - for (auto id : designatedNominalTypeDeclIDs) { - Expected nominal = MF.getDeclChecked(id); - if (!nominal) - return nominal.takeError(); - designatedNominalTypes.push_back(cast(nominal.get())); - } - auto result = MF.createDecl( - DC, SourceLoc(), name, SourceLoc(), SourceLoc(), - ArrayRef>{}); - result->setDesignatedNominalTypes(ctx.AllocateCopy(designatedNominalTypes)); + DC, SourceLoc(), name, SourceLoc(), SourceLoc(), Identifier(), + SourceLoc()); ctx.evaluator.cacheOutput( OperatorPrecedenceGroupRequest{result}, std::move(cast_or_null(precedenceGroup.get()))); @@ -4125,7 +4187,7 @@ class DeclDeserializer { paramCount += paramList->size(); } assert(paramCount == - extension->getGenericSignature()->getGenericParams().size()); + extension->getGenericSignature().getGenericParams().size()); } #endif @@ -4284,14 +4346,6 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { break; } - case decls_block::ActorIndependent_DECL_ATTR: { - unsigned kind; - serialization::decls_block::ActorIndependentDeclAttrLayout::readRecord( - scratch, kind); - Attr = new (ctx) ActorIndependentAttr((ActorIndependentKind)kind); - break; - } - case decls_block::Optimize_DECL_ATTR: { unsigned kind; serialization::decls_block::OptimizeDeclAttrLayout::readRecord( @@ -4338,7 +4392,9 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { DEF_VER_TUPLE_PIECES(Introduced); DEF_VER_TUPLE_PIECES(Deprecated); DEF_VER_TUPLE_PIECES(Obsoleted); + DeclID renameDeclID; unsigned platform, messageSize, renameSize; + // Decode the record, pulling the version tuple information. serialization::decls_block::AvailableDeclAttrLayout::readRecord( scratch, isImplicit, isUnavailable, isDeprecated, @@ -4346,7 +4402,12 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { LIST_VER_TUPLE_PIECES(Introduced), LIST_VER_TUPLE_PIECES(Deprecated), LIST_VER_TUPLE_PIECES(Obsoleted), - platform, messageSize, renameSize); + platform, renameDeclID, messageSize, renameSize); + + ValueDecl *renameDecl = nullptr; + if (renameDeclID) { + renameDecl = cast(MF.getDecl(renameDeclID)); + } StringRef message = blobData.substr(0, messageSize); blobData = blobData.substr(messageSize); @@ -4373,7 +4434,7 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { Attr = new (ctx) AvailableAttr( SourceLoc(), SourceRange(), - (PlatformKind)platform, message, rename, + (PlatformKind)platform, message, rename, renameDecl, Introduced, SourceRange(), Deprecated, SourceRange(), Obsoleted, SourceRange(), @@ -4496,14 +4557,19 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { Expected deserialized = MF.getTypeChecked(typeID); if (!deserialized) { - if (deserialized.errorIsA()) { + if (deserialized.errorIsA() || + MF.allowCompilerErrors()) { // A custom attribute defined behind an implementation-only import // is safe to drop when it can't be deserialized. - // rdar://problem/56599179 + // rdar://problem/56599179. When allowing errors we're doing a best + // effort to create a module, so ignore in that case as well. consumeError(deserialized.takeError()); skipAttr = true; } else return deserialized.takeError(); + } else if (!deserialized.get() && MF.allowCompilerErrors()) { + // Serialized an invalid attribute, just skip it when allowing errors + skipAttr = true; } else { auto *TE = TypeExpr::createImplicit(deserialized.get(), ctx); auto custom = CustomAttr::create(ctx, SourceLoc(), TE, isImplicit); @@ -4638,21 +4704,6 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { break; } - case decls_block::CompletionHandlerAsync_DECL_ATTR: { - bool isImplicit; - uint64_t handlerIndex; - uint64_t asyncFunctionDeclID; - serialization::decls_block::CompletionHandlerAsyncDeclAttrLayout:: - readRecord(scratch, isImplicit, handlerIndex, asyncFunctionDeclID); - - auto mappedFunctionDecl = - cast(MF.getDecl(asyncFunctionDeclID)); - Attr = new (ctx) CompletionHandlerAsyncAttr( - *mappedFunctionDecl, handlerIndex, /*handlerIndexLoc*/ SourceLoc(), - /*atLoc*/ SourceLoc(), /*range*/ SourceRange(), isImplicit); - break; - } - #define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \ case decls_block::CLASS##_DECL_ATTR: { \ bool isImplicit; \ @@ -5253,11 +5304,12 @@ class TypeDeserializer { IdentifierID labelID; IdentifierID internalLabelID; TypeID typeID; - bool isVariadic, isAutoClosure, isNonEphemeral, isNoDerivative; + bool isVariadic, isAutoClosure, isNonEphemeral, isIsolated; + bool isNoDerivative; unsigned rawOwnership; decls_block::FunctionParamLayout::readRecord( scratch, labelID, internalLabelID, typeID, isVariadic, isAutoClosure, - isNonEphemeral, rawOwnership, isNoDerivative); + isNonEphemeral, rawOwnership, isIsolated, isNoDerivative); auto ownership = getActualValueOwnership((serialization::ValueOwnership)rawOwnership); @@ -5271,7 +5323,7 @@ class TypeDeserializer { params.emplace_back(paramTy.get(), MF.getIdentifier(labelID), ParameterTypeFlags(isVariadic, isAutoClosure, isNonEphemeral, *ownership, - isNoDerivative), + isIsolated, isNoDerivative), MF.getIdentifier(internalLabelID)); } @@ -5384,7 +5436,7 @@ class TypeDeserializer { MF.fatal(); Type interfaceType = GenericTypeParamType::get(depth, index, ctx); - Type contextType = sig->getGenericEnvironment() + Type contextType = sig.getGenericEnvironment() ->mapTypeIntoContext(interfaceType); if (contextType->hasError()) @@ -5419,7 +5471,9 @@ class TypeDeserializer { if (!subsOrError) return subsOrError.takeError(); - return OpaqueTypeArchetypeType::get(opaqueDecl, subsOrError.get()); + // TODO [OPAQUE SUPPORT]: to support multiple opaque types we will probably + // have to serialize the ordinal, which is always 0 for now + return OpaqueTypeArchetypeType::get(opaqueDecl, 0, subsOrError.get()); } Expected deserializeNestedArchetypeType(ArrayRef scratch, @@ -5844,6 +5898,18 @@ class TypeDeserializer { return OptionalType::get(baseTy.get()); } + Expected deserializeVariadicSequenceType(ArrayRef scratch, + StringRef blobData) { + TypeID baseID; + decls_block::VariadicSequenceTypeLayout::readRecord(scratch, baseID); + + auto baseTy = MF.getTypeChecked(baseID); + if (!baseTy) + return baseTy.takeError(); + + return VariadicSequenceType::get(baseTy.get()); + } + Expected deserializeUnboundGenericType(ArrayRef scratch, StringRef blobData) { DeclID genericID; @@ -5872,7 +5938,7 @@ class TypeDeserializer { return origTyOrError.takeError(); auto origTy = *origTyOrError; - auto diagId = ctx.LangOpts.AllowModuleWithCompilerErrors + auto diagId = MF.allowCompilerErrors() ? diag::serialization_allowing_error_type : diag::serialization_error_type; // Generally not a super useful diagnostic, so only output once if there @@ -5910,8 +5976,7 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { #ifndef NDEBUG PrettyStackTraceType trace(getContext(), "deserializing", typeOrOffset.get()); - if (typeOrOffset.get()->hasError() && - !isAllowModuleWithCompilerErrorsEnabled()) { + if (typeOrOffset.get()->hasError() && !allowCompilerErrors()) { typeOrOffset.get()->dump(llvm::errs()); llvm_unreachable("deserialization produced an invalid type " "(rdar://problem/30382791)"); @@ -5968,6 +6033,7 @@ Expected TypeDeserializer::getTypeCheckedImpl() { CASE(ArraySlice) CASE(Dictionary) CASE(Optional) + CASE(VariadicSequence) CASE(UnboundGeneric) CASE(Error) @@ -6256,9 +6322,15 @@ ModuleFile::loadAllConformances(const Decl *D, uint64_t contextData, if (!conformance) { auto unconsumedError = consumeErrorIfXRefNonLoadedModule(conformance.takeError()); - if (unconsumedError) - fatal(std::move(unconsumedError)); - return; + if (unconsumedError) { + // Ignore if allowing errors, it's just doing a best effort to produce + // *some* module anyway. + if (allowCompilerErrors()) + consumeError(std::move(unconsumedError)); + else + fatal(std::move(unconsumedError)); + } + continue; } if (conformance.get().isConcrete()) @@ -6299,7 +6371,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, uint64_t contextData) { using namespace decls_block; - PrettyStackTraceModuleFile traceModule("While reading from", *this); + PrettyStackTraceModuleFile traceModule(*this); PrettyStackTraceConformance trace("finishing conformance for", conformance); ++NumNormalProtocolConformancesCompleted; @@ -6318,7 +6390,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, DeclID protoID; DeclContextID contextID; - unsigned valueCount, typeCount, conformanceCount; + unsigned valueCount, typeCount, conformanceCount, isUnchecked; ArrayRef rawIDs; SmallVector scratch; @@ -6330,7 +6402,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, NormalProtocolConformanceLayout::readRecord(scratch, protoID, contextID, typeCount, valueCount, conformanceCount, - rawIDs); + isUnchecked, rawIDs); // Read requirement signature conformances. const ProtocolDecl *proto = conformance->getProtocol(); @@ -6372,15 +6444,24 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, auto isConformanceReq = [](const Requirement &req) { return req.getKind() == RequirementKind::Conformance; }; - if (!isAllowModuleWithCompilerErrorsEnabled() && + if (!allowCompilerErrors() && conformanceCount != llvm::count_if(proto->getRequirementSignature(), isConformanceReq)) { fatal(llvm::make_error( "serialized conformances do not match requirement signature", llvm::inconvertibleErrorCode())); } - while (conformanceCount--) - reqConformances.push_back(readConformance(DeclTypeCursor)); + while (conformanceCount--) { + auto conformance = readConformanceChecked(DeclTypeCursor); + if (conformance) { + reqConformances.push_back(conformance.get()); + } else if (allowCompilerErrors()) { + consumeError(conformance.takeError()); + reqConformances.push_back(ProtocolConformanceRef::forInvalid()); + } else { + fatal(conformance.takeError()); + } + } } conformance->setSignatureConformances(reqConformances); @@ -6411,8 +6492,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, } else { fatal(thirdOrError.takeError()); } - if (third && - isa(third) && + if (isa_and_nonnull(third) && third->getModuleContext() != getAssociatedModule() && !third->getDeclaredInterfaceType()->isEqual(second)) { // Conservatively drop references to typealiases in other modules @@ -6472,8 +6552,8 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, } else { fatal(deserializedWitness.takeError()); } - - assert(!req || isOpaque || witness || + + assert(allowCompilerErrors() || !req || isOpaque || witness || req->getAttrs().hasAttribute() || req->getAttrs().isUnavailable(getContext())); if (!witness && !isOpaque) { @@ -6493,8 +6573,11 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, if (!witnessSubstitutions) { // Missing module errors are most likely caused by an // implementation-only import hiding types and decls. - // rdar://problem/52837313 - if (witnessSubstitutions.errorIsA()) { + // rdar://problem/52837313. Ignore completely if allowing + // errors - we're just doing a best effort to create the + // module in that case. + if (witnessSubstitutions.errorIsA() || + allowCompilerErrors()) { consumeError(witnessSubstitutions.takeError()); isOpaque = true; } @@ -6536,6 +6619,14 @@ void ModuleFile::loadRequirementSignature(const ProtocolDecl *decl, readGenericRequirements(reqs, DeclTypeCursor); } +void ModuleFile::loadAssociatedTypes(const ProtocolDecl *decl, + uint64_t contextData, + SmallVectorImpl &assocTypes) { + BCOffsetRAII restoreOffset(DeclTypeCursor); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(contextData)); + readAssociatedTypes(assocTypes, DeclTypeCursor); +} + static Optional decodeRawStableForeignErrorConventionKind(uint8_t kind) { switch (kind) { diff --git a/lib/Serialization/DeserializationErrors.h b/lib/Serialization/DeserializationErrors.h index 9635943cd15e4..39e9f15452a05 100644 --- a/lib/Serialization/DeserializationErrors.h +++ b/lib/Serialization/DeserializationErrors.h @@ -13,6 +13,8 @@ #ifndef SWIFT_SERIALIZATION_DESERIALIZATIONERRORS_H #define SWIFT_SERIALIZATION_DESERIALIZATIONERRORS_H +#include "ModuleFile.h" +#include "ModuleFileSharedCore.h" #include "ModuleFormat.h" #include "swift/AST/Identifier.h" #include "swift/AST/Module.h" @@ -20,12 +22,6 @@ #include "llvm/Support/PrettyStackTrace.h" namespace swift { -class ModuleFile; -class ModuleFileSharedCore; - -StringRef getNameOfModule(const ModuleFile *); -StringRef getNameOfModule(const ModuleFileSharedCore *); - namespace serialization { class XRefTracePath { @@ -470,7 +466,9 @@ class PrettyStackTraceModuleFile : public llvm::PrettyStackTraceEntry { : PrettyStackTraceModuleFile("While reading from", module) {} void print(raw_ostream &os) const override { - os << Action << " \'" << getNameOfModule(&MF) << "'\n"; + os << Action << " "; + MF.outputDiagnosticInfo(os); + os << "\n"; } }; @@ -481,7 +479,9 @@ class PrettyStackTraceModuleFileCore : public llvm::PrettyStackTraceEntry { : MF(module) {} void print(raw_ostream &os) const override { - os << "While reading from \'" << getNameOfModule(&MF) << "'\n"; + os << "While reading from "; + MF.outputDiagnosticInfo(os); + os << "\n"; } }; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 6613fe1651b57..234a4e0e2a91f 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -406,7 +406,14 @@ SILFunction *SILDeserializer::getFuncForReference(StringRef name, // SIL. SourceLoc sourceLoc; SILSerializationFunctionBuilder builder(SILMod); - return builder.createDeclaration(name, type, RegularLocation(sourceLoc)); + fn = builder.createDeclaration(name, type, + RegularLocation(sourceLoc)); + // The function is not really de-serialized, but it's important to call + // `didDeserialize` on every new function. Otherwise some Analysis might miss + // `notifyAddedOrModifiedFunction` notifications. + if (Callback) + Callback->didDeserialize(MF->getAssociatedModule(), fn); + return fn; } /// Helper function to find a SILFunction, given its name and type. @@ -613,6 +620,10 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, if (getFile()->getParentModule() == SILMod.getSwiftModule()) fn->setLinkage(linkage); + if (getFile()->getParentModule()->isStaticLibrary() || + getFile()->getParentModule() == SILMod.getSwiftModule()) + fn->setIsStaticallyLinked(true); + // Don't override the transparency or linkage of a function with // an existing declaration, except if we deserialized a // PublicNonABI function, which has HiddenExternal when @@ -746,8 +757,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, GenericEnvironment *genericEnv = nullptr; if (!declarationOnly) - if (auto genericSig = MF->getGenericSignature(genericSigID)) - genericEnv = genericSig->getGenericEnvironment(); + genericEnv = MF->getGenericSignature(genericSigID).getGenericEnvironment(); // If the next entry is the end of the block, then this function has // no contents. @@ -1186,7 +1196,6 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILInstruction *ResultInst; switch (OpCode) { case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: llvm_unreachable("not supported"); case SILInstructionKind::AllocBoxInst: @@ -1842,17 +1851,26 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, UNARY_INSTRUCTION(EndLifetime) UNARY_INSTRUCTION(CopyBlock) UNARY_INSTRUCTION(LoadBorrow) - UNARY_INSTRUCTION(BeginBorrow) REFCOUNTING_INSTRUCTION(StrongRetain) REFCOUNTING_INSTRUCTION(StrongRelease) UNARY_INSTRUCTION(IsUnique) UNARY_INSTRUCTION(AbortApply) UNARY_INSTRUCTION(EndApply) - UNARY_INSTRUCTION(HopToExecutor) UNARY_INSTRUCTION(ExtractExecutor) #undef UNARY_INSTRUCTION #undef REFCOUNTING_INSTRUCTION + case SILInstructionKind::BeginBorrowInst: { + assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); + unsigned defined = Attr; + ResultInst = Builder.createBeginBorrow( + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID), + (SILValueCategory)TyCategory, Fn)), + defined == 1); + break; + } + case SILInstructionKind::IsEscapingClosureInst: { assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); unsigned verificationType = Attr; @@ -1864,6 +1882,16 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, break; } + case SILInstructionKind::HopToExecutorInst: { + assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); + unsigned mandatory = Attr; + ResultInst = Builder.createHopToExecutor( + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID), + (SILValueCategory)TyCategory, Fn)), + mandatory != 0); + break; + } case SILInstructionKind::DestroyValueInst: { assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); unsigned poisonRefs = Attr; diff --git a/lib/Serialization/ModuleDependencyScanner.cpp b/lib/Serialization/ModuleDependencyScanner.cpp index 6b325622cd16c..84d56861e3f1d 100644 --- a/lib/Serialization/ModuleDependencyScanner.cpp +++ b/lib/Serialization/ModuleDependencyScanner.cpp @@ -54,6 +54,7 @@ std::error_code ModuleDependencyScanner::findModuleFilesInDirectory( } } assert(fs.exists(InPath)); + // Use the private interface file if exits. auto PrivateInPath = BaseName.getName(file_types::TY_PrivateSwiftModuleInterfaceFile); @@ -85,9 +86,6 @@ std::error_code PlaceholderSwiftModuleScanner::findModuleFilesInDirectory( return std::make_error_code(std::errc::not_supported); } auto &moduleInfo = it->getValue(); - assert(!moduleInfo.moduleBuffer && - "Placeholder dependency module stubs cannot have an associated buffer"); - auto dependencies = ModuleDependencies::forPlaceholderSwiftModuleStub( moduleInfo.modulePath, moduleInfo.moduleDocPath, moduleInfo.moduleSourceInfoPath); @@ -123,7 +121,7 @@ ErrorOr ModuleDependencyScanner::scanInterfaceFile( std::string InPath = moduleInterfacePath.str(); auto compiledCandidates = getCompiledCandidates(Ctx, moduleName.str(), InPath); - Result = ModuleDependencies::forSwiftInterface(InPath, + Result = ModuleDependencies::forSwiftTextualModule(InPath, compiledCandidates, Args, PCMArgs, @@ -164,15 +162,19 @@ ErrorOr ModuleDependencyScanner::scanInterfaceFile( Optional SerializedModuleLoaderBase::getModuleDependencies( StringRef moduleName, ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate) { + auto currentSearchPathSet = Ctx.getAllModuleSearchPathsSet(); // Check whether we've cached this result. if (auto found = cache.findDependencies( - moduleName, ModuleDependenciesKind::SwiftTextual)) + moduleName, + {ModuleDependenciesKind::SwiftTextual, currentSearchPathSet})) return found; if (auto found = cache.findDependencies( - moduleName, ModuleDependenciesKind::SwiftBinary)) + moduleName, + {ModuleDependenciesKind::SwiftBinary, currentSearchPathSet})) return found; if (auto found = cache.findDependencies( - moduleName, ModuleDependenciesKind::SwiftPlaceholder)) + moduleName, + {ModuleDependenciesKind::SwiftPlaceholder, currentSearchPathSet})) return found; auto moduleId = Ctx.getIdentifier(moduleName); diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index f60ca4f1329cc..1d2aeeb027679 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -117,6 +117,10 @@ ModuleFile::ModuleFile(std::shared_ptr core) allocateBuffer(Identifiers, core->Identifiers); } +bool ModuleFile::allowCompilerErrors() const { + return getContext().LangOpts.AllowModuleWithCompilerErrors; +} + Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc, bool recoverFromIncompatibility) { PrettyStackTraceModuleFile stackEntry(*this); @@ -790,7 +794,13 @@ void ModuleFile::lookupClassMembers(ImportPath::Access accessPath, if (!accessPath.empty()) { for (const auto &list : Core->ClassMembersForDynamicLookup->data()) { for (auto item : list) { - auto vd = cast(getDecl(item.second)); + auto decl = getDeclChecked(item.second); + if (!decl) { + llvm::consumeError(decl.takeError()); + continue; + } + + auto vd = cast(decl.get()); auto dc = vd->getDeclContext(); while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); @@ -804,10 +814,17 @@ void ModuleFile::lookupClassMembers(ImportPath::Access accessPath, } for (const auto &list : Core->ClassMembersForDynamicLookup->data()) { - for (auto item : list) - consumer.foundDecl(cast(getDecl(item.second)), + for (auto item : list) { + auto decl = getDeclChecked(item.second); + if (!decl) { + llvm::consumeError(decl.takeError()); + continue; + } + + consumer.foundDecl(cast(decl.get()), DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); + } } } @@ -1245,9 +1262,9 @@ bool SerializedASTFile::getAllGenericSignatures( return true; } -Decl *SerializedASTFile::getMainDecl() const { +ValueDecl *SerializedASTFile::getMainDecl() const { assert(hasEntryPoint()); - return File.getDecl(File.getEntryPointDeclID()); + return cast_or_null(File.getDecl(File.getEntryPointDeclID())); } const version::Version &SerializedASTFile::getLanguageVersionBuiltWith() const { diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 5dd11a7761d22..20e512c0df5ab 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -75,8 +75,6 @@ class ModuleFile llvm::BitstreamCursor SILIndexCursor; llvm::BitstreamCursor DeclMemberTablesCursor; - friend StringRef getNameOfModule(const ModuleFile *); - public: static std::unique_ptr getModuleName(ASTContext &Ctx, StringRef modulePath, @@ -333,24 +331,27 @@ class ModuleFile return issue; } - /// Emits one last diagnostic, logs the error, and then aborts for the stack - /// trace. - LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error error); - void fatalIfNotSuccess(llvm::Error error) { + /// Emits one last diagnostic, adds the current module details and errors to + /// the pretty stack trace, and then aborts. + [[noreturn]] void fatal(llvm::Error error) const; + void fatalIfNotSuccess(llvm::Error error) const { if (error) fatal(std::move(error)); } - template T fatalIfUnexpected(llvm::Expected expected) { + template T fatalIfUnexpected(llvm::Expected expected) const { if (expected) return std::move(expected.get()); fatal(expected.takeError()); } - LLVM_ATTRIBUTE_NORETURN void fatal() { + [[noreturn]] void fatal() const { fatal(llvm::make_error( "(see \"While...\" info below)", llvm::inconvertibleErrorCode())); } + /// Outputs information useful for diagnostics to \p out + void outputDiagnosticInfo(llvm::raw_ostream &os) const; + ASTContext &getContext() const { assert(FileContext && "no associated context yet"); return FileContext->getParentModule()->getASTContext(); @@ -388,6 +389,10 @@ class ModuleFile readGenericRequirementsChecked(SmallVectorImpl &requirements, llvm::BitstreamCursor &Cursor); + /// Read a list of associated type declarations in a protocol. + void readAssociatedTypes(SmallVectorImpl &assocTypes, + llvm::BitstreamCursor &Cursor); + /// Populates the protocol's default witness table. /// /// Returns true if there is an error. @@ -478,10 +483,14 @@ class ModuleFile /// Whether this module is compiled while allowing errors /// ('-experimental-allow-module-with-compiler-errors'). - bool isAllowModuleWithCompilerErrorsEnabled() const { + bool compiledAllowingCompilerErrors() const { return Core->Bits.IsAllowModuleWithCompilerErrorsEnabled; } + /// Whether currently allowing modules with compiler errors (ie. + /// '-experimental-allow-module-with-compiler-errors' is currently enabled). + bool allowCompilerErrors() const; + /// \c true if this module has incremental dependency information. bool hasIncrementalInfo() const { return Core->hasIncrementalInfo(); } @@ -492,6 +501,9 @@ class ModuleFile /// .swiftsourceinfo file (ie. the file exists and has been read). bool hasSourceInfo() const { return Core->hasSourceInfo(); } + /// \c true if this module was built with complete checking for concurrency. + bool isConcurrencyChecked() const { return Core->isConcurrencyChecked(); } + /// Associates this module file with the AST node representing it. /// /// Checks that the file is compatible with the AST module it's being loaded @@ -704,6 +716,10 @@ class ModuleFile loadRequirementSignature(const ProtocolDecl *proto, uint64_t contextData, SmallVectorImpl &requirements) override; + void + loadAssociatedTypes(const ProtocolDecl *proto, uint64_t contextData, + SmallVectorImpl &assocTypes) override; + Optional getGroupNameById(unsigned Id) const; Optional getSourceFileNameById(unsigned Id) const; Optional getGroupNameForDecl(const Decl *D) const; diff --git a/lib/Serialization/ModuleFileSharedCore.cpp b/lib/Serialization/ModuleFileSharedCore.cpp index f93f6db26412d..8fba7e2ed85b1 100644 --- a/lib/Serialization/ModuleFileSharedCore.cpp +++ b/lib/Serialization/ModuleFileSharedCore.cpp @@ -14,19 +14,17 @@ #include "ModuleFileCoreTableInfo.h" #include "BCReadingExtras.h" #include "DeserializationErrors.h" +#include "swift/Basic/LangOptions.h" #include "swift/Strings.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/OnDiskHashTable.h" +#include "llvm/Support/PrettyStackTrace.h" using namespace swift; using namespace swift::serialization; using namespace llvm::support; using llvm::Expected; -StringRef swift::getNameOfModule(const ModuleFileSharedCore *MF) { - return MF->getName(); -} - static bool checkModuleSignature(llvm::BitstreamCursor &cursor, ArrayRef signature) { for (unsigned char byte : signature) { @@ -154,6 +152,9 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor, case options_block::MODULE_ABI_NAME: extendedInfo.setModuleABIName(blobData); break; + case options_block::IS_CONCURRENCY_CHECKED: + extendedInfo.setIsConcurrencyChecked(true); + break; default: // Unknown options record, possibly for use by a future version of the // module format. @@ -255,9 +256,21 @@ validateControlBlock(llvm::BitstreamCursor &cursor, switch (scratch.size()) { default: // Add new cases here, in descending order. + case 8: + case 7: case 6: case 5: { - result.userModuleVersion = llvm::VersionTuple(scratch[4], scratch[5]); + auto subMinor = 0; + auto build = 0; + // case 7 and 8 were added after case 5 and 6, so we need to have this + // special handling to make sure we can still load the module without + // case 7 and case 8 successfully. + if (scratch.size() >= 8) { + subMinor = scratch[6]; + build = scratch[7]; + } + result.userModuleVersion = llvm::VersionTuple(scratch[4], scratch[5], + subMinor, build); LLVM_FALLTHROUGH; } case 4: @@ -460,13 +473,31 @@ std::string ModuleFileSharedCore::Dependency::getPrettyPrintedPath() const { return output; } -void ModuleFileSharedCore::fatal(llvm::Error error) { - logAllUnhandledErrors(std::move(error), llvm::errs(), - "\n*** DESERIALIZATION FAILURE (please include this " - "section in any bug report) ***\n"); +void ModuleFileSharedCore::fatal(llvm::Error error) const { + llvm::SmallString<0> errorStr; + llvm::raw_svector_ostream out(errorStr); + + out << "*** DESERIALIZATION FAILURE ***\n"; + outputDiagnosticInfo(out); + out << "\n"; + if (error) { + handleAllErrors(std::move(error), [&](const llvm::ErrorInfoBase &ei) { + ei.log(out); + out << "\n"; + }); + } + + llvm::PrettyStackTraceString trace(errorStr.c_str()); abort(); } +void ModuleFileSharedCore::outputDiagnosticInfo(llvm::raw_ostream &os) const { + os << "module '" << Name << "' with full misc version '" << MiscVersion + << "'"; + if (Bits.IsAllowModuleWithCompilerErrorsEnabled) + os << " (built with -experimental-allow-module-with-compiler-errors)"; +} + ModuleFileSharedCore::~ModuleFileSharedCore() { } std::unique_ptr @@ -1189,6 +1220,7 @@ ModuleFileSharedCore::ModuleFileSharedCore( Bits.IsImplicitDynamicEnabled = extInfo.isImplicitDynamicEnabled(); Bits.IsAllowModuleWithCompilerErrorsEnabled = extInfo.isAllowModuleWithCompilerErrorsEnabled(); + Bits.IsConcurrencyChecked = extInfo.isConcurrencyChecked(); MiscVersion = info.miscVersion; ModuleABIName = extInfo.getModuleABIName(); diff --git a/lib/Serialization/ModuleFileSharedCore.h b/lib/Serialization/ModuleFileSharedCore.h index ec344cf93d614..e192013c596fc 100644 --- a/lib/Serialization/ModuleFileSharedCore.h +++ b/lib/Serialization/ModuleFileSharedCore.h @@ -341,8 +341,11 @@ class ModuleFileSharedCore { /// Whether this module is compiled while allowing errors. unsigned IsAllowModuleWithCompilerErrorsEnabled: 1; + /// \c true if this module was built with complete checking for concurrency. + unsigned IsConcurrencyChecked: 1; + // Explicitly pad out to the next word boundary. - unsigned : 3; + unsigned : 5; } Bits = {}; static_assert(sizeof(ModuleBits) <= 8, "The bit set should be small"); @@ -371,12 +374,12 @@ class ModuleFileSharedCore { /// Emits one last diagnostic, logs the error, and then aborts for the stack /// trace. - LLVM_ATTRIBUTE_NORETURN static void fatal(llvm::Error error); - void fatalIfNotSuccess(llvm::Error error) { + [[noreturn]] void fatal(llvm::Error error) const; + void fatalIfNotSuccess(llvm::Error error) const { if (error) fatal(std::move(error)); } - template T fatalIfUnexpected(llvm::Expected expected) { + template T fatalIfUnexpected(llvm::Expected expected) const { if (expected) return std::move(expected.get()); fatal(expected.takeError()); @@ -508,6 +511,9 @@ class ModuleFileSharedCore { return info; } + /// Outputs information useful for diagnostics to \p out + void outputDiagnosticInfo(llvm::raw_ostream &os) const; + // Out of line to avoid instantiation OnDiskChainedHashTable here. ~ModuleFileSharedCore(); @@ -531,6 +537,8 @@ class ModuleFileSharedCore { /// Returns \c true if a corresponding .swiftsourceinfo has been found *and /// read*. bool hasSourceInfo() const; + + bool isConcurrencyChecked() const { return Bits.IsConcurrencyChecked; } }; template diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index b2a0421ceef6c..e2165a62c24f2 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 613; // isStaticLibrary option +const uint16_t SWIFTMODULE_VERSION_MINOR = 625; // is concurrency checked? /// A standard hash seed used for all string hashes in a serialized module. /// @@ -765,6 +765,8 @@ namespace control_block { BCVBR<8>, // length of "short compatibility version string" in the blob BCVBR<17>, // User module format major version BCVBR<17>, // User module format minor version + BCVBR<17>, // User module format sub-minor version + BCVBR<17>, // User module format build version BCBlob // misc. version information >; @@ -795,6 +797,7 @@ namespace options_block { IS_IMPLICIT_DYNAMIC_ENABLED, IS_ALLOW_MODULE_WITH_COMPILER_ERRORS_ENABLED, MODULE_ABI_NAME, + IS_CONCURRENCY_CHECKED, }; using SDKPathLayout = BCRecordLayout< @@ -841,6 +844,10 @@ namespace options_block { MODULE_ABI_NAME, BCBlob >; + + using IsConcurrencyCheckedLayout = BCRecordLayout< + IS_CONCURRENCY_CHECKED + >; } /// The record types within the input block. @@ -1020,6 +1027,7 @@ namespace decls_block { BCFixed<1>, // autoclosure? BCFixed<1>, // non-ephemeral? ValueOwnershipField, // inout, shared or owned? + BCFixed<1>, // isolated BCFixed<1> // noDerivative? >; @@ -1142,6 +1150,7 @@ namespace decls_block { using ArraySliceTypeLayout = SyntaxSugarTypeLayout; using OptionalTypeLayout = SyntaxSugarTypeLayout; + using VariadicSequenceTypeLayout = SyntaxSugarTypeLayout; using DictionaryTypeLayout = BCRecordLayout< DICTIONARY_TYPE, @@ -1248,7 +1257,6 @@ namespace decls_block { BCFixed<1>, // implicit flag BCFixed<1>, // class-bounded? BCFixed<1>, // objc? - BCFixed<1>, // existential-type-supported? AccessLevelField, // access level BCVBR<4>, // number of inherited types BCArray // inherited types, followed by dependency types @@ -1424,8 +1432,7 @@ namespace decls_block { using UnaryOperatorLayout = BCRecordLayout< Code, // ID field IdentifierIDField, // name - DeclContextIDField, // context decl - BCArray // designated types + DeclContextIDField // context decl >; using PrefixOperatorLayout = UnaryOperatorLayout; @@ -1435,8 +1442,7 @@ namespace decls_block { INFIX_OPERATOR_DECL, IdentifierIDField, // name DeclContextIDField,// context decl - DeclIDField, // precedence group - BCArray // designated types + DeclIDField // precedence group >; using PrecedenceGroupLayout = BCRecordLayout< @@ -1609,6 +1615,11 @@ namespace decls_block { BCVBR<8> // alignment >; + using AssociatedTypeLayout = BCRecordLayout< + ASSOCIATED_TYPE, + DeclIDField // associated type decl + >; + /// Specifies the private discriminator string for a private declaration. This /// identifies the declaration's original source file in some opaque way. using PrivateDiscriminatorLayout = BCRecordLayout< @@ -1644,6 +1655,7 @@ namespace decls_block { BCVBR<5>, // type mapping count BCVBR<5>, // value mapping count BCVBR<5>, // requirement signature conformance count + BCFixed<1>, // unchecked BCArray // The array contains type witnesses, then value witnesses. // Requirement signature conformances follow, then the substitution records @@ -1667,6 +1679,15 @@ namespace decls_block { TypeIDField // the conforming type >; + using BuiltinProtocolConformanceLayout = BCRecordLayout< + BUILTIN_PROTOCOL_CONFORMANCE, + TypeIDField, // the conforming type + DeclIDField, // the protocol + GenericSignatureIDField, // the generic signature + BCFixed<2> // the builtin conformance kind + // the (optional) conditional requirements follow + >; + // Refers to a normal protocol conformance in the given module via its id. using NormalProtocolConformanceIdLayout = BCRecordLayout< NORMAL_PROTOCOL_CONFORMANCE_ID, @@ -1855,11 +1876,6 @@ namespace decls_block { BCFixed<2> // inline value >; - using ActorIndependentDeclAttrLayout = BCRecordLayout< - ActorIndependent_DECL_ATTR, - BCFixed<1> // unsafe flag - >; - using OptimizeDeclAttrLayout = BCRecordLayout< Optimize_DECL_ATTR, BCFixed<2> // optimize value @@ -1874,10 +1890,11 @@ namespace decls_block { BC_AVAIL_TUPLE, // Introduced BC_AVAIL_TUPLE, // Deprecated BC_AVAIL_TUPLE, // Obsoleted - BCVBR<5>, // platform - BCVBR<5>, // number of bytes in message string - BCVBR<5>, // number of bytes in rename string - BCBlob // platform, followed by message + BCVBR<5>, // platform + DeclIDField, // rename declaration (if any) + BCVBR<5>, // number of bytes in message string + BCVBR<5>, // number of bytes in rename string + BCBlob // message, followed by rename >; using OriginallyDefinedInDeclAttrLayout = BCRecordLayout< @@ -1935,13 +1952,6 @@ namespace decls_block { BCArray> // Transposed parameter indices' bitvector. >; - using CompletionHandlerAsyncDeclAttrLayout = BCRecordLayout< - CompletionHandlerAsync_DECL_ATTR, - BCFixed<1>, // Implicit flag. - BCVBR<5>, // Completion handler index - DeclIDField // Mapped async function decl - >; - #define SIMPLE_DECL_ATTR(X, CLASS, ...) \ using CLASS##DeclAttrLayout = BCRecordLayout< \ CLASS##_DECL_ATTR, \ diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 70b805453b35b..b326e40795fdf 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -76,10 +76,7 @@ using namespace llvm::support; using swift::version::Version; using llvm::BCBlockRAII; - -ASTContext &SerializerBase::getASTContext() { - return M->getASTContext(); -} +ASTContext &SerializerBase::getASTContext() const { return M->getASTContext(); } /// Used for static_assert. static constexpr bool declIDFitsIn32Bits() { @@ -644,8 +641,9 @@ serialization::TypeID Serializer::addTypeRef(Type ty) { #ifndef NDEBUG PrettyStackTraceType trace(M->getASTContext(), "serializing", typeToSerialize); - assert(M->getASTContext().LangOpts.AllowModuleWithCompilerErrors || - !typeToSerialize || !typeToSerialize->hasError() && "serializing type with an error"); + assert((allowCompilerErrors() || !typeToSerialize || + !typeToSerialize->hasError()) && + "serializing type with an error"); #endif return TypesToSerialize.addRef(typeToSerialize); @@ -814,6 +812,7 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(options_block, RESILIENCE_STRATEGY); BLOCK_RECORD(options_block, IS_ALLOW_MODULE_WITH_COMPILER_ERRORS_ENABLED); BLOCK_RECORD(options_block, MODULE_ABI_NAME); + BLOCK_RECORD(options_block, IS_CONCURRENCY_CHECKED); BLOCK(INPUT_BLOCK); BLOCK_RECORD(input_block, IMPORTED_MODULE); @@ -906,6 +905,8 @@ void Serializer::writeBlockInfoBlock() { decls_block::SPECIALIZED_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::INHERITED_PROTOCOL_CONFORMANCE); + BLOCK_RECORD_WITH_NAMESPACE(sil_block, + decls_block::BUILTIN_PROTOCOL_CONFORMANCE); BLOCK_RECORD_WITH_NAMESPACE(sil_block, decls_block::NORMAL_PROTOCOL_CONFORMANCE_ID); BLOCK_RECORD_WITH_NAMESPACE(sil_block, @@ -966,11 +967,20 @@ void Serializer::writeHeader(const SerializationOptions &options) { if (auto minor = options.UserModuleVersion.getMinor()) { userModuleMinor = *minor; } + auto userModuleSubminor = 0; + if (auto subMinor = options.UserModuleVersion.getSubminor()) { + userModuleSubminor = *subMinor; + } + auto userModuleBuild = 0; + if (auto build = options.UserModuleVersion.getBuild()) { + userModuleBuild = *build; + } Metadata.emit(ScratchRecord, SWIFTMODULE_VERSION_MAJOR, SWIFTMODULE_VERSION_MINOR, shortVersionStringLength, compatibilityVersionStringLength, userModuleMajor, userModuleMinor, + userModuleSubminor, userModuleBuild, versionString.str()); Target.emit(ScratchRecord, M->getASTContext().LangOpts.Target.str()); @@ -1006,7 +1016,7 @@ void Serializer::writeHeader(const SerializationOptions &options) { Strategy.emit(ScratchRecord, unsigned(M->getResilienceStrategy())); } - if (getASTContext().LangOpts.AllowModuleWithCompilerErrors) { + if (allowCompilerErrors()) { options_block::IsAllowModuleWithCompilerErrorsEnabledLayout AllowErrors(Out); AllowErrors.emit(ScratchRecord); @@ -1017,6 +1027,11 @@ void Serializer::writeHeader(const SerializationOptions &options) { ABIName.emit(ScratchRecord, M->getABIName().str()); } + if (M->isConcurrencyChecked()) { + options_block::IsConcurrencyCheckedLayout IsConcurrencyChecked(Out); + IsConcurrencyChecked.emit(ScratchRecord); + } + if (options.SerializeOptionsForDebugging) { options_block::SDKPathLayout SDKPath(Out); options_block::XCCLayout XCC(Out); @@ -1210,6 +1225,10 @@ void Serializer::writeInputBlock(const SerializationOptions &options) { LinkLibrary.emit(ScratchRecord, serialization::LibraryKind::Library, options.AutolinkForceLoad, options.ModuleLinkName); } + for (auto dependentLib : options.PublicDependentLibraries) { + LinkLibrary.emit(ScratchRecord, serialization::LibraryKind::Library, + options.AutolinkForceLoad, dependentLib); + } } /// Translate AST default argument kind to the Serialization enum values, which @@ -1333,6 +1352,19 @@ void Serializer::writeGenericRequirements(ArrayRef requirements, } } +void Serializer::writeAssociatedTypes(ArrayRef assocTypes, + const std::array &abbrCodes) { + using namespace decls_block; + + auto assocTypeAbbrCode = abbrCodes[AssociatedTypeLayout::Code]; + + for (auto *assocType : assocTypes) { + AssociatedTypeLayout::emitRecord( + Out, ScratchRecord, assocTypeAbbrCode, + addDeclRef(assocType)); + } +} + void Serializer::writeASTBlockEntity(GenericSignature sig) { using namespace decls_block; @@ -1343,7 +1375,7 @@ void Serializer::writeASTBlockEntity(GenericSignature sig) { // have to encode them manually because one of them has a declaration with // module context (which can happen in SIL). bool mustEncodeParamsManually = - llvm::any_of(sig->getGenericParams(), + llvm::any_of(sig.getGenericParams(), [](const GenericTypeParamType *paramTy) { auto *decl = paramTy->getDecl(); return decl && decl->getDeclContext()->isModuleScopeContext(); @@ -1352,7 +1384,7 @@ void Serializer::writeASTBlockEntity(GenericSignature sig) { if (!mustEncodeParamsManually) { // Record the generic parameters. SmallVector rawParamIDs; - for (auto *paramTy : sig->getGenericParams()) { + for (auto *paramTy : sig.getGenericParams()) { rawParamIDs.push_back(addTypeRef(paramTy)); } @@ -1362,7 +1394,7 @@ void Serializer::writeASTBlockEntity(GenericSignature sig) { } else { // Record the generic parameters. SmallVector rawParamIDs; - for (auto *paramTy : sig->getGenericParams()) { + for (auto *paramTy : sig.getGenericParams()) { auto *decl = paramTy->getDecl(); // For a full environment, add the name and canonicalize the param type. @@ -1378,7 +1410,7 @@ void Serializer::writeASTBlockEntity(GenericSignature sig) { rawParamIDs); } - writeGenericRequirements(sig->getRequirements(), DeclTypeAbbrCodes); + writeGenericRequirements(sig.getRequirements(), DeclTypeAbbrCodes); } void Serializer::writeASTBlockEntity(const SubstitutionMap substitutions) { @@ -1429,9 +1461,10 @@ void Serializer::writeASTBlockEntity( const NormalProtocolConformance *conformance) { using namespace decls_block; + PrettyStackTraceConformance trace("serializing", conformance); + // The conformance must be complete, or we can't serialize it. - assert(conformance->isComplete() || - getASTContext().LangOpts.AllowModuleWithCompilerErrors); + assert(conformance->isComplete() || allowCompilerErrors()); assert(NormalConformancesToSerialize.hasRef(conformance)); auto protocol = conformance->getProtocol(); @@ -1450,11 +1483,15 @@ void Serializer::writeASTBlockEntity( }); conformance->forEachValueWitness([&](ValueDecl *req, Witness witness) { + PrettyStackTraceDecl traceValueWitness( + "serializing value witness for requirement", req); + ++numValueWitnesses; data.push_back(addDeclRef(req)); data.push_back(addDeclRef(witness.getDecl())); assert(witness.getDecl() || req->getAttrs().hasAttribute() - || req->getAttrs().isUnavailable(req->getASTContext())); + || req->getAttrs().isUnavailable(req->getASTContext()) + || allowCompilerErrors()); // If there is no witness, we're done. if (!witness.getDecl()) return; @@ -1488,6 +1525,7 @@ void Serializer::writeASTBlockEntity( numTypeWitnesses, numValueWitnesses, numSignatureConformances, + conformance->isUnchecked(), data); // Write requirement signature conformances. @@ -1586,6 +1624,17 @@ Serializer::writeConformance(ProtocolConformanceRef conformanceRef, writeConformance(conf->getInheritedConformance(), abbrCodes, genericEnv); break; } + case ProtocolConformanceKind::Builtin: + auto builtin = cast(conformance); + unsigned abbrCode = abbrCodes[BuiltinProtocolConformanceLayout::Code]; + auto typeID = addTypeRef(builtin->getType()); + auto protocolID = addDeclRef(builtin->getProtocol()); + auto genericSigID = addGenericSignatureRef(builtin->getGenericSignature()); + BuiltinProtocolConformanceLayout::emitRecord( + Out, ScratchRecord, abbrCode, typeID, protocolID, genericSigID, + static_cast(builtin->getBuiltinConformanceKind())); + writeGenericRequirements(builtin->getConditionalRequirements(), abbrCodes); + break; } } @@ -1622,6 +1671,8 @@ static bool shouldSerializeMember(Decl *D) { llvm_unreachable("decl should never be a member"); case DeclKind::MissingMember: + if (D->getASTContext().LangOpts.AllowModuleWithCompilerErrors) + return false; llvm_unreachable("should never need to reserialize a member placeholder"); case DeclKind::IfConfig: @@ -2004,6 +2055,8 @@ getStableSelfAccessKind(swift::SelfAccessKind MM) { # define DECL(KIND, PARENT)\ LLVM_ATTRIBUTE_UNUSED \ static void verifyAttrSerializable(const KIND ## Decl *D) {\ + if (D->Decl::getASTContext().LangOpts.AllowModuleWithCompilerErrors)\ + return;\ for (auto Attr : D->getAttrs()) {\ assert(Attr->canAppearOnDecl(D) && "attribute cannot appear on a " #KIND);\ }\ @@ -2037,8 +2090,10 @@ void Serializer::writePatternBindingInitializer(PatternBindingDecl *binding, StringRef initStr; SmallString<128> scratch; auto varDecl = binding->getAnchoringVarDecl(bindingIndex); + assert((varDecl || allowCompilerErrors()) && + "Serializing PDB without anchoring VarDecl"); if (binding->hasInitStringRepresentation(bindingIndex) && - varDecl->isInitExposedToClients()) { + varDecl && varDecl->isInitExposedToClients()) { initStr = binding->getInitStringRepresentation(bindingIndex, scratch); } @@ -2395,14 +2450,6 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } - case DAK_ActorIndependent: { - auto *theAttr = cast(DA); - auto abbrCode = S.DeclTypeAbbrCodes[ActorIndependentDeclAttrLayout::Code]; - ActorIndependentDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, - abbrCode, (unsigned)theAttr->getKind()); - return; - } - case DAK_Optimize: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[OptimizeDeclAttrLayout::Code]; @@ -2441,6 +2488,7 @@ class Serializer::DeclSerializer : public DeclVisitor { ENCODE_VER_TUPLE(Deprecated, theAttr->Deprecated) ENCODE_VER_TUPLE(Obsoleted, theAttr->Obsoleted) + auto renameDeclID = S.addDeclRef(theAttr->RenameDecl); llvm::SmallString<32> blob; blob.append(theAttr->Message); blob.append(theAttr->Rename); @@ -2455,6 +2503,7 @@ class Serializer::DeclSerializer : public DeclVisitor { LIST_VER_TUPLE_PIECES(Deprecated), LIST_VER_TUPLE_PIECES(Obsoleted), static_cast(theAttr->Platform), + renameDeclID, theAttr->Message.size(), theAttr->Rename.size(), blob); @@ -2633,21 +2682,6 @@ class Serializer::DeclSerializer : public DeclVisitor { origDeclID, paramIndicesVector); return; } - - case DAK_CompletionHandlerAsync: { - auto *attr = cast(DA); - auto abbrCode = - S.DeclTypeAbbrCodes[CompletionHandlerAsyncDeclAttrLayout::Code]; - - assert(attr->AsyncFunctionDecl && - "Serializing unresolved completion handler async function decl"); - auto asyncFuncDeclID = S.addDeclRef(attr->AsyncFunctionDecl); - - CompletionHandlerAsyncDeclAttrLayout::emitRecord( - S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), - attr->CompletionHandlerIndex, asyncFuncDeclID); - return; - } } } @@ -2850,7 +2884,7 @@ class Serializer::DeclSerializer : public DeclVisitor { // Retrieve the type of the pattern. auto getPatternType = [&] { if (!pattern->hasType()) { - if (S.getASTContext().LangOpts.AllowModuleWithCompilerErrors) + if (S.allowCompilerErrors()) return ErrorType::get(S.getASTContext()); llvm_unreachable("all nodes should have types"); } @@ -3053,6 +3087,24 @@ class Serializer::DeclSerializer : public DeclVisitor { /// If this gets referenced, we forgot to handle a decl. void visitDecl(const Decl *) = delete; + /// Add all of the inherited entries to the result vector. + /// + /// \returns the number of entries added. + unsigned addInherited(ArrayRef inheritedEntries, + SmallVectorImpl &result) { + for (const auto &inherited : inheritedEntries) { + assert(!inherited.getType() || !inherited.getType()->hasArchetype()); + TypeID typeRef = S.addTypeRef(inherited.getType()); + + // Encode "unchecked" in the low bit. + typeRef = (typeRef << 1) | (inherited.isUnchecked ? 0x01 : 0x00); + + result.push_back(typeRef); + } + + return inheritedEntries.size(); + } + void visitExtensionDecl(const ExtensionDecl *extension) { using namespace decls_block; @@ -3076,14 +3128,8 @@ class Serializer::DeclSerializer : public DeclVisitor { ConformanceLookupKind::All); SmallVector inheritedAndDependencyTypes; - for (auto inherited : extension->getInherited()) { - if (extension->getASTContext().LangOpts.AllowModuleWithCompilerErrors && - !inherited.getType()) - continue; - assert(!inherited.getType()->hasArchetype()); - inheritedAndDependencyTypes.push_back(S.addTypeRef(inherited.getType())); - } - size_t numInherited = inheritedAndDependencyTypes.size(); + size_t numInherited = addInherited( + extension->getInherited(), inheritedAndDependencyTypes); llvm::SmallSetVector dependencies; collectDependenciesFromType( @@ -3200,29 +3246,21 @@ class Serializer::DeclSerializer : public DeclVisitor { auto contextID = S.addDeclContextRef(op->getDeclContext()); auto nameID = S.addDeclBaseNameRef(op->getName()); auto groupID = S.addDeclRef(op->getPrecedenceGroup()); - SmallVector designatedNominalTypeDeclIDs; - for (auto *decl : op->getDesignatedNominalTypes()) - designatedNominalTypeDeclIDs.push_back(S.addDeclRef(decl)); unsigned abbrCode = S.DeclTypeAbbrCodes[InfixOperatorLayout::Code]; InfixOperatorLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, nameID, - contextID.getOpaqueValue(), groupID, - designatedNominalTypeDeclIDs); + contextID.getOpaqueValue(), groupID); } template void visitUnaryOperatorDecl(const OperatorDecl *op) { auto contextID = S.addDeclContextRef(op->getDeclContext()); - SmallVector designatedNominalTypeDeclIDs; - for (auto *decl : op->getDesignatedNominalTypes()) - designatedNominalTypeDeclIDs.push_back(S.addDeclRef(decl)); unsigned abbrCode = S.DeclTypeAbbrCodes[Layout::Code]; Layout::emitRecord(S.Out, S.ScratchRecord, abbrCode, S.addDeclBaseNameRef(op->getName()), - contextID.getOpaqueValue(), - designatedNominalTypeDeclIDs); + contextID.getOpaqueValue()); } void visitPrefixOperatorDecl(const PrefixOperatorDecl *op) { @@ -3317,10 +3355,8 @@ class Serializer::DeclSerializer : public DeclVisitor { ConformanceLookupKind::All); SmallVector inheritedAndDependencyTypes; - for (auto inherited : theStruct->getInherited()) { - assert(!inherited.getType()->hasArchetype()); - inheritedAndDependencyTypes.push_back(S.addTypeRef(inherited.getType())); - } + unsigned numInherited = addInherited( + theStruct->getInherited(), inheritedAndDependencyTypes); llvm::SmallSetVector dependencyTypes; for (Requirement req : theStruct->getGenericRequirements()) { @@ -3343,7 +3379,7 @@ class Serializer::DeclSerializer : public DeclVisitor { theStruct->getGenericSignature()), rawAccessLevel, conformances.size(), - theStruct->getInherited().size(), + numInherited, inheritedAndDependencyTypes); @@ -3362,10 +3398,8 @@ class Serializer::DeclSerializer : public DeclVisitor { ConformanceLookupKind::All); SmallVector inheritedAndDependencyTypes; - for (auto inherited : theEnum->getInherited()) { - assert(!inherited.getType()->hasArchetype()); - inheritedAndDependencyTypes.push_back(S.addTypeRef(inherited.getType())); - } + unsigned numInherited = addInherited( + theEnum->getInherited(), inheritedAndDependencyTypes); llvm::SmallSetVector dependencyTypes; for (const EnumElementDecl *nextElt : theEnum->getAllElements()) { @@ -3401,7 +3435,7 @@ class Serializer::DeclSerializer : public DeclVisitor { S.addTypeRef(theEnum->getRawType()), rawAccessLevel, conformances.size(), - theEnum->getInherited().size(), + numInherited, inheritedAndDependencyTypes); writeGenericParams(theEnum->getGenericParams()); @@ -3420,10 +3454,8 @@ class Serializer::DeclSerializer : public DeclVisitor { ConformanceLookupKind::NonInherited); SmallVector inheritedAndDependencyTypes; - for (auto inherited : theClass->getInherited()) { - assert(!inherited.getType()->hasArchetype()); - inheritedAndDependencyTypes.push_back(S.addTypeRef(inherited.getType())); - } + unsigned numInherited = addInherited( + theClass->getInherited(), inheritedAndDependencyTypes); llvm::SmallSetVector dependencyTypes; if (theClass->hasSuperclass()) { @@ -3461,7 +3493,7 @@ class Serializer::DeclSerializer : public DeclVisitor { S.addTypeRef(theClass->getSuperclass()), rawAccessLevel, conformances.size(), - theClass->getInherited().size(), + numInherited, inheritedAndDependencyTypes); writeGenericParams(theClass->getGenericParams()); @@ -3478,11 +3510,17 @@ class Serializer::DeclSerializer : public DeclVisitor { SmallVector inheritedAndDependencyTypes; llvm::SmallSetVector dependencyTypes; + unsigned numInherited = addInherited( + proto->getInherited(), inheritedAndDependencyTypes); + + // Separately collect inherited protocol types as dependencies. for (auto element : proto->getInherited()) { - assert(!element.getType()->hasArchetype()); - inheritedAndDependencyTypes.push_back(S.addTypeRef(element.getType())); - if (element.getType()->is()) - dependencyTypes.insert(element.getType()); + auto elementType = element.getType(); + assert(!elementType || !elementType->hasArchetype()); + if (elementType && + (elementType->is() || + elementType->is())) + dependencyTypes.insert(elementType); } for (Requirement req : proto->getRequirementSignature()) { @@ -3507,13 +3545,14 @@ class Serializer::DeclSerializer : public DeclVisitor { const_cast(proto) ->requiresClass(), proto->isObjC(), - proto->existentialTypeSupported(), - rawAccessLevel, proto->getInherited().size(), + rawAccessLevel, numInherited, inheritedAndDependencyTypes); writeGenericParams(proto->getGenericParams()); S.writeGenericRequirements( proto->getRequirementSignature(), S.DeclTypeAbbrCodes); + S.writeAssociatedTypes( + proto->getAssociatedTypeMembers(), S.DeclTypeAbbrCodes); writeMembers(id, proto->getAllMembers(), true); writeDefaultWitnessTable(proto); } @@ -3616,8 +3655,7 @@ class Serializer::DeclSerializer : public DeclVisitor { getRawStableDefaultArgumentKind(argKind), defaultArgumentText); - if (interfaceType->hasError() && - !S.getASTContext().LangOpts.AllowModuleWithCompilerErrors) { + if (interfaceType->hasError() && !S.allowCompilerErrors()) { param->getDeclContext()->printContext(llvm::errs()); interfaceType->dump(llvm::errs()); llvm_unreachable("error in interface type of parameter"); @@ -3969,12 +4007,34 @@ class Serializer::DeclSerializer : public DeclVisitor { } }; +/// When allowing modules with errors there may be cases where there's little +/// point in serializing a declaration and doing so would create a maintenance +/// burden on the deserialization side. Returns \c true if the given declaration +/// should be skipped and \c false otherwise. +static bool canSkipWhenInvalid(const Decl *D) { + // There's no point writing out the deinit when its context is not a class + // as nothing would be able to reference it + if (auto *deinit = dyn_cast(D)) { + if (!isa(D->getDeclContext())) + return true; + } + return false; +} + void Serializer::writeASTBlockEntity(const Decl *D) { using namespace decls_block; PrettyStackTraceDecl trace("serializing", D); assert(DeclsToSerialize.hasRef(D)); + if (D->isInvalid()) { + assert(allowCompilerErrors() && + "cannot create a module with an invalid decl"); + + if (canSkipWhenInvalid(D)) + return; + } + BitOffset initialOffset = Out.GetCurrentBitNo(); SWIFT_DEFER { // This is important enough to leave on in Release builds. @@ -3984,8 +4044,6 @@ void Serializer::writeASTBlockEntity(const Decl *D) { } }; - assert(getASTContext().LangOpts.AllowModuleWithCompilerErrors || - !D->isInvalid() && "cannot create a module with an invalid decl"); if (isDeclXRef(D)) { writeCrossReference(D); return; @@ -4147,7 +4205,7 @@ class Serializer::TypeSerializer : public TypeVisitor { void visitType(const TypeBase *) = delete; void visitErrorType(const ErrorType *ty) { - if (S.getASTContext().LangOpts.AllowModuleWithCompilerErrors) { + if (S.allowCompilerErrors()) { using namespace decls_block; unsigned abbrCode = S.DeclTypeAbbrCodes[ErrorTypeLayout::Code]; ErrorTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, @@ -4158,6 +4216,13 @@ class Serializer::TypeSerializer : public TypeVisitor { } void visitUnresolvedType(const UnresolvedType *) { + // If for some reason we have an unresolved type while compiling with + // errors, just serialize an ErrorType and continue. + if (S.getASTContext().LangOpts.AllowModuleWithCompilerErrors) { + visitErrorType( + cast(ErrorType::get(S.getASTContext()).getPointer())); + return; + } llvm_unreachable("should not serialize an UnresolvedType"); } @@ -4364,7 +4429,7 @@ class Serializer::TypeSerializer : public TypeVisitor { S.addDeclBaseNameRef(param.getInternalLabel()), S.addTypeRef(param.getPlainType()), paramFlags.isVariadic(), paramFlags.isAutoClosure(), paramFlags.isNonEphemeral(), rawOwnership, - paramFlags.isNoDerivative()); + paramFlags.isIsolated(), paramFlags.isNoDerivative()); } } @@ -4505,6 +4570,11 @@ class Serializer::TypeSerializer : public TypeVisitor { S.addTypeRef(dictTy->getValueType())); } + void visitVariadicSequenceType(const VariadicSequenceType *seqTy) { + using namespace decls_block; + serializeSimpleWrapper(seqTy->getBaseType()); + } + void visitOptionalType(const OptionalType *optionalTy) { using namespace decls_block; serializeSimpleWrapper(optionalTy->getBaseType()); @@ -4708,6 +4778,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); @@ -4730,6 +4801,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); @@ -4780,6 +4852,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); @@ -5243,8 +5316,7 @@ static void collectInterestingNestedDeclarations( if (!nominalParent) { const DeclContext *DC = member->getDeclContext(); nominalParent = DC->getSelfNominalTypeDecl(); - assert(nominalParent || - nestedType->getASTContext().LangOpts.AllowModuleWithCompilerErrors && + assert((nominalParent || S.allowCompilerErrors()) && "parent context is not a type or extension"); } nestedTypeDecls[nestedType->getName()].push_back({ @@ -5530,6 +5602,10 @@ void Serializer::writeToStream( S.writeToStream(os); } +bool Serializer::allowCompilerErrors() const { + return getASTContext().LangOpts.AllowModuleWithCompilerErrors; +} + void swift::serializeToBuffers( ModuleOrSourceFile DC, const SerializationOptions &options, std::unique_ptr *moduleBuffer, @@ -5652,6 +5728,7 @@ void swift::serialize(ModuleOrSourceFile DC, /*EmitSynthesizedMembers*/true, /*PrintMessages*/false, /*EmitInheritedDocs*/options.SkipSymbolGraphInheritedDocs, + /*IncludeSPISymbols*/options.IncludeSPISymbolsInSymbolGraph, }; symbolgraphgen::emitSymbolGraphForModule(M, SGOpts); } diff --git a/lib/Serialization/Serialization.h b/lib/Serialization/Serialization.h index 6608d3fa740b9..58f53b6e6f117 100644 --- a/lib/Serialization/Serialization.h +++ b/lib/Serialization/Serialization.h @@ -72,7 +72,7 @@ class SerializerBase { public: SerializerBase(ArrayRef signature, ModuleOrSourceFile DC); - ASTContext &getASTContext(); + ASTContext &getASTContext() const; }; class Serializer : public SerializerBase { @@ -538,6 +538,12 @@ class Serializer : public SerializerBase { /// Writes a set of generic requirements. void writeGenericRequirements(ArrayRef requirements, const std::array &abbrCodes); + + /// Writes a protocol's associated type table. + void writeAssociatedTypes(ArrayRef assocTypes, + const std::array &abbrCodes); + + bool allowCompilerErrors() const; }; /// Serialize module documentation to the given stream. diff --git a/lib/Serialization/SerializeDoc.cpp b/lib/Serialization/SerializeDoc.cpp index 0502b263f5df9..233fdd93bba90 100644 --- a/lib/Serialization/SerializeDoc.cpp +++ b/lib/Serialization/SerializeDoc.cpp @@ -341,8 +341,8 @@ static bool shouldIncludeDecl(Decl *D, bool ExcludeDoubleUnderscore) { return false; } - // Skip SPI decls. - if (D->isSPI()) + // Skip SPI decls, unless we're generating a symbol graph with SPI information. + if (D->isSPI() && !D->getASTContext().SymbolGraphOpts.IncludeSPISymbols) return false; if (auto *ED = dyn_cast(D)) { @@ -493,7 +493,10 @@ void DocSerializer::writeDocHeader() { Metadata.emit(ScratchRecord, SWIFTDOC_VERSION_MAJOR, SWIFTDOC_VERSION_MINOR, /*short version string length*/0, /*compatibility length*/0, /*user module version major*/0, - /*user module version minor*/0, verText); + /*user module version minor*/0, + /*user module version subminor*/0, + /*user module version build*/0, + verText); ModuleName.emit(ScratchRecord, M->getName().str()); Target.emit(ScratchRecord, LangOpts.Target.str()); @@ -878,7 +881,10 @@ class SourceInfoSerializer : public SerializerBase { SWIFTSOURCEINFO_VERSION_MINOR, /*short version string length*/0, /*compatibility length*/0, /*user module version major*/0, - /*user module version minor*/0, verText); + /*user module version minor*/0, + /*user module version sub-minor*/0, + /*user module version build*/0, + verText); ModuleName.emit(ScratchRecord, M->getName().str()); Target.emit(ScratchRecord, LangOpts.Target.str()); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 767db7a97cfa5..c0f049ffb21a5 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -108,6 +108,7 @@ toStableDifferentiabilityKind(swift::DifferentiabilityKind kind) { case swift::DifferentiabilityKind::Linear: return (unsigned)serialization::DifferentiabilityKind::Linear; } + llvm_unreachable("covered switch"); } namespace { @@ -801,12 +802,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { } case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: // Currently we don't serialize debug variable infos, so it doesn't make // sense to write the instruction at all. // TODO: decide if we want to serialize those instructions. return; - + case SILInstructionKind::UnwindInst: case SILInstructionKind::UnreachableInst: { writeNoOperandLayout(&SI); @@ -1403,12 +1403,16 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { Attr = unsigned(SILValue(UOCI).getOwnershipKind()); } else if (auto *IEC = dyn_cast(&SI)) { Attr = IEC->getVerificationType(); + } else if (auto *HTE = dyn_cast(&SI)) { + Attr = HTE->isMandatory(); } else if (auto *DVI = dyn_cast(&SI)) { Attr = DVI->poisonRefs(); } else if (auto *BCMI = dyn_cast(&SI)) { Attr = BCMI->isNative(); } else if (auto *ECMI = dyn_cast(&SI)) { Attr = ECMI->doKeepUnique(); + } else if (auto *BBI = dyn_cast(&SI)) { + Attr = BBI->isDefined(); } writeOneOperandLayout(SI.getKind(), Attr, SI.getOperand(0)); break; @@ -2235,10 +2239,10 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { ArrayRef reqts; if (auto sig = pattern->getGenericSignature()) { - ListOfValues.push_back(sig->getGenericParams().size()); - for (auto param : sig->getGenericParams()) + ListOfValues.push_back(sig.getGenericParams().size()); + for (auto param : sig.getGenericParams()) ListOfValues.push_back(S.addTypeRef(param)); - reqts = sig->getRequirements(); + reqts = sig.getRequirements(); } else { ListOfValues.push_back(0); } @@ -2775,6 +2779,7 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); + registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index bc94b8a6dd885..5ba5de6eeebfc 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -22,7 +22,10 @@ #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Version.h" +#include "swift/Option/Options.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/ArgList.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Debug.h" @@ -85,18 +88,10 @@ Optional forEachModuleSearchPath( // Apple platforms have extra implicit framework search paths: // $SDKROOT/System/Library/Frameworks/ and $SDKROOT/Library/Frameworks/. if (Ctx.LangOpts.Target.isOSDarwin()) { - SmallString<128> scratch; - scratch = Ctx.SearchPathOpts.SDKPath; - llvm::sys::path::append(scratch, "System", "Library", "Frameworks"); - if (auto result = - callback(scratch, SearchPathKind::Framework, /*isSystem=*/true)) - return result; - - scratch = Ctx.SearchPathOpts.SDKPath; - llvm::sys::path::append(scratch, "Library", "Frameworks"); - if (auto result = - callback(scratch, SearchPathKind::Framework, /*isSystem=*/true)) - return result; + for (const auto &path : Ctx.getDarwinImplicitFrameworkSearchPaths()) + if (auto result = + callback(path, SearchPathKind::Framework, /*isSystem=*/true)) + return result; } for (auto importPath : Ctx.SearchPathOpts.RuntimeLibraryImportPaths) { @@ -733,6 +728,8 @@ LoadedFile *SerializedModuleLoaderBase::loadAST( M.setHasIncrementalInfo(); if (!loadedModuleFile->getModuleABIName().empty()) M.setABIName(Ctx.getIdentifier(loadedModuleFile->getModuleABIName())); + if (loadedModuleFile->isConcurrencyChecked()) + M.setIsConcurrencyChecked(); M.setUserModuleVersion(loadedModuleFile->getUserModuleVersion()); auto diagLocOrInvalid = diagLoc.getValueOr(SourceLoc()); loadInfo.status = loadedModuleFile->associateWithFileContext( @@ -961,7 +958,8 @@ void swift::serialization::diagnoseSerializedASTLoadFailure( } } -bool swift::extractCompilerFlagsFromInterface(StringRef buffer, +bool swift::extractCompilerFlagsFromInterface(StringRef interfacePath, + StringRef buffer, llvm::StringSaver &ArgSaver, SmallVectorImpl &SubArgs) { SmallVector FlagMatches; @@ -970,9 +968,81 @@ bool swift::extractCompilerFlagsFromInterface(StringRef buffer, return true; assert(FlagMatches.size() == 2); llvm::cl::TokenizeGNUCommandLine(FlagMatches[1], ArgSaver, SubArgs); + + auto intFileName = llvm::sys::path::filename(interfacePath); + + // Sanitize arch if the file name and the encoded flags disagree. + // It's a known issue that we are using arm64e interfaces contents for the arm64 target, + // meaning the encoded module flags are using -target arm64e-x-x. Fortunately, + // we can tell the target arch from the interface file name, so we could sanitize + // the target to use by inferring target from the file name. + StringRef arm64 = "arm64"; + StringRef arm64e = "arm64e"; + if (intFileName.contains(arm64) && !intFileName.contains(arm64e)) { + for (unsigned I = 1; I < SubArgs.size(); ++I) { + if (strcmp(SubArgs[I - 1], "-target") != 0) { + continue; + } + StringRef triple(SubArgs[I]); + if (triple.startswith(arm64e)) { + SubArgs[I] = ArgSaver.save((llvm::Twine(arm64) + + triple.substr(arm64e.size())).str()).data(); + } + } + } + + SmallVector IgnFlagMatches; + // Cherry-pick supported options from the ignorable list. + auto IgnFlagRe = llvm::Regex("^// swift-module-flags-ignorable:(.*)$", + llvm::Regex::Newline); + // It's OK the interface doesn't have the ignorable list, we just ignore them + // all. + if (!IgnFlagRe.match(buffer, &IgnFlagMatches)) + return false; + SmallVector IgnSubArgs; + llvm::cl::TokenizeGNUCommandLine(IgnFlagMatches[1], ArgSaver, IgnSubArgs); + std::unique_ptr table = swift::createSwiftOptTable(); + unsigned missingArgIdx = 0; + unsigned missingArgCount = 0; + auto parsedIgns = table->ParseArgs(IgnSubArgs, missingArgIdx, missingArgCount); + for (auto parse: parsedIgns) { + // Check if the option is a frontend option. This will filter out unknown + // options and input-like options. + if (!parse->getOption().hasFlag(options::FrontendOption)) + continue; + // Push the supported option and its value to the list. + // We shouldn't need to worry about cases like -tbd-install_name=Foo because + // the parsing function should have droped alias options already. + SubArgs.push_back(ArgSaver.save(parse->getSpelling()).data()); + for (auto value: parse->getValues()) + SubArgs.push_back(value); + } + return false; } +llvm::VersionTuple +swift::extractUserModuleVersionFromInterface(StringRef moduleInterfacePath) { + llvm::VersionTuple result; + // Read the inteface file and extract its compiler arguments line + if (auto file = llvm::MemoryBuffer::getFile(moduleInterfacePath)) { + llvm::BumpPtrAllocator alloc; + llvm::StringSaver argSaver(alloc); + SmallVector args; + (void)extractCompilerFlagsFromInterface(moduleInterfacePath, + (*file)->getBuffer(), argSaver, args); + for (unsigned I = 0, N = args.size(); I + 1 < N; I++) { + // Check the version number specified via -user-module-version. + StringRef current(args[I]), next(args[I + 1]); + if (current == "-user-module-version") { + result.tryParse(next); + break; + } + } + } + return result; +} + bool SerializedModuleLoaderBase::canImportModule( ImportPath::Element mID, llvm::VersionTuple version, bool underlyingVersion) { // If underlying version is specified, this should be handled by Clang importer. @@ -1009,21 +1079,7 @@ bool SerializedModuleLoaderBase::canImportModule( assert(!underlyingVersion); llvm::VersionTuple currentVersion; if (!moduleInterfacePath.empty()) { - // Read the inteface file and extract its compiler arguments line - if (auto file = llvm::MemoryBuffer::getFile(moduleInterfacePath)) { - llvm::BumpPtrAllocator alloc; - llvm::StringSaver argSaver(alloc); - SmallVector args; - (void)extractCompilerFlagsFromInterface((*file)->getBuffer(), argSaver, args); - for (unsigned I = 0, N = args.size(); I + 1 < N; I++) { - // Check the version number specified via -user-module-version. - StringRef current(args[I]), next(args[I + 1]); - if (current == "-user-module-version") { - currentVersion.tryParse(next); - break; - } - } - } + currentVersion = extractUserModuleVersionFromInterface(moduleInterfacePath); } // If failing to extract the user version from the interface file, try the binary // format, if present. diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp index c7f9707b0216c..76ac521058bcd 100644 --- a/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp @@ -96,6 +96,7 @@ void DeclarationFragmentPrinter::printNamePre(PrintNameContext Context) { switch (Context) { case PrintNameContext::Keyword: + case PrintNameContext::IntroducerKeyword: openFragment(FragmentKind::Keyword); break; case PrintNameContext::GenericParameter: diff --git a/lib/SymbolGraphGen/Edge.cpp b/lib/SymbolGraphGen/Edge.cpp index e4080e634f921..a133d1c02f941 100644 --- a/lib/SymbolGraphGen/Edge.cpp +++ b/lib/SymbolGraphGen/Edge.cpp @@ -15,9 +15,38 @@ #include "Symbol.h" #include "SymbolGraphASTWalker.h" +#include + using namespace swift; using namespace symbolgraphgen; +namespace { +const ValueDecl *getForeignProtocolRequirement(const ValueDecl *VD, const ModuleDecl *M) { + std::queue requirements; + while (true) { + for (auto *req : VD->getSatisfiedProtocolRequirements()) { + if (req->getModuleContext()->getNameStr() != M->getNameStr()) + return req; + else + requirements.push(req); + } + if (requirements.empty()) + return nullptr; + VD = requirements.front(); + requirements.pop(); + } +} + +const ValueDecl *getProtocolRequirement(const ValueDecl *VD) { + auto reqs = VD->getSatisfiedProtocolRequirements(); + + if (!reqs.empty()) + return reqs.front(); + else + return nullptr; +} +} // end anonymous namespace + void Edge::serialize(llvm::json::OStream &OS) const { OS.object([&](){ OS.attribute("kind", Kind.Name); @@ -59,11 +88,22 @@ void Edge::serialize(llvm::json::OStream &OS) const { } const ValueDecl *InheritingDecl = nullptr; - if (const auto *ID = Source.getDeclInheritingDocs()) { - if (Target.getSymbolDecl() == ID || Source.getSynthesizedBaseTypeDecl()) + if (const auto *ID = Source.getDeclInheritingDocs()) + InheritingDecl = ID; + + if (!InheritingDecl && Source.getSynthesizedBaseTypeDecl()) + InheritingDecl = Source.getSymbolDecl(); + + if (!InheritingDecl) { + if (const auto *ID = getForeignProtocolRequirement(Source.getSymbolDecl(), &Graph->M)) InheritingDecl = ID; } - + + if (!InheritingDecl) { + if (const auto *ID = getProtocolRequirement(Source.getSymbolDecl())) + InheritingDecl = ID; + } + // If our source symbol is a inheriting decl, write in information about // where it's inheriting docs from. if (InheritingDecl) { diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index db8cb19979e06..62fda7ebe6d35 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -318,7 +318,7 @@ void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const { SmallVector FilteredParams; SmallVector FilteredRequirements; - filterGenericParams(Generics->getGenericParams(), FilteredParams, + filterGenericParams(Generics.getGenericParams(), FilteredParams, SubMap); const auto *Self = dyn_cast(VD); @@ -326,7 +326,7 @@ void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const { Self = VD->getDeclContext()->getSelfNominalTypeDecl(); } - filterGenericRequirements(Generics->getRequirements(), Self, + filterGenericRequirements(Generics.getRequirements(), Self, FilteredRequirements, SubMap, FilteredParams); if (FilteredParams.empty() && FilteredRequirements.empty()) { @@ -471,6 +471,11 @@ void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { }); } +void Symbol::serializeSPIMixin(llvm::json::OStream &OS) const { + if (VD->isSPI()) + OS.attribute("spi", true); +} + void Symbol::serialize(llvm::json::OStream &OS) const { OS.object([&](){ serializeKind(OS); @@ -487,6 +492,7 @@ void Symbol::serialize(llvm::json::OStream &OS) const { serializeAccessLevelMixin(OS); serializeAvailabilityMixin(OS); serializeLocationMixin(OS); + serializeSPIMixin(OS); }); } diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h index 25738c0b7caf2..64585288f6b19 100644 --- a/lib/SymbolGraphGen/Symbol.h +++ b/lib/SymbolGraphGen/Symbol.h @@ -76,6 +76,8 @@ class Symbol { void serializeAvailabilityMixin(llvm::json::OStream &OS) const; + void serializeSPIMixin(llvm::json::OStream &OS) const; + public: Symbol(SymbolGraph *Graph, const ValueDecl *VD, const NominalTypeDecl *SynthesizedBaseTypeDecl, diff --git a/lib/SymbolGraphGen/SymbolGraph.cpp b/lib/SymbolGraphGen/SymbolGraph.cpp index a6457a0055dab..68f34b260e14b 100644 --- a/lib/SymbolGraphGen/SymbolGraph.cpp +++ b/lib/SymbolGraphGen/SymbolGraph.cpp @@ -196,7 +196,6 @@ void SymbolGraph::recordNode(Symbol S) { // with this declaration. recordMemberRelationship(S); recordConformanceSynthesizedMemberRelationships(S); - recordSuperclassSynthesizedMemberRelationships(S); recordConformanceRelationships(S); recordInheritanceRelationships(S); recordDefaultImplementationRelationships(S); @@ -259,45 +258,6 @@ void SymbolGraph::recordMemberRelationship(Symbol S) { } } -void SymbolGraph::recordSuperclassSynthesizedMemberRelationships(Symbol S) { - if (!Walker.Options.EmitSynthesizedMembers) { - return; - } - // Via class inheritance... - if (const auto *C = dyn_cast(S.getSymbolDecl())) { - // Collect all superclass members up the inheritance chain. - SmallPtrSet SuperClassMembers; - const auto *Super = C->getSuperclassDecl(); - while (Super) { - for (const auto *SuperMember : Super->getMembers()) { - if (const auto *SuperMemberVD = dyn_cast(SuperMember)) { - SuperClassMembers.insert(SuperMemberVD); - } - } - Super = Super->getSuperclassDecl(); - } - // Remove any that are overridden by this class. - for (const auto *DerivedMember : C->getMembers()) { - if (const auto *DerivedMemberVD = dyn_cast(DerivedMember)) { - if (const auto *Overridden = DerivedMemberVD->getOverriddenDecl()) { - SuperClassMembers.erase(Overridden); - } - } - } - // What remains in SuperClassMembers are inherited members that - // haven't been overridden by the class. - // Add a synthesized relationship. - for (const auto *InheritedMember : SuperClassMembers) { - if (canIncludeDeclAsNode(InheritedMember)) { - Symbol Source(this, InheritedMember, C); - Symbol Target(this, C, nullptr); - Nodes.insert(Source); - recordEdge(Source, Target, RelationshipKind::MemberOf()); - } - } - } -} - bool SymbolGraph::synthesizedMemberIsBestCandidate(const ValueDecl *VD, const NominalTypeDecl *Owner) const { const auto *FD = dyn_cast(VD); @@ -430,6 +390,17 @@ void SymbolGraph::recordDefaultImplementationRelationships(Symbol S) { recordEdge(Symbol(this, VD, nullptr), Symbol(this, MemberVD, nullptr), RelationshipKind::DefaultImplementationOf()); + + // If P is from a different module, and it's being added to a type + // from the current module, add a `memberOf` relation to the extended + // protocol. + if (MemberVD->getModuleContext()->getNameStr() != M.getNameStr() && VD->getDeclContext()) { + if (auto *ExP = VD->getDeclContext()->getSelfNominalTypeDecl()) { + recordEdge(Symbol(this, VD, nullptr), + Symbol(this, ExP, nullptr), + RelationshipKind::MemberOf()); + } + } } } } @@ -619,7 +590,7 @@ bool SymbolGraph::isImplicitlyPrivate(const Decl *D, // Don't include declarations with the @_spi attribute unless the // access control filter is internal or below. - if (D->isSPI()) { + if (D->isSPI() && !Walker.Options.IncludeSPISymbols) { return Walker.Options.MinimumAccessLevel > AccessLevel::Internal; } diff --git a/lib/SymbolGraphGen/SymbolGraph.h b/lib/SymbolGraphGen/SymbolGraph.h index 157ede4cf6e3c..a06c0268a5488 100644 --- a/lib/SymbolGraphGen/SymbolGraph.h +++ b/lib/SymbolGraphGen/SymbolGraph.h @@ -145,15 +145,6 @@ struct SymbolGraph { */ void recordConformanceSynthesizedMemberRelationships(Symbol S); - /** - If a declaration has members by subclassing, record a symbol with a - "synthesized" USR to disambiguate from the superclass's real implementation. - - This makes it more convenient - to curate symbols on a subclass's documentation. - */ - void recordSuperclassSynthesizedMemberRelationships(Symbol S); - /** Record InheritsFrom relationships for every class from which the declaration inherits. diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp index 5b868eb2e9218..2cffb22ebc91a 100644 --- a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp @@ -45,6 +45,11 @@ SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(const Decl *D) { if (this->M.getNameStr().equals(M->getNameStr())) { return &MainGraph; + } else if (MainGraph.DeclaringModule.hasValue() && + MainGraph.DeclaringModule.getValue()->getNameStr().equals(M->getNameStr())) { + // Cross-import overlay modules already appear as "extensions" of their declaring module; we + // should put actual extensions of that module into the main graph + return &MainGraph; } auto Found = ExtendedModuleGraphs.find(M->getNameStr()); if (Found != ExtendedModuleGraphs.end()) { diff --git a/lib/SymbolGraphGen/SymbolGraphGen.cpp b/lib/SymbolGraphGen/SymbolGraphGen.cpp index 1c56f30a20ec9..eceac686154c4 100644 --- a/lib/SymbolGraphGen/SymbolGraphGen.cpp +++ b/lib/SymbolGraphGen/SymbolGraphGen.cpp @@ -25,26 +25,14 @@ namespace { int serializeSymbolGraph(SymbolGraph &SG, const SymbolGraphOptions &Options) { SmallString<256> FileName; - if (SG.DeclaringModule.hasValue()) { - // Save a cross-import overlay symbol graph as `MainModule@BystandingModule[@BystandingModule...]@OverlayModule.symbols.json` - // - // The overlay module's name is added as a disambiguator in case an overlay - // declares multiple modules for the same set of imports. - FileName.append(SG.DeclaringModule.getValue()->getNameStr()); - for (auto BystanderModule : SG.BystanderModules) { - FileName.push_back('@'); - FileName.append(BystanderModule.str()); - } - + FileName.append(SG.M.getNameStr()); + if (SG.ExtendedModule.hasValue()) { FileName.push_back('@'); - FileName.append(SG.M.getNameStr()); - } else { - FileName.append(SG.M.getNameStr()); - - if (SG.ExtendedModule.hasValue()) { - FileName.push_back('@'); - FileName.append(SG.ExtendedModule.getValue()->getNameStr()); - } + FileName.append(SG.ExtendedModule.getValue()->getNameStr()); + } else if (SG.DeclaringModule.hasValue()) { + // Treat cross-import overlay modules as "extensions" of their declaring module + FileName.push_back('@'); + FileName.append(SG.DeclaringModule.getValue()->getNameStr()); } FileName.append(".symbols.json"); diff --git a/lib/SyntaxParse/CMakeLists.txt b/lib/SyntaxParse/CMakeLists.txt index 877f84f73431b..dec2fa7a789c6 100644 --- a/lib/SyntaxParse/CMakeLists.txt +++ b/lib/SyntaxParse/CMakeLists.txt @@ -1,6 +1,8 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftSyntaxParse STATIC SyntaxTreeCreator.cpp) target_link_libraries(swiftSyntaxParse PRIVATE swiftParse swiftSyntax) + +set_swift_llvm_is_available(swiftSyntaxParse) diff --git a/lib/TBDGen/CMakeLists.txt b/lib/TBDGen/CMakeLists.txt index 46135190a1ac8..115ff925f070e 100644 --- a/lib/TBDGen/CMakeLists.txt +++ b/lib/TBDGen/CMakeLists.txt @@ -1,4 +1,4 @@ -set_swift_llvm_is_available() + add_swift_host_library(swiftTBDGen STATIC APIGen.cpp TBDGen.cpp @@ -11,3 +11,5 @@ target_link_libraries(swiftTBDGen PRIVATE swiftAST swiftIRGen swiftSIL) + +set_swift_llvm_is_available(swiftTBDGen) diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 3c336f7ebff44..26bcde167d967 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -125,16 +125,6 @@ StringRef InstallNameStore::getInstallName(LinkerPlatformId Id) const { return It->second; } -void InstallNameStore::remark(ASTContext &Ctx, StringRef ModuleName) const { - Ctx.Diags.diagnose(SourceLoc(), diag::default_previous_install_name, - ModuleName, InstallName); - for (auto Pair: PlatformInstallName) { - Ctx.Diags.diagnose(SourceLoc(), diag::platform_previous_install_name, - ModuleName, getLinkerPlatformName(Pair.first), - Pair.second); - } -} - static std::string getScalaNodeText(Node *N) { SmallString<32> Buffer; return cast(N)->getValue(Buffer).str(); @@ -214,11 +204,6 @@ TBDGenVisitor::parsePreviousModuleInstallNameMap() { std::unique_ptr> pResult( new std::map()); auto &AllInstallNames = *pResult; - SWIFT_DEFER { - for (auto Pair: AllInstallNames) { - Pair.second.remark(Ctx, Pair.first); - } - }; // Load the input file. llvm::ErrorOr> FileBufOrErr = @@ -426,15 +411,14 @@ void TBDGenVisitor::addSymbol(SILDeclRef declRef) { addSymbol(declRef.mangle(), SymbolSource::forSILDeclRef(declRef)); } -void TBDGenVisitor::addAsyncFunctionPointerSymbol(AbstractFunctionDecl *AFD) { - auto declRef = SILDeclRef(AFD); +void TBDGenVisitor::addAsyncFunctionPointerSymbol(SILDeclRef declRef) { auto silLinkage = effectiveLinkageForClassMember( declRef.getLinkage(ForDefinition), declRef.getSubclassScope()); if (Opts.PublicSymbolsOnly && silLinkage != SILLinkage::Public) return; - auto entity = LinkEntity::forAsyncFunctionPointer(AFD); + auto entity = LinkEntity::forAsyncFunctionPointer(declRef); auto linkage = LinkInfo::get(UniversalLinkInfo, SwiftModule, entity, ForDefinition); addSymbol(linkage.getName(), SymbolSource::forSILDeclRef(declRef)); @@ -732,6 +716,10 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { addSymbol(SILDeclRef(AFD).asForeign()); } + if (AFD->isDistributed()) { + addSymbol(SILDeclRef(AFD).asDistributed()); + } + // Add derivative function symbols. for (const auto *differentiableAttr : AFD->getAttrs().getAttributes()) @@ -753,7 +741,7 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { visitDefaultArguments(AFD, AFD->getParameters()); if (AFD->hasAsync()) { - addAsyncFunctionPointerSymbol(AFD); + addAsyncFunctionPointerSymbol(SILDeclRef(AFD)); } } @@ -1000,6 +988,10 @@ void TBDGenVisitor::visitConstructorDecl(ConstructorDecl *CD) { // default ValueDecl handling gives the allocating one, so we have to // manually include the non-allocating one. addSymbol(SILDeclRef(CD, SILDeclRef::Kind::Initializer)); + if (CD->hasAsync()) { + addAsyncFunctionPointerSymbol( + SILDeclRef(CD, SILDeclRef::Kind::Initializer)); + } if (auto parentClass = CD->getParent()->getSelfClassDecl()) { if (parentClass->isObjC() || CD->isObjC()) recorder.addObjCMethod(parentClass, SILDeclRef(CD)); @@ -1152,6 +1144,29 @@ void TBDGenVisitor::addFirstFileSymbols() { } } +void TBDGenVisitor::addMainIfNecessary(FileUnit *file) { + // HACK: 'main' is a special symbol that's always emitted in SILGen if + // the file has an entry point. Since it doesn't show up in the + // module until SILGen, we need to explicitly add it here. + // + // Make sure to only add the main symbol for the module that we're emitting + // TBD for, and not for any statically linked libraries. + if (!file->hasEntryPoint() || file->getParentModule() != SwiftModule) + return; + + auto entryPointSymbol = + SwiftModule->getASTContext().getEntryPointFunctionName(); + + if (auto *decl = file->getMainDecl()) { + auto ref = SILDeclRef::getMainDeclEntryPoint(decl); + addSymbol(entryPointSymbol, SymbolSource::forSILDeclRef(ref)); + return; + } + + auto ref = SILDeclRef::getMainFileEntryPoint(file); + addSymbol(entryPointSymbol, SymbolSource::forSILDeclRef(ref)); +} + void TBDGenVisitor::visit(Decl *D) { DeclStack.push_back(D); SWIFT_DEFER { DeclStack.pop_back(); }; @@ -1163,9 +1178,6 @@ static bool hasLinkerDirective(Decl *D) { } void TBDGenVisitor::visitFile(FileUnit *file) { - if (file == SwiftModule->getFiles()[0]) - addFirstFileSymbols(); - SmallVector decls; file->getTopLevelDecls(decls); @@ -1179,6 +1191,9 @@ void TBDGenVisitor::visitFile(FileUnit *file) { } void TBDGenVisitor::visit(const TBDGenDescriptor &desc) { + // Add any autolinking force_load symbols. + addFirstFileSymbols(); + if (auto *singleFile = desc.getSingleFile()) { assert(SwiftModule == singleFile->getParentModule() && "mismatched file and module"); @@ -1207,6 +1222,7 @@ void TBDGenVisitor::visit(const TBDGenDescriptor &desc) { // Diagnose module name that cannot be found ctx.Diags.diagnose(SourceLoc(), diag::unknown_swift_module_name, Name); } + // Collect symbols in each module. llvm::for_each(Modules, [&](ModuleDecl *M) { for (auto *file : M->getFiles()) { @@ -1348,8 +1364,8 @@ class APIGenRecorder final : public APIRecorder { apigen::APIAvailability availability; if (source.kind == SymbolSource::Kind::SIL) { auto ref = source.getSILDeclRef(); - if (auto *decl = ref.getDecl()) - availability = getAvailability(decl); + if (ref.hasDecl()) + availability = getAvailability(ref.getDecl()); } api.addSymbol(symbol, moduleLoc, apigen::APILinkage::Exported, diff --git a/lib/TBDGen/TBDGenVisitor.h b/lib/TBDGen/TBDGenVisitor.h index a71573aa3eeb8..75f79f1ee8ad8 100644 --- a/lib/TBDGen/TBDGenVisitor.h +++ b/lib/TBDGen/TBDGenVisitor.h @@ -58,7 +58,6 @@ struct InstallNameStore { // the default install name. std::map PlatformInstallName; StringRef getInstallName(LinkerPlatformId Id) const; - void remark(ASTContext &Ctx, StringRef ModuleName) const; }; /// A set of callbacks for recording APIs. @@ -122,7 +121,7 @@ class TBDGenVisitor : public ASTVisitor { SymbolKind kind = SymbolKind::GlobalSymbol); void addSymbol(SILDeclRef declRef); - void addAsyncFunctionPointerSymbol(AbstractFunctionDecl *AFD); + void addAsyncFunctionPointerSymbol(SILDeclRef declRef); void addSymbol(LinkEntity entity); @@ -181,18 +180,9 @@ class TBDGenVisitor : public ASTVisitor { TBDGenVisitor(const TBDGenDescriptor &desc, APIRecorder &recorder); ~TBDGenVisitor() { assert(DeclStack.empty()); } - void addMainIfNecessary(FileUnit *file) { - // HACK: 'main' is a special symbol that's always emitted in SILGen if - // the file has an entry point. Since it doesn't show up in the - // module until SILGen, we need to explicitly add it here. - // - // Make sure to only add the main symbol for the module that we're emitting - // TBD for, and not for any statically linked libraries. - // FIXME: We should have a SymbolSource for main. - if (file->hasEntryPoint() && file->getParentModule() == SwiftModule) - addSymbol(SwiftModule->getASTContext().getEntryPointFunctionName(), - SymbolSource::forUnknown()); - } + + /// Add the main symbol. + void addMainIfNecessary(FileUnit *file); /// Adds the global symbols associated with the first file. void addFirstFileSymbols(); diff --git a/libswift/CMakeLists.txt b/libswift/CMakeLists.txt new file mode 100644 index 0000000000000..70469a5de67ad --- /dev/null +++ b/libswift/CMakeLists.txt @@ -0,0 +1,23 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +if (NOT SWIFT_TOOLS_ENABLE_LIBSWIFT) + # A dummy libswift if libswift is disabled + add_swift_host_library(libswift OBJECT LibSwiftStubs.cpp) +else() + project(LibSwift LANGUAGES C CXX Swift) + + if (NOT CMAKE_Swift_COMPILER) + message(FATAL_ERROR "Need a swift toolchain for building libswift") + endif() + + add_subdirectory(Sources) + + add_libswift("libswift") +endif() + diff --git a/libswift/LibSwiftStubs.cpp b/libswift/LibSwiftStubs.cpp new file mode 100644 index 0000000000000..ffce6e63cc583 --- /dev/null +++ b/libswift/LibSwiftStubs.cpp @@ -0,0 +1,20 @@ +//===--- LibSwiftStubs.cpp ------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +extern "C" { + +void initializeLibSwift(); + +} + +void initializeLibSwift() {} + diff --git a/libswift/Package.swift b/libswift/Package.swift new file mode 100644 index 0000000000000..6409e36f8beb6 --- /dev/null +++ b/libswift/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:5.3 + +import PackageDescription + +let package = Package( + name: "libswift", + platforms: [ + .macOS("10.9"), + ], + products: [ + .library( + name: "Swift", + type: .static, + targets: ["SIL", "Optimizer"]), + ], + dependencies: [ + ], + // Note that all modules must be added to LIBSWIFT_MODULES in the top-level + // CMakeLists.txt file to get debugging working. + targets: [ + .target( + name: "SIL", + dependencies: [], + swiftSettings: [SwiftSetting.unsafeFlags([ + "-I", "../include/swift", + "-cross-module-optimization" + ])]), + .target( + name: "Optimizer", + dependencies: ["SIL"], + swiftSettings: [SwiftSetting.unsafeFlags([ + "-I", "../include/swift", + "-cross-module-optimization" + ])]), + ] +) diff --git a/libswift/README.md b/libswift/README.md new file mode 100644 index 0000000000000..a2ce44697337d --- /dev/null +++ b/libswift/README.md @@ -0,0 +1,109 @@ +# Swift implemented in Swift + +_Libswift_ is the part of the Swift compiler, which is implemented in Swift. + +With _libswift_ it is possible to add SIL optimization passes written in Swift. It allows to gradually migrate the SIL optimizer from C++ to Swift. + +## Building + +_Libswift_ is a static library and it is built as part of the swift compiler using build-script and CMake. + +To enable _libswift_, add the build-script option `--libswift`. + +In this early stage of development _libswift_ is disabled by default. Right now _libswift_ does not contain any optimizations or features which are not available in the existing optimizer. Therefore a compiler with disabled _libswift_ still behaves as a compiler with enabled _libswift_. This will change soon. + +When _libswift_ is enabled, it is built with a Swift toolchain (5.3 or newer), which must be installed on the host system. The `swiftc` compiler driver is expected to be in the command search path. + +Currently the `swift-frontend` and `sil-opt` tools use _libswift_. Tools, which don't use any optimization passes from _libswift_ don't need to link _libswift_. For example, all tools, which compile Swift source code, but don't optimize it, like SourceKit or lldb, don't need to link _libswift_. As long as `initializeLibSwift()` is not called there is no dependency on _libswift_. + +This also means that currently it is not possible to implement mandatory passes in _libswift_, because this would break tools which compile Swift code but don't use _libswift_. When we want to implement mandatory passes in _libswift_ in the future, we'll need to link _libswift_ to all those tools. + +## SIL + +The design of SIL in _libswift_ matches very closely the design on the C++ side. For example, there are functions, basic blocks, instructions, SIL values, etc. + +Though, there are some small deviations from the C++ SIL design. Either due to the nature of the Swift language (e.g. the SIL `Value` is a protocol, not a class), or improvements, which could be done in C++ as well. + +Bridging SIL between C++ and Swift is toll-free, i.e. does not involve any "conversion" between C++ and Swift SIL. + +### The bridging layer + +The bridging layer is a small interface layer which enables calling into the SIL C++ API from the Swift side. Currently the bridging layer is implemented in C using C interop. In future it can be replaced by a C++ implementation by using C++ interop, which will further simplify the bridging layer or make it completely obsolete. But this is an implementation detail and does not affect the API of SIL in _libswift_. + +The bridging layer consists of the C header file `SILBridging.h` and its implementation file `SILBridging.cpp`. The header file contains all the bridging functions and some C data structures like `BridgedStringRef` (once we use C++ interop, those C data structures are not required anymore and can be removed). + +### SIL C++ objects in Swift + +The core SIL C++ classes have corresponding classes in _libswift_, for example `Function`, `BasicBlock` or all the instruction classes. + +This makes _libswift_ easy to use and it allows to program in a "Swifty" style. For example one can write + +```swift + for inst in block.instructions { + if let cfi = inst as? CondFailInst { + // ... + } + } +``` + +Bridging SIL classes is implemented by including a two word Swift object header (`SwiftObjectHeader`) in the C++ definition of a class, like in `SILFunction`, `SILBasicBlock` or `SILNode`. This enables to use SIL objects on both, the C++ and the Swift, side. + +The Swift class metatypes are "registered" by `registerClass()`, called from `initializeLibSwift()`. On the C++ side, they are stored in static global variables (see `registerBridgedClass()`) and then used to initialize the object headers in the class constructors. + +The reference counts in the object header are initialized to "immortal", which let's all ARC operations on SIL objects be no-ops. + +The Swift classes don't define any stored properties, because those would overlap data fields in the C++ classes. Instead, data fields are accessed via computed properties, which call bridging functions to retrieve the actual data values. + +### Lazy implementation + +In the current state the SIL functionality and API is not completely implemented, yet. For example, not all instruction classes have a corresponding class in _libswift_. Whenever a new _libswift_ optimization needs a specific SIL feature, like an instruction, a Builder-function or an accessor to a data field, it's easy to add the missing parts. + +For example, to add a new instruction class: + +* replace the macro which defines the instruction in `SILNodes.def` with a `BRIDGED-*` macro +* add the instruction class in `Instruction.swift` +* register the class in `registerSILClasses()` +* if needed, add bridging functions to access the instruction's data fields. + + +No yet implemented instruction classes are mapped to a "placeholder" instruction, e.g `UnimplementedInstruction`. This ensures that optimizations can process any kind of SIL, even if some instructions don't have a representation in _libswift_ yet. + +## The Optimizer + +Similar to SIL, the optimizer also uses a small bridging layer (`OptimizerBridging.h`). +Passes are registered in `registerSwiftPasses()`, called from `initializeLibSwift()`. +The C++ PassManager can then call a _libwift_ pass like any other `SILFunctionTransform` pass. + +To add a new function pass: + +* add a `SWIFT_FUNCTION_PASS` entry in `Passes.def` +* create a new Swift file in `libswift/Sources/Optimizer/FunctionPasses` +* add a `FunctionPass` global +* register the pass in `registerSwiftPasses()` + +All SIL modifications, which a pass can do, are going through the `FunctionPassContext` - the second parameter of its run-function. In other words, the context is the central place to make modifications. This enables automatic change notifications to the pass manager. Also, it makes it easier to build a concurrent pass manager in future. + +### Instruction Passes + +In addition to function passes, _libswift_ provides the infrastructure for instruction passes. Instruction passes are invoked from SILCombine (in the C++ SILOptimizer) and correspond to a visit-function in SILCombine. + +With instruction passes it's possible to implement small peephole optimizations for certain instruction classes. + +To add a new instruction pass: + +* add a `SWIFT_INSTRUCTION_PASS` entry in `Passes.def` +* create a new Swift file in `libswift/Sources/Optimizer/InstructionPasses` +* add an `InstructionPass` global +* register the pass in `registerSwiftPasses()` +* if this passes replaces an existing `SILCombiner` visit function, remove the old visit function + +## Performance + +The performance of _libswift_ is very important, because compile time is critical. +Some performance considerations: + +* Memory is managed on the C++ side. On the Swift side, SIL objects are treated as "immortal" objects, which avoids (most of) ARC overhead. ARC runtime functions are still being called, but no atomic reference counting operations are done. In future we could add a compiler feature to mark classes as immortal to avoid the runtime calls at all. + +* Minimizing memory allocations: _libswift_ provides data structures which are malloc-free. For example `StackList` can be used in optimizations to implement work lists without any memory allocations. (Not yet done: `BasicBlockSet`, `BasicBlockData`) + +But most importantly, if there are performance issues with the current compiler, the design of _libswift_ should make it possible to fix performance deficiencies with future compiler improvements. diff --git a/libswift/Sources/CMakeLists.txt b/libswift/Sources/CMakeLists.txt new file mode 100644 index 0000000000000..41c34ff9e46f8 --- /dev/null +++ b/libswift/Sources/CMakeLists.txt @@ -0,0 +1,10 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_subdirectory(SIL) +add_subdirectory(Optimizer) diff --git a/libswift/Sources/Optimizer/Analysis/AliasAnalysis.swift b/libswift/Sources/Optimizer/Analysis/AliasAnalysis.swift new file mode 100644 index 0000000000000..d522038618a0f --- /dev/null +++ b/libswift/Sources/Optimizer/Analysis/AliasAnalysis.swift @@ -0,0 +1,46 @@ +//===--- AliasAnalysis.swift - the alias analysis -------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import OptimizerBridging +import SIL + +struct AliasAnalysis { + let bridged: BridgedAliasAnalysis + + func mayRead(_ inst: Instruction, fromAddress: Value) -> Bool { + switch AliasAnalysis_getMemBehavior(bridged, inst.bridged, fromAddress.bridged) { + case MayReadBehavior, MayReadWriteBehavior, MayHaveSideEffectsBehavior: + return true + default: + return false + } + } + + func mayWrite(_ inst: Instruction, toAddress: Value) -> Bool { + switch AliasAnalysis_getMemBehavior(bridged, inst.bridged, toAddress.bridged) { + case MayWriteBehavior, MayReadWriteBehavior, MayHaveSideEffectsBehavior: + return true + default: + return false + } + } + + func mayReadOrWrite(_ inst: Instruction, address: Value) -> Bool { + switch AliasAnalysis_getMemBehavior(bridged, inst.bridged, address.bridged) { + case MayReadBehavior, MayWriteBehavior, MayReadWriteBehavior, + MayHaveSideEffectsBehavior: + return true + default: + return false + } + } +} diff --git a/libswift/Sources/Optimizer/Analysis/CMakeLists.txt b/libswift/Sources/Optimizer/Analysis/CMakeLists.txt new file mode 100644 index 0000000000000..d0de11503a12b --- /dev/null +++ b/libswift/Sources/Optimizer/Analysis/CMakeLists.txt @@ -0,0 +1,11 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +libswift_sources(Optimizer + AliasAnalysis.swift + CalleeAnalysis.swift) diff --git a/libswift/Sources/Optimizer/Analysis/CalleeAnalysis.swift b/libswift/Sources/Optimizer/Analysis/CalleeAnalysis.swift new file mode 100644 index 0000000000000..41ab03b5a450b --- /dev/null +++ b/libswift/Sources/Optimizer/Analysis/CalleeAnalysis.swift @@ -0,0 +1,40 @@ +//===--- CalleeAnalysis.swift - the callee analysis -----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import OptimizerBridging +import SIL + +public struct CalleeAnalysis { + let bridged: BridgedCalleeAnalysis + + public func getCallees(callee: Value) -> FunctionArray { + return FunctionArray(bridged: CalleeAnalysis_getCallees(bridged, callee.bridged)) + } +} + +public struct FunctionArray : RandomAccessCollection, CustomReflectable { + fileprivate let bridged: BridgedCalleeList + + public var startIndex: Int { 0 } + public var endIndex: Int { BridgedFunctionArray_size(bridged) } + + public subscript(_ index: Int) -> Function { + return BridgedFunctionArray_get(bridged, index).function + } + + public var allCalleesKnown: Bool { bridged.incomplete == 0 } + + public var customMirror: Mirror { + let c: [Mirror.Child] = map { (label: nil, value: $0.name) } + return Mirror(self, children: c) + } +} diff --git a/libswift/Sources/Optimizer/CMakeLists.txt b/libswift/Sources/Optimizer/CMakeLists.txt new file mode 100644 index 0000000000000..e5f3dbc18aac3 --- /dev/null +++ b/libswift/Sources/Optimizer/CMakeLists.txt @@ -0,0 +1,15 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_libswift_module(Optimizer + DEPENDS SIL) + +add_subdirectory(Analysis) +add_subdirectory(InstructionPasses) +add_subdirectory(PassManager) +add_subdirectory(FunctionPasses) diff --git a/libswift/Sources/Optimizer/FunctionPasses/CMakeLists.txt b/libswift/Sources/Optimizer/FunctionPasses/CMakeLists.txt new file mode 100644 index 0000000000000..f17b18937cd14 --- /dev/null +++ b/libswift/Sources/Optimizer/FunctionPasses/CMakeLists.txt @@ -0,0 +1,12 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +libswift_sources(Optimizer + SILPrinter.swift + MergeCondFails.swift +) diff --git a/libswift/Sources/Optimizer/FunctionPasses/MergeCondFails.swift b/libswift/Sources/Optimizer/FunctionPasses/MergeCondFails.swift new file mode 100644 index 0000000000000..33bf2fe37f430 --- /dev/null +++ b/libswift/Sources/Optimizer/FunctionPasses/MergeCondFails.swift @@ -0,0 +1,94 @@ +//===--- MergeCondFail.swift - Merge cond_fail instructions --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SIL + +let mergeCondFailsPass = FunctionPass(name: "merge-cond_fails", runMergeCondFails) + +/// Return true if the operand of the cond_fail instruction looks like +/// the overflow bit of an arithmetic instruction. +private func hasOverflowConditionOperand(_ cfi: CondFailInst) -> Bool { + if let tei = cfi.operand as? TupleExtractInst { + return tei.operand is BuiltinInst + } + return false +} + +/// Merge cond_fail instructions. +/// +/// We can merge cond_fail instructions if there is no side-effect or memory +/// write in between them. +/// This pass merges cond_fail instructions by building the disjunction of +/// their operands. +private func runMergeCondFails(function: Function, context: PassContext) { + + // Merge cond_fail instructions if there is no side-effect or read in + // between them. + for block in function.blocks { + // Per basic block list of cond_fails to merge. + var condFailToMerge = StackList(context) + + for inst in block.instructions { + if let cfi = inst as? CondFailInst { + // Do not process arithmetic overflow checks. We typically generate more + // efficient code with separate jump-on-overflow. + if !hasOverflowConditionOperand(cfi) && + (condFailToMerge.isEmpty || cfi.message == condFailToMerge.first!.message) { + condFailToMerge.push(cfi) + } + } else if inst.mayHaveSideEffects || inst.mayReadFromMemory { + // Stop merging at side-effects or reads from memory. + mergeCondFails(&condFailToMerge, context: context) + } + } + // Process any remaining cond_fail instructions in the current basic + // block. + mergeCondFails(&condFailToMerge, context: context) + } +} + +/// Try to merge the cond_fail instructions. Returns true if any could +/// be merge. +private func mergeCondFails(_ condFailToMerge: inout StackList, + context: PassContext) { + guard let lastCFI = condFailToMerge.last else { + return + } + var mergedCond: Value? = nil + var didMerge = false + let builder = Builder(at: lastCFI.next!, location: lastCFI.location, context) + + // Merge conditions and remove the merged cond_fail instructions. + for cfi in condFailToMerge { + if let prevCond = mergedCond { + mergedCond = builder.createBuiltinBinaryFunction(name: "or", + operandType: prevCond.type, + resultType: prevCond.type, + arguments: [prevCond, cfi.operand]) + didMerge = true + } else { + mergedCond = cfi.operand + } + } + if !didMerge { + condFailToMerge.removeAll() + return + } + + // Create a new cond_fail using the merged condition. + _ = builder.createCondFail(condition: mergedCond!, + message: lastCFI.message) + + while let cfi = condFailToMerge.pop() { + context.erase(instruction: cfi) + } +} diff --git a/libswift/Sources/Optimizer/FunctionPasses/SILPrinter.swift b/libswift/Sources/Optimizer/FunctionPasses/SILPrinter.swift new file mode 100644 index 0000000000000..2e15e24c443c1 --- /dev/null +++ b/libswift/Sources/Optimizer/FunctionPasses/SILPrinter.swift @@ -0,0 +1,50 @@ +//===--- SILPrinter.swift - Example swift function pass -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SIL + +let silPrinterPass = FunctionPass(name: "sil-printer", runSILPrinter) + +func runSILPrinter(function: Function, context: PassContext) { + print("run SILPrinter on function: \(function.name)") + + for (bbIdx, block) in function.blocks.enumerated() { + print("bb\(bbIdx):") + + print(" predecessors: " + + block.predecessors.map { $0.label }.joined(separator: ", ")) + print(" successors: " + + block.successors.map { $0.label }.joined(separator: ", ")) + + print(" arguments:") + for arg in block.arguments { + print(" arg: \(arg)") + for use in arg.uses { + print(" user: \(use.instruction)") + } + } + + print(" instructions:") + for inst in block.instructions { + print(" \(inst)") + for op in inst.operands { + print(" op: \(op.value)") + } + for (resultIdx, result) in inst.results.enumerated() { + for use in result.uses { + print(" user of result \(resultIdx): \(use.instruction)") + } + } + } + print() + } +} diff --git a/libswift/Sources/Optimizer/InstructionPasses/CMakeLists.txt b/libswift/Sources/Optimizer/InstructionPasses/CMakeLists.txt new file mode 100644 index 0000000000000..15ace2552d31d --- /dev/null +++ b/libswift/Sources/Optimizer/InstructionPasses/CMakeLists.txt @@ -0,0 +1,11 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +libswift_sources(Optimizer + SimplifyGlobalValue.swift + SimplifyStrongRetainRelease.swift) diff --git a/libswift/Sources/Optimizer/InstructionPasses/SimplifyGlobalValue.swift b/libswift/Sources/Optimizer/InstructionPasses/SimplifyGlobalValue.swift new file mode 100644 index 0000000000000..e644a84f1e761 --- /dev/null +++ b/libswift/Sources/Optimizer/InstructionPasses/SimplifyGlobalValue.swift @@ -0,0 +1,58 @@ +//===--- SimplifyGlobalValue.swift - Simplify global_value instruction ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SIL + +// Removes all reference counting instructions of a `global_value` instruction +// if it does not escape. +// +// Note that `simplifyStrongRetainPass` and `simplifyStrongReleasePass` can +// even remove "unbalanced" retains/releases of a `global_value`, but this +// requires a minimum deployment target. +let simplifyGlobalValuePass = InstructionPass( + name: "simplify-global_value", { + (globalValue: GlobalValueInst, context: PassContext) in + + var users = StackList(context) + if checkUsers(of: globalValue, users: &users) { + while let inst = users.pop() { + context.erase(instruction: inst) + } + } else { + users.removeAll() + } +}) + +/// Returns true if reference counting and debug_value users of a global_value +/// can be deleted. +private func checkUsers(of val: Value, users: inout StackList) -> Bool { + for use in val.uses { + let user = use.instruction + if user is RefCountingInst || user is DebugValueInst { + users.push(user) + continue + } + if let upCast = user as? UpcastInst { + if !checkUsers(of: upCast, users: &users) { + return false + } + continue + } + // Projection instructions don't access the object header, so they don't + // prevent deleting reference counting instructions. + if user is RefElementAddrInst || user is RefTailAddrInst { + continue + } + return false + } + return true +} diff --git a/libswift/Sources/Optimizer/InstructionPasses/SimplifyStrongRetainRelease.swift b/libswift/Sources/Optimizer/InstructionPasses/SimplifyStrongRetainRelease.swift new file mode 100644 index 0000000000000..b9cd4b78bc39b --- /dev/null +++ b/libswift/Sources/Optimizer/InstructionPasses/SimplifyStrongRetainRelease.swift @@ -0,0 +1,114 @@ +//===--- SimplifyStrongRetainRelease.swift - strong_retain/release opt ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SIL + +let simplifyStrongRetainPass = InstructionPass( + name: "simplify-strong_retain", { + (retain: StrongRetainInst, context: PassContext) in + + if isNotReferenceCounted(value: retain.operand, context: context) { + context.erase(instruction: retain) + return + } + + // Sometimes in the stdlib due to hand offs, we will see code like: + // + // strong_release %0 + // strong_retain %0 + // + // with the matching strong_retain to the strong_release in a predecessor + // basic block and the matching strong_release for the strong_retain in a + // successor basic block. + // + // Due to the matching pairs being in different basic blocks, the ARC + // Optimizer (which is currently local to one basic block does not handle + // it). But that does not mean that we cannot eliminate this pair with a + // peephole. + if let prev = retain.previous { + if let release = prev as? StrongReleaseInst { + if release.operand == retain.operand { + context.erase(instruction: retain) + context.erase(instruction: release) + return + } + } + } +}) + +let simplifyStrongReleasePass = InstructionPass( + name: "simplify-strong_release", { + (release: StrongReleaseInst, context: PassContext) in + + let op = release.operand + if isNotReferenceCounted(value: op, context: context) { + context.erase(instruction: release) + return + } + + // Release of a classbound existential converted from a class is just a + // release of the class, squish the conversion. + if let ier = op as? InitExistentialRefInst { + if ier.uses.isSingleUse { + context.setOperand(of: release, at: 0, to: ier.operand) + context.erase(instruction: ier) + return + } + } +}) + +/// Returns true if \p value is something where reference counting instructions +/// don't have any effect. +private func isNotReferenceCounted(value: Value, context: PassContext) -> Bool { + switch value { + case let cfi as ConvertFunctionInst: + return isNotReferenceCounted(value: cfi.operand, context: context) + case let uci as UpcastInst: + return isNotReferenceCounted(value: uci.operand, context: context) + case let urc as UncheckedRefCastInst: + return isNotReferenceCounted(value: urc.operand, context: context) + case is GlobalValueInst: + // Since Swift 5.1, statically allocated objects have "immortal" reference + // counts. Therefore we can safely eliminate unbalaced retains and + // releases, because they are no-ops on immortal objects. + // Note that the `simplifyGlobalValuePass` pass is deleting balanced + // retains/releases, which doesn't require a Swift 5.1 minimum deployment + // targert. + return context.isSwift51RuntimeAvailable + case let rptr as RawPointerToRefInst: + // Like `global_value` but for the empty collection singletons from the + // stdlib, e.g. the empty Array singleton. + if context.isSwift51RuntimeAvailable { + // The pattern generated for empty collection singletons is: + // %0 = global_addr @_swiftEmptyArrayStorage + // %1 = address_to_pointer %0 + // %2 = raw_pointer_to_ref %1 + if let atp = rptr.operand as? AddressToPointerInst { + return atp.operand is GlobalAddrInst + } + } + return false + case // Thin functions are not reference counted. + is ThinToThickFunctionInst, + // The same for meta types. + is ObjCExistentialMetatypeToObjectInst, + is ObjCMetatypeToObjectInst, + // Retain and Release of tagged strings is a no-op. + // The builtin code pattern to find tagged strings is: + // builtin "stringObjectOr_Int64" (or to tag the string) + // value_to_bridge_object (cast the UInt to bridge object) + is ValueToBridgeObjectInst: + return true + default: + return false + } +} diff --git a/libswift/Sources/Optimizer/PassManager/CMakeLists.txt b/libswift/Sources/Optimizer/PassManager/CMakeLists.txt new file mode 100644 index 0000000000000..0a0b7146d1fe4 --- /dev/null +++ b/libswift/Sources/Optimizer/PassManager/CMakeLists.txt @@ -0,0 +1,12 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +libswift_sources(Optimizer + PassUtils.swift + PassRegistration.swift) + diff --git a/libswift/Sources/Optimizer/PassManager/PassRegistration.swift b/libswift/Sources/Optimizer/PassManager/PassRegistration.swift new file mode 100644 index 0000000000000..f20ac49438fe4 --- /dev/null +++ b/libswift/Sources/Optimizer/PassManager/PassRegistration.swift @@ -0,0 +1,44 @@ +//===--- PassRegistration.swift - Register optimzation passes -------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SIL +import OptimizerBridging + +@_cdecl("initializeLibSwift") +public func initializeLibSwift() { + registerSILClasses() + registerSwiftPasses() +} + +private func registerPass( + _ pass: FunctionPass, + _ runFn: @escaping (@convention(c) (BridgedFunctionPassCtxt) -> ())) { + pass.name.withBridgedStringRef { nameStr in + SILPassManager_registerFunctionPass(nameStr, runFn) + } +} + +private func registerPass( + _ pass: InstructionPass, + _ runFn: @escaping (@convention(c) (BridgedInstructionPassCtxt) -> ())) { + pass.name.withBridgedStringRef { nameStr in + SILCombine_registerInstructionPass(nameStr, runFn) + } +} + +private func registerSwiftPasses() { + registerPass(silPrinterPass, { silPrinterPass.run($0) }) + registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) }) + registerPass(simplifyGlobalValuePass, { simplifyGlobalValuePass.run($0) }) + registerPass(simplifyStrongRetainPass, { simplifyStrongRetainPass.run($0) }) + registerPass(simplifyStrongReleasePass, { simplifyStrongReleasePass.run($0) }) +} diff --git a/libswift/Sources/Optimizer/PassManager/PassUtils.swift b/libswift/Sources/Optimizer/PassManager/PassUtils.swift new file mode 100644 index 0000000000000..c1ad65a37d08d --- /dev/null +++ b/libswift/Sources/Optimizer/PassManager/PassUtils.swift @@ -0,0 +1,114 @@ +//===--- PassUtils.swift - Utilities for optimzation passes ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SIL +import OptimizerBridging + +public typealias BridgedFunctionPassCtxt = + OptimizerBridging.BridgedFunctionPassCtxt +public typealias BridgedInstructionPassCtxt = + OptimizerBridging.BridgedInstructionPassCtxt + +struct PassContext { + + fileprivate let passContext: BridgedPassContext + + var isSwift51RuntimeAvailable: Bool { + PassContext_isSwift51RuntimeAvailable(passContext) != 0 + } + + var aliasAnalysis: AliasAnalysis { + let bridgedAA = PassContext_getAliasAnalysis(passContext) + return AliasAnalysis(bridged: bridgedAA) + } + + var calleeAnalysis: CalleeAnalysis { + let bridgeCA = PassContext_getCalleeAnalysis(passContext) + return CalleeAnalysis(bridged: bridgeCA) + } + + func erase(instruction: Instruction) { + if instruction is FullApplySite { + PassContext_notifyChanges(passContext, callsChanged) + } + if instruction is TermInst { + PassContext_notifyChanges(passContext, branchesChanged) + } + PassContext_notifyChanges(passContext, instructionsChanged) + + PassContext_eraseInstruction(passContext, instruction.bridged) + } + + func setOperand(of instruction: Instruction, at index : Int, to value: Value) { + if instruction is FullApplySite && index == ApplyOperands.calleeOperandIndex { + PassContext_notifyChanges(passContext, callsChanged) + } + PassContext_notifyChanges(passContext, instructionsChanged) + + SILInstruction_setOperand(instruction.bridged, index, value.bridged) + } +} + +struct FunctionPass { + + let name: String + let runFunction: (Function, PassContext) -> () + + public init(name: String, + _ runFunction: @escaping (Function, PassContext) -> ()) { + self.name = name + self.runFunction = runFunction + } + + func run(_ bridgedCtxt: BridgedFunctionPassCtxt) { + let function = bridgedCtxt.function.function + let context = PassContext(passContext: bridgedCtxt.passContext) + runFunction(function, context) + } +} + +struct InstructionPass { + + let name: String + let runFunction: (InstType, PassContext) -> () + + public init(name: String, + _ runFunction: @escaping (InstType, PassContext) -> ()) { + self.name = name + self.runFunction = runFunction + } + + func run(_ bridgedCtxt: BridgedInstructionPassCtxt) { + let inst = bridgedCtxt.instruction.getAs(InstType.self) + let context = PassContext(passContext: bridgedCtxt.passContext) + runFunction(inst, context) + } +} + +extension StackList { + init(_ context: PassContext) { + self.init(context: context.passContext) + } +} + +extension Builder { + init(at insPnt: Instruction, location: Location, + _ context: PassContext) { + self.init(insertionPoint: insPnt, location: location, + passContext: context.passContext) + } + + init(at insPnt: Instruction, _ context: PassContext) { + self.init(insertionPoint: insPnt, location: insPnt.location, + passContext: context.passContext) + } +} diff --git a/libswift/Sources/SIL/ApplySite.swift b/libswift/Sources/SIL/ApplySite.swift new file mode 100644 index 0000000000000..520be5e8b4fc2 --- /dev/null +++ b/libswift/Sources/SIL/ApplySite.swift @@ -0,0 +1,39 @@ +//===--- ApplySite.swift - Defines the ApplySite protocols ----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +public struct ApplyOperands { + public static let calleeOperandIndex: Int = 0 + public static let firstArgumentIndex = 1 +} + +public protocol ApplySite : AnyObject { + var operands: OperandArray { get } + var numArguments: Int { get } +} + +extension ApplySite { + public var callee: Value { operands[ApplyOperands.calleeOperandIndex].value } + + public func argumentIndex(of operand: Operand) -> Int? { + let opIdx = operand.index + if opIdx >= ApplyOperands.firstArgumentIndex && + opIdx <= ApplyOperands.firstArgumentIndex + numArguments { + return opIdx - ApplyOperands.firstArgumentIndex + } + return nil + } + +} + +public protocol FullApplySite : ApplySite { + var singleDirectResult: Value? { get } +} diff --git a/libswift/Sources/SIL/Argument.swift b/libswift/Sources/SIL/Argument.swift new file mode 100644 index 0000000000000..8c059353213d8 --- /dev/null +++ b/libswift/Sources/SIL/Argument.swift @@ -0,0 +1,64 @@ +//===--- Argument.swift - Defines the Argument classes --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +/// A basic block argument. +/// +/// Maps to both, SILPhiArgument and SILFunctionArgument. +public class Argument : Value, Equatable { + public var definingInstruction: Instruction? { nil } + + public var block: BasicBlock { + return SILArgument_getParent(bridged).block + } + + var bridged: BridgedArgument { BridgedArgument(obj: SwiftObject(self)) } + + public var index: Int { + return block.arguments.firstIndex(of: self)! + } + + public static func ==(lhs: Argument, rhs: Argument) -> Bool { + lhs === rhs + } +} + +final public class FunctionArgument : Argument { +} + +final public class BlockArgument : Argument { + /// Note: critical edges are not supported, i.e. this is false if there is + /// a cond_br in the predecessors. + public var isPhiArgument: Bool { + block.predecessors.allSatisfy { $0.terminator is BranchInst } + } + + public var incomingPhiOperands: LazyMapSequence { + assert(isPhiArgument) + let idx = index + return block.predecessors.lazy.map { $0.terminator.operands[idx] } + } + + public var incomingPhiValues: LazyMapSequence { + assert(isPhiArgument) + let idx = index + return block.predecessors.lazy.map { $0.terminator.operands[idx].value } + } +} + +// Bridging utilities + +extension BridgedArgument { + var argument: Argument { obj.getAs(Argument.self) } + var functionArgument: FunctionArgument { obj.getAs(FunctionArgument.self) } +} diff --git a/libswift/Sources/SIL/BasicBlock.swift b/libswift/Sources/SIL/BasicBlock.swift new file mode 100644 index 0000000000000..4e276a0f0d8c0 --- /dev/null +++ b/libswift/Sources/SIL/BasicBlock.swift @@ -0,0 +1,133 @@ +//===--- BasicBlock.swift - Defines the BasicBlock class ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +final public class BasicBlock : ListNode, CustomStringConvertible { + public var next: BasicBlock? { SILBasicBlock_next(bridged).block } + public var previous: BasicBlock? { SILBasicBlock_previous(bridged).block } + + public var function: Function { SILBasicBlock_getFunction(bridged).function } + + public var description: String { + SILBasicBlock_debugDescription(bridged).takeString() + } + + public var arguments: ArgumentArray { ArgumentArray(block: self) } + + public var instructions: List { + List(startAt: SILBasicBlock_firstInst(bridged).instruction) + } + + public var reverseInstructions: ReverseList { + ReverseList(startAt: SILBasicBlock_lastInst(bridged).instruction) + } + + public var terminator: TermInst { + SILBasicBlock_lastInst(bridged).instruction as! TermInst + } + + public var successors: SuccessorArray { terminator.successors } + + public var predecessors: PredecessorList { + PredecessorList(startAt: SILBasicBlock_getFirstPred(bridged)) + } + + public var singlePredecessor: BasicBlock? { + var preds = predecessors + if let p = preds.next() { + if preds.next() == nil { + return p + } + } + return nil + } + + /// The index of the basic block in its function. + /// This has O(n) complexity. Only use it for debugging + public var index: Int { + for (idx, block) in function.blocks.enumerated() { + if block == self { return idx } + } + fatalError() + } + + public var label: String { "bb\(index)" } + + var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) } +} + +public func == (lhs: BasicBlock, rhs: BasicBlock) -> Bool { lhs === rhs } + +public struct ArgumentArray : RandomAccessCollection { + fileprivate let block: BasicBlock + + public var startIndex: Int { return 0 } + public var endIndex: Int { SILBasicBlock_getNumArguments(block.bridged) } + + public subscript(_ index: Int) -> Argument { + SILBasicBlock_getArgument(block.bridged, index).argument + } +} + +public struct SuccessorArray : RandomAccessCollection, CustomReflectable { + private let succArray: BridgedArrayRef + + init(succArray: BridgedArrayRef) { + self.succArray = succArray + } + + public var startIndex: Int { return 0 } + public var endIndex: Int { return Int(succArray.numElements) } + + public subscript(_ index: Int) -> BasicBlock { + precondition(index >= 0 && index < endIndex) + let s = BridgedSuccessor(succ: succArray.data + index &* BridgedSuccessorSize); + return SILSuccessor_getTargetBlock(s).block + } + + public var customMirror: Mirror { + let c: [Mirror.Child] = map { (label: nil, value: $0.label) } + return Mirror(self, children: c) + } +} + +public struct PredecessorList : Sequence, IteratorProtocol, CustomReflectable { + private var currentSucc: OptionalBridgedSuccessor + + public init(startAt: OptionalBridgedSuccessor) { currentSucc = startAt } + + public mutating func next() -> BasicBlock? { + if let succPtr = currentSucc.succ { + let succ = BridgedSuccessor(succ: succPtr) + currentSucc = SILSuccessor_getNext(succ) + return SILSuccessor_getContainingInst(succ).instruction.block + } + return nil + } + + public var customMirror: Mirror { + let c: [Mirror.Child] = map { (label: nil, value: $0) } + return Mirror(self, children: c) + } +} + + +// Bridging utilities + +extension BridgedBasicBlock { + var block: BasicBlock { obj.getAs(BasicBlock.self) } +} + +extension OptionalBridgedBasicBlock { + var block: BasicBlock? { obj.getAs(BasicBlock.self) } +} diff --git a/libswift/Sources/SIL/Builder.swift b/libswift/Sources/SIL/Builder.swift new file mode 100644 index 0000000000000..a19423b515cbc --- /dev/null +++ b/libswift/Sources/SIL/Builder.swift @@ -0,0 +1,63 @@ +//===--- Builder.swift - Building and modifying SIL ----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +/// A utility to create new instructions at a given insertion point. +public struct Builder { + let insertionPoint: Instruction + let location: Location + private let passContext: BridgedPassContext + + private var bridgedInsPoint: BridgedInstruction { insertionPoint.bridged } + + private func notifyInstructionsChanged() { + PassContext_notifyChanges(passContext, instructionsChanged) + } + + private func notifyCallsChanged() { + PassContext_notifyChanges(passContext, callsChanged) + } + + private func notifyBranchesChanged() { + PassContext_notifyChanges(passContext, branchesChanged) + } + + public init(insertionPoint: Instruction, location: Location, + passContext: BridgedPassContext) { + self.insertionPoint = insertionPoint + self.location = location; + self.passContext = passContext + } + + public func createBuiltinBinaryFunction(name: String, + operandType: Type, resultType: Type, arguments: [Value]) -> BuiltinInst { + notifyInstructionsChanged() + return arguments.withBridgedValues { valuesRef in + return name.withBridgedStringRef { nameStr in + let bi = SILBuilder_createBuiltinBinaryFunction( + bridgedInsPoint, location.bridgedLocation, nameStr, + operandType.bridged, resultType.bridged, valuesRef) + return bi.getAs(BuiltinInst.self) + } + } + } + + public func createCondFail(condition: Value, message: String) -> CondFailInst { + notifyInstructionsChanged() + return message.withBridgedStringRef { messageStr in + let cf = SILBuilder_createCondFail( + bridgedInsPoint, location.bridgedLocation, condition.bridged, messageStr) + return cf.getAs(CondFailInst.self) + } + } +} diff --git a/libswift/Sources/SIL/CMakeLists.txt b/libswift/Sources/SIL/CMakeLists.txt new file mode 100644 index 0000000000000..f7b7083dba03a --- /dev/null +++ b/libswift/Sources/SIL/CMakeLists.txt @@ -0,0 +1,24 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_libswift_module(SIL + ApplySite.swift + Argument.swift + BasicBlock.swift + Builder.swift + Function.swift + GlobalVariable.swift + Instruction.swift + Location.swift + Operand.swift + Registration.swift + Type.swift + Utils.swift + StackList.swift + Value.swift) + diff --git a/libswift/Sources/SIL/Function.swift b/libswift/Sources/SIL/Function.swift new file mode 100644 index 0000000000000..f39f3719b47fa --- /dev/null +++ b/libswift/Sources/SIL/Function.swift @@ -0,0 +1,43 @@ +//===--- Function.swift - Defines the Function class ----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +final public class Function : CustomStringConvertible { + public var name: String { + return SILFunction_getName(bridged).string + } + + final public var description: String { + return SILFunction_debugDescription(bridged).takeString() + } + + public var entryBlock: BasicBlock { + SILFunction_firstBlock(bridged).block! + } + + public var blocks : List { + return List(startAt: SILFunction_firstBlock(bridged).block) + } + + public var reverseBlocks : ReverseList { + return ReverseList(startAt: SILFunction_lastBlock(bridged).block) + } + + public var bridged: BridgedFunction { BridgedFunction(obj: SwiftObject(self)) } +} + +// Bridging utilities + +extension BridgedFunction { + public var function: Function { obj.getAs(Function.self) } +} diff --git a/libswift/Sources/SIL/GlobalVariable.swift b/libswift/Sources/SIL/GlobalVariable.swift new file mode 100644 index 0000000000000..57124be60da75 --- /dev/null +++ b/libswift/Sources/SIL/GlobalVariable.swift @@ -0,0 +1,33 @@ +//===--- GlobalVariable.swift - Defines the GlobalVariable class ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +final public class GlobalVariable : CustomStringConvertible { + public var name: String { + return SILGlobalVariable_getName(bridged).string + } + + public var description: String { + return SILGlobalVariable_debugDescription(bridged).takeString() + } + + // TODO: initializer instructions + + var bridged: BridgedGlobalVar { BridgedGlobalVar(obj: SwiftObject(self)) } +} + +// Bridging utilities + +extension BridgedGlobalVar { + var globalVar: GlobalVariable { obj.getAs(GlobalVariable.self) } +} diff --git a/libswift/Sources/SIL/Instruction.swift b/libswift/Sources/SIL/Instruction.swift new file mode 100644 index 0000000000000..fd2979277809a --- /dev/null +++ b/libswift/Sources/SIL/Instruction.swift @@ -0,0 +1,529 @@ +//===--- Instruction.swift - Defines the Instruction classes --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +//===----------------------------------------------------------------------===// +// Instruction base classes +//===----------------------------------------------------------------------===// + +public class Instruction : ListNode, CustomStringConvertible, Hashable { + final public var next: Instruction? { + SILInstruction_next(bridged).instruction + } + + final public var previous: Instruction? { + SILInstruction_previous(bridged).instruction + } + + final public var block: BasicBlock { + SILInstruction_getParent(bridged).block + } + + final public var description: String { + SILNode_debugDescription(bridgedNode).takeString() + } + + final public var operands: OperandArray { + return OperandArray(opArray: SILInstruction_getOperands(bridged)) + } + + fileprivate var resultCount: Int { 0 } + fileprivate func getResult(index: Int) -> Value { fatalError() } + + final public var results: InstructionResults { InstructionResults(self) } + + final public var location: Location { + return Location(bridgedLocation: SILInstruction_getLocation(bridged)) + } + + public var mayTrap: Bool { false } + + final public var mayHaveSideEffects: Bool { + return mayTrap || mayWriteToMemory + } + + final public var mayReadFromMemory: Bool { + switch SILInstruction_getMemBehavior(bridged) { + case MayReadBehavior, MayReadWriteBehavior, MayHaveSideEffectsBehavior: + return true + default: + return false + } + } + + final public var mayWriteToMemory: Bool { + switch SILInstruction_getMemBehavior(bridged) { + case MayWriteBehavior, MayReadWriteBehavior, MayHaveSideEffectsBehavior: + return true + default: + return false + } + } + + final public var mayReadOrWriteMemory: Bool { + switch SILInstruction_getMemBehavior(bridged) { + case MayReadBehavior, MayWriteBehavior, MayReadWriteBehavior, + MayHaveSideEffectsBehavior: + return true + default: + return false + } + } + + public static func ==(lhs: Instruction, rhs: Instruction) -> Bool { + lhs === rhs + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } + + public var bridged: BridgedInstruction { + BridgedInstruction(obj: SwiftObject(self)) + } + var bridgedNode: BridgedNode { BridgedNode(obj: SwiftObject(self)) } +} + +extension BridgedInstruction { + public var instruction: Instruction { obj.getAs(Instruction.self) } + public func getAs(_ instType: T.Type) -> T { obj.getAs(T.self) } +} + +extension OptionalBridgedInstruction { + var instruction: Instruction? { obj.getAs(Instruction.self) } +} + +public struct InstructionResults : Sequence, IteratorProtocol { + let inst: Instruction + let numResults: Int + var index: Int = 0 + + init(_ inst: Instruction) { + self.inst = inst + numResults = inst.resultCount + } + + public mutating func next() -> Value? { + let idx = index + if idx < numResults { + index += 1 + return inst.getResult(index: idx) + } + return nil + } +} + +public class SingleValueInstruction : Instruction, Value { + final public var definingInstruction: Instruction? { self } + + fileprivate final override var resultCount: Int { 1 } + fileprivate final override func getResult(index: Int) -> Value { self } +} + +public final class MultipleValueInstructionResult : Value { + final public var description: String { + SILNode_debugDescription(bridgedNode).takeString() + } + + public var definingInstruction: Instruction? { + MultiValueInstResult_getParent(bridged).instruction + } + + var bridged: BridgedMultiValueResult { + BridgedMultiValueResult(obj: SwiftObject(self)) + } + var bridgedNode: BridgedNode { BridgedNode(obj: SwiftObject(self)) } +} + +extension BridgedMultiValueResult { + var result: MultipleValueInstructionResult { + obj.getAs(MultipleValueInstructionResult.self) + } +} + +public class MultipleValueInstruction : Instruction { + fileprivate final override var resultCount: Int { + return MultipleValueInstruction_getNumResults(bridged) + } + fileprivate final override func getResult(index: Int) -> Value { + MultipleValueInstruction_getResult(bridged, index).result + } +} + +/// Instructions, which have a single operand. +public protocol UnaryInstruction : AnyObject { + var operands: OperandArray { get } + var operand: Value { get } +} + +extension UnaryInstruction { + public var operand: Value { operands[0].value } +} + +//===----------------------------------------------------------------------===// +// no-value instructions +//===----------------------------------------------------------------------===// + +/// Used for all non-value instructions which are not implemented here, yet. +/// See registerBridgedClass() in SILBridgingUtils.cpp. +final public class UnimplementedInstruction : Instruction { +} + +final public class StoreInst : Instruction { + public var sourceOperand: Operand { return operands[0] } + public var destinationOperand: Operand { return operands[1] } + public var source: Value { return sourceOperand.value } + public var destination: Value { return destinationOperand.value } +} + +final public class CopyAddrInst : Instruction { + public var sourceOperand: Operand { return operands[0] } + public var destinationOperand: Operand { return operands[1] } + public var source: Value { return sourceOperand.value } + public var destination: Value { return destinationOperand.value } +} + +final public class EndAccessInst : Instruction, UnaryInstruction { + public var beginAccess: BeginAccessInst { + return operand as! BeginAccessInst + } +} + +final public class EndBorrowInst : Instruction, UnaryInstruction {} + +final public class DeallocStackInst : Instruction, UnaryInstruction { + public var allocstack: AllocStackInst { + return operand as! AllocStackInst + } +} + +final public class CondFailInst : Instruction, UnaryInstruction { + public override var mayTrap: Bool { true } + + public var message: String { CondFailInst_getMessage(bridged).string } +} + +final public class FixLifetimeInst : Instruction, UnaryInstruction {} + +final public class DebugValueInst : Instruction, UnaryInstruction {} + +final public class UnconditionalCheckedCastAddrInst : Instruction { + public override var mayTrap: Bool { true } +} + +final public class SetDeallocatingInst : Instruction, UnaryInstruction {} + +final public class DeallocRefInst : Instruction, UnaryInstruction {} + +public class RefCountingInst : Instruction, UnaryInstruction {} + +final public class StrongRetainInst : RefCountingInst { +} + +final public class RetainValueInst : RefCountingInst { +} + +final public class StrongReleaseInst : RefCountingInst { +} + +final public class ReleaseValueInst : RefCountingInst { +} + +final public class DestroyValueInst : Instruction, UnaryInstruction {} + +final public class DestroyAddrInst : Instruction, UnaryInstruction {} + +final public class UnimplementedRefCountingInst : RefCountingInst {} + +//===----------------------------------------------------------------------===// +// single-value instructions +//===----------------------------------------------------------------------===// + +/// Used for all SingleValueInstructions which are not implemented here, yet. +/// See registerBridgedClass() in SILBridgingUtils.cpp. +final public class UnimplementedSingleValueInst : SingleValueInstruction { +} + +final public class LoadInst : SingleValueInstruction, UnaryInstruction {} + +final public class LoadBorrowInst : SingleValueInstruction, UnaryInstruction {} + +final public class BuiltinInst : SingleValueInstruction {} + +final public class UpcastInst : SingleValueInstruction, UnaryInstruction {} + +final public +class UncheckedRefCastInst : SingleValueInstruction, UnaryInstruction {} + +final public +class RawPointerToRefInst : SingleValueInstruction, UnaryInstruction {} + +final public +class AddressToPointerInst : SingleValueInstruction, UnaryInstruction {} + +final public +class InitExistentialRefInst : SingleValueInstruction, UnaryInstruction {} + +final public +class OpenExistentialRefInst : SingleValueInstruction, UnaryInstruction {} + +final public +class InitExistentialMetatypeInst : SingleValueInstruction, UnaryInstruction {} + +final public +class OpenExistentialMetatypeInst : SingleValueInstruction, UnaryInstruction {} + +final public +class ValueMetatypeInst : SingleValueInstruction, UnaryInstruction {} + +final public +class ExistentialMetatypeInst : SingleValueInstruction, UnaryInstruction {} + +public class GlobalAccessInst : SingleValueInstruction { + final public var global: GlobalVariable { + GlobalAccessInst_getGlobal(bridged).globalVar + } +} + +final public class GlobalAddrInst : GlobalAccessInst {} + +final public class GlobalValueInst : GlobalAccessInst {} + +final public class TupleInst : SingleValueInstruction { +} + +final public class TupleExtractInst : SingleValueInstruction, UnaryInstruction { + public var fieldIndex: Int { TupleExtractInst_fieldIndex(bridged) } +} + +final public +class TupleElementAddrInst : SingleValueInstruction, UnaryInstruction { + public var fieldIndex: Int { TupleElementAddrInst_fieldIndex(bridged) } +} + +final public class StructInst : SingleValueInstruction { +} + +final public class StructExtractInst : SingleValueInstruction, UnaryInstruction { + public var fieldIndex: Int { StructExtractInst_fieldIndex(bridged) } +} + +final public +class StructElementAddrInst : SingleValueInstruction, UnaryInstruction { + public var fieldIndex: Int { StructElementAddrInst_fieldIndex(bridged) } +} + +final public class EnumInst : SingleValueInstruction, UnaryInstruction { + public var caseIndex: Int { EnumInst_caseIndex(bridged) } +} + +final public +class UncheckedEnumDataInst : SingleValueInstruction, UnaryInstruction { + public var caseIndex: Int { UncheckedEnumDataInst_caseIndex(bridged) } +} + +final public class RefElementAddrInst : SingleValueInstruction, UnaryInstruction { + public var fieldIndex: Int { RefElementAddrInst_fieldIndex(bridged) } +} + +final public class RefTailAddrInst : SingleValueInstruction, UnaryInstruction {} + +final public +class UnconditionalCheckedCastInst : SingleValueInstruction, UnaryInstruction { + public override var mayTrap: Bool { true } +} + +final public +class UnconditionalCheckedCastValueInst : SingleValueInstruction, + UnaryInstruction { + public override var mayTrap: Bool { true } +} + +final public +class ConvertFunctionInst : SingleValueInstruction, UnaryInstruction {} + +final public +class ThinToThickFunctionInst : SingleValueInstruction, UnaryInstruction {} + +final public +class ObjCExistentialMetatypeToObjectInst : SingleValueInstruction, + UnaryInstruction {} + +final public +class ObjCMetatypeToObjectInst : SingleValueInstruction, UnaryInstruction {} + +final public +class ValueToBridgeObjectInst : SingleValueInstruction, UnaryInstruction {} + +final public class BeginAccessInst : SingleValueInstruction, UnaryInstruction {} + +final public class BeginBorrowInst : SingleValueInstruction, UnaryInstruction {} + +final public class CopyValueInst : SingleValueInstruction, UnaryInstruction {} + +final public +class ClassifyBridgeObjectInst : SingleValueInstruction, UnaryInstruction {} + +final public class PartialApplyInst : SingleValueInstruction, ApplySite { + public var numArguments: Int { PartialApplyInst_numArguments(bridged) } +} + +final public class ApplyInst : SingleValueInstruction, FullApplySite { + public var numArguments: Int { ApplyInst_numArguments(bridged) } + + public var singleDirectResult: Value? { self } +} + +//===----------------------------------------------------------------------===// +// single-value allocation instructions +//===----------------------------------------------------------------------===// + +public protocol Allocation : AnyObject { } + +final public class AllocStackInst : SingleValueInstruction, Allocation { +} + +final public class AllocRefInst : SingleValueInstruction, Allocation { +} + +final public class AllocRefDynamicInst : SingleValueInstruction, Allocation { +} + +final public class AllocValueBufferInst : SingleValueInstruction, Allocation { +} + +final public class AllocBoxInst : SingleValueInstruction, Allocation { +} + +final public class AllocExistentialBoxInst : SingleValueInstruction, Allocation { +} + +//===----------------------------------------------------------------------===// +// multi-value instructions +//===----------------------------------------------------------------------===// + +final public class BeginCOWMutationInst : MultipleValueInstruction { +} + +final public class DestructureStructInst : MultipleValueInstruction { +} + +final public class DestructureTupleInst : MultipleValueInstruction { +} + +final public class BeginApplyInst : MultipleValueInstruction, FullApplySite { + public var numArguments: Int { BeginApplyInst_numArguments(bridged) } + + public var singleDirectResult: Value? { nil } +} + +//===----------------------------------------------------------------------===// +// terminator instructions +//===----------------------------------------------------------------------===// + +public class TermInst : Instruction { + final public var successors: SuccessorArray { + SuccessorArray(succArray: TermInst_getSuccessors(bridged)) + } +} + +final public class UnreachableInst : TermInst { +} + +final public class ReturnInst : TermInst, UnaryInstruction { +} + +final public class ThrowInst : TermInst, UnaryInstruction { +} + +final public class YieldInst : TermInst { +} + +final public class UnwindInst : TermInst { +} + +final public class TryApplyInst : TermInst, FullApplySite { + public var numArguments: Int { TryApplyInst_numArguments(bridged) } + + public var normalBlock: BasicBlock { successors[0] } + public var errorBlock: BasicBlock { successors[1] } + + public var singleDirectResult: Value? { normalBlock.arguments[0] } +} + +final public class BranchInst : TermInst { + public var targetBlock: BasicBlock { BranchInst_getTargetBlock(bridged).block } + + public func getArgument(for operand: Operand) -> Argument { + return targetBlock.arguments[operand.index] + } +} + +final public class CondBranchInst : TermInst { +} + +final public class SwitchValueInst : TermInst { +} + +final public class SwitchEnumInst : TermInst { + + public var enumOp: Value { operands[0].value } + + public struct CaseIndexArray : RandomAccessCollection { + fileprivate let switchEnum: SwitchEnumInst + + public var startIndex: Int { return 0 } + public var endIndex: Int { SwitchEnumInst_getNumCases(switchEnum.bridged) } + + public subscript(_ index: Int) -> Int { + SwitchEnumInst_getCaseIndex(switchEnum.bridged, index) + } + } + + var caseIndices: CaseIndexArray { CaseIndexArray(switchEnum: self) } + + var cases: Zip2Sequence { + zip(caseIndices, successors) + } + + // This does not handle the special case where the default covers exactly + // the "missing" case. + public func getUniqueSuccessor(forCaseIndex: Int) -> BasicBlock? { + cases.first(where: { $0.0 == forCaseIndex })?.1 + } + + // This does not handle the special case where the default covers exactly + // the "missing" case. + public func getUniqueCase(forSuccessor: BasicBlock) -> Int? { + cases.first(where: { $0.1 == forSuccessor })?.0 + } +} + +final public class SwitchEnumAddrInst : TermInst { +} + +final public class DynamicMethodBranchInst : TermInst { +} + +final public class AwaitAsyncContinuationInst : TermInst, UnaryInstruction { +} + +final public class CheckedCastBranchInst : TermInst, UnaryInstruction { +} + +final public class CheckedCastAddrBranchInst : TermInst, UnaryInstruction { +} + +final public class CheckedCastValueBranchInst : TermInst, UnaryInstruction { +} + diff --git a/libswift/Sources/SIL/Location.swift b/libswift/Sources/SIL/Location.swift new file mode 100644 index 0000000000000..fbfc75f1e2def --- /dev/null +++ b/libswift/Sources/SIL/Location.swift @@ -0,0 +1,17 @@ +//===--- Location.swift - Source location ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +public struct Location { + let bridgedLocation: BridgedLocation +} diff --git a/libswift/Sources/SIL/Operand.swift b/libswift/Sources/SIL/Operand.swift new file mode 100644 index 0000000000000..9d09dc85d2f36 --- /dev/null +++ b/libswift/Sources/SIL/Operand.swift @@ -0,0 +1,117 @@ +//===--- Operand.swift - Instruction operands -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +/// An operand of an instruction. +public struct Operand : CustomStringConvertible, CustomReflectable { + fileprivate let bridged: BridgedOperand + + init(_ bridged: BridgedOperand) { + self.bridged = bridged + } + + public var value: Value { + let v = Operand_getValue(bridged).getAs(AnyObject.self) + switch v { + case let inst as SingleValueInstruction: + return inst + case let arg as Argument: + return arg + case let mvr as MultipleValueInstructionResult: + return mvr + case let undef as Undef: + return undef + default: + fatalError("unknown Value type") + } + } + + public static func ==(lhs: Operand, rhs: Operand) -> Bool { + return lhs.bridged.op == rhs.bridged.op + } + + public var instruction: Instruction { + return Operand_getUser(bridged).instruction + } + + public var index: Int { instruction.operands.getIndex(of: self) } + + public var description: String { "operand #\(index) of \(instruction)" } + + public var customMirror: Mirror { Mirror(self, children: []) } +} + +public struct OperandArray : RandomAccessCollection, CustomReflectable { + private let opArray: BridgedArrayRef + + init(opArray: BridgedArrayRef) { + self.opArray = opArray + } + + public var startIndex: Int { return 0 } + public var endIndex: Int { return Int(opArray.numElements) } + + public subscript(_ index: Int) -> Operand { + precondition(index >= 0 && index < endIndex) + return Operand(BridgedOperand(op: opArray.data + index &* BridgedOperandSize)) + } + + public func getIndex(of operand: Operand) -> Int { + let idx = (operand.bridged.op - UnsafeRawPointer(opArray.data)) / + BridgedOperandSize + precondition(self[idx].bridged.op == operand.bridged.op) + return idx + } + + public var customMirror: Mirror { + let c: [Mirror.Child] = map { (label: nil, value: $0.value) } + return Mirror(self, children: c) + } +} + +public struct UseList : Sequence, CustomReflectable { + public struct Iterator : IteratorProtocol { + var currentOpPtr: UnsafeRawPointer? + + public mutating func next() -> Operand? { + if let opPtr = currentOpPtr { + let bridged = BridgedOperand(op: opPtr) + currentOpPtr = Operand_nextUse(bridged).op + return Operand(bridged) + } + return nil + } + } + + private let firstOpPtr: UnsafeRawPointer? + + init(_ firstOpPtr: OptionalBridgedOperand) { + self.firstOpPtr = firstOpPtr.op + } + + public var isSingleUse: Bool { + if let opPtr = firstOpPtr { + return Operand_nextUse(BridgedOperand(op: opPtr)).op == nil + } + return false + } + + public func makeIterator() -> Iterator { + return Iterator(currentOpPtr: firstOpPtr) + } + + public var customMirror: Mirror { + let c: [Mirror.Child] = map { (label: "use", value: $0) } + return Mirror(self, children: c) + } +} diff --git a/libswift/Sources/SIL/Registration.swift b/libswift/Sources/SIL/Registration.swift new file mode 100644 index 0000000000000..d0a09e4ac117a --- /dev/null +++ b/libswift/Sources/SIL/Registration.swift @@ -0,0 +1,123 @@ +//===--- Registration.swift - register SIL classes ------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +private func register(_ cl: T.Type) { + String(describing: cl).withBridgedStringRef { nameStr in + let metatype = unsafeBitCast(cl, to: SwiftMetatype.self) + registerBridgedClass(nameStr, metatype) + } +} + +public func registerSILClasses() { + register(Function.self) + register(BasicBlock.self) + register(GlobalVariable.self) + + // The "unimplemented" registrations must be done before all other node + // registrations. In the order from super -> sub class. + register(UnimplementedInstruction.self) + register(UnimplementedSingleValueInst.self) + register(UnimplementedRefCountingInst.self) + register(MultipleValueInstructionResult.self) + + register(Undef.self) + register(PlaceholderValue.self) + + register(FunctionArgument.self) + register(BlockArgument.self) + + register(StoreInst.self) + register(CopyAddrInst.self) + register(EndAccessInst.self) + register(EndBorrowInst.self) + register(DeallocStackInst.self) + register(CondFailInst.self) + register(FixLifetimeInst.self) + register(DebugValueInst.self) + register(UnconditionalCheckedCastAddrInst.self) + register(SetDeallocatingInst.self) + register(DeallocRefInst.self) + register(StrongRetainInst.self) + register(RetainValueInst.self) + register(StrongReleaseInst.self) + register(ReleaseValueInst.self) + register(DestroyValueInst.self) + register(DestroyAddrInst.self) + register(LoadInst.self) + register(LoadBorrowInst.self) + register(BuiltinInst.self) + register(UpcastInst.self) + register(UncheckedRefCastInst.self) + register(RawPointerToRefInst.self) + register(AddressToPointerInst.self) + register(InitExistentialRefInst.self) + register(OpenExistentialRefInst.self) + register(InitExistentialMetatypeInst.self) + register(OpenExistentialMetatypeInst.self) + register(ValueMetatypeInst.self) + register(ExistentialMetatypeInst.self) + register(GlobalAddrInst.self) + register(GlobalValueInst.self) + register(TupleInst.self) + register(TupleExtractInst.self) + register(TupleElementAddrInst.self) + register(StructInst.self) + register(StructExtractInst.self) + register(StructElementAddrInst.self) + register(EnumInst.self) + register(UncheckedEnumDataInst.self) + register(RefElementAddrInst.self) + register(RefTailAddrInst.self) + register(UnconditionalCheckedCastInst.self) + register(UnconditionalCheckedCastValueInst.self) + register(ConvertFunctionInst.self) + register(ThinToThickFunctionInst.self) + register(ObjCExistentialMetatypeToObjectInst.self) + register(ObjCMetatypeToObjectInst.self) + register(ValueToBridgeObjectInst.self) + register(BeginAccessInst.self) + register(BeginBorrowInst.self) + register(CopyValueInst.self) + register(ClassifyBridgeObjectInst.self) + register(ApplyInst.self) + register(PartialApplyInst.self) + register(AllocStackInst.self) + register(AllocRefInst.self) + register(AllocRefDynamicInst.self) + register(AllocValueBufferInst.self) + register(AllocBoxInst.self) + register(AllocExistentialBoxInst.self) + + register(BeginCOWMutationInst.self) + register(DestructureStructInst.self) + register(DestructureTupleInst.self) + register(BeginApplyInst.self) + + register(UnreachableInst.self) + register(ReturnInst.self) + register(ThrowInst.self) + register(YieldInst.self) + register(UnwindInst.self) + register(TryApplyInst.self) + register(BranchInst.self) + register(CondBranchInst.self) + register(SwitchValueInst.self) + register(SwitchEnumInst.self) + register(SwitchEnumAddrInst.self) + register(DynamicMethodBranchInst.self) + register(AwaitAsyncContinuationInst.self) + register(CheckedCastBranchInst.self) + register(CheckedCastAddrBranchInst.self) + register(CheckedCastValueBranchInst.self) +} diff --git a/libswift/Sources/SIL/StackList.swift b/libswift/Sources/SIL/StackList.swift new file mode 100644 index 0000000000000..95e73960607fc --- /dev/null +++ b/libswift/Sources/SIL/StackList.swift @@ -0,0 +1,137 @@ +//===--- StackList.swift - defines the StackList data structure -----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +/// A very efficient implementation of a stack, which can also be iterated over. +/// +/// A StackList is the best choice for things like worklists, etc., if no random +/// access is needed. +/// Compared to Array, it does not require any memory allocations, because it +/// uses the bump pointer allocator of the SILModule. +/// All operations have (almost) zero cost. +/// +/// Ideally this would be a move-only type. Until then, only pass StackLists as +/// inout! +/// Note: it is required to manually remove all elements - either by pop() or +/// removeAll(). +public struct StackList : Sequence, CustomReflectable { + + private let context: BridgedPassContext + private var firstSlab = BridgedSlab(data: nil) + private var lastSlab = BridgedSlab(data: nil) + private var endIndex: Int = slabCapacity + + private static var slabCapacity: Int { + BridgedSlabCapacity / MemoryLayout.size + } + + private static func bind(_ slab: BridgedSlab) -> UnsafeMutablePointer { + return slab.data!.bindMemory(to: Element.self, capacity: StackList.slabCapacity) + } + + public struct Iterator : IteratorProtocol { + var slab: BridgedSlab + var index: Int + let lastSlab: BridgedSlab + let endIndex: Int + + public mutating func next() -> Element? { + let end = (slab.data == lastSlab.data ? endIndex : slabCapacity) + if index < end { + let elem = StackList.bind(slab)[index] + index += 1 + + if index >= end && slab.data != lastSlab.data { + slab = PassContext_getNextSlab(slab) + index = 0 + } + return elem + } + return nil + } + } + + public init(context: BridgedPassContext) { self.context = context } + + public func makeIterator() -> Iterator { + return Iterator(slab: firstSlab, index: 0, lastSlab: lastSlab, endIndex: endIndex) + } + + public var first: Element? { + if isEmpty { + return nil + } + return StackList.bind(firstSlab)[0] + } + + public var last: Element? { + if isEmpty { + return nil + } + return StackList.bind(lastSlab)[endIndex - 1] + } + + public mutating func push(_ element: Element) { + if endIndex >= StackList.slabCapacity { + lastSlab = PassContext_allocSlab(context, lastSlab) + if firstSlab.data == nil { + firstSlab = lastSlab + } + endIndex = 0 + } + (StackList.bind(lastSlab) + endIndex).initialize(to: element) + endIndex += 1 + } + + public var isEmpty: Bool { return firstSlab.data == nil } + + public mutating func pop() -> Element? { + if isEmpty { + return nil + } + assert(endIndex > 0) + endIndex -= 1 + let elem = (StackList.bind(lastSlab) + endIndex).move() + + if endIndex == 0 { + if lastSlab.data == firstSlab.data { + _ = PassContext_freeSlab(context, lastSlab) + firstSlab.data = nil + lastSlab.data = nil + } else { + lastSlab = PassContext_freeSlab(context, lastSlab) + } + endIndex = StackList.slabCapacity + } + + return elem + } + + public mutating func removeAll() { + if isEmpty { + return + } + while lastSlab.data != firstSlab.data { + lastSlab = PassContext_freeSlab(context, lastSlab) + } + _ = PassContext_freeSlab(context, lastSlab) + firstSlab.data = nil + lastSlab.data = nil + endIndex = StackList.slabCapacity + } + + public var customMirror: Mirror { + let c: [Mirror.Child] = map { (label: nil, value: $0) } + return Mirror(self, children: c) + } +} diff --git a/libswift/Sources/SIL/Type.swift b/libswift/Sources/SIL/Type.swift new file mode 100644 index 0000000000000..a102b367113e1 --- /dev/null +++ b/libswift/Sources/SIL/Type.swift @@ -0,0 +1,20 @@ +//===--- Type.swift - Value type ------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +public struct Type { + var bridged: BridgedType + + public var isAddress: Bool { SILType_isAddress(bridged) != 0 } + public var isObject: Bool { !isAddress } +} diff --git a/libswift/Sources/SIL/Utils.swift b/libswift/Sources/SIL/Utils.swift new file mode 100644 index 0000000000000..6444c182e2c0c --- /dev/null +++ b/libswift/Sources/SIL/Utils.swift @@ -0,0 +1,123 @@ +//===--- Utils.swift - some SIL utilities ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +//===----------------------------------------------------------------------===// +// Lists +//===----------------------------------------------------------------------===// + +public protocol ListNode : AnyObject { + associatedtype Element + var next: Element? { get } + var previous: Element? { get } +} + +public struct List : + Sequence, IteratorProtocol, CustomReflectable + where NodeType.Element == NodeType { + private var currentNode: NodeType? + + public init(startAt: NodeType?) { currentNode = startAt } + + public mutating func next() -> NodeType? { + if let node = currentNode { + currentNode = node.next + return node + } + return nil + } + + public var customMirror: Mirror { + let c: [Mirror.Child] = map { (label: nil, value: $0) } + return Mirror(self, children: c) + } +} + +public struct ReverseList : + Sequence, IteratorProtocol, CustomReflectable + where NodeType.Element == NodeType { + private var currentNode: NodeType? + + public init(startAt: NodeType?) { currentNode = startAt } + + public mutating func next() -> NodeType? { + if let node = currentNode { + currentNode = node.previous + return node + } + return nil + } + + public var customMirror: Mirror { + let c: [Mirror.Child] = map { (label: nil, value: $0) } + return Mirror(self, children: c) + } +} + +//===----------------------------------------------------------------------===// +// Bridging Utilities +//===----------------------------------------------------------------------===// + +extension BridgedStringRef { + public var string: String { + let buffer = UnsafeBufferPointer(start: data, count: Int(length)) + return String(decoding: buffer, as: UTF8.self) + } + + func takeString() -> String { + let str = string + freeBridgedStringRef(self) + return str + } +} + +extension String { + public func withBridgedStringRef(_ c: (BridgedStringRef) -> T) -> T { + var str = self + return str.withUTF8 { buffer in + return c(BridgedStringRef(data: buffer.baseAddress, length: buffer.count)) + } + } +} + +extension Array where Element == Value { + public func withBridgedValues(_ c: (BridgedValueArray) -> T) -> T { + return self.withUnsafeBytes { valPtr in + assert(valPtr.count == self.count * 16) + return c(BridgedValueArray(data: valPtr.baseAddress, count: self.count)) + } + } +} + +public typealias SwiftObject = UnsafeMutablePointer + +extension UnsafeMutablePointer where Pointee == BridgedSwiftObject { + init(_ object: T) { + let ptr = Unmanaged.passUnretained(object).toOpaque() + self = ptr.bindMemory(to: BridgedSwiftObject.self, capacity: 1) + } + + func getAs(_ objectType: T.Type) -> T { + return Unmanaged.fromOpaque(self).takeUnretainedValue() + } +} + +extension Optional where Wrapped == UnsafeMutablePointer { + func getAs(_ objectType: T.Type) -> T? { + if let pointer = self { + return Unmanaged.fromOpaque(pointer).takeUnretainedValue() + } + return nil + } +} + diff --git a/libswift/Sources/SIL/Value.swift b/libswift/Sources/SIL/Value.swift new file mode 100644 index 0000000000000..097bbfb58c7d1 --- /dev/null +++ b/libswift/Sources/SIL/Value.swift @@ -0,0 +1,60 @@ +//===--- Value.swift - the Value protocol ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +import SILBridging + +public protocol Value : AnyObject, CustomStringConvertible { + var uses: UseList { get } + var type: Type { get } + var definingInstruction: Instruction? { get } +} + +extension Value { + public var description: String { + SILNode_debugDescription(bridgedNode).takeString() + } + + public var uses: UseList { + return UseList(SILValue_firstUse(bridged)) + } + + public var type: Type { + return Type(bridged: SILValue_getType(bridged)) + } + + public var hashable: HashableValue { ObjectIdentifier(self) } + + public var bridged: BridgedValue { + BridgedValue(obj: SwiftObject(self as AnyObject)) + } + var bridgedNode: BridgedNode { + BridgedNode(obj: SwiftObject(self as AnyObject)) + } +} + +public typealias HashableValue = ObjectIdentifier + +public func ==(_ lhs: Value, _ rhs: Value) -> Bool { + return lhs === rhs +} + +extension BridgedValue { + func getAs(_ valueType: T.Type) -> T { obj.getAs(T.self) } +} + +final class Undef : Value { + public var definingInstruction: Instruction? { nil } +} + +final class PlaceholderValue : Value { + public var definingInstruction: Instruction? { nil } +} diff --git a/stdlib/CMakeLists.txt b/stdlib/CMakeLists.txt index e21f36661363e..a2e5f71c3da43 100644 --- a/stdlib/CMakeLists.txt +++ b/stdlib/CMakeLists.txt @@ -44,6 +44,14 @@ else() set(SWIFT_BUILD_TEST_SUPPORT_MODULES_default FALSE) endif() +if("${SWIFT_HOST_VARIANT_SDK}" MATCHES "(OSX|IOS*|TVOS*|WATCHOS*)") + set(SWIFT_STDLIB_ENABLE_PRESPECIALIZATION_default TRUE) +elseif("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "LINUX") + set(SWIFT_STDLIB_ENABLE_PRESPECIALIZATION_default TRUE) +else() + set(SWIFT_STDLIB_ENABLE_PRESPECIALIZATION_default FALSE) +endif() + # # User-configurable options for the standard library. # @@ -67,6 +75,10 @@ option(SWIFT_RUNTIME_MACHO_NO_DYLD "Build stdlib assuming the runtime environment uses Mach-O but does not support dynamic modules." FALSE) +option(SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC + "Build stdlib assuming the Darwin build of stdlib can use extended libmalloc APIs" + ) + option(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME "Build the standard libraries assuming that they will be used in an environment with only a single thread." FALSE) @@ -79,6 +91,13 @@ option(SWIFT_BUILD_TEST_SUPPORT_MODULES "Whether to build StdlibUnittest and other test support modules. Defaults to On when SWIFT_BUILD_SDK_OVERLAY is On, or when SWIFT_INCLUDE_TESTS is On." "${SWIFT_BUILD_TEST_SUPPORT_MODULES_default}") +option(SWIFT_FREESTANDING_FLAVOR + "When building the FREESTANDING stdlib, which build style to use (options: apple, linux)") + +option(SWIFT_STDLIB_ENABLE_PRESPECIALIZATION + "Should stdlib be built with generic metadata prespecialization enabled. Defaults to On on Darwin and on " + "${SWIFT_STDLIB_ENABLE_PRESPECIALIZATION_default}") + set(SWIFT_STDLIB_ENABLE_LTO OFF CACHE STRING "Build Swift stdlib with LTO. One must specify the form of LTO by setting this to one of: 'full', 'thin'. This option only affects the standard library and runtime, not tools.") @@ -144,14 +163,13 @@ endif() # built and cause link failures for mismatches. Without linking that library, # we get link failures regardless, so instead, this just disables the checks. add_compile_definitions($<$,$>:LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING=1>) -include_directories(BEFORE ${CMAKE_PROJECT_SOURCE_DIR}/include) set(SWIFT_STDLIB_LIBRARY_BUILD_TYPES) if(SWIFT_BUILD_DYNAMIC_STDLIB) list(APPEND SWIFT_STDLIB_LIBRARY_BUILD_TYPES SHARED) endif() if(SWIFT_BUILD_STATIC_STDLIB) - list_intersect("${SWIFT_APPLE_PLATFORMS}" "${SWIFT_SDKS}" building_darwin_sdks) + list_intersect("${SWIFT_DARWIN_PLATFORMS}" "${SWIFT_SDKS}" building_darwin_sdks) if(building_darwin_sdks) message(SEND_ERROR "cannot build static standard library for Darwin SDKs") else() diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index da301dc867db6..4d8a588a5d4e6 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -49,7 +49,7 @@ function(_add_target_variant_c_compile_link_flags) set(result ${${CFLAGS_RESULT_VAR_NAME}}) - if("${CFLAGS_SDK}" IN_LIST SWIFT_APPLE_PLATFORMS) + if("${CFLAGS_SDK}" IN_LIST SWIFT_DARWIN_PLATFORMS) # Check if there's a specific OS deployment version needed for this invocation if("${CFLAGS_SDK}" STREQUAL "OSX") if(DEFINED maccatalyst_build_flavor) @@ -102,7 +102,7 @@ function(_add_target_variant_c_compile_link_flags) endif() endif() - if("${CFLAGS_SDK}" IN_LIST SWIFT_APPLE_PLATFORMS) + if("${CFLAGS_SDK}" IN_LIST SWIFT_DARWIN_PLATFORMS) # We collate -F with the framework path to avoid unwanted deduplication # of options by target_compile_options -- this way no undesired # side effects are introduced should a new search path be added. @@ -138,6 +138,13 @@ function(_add_target_variant_c_compile_flags) set(result ${${CFLAGS_RESULT_VAR_NAME}}) + if ("${CFLAGS_ARCH}" STREQUAL "arm64" OR + "${CFLAGS_ARCH}" STREQUAL "arm64_32") + if (SWIFT_ENABLE_GLOBAL_ISEL_ARM64) + list(APPEND result "-fglobal-isel") + endif() + endif() + _add_target_variant_c_compile_link_flags( SDK "${CFLAGS_SDK}" ARCH "${CFLAGS_ARCH}" @@ -312,6 +319,12 @@ function(_add_target_variant_c_compile_flags) list(APPEND result "-DSWIFT_RUNTIME_MACHO_NO_DYLD") endif() + if(SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC) + list(APPEND result "-DSWIFT_STDLIB_HAS_DARWIN_LIBMALLOC=1") + else() + list(APPEND result "-DSWIFT_STDLIB_HAS_DARWIN_LIBMALLOC=0") + endif() + if(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) list(APPEND result "-DSWIFT_STDLIB_SINGLE_THREADED_RUNTIME") endif() @@ -614,7 +627,6 @@ function(_add_swift_target_library_single target name) OBJECT_LIBRARY SHARED STATIC - TARGET_LIBRARY INSTALL_WITH_SHARED) set(SWIFTLIB_SINGLE_single_parameter_options ARCHITECTURE @@ -627,6 +639,7 @@ function(_add_swift_target_library_single target name) SDK DEPLOYMENT_VERSION_MACCATALYST MACCATALYST_BUILD_FLAVOR + BACK_DEPLOYMENT_LIBRARY ENABLE_LTO) set(SWIFTLIB_SINGLE_multiple_parameter_options C_COMPILE_FLAGS @@ -729,7 +742,7 @@ function(_add_swift_target_library_single target name) handle_gyb_sources( gyb_dependency_targets SWIFTLIB_SINGLE_GYB_SOURCES - "${SWIFTLIB_SINGLE_ARCHITECTURE}") + ARCH "${SWIFTLIB_SINGLE_ARCHITECTURE}") set(SWIFTLIB_SINGLE_SOURCES ${SWIFTLIB_SINGLE_SOURCES} ${SWIFTLIB_SINGLE_GYB_SOURCES}) endif() @@ -758,6 +771,13 @@ function(_add_swift_target_library_single target name) -libc;${SWIFT_STDLIB_MSVC_RUNTIME_LIBRARY}) endif() + # Don't install the Swift module content for back-deployment libraries. + if (SWIFTLIB_SINGLE_BACK_DEPLOYMENT_LIBRARY) + set(install_in_component "never_install") + else() + set(install_in_component "${SWIFTLIB_SINGLE_INSTALL_IN_COMPONENT}") + endif() + # FIXME: don't actually depend on the libraries in SWIFTLIB_SINGLE_LINK_LIBRARIES, # just any swiftmodule files that are associated with them. handle_swift_sources( @@ -783,7 +803,7 @@ function(_add_swift_target_library_single target name) ${embed_bitcode_arg} ${SWIFTLIB_SINGLE_STATIC_keyword} ENABLE_LTO "${SWIFTLIB_SINGLE_ENABLE_LTO}" - INSTALL_IN_COMPONENT "${SWIFTLIB_SINGLE_INSTALL_IN_COMPONENT}" + INSTALL_IN_COMPONENT "${install_in_component}" MACCATALYST_BUILD_FLAVOR "${SWIFTLIB_SINGLE_MACCATALYST_BUILD_FLAVOR}") add_swift_source_group("${SWIFTLIB_SINGLE_EXTERNAL_SOURCES}") @@ -819,7 +839,7 @@ function(_add_swift_target_library_single target name) endif() # Only build the modules for any arch listed in the *_MODULE_ARCHITECTURES. - if(SWIFTLIB_SINGLE_SDK IN_LIST SWIFT_APPLE_PLATFORMS + if(SWIFTLIB_SINGLE_SDK IN_LIST SWIFT_DARWIN_PLATFORMS AND SWIFTLIB_SINGLE_ARCHITECTURE IN_LIST SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_MODULE_ARCHITECTURES) # Create dummy target to hook up the module target dependency. add_custom_target("${target}" @@ -842,7 +862,7 @@ function(_add_swift_target_library_single target name) endforeach() set(SWIFTLIB_SINGLE_XCODE_WORKAROUND_SOURCES) - if(XCODE AND SWIFTLIB_SINGLE_TARGET_LIBRARY) + if(XCODE) set(SWIFTLIB_SINGLE_XCODE_WORKAROUND_SOURCES # Note: the dummy.cpp source file provides no definitions. However, # it forces Xcode to properly link the static library. @@ -865,8 +885,7 @@ function(_add_swift_target_library_single target name) target_include_directories(${target} BEFORE PRIVATE ${SWIFT_SOURCE_DIR}/stdlib/include) if(("${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "ELF" OR - "${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "COFF") AND - SWIFTLIB_SINGLE_TARGET_LIBRARY) + "${SWIFT_SDK_${SWIFTLIB_SINGLE_SDK}_OBJECT_FORMAT}" STREQUAL "COFF")) if("${libkind}" STREQUAL "SHARED" AND NOT SWIFTLIB_SINGLE_NOSWIFTRT) # TODO(compnerd) switch to the generator expression when cmake is upgraded # to a version which supports it. @@ -929,29 +948,34 @@ function(_add_swift_target_library_single target name) SUFFIX ${LLVM_PLUGIN_EXT}) endif() - if(SWIFTLIB_SINGLE_TARGET_LIBRARY) - # Install runtime libraries to lib/swift instead of lib. This works around - # the fact that -isysroot prevents linking to libraries in the system - # /usr/lib if Swift is installed in /usr. - set_target_properties("${target}" PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${SWIFTLIB_DIR}/${SWIFTLIB_SINGLE_SUBDIR} - ARCHIVE_OUTPUT_DIRECTORY ${SWIFTLIB_DIR}/${SWIFTLIB_SINGLE_SUBDIR}) - if(SWIFTLIB_SINGLE_SDK STREQUAL WINDOWS AND SWIFTLIB_SINGLE_IS_STDLIB_CORE - AND libkind STREQUAL SHARED) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${SWIFTLIB_DIR}/${SWIFTLIB_SINGLE_SUBDIR}) - endif() - - foreach(config ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER ${config} config_upper) - escape_path_for_xcode("${config}" "${SWIFTLIB_DIR}" config_lib_dir) - set_target_properties(${target} PROPERTIES - LIBRARY_OUTPUT_DIRECTORY_${config_upper} ${config_lib_dir}/${SWIFTLIB_SINGLE_SUBDIR} - ARCHIVE_OUTPUT_DIRECTORY_${config_upper} ${config_lib_dir}/${SWIFTLIB_SINGLE_SUBDIR}) - endforeach() - endif() + # For back-deployed libraries, install into lib/swift-. + if (SWIFTLIB_SINGLE_BACK_DEPLOYMENT_LIBRARY) + set(swiftlib_prefix "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib/swift-${SWIFTLIB_SINGLE_BACK_DEPLOYMENT_LIBRARY}") + else() + set(swiftlib_prefix ${SWIFTLIB_DIR}) + endif() + + # Install runtime libraries to lib/swift instead of lib. This works around + # the fact that -isysroot prevents linking to libraries in the system + # /usr/lib if Swift is installed in /usr. + set_target_properties("${target}" PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${swiftlib_prefix}/${SWIFTLIB_SINGLE_SUBDIR} + ARCHIVE_OUTPUT_DIRECTORY ${swiftlib_prefix}/${SWIFTLIB_SINGLE_SUBDIR}) + if(SWIFTLIB_SINGLE_SDK STREQUAL WINDOWS AND SWIFTLIB_SINGLE_IS_STDLIB_CORE + AND libkind STREQUAL SHARED) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${swiftlib_prefix}/${SWIFTLIB_SINGLE_SUBDIR}) + endif() + + foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${config} config_upper) + escape_path_for_xcode("${config}" "${swiftlib_prefix}" config_lib_dir) + set_target_properties(${target} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY_${config_upper} ${config_lib_dir}/${SWIFTLIB_SINGLE_SUBDIR} + ARCHIVE_OUTPUT_DIRECTORY_${config_upper} ${config_lib_dir}/${SWIFTLIB_SINGLE_SUBDIR}) + endforeach() - if(SWIFTLIB_SINGLE_SDK IN_LIST SWIFT_APPLE_PLATFORMS) + if(SWIFTLIB_SINGLE_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) set(install_name_dir "@rpath") if(SWIFTLIB_SINGLE_IS_STDLIB) @@ -965,11 +989,6 @@ function(_add_swift_target_library_single target name) endif() endif() - # Always use @rpath for XCTest - if(module_name STREQUAL "XCTest") - set(install_name_dir "@rpath") - endif() - if(SWIFTLIB_SINGLE_DARWIN_INSTALL_NAME_DIR) set(install_name_dir "${SWIFTLIB_SINGLE_DARWIN_INSTALL_NAME_DIR}") endif() @@ -990,9 +1009,7 @@ function(_add_swift_target_library_single target name) # for an Android cross-build from a macOS host. Construct the proper linker # flags manually in add_swift_target_library instead, see there with # variable `swiftlib_link_flags_all`. - if(SWIFTLIB_SINGLE_TARGET_LIBRARY) - set_target_properties("${target}" PROPERTIES NO_SONAME TRUE) - endif() + set_target_properties("${target}" PROPERTIES NO_SONAME TRUE) # Only set the install RPATH if the toolchain and stdlib will be in Termux # or some other native sysroot on Android. if(NOT "${SWIFT_ANDROID_NATIVE_SYSROOT}" STREQUAL "") @@ -1000,6 +1017,10 @@ function(_add_swift_target_library_single target name) PROPERTIES INSTALL_RPATH "$ORIGIN") endif() + elseif("${SWIFTLIB_SINGLE_SDK}" STREQUAL "OPENBSD") + set_target_properties("${target}" + PROPERTIES + INSTALL_RPATH "$ORIGIN") endif() set_target_properties("${target}" PROPERTIES BUILD_WITH_INSTALL_RPATH YES) @@ -1055,11 +1076,9 @@ function(_add_swift_target_library_single target name) # Don't build standard libraries by default. We will enable building # standard libraries that the user requested; the rest can be built on-demand. - if(SWIFTLIB_SINGLE_TARGET_LIBRARY) - foreach(t "${target}" ${target_static}) - set_target_properties(${t} PROPERTIES EXCLUDE_FROM_ALL TRUE) - endforeach() - endif() + foreach(t "${target}" ${target_static}) + set_target_properties(${t} PROPERTIES EXCLUDE_FROM_ALL TRUE) + endforeach() # Handle linking and dependencies. add_dependencies_multiple_targets( @@ -1134,18 +1153,9 @@ function(_add_swift_target_library_single target name) list(APPEND library_search_directories "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}/usr/lib/swift") # Add variant-specific flags. - if(SWIFTLIB_SINGLE_TARGET_LIBRARY) - set(build_type "${SWIFT_STDLIB_BUILD_TYPE}") - set(enable_assertions "${SWIFT_STDLIB_ASSERTIONS}") - else() - set(build_type "${CMAKE_BUILD_TYPE}") - set(enable_assertions "${LLVM_ENABLE_ASSERTIONS}") - set(analyze_code_coverage "${SWIFT_ANALYZE_CODE_COVERAGE}") - endif() - - if (NOT SWIFTLIB_SINGLE_TARGET_LIBRARY) - set(lto_type "${SWIFT_STDLIB_ENABLE_LTO}") - endif() + set(build_type "${SWIFT_STDLIB_BUILD_TYPE}") + set(enable_assertions "${SWIFT_STDLIB_ASSERTIONS}") + set(lto_type "${SWIFT_STDLIB_ENABLE_LTO}") _add_target_variant_c_compile_flags( SDK "${SWIFTLIB_SINGLE_SDK}" @@ -1189,7 +1199,7 @@ function(_add_swift_target_library_single target name) # Configure plist creation for OS X. set(PLIST_INFO_PLIST "Info.plist" CACHE STRING "Plist name") - if("${SWIFTLIB_SINGLE_SDK}" IN_LIST SWIFT_APPLE_PLATFORMS AND SWIFTLIB_SINGLE_IS_STDLIB) + if("${SWIFTLIB_SINGLE_SDK}" IN_LIST SWIFT_DARWIN_PLATFORMS AND SWIFTLIB_SINGLE_IS_STDLIB) set(PLIST_INFO_NAME ${name}) set(PLIST_INFO_UTI "com.apple.dt.runtime.${name}") set(PLIST_INFO_VERSION "${SWIFT_VERSION}") @@ -1246,7 +1256,7 @@ function(_add_swift_target_library_single target name) ${c_compile_flags}) target_link_options(${target} PRIVATE ${link_flags}) - if(${SWIFTLIB_SINGLE_SDK} IN_LIST SWIFT_APPLE_PLATFORMS) + if(${SWIFTLIB_SINGLE_SDK} IN_LIST SWIFT_DARWIN_PLATFORMS) target_link_options(${target} PRIVATE "LINKER:-compatibility_version,1") if(SWIFT_COMPILER_VERSION) @@ -1348,6 +1358,16 @@ function(_add_swift_target_library_single target name) ${library_search_directories}) target_link_libraries("${target_static}" PRIVATE ${SWIFTLIB_SINGLE_PRIVATE_LINK_LIBRARIES}) + + # Force executables linker language to be CXX so that we do not link using the + # host toolchain swiftc. + if("${SWIFTLIB_SINGLE_SDK}" STREQUAL "ANDROID") + set_property(TARGET "${target_static}" PROPERTY + LINKER_LANGUAGE "C") + else() + set_property(TARGET "${target_static}" PROPERTY + LINKER_LANGUAGE "CXX") + endif() endif() # Do not add code here. @@ -1384,6 +1404,7 @@ endfunction() # DEPLOYMENT_VERSION_TVOS version # DEPLOYMENT_VERSION_WATCHOS version # MACCATALYST_BUILD_FLAVOR flavor +# BACK_DEPLOYMENT_LIBRARY version # source1 [source2 source3 ...]) # # name @@ -1479,6 +1500,9 @@ endfunction() # IS_SDK_OVERLAY # Treat the library as a part of the Swift SDK overlay. # +# BACK_DEPLOYMENT_LIBRARY +# Treat this as a back-deployment library to the given Swift version +# # INSTALL_IN_COMPONENT comp # The Swift installation component that this library belongs to. # @@ -1527,7 +1551,8 @@ function(add_swift_target_library name) INSTALL_IN_COMPONENT DARWIN_INSTALL_NAME_DIR DEPLOYMENT_VERSION_MACCATALYST - MACCATALYST_BUILD_FLAVOR) + MACCATALYST_BUILD_FLAVOR + BACK_DEPLOYMENT_LIBRARY) set(SWIFTLIB_multiple_parameter_options C_COMPILE_FLAGS DEPENDS @@ -1599,7 +1624,7 @@ function(add_swift_target_library name) if("${SWIFTLIB_TARGET_SDKS}" STREQUAL "") set(SWIFTLIB_TARGET_SDKS ${SWIFT_SDKS}) endif() - list_replace(SWIFTLIB_TARGET_SDKS ALL_APPLE_PLATFORMS "${SWIFT_APPLE_PLATFORMS}") + list_replace(SWIFTLIB_TARGET_SDKS ALL_APPLE_PLATFORMS "${SWIFT_DARWIN_PLATFORMS}") # All Swift code depends on the standard library, except for the standard # library itself. @@ -1650,6 +1675,16 @@ function(add_swift_target_library name) "-Xfrontend;-disable-implicit-concurrency-module-import") endif() + # Turn off implicit import of _Distributed when building libraries + if(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) + list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS + "-Xfrontend;-disable-implicit-distributed-module-import") + endif() + + if(SWIFTLIB_IS_STDLIB AND SWIFT_STDLIB_ENABLE_PRESPECIALIZATION) + list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS "-Xfrontend;-prespecialize-generic-metadata") + endif() + # If we are building this library for targets, loop through the various # SDKs building the variants of this library. list_intersect( @@ -1807,7 +1842,7 @@ function(add_swift_target_library name) list(APPEND swiftlib_link_flags_all "-Wl,-z,defs") endif() # Setting back linker flags which are not supported when making Android build on macOS cross-compile host. - if(SWIFTLIB_SHARED AND sdk IN_LIST SWIFT_APPLE_PLATFORMS) + if(SWIFTLIB_SHARED AND sdk IN_LIST SWIFT_DARWIN_PLATFORMS) list(APPEND swiftlib_link_flags_all "-dynamiclib -Wl,-headerpad_max_install_names") endif() @@ -1914,9 +1949,7 @@ function(add_swift_target_library name) # These paths must come before their normal counterparts so that when compiling # macCatalyst-only or unzippered-twin overlays the macCatalyst version # of a framework is found and not the Mac version. - if(maccatalyst_build_flavor STREQUAL "ios-like" - OR (name STREQUAL "swiftXCTest" - AND maccatalyst_build_flavor STREQUAL "zippered")) + if(maccatalyst_build_flavor STREQUAL "ios-like") # The path to find iOS-only frameworks (such as UIKit) under macCatalyst. set(ios_support_frameworks_path "${SWIFT_SDK_${sdk}_PATH}/System/iOSSupport/System/Library/Frameworks/") @@ -1929,7 +1962,7 @@ function(add_swift_target_library name) list(APPEND swiftlib_link_flags_all "-F${ios_support_frameworks_path}") endif() - if(sdk IN_LIST SWIFT_APPLE_PLATFORMS AND SWIFTLIB_IS_SDK_OVERLAY) + if(sdk IN_LIST SWIFT_DARWIN_PLATFORMS AND SWIFTLIB_IS_SDK_OVERLAY) set(swiftlib_swift_compile_private_frameworks_flag "-Fsystem" "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}/System/Library/PrivateFrameworks/") foreach(tbd_lib ${SWIFTLIB_SWIFT_MODULE_DEPENDS_FROM_SDK}) list(APPEND swiftlib_link_flags_all "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}/usr/lib/swift/libswift${tbd_lib}.tbd") @@ -1956,6 +1989,12 @@ function(add_swift_target_library name) list(APPEND swiftlib_link_flags_all "-Wl,-soname,lib${name}.so") endif() + if (SWIFTLIB_BACK_DEPLOYMENT_LIBRARY) + set(back_deployment_library_option BACK_DEPLOYMENT_LIBRARY ${SWIFTLIB_BACK_DEPLOYMENT_LIBRARY}) + else() + set(back_deployment_library_option) + endif() + # Add this library variant. _add_swift_target_library_single( ${variant_name} @@ -1965,7 +2004,6 @@ function(add_swift_target_library name) ${SWIFTLIB_OBJECT_LIBRARY_keyword} ${SWIFTLIB_INSTALL_WITH_SHARED_keyword} ${SWIFTLIB_SOURCES} - TARGET_LIBRARY MODULE_TARGETS ${module_variant_names} SDK ${sdk} ARCHITECTURE ${arch} @@ -1993,6 +2031,7 @@ function(add_swift_target_library name) DEPLOYMENT_VERSION_TVOS "${SWIFTLIB_DEPLOYMENT_VERSION_TVOS}" DEPLOYMENT_VERSION_WATCHOS "${SWIFTLIB_DEPLOYMENT_VERSION_WATCHOS}" MACCATALYST_BUILD_FLAVOR "${maccatalyst_build_flavor}" + ${back_deployment_library_option} ENABLE_LTO "${SWIFT_STDLIB_ENABLE_LTO}" GYB_SOURCES ${SWIFTLIB_GYB_SOURCES} ) @@ -2042,6 +2081,19 @@ function(add_swift_target_library name) list(APPEND THIN_INPUT_TARGETS ${VARIANT_NAME}) endif() endif() + + if(sdk IN_LIST SWIFT_APPLE_PLATFORMS) + # In the past, we relied on unsetting globally + # CMAKE_OSX_ARCHITECTURES to ensure that CMake would + # not add the -arch flag + # This is no longer the case when running on Apple Silicon, + # when CMake will enforce a default (see + # https://gitlab.kitware.com/cmake/cmake/-/merge_requests/5291) + set_property(TARGET ${VARIANT_NAME} PROPERTY OSX_ARCHITECTURES "${arch}") + if (SWIFTLIB_IS_STDLIB AND SWIFTLIB_STATIC) + set_property(TARGET ${VARIANT_NAME}-static PROPERTY OSX_ARCHITECTURES "${arch}") + endif() + endif() endforeach() # Configure module-only targets @@ -2080,6 +2132,9 @@ function(add_swift_target_library name) if("${sdk}" STREQUAL "WINDOWS") set(UNIVERSAL_LIBRARY_NAME "${SWIFTLIB_DIR}/${library_subdir}/${name}.dll") + elseif(SWIFTLIB_BACK_DEPLOYMENT_LIBRARY) + set(UNIVERSAL_LIBRARY_NAME + "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib/swift-${SWIFTLIB_BACK_DEPLOYMENT_LIBRARY}/${library_subdir}/${CMAKE_SHARED_LIBRARY_PREFIX}${name}${CMAKE_SHARED_LIBRARY_SUFFIX}") else() set(UNIVERSAL_LIBRARY_NAME "${SWIFTLIB_DIR}/${library_subdir}/${CMAKE_SHARED_LIBRARY_PREFIX}${name}${CMAKE_SHARED_LIBRARY_SUFFIX}") @@ -2137,7 +2192,7 @@ function(add_swift_target_library name) endif() set(optional_arg) - if(sdk IN_LIST SWIFT_APPLE_PLATFORMS) + if(sdk IN_LIST SWIFT_DARWIN_PLATFORMS) # Allow installation of stdlib without building all variants on Darwin. set(optional_arg "OPTIONAL") endif() @@ -2159,8 +2214,16 @@ function(add_swift_target_library name) # NOTE: ${UNIVERSAL_LIBRARY_NAME} is the output associated with the target # ${lipo_target} add_dependencies(${SWIFTLIB_INSTALL_IN_COMPONENT} ${lipo_target}) + + if (SWIFTLIB_BACK_DEPLOYMENT_LIBRARY) + # Back-deployment libraries get installed into a versioned directory. + set(install_dest "lib${LLVM_LIBDIR_SUFFIX}/${resource_dir}-${SWIFTLIB_BACK_DEPLOYMENT_LIBRARY}/${resource_dir_sdk_subdir}") + else() + set(install_dest "lib${LLVM_LIBDIR_SUFFIX}/${resource_dir}/${resource_dir_sdk_subdir}") + endif() + swift_install_in_component(FILES "${UNIVERSAL_LIBRARY_NAME}" - DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/${resource_dir}/${resource_dir_sdk_subdir}" + DESTINATION ${install_dest} COMPONENT "${SWIFTLIB_INSTALL_IN_COMPONENT}" PERMISSIONS ${file_permissions} "${optional_arg}") @@ -2397,7 +2460,7 @@ function(_add_swift_target_executable_single name) if (SWIFT_PARALLEL_LINK_JOBS) set_property(TARGET ${name} PROPERTY JOB_POOL_LINK swift_link_job_pool) endif() - if(${SWIFTEXE_SINGLE_SDK} IN_LIST SWIFT_APPLE_PLATFORMS) + if(${SWIFTEXE_SINGLE_SDK} IN_LIST SWIFT_DARWIN_PLATFORMS) set_target_properties(${name} PROPERTIES BUILD_WITH_INSTALL_RPATH YES INSTALL_RPATH "@executable_path/../lib/swift/${SWIFT_SDK_${SWIFTEXE_SINGLE_SDK}_LIB_SUBDIR}") @@ -2480,6 +2543,14 @@ function(add_swift_target_executable name) endif() if(${sdk} IN_LIST SWIFT_APPLE_PLATFORMS) + # In the past, we relied on unsetting globally + # CMAKE_OSX_ARCHITECTURES to ensure that CMake would + # not add the -arch flag + # This is no longer the case when running on Apple Silicon, + # when CMake will enforce a default (see + # https://gitlab.kitware.com/cmake/cmake/-/merge_requests/5291) + set_property(TARGET ${VARIANT_NAME} PROPERTY OSX_ARCHITECTURES "${arch}") + add_custom_command_target(unused_var2 COMMAND "codesign" "-f" "-s" "-" "${SWIFT_RUNTIME_OUTPUT_INTDIR}/${VARIANT_NAME}" CUSTOM_TARGET_NAME "${VARIANT_NAME}_signed" diff --git a/stdlib/cmake/modules/SwiftSource.cmake b/stdlib/cmake/modules/SwiftSource.cmake index acd0f2470b93b..9ca753c1ac909 100644 --- a/stdlib/cmake/modules/SwiftSource.cmake +++ b/stdlib/cmake/modules/SwiftSource.cmake @@ -113,7 +113,7 @@ function(handle_swift_sources # FIXME: We shouldn't /have/ to build things in a single process. # list(APPEND swift_compile_flags "-whole-module-optimization") - if(sdk IN_LIST SWIFT_APPLE_PLATFORMS OR sdk STREQUAL "MACCATALYST") + if(sdk IN_LIST SWIFT_DARWIN_PLATFORMS OR sdk STREQUAL "MACCATALYST") list(APPEND swift_compile_flags "-save-optimization-record=bitstream") endif() if (SWIFTSOURCES_ENABLE_LTO) @@ -218,7 +218,7 @@ function(_add_target_variant_swift_compile_flags list(APPEND result "-sdk" "${SWIFT_SDK_${sdk}_ARCH_${arch}_PATH}") endif() - if("${sdk}" IN_LIST SWIFT_APPLE_PLATFORMS) + if("${sdk}" IN_LIST SWIFT_DARWIN_PLATFORMS) set(sdk_deployment_version "${SWIFT_SDK_${sdk}_DEPLOYMENT_VERSION}") get_target_triple(target target_variant "${sdk}" "${arch}" MACCATALYST_BUILD_FLAVOR "${VARIANT_MACCATALYST_BUILD_FLAVOR}" @@ -237,7 +237,7 @@ function(_add_target_variant_swift_compile_flags list(APPEND result "-resource-dir" "${SWIFTLIB_DIR}") endif() - if("${sdk}" IN_LIST SWIFT_APPLE_PLATFORMS) + if("${sdk}" IN_LIST SWIFT_DARWIN_PLATFORMS) # We collate -F with the framework path to avoid unwanted deduplication # of options by target_compile_options -- this way no undesired # side effects are introduced should a new search path be added. @@ -264,6 +264,10 @@ function(_add_target_variant_swift_compile_flags list(APPEND result "-D" "SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY") endif() + if(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) + list(APPEND result "-D" "SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED") + endif() + if(SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS) list(APPEND result "-D" "SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS") endif() @@ -455,6 +459,7 @@ function(_compile_swift_files if (SWIFTFILE_IS_STDLIB OR SWIFTFILE_IS_SDK_OVERLAY) list(APPEND swift_flags "-runtime-compatibility-version" "none") list(APPEND swift_flags "-disable-autolinking-runtime-compatibility-dynamic-replacements") + list(APPEND swift_flags "-Xfrontend" "-disable-autolinking-runtime-compatibility-concurrency") endif() if (SWIFTFILE_IS_STDLIB_CORE OR SWIFTFILE_IS_SDK_OVERLAY) @@ -539,7 +544,7 @@ function(_compile_swift_files endif() set(optional_arg) - if(SWIFTFILE_SDK IN_LIST SWIFT_APPLE_PLATFORMS OR + if(SWIFTFILE_SDK IN_LIST SWIFT_DARWIN_PLATFORMS OR SWIFTFILE_SDK STREQUAL "MACCATALYST") # Allow installation of stdlib without building all variants on Darwin. set(optional_arg "OPTIONAL") @@ -626,7 +631,17 @@ function(_compile_swift_files if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) set(HOST_EXECUTABLE_SUFFIX .exe) endif() - set(swift_compiler_tool "${SWIFT_NATIVE_SWIFT_TOOLS_PATH}/swiftc${HOST_EXECUTABLE_SUFFIX}") + if(SWIFT_BUILD_RUNTIME_WITH_HOST_COMPILER) + if(SWIFT_PREBUILT_SWIFT) + set(swift_compiler_tool "${SWIFT_NATIVE_SWIFT_TOOLS_PATH}/swiftc${HOST_EXECUTABLE_SUFFIX}") + elseif(CMAKE_Swift_COMPILER) + set(swift_compiler_tool "${CMAKE_Swift_COMPILER}") + else() + message(ERROR "Must pass in prebuilt tools using SWIFT_NATIVE_SWIFT_TOOLS_PATH or set CMAKE_Swift_COMPILER") + endif() + else() + set(swift_compiler_tool "${SWIFT_NATIVE_SWIFT_TOOLS_PATH}/swiftc${HOST_EXECUTABLE_SUFFIX}") + endif() set(swift_compiler_tool_dep) if(SWIFT_INCLUDE_TOOLS) diff --git a/stdlib/private/BlocksRuntimeStubs/CMakeLists.txt b/stdlib/private/BlocksRuntimeStubs/CMakeLists.txt index c24c956f36a19..74550981212c5 100644 --- a/stdlib/private/BlocksRuntimeStubs/CMakeLists.txt +++ b/stdlib/private/BlocksRuntimeStubs/CMakeLists.txt @@ -14,7 +14,7 @@ foreach(SDK ${SWIFT_SDKS}) _add_swift_target_library_single( BlocksRuntimeStub${VARIANT_SUFFIX} BlocksRuntimeStub - SHARED + SHARED NOSWIFTRT ARCHITECTURE ${ARCH} SDK ${SDK} INSTALL_IN_COMPONENT dev diff --git a/stdlib/private/DifferentiationUnittest/CMakeLists.txt b/stdlib/private/DifferentiationUnittest/CMakeLists.txt index 33da12b9b766a..1ea0e57c07a02 100644 --- a/stdlib/private/DifferentiationUnittest/CMakeLists.txt +++ b/stdlib/private/DifferentiationUnittest/CMakeLists.txt @@ -3,5 +3,6 @@ add_swift_target_library(swiftDifferentiationUnittest ${SWIFT_STDLIB_LIBRARY_BUI GYB_SOURCES DifferentiationUnittest.swift.gyb SWIFT_MODULE_DEPENDS _Differentiation StdlibUnittest + SWIFT_COMPILE_FLAGS -Xfrontend -requirement-machine=off INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/DifferentiationUnittest/DifferentiationUnittest.swift.gyb b/stdlib/private/DifferentiationUnittest/DifferentiationUnittest.swift.gyb index d8c03d62033ce..a06161bf1da35 100644 --- a/stdlib/private/DifferentiationUnittest/DifferentiationUnittest.swift.gyb +++ b/stdlib/private/DifferentiationUnittest/DifferentiationUnittest.swift.gyb @@ -196,8 +196,7 @@ extension ${Self}: Equatable where T: Equatable { } } -extension ${Self}: SignedNumeric & Numeric -where T: SignedNumeric, T == T.Magnitude { +extension ${Self}: Numeric where T: Numeric { public typealias Magnitude = ${Self} public init?(exactly source: U) where U: BinaryInteger { @@ -217,6 +216,12 @@ where T: SignedNumeric, T == T.Magnitude { } } +extension ${Self}: SignedNumeric where T: SignedNumeric, T == T.Magnitude { + public prefix static func - (operand: ${Self}) -> ${Self} { + ${Self}(-operand.value) + } +} + extension ${Self} where T: FloatingPoint { public static func / (lhs: ${Self}, rhs: ${Self}) -> ${Self} { return ${Self}(lhs.value / rhs.value) diff --git a/stdlib/private/OSLog/CMakeLists.txt b/stdlib/private/OSLog/CMakeLists.txt index 2e37b642fb6c0..b80fd3cb428e6 100644 --- a/stdlib/private/OSLog/CMakeLists.txt +++ b/stdlib/private/OSLog/CMakeLists.txt @@ -14,10 +14,10 @@ add_swift_target_library(swiftOSLogTestHelper OSLogPrivacy.swift OSLogFloatFormatting.swift - SWIFT_MODULE_DEPENDS_IOS Darwin ObjectiveC - SWIFT_MODULE_DEPENDS_OSX Darwin ObjectiveC - SWIFT_MODULE_DEPENDS_TVOS Darwin ObjectiveC - SWIFT_MODULE_DEPENDS_WATCHOS Darwin ObjectiveC + SWIFT_MODULE_DEPENDS_IOS Darwin _Concurrency + SWIFT_MODULE_DEPENDS_OSX Darwin _Concurrency + SWIFT_MODULE_DEPENDS_TVOS Darwin _Concurrency + SWIFT_MODULE_DEPENDS_WATCHOS Darwin _Concurrency TARGET_SDKS ALL_APPLE_PLATFORMS SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT never_install diff --git a/stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift b/stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift index 1e4e7c3f961eb..22743470dce0f 100644 --- a/stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift +++ b/stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift @@ -982,7 +982,7 @@ self.test("\(testNamePrefix).partition/DispatchesThroughDirectStorageAccessors") withUnsafeMutableBufferPointerIsSupported ? 1 : 0, actualWUMBPIFNonNil + actualWCMSIAIFNonNil) - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { // `partition(by:)` is expected to dispatch to the public API in releases // that contain https://github.com/apple/swift/pull/36003. expectEqual(0, actualWUMBPIF) diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index 4d999311c50f3..f2e29ff24adfc 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -11,8 +11,13 @@ if (NOT IS_BUILD_TYPE_OPTIMIZED) endif() set(swift_stdlib_unittest_link_libraries "") +set(swift_stdlib_unittest_modules "") if (SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY) list(APPEND swift_stdlib_unittest_link_libraries "swift_Concurrency") + list(APPEND swift_stdlib_unittest_modules "_Concurrency") +endif() +if (SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) + list(APPEND swift_stdlib_unittest_link_libraries "swift_Distributed") endif() add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB @@ -36,12 +41,12 @@ add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} TestHelpers.swift TypeIndexed.swift - SWIFT_MODULE_DEPENDS SwiftPrivate SwiftPrivateThreadExtras SwiftPrivateLibcExtras - SWIFT_MODULE_DEPENDS_IOS Darwin Foundation - SWIFT_MODULE_DEPENDS_OSX Darwin Foundation - SWIFT_MODULE_DEPENDS_TVOS Darwin Foundation - SWIFT_MODULE_DEPENDS_WATCHOS Darwin Foundation - SWIFT_MODULE_DEPENDS_FREESTANDING Darwin + SWIFT_MODULE_DEPENDS SwiftPrivate SwiftPrivateThreadExtras SwiftPrivateLibcExtras ${swift_stdlib_unittest_modules} + SWIFT_MODULE_DEPENDS_IOS Darwin + SWIFT_MODULE_DEPENDS_OSX Darwin + SWIFT_MODULE_DEPENDS_TVOS Darwin + SWIFT_MODULE_DEPENDS_WATCHOS Darwin + SWIFT_MODULE_DEPENDS_FREESTANDING "${SWIFT_FREESTANDING_TEST_DEPENDENCIES}" SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_OPENBSD Glibc @@ -49,6 +54,8 @@ add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} SWIFT_MODULE_DEPENDS_HAIKU Glibc SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK SWIFT_COMPILE_FLAGS ${swift_stdlib_unittest_compile_flags} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + -Xfrontend -define-availability + -Xfrontend "SwiftStdlib 5.5:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0" INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}" LINK_LIBRARIES ${swift_stdlib_unittest_link_libraries}) diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index 3c4ff221a32e8..57d19a7cfee62 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -876,6 +876,7 @@ func _childProcess() { } #if SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY +@available(SwiftStdlib 5.5, *) @inline(never) func _childProcessAsync() async { _installTrapInterceptor() @@ -1370,6 +1371,7 @@ class _ParentProcess { } #if SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY + @available(SwiftStdlib 5.5, *) internal func runOneTestAsync( fullTestName: String, testSuite: TestSuite, @@ -1536,6 +1538,7 @@ class _ParentProcess { } #if SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY + @available(SwiftStdlib 5.5, *) func runAsync() async { if let filter = _filter { print("StdlibUnittest: using filter: \(filter)") @@ -1558,7 +1561,7 @@ class _ParentProcess { continue } - switch runOneTest( + switch await runOneTestAsync( fullTestName: fullTestName, testSuite: testSuite, test: t, @@ -1720,6 +1723,7 @@ public func runAllTests() { } #if SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY +@available(SwiftStdlib 5.5, *) public func runAllTestsAsync() async { if PersistentState.runNoTestsWasCalled { print("runAllTests() called after runNoTests(). Aborting.") @@ -1907,6 +1911,7 @@ public final class TestSuite { } #if SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY + @available(SwiftStdlib 5.5, *) func _runTestAsync(name testName: String, parameter: Int?) async { PersistentState.ranSomething = true for r in _allResettables { diff --git a/stdlib/private/StdlibUnittestFoundationExtras/CMakeLists.txt b/stdlib/private/StdlibUnittestFoundationExtras/CMakeLists.txt index 7fc465e10bb0e..ca3dd754ee9ef 100644 --- a/stdlib/private/StdlibUnittestFoundationExtras/CMakeLists.txt +++ b/stdlib/private/StdlibUnittestFoundationExtras/CMakeLists.txt @@ -4,7 +4,7 @@ add_swift_target_library(swiftStdlibUnittestFoundationExtras ${SWIFT_STDLIB_LIBR StdlibUnittestFoundationExtras.swift UnavailableFoundationMethodThunks.mm - SWIFT_MODULE_DEPENDS Foundation StdlibUnittest + SWIFT_MODULE_DEPENDS StdlibUnittest SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") diff --git a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt index 29a377932b99c..c941a69a25db2 100644 --- a/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt @@ -13,7 +13,7 @@ add_swift_target_library(swiftSwiftPrivateLibcExtras ${SWIFT_STDLIB_LIBRARY_BUIL SWIFT_MODULE_DEPENDS_IOS Darwin SWIFT_MODULE_DEPENDS_TVOS Darwin SWIFT_MODULE_DEPENDS_WATCHOS Darwin - SWIFT_MODULE_DEPENDS_FREESTANDING Darwin + SWIFT_MODULE_DEPENDS_FREESTANDING "${SWIFT_FREESTANDING_TEST_DEPENDENCIES}" SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_OPENBSD Glibc diff --git a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt index e2c7b198ecb73..062f51052c4cc 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt +++ b/stdlib/private/SwiftPrivateThreadExtras/CMakeLists.txt @@ -10,7 +10,7 @@ add_swift_target_library(swiftSwiftPrivateThreadExtras ${SWIFT_STDLIB_LIBRARY_BU SWIFT_MODULE_DEPENDS_OSX Darwin SWIFT_MODULE_DEPENDS_TVOS Darwin SWIFT_MODULE_DEPENDS_WATCHOS Darwin - SWIFT_MODULE_DEPENDS_FREESTANDING Darwin + SWIFT_MODULE_DEPENDS_FREESTANDING "${SWIFT_FREESTANDING_TEST_DEPENDENCIES}" SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_OPENBSD Glibc diff --git a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift index 183bc1da6e3cf..7904d43015b19 100644 --- a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift +++ b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift @@ -17,6 +17,7 @@ //===----------------------------------------------------------------------===// let RequestInstanceKind = "k" +let RequestShouldUnwrapClassExistential = "u" let RequestInstanceAddress = "i" let RequestReflectionInfos = "r" let RequestImages = "m" @@ -346,11 +347,15 @@ internal func sendPointerSize() { /// The parent sends a Done message to indicate that it's done /// looking at this instance. It will continue to ask for instances, /// so call doneReflecting() when you don't have any more instances. -internal func reflect(instanceAddress: UInt, kind: InstanceKind) { +internal func reflect(instanceAddress: UInt, + kind: InstanceKind, + shouldUnwrapClassExistential: Bool = false) { while let command = readLine(strippingNewline: true) { switch command { case String(validatingUTF8: RequestInstanceKind)!: sendValue(kind.rawValue) + case String(validatingUTF8: RequestShouldUnwrapClassExistential)!: + sendValue(shouldUnwrapClassExistential) case String(validatingUTF8: RequestInstanceAddress)!: sendValue(instanceAddress) case String(validatingUTF8: RequestReflectionInfos)!: @@ -437,12 +442,18 @@ public func reflect(object: AnyObject) { /// The test doesn't care about the witness tables - we only care /// about what's in the buffer, so we always put these values into /// an Any existential. -public func reflect(any: T, kind: InstanceKind = .Existential) { +/// +/// If shouldUnwrapClassExistential is set to true, this exercises +/// projectExistentialAndUnwrapClass instead of projectExistential. +public func reflect(any: T, kind: InstanceKind = .Existential, + shouldUnwrapClassExistential: Bool = false) { let any: Any = any let anyPointer = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size) anyPointer.initialize(to: any) let anyPointerValue = UInt(bitPattern: anyPointer) - reflect(instanceAddress: anyPointerValue, kind: kind) + reflect(instanceAddress: anyPointerValue, + kind: kind, + shouldUnwrapClassExistential: shouldUnwrapClassExistential) anyPointer.deallocate() } @@ -474,6 +485,21 @@ public func reflect(error: T) { reflect(instanceAddress: errorPointerValue, kind: .ErrorExistential) } +// Like reflect(error: T), but calls projectExistentialAndUnwrapClass +// instead of projectExistential and adds an extra level of indirection, which is +// what projectExistentialAndUnwrapClass expects. +public func reflectUnwrappingClassExistential(error: T) { + let error: Error = error + let errorPointerValue = unsafeBitCast(error, to: UInt.self) + let anyPointer = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size) + anyPointer.initialize(to: errorPointerValue) + let anyPointerValue = UInt(bitPattern: anyPointer) + reflect(instanceAddress: anyPointerValue, + kind: .ErrorExistential, + shouldUnwrapClassExistential: true) + anyPointer.deallocate() +} + // Reflect an `Enum` // // These are handled like existentials, but diff --git a/stdlib/public/BackDeployConcurrency/CMakeLists.txt b/stdlib/public/BackDeployConcurrency/CMakeLists.txt new file mode 100644 index 0000000000000..e01a67849836b --- /dev/null +++ b/stdlib/public/BackDeployConcurrency/CMakeLists.txt @@ -0,0 +1,47 @@ +#===--- CMakeLists.txt - Back-deployed concurrency support library -------===# +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2021 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 is always build standalone +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/modules/StandaloneOverlay.cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules") +include(AddSwiftStdlib) + +# Don't build the libraries for 32-bit iOS targets; there is no back-deployment +# to them. +list(REMOVE_ITEM SWIFT_SDK_IOS_ARCHITECTURES "armv7" "armv7s") +list(REMOVE_ITEM SWIFT_SDK_IOS_SIMULATOR_ARCHITECTURES "i386") + +# The back-deployed library can only be shared. +list(APPEND SWIFT_STDLIB_LIBRARY_BUILD_TYPES SHARED) + +# Link aginst the libswiftCore in the SDK. This intentionally avoids using +# the locally-built libswiftCore. +foreach(sdk ${SWIFT_SDKS}) + set(sdk_name ${SWIFT_SDK_${sdk}_LIB_SUBDIR}) + set(swift_core_target "swiftCore-${sdk_name}") + add_library(${swift_core_target} SHARED IMPORTED GLOBAL) + set_property(TARGET ${swift_core_target} + PROPERTY IMPORTED_LOCATION "${SWIFT_SDK_${sdk}_PUBLIC_PATH}/usr/lib/swift/libswiftCore.tbd") + foreach(arch in ${SWIFT_SDK_${sdk}_ARCHITECTURES}) + add_library("${swift_core_target}-${arch}" ALIAS "${swift_core_target}") + endforeach() +endforeach() + +# Build the concurrency library for back deployment. +add_compile_definitions(SWIFT_CONCURRENCY_BACK_DEPLOYMENT) +set(swift_concurrency_install_component back-deployment) +set(swift_concurrency_options + BACK_DEPLOYMENT_LIBRARY 5.5 + DARWIN_INSTALL_NAME_DIR "@rpath") +set(swift_concurrency_extra_sources "../BackDeployConcurrency/Exclusivity.cpp") + +add_subdirectory(../Concurrency stdlib/public/BackDeployConcurrency) diff --git a/stdlib/public/BackDeployConcurrency/Exclusivity.cpp b/stdlib/public/BackDeployConcurrency/Exclusivity.cpp new file mode 100644 index 0000000000000..6b4667fb1cded --- /dev/null +++ b/stdlib/public/BackDeployConcurrency/Exclusivity.cpp @@ -0,0 +1,35 @@ +//===--- Exclusivity.cpp - Exclusivity tracking ---------------------------===// +// +// 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 implements the runtime support for dynamically tracking exclusivity. +// +//===----------------------------------------------------------------------===// +#include + +#include "swift/Runtime/Exclusivity.h" +#include "../runtime/ExclusivityPrivate.h" +#include "../runtime/SwiftTLSContext.h" + +using namespace swift; +using namespace swift::runtime; + +// Thread-local storage used by the back-deployed concurrency library. +namespace { + +static thread_local SwiftTLSContext TLSContext; + +} // anonymous namespace + +SwiftTLSContext &SwiftTLSContext::get() { return TLSContext; } + +// Bring in the concurrency-specific exclusivity code. +#include "../runtime/ConcurrencyExclusivity.inc" diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index f32785db29943..3d87e3a3d81fa 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -4,6 +4,7 @@ set(SWIFT_RUNTIME_CXX_FLAGS) set(SWIFT_RUNTIME_LINK_FLAGS) set(SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS) set(SWIFT_RUNTIME_SWIFT_LINK_FLAGS) +set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_LINK_FLAGS) if(SWIFT_RUNTIME_USE_SANITIZERS) # TODO: Refactor this @@ -18,7 +19,7 @@ endif() list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-verify-syntax-tree") if(SWIFT_STDLIB_SIL_DEBUGGING) - list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-gsil") + list(APPEND SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS "-Xfrontend" "-sil-based-debuginfo") endif() # Build the runtime with -Wall to catch, e.g., uninitialized variables @@ -40,16 +41,18 @@ endif() # C++ code in the runtime and standard library should generally avoid # introducing static constructors or destructors. -check_cxx_compiler_flag("-Werror -Wglobal-constructors" CXX_SUPPORTS_GLOBAL_CONSTRUCTORS_WARNING) +check_cxx_compiler_flag("-Wglobal-constructors -Werror=global-constructors" CXX_SUPPORTS_GLOBAL_CONSTRUCTORS_WARNING) if(CXX_SUPPORTS_GLOBAL_CONSTRUCTORS_WARNING) - list(APPEND SWIFT_RUNTIME_CORE_CXX_FLAGS "-Wglobal-constructors") + list(APPEND SWIFT_RUNTIME_CORE_CXX_FLAGS "-Wglobal-constructors" + "-Werror=global-constructors") endif() # C++ code in the runtime and standard library should generally avoid # introducing static constructors or destructors. -check_cxx_compiler_flag("-Wexit-time-destructors" CXX_SUPPORTS_EXIT_TIME_DESTRUCTORS_WARNING) +check_cxx_compiler_flag("-Wexit-time-destructors -Werror=exit-time-destructors" CXX_SUPPORTS_EXIT_TIME_DESTRUCTORS_WARNING) if(CXX_SUPPORTS_EXIT_TIME_DESTRUCTORS_WARNING) - list(APPEND SWIFT_RUNTIME_CORE_CXX_FLAGS "-Wexit-time-destructors") + list(APPEND SWIFT_RUNTIME_CORE_CXX_FLAGS "-Wexit-time-destructors" + "-Werror=exit-time-destructors") endif() add_subdirectory(SwiftShims) @@ -67,14 +70,8 @@ if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_REMOTE_MIRROR) "${SWIFT_SOURCE_DIR}/lib/Demangling/OldDemangler.cpp" "${SWIFT_SOURCE_DIR}/lib/Demangling/OldRemangler.cpp" "${SWIFT_SOURCE_DIR}/lib/Demangling/Punycode.cpp" - "${SWIFT_SOURCE_DIR}/lib/Demangling/Remangler.cpp") - - # When we're building with assertions, include the demangle node dumper to aid - # in debugging. - if(LLVM_ENABLE_ASSERTIONS) - list(APPEND swiftDemanglingSources - "${SWIFT_SOURCE_DIR}/lib/Demangling/NodeDumper.cpp") - endif() + "${SWIFT_SOURCE_DIR}/lib/Demangling/Remangler.cpp" + "${SWIFT_SOURCE_DIR}/lib/Demangling/NodeDumper.cpp") add_swift_target_library(swiftDemangling OBJECT_LIBRARY ${swiftDemanglingSources} @@ -97,6 +94,10 @@ if(SWIFT_BUILD_STDLIB) if(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY) add_subdirectory(Concurrency) endif() + + if(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) + add_subdirectory(Distributed) + endif() endif() if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_REMOTE_MIRROR) @@ -109,13 +110,17 @@ if(SWIFT_BUILD_SDK_OVERLAY OR SWIFT_BUILD_TEST_SUPPORT_MODULES) endif() if(SWIFT_BUILD_SDK_OVERLAY) - list_intersect("${SWIFT_APPLE_PLATFORMS}" "${SWIFT_SDKS}" building_darwin_sdks) - if(building_darwin_sdks) - add_subdirectory(Darwin) - endif() + # On Apple platforms, we aren't building any overlays (other than Darwin in + # Platform above). Instead, we're picking them up from the SDK. if(WINDOWS IN_LIST SWIFT_SDKS) add_subdirectory(Windows) endif() endif() +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + list(APPEND SWIFT_RUNTIME_CONCURRENCY_SWIFT_LINK_FLAGS + "-Wl,-reexported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/Concurrency/ReexportedSymbols") +endif() + +list(APPEND SWIFT_RUNTIME_CONCURRENCY_SWIFT_LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}") diff --git a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def index faf29be474a43..8648ddd6af8e8 100644 --- a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def +++ b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def @@ -78,10 +78,6 @@ OVERRIDE_ACTOR(job_run, void, swift::, (class Job *job, ExecutorRef executor), (job, executor)) -OVERRIDE_ACTOR(task_getCurrent, AsyncTask *, - SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, ,) - OVERRIDE_ACTOR(task_getCurrentExecutor, ExecutorRef, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, ,) @@ -96,29 +92,33 @@ OVERRIDE_ACTOR(task_switch, void, TaskContinuationFunction *resumeFunction, ExecutorRef newExecutor), (resumeToContext, resumeFunction, newExecutor)) -OVERRIDE_TASK(task_create_group_future_common, AsyncTaskAndContext, , , , - (JobFlags flags, TaskGroup *group, +OVERRIDE_TASK(task_create_common, AsyncTaskAndContext, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, + (size_t taskCreateFlags, + TaskOptionRecord *options, const Metadata *futureResultType, FutureAsyncSignature::FunctionType *function, - void *closureContext, bool isSpawnLetTask, + void *closureContext, size_t initialContextSize), - (flags, group, futureResultType, function, closureContext, - isSpawnLetTask, initialContextSize)) + (taskCreateFlags, options, futureResultType, function, + closureContext, initialContextSize)) OVERRIDE_TASK(task_future_wait, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), swift::, (OpaqueValue *result, - SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, AsyncTask *task, - Metadata *T), - (result, rawContext, task, T)) + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, AsyncTask *task, + TaskContinuationFunction *resumeFunction, + AsyncContext *callContext), + (result, callerContext, task, resumeFunction, callContext)) OVERRIDE_TASK(task_future_wait_throwing, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), swift::, (OpaqueValue *result, - SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, AsyncTask *task, - Metadata *T), - (result, rawContext, task, T)) + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, AsyncTask *task, + ThrowingTaskFutureWaitContinuationFunction *resumeFunction, + AsyncContext *callContext), + (result, callerContext, task, resumeFunction, callContext)) OVERRIDE_TASK(continuation_resume, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, @@ -146,43 +146,100 @@ OVERRIDE_TASK(task_removeCancellationHandler, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, (CancellationNotificationStatusRecord *record), (record)) +OVERRIDE_TASK(task_createNullaryContinuationJob, NullaryContinuationJob *, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, + (size_t priority, + AsyncTask *continuation), (priority, continuation)) + OVERRIDE_TASK(task_asyncMainDrainQueue, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, , ) -OVERRIDE_ASYNC_LET(asyncLet_start, void, - SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, - (AsyncLet *alet, - const Metadata *futureResultType, - void *closureEntryPoint, - void *closureContext - ), - (alet, futureResultType, - closureEntryPoint, closureContext)) +OVERRIDE_TASK(task_suspend, AsyncTask *, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), + swift::, ,) + +OVERRIDE_TASK(continuation_init, AsyncTask *, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), + swift::, (ContinuationAsyncContext *context, + AsyncContinuationFlags flags), + (context, flags)) + +OVERRIDE_TASK(continuation_await, void, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), + swift::, (ContinuationAsyncContext *context), + (context)) OVERRIDE_ASYNC_LET(asyncLet_wait, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), swift::, (OpaqueValue *result, - SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - AsyncLet *alet, Metadata *T), - (result, rawContext, alet, T)) + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, TaskContinuationFunction *resumeFn, + AsyncContext *callContext), + (result, callerContext, alet, resumeFn, callContext)) OVERRIDE_ASYNC_LET(asyncLet_wait_throwing, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), swift::, (OpaqueValue *result, - SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - AsyncLet *alet, Metadata *T), - (result, rawContext, alet, T)) + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, + ThrowingTaskFutureWaitContinuationFunction *resume, + AsyncContext *callContext), + (result, callerContext, alet, resume, callContext)) OVERRIDE_ASYNC_LET(asyncLet_end, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, (AsyncLet *alet), (alet)) +OVERRIDE_ASYNC_LET(asyncLet_get, void, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), + swift::, + (SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, void *resultBuffer, + TaskContinuationFunction *resumeFn, + AsyncContext *callContext), + (callerContext, alet, resultBuffer, resumeFn, callContext)) + +OVERRIDE_ASYNC_LET(asyncLet_get_throwing, void, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), + swift::, + (SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, void *resultBuffer, + ThrowingTaskFutureWaitContinuationFunction *resumeFn, + AsyncContext *callContext), + (callerContext, alet, resultBuffer, resumeFn, callContext)) + +OVERRIDE_ASYNC_LET(asyncLet_consume, void, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), + swift::, + (SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, void *resultBuffer, + TaskContinuationFunction *resumeFn, + AsyncContext *callContext), + (callerContext, alet, resultBuffer, resumeFn, callContext)) + +OVERRIDE_ASYNC_LET(asyncLet_consume_throwing, void, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), + swift::, + (SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, void *resultBuffer, + ThrowingTaskFutureWaitContinuationFunction *resumeFn, + AsyncContext *callContext), + (callerContext, alet, resultBuffer, resumeFn, callContext)) + +OVERRIDE_ASYNC_LET(asyncLet_finish, void, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), + swift::, + (SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, void *resultBuffer, + TaskContinuationFunction *resumeFn, + AsyncContext *callContext), + (callerContext, alet, resultBuffer, resumeFn, callContext)) + OVERRIDE_TASK_GROUP(taskGroup_initialize, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, (TaskGroup *group), (group)) + swift::, (TaskGroup *group, const Metadata *T), (group, T)) OVERRIDE_TASK_GROUP(taskGroup_attachChild, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), @@ -197,9 +254,12 @@ OVERRIDE_TASK_GROUP(taskGroup_wait_next_throwing, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync), swift::, (OpaqueValue *resultPointer, - SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - TaskGroup *_group, const Metadata *successType), - (resultPointer, rawContext, _group, successType)) + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + TaskGroup *_group, + ThrowingTaskFutureWaitContinuationFunction *resumeFn, + AsyncContext *callContext), + (resultPointer, callerContext, _group, resumeFn, + callContext)) OVERRIDE_TASK_GROUP(taskGroup_isEmpty, bool, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), @@ -228,19 +288,25 @@ OVERRIDE_TASK_LOCAL(task_reportIllegalTaskLocalBindingWithinWithTaskGroup, void, OVERRIDE_TASK_LOCAL(task_localValuePush, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, - (AsyncTask *task, const HeapObject *key, - OpaqueValue *value, const Metadata *valueType), - (task, key, value, valueType)) + (const HeapObject *key, OpaqueValue *value, + const Metadata *valueType), + (key, value, valueType)) OVERRIDE_TASK_LOCAL(task_localValueGet, OpaqueValue *, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, - (AsyncTask *task, const HeapObject *key), - (task, key)) + (const HeapObject *key), + (key)) OVERRIDE_TASK_LOCAL(task_localValuePop, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, (AsyncTask *task), (task)) + swift::, ,) + +OVERRIDE_TASK_LOCAL(task_localsCopyTo, void, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), + swift::, + (AsyncTask *target), + (target)) OVERRIDE_TASK_STATUS(task_addStatusRecord, bool, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 0db21db2eeec7..8957d553d5617 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -26,14 +26,15 @@ #endif #include "../CompatibilityOverride/CompatibilityOverride.h" -#include "../runtime/ThreadLocalStorage.h" #include "swift/Runtime/Atomic.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/Once.h" #include "swift/Runtime/Mutex.h" #include "swift/Runtime/ThreadLocal.h" +#include "swift/Runtime/ThreadLocalStorage.h" #include "swift/ABI/Task.h" #include "swift/ABI/Actor.h" +#include "llvm/Config/config.h" #include "llvm/ADT/PointerIntPair.h" #include "TaskPrivate.h" @@ -63,6 +64,11 @@ #if HAVE_PTHREAD_H #include + +// Only use __has_include since HAVE_PTHREAD_NP_H is not provided. +#if __has_include() +#include +#endif #endif #if defined(_WIN32) @@ -107,15 +113,16 @@ class ExecutorTrackingInfo { /// information about the current thread, if any. /// /// TODO: this is obviously runtime-internal and therefore not - /// reasonable to make ABI. We might want to also provide a way + /// reasonable to make ABI. We might want to also provide a way /// for generated code to efficiently query the identity of the /// current executor, in order to do a cheap comparison to avoid /// doing all the work to suspend the task when we're already on /// the right executor. It would make sense for that to be a /// separate thread-local variable (or whatever is most efficient /// on the target platform). - static SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(Pointer, - ActiveInfoInThread); + static SWIFT_RUNTIME_DECLARE_THREAD_LOCAL( + Pointer, ActiveInfoInThread, + SWIFT_CONCURRENCY_EXECUTOR_TRACKING_INFO_KEY); /// The active executor. ExecutorRef ActiveExecutor = ExecutorRef::generic(); @@ -189,21 +196,11 @@ class ExecutorTrackingInfo { } }; -#ifdef SWIFT_TLS_HAS_RESERVED_PTHREAD_SPECIFIC -class ActiveTask { -public: - static void set(AsyncTask *task) { - SWIFT_THREAD_SETSPECIFIC(SWIFT_CONCURRENCY_TASK_KEY, task); - } - static AsyncTask *get() { - return (AsyncTask *)SWIFT_THREAD_GETSPECIFIC(SWIFT_CONCURRENCY_TASK_KEY); - } -}; -#else class ActiveTask { /// A thread-local variable pointing to the active tracking /// information about the current thread, if any. - static SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(Pointer, Value); + static SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(Pointer, Value, + SWIFT_CONCURRENCY_TASK_KEY); public: static void set(AsyncTask *task) { Value.set(task); } @@ -213,11 +210,13 @@ class ActiveTask { /// Define the thread-locals. SWIFT_RUNTIME_DECLARE_THREAD_LOCAL( Pointer, - ActiveTask::Value); -#endif + ActiveTask::Value, + SWIFT_CONCURRENCY_TASK_KEY); + SWIFT_RUNTIME_DECLARE_THREAD_LOCAL( Pointer, - ExecutorTrackingInfo::ActiveInfoInThread); + ExecutorTrackingInfo::ActiveInfoInThread, + SWIFT_CONCURRENCY_EXECUTOR_TRACKING_INFO_KEY); } // end anonymous namespace @@ -232,15 +231,16 @@ void swift::runJobInEstablishedExecutorContext(Job *job) { // Update the active task in the current thread. ActiveTask::set(task); - // FIXME: update the task status to say that it's running - // on the current thread. If the task suspends itself to run - // on an actor, it should update the task status appropriately; - // we don't need to update it afterwards. + // Update the task status to say that it's running on the + // current thread. If the task suspends somewhere, it should + // update the task status appropriately; we don't need to update + // it afterwards. + task->flagAsRunning(); task->runInFullyEstablishedContext(); - // Clear the active task. - ActiveTask::set(nullptr); + assert(ActiveTask::get() == nullptr && + "active task wasn't cleared before susspending?"); } else { // There's no extra bookkeeping to do for simple jobs. job->runSimpleInFullyEstablishedContext(); @@ -254,7 +254,7 @@ void swift::runJobInEstablishedExecutorContext(Job *job) { } SWIFT_CC(swift) -static AsyncTask *swift_task_getCurrentImpl() { +AsyncTask *swift::swift_task_getCurrent() { return ActiveTask::get(); } @@ -269,9 +269,7 @@ static ExecutorRef swift_task_getCurrentExecutorImpl() { auto currentTracking = ExecutorTrackingInfo::current(); auto result = (currentTracking ? currentTracking->getActiveExecutor() : ExecutorRef::generic()); -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] getting current executor %p\n", pthread_self(), result.getIdentity()); -#endif + SWIFT_TASK_DEBUG_LOG("getting current executor %p", result.getIdentity()); return result; } @@ -314,8 +312,7 @@ static bool swift_task_isCurrentExecutorImpl(ExecutorRef executor) { return currentTracking->getActiveExecutor() == executor; } - return executor == _swift_task_getMainExecutor() - && isExecutingOnMainThread(); + return executor.isMainExecutor() && isExecutingOnMainThread(); } /// Logging level for unexpected executors: @@ -358,7 +355,7 @@ void swift::swift_task_reportUnexpectedExecutor( const char *functionIsolation; const char *whereExpected; - if (executor == _swift_task_getMainExecutor()) { + if (executor.isMainExecutor()) { functionIsolation = "@MainActor function"; whereExpected = "the main thread"; } else { @@ -619,6 +616,8 @@ class DefaultActorImpl : public HeapObject { HasActiveInlineJob = 3, + IsDistributedRemote = 4, + MaxPriority = 8, MaxPriority_width = JobFlags::Priority_width, @@ -648,6 +647,12 @@ class DefaultActorImpl : public HeapObject { FLAGSET_DEFINE_FLAG_ACCESSORS(HasActiveInlineJob, hasActiveInlineJob, setHasActiveInlineJob) + /// Is the actor a distributed 'remote' actor? + /// I.e. it does not have storage for user-defined properties and all + /// function call must be transformed into $distributed_ function calls. + FLAGSET_DEFINE_FLAG_ACCESSORS(IsDistributedRemote, + isDistributedRemote, setIsDistributedRemote) + /// What is the maximum priority of jobs that are currently running /// or enqueued on this actor? /// @@ -675,10 +680,11 @@ class DefaultActorImpl : public HeapObject { }; public: - /// Properly construct an actor, except for the heap header. - void initialize() { - new (&CurrentState) std::atomic(State{JobRef(), Flags()}); + void initialize(bool isDistributedRemote = false) { + auto flags = Flags(); + flags.setIsDistributedRemote(isDistributedRemote); + new (&CurrentState) std::atomic(State{JobRef(), flags}); } /// Properly destruct an actor, except for the heap header. @@ -701,6 +707,12 @@ class DefaultActorImpl : public HeapObject { /// Claim the next job off the actor or give it up. Job *claimNextJobOrGiveUp(bool actorIsOwned, RunningJobInfo runner); + /// Check if the actor is actually a distributed *remote* actor. + /// + /// Note that a distributed *local* actor instance is the same as any other + /// ordinary default (local) actor, and no special handling is needed for them. + bool isDistributedRemote(); + private: void deallocateUnconditional(); @@ -895,7 +907,7 @@ class ProcessOverrideJob : public Job { IsAbandoned = true; shouldDelete = IsResolvedByJob && IsResolvedByActor; }); - if (shouldDelete) delete this; + if (shouldDelete) delete this; } /// Called by the job to notify the actor that the job has successfully @@ -910,7 +922,7 @@ class ProcessOverrideJob : public Job { IsResolvedByJob = true; shouldDelete = IsResolvedByJob && IsResolvedByActor; }); - if (shouldDelete) delete this; + if (shouldDelete) delete this; } /// Called by the job to wait for the actor to resolve what the job @@ -1034,8 +1046,7 @@ static void wakeOverrides(ProcessOverrideJob *nextOverride, nextOverride = cur->NextJob.getAsPreprocessedOverride(); if (hasAlreadyActivated || - !targetPriority || - cur->getPriority() != *targetPriority) + !targetPriority) cur->wakeAndAbandon(); else hasAlreadyActivated = cur->wakeAndActivate(); @@ -1158,9 +1169,7 @@ static Job *preprocessQueue(JobRef first, } void DefaultActorImpl::giveUpThread(RunningJobInfo runner) { -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] giving up thread for actor %p\n", pthread_self(), this); -#endif + SWIFT_TASK_DEBUG_LOG("giving up thread for actor %p", this); auto oldState = CurrentState.load(std::memory_order_acquire); assert(oldState.Flags.isAnyRunningStatus()); @@ -1197,8 +1206,7 @@ void DefaultActorImpl::giveUpThread(RunningJobInfo runner) { } bool hasMoreJobs = (bool) newState.FirstJob; - bool hasOverrideAtNewPriority = - (runner.Priority < oldState.Flags.getMaxPriority()); + bool hasOverrideAtNewPriority = false; bool hasActiveInlineJob = newState.Flags.hasActiveInlineJob(); bool needsNewProcessJob = hasMoreJobs && !hasOverrideAtNewPriority; @@ -1221,13 +1229,11 @@ void DefaultActorImpl::giveUpThread(RunningJobInfo runner) { // Try again. continue; } - -#if SWIFT_TASK_PRINTF_DEBUG -# define LOG_STATE_TRANSITION fprintf(stderr, "[%p] actor %p transitioned from %zx to %zx (%s)\n", \ - pthread_self(), this, oldState.Flags.getOpaqueValue(), newState.Flags.getOpaqueValue(), __FUNCTION__) -#else -# define LOG_STATE_TRANSITION ((void)0) -#endif + +#define LOG_STATE_TRANSITION \ + SWIFT_TASK_DEBUG_LOG("actor %p transitioned from %zx to %zx (%s)\n", this, \ + oldState.Flags.getOpaqueValue(), \ + newState.Flags.getOpaqueValue(), __FUNCTION__) LOG_STATE_TRANSITION; // The priority of the remaining work. @@ -1297,8 +1303,7 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned, // If the actor is out of work, or its priority doesn't match our // priority, don't try to take over the actor. - if (!oldState.FirstJob || - oldState.Flags.getMaxPriority() != runner.Priority) { + if (!oldState.FirstJob) { // The only change we need here is inline-runner bookkeeping. if (!tryUpdateForInlineRunner()) @@ -1343,7 +1348,7 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned, continue; LOG_STATE_TRANSITION; _swift_tsan_acquire(this); - + // If that succeeded, we can proceed to the main body. oldState = newState; runner.setRunning(); @@ -1380,8 +1385,7 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned, // FIXME: should this be an exact match in priority instead of // potentially running jobs with too high a priority? Job *jobToRun; - if (oldState.Flags.getMaxPriority() <= runner.Priority && - newFirstJob) { + if (newFirstJob) { jobToRun = newFirstJob; newState.FirstJob = getNextJobInQueue(newFirstJob); newState.Flags.setStatus(Status::Running); @@ -1414,7 +1418,7 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned, continue; } LOG_STATE_TRANSITION; - + // We successfully updated the state. // If we're giving up the thread with jobs remaining, we need @@ -1477,11 +1481,10 @@ static void swift_job_runImpl(Job *job, ExecutorRef executor) { /// the actor lives for the duration of job execution. /// Note that this may conflict with the retain/release /// design in the DefaultActorImpl, but it does fix bugs! +SWIFT_CC(swiftasync) static void processDefaultActor(DefaultActorImpl *currentActor, RunningJobInfo runner) { -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] processDefaultActor %p\n", pthread_self(), currentActor); -#endif + SWIFT_TASK_DEBUG_LOG("processDefaultActor %p", currentActor); DefaultActorImpl *actor = currentActor; // If we actually have work to do, we'll need to set up tracking info. @@ -1505,9 +1508,8 @@ static void processDefaultActor(DefaultActorImpl *currentActor, auto job = currentActor->claimNextJobOrGiveUp(threadIsRunningActor, runner); -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] processDefaultActor %p claimed job %p\n", pthread_self(), currentActor, job); -#endif + SWIFT_TASK_DEBUG_LOG("processDefaultActor %p claimed job %p", currentActor, + job); // If we failed to claim a job, we have nothing to do. if (!job) { @@ -1527,9 +1529,8 @@ static void processDefaultActor(DefaultActorImpl *currentActor, // If it's become nil, or not a default actor, we have nothing to do. auto currentExecutor = trackingInfo.getActiveExecutor(); -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] processDefaultActor %p current executor now %p\n", pthread_self(), currentActor, currentExecutor.getIdentity()); -#endif + SWIFT_TASK_DEBUG_LOG("processDefaultActor %p current executor now %p", + currentActor, currentExecutor.getIdentity()); if (!currentExecutor.isDefaultActor()) { // The job already gave up the thread for us. @@ -1551,6 +1552,7 @@ static void processDefaultActor(DefaultActorImpl *currentActor, swift_release(actor); } +SWIFT_CC(swiftasync) void ProcessInlineJob::process(Job *job) { DefaultActorImpl *actor = DefaultActorImpl::fromInlineJob(job); @@ -1559,11 +1561,11 @@ void ProcessInlineJob::process(Job *job) { auto targetPriority = job->getPriority(); auto runner = RunningJobInfo::forInline(targetPriority); - // FIXME: force tail call swift_retain(actor); - return processDefaultActor(actor, runner); + return processDefaultActor(actor, runner); // 'return' forces tail call } +SWIFT_CC(swiftasync) void ProcessOutOfLineJob::process(Job *job) { auto self = cast(job); DefaultActorImpl *actor = self->Actor; @@ -1575,11 +1577,11 @@ void ProcessOutOfLineJob::process(Job *job) { delete self; - // FIXME: force tail call swift_retain(actor); - return processDefaultActor(actor, runner); + return processDefaultActor(actor, runner); // 'return' forces tail call } +SWIFT_CC(swiftasync) void ProcessOverrideJob::process(Job *job) { auto self = cast(job); @@ -1587,9 +1589,8 @@ void ProcessOverrideJob::process(Job *job) { auto actor = self->Actor; auto runner = RunningJobInfo::forOverride(self); - // FIXME: force tail call swift_retain(actor); - return processDefaultActor(actor, runner); + return processDefaultActor(actor, runner); // 'return' forces tail call } void DefaultActorImpl::enqueue(Job *job) { @@ -1623,7 +1624,7 @@ void DefaultActorImpl::enqueue(Job *job) { // If we need an override job, create it (if necessary) and // register it with the queue. - bool needsOverride = !wasIdle && newPriority != oldPriority; + bool needsOverride = false; if (needsOverride) { overrideJob.addToState(this, newState); } else { @@ -1733,6 +1734,11 @@ void swift::swift_defaultActor_deallocateResilient(HeapObject *actor) { metadata->getInstanceAlignMask()); } +/// FIXME: only exists for the quick-and-dirty MainActor implementation. +namespace swift { + Metadata* MainActorMetadata = nullptr; +} + /*****************************************************************************/ /****************************** ACTOR SWITCHING ******************************/ /*****************************************************************************/ @@ -1800,14 +1806,16 @@ SWIFT_CC(swiftasync) static void runOnAssumedThread(AsyncTask *task, ExecutorRef executor, ExecutorTrackingInfo *oldTracking, RunningJobInfo runner) { + // Note that this doesn't change the active task and so doesn't + // need to either update ActiveTask or flagAsRunning/flagAsSuspended. + // If there's alreaady tracking info set up, just change the executor // there and tail-call the task. We don't want these frames to // potentially accumulate linearly. if (oldTracking) { oldTracking->setActiveExecutor(executor); - // FIXME: force tail call - return task->runInFullyEstablishedContext(); + return task->runInFullyEstablishedContext(); // 'return' forces tail call } // Otherwise, set up tracking info. @@ -1826,9 +1834,8 @@ static void runOnAssumedThread(AsyncTask *task, ExecutorRef executor, executor = trackingInfo.getActiveExecutor(); trackingInfo.leave(); -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] leaving assumed thread, current executor is %p\n", pthread_self(), executor.getIdentity()); -#endif + SWIFT_TASK_DEBUG_LOG("leaving assumed thread, current executor is %p", + executor.getIdentity()); if (executor.isDefaultActor()) asImpl(executor.getDefaultActor())->giveUpThread(runner); @@ -1842,16 +1849,15 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex auto currentExecutor = (trackingInfo ? trackingInfo->getActiveExecutor() : ExecutorRef::generic()); -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] trying to switch from executor %p to %p\n", pthread_self(), currentExecutor.getIdentity(), newExecutor.getIdentity()); -#endif + SWIFT_TASK_DEBUG_LOG("trying to switch from executor %p to %p", + currentExecutor.getIdentity(), + newExecutor.getIdentity()); // If the current executor is compatible with running the new executor, // we can just immediately continue running with the resume function // we were passed in. if (!currentExecutor.mustSwitchToRun(newExecutor)) { - // FIXME: force tail call - return resumeFunction(resumeContext); + return resumeFunction(resumeContext); // 'return' forces tail call } auto task = swift_task_getCurrent(); @@ -1871,19 +1877,20 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex if (canGiveUpThreadForSwitch(trackingInfo, currentExecutor) && !shouldYieldThread() && tryAssumeThreadForSwitch(newExecutor, runner)) { -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] switch succeeded, task %p assumed thread for executor %p\n", pthread_self(), task, newExecutor.getIdentity()); -#endif + SWIFT_TASK_DEBUG_LOG( + "switch succeeded, task %p assumed thread for executor %p", task, + newExecutor.getIdentity()); giveUpThreadForSwitch(currentExecutor, runner); - // FIXME: force tail call + // 'return' forces tail call return runOnAssumedThread(task, newExecutor, trackingInfo, runner); } // Otherwise, just asynchronously enqueue the task on the given // executor. -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] switch failed, task %p enqueued on executor %p\n", pthread_self(), task, newExecutor.getIdentity()); -#endif + SWIFT_TASK_DEBUG_LOG("switch failed, task %p enqueued on executor %p", task, + newExecutor.getIdentity()); + task->flagAsSuspended(); + _swift_task_clearCurrent(); swift_task_enqueue(task, newExecutor); } @@ -1901,9 +1908,8 @@ void _swift_task_enqueueOnExecutor(Job *job, HeapObject *executor, SWIFT_CC(swift) static void swift_task_enqueueImpl(Job *job, ExecutorRef executor) { -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] enqueue job %p on executor %p\n", pthread_self(), job, executor.getIdentity()); -#endif + SWIFT_TASK_DEBUG_LOG("enqueue job %p on executor %p", job, + executor.getIdentity()); assert(job && "no job provided"); @@ -1923,3 +1929,49 @@ static void swift_task_enqueueImpl(Job *job, ExecutorRef executor) { #define OVERRIDE_ACTOR COMPATIBILITY_OVERRIDE #include COMPATIBILITY_OVERRIDE_INCLUDE_PATH + + +/*****************************************************************************/ +/***************************** DISTRIBUTED ACTOR *****************************/ +/*****************************************************************************/ + +OpaqueValue* +swift::swift_distributedActor_remote_initialize(const Metadata *actorType) { + auto *classMetadata = actorType->getClassObject(); + + // TODO(distributed): make this allocation smaller + // ==== Allocate the memory for the remote instance + HeapObject *alloc = swift_allocObject(classMetadata, + classMetadata->getInstanceSize(), + classMetadata->getInstanceAlignMask()); + + // TODO: remove this memset eventually, today we only do this to not have + // to modify the destructor logic, as releasing zeroes is no-op + memset(alloc + 1, 0, classMetadata->getInstanceSize() - sizeof(HeapObject)); + + // TODO(distributed): a remote one does not have to have the "real" + // default actor body, e.g. we don't need an executor at all; so + // we can allocate more efficiently and only share the flags/status field + // between the both memory representations + // --- Currently we ride on the DefaultActorImpl to reuse the memory layout + // of the flags etc. So initialize the default actor into the allocation. + auto actor = asImpl(reinterpret_cast(alloc)); + actor->initialize(/*remote*/true); + assert(actor->isDistributedRemote()); + + return reinterpret_cast(actor); +} + +void swift::swift_distributedActor_destroy(DefaultActor *_actor) { + // FIXME(distributed): if this is a proxy, we would destroy a bit differently I guess? less memory was allocated etc. + asImpl(_actor)->destroy(); // today we just replicate what defaultActor_destroy does +} + +bool swift::swift_distributed_actor_is_remote(DefaultActor *_actor) { + return asImpl(_actor)->isDistributedRemote(); +} + +bool DefaultActorImpl::isDistributedRemote() { + auto state = CurrentState.load(std::memory_order_relaxed); + return state.Flags.isDistributedRemote(); +} \ No newline at end of file diff --git a/stdlib/public/Concurrency/Actor.swift b/stdlib/public/Concurrency/Actor.swift index 183240b63cd06..9cb23e02c0d60 100644 --- a/stdlib/public/Concurrency/Actor.swift +++ b/stdlib/public/Concurrency/Actor.swift @@ -15,7 +15,7 @@ import Swift /// Common protocol to which all actors conform. /// -/// The \c Actor protocol generalizes over all actor types. Actor types +/// The `Actor` protocol generalizes over all actor types. Actor types /// implicitly conform to this protocol. @available(SwiftStdlib 5.5, *) public protocol Actor: AnyObject, Sendable { @@ -51,49 +51,3 @@ public func _defaultActorDestroy(_ actor: AnyObject) @_silgen_name("swift_task_enqueueMainExecutor") @usableFromInline internal func _enqueueOnMain(_ job: UnownedJob) - -/// A singleton actor whose executor is equivalent to -/// \c DispatchQueue.main, which is the main dispatch queue. -@available(SwiftStdlib 5.5, *) -@globalActor public final actor MainActor: SerialExecutor { - public static let shared = MainActor() - - @inlinable - public nonisolated var unownedExecutor: UnownedSerialExecutor { - return asUnownedSerialExecutor() - } - - @inlinable - public nonisolated func asUnownedSerialExecutor() -> UnownedSerialExecutor { - return UnownedSerialExecutor(ordinary: self) - } - - @inlinable - public nonisolated func enqueue(_ job: UnownedJob) { - _enqueueOnMain(job) - } -} - -// Used by the concurrency runtime -@available(SwiftStdlib 5.5, *) -extension SerialExecutor { - @_silgen_name("_swift_task_getMainExecutor") - internal func _getMainExecutor() -> UnownedSerialExecutor { - return MainActor.shared.unownedExecutor - } -} - -@available(SwiftStdlib 5.5, *) -extension MainActor { - /// Execute the given body closure on the main actor. - public static func run( - resultType: T.Type = T.self, - body: @MainActor @Sendable () throws -> T - ) async rethrows -> T { - @MainActor func runOnMain(body: @MainActor @Sendable () throws -> T) async rethrows -> T { - return try body() - } - - return try await runOnMain(body: body) - } -} diff --git a/stdlib/public/Concurrency/AsyncCall.h b/stdlib/public/Concurrency/AsyncCall.h deleted file mode 100644 index 2257f0b05b94d..0000000000000 --- a/stdlib/public/Concurrency/AsyncCall.h +++ /dev/null @@ -1,192 +0,0 @@ -//===--- AsyncCall.h - Conveniences for doing async calls ----------*- C++ -*-// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2020 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 -// -//===----------------------------------------------------------------------===// -// -// Convenience functions for implementing Swift asynchronous functions -// in C++ code. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_CONCURRENCY_ASYNCCALL_H -#define SWIFT_CONCURRENCY_ASYNCCALL_H - -#include "swift/Runtime/Concurrency.h" -#include "swift/ABI/Task.h" -#include - -namespace swift { -namespace { - -/// Template-metaprogrammed basic layout for the given sequence of types. -template -struct BasicLayout; -template -struct BasicLayout { - static constexpr size_t size = StartingOffset; -}; -template -struct BasicLayout { - // Round up to a multiple of the alignment. - static constexpr size_t fieldOffset = - (StartingOffset + alignof(HeadTy) - 1) & ~(alignof(HeadTy) - 1); - static constexpr size_t fieldEnd = fieldOffset + sizeof(HeadTy); - using TailLayout = BasicLayout; - static constexpr size_t size = TailLayout::size; -}; - -template -struct BasicLayoutOffset { - static constexpr size_t value = - BasicLayoutOffset::value; -}; -template -struct BasicLayoutOffset { - static constexpr size_t value = Layout::fieldOffset; -}; - -/// Template-metaprogrammed layout for an async frame with the given -/// signature. Works as long as you don't have a mix of indirect and -/// direct results; indirect results should be coded as initial -/// indirect arguments in the function signature. -/// -/// Note that there's always a slot for an error result. -template -struct AsyncFrameLayout; - -template -struct AsyncFrameLayout> { - using BasicLayout = BasicLayout<0, SwiftError **, ArgTys...>; - static constexpr size_t firstArgIndex = 1; -}; -template -struct AsyncFrameLayout> { - using BasicLayout = BasicLayout<0, SwiftError **, ResultTy, ArgTys...>; - static constexpr size_t firstArgIndex = 2; -}; - -/// A helper class which, when used as a base class under common -/// C++ ABIs, adds no extra size to a struct when the template -/// argument is 0. -template -class AsyncFrameStorageHelper: public AsyncContext { - // This needs to be aligned at least this much or else Itanium - // will try to put it in the tail-padding of the AsyncContext. - alignas(void*) - char buffer[Size]; -public: - using AsyncContext::AsyncContext; - char *data() { return buffer; } -}; -template <> -class AsyncFrameStorageHelper<0>: public AsyncContext { -public: - using AsyncContext::AsyncContext; - char *data() { return reinterpret_cast(this); } -}; - -template > -struct AsyncFrameStorage; -template -struct AsyncFrameStorage, - FrameLayout> - : AsyncFrameStorageHelper { - - AsyncFrameStorage(AsyncContextFlags flags, - TaskContinuationFunction *resumeFunction, - AsyncContext *resumeToContext, - ArgTys... args) - : AsyncFrameStorageHelper( - flags, resumeFunction, resumeToContext) { - initializeHelper(this->data(), args...); - } - -private: - template - void initializeHelper(char *buffer) {} - - template - void initializeHelper(char *buffer, NextArgTy nextArg, - TailArgTys... tailArgs) { - auto offset = BasicLayoutOffset::value; - new (buffer + offset) NextArgTy(nextArg); - initializeHelper(buffer, tailArgs...); - } -}; - - -/// The context header for calling a function that takes the -/// given arguments. -template -struct AsyncCalleeContext : AsyncFrameStorage { - using CallerContext = CallerContextType; - - template - AsyncCalleeContext(TaskContinuationFunction *resumeFunction, - CallerContext *resumeToContext, - Args... args) - : AsyncFrameStorage(AsyncContextKind::Ordinary, - resumeFunction, - resumeToContext, args...) {} - - CallerContext *getParent() const { - return static_cast(this->Parent); - } -}; - -/// Push a context to call a function. -template -static AsyncCalleeContext * -pushAsyncContext(CallerContext *callerContext, size_t calleeContextSize, - TaskContinuationFunction *resumeFunction, - Args... args) { - using CalleeContext = - AsyncCalleeContext; - - void *rawCalleeContext = swift_task_alloc(calleeContextSize); - // We no longer store arguments in the context so we can just cast to an async - // context. - return reinterpret_cast(new (rawCalleeContext) AsyncContext( - AsyncContextKind::Ordinary, resumeFunction, callerContext)); -} - -/// Make an asynchronous call. -template -SWIFT_CC(swiftasync) -static void callAsync(CallerContext *callerContext, - TaskContinuationFunction *resumeFunction, - const typename CalleeSignature::FunctionPointer *function, - Args... args) { - auto calleeContextSize = function->ExpectedContextSize; - auto calleeContext = pushAsyncContext(callerContext, - calleeContextSize, resumeFunction, - args...); - return function->Function(calleeContext); -} - -/// Given that that we've just entered the caller's continuation function -/// upon return from a function previously called with callAsync, pop the -/// callee's context and return the caller's context. -template -static typename CalleeContext::CallerContext * -popAsyncContext(CalleeContext *calleeContext) { - auto callerContext = calleeContext->getParent(); - swift_task_dealloc(calleeContext); - return callerContext; -} - -} // end anonymous namespace -} // end namespace swift - -#endif diff --git a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift index 7fe0d7345de9e..1ae17a3c6e8f2 100644 --- a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift @@ -14,6 +14,37 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Creates an asynchronous sequence that maps the given closure over the + /// asynchronous sequence’s elements, omitting results that don't return a + /// value. + /// + /// Use the `compactMap(_:)` method to transform every element received from + /// a base asynchronous sequence, while also discarding any `nil` results + /// from the closure. Typically, you use this to transform from one type of + /// element to another. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `5`. The closure provided to the `compactMap(_:)` + /// method takes each `Int` and looks up a corresponding `String` from a + /// `romanNumeralDict` dictionary. Because there is no key for `4`, the closure + /// returns `nil` in this case, which `compactMap(_:)` omits from the + /// transformed asynchronous sequence. + /// + /// let romanNumeralDict: [Int : String] = + /// [1: "I", 2: "II", 3: "III", 5: "V"] + /// + /// let stream = Counter(howHigh: 5) + /// .compactMap { romanNumeralDict[$0] } + /// for await numeral in stream { + /// print("\(numeral) ", terminator: " ") + /// } + /// // Prints: I II III V + /// + /// - Parameter transform: A mapping closure. `transform` accepts an element + /// of this sequence as its parameter and returns a transformed value of the + /// same or of a different type. + /// - Returns: An asynchronous sequence that contains, in order, the + /// non-`nil` elements produced by the `transform` closure. @inlinable public __consuming func compactMap( _ transform: @escaping (Element) async -> ElementOfResult? @@ -22,7 +53,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence that maps a given closure over the asynchronous +/// sequence’s elements, omitting results that don't return a value. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncCompactMapSequence { @usableFromInline let base: Base @@ -30,7 +64,7 @@ public struct AsyncCompactMapSequence { @usableFromInline let transform: (Base.Element) async -> ElementOfResult? - @usableFromInline + @inlinable init( _ base: Base, transform: @escaping (Base.Element) async -> ElementOfResult? @@ -42,9 +76,16 @@ public struct AsyncCompactMapSequence { @available(SwiftStdlib 5.5, *) extension AsyncCompactMapSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The compact map sequence produces whatever type of element its + /// transforming closure produces. public typealias Element = ElementOfResult + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the compact map sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { public typealias Element = ElementOfResult @@ -54,7 +95,7 @@ extension AsyncCompactMapSequence: AsyncSequence { @usableFromInline let transform: (Base.Element) async -> ElementOfResult? - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, transform: @escaping (Base.Element) async -> ElementOfResult? @@ -63,6 +104,14 @@ extension AsyncCompactMapSequence: AsyncSequence { self.transform = transform } + /// Produces the next element in the compact map sequence. + /// + /// This iterator calls `next()` on its base iterator; if this call returns + /// `nil`, `next()` returns `nil`. Otherwise, `next()` calls the + /// transforming closure on the received element, returning it if the + /// transform returns a non-`nil` value. If the transform returns `nil`, + /// this method continues to wait for further elements until it gets one + /// that transforms to a non-`nil` value. @inlinable public mutating func next() async rethrows -> ElementOfResult? { while true { diff --git a/stdlib/public/Concurrency/AsyncDropFirstSequence.swift b/stdlib/public/Concurrency/AsyncDropFirstSequence.swift index d154ad59a8679..9408afd339cca 100644 --- a/stdlib/public/Concurrency/AsyncDropFirstSequence.swift +++ b/stdlib/public/Concurrency/AsyncDropFirstSequence.swift @@ -14,6 +14,28 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Omits a specified number of elements from the base asynchronous sequence, + /// then passes through all remaining elements. + /// + /// Use `dropFirst(_:)` when you want to drop the first *n* elements from the + /// base sequence and pass through the remaining elements. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `dropFirst(_:)` method causes the modified + /// sequence to ignore the values `0` through `4`, and instead emit `5` through `10`: + /// + /// for await number in Counter(howHigh: 10).dropFirst(3) { + /// print("\(number) ", terminator: " ") + /// } + /// // prints "4 5 6 7 8 9 10" + /// + /// If the number of elements to drop exceeds the number of elements in the + /// sequence, the result is an empty sequence. + /// + /// - Parameter count: The number of elements to drop from the beginning of + /// the sequence. `count` must be greater than or equal to zero. + /// - Returns: An asynchronous sequence that drops the first `count` + /// elements from the base sequence. @inlinable public __consuming func dropFirst( _ count: Int = 1 @@ -24,7 +46,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence which omits a specified number of elements from the +/// base asynchronous sequence, then passes through all remaining elements. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncDropFirstSequence { @usableFromInline let base: Base @@ -32,7 +57,7 @@ public struct AsyncDropFirstSequence { @usableFromInline let count: Int - @usableFromInline + @inlinable init(_ base: Base, dropping count: Int) { self.base = base self.count = count @@ -41,9 +66,16 @@ public struct AsyncDropFirstSequence { @available(SwiftStdlib 5.5, *) extension AsyncDropFirstSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The drop-first sequence produces whatever type of element its base + /// iterator produces. public typealias Element = Base.Element + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the drop-first sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @@ -51,12 +83,20 @@ extension AsyncDropFirstSequence: AsyncSequence { @usableFromInline var count: Int - @usableFromInline + @inlinable init(_ baseIterator: Base.AsyncIterator, count: Int) { self.baseIterator = baseIterator self.count = count } + /// Produces the next element in the drop-first sequence. + /// + /// Until reaching the number of elements to drop, this iterator calls + /// `next()` on its base iterator and discards the result. If the base + /// iterator returns `nil`, indicating the end of the sequence, this + /// iterator returns `nil`. After reaching the number of elements to + /// drop, this iterator passes along the result of calling `next()` on + /// the base iterator. @inlinable public mutating func next() async rethrows -> Base.Element? { var remainingToDrop = count @@ -80,6 +120,12 @@ extension AsyncDropFirstSequence: AsyncSequence { @available(SwiftStdlib 5.5, *) extension AsyncDropFirstSequence { + /// Omits a specified number of elements from the base asynchronous sequence, + /// then passes through all remaining elements. + /// + /// When you call `dropFirst(_:)` on an asynchronous sequence that is already + /// an `AsyncDropFirstSequence`, the returned sequence simply adds the new + /// drop count to the current drop count. @inlinable public __consuming func dropFirst( _ count: Int = 1 @@ -91,4 +137,3 @@ extension AsyncDropFirstSequence { return AsyncDropFirstSequence(base, dropping: self.count + count) } } - diff --git a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift index ea66740ca8744..7f52fde1da482 100644 --- a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift @@ -14,6 +14,33 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Omits elements from the base asynchronous sequence until a given closure + /// returns false, after which it passes through all remaining elements. + /// + /// Use `drop(while:)` to omit elements from an asynchronous sequence until + /// the element received meets a condition you specify. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `drop(while:)` method causes the modified + /// sequence to ignore received values until it encounters one that is + /// divisible by `3`: + /// + /// let stream = Counter(howHigh: 10) + /// .drop { $0 % 3 != 0 } + /// for await number in stream { + /// print("\(number) ", terminator: " ") + /// } + /// // prints "3 4 5 6 7 8 9 10" + /// + /// After the predicate returns `false`, the sequence never executes it again, + /// and from then on the sequence passes through elements from its underlying + /// sequence as-is. + /// + /// - Parameter predicate: A closure that takes an element as a parameter and + /// returns a Boolean value indicating whether to drop the element from the + /// modified sequence. + /// - Returns: An asynchronous sequence that skips over values from the + /// base sequence until the provided closure returns `false`. @inlinable public __consuming func drop( while predicate: @escaping (Element) async -> Bool @@ -22,7 +49,11 @@ extension AsyncSequence { } } +/// An asynchronous sequence which omits elements from the base sequence until a +/// given closure returns false, after which it passes through all remaining +/// elements. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncDropWhileSequence { @usableFromInline let base: Base @@ -30,7 +61,7 @@ public struct AsyncDropWhileSequence { @usableFromInline let predicate: (Base.Element) async -> Bool - @usableFromInline + @inlinable init( _ base: Base, predicate: @escaping (Base.Element) async -> Bool @@ -42,9 +73,17 @@ public struct AsyncDropWhileSequence { @available(SwiftStdlib 5.5, *) extension AsyncDropWhileSequence: AsyncSequence { + + /// The type of element produced by this asynchronous sequence. + /// + /// The drop-while sequence produces whatever type of element its base + /// sequence produces. public typealias Element = Base.Element + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the drop-while sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @@ -52,7 +91,7 @@ extension AsyncDropWhileSequence: AsyncSequence { @usableFromInline var predicate: ((Base.Element) async -> Bool)? - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, predicate: @escaping (Base.Element) async -> Bool @@ -61,6 +100,14 @@ extension AsyncDropWhileSequence: AsyncSequence { self.predicate = predicate } + /// Produces the next element in the drop-while sequence. + /// + /// This iterator calls `next()` on its base iterator and evaluates the + /// result with the `predicate` closure. As long as the predicate returns + /// `true`, this method returns `nil`. After the predicate returns `false`, + /// for a value received from the base iterator, this method returns that + /// value. After that, the iterator returns values received from its + /// base iterator as-is, and never executes the predicate closure again. @inlinable public mutating func next() async rethrows -> Base.Element? { while let predicate = self.predicate { @@ -76,6 +123,7 @@ extension AsyncDropWhileSequence: AsyncSequence { } } + /// Creates an instance of the drop-while sequence iterator. @inlinable public __consuming func makeAsyncIterator() -> Iterator { return Iterator(base.makeAsyncIterator(), predicate: predicate) diff --git a/stdlib/public/Concurrency/AsyncFilterSequence.swift b/stdlib/public/Concurrency/AsyncFilterSequence.swift index 3e745cd374b3c..ca63f19a92fd3 100644 --- a/stdlib/public/Concurrency/AsyncFilterSequence.swift +++ b/stdlib/public/Concurrency/AsyncFilterSequence.swift @@ -14,6 +14,25 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Creates an asynchronous sequence that contains, in order, the elements of + /// the base sequence that satisfy the given predicate. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `filter(_:)` method returns `true` for even + /// values and `false` for odd values, thereby filtering out the odd values: + /// + /// let stream = Counter(howHigh: 10) + /// .filter { $0 % 2 == 0 } + /// for await number in stream { + /// print("\(number) ", terminator: " ") + /// } + /// // Prints: 2 4 6 8 10 + /// + /// - Parameter isIncluded: A closure that takes an element of the + /// asynchronous sequence as its argument and returns a Boolean value + /// that indicates whether to include the element in the filtered sequence. + /// - Returns: An asynchronous sequence that contains, in order, the elements + /// of the base sequence that satisfy the given predicate. @inlinable public __consuming func filter( _ isIncluded: @escaping (Element) async -> Bool @@ -22,7 +41,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence that contains, in order, the elements of +/// the base sequence that satisfy a given predicate. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncFilterSequence { @usableFromInline let base: Base @@ -30,7 +52,7 @@ public struct AsyncFilterSequence { @usableFromInline let isIncluded: (Element) async -> Bool - @usableFromInline + @inlinable init( _ base: Base, isIncluded: @escaping (Base.Element) async -> Bool @@ -42,9 +64,16 @@ public struct AsyncFilterSequence { @available(SwiftStdlib 5.5, *) extension AsyncFilterSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The filter sequence produces whatever type of element its base + /// sequence produces. public typealias Element = Base.Element + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the filter sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @@ -52,7 +81,7 @@ extension AsyncFilterSequence: AsyncSequence { @usableFromInline let isIncluded: (Base.Element) async -> Bool - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, isIncluded: @escaping (Base.Element) async -> Bool @@ -61,6 +90,13 @@ extension AsyncFilterSequence: AsyncSequence { self.isIncluded = isIncluded } + /// Produces the next element in the filter sequence. + /// + /// This iterator calls `next()` on its base iterator; if this call returns + /// `nil`, `next()` returns nil. Otherwise, `next()` evaluates the + /// result with the `predicate` closure. If the closure returns `true`, + /// `next()` returns the received element; otherwise it awaits the next + /// element from the base iterator. @inlinable public mutating func next() async rethrows -> Base.Element? { while true { diff --git a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift index 0c2894fca8d3b..ce05bfd7238ac 100644 --- a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift @@ -14,7 +14,32 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { - @inlinable + /// Creates an asynchronous sequence that concatenates the results of calling + /// the given transformation with each element of this sequence. + /// + /// Use this method to receive a single-level asynchronous sequence when your + /// transformation produces an asynchronous sequence for each element. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `5`. The transforming closure takes the received `Int` + /// and returns a new `Counter` that counts that high. For example, when the + /// transform receives `3` from the base sequence, it creates a new `Counter` + /// that produces the values `1`, `2`, and `3`. The `flatMap(_:)` method + /// "flattens" the resulting sequence-of-sequences into a single + /// `AsyncSequence`. + /// + /// let stream = Counter(howHigh: 5) + /// .flatMap { Counter(howHigh: $0) } + /// for await number in stream { + /// print("\(number)", terminator: " ") + /// } + /// // Prints: 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 + /// + /// - Parameter transform: A mapping closure. `transform` accepts an element + /// of this sequence as its parameter and returns an `AsyncSequence`. + /// - Returns: A single, flattened asynchronous sequence that contains all + /// elements in all the asychronous sequences produced by `transform`. + @inlinable public __consuming func flatMap( _ transform: @escaping (Element) async -> SegmentOfResult ) -> AsyncFlatMapSequence { @@ -22,7 +47,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence that concatenates the results of calling a given +/// transformation with each element of this sequence. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncFlatMapSequence { @usableFromInline let base: Base @@ -30,7 +58,7 @@ public struct AsyncFlatMapSequence SegmentOfResult - @usableFromInline + @inlinable init( _ base: Base, transform: @escaping (Base.Element) async -> SegmentOfResult @@ -42,9 +70,16 @@ public struct AsyncFlatMapSequence SegmentOfResult @@ -67,6 +102,15 @@ extension AsyncFlatMapSequence: AsyncSequence { self.transform = transform } + /// Produces the next element in the flat map sequence. + /// + /// This iterator calls `next()` on its base iterator; if this call returns + /// `nil`, `next()` returns `nil`. Otherwise, `next()` calls the + /// transforming closure on the received element, takes the resulting + /// asynchronous sequence, and creates an asynchronous iterator from it. + /// `next()` then consumes values from this iterator until it terminates. + /// At this point, `next()` is ready to receive the next value from the base + /// sequence. @inlinable public mutating func next() async rethrows -> SegmentOfResult.Element? { while !finished { diff --git a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift index 8487a6642a508..5fdf352a0a169 100644 --- a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift +++ b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift @@ -12,9 +12,86 @@ import Swift +/// A type that that asychronously supplies the values of a sequence one at a +/// time. +/// +/// The `AsyncIteratorProtocol` defines the type returned by the +/// `makeAsyncIterator()` method of the `AsyncSequence` protocol. In short, +/// the iterator is what produces the asynchronous sequence's values. The +/// protocol defines a single asynchronous method, `next()`, which either +/// produces the next element of the sequence, or returns `nil` to signal +/// the end of the sequence. +/// +/// To implement your own `AsyncSequence`, implement a wrapped type that +/// conforms to `AsyncIteratorProtocol`. The following example shows a `Counter` +/// type that uses an inner iterator to monotonically generate `Int` values +/// until reaching a `howHigh` value. While this example isn't itself +/// asychronous, it shows the shape of a custom sequence and iterator, and how +/// to use it as if it were asynchronous: +/// +/// struct Counter : AsyncSequence { +/// typealias Element = Int +/// let howHigh: Int +/// +/// struct AsyncIterator : AsyncIteratorProtocol { +/// let howHigh: Int +/// var current = 1 +/// mutating func next() async -> Int? { +/// // A genuinely asychronous implementation uses the `Task` +/// // API to check for cancellation here and return early. +/// guard current <= howHigh else { +/// return nil +/// } +/// +/// let result = current +/// current += 1 +/// return result +/// } +/// } +/// +/// func makeAsyncIterator() -> AsyncIterator { +/// return AsyncIterator(howHigh: howHigh) +/// } +/// } +/// +/// At the call site, this looks like: +/// +/// for await i in Counter(howHigh: 10) { +/// print(i, terminator: " ") +/// } +/// // Prints: 1 2 3 4 5 6 7 8 9 10 +/// +/// ### End of Iteration +/// +/// The iterator returns `nil` to indicate the end of the sequence. After +/// returning `nil` (or throwing an error) from `next()`, the iterator enters +/// a terminal state, and all future calls to `next()` must return `nil`. +/// +/// ### Cancellation +/// +/// Types conforming to `AsyncIteratorProtocol` should use the cancellation +/// primitives provided by Swift's `Task` API. The iterator can choose how to +/// handle and respond to cancellation, including: +/// +/// - Checking the `isCancelled` value of the current `Task` inside `next()` +/// and returning `nil` to terminate the sequence. +/// - Calling `checkCancellation()` on the `Task`, which throws a +/// `CancellationError`. +/// - Implementing `next()` with a +/// `withTaskCancellationHandler(handler:operation:)` invocation to +/// immediately react to cancellation. +/// +/// If the iterator needs to clean up on cancellation, it can do so after +/// checking for cancellation as described above, or in `deinit` if it's +/// a reference type. @available(SwiftStdlib 5.5, *) @rethrows public protocol AsyncIteratorProtocol { associatedtype Element + /// Asynchronously advances to the next element and returns it, or ends the + /// sequence if there is no next element. + /// + /// - Returns: The next element, if it exists, or `nil` to signal the end of + /// the sequence. mutating func next() async throws -> Element? } diff --git a/stdlib/public/Concurrency/AsyncLet.cpp b/stdlib/public/Concurrency/AsyncLet.cpp index 81e69520e3044..a927f7e543c76 100644 --- a/stdlib/public/Concurrency/AsyncLet.cpp +++ b/stdlib/public/Concurrency/AsyncLet.cpp @@ -16,13 +16,14 @@ #include "../CompatibilityOverride/CompatibilityOverride.h" #include "swift/Runtime/Concurrency.h" -#include "swift/ABI/Task.h" #include "swift/ABI/AsyncLet.h" #include "swift/ABI/Metadata.h" +#include "swift/ABI/Task.h" +#include "swift/ABI/TaskOptions.h" #include "swift/Runtime/Mutex.h" #include "swift/Runtime/HeapObject.h" +#include "llvm/ADT/PointerIntPair.h" #include "TaskPrivate.h" -#include "AsyncCall.h" #include "Debug.h" #if !defined(_WIN32) @@ -32,24 +33,32 @@ using namespace swift; namespace { -class AsyncLetImpl: public ChildTaskStatusRecord { +class alignas(Alignment_AsyncLet) AsyncLetImpl: public ChildTaskStatusRecord { public: // This is where we could define a Status or other types important for async-let private: - - /// The task that was kicked off to initialize this `async let`. - AsyncTask *task; - - // TODO: more additional flags here, we can use them for future optimizations. - // e.g. "was awaited on" or "needs free" - - friend class AsyncTask; + // Flags stored in the low bits of the task pointer. + enum { + HasResult = 1 << 0, + DidAllocateFromParentTask = 1 << 1, + }; + + /// The task that was kicked off to initialize this `async let`, + /// and flags. + llvm::PointerIntPair taskAndFlags; + + /// Reserved space for a future_wait context frame, used during suspensions + /// on the child task future. + std::aligned_storage::type futureWaitContextStorage; + + friend class ::swift::AsyncTask; public: explicit AsyncLetImpl(AsyncTask* task) : ChildTaskStatusRecord(task), - task(task) { + taskAndFlags(task, 0) { assert(task->hasChildFragment() && "async let task must be a child task."); } @@ -61,9 +70,48 @@ class AsyncLetImpl: public ChildTaskStatusRecord { } AsyncTask *getTask() const { - return task; + return taskAndFlags.getPointer(); + } + + bool hasResultInBuffer() const { + return taskAndFlags.getInt() & HasResult; + } + + void setHasResultInBuffer(bool value = true) { + if (value) + taskAndFlags.setInt(taskAndFlags.getInt() | HasResult); + else + taskAndFlags.setInt(taskAndFlags.getInt() & ~HasResult); + } + + bool didAllocateFromParentTask() const { + return taskAndFlags.getInt() & DidAllocateFromParentTask; + } + + void setDidAllocateFromParentTask(bool value = true) { + if (value) + taskAndFlags.setInt(taskAndFlags.getInt() | DidAllocateFromParentTask); + else + taskAndFlags.setInt(taskAndFlags.getInt() & ~DidAllocateFromParentTask); + } + + // The compiler preallocates a large fixed space for the `async let`, with the + // intent that most of it be used for the child task context. The next two + // methods return the address and size of that space. + + /// Return a pointer to the unused space within the async let block. + void *getPreallocatedSpace() { + return (void*)(this + 1); + } + + /// Return the size of the unused space within the async let block. + static constexpr size_t getSizeOfPreallocatedSpace() { + return sizeof(AsyncLet) - sizeof(AsyncLetImpl); + } + + TaskFutureWaitAsyncContext *getFutureContext() { + return reinterpret_cast(&futureWaitContextStorage); } - }; // end AsyncLetImpl } // end anonymous namespace @@ -86,45 +134,62 @@ static AsyncLetImpl *asImpl(const AsyncLet *alet) { const_cast(alet)); } -static AsyncLet *asAbstract(AsyncLetImpl *alet) { - return reinterpret_cast(alet); +void swift::asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet, + bool didAllocateInParentTask) { + AsyncLetImpl *impl = new (asyncLet) AsyncLetImpl(task); + impl->setDidAllocateFromParentTask(didAllocateInParentTask); + + auto record = impl->getTaskRecord(); + assert(impl == record && "the async-let IS the task record"); + + // ok, now that the group actually is initialized: attach it to the task + swift_task_addStatusRecord(record); } // ============================================================================= // ==== start ------------------------------------------------------------------ SWIFT_CC(swift) -static void swift_asyncLet_startImpl(AsyncLet *alet, - const Metadata *futureResultType, - void *closureEntryPoint, - void *closureContext) { - AsyncTask *parent = swift_task_getCurrent(); - assert(parent && "async-let cannot be created without parent task"); - - auto flags = JobFlags(JobKind::Task, parent->getPriority()); - flags.task_setIsFuture(true); - flags.task_setIsChildTask(true); - - auto childTaskAndContext = swift_task_create_async_let_future( - flags, +void swift::swift_asyncLet_start(AsyncLet *alet, + TaskOptionRecord *options, + const Metadata *futureResultType, + void *closureEntryPoint, + HeapObject *closureContext) { + auto flags = TaskCreateFlags(); + flags.setEnqueueJob(true); + + AsyncLetTaskOptionRecord asyncLetOptionRecord(alet); + asyncLetOptionRecord.Parent = options; + + swift_task_create( + flags.getOpaqueValue(), + &asyncLetOptionRecord, futureResultType, - closureEntryPoint, - closureContext); - - AsyncTask *childTask = childTaskAndContext.Task; - - assert(childTask->isFuture()); - assert(childTask->hasChildFragment()); - AsyncLetImpl *impl = new (alet) AsyncLetImpl(childTask); - - auto record = impl->getTaskRecord(); - assert(impl == record && "the async-let IS the task record"); - - // ok, now that the group actually is initialized: attach it to the task - swift_task_addStatusRecord(record); + closureEntryPoint, closureContext); +} - // schedule the task - swift_task_enqueueGlobal(childTask); +SWIFT_CC(swift) +void swift::swift_asyncLet_begin(AsyncLet *alet, + TaskOptionRecord *options, + const Metadata *futureResultType, + void *closureEntryPoint, + HeapObject *closureContext, + void *resultBuffer) { + SWIFT_TASK_DEBUG_LOG("creating async let buffer of type %s at %p", + swift_getTypeName(futureResultType, true).data, + resultBuffer); + + auto flags = TaskCreateFlags(); + flags.setEnqueueJob(true); + + AsyncLetWithBufferTaskOptionRecord asyncLetOptionRecord(alet, resultBuffer); + asyncLetOptionRecord.Parent = options; + + swift_task_create( + flags.getOpaqueValue(), + &asyncLetOptionRecord, + futureResultType, + closureEntryPoint, closureContext); } // ============================================================================= @@ -132,19 +197,104 @@ static void swift_asyncLet_startImpl(AsyncLet *alet, SWIFT_CC(swiftasync) static void swift_asyncLet_waitImpl( - OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - AsyncLet *alet, Metadata *T) { - auto waitingTask = swift_task_getCurrent(); + OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, TaskContinuationFunction *resumeFunction, + AsyncContext *callContext) { auto task = alet->getTask(); - swift_task_future_wait(result, rawContext, task, T); + swift_task_future_wait(result, callerContext, task, resumeFunction, + callContext); } SWIFT_CC(swiftasync) static void swift_asyncLet_wait_throwingImpl( - OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - AsyncLet *alet, Metadata *T) { + OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, + ThrowingTaskFutureWaitContinuationFunction *resumeFunction, + AsyncContext * callContext) { auto task = alet->getTask(); - swift_task_future_wait_throwing(result, rawContext, task, T); + swift_task_future_wait_throwing(result, callerContext, task, resumeFunction, + callerContext); +} + +// ============================================================================= +// ==== get ------------------------------------------------------------------- + +SWIFT_CC(swiftasync) +static void swift_asyncLet_getImpl(SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, + void *resultBuffer, + TaskContinuationFunction *resumeFunction, + AsyncContext *callContext) { + // Don't need to do anything if the result buffer is already populated. + if (asImpl(alet)->hasResultInBuffer()) { + return resumeFunction(callerContext); + } + + // Mark the async let as having its result populated. + // The only task that can ask this of the async let is the same parent task + // that's currently executing, so we can set it now and tail-call future_wait, + // since by the time we can call back it will be populated. + asImpl(alet)->setHasResultInBuffer(); + swift_task_future_wait(reinterpret_cast(resultBuffer), + callerContext, alet->getTask(), + resumeFunction, callContext); +} + +struct AsyncLetContinuationContext: AsyncContext { + AsyncLet *alet; + OpaqueValue *resultBuffer; +}; + +static_assert(sizeof(AsyncLetContinuationContext) <= sizeof(TaskFutureWaitAsyncContext), + "compiler provides the same amount of context space to each"); + +SWIFT_CC(swiftasync) +static void _asyncLet_get_throwing_continuation( + SWIFT_ASYNC_CONTEXT AsyncContext *callContext, + SWIFT_CONTEXT void *error) { + auto continuationContext = static_cast(callContext); + auto alet = continuationContext->alet; + + // If the future completed successfully, its result is now in the async let + // buffer. + if (!error) { + asImpl(alet)->setHasResultInBuffer(); + } + + // Continue the caller's execution. + auto throwingResume + = reinterpret_cast(callContext->ResumeParent); + return throwingResume(callContext->Parent, error); +} + +SWIFT_CC(swiftasync) +static void swift_asyncLet_get_throwingImpl( + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, + void *resultBuffer, + ThrowingTaskFutureWaitContinuationFunction *resumeFunction, + AsyncContext *callContext) { + // Don't need to do anything if the result buffer is already populated. + if (asImpl(alet)->hasResultInBuffer()) { + return resumeFunction(callerContext, nullptr); + } + + auto aletContext = static_cast(callContext); + aletContext->ResumeParent + = reinterpret_cast(resumeFunction); + aletContext->Parent = callerContext; + aletContext->alet = alet; + auto futureContext = asImpl(alet)->getFutureContext(); + + // Unlike the non-throwing variant, whether we end up with a result depends + // on the success of the task. If we raise an error, then the result buffer + // will not be populated. Save the async let binding so we can fetch it + // after completion. + return swift_task_future_wait_throwing( + reinterpret_cast(resultBuffer), + aletContext, alet->getTask(), + _asyncLet_get_throwing_continuation, + futureContext); } // ============================================================================= @@ -167,12 +317,211 @@ static void swift_asyncLet_endImpl(AsyncLet *alet) { AsyncTask *parent = swift_task_getCurrent(); assert(parent && "async-let must have a parent task"); -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] async let end of task %p, parent: %p\n", pthread_self(), task, parent); -#endif + SWIFT_TASK_DEBUG_LOG("async let end of task %p, parent: %p", task, parent); _swift_task_dealloc_specific(parent, task); } +// ============================================================================= +// ==== finish ----------------------------------------------------------------- + +SWIFT_CC(swiftasync) +// FIXME: noinline to work around an LLVM bug where the outliner breaks +// musttail. +SWIFT_NOINLINE +static void asyncLet_finish_after_task_completion(SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, + TaskContinuationFunction *resumeFunction, + AsyncContext *callContext, + SWIFT_CONTEXT void *error) { + auto task = alet->getTask(); + + // Remove the child record from the parent task + auto record = asImpl(alet)->getTaskRecord(); + swift_task_removeStatusRecord(record); + + // and finally, release the task and destroy the async-let + assert(swift_task_getCurrent() && "async-let must have a parent task"); + + SWIFT_TASK_DEBUG_LOG("async let end of task %p, parent: %p", task, + swift_task_getCurrent()); + // Destruct the task. + task->~AsyncTask(); + // Deallocate it out of the parent, if it was allocated there. + if (alet->didAllocateFromParentTask()) { + swift_task_dealloc(task); + } + + return reinterpret_cast(resumeFunction) + (callerContext, error); +} + + +SWIFT_CC(swiftasync) +static void _asyncLet_finish_continuation( + SWIFT_ASYNC_CONTEXT AsyncContext *callContext, + SWIFT_CONTEXT void *error) { + // Retrieve the async let pointer from the context. + auto continuationContext + = reinterpret_cast(callContext); + auto alet = continuationContext->alet; + auto resultBuffer = continuationContext->resultBuffer; + + // Destroy the error, or the result that was stored to the buffer. + if (error) { + swift_errorRelease((SwiftError*)error); + } else { + alet->getTask()->futureFragment()->getResultType()->vw_destroy(resultBuffer); + } + + // Clean up the async let now that the task has finished. + return asyncLet_finish_after_task_completion(callContext->Parent, + alet, + callContext->ResumeParent, + callContext, + nullptr); +} + +SWIFT_CC(swiftasync) +static void swift_asyncLet_finishImpl(SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, + void *resultBuffer, + TaskContinuationFunction *resumeFunction, + AsyncContext *callContext) { + auto task = alet->getTask(); + + // If the result buffer is already populated, then we just need to destroy + // the value in it and then clean up the task. + if (asImpl(alet)->hasResultInBuffer()) { + task->futureFragment()->getResultType()->vw_destroy( + reinterpret_cast(resultBuffer)); + return asyncLet_finish_after_task_completion(callerContext, + alet, + resumeFunction, + callContext, + nullptr); + } + // Otherwise, cancel the task and let it finish first. + swift_task_cancel(task); + + // Save the async let pointer in the context so we can clean it up once the + // future completes. + auto aletContext = static_cast(callContext); + aletContext->Parent = callerContext; + aletContext->ResumeParent = resumeFunction; + aletContext->alet = alet; + aletContext->resultBuffer = reinterpret_cast(resultBuffer); + auto futureContext = asImpl(alet)->getFutureContext(); + + // TODO: It would be nice if we could await the future without having to + // provide a buffer to store the value to, since we're going to dispose of + // it anyway. + return swift_task_future_wait_throwing( + reinterpret_cast(resultBuffer), + callContext, alet->getTask(), + _asyncLet_finish_continuation, + futureContext); +} + +// ============================================================================= +// ==== consume ---------------------------------------------------------------- + +SWIFT_CC(swiftasync) +static void _asyncLet_consume_continuation( + SWIFT_ASYNC_CONTEXT AsyncContext *callContext) { + // Retrieve the async let pointer from the context. + auto continuationContext + = reinterpret_cast(callContext); + auto alet = continuationContext->alet; + + // Clean up the async let now that the task has finished. + return asyncLet_finish_after_task_completion(callContext->Parent, alet, + callContext->ResumeParent, + callContext, + nullptr); +} + +SWIFT_CC(swiftasync) +static void swift_asyncLet_consumeImpl(SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, + void *resultBuffer, + TaskContinuationFunction *resumeFunction, + AsyncContext *callContext) { + // If the result buffer is already populated, then we just need to clean up + // the task. + if (asImpl(alet)->hasResultInBuffer()) { + return asyncLet_finish_after_task_completion(callerContext, + alet, + resumeFunction, + callContext, + nullptr); + } + + // Save the async let pointer in the context so we can clean it up once the + // future completes. + auto aletContext = static_cast(callContext); + aletContext->Parent = callerContext; + aletContext->ResumeParent = resumeFunction; + aletContext->alet = alet; + auto futureContext = asImpl(alet)->getFutureContext(); + + // Await completion of the task. We'll destroy the task afterward. + return swift_task_future_wait( + reinterpret_cast(resultBuffer), + callContext, alet->getTask(), + _asyncLet_consume_continuation, + futureContext); +} + +SWIFT_CC(swiftasync) +static void _asyncLet_consume_throwing_continuation( + SWIFT_ASYNC_CONTEXT AsyncContext *callContext, + SWIFT_CONTEXT void *error) { + // Get the async let pointer so we can destroy the task. + auto continuationContext = static_cast(callContext); + auto alet = continuationContext->alet; + + return asyncLet_finish_after_task_completion(callContext->Parent, + alet, + callContext->ResumeParent, + callContext, + error); +} + +SWIFT_CC(swiftasync) +static void swift_asyncLet_consume_throwingImpl( + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncLet *alet, + void *resultBuffer, + ThrowingTaskFutureWaitContinuationFunction *resumeFunction, + AsyncContext *callContext) { + // If the result buffer is already populated, we just need to clean up the + // task. + if (asImpl(alet)->hasResultInBuffer()) { + return asyncLet_finish_after_task_completion(callerContext, + alet, + reinterpret_cast(resumeFunction), + callContext, + nullptr); + } + + auto aletContext = static_cast(callContext); + aletContext->ResumeParent + = reinterpret_cast(resumeFunction); + aletContext->Parent = callerContext; + aletContext->alet = alet; + auto futureContext = asImpl(alet)->getFutureContext(); + + // Unlike the non-throwing variant, whether we end up with a result depends + // on the success of the task. If we raise an error, then the result buffer + // will not be populated. Save the async let binding so we can fetch it + // after completion. + return swift_task_future_wait_throwing( + reinterpret_cast(resultBuffer), + aletContext, alet->getTask(), + _asyncLet_consume_throwing_continuation, + futureContext); +} + // ============================================================================= // ==== AsyncLet Implementation ------------------------------------------------ @@ -180,6 +529,22 @@ AsyncTask* AsyncLet::getTask() const { return asImpl(this)->getTask(); } +void *AsyncLet::getPreallocatedSpace() { + return asImpl(this)->getPreallocatedSpace(); +} + +size_t AsyncLet::getSizeOfPreallocatedSpace() { + return AsyncLetImpl::getSizeOfPreallocatedSpace(); +} + +bool AsyncLet::didAllocateFromParentTask() { + return asImpl(this)->didAllocateFromParentTask(); +} + +void AsyncLet::setDidAllocateFromParentTask(bool value) { + return asImpl(this)->setDidAllocateFromParentTask(value); +} + // ============================================================================= #define OVERRIDE_ASYNC_LET COMPATIBILITY_OVERRIDE diff --git a/stdlib/public/Concurrency/AsyncLet.swift b/stdlib/public/Concurrency/AsyncLet.swift index 00ee815333007..be69a63239c53 100644 --- a/stdlib/public/Concurrency/AsyncLet.swift +++ b/stdlib/public/Concurrency/AsyncLet.swift @@ -20,25 +20,41 @@ import Swift @_silgen_name("swift_asyncLet_start") public func _asyncLetStart( asyncLet: Builtin.RawPointer, + options: Builtin.RawPointer?, operation: @Sendable () async throws -> T ) -/// Similar to _taskFutureGet but for AsyncLet +/// DEPRECATED. use _asyncLet_get instead @available(SwiftStdlib 5.5, *) @_silgen_name("swift_asyncLet_wait") public func _asyncLetGet(asyncLet: Builtin.RawPointer) async -> T -///// Similar to _taskFutureGetThrowing but for AsyncLet +/// DEPRECATED. use _asyncLet_get_throwing instead @available(SwiftStdlib 5.5, *) @_silgen_name("swift_asyncLet_wait_throwing") public func _asyncLetGetThrowing(asyncLet: Builtin.RawPointer) async throws -> T +/// DEPRECATED. use _asyncLet_finish instead @available(SwiftStdlib 5.5, *) @_silgen_name("swift_asyncLet_end") public func _asyncLetEnd( asyncLet: Builtin.RawPointer // TODO: should this take __owned? ) +/// Wait if necessary and then project the result value of an async let +@available(SwiftStdlib 5.5, *) +@_silgen_name("swift_asyncLet_get") +public func _asyncLet_get(_ asyncLet: Builtin.RawPointer, _ resultBuffer: Builtin.RawPointer) async -> Builtin.RawPointer + +/// Wait if necessary and then project the result value of an async let that throws +@available(SwiftStdlib 5.5, *) +@_silgen_name("swift_asyncLet_get_throwing") +public func _asyncLet_get_throwing(_ asyncLet: Builtin.RawPointer, _ resultBuffer: Builtin.RawPointer) async throws -> Builtin.RawPointer + +/// Wait if necessary and then tear down the async let task +@available(SwiftStdlib 5.5, *) +@_silgen_name("swift_asyncLet_finish") +public func _asyncLet_finish(_ asyncLet: Builtin.RawPointer, _ resultBuffer: Builtin.RawPointer) async @_silgen_name("swift_asyncLet_extractTask") func _asyncLetExtractTask( diff --git a/stdlib/public/Concurrency/AsyncMapSequence.swift b/stdlib/public/Concurrency/AsyncMapSequence.swift index 08d1c921eff0b..1d43e29f694bf 100644 --- a/stdlib/public/Concurrency/AsyncMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncMapSequence.swift @@ -14,6 +14,35 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Creates an asynchronous sequence that maps the given closure over the + /// asynchronous sequence’s elements. + /// + /// Use the `map(_:)` method to transform every element received from a base + /// asynchronous sequence. Typically, you use this to transform from one type + /// of element to another. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `5`. The closure provided to the `map(_:)` method + /// takes each `Int` and looks up a corresponding `String` from a + /// `romanNumeralDict` dictionary. This means the outer `for await in` loop + /// iterates over `String` instances instead of the underlying `Int` values + /// that `Counter` produces: + /// + /// let romanNumeralDict: [Int: String] = + /// [1: "I", 2: "II", 3: "III", 5: "V"] + /// + /// let stream = Counter(howHigh: 5) + /// .map { romanNumeralDict[$0] ?? "(unknown)" } + /// for await numeral in stream { + /// print("\(numeral) ", terminator: " ") + /// } + /// // Prints: I II III (unknown) V + /// + /// - Parameter transform: A mapping closure. `transform` accepts an element + /// of this sequence as its parameter and returns a transformed value of the + /// same or of a different type. + /// - Returns: An asynchronous sequence that contains, in order, the elements + /// produced by the `transform` closure. @inlinable public __consuming func map( _ transform: @escaping (Element) async -> Transformed @@ -22,7 +51,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence that maps the given closure over the asynchronous +/// sequence’s elements. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncMapSequence { @usableFromInline let base: Base @@ -30,7 +62,7 @@ public struct AsyncMapSequence { @usableFromInline let transform: (Base.Element) async -> Transformed - @usableFromInline + @inlinable init( _ base: Base, transform: @escaping (Base.Element) async -> Transformed @@ -42,9 +74,16 @@ public struct AsyncMapSequence { @available(SwiftStdlib 5.5, *) extension AsyncMapSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The map sequence produces whatever type of element its transforming + /// closure produces. public typealias Element = Transformed + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the map sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @@ -52,7 +91,7 @@ extension AsyncMapSequence: AsyncSequence { @usableFromInline let transform: (Base.Element) async -> Transformed - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, transform: @escaping (Base.Element) async -> Transformed @@ -61,6 +100,11 @@ extension AsyncMapSequence: AsyncSequence { self.transform = transform } + /// Produces the next element in the map sequence. + /// + /// This iterator calls `next()` on its base iterator; if this call returns + /// `nil`, `next()` returns `nil`. Otherwise, `next()` returns the result of + /// calling the transforming closure on the received element. @inlinable public mutating func next() async rethrows -> Transformed? { guard let element = try await baseIterator.next() else { diff --git a/stdlib/public/Concurrency/AsyncPrefixSequence.swift b/stdlib/public/Concurrency/AsyncPrefixSequence.swift index 4f3e333f03db8..b6ffa7fa8959d 100644 --- a/stdlib/public/Concurrency/AsyncPrefixSequence.swift +++ b/stdlib/public/Concurrency/AsyncPrefixSequence.swift @@ -14,6 +14,28 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Returns an asynchronous sequence, up to the specified maximum length, + /// containing the initial elements of the base asynchronous sequence. + /// + /// Use `prefix(_:)` to reduce the number of elements produced by the + /// asynchronous sequence. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `prefix(_:)` method causes the modified + /// sequence to pass through the first six values, then end. + /// + /// for await number in Counter(howHigh: 10).prefix(6) { + /// print("\(number) ") + /// } + /// // prints "1 2 3 4 5 6" + /// + /// If the count passed to `prefix(_:)` exceeds the number of elements in the + /// base sequence, the result contains all of the elements in the sequence. + /// + /// - Parameter count: The maximum number of elements to return. The value of + /// `count` must be greater than or equal to zero. + /// - Returns: An asynchronous sequence starting at the beginning of the + /// base sequence with at most `count` elements. @inlinable public __consuming func prefix( _ count: Int @@ -24,7 +46,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence, up to a specified maximum length, +/// containing the initial elements of a base asynchronous sequence. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncPrefixSequence { @usableFromInline let base: Base @@ -32,7 +57,7 @@ public struct AsyncPrefixSequence { @usableFromInline let count: Int - @usableFromInline + @inlinable init(_ base: Base, count: Int) { self.base = base self.count = count @@ -41,9 +66,16 @@ public struct AsyncPrefixSequence { @available(SwiftStdlib 5.5, *) extension AsyncPrefixSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The prefix sequence produces whatever type of element its base iterator + /// produces. public typealias Element = Base.Element + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the prefix sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @@ -51,12 +83,18 @@ extension AsyncPrefixSequence: AsyncSequence { @usableFromInline var remaining: Int - @usableFromInline + @inlinable init(_ baseIterator: Base.AsyncIterator, count: Int) { self.baseIterator = baseIterator self.remaining = count } + /// Produces the next element in the prefix sequence. + /// + /// Until reaching the number of elements to include, this iterator calls + /// `next()` on its base iterator and passes through the result. After + /// reaching the maximum number of elements, subsequent calls to `next()` + /// return `nil`. @inlinable public mutating func next() async rethrows -> Base.Element? { if remaining != 0 { diff --git a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift index 843cdc4374aa7..1af5ad946fab7 100644 --- a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift @@ -14,6 +14,30 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Returns an asynchronous sequence, containing the initial, consecutive + /// elements of the base sequence that satisfy the given predicate. + /// + /// Use `prefix(while:)` to produce values while elements from the base + /// sequence meet a condition you specify. The modified sequence ends when + /// the predicate closure returns `false`. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `prefix(while:)` method causes the modified + /// sequence to pass along values so long as they aren’t divisible by `2` and + /// `3`. Upon reaching `6`, the sequence ends: + /// + /// let stream = Counter(howHigh: 10) + /// .prefix { $0 % 2 != 0 || $0 % 3 != 0 } + /// for try await number in stream { + /// print("\(number) ", terminator: " ") + /// } + /// // prints "1 2 3 4 5" + /// + /// - Parameter predicate: A closure that takes an element as a parameter and + /// returns a Boolean value indicating whether the element should be + /// included in the modified sequence. + /// - Returns: An asynchronous sequence of the initial, consecutive + /// elements that satisfy `predicate`. @inlinable public __consuming func prefix( while predicate: @escaping (Element) async -> Bool @@ -22,7 +46,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence, containing the initial, consecutive +/// elements of the base sequence that satisfy a given predicate. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncPrefixWhileSequence { @usableFromInline let base: Base @@ -30,7 +57,7 @@ public struct AsyncPrefixWhileSequence { @usableFromInline let predicate: (Base.Element) async -> Bool - @usableFromInline + @inlinable init( _ base: Base, predicate: @escaping (Base.Element) async -> Bool @@ -42,9 +69,16 @@ public struct AsyncPrefixWhileSequence { @available(SwiftStdlib 5.5, *) extension AsyncPrefixWhileSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The prefix-while sequence produces whatever type of element its base + /// iterator produces. public typealias Element = Base.Element + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the prefix-while sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var predicateHasFailed = false @@ -55,7 +89,7 @@ extension AsyncPrefixWhileSequence: AsyncSequence { @usableFromInline let predicate: (Base.Element) async -> Bool - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, predicate: @escaping (Base.Element) async -> Bool @@ -64,6 +98,12 @@ extension AsyncPrefixWhileSequence: AsyncSequence { self.predicate = predicate } + /// Produces the next element in the prefix-while sequence. + /// + /// If the predicate hasn't yet failed, this method gets the next element + /// from the base sequence and calls the predicate with it. If this call + /// succeeds, this method passes along the element. Otherwise, it returns + /// `nil`, ending the sequence. @inlinable public mutating func next() async rethrows -> Base.Element? { if !predicateHasFailed, let nextElement = try await baseIterator.next() { diff --git a/stdlib/public/Concurrency/AsyncSequence.swift b/stdlib/public/Concurrency/AsyncSequence.swift index d28c7a4c6f241..3b52aae57ca6e 100644 --- a/stdlib/public/Concurrency/AsyncSequence.swift +++ b/stdlib/public/Concurrency/AsyncSequence.swift @@ -12,16 +12,115 @@ import Swift +/// A type that provides asynchronous, sequential, iterated access to its +/// elements. +/// +/// An `AsyncSequence` resembles the `Sequence` type --- offering a list of +/// values you can step through one at a time --- and adds asynchronicity. An +/// `AsyncSequence` may have all, some, or none of its values available when +/// you first use it. Instead, you use `await` to receive values as they become +/// available. +/// +/// As with `Sequence`, you typically iterate through an `AsyncSequence` with a +/// `for await`-`in` loop. However, because the caller must potentially wait for values, +/// you use the `await` keyword. The following example shows how to iterate +/// over `Counter`, a custom `AsyncSequence` that produces `Int` values from +/// `1` up to a `howHigh` value: +/// +/// for await i in Counter(howHigh: 10) { +/// print(i, terminator: " ") +/// } +/// // Prints: 1 2 3 4 5 6 7 8 9 10 +/// +/// An `AsyncSequence` doesn't generate or contain the values; it just defines +/// how you access them. Along with defining the type of values as an associated +/// type called `Element`, the `AsyncSequence` defines a `makeAsyncIterator()` +/// method. This returns an instance of type `AsyncIterator`. Like the standard +/// `IteratorProtocol`, the `AsyncIteratorProtocol` defines a single `next()` +/// method to produce elements. The difference is that the `AsyncIterator` +/// defines its `next()` method as `async`, which requires a caller to wait for +/// the next value with the `await` keyword. +/// +/// `AsyncSequence` also defines methods for processing the elements you +/// receive, modeled on the operations provided by the basic `Sequence` in the +/// standard library. There are two categories of methods: those that return a +/// single value, and those that return another `AsyncSequence`. +/// +/// Single-value methods eliminate the need for a `for await`-`in` loop, and instead +/// let you make a single `await` call. For example, the `contains(_:)` method +/// returns a Boolean value that indicates if a given value exists in the +/// `AsyncSequence`. Given the `Counter` sequence from the previous example, +/// you can test for the existence of a sequence member with a one-line call: +/// +/// let found = await Counter(howHigh: 10).contains(5) // true +/// +/// Methods that return another `AsyncSequence` return a type specific to the +/// method's semantics. For example, the `.map(_:)` method returns a +/// `AsyncMapSequence` (or a `AsyncThrowingMapSequence`, if the closure you +/// provide to the `map(_:)` method can throw an error). These returned +/// sequences don't eagerly await the next member of the sequence, which allows +/// the caller to decide when to start work. Typically, you'll iterate over +/// these sequences with `for await`-`in`, like the base `AsyncSequence` you started +/// with. In the following example, the `map(_:)` method transforms each `Int` +/// received from a `Counter` sequence into a `String`: +/// +/// let stream = Counter(howHigh: 10) +/// .map { $0 % 2 == 0 ? "Even" : "Odd" } +/// for await s in stream { +/// print(s, terminator: " ") +/// } +/// // Prints: Odd Even Odd Even Odd Even Odd Even Odd Even +/// @available(SwiftStdlib 5.5, *) @rethrows public protocol AsyncSequence { + /// The type of asynchronous iterator that produces elements of this + /// asynchronous sequence. associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element == Element + /// The type of element produced by this asynchronous sequence. associatedtype Element + /// Creates the asynchronous iterator that produces elements of this + /// asynchronous sequence. + /// + /// - Returns: An instance of the `AsyncIterator` type used to produce + /// elements of the asynchronous sequence. __consuming func makeAsyncIterator() -> AsyncIterator } @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Returns the result of combining the elements of the asynchronous sequence + /// using the given closure. + /// + /// Use the `reduce(_:_:)` method to produce a single value from the elements of + /// an entire sequence. For example, you can use this method on an sequence of + /// numbers to find their sum or product. + /// + /// The `nextPartialResult` closure executes sequentially with an accumulating + /// value initialized to `initialResult` and each element of the sequence. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `4`. The `reduce(_:_:)` method sums the values + /// received from the asynchronous sequence. + /// + /// let sum = await Counter(howHigh: 4) + /// .reduce(0) { + /// $0 + $1 + /// } + /// print(sum) + /// // Prints: 10 + /// + /// + /// - Parameters: + /// - initialResult: The value to use as the initial accumulating value. + /// The `nextPartialResult` closure receives `initialResult` the first + /// time the closure runs. + /// - nextPartialResult: A closure that combines an accumulating value and + /// an element of the asynchronous sequence into a new accumulating value, + /// for use in the next call of the `nextPartialResult` closure or + /// returned to the caller. + /// - Returns: The final accumulated value. If the sequence has no elements, + /// the result is `initialResult`. @inlinable public func reduce( _ initialResult: Result, @@ -36,6 +135,29 @@ extension AsyncSequence { return accumulator } + /// Returns the result of combining the elements of the asynchronous sequence + /// using the given closure, given a mutable initial value. + /// + /// Use the `reduce(into:_:)` method to produce a single value from the + /// elements of an entire sequence. For example, you can use this method on a + /// sequence of numbers to find their sum or product. + /// + /// The `nextPartialResult` closure executes sequentially with an accumulating + /// value initialized to `initialResult` and each element of the sequence. + /// + /// Prefer this method over `reduce(_:_:)` for efficiency when the result is + /// a copy-on-write type, for example an `Array` or `Dictionary`. + /// + /// - Parameters: + /// - initialResult: The value to use as the initial accumulating value. + /// The `nextPartialResult` closure receives `initialResult` the first + /// time the closure executes. + /// - nextPartialResult: A closure that combines an accumulating value and + /// an element of the asynchronous sequence into a new accumulating value, + /// for use in the next call of the `nextPartialResult` closure or + /// returned to the caller. + /// - Returns: The final accumulated value. If the sequence has no elements, + /// the result is `initialResult`. @inlinable public func reduce( into initialResult: __owned Result, @@ -68,13 +190,57 @@ func _contains( @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Returns a Boolean value that indicates whether the asynchronous sequence + /// contains an element that satisfies the given predicate. + /// + /// You can use the predicate to check for an element of a type that doesn’t + /// conform to the `Equatable` protocol, or to find an element that satisfies + /// a general condition. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `contains(where:)` method checks to see + /// whether the sequence produces a value divisible by `3`: + /// + /// let containsDivisibleByThree = await Counter(howHigh: 10) + /// .contains { $0 % 3 == 0 } + /// print(containsDivisibleByThree) + /// // Prints: true + /// + /// The predicate executes each time the asynchronous sequence produces an + /// element, until either the predicate finds a match or the sequence ends. + /// + /// - Parameter predicate: A closure that takes an element of the asynchronous + /// sequence as its argument and returns a Boolean value that indicates + /// whether the passed element represents a match. + /// - Returns: `true` if the sequence contains an element that satisfies + /// predicate; otherwise, `false`. @inlinable public func contains( where predicate: (Element) async throws -> Bool ) async rethrows -> Bool { return try await _contains(self, where: predicate) } - + + /// Returns a Boolean value that indicates whether all elements produced by the + /// asynchronous sequence satisfies the given predicate. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `allSatisfy(_:)` method checks to see whether + /// all elements produced by the sequence are less than `10`. + /// + /// let allLessThanTen = await Counter(howHigh: 10) + /// .allSatisfy { $0 < 10 } + /// print(allLessThanTen) + /// // Prints: false + /// + /// The predicate executes each time the asynchronous sequence produces an + /// element, until either the predicate returns `false` or the sequence ends. + /// + /// - Parameter predicate: A closure that takes an element of the asynchronous + /// sequence as its argument and returns a Boolean value that indicates + /// whether the passed element satisfies a condition. + /// - Returns: `true` if the sequence contains only elements that satisfy + /// `predicate`; otherwise, `false`. @inlinable public func allSatisfy( _ predicate: (Element) async throws -> Bool @@ -85,6 +251,21 @@ extension AsyncSequence { @available(SwiftStdlib 5.5, *) extension AsyncSequence where Element: Equatable { + /// Returns a Boolean value that indicates whether the asynchronous sequence + /// contains the given element. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `contains(_:)` method checks to see whether + /// the sequence produces the value `5`: + /// + /// let containsFive = await Counter(howHigh: 10) + /// .contains(5) + /// print(containsFive) + /// // Prints: true + /// + /// - Parameter search: The element to find in the asynchronous sequence. + /// - Returns: `true` if the method found the element in the asynchronous + /// sequence; otherwise, `false`. @inlinable public func contains(_ search: Element) async rethrows -> Bool { for try await element in self { @@ -113,6 +294,26 @@ func _first( @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Returns the first element of the sequence that satisfies the given + /// predicate. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `first(where:)` method returns the first + /// member of the sequence that's evenly divisible by both `2` and `3`. + /// + /// let divisibleBy2And3 = await Counter(howHigh: 10) + /// .first { $0 % 2 == 0 && $0 % 3 == 0 } + /// print(divisibleBy2And3 ?? "none") + /// // Prints: 6 + /// + /// The predicate executes each time the asynchronous sequence produces an + /// element, until either the predicate finds a match or the sequence ends. + /// + /// - Parameter predicate: A closure that takes an element of the asynchronous + /// sequence as its argument and returns a Boolean value that indicates + /// whether the element is a match. + /// - Returns: The first element of the sequence that satisfies `predicate`, + /// or `nil` if there is no element that satisfies `predicate`. @inlinable public func first( where predicate: (Element) async throws -> Bool @@ -123,6 +324,44 @@ extension AsyncSequence { @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Returns the minimum element in the asynchronous sequence, using the given + /// predicate as the comparison between elements. + /// + /// Use this method when the asynchronous sequence's values don't conform + /// to `Comparable`, or when you want to apply a custom ordering to the + /// sequence. + /// + /// The predicate must be a *strict weak ordering* over the elements. That is, + /// for any elements `a`, `b`, and `c`, the following conditions must hold: + /// + /// - `areInIncreasingOrder(a, a)` is always `false`. (Irreflexivity) + /// - If `areInIncreasingOrder(a, b)` and `areInIncreasingOrder(b, c)` are + /// both `true`, then `areInIncreasingOrder(a, c)` is also + /// `true`. (Transitive comparability) + /// - Two elements are *incomparable* if neither is ordered before the other + /// according to the predicate. If `a` and `b` are incomparable, and `b` + /// and `c` are incomparable, then `a` and `c` are also incomparable. + /// (Transitive incomparability) + /// + /// The following example uses an enumeration of playing cards ranks, `Rank`, + /// which ranges from `ace` (low) to `king` (high). An asynchronous sequence + /// called `RankCounter` produces all elements of the array. The predicate + /// provided to the `min(by:)` method sorts ranks based on their `rawValue`: + /// + /// enum Rank: Int { + /// case ace = 1, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king + /// } + /// + /// let min = await RankCounter() + /// .min { $0.rawValue < $1.rawValue } + /// print(min ?? "none") + /// // Prints: ace + /// + /// - Parameter areInIncreasingOrder: A predicate that returns `true` if its + /// first argument should be ordered before its second argument; otherwise, + /// `false`. + /// - Returns: The sequence’s minimum element, according to + /// `areInIncreasingOrder`. If the sequence has no elements, returns `nil`. @inlinable @warn_unqualified_access public func min( @@ -140,6 +379,44 @@ extension AsyncSequence { return result } + /// Returns the maximum element in the asynchronous sequence, using the given + /// predicate as the comparison between elements. + /// + /// Use this method when the asynchronous sequence's values don't conform + /// to `Comparable`, or when you want to apply a custom ordering to the + /// sequence. + /// + /// The predicate must be a *strict weak ordering* over the elements. That is, + /// for any elements `a`, `b`, and `c`, the following conditions must hold: + /// + /// - `areInIncreasingOrder(a, a)` is always `false`. (Irreflexivity) + /// - If `areInIncreasingOrder(a, b)` and `areInIncreasingOrder(b, c)` are + /// both `true`, then `areInIncreasingOrder(a, c)` is also + /// `true`. (Transitive comparability) + /// - Two elements are *incomparable* if neither is ordered before the other + /// according to the predicate. If `a` and `b` are incomparable, and `b` + /// and `c` are incomparable, then `a` and `c` are also incomparable. + /// (Transitive incomparability) + /// + /// The following example uses an enumeration of playing cards ranks, `Rank`, + /// which ranges from `ace` (low) to `king` (high). An asynchronous sequence + /// called `RankCounter` produces all elements of the array. The predicate + /// provided to the `max(by:)` method sorts ranks based on their `rawValue`: + /// + /// enum Rank: Int { + /// case ace = 1, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king + /// } + /// + /// let max = await RankCounter() + /// .max { $0.rawValue < $1.rawValue } + /// print(max ?? "none") + /// // Prints: king + /// + /// - Parameter areInIncreasingOrder: A predicate that returns `true` if its + /// first argument should be ordered before its second argument; otherwise, + /// `false`. + /// - Returns: The sequence’s minimum element, according to + /// `areInIncreasingOrder`. If the sequence has no elements, returns `nil`. @inlinable @warn_unqualified_access public func max( @@ -160,12 +437,40 @@ extension AsyncSequence { @available(SwiftStdlib 5.5, *) extension AsyncSequence where Element: Comparable { + /// Returns the minimum element in an asynchronous sequence of comparable + /// elements. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `min()` method returns the minimum value + /// of the sequence. + /// + /// let min = await Counter(howHigh: 10) + /// .min() + /// print(min ?? "none") + /// // Prints: 1 + /// + /// - Returns: The sequence’s minimum element. If the sequence has no + /// elements, returns `nil`. @inlinable @warn_unqualified_access public func min() async rethrows -> Element? { return try await self.min(by: <) } + /// Returns the maximum element in an asynchronous sequence of comparable + /// elements. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `max()` method returns the max value + /// of the sequence. + /// + /// let max = await Counter(howHigh: 10) + /// .max() + /// print(max ?? "none") + /// // Prints: 10 + /// + /// - Returns: The sequence’s maximum element. If the sequence has no + /// elements, returns `nil`. @inlinable @warn_unqualified_access public func max() async rethrows -> Element? { diff --git a/stdlib/public/Concurrency/AsyncStream.cpp b/stdlib/public/Concurrency/AsyncStream.cpp new file mode 100644 index 0000000000000..a58e936d0c24b --- /dev/null +++ b/stdlib/public/Concurrency/AsyncStream.cpp @@ -0,0 +1,39 @@ +//===--- AsyncStream.cpp - Multi-resume locking interface -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Mutex.h" + +namespace swift { +// return the size in words for the given mutex primitive +extern "C" +size_t _swift_async_stream_lock_size() { + size_t words = sizeof(MutexHandle) / sizeof(void *); + if (words < 1) { return 1; } + return words; +} + +extern "C" +void _swift_async_stream_lock_init(MutexHandle &lock) { + MutexPlatformHelper::init(lock); +} + +extern "C" +void _swift_async_stream_lock_lock(MutexHandle &lock) { + MutexPlatformHelper::lock(lock); +} + +extern "C" +void _swift_async_stream_lock_unlock(MutexHandle &lock) { + MutexPlatformHelper::unlock(lock); +} + +}; diff --git a/stdlib/public/Concurrency/AsyncStream.swift b/stdlib/public/Concurrency/AsyncStream.swift new file mode 100644 index 0000000000000..dcdb21aec93c0 --- /dev/null +++ b/stdlib/public/Concurrency/AsyncStream.swift @@ -0,0 +1,254 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020-2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +/// An ordered, asynchronously generated sequence of elements. +/// +/// AsyncStream is an interface type to adapt from code producing values to an +/// asynchronous context iterating them. This is intended to be used to allow +/// callback or delegation based APIs to participate with async/await. +/// +/// When values are produced from a non async/await source there is a +/// consideration that must be made on behavioral characteristics of how that +/// production of values interacts with the iteration. AsyncStream offers a +/// initialization strategy that provides a method of yielding values into +/// iteration. +/// +/// AsyncStream can be initialized with the option to buffer to a given limit. +/// The default value for this limit is Int.max. The buffering is only for +/// values that have yet to be consumed by iteration. Values can be yielded in +/// case to the continuation passed into the build closure. That continuation +/// is Sendable, in that it is intended to be used from concurrent contexts +/// external to the iteration of the AsyncStream. +/// +/// A trivial use case producing values from a detached task would work as such: +/// +/// let digits = AsyncStream(Int.self) { continuation in +/// detach { +/// for digit in 0..<10 { +/// continuation.yield(digit) +/// } +/// continuation.finish() +/// } +/// } +/// +/// for await digit in digits { +/// print(digit) +/// } +/// +@available(SwiftStdlib 5.5, *) +public struct AsyncStream { + public struct Continuation: Sendable { + /// Indication of the type of termination informed to + /// `onTermination`. + public enum Termination { + + /// The stream was finished via the `finish` method + case finished + + /// The stream was cancelled + case cancelled + } + + /// A result of yielding values. + public enum YieldResult { + + /// When a value is successfully enqueued, either buffered + /// or immediately consumed to resume a pending call to next + /// and a count of remaining slots available in the buffer at + /// the point in time of yielding. Note: transacting upon the + /// remaining count is only valid when then calls to yield are + /// mutually exclusive. + case enqueued(remaining: Int) + + /// Yielding resulted in not buffering an element because the + /// buffer was full. The element is the dropped value. + case dropped(Element) + + /// Indication that the continuation was yielded when the + /// stream was already in a terminal state: either by cancel or + /// by finishing. + case terminated + } + + /// A strategy that handles exhaustion of a buffer’s capacity. + public enum BufferingPolicy { + case unbounded + + /// When the buffer is full, discard the newly received element. + /// This enforces keeping the specified amount of oldest values. + case bufferingOldest(Int) + + /// When the buffer is full, discard the oldest element in the buffer. + /// This enforces keeping the specified amount of newest values. + case bufferingNewest(Int) + } + + let storage: _Storage + + /// Resume the task awaiting the next iteration point by having it return + /// normally from its suspension point or buffer the value if no awaiting + /// next iteration is active. + /// + /// - Parameter value: The value to yield from the continuation. + /// + /// This can be called more than once and returns to the caller immediately + /// without blocking for any awaiting consumption from the iteration. + @discardableResult + public func yield(_ value: __owned Element) -> YieldResult { + storage.yield(value) + } + + /// Resume the task awaiting the next iteration point by having it return + /// nil which signifies the end of the iteration. + /// + /// Calling this function more than once is idempotent; i.e. finishing more + /// than once does not alter the state beyond the requirements of + /// AsyncSequence; which claims that all values past a terminal state are + /// nil. + public func finish() { + storage.finish() + } + + /// A callback to invoke when iteration of a AsyncStream is cancelled. + /// + /// If an `onTermination` callback is set, when iteration of a AsyncStream is + /// cancelled via task cancellation that callback is invoked. The callback + /// is disposed of after any terminal state is reached. + /// + /// Cancelling an active iteration will first invoke the onTermination callback + /// and then resume yielding nil. This means that any cleanup state can be + /// emitted accordingly in the cancellation handler + public var onTermination: (@Sendable (Termination) -> Void)? { + get { + return storage.getOnTermination() + } + nonmutating set { + storage.setOnTermination(newValue) + } + } + } + + let produce: () async -> Element? + + /// Construct a AsyncStream buffering given an Element type. + /// + /// - Parameter elementType: The type the AsyncStream will produce. + /// - Parameter maxBufferedElements: The maximum number of elements to + /// hold in the buffer past any checks for continuations being resumed. + /// - Parameter build: The work associated with yielding values to the + /// AsyncStream. + /// + /// The maximum number of pending elements limited by dropping the oldest + /// value when a new value comes in if the buffer would exceed the limit + /// placed upon it. By default this limit is unlimited. + /// + /// The build closure passes in a Continuation which can be used in + /// concurrent contexts. It is thread safe to send and finish; all calls are + /// to the continuation are serialized, however calling this from multiple + /// concurrent contexts could result in out of order delivery. + public init( + _ elementType: Element.Type = Element.self, + bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded, + _ build: (Continuation) -> Void + ) { + let storage: _Storage = .create(limit: limit) + self.init(unfolding: storage.next) + build(Continuation(storage: storage)) + } + + + public init( + unfolding produce: @escaping () async -> Element?, + onCancel: (@Sendable () -> Void)? = nil + ) { + let storage: _AsyncStreamCriticalStorage Element?>> + = .create(produce) + self.produce = { + return await withTaskCancellationHandler { + guard let result = await storage.value?() else { + storage.value = nil + return nil + } + return result + } onCancel: { + storage.value = nil + onCancel?() + } + } + } +} + +@available(SwiftStdlib 5.5, *) +extension AsyncStream: AsyncSequence { + /// The asynchronous iterator for iterating a AsyncStream. + /// + /// This type is specifically not Sendable. It is not intended to be used + /// from multiple concurrent contexts. Any such case that next is invoked + /// concurrently and contends with another call to next is a programmer error + /// and will fatalError. + public struct Iterator: AsyncIteratorProtocol { + let produce: () async -> Element? + + /// The next value from the AsyncStream. + /// + /// When next returns nil this signifies the end of the AsyncStream. Any + /// such case that next is invoked concurrently and contends with another + /// call to next is a programmer error and will fatalError. + /// + /// If the task this iterator is running in is canceled while next is + /// awaiting a value, this will terminate the AsyncStream and next may return nil + /// immediately (or will return nil on subsequent calls) + public mutating func next() async -> Element? { + await produce() + } + } + + /// Construct an iterator. + public func makeAsyncIterator() -> Iterator { + return Iterator(produce: produce) + } +} + +@available(SwiftStdlib 5.5, *) +extension AsyncStream.Continuation { + /// Resume the task awaiting the next iteration point by having it return + /// normally from its suspension point or buffer the value if no awaiting + /// next iteration is active. + /// + /// - Parameter result: A result to yield from the continuation. + /// + /// This can be called more than once and returns to the caller immediately + /// without blocking for any awaiting consumption from the iteration. + @discardableResult + public func yield( + with result: Result + ) -> YieldResult { + switch result { + case .success(let val): + return storage.yield(val) + } + } + + /// Resume the task awaiting the next iteration point by having it return + /// normally from its suspension point or buffer the value if no awaiting + /// next iteration is active where the `Element` is `Void`. + /// + /// This can be called more than once and returns to the caller immediately + /// without blocking for any awaiting consumption from the iteration. + /// without blocking for any awaiting consuption from the iteration. + @discardableResult + public func yield() -> YieldResult where Element == Void { + return storage.yield(()) + } +} diff --git a/stdlib/public/Concurrency/AsyncStreamBuffer.swift b/stdlib/public/Concurrency/AsyncStreamBuffer.swift new file mode 100644 index 0000000000000..3e8f4233b470d --- /dev/null +++ b/stdlib/public/Concurrency/AsyncStreamBuffer.swift @@ -0,0 +1,597 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020-2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +#if ASYNC_STREAM_STANDALONE +@_exported import _Concurrency +import Darwin + +func _lockWordCount() -> Int { + let sz = + MemoryLayout.size / MemoryLayout.size + return max(sz, 1) +} + +func _lockInit(_ ptr: UnsafeRawPointer) { + UnsafeMutableRawPointer(mutating: ptr) + .assumingMemoryBound(to: os_unfair_lock.self) + .initialize(to: os_unfair_lock()) +} + +func _lock(_ ptr: UnsafeRawPointer) { + os_unfair_lock_lock(UnsafeMutableRawPointer(mutating: ptr) + .assumingMemoryBound(to: os_unfair_lock.self)) +} + +func _unlock(_ ptr: UnsafeRawPointer) { + os_unfair_lock_unlock(UnsafeMutableRawPointer(mutating: ptr) + .assumingMemoryBound(to: os_unfair_lock.self)) +} +#else +@_silgen_name("_swift_async_stream_lock_size") +func _lockWordCount() -> Int + +@_silgen_name("_swift_async_stream_lock_init") +func _lockInit(_ ptr: UnsafeRawPointer) + +@_silgen_name("_swift_async_stream_lock_lock") +func _lock(_ ptr: UnsafeRawPointer) + +@_silgen_name("_swift_async_stream_lock_unlock") +func _unlock(_ ptr: UnsafeRawPointer) +#endif + +@available(SwiftStdlib 5.5, *) +extension AsyncStream { + internal final class _Storage: UnsafeSendable { + typealias TerminationHandler = @Sendable (Continuation.Termination) -> Void + + struct State { + var continuation: UnsafeContinuation? + var pending = _Deque() + let limit: Continuation.BufferingPolicy + var onTermination: TerminationHandler? + var terminal: Bool = false + + init(limit: Continuation.BufferingPolicy) { + self.limit = limit + } + } + // Stored as a singular structured assignment for initialization + var state: State + + private init(_doNotCallMe: ()) { + fatalError("Storage must be initialized by create") + } + + deinit { + state.onTermination?(.cancelled) + } + + private func lock() { + let ptr = + UnsafeRawPointer(Builtin.projectTailElems(self, UnsafeRawPointer.self)) + _lock(ptr) + } + + private func unlock() { + let ptr = + UnsafeRawPointer(Builtin.projectTailElems(self, UnsafeRawPointer.self)) + _unlock(ptr) + } + + func getOnTermination() -> TerminationHandler? { + lock() + let handler = state.onTermination + unlock() + return handler + } + + func setOnTermination(_ newValue: TerminationHandler?) { + lock() + withExtendedLifetime(state.onTermination) { + state.onTermination = newValue + unlock() + } + } + + func cancel() { + lock() + // swap out the handler before we invoke it to prevent double cancel + let handler = state.onTermination + state.onTermination = nil + unlock() + + // handler must be invoked before yielding nil for termination + handler?(.cancelled) + + finish() + } + + func yield(_ value: __owned Element) -> Continuation.YieldResult { + var result: Continuation.YieldResult + lock() + let limit = state.limit + let count = state.pending.count + if let continuation = state.continuation { + if count > 0 { + if !state.terminal { + switch limit { + case .unbounded: + state.pending.append(value) + result = .enqueued(remaining: .max) + case .bufferingOldest(let limit): + if count < limit { + state.pending.append(value) + result = .enqueued(remaining: limit - (count + 1)) + } else { + result = .dropped(value) + } + case .bufferingNewest(let limit): + if count < limit { + state.pending.append(value) + result = .enqueued(remaining: limit - (count + 1)) + } else if count > 0 { + result = .dropped(state.pending.removeFirst()) + state.pending.append(value) + } else { + result = .dropped(value) + } + } + } else { + result = .terminated + } + state.continuation = nil + let toSend = state.pending.removeFirst() + unlock() + continuation.resume(returning: toSend) + } else if state.terminal { + state.continuation = nil + result = .terminated + unlock() + continuation.resume(returning: nil) + } else { + state.continuation = nil + switch limit { + case .unbounded: + result = .enqueued(remaining: .max) + case .bufferingNewest(let limit): + result = .enqueued(remaining: limit) + case .bufferingOldest(let limit): + result = .enqueued(remaining: limit) + } + + unlock() + continuation.resume(returning: value) + } + } else { + if !state.terminal { + switch limit { + case .unbounded: + result = .enqueued(remaining: .max) + state.pending.append(value) + case .bufferingOldest(let limit): + if count < limit { + result = .enqueued(remaining: limit - (count + 1)) + state.pending.append(value) + } else { + result = .dropped(value) + } + case .bufferingNewest(let limit): + if count < limit { + state.pending.append(value) + result = .enqueued(remaining: limit - (count + 1)) + } else if count > 0 { + result = .dropped(state.pending.removeFirst()) + state.pending.append(value) + } else { + result = .dropped(value) + } + } + } else { + result = .terminated + } + unlock() + } + return result + } + + func finish() { + lock() + let handler = state.onTermination + state.onTermination = nil + state.terminal = true + + if let continuation = state.continuation { + if state.pending.count > 0 { + state.continuation = nil + let toSend = state.pending.removeFirst() + unlock() + handler?(.finished) + continuation.resume(returning: toSend) + } else if state.terminal { + state.continuation = nil + unlock() + handler?(.finished) + continuation.resume(returning: nil) + } else { + unlock() + handler?(.finished) + } + } else { + unlock() + handler?(.finished) + } + } + + func next(_ continuation: UnsafeContinuation) { + lock() + if state.continuation == nil { + if state.pending.count > 0 { + let toSend = state.pending.removeFirst() + unlock() + continuation.resume(returning: toSend) + } else if state.terminal { + unlock() + continuation.resume(returning: nil) + } else { + state.continuation = continuation + unlock() + } + } else { + unlock() + fatalError("attempt to await next() on more than one task") + } + } + + func next() async -> Element? { + await withTaskCancellationHandler { [cancel] in + cancel() + } operation: { + await withUnsafeContinuation { + next($0) + } + } + } + + static func create(limit: Continuation.BufferingPolicy) -> _Storage { + let minimumCapacity = _lockWordCount() + let storage = Builtin.allocWithTailElems_1( + _Storage.self, + minimumCapacity._builtinWordValue, + UnsafeRawPointer.self + ) + + let state = + UnsafeMutablePointer(Builtin.addressof(&storage.state)) + state.initialize(to: State(limit: limit)) + let ptr = UnsafeRawPointer( + Builtin.projectTailElems(storage, UnsafeRawPointer.self)) + _lockInit(ptr) + return storage + } + } +} + +@available(SwiftStdlib 5.5, *) +extension AsyncThrowingStream { + internal final class _Storage: UnsafeSendable { + typealias TerminationHandler = @Sendable (Continuation.Termination) -> Void + enum Terminal { + case finished + case failed(Failure) + } + + struct State { + var continuation: UnsafeContinuation? + var pending = _Deque() + let limit: Continuation.BufferingPolicy + var onTermination: TerminationHandler? + var terminal: Terminal? + + init(limit: Continuation.BufferingPolicy) { + self.limit = limit + } + } + // Stored as a singular structured assignment for initialization + var state: State + + private init(_doNotCallMe: ()) { + fatalError("Storage must be initialized by create") + } + + deinit { + state.onTermination?(.cancelled) + } + + private func lock() { + let ptr = + UnsafeRawPointer(Builtin.projectTailElems(self, UnsafeRawPointer.self)) + _lock(ptr) + } + + private func unlock() { + let ptr = + UnsafeRawPointer(Builtin.projectTailElems(self, UnsafeRawPointer.self)) + _unlock(ptr) + } + + func getOnTermination() -> TerminationHandler? { + lock() + let handler = state.onTermination + unlock() + return handler + } + + func setOnTermination(_ newValue: TerminationHandler?) { + lock() + withExtendedLifetime(state.onTermination) { + state.onTermination = newValue + unlock() + } + } + + func cancel() { + lock() + // swap out the handler before we invoke it to prevent double cancel + let handler = state.onTermination + state.onTermination = nil + unlock() + + // handler must be invoked before yielding nil for termination + handler?(.cancelled) + + finish() + } + + func yield(_ value: __owned Element) -> Continuation.YieldResult { + var result: Continuation.YieldResult + lock() + let limit = state.limit + let count = state.pending.count + if let continuation = state.continuation { + if count > 0 { + if state.terminal == nil { + switch limit { + case .unbounded: + result = .enqueued(remaining: .max) + state.pending.append(value) + case .bufferingOldest(let limit): + if count < limit { + result = .enqueued(remaining: limit - (count + 1)) + state.pending.append(value) + } else { + result = .dropped(value) + } + case .bufferingNewest(let limit): + if count < limit { + state.pending.append(value) + result = .enqueued(remaining: limit - (count + 1)) + } else if count > 0 { + result = .dropped(state.pending.removeFirst()) + state.pending.append(value) + } else { + result = .dropped(value) + } + } + } else { + result = .terminated + } + state.continuation = nil + let toSend = state.pending.removeFirst() + unlock() + continuation.resume(returning: toSend) + } else if let terminal = state.terminal { + result = .terminated + state.continuation = nil + state.terminal = .finished + unlock() + switch terminal { + case .finished: + continuation.resume(returning: nil) + case .failed(let error): + continuation.resume(throwing: error) + } + } else { + switch limit { + case .unbounded: + result = .enqueued(remaining: .max) + case .bufferingOldest(let limit): + result = .enqueued(remaining: limit) + case .bufferingNewest(let limit): + result = .enqueued(remaining: limit) + } + + state.continuation = nil + unlock() + continuation.resume(returning: value) + } + } else { + if state.terminal == nil { + switch limit { + case .unbounded: + result = .enqueued(remaining: .max) + state.pending.append(value) + case .bufferingOldest(let limit): + if count < limit { + result = .enqueued(remaining: limit - (count + 1)) + state.pending.append(value) + } else { + result = .dropped(value) + } + case .bufferingNewest(let limit): + if count < limit { + state.pending.append(value) + result = .enqueued(remaining: limit - (count + 1)) + } else if count > 0 { + result = .dropped(state.pending.removeFirst()) + state.pending.append(value) + } else { + result = .dropped(value) + } + } + } else { + result = .terminated + } + unlock() + } + return result + } + + func finish(throwing error: __owned Failure? = nil) { + lock() + let handler = state.onTermination + state.onTermination = nil + if state.terminal == nil { + if let failure = error { + state.terminal = .failed(failure) + } else { + state.terminal = .finished + } + } + + if let continuation = state.continuation { + if state.pending.count > 0 { + state.continuation = nil + let toSend = state.pending.removeFirst() + unlock() + handler?(.finished(error)) + continuation.resume(returning: toSend) + } else if let terminal = state.terminal { + state.continuation = nil + unlock() + handler?(.finished(error)) + switch terminal { + case .finished: + continuation.resume(returning: nil) + case .failed(let error): + continuation.resume(throwing: error) + } + } else { + unlock() + handler?(.finished(error)) + } + } else { + unlock() + handler?(.finished(error)) + } + } + + func next(_ continuation: UnsafeContinuation) { + lock() + if state.continuation == nil { + if state.pending.count > 0 { + let toSend = state.pending.removeFirst() + unlock() + continuation.resume(returning: toSend) + } else if let terminal = state.terminal { + state.terminal = .finished + unlock() + switch terminal { + case .finished: + continuation.resume(returning: nil) + case .failed(let error): + continuation.resume(throwing: error) + } + } else { + state.continuation = continuation + unlock() + } + } else { + unlock() + fatalError("attempt to await next() on more than one task") + } + } + + func next() async throws -> Element? { + try await withTaskCancellationHandler { [cancel] in + cancel() + } operation: { + try await withUnsafeThrowingContinuation { + next($0) + } + } + } + + static func create(limit: Continuation.BufferingPolicy) -> _Storage { + let minimumCapacity = _lockWordCount() + let storage = Builtin.allocWithTailElems_1( + _Storage.self, + minimumCapacity._builtinWordValue, + UnsafeRawPointer.self + ) + + let state = + UnsafeMutablePointer(Builtin.addressof(&storage.state)) + state.initialize(to: State(limit: limit)) + let ptr = UnsafeRawPointer( + Builtin.projectTailElems(storage, UnsafeRawPointer.self)) + _lockInit(ptr) + return storage + } + } +} + +// this is used to store closures; which are two words +final class _AsyncStreamCriticalStorage: UnsafeSendable { + var _value: Contents + private init(_doNotCallMe: ()) { + fatalError("_AsyncStreamCriticalStorage must be initialized by create") + } + + private func lock() { + let ptr = + UnsafeRawPointer(Builtin.projectTailElems(self, UnsafeRawPointer.self)) + _lock(ptr) + } + + private func unlock() { + let ptr = + UnsafeRawPointer(Builtin.projectTailElems(self, UnsafeRawPointer.self)) + _unlock(ptr) + } + + var value: Contents { + get { + lock() + let contents = _value + unlock() + return contents + } + + set { + lock() + withExtendedLifetime(_value) { + _value = newValue + unlock() + } + } + } + + static func create(_ initial: Contents) -> _AsyncStreamCriticalStorage { + let minimumCapacity = _lockWordCount() + let storage = Builtin.allocWithTailElems_1( + _AsyncStreamCriticalStorage.self, + minimumCapacity._builtinWordValue, + UnsafeRawPointer.self + ) + + let state = + UnsafeMutablePointer(Builtin.addressof(&storage._value)) + state.initialize(to: initial) + let ptr = UnsafeRawPointer( + Builtin.projectTailElems(storage, UnsafeRawPointer.self)) + _lockInit(ptr) + return storage + } +} diff --git a/stdlib/public/Concurrency/AsyncThrowingCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncThrowingCompactMapSequence.swift index 6db91dcfa9011..0f0d3769bd83e 100644 --- a/stdlib/public/Concurrency/AsyncThrowingCompactMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncThrowingCompactMapSequence.swift @@ -14,6 +14,49 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Creates an asynchronous sequence that maps an error-throwing closure over + /// the base sequence’s elements, omitting results that don't return a value. + /// + /// Use the `compactMap(_:)` method to transform every element received from + /// a base asynchronous sequence, while also discarding any `nil` results + /// from the closure. Typically, you use this to transform from one type of + /// element to another. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `5`. The closure provided to the `compactMap(_:)` + /// method takes each `Int` and looks up a corresponding `String` from a + /// `romanNumeralDict` dictionary. Since there is no key for `4`, the closure + /// returns `nil` in this case, which `compactMap(_:)` omits from the + /// transformed asynchronous sequence. When the value is `5`, the closure + /// throws `MyError`, terminating the sequence. + /// + /// let romanNumeralDict: [Int : String] = + /// [1: "I", 2: "II", 3: "III", 5: "V"] + /// + /// do { + /// let stream = Counter(howHigh: 5) + /// .compactMap { (value) throws -> String? in + /// if value == 5 { + /// throw MyError() + /// } + /// return romanNumeralDict[value] + /// } + /// for try await numeral in stream { + /// print("\(numeral) ", terminator: " ") + /// } + /// } catch { + /// print("Error: \(error)") + /// } + /// // Prints: I II III Error: MyError() + /// + /// - Parameter transform: An error-throwing mapping closure. `transform` + /// accepts an element of this sequence as its parameter and returns a + /// transformed value of the same or of a different type. If `transform` + /// throws an error, the sequence ends. + /// - Returns: An asynchronous sequence that contains, in order, the + /// non-`nil` elements produced by the `transform` closure. The sequence + /// ends either when the base sequence ends or when `transform` throws an + /// error. @inlinable public __consuming func compactMap( _ transform: @escaping (Element) async throws -> ElementOfResult? @@ -22,7 +65,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence that maps an error-throwing closure over the base +/// sequence’s elements, omitting results that don't return a value. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncThrowingCompactMapSequence { @usableFromInline let base: Base @@ -30,7 +76,7 @@ public struct AsyncThrowingCompactMapSequence ElementOfResult? - @usableFromInline + @inlinable init( _ base: Base, transform: @escaping (Base.Element) async throws -> ElementOfResult? @@ -42,9 +88,16 @@ public struct AsyncThrowingCompactMapSequence ElementOfResult? @@ -66,6 +119,15 @@ extension AsyncThrowingCompactMapSequence: AsyncSequence { self.transform = transform } + /// Produces the next element in the compact map sequence. + /// + /// This iterator calls `next()` on its base iterator; if this call returns + /// `nil`, `next()` returns `nil`. Otherwise, `next()` calls the + /// transforming closure on the received element, returning it if the + /// transform returns a non-`nil` value. If the transform returns `nil`, + /// this method continues to wait for further elements until it gets one + /// that transforms to a non-`nil` value. If calling the closure throws an + /// error, the sequence ends and `next()` rethrows the error. @inlinable public mutating func next() async throws -> ElementOfResult? { while !finished { diff --git a/stdlib/public/Concurrency/AsyncThrowingDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncThrowingDropWhileSequence.swift index c7901f973c09e..c3c01c8fafd75 100644 --- a/stdlib/public/Concurrency/AsyncThrowingDropWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncThrowingDropWhileSequence.swift @@ -14,6 +14,46 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Omits elements from the base sequence until a given error-throwing closure + /// returns false, after which it passes through all remaining elements. + /// + /// Use `drop(while:)` to omit elements from an asynchronous sequence until + /// the element received meets a condition you specify. If the closure you + /// provide throws an error, the sequence produces no elements and throws + /// the error instead. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The predicate passed to the `drop(while:)` + /// method throws an error if it encounters an even number, and otherwise + /// returns `true` while it receives elements less than `5`. Because the + /// predicate throws when it receives `2` from the base sequence, this example + /// throws without ever printing anything. + /// + /// do { + /// let stream = Counter(howHigh: 10) + /// .drop { + /// if $0 % 2 == 0 { + /// throw EvenError() + /// } + /// return $0 < 5 + /// } + /// for try await number in stream { + /// print("\(number) ") + /// } + /// } catch { + /// print ("\(error)") + /// } + /// // Prints: EvenError() + /// + /// After the predicate returns `false`, the sequence never executes it again, + /// and from then on the sequence passes through elements from its underlying + /// sequence. A predicate that throws an error also never executes again. + /// + /// - Parameter predicate: An error-throwing closure that takes an element as + /// a parameter and returns a Boolean value indicating whether to drop the + /// element from the modified sequence. + /// - Returns: An asynchronous sequence that skips over values until the + /// provided closure returns `false` or throws an error. @inlinable public __consuming func drop( while predicate: @escaping (Element) async throws -> Bool @@ -22,7 +62,11 @@ extension AsyncSequence { } } +/// An asynchronous sequence which omits elements from the base sequence until a +/// given error-throwing closure returns false, after which it passes through +/// all remaining elements. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncThrowingDropWhileSequence { @usableFromInline let base: Base @@ -30,7 +74,7 @@ public struct AsyncThrowingDropWhileSequence { @usableFromInline let predicate: (Base.Element) async throws -> Bool - @usableFromInline + @inlinable init( _ base: Base, predicate: @escaping (Base.Element) async throws -> Bool @@ -42,9 +86,16 @@ public struct AsyncThrowingDropWhileSequence { @available(SwiftStdlib 5.5, *) extension AsyncThrowingDropWhileSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The drop-while sequence produces whatever type of element its base + /// sequence produces. public typealias Element = Base.Element + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the drop-while sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @@ -58,7 +109,7 @@ extension AsyncThrowingDropWhileSequence: AsyncSequence { @usableFromInline var doneDropping = false - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, predicate: @escaping (Base.Element) async throws -> Bool @@ -67,6 +118,16 @@ extension AsyncThrowingDropWhileSequence: AsyncSequence { self.predicate = predicate } + /// Produces the next element in the drop-while sequence. + /// + /// This iterator calls `next()` on its base iterator and evaluates the + /// result with the `predicate` closure. As long as the predicate returns + /// `true`, this method returns `nil`. After the predicate returns `false`, + /// for a value received from the base iterator, this method returns that + /// value. After that, the iterator returns values received from its + /// base iterator as-is, and never executes the predicate closure again. + /// If calling the closure throws an error, the sequence ends and `next()` + /// rethrows the error. @inlinable public mutating func next() async throws -> Base.Element? { while !finished && !doneDropping { diff --git a/stdlib/public/Concurrency/AsyncThrowingFilterSequence.swift b/stdlib/public/Concurrency/AsyncThrowingFilterSequence.swift index ecab60b9b82c7..f8221b50fe00b 100644 --- a/stdlib/public/Concurrency/AsyncThrowingFilterSequence.swift +++ b/stdlib/public/Concurrency/AsyncThrowingFilterSequence.swift @@ -14,6 +14,37 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Creates an asynchronous sequence that contains, in order, the elements of + /// the base sequence that satisfy the given error-throwing predicate. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `filter(_:)` method returns `true` for even + /// values and `false` for odd values, thereby filtering out the odd values, + /// but also throws an error for values divisible by 5: + /// + /// do { + /// let stream = Counter(howHigh: 10) + /// .filter { + /// if $0 % 5 == 0 { + /// throw MyError() + /// } + /// return $0 % 2 == 0 + /// } + /// for try await number in stream { + /// print("\(number) ", terminator: " ") + /// } + /// } catch { + /// print("Error: \(error)") + /// } + /// // Prints: 2 4 Error: MyError() + /// + /// - Parameter isIncluded: An error-throwing closure that takes an element + /// of the asynchronous sequence as its argument and returns a Boolean value + /// that indicates whether to include the element in the filtered sequence. + /// - Returns: An asynchronous sequence that contains, in order, the elements + /// of the base sequence that satisfy the given predicate. If the predicate + /// throws an error, the sequence contains only values produced prior to + /// the error. @inlinable public __consuming func filter( _ isIncluded: @escaping (Element) async throws -> Bool @@ -22,7 +53,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence that contains, in order, the elements of +/// the base sequence that satisfy the given error-throwing predicate. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncThrowingFilterSequence { @usableFromInline let base: Base @@ -30,7 +64,7 @@ public struct AsyncThrowingFilterSequence { @usableFromInline let isIncluded: (Element) async throws -> Bool - @usableFromInline + @inlinable init( _ base: Base, isIncluded: @escaping (Base.Element) async throws -> Bool @@ -42,9 +76,16 @@ public struct AsyncThrowingFilterSequence { @available(SwiftStdlib 5.5, *) extension AsyncThrowingFilterSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The filter sequence produces whatever type of element its base + /// sequence produces. public typealias Element = Base.Element + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the filter sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @@ -55,7 +96,7 @@ extension AsyncThrowingFilterSequence: AsyncSequence { @usableFromInline var finished = false - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, isIncluded: @escaping (Base.Element) async throws -> Bool @@ -64,6 +105,14 @@ extension AsyncThrowingFilterSequence: AsyncSequence { self.isIncluded = isIncluded } + /// Produces the next element in the filter sequence. + /// + /// This iterator calls `next()` on its base iterator; if this call returns + /// `nil`, `next()` returns nil. Otherwise, `next()` evaluates the + /// result with the `predicate` closure. If the closure returns `true`, + /// `next()` returns the received element; otherwise it awaits the next + /// element from the base iterator. If calling the closure throws an error, + /// the sequence ends and `next()` rethrows the error. @inlinable public mutating func next() async throws -> Base.Element? { while !finished { diff --git a/stdlib/public/Concurrency/AsyncThrowingFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncThrowingFlatMapSequence.swift index 52a22db994aae..e14183c956876 100644 --- a/stdlib/public/Concurrency/AsyncThrowingFlatMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncThrowingFlatMapSequence.swift @@ -14,6 +14,45 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Creates an asynchronous sequence that concatenates the results of calling + /// the given error-throwing transformation with each element of this + /// sequence. + /// + /// Use this method to receive a single-level asynchronous sequence when your + /// transformation produces an asynchronous sequence for each element. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `5`. The transforming closure takes the received `Int` + /// and returns a new `Counter` that counts that high. For example, when the + /// transform receives `3` from the base sequence, it creates a new `Counter` + /// that produces the values `1`, `2`, and `3`. The `flatMap(_:)` method + /// "flattens" the resulting sequence-of-sequences into a single + /// `AsyncSequence`. However, when the closure receives `4`, it throws an + /// error, terminating the sequence. + /// + /// do { + /// let stream = Counter(howHigh: 5) + /// .flatMap { (value) -> Counter in + /// if value == 4 { + /// throw MyError() + /// } + /// return Counter(howHigh: value) + /// } + /// for try await number in stream { + /// print ("\(number)", terminator: " ") + /// } + /// } catch { + /// print(error) + /// } + /// // Prints: 1 1 2 1 2 3 MyError() + /// + /// - Parameter transform: An error-throwing mapping closure. `transform` + /// accepts an element of this sequence as its parameter and returns an + /// `AsyncSequence`. If `transform` throws an error, the sequence ends. + /// - Returns: A single, flattened asynchronous sequence that contains all + /// elements in all the asychronous sequences produced by `transform`. The + /// sequence ends either when the the last sequence created from the last + /// element from base sequence ends, or when `transform` throws an error. @inlinable public __consuming func flatMap( _ transform: @escaping (Element) async throws -> SegmentOfResult @@ -22,7 +61,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence that concatenates the results of calling a given +/// error-throwing transformation with each element of this sequence. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncThrowingFlatMapSequence { @usableFromInline let base: Base @@ -42,9 +84,16 @@ public struct AsyncThrowingFlatMapSequence SegmentOfResult.Element? { while !finished { diff --git a/stdlib/public/Concurrency/AsyncThrowingMapSequence.swift b/stdlib/public/Concurrency/AsyncThrowingMapSequence.swift index 8a33191076146..31af8eaf6fa02 100644 --- a/stdlib/public/Concurrency/AsyncThrowingMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncThrowingMapSequence.swift @@ -14,6 +14,48 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Creates an asynchronous sequence that maps the given error-throwing + /// closure over the asynchronous sequence’s elements. + /// + /// Use the `map(_:)` method to transform every element received from a base + /// asynchronous sequence. Typically, you use this to transform from one type + /// of element to another. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `5`. The closure provided to the `map(_:)` method + /// takes each `Int` and looks up a corresponding `String` from a + /// `romanNumeralDict` dictionary. This means the outer `for await in` loop + /// iterates over `String` instances instead of the underlying `Int` values + /// that `Counter` produces. Also, the dictionary doesn't provide a key for + /// `4`, and the closure throws an error for any key it can't look up, so + /// receiving this value from `Counter` ends the modified sequence with an + /// error. + /// + /// let romanNumeralDict: [Int : String] = + /// [1: "I", 2: "II", 3: "III", 5: "V"] + /// + /// do { + /// let stream = Counter(howHigh: 5) + /// .map { (value) throws -> String in + /// guard let roman = romanNumeralDict[value] else { + /// throw MyError() + /// } + /// return roman + /// } + /// for try await numeral in stream { + /// print("\(numeral) ", terminator: " ") + /// } + /// } catch { + /// print ("Error: \(error)") + /// } + /// // Prints: I II III Error: MyError() + /// + /// - Parameter transform: A mapping closure. `transform` accepts an element + /// of this sequence as its parameter and returns a transformed value of the + /// same or of a different type. `transform` can also throw an error, which + /// ends the transformed sequence. + /// - Returns: An asynchronous sequence that contains, in order, the elements + /// produced by the `transform` closure. @inlinable public __consuming func map( _ transform: @escaping (Element) async throws -> Transformed @@ -22,7 +64,10 @@ extension AsyncSequence { } } +/// An asynchronous sequence that maps the given error-throwing closure over the +/// asynchronous sequence’s elements. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncThrowingMapSequence { @usableFromInline let base: Base @@ -30,7 +75,7 @@ public struct AsyncThrowingMapSequence { @usableFromInline let transform: (Base.Element) async throws -> Transformed - @usableFromInline + @inlinable init( _ base: Base, transform: @escaping (Base.Element) async throws -> Transformed @@ -42,9 +87,16 @@ public struct AsyncThrowingMapSequence { @available(SwiftStdlib 5.5, *) extension AsyncThrowingMapSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The map sequence produces whatever type of element its the transforming + /// closure produces. public typealias Element = Transformed + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the map sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @@ -55,7 +107,7 @@ extension AsyncThrowingMapSequence: AsyncSequence { @usableFromInline var finished = false - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, transform: @escaping (Base.Element) async throws -> Transformed @@ -64,7 +116,14 @@ extension AsyncThrowingMapSequence: AsyncSequence { self.transform = transform } - @inlinable + /// Produces the next element in the map sequence. + /// + /// This iterator calls `next()` on its base iterator; if this call returns + /// `nil`, `next()` returns nil. Otherwise, `next()` returns the result of + /// calling the transforming closure on the received element. If calling + /// the closure throws an error, the sequence ends and `next()` rethrows + /// the error. + @inlinable public mutating func next() async throws -> Transformed? { guard !finished, let element = try await baseIterator.next() else { return nil diff --git a/stdlib/public/Concurrency/AsyncThrowingPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncThrowingPrefixWhileSequence.swift index 9f1ece068561c..b05067693debf 100644 --- a/stdlib/public/Concurrency/AsyncThrowingPrefixWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncThrowingPrefixWhileSequence.swift @@ -14,6 +14,42 @@ import Swift @available(SwiftStdlib 5.5, *) extension AsyncSequence { + /// Returns an asynchronous sequence, containing the initial, consecutive + /// elements of the base sequence that satisfy the given error-throwing + /// predicate. + /// + /// Use `prefix(while:)` to produce values while elements from the base + /// sequence meet a condition you specify. The modified sequence ends when + /// the predicate closure returns `false` or throws an error. + /// + /// In this example, an asynchronous sequence called `Counter` produces `Int` + /// values from `1` to `10`. The `prefix(_:)` method causes the modified + /// sequence to pass through values less than `8`, but throws an + /// error when it receives a value that's divisible by `5`: + /// + /// do { + /// let stream = try Counter(howHigh: 10) + /// .prefix { + /// if $0 % 5 == 0 { + /// throw MyError() + /// } + /// return $0 < 8 + /// } + /// for try await number in stream { + /// print("\(number) ", terminator: " ") + /// } + /// } catch { + /// print("Error: \(error)") + /// } + /// // Prints: 1 2 3 4 Error: MyError() + /// + /// - Parameter isIncluded: A error-throwing closure that takes an element of + /// the asynchronous sequence as its argument and returns a Boolean value + /// that indicates whether to include the element in the modified sequence. + /// - Returns: An asynchronous sequence that contains, in order, the elements + /// of the base sequence that satisfy the given predicate. If the predicate + /// throws an error, the sequence contains only values produced prior to + /// the error. @inlinable public __consuming func prefix( while predicate: @escaping (Element) async throws -> Bool @@ -22,7 +58,11 @@ extension AsyncSequence { } } +/// An asynchronous sequence, containing the initial, consecutive +/// elements of the base sequence that satisfy the given error-throwing +/// predicate. @available(SwiftStdlib 5.5, *) +@frozen public struct AsyncThrowingPrefixWhileSequence { @usableFromInline let base: Base @@ -30,7 +70,7 @@ public struct AsyncThrowingPrefixWhileSequence { @usableFromInline let predicate: (Base.Element) async throws -> Bool - @usableFromInline + @inlinable init( _ base: Base, predicate: @escaping (Base.Element) async throws -> Bool @@ -42,9 +82,16 @@ public struct AsyncThrowingPrefixWhileSequence { @available(SwiftStdlib 5.5, *) extension AsyncThrowingPrefixWhileSequence: AsyncSequence { + /// The type of element produced by this asynchronous sequence. + /// + /// The prefix-while sequence produces whatever type of element its base + /// iterator produces. public typealias Element = Base.Element + /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator + /// The iterator that produces elements of the prefix-while sequence. + @frozen public struct Iterator: AsyncIteratorProtocol { @usableFromInline var predicateHasFailed = false @@ -55,7 +102,7 @@ extension AsyncThrowingPrefixWhileSequence: AsyncSequence { @usableFromInline let predicate: (Base.Element) async throws -> Bool - @usableFromInline + @inlinable init( _ baseIterator: Base.AsyncIterator, predicate: @escaping (Base.Element) async throws -> Bool @@ -64,6 +111,13 @@ extension AsyncThrowingPrefixWhileSequence: AsyncSequence { self.predicate = predicate } + /// Produces the next element in the prefix-while sequence. + /// + /// If the predicate hasn't failed yet, this method gets the next element + /// from the base sequence and calls the predicate with it. If this call + /// succeeds, this method passes along the element. Otherwise, it returns + /// `nil`, ending the sequence. If calling the predicate closure throws an + /// error, the sequence ends and `next()` rethrows the error. @inlinable public mutating func next() async throws -> Base.Element? { if !predicateHasFailed, let nextElement = try await baseIterator.next() { diff --git a/stdlib/public/Concurrency/AsyncThrowingStream.swift b/stdlib/public/Concurrency/AsyncThrowingStream.swift new file mode 100644 index 0000000000000..86c3e5d4dca2a --- /dev/null +++ b/stdlib/public/Concurrency/AsyncThrowingStream.swift @@ -0,0 +1,201 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020-2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +@available(SwiftStdlib 5.5, *) +public struct AsyncThrowingStream { + public struct Continuation: Sendable { + /// Indication of the type of termination informed to + /// `onTermination`. + public enum Termination { + + /// The stream was finished via the `finish` method + case finished(Failure?) + + /// The stream was cancelled + case cancelled + } + + /// A result of yielding values. + public enum YieldResult { + + /// When a value is successfully enqueued, either buffered + /// or immediately consumed to resume a pending call to next + /// and a count of remaining slots available in the buffer at + /// the point in time of yielding. Note: transacting upon the + /// remaining count is only valid when then calls to yield are + /// mutually exclusive. + case enqueued(remaining: Int) + + /// Yielding resulted in not buffering an element because the + /// buffer was full. The element is the dropped value. + case dropped(Element) + + /// Indication that the continuation was yielded when the + /// stream was already in a terminal state: either by cancel or + /// by finishing. + case terminated + } + + /// A strategy that handles exhaustion of a buffer’s capacity. + public enum BufferingPolicy { + case unbounded + + /// When the buffer is full, discard the newly received element. + /// This enforces keeping the specified amount of oldest values. + case bufferingOldest(Int) + + /// When the buffer is full, discard the oldest element in the buffer. + /// This enforces keeping the specified amount of newest values. + case bufferingNewest(Int) + } + + let storage: _Storage + + /// Resume the task awaiting the next iteration point by having it return + /// normally from its suspension point or buffer the value if no awaiting + /// next iteration is active. + /// + /// - Parameter value: The value to yield from the continuation. + /// + /// This can be called more than once and returns to the caller immediately + /// without blocking for any awaiting consumption from the iteration. + @discardableResult + public func yield(_ value: __owned Element) -> YieldResult { + storage.yield(value) + } + + /// Resume the task awaiting the next iteration point by having it return + /// nil or throw which signifies the end of the iteration. + /// + /// - Parameter error: The error to throw or nil to signify termination. + /// + /// Calling this function more than once is idempotent; i.e. finishing more + /// than once does not alter the state beyond the requirements of + /// AsyncSequence; which claims that all values past a terminal state are + /// nil. + public func finish(throwing error: __owned Failure? = nil) { + storage.finish(throwing: error) + } + + /// A callback to invoke when iteration of a AsyncThrowingStream is + /// cancelled. + /// + /// If an `onTermination` callback is set, when iteration of a AsyncStream + /// is cancelled via task cancellation that callback is invoked. The + /// callback is disposed of after any terminal state is reached. + /// + /// Cancelling an active iteration will first invoke the onTermination + /// callback and then resume yeilding nil. This means that any cleanup state + /// can be emitted accordingly in the cancellation handler + public var onTermination: (@Sendable (Termination) -> Void)? { + get { + return storage.getOnTermination() + } + nonmutating set { + storage.setOnTermination(newValue) + } + } + } + + let produce: () async throws -> Element? + + /// Construct a AsyncThrowingStream buffering given an Element type. + /// + /// - Parameter elementType: The type the AsyncStream will produce. + /// - Parameter maxBufferedElements: The maximum number of elements to + /// hold in the buffer past any checks for continuations being resumed. + /// - Parameter build: The work associated with yielding values to the + /// AsyncStream. + /// + /// The maximum number of pending elements limited by dropping the oldest + /// value when a new value comes in if the buffer would excede the limit + /// placed upon it. By default this limit is unlimited. + /// + /// The build closure passes in a Continuation which can be used in + /// concurrent contexts. It is thread safe to send and finish; all calls + /// to the continuation are serialized, however calling this from multiple + /// concurrent contexts could result in out of order delivery. + public init( + _ elementType: Element.Type = Element.self, + bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded, + _ build: (Continuation) -> Void + ) where Failure == Error { + let storage: _Storage = .create(limit: limit) + self.init(unfolding: storage.next) + build(Continuation(storage: storage)) + } + + public init( + unfolding produce: @escaping () async throws -> Element? + ) where Failure == Error { + self.produce = produce + } +} + +@available(SwiftStdlib 5.5, *) +extension AsyncThrowingStream: AsyncSequence { + /// The asynchronous iterator for iterating a AsyncThrowingStream. + /// + /// This type is specificially not Sendable. It is not intended to be used + /// from multiple concurrent contexts. Any such case that next is invoked + /// concurrently and contends with another call to next is a programmer error + /// and will fatalError. + public struct Iterator: AsyncIteratorProtocol { + let produce: () async throws -> Element? + + public mutating func next() async throws -> Element? { + return try await produce() + } + } + + /// Construct an iterator. + public func makeAsyncIterator() -> Iterator { + return Iterator(produce: produce) + } +} + +@available(SwiftStdlib 5.5, *) +extension AsyncThrowingStream.Continuation { + /// Resume the task awaiting the next iteration point by having it return + /// normally from its suspension point or buffer the value if no awaiting + /// next iteration is active. + /// + /// - Parameter result: A result to yield from the continuation. + /// + /// This can be called more than once and returns to the caller immediately + /// without blocking for any awaiting consuption from the iteration. + @discardableResult + public func yield( + with result: Result + ) -> YieldResult where Failure == Error { + switch result { + case .success(let val): + return storage.yield(val) + case .failure(let err): + storage.finish(throwing: err) + return .terminated + } + } + + /// Resume the task awaiting the next iteration point by having it return + /// normally from its suspension point or buffer the value if no awaiting + /// next iteration is active where the `Element` is `Void`. + /// + /// This can be called more than once and returns to the caller immediately + /// without blocking for any awaiting consuption from the iteration. + @discardableResult + public func yield() -> YieldResult where Element == Void { + storage.yield(()) + } +} diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 9354ed977317f..7376eac880275 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -10,14 +10,13 @@ # #===----------------------------------------------------------------------===# -set(swift_concurrency_objc_sources - SwiftNativeNSObject.mm) - -set(LLVM_OPTIONAL_SOURCES - ${swift_concurrency_objc_sources}) +if(NOT swift_concurrency_extra_sources) + set(swift_concurrency_extra_sources) +endif() -set(swift_concurrency_link_libraries - swiftCore) +if(NOT swift_concurrency_install_component) + set(swift_concurrency_install_component stdlib) +endif() if(SWIFT_CONCURRENCY_USES_DISPATCH) if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) @@ -32,7 +31,6 @@ if(SWIFT_CONCURRENCY_USES_DISPATCH) endif() endif() - add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB ../CompatibilityOverride/CompatibilityOverride.cpp Actor.cpp @@ -60,7 +58,10 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I AsyncThrowingFlatMapSequence.swift AsyncThrowingMapSequence.swift AsyncThrowingPrefixWhileSequence.swift + GlobalActor.swift + MainActor.swift PartialAsyncTask.swift + SourceCompatibilityShims.swift Task.cpp Task.swift TaskCancellation.swift @@ -70,10 +71,15 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I TaskGroup.swift TaskLocal.cpp TaskLocal.swift + TaskSleep.swift ThreadSanitizer.cpp Mutex.cpp - YieldingContinuation.swift - ${swift_concurrency_objc_sources} + AsyncStreamBuffer.swift + AsyncStream.swift + AsyncThrowingStream.swift + AsyncStream.cpp + Deque.swift + ${swift_concurrency_extra_sources} SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc @@ -91,7 +97,8 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I -parse-stdlib -Xfrontend -enable-experimental-concurrency -Xfrontend -define-availability - -Xfrontend \"SwiftStdlib 5.5:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999\" - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - INSTALL_IN_COMPONENT stdlib + -Xfrontend "SwiftStdlib 5.5:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0" + LINK_FLAGS "${SWIFT_RUNTIME_CONCURRENCY_SWIFT_LINK_FLAGS}" + ${swift_concurrency_options} + INSTALL_IN_COMPONENT ${swift_concurrency_install_component} ) diff --git a/stdlib/public/Concurrency/CheckedContinuation.swift b/stdlib/public/Concurrency/CheckedContinuation.swift index fd8cf131403f1..d98970119a57d 100644 --- a/stdlib/public/Concurrency/CheckedContinuation.swift +++ b/stdlib/public/Concurrency/CheckedContinuation.swift @@ -146,11 +146,11 @@ public struct CheckedContinuation { /// After `resume` enqueues the task, control is immediately returned to /// the caller. The task will continue executing when its executor is /// able to reschedule it. - public func resume(returning x: __owned T) { + public func resume(returning value: __owned T) { if let c: UnsafeContinuation = canary.takeContinuation() { - c.resume(returning: x) + c.resume(returning: value) } else { - fatalError("SWIFT TASK CONTINUATION MISUSE: \(canary.function) tried to resume its continuation more than once, returning \(x)!\n") + fatalError("SWIFT TASK CONTINUATION MISUSE: \(canary.function) tried to resume its continuation more than once, returning \(value)!\n") } } @@ -166,11 +166,11 @@ public struct CheckedContinuation { /// After `resume` enqueues the task, control is immediately returned to /// the caller. The task will continue executing when its executor is /// able to reschedule it. - public func resume(throwing x: __owned E) { + public func resume(throwing error: __owned E) { if let c: UnsafeContinuation = canary.takeContinuation() { - c.resume(throwing: x) + c.resume(throwing: error) } else { - fatalError("SWIFT TASK CONTINUATION MISUSE: \(canary.function) tried to resume its continuation more than once, throwing \(x)!\n") + fatalError("SWIFT TASK CONTINUATION MISUSE: \(canary.function) tried to resume its continuation more than once, throwing \(error)!\n") } } } diff --git a/stdlib/public/Concurrency/Debug.h b/stdlib/public/Concurrency/Debug.h index efedeb6544e11..997fc88c0ceb5 100644 --- a/stdlib/public/Concurrency/Debug.h +++ b/stdlib/public/Concurrency/Debug.h @@ -21,8 +21,15 @@ namespace swift { +// Dispatch knows about these symbol names. Don't change them without consulting +// dispatch. + +/// The metadata pointer used for job objects. +SWIFT_EXPORT_FROM(swift_Concurrency) +const void *const _swift_concurrency_debug_jobMetadata; + /// The metadata pointer used for async task objects. -SWIFT_RUNTIME_STDLIB_SPI +SWIFT_EXPORT_FROM(swift_Concurrency) const void *const _swift_concurrency_debug_asyncTaskMetadata; } // namespace swift diff --git a/stdlib/public/Concurrency/Deque.swift b/stdlib/public/Concurrency/Deque.swift new file mode 100644 index 0000000000000..625aa25674a3b --- /dev/null +++ b/stdlib/public/Concurrency/Deque.swift @@ -0,0 +1,415 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020-2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +@available(SwiftStdlib 5.5, *) +struct _Deque { + internal struct _UnsafeHandle { + let _header: UnsafeMutablePointer<_Storage._Header> + let _elements: UnsafeMutablePointer? + + init( + header: UnsafeMutablePointer<_Storage._Header>, + elements: UnsafeMutablePointer?, + isMutable: Bool + ) { + self._header = header + self._elements = elements + } + + var header: _Storage._Header { + _header.pointee + } + + var capacity: Int { + _header.pointee.capacity + } + + var count: Int { + get { _header.pointee.count } + nonmutating set { _header.pointee.count = newValue } + } + + internal func slot(after slot: Int) -> Int { + assert(slot < capacity) + let position = slot + 1 + if position >= capacity { + return 0 + } + return position + } + + + internal func slot(_ slot: Int, offsetBy delta: Int) -> Int { + assert(slot <= capacity) + let position = slot + delta + if delta >= 0 { + if position >= capacity { return position - capacity } + } else { + if position < 0 { return position + capacity } + } + return position + } + + internal var endSlot: Int { + slot(startSlot, offsetBy: count) + } + + internal func uncheckedAppend(_ element: Element) { + assert(count < capacity) + ptr(at: endSlot).initialize(to: element) + count += 1 + } + + internal func uncheckedRemoveFirst() -> Element { + assert(count > 0) + let result = ptr(at: startSlot).move() + startSlot = slot(after: startSlot) + count -= 1 + return result + } + + internal func uncheckedRemoveFirstIfPresent() -> Element? { + if count > 0 { + let result = ptr(at: startSlot).move() + startSlot = slot(after: startSlot) + count -= 1 + return result + } else { + return nil + } + } + + struct _UnsafeWrappedBuffer { + internal let first: UnsafeBufferPointer + + internal let second: UnsafeBufferPointer? + + internal init( + _ first: UnsafeBufferPointer, + _ second: UnsafeBufferPointer? = nil + ) { + self.first = first + self.second = second + assert(first.count > 0 || second == nil) + } + + internal init( + start: UnsafePointer, + count: Int + ) { + self.init(UnsafeBufferPointer(start: start, count: count)) + } + + internal init( + first start1: UnsafePointer, + count count1: Int, + second start2: UnsafePointer, + count count2: Int + ) { + self.init(UnsafeBufferPointer(start: start1, count: count1), + UnsafeBufferPointer(start: start2, count: count2)) + } + + internal var count: Int { first.count + (second?.count ?? 0) } + } + + internal struct _UnsafeMutableWrappedBuffer { + internal let first: UnsafeMutableBufferPointer + + internal let second: UnsafeMutableBufferPointer? + + internal init( + _ first: UnsafeMutableBufferPointer, + _ second: UnsafeMutableBufferPointer? = nil + ) { + self.first = first + self.second = second?.count == 0 ? nil : second + assert(first.count > 0 || second == nil) + } + + internal init( + start: UnsafeMutablePointer, + count: Int + ) { + self.init(UnsafeMutableBufferPointer(start: start, count: count)) + } + + internal init( + first start1: UnsafeMutablePointer, + count count1: Int, + second start2: UnsafeMutablePointer, + count count2: Int + ) { + self.init(UnsafeMutableBufferPointer(start: start1, count: count1), + UnsafeMutableBufferPointer(start: start2, count: count2)) + } + + internal init(mutating buffer: _UnsafeWrappedBuffer) { + self.init(.init(mutating: buffer.first), + buffer.second.map { .init(mutating: $0) }) + } + } + + internal func segments() -> _UnsafeWrappedBuffer { + let wrap = capacity - startSlot + if count <= wrap { + return .init(start: ptr(at: startSlot), count: count) + } + return .init(first: ptr(at: startSlot), count: wrap, + second: ptr(at: .zero), count: count - wrap) + } + + internal func mutableSegments() -> _UnsafeMutableWrappedBuffer { + return .init(mutating: segments()) + } + + var startSlot: Int { + get { _header.pointee.startSlot } + nonmutating set { _header.pointee.startSlot = newValue } + } + + func ptr(at slot: Int) -> UnsafeMutablePointer { + assert(slot >= 0 && slot <= capacity) + return _elements! + slot + } + + @discardableResult + func initialize( + at start: Int, + from source: UnsafeBufferPointer + ) -> Int { + assert(start + source.count <= capacity) + guard source.count > 0 else { return start } + ptr(at: start).initialize(from: source.baseAddress!, count: source.count) + return start + source.count + } + + @discardableResult + func moveInitialize( + at start: Int, + from source: UnsafeMutableBufferPointer + ) -> Int { + assert(start + source.count <= capacity) + guard source.count > 0 else { return start } + ptr(at: start).moveInitialize(from: source.baseAddress!, count: source.count) + return start + source.count + } + + internal func copyElements() -> _Storage { + let object = _Storage._DequeBuffer.create( + minimumCapacity: capacity, + makingHeaderWith: { _ in header }) + let result = _Storage(_buffer: ManagedBufferPointer(unsafeBufferObject: object)) + guard self.count > 0 else { return result } + result.update { target in + let source = self.segments() + target.initialize(at: startSlot, from: source.first) + if let second = source.second { + target.initialize(at: 0, from: second) + } + } + return result + } + + internal func moveElements(minimumCapacity: Int) -> _Storage { + let count = self.count + assert(minimumCapacity >= count) + let object = _Storage._DequeBuffer.create( + minimumCapacity: minimumCapacity, + makingHeaderWith: { +#if os(OpenBSD) + let capacity = minimumCapacity +#else + let capacity = $0.capacity +#endif + return _Storage._Header( + capacity: capacity, + count: count, + startSlot: .zero) + }) + let result = _Storage(_buffer: ManagedBufferPointer(unsafeBufferObject: object)) + guard count > 0 else { return result } + result.update { target in + let source = self.mutableSegments() + let next = target.moveInitialize(at: .zero, from: source.first) + if let second = source.second { + target.moveInitialize(at: next, from: second) + } + } + self.count = 0 + return result + } + } + + enum _Storage { + internal struct _Header { + var capacity: Int + + var count: Int + + var startSlot: Int + + init(capacity: Int, count: Int, startSlot: Int) { + self.capacity = capacity + self.count = count + self.startSlot = startSlot + } + } + + internal typealias _Buffer = ManagedBufferPointer<_Header, Element> + + case empty + case buffer(_Buffer) + + internal class _DequeBuffer: ManagedBuffer<_Header, Element> { + deinit { + self.withUnsafeMutablePointers { header, elements in + let capacity = header.pointee.capacity + let count = header.pointee.count + let startSlot = header.pointee.startSlot + + if startSlot + count <= capacity { + (elements + startSlot).deinitialize(count: count) + } else { + let firstRegion = capacity - startSlot + (elements + startSlot).deinitialize(count: firstRegion) + elements.deinitialize(count: count - firstRegion) + } + } + } + } + + internal init(_buffer: _Buffer) { + self = .buffer(_buffer) + } + + internal init() { + self = .empty + } + + internal init(_ object: _DequeBuffer) { + self.init(_buffer: _Buffer(unsafeBufferObject: object)) + } + + internal var capacity: Int { + switch self { + case .empty: return 0 + case .buffer(let buffer): + return buffer.withUnsafeMutablePointerToHeader { $0.pointee.capacity } + } + + } + + internal mutating func ensure( + minimumCapacity: Int + ) { + if _slowPath(capacity < minimumCapacity) { + _ensure(minimumCapacity: minimumCapacity) + } + } + + internal static var growthFactor: Double { 1.5 } + + internal func _growCapacity( + to minimumCapacity: Int + ) -> Int { + return Swift.max(Int((Self.growthFactor * Double(capacity)).rounded(.up)), + minimumCapacity) + } + + internal mutating func _ensure( + minimumCapacity: Int + ) { + if capacity >= minimumCapacity { + self = self.read { $0.copyElements() } + } else { + let minimumCapacity = _growCapacity(to: minimumCapacity) + self = self.update { source in + source.moveElements(minimumCapacity: minimumCapacity) + } + } + } + + internal var count: Int { + switch self { + case .empty: return 0 + case .buffer(let buffer): + return buffer.withUnsafeMutablePointerToHeader { $0.pointee.count } + } + + } + + internal func read(_ body: (_UnsafeHandle) throws -> R) rethrows -> R { + switch self { + case .empty: + var header = _Header(capacity: 0, count: 0, startSlot: 0) + return try withUnsafeMutablePointer(to: &header) { headerPtr in + return try body(_UnsafeHandle(header: headerPtr, elements: nil, isMutable: false)) + } + case .buffer(let buffer): + return try buffer.withUnsafeMutablePointers { header, elements in + let handle = _UnsafeHandle(header: header, + elements: elements, + isMutable: false) + return try body(handle) + } + } + + } + + internal func update(_ body: (_UnsafeHandle) throws -> R) rethrows -> R { + switch self { + case .empty: + var header = _Header(capacity: 0, count: 0, startSlot: 0) + return try withUnsafeMutablePointer(to: &header) { headerPtr in + return try body(_UnsafeHandle(header: headerPtr, elements: nil, isMutable: false)) + } + case .buffer(let buffer): + return try buffer.withUnsafeMutablePointers { header, elements in + let handle = _UnsafeHandle(header: header, + elements: elements, + isMutable: true) + return try body(handle) + } + } + } + } + + + internal var _storage: _Storage + + init() { + _storage = _Storage() + } + + var count: Int { _storage.count } + + mutating func append(_ newElement: Element) { + _storage.ensure(minimumCapacity: _storage.count + 1) + _storage.update { + $0.uncheckedAppend(newElement) + } + } + + @discardableResult + mutating func removeFirst() -> Element { + return _storage.update { $0.uncheckedRemoveFirst() } + } + + @discardableResult + mutating func removeFirstIfPresent() -> Element? { + return _storage.update { $0.uncheckedRemoveFirstIfPresent() } + } +} + diff --git a/stdlib/public/Concurrency/Errors.swift b/stdlib/public/Concurrency/Errors.swift index 28fe7ca27ef59..e11f80dde648f 100644 --- a/stdlib/public/Concurrency/Errors.swift +++ b/stdlib/public/Concurrency/Errors.swift @@ -1,18 +1,19 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 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 source file is part of the Swift.org open source project +// +// Copyright (c) 2020 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 +// +//===----------------------------------------------------------------------===// import Swift @_implementationOnly import _SwiftConcurrencyShims +@available(SwiftStdlib 5.5, *) @_silgen_name("swift_deletedAsyncMethodError") public func swift_deletedAsyncMethodError() async { fatalError("Fatal error: Call of deleted method") diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index b7edd460997a0..1daf120fee8fa 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -92,3 +92,32 @@ func _checkExpectedExecutor(_filenameStart: Builtin.RawPointer, _reportUnexpectedExecutor( _filenameStart, _filenameLength, _filenameIsASCII, _line, _executor) } + +#if !SWIFT_STDLIB_SINGLE_THREADED_RUNTIME +// This must take a DispatchQueueShim, not something like AnyObject, +// or else SILGen will emit a retain/release in unoptimized builds, +// which won't work because DispatchQueues aren't actually +// Swift-retainable. +@available(SwiftStdlib 5.5, *) +@_silgen_name("swift_task_enqueueOnDispatchQueue") +internal func _enqueueOnDispatchQueue(_ job: UnownedJob, + queue: DispatchQueueShim) + +/// Used by the runtime solely for the witness table it produces. +/// FIXME: figure out some way to achieve that which doesn't generate +/// all the other metadata +/// +/// Expected to work for any primitive dispatch queue; note that this +/// means a dispatch_queue_t, which is not the same as DispatchQueue +/// on platforms where that is an instance of a wrapper class. +@available(SwiftStdlib 5.5, *) +internal final class DispatchQueueShim: UnsafeSendable, SerialExecutor { + func enqueue(_ job: UnownedJob) { + _enqueueOnDispatchQueue(job, queue: self) + } + + func asUnownedSerialExecutor() -> UnownedSerialExecutor { + return UnownedSerialExecutor(ordinary: self) + } +} +#endif \ No newline at end of file diff --git a/stdlib/public/Concurrency/GlobalActor.swift b/stdlib/public/Concurrency/GlobalActor.swift new file mode 100644 index 0000000000000..af01b8816fa1a --- /dev/null +++ b/stdlib/public/Concurrency/GlobalActor.swift @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +/// A type that represents a globally-unique actor that can be used to isolate +/// various declarations anywhere in the program. +/// +/// A type that conforms to the `GlobalActor` protocol and is marked with the +/// the `@globalActor` attribute can be used as a custom attribute. Such types +/// are called global actor types, and can be applied to any declaration to +/// specify that such types are isolated to that global actor type. When using +/// such a declaration from another actor (or from nonisolated code), +/// synchronization is performed through the \c shared actor instance to ensure +/// mutually-exclusive access to the declaration. +@available(SwiftStdlib 5.5, *) +public protocol GlobalActor { + /// The type of the shared actor instance that will be used to provide + /// mutually-exclusive access to declarations annotated with the given global + /// actor type. + associatedtype ActorType: Actor + + /// The shared actor instance that will be used to provide mutually-exclusive + /// access to declarations annotated with the given global actor type. + /// + /// The value of this property must always evaluate to the same actor + /// instance. + static var shared: ActorType { get } + + /// The shared executor instance that will be used to provide + /// mutually-exclusive access for the global actor. + /// + /// The value of this property must be equivalent to `shared.unownedExecutor`. + static var sharedUnownedExecutor: UnownedSerialExecutor { get } +} + +@available(SwiftStdlib 5.5, *) +extension GlobalActor { + public static var sharedUnownedExecutor: UnownedSerialExecutor { + shared.unownedExecutor + } +} + diff --git a/stdlib/public/Concurrency/GlobalExecutor.cpp b/stdlib/public/Concurrency/GlobalExecutor.cpp index a7ada5f4ed70b..49a3ae90ed7d9 100644 --- a/stdlib/public/Concurrency/GlobalExecutor.cpp +++ b/stdlib/public/Concurrency/GlobalExecutor.cpp @@ -59,12 +59,15 @@ #include "TaskPrivate.h" #include "Error.h" +#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR #include #if !defined(_WIN32) #include #endif +#endif + using namespace swift; SWIFT_CC(swift) @@ -180,7 +183,7 @@ void swift::donateThreadToGlobalExecutorUntil(bool (*condition)(void *), while (!condition(conditionContext)) { auto job = claimNextFromJobQueue(); if (!job) return; - job->run(ExecutorRef::generic()); + swift_job_run(job, ExecutorRef::generic()); } } @@ -239,8 +242,9 @@ static void initializeDispatchEnqueueFunc(dispatch_queue_t queue, void *obj, dispatch_qos_class_t qos) { dispatchEnqueueFuncType func = nullptr; - // Always fall back to plain dispatch_async_f on Windows for now. -#if !defined(_WIN32) + // Always fall back to plain dispatch_async_f on Windows for now, and + // also for back-deployed concurrency. +#if !defined(_WIN32) && !defined(SWIFT_CONCURRENCY_BACK_DEPLOYMENT) if (runtime::environment::concurrencyEnableJobDispatchIntegration()) func = reinterpret_cast( dlsym(RTLD_NEXT, "dispatch_async_swift_job")); @@ -336,8 +340,10 @@ static void swift_task_enqueueGlobalImpl(Job *job) { } void swift::swift_task_enqueueGlobal(Job *job) { + _swift_tsan_release(job); + if (swift_task_enqueueGlobal_hook) - swift_task_enqueueGlobal_hook(job, swift_task_enqueueGlobal); + swift_task_enqueueGlobal_hook(job, swift_task_enqueueGlobalImpl); else swift_task_enqueueGlobalImpl(job); } @@ -390,8 +396,7 @@ static void swift_task_enqueueMainExecutorImpl(Job *job) { // This is an inline function that compiles down to a pointer to a global. auto mainQueue = dispatch_get_main_queue(); - dispatchEnqueue(mainQueue, job, (dispatch_qos_class_t)priority, - DISPATCH_QUEUE_MAIN_EXECUTOR); + dispatchEnqueue(mainQueue, job, (dispatch_qos_class_t)priority, mainQueue); #endif } @@ -404,5 +409,36 @@ void swift::swift_task_enqueueMainExecutor(Job *job) { swift_task_enqueueMainExecutorImpl(job); } +#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR +void swift::swift_task_enqueueOnDispatchQueue(Job *job, + HeapObject *_queue) { + JobPriority priority = job->getPriority(); + auto queue = reinterpret_cast(_queue); + dispatchEnqueue(queue, job, (dispatch_qos_class_t)priority, queue); +} +#endif + +#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR +static HeapObject _swift_mainExecutorIdentity; +#endif + +ExecutorRef swift::swift_task_getMainExecutor() { +#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR + return ExecutorRef::forOrdinary(&_swift_mainExecutorIdentity, nullptr); +#else + return ExecutorRef::forOrdinary( + reinterpret_cast(&_dispatch_main_q), + _swift_task_getDispatchQueueSerialExecutorWitnessTable()); +#endif +} + +bool ExecutorRef::isMainExecutor() const { +#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR + return Identity == &_swift_mainExecutorIdentity; +#else + return Identity == reinterpret_cast(&_dispatch_main_q); +#endif +} + #define OVERRIDE_GLOBAL_EXECUTOR COMPATIBILITY_OVERRIDE #include COMPATIBILITY_OVERRIDE_INCLUDE_PATH diff --git a/stdlib/public/Concurrency/MainActor.swift b/stdlib/public/Concurrency/MainActor.swift new file mode 100644 index 0000000000000..6220430eb1fc5 --- /dev/null +++ b/stdlib/public/Concurrency/MainActor.swift @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +/// A singleton actor whose executor is equivalent to the main +/// dispatch queue. +@available(SwiftStdlib 5.5, *) +@globalActor public final actor MainActor: GlobalActor { + public static let shared = MainActor() + + @inlinable + public nonisolated var unownedExecutor: UnownedSerialExecutor { + #if compiler(>=5.5) && $BuiltinBuildMainExecutor + return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) + #else + fatalError("Swift compiler is incompatible with this SDK version") + #endif + } + + @inlinable + public static var sharedUnownedExecutor: UnownedSerialExecutor { + #if compiler(>=5.5) && $BuiltinBuildMainExecutor + return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) + #else + fatalError("Swift compiler is incompatible with this SDK version") + #endif + } + + @inlinable + public nonisolated func enqueue(_ job: UnownedJob) { + _enqueueOnMain(job) + } +} + +@available(SwiftStdlib 5.5, *) +extension MainActor { + /// Execute the given body closure on the main actor. + public static func run( + resultType: T.Type = T.self, + body: @MainActor @Sendable () throws -> T + ) async rethrows -> T { + @MainActor func runOnMain(body: @MainActor @Sendable () throws -> T) async rethrows -> T { + return try body() + } + + return try await runOnMain(body: body) + } +} diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift index 1f73d702aad5e..b69fde7e4b94a 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2020 - 2021 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 @@ -13,9 +13,11 @@ import Swift @_implementationOnly import _SwiftConcurrencyShims -/// TODO: remove this before shipping @available(SwiftStdlib 5.5, *) -public typealias PartialAsyncTask = UnownedJob +@_silgen_name("swift_job_run") +@usableFromInline +internal func _swiftJobRun(_ job: UnownedJob, + _ executor: UnownedSerialExecutor) -> () /// A job is a unit of scheduleable work. @available(SwiftStdlib 5.5, *) @@ -23,7 +25,11 @@ public typealias PartialAsyncTask = UnownedJob public struct UnownedJob { private var context: Builtin.Job - public func run() { } + @_alwaysEmitIntoClient + @inlinable + public func _runSynchronously(on executor: UnownedSerialExecutor) { + _swiftJobRun(self, executor) + } } @available(SwiftStdlib 5.5, *) @@ -224,6 +230,9 @@ public func withUnsafeThrowingContinuation( } } +/// A hack to mark an SDK that supports swift_continuation_await. @available(SwiftStdlib 5.5, *) -@available(*, deprecated, message: "please use UnsafeContination<..., Error>") -public typealias UnsafeThrowingContinuation = UnsafeContinuation +@_alwaysEmitIntoClient +public func _abiEnableAwaitContinuation() { + fatalError("never use this function") +} diff --git a/stdlib/public/Concurrency/ReexportedSymbols b/stdlib/public/Concurrency/ReexportedSymbols new file mode 100644 index 0000000000000..2ed597566ea64 --- /dev/null +++ b/stdlib/public/Concurrency/ReexportedSymbols @@ -0,0 +1,2 @@ +_OBJC_CLASS_$_SwiftNativeNSObject +_OBJC_METACLASS_$_SwiftNativeNSObject diff --git a/stdlib/public/Concurrency/SourceCompatibilityShims.swift b/stdlib/public/Concurrency/SourceCompatibilityShims.swift new file mode 100644 index 0000000000000..eaacf57bb0ddd --- /dev/null +++ b/stdlib/public/Concurrency/SourceCompatibilityShims.swift @@ -0,0 +1,303 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2021 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 source compatibility shims to help migrate code +// using earlier versions of the concurrency library to the latest syntax. +//===----------------------------------------------------------------------===// + +import Swift +@_implementationOnly import _SwiftConcurrencyShims + +@available(SwiftStdlib 5.5, *) +extension Task where Success == Never, Failure == Never { + @available(*, deprecated, message: "Task.Priority has been removed; use TaskPriority") + public typealias Priority = TaskPriority + + @available(*, deprecated, message: "Task.Handle has been removed; use Task") + public typealias Handle = _Concurrency.Task + + @available(*, deprecated, message: "Task.CancellationError has been removed; use CancellationError") + @_alwaysEmitIntoClient + public static func CancellationError() -> _Concurrency.CancellationError { + return _Concurrency.CancellationError() + } + + @available(*, deprecated, renamed: "yield()") + @_alwaysEmitIntoClient + public static func suspend() async { + await yield() + } +} + +@available(SwiftStdlib 5.5, *) +extension TaskPriority { + @available(*, deprecated, message: "unspecified priority will be removed; use nil") + @_alwaysEmitIntoClient + public static var unspecified: TaskPriority { + .init(rawValue: 0x00) + } + + @available(*, deprecated, message: "userInteractive priority will be removed") + @_alwaysEmitIntoClient + public static var userInteractive: TaskPriority { + .init(rawValue: 0x21) + } +} + +@available(SwiftStdlib 5.5, *) +@_alwaysEmitIntoClient +public func withTaskCancellationHandler( + handler: @Sendable () -> Void, + operation: () async throws -> T +) async rethrows -> T { + try await withTaskCancellationHandler(operation: operation, onCancel: handler) +} + +@available(SwiftStdlib 5.5, *) +extension Task where Success == Never, Failure == Never { + @available(*, deprecated, message: "`Task.withCancellationHandler` has been replaced by `withTaskCancellationHandler` and will be removed shortly.") + @_alwaysEmitIntoClient + public static func withCancellationHandler( + handler: @Sendable () -> Void, + operation: () async throws -> T + ) async rethrows -> T { + try await withTaskCancellationHandler(handler: handler, operation: operation) + } +} + +@available(SwiftStdlib 5.5, *) +extension Task where Failure == Error { + @discardableResult + @_alwaysEmitIntoClient + @available(*, deprecated, message: "`Task.runDetached` was replaced by `Task.detached` and will be removed shortly.") + public static func runDetached( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> Success + ) -> Task { + detached(priority: priority, operation: operation) + } +} + +@discardableResult +@available(SwiftStdlib 5.5, *) +@available(*, deprecated, message: "`detach` was replaced by `Task.detached` and will be removed shortly.") +@_alwaysEmitIntoClient +public func detach( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> T +) -> Task { + Task.detached(priority: priority, operation: operation) +} + +@discardableResult +@available(SwiftStdlib 5.5, *) +@available(*, deprecated, message: "`detach` was replaced by `Task.detached` and will be removed shortly.") +@_alwaysEmitIntoClient +public func detach( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> T +) -> Task { + Task.detached(priority: priority, operation: operation) +} + +@discardableResult +@available(SwiftStdlib 5.5, *) +@available(*, deprecated, message: "`asyncDetached` was replaced by `Task.detached` and will be removed shortly.") +@_alwaysEmitIntoClient +public func asyncDetached( + priority: TaskPriority? = nil, + @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> T +) -> Task { + return Task.detached(priority: priority, operation: operation) +} + +@discardableResult +@available(SwiftStdlib 5.5, *) +@available(*, deprecated, message: "`asyncDetached` was replaced by `Task.detached` and will be removed shortly.") +@_alwaysEmitIntoClient +public func asyncDetached( + priority: TaskPriority? = nil, + @_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> T +) -> Task { + return Task.detached(priority: priority, operation: operation) +} + +@available(SwiftStdlib 5.5, *) +@available(*, deprecated, message: "`async` was replaced by `Task.init` and will be removed shortly.") +@discardableResult +@_alwaysEmitIntoClient +public func async( + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> T +) -> Task { + .init(priority: priority, operation: operation) +} + +@available(SwiftStdlib 5.5, *) +@available(*, deprecated, message: "`async` was replaced by `Task.init` and will be removed shortly.") +@discardableResult +@_alwaysEmitIntoClient +public func async( + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> T +) -> Task { + .init(priority: priority, operation: operation) +} + +@available(SwiftStdlib 5.5, *) +extension Task where Success == Never, Failure == Never { + @available(*, deprecated, message: "`Task.Group` was replaced by `ThrowingTaskGroup` and `TaskGroup` and will be removed shortly.") + public typealias Group = ThrowingTaskGroup + + @available(*, deprecated, message: "`Task.withGroup` was replaced by `withThrowingTaskGroup` and `withTaskGroup` and will be removed shortly.") + @_alwaysEmitIntoClient + public static func withGroup( + resultType: TaskResult.Type, + returning returnType: BodyResult.Type = BodyResult.self, + body: (inout Task.Group) async throws -> BodyResult + ) async rethrows -> BodyResult { + try await withThrowingTaskGroup(of: resultType) { group in + try await body(&group) + } + } +} + +@available(SwiftStdlib 5.5, *) +extension Task { + @available(*, deprecated, message: "get() has been replaced by .value") + @_alwaysEmitIntoClient + public func get() async throws -> Success { + return try await value + } + + @available(*, deprecated, message: "getResult() has been replaced by .result") + @_alwaysEmitIntoClient + public func getResult() async -> Result { + return await result + } +} + +@available(SwiftStdlib 5.5, *) +extension Task where Failure == Never { + @available(*, deprecated, message: "get() has been replaced by .value") + @_alwaysEmitIntoClient + public func get() async -> Success { + return await value + } +} + +@available(SwiftStdlib 5.5, *) +extension TaskGroup { + @available(*, deprecated, renamed: "addTask(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func add( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) async -> Bool { + return self.addTaskUnlessCancelled(priority: priority) { + await operation() + } + } + + @available(*, deprecated, renamed: "addTask(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func spawn( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) { + addTask(priority: priority, operation: operation) + } + + @available(*, deprecated, renamed: "addTaskUnlessCancelled(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func spawnUnlessCancelled( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) -> Bool { + addTaskUnlessCancelled(priority: priority, operation: operation) + } + + @available(*, deprecated, renamed: "addTask(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func async( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) { + addTask(priority: priority, operation: operation) + } + + @available(*, deprecated, renamed: "addTaskUnlessCancelled(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func asyncUnlessCancelled( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) -> Bool { + addTaskUnlessCancelled(priority: priority, operation: operation) + } +} + +@available(SwiftStdlib 5.5, *) +extension ThrowingTaskGroup { + @available(*, deprecated, renamed: "addTask(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func add( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) async -> Bool { + return self.addTaskUnlessCancelled(priority: priority) { + try await operation() + } + } + + @available(*, deprecated, renamed: "addTask(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func spawn( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) { + addTask(priority: priority, operation: operation) + } + + @available(*, deprecated, renamed: "addTaskUnlessCancelled(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func spawnUnlessCancelled( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) -> Bool { + addTaskUnlessCancelled(priority: priority, operation: operation) + } + + @available(*, deprecated, renamed: "addTask(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func async( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) { + addTask(priority: priority, operation: operation) + } + + @available(*, deprecated, renamed: "addTaskUnlessCancelled(priority:operation:)") + @_alwaysEmitIntoClient + public mutating func asyncUnlessCancelled( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) -> Bool { + addTaskUnlessCancelled(priority: priority, operation: operation) + } +} + +@available(SwiftStdlib 5.5, *) +@available(*, deprecated, message: "please use UnsafeContination<..., Error>") +public typealias UnsafeThrowingContinuation = UnsafeContinuation + +@available(SwiftStdlib 5.5, *) +@available(*, deprecated, renamed: "UnownedJob") +public typealias PartialAsyncTask = UnownedJob diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index f929dbfc92177..bc30b81103612 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -18,21 +18,36 @@ #include "swift/Runtime/Concurrency.h" #include "swift/ABI/Task.h" #include "swift/ABI/TaskLocal.h" +#include "swift/ABI/TaskOptions.h" #include "swift/ABI/Metadata.h" #include "swift/Runtime/Mutex.h" #include "swift/Runtime/HeapObject.h" #include "TaskGroupPrivate.h" #include "TaskPrivate.h" -#include "AsyncCall.h" #include "Debug.h" #include "Error.h" +#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR #include +#endif #if !defined(_WIN32) #include #endif +#ifdef __APPLE__ +#if __POINTER_WIDTH__ == 64 +asm("\n .globl _swift_async_extendedFramePointerFlags" \ + "\n _swift_async_extendedFramePointerFlags = 0x1000000000000000"); +#elif __ARM64_ARCH_8_32__ +asm("\n .globl _swift_async_extendedFramePointerFlags" \ + "\n _swift_async_extendedFramePointerFlags = 0x10000000"); +#else +asm("\n .globl _swift_async_extendedFramePointerFlags" \ + "\n _swift_async_extendedFramePointerFlags = 0x0"); +#endif +#endif // __APPLE__ + using namespace swift; using FutureFragment = AsyncTask::FutureFragment; using TaskGroup = swift::TaskGroup; @@ -48,12 +63,16 @@ void FutureFragment::destroy() { break; case Status::Error: - swift_unknownObjectRelease(reinterpret_cast(getError())); + swift_errorRelease(getError()); break; } } -FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask) { +FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask, + AsyncContext *waitingTaskContext, + TaskContinuationFunction *resumeFn, + AsyncContext *callerContext, + OpaqueValue *result) { using Status = FutureFragment::Status; using WaitQueueItem = FutureFragment::WaitQueueItem; @@ -61,26 +80,37 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask) { auto fragment = futureFragment(); auto queueHead = fragment->waitQueue.load(std::memory_order_acquire); + bool contextIntialized = false; while (true) { switch (queueHead.getStatus()) { case Status::Error: case Status::Success: -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] task %p waiting on task %p, completed immediately\n", pthread_self(), waitingTask, this); -#endif + SWIFT_TASK_DEBUG_LOG("task %p waiting on task %p, completed immediately", + waitingTask, this); _swift_tsan_acquire(static_cast(this)); + if (contextIntialized) waitingTask->flagAsRunning(); // The task is done; we don't need to wait. return queueHead.getStatus(); case Status::Executing: -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] task %p waiting on task %p, going to sleep\n", pthread_self(), waitingTask, this); -#endif + SWIFT_TASK_DEBUG_LOG("task %p waiting on task %p, going to sleep", + waitingTask, this); _swift_tsan_release(static_cast(waitingTask)); - // Task is now complete. We'll need to add ourselves to the queue. + // Task is not complete. We'll need to add ourselves to the queue. break; } + if (!contextIntialized) { + contextIntialized = true; + auto context = + reinterpret_cast(waitingTaskContext); + context->errorResult = nullptr; + context->successResultPointer = result; + context->ResumeParent = resumeFn; + context->Parent = callerContext; + waitingTask->flagAsSuspended(); + } + // Put the waiting task at the beginning of the wait queue. waitingTask->getNextWaitingTask() = queueHead.getTask(); auto newQueueHead = WaitQueueItem::get(Status::Executing, waitingTask); @@ -91,11 +121,26 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask) { // Escalate the priority of this task based on the priority // of the waiting task. swift_task_escalate(this, waitingTask->Flags.getPriority()); + _swift_task_clearCurrent(); return FutureFragment::Status::Executing; } } } +void NullaryContinuationJob::process(Job *_job) { + auto *job = cast(_job); + + auto *task = job->Task; + auto *continuation = job->Continuation; + + _swift_task_dealloc_specific(task, job); + + auto *context = cast(continuation->ResumeContext); + + context->setErrorResult(nullptr); + swift_continuation_resume(continuation); +} + void AsyncTask::completeFuture(AsyncContext *context) { using Status = FutureFragment::Status; using WaitQueueItem = FutureFragment::WaitQueueItem; @@ -135,19 +180,16 @@ void AsyncTask::completeFuture(AsyncContext *context) { // Schedule every waiting task on the executor. auto waitingTask = queueHead.getTask(); -#if SWIFT_TASK_PRINTF_DEBUG if (!waitingTask) - fprintf(stderr, "[%p] task %p had no waiting tasks\n", pthread_self(), this); -#endif + SWIFT_TASK_DEBUG_LOG("task %p had no waiting tasks", this); while (waitingTask) { // Find the next waiting task before we invalidate it by resuming // the task. auto nextWaitingTask = waitingTask->getNextWaitingTask(); -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] waking task %p from future of task %p\n", pthread_self(), waitingTask, this); -#endif + SWIFT_TASK_DEBUG_LOG("waking task %p from future of task %p", waitingTask, + this); // Fill in the return context. auto waitingContext = @@ -175,13 +217,14 @@ static void destroyJob(SWIFT_CONTEXT HeapObject *obj) { } AsyncTask::~AsyncTask() { + flagAsCompleted(); + // For a future, destroy the result. if (isFuture()) { futureFragment()->destroy(); } - // Release any objects potentially held as task local values. - Local.destroy(this); + Private.destroy(); } SWIFT_CC(swift) @@ -196,20 +239,21 @@ static void destroyTask(SWIFT_CONTEXT HeapObject *obj) { // the task-local allocator. There's actually nothing else to clean up // here. -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] destroy task %p\n", pthread_self(), task); -#endif + SWIFT_TASK_DEBUG_LOG("destroy task %p", task); free(task); } static ExecutorRef executorForEnqueuedJob(Job *job) { +#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR + return ExecutorRef::generic(); +#else void *jobQueue = job->SchedulerPrivate[Job::DispatchQueueIndex]; if (jobQueue == DISPATCH_QUEUE_GLOBAL_EXECUTOR) return ExecutorRef::generic(); - else if (jobQueue == DISPATCH_QUEUE_MAIN_EXECUTOR) - return _swift_task_getMainExecutor(); else - swift_unreachable("jobQueue was not a known value."); + return ExecutorRef::forOrdinary(reinterpret_cast(jobQueue), + _swift_task_getDispatchQueueSerialExecutorWitnessTable()); +#endif } static void jobInvoke(void *obj, void *unused, uint32_t flags) { @@ -255,6 +299,8 @@ static FullMetadata taskHeapMetadata = { } }; +const void *const swift::_swift_concurrency_debug_jobMetadata = + static_cast(&jobHeapMetadata); const void *const swift::_swift_concurrency_debug_asyncTaskMetadata = static_cast(&taskHeapMetadata); @@ -268,17 +314,9 @@ static void completeTaskImpl(AsyncTask *task, reinterpret_cast(context) - sizeof(AsyncContextPrefix)); asyncContextPrefix->errorResult = error; - // Destroy and deallocate any remaining task local items. - // We need to do this before we destroy the task local deallocator. - task->Local.destroy(task); + task->Private.complete(task); - // Tear down the task-local allocator immediately; - // there's no need to wait for the object to be destroyed. - _swift_task_alloc_destroy(task); - -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] task %p completed\n", pthread_self(), task); -#endif + SWIFT_TASK_DEBUG_LOG("task %p completed", task); // Complete the future. // Warning: This deallocates the task in case it's an async let task. @@ -358,42 +396,116 @@ SWIFT_CC(swiftasync) static void task_wait_throwing_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) { auto context = static_cast(_context); - return context->asyncResumeEntryPoint(_context, context->errorResult); + auto resumeWithError = + reinterpret_cast(context->ResumeParent); + return resumeWithError(context->Parent, context->errorResult); } -/// All `swift_task_create*` variants funnel into this common implementation. -/// -/// If \p isSpawnLetTask is true, the \p closureContext is not heap allocated, -/// but stack-allocated (and must not be ref-counted). -/// Also, async-let tasks are not heap allcoated, but allcoated with the parent -/// task's stack allocator. -static AsyncTaskAndContext swift_task_create_group_future_commonImpl( - JobFlags flags, TaskGroup *group, +SWIFT_CC(swiftasync) +static void +task_future_wait_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) { + return _context->ResumeParent(_context->Parent); +} + +/// Implementation of task creation. +SWIFT_CC(swift) +static AsyncTaskAndContext swift_task_create_commonImpl( + size_t rawTaskCreateFlags, + TaskOptionRecord *options, const Metadata *futureResultType, - FutureAsyncSignature::FunctionType *function, - void *closureContext, bool isSpawnLetTask, + FutureAsyncSignature::FunctionType *function, void *closureContext, size_t initialContextSize) { - assert((futureResultType != nullptr) == flags.task_isFuture()); - assert(!flags.task_isFuture() || - initialContextSize >= sizeof(FutureAsyncContext)); - assert((group != nullptr) == flags.task_isGroupChildTask()); + TaskCreateFlags taskCreateFlags(rawTaskCreateFlags); + + // Propagate task-creation flags to job flags as appropriate. + JobFlags jobFlags(JobKind::Task, taskCreateFlags.getPriority()); + jobFlags.task_setIsChildTask(taskCreateFlags.isChildTask()); + if (futureResultType) { + jobFlags.task_setIsFuture(true); + assert(initialContextSize >= sizeof(FutureAsyncContext)); + } + + // Collect the options we know about. + ExecutorRef executor = ExecutorRef::generic(); + TaskGroup *group = nullptr; + AsyncLet *asyncLet = nullptr; + void *asyncLetBuffer = nullptr; + bool hasAsyncLetResultBuffer = false; + for (auto option = options; option; option = option->getParent()) { + switch (option->getKind()) { + case TaskOptionRecordKind::Executor: + executor = cast(option)->getExecutor(); + break; + + case TaskOptionRecordKind::TaskGroup: + group = cast(option)->getGroup(); + assert(group && "Missing group"); + jobFlags.task_setIsGroupChildTask(true); + break; + + case TaskOptionRecordKind::AsyncLet: + asyncLet = cast(option)->getAsyncLet(); + assert(asyncLet && "Missing async let storage"); + jobFlags.task_setIsAsyncLetTask(true); + jobFlags.task_setIsChildTask(true); + break; + + case TaskOptionRecordKind::AsyncLetWithBuffer: + auto *aletRecord = cast(option); + asyncLet = aletRecord->getAsyncLet(); + // TODO: Actually digest the result buffer into the async let task + // context, so that we can emplace the eventual result there instead + // of in a FutureFragment. + hasAsyncLetResultBuffer = true; + asyncLetBuffer = aletRecord->getResultBuffer(); + assert(asyncLet && "Missing async let storage"); + + jobFlags.task_setIsAsyncLetTask(true); + jobFlags.task_setIsChildTask(true); + break; + } + } + + // Add to the task group, if requested. + if (taskCreateFlags.addPendingGroupTaskUnconditionally()) { + assert(group && "Missing group"); + swift_taskGroup_addPending(group, /*unconditionally=*/true); + } AsyncTask *parent = nullptr; - if (flags.task_isChildTask()) { + if (jobFlags.task_isChildTask()) { parent = swift_task_getCurrent(); assert(parent != nullptr && "creating a child task with no active task"); + } - // Inherit the priority of the parent task if unspecified. - if (flags.getPriority() == JobPriority::Unspecified) - flags.setPriority(parent->getPriority()); + // Inherit the priority of the currently-executing task if unspecified and + // we want to inherit. + if (jobFlags.getPriority() == JobPriority::Unspecified && + (jobFlags.task_isChildTask() || taskCreateFlags.inheritContext())) { + AsyncTask *currentTask = parent; + if (!currentTask) + currentTask = swift_task_getCurrent(); + + if (currentTask) + jobFlags.setPriority(currentTask->getPriority()); + else if (taskCreateFlags.inheritContext()) + jobFlags.setPriority(swift_task_getCurrentThreadPriority()); } + // Adjust user-interactive priorities down to user-initiated. + if (jobFlags.getPriority() == JobPriority::UserInteractive) + jobFlags.setPriority(JobPriority::UserInitiated); + + // If there is still no job priority, use the default priority. + if (jobFlags.getPriority() == JobPriority::Unspecified) + jobFlags.setPriority(JobPriority::Default); + // Figure out the size of the header. size_t headerSize = sizeof(AsyncTask); if (parent) { headerSize += sizeof(AsyncTask::ChildFragment); } - if (flags.task_isGroupChildTask()) { + if (group) { headerSize += sizeof(AsyncTask::GroupChildFragment); } if (futureResultType) { @@ -413,19 +525,37 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl( assert(amountToAllocate % MaximumAlignment == 0); - constexpr unsigned initialSlabSize = 512; + unsigned initialSlabSize = 512; void *allocation = nullptr; - if (isSpawnLetTask) { + if (asyncLet) { assert(parent); - allocation = _swift_task_alloc_specific(parent, - amountToAllocate + initialSlabSize); + + // If there isn't enough room in the fixed async let allocation to + // set up the initial context, then we'll have to allocate more space + // from the parent. + if (asyncLet->getSizeOfPreallocatedSpace() < amountToAllocate) { + hasAsyncLetResultBuffer = false; + } + + // DEPRECATED. This is separated from the above condition because we + // also have to handle an older async let ABI that did not provide + // space for the initial slab in the compiler-generated preallocation. + if (!hasAsyncLetResultBuffer) { + allocation = _swift_task_alloc_specific(parent, + amountToAllocate + initialSlabSize); + } else { + allocation = asyncLet->getPreallocatedSpace(); + assert(asyncLet->getSizeOfPreallocatedSpace() >= amountToAllocate + && "async let does not preallocate enough space for child task"); + initialSlabSize = asyncLet->getSizeOfPreallocatedSpace() + - amountToAllocate; + } } else { allocation = malloc(amountToAllocate); } -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] allocate task %p, parent = %p\n", pthread_self(), allocation, parent); -#endif + SWIFT_TASK_DEBUG_LOG("allocate task %p, parent = %p, slab %u", allocation, + parent, initialSlabSize); AsyncContext *initialContext = reinterpret_cast( @@ -462,14 +592,14 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl( // Initialize the task so that resuming it will run the given // function on the initial context. AsyncTask *task = nullptr; - if (isSpawnLetTask) { + if (asyncLet) { // Initialize the refcount bits to "immortal", so that // ARC operations don't have any effect on the task. task = new(allocation) AsyncTask(&taskHeapMetadata, - InlineRefCounts::Immortal, flags, + InlineRefCounts::Immortal, jobFlags, function, initialContext); } else { - task = new(allocation) AsyncTask(&taskHeapMetadata, flags, + task = new(allocation) AsyncTask(&taskHeapMetadata, jobFlags, function, initialContext); } @@ -480,7 +610,7 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl( } // Initialize the group child fragment if applicable. - if (flags.task_isGroupChildTask()) { + if (group) { auto groupChildFragment = task->groupChildFragment(); new (groupChildFragment) AsyncTask::GroupChildFragment(group); } @@ -493,7 +623,6 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl( // Set up the context for the future so there is no error, and a successful // result will be written into the future fragment's storage. - auto futureContext = static_cast(initialContext); auto futureAsyncContextPrefix = reinterpret_cast( reinterpret_cast(allocation) + headerSize - @@ -501,9 +630,20 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl( futureAsyncContextPrefix->indirectResult = futureFragment->getStoragePtr(); } -#if SWIFT_TASK_PRINTF_DEBUG - fprintf(stderr, "[%p] creating task %p with parent %p\n", pthread_self(), task, parent); -#endif + SWIFT_TASK_DEBUG_LOG("creating task %p with parent %p", task, parent); + + // Initialize the task-local allocator. + initialContext->ResumeParent = reinterpret_cast( + asyncLet ? &completeTask + : closureContext ? &completeTaskWithClosure + : &completeTaskAndRelease); + if (asyncLet && initialSlabSize > 0) { + assert(parent); + void *initialSlab = (char*)allocation + amountToAllocate; + task->Private.initializeWithSlab(task, initialSlab, initialSlabSize); + } else { + task->Private.initialize(task); + } // Perform additional linking between parent and child task. if (parent) { @@ -512,8 +652,12 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl( // In a task group we would not have allowed the `add` to create a child anymore, // however better safe than sorry and `async let` are not expressed as task groups, // so they may have been spawned in any case still. - if (swift_task_isCancelled(parent)) + if (swift_task_isCancelled(parent) || + (group && group->isCancelled())) swift_task_cancel(task); + + // Initialize task locals with a link to the parent task. + task->_private().Local.initializeLinkParent(task, parent); } // Configure the initial context. @@ -524,66 +668,29 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl( // be is the final hop. Store a signed null instead. initialContext->Parent = nullptr; initialContext->Flags = AsyncContextKind::Ordinary; - initialContext->Flags.setShouldNotDeallocateInCallee(true); - // Initialize the task-local allocator. - if (isSpawnLetTask) { - initialContext->ResumeParent = reinterpret_cast( - &completeTask); - assert(parent); - void *initialSlab = (char*)allocation + amountToAllocate; - _swift_task_alloc_initialize_with_slab(task, initialSlab, initialSlabSize); - } else { - initialContext->ResumeParent = reinterpret_cast( - closureContext ? &completeTaskWithClosure : &completeTaskAndRelease); - _swift_task_alloc_initialize(task); + // Attach to the group, if needed. + if (group) { + swift_taskGroup_attachChild(group, task); } - // TODO: if the allocator would be prepared earlier we could do this in some - // other existing if-parent if rather than adding another one here. - if (parent) { - // Initialize task locals with a link to the parent task. - task->Local.initializeLinkParent(task, parent); + // If we're supposed to copy task locals, do so now. + if (taskCreateFlags.copyTaskLocals()) { + swift_task_localsCopyTo(task); } - return {task, initialContext}; -} - -static AsyncTaskAndContext swift_task_create_group_future_common( - JobFlags flags, TaskGroup *group, const Metadata *futureResultType, - FutureAsyncSignature::FunctionType *function, - void *closureContext, bool isSpawnLetTask, - size_t initialContextSize); - -AsyncTaskAndContext -swift::swift_task_create_f(JobFlags flags, - ThinNullaryAsyncSignature::FunctionType *function, - size_t initialContextSize) { - return swift_task_create_future_f( - flags, nullptr, function, initialContextSize); -} + // Push the async let task status record. + if (asyncLet) { + asyncLet_addImpl(task, asyncLet, !hasAsyncLetResultBuffer); + } -AsyncTaskAndContext swift::swift_task_create_future_f( - JobFlags flags, - const Metadata *futureResultType, - FutureAsyncSignature::FunctionType *function, size_t initialContextSize) { - assert(!flags.task_isGroupChildTask() && - "use swift_task_create_group_future_f to initialize task group child tasks"); - return swift_task_create_group_future_f( - flags, /*group=*/nullptr, futureResultType, - function, initialContextSize); -} + // If we're supposed to enqueue the task, do so now. + if (taskCreateFlags.enqueueJob()) { + swift_retain(task); + swift_task_enqueue(task, executor); + } -AsyncTaskAndContext swift::swift_task_create_group_future_f( - JobFlags flags, TaskGroup *group, - const Metadata *futureResultType, - FutureAsyncSignature::FunctionType *function, - size_t initialContextSize) { - return swift_task_create_group_future_common(flags, group, - futureResultType, - function, nullptr, - /*isSpawnLetTask*/ false, - initialContextSize); + return {task, initialContext}; } /// Extract the entry point address and initial context size from an async closure value. @@ -603,10 +710,12 @@ getAsyncClosureEntryPointAndContextSize(void *function, fnPtr->ExpectedContextSize}; } -AsyncTaskAndContext swift::swift_task_create_future(JobFlags flags, - const Metadata *futureResultType, - void *closureEntry, - HeapObject * /* +1 */ closureContext) { +SWIFT_CC(swift) +AsyncTaskAndContext swift::swift_task_create( + size_t taskCreateFlags, + TaskOptionRecord *options, + const Metadata *futureResultType, + void *closureEntry, HeapObject *closureContext) { FutureAsyncSignature::FunctionType *taskEntry; size_t initialContextSize; std::tie(taskEntry, initialContextSize) @@ -615,256 +724,150 @@ AsyncTaskAndContext swift::swift_task_create_future(JobFlags flags, SpecialPointerAuthDiscriminators::AsyncFutureFunction >(closureEntry, closureContext); - return swift_task_create_group_future_common( - flags, nullptr, futureResultType, - taskEntry, closureContext, - /*isSpawnLetTask*/ false, - initialContextSize); -} - -AsyncTaskAndContext swift::swift_task_create_async_let_future(JobFlags flags, - const Metadata *futureResultType, - void *closureEntry, - void *closureContext) { - FutureAsyncSignature::FunctionType *taskEntry; - size_t initialContextSize; - std::tie(taskEntry, initialContextSize) - = getAsyncClosureEntryPointAndContextSize< - FutureAsyncSignature, - SpecialPointerAuthDiscriminators::AsyncFutureFunction - >(closureEntry, (HeapObject *)closureContext); - - return swift_task_create_group_future_common( - flags, nullptr, futureResultType, - taskEntry, closureContext, - /*isSpawnLetTask*/ true, + return swift_task_create_common( + taskCreateFlags, options, futureResultType, taskEntry, closureContext, initialContextSize); } -AsyncTaskAndContext -swift::swift_task_create_group_future( - JobFlags flags, TaskGroup *group, - const Metadata *futureResultType, - void *closureEntry, - HeapObject * /*+1*/closureContext) { - FutureAsyncSignature::FunctionType *taskEntry; - size_t initialContextSize; - std::tie(taskEntry, initialContextSize) - = getAsyncClosureEntryPointAndContextSize< - FutureAsyncSignature, - SpecialPointerAuthDiscriminators::AsyncFutureFunction - >(closureEntry, closureContext); - return swift_task_create_group_future_common( - flags, group, futureResultType, - taskEntry, closureContext, - /*isSpawnLetTask*/ false, - initialContextSize); +#ifdef __ARM_ARCH_7K__ +__attribute__((noinline)) +SWIFT_CC(swiftasync) static void workaround_function_swift_task_future_waitImpl( + OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncTask *task, TaskContinuationFunction resumeFunction, + AsyncContext *callContext) { + // Make sure we don't eliminate calls to this function. + asm volatile("" // Do nothing. + : // Output list, empty. + : "r"(result), "r"(callerContext), "r"(task) // Input list. + : // Clobber list, empty. + ); + return; } +#endif SWIFT_CC(swiftasync) -static void swift_task_future_waitImpl(OpaqueValue *result, - SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - AsyncTask *task, Metadata *T) { +static void swift_task_future_waitImpl( + OpaqueValue *result, + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncTask *task, + TaskContinuationFunction *resumeFn, + AsyncContext *callContext) { // Suspend the waiting task. auto waitingTask = swift_task_getCurrent(); - waitingTask->ResumeTask = rawContext->ResumeParent; - waitingTask->ResumeContext = rawContext; - - // Stash the result pointer for when we resume later. - auto context = static_cast(rawContext); - context->asyncResumeEntryPoint = nullptr; - context->successResultPointer = result; - context->errorResult = nullptr; + waitingTask->ResumeTask = task_future_wait_resume_adapter; + waitingTask->ResumeContext = callContext; // Wait on the future. assert(task->isFuture()); - switch (task->waitFuture(waitingTask)) { + switch (task->waitFuture(waitingTask, callContext, resumeFn, callerContext, + result)) { case FutureFragment::Status::Executing: // The waiting task has been queued on the future. +#ifdef __ARM_ARCH_7K__ + return workaround_function_swift_task_future_waitImpl( + result, callerContext, task, resumeFn, callContext); +#else return; +#endif - case FutureFragment::Status::Success: + case FutureFragment::Status::Success: { // Run the task with a successful result. - context->fillWithSuccess(task->futureFragment()); - // FIXME: force tail call - return waitingTask->runInFullyEstablishedContext(); + auto future = task->futureFragment(); + future->getResultType()->vw_initializeWithCopy(result, + future->getStoragePtr()); + return resumeFn(callerContext); + } case FutureFragment::Status::Error: swift_Concurrency_fatalError(0, "future reported an error, but wait cannot throw"); } } +#ifdef __ARM_ARCH_7K__ +__attribute__((noinline)) +SWIFT_CC(swiftasync) static void workaround_function_swift_task_future_wait_throwingImpl( + OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncTask *task, ThrowingTaskFutureWaitContinuationFunction resumeFunction, + AsyncContext *callContext) { + // Make sure we don't eliminate calls to this function. + asm volatile("" // Do nothing. + : // Output list, empty. + : "r"(result), "r"(callerContext), "r"(task) // Input list. + : // Clobber list, empty. + ); + return; +} +#endif + SWIFT_CC(swiftasync) void swift_task_future_wait_throwingImpl( - OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - AsyncTask *task, Metadata *T) { + OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + AsyncTask *task, + ThrowingTaskFutureWaitContinuationFunction *resumeFunction, + AsyncContext *callContext) { auto waitingTask = swift_task_getCurrent(); // Suspend the waiting task. - auto originalResumeParent = - reinterpret_cast( - rawContext->ResumeParent); waitingTask->ResumeTask = task_wait_throwing_resume_adapter; - waitingTask->ResumeContext = rawContext; + waitingTask->ResumeContext = callContext; - // Stash the result pointer for when we resume later. - auto context = static_cast(rawContext); - context->successResultPointer = result; - context->asyncResumeEntryPoint = originalResumeParent; - context->errorResult = nullptr; + auto resumeFn = reinterpret_cast(resumeFunction); // Wait on the future. assert(task->isFuture()); - switch (task->waitFuture(waitingTask)) { + switch (task->waitFuture(waitingTask, callContext, resumeFn, callerContext, + result)) { case FutureFragment::Status::Executing: // The waiting task has been queued on the future. +#ifdef __ARM_ARCH_7K__ + return workaround_function_swift_task_future_wait_throwingImpl( + result, callerContext, task, resumeFunction, callContext); +#else return; +#endif - case FutureFragment::Status::Success: - // Run the task with a successful result. - context->fillWithSuccess(task->futureFragment()); - // FIXME: force tail call - return waitingTask->runInFullyEstablishedContext(); - - case FutureFragment::Status::Error: - // Run the task with an error result. - context->fillWithError(task->futureFragment()); - // FIXME: force tail call - return waitingTask->runInFullyEstablishedContext(); - } -} - -namespace { - -#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR - -class RunAndBlockSemaphore { - bool Finished = false; -public: - void wait() { - donateThreadToGlobalExecutorUntil([](void *context) { - return *reinterpret_cast(context); - }, &Finished); - - assert(Finished && "ran out of tasks before we were signalled"); - } - - void signal() { - Finished = true; + case FutureFragment::Status::Success: { + auto future = task->futureFragment(); + future->getResultType()->vw_initializeWithCopy(result, + future->getStoragePtr()); + return resumeFunction(callerContext, nullptr /*error*/); } -}; - -#else -class RunAndBlockSemaphore { - ConditionVariable Queue; - ConditionVariable::Mutex Lock; - bool Finished = false; -public: - /// Wait for a signal. - void wait() { - Lock.withLockOrWait(Queue, [&] { - return Finished; - }); + case FutureFragment::Status::Error: { + // Run the task with an error result. + auto future = task->futureFragment(); + auto error = future->getError(); + swift_errorRetain(error); + return resumeFunction(callerContext, error); } - - void signal() { - Lock.withLockThenNotifyAll(Queue, [&]{ - Finished = true; - }); } -}; - -#endif - -using RunAndBlockSignature = - AsyncSignature; -struct RunAndBlockContext: AsyncContext { - const void *Function; - HeapObject *FunctionContext; - RunAndBlockSemaphore *Semaphore; -}; -using RunAndBlockCalleeContext = - AsyncCalleeContext; - -} // end anonymous namespace - -/// Second half of the runAndBlock async function. -SWIFT_CC(swiftasync) -static void runAndBlock_finish(SWIFT_ASYNC_CONTEXT AsyncContext *_context) { - auto calleeContext = static_cast(_context); - - auto context = popAsyncContext(calleeContext); - - context->Semaphore->signal(); - - return context->ResumeParent(context); -} - -/// First half of the runAndBlock async function. -SWIFT_CC(swiftasync) -static void runAndBlock_start(SWIFT_ASYNC_CONTEXT AsyncContext *_context, - SWIFT_CONTEXT HeapObject *closureContext) { - auto callerContext = static_cast(_context); - - RunAndBlockSignature::FunctionType *function; - size_t calleeContextSize; - auto functionContext = callerContext->FunctionContext; - assert(closureContext == functionContext); - std::tie(function, calleeContextSize) - = getAsyncClosureEntryPointAndContextSize< - RunAndBlockSignature, - SpecialPointerAuthDiscriminators::AsyncRunAndBlockFunction - >(const_cast(callerContext->Function), functionContext); - - auto calleeContext = - pushAsyncContext(callerContext, - calleeContextSize, - &runAndBlock_finish, - functionContext); - return reinterpret_cast(function)( - calleeContext, functionContext); -} - -// TODO: Remove this hack. -void swift::swift_task_runAndBlockThread(const void *function, - HeapObject *functionContext) { - RunAndBlockSemaphore semaphore; - - // Set up a task that runs the runAndBlock async function above. - auto flags = JobFlags(JobKind::Task, JobPriority::Default); - auto pair = swift_task_create_f( - flags, - reinterpret_cast( - &runAndBlock_start), - sizeof(RunAndBlockContext)); - auto context = static_cast(pair.InitialContext); - context->Function = function; - context->FunctionContext = functionContext; - context->Semaphore = &semaphore; - - // Enqueue the task. - swift_task_enqueueGlobal(pair.Task); - - // Wait until the task completes. - semaphore.wait(); } size_t swift::swift_task_getJobFlags(AsyncTask *task) { return task->Flags.getOpaqueValue(); } -AsyncTask *swift::swift_continuation_init(ContinuationAsyncContext *context, - AsyncContinuationFlags flags) { +SWIFT_CC(swift) +static AsyncTask *swift_task_suspendImpl() { + auto task = _swift_task_clearCurrent(); + task->flagAsSuspended(); + return task; +} + +SWIFT_CC(swift) +static AsyncTask *swift_continuation_initImpl(ContinuationAsyncContext *context, + AsyncContinuationFlags flags) { context->Flags = AsyncContextKind::Continuation; if (flags.canThrow()) context->Flags.setCanThrow(true); + if (flags.isExecutorSwitchForced()) + context->Flags.continuation_setIsExecutorSwitchForced(true); context->ErrorResult = nullptr; // Set the current executor as the target executor unless there's // an executor override. if (!flags.hasExecutorOverride()) - context->ResumeToExecutor = swift_task_getCurrentExecutor(); + context->ResumeToExecutor = ExecutorRef::generic(); // We can initialize this with a relaxed store because resumption // must happen-after this call. @@ -873,14 +876,82 @@ AsyncTask *swift::swift_continuation_init(ContinuationAsyncContext *context, : ContinuationStatus::Pending, std::memory_order_relaxed); - auto task = swift_task_getCurrent(); - assert(task && "initializing a continuation with no current task"); + AsyncTask *task; + + // A preawait immediately suspends the task. + if (flags.isPreawaited()) { + task = _swift_task_clearCurrent(); + assert(task && "initializing a continuation with no current task"); + task->flagAsSuspended(); + } else { + task = swift_task_getCurrent(); + assert(task && "initializing a continuation with no current task"); + } + task->ResumeContext = context; task->ResumeTask = context->ResumeParent; return task; } +SWIFT_CC(swiftasync) +static void swift_continuation_awaitImpl(ContinuationAsyncContext *context) { +#ifndef NDEBUG + auto task = swift_task_getCurrent(); + assert(task && "awaiting continuation without a task"); + assert(task->ResumeContext == context); + assert(task->ResumeTask == context->ResumeParent); +#endif + + auto &sync = context->AwaitSynchronization; + + auto oldStatus = sync.load(std::memory_order_acquire); + assert((oldStatus == ContinuationStatus::Pending || + oldStatus == ContinuationStatus::Resumed) && + "awaiting a corrupt or already-awaited continuation"); + + // If the status is already Resumed, we can resume immediately. + // Comparing against Pending may be very slightly more compact. + if (oldStatus != ContinuationStatus::Pending) { + if (context->isExecutorSwitchForced()) + return swift_task_switch(context, context->ResumeParent, + context->ResumeToExecutor); + return context->ResumeParent(context); + } + + // Load the current task (we alreaady did this in assertions builds). +#ifdef NDEBUG + auto task = swift_task_getCurrent(); +#endif + + // Flag the task as suspended. + task->flagAsSuspended(); + + // Try to transition to Awaited. + bool success = + sync.compare_exchange_strong(oldStatus, ContinuationStatus::Awaited, + /*success*/ std::memory_order_release, + /*failure*/ std::memory_order_acquire); + + // If that succeeded, we have nothing to do. + if (success) { + _swift_task_clearCurrent(); + return; + } + + // If it failed, it should be because someone concurrently resumed + // (note that the compare-exchange above is strong). + assert(oldStatus == ContinuationStatus::Resumed && + "continuation was concurrently corrupted or awaited"); + + // Restore the running state of the task and resume it. + task->flagAsRunning(); + if (context->isExecutorSwitchForced()) + return swift_task_switch(context, context->ResumeParent, + context->ResumeToExecutor); + return context->ResumeParent(context); +} + static void resumeTaskAfterContinuation(AsyncTask *task, ContinuationAsyncContext *context) { auto &sync = context->AwaitSynchronization; @@ -890,7 +961,7 @@ static void resumeTaskAfterContinuation(AsyncTask *task, // Make sure TSan knows that the resume call happens-before the task // restarting. - _swift_tsan_release(task); + _swift_tsan_release(static_cast(task)); // The status should be either Pending or Awaited. If it's Awaited, // which is probably the most likely option, then we should immediately @@ -951,7 +1022,12 @@ swift_task_addCancellationHandlerImpl( auto *record = new (allocation) CancellationNotificationStatusRecord(unsigned_handler, context); - swift_task_addStatusRecord(record); + if (swift_task_addStatusRecord(record)) + return record; + + // else, the task was already cancelled, so while the record was added, + // we must run it immediately here since no other task will trigger it. + record->run(); return record; } @@ -962,6 +1038,21 @@ static void swift_task_removeCancellationHandlerImpl( swift_task_dealloc(record); } +SWIFT_CC(swift) +static NullaryContinuationJob* +swift_task_createNullaryContinuationJobImpl( + size_t priority, + AsyncTask *continuation) { + void *allocation = + swift_task_alloc(sizeof(NullaryContinuationJob)); + auto *job = + new (allocation) NullaryContinuationJob( + swift_task_getCurrent(), static_cast(priority), + continuation); + + return job; +} + SWIFT_CC(swift) void swift::swift_continuation_logFailedCheck(const char *message) { swift_reportError(0, message); diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index b40cd61614a61..1f1bc5ad977c5 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -18,9 +18,13 @@ import Swift /// An asynchronous task (just "Task" hereafter) is the analogue of a thread for /// asynchronous functions. All asynchronous functions run as part of some task. /// -/// A task can only be interacted with by code running "in" the task, -/// by invoking the appropriate context sensitive static functions which operate -/// on the "current" task. Because all such functions are `async` they can only +/// An instance of `Task` always represents a top-level task. The instance +/// can be used to await its completion, cancel the task, etc., The task will +/// run to completion even if there are no other instances of the `Task`. +/// +/// `Task` also provides appropriate context-sensitive static functions which +/// operate on the "current" task, which might either be a detached task or +/// a child task. Because all such functions are `async` they can only /// be invoked as part of an existing task, and therefore are guaranteed to be /// effective. /// @@ -32,184 +36,93 @@ import Swift /// individually schedulable as jobs. Jobs are generally not interacted /// with by end-users directly, unless implementing a scheduler. @available(SwiftStdlib 5.5, *) -public struct Task { - // Task instances should not be used as they could be stored away, - // and sine some tasks may be task-local allocated such stored away - // references could point at already destroyed task memory (!). - // - // If necessary to obtain a task instance, please use withUnsafeCurrentTask. -} +@frozen +public struct Task: Sendable { + @usableFromInline + internal let _task: Builtin.NativeObject -// ==== Task Priority ---------------------------------------------------------- + @_alwaysEmitIntoClient + internal init(_ task: Builtin.NativeObject) { + self._task = task + } +} @available(SwiftStdlib 5.5, *) extension Task { - - /// Returns the `current` task's priority. + /// Wait for the task to complete, returning (or throwing) its result. /// - /// If no current `Task` is available, queries the system to determine the - /// priority at which the current function is running. If the system cannot - /// provide an appropriate priority, returns `Priority.default`. + /// ### Priority + /// If the task has not completed yet, its priority will be elevated to the + /// priority of the current task. Note that this may not be as effective as + /// creating the task with the "right" priority to in the first place. /// - /// - SeeAlso: `Task.Priority` - /// - SeeAlso: `Task.priority` - public static var currentPriority: Priority { - withUnsafeCurrentTask { task in - // If we are running on behalf of a task, use that task's priority. - if let task = task { - return task.priority - } - - // Otherwise, query the system. - return Task.Priority(rawValue: _getCurrentThreadPriority()) ?? .default + /// ### Cancellation + /// If the awaited on task gets cancelled externally the `get()` will throw + /// a cancellation error. + /// + /// If the task gets cancelled internally, e.g. by checking for cancellation + /// and throwing a specific error or using `checkCancellation` the error + /// thrown out of the task will be re-thrown here. + public var value: Success { + get async throws { + return try await _taskFutureGetThrowing(_task) } } - /// Task priority may inform decisions an `Executor` makes about how and when - /// to schedule tasks submitted to it. + /// Wait for the task to complete, returning (or throwing) its result. /// - /// ### Priority scheduling - /// An executor MAY utilize priority information to attempt running higher - /// priority tasks first, and then continuing to serve lower priority tasks. - /// - /// The exact semantics of how priority is treated are left up to each - /// platform and `Executor` implementation. + /// ### Priority + /// If the task has not completed yet, its priority will be elevated to the + /// priority of the current task. Note that this may not be as effective as + /// creating the task with the "right" priority to in the first place. /// - /// ### Priority inheritance - /// Child tasks automatically inherit their parent task's priority. + /// ### Cancellation + /// If the awaited on task gets cancelled externally the `get()` will throw + /// a cancellation error. /// - /// Detached tasks (created by `detach`) DO NOT inherit task priority, - /// as they are "detached" from their parent tasks after all. + /// If the task gets cancelled internally, e.g. by checking for cancellation + /// and throwing a specific error or using `checkCancellation` the error + /// thrown out of the task will be re-thrown here. + + /// Wait for the task to complete, returning its `Result`. /// - /// ### Priority elevation - /// In some situations the priority of a task must be elevated (or "escalated", "raised"): + /// ### Priority + /// If the task has not completed yet, its priority will be elevated to the + /// priority of the current task. Note that this may not be as effective as + /// creating the task with the "right" priority to in the first place. /// - /// - if a `Task` running on behalf of an actor, and a new higher-priority - /// task is enqueued to the actor, its current task must be temporarily - /// elevated to the priority of the enqueued task, in order to allow the new - /// task to be processed at--effectively-- the priority it was enqueued with. - /// - this DOES NOT affect `Task.currentPriority()`. - /// - if a task is created with a `Task.Handle`, and a higher-priority task - /// calls the `await handle.get()` function the priority of this task must be - /// permanently increased until the task completes. - /// - this DOES affect `Task.currentPriority()`. + /// ### Cancellation + /// If the awaited on task gets cancelled externally the `get()` will throw + /// a cancellation error. /// - /// TODO: Define the details of task priority; It is likely to be a concept - /// similar to Darwin Dispatch's QoS; bearing in mind that priority is not as - /// much of a thing on other platforms (i.e. server side Linux systems). - public enum Priority: Int, Comparable { - // Values must be same as defined by the internal `JobPriority`. - case userInteractive = 0x21 - case userInitiated = 0x19 - case `default` = 0x15 - case utility = 0x11 - case background = 0x09 - - @available(*, deprecated, message: "unspecified priority will be removed; use nil") - case unspecified = 0x00 - - public static func < (lhs: Priority, rhs: Priority) -> Bool { - lhs.rawValue < rhs.rawValue - } - } -} - -@available(SwiftStdlib 5.5, *) -extension Task.Priority { - /// Downgrade user-interactive to user-initiated. - var _downgradeUserInteractive: Task.Priority { - if self == .userInteractive { - return .userInitiated - } - - return self - } -} - -// ==== Task Handle ------------------------------------------------------------ - -@available(SwiftStdlib 5.5, *) -extension Task { - /// A task handle refers to an in-flight `Task`, - /// allowing for potentially awaiting for its result or Cancelling it. - /// - /// It is not a programming error to drop a handle without awaiting or cancelling it, - /// i.e. the task will run regardless of the handle still being present or not. - /// Dropping a handle however means losing the ability to await on the task's result - /// and losing the ability to cancel it. - /// - // Implementation notes: - // A task handle can ONLY be obtained for a detached task, and as such shares - // no lifetime concerns with regards to holding and storing the `_task` with - // the `Task` type, which would have also be obtainable for any task, including - // a potentially task-local allocated one. I.e. it is always safe to store away - // a Task.Handle, yet the same is not true for the "current task" which may be - // a async-let created task, at risk of getting destroyed while the reference - // lingers around. - public struct Handle: Sendable { - internal let _task: Builtin.NativeObject - - internal init(_ task: Builtin.NativeObject) { - self._task = task - } - - /// Wait for the task to complete, returning (or throwing) its result. - /// - /// ### Priority - /// If the task has not completed yet, its priority will be elevated to the - /// priority of the current task. Note that this may not be as effective as - /// creating the task with the "right" priority to in the first place. - /// - /// ### Cancellation - /// If the awaited on task gets cancelled externally the `get()` will throw - /// a cancellation error. - /// - /// If the task gets cancelled internally, e.g. by checking for cancellation - /// and throwing a specific error or using `checkCancellation` the error - /// thrown out of the task will be re-thrown here. - public func get() async throws -> Success { - return try await _taskFutureGetThrowing(_task) - } - - /// Wait for the task to complete, returning its `Result`. - /// - /// ### Priority - /// If the task has not completed yet, its priority will be elevated to the - /// priority of the current task. Note that this may not be as effective as - /// creating the task with the "right" priority to in the first place. - /// - /// ### Cancellation - /// If the awaited on task gets cancelled externally the `get()` will throw - /// a cancellation error. - /// - /// If the task gets cancelled internally, e.g. by checking for cancellation - /// and throwing a specific error or using `checkCancellation` the error - /// thrown out of the task will be re-thrown here. - public func getResult() async -> Result { + /// If the task gets cancelled internally, e.g. by checking for cancellation + /// and throwing a specific error or using `checkCancellation` the error + /// thrown out of the task will be re-thrown here. + public var result: Result { + get async { do { - return .success(try await get()) + return .success(try await value) } catch { return .failure(error as! Failure) // as!-safe, guaranteed to be Failure } } + } - /// Attempt to cancel the task. - /// - /// Whether this function has any effect is task-dependent. - /// - /// For a task to respect cancellation it must cooperatively check for it - /// while running. Many tasks will check for cancellation before beginning - /// their "actual work", however this is not a requirement nor is it guaranteed - /// how and when tasks check for cancellation in general. - public func cancel() { - Builtin.cancelAsyncTask(_task) - } + /// Attempt to cancel the task. + /// + /// Whether this function has any effect is task-dependent. + /// + /// For a task to respect cancellation it must cooperatively check for it + /// while running. Many tasks will check for cancellation before beginning + /// their "actual work", however this is not a requirement nor is it guaranteed + /// how and when tasks check for cancellation in general. + public func cancel() { + Builtin.cancelAsyncTask(_task) } } @available(SwiftStdlib 5.5, *) -extension Task.Handle where Failure == Never { - +extension Task where Failure == Never { /// Wait for the task to complete, returning its result. /// /// ### Priority @@ -218,405 +131,494 @@ extension Task.Handle where Failure == Never { /// creating the task with the "right" priority to in the first place. /// /// ### Cancellation - /// The task this handle refers to may check for cancellation, however + /// The task this refers to may check for cancellation, however /// since it is not-throwing it would have to handle it using some other /// way than throwing a `CancellationError`, e.g. it could provide a neutral /// value of the `Success` type, or encode that cancellation has occurred in /// that type itself. - public func get() async -> Success { - return await _taskFutureGet(_task) + public var value: Success { + get async { + return await _taskFutureGet(_task) + } } - } @available(SwiftStdlib 5.5, *) -extension Task.Handle: Hashable { +extension Task: Hashable { public func hash(into hasher: inout Hasher) { UnsafeRawPointer(Builtin.bridgeToRawPointer(_task)).hash(into: &hasher) } } @available(SwiftStdlib 5.5, *) -extension Task.Handle: Equatable { +extension Task: Equatable { public static func ==(lhs: Self, rhs: Self) -> Bool { UnsafeRawPointer(Builtin.bridgeToRawPointer(lhs._task)) == UnsafeRawPointer(Builtin.bridgeToRawPointer(rhs._task)) } } -// ==== Job Flags -------------------------------------------------------------- +// ==== Task Priority ---------------------------------------------------------- +/// Task priority may inform decisions an `Executor` makes about how and when +/// to schedule tasks submitted to it. +/// +/// ### Priority scheduling +/// An executor MAY utilize priority information to attempt running higher +/// priority tasks first, and then continuing to serve lower priority tasks. +/// +/// The exact semantics of how priority is treated are left up to each +/// platform and `Executor` implementation. +/// +/// ### Priority inheritance +/// Child tasks automatically inherit their parent task's priority. +/// +/// Detached tasks (created by `Task.detached`) DO NOT inherit task priority, +/// as they are "detached" from their parent tasks after all. +/// +/// ### Priority elevation +/// In some situations the priority of a task must be elevated (or "escalated", "raised"): +/// +/// - if a `Task` running on behalf of an actor, and a new higher-priority +/// task is enqueued to the actor, its current task must be temporarily +/// elevated to the priority of the enqueued task, in order to allow the new +/// task to be processed at--effectively-- the priority it was enqueued with. +/// - this DOES NOT affect `Task.currentPriority()`. +/// - if a task is created with a `Task.Handle`, and a higher-priority task +/// calls the `await handle.get()` function the priority of this task must be +/// permanently increased until the task completes. +/// - this DOES affect `Task.currentPriority()`. +/// +/// TODO: Define the details of task priority; It is likely to be a concept +/// similar to Darwin Dispatch's QoS; bearing in mind that priority is not as +/// much of a thing on other platforms (i.e. server side Linux systems). @available(SwiftStdlib 5.5, *) -extension Task { - /// Flags for schedulable jobs. +public struct TaskPriority: RawRepresentable, Sendable { + public typealias RawValue = UInt8 + public var rawValue: UInt8 + + public init(rawValue: UInt8) { + self.rawValue = rawValue + } + + public static let high: TaskPriority = .init(rawValue: 0x19) + + @_alwaysEmitIntoClient + public static var medium: TaskPriority { + .init(rawValue: 0x15) + } + + public static let low: TaskPriority = .init(rawValue: 0x11) + + public static let userInitiated: TaskPriority = high + public static let utility: TaskPriority = low + public static let background: TaskPriority = .init(rawValue: 0x09) + + @available(*, deprecated, renamed: "medium") + public static let `default`: TaskPriority = .init(rawValue: 0x15) +} + +@available(SwiftStdlib 5.5, *) +extension TaskPriority: Equatable { + public static func == (lhs: TaskPriority, rhs: TaskPriority) -> Bool { + lhs.rawValue == rhs.rawValue + } + + public static func != (lhs: TaskPriority, rhs: TaskPriority) -> Bool { + lhs.rawValue != rhs.rawValue + } +} + +@available(SwiftStdlib 5.5, *) +extension TaskPriority: Comparable { + public static func < (lhs: TaskPriority, rhs: TaskPriority) -> Bool { + lhs.rawValue < rhs.rawValue + } + + public static func <= (lhs: TaskPriority, rhs: TaskPriority) -> Bool { + lhs.rawValue <= rhs.rawValue + } + + public static func > (lhs: TaskPriority, rhs: TaskPriority) -> Bool { + lhs.rawValue > rhs.rawValue + } + + public static func >= (lhs: TaskPriority, rhs: TaskPriority) -> Bool { + lhs.rawValue >= rhs.rawValue + } +} + +@available(SwiftStdlib 5.5, *) +extension TaskPriority: Codable { } + +@available(SwiftStdlib 5.5, *) +extension Task where Success == Never, Failure == Never { + + /// Returns the `current` task's priority. + /// + /// If no current `Task` is available, queries the system to determine the + /// priority at which the current function is running. If the system cannot + /// provide an appropriate priority, returns `Priority.default`. /// - /// This is a port of the C++ FlagSet. - struct JobFlags { - /// Kinds of schedulable jobs. - enum Kind: Int { - case task = 0 + /// - SeeAlso: `TaskPriority` + public static var currentPriority: TaskPriority { + withUnsafeCurrentTask { task in + // If we are running on behalf of a task, use that task's priority. + if let task = task { + return task.priority + } + + // Otherwise, query the system. + return TaskPriority(rawValue: UInt8(_getCurrentThreadPriority())) } + } +} - /// The actual bit representation of these flags. - var bits: Int = 0 +@available(SwiftStdlib 5.5, *) +extension TaskPriority { + /// Downgrade user-interactive to user-initiated. + var _downgradeUserInteractive: TaskPriority { + return self + } +} - /// The kind of job described by these flags. - var kind: Kind { - get { - Kind(rawValue: bits & 0xFF)! - } +// ==== Job Flags -------------------------------------------------------------- - set { - bits = (bits & ~0xFF) | newValue.rawValue - } +/// Flags for schedulable jobs. +/// +/// This is a port of the C++ FlagSet. +@available(SwiftStdlib 5.5, *) +struct JobFlags { + /// Kinds of schedulable jobs. + enum Kind: Int32 { + case task = 0 + } + + /// The actual bit representation of these flags. + var bits: Int32 = 0 + + /// The kind of job described by these flags. + var kind: Kind { + get { + Kind(rawValue: bits & 0xFF)! } - /// Whether this is an asynchronous task. - var isAsyncTask: Bool { kind == .task } + set { + bits = (bits & ~0xFF) | newValue.rawValue + } + } - /// The priority given to the job. - var priority: Priority? { - get { - Priority(rawValue: (bits & 0xFF00) >> 8) - } + /// Whether this is an asynchronous task. + var isAsyncTask: Bool { kind == .task } - set { - bits = (bits & ~0xFF00) | ((newValue?.rawValue ?? 0) << 8) + /// The priority given to the job. + var priority: TaskPriority? { + get { + let value = (Int(bits) & 0xFF00) >> 8 + + if value == 0 { + return nil } + + return TaskPriority(rawValue: UInt8(value)) } - /// Whether this is a child task. - var isChildTask: Bool { - get { - (bits & (1 << 24)) != 0 - } + set { + bits = (bits & ~0xFF00) | Int32((Int(newValue?.rawValue ?? 0) << 8)) + } + } - set { - if newValue { - bits = bits | 1 << 24 - } else { - bits = (bits & ~(1 << 24)) - } - } + /// Whether this is a child task. + var isChildTask: Bool { + get { + (bits & (1 << 24)) != 0 } - /// Whether this is a future. - var isFuture: Bool { - get { - (bits & (1 << 25)) != 0 + set { + if newValue { + bits = bits | 1 << 24 + } else { + bits = (bits & ~(1 << 24)) } + } + } - set { - if newValue { - bits = bits | 1 << 25 - } else { - bits = (bits & ~(1 << 25)) - } - } + /// Whether this is a future. + var isFuture: Bool { + get { + (bits & (1 << 25)) != 0 } - /// Whether this is a group child. - var isGroupChildTask: Bool { - get { - (bits & (1 << 26)) != 0 + set { + if newValue { + bits = bits | 1 << 25 + } else { + bits = (bits & ~(1 << 25)) } + } + } - set { - if newValue { - bits = bits | 1 << 26 - } else { - bits = (bits & ~(1 << 26)) - } - } + /// Whether this is a group child. + var isGroupChildTask: Bool { + get { + (bits & (1 << 26)) != 0 } - /// Whether this is a task created by the 'async' operation, which - /// conceptually continues the work of the synchronous code that invokes - /// it. - var isContinuingAsyncTask: Bool { - get { - (bits & (1 << 27)) != 0 + set { + if newValue { + bits = bits | 1 << 26 + } else { + bits = (bits & ~(1 << 26)) } + } + } - set { - if newValue { - bits = bits | 1 << 27 - } else { - bits = (bits & ~(1 << 27)) - } + /// Whether this is a task created by the 'async' operation, which + /// conceptually continues the work of the synchronous code that invokes + /// it. + var isContinuingAsyncTask: Bool { + get { + (bits & (1 << 27)) != 0 + } + + set { + if newValue { + bits = bits | 1 << 27 + } else { + bits = (bits & ~(1 << 27)) } } } } -// ==== Detached Tasks --------------------------------------------------------- +// ==== Task Creation Flags -------------------------------------------------- +/// Form task creation flags for use with the createAsyncTask builtins. @available(SwiftStdlib 5.5, *) -extension Task { - - @discardableResult - @available(*, deprecated, message: "`Task.runDetached` was replaced by `detach` and will be removed shortly.") - public static func runDetached( - priority: Task.Priority = .unspecified, - operation: __owned @Sendable @escaping () async throws -> T - ) -> Task.Handle { - detach(priority: priority, operation: operation) +@_alwaysEmitIntoClient +func taskCreateFlags( + priority: TaskPriority?, isChildTask: Bool, copyTaskLocals: Bool, + inheritContext: Bool, enqueueJob: Bool, + addPendingGroupTaskUnconditionally: Bool +) -> Int { + var bits = 0 + bits |= (bits & ~0xFF) | Int(priority?.rawValue ?? 0) + if isChildTask { + bits |= 1 << 8 } - + if copyTaskLocals { + bits |= 1 << 10 + } + if inheritContext { + bits |= 1 << 11 + } + if enqueueJob { + bits |= 1 << 12 + } + if addPendingGroupTaskUnconditionally { + bits |= 1 << 13 + } + return bits } -/// Run given throwing `operation` as part of a new top-level task. -/// -/// Creating detached tasks should, generally, be avoided in favor of using -/// `async` functions, `async let` declarations and `await` expressions - as -/// those benefit from structured, bounded concurrency which is easier to reason -/// about, as well as automatically inheriting the parent tasks priority, -/// task-local storage, deadlines, as well as being cancelled automatically -/// when their parent task is cancelled. Detached tasks do not get any of those -/// benefits, and thus should only be used when an operation is impossible to -/// be modelled with child tasks. -/// -/// ### Cancellation -/// A detached task always runs to completion unless it is explicitly cancelled. -/// Specifically, dropping a detached tasks `Task.Handle` does _not_ automatically -/// cancel given task. -/// -/// Cancelling a task must be performed explicitly via `handle.cancel()`. -/// -/// - Note: it is generally preferable to use child tasks rather than detached -/// tasks. Child tasks automatically carry priorities, task-local state, -/// deadlines and have other benefits resulting from the structured -/// concurrency concepts that they model. Consider using detached tasks only -/// when strictly necessary and impossible to model operations otherwise. -/// -/// - Parameters: -/// - priority: priority of the task -/// - executor: the executor on which the detached closure should start -/// executing on. -/// - operation: the operation to execute -/// - Returns: handle to the task, allowing to `await handle.get()` on the -/// tasks result or `cancel` it. If the operation fails the handle will -/// throw the error the operation has thrown when awaited on. -@discardableResult -@available(SwiftStdlib 5.5, *) -public func detach( - priority: Task.Priority = .unspecified, - operation: __owned @Sendable @escaping () async -> T -) -> Task.Handle { - // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = priority - flags.isFuture = true - - // Create the asynchronous task future. - let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(task)) - - return Task.Handle(task) -} +// ==== Task Creation ---------------------------------------------------------- +@available(SwiftStdlib 5.5, *) +extension Task where Failure == Never { + /// Run given `operation` as asynchronously in its own top-level task. + /// + /// The `async` function should be used when creating asynchronous work + /// that operates on behalf of the synchronous function that calls it. + /// Like `Task.detached`, the async function creates a separate, top-level + /// task. + /// + /// Unlike `Task.detached`, the task creating by the `Task` initializer + /// inherits the priority and actor context of the caller, so the `operation` + /// is treated more like an asynchronous extension to the synchronous + /// operation. + /// + /// - Parameters: + /// - priority: priority of the task. If nil, the priority will come from + /// Task.currentPriority. + /// - operation: the operation to execute + @discardableResult + @_alwaysEmitIntoClient + public init( + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> Success + ) { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: true, + inheritContext: true, enqueueJob: true, + addPendingGroupTaskUnconditionally: false) -/// Run given throwing `operation` as part of a new top-level task. -/// -/// Creating detached tasks should, generally, be avoided in favor of using -/// `async` functions, `async let` declarations and `await` expressions - as -/// those benefit from structured, bounded concurrency which is easier to reason -/// about, as well as automatically inheriting the parent tasks priority, -/// task-local storage, deadlines, as well as being cancelled automatically -/// when their parent task is cancelled. Detached tasks do not get any of those -/// benefits, and thus should only be used when an operation is impossible to -/// be modelled with child tasks. -/// -/// ### Cancellation -/// A detached task always runs to completion unless it is explicitly cancelled. -/// Specifically, dropping a detached tasks `Task.Handle` does _not_ automatically -/// cancel given task. -/// -/// Cancelling a task must be performed explicitly via `handle.cancel()`. -/// -/// - Note: it is generally preferable to use child tasks rather than detached -/// tasks. Child tasks automatically carry priorities, task-local state, -/// deadlines and have other benefits resulting from the structured -/// concurrency concepts that they model. Consider using detached tasks only -/// when strictly necessary and impossible to model operations otherwise. -/// -/// - Parameters: -/// - priority: priority of the task -/// - executor: the executor on which the detached closure should start -/// executing on. -/// - operation: the operation to execute -/// - Returns: handle to the task, allowing to `await handle.get()` on the -/// tasks result or `cancel` it. If the operation fails the handle will -/// throw the error the operation has thrown when awaited on. -@discardableResult -@available(SwiftStdlib 5.5, *) -public func detach( - priority: Task.Priority = .unspecified, - operation: __owned @Sendable @escaping () async throws -> T -) -> Task.Handle { - // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = priority - flags.isFuture = true - - // Create the asynchronous task future. - let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(task)) - - return Task.Handle(task) -} + // Create the asynchronous task. + let (task, _) = Builtin.createAsyncTask(flags, operation) -@discardableResult -@available(SwiftStdlib 5.5, *) -public func asyncDetached( - priority: Task.Priority? = nil, - @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> T -) -> Task.Handle { - return detach(priority: priority ?? .unspecified, operation: operation) + self._task = task +#else + fatalError("Unsupported Swift compiler") +#endif + } } -@discardableResult @available(SwiftStdlib 5.5, *) -public func asyncDetached( - priority: Task.Priority? = nil, - @_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> T -) -> Task.Handle { - return detach(priority: priority ?? .unspecified, operation: operation) -} +extension Task where Failure == Error { + /// Run given `operation` as asynchronously in its own top-level task. + /// + /// This initializer creates asynchronous work on behalf of the synchronous function that calls it. + /// Like `Task.detached`, this initializer creates a separate, top-level task. + /// Unlike `Task.detached`, the task created inherits the priority and + /// actor context of the caller, so the `operation` is treated more like an + /// asynchronous extension to the synchronous operation. + /// + /// - Parameters: + /// - priority: priority of the task. If nil, the priority will come from + /// Task.currentPriority. + /// - operation: the operation to execute + @discardableResult + @_alwaysEmitIntoClient + public init( + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> Success + ) { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + // Set up the task flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: true, + inheritContext: true, enqueueJob: true, + addPendingGroupTaskUnconditionally: false + ) -/// ABI stub while we stage in the new signatures -@available(SwiftStdlib 5.5, *) -@usableFromInline -func async( - priority: Task.Priority, - @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> Void -) { - let adjustedPriority: Task.Priority? - if priority == .unspecified { - adjustedPriority = nil - } else { - adjustedPriority = priority - } - let _: Task.Handle = async(priority: adjustedPriority, operation: operation) -} + // Create the asynchronous task future. + let (task, _) = Builtin.createAsyncTask(flags, operation) -/// Run given `operation` as asynchronously in its own top-level task. -/// -/// The `async` function should be used when creating asynchronous work -/// that operates on behalf of the synchronous function that calls it. -/// Like `detach`, the async function creates a separate, top-level task. -/// Unlike `detach`, the task creating by `async` inherits the priority and -/// actor context of the caller, so the `operation` is treated more like an -/// asynchronous extension to the synchronous operation. Additionally, `async` -/// does not return a handle to refer to the task. -/// -/// - Parameters: -/// - priority: priority of the task. If nil, the priority will come from -/// Task.currentPriority. -/// - operation: the operation to execute -@available(SwiftStdlib 5.5, *) -@discardableResult -public func async( - priority: Task.Priority? = nil, - @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> T -) -> Task.Handle { - // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = priority ?? Task.currentPriority._downgradeUserInteractive - flags.isFuture = true - flags.isContinuingAsyncTask = true - - // Create the asynchronous task future. - let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(task)) - - return Task.Handle(task) + self._task = task +#else + fatalError("Unsupported Swift compiler") +#endif + } } -/// Run given `operation` as asynchronously in its own top-level task. -/// -/// The `async` function should be used when creating asynchronous work -/// that operates on behalf of the synchronous function that calls it. -/// Like `detach`, the async function creates a separate, top-level task. -/// Unlike `detach`, the task creating by `async` inherits the priority and -/// actor context of the caller, so the `operation` is treated more like an -/// asynchronous extension to the synchronous operation. Additionally, `async` -/// does not return a handle to refer to the task. -/// -/// - Parameters: -/// - priority: priority of the task. If nil, the priority will come from -/// Task.currentPriority. -/// - operation: the operation to execute -@available(SwiftStdlib 5.5, *) -@discardableResult -public func async( - priority: Task.Priority? = nil, - @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> T -) -> Task.Handle { - // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = priority ?? Task.currentPriority._downgradeUserInteractive - flags.isFuture = true - flags.isContinuingAsyncTask = true - - // Create the asynchronous task future. - let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(task)) - - return Task.Handle(task) -} +// ==== Detached Tasks --------------------------------------------------------- +@available(SwiftStdlib 5.5, *) +extension Task where Failure == Never { + /// Run given throwing `operation` as part of a new top-level task. + /// + /// Creating detached tasks should, generally, be avoided in favor of using + /// `async` functions, `async let` declarations and `await` expressions - as + /// those benefit from structured, bounded concurrency which is easier to reason + /// about, as well as automatically inheriting the parent tasks priority, + /// task-local storage, deadlines, as well as being cancelled automatically + /// when their parent task is cancelled. Detached tasks do not get any of those + /// benefits, and thus should only be used when an operation is impossible to + /// be modelled with child tasks. + /// + /// ### Cancellation + /// A detached task always runs to completion unless it is explicitly cancelled. + /// Specifically, dropping a detached tasks `Task` does _not_ automatically + /// cancel given task. + /// + /// Cancelling a task must be performed explicitly via `cancel()`. + /// + /// - Note: it is generally preferable to use child tasks rather than detached + /// tasks. Child tasks automatically carry priorities, task-local state, + /// deadlines and have other benefits resulting from the structured + /// concurrency concepts that they model. Consider using detached tasks only + /// when strictly necessary and impossible to model operations otherwise. + /// + /// - Parameters: + /// - priority: priority of the task + /// - operation: the operation to execute + /// - Returns: handle to the task, allowing to `await get()` on the + /// tasks result or `cancel` it. If the operation fails the handle will + /// throw the error the operation has thrown when awaited on. + @discardableResult + @_alwaysEmitIntoClient + public static func detached( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> Success + ) -> Task { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: false) -// ==== Async Handler ---------------------------------------------------------- + // Create the asynchronous task future. + let (task, _) = Builtin.createAsyncTask(flags, operation) -// TODO: remove this? -@available(SwiftStdlib 5.5, *) -func _runAsyncHandler(operation: @escaping () async -> ()) { - typealias ConcurrentFunctionType = @Sendable () async -> () - detach( - operation: unsafeBitCast(operation, to: ConcurrentFunctionType.self) - ) + return Task(task) +#else + fatalError("Unsupported Swift compiler") +#endif + } } -// ==== Async Sleep ------------------------------------------------------------ - @available(SwiftStdlib 5.5, *) -extension Task { - /// Suspends the current task for _at least_ the given duration - /// in nanoseconds. +extension Task where Failure == Error { + /// Run given throwing `operation` as part of a new top-level task. /// - /// This function does _not_ block the underlying thread. - public static func sleep(_ duration: UInt64) async { + /// Creating detached tasks should, generally, be avoided in favor of using + /// `async` functions, `async let` declarations and `await` expressions - as + /// those benefit from structured, bounded concurrency which is easier to reason + /// about, as well as automatically inheriting the parent tasks priority, + /// task-local storage, deadlines, as well as being cancelled automatically + /// when their parent task is cancelled. Detached tasks do not get any of those + /// benefits, and thus should only be used when an operation is impossible to + /// be modelled with child tasks. + /// + /// ### Cancellation + /// A detached task always runs to completion unless it is explicitly cancelled. + /// Specifically, dropping a detached tasks `Task.Handle` does _not_ automatically + /// cancel given task. + /// + /// Cancelling a task must be performed explicitly via `handle.cancel()`. + /// + /// - Note: it is generally preferable to use child tasks rather than detached + /// tasks. Child tasks automatically carry priorities, task-local state, + /// deadlines and have other benefits resulting from the structured + /// concurrency concepts that they model. Consider using detached tasks only + /// when strictly necessary and impossible to model operations otherwise. + /// + /// - Parameters: + /// - priority: priority of the task + /// - executor: the executor on which the detached closure should start + /// executing on. + /// - operation: the operation to execute + /// - Returns: handle to the task, allowing to `await handle.get()` on the + /// tasks result or `cancel` it. If the operation fails the handle will + /// throw the error the operation has thrown when awaited on. + @discardableResult + @_alwaysEmitIntoClient + public static func detached( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> Success + ) -> Task { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = .default - flags.isFuture = true + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: false + ) // Create the asynchronous task future. - let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, {}) + let (task, _) = Builtin.createAsyncTask(flags, operation) - // Enqueue the resulting job. - _enqueueJobGlobalWithDelay(duration, Builtin.convertTaskToJob(task)) - - await Handle(task).get() + return Task(task) +#else + fatalError("Unsupported Swift compiler") +#endif } } // ==== Voluntary Suspension ----------------------------------------------------- @available(SwiftStdlib 5.5, *) -extension Task { +extension Task where Success == Never, Failure == Never { /// Explicitly suspend the current task, potentially giving up execution actor /// of current actor/task, allowing other tasks to execute. @@ -624,44 +626,23 @@ extension Task { /// This is not a perfect cure for starvation; /// if the task is the highest-priority task in the system, it might go /// immediately back to executing. + /// + /// If this task is the highest-priority task in the system, + /// the executor immediately resumes execution of the same task. + /// As such, + /// this method isn't necessarily a way to avoid resource starvation. public static func yield() async { - // Prepare the job flags - var flags = JobFlags() - flags.kind = .task - flags.priority = .default - flags.isFuture = true - - // Create the asynchronous task future, it will do nothing, but simply serves - // as a way for us to yield our execution until the executor gets to it and - // resumes us. - // TODO: consider if it would be useful for this task to be a child task - let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, {}) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(task)) - - let _ = await Handle(task).get() + return await Builtin.withUnsafeContinuation { (continuation: Builtin.RawUnsafeContinuation) -> Void in + let job = _taskCreateNullaryContinuationJob( + priority: Int(Task.currentPriority.rawValue), + continuation: continuation) + _enqueueJobGlobal(job) + } } } // ==== UnsafeCurrentTask ------------------------------------------------------ -@available(SwiftStdlib 5.5, *) -extension Task { - - @available(*, deprecated, message: "`Task.unsafeCurrent` was replaced by `withUnsafeCurrentTask { task in ... }`, and will be removed soon.") - public static var unsafeCurrent: UnsafeCurrentTask? { // TODO: remove as soon as possible - guard let _task = _getCurrentAsyncTask() else { - return nil - } - // FIXME: This retain seems pretty wrong, however if we don't we WILL crash - // with "destroying a task that never completed" in the task's destroy. - // How do we solve this properly? - Builtin.retain(_task) - return UnsafeCurrentTask(_task) - } -} - /// Calls the given closure with the with the "current" task in which this /// function was invoked. /// @@ -723,12 +704,17 @@ public struct UnsafeCurrentTask { /// Returns the `current` task's priority. /// - /// - SeeAlso: `Task.Priority` + /// - SeeAlso: `TaskPriority` /// - SeeAlso: `Task.currentPriority` - public var priority: Task.Priority { - getJobFlags(_task).priority ?? .default + public var priority: TaskPriority { + getJobFlags(_task).priority ?? TaskPriority( + rawValue: UInt8(_getCurrentThreadPriority())) } + /// Cancel the current task. + public func cancel() { + _taskCancel(_task) + } } @available(SwiftStdlib 5.5, *) @@ -747,14 +733,13 @@ extension UnsafeCurrentTask: Equatable { } // ==== Internal --------------------------------------------------------------- - @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_getCurrent") func _getCurrentAsyncTask() -> Builtin.NativeObject? @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_getJobFlags") -func getJobFlags(_ task: Builtin.NativeObject) -> Task.JobFlags +func getJobFlags(_ task: Builtin.NativeObject) -> JobFlags @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_enqueueGlobal") @@ -771,31 +756,22 @@ func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: Builtin.Job) public func _asyncMainDrainQueue() -> Never @available(SwiftStdlib 5.5, *) -public func _runAsyncMain(_ asyncFun: @escaping () async throws -> ()) { -#if os(Windows) - detach { +public func _runAsyncMain(@_unsafeSendable _ asyncFun: @escaping () async throws -> ()) { + Task.detached { do { - try await asyncFun() - exit(0) - } catch { - _errorInMain(error) - } - } +#if !os(Windows) +#if compiler(>=5.5) && $BuiltinHopToActor + Builtin.hopToActor(MainActor.shared) #else - @MainActor @Sendable - func _doMain(_ asyncFun: @escaping () async throws -> ()) async { - do { + fatalError("Swift compiler is incompatible with this SDK version") +#endif +#endif try await asyncFun() + exit(0) } catch { _errorInMain(error) } } - - detach { - await _doMain(asyncFun) - exit(0) - } -#endif _asyncMainDrainQueue() } @@ -818,6 +794,10 @@ func _taskCancel(_ task: Builtin.NativeObject) @_silgen_name("swift_task_isCancelled") func _taskIsCancelled(_ task: Builtin.NativeObject) -> Bool +@available(SwiftStdlib 5.5, *) +@_silgen_name("swift_task_createNullaryContinuationJob") +func _taskCreateNullaryContinuationJob(priority: Int, continuation: Builtin.RawUnsafeContinuation) -> Builtin.Job + @available(SwiftStdlib 5.5, *) @usableFromInline @_silgen_name("swift_task_isCurrentExecutor") @@ -843,10 +823,8 @@ func _getCurrentThreadPriority() -> Int @available(SwiftStdlib 5.5, *) @_alwaysEmitIntoClient @usableFromInline -internal func _runTaskForBridgedAsyncMethod(_ body: @escaping () async -> Void) { -#if compiler(>=5.5) && $Sendable - async { await body() } -#endif +internal func _runTaskForBridgedAsyncMethod(@_inheritActorContext _ body: __owned @Sendable @escaping () async -> Void) { + Task(operation: body) } #endif diff --git a/stdlib/public/Concurrency/TaskAlloc.cpp b/stdlib/public/Concurrency/TaskAlloc.cpp index 3f22ec2e9ec6e..af5a7a5ac9f13 100644 --- a/stdlib/public/Concurrency/TaskAlloc.cpp +++ b/stdlib/public/Concurrency/TaskAlloc.cpp @@ -20,23 +20,13 @@ #include "swift/Runtime/Concurrency.h" #include "swift/ABI/Task.h" #include "TaskPrivate.h" -#include "Error.h" -#define SWIFT_FATAL_ERROR swift_Concurrency_fatalError -#include "../runtime/StackAllocator.h" #include using namespace swift; namespace { -/// The size of an allocator slab. -/// -/// TODO: find the optimal value by experiment. -static constexpr size_t SlabCapacity = 1024; - -using TaskAllocator = StackAllocator; - struct GlobalAllocator { TaskAllocator allocator; void *spaceForFirstSlab[64]; @@ -44,25 +34,11 @@ struct GlobalAllocator { GlobalAllocator() : allocator(spaceForFirstSlab, sizeof(spaceForFirstSlab)) {} }; -static_assert(alignof(TaskAllocator) <= alignof(decltype(AsyncTask::AllocatorPrivate)), - "task allocator must not be more aligned than " - "allocator-private slot"); - } // end anonymous namespace -void swift::_swift_task_alloc_initialize(AsyncTask *task) { - new (task->AllocatorPrivate) TaskAllocator(); -} - -void swift::_swift_task_alloc_initialize_with_slab(AsyncTask *task, - void *firstSlabBuffer, - size_t bufferCapacity) { - new (task->AllocatorPrivate) TaskAllocator(firstSlabBuffer, bufferCapacity); -} - static TaskAllocator &allocator(AsyncTask *task) { if (task) - return reinterpret_cast(task->AllocatorPrivate); + return task->Private.get().Allocator; // FIXME: this fall-back shouldn't be necessary, but it's useful // for now, since the current execution tests aren't setting up a task @@ -71,10 +47,6 @@ static TaskAllocator &allocator(AsyncTask *task) { return global.allocator; } -void swift::_swift_task_alloc_destroy(AsyncTask *task) { - allocator(task).~TaskAllocator(); -} - void *swift::swift_task_alloc(size_t size) { return allocator(swift_task_getCurrent()).alloc(size); } diff --git a/stdlib/public/Concurrency/TaskCancellation.swift b/stdlib/public/Concurrency/TaskCancellation.swift index 68fb876f61bae..15755ddb9ab39 100644 --- a/stdlib/public/Concurrency/TaskCancellation.swift +++ b/stdlib/public/Concurrency/TaskCancellation.swift @@ -29,17 +29,11 @@ import Swift /// This function returns instantly and will never suspend. @available(SwiftStdlib 5.5, *) public func withTaskCancellationHandler( - handler: @Sendable () -> (), - operation: () async throws -> T + operation: () async throws -> T, + onCancel handler: @Sendable () -> Void ) async rethrows -> T { - let task = Builtin.getCurrentAsyncTask() - - guard !_taskIsCancelled(task) else { - // If the current task is already cancelled, run the handler immediately. - handler() - return try await operation() - } - + // unconditionally add the cancellation record to the task. + // if the task was already cancelled, it will be executed right away. let record = _taskAddCancellationHandler(handler: handler) defer { _taskRemoveCancellationHandler(record: record) } @@ -48,23 +42,9 @@ public func withTaskCancellationHandler( @available(SwiftStdlib 5.5, *) extension Task { - - /// Returns `true` if the task is cancelled, and should stop executing. - /// - /// If no current `Task` is available, returns `false`, as outside of a task - /// context no task cancellation may be observed. - /// - /// - SeeAlso: `checkCancellation()` - public static var isCancelled: Bool { - withUnsafeCurrentTask { task in - task?.isCancelled ?? false - } - } - /// Returns `true` if the task is cancelled, and should stop executing. /// /// - SeeAlso: `checkCancellation()` - @available(*, deprecated, message: "Storing `Task` instances has been deprecated and will be removed soon. Use the static 'Task.isCancelled' instead.") public var isCancelled: Bool { withUnsafeCurrentTask { task in guard let task = task else { @@ -74,7 +54,25 @@ extension Task { return _taskIsCancelled(task._task) } } +} +@available(SwiftStdlib 5.5, *) +extension Task where Success == Never, Failure == Never { + /// Returns `true` if the task is cancelled, and should stop executing. + /// + /// If no current `Task` is available, returns `false`, as outside of a task + /// context no task cancellation may be observed. + /// + /// - SeeAlso: `checkCancellation()` + public static var isCancelled: Bool { + withUnsafeCurrentTask { task in + task?.isCancelled ?? false + } + } +} + +@available(SwiftStdlib 5.5, *) +extension Task where Success == Never, Failure == Never { /// Check if the task is cancelled and throw an `CancellationError` if it was. /// /// It is intentional that no information is passed to the task about why it @@ -90,32 +88,25 @@ extension Task { /// /// - SeeAlso: `isCancelled()` public static func checkCancellation() throws { - if Task.isCancelled { - throw CancellationError() + if Task.isCancelled { + throw _Concurrency.CancellationError() } } +} - @available(*, deprecated, message: "`Task.withCancellationHandler` has been replaced by `withTaskCancellationHandler` and will be removed shortly.") - public static func withCancellationHandler( - handler: @Sendable () -> (), - operation: () async throws -> T - ) async rethrows -> T { - try await withTaskCancellationHandler(handler: handler, operation: operation) - } - - /// The default cancellation thrown when a task is cancelled. - /// - /// This error is also thrown automatically by `Task.checkCancellation()`, - /// if the current task has been cancelled. - public struct CancellationError: Error { - // no extra information, cancellation is intended to be light-weight - public init() {} - } +/// The default cancellation thrown when a task is cancelled. +/// +/// This error is also thrown automatically by `Task.checkCancellation()`, +/// if the current task has been cancelled. +@available(SwiftStdlib 5.5, *) +public struct CancellationError: Error { + // no extra information, cancellation is intended to be light-weight + public init() {} } @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_addCancellationHandler") -func _taskAddCancellationHandler(handler: @Sendable () -> ()) -> UnsafeRawPointer /*CancellationNotificationStatusRecord*/ +func _taskAddCancellationHandler(handler: () -> Void) -> UnsafeRawPointer /*CancellationNotificationStatusRecord*/ @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_removeCancellationHandler") diff --git a/stdlib/public/Concurrency/TaskGroup.cpp b/stdlib/public/Concurrency/TaskGroup.cpp index 4501916bdc1f0..e2ce5c0951ad2 100644 --- a/stdlib/public/Concurrency/TaskGroup.cpp +++ b/stdlib/public/Concurrency/TaskGroup.cpp @@ -28,14 +28,15 @@ #include "swift/Runtime/Config.h" #include "swift/Runtime/Mutex.h" #include "swift/Runtime/HeapObject.h" -#include "AsyncCall.h" #include "Debug.h" #include "bitset" #include "string" #include "queue" // TODO: remove and replace with usage of our mpsc queue #include #include +#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR #include +#endif #if !defined(_WIN32) #include @@ -100,6 +101,8 @@ class TaskGroupImpl: public TaskGroupTaskStatusRecord { /// object itself. OpaqueValue *storage; + const Metadata *successType; + /// The completed task, if necessary to keep alive until consumed by next(). /// /// # Important: swift_release @@ -124,6 +127,7 @@ class TaskGroupImpl: public TaskGroupTaskStatusRecord { /*storage*/ hadErrorResult ? reinterpret_cast(fragment->getError()) : fragment->getStoragePtr(), + /*successType*/fragment->getResultType(), /*task*/ asyncTask }; } @@ -292,15 +296,17 @@ class TaskGroupImpl: public TaskGroupTaskStatusRecord { /// or `nullptr` if no task is currently waiting. std::atomic waitQueue; - friend class AsyncTask; + const Metadata *successType; + + friend class ::swift::AsyncTask; public: - explicit TaskGroupImpl() + explicit TaskGroupImpl(const Metadata *T) : TaskGroupTaskStatusRecord(), status(GroupStatus::initial().status), readyQueue(), // readyQueue(ReadyQueueItem::get(ReadyStatus::Empty, nullptr)), - waitQueue(nullptr) {} + waitQueue(nullptr), successType(T) {} TaskGroupTaskStatusRecord *getTaskRecord() { @@ -451,8 +457,8 @@ static TaskGroup *asAbstract(TaskGroupImpl *group) { // Initializes into the preallocated _group an actual TaskGroupImpl. SWIFT_CC(swift) -static void swift_taskGroup_initializeImpl(TaskGroup *group) { - TaskGroupImpl *impl = new (group) TaskGroupImpl(); +static void swift_taskGroup_initializeImpl(TaskGroup *group, const Metadata *T) { + TaskGroupImpl *impl = new (group) TaskGroupImpl(T); auto record = impl->getTaskRecord(); assert(impl == record && "the group IS the task record"); @@ -503,7 +509,11 @@ void TaskGroup::offer(AsyncTask *completedTask, AsyncContext *context) { asImpl(this)->offer(completedTask, context); } -static void fillGroupNextResult(TaskGroupNextAsyncContext *context, +bool TaskGroup::isCancelled() { + return asImpl(this)->isCancelled(); +} + +static void fillGroupNextResult(TaskFutureWaitAsyncContext *context, PollResult result) { /// Fill in the result value switch (result.status) { @@ -517,7 +527,7 @@ static void fillGroupNextResult(TaskGroupNextAsyncContext *context, case PollStatus::Success: { // Initialize the result as an Optional. - const Metadata *successType = context->successType; + const Metadata *successType = result.successType; OpaqueValue *destPtr = context->successResultPointer; // TODO: figure out a way to try to optimistically take the // value out of the finished task's future, if there are no @@ -529,7 +539,7 @@ static void fillGroupNextResult(TaskGroupNextAsyncContext *context, case PollStatus::Empty: { // Initialize the result as a nil Optional. - const Metadata *successType = context->successType; + const Metadata *successType = result.successType; OpaqueValue *destPtr = context->successResultPointer; successType->vw_storeEnumTagSinglePayload(destPtr, 1, 1); return; @@ -593,7 +603,7 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) { mutex.unlock(); // TODO: remove fragment lock, and use status for synchronization auto waitingContext = - static_cast( + static_cast( waitingTask->ResumeContext); fillGroupNextResult(waitingContext, result); @@ -633,31 +643,49 @@ SWIFT_CC(swiftasync) static void task_group_wait_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) { - auto context = static_cast(_context); - return context->asyncResumeEntryPoint(_context, context->errorResult); + auto context = static_cast(_context); + auto resumeWithError = + reinterpret_cast(context->ResumeParent); + return resumeWithError(context->Parent, context->errorResult); +} + +#ifdef __ARM_ARCH_7K__ +__attribute__((noinline)) +SWIFT_CC(swiftasync) static void workaround_function_swift_taskGroup_wait_next_throwingImpl( + OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + TaskGroup *_group, + ThrowingTaskFutureWaitContinuationFunction resumeFunction, + AsyncContext *callContext) { + // Make sure we don't eliminate calls to this function. + asm volatile("" // Do nothing. + : // Output list, empty. + : "r"(result), "r"(callerContext), "r"(_group) // Input list. + : // Clobber list, empty. + ); + return; } +#endif // ============================================================================= // ==== group.next() implementation (wait_next and groupPoll) ------------------ SWIFT_CC(swiftasync) static void swift_taskGroup_wait_next_throwingImpl( - OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *rawContext, - TaskGroup *_group, const Metadata *successType) { + OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, + TaskGroup *_group, + ThrowingTaskFutureWaitContinuationFunction *resumeFunction, + AsyncContext *rawContext) { auto waitingTask = swift_task_getCurrent(); - auto originalResumeParent = - reinterpret_cast( - rawContext->ResumeParent); waitingTask->ResumeTask = task_group_wait_resume_adapter; waitingTask->ResumeContext = rawContext; - auto context = static_cast(rawContext); + auto context = static_cast(rawContext); + context->ResumeParent = + reinterpret_cast(resumeFunction); + context->Parent = callerContext; context->errorResult = nullptr; - context->asyncResumeEntryPoint = originalResumeParent; context->successResultPointer = resultPointer; - context->group = _group; - context->successType = successType; - auto group = asImpl(context->group); + auto group = asImpl(_group); assert(group && "swift_taskGroup_wait_next_throwing was passed context without group!"); PollResult polled = group->poll(waitingTask); @@ -665,7 +693,12 @@ static void swift_taskGroup_wait_next_throwingImpl( case PollStatus::MustWait: // The waiting task has been queued on the channel, // there were pending tasks so it will be woken up eventually. +#ifdef __ARM_ARCH_7K__ + return workaround_function_swift_taskGroup_wait_next_throwingImpl( + resultPointer, callerContext, _group, resumeFunction, rawContext); +#else return; +#endif case PollStatus::Empty: case PollStatus::Error: @@ -681,6 +714,7 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) { PollResult result; result.storage = nullptr; + result.successType = nullptr; result.retainedTask = nullptr; // ==== 1) bail out early if no tasks are pending ---------------------------- @@ -690,10 +724,14 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) { // Bail out and return `nil` from `group.next()`. statusRemoveWaiting(); result.status = PollStatus::Empty; + result.successType = this->successType; mutex.unlock(); // TODO: remove group lock, and use status for synchronization return result; } + // Have we suspended the task? + bool hasSuspended = false; + auto waitHead = waitQueue.load(std::memory_order_acquire); // ==== 2) Ready task was polled, return with it immediately ----------------- @@ -708,18 +746,18 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) { // Success! We are allowed to poll. ReadyQueueItem item; bool taskDequeued = readyQueue.dequeue(item); - if (!taskDequeued) { - result.status = PollStatus::MustWait; - result.storage = nullptr; - result.retainedTask = nullptr; - mutex.unlock(); // TODO: remove group lock, and use status for synchronization - return result; + assert(taskDequeued); (void) taskDequeued; + + // We're going back to running the task, so if we suspended before, + // we need to flag it as running again. + if (hasSuspended) { + waitingTask->flagAsRunning(); } assert(item.getTask()->isFuture()); auto futureFragment = item.getTask()->futureFragment(); - // Store the task in the result, so after we're done processing it it may + // Store the task in the result, so after we're done processing it may // be swift_release'd; we kept it alive while it was in the readyQueue by // an additional retain issued as we enqueued it there. result.retainedTask = item.getTask(); @@ -728,6 +766,7 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) { // Immediately return the polled value result.status = PollStatus::Success; result.storage = futureFragment->getStoragePtr(); + result.successType = futureFragment->getResultType(); assert(result.retainedTask && "polled a task, it must be not null"); _swift_tsan_acquire(static_cast(result.retainedTask)); mutex.unlock(); // TODO: remove fragment lock, and use status for synchronization @@ -738,6 +777,7 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) { result.status = PollStatus::Error; result.storage = reinterpret_cast(futureFragment->getError()); + result.successType = nullptr; assert(result.retainedTask && "polled a task, it must be not null"); _swift_tsan_acquire(static_cast(result.retainedTask)); mutex.unlock(); // TODO: remove fragment lock, and use status for synchronization @@ -747,6 +787,7 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) { result.status = PollStatus::Empty; result.storage = nullptr; result.retainedTask = nullptr; + result.successType = this->successType; mutex.unlock(); // TODO: remove fragment lock, and use status for synchronization return result; } @@ -758,6 +799,10 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) { assert(assumed.readyTasks() == 0); _swift_tsan_release(static_cast(waitingTask)); while (true) { + if (!hasSuspended) { + hasSuspended = true; + waitingTask->flagAsSuspended(); + } // Put the waiting task at the beginning of the wait queue. if (waitQueue.compare_exchange_weak( waitHead, waitingTask, @@ -766,10 +811,10 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) { mutex.unlock(); // TODO: remove fragment lock, and use status for synchronization // no ready tasks, so we must wait. result.status = PollStatus::MustWait; + _swift_task_clearCurrent(); return result; } // else, try again } - assert(false && "must successfully compare exchange the waiting task."); } // ============================================================================= diff --git a/stdlib/public/Concurrency/TaskGroup.swift b/stdlib/public/Concurrency/TaskGroup.swift index 2b9aefd7fe169..36fb049520bd2 100644 --- a/stdlib/public/Concurrency/TaskGroup.swift +++ b/stdlib/public/Concurrency/TaskGroup.swift @@ -15,24 +15,6 @@ import Swift // ==== TaskGroup -------------------------------------------------------------- -@available(SwiftStdlib 5.5, *) -extension Task { - @available(*, deprecated, message: "`Task.Group` was replaced by `ThrowingTaskGroup` and `TaskGroup` and will be removed shortly.") - public typealias Group = ThrowingTaskGroup - - @available(*, deprecated, message: "`Task.withGroup` was replaced by `withThrowingTaskGroup` and `withTaskGroup` and will be removed shortly.") - public static func withGroup( - resultType: TaskResult.Type, - returning returnType: BodyResult.Type = BodyResult.self, - body: (inout Task.Group) async throws -> BodyResult - ) async rethrows -> BodyResult { - try await withThrowingTaskGroup(of: resultType) { group in - try await body(&group) - } - } -} - - /// Starts a new task group which provides a scope in which a dynamic number of /// tasks may be spawned. /// @@ -86,9 +68,9 @@ public func withTaskGroup( returning returnType: GroupResult.Type = GroupResult.self, body: (inout TaskGroup) async -> GroupResult ) async -> GroupResult { - #if compiler(>=5.5) && $BuiltinTaskGroup + #if compiler(>=5.5) && $BuiltinTaskGroupWithArgument - let _group = Builtin.createTaskGroup() + let _group = Builtin.createTaskGroup(ChildTaskResult.self) var group = TaskGroup(group: _group) // Run the withTaskGroup body. @@ -165,9 +147,9 @@ public func withThrowingTaskGroup( returning returnType: GroupResult.Type = GroupResult.self, body: (inout ThrowingTaskGroup) async throws -> GroupResult ) async rethrows -> GroupResult { - #if compiler(>=5.5) && $BuiltinTaskGroup + #if compiler(>=5.5) && $BuiltinTaskGroupWithArgument - let _group = Builtin.createTaskGroup() + let _group = Builtin.createTaskGroup(ChildTaskResult.self) var group = ThrowingTaskGroup(group: _group) do { @@ -197,7 +179,7 @@ public func withThrowingTaskGroup( /// It is created by the `withTaskGroup` function. @available(SwiftStdlib 5.5, *) @frozen -public struct TaskGroup { +public struct TaskGroup { /// Group task into which child tasks offer their results, /// and the `next()` function polls those results from. @@ -210,26 +192,6 @@ public struct TaskGroup { self._group = group } - @available(*, deprecated, message: "`Task.Group.add` has been replaced by `TaskGroup.spawn` or `TaskGroup.spawnUnlessCancelled` and will be removed shortly.") - public mutating func add( - priority: Task.Priority = .unspecified, - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) async -> Bool { - return self.spawnUnlessCancelled(priority: priority) { - await operation() - } - } - - // Historical entry point, maintained for ABI compatibility - @usableFromInline - mutating func spawn( - priority: Task.Priority, - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) { - let optPriority: Task.Priority? = priority - spawn(priority: optPriority, operation: operation) - } - /// Add a child task to the group. /// /// ### Error handling @@ -245,39 +207,23 @@ public struct TaskGroup { /// - Returns: /// - `true` if the operation was added to the group successfully, /// `false` otherwise (e.g. because the group `isCancelled`) - public mutating func spawn( - priority: Task.Priority? = nil, + @_alwaysEmitIntoClient + public mutating func addTask( + priority: TaskPriority? = nil, operation: __owned @Sendable @escaping () async -> ChildTaskResult ) { - _ = _taskGroupAddPendingTask(group: _group, unconditionally: true) - - // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = priority - flags.isFuture = true - flags.isChildTask = true - flags.isGroupChildTask = true - - // Create the asynchronous task future. - let (childTask, _) = Builtin.createAsyncTaskGroupFuture( - flags.bits, _group, operation) - - // Attach it to the group's task record in the current task. - _taskGroupAttachChild(group: _group, child: childTask) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(childTask)) - } - - // Historical entry point, maintained for ABI compatibility - @usableFromInline - mutating func spawnUnlessCancelled( - priority: Task.Priority = .unspecified, - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) -> Bool { - let optPriority: Task.Priority? = priority - return spawnUnlessCancelled(priority: optPriority, operation: operation) +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + let flags = taskCreateFlags( + priority: priority, isChildTask: true, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: true + ) + + // Create the task in this group. + _ = Builtin.createAsyncTaskInGroup(flags, _group, operation) +#else + fatalError("Unsupported Swift compiler") +#endif } /// Add a child task to the group. @@ -295,10 +241,12 @@ public struct TaskGroup { /// - Returns: /// - `true` if the operation was added to the group successfully, /// `false` otherwise (e.g. because the group `isCancelled`) - public mutating func spawnUnlessCancelled( - priority: Task.Priority? = nil, + @_alwaysEmitIntoClient + public mutating func addTaskUnlessCancelled( + priority: TaskPriority? = nil, operation: __owned @Sendable @escaping () async -> ChildTaskResult ) -> Bool { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) guard canAdd else { @@ -306,25 +254,19 @@ public struct TaskGroup { return false } - // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = priority - flags.isFuture = true - flags.isChildTask = true - flags.isGroupChildTask = true + let flags = taskCreateFlags( + priority: priority, isChildTask: true, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: false + ) - // Create the asynchronous task future. - let (childTask, _) = Builtin.createAsyncTaskGroupFuture( - flags.bits, _group, operation) - - // Attach it to the group's task record in the current task. - _taskGroupAttachChild(group: _group, child: childTask) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(childTask)) + // Create the task in this group. + _ = Builtin.createAsyncTaskInGroup(flags, _group, operation) return true +#else + fatalError("Unsupported Swift compiler") +#endif } /// Wait for the a child task that was added to the group to complete, @@ -391,7 +333,14 @@ public struct TaskGroup { internal mutating func awaitAllRemainingTasks() async { while let _ = await next() {} } - + + /// Wait for all remaining tasks in the task group to complete before + /// returning. + @_alwaysEmitIntoClient + public mutating func waitForAll() async { + await awaitAllRemainingTasks() + } + /// Query whether the group has any remaining tasks. /// /// Task groups are always empty upon entry to the `withTaskGroup` body, and @@ -403,12 +352,12 @@ public struct TaskGroup { _taskGroupIsEmpty(_group) } - /// Cancel all the remaining tasks in the group. + /// Cancel all the remaining, and future, tasks in the group. /// - /// A cancelled group will not will NOT accept new tasks being added into it. - /// - /// Any results, including errors thrown by tasks affected by this - /// cancellation, are silently discarded. + /// A cancelled group will not will create new tasks when the `asyncUnlessCancelled`, + /// function is used. It will, however, continue to create tasks when the plain `async` + /// function is used. Such tasks will be created yet immediately cancelled, allowing + /// the tasks to perform some short-cut implementation, if they are responsive to cancellation. /// /// This function may be called even from within child (or any other) tasks, /// and will reliably cause the group to become cancelled. @@ -457,7 +406,7 @@ public struct TaskGroup { /// It is created by the `withTaskGroup` function. @available(SwiftStdlib 5.5, *) @frozen -public struct ThrowingTaskGroup { +public struct ThrowingTaskGroup { /// Group task into which child tasks offer their results, /// and the `next()` function polls those results from. @@ -482,24 +431,16 @@ public struct ThrowingTaskGroup { } } - @available(*, deprecated, message: "`Task.Group.add` has been replaced by `(Throwing)TaskGroup.spawn` or `(Throwing)TaskGroup.spawnUnlessCancelled` and will be removed shortly.") - public mutating func add( - priority: Task.Priority = .unspecified, - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) async -> Bool { - return self.spawnUnlessCancelled(priority: priority) { - try await operation() - } + @usableFromInline + internal mutating func _waitForAll() async throws { + while let _ = try await next() { } } - // Historical entry point for ABI reasons - @usableFromInline - mutating func spawn( - priority: Task.Priority = .unspecified, - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) { - let optPriority: Task.Priority? = priority - return spawn(priority: optPriority, operation: operation) + /// Wait for all remaining tasks in the task group to complete before + /// returning. + @_alwaysEmitIntoClient + public mutating func waitForAll() async throws { + while let _ = try await next() { } } /// Spawn, unconditionally, a child task in the group. @@ -517,40 +458,23 @@ public struct ThrowingTaskGroup { /// - Returns: /// - `true` if the operation was added to the group successfully, /// `false` otherwise (e.g. because the group `isCancelled`) - public mutating func spawn( - priority: Task.Priority? = nil, + @_alwaysEmitIntoClient + public mutating func addTask( + priority: TaskPriority? = nil, operation: __owned @Sendable @escaping () async throws -> ChildTaskResult ) { - // we always add, so no need to check if group was cancelled - _ = _taskGroupAddPendingTask(group: _group, unconditionally: true) - - // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = priority - flags.isFuture = true - flags.isChildTask = true - flags.isGroupChildTask = true - - // Create the asynchronous task future. - let (childTask, _) = Builtin.createAsyncTaskGroupFuture( - flags.bits, _group, operation) - - // Attach it to the group's task record in the current task. - _taskGroupAttachChild(group: _group, child: childTask) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(childTask)) - } - - // Historical entry point for ABI reasons - @usableFromInline - mutating func spawnUnlessCancelled( - priority: Task.Priority, - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) -> Bool { - let optPriority: Task.Priority? = priority - return spawnUnlessCancelled(priority: optPriority, operation: operation) +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + let flags = taskCreateFlags( + priority: priority, isChildTask: true, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: true + ) + + // Create the task in this group. + _ = Builtin.createAsyncTaskInGroup(flags, _group, operation) +#else + fatalError("Unsupported Swift compiler") +#endif } /// Add a child task to the group. @@ -568,10 +492,12 @@ public struct ThrowingTaskGroup { /// - Returns: /// - `true` if the operation was added to the group successfully, /// `false` otherwise (e.g. because the group `isCancelled`) - public mutating func spawnUnlessCancelled( - priority: Task.Priority? = nil, + @_alwaysEmitIntoClient + public mutating func addTaskUnlessCancelled( + priority: TaskPriority? = nil, operation: __owned @Sendable @escaping () async throws -> ChildTaskResult ) -> Bool { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) guard canAdd else { @@ -579,25 +505,19 @@ public struct ThrowingTaskGroup { return false } - // Set up the job flags for a new task. - var flags = Task.JobFlags() - flags.kind = .task - flags.priority = priority - flags.isFuture = true - flags.isChildTask = true - flags.isGroupChildTask = true - - // Create the asynchronous task future. - let (childTask, _) = Builtin.createAsyncTaskGroupFuture( - flags.bits, _group, operation) + let flags = taskCreateFlags( + priority: priority, isChildTask: true, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: false + ) - // Attach it to the group's task record in the current task. - _taskGroupAttachChild(group: _group, child: childTask) - - // Enqueue the resulting job. - _enqueueJobGlobal(Builtin.convertTaskToJob(childTask)) + // Create the task in this group. + _ = Builtin.createAsyncTaskInGroup(flags, _group, operation) return true +#else + fatalError("Unsupported Swift compiler") +#endif } /// Wait for the a child task that was added to the group to complete, @@ -657,8 +577,9 @@ public struct ThrowingTaskGroup { return try await _taskGroupWaitNext(group: _group) } - /// - SeeAlso: `next()` - public mutating func nextResult() async throws -> Result? { + @_silgen_name("$sScg10nextResults0B0Oyxq_GSgyYaKF") + @usableFromInline + mutating func nextResultForABI() async throws -> Result? { do { guard let success: ChildTaskResult = try await _taskGroupWaitNext(group: _group) else { return nil @@ -670,6 +591,12 @@ public struct ThrowingTaskGroup { } } + /// - SeeAlso: `next()` + @_alwaysEmitIntoClient + public mutating func nextResult() async -> Result? { + return try! await nextResultForABI() + } + /// Query whether the group has any remaining tasks. /// /// Task groups are always empty upon entry to the `withTaskGroup` body, and @@ -824,20 +751,13 @@ extension ThrowingTaskGroup: AsyncSequence { /// ==== ----------------------------------------------------------------------- -/// Attach task group child to the group group to the task. -@available(SwiftStdlib 5.5, *) -@_silgen_name("swift_taskGroup_attachChild") -func _taskGroupAttachChild( - group: Builtin.RawPointer, - child: Builtin.NativeObject -) - @available(SwiftStdlib 5.5, *) @_silgen_name("swift_taskGroup_destroy") func _taskGroupDestroy(group: __owned Builtin.RawPointer) @available(SwiftStdlib 5.5, *) @_silgen_name("swift_taskGroup_addPending") +@usableFromInline func _taskGroupAddPendingTask( group: Builtin.RawPointer, unconditionally: Bool diff --git a/stdlib/public/Concurrency/TaskLocal.cpp b/stdlib/public/Concurrency/TaskLocal.cpp index 1ef4ea51d0fbc..9715d0f589df1 100644 --- a/stdlib/public/Concurrency/TaskLocal.cpp +++ b/stdlib/public/Concurrency/TaskLocal.cpp @@ -11,14 +11,20 @@ //===----------------------------------------------------------------------===// #include "../CompatibilityOverride/CompatibilityOverride.h" -#include "swift/ABI/TaskLocal.h" -#include "swift/ABI/Actor.h" -#include "swift/ABI/Task.h" -#include "swift/ABI/Metadata.h" +#include "swift/Runtime/Atomic.h" +#include "swift/Runtime/Casting.h" #include "swift/Runtime/Once.h" #include "swift/Runtime/Mutex.h" #include "swift/Runtime/Concurrency.h" +#include "swift/Runtime/ThreadLocal.h" +#include "swift/Runtime/ThreadLocalStorage.h" +#include "swift/ABI/TaskLocal.h" +#include "swift/ABI/Task.h" +#include "swift/ABI/Actor.h" +#include "swift/ABI/Metadata.h" +#include "llvm/ADT/PointerIntPair.h" #include "TaskPrivate.h" +#include #if defined(__APPLE__) #include @@ -26,32 +32,123 @@ #include #endif +#if HAVE_PTHREAD_H +#include +#endif + #if defined(_WIN32) #include +#include +#include #endif using namespace swift; // ============================================================================= + +/// An extremely silly class which exists to make pointer +/// default-initialization constexpr. +template struct Pointer { + T *Value; + constexpr Pointer() : Value(nullptr) {} + constexpr Pointer(T *value) : Value(value) {} + operator T *() const { return Value; } + T *operator->() const { return Value; } +}; + +/// THIS IS RUNTIME INTERNAL AND NOT ABI. +class FallbackTaskLocalStorage { + static SWIFT_RUNTIME_DECLARE_THREAD_LOCAL( + Pointer, Value, + SWIFT_CONCURRENCY_FALLBACK_TASK_LOCAL_STORAGE_KEY); + +public: + static void set(TaskLocal::Storage *task) { Value.set(task); } + static TaskLocal::Storage *get() { return Value.get(); } +}; + +/// Define the thread-locals. +SWIFT_RUNTIME_DECLARE_THREAD_LOCAL( + Pointer, FallbackTaskLocalStorage::Value, + SWIFT_CONCURRENCY_FALLBACK_TASK_LOCAL_STORAGE_KEY); + // ==== ABI -------------------------------------------------------------------- SWIFT_CC(swift) -static void swift_task_localValuePushImpl(AsyncTask *task, - const HeapObject *key, - /* +1 */ OpaqueValue *value, - const Metadata *valueType) { - task->localValuePush(key, value, valueType); +static void swift_task_localValuePushImpl(const HeapObject *key, + /* +1 */ OpaqueValue *value, + const Metadata *valueType) { + if (AsyncTask *task = swift_task_getCurrent()) { + task->localValuePush(key, value, valueType); + return; + } + + // no AsyncTask available so we must check the fallback + TaskLocal::Storage *Local = nullptr; + if (auto storage = FallbackTaskLocalStorage::get()) { + Local = storage; + } else { + void *allocation = malloc(sizeof(TaskLocal::Storage)); + auto *freshStorage = new(allocation) TaskLocal::Storage(); + + FallbackTaskLocalStorage::set(freshStorage); + Local = freshStorage; + } + + Local->pushValue(/*task=*/nullptr, key, value, valueType); } SWIFT_CC(swift) -static OpaqueValue* swift_task_localValueGetImpl(AsyncTask *task, - const HeapObject *key) { - return task->localValueGet(key); +static OpaqueValue* swift_task_localValueGetImpl(const HeapObject *key) { + if (AsyncTask *task = swift_task_getCurrent()) { + // we're in the context of a task and can use the task's storage + return task->localValueGet(key); + } + + // no AsyncTask available so we must check the fallback + if (auto Local = FallbackTaskLocalStorage::get()) { + return Local->getValue(/*task*/nullptr, key); + } + + // no value found in task-local or fallback thread-local storage. + return nullptr; } SWIFT_CC(swift) -static void swift_task_localValuePopImpl(AsyncTask *task) { - task->localValuePop(); +static void swift_task_localValuePopImpl() { + if (AsyncTask *task = swift_task_getCurrent()) { + task->localValuePop(); + return; + } + + if (TaskLocal::Storage *Local = FallbackTaskLocalStorage::get()) { + bool hasRemainingBindings = Local->popValue(nullptr); + if (!hasRemainingBindings) { + // We clean up eagerly, it may be that this non-swift-concurrency thread + // never again will use task-locals, and as such we better remove the storage. + FallbackTaskLocalStorage::set(nullptr); + free(Local); + } + return; + } + + assert(false && "Attempted to pop value but no task or thread-local storage available!"); +} + +SWIFT_CC(swift) +static void swift_task_localsCopyToImpl(AsyncTask *task) { + TaskLocal::Storage *Local = nullptr; + + if (AsyncTask *task = swift_task_getCurrent()) { + Local = &task->_private().Local; + } else if (auto *storage = FallbackTaskLocalStorage::get()) { + Local = storage; + } else { + // bail out, there are no values to copy + return; + } + + Local->copyTo(task); } // ============================================================================= @@ -67,12 +164,10 @@ void TaskLocal::Storage::initializeLinkParent(AsyncTask* task, TaskLocal::Item* TaskLocal::Item::createParentLink(AsyncTask *task, AsyncTask *parent) { size_t amountToAllocate = Item::itemSize(/*valueType*/nullptr); - // assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this? void *allocation = _swift_task_alloc_specific(task, amountToAllocate); Item *item = new(allocation) Item(); - // FIXME: parent pointer must to be the parent STORAGE not just the next item. - auto parentHead = parent->Local.head; + auto parentHead = parent->_private().Local.head; if (parentHead) { if (parentHead->isEmpty()) { switch (parentHead->getNextLinkType()) { @@ -105,6 +200,45 @@ TaskLocal::Item::createParentLink(AsyncTask *task, AsyncTask *parent) { return item; } +TaskLocal::Item* +TaskLocal::Item::createLink(AsyncTask *task, + const HeapObject *key, + const Metadata *valueType) { + size_t amountToAllocate = Item::itemSize(valueType); + void *allocation = task ? _swift_task_alloc_specific(task, amountToAllocate) + : malloc(amountToAllocate); + Item *item = new (allocation) Item(key, valueType); + + auto next = task ? task->_private().Local.head + : FallbackTaskLocalStorage::get()->head; + item->next = reinterpret_cast(next) | + static_cast(NextLinkType::IsNext); + + return item; +} + + +void TaskLocal::Item::copyTo(AsyncTask *target) { + assert(target && "TaskLocal item attempt to copy to null target task!"); + + // 'parent' pointers are signified by null valueType. + // We must not copy parent pointers, but rather perform a deep copy of all values, + // as such, we skip parent pointers here entirely. + if (isParentPointer()) + return; + + auto item = Item::createLink(target, key, valueType); + valueType->vw_initializeWithCopy(item->getStoragePtr(), getStoragePtr()); + + /// A `copyTo` may ONLY be invoked BEFORE the task is actually scheduled, + /// so right now we can safely copy the value into the task without additional + /// synchronization. + target->_private().Local.head = item; +} + +// ============================================================================= +// ==== checks ----------------------------------------------------------------- + SWIFT_CC(swift) static void swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroupImpl( const unsigned char *file, uintptr_t fileLength, @@ -177,25 +311,6 @@ static void swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroupImpl( abort(); } -TaskLocal::Item* -TaskLocal::Item::createLink(AsyncTask *task, - const HeapObject *key, - const Metadata *valueType) { - assert(task); - - size_t amountToAllocate = Item::itemSize(valueType); - // assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this? - - void *allocation = _swift_task_alloc_specific(task, amountToAllocate); - Item *item = new(allocation) Item(key, valueType); - - auto next = task->Local.head; - item->next = reinterpret_cast(next) | - static_cast(NextLinkType::IsNext); - - return item; -} - // ============================================================================= // ==== destroy ---------------------------------------------------------------- @@ -205,7 +320,10 @@ void TaskLocal::Item::destroy(AsyncTask *task) { valueType->vw_destroy(getStoragePtr()); } - _swift_task_dealloc_specific(task, this); + // if task is available, we must have used the task allocator to allocate this item, + // so we must deallocate it using the same. Otherwise, we must have used malloc. + if (task) _swift_task_dealloc_specific(task, this); + else free(this); } void TaskLocal::Storage::destroy(AsyncTask *task) { @@ -231,7 +349,7 @@ void TaskLocal::Storage::destroy(AsyncTask *task) { } // ============================================================================= -// ==== push / pop / get ------------------------------------------------------- +// ==== Task Local Storage: operations ----------------------------------------- void TaskLocal::Storage::pushValue(AsyncTask *task, const HeapObject *key, @@ -244,11 +362,14 @@ void TaskLocal::Storage::pushValue(AsyncTask *task, head = item; } -void TaskLocal::Storage::popValue(AsyncTask *task) { +bool TaskLocal::Storage::popValue(AsyncTask *task) { assert(head && "attempted to pop value off empty task-local stack"); auto old = head; head = head->getNext(); old->destroy(task); + + /// if pointing at not-null next item, there are remaining bindings. + return head != nullptr; } OpaqueValue* TaskLocal::Storage::getValue(AsyncTask *task, @@ -267,5 +388,30 @@ OpaqueValue* TaskLocal::Storage::getValue(AsyncTask *task, return nullptr; } + +void TaskLocal::Storage::copyTo(AsyncTask *target) { + assert(target && "task must not be null when copying values into it"); + assert(!(target->_private().Local.head) && + "Task must not have any task-local values bound before copying into it"); + + // Set of keys for which we already have copied to the new task. + // We only ever need to copy the *first* encounter of any given key, + // because it is the most "specific"/"recent" binding and any other binding + // of a key does not matter for the target task as it will never be able to + // observe it. + std::set copied = {}; + + auto item = head; + while (item) { + // we only have to copy an item if it is the most recent binding of a key. + // i.e. if we've already seen an item for this key, we can skip it. + if (copied.emplace(item->key).second) { + item->copyTo(target); + } + + item = item->getNext(); + } +} + #define OVERRIDE_TASK_LOCAL COMPATIBILITY_OVERRIDE #include COMPATIBILITY_OVERRIDE_INCLUDE_PATH diff --git a/stdlib/public/Concurrency/TaskLocal.swift b/stdlib/public/Concurrency/TaskLocal.swift index a2686b334bc83..44a5948bb3173 100644 --- a/stdlib/public/Concurrency/TaskLocal.swift +++ b/stdlib/public/Concurrency/TaskLocal.swift @@ -64,8 +64,8 @@ import Swift /// are created within that scope. /// /// Detached tasks do not inherit task-local values, however tasks created using -/// the `async {}` operation do inherit task-locals by copying them to the new -/// asynchronous task, even though it is an un-structured task. +/// the `Task { ... }` initializer do inherit task-locals by copying them to the +/// new asynchronous task, even though it is an un-structured task. /// /// ### Examples /// @@ -78,16 +78,15 @@ import Swift /// print("traceID: \(traceID)") // traceID: 1234 /// call() // traceID: 1234 /// -/// asyncDetached { // detached tasks do not inherit task-local values -/// call() // traceID: nil +/// Task { // unstructured tasks do inherit task locals by copying +/// call() // traceID: 1234 /// } /// -/// async { // async tasks do inherit task locals by copying -/// call() // traceID: 1234 +/// Task.detached { // detached tasks do not inherit task-local values +/// call() // traceID: nil /// } /// } /// -/// /// func call() { /// print("traceID: \(traceID)") // 1234 /// } @@ -96,8 +95,7 @@ import Swift /// value for lookups in the task local storage. @propertyWrapper @available(SwiftStdlib 5.5, *) -// TODO: add Sendable enforcement when we're ready to do so rdar://77441933 -public final class TaskLocal: UnsafeSendable, CustomStringConvertible { +public final class TaskLocal: Sendable, CustomStringConvertible { let defaultValue: Value public init(wrappedValue defaultValue: Value) { @@ -114,25 +112,17 @@ public final class TaskLocal: UnsafeSendable, CustomStringConvertible { /// or if the task-local has no value bound, this will return the `defaultValue` /// of the task local. public func get() -> Value { - withUnsafeCurrentTask { task in - guard let task = task else { - return self.defaultValue - } - - let value = _taskLocalValueGet(task._task, key: key) - - guard let rawValue = value else { - return self.defaultValue - } - - // Take the value; The type should be correct by construction - let storagePtr = - rawValue.bindMemory(to: Value.self, capacity: 1) - return UnsafeMutablePointer(mutating: storagePtr).pointee + guard let rawValue = _taskLocalValueGet(key: key) else { + return self.defaultValue } + + // Take the value; The type should be correct by construction + let storagePtr = + rawValue.bindMemory(to: Value.self, capacity: 1) + return UnsafeMutablePointer(mutating: storagePtr).pointee } - /// Binds the task-local to the specific value for the duration of the operation. + /// Binds the task-local to the specific value for the duration of the asynchronous operation. /// /// The value is available throughout the execution of the operation closure, /// including any `get` operations performed by child-tasks created during the @@ -150,18 +140,37 @@ public final class TaskLocal: UnsafeSendable, CustomStringConvertible { // check if we're not trying to bind a value from an illegal context; this may crash _checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line) - // we need to escape the `_task` since the withUnsafeCurrentTask closure is not `async`. - // this is safe, since we know the task will remain alive because we are running inside of it. - let _task = withUnsafeCurrentTask { task in - task!._task // !-safe, guaranteed to have task available inside async function - } - - _taskLocalValuePush(_task, key: key, value: valueDuringOperation) - defer { _taskLocalValuePop(_task) } + _taskLocalValuePush(key: key, value: valueDuringOperation) + defer { _taskLocalValuePop() } return try await operation() } + /// Binds the task-local to the specific value for the duration of the + /// synchronous operation. + /// + /// The value is available throughout the execution of the operation closure, + /// including any `get` operations performed by child-tasks created during the + /// execution of the operation closure. + /// + /// If the same task-local is bound multiple times, be it in the same task, or + /// in specific child tasks, the "more specific" binding is returned when the + /// value is read. + /// + /// If the value is a reference type, it will be retained for the duration of + /// the operation closure. + @discardableResult + public func withValue(_ valueDuringOperation: Value, operation: () throws -> R, + file: String = #file, line: UInt = #line) rethrows -> R { + // check if we're not trying to bind a value from an illegal context; this may crash + _checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line) + + _taskLocalValuePush(key: key, value: valueDuringOperation) + defer { _taskLocalValuePop() } + + return try operation() + } + public var projectedValue: TaskLocal { get { self @@ -179,13 +188,14 @@ public final class TaskLocal: UnsafeSendable, CustomStringConvertible { // the type-checker that this property-wrapper never wants to have an enclosing // instance (it is impossible to declare a property wrapper inside the `Never` // type). + @available(*, unavailable, message: "property wrappers cannot be instance members") public static subscript( _enclosingInstance object: Never, wrapped wrappedKeyPath: ReferenceWritableKeyPath, storage storageKeyPath: ReferenceWritableKeyPath> ) -> Value { get { - fatalError() + fatalError("Will never be executed, since enclosing instance is Never") } } @@ -199,53 +209,31 @@ public final class TaskLocal: UnsafeSendable, CustomStringConvertible { } -@available(SwiftStdlib 5.5, *) -extension UnsafeCurrentTask { - - /// Allows for executing a synchronous `operation` while binding a task-local value - /// in the current task. - /// - /// This function MUST NOT be invoked by any other task than the current task - /// represented by this object. - @discardableResult - public func withTaskLocal( - _ taskLocal: TaskLocal, - boundTo valueDuringOperation: Value, - operation: () throws -> R, - file: String = #file, line: UInt = #line) rethrows -> R { - // check if we're not trying to bind a value from an illegal context; this may crash - _checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line) - - _taskLocalValuePush(self._task, key: taskLocal.key, value: valueDuringOperation) - defer { _taskLocalValuePop(_task) } - - return try operation() - } -} - // ==== ------------------------------------------------------------------------ @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_localValuePush") -public func _taskLocalValuePush( - _ task: Builtin.NativeObject, +func _taskLocalValuePush( key: Builtin.RawPointer/*: Key*/, value: __owned Value ) // where Key: TaskLocal @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_localValuePop") -public func _taskLocalValuePop( - _ task: Builtin.NativeObject -) +func _taskLocalValuePop() @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_localValueGet") -public func _taskLocalValueGet( - _ task: Builtin.NativeObject, +func _taskLocalValueGet( key: Builtin.RawPointer/*Key*/ ) -> UnsafeMutableRawPointer? // where Key: TaskLocal +@available(SwiftStdlib 5.5, *) +@_silgen_name("swift_task_localsCopyTo") +func _taskLocalsCopy( + to target: Builtin.NativeObject +) + // ==== Checks ----------------------------------------------------------------- @available(SwiftStdlib 5.5, *) diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index a77e713f77b3c..a605819e92133 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -17,29 +17,55 @@ #ifndef SWIFT_CONCURRENCY_TASKPRIVATE_H #define SWIFT_CONCURRENCY_TASKPRIVATE_H -#include "swift/Runtime/Concurrency.h" -#include "swift/ABI/Task.h" +#include "Error.h" #include "swift/ABI/Metadata.h" -#include "swift/Runtime/HeapObject.h" +#include "swift/ABI/Task.h" +#include "swift/Runtime/Atomic.h" +#include "swift/Runtime/Concurrency.h" #include "swift/Runtime/Error.h" +#include "swift/Runtime/Exclusivity.h" +#include "swift/Runtime/HeapObject.h" -namespace swift { +#define SWIFT_FATAL_ERROR swift_Concurrency_fatalError +#include "../runtime/StackAllocator.h" + +#if HAVE_PTHREAD_H +#include +#endif +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRA_LEAN +#define NOMINMAX +#include +#endif -// Uncomment to enable helpful debug spew to stderr -//#define SWIFT_TASK_PRINTF_DEBUG 1 +namespace swift { -class AsyncTask; -class TaskGroup; +// Set to 1 to enable helpful debug spew to stderr +#if 0 +#define SWIFT_TASK_DEBUG_LOG(fmt, ...) \ + fprintf(stderr, "[%lu] " fmt "\n", (unsigned long)_swift_get_thread_id(), \ + __VA_ARGS__) +#else +#define SWIFT_TASK_DEBUG_LOG(fmt, ...) (void)0 +#endif -/// Initialize the task-local allocator in the given task. -void _swift_task_alloc_initialize(AsyncTask *task); +#if defined(_WIN32) +using ThreadID = decltype(GetCurrentThreadId()); +#else +using ThreadID = decltype(pthread_self()); +#endif -void _swift_task_alloc_initialize_with_slab(AsyncTask *task, - void *firstSlabBuffer, - size_t bufferCapacity); +inline ThreadID _swift_get_thread_id() { +#if defined(_WIN32) + return GetCurrentThreadId(); +#else + return pthread_self(); +#endif +} -/// Destroy the task-local allocator in the given task. -void _swift_task_alloc_destroy(AsyncTask *task); +class AsyncTask; +class TaskGroup; /// Allocate task-local memory on behalf of a specific task, /// not necessarily the current one. Generally this should only be @@ -56,14 +82,13 @@ void _swift_task_dealloc_specific(AsyncTask *task, void *ptr); /// related to the active task. void runJobInEstablishedExecutorContext(Job *job); +/// Initialize the async let storage for the given async-let child task. +void asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet, + bool didAllocateInParentTask); + /// Clear the active task reference for the current thread. AsyncTask *_swift_task_clearCurrent(); -AsyncTaskAndContext swift_task_create_async_let_future(JobFlags flags, - const Metadata *futureResultType, - void *closureEntry, - void *closureContext); - #if defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) #define SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR 1 #else @@ -86,15 +111,15 @@ void _swift_tsan_release(void *addr); /// Special values used with DispatchQueueIndex to indicate the global and main /// executors. #define DISPATCH_QUEUE_GLOBAL_EXECUTOR (void *)1 -#define DISPATCH_QUEUE_MAIN_EXECUTOR (void *)2 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" -// FIXME: remove this and switch to a representation that uses -// _dispatch_main_q somehow -extern "C" SWIFT_CC(swift) -ExecutorRef _swift_task_getMainExecutor(); -#pragma clang diagnostic pop +#if !defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) +inline SerialExecutorWitnessTable * +_swift_task_getDispatchQueueSerialExecutorWitnessTable() { + extern SerialExecutorWitnessTable wtable + SWIFT_ASM_LABEL_WITH_PREFIX("$ss17DispatchQueueShimCScfsWP"); + return &wtable; +} +#endif // ==== ------------------------------------------------------------------------ @@ -114,15 +139,15 @@ namespace { /// @_silgen_name("swift_asyncLet_waitThrowing") /// func _asyncLetGetThrowing(_ task: Builtin.RawPointer) async throws -> T /// +/// @_silgen_name("swift_taskGroup_wait_next_throwing") +/// func _taskGroupWaitNext(group: Builtin.RawPointer) async throws -> T? +/// class TaskFutureWaitAsyncContext : public AsyncContext { public: SwiftError *errorResult; OpaqueValue *successResultPointer; - AsyncVoidClosureResumeEntryPoint *__ptrauth_swift_task_resume_function - asyncResumeEntryPoint; - void fillWithSuccess(AsyncTask::FutureFragment *future) { fillWithSuccess(future->getStoragePtr(), future->getResultType(), successResultPointer); @@ -141,36 +166,270 @@ class TaskFutureWaitAsyncContext : public AsyncContext { } }; -/// The layout of a frame to call the following function: -/// -/// @_silgen_name("swift_taskGroup_wait_next_throwing") -/// func _taskGroupWaitNext(group: Builtin.RawPointer) async throws -> T? -/// -class TaskGroupNextAsyncContext : public AsyncContext { +} // end anonymous namespace + +/// The current state of a task's status records. +class alignas(sizeof(void*) * 2) ActiveTaskStatus { + enum : uintptr_t { + /// The current running priority of the task. + PriorityMask = 0xFF, + + /// Has the task been cancelled? + IsCancelled = 0x100, + + /// Whether the task status is "locked", meaning that further + /// accesses need to wait on the task status record lock + IsLocked = 0x200, + + /// Whether the running priority has been escalated above the + /// priority recorded in the Job header. + IsEscalated = 0x400, + + /// Whether the task is actively running. + /// We don't really need to be tracking this in the runtime right + /// now, but we will need to eventually track enough information to + /// escalate the thread that's running a task, so doing the stores + /// necessary to maintain this gives us a more realistic baseline + /// for performance. + IsRunning = 0x800, + }; + + TaskStatusRecord *Record; + uintptr_t Flags; + + ActiveTaskStatus(TaskStatusRecord *record, uintptr_t flags) + : Record(record), Flags(flags) {} + public: - SwiftError *errorResult; +#ifdef __GLIBCXX__ + /// We really don't want to provide this constructor, but in old + /// versions of libstdc++, std::atomic::load incorrectly requires + /// the type to be default-constructible. + ActiveTaskStatus() = default; +#endif - OpaqueValue *successResultPointer; + constexpr ActiveTaskStatus(JobFlags flags) + : Record(nullptr), Flags(uintptr_t(flags.getPriority())) {} - AsyncVoidClosureResumeEntryPoint *__ptrauth_swift_task_resume_function - asyncResumeEntryPoint; + /// Is the task currently cancelled? + bool isCancelled() const { return Flags & IsCancelled; } + ActiveTaskStatus withCancelled() const { + return ActiveTaskStatus(Record, Flags | IsCancelled); + } - // Arguments. - TaskGroup *group; + /// Is the task currently running? + /// Eventually we'll track this with more specificity, like whether + /// it's running on a specific thread, enqueued on a specific actor, + /// etc. + bool isRunning() const { return Flags & IsRunning; } + ActiveTaskStatus withRunning(bool isRunning) const { + return ActiveTaskStatus(Record, isRunning ? (Flags | IsRunning) + : (Flags & ~IsRunning)); + } - const Metadata *successType; + /// Is there an active lock on the cancellation information? + bool isLocked() const { return Flags & IsLocked; } + ActiveTaskStatus withLockingRecord(TaskStatusRecord *lockRecord) const { + assert(!isLocked()); + assert(lockRecord->Parent == Record); + return ActiveTaskStatus(lockRecord, Flags | IsLocked); + } - void fillWithSuccess(OpaqueValue *src, const Metadata *successType) { - successType->vw_initializeWithCopy(successResultPointer, src); + JobPriority getStoredPriority() const { + return JobPriority(Flags & PriorityMask); + } + bool isStoredPriorityEscalated() const { + return Flags & IsEscalated; + } + ActiveTaskStatus withEscalatedPriority(JobPriority priority) const { + assert(priority > getStoredPriority()); + return ActiveTaskStatus(Record, + (Flags & ~PriorityMask) + | IsEscalated | uintptr_t(priority)); + } + ActiveTaskStatus withoutStoredPriorityEscalation() const { + assert(isStoredPriorityEscalated()); + return ActiveTaskStatus(Record, Flags & ~IsEscalated); } - void fillWithError(SwiftError *error) { - errorResult = error; - swift_errorRetain(error); + /// Return the innermost cancellation record. Code running + /// asynchronously with this task should not access this record + /// without having first locked it; see swift_taskCancel. + TaskStatusRecord *getInnermostRecord() const { + return Record; + } + ActiveTaskStatus withInnermostRecord(TaskStatusRecord *newRecord) { + return ActiveTaskStatus(newRecord, Flags); + } + + static TaskStatusRecord *getStatusRecordParent(TaskStatusRecord *ptr); + + using record_iterator = + LinkedListIterator; + llvm::iterator_range records() const { + return record_iterator::rangeBeginning(getInnermostRecord()); } }; -} // end anonymous namespace +/// The size of an allocator slab. +static constexpr size_t SlabCapacity = 1000; + +using TaskAllocator = StackAllocator; + +/// Private storage in an AsyncTask object. +struct AsyncTask::PrivateStorage { + /// The currently-active information about cancellation. + /// Currently two words. + swift::atomic Status; + + /// The allocator for the task stack. + /// Currently 2 words + 8 bytes. + TaskAllocator Allocator; + + /// Storage for task-local values. + /// Currently one word. + TaskLocal::Storage Local; + + /// State inside the AsyncTask whose state is only managed by the exclusivity + /// runtime in stdlibCore. We zero initialize to provide a safe initial value, + /// but actually initialize its bit state to a const global provided by + /// libswiftCore so that libswiftCore can control the layout of our initial + /// state. + uintptr_t ExclusivityAccessSet[2] = {0, 0}; + + PrivateStorage(JobFlags flags) + : Status(ActiveTaskStatus(flags)), Local(TaskLocal::Storage()) {} + + PrivateStorage(JobFlags flags, void *slab, size_t slabCapacity) + : Status(ActiveTaskStatus(flags)), Allocator(slab, slabCapacity), + Local(TaskLocal::Storage()) {} + + void complete(AsyncTask *task) { + // Destroy and deallocate any remaining task local items. + // We need to do this before we destroy the task local deallocator. + Local.destroy(task); + + this->~PrivateStorage(); + } +}; + +static_assert(sizeof(AsyncTask::PrivateStorage) + <= sizeof(AsyncTask::OpaquePrivateStorage) && + alignof(AsyncTask::PrivateStorage) + <= alignof(AsyncTask::OpaquePrivateStorage), + "Task-private storage doesn't fit in reserved space"); + +inline AsyncTask::PrivateStorage & +AsyncTask::OpaquePrivateStorage::get() { + return reinterpret_cast(*this); +} +inline const AsyncTask::PrivateStorage & +AsyncTask::OpaquePrivateStorage::get() const { + return reinterpret_cast(*this); +} +inline void AsyncTask::OpaquePrivateStorage::initialize(AsyncTask *task) { + new (this) PrivateStorage(task->Flags); +} +inline void +AsyncTask::OpaquePrivateStorage::initializeWithSlab(AsyncTask *task, + void *slab, + size_t slabCapacity) { + new (this) PrivateStorage(task->Flags, slab, slabCapacity); +} +inline void AsyncTask::OpaquePrivateStorage::complete(AsyncTask *task) { + get().complete(task); +} +inline void AsyncTask::OpaquePrivateStorage::destroy() { + // nothing else to do +} + +inline AsyncTask::PrivateStorage &AsyncTask::_private() { + return Private.get(); +} +inline const AsyncTask::PrivateStorage &AsyncTask::_private() const { + return Private.get(); +} + +inline bool AsyncTask::isCancelled() const { + return _private().Status.load(std::memory_order_relaxed) + .isCancelled(); +} + +inline void AsyncTask::flagAsRunning() { + auto oldStatus = _private().Status.load(std::memory_order_relaxed); + while (true) { + assert(!oldStatus.isRunning()); + if (oldStatus.isLocked()) { + flagAsRunning_slow(); + swift_task_enterThreadLocalContext( + (char *)&_private().ExclusivityAccessSet[0]); + return; + } + + auto newStatus = oldStatus.withRunning(true); + if (newStatus.isStoredPriorityEscalated()) { + newStatus = newStatus.withoutStoredPriorityEscalation(); + Flags.setPriority(oldStatus.getStoredPriority()); + } + + if (_private().Status.compare_exchange_weak(oldStatus, newStatus, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + swift_task_enterThreadLocalContext( + (char *)&_private().ExclusivityAccessSet[0]); + return; + } + } +} + +inline void AsyncTask::flagAsSuspended() { + auto oldStatus = _private().Status.load(std::memory_order_relaxed); + while (true) { + assert(oldStatus.isRunning()); + if (oldStatus.isLocked()) { + flagAsSuspended_slow(); + swift_task_exitThreadLocalContext( + (char *)&_private().ExclusivityAccessSet[0]); + return; + } + + auto newStatus = oldStatus.withRunning(false); + if (newStatus.isStoredPriorityEscalated()) { + newStatus = newStatus.withoutStoredPriorityEscalation(); + Flags.setPriority(oldStatus.getStoredPriority()); + } + + if (_private().Status.compare_exchange_weak(oldStatus, newStatus, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + swift_task_exitThreadLocalContext( + (char *)&_private().ExclusivityAccessSet[0]); + return; + } + } +} + +// READ ME: This is not a dead function! Do not remove it! This is a function +// that can be used when debugging locally to instrument when a task actually is +// dealloced. +inline void AsyncTask::flagAsCompleted() { + SWIFT_TASK_DEBUG_LOG("task completed %p", this); +} + +inline void AsyncTask::localValuePush(const HeapObject *key, + /* +1 */ OpaqueValue *value, + const Metadata *valueType) { + _private().Local.pushValue(this, key, value, valueType); +} + +inline OpaqueValue *AsyncTask::localValueGet(const HeapObject *key) { + return _private().Local.getValue(this, key); +} + +/// Returns true if storage has still more bindings. +inline bool AsyncTask::localValuePop() { + return _private().Local.popValue(this); +} } // end namespace swift diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift new file mode 100644 index 0000000000000..cc2a0ff7b36d1 --- /dev/null +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -0,0 +1,290 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 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 +// +//===----------------------------------------------------------------------===// +import Swift +@_implementationOnly import _SwiftConcurrencyShims + +@available(SwiftStdlib 5.5, *) +extension Task where Success == Never, Failure == Never { + @available(*, deprecated, renamed: "Task.sleep(nanoseconds:)") + public static func sleep(_ duration: UInt64) async { + return await Builtin.withUnsafeContinuation { (continuation: Builtin.RawUnsafeContinuation) -> Void in + let job = _taskCreateNullaryContinuationJob( + priority: Int(Task.currentPriority.rawValue), + continuation: continuation) + _enqueueJobGlobalWithDelay(duration, job) + } + } + + /// The type of continuation used in the implementation of + /// sleep(nanoseconds:). + private typealias SleepContinuation = UnsafeContinuation<(), Error> + + /// Describes the state of a sleep() operation. + private enum SleepState { + /// The sleep continuation has not yet begun. + case notStarted + + // The sleep continuation has been created and is available here. + case activeContinuation(SleepContinuation) + + /// The sleep has finished. + case finished + + /// The sleep was cancelled. + case cancelled + + /// The sleep was cancelled before it even got started. + case cancelledBeforeStarted + + /// Decode sleep state from the word of storage. + init(word: Builtin.Word) { + switch UInt(word) & 0x03 { + case 0: + let continuationBits = UInt(word) & ~0x03 + if continuationBits == 0 { + self = .notStarted + } else { + let continuation = unsafeBitCast( + continuationBits, to: SleepContinuation.self) + self = .activeContinuation(continuation) + } + + case 1: + self = .finished + + case 2: + self = .cancelled + + case 3: + self = .cancelledBeforeStarted + + default: + fatalError("Bitmask failure") + } + } + + /// Decode sleep state by loading from the given pointer + init(loading wordPtr: UnsafeMutablePointer) { + self.init(word: Builtin.atomicload_seqcst_Word(wordPtr._rawValue)) + } + + /// Encode sleep state into a word of storage. + var word: UInt { + switch self { + case .notStarted: + return 0 + + case .activeContinuation(let continuation): + let continuationBits = unsafeBitCast(continuation, to: UInt.self) + return continuationBits + + case .finished: + return 1 + + case .cancelled: + return 2 + + case .cancelledBeforeStarted: + return 3 + } + } + } + + /// Called when the sleep(nanoseconds:) operation woke up without being + /// cancelled. + private static func onSleepWake( + _ wordPtr: UnsafeMutablePointer + ) { + while true { + let state = SleepState(loading: wordPtr) + switch state { + case .notStarted: + fatalError("Cannot wake before we even started") + + case .activeContinuation(let continuation): + // We have an active continuation, so try to transition to the + // "finished" state. + let (_, won) = Builtin.cmpxchg_seqcst_seqcst_Word( + wordPtr._rawValue, + state.word._builtinWordValue, + SleepState.finished.word._builtinWordValue) + if Bool(_builtinBooleanLiteral: won) { + // The sleep finished, so invoke the continuation: we're done. + continuation.resume() + return + } + + // Try again! + continue + + case .finished: + fatalError("Already finished normally, can't do that again") + + case .cancelled: + // The task was cancelled, which means the continuation was + // called by the cancellation handler. We need to deallocate the flag + // word, because it was left over for this task to complete. + wordPtr.deallocate() + return + + case .cancelledBeforeStarted: + // Nothing to do; + return + } + } + } + + /// Called when the sleep(nanoseconds:) operation has been cancelled before + /// the sleep completed. + private static func onSleepCancel( + _ wordPtr: UnsafeMutablePointer + ) { + while true { + let state = SleepState(loading: wordPtr) + switch state { + case .notStarted: + // We haven't started yet, so try to transition to the cancelled-before + // started state. + let (_, won) = Builtin.cmpxchg_seqcst_seqcst_Word( + wordPtr._rawValue, + state.word._builtinWordValue, + SleepState.cancelledBeforeStarted.word._builtinWordValue) + if Bool(_builtinBooleanLiteral: won) { + return + } + + // Try again! + continue + + case .activeContinuation(let continuation): + // We have an active continuation, so try to transition to the + // "cancelled" state. + let (_, won) = Builtin.cmpxchg_seqcst_seqcst_Word( + wordPtr._rawValue, + state.word._builtinWordValue, + SleepState.cancelled.word._builtinWordValue) + if Bool(_builtinBooleanLiteral: won) { + // We recorded the task cancellation before the sleep finished, so + // invoke the continuation with the cancellation error. + continuation.resume(throwing: _Concurrency.CancellationError()) + return + } + + // Try again! + continue + + case .finished, .cancelled, .cancelledBeforeStarted: + // The operation already finished, so there is nothing more to do. + return + } + } + } + + /// Suspends the current task for _at least_ the given duration + /// in nanoseconds, unless the task is cancelled. If the task is cancelled, + /// throws \c CancellationError without waiting for the duration. + /// + /// This function does _not_ block the underlying thread. + public static func sleep(nanoseconds duration: UInt64) async throws { + // Allocate storage for the storage word. + let wordPtr = UnsafeMutablePointer.allocate(capacity: 1) + + // Initialize the flag word to "not started", which means the continuation + // has neither been created nor completed. + Builtin.atomicstore_seqcst_Word( + wordPtr._rawValue, SleepState.notStarted.word._builtinWordValue) + + do { + // Install a cancellation handler to resume the continuation by + // throwing CancellationError. + try await withTaskCancellationHandler { + let _: () = try await withUnsafeThrowingContinuation { continuation in + while true { + let state = SleepState(loading: wordPtr) + switch state { + case .notStarted: + // The word that describes the active continuation state. + let continuationWord = + SleepState.activeContinuation(continuation).word + + // Try to swap in the continuation word. + let (_, won) = Builtin.cmpxchg_seqcst_seqcst_Word( + wordPtr._rawValue, + state.word._builtinWordValue, + continuationWord._builtinWordValue) + if !Bool(_builtinBooleanLiteral: won) { + // Keep trying! + continue + } + + // Create a task that resumes the continuation normally if it + // finishes first. Enqueue it directly with the delay, so it fires + // when we're done sleeping. + let sleepTaskFlags = taskCreateFlags( + priority: nil, isChildTask: false, copyTaskLocals: false, + inheritContext: false, enqueueJob: false, + addPendingGroupTaskUnconditionally: false) + let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) { + onSleepWake(wordPtr) + } + _enqueueJobGlobalWithDelay( + duration, Builtin.convertTaskToJob(sleepTask)) + return + + case .activeContinuation, .finished: + fatalError("Impossible to have multiple active continuations") + + case .cancelled: + fatalError("Impossible to have cancelled before we began") + + case .cancelledBeforeStarted: + // Finish the continuation normally. We'll throw later, after + // we clean up. + continuation.resume() + return + } + } + } + } onCancel: { + onSleepCancel(wordPtr) + } + + // Determine whether we got cancelled before we even started. + let cancelledBeforeStarted: Bool + switch SleepState(loading: wordPtr) { + case .notStarted, .activeContinuation, .cancelled: + fatalError("Invalid state for non-cancelled sleep task") + + case .cancelledBeforeStarted: + cancelledBeforeStarted = true + + case .finished: + cancelledBeforeStarted = false + } + + // We got here without being cancelled, so deallocate the storage for + // the flag word and continuation. + wordPtr.deallocate() + + // If we got cancelled before we even started, through the cancellation + // error now. + if cancelledBeforeStarted { + throw _Concurrency.CancellationError() + } + } catch { + // The task was cancelled; propagate the error. The "on wake" task is + // responsible for deallocating the flag word and continuation, if it's + // still running. + throw error + } + } +} diff --git a/stdlib/public/Concurrency/TaskStatus.cpp b/stdlib/public/Concurrency/TaskStatus.cpp index b1113b4826c0d..54bcf887a949e 100644 --- a/stdlib/public/Concurrency/TaskStatus.cpp +++ b/stdlib/public/Concurrency/TaskStatus.cpp @@ -19,10 +19,16 @@ #include "swift/Runtime/Concurrency.h" #include "swift/Runtime/Mutex.h" #include "swift/ABI/TaskStatus.h" +#include "TaskPrivate.h" #include using namespace swift; +inline TaskStatusRecord * +ActiveTaskStatus::getStatusRecordParent(TaskStatusRecord *ptr) { + return ptr->getParent(); +} + /**************************************************************************/ /************************* RECORD LOCK MANAGEMENT *************************/ /**************************************************************************/ @@ -167,7 +173,7 @@ static void waitForStatusRecordUnlock(AsyncTask *task, while (true) { // Check that oldStatus is still correct. - oldStatus = task->Status.load(std::memory_order_acquire); + oldStatus = task->_private().Status.load(std::memory_order_acquire); if (!oldStatus.isLocked()) return; @@ -179,6 +185,19 @@ static void waitForStatusRecordUnlock(AsyncTask *task, } } +enum class LockContext { + /// The lock is being acquired from within the running task. + OnTask, + + /// The lock is being acquired asynchronously in order to cancel the + /// task. + Cancellation, + + /// The lock is being acquired asynchronously in order to read the + /// status records for some other reason. + OtherAsynchronous +}; + /// Acquire a task's status record lock and return the /// previous value of its status record state. /// @@ -189,17 +208,18 @@ static void waitForStatusRecordUnlock(AsyncTask *task, static ActiveTaskStatus acquireStatusRecordLock(AsyncTask *task, Optional &recordLockRecord, - bool forCancellation) { - auto loadOrdering = forCancellation + LockContext lockContext) { + auto loadOrdering = lockContext != LockContext::OnTask ? std::memory_order_acquire : std::memory_order_relaxed; + bool forCancellation = lockContext == LockContext::Cancellation; // Load the current state. We can use relaxed loads if this isn't // for cancellation because (1) this operation should be synchronous // with the task, so the only thing that can modify it asynchronously // is a cancelling thread, and (2) we'll reload with acquire ordering // if a cancelling thread forces us to wait for an unlock. - auto oldStatus = task->Status.load(loadOrdering); + auto oldStatus = task->_private().Status.load(loadOrdering); while (true) { // Cancellation should be idempotent: if the task has already @@ -218,10 +238,8 @@ acquireStatusRecordLock(AsyncTask *task, // try to just set the cancelled bit and return. auto oldRecord = oldStatus.getInnermostRecord(); if (!oldRecord && forCancellation) { - ActiveTaskStatus newStatus(nullptr, - /*cancelled*/ true, - /*locked*/ false); - if (task->Status.compare_exchange_weak(oldStatus, newStatus, + ActiveTaskStatus newStatus = oldStatus.withCancelled(); + if (task->_private().Status.compare_exchange_weak(oldStatus, newStatus, /*success*/ std::memory_order_relaxed, /*failure*/ loadOrdering)) return newStatus; @@ -239,11 +257,11 @@ acquireStatusRecordLock(AsyncTask *task, // Install the lock record as the active cancellation info, or // restart if that fails. - bool newIsCancelled = forCancellation || oldStatus.isCancelled(); - ActiveTaskStatus newStatus(&*recordLockRecord, - /*cancelled*/ newIsCancelled, - /*locked*/ true); - if (task->Status.compare_exchange_weak(oldStatus, newStatus, + ActiveTaskStatus newStatus = + oldStatus.withLockingRecord(&*recordLockRecord); + if (forCancellation) + newStatus = newStatus.withCancelled(); + if (task->_private().Status.compare_exchange_weak(oldStatus, newStatus, /*success*/ std::memory_order_release, /*failure*/ loadOrdering)) return oldStatus; @@ -261,7 +279,7 @@ static void releaseStatusRecordLock(AsyncTask *task, // the state while we've locked it. The task shouldn't depend // on memory-ordering with anything we've done, so we can use a // relaxed store. - task->Status.store(newStatus, std::memory_order_relaxed); + task->_private().Status.store(newStatus, std::memory_order_relaxed); // Unlock the record lock. recordLockRecord->unlock(); @@ -277,7 +295,7 @@ static bool swift_task_addStatusRecordImpl(TaskStatusRecord *newRecord) { // Load the current state. We can use a relaxed load because we're // synchronous with the task. - auto oldStatus = task->Status.load(std::memory_order_relaxed); + auto oldStatus = task->_private().Status.load(std::memory_order_relaxed); while (true) { // Wait for any active lock to be released. @@ -290,10 +308,8 @@ static bool swift_task_addStatusRecordImpl(TaskStatusRecord *newRecord) { // Set the record as the new innermost record. // We have to use a release on success to make the initialization of // the new record visible to the cancelling thread. - ActiveTaskStatus newStatus(newRecord, - oldStatus.isCancelled(), - /*locked*/ false); - if (task->Status.compare_exchange_weak(oldStatus, newStatus, + ActiveTaskStatus newStatus = oldStatus.withInnermostRecord(newRecord); + if (task->_private().Status.compare_exchange_weak(oldStatus, newStatus, /*success*/ std::memory_order_release, /*failure*/ std::memory_order_relaxed)) return !oldStatus.isCancelled(); @@ -306,7 +322,7 @@ static bool swift_task_tryAddStatusRecordImpl(TaskStatusRecord *newRecord) { // Load the current state. We can use a relaxed load because we're // synchronous with the task. - auto oldStatus = task->Status.load(std::memory_order_relaxed); + auto oldStatus = task->_private().Status.load(std::memory_order_relaxed); while (true) { // If the old info is already cancelled, do nothing. @@ -327,10 +343,8 @@ static bool swift_task_tryAddStatusRecordImpl(TaskStatusRecord *newRecord) { // Set the record as the new innermost record. // We have to use a release on success to make the initialization of // the new record visible to the cancelling thread. - ActiveTaskStatus newStatus(newRecord, - /*cancelled*/ false, - /*locked*/ false); - if (task->Status.compare_exchange_weak(oldStatus, newStatus, + ActiveTaskStatus newStatus = oldStatus.withInnermostRecord(newRecord); + if (task->_private().Status.compare_exchange_weak(oldStatus, newStatus, /*success*/ std::memory_order_release, /*failure*/ std::memory_order_relaxed)) return true; @@ -342,7 +356,8 @@ static bool swift_task_removeStatusRecordImpl(TaskStatusRecord *record) { auto task = swift_task_getCurrent(); // Load the current state. - auto oldStatus = task->Status.load(std::memory_order_relaxed); + auto &status = task->_private().Status; + auto oldStatus = status.load(std::memory_order_relaxed); while (true) { // Wait for any active lock to be released. @@ -351,10 +366,9 @@ static bool swift_task_removeStatusRecordImpl(TaskStatusRecord *record) { // If the record is the innermost record, try to just pop it off. if (oldStatus.getInnermostRecord() == record) { - ActiveTaskStatus newStatus(record->getParent(), - oldStatus.isCancelled(), - /*locked*/ false); - if (task->Status.compare_exchange_weak(oldStatus, newStatus, + ActiveTaskStatus newStatus = + oldStatus.withInnermostRecord(record->getParent()); + if (status.compare_exchange_weak(oldStatus, newStatus, /*success*/ std::memory_order_release, /*failure*/ std::memory_order_relaxed)) { return !oldStatus.isCancelled(); @@ -373,7 +387,7 @@ static bool swift_task_removeStatusRecordImpl(TaskStatusRecord *record) { // Acquire the status record lock. Optional recordLockRecord; oldStatus = acquireStatusRecordLock(task, recordLockRecord, - /*forCancellation*/ false); + LockContext::OnTask); assert(!oldStatus.isLocked()); // We can't observe the record to be the innermost record here because @@ -404,11 +418,16 @@ SWIFT_CC(swift) static bool swift_task_hasTaskGroupStatusRecordImpl() { auto task = swift_task_getCurrent(); + // a group must be in a task, so if we're not in a task... + // then, we certainly are not in a group either! + if (!task) + return false; + Optional recordLockRecord; // Acquire the status record lock. auto oldStatus = acquireStatusRecordLock(task, recordLockRecord, - /*forCancellation*/ false); + LockContext::OnTask); assert(!oldStatus.isLocked()); // Scan for the task group record within all the active records. @@ -420,12 +439,7 @@ static bool swift_task_hasTaskGroupStatusRecordImpl() { } } - // Release the status record lock, being sure to flag that - // the task is now cancelled. - ActiveTaskStatus cancelledStatus(oldStatus.getInnermostRecord(), - /*cancelled*/ false, // FIXME: is this right, or must be the same as previous cancelled status? - /*locked*/ false); - releaseStatusRecordLock(task, cancelledStatus, recordLockRecord); + releaseStatusRecordLock(task, oldStatus, recordLockRecord); return foundTaskGroupRecord; } @@ -538,11 +552,12 @@ static void swift_task_cancelImpl(AsyncTask *task) { // Acquire the status record lock. auto oldStatus = acquireStatusRecordLock(task, recordLockRecord, - /*forCancellation*/ true); + LockContext::Cancellation); assert(!oldStatus.isLocked()); - // If we were already cancelled or were able to cancel without acquiring - // the lock, there's nothing else to do. + // Lock acquisition will fail for LockContext::Cancellation if + // the task is already cancelled. In this case, we have nothing + // to do, not even releasing the lock. if (oldStatus.isCancelled()) { return; } @@ -558,9 +573,7 @@ static void swift_task_cancelImpl(AsyncTask *task) { // Release the status record lock, being sure to flag that // the task is now cancelled. - ActiveTaskStatus cancelledStatus(oldStatus.getInnermostRecord(), - /*cancelled*/ true, - /*locked*/ false); + ActiveTaskStatus cancelledStatus = oldStatus.withCancelled(); releaseStatusRecordLock(task, cancelledStatus, recordLockRecord); } @@ -575,19 +588,15 @@ static void swift_task_cancel_group_child_tasksImpl(TaskGroup *group) { // We are NOT cancelling the entire parent task though. auto task = swift_task_getCurrent(); auto oldStatus = acquireStatusRecordLock(task, recordLockRecord, - /*forCancellation*/ false); + LockContext::OnTask); // Carry out the cancellation operations associated with all // the active records. for (auto cur: oldStatus.records()) { performGroupCancellationAction(cur); } - // Release the status record lock, being sure to flag that - // the task is now cancelled. - ActiveTaskStatus cancelledStatus(oldStatus.getInnermostRecord(), - /*cancelled*/ oldStatus.isCancelled(), - /*locked*/ false); - releaseStatusRecordLock(task, cancelledStatus, recordLockRecord); + // Release the status record lock, restoring exactly the old status. + releaseStatusRecordLock(task, oldStatus, recordLockRecord); } /**************************************************************************/ @@ -643,26 +652,23 @@ JobPriority static swift_task_escalateImpl(AsyncTask *task, JobPriority newPriority) { Optional recordLockRecord; - // Fast path: check that the task's priority is not already at least - // as high as the target. The task's priority can only be modified - // under the status record lock; it's possible that the priority could - // be getting simultaneously escalated, but it's okay for us to return - // before that's complete. - if (task->Flags.getPriority() >= newPriority) - return task->Flags.getPriority(); + // Fast path: check that the stored priority is already at least + // as high as the desired priority. + auto oldStatus = task->_private().Status.load(std::memory_order_relaxed); + if (oldStatus.getStoredPriority() >= newPriority) + return oldStatus.getStoredPriority(); - // Acquire the status record lock. - auto oldStatus = acquireStatusRecordLock(task, recordLockRecord, - /*forCancellation*/ false); + // Acquire the status record lock. This has to do a load-acquire + // because we need to read the status records. + oldStatus = acquireStatusRecordLock(task, recordLockRecord, + LockContext::OtherAsynchronous); assert(!oldStatus.isLocked()); // Now that we have the task's status lock, check again that the // priority is still too low. - auto priorityToReturn = task->Flags.getPriority(); - if (priorityToReturn < newPriority) { - // Change the priority. - task->Flags.setPriority(newPriority); - priorityToReturn = newPriority; + auto newStatus = oldStatus; + if (oldStatus.getStoredPriority() < newPriority) { + newStatus = oldStatus.withEscalatedPriority(newPriority); // TODO: attempt to escalate the thread running the task, if it's // currently running. This probably requires the task to be enqueued @@ -675,9 +681,43 @@ static swift_task_escalateImpl(AsyncTask *task, JobPriority newPriority) { } // Release the status record lock, restoring the old status. - releaseStatusRecordLock(task, oldStatus, recordLockRecord); + releaseStatusRecordLock(task, newStatus, recordLockRecord); + + return newStatus.getStoredPriority(); +} + +void AsyncTask::flagAsRunning_slow() { + Optional recordLockRecord; + + auto oldStatus = acquireStatusRecordLock(this, recordLockRecord, + LockContext::OnTask); + assert(!oldStatus.isLocked()); + assert(!oldStatus.isRunning()); + + auto newStatus = oldStatus.withRunning(true); + if (newStatus.isStoredPriorityEscalated()) { + newStatus = newStatus.withoutStoredPriorityEscalation(); + Flags.setPriority(oldStatus.getStoredPriority()); + } + + releaseStatusRecordLock(this, newStatus, recordLockRecord); +} + +void AsyncTask::flagAsSuspended_slow() { + Optional recordLockRecord; + + auto oldStatus = acquireStatusRecordLock(this, recordLockRecord, + LockContext::OnTask); + assert(!oldStatus.isLocked()); + assert(oldStatus.isRunning()); + + auto newStatus = oldStatus.withRunning(false); + if (newStatus.isStoredPriorityEscalated()) { + newStatus = newStatus.withoutStoredPriorityEscalation(); + Flags.setPriority(oldStatus.getStoredPriority()); + } - return priorityToReturn; + releaseStatusRecordLock(this, newStatus, recordLockRecord); } /**************************************************************************/ @@ -690,7 +730,8 @@ static NearestTaskDeadline swift_task_getNearestDeadlineImpl(AsyncTask *task) { // ignoring the possibility of a concurrent cancelling task. // Load the current state. - auto oldStatus = task->Status.load(std::memory_order_relaxed); + auto &status = task->_private().Status; + auto oldStatus = status.load(std::memory_order_relaxed); NearestTaskDeadline result; diff --git a/stdlib/public/Concurrency/ThreadSanitizer.cpp b/stdlib/public/Concurrency/ThreadSanitizer.cpp index 336c35ff8535d..e8c8701b8247c 100644 --- a/stdlib/public/Concurrency/ThreadSanitizer.cpp +++ b/stdlib/public/Concurrency/ThreadSanitizer.cpp @@ -31,12 +31,14 @@ TSanFunc *tsan_acquire, *tsan_release; void swift::_swift_tsan_acquire(void *addr) { if (tsan_acquire) { tsan_acquire(addr); + SWIFT_TASK_DEBUG_LOG("tsan_acquire on %p", addr); } } void swift::_swift_tsan_release(void *addr) { if (tsan_release) { tsan_release(addr); + SWIFT_TASK_DEBUG_LOG("tsan_release on %p", addr); } } diff --git a/stdlib/public/Concurrency/YieldingContinuation.swift b/stdlib/public/Concurrency/YieldingContinuation.swift deleted file mode 100644 index 9a76b2910f2bf..0000000000000 --- a/stdlib/public/Concurrency/YieldingContinuation.swift +++ /dev/null @@ -1,193 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2020-2021 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 -// -//===----------------------------------------------------------------------===// - -import Swift - -internal final class _YieldingContinuationStorage: UnsafeSendable { - var continuation: Builtin.RawUnsafeContinuation? -} - -@available(SwiftStdlib 5.5, *) -public struct YieldingContinuation: Sendable { - let storage = _YieldingContinuationStorage() - - /// Construct a YieldingContinuation. - /// - /// This continuation type can be called more than once, unlike the unsafe and - /// checked counterparts. Each call to the yielding functions will resume any - /// awaiter on the next function. This type is inherently sendable and can - /// safely be used and stored in multiple task contexts. - public init() { } - - /// Construct a YieldingContinuation with specific types including a failure. - /// - /// This continuation type can be called more than once, unlike the unsafe and - /// checked counterparts. Each call to the yielding functions will resume any - /// awaiter on the next function. This type is inherently sendable and can - /// safely be used and stored in multiple task contexts. - public init(yielding: Element.Type, throwing: Failure.Type) { } - - internal func _extract() -> UnsafeContinuation? { - let raw = Builtin.atomicrmw_xchg_acqrel_Word( - Builtin.addressof(&storage.continuation), - UInt(bitPattern: 0)._builtinWordValue) - return unsafeBitCast(raw, to: UnsafeContinuation?.self) - } - - internal func _inject( - _ continuation: UnsafeContinuation - ) -> UnsafeContinuation? { - let rawContinuation = unsafeBitCast(continuation, to: Builtin.Word.self) - let raw = Builtin.atomicrmw_xchg_acqrel_Word( - Builtin.addressof(&storage.continuation), rawContinuation) - return unsafeBitCast(raw, to: UnsafeContinuation?.self) - } - - /// Resume the task awaiting next by having it return normally from its - /// suspension point. - /// - /// - Parameter value: The value to return from an awaiting call to next. - /// - /// Unlike other continuations `YieldingContinuation` may resume more than - /// once. However if there are no potential awaiting calls to `next` this - /// function will return false, indicating that the caller needs to decide how - /// the behavior should be handled. - public func yield(_ value: __owned Element) -> Bool { - if let continuation = _extract() { - continuation.resume(returning: value) - return true - } - return false - } - - /// Resume the task awaiting the continuation by having it throw an error - /// from its suspension point. - /// - /// - Parameter error: The error to throw from an awaiting call to next. - /// - /// Unlike other continuations `YieldingContinuation` may resume more than - /// once. However if there are no potential awaiting calls to `next` this - /// function will return false, indicating that the caller needs to decide how - /// the behavior should be handled. - public func yield(throwing error: __owned Failure) -> Bool { - if let continuation = _extract() { - continuation.resume(throwing: error) - return true - } - return false - } -} - -@available(SwiftStdlib 5.5, *) -extension YieldingContinuation where Failure == Error { - /// Await a resume from a call to a yielding function. - /// - /// - Return: The element that was yielded or a error that was thrown. - /// - /// When multiple calls are awaiting a produced value from next any call to - /// yield will resume all awaiting calls to next with that value. - public func next() async throws -> Element { - var existing: UnsafeContinuation? - do { - let result = try await withUnsafeThrowingContinuation { - (continuation: UnsafeContinuation) in - existing = _inject(continuation) - } - existing?.resume(returning: result) - return result - } catch { - existing?.resume(throwing: error) - throw error - } - } -} - -@available(SwiftStdlib 5.5, *) -extension YieldingContinuation where Failure == Never { - /// Construct a YieldingContinuation with a specific Element type. - /// - /// This continuation type can be called more than once, unlike the unsafe and - /// checked counterparts. Each call to the yielding functions will resume any - /// awaiter on the next function. This type is inherently sendable and can - /// safely be used and stored in multiple task contexts. - public init(yielding: Element.Type) { } - - /// Await a resume from a call to a yielding function. - /// - /// - Return: The element that was yielded. - public func next() async -> Element { - var existing: UnsafeContinuation? - let result = try! await withUnsafeThrowingContinuation { - (continuation: UnsafeContinuation) in - existing = _inject(continuation) - } - existing?.resume(returning: result) - return result - } -} - -@available(SwiftStdlib 5.5, *) -extension YieldingContinuation { - /// Resume the task awaiting the continuation by having it either - /// return normally or throw an error based on the state of the given - /// `Result` value. - /// - /// - Parameter result: A value to either return or throw from the - /// continuation. - /// - /// Unlike other continuations `YieldingContinuation` may resume more than - /// once. However if there are no potential awaiting calls to `next` this - /// function will return false, indicating that the caller needs to decide how - /// the behavior should be handled. - public func yield( - with result: Result - ) -> Bool where Failure == Error { - switch result { - case .success(let val): - return self.yield(val) - case .failure(let err): - return self.yield(throwing: err) - } - } - - /// Resume the task awaiting the continuation by having it either - /// return normally or throw an error based on the state of the given - /// `Result` value. - /// - /// - Parameter result: A value to either return or throw from the - /// continuation. - /// - /// Unlike other continuations `YieldingContinuation` may resume more than - /// once. However if there are no potential awaiting calls to `next` this - /// function will return false, indicating that the caller needs to decide how - /// the behavior should be handled. - public func yield(with result: Result) -> Bool { - switch result { - case .success(let val): - return self.yield(val) - case .failure(let err): - return self.yield(throwing: err) - } - } - - /// Resume the task awaiting the continuation by having it return normally - /// from its suspension point. - /// - /// Unlike other continuations `YieldingContinuation` may resume more than - /// once. However if there are no potential awaiting calls to `next` this - /// function will return false, indicating that the caller needs to decide how - /// the behavior should be handled. - public func yield() -> Bool where Element == Void { - return self.yield(()) - } -} - diff --git a/stdlib/public/Darwin/CMakeLists.txt b/stdlib/public/Darwin/CMakeLists.txt deleted file mode 100644 index b8be8dfc71188..0000000000000 --- a/stdlib/public/Darwin/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# All libraries in this directory tree are overlays that depend on Darwin SDK. - -set(SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES) -if(SWIFT_BUILD_DYNAMIC_SDK_OVERLAY) - list(APPEND SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES SHARED) -endif() -if(SWIFT_BUILD_STATIC_SDK_OVERLAY) - list_intersect("${SWIFT_APPLE_PLATFORMS}" "${SWIFT_SDKS}" building_darwin_sdks) - if(building_darwin_sdks) - message(SEND_ERROR "cannot build static standard library for Darwin SDKs") - else() - list(APPEND SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES STATIC) - endif() -endif() - -set(all_overlays - CoreFoundation - CoreGraphics - Dispatch - Foundation - ObjectiveC - XCTest -) - -if(DEFINED SWIFT_OVERLAY_TARGETS) - set(overlays_to_build ${SWIFT_OVERLAY_TARGETS}) -else() - set(overlays_to_build ${all_overlays}) -endif() - -message(STATUS "Building overlays: ${overlays_to_build}") -foreach(overlay ${overlays_to_build}) - message(STATUS "INCLUDING OVERLAY: ${overlay}") - add_subdirectory(${overlay}) -endforeach() diff --git a/stdlib/public/Darwin/CoreFoundation/CMakeLists.txt b/stdlib/public/Darwin/CoreFoundation/CMakeLists.txt deleted file mode 100644 index 7050e7f1a3ea6..0000000000000 --- a/stdlib/public/Darwin/CoreFoundation/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.4.3) -include("../../../../cmake/modules/StandaloneOverlay.cmake") - -add_swift_target_library(swiftCoreFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY - CoreFoundation.swift - - "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" - - SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - SWIFT_MODULE_DEPENDS_OSX Darwin Dispatch ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_IOS Darwin Dispatch ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_TVOS Darwin Dispatch ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_WATCHOS Darwin Dispatch ObjectiveC # auto-updated - FRAMEWORK_DEPENDS CoreFoundation - INSTALL_IN_COMPONENT sdk-overlay -) diff --git a/stdlib/public/Darwin/CoreFoundation/CoreFoundation.swift b/stdlib/public/Darwin/CoreFoundation/CoreFoundation.swift deleted file mode 100644 index f9490218e15e3..0000000000000 --- a/stdlib/public/Darwin/CoreFoundation/CoreFoundation.swift +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import CoreFoundation - -public protocol _CFObject: AnyObject, Hashable {} -extension _CFObject { - public var hashValue: Int { - return Int(bitPattern: CFHash(self)) - } - public func hash(into hasher: inout Hasher) { - hasher.combine(self.hashValue) - } - public static func ==(left: Self, right: Self) -> Bool { - return CFEqual(left, right) - } -} diff --git a/stdlib/public/Darwin/CoreGraphics/CGFloat.swift.gyb b/stdlib/public/Darwin/CoreGraphics/CGFloat.swift.gyb deleted file mode 100644 index 4998f485365ab..0000000000000 --- a/stdlib/public/Darwin/CoreGraphics/CGFloat.swift.gyb +++ /dev/null @@ -1,678 +0,0 @@ -//===--- CGFloat.swift.gyb ------------------------------------*- swift -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -%{ - -from SwiftIntTypes import all_integer_types - -# Number of bits in the Builtin.Word type -word_bits = int(CMAKE_SIZEOF_VOID_P) * 8 - -}% - -@_exported import CoreGraphics -import Darwin - -@frozen -public struct CGFloat { -#if arch(i386) || arch(arm) || arch(arm64_32) - /// The native type used to store the CGFloat, which is Float on - /// 32-bit architectures and Double on 64-bit architectures. - public typealias NativeType = Float -#elseif arch(x86_64) || arch(arm64) - /// The native type used to store the CGFloat, which is Float on - /// 32-bit architectures and Double on 64-bit architectures. - public typealias NativeType = Double -#endif - - @_transparent public init() { - self.native = 0.0 - } - - @_transparent public init(_ value: Float) { - self.native = NativeType(value) - } - - @_transparent public init(_ value: Double) { - self.native = NativeType(value) - } - -#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) - @_transparent public init(_ value: Float80) { - self.native = NativeType(value) - } -#endif - - @_transparent public init(_ value: CGFloat) { - self.native = value.native - } - - /// The native value. - public var native: NativeType -} - -extension CGFloat : SignedNumeric { - @_alwaysEmitIntoClient // availability - public init(_ source: T) { - self.native = NativeType(source) - } - - @_transparent - public init?(exactly source: T) { - guard let native = NativeType(exactly: source) else { return nil } - self.native = native - } - - @_transparent - public var magnitude: CGFloat { - return CGFloat(native.magnitude) - } -} - -extension CGFloat : BinaryFloatingPoint { - - public typealias RawSignificand = UInt - public typealias Exponent = Int - - @_transparent - public static var exponentBitCount: Int { - return NativeType.exponentBitCount - } - - @_transparent - public static var significandBitCount: Int { - return NativeType.significandBitCount - } - - // Conversions to/from integer encoding. These are not part of the - // BinaryFloatingPoint prototype because there's no guarantee that an - // integer type of the same size actually exists (e.g. Float80). - @_transparent - public var bitPattern: UInt { - return UInt(native.bitPattern) - } - - @_transparent - public init(bitPattern: UInt) { - native = NativeType(bitPattern: UInt${word_bits}(bitPattern)) - } - - @_transparent - public var sign: FloatingPointSign { - return native.sign - } - - @_transparent - public var exponentBitPattern: UInt { - return native.exponentBitPattern - } - - @_transparent - public var significandBitPattern: UInt { - return UInt(native.significandBitPattern) - } - - @_transparent - public init(sign: FloatingPointSign, - exponentBitPattern: UInt, - significandBitPattern: UInt) { - native = NativeType(sign: sign, - exponentBitPattern: exponentBitPattern, - significandBitPattern: NativeType.RawSignificand(significandBitPattern)) - } - - @_transparent - public init(nan payload: RawSignificand, signaling: Bool) { - native = NativeType(nan: NativeType.RawSignificand(payload), - signaling: signaling) - } - - @_transparent - public static var infinity: CGFloat { - return CGFloat(NativeType.infinity) - } - - @_transparent - public static var nan: CGFloat { - return CGFloat(NativeType.nan) - } - - @_transparent - public static var signalingNaN: CGFloat { - return CGFloat(NativeType.signalingNaN) - } - - @available(*, unavailable, renamed: "nan") - public static var quietNaN: CGFloat { - fatalError("unavailable") - } - - @_transparent - public static var greatestFiniteMagnitude: CGFloat { - return CGFloat(NativeType.greatestFiniteMagnitude) - } - - @_transparent - public static var pi: CGFloat { - return CGFloat(NativeType.pi) - } - - @_transparent - public var ulp: CGFloat { - return CGFloat(native.ulp) - } - - @_transparent - public static var leastNormalMagnitude: CGFloat { - return CGFloat(NativeType.leastNormalMagnitude) - } - - @_transparent - public static var leastNonzeroMagnitude: CGFloat { - return CGFloat(NativeType.leastNonzeroMagnitude) - } - - @_transparent - public var exponent: Int { - return native.exponent - } - - @_transparent - public var significand: CGFloat { - return CGFloat(native.significand) - } - - @_transparent - public init(sign: FloatingPointSign, exponent: Int, significand: CGFloat) { - native = NativeType(sign: sign, - exponent: exponent, significand: significand.native) - } - - @_transparent - public mutating func round(_ rule: FloatingPointRoundingRule) { - native.round(rule) - } - - @_transparent - public var nextUp: CGFloat { - return CGFloat(native.nextUp) - } - - @_transparent - public mutating func negate() { - native.negate() - } - - @_transparent - public static func +=(lhs: inout CGFloat, rhs: CGFloat) { - lhs.native += rhs.native - } - - @_transparent - public static func -=(lhs: inout CGFloat, rhs: CGFloat) { - lhs.native -= rhs.native - } - - @_transparent - public static func *=(lhs: inout CGFloat, rhs: CGFloat) { - lhs.native *= rhs.native - } - - @_transparent - public static func /=(lhs: inout CGFloat, rhs: CGFloat) { - lhs.native /= rhs.native - } - - @_transparent - public mutating func formTruncatingRemainder(dividingBy other: CGFloat) { - native.formTruncatingRemainder(dividingBy: other.native) - } - - @_transparent - public mutating func formRemainder(dividingBy other: CGFloat) { - native.formRemainder(dividingBy: other.native) - } - - @_transparent - public mutating func formSquareRoot( ) { - native.formSquareRoot( ) - } - - @_transparent - public mutating func addProduct(_ lhs: CGFloat, _ rhs: CGFloat) { - native.addProduct(lhs.native, rhs.native) - } - - @_transparent - public func isEqual(to other: CGFloat) -> Bool { - return self.native.isEqual(to: other.native) - } - - @_transparent - public func isLess(than other: CGFloat) -> Bool { - return self.native.isLess(than: other.native) - } - - @_transparent - public func isLessThanOrEqualTo(_ other: CGFloat) -> Bool { - return self.native.isLessThanOrEqualTo(other.native) - } - - @_transparent - public var isNormal: Bool { - return native.isNormal - } - - @_transparent - public var isFinite: Bool { - return native.isFinite - } - - @_transparent - public var isZero: Bool { - return native.isZero - } - - @_transparent - public var isSubnormal: Bool { - return native.isSubnormal - } - - @_transparent - public var isInfinite: Bool { - return native.isInfinite - } - - @_transparent - public var isNaN: Bool { - return native.isNaN - } - - @_transparent - public var isSignalingNaN: Bool { - return native.isSignalingNaN - } - - @available(*, unavailable, renamed: "isSignalingNaN") - public var isSignaling: Bool { - fatalError("unavailable") - } - - @_transparent - public var isCanonical: Bool { - return true - } - - @_transparent - public var floatingPointClass: FloatingPointClassification { - return native.floatingPointClass - } - - @_transparent - public var binade: CGFloat { - return CGFloat(native.binade) - } - - @_transparent - public var significandWidth: Int { - return native.significandWidth - } - - /// Create an instance initialized to `value`. - @_transparent - public init(floatLiteral value: NativeType) { - native = value - } - - /// Create an instance initialized to `value`. - @_transparent - public init(integerLiteral value: Int) { - native = NativeType(value) - } -} - -extension CGFloat { - @available(*, unavailable, renamed: "leastNormalMagnitude") - public static var min: CGFloat { - fatalError("unavailable") - } - - @available(*, unavailable, renamed: "greatestFiniteMagnitude") - public static var max: CGFloat { - fatalError("unavailable") - } -} - -@available(*, unavailable, renamed: "CGFloat.leastNormalMagnitude") -public var CGFLOAT_MIN: CGFloat { - fatalError("unavailable") -} - -@available(*, unavailable, renamed: "CGFloat.greatestFiniteMagnitude") -public var CGFLOAT_MAX: CGFloat { - fatalError("unavailable") -} - -extension CGFloat : CustomReflectable { - /// Returns a mirror that reflects `self`. - public var customMirror: Mirror { - return Mirror(reflecting: native) - } -} - -extension CGFloat : CustomStringConvertible { - /// A textual representation of `self`. - @_transparent - public var description: String { - return native.description - } -} - -extension CGFloat : Hashable { - /// The hash value. - /// - /// **Axiom:** `x == y` implies `x.hashValue == y.hashValue` - /// - /// - Note: the hash value is not guaranteed to be stable across - /// different invocations of the same program. Do not persist the - /// hash value across program runs. - @_transparent - public var hashValue: Int { - return native.hashValue - } - - /// Hashes the essential components of this value by feeding them into the - /// given hasher. - /// - /// - Parameter hasher: The hasher to use when combining the components - /// of this instance. - @inlinable @_transparent - public func hash(into hasher: inout Hasher) { - hasher.combine(native) - } - - @_alwaysEmitIntoClient @inlinable // Introduced in 5.1 - public func _rawHashValue(seed: Int) -> Int { - return native._rawHashValue(seed: seed) - } -} - -% for dst_ty in all_integer_types(word_bits): - -extension ${dst_ty.stdlib_name} { - @_transparent - public init(_ value: CGFloat) { - self = ${dst_ty.stdlib_name}(value.native) - } -} - -% end - - -extension Double { - @_transparent - public init(_ value: CGFloat) { - self = Double(value.native) - } -} - -extension Float { - @_transparent - public init(_ value: CGFloat) { - self = Float(value.native) - } -} - -//===----------------------------------------------------------------------===// -// Standard Operator Table -//===----------------------------------------------------------------------===// - -// TODO: These should not be necessary, since they're already provided by -// , but in practice they are currently needed to -// disambiguate overloads. We should find a way to remove them, either by -// tweaking the overload resolution rules, or by removing the other -// definitions in the standard lib, or both. - -extension CGFloat { - @_transparent - public static func +(lhs: CGFloat, rhs: CGFloat) -> CGFloat { - var lhs = lhs - lhs += rhs - return lhs - } - - @_transparent - public static func -(lhs: CGFloat, rhs: CGFloat) -> CGFloat { - var lhs = lhs - lhs -= rhs - return lhs - } - - @_transparent - public static func *(lhs: CGFloat, rhs: CGFloat) -> CGFloat { - var lhs = lhs - lhs *= rhs - return lhs - } - - @_transparent - public static func /(lhs: CGFloat, rhs: CGFloat) -> CGFloat { - var lhs = lhs - lhs /= rhs - return lhs - } -} - -//===----------------------------------------------------------------------===// -// Strideable Conformance -//===----------------------------------------------------------------------===// - -extension CGFloat : Strideable { - /// Returns a stride `x` such that `self.advanced(by: x)` approximates - /// `other`. - /// - /// - Complexity: O(1). - @_transparent - public func distance(to other: CGFloat) -> CGFloat { - return CGFloat(other.native - self.native) - } - - /// Returns a `Self` `x` such that `self.distance(to: x)` approximates - /// `n`. - /// - /// - Complexity: O(1). - @_transparent - public func advanced(by amount: CGFloat) -> CGFloat { - return CGFloat(self.native + amount.native) - } -} - -//===----------------------------------------------------------------------===// -// Deprecated operators -//===----------------------------------------------------------------------===// - -@_transparent -@available(*, unavailable, message: "Use truncatingRemainder instead") -public func %(lhs: CGFloat, rhs: CGFloat) -> CGFloat { - fatalError("% is not available.") -} - -@_transparent -@available(*, unavailable, message: "Use formTruncatingRemainder instead") -public func %=(lhs: inout CGFloat, rhs: CGFloat) { - fatalError("%= is not available.") -} - -//===----------------------------------------------------------------------===// -// tgmath -//===----------------------------------------------------------------------===// - -%{ -UnaryFunctions = [ - 'acos', 'asin', 'atan', 'cos', 'sin', 'tan', - 'acosh', 'asinh', 'atanh', 'cosh', 'sinh', 'tanh', - 'exp', 'exp2', 'expm1', - 'log', 'log10', 'log1p', 'log2', 'logb', - 'cbrt', 'erf', 'erfc', 'tgamma', - 'nearbyint', 'rint' -] - -BinaryFunctions = [ - 'atan2', 'hypot', 'pow', 'copysign', 'nextafter', 'fdim', 'fmax', 'fmin' -] -}% - -%for ufunc in UnaryFunctions: -@_transparent -public func ${ufunc}(_ x: CGFloat) -> CGFloat { - return CGFloat(${ufunc}(x.native)) -} - -%end - -%for bfunc in BinaryFunctions: -@_transparent -public func ${bfunc}(_ lhs: CGFloat, _ rhs: CGFloat) -> CGFloat { - return CGFloat(${bfunc}(lhs.native, rhs.native)) -} - -%end - -@_transparent -@available(*, unavailable, message: "use the floatingPointClass property.") -public func fpclassify(_ x: CGFloat) -> Int { - fatalError("unavailable") -} - -@available(*, unavailable, message: "use the isNormal property.") -public func isnormal(_ value: CGFloat) -> Bool { return value.isNormal } - -@available(*, unavailable, message: "use the isFinite property.") -public func isfinite(_ value: CGFloat) -> Bool { return value.isFinite } - -@available(*, unavailable, message: "use the isInfinite property.") -public func isinf(_ value: CGFloat) -> Bool { return value.isInfinite } - -@available(*, unavailable, message: "use the isNaN property.") -public func isnan(_ value: CGFloat) -> Bool { return value.isNaN } - -@available(*, unavailable, message: "use the sign property.") -public func signbit(_ value: CGFloat) -> Int { return value.sign.rawValue } - -@available(swift, deprecated: 4.2, renamed: "scalbn") -@_transparent -public func ldexp(_ x: CGFloat, _ n: Int) -> CGFloat { - return CGFloat(ldexp(x.native, n)) -} - -@available(swift, deprecated: 4.2, message: "use the exponent property.") -@_transparent -public func ilogb(_ x: CGFloat) -> Int { - return Int(x.exponent) -} - -@_transparent -public func lgamma(_ x: CGFloat) -> (CGFloat, Int) { - let (value, sign) = lgamma(x.native) - return (CGFloat(value), sign) -} - -@_transparent -public func remquo(_ x: CGFloat, _ y: CGFloat) -> (CGFloat, Int) { - let (rem, quo) = remquo(x.native, y.native) - return (CGFloat(rem), quo) -} - -@available(swift, deprecated: 4.2, message: - "use CGFloat(nan: CGFloat.RawSignificand) instead.") -@_transparent -public func nan(_ tag: String) -> CGFloat { - return CGFloat(nan(tag) as CGFloat.NativeType) -} - -@_transparent -public func j0(_ x: CGFloat) -> CGFloat { - return CGFloat(j0(Double(x.native))) -} - -@_transparent -public func j1(_ x: CGFloat) -> CGFloat { - return CGFloat(j1(Double(x.native))) -} - -@_transparent -public func jn(_ n: Int, _ x: CGFloat) -> CGFloat { - return CGFloat(jn(n, Double(x.native))) -} - -@_transparent -public func y0(_ x: CGFloat) -> CGFloat { - return CGFloat(y0(Double(x.native))) -} - -@_transparent -public func y1(_ x: CGFloat) -> CGFloat { - return CGFloat(y1(Double(x.native))) -} - -@_transparent -public func yn(_ n: Int, _ x: CGFloat) -> CGFloat { - return CGFloat(yn(n, Double(x.native))) -} - -extension CGFloat : _CVarArgPassedAsDouble, _CVarArgAligned { - /// Transform `self` into a series of machine words that can be - /// appropriately interpreted by C varargs - @_transparent - public var _cVarArgEncoding: [Int] { - return native._cVarArgEncoding - } - - /// Return the required alignment in bytes of - /// the value returned by `_cVarArgEncoding`. - @_transparent - public var _cVarArgAlignment: Int { - return native._cVarArgAlignment - } -} - -extension CGFloat : Codable { - @_transparent - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - do { - self.native = try container.decode(NativeType.self) - } catch DecodingError.typeMismatch(let type, let context) { - // We may have encoded as a different type on a different platform. A - // strict fixed-format decoder may disallow a conversion, so let's try the - // other type. - do { - if NativeType.self == Float.self { - self.native = NativeType(try container.decode(Double.self)) - } else { - self.native = NativeType(try container.decode(Float.self)) - } - } catch { - // Failed to decode as the other type, too. This is neither a Float nor - // a Double. Throw the old error; we don't want to clobber the original - // info. - throw DecodingError.typeMismatch(type, context) - } - } - } - - @_transparent - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.native) - } -} diff --git a/stdlib/public/Darwin/CoreGraphics/CMakeLists.txt b/stdlib/public/Darwin/CoreGraphics/CMakeLists.txt deleted file mode 100644 index a424ca4218150..0000000000000 --- a/stdlib/public/Darwin/CoreGraphics/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required(VERSION 3.4.3) -include("../../../../cmake/modules/StandaloneOverlay.cmake") - -add_swift_target_library(swiftCoreGraphics ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY - CoreGraphics.swift - Private.swift - - "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" - - GYB_SOURCES - CGFloat.swift.gyb - - SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - SWIFT_MODULE_DEPENDS_OSX Darwin Dispatch CoreFoundation ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_IOS Darwin Dispatch CoreFoundation ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_TVOS Darwin Dispatch CoreFoundation ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_WATCHOS Darwin Dispatch CoreFoundation ObjectiveC # auto-updated - FRAMEWORK_DEPENDS CoreGraphics - - DEPLOYMENT_VERSION_OSX ${SWIFTLIB_DEPLOYMENT_VERSION_COREGRAPHICS_OSX} - DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_COREGRAPHICS_IOS} - DEPLOYMENT_VERSION_TVOS ${SWIFTLIB_DEPLOYMENT_VERSION_COREGRAPHICS_TVOS} - DEPLOYMENT_VERSION_WATCHOS ${SWIFTLIB_DEPLOYMENT_VERSION_COREGRAPHICS_WATCHOS} - INSTALL_IN_COMPONENT sdk-overlay -) diff --git a/stdlib/public/Darwin/CoreGraphics/CoreGraphics.swift b/stdlib/public/Darwin/CoreGraphics/CoreGraphics.swift deleted file mode 100644 index e4c0d27758243..0000000000000 --- a/stdlib/public/Darwin/CoreGraphics/CoreGraphics.swift +++ /dev/null @@ -1,702 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import CoreGraphics -import Darwin - -//===----------------------------------------------------------------------===// -// CGAffineTransform -//===----------------------------------------------------------------------===// - -extension CGAffineTransform: Equatable { - public static func ==(lhs: CGAffineTransform, - rhs: CGAffineTransform) -> Bool { - return lhs.__equalTo(rhs) - } -} - -//===----------------------------------------------------------------------===// -// CGColor -//===----------------------------------------------------------------------===// - -extension CGColor { - @available(macOS 10.3, iOS 2.0, *) - public var components: [CGFloat]? { - guard let pointer = self.__unsafeComponents else { return nil } - let buffer = UnsafeBufferPointer(start: pointer, count: self.numberOfComponents) - return Array(buffer) - } - -#if os(macOS) - public class var white: CGColor - { return CGColor.__constantColor(for: CGColor.__whiteColorName)! } - - public class var black: CGColor - { return CGColor.__constantColor(for: CGColor.__blackColorName)! } - - public class var clear: CGColor - { return CGColor.__constantColor(for: CGColor.__clearColorName)! } -#endif -} - -public protocol _CGColorInitTrampoline { -#if os(macOS) - init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -#else - init?(colorSpace space: CGColorSpace, components: UnsafePointer) -#endif -} - -extension _CGColorInitTrampoline { - public init(_colorLiteralRed red: Float, green: Float, blue: Float, - alpha: Float) { - let red = CGFloat(red) - let green = CGFloat(green) - let blue = CGFloat(blue) - let alpha = CGFloat(alpha) - // This initializer used to call the CGColorCreateGenericRGB, which is - // known to Swift as CGColor(red:green:blue:alpha:). Unfortunately this API - // is not available on platforms other than macOS. It would be possible to - // replicate the exact functionality of that API using - // kGColorSpaceGenericRGB, but it is marked as unavailable for Swift. The - // next best option is to use an sRGB color space, which is available but - // was introduced a little later than the (now legacy) generic RGB color - // space. - // Should be OK, since this code only affects the playgrounds, where - // you can't really pick the OS version other than "what's currently - // shipping". -#if os(macOS) - self.init(red: red, green: green, blue: blue, alpha: alpha) -#else - if #available(iOS 9.0, tvOS 9.0, watchOS 2.0, *) { - guard let space = CGColorSpace(name: CGColorSpace.sRGB) else { - fatalError("Unable to create an sRGB color space") - } - self.init(colorSpace: space, components: [red, green, blue, alpha])! - } - else { - fatalError("Cannot create a CGColor on this version of OS") - } -#endif - } -} - -extension CGColor : _CGColorInitTrampoline, _ExpressibleByColorLiteral { } - -//===----------------------------------------------------------------------===// -// CGColorSpace -//===----------------------------------------------------------------------===// - -extension CGColorSpace { - public var colorTable: [UInt8]? { - guard self.model == .indexed else { return nil } - let components = self.baseColorSpace?.numberOfComponents ?? 1 - let count = self.__colorTableCount * components - return [UInt8](unsafeUninitializedCapacity: count) { buf, initializedCount in - self.__unsafeGetColorTable(buf.baseAddress!) - initializedCount = count - } - } -} - -//===----------------------------------------------------------------------===// -// CGContext -//===----------------------------------------------------------------------===// - -extension CGContext { - - public func setLineDash(phase: CGFloat, lengths: [CGFloat]) { - self.__setLineDash(phase: phase, lengths: lengths, count: lengths.count) - } - - public func move(to point: CGPoint) { - self.__moveTo(x: point.x, y: point.y) - } - - public func addLine(to point: CGPoint) { - self.__addLineTo(x: point.x, y: point.y) - } - - public func addCurve(to end: CGPoint, control1: CGPoint, control2: CGPoint) { - self.__addCurveTo(cp1x: control1.x, cp1y: control1.y, - cp2x: control2.x, cp2y: control2.y, endingAtX: end.x, y: end.y) - } - - public func addQuadCurve(to end: CGPoint, control: CGPoint) { - self.__addQuadCurveTo(cpx: control.x, cpy: control.y, - endingAtX: end.x, y: end.y) - } - - public func addRects(_ rects: [CGRect]) { - self.__addRects(rects, count: rects.count) - } - - public func addLines(between points: [CGPoint]) { - self.__addLines(between: points, count: points.count) - } - - public func addArc(center: CGPoint, radius: CGFloat, startAngle: CGFloat, - endAngle: CGFloat, clockwise: Bool) { - self.__addArc(centerX: center.x, y: center.y, radius: radius, - startAngle: startAngle, endAngle: endAngle, clockwise: clockwise ? 1 : 0) - } - - public func addArc(tangent1End: CGPoint, tangent2End: CGPoint, - radius: CGFloat) { - self.__addArc(x1: tangent1End.x, y1: tangent1End.y, - x2: tangent2End.x, y2: tangent2End.y, radius: radius) - } - - /// Fills the current path using the specified rule (winding by default). - /// - /// Any open subpath is implicitly closed. - public func fillPath(using rule: CGPathFillRule = .winding) { - switch rule { - case .winding: self.__fillPath() - case .evenOdd: self.__eoFillPath() - } - } - - /// Intersects the current path with the current clipping region and uses the - /// result as the new clipping region for subsequent drawing. - /// - /// Uses the specified fill rule (winding by default) to determine which - /// areas to treat as the interior of the clipping region. When evaluating - /// the path, any open subpath is implicitly closed. - public func clip(using rule: CGPathFillRule = .winding) { - switch rule { - case .winding: self.__clip() - case .evenOdd: self.__eoClip() - } - } - - public func fill(_ rects: [CGRect]) { - self.__fill(rects, count: rects.count) - } - - public func strokeLineSegments(between points: [CGPoint]) { - self.__strokeLineSegments(between: points, count: points.count) - } - - public func clip(to rects: [CGRect]) { - self.__clip(to: rects, count: rects.count) - } - - public func draw(_ image: CGImage, in rect: CGRect, byTiling: Bool = false) { - if byTiling { - self.__draw(in: rect, byTiling: image) - } else { - self.__draw(in: rect, image: image) - } - } - - public var textPosition: CGPoint { - get { return self.__textPosition } - set { self.__setTextPosition(x: newValue.x, y: newValue.y) } - } - - public func showGlyphs(_ glyphs: [CGGlyph], at positions: [CGPoint]) { - precondition(glyphs.count == positions.count) - self.__showGlyphs(glyphs, atPositions: positions, count: glyphs.count) - } - -} - -//===----------------------------------------------------------------------===// -// CGDataProvider -//===----------------------------------------------------------------------===// - -// TODO: replace init(UnsafePointer) with init(String) -// blocked on rdar://problem/27444567 - -//===----------------------------------------------------------------------===// -// CGDirectDisplay -//===----------------------------------------------------------------------===// - -#if os(macOS) -public func CGGetLastMouseDelta() -> (x: Int32, y: Int32) { - var pair: (x: Int32, y: Int32) = (0, 0) - __CGGetLastMouseDelta(&pair.x, &pair.y) - return pair -} -#endif - -//===----------------------------------------------------------------------===// -// CGGeometry -//===----------------------------------------------------------------------===// - -public extension CGPoint { - static var zero: CGPoint { - @_transparent // @fragile - get { return CGPoint(x: 0, y: 0) } - } - - @_transparent // @fragile - init(x: Int, y: Int) { - self.init(x: CGFloat(x), y: CGFloat(y)) - } - - @_transparent // @fragile - init(x: Double, y: Double) { - self.init(x: CGFloat(x), y: CGFloat(y)) - } - - init?(dictionaryRepresentation dict: CFDictionary) { - var point = CGPoint() - if CGPoint.__setFromDictionaryRepresentation(dict, &point) { - self = point - } else { - return nil - } - } -} - -extension CGPoint : CustomReflectable { - public var customMirror: Mirror { - return Mirror(self, children: ["x": x, "y": y], displayStyle: .`struct`) - } -} - -extension CGPoint : _CustomPlaygroundQuickLookable { - @available(*, deprecated, message: "CGPoint.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - return .point(Double(x), Double(y)) - } -} - -extension CGPoint : CustomDebugStringConvertible { - public var debugDescription: String { - return "(\(x), \(y))" - } -} - -extension CGPoint : Equatable { - @_transparent // @fragile - public static func == (lhs: CGPoint, rhs: CGPoint) -> Bool { - return lhs.x == rhs.x && lhs.y == rhs.y - } -} - -extension CGPoint : Codable { - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - let x = try container.decode(CGFloat.self) - let y = try container.decode(CGFloat.self) - self.init(x: x, y: y) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(x) - try container.encode(y) - } -} - -public extension CGSize { - static var zero: CGSize { - @_transparent // @fragile - get { return CGSize(width: 0, height: 0) } - } - - @_transparent // @fragile - init(width: Int, height: Int) { - self.init(width: CGFloat(width), height: CGFloat(height)) - } - - @_transparent // @fragile - init(width: Double, height: Double) { - self.init(width: CGFloat(width), height: CGFloat(height)) - } - - init?(dictionaryRepresentation dict: CFDictionary) { - var size = CGSize() - if CGSize.__setFromDictionaryRepresentation(dict, &size) { - self = size - } else { - return nil - } - } -} - -extension CGSize : CustomReflectable { - public var customMirror: Mirror { - return Mirror( - self, - children: ["width": width, "height": height], - displayStyle: .`struct`) - } -} - -extension CGSize : _CustomPlaygroundQuickLookable { - @available(*, deprecated, message: "CGSize.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - return .size(Double(width), Double(height)) - } -} - -extension CGSize : CustomDebugStringConvertible { - public var debugDescription : String { - return "(\(width), \(height))" - } -} - -extension CGSize : Equatable { - @_transparent // @fragile - public static func == (lhs: CGSize, rhs: CGSize) -> Bool { - return lhs.width == rhs.width && lhs.height == rhs.height - } -} - -extension CGSize : Codable { - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - let width = try container.decode(CGFloat.self) - let height = try container.decode(CGFloat.self) - self.init(width: width, height: height) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(width) - try container.encode(height) - } -} - -public extension CGVector { - static var zero: CGVector { - @_transparent // @fragile - get { return CGVector(dx: 0, dy: 0) } - } - - @_transparent // @fragile - init(dx: Int, dy: Int) { - self.init(dx: CGFloat(dx), dy: CGFloat(dy)) - } - - @_transparent // @fragile - init(dx: Double, dy: Double) { - self.init(dx: CGFloat(dx), dy: CGFloat(dy)) - } -} - -extension CGVector : Equatable { - @_transparent // @fragile - public static func == (lhs: CGVector, rhs: CGVector) -> Bool { - return lhs.dx == rhs.dx && lhs.dy == rhs.dy - } -} - -extension CGVector : CustomDebugStringConvertible { - public var debugDescription : String { - return "(\(dx), \(dy))" - } -} - -extension CGVector : Codable { - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - let dx = try container.decode(CGFloat.self) - let dy = try container.decode(CGFloat.self) - self.init(dx: dx, dy: dy) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(dx) - try container.encode(dy) - } -} - -public extension CGRect { - static var zero: CGRect { - @_transparent // @fragile - get { return CGRect(x: 0, y: 0, width: 0, height: 0) } - } - - @_transparent // @fragile - init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) { - self.init(origin: CGPoint(x: x, y: y), - size: CGSize(width: width, height: height)) - } - - @_transparent // @fragile - init(x: Double, y: Double, width: Double, height: Double) { - self.init(origin: CGPoint(x: x, y: y), - size: CGSize(width: width, height: height)) - } - - @_transparent // @fragile - init(x: Int, y: Int, width: Int, height: Int) { - self.init(origin: CGPoint(x: x, y: y), - size: CGSize(width: width, height: height)) - } - - init?(dictionaryRepresentation dict: CFDictionary) { - var rect = CGRect() - if CGRect.__setFromDictionaryRepresentation(dict, &rect) { - self = rect - } else { - return nil - } - } - - @_transparent // @fragile - func divided(atDistance: CGFloat, from fromEdge: CGRectEdge) - -> (slice: CGRect, remainder: CGRect) - { - var slice = CGRect.zero - var remainder = CGRect.zero - self.__divided(slice: &slice, remainder: &remainder, atDistance: atDistance, - from: fromEdge) - return (slice, remainder) - } - - @available(*, unavailable, renamed: "minX") - var x: CGFloat { return minX } - - @available(*, unavailable, renamed: "minY") - var y: CGFloat { return minY } -} - -extension CGRect : CustomReflectable { - public var customMirror: Mirror { - return Mirror( - self, - children: ["origin": origin, "size": size], - displayStyle: .`struct`) - } -} - -extension CGRect : _CustomPlaygroundQuickLookable { - @available(*, deprecated, message: "CGRect.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - return .rectangle( - Double(origin.x), Double(origin.y), - Double(size.width), Double(size.height)) - } -} - -extension CGRect : CustomDebugStringConvertible { - public var debugDescription : String { - return "(\(origin.x), \(origin.y), \(size.width), \(size.height))" - } -} - -extension CGRect : Equatable { - @_transparent // @fragile - public static func == (lhs: CGRect, rhs: CGRect) -> Bool { - return lhs.equalTo(rhs) - } -} - -extension CGRect : Codable { - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - let origin = try container.decode(CGPoint.self) - let size = try container.decode(CGSize.self) - self.init(origin: origin, size: size) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(origin) - try container.encode(size) - } -} - -extension CGAffineTransform { - public static var identity: CGAffineTransform { - @_transparent // @fragile - get { return CGAffineTransform(a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0) } - } -} - -extension CGAffineTransform : Codable { - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - let a = try container.decode(CGFloat.self) - let b = try container.decode(CGFloat.self) - let c = try container.decode(CGFloat.self) - let d = try container.decode(CGFloat.self) - let tx = try container.decode(CGFloat.self) - let ty = try container.decode(CGFloat.self) - self.init(a: a, b: b, c: c, d: d, tx: tx, ty: ty) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(a) - try container.encode(b) - try container.encode(c) - try container.encode(d) - try container.encode(tx) - try container.encode(ty) - } -} - -//===----------------------------------------------------------------------===// -// CGImage -//===----------------------------------------------------------------------===// - -extension CGImage { - public func copy(maskingColorComponents components: [CGFloat]) -> CGImage? { - return self.__copy(maskingColorComponents: components) - } -} - -//===----------------------------------------------------------------------===// -// CGLayer -//===----------------------------------------------------------------------===// - -// TODO: remove auxiliaryInfo parameter from CGLayer.init, -// or at least give it a default value (empty/nil) -// blocked on rdar://problem/27444567 -extension CGContext { - public func draw(_ layer: CGLayer, in rect: CGRect) { - self.__draw(in: rect, layer: layer) - } - - public func draw(_ layer: CGLayer, at point: CGPoint) { - self.__draw(at: point, layer: layer) - } -} - -//===----------------------------------------------------------------------===// -// CGPath & CGMutablePath -//===----------------------------------------------------------------------===// - -// TODO: Make this a nested type (CGPath.FillRule) -public enum CGPathFillRule: Int { - /// Nonzero winding number fill rule. - /// - /// This rule plots a ray from the interior of the region to be evaluated - /// toward the bounds of the drawing, and sums the closed path elements - /// that the ray crosses: +1 for counterclockwise paths, -1 for clockwise. - /// If the sum is zero, the region is left empty; if the sum is nonzero, - /// the region is filled. - case winding - - /// Even-Odd fill rule. - /// - /// This rule plots a ray from the interior of the region to be evaluated - /// toward the bounds of the drawing, and sums the closed path elements - /// that the ray crosses. - /// If the sum is an even numner, the region is left empty; if the sum is - /// an odd number, the region is filled. - case evenOdd -} - -extension CGPath { - public func copy(dashingWithPhase phase: CGFloat, lengths: [CGFloat], - transform: CGAffineTransform = .identity) -> CGPath { - return CGPath(__byDashing: self, transform: [transform], - phase: phase, lengths: lengths, count: lengths.count)! - // force unwrap / non-optional return ok: underlying func returns nil - // only on bad input that we've made impossible (self and transform) - } - - public func copy(strokingWithWidth lineWidth: CGFloat, lineCap: CGLineCap, - lineJoin: CGLineJoin, miterLimit: CGFloat, - transform: CGAffineTransform = .identity) -> CGPath { - return CGPath(__byStroking: self, transform: [transform], - lineWidth: lineWidth, lineCap: lineCap, lineJoin: lineJoin, - miterLimit: miterLimit)! - // force unwrap / non-optional return ok: underlying func returns nil - // only on bad input that we've made impossible (self and transform) - } - - public func contains(_ point: CGPoint, using rule: CGPathFillRule = .winding, - transform: CGAffineTransform = .identity) -> Bool { - return self.__containsPoint(transform: [transform], - point: point, eoFill: (rule == .evenOdd)) - } -} - -extension CGMutablePath { - - public func addRoundedRect(in rect: CGRect, cornerWidth: CGFloat, - cornerHeight: CGFloat, transform: CGAffineTransform = .identity) { - self.__addRoundedRect(transform: [transform], rect: rect, - cornerWidth: cornerWidth, cornerHeight: cornerHeight) - } - - public func move(to point: CGPoint, - transform: CGAffineTransform = .identity) { - self.__moveTo(transform: [transform], x: point.x, y: point.y) - } - - public func addLine(to point: CGPoint, - transform: CGAffineTransform = .identity) { - self.__addLineTo(transform: [transform], x: point.x, y: point.y) - } - - public func addQuadCurve(to end: CGPoint, control: CGPoint, - transform: CGAffineTransform = .identity) { - self.__addQuadCurve(transform: [transform], cpx: control.x, cpy: control.y, - endingAtX: end.x, y: end.y) - } - - public func addCurve(to end: CGPoint, control1: CGPoint, control2: CGPoint, - transform: CGAffineTransform = .identity) { - self.__addCurve(transform: [transform], cp1x: control1.x, cp1y: control1.y, - cp2x: control2.x, cp2y: control2.y, endingAtX: end.x, y: end.y) - } - - public func addRect(_ rect: CGRect, - transform: CGAffineTransform = .identity) { - self.__addRect(transform: [transform], rect: rect) - } - - public func addRects(_ rects: [CGRect], - transform: CGAffineTransform = .identity) { - self.__addRects(transform: [transform], rects: rects, count: rects.count) - } - - public func addLines(between points: [CGPoint], - transform: CGAffineTransform = .identity) { - self.__addLines(transform: [transform], - between: points, count: points.count) - } - - public func addEllipse(in rect: CGRect, - transform: CGAffineTransform = .identity) { - self.__addEllipse(transform: [transform], rect: rect) - } - - public func addRelativeArc(center: CGPoint, radius: CGFloat, - startAngle: CGFloat, delta: CGFloat, - transform: CGAffineTransform = .identity) { - self.__addRelativeArc(transform: [transform], x: center.x, y: center.y, - radius: radius, startAngle: startAngle, delta: delta) - } - - public func addArc(center: CGPoint, radius: CGFloat, - startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool, - transform: CGAffineTransform = .identity) { - self.__addArc(transform: [transform], x: center.x, y: center.y, - radius: radius, startAngle: startAngle, endAngle: endAngle, - clockwise: clockwise) - } - - public func addArc(tangent1End: CGPoint, tangent2End: CGPoint, - radius: CGFloat, transform: CGAffineTransform = .identity) { - self.__addArc(transform: [transform], x1: tangent1End.x, y1: tangent1End.y, - x2: tangent2End.x, y2: tangent2End.y, radius: radius) - } - - public func addPath(_ path: CGPath, - transform: CGAffineTransform = .identity) { - self.__addPath(transform: [transform], path: path) - } - -} - diff --git a/stdlib/public/Darwin/CoreGraphics/Private.swift b/stdlib/public/Darwin/CoreGraphics/Private.swift deleted file mode 100644 index 4b792088bd708..0000000000000 --- a/stdlib/public/Darwin/CoreGraphics/Private.swift +++ /dev/null @@ -1,229 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// Redeclarations of all SwiftPrivate symbols with appropriate markup, -// so that tools can help with migration - -// @available(*, unavailable, renamed:"DispatchQueue.init(label:attributes:target:)") -@available(*, unavailable, message:"Use == instead") -public func CGAffineTransformEqualToTransform(_ t1: CGAffineTransform, _ t2: CGAffineTransform) -> Bool - { fatalError() } - -@available(*, unavailable, message:"Use class var white/black/clear instead") -public func CGColorGetConstantColor(_ colorName: CFString?) -> CGColor? - { fatalError() } - -@available(*, unavailable, message:"Use == instead") -public func CGColorEqualToColor(_ color1: CGColor?, _ color2: CGColor?) -> Bool - { fatalError() } - -@available(*, unavailable, renamed:"getter:CGColor.components(self:)") -public func CGColorGetComponents(_ color: CGColor?) -> UnsafePointer - { fatalError() } - -@available(*, unavailable, message:"Use colorTable.count instead") -public func CGColorSpaceGetColorTableCount(_ space: CGColorSpace?) -> Int - { fatalError() } - -@available(*, unavailable, renamed:"CGColorSpace.colorTable(self:_:)") -public func CGColorSpaceGetColorTable(_ space: CGColorSpace?, _ table: UnsafeMutablePointer) - { fatalError() } - -@available(*, unavailable, message:"Use setLineDash(self:phase:lengths:)") -public func CGContextSetLineDash(_ c: CGContext?, _ phase: CGFloat, _ lengths: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, message:"Use move(to:) instead") -public func CGContextMoveToPoint(_ c: CGContext?, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addLine(to:) instead") -public func CGContextAddLineToPoint(_ c: CGContext?, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addCurve(to:control1:control2:) instead") -public func CGContextAddCurveToPoint(_ c: CGContext?, _ cp1x: CGFloat, _ cp1y: CGFloat, _ cp2x: CGFloat, _ cp2y: CGFloat, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addQuadCurve(to:control:)") -public func CGContextAddQuadCurveToPoint(_ c: CGContext?, _ cpx: CGFloat, _ cpy: CGFloat, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addRects(_:)") -public func CGContextAddRects(_ c: CGContext?, _ rects: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, message:"Use addLines(between:)") -public func CGContextAddLines(_ c: CGContext?, _ points: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, message:"Use addArc(center:radius:startAngle:endAngle:clockwise:)") -public func CGContextAddArc(_ c: CGContext?, _ x: CGFloat, _ y: CGFloat, _ radius: CGFloat, _ startAngle: CGFloat, _ endAngle: CGFloat, _ clockwise: Int32) - { fatalError() } - -@available(*, unavailable, message:"Use addArc(self:x1:y1:x2:y2:radius:)") -public func CGContextAddArcToPoint(_ c: CGContext?, _ x1: CGFloat, _ y1: CGFloat, _ x2: CGFloat, _ y2: CGFloat, _ radius: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use fill(self:_:count:)") -public func CGContextFillRects(_ c: CGContext?, _ rects: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, message:"Use strokeLineSegments(self:between:count:)") -public func CGContextStrokeLineSegments(_ c: CGContext?, _ points: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, message:"Use clip(to:)") -public func CGContextClipToRects(_ c: CGContext?, _ rects: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, message:"Use draw(_:in:)") -public func CGContextDrawImage(_ c: CGContext?, _ rect: CGRect, _ image: CGImage?) - { fatalError() } - -@available(*, unavailable, message:"Use draw(_:in:byTiling:)") -public func CGContextDrawTiledImage(_ c: CGContext?, _ rect: CGRect, _ image: CGImage?) - { fatalError() } - -@available(*, unavailable, renamed:"getter:CGContext.textPosition(self:)") -public func CGContextGetTextPosition(_ c: CGContext?) -> CGPoint - { fatalError() } - -@available(*, unavailable, message:"Use var textPosition") -public func CGContextSetTextPosition(_ c: CGContext?, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use showGlyphs(_:at:)") -public func CGContextShowGlyphsAtPositions(_ c: CGContext?, _ glyphs: UnsafePointer, _ Lpositions: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, renamed:"CGContext.fillPath(self:)") -public func CGContextFillPath(_ c: CGContext?) - { fatalError() } - -@available(*, unavailable, message:"Use fillPath(using:)") -public func CGContextEOFillPath(_ c: CGContext?) - { fatalError() } - -@available(*, unavailable, renamed:"CGContext.clip(self:)") -public func CGContextClip(_ c: CGContext?) - { fatalError() } - -@available(*, unavailable, message:"Use clip(using:)") -public func CGContextEOClip(_ c: CGContext?) - { fatalError() } - -@available(*, unavailable, renamed:"CGGetLastMouseDelta") // different type -public func CGGetLastMouseDelta(_ deltaX: UnsafeMutablePointer?, _ deltaY: UnsafeMutablePointer?) - { fatalError() } - -@available(*, unavailable, message:"Use divided(atDistance:from:)") -public func CGRectDivide(_ rect: CGRect, _ slice: UnsafeMutablePointer, _ remainder: UnsafeMutablePointer, _ amount: CGFloat, _ edge: CGRectEdge) - { fatalError() } - -@available(*, unavailable, message:"Use CGPoint.init(dictionaryRepresentation:)") -public func CGPointMakeWithDictionaryRepresentation(_ dict: CFDictionary?, _ point: UnsafeMutablePointer) -> Bool - { fatalError() } - -@available(*, unavailable, message:"Use CGSize.init(dictionaryRepresentation:)") -public func CGSizeMakeWithDictionaryRepresentation(_ dict: CFDictionary?, _ size: UnsafeMutablePointer) -> Bool - { fatalError() } - -@available(*, unavailable, message:"Use CGRect.init(dictionaryRepresentation:)") -public func CGRectMakeWithDictionaryRepresentation(_ dict: CFDictionary?, _ rect: UnsafeMutablePointer) -> Bool - { fatalError() } - -@available(*, unavailable, renamed:"CGImage.copy(self:maskingColorComponents:)") -public func CGImageCreateWithMaskingColors(_ image: CGImage?, _ components: UnsafePointer) -> CGImage? - { fatalError() } - -@available(*, unavailable, message:"Use draw(_:in:)") -public func CGContextDrawLayerInRect(_ context: CGContext?, _ rect: CGRect, _ layer: CGLayer?) - { fatalError() } - -@available(*, unavailable, message:"Use draw(_:at:)") -public func CGContextDrawLayerAtPoint(_ context: CGContext?, _ point: CGPoint, _ layer: CGLayer?) - { fatalError() } - -@available(*, unavailable, message:"Use copy(byDashingWithPhase:lengths:transform:)") -public func CGPathCreateCopyByDashingPath(_ path: CGPath?, _ transform: UnsafePointer, _ phase: CGFloat, _ lengths: UnsafePointer, _ count: Int) -> CGPath? - { fatalError() } - -@available(*, unavailable, message:"Use copy(byStroking:lineWidth:lineCap:lineJoin:miterLimit:transform:)") -public func CGPathCreateCopyByStrokingPath(_ path: CGPath?, _ transform: UnsafePointer, _ lineWidth: CGFloat, _ lineCap: CGLineCap, _ lineJoin: CGLineJoin, _ miterLimit: CGFloat) -> CGPath? - { fatalError() } - -@available(*, unavailable, message:"Use == instead") -public func CGPathEqualToPath(_ path1: CGPath?, _ path2: CGPath?) -> Bool - { fatalError() } - -@available(*, unavailable, message:"Use move(to:transform:)") -public func CGPathMoveToPoint(_ path: CGMutablePath?, _ m: UnsafePointer, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addLine(to:transform:)") -public func CGPathAddLineToPoint(_ path: CGMutablePath?, _ m: UnsafePointer, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addCurve(to:control1:control2:transform:)") -public func CGPathAddCurveToPoint(_ path: CGMutablePath?, _ m: UnsafePointer, _ cp1x: CGFloat, _ cp1y: CGFloat, _ cp2x: CGFloat, _ cp2y: CGFloat, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addQuadCurve(to:control:transform:)") -public func CGPathAddQuadCurveToPoint(_ path: CGMutablePath?, _ m: UnsafePointer, _ cpx: CGFloat, _ cpy: CGFloat, _ x: CGFloat, _ y: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addRect(_:transform:)") -public func CGPathAddRect(_ path: CGMutablePath?, _ m: UnsafePointer, _ rect: CGRect) - { fatalError() } - -@available(*, unavailable, message:"Use addRects(_:transform:)") -public func CGPathAddRects(_ path: CGMutablePath?, _ m: UnsafePointer, _ rects: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, message:"Use addLines(between:transform:)") -public func CGPathAddLines(_ path: CGMutablePath?, _ m: UnsafePointer, _ points: UnsafePointer, _ count: Int) - { fatalError() } - -@available(*, unavailable, message:"Use addEllipse(rect:transform:)") -public func CGPathAddEllipseInRect(_ path: CGMutablePath?, _ m: UnsafePointer, _ rect: CGRect) - { fatalError() } - -@available(*, unavailable, message:"Use addRelativeArc(center:radius:startAngle:delta:transform:)") -public func CGPathAddRelativeArc(_ path: CGMutablePath?, _ matrix: UnsafePointer, _ x: CGFloat, _ y: CGFloat, _ radius: CGFloat, _ startAngle: CGFloat, _ delta: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addArc(center:radius:startAngle:endAngle:clockwise:transform:)") -public func CGPathAddArc(_ path: CGMutablePath?, _ m: UnsafePointer, _ x: CGFloat, _ y: CGFloat, _ radius: CGFloat, _ startAngle: CGFloat, _ endAngle: CGFloat, _ clockwise: Bool) - { fatalError() } - -@available(*, unavailable, message:"Use addArc(tangent1End:tangent2End:radius:transform:)") -public func CGPathAddArcToPoint(_ path: CGMutablePath?, _ m: UnsafePointer, _ x1: CGFloat, _ y1: CGFloat, _ x2: CGFloat, _ y2: CGFloat, _ radius: CGFloat) - { fatalError() } - -@available(*, unavailable, message:"Use addPath(_:transform:)") -public func CGPathAddPath(_ path1: CGMutablePath?, _ m: UnsafePointer, _ path2: CGPath?) - { fatalError() } - -@available(*, unavailable, message:"Use CGColor.white") // retyped -public var kCGColorWhite: CFString - { fatalError() } - -@available(*, unavailable, message:"Use CGColor.black") // retyped -public var kCGColorBlack: CFString - { fatalError() } - -@available(*, unavailable, message:"Use CGColor.clear") // retyped -public var kCGColorClear: CFString - { fatalError() } - -// TODO: also add migration support from previous Swift3 betas? diff --git a/stdlib/public/Darwin/Dispatch/Block.swift b/stdlib/public/Darwin/Dispatch/Block.swift deleted file mode 100644 index 0db180fc38e52..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Block.swift +++ /dev/null @@ -1,107 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_implementationOnly import _SwiftDispatchOverlayShims - -public struct DispatchWorkItemFlags : OptionSet, RawRepresentable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let barrier = DispatchWorkItemFlags(rawValue: 0x1) - - @available(macOS 10.10, iOS 8.0, *) - public static let detached = DispatchWorkItemFlags(rawValue: 0x2) - - @available(macOS 10.10, iOS 8.0, *) - public static let assignCurrentContext = DispatchWorkItemFlags(rawValue: 0x4) - - @available(macOS 10.10, iOS 8.0, *) - public static let noQoS = DispatchWorkItemFlags(rawValue: 0x8) - - @available(macOS 10.10, iOS 8.0, *) - public static let inheritQoS = DispatchWorkItemFlags(rawValue: 0x10) - - @available(macOS 10.10, iOS 8.0, *) - public static let enforceQoS = DispatchWorkItemFlags(rawValue: 0x20) -} - -// NOTE: older overlays had Dispatch.DispatchWorkItem as the ObjC name. -// The two must coexist, so it was renamed. The old name must not be -// used in the new runtime. _TtC8Dispatch17_DispatchWorkItem is the -// mangled name for Dispatch._DispatchWorkItem. -@available(macOS 10.10, iOS 8.0, *) -@_objcRuntimeName(_TtC8Dispatch17_DispatchWorkItem) -public class DispatchWorkItem { - internal var _block: _DispatchBlock - - public init(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], block: @escaping @convention(block) () -> Void) { - _block = _swift_dispatch_block_create_with_qos_class( - __dispatch_block_flags_t(rawValue: flags.rawValue), - qos.qosClass.rawValue, Int32(qos.relativePriority), block) - } - - // Used by DispatchQueue.synchronously to provide a path through - // dispatch_block_t, as we know the lifetime of the block in question. - internal init(flags: DispatchWorkItemFlags = [], noescapeBlock: () -> Void) { - _block = _swift_dispatch_block_create_noescape( - __dispatch_block_flags_t(rawValue: flags.rawValue), noescapeBlock) - } - - public func perform() { - _block() - } - - public func wait() { - _ = _swift_dispatch_block_wait(_block, DispatchTime.distantFuture.rawValue) - } - - public func wait(timeout: DispatchTime) -> DispatchTimeoutResult { - return _swift_dispatch_block_wait(_block, timeout.rawValue) == 0 ? .success : .timedOut - } - - public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult { - return _swift_dispatch_block_wait(_block, wallTimeout.rawValue) == 0 ? .success : .timedOut - } - - public func notify( - qos: DispatchQoS = .unspecified, - flags: DispatchWorkItemFlags = [], - queue: DispatchQueue, - execute: @escaping @convention(block) () -> Void) - { - if qos != .unspecified || !flags.isEmpty { - let item = DispatchWorkItem(qos: qos, flags: flags, block: execute) - _swift_dispatch_block_notify(_block, queue, item._block) - } else { - _swift_dispatch_block_notify(_block, queue, execute) - } - } - - public func notify(queue: DispatchQueue, execute: DispatchWorkItem) { - _swift_dispatch_block_notify(_block, queue, execute._block) - } - - public func cancel() { - _swift_dispatch_block_cancel(_block) - } - - public var isCancelled: Bool { - return _swift_dispatch_block_testcancel(_block) != 0 - } -} - -/// The dispatch_block_t typealias is different from usual closures in that it -/// uses @convention(block). This is to avoid unnecessary bridging between -/// C blocks and Swift closures, which interferes with dispatch APIs that depend -/// on the referential identity of a block. Particularly, dispatch_block_create. -internal typealias _DispatchBlock = @convention(block) () -> Void - diff --git a/stdlib/public/Darwin/Dispatch/CMakeLists.txt b/stdlib/public/Darwin/Dispatch/CMakeLists.txt deleted file mode 100644 index 83ec1e89e9dd4..0000000000000 --- a/stdlib/public/Darwin/Dispatch/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -cmake_minimum_required(VERSION 3.4.3) -include("../../../../cmake/modules/StandaloneOverlay.cmake") - -add_swift_target_library(swiftDispatch ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY - Dispatch.swift - Dispatch.mm - Block.swift - - Data.swift - IO.swift - Private.swift - Queue.swift - Source.swift - Schedulers+DispatchQueue.swift - Time.swift - - "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" - - SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - SWIFT_MODULE_DEPENDS_OSX Darwin ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_IOS Darwin ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_TVOS Darwin ObjectiveC # auto-updated - SWIFT_MODULE_DEPENDS_WATCHOS Darwin ObjectiveC # auto-updated - FRAMEWORK_DEPENDS_WEAK Combine - - DEPLOYMENT_VERSION_OSX ${SWIFTLIB_DEPLOYMENT_VERSION_DISPATCH_OSX} - DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_DISPATCH_IOS} - DEPLOYMENT_VERSION_TVOS ${SWIFTLIB_DEPLOYMENT_VERSION_DISPATCH_TVOS} - DEPLOYMENT_VERSION_WATCHOS ${SWIFTLIB_DEPLOYMENT_VERSION_DISPATCH_WATCHOS} - INSTALL_IN_COMPONENT sdk-overlay -) diff --git a/stdlib/public/Darwin/Dispatch/Data.swift b/stdlib/public/Darwin/Dispatch/Data.swift deleted file mode 100644 index 186aa0a87fdf0..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Data.swift +++ /dev/null @@ -1,365 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_implementationOnly import _SwiftDispatchOverlayShims - -public struct DispatchData : RandomAccessCollection, _ObjectiveCBridgeable { - public typealias Iterator = DispatchDataIterator - public typealias Index = Int - public typealias Indices = DefaultIndices - - public static let empty: DispatchData = DispatchData(data: _swift_dispatch_data_empty()) - - public enum Deallocator { - /// Use `free` - case free - - /// Use `munmap` - case unmap - - /// A custom deallocator - case custom(DispatchQueue?, @convention(block) () -> Void) - - fileprivate var _deallocator: (DispatchQueue?, @convention(block) () -> Void) { - switch self { - case .free: return (nil, _swift_dispatch_data_destructor_free()) - case .unmap: return (nil, _swift_dispatch_data_destructor_munmap()) - case .custom(let q, let b): return (q, b) - } - } - } - - fileprivate var __wrapped: __DispatchData - - /// Initialize a `Data` with copied memory content. - /// - /// - parameter bytes: A pointer to the memory. It will be copied. - /// - parameter count: The number of bytes to copy. - @available(swift, deprecated: 4, message: "Use init(bytes: UnsafeRawBufferPointer) instead") - public init(bytes buffer: UnsafeBufferPointer) { - __wrapped = buffer.baseAddress == nil ? _swift_dispatch_data_empty() - : _swift_dispatch_data_create(buffer.baseAddress!, buffer.count, nil, - _swift_dispatch_data_destructor_default()) as! __DispatchData - } - - /// Initialize a `Data` with copied memory content. - /// - /// - parameter bytes: A pointer to the memory. It will be copied. - /// - parameter count: The number of bytes to copy. - public init(bytes buffer: UnsafeRawBufferPointer) { - __wrapped = buffer.baseAddress == nil ? _swift_dispatch_data_empty() - : _swift_dispatch_data_create(buffer.baseAddress!, buffer.count, nil, - _swift_dispatch_data_destructor_default()) as! __DispatchData - } - - /// Initialize a `Data` without copying the bytes. - /// - /// - parameter bytes: A pointer to the bytes. - /// - parameter count: The size of the bytes. - /// - parameter deallocator: Specifies the mechanism to free the indicated buffer. - @available(swift, deprecated: 4, message: "Use init(bytesNoCopy: UnsafeRawBufferPointer, deallocater: Deallocator) instead") - public init(bytesNoCopy bytes: UnsafeBufferPointer, deallocator: Deallocator = .free) { - let (q, b) = deallocator._deallocator - __wrapped = bytes.baseAddress == nil ? _swift_dispatch_data_empty() - : _swift_dispatch_data_create(bytes.baseAddress!, bytes.count, q, b) as! __DispatchData - } - - /// Initialize a `Data` without copying the bytes. - /// - /// - parameter bytes: A pointer to the bytes. - /// - parameter count: The size of the bytes. - /// - parameter deallocator: Specifies the mechanism to free the indicated buffer. - public init(bytesNoCopy bytes: UnsafeRawBufferPointer, deallocator: Deallocator = .free) { - let (q, b) = deallocator._deallocator - __wrapped = bytes.baseAddress == nil ? _swift_dispatch_data_empty() - : _swift_dispatch_data_create(bytes.baseAddress!, bytes.count, q, b) as! __DispatchData - } - - internal init(data: __DispatchData) { - __wrapped = data - } - - public var count: Int { - return __dispatch_data_get_size(__wrapped) - } - - public func withUnsafeBytes( - body: (UnsafePointer) throws -> Result) rethrows -> Result - { - var ptr: UnsafeRawPointer? - var size = 0 - let data = __dispatch_data_create_map(__wrapped, &ptr, &size) - let contentPtr = ptr!.bindMemory( - to: ContentType.self, capacity: size / MemoryLayout.stride) - defer { _fixLifetime(data) } - return try body(contentPtr) - } - - @available(swift 4.2) - public func enumerateBytes( - _ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Int, _ stop: inout Bool) -> Void) - { - enumerateBytesCommon(block) - } - - @available(swift, obsoleted: 4.2, renamed: "enumerateBytes(_:)") - public func enumerateBytes( - block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Int, _ stop: inout Bool) -> Void) - { - enumerateBytesCommon(block) - } - - private func enumerateBytesCommon( - _ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Int, _ stop: inout Bool) -> Void) - { - _swift_dispatch_data_apply(__wrapped) { (_, offset: Int, ptr: UnsafeRawPointer, size: Int) in - let bytePtr = ptr.bindMemory(to: UInt8.self, capacity: size) - let bp = UnsafeBufferPointer(start: bytePtr, count: size) - var stop = false - block(bp, offset, &stop) - return stop ? 0 : 1 - } - } - - /// Append bytes to the data. - /// - /// - parameter bytes: A pointer to the bytes to copy in to the data. - /// - parameter count: The number of bytes to copy. - @available(swift, deprecated: 4, message: "Use append(_: UnsafeRawBufferPointer) instead") - public mutating func append(_ bytes: UnsafePointer, count: Int) { - let data = _swift_dispatch_data_create(bytes, count, nil, _swift_dispatch_data_destructor_default()) as! __DispatchData - self.append(DispatchData(data: data)) - } - - /// Append bytes to the data. - /// - /// - parameter bytes: A pointer to the bytes to copy in to the data. - /// - parameter count: The number of bytes to copy. - public mutating func append(_ bytes: UnsafeRawBufferPointer) { - // Nil base address does nothing. - guard bytes.baseAddress != nil else { return } - let data = _swift_dispatch_data_create(bytes.baseAddress!, bytes.count, nil, _swift_dispatch_data_destructor_default()) as! __DispatchData - self.append(DispatchData(data: data)) - } - - /// Append data to the data. - /// - /// - parameter data: The data to append to this data. - public mutating func append(_ other: DispatchData) { - let data = __dispatch_data_create_concat(__wrapped, other.__wrapped) - __wrapped = data - } - - /// Append a buffer of bytes to the data. - /// - /// - parameter buffer: The buffer of bytes to append. The size is calculated from `SourceType` and `buffer.count`. - public mutating func append(_ buffer : UnsafeBufferPointer) { - self.append(UnsafeRawBufferPointer(buffer)) - } - - private func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: Range) { - var copiedCount = 0 - if range.isEmpty { return } - let rangeSize = range.count - __dispatch_data_apply(__wrapped) { (_, offset: Int, ptr: UnsafeRawPointer, size: Int) in - if offset >= range.endIndex { return false } // This region is after endIndex - let copyOffset = range.startIndex > offset ? range.startIndex - offset : 0 // offset of first byte, in this region - if copyOffset >= size { return true } // This region is before startIndex - let count = Swift.min(rangeSize - copiedCount, size - copyOffset) - memcpy(pointer + copiedCount, ptr + copyOffset, count) - copiedCount += count - return copiedCount < rangeSize - } - } - - /// Copy the contents of the data to a pointer. - /// - /// - parameter pointer: A pointer to the buffer you wish to copy the bytes into. - /// - parameter count: The number of bytes to copy. - /// - warning: This method does not verify that the contents at pointer have enough space to hold `count` bytes. - @available(swift, deprecated: 4, message: "Use copyBytes(to: UnsafeMutableRawBufferPointer, count: Int) instead") - public func copyBytes(to pointer: UnsafeMutablePointer, count: Int) { - _copyBytesHelper(to: pointer, from: 0..) instead") - public func copyBytes(to pointer: UnsafeMutablePointer, from range: Range) { - _copyBytesHelper(to: pointer, from: range) - } - - /// Copy a subset of the contents of the data to a pointer. - /// - /// - parameter pointer: A pointer to the buffer you wish to copy the bytes into. The buffer must be large - /// enough to hold `count` bytes. - /// - parameter range: The range in the `Data` to copy. - public func copyBytes(to pointer: UnsafeMutableRawBufferPointer, from range: Range) { - assert(range.count <= pointer.count, "Buffer too small to copy \(range.count) bytes") - guard pointer.baseAddress != nil else { return } - _copyBytesHelper(to: pointer.baseAddress!, from: range) - } - - /// Copy the contents of the data into a buffer. - /// - /// This function copies the bytes in `range` from the data into the buffer. If the count of the `range` is greater than `MemoryLayout.stride * buffer.count` then the first N bytes will be copied into the buffer. - /// - precondition: The range must be within the bounds of the data. Otherwise `fatalError` is called. - /// - parameter buffer: A buffer to copy the data into. - /// - parameter range: A range in the data to copy into the buffer. If the range is empty, this function will return 0 without copying anything. If the range is nil, as much data as will fit into `buffer` is copied. - /// - returns: Number of bytes copied into the destination buffer. - public func copyBytes(to buffer: UnsafeMutableBufferPointer, from range: Range? = nil) -> Int { - let cnt = count - guard cnt > 0 else { return 0 } - - let copyRange : Range - if let r = range { - guard !r.isEmpty else { return 0 } - precondition(r.startIndex >= 0) - precondition(r.startIndex < cnt, "The range is outside the bounds of the data") - - precondition(r.endIndex >= 0) - precondition(r.endIndex <= cnt, "The range is outside the bounds of the data") - - copyRange = r.startIndex..<(r.startIndex + Swift.min(buffer.count * MemoryLayout.stride, r.count)) - } else { - copyRange = 0...stride, cnt) - } - - guard !copyRange.isEmpty else { return 0 } - - _copyBytesHelper(to: buffer.baseAddress!, from: copyRange) - return copyRange.count - } - - /// Sets or returns the byte at the specified index. - public subscript(index: Index) -> UInt8 { - var offset = 0 - let subdata = __dispatch_data_copy_region(__wrapped, index, &offset) - - var ptr: UnsafeRawPointer? - var size = 0 - let map = __dispatch_data_create_map(subdata, &ptr, &size) - defer { _fixLifetime(map) } - - return ptr!.load(fromByteOffset: index - offset, as: UInt8.self) - } - - public subscript(bounds: Range) -> Slice { - return Slice(base: self, bounds: bounds) - } - - /// Return a new copy of the data in a specified range. - /// - /// - parameter range: The range to copy. - public func subdata(in range: Range) -> DispatchData { - let subrange = __dispatch_data_create_subrange( - __wrapped, range.startIndex, range.endIndex - range.startIndex) - return DispatchData(data: subrange) - } - - public func region(location: Int) -> (data: DispatchData, offset: Int) { - var offset: Int = 0 - let data = __dispatch_data_copy_region(__wrapped, location, &offset) - return (DispatchData(data: data), offset) - } - - public var startIndex: Index { - return 0 - } - - public var endIndex: Index { - return count - } - - public func index(before i: Index) -> Index { - return i - 1 - } - - public func index(after i: Index) -> Index { - return i + 1 - } - - /// An iterator over the contents of the data. - /// - /// The iterator will increment byte-by-byte. - public func makeIterator() -> DispatchData.Iterator { - return DispatchDataIterator(_data: self) - } -} - -public struct DispatchDataIterator : IteratorProtocol, Sequence { - public typealias Element = UInt8 - - /// Create an iterator over the given DispatchData - public init(_data: DispatchData) { - var ptr: UnsafeRawPointer? - self._count = 0 - self._data = __dispatch_data_create_map( - _data as __DispatchData, &ptr, &self._count) - self._ptr = ptr - self._position = _data.startIndex - - // The only time we expect a 'nil' pointer is when the data is empty. - assert(self._ptr != nil || self._count == self._position) - } - - /// Advance to the next element and return it, or `nil` if no next - /// element exists. - public mutating func next() -> DispatchData.Element? { - if _position == _count { return nil } - let element = _ptr.load(fromByteOffset: _position, as: UInt8.self) - _position = _position + 1 - return element - } - - internal let _data: __DispatchData - internal var _ptr: UnsafeRawPointer! - internal var _count: Int - internal var _position: DispatchData.Index -} - -extension DispatchData { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> __DispatchData { - return __wrapped - } - - public static func _forceBridgeFromObjectiveC(_ input: __DispatchData, result: inout DispatchData?) { - result = DispatchData(data: input) - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: __DispatchData, result: inout DispatchData?) -> Bool { - result = DispatchData(data: input) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: __DispatchData?) -> DispatchData { - var result: DispatchData? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} diff --git a/stdlib/public/Darwin/Dispatch/Dispatch.mm b/stdlib/public/Darwin/Dispatch/Dispatch.mm deleted file mode 100644 index 6b487edac4864..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Dispatch.mm +++ /dev/null @@ -1,57 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include -#include -#include -#include - -@protocol OS_dispatch_source; -@protocol OS_dispatch_source_mach_send; -@protocol OS_dispatch_source_mach_recv; -@protocol OS_dispatch_source_memorypressure; -@protocol OS_dispatch_source_proc; -@protocol OS_dispatch_source_read; -@protocol OS_dispatch_source_signal; -@protocol OS_dispatch_source_timer; -@protocol OS_dispatch_source_data_add; -@protocol OS_dispatch_source_data_or; -@protocol OS_dispatch_source_vnode; -@protocol OS_dispatch_source_write; - -// #include -__attribute__((constructor)) -static void _dispatch_overlay_constructor() { - Class source = objc_lookUpClass("OS_dispatch_source"); - if (source) { - class_addProtocol(source, @protocol(OS_dispatch_source)); - class_addProtocol(source, @protocol(OS_dispatch_source_mach_send)); - class_addProtocol(source, @protocol(OS_dispatch_source_mach_recv)); - class_addProtocol(source, @protocol(OS_dispatch_source_memorypressure)); - class_addProtocol(source, @protocol(OS_dispatch_source_proc)); - class_addProtocol(source, @protocol(OS_dispatch_source_read)); - class_addProtocol(source, @protocol(OS_dispatch_source_signal)); - class_addProtocol(source, @protocol(OS_dispatch_source_timer)); - class_addProtocol(source, @protocol(OS_dispatch_source_data_add)); - class_addProtocol(source, @protocol(OS_dispatch_source_data_or)); - class_addProtocol(source, @protocol(OS_dispatch_source_vnode)); - class_addProtocol(source, @protocol(OS_dispatch_source_write)); - } -} - -extern "C" void -_swift_dispatch_source_create_abort(void) -{ - swift::swift_reportError(0, - "dispatch_source_create returned NULL, invalid parameters passed to dispatch_source_create"); - abort(); -} diff --git a/stdlib/public/Darwin/Dispatch/Dispatch.swift b/stdlib/public/Darwin/Dispatch/Dispatch.swift deleted file mode 100644 index da987ea2cbff7..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Dispatch.swift +++ /dev/null @@ -1,180 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Dispatch -@_implementationOnly import _SwiftDispatchOverlayShims - -/// dispatch_assert - -@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) -public enum DispatchPredicate { - case onQueue(DispatchQueue) - case onQueueAsBarrier(DispatchQueue) - case notOnQueue(DispatchQueue) -} - -@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) -public func _dispatchPreconditionTest(_ condition: DispatchPredicate) -> Bool { - switch condition { - case .onQueue(let q): - __dispatch_assert_queue(q) - case .onQueueAsBarrier(let q): - __dispatch_assert_queue_barrier(q) - case .notOnQueue(let q): - __dispatch_assert_queue_not(q) - } - return true -} - -@_transparent -@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) -public func dispatchPrecondition(condition: @autoclosure () -> DispatchPredicate) { - // precondition is able to determine release-vs-debug asserts where the overlay - // cannot, so formulating this into a call that we can call with precondition() - precondition(_dispatchPreconditionTest(condition()), "dispatchPrecondition failure") -} - -/// qos_class_t - -public struct DispatchQoS : Equatable { - public let qosClass: QoSClass - public let relativePriority: Int - - @available(macOS 10.10, iOS 8.0, *) - public static let background = DispatchQoS(qosClass: .background, relativePriority: 0) - - @available(macOS 10.10, iOS 8.0, *) - public static let utility = DispatchQoS(qosClass: .utility, relativePriority: 0) - - @available(macOS 10.10, iOS 8.0, *) - public static let `default` = DispatchQoS(qosClass: .default, relativePriority: 0) - - @available(macOS 10.10, iOS 8.0, *) - public static let userInitiated = DispatchQoS(qosClass: .userInitiated, relativePriority: 0) - - @available(macOS 10.10, iOS 8.0, *) - public static let userInteractive = DispatchQoS(qosClass: .userInteractive, relativePriority: 0) - - public static let unspecified = DispatchQoS(qosClass: .unspecified, relativePriority: 0) - - public enum QoSClass { - @available(macOS 10.10, iOS 8.0, *) - case background - - @available(macOS 10.10, iOS 8.0, *) - case utility - - @available(macOS 10.10, iOS 8.0, *) - case `default` - - @available(macOS 10.10, iOS 8.0, *) - case userInitiated - - @available(macOS 10.10, iOS 8.0, *) - case userInteractive - - case unspecified - - @available(macOS 10.10, iOS 8.0, *) - public init?(rawValue: qos_class_t) { - switch rawValue { - case QOS_CLASS_BACKGROUND: self = .background - case QOS_CLASS_UTILITY: self = .utility - case QOS_CLASS_DEFAULT: self = .default - case QOS_CLASS_USER_INITIATED: self = .userInitiated - case QOS_CLASS_USER_INTERACTIVE: self = .userInteractive - case QOS_CLASS_UNSPECIFIED: self = .unspecified - default: return nil - } - } - - @available(macOS 10.10, iOS 8.0, *) - public var rawValue: qos_class_t { - switch self { - case .background: return QOS_CLASS_BACKGROUND - case .utility: return QOS_CLASS_UTILITY - case .default: return QOS_CLASS_DEFAULT - case .userInitiated: return QOS_CLASS_USER_INITIATED - case .userInteractive: return QOS_CLASS_USER_INTERACTIVE - case .unspecified: return QOS_CLASS_UNSPECIFIED - } - } - } - - public init(qosClass: QoSClass, relativePriority: Int) { - self.qosClass = qosClass - self.relativePriority = relativePriority - } - - public static func ==(a: DispatchQoS, b: DispatchQoS) -> Bool { - return a.qosClass == b.qosClass && a.relativePriority == b.relativePriority - } -} - -/// -@frozen -public enum DispatchTimeoutResult { - case success - case timedOut -} - -/// dispatch_group - -extension DispatchGroup { - public func notify(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], queue: DispatchQueue, execute work: @escaping @convention(block) () -> Void) { - if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { - let item = DispatchWorkItem(qos: qos, flags: flags, block: work) - _swift_dispatch_group_notify(self, queue, item._block) - } else { - _swift_dispatch_group_notify(self, queue, work) - } - } - - @available(macOS 10.10, iOS 8.0, *) - public func notify(queue: DispatchQueue, work: DispatchWorkItem) { - _swift_dispatch_group_notify(self, queue, work._block) - } - - public func wait() { - _ = __dispatch_group_wait(self, DispatchTime.distantFuture.rawValue) - } - - public func wait(timeout: DispatchTime) -> DispatchTimeoutResult { - return __dispatch_group_wait(self, timeout.rawValue) == 0 ? .success : .timedOut - } - - public func wait(wallTimeout timeout: DispatchWallTime) -> DispatchTimeoutResult { - return __dispatch_group_wait(self, timeout.rawValue) == 0 ? .success : .timedOut - } -} - -/// dispatch_semaphore - -extension DispatchSemaphore { - @discardableResult - public func signal() -> Int { - return __dispatch_semaphore_signal(self) - } - - public func wait() { - _ = __dispatch_semaphore_wait(self, DispatchTime.distantFuture.rawValue) - } - - public func wait(timeout: DispatchTime) -> DispatchTimeoutResult { - return __dispatch_semaphore_wait(self, timeout.rawValue) == 0 ? .success : .timedOut - } - - public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult { - return __dispatch_semaphore_wait(self, wallTimeout.rawValue) == 0 ? .success : .timedOut - } -} - diff --git a/stdlib/public/Darwin/Dispatch/IO.swift b/stdlib/public/Darwin/Dispatch/IO.swift deleted file mode 100644 index d860766e4eae8..0000000000000 --- a/stdlib/public/Darwin/Dispatch/IO.swift +++ /dev/null @@ -1,109 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -extension DispatchIO { - - public enum StreamType : UInt { - case stream = 0 - case random = 1 - } - - public struct CloseFlags : OptionSet, RawRepresentable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let stop = CloseFlags(rawValue: 1) - } - - public struct IntervalFlags : OptionSet, RawRepresentable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - public init(nilLiteral: ()) { self.rawValue = 0 } - - public static let strictInterval = IntervalFlags(rawValue: 1) - } - - public class func read(fromFileDescriptor: Int32, maxLength: Int, runningHandlerOn queue: DispatchQueue, handler: @escaping (_ data: DispatchData, _ error: Int32) -> Void) { - __dispatch_read(fromFileDescriptor, maxLength, queue) { (data: __DispatchData, error: Int32) in - handler(DispatchData(data: data), error) - } - } - - public class func write(toFileDescriptor: Int32, data: DispatchData, runningHandlerOn queue: DispatchQueue, handler: @escaping (_ data: DispatchData?, _ error: Int32) -> Void) { - __dispatch_write(toFileDescriptor, data as __DispatchData, queue) { (data: __DispatchData?, error: Int32) in - handler(data.map { DispatchData(data: $0) }, error) - } - } - - public convenience init( - type: StreamType, - fileDescriptor: Int32, - queue: DispatchQueue, - cleanupHandler: @escaping (_ error: Int32) -> Void) - { - self.init(__type: type.rawValue, fd: fileDescriptor, queue: queue, handler: cleanupHandler) - } - - @available(swift, obsoleted: 4) - public convenience init( - type: StreamType, - path: UnsafePointer, - oflag: Int32, - mode: mode_t, - queue: DispatchQueue, - cleanupHandler: @escaping (_ error: Int32) -> Void) - { - self.init(__type: type.rawValue, path: path, oflag: oflag, mode: mode, queue: queue, handler: cleanupHandler) - } - - @available(swift, introduced: 4) - public convenience init?( - type: StreamType, - path: UnsafePointer, - oflag: Int32, - mode: mode_t, - queue: DispatchQueue, - cleanupHandler: @escaping (_ error: Int32) -> Void) - { - self.init(__type: type.rawValue, path: path, oflag: oflag, mode: mode, queue: queue, handler: cleanupHandler) - } - - public convenience init( - type: StreamType, - io: DispatchIO, - queue: DispatchQueue, - cleanupHandler: @escaping (_ error: Int32) -> Void) - { - self.init(__type: type.rawValue, io: io, queue: queue, handler: cleanupHandler) - } - - public func read(offset: off_t, length: Int, queue: DispatchQueue, ioHandler: @escaping (_ done: Bool, _ data: DispatchData?, _ error: Int32) -> Void) { - __dispatch_io_read(self, offset, length, queue) { (done: Bool, data: __DispatchData?, error: Int32) in - ioHandler(done, data.map { DispatchData(data: $0) }, error) - } - } - - public func write(offset: off_t, data: DispatchData, queue: DispatchQueue, ioHandler: @escaping (_ done: Bool, _ data: DispatchData?, _ error: Int32) -> Void) { - __dispatch_io_write(self, offset, data as __DispatchData, queue) { (done: Bool, data: __DispatchData?, error: Int32) in - ioHandler(done, data.map { DispatchData(data: $0) }, error) - } - } - - public func setInterval(interval: DispatchTimeInterval, flags: IntervalFlags = []) { - __dispatch_io_set_interval(self, UInt64(interval.rawValue), flags.rawValue) - } - - public func close(flags: CloseFlags = []) { - __dispatch_io_close(self, flags.rawValue) - } -} - diff --git a/stdlib/public/Darwin/Dispatch/Private.swift b/stdlib/public/Darwin/Dispatch/Private.swift deleted file mode 100644 index 9716834397db0..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Private.swift +++ /dev/null @@ -1,472 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// Redeclarations of all SwiftPrivate functions with appropriate markup. - -@available(*, unavailable, renamed:"DispatchQueue.init(label:qos:attributes:autoreleaseFrequency:target:)") -public func dispatch_queue_create(_ label: UnsafePointer?, _ attr: __OS_dispatch_queue_attr?) -> DispatchQueue -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.init(label:qos:attributes:autoreleaseFrequency:target:)") -public func dispatch_queue_create_with_target(_ label: UnsafePointer?, _ attr: __OS_dispatch_queue_attr?, _ queue: DispatchQueue?) -> DispatchQueue -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.init(type:fileDescriptor:queue:cleanupHandler:)") -public func dispatch_io_create(_ type: UInt, _ fd: Int32, _ queue: DispatchQueue, _ cleanup_handler: (Int32) -> Void) -> DispatchIO -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.init(type:path:oflag:mode:queue:cleanupHandler:)") -public func dispatch_io_create_with_path(_ type: UInt, _ path: UnsafePointer, _ oflag: Int32, _ mode: mode_t, _ queue: DispatchQueue, _ cleanup_handler: (Int32) -> Void) -> DispatchIO -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.init(type:io:queue:cleanupHandler:)") -public func dispatch_io_create_with_io(_ type: UInt, _ io: DispatchIO, _ queue: DispatchQueue, _ cleanup_handler: (Int32) -> Void) -> DispatchIO -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.read(fileDescriptor:length:queue:handler:)") -public func dispatch_read(_ fd: Int32, _ length: Int, _ queue: DispatchQueue, _ handler: (__DispatchData, Int32) -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.read(self:offset:length:queue:ioHandler:)") -func dispatch_io_read(_ channel: DispatchIO, _ offset: off_t, _ length: Int, _ queue: DispatchQueue, _ io_handler: (Bool, __DispatchData?, Int32) -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.write(self:offset:data:queue:ioHandler:)") -func dispatch_io_write(_ channel: DispatchIO, _ offset: off_t, _ data: __DispatchData, _ queue: DispatchQueue, _ io_handler: (Bool, __DispatchData?, Int32) -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.write(fileDescriptor:data:queue:handler:)") -func dispatch_write(_ fd: Int32, _ data: __DispatchData, _ queue: DispatchQueue, _ handler: (__DispatchData?, Int32) -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchData.init(bytes:)") -public func dispatch_data_create(_ buffer: UnsafeRawPointer, _ size: Int, _ queue: DispatchQueue?, _ destructor: (() -> Void)?) -> __DispatchData -{ - fatalError() -} - -@available(*, unavailable, renamed:"getter:DispatchData.count(self:)") -public func dispatch_data_get_size(_ data: __DispatchData) -> Int -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchData.withUnsafeBytes(self:body:)") -public func dispatch_data_create_map(_ data: __DispatchData, _ buffer_ptr: UnsafeMutablePointer?, _ size_ptr: UnsafeMutablePointer?) -> __DispatchData -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchData.append(self:_:)") -public func dispatch_data_create_concat(_ data1: __DispatchData, _ data2: __DispatchData) -> __DispatchData -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchData.subdata(self:in:)") -public func dispatch_data_create_subrange(_ data: __DispatchData, _ offset: Int, _ length: Int) -> __DispatchData -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchData.enumerateBytes(self:block:)") -public func dispatch_data_apply(_ data: __DispatchData, _ applier: (__DispatchData, Int, UnsafeRawPointer, Int) -> Bool) -> Bool -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchData.region(self:location:)") -public func dispatch_data_copy_region(_ data: __DispatchData, _ location: Int, _ offset_ptr: UnsafeMutablePointer) -> __DispatchData -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.async(self:group:qos:flags:execute:)") -public func dispatch_group_async(_ group: DispatchGroup, _ queue: DispatchQueue, _ block: () -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed: "DispatchGroup.notify(self:qos:flags:queue:execute:)") -public func dispatch_group_notify(_ group: DispatchGroup, _ queue: DispatchQueue, _ block: () -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchGroup.wait(self:timeout:)") -public func dispatch_group_wait(_ group: DispatchGroup, _ timeout: dispatch_time_t) -> Int -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.close(self:flags:)") -public func dispatch_io_close(_ channel: DispatchIO, _ flags: UInt) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchIO.setInterval(self:interval:flags:)") -public func dispatch_io_set_interval(_ channel: DispatchIO, _ interval: UInt64, _ flags: UInt) -{ - fatalError() -} - -@available(*, unavailable, message:"Use DispatchQueue.concurrentPerform(iterations:execute:). The 'queue' argument is not required because the system chooses the appropriate execution context for the block") -public func dispatch_apply(_ iterations: Int, _ queue: DispatchQueue, _ block: (Int) -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.async(self:execute:)") -public func dispatch_async(_ queue: DispatchQueue, _ block: () -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.global(attributes:)") -public func dispatch_get_global_queue(_ identifier: Int, _ flags: UInt) -> DispatchQueue -{ - fatalError() -} - -@available(*, unavailable, renamed: "getter:DispatchQueue.main()") -public func dispatch_get_main_queue() -> DispatchQueue -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.Attributes.initiallyInactive") -public func dispatch_queue_attr_make_initially_inactive(_ attr: __OS_dispatch_queue_attr?) -> __OS_dispatch_queue_attr -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.AutoreleaseFrequency.workItem") -public func dispatch_queue_attr_make_with_autorelease_frequency(_ attr: __OS_dispatch_queue_attr?, _ frequency: __dispatch_autorelease_frequency_t) -> __OS_dispatch_queue_attr -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQoS") -public func dispatch_queue_attr_make_with_qos_class(_ attr: __OS_dispatch_queue_attr?, _ qos_class: qos_class_t, _ relative_priority: Int32) -> __OS_dispatch_queue_attr -{ - fatalError() -} - -@available(*, unavailable, renamed:"getter:DispatchQueue.label(self:)") -public func dispatch_queue_get_label(_ queue: DispatchQueue?) -> UnsafePointer -{ - fatalError() -} - -@available(*, unavailable, renamed:"getter:DispatchQueue.qos(self:)") -public func dispatch_queue_get_qos_class(_ queue: DispatchQueue, _ relative_priority_ptr: UnsafeMutablePointer?) -> qos_class_t -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.asyncAfter(self:deadline:qos:flags:execute:)") -public func dispatch_after(_ when: dispatch_time_t, _ queue: DispatchQueue, _ block: () -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.async(self:group:qos:flags:execute:)") -public func dispatch_barrier_async(_ queue: DispatchQueue, _ block: () -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.sync(self:flags:execute:)") -public func dispatch_barrier_sync(_ queue: DispatchQueue, _ block: () -> Void) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.setSpecific(self:key:value:)") -public func dispatch_queue_set_specific(_ queue: DispatchQueue, _ key: UnsafeRawPointer, _ context: UnsafeMutableRawPointer?, _ destructor: (@convention(c) (UnsafeMutableRawPointer?) -> Void)?) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.getSpecific(self:key:)") -public func dispatch_queue_get_specific(_ queue: DispatchQueue, _ key: UnsafeRawPointer) -> UnsafeMutableRawPointer? -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchQueue.getSpecific(key:)") -public func dispatch_get_specific(_ key: UnsafeRawPointer) -> UnsafeMutableRawPointer? -{ - fatalError() -} - -@available(*, unavailable, renamed:"dispatchPrecondition(_:)") -public func dispatch_assert_queue(_ queue: DispatchQueue) -{ - fatalError() -} - -@available(*, unavailable, renamed:"dispatchPrecondition(_:)") -public func dispatch_assert_queue_barrier(_ queue: DispatchQueue) -{ - fatalError() -} - -@available(*, unavailable, renamed:"dispatchPrecondition(_:)") -public func dispatch_assert_queue_not(_ queue: DispatchQueue) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchSemaphore.wait(self:timeout:)") -public func dispatch_semaphore_wait(_ dsema: DispatchSemaphore, _ timeout: dispatch_time_t) -> Int -{ - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSemaphore.signal(self:)") -public func dispatch_semaphore_signal(_ dsema: DispatchSemaphore) -> Int -{ - fatalError() -} - -@available(*, unavailable, message:"Use DispatchSource class methods") -public func dispatch_source_create(_ type: __dispatch_source_type_t, _ handle: UInt, _ mask: UInt, _ queue: DispatchQueue?) -> DispatchSource -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchSource.setEventHandler(self:handler:)") -public func dispatch_source_set_event_handler(_ source: DispatchSource, _ handler: (() -> Void)?) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchSource.setCancelHandler(self:handler:)") -public func dispatch_source_set_cancel_handler(_ source: DispatchSource, _ handler: (() -> Void)?) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchSource.cancel(self:)") -public func dispatch_source_cancel(_ source: DispatchSource) -{ - fatalError() -} - -@available(*, unavailable, renamed:"getter:DispatchSource.isCancelled(self:)") -public func dispatch_source_testcancel(_ source: DispatchSource) -> Int -{ - fatalError() -} - -@available(*, unavailable, renamed:"getter:DispatchSource.handle(self:)") -public func dispatch_source_get_handle(_ source: DispatchSource) -> UInt -{ - fatalError() -} - -@available(*, unavailable, renamed:"getter:DispatchSource.mask(self:)") -public func dispatch_source_get_mask(_ source: DispatchSource) -> UInt -{ - fatalError() -} - -@available(*, unavailable, renamed:"getter:DispatchSource.data(self:)") -public func dispatch_source_get_data(_ source: DispatchSource) -> UInt -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchUserDataAdd.mergeData(self:value:)") -public func dispatch_source_merge_data(_ source: DispatchSource, _ value: UInt) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchTimerSource.setTimer(self:start:interval:leeway:)") -public func dispatch_source_set_timer(_ source: DispatchSource, _ start: dispatch_time_t, _ interval: UInt64, _ leeway: UInt64) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchSource.setRegistrationHandler(self:handler:)") -public func dispatch_source_set_registration_handler(_ source: DispatchSource, _ handler: (() -> Void)?) -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchTime.now()") -public func dispatch_time(_ when: dispatch_time_t, _ delta: Int64) -> dispatch_time_t -{ - fatalError() -} - -@available(*, unavailable, renamed:"DispatchWalltime.init(time:)") -public func dispatch_walltime(_ when: UnsafePointer?, _ delta: Int64) -> dispatch_time_t -{ - fatalError() -} - -@available(*, unavailable, renamed: "DispatchQueue.GlobalQueuePriority.high") -public var DISPATCH_QUEUE_PRIORITY_HIGH: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchQueue.GlobalQueuePriority.default") -public var DISPATCH_QUEUE_PRIORITY_DEFAULT: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchQueue.GlobalQueuePriority.low") -public var DISPATCH_QUEUE_PRIORITY_LOW: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchQueue.GlobalQueuePriority.background") -public var DISPATCH_QUEUE_PRIORITY_BACKGROUND: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchIO.StreamType.stream") -public var DISPATCH_IO_STREAM: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchIO.StreamType.random") -public var DISPATCH_IO_RANDOM: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchIO.CloseFlags.stop") -public var DISPATCH_IO_STOP: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchIO.IntervalFlags.strictInterval") -public var DISPATCH_IO_STRICT_INTERVAL: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.MachSendEvent.dead") -public var DISPATCH_MACH_SEND_DEAD: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.MemoryPressureEvent.normal") -public var DISPATCH_MEMORYPRESSURE_NORMAL: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.MemoryPressureEvent.warning") -public var DISPATCH_MEMORYPRESSURE_WARN: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.MemoryPressureEvent.critical") -public var DISPATCH_MEMORYPRESSURE_CRITICAL: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.ProcessEvent.exit") -public var DISPATCH_PROC_EXIT: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.ProcessEvent.fork") -public var DISPATCH_PROC_FORK: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.ProcessEvent.exec") -public var DISPATCH_PROC_EXEC: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.ProcessEvent.signal") -public var DISPATCH_PROC_SIGNAL: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.TimerFlags.strict") -public var DISPATCH_TIMER_STRICT: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.FileSystemEvent.delete") -public var DISPATCH_VNODE_DELETE: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.FileSystemEvent.write") -public var DISPATCH_VNODE_WRITE: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.FileSystemEvent.extend") -public var DISPATCH_VNODE_EXTEND: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.FileSystemEvent.attrib") -public var DISPATCH_VNODE_ATTRIB: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.FileSystemEvent.link") -public var DISPATCH_VNODE_LINK: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.FileSystemEvent.rename") -public var DISPATCH_VNODE_RENAME: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.FileSystemEvent.revoke") -public var DISPATCH_VNODE_REVOKE: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchSource.FileSystemEvent.funlock") -public var DISPATCH_VNODE_FUNLOCK: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchTime.now()") -public var DISPATCH_TIME_NOW: Int { - fatalError() -} - -@available(*, unavailable, renamed: "DispatchTime.distantFuture") -public var DISPATCH_TIME_FOREVER: Int { - fatalError() -} diff --git a/stdlib/public/Darwin/Dispatch/Queue.swift b/stdlib/public/Darwin/Dispatch/Queue.swift deleted file mode 100644 index ca26c87f82118..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Queue.swift +++ /dev/null @@ -1,502 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// dispatch/queue.h - -@_implementationOnly import _SwiftDispatchOverlayShims - -public final class DispatchSpecificKey { - public init() {} -} - -internal class _DispatchSpecificValue { - internal let value: T - internal init(value: T) { self.value = value } -} - -extension DispatchQueue { - public struct Attributes : OptionSet { - public let rawValue: UInt64 - public init(rawValue: UInt64) { self.rawValue = rawValue } - - public static let concurrent = Attributes(rawValue: 1<<1) - - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public static let initiallyInactive = Attributes(rawValue: 1<<2) - - fileprivate func _attr() -> __OS_dispatch_queue_attr? { - var attr: __OS_dispatch_queue_attr? - - if self.contains(.concurrent) { - attr = _swift_dispatch_queue_concurrent() - } - if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { - if self.contains(.initiallyInactive) { - attr = __dispatch_queue_attr_make_initially_inactive(attr) - } - } - return attr - } - } - - public enum GlobalQueuePriority { - @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") - @available(iOS, deprecated: 8.0, message: "Use qos attributes instead") - @available(tvOS, deprecated, message: "Use qos attributes instead") - @available(watchOS, deprecated, message: "Use qos attributes instead") - case high - - @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") - @available(iOS, deprecated: 8.0, message: "Use qos attributes instead") - @available(tvOS, deprecated, message: "Use qos attributes instead") - @available(watchOS, deprecated, message: "Use qos attributes instead") - case `default` - - @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") - @available(iOS, deprecated: 8.0, message: "Use qos attributes instead") - @available(tvOS, deprecated, message: "Use qos attributes instead") - @available(watchOS, deprecated, message: "Use qos attributes instead") - case low - - @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") - @available(iOS, deprecated: 8.0, message: "Use qos attributes instead") - @available(tvOS, deprecated, message: "Use qos attributes instead") - @available(watchOS, deprecated, message: "Use qos attributes instead") - case background - - internal var _translatedValue: Int { - switch self { - case .high: return 2 // DISPATCH_QUEUE_PRIORITY_HIGH - case .default: return 0 // DISPATCH_QUEUE_PRIORITY_DEFAULT - case .low: return -2 // DISPATCH_QUEUE_PRIORITY_LOW - case .background: return Int(Int16.min) // DISPATCH_QUEUE_PRIORITY_BACKGROUND - } - } - } - - public enum AutoreleaseFrequency { - case inherit - - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - case workItem - - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - case never - - internal func _attr(attr: __OS_dispatch_queue_attr?) -> __OS_dispatch_queue_attr? { - if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { - return __dispatch_queue_attr_make_with_autorelease_frequency(attr, self._rawValue) - } else { - return attr - } - } - - internal var _rawValue: __dispatch_autorelease_frequency_t { - switch self { - case .inherit: - // DISPATCH_AUTORELEASE_FREQUENCY_INHERIT - return (__dispatch_autorelease_frequency_t(rawValue: 0) as Optional)! - case .workItem: - // DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM - return (__dispatch_autorelease_frequency_t(rawValue: 1) as Optional)! - case .never: - // DISPATCH_AUTORELEASE_FREQUENCY_NEVER - return (__dispatch_autorelease_frequency_t(rawValue: 2) as Optional)! - } - } - } - - public class func concurrentPerform(iterations: Int, execute work: (Int) -> Void) { - _swift_dispatch_apply_current(iterations, work) - } - - public class var main: DispatchQueue { - return _swift_dispatch_get_main_queue() - } - - @available(macOS, deprecated: 10.10) - @available(iOS, deprecated: 8.0) - @available(tvOS, deprecated) - @available(watchOS, deprecated) - public class func global(priority: GlobalQueuePriority) -> DispatchQueue { - return __dispatch_get_global_queue(priority._translatedValue, 0) - } - - @available(macOS 10.10, iOS 8.0, *) - public class func global(qos: DispatchQoS.QoSClass = .default) -> DispatchQueue { - return __dispatch_get_global_queue(Int(qos.rawValue.rawValue), 0) - } - - public class func getSpecific(key: DispatchSpecificKey) -> T? { - let k = Unmanaged.passUnretained(key).toOpaque() - if let p = __dispatch_get_specific(k) { - let v = Unmanaged<_DispatchSpecificValue> - .fromOpaque(p) - .takeUnretainedValue() - return v.value - } - return nil - } - - public convenience init( - label: String, - qos: DispatchQoS = .unspecified, - attributes: Attributes = [], - autoreleaseFrequency: AutoreleaseFrequency = .inherit, - target: DispatchQueue? = nil) - { - var attr = attributes._attr() - if autoreleaseFrequency != .inherit { - attr = autoreleaseFrequency._attr(attr: attr) - } - if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified { - attr = __dispatch_queue_attr_make_with_qos_class(attr, qos.qosClass.rawValue, Int32(qos.relativePriority)) - } - - if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { - self.init(__label: label, attr: attr, queue: target) - } else { - self.init(__label: label, attr: attr) - if let tq = target { self.setTarget(queue: tq) } - } - } - - public var label: String { - return String(validatingUTF8: __dispatch_queue_get_label(self))! - } - - /// - /// Submits a block for synchronous execution on this queue. - /// - /// Submits a work item to a dispatch queue like `async(execute:)`, however - /// `sync(execute:)` will not return until the work item has finished. - /// - /// Work items submitted to a queue with `sync(execute:)` do not observe certain - /// queue attributes of that queue when invoked (such as autorelease frequency - /// and QoS class). - /// - /// Calls to `sync(execute:)` targeting the current queue will result - /// in deadlock. Use of `sync(execute:)` is also subject to the same - /// multi-party deadlock problems that may result from the use of a mutex. - /// Use of `async(execute:)` is preferred. - /// - /// As an optimization, `sync(execute:)` invokes the work item on the thread which - /// submitted it, except when the queue is the main queue or - /// a queue targetting it. - /// - /// - parameter execute: The work item to be invoked on the queue. - /// - SeeAlso: `async(execute:)` - /// - @available(macOS 10.10, iOS 8.0, *) - public func sync(execute workItem: DispatchWorkItem) { - // _swift_dispatch_sync preserves the @convention(block) for - // work item blocks. - _swift_dispatch_sync(self, workItem._block) - } - - /// - /// Submits a work item for asynchronous execution on a dispatch queue. - /// - /// `async(execute:)` is the fundamental mechanism for submitting - /// work items to a dispatch queue. - /// - /// Calls to `async(execute:)` always return immediately after the work item has - /// been submitted, and never wait for the work item to be invoked. - /// - /// The target queue determines whether the work item will be invoked serially or - /// concurrently with respect to other work items submitted to that same queue. - /// Serial queues are processed concurrently with respect to each other. - /// - /// - parameter execute: The work item to be invoked on the queue. - /// - SeeAlso: `sync(execute:)` - /// - /// - @available(macOS 10.10, iOS 8.0, *) - public func async(execute workItem: DispatchWorkItem) { - // _swift_dispatch_async preserves the @convention(block) - // for work item blocks. - _swift_dispatch_async(self, workItem._block) - } - - /// - /// Submits a work item to a dispatch queue and associates it with the given - /// dispatch group. The dispatch group may be used to wait for the completion - /// of the work items it references. - /// - /// - parameter group: the dispatch group to associate with the submitted block. - /// - parameter execute: The work item to be invoked on the queue. - /// - SeeAlso: `sync(execute:)` - /// - @available(macOS 10.10, iOS 8.0, *) - public func async(group: DispatchGroup, execute workItem: DispatchWorkItem) { - // _swift_dispatch_group_async preserves the @convention(block) - // for work item blocks. - _swift_dispatch_group_async(group, self, workItem._block) - } - - /// - /// Submits a work item to a dispatch queue and optionally associates it with a - /// dispatch group. The dispatch group may be used to wait for the completion - /// of the work items it references. - /// - /// - parameter group: the dispatch group to associate with the submitted - /// work item. If this is `nil`, the work item is not associated with a group. - /// - parameter flags: flags that control the execution environment of the - /// - parameter qos: the QoS at which the work item should be executed. - /// Defaults to `DispatchQoS.unspecified`. - /// - parameter flags: flags that control the execution environment of the - /// work item. - /// - parameter execute: The work item to be invoked on the queue. - /// - SeeAlso: `sync(execute:)` - /// - SeeAlso: `DispatchQoS` - /// - SeeAlso: `DispatchWorkItemFlags` - /// - public func async( - group: DispatchGroup? = nil, - qos: DispatchQoS = .unspecified, - flags: DispatchWorkItemFlags = [], - execute work: @escaping @convention(block) () -> Void) - { - if group == nil && qos == .unspecified { - // Fast-path route for the most common API usage - if flags.isEmpty { - _swift_dispatch_async(self, work) - return - } else if flags == .barrier { - _swift_dispatch_barrier_async(self, work) - return - } - } - - var block: @convention(block) () -> Void = work - if #available(macOS 10.10, iOS 8.0, *), (qos != .unspecified || !flags.isEmpty) { - let workItem = DispatchWorkItem(qos: qos, flags: flags, block: work) - block = workItem._block - } - - if let g = group { - _swift_dispatch_group_async(g, self, block) - } else { - _swift_dispatch_async(self, block) - } - } - - private func _syncBarrier(block: () -> Void) { - __dispatch_barrier_sync(self, block) - } - - private func _syncHelper( - fn: (() -> Void) -> Void, - execute work: () throws -> T, - rescue: ((Error) throws -> (T))) rethrows -> T - { - var result: T? - var error: Error? - withoutActuallyEscaping(work) { _work in - fn { - do { - result = try _work() - } catch let e { - error = e - } - } - } - if let e = error { - return try rescue(e) - } else { - return result! - } - } - - @available(macOS 10.10, iOS 8.0, *) - private func _syncHelper( - fn: (DispatchWorkItem) -> Void, - flags: DispatchWorkItemFlags, - execute work: () throws -> T, - rescue: ((Error) throws -> (T))) rethrows -> T - { - var result: T? - var error: Error? - let workItem = DispatchWorkItem(flags: flags, noescapeBlock: { - do { - result = try work() - } catch let e { - error = e - } - }) - fn(workItem) - if let e = error { - return try rescue(e) - } else { - return result! - } - } - - /// - /// Submits a block for synchronous execution on this queue. - /// - /// Submits a work item to a dispatch queue like `sync(execute:)`, and returns - /// the value, of type `T`, returned by that work item. - /// - /// - parameter execute: The work item to be invoked on the queue. - /// - returns the value returned by the work item. - /// - SeeAlso: `sync(execute:)` - /// - public func sync(execute work: () throws -> T) rethrows -> T { - return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 }) - } - - /// - /// Submits a block for synchronous execution on this queue. - /// - /// Submits a work item to a dispatch queue like `sync(execute:)`, and returns - /// the value, of type `T`, returned by that work item. - /// - /// - parameter flags: flags that control the execution environment of the - /// - parameter execute: The work item to be invoked on the queue. - /// - returns the value returned by the work item. - /// - SeeAlso: `sync(execute:)` - /// - SeeAlso: `DispatchWorkItemFlags` - /// - public func sync(flags: DispatchWorkItemFlags, execute work: () throws -> T) rethrows -> T { - if flags == .barrier { - return try self._syncHelper(fn: _syncBarrier, execute: work, rescue: { throw $0 }) - } else if #available(macOS 10.10, iOS 8.0, *), !flags.isEmpty { - return try self._syncHelper(fn: sync, flags: flags, execute: work, rescue: { throw $0 }) - } else { - return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 }) - } - } - - /// - /// Submits a work item to a dispatch queue for asynchronous execution after - /// a specified time. - /// - /// - parameter: deadline the time after which the work item should be executed, - /// given as a `DispatchTime`. - /// - parameter qos: the QoS at which the work item should be executed. - /// Defaults to `DispatchQoS.unspecified`. - /// - parameter flags: flags that control the execution environment of the - /// work item. - /// - parameter execute: The work item to be invoked on the queue. - /// - SeeAlso: `async(execute:)` - /// - SeeAlso: `asyncAfter(deadline:execute:)` - /// - SeeAlso: `DispatchQoS` - /// - SeeAlso: `DispatchWorkItemFlags` - /// - SeeAlso: `DispatchTime` - /// - public func asyncAfter( - deadline: DispatchTime, - qos: DispatchQoS = .unspecified, - flags: DispatchWorkItemFlags = [], - execute work: @escaping @convention(block) () -> Void) - { - if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { - let item = DispatchWorkItem(qos: qos, flags: flags, block: work) - _swift_dispatch_after(deadline.rawValue, self, item._block) - } else { - _swift_dispatch_after(deadline.rawValue, self, work) - } - } - - /// - /// Submits a work item to a dispatch queue for asynchronous execution after - /// a specified time. - /// - /// - parameter: deadline the time after which the work item should be executed, - /// given as a `DispatchWallTime`. - /// - parameter qos: the QoS at which the work item should be executed. - /// Defaults to `DispatchQoS.unspecified`. - /// - parameter flags: flags that control the execution environment of the - /// work item. - /// - parameter execute: The work item to be invoked on the queue. - /// - SeeAlso: `async(execute:)` - /// - SeeAlso: `asyncAfter(wallDeadline:execute:)` - /// - SeeAlso: `DispatchQoS` - /// - SeeAlso: `DispatchWorkItemFlags` - /// - SeeAlso: `DispatchWallTime` - /// - public func asyncAfter( - wallDeadline: DispatchWallTime, - qos: DispatchQoS = .unspecified, - flags: DispatchWorkItemFlags = [], - execute work: @escaping @convention(block) () -> Void) - { - if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { - let item = DispatchWorkItem(qos: qos, flags: flags, block: work) - _swift_dispatch_after(wallDeadline.rawValue, self, item._block) - } else { - _swift_dispatch_after(wallDeadline.rawValue, self, work) - } - } - - /// - /// Submits a work item to a dispatch queue for asynchronous execution after - /// a specified time. - /// - /// - parameter: deadline the time after which the work item should be executed, - /// given as a `DispatchTime`. - /// - parameter execute: The work item to be invoked on the queue. - /// - SeeAlso: `asyncAfter(deadline:qos:flags:execute:)` - /// - SeeAlso: `DispatchTime` - /// - @available(macOS 10.10, iOS 8.0, *) - public func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem) { - _swift_dispatch_after(deadline.rawValue, self, execute._block) - } - - /// - /// Submits a work item to a dispatch queue for asynchronous execution after - /// a specified time. - /// - /// - parameter: deadline the time after which the work item should be executed, - /// given as a `DispatchWallTime`. - /// - parameter execute: The work item to be invoked on the queue. - /// - SeeAlso: `asyncAfter(wallDeadline:qos:flags:execute:)` - /// - SeeAlso: `DispatchTime` - /// - @available(macOS 10.10, iOS 8.0, *) - public func asyncAfter(wallDeadline: DispatchWallTime, execute: DispatchWorkItem) { - _swift_dispatch_after(wallDeadline.rawValue, self, execute._block) - } - - @available(macOS 10.10, iOS 8.0, *) - public var qos: DispatchQoS { - var relPri: Int32 = 0 - let cls = DispatchQoS.QoSClass(rawValue: __dispatch_queue_get_qos_class(self, &relPri))! - return DispatchQoS(qosClass: cls, relativePriority: Int(relPri)) - } - - public func getSpecific(key: DispatchSpecificKey) -> T? { - let k = Unmanaged.passUnretained(key).toOpaque() - if let p = __dispatch_queue_get_specific(self, k) { - let v = Unmanaged<_DispatchSpecificValue> - .fromOpaque(p) - .takeUnretainedValue() - return v.value - } - return nil - } - - public func setSpecific(key: DispatchSpecificKey, value: T?) { - let k = Unmanaged.passUnretained(key).toOpaque() - let v = value.map { _DispatchSpecificValue(value: $0) } - let p = v.map { Unmanaged.passRetained($0).toOpaque() } - __dispatch_queue_set_specific(self, k, p, _destructDispatchSpecificValue) - } -} - -private func _destructDispatchSpecificValue(ptr: UnsafeMutableRawPointer?) { - if let p = ptr { - Unmanaged.fromOpaque(p).release() - } -} diff --git a/stdlib/public/Darwin/Dispatch/Schedulers+DispatchQueue.swift b/stdlib/public/Darwin/Dispatch/Schedulers+DispatchQueue.swift deleted file mode 100644 index 427ceb44cb4b8..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Schedulers+DispatchQueue.swift +++ /dev/null @@ -1,283 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -import Combine - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -private func clampedIntProduct(_ m1: Int, _ m2: UInt64) -> Int { - assert(m2 > 0, "multiplier must be positive") - guard m1 < Int.max, m2 < Int.max else { return Int.max } - let (result, overflow) = m1.multipliedReportingOverflow(by: Int(m2)) - if overflow { - return m1 > 0 ? Int.max : Int.min - } - return result -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension DispatchTimeInterval { - fileprivate var nanoseconds: Int { - switch self { - case .seconds(let s): return clampedIntProduct(s, NSEC_PER_SEC) - case .milliseconds(let ms): return clampedIntProduct(ms, NSEC_PER_MSEC) - case .microseconds(let us): return clampedIntProduct(us, NSEC_PER_USEC) - case .nanoseconds(let ns): return ns - case .never: return Int.max - } - } -} - -// This is Strideable except: -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension DispatchTime /* : Strideable */ { - typealias Stride = DispatchTimeInterval - - public func distance(to other: DispatchTime) -> DispatchTimeInterval { - let lhs = other.uptimeNanoseconds - let rhs = uptimeNanoseconds - if lhs >= rhs { - return DispatchTimeInterval.nanoseconds(Int(lhs - rhs)) - } else { - return DispatchTimeInterval.nanoseconds(0 - Int(rhs - lhs)) - } - } - - public func advanced(by n: DispatchTimeInterval) -> DispatchTime { - return self + n - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension DispatchQueue: Scheduler { - /// The scheduler time type used by the dispatch queue. - public struct SchedulerTimeType: Strideable, Codable, Hashable { - /// The dispatch time represented by this type. - public var dispatchTime: DispatchTime - - /// Creates a dispatch queue time type instance. - /// - /// - Parameter time: The dispatch time to represent. - public init(_ time: DispatchTime) { - dispatchTime = time - } - - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let time = DispatchTime(uptimeNanoseconds: try container.decode(UInt64.self)) - self.init(time) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(dispatchTime.uptimeNanoseconds) - } - - /// Returns the distance to another dispatch queue time. - /// - /// - Parameter other: Another dispatch queue time. - /// - Returns: The time interval between this time and the provided time. - public func distance(to other: SchedulerTimeType) -> Stride { - return Stride(self.dispatchTime.distance(to: other.dispatchTime)) - } - - /// Returns a dispatch queue scheduler time calculated by advancing this instance’s time by the given interval. - /// - /// - Parameter n: A time interval to advance. - /// - Returns: A dispatch queue time advanced by the given interval from this instance’s time. - public func advanced(by n: Stride) -> SchedulerTimeType { - return SchedulerTimeType(self.dispatchTime.advanced(by: n.timeInterval)) - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(dispatchTime.rawValue) - } - - public struct Stride: SchedulerTimeIntervalConvertible, Comparable, SignedNumeric, ExpressibleByFloatLiteral, Hashable, Codable { - /// If created via floating point literal, the value is converted to nanoseconds via multiplication. - public typealias FloatLiteralType = Double - - /// Nanoseconds, same as DispatchTimeInterval. - public typealias IntegerLiteralType = Int - public typealias Magnitude = Int - - /// The value of this time interval in nanoseconds. - public var magnitude: Int - - /// A `DispatchTimeInterval` created with the value of this type in nanoseconds. - public var timeInterval: DispatchTimeInterval { - return .nanoseconds(magnitude) - } - - /// Creates a dispatch queue time interval from the given dispatch time interval. - /// - /// - Parameter timeInterval: A dispatch time interval. - public init(_ timeInterval: DispatchTimeInterval) { - magnitude = Int(timeInterval.nanoseconds) - } - - /// Creates a dispatch queue time interval from a floating-point seconds value. - /// - /// - Parameter value: The number of seconds, as a `Double`. - public init(floatLiteral value: Double) { - magnitude = Int(value * 1_000_000_000) - } - - /// Creates a dispatch queue time interval from an integer seconds value. - /// - /// - Parameter value: The number of seconds, as an `Int`. - public init(integerLiteral value: Int) { - magnitude = value * 1_000_000_000 - } - - /// Creates a dispatch queue time interval from a binary integer type. - /// - /// If `exactly` cannot convert to an `Int`, the resulting time interval is `nil`. - /// - Parameter exactly: A binary integer representing a time interval. - public init?(exactly source: T) where T: BinaryInteger { - if let v = Int(exactly: source) { - magnitude = v - } else { - return nil - } - } - - // --- - - public static func < (lhs: Stride, rhs: Stride) -> Bool { - return lhs.magnitude < rhs.magnitude - } - - // --- - - public static func * (lhs: Stride, rhs: Stride) -> Stride { - return Stride(.nanoseconds(lhs.magnitude * rhs.magnitude)) - } - - public static func + (lhs: Stride, rhs: Stride) -> Stride { - return Stride(.nanoseconds(lhs.magnitude + rhs.magnitude)) - } - - public static func - (lhs: Stride, rhs: Stride) -> Stride { - return Stride(.nanoseconds(lhs.magnitude - rhs.magnitude)) - } - - // --- - - public static func -= (lhs: inout Stride, rhs: Stride) { - let result = lhs - rhs - lhs = result - } - - public static func *= (lhs: inout Stride, rhs: Stride) { - let result = lhs * rhs - lhs = result - } - - public static func += (lhs: inout Stride, rhs: Stride) { - let result = lhs + rhs - lhs = result - } - - // --- - - public static func seconds(_ s: Double) -> Stride { - return Stride(.nanoseconds(Int(s * 1_000_000_000))) - } - - public static func seconds(_ s: Int) -> Stride { - return Stride(.seconds(s)) - } - - public static func milliseconds(_ ms: Int) -> Stride { - return Stride(.milliseconds(ms)) - } - - public static func microseconds(_ us: Int) -> Stride { - return Stride(.microseconds(us)) - } - - public static func nanoseconds(_ ns: Int) -> Stride { - return Stride(.nanoseconds(ns)) - } - } - } - - /// Options that affect the operation of the dispatch queue scheduler. - public struct SchedulerOptions { - /// The dispatch queue quality of service. - public var qos: DispatchQoS - - /// The dispatch queue work item flags. - public var flags: DispatchWorkItemFlags - - /// The dispatch group, if any, that should be used for performing actions. - public var group: DispatchGroup? - - public init(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], group: DispatchGroup? = nil) { - self.qos = qos - self.flags = flags - self.group = group - } - } - - public var minimumTolerance: SchedulerTimeType.Stride { - return SchedulerTimeType.Stride(DispatchTimeInterval.seconds(0)) - } - - public var now: DispatchQueue.SchedulerTimeType { - return SchedulerTimeType(DispatchTime.now()) - } - - public func schedule(options: SchedulerOptions?, _ action: @escaping () -> Void) { - let qos = options?.qos ?? .unspecified - let flags = options?.flags ?? [] - - if let group = options?.group { - // Distinguish on the group because it appears to not be a call-through like the others. This may need to be adjusted. - self.async(group: group, qos: qos, flags: flags, execute: action) - } else { - self.async(qos: qos, flags: flags, execute: action) - } - } - - public func schedule(after date: SchedulerTimeType, - tolerance: SchedulerTimeType.Stride, - options: SchedulerOptions?, - _ action: @escaping () -> Void) { - // TODO: Tolerance ignored - let qos = options?.qos ?? .unspecified - let flags = options?.flags ?? [] - - self.asyncAfter(deadline: date.dispatchTime, qos: qos, flags: flags, execute: action) - } - - public func schedule(after date: SchedulerTimeType, - interval: SchedulerTimeType.Stride, - tolerance: SchedulerTimeType.Stride, - options: SchedulerOptions?, - _ action: @escaping () -> Void) -> Cancellable { - let source = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(), queue: self) - - source.schedule(deadline: date.dispatchTime, - repeating: interval.timeInterval, - leeway: tolerance.timeInterval) - source.setEventHandler(handler: action) - source.resume() - - return AnyCancellable(source.cancel) - } -} - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Dispatch/Source.swift b/stdlib/public/Darwin/Dispatch/Source.swift deleted file mode 100644 index b0a1912d4301a..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Source.swift +++ /dev/null @@ -1,661 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// import Foundation -@_implementationOnly import _SwiftDispatchOverlayShims - -extension DispatchSourceProtocol { - public typealias DispatchSourceHandler = @convention(block) () -> Void - - public func setEventHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: DispatchSourceHandler?) { - if #available(macOS 10.10, iOS 8.0, *), - let h = handler, - qos != .unspecified || !flags.isEmpty { - let item = DispatchWorkItem(qos: qos, flags: flags, block: h) - _swift_dispatch_source_set_event_handler(self as! DispatchSource, item._block) - } else { - _swift_dispatch_source_set_event_handler(self as! DispatchSource, handler) - } - } - - @available(macOS 10.10, iOS 8.0, *) - public func setEventHandler(handler: DispatchWorkItem) { - _swift_dispatch_source_set_event_handler(self as! DispatchSource, handler._block) - } - - public func setCancelHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: DispatchSourceHandler?) { - if #available(macOS 10.10, iOS 8.0, *), - let h = handler, - qos != .unspecified || !flags.isEmpty { - let item = DispatchWorkItem(qos: qos, flags: flags, block: h) - _swift_dispatch_source_set_cancel_handler(self as! DispatchSource, item._block) - } else { - _swift_dispatch_source_set_cancel_handler(self as! DispatchSource, handler) - } - } - - @available(macOS 10.10, iOS 8.0, *) - public func setCancelHandler(handler: DispatchWorkItem) { - _swift_dispatch_source_set_cancel_handler(self as! DispatchSource, handler._block) - } - - public func setRegistrationHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: DispatchSourceHandler?) { - if #available(macOS 10.10, iOS 8.0, *), - let h = handler, - qos != .unspecified || !flags.isEmpty { - let item = DispatchWorkItem(qos: qos, flags: flags, block: h) - _swift_dispatch_source_set_registration_handler(self as! DispatchSource, item._block) - } else { - _swift_dispatch_source_set_registration_handler(self as! DispatchSource, handler) - } - } - - @available(macOS 10.10, iOS 8.0, *) - public func setRegistrationHandler(handler: DispatchWorkItem) { - _swift_dispatch_source_set_registration_handler(self as! DispatchSource, handler._block) - } - - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public func activate() { - (self as! DispatchSource).activate() - } - - public func cancel() { - __dispatch_source_cancel(self as! DispatchSource) - } - - public func resume() { - (self as! DispatchSource).resume() - } - - public func suspend() { - (self as! DispatchSource).suspend() - } - - public var handle: UInt { - return __dispatch_source_get_handle(self as! DispatchSource) - } - - public var mask: UInt { - return __dispatch_source_get_mask(self as! DispatchSource) - } - - public var data: UInt { - return __dispatch_source_get_data(self as! DispatchSource) - } - - public var isCancelled: Bool { - return __dispatch_source_testcancel(self as! DispatchSource) != 0 - } -} - -extension DispatchSource { - public struct MachSendEvent : OptionSet, RawRepresentable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let dead = MachSendEvent(rawValue: 0x1) - } - - public struct MemoryPressureEvent : OptionSet, RawRepresentable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let normal = MemoryPressureEvent(rawValue: 0x1) - public static let warning = MemoryPressureEvent(rawValue: 0x2) - public static let critical = MemoryPressureEvent(rawValue: 0x4) - public static let all: MemoryPressureEvent = [.normal, .warning, .critical] - } - - public struct ProcessEvent : OptionSet, RawRepresentable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let exit = ProcessEvent(rawValue: 0x80000000) - public static let fork = ProcessEvent(rawValue: 0x40000000) - public static let exec = ProcessEvent(rawValue: 0x20000000) - public static let signal = ProcessEvent(rawValue: 0x08000000) - public static let all: ProcessEvent = [.exit, .fork, .exec, .signal] - } - - public struct TimerFlags : OptionSet, RawRepresentable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let strict = TimerFlags(rawValue: 1) - } - - public struct FileSystemEvent : OptionSet, RawRepresentable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let delete = FileSystemEvent(rawValue: 0x1) - public static let write = FileSystemEvent(rawValue: 0x2) - public static let extend = FileSystemEvent(rawValue: 0x4) - public static let attrib = FileSystemEvent(rawValue: 0x8) - public static let link = FileSystemEvent(rawValue: 0x10) - public static let rename = FileSystemEvent(rawValue: 0x20) - public static let revoke = FileSystemEvent(rawValue: 0x40) - public static let funlock = FileSystemEvent(rawValue: 0x100) - - public static let all: FileSystemEvent = [ - .delete, .write, .extend, .attrib, .link, .rename, .revoke] - } - - public class func makeMachSendSource(port: mach_port_t, eventMask: MachSendEvent, queue: DispatchQueue? = nil) -> DispatchSourceMachSend { - return _swift_dispatch_source_create( - _swift_dispatch_source_type_MACH_SEND(), UInt(port), eventMask.rawValue, queue) as DispatchSourceMachSend - } - - public class func makeMachReceiveSource(port: mach_port_t, queue: DispatchQueue? = nil) -> DispatchSourceMachReceive { - return _swift_dispatch_source_create( - _swift_dispatch_source_type_MACH_RECV(), UInt(port), 0, queue) as DispatchSourceMachReceive - } - - public class func makeMemoryPressureSource(eventMask: MemoryPressureEvent, queue: DispatchQueue? = nil) -> DispatchSourceMemoryPressure { - return _swift_dispatch_source_create( - _swift_dispatch_source_type_MEMORYPRESSURE(), 0, eventMask.rawValue, queue) as DispatchSourceMemoryPressure - } - - public class func makeProcessSource(identifier: pid_t, eventMask: ProcessEvent, queue: DispatchQueue? = nil) -> DispatchSourceProcess { - return _swift_dispatch_source_create( - _swift_dispatch_source_type_PROC(), UInt(identifier), eventMask.rawValue, queue) as DispatchSourceProcess - } - - public class func makeReadSource(fileDescriptor: Int32, queue: DispatchQueue? = nil) -> DispatchSourceRead { - return _swift_dispatch_source_create( - _swift_dispatch_source_type_READ(), UInt(fileDescriptor), 0, queue) as DispatchSourceRead - } - - public class func makeSignalSource(signal: Int32, queue: DispatchQueue? = nil) -> DispatchSourceSignal { - return _swift_dispatch_source_create( - _swift_dispatch_source_type_SIGNAL(), UInt(signal), 0, queue) as DispatchSourceSignal - } - - public class func makeTimerSource(flags: TimerFlags = [], queue: DispatchQueue? = nil) -> DispatchSourceTimer { - return _swift_dispatch_source_create(_swift_dispatch_source_type_TIMER(), 0, flags.rawValue, queue) as DispatchSourceTimer - } - - public class func makeUserDataAddSource(queue: DispatchQueue? = nil) -> DispatchSourceUserDataAdd { - return _swift_dispatch_source_create(_swift_dispatch_source_type_DATA_ADD(), 0, 0, queue) as DispatchSourceUserDataAdd - } - - public class func makeUserDataOrSource(queue: DispatchQueue? = nil) -> DispatchSourceUserDataOr { - return _swift_dispatch_source_create(_swift_dispatch_source_type_DATA_OR(), 0, 0, queue) as DispatchSourceUserDataOr - } - - public class func makeUserDataReplaceSource(queue: DispatchQueue? = nil) -> DispatchSourceUserDataReplace { - return _swift_dispatch_source_create(_swift_dispatch_source_type_DATA_REPLACE(), 0, 0, queue) as DispatchSourceUserDataReplace - } - - public class func makeFileSystemObjectSource( - fileDescriptor: Int32, eventMask: FileSystemEvent, queue: DispatchQueue? = nil) -> DispatchSourceFileSystemObject - { - return _swift_dispatch_source_create( - _swift_dispatch_source_type_VNODE(), UInt(fileDescriptor), eventMask.rawValue, queue) as DispatchSourceFileSystemObject - } - - public class func makeWriteSource(fileDescriptor: Int32, queue: DispatchQueue? = nil) -> DispatchSourceWrite { - return _swift_dispatch_source_create( - _swift_dispatch_source_type_WRITE(), UInt(fileDescriptor), 0, queue) as DispatchSourceWrite - } -} - -extension DispatchSourceMachSend { - public var handle: mach_port_t { - return mach_port_t(__dispatch_source_get_handle(self as! DispatchSource)) - } - - public var data: DispatchSource.MachSendEvent { - let data = __dispatch_source_get_data(self as! DispatchSource) - return DispatchSource.MachSendEvent(rawValue: data) - } - - public var mask: DispatchSource.MachSendEvent { - let mask = __dispatch_source_get_mask(self as! DispatchSource) - return DispatchSource.MachSendEvent(rawValue: mask) - } -} - -extension DispatchSourceMachReceive { - public var handle: mach_port_t { - return mach_port_t(__dispatch_source_get_handle(self as! DispatchSource)) - } -} - -extension DispatchSourceMemoryPressure { - public var data: DispatchSource.MemoryPressureEvent { - let data = __dispatch_source_get_data(self as! DispatchSource) - return DispatchSource.MemoryPressureEvent(rawValue: data) - } - - public var mask: DispatchSource.MemoryPressureEvent { - let mask = __dispatch_source_get_mask(self as! DispatchSource) - return DispatchSource.MemoryPressureEvent(rawValue: mask) - } -} - -extension DispatchSourceProcess { - public var handle: pid_t { - return pid_t(__dispatch_source_get_handle(self as! DispatchSource)) - } - - public var data: DispatchSource.ProcessEvent { - let data = __dispatch_source_get_data(self as! DispatchSource) - return DispatchSource.ProcessEvent(rawValue: data) - } - - public var mask: DispatchSource.ProcessEvent { - let mask = __dispatch_source_get_mask(self as! DispatchSource) - return DispatchSource.ProcessEvent(rawValue: mask) - } -} - -extension DispatchSourceTimer { - /// - /// Sets the deadline and leeway for a timer event that fires once. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared and the next timer event will occur at `deadline`. - /// - /// Delivery of the timer event may be delayed by the system in order to improve power consumption - /// and system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - note: Delivery of the timer event does not cancel the timer source. - /// - /// - parameter deadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on Mach absolute - /// time. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, deprecated: 4, renamed: "schedule(deadline:repeating:leeway:)") - public func scheduleOneshot(deadline: DispatchTime, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, UInt64(deadline.rawValue), ~0, UInt64(leeway.rawValue)) - } - - /// - /// Sets the deadline and leeway for a timer event that fires once. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared and the next timer event will occur at `wallDeadline`. - /// - /// Delivery of the timer event may be delayed by the system in order to improve power consumption - /// and system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - note: Delivery of the timer event does not cancel the timer source. - /// - /// - parameter wallDeadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on - /// `gettimeofday(3)`. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, deprecated: 4, renamed: "schedule(wallDeadline:repeating:leeway:)") - public func scheduleOneshot(wallDeadline: DispatchWallTime, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, UInt64(wallDeadline.rawValue), ~0, UInt64(leeway.rawValue)) - } - /// - /// Sets the deadline, interval and leeway for a timer event that fires at least once. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared. The next timer event will occur at `deadline` and every `interval` units of - /// time thereafter until the timer source is canceled. - /// - /// Delivery of a timer event may be delayed by the system in order to improve power consumption - /// and system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// For the initial timer fire at `deadline`, the upper limit to the allowable delay is set to - /// `leeway`. For the subsequent timer fires at `deadline + N * interval`, the upper - /// limit is the smaller of `leeway` and `interval/2`. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - /// - parameter deadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on Mach absolute - /// time. - /// - parameter interval: the interval for the timer. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, deprecated: 4, renamed: "schedule(deadline:repeating:leeway:)") - public func scheduleRepeating(deadline: DispatchTime, interval: DispatchTimeInterval, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, deadline.rawValue, interval == .never ? ~0 : UInt64(interval.rawValue), UInt64(leeway.rawValue)) - } - - /// - /// Sets the deadline, interval and leeway for a timer event that fires at least once. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared. The next timer event will occur at `deadline` and every `interval` seconds - /// thereafter until the timer source is canceled. - /// - /// Delivery of a timer event may be delayed by the system in order to improve power consumption and - /// system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// For the initial timer fire at `deadline`, the upper limit to the allowable delay is set to - /// `leeway`. For the subsequent timer fires at `deadline + N * interval`, the upper - /// limit is the smaller of `leeway` and `interval/2`. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - /// - parameter deadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on Mach absolute - /// time. - /// - parameter interval: the interval for the timer in seconds. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, deprecated: 4, renamed: "schedule(deadline:repeating:leeway:)") - public func scheduleRepeating(deadline: DispatchTime, interval: Double, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, deadline.rawValue, interval.isInfinite ? ~0 : UInt64(interval * Double(NSEC_PER_SEC)), UInt64(leeway.rawValue)) - } - - /// - /// Sets the deadline, interval and leeway for a timer event that fires at least once. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared. The next timer event will occur at `wallDeadline` and every `interval` units of - /// time thereafter until the timer source is canceled. - /// - /// Delivery of a timer event may be delayed by the system in order to improve power consumption and - /// system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// For the initial timer fire at `wallDeadline`, the upper limit to the allowable delay is set to - /// `leeway`. For the subsequent timer fires at `wallDeadline + N * interval`, the upper - /// limit is the smaller of `leeway` and `interval/2`. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - /// - parameter wallDeadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on - /// `gettimeofday(3)`. - /// - parameter interval: the interval for the timer. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, deprecated: 4, renamed: "schedule(wallDeadline:repeating:leeway:)") - public func scheduleRepeating(wallDeadline: DispatchWallTime, interval: DispatchTimeInterval, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, wallDeadline.rawValue, interval == .never ? ~0 : UInt64(interval.rawValue), UInt64(leeway.rawValue)) - } - - /// - /// Sets the deadline, interval and leeway for a timer event that fires at least once. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared. The next timer event will occur at `wallDeadline` and every `interval` seconds - /// thereafter until the timer source is canceled. - /// - /// Delivery of a timer event may be delayed by the system in order to improve power consumption and - /// system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// For the initial timer fire at `wallDeadline`, the upper limit to the allowable delay is set to - /// `leeway`. For the subsequent timer fires at `wallDeadline + N * interval`, the upper - /// limit is the smaller of `leeway` and `interval/2`. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - /// - parameter wallDeadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on - /// `gettimeofday(3)`. - /// - parameter interval: the interval for the timer in seconds. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, deprecated: 4, renamed: "schedule(wallDeadline:repeating:leeway:)") - public func scheduleRepeating(wallDeadline: DispatchWallTime, interval: Double, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, wallDeadline.rawValue, interval.isInfinite ? ~0 : UInt64(interval * Double(NSEC_PER_SEC)), UInt64(leeway.rawValue)) - } - - /// - /// Sets the deadline, repeat interval and leeway for a timer event. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared. The next timer event will occur at `deadline` and every `repeating` units of - /// time thereafter until the timer source is canceled. If the value of `repeating` is `.never`, - /// or is defaulted, the timer fires only once. - /// - /// Delivery of a timer event may be delayed by the system in order to improve power consumption - /// and system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// For the initial timer fire at `deadline`, the upper limit to the allowable delay is set to - /// `leeway`. For the subsequent timer fires at `deadline + N * repeating`, the upper - /// limit is the smaller of `leeway` and `repeating/2`. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - /// - parameter deadline: the time at which the first timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on Mach absolute - /// time. - /// - parameter repeating: the repeat interval for the timer, or `.never` if the timer should fire - /// only once. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, introduced: 4) - public func schedule(deadline: DispatchTime, repeating interval: DispatchTimeInterval = .never, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, deadline.rawValue, interval == .never ? ~0 : UInt64(interval.rawValue), UInt64(leeway.rawValue)) - } - - /// - /// Sets the deadline, repeat interval and leeway for a timer event. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared. The next timer event will occur at `deadline` and every `repeating` seconds - /// thereafter until the timer source is canceled. If the value of `repeating` is `.infinity`, - /// the timer fires only once. - /// - /// Delivery of a timer event may be delayed by the system in order to improve power consumption - /// and system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// For the initial timer fire at `deadline`, the upper limit to the allowable delay is set to - /// `leeway`. For the subsequent timer fires at `deadline + N * repeating`, the upper - /// limit is the smaller of `leeway` and `repeating/2`. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - /// - parameter deadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on Mach absolute - /// time. - /// - parameter repeating: the repeat interval for the timer in seconds, or `.infinity` if the timer - /// should fire only once. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, introduced: 4) - public func schedule(deadline: DispatchTime, repeating interval: Double, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, deadline.rawValue, interval.isInfinite ? ~0 : UInt64(interval * Double(NSEC_PER_SEC)), UInt64(leeway.rawValue)) - } - - /// - /// Sets the deadline, repeat interval and leeway for a timer event. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared. The next timer event will occur at `wallDeadline` and every `repeating` units of - /// time thereafter until the timer source is canceled. If the value of `repeating` is `.never`, - /// or is defaulted, the timer fires only once. - /// - /// Delivery of a timer event may be delayed by the system in order to improve power consumption and - /// system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// For the initial timer fire at `wallDeadline`, the upper limit to the allowable delay is set to - /// `leeway`. For the subsequent timer fires at `wallDeadline + N * repeating`, the upper - /// limit is the smaller of `leeway` and `repeating/2`. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - /// - parameter wallDeadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on - /// `gettimeofday(3)`. - /// - parameter repeating: the repeat interval for the timer, or `.never` if the timer should fire - /// only once. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, introduced: 4) - public func schedule(wallDeadline: DispatchWallTime, repeating interval: DispatchTimeInterval = .never, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, wallDeadline.rawValue, interval == .never ? ~0 : UInt64(interval.rawValue), UInt64(leeway.rawValue)) - } - - /// - /// Sets the deadline, repeat interval and leeway for a timer event that fires at least once. - /// - /// Once this function returns, any pending source data accumulated for the previous timer values - /// has been cleared. The next timer event will occur at `wallDeadline` and every `repeating` seconds - /// thereafter until the timer source is canceled. If the value of `repeating` is `.infinity`, - /// the timer fires only once. - /// - /// Delivery of a timer event may be delayed by the system in order to improve power consumption - /// and system performance. The upper limit to the allowable delay may be configured with the `leeway` - /// argument; the lower limit is under the control of the system. - /// - /// For the initial timer fire at `wallDeadline`, the upper limit to the allowable delay is set to - /// `leeway`. For the subsequent timer fires at `wallDeadline + N * repeating`, the upper - /// limit is the smaller of `leeway` and `repeating/2`. - /// - /// The lower limit to the allowable delay may vary with process state such as visibility of the - /// application UI. If the timer source was created with flags `TimerFlags.strict`, the system - /// will make a best effort to strictly observe the provided `leeway` value, even if it is smaller - /// than the current lower limit. Note that a minimal amount of delay is to be expected even if - /// this flag is specified. - /// - /// Calling this method has no effect if the timer source has already been canceled. - /// - /// - parameter wallDeadline: the time at which the timer event will be delivered, subject to the - /// leeway and other considerations described above. The deadline is based on - /// `gettimeofday(3)`. - /// - parameter repeating: the repeat interval for the timer in secondss, or `.infinity` if the timer - /// should fire only once. - /// - parameter leeway: the leeway for the timer. - /// - @available(swift, introduced: 4) - public func schedule(wallDeadline: DispatchWallTime, repeating interval: Double, leeway: DispatchTimeInterval = .nanoseconds(0)) { - __dispatch_source_set_timer(self as! DispatchSource, wallDeadline.rawValue, interval.isInfinite ? ~0 : UInt64(interval * Double(NSEC_PER_SEC)), UInt64(leeway.rawValue)) - } -} - -extension DispatchSourceFileSystemObject { - public var handle: Int32 { - return Int32(__dispatch_source_get_handle(self as! DispatchSource)) - } - - public var data: DispatchSource.FileSystemEvent { - let data = __dispatch_source_get_data(self as! DispatchSource) - return DispatchSource.FileSystemEvent(rawValue: data) - } - - public var mask: DispatchSource.FileSystemEvent { - let data = __dispatch_source_get_mask(self as! DispatchSource) - return DispatchSource.FileSystemEvent(rawValue: data) - } -} - -extension DispatchSourceUserDataAdd { - /// @function add - /// - /// @abstract - /// Merges data into a dispatch source of type DISPATCH_SOURCE_TYPE_DATA_ADD - /// and submits its event handler block to its target queue. - /// - /// @param data - /// The value to add to the current pending data. A value of zero has no effect - /// and will not result in the submission of the event handler block. - public func add(data: UInt) { - __dispatch_source_merge_data(self as! DispatchSource, data) - } -} - -extension DispatchSourceUserDataOr { - /// @function or - /// - /// @abstract - /// Merges data into a dispatch source of type DISPATCH_SOURCE_TYPE_DATA_OR and - /// submits its event handler block to its target queue. - /// - /// @param data - /// The value to OR into the current pending data. A value of zero has no effect - /// and will not result in the submission of the event handler block. - public func or(data: UInt) { - __dispatch_source_merge_data(self as! DispatchSource, data) - } -} - -extension DispatchSourceUserDataReplace { - /// @function replace - /// - /// @abstract - /// Merges data into a dispatch source of type DISPATCH_SOURCE_TYPE_DATA_REPLACE - /// and submits its event handler block to its target queue. - /// - /// @param data - /// The value that will replace the current pending data. A value of zero will be stored - /// but will not result in the submission of the event handler block. - public func replace(data: UInt) { - __dispatch_source_merge_data(self as! DispatchSource, data) - } -} diff --git a/stdlib/public/Darwin/Dispatch/Time.swift b/stdlib/public/Darwin/Dispatch/Time.swift deleted file mode 100644 index 550453040a55b..0000000000000 --- a/stdlib/public/Darwin/Dispatch/Time.swift +++ /dev/null @@ -1,226 +0,0 @@ - -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_implementationOnly import _SwiftDispatchOverlayShims - -public struct DispatchTime : Comparable { - private static let timebaseInfo: mach_timebase_info_data_t = { - var info = mach_timebase_info_data_t(numer: 1, denom: 1) - mach_timebase_info(&info) - return info - }() - - public let rawValue: dispatch_time_t - - public static func now() -> DispatchTime { - let t = __dispatch_time(0, 0) - return DispatchTime(rawValue: t) - } - - public static let distantFuture = DispatchTime(rawValue: ~0) - - fileprivate init(rawValue: dispatch_time_t) { - self.rawValue = rawValue - } - - /// Creates a `DispatchTime` relative to the system clock that - /// ticks since boot. - /// - /// - Parameters: - /// - uptimeNanoseconds: The number of nanoseconds since boot, excluding - /// time the system spent asleep - /// - Returns: A new `DispatchTime` - /// - Discussion: This clock is the same as the value returned by - /// `mach_absolute_time` when converted into nanoseconds. - /// On some platforms, the nanosecond value is rounded up to a - /// multiple of the Mach timebase, using the conversion factors - /// returned by `mach_timebase_info()`. The nanosecond equivalent - /// of the rounded result can be obtained by reading the - /// `uptimeNanoseconds` property. - /// Note that `DispatchTime(uptimeNanoseconds: 0)` is - /// equivalent to `DispatchTime.now()`, that is, its value - /// represents the number of nanoseconds since boot (excluding - /// system sleep time), not zero nanoseconds since boot. - public init(uptimeNanoseconds: UInt64) { - var rawValue = uptimeNanoseconds - - // UInt64.max means distantFuture. Do not try to scale it. - if rawValue != UInt64.max && DispatchTime.timebaseInfo.numer != DispatchTime.timebaseInfo.denom { - var (result, overflow) = rawValue.multipliedReportingOverflow(by: UInt64(DispatchTime.timebaseInfo.denom)) - if !overflow { - (result, overflow) = result.addingReportingOverflow(UInt64(DispatchTime.timebaseInfo.numer - 1)) - } - rawValue = overflow ? UInt64.max : result / UInt64(DispatchTime.timebaseInfo.numer) - } - self.rawValue = dispatch_time_t(rawValue) - } - - public var uptimeNanoseconds: UInt64 { - var result = self.rawValue - var overflow: Bool - - // UInt64.max means distantFuture. Do not try to scale it. - if rawValue != UInt64.max && DispatchTime.timebaseInfo.numer != DispatchTime.timebaseInfo.denom { - (result, overflow) = result.multipliedReportingOverflow(by: UInt64(DispatchTime.timebaseInfo.numer)) - result = overflow ? UInt64.max : result / UInt64(DispatchTime.timebaseInfo.denom) - } - return result - } -} - -extension DispatchTime { - public static func < (a: DispatchTime, b: DispatchTime) -> Bool { - return a.rawValue < b.rawValue - } - - public static func ==(a: DispatchTime, b: DispatchTime) -> Bool { - return a.rawValue == b.rawValue - } -} - -public struct DispatchWallTime : Comparable { - public let rawValue: dispatch_time_t - - public static func now() -> DispatchWallTime { - return DispatchWallTime(rawValue: __dispatch_walltime(nil, 0)) - } - - public static let distantFuture = DispatchWallTime(rawValue: ~0) - - fileprivate init(rawValue: dispatch_time_t) { - self.rawValue = rawValue - } - - public init(timespec: timespec) { - var t = timespec - self.rawValue = __dispatch_walltime(&t, 0) - } -} - -extension DispatchWallTime { - public static func <(a: DispatchWallTime, b: DispatchWallTime) -> Bool { - let negativeOne: dispatch_time_t = ~0 - if b.rawValue == negativeOne { - return a.rawValue != negativeOne - } else if a.rawValue == negativeOne { - return false - } - return -Int64(bitPattern: a.rawValue) < -Int64(bitPattern: b.rawValue) - } - - public static func ==(a: DispatchWallTime, b: DispatchWallTime) -> Bool { - return a.rawValue == b.rawValue - } -} - - -// Returns m1 * m2, clamped to the range [Int64.min, Int64.max]. -// Because of the way this function is used, we can always assume -// that m2 > 0. -private func clampedInt64Product(_ m1: Int64, _ m2: Int64) -> Int64 { - assert(m2 > 0, "multiplier must be positive") - let (result, overflow) = m1.multipliedReportingOverflow(by: m2) - if overflow { - return m1 > 0 ? Int64.max : Int64.min - } - return result -} - -// Returns its argument clamped to the range [Int64.min, Int64.max]. -private func toInt64Clamped(_ value: Double) -> Int64 { - if value.isNaN { return Int64.max } - if value >= Double(Int64.max) { return Int64.max } - if value <= Double(Int64.min) { return Int64.min } - return Int64(value) -} - -/// Represents a time interval that can be used as an offset from a `DispatchTime` -/// or `DispatchWallTime`. -/// -/// For example: -/// let inOneSecond = DispatchTime.now() + DispatchTimeInterval.seconds(1) -/// -/// If the requested time interval is larger then the internal representation -/// permits, the result of adding it to a `DispatchTime` or `DispatchWallTime` -/// is `DispatchTime.distantFuture` and `DispatchWallTime.distantFuture` -/// respectively. Such time intervals compare as equal: -/// -/// let t1 = DispatchTimeInterval.seconds(Int.max) -/// let t2 = DispatchTimeInterval.milliseconds(Int.max) -/// let result = t1 == t2 // true -public enum DispatchTimeInterval : Equatable { - case seconds(Int) - case milliseconds(Int) - case microseconds(Int) - case nanoseconds(Int) - case never - - internal var rawValue: Int64 { - switch self { - case .seconds(let s): return clampedInt64Product(Int64(s), Int64(NSEC_PER_SEC)) - case .milliseconds(let ms): return clampedInt64Product(Int64(ms), Int64(NSEC_PER_MSEC)) - case .microseconds(let us): return clampedInt64Product(Int64(us), Int64(NSEC_PER_USEC)) - case .nanoseconds(let ns): return Int64(ns) - case .never: return Int64.max - } - } - - public static func ==(lhs: DispatchTimeInterval, rhs: DispatchTimeInterval) -> Bool { - switch (lhs, rhs) { - case (.never, .never): return true - case (.never, _): return false - case (_, .never): return false - default: return lhs.rawValue == rhs.rawValue - } - } -} - -public func +(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime { - let t = __dispatch_time(time.rawValue, interval.rawValue) - return DispatchTime(rawValue: t) -} - -public func -(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime { - let t = __dispatch_time(time.rawValue, -interval.rawValue) - return DispatchTime(rawValue: t) -} - -public func +(time: DispatchTime, seconds: Double) -> DispatchTime { - let t = __dispatch_time(time.rawValue, toInt64Clamped(seconds * Double(NSEC_PER_SEC))); - return DispatchTime(rawValue: t) -} - -public func -(time: DispatchTime, seconds: Double) -> DispatchTime { - let t = __dispatch_time(time.rawValue, toInt64Clamped(-seconds * Double(NSEC_PER_SEC))); - return DispatchTime(rawValue: t) -} - -public func +(time: DispatchWallTime, interval: DispatchTimeInterval) -> DispatchWallTime { - let t = __dispatch_time(time.rawValue, interval.rawValue) - return DispatchWallTime(rawValue: t) -} - -public func -(time: DispatchWallTime, interval: DispatchTimeInterval) -> DispatchWallTime { - let t = __dispatch_time(time.rawValue, -interval.rawValue) - return DispatchWallTime(rawValue: t) -} - -public func +(time: DispatchWallTime, seconds: Double) -> DispatchWallTime { - let t = __dispatch_time(time.rawValue, toInt64Clamped(seconds * Double(NSEC_PER_SEC))); - return DispatchWallTime(rawValue: t) -} - -public func -(time: DispatchWallTime, seconds: Double) -> DispatchWallTime { - let t = __dispatch_time(time.rawValue, toInt64Clamped(-seconds * Double(NSEC_PER_SEC))); - return DispatchWallTime(rawValue: t) -} diff --git a/stdlib/public/Darwin/Foundation/AffineTransform.swift b/stdlib/public/Darwin/Foundation/AffineTransform.swift deleted file mode 100644 index 9db3302ca27dd..0000000000000 --- a/stdlib/public/Darwin/Foundation/AffineTransform.swift +++ /dev/null @@ -1,362 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -#if os(macOS) - -private let ε: CGFloat = 2.22045e-16 - -public struct AffineTransform : ReferenceConvertible, Hashable, CustomStringConvertible { - public var m11, m12, m21, m22, tX, tY: CGFloat - - public typealias ReferenceType = NSAffineTransform - - /** - Creates an affine transformation. - */ - public init(m11: CGFloat, m12: CGFloat, m21: CGFloat, m22: CGFloat, tX: CGFloat, tY: CGFloat) { - self.m11 = m11 - self.m12 = m12 - self.m21 = m21 - self.m22 = m22 - self.tX = tX - self.tY = tY - } - - private init(reference: __shared NSAffineTransform) { - m11 = reference.transformStruct.m11 - m12 = reference.transformStruct.m12 - m21 = reference.transformStruct.m21 - m22 = reference.transformStruct.m22 - tX = reference.transformStruct.tX - tY = reference.transformStruct.tY - } - - private var reference: NSAffineTransform { - let ref = NSAffineTransform() - ref.transformStruct = NSAffineTransformStruct(m11: m11, m12: m12, m21: m21, m22: m22, tX: tX, tY: tY) - return ref - } - - /** - Creates an affine transformation matrix with identity values. - - seealso: identity - */ - public init() { - self.init(m11: CGFloat(1.0), m12: CGFloat(0.0), - m21: CGFloat(0.0), m22: CGFloat(1.0), - tX: CGFloat(0.0), tY: CGFloat(0.0)) - } - - /** - Creates an affine transformation matrix from translation values. - The matrix takes the following form: - - [ 1 0 0 ] - [ 0 1 0 ] - [ x y 1 ] - */ - public init(translationByX x: CGFloat, byY y: CGFloat) { - self.init(m11: CGFloat(1.0), m12: CGFloat(0.0), - m21: CGFloat(0.0), m22: CGFloat(1.0), - tX: x, tY: y) - } - - /** - Creates an affine transformation matrix from scaling values. - The matrix takes the following form: - - [ x 0 0 ] - [ 0 y 0 ] - [ 0 0 1 ] - */ - public init(scaleByX x: CGFloat, byY y: CGFloat) { - self.init(m11: x, m12: CGFloat(0.0), - m21: CGFloat(0.0), m22: y, - tX: CGFloat(0.0), tY: CGFloat(0.0)) - } - - /** - Creates an affine transformation matrix from scaling a single value. - The matrix takes the following form: - - [ f 0 0 ] - [ 0 f 0 ] - [ 0 0 1 ] - */ - public init(scale factor: CGFloat) { - self.init(scaleByX: factor, byY: factor) - } - - /** - Creates an affine transformation matrix from rotation value (angle in radians). - The matrix takes the following form: - - [ cos α sin α 0 ] - [ -sin α cos α 0 ] - [ 0 0 1 ] - */ - public init(rotationByRadians angle: CGFloat) { - let α = Double(angle) - - let sine = CGFloat(sin(α)) - let cosine = CGFloat(cos(α)) - - self.init(m11: cosine, m12: sine, m21: -sine, m22: cosine, tX: 0, tY: 0) - } - - /** - Creates an affine transformation matrix from a rotation value (angle in degrees). - The matrix takes the following form: - - [ cos α sin α 0 ] - [ -sin α cos α 0 ] - [ 0 0 1 ] - */ - public init(rotationByDegrees angle: CGFloat) { - let α = angle * .pi / 180.0 - self.init(rotationByRadians: α) - } - - /** - An identity affine transformation matrix - - [ 1 0 0 ] - [ 0 1 0 ] - [ 0 0 1 ] - */ - public static let identity = AffineTransform(m11: 1, m12: 0, m21: 0, m22: 1, tX: 0, tY: 0) - - // Translating - - public mutating func translate(x: CGFloat, y: CGFloat) { - tX += m11 * x + m21 * y - tY += m12 * x + m22 * y - } - - /** - Mutates an affine transformation matrix from a rotation value (angle α in degrees). - The matrix takes the following form: - - [ cos α sin α 0 ] - [ -sin α cos α 0 ] - [ 0 0 1 ] - */ - public mutating func rotate(byDegrees angle: CGFloat) { - let α = angle * .pi / 180.0 - return rotate(byRadians: α) - } - - /** - Mutates an affine transformation matrix from a rotation value (angle α in radians). - The matrix takes the following form: - - [ cos α sin α 0 ] - [ -sin α cos α 0 ] - [ 0 0 1 ] - */ - public mutating func rotate(byRadians angle: CGFloat) { - let t2 = self - let t1 = AffineTransform(rotationByRadians: angle) - - var t = AffineTransform.identity - - t.m11 = t1.m11 * t2.m11 + t1.m12 * t2.m21 - t.m12 = t1.m11 * t2.m12 + t1.m12 * t2.m22 - t.m21 = t1.m21 * t2.m11 + t1.m22 * t2.m21 - t.m22 = t1.m21 * t2.m12 + t1.m22 * t2.m22 - t.tX = t1.tX * t2.m11 + t1.tY * t2.m21 + t2.tX - t.tY = t1.tX * t2.m12 + t1.tY * t2.m22 + t2.tY - - self = t - } - - /** - Creates an affine transformation matrix by combining the receiver with `transformStruct`. - That is, it computes `T * M` and returns the result, where `T` is the receiver's and `M` is - the `transformStruct`'s affine transformation matrix. - The resulting matrix takes the following form: - - [ m11_T m12_T 0 ] [ m11_M m12_M 0 ] - T * M = [ m21_T m22_T 0 ] [ m21_M m22_M 0 ] - [ tX_T tY_T 1 ] [ tX_M tY_M 1 ] - - [ (m11_T*m11_M + m12_T*m21_M) (m11_T*m12_M + m12_T*m22_M) 0 ] - = [ (m21_T*m11_M + m22_T*m21_M) (m21_T*m12_M + m22_T*m22_M) 0 ] - [ (tX_T*m11_M + tY_T*m21_M + tX_M) (tX_T*m12_M + tY_T*m22_M + tY_M) 1 ] - */ - private func concatenated(_ other: AffineTransform) -> AffineTransform { - let (t, m) = (self, other) - - // this could be optimized with a vector version - return AffineTransform( - m11: (t.m11 * m.m11) + (t.m12 * m.m21), m12: (t.m11 * m.m12) + (t.m12 * m.m22), - m21: (t.m21 * m.m11) + (t.m22 * m.m21), m22: (t.m21 * m.m12) + (t.m22 * m.m22), - tX: (t.tX * m.m11) + (t.tY * m.m21) + m.tX, - tY: (t.tX * m.m12) + (t.tY * m.m22) + m.tY - ) - } - - // Scaling - public mutating func scale(_ scale: CGFloat) { - self.scale(x: scale, y: scale) - } - - public mutating func scale(x: CGFloat, y: CGFloat) { - m11 *= x - m12 *= x - m21 *= y - m22 *= y - } - - /** - Inverts the transformation matrix if possible. Matrices with a determinant that is less than - the smallest valid representation of a double value greater than zero are considered to be - invalid for representing as an inverse. If the input AffineTransform can potentially fall into - this case then the inverted() method is suggested to be used instead since that will return - an optional value that will be nil in the case that the matrix cannot be inverted. - - D = (m11 * m22) - (m12 * m21) - - D < ε the inverse is undefined and will be nil - */ - public mutating func invert() { - guard let inverse = inverted() else { - fatalError("Transform has no inverse") - } - self = inverse - } - - public func inverted() -> AffineTransform? { - let determinant = (m11 * m22) - (m12 * m21) - if abs(determinant) <= ε { - return nil - } - var inverse = AffineTransform() - inverse.m11 = m22 / determinant - inverse.m12 = -m12 / determinant - inverse.m21 = -m21 / determinant - inverse.m22 = m11 / determinant - inverse.tX = (m21 * tY - m22 * tX) / determinant - inverse.tY = (m12 * tX - m11 * tY) / determinant - return inverse - } - - // Transforming with transform - public mutating func append(_ transform: AffineTransform) { - self = concatenated(transform) - } - - public mutating func prepend(_ transform: AffineTransform) { - self = transform.concatenated(self) - } - - // Transforming points and sizes - public func transform(_ point: NSPoint) -> NSPoint { - var newPoint = NSPoint() - newPoint.x = (m11 * point.x) + (m21 * point.y) + tX - newPoint.y = (m12 * point.x) + (m22 * point.y) + tY - return newPoint - } - - public func transform(_ size: NSSize) -> NSSize { - var newSize = NSSize() - newSize.width = (m11 * size.width) + (m21 * size.height) - newSize.height = (m12 * size.width) + (m22 * size.height) - return newSize - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(m11) - hasher.combine(m12) - hasher.combine(m21) - hasher.combine(m22) - hasher.combine(tX) - hasher.combine(tY) - } - - public var description: String { - return "{m11:\(m11), m12:\(m12), m21:\(m21), m22:\(m22), tX:\(tX), tY:\(tY)}" - } - - public var debugDescription: String { - return description - } - - public static func ==(lhs: AffineTransform, rhs: AffineTransform) -> Bool { - return lhs.m11 == rhs.m11 && lhs.m12 == rhs.m12 && - lhs.m21 == rhs.m21 && lhs.m22 == rhs.m22 && - lhs.tX == rhs.tX && lhs.tY == rhs.tY - } - -} - -extension AffineTransform : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSAffineTransform.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSAffineTransform { - return self.reference - } - - public static func _forceBridgeFromObjectiveC(_ x: NSAffineTransform, result: inout AffineTransform?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge type") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSAffineTransform, result: inout AffineTransform?) -> Bool { - result = AffineTransform(reference: x) - return true // Can't fail - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ x: NSAffineTransform?) -> AffineTransform { - guard let src = x else { return AffineTransform.identity } - return AffineTransform(reference: src) - } -} - -extension NSAffineTransform : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as AffineTransform) - } -} - -extension AffineTransform : Codable { - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - m11 = try container.decode(CGFloat.self) - m12 = try container.decode(CGFloat.self) - m21 = try container.decode(CGFloat.self) - m22 = try container.decode(CGFloat.self) - tX = try container.decode(CGFloat.self) - tY = try container.decode(CGFloat.self) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(self.m11) - try container.encode(self.m12) - try container.encode(self.m21) - try container.encode(self.m22) - try container.encode(self.tX) - try container.encode(self.tY) - } -} - -#endif diff --git a/stdlib/public/Darwin/Foundation/Boxing.swift b/stdlib/public/Darwin/Foundation/Boxing.swift deleted file mode 100644 index 272b5956e0deb..0000000000000 --- a/stdlib/public/Darwin/Foundation/Boxing.swift +++ /dev/null @@ -1,64 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -/// A class type which acts as a handle (pointer-to-pointer) to a Foundation reference type which has only a mutable class (e.g., NSURLComponents). -/// -/// Note: This assumes that the result of calling copy() is mutable. The documentation says that classes which do not have a mutable/immutable distinction should just adopt NSCopying instead of NSMutableCopying. -internal final class _MutableHandle - where MutableType : NSCopying { - fileprivate var _pointer : MutableType - - init(reference : __shared MutableType) { - _pointer = reference.copy() as! MutableType - } - - init(adoptingReference reference: MutableType) { - _pointer = reference - } - - /// Apply a closure to the reference type. - func map(_ whatToDo : (MutableType) throws -> ReturnType) rethrows -> ReturnType { - return try whatToDo(_pointer) - } - - func _copiedReference() -> MutableType { - return _pointer.copy() as! MutableType - } - - func _uncopiedReference() -> MutableType { - return _pointer - } -} - -/// Describes common operations for Foundation struct types that are bridged to a mutable object (e.g. NSURLComponents). -internal protocol _MutableBoxing : ReferenceConvertible { - var _handle : _MutableHandle { get set } - - /// Apply a mutating closure to the reference type, regardless if it is mutable or immutable. - /// - /// This function performs the correct copy-on-write check for efficient mutation. - mutating func _applyMutation(_ whatToDo : (ReferenceType) -> ReturnType) -> ReturnType -} - -extension _MutableBoxing { - @inline(__always) - mutating func _applyMutation(_ whatToDo : (ReferenceType) -> ReturnType) -> ReturnType { - // Only create a new box if we are not uniquely referenced - if !isKnownUniquelyReferenced(&_handle) { - let ref = _handle._pointer - _handle = _MutableHandle(reference: ref) - } - return whatToDo(_handle._pointer) - } -} diff --git a/stdlib/public/Darwin/Foundation/BundleLookup.mm b/stdlib/public/Darwin/Foundation/BundleLookup.mm deleted file mode 100644 index e19207d9833a2..0000000000000 --- a/stdlib/public/Darwin/Foundation/BundleLookup.mm +++ /dev/null @@ -1,46 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2018 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 -// -//===----------------------------------------------------------------------===// - -#import - -#include - -// This method is only used on "embedded" targets. It's not necessary on -// Mac or simulators. -#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR - -/// CoreFoundation SPI for finding the enclosing bundle. This is only -/// ever called on older OSes, so there's no worry of running into -/// trouble if the implementation is changed later on. -extern "C" CFURLRef _CFBundleCopyBundleURLForExecutableURL(CFURLRef url); - -@implementation NSBundle (SwiftAdditions) - -/// Given an executable path as a C string, look up the corresponding -/// NSBundle instance, if any. -+ (NSBundle *)_swift_bundleWithExecutablePath: (const char *)path { - NSString *nspath = [[NSFileManager defaultManager] - stringWithFileSystemRepresentation:path length:strlen(path)]; - NSURL *executableURL = [NSURL fileURLWithPath:nspath]; - NSURL *bundleURL = - (NSURL *)_CFBundleCopyBundleURLForExecutableURL((CFURLRef)executableURL); - if (!bundleURL) - return nil; - - NSBundle *bundle = [NSBundle bundleWithURL: bundleURL]; - [bundleURL release]; - return bundle; -} - -@end - -#endif diff --git a/stdlib/public/Darwin/Foundation/CMakeLists.txt b/stdlib/public/Darwin/Foundation/CMakeLists.txt deleted file mode 100644 index da9a8428a25db..0000000000000 --- a/stdlib/public/Darwin/Foundation/CMakeLists.txt +++ /dev/null @@ -1,102 +0,0 @@ -cmake_minimum_required(VERSION 3.4.3) -include("../../../../cmake/modules/StandaloneOverlay.cmake") - -add_swift_target_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY - AffineTransform.swift - Boxing.swift - BundleLookup.mm - Calendar.swift - CharacterSet.swift - CheckClass.swift - Codable.swift - Collections+DataProtocol.swift - CombineTypealiases.swift - ContiguousBytes.swift - Data.swift - DataProtocol.swift - DataThunks.m - Date.swift - DateComponents.swift - DateInterval.swift - Decimal.swift - DispatchData+DataProtocol.swift - FileManager.swift - Foundation.swift - IndexPath.swift - IndexSet.swift - JSONEncoder.swift - Locale.swift - Measurement.swift - Notification.swift - NSArray.swift - NSCoder.swift - NSData+DataProtocol.swift - NSDate.swift - NSDictionary.swift - NSError.swift - NSExpression.swift - NSFastEnumeration.swift - NSGeometry.swift - NSIndexSet.swift - NSItemProvider.swift - NSNumber.swift - NSObject.swift - NSOrderedCollectionDifference.swift - NSPredicate.swift - NSRange.swift - NSSet.swift - NSSortDescriptor.swift - NSString.swift - NSStringAPI.swift - NSStringEncodings.swift - NSTextCheckingResult.swift - NSUndoManager.swift - NSURL.swift - PersonNameComponents.swift - PlistEncoder.swift - Pointers+DataProtocol.swift - Progress.swift - Publishers+KeyValueObserving.swift - Publishers+Locking.swift - Publishers+NotificationCenter.swift - Publishers+Timer.swift - Publishers+URLSession.swift - ReferenceConvertible.swift - Scanner.swift - Schedulers+Date.swift - Schedulers+OperationQueue.swift - Schedulers+RunLoop.swift - String.swift - TimeZone.swift - URL.swift - URLCache.swift - URLComponents.swift - URLRequest.swift - URLSession.swift - UUID.swift - - "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" - - GYB_SOURCES - NSValue.swift.gyb - - SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}" "-Xllvm" "-sil-inline-generics" "-Xllvm" "-sil-partial-specialization" "${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}" - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - - SWIFT_MODULE_DEPENDS_OSX Darwin CoreGraphics Dispatch CoreFoundation ObjectiveC # auto-updated - CoreGraphics # imported in Swift - SWIFT_MODULE_DEPENDS_IOS Darwin Dispatch CoreFoundation ObjectiveC # auto-updated - CoreGraphics # imported in Swift - SWIFT_MODULE_DEPENDS_TVOS Darwin Dispatch CoreFoundation ObjectiveC # auto-updated - CoreGraphics # imported in Swift - SWIFT_MODULE_DEPENDS_WATCHOS Darwin Dispatch CoreFoundation ObjectiveC # auto-updated - CoreGraphics # imported in Swift - FRAMEWORK_DEPENDS Foundation - FRAMEWORK_DEPENDS_WEAK Combine - - DEPLOYMENT_VERSION_OSX ${SWIFTLIB_DEPLOYMENT_VERSION_FOUNDATION_OSX} - DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_FOUNDATION_IOS} - DEPLOYMENT_VERSION_TVOS ${SWIFTLIB_DEPLOYMENT_VERSION_FOUNDATION_TVOS} - DEPLOYMENT_VERSION_WATCHOS ${SWIFTLIB_DEPLOYMENT_VERSION_FOUNDATION_WATCHOS} - INSTALL_IN_COMPONENT sdk-overlay -) diff --git a/stdlib/public/Darwin/Foundation/Calendar.swift b/stdlib/public/Darwin/Foundation/Calendar.swift deleted file mode 100644 index 00e545325cd34..0000000000000 --- a/stdlib/public/Darwin/Foundation/Calendar.swift +++ /dev/null @@ -1,1164 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -/** - `Calendar` encapsulates information about systems of reckoning time in which the beginning, length, and divisions of a year are defined. It provides information about the calendar and support for calendrical computations such as determining the range of a given calendrical unit and adding units to a given absolute time. -*/ -public struct Calendar : Hashable, Equatable, ReferenceConvertible, _MutableBoxing { - public typealias ReferenceType = NSCalendar - - private var _autoupdating: Bool - internal var _handle: _MutableHandle - - /// Calendar supports many different kinds of calendars. Each is identified by an identifier here. - public enum Identifier { - /// The common calendar in Europe, the Western Hemisphere, and elsewhere. - case gregorian - - case buddhist - case chinese - case coptic - case ethiopicAmeteMihret - case ethiopicAmeteAlem - case hebrew - case iso8601 - case indian - case islamic - case islamicCivil - case japanese - case persian - case republicOfChina - - /// A simple tabular Islamic calendar using the astronomical/Thursday epoch of CE 622 July 15 - @available(macOS 10.10, iOS 8.0, *) - case islamicTabular - - /// The Islamic Umm al-Qura calendar used in Saudi Arabia. This is based on astronomical calculation, instead of tabular behavior. - @available(macOS 10.10, iOS 8.0, *) - case islamicUmmAlQura - - } - - /// An enumeration for the various components of a calendar date. - /// - /// Several `Calendar` APIs use either a single unit or a set of units as input to a search algorithm. - /// - /// - seealso: `DateComponents` - public enum Component { - case era - case year - case month - case day - case hour - case minute - case second - case weekday - case weekdayOrdinal - case quarter - case weekOfMonth - case weekOfYear - case yearForWeekOfYear - case nanosecond - case calendar - case timeZone - } - - /// Returns the user's current calendar. - /// - /// This calendar does not track changes that the user makes to their preferences. - public static var current : Calendar { - return Calendar(adoptingReference: __NSCalendarCurrent() as! NSCalendar, autoupdating: false) - } - - /// A Calendar that tracks changes to user's preferred calendar. - /// - /// If mutated, this calendar will no longer track the user's preferred calendar. - /// - /// - note: The autoupdating Calendar will only compare equal to another autoupdating Calendar. - public static var autoupdatingCurrent : Calendar { - return Calendar(adoptingReference: __NSCalendarAutoupdating() as! NSCalendar, autoupdating: true) - } - - // MARK: - - // MARK: init - - /// Returns a new Calendar. - /// - /// - parameter identifier: The kind of calendar to use. - public init(identifier: __shared Identifier) { - let result = __NSCalendarCreate(Calendar._toNSCalendarIdentifier(identifier)) - _handle = _MutableHandle(adoptingReference: result as! NSCalendar) - _autoupdating = false - } - - // MARK: - - // MARK: Bridging - - fileprivate init(reference : __shared NSCalendar) { - _handle = _MutableHandle(reference: reference) - if __NSCalendarIsAutoupdating(reference) { - _autoupdating = true - } else { - _autoupdating = false - } - } - - private init(adoptingReference reference: NSCalendar, autoupdating: Bool) { - _handle = _MutableHandle(adoptingReference: reference) - _autoupdating = autoupdating - } - - // MARK: - - // - - /// The identifier of the calendar. - public var identifier : Identifier { - return Calendar._fromNSCalendarIdentifier(_handle.map({ $0.calendarIdentifier })) - } - - /// The locale of the calendar. - public var locale : Locale? { - get { - return _handle.map { $0.locale } - } - set { - _applyMutation { $0.locale = newValue } - } - } - - /// The time zone of the calendar. - public var timeZone : TimeZone { - get { - return _handle.map { $0.timeZone } - } - set { - _applyMutation { $0.timeZone = newValue } - } - } - - /// The first weekday of the calendar. - public var firstWeekday : Int { - get { - return _handle.map { $0.firstWeekday } - } - set { - _applyMutation { $0.firstWeekday = newValue } - } - } - - /// The number of minimum days in the first week. - public var minimumDaysInFirstWeek : Int { - get { - return _handle.map { $0.minimumDaysInFirstWeek } - } - set { - _applyMutation { $0.minimumDaysInFirstWeek = newValue } - } - } - - /// A list of eras in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["BC", "AD"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var eraSymbols: [String] { - return _handle.map { $0.eraSymbols } - } - - /// A list of longer-named eras in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Before Christ", "Anno Domini"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var longEraSymbols: [String] { - return _handle.map { $0.longEraSymbols } - } - - /// A list of months in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var monthSymbols: [String] { - return _handle.map { $0.monthSymbols } - } - - /// A list of shorter-named months in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var shortMonthSymbols: [String] { - return _handle.map { $0.shortMonthSymbols } - } - - /// A list of very-shortly-named months in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var veryShortMonthSymbols: [String] { - return _handle.map { $0.veryShortMonthSymbols } - } - - /// A list of standalone months in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]`. - /// - note: Stand-alone properties are for use in places like calendar headers. Non-stand-alone properties are for use in context (for example, "Saturday, November 12th"). - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var standaloneMonthSymbols: [String] { - return _handle.map { $0.standaloneMonthSymbols } - } - - /// A list of shorter-named standalone months in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]`. - /// - note: Stand-alone properties are for use in places like calendar headers. Non-stand-alone properties are for use in context (for example, "Saturday, November 12th"). - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var shortStandaloneMonthSymbols: [String] { - return _handle.map { $0.shortStandaloneMonthSymbols } - } - - /// A list of very-shortly-named standalone months in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"]`. - /// - note: Stand-alone properties are for use in places like calendar headers. Non-stand-alone properties are for use in context (for example, "Saturday, November 12th"). - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var veryShortStandaloneMonthSymbols: [String] { - return _handle.map { $0.veryShortStandaloneMonthSymbols } - } - - /// A list of weekdays in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var weekdaySymbols: [String] { - return _handle.map { $0.weekdaySymbols } - } - - /// A list of shorter-named weekdays in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var shortWeekdaySymbols: [String] { - return _handle.map { $0.shortWeekdaySymbols } - } - - /// A list of very-shortly-named weekdays in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["S", "M", "T", "W", "T", "F", "S"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var veryShortWeekdaySymbols: [String] { - return _handle.map { $0.veryShortWeekdaySymbols } - } - - /// A list of standalone weekday names in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]`. - /// - note: Stand-alone properties are for use in places like calendar headers. Non-stand-alone properties are for use in context (for example, "Saturday, November 12th"). - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var standaloneWeekdaySymbols: [String] { - return _handle.map { $0.standaloneWeekdaySymbols } - } - - /// A list of shorter-named standalone weekdays in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]`. - /// - note: Stand-alone properties are for use in places like calendar headers. Non-stand-alone properties are for use in context (for example, "Saturday, November 12th"). - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var shortStandaloneWeekdaySymbols: [String] { - return _handle.map { $0.shortStandaloneWeekdaySymbols } - } - - /// A list of very-shortly-named weekdays in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["S", "M", "T", "W", "T", "F", "S"]`. - /// - note: Stand-alone properties are for use in places like calendar headers. Non-stand-alone properties are for use in context (for example, "Saturday, November 12th"). - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var veryShortStandaloneWeekdaySymbols: [String] { - return _handle.map { $0.veryShortStandaloneWeekdaySymbols } - } - - /// A list of quarter names in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var quarterSymbols: [String] { - return _handle.map { $0.quarterSymbols } - } - - /// A list of shorter-named quarters in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Q1", "Q2", "Q3", "Q4"]`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var shortQuarterSymbols: [String] { - return _handle.map { $0.shortQuarterSymbols } - } - - /// A list of standalone quarter names in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"]`. - /// - note: Stand-alone properties are for use in places like calendar headers. Non-stand-alone properties are for use in context (for example, "Saturday, November 12th"). - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var standaloneQuarterSymbols: [String] { - return _handle.map { $0.standaloneQuarterSymbols } - } - - /// A list of shorter-named standalone quarters in this calendar, localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `["Q1", "Q2", "Q3", "Q4"]`. - /// - note: Stand-alone properties are for use in places like calendar headers. Non-stand-alone properties are for use in context (for example, "Saturday, November 12th"). - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var shortStandaloneQuarterSymbols: [String] { - return _handle.map { $0.shortStandaloneQuarterSymbols } - } - - /// The symbol used to represent "AM", localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `"AM"`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var amSymbol: String { - return _handle.map { $0.amSymbol } - } - - /// The symbol used to represent "PM", localized to the Calendar's `locale`. - /// - /// For example, for English in the Gregorian calendar, returns `"PM"`. - /// - /// - note: By default, Calendars have no locale set. If you wish to receive a localized answer, be sure to set the `locale` property first - most likely to `Locale.autoupdatingCurrent`. - public var pmSymbol: String { - return _handle.map { $0.pmSymbol } - } - - // MARK: - - // - - /// Returns the minimum range limits of the values that a given component can take on in the receiver. - /// - /// As an example, in the Gregorian calendar the minimum range of values for the Day component is 1-28. - /// - parameter component: A component to calculate a range for. - /// - returns: The range, or nil if it could not be calculated. - public func minimumRange(of component: Component) -> Range? { - return _handle.map { Range($0.minimumRange(of: Calendar._toCalendarUnit([component]))) } - } - - /// The maximum range limits of the values that a given component can take on in the receive - /// - /// As an example, in the Gregorian calendar the maximum range of values for the Day component is 1-31. - /// - parameter component: A component to calculate a range for. - /// - returns: The range, or nil if it could not be calculated. - public func maximumRange(of component: Component) -> Range? { - return _handle.map { Range($0.maximumRange(of: Calendar._toCalendarUnit([component]))) } - } - - - @available(*, unavailable, message: "use range(of:in:for:) instead") - public func range(of smaller: NSCalendar.Unit, in larger: NSCalendar.Unit, for date: Date) -> NSRange { - fatalError() - } - - /// Returns the range of absolute time values that a smaller calendar component (such as a day) can take on in a larger calendar component (such as a month) that includes a specified absolute time. - /// - /// You can use this method to calculate, for example, the range the `day` component can take on in the `month` in which `date` lies. - /// - parameter smaller: The smaller calendar component. - /// - parameter larger: The larger calendar component. - /// - parameter date: The absolute time for which the calculation is performed. - /// - returns: The range of absolute time values smaller can take on in larger at the time specified by date. Returns `nil` if larger is not logically bigger than smaller in the calendar, or the given combination of components does not make sense (or is a computation which is undefined). - public func range(of smaller: Component, in larger: Component, for date: Date) -> Range? { - return _handle.map { Range($0.range(of: Calendar._toCalendarUnit([smaller]), in: Calendar._toCalendarUnit([larger]), for: date)) } - } - - @available(*, unavailable, message: "use range(of:in:for:) instead") - public func range(of unit: NSCalendar.Unit, start datep: AutoreleasingUnsafeMutablePointer?, interval tip: UnsafeMutablePointer?, for date: Date) -> Bool { - fatalError() - } - - /// Returns, via two inout parameters, the starting time and duration of a given calendar component that contains a given date. - /// - /// - seealso: `range(of:for:)` - /// - seealso: `dateInterval(of:for:)` - /// - parameter component: A calendar component. - /// - parameter start: Upon return, the starting time of the calendar component that contains the date. - /// - parameter interval: Upon return, the duration of the calendar component that contains the date. - /// - parameter date: The specified date. - /// - returns: `true` if the starting time and duration of a component could be calculated, otherwise `false`. - public func dateInterval(of component: Component, start: inout Date, interval: inout TimeInterval, for date: Date) -> Bool { - var nsDate : NSDate? - var ti : TimeInterval = 0 - if _handle.map({ $0.range(of: Calendar._toCalendarUnit([component]), start: &nsDate, interval: &ti, for: date) }) { - start = nsDate! as Date - interval = ti - return true - } else { - return false - } - } - - /// Returns the starting time and duration of a given calendar component that contains a given date. - /// - /// - parameter component: A calendar component. - /// - parameter date: The specified date. - /// - returns: A new `DateInterval` if the starting time and duration of a component could be calculated, otherwise `nil`. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public func dateInterval(of component: Component, for date: Date) -> DateInterval? { - var start : Date = Date(timeIntervalSinceReferenceDate: 0) - var interval : TimeInterval = 0 - if self.dateInterval(of: component, start: &start, interval: &interval, for: date) { - return DateInterval(start: start, duration: interval) - } else { - return nil - } - } - - /// Returns, for a given absolute time, the ordinal number of a smaller calendar component (such as a day) within a specified larger calendar component (such as a week). - /// - /// The ordinality is in most cases not the same as the decomposed value of the component. Typically return values are 1 and greater. For example, the time 00:45 is in the first hour of the day, and for components `hour` and `day` respectively, the result would be 1. An exception is the week-in-month calculation, which returns 0 for days before the first week in the month containing the date. - /// - /// - note: Some computations can take a relatively long time. - /// - parameter smaller: The smaller calendar component. - /// - parameter larger: The larger calendar component. - /// - parameter date: The absolute time for which the calculation is performed. - /// - returns: The ordinal number of smaller within larger at the time specified by date. Returns `nil` if larger is not logically bigger than smaller in the calendar, or the given combination of components does not make sense (or is a computation which is undefined). - public func ordinality(of smaller: Component, in larger: Component, for date: Date) -> Int? { - let result = _handle.map { $0.ordinality(of: Calendar._toCalendarUnit([smaller]), in: Calendar._toCalendarUnit([larger]), for: date) } - if result == NSNotFound { return nil } - return result - } - - // MARK: - - // - - @available(*, unavailable, message: "use dateComponents(_:from:) instead") - public func getEra(_ eraValuePointer: UnsafeMutablePointer?, year yearValuePointer: UnsafeMutablePointer?, month monthValuePointer: UnsafeMutablePointer?, day dayValuePointer: UnsafeMutablePointer?, from date: Date) { fatalError() } - - @available(*, unavailable, message: "use dateComponents(_:from:) instead") - public func getEra(_ eraValuePointer: UnsafeMutablePointer?, yearForWeekOfYear yearValuePointer: UnsafeMutablePointer?, weekOfYear weekValuePointer: UnsafeMutablePointer?, weekday weekdayValuePointer: UnsafeMutablePointer?, from date: Date) { fatalError() } - - @available(*, unavailable, message: "use dateComponents(_:from:) instead") - public func getHour(_ hourValuePointer: UnsafeMutablePointer?, minute minuteValuePointer: UnsafeMutablePointer?, second secondValuePointer: UnsafeMutablePointer?, nanosecond nanosecondValuePointer: UnsafeMutablePointer?, from date: Date) { fatalError() } - - // MARK: - - // - - - @available(*, unavailable, message: "use date(byAdding:to:wrappingComponents:) instead") - public func date(byAdding unit: NSCalendar.Unit, value: Int, to date: Date, options: NSCalendar.Options = []) -> Date? { fatalError() } - - /// Returns a new `Date` representing the date calculated by adding components to a given date. - /// - /// - parameter components: A set of values to add to the date. - /// - parameter date: The starting date. - /// - parameter wrappingComponents: If `true`, the component should be incremented and wrap around to zero/one on overflow, and should not cause higher components to be incremented. The default value is `false`. - /// - returns: A new date, or nil if a date could not be calculated with the given input. - public func date(byAdding components: DateComponents, to date: Date, wrappingComponents: Bool = false) -> Date? { - return _handle.map { $0.date(byAdding: components, to: date, options: wrappingComponents ? [.wrapComponents] : []) } - } - - - @available(*, unavailable, message: "use date(byAdding:to:wrappingComponents:) instead") - public func date(byAdding comps: DateComponents, to date: Date, options opts: NSCalendar.Options = []) -> Date? { fatalError() } - - /// Returns a new `Date` representing the date calculated by adding an amount of a specific component to a given date. - /// - /// - parameter component: A single component to add. - /// - parameter value: The value of the specified component to add. - /// - parameter date: The starting date. - /// - parameter wrappingComponents: If `true`, the component should be incremented and wrap around to zero/one on overflow, and should not cause higher components to be incremented. The default value is `false`. - /// - returns: A new date, or nil if a date could not be calculated with the given input. - @available(iOS 8.0, *) - public func date(byAdding component: Component, value: Int, to date: Date, wrappingComponents: Bool = false) -> Date? { - return _handle.map { $0.date(byAdding: Calendar._toCalendarUnit([component]), value: value, to: date, options: wrappingComponents ? [.wrapComponents] : []) } - } - - /// Returns a date created from the specified components. - /// - /// - parameter components: Used as input to the search algorithm for finding a corresponding date. - /// - returns: A new `Date`, or nil if a date could not be found which matches the components. - public func date(from components: DateComponents) -> Date? { - return _handle.map { $0.date(from: components) } - } - - @available(*, unavailable, renamed: "dateComponents(_:from:)") - public func components(_ unitFlags: NSCalendar.Unit, from date: Date) -> DateComponents { fatalError() } - - /// Returns all the date components of a date, using the calendar time zone. - /// - /// - note: If you want "date information in a given time zone" in order to display it, you should use `DateFormatter` to format the date. - /// - parameter date: The `Date` to use. - /// - returns: The date components of the specified date. - public func dateComponents(_ components: Set, from date: Date) -> DateComponents { - return _handle.map { $0.components(Calendar._toCalendarUnit(components), from: date) } - } - - - @available(*, unavailable, renamed: "dateComponents(in:from:)") - public func components(in timezone: TimeZone, from date: Date) -> DateComponents { fatalError() } - - /// Returns all the date components of a date, as if in a given time zone (instead of the `Calendar` time zone). - /// - /// The time zone overrides the time zone of the `Calendar` for the purposes of this calculation. - /// - note: If you want "date information in a given time zone" in order to display it, you should use `DateFormatter` to format the date. - /// - parameter timeZone: The `TimeZone` to use. - /// - parameter date: The `Date` to use. - /// - returns: All components, calculated using the `Calendar` and `TimeZone`. - @available(iOS 8.0, *) - public func dateComponents(in timeZone: TimeZone, from date: Date) -> DateComponents { - return _handle.map { $0.components(in: timeZone, from: date) } - } - - @available(*, unavailable, renamed: "dateComponents(_:from:to:)") - public func components(_ unitFlags: NSCalendar.Unit, from startingDate: Date, to resultDate: Date, options opts: NSCalendar.Options = []) -> DateComponents { fatalError() } - - /// Returns the difference between two dates. - /// - /// - parameter components: Which components to compare. - /// - parameter start: The starting date. - /// - parameter end: The ending date. - /// - returns: The result of calculating the difference from start to end. - public func dateComponents(_ components: Set, from start: Date, to end: Date) -> DateComponents { - return _handle.map { $0.components(Calendar._toCalendarUnit(components), from: start, to: end, options: []) } - } - - @available(*, unavailable, renamed: "dateComponents(_:from:to:)") - public func components(_ unitFlags: NSCalendar.Unit, from startingDateComp: DateComponents, to resultDateComp: DateComponents, options: NSCalendar.Options = []) -> DateComponents { fatalError() } - - /// Returns the difference between two dates specified as `DateComponents`. - /// - /// For components which are not specified in each `DateComponents`, but required to specify an absolute date, the base value of the component is assumed. For example, for an `DateComponents` with just a `year` and a `month` specified, a `day` of 1, and an `hour`, `minute`, `second`, and `nanosecond` of 0 are assumed. - /// Calendrical calculations with unspecified `year` or `year` value prior to the start of a calendar are not advised. - /// For each `DateComponents`, if its `timeZone` property is set, that time zone is used for it. If the `calendar` property is set, that is used rather than the receiving calendar, and if both the `calendar` and `timeZone` are set, the `timeZone` property value overrides the time zone of the `calendar` property. - /// - /// - parameter components: Which components to compare. - /// - parameter start: The starting date components. - /// - parameter end: The ending date components. - /// - returns: The result of calculating the difference from start to end. - @available(iOS 8.0, *) - public func dateComponents(_ components: Set, from start: DateComponents, to end: DateComponents) -> DateComponents { - return _handle.map { $0.components(Calendar._toCalendarUnit(components), from: start, to: end, options: []) } - } - - - /// Returns the value for one component of a date. - /// - /// - parameter component: The component to calculate. - /// - parameter date: The date to use. - /// - returns: The value for the component. - @available(iOS 8.0, *) - public func component(_ component: Component, from date: Date) -> Int { - return _handle.map { $0.component(Calendar._toCalendarUnit([component]), from: date) } - } - - - @available(*, unavailable, message: "Use date(from:) instead") - public func date(era: Int, year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, nanosecond: Int) -> Date? { fatalError() } - - - @available(*, unavailable, message: "Use date(from:) instead") - public func date(era: Int, yearForWeekOfYear: Int, weekOfYear: Int, weekday: Int, hour: Int, minute: Int, second: Int, nanosecond: Int) -> Date? { fatalError() } - - - /// Returns the first moment of a given Date, as a Date. - /// - /// For example, pass in `Date()`, if you want the start of today. - /// If there were two midnights, it returns the first. If there was none, it returns the first moment that did exist. - /// - parameter date: The date to search. - /// - returns: The first moment of the given date. - @available(iOS 8.0, *) - public func startOfDay(for date: Date) -> Date { - return _handle.map { $0.startOfDay(for: date) } - } - - - @available(*, unavailable, renamed: "compare(_:to:toGranularity:)") - public func compare(_ date1: Date, to date2: Date, toUnitGranularity unit: NSCalendar.Unit) -> ComparisonResult { fatalError() } - - - /// Compares the given dates down to the given component, reporting them `orderedSame` if they are the same in the given component and all larger components, otherwise either `orderedAscending` or `orderedDescending`. - /// - /// - parameter date1: A date to compare. - /// - parameter date2: A date to compare. - /// - parameter: component: A granularity to compare. For example, pass `.hour` to check if two dates are in the same hour. - @available(iOS 8.0, *) - public func compare(_ date1: Date, to date2: Date, toGranularity component: Component) -> ComparisonResult { - return _handle.map { $0.compare(date1, to: date2, toUnitGranularity: Calendar._toCalendarUnit([component])) } - } - - - @available(*, unavailable, renamed: "isDate(_:equalTo:toGranularity:)") - public func isDate(_ date1: Date, equalTo date2: Date, toUnitGranularity unit: NSCalendar.Unit) -> Bool { fatalError() } - - /// Compares the given dates down to the given component, reporting them equal if they are the same in the given component and all larger components. - /// - /// - parameter date1: A date to compare. - /// - parameter date2: A date to compare. - /// - parameter component: A granularity to compare. For example, pass `.hour` to check if two dates are in the same hour. - /// - returns: `true` if the given date is within tomorrow. - @available(iOS 8.0, *) - public func isDate(_ date1: Date, equalTo date2: Date, toGranularity component: Component) -> Bool { - return _handle.map { $0.isDate(date1, equalTo: date2, toUnitGranularity: Calendar._toCalendarUnit([component])) } - } - - - /// Returns `true` if the given date is within the same day as another date, as defined by the calendar and calendar's locale. - /// - /// - parameter date1: A date to check for containment. - /// - parameter date2: A date to check for containment. - /// - returns: `true` if `date1` and `date2` are in the same day. - @available(iOS 8.0, *) - public func isDate(_ date1: Date, inSameDayAs date2: Date) -> Bool { - return _handle.map { $0.isDate(date1, inSameDayAs: date2) } - } - - - /// Returns `true` if the given date is within today, as defined by the calendar and calendar's locale. - /// - /// - parameter date: The specified date. - /// - returns: `true` if the given date is within today. - @available(iOS 8.0, *) - public func isDateInToday(_ date: Date) -> Bool { - return _handle.map { $0.isDateInToday(date) } - } - - - /// Returns `true` if the given date is within yesterday, as defined by the calendar and calendar's locale. - /// - /// - parameter date: The specified date. - /// - returns: `true` if the given date is within yesterday. - @available(iOS 8.0, *) - public func isDateInYesterday(_ date: Date) -> Bool { - return _handle.map { $0.isDateInYesterday(date) } - } - - - /// Returns `true` if the given date is within tomorrow, as defined by the calendar and calendar's locale. - /// - /// - parameter date: The specified date. - /// - returns: `true` if the given date is within tomorrow. - @available(iOS 8.0, *) - public func isDateInTomorrow(_ date: Date) -> Bool { - return _handle.map { $0.isDateInTomorrow(date) } - } - - - /// Returns `true` if the given date is within a weekend period, as defined by the calendar and calendar's locale. - /// - /// - parameter date: The specified date. - /// - returns: `true` if the given date is within a weekend. - @available(iOS 8.0, *) - public func isDateInWeekend(_ date: Date) -> Bool { - return _handle.map { $0.isDateInWeekend(date) } - } - - @available(*, unavailable, message: "use dateIntervalOfWeekend(containing:start:interval:) instead") - public func range(ofWeekendStart datep: AutoreleasingUnsafeMutablePointer?, interval tip: UnsafeMutablePointer?, containing date: Date) -> Bool { fatalError() } - - /// Find the range of the weekend around the given date, returned via two by-reference parameters. - /// - /// Note that a given entire day within a calendar is not necessarily all in a weekend or not; weekends can start in the middle of a day in some calendars and locales. - /// - seealso: `dateIntervalOfWeekend(containing:)` - /// - parameter date: The date at which to start the search. - /// - parameter start: When the result is `true`, set - /// - returns: `true` if a date range could be found, and `false` if the date is not in a weekend. - @available(iOS 8.0, *) - public func dateIntervalOfWeekend(containing date: Date, start: inout Date, interval: inout TimeInterval) -> Bool { - var nsDate : NSDate? - var ti : TimeInterval = 0 - if _handle.map({ $0.range(ofWeekendStart: &nsDate, interval: &ti, containing: date) }) { - start = nsDate! as Date - interval = ti - return true - } else { - return false - } - } - - /// Returns a `DateInterval` of the weekend contained by the given date, or nil if the date is not in a weekend. - /// - /// - parameter date: The date contained in the weekend. - /// - returns: A `DateInterval`, or nil if the date is not in a weekend. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public func dateIntervalOfWeekend(containing date: Date) -> DateInterval? { - var nsDate : NSDate? - var ti : TimeInterval = 0 - if _handle.map({ $0.range(ofWeekendStart: &nsDate, interval: &ti, containing: date) }) { - return DateInterval(start: nsDate! as Date, duration: ti) - } else { - return nil - } - } - - - @available(*, unavailable, message: "use nextWeekend(startingAfter:start:interval:direction:) instead") - public func nextWeekendStart(_ datep: AutoreleasingUnsafeMutablePointer?, interval tip: UnsafeMutablePointer?, options: NSCalendar.Options = [], after date: Date) -> Bool { fatalError() } - - /// Returns the range of the next weekend via two inout parameters. The weekend starts strictly after the given date. - /// - /// If `direction` is `.backward`, then finds the previous weekend range strictly before the given date. - /// - /// Note that a given entire Day within a calendar is not necessarily all in a weekend or not; weekends can start in the middle of a day in some calendars and locales. - /// - parameter date: The date at which to begin the search. - /// - parameter direction: Which direction in time to search. The default value is `.forward`. - /// - returns: A `DateInterval`, or nil if the weekend could not be found. - @available(iOS 8.0, *) - public func nextWeekend(startingAfter date: Date, start: inout Date, interval: inout TimeInterval, direction: SearchDirection = .forward) -> Bool { - // The implementation actually overrides previousKeepSmaller and nextKeepSmaller with matchNext, always - but strict still trumps all. - var nsDate : NSDate? - var ti : TimeInterval = 0 - if _handle.map({ $0.nextWeekendStart(&nsDate, interval: &ti, options: direction == .backward ? [.searchBackwards] : [], after: date) }) { - start = nsDate! as Date - interval = ti - return true - } else { - return false - } - } - - /// Returns a `DateInterval` of the next weekend, which starts strictly after the given date. - /// - /// If `direction` is `.backward`, then finds the previous weekend range strictly before the given date. - /// - /// Note that a given entire Day within a calendar is not necessarily all in a weekend or not; weekends can start in the middle of a day in some calendars and locales. - /// - parameter date: The date at which to begin the search. - /// - parameter direction: Which direction in time to search. The default value is `.forward`. - /// - returns: A `DateInterval`, or nil if weekends do not exist in the specific calendar or locale. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public func nextWeekend(startingAfter date: Date, direction: SearchDirection = .forward) -> DateInterval? { - // The implementation actually overrides previousKeepSmaller and nextKeepSmaller with matchNext, always - but strict still trumps all. - var nsDate : NSDate? - var ti : TimeInterval = 0 - if _handle.map({ $0.nextWeekendStart(&nsDate, interval: &ti, options: direction == .backward ? [.searchBackwards] : [], after: date) }) { - /// WARNING: searching backwards is totally broken! 26643365 - return DateInterval(start: nsDate! as Date, duration: ti) - } else { - return nil - } - } - - // MARK: - - // MARK: Searching - - /// The direction in time to search. - public enum SearchDirection { - /// Search for a date later in time than the start date. - case forward - - /// Search for a date earlier in time than the start date. - case backward - } - - /// Determines which result to use when a time is repeated on a day in a calendar (for example, during a daylight saving transition when the times between 2:00am and 3:00am may happen twice). - public enum RepeatedTimePolicy { - /// If there are two or more matching times (all the components are the same, including isLeapMonth) before the end of the next instance of the next higher component to the highest specified component, then the algorithm will return the first occurrence. - case first - - /// If there are two or more matching times (all the components are the same, including isLeapMonth) before the end of the next instance of the next higher component to the highest specified component, then the algorithm will return the last occurrence. - case last - } - - /// A hint to the search algorithm to control the method used for searching for dates. - public enum MatchingPolicy { - /// If there is no matching time before the end of the next instance of the next higher component to the highest specified component in the `DateComponents` argument, the algorithm will return the next existing time which exists. - /// - /// For example, during a daylight saving transition there may be no 2:37am. The result would then be 3:00am, if that does exist. - case nextTime - - /// If specified, and there is no matching time before the end of the next instance of the next higher component to the highest specified component in the `DateComponents` argument, the method will return the next existing value of the missing component and preserves the lower components' values (e.g., no 2:37am results in 3:37am, if that exists). - case nextTimePreservingSmallerComponents - - /// If there is no matching time before the end of the next instance of the next higher component to the highest specified component in the `DateComponents` argument, the algorithm will return the previous existing value of the missing component and preserves the lower components' values. - /// - /// For example, during a daylight saving transition there may be no 2:37am. The result would then be 1:37am, if that does exist. - case previousTimePreservingSmallerComponents - - /// If specified, the algorithm travels as far forward or backward as necessary looking for a match. - /// - /// For example, if searching for Feb 29 in the Gregorian calendar, the algorithm may choose March 1 instead (for example, if the year is not a leap year). If you wish to find the next Feb 29 without the algorithm adjusting the next higher component in the specified `DateComponents`, then use the `strict` option. - /// - note: There are ultimately implementation-defined limits in how far distant the search will go. - case strict - } - - @available(*, unavailable, message: "use nextWeekend(startingAfter:matching:matchingPolicy:repeatedTimePolicy:direction:using:) instead") - public func enumerateDates(startingAfter start: Date, matching comps: DateComponents, options opts: NSCalendar.Options = [], using block: (Date?, Bool, UnsafeMutablePointer) -> Swift.Void) { fatalError() } - - /// Computes the dates which match (or most closely match) a given set of components, and calls the closure once for each of them, until the enumeration is stopped. - /// - /// There will be at least one intervening date which does not match all the components (or the given date itself must not match) between the given date and any result. - /// - /// If `direction` is set to `.backward`, this method finds the previous match before the given date. The intent is that the same matches as for a `.forward` search will be found (that is, if you are enumerating forwards or backwards for each hour with minute "27", the seconds in the date you will get in forwards search would obviously be 00, and the same will be true in a backwards search in order to implement this rule. Similarly for DST backwards jumps which repeats times, you'll get the first match by default, where "first" is defined from the point of view of searching forwards. So, when searching backwards looking for a particular hour, with no minute and second specified, you don't get a minute and second of 59:59 for the matching hour (which would be the nominal first match within a given hour, given the other rules here, when searching backwards). - /// - /// If an exact match is not possible, and requested with the `strict` option, nil is passed to the closure and the enumeration ends. (Logically, since an exact match searches indefinitely into the future, if no match is found there's no point in continuing the enumeration.) - /// - /// Result dates have an integer number of seconds (as if 0 was specified for the nanoseconds property of the `DateComponents` matching parameter), unless a value was set in the nanoseconds property, in which case the result date will have that number of nanoseconds (or as close as possible with floating point numbers). - /// - /// The enumeration is stopped by setting `stop` to `true` in the closure and returning. It is not necessary to set `stop` to `false` to keep the enumeration going. - /// - parameter start: The `Date` at which to start the search. - /// - parameter components: The `DateComponents` to use as input to the search algorithm. - /// - parameter matchingPolicy: Determines the behavior of the search algorithm when the input produces an ambiguous result. - /// - parameter repeatedTimePolicy: Determines the behavior of the search algorithm when the input produces a time that occurs twice on a particular day. - /// - parameter direction: Which direction in time to search. The default value is `.forward`, which means later in time. - /// - parameter block: A closure that is called with search results. - @available(iOS 8.0, *) - public func enumerateDates(startingAfter start: Date, matching components: DateComponents, matchingPolicy: MatchingPolicy, repeatedTimePolicy: RepeatedTimePolicy = .first, direction: SearchDirection = .forward, using block: (_ result: Date?, _ exactMatch: Bool, _ stop: inout Bool) -> Void) { - _handle.map { - $0.enumerateDates(startingAfter: start, matching: components, options: Calendar._toCalendarOptions(matchingPolicy: matchingPolicy, repeatedTimePolicy: repeatedTimePolicy, direction: direction)) { (result, exactMatch, stop) in - var stopv = false - block(result, exactMatch, &stopv) - if stopv { - stop.pointee = true - } - } - } - } - - - @available(*, unavailable, message: "use nextDate(after:matching:matchingPolicy:repeatedTimePolicy:direction:) instead") - public func nextDate(after date: Date, matching comps: DateComponents, options: NSCalendar.Options = []) -> Date? { fatalError() } - - /// Computes the next date which matches (or most closely matches) a given set of components. - /// - /// The general semantics follow those of the `enumerateDates` function. - /// To compute a sequence of results, use the `enumerateDates` function, rather than looping and calling this method with the previous loop iteration's result. - /// - parameter date: The starting date. - /// - parameter components: The components to search for. - /// - parameter matchingPolicy: Specifies the technique the search algorithm uses to find results. Default value is `.nextTime`. - /// - parameter repeatedTimePolicy: Specifies the behavior when multiple matches are found. Default value is `.first`. - /// - parameter direction: Specifies the direction in time to search. Default is `.forward`. - /// - returns: A `Date` representing the result of the search, or `nil` if a result could not be found. - @available(iOS 8.0, *) - public func nextDate(after date: Date, matching components: DateComponents, matchingPolicy: MatchingPolicy, repeatedTimePolicy: RepeatedTimePolicy = .first, direction: SearchDirection = .forward) -> Date? { - return _handle.map { $0.nextDate(after: date, matching: components, options: Calendar._toCalendarOptions(matchingPolicy: matchingPolicy, repeatedTimePolicy: repeatedTimePolicy, direction: direction)) } - } - - @available(*, unavailable, message: "use nextDate(after:matching:matchingPolicy:repeatedTimePolicy:direction:) instead") - public func nextDate(after date: Date, matchingHour hourValue: Int, minute minuteValue: Int, second secondValue: Int, options: NSCalendar.Options = []) -> Date? { fatalError() } - - // MARK: - - // - - @available(*, unavailable, renamed: "date(bySetting:value:of:)") - public func date(bySettingUnit unit: NSCalendar.Unit, value v: Int, of date: Date, options opts: NSCalendar.Options = []) -> Date? { fatalError() } - - /// Returns a new `Date` representing the date calculated by setting a specific component to a given time, and trying to keep lower components the same. If the component already has that value, this may result in a date which is the same as the given date. - /// - /// Changing a component's value often will require higher or coupled components to change as well. For example, setting the Weekday to Thursday usually will require the Day component to change its value, and possibly the Month and Year as well. - /// If no such time exists, the next available time is returned (which could, for example, be in a different day, week, month, ... than the nominal target date). Setting a component to something which would be inconsistent forces other components to change; for example, setting the Weekday to Thursday probably shifts the Day and possibly Month and Year. - /// The exact behavior of this method is implementation-defined. For example, if changing the weekday to Thursday, does that move forward to the next, backward to the previous, or to the nearest Thursday? The algorithm will try to produce a result which is in the next-larger component to the one given (there's a table of this mapping at the top of this document). So for the "set to Thursday" example, find the Thursday in the Week in which the given date resides (which could be a forwards or backwards move, and not necessarily the nearest Thursday). For more control over the exact behavior, use `nextDate(after:matching:matchingPolicy:behavior:direction:)`. - @available(iOS 8.0, *) - public func date(bySetting component: Component, value: Int, of date: Date) -> Date? { - return _handle.map { $0.date(bySettingUnit: Calendar._toCalendarUnit([component]), value: value, of: date, options: []) } - } - - - @available(*, unavailable, message: "use date(bySettingHour:minute:second:of:matchingPolicy:repeatedTimePolicy:direction:) instead") - public func date(bySettingHour h: Int, minute m: Int, second s: Int, of date: Date, options opts: NSCalendar.Options = []) -> Date? { fatalError() } - - /// Returns a new `Date` representing the date calculated by setting hour, minute, and second to a given time on a specified `Date`. - /// - /// If no such time exists, the next available time is returned (which could, for example, be in a different day than the nominal target date). - /// The intent is to return a date on the same day as the original date argument. This may result in a date which is backward than the given date, of course. - /// - parameter hour: A specified hour. - /// - parameter minute: A specified minute. - /// - parameter second: A specified second. - /// - parameter date: The date to start calculation with. - /// - parameter matchingPolicy: Specifies the technique the search algorithm uses to find results. Default value is `.nextTime`. - /// - parameter repeatedTimePolicy: Specifies the behavior when multiple matches are found. Default value is `.first`. - /// - parameter direction: Specifies the direction in time to search. Default is `.forward`. - /// - returns: A `Date` representing the result of the search, or `nil` if a result could not be found. - @available(iOS 8.0, *) - public func date(bySettingHour hour: Int, minute: Int, second: Int, of date: Date, matchingPolicy: MatchingPolicy = .nextTime, repeatedTimePolicy: RepeatedTimePolicy = .first, direction: SearchDirection = .forward) -> Date? { - return _handle.map { $0.date(bySettingHour: hour, minute: minute, second: second, of: date, options: Calendar._toCalendarOptions(matchingPolicy: matchingPolicy, repeatedTimePolicy: repeatedTimePolicy, direction: direction)) } - } - - /// Determine if the `Date` has all of the specified `DateComponents`. - /// - /// It may be useful to test the return value of `nextDate(after:matching:matchingPolicy:behavior:direction:)` to find out if the components were obeyed or if the method had to fudge the result value due to missing time (for example, a daylight saving time transition). - /// - /// - returns: `true` if the date matches all of the components, otherwise `false`. - @available(iOS 8.0, *) - public func date(_ date: Date, matchesComponents components: DateComponents) -> Bool { - return _handle.map { $0.date(date, matchesComponents: components) } - } - - // MARK: - - - public func hash(into hasher: inout Hasher) { - // We need to make sure autoupdating calendars have the same hash - if _autoupdating { - hasher.combine(false) - } else { - hasher.combine(true) - hasher.combine(_handle.map { $0 }) - } - } - - // MARK: - - // MARK: Conversion Functions - - /// Turn our more-specific options into the big bucket option set of NSCalendar - private static func _toCalendarOptions(matchingPolicy: MatchingPolicy, repeatedTimePolicy: RepeatedTimePolicy, direction: SearchDirection) -> NSCalendar.Options { - var result : NSCalendar.Options = [] - - switch matchingPolicy { - case .nextTime: - result.insert(.matchNextTime) - case .nextTimePreservingSmallerComponents: - result.insert(.matchNextTimePreservingSmallerUnits) - case .previousTimePreservingSmallerComponents: - result.insert(.matchPreviousTimePreservingSmallerUnits) - case .strict: - result.insert(.matchStrictly) - } - - switch repeatedTimePolicy { - case .first: - result.insert(.matchFirst) - case .last: - result.insert(.matchLast) - } - - switch direction { - case .backward: - result.insert(.searchBackwards) - case .forward: - break - } - - return result - } - - internal static func _toCalendarUnit(_ units : Set) -> NSCalendar.Unit { - let unitMap : [Component : NSCalendar.Unit] = - [.era : .era, - .year : .year, - .month : .month, - .day : .day, - .hour : .hour, - .minute : .minute, - .second : .second, - .weekday : .weekday, - .weekdayOrdinal : .weekdayOrdinal, - .quarter : .quarter, - .weekOfMonth : .weekOfMonth, - .weekOfYear : .weekOfYear, - .yearForWeekOfYear : .yearForWeekOfYear, - .nanosecond : .nanosecond, - .calendar : .calendar, - .timeZone : .timeZone] - - var result = NSCalendar.Unit() - for u in units { - result.insert(unitMap[u]!) - } - return result - } - - internal static func _toNSCalendarIdentifier(_ identifier : Identifier) -> NSCalendar.Identifier { - if #available(macOS 10.10, iOS 8.0, *) { - let identifierMap : [Identifier : NSCalendar.Identifier] = - [.gregorian : .gregorian, - .buddhist : .buddhist, - .chinese : .chinese, - .coptic : .coptic, - .ethiopicAmeteMihret : .ethiopicAmeteMihret, - .ethiopicAmeteAlem : .ethiopicAmeteAlem, - .hebrew : .hebrew, - .iso8601 : .ISO8601, - .indian : .indian, - .islamic : .islamic, - .islamicCivil : .islamicCivil, - .japanese : .japanese, - .persian : .persian, - .republicOfChina : .republicOfChina, - .islamicTabular : .islamicTabular, - .islamicUmmAlQura : .islamicUmmAlQura] - return identifierMap[identifier]! - } else { - let identifierMap : [Identifier : NSCalendar.Identifier] = - [.gregorian : .gregorian, - .buddhist : .buddhist, - .chinese : .chinese, - .coptic : .coptic, - .ethiopicAmeteMihret : .ethiopicAmeteMihret, - .ethiopicAmeteAlem : .ethiopicAmeteAlem, - .hebrew : .hebrew, - .iso8601 : .ISO8601, - .indian : .indian, - .islamic : .islamic, - .islamicCivil : .islamicCivil, - .japanese : .japanese, - .persian : .persian, - .republicOfChina : .republicOfChina] - return identifierMap[identifier]! - } - } - - internal static func _fromNSCalendarIdentifier(_ identifier : NSCalendar.Identifier) -> Identifier { - if #available(macOS 10.10, iOS 8.0, *) { - let identifierMap : [NSCalendar.Identifier : Identifier] = - [.gregorian : .gregorian, - .buddhist : .buddhist, - .chinese : .chinese, - .coptic : .coptic, - .ethiopicAmeteMihret : .ethiopicAmeteMihret, - .ethiopicAmeteAlem : .ethiopicAmeteAlem, - .hebrew : .hebrew, - .ISO8601 : .iso8601, - .indian : .indian, - .islamic : .islamic, - .islamicCivil : .islamicCivil, - .japanese : .japanese, - .persian : .persian, - .republicOfChina : .republicOfChina, - .islamicTabular : .islamicTabular, - .islamicUmmAlQura : .islamicUmmAlQura] - return identifierMap[identifier]! - } else { - let identifierMap : [NSCalendar.Identifier : Identifier] = - [.gregorian : .gregorian, - .buddhist : .buddhist, - .chinese : .chinese, - .coptic : .coptic, - .ethiopicAmeteMihret : .ethiopicAmeteMihret, - .ethiopicAmeteAlem : .ethiopicAmeteAlem, - .hebrew : .hebrew, - .ISO8601 : .iso8601, - .indian : .indian, - .islamic : .islamic, - .islamicCivil : .islamicCivil, - .japanese : .japanese, - .persian : .persian, - .republicOfChina : .republicOfChina] - return identifierMap[identifier]! - } - } - - public static func ==(lhs: Calendar, rhs: Calendar) -> Bool { - if lhs._autoupdating || rhs._autoupdating { - return lhs._autoupdating == rhs._autoupdating - } else { - // NSCalendar's isEqual is broken (27019864) so we must implement this ourselves - return lhs.identifier == rhs.identifier && - lhs.locale == rhs.locale && - lhs.timeZone == rhs.timeZone && - lhs.firstWeekday == rhs.firstWeekday && - lhs.minimumDaysInFirstWeek == rhs.minimumDaysInFirstWeek - } - } - -} - -extension Calendar : CustomDebugStringConvertible, CustomStringConvertible, CustomReflectable { - private var _kindDescription : String { - if self == Calendar.autoupdatingCurrent { - return "autoupdatingCurrent" - } else if self == Calendar.current { - return "current" - } else { - return "fixed" - } - } - - public var description: String { - return "\(identifier) (\(_kindDescription))" - } - - public var debugDescription : String { - return "\(identifier) (\(_kindDescription))" - } - - public var customMirror : Mirror { - let c: [(label: String?, value: Any)] = [ - ("identifier", identifier), - ("kind", _kindDescription), - ("locale", locale as Any), - ("timeZone", timeZone), - ("firstWeekday", firstWeekday), - ("minimumDaysInFirstWeek", minimumDaysInFirstWeek), - ] - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - -extension Calendar : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSCalendar { - return _handle._copiedReference() - } - - public static func _forceBridgeFromObjectiveC(_ input: NSCalendar, result: inout Calendar?) { - if !_conditionallyBridgeFromObjectiveC(input, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: NSCalendar, result: inout Calendar?) -> Bool { - result = Calendar(reference: input) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSCalendar?) -> Calendar { - var result: Calendar? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -extension NSCalendar : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as Calendar) - } -} - -extension Calendar : Codable { - private enum CodingKeys : Int, CodingKey { - case identifier - case locale - case timeZone - case firstWeekday - case minimumDaysInFirstWeek - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let identifierString = try container.decode(String.self, forKey: .identifier) - let identifier = Calendar._fromNSCalendarIdentifier(NSCalendar.Identifier(rawValue: identifierString)) - self.init(identifier: identifier) - - self.locale = try container.decodeIfPresent(Locale.self, forKey: .locale) - self.timeZone = try container.decode(TimeZone.self, forKey: .timeZone) - self.firstWeekday = try container.decode(Int.self, forKey: .firstWeekday) - self.minimumDaysInFirstWeek = try container.decode(Int.self, forKey: .minimumDaysInFirstWeek) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - let identifier = Calendar._toNSCalendarIdentifier(self.identifier).rawValue - try container.encode(identifier, forKey: .identifier) - try container.encode(self.locale, forKey: .locale) - try container.encode(self.timeZone, forKey: .timeZone) - try container.encode(self.firstWeekday, forKey: .firstWeekday) - try container.encode(self.minimumDaysInFirstWeek, forKey: .minimumDaysInFirstWeek) - } -} diff --git a/stdlib/public/Darwin/Foundation/CharacterSet.swift b/stdlib/public/Darwin/Foundation/CharacterSet.swift deleted file mode 100644 index afaa6da514811..0000000000000 --- a/stdlib/public/Darwin/Foundation/CharacterSet.swift +++ /dev/null @@ -1,829 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import CoreFoundation -@_implementationOnly import _SwiftCoreFoundationOverlayShims -@_implementationOnly import _SwiftFoundationOverlayShims - -private func _utfRangeToCFRange(_ inRange : Range) -> CFRange { - return CFRange( - location: Int(inRange.lowerBound.value), - length: Int(inRange.upperBound.value - inRange.lowerBound.value)) -} - -private func _utfRangeToCFRange(_ inRange : ClosedRange) -> CFRange { - return CFRange( - location: Int(inRange.lowerBound.value), - length: Int(inRange.upperBound.value - inRange.lowerBound.value + 1)) -} - -// MARK: - - -// NOTE: older overlays called this class _CharacterSetStorage. -// The two must coexist without a conflicting ObjC class name, so it -// was renamed. The old name must not be used in the new runtime. -private final class __CharacterSetStorage : Hashable { - enum Backing { - case immutable(CFCharacterSet) - case mutable(CFMutableCharacterSet) - } - - var _backing: Backing - - @nonobjc - init(immutableReference r: CFCharacterSet) { - _backing = .immutable(r) - } - - @nonobjc - init(mutableReference r: CFMutableCharacterSet) { - _backing = .mutable(r) - } - - // MARK: - - - func hash(into hasher: inout Hasher) { - switch _backing { - case .immutable(let cs): - hasher.combine(CFHash(cs)) - case .mutable(let cs): - hasher.combine(CFHash(cs)) - } - } - - static func ==(lhs: __CharacterSetStorage, rhs: __CharacterSetStorage) -> Bool { - switch (lhs._backing, rhs._backing) { - case (.immutable(let cs1), .immutable(let cs2)): - return CFEqual(cs1, cs2) - case (.immutable(let cs1), .mutable(let cs2)): - return CFEqual(cs1, cs2) - case (.mutable(let cs1), .immutable(let cs2)): - return CFEqual(cs1, cs2) - case (.mutable(let cs1), .mutable(let cs2)): - return CFEqual(cs1, cs2) - } - } - - // MARK: - - - func mutableCopy() -> __CharacterSetStorage { - switch _backing { - case .immutable(let cs): - return __CharacterSetStorage(mutableReference: CFCharacterSetCreateMutableCopy(nil, cs)) - case .mutable(let cs): - return __CharacterSetStorage(mutableReference: CFCharacterSetCreateMutableCopy(nil, cs)) - } - } - - - // MARK: Immutable Functions - - var bitmapRepresentation: Data { - switch _backing { - case .immutable(let cs): - return CFCharacterSetCreateBitmapRepresentation(nil, cs) as Data - case .mutable(let cs): - return CFCharacterSetCreateBitmapRepresentation(nil, cs) as Data - } - } - - func hasMember(inPlane plane: UInt8) -> Bool { - switch _backing { - case .immutable(let cs): - return CFCharacterSetHasMemberInPlane(cs, CFIndex(plane)) - case .mutable(let cs): - return CFCharacterSetHasMemberInPlane(cs, CFIndex(plane)) - } - } - - // MARK: Mutable functions - - func insert(charactersIn range: Range) { - switch _backing { - case .immutable(let cs): - let r = CFCharacterSetCreateMutableCopy(nil, cs)! - CFCharacterSetAddCharactersInRange(r, _utfRangeToCFRange(range)) - _backing = .mutable(r) - case .mutable(let cs): - CFCharacterSetAddCharactersInRange(cs, _utfRangeToCFRange(range)) - } - } - - func insert(charactersIn range: ClosedRange) { - switch _backing { - case .immutable(let cs): - let r = CFCharacterSetCreateMutableCopy(nil, cs)! - CFCharacterSetAddCharactersInRange(r, _utfRangeToCFRange(range)) - _backing = .mutable(r) - case .mutable(let cs): - CFCharacterSetAddCharactersInRange(cs, _utfRangeToCFRange(range)) - } - } - - func remove(charactersIn range: Range) { - switch _backing { - case .immutable(let cs): - let r = CFCharacterSetCreateMutableCopy(nil, cs)! - CFCharacterSetRemoveCharactersInRange(r, _utfRangeToCFRange(range)) - _backing = .mutable(r) - case .mutable(let cs): - CFCharacterSetRemoveCharactersInRange(cs, _utfRangeToCFRange(range)) - } - } - - func remove(charactersIn range: ClosedRange) { - switch _backing { - case .immutable(let cs): - let r = CFCharacterSetCreateMutableCopy(nil, cs)! - CFCharacterSetRemoveCharactersInRange(r, _utfRangeToCFRange(range)) - _backing = .mutable(r) - case .mutable(let cs): - CFCharacterSetRemoveCharactersInRange(cs, _utfRangeToCFRange(range)) - } - } - - func insert(charactersIn string: String) { - switch _backing { - case .immutable(let cs): - let r = CFCharacterSetCreateMutableCopy(nil, cs)! - CFCharacterSetAddCharactersInString(r, string as CFString) - _backing = .mutable(r) - case .mutable(let cs): - CFCharacterSetAddCharactersInString(cs, string as CFString) - } - } - - func remove(charactersIn string: String) { - switch _backing { - case .immutable(let cs): - let r = CFCharacterSetCreateMutableCopy(nil, cs)! - CFCharacterSetRemoveCharactersInString(r, string as CFString) - _backing = .mutable(r) - case .mutable(let cs): - CFCharacterSetRemoveCharactersInString(cs, string as CFString) - } - } - - func invert() { - switch _backing { - case .immutable(let cs): - let r = CFCharacterSetCreateMutableCopy(nil, cs)! - CFCharacterSetInvert(r) - _backing = .mutable(r) - case .mutable(let cs): - CFCharacterSetInvert(cs) - } - } - - // ----- - // MARK: - - // MARK: SetAlgebra - - @discardableResult - func insert(_ character: Unicode.Scalar) -> (inserted: Bool, memberAfterInsert: Unicode.Scalar) { - insert(charactersIn: character...character) - // TODO: This should probably return the truth, but figuring it out requires two calls into NSCharacterSet - return (true, character) - } - - @discardableResult - func update(with character: Unicode.Scalar) -> Unicode.Scalar? { - insert(character) - // TODO: This should probably return the truth, but figuring it out requires two calls into NSCharacterSet - return character - } - - @discardableResult - func remove(_ character: Unicode.Scalar) -> Unicode.Scalar? { - // TODO: Add method to CFCharacterSet to do this in one call - let result : Unicode.Scalar? = contains(character) ? character : nil - remove(charactersIn: character...character) - return result - } - - func contains(_ member: Unicode.Scalar) -> Bool { - switch _backing { - case .immutable(let cs): - return CFCharacterSetIsLongCharacterMember(cs, member.value) - case .mutable(let cs): - return CFCharacterSetIsLongCharacterMember(cs, member.value) - } - } - - // MARK: - - // Why do these return CharacterSet instead of CharacterSetStorage? - // We want to keep the knowledge of if the returned value happened to contain a mutable or immutable CFCharacterSet as close to the creation of that instance as possible - - - // When the underlying collection does not have a method to return new CharacterSets with changes applied, so we will copy and apply here - private static func _apply(_ lhs : __CharacterSetStorage, _ rhs : __CharacterSetStorage, _ f : (CFMutableCharacterSet, CFCharacterSet) -> ()) -> CharacterSet { - let copyOfMe : CFMutableCharacterSet - switch lhs._backing { - case .immutable(let cs): - copyOfMe = CFCharacterSetCreateMutableCopy(nil, cs)! - case .mutable(let cs): - copyOfMe = CFCharacterSetCreateMutableCopy(nil, cs)! - } - - switch rhs._backing { - case .immutable(let cs): - f(copyOfMe, cs) - case .mutable(let cs): - f(copyOfMe, cs) - } - - return CharacterSet(_uncopiedStorage: __CharacterSetStorage(mutableReference: copyOfMe)) - } - - private func _applyMutation(_ other : __CharacterSetStorage, _ f : (CFMutableCharacterSet, CFCharacterSet) -> ()) { - switch _backing { - case .immutable(let cs): - let r = CFCharacterSetCreateMutableCopy(nil, cs)! - switch other._backing { - case .immutable(let otherCs): - f(r, otherCs) - case .mutable(let otherCs): - f(r, otherCs) - } - _backing = .mutable(r) - case .mutable(let cs): - switch other._backing { - case .immutable(let otherCs): - f(cs, otherCs) - case .mutable(let otherCs): - f(cs, otherCs) - } - } - - } - - var inverted: CharacterSet { - switch _backing { - case .immutable(let cs): - return CharacterSet(_uncopiedStorage: __CharacterSetStorage(immutableReference: CFCharacterSetCreateInvertedSet(nil, cs))) - case .mutable(let cs): - // Even if input is mutable, the result is immutable - return CharacterSet(_uncopiedStorage: __CharacterSetStorage(immutableReference: CFCharacterSetCreateInvertedSet(nil, cs))) - } - } - - func union(_ other: __CharacterSetStorage) -> CharacterSet { - return __CharacterSetStorage._apply(self, other, CFCharacterSetUnion) - } - - func formUnion(_ other: __CharacterSetStorage) { - _applyMutation(other, CFCharacterSetUnion) - } - - func intersection(_ other: __CharacterSetStorage) -> CharacterSet { - return __CharacterSetStorage._apply(self, other, CFCharacterSetIntersect) - } - - func formIntersection(_ other: __CharacterSetStorage) { - _applyMutation(other, CFCharacterSetIntersect) - } - - func subtracting(_ other: __CharacterSetStorage) -> CharacterSet { - return intersection(other.inverted._storage) - } - - func subtract(_ other: __CharacterSetStorage) { - _applyMutation(other.inverted._storage, CFCharacterSetIntersect) - } - - func symmetricDifference(_ other: __CharacterSetStorage) -> CharacterSet { - return union(other).subtracting(intersection(other)) - } - - func formSymmetricDifference(_ other: __CharacterSetStorage) { - // This feels like cheating - _backing = symmetricDifference(other)._storage._backing - } - - func isSuperset(of other: __CharacterSetStorage) -> Bool { - switch _backing { - case .immutable(let cs): - switch other._backing { - case .immutable(let otherCs): - return CFCharacterSetIsSupersetOfSet(cs, otherCs) - case .mutable(let otherCs): - return CFCharacterSetIsSupersetOfSet(cs, otherCs) - } - case .mutable(let cs): - switch other._backing { - case .immutable(let otherCs): - return CFCharacterSetIsSupersetOfSet(cs, otherCs) - case .mutable(let otherCs): - return CFCharacterSetIsSupersetOfSet(cs, otherCs) - } - } - } - - // MARK: - - - var description: String { - switch _backing { - case .immutable(let cs): - return CFCopyDescription(cs) as String - case .mutable(let cs): - return CFCopyDescription(cs) as String - } - } - - var debugDescription: String { - return description - } - - // MARK: - - - public func bridgedReference() -> NSCharacterSet { - switch _backing { - case .immutable(let cs): - return cs as NSCharacterSet - case .mutable(let cs): - return cs as NSCharacterSet - } - } -} - -// MARK: - - -/** - A `CharacterSet` represents a set of Unicode-compliant characters. Foundation types use `CharacterSet` to group characters together for searching operations, so that they can find any of a particular set of characters during a search. - - This type provides "copy-on-write" behavior, and is also bridged to the Objective-C `NSCharacterSet` class. -*/ -public struct CharacterSet : ReferenceConvertible, Equatable, Hashable, SetAlgebra { - public typealias ReferenceType = NSCharacterSet - - fileprivate var _storage: __CharacterSetStorage - - // MARK: Init methods - - /// Initialize an empty instance. - public init() { - // It's unlikely that we are creating an empty character set with no intention to mutate it - _storage = __CharacterSetStorage(mutableReference: CFCharacterSetCreateMutable(nil)) - } - - /// Initialize with a range of integers. - /// - /// It is the caller's responsibility to ensure that the values represent valid `Unicode.Scalar` values, if that is what is desired. - public init(charactersIn range: Range) { - _storage = __CharacterSetStorage(immutableReference: CFCharacterSetCreateWithCharactersInRange(nil, _utfRangeToCFRange(range))) - } - - /// Initialize with a closed range of integers. - /// - /// It is the caller's responsibility to ensure that the values represent valid `Unicode.Scalar` values, if that is what is desired. - public init(charactersIn range: ClosedRange) { - _storage = __CharacterSetStorage(immutableReference: CFCharacterSetCreateWithCharactersInRange(nil, _utfRangeToCFRange(range))) - } - - /// Initialize with the characters in the given string. - /// - /// - parameter string: The string content to inspect for characters. - public init(charactersIn string: __shared String) { - _storage = __CharacterSetStorage(immutableReference: CFCharacterSetCreateWithCharactersInString(nil, string as CFString)) - } - - /// Initialize with a bitmap representation. - /// - /// This method is useful for creating a character set object with data from a file or other external data source. - /// - parameter data: The bitmap representation. - public init(bitmapRepresentation data: __shared Data) { - _storage = __CharacterSetStorage(immutableReference: CFCharacterSetCreateWithBitmapRepresentation(nil, data as CFData)) - } - - /// Initialize with the contents of a file. - /// - /// Returns `nil` if there was an error reading the file. - /// - parameter file: The file to read. - public init?(contentsOfFile file: __shared String) { - do { - let data = try Data(contentsOf: URL(fileURLWithPath: file), options: .mappedIfSafe) - _storage = __CharacterSetStorage(immutableReference: CFCharacterSetCreateWithBitmapRepresentation(nil, data as CFData)) - } catch { - return nil - } - } - - private init(_bridged characterSet: __shared NSCharacterSet) { - _storage = __CharacterSetStorage(immutableReference: characterSet.copy() as! CFCharacterSet) - } - - private init(_uncopiedImmutableReference characterSet: CFCharacterSet) { - _storage = __CharacterSetStorage(immutableReference: characterSet) - } - - fileprivate init(_uncopiedStorage: __CharacterSetStorage) { - _storage = _uncopiedStorage - } - - private init(_builtIn: __shared CFCharacterSetPredefinedSet) { - _storage = __CharacterSetStorage(immutableReference: CFCharacterSetGetPredefined(_builtIn)) - } - - // MARK: Static functions - - /// Returns a character set containing the characters in Unicode General Category Cc and Cf. - public static var controlCharacters : CharacterSet { - return CharacterSet(_builtIn: .control) - } - - /// Returns a character set containing the characters in Unicode General Category Zs and `CHARACTER TABULATION (U+0009)`. - public static var whitespaces : CharacterSet { - return CharacterSet(_builtIn: .whitespace) - } - - /// Returns a character set containing characters in Unicode General Category Z*, `U+000A ~ U+000D`, and `U+0085`. - public static var whitespacesAndNewlines : CharacterSet { - return CharacterSet(_builtIn: .whitespaceAndNewline) - } - - /// Returns a character set containing the characters in the category of Decimal Numbers. - public static var decimalDigits : CharacterSet { - return CharacterSet(_builtIn: .decimalDigit) - } - - /// Returns a character set containing the characters in Unicode General Category L* & M*. - public static var letters : CharacterSet { - return CharacterSet(_builtIn: .letter) - } - - /// Returns a character set containing the characters in Unicode General Category Ll. - public static var lowercaseLetters : CharacterSet { - return CharacterSet(_builtIn: .lowercaseLetter) - } - - /// Returns a character set containing the characters in Unicode General Category Lu and Lt. - public static var uppercaseLetters : CharacterSet { - return CharacterSet(_builtIn: .uppercaseLetter) - } - - /// Returns a character set containing the characters in Unicode General Category M*. - public static var nonBaseCharacters : CharacterSet { - return CharacterSet(_builtIn: .nonBase) - } - - /// Returns a character set containing the characters in Unicode General Categories L*, M*, and N*. - public static var alphanumerics : CharacterSet { - return CharacterSet(_builtIn: .alphaNumeric) - } - - /// Returns a character set containing individual Unicode characters that can also be represented as composed character sequences (such as for letters with accents), by the definition of "standard decomposition" in version 3.2 of the Unicode character encoding standard. - public static var decomposables : CharacterSet { - return CharacterSet(_builtIn: .decomposable) - } - - /// Returns a character set containing values in the category of Non-Characters or that have not yet been defined in version 3.2 of the Unicode standard. - public static var illegalCharacters : CharacterSet { - return CharacterSet(_builtIn: .illegal) - } - - @available(*, unavailable, renamed: "punctuationCharacters") - public static var punctuation : CharacterSet { - return CharacterSet(_builtIn: .punctuation) - } - - /// Returns a character set containing the characters in Unicode General Category P*. - public static var punctuationCharacters : CharacterSet { - return CharacterSet(_builtIn: .punctuation) - } - - /// Returns a character set containing the characters in Unicode General Category Lt. - public static var capitalizedLetters : CharacterSet { - return CharacterSet(_builtIn: .capitalizedLetter) - } - - /// Returns a character set containing the characters in Unicode General Category S*. - public static var symbols : CharacterSet { - return CharacterSet(_builtIn: .symbol) - } - - /// Returns a character set containing the newline characters (`U+000A ~ U+000D`, `U+0085`, `U+2028`, and `U+2029`). - public static var newlines : CharacterSet { - return CharacterSet(_builtIn: .newline) - } - - // MARK: Static functions, from NSURL - - /// Returns the character set for characters allowed in a user URL subcomponent. - public static var urlUserAllowed : CharacterSet { - if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - return CharacterSet(_uncopiedImmutableReference: _CFURLComponentsGetURLUserAllowedCharacterSet() as NSCharacterSet) - } else { - return CharacterSet(_uncopiedImmutableReference: _NSURLComponentsGetURLUserAllowedCharacterSet() as! NSCharacterSet) - } - } - - /// Returns the character set for characters allowed in a password URL subcomponent. - public static var urlPasswordAllowed : CharacterSet { - if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - return CharacterSet(_uncopiedImmutableReference: _CFURLComponentsGetURLPasswordAllowedCharacterSet() as NSCharacterSet) - } else { - return CharacterSet(_uncopiedImmutableReference: _NSURLComponentsGetURLPasswordAllowedCharacterSet() as! NSCharacterSet) - } - } - - /// Returns the character set for characters allowed in a host URL subcomponent. - public static var urlHostAllowed : CharacterSet { - if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - return CharacterSet(_uncopiedImmutableReference: _CFURLComponentsGetURLHostAllowedCharacterSet() as NSCharacterSet) - } else { - return CharacterSet(_uncopiedImmutableReference: _NSURLComponentsGetURLHostAllowedCharacterSet() as! NSCharacterSet) - } - } - - /// Returns the character set for characters allowed in a path URL component. - public static var urlPathAllowed : CharacterSet { - if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - return CharacterSet(_uncopiedImmutableReference: _CFURLComponentsGetURLPathAllowedCharacterSet() as NSCharacterSet) - } else { - return CharacterSet(_uncopiedImmutableReference: _NSURLComponentsGetURLPathAllowedCharacterSet() as! NSCharacterSet) - } - } - - /// Returns the character set for characters allowed in a query URL component. - public static var urlQueryAllowed : CharacterSet { - if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - return CharacterSet(_uncopiedImmutableReference: _CFURLComponentsGetURLQueryAllowedCharacterSet() as NSCharacterSet) - } else { - return CharacterSet(_uncopiedImmutableReference: _NSURLComponentsGetURLQueryAllowedCharacterSet() as! NSCharacterSet) - } - } - - /// Returns the character set for characters allowed in a fragment URL component. - public static var urlFragmentAllowed : CharacterSet { - if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - return CharacterSet(_uncopiedImmutableReference: _CFURLComponentsGetURLFragmentAllowedCharacterSet() as NSCharacterSet) - } else { - return CharacterSet(_uncopiedImmutableReference: _NSURLComponentsGetURLFragmentAllowedCharacterSet() as! NSCharacterSet) - } - } - - // MARK: Immutable functions - - /// Returns a representation of the `CharacterSet` in binary format. - @nonobjc - public var bitmapRepresentation: Data { - return _storage.bitmapRepresentation - } - - /// Returns an inverted copy of the receiver. - @nonobjc - public var inverted : CharacterSet { - return _storage.inverted - } - - /// Returns true if the `CharacterSet` has a member in the specified plane. - /// - /// This method makes it easier to find the plane containing the members of the current character set. The Basic Multilingual Plane (BMP) is plane 0. - public func hasMember(inPlane plane: UInt8) -> Bool { - return _storage.hasMember(inPlane: plane) - } - - // MARK: Mutable functions - - /// Insert a range of integer values in the `CharacterSet`. - /// - /// It is the caller's responsibility to ensure that the values represent valid `Unicode.Scalar` values, if that is what is desired. - public mutating func insert(charactersIn range: Range) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.insert(charactersIn: range) - } - - /// Insert a closed range of integer values in the `CharacterSet`. - /// - /// It is the caller's responsibility to ensure that the values represent valid `Unicode.Scalar` values, if that is what is desired. - public mutating func insert(charactersIn range: ClosedRange) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.insert(charactersIn: range) - } - - /// Remove a range of integer values from the `CharacterSet`. - public mutating func remove(charactersIn range: Range) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.remove(charactersIn: range) - } - - /// Remove a closed range of integer values from the `CharacterSet`. - public mutating func remove(charactersIn range: ClosedRange) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.remove(charactersIn: range) - } - - /// Insert the values from the specified string into the `CharacterSet`. - public mutating func insert(charactersIn string: String) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.insert(charactersIn: string) - } - - /// Remove the values from the specified string from the `CharacterSet`. - public mutating func remove(charactersIn string: String) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.remove(charactersIn: string) - } - - /// Invert the contents of the `CharacterSet`. - public mutating func invert() { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.invert() - } - - // ----- - // MARK: - - // MARK: SetAlgebra - - /// Insert a `Unicode.Scalar` representation of a character into the `CharacterSet`. - /// - /// `Unicode.Scalar` values are available on `Swift.String.UnicodeScalarView`. - @discardableResult - public mutating func insert(_ character: Unicode.Scalar) -> (inserted: Bool, memberAfterInsert: Unicode.Scalar) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - return _storage.insert(character) - } - - /// Insert a `Unicode.Scalar` representation of a character into the `CharacterSet`. - /// - /// `Unicode.Scalar` values are available on `Swift.String.UnicodeScalarView`. - @discardableResult - public mutating func update(with character: Unicode.Scalar) -> Unicode.Scalar? { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - return _storage.update(with: character) - } - - - /// Remove a `Unicode.Scalar` representation of a character from the `CharacterSet`. - /// - /// `Unicode.Scalar` values are available on `Swift.String.UnicodeScalarView`. - @discardableResult - public mutating func remove(_ character: Unicode.Scalar) -> Unicode.Scalar? { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - return _storage.remove(character) - } - - /// Test for membership of a particular `Unicode.Scalar` in the `CharacterSet`. - public func contains(_ member: Unicode.Scalar) -> Bool { - return _storage.contains(member) - } - - /// Returns a union of the `CharacterSet` with another `CharacterSet`. - public func union(_ other: CharacterSet) -> CharacterSet { - return _storage.union(other._storage) - } - - /// Sets the value to a union of the `CharacterSet` with another `CharacterSet`. - public mutating func formUnion(_ other: CharacterSet) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.formUnion(other._storage) - } - - /// Returns an intersection of the `CharacterSet` with another `CharacterSet`. - public func intersection(_ other: CharacterSet) -> CharacterSet { - return _storage.intersection(other._storage) - } - - /// Sets the value to an intersection of the `CharacterSet` with another `CharacterSet`. - public mutating func formIntersection(_ other: CharacterSet) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.formIntersection(other._storage) - } - - /// Returns a `CharacterSet` created by removing elements in `other` from `self`. - public func subtracting(_ other: CharacterSet) -> CharacterSet { - return _storage.subtracting(other._storage) - } - - /// Sets the value to a `CharacterSet` created by removing elements in `other` from `self`. - public mutating func subtract(_ other: CharacterSet) { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _storage.mutableCopy() - } - _storage.subtract(other._storage) - } - - /// Returns an exclusive or of the `CharacterSet` with another `CharacterSet`. - public func symmetricDifference(_ other: CharacterSet) -> CharacterSet { - return _storage.symmetricDifference(other._storage) - } - - /// Sets the value to an exclusive or of the `CharacterSet` with another `CharacterSet`. - public mutating func formSymmetricDifference(_ other: CharacterSet) { - self = symmetricDifference(other) - } - - /// Returns true if `self` is a superset of `other`. - public func isSuperset(of other: CharacterSet) -> Bool { - return _storage.isSuperset(of: other._storage) - } - - // MARK: - - - public func hash(into hasher: inout Hasher) { - hasher.combine(_storage) - } - - /// Returns true if the two `CharacterSet`s are equal. - public static func ==(lhs : CharacterSet, rhs: CharacterSet) -> Bool { - return lhs._storage == rhs._storage - } -} - - -// MARK: Objective-C Bridging -extension CharacterSet : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSCharacterSet.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSCharacterSet { - return _storage.bridgedReference() - } - - public static func _forceBridgeFromObjectiveC(_ input: NSCharacterSet, result: inout CharacterSet?) { - result = CharacterSet(_bridged: input) - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: NSCharacterSet, result: inout CharacterSet?) -> Bool { - result = CharacterSet(_bridged: input) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSCharacterSet?) -> CharacterSet { - guard let src = source else { return CharacterSet() } - return CharacterSet(_bridged: src) - } - -} - -extension CharacterSet : CustomStringConvertible, CustomDebugStringConvertible { - public var description: String { - return _storage.description - } - - public var debugDescription: String { - return _storage.debugDescription - } -} - -extension NSCharacterSet : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as CharacterSet) - } -} - -extension CharacterSet : Codable { - private enum CodingKeys : Int, CodingKey { - case bitmap - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let bitmap = try container.decode(Data.self, forKey: .bitmap) - self.init(bitmapRepresentation: bitmap) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.bitmapRepresentation, forKey: .bitmap) - } -} diff --git a/stdlib/public/Darwin/Foundation/CheckClass.swift b/stdlib/public/Darwin/Foundation/CheckClass.swift deleted file mode 100644 index 3889504eb6bf7..0000000000000 --- a/stdlib/public/Darwin/Foundation/CheckClass.swift +++ /dev/null @@ -1,67 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2020 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims -import Dispatch - -private let _queue = DispatchQueue(label: "com.apple.SwiftFoundation._checkClassAndWarnForKeyedArchivingQueue") -private var _seenClasses: Set = [] -private func _isClassFirstSeen(_ theClass: AnyClass) -> Bool { - _queue.sync { - let id = ObjectIdentifier(theClass) - return _seenClasses.insert(id).inserted - } -} - -internal func _logRuntimeIssue(_ message: String) { - NSLog("%@", message) - _swift_reportToDebugger(0, message, nil) -} - -extension NSKeyedUnarchiver { - /// Checks if class `theClass` is good for archiving. - /// - /// If not, a runtime warning is printed. - /// - /// - Parameter operation: Specifies the archiving operation. Supported values - /// are 0 for archiving, and 1 for unarchiving. - /// - Returns: 0 if the given class is safe to archive, and non-zero if it - /// isn't. - @objc(_swift_checkClassAndWarnForKeyedArchiving:operation:) - internal class func __swift_checkClassAndWarnForKeyedArchiving( - _ theClass: AnyClass, - operation: CInt - ) -> CInt { - if _swift_isObjCTypeNameSerializable(theClass) { return 0 } - - if _isClassFirstSeen(theClass) { - let demangledName = String(reflecting: theClass) - let mangledName = NSStringFromClass(theClass) - - let op = (operation == 1 ? "unarchive" : "archive") - - let message = """ - Attempting to \(op) Swift class '\(demangledName)' with unstable runtime name '\(mangledName)'. - The runtime name for this class may change in the future, leading to non-decodable data. - - You can use the 'objc' attribute to ensure that the name will not change: - "@objc(\(mangledName))" - - If there are no existing archives containing this class, you should choose a unique, prefixed name instead: - "@objc(ABCMyModel)" - """ - _logRuntimeIssue(message) - } - return 1 - } -} diff --git a/stdlib/public/Darwin/Foundation/Codable.swift b/stdlib/public/Darwin/Foundation/Codable.swift deleted file mode 100644 index 59f47fab2688d..0000000000000 --- a/stdlib/public/Darwin/Foundation/Codable.swift +++ /dev/null @@ -1,80 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// Errors -//===----------------------------------------------------------------------===// - -// Both of these error types bridge to NSError, and through the entry points they use, no further work is needed to make them localized. -extension EncodingError : LocalizedError {} -extension DecodingError : LocalizedError {} - -//===----------------------------------------------------------------------===// -// Error Utilities -//===----------------------------------------------------------------------===// - -extension DecodingError { - /// Returns a `.typeMismatch` error describing the expected type. - /// - /// - parameter path: The path of `CodingKey`s taken to decode a value of this type. - /// - parameter expectation: The type expected to be encountered. - /// - parameter reality: The value that was encountered instead of the expected type. - /// - returns: A `DecodingError` with the appropriate path and debug description. - internal static func _typeMismatch(at path: [CodingKey], expectation: Any.Type, reality: Any) -> DecodingError { - let description = "Expected to decode \(expectation) but found \(_typeDescription(of: reality)) instead." - return .typeMismatch(expectation, Context(codingPath: path, debugDescription: description)) - } - - /// Returns a description of the type of `value` appropriate for an error message. - /// - /// - parameter value: The value whose type to describe. - /// - returns: A string describing `value`. - /// - precondition: `value` is one of the types below. - private static func _typeDescription(of value: Any) -> String { - if value is NSNull { - return "a null value" - } else if value is NSNumber /* FIXME: If swift-corelibs-foundation isn't updated to use NSNumber, this check will be necessary: || value is Int || value is Double */ { - return "a number" - } else if value is String { - return "a string/data" - } else if value is [Any] { - return "an array" - } else if value is [String : Any] { - return "a dictionary" - } else { - return "\(type(of: value))" - } - } -} - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -import Combine - -//===----------------------------------------------------------------------===// -// Generic Decoding -//===----------------------------------------------------------------------===// - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension JSONEncoder: TopLevelEncoder { } - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension PropertyListEncoder: TopLevelEncoder { } - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension JSONDecoder: TopLevelDecoder { } - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension PropertyListDecoder: TopLevelDecoder { } - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/Collections+DataProtocol.swift b/stdlib/public/Darwin/Foundation/Collections+DataProtocol.swift deleted file mode 100644 index 7e747bf4387f6..0000000000000 --- a/stdlib/public/Darwin/Foundation/Collections+DataProtocol.swift +++ /dev/null @@ -1,61 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2018 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 -// -//===----------------------------------------------------------------------===// - -//===--- DataProtocol -----------------------------------------------------===// - -extension Array: DataProtocol where Element == UInt8 { - public var regions: CollectionOfOne> { - return CollectionOfOne(self) - } -} - -extension ArraySlice: DataProtocol where Element == UInt8 { - public var regions: CollectionOfOne> { - return CollectionOfOne(self) - } -} - -extension ContiguousArray: DataProtocol where Element == UInt8 { - public var regions: CollectionOfOne> { - return CollectionOfOne(self) - } -} - -// FIXME: This currently crashes compilation in the Late Inliner. -// extension CollectionOfOne : DataProtocol where Element == UInt8 { -// public typealias Regions = CollectionOfOne -// -// public var regions: CollectionOfOne { -// return CollectionOfOne(Data(self)) -// } -// } - -extension EmptyCollection : DataProtocol where Element == UInt8 { - public var regions: EmptyCollection { - return EmptyCollection() - } -} - -extension Repeated: DataProtocol where Element == UInt8 { - public typealias Regions = Repeated - - public var regions: Repeated { - guard !self.isEmpty else { return repeatElement(Data(), count: 0) } - return repeatElement(Data(CollectionOfOne(self.first!)), count: self.count) - } -} - -//===--- MutableDataProtocol ----------------------------------------------===// - -extension Array: MutableDataProtocol where Element == UInt8 { } - -extension ContiguousArray: MutableDataProtocol where Element == UInt8 { } diff --git a/stdlib/public/Darwin/Foundation/CombineTypealiases.swift b/stdlib/public/Darwin/Foundation/CombineTypealiases.swift deleted file mode 100644 index cb3cb62c9057e..0000000000000 --- a/stdlib/public/Darwin/Foundation/CombineTypealiases.swift +++ /dev/null @@ -1,23 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2018 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 -// -//===----------------------------------------------------------------------===// - -#if !(os(iOS) && (arch(i386) || arch(arm))) // Combine isn't on 32-bit iOS - -import Combine - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -public typealias Published = Combine.Published - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -public typealias ObservableObject = Combine.ObservableObject - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/ContiguousBytes.swift b/stdlib/public/Darwin/Foundation/ContiguousBytes.swift deleted file mode 100644 index 0d1f69b00d5f2..0000000000000 --- a/stdlib/public/Darwin/Foundation/ContiguousBytes.swift +++ /dev/null @@ -1,100 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2018 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 -// -//===----------------------------------------------------------------------===// - -//===--- ContiguousBytes --------------------------------------------------===// - -/// Indicates that the conforming type is a contiguous collection of raw bytes -/// whose underlying storage is directly accessible by withUnsafeBytes. -public protocol ContiguousBytes { - /// Calls the given closure with the contents of underlying storage. - /// - /// - note: Calling `withUnsafeBytes` multiple times does not guarantee that - /// the same buffer pointer will be passed in every time. - /// - warning: The buffer argument to the body should not be stored or used - /// outside of the lifetime of the call to the closure. - func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R -} - -//===--- Collection Conformances ------------------------------------------===// - -// FIXME: When possible, expand conformance to `where Element : Trivial`. -extension Array : ContiguousBytes where Element == UInt8 { } - -// FIXME: When possible, expand conformance to `where Element : Trivial`. -extension ArraySlice : ContiguousBytes where Element == UInt8 { } - -// FIXME: When possible, expand conformance to `where Element : Trivial`. -extension ContiguousArray : ContiguousBytes where Element == UInt8 { } - -//===--- Pointer Conformances ---------------------------------------------===// - -extension UnsafeRawBufferPointer : ContiguousBytes { - @inlinable - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try body(self) - } -} - -extension UnsafeMutableRawBufferPointer : ContiguousBytes { - @inlinable - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try body(UnsafeRawBufferPointer(self)) - } -} - -// FIXME: When possible, expand conformance to `where Element : Trivial`. -extension UnsafeBufferPointer : ContiguousBytes where Element == UInt8 { - @inlinable - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try body(UnsafeRawBufferPointer(self)) - } -} - -// FIXME: When possible, expand conformance to `where Element : Trivial`. -extension UnsafeMutableBufferPointer : ContiguousBytes where Element == UInt8 { - @inlinable - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try body(UnsafeRawBufferPointer(self)) - } -} - -// FIXME: When possible, expand conformance to `where Element : Trivial`. -extension EmptyCollection : ContiguousBytes where Element == UInt8 { - @inlinable - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try body(UnsafeRawBufferPointer(start: nil, count: 0)) - } -} - -// FIXME: When possible, expand conformance to `where Element : Trivial`. -extension CollectionOfOne : ContiguousBytes where Element == UInt8 { - @inlinable - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - let element = self.first! - return try Swift.withUnsafeBytes(of: element) { - return try body($0) - } - } -} - -//===--- Conditional Conformances -----------------------------------------===// - -extension Slice : ContiguousBytes where Base : ContiguousBytes { - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { - let offset = base.distance(from: base.startIndex, to: self.startIndex) - return try base.withUnsafeBytes { ptr in - let slicePtr = ptr.baseAddress?.advanced(by: offset) - let sliceBuffer = UnsafeRawBufferPointer(start: slicePtr, count: self.count) - return try body(sliceBuffer) - } - } -} diff --git a/stdlib/public/Darwin/Foundation/Data.swift b/stdlib/public/Darwin/Foundation/Data.swift deleted file mode 100644 index 5bc9ef65e80f3..0000000000000 --- a/stdlib/public/Darwin/Foundation/Data.swift +++ /dev/null @@ -1,2859 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#if DEPLOYMENT_RUNTIME_SWIFT - -#if os(macOS) || os(iOS) -import Darwin -#elseif os(Linux) -import Glibc - -@inlinable // This is @inlinable as trivially computable. -private func malloc_good_size(_ size: Int) -> Int { - return size -} - -#endif - -import CoreFoundation - -internal func __NSDataInvokeDeallocatorUnmap(_ mem: UnsafeMutableRawPointer, _ length: Int) { - munmap(mem, length) -} - -internal func __NSDataInvokeDeallocatorFree(_ mem: UnsafeMutableRawPointer, _ length: Int) { - free(mem) -} - -internal func __NSDataIsCompact(_ data: NSData) -> Bool { - return data._isCompact() -} - -#else - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims -@_implementationOnly import _SwiftCoreFoundationOverlayShims - -internal func __NSDataIsCompact(_ data: NSData) -> Bool { - if #available(OSX 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) { - return data._isCompact() - } else { - var compact = true - let len = data.length - data.enumerateBytes { (_, byteRange, stop) in - if byteRange.length != len { - compact = false - } - stop.pointee = true - } - return compact - } -} - -#endif - -@_alwaysEmitIntoClient -internal func _withStackOrHeapBuffer(capacity: Int, _ body: (UnsafeMutableBufferPointer) -> Void) { - guard capacity > 0 else { - body(UnsafeMutableBufferPointer(start: nil, count: 0)) - return - } - typealias InlineBuffer = ( // 32 bytes - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 - ) - let inlineCount = MemoryLayout.size - if capacity <= inlineCount { - var buffer: InlineBuffer = ( - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ) - withUnsafeMutableBytes(of: &buffer) { buffer in - assert(buffer.count == inlineCount) - let start = buffer.baseAddress!.assumingMemoryBound(to: UInt8.self) - body(UnsafeMutableBufferPointer(start: start, count: capacity)) - } - return - } - - let buffer = UnsafeMutableBufferPointer.allocate(capacity: capacity) - defer { buffer.deallocate() } - body(buffer) -} - -// Underlying storage representation for medium and large data. -// Inlinability strategy: methods from here should not inline into InlineSlice or LargeSlice unless trivial. -// NOTE: older overlays called this class _DataStorage. The two must -// coexist without a conflicting ObjC class name, so it was renamed. -// The old name must not be used in the new runtime. -@usableFromInline -internal final class __DataStorage { - @usableFromInline static let maxSize = Int.max >> 1 - @usableFromInline static let vmOpsThreshold = NSPageSize() * 4 - - @inlinable // This is @inlinable as trivially forwarding, and does not escape the _DataStorage boundary layer. - static func allocate(_ size: Int, _ clear: Bool) -> UnsafeMutableRawPointer? { - if clear { - return calloc(1, size) - } else { - return malloc(size) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - static func move(_ dest_: UnsafeMutableRawPointer, _ source_: UnsafeRawPointer?, _ num_: Int) { - var dest = dest_ - var source = source_ - var num = num_ - if __DataStorage.vmOpsThreshold <= num && ((unsafeBitCast(source, to: Int.self) | Int(bitPattern: dest)) & (NSPageSize() - 1)) == 0 { - let pages = NSRoundDownToMultipleOfPageSize(num) - NSCopyMemoryPages(source!, dest, pages) - source = source!.advanced(by: pages) - dest = dest.advanced(by: pages) - num -= pages - } - if num > 0 { - memmove(dest, source!, num) - } - } - - @inlinable // This is @inlinable as trivially forwarding, and does not escape the _DataStorage boundary layer. - static func shouldAllocateCleared(_ size: Int) -> Bool { - return (size > (128 * 1024)) - } - - @usableFromInline var _bytes: UnsafeMutableRawPointer? - @usableFromInline var _length: Int - @usableFromInline var _capacity: Int - @usableFromInline var _offset: Int - @usableFromInline var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? - @usableFromInline var _needToZero: Bool - - @inlinable // This is @inlinable as trivially computable. - var bytes: UnsafeRawPointer? { - return UnsafeRawPointer(_bytes)?.advanced(by: -_offset) - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. - @discardableResult - func withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. - @discardableResult - func withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - return try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) - } - - @inlinable // This is @inlinable as trivially computable. - var mutableBytes: UnsafeMutableRawPointer? { - return _bytes?.advanced(by: -_offset) - } - - @inlinable // This is @inlinable as trivially computable. - var capacity: Int { - return _capacity - } - - @inlinable // This is @inlinable as trivially computable. - var length: Int { - get { - return _length - } - set { - setLength(newValue) - } - } - - @inlinable // This is inlinable as trivially computable. - var isExternallyOwned: Bool { - // all __DataStorages will have some sort of capacity, because empty cases hit the .empty enum _Representation - // anything with 0 capacity means that we have not allocated this pointer and consequently mutation is not ours to make. - return _capacity == 0 - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - func ensureUniqueBufferReference(growingTo newLength: Int = 0, clear: Bool = false) { - guard isExternallyOwned || newLength > _capacity else { return } - - if newLength == 0 { - if isExternallyOwned { - let newCapacity = malloc_good_size(_length) - let newBytes = __DataStorage.allocate(newCapacity, false) - __DataStorage.move(newBytes!, _bytes!, _length) - _freeBytes() - _bytes = newBytes - _capacity = newCapacity - _needToZero = false - } - } else if isExternallyOwned { - let newCapacity = malloc_good_size(newLength) - let newBytes = __DataStorage.allocate(newCapacity, clear) - if let bytes = _bytes { - __DataStorage.move(newBytes!, bytes, _length) - } - _freeBytes() - _bytes = newBytes - _capacity = newCapacity - _length = newLength - _needToZero = true - } else { - let cap = _capacity - var additionalCapacity = (newLength >> (__DataStorage.vmOpsThreshold <= newLength ? 2 : 1)) - if Int.max - additionalCapacity < newLength { - additionalCapacity = 0 - } - var newCapacity = malloc_good_size(Swift.max(cap, newLength + additionalCapacity)) - let origLength = _length - var allocateCleared = clear && __DataStorage.shouldAllocateCleared(newCapacity) - var newBytes: UnsafeMutableRawPointer? = nil - if _bytes == nil { - newBytes = __DataStorage.allocate(newCapacity, allocateCleared) - if newBytes == nil { - /* Try again with minimum length */ - allocateCleared = clear && __DataStorage.shouldAllocateCleared(newLength) - newBytes = __DataStorage.allocate(newLength, allocateCleared) - } - } else { - let tryCalloc = (origLength == 0 || (newLength / origLength) >= 4) - if allocateCleared && tryCalloc { - newBytes = __DataStorage.allocate(newCapacity, true) - if let newBytes = newBytes { - __DataStorage.move(newBytes, _bytes!, origLength) - _freeBytes() - } - } - /* Where calloc/memmove/free fails, realloc might succeed */ - if newBytes == nil { - allocateCleared = false - if _deallocator != nil { - newBytes = __DataStorage.allocate(newCapacity, true) - if let newBytes = newBytes { - __DataStorage.move(newBytes, _bytes!, origLength) - _freeBytes() - } - } else { - newBytes = realloc(_bytes!, newCapacity) - } - } - /* Try again with minimum length */ - if newBytes == nil { - newCapacity = malloc_good_size(newLength) - allocateCleared = clear && __DataStorage.shouldAllocateCleared(newCapacity) - if allocateCleared && tryCalloc { - newBytes = __DataStorage.allocate(newCapacity, true) - if let newBytes = newBytes { - __DataStorage.move(newBytes, _bytes!, origLength) - _freeBytes() - } - } - if newBytes == nil { - allocateCleared = false - newBytes = realloc(_bytes!, newCapacity) - } - } - } - - if newBytes == nil { - /* Could not allocate bytes */ - // At this point if the allocation cannot occur the process is likely out of memory - // and Bad-Things™ are going to happen anyhow - fatalError("unable to allocate memory for length (\(newLength))") - } - - if origLength < newLength && clear && !allocateCleared { - memset(newBytes!.advanced(by: origLength), 0, newLength - origLength) - } - - /* _length set by caller */ - _bytes = newBytes - _capacity = newCapacity - /* Realloc/memset doesn't zero out the entire capacity, so we must be safe and clear next time we grow the length */ - _needToZero = !allocateCleared - } - } - - @inlinable // This is @inlinable as it does not escape the _DataStorage boundary layer. - func _freeBytes() { - if let bytes = _bytes { - if let dealloc = _deallocator { - dealloc(bytes, length) - } else { - free(bytes) - } - } - _deallocator = nil - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. - func enumerateBytes(in range: Range, _ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Data.Index, _ stop: inout Bool) -> Void) { - var stopv: Bool = false - block(UnsafeBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset).assumingMemoryBound(to: UInt8.self), count: Swift.min(range.upperBound - range.lowerBound, _length)), 0, &stopv) - } - - @inlinable // This is @inlinable as it does not escape the _DataStorage boundary layer. - func setLength(_ length: Int) { - let origLength = _length - let newLength = length - if _capacity < newLength || _bytes == nil { - ensureUniqueBufferReference(growingTo: newLength, clear: true) - } else if origLength < newLength && _needToZero { - memset(_bytes! + origLength, 0, newLength - origLength) - } else if newLength < origLength { - _needToZero = true - } - _length = newLength - } - - @inlinable // This is @inlinable as it does not escape the _DataStorage boundary layer. - func append(_ bytes: UnsafeRawPointer, length: Int) { - precondition(length >= 0, "Length of appending bytes must not be negative") - let origLength = _length - let newLength = origLength + length - if _capacity < newLength || _bytes == nil { - ensureUniqueBufferReference(growingTo: newLength, clear: false) - } - _length = newLength - __DataStorage.move(_bytes!.advanced(by: origLength), bytes, length) - } - - @inlinable // This is @inlinable despite escaping the __DataStorage boundary layer because it is trivially computed. - func get(_ index: Int) -> UInt8 { - return _bytes!.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. - func set(_ index: Int, to value: UInt8) { - ensureUniqueBufferReference() - _bytes!.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee = value - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. - func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { - let offsetPointer = UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length)) - UnsafeMutableRawBufferPointer(start: pointer, count: range.upperBound - range.lowerBound).copyMemory(from: offsetPointer) - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - func replaceBytes(in range_: NSRange, with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) { - let range = NSRange(location: range_.location - _offset, length: range_.length) - let currentLength = _length - let resultingLength = currentLength - range.length + replacementLength - let shift = resultingLength - currentLength - let mutableBytes: UnsafeMutableRawPointer - if resultingLength > currentLength { - ensureUniqueBufferReference(growingTo: resultingLength) - _length = resultingLength - } else { - ensureUniqueBufferReference() - } - mutableBytes = _bytes! - /* shift the trailing bytes */ - let start = range.location - let length = range.length - if shift != 0 { - memmove(mutableBytes + start + replacementLength, mutableBytes + start + length, currentLength - start - length) - } - if replacementLength != 0 { - if let replacementBytes = replacementBytes { - memmove(mutableBytes + start, replacementBytes, replacementLength) - } else { - memset(mutableBytes + start, 0, replacementLength) - } - } - - if resultingLength < currentLength { - setLength(resultingLength) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - func resetBytes(in range_: Range) { - let range = NSRange(location: range_.lowerBound - _offset, length: range_.upperBound - range_.lowerBound) - if range.length == 0 { return } - if _length < range.location + range.length { - let newLength = range.location + range.length - if _capacity <= newLength { - ensureUniqueBufferReference(growingTo: newLength, clear: false) - } - _length = newLength - } else { - ensureUniqueBufferReference() - } - memset(_bytes!.advanced(by: range.location), 0, range.length) - } - - @usableFromInline // This is not @inlinable as a non-trivial, non-convenience initializer. - init(length: Int) { - precondition(length < __DataStorage.maxSize) - var capacity = (length < 1024 * 1024 * 1024) ? length + (length >> 2) : length - if __DataStorage.vmOpsThreshold <= capacity { - capacity = NSRoundUpToMultipleOfPageSize(capacity) - } - - let clear = __DataStorage.shouldAllocateCleared(length) - _bytes = __DataStorage.allocate(capacity, clear)! - _capacity = capacity - _needToZero = !clear - _length = 0 - _offset = 0 - setLength(length) - } - - @usableFromInline // This is not @inlinable as a non-convience initializer. - init(capacity capacity_: Int = 0) { - var capacity = capacity_ - precondition(capacity < __DataStorage.maxSize) - if __DataStorage.vmOpsThreshold <= capacity { - capacity = NSRoundUpToMultipleOfPageSize(capacity) - } - _length = 0 - _bytes = __DataStorage.allocate(capacity, false)! - _capacity = capacity - _needToZero = true - _offset = 0 - } - - @usableFromInline // This is not @inlinable as a non-convience initializer. - init(bytes: UnsafeRawPointer?, length: Int) { - precondition(length < __DataStorage.maxSize) - _offset = 0 - if length == 0 { - _capacity = 0 - _length = 0 - _needToZero = false - _bytes = nil - } else if __DataStorage.vmOpsThreshold <= length { - _capacity = length - _length = length - _needToZero = true - _bytes = __DataStorage.allocate(length, false)! - __DataStorage.move(_bytes!, bytes, length) - } else { - var capacity = length - if __DataStorage.vmOpsThreshold <= capacity { - capacity = NSRoundUpToMultipleOfPageSize(capacity) - } - _length = length - _bytes = __DataStorage.allocate(capacity, false)! - _capacity = capacity - _needToZero = true - __DataStorage.move(_bytes!, bytes, length) - } - } - - @usableFromInline // This is not @inlinable as a non-convience initializer. - init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?, offset: Int) { - precondition(length < __DataStorage.maxSize) - _offset = offset - if length == 0 { - _capacity = 0 - _length = 0 - _needToZero = false - _bytes = nil - if let dealloc = deallocator, - let bytes_ = bytes { - dealloc(bytes_, length) - } - } else if !copy { - _capacity = length - _length = length - _needToZero = false - _bytes = bytes - _deallocator = deallocator - } else if __DataStorage.vmOpsThreshold <= length { - _capacity = length - _length = length - _needToZero = true - _bytes = __DataStorage.allocate(length, false)! - __DataStorage.move(_bytes!, bytes, length) - if let dealloc = deallocator { - dealloc(bytes!, length) - } - } else { - var capacity = length - if __DataStorage.vmOpsThreshold <= capacity { - capacity = NSRoundUpToMultipleOfPageSize(capacity) - } - _length = length - _bytes = __DataStorage.allocate(capacity, false)! - _capacity = capacity - _needToZero = true - __DataStorage.move(_bytes!, bytes, length) - if let dealloc = deallocator { - dealloc(bytes!, length) - } - } - } - - @usableFromInline // This is not @inlinable as a non-convience initializer. - init(immutableReference: NSData, offset: Int) { - _offset = offset - _bytes = UnsafeMutableRawPointer(mutating: immutableReference.bytes) - _capacity = 0 - _needToZero = false - _length = immutableReference.length - _deallocator = { _, _ in - _fixLifetime(immutableReference) - } - } - - @usableFromInline // This is not @inlinable as a non-convience initializer. - init(mutableReference: NSMutableData, offset: Int) { - _offset = offset - _bytes = mutableReference.mutableBytes - _capacity = 0 - _needToZero = false - _length = mutableReference.length - _deallocator = { _, _ in - _fixLifetime(mutableReference) - } - } - - @usableFromInline // This is not @inlinable as a non-convience initializer. - init(customReference: NSData, offset: Int) { - _offset = offset - _bytes = UnsafeMutableRawPointer(mutating: customReference.bytes) - _capacity = 0 - _needToZero = false - _length = customReference.length - _deallocator = { _, _ in - _fixLifetime(customReference) - } - } - - @usableFromInline // This is not @inlinable as a non-convience initializer. - init(customMutableReference: NSMutableData, offset: Int) { - _offset = offset - _bytes = customMutableReference.mutableBytes - _capacity = 0 - _needToZero = false - _length = customMutableReference.length - _deallocator = { _, _ in - _fixLifetime(customMutableReference) - } - } - - deinit { - _freeBytes() - } - - @inlinable // This is @inlinable despite escaping the __DataStorage boundary layer because it is trivially computed. - func mutableCopy(_ range: Range) -> __DataStorage { - return __DataStorage(bytes: _bytes?.advanced(by: range.lowerBound - _offset), length: range.upperBound - range.lowerBound, copy: true, deallocator: nil, offset: range.lowerBound) - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially computed. - func withInteriorPointerReference(_ range: Range, _ work: (NSData) throws -> T) rethrows -> T { - if range.isEmpty { - return try work(NSData()) // zero length data can be optimized as a singleton - } - return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound - _offset), length: range.upperBound - range.lowerBound, freeWhenDone: false)) - } - - @inline(never) // This is not @inlinable to avoid emission of the private `__NSSwiftData` class name into clients. - @usableFromInline - func bridgedReference(_ range: Range) -> NSData { - if range.isEmpty { - return NSData() // zero length data can be optimized as a singleton - } - - return __NSSwiftData(backing: self, range: range) - } -} - -// NOTE: older overlays called this _NSSwiftData. The two must -// coexist, so it was renamed. The old name must not be used in the new -// runtime. -internal class __NSSwiftData : NSData { - var _backing: __DataStorage! - var _range: Range! - - convenience init(backing: __DataStorage, range: Range) { - self.init() - _backing = backing - _range = range - } - @objc override var length: Int { - return _range.upperBound - _range.lowerBound - } - - @objc override var bytes: UnsafeRawPointer { - // NSData's byte pointer methods are not annotated for nullability correctly - // (but assume non-null by the wrapping macro guards). This placeholder value - // is to work-around this bug. Any indirection to the underlying bytes of an NSData - // with a length of zero would have been a programmer error anyhow so the actual - // return value here is not needed to be an allocated value. This is specifically - // needed to live like this to be source compatible with Swift3. Beyond that point - // this API may be subject to correction. - guard let bytes = _backing.bytes else { - return UnsafeRawPointer(bitPattern: 0xBAD0)! - } - - return bytes.advanced(by: _range.lowerBound) - } - - @objc override func copy(with zone: NSZone? = nil) -> Any { - return self - } - - @objc override func mutableCopy(with zone: NSZone? = nil) -> Any { - return NSMutableData(bytes: bytes, length: length) - } - -#if !DEPLOYMENT_RUNTIME_SWIFT - @objc override - func _isCompact() -> Bool { - return true - } -#endif - -#if DEPLOYMENT_RUNTIME_SWIFT - override func _providesConcreteBacking() -> Bool { - return true - } -#else - @objc(_providesConcreteBacking) - func _providesConcreteBacking() -> Bool { - return true - } -#endif -} - -@frozen -public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessCollection, MutableCollection, RangeReplaceableCollection, MutableDataProtocol, ContiguousBytes { - public typealias ReferenceType = NSData - - public typealias ReadingOptions = NSData.ReadingOptions - public typealias WritingOptions = NSData.WritingOptions - public typealias SearchOptions = NSData.SearchOptions - public typealias Base64EncodingOptions = NSData.Base64EncodingOptions - public typealias Base64DecodingOptions = NSData.Base64DecodingOptions - - public typealias Index = Int - public typealias Indices = Range - - // A small inline buffer of bytes suitable for stack-allocation of small data. - // Inlinability strategy: everything here should be inlined for direct operation on the stack wherever possible. - @usableFromInline - @frozen - internal struct InlineData { -#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) - @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum - @usableFromInline var bytes: Buffer -#elseif arch(i386) || arch(arm) || arch(arm64_32) - @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8) //len //enum - @usableFromInline var bytes: Buffer -#else - #error ("Unsupported architecture: a definition of Buffer needs to be made with N = (MemoryLayout<(Int, Int)>.size - 2) UInt8 members to a tuple") -#endif - @usableFromInline var length: UInt8 - - @inlinable // This is @inlinable as trivially computable. - static func canStore(count: Int) -> Bool { - return count <= MemoryLayout.size - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ srcBuffer: UnsafeRawBufferPointer) { - self.init(count: srcBuffer.count) - if !srcBuffer.isEmpty { - Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in - dstBuffer.baseAddress?.copyMemory(from: srcBuffer.baseAddress!, byteCount: srcBuffer.count) - } - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(count: Int = 0) { - assert(count <= MemoryLayout.size) -#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) - bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) -#elseif arch(i386) || arch(arm) || arch(arm64_32) - bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) -#else - #error ("Unsupported architecture: initialization for Buffer is required for this architecture") -#endif - length = UInt8(count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ slice: InlineSlice, count: Int) { - self.init(count: count) - Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in - slice.withUnsafeBytes { srcBuffer in - dstBuffer.copyMemory(from: UnsafeRawBufferPointer(start: srcBuffer.baseAddress, count: count)) - } - } - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ slice: LargeSlice, count: Int) { - self.init(count: count) - Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in - slice.withUnsafeBytes { srcBuffer in - dstBuffer.copyMemory(from: UnsafeRawBufferPointer(start: srcBuffer.baseAddress, count: count)) - } - } - } - - @inlinable // This is @inlinable as trivially computable. - var capacity: Int { - return MemoryLayout.size - } - - @inlinable // This is @inlinable as trivially computable. - var count: Int { - get { - return Int(length) - } - set(newValue) { - assert(newValue <= MemoryLayout.size) - length = UInt8(newValue) - } - } - - @inlinable // This is @inlinable as trivially computable. - var startIndex: Int { - return 0 - } - - @inlinable // This is @inlinable as trivially computable. - var endIndex: Int { - return count - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - let count = Int(length) - return try Swift.withUnsafeBytes(of: bytes) { (rawBuffer) throws -> Result in - return try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count)) - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - let count = Int(length) - return try Swift.withUnsafeMutableBytes(of: &bytes) { (rawBuffer) throws -> Result in - return try apply(UnsafeMutableRawBufferPointer(start: rawBuffer.baseAddress, count: count)) - } - } - - @inlinable // This is @inlinable as tribially computable. - mutating func append(byte: UInt8) { - let count = self.count - assert(count + 1 <= MemoryLayout.size) - Swift.withUnsafeMutableBytes(of: &bytes) { $0[count] = byte } - self.length += 1 - } - - @inlinable // This is @inlinable as trivially computable. - mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { - guard !buffer.isEmpty else { return } - assert(count + buffer.count <= MemoryLayout.size) - let cnt = count - _ = Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in - rawBuffer.baseAddress?.advanced(by: cnt).copyMemory(from: buffer.baseAddress!, byteCount: buffer.count) - } - - length += UInt8(buffer.count) - } - - @inlinable // This is @inlinable as trivially computable. - subscript(index: Index) -> UInt8 { - get { - assert(index <= MemoryLayout.size) - precondition(index < length, "index \(index) is out of bounds of 0..<\(length)") - return Swift.withUnsafeBytes(of: bytes) { rawBuffer -> UInt8 in - return rawBuffer[index] - } - } - set(newValue) { - assert(index <= MemoryLayout.size) - precondition(index < length, "index \(index) is out of bounds of 0..<\(length)") - Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in - rawBuffer[index] = newValue - } - } - } - - @inlinable // This is @inlinable as trivially computable. - mutating func resetBytes(in range: Range) { - assert(range.lowerBound <= MemoryLayout.size) - assert(range.upperBound <= MemoryLayout.size) - precondition(range.lowerBound <= length, "index \(range.lowerBound) is out of bounds of 0..<\(length)") - if count < range.upperBound { - count = range.upperBound - } - - let _ = Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in - memset(rawBuffer.baseAddress?.advanced(by: range.lowerBound), 0, range.upperBound - range.lowerBound) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - mutating func replaceSubrange(_ subrange: Range, with replacementBytes: UnsafeRawPointer?, count replacementLength: Int) { - assert(subrange.lowerBound <= MemoryLayout.size) - assert(subrange.upperBound <= MemoryLayout.size) - assert(count - (subrange.upperBound - subrange.lowerBound) + replacementLength <= MemoryLayout.size) - precondition(subrange.lowerBound <= length, "index \(subrange.lowerBound) is out of bounds of 0..<\(length)") - precondition(subrange.upperBound <= length, "index \(subrange.upperBound) is out of bounds of 0..<\(length)") - let currentLength = count - let resultingLength = currentLength - (subrange.upperBound - subrange.lowerBound) + replacementLength - let shift = resultingLength - currentLength - Swift.withUnsafeMutableBytes(of: &bytes) { mutableBytes in - /* shift the trailing bytes */ - let start = subrange.lowerBound - let length = subrange.upperBound - subrange.lowerBound - if shift != 0 { - memmove(mutableBytes.baseAddress?.advanced(by: start + replacementLength), mutableBytes.baseAddress?.advanced(by: start + length), currentLength - start - length) - } - if replacementLength != 0 { - memmove(mutableBytes.baseAddress?.advanced(by: start), replacementBytes!, replacementLength) - } - } - count = resultingLength - } - - @inlinable // This is @inlinable as trivially computable. - func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { - precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - - Swift.withUnsafeBytes(of: bytes) { - let cnt = Swift.min($0.count, range.upperBound - range.lowerBound) - guard cnt > 0 else { return } - pointer.copyMemory(from: $0.baseAddress!.advanced(by: range.lowerBound), byteCount: cnt) - } - } - - @inline(__always) // This should always be inlined into _Representation.hash(into:). - func hash(into hasher: inout Hasher) { - // **NOTE**: this uses `count` (an Int) and NOT `length` (a UInt8) - // Despite having the same value, they hash differently. InlineSlice and LargeSlice both use `count` (an Int); if you combine the same bytes but with `length` over `count`, you can get a different hash. - // - // This affects slices, which are InlineSlice and not InlineData: - // - // let d = Data([0xFF, 0xFF]) // InlineData - // let s = Data([0, 0xFF, 0xFF]).dropFirst() // InlineSlice - // assert(s == d) - // assert(s.hashValue == d.hashValue) - hasher.combine(count) - - Swift.withUnsafeBytes(of: bytes) { - // We have access to the full byte buffer here, but not all of it is meaningfully used (bytes past self.length may be garbage). - let bytes = UnsafeRawBufferPointer(start: $0.baseAddress, count: self.count) - hasher.combine(bytes: bytes) - } - } - } - -#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) - @usableFromInline internal typealias HalfInt = Int32 -#elseif arch(i386) || arch(arm) || arch(arm64_32) - @usableFromInline internal typealias HalfInt = Int16 -#else - #error ("Unsupported architecture: a definition of half of the pointer sized Int needs to be defined for this architecture") -#endif - - // A buffer of bytes too large to fit in an InlineData, but still small enough to fit a storage pointer + range in two words. - // Inlinability strategy: everything here should be easily inlinable as large _DataStorage methods should not inline into here. - @usableFromInline - @frozen - internal struct InlineSlice { - // ***WARNING*** - // These ivars are specifically laid out so that they cause the enum _Representation to be 16 bytes on 64 bit platforms. This means we _MUST_ have the class type thing last - @usableFromInline var slice: Range - @usableFromInline var storage: __DataStorage - - @inlinable // This is @inlinable as trivially computable. - static func canStore(count: Int) -> Bool { - return count < HalfInt.max - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ buffer: UnsafeRawBufferPointer) { - assert(buffer.count < HalfInt.max) - self.init(__DataStorage(bytes: buffer.baseAddress, length: buffer.count), count: buffer.count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(capacity: Int) { - assert(capacity < HalfInt.max) - self.init(__DataStorage(capacity: capacity), count: 0) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(count: Int) { - assert(count < HalfInt.max) - self.init(__DataStorage(length: count), count: count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ inline: InlineData) { - assert(inline.count < HalfInt.max) - self.init(inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) }, count: inline.count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ inline: InlineData, range: Range) { - assert(range.lowerBound < HalfInt.max) - assert(range.upperBound < HalfInt.max) - self.init(inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) }, range: range) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ large: LargeSlice) { - assert(large.range.lowerBound < HalfInt.max) - assert(large.range.upperBound < HalfInt.max) - self.init(large.storage, range: large.range) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ large: LargeSlice, range: Range) { - assert(range.lowerBound < HalfInt.max) - assert(range.upperBound < HalfInt.max) - self.init(large.storage, range: range) - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ storage: __DataStorage, count: Int) { - assert(count < HalfInt.max) - self.storage = storage - slice = 0..) { - assert(range.lowerBound < HalfInt.max) - assert(range.upperBound < HalfInt.max) - self.storage = storage - slice = HalfInt(range.lowerBound).. { - get { - return Int(slice.lowerBound)..(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try storage.withUnsafeBytes(in: range, apply: apply) - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - ensureUniqueReference() - return try storage.withUnsafeMutableBytes(in: range, apply: apply) - } - - @inlinable // This is @inlinable as reasonably small. - mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { - assert(endIndex + buffer.count < HalfInt.max) - ensureUniqueReference() - storage.replaceBytes(in: NSRange(location: range.upperBound, length: storage.length - (range.upperBound - storage._offset)), with: buffer.baseAddress, length: buffer.count) - slice = slice.lowerBound.. UInt8 { - get { - assert(index < HalfInt.max) - precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - return storage.get(index) - } - set(newValue) { - assert(index < HalfInt.max) - precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - ensureUniqueReference() - storage.set(index, to: newValue) - } - } - - @inlinable // This is @inlinable as trivially forwarding. - func bridgedReference() -> NSData { - return storage.bridgedReference(self.range) - } - - @inlinable // This is @inlinable as reasonably small. - mutating func resetBytes(in range: Range) { - assert(range.lowerBound < HalfInt.max) - assert(range.upperBound < HalfInt.max) - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - ensureUniqueReference() - storage.resetBytes(in: range) - if slice.upperBound < range.upperBound { - slice = slice.lowerBound.., with bytes: UnsafeRawPointer?, count cnt: Int) { - precondition(startIndex <= subrange.lowerBound, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(subrange.lowerBound <= endIndex, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= subrange.upperBound, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(subrange.upperBound <= endIndex, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - - let nsRange = NSRange(location: subrange.lowerBound, length: subrange.upperBound - subrange.lowerBound) - ensureUniqueReference() - let upper = range.upperBound - storage.replaceBytes(in: nsRange, with: bytes, length: cnt) - let resultingUpper = upper - nsRange.length + cnt - slice = slice.lowerBound..) { - precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - storage.copyBytes(to: pointer, from: range) - } - - @inline(__always) // This should always be inlined into _Representation.hash(into:). - func hash(into hasher: inout Hasher) { - hasher.combine(count) - - // At most, hash the first 80 bytes of this data. - let range = startIndex ..< Swift.min(startIndex + 80, endIndex) - storage.withUnsafeBytes(in: range) { - hasher.combine(bytes: $0) - } - } - } - - // A reference wrapper around a Range for when the range of a data buffer is too large to whole in a single word. - // Inlinability strategy: everything should be inlinable as trivial. - @usableFromInline - @_fixed_layout - internal final class RangeReference { - @usableFromInline var range: Range - - @inlinable @inline(__always) // This is @inlinable as trivially forwarding. - var lowerBound: Int { - return range.lowerBound - } - - @inlinable @inline(__always) // This is @inlinable as trivially forwarding. - var upperBound: Int { - return range.upperBound - } - - @inlinable @inline(__always) // This is @inlinable as trivially computable. - var count: Int { - return range.upperBound - range.lowerBound - } - - @inlinable @inline(__always) // This is @inlinable as a trivial initializer. - init(_ range: Range) { - self.range = range - } - } - - // A buffer of bytes whose range is too large to fit in a signle word. Used alongside a RangeReference to make it fit into _Representation's two-word size. - // Inlinability strategy: everything here should be easily inlinable as large _DataStorage methods should not inline into here. - @usableFromInline - @frozen - internal struct LargeSlice { - // ***WARNING*** - // These ivars are specifically laid out so that they cause the enum _Representation to be 16 bytes on 64 bit platforms. This means we _MUST_ have the class type thing last - @usableFromInline var slice: RangeReference - @usableFromInline var storage: __DataStorage - - @inlinable // This is @inlinable as a convenience initializer. - init(_ buffer: UnsafeRawBufferPointer) { - self.init(__DataStorage(bytes: buffer.baseAddress, length: buffer.count), count: buffer.count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(capacity: Int) { - self.init(__DataStorage(capacity: capacity), count: 0) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(count: Int) { - self.init(__DataStorage(length: count), count: count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ inline: InlineData) { - let storage = inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) } - self.init(storage, count: inline.count) - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ slice: InlineSlice) { - self.storage = slice.storage - self.slice = RangeReference(slice.range) - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ storage: __DataStorage, count: Int) { - self.storage = storage - self.slice = RangeReference(0.. { - return slice.range - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try storage.withUnsafeBytes(in: range, apply: apply) - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - ensureUniqueReference() - return try storage.withUnsafeMutableBytes(in: range, apply: apply) - } - - @inlinable // This is @inlinable as reasonably small. - mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { - ensureUniqueReference() - storage.replaceBytes(in: NSRange(location: range.upperBound, length: storage.length - (range.upperBound - storage._offset)), with: buffer.baseAddress, length: buffer.count) - slice.range = slice.range.lowerBound.. UInt8 { - get { - precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - return storage.get(index) - } - set(newValue) { - precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - ensureUniqueReference() - storage.set(index, to: newValue) - } - } - - @inlinable // This is @inlinable as trivially forwarding. - func bridgedReference() -> NSData { - return storage.bridgedReference(self.range) - } - - @inlinable // This is @inlinable as reasonably small. - mutating func resetBytes(in range: Range) { - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - ensureUniqueReference() - storage.resetBytes(in: range) - if slice.range.upperBound < range.upperBound { - slice.range = slice.range.lowerBound.., with bytes: UnsafeRawPointer?, count cnt: Int) { - precondition(startIndex <= subrange.lowerBound, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(subrange.lowerBound <= endIndex, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= subrange.upperBound, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(subrange.upperBound <= endIndex, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - - let nsRange = NSRange(location: subrange.lowerBound, length: subrange.upperBound - subrange.lowerBound) - ensureUniqueReference() - let upper = range.upperBound - storage.replaceBytes(in: nsRange, with: bytes, length: cnt) - let resultingUpper = upper - nsRange.length + cnt - slice.range = slice.range.lowerBound..) { - precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - storage.copyBytes(to: pointer, from: range) - } - - @inline(__always) // This should always be inlined into _Representation.hash(into:). - func hash(into hasher: inout Hasher) { - hasher.combine(count) - - // Hash at most the first 80 bytes of this data. - let range = startIndex ..< Swift.min(startIndex + 80, endIndex) - storage.withUnsafeBytes(in: range) { - hasher.combine(bytes: $0) - } - } - } - - // The actual storage for Data's various representations. - // Inlinability strategy: almost everything should be inlinable as forwarding the underlying implementations. (Inlining can also help avoid retain-release traffic around pulling values out of enums.) - @usableFromInline - @frozen - internal enum _Representation { - case empty - case inline(InlineData) - case slice(InlineSlice) - case large(LargeSlice) - - @inlinable // This is @inlinable as a trivial initializer. - init(_ buffer: UnsafeRawBufferPointer) { - if buffer.isEmpty { - self = .empty - } else if InlineData.canStore(count: buffer.count) { - self = .inline(InlineData(buffer)) - } else if InlineSlice.canStore(count: buffer.count) { - self = .slice(InlineSlice(buffer)) - } else { - self = .large(LargeSlice(buffer)) - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ buffer: UnsafeRawBufferPointer, owner: AnyObject) { - if buffer.isEmpty { - self = .empty - } else if InlineData.canStore(count: buffer.count) { - self = .inline(InlineData(buffer)) - } else { - let count = buffer.count - let storage = __DataStorage(bytes: UnsafeMutableRawPointer(mutating: buffer.baseAddress), length: count, copy: false, deallocator: { _, _ in - _fixLifetime(owner) - }, offset: 0) - if InlineSlice.canStore(count: count) { - self = .slice(InlineSlice(storage, count: count)) - } else { - self = .large(LargeSlice(storage, count: count)) - } - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(capacity: Int) { - if capacity == 0 { - self = .empty - } else if InlineData.canStore(count: capacity) { - self = .inline(InlineData()) - } else if InlineSlice.canStore(count: capacity) { - self = .slice(InlineSlice(capacity: capacity)) - } else { - self = .large(LargeSlice(capacity: capacity)) - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(count: Int) { - if count == 0 { - self = .empty - } else if InlineData.canStore(count: count) { - self = .inline(InlineData(count: count)) - } else if InlineSlice.canStore(count: count) { - self = .slice(InlineSlice(count: count)) - } else { - self = .large(LargeSlice(count: count)) - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ storage: __DataStorage, count: Int) { - if count == 0 { - self = .empty - } else if InlineData.canStore(count: count) { - self = .inline(storage.withUnsafeBytes(in: 0.. 0 else { return } - switch self { - case .empty: - if InlineData.canStore(count: minimumCapacity) { - self = .inline(InlineData()) - } else if InlineSlice.canStore(count: minimumCapacity) { - self = .slice(InlineSlice(capacity: minimumCapacity)) - } else { - self = .large(LargeSlice(capacity: minimumCapacity)) - } - case .inline(let inline): - guard minimumCapacity > inline.capacity else { return } - // we know we are going to be heap promoted - if InlineSlice.canStore(count: minimumCapacity) { - var slice = InlineSlice(inline) - slice.reserveCapacity(minimumCapacity) - self = .slice(slice) - } else { - var slice = LargeSlice(inline) - slice.reserveCapacity(minimumCapacity) - self = .large(slice) - } - case .slice(var slice): - guard minimumCapacity > slice.capacity else { return } - if InlineSlice.canStore(count: minimumCapacity) { - self = .empty - slice.reserveCapacity(minimumCapacity) - self = .slice(slice) - } else { - var large = LargeSlice(slice) - large.reserveCapacity(minimumCapacity) - self = .large(large) - } - case .large(var slice): - guard minimumCapacity > slice.capacity else { return } - self = .empty - slice.reserveCapacity(minimumCapacity) - self = .large(slice) - } - } - - @inlinable // This is @inlinable as reasonably small. - var count: Int { - get { - switch self { - case .empty: return 0 - case .inline(let inline): return inline.count - case .slice(let slice): return slice.count - case .large(let slice): return slice.count - } - } - set(newValue) { - // HACK: The definition of this inline function takes an inout reference to self, giving the optimizer a unique referencing guarantee. - // This allows us to avoid excessive retain-release traffic around modifying enum values, and inlining the function then avoids the additional frame. - @inline(__always) - func apply(_ representation: inout _Representation, _ newValue: Int) -> _Representation? { - switch representation { - case .empty: - if newValue == 0 { - return nil - } else if InlineData.canStore(count: newValue) { - return .inline(InlineData(count: newValue)) - } else if InlineSlice.canStore(count: newValue) { - return .slice(InlineSlice(count: newValue)) - } else { - return .large(LargeSlice(count: newValue)) - } - case .inline(var inline): - if newValue == 0 { - return .empty - } else if InlineData.canStore(count: newValue) { - guard inline.count != newValue else { return nil } - inline.count = newValue - return .inline(inline) - } else if InlineSlice.canStore(count: newValue) { - var slice = InlineSlice(inline) - slice.count = newValue - return .slice(slice) - } else { - var slice = LargeSlice(inline) - slice.count = newValue - return .large(slice) - } - case .slice(var slice): - if newValue == 0 && slice.startIndex == 0 { - return .empty - } else if slice.startIndex == 0 && InlineData.canStore(count: newValue) { - return .inline(InlineData(slice, count: newValue)) - } else if InlineSlice.canStore(count: newValue + slice.startIndex) { - guard slice.count != newValue else { return nil } - representation = .empty // TODO: remove this when mgottesman lands optimizations - slice.count = newValue - return .slice(slice) - } else { - var newSlice = LargeSlice(slice) - newSlice.count = newValue - return .large(newSlice) - } - case .large(var slice): - if newValue == 0 && slice.startIndex == 0 { - return .empty - } else if slice.startIndex == 0 && InlineData.canStore(count: newValue) { - return .inline(InlineData(slice, count: newValue)) - } else { - guard slice.count != newValue else { return nil} - representation = .empty // TODO: remove this when mgottesman lands optimizations - slice.count = newValue - return .large(slice) - } - } - } - - if let rep = apply(&self, newValue) { - self = rep - } - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - switch self { - case .empty: - let empty = InlineData() - return try empty.withUnsafeBytes(apply) - case .inline(let inline): - return try inline.withUnsafeBytes(apply) - case .slice(let slice): - return try slice.withUnsafeBytes(apply) - case .large(let slice): - return try slice.withUnsafeBytes(apply) - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - switch self { - case .empty: - var empty = InlineData() - return try empty.withUnsafeMutableBytes(apply) - case .inline(var inline): - defer { self = .inline(inline) } - return try inline.withUnsafeMutableBytes(apply) - case .slice(var slice): - self = .empty - defer { self = .slice(slice) } - return try slice.withUnsafeMutableBytes(apply) - case .large(var slice): - self = .empty - defer { self = .large(slice) } - return try slice.withUnsafeMutableBytes(apply) - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withInteriorPointerReference(_ work: (NSData) throws -> T) rethrows -> T { - switch self { - case .empty: - return try work(NSData()) - case .inline(let inline): - return try inline.withUnsafeBytes { - return try work(NSData(bytesNoCopy: UnsafeMutableRawPointer(mutating: $0.baseAddress ?? UnsafeRawPointer(bitPattern: 0xBAD0)!), length: $0.count, freeWhenDone: false)) - } - case .slice(let slice): - return try slice.storage.withInteriorPointerReference(slice.range, work) - case .large(let slice): - return try slice.storage.withInteriorPointerReference(slice.range, work) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Index, _ stop: inout Bool) -> Void) { - switch self { - case .empty: - var stop = false - block(UnsafeBufferPointer(start: nil, count: 0), 0, &stop) - case .inline(let inline): - inline.withUnsafeBytes { - var stop = false - block(UnsafeBufferPointer(start: $0.baseAddress?.assumingMemoryBound(to: UInt8.self), count: $0.count), 0, &stop) - } - case .slice(let slice): - slice.storage.enumerateBytes(in: slice.range, block) - case .large(let slice): - slice.storage.enumerateBytes(in: slice.range, block) - } - } - - @inlinable // This is @inlinable as reasonably small. - mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { - switch self { - case .empty: - self = _Representation(buffer) - case .inline(var inline): - if InlineData.canStore(count: inline.count + buffer.count) { - inline.append(contentsOf: buffer) - self = .inline(inline) - } else if InlineSlice.canStore(count: inline.count + buffer.count) { - var newSlice = InlineSlice(inline) - newSlice.append(contentsOf: buffer) - self = .slice(newSlice) - } else { - var newSlice = LargeSlice(inline) - newSlice.append(contentsOf: buffer) - self = .large(newSlice) - } - case .slice(var slice): - if InlineSlice.canStore(count: slice.range.upperBound + buffer.count) { - self = .empty - defer { self = .slice(slice) } - slice.append(contentsOf: buffer) - } else { - self = .empty - var newSlice = LargeSlice(slice) - newSlice.append(contentsOf: buffer) - self = .large(newSlice) - } - case .large(var slice): - self = .empty - defer { self = .large(slice) } - slice.append(contentsOf: buffer) - } - } - - @inlinable // This is @inlinable as reasonably small. - mutating func resetBytes(in range: Range) { - switch self { - case .empty: - if range.upperBound == 0 { - self = .empty - } else if InlineData.canStore(count: range.upperBound) { - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - self = .inline(InlineData(count: range.upperBound)) - } else if InlineSlice.canStore(count: range.upperBound) { - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - self = .slice(InlineSlice(count: range.upperBound)) - } else { - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - self = .large(LargeSlice(count: range.upperBound)) - } - case .inline(var inline): - if inline.count < range.upperBound { - if InlineSlice.canStore(count: range.upperBound) { - var slice = InlineSlice(inline) - slice.resetBytes(in: range) - self = .slice(slice) - } else { - var slice = LargeSlice(inline) - slice.resetBytes(in: range) - self = .large(slice) - } - } else { - inline.resetBytes(in: range) - self = .inline(inline) - } - case .slice(var slice): - if InlineSlice.canStore(count: range.upperBound) { - self = .empty - slice.resetBytes(in: range) - self = .slice(slice) - } else { - self = .empty - var newSlice = LargeSlice(slice) - newSlice.resetBytes(in: range) - self = .large(newSlice) - } - case .large(var slice): - self = .empty - slice.resetBytes(in: range) - self = .large(slice) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - mutating func replaceSubrange(_ subrange: Range, with bytes: UnsafeRawPointer?, count cnt: Int) { - switch self { - case .empty: - precondition(subrange.lowerBound == 0 && subrange.upperBound == 0, "range \(subrange) out of bounds of 0..<0") - if cnt == 0 { - return - } else if InlineData.canStore(count: cnt) { - self = .inline(InlineData(UnsafeRawBufferPointer(start: bytes, count: cnt))) - } else if InlineSlice.canStore(count: cnt) { - self = .slice(InlineSlice(UnsafeRawBufferPointer(start: bytes, count: cnt))) - } else { - self = .large(LargeSlice(UnsafeRawBufferPointer(start: bytes, count: cnt))) - } - case .inline(var inline): - let resultingCount = inline.count + cnt - (subrange.upperBound - subrange.lowerBound) - if resultingCount == 0 { - self = .empty - } else if InlineData.canStore(count: resultingCount) { - inline.replaceSubrange(subrange, with: bytes, count: cnt) - self = .inline(inline) - } else if InlineSlice.canStore(count: resultingCount) { - var slice = InlineSlice(inline) - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .slice(slice) - } else { - var slice = LargeSlice(inline) - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .large(slice) - } - case .slice(var slice): - let resultingUpper = slice.endIndex + cnt - (subrange.upperBound - subrange.lowerBound) - if slice.startIndex == 0 && resultingUpper == 0 { - self = .empty - } else if slice.startIndex == 0 && InlineData.canStore(count: resultingUpper) { - self = .empty - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .inline(InlineData(slice, count: slice.count)) - } else if InlineSlice.canStore(count: resultingUpper) { - self = .empty - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .slice(slice) - } else { - self = .empty - var newSlice = LargeSlice(slice) - newSlice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .large(newSlice) - } - case .large(var slice): - let resultingUpper = slice.endIndex + cnt - (subrange.upperBound - subrange.lowerBound) - if slice.startIndex == 0 && resultingUpper == 0 { - self = .empty - } else if slice.startIndex == 0 && InlineData.canStore(count: resultingUpper) { - var inline = InlineData(count: resultingUpper) - inline.withUnsafeMutableBytes { inlineBuffer in - if cnt > 0 { - inlineBuffer.baseAddress?.advanced(by: subrange.lowerBound).copyMemory(from: bytes!, byteCount: cnt) - } - slice.withUnsafeBytes { buffer in - if subrange.lowerBound > 0 { - inlineBuffer.baseAddress?.copyMemory(from: buffer.baseAddress!, byteCount: subrange.lowerBound) - } - if subrange.upperBound < resultingUpper { - inlineBuffer.baseAddress?.advanced(by: subrange.upperBound).copyMemory(from: buffer.baseAddress!.advanced(by: subrange.upperBound), byteCount: resultingUpper - subrange.upperBound) - } - } - } - self = .inline(inline) - } else if InlineSlice.canStore(count: slice.startIndex) && InlineSlice.canStore(count: resultingUpper) { - self = .empty - var newSlice = InlineSlice(slice) - newSlice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .slice(newSlice) - } else { - self = .empty - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .large(slice) - } - } - } - - @inlinable // This is @inlinable as trivially forwarding. - subscript(index: Index) -> UInt8 { - get { - switch self { - case .empty: preconditionFailure("index \(index) out of range of 0") - case .inline(let inline): return inline[index] - case .slice(let slice): return slice[index] - case .large(let slice): return slice[index] - } - } - set(newValue) { - switch self { - case .empty: preconditionFailure("index \(index) out of range of 0") - case .inline(var inline): - inline[index] = newValue - self = .inline(inline) - case .slice(var slice): - self = .empty - slice[index] = newValue - self = .slice(slice) - case .large(var slice): - self = .empty - slice[index] = newValue - self = .large(slice) - } - } - } - - @inlinable // This is @inlinable as reasonably small. - subscript(bounds: Range) -> Data { - get { - switch self { - case .empty: - precondition(bounds.lowerBound == 0 && (bounds.upperBound - bounds.lowerBound) == 0, "Range \(bounds) out of bounds 0..<0") - return Data() - case .inline(let inline): - precondition(bounds.upperBound <= inline.count, "Range \(bounds) out of bounds 0..<\(inline.count)") - if bounds.lowerBound == 0 { - var newInline = inline - newInline.count = bounds.upperBound - return Data(representation: .inline(newInline)) - } else { - return Data(representation: .slice(InlineSlice(inline, range: bounds))) - } - case .slice(let slice): - precondition(slice.startIndex <= bounds.lowerBound, "Range \(bounds) out of bounds \(slice.range)") - precondition(bounds.lowerBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") - precondition(slice.startIndex <= bounds.upperBound, "Range \(bounds) out of bounds \(slice.range)") - precondition(bounds.upperBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") - if bounds.lowerBound == 0 && bounds.upperBound == 0 { - return Data() - } else if bounds.lowerBound == 0 && InlineData.canStore(count: bounds.count) { - return Data(representation: .inline(InlineData(slice, count: bounds.count))) - } else { - var newSlice = slice - newSlice.range = bounds - return Data(representation: .slice(newSlice)) - } - case .large(let slice): - precondition(slice.startIndex <= bounds.lowerBound, "Range \(bounds) out of bounds \(slice.range)") - precondition(bounds.lowerBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") - precondition(slice.startIndex <= bounds.upperBound, "Range \(bounds) out of bounds \(slice.range)") - precondition(bounds.upperBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") - if bounds.lowerBound == 0 && bounds.upperBound == 0 { - return Data() - } else if bounds.lowerBound == 0 && InlineData.canStore(count: bounds.upperBound) { - return Data(representation: .inline(InlineData(slice, count: bounds.upperBound))) - } else if InlineSlice.canStore(count: bounds.lowerBound) && InlineSlice.canStore(count: bounds.upperBound) { - return Data(representation: .slice(InlineSlice(slice, range: bounds))) - } else { - var newSlice = slice - newSlice.slice = RangeReference(bounds) - return Data(representation: .large(newSlice)) - } - } - } - } - - @inlinable // This is @inlinable as trivially forwarding. - var startIndex: Int { - switch self { - case .empty: return 0 - case .inline: return 0 - case .slice(let slice): return slice.startIndex - case .large(let slice): return slice.startIndex - } - } - - @inlinable // This is @inlinable as trivially forwarding. - var endIndex: Int { - switch self { - case .empty: return 0 - case .inline(let inline): return inline.count - case .slice(let slice): return slice.endIndex - case .large(let slice): return slice.endIndex - } - } - - @inlinable // This is @inlinable as trivially forwarding. - func bridgedReference() -> NSData { - switch self { - case .empty: return NSData() - case .inline(let inline): - return inline.withUnsafeBytes { - return NSData(bytes: $0.baseAddress, length: $0.count) - } - case .slice(let slice): - return slice.bridgedReference() - case .large(let slice): - return slice.bridgedReference() - } - } - - @inlinable // This is @inlinable as trivially forwarding. - func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { - switch self { - case .empty: - precondition(range.lowerBound == 0 && range.upperBound == 0, "Range \(range) out of bounds 0..<0") - return - case .inline(let inline): - inline.copyBytes(to: pointer, from: range) - case .slice(let slice): - slice.copyBytes(to: pointer, from: range) - case .large(let slice): - slice.copyBytes(to: pointer, from: range) - } - } - - @inline(__always) // This should always be inlined into Data.hash(into:). - func hash(into hasher: inout Hasher) { - switch self { - case .empty: - hasher.combine(0) - case .inline(let inline): - inline.hash(into: &hasher) - case .slice(let slice): - slice.hash(into: &hasher) - case .large(let large): - large.hash(into: &hasher) - } - } - } - - @usableFromInline internal var _representation: _Representation - - // A standard or custom deallocator for `Data`. - /// - /// When creating a `Data` with the no-copy initializer, you may specify a `Data.Deallocator` to customize the behavior of how the backing store is deallocated. - public enum Deallocator { - /// Use a virtual memory deallocator. -#if !DEPLOYMENT_RUNTIME_SWIFT - case virtualMemory -#endif - - /// Use `munmap`. - case unmap - - /// Use `free`. - case free - - /// Do nothing upon deallocation. - case none - - /// A custom deallocator. - case custom((UnsafeMutableRawPointer, Int) -> Void) - - @usableFromInline internal var _deallocator : ((UnsafeMutableRawPointer, Int) -> Void) { -#if DEPLOYMENT_RUNTIME_SWIFT - switch self { - case .unmap: - return { __NSDataInvokeDeallocatorUnmap($0, $1) } - case .free: - return { __NSDataInvokeDeallocatorFree($0, $1) } - case .none: - return { _, _ in } - case .custom(let b): - return b - } -#else - switch self { - case .virtualMemory: - return { NSDataDeallocatorVM($0, $1) } - case .unmap: - return { NSDataDeallocatorUnmap($0, $1) } - case .free: - return { NSDataDeallocatorFree($0, $1) } - case .none: - return { _, _ in } - case .custom(let b): - return b - } -#endif - } - } - - // MARK: - - // MARK: Init methods - - /// Initialize a `Data` with copied memory content. - /// - /// - parameter bytes: A pointer to the memory. It will be copied. - /// - parameter count: The number of bytes to copy. - @inlinable // This is @inlinable as a trivial initializer. - public init(bytes: UnsafeRawPointer, count: Int) { - _representation = _Representation(UnsafeRawBufferPointer(start: bytes, count: count)) - } - - /// Initialize a `Data` with copied memory content. - /// - /// - parameter buffer: A buffer pointer to copy. The size is calculated from `SourceType` and `buffer.count`. - @inlinable // This is @inlinable as a trivial, generic initializer. - public init(buffer: UnsafeBufferPointer) { - _representation = _Representation(UnsafeRawBufferPointer(buffer)) - } - - /// Initialize a `Data` with copied memory content. - /// - /// - parameter buffer: A buffer pointer to copy. The size is calculated from `SourceType` and `buffer.count`. - @inlinable // This is @inlinable as a trivial, generic initializer. - public init(buffer: UnsafeMutableBufferPointer) { - _representation = _Representation(UnsafeRawBufferPointer(buffer)) - } - - /// Initialize a `Data` with a repeating byte pattern - /// - /// - parameter repeatedValue: A byte to initialize the pattern - /// - parameter count: The number of bytes the data initially contains initialized to the repeatedValue - @inlinable // This is @inlinable as a convenience initializer. - public init(repeating repeatedValue: UInt8, count: Int) { - self.init(count: count) - withUnsafeMutableBytes { (buffer: UnsafeMutableRawBufferPointer) -> Void in - memset(buffer.baseAddress, Int32(repeatedValue), buffer.count) - } - } - - /// Initialize a `Data` with the specified size. - /// - /// This initializer doesn't necessarily allocate the requested memory right away. `Data` allocates additional memory as needed, so `capacity` simply establishes the initial capacity. When it does allocate the initial memory, though, it allocates the specified amount. - /// - /// This method sets the `count` of the data to 0. - /// - /// If the capacity specified in `capacity` is greater than four memory pages in size, this may round the amount of requested memory up to the nearest full page. - /// - /// - parameter capacity: The size of the data. - @inlinable // This is @inlinable as a trivial initializer. - public init(capacity: Int) { - _representation = _Representation(capacity: capacity) - } - - /// Initialize a `Data` with the specified count of zeroed bytes. - /// - /// - parameter count: The number of bytes the data initially contains. - @inlinable // This is @inlinable as a trivial initializer. - public init(count: Int) { - _representation = _Representation(count: count) - } - - /// Initialize an empty `Data`. - @inlinable // This is @inlinable as a trivial initializer. - public init() { - _representation = .empty - } - - - /// Initialize a `Data` without copying the bytes. - /// - /// If the result is mutated and is not a unique reference, then the `Data` will still follow copy-on-write semantics. In this case, the copy will use its own deallocator. Therefore, it is usually best to only use this initializer when you either enforce immutability with `let` or ensure that no other references to the underlying data are formed. - /// - parameter bytes: A pointer to the bytes. - /// - parameter count: The size of the bytes. - /// - parameter deallocator: Specifies the mechanism to free the indicated buffer, or `.none`. - @inlinable // This is @inlinable as a trivial initializer. - public init(bytesNoCopy bytes: UnsafeMutableRawPointer, count: Int, deallocator: Deallocator) { - let whichDeallocator = deallocator._deallocator - if count == 0 { - deallocator._deallocator(bytes, count) - _representation = .empty - } else { - _representation = _Representation(__DataStorage(bytes: bytes, length: count, copy: false, deallocator: whichDeallocator, offset: 0), count: count) - } - } - - /// Initialize a `Data` with the contents of a `URL`. - /// - /// - parameter url: The `URL` to read. - /// - parameter options: Options for the read operation. Default value is `[]`. - /// - throws: An error in the Cocoa domain, if `url` cannot be read. - @inlinable // This is @inlinable as a convenience initializer. - public init(contentsOf url: __shared URL, options: Data.ReadingOptions = []) throws { - let d = try NSData(contentsOf: url, options: ReadingOptions(rawValue: options.rawValue)) - self.init(referencing: d) - } - - /// Initialize a `Data` from a Base-64 encoded String using the given options. - /// - /// Returns nil when the input is not recognized as valid Base-64. - /// - parameter base64String: The string to parse. - /// - parameter options: Encoding options. Default value is `[]`. - @inlinable // This is @inlinable as a convenience initializer. - public init?(base64Encoded base64String: __shared String, options: Data.Base64DecodingOptions = []) { - if let d = NSData(base64Encoded: base64String, options: Base64DecodingOptions(rawValue: options.rawValue)) { - self.init(referencing: d) - } else { - return nil - } - } - - /// Initialize a `Data` from a Base-64, UTF-8 encoded `Data`. - /// - /// Returns nil when the input is not recognized as valid Base-64. - /// - /// - parameter base64Data: Base-64, UTF-8 encoded input data. - /// - parameter options: Decoding options. Default value is `[]`. - @inlinable // This is @inlinable as a convenience initializer. - public init?(base64Encoded base64Data: __shared Data, options: Data.Base64DecodingOptions = []) { - if let d = NSData(base64Encoded: base64Data, options: Base64DecodingOptions(rawValue: options.rawValue)) { - self.init(referencing: d) - } else { - return nil - } - } - - /// Initialize a `Data` by adopting a reference type. - /// - /// You can use this initializer to create a `struct Data` that wraps a `class NSData`. `struct Data` will use the `class NSData` for all operations. Other initializers (including casting using `as Data`) may choose to hold a reference or not, based on a what is the most efficient representation. - /// - /// If the resulting value is mutated, then `Data` will invoke the `mutableCopy()` function on the reference to copy the contents. You may customize the behavior of that function if you wish to return a specialized mutable subclass. - /// - /// - parameter reference: The instance of `NSData` that you wish to wrap. This instance will be copied by `struct Data`. - public init(referencing reference: __shared NSData) { - // This is not marked as inline because _providesConcreteBacking would need to be marked as usable from inline however that is a dynamic lookup in objc contexts. - let length = reference.length - if length == 0 { - _representation = .empty - } else { -#if DEPLOYMENT_RUNTIME_SWIFT - let providesConcreteBacking = reference._providesConcreteBacking() -#else - let providesConcreteBacking = (reference as AnyObject)._providesConcreteBacking?() ?? false -#endif - if providesConcreteBacking { - _representation = _Representation(__DataStorage(immutableReference: reference.copy() as! NSData, offset: 0), count: length) - } else { - _representation = _Representation(__DataStorage(customReference: reference.copy() as! NSData, offset: 0), count: length) - } - } - - } - - // slightly faster paths for common sequences - @inlinable // This is @inlinable as an important generic funnel point, despite being a non-trivial initializer. - public init(_ elements: S) where S.Element == UInt8 { - // If the sequence is already contiguous, access the underlying raw memory directly. - if let contiguous = elements as? ContiguousBytes { - _representation = contiguous.withUnsafeBytes { return _Representation($0) } - return - } - - // The sequence might still be able to provide direct access to typed memory. - // NOTE: It's safe to do this because we're already guarding on S's element as `UInt8`. This would not be safe on arbitrary sequences. - let representation = elements.withContiguousStorageIfAvailable { - _Representation(UnsafeRawBufferPointer($0)) - } - if let representation = representation { - _representation = representation - return - } - - // Copy as much as we can in one shot from the sequence. - let underestimatedCount = elements.underestimatedCount - _representation = _Representation(count: underestimatedCount) - var (iter, endIndex): (S.Iterator, Int) = _representation.withUnsafeMutableBytes { buffer in - let start = buffer.baseAddress!.assumingMemoryBound(to: UInt8.self) - let b = UnsafeMutableBufferPointer(start: start, count: buffer.count) - return elements._copyContents(initializing: b) - } - guard endIndex == _representation.count else { - // We can't trap here. We have to allow an underfilled buffer - // to emulate the previous implementation. - _representation.replaceSubrange(endIndex ..< _representation.endIndex, with: nil, count: 0) - return - } - - // Append the rest byte-wise, buffering through an InlineData. - var buffer = InlineData() - while let element = iter.next() { - buffer.append(byte: element) - if buffer.count == buffer.capacity { - buffer.withUnsafeBytes { _representation.append(contentsOf: $0) } - buffer.count = 0 - } - } - - // If we've still got bytes left in the buffer (i.e. the loop ended before we filled up the buffer and cleared it out), append them. - if buffer.count > 0 { - buffer.withUnsafeBytes { _representation.append(contentsOf: $0) } - buffer.count = 0 - } - } - - @available(swift, introduced: 4.2) - @available(swift, deprecated: 5, message: "use `init(_:)` instead") - public init(bytes elements: S) where S.Iterator.Element == UInt8 { - self.init(elements) - } - - @available(swift, obsoleted: 4.2) - public init(bytes: Array) { - self.init(bytes) - } - - @available(swift, obsoleted: 4.2) - public init(bytes: ArraySlice) { - self.init(bytes) - } - - @inlinable // This is @inlinable as a trivial initializer. - internal init(representation: _Representation) { - _representation = representation - } - - // ----------------------------------- - // MARK: - Properties and Functions - - @inlinable // This is @inlinable as trivially forwarding. - public mutating func reserveCapacity(_ minimumCapacity: Int) { - _representation.reserveCapacity(minimumCapacity) - } - - /// The number of bytes in the data. - @inlinable // This is @inlinable as trivially forwarding. - public var count: Int { - get { - return _representation.count - } - set(newValue) { - precondition(newValue >= 0, "count must not be negative") - _representation.count = newValue - } - } - - @inlinable // This is @inlinable as trivially computable. - public var regions: CollectionOfOne { - return CollectionOfOne(self) - } - - /// Access the bytes in the data. - /// - /// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure. - @available(swift, deprecated: 5, message: "use `withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R` instead") - public func withUnsafeBytes(_ body: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { - return try _representation.withUnsafeBytes { - return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafePointer(bitPattern: 0xBAD0)!) - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { - return try _representation.withUnsafeBytes(body) - } - - /// Mutate the bytes in the data. - /// - /// This function assumes that you are mutating the contents. - /// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure. - @available(swift, deprecated: 5, message: "use `withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R` instead") - public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { - return try _representation.withUnsafeMutableBytes { - return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafeMutablePointer(bitPattern: 0xBAD0)!) - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { - return try _representation.withUnsafeMutableBytes(body) - } - - // MARK: - - // MARK: Copy Bytes - - /// Copy the contents of the data to a pointer. - /// - /// - parameter pointer: A pointer to the buffer you wish to copy the bytes into. - /// - parameter count: The number of bytes to copy. - /// - warning: This method does not verify that the contents at pointer have enough space to hold `count` bytes. - @inlinable // This is @inlinable as trivially forwarding. - public func copyBytes(to pointer: UnsafeMutablePointer, count: Int) { - precondition(count >= 0, "count of bytes to copy must not be negative") - if count == 0 { return } - _copyBytesHelper(to: UnsafeMutableRawPointer(pointer), from: startIndex..<(startIndex + count)) - } - - @inlinable // This is @inlinable as trivially forwarding. - internal func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: Range) { - if range.isEmpty { return } - _representation.copyBytes(to: pointer, from: range) - } - - /// Copy a subset of the contents of the data to a pointer. - /// - /// - parameter pointer: A pointer to the buffer you wish to copy the bytes into. - /// - parameter range: The range in the `Data` to copy. - /// - warning: This method does not verify that the contents at pointer have enough space to hold the required number of bytes. - @inlinable // This is @inlinable as trivially forwarding. - public func copyBytes(to pointer: UnsafeMutablePointer, from range: Range) { - _copyBytesHelper(to: pointer, from: range) - } - - // Copy the contents of the data into a buffer. - /// - /// This function copies the bytes in `range` from the data into the buffer. If the count of the `range` is greater than `MemoryLayout.stride * buffer.count` then the first N bytes will be copied into the buffer. - /// - precondition: The range must be within the bounds of the data. Otherwise `fatalError` is called. - /// - parameter buffer: A buffer to copy the data into. - /// - parameter range: A range in the data to copy into the buffer. If the range is empty, this function will return 0 without copying anything. If the range is nil, as much data as will fit into `buffer` is copied. - /// - returns: Number of bytes copied into the destination buffer. - @inlinable // This is @inlinable as generic and reasonably small. - public func copyBytes(to buffer: UnsafeMutableBufferPointer, from range: Range? = nil) -> Int { - let cnt = count - guard cnt > 0 else { return 0 } - - let copyRange : Range - if let r = range { - guard !r.isEmpty else { return 0 } - copyRange = r.lowerBound..<(r.lowerBound + Swift.min(buffer.count * MemoryLayout.stride, r.upperBound - r.lowerBound)) - } else { - copyRange = 0...stride, cnt) - } - - guard !copyRange.isEmpty else { return 0 } - - _copyBytesHelper(to: buffer.baseAddress!, from: copyRange) - return copyRange.upperBound - copyRange.lowerBound - } - - // MARK: - -#if !DEPLOYMENT_RUNTIME_SWIFT - private func _shouldUseNonAtomicWriteReimplementation(options: Data.WritingOptions = []) -> Bool { - - // Avoid a crash that happens on OS X 10.11.x and iOS 9.x or before when writing a bridged Data non-atomically with Foundation's standard write() implementation. - if !options.contains(.atomic) { - #if os(macOS) - return NSFoundationVersionNumber <= Double(NSFoundationVersionNumber10_11_Max) - #else - return NSFoundationVersionNumber <= Double(NSFoundationVersionNumber_iOS_9_x_Max) - #endif - } else { - return false - } - } -#endif - - /// Write the contents of the `Data` to a location. - /// - /// - parameter url: The location to write the data into. - /// - parameter options: Options for writing the data. Default value is `[]`. - /// - throws: An error in the Cocoa domain, if there is an error writing to the `URL`. - public func write(to url: URL, options: Data.WritingOptions = []) throws { - // this should not be marked as inline since in objc contexts we correct atomicity via _shouldUseNonAtomicWriteReimplementation - try _representation.withInteriorPointerReference { -#if DEPLOYMENT_RUNTIME_SWIFT - try $0.write(to: url, options: WritingOptions(rawValue: options.rawValue)) -#else - if _shouldUseNonAtomicWriteReimplementation(options: options) { - var error: NSError? = nil - guard __NSDataWriteToURL($0, url, options, &error) else { throw error! } - } else { - try $0.write(to: url, options: options) - } -#endif - } - } - - // MARK: - - - /// Find the given `Data` in the content of this `Data`. - /// - /// - parameter dataToFind: The data to be searched for. - /// - parameter options: Options for the search. Default value is `[]`. - /// - parameter range: The range of this data in which to perform the search. Default value is `nil`, which means the entire content of this data. - /// - returns: A `Range` specifying the location of the found data, or nil if a match could not be found. - /// - precondition: `range` must be in the bounds of the Data. - public func range(of dataToFind: Data, options: Data.SearchOptions = [], in range: Range? = nil) -> Range? { - let nsRange : NSRange - if let r = range { - nsRange = NSRange(location: r.lowerBound - startIndex, length: r.upperBound - r.lowerBound) - } else { - nsRange = NSRange(location: 0, length: count) - } - let result = _representation.withInteriorPointerReference { - $0.range(of: dataToFind, options: options, in: nsRange) - } - if result.location == NSNotFound { - return nil - } - return (result.location + startIndex)..<((result.location + startIndex) + result.length) - } - - /// Enumerate the contents of the data. - /// - /// In some cases, (for example, a `Data` backed by a `dispatch_data_t`, the bytes may be stored discontiguously. In those cases, this function invokes the closure for each contiguous region of bytes. - /// - parameter block: The closure to invoke for each region of data. You may stop the enumeration by setting the `stop` parameter to `true`. - @available(swift, deprecated: 5, message: "use `regions` or `for-in` instead") - public func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Index, _ stop: inout Bool) -> Void) { - _representation.enumerateBytes(block) - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - internal mutating func _append(_ buffer : UnsafeBufferPointer) { - if buffer.isEmpty { return } - _representation.append(contentsOf: UnsafeRawBufferPointer(buffer)) - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - public mutating func append(_ bytes: UnsafePointer, count: Int) { - if count == 0 { return } - _append(UnsafeBufferPointer(start: bytes, count: count)) - } - - public mutating func append(_ other: Data) { - guard !other.isEmpty else { return } - other.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - _representation.append(contentsOf: buffer) - } - } - - /// Append a buffer of bytes to the data. - /// - /// - parameter buffer: The buffer of bytes to append. The size is calculated from `SourceType` and `buffer.count`. - @inlinable // This is @inlinable as a generic, trivially forwarding function. - public mutating func append(_ buffer : UnsafeBufferPointer) { - _append(buffer) - } - - @inlinable // This is @inlinable as trivially forwarding. - public mutating func append(contentsOf bytes: [UInt8]) { - bytes.withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> Void in - _append(buffer) - } - } - - @inlinable // This is @inlinable as an important generic funnel point, despite being non-trivial. - public mutating func append(contentsOf elements: S) where S.Element == Element { - // If the sequence is already contiguous, access the underlying raw memory directly. - if let contiguous = elements as? ContiguousBytes { - contiguous.withUnsafeBytes { - _representation.append(contentsOf: $0) - } - - return - } - - // The sequence might still be able to provide direct access to typed memory. - // NOTE: It's safe to do this because we're already guarding on S's element as `UInt8`. This would not be safe on arbitrary sequences. - let appended: Void? = elements.withContiguousStorageIfAvailable { - _representation.append(contentsOf: UnsafeRawBufferPointer($0)) - } - guard appended == nil else { return } - - // The sequence is really not contiguous. - // Copy as much as we can in one shot. - let underestimatedCount = elements.underestimatedCount - let originalCount = _representation.count - resetBytes(in: self.endIndex ..< self.endIndex + underestimatedCount) - var (iter, copiedCount): (S.Iterator, Int) = _representation.withUnsafeMutableBytes { buffer in - assert(buffer.count == originalCount + underestimatedCount) - let start = buffer.baseAddress!.assumingMemoryBound(to: UInt8.self) + originalCount - let b = UnsafeMutableBufferPointer(start: start, count: buffer.count - originalCount) - return elements._copyContents(initializing: b) - } - guard copiedCount == underestimatedCount else { - // We can't trap here. We have to allow an underfilled buffer - // to emulate the previous implementation. - _representation.replaceSubrange(startIndex + originalCount + copiedCount ..< endIndex, with: nil, count: 0) - return - } - - // Append the rest byte-wise, buffering through an InlineData. - var buffer = InlineData() - while let element = iter.next() { - buffer.append(byte: element) - if buffer.count == buffer.capacity { - buffer.withUnsafeBytes { _representation.append(contentsOf: $0) } - buffer.count = 0 - } - } - - // If we've still got bytes left in the buffer (i.e. the loop ended before we filled up the buffer and cleared it out), append them. - if buffer.count > 0 { - buffer.withUnsafeBytes { _representation.append(contentsOf: $0) } - buffer.count = 0 - } - } - - // MARK: - - - /// Set a region of the data to `0`. - /// - /// If `range` exceeds the bounds of the data, then the data is resized to fit. - /// - parameter range: The range in the data to set to `0`. - @inlinable // This is @inlinable as trivially forwarding. - public mutating func resetBytes(in range: Range) { - // it is worth noting that the range here may be out of bounds of the Data itself (which triggers a growth) - precondition(range.lowerBound >= 0, "Ranges must not be negative bounds") - precondition(range.upperBound >= 0, "Ranges must not be negative bounds") - _representation.resetBytes(in: range) - } - - /// Replace a region of bytes in the data with new data. - /// - /// This will resize the data if required, to fit the entire contents of `data`. - /// - /// - precondition: The bounds of `subrange` must be valid indices of the collection. - /// - parameter subrange: The range in the data to replace. If `subrange.lowerBound == data.count && subrange.count == 0` then this operation is an append. - /// - parameter data: The replacement data. - @inlinable // This is @inlinable as trivially forwarding. - public mutating func replaceSubrange(_ subrange: Range, with data: Data) { - data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - _representation.replaceSubrange(subrange, with: buffer.baseAddress, count: buffer.count) - } - } - - /// Replace a region of bytes in the data with new bytes from a buffer. - /// - /// This will resize the data if required, to fit the entire contents of `buffer`. - /// - /// - precondition: The bounds of `subrange` must be valid indices of the collection. - /// - parameter subrange: The range in the data to replace. - /// - parameter buffer: The replacement bytes. - @inlinable // This is @inlinable as a generic, trivially forwarding function. - public mutating func replaceSubrange(_ subrange: Range, with buffer: UnsafeBufferPointer) { - guard !buffer.isEmpty else { return } - replaceSubrange(subrange, with: buffer.baseAddress!, count: buffer.count * MemoryLayout.stride) - } - - /// Replace a region of bytes in the data with new bytes from a collection. - /// - /// This will resize the data if required, to fit the entire contents of `newElements`. - /// - /// - precondition: The bounds of `subrange` must be valid indices of the collection. - /// - parameter subrange: The range in the data to replace. - /// - parameter newElements: The replacement bytes. - @inlinable // This is @inlinable as generic and reasonably small. - public mutating func replaceSubrange(_ subrange: Range, with newElements: ByteCollection) where ByteCollection.Iterator.Element == Data.Iterator.Element { - // If the collection is already contiguous, access the underlying raw memory directly. - if let contiguous = newElements as? ContiguousBytes { - contiguous.withUnsafeBytes { buffer in - _representation.replaceSubrange(subrange, with: buffer.baseAddress, count: buffer.count) - } - return - } - // The collection might still be able to provide direct access to typed memory. - // NOTE: It's safe to do this because we're already guarding on ByteCollection's element as `UInt8`. This would not be safe on arbitrary collections. - let replaced: Void? = newElements.withContiguousStorageIfAvailable { buffer in - _representation.replaceSubrange(subrange, with: buffer.baseAddress, count: buffer.count) - } - guard replaced == nil else { return } - - let totalCount = Int(newElements.count) - _withStackOrHeapBuffer(capacity: totalCount) { buffer in - var (iterator, index) = newElements._copyContents(initializing: buffer) - precondition(index == buffer.endIndex, "Collection has less elements than its count") - precondition(iterator.next() == nil, "Collection has more elements than its count") - _representation.replaceSubrange(subrange, with: buffer.baseAddress, count: totalCount) - } - } - - @inlinable // This is @inlinable as trivially forwarding. - public mutating func replaceSubrange(_ subrange: Range, with bytes: UnsafeRawPointer, count cnt: Int) { - _representation.replaceSubrange(subrange, with: bytes, count: cnt) - } - - /// Return a new copy of the data in a specified range. - /// - /// - parameter range: The range to copy. - public func subdata(in range: Range) -> Data { - if isEmpty || range.upperBound - range.lowerBound == 0 { - return Data() - } - let slice = self[range] - - return slice.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> Data in - return Data(bytes: buffer.baseAddress!, count: buffer.count) - } - } - - // MARK: - - // - - /// Returns a Base-64 encoded string. - /// - /// - parameter options: The options to use for the encoding. Default value is `[]`. - /// - returns: The Base-64 encoded string. - @inlinable // This is @inlinable as trivially forwarding. - public func base64EncodedString(options: Data.Base64EncodingOptions = []) -> String { - return _representation.withInteriorPointerReference { - return $0.base64EncodedString(options: options) - } - } - - /// Returns a Base-64 encoded `Data`. - /// - /// - parameter options: The options to use for the encoding. Default value is `[]`. - /// - returns: The Base-64 encoded data. - @inlinable // This is @inlinable as trivially forwarding. - public func base64EncodedData(options: Data.Base64EncodingOptions = []) -> Data { - return _representation.withInteriorPointerReference { - return $0.base64EncodedData(options: options) - } - } - - // MARK: - - // - - /// The hash value for the data. - @inline(never) // This is not inlinable as emission into clients could cause cross-module inconsistencies if they are not all recompiled together. - public func hash(into hasher: inout Hasher) { - _representation.hash(into: &hasher) - } - - public func advanced(by amount: Int) -> Data { - let length = count - amount - precondition(length > 0) - return withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> Data in - return Data(bytes: ptr.baseAddress!.advanced(by: amount), count: length) - } - } - - // MARK: - - - // MARK: - - // MARK: Index and Subscript - - /// Sets or returns the byte at the specified index. - @inlinable // This is @inlinable as trivially forwarding. - public subscript(index: Index) -> UInt8 { - get { - return _representation[index] - } - set(newValue) { - _representation[index] = newValue - } - } - - @inlinable // This is @inlinable as trivially forwarding. - public subscript(bounds: Range) -> Data { - get { - return _representation[bounds] - } - set { - replaceSubrange(bounds, with: newValue) - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - public subscript(_ rangeExpression: R) -> Data - where R.Bound: FixedWidthInteger { - get { - let lower = R.Bound(startIndex) - let upper = R.Bound(endIndex) - let range = rangeExpression.relative(to: lower.. = start.. = start.. Index { - return i - 1 - } - - @inlinable // This is @inlinable as trivially computable. - public func index(after i: Index) -> Index { - return i + 1 - } - - @inlinable // This is @inlinable as trivially computable. - public var indices: Range { - get { - return startIndex..) -> (Iterator, UnsafeMutableBufferPointer.Index) { - guard !isEmpty else { return (makeIterator(), buffer.startIndex) } - let cnt = Swift.min(count, buffer.count) - - withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in - _ = memcpy(UnsafeMutableRawPointer(buffer.baseAddress), bytes.baseAddress, cnt) - } - - return (Iterator(self, at: startIndex + cnt), buffer.index(buffer.startIndex, offsetBy: cnt)) - } - - /// An iterator over the contents of the data. - /// - /// The iterator will increment byte-by-byte. - @inlinable // This is @inlinable as trivially computable. - public func makeIterator() -> Data.Iterator { - return Iterator(self, at: startIndex) - } - - public struct Iterator : IteratorProtocol { - @usableFromInline - internal typealias Buffer = ( - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) - - @usableFromInline internal let _data: Data - @usableFromInline internal var _buffer: Buffer - @usableFromInline internal var _idx: Data.Index - @usableFromInline internal let _endIdx: Data.Index - - @usableFromInline // This is @usableFromInline as a non-trivial initializer. - internal init(_ data: Data, at loc: Data.Index) { - // The let vars prevent this from being marked as @inlinable - _data = data - _buffer = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) - _idx = loc - _endIdx = data.endIndex - - let bufferSize = MemoryLayout.size - Swift.withUnsafeMutableBytes(of: &_buffer) { - let ptr = $0.bindMemory(to: UInt8.self) - let bufferIdx = (loc - data.startIndex) % bufferSize - data.copyBytes(to: ptr, from: (loc - bufferIdx)..<(data.endIndex - (loc - bufferIdx) > bufferSize ? (loc - bufferIdx) + bufferSize : data.endIndex)) - } - } - - public mutating func next() -> UInt8? { - let idx = _idx - let bufferSize = MemoryLayout.size - - guard idx < _endIdx else { return nil } - _idx += 1 - - let bufferIdx = (idx - _data.startIndex) % bufferSize - - - if bufferIdx == 0 { - var buffer = _buffer - Swift.withUnsafeMutableBytes(of: &buffer) { - let ptr = $0.bindMemory(to: UInt8.self) - // populate the buffer - _data.copyBytes(to: ptr, from: idx..<(_endIdx - idx > bufferSize ? idx + bufferSize : _endIdx)) - } - _buffer = buffer - } - - return Swift.withUnsafeMutableBytes(of: &_buffer) { - let ptr = $0.bindMemory(to: UInt8.self) - return ptr[bufferIdx] - } - } - } - - // MARK: - - // - - @available(*, unavailable, renamed: "count") - public var length: Int { - get { fatalError() } - set { fatalError() } - } - - @available(*, unavailable, message: "use withUnsafeBytes instead") - public var bytes: UnsafeRawPointer { fatalError() } - - @available(*, unavailable, message: "use withUnsafeMutableBytes instead") - public var mutableBytes: UnsafeMutableRawPointer { fatalError() } - - /// Returns `true` if the two `Data` arguments are equal. - @inlinable // This is @inlinable as emission into clients is safe -- the concept of equality on Data will not change. - public static func ==(d1 : Data, d2 : Data) -> Bool { - let length1 = d1.count - if length1 != d2.count { - return false - } - if length1 > 0 { - return d1.withUnsafeBytes { (b1: UnsafeRawBufferPointer) in - return d2.withUnsafeBytes { (b2: UnsafeRawBufferPointer) in - return memcmp(b1.baseAddress!, b2.baseAddress!, b2.count) == 0 - } - } - } - return true - } -} - - -extension Data : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - /// A human-readable description for the data. - public var description: String { - return "\(self.count) bytes" - } - - /// A human-readable debug description for the data. - public var debugDescription: String { - return self.description - } - - public var customMirror: Mirror { - let nBytes = self.count - var children: [(label: String?, value: Any)] = [] - children.append((label: "count", value: nBytes)) - - self.withUnsafeBytes { (bytes : UnsafeRawBufferPointer) in - children.append((label: "pointer", value: bytes.baseAddress!)) - } - - // Minimal size data is output as an array - if nBytes < 64 { - children.append((label: "bytes", value: Array(self[startIndex..(_ buffer: UnsafeMutablePointerVoid, length: Int) { } - - @available(*, unavailable, renamed: "copyBytes(to:from:)") - public func getBytes(_ buffer: UnsafeMutablePointerVoid, range: NSRange) { } -} - -/// Provides bridging functionality for struct Data to class NSData and vice-versa. - -extension Data : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSData { - return _representation.bridgedReference() - } - - public static func _forceBridgeFromObjectiveC(_ input: NSData, result: inout Data?) { - // We must copy the input because it might be mutable; just like storing a value type in ObjC - result = Data(referencing: input) - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: NSData, result: inout Data?) -> Bool { - // We must copy the input because it might be mutable; just like storing a value type in ObjC - result = Data(referencing: input) - return true - } - -// @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSData?) -> Data { - guard let src = source else { return Data() } - return Data(referencing: src) - } -} - -extension NSData : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(Data._unconditionallyBridgeFromObjectiveC(self)) - } -} - -extension Data : Codable { - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - - // It's more efficient to pre-allocate the buffer if we can. - if let count = container.count { - self.init(count: count) - - // Loop only until count, not while !container.isAtEnd, in case count is underestimated (this is misbehavior) and we haven't allocated enough space. - // We don't want to write past the end of what we allocated. - for i in 0 ..< count { - let byte = try container.decode(UInt8.self) - self[i] = byte - } - } else { - self.init() - } - - while !container.isAtEnd { - var byte = try container.decode(UInt8.self) - self.append(&byte, count: 1) - } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - try container.encode(contentsOf: buffer) - } - } -} diff --git a/stdlib/public/Darwin/Foundation/DataProtocol.swift b/stdlib/public/Darwin/Foundation/DataProtocol.swift deleted file mode 100644 index 4a920511435bf..0000000000000 --- a/stdlib/public/Darwin/Foundation/DataProtocol.swift +++ /dev/null @@ -1,295 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2018 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 -// -//===----------------------------------------------------------------------===// - -#if os(macOS) || os(iOS) -import Darwin -#elseif os(Linux) -import Glibc -#endif - -//===--- DataProtocol -----------------------------------------------------===// - -public protocol DataProtocol : RandomAccessCollection where Element == UInt8, SubSequence : DataProtocol { - // FIXME: Remove in favor of opaque type on `regions`. - associatedtype Regions: BidirectionalCollection where Regions.Element : DataProtocol & ContiguousBytes, Regions.Element.SubSequence : ContiguousBytes - - /// A `BidirectionalCollection` of `DataProtocol` elements which compose a - /// discontiguous buffer of memory. Each region is a contiguous buffer of - /// bytes. - /// - /// The sum of the lengths of the associated regions must equal `self.count` - /// (such that iterating `regions` and iterating `self` produces the same - /// sequence of indices in the same number of index advancements). - var regions: Regions { get } - - /// Returns the first found range of the given data buffer. - /// - /// A default implementation is given in terms of `self.regions`. - func firstRange(of: D, in: R) -> Range? where R.Bound == Index - - /// Returns the last found range of the given data buffer. - /// - /// A default implementation is given in terms of `self.regions`. - func lastRange(of: D, in: R) -> Range? where R.Bound == Index - - /// Copies `count` bytes from the start of the buffer to the destination - /// buffer. - /// - /// A default implementation is given in terms of `copyBytes(to:from:)`. - @discardableResult - func copyBytes(to: UnsafeMutableRawBufferPointer, count: Int) -> Int - - /// Copies `count` bytes from the start of the buffer to the destination - /// buffer. - /// - /// A default implementation is given in terms of `copyBytes(to:from:)`. - @discardableResult - func copyBytes(to: UnsafeMutableBufferPointer, count: Int) -> Int - - /// Copies the bytes from the given range to the destination buffer. - /// - /// A default implementation is given in terms of `self.regions`. - @discardableResult - func copyBytes(to: UnsafeMutableRawBufferPointer, from: R) -> Int where R.Bound == Index - - /// Copies the bytes from the given range to the destination buffer. - /// - /// A default implementation is given in terms of `self.regions`. - @discardableResult - func copyBytes(to: UnsafeMutableBufferPointer, from: R) -> Int where R.Bound == Index -} - -//===--- MutableDataProtocol ----------------------------------------------===// - -public protocol MutableDataProtocol : DataProtocol, MutableCollection, RangeReplaceableCollection { - /// Replaces the contents of the buffer at the given range with zeroes. - /// - /// A default implementation is given in terms of - /// `replaceSubrange(_:with:)`. - mutating func resetBytes(in range: R) where R.Bound == Index -} - -//===--- DataProtocol Extensions ------------------------------------------===// - -extension DataProtocol { - public func firstRange(of data: D) -> Range? { - return self.firstRange(of: data, in: self.startIndex ..< self.endIndex) - } - - public func lastRange(of data: D) -> Range? { - return self.lastRange(of: data, in: self.startIndex ..< self.endIndex) - } - - @discardableResult - public func copyBytes(to ptr: UnsafeMutableRawBufferPointer) -> Int { - return copyBytes(to: ptr, from: self.startIndex ..< self.endIndex) - } - - @discardableResult - public func copyBytes(to ptr: UnsafeMutableBufferPointer) -> Int { - return copyBytes(to: ptr, from: self.startIndex ..< self.endIndex) - } - - @discardableResult - public func copyBytes(to ptr: UnsafeMutableRawBufferPointer, count: Int) -> Int { - return copyBytes(to: ptr, from: self.startIndex ..< self.index(self.startIndex, offsetBy: count)) - } - - @discardableResult - public func copyBytes(to ptr: UnsafeMutableBufferPointer, count: Int) -> Int { - return copyBytes(to: ptr, from: self.startIndex ..< self.index(self.startIndex, offsetBy: count)) - } - - @discardableResult - public func copyBytes(to ptr: UnsafeMutableRawBufferPointer, from range: R) -> Int where R.Bound == Index { - precondition(ptr.baseAddress != nil) - - let concreteRange = range.relative(to: self) - let slice = self[concreteRange] - - // The type isn't contiguous, so we need to copy one region at a time. - var offset = 0 - let rangeCount = distance(from: concreteRange.lowerBound, to: concreteRange.upperBound) - var amountToCopy = Swift.min(ptr.count, rangeCount) - for region in slice.regions { - guard amountToCopy > 0 else { - break - } - - region.withUnsafeBytes { buffer in - let offsetPtr = UnsafeMutableRawBufferPointer(rebasing: ptr[offset...]) - let buf = UnsafeRawBufferPointer(start: buffer.baseAddress, count: Swift.min(buffer.count, amountToCopy)) - offsetPtr.copyMemory(from: buf) - offset += buf.count - amountToCopy -= buf.count - } - } - - return offset - } - - @discardableResult - public func copyBytes(to ptr: UnsafeMutableBufferPointer, from range: R) -> Int where R.Bound == Index { - return self.copyBytes(to: UnsafeMutableRawBufferPointer(start: ptr.baseAddress, count: ptr.count * MemoryLayout.stride), from: range) - } - - private func matches(_ data: D, from index: Index) -> Bool { - var haystackIndex = index - var needleIndex = data.startIndex - - while true { - guard self[haystackIndex] == data[needleIndex] else { return false } - - haystackIndex = self.index(after: haystackIndex) - needleIndex = data.index(after: needleIndex) - if needleIndex == data.endIndex { - // i.e. needle is found. - return true - } else if haystackIndex == endIndex { - return false - } - } - } - - public func firstRange(of data: D, in range: R) -> Range? where R.Bound == Index { - let r = range.relative(to: self) - let length = data.count - - if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) { - return nil - } - - var position = r.lowerBound - while position < r.upperBound && distance(from: position, to: r.upperBound) >= length { - if matches(data, from: position) { - return position..(of data: D, in range: R) -> Range? where R.Bound == Index { - let r = range.relative(to: self) - let length = data.count - - if length == 0 || length > distance(from: r.lowerBound, to: r.upperBound) { - return nil - } - - var position = index(r.upperBound, offsetBy: -length) - while position >= r.lowerBound { - if matches(data, from: position) { - return position..(to ptr: UnsafeMutableBufferPointer, from range: R) where R.Bound == Index { - precondition(ptr.baseAddress != nil) - - let concreteRange = range.relative(to: self) - withUnsafeBytes { fullBuffer in - let adv = distance(from: startIndex, to: concreteRange.lowerBound) - let delta = distance(from: concreteRange.lowerBound, to: concreteRange.upperBound) - memcpy(ptr.baseAddress!, fullBuffer.baseAddress?.advanced(by: adv), delta) - } - } -} - -//===--- MutableDataProtocol Extensions -----------------------------------===// - -extension MutableDataProtocol { - public mutating func resetBytes(in range: R) where R.Bound == Index { - let r = range.relative(to: self) - let count = distance(from: r.lowerBound, to: r.upperBound) - replaceSubrange(r, with: repeatElement(UInt8(0), count: count)) - } -} - -//===--- DataProtocol Conditional Conformances ----------------------------===// - -extension Slice : DataProtocol where Base : DataProtocol { - public typealias Regions = [Base.Regions.Element.SubSequence] - - public var regions: [Base.Regions.Element.SubSequence] { - let sliceLowerBound = startIndex - let sliceUpperBound = endIndex - var regionUpperBound = base.startIndex - - return base.regions.compactMap { (region) -> Base.Regions.Element.SubSequence? in - let regionLowerBound = regionUpperBound - regionUpperBound = base.index(regionUpperBound, offsetBy: region.count) - - /* - [------ Region ------] - [--- Slice ---] => - - OR - - [------ Region ------] - <= [--- Slice ---] - */ - if sliceLowerBound >= regionLowerBound && sliceUpperBound <= regionUpperBound { - let regionRelativeSliceLowerBound = region.index(region.startIndex, offsetBy: base.distance(from: regionLowerBound, to: sliceLowerBound)) - let regionRelativeSliceUpperBound = region.index(region.startIndex, offsetBy: base.distance(from: regionLowerBound, to: sliceUpperBound)) - return region[regionRelativeSliceLowerBound.. - [------ Slice ------] - - OR - - <= [--- Region ---] - [------ Slice ------] - */ - if regionLowerBound >= sliceLowerBound && regionUpperBound <= sliceUpperBound { - return region[region.startIndex..= regionLowerBound && sliceLowerBound <= regionUpperBound { - let regionRelativeSliceLowerBound = region.index(region.startIndex, offsetBy: base.distance(from: regionLowerBound, to: sliceLowerBound)) - return region[regionRelativeSliceLowerBound..= sliceLowerBound && regionLowerBound <= sliceUpperBound { - let regionRelativeSliceUpperBound = region.index(region.startIndex, offsetBy: base.distance(from: regionLowerBound, to: sliceUpperBound)) - return region[region.startIndex.. -#import - -#include "swift/Runtime/Config.h" - -#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR -static int __NSFileProtectionClassForOptions(NSUInteger options) { - int result; - switch (options & NSDataWritingFileProtectionMask) { - case NSDataWritingFileProtectionComplete: // Class A - result = 1; - break; - case NSDataWritingFileProtectionCompleteUnlessOpen: // Class B - result = 2; - break; - case NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication: // Class C - result = 3; - break; - case NSDataWritingFileProtectionNone: // Class D - result = 4; - break; - default: - result = 0; - break; - } - return result; -} -#endif - -static int32_t _NSOpenFileDescriptor(const char *path, NSInteger flags, int protectionClass, NSInteger mode) { - int fd = -1; - if (protectionClass != 0) { - fd = open_dprotected_np(path, flags, protectionClass, 0, mode); - } else { - fd = open(path, flags, mode); - } - return fd; -} - -static NSInteger _NSWriteToFileDescriptor(int32_t fd, const void *buffer, NSUInteger length) { - size_t preferredChunkSize = (size_t)length; - size_t numBytesRemaining = (size_t)length; - while (numBytesRemaining > 0UL) { - size_t numBytesRequested = (preferredChunkSize < (1LL << 31)) ? preferredChunkSize : ((1LL << 31) - 1); - if (numBytesRequested > numBytesRemaining) numBytesRequested = numBytesRemaining; - ssize_t numBytesWritten; - do { - numBytesWritten = write(fd, buffer, numBytesRequested); - } while (numBytesWritten < 0L && errno == EINTR); - if (numBytesWritten < 0L) { - return -1; - } else if (numBytesWritten == 0L) { - break; - } else { - numBytesRemaining -= numBytesWritten; - if ((size_t)numBytesWritten < numBytesRequested) break; - buffer = (char *)buffer + numBytesWritten; - } - } - return length - numBytesRemaining; -} - -static NSError *_NSErrorWithFilePath(NSInteger code, id pathOrURL) { - NSString *key = [pathOrURL isKindOfClass:[NSURL self]] ? NSURLErrorKey : NSFilePathErrorKey; - return [NSError errorWithDomain:NSCocoaErrorDomain code:code userInfo:[NSDictionary dictionaryWithObjectsAndKeys:pathOrURL, key, nil]]; -} - -static NSError *_NSErrorWithFilePathAndErrno(NSInteger posixErrno, id pathOrURL, BOOL reading) { - NSInteger code; - if (reading) { - switch (posixErrno) { - case EFBIG: code = NSFileReadTooLargeError; break; - case ENOENT: code = NSFileReadNoSuchFileError; break; - case EPERM: // fallthrough - case EACCES: code = NSFileReadNoPermissionError; break; - case ENAMETOOLONG: code = NSFileReadInvalidFileNameError; break; - default: code = NSFileReadUnknownError; break; - } - } else { - switch (posixErrno) { - case ENOENT: code = NSFileNoSuchFileError; break; - case EPERM: // fallthrough - case EACCES: code = NSFileWriteNoPermissionError; break; - case ENAMETOOLONG: code = NSFileWriteInvalidFileNameError; break; -#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED - case EDQUOT: -#endif - case ENOSPC: code = NSFileWriteOutOfSpaceError; break; - case EROFS: code = NSFileWriteVolumeReadOnlyError; break; - case EEXIST: code = NSFileWriteFileExistsError; break; - default: code = NSFileWriteUnknownError; break; - } - } - - NSString *key = [pathOrURL isKindOfClass:[NSURL self]] ? NSURLErrorKey : NSFilePathErrorKey; - NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:pathOrURL, key, [NSError errorWithDomain:NSPOSIXErrorDomain code:posixErrno userInfo:nil], NSUnderlyingErrorKey, nil]; - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:code userInfo:userInfo]; - [userInfo release]; - return error; -} - -SWIFT_RUNTIME_STDLIB_INTERNAL -BOOL __NSDataWriteToURL(NSData * _Nonnull data NS_RELEASES_ARGUMENT, NSURL * _Nonnull url NS_RELEASES_ARGUMENT, NSDataWritingOptions writingOptions, NSError **errorPtr) { - assert((writingOptions & NSDataWritingAtomic) == 0); - - NSString *path = url.path; - char cpath[1026]; - - if (![path getFileSystemRepresentation:cpath maxLength:1024]) { - if (errorPtr) *errorPtr = _NSErrorWithFilePath(NSFileWriteInvalidFileNameError, path); - return NO; - } - - int protectionClass = 0; -#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR - protectionClass = __NSFileProtectionClassForOptions(writingOptions); -#endif - - int flags = O_WRONLY|O_CREAT|O_TRUNC; - if (writingOptions & NSDataWritingWithoutOverwriting) { - flags |= O_EXCL; - } - int32_t fd = _NSOpenFileDescriptor(cpath, flags, protectionClass, 0666); - if (fd < 0) { - if (errorPtr) *errorPtr = _NSErrorWithFilePathAndErrno(errno, path, NO); - return NO; - } - - __block BOOL writingFailed = NO; - __block int32_t saveerr = 0; - NSUInteger dataLength = [data length]; - [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { - NSUInteger length = byteRange.length; - BOOL success = NO; - if (length > 0) { - NSInteger writtenLength = _NSWriteToFileDescriptor(fd, bytes, length); - success = writtenLength > 0 && (NSUInteger)writtenLength == length; - } else { - success = YES; // Writing nothing always succeeds. - } - if (!success) { - saveerr = errno; - writingFailed = YES; - *stop = YES; - } - }]; - if (dataLength && !writingFailed) { - if (fsync(fd) < 0) { - writingFailed = YES; - saveerr = errno; - } - } - if (writingFailed) { - close(fd); - errno = (saveerr); - if (errorPtr) { - *errorPtr = _NSErrorWithFilePathAndErrno(errno, path, NO); - } - return NO; - } - close(fd); - return YES; -} diff --git a/stdlib/public/Darwin/Foundation/Date.swift b/stdlib/public/Darwin/Foundation/Date.swift deleted file mode 100644 index 0f0d5967383dd..0000000000000 --- a/stdlib/public/Darwin/Foundation/Date.swift +++ /dev/null @@ -1,297 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import CoreFoundation -@_implementationOnly import _SwiftCoreFoundationOverlayShims - -/** - `Date` represents a single point in time. - - A `Date` is independent of a particular calendar or time zone. To represent a `Date` to a user, you must interpret it in the context of a `Calendar`. -*/ -public struct Date : ReferenceConvertible, Comparable, Equatable { - public typealias ReferenceType = NSDate - - fileprivate var _time : TimeInterval - - /// The number of seconds from 1 January 1970 to the reference date, 1 January 2001. - public static let timeIntervalBetween1970AndReferenceDate : TimeInterval = 978307200.0 - - /// The interval between 00:00:00 UTC on 1 January 2001 and the current date and time. - public static var timeIntervalSinceReferenceDate : TimeInterval { - return CFAbsoluteTimeGetCurrent() - } - - /// Returns a `Date` initialized to the current date and time. - public init() { - _time = CFAbsoluteTimeGetCurrent() - } - - /// Returns a `Date` initialized relative to the current date and time by a given number of seconds. - public init(timeIntervalSinceNow: TimeInterval) { - self.init(timeIntervalSinceReferenceDate: timeIntervalSinceNow + CFAbsoluteTimeGetCurrent()) - } - - /// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 1970 by a given number of seconds. - public init(timeIntervalSince1970: TimeInterval) { - self.init(timeIntervalSinceReferenceDate: timeIntervalSince1970 - Date.timeIntervalBetween1970AndReferenceDate) - } - - /** - Returns a `Date` initialized relative to another given date by a given number of seconds. - - - Parameter timeInterval: The number of seconds to add to `date`. A negative value means the receiver will be earlier than `date`. - - Parameter date: The reference date. - */ - public init(timeInterval: TimeInterval, since date: Date) { - self.init(timeIntervalSinceReferenceDate: date.timeIntervalSinceReferenceDate + timeInterval) - } - - /// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 2001 by a given number of seconds. - public init(timeIntervalSinceReferenceDate ti: TimeInterval) { - _time = ti - } - - /** - Returns the interval between the date object and 00:00:00 UTC on 1 January 2001. - - This property's value is negative if the date object is earlier than the system's absolute reference date (00:00:00 UTC on 1 January 2001). - */ - public var timeIntervalSinceReferenceDate: TimeInterval { - return _time - } - - /** - Returns the interval between the receiver and another given date. - - - Parameter another: The date with which to compare the receiver. - - - Returns: The interval between the receiver and the `another` parameter. If the receiver is earlier than `anotherDate`, the return value is negative. If `anotherDate` is `nil`, the results are undefined. - - - SeeAlso: `timeIntervalSince1970` - - SeeAlso: `timeIntervalSinceNow` - - SeeAlso: `timeIntervalSinceReferenceDate` - */ - public func timeIntervalSince(_ date: Date) -> TimeInterval { - return self.timeIntervalSinceReferenceDate - date.timeIntervalSinceReferenceDate - } - - /** - The time interval between the date and the current date and time. - - If the date is earlier than the current date and time, this property's value is negative. - - - SeeAlso: `timeIntervalSince(_:)` - - SeeAlso: `timeIntervalSince1970` - - SeeAlso: `timeIntervalSinceReferenceDate` - */ - public var timeIntervalSinceNow: TimeInterval { - return self.timeIntervalSinceReferenceDate - CFAbsoluteTimeGetCurrent() - } - - /** - The interval between the date object and 00:00:00 UTC on 1 January 1970. - - This property's value is negative if the date object is earlier than 00:00:00 UTC on 1 January 1970. - - - SeeAlso: `timeIntervalSince(_:)` - - SeeAlso: `timeIntervalSinceNow` - - SeeAlso: `timeIntervalSinceReferenceDate` - */ - public var timeIntervalSince1970: TimeInterval { - return self.timeIntervalSinceReferenceDate + Date.timeIntervalBetween1970AndReferenceDate - } - - /// Return a new `Date` by adding a `TimeInterval` to this `Date`. - /// - /// - parameter timeInterval: The value to add, in seconds. - /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. - public func addingTimeInterval(_ timeInterval: TimeInterval) -> Date { - return self + timeInterval - } - - /// Add a `TimeInterval` to this `Date`. - /// - /// - parameter timeInterval: The value to add, in seconds. - /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. - public mutating func addTimeInterval(_ timeInterval: TimeInterval) { - self += timeInterval - } - - /** - Creates and returns a Date value representing a date in the distant future. - - The distant future is in terms of centuries. - */ - public static let distantFuture = Date(timeIntervalSinceReferenceDate: 63113904000.0) - - /** - Creates and returns a Date value representing a date in the distant past. - - The distant past is in terms of centuries. - */ - public static let distantPast = Date(timeIntervalSinceReferenceDate: -63114076800.0) - - public func hash(into hasher: inout Hasher) { - hasher.combine(_time) - } - - /// Compare two `Date` values. - public func compare(_ other: Date) -> ComparisonResult { - if _time < other.timeIntervalSinceReferenceDate { - return .orderedAscending - } else if _time > other.timeIntervalSinceReferenceDate { - return .orderedDescending - } else { - return .orderedSame - } - } - - /// Returns true if the two `Date` values represent the same point in time. - public static func ==(lhs: Date, rhs: Date) -> Bool { - return lhs.timeIntervalSinceReferenceDate == rhs.timeIntervalSinceReferenceDate - } - - /// Returns true if the left hand `Date` is earlier in time than the right hand `Date`. - public static func <(lhs: Date, rhs: Date) -> Bool { - return lhs.timeIntervalSinceReferenceDate < rhs.timeIntervalSinceReferenceDate - } - - /// Returns true if the left hand `Date` is later in time than the right hand `Date`. - public static func >(lhs: Date, rhs: Date) -> Bool { - return lhs.timeIntervalSinceReferenceDate > rhs.timeIntervalSinceReferenceDate - } - - /// Returns a `Date` with a specified amount of time added to it. - public static func +(lhs: Date, rhs: TimeInterval) -> Date { - return Date(timeIntervalSinceReferenceDate: lhs.timeIntervalSinceReferenceDate + rhs) - } - - /// Returns a `Date` with a specified amount of time subtracted from it. - public static func -(lhs: Date, rhs: TimeInterval) -> Date { - return Date(timeIntervalSinceReferenceDate: lhs.timeIntervalSinceReferenceDate - rhs) - } - - /// Add a `TimeInterval` to a `Date`. - /// - /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. - public static func +=(lhs: inout Date, rhs: TimeInterval) { - lhs = lhs + rhs - } - - /// Subtract a `TimeInterval` from a `Date`. - /// - /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. - public static func -=(lhs: inout Date, rhs: TimeInterval) { - lhs = lhs - rhs - } - -} - -extension Date : CustomDebugStringConvertible, CustomStringConvertible, CustomReflectable { - /** - A string representation of the date object (read-only). - - The representation is useful for debugging only. - - There are a number of options to acquire a formatted string for a date including: date formatters (see - [NSDateFormatter](//apple_ref/occ/cl/NSDateFormatter) and [Data Formatting Guide](//apple_ref/doc/uid/10000029i)), and the `Date` function `description(locale:)`. - */ - public var description: String { - // Defer to NSDate for description - return NSDate(timeIntervalSinceReferenceDate: _time).description - } - - /** - Returns a string representation of the receiver using the given - locale. - - - Parameter locale: A `Locale`. If you pass `nil`, `Date` formats the date in the same way as the `description` property. - - - Returns: A string representation of the `Date`, using the given locale, or if the locale argument is `nil`, in the international format `YYYY-MM-DD HH:MM:SS ±HHMM`, where `±HHMM` represents the time zone offset in hours and minutes from UTC (for example, "`2001-03-24 10:45:32 +0600`"). - */ - public func description(with locale: Locale?) -> String { - return NSDate(timeIntervalSinceReferenceDate: _time).description(with: locale) - } - - public var debugDescription: String { - return description - } - - public var customMirror: Mirror { - let c: [(label: String?, value: Any)] = [ - ("timeIntervalSinceReferenceDate", timeIntervalSinceReferenceDate) - ] - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - -extension Date : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSDate { - return NSDate(timeIntervalSinceReferenceDate: _time) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSDate, result: inout Date?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSDate, result: inout Date?) -> Bool { - result = Date(timeIntervalSinceReferenceDate: x.timeIntervalSinceReferenceDate) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSDate?) -> Date { - var result: Date? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -extension NSDate : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as Date) - } -} - -extension Date : _CustomPlaygroundQuickLookable { - var summary: String { - let df = DateFormatter() - df.dateStyle = .medium - df.timeStyle = .short - return df.string(from: self) - } - - @available(*, deprecated, message: "Date.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - return .text(summary) - } -} - -extension Date : Codable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let timestamp = try container.decode(Double.self) - self.init(timeIntervalSinceReferenceDate: timestamp) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.timeIntervalSinceReferenceDate) - } -} diff --git a/stdlib/public/Darwin/Foundation/DateComponents.swift b/stdlib/public/Darwin/Foundation/DateComponents.swift deleted file mode 100644 index ea18b52f5d529..0000000000000 --- a/stdlib/public/Darwin/Foundation/DateComponents.swift +++ /dev/null @@ -1,429 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -/** - `DateComponents` encapsulates the components of a date in an extendable, structured manner. - - It is used to specify a date by providing the temporal components that make up a date and time in a particular calendar: hour, minutes, seconds, day, month, year, and so on. It can also be used to specify a duration of time, for example, 5 hours and 16 minutes. A `DateComponents` is not required to define all the component fields. - - When a new instance of `DateComponents` is created, the date components are set to `nil`. -*/ -public struct DateComponents : ReferenceConvertible, Hashable, Equatable, _MutableBoxing { - public typealias ReferenceType = NSDateComponents - - internal var _handle: _MutableHandle - - /// Initialize a `DateComponents`, optionally specifying values for its fields. - public init(calendar: Calendar? = nil, - timeZone: TimeZone? = nil, - era: Int? = nil, - year: Int? = nil, - month: Int? = nil, - day: Int? = nil, - hour: Int? = nil, - minute: Int? = nil, - second: Int? = nil, - nanosecond: Int? = nil, - weekday: Int? = nil, - weekdayOrdinal: Int? = nil, - quarter: Int? = nil, - weekOfMonth: Int? = nil, - weekOfYear: Int? = nil, - yearForWeekOfYear: Int? = nil) { - _handle = _MutableHandle(adoptingReference: NSDateComponents()) - if let _calendar = calendar { self.calendar = _calendar } - if let _timeZone = timeZone { self.timeZone = _timeZone } - if let _era = era { self.era = _era } - if let _year = year { self.year = _year } - if let _month = month { self.month = _month } - if let _day = day { self.day = _day } - if let _hour = hour { self.hour = _hour } - if let _minute = minute { self.minute = _minute } - if let _second = second { self.second = _second } - if let _nanosecond = nanosecond { self.nanosecond = _nanosecond } - if let _weekday = weekday { self.weekday = _weekday } - if let _weekdayOrdinal = weekdayOrdinal { self.weekdayOrdinal = _weekdayOrdinal } - if let _quarter = quarter { self.quarter = _quarter } - if let _weekOfMonth = weekOfMonth { self.weekOfMonth = _weekOfMonth } - if let _weekOfYear = weekOfYear { self.weekOfYear = _weekOfYear } - if let _yearForWeekOfYear = yearForWeekOfYear { self.yearForWeekOfYear = _yearForWeekOfYear } - } - - - // MARK: - Properties - - /// Translate from the NSDateComponentUndefined value into a proper Swift optional - private func _getter(_ x : Int) -> Int? { return x == NSDateComponentUndefined ? nil : x } - - /// Translate from the proper Swift optional value into an NSDateComponentUndefined - private static func _setter(_ x : Int?) -> Int { if let xx = x { return xx } else { return NSDateComponentUndefined } } - - /// The `Calendar` used to interpret the other values in this structure. - /// - /// - note: API which uses `DateComponents` may have different behavior if this value is `nil`. For example, assuming the current calendar or ignoring certain values. - public var calendar: Calendar? { - get { return _handle.map { $0.calendar } } - set { _applyMutation { $0.calendar = newValue } } - } - - /// A time zone. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var timeZone: TimeZone? { - get { return _handle.map { $0.timeZone } } - set { _applyMutation { $0.timeZone = newValue } } - } - - /// An era or count of eras. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var era: Int? { - get { return _handle.map { _getter($0.era) } } - set { _applyMutation { $0.era = DateComponents._setter(newValue) } } - } - - /// A year or count of years. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var year: Int? { - get { return _handle.map { _getter($0.year) } } - set { _applyMutation { $0.year = DateComponents._setter(newValue) } } - } - - /// A month or count of months. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var month: Int? { - get { return _handle.map { _getter($0.month) } } - set { _applyMutation { $0.month = DateComponents._setter(newValue) } } - } - - /// A day or count of days. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var day: Int? { - get { return _handle.map { _getter($0.day) } } - set { _applyMutation { $0.day = DateComponents._setter(newValue) } } - } - - /// An hour or count of hours. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var hour: Int? { - get { return _handle.map { _getter($0.hour) } } - set { _applyMutation { $0.hour = DateComponents._setter(newValue) } } - } - - /// A minute or count of minutes. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var minute: Int? { - get { return _handle.map { _getter($0.minute) } } - set { _applyMutation { $0.minute = DateComponents._setter(newValue) } } - } - - /// A second or count of seconds. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var second: Int? { - get { return _handle.map { _getter($0.second) } } - set { _applyMutation { $0.second = DateComponents._setter(newValue) } } - } - - /// A nanosecond or count of nanoseconds. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var nanosecond: Int? { - get { return _handle.map { _getter($0.nanosecond) } } - set { _applyMutation { $0.nanosecond = DateComponents._setter(newValue) } } - } - - /// A weekday or count of weekdays. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var weekday: Int? { - get { return _handle.map { _getter($0.weekday) } } - set { _applyMutation { $0.weekday = DateComponents._setter(newValue) } } - } - - /// A weekday ordinal or count of weekday ordinals. - /// Weekday ordinal units represent the position of the weekday within the next larger calendar unit, such as the month. For example, 2 is the weekday ordinal unit for the second Friday of the month./// - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var weekdayOrdinal: Int? { - get { return _handle.map { _getter($0.weekdayOrdinal) } } - set { _applyMutation { $0.weekdayOrdinal = DateComponents._setter(newValue) } } - } - - /// A quarter or count of quarters. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var quarter: Int? { - get { return _handle.map { _getter($0.quarter) } } - set { _applyMutation { $0.quarter = DateComponents._setter(newValue) } } - } - - /// A week of the month or a count of weeks of the month. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var weekOfMonth: Int? { - get { return _handle.map { _getter($0.weekOfMonth) } } - set { _applyMutation { $0.weekOfMonth = DateComponents._setter(newValue) } } - } - - /// A week of the year or count of the weeks of the year. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var weekOfYear: Int? { - get { return _handle.map { _getter($0.weekOfYear) } } - set { _applyMutation { $0.weekOfYear = DateComponents._setter(newValue) } } - } - - /// The ISO 8601 week-numbering year of the receiver. - /// - /// The Gregorian calendar defines a week to have 7 days, and a year to have 365 days, or 366 in a leap year. However, neither 365 or 366 divide evenly into a 7 day week, so it is often the case that the last week of a year ends on a day in the next year, and the first week of a year begins in the preceding year. To reconcile this, ISO 8601 defines a week-numbering year, consisting of either 52 or 53 full weeks (364 or 371 days), such that the first week of a year is designated to be the week containing the first Thursday of the year. - /// - /// You can use the yearForWeekOfYear property with the weekOfYear and weekday properties to get the date corresponding to a particular weekday of a given week of a year. For example, the 6th day of the 53rd week of the year 2005 (ISO 2005-W53-6) corresponds to Sat 1 January 2005 on the Gregorian calendar. - /// - note: This value is interpreted in the context of the calendar in which it is used. - public var yearForWeekOfYear: Int? { - get { return _handle.map { _getter($0.yearForWeekOfYear) } } - set { _applyMutation { $0.yearForWeekOfYear = DateComponents._setter(newValue) } } - } - - /// Set to true if these components represent a leap month. - public var isLeapMonth: Bool? { - get { return _handle.map { $0.isLeapMonth } } - set { - _applyMutation { - // Technically, the underlying class does not support setting isLeapMonth to nil, but it could - so we leave the API consistent. - if let b = newValue { - $0.isLeapMonth = b - } else { - $0.isLeapMonth = false - } - } - } - } - - /// Returns a `Date` calculated from the current components using the `calendar` property. - public var date: Date? { - return _handle.map { $0.date } - } - - // MARK: - Generic Setter/Getters - - /// Set the value of one of the properties, using an enumeration value instead of a property name. - /// - /// The calendar and timeZone and isLeapMonth properties cannot be set by this method. - @available(macOS 10.9, iOS 8.0, *) - public mutating func setValue(_ value: Int?, for component: Calendar.Component) { - _applyMutation { - $0.setValue(DateComponents._setter(value), forComponent: Calendar._toCalendarUnit([component])) - } - } - - /// Returns the value of one of the properties, using an enumeration value instead of a property name. - /// - /// The calendar and timeZone and isLeapMonth property values cannot be retrieved by this method. - @available(macOS 10.9, iOS 8.0, *) - public func value(for component: Calendar.Component) -> Int? { - return _handle.map { - $0.value(forComponent: Calendar._toCalendarUnit([component])) - } - } - - // MARK: - - - /// Returns true if the combination of properties which have been set in the receiver is a date which exists in the `calendar` property. - /// - /// This method is not appropriate for use on `DateComponents` values which are specifying relative quantities of calendar components. - /// - /// Except for some trivial cases (e.g., 'seconds' should be 0 - 59 in any calendar), this method is not necessarily cheap. - /// - /// If the time zone property is set in the `DateComponents`, it is used. - /// - /// The calendar property must be set, or the result is always `false`. - @available(macOS 10.9, iOS 8.0, *) - public var isValidDate: Bool { - return _handle.map { $0.isValidDate } - } - - /// Returns true if the combination of properties which have been set in the receiver is a date which exists in the specified `Calendar`. - /// - /// This method is not appropriate for use on `DateComponents` values which are specifying relative quantities of calendar components. - /// - /// Except for some trivial cases (e.g., 'seconds' should be 0 - 59 in any calendar), this method is not necessarily cheap. - /// - /// If the time zone property is set in the `DateComponents`, it is used. - @available(macOS 10.9, iOS 8.0, *) - public func isValidDate(in calendar: Calendar) -> Bool { - return _handle.map { $0.isValidDate(in: calendar) } - } - - // MARK: - - - public func hash(into hasher: inout Hasher) { - hasher.combine(_handle._uncopiedReference()) - } - - // MARK: - Bridging Helpers - - private init(reference: __shared NSDateComponents) { - _handle = _MutableHandle(reference: reference) - } - - public static func ==(lhs : DateComponents, rhs: DateComponents) -> Bool { - // Don't copy references here; no one should be storing anything - return lhs._handle._uncopiedReference().isEqual(rhs._handle._uncopiedReference()) - } - -} - -extension DateComponents : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - - public var description: String { - return self.customMirror.children.reduce("") { - $0.appending("\($1.label ?? ""): \($1.value) ") - } - } - - public var debugDescription: String { - return self.description - } - - public var customMirror: Mirror { - var c: [(label: String?, value: Any)] = [] - if let r = calendar { c.append((label: "calendar", value: r)) } - if let r = timeZone { c.append((label: "timeZone", value: r)) } - if let r = era { c.append((label: "era", value: r)) } - if let r = year { c.append((label: "year", value: r)) } - if let r = month { c.append((label: "month", value: r)) } - if let r = day { c.append((label: "day", value: r)) } - if let r = hour { c.append((label: "hour", value: r)) } - if let r = minute { c.append((label: "minute", value: r)) } - if let r = second { c.append((label: "second", value: r)) } - if let r = nanosecond { c.append((label: "nanosecond", value: r)) } - if let r = weekday { c.append((label: "weekday", value: r)) } - if let r = weekdayOrdinal { c.append((label: "weekdayOrdinal", value: r)) } - if let r = quarter { c.append((label: "quarter", value: r)) } - if let r = weekOfMonth { c.append((label: "weekOfMonth", value: r)) } - if let r = weekOfYear { c.append((label: "weekOfYear", value: r)) } - if let r = yearForWeekOfYear { c.append((label: "yearForWeekOfYear", value: r)) } - if let r = isLeapMonth { c.append((label: "isLeapMonth", value: r)) } - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - -// MARK: - Bridging - -extension DateComponents : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSDateComponents.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSDateComponents { - return _handle._copiedReference() - } - - public static func _forceBridgeFromObjectiveC(_ dateComponents: NSDateComponents, result: inout DateComponents?) { - if !_conditionallyBridgeFromObjectiveC(dateComponents, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ dateComponents: NSDateComponents, result: inout DateComponents?) -> Bool { - result = DateComponents(reference: dateComponents) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSDateComponents?) -> DateComponents { - guard let src = source else { return DateComponents() } - return DateComponents(reference: src) - } -} - -extension NSDateComponents : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as DateComponents) - } -} - -extension DateComponents : Codable { - private enum CodingKeys : Int, CodingKey { - case calendar - case timeZone - case era - case year - case month - case day - case hour - case minute - case second - case nanosecond - case weekday - case weekdayOrdinal - case quarter - case weekOfMonth - case weekOfYear - case yearForWeekOfYear - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let calendar = try container.decodeIfPresent(Calendar.self, forKey: .calendar) - let timeZone = try container.decodeIfPresent(TimeZone.self, forKey: .timeZone) - let era = try container.decodeIfPresent(Int.self, forKey: .era) - let year = try container.decodeIfPresent(Int.self, forKey: .year) - let month = try container.decodeIfPresent(Int.self, forKey: .month) - let day = try container.decodeIfPresent(Int.self, forKey: .day) - let hour = try container.decodeIfPresent(Int.self, forKey: .hour) - let minute = try container.decodeIfPresent(Int.self, forKey: .minute) - let second = try container.decodeIfPresent(Int.self, forKey: .second) - let nanosecond = try container.decodeIfPresent(Int.self, forKey: .nanosecond) - - let weekday = try container.decodeIfPresent(Int.self, forKey: .weekday) - let weekdayOrdinal = try container.decodeIfPresent(Int.self, forKey: .weekdayOrdinal) - let quarter = try container.decodeIfPresent(Int.self, forKey: .quarter) - let weekOfMonth = try container.decodeIfPresent(Int.self, forKey: .weekOfMonth) - let weekOfYear = try container.decodeIfPresent(Int.self, forKey: .weekOfYear) - let yearForWeekOfYear = try container.decodeIfPresent(Int.self, forKey: .yearForWeekOfYear) - - self.init(calendar: calendar, - timeZone: timeZone, - era: era, - year: year, - month: month, - day: day, - hour: hour, - minute: minute, - second: second, - nanosecond: nanosecond, - weekday: weekday, - weekdayOrdinal: weekdayOrdinal, - quarter: quarter, - weekOfMonth: weekOfMonth, - weekOfYear: weekOfYear, - yearForWeekOfYear: yearForWeekOfYear) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeIfPresent(self.calendar, forKey: .calendar) - try container.encodeIfPresent(self.timeZone, forKey: .timeZone) - try container.encodeIfPresent(self.era, forKey: .era) - try container.encodeIfPresent(self.year, forKey: .year) - try container.encodeIfPresent(self.month, forKey: .month) - try container.encodeIfPresent(self.day, forKey: .day) - try container.encodeIfPresent(self.hour, forKey: .hour) - try container.encodeIfPresent(self.minute, forKey: .minute) - try container.encodeIfPresent(self.second, forKey: .second) - try container.encodeIfPresent(self.nanosecond, forKey: .nanosecond) - try container.encodeIfPresent(self.weekday, forKey: .weekday) - try container.encodeIfPresent(self.weekdayOrdinal, forKey: .weekdayOrdinal) - try container.encodeIfPresent(self.quarter, forKey: .quarter) - try container.encodeIfPresent(self.weekOfMonth, forKey: .weekOfMonth) - try container.encodeIfPresent(self.weekOfYear, forKey: .weekOfYear) - try container.encodeIfPresent(self.yearForWeekOfYear, forKey: .yearForWeekOfYear) - } -} diff --git a/stdlib/public/Darwin/Foundation/DateInterval.swift b/stdlib/public/Darwin/Foundation/DateInterval.swift deleted file mode 100644 index 5066f5fa30993..0000000000000 --- a/stdlib/public/Darwin/Foundation/DateInterval.swift +++ /dev/null @@ -1,228 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftCoreFoundationOverlayShims - -/// DateInterval represents a closed date interval in the form of [startDate, endDate]. It is possible for the start and end dates to be the same with a duration of 0. DateInterval does not support reverse intervals i.e. intervals where the duration is less than 0 and the end date occurs earlier in time than the start date. -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -public struct DateInterval : ReferenceConvertible, Comparable, Hashable, Codable { - public typealias ReferenceType = NSDateInterval - - /// The start date. - public var start : Date - - /// The end date. - /// - /// - precondition: `end >= start` - public var end : Date { - get { - return start + duration - } - set { - precondition(newValue >= start, "Reverse intervals are not allowed") - duration = newValue.timeIntervalSinceReferenceDate - start.timeIntervalSinceReferenceDate - } - } - - /// The duration. - /// - /// - precondition: `duration >= 0` - public var duration : TimeInterval { - willSet { - precondition(newValue >= 0, "Negative durations are not allowed") - } - } - - /// Initializes a `DateInterval` with start and end dates set to the current date and the duration set to `0`. - public init() { - let d = Date() - start = d - duration = 0 - } - - /// Initialize a `DateInterval` with the specified start and end date. - /// - /// - precondition: `end >= start` - public init(start: Date, end: Date) { - precondition(end >= start, "Reverse intervals are not allowed") - self.start = start - duration = end.timeIntervalSince(start) - } - - /// Initialize a `DateInterval` with the specified start date and duration. - /// - /// - precondition: `duration >= 0` - public init(start: Date, duration: TimeInterval) { - precondition(duration >= 0, "Negative durations are not allowed") - self.start = start - self.duration = duration - } - - /** - Compare two DateIntervals. - - This method prioritizes ordering by start date. If the start dates are equal, then it will order by duration. - e.g. Given intervals a and b - ``` - a. |-----| - b. |-----| - ``` - - `a.compare(b)` would return `.OrderedAscending` because a's start date is earlier in time than b's start date. - - In the event that the start dates are equal, the compare method will attempt to order by duration. - e.g. Given intervals c and d - ``` - c. |-----| - d. |---| - ``` - `c.compare(d)` would result in `.OrderedDescending` because c is longer than d. - - If both the start dates and the durations are equal, then the intervals are considered equal and `.OrderedSame` is returned as the result. - */ - public func compare(_ dateInterval: DateInterval) -> ComparisonResult { - let result = start.compare(dateInterval.start) - if result == .orderedSame { - if self.duration < dateInterval.duration { return .orderedAscending } - if self.duration > dateInterval.duration { return .orderedDescending } - return .orderedSame - } - return result - } - - /// Returns `true` if `self` intersects the `dateInterval`. - public func intersects(_ dateInterval: DateInterval) -> Bool { - return contains(dateInterval.start) || contains(dateInterval.end) || dateInterval.contains(start) || dateInterval.contains(end) - } - - /// Returns a DateInterval that represents the interval where the given date interval and the current instance intersect. - /// - /// In the event that there is no intersection, the method returns nil. - public func intersection(with dateInterval: DateInterval) -> DateInterval? { - if !intersects(dateInterval) { - return nil - } - - if self == dateInterval { - return self - } - - let timeIntervalForSelfStart = start.timeIntervalSinceReferenceDate - let timeIntervalForSelfEnd = end.timeIntervalSinceReferenceDate - let timeIntervalForGivenStart = dateInterval.start.timeIntervalSinceReferenceDate - let timeIntervalForGivenEnd = dateInterval.end.timeIntervalSinceReferenceDate - - let resultStartDate : Date - if timeIntervalForGivenStart >= timeIntervalForSelfStart { - resultStartDate = dateInterval.start - } else { - // self starts after given - resultStartDate = start - } - - let resultEndDate : Date - if timeIntervalForGivenEnd >= timeIntervalForSelfEnd { - resultEndDate = end - } else { - // given ends before self - resultEndDate = dateInterval.end - } - - return DateInterval(start: resultStartDate, end: resultEndDate) - } - - /// Returns `true` if `self` contains `date`. - public func contains(_ date: Date) -> Bool { - let timeIntervalForGivenDate = date.timeIntervalSinceReferenceDate - let timeIntervalForSelfStart = start.timeIntervalSinceReferenceDate - let timeIntervalForSelfEnd = end.timeIntervalSinceReferenceDate - if (timeIntervalForGivenDate >= timeIntervalForSelfStart) && (timeIntervalForGivenDate <= timeIntervalForSelfEnd) { - return true - } - return false - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(start) - hasher.combine(duration) - } - - @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) - public static func ==(lhs: DateInterval, rhs: DateInterval) -> Bool { - return lhs.start == rhs.start && lhs.duration == rhs.duration - } - - @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) - public static func <(lhs: DateInterval, rhs: DateInterval) -> Bool { - return lhs.compare(rhs) == .orderedAscending - } -} - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension DateInterval : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - public var description: String { - return "\(start) to \(end)" - } - - public var debugDescription: String { - return description - } - - public var customMirror: Mirror { - var c: [(label: String?, value: Any)] = [] - c.append((label: "start", value: start)) - c.append((label: "end", value: end)) - c.append((label: "duration", value: duration)) - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension DateInterval : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSDateInterval.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSDateInterval { - return NSDateInterval(start: start, duration: duration) - } - - public static func _forceBridgeFromObjectiveC(_ dateInterval: NSDateInterval, result: inout DateInterval?) { - if !_conditionallyBridgeFromObjectiveC(dateInterval, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ dateInterval : NSDateInterval, result: inout DateInterval?) -> Bool { - result = DateInterval(start: dateInterval.startDate, duration: dateInterval.duration) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSDateInterval?) -> DateInterval { - var result: DateInterval? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension NSDateInterval : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as DateInterval) - } -} - diff --git a/stdlib/public/Darwin/Foundation/Decimal.swift b/stdlib/public/Darwin/Foundation/Decimal.swift deleted file mode 100644 index 8ca54ec738023..0000000000000 --- a/stdlib/public/Darwin/Foundation/Decimal.swift +++ /dev/null @@ -1,630 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftCoreFoundationOverlayShims - -extension Decimal { - public typealias RoundingMode = NSDecimalNumber.RoundingMode - public typealias CalculationError = NSDecimalNumber.CalculationError -} - -public func pow(_ x: Decimal, _ y: Int) -> Decimal { - var x = x - var result = Decimal() - NSDecimalPower(&result, &x, y, .plain) - return result -} - -extension Decimal : Hashable, Comparable { - private subscript(index: UInt32) -> UInt16 { - get { - switch index { - case 0: return _mantissa.0 - case 1: return _mantissa.1 - case 2: return _mantissa.2 - case 3: return _mantissa.3 - case 4: return _mantissa.4 - case 5: return _mantissa.5 - case 6: return _mantissa.6 - case 7: return _mantissa.7 - default: fatalError("Invalid index \(index) for _mantissa") - } - } - } - - internal var doubleValue: Double { - if _length == 0 { - return _isNegative == 1 ? Double.nan : 0 - } - - var d = 0.0 - for idx in (0.. Bool { - var lhsVal = lhs - var rhsVal = rhs - // Note: In swift-corelibs-foundation, a bitwise comparison is first - // performed using fileprivate members not accessible here. - return NSDecimalCompare(&lhsVal, &rhsVal) == .orderedSame - } - - public static func <(lhs: Decimal, rhs: Decimal) -> Bool { - var lhsVal = lhs - var rhsVal = rhs - return NSDecimalCompare(&lhsVal, &rhsVal) == .orderedAscending - } -} - -extension Decimal : CustomStringConvertible { - public init?(string: __shared String, locale: __shared Locale? = nil) { - let scan = Scanner(string: string) - var theDecimal = Decimal() - scan.locale = locale - if !scan.scanDecimal(&theDecimal) { - return nil - } - self = theDecimal - } - - // Note: In swift-corelibs-foundation, `NSDecimalString(_:_:)` is - // implemented in terms of `description`; here, it's the other way around. - public var description: String { - var value = self - return NSDecimalString(&value, nil) - } -} - -extension Decimal : Codable { - private enum CodingKeys : Int, CodingKey { - case exponent - case length - case isNegative - case isCompact - case mantissa - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let exponent = try container.decode(CInt.self, forKey: .exponent) - let length = try container.decode(CUnsignedInt.self, forKey: .length) - let isNegative = try container.decode(Bool.self, forKey: .isNegative) - let isCompact = try container.decode(Bool.self, forKey: .isCompact) - - var mantissaContainer = try container.nestedUnkeyedContainer(forKey: .mantissa) - var mantissa: (CUnsignedShort, CUnsignedShort, CUnsignedShort, CUnsignedShort, - CUnsignedShort, CUnsignedShort, CUnsignedShort, CUnsignedShort) = (0,0,0,0,0,0,0,0) - mantissa.0 = try mantissaContainer.decode(CUnsignedShort.self) - mantissa.1 = try mantissaContainer.decode(CUnsignedShort.self) - mantissa.2 = try mantissaContainer.decode(CUnsignedShort.self) - mantissa.3 = try mantissaContainer.decode(CUnsignedShort.self) - mantissa.4 = try mantissaContainer.decode(CUnsignedShort.self) - mantissa.5 = try mantissaContainer.decode(CUnsignedShort.self) - mantissa.6 = try mantissaContainer.decode(CUnsignedShort.self) - mantissa.7 = try mantissaContainer.decode(CUnsignedShort.self) - - self = Decimal(_exponent: exponent, - _length: length, - _isNegative: CUnsignedInt(isNegative ? 1 : 0), - _isCompact: CUnsignedInt(isCompact ? 1 : 0), - _reserved: 0, - _mantissa: mantissa) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(_exponent, forKey: .exponent) - try container.encode(_length, forKey: .length) - try container.encode(_isNegative == 0 ? false : true, forKey: .isNegative) - try container.encode(_isCompact == 0 ? false : true, forKey: .isCompact) - - var mantissaContainer = container.nestedUnkeyedContainer(forKey: .mantissa) - try mantissaContainer.encode(_mantissa.0) - try mantissaContainer.encode(_mantissa.1) - try mantissaContainer.encode(_mantissa.2) - try mantissaContainer.encode(_mantissa.3) - try mantissaContainer.encode(_mantissa.4) - try mantissaContainer.encode(_mantissa.5) - try mantissaContainer.encode(_mantissa.6) - try mantissaContainer.encode(_mantissa.7) - } -} - -extension Decimal : ExpressibleByFloatLiteral { - public init(floatLiteral value: Double) { - self.init(value) - } -} - -extension Decimal : ExpressibleByIntegerLiteral { - public init(integerLiteral value: Int) { - self.init(value) - } -} - -extension Decimal : SignedNumeric { - public var magnitude: Decimal { - guard _length != 0 else { return self } - return Decimal( - _exponent: self._exponent, _length: self._length, - _isNegative: 0, _isCompact: self._isCompact, - _reserved: 0, _mantissa: self._mantissa) - } - - // FIXME(integers): implement properly - public init?(exactly source: T) { - fatalError() - } - - public static func +=(lhs: inout Decimal, rhs: Decimal) { - var rhs = rhs - _ = withUnsafeMutablePointer(to: &lhs) { - NSDecimalAdd($0, $0, &rhs, .plain) - } - } - - public static func -=(lhs: inout Decimal, rhs: Decimal) { - var rhs = rhs - _ = withUnsafeMutablePointer(to: &lhs) { - NSDecimalSubtract($0, $0, &rhs, .plain) - } - } - - public static func *=(lhs: inout Decimal, rhs: Decimal) { - var rhs = rhs - _ = withUnsafeMutablePointer(to: &lhs) { - NSDecimalMultiply($0, $0, &rhs, .plain) - } - } - - public static func /=(lhs: inout Decimal, rhs: Decimal) { - var rhs = rhs - _ = withUnsafeMutablePointer(to: &lhs) { - NSDecimalDivide($0, $0, &rhs, .plain) - } - } - - public static func +(lhs: Decimal, rhs: Decimal) -> Decimal { - var answer = lhs - answer += rhs - return answer - } - - public static func -(lhs: Decimal, rhs: Decimal) -> Decimal { - var answer = lhs - answer -= rhs - return answer - } - - public static func *(lhs: Decimal, rhs: Decimal) -> Decimal { - var answer = lhs - answer *= rhs - return answer - } - - public static func /(lhs: Decimal, rhs: Decimal) -> Decimal { - var answer = lhs - answer /= rhs - return answer - } - - public mutating func negate() { - guard _length != 0 else { return } - _isNegative = _isNegative == 0 ? 1 : 0 - } -} - -extension Decimal { - @available(swift, obsoleted: 4, message: "Please use arithmetic operators instead") - @_transparent - public mutating func add(_ other: Decimal) { - self += other - } - - @available(swift, obsoleted: 4, message: "Please use arithmetic operators instead") - @_transparent - public mutating func subtract(_ other: Decimal) { - self -= other - } - - @available(swift, obsoleted: 4, message: "Please use arithmetic operators instead") - @_transparent - public mutating func multiply(by other: Decimal) { - self *= other - } - - @available(swift, obsoleted: 4, message: "Please use arithmetic operators instead") - @_transparent - public mutating func divide(by other: Decimal) { - self /= other - } -} - -extension Decimal : Strideable { - public func distance(to other: Decimal) -> Decimal { - return self - other - } - - public func advanced(by n: Decimal) -> Decimal { - return self + n - } -} - -// The methods in this extension exist to match the protocol requirements of -// FloatingPoint, even if we can't conform directly. -// -// If it becomes clear that conformance is truly impossible, we can deprecate -// some of the methods (e.g. `isEqual(to:)` in favor of operators). -extension Decimal { - public static let leastFiniteMagnitude = Decimal( - _exponent: 127, - _length: 8, - _isNegative: 1, - _isCompact: 1, - _reserved: 0, - _mantissa: (0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff) - ) - - public static let greatestFiniteMagnitude = Decimal( - _exponent: 127, - _length: 8, - _isNegative: 0, - _isCompact: 1, - _reserved: 0, - _mantissa: (0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff) - ) - - public static let leastNormalMagnitude = Decimal( - _exponent: -127, - _length: 1, - _isNegative: 0, - _isCompact: 1, - _reserved: 0, - _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000) - ) - - public static let leastNonzeroMagnitude = Decimal( - _exponent: -127, - _length: 1, - _isNegative: 0, - _isCompact: 1, - _reserved: 0, - _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000) - ) - - public static let pi = Decimal( - _exponent: -38, - _length: 8, - _isNegative: 0, - _isCompact: 1, - _reserved: 0, - _mantissa: (0x6623, 0x7d57, 0x16e7, 0xad0d, 0xaf52, 0x4641, 0xdfa7, 0xec58) - ) - - @available(*, unavailable, message: "Decimal does not yet fully adopt FloatingPoint.") - public static var infinity: Decimal { fatalError("Decimal does not yet fully adopt FloatingPoint") } - - @available(*, unavailable, message: "Decimal does not yet fully adopt FloatingPoint.") - public static var signalingNaN: Decimal { fatalError("Decimal does not yet fully adopt FloatingPoint") } - - public static var quietNaN: Decimal { - return Decimal( - _exponent: 0, _length: 0, _isNegative: 1, _isCompact: 0, - _reserved: 0, _mantissa: (0, 0, 0, 0, 0, 0, 0, 0)) - } - - public static var nan: Decimal { quietNaN } - - public static var radix: Int { 10 } - - public init(_ value: UInt8) { - self.init(UInt64(value)) - } - - public init(_ value: Int8) { - self.init(Int64(value)) - } - - public init(_ value: UInt16) { - self.init(UInt64(value)) - } - - public init(_ value: Int16) { - self.init(Int64(value)) - } - - public init(_ value: UInt32) { - self.init(UInt64(value)) - } - - public init(_ value: Int32) { - self.init(Int64(value)) - } - - public init(_ value: UInt64) { - self = Decimal() - if value == 0 { - return - } - - var compactValue = value - var exponent: Int32 = 0 - while compactValue % 10 == 0 { - compactValue /= 10 - exponent += 1 - } - _isCompact = 1 - _exponent = exponent - - let wordCount = ((UInt64.bitWidth - compactValue.leadingZeroBitCount) + (UInt16.bitWidth - 1)) / UInt16.bitWidth - _length = UInt32(wordCount) - _mantissa.0 = UInt16(truncatingIfNeeded: compactValue >> 0) - _mantissa.1 = UInt16(truncatingIfNeeded: compactValue >> 16) - _mantissa.2 = UInt16(truncatingIfNeeded: compactValue >> 32) - _mantissa.3 = UInt16(truncatingIfNeeded: compactValue >> 48) - } - - public init(_ value: Int64) { - self.init(value.magnitude) - if value < 0 { - _isNegative = 1 - } - } - - public init(_ value: UInt) { - self.init(UInt64(value)) - } - - public init(_ value: Int) { - self.init(Int64(value)) - } - - public init(_ value: Double) { - precondition(!value.isInfinite, "Decimal does not yet fully adopt FloatingPoint") - if value.isNaN { - self = Decimal.nan - } else if value == 0.0 { - self = Decimal() - } else { - self = Decimal() - let negative = value < 0 - var val = negative ? -1 * value : value - var exponent = 0 - while val < Double(UInt64.max - 1) { - val *= 10.0 - exponent -= 1 - } - while Double(UInt64.max - 1) < val { - val /= 10.0 - exponent += 1 - } - var mantissa = UInt64(val) - - var i: UInt32 = 0 - // This is a bit ugly but it is the closest approximation of the C - // initializer that can be expressed here. - while mantissa != 0 && i < 8 /* NSDecimalMaxSize */ { - switch i { - case 0: - _mantissa.0 = UInt16(truncatingIfNeeded: mantissa) - case 1: - _mantissa.1 = UInt16(truncatingIfNeeded: mantissa) - case 2: - _mantissa.2 = UInt16(truncatingIfNeeded: mantissa) - case 3: - _mantissa.3 = UInt16(truncatingIfNeeded: mantissa) - case 4: - _mantissa.4 = UInt16(truncatingIfNeeded: mantissa) - case 5: - _mantissa.5 = UInt16(truncatingIfNeeded: mantissa) - case 6: - _mantissa.6 = UInt16(truncatingIfNeeded: mantissa) - case 7: - _mantissa.7 = UInt16(truncatingIfNeeded: mantissa) - default: - fatalError("initialization overflow") - } - mantissa = mantissa >> 16 - i += 1 - } - _length = i - _isNegative = negative ? 1 : 0 - _isCompact = 0 - _exponent = Int32(exponent) - NSDecimalCompact(&self) - } - } - - public init(sign: FloatingPointSign, exponent: Int, significand: Decimal) { - self.init( - _exponent: Int32(exponent) + significand._exponent, - _length: significand._length, - _isNegative: sign == .plus ? 0 : 1, - _isCompact: significand._isCompact, - _reserved: 0, - _mantissa: significand._mantissa) - } - - public init(signOf: Decimal, magnitudeOf magnitude: Decimal) { - self.init( - _exponent: magnitude._exponent, - _length: magnitude._length, - _isNegative: signOf._isNegative, - _isCompact: magnitude._isCompact, - _reserved: 0, - _mantissa: magnitude._mantissa) - } - - public var exponent: Int { - return Int(_exponent) - } - - public var significand: Decimal { - return Decimal( - _exponent: 0, _length: _length, _isNegative: _isNegative, _isCompact: _isCompact, - _reserved: 0, _mantissa: _mantissa) - } - - public var sign: FloatingPointSign { - return _isNegative == 0 ? FloatingPointSign.plus : FloatingPointSign.minus - } - - public var ulp: Decimal { - if !self.isFinite { return Decimal.nan } - return Decimal( - _exponent: _exponent, _length: 8, _isNegative: 0, _isCompact: 1, - _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) - } - - public var nextUp: Decimal { - return self + Decimal( - _exponent: _exponent, _length: 1, _isNegative: 0, _isCompact: 1, - _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) - } - - public var nextDown: Decimal { - return self - Decimal( - _exponent: _exponent, _length: 1, _isNegative: 0, _isCompact: 1, - _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) - } - - /// The IEEE 754 "class" of this type. - public var floatingPointClass: FloatingPointClassification { - if _length == 0 && _isNegative == 1 { - return .quietNaN - } else if _length == 0 { - return .positiveZero - } - // NSDecimal does not really represent normal and subnormal in the same - // manner as the IEEE standard, for now we can probably claim normal for - // any nonzero, non-NaN values. - if _isNegative == 1 { - return .negativeNormal - } else { - return .positiveNormal - } - } - - public var isCanonical: Bool { true } - - /// `true` if `self` is negative, `false` otherwise. - public var isSignMinus: Bool { _isNegative != 0 } - - /// `true` if `self` is +0.0 or -0.0, `false` otherwise. - public var isZero: Bool { _length == 0 && _isNegative == 0 } - - /// `true` if `self` is subnormal, `false` otherwise. - public var isSubnormal: Bool { false } - - /// `true` if `self` is normal (not zero, subnormal, infinity, or NaN), - /// `false` otherwise. - public var isNormal: Bool { !isZero && !isInfinite && !isNaN } - - /// `true` if `self` is zero, subnormal, or normal (not infinity or NaN), - /// `false` otherwise. - public var isFinite: Bool { !isNaN } - - /// `true` if `self` is infinity, `false` otherwise. - public var isInfinite: Bool { false } - - /// `true` if `self` is NaN, `false` otherwise. - public var isNaN: Bool { _length == 0 && _isNegative == 1 } - - /// `true` if `self` is a signaling NaN, `false` otherwise. - public var isSignaling: Bool { false } - - /// `true` if `self` is a signaling NaN, `false` otherwise. - public var isSignalingNaN: Bool { false } - - public func isEqual(to other: Decimal) -> Bool { - var lhs = self - var rhs = other - return NSDecimalCompare(&lhs, &rhs) == .orderedSame - } - - public func isLess(than other: Decimal) -> Bool { - var lhs = self - var rhs = other - return NSDecimalCompare(&lhs, &rhs) == .orderedAscending - } - - public func isLessThanOrEqualTo(_ other: Decimal) -> Bool { - var lhs = self - var rhs = other - let order = NSDecimalCompare(&lhs, &rhs) - return order == .orderedAscending || order == .orderedSame - } - - public func isTotallyOrdered(belowOrEqualTo other: Decimal) -> Bool { - // Note: Decimal does not have -0 or infinities to worry about - if self.isNaN { - return false - } - if self < other { - return true - } - if other < self { - return false - } - // Fall through to == behavior - return true - } - - @available(*, unavailable, message: "Decimal does not yet fully adopt FloatingPoint.") - public mutating func formTruncatingRemainder(dividingBy other: Decimal) { fatalError("Decimal does not yet fully adopt FloatingPoint") } -} - -extension Decimal : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSDecimalNumber { - return NSDecimalNumber(decimal: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSDecimalNumber, result: inout Decimal?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: NSDecimalNumber, result: inout Decimal?) -> Bool { - result = input.decimalValue - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSDecimalNumber?) -> Decimal { - guard let src = source else { return Decimal(_exponent: 0, _length: 0, _isNegative: 0, _isCompact: 0, _reserved: 0, _mantissa: (0, 0, 0, 0, 0, 0, 0, 0)) } - return src.decimalValue - } -} diff --git a/stdlib/public/Darwin/Foundation/DispatchData+DataProtocol.swift b/stdlib/public/Darwin/Foundation/DispatchData+DataProtocol.swift deleted file mode 100644 index 97b82e4ae52b0..0000000000000 --- a/stdlib/public/Darwin/Foundation/DispatchData+DataProtocol.swift +++ /dev/null @@ -1,56 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2018 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 -// -//===----------------------------------------------------------------------===// - - -import Dispatch - -extension DispatchData : DataProtocol { - public struct Region : DataProtocol, ContiguousBytes { - internal let bytes: UnsafeBufferPointer - internal let index: DispatchData.Index - internal let owner: DispatchData - internal init(bytes: UnsafeBufferPointer, index: DispatchData.Index, owner: DispatchData) { - self.bytes = bytes - self.index = index - self.owner = owner - } - - public var regions: CollectionOfOne { - return CollectionOfOne(self) - } - - public subscript(position: DispatchData.Index) -> UInt8 { - precondition(index <= position && position <= index + bytes.count) - return bytes[position - index] - } - - public var startIndex: DispatchData.Index { - return index - } - - public var endIndex: DispatchData.Index { - return index + bytes.count - } - - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { - return try body(UnsafeRawBufferPointer(bytes)) - } - } - - public var regions: [Region] { - var regions = [Region]() - enumerateBytes { (bytes, index, stop) in - regions.append(Region(bytes: bytes, index: index, owner: self)) - } - return regions - } -} diff --git a/stdlib/public/Darwin/Foundation/FileManager.swift b/stdlib/public/Darwin/Foundation/FileManager.swift deleted file mode 100644 index 42feae85813a6..0000000000000 --- a/stdlib/public/Darwin/Foundation/FileManager.swift +++ /dev/null @@ -1,56 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -extension FileManager { - /* - renamed syntax should be: - public func replaceItem(at originalItemURL: URL, withItemAt newItemURL: URL, backupItemName: String? = nil, options : FileManager.ItemReplacementOptions = []) throws -> URL? - */ - - @available(*, deprecated, renamed:"replaceItemAt(_:withItemAt:backupItemName:options:)") - public func replaceItemAtURL(originalItemURL: NSURL, withItemAtURL newItemURL: NSURL, backupItemName: String? = nil, options: FileManager.ItemReplacementOptions = []) throws -> NSURL? { - var error: NSError? = nil - guard let result = __NSFileManagerReplaceItemAtURL(self, originalItemURL as URL, newItemURL as URL, backupItemName, options, &error) else { throw error! } - return result as NSURL - } - - @available(swift, obsoleted: 4) - @available(macOS 10.6, iOS 4.0, *) - public func replaceItemAt(_ originalItemURL: URL, withItemAt newItemURL: URL, backupItemName: String? = nil, options: FileManager.ItemReplacementOptions = []) throws -> NSURL? { - var error: NSError? - guard let result = __NSFileManagerReplaceItemAtURL(self, originalItemURL, newItemURL , backupItemName, options, &error) else { throw error! } - return result as NSURL - } - - @available(swift, introduced: 4) - @available(macOS 10.6, iOS 4.0, *) - public func replaceItemAt(_ originalItemURL: URL, withItemAt newItemURL: URL, backupItemName: String? = nil, options: FileManager.ItemReplacementOptions = []) throws -> URL? { - var error: NSError? - guard let result = __NSFileManagerReplaceItemAtURL(self, originalItemURL, newItemURL , backupItemName, options, &error) else { throw error! } - return result - } - - @available(macOS 10.6, iOS 4.0, *) - @nonobjc - public func enumerator(at url: URL, includingPropertiesForKeys keys: [URLResourceKey]?, options mask: FileManager.DirectoryEnumerationOptions = [], errorHandler handler: ((URL, Error) -> Bool)? = nil) -> FileManager.DirectoryEnumerator? { - return __NSFileManagerEnumeratorAtURL(self, url, keys, mask, { (url, error) in - var errorResult = true - if let h = handler { - errorResult = h(url, error) - } - return errorResult - }) - } -} diff --git a/stdlib/public/Darwin/Foundation/Foundation.swift b/stdlib/public/Darwin/Foundation/Foundation.swift deleted file mode 100644 index 57a81be0a3155..0000000000000 --- a/stdlib/public/Darwin/Foundation/Foundation.swift +++ /dev/null @@ -1,245 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import CoreFoundation -import CoreGraphics - -//===----------------------------------------------------------------------===// -// NSObject -//===----------------------------------------------------------------------===// - -// These conformances should be located in the `ObjectiveC` module, but they can't -// be placed there because string bridging is not available there. -extension NSObject : CustomStringConvertible {} -extension NSObject : CustomDebugStringConvertible {} - -public let NSNotFound: Int = .max - -//===----------------------------------------------------------------------===// -// NSLocalizedString -//===----------------------------------------------------------------------===// - -/// Returns the localized version of a string. -/// -/// - parameter key: An identifying value used to reference a localized string. -/// Don't use the empty string as a key. Values keyed by the empty string will -/// not be localized. -/// - parameter tableName: The name of the table containing the localized string -/// identified by `key`. This is the prefix of the strings file—a file with -/// the `.strings` extension—containing the localized values. If `tableName` -/// is `nil` or the empty string, the `Localizable` table is used. -/// - parameter bundle: The bundle containing the table's strings file. The main -/// bundle is used by default. -/// - parameter value: A user-visible string to return when the localized string -/// for `key` cannot be found in the table. If `value` is the empty string, -/// `key` would be returned instead. -/// - parameter comment: A note to the translator describing the context where -/// the localized string is presented to the user. -/// -/// - returns: A localized version of the string designated by `key` in the -/// table identified by `tableName`. If the localized string for `key` cannot -/// be found within the table, `value` is returned. However, `key` is returned -/// instead when `value` is the empty string. -/// -/// Export Localizations with Xcode -/// ------------------------------- -/// -/// Xcode can read through a project's code to find invocations of -/// `NSLocalizedString(_:tableName:bundle:value:comment:)` and automatically -/// generate the appropriate strings files for the project's base localization. -/// -/// In Xcode, open the project file and, in the `Edit` menu, select -/// `Export for Localization`. This will generate an XLIFF bundle containing -/// strings files derived from your code along with other localizable assets. -/// `xcodebuild` can also be used to generate the localization bundle from the -/// command line with the `exportLocalizations` option. -/// -/// xcodebuild -exportLocalizations -project .xcodeproj \ -/// -localizationPath -/// -/// These bundles can be sent to translators for localization, and then -/// reimported into your Xcode project. In Xcode, open the project file. In the -/// `Edit` menu, select `Import Localizations...`, and select the XLIFF -/// folder to import. You can also use `xcodebuild` to import localizations with -/// the `importLocalizations` option. -/// -/// xcodebuild -importLocalizations -project .xcodeproj \ -/// -localizationPath -/// -/// Choose Meaningful Keys -/// ---------------------- -/// -/// Words can often have multiple different meanings depending on the context -/// in which they're used. For example, the word "Book" can be used as a noun—a -/// printed literary work—and it can be used as a verb—the action of making a -/// reservation. Words with different meanings which share the same spelling are -/// heteronyms. -/// -/// Different languages often have different heteronyms. "Book" in English is -/// one such heteronym, but that's not so in French, where the noun translates -/// to "Livre", and the verb translates to "Réserver". For this reason, it's -/// important make sure that each use of the same phrase is translated -/// appropriately for its context by assigning unique keys to each phrase and -/// adding a description comment describing how that phrase is used. -/// -/// NSLocalizedString("book-tag-title", value: "Book", comment: """ -/// noun: A label attached to literary items in the library. -/// """) -/// -/// NSLocalizedString("book-button-title", value: "Book", comment: """ -/// verb: Title of the button that makes a reservation. -/// """) -/// -/// Use Only String Literals -/// ------------------------ -/// -/// String literal values must be used with `key`, `tableName`, `value`, and -/// `comment`. -/// -/// Xcode does not evaluate interpolated strings and string variables when -/// generating strings files from code. Attempting to localize a string using -/// those language features will cause Xcode to export something that resembles -/// the original code expression instead of its expected value at runtime. -/// Translators would then translate that exported value—leaving -/// international users with a localized string containing code. -/// -/// // Translators will see "1 + 1 = (1 + 1)". -/// // International users will see a localization "1 + 1 = (1 + 1)". -/// let localizedString = NSLocalizedString("string-interpolation", -/// value: "1 + 1 = \(1 + 1)" -/// comment: "A math equation.") -/// -/// To dynamically insert values within localized strings, set `value` to a -/// format string, and use `String.localizedStringWithFormat(_:_:)` to insert -/// those values. -/// -/// // Translators will see "1 + 1 = %d" (they know what "%d" means). -/// // International users will see a localization of "1 + 1 = 2". -/// let format = NSLocalizedString("string-literal", -/// value: "1 + 1 = %d", -/// comment: "A math equation.") -/// let localizedString = String.localizedStringWithFormat(format, (1 + 1)) -/// -/// Multiline string literals are technically supported, but will result in -/// unexpected behavior during internationalization. A newline will be inserted -/// before and after the body of text within the string, and translators will -/// likely preserve those in their internationalizations. -/// -/// To preserve some of the aesthetics of having newlines in the string mirrored -/// in their code representation, string literal concatenation with the `+` -/// operator can be used. -/// -/// NSLocalizedString("multiline-string-literal", -/// value: """ -/// This multiline string literal won't work as expected. -/// An extra newline is added to the beginning and end of the string. -/// """, -/// comment: "The description of a sample of code.") -/// -/// NSLocalizedString("string-literal-contatenation", -/// value: "This string literal concatenated with" -/// + "this other string literal works just fine.", -/// comment: "The description of a sample of code.") -/// -/// Since comments aren't localized, multiline string literals can be safely -/// used with `comment`. -/// -/// Work with Manually Managed Strings -/// ---------------------------------- -/// -/// If having Xcode generate strings files from code isn't desired behavior, -/// call `Bundle.localizedString(forKey:value:table:)` instead. -/// -/// let greeting = Bundle.localizedString(forKey: "program-greeting", -/// value: "Hello, World!", -/// table: "Localization") -/// -/// However, this requires the manual creation and management of that table's -/// strings file. -/// -/// /* Localization.strings */ -/// -/// /* A friendly greeting to the user when the program starts. */ -/// "program-greeting" = "Hello, World!"; -/// -/// - note: Although `NSLocalizedString(_:tableName:bundle:value:comment:)` -/// and `Bundle.localizedString(forKey:value:table:)` can be used in a project -/// at the same time, data from manually managed strings files will be -/// overwritten by Xcode when their table is also used to look up localized -/// strings with `NSLocalizedString(_:tableName:bundle:value:comment:)`. -public -func NSLocalizedString(_ key: String, - tableName: String? = nil, - bundle: Bundle = Bundle.main, - value: String = "", - comment: String) -> String { - return bundle.localizedString(forKey: key, value:value, table:tableName) -} - -//===----------------------------------------------------------------------===// -// NSLog -//===----------------------------------------------------------------------===// - -public func NSLog(_ format: String, _ args: CVarArg...) { - withVaList(args) { NSLogv(format, $0) } -} - -//===----------------------------------------------------------------------===// -// AnyHashable -//===----------------------------------------------------------------------===// - -extension AnyHashable : _ObjectiveCBridgeable { - public func _bridgeToObjectiveC() -> NSObject { - // This is unprincipled, but pretty much any object we'll encounter in - // Swift is NSObject-conforming enough to have -hash and -isEqual:. - return unsafeBitCast(base as AnyObject, to: NSObject.self) - } - - public static func _forceBridgeFromObjectiveC( - _ x: NSObject, - result: inout AnyHashable? - ) { - result = AnyHashable(x) - } - - public static func _conditionallyBridgeFromObjectiveC( - _ x: NSObject, - result: inout AnyHashable? - ) -> Bool { - self._forceBridgeFromObjectiveC(x, result: &result) - return result != nil - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC( - _ source: NSObject? - ) -> AnyHashable { - // `nil` has historically been used as a stand-in for an empty - // string; map it to an empty string. - if _slowPath(source == nil) { return AnyHashable(String()) } - return AnyHashable(source!) - } -} - -//===----------------------------------------------------------------------===// -// CVarArg for bridged types -//===----------------------------------------------------------------------===// - -extension CVarArg where Self: _ObjectiveCBridgeable { - /// Default implementation for bridgeable types. - public var _cVarArgEncoding: [Int] { - let object = self._bridgeToObjectiveC() - _autorelease(object) - return _encodeBitsAsWords(object) - } -} diff --git a/stdlib/public/Darwin/Foundation/IndexPath.swift b/stdlib/public/Darwin/Foundation/IndexPath.swift deleted file mode 100644 index 5c5d89d4ed702..0000000000000 --- a/stdlib/public/Darwin/Foundation/IndexPath.swift +++ /dev/null @@ -1,767 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -/** - `IndexPath` represents the path to a specific node in a tree of nested array collections. - - Each index in an index path represents the index into an array of children from one node in the tree to another, deeper, node. - */ -public struct IndexPath : ReferenceConvertible, Equatable, Hashable, MutableCollection, RandomAccessCollection, Comparable, ExpressibleByArrayLiteral { - public typealias ReferenceType = NSIndexPath - public typealias Element = Int - public typealias Index = Array.Index - public typealias Indices = DefaultIndices - - fileprivate enum Storage : ExpressibleByArrayLiteral { - typealias Element = Int - case empty - case single(Int) - case pair(Int, Int) - case array([Int]) - - init(arrayLiteral elements: Int...) { - self.init(elements) - } - - init(_ elements: [Int]) { - switch elements.count { - case 0: - self = .empty - case 1: - self = .single(elements[0]) - case 2: - self = .pair(elements[0], elements[1]) - default: - self = .array(elements) - } - } - - func dropLast() -> Storage { - switch self { - case .empty: - return .empty - case .single(_): - return .empty - case .pair(let first, _): - return .single(first) - case .array(let indexes): - switch indexes.count { - case 3: - return .pair(indexes[0], indexes[1]) - default: - return .array(Array(indexes.dropLast())) - } - } - } - - mutating func append(_ other: Int) { - switch self { - case .empty: - self = .single(other) - case .single(let first): - self = .pair(first, other) - case .pair(let first, let second): - self = .array([first, second, other]) - case .array(let indexes): - self = .array(indexes + [other]) - } - } - - mutating func append(contentsOf other: Storage) { - switch self { - case .empty: - switch other { - case .empty: - // DO NOTHING - break - case .single(let rhsIndex): - self = .single(rhsIndex) - case .pair(let rhsFirst, let rhsSecond): - self = .pair(rhsFirst, rhsSecond) - case .array(let rhsIndexes): - self = .array(rhsIndexes) - } - case .single(let lhsIndex): - switch other { - case .empty: - // DO NOTHING - break - case .single(let rhsIndex): - self = .pair(lhsIndex, rhsIndex) - case .pair(let rhsFirst, let rhsSecond): - self = .array([lhsIndex, rhsFirst, rhsSecond]) - case .array(let rhsIndexes): - self = .array([lhsIndex] + rhsIndexes) - } - case .pair(let lhsFirst, let lhsSecond): - switch other { - case .empty: - // DO NOTHING - break - case .single(let rhsIndex): - self = .array([lhsFirst, lhsSecond, rhsIndex]) - case .pair(let rhsFirst, let rhsSecond): - self = .array([lhsFirst, lhsSecond, rhsFirst, rhsSecond]) - case .array(let rhsIndexes): - self = .array([lhsFirst, lhsSecond] + rhsIndexes) - } - case .array(let lhsIndexes): - switch other { - case .empty: - // DO NOTHING - break - case .single(let rhsIndex): - self = .array(lhsIndexes + [rhsIndex]) - case .pair(let rhsFirst, let rhsSecond): - self = .array(lhsIndexes + [rhsFirst, rhsSecond]) - case .array(let rhsIndexes): - self = .array(lhsIndexes + rhsIndexes) - } - } - } - - mutating func append(contentsOf other: __owned [Int]) { - switch self { - case .empty: - switch other.count { - case 0: - // DO NOTHING - break - case 1: - self = .single(other[0]) - case 2: - self = .pair(other[0], other[1]) - default: - self = .array(other) - } - case .single(let first): - switch other.count { - case 0: - // DO NOTHING - break - case 1: - self = .pair(first, other[0]) - default: - self = .array([first] + other) - } - case .pair(let first, let second): - switch other.count { - case 0: - // DO NOTHING - break - default: - self = .array([first, second] + other) - } - case .array(let indexes): - self = .array(indexes + other) - } - } - - subscript(_ index: Int) -> Int { - get { - switch self { - case .empty: - fatalError("Index \(index) out of bounds of count 0") - case .single(let first): - precondition(index == 0, "Index \(index) out of bounds of count 1") - return first - case .pair(let first, let second): - precondition(index >= 0 && index < 2, "Index \(index) out of bounds of count 2") - return index == 0 ? first : second - case .array(let indexes): - return indexes[index] - } - } - set { - switch self { - case .empty: - fatalError("Index \(index) out of bounds of count 0") - case .single(_): - precondition(index == 0, "Index \(index) out of bounds of count 1") - self = .single(newValue) - case .pair(let first, let second): - precondition(index >= 0 && index < 2, "Index \(index) out of bounds of count 2") - if index == 0 { - self = .pair(newValue, second) - } else { - self = .pair(first, newValue) - } - case .array(let indexes_): - var indexes = indexes_ - indexes[index] = newValue - self = .array(indexes) - } - } - } - - subscript(range: Range) -> Storage { - get { - switch self { - case .empty: - switch (range.lowerBound, range.upperBound) { - case (0, 0): - return .empty - default: - fatalError("Range \(range) is out of bounds of count 0") - } - case .single(let index): - switch (range.lowerBound, range.upperBound) { - case (0, 0), - (1, 1): - return .empty - case (0, 1): - return .single(index) - default: - fatalError("Range \(range) is out of bounds of count 1") - } - case .pair(let first, let second): - switch (range.lowerBound, range.upperBound) { - case (0, 0), - (1, 1), - (2, 2): - return .empty - case (0, 1): - return .single(first) - case (1, 2): - return .single(second) - case (0, 2): - return self - default: - fatalError("Range \(range) is out of bounds of count 2") - } - case .array(let indexes): - let slice = indexes[range] - switch slice.count { - case 0: - return .empty - case 1: - return .single(slice.first!) - case 2: - return .pair(slice.first!, slice.last!) - default: - return .array(Array(slice)) - } - } - } - set { - switch self { - case .empty: - precondition(range.lowerBound == 0 && range.upperBound == 0, "Range \(range) is out of bounds of count 0") - self = newValue - case .single(let index): - switch (range.lowerBound, range.upperBound, newValue) { - case (0, 0, .empty), - (1, 1, .empty): - break - case (0, 0, .single(let other)): - self = .pair(other, index) - case (0, 0, .pair(let first, let second)): - self = .array([first, second, index]) - case (0, 0, .array(let other)): - self = .array(other + [index]) - case (0, 1, .empty), - (0, 1, .single), - (0, 1, .pair), - (0, 1, .array): - self = newValue - case (1, 1, .single(let other)): - self = .pair(index, other) - case (1, 1, .pair(let first, let second)): - self = .array([index, first, second]) - case (1, 1, .array(let other)): - self = .array([index] + other) - default: - fatalError("Range \(range) is out of bounds of count 1") - } - case .pair(let first, let second): - switch (range.lowerBound, range.upperBound) { - case (0, 0): - switch newValue { - case .empty: - break - case .single(let other): - self = .array([other, first, second]) - case .pair(let otherFirst, let otherSecond): - self = .array([otherFirst, otherSecond, first, second]) - case .array(let other): - self = .array(other + [first, second]) - } - case (0, 1): - switch newValue { - case .empty: - self = .single(second) - case .single(let other): - self = .pair(other, second) - case .pair(let otherFirst, let otherSecond): - self = .array([otherFirst, otherSecond, second]) - case .array(let other): - self = .array(other + [second]) - } - case (0, 2): - self = newValue - case (1, 2): - switch newValue { - case .empty: - self = .single(first) - case .single(let other): - self = .pair(first, other) - case .pair(let otherFirst, let otherSecond): - self = .array([first, otherFirst, otherSecond]) - case .array(let other): - self = .array([first] + other) - } - case (2, 2): - switch newValue { - case .empty: - break - case .single(let other): - self = .array([first, second, other]) - case .pair(let otherFirst, let otherSecond): - self = .array([first, second, otherFirst, otherSecond]) - case .array(let other): - self = .array([first, second] + other) - } - default: - fatalError("Range \(range) is out of bounds of count 2") - } - case .array(let indexes): - var newIndexes = indexes - newIndexes.removeSubrange(range) - switch newValue { - case .empty: - break - case .single(let index): - newIndexes.insert(index, at: range.lowerBound) - case .pair(let first, let second): - newIndexes.insert(first, at: range.lowerBound) - newIndexes.insert(second, at: range.lowerBound + 1) - case .array(let other): - newIndexes.insert(contentsOf: other, at: range.lowerBound) - } - self = Storage(newIndexes) - } - } - } - - var count: Int { - switch self { - case .empty: - return 0 - case .single: - return 1 - case .pair: - return 2 - case .array(let indexes): - return indexes.count - } - } - - var startIndex: Int { - return 0 - } - - var endIndex: Int { - return count - } - - var allValues: [Int] { - switch self { - case .empty: return [] - case .single(let index): return [index] - case .pair(let first, let second): return [first, second] - case .array(let indexes): return indexes - } - } - - func index(before i: Int) -> Int { - return i - 1 - } - - func index(after i: Int) -> Int { - return i + 1 - } - - var description: String { - switch self { - case .empty: - return "[]" - case .single(let index): - return "[\(index)]" - case .pair(let first, let second): - return "[\(first), \(second)]" - case .array(let indexes): - return indexes.description - } - } - - func withUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R { - switch self { - case .empty: - return try body(UnsafeBufferPointer(start: nil, count: 0)) - case .single(let index_): - var index = index_ - return try withUnsafePointer(to: &index) { (start) throws -> R in - return try body(UnsafeBufferPointer(start: start, count: 1)) - } - case .pair(let first, let second): - var pair = (first, second) - return try withUnsafeBytes(of: &pair) { (rawBuffer: UnsafeRawBufferPointer) throws -> R in - return try body(UnsafeBufferPointer(start: rawBuffer.baseAddress?.assumingMemoryBound(to: Int.self), count: 2)) - } - case .array(let indexes): - return try indexes.withUnsafeBufferPointer(body) - } - } - - var debugDescription: String { return description } - - static func +(lhs: Storage, rhs: Storage) -> Storage { - var res = lhs - res.append(contentsOf: rhs) - return res - } - - static func +(lhs: Storage, rhs: [Int]) -> Storage { - var res = lhs - res.append(contentsOf: rhs) - return res - } - - static func ==(lhs: Storage, rhs: Storage) -> Bool { - switch (lhs, rhs) { - case (.empty, .empty): - return true - case (.single(let lhsIndex), .single(let rhsIndex)): - return lhsIndex == rhsIndex - case (.pair(let lhsFirst, let lhsSecond), .pair(let rhsFirst, let rhsSecond)): - return lhsFirst == rhsFirst && lhsSecond == rhsSecond - case (.array(let lhsIndexes), .array(let rhsIndexes)): - return lhsIndexes == rhsIndexes - default: - return false - } - } - } - - fileprivate var _indexes : Storage - - /// Initialize an empty index path. - public init() { - _indexes = [] - } - - /// Initialize with a sequence of integers. - public init(indexes: ElementSequence) - where ElementSequence.Iterator.Element == Element { - _indexes = Storage(indexes.map { $0 }) - } - - /// Initialize with an array literal. - public init(arrayLiteral indexes: Element...) { - _indexes = Storage(indexes) - } - - /// Initialize with an array of elements. - public init(indexes: Array) { - _indexes = Storage(indexes) - } - - fileprivate init(storage: Storage) { - _indexes = storage - } - - /// Initialize with a single element. - public init(index: Element) { - _indexes = [index] - } - - /// Return a new `IndexPath` containing all but the last element. - public func dropLast() -> IndexPath { - return IndexPath(storage: _indexes.dropLast()) - } - - /// Append an `IndexPath` to `self`. - public mutating func append(_ other: IndexPath) { - _indexes.append(contentsOf: other._indexes) - } - - /// Append a single element to `self`. - public mutating func append(_ other: Element) { - _indexes.append(other) - } - - /// Append an array of elements to `self`. - public mutating func append(_ other: Array) { - _indexes.append(contentsOf: other) - } - - /// Return a new `IndexPath` containing the elements in self and the elements in `other`. - public func appending(_ other: Element) -> IndexPath { - var result = _indexes - result.append(other) - return IndexPath(storage: result) - } - - /// Return a new `IndexPath` containing the elements in self and the elements in `other`. - public func appending(_ other: IndexPath) -> IndexPath { - return IndexPath(storage: _indexes + other._indexes) - } - - /// Return a new `IndexPath` containing the elements in self and the elements in `other`. - public func appending(_ other: Array) -> IndexPath { - return IndexPath(storage: _indexes + other) - } - - public subscript(index: Index) -> Element { - get { - return _indexes[index] - } - set { - _indexes[index] = newValue - } - } - - public subscript(range: Range) -> IndexPath { - get { - return IndexPath(storage: _indexes[range]) - } - set { - _indexes[range] = newValue._indexes - } - } - - public func makeIterator() -> IndexingIterator { - return IndexingIterator(_elements: self) - } - - public var count: Int { - return _indexes.count - } - - public var startIndex: Index { - return _indexes.startIndex - } - - public var endIndex: Index { - return _indexes.endIndex - } - - public func index(before i: Index) -> Index { - return _indexes.index(before: i) - } - - public func index(after i: Index) -> Index { - return _indexes.index(after: i) - } - - /// Sorting an array of `IndexPath` using this comparison results in an array representing nodes in depth-first traversal order. - public func compare(_ other: IndexPath) -> ComparisonResult { - let thisLength = count - let otherLength = other.count - let length = Swift.min(thisLength, otherLength) - for idx in 0.. otherValue { - return .orderedDescending - } - } - if thisLength > otherLength { - return .orderedDescending - } else if thisLength < otherLength { - return .orderedAscending - } - return .orderedSame - } - - public func hash(into hasher: inout Hasher) { - // Note: We compare all indices in ==, so for proper hashing, we must - // also feed them all to the hasher. - // - // To ensure we have unique hash encodings in nested hashing contexts, - // we combine the count of indices as well as the indices themselves. - // (This matches what Array does.) - switch _indexes { - case .empty: - hasher.combine(0) - case let .single(index): - hasher.combine(1) - hasher.combine(index) - case let .pair(first, second): - hasher.combine(2) - hasher.combine(first) - hasher.combine(second) - case let .array(indexes): - hasher.combine(indexes.count) - for index in indexes { - hasher.combine(index) - } - } - } - - // MARK: - Bridging Helpers - - fileprivate init(nsIndexPath: __shared ReferenceType) { - let count = nsIndexPath.length - switch count { - case 0: - _indexes = [] - case 1: - _indexes = .single(nsIndexPath.index(atPosition: 0)) - case 2: - _indexes = .pair(nsIndexPath.index(atPosition: 0), nsIndexPath.index(atPosition: 1)) - default: - let indexes = Array(unsafeUninitializedCapacity: count) { buffer, initializedCount in - nsIndexPath.getIndexes(buffer.baseAddress!, range: NSRange(location: 0, length: count)) - initializedCount = count - } - _indexes = .array(indexes) - } - } - - fileprivate func makeReference() -> ReferenceType { - switch _indexes { - case .empty: - return ReferenceType() - case .single(let index): - return ReferenceType(index: index) - case .pair(let first, let second): - return _NSIndexPathCreateFromIndexes(first, second) as! ReferenceType - default: - return _indexes.withUnsafeBufferPointer { - return ReferenceType(indexes: $0.baseAddress, length: $0.count) - } - } - } - - public static func ==(lhs: IndexPath, rhs: IndexPath) -> Bool { - return lhs._indexes == rhs._indexes - } - - public static func +(lhs: IndexPath, rhs: IndexPath) -> IndexPath { - return lhs.appending(rhs) - } - - public static func +=(lhs: inout IndexPath, rhs: IndexPath) { - lhs.append(rhs) - } - - public static func <(lhs: IndexPath, rhs: IndexPath) -> Bool { - return lhs.compare(rhs) == ComparisonResult.orderedAscending - } - - public static func <=(lhs: IndexPath, rhs: IndexPath) -> Bool { - let order = lhs.compare(rhs) - return order == ComparisonResult.orderedAscending || order == ComparisonResult.orderedSame - } - - public static func >(lhs: IndexPath, rhs: IndexPath) -> Bool { - return lhs.compare(rhs) == ComparisonResult.orderedDescending - } - - public static func >=(lhs: IndexPath, rhs: IndexPath) -> Bool { - let order = lhs.compare(rhs) - return order == ComparisonResult.orderedDescending || order == ComparisonResult.orderedSame - } -} - -extension IndexPath : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - public var description: String { - return _indexes.description - } - - public var debugDescription: String { - return _indexes.debugDescription - } - - public var customMirror: Mirror { - return Mirror(self, unlabeledChildren: self, displayStyle: .collection) - } -} - -extension IndexPath : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSIndexPath.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSIndexPath { - return makeReference() - } - - public static func _forceBridgeFromObjectiveC(_ x: NSIndexPath, result: inout IndexPath?) { - result = IndexPath(nsIndexPath: x) - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSIndexPath, result: inout IndexPath?) -> Bool { - result = IndexPath(nsIndexPath: x) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSIndexPath?) -> IndexPath { - guard let src = source else { return IndexPath() } - return IndexPath(nsIndexPath: src) - } -} - -extension NSIndexPath : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as IndexPath) - } -} - -extension IndexPath : Codable { - private enum CodingKeys : Int, CodingKey { - case indexes - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - var indexesContainer = try container.nestedUnkeyedContainer(forKey: .indexes) - - var indexes = [Int]() - if let count = indexesContainer.count { - indexes.reserveCapacity(count) - } - - while !indexesContainer.isAtEnd { - let index = try indexesContainer.decode(Int.self) - indexes.append(index) - } - - self.init(indexes: indexes) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - var indexesContainer = container.nestedUnkeyedContainer(forKey: .indexes) - switch self._indexes { - case .empty: - break - case .single(let index): - try indexesContainer.encode(index) - case .pair(let first, let second): - try indexesContainer.encode(first) - try indexesContainer.encode(second) - case .array(let indexes): - try indexesContainer.encode(contentsOf: indexes) - } - } -} diff --git a/stdlib/public/Darwin/Foundation/IndexSet.swift b/stdlib/public/Darwin/Foundation/IndexSet.swift deleted file mode 100644 index c3fb0a82ca58e..0000000000000 --- a/stdlib/public/Darwin/Foundation/IndexSet.swift +++ /dev/null @@ -1,899 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -extension IndexSet.Index { - public static func ==(lhs: IndexSet.Index, rhs: IndexSet.Index) -> Bool { - return lhs.value == rhs.value - } - - public static func <(lhs: IndexSet.Index, rhs: IndexSet.Index) -> Bool { - return lhs.value < rhs.value - } - - public static func <=(lhs: IndexSet.Index, rhs: IndexSet.Index) -> Bool { - return lhs.value <= rhs.value - } - - public static func >(lhs: IndexSet.Index, rhs: IndexSet.Index) -> Bool { - return lhs.value > rhs.value - } - - public static func >=(lhs: IndexSet.Index, rhs: IndexSet.Index) -> Bool { - return lhs.value >= rhs.value - } -} - -extension IndexSet.RangeView { - public static func ==(lhs: IndexSet.RangeView, rhs: IndexSet.RangeView) -> Bool { - return lhs.startIndex == rhs.startIndex && lhs.endIndex == rhs.endIndex && lhs.indexSet == rhs.indexSet - } -} - -/// Manages a `Set` of integer values, which are commonly used as an index type in Cocoa API. -/// -/// The range of valid integer values is 0..?) { - if let r = range { - let otherIndexes = IndexSet(integersIn: r) - self.indexSet = indexSet.intersection(otherIndexes) - } else { - self.indexSet = indexSet - } - - self.startIndex = 0 - self.endIndex = self.indexSet._rangeCount - } - - public func makeIterator() -> IndexingIterator { - return IndexingIterator(_elements: self) - } - - public subscript(index : Index) -> Range { - let indexSetRange = indexSet._range(at: index) - return indexSetRange.lowerBound..) -> Slice { - return Slice(base: self, bounds: bounds) - } - - public func index(after i: Index) -> Index { - return i + 1 - } - - public func index(before i: Index) -> Index { - return i - 1 - } - - } - - /// The mechanism for accessing the integers stored in an IndexSet. - public struct Index : CustomStringConvertible, Comparable { - fileprivate var value: IndexSet.Element - fileprivate var extent: Range - fileprivate var rangeIndex: Int - fileprivate let rangeCount: Int - - fileprivate init(value: Int, extent: Range, rangeIndex: Int, rangeCount: Int) { - self.value = value - self.extent = extent - self.rangeCount = rangeCount - self.rangeIndex = rangeIndex - } - - public var description: String { - return "index \(value) in a range of \(extent) [range #\(rangeIndex + 1)/\(rangeCount)]" - } - } - - public typealias ReferenceType = NSIndexSet - public typealias Element = Int - - private var _handle: _MutablePairHandle - - /// Initialize an `IndexSet` with a range of integers. - public init(integersIn range: Range) { - _handle = _MutablePairHandle(NSIndexSet(indexesIn: _toNSRange(range)), copying: false) - } - - /// Initialize an `IndexSet` with a range of integers. - public init(integersIn range: R) where R.Bound == Element { - self.init(integersIn: range.relative(to: 0.. IndexingIterator { - return IndexingIterator(_elements: self) - } - - /// Returns a `Range`-based view of the entire contents of `self`. - /// - /// - seealso: rangeView(of:) - public var rangeView: RangeView { - return RangeView(indexSet: self, intersecting: nil) - } - - /// Returns a `Range`-based view of `self`. - /// - /// - parameter range: A subrange of `self` to view. - public func rangeView(of range : Range) -> RangeView { - return RangeView(indexSet: self, intersecting: range) - } - - /// Returns a `Range`-based view of `self`. - /// - /// - parameter range: A subrange of `self` to view. - public func rangeView(of range : R) -> RangeView where R.Bound == Element { - return self.rangeView(of: range.relative(to: 0.. RangeView.Index? { - let result = _handle.map { - __NSIndexSetIndexOfRangeContainingIndex($0, integer) - } - if result == NSNotFound { - return nil - } else { - return Int(result) - } - } - - private func _range(at index: RangeView.Index) -> Range { - return _handle.map { - var location: Int = 0 - var length: Int = 0 - __NSIndexSetRangeAtIndex($0, index, &location, &length) - return Int(location).. 0 { - // If this winds up being NSNotFound, that's ok because then endIndex is also NSNotFound, and empty collections have startIndex == endIndex - let extent = _range(at: 0) - return Index(value: extent.lowerBound, extent: extent, rangeIndex: 0, rangeCount: _rangeCount) - } else { - return Index(value: 0, extent: 0..<0, rangeIndex: -1, rangeCount: rangeCount) - } - } - - public var endIndex: Index { - let rangeCount = _rangeCount - let rangeIndex = rangeCount - 1 - let extent: Range - let value: Int - if rangeCount > 0 { - extent = _range(at: rangeCount - 1) - value = extent.upperBound // "1 past the end" position is the last range, 1 + the end of that range's extent - } else { - extent = 0..<0 - value = 0 - } - - return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount) - } - - public subscript(index : Index) -> Element { - return index.value - } - - public subscript(bounds: Range) -> Slice { - return Slice(base: self, bounds: bounds) - } - - // We adopt the default implementation of subscript(range: Range) from MutableCollection - - private func _toOptional(_ x : Int) -> Int? { - if x == NSNotFound { return nil } else { return x } - } - - /// Returns the first integer in `self`, or nil if `self` is empty. - public var first: Element? { - return _handle.map { _toOptional($0.firstIndex) } - } - - /// Returns the last integer in `self`, or nil if `self` is empty. - public var last: Element? { - return _handle.map { _toOptional($0.lastIndex) } - } - - /// Returns an integer contained in `self` which is greater than `integer`, or `nil` if a result could not be found. - public func integerGreaterThan(_ integer: Element) -> Element? { - return _handle.map { _toOptional($0.indexGreaterThanIndex(integer)) } - } - - /// Returns an integer contained in `self` which is less than `integer`, or `nil` if a result could not be found. - public func integerLessThan(_ integer: Element) -> Element? { - return _handle.map { _toOptional($0.indexLessThanIndex(integer)) } - } - - /// Returns an integer contained in `self` which is greater than or equal to `integer`, or `nil` if a result could not be found. - public func integerGreaterThanOrEqualTo(_ integer: Element) -> Element? { - return _handle.map { _toOptional($0.indexGreaterThanOrEqual(to: integer)) } - } - - /// Returns an integer contained in `self` which is less than or equal to `integer`, or `nil` if a result could not be found. - public func integerLessThanOrEqualTo(_ integer: Element) -> Element? { - return _handle.map { _toOptional($0.indexLessThanOrEqual(to: integer)) } - } - - /// Return a `Range` which can be used to subscript the index set. - /// - /// The resulting range is the range of the intersection of the integers in `range` with the index set. The resulting range will be `isEmpty` if the intersection is empty. - /// - /// - parameter range: The range of integers to include. - public func indexRange(in range: Range) -> Range { - guard !range.isEmpty, let first = first, let last = last else { - let i = _index(ofInteger: 0) - return i.. last || (range.upperBound - 1) < first { - let i = _index(ofInteger: 0) - return i..` which can be used to subscript the index set. - /// - /// The resulting range is the range of the intersection of the integers in `range` with the index set. - /// - /// - parameter range: The range of integers to include. - public func indexRange(in range: R) -> Range where R.Bound == Element { - return self.indexRange(in: range.relative(to: 0..) -> Int { - return _handle.map { $0.countOfIndexes(in: _toNSRange(range)) } - } - - /// Returns the count of integers in `self` that intersect `range`. - public func count(in range: R) -> Int where R.Bound == Element { - return self.count(in: range.relative(to: 0.. Bool { - return _handle.map { $0.contains(integer) } - } - - /// Returns `true` if `self` contains all of the integers in `range`. - public func contains(integersIn range: Range) -> Bool { - return _handle.map { $0.contains(in: _toNSRange(range)) } - } - - /// Returns `true` if `self` contains all of the integers in `range`. - public func contains(integersIn range: R) -> Bool where R.Bound == Element { - return self.contains(integersIn: range.relative(to: 0.. Bool { - return _handle.map { $0.contains(indexSet) } - } - - /// Returns `true` if `self` intersects any of the integers in `range`. - public func intersects(integersIn range: Range) -> Bool { - return _handle.map { $0.intersects(in: _toNSRange(range)) } - } - - /// Returns `true` if `self` intersects any of the integers in `range`. - public func intersects(integersIn range: R) -> Bool where R.Bound == Element { - return self.intersects(integersIn: range.relative(to: 0.. Index { - if i.value + 1 == i.extent.upperBound { - // Move to the next range - if i.rangeIndex + 1 == i.rangeCount { - // We have no more to go; return a 'past the end' index - return Index(value: i.value + 1, extent: i.extent, rangeIndex: i.rangeIndex, rangeCount: i.rangeCount) - } else { - let rangeIndex = i.rangeIndex + 1 - let rangeCount = i.rangeCount - let extent = _range(at: rangeIndex) - let value = extent.lowerBound - return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount) - } - } else { - // Move to the next value in this range - return Index(value: i.value + 1, extent: i.extent, rangeIndex: i.rangeIndex, rangeCount: i.rangeCount) - } - } - - public func formIndex(after i: inout Index) { - if i.value + 1 == i.extent.upperBound { - // Move to the next range - if i.rangeIndex + 1 == i.rangeCount { - // We have no more to go; return a 'past the end' index - i.value += 1 - } else { - i.rangeIndex += 1 - i.extent = _range(at: i.rangeIndex) - i.value = i.extent.lowerBound - } - } else { - // Move to the next value in this range - i.value += 1 - } - } - - public func index(before i: Index) -> Index { - if i.value == i.extent.lowerBound { - // Move to the next range - if i.rangeIndex == 0 { - // We have no more to go - return Index(value: i.value, extent: i.extent, rangeIndex: i.rangeIndex, rangeCount: i.rangeCount) - } else { - let rangeIndex = i.rangeIndex - 1 - let rangeCount = i.rangeCount - let extent = _range(at: rangeIndex) - let value = extent.upperBound - 1 - return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount) - } - } else { - // Move to the previous value in this range - return Index(value: i.value - 1, extent: i.extent, rangeIndex: i.rangeIndex, rangeCount: i.rangeCount) - } - } - - public func formIndex(before i: inout Index) { - if i.value == i.extent.lowerBound { - // Move to the next range - if i.rangeIndex == 0 { - // We have no more to go - } else { - i.rangeIndex -= 1 - i.extent = _range(at: i.rangeIndex) - i.value = i.extent.upperBound - 1 - } - } else { - // Move to the previous value in this range - i.value -= 1 - } - } - - private func _index(ofInteger integer: Element) -> Index { - let rangeCount = _rangeCount - let value = integer - if let rangeIndex = _indexOfRange(containing: integer) { - let extent = _range(at: rangeIndex) - let rangeIndex = rangeIndex - return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount) - } else { - let extent = 0..<0 - let rangeIndex = 0 - return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount) - } - } - - // MARK: - - // MARK: SetAlgebra - - /// Union the `IndexSet` with `other`. - public mutating func formUnion(_ other: IndexSet) { - self = self.union(other) - } - - /// Union the `IndexSet` with `other`. - public func union(_ other: IndexSet) -> IndexSet { - var result: IndexSet - var dense: IndexSet - - // Prepare to make a copy of the more sparse IndexSet to prefer copy over repeated inserts - if self.rangeView.count > other.rangeView.count { - result = self - dense = other - } else { - result = other - dense = self - } - - // Insert each range from the less sparse IndexSet - dense.rangeView.forEach { - result.insert(integersIn: $0) - } - - return result - } - - /// Exclusive or the `IndexSet` with `other`. - public func symmetricDifference(_ other: IndexSet) -> IndexSet { - var result = IndexSet() - var boundaryIterator = IndexSetBoundaryIterator(self, other) - var flag = false - var start = 0 - - while let i = boundaryIterator.next() { - if !flag { - // Start a range if one set contains but not the other. - if self.contains(i) != other.contains(i) { - flag = true - start = i - } - } else { - // End a range if both sets contain or both sets do not contain. - if self.contains(i) == other.contains(i) { - flag = false - result.insert(integersIn: start.. IndexSet { - var result = IndexSet() - var boundaryIterator = IndexSetBoundaryIterator(self, other) - var flag = false - var start = 0 - - while let i = boundaryIterator.next() { - if !flag { - // If both sets contain then start a range. - if self.contains(i) && other.contains(i) { - flag = true - start = i - } - } else { - // If both sets do not contain then end a range. - if !self.contains(i) || !other.contains(i) { - flag = false - result.insert(integersIn: start.. (inserted: Bool, memberAfterInsert: Element) { - _applyMutation { $0.add(integer) } - // TODO: figure out how to return the truth here - return (true, integer) - } - - /// Insert an integer into the `IndexSet`. - @discardableResult - public mutating func update(with integer: Element) -> Element? { - _applyMutation { $0.add(integer) } - // TODO: figure out how to return the truth here - return integer - } - - - /// Remove an integer from the `IndexSet`. - @discardableResult - public mutating func remove(_ integer: Element) -> Element? { - // TODO: Add method to NSIndexSet to do this in one call - let result : Element? = contains(integer) ? integer : nil - _applyMutation { $0.remove(integer) } - return result - } - - // MARK: - - - /// Remove all values from the `IndexSet`. - public mutating func removeAll() { - _applyMutation { $0.removeAllIndexes() } - } - - /// Insert a range of integers into the `IndexSet`. - public mutating func insert(integersIn range: Range) { - _applyMutation { $0.add(in: _toNSRange(range)) } - } - - /// Insert a range of integers into the `IndexSet`. - public mutating func insert(integersIn range: R) where R.Bound == Element { - self.insert(integersIn: range.relative(to: 0..) { - _applyMutation { $0.remove(in: _toNSRange(range)) } - } - - /// Remove a range of integers from the `IndexSet`. - public mutating func remove(integersIn range: ClosedRange) { - self.remove(integersIn: Range(range)) - } - - /// Returns `true` if self contains no values. - public var isEmpty : Bool { - return self.count == 0 - } - - /// Returns an IndexSet filtered according to the result of `includeInteger`. - /// - /// - parameter range: A range of integers. For each integer in the range that intersects the integers in the IndexSet, then the `includeInteger` predicate will be invoked. - /// - parameter includeInteger: The predicate which decides if an integer will be included in the result or not. - public func filteredIndexSet(in range : Range, includeInteger: (Element) throws -> Bool) rethrows -> IndexSet { - let r : NSRange = _toNSRange(range) - return try _handle.map { - var error: Error? - let result = $0.indexes(in: r, options: [], passingTest: { (i, stop) -> Bool in - do { - let include = try includeInteger(i) - return include - } catch let e { - error = e - stop.pointee = true - return false - } - }) - if let e = error { - throw e - } else { - return result - } - } - } - - /// Returns an IndexSet filtered according to the result of `includeInteger`. - /// - /// - parameter range: A range of integers. For each integer in the range that intersects the integers in the IndexSet, then the `includeInteger` predicate will be invoked. - /// - parameter includeInteger: The predicate which decides if an integer will be included in the result or not. - public func filteredIndexSet(in range : ClosedRange, includeInteger: (Element) throws -> Bool) rethrows -> IndexSet { - return try self.filteredIndexSet(in: Range(range), includeInteger: includeInteger) - } - - /// Returns an IndexSet filtered according to the result of `includeInteger`. - /// - /// - parameter includeInteger: The predicate which decides if an integer will be included in the result or not. - public func filteredIndexSet(includeInteger: (Element) throws -> Bool) rethrows -> IndexSet { - return try self.filteredIndexSet(in: 0..(_ whatToDo : (NSMutableIndexSet) throws -> ReturnType) rethrows -> ReturnType { - // This check is done twice because: Value kept live for too long causing uniqueness check to fail - var unique = true - switch _handle._pointer { - case .Default(_): - break - case .Mutable(_): - unique = isKnownUniquelyReferenced(&_handle) - } - - switch _handle._pointer { - case .Default(let i): - // We need to become mutable; by creating a new box we also become unique - let copy = i.mutableCopy() as! NSMutableIndexSet - // Be sure to set the _handle before calling out; otherwise references to the struct in the closure may be looking at the old _handle - _handle = _MutablePairHandle(copy, copying: false) - let result = try whatToDo(copy) - return result - case .Mutable(let m): - // Only create a new box if we are not uniquely referenced - if !unique { - let copy = m.mutableCopy() as! NSMutableIndexSet - _handle = _MutablePairHandle(copy, copying: false) - let result = try whatToDo(copy) - return result - } else { - return try whatToDo(m) - } - } - } - - // MARK: - Bridging Support - - private var reference: NSIndexSet { - return _handle.reference - } - - private init(reference: __shared NSIndexSet) { - _handle = _MutablePairHandle(reference) - } -} - -extension IndexSet : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - public var description: String { - return "\(count) indexes" - } - - public var debugDescription: String { - return "\(count) indexes" - } - - public var customMirror: Mirror { - var c: [(label: String?, value: Any)] = [] - c.append((label: "ranges", value: Array(rangeView))) - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - -/// Iterate two index sets on the boundaries of their ranges. This is where all of the interesting stuff happens for exclusive or, intersect, etc. -private struct IndexSetBoundaryIterator : IteratorProtocol { - typealias Element = IndexSet.Element - - private var i1: IndexSet.RangeView.Iterator - private var i2: IndexSet.RangeView.Iterator - private var i1Range: Range? - private var i2Range: Range? - private var i1UsedLower: Bool - private var i2UsedLower: Bool - - init(_ is1: IndexSet, _ is2: IndexSet) { - i1 = is1.rangeView.makeIterator() - i2 = is2.rangeView.makeIterator() - - i1Range = i1.next() - i2Range = i2.next() - - // A sort of cheap iterator on [i1Range.lowerBound, i1Range.upperBound] - i1UsedLower = false - i2UsedLower = false - } - - mutating func next() -> Element? { - if i1Range == nil && i2Range == nil { - return nil - } - - let nextIn1: Element - if let r = i1Range { - nextIn1 = i1UsedLower ? r.upperBound : r.lowerBound - } else { - nextIn1 = Int.max - } - - let nextIn2: Element - if let r = i2Range { - nextIn2 = i2UsedLower ? r.upperBound : r.lowerBound - } else { - nextIn2 = Int.max - } - - var result: Element - if nextIn1 <= nextIn2 { - // 1 has the next element, or they are the same. - result = nextIn1 - if i1UsedLower { i1Range = i1.next() } - // We need to iterate both the value from is1 and is2 in the == case. - if result == nextIn2 { - if i2UsedLower { i2Range = i2.next() } - i2UsedLower = !i2UsedLower - } - i1UsedLower = !i1UsedLower - } else { - // 2 has the next element - result = nextIn2 - if i2UsedLower { i2Range = i2.next() } - i2UsedLower = !i2UsedLower - } - - return result - } -} - -extension IndexSet { - public static func ==(lhs: IndexSet, rhs: IndexSet) -> Bool { - return lhs._handle.map { $0.isEqual(to: rhs) } - } -} - -private func _toNSRange(_ r: Range) -> NSRange { - return NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound) -} - -extension IndexSet : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSIndexSet.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSIndexSet { - return reference - } - - public static func _forceBridgeFromObjectiveC(_ x: NSIndexSet, result: inout IndexSet?) { - result = IndexSet(reference: x) - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSIndexSet, result: inout IndexSet?) -> Bool { - result = IndexSet(reference: x) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSIndexSet?) -> IndexSet { - guard let src = source else { return IndexSet() } - return IndexSet(reference: src) - } - -} - -extension NSIndexSet : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as IndexSet) - } -} - -// MARK: Protocol - -// TODO: This protocol should be replaced with a native Swift object like the other Foundation bridged types. However, NSIndexSet does not have an abstract zero-storage base class like NSCharacterSet, NSData, and NSAttributedString. Therefore the same trick of laying it out with Swift ref counting does not work.and -/// Holds either the immutable or mutable version of a Foundation type. -/// -/// In many cases, the immutable type has optimizations which make it preferred when we know we do not need mutation. -private enum _MutablePair { - case Default(ImmutableType) - case Mutable(MutableType) -} - -/// A class type which acts as a handle (pointer-to-pointer) to a Foundation reference type which has both an immutable and mutable class (e.g., NSData, NSMutableData). -/// -/// a.k.a. Box -private final class _MutablePairHandle - where ImmutableType : NSMutableCopying, MutableType : NSMutableCopying { - var _pointer: _MutablePair - - /// Initialize with an immutable reference instance. - /// - /// - parameter immutable: The thing to stash. - /// - parameter copying: Should be true unless you just created the instance (or called copy) and want to transfer ownership to this handle. - init(_ immutable: ImmutableType, copying: Bool = true) { - if copying { - self._pointer = _MutablePair.Default(immutable.copy() as! ImmutableType) - } else { - self._pointer = _MutablePair.Default(immutable) - } - } - - /// Initialize with a mutable reference instance. - /// - /// - parameter mutable: The thing to stash. - /// - parameter copying: Should be true unless you just created the instance (or called copy) and want to transfer ownership to this handle. - init(_ mutable: MutableType, copying: Bool = true) { - if copying { - self._pointer = _MutablePair.Mutable(mutable.mutableCopy() as! MutableType) - } else { - self._pointer = _MutablePair.Mutable(mutable) - } - } - - /// Apply a closure to the reference type, regardless if it is mutable or immutable. - @inline(__always) - func map(_ whatToDo: (ImmutableType) throws -> ReturnType) rethrows -> ReturnType { - switch _pointer { - case .Default(let i): - return try whatToDo(i) - case .Mutable(let m): - // TODO: It should be possible to reflect the constraint that MutableType is a subtype of ImmutableType in the generics for the class, but I haven't figured out how yet. For now, cheat and unsafe bit cast. - return try whatToDo(unsafeDowncast(m, to: ImmutableType.self)) - } - } - - var reference: ImmutableType { - switch _pointer { - case .Default(let i): - return i - case .Mutable(let m): - // TODO: It should be possible to reflect the constraint that MutableType is a subtype of ImmutableType in the generics for the class, but I haven't figured out how yet. For now, cheat and unsafe bit cast. - return unsafeDowncast(m, to: ImmutableType.self) - } - } -} - -extension IndexSet : Codable { - private enum CodingKeys : Int, CodingKey { - case indexes - } - - private enum RangeCodingKeys : Int, CodingKey { - case location - case length - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - var indexesContainer = try container.nestedUnkeyedContainer(forKey: .indexes) - self.init() - - while !indexesContainer.isAtEnd { - let rangeContainer = try indexesContainer.nestedContainer(keyedBy: RangeCodingKeys.self) - let startIndex = try rangeContainer.decode(Int.self, forKey: .location) - let count = try rangeContainer.decode(Int.self, forKey: .length) - self.insert(integersIn: startIndex ..< (startIndex + count)) - } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - var indexesContainer = container.nestedUnkeyedContainer(forKey: .indexes) - - for range in self.rangeView { - var rangeContainer = indexesContainer.nestedContainer(keyedBy: RangeCodingKeys.self) - try rangeContainer.encode(range.startIndex, forKey: .location) - try rangeContainer.encode(range.count, forKey: .length) - } - } -} diff --git a/stdlib/public/Darwin/Foundation/JSONEncoder.swift b/stdlib/public/Darwin/Foundation/JSONEncoder.swift deleted file mode 100644 index e149c981ca7c0..0000000000000 --- a/stdlib/public/Darwin/Foundation/JSONEncoder.swift +++ /dev/null @@ -1,2583 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -/// A marker protocol used to determine whether a value is a `String`-keyed `Dictionary` -/// containing `Encodable` values (in which case it should be exempt from key conversion strategies). -/// -/// NOTE: The architecture and environment check is due to a bug in the current (2018-08-08) Swift 4.2 -/// runtime when running on i386 simulator. The issue is tracked in https://bugs.swift.org/browse/SR-8276 -/// Making the protocol `internal` instead of `private` works around this issue. -/// Once SR-8276 is fixed, this check can be removed and the protocol always be made private. -#if arch(i386) || arch(arm) -internal protocol _JSONStringDictionaryEncodableMarker { } -#else -private protocol _JSONStringDictionaryEncodableMarker { } -#endif - -extension Dictionary : _JSONStringDictionaryEncodableMarker where Key == String, Value: Encodable { } - -/// A marker protocol used to determine whether a value is a `String`-keyed `Dictionary` -/// containing `Decodable` values (in which case it should be exempt from key conversion strategies). -/// -/// The marker protocol also provides access to the type of the `Decodable` values, -/// which is needed for the implementation of the key conversion strategy exemption. -/// -/// NOTE: Please see comment above regarding SR-8276 -#if arch(i386) || arch(arm) -internal protocol _JSONStringDictionaryDecodableMarker { - static var elementType: Decodable.Type { get } -} -#else -private protocol _JSONStringDictionaryDecodableMarker { - static var elementType: Decodable.Type { get } -} -#endif - -extension Dictionary : _JSONStringDictionaryDecodableMarker where Key == String, Value: Decodable { - static var elementType: Decodable.Type { return Value.self } -} - -//===----------------------------------------------------------------------===// -// JSON Encoder -//===----------------------------------------------------------------------===// - -/// `JSONEncoder` facilitates the encoding of `Encodable` values into JSON. -// NOTE: older overlays had Foundation.JSONEncoder as the ObjC name. -// The two must coexist, so it was renamed. The old name must not be -// used in the new runtime. _TtC10Foundation13__JSONEncoder is the -// mangled name for Foundation.__JSONEncoder. -@_objcRuntimeName(_TtC10Foundation13__JSONEncoder) -open class JSONEncoder { - // MARK: Options - - /// The formatting of the output JSON data. - public struct OutputFormatting : OptionSet { - /// The format's default value. - public let rawValue: UInt - - /// Creates an OutputFormatting value with the given raw value. - public init(rawValue: UInt) { - self.rawValue = rawValue - } - - /// Produce human-readable JSON with indented output. - public static let prettyPrinted = OutputFormatting(rawValue: 1 << 0) - - /// Produce JSON with dictionary keys sorted in lexicographic order. - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public static let sortedKeys = OutputFormatting(rawValue: 1 << 1) - - /// By default slashes get escaped ("/" → "\/", "http://apple.com/" → "http:\/\/apple.com\/") - /// for security reasons, allowing outputted JSON to be safely embedded within HTML/XML. - /// In contexts where this escaping is unnecessary, the JSON is known to not be embedded, - /// or is intended only for display, this option avoids this escaping. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public static let withoutEscapingSlashes = OutputFormatting(rawValue: 1 << 3) - } - - /// The strategy to use for encoding `Date` values. - public enum DateEncodingStrategy { - /// Defer to `Date` for choosing an encoding. This is the default strategy. - case deferredToDate - - /// Encode the `Date` as a UNIX timestamp (as a JSON number). - case secondsSince1970 - - /// Encode the `Date` as UNIX millisecond timestamp (as a JSON number). - case millisecondsSince1970 - - /// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format). - @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) - case iso8601 - - /// Encode the `Date` as a string formatted by the given formatter. - case formatted(DateFormatter) - - /// Encode the `Date` as a custom value encoded by the given closure. - /// - /// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place. - case custom((Date, Encoder) throws -> Void) - } - - /// The strategy to use for encoding `Data` values. - public enum DataEncodingStrategy { - /// Defer to `Data` for choosing an encoding. - case deferredToData - - /// Encoded the `Data` as a Base64-encoded string. This is the default strategy. - case base64 - - /// Encode the `Data` as a custom value encoded by the given closure. - /// - /// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place. - case custom((Data, Encoder) throws -> Void) - } - - /// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN). - public enum NonConformingFloatEncodingStrategy { - /// Throw upon encountering non-conforming values. This is the default strategy. - case `throw` - - /// Encode the values using the given representation strings. - case convertToString(positiveInfinity: String, negativeInfinity: String, nan: String) - } - - /// The strategy to use for automatically changing the value of keys before encoding. - public enum KeyEncodingStrategy { - /// Use the keys specified by each type. This is the default strategy. - case useDefaultKeys - - /// Convert from "camelCaseKeys" to "snake_case_keys" before writing a key to JSON payload. - /// - /// Capital characters are determined by testing membership in `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` (Unicode General Categories Lu and Lt). - /// The conversion to lower case uses `Locale.system`, also known as the ICU "root" locale. This means the result is consistent regardless of the current user's locale and language preferences. - /// - /// Converting from camel case to snake case: - /// 1. Splits words at the boundary of lower-case to upper-case - /// 2. Inserts `_` between words - /// 3. Lowercases the entire string - /// 4. Preserves starting and ending `_`. - /// - /// For example, `oneTwoThree` becomes `one_two_three`. `_oneTwoThree_` becomes `_one_two_three_`. - /// - /// - Note: Using a key encoding strategy has a nominal performance cost, as each string key has to be converted. - case convertToSnakeCase - - /// Provide a custom conversion to the key in the encoded JSON from the keys specified by the encoded types. - /// The full path to the current encoding position is provided for context (in case you need to locate this key within the payload). The returned key is used in place of the last component in the coding path before encoding. - /// If the result of the conversion is a duplicate key, then only one value will be present in the result. - case custom((_ codingPath: [CodingKey]) -> CodingKey) - - fileprivate static func _convertToSnakeCase(_ stringKey: String) -> String { - guard !stringKey.isEmpty else { return stringKey } - - var words : [Range] = [] - // The general idea of this algorithm is to split words on transition from lower to upper case, then on transition of >1 upper case characters to lowercase - // - // myProperty -> my_property - // myURLProperty -> my_url_property - // - // We assume, per Swift naming conventions, that the first character of the key is lowercase. - var wordStart = stringKey.startIndex - var searchRange = stringKey.index(after: wordStart)..1 capital letters. Turn those into a word, stopping at the capital before the lower case character. - let beforeLowerIndex = stringKey.index(before: lowerCaseRange.lowerBound) - words.append(upperCaseRange.lowerBound..(_ value: T) throws -> Data { - let encoder = __JSONEncoder(options: self.options) - - guard let topLevel = try encoder.box_(value) else { - throw EncodingError.invalidValue(value, - EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) did not encode any values.")) - } - - let writingOptions = JSONSerialization.WritingOptions(rawValue: self.outputFormatting.rawValue).union(.fragmentsAllowed) - do { - return try JSONSerialization.data(withJSONObject: topLevel, options: writingOptions) - } catch { - throw EncodingError.invalidValue(value, - EncodingError.Context(codingPath: [], debugDescription: "Unable to encode the given top-level value to JSON.", underlyingError: error)) - } - } -} - -// MARK: - __JSONEncoder - -// NOTE: older overlays called this class _JSONEncoder. -// The two must coexist without a conflicting ObjC class name, so it -// was renamed. The old name must not be used in the new runtime. -private class __JSONEncoder : Encoder { - // MARK: Properties - - /// The encoder's storage. - var storage: _JSONEncodingStorage - - /// Options set on the top-level encoder. - let options: JSONEncoder._Options - - /// The path to the current point in encoding. - public var codingPath: [CodingKey] - - /// Contextual user-provided information for use during encoding. - public var userInfo: [CodingUserInfoKey : Any] { - return self.options.userInfo - } - - // MARK: - Initialization - - /// Initializes `self` with the given top-level encoder options. - init(options: JSONEncoder._Options, codingPath: [CodingKey] = []) { - self.options = options - self.storage = _JSONEncodingStorage() - self.codingPath = codingPath - } - - /// Returns whether a new element can be encoded at this coding path. - /// - /// `true` if an element has not yet been encoded at this coding path; `false` otherwise. - var canEncodeNewValue: Bool { - // Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container). - // At the same time, every time a container is requested, a new value gets pushed onto the storage stack. - // If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition. - // - // This means that anytime something that can request a new container goes onto the stack, we MUST push a key onto the coding path. - // Things which will not request containers do not need to have the coding path extended for them (but it doesn't matter if it is, because they will not reach here). - return self.storage.count == self.codingPath.count - } - - // MARK: - Encoder Methods - public func container(keyedBy: Key.Type) -> KeyedEncodingContainer { - // If an existing keyed container was already requested, return that one. - let topContainer: NSMutableDictionary - if self.canEncodeNewValue { - // We haven't yet pushed a container at this level; do so here. - topContainer = self.storage.pushKeyedContainer() - } else { - guard let container = self.storage.containers.last as? NSMutableDictionary else { - preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.") - } - - topContainer = container - } - - let container = _JSONKeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer) - return KeyedEncodingContainer(container) - } - - public func unkeyedContainer() -> UnkeyedEncodingContainer { - // If an existing unkeyed container was already requested, return that one. - let topContainer: NSMutableArray - if self.canEncodeNewValue { - // We haven't yet pushed a container at this level; do so here. - topContainer = self.storage.pushUnkeyedContainer() - } else { - guard let container = self.storage.containers.last as? NSMutableArray else { - preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.") - } - - topContainer = container - } - - return _JSONUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer) - } - - public func singleValueContainer() -> SingleValueEncodingContainer { - return self - } -} - -// MARK: - Encoding Storage and Containers - -private struct _JSONEncodingStorage { - // MARK: Properties - - /// The container stack. - /// Elements may be any one of the JSON types (NSNull, NSNumber, NSString, NSArray, NSDictionary). - private(set) var containers: [NSObject] = [] - - // MARK: - Initialization - - /// Initializes `self` with no containers. - init() {} - - // MARK: - Modifying the Stack - - var count: Int { - return self.containers.count - } - - mutating func pushKeyedContainer() -> NSMutableDictionary { - let dictionary = NSMutableDictionary() - self.containers.append(dictionary) - return dictionary - } - - mutating func pushUnkeyedContainer() -> NSMutableArray { - let array = NSMutableArray() - self.containers.append(array) - return array - } - - mutating func push(container: __owned NSObject) { - self.containers.append(container) - } - - mutating func popContainer() -> NSObject { - precondition(!self.containers.isEmpty, "Empty container stack.") - return self.containers.popLast()! - } -} - -// MARK: - Encoding Containers - -private struct _JSONKeyedEncodingContainer : KeyedEncodingContainerProtocol { - typealias Key = K - - // MARK: Properties - - /// A reference to the encoder we're writing to. - private let encoder: __JSONEncoder - - /// A reference to the container we're writing to. - private let container: NSMutableDictionary - - /// The path of coding keys taken to get to this point in encoding. - private(set) public var codingPath: [CodingKey] - - // MARK: - Initialization - - /// Initializes `self` with the given references. - init(referencing encoder: __JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) { - self.encoder = encoder - self.codingPath = codingPath - self.container = container - } - - // MARK: - Coding Path Operations - - private func _converted(_ key: CodingKey) -> CodingKey { - switch encoder.options.keyEncodingStrategy { - case .useDefaultKeys: - return key - case .convertToSnakeCase: - let newKeyString = JSONEncoder.KeyEncodingStrategy._convertToSnakeCase(key.stringValue) - return _JSONKey(stringValue: newKeyString, intValue: key.intValue) - case .custom(let converter): - return converter(codingPath + [key]) - } - } - - // MARK: - KeyedEncodingContainerProtocol Methods - - public mutating func encodeNil(forKey key: Key) throws { - self.container[_converted(key).stringValue] = NSNull() - } - public mutating func encode(_ value: Bool, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: Int, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: Int8, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: Int16, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: Int32, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: Int64, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: UInt, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: UInt8, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: UInt16, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: UInt32, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: UInt64, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - public mutating func encode(_ value: String, forKey key: Key) throws { - self.container[_converted(key).stringValue] = self.encoder.box(value) - } - - public mutating func encode(_ value: Float, forKey key: Key) throws { - // Since the float may be invalid and throw, the coding path needs to contain this key. - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - self.container[_converted(key).stringValue] = try self.encoder.box(value) - } - - public mutating func encode(_ value: Double, forKey key: Key) throws { - // Since the double may be invalid and throw, the coding path needs to contain this key. - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - self.container[_converted(key).stringValue] = try self.encoder.box(value) - } - - public mutating func encode(_ value: T, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - self.container[_converted(key).stringValue] = try self.encoder.box(value) - } - - public mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { - let containerKey = _converted(key).stringValue - let dictionary: NSMutableDictionary - if let existingContainer = self.container[containerKey] { - precondition( - existingContainer is NSMutableDictionary, - "Attempt to re-encode into nested KeyedEncodingContainer<\(Key.self)> for key \"\(containerKey)\" is invalid: non-keyed container already encoded for this key" - ) - dictionary = existingContainer as! NSMutableDictionary - } else { - dictionary = NSMutableDictionary() - self.container[containerKey] = dictionary - } - - self.codingPath.append(key) - defer { self.codingPath.removeLast() } - - let container = _JSONKeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary) - return KeyedEncodingContainer(container) - } - - public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - let containerKey = _converted(key).stringValue - let array: NSMutableArray - if let existingContainer = self.container[containerKey] { - precondition( - existingContainer is NSMutableArray, - "Attempt to re-encode into nested UnkeyedEncodingContainer for key \"\(containerKey)\" is invalid: keyed container/single value already encoded for this key" - ) - array = existingContainer as! NSMutableArray - } else { - array = NSMutableArray() - self.container[containerKey] = array - } - - self.codingPath.append(key) - defer { self.codingPath.removeLast() } - return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array) - } - - public mutating func superEncoder() -> Encoder { - return __JSONReferencingEncoder(referencing: self.encoder, key: _JSONKey.super, convertedKey: _converted(_JSONKey.super), wrapping: self.container) - } - - public mutating func superEncoder(forKey key: Key) -> Encoder { - return __JSONReferencingEncoder(referencing: self.encoder, key: key, convertedKey: _converted(key), wrapping: self.container) - } -} - -private struct _JSONUnkeyedEncodingContainer : UnkeyedEncodingContainer { - // MARK: Properties - - /// A reference to the encoder we're writing to. - private let encoder: __JSONEncoder - - /// A reference to the container we're writing to. - private let container: NSMutableArray - - /// The path of coding keys taken to get to this point in encoding. - private(set) public var codingPath: [CodingKey] - - /// The number of elements encoded into the container. - public var count: Int { - return self.container.count - } - - // MARK: - Initialization - - /// Initializes `self` with the given references. - init(referencing encoder: __JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) { - self.encoder = encoder - self.codingPath = codingPath - self.container = container - } - - // MARK: - UnkeyedEncodingContainer Methods - - public mutating func encodeNil() throws { self.container.add(NSNull()) } - public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) } - - public mutating func encode(_ value: Float) throws { - // Since the float may be invalid and throw, the coding path needs to contain this key. - self.encoder.codingPath.append(_JSONKey(index: self.count)) - defer { self.encoder.codingPath.removeLast() } - self.container.add(try self.encoder.box(value)) - } - - public mutating func encode(_ value: Double) throws { - // Since the double may be invalid and throw, the coding path needs to contain this key. - self.encoder.codingPath.append(_JSONKey(index: self.count)) - defer { self.encoder.codingPath.removeLast() } - self.container.add(try self.encoder.box(value)) - } - - public mutating func encode(_ value: T) throws { - self.encoder.codingPath.append(_JSONKey(index: self.count)) - defer { self.encoder.codingPath.removeLast() } - self.container.add(try self.encoder.box(value)) - } - - public mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer { - self.codingPath.append(_JSONKey(index: self.count)) - defer { self.codingPath.removeLast() } - - let dictionary = NSMutableDictionary() - self.container.add(dictionary) - - let container = _JSONKeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary) - return KeyedEncodingContainer(container) - } - - public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - self.codingPath.append(_JSONKey(index: self.count)) - defer { self.codingPath.removeLast() } - - let array = NSMutableArray() - self.container.add(array) - return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array) - } - - public mutating func superEncoder() -> Encoder { - return __JSONReferencingEncoder(referencing: self.encoder, at: self.container.count, wrapping: self.container) - } -} - -extension __JSONEncoder : SingleValueEncodingContainer { - // MARK: - SingleValueEncodingContainer Methods - - private func assertCanEncodeNewValue() { - precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.") - } - - public func encodeNil() throws { - assertCanEncodeNewValue() - self.storage.push(container: NSNull()) - } - - public func encode(_ value: Bool) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int8) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int16) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int32) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int64) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt8) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt16) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt32) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt64) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: String) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Float) throws { - assertCanEncodeNewValue() - try self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Double) throws { - assertCanEncodeNewValue() - try self.storage.push(container: self.box(value)) - } - - public func encode(_ value: T) throws { - assertCanEncodeNewValue() - try self.storage.push(container: self.box(value)) - } -} - -// MARK: - Concrete Value Representations - -private extension __JSONEncoder { - /// Returns the given value boxed in a container appropriate for pushing onto the container stack. - func box(_ value: Bool) -> NSObject { return NSNumber(value: value) } - func box(_ value: Int) -> NSObject { return NSNumber(value: value) } - func box(_ value: Int8) -> NSObject { return NSNumber(value: value) } - func box(_ value: Int16) -> NSObject { return NSNumber(value: value) } - func box(_ value: Int32) -> NSObject { return NSNumber(value: value) } - func box(_ value: Int64) -> NSObject { return NSNumber(value: value) } - func box(_ value: UInt) -> NSObject { return NSNumber(value: value) } - func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) } - func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) } - func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) } - func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) } - func box(_ value: String) -> NSObject { return NSString(string: value) } - - func box(_ float: Float) throws -> NSObject { - guard !float.isInfinite && !float.isNaN else { - guard case let .convertToString(positiveInfinity: posInfString, - negativeInfinity: negInfString, - nan: nanString) = self.options.nonConformingFloatEncodingStrategy else { - throw EncodingError._invalidFloatingPointValue(float, at: codingPath) - } - - if float == Float.infinity { - return NSString(string: posInfString) - } else if float == -Float.infinity { - return NSString(string: negInfString) - } else { - return NSString(string: nanString) - } - } - - return NSNumber(value: float) - } - - func box(_ double: Double) throws -> NSObject { - guard !double.isInfinite && !double.isNaN else { - guard case let .convertToString(positiveInfinity: posInfString, - negativeInfinity: negInfString, - nan: nanString) = self.options.nonConformingFloatEncodingStrategy else { - throw EncodingError._invalidFloatingPointValue(double, at: codingPath) - } - - if double == Double.infinity { - return NSString(string: posInfString) - } else if double == -Double.infinity { - return NSString(string: negInfString) - } else { - return NSString(string: nanString) - } - } - - return NSNumber(value: double) - } - - func box(_ date: Date) throws -> NSObject { - switch self.options.dateEncodingStrategy { - case .deferredToDate: - // Must be called with a surrounding with(pushedKey:) call. - // Dates encode as single-value objects; this can't both throw and push a container, so no need to catch the error. - try date.encode(to: self) - return self.storage.popContainer() - - case .secondsSince1970: - return NSNumber(value: date.timeIntervalSince1970) - - case .millisecondsSince1970: - return NSNumber(value: 1000.0 * date.timeIntervalSince1970) - - case .iso8601: - if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - return NSString(string: _iso8601Formatter.string(from: date)) - } else { - fatalError("ISO8601DateFormatter is unavailable on this platform.") - } - - case .formatted(let formatter): - return NSString(string: formatter.string(from: date)) - - case .custom(let closure): - let depth = self.storage.count - do { - try closure(date, self) - } catch { - // If the value pushed a container before throwing, pop it back off to restore state. - if self.storage.count > depth { - let _ = self.storage.popContainer() - } - - throw error - } - - guard self.storage.count > depth else { - // The closure didn't encode anything. Return the default keyed container. - return NSDictionary() - } - - // We can pop because the closure encoded something. - return self.storage.popContainer() - } - } - - func box(_ data: Data) throws -> NSObject { - switch self.options.dataEncodingStrategy { - case .deferredToData: - // Must be called with a surrounding with(pushedKey:) call. - let depth = self.storage.count - do { - try data.encode(to: self) - } catch { - // If the value pushed a container before throwing, pop it back off to restore state. - // This shouldn't be possible for Data (which encodes as an array of bytes), but it can't hurt to catch a failure. - if self.storage.count > depth { - let _ = self.storage.popContainer() - } - - throw error - } - - return self.storage.popContainer() - - case .base64: - return NSString(string: data.base64EncodedString()) - - case .custom(let closure): - let depth = self.storage.count - do { - try closure(data, self) - } catch { - // If the value pushed a container before throwing, pop it back off to restore state. - if self.storage.count > depth { - let _ = self.storage.popContainer() - } - - throw error - } - - guard self.storage.count > depth else { - // The closure didn't encode anything. Return the default keyed container. - return NSDictionary() - } - - // We can pop because the closure encoded something. - return self.storage.popContainer() - } - } - - func box(_ dict: [String : Encodable]) throws -> NSObject? { - let depth = self.storage.count - let result = self.storage.pushKeyedContainer() - do { - for (key, value) in dict { - self.codingPath.append(_JSONKey(stringValue: key, intValue: nil)) - defer { self.codingPath.removeLast() } - result[key] = try box(value) - } - } catch { - // If the value pushed a container before throwing, pop it back off to restore state. - if self.storage.count > depth { - let _ = self.storage.popContainer() - } - - throw error - } - - // The top container should be a new container. - guard self.storage.count > depth else { - return nil - } - - return self.storage.popContainer() - } - - func box(_ value: Encodable) throws -> NSObject { - return try self.box_(value) ?? NSDictionary() - } - - // This method is called "box_" instead of "box" to disambiguate it from the overloads. Because the return type here is different from all of the "box" overloads (and is more general), any "box" calls in here would call back into "box" recursively instead of calling the appropriate overload, which is not what we want. - func box_(_ value: Encodable) throws -> NSObject? { - // Disambiguation between variable and function is required due to - // issue tracked at: https://bugs.swift.org/browse/SR-1846 - let type = Swift.type(of: value) - if type == Date.self || type == NSDate.self { - // Respect Date encoding strategy - return try self.box((value as! Date)) - } else if type == Data.self || type == NSData.self { - // Respect Data encoding strategy - return try self.box((value as! Data)) - } else if type == URL.self || type == NSURL.self { - // Encode URLs as single strings. - return self.box((value as! URL).absoluteString) - } else if type == Decimal.self || type == NSDecimalNumber.self { - // JSONSerialization can natively handle NSDecimalNumber. - return (value as! NSDecimalNumber) - } else if value is _JSONStringDictionaryEncodableMarker { - return try self.box(value as! [String : Encodable]) - } - - // The value should request a container from the __JSONEncoder. - let depth = self.storage.count - do { - try value.encode(to: self) - } catch { - // If the value pushed a container before throwing, pop it back off to restore state. - if self.storage.count > depth { - let _ = self.storage.popContainer() - } - - throw error - } - - // The top container should be a new container. - guard self.storage.count > depth else { - return nil - } - - return self.storage.popContainer() - } -} - -// MARK: - __JSONReferencingEncoder - -/// __JSONReferencingEncoder is a special subclass of __JSONEncoder which has its own storage, but references the contents of a different encoder. -/// It's used in superEncoder(), which returns a new encoder for encoding a superclass -- the lifetime of the encoder should not escape the scope it's created in, but it doesn't necessarily know when it's done being used (to write to the original container). -// NOTE: older overlays called this class _JSONReferencingEncoder. -// The two must coexist without a conflicting ObjC class name, so it -// was renamed. The old name must not be used in the new runtime. -private class __JSONReferencingEncoder : __JSONEncoder { - // MARK: Reference types. - - /// The type of container we're referencing. - private enum Reference { - /// Referencing a specific index in an array container. - case array(NSMutableArray, Int) - - /// Referencing a specific key in a dictionary container. - case dictionary(NSMutableDictionary, String) - } - - // MARK: - Properties - - /// The encoder we're referencing. - let encoder: __JSONEncoder - - /// The container reference itself. - private let reference: Reference - - // MARK: - Initialization - - /// Initializes `self` by referencing the given array container in the given encoder. - init(referencing encoder: __JSONEncoder, at index: Int, wrapping array: NSMutableArray) { - self.encoder = encoder - self.reference = .array(array, index) - super.init(options: encoder.options, codingPath: encoder.codingPath) - - self.codingPath.append(_JSONKey(index: index)) - } - - /// Initializes `self` by referencing the given dictionary container in the given encoder. - init(referencing encoder: __JSONEncoder, key: CodingKey, convertedKey: __shared CodingKey, wrapping dictionary: NSMutableDictionary) { - self.encoder = encoder - self.reference = .dictionary(dictionary, convertedKey.stringValue) - super.init(options: encoder.options, codingPath: encoder.codingPath) - - self.codingPath.append(key) - } - - // MARK: - Coding Path Operations - - override var canEncodeNewValue: Bool { - // With a regular encoder, the storage and coding path grow together. - // A referencing encoder, however, inherits its parents coding path, as well as the key it was created for. - // We have to take this into account. - return self.storage.count == self.codingPath.count - self.encoder.codingPath.count - 1 - } - - // MARK: - Deinitialization - - // Finalizes `self` by writing the contents of our storage to the referenced encoder's storage. - deinit { - let value: Any - switch self.storage.count { - case 0: value = NSDictionary() - case 1: value = self.storage.popContainer() - default: fatalError("Referencing encoder deallocated with multiple containers on stack.") - } - - switch self.reference { - case .array(let array, let index): - array.insert(value, at: index) - - case .dictionary(let dictionary, let key): - dictionary[NSString(string: key)] = value - } - } -} - -//===----------------------------------------------------------------------===// -// JSON Decoder -//===----------------------------------------------------------------------===// - -/// `JSONDecoder` facilitates the decoding of JSON into semantic `Decodable` types. -// NOTE: older overlays had Foundation.JSONDecoder as the ObjC name. -// The two must coexist, so it was renamed. The old name must not be -// used in the new runtime. _TtC10Foundation13__JSONDecoder is the -// mangled name for Foundation.__JSONDecoder. -@_objcRuntimeName(_TtC10Foundation13__JSONDecoder) -open class JSONDecoder { - // MARK: Options - - /// The strategy to use for decoding `Date` values. - public enum DateDecodingStrategy { - /// Defer to `Date` for decoding. This is the default strategy. - case deferredToDate - - /// Decode the `Date` as a UNIX timestamp from a JSON number. - case secondsSince1970 - - /// Decode the `Date` as UNIX millisecond timestamp from a JSON number. - case millisecondsSince1970 - - /// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format). - @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) - case iso8601 - - /// Decode the `Date` as a string parsed by the given formatter. - case formatted(DateFormatter) - - /// Decode the `Date` as a custom value decoded by the given closure. - case custom((_ decoder: Decoder) throws -> Date) - } - - /// The strategy to use for decoding `Data` values. - public enum DataDecodingStrategy { - /// Defer to `Data` for decoding. - case deferredToData - - /// Decode the `Data` from a Base64-encoded string. This is the default strategy. - case base64 - - /// Decode the `Data` as a custom value decoded by the given closure. - case custom((_ decoder: Decoder) throws -> Data) - } - - /// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN). - public enum NonConformingFloatDecodingStrategy { - /// Throw upon encountering non-conforming values. This is the default strategy. - case `throw` - - /// Decode the values from the given representation strings. - case convertFromString(positiveInfinity: String, negativeInfinity: String, nan: String) - } - - /// The strategy to use for automatically changing the value of keys before decoding. - public enum KeyDecodingStrategy { - /// Use the keys specified by each type. This is the default strategy. - case useDefaultKeys - - /// Convert from "snake_case_keys" to "camelCaseKeys" before attempting to match a key with the one specified by each type. - /// - /// The conversion to upper case uses `Locale.system`, also known as the ICU "root" locale. This means the result is consistent regardless of the current user's locale and language preferences. - /// - /// Converting from snake case to camel case: - /// 1. Capitalizes the word starting after each `_` - /// 2. Removes all `_` - /// 3. Preserves starting and ending `_` (as these are often used to indicate private variables or other metadata). - /// For example, `one_two_three` becomes `oneTwoThree`. `_one_two_three_` becomes `_oneTwoThree_`. - /// - /// - Note: Using a key decoding strategy has a nominal performance cost, as each string key has to be inspected for the `_` character. - case convertFromSnakeCase - - /// Provide a custom conversion from the key in the encoded JSON to the keys specified by the decoded types. - /// The full path to the current decoding position is provided for context (in case you need to locate this key within the payload). The returned key is used in place of the last component in the coding path before decoding. - /// If the result of the conversion is a duplicate key, then only one value will be present in the container for the type to decode from. - case custom((_ codingPath: [CodingKey]) -> CodingKey) - - fileprivate static func _convertFromSnakeCase(_ stringKey: String) -> String { - guard !stringKey.isEmpty else { return stringKey } - - // Find the first non-underscore character - guard let firstNonUnderscore = stringKey.firstIndex(where: { $0 != "_" }) else { - // Reached the end without finding an _ - return stringKey - } - - // Find the last non-underscore character - var lastNonUnderscore = stringKey.index(before: stringKey.endIndex) - while lastNonUnderscore > firstNonUnderscore && stringKey[lastNonUnderscore] == "_" { - stringKey.formIndex(before: &lastNonUnderscore) - } - - let keyRange = firstNonUnderscore...lastNonUnderscore - let leadingUnderscoreRange = stringKey.startIndex..(_ type: T.Type, from data: Data) throws -> T { - let topLevel: Any - do { - topLevel = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) - } catch { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: error)) - } - - let decoder = __JSONDecoder(referencing: topLevel, options: self.options) - guard let value = try decoder.unbox(topLevel, as: type) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: [], debugDescription: "The given data did not contain a top-level value.")) - } - - return value - } -} - -// MARK: - __JSONDecoder - -// NOTE: older overlays called this class _JSONDecoder. The two must -// coexist without a conflicting ObjC class name, so it was renamed. -// The old name must not be used in the new runtime. -private class __JSONDecoder : Decoder { - // MARK: Properties - - /// The decoder's storage. - var storage: _JSONDecodingStorage - - /// Options set on the top-level decoder. - let options: JSONDecoder._Options - - /// The path to the current point in encoding. - fileprivate(set) public var codingPath: [CodingKey] - - /// Contextual user-provided information for use during encoding. - public var userInfo: [CodingUserInfoKey : Any] { - return self.options.userInfo - } - - // MARK: - Initialization - - /// Initializes `self` with the given top-level container and options. - init(referencing container: Any, at codingPath: [CodingKey] = [], options: JSONDecoder._Options) { - self.storage = _JSONDecodingStorage() - self.storage.push(container: container) - self.codingPath = codingPath - self.options = options - } - - // MARK: - Decoder Methods - - public func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer { - guard !(self.storage.topContainer is NSNull) else { - throw DecodingError.valueNotFound(KeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get keyed decoding container -- found null value instead.")) - } - - guard let topContainer = self.storage.topContainer as? [String : Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: self.storage.topContainer) - } - - let container = _JSONKeyedDecodingContainer(referencing: self, wrapping: topContainer) - return KeyedDecodingContainer(container) - } - - public func unkeyedContainer() throws -> UnkeyedDecodingContainer { - guard !(self.storage.topContainer is NSNull) else { - throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get unkeyed decoding container -- found null value instead.")) - } - - guard let topContainer = self.storage.topContainer as? [Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: self.storage.topContainer) - } - - return _JSONUnkeyedDecodingContainer(referencing: self, wrapping: topContainer) - } - - public func singleValueContainer() throws -> SingleValueDecodingContainer { - return self - } -} - -// MARK: - Decoding Storage - -private struct _JSONDecodingStorage { - // MARK: Properties - - /// The container stack. - /// Elements may be any one of the JSON types (NSNull, NSNumber, String, Array, [String : Any]). - private(set) var containers: [Any] = [] - - // MARK: - Initialization - - /// Initializes `self` with no containers. - init() {} - - // MARK: - Modifying the Stack - - var count: Int { - return self.containers.count - } - - var topContainer: Any { - precondition(!self.containers.isEmpty, "Empty container stack.") - return self.containers.last! - } - - mutating func push(container: __owned Any) { - self.containers.append(container) - } - - mutating func popContainer() { - precondition(!self.containers.isEmpty, "Empty container stack.") - self.containers.removeLast() - } -} - -// MARK: Decoding Containers - -private struct _JSONKeyedDecodingContainer : KeyedDecodingContainerProtocol { - typealias Key = K - - // MARK: Properties - - /// A reference to the decoder we're reading from. - private let decoder: __JSONDecoder - - /// A reference to the container we're reading from. - private let container: [String : Any] - - /// The path of coding keys taken to get to this point in decoding. - private(set) public var codingPath: [CodingKey] - - // MARK: - Initialization - - /// Initializes `self` by referencing the given decoder and container. - init(referencing decoder: __JSONDecoder, wrapping container: [String : Any]) { - self.decoder = decoder - switch decoder.options.keyDecodingStrategy { - case .useDefaultKeys: - self.container = container - case .convertFromSnakeCase: - // Convert the snake case keys in the container to camel case. - // If we hit a duplicate key after conversion, then we'll use the first one we saw. Effectively an undefined behavior with JSON dictionaries. - self.container = Dictionary(container.map { - key, value in (JSONDecoder.KeyDecodingStrategy._convertFromSnakeCase(key), value) - }, uniquingKeysWith: { (first, _) in first }) - case .custom(let converter): - self.container = Dictionary(container.map { - key, value in (converter(decoder.codingPath + [_JSONKey(stringValue: key, intValue: nil)]).stringValue, value) - }, uniquingKeysWith: { (first, _) in first }) - } - self.codingPath = decoder.codingPath - } - - // MARK: - KeyedDecodingContainerProtocol Methods - - public var allKeys: [Key] { - return self.container.keys.compactMap { Key(stringValue: $0) } - } - - public func contains(_ key: Key) -> Bool { - return self.container[key.stringValue] != nil - } - - private func _errorDescription(of key: CodingKey) -> String { - switch decoder.options.keyDecodingStrategy { - case .convertFromSnakeCase: - // In this case we can attempt to recover the original value by reversing the transform - let original = key.stringValue - let converted = JSONEncoder.KeyEncodingStrategy._convertToSnakeCase(original) - let roundtrip = JSONDecoder.KeyDecodingStrategy._convertFromSnakeCase(converted) - if converted == original { - return "\(key) (\"\(original)\")" - } else if roundtrip == original { - return "\(key) (\"\(original)\"), converted to \(converted)" - } else { - return "\(key) (\"\(original)\"), with divergent representation \(roundtrip), converted to \(converted)" - } - default: - // Otherwise, just report the converted string - return "\(key) (\"\(key.stringValue)\")" - } - } - - public func decodeNil(forKey key: Key) throws -> Bool { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - return entry is NSNull - } - - public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Bool.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int.Type, forKey key: Key) throws -> Int { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int8.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int16.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int32.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int64.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt8.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt16.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt32.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt64.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Float.Type, forKey key: Key) throws -> Float { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Float.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Double.Type, forKey key: Key) throws -> Double { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Double.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: String.Type, forKey key: Key) throws -> String { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: String.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: T.Type, forKey key: Key) throws -> T { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key)).")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: type) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer { - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get \(KeyedDecodingContainer.self) -- no value found for key \(_errorDescription(of: key))")) - } - - guard let dictionary = value as? [String : Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value) - } - - let container = _JSONKeyedDecodingContainer(referencing: self.decoder, wrapping: dictionary) - return KeyedDecodingContainer(container) - } - - public func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get UnkeyedDecodingContainer -- no value found for key \(_errorDescription(of: key))")) - } - - guard let array = value as? [Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: value) - } - - return _JSONUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array) - } - - private func _superDecoder(forKey key: __owned CodingKey) throws -> Decoder { - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - let value: Any = self.container[key.stringValue] ?? NSNull() - return __JSONDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options) - } - - public func superDecoder() throws -> Decoder { - return try _superDecoder(forKey: _JSONKey.super) - } - - public func superDecoder(forKey key: Key) throws -> Decoder { - return try _superDecoder(forKey: key) - } -} - -private struct _JSONUnkeyedDecodingContainer : UnkeyedDecodingContainer { - // MARK: Properties - - /// A reference to the decoder we're reading from. - private let decoder: __JSONDecoder - - /// A reference to the container we're reading from. - private let container: [Any] - - /// The path of coding keys taken to get to this point in decoding. - private(set) public var codingPath: [CodingKey] - - /// The index of the element we're about to decode. - private(set) public var currentIndex: Int - - // MARK: - Initialization - - /// Initializes `self` by referencing the given decoder and container. - init(referencing decoder: __JSONDecoder, wrapping container: [Any]) { - self.decoder = decoder - self.container = container - self.codingPath = decoder.codingPath - self.currentIndex = 0 - } - - // MARK: - UnkeyedDecodingContainer Methods - - public var count: Int? { - return self.container.count - } - - public var isAtEnd: Bool { - return self.currentIndex >= self.count! - } - - public mutating func decodeNil() throws -> Bool { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(Any?.self, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - if self.container[self.currentIndex] is NSNull { - self.currentIndex += 1 - return true - } else { - return false - } - } - - public mutating func decode(_ type: Bool.Type) throws -> Bool { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Bool.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int.Type) throws -> Int { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int8.Type) throws -> Int8 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int8.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int16.Type) throws -> Int16 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int16.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int32.Type) throws -> Int32 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int32.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int64.Type) throws -> Int64 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int64.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt.Type) throws -> UInt { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt8.Type) throws -> UInt8 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt8.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt16.Type) throws -> UInt16 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt16.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt32.Type) throws -> UInt32 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt32.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt64.Type) throws -> UInt64 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt64.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Float.Type) throws -> Float { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Float.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Double.Type) throws -> Double { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Double.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: String.Type) throws -> String { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: String.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: T.Type) throws -> T { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: type) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer { - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(KeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get nested keyed container -- unkeyed container is at end.")) - } - - let value = self.container[self.currentIndex] - guard !(value is NSNull) else { - throw DecodingError.valueNotFound(KeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get keyed decoding container -- found null value instead.")) - } - - guard let dictionary = value as? [String : Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value) - } - - self.currentIndex += 1 - let container = _JSONKeyedDecodingContainer(referencing: self.decoder, wrapping: dictionary) - return KeyedDecodingContainer(container) - } - - public mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get nested keyed container -- unkeyed container is at end.")) - } - - let value = self.container[self.currentIndex] - guard !(value is NSNull) else { - throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get keyed decoding container -- found null value instead.")) - } - - guard let array = value as? [Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: value) - } - - self.currentIndex += 1 - return _JSONUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array) - } - - public mutating func superDecoder() throws -> Decoder { - self.decoder.codingPath.append(_JSONKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(Decoder.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get superDecoder() -- unkeyed container is at end.")) - } - - let value = self.container[self.currentIndex] - self.currentIndex += 1 - return __JSONDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options) - } -} - -extension __JSONDecoder : SingleValueDecodingContainer { - // MARK: SingleValueDecodingContainer Methods - - private func expectNonNull(_ type: T.Type) throws { - guard !self.decodeNil() else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected \(type) but found null value instead.")) - } - } - - public func decodeNil() -> Bool { - return self.storage.topContainer is NSNull - } - - public func decode(_ type: Bool.Type) throws -> Bool { - try expectNonNull(Bool.self) - return try self.unbox(self.storage.topContainer, as: Bool.self)! - } - - public func decode(_ type: Int.Type) throws -> Int { - try expectNonNull(Int.self) - return try self.unbox(self.storage.topContainer, as: Int.self)! - } - - public func decode(_ type: Int8.Type) throws -> Int8 { - try expectNonNull(Int8.self) - return try self.unbox(self.storage.topContainer, as: Int8.self)! - } - - public func decode(_ type: Int16.Type) throws -> Int16 { - try expectNonNull(Int16.self) - return try self.unbox(self.storage.topContainer, as: Int16.self)! - } - - public func decode(_ type: Int32.Type) throws -> Int32 { - try expectNonNull(Int32.self) - return try self.unbox(self.storage.topContainer, as: Int32.self)! - } - - public func decode(_ type: Int64.Type) throws -> Int64 { - try expectNonNull(Int64.self) - return try self.unbox(self.storage.topContainer, as: Int64.self)! - } - - public func decode(_ type: UInt.Type) throws -> UInt { - try expectNonNull(UInt.self) - return try self.unbox(self.storage.topContainer, as: UInt.self)! - } - - public func decode(_ type: UInt8.Type) throws -> UInt8 { - try expectNonNull(UInt8.self) - return try self.unbox(self.storage.topContainer, as: UInt8.self)! - } - - public func decode(_ type: UInt16.Type) throws -> UInt16 { - try expectNonNull(UInt16.self) - return try self.unbox(self.storage.topContainer, as: UInt16.self)! - } - - public func decode(_ type: UInt32.Type) throws -> UInt32 { - try expectNonNull(UInt32.self) - return try self.unbox(self.storage.topContainer, as: UInt32.self)! - } - - public func decode(_ type: UInt64.Type) throws -> UInt64 { - try expectNonNull(UInt64.self) - return try self.unbox(self.storage.topContainer, as: UInt64.self)! - } - - public func decode(_ type: Float.Type) throws -> Float { - try expectNonNull(Float.self) - return try self.unbox(self.storage.topContainer, as: Float.self)! - } - - public func decode(_ type: Double.Type) throws -> Double { - try expectNonNull(Double.self) - return try self.unbox(self.storage.topContainer, as: Double.self)! - } - - public func decode(_ type: String.Type) throws -> String { - try expectNonNull(String.self) - return try self.unbox(self.storage.topContainer, as: String.self)! - } - - public func decode(_ type: T.Type) throws -> T { - try expectNonNull(type) - return try self.unbox(self.storage.topContainer, as: type)! - } -} - -// MARK: - Concrete Value Representations - -private extension __JSONDecoder { - /// Returns the given value unboxed from a container. - func unbox(_ value: Any, as type: Bool.Type) throws -> Bool? { - guard !(value is NSNull) else { return nil } - - if let number = value as? NSNumber { - // TODO: Add a flag to coerce non-boolean numbers into Bools? - if number === kCFBooleanTrue as NSNumber { - return true - } else if number === kCFBooleanFalse as NSNumber { - return false - } - - /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested: - } else if let bool = value as? Bool { - return bool - */ - - } - - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - func unbox(_ value: Any, as type: Int.Type) throws -> Int? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int = number.intValue - guard NSNumber(value: int) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return int - } - - func unbox(_ value: Any, as type: Int8.Type) throws -> Int8? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int8 = number.int8Value - guard NSNumber(value: int8) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return int8 - } - - func unbox(_ value: Any, as type: Int16.Type) throws -> Int16? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int16 = number.int16Value - guard NSNumber(value: int16) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return int16 - } - - func unbox(_ value: Any, as type: Int32.Type) throws -> Int32? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int32 = number.int32Value - guard NSNumber(value: int32) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return int32 - } - - func unbox(_ value: Any, as type: Int64.Type) throws -> Int64? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int64 = number.int64Value - guard NSNumber(value: int64) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return int64 - } - - func unbox(_ value: Any, as type: UInt.Type) throws -> UInt? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint = number.uintValue - guard NSNumber(value: uint) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return uint - } - - func unbox(_ value: Any, as type: UInt8.Type) throws -> UInt8? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint8 = number.uint8Value - guard NSNumber(value: uint8) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return uint8 - } - - func unbox(_ value: Any, as type: UInt16.Type) throws -> UInt16? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint16 = number.uint16Value - guard NSNumber(value: uint16) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return uint16 - } - - func unbox(_ value: Any, as type: UInt32.Type) throws -> UInt32? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint32 = number.uint32Value - guard NSNumber(value: uint32) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return uint32 - } - - func unbox(_ value: Any, as type: UInt64.Type) throws -> UInt64? { - guard !(value is NSNull) else { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint64 = number.uint64Value - guard NSNumber(value: uint64) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type).")) - } - - return uint64 - } - - func unbox(_ value: Any, as type: Float.Type) throws -> Float? { - guard !(value is NSNull) else { return nil } - - if let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse { - // We are willing to return a Float by losing precision: - // * If the original value was integral, - // * and the integral value was > Float.greatestFiniteMagnitude, we will fail - // * and the integral value was <= Float.greatestFiniteMagnitude, we are willing to lose precision past 2^24 - // * If it was a Float, you will get back the precise value - // * If it was a Double or Decimal, you will get back the nearest approximation if it will fit - let double = number.doubleValue - guard abs(double) <= Double(Float.greatestFiniteMagnitude) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number \(number) does not fit in \(type).")) - } - - return Float(double) - - /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested: - } else if let double = value as? Double { - if abs(double) <= Double(Float.max) { - return Float(double) - } - - overflow = true - } else if let int = value as? Int { - if let float = Float(exactly: int) { - return float - } - - overflow = true - */ - - } else if let string = value as? String, - case .convertFromString(let posInfString, let negInfString, let nanString) = self.options.nonConformingFloatDecodingStrategy { - if string == posInfString { - return Float.infinity - } else if string == negInfString { - return -Float.infinity - } else if string == nanString { - return Float.nan - } - } - - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - func unbox(_ value: Any, as type: Double.Type) throws -> Double? { - guard !(value is NSNull) else { return nil } - - if let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse { - // We are always willing to return the number as a Double: - // * If the original value was integral, it is guaranteed to fit in a Double; we are willing to lose precision past 2^53 if you encoded a UInt64 but requested a Double - // * If it was a Float or Double, you will get back the precise value - // * If it was Decimal, you will get back the nearest approximation - return number.doubleValue - - /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested: - } else if let double = value as? Double { - return double - } else if let int = value as? Int { - if let double = Double(exactly: int) { - return double - } - - overflow = true - */ - - } else if let string = value as? String, - case .convertFromString(let posInfString, let negInfString, let nanString) = self.options.nonConformingFloatDecodingStrategy { - if string == posInfString { - return Double.infinity - } else if string == negInfString { - return -Double.infinity - } else if string == nanString { - return Double.nan - } - } - - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - func unbox(_ value: Any, as type: String.Type) throws -> String? { - guard !(value is NSNull) else { return nil } - - guard let string = value as? String else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - return string - } - - func unbox(_ value: Any, as type: Date.Type) throws -> Date? { - guard !(value is NSNull) else { return nil } - - switch self.options.dateDecodingStrategy { - case .deferredToDate: - self.storage.push(container: value) - defer { self.storage.popContainer() } - return try Date(from: self) - - case .secondsSince1970: - let double = try self.unbox(value, as: Double.self)! - return Date(timeIntervalSince1970: double) - - case .millisecondsSince1970: - let double = try self.unbox(value, as: Double.self)! - return Date(timeIntervalSince1970: double / 1000.0) - - case .iso8601: - if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - let string = try self.unbox(value, as: String.self)! - guard let date = _iso8601Formatter.date(from: string) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected date string to be ISO8601-formatted.")) - } - - return date - } else { - fatalError("ISO8601DateFormatter is unavailable on this platform.") - } - - case .formatted(let formatter): - let string = try self.unbox(value, as: String.self)! - guard let date = formatter.date(from: string) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Date string does not match format expected by formatter.")) - } - - return date - - case .custom(let closure): - self.storage.push(container: value) - defer { self.storage.popContainer() } - return try closure(self) - } - } - - func unbox(_ value: Any, as type: Data.Type) throws -> Data? { - guard !(value is NSNull) else { return nil } - - switch self.options.dataDecodingStrategy { - case .deferredToData: - self.storage.push(container: value) - defer { self.storage.popContainer() } - return try Data(from: self) - - case .base64: - guard let string = value as? String else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - guard let data = Data(base64Encoded: string) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Encountered Data is not valid Base64.")) - } - - return data - - case .custom(let closure): - self.storage.push(container: value) - defer { self.storage.popContainer() } - return try closure(self) - } - } - - func unbox(_ value: Any, as type: Decimal.Type) throws -> Decimal? { - guard !(value is NSNull) else { return nil } - - // Attempt to bridge from NSDecimalNumber. - if let decimal = value as? Decimal { - return decimal - } else { - let doubleValue = try self.unbox(value, as: Double.self)! - return Decimal(doubleValue) - } - } - - func unbox(_ value: Any, as type: _JSONStringDictionaryDecodableMarker.Type) throws -> T? { - guard !(value is NSNull) else { return nil } - - var result = [String : Any]() - guard let dict = value as? NSDictionary else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - let elementType = type.elementType - for (key, value) in dict { - let key = key as! String - self.codingPath.append(_JSONKey(stringValue: key, intValue: nil)) - defer { self.codingPath.removeLast() } - - result[key] = try unbox_(value, as: elementType) - } - - return result as? T - } - - func unbox(_ value: Any, as type: T.Type) throws -> T? { - return try unbox_(value, as: type) as? T - } - - func unbox_(_ value: Any, as type: Decodable.Type) throws -> Any? { - if type == Date.self || type == NSDate.self { - return try self.unbox(value, as: Date.self) - } else if type == Data.self || type == NSData.self { - return try self.unbox(value, as: Data.self) - } else if type == URL.self || type == NSURL.self { - guard let urlString = try self.unbox(value, as: String.self) else { - return nil - } - - guard let url = URL(string: urlString) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Invalid URL string.")) - } - return url - } else if type == Decimal.self || type == NSDecimalNumber.self { - return try self.unbox(value, as: Decimal.self) - } else if let stringKeyedDictType = type as? _JSONStringDictionaryDecodableMarker.Type { - return try self.unbox(value, as: stringKeyedDictType) - } else { - self.storage.push(container: value) - defer { self.storage.popContainer() } - return try type.init(from: self) - } - } -} - -//===----------------------------------------------------------------------===// -// Shared Key Types -//===----------------------------------------------------------------------===// - -private struct _JSONKey : CodingKey { - public var stringValue: String - public var intValue: Int? - - public init?(stringValue: String) { - self.stringValue = stringValue - self.intValue = nil - } - - public init?(intValue: Int) { - self.stringValue = "\(intValue)" - self.intValue = intValue - } - - public init(stringValue: String, intValue: Int?) { - self.stringValue = stringValue - self.intValue = intValue - } - - init(index: Int) { - self.stringValue = "Index \(index)" - self.intValue = index - } - - static let `super` = _JSONKey(stringValue: "super")! -} - -//===----------------------------------------------------------------------===// -// Shared ISO8601 Date Formatter -//===----------------------------------------------------------------------===// - -// NOTE: This value is implicitly lazy and _must_ be lazy. We're compiled against the latest SDK (w/ ISO8601DateFormatter), but linked against whichever Foundation the user has. ISO8601DateFormatter might not exist, so we better not hit this code path on an older OS. -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -private var _iso8601Formatter: ISO8601DateFormatter = { - let formatter = ISO8601DateFormatter() - formatter.formatOptions = .withInternetDateTime - return formatter -}() - -//===----------------------------------------------------------------------===// -// Error Utilities -//===----------------------------------------------------------------------===// - -extension EncodingError { - /// Returns a `.invalidValue` error describing the given invalid floating-point value. - /// - /// - /// - parameter value: The value that was invalid to encode. - /// - parameter path: The path of `CodingKey`s taken to encode this value. - /// - returns: An `EncodingError` with the appropriate path and debug description. - fileprivate static func _invalidFloatingPointValue(_ value: T, at codingPath: [CodingKey]) -> EncodingError { - let valueDescription: String - if value == T.infinity { - valueDescription = "\(T.self).infinity" - } else if value == -T.infinity { - valueDescription = "-\(T.self).infinity" - } else { - valueDescription = "\(T.self).nan" - } - - let debugDescription = "Unable to encode \(valueDescription) directly in JSON. Use JSONEncoder.NonConformingFloatEncodingStrategy.convertToString to specify how the value should be encoded." - return .invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: debugDescription)) - } -} diff --git a/stdlib/public/Darwin/Foundation/Locale.swift b/stdlib/public/Darwin/Foundation/Locale.swift deleted file mode 100644 index e2b06d6001439..0000000000000 --- a/stdlib/public/Darwin/Foundation/Locale.swift +++ /dev/null @@ -1,501 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -/** - `Locale` encapsulates information about linguistic, cultural, and technological conventions and standards. Examples of information encapsulated by a locale include the symbol used for the decimal separator in numbers and the way dates are formatted. - - Locales are typically used to provide, format, and interpret information about and according to the user's customs and preferences. They are frequently used in conjunction with formatters. Although you can use many locales, you usually use the one associated with the current user. -*/ -public struct Locale : Hashable, Equatable, ReferenceConvertible { - public typealias ReferenceType = NSLocale - - public typealias LanguageDirection = NSLocale.LanguageDirection - - fileprivate var _wrapped : NSLocale - private var _autoupdating : Bool - - /// Returns a locale which tracks the user's current preferences. - /// - /// If mutated, this Locale will no longer track the user's preferences. - /// - /// - note: The autoupdating Locale will only compare equal to another autoupdating Locale. - public static var autoupdatingCurrent : Locale { - return Locale(adoptingReference: __NSLocaleAutoupdating() as! NSLocale, autoupdating: true) - } - - /// Returns the user's current locale. - public static var current : Locale { - return Locale(adoptingReference: __NSLocaleCurrent() as! NSLocale, autoupdating: false) - } - - @available(*, unavailable, message: "Consider using the user's locale or nil instead, depending on use case") - public static var system : Locale { fatalError() } - - // MARK: - - // - - /// Return a locale with the specified identifier. - public init(identifier: String) { - _wrapped = NSLocale(localeIdentifier: identifier) - _autoupdating = false - } - - fileprivate init(reference: __shared NSLocale) { - _wrapped = reference.copy() as! NSLocale - if __NSLocaleIsAutoupdating(reference) { - _autoupdating = true - } else { - _autoupdating = false - } - } - - private init(adoptingReference reference: NSLocale, autoupdating: Bool) { - _wrapped = reference - _autoupdating = autoupdating - } - - // MARK: - - // - - /// Returns a localized string for a specified identifier. - /// - /// For example, in the "en" locale, the result for `"es"` is `"Spanish"`. - public func localizedString(forIdentifier identifier: String) -> String? { - return _wrapped.displayName(forKey: .identifier, value: identifier) - } - - /// Returns a localized string for a specified language code. - /// - /// For example, in the "en" locale, the result for `"es"` is `"Spanish"`. - public func localizedString(forLanguageCode languageCode: String) -> String? { - return _wrapped.displayName(forKey: .languageCode, value: languageCode) - } - - /// Returns a localized string for a specified region code. - /// - /// For example, in the "en" locale, the result for `"fr"` is `"France"`. - public func localizedString(forRegionCode regionCode: String) -> String? { - return _wrapped.displayName(forKey: .countryCode, value: regionCode) - } - - /// Returns a localized string for a specified script code. - /// - /// For example, in the "en" locale, the result for `"Hans"` is `"Simplified Han"`. - public func localizedString(forScriptCode scriptCode: String) -> String? { - return _wrapped.displayName(forKey: .scriptCode, value: scriptCode) - } - - /// Returns a localized string for a specified variant code. - /// - /// For example, in the "en" locale, the result for `"POSIX"` is `"Computer"`. - public func localizedString(forVariantCode variantCode: String) -> String? { - return _wrapped.displayName(forKey: .variantCode, value: variantCode) - } - - /// Returns a localized string for a specified `Calendar.Identifier`. - /// - /// For example, in the "en" locale, the result for `.buddhist` is `"Buddhist Calendar"`. - public func localizedString(for calendarIdentifier: Calendar.Identifier) -> String? { - // NSLocale doesn't export a constant for this - let result = CFLocaleCopyDisplayNameForPropertyValue(unsafeBitCast(_wrapped, to: CFLocale.self), .calendarIdentifier, Calendar._toNSCalendarIdentifier(calendarIdentifier).rawValue as CFString) as String? - return result - } - - /// Returns a localized string for a specified ISO 4217 currency code. - /// - /// For example, in the "en" locale, the result for `"USD"` is `"US Dollar"`. - /// - seealso: `Locale.isoCurrencyCodes` - public func localizedString(forCurrencyCode currencyCode: String) -> String? { - return _wrapped.displayName(forKey: .currencyCode, value: currencyCode) - } - - /// Returns a localized string for a specified ICU collation identifier. - /// - /// For example, in the "en" locale, the result for `"phonebook"` is `"Phonebook Sort Order"`. - public func localizedString(forCollationIdentifier collationIdentifier: String) -> String? { - return _wrapped.displayName(forKey: .collationIdentifier, value: collationIdentifier) - } - - /// Returns a localized string for a specified ICU collator identifier. - public func localizedString(forCollatorIdentifier collatorIdentifier: String) -> String? { - return _wrapped.displayName(forKey: .collatorIdentifier, value: collatorIdentifier) - } - - // MARK: - - // - - /// Returns the identifier of the locale. - public var identifier: String { - return _wrapped.localeIdentifier - } - - /// Returns the language code of the locale, or nil if has none. - /// - /// For example, for the locale "zh-Hant-HK", returns "zh". - public var languageCode: String? { - return _wrapped.object(forKey: .languageCode) as? String - } - - /// Returns the region code of the locale, or nil if it has none. - /// - /// For example, for the locale "zh-Hant-HK", returns "HK". - public var regionCode: String? { - // n.b. this is called countryCode in ObjC - if let result = _wrapped.object(forKey: .countryCode) as? String { - if result.isEmpty { - return nil - } else { - return result - } - } else { - return nil - } - } - - /// Returns the script code of the locale, or nil if has none. - /// - /// For example, for the locale "zh-Hant-HK", returns "Hant". - public var scriptCode: String? { - return _wrapped.object(forKey: .scriptCode) as? String - } - - /// Returns the variant code for the locale, or nil if it has none. - /// - /// For example, for the locale "en_POSIX", returns "POSIX". - public var variantCode: String? { - if let result = _wrapped.object(forKey: .variantCode) as? String { - if result.isEmpty { - return nil - } else { - return result - } - } else { - return nil - } - } - - /// Returns the exemplar character set for the locale, or nil if has none. - public var exemplarCharacterSet: CharacterSet? { - return _wrapped.object(forKey: .exemplarCharacterSet) as? CharacterSet - } - - /// Returns the calendar for the locale, or the Gregorian calendar as a fallback. - public var calendar: Calendar { - // NSLocale should not return nil here - if let result = _wrapped.object(forKey: .calendar) as? Calendar { - return result - } else { - return Calendar(identifier: .gregorian) - } - } - - /// Returns the collation identifier for the locale, or nil if it has none. - /// - /// For example, for the locale "en_US@collation=phonebook", returns "phonebook". - public var collationIdentifier: String? { - return _wrapped.object(forKey: .collationIdentifier) as? String - } - - /// Returns true if the locale uses the metric system. - /// - /// -seealso: MeasurementFormatter - public var usesMetricSystem: Bool { - // NSLocale should not return nil here, but just in case - if let result = (_wrapped.object(forKey: .usesMetricSystem) as? NSNumber)?.boolValue { - return result - } else { - return false - } - } - - /// Returns the decimal separator of the locale. - /// - /// For example, for "en_US", returns ".". - public var decimalSeparator: String? { - return _wrapped.object(forKey: .decimalSeparator) as? String - } - - /// Returns the grouping separator of the locale. - /// - /// For example, for "en_US", returns ",". - public var groupingSeparator: String? { - return _wrapped.object(forKey: .groupingSeparator) as? String - } - - /// Returns the currency symbol of the locale. - /// - /// For example, for "zh-Hant-HK", returns "HK$". - public var currencySymbol: String? { - return _wrapped.object(forKey: .currencySymbol) as? String - } - - /// Returns the currency code of the locale. - /// - /// For example, for "zh-Hant-HK", returns "HKD". - public var currencyCode: String? { - return _wrapped.object(forKey: .currencyCode) as? String - } - - /// Returns the collator identifier of the locale. - public var collatorIdentifier: String? { - return _wrapped.object(forKey: .collatorIdentifier) as? String - } - - /// Returns the quotation begin delimiter of the locale. - /// - /// For example, returns `“` for "en_US", and `「` for "zh-Hant-HK". - public var quotationBeginDelimiter: String? { - return _wrapped.object(forKey: .quotationBeginDelimiterKey) as? String - } - - /// Returns the quotation end delimiter of the locale. - /// - /// For example, returns `”` for "en_US", and `」` for "zh-Hant-HK". - public var quotationEndDelimiter: String? { - return _wrapped.object(forKey: .quotationEndDelimiterKey) as? String - } - - /// Returns the alternate quotation begin delimiter of the locale. - /// - /// For example, returns `‘` for "en_US", and `『` for "zh-Hant-HK". - public var alternateQuotationBeginDelimiter: String? { - return _wrapped.object(forKey: .alternateQuotationBeginDelimiterKey) as? String - } - - /// Returns the alternate quotation end delimiter of the locale. - /// - /// For example, returns `’` for "en_US", and `』` for "zh-Hant-HK". - public var alternateQuotationEndDelimiter: String? { - return _wrapped.object(forKey: .alternateQuotationEndDelimiterKey) as? String - } - - // MARK: - - // - - /// Returns a list of available `Locale` identifiers. - public static var availableIdentifiers: [String] { - return NSLocale.availableLocaleIdentifiers - } - - /// Returns a list of available `Locale` language codes. - public static var isoLanguageCodes: [String] { - return NSLocale.isoLanguageCodes - } - - /// Returns a list of available `Locale` region codes. - public static var isoRegionCodes: [String] { - // This was renamed from Obj-C - return NSLocale.isoCountryCodes - } - - /// Returns a list of available `Locale` currency codes. - public static var isoCurrencyCodes: [String] { - return NSLocale.isoCurrencyCodes - } - - /// Returns a list of common `Locale` currency codes. - public static var commonISOCurrencyCodes: [String] { - return NSLocale.commonISOCurrencyCodes - } - - /// Returns a list of the user's preferred languages. - /// - /// - note: `Bundle` is responsible for determining the language that your application will run in, based on the result of this API and combined with the languages your application supports. - /// - seealso: `Bundle.preferredLocalizations(from:)` - /// - seealso: `Bundle.preferredLocalizations(from:forPreferences:)` - public static var preferredLanguages: [String] { - return NSLocale.preferredLanguages - } - - /// Returns a dictionary that splits an identifier into its component pieces. - public static func components(fromIdentifier string: String) -> [String : String] { - return NSLocale.components(fromLocaleIdentifier: string) - } - - /// Constructs an identifier from a dictionary of components. - public static func identifier(fromComponents components: [String : String]) -> String { - return NSLocale.localeIdentifier(fromComponents: components) - } - - /// Returns a canonical identifier from the given string. - public static func canonicalIdentifier(from string: String) -> String { - return NSLocale.canonicalLocaleIdentifier(from: string) - } - - /// Returns a canonical language identifier from the given string. - public static func canonicalLanguageIdentifier(from string: String) -> String { - return NSLocale.canonicalLanguageIdentifier(from: string) - } - - /// Returns the `Locale` identifier from a given Windows locale code, or nil if it could not be converted. - public static func identifier(fromWindowsLocaleCode code: Int) -> String? { - return NSLocale.localeIdentifier(fromWindowsLocaleCode: UInt32(code)) - } - - /// Returns the Windows locale code from a given identifier, or nil if it could not be converted. - public static func windowsLocaleCode(fromIdentifier identifier: String) -> Int? { - let result = NSLocale.windowsLocaleCode(fromLocaleIdentifier: identifier) - if result == 0 { - return nil - } else { - return Int(result) - } - } - - /// Returns the character direction for a specified language code. - public static func characterDirection(forLanguage isoLangCode: String) -> Locale.LanguageDirection { - return NSLocale.characterDirection(forLanguage: isoLangCode) - } - - /// Returns the line direction for a specified language code. - public static func lineDirection(forLanguage isoLangCode: String) -> Locale.LanguageDirection { - return NSLocale.lineDirection(forLanguage: isoLangCode) - } - - // MARK: - - - @available(*, unavailable, renamed: "init(identifier:)") - public init(localeIdentifier: String) { fatalError() } - - @available(*, unavailable, renamed: "identifier") - public var localeIdentifier: String { fatalError() } - - @available(*, unavailable, renamed: "localizedString(forIdentifier:)") - public func localizedString(forLocaleIdentifier localeIdentifier: String) -> String { fatalError() } - - @available(*, unavailable, renamed: "availableIdentifiers") - public static var availableLocaleIdentifiers: [String] { fatalError() } - - @available(*, unavailable, renamed: "components(fromIdentifier:)") - public static func components(fromLocaleIdentifier string: String) -> [String : String] { fatalError() } - - @available(*, unavailable, renamed: "identifier(fromComponents:)") - public static func localeIdentifier(fromComponents dict: [String : String]) -> String { fatalError() } - - @available(*, unavailable, renamed: "canonicalIdentifier(from:)") - public static func canonicalLocaleIdentifier(from string: String) -> String { fatalError() } - - @available(*, unavailable, renamed: "identifier(fromWindowsLocaleCode:)") - public static func localeIdentifier(fromWindowsLocaleCode lcid: UInt32) -> String? { fatalError() } - - @available(*, unavailable, renamed: "windowsLocaleCode(fromIdentifier:)") - public static func windowsLocaleCode(fromLocaleIdentifier localeIdentifier: String) -> UInt32 { fatalError() } - - @available(*, unavailable, message: "use regionCode instead") - public var countryCode: String { fatalError() } - - @available(*, unavailable, message: "use localizedString(forRegionCode:) instead") - public func localizedString(forCountryCode countryCode: String) -> String { fatalError() } - - @available(*, unavailable, renamed: "isoRegionCodes") - public static var isoCountryCodes: [String] { fatalError() } - - // MARK: - - // - - public func hash(into hasher: inout Hasher) { - if _autoupdating { - hasher.combine(false) - } else { - hasher.combine(true) - hasher.combine(_wrapped) - } - } - - public static func ==(lhs: Locale, rhs: Locale) -> Bool { - if lhs._autoupdating || rhs._autoupdating { - return lhs._autoupdating == rhs._autoupdating - } else { - return lhs._wrapped.isEqual(rhs._wrapped) - } - } -} - -extension Locale : CustomDebugStringConvertible, CustomStringConvertible, CustomReflectable { - private var _kindDescription : String { - if self == Locale.autoupdatingCurrent { - return "autoupdatingCurrent" - } else if self == Locale.current { - return "current" - } else { - return "fixed" - } - } - - public var customMirror : Mirror { - var c: [(label: String?, value: Any)] = [] - c.append((label: "identifier", value: identifier)) - c.append((label: "kind", value: _kindDescription)) - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } - - public var description: String { - return "\(identifier) (\(_kindDescription))" - } - - public var debugDescription : String { - return "\(identifier) (\(_kindDescription))" - } -} - -extension Locale : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSLocale { - return _wrapped - } - - public static func _forceBridgeFromObjectiveC(_ input: NSLocale, result: inout Locale?) { - if !_conditionallyBridgeFromObjectiveC(input, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: NSLocale, result: inout Locale?) -> Bool { - result = Locale(reference: input) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSLocale?) -> Locale { - var result: Locale? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -extension NSLocale : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as Locale) - } -} - -extension Locale : Codable { - private enum CodingKeys : Int, CodingKey { - case identifier - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let identifier = try container.decode(String.self, forKey: .identifier) - self.init(identifier: identifier) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.identifier, forKey: .identifier) - } -} diff --git a/stdlib/public/Darwin/Foundation/Measurement.swift b/stdlib/public/Darwin/Foundation/Measurement.swift deleted file mode 100644 index 04a45f94d403c..0000000000000 --- a/stdlib/public/Darwin/Foundation/Measurement.swift +++ /dev/null @@ -1,358 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#if DEPLOYMENT_RUNTIME_SWIFT -import CoreFoundation -#else -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftCoreFoundationOverlayShims -#endif - -/// A `Measurement` is a model type that holds a `Double` value associated with a `Unit`. -/// -/// Measurements support a large set of operators, including `+`, `-`, `*`, `/`, and a full set of comparison operators. -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -public struct Measurement : ReferenceConvertible, Comparable, Equatable { - public typealias ReferenceType = NSMeasurement - - /// The unit component of the `Measurement`. - public let unit: UnitType - - /// The value component of the `Measurement`. - public var value: Double - - /// Create a `Measurement` given a specified value and unit. - public init(value: Double, unit: UnitType) { - self.value = value - self.unit = unit - } - - public func hash(into hasher: inout Hasher) { - // Warning: The canonicalization performed here needs to be kept in - // perfect sync with the definition of == below. The floating point - // values that are compared there must match exactly with the values fed - // to the hasher here, or hashing would break. - if let dimension = unit as? Dimension { - // We don't need to feed the base unit to the hasher here; all - // dimensional measurements of the same type share the same unit. - hasher.combine(dimension.converter.baseUnitValue(fromValue: value)) - } else { - hasher.combine(unit) - hasher.combine(value) - } - } -} - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension Measurement : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - public var description: String { - return "\(value) \(unit.symbol)" - } - - public var debugDescription: String { - return "\(value) \(unit.symbol)" - } - - public var customMirror: Mirror { - var c: [(label: String?, value: Any)] = [] - c.append((label: "value", value: value)) - c.append((label: "unit", value: unit.symbol)) - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - - -/// When a `Measurement` contains a `Dimension` unit, it gains the ability to convert between the kinds of units in that dimension. -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension Measurement where UnitType : Dimension { - /// Returns a new measurement created by converting to the specified unit. - /// - /// - parameter otherUnit: A unit of the same `Dimension`. - /// - returns: A converted measurement. - public func converted(to otherUnit: UnitType) -> Measurement { - if unit.isEqual(otherUnit) { - return Measurement(value: value, unit: otherUnit) - } else { - let valueInTermsOfBase = unit.converter.baseUnitValue(fromValue: value) - if otherUnit.isEqual(type(of: unit).baseUnit()) { - return Measurement(value: valueInTermsOfBase, unit: otherUnit) - } else { - let otherValueFromTermsOfBase = otherUnit.converter.value(fromBaseUnitValue: valueInTermsOfBase) - return Measurement(value: otherValueFromTermsOfBase, unit: otherUnit) - } - } - } - - /// Converts the measurement to the specified unit. - /// - /// - parameter otherUnit: A unit of the same `Dimension`. - public mutating func convert(to otherUnit: UnitType) { - self = converted(to: otherUnit) - } - - /// Add two measurements of the same Dimension. - /// - /// If the `unit` of the `lhs` and `rhs` are `isEqual`, then this returns the result of adding the `value` of each `Measurement`. If they are not equal, then this will convert both to the base unit of the `Dimension` and return the result as a `Measurement` of that base unit. - /// - returns: The result of adding the two measurements. - public static func +(lhs: Measurement, rhs: Measurement) -> Measurement { - if lhs.unit.isEqual(rhs.unit) { - return Measurement(value: lhs.value + rhs.value, unit: lhs.unit) - } else { - let lhsValueInTermsOfBase = lhs.unit.converter.baseUnitValue(fromValue: lhs.value) - let rhsValueInTermsOfBase = rhs.unit.converter.baseUnitValue(fromValue: rhs.value) - return Measurement(value: lhsValueInTermsOfBase + rhsValueInTermsOfBase, unit: type(of: lhs.unit).baseUnit()) - } - } - - /// Subtract two measurements of the same Dimension. - /// - /// If the `unit` of the `lhs` and `rhs` are `==`, then this returns the result of subtracting the `value` of each `Measurement`. If they are not equal, then this will convert both to the base unit of the `Dimension` and return the result as a `Measurement` of that base unit. - /// - returns: The result of adding the two measurements. - public static func -(lhs: Measurement, rhs: Measurement) -> Measurement { - if lhs.unit == rhs.unit { - return Measurement(value: lhs.value - rhs.value, unit: lhs.unit) - } else { - let lhsValueInTermsOfBase = lhs.unit.converter.baseUnitValue(fromValue: lhs.value) - let rhsValueInTermsOfBase = rhs.unit.converter.baseUnitValue(fromValue: rhs.value) - return Measurement(value: lhsValueInTermsOfBase - rhsValueInTermsOfBase, unit: type(of: lhs.unit).baseUnit()) - } - } -} - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension Measurement { - /// Add two measurements of the same Unit. - /// - precondition: The `unit` of `lhs` and `rhs` must be `isEqual`. - /// - returns: A measurement of value `lhs.value + rhs.value` and unit `lhs.unit`. - public static func +(lhs: Measurement, rhs: Measurement) -> Measurement { - if lhs.unit.isEqual(rhs.unit) { - return Measurement(value: lhs.value + rhs.value, unit: lhs.unit) - } else { - fatalError("Attempt to add measurements with non-equal units") - } - } - - /// Subtract two measurements of the same Unit. - /// - precondition: The `unit` of `lhs` and `rhs` must be `isEqual`. - /// - returns: A measurement of value `lhs.value - rhs.value` and unit `lhs.unit`. - public static func -(lhs: Measurement, rhs: Measurement) -> Measurement { - if lhs.unit.isEqual(rhs.unit) { - return Measurement(value: lhs.value - rhs.value, unit: lhs.unit) - } else { - fatalError("Attempt to subtract measurements with non-equal units") - } - } - - /// Multiply a measurement by a scalar value. - /// - returns: A measurement of value `lhs.value * rhs` with the same unit as `lhs`. - public static func *(lhs: Measurement, rhs: Double) -> Measurement { - return Measurement(value: lhs.value * rhs, unit: lhs.unit) - } - - /// Multiply a scalar value by a measurement. - /// - returns: A measurement of value `lhs * rhs.value` with the same unit as `rhs`. - public static func *(lhs: Double, rhs: Measurement) -> Measurement { - return Measurement(value: lhs * rhs.value, unit: rhs.unit) - } - - /// Divide a measurement by a scalar value. - /// - returns: A measurement of value `lhs.value / rhs` with the same unit as `lhs`. - public static func /(lhs: Measurement, rhs: Double) -> Measurement { - return Measurement(value: lhs.value / rhs, unit: lhs.unit) - } - - /// Divide a scalar value by a measurement. - /// - returns: A measurement of value `lhs / rhs.value` with the same unit as `rhs`. - public static func /(lhs: Double, rhs: Measurement) -> Measurement { - return Measurement(value: lhs / rhs.value, unit: rhs.unit) - } - - /// Compare two measurements of the same `Dimension`. - /// - /// If `lhs.unit == rhs.unit`, returns `lhs.value == rhs.value`. Otherwise, converts `rhs` to the same unit as `lhs` and then compares the resulting values. - /// - returns: `true` if the measurements are equal. - public static func ==(lhs: Measurement, rhs: Measurement) -> Bool { - // Warning: This defines an equivalence relation that needs to be kept - // in perfect sync with the hash(into:) definition above. The floating - // point values that are fed to the hasher there must match exactly with - // the values compared here, or hashing would break. - if lhs.unit == rhs.unit { - return lhs.value == rhs.value - } else { - if let lhsDimensionalUnit = lhs.unit as? Dimension, - let rhsDimensionalUnit = rhs.unit as? Dimension { - if type(of: lhsDimensionalUnit).baseUnit() == type(of: rhsDimensionalUnit).baseUnit() { - let lhsValueInTermsOfBase = lhsDimensionalUnit.converter.baseUnitValue(fromValue: lhs.value) - let rhsValueInTermsOfBase = rhsDimensionalUnit.converter.baseUnitValue(fromValue: rhs.value) - return lhsValueInTermsOfBase == rhsValueInTermsOfBase - } - } - return false - } - } - - /// Compare two measurements of the same `Unit`. - /// - returns: `true` if the measurements can be compared and the `lhs` is less than the `rhs` converted value. - public static func <(lhs: Measurement, rhs: Measurement) -> Bool { - if lhs.unit == rhs.unit { - return lhs.value < rhs.value - } else { - if let lhsDimensionalUnit = lhs.unit as? Dimension, - let rhsDimensionalUnit = rhs.unit as? Dimension { - if type(of: lhsDimensionalUnit).baseUnit() == type(of: rhsDimensionalUnit).baseUnit() { - let lhsValueInTermsOfBase = lhsDimensionalUnit.converter.baseUnitValue(fromValue: lhs.value) - let rhsValueInTermsOfBase = rhsDimensionalUnit.converter.baseUnitValue(fromValue: rhs.value) - return lhsValueInTermsOfBase < rhsValueInTermsOfBase - } - } - fatalError("Attempt to compare measurements with non-equal dimensions") - } - } -} - -// Implementation note: similar to NSArray, NSDictionary, etc., NSMeasurement's import as an ObjC generic type is suppressed by the importer. Eventually we will need a more general purpose mechanism to correctly import generic types. - -// FIXME: Remove @usableFromInline from MeasurementBridgeType once -// rdar://problem/44662501 is fixed. (The Radar basically just says "look -// through typealiases and inherited protocols when printing extensions".) - -#if DEPLOYMENT_RUNTIME_SWIFT -@usableFromInline -internal typealias MeasurementBridgeType = _ObjectTypeBridgeable -#else -@usableFromInline -internal typealias MeasurementBridgeType = _ObjectiveCBridgeable -#endif - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension Measurement : MeasurementBridgeType { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSMeasurement { - return NSMeasurement(doubleValue: value, unit: unit) - } - - public static func _forceBridgeFromObjectiveC(_ source: NSMeasurement, result: inout Measurement?) { - result = Measurement(value: source.doubleValue, unit: source.unit as! UnitType) - } - - public static func _conditionallyBridgeFromObjectiveC(_ source: NSMeasurement, result: inout Measurement?) -> Bool { - if let u = source.unit as? UnitType { - result = Measurement(value: source.doubleValue, unit: u) - return true - } else { - return false - } - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSMeasurement?) -> Measurement { - let u = source!.unit as! UnitType - return Measurement(value: source!.doubleValue, unit: u) - } -} - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension NSMeasurement : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { -#if DEPLOYMENT_RUNTIME_SWIFT - return AnyHashable(Measurement._unconditionallyBridgeFromObjectiveC(self)) -#else - return AnyHashable(self as Measurement) -#endif - } -} - -// This workaround is required for the time being, because Swift doesn't support covariance for Measurement (26607639) -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension MeasurementFormatter { - public func string(from measurement: Measurement) -> String { - if let result = string(for: measurement) { - return result - } else { - return "" - } - } -} - -// @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -// extension Unit : Codable { -// public convenience init(from decoder: Decoder) throws { -// let container = try decoder.singleValueContainer() -// let symbol = try container.decode(String.self) -// self.init(symbol: symbol) -// } - -// public func encode(to encoder: Encoder) throws { -// var container = encoder.singleValueContainer() -// try container.encode(self.symbol) -// } -// } - -@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -extension Measurement : Codable { - private enum CodingKeys : Int, CodingKey { - case value - case unit - } - - private enum UnitCodingKeys : Int, CodingKey { - case symbol - case converter - } - - private enum LinearConverterCodingKeys : Int, CodingKey { - case coefficient - case constant - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let value = try container.decode(Double.self, forKey: .value) - - let unitContainer = try container.nestedContainer(keyedBy: UnitCodingKeys.self, forKey: .unit) - let symbol = try unitContainer.decode(String.self, forKey: .symbol) - - let unit: UnitType - if UnitType.self is Dimension.Type { - let converterContainer = try unitContainer.nestedContainer(keyedBy: LinearConverterCodingKeys.self, forKey: .converter) - let coefficient = try converterContainer.decode(Double.self, forKey: .coefficient) - let constant = try converterContainer.decode(Double.self, forKey: .constant) - let unitMetaType = (UnitType.self as! Dimension.Type) - unit = (unitMetaType.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient, constant: constant)) as! UnitType) - } else { - unit = UnitType(symbol: symbol) - } - - self.init(value: value, unit: unit) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.value, forKey: .value) - - var unitContainer = container.nestedContainer(keyedBy: UnitCodingKeys.self, forKey: .unit) - try unitContainer.encode(self.unit.symbol, forKey: .symbol) - - if UnitType.self is Dimension.Type { - guard type(of: (self.unit as! Dimension).converter) is UnitConverterLinear.Type else { - preconditionFailure("Cannot encode a Measurement whose UnitType has a non-linear unit converter.") - } - - let converter = (self.unit as! Dimension).converter as! UnitConverterLinear - var converterContainer = unitContainer.nestedContainer(keyedBy: LinearConverterCodingKeys.self, forKey: .converter) - try converterContainer.encode(converter.coefficient, forKey: .coefficient) - try converterContainer.encode(converter.constant, forKey: .constant) - } - } -} diff --git a/stdlib/public/Darwin/Foundation/NSArray.swift b/stdlib/public/Darwin/Foundation/NSArray.swift deleted file mode 100644 index 0b2879b2df10a..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSArray.swift +++ /dev/null @@ -1,166 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -//===----------------------------------------------------------------------===// -// Arrays -//===----------------------------------------------------------------------===// - -extension NSArray : ExpressibleByArrayLiteral { - /// Create an instance initialized with `elements`. - public required convenience init(arrayLiteral elements: Any...) { - // Let bridging take care of it. - self.init(array: elements) - } -} - -extension Array : _ObjectiveCBridgeable { - - /// Private initializer used for bridging. - /// - /// The provided `NSArray` will be copied to ensure that the copy can - /// not be mutated by other code. - internal init(_cocoaArray: __shared NSArray) { - assert(_isBridgedVerbatimToObjectiveC(Element.self), - "Array can be backed by NSArray only when the element type can be bridged verbatim to Objective-C") - // FIXME: We would like to call CFArrayCreateCopy() to avoid doing an - // objc_msgSend() for instances of CoreFoundation types. We can't do that - // today because CFArrayCreateCopy() copies array contents unconditionally, - // resulting in O(n) copies even for immutable arrays. - // - // CFArrayCreateCopy() is >10x slower than - // -[NSArray copyWithZone:] - // - // The bug is fixed in: OS X 10.11.0, iOS 9.0, all versions of tvOS - // and watchOS. - self = Array(_immutableCocoaArray: _cocoaArray.copy() as AnyObject) - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSArray { - return unsafeBitCast(self._bridgeToObjectiveCImpl(), to: NSArray.self) - } - - public static func _forceBridgeFromObjectiveC( - _ source: NSArray, - result: inout Array? - ) { - // If we have the appropriate native storage already, just adopt it. - if let native = - Array._bridgeFromObjectiveCAdoptingNativeStorageOf(source) { - result = native - return - } - - if _fastPath(_isBridgedVerbatimToObjectiveC(Element.self)) { - // Forced down-cast (possible deferred type-checking) - result = Array(_cocoaArray: source) - return - } - - result = _arrayForceCast([AnyObject](_cocoaArray: source)) - } - - public static func _conditionallyBridgeFromObjectiveC( - _ source: NSArray, - result: inout Array? - ) -> Bool { - // Construct the result array by conditionally bridging each element. - let anyObjectArr = [AnyObject](_cocoaArray: source) - - result = _arrayConditionalCast(anyObjectArr) - return result != nil - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC( - _ source: NSArray? - ) -> Array { - // `nil` has historically been used as a stand-in for an empty - // array; map it to an empty array instead of failing. - if _slowPath(source == nil) { return Array() } - - // If we have the appropriate native storage already, just adopt it. - if let native = - Array._bridgeFromObjectiveCAdoptingNativeStorageOf(source!) { - return native - } - - if _fastPath(_isBridgedVerbatimToObjectiveC(Element.self)) { - // Forced down-cast (possible deferred type-checking) - return Array(_cocoaArray: source!) - } - - return _arrayForceCast([AnyObject](_cocoaArray: source!)) - } -} - -extension NSArray : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as! Array) - } -} - -extension NSArray : Sequence { - /// Return an *iterator* over the elements of this *sequence*. - /// - /// - Complexity: O(1). - final public func makeIterator() -> NSFastEnumerationIterator { - return NSFastEnumerationIterator(self) - } -} - -/* TODO: API review -extension NSArray : Swift.Collection { - final public var startIndex: Int { - return 0 - } - - final public var endIndex: Int { - return count - } -} - */ - -extension NSArray { - // Overlay: - (instancetype)initWithObjects:(id)firstObj, ... - public convenience init(objects elements: Any...) { - self.init(array: elements) - } -} - -extension NSArray { - /// Initializes a newly allocated array by placing in it the objects - /// contained in a given array. - /// - /// - Returns: An array initialized to contain the objects in - /// `anArray``. The returned object might be different than the - /// original receiver. - /// - /// Discussion: After an immutable array has been initialized in - /// this way, it cannot be modified. - @nonobjc - public convenience init(array anArray: __shared NSArray) { - self.init(array: anArray as Array) - } -} - -extension NSArray : CustomReflectable { - public var customMirror: Mirror { - return Mirror(reflecting: self as [AnyObject]) - } -} - -extension Array: CVarArg {} diff --git a/stdlib/public/Darwin/Foundation/NSCoder.swift b/stdlib/public/Darwin/Foundation/NSCoder.swift deleted file mode 100644 index ac5733d252352..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSCoder.swift +++ /dev/null @@ -1,231 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -//===----------------------------------------------------------------------===// -// NSCoder -//===----------------------------------------------------------------------===// - -@available(macOS 10.11, iOS 9.0, *) -internal func resolveError(_ error: NSError?) throws { - if let error = error, error.code != NSCoderValueNotFoundError { - throw error - } -} - -extension NSCoder { - @available(*, unavailable, renamed: "decodeObject(of:forKey:)") - public func decodeObjectOfClass( - _ cls: DecodedObjectType.Type, forKey key: String - ) -> DecodedObjectType? - where DecodedObjectType : NSCoding, DecodedObjectType : NSObject { - fatalError("This API has been renamed") - } - - public func decodeObject( - of cls: DecodedObjectType.Type, forKey key: String - ) -> DecodedObjectType? - where DecodedObjectType : NSCoding, DecodedObjectType : NSObject { - let result = __NSCoderDecodeObjectOfClassForKey(self, cls, key, nil) - return result as? DecodedObjectType - } - - @available(*, unavailable, renamed: "decodeObject(of:forKey:)") - @nonobjc - public func decodeObjectOfClasses(_ classes: NSSet?, forKey key: String) -> AnyObject? { - fatalError("This API has been renamed") - } - - @nonobjc - public func decodeObject(of classes: [AnyClass]?, forKey key: String) -> Any? { - var classesAsNSObjects: NSSet? - if let theClasses = classes { - classesAsNSObjects = NSSet(array: theClasses.map { $0 as AnyObject }) - } - return __NSCoderDecodeObjectOfClassesForKey(self, classesAsNSObjects, key, nil).map { $0 } - } - - @nonobjc - @available(macOS 10.11, iOS 9.0, *) - public func decodeTopLevelObject() throws -> Any? { - var error: NSError? - let result = __NSCoderDecodeObject(self, &error) - try resolveError(error) - return result.map { $0 } - } - - @available(*, unavailable, renamed: "decodeTopLevelObject(forKey:)") - public func decodeTopLevelObjectForKey(_ key: String) throws -> AnyObject? { - fatalError("This API has been renamed") - } - - @nonobjc - @available(swift, obsoleted: 4) - @available(macOS 10.11, iOS 9.0, *) - public func decodeTopLevelObject(forKey key: String) throws -> AnyObject? { - var error: NSError? - let result = __NSCoderDecodeObjectForKey(self, key, &error) - try resolveError(error) - return result as AnyObject? - } - - @nonobjc - @available(swift, introduced: 4) - @available(macOS 10.11, iOS 9.0, *) - public func decodeTopLevelObject(forKey key: String) throws -> Any? { - var error: NSError? - let result = __NSCoderDecodeObjectForKey(self, key, &error) - try resolveError(error) - return result - } - - @available(*, unavailable, renamed: "decodeTopLevelObject(of:forKey:)") - public func decodeTopLevelObjectOfClass( - _ cls: DecodedObjectType.Type, forKey key: String - ) throws -> DecodedObjectType? - where DecodedObjectType : NSCoding, DecodedObjectType : NSObject { - fatalError("This API has been renamed") - } - - @available(macOS 10.11, iOS 9.0, *) - public func decodeTopLevelObject( - of cls: DecodedObjectType.Type, forKey key: String - ) throws -> DecodedObjectType? - where DecodedObjectType : NSCoding, DecodedObjectType : NSObject { - var error: NSError? - let result = __NSCoderDecodeObjectOfClassForKey(self, cls, key, &error) - try resolveError(error) - return result as? DecodedObjectType - } - - @nonobjc - @available(*, unavailable, renamed: "decodeTopLevelObject(of:forKey:)") - public func decodeTopLevelObjectOfClasses(_ classes: NSSet?, forKey key: String) throws -> AnyObject? { - fatalError("This API has been renamed") - } - - @nonobjc - @available(macOS 10.11, iOS 9.0, *) - public func decodeTopLevelObject(of classes: [AnyClass]?, forKey key: String) throws -> Any? { - var error: NSError? - var classesAsNSObjects: NSSet? - if let theClasses = classes { - classesAsNSObjects = NSSet(array: theClasses.map { $0 as AnyObject }) - } - let result = __NSCoderDecodeObjectOfClassesForKey(self, classesAsNSObjects, key, &error) - try resolveError(error) - return result.map { $0 } - } -} - -//===----------------------------------------------------------------------===// -// NSKeyedArchiver -//===----------------------------------------------------------------------===// - -extension NSKeyedArchiver { - @nonobjc - @available(macOS 10.11, iOS 9.0, *) - public func encodeEncodable(_ value: T, forKey key: String) throws { - let plistEncoder = PropertyListEncoder() - let plist = try plistEncoder.encodeToTopLevelContainer(value) - self.encode(plist, forKey: key) - } -} - -//===----------------------------------------------------------------------===// -// NSKeyedUnarchiver -//===----------------------------------------------------------------------===// - -extension NSKeyedUnarchiver { - @nonobjc - @available(swift, obsoleted: 4) - @available(macOS 10.11, iOS 9.0, *) - public class func unarchiveTopLevelObjectWithData(_ data: NSData) throws -> AnyObject? { - var error: NSError? - let result = __NSKeyedUnarchiverUnarchiveObject(self, data, &error) - try resolveError(error) - return result as AnyObject? - } - - @nonobjc - @available(swift, introduced: 4) - @available(macOS 10.11, iOS 9.0, *) - public class func unarchiveTopLevelObjectWithData(_ data: Data) throws -> Any? { - var error: NSError? - let result = __NSKeyedUnarchiverUnarchiveObject(self, data as NSData, &error) - try resolveError(error) - return result - } - - @nonobjc - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public static func unarchivedObject(ofClass cls: DecodedObjectType.Type, from data: Data) throws -> DecodedObjectType? where DecodedObjectType : NSCoding, DecodedObjectType : NSObject { - var error: NSError? - let result = __NSKeyedUnarchiverSecureUnarchiveObjectOfClass(cls as AnyClass, data, &error) - if let error = error { throw error } - return result as? DecodedObjectType - } - - @nonobjc - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public static func unarchivedObject(ofClasses classes: [AnyClass], from data: Data) throws -> Any? { - var error: NSError? - let classesAsNSObjects = NSSet(array: classes.map { $0 as AnyObject }) - let result = __NSKeyedUnarchiverSecureUnarchiveObjectOfClasses(classesAsNSObjects, data, &error) - if let error = error { throw error } - return result - } - - @nonobjc - private static let __plistClasses: [AnyClass] = [ - NSArray.self, - NSData.self, - NSDate.self, - NSDictionary.self, - NSNumber.self, - NSString.self - ] - - @nonobjc - @available(macOS 10.11, iOS 9.0, *) - public func decodeDecodable(_ type: T.Type, forKey key: String) -> T? { - guard let value = self.decodeObject(of: NSKeyedUnarchiver.__plistClasses, forKey: key) else { - return nil - } - - let plistDecoder = PropertyListDecoder() - do { - return try plistDecoder.decode(T.self, fromTopLevel: value) - } catch { - self.failWithError(error) - return nil - } - } - - @nonobjc - @available(macOS 10.11, iOS 9.0, *) - public func decodeTopLevelDecodable(_ type: T.Type, forKey key: String) throws -> T? { - guard let value = try self.decodeTopLevelObject(of: NSKeyedUnarchiver.__plistClasses, forKey: key) else { - return nil - } - - let plistDecoder = PropertyListDecoder() - do { - return try plistDecoder.decode(T.self, fromTopLevel: value) - } catch { - self.failWithError(error) - throw error; - } - } -} diff --git a/stdlib/public/Darwin/Foundation/NSData+DataProtocol.swift b/stdlib/public/Darwin/Foundation/NSData+DataProtocol.swift deleted file mode 100644 index f01ee86190f27..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSData+DataProtocol.swift +++ /dev/null @@ -1,56 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2018 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 -// -//===----------------------------------------------------------------------===// - - -extension NSData : DataProtocol { - - @nonobjc - public var startIndex: Int { return 0 } - - @nonobjc - public var endIndex: Int { return length } - - @nonobjc - public func lastRange(of data: D, in r: R) -> Range? where D : DataProtocol, R : RangeExpression, NSData.Index == R.Bound { - return Range(range(of: Data(data), options: .backwards, in: NSRange(r))) - } - - @nonobjc - public func firstRange(of data: D, in r: R) -> Range? where D : DataProtocol, R : RangeExpression, NSData.Index == R.Bound { - return Range(range(of: Data(data), in: NSRange(r))) - } - - @nonobjc - public var regions: [Data] { - var datas = [Data]() - enumerateBytes { (ptr, range, stop) in - datas.append(Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: ptr), count: range.length, deallocator: .custom({ (ptr: UnsafeMutableRawPointer, count: Int) -> Void in - withExtendedLifetime(self) { } - }))) - } - return datas - } - - @nonobjc - public subscript(position: Int) -> UInt8 { - var byte = UInt8(0) - var offset = position - enumerateBytes { (ptr, range, stop) in - offset -= range.lowerBound - if range.contains(position) { - byte = ptr.load(fromByteOffset: offset, as: UInt8.self) - stop.pointee = true - } - } - return byte - } -} diff --git a/stdlib/public/Darwin/Foundation/NSDictionary.swift b/stdlib/public/Darwin/Foundation/NSDictionary.swift deleted file mode 100644 index 824bc61691619..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSDictionary.swift +++ /dev/null @@ -1,465 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -// We don't check for NSCopying here for performance reasons. We would -// just crash anyway, and NSMutableDictionary will still do that when -// it tries to call -copyWithZone: and it's not there -private func duckCastToNSCopying(_ x: Any) -> NSCopying { - return _unsafeReferenceCast(x as AnyObject, to: NSCopying.self) -} - -//===----------------------------------------------------------------------===// -// Dictionaries -//===----------------------------------------------------------------------===// - -extension NSDictionary : ExpressibleByDictionaryLiteral { - public required convenience init( - dictionaryLiteral elements: (Any, Any)... - ) { - - self.init( - objects: elements.map { $0.1 as AnyObject }, - forKeys: elements.map { duckCastToNSCopying($0.0) }, - count: elements.count) - } -} - -extension Dictionary { - /// Private initializer used for bridging. - /// - /// The provided `NSDictionary` will be copied to ensure that the copy can - /// not be mutated by other code. - private init(_cocoaDictionary: __shared NSDictionary) { - assert( - _isBridgedVerbatimToObjectiveC(Key.self) && - _isBridgedVerbatimToObjectiveC(Value.self), - "Dictionary can be backed by NSDictionary storage only when both key and value are bridged verbatim to Objective-C") - // FIXME: We would like to call CFDictionaryCreateCopy() to avoid doing an - // objc_msgSend() for instances of CoreFoundation types. We can't do that - // today because CFDictionaryCreateCopy() copies dictionary contents - // unconditionally, resulting in O(n) copies even for immutable dictionaries. - // - // CFDictionaryCreateCopy() does not call copyWithZone: - // - // The bug is fixed in: OS X 10.11.0, iOS 9.0, all versions of tvOS - // and watchOS. - self = Dictionary( - _immutableCocoaDictionary: _cocoaDictionary.copy(with: nil) as AnyObject) - } -} - -// Dictionary is conditionally bridged to NSDictionary -extension Dictionary : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSDictionary { - return unsafeBitCast(_bridgeToObjectiveCImpl(), - to: NSDictionary.self) - } - - /*** - Precondition: `buffer` points to a region of memory bound to `AnyObject`, - with a capacity large enough to fit at least `index`+1 elements of type `T` - - _bridgeInitialize rebinds the `index`th `T` of `buffer` to `T`, - and initializes it to `value` - - Note: *not* the `index`th element of `buffer`, since T and AnyObject may be - different sizes. e.g. if T is String (2 words) then given a buffer like so: - - [object:AnyObject, object:AnyObject, uninitialized, uninitialized] - - `_bridgeInitialize(1, of: buffer, to: buffer[1] as! T)` will leave it as: - - [object:AnyObject, object:AnyObject, string:String] - - and `_bridgeInitialize(0, of: buffer, to: buffer[0] as! T)` will then leave: - - [string:String, string:String] - - Doing this in reverse order as shown above is required if T and AnyObject are - different sizes. Here's what we get if instead of 1, 0 we did 0, 1: - - [object:AnyObject, object:AnyObject, uninitialized, uninitialized] - [string:String, uninitialized, uninitialized] - - - Note: if you have retained any of the objects in `buffer`, you must release - them separately, _bridgeInitialize will overwrite them without releasing them - */ - @inline(__always) - private static func _bridgeInitialize(index:Int, - of buffer: UnsafePointer, to value: T) { - let typedBase = UnsafeMutableRawPointer(mutating: - buffer).assumingMemoryBound(to: T.self) - let rawTarget = UnsafeMutableRawPointer(mutating: typedBase + index) - rawTarget.initializeMemory(as: T.self, repeating: value, count: 1) - } - - @inline(__always) - private static func _verbatimForceBridge( - _ buffer: UnsafeMutablePointer, - count: Int, - to: T.Type - ) { - //doesn't have to iterate in reverse because sizeof(T) == sizeof(AnyObject) - for i in 0..( - _ buffer: UnsafeMutablePointer, - count: Int, - to type: T.Type - ) -> Int { - var numUninitialized = count - while numUninitialized > 0 { - guard let bridged = buffer[numUninitialized - 1] as? T else { - return numUninitialized - } - numUninitialized -= 1 - _bridgeInitialize(index: numUninitialized, of: buffer, to: bridged) - } - return numUninitialized - } - - @inline(__always) - private static func _nonVerbatimForceBridge( - _ buffer: UnsafeMutablePointer, - count: Int, - to: T.Type - ) { - for i in (0..( - _ buffer: UnsafeMutablePointer, - count: Int, - to: T.Type - ) -> Int { - var numUninitialized = count - while numUninitialized > 0 { - guard let bridged = Swift._conditionallyBridgeFromObjectiveC( - buffer[numUninitialized - 1], T.self) - else { - return numUninitialized - } - numUninitialized -= 1 - _bridgeInitialize(index: numUninitialized, of: buffer, to: bridged) - } - return numUninitialized - } - - @inline(__always) - private static func _forceBridge( - _ buffer: UnsafeMutablePointer, - count: Int, - to: T.Type - ) { - if _isBridgedVerbatimToObjectiveC(T.self) { - _verbatimForceBridge(buffer, count: count, to: T.self) - } else { - _nonVerbatimForceBridge(buffer, count: count, to: T.self) - } - } - - @inline(__always) - private static func _conditionallyBridge( - _ buffer: UnsafeMutablePointer, - count: Int, - to: T.Type - ) -> Bool { - let numUninitialized:Int - if _isBridgedVerbatimToObjectiveC(T.self) { - numUninitialized = _verbatimBridge(buffer, count: count, to: T.self) - } else { - numUninitialized = _nonVerbatimBridge(buffer, count: count, to: T.self) - } - if numUninitialized == 0 { - return true - } - let numInitialized = count - numUninitialized - (UnsafeMutableRawPointer(mutating: buffer).assumingMemoryBound(to: - T.self) + numUninitialized).deinitialize(count: numInitialized) - return false - } - - @_specialize(where Key == String, Value == Any) - public static func _forceBridgeFromObjectiveC( - _ d: NSDictionary, - result: inout Dictionary? - ) { - if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf( - d as AnyObject) { - result = native - return - } - - if _isBridgedVerbatimToObjectiveC(Key.self) && - _isBridgedVerbatimToObjectiveC(Value.self) { - //Lazily type-checked on access - result = [Key : Value](_cocoaDictionary: d) - return - } - - let keyStride = MemoryLayout.stride - let valueStride = MemoryLayout.stride - let objectStride = MemoryLayout.stride - - //If Key or Value are smaller than AnyObject, a Dictionary with N elements - //doesn't have large enough backing stores to hold the objects to be bridged - //For now we just handle that case the slow way. - if keyStride < objectStride || valueStride < objectStride { - var builder = _DictionaryBuilder(count: d.count) - d.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in - builder.add( - key: anyKey as! Key, - value: anyValue as! Value) - }) - result = builder.take() - } else { - defer { _fixLifetime(d) } - - let numElems = d.count - - // String and NSString have different concepts of equality, so - // string-keyed NSDictionaries may generate key collisions when bridged - // over to Swift. See rdar://problem/35995647 - let handleDuplicates = (Key.self == String.self) - - result = Dictionary(_unsafeUninitializedCapacity: numElems, - allowingDuplicates: handleDuplicates) { keys, vals in - - let objectKeys = UnsafeMutableRawPointer(mutating: - keys.baseAddress!).assumingMemoryBound(to: AnyObject.self) - let objectVals = UnsafeMutableRawPointer(mutating: - vals.baseAddress!).assumingMemoryBound(to: AnyObject.self) - - //This initializes the first N AnyObjects of the Dictionary buffers. - //Any unused buffer space is left uninitialized - //This is fixed up in-place as we bridge elements, by _bridgeInitialize - __NSDictionaryGetObjects(d, objectVals, objectKeys, numElems) - - _forceBridge(objectKeys, count: numElems, to: Key.self) - _forceBridge(objectVals, count: numElems, to: Value.self) - - return numElems - } - } - } - - @_specialize(where Key == String, Value == Any) - public static func _conditionallyBridgeFromObjectiveC( - _ x: NSDictionary, - result: inout Dictionary? - ) -> Bool { - - if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf( - x as AnyObject) { - result = native - return true - } - - let keyStride = MemoryLayout.stride - let valueStride = MemoryLayout.stride - let objectStride = MemoryLayout.stride - - //If Key or Value are smaller than AnyObject, a Dictionary with N elements - //doesn't have large enough backing stores to hold the objects to be bridged - //For now we just handle that case the slow way. - if keyStride < objectStride || valueStride < objectStride { - result = x as [NSObject : AnyObject] as? Dictionary - return result != nil - } - - defer { _fixLifetime(x) } - - let numElems = x.count - var success = true - - // String and NSString have different concepts of equality, so - // string-keyed NSDictionaries may generate key collisions when bridged - // over to Swift. See rdar://problem/35995647 - let handleDuplicates = (Key.self == String.self) - - let tmpResult = Dictionary(_unsafeUninitializedCapacity: numElems, - allowingDuplicates: handleDuplicates) { keys, vals in - - let objectKeys = UnsafeMutableRawPointer(mutating: - keys.baseAddress!).assumingMemoryBound(to: AnyObject.self) - let objectVals = UnsafeMutableRawPointer(mutating: - vals.baseAddress!).assumingMemoryBound(to: AnyObject.self) - - //This initializes the first N AnyObjects of the Dictionary buffers. - //Any unused buffer space is left uninitialized - //This is fixed up in-place as we bridge elements, by _bridgeInitialize - __NSDictionaryGetObjects(x, objectVals, objectKeys, numElems) - - success = _conditionallyBridge(objectKeys, count: numElems, to: Key.self) - if success { - success = _conditionallyBridge(objectVals, - count: numElems, to: Value.self) - if !success { - (UnsafeMutableRawPointer(mutating: objectKeys).assumingMemoryBound(to: - Key.self)).deinitialize(count: numElems) - } - } - return success ? numElems : 0 - } - - result = success ? tmpResult : nil - return success - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC( - _ d: NSDictionary? - ) -> Dictionary { - // `nil` has historically been used as a stand-in for an empty - // dictionary; map it to an empty dictionary. - if _slowPath(d == nil) { return Dictionary() } - - var result: Dictionary? = nil - _forceBridgeFromObjectiveC(d!, result: &result) - return result! - } -} - -extension NSDictionary : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as! Dictionary) - } -} - -extension NSDictionary : Sequence { - // FIXME: A class because we can't pass a struct with class fields through an - // [objc] interface without prematurely destroying the references. - // NOTE: older runtimes had - // _TtCE10FoundationCSo12NSDictionary8Iterator as the ObjC name. The - // two must coexist, so it was renamed. The old name must not be used - // in the new runtime. - @_objcRuntimeName(_TtCE10FoundationCSo12NSDictionary9_Iterator) - final public class Iterator : IteratorProtocol { - var _fastIterator: NSFastEnumerationIterator - var _dictionary: NSDictionary { - return _fastIterator.enumerable as! NSDictionary - } - - public func next() -> (key: Any, value: Any)? { - if let key = _fastIterator.next() { - // Deliberately avoid the subscript operator in case the dictionary - // contains non-copyable keys. This is rare since NSMutableDictionary - // requires them, but we don't want to paint ourselves into a corner. - return (key: key, value: _dictionary.object(forKey: key)!) - } - return nil - } - - internal init(_ _dict: __shared NSDictionary) { - _fastIterator = NSFastEnumerationIterator(_dict) - } - } - - // Bridging subscript. - @objc - public subscript(key: Any) -> Any? { - @objc(__swift_objectForKeyedSubscript:) - get { - // Deliberately avoid the subscript operator in case the dictionary - // contains non-copyable keys. This is rare since NSMutableDictionary - // requires them, but we don't want to paint ourselves into a corner. - return self.object(forKey: key) - } - } - - /// Return an *iterator* over the elements of this *sequence*. - /// - /// - Complexity: O(1). - public func makeIterator() -> Iterator { - return Iterator(self) - } -} - -extension NSMutableDictionary { - // Bridging subscript. - @objc override public subscript(key: Any) -> Any? { - @objc(__swift_objectForKeyedSubscript:) - get { - return self.object(forKey: key) - } - @objc(__swift_setObject:forKeyedSubscript:) - set { - let copyingKey = duckCastToNSCopying(key) - if let newValue = newValue { - self.setObject(newValue, forKey: copyingKey) - } else { - self.removeObject(forKey: copyingKey) - } - } - } -} - -extension NSDictionary { - /// Initializes a newly allocated dictionary and adds to it objects from - /// another given dictionary. - /// - /// - Returns: An initialized dictionary--which might be different - /// than the original receiver--containing the keys and values - /// found in `otherDictionary`. - @objc(__swiftInitWithDictionary_NSDictionary:) - public convenience init(dictionary otherDictionary: __shared NSDictionary) { - // FIXME(performance)(compiler limitation): we actually want to do just - // `self = otherDictionary.copy()`, but Swift does not have factory - // initializers right now. - let numElems = otherDictionary.count - let stride = MemoryLayout.stride - let alignment = MemoryLayout.alignment - let singleSize = stride * numElems - let totalSize = singleSize * 2 - assert(stride == MemoryLayout.stride) - assert(alignment == MemoryLayout.alignment) - - // Allocate a buffer containing both the keys and values. - let buffer = UnsafeMutableRawPointer.allocate( - byteCount: totalSize, alignment: alignment) - defer { - buffer.deallocate() - _fixLifetime(otherDictionary) - } - - let valueBuffer = buffer.bindMemory(to: AnyObject.self, capacity: numElems) - let buffer2 = buffer + singleSize - - __NSDictionaryGetObjects(otherDictionary, buffer, buffer2, numElems) - - let keyBufferCopying = buffer2.assumingMemoryBound(to: NSCopying.self) - self.init(objects: valueBuffer, forKeys: keyBufferCopying, count: numElems) - } -} - -extension NSDictionary : CustomReflectable { - public var customMirror: Mirror { - return Mirror(reflecting: self as [NSObject : AnyObject]) - } -} - -extension Dictionary: CVarArg {} diff --git a/stdlib/public/Darwin/Foundation/NSError.swift b/stdlib/public/Darwin/Foundation/NSError.swift deleted file mode 100644 index b31c062f89c3e..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSError.swift +++ /dev/null @@ -1,3330 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import CoreFoundation -import Darwin -@_implementationOnly import _SwiftFoundationOverlayShims - -//===----------------------------------------------------------------------===// -// NSError (as an out parameter). -//===----------------------------------------------------------------------===// - -public typealias NSErrorPointer = AutoreleasingUnsafeMutablePointer? - -// Note: NSErrorPointer becomes ErrorPointer in Swift 3. -public typealias ErrorPointer = NSErrorPointer - -// An error value to use when an Objective-C API indicates error -// but produces a nil error object. -// This is 'internal' rather than 'private' for no other reason but to make the -// type print more nicely. It's not part of the ABI, so if type printing of -// private things improves we can change it. -internal enum _GenericObjCError : Error { - case nilError -} -// A cached instance of the above in order to save on the conversion to Error. -private let _nilObjCError: Error = _GenericObjCError.nilError - -public // COMPILER_INTRINSIC -func _convertNSErrorToError(_ error: NSError?) -> Error { - if let error = error { - return error - } - return _nilObjCError -} - -public // COMPILER_INTRINSIC -func _convertErrorToNSError(_ error: Error) -> NSError { - return unsafeDowncast(_bridgeErrorToNSError(error), to: NSError.self) -} - -/// Describes an error that provides localized messages describing why -/// an error occurred and provides more information about the error. -public protocol LocalizedError : Error { - /// A localized message describing what error occurred. - var errorDescription: String? { get } - - /// A localized message describing the reason for the failure. - var failureReason: String? { get } - - /// A localized message describing how one might recover from the failure. - var recoverySuggestion: String? { get } - - /// A localized message providing "help" text if the user requests help. - var helpAnchor: String? { get } -} - -public extension LocalizedError { - var errorDescription: String? { return nil } - var failureReason: String? { return nil } - var recoverySuggestion: String? { return nil } - var helpAnchor: String? { return nil } -} - -/// Class that implements the informal protocol -/// NSErrorRecoveryAttempting, which is used by NSError when it -/// attempts recovery from an error. -// NOTE: older overlays called this class _NSErrorRecoveryAttempter. -// The two must coexist without a conflicting ObjC class name, so it -// was renamed. The old name must not be used in the new runtime. -class __NSErrorRecoveryAttempter { - @objc(attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo:) - func attemptRecovery(fromError nsError: NSError, - optionIndex recoveryOptionIndex: Int, - delegate: AnyObject?, - didRecoverSelector: Selector, - contextInfo: UnsafeMutableRawPointer?) { - let error = nsError as Error as! RecoverableError - error.attemptRecovery(optionIndex: recoveryOptionIndex) { success in - __NSErrorPerformRecoverySelector(delegate, didRecoverSelector, success, contextInfo) - } - } - - @objc(attemptRecoveryFromError:optionIndex:) - func attemptRecovery(fromError nsError: NSError, - optionIndex recoveryOptionIndex: Int) -> Bool { - let error = nsError as Error as! RecoverableError - return error.attemptRecovery(optionIndex: recoveryOptionIndex) - } -} - -/// Describes an error that may be recoverable by presenting several -/// potential recovery options to the user. -public protocol RecoverableError : Error { - /// Provides a set of possible recovery options to present to the user. - var recoveryOptions: [String] { get } - - /// Attempt to recover from this error when the user selected the - /// option at the given index. This routine must call handler and - /// indicate whether recovery was successful (or not). - /// - /// This entry point is used for recovery of errors handled at a - /// "document" granularity, that do not affect the entire - /// application. - func attemptRecovery(optionIndex recoveryOptionIndex: Int, - resultHandler handler: @escaping (_ recovered: Bool) -> Void) - - /// Attempt to recover from this error when the user selected the - /// option at the given index. Returns true to indicate - /// successful recovery, and false otherwise. - /// - /// This entry point is used for recovery of errors handled at - /// the "application" granularity, where nothing else in the - /// application can proceed until the attempted error recovery - /// completes. - func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool -} - -public extension RecoverableError { - /// Default implementation that uses the application-model recovery - /// mechanism (``attemptRecovery(optionIndex:)``) to implement - /// document-modal recovery. - func attemptRecovery(optionIndex recoveryOptionIndex: Int, - resultHandler handler: @escaping (_ recovered: Bool) -> Void) { - handler(attemptRecovery(optionIndex: recoveryOptionIndex)) - } -} - -/// Describes an error type that specifically provides a domain, code, -/// and user-info dictionary. -public protocol CustomNSError : Error { - /// The domain of the error. - static var errorDomain: String { get } - - /// The error code within the given domain. - var errorCode: Int { get } - - /// The user-info dictionary. - var errorUserInfo: [String : Any] { get } -} - -public extension CustomNSError { - /// Default domain of the error. - static var errorDomain: String { - return String(reflecting: self) - } - - /// The error code within the given domain. - var errorCode: Int { - return _getDefaultErrorCode(self) - } - - /// The default user-info dictionary. - var errorUserInfo: [String : Any] { - return [:] - } -} - -/// Convert an arbitrary binary integer to an Int, reinterpreting signed -/// -> unsigned if needed but trapping if the result is otherwise not -/// expressible. -func unsafeBinaryIntegerToInt(_ value: T) -> Int { - if T.isSigned { - return numericCast(value) - } - - let uintValue: UInt = numericCast(value) - return Int(bitPattern: uintValue) -} - -/// Convert from an Int to an arbitrary binary integer, reinterpreting signed -> -/// unsigned if needed but trapping if the result is otherwise not expressible. -func unsafeBinaryIntegerFromInt(_ value: Int) -> T { - if T.isSigned { - return numericCast(value) - } - - let uintValue = UInt(bitPattern: value) - return numericCast(uintValue) -} - -extension CustomNSError - where Self: RawRepresentable, Self.RawValue: FixedWidthInteger { - // The error code of Error with integral raw values is the raw value. - public var errorCode: Int { - return unsafeBinaryIntegerToInt(self.rawValue) - } -} - -public extension Error where Self : CustomNSError { - /// Default implementation for customized NSErrors. - var _domain: String { return Self.errorDomain } - - /// Default implementation for customized NSErrors. - var _code: Int { return self.errorCode } -} - -public extension Error where Self: CustomNSError, Self: RawRepresentable, - Self.RawValue: FixedWidthInteger { - /// Default implementation for customized NSErrors. - var _code: Int { return self.errorCode } -} - -public extension Error { - /// Retrieve the localized description for this error. - var localizedDescription: String { - return (self as NSError).localizedDescription - } -} - -internal let _errorDomainUserInfoProviderQueue = DispatchQueue( - label: "SwiftFoundation._errorDomainUserInfoProviderQueue") - -/// Retrieve the default userInfo dictionary for a given error. -public func _getErrorDefaultUserInfo(_ error: T) - -> AnyObject? { - let hasUserInfoValueProvider: Bool - - // If the OS supports user info value providers, use those - // to lazily populate the user-info dictionary for this domain. - if #available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) { - // Note: the Cocoa error domain specifically excluded from - // user-info value providers. - let domain = error._domain - if domain != NSCocoaErrorDomain { - _errorDomainUserInfoProviderQueue.sync { - if NSError.userInfoValueProvider(forDomain: domain) != nil { return } - NSError.setUserInfoValueProvider(forDomain: domain) { (error, key) in - - switch key { - case NSLocalizedDescriptionKey: - return (error as? LocalizedError)?.errorDescription - - case NSLocalizedFailureReasonErrorKey: - return (error as? LocalizedError)?.failureReason - - case NSLocalizedRecoverySuggestionErrorKey: - return (error as? LocalizedError)?.recoverySuggestion - - case NSHelpAnchorErrorKey: - return (error as? LocalizedError)?.helpAnchor - - case NSLocalizedRecoveryOptionsErrorKey: - return (error as? RecoverableError)?.recoveryOptions - - case NSRecoveryAttempterErrorKey: - if error is RecoverableError { - return __NSErrorRecoveryAttempter() - } - return nil - - default: - return nil - } - } - } - assert(NSError.userInfoValueProvider(forDomain: domain) != nil) - - hasUserInfoValueProvider = true - } else { - hasUserInfoValueProvider = false - } - } else { - hasUserInfoValueProvider = false - } - - // Populate the user-info dictionary - var result: [String : Any] - - // Initialize with custom user-info. - if let customNSError = error as? CustomNSError { - result = customNSError.errorUserInfo - } else { - result = [:] - } - - // Handle localized errors. If we registered a user-info value - // provider, these will computed lazily. - if !hasUserInfoValueProvider, - let localizedError = error as? LocalizedError { - if let description = localizedError.errorDescription { - result[NSLocalizedDescriptionKey] = description - } - - if let reason = localizedError.failureReason { - result[NSLocalizedFailureReasonErrorKey] = reason - } - - if let suggestion = localizedError.recoverySuggestion { - result[NSLocalizedRecoverySuggestionErrorKey] = suggestion - } - - if let helpAnchor = localizedError.helpAnchor { - result[NSHelpAnchorErrorKey] = helpAnchor - } - } - - // Handle recoverable errors. If we registered a user-info value - // provider, these will computed lazily. - if !hasUserInfoValueProvider, - let recoverableError = error as? RecoverableError { - result[NSLocalizedRecoveryOptionsErrorKey] = - recoverableError.recoveryOptions - result[NSRecoveryAttempterErrorKey] = __NSErrorRecoveryAttempter() - } - - return result as AnyObject -} - -// NSError and CFError conform to the standard Error protocol. Compiler -// magic allows this to be done as a "toll-free" conversion when an NSError -// or CFError is used as an Error existential. - -extension NSError : Error { - @nonobjc - public var _domain: String { return domain } - - @nonobjc - public var _code: Int { return code } - - @nonobjc - public var _userInfo: AnyObject? { return userInfo as NSDictionary } - - /// The "embedded" NSError is itself. - @nonobjc - public func _getEmbeddedNSError() -> AnyObject? { - return self - } -} - -extension CFError : Error { - public var _domain: String { - return CFErrorGetDomain(self) as String - } - - public var _code: Int { - return CFErrorGetCode(self) - } - - public var _userInfo: AnyObject? { - return CFErrorCopyUserInfo(self) as AnyObject - } - - /// The "embedded" NSError is itself. - public func _getEmbeddedNSError() -> AnyObject? { - return self - } -} - -/// An internal protocol to represent Swift error enums that map to standard -/// Cocoa NSError domains. -public protocol _ObjectiveCBridgeableError : Error { - /// Produce a value of the error type corresponding to the given NSError, - /// or return nil if it cannot be bridged. - init?(_bridgedNSError: __shared NSError) -} - -/// A hook for the runtime to use _ObjectiveCBridgeableError in order to -/// attempt an "errorTypeValue as? SomeError" cast. -/// -/// If the bridge succeeds, the bridged value is written to the uninitialized -/// memory pointed to by 'out', and true is returned. Otherwise, 'out' is -/// left uninitialized, and false is returned. -public func _bridgeNSErrorToError< - T : _ObjectiveCBridgeableError ->(_ error: NSError, out: UnsafeMutablePointer) -> Bool { - if let bridged = T(_bridgedNSError: error) { - out.initialize(to: bridged) - return true - } else { - return false - } -} - -/// Describes a raw representable type that is bridged to a particular -/// NSError domain. -/// -/// This protocol is used primarily to generate the conformance to -/// _ObjectiveCBridgeableError for such an enum defined in Swift. -public protocol _BridgedNSError : - _ObjectiveCBridgeableError, RawRepresentable, Hashable - where Self.RawValue: FixedWidthInteger { - /// The NSError domain to which this type is bridged. - static var _nsErrorDomain: String { get } -} - -extension _BridgedNSError { - public var _domain: String { return Self._nsErrorDomain } -} - -extension _BridgedNSError where Self.RawValue: FixedWidthInteger { - public var _code: Int { return Int(rawValue) } - - public init?(_bridgedNSError: __shared NSError) { - if _bridgedNSError.domain != Self._nsErrorDomain { - return nil - } - - self.init(rawValue: RawValue(_bridgedNSError.code)) - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(_code) - } -} - -/// Describes a bridged error that stores the underlying NSError, so -/// it can be queried. -public protocol _BridgedStoredNSError : - _ObjectiveCBridgeableError, CustomNSError, Hashable { - /// The type of an error code. - associatedtype Code: _ErrorCodeProtocol, RawRepresentable - where Code.RawValue: FixedWidthInteger - - //// Retrieves the embedded NSError. - var _nsError: NSError { get } - - /// Create a new instance of the error type with the given embedded - /// NSError. - /// - /// The \c error must have the appropriate domain for this error - /// type. - init(_nsError error: NSError) -} - -/// Various helper implementations for _BridgedStoredNSError -extension _BridgedStoredNSError { - public var code: Code { - return Code(rawValue: unsafeBinaryIntegerFromInt(_nsError.code))! - } - - /// Initialize an error within this domain with the given ``code`` - /// and ``userInfo``. - public init(_ code: Code, userInfo: [String : Any] = [:]) { - self.init(_nsError: NSError(domain: Self.errorDomain, - code: unsafeBinaryIntegerToInt(code.rawValue), - userInfo: userInfo)) - } - - /// The user-info dictionary for an error that was bridged from - /// NSError. - public var userInfo: [String : Any] { return errorUserInfo } -} - -/// Implementation of _ObjectiveCBridgeableError for all _BridgedStoredNSErrors. -extension _BridgedStoredNSError { - /// Default implementation of ``init(_bridgedNSError:)`` to provide - /// bridging from NSError. - public init?(_bridgedNSError error: NSError) { - if error.domain != Self.errorDomain { - return nil - } - - self.init(_nsError: error) - } -} - -/// Implementation of CustomNSError for all _BridgedStoredNSErrors. -public extension _BridgedStoredNSError { - // FIXME: Would prefer to have a clear "extract an NSError - // directly" operation. - - // Synthesized by the compiler. - // static var errorDomain: String - - var errorCode: Int { return _nsError.code } - - var errorUserInfo: [String : Any] { - var result: [String : Any] = [:] - for (key, value) in _nsError.userInfo { - result[key] = value - } - return result - } -} - -/// Implementation of Hashable for all _BridgedStoredNSErrors. -extension _BridgedStoredNSError { - public func hash(into hasher: inout Hasher) { - hasher.combine(_nsError) - } - - @_alwaysEmitIntoClient public var hashValue: Int { - return _nsError.hashValue - } -} - -/// Describes the code of an error. -public protocol _ErrorCodeProtocol : Equatable { - /// The corresponding error code. - associatedtype _ErrorType: _BridgedStoredNSError where _ErrorType.Code == Self -} - -extension _ErrorCodeProtocol { - /// Allow one to match an error code against an arbitrary error. - public static func ~=(match: Self, error: Error) -> Bool { - guard let specificError = error as? Self._ErrorType else { return false } - - return match == specificError.code - } -} - -extension _BridgedStoredNSError { - /// Retrieve the embedded NSError from a bridged, stored NSError. - public func _getEmbeddedNSError() -> AnyObject? { - return _nsError - } - - public static func == (lhs: Self, rhs: Self) -> Bool { - return lhs._nsError.isEqual(rhs._nsError) - } -} - -extension _SwiftNewtypeWrapper where Self.RawValue == Error { - @inlinable // FIXME(sil-serialize-all) - public func _bridgeToObjectiveC() -> NSError { - return rawValue as NSError - } - - @inlinable // FIXME(sil-serialize-all) - public static func _forceBridgeFromObjectiveC( - _ source: NSError, - result: inout Self? - ) { - result = Self(rawValue: source) - } - - @inlinable // FIXME(sil-serialize-all) - public static func _conditionallyBridgeFromObjectiveC( - _ source: NSError, - result: inout Self? - ) -> Bool { - result = Self(rawValue: source) - return result != nil - } - - @inlinable // FIXME(sil-serialize-all) - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC( - _ source: NSError? - ) -> Self { - return Self(rawValue: _convertNSErrorToError(source))! - } -} - - -@available(*, unavailable, renamed: "CocoaError") -public typealias NSCocoaError = CocoaError - -/// Describes errors within the Cocoa error domain. -public struct CocoaError : _BridgedStoredNSError { - public let _nsError: NSError - - public init(_nsError error: NSError) { - precondition(error.domain == NSCocoaErrorDomain) - self._nsError = error - } - - public static var errorDomain: String { return NSCocoaErrorDomain } - - public var hashValue: Int { - return _nsError.hashValue - } - - /// The error code itself. - public struct Code : RawRepresentable, Hashable, _ErrorCodeProtocol { - public typealias _ErrorType = CocoaError - - public let rawValue: Int - - public init(rawValue: Int) { - self.rawValue = rawValue - } - } -} - -public extension CocoaError { - private var _nsUserInfo: [AnyHashable : Any] { - return (self as NSError).userInfo - } - - /// The file path associated with the error, if any. - var filePath: String? { - return _nsUserInfo[NSFilePathErrorKey as NSString] as? String - } - - /// The string encoding associated with this error, if any. - var stringEncoding: String.Encoding? { - return (_nsUserInfo[NSStringEncodingErrorKey as NSString] as? NSNumber) - .map { String.Encoding(rawValue: $0.uintValue) } - } - - /// The underlying error behind this error, if any. - var underlying: Error? { - return _nsUserInfo[NSUnderlyingErrorKey as NSString] as? Error - } - - /// The URL associated with this error, if any. - var url: URL? { - return _nsUserInfo[NSURLErrorKey as NSString] as? URL - } -} - -extension CocoaError { - public static func error(_ code: CocoaError.Code, userInfo: [AnyHashable : Any]? = nil, url: URL? = nil) -> Error { - var info: [String : Any] = userInfo as? [String : Any] ?? [:] - if let url = url { - info[NSURLErrorKey] = url - } - return NSError(domain: NSCocoaErrorDomain, code: code.rawValue, userInfo: info) - } -} - -extension CocoaError.Code { - public static var fileNoSuchFile: CocoaError.Code { - return CocoaError.Code(rawValue: 4) - } - public static var fileLocking: CocoaError.Code { - return CocoaError.Code(rawValue: 255) - } - public static var fileReadUnknown: CocoaError.Code { - return CocoaError.Code(rawValue: 256) - } - public static var fileReadNoPermission: CocoaError.Code { - return CocoaError.Code(rawValue: 257) - } - public static var fileReadInvalidFileName: CocoaError.Code { - return CocoaError.Code(rawValue: 258) - } - public static var fileReadCorruptFile: CocoaError.Code { - return CocoaError.Code(rawValue: 259) - } - public static var fileReadNoSuchFile: CocoaError.Code { - return CocoaError.Code(rawValue: 260) - } - public static var fileReadInapplicableStringEncoding: CocoaError.Code { - return CocoaError.Code(rawValue: 261) - } - public static var fileReadUnsupportedScheme: CocoaError.Code { - return CocoaError.Code(rawValue: 262) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var fileReadTooLarge: CocoaError.Code { - return CocoaError.Code(rawValue: 263) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var fileReadUnknownStringEncoding: CocoaError.Code { - return CocoaError.Code(rawValue: 264) - } - - public static var fileWriteUnknown: CocoaError.Code { - return CocoaError.Code(rawValue: 512) - } - public static var fileWriteNoPermission: CocoaError.Code { - return CocoaError.Code(rawValue: 513) - } - public static var fileWriteInvalidFileName: CocoaError.Code { - return CocoaError.Code(rawValue: 514) - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 5.0) - public static var fileWriteFileExists: CocoaError.Code { - return CocoaError.Code(rawValue: 516) - } - - public static var fileWriteInapplicableStringEncoding: CocoaError.Code { - return CocoaError.Code(rawValue: 517) - } - public static var fileWriteUnsupportedScheme: CocoaError.Code { - return CocoaError.Code(rawValue: 518) - } - public static var fileWriteOutOfSpace: CocoaError.Code { - return CocoaError.Code(rawValue: 640) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var fileWriteVolumeReadOnly: CocoaError.Code { - return CocoaError.Code(rawValue: 642) - } - - @available(macOS, introduced: 10.11) @available(iOS, unavailable) - public static var fileManagerUnmountUnknown: CocoaError.Code { - return CocoaError.Code(rawValue: 768) - } - - @available(macOS, introduced: 10.11) @available(iOS, unavailable) - public static var fileManagerUnmountBusy: CocoaError.Code { - return CocoaError.Code(rawValue: 769) - } - - public static var keyValueValidation: CocoaError.Code { - return CocoaError.Code(rawValue: 1024) - } - public static var formatting: CocoaError.Code { - return CocoaError.Code(rawValue: 2048) - } - public static var userCancelled: CocoaError.Code { - return CocoaError.Code(rawValue: 3072) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public static var featureUnsupported: CocoaError.Code { - return CocoaError.Code(rawValue: 3328) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableNotLoadable: CocoaError.Code { - return CocoaError.Code(rawValue: 3584) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableArchitectureMismatch: CocoaError.Code { - return CocoaError.Code(rawValue: 3585) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableRuntimeMismatch: CocoaError.Code { - return CocoaError.Code(rawValue: 3586) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableLoad: CocoaError.Code { - return CocoaError.Code(rawValue: 3587) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableLink: CocoaError.Code { - return CocoaError.Code(rawValue: 3588) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var propertyListReadCorrupt: CocoaError.Code { - return CocoaError.Code(rawValue: 3840) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var propertyListReadUnknownVersion: CocoaError.Code { - return CocoaError.Code(rawValue: 3841) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var propertyListReadStream: CocoaError.Code { - return CocoaError.Code(rawValue: 3842) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var propertyListWriteStream: CocoaError.Code { - return CocoaError.Code(rawValue: 3851) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var propertyListWriteInvalid: CocoaError.Code { - return CocoaError.Code(rawValue: 3852) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public static var xpcConnectionInterrupted: CocoaError.Code { - return CocoaError.Code(rawValue: 4097) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public static var xpcConnectionInvalid: CocoaError.Code { - return CocoaError.Code(rawValue: 4099) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public static var xpcConnectionReplyInvalid: CocoaError.Code { - return CocoaError.Code(rawValue: 4101) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - public static var ubiquitousFileUnavailable: CocoaError.Code { - return CocoaError.Code(rawValue: 4353) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - public static var ubiquitousFileNotUploadedDueToQuota: CocoaError.Code { - return CocoaError.Code(rawValue: 4354) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - public static var ubiquitousFileUbiquityServerNotAvailable: CocoaError.Code { - return CocoaError.Code(rawValue: 4355) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var userActivityHandoffFailed: CocoaError.Code { - return CocoaError.Code(rawValue: 4608) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var userActivityConnectionUnavailable: CocoaError.Code { - return CocoaError.Code(rawValue: 4609) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var userActivityRemoteApplicationTimedOut: CocoaError.Code { - return CocoaError.Code(rawValue: 4610) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var userActivityHandoffUserInfoTooLarge: CocoaError.Code { - return CocoaError.Code(rawValue: 4611) - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - public static var coderReadCorrupt: CocoaError.Code { - return CocoaError.Code(rawValue: 4864) - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - public static var coderValueNotFound: CocoaError.Code { - return CocoaError.Code(rawValue: 4865) - } - - public static var coderInvalidValue: CocoaError.Code { - return CocoaError.Code(rawValue: 4866) - } -} - -extension CocoaError.Code { - @available(*, deprecated, renamed: "fileNoSuchFile") - public static var fileNoSuchFileError: CocoaError.Code { - return CocoaError.Code(rawValue: 4) - } - @available(*, deprecated, renamed: "fileLocking") - public static var fileLockingError: CocoaError.Code { - return CocoaError.Code(rawValue: 255) - } - @available(*, deprecated, renamed: "fileReadUnknown") - public static var fileReadUnknownError: CocoaError.Code { - return CocoaError.Code(rawValue: 256) - } - @available(*, deprecated, renamed: "fileReadNoPermission") - public static var fileReadNoPermissionError: CocoaError.Code { - return CocoaError.Code(rawValue: 257) - } - @available(*, deprecated, renamed: "fileReadInvalidFileName") - public static var fileReadInvalidFileNameError: CocoaError.Code { - return CocoaError.Code(rawValue: 258) - } - @available(*, deprecated, renamed: "fileReadCorruptFile") - public static var fileReadCorruptFileError: CocoaError.Code { - return CocoaError.Code(rawValue: 259) - } - @available(*, deprecated, renamed: "fileReadNoSuchFile") - public static var fileReadNoSuchFileError: CocoaError.Code { - return CocoaError.Code(rawValue: 260) - } - @available(*, deprecated, renamed: "fileReadInapplicableStringEncoding") - public static var fileReadInapplicableStringEncodingError: CocoaError.Code { - return CocoaError.Code(rawValue: 261) - } - @available(*, deprecated, renamed: "fileReadUnsupportedScheme") - public static var fileReadUnsupportedSchemeError: CocoaError.Code { - return CocoaError.Code(rawValue: 262) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "fileReadTooLarge") - public static var fileReadTooLargeError: CocoaError.Code { - return CocoaError.Code(rawValue: 263) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "fileReadUnknownStringEncoding") - public static var fileReadUnknownStringEncodingError: CocoaError.Code { - return CocoaError.Code(rawValue: 264) - } - - @available(*, deprecated, renamed: "fileWriteUnknown") - public static var fileWriteUnknownError: CocoaError.Code { - return CocoaError.Code(rawValue: 512) - } - - @available(*, deprecated, renamed: "fileWriteNoPermission") - public static var fileWriteNoPermissionError: CocoaError.Code { - return CocoaError.Code(rawValue: 513) - } - - @available(*, deprecated, renamed: "fileWriteInvalidFileName") - public static var fileWriteInvalidFileNameError: CocoaError.Code { - return CocoaError.Code(rawValue: 514) - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 5.0) - @available(*, deprecated, renamed: "fileWriteFileExists") - public static var fileWriteFileExistsError: CocoaError.Code { - return CocoaError.Code(rawValue: 516) - } - - @available(*, deprecated, renamed: "fileWriteInapplicableStringEncoding") - public static var fileWriteInapplicableStringEncodingError: CocoaError.Code { - return CocoaError.Code(rawValue: 517) - } - - @available(*, deprecated, renamed: "fileWriteUnsupportedScheme") - public static var fileWriteUnsupportedSchemeError: CocoaError.Code { - return CocoaError.Code(rawValue: 518) - } - - @available(*, deprecated, renamed: "fileWriteOutOfSpace") - public static var fileWriteOutOfSpaceError: CocoaError.Code { - return CocoaError.Code(rawValue: 640) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "fileWriteVolumeReadOnly") - public static var fileWriteVolumeReadOnlyError: CocoaError.Code { - return CocoaError.Code(rawValue: 642) - } - - @available(macOS, introduced: 10.11) @available(iOS, unavailable) - @available(*, deprecated, renamed: "fileManagerUnmountUnknown") - public static var fileManagerUnmountUnknownError: CocoaError.Code { - return CocoaError.Code(rawValue: 768) - } - - @available(macOS, introduced: 10.11) @available(iOS, unavailable) - @available(*, deprecated, renamed: "fileManagerUnmountBusy") - public static var fileManagerUnmountBusyError: CocoaError.Code { - return CocoaError.Code(rawValue: 769) - } - - @available(*, deprecated, renamed: "keyValueValidation") - public static var keyValueValidationError: CocoaError.Code { - return CocoaError.Code(rawValue: 1024) - } - - @available(*, deprecated, renamed: "formatting") - public static var formattingError: CocoaError.Code { - return CocoaError.Code(rawValue: 2048) - } - - @available(*, deprecated, renamed: "userCancelled") - public static var userCancelledError: CocoaError.Code { - return CocoaError.Code(rawValue: 3072) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - @available(*, deprecated, renamed: "featureUnsupported") - public static var featureUnsupportedError: CocoaError.Code { - return CocoaError.Code(rawValue: 3328) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableNotLoadable") - public static var executableNotLoadableError: CocoaError.Code { - return CocoaError.Code(rawValue: 3584) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableArchitectureMismatch") - public static var executableArchitectureMismatchError: CocoaError.Code { - return CocoaError.Code(rawValue: 3585) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableRuntimeMismatch") - public static var executableRuntimeMismatchError: CocoaError.Code { - return CocoaError.Code(rawValue: 3586) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableLoad") - public static var executableLoadError: CocoaError.Code { - return CocoaError.Code(rawValue: 3587) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableLink") - public static var executableLinkError: CocoaError.Code { - return CocoaError.Code(rawValue: 3588) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "propertyListReadCorrupt") - public static var propertyListReadCorruptError: CocoaError.Code { - return CocoaError.Code(rawValue: 3840) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "propertyListReadUnknownVersion") - public static var propertyListReadUnknownVersionError: CocoaError.Code { - return CocoaError.Code(rawValue: 3841) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "propertyListReadStream") - public static var propertyListReadStreamError: CocoaError.Code { - return CocoaError.Code(rawValue: 3842) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "propertyListWriteStream") - public static var propertyListWriteStreamError: CocoaError.Code { - return CocoaError.Code(rawValue: 3851) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "propertyListWriteInvalid") - public static var propertyListWriteInvalidError: CocoaError.Code { - return CocoaError.Code(rawValue: 3852) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - @available(*, deprecated, renamed: "ubiquitousFileUnavailable") - public static var ubiquitousFileUnavailableError: CocoaError.Code { - return CocoaError.Code(rawValue: 4353) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - @available(*, deprecated, renamed: "ubiquitousFileNotUploadedDueToQuota") - public static var ubiquitousFileNotUploadedDueToQuotaError: CocoaError.Code { - return CocoaError.Code(rawValue: 4354) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "userActivityHandoffFailed") - public static var userActivityHandoffFailedError: CocoaError.Code { - return CocoaError.Code(rawValue: 4608) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "userActivityConnectionUnavailable") - public static var userActivityConnectionUnavailableError: CocoaError.Code { - return CocoaError.Code(rawValue: 4609) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "userActivityRemoteApplicationTimedOut") - public static var userActivityRemoteApplicationTimedOutError: CocoaError.Code { - return CocoaError.Code(rawValue: 4610) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "userActivityHandoffUserInfoTooLarge") - public static var userActivityHandoffUserInfoTooLargeError: CocoaError.Code { - return CocoaError.Code(rawValue: 4611) - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - @available(*, deprecated, renamed: "coderReadCorrupt") - public static var coderReadCorruptError: CocoaError.Code { - return CocoaError.Code(rawValue: 4864) - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - @available(*, deprecated, renamed: "coderValueNotFound") - public static var coderValueNotFoundError: CocoaError.Code { - return CocoaError.Code(rawValue: 4865) - } -} - -extension CocoaError { - public static var fileNoSuchFile: CocoaError.Code { - return CocoaError.Code(rawValue: 4) - } - public static var fileLocking: CocoaError.Code { - return CocoaError.Code(rawValue: 255) - } - public static var fileReadUnknown: CocoaError.Code { - return CocoaError.Code(rawValue: 256) - } - public static var fileReadNoPermission: CocoaError.Code { - return CocoaError.Code(rawValue: 257) - } - public static var fileReadInvalidFileName: CocoaError.Code { - return CocoaError.Code(rawValue: 258) - } - public static var fileReadCorruptFile: CocoaError.Code { - return CocoaError.Code(rawValue: 259) - } - public static var fileReadNoSuchFile: CocoaError.Code { - return CocoaError.Code(rawValue: 260) - } - public static var fileReadInapplicableStringEncoding: CocoaError.Code { - return CocoaError.Code(rawValue: 261) - } - public static var fileReadUnsupportedScheme: CocoaError.Code { - return CocoaError.Code(rawValue: 262) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var fileReadTooLarge: CocoaError.Code { - return CocoaError.Code(rawValue: 263) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var fileReadUnknownStringEncoding: CocoaError.Code { - return CocoaError.Code(rawValue: 264) - } - - public static var fileWriteUnknown: CocoaError.Code { - return CocoaError.Code(rawValue: 512) - } - public static var fileWriteNoPermission: CocoaError.Code { - return CocoaError.Code(rawValue: 513) - } - public static var fileWriteInvalidFileName: CocoaError.Code { - return CocoaError.Code(rawValue: 514) - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 5.0) - public static var fileWriteFileExists: CocoaError.Code { - return CocoaError.Code(rawValue: 516) - } - - public static var fileWriteInapplicableStringEncoding: CocoaError.Code { - return CocoaError.Code(rawValue: 517) - } - public static var fileWriteUnsupportedScheme: CocoaError.Code { - return CocoaError.Code(rawValue: 518) - } - public static var fileWriteOutOfSpace: CocoaError.Code { - return CocoaError.Code(rawValue: 640) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var fileWriteVolumeReadOnly: CocoaError.Code { - return CocoaError.Code(rawValue: 642) - } - - @available(macOS, introduced: 10.11) @available(iOS, unavailable) - public static var fileManagerUnmountUnknown: CocoaError.Code { - return CocoaError.Code(rawValue: 768) - } - - @available(macOS, introduced: 10.11) @available(iOS, unavailable) - public static var fileManagerUnmountBusy: CocoaError.Code { - return CocoaError.Code(rawValue: 769) - } - - public static var keyValueValidation: CocoaError.Code { - return CocoaError.Code(rawValue: 1024) - } - public static var formatting: CocoaError.Code { - return CocoaError.Code(rawValue: 2048) - } - public static var userCancelled: CocoaError.Code { - return CocoaError.Code(rawValue: 3072) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public static var featureUnsupported: CocoaError.Code { - return CocoaError.Code(rawValue: 3328) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableNotLoadable: CocoaError.Code { - return CocoaError.Code(rawValue: 3584) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableArchitectureMismatch: CocoaError.Code { - return CocoaError.Code(rawValue: 3585) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableRuntimeMismatch: CocoaError.Code { - return CocoaError.Code(rawValue: 3586) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableLoad: CocoaError.Code { - return CocoaError.Code(rawValue: 3587) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var executableLink: CocoaError.Code { - return CocoaError.Code(rawValue: 3588) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var propertyListReadCorrupt: CocoaError.Code { - return CocoaError.Code(rawValue: 3840) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var propertyListReadUnknownVersion: CocoaError.Code { - return CocoaError.Code(rawValue: 3841) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var propertyListReadStream: CocoaError.Code { - return CocoaError.Code(rawValue: 3842) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public static var propertyListWriteStream: CocoaError.Code { - return CocoaError.Code(rawValue: 3851) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var propertyListWriteInvalid: CocoaError.Code { - return CocoaError.Code(rawValue: 3852) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public static var xpcConnectionInterrupted: CocoaError.Code { - return CocoaError.Code(rawValue: 4097) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public static var xpcConnectionInvalid: CocoaError.Code { - return CocoaError.Code(rawValue: 4099) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public static var xpcConnectionReplyInvalid: CocoaError.Code { - return CocoaError.Code(rawValue: 4101) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - public static var ubiquitousFileUnavailable: CocoaError.Code { - return CocoaError.Code(rawValue: 4353) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - public static var ubiquitousFileNotUploadedDueToQuota: CocoaError.Code { - return CocoaError.Code(rawValue: 4354) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - public static var ubiquitousFileUbiquityServerNotAvailable: CocoaError.Code { - return CocoaError.Code(rawValue: 4355) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var userActivityHandoffFailed: CocoaError.Code { - return CocoaError.Code(rawValue: 4608) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var userActivityConnectionUnavailable: CocoaError.Code { - return CocoaError.Code(rawValue: 4609) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var userActivityRemoteApplicationTimedOut: CocoaError.Code { - return CocoaError.Code(rawValue: 4610) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var userActivityHandoffUserInfoTooLarge: CocoaError.Code { - return CocoaError.Code(rawValue: 4611) - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - public static var coderReadCorrupt: CocoaError.Code { - return CocoaError.Code(rawValue: 4864) - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - public static var coderValueNotFound: CocoaError.Code { - return CocoaError.Code(rawValue: 4865) - } - - public static var coderInvalidValue: CocoaError.Code { - return CocoaError.Code(rawValue: 4866) - } -} - -extension CocoaError { - @available(*, deprecated, renamed: "fileNoSuchFile") - public static var fileNoSuchFileError: CocoaError.Code { - return CocoaError.Code(rawValue: 4) - } - @available(*, deprecated, renamed: "fileLocking") - public static var fileLockingError: CocoaError.Code { - return CocoaError.Code(rawValue: 255) - } - @available(*, deprecated, renamed: "fileReadUnknown") - public static var fileReadUnknownError: CocoaError.Code { - return CocoaError.Code(rawValue: 256) - } - @available(*, deprecated, renamed: "fileReadNoPermission") - public static var fileReadNoPermissionError: CocoaError.Code { - return CocoaError.Code(rawValue: 257) - } - @available(*, deprecated, renamed: "fileReadInvalidFileName") - public static var fileReadInvalidFileNameError: CocoaError.Code { - return CocoaError.Code(rawValue: 258) - } - @available(*, deprecated, renamed: "fileReadCorruptFile") - public static var fileReadCorruptFileError: CocoaError.Code { - return CocoaError.Code(rawValue: 259) - } - @available(*, deprecated, renamed: "fileReadNoSuchFile") - public static var fileReadNoSuchFileError: CocoaError.Code { - return CocoaError.Code(rawValue: 260) - } - @available(*, deprecated, renamed: "fileReadInapplicableStringEncoding") - public static var fileReadInapplicableStringEncodingError: CocoaError.Code { - return CocoaError.Code(rawValue: 261) - } - @available(*, deprecated, renamed: "fileReadUnsupportedScheme") - public static var fileReadUnsupportedSchemeError: CocoaError.Code { - return CocoaError.Code(rawValue: 262) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "fileReadTooLarge") - public static var fileReadTooLargeError: CocoaError.Code { - return CocoaError.Code(rawValue: 263) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "fileReadUnknownStringEncoding") - public static var fileReadUnknownStringEncodingError: CocoaError.Code { - return CocoaError.Code(rawValue: 264) - } - - @available(*, deprecated, renamed: "fileWriteUnknown") - public static var fileWriteUnknownError: CocoaError.Code { - return CocoaError.Code(rawValue: 512) - } - - @available(*, deprecated, renamed: "fileWriteNoPermission") - public static var fileWriteNoPermissionError: CocoaError.Code { - return CocoaError.Code(rawValue: 513) - } - - @available(*, deprecated, renamed: "fileWriteInvalidFileName") - public static var fileWriteInvalidFileNameError: CocoaError.Code { - return CocoaError.Code(rawValue: 514) - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 5.0) - @available(*, deprecated, renamed: "fileWriteFileExists") - public static var fileWriteFileExistsError: CocoaError.Code { - return CocoaError.Code(rawValue: 516) - } - - @available(*, deprecated, renamed: "fileWriteInapplicableStringEncoding") - public static var fileWriteInapplicableStringEncodingError: CocoaError.Code { - return CocoaError.Code(rawValue: 517) - } - - @available(*, deprecated, renamed: "fileWriteUnsupportedScheme") - public static var fileWriteUnsupportedSchemeError: CocoaError.Code { - return CocoaError.Code(rawValue: 518) - } - - @available(*, deprecated, renamed: "fileWriteOutOfSpace") - public static var fileWriteOutOfSpaceError: CocoaError.Code { - return CocoaError.Code(rawValue: 640) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "fileWriteVolumeReadOnly") - public static var fileWriteVolumeReadOnlyError: CocoaError.Code { - return CocoaError.Code(rawValue: 642) - } - - @available(macOS, introduced: 10.11) @available(iOS, unavailable) - @available(*, deprecated, renamed: "fileManagerUnmountUnknown") - public static var fileManagerUnmountUnknownError: CocoaError.Code { - return CocoaError.Code(rawValue: 768) - } - - @available(macOS, introduced: 10.11) @available(iOS, unavailable) - @available(*, deprecated, renamed: "fileManagerUnmountBusy") - public static var fileManagerUnmountBusyError: CocoaError.Code { - return CocoaError.Code(rawValue: 769) - } - - @available(*, deprecated, renamed: "keyValueValidation") - public static var keyValueValidationError: CocoaError.Code { - return CocoaError.Code(rawValue: 1024) - } - - @available(*, deprecated, renamed: "formatting") - public static var formattingError: CocoaError.Code { - return CocoaError.Code(rawValue: 2048) - } - - @available(*, deprecated, renamed: "userCancelled") - public static var userCancelledError: CocoaError.Code { - return CocoaError.Code(rawValue: 3072) - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - @available(*, deprecated, renamed: "featureUnsupported") - public static var featureUnsupportedError: CocoaError.Code { - return CocoaError.Code(rawValue: 3328) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableNotLoadable") - public static var executableNotLoadableError: CocoaError.Code { - return CocoaError.Code(rawValue: 3584) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableArchitectureMismatch") - public static var executableArchitectureMismatchError: CocoaError.Code { - return CocoaError.Code(rawValue: 3585) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableRuntimeMismatch") - public static var executableRuntimeMismatchError: CocoaError.Code { - return CocoaError.Code(rawValue: 3586) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableLoad") - public static var executableLoadError: CocoaError.Code { - return CocoaError.Code(rawValue: 3587) - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - @available(*, deprecated, renamed: "executableLink") - public static var executableLinkError: CocoaError.Code { - return CocoaError.Code(rawValue: 3588) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "propertyListReadCorrupt") - public static var propertyListReadCorruptError: CocoaError.Code { - return CocoaError.Code(rawValue: 3840) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "propertyListReadUnknownVersion") - public static var propertyListReadUnknownVersionError: CocoaError.Code { - return CocoaError.Code(rawValue: 3841) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "propertyListReadStream") - public static var propertyListReadStreamError: CocoaError.Code { - return CocoaError.Code(rawValue: 3842) - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - @available(*, deprecated, renamed: "propertyListWriteStream") - public static var propertyListWriteStreamError: CocoaError.Code { - return CocoaError.Code(rawValue: 3851) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "propertyListWriteInvalid") - public static var propertyListWriteInvalidError: CocoaError.Code { - return CocoaError.Code(rawValue: 3852) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - @available(*, deprecated, renamed: "ubiquitousFileUnavailable") - public static var ubiquitousFileUnavailableError: CocoaError.Code { - return CocoaError.Code(rawValue: 4353) - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - @available(*, deprecated, renamed: "ubiquitousFileNotUploadedDueToQuota") - public static var ubiquitousFileNotUploadedDueToQuotaError: CocoaError.Code { - return CocoaError.Code(rawValue: 4354) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "userActivityHandoffFailed") - public static var userActivityHandoffFailedError: CocoaError.Code { - return CocoaError.Code(rawValue: 4608) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "userActivityConnectionUnavailable") - public static var userActivityConnectionUnavailableError: CocoaError.Code { - return CocoaError.Code(rawValue: 4609) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "userActivityRemoteApplicationTimedOut") - public static var userActivityRemoteApplicationTimedOutError: CocoaError.Code { - return CocoaError.Code(rawValue: 4610) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - @available(*, deprecated, renamed: "userActivityHandoffUserInfoTooLarge") - public static var userActivityHandoffUserInfoTooLargeError: CocoaError.Code { - return CocoaError.Code(rawValue: 4611) - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - @available(*, deprecated, renamed: "coderReadCorrupt") - public static var coderReadCorruptError: CocoaError.Code { - return CocoaError.Code(rawValue: 4864) - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - @available(*, deprecated, renamed: "coderValueNotFound") - public static var coderValueNotFoundError: CocoaError.Code { - return CocoaError.Code(rawValue: 4865) - } -} - -extension CocoaError { - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - public var isCoderError: Bool { - return code.rawValue >= 4864 && code.rawValue <= 4991 - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public var isExecutableError: Bool { - return code.rawValue >= 3584 && code.rawValue <= 3839 - } - - public var isFileError: Bool { - return code.rawValue >= 0 && code.rawValue <= 1023 - } - - public var isFormattingError: Bool { - return code.rawValue >= 2048 && code.rawValue <= 2559 - } - - @available(macOS, introduced: 10.6) @available(iOS, introduced: 4.0) - public var isPropertyListError: Bool { - return code.rawValue >= 3840 && code.rawValue <= 4095 - } - - @available(macOS, introduced: 10.9) @available(iOS, introduced: 7.0) - public var isUbiquitousFileError: Bool { - return code.rawValue >= 4352 && code.rawValue <= 4607 - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public var isUserActivityError: Bool { - return code.rawValue >= 4608 && code.rawValue <= 4863 - } - - public var isValidationError: Bool { - return code.rawValue >= 1024 && code.rawValue <= 2047 - } - - @available(macOS, introduced: 10.8) @available(iOS, introduced: 6.0) - public var isXPCConnectionError: Bool { - return code.rawValue >= 4096 && code.rawValue <= 4224 - } -} - -extension CocoaError.Code { - @available(*, unavailable, renamed: "fileNoSuchFile") - public static var FileNoSuchFileError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileLocking") - public static var FileLockingError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadUnknown") - public static var FileReadUnknownError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadNoPermission") - public static var FileReadNoPermissionError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadInvalidFileName") - public static var FileReadInvalidFileNameError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadCorruptFile") - public static var FileReadCorruptFileError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadNoSuchFile") - public static var FileReadNoSuchFileError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadInapplicableStringEncoding") - public static var FileReadInapplicableStringEncodingError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadUnsupportedScheme") - public static var FileReadUnsupportedSchemeError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadTooLarge") - public static var FileReadTooLargeError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileReadUnknownStringEncoding") - public static var FileReadUnknownStringEncodingError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileWriteUnknown") - public static var FileWriteUnknownError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileWriteNoPermission") - public static var FileWriteNoPermissionError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileWriteInvalidFileName") - public static var FileWriteInvalidFileNameError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileWriteFileExists") - public static var FileWriteFileExistsError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileWriteInapplicableStringEncoding") - public static var FileWriteInapplicableStringEncodingError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileWriteUnsupportedScheme") - public static var FileWriteUnsupportedSchemeError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileWriteOutOfSpace") - public static var FileWriteOutOfSpaceError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileWriteVolumeReadOnly") - public static var FileWriteVolumeReadOnlyError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileManagerUnmountUnknown") - public static var FileManagerUnmountUnknownError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileManagerUnmountBusy") - public static var FileManagerUnmountBusyError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "keyValueValidation") - public static var KeyValueValidationError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "formatting") - public static var FormattingError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "userCancelled") - public static var UserCancelledError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "featureUnsupported") - public static var FeatureUnsupportedError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "executableNotLoadable") - public static var ExecutableNotLoadableError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "executableArchitectureMismatch") - public static var ExecutableArchitectureMismatchError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "executableRuntimeMismatch") - public static var ExecutableRuntimeMismatchError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "executableLoad") - public static var ExecutableLoadError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "executableLink") - public static var ExecutableLinkError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "propertyListReadCorrupt") - public static var PropertyListReadCorruptError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "propertyListReadUnknownVersion") - public static var PropertyListReadUnknownVersionError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "propertyListReadStream") - public static var PropertyListReadStreamError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "propertyListWriteStream") - public static var PropertyListWriteStreamError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "propertyListWriteInvalid") - public static var PropertyListWriteInvalidError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "xpcConnectionInterrupted") - public static var XPCConnectionInterrupted: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "xpcConnectionInvalid") - public static var XPCConnectionInvalid: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "xpcConnectionReplyInvalid") - public static var XPCConnectionReplyInvalid: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "ubiquitousFileUnavailable") - public static var UbiquitousFileUnavailableError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "ubiquitousFileNotUploadedDueToQuota") - public static var UbiquitousFileNotUploadedDueToQuotaError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "ubiquitousFileUbiquityServerNotAvailable") - public static var UbiquitousFileUbiquityServerNotAvailable: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "userActivityHandoffFailed") - public static var UserActivityHandoffFailedError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "userActivityConnectionUnavailable") - public static var UserActivityConnectionUnavailableError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "userActivityRemoteApplicationTimedOut") - public static var UserActivityRemoteApplicationTimedOutError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "userActivityHandoffUserInfoTooLarge") - public static var UserActivityHandoffUserInfoTooLargeError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "coderReadCorrupt") - public static var CoderReadCorruptError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "coderValueNotFound") - public static var CoderValueNotFoundError: CocoaError.Code { - fatalError("unavailable accessor can't be called") - } -} - -/// Describes errors in the URL error domain. -public struct URLError : _BridgedStoredNSError { - public let _nsError: NSError - - public init(_nsError error: NSError) { - precondition(error.domain == NSURLErrorDomain) - self._nsError = error - } - - public static var errorDomain: String { return NSURLErrorDomain } - - public var hashValue: Int { - return _nsError.hashValue - } - - /// The error code itself. - public struct Code : RawRepresentable, Hashable, _ErrorCodeProtocol { - public typealias _ErrorType = URLError - - public let rawValue: Int - - public init(rawValue: Int) { - self.rawValue = rawValue - } - } -} - -extension URLError.Code { - public static var unknown: URLError.Code { - return URLError.Code(rawValue: -1) - } - public static var cancelled: URLError.Code { - return URLError.Code(rawValue: -999) - } - public static var badURL: URLError.Code { - return URLError.Code(rawValue: -1000) - } - public static var timedOut: URLError.Code { - return URLError.Code(rawValue: -1001) - } - public static var unsupportedURL: URLError.Code { - return URLError.Code(rawValue: -1002) - } - public static var cannotFindHost: URLError.Code { - return URLError.Code(rawValue: -1003) - } - public static var cannotConnectToHost: URLError.Code { - return URLError.Code(rawValue: -1004) - } - public static var networkConnectionLost: URLError.Code { - return URLError.Code(rawValue: -1005) - } - public static var dnsLookupFailed: URLError.Code { - return URLError.Code(rawValue: -1006) - } - public static var httpTooManyRedirects: URLError.Code { - return URLError.Code(rawValue: -1007) - } - public static var resourceUnavailable: URLError.Code { - return URLError.Code(rawValue: -1008) - } - public static var notConnectedToInternet: URLError.Code { - return URLError.Code(rawValue: -1009) - } - public static var redirectToNonExistentLocation: URLError.Code { - return URLError.Code(rawValue: -1010) - } - public static var badServerResponse: URLError.Code { - return URLError.Code(rawValue: -1011) - } - public static var userCancelledAuthentication: URLError.Code { - return URLError.Code(rawValue: -1012) - } - public static var userAuthenticationRequired: URLError.Code { - return URLError.Code(rawValue: -1013) - } - public static var zeroByteResource: URLError.Code { - return URLError.Code(rawValue: -1014) - } - public static var cannotDecodeRawData: URLError.Code { - return URLError.Code(rawValue: -1015) - } - public static var cannotDecodeContentData: URLError.Code { - return URLError.Code(rawValue: -1016) - } - public static var cannotParseResponse: URLError.Code { - return URLError.Code(rawValue: -1017) - } - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - public static var appTransportSecurityRequiresSecureConnection: URLError.Code { - return URLError.Code(rawValue: -1022) - } - public static var fileDoesNotExist: URLError.Code { - return URLError.Code(rawValue: -1100) - } - public static var fileIsDirectory: URLError.Code { - return URLError.Code(rawValue: -1101) - } - public static var noPermissionsToReadFile: URLError.Code { - return URLError.Code(rawValue: -1102) - } - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var dataLengthExceedsMaximum: URLError.Code { - return URLError.Code(rawValue: -1103) - } - public static var secureConnectionFailed: URLError.Code { - return URLError.Code(rawValue: -1200) - } - public static var serverCertificateHasBadDate: URLError.Code { - return URLError.Code(rawValue: -1201) - } - public static var serverCertificateUntrusted: URLError.Code { - return URLError.Code(rawValue: -1202) - } - public static var serverCertificateHasUnknownRoot: URLError.Code { - return URLError.Code(rawValue: -1203) - } - public static var serverCertificateNotYetValid: URLError.Code { - return URLError.Code(rawValue: -1204) - } - public static var clientCertificateRejected: URLError.Code { - return URLError.Code(rawValue: -1205) - } - public static var clientCertificateRequired: URLError.Code { - return URLError.Code(rawValue: -1206) - } - public static var cannotLoadFromNetwork: URLError.Code { - return URLError.Code(rawValue: -2000) - } - public static var cannotCreateFile: URLError.Code { - return URLError.Code(rawValue: -3000) - } - public static var cannotOpenFile: URLError.Code { - return URLError.Code(rawValue: -3001) - } - public static var cannotCloseFile: URLError.Code { - return URLError.Code(rawValue: -3002) - } - public static var cannotWriteToFile: URLError.Code { - return URLError.Code(rawValue: -3003) - } - public static var cannotRemoveFile: URLError.Code { - return URLError.Code(rawValue: -3004) - } - public static var cannotMoveFile: URLError.Code { - return URLError.Code(rawValue: -3005) - } - public static var downloadDecodingFailedMidStream: URLError.Code { - return URLError.Code(rawValue: -3006) - } - public static var downloadDecodingFailedToComplete: URLError.Code { - return URLError.Code(rawValue: -3007) - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 3.0) - public static var internationalRoamingOff: URLError.Code { - return URLError.Code(rawValue: -1018) - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 3.0) - public static var callIsActive: URLError.Code { - return URLError.Code(rawValue: -1019) - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 3.0) - public static var dataNotAllowed: URLError.Code { - return URLError.Code(rawValue: -1020) - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 3.0) - public static var requestBodyStreamExhausted: URLError.Code { - return URLError.Code(rawValue: -1021) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var backgroundSessionRequiresSharedContainer: URLError.Code { - return URLError.Code(rawValue: -995) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var backgroundSessionInUseByAnotherProcess: URLError.Code { - return URLError.Code(rawValue: -996) - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var backgroundSessionWasDisconnected: URLError.Code { - return URLError.Code(rawValue: -997) - } -} - -extension URLError { - /// Reasons used by URLError to indicate why a background URLSessionTask was cancelled. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public enum BackgroundTaskCancelledReason : Int { - case userForceQuitApplication - case backgroundUpdatesDisabled - case insufficientSystemResources - } -} - -extension URLError { - /// Reasons used by URLError to indicate that a URLSessionTask failed because of unsatisfiable network constraints. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public enum NetworkUnavailableReason : Int { - case cellular - case expensive - case constrained - } -} - -extension URLError { - private var _nsUserInfo: [AnyHashable : Any] { - return (self as NSError).userInfo - } - - /// The URL which caused a load to fail. - public var failingURL: URL? { - return _nsUserInfo[NSURLErrorFailingURLErrorKey as NSString] as? URL - } - - /// The string for the URL which caused a load to fail. - public var failureURLString: String? { - return _nsUserInfo[NSURLErrorFailingURLStringErrorKey as NSString] as? String - } - - /// The state of a failed SSL handshake. - public var failureURLPeerTrust: SecTrust? { - if let secTrust = _nsUserInfo[NSURLErrorFailingURLPeerTrustErrorKey as NSString] { - return (secTrust as! SecTrust) - } - - return nil - } - - /// The reason why a background URLSessionTask was cancelled. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public var backgroundTaskCancelledReason: BackgroundTaskCancelledReason? { - return (_nsUserInfo[NSURLErrorBackgroundTaskCancelledReasonKey] as? Int).flatMap(BackgroundTaskCancelledReason.init(rawValue:)) - } - - /// The reason why the network is unavailable when the task failed due to unsatisfiable network constraints. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public var networkUnavailableReason: NetworkUnavailableReason? { - return (_nsUserInfo[NSURLErrorNetworkUnavailableReasonKey] as? Int).flatMap(NetworkUnavailableReason.init(rawValue:)) - } - - /// An opaque data blob to resume a failed download task. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public var downloadTaskResumeData: Data? { - return _nsUserInfo[NSURLSessionDownloadTaskResumeData] as? Data - } -} - -extension URLError { - public static var unknown: URLError.Code { - return .unknown - } - - public static var cancelled: URLError.Code { - return .cancelled - } - - public static var badURL: URLError.Code { - return .badURL - } - - public static var timedOut: URLError.Code { - return .timedOut - } - - public static var unsupportedURL: URLError.Code { - return .unsupportedURL - } - - public static var cannotFindHost: URLError.Code { - return .cannotFindHost - } - - public static var cannotConnectToHost: URLError.Code { - return .cannotConnectToHost - } - - public static var networkConnectionLost: URLError.Code { - return .networkConnectionLost - } - - public static var dnsLookupFailed: URLError.Code { - return .dnsLookupFailed - } - - public static var httpTooManyRedirects: URLError.Code { - return .httpTooManyRedirects - } - - public static var resourceUnavailable: URLError.Code { - return .resourceUnavailable - } - - public static var notConnectedToInternet: URLError.Code { - return .notConnectedToInternet - } - - public static var redirectToNonExistentLocation: URLError.Code { - return .redirectToNonExistentLocation - } - - public static var badServerResponse: URLError.Code { - return .badServerResponse - } - - public static var userCancelledAuthentication: URLError.Code { - return .userCancelledAuthentication - } - - public static var userAuthenticationRequired: URLError.Code { - return .userAuthenticationRequired - } - - public static var zeroByteResource: URLError.Code { - return .zeroByteResource - } - - public static var cannotDecodeRawData: URLError.Code { - return .cannotDecodeRawData - } - - public static var cannotDecodeContentData: URLError.Code { - return .cannotDecodeContentData - } - - public static var cannotParseResponse: URLError.Code { - return .cannotParseResponse - } - - @available(macOS, introduced: 10.11) @available(iOS, introduced: 9.0) - public static var appTransportSecurityRequiresSecureConnection: URLError.Code { - return .appTransportSecurityRequiresSecureConnection - } - - public static var fileDoesNotExist: URLError.Code { - return .fileDoesNotExist - } - - public static var fileIsDirectory: URLError.Code { - return .fileIsDirectory - } - - public static var noPermissionsToReadFile: URLError.Code { - return .noPermissionsToReadFile - } - - @available(macOS, introduced: 10.5) @available(iOS, introduced: 2.0) - public static var dataLengthExceedsMaximum: URLError.Code { - return .dataLengthExceedsMaximum - } - - public static var secureConnectionFailed: URLError.Code { - return .secureConnectionFailed - } - - public static var serverCertificateHasBadDate: URLError.Code { - return .serverCertificateHasBadDate - } - - public static var serverCertificateUntrusted: URLError.Code { - return .serverCertificateUntrusted - } - - public static var serverCertificateHasUnknownRoot: URLError.Code { - return .serverCertificateHasUnknownRoot - } - - public static var serverCertificateNotYetValid: URLError.Code { - return .serverCertificateNotYetValid - } - - public static var clientCertificateRejected: URLError.Code { - return .clientCertificateRejected - } - - public static var clientCertificateRequired: URLError.Code { - return .clientCertificateRequired - } - - public static var cannotLoadFromNetwork: URLError.Code { - return .cannotLoadFromNetwork - } - - public static var cannotCreateFile: URLError.Code { - return .cannotCreateFile - } - - public static var cannotOpenFile: URLError.Code { - return .cannotOpenFile - } - - public static var cannotCloseFile: URLError.Code { - return .cannotCloseFile - } - - public static var cannotWriteToFile: URLError.Code { - return .cannotWriteToFile - } - - public static var cannotRemoveFile: URLError.Code { - return .cannotRemoveFile - } - - public static var cannotMoveFile: URLError.Code { - return .cannotMoveFile - } - - public static var downloadDecodingFailedMidStream: URLError.Code { - return .downloadDecodingFailedMidStream - } - - public static var downloadDecodingFailedToComplete: URLError.Code { - return .downloadDecodingFailedToComplete - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 3.0) - public static var internationalRoamingOff: URLError.Code { - return .internationalRoamingOff - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 3.0) - public static var callIsActive: URLError.Code { - return .callIsActive - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 3.0) - public static var dataNotAllowed: URLError.Code { - return .dataNotAllowed - } - - @available(macOS, introduced: 10.7) @available(iOS, introduced: 3.0) - public static var requestBodyStreamExhausted: URLError.Code { - return .requestBodyStreamExhausted - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var backgroundSessionRequiresSharedContainer: Code { - return .backgroundSessionRequiresSharedContainer - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var backgroundSessionInUseByAnotherProcess: Code { - return .backgroundSessionInUseByAnotherProcess - } - - @available(macOS, introduced: 10.10) @available(iOS, introduced: 8.0) - public static var backgroundSessionWasDisconnected: Code { - return .backgroundSessionWasDisconnected - } -} - -extension URLError { - @available(*, unavailable, renamed: "unknown") - public static var Unknown: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cancelled") - public static var Cancelled: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "badURL") - public static var BadURL: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "timedOut") - public static var TimedOut: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "unsupportedURL") - public static var UnsupportedURL: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotFindHost") - public static var CannotFindHost: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotConnectToHost") - public static var CannotConnectToHost: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "networkConnectionLost") - public static var NetworkConnectionLost: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "dnsLookupFailed") - public static var DNSLookupFailed: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "httpTooManyRedirects") - public static var HTTPTooManyRedirects: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "resourceUnavailable") - public static var ResourceUnavailable: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "notConnectedToInternet") - public static var NotConnectedToInternet: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "redirectToNonExistentLocation") - public static var RedirectToNonExistentLocation: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "badServerResponse") - public static var BadServerResponse: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "userCancelledAuthentication") - public static var UserCancelledAuthentication: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "userAuthenticationRequired") - public static var UserAuthenticationRequired: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "zeroByteResource") - public static var ZeroByteResource: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotDecodeRawData") - public static var CannotDecodeRawData: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotDecodeContentData") - public static var CannotDecodeContentData: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotParseResponse") - public static var CannotParseResponse: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "appTransportSecurityRequiresSecureConnection") - public static var AppTransportSecurityRequiresSecureConnection: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileDoesNotExist") - public static var FileDoesNotExist: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "fileIsDirectory") - public static var FileIsDirectory: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "noPermissionsToReadFile") - public static var NoPermissionsToReadFile: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "dataLengthExceedsMaximum") - public static var DataLengthExceedsMaximum: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "secureConnectionFailed") - public static var SecureConnectionFailed: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "serverCertificateHasBadDate") - public static var ServerCertificateHasBadDate: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "serverCertificateUntrusted") - public static var ServerCertificateUntrusted: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "serverCertificateHasUnknownRoot") - public static var ServerCertificateHasUnknownRoot: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "serverCertificateNotYetValid") - public static var ServerCertificateNotYetValid: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "clientCertificateRejected") - public static var ClientCertificateRejected: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "clientCertificateRequired") - public static var ClientCertificateRequired: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotLoadFromNetwork") - public static var CannotLoadFromNetwork: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotCreateFile") - public static var CannotCreateFile: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotOpenFile") - public static var CannotOpenFile: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotCloseFile") - public static var CannotCloseFile: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotWriteToFile") - public static var CannotWriteToFile: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotRemoveFile") - public static var CannotRemoveFile: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "cannotMoveFile") - public static var CannotMoveFile: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "downloadDecodingFailedMidStream") - public static var DownloadDecodingFailedMidStream: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "downloadDecodingFailedToComplete") - public static var DownloadDecodingFailedToComplete: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "internationalRoamingOff") - public static var InternationalRoamingOff: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "callIsActive") - public static var CallIsActive: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "dataNotAllowed") - public static var DataNotAllowed: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "requestBodyStreamExhausted") - public static var RequestBodyStreamExhausted: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "backgroundSessionRequiresSharedContainer") - public static var BackgroundSessionRequiresSharedContainer: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "backgroundSessionInUseByAnotherProcess") - public static var BackgroundSessionInUseByAnotherProcess: URLError.Code { - fatalError("unavailable accessor can't be called") - } - - @available(*, unavailable, renamed: "backgroundSessionWasDisconnected") - public static var BackgroundSessionWasDisconnected: URLError.Code { - fatalError("unavailable accessor can't be called") - } -} - -/// Describes an error in the POSIX error domain. -public struct POSIXError : _BridgedStoredNSError { - public let _nsError: NSError - - public init(_nsError error: NSError) { - precondition(error.domain == NSPOSIXErrorDomain) - self._nsError = error - } - - public static var errorDomain: String { return NSPOSIXErrorDomain } - - public var hashValue: Int { - return _nsError.hashValue - } - - public typealias Code = POSIXErrorCode -} - -extension POSIXErrorCode : _ErrorCodeProtocol { - public typealias _ErrorType = POSIXError -} - -extension POSIXError { - public static var EPERM: POSIXErrorCode { - return .EPERM - } - - /// No such file or directory. - public static var ENOENT: POSIXErrorCode { - return .ENOENT - } - - /// No such process. - public static var ESRCH: POSIXErrorCode { - return .ESRCH - } - - /// Interrupted system call. - public static var EINTR: POSIXErrorCode { - return .EINTR - } - - /// Input/output error. - public static var EIO: POSIXErrorCode { - return .EIO - } - - /// Device not configured. - public static var ENXIO: POSIXErrorCode { - return .ENXIO - } - - /// Argument list too long. - public static var E2BIG: POSIXErrorCode { - return .E2BIG - } - - /// Exec format error. - public static var ENOEXEC: POSIXErrorCode { - return .ENOEXEC - } - - /// Bad file descriptor. - public static var EBADF: POSIXErrorCode { - return .EBADF - } - - /// No child processes. - public static var ECHILD: POSIXErrorCode { - return .ECHILD - } - - /// Resource deadlock avoided. - public static var EDEADLK: POSIXErrorCode { - return .EDEADLK - } - - /// Cannot allocate memory. - public static var ENOMEM: POSIXErrorCode { - return .ENOMEM - } - - /// Permission denied. - public static var EACCES: POSIXErrorCode { - return .EACCES - } - - /// Bad address. - public static var EFAULT: POSIXErrorCode { - return .EFAULT - } - - /// Block device required. - public static var ENOTBLK: POSIXErrorCode { - return .ENOTBLK - } - /// Device / Resource busy. - public static var EBUSY: POSIXErrorCode { - return .EBUSY - } - /// File exists. - public static var EEXIST: POSIXErrorCode { - return .EEXIST - } - /// Cross-device link. - public static var EXDEV: POSIXErrorCode { - return .EXDEV - } - /// Operation not supported by device. - public static var ENODEV: POSIXErrorCode { - return .ENODEV - } - /// Not a directory. - public static var ENOTDIR: POSIXErrorCode { - return .ENOTDIR - } - /// Is a directory. - public static var EISDIR: POSIXErrorCode { - return .EISDIR - } - /// Invalid argument. - public static var EINVAL: POSIXErrorCode { - return .EINVAL - } - /// Too many open files in system. - public static var ENFILE: POSIXErrorCode { - return .ENFILE - } - /// Too many open files. - public static var EMFILE: POSIXErrorCode { - return .EMFILE - } - /// Inappropriate ioctl for device. - public static var ENOTTY: POSIXErrorCode { - return .ENOTTY - } - /// Text file busy. - public static var ETXTBSY: POSIXErrorCode { - return .ETXTBSY - } - /// File too large. - public static var EFBIG: POSIXErrorCode { - return .EFBIG - } - /// No space left on device. - public static var ENOSPC: POSIXErrorCode { - return .ENOSPC - } - /// Illegal seek. - public static var ESPIPE: POSIXErrorCode { - return .ESPIPE - } - /// Read-only file system. - public static var EROFS: POSIXErrorCode { - return .EROFS - } - /// Too many links. - public static var EMLINK: POSIXErrorCode { - return .EMLINK - } - /// Broken pipe. - public static var EPIPE: POSIXErrorCode { - return .EPIPE - } - -/// math software. - /// Numerical argument out of domain. - public static var EDOM: POSIXErrorCode { - return .EDOM - } - /// Result too large. - public static var ERANGE: POSIXErrorCode { - return .ERANGE - } - -/// non-blocking and interrupt i/o. - /// Resource temporarily unavailable. - public static var EAGAIN: POSIXErrorCode { - return .EAGAIN - } - /// Operation would block. - public static var EWOULDBLOCK: POSIXErrorCode { - return .EWOULDBLOCK - } - /// Operation now in progress. - public static var EINPROGRESS: POSIXErrorCode { - return .EINPROGRESS - } - /// Operation already in progress. - public static var EALREADY: POSIXErrorCode { - return .EALREADY - } - -/// ipc/network software -- argument errors. - /// Socket operation on non-socket. - public static var ENOTSOCK: POSIXErrorCode { - return .ENOTSOCK - } - /// Destination address required. - public static var EDESTADDRREQ: POSIXErrorCode { - return .EDESTADDRREQ - } - /// Message too long. - public static var EMSGSIZE: POSIXErrorCode { - return .EMSGSIZE - } - /// Protocol wrong type for socket. - public static var EPROTOTYPE: POSIXErrorCode { - return .EPROTOTYPE - } - /// Protocol not available. - public static var ENOPROTOOPT: POSIXErrorCode { - return .ENOPROTOOPT - } - /// Protocol not supported. - public static var EPROTONOSUPPORT: POSIXErrorCode { - return .EPROTONOSUPPORT - } - /// Socket type not supported. - public static var ESOCKTNOSUPPORT: POSIXErrorCode { - return .ESOCKTNOSUPPORT - } - /// Operation not supported. - public static var ENOTSUP: POSIXErrorCode { - return .ENOTSUP - } - /// Protocol family not supported. - public static var EPFNOSUPPORT: POSIXErrorCode { - return .EPFNOSUPPORT - } - /// Address family not supported by protocol family. - public static var EAFNOSUPPORT: POSIXErrorCode { - return .EAFNOSUPPORT - } - - /// Address already in use. - public static var EADDRINUSE: POSIXErrorCode { - return .EADDRINUSE - } - /// Can't assign requested address. - public static var EADDRNOTAVAIL: POSIXErrorCode { - return .EADDRNOTAVAIL - } - -/// ipc/network software -- operational errors - /// Network is down. - public static var ENETDOWN: POSIXErrorCode { - return .ENETDOWN - } - /// Network is unreachable. - public static var ENETUNREACH: POSIXErrorCode { - return .ENETUNREACH - } - /// Network dropped connection on reset. - public static var ENETRESET: POSIXErrorCode { - return .ENETRESET - } - /// Software caused connection abort. - public static var ECONNABORTED: POSIXErrorCode { - return .ECONNABORTED - } - /// Connection reset by peer. - public static var ECONNRESET: POSIXErrorCode { - return .ECONNRESET - } - /// No buffer space available. - public static var ENOBUFS: POSIXErrorCode { - return .ENOBUFS - } - /// Socket is already connected. - public static var EISCONN: POSIXErrorCode { - return .EISCONN - } - /// Socket is not connected. - public static var ENOTCONN: POSIXErrorCode { - return .ENOTCONN - } - /// Can't send after socket shutdown. - public static var ESHUTDOWN: POSIXErrorCode { - return .ESHUTDOWN - } - /// Too many references: can't splice. - public static var ETOOMANYREFS: POSIXErrorCode { - return .ETOOMANYREFS - } - /// Operation timed out. - public static var ETIMEDOUT: POSIXErrorCode { - return .ETIMEDOUT - } - /// Connection refused. - public static var ECONNREFUSED: POSIXErrorCode { - return .ECONNREFUSED - } - - /// Too many levels of symbolic links. - public static var ELOOP: POSIXErrorCode { - return .ELOOP - } - /// File name too long. - public static var ENAMETOOLONG: POSIXErrorCode { - return .ENAMETOOLONG - } - - /// Host is down. - public static var EHOSTDOWN: POSIXErrorCode { - return .EHOSTDOWN - } - /// No route to host. - public static var EHOSTUNREACH: POSIXErrorCode { - return .EHOSTUNREACH - } - /// Directory not empty. - public static var ENOTEMPTY: POSIXErrorCode { - return .ENOTEMPTY - } - -/// quotas & mush. - /// Too many processes. - public static var EPROCLIM: POSIXErrorCode { - return .EPROCLIM - } - /// Too many users. - public static var EUSERS: POSIXErrorCode { - return .EUSERS - } - /// Disc quota exceeded. - public static var EDQUOT: POSIXErrorCode { - return .EDQUOT - } - -/// Network File System. - /// Stale NFS file handle. - public static var ESTALE: POSIXErrorCode { - return .ESTALE - } - /// Too many levels of remote in path. - public static var EREMOTE: POSIXErrorCode { - return .EREMOTE - } - /// RPC struct is bad. - public static var EBADRPC: POSIXErrorCode { - return .EBADRPC - } - /// RPC version wrong. - public static var ERPCMISMATCH: POSIXErrorCode { - return .ERPCMISMATCH - } - /// RPC prog. not avail. - public static var EPROGUNAVAIL: POSIXErrorCode { - return .EPROGUNAVAIL - } - /// Program version wrong. - public static var EPROGMISMATCH: POSIXErrorCode { - return .EPROGMISMATCH - } - /// Bad procedure for program. - public static var EPROCUNAVAIL: POSIXErrorCode { - return .EPROCUNAVAIL - } - - /// No locks available. - public static var ENOLCK: POSIXErrorCode { - return .ENOLCK - } - /// Function not implemented. - public static var ENOSYS: POSIXErrorCode { - return .ENOSYS - } - - /// Inappropriate file type or format. - public static var EFTYPE: POSIXErrorCode { - return .EFTYPE - } - /// Authentication error. - public static var EAUTH: POSIXErrorCode { - return .EAUTH - } - /// Need authenticator. - public static var ENEEDAUTH: POSIXErrorCode { - return .ENEEDAUTH - } - -/// Intelligent device errors. - /// Device power is off. - public static var EPWROFF: POSIXErrorCode { - return .EPWROFF - } - /// Device error, e.g. paper out. - public static var EDEVERR: POSIXErrorCode { - return .EDEVERR - } - - /// Value too large to be stored in data type. - public static var EOVERFLOW: POSIXErrorCode { - return .EOVERFLOW - } - -/// Program loading errors. - /// Bad executable. - public static var EBADEXEC: POSIXErrorCode { - return .EBADEXEC - } - /// Bad CPU type in executable. - public static var EBADARCH: POSIXErrorCode { - return .EBADARCH - } - /// Shared library version mismatch. - public static var ESHLIBVERS: POSIXErrorCode { - return .ESHLIBVERS - } - /// Malformed Macho file. - public static var EBADMACHO: POSIXErrorCode { - return .EBADMACHO - } - - /// Operation canceled. - public static var ECANCELED: POSIXErrorCode { - return .ECANCELED - } - - /// Identifier removed. - public static var EIDRM: POSIXErrorCode { - return .EIDRM - } - /// No message of desired type. - public static var ENOMSG: POSIXErrorCode { - return .ENOMSG - } - /// Illegal byte sequence. - public static var EILSEQ: POSIXErrorCode { - return .EILSEQ - } - /// Attribute not found. - public static var ENOATTR: POSIXErrorCode { - return .ENOATTR - } - - /// Bad message. - public static var EBADMSG: POSIXErrorCode { - return .EBADMSG - } - /// Reserved. - public static var EMULTIHOP: POSIXErrorCode { - return .EMULTIHOP - } - /// No message available on STREAM. - public static var ENODATA: POSIXErrorCode { - return .ENODATA - } - /// Reserved. - public static var ENOLINK: POSIXErrorCode { - return .ENOLINK - } - /// No STREAM resources. - public static var ENOSR: POSIXErrorCode { - return .ENOSR - } - /// Not a STREAM. - public static var ENOSTR: POSIXErrorCode { - return .ENOSTR - } - /// Protocol error. - public static var EPROTO: POSIXErrorCode { - return .EPROTO - } - /// STREAM ioctl timeout. - public static var ETIME: POSIXErrorCode { - return .ETIME - } - - /// No such policy registered. - public static var ENOPOLICY: POSIXErrorCode { - return .ENOPOLICY - } - - /// State not recoverable. - public static var ENOTRECOVERABLE: POSIXErrorCode { - return .ENOTRECOVERABLE - } - /// Previous owner died. - public static var EOWNERDEAD: POSIXErrorCode { - return .EOWNERDEAD - } - - /// Interface output queue is full. - public static var EQFULL: POSIXErrorCode { - return .EQFULL - } -} - -/// Describes an error in the Mach error domain. -public struct MachError : _BridgedStoredNSError { - public let _nsError: NSError - - public init(_nsError error: NSError) { - precondition(error.domain == NSMachErrorDomain) - self._nsError = error - } - - public static var errorDomain: String { return NSMachErrorDomain } - - public var hashValue: Int { - return _nsError.hashValue - } - - public typealias Code = MachErrorCode -} - -extension MachErrorCode : _ErrorCodeProtocol { - public typealias _ErrorType = MachError -} - -extension MachError { - public static var success: MachError.Code { - return .success - } - - /// Specified address is not currently valid. - public static var invalidAddress: MachError.Code { - return .invalidAddress - } - - /// Specified memory is valid, but does not permit the required - /// forms of access. - public static var protectionFailure: MachError.Code { - return .protectionFailure - } - - /// The address range specified is already in use, or no address - /// range of the size specified could be found. - public static var noSpace: MachError.Code { - return .noSpace - } - - /// The function requested was not applicable to this type of - /// argument, or an argument is invalid. - public static var invalidArgument: MachError.Code { - return .invalidArgument - } - - /// The function could not be performed. A catch-all. - public static var failure: MachError.Code { - return .failure - } - - /// A system resource could not be allocated to fulfill this - /// request. This failure may not be permanent. - public static var resourceShortage: MachError.Code { - return .resourceShortage - } - - /// The task in question does not hold receive rights for the port - /// argument. - public static var notReceiver: MachError.Code { - return .notReceiver - } - - /// Bogus access restriction. - public static var noAccess: MachError.Code { - return .noAccess - } - - /// During a page fault, the target address refers to a memory - /// object that has been destroyed. This failure is permanent. - public static var memoryFailure: MachError.Code { - return .memoryFailure - } - - /// During a page fault, the memory object indicated that the data - /// could not be returned. This failure may be temporary; future - /// attempts to access this same data may succeed, as defined by the - /// memory object. - public static var memoryError: MachError.Code { - return .memoryError - } - - /// The receive right is already a member of the portset. - public static var alreadyInSet: MachError.Code { - return .alreadyInSet - } - - /// The receive right is not a member of a port set. - public static var notInSet: MachError.Code { - return .notInSet - } - - /// The name already denotes a right in the task. - public static var nameExists: MachError.Code { - return .nameExists - } - - /// The operation was aborted. Ipc code will catch this and reflect - /// it as a message error. - public static var aborted: MachError.Code { - return .aborted - } - - /// The name doesn't denote a right in the task. - public static var invalidName: MachError.Code { - return .invalidName - } - - /// Target task isn't an active task. - public static var invalidTask: MachError.Code { - return .invalidTask - } - - /// The name denotes a right, but not an appropriate right. - public static var invalidRight: MachError.Code { - return .invalidRight - } - - /// A blatant range error. - public static var invalidValue: MachError.Code { - return .invalidValue - } - - /// Operation would overflow limit on user-references. - public static var userReferencesOverflow: MachError.Code { - return .userReferencesOverflow - } - - /// The supplied (port) capability is improper. - public static var invalidCapability: MachError.Code { - return .invalidCapability - } - - /// The task already has send or receive rights for the port under - /// another name. - public static var rightExists: MachError.Code { - return .rightExists - } - - /// Target host isn't actually a host. - public static var invalidHost: MachError.Code { - return .invalidHost - } - - /// An attempt was made to supply "precious" data for memory that is - /// already present in a memory object. - public static var memoryPresent: MachError.Code { - return .memoryPresent - } - - /// A page was requested of a memory manager via - /// memory_object_data_request for an object using a - /// MEMORY_OBJECT_COPY_CALL strategy, with the VM_PROT_WANTS_COPY - /// flag being used to specify that the page desired is for a copy - /// of the object, and the memory manager has detected the page was - /// pushed into a copy of the object while the kernel was walking - /// the shadow chain from the copy to the object. This error code is - /// delivered via memory_object_data_error and is handled by the - /// kernel (it forces the kernel to restart the fault). It will not - /// be seen by users. - public static var memoryDataMoved: MachError.Code { - return .memoryDataMoved - } - - /// A strategic copy was attempted of an object upon which a quicker - /// copy is now possible. The caller should retry the copy using - /// vm_object_copy_quickly. This error code is seen only by the - /// kernel. - public static var memoryRestartCopy: MachError.Code { - return .memoryRestartCopy - } - - /// An argument applied to assert processor set privilege was not a - /// processor set control port. - public static var invalidProcessorSet: MachError.Code { - return .invalidProcessorSet - } - - /// The specified scheduling attributes exceed the thread's limits. - public static var policyLimit: MachError.Code { - return .policyLimit - } - - /// The specified scheduling policy is not currently enabled for the - /// processor set. - public static var invalidPolicy: MachError.Code { - return .invalidPolicy - } - - /// The external memory manager failed to initialize the memory object. - public static var invalidObject: MachError.Code { - return .invalidObject - } - - /// A thread is attempting to wait for an event for which there is - /// already a waiting thread. - public static var alreadyWaiting: MachError.Code { - return .alreadyWaiting - } - - /// An attempt was made to destroy the default processor set. - public static var defaultSet: MachError.Code { - return .defaultSet - } - - /// An attempt was made to fetch an exception port that is - /// protected, or to abort a thread while processing a protected - /// exception. - public static var exceptionProtected: MachError.Code { - return .exceptionProtected - } - - /// A ledger was required but not supplied. - public static var invalidLedger: MachError.Code { - return .invalidLedger - } - - /// The port was not a memory cache control port. - public static var invalidMemoryControl: MachError.Code { - return .invalidMemoryControl - } - - /// An argument supplied to assert security privilege was not a host - /// security port. - public static var invalidSecurity: MachError.Code { - return .invalidSecurity - } - - /// thread_depress_abort was called on a thread which was not - /// currently depressed. - public static var notDepressed: MachError.Code { - return .notDepressed - } - - /// Object has been terminated and is no longer available. - public static var terminated: MachError.Code { - return .terminated - } - - /// Lock set has been destroyed and is no longer available. - public static var lockSetDestroyed: MachError.Code { - return .lockSetDestroyed - } - - /// The thread holding the lock terminated before releasing the lock. - public static var lockUnstable: MachError.Code { - return .lockUnstable - } - - /// The lock is already owned by another thread. - public static var lockOwned: MachError.Code { - return .lockOwned - } - - /// The lock is already owned by the calling thread. - public static var lockOwnedSelf: MachError.Code { - return .lockOwnedSelf - } - - /// Semaphore has been destroyed and is no longer available. - public static var semaphoreDestroyed: MachError.Code { - return .semaphoreDestroyed - } - - /// Return from RPC indicating the target server was terminated - /// before it successfully replied. - public static var rpcServerTerminated: MachError.Code { - return .rpcServerTerminated - } - - /// Terminate an orphaned activation. - public static var rpcTerminateOrphan: MachError.Code { - return .rpcTerminateOrphan - } - - /// Allow an orphaned activation to continue executing. - public static var rpcContinueOrphan: MachError.Code { - return .rpcContinueOrphan - } - - /// Empty thread activation (No thread linked to it). - public static var notSupported: MachError.Code { - return .notSupported - } - - /// Remote node down or inaccessible. - public static var nodeDown: MachError.Code { - return .nodeDown - } - - /// A signalled thread was not actually waiting. - public static var notWaiting: MachError.Code { - return .notWaiting - } - - /// Some thread-oriented operation (semaphore_wait) timed out. - public static var operationTimedOut: MachError.Code { - return .operationTimedOut - } - - /// During a page fault, indicates that the page was rejected as a - /// result of a signature check. - public static var codesignError: MachError.Code { - return .codesignError - } - - /// The requested property cannot be changed at this time. - public static var policyStatic: MachError.Code { - return .policyStatic - } -} - -public struct ErrorUserInfoKey : RawRepresentable, _SwiftNewtypeWrapper, Equatable, Hashable, _ObjectiveCBridgeable { - public typealias _ObjectiveCType = NSString - public init(rawValue: String) { self.rawValue = rawValue } - public var rawValue: String -} - -public extension ErrorUserInfoKey { - @available(*, deprecated, renamed: "NSUnderlyingErrorKey") - static let underlyingErrorKey = ErrorUserInfoKey(rawValue: NSUnderlyingErrorKey) - - @available(*, deprecated, renamed: "NSLocalizedDescriptionKey") - static let localizedDescriptionKey = ErrorUserInfoKey(rawValue: NSLocalizedDescriptionKey) - - @available(*, deprecated, renamed: "NSLocalizedFailureReasonErrorKey") - static let localizedFailureReasonErrorKey = ErrorUserInfoKey(rawValue: NSLocalizedFailureReasonErrorKey) - - @available(*, deprecated, renamed: "NSLocalizedRecoverySuggestionErrorKey") - static let localizedRecoverySuggestionErrorKey = ErrorUserInfoKey(rawValue: NSLocalizedRecoverySuggestionErrorKey) - - @available(*, deprecated, renamed: "NSLocalizedRecoveryOptionsErrorKey") - static let localizedRecoveryOptionsErrorKey = ErrorUserInfoKey(rawValue: NSLocalizedRecoveryOptionsErrorKey) - - @available(*, deprecated, renamed: "NSRecoveryAttempterErrorKey") - static let recoveryAttempterErrorKey = ErrorUserInfoKey(rawValue: NSRecoveryAttempterErrorKey) - - @available(*, deprecated, renamed: "NSHelpAnchorErrorKey") - static let helpAnchorErrorKey = ErrorUserInfoKey(rawValue: NSHelpAnchorErrorKey) - - @available(*, deprecated, renamed: "NSStringEncodingErrorKey") - static let stringEncodingErrorKey = ErrorUserInfoKey(rawValue: NSStringEncodingErrorKey) - - @available(*, deprecated, renamed: "NSURLErrorKey") - static let NSURLErrorKey = ErrorUserInfoKey(rawValue: Foundation.NSURLErrorKey) - - @available(*, deprecated, renamed: "NSFilePathErrorKey") - static let filePathErrorKey = ErrorUserInfoKey(rawValue: NSFilePathErrorKey) -} diff --git a/stdlib/public/Darwin/Foundation/NSExpression.swift b/stdlib/public/Darwin/Foundation/NSExpression.swift deleted file mode 100644 index d0562f5e0175d..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSExpression.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -extension NSExpression { - // + (NSExpression *) expressionWithFormat:(NSString *)expressionFormat, ...; - public - convenience init(format expressionFormat: __shared String, _ args: CVarArg...) { - let va_args = getVaList(args) - self.init(format: expressionFormat, arguments: va_args) - } -} - -extension NSExpression { - public convenience init(forKeyPath keyPath: KeyPath) { - self.init(forKeyPath: _bridgeKeyPathToString(keyPath)) - } -} diff --git a/stdlib/public/Darwin/Foundation/NSFastEnumeration.swift b/stdlib/public/Darwin/Foundation/NSFastEnumeration.swift deleted file mode 100644 index dcee776a7d148..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSFastEnumeration.swift +++ /dev/null @@ -1,96 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -/// A dummy value to be used as the target for `mutationsPtr` in fast enumeration implementations. -private var _fastEnumerationMutationsTarget: CUnsignedLong = 0 -/// A dummy pointer to be used as `mutationsPtr` in fast enumeration implementations. -private let _fastEnumerationMutationsPtr = UnsafeMutablePointer(&_fastEnumerationMutationsTarget) - -//===----------------------------------------------------------------------===// -// Fast enumeration -//===----------------------------------------------------------------------===// -public struct NSFastEnumerationIterator : IteratorProtocol { - var enumerable: NSFastEnumeration - var objects: (Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?, Unmanaged?) = (nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) - var state = NSFastEnumerationState(state: 0, itemsPtr: nil, mutationsPtr: _fastEnumerationMutationsPtr, extra: (0, 0, 0, 0, 0)) - var index = 0 - var count = 0 - var useObjectsBuffer = false - - public init(_ enumerable: NSFastEnumeration) { - self.enumerable = enumerable - } - - public mutating func next() -> Any? { - if index + 1 > count { - index = 0 - // ensure NO ivars of self are actually captured - let enumeratedObject = enumerable - var localState = state - var localObjects = objects - - (count, useObjectsBuffer) = withUnsafeMutablePointer(to: &localObjects) { - let buffer = AutoreleasingUnsafeMutablePointer($0) - return withUnsafeMutablePointer(to: &localState) { (statePtr: UnsafeMutablePointer) -> (Int, Bool) in - let result = enumeratedObject.countByEnumerating(with: statePtr, objects: buffer, count: 16) - if statePtr.pointee.itemsPtr == buffer { - // Most cocoa classes will emit their own inner pointer buffers instead of traversing this path. Notable exceptions include NSDictionary and NSSet - return (result, true) - } else { - // this is the common case for things like NSArray - return (result, false) - } - } - } - - state = localState // restore the state value - objects = localObjects // copy the object pointers back to the self storage - - if count == 0 { return nil } - } - defer { index += 1 } - if !useObjectsBuffer { - return state.itemsPtr![index] - } else { - switch index { - case 0: return objects.0!.takeUnretainedValue() - case 1: return objects.1!.takeUnretainedValue() - case 2: return objects.2!.takeUnretainedValue() - case 3: return objects.3!.takeUnretainedValue() - case 4: return objects.4!.takeUnretainedValue() - case 5: return objects.5!.takeUnretainedValue() - case 6: return objects.6!.takeUnretainedValue() - case 7: return objects.7!.takeUnretainedValue() - case 8: return objects.8!.takeUnretainedValue() - case 9: return objects.9!.takeUnretainedValue() - case 10: return objects.10!.takeUnretainedValue() - case 11: return objects.11!.takeUnretainedValue() - case 12: return objects.12!.takeUnretainedValue() - case 13: return objects.13!.takeUnretainedValue() - case 14: return objects.14!.takeUnretainedValue() - case 15: return objects.15!.takeUnretainedValue() - default: fatalError("Access beyond storage buffer") - } - } - } -} - -extension NSEnumerator : Sequence { - /// Return an *iterator* over the *enumerator*. - /// - /// - Complexity: O(1). - public func makeIterator() -> NSFastEnumerationIterator { - return NSFastEnumerationIterator(self) - } -} diff --git a/stdlib/public/Darwin/Foundation/NSGeometry.swift b/stdlib/public/Darwin/Foundation/NSGeometry.swift deleted file mode 100644 index 4995bf5e20fa0..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSGeometry.swift +++ /dev/null @@ -1,37 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import CoreGraphics - - -#if os(macOS) - -//===----------------------------------------------------------------------===// -// NSRectEdge -//===----------------------------------------------------------------------===// - -extension NSRectEdge { - @inlinable - public init(rectEdge: CGRectEdge) { - self = NSRectEdge(rawValue: UInt(rectEdge.rawValue))! - } -} - -extension CGRectEdge { - @inlinable - public init(rectEdge: NSRectEdge) { - self = CGRectEdge(rawValue: UInt32(rectEdge.rawValue))! - } -} - -#endif diff --git a/stdlib/public/Darwin/Foundation/NSIndexSet.swift b/stdlib/public/Darwin/Foundation/NSIndexSet.swift deleted file mode 100644 index 5bc535ec0b97b..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSIndexSet.swift +++ /dev/null @@ -1,51 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -// TODO: Evaluate deprecating with a message in favor of IndexSet. -public struct NSIndexSetIterator : IteratorProtocol { - public typealias Element = Int - - internal let _set: NSIndexSet - internal var _first: Bool = true - internal var _current: Int? - - internal init(set: NSIndexSet) { - self._set = set - self._current = nil - } - - public mutating func next() -> Int? { - if _first { - _current = _set.firstIndex - _first = false - } else if let c = _current { - _current = _set.indexGreaterThanIndex(c) - } else { - // current is already nil - } - if _current == NSNotFound { - _current = nil - } - return _current - } -} - -extension NSIndexSet : Sequence { - /// Return an *iterator* over the elements of this *sequence*. - /// - /// - Complexity: O(1). - public func makeIterator() -> NSIndexSetIterator { - return NSIndexSetIterator(set: self) - } -} diff --git a/stdlib/public/Darwin/Foundation/NSItemProvider.swift b/stdlib/public/Darwin/Foundation/NSItemProvider.swift deleted file mode 100644 index af8456eeddd0b..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSItemProvider.swift +++ /dev/null @@ -1,56 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -@available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) -extension NSItemProvider { - - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public func registerObject< - T : _ObjectiveCBridgeable - > ( - ofClass: T.Type, - visibility: NSItemProviderRepresentationVisibility, - loadHandler: @escaping ((T?, Error?) -> Void) -> Progress? - ) where T._ObjectiveCType : NSItemProviderWriting { - self.registerObject( - ofClass: T._ObjectiveCType.self, visibility: visibility) { - completionHandler in loadHandler { - // Using `x as! T._ObjectiveCType?` triggers an assertion in the - // compiler, hence the explicit call to `_bridgeToObjectiveC`. - (x, error) in completionHandler(x?._bridgeToObjectiveC(), error) - } - } - } - - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public func canLoadObject< - T : _ObjectiveCBridgeable - >(ofClass: T.Type) -> Bool - where T._ObjectiveCType : NSItemProviderReading { - return self.canLoadObject(ofClass: T._ObjectiveCType.self) - } - - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public func loadObject< - T : _ObjectiveCBridgeable - >( - ofClass: T.Type, - completionHandler: @escaping (T?, Error?) -> Void - ) -> Progress where T._ObjectiveCType : NSItemProviderReading { - return self.loadObject(ofClass: T._ObjectiveCType.self) { - x, error in completionHandler(x as! T?, error) - } - } - -} diff --git a/stdlib/public/Darwin/Foundation/NSNumber.swift b/stdlib/public/Darwin/Foundation/NSNumber.swift deleted file mode 100644 index 5236064075d4d..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSNumber.swift +++ /dev/null @@ -1,700 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import CoreGraphics - -extension Int8 : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.int8Value - } - - public init(truncating number: __shared NSNumber) { - self = number.int8Value - } - - public init?(exactly number: __shared NSNumber) { - let value = number.int8Value - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout Int8?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout Int8?) -> Bool { - guard let value = Int8(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> Int8 { - var result: Int8? - guard let src = source else { return Int8(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return Int8(0) } - return result! - } -} - -extension UInt8 : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.uint8Value - } - - public init(truncating number: __shared NSNumber) { - self = number.uint8Value - } - - public init?(exactly number: __shared NSNumber) { - let value = number.uint8Value - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt8?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt8?) -> Bool { - guard let value = UInt8(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> UInt8 { - var result: UInt8? - guard let src = source else { return UInt8(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return UInt8(0) } - return result! - } -} - -extension Int16 : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.int16Value - } - - public init(truncating number: __shared NSNumber) { - self = number.int16Value - } - - public init?(exactly number: __shared NSNumber) { - let value = number.int16Value - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout Int16?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout Int16?) -> Bool { - guard let value = Int16(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> Int16 { - var result: Int16? - guard let src = source else { return Int16(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return Int16(0) } - return result! - } -} - -extension UInt16 : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.uint16Value - } - - public init(truncating number: __shared NSNumber) { - self = number.uint16Value - } - - public init?(exactly number: __shared NSNumber) { - let value = number.uint16Value - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt16?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt16?) -> Bool { - guard let value = UInt16(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> UInt16 { - var result: UInt16? - guard let src = source else { return UInt16(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return UInt16(0) } - return result! - } -} - -extension Int32 : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.int32Value - } - - public init(truncating number: __shared NSNumber) { - self = number.int32Value - } - - public init?(exactly number: __shared NSNumber) { - let value = number.int32Value - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout Int32?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout Int32?) -> Bool { - guard let value = Int32(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> Int32 { - var result: Int32? - guard let src = source else { return Int32(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return Int32(0) } - return result! - } -} - -extension UInt32 : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.uint32Value - } - - public init(truncating number: __shared NSNumber) { - self = number.uint32Value - } - - public init?(exactly number: __shared NSNumber) { - let value = number.uint32Value - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt32?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt32?) -> Bool { - guard let value = UInt32(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> UInt32 { - var result: UInt32? - guard let src = source else { return UInt32(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return UInt32(0) } - return result! - } -} - -extension Int64 : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.int64Value - } - - public init(truncating number: __shared NSNumber) { - self = number.int64Value - } - - public init?(exactly number: __shared NSNumber) { - let value = number.int64Value - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout Int64?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout Int64?) -> Bool { - guard let value = Int64(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> Int64 { - var result: Int64? - guard let src = source else { return Int64(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return Int64(0) } - return result! - } -} - -extension UInt64 : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.uint64Value - } - - public init(truncating number: __shared NSNumber) { - self = number.uint64Value - } - - public init?(exactly number: __shared NSNumber) { - let value = number.uint64Value - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt64?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt64?) -> Bool { - guard let value = UInt64(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> UInt64 { - var result: UInt64? - guard let src = source else { return UInt64(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return UInt64(0) } - return result! - } -} - -extension Int : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.intValue - } - - public init(truncating number: __shared NSNumber) { - self = number.intValue - } - - public init?(exactly number: __shared NSNumber) { - let value = number.intValue - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout Int?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout Int?) -> Bool { - guard let value = Int(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> Int { - var result: Int? - guard let src = source else { return Int(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return Int(0) } - return result! - } -} - -extension UInt : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.uintValue - } - - public init(truncating number: __shared NSNumber) { - self = number.uintValue - } - - public init?(exactly number: __shared NSNumber) { - let value = number.uintValue - guard NSNumber(value: value) == number else { return nil } - self = value - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout UInt?) -> Bool { - guard let value = UInt(exactly: x) else { return false } - result = value - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> UInt { - var result: UInt? - guard let src = source else { return UInt(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return UInt(0) } - return result! - } -} - -extension Float : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.floatValue - } - - public init(truncating number: __shared NSNumber) { - self = number.floatValue - } - - public init?(exactly number: __shared NSNumber) { - let type = number.objCType.pointee - if type == 0x49 || type == 0x4c || type == 0x51 { - guard let result = Float(exactly: number.uint64Value) else { return nil } - self = result - } else if type == 0x69 || type == 0x6c || type == 0x71 { - guard let result = Float(exactly: number.int64Value) else { return nil } - self = result - } else { - guard let result = Float(exactly: number.doubleValue) else { return nil } - self = result - } - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout Float?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout Float?) -> Bool { - if x.floatValue.isNaN { - result = x.floatValue - return true - } - result = Float(exactly: x) - return result != nil - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> Float { - var result: Float? - guard let src = source else { return Float(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return Float(0) } - return result! - } -} - -extension Double : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.doubleValue - } - - public init(truncating number: __shared NSNumber) { - self = number.doubleValue - } - - public init?(exactly number: __shared NSNumber) { - let type = number.objCType.pointee - if type == 0x51 { - guard let result = Double(exactly: number.uint64Value) else { return nil } - self = result - } else if type == 0x71 { - guard let result = Double(exactly: number.int64Value) else { return nil } - self = result - } else { - // All other integer types and single-precision floating points will - // fit in a `Double` without truncation. - guard let result = Double(exactly: number.doubleValue) else { return nil } - self = result - } - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout Double?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout Double?) -> Bool { - if x.doubleValue.isNaN { - result = x.doubleValue - return true - } - result = Double(exactly: x) - return result != nil - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> Double { - var result: Double? - guard let src = source else { return Double(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return Double(0) } - return result! - } -} - -extension Bool : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self = number.boolValue - } - - public init(truncating number: __shared NSNumber) { - self = number.boolValue - } - - public init?(exactly number: __shared NSNumber) { - if number === kCFBooleanTrue as NSNumber || NSNumber(value: 1) == number { - self = true - } else if number === kCFBooleanFalse as NSNumber || NSNumber(value: 0) == number { - self = false - } else { - return nil - } - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout Bool?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout Bool?) -> Bool { - if x === kCFBooleanTrue as NSNumber || NSNumber(value: 1) == x { - result = true - return true - } else if x === kCFBooleanFalse as NSNumber || NSNumber(value: 0) == x { - result = false - return true - } - - return false - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> Bool { - var result: Bool? - guard let src = source else { return false } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return false } - return result! - } -} - -extension CGFloat : _ObjectiveCBridgeable { - @available(swift, deprecated: 4, renamed: "init(truncating:)") - public init(_ number: __shared NSNumber) { - self.init(CGFloat.NativeType(truncating: number)) - } - - public init(truncating number: __shared NSNumber) { - self.init(CGFloat.NativeType(truncating: number)) - } - - public init?(exactly number: __shared NSNumber) { - var nativeValue: CGFloat.NativeType? = 0 - guard CGFloat.NativeType._conditionallyBridgeFromObjectiveC(number, result: &nativeValue) else { return nil } - self.init(nativeValue!) - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNumber { - return NSNumber(value: self.native) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNumber, result: inout CGFloat?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNumber, result: inout CGFloat?) -> Bool { - var nativeValue: CGFloat.NativeType? = 0 - guard CGFloat.NativeType._conditionallyBridgeFromObjectiveC(x, result: &nativeValue) else { return false } - result = CGFloat(nativeValue!) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNumber?) -> CGFloat { - var result: CGFloat? - guard let src = source else { return CGFloat(0) } - guard _conditionallyBridgeFromObjectiveC(src, result: &result) else { return CGFloat(0) } - return result! - } -} - -// Literal support for NSNumber -extension NSNumber : ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral, ExpressibleByBooleanLiteral { - /// Create an instance initialized to `value`. - @nonobjc - public required convenience init(integerLiteral value: Int) { - self.init(value: value) - } - - /// Create an instance initialized to `value`. - @nonobjc - public required convenience init(floatLiteral value: Double) { - self.init(value: value) - } - - /// Create an instance initialized to `value`. - @nonobjc - public required convenience init(booleanLiteral value: Bool) { - self.init(value: value) - } -} - -extension NSNumber : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to prevent infinite recursion trying to bridge - // AnyHashable to NSObject. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - // The custom AnyHashable representation here is used when checking for - // equality during bridging NSDictionary to Dictionary (or looking up - // values in a Dictionary bridged to NSDictionary). - // - // When we've got NSNumber values as keys that we want to compare - // through an AnyHashable box, we want to compare values through the - // largest box size we've got available to us (i.e. upcast numbers). - // This happens to resemble the representation that NSNumber uses - // internally: (long long | unsigned long long | double). - // - // This allows us to compare things like - // - // ([Int : Any] as [AnyHashable : Any]) vs. [NSNumber : Any] - // - // because Int can be upcast to Int64 and compared with the number's - // Int64 value. - // - // If NSNumber adds 128-bit representations, this will need to be - // updated to use those. - if let nsDecimalNumber: NSDecimalNumber = self as? NSDecimalNumber { - return AnyHashable(nsDecimalNumber.decimalValue) - } else if self === kCFBooleanTrue as NSNumber { - return AnyHashable(true) - } else if self === kCFBooleanFalse as NSNumber { - return AnyHashable(false) - } else if NSNumber(value: int64Value) == self { - return AnyHashable(int64Value) - } else if NSNumber(value: uint64Value) == self { - return AnyHashable(uint64Value) - } else if NSNumber(value: doubleValue) == self { - return AnyHashable(doubleValue) - } else { - return nil - } - } -} diff --git a/stdlib/public/Darwin/Foundation/NSObject.swift b/stdlib/public/Darwin/Foundation/NSObject.swift deleted file mode 100644 index eff69d661f546..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSObject.swift +++ /dev/null @@ -1,313 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2017 - 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import ObjectiveC -@_implementationOnly import _SwiftFoundationOverlayShims - -// This exists to allow for dynamic dispatch on KVO methods added to NSObject. -// Extending NSObject with these methods would disallow overrides. -public protocol _KeyValueCodingAndObserving {} -extension NSObject : _KeyValueCodingAndObserving {} - -public struct NSKeyValueObservedChange { - public typealias Kind = NSKeyValueChange - public let kind: Kind - ///newValue and oldValue will only be non-nil if .new/.old is passed to `observe()`. In general, get the most up to date value by accessing it directly on the observed object instead. - public let newValue: Value? - public let oldValue: Value? - ///indexes will be nil unless the observed KeyPath refers to an ordered to-many property - public let indexes: IndexSet? - ///'isPrior' will be true if this change observation is being sent before the change happens, due to .prior being passed to `observe()` - public let isPrior:Bool -} - -///Conforming to NSKeyValueObservingCustomization is not required to use Key-Value Observing. Provide an implementation of these functions if you need to disable auto-notifying for a key, or add dependent keys -public protocol NSKeyValueObservingCustomization : NSObjectProtocol { - static func keyPathsAffectingValue(for key: AnyKeyPath) -> Set - static func automaticallyNotifiesObservers(for key: AnyKeyPath) -> Bool -} - -private extension NSObject { - - @objc class func __old_unswizzled_automaticallyNotifiesObservers(forKey key: String?) -> Bool { - fatalError("Should never be reached") - } - - @objc class func __old_unswizzled_keyPathsForValuesAffectingValue(forKey key: String?) -> Set { - fatalError("Should never be reached") - } - -} - -// NOTE: older overlays called this _KVOKeyPathBridgeMachinery. The two -// must coexist, so it was renamed. The old name must not be used in the -// new runtime. -@objc private class __KVOKeyPathBridgeMachinery : NSObject { - @nonobjc static let swizzler: () = { - /* - Move all our methods into place. We want the following: - __KVOKeyPathBridgeMachinery's automaticallyNotifiesObserversForKey:, and keyPathsForValuesAffectingValueForKey: methods replaces NSObject's versions of them - NSObject's automaticallyNotifiesObserversForKey:, and keyPathsForValuesAffectingValueForKey: methods replace NSObject's __old_unswizzled_* methods - NSObject's _old_unswizzled_* methods replace __KVOKeyPathBridgeMachinery's methods, and are never invoked - */ - threeWaySwizzle(#selector(NSObject.keyPathsForValuesAffectingValue(forKey:)), with: #selector(NSObject.__old_unswizzled_keyPathsForValuesAffectingValue(forKey:))) - threeWaySwizzle(#selector(NSObject.automaticallyNotifiesObservers(forKey:)), with: #selector(NSObject.__old_unswizzled_automaticallyNotifiesObservers(forKey:))) - }() - - /// Performs a 3-way swizzle between `NSObject` and `__KVOKeyPathBridgeMachinery`. - /// - /// The end result of this swizzle is the following: - /// * `NSObject.selector` contains the IMP from `__KVOKeyPathBridgeMachinery.selector` - /// * `NSObject.unswizzledSelector` contains the IMP from the original `NSObject.selector`. - /// * __KVOKeyPathBridgeMachinery.selector` contains the (useless) IMP from `NSObject.unswizzledSelector`. - /// - /// This swizzle is done in a manner that modifies `NSObject.selector` last, in order to ensure thread safety - /// (by the time `NSObject.selector` is swizzled, `NSObject.unswizzledSelector` will contain the original IMP) - @nonobjc private static func threeWaySwizzle(_ selector: Selector, with unswizzledSelector: Selector) { - let rootClass: AnyClass = NSObject.self - let bridgeClass: AnyClass = __KVOKeyPathBridgeMachinery.self - - // Swap bridge.selector <-> NSObject.unswizzledSelector - let unswizzledMethod = class_getClassMethod(rootClass, unswizzledSelector)! - let bridgeMethod = class_getClassMethod(bridgeClass, selector)! - method_exchangeImplementations(unswizzledMethod, bridgeMethod) - - // Swap NSObject.selector <-> NSObject.unswizzledSelector - // NSObject.unswizzledSelector at this point contains the bridge IMP - let rootMethod = class_getClassMethod(rootClass, selector)! - method_exchangeImplementations(rootMethod, unswizzledMethod) - } - - private class BridgeKey : NSObject, NSCopying { - let value: String - - init(_ value: String) { - self.value = value - } - - func copy(with zone: NSZone? = nil) -> Any { - return self - } - - override func isEqual(_ object: Any?) -> Bool { - return value == (object as? BridgeKey)?.value - } - - override var hash: Int { - var hasher = Hasher() - hasher.combine(ObjectIdentifier(BridgeKey.self)) - hasher.combine(value) - return hasher.finalize() - } - } - - /// Temporarily maps a `String` to an `AnyKeyPath` that can be retrieved with `_bridgeKeyPath(_:)`. - /// - /// This uses a per-thread storage so key paths on other threads don't interfere. - @nonobjc static func _withBridgeableKeyPath(from keyPathString: String, to keyPath: AnyKeyPath, block: () -> Void) { - _ = __KVOKeyPathBridgeMachinery.swizzler - let key = BridgeKey(keyPathString) - let oldValue = Thread.current.threadDictionary[key] - Thread.current.threadDictionary[key] = keyPath - defer { Thread.current.threadDictionary[key] = oldValue } - block() - } - - @nonobjc static func _bridgeKeyPath(_ keyPath:String?) -> AnyKeyPath? { - guard let keyPath = keyPath else { return nil } - return Thread.current.threadDictionary[BridgeKey(keyPath)] as? AnyKeyPath - } - - @objc override class func automaticallyNotifiesObservers(forKey key: String) -> Bool { - //This is swizzled so that it's -[NSObject automaticallyNotifiesObserversForKey:] - if let customizingSelf = self as? NSKeyValueObservingCustomization.Type, let path = __KVOKeyPathBridgeMachinery._bridgeKeyPath(key) { - return customizingSelf.automaticallyNotifiesObservers(for: path) - } else { - return self.__old_unswizzled_automaticallyNotifiesObservers(forKey: key) //swizzled to be NSObject's original implementation - } - } - - @objc override class func keyPathsForValuesAffectingValue(forKey key: String?) -> Set { - //This is swizzled so that it's -[NSObject keyPathsForValuesAffectingValueForKey:] - if let customizingSelf = self as? NSKeyValueObservingCustomization.Type, let path = __KVOKeyPathBridgeMachinery._bridgeKeyPath(key!) { - let resultSet = customizingSelf.keyPathsAffectingValue(for: path) - return Set(resultSet.lazy.map { - guard let str = $0._kvcKeyPathString else { fatalError("Could not extract a String from KeyPath \($0)") } - return str - }) - } else { - return self.__old_unswizzled_keyPathsForValuesAffectingValue(forKey: key) //swizzled to be NSObject's original implementation - } - } -} - -func _bridgeKeyPathToString(_ keyPath:AnyKeyPath) -> String { - guard let keyPathString = keyPath._kvcKeyPathString else { fatalError("Could not extract a String from KeyPath \(keyPath)") } - return keyPathString -} - -// NOTE: older overlays called this NSKeyValueObservation. We now use -// that name in the source code, but add an underscore to the runtime -// name to avoid conflicts when both are loaded into the same process. -@objc(_NSKeyValueObservation) -public class NSKeyValueObservation : NSObject { - // We use a private helper class as the actual observer. This lets us attach the helper as an associated object - // to the object we're observing, thus ensuring the helper will still be alive whenever a KVO change notification - // is broadcast, even on a background thread. - // - // For the associated object, we use the Helper instance itself as its own key. This guarantees key uniqueness. - private class Helper : NSObject { - @nonobjc weak var object : NSObject? - @nonobjc let path: String - @nonobjc let callback : (NSObject, NSKeyValueObservedChange) -> Void - - // workaround for Erroneous (?) error when using bridging in the Foundation overlay - // specifically, overriding observeValue(forKeyPath:of:change:context:) complains that it's not Obj-C-compatible - @nonobjc static let swizzler: () = { - let cls = NSClassFromString("_NSKVOCompatibility") as? _NSKVOCompatibilityShim.Type - cls?._noteProcessHasUsedKVOSwiftOverlay() - - let bridgeClass: AnyClass = Helper.self - let observeSel = #selector(NSObject.observeValue(forKeyPath:of:change:context:)) - let swapSel = #selector(Helper._swizzle_me_observeValue(forKeyPath:of:change:context:)) - let swapObserveMethod = class_getInstanceMethod(bridgeClass, swapSel)! - class_addMethod(bridgeClass, observeSel, method_getImplementation(swapObserveMethod), method_getTypeEncoding(swapObserveMethod)) - }() - - @nonobjc init(object: NSObject, keyPath: AnyKeyPath, options: NSKeyValueObservingOptions, callback: @escaping (NSObject, NSKeyValueObservedChange) -> Void) { - _ = Helper.swizzler - let path = _bridgeKeyPathToString(keyPath) - self.object = object - self.path = path - self.callback = callback - super.init() - objc_setAssociatedObject(object, associationKey(), self, .OBJC_ASSOCIATION_RETAIN) - __KVOKeyPathBridgeMachinery._withBridgeableKeyPath(from: path, to: keyPath) { - object.addObserver(self, forKeyPath: path, options: options, context: nil) - } - } - - @nonobjc func invalidate() { - guard let object = self.object else { return } - object.removeObserver(self, forKeyPath: path, context: nil) - objc_setAssociatedObject(object, associationKey(), nil, .OBJC_ASSOCIATION_ASSIGN) - self.object = nil - } - - @nonobjc private func associationKey() -> UnsafeRawPointer { - return UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()) - } - - @objc private func _swizzle_me_observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSString : Any]?, context: UnsafeMutableRawPointer?) { - guard let object = object as? NSObject, object === self.object, let change = change else { return } - let rawKind:UInt = change[NSKeyValueChangeKey.kindKey.rawValue as NSString] as! UInt - let kind = NSKeyValueChange(rawValue: rawKind)! - let notification = NSKeyValueObservedChange(kind: kind, - newValue: change[NSKeyValueChangeKey.newKey.rawValue as NSString], - oldValue: change[NSKeyValueChangeKey.oldKey.rawValue as NSString], - indexes: change[NSKeyValueChangeKey.indexesKey.rawValue as NSString] as! IndexSet?, - isPrior: change[NSKeyValueChangeKey.notificationIsPriorKey.rawValue as NSString] as? Bool ?? false) - callback(object, notification) - } - } - - @nonobjc private let helper: Helper - - fileprivate init(object: NSObject, keyPath: AnyKeyPath, options: NSKeyValueObservingOptions, callback: @escaping (NSObject, NSKeyValueObservedChange) -> Void) { - helper = Helper(object: object, keyPath: keyPath, options: options, callback: callback) - } - - ///invalidate() will be called automatically when an NSKeyValueObservation is deinited - @objc public func invalidate() { - helper.invalidate() - } - - deinit { - invalidate() - } -} - -// Used for type-erase Optional type -private protocol _OptionalForKVO { - static func _castForKVO(_ value: Any) -> Any? -} - -extension Optional: _OptionalForKVO { - static func _castForKVO(_ value: Any) -> Any? { - return value as? Wrapped - } -} - -extension _KeyValueCodingAndObserving { - - ///when the returned NSKeyValueObservation is deinited or invalidated, it will stop observing - public func observe( - _ keyPath: KeyPath, - options: NSKeyValueObservingOptions = [], - changeHandler: @escaping (Self, NSKeyValueObservedChange) -> Void) - -> NSKeyValueObservation { - return NSKeyValueObservation(object: self as! NSObject, keyPath: keyPath, options: options) { (obj, change) in - - let converter = { (changeValue: Any?) -> Value? in - if let optionalType = Value.self as? _OptionalForKVO.Type { - // Special logic for keyPath having a optional target value. When the keyPath referencing a nil value, the newValue/oldValue should be in the form .some(nil) instead of .none - // Solve https://bugs.swift.org/browse/SR-6066 - - // NSNull is used by KVO to signal that the keyPath value is nil. - // If Value == Optional.self, We will get nil instead of .some(nil) when casting Optional() directly. - // To fix this behavior, we will eliminate NSNull first, then cast the transformed value. - - if let unwrapped = changeValue { - // We use _castForKVO to cast first. - // If Value != Optional.self, the NSNull value will be eliminated. - let nullEliminatedValue = optionalType._castForKVO(unwrapped) as Any - let transformedOptional: Any? = nullEliminatedValue - return transformedOptional as? Value - } - } - return changeValue as? Value - } - - let notification = NSKeyValueObservedChange(kind: change.kind, - newValue: converter(change.newValue), - oldValue: converter(change.oldValue), - indexes: change.indexes, - isPrior: change.isPrior) - changeHandler(obj as! Self, notification) - } - } - - public func willChangeValue(for keyPath: __owned KeyPath) { - (self as! NSObject).willChangeValue(forKey: _bridgeKeyPathToString(keyPath)) - } - - public func willChange(_ changeKind: NSKeyValueChange, valuesAt indexes: IndexSet, for keyPath: __owned KeyPath) { - (self as! NSObject).willChange(changeKind, valuesAt: indexes, forKey: _bridgeKeyPathToString(keyPath)) - } - - public func willChangeValue(for keyPath: __owned KeyPath, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set) -> Void { - (self as! NSObject).willChangeValue(forKey: _bridgeKeyPathToString(keyPath), withSetMutation: mutation, using: set) - } - - public func didChangeValue(for keyPath: __owned KeyPath) { - (self as! NSObject).didChangeValue(forKey: _bridgeKeyPathToString(keyPath)) - } - - public func didChange(_ changeKind: NSKeyValueChange, valuesAt indexes: IndexSet, for keyPath: __owned KeyPath) { - (self as! NSObject).didChange(changeKind, valuesAt: indexes, forKey: _bridgeKeyPathToString(keyPath)) - } - - public func didChangeValue(for keyPath: __owned KeyPath, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set) -> Void { - (self as! NSObject).didChangeValue(forKey: _bridgeKeyPathToString(keyPath), withSetMutation: mutation, using: set) - } -} diff --git a/stdlib/public/Darwin/Foundation/NSOrderedCollectionDifference.swift b/stdlib/public/Darwin/Foundation/NSOrderedCollectionDifference.swift deleted file mode 100644 index 83134e63d5714..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSOrderedCollectionDifference.swift +++ /dev/null @@ -1,106 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -// CollectionDifference.Change is conditionally bridged to NSOrderedCollectionChange -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -extension CollectionDifference.Change : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSOrderedCollectionChange { - switch self { - case .insert(offset: let o, element: let e, associatedWith: let a): - return NSOrderedCollectionChange(object: e, type: .insert, index: o, associatedIndex: a ?? NSNotFound) - case .remove(offset: let o, element: let e, associatedWith: let a): - return NSOrderedCollectionChange(object: e, type: .remove, index: o, associatedIndex: a ?? NSNotFound) - } - } - - public static func _forceBridgeFromObjectiveC(_ input: NSOrderedCollectionChange, result: inout CollectionDifference.Change?) { - let _ = input.object as! ChangeElement - - if !_conditionallyBridgeFromObjectiveC(input, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC( - _ x: NSOrderedCollectionChange, result: inout CollectionDifference.Change? - ) -> Bool { - guard let element = x.object as? ChangeElement else { return false } - - let a: Int? - if x.associatedIndex == NSNotFound { - a = nil - } else { - a = x.associatedIndex - } - - switch x.changeType { - case .insert: - result = .insert(offset: x.index, element: element, associatedWith: a) - case .remove: - result = .remove(offset: x.index, element: element, associatedWith: a) - default: - return false - } - - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ s: NSOrderedCollectionChange?) -> CollectionDifference.Change { - var result: CollectionDifference.Change? = nil - CollectionDifference.Change._forceBridgeFromObjectiveC(s!, result: &result) - return result! - } -} - -// CollectionDifference is conditionally bridged to NSOrderedCollectionDifference -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -extension CollectionDifference : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSOrderedCollectionDifference { - return NSOrderedCollectionDifference(changes: self.map({ $0 as NSOrderedCollectionChange })) - } - - public static func _forceBridgeFromObjectiveC(_ input: NSOrderedCollectionDifference, result: inout CollectionDifference?) { - if !_conditionallyBridgeFromObjectiveC(input, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - private static func _formDifference( - from input: NSOrderedCollectionDifference, - _ changeConverter: (Any) -> CollectionDifference.Change? - ) -> CollectionDifference? { - var changes = Array() - let iteratorSeq = IteratorSequence(NSFastEnumerationIterator(input)) - for objc_change in iteratorSeq { - guard let swift_change = changeConverter(objc_change) else { return nil } - changes.append(swift_change) - } - return CollectionDifference(changes) - } - - public static func _conditionallyBridgeFromObjectiveC( - _ input: NSOrderedCollectionDifference, result: inout CollectionDifference? - ) -> Bool { - result = _formDifference(from: input) { $0 as? Change } - return result != nil - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ s: NSOrderedCollectionDifference?) -> CollectionDifference { - return _formDifference(from: s!) { ($0 as! Change) }! - } -} diff --git a/stdlib/public/Darwin/Foundation/NSRange.swift b/stdlib/public/Darwin/Foundation/NSRange.swift deleted file mode 100644 index 0a8ed667a301c..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSRange.swift +++ /dev/null @@ -1,249 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2018 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -extension NSRange : Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(location) - hasher.combine(length) - } - - public static func==(lhs: NSRange, rhs: NSRange) -> Bool { - return lhs.location == rhs.location && lhs.length == rhs.length - } -} - -extension NSRange : CustomStringConvertible, CustomDebugStringConvertible { - public var description: String { return "{\(location), \(length)}" } - public var debugDescription: String { - guard location != NSNotFound else { - return "{NSNotFound, \(length)}" - } - return "{\(location), \(length)}" - } -} - -extension NSRange { - public init?(_ string: __shared String) { - var savedLocation = 0 - if string.isEmpty { - // fail early if the string is empty - return nil - } - let scanner = Scanner(string: string) - let digitSet = CharacterSet.decimalDigits - let _ = scanner.scanUpToCharacters(from: digitSet, into: nil) - if scanner.isAtEnd { - // fail early if there are no decimal digits - return nil - } - var location = 0 - savedLocation = scanner.scanLocation - guard scanner.scanInt(&location) else { - return nil - } - if scanner.isAtEnd { - // return early if there are no more characters after the first int in the string - return nil - } - if scanner.scanString(".", into: nil) { - scanner.scanLocation = savedLocation - var double = 0.0 - guard scanner.scanDouble(&double) else { - return nil - } - guard let integral = Int(exactly: double) else { - return nil - } - location = integral - } - - let _ = scanner.scanUpToCharacters(from: digitSet, into: nil) - if scanner.isAtEnd { - // return early if there are no integer characters after the first int in the string - return nil - } - var length = 0 - savedLocation = scanner.scanLocation - guard scanner.scanInt(&length) else { - return nil - } - - if !scanner.isAtEnd { - if scanner.scanString(".", into: nil) { - scanner.scanLocation = savedLocation - var double = 0.0 - guard scanner.scanDouble(&double) else { - return nil - } - guard let integral = Int(exactly: double) else { - return nil - } - length = integral - } - } - - - self.init(location: location, length: length) - } -} - -extension NSRange { - public var lowerBound: Int { return location } - - public var upperBound: Int { return location + length } - - public func contains(_ index: Int) -> Bool { return (!(index < location) && (index - location) < length) } - - public mutating func formUnion(_ other: NSRange) { - self = union(other) - } - - public func union(_ other: NSRange) -> NSRange { - let max1 = location + length - let max2 = other.location + other.length - let maxend = (max1 < max2) ? max2 : max1 - let minloc = location < other.location ? location : other.location - return NSRange(location: minloc, length: maxend - minloc) - } - - public func intersection(_ other: NSRange) -> NSRange? { - let max1 = location + length - let max2 = other.location + other.length - let minend = (max1 < max2) ? max1 : max2 - if other.location <= location && location < max2 { - return NSRange(location: location, length: minend - location) - } else if location <= other.location && other.location < max1 { - return NSRange(location: other.location, length: minend - other.location); - } - return nil - } -} - - -//===----------------------------------------------------------------------===// -// Ranges -//===----------------------------------------------------------------------===// - -extension NSRange { - public init(_ region: R) - where R.Bound: FixedWidthInteger { - let r = region.relative(to: 0..(_ region: R, in target: S) - where R.Bound == S.Index { - let r = region.relative(to: target) - self.init(target._toUTF16Offsets(r)) - } - - @available(swift, deprecated: 4, renamed: "Range.init(_:)") - public func toRange() -> Range? { - if location == NSNotFound { return nil } - return location..<(location+length) - } -} - -extension Range where Bound: BinaryInteger { - public init?(_ range: NSRange) { - guard range.location != NSNotFound else { return nil } - self.init(uncheckedBounds: (numericCast(range.lowerBound), numericCast(range.upperBound))) - } -} - -// This additional overload will mean Range.init(_:) defaults to Range when -// no additional type context is provided: -extension Range where Bound == Int { - public init?(_ range: NSRange) { - guard range.location != NSNotFound else { return nil } - self.init(uncheckedBounds: (range.lowerBound, range.upperBound)) - } -} - -extension Range where Bound == String.Index { - private init?( - _ range: NSRange, _genericIn string: __shared S - ) { - // Corresponding stdlib version - guard #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) else { - fatalError() - } - let u = string.utf16 - guard range.location != NSNotFound, - let start = u.index( - u.startIndex, offsetBy: range.location, limitedBy: u.endIndex), - let end = u.index( - start, offsetBy: range.length, limitedBy: u.endIndex), - let lowerBound = String.Index(start, within: string), - let upperBound = String.Index(end, within: string) - else { return nil } - - self = lowerBound..(_ range: NSRange, in string: __shared S) { - self.init(range, _genericIn: string) - } -} - -extension NSRange : CustomReflectable { - public var customMirror: Mirror { - return Mirror(self, children: ["location": location, "length": length]) - } -} - -extension NSRange : _CustomPlaygroundQuickLookable { - @available(*, deprecated, message: "NSRange.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - return .range(Int64(location), Int64(length)) - } -} - -extension NSRange : Codable { - public init(from decoder: Decoder) throws { - var container = try decoder.unkeyedContainer() - let location = try container.decode(Int.self) - let length = try container.decode(Int.self) - self.init(location: location, length: length) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - try container.encode(self.location) - try container.encode(self.length) - } -} diff --git a/stdlib/public/Darwin/Foundation/NSSet.swift b/stdlib/public/Darwin/Foundation/NSSet.swift deleted file mode 100644 index cffc2c7a534f3..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSSet.swift +++ /dev/null @@ -1,195 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -extension Set { - /// Private initializer used for bridging. - /// - /// The provided `NSSet` will be copied to ensure that the copy can - /// not be mutated by other code. - private init(_cocoaSet: __shared NSSet) { - assert(_isBridgedVerbatimToObjectiveC(Element.self), - "Set can be backed by NSSet _variantStorage only when the member type can be bridged verbatim to Objective-C") - // FIXME: We would like to call CFSetCreateCopy() to avoid doing an - // objc_msgSend() for instances of CoreFoundation types. We can't do that - // today because CFSetCreateCopy() copies dictionary contents - // unconditionally, resulting in O(n) copies even for immutable dictionaries. - // - // CFSetCreateCopy() does not call copyWithZone: - // - // The bug is fixed in: OS X 10.11.0, iOS 9.0, all versions of tvOS - // and watchOS. - self = Set(_immutableCocoaSet: _cocoaSet.copy(with: nil) as AnyObject) - } -} - -extension NSSet : Sequence { - /// Return an *iterator* over the elements of this *sequence*. - /// - /// - Complexity: O(1). - public func makeIterator() -> NSFastEnumerationIterator { - return NSFastEnumerationIterator(self) - } -} - -extension NSOrderedSet : Sequence { - /// Return an *iterator* over the elements of this *sequence*. - /// - /// - Complexity: O(1). - public func makeIterator() -> NSFastEnumerationIterator { - return NSFastEnumerationIterator(self) - } -} - -// Set is conditionally bridged to NSSet -extension Set : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSSet { - return unsafeBitCast(_bridgeToObjectiveCImpl(), to: NSSet.self) - } - - public static func _forceBridgeFromObjectiveC(_ s: NSSet, result: inout Set?) { - if let native = - Set._bridgeFromObjectiveCAdoptingNativeStorageOf(s as AnyObject) { - - result = native - return - } - - if _isBridgedVerbatimToObjectiveC(Element.self) { - result = Set(_cocoaSet: s) - return - } - - if Element.self == String.self { - // String and NSString have different concepts of equality, so - // string-keyed NSSets may generate key collisions when bridged over to - // Swift. See rdar://problem/35995647 - var set = Set(minimumCapacity: s.count) - s.enumerateObjects({ (anyMember: Any, _) in - // FIXME: Log a warning if `member` is already in the set. - set.insert(anyMember as! Element) - }) - result = set - return - } - - // `Set` where `Element` is a value type may not be backed by - // an NSSet. - var builder = _SetBuilder(count: s.count) - s.enumerateObjects({ (anyMember: Any, _) in - builder.add(member: anyMember as! Element) - }) - result = builder.take() - } - - public static func _conditionallyBridgeFromObjectiveC( - _ x: NSSet, result: inout Set? - ) -> Bool { - let anySet = x as Set - if _isBridgedVerbatimToObjectiveC(Element.self) { - result = Swift._setDownCastConditional(anySet) - return result != nil - } - - result = anySet as? Set - return result != nil - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ s: NSSet?) -> Set { - // `nil` has historically been used as a stand-in for an empty - // set; map it to an empty set. - if _slowPath(s == nil) { return Set() } - - var result: Set? = nil - Set._forceBridgeFromObjectiveC(s!, result: &result) - return result! - } -} - -extension NSSet : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as! Set) - } -} - -extension NSOrderedSet { - // - (instancetype)initWithObjects:(id)firstObj, ... - public convenience init(objects elements: Any...) { - self.init(array: elements) - } -} - -extension NSSet { - // - (instancetype)initWithObjects:(id)firstObj, ... - public convenience init(objects elements: Any...) { - self.init(array: elements) - } -} - -extension NSSet : ExpressibleByArrayLiteral { - public required convenience init(arrayLiteral elements: Any...) { - self.init(array: elements) - } -} - -extension NSOrderedSet : ExpressibleByArrayLiteral { - public required convenience init(arrayLiteral elements: Any...) { - self.init(array: elements) - } -} - -extension NSSet { - /// Initializes a newly allocated set and adds to it objects from - /// another given set. - /// - /// - Returns: An initialized objects set containing the objects from - /// `set`. The returned set might be different than the original - /// receiver. - @nonobjc - public convenience init(set anSet: __shared NSSet) { - // FIXME(performance)(compiler limitation): we actually want to do just - // `self = anSet.copy()`, but Swift does not have factory - // initializers right now. - let numElems = anSet.count - let stride = MemoryLayout>.stride - let alignment = MemoryLayout>.alignment - let bufferSize = stride * numElems - assert(stride == MemoryLayout.stride) - assert(alignment == MemoryLayout.alignment) - - let rawBuffer = UnsafeMutableRawPointer.allocate( - byteCount: bufferSize, alignment: alignment) - defer { - rawBuffer.deallocate() - _fixLifetime(anSet) - } - let valueBuffer = rawBuffer.bindMemory( - to: Optional.self, capacity: numElems) - - CFSetGetValues(anSet, valueBuffer) - let valueBufferForInit = rawBuffer.assumingMemoryBound(to: AnyObject.self) - self.init(objects: valueBufferForInit, count: numElems) - } -} - -extension NSSet : CustomReflectable { - public var customMirror: Mirror { - return Mirror(reflecting: self as Set) - } -} - -extension Set: CVarArg {} diff --git a/stdlib/public/Darwin/Foundation/NSSortDescriptor.swift b/stdlib/public/Darwin/Foundation/NSSortDescriptor.swift deleted file mode 100644 index c3c35102fc8c0..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSSortDescriptor.swift +++ /dev/null @@ -1,32 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2017 - 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import ObjectiveC - -extension NSSortDescriptor { - public convenience init(keyPath: KeyPath, ascending: Bool) { - self.init(key: _bridgeKeyPathToString(keyPath), ascending: ascending) - objc_setAssociatedObject(self, UnsafeRawPointer(&associationKey), keyPath, .OBJC_ASSOCIATION_RETAIN) - } - - public convenience init(keyPath: KeyPath, ascending: Bool, comparator cmptr: @escaping Foundation.Comparator) { - self.init(key: _bridgeKeyPathToString(keyPath), ascending: ascending, comparator: cmptr) - objc_setAssociatedObject(self, UnsafeRawPointer(&associationKey), keyPath, .OBJC_ASSOCIATION_RETAIN) - } - - public var keyPath: AnyKeyPath? { - return objc_getAssociatedObject(self, UnsafeRawPointer(&associationKey)) as? AnyKeyPath - } -} - -private var associationKey: ()? diff --git a/stdlib/public/Darwin/Foundation/NSString.swift b/stdlib/public/Darwin/Foundation/NSString.swift deleted file mode 100644 index a97deca4171b7..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSString.swift +++ /dev/null @@ -1,109 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -//===----------------------------------------------------------------------===// -// Strings -//===----------------------------------------------------------------------===// - -extension NSString : ExpressibleByStringLiteral { - /// Create an instance initialized to `value`. - public required convenience init(stringLiteral value: StaticString) { - var immutableResult: NSString - if value.hasPointerRepresentation { - immutableResult = NSString( - bytesNoCopy: UnsafeMutableRawPointer(mutating: value.utf8Start), - length: Int(value.utf8CodeUnitCount), - encoding: value.isASCII ? String.Encoding.ascii.rawValue : String.Encoding.utf8.rawValue, - freeWhenDone: false)! - } else { - var uintValue = value.unicodeScalar - immutableResult = NSString( - bytes: &uintValue, - length: 4, - encoding: String.Encoding.utf32.rawValue)! - } - self.init(string: immutableResult as String) - } -} - -extension NSString : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to prevent infinite recursion trying to bridge - // AnyHashable to NSObject. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - // Consistently use Swift equality and hashing semantics for all strings. - return AnyHashable(self as String) - } -} - -extension NSString { - public convenience init(format: __shared NSString, _ args: CVarArg...) { - // We can't use withVaList because 'self' cannot be captured by a closure - // before it has been initialized. - let va_args = getVaList(args) - self.init(format: format as String, arguments: va_args) - } - - public convenience init( - format: __shared NSString, locale: Locale?, _ args: CVarArg... - ) { - // We can't use withVaList because 'self' cannot be captured by a closure - // before it has been initialized. - let va_args = getVaList(args) - self.init(format: format as String, locale: locale, arguments: va_args) - } - - public class func localizedStringWithFormat( - _ format: NSString, _ args: CVarArg... - ) -> Self { - return withVaList(args) { - self.init(format: format as String, locale: Locale.current, arguments: $0) - } - } - - public func appendingFormat(_ format: NSString, _ args: CVarArg...) - -> NSString { - return withVaList(args) { - self.appending(NSString(format: format as String, arguments: $0) as String) as NSString - } - } -} - -extension NSMutableString { - public func appendFormat(_ format: NSString, _ args: CVarArg...) { - return withVaList(args) { - self.append(NSString(format: format as String, arguments: $0) as String) - } - } -} - -extension NSString { - /// Returns an `NSString` object initialized by copying the characters - /// from another given string. - /// - /// - Returns: An `NSString` object initialized by copying the - /// characters from `aString`. The returned object may be different - /// from the original receiver. - @nonobjc - public convenience init(string aString: __shared NSString) { - self.init(string: aString as String) - } -} - -extension NSString : _CustomPlaygroundQuickLookable { - @available(*, deprecated, message: "NSString.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - return .text(self as String) - } -} diff --git a/stdlib/public/Darwin/Foundation/NSStringAPI.swift b/stdlib/public/Darwin/Foundation/NSStringAPI.swift deleted file mode 100644 index ece80245f2850..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSStringAPI.swift +++ /dev/null @@ -1,2206 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2018 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 -// -//===----------------------------------------------------------------------===// -// -// Exposing the API of NSString on Swift's String -// -//===----------------------------------------------------------------------===// - -// Important Note -// ============== -// -// This file is shared between two projects: -// -// 1. https://github.com/apple/swift/tree/main/stdlib/public/Darwin/Foundation -// 2. https://github.com/apple/swift-corelibs-foundation/tree/main/Foundation -// -// If you change this file, you must update it in both places. - -#if !DEPLOYMENT_RUNTIME_SWIFT -@_exported import Foundation // Clang module -#endif - -// Open Issues -// =========== -// -// Property Lists need to be properly bridged -// - -func _toNSArray(_ a: [T], f: (T) -> U) -> NSArray { - let result = NSMutableArray(capacity: a.count) - for s in a { - result.add(f(s)) - } - return result -} - -#if !DEPLOYMENT_RUNTIME_SWIFT -// We only need this for UnsafeMutablePointer, but there's not currently a way -// to write that constraint. -extension Optional { - /// Invokes `body` with `nil` if `self` is `nil`; otherwise, passes the - /// address of `object` to `body`. - /// - /// This is intended for use with Foundation APIs that return an Objective-C - /// type via out-parameter where it is important to be able to *ignore* that - /// parameter by passing `nil`. (For some APIs, this may allow the - /// implementation to avoid some work.) - /// - /// In most cases it would be simpler to just write this code inline, but if - /// `body` is complicated than that results in unnecessarily repeated code. - internal func _withNilOrAddress( - of object: inout NSType?, - _ body: - (AutoreleasingUnsafeMutablePointer?) -> ResultType - ) -> ResultType { - return self == nil ? body(nil) : body(&object) - } -} -#endif - -/// From a non-`nil` `UnsafePointer` to a null-terminated string -/// with possibly-transient lifetime, create a null-terminated array of 'C' char. -/// Returns `nil` if passed a null pointer. -internal func _persistCString(_ p: UnsafePointer?) -> [CChar]? { - guard let cString = p else { - return nil - } - let bytesToCopy = UTF8._nullCodeUnitOffset(in: cString) + 1 // +1 for the terminating NUL - let result = [CChar](unsafeUninitializedCapacity: bytesToCopy) { buffer, initializedCount in - buffer.baseAddress!.initialize(from: cString, count: bytesToCopy) - initializedCount = bytesToCopy - } - return result -} - -extension String { - //===--- Class Methods --------------------------------------------------===// - //===--------------------------------------------------------------------===// - - // @property (class) const NSStringEncoding *availableStringEncodings; - - /// An array of the encodings that strings support in the application's - /// environment. - public static var availableStringEncodings: [Encoding] { - var result = [Encoding]() - var p = NSString.availableStringEncodings - while p.pointee != 0 { - result.append(Encoding(rawValue: p.pointee)) - p += 1 - } - return result - } - - // @property (class) NSStringEncoding defaultCStringEncoding; - - /// The C-string encoding assumed for any method accepting a C string as an - /// argument. - public static var defaultCStringEncoding: Encoding { - return Encoding(rawValue: NSString.defaultCStringEncoding) - } - - // + (NSString *)localizedNameOfStringEncoding:(NSStringEncoding)encoding - - /// Returns a human-readable string giving the name of the specified encoding. - /// - /// - Parameter encoding: A string encoding. For possible values, see - /// `String.Encoding`. - /// - Returns: A human-readable string giving the name of `encoding` in the - /// current locale. - public static func localizedName( - of encoding: Encoding - ) -> String { - return NSString.localizedName(of: encoding.rawValue) - } - - // + (instancetype)localizedStringWithFormat:(NSString *)format, ... - - /// Returns a string created by using a given format string as a - /// template into which the remaining argument values are substituted - /// according to the user's default locale. - public static func localizedStringWithFormat( - _ format: String, _ arguments: CVarArg... - ) -> String { - return String(format: format, locale: Locale.current, - arguments: arguments) - } - - //===--------------------------------------------------------------------===// - // NSString factory functions that have a corresponding constructor - // are omitted. - // - // + (instancetype)string - // - // + (instancetype) - // stringWithCharacters:(const unichar *)chars length:(NSUInteger)length - // - // + (instancetype)stringWithFormat:(NSString *)format, ... - // - // + (instancetype) - // stringWithContentsOfFile:(NSString *)path - // encoding:(NSStringEncoding)enc - // error:(NSError **)error - // - // + (instancetype) - // stringWithContentsOfFile:(NSString *)path - // usedEncoding:(NSStringEncoding *)enc - // error:(NSError **)error - // - // + (instancetype) - // stringWithContentsOfURL:(NSURL *)url - // encoding:(NSStringEncoding)enc - // error:(NSError **)error - // - // + (instancetype) - // stringWithContentsOfURL:(NSURL *)url - // usedEncoding:(NSStringEncoding *)enc - // error:(NSError **)error - // - // + (instancetype) - // stringWithCString:(const char *)cString - // encoding:(NSStringEncoding)enc - //===--------------------------------------------------------------------===// - - //===--- Adds nothing for String beyond what String(s) does -------------===// - // + (instancetype)stringWithString:(NSString *)aString - //===--------------------------------------------------------------------===// - - // + (instancetype)stringWithUTF8String:(const char *)bytes - - /// Creates a string by copying the data from a given - /// C array of UTF8-encoded bytes. - public init?(utf8String bytes: UnsafePointer) { - if let str = String(validatingUTF8: bytes) { - self = str - return - } - if let ns = NSString(utf8String: bytes) { - self = String._unconditionallyBridgeFromObjectiveC(ns) - } else { - return nil - } - } -} - -extension String { - //===--- Already provided by String's core ------------------------------===// - // - (instancetype)init - - //===--- Initializers that can fail -------------------------------------===// - // - (instancetype) - // initWithBytes:(const void *)bytes - // length:(NSUInteger)length - // encoding:(NSStringEncoding)encoding - - /// Creates a new string equivalent to the given bytes interpreted in the - /// specified encoding. - /// - /// - Parameters: - /// - bytes: A sequence of bytes to interpret using `encoding`. - /// - encoding: The encoding to use to interpret `bytes`. - public init?(bytes: __shared S, encoding: Encoding) - where S.Iterator.Element == UInt8 { - let byteArray = Array(bytes) - if encoding == .utf8, - let str = byteArray.withUnsafeBufferPointer({ String._tryFromUTF8($0) }) - { - self = str - return - } - - if let ns = NSString( - bytes: byteArray, length: byteArray.count, encoding: encoding.rawValue) { - self = String._unconditionallyBridgeFromObjectiveC(ns) - } else { - return nil - } - } - - // - (instancetype) - // initWithBytesNoCopy:(void *)bytes - // length:(NSUInteger)length - // encoding:(NSStringEncoding)encoding - // freeWhenDone:(BOOL)flag - - /// Creates a new string that contains the specified number of bytes from the - /// given buffer, interpreted in the specified encoding, and optionally - /// frees the buffer. - /// - /// - Warning: This initializer is not memory-safe! - public init?( - bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, - encoding: Encoding, freeWhenDone flag: Bool - ) { - if let ns = NSString( - bytesNoCopy: bytes, length: length, encoding: encoding.rawValue, - freeWhenDone: flag) { - - self = String._unconditionallyBridgeFromObjectiveC(ns) - } else { - return nil - } - } - - - // - (instancetype) - // initWithCharacters:(const unichar *)characters - // length:(NSUInteger)length - - /// Creates a new string that contains the specified number of characters - /// from the given C array of Unicode characters. - public init( - utf16CodeUnits: UnsafePointer, - count: Int - ) { - self = String._unconditionallyBridgeFromObjectiveC(NSString(characters: utf16CodeUnits, length: count)) - } - - // - (instancetype) - // initWithCharactersNoCopy:(unichar *)characters - // length:(NSUInteger)length - // freeWhenDone:(BOOL)flag - - /// Creates a new string that contains the specified number of characters - /// from the given C array of UTF-16 code units. - public init( - utf16CodeUnitsNoCopy: UnsafePointer, - count: Int, - freeWhenDone flag: Bool - ) { - self = String._unconditionallyBridgeFromObjectiveC(NSString( - charactersNoCopy: UnsafeMutablePointer(mutating: utf16CodeUnitsNoCopy), - length: count, - freeWhenDone: flag)) - } - - //===--- Initializers that can fail -------------------------------------===// - - // - (instancetype) - // initWithContentsOfFile:(NSString *)path - // encoding:(NSStringEncoding)enc - // error:(NSError **)error - // - - /// Produces a string created by reading data from the file at a - /// given path interpreted using a given encoding. - public init( - contentsOfFile path: __shared String, - encoding enc: Encoding - ) throws { - let ns = try NSString(contentsOfFile: path, encoding: enc.rawValue) - self = String._unconditionallyBridgeFromObjectiveC(ns) - } - - // - (instancetype) - // initWithContentsOfFile:(NSString *)path - // usedEncoding:(NSStringEncoding *)enc - // error:(NSError **)error - - /// Produces a string created by reading data from the file at - /// a given path and returns by reference the encoding used to - /// interpret the file. - public init( - contentsOfFile path: __shared String, - usedEncoding: inout Encoding - ) throws { - var enc: UInt = 0 - let ns = try NSString(contentsOfFile: path, usedEncoding: &enc) - usedEncoding = Encoding(rawValue: enc) - self = String._unconditionallyBridgeFromObjectiveC(ns) - } - - public init( - contentsOfFile path: __shared String - ) throws { - let ns = try NSString(contentsOfFile: path, usedEncoding: nil) - self = String._unconditionallyBridgeFromObjectiveC(ns) - } - - // - (instancetype) - // initWithContentsOfURL:(NSURL *)url - // encoding:(NSStringEncoding)enc - // error:(NSError**)error - - /// Produces a string created by reading data from a given URL - /// interpreted using a given encoding. Errors are written into the - /// inout `error` argument. - public init( - contentsOf url: __shared URL, - encoding enc: Encoding - ) throws { - let ns = try NSString(contentsOf: url, encoding: enc.rawValue) - self = String._unconditionallyBridgeFromObjectiveC(ns) - } - - // - (instancetype) - // initWithContentsOfURL:(NSURL *)url - // usedEncoding:(NSStringEncoding *)enc - // error:(NSError **)error - - /// Produces a string created by reading data from a given URL - /// and returns by reference the encoding used to interpret the - /// data. Errors are written into the inout `error` argument. - public init( - contentsOf url: __shared URL, - usedEncoding: inout Encoding - ) throws { - var enc: UInt = 0 - let ns = try NSString(contentsOf: url, usedEncoding: &enc) - usedEncoding = Encoding(rawValue: enc) - self = String._unconditionallyBridgeFromObjectiveC(ns) - } - - public init( - contentsOf url: __shared URL - ) throws { - let ns = try NSString(contentsOf: url, usedEncoding: nil) - self = String._unconditionallyBridgeFromObjectiveC(ns) - } - - // - (instancetype) - // initWithCString:(const char *)nullTerminatedCString - // encoding:(NSStringEncoding)encoding - - /// Produces a string containing the bytes in a given C array, - /// interpreted according to a given encoding. - public init?( - cString: UnsafePointer, - encoding enc: Encoding - ) { - if enc == .utf8, let str = String(validatingUTF8: cString) { - self = str - return - } - if let ns = NSString(cString: cString, encoding: enc.rawValue) { - self = String._unconditionallyBridgeFromObjectiveC(ns) - } else { - return nil - } - } - - // FIXME: handle optional locale with default arguments - - // - (instancetype) - // initWithData:(NSData *)data - // encoding:(NSStringEncoding)encoding - - /// Returns a `String` initialized by converting given `data` into - /// Unicode characters using a given `encoding`. - public init?(data: __shared Data, encoding: Encoding) { - if encoding == .utf8, - let str = data.withUnsafeBytes({ - String._tryFromUTF8($0.bindMemory(to: UInt8.self)) - }) { - self = str - return - } - - guard let s = NSString(data: data, encoding: encoding.rawValue) else { return nil } - self = String._unconditionallyBridgeFromObjectiveC(s) - } - - // - (instancetype)initWithFormat:(NSString *)format, ... - - /// Returns a `String` object initialized by using a given - /// format string as a template into which the remaining argument - /// values are substituted. - public init(format: __shared String, _ arguments: CVarArg...) { - self = String(format: format, arguments: arguments) - } - - // - (instancetype) - // initWithFormat:(NSString *)format - // arguments:(va_list)argList - - /// Returns a `String` object initialized by using a given - /// format string as a template into which the remaining argument - /// values are substituted according to the user's default locale. - public init(format: __shared String, arguments: __shared [CVarArg]) { - self = String(format: format, locale: nil, arguments: arguments) - } - - // - (instancetype)initWithFormat:(NSString *)format locale:(id)locale, ... - - /// Returns a `String` object initialized by using a given - /// format string as a template into which the remaining argument - /// values are substituted according to given locale information. - public init(format: __shared String, locale: __shared Locale?, _ args: CVarArg...) { - self = String(format: format, locale: locale, arguments: args) - } - - // - (instancetype) - // initWithFormat:(NSString *)format - // locale:(id)locale - // arguments:(va_list)argList - - /// Returns a `String` object initialized by using a given - /// format string as a template into which the remaining argument - /// values are substituted according to given locale information. - public init(format: __shared String, locale: __shared Locale?, arguments: __shared [CVarArg]) { -#if DEPLOYMENT_RUNTIME_SWIFT - self = withVaList(arguments) { - String._unconditionallyBridgeFromObjectiveC( - NSString(format: format, locale: locale?._bridgeToObjectiveC(), arguments: $0) - ) - } -#else - self = withVaList(arguments) { - NSString(format: format, locale: locale, arguments: $0) as String - } -#endif - } - -} - -extension StringProtocol where Index == String.Index { - //===--- Bridging Helpers -----------------------------------------------===// - //===--------------------------------------------------------------------===// - - /// The corresponding `NSString` - a convenience for bridging code. - // FIXME(strings): There is probably a better way to bridge Self to NSString - var _ns: NSString { - return self._ephemeralString._bridgeToObjectiveC() - } - - /// Return an `Index` corresponding to the given offset in our UTF-16 - /// representation. - func _toIndex(_ utf16Index: Int) -> Index { - return self._toUTF16Index(utf16Index) - } - - /// Return the UTF-16 code unit offset corresponding to an Index - func _toOffset(_ idx: String.Index) -> Int { - return self._toUTF16Offset(idx) - } - - @inlinable - internal func _toRelativeNSRange(_ r: Range) -> NSRange { - return NSRange(self._toUTF16Offsets(r)) - } - - /// Return a `Range` corresponding to the given `NSRange` of - /// our UTF-16 representation. - func _toRange(_ r: NSRange) -> Range { - return self._toUTF16Indices(Range(r)!) - } - - /// Return a `Range?` corresponding to the given `NSRange` of - /// our UTF-16 representation. - func _optionalRange(_ r: NSRange) -> Range? { - if r.location == NSNotFound { - return nil - } - return _toRange(r) - } - - /// Invoke `body` on an `Int` buffer. If `index` was converted from - /// non-`nil`, convert the buffer to an `Index` and write it into the - /// memory referred to by `index` - func _withOptionalOutParameter( - _ index: UnsafeMutablePointer?, - _ body: (UnsafeMutablePointer?) -> Result - ) -> Result { - var utf16Index: Int = 0 - let result = (index != nil ? body(&utf16Index) : body(nil)) - index?.pointee = _toIndex(utf16Index) - return result - } - - /// Invoke `body` on an `NSRange` buffer. If `range` was converted - /// from non-`nil`, convert the buffer to a `Range` and write - /// it into the memory referred to by `range` - func _withOptionalOutParameter( - _ range: UnsafeMutablePointer>?, - _ body: (UnsafeMutablePointer?) -> Result - ) -> Result { - var nsRange = NSRange(location: 0, length: 0) - let result = (range != nil ? body(&nsRange) : body(nil)) - range?.pointee = self._toRange(nsRange) - return result - } - - //===--- Instance Methods/Properties-------------------------------------===// - //===--------------------------------------------------------------------===// - - //===--- Omitted by agreement during API review 5/20/2014 ---------------===// - // @property BOOL boolValue; - - // - (BOOL)canBeConvertedToEncoding:(NSStringEncoding)encoding - - /// Returns a Boolean value that indicates whether the string can be - /// converted to the specified encoding without loss of information. - /// - /// - Parameter encoding: A string encoding. - /// - Returns: `true` if the string can be encoded in `encoding` without loss - /// of information; otherwise, `false`. - public func canBeConverted(to encoding: String.Encoding) -> Bool { - return _ns.canBeConverted(to: encoding.rawValue) - } - - // @property NSString* capitalizedString - - /// A copy of the string with each word changed to its corresponding - /// capitalized spelling. - /// - /// This property performs the canonical (non-localized) mapping. It is - /// suitable for programming operations that require stable results not - /// depending on the current locale. - /// - /// A capitalized string is a string with the first character in each word - /// changed to its corresponding uppercase value, and all remaining - /// characters set to their corresponding lowercase values. A "word" is any - /// sequence of characters delimited by spaces, tabs, or line terminators. - /// Some common word delimiting punctuation isn't considered, so this - /// property may not generally produce the desired results for multiword - /// strings. See the `getLineStart(_:end:contentsEnd:for:)` method for - /// additional information. - /// - /// Case transformations aren’t guaranteed to be symmetrical or to produce - /// strings of the same lengths as the originals. - public var capitalized: String { - return _ns.capitalized - } - - // @property (readonly, copy) NSString *localizedCapitalizedString NS_AVAILABLE(10_11, 9_0); - - /// A capitalized representation of the string that is produced - /// using the current locale. - @available(macOS 10.11, iOS 9.0, *) - public var localizedCapitalized: String { - return _ns.localizedCapitalized - } - - // - (NSString *)capitalizedStringWithLocale:(Locale *)locale - - /// Returns a capitalized representation of the string - /// using the specified locale. - public func capitalized(with locale: Locale?) -> String { - return _ns.capitalized(with: locale) - } - - // - (NSComparisonResult)caseInsensitiveCompare:(NSString *)aString - - /// Returns the result of invoking `compare:options:` with - /// `NSCaseInsensitiveSearch` as the only option. - public func caseInsensitiveCompare< - T : StringProtocol - >(_ aString: T) -> ComparisonResult { - return _ns.caseInsensitiveCompare(aString._ephemeralString) - } - - //===--- Omitted by agreement during API review 5/20/2014 ---------------===// - // - (unichar)characterAtIndex:(NSUInteger)index - // - // We have a different meaning for "Character" in Swift, and we are - // trying not to expose error-prone UTF-16 integer indexes - - // - (NSString *) - // commonPrefixWithString:(NSString *)aString - // options:(StringCompareOptions)mask - - /// Returns a string containing characters this string and the - /// given string have in common, starting from the beginning of each - /// up to the first characters that aren't equivalent. - public func commonPrefix< - T : StringProtocol - >(with aString: T, options: String.CompareOptions = []) -> String { - return _ns.commonPrefix(with: aString._ephemeralString, options: options) - } - - // - (NSComparisonResult) - // compare:(NSString *)aString - // - // - (NSComparisonResult) - // compare:(NSString *)aString options:(StringCompareOptions)mask - // - // - (NSComparisonResult) - // compare:(NSString *)aString options:(StringCompareOptions)mask - // range:(NSRange)range - // - // - (NSComparisonResult) - // compare:(NSString *)aString options:(StringCompareOptions)mask - // range:(NSRange)range locale:(id)locale - - /// Compares the string using the specified options and - /// returns the lexical ordering for the range. - public func compare( - _ aString: T, - options mask: String.CompareOptions = [], - range: Range? = nil, - locale: Locale? = nil - ) -> ComparisonResult { - // According to Ali Ozer, there may be some real advantage to - // dispatching to the minimal selector for the supplied options. - // So let's do that; the switch should compile away anyhow. - let aString = aString._ephemeralString - return locale != nil ? _ns.compare( - aString, - options: mask, - range: _toRelativeNSRange( - range ?? startIndex..? = nil, - caseSensitive: Bool, - matchesInto outputArray: UnsafeMutablePointer<[String]>? = nil, - filterTypes: [String]? = nil - ) -> Int { -#if DEPLOYMENT_RUNTIME_SWIFT - var outputNamePlaceholder: String? - var outputArrayPlaceholder = [String]() - let res = self._ns.completePath( - into: &outputNamePlaceholder, - caseSensitive: caseSensitive, - matchesInto: &outputArrayPlaceholder, - filterTypes: filterTypes - ) - if let n = outputNamePlaceholder { - outputName?.pointee = n - } else { - outputName?.pointee = "" - } - outputArray?.pointee = outputArrayPlaceholder - return res -#else // DEPLOYMENT_RUNTIME_SWIFT - var nsMatches: NSArray? - var nsOutputName: NSString? - - let result: Int = outputName._withNilOrAddress(of: &nsOutputName) { - outputName in outputArray._withNilOrAddress(of: &nsMatches) { - outputArray in - // FIXME: completePath(...) is incorrectly annotated as requiring - // non-optional output parameters. rdar://problem/25494184 - let outputNonOptionalName = unsafeBitCast( - outputName, to: AutoreleasingUnsafeMutablePointer.self) - let outputNonOptionalArray = unsafeBitCast( - outputArray, to: AutoreleasingUnsafeMutablePointer.self) - return self._ns.completePath( - into: outputNonOptionalName, - caseSensitive: caseSensitive, - matchesInto: outputNonOptionalArray, - filterTypes: filterTypes - ) - } - } - - if let matches = nsMatches { - // Since this function is effectively a bridge thunk, use the - // bridge thunk semantics for the NSArray conversion - outputArray?.pointee = matches as! [String] - } - - if let n = nsOutputName { - outputName?.pointee = n as String - } - return result -#endif // DEPLOYMENT_RUNTIME_SWIFT - } - - // - (NSArray *) - // componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator - - /// Returns an array containing substrings from the string - /// that have been divided by characters in the given set. - public func components(separatedBy separator: CharacterSet) -> [String] { - return _ns.components(separatedBy: separator) - } - - // - (NSArray *)componentsSeparatedByString:(NSString *)separator - - /// Returns an array containing substrings from the string that have been - /// divided by the given separator. - /// - /// The substrings in the resulting array appear in the same order as the - /// original string. Adjacent occurrences of the separator string produce - /// empty strings in the result. Similarly, if the string begins or ends - /// with the separator, the first or last substring, respectively, is empty. - /// The following example shows this behavior: - /// - /// let list1 = "Karin, Carrie, David" - /// let items1 = list1.components(separatedBy: ", ") - /// // ["Karin", "Carrie", "David"] - /// - /// // Beginning with the separator: - /// let list2 = ", Norman, Stanley, Fletcher" - /// let items2 = list2.components(separatedBy: ", ") - /// // ["", "Norman", "Stanley", "Fletcher" - /// - /// If the list has no separators, the array contains only the original - /// string itself. - /// - /// let name = "Karin" - /// let list = name.components(separatedBy: ", ") - /// // ["Karin"] - /// - /// - Parameter separator: The separator string. - /// - Returns: An array containing substrings that have been divided from the - /// string using `separator`. - // FIXME(strings): now when String conforms to Collection, this can be - // replaced by split(separator:maxSplits:omittingEmptySubsequences:) - public func components< - T : StringProtocol - >(separatedBy separator: T) -> [String] { - return _ns.components(separatedBy: separator._ephemeralString) - } - - // - (const char *)cStringUsingEncoding:(NSStringEncoding)encoding - - /// Returns a representation of the string as a C string - /// using a given encoding. - public func cString(using encoding: String.Encoding) -> [CChar]? { - return withExtendedLifetime(_ns) { - (s: NSString) -> [CChar]? in - _persistCString(s.cString(using: encoding.rawValue)) - } - } - - // - (NSData *)dataUsingEncoding:(NSStringEncoding)encoding - // - // - (NSData *) - // dataUsingEncoding:(NSStringEncoding)encoding - // allowLossyConversion:(BOOL)flag - - /// Returns a `Data` containing a representation of - /// the `String` encoded using a given encoding. - public func data( - using encoding: String.Encoding, - allowLossyConversion: Bool = false - ) -> Data? { - switch encoding { - case .utf8: - return Data(self.utf8) - default: - return _ns.data( - using: encoding.rawValue, - allowLossyConversion: allowLossyConversion) - } - } - - // @property NSString* decomposedStringWithCanonicalMapping; - - /// A string created by normalizing the string's contents using Form D. - public var decomposedStringWithCanonicalMapping: String { - return _ns.decomposedStringWithCanonicalMapping - } - - // @property NSString* decomposedStringWithCompatibilityMapping; - - /// A string created by normalizing the string's contents using Form KD. - public var decomposedStringWithCompatibilityMapping: String { - return _ns.decomposedStringWithCompatibilityMapping - } - - //===--- Importing Foundation should not affect String printing ---------===// - // Therefore, we're not exposing this: - // - // @property NSString* description - - - //===--- Omitted for consistency with API review results 5/20/2014 -----===// - // @property double doubleValue; - - // - (void) - // enumerateLinesUsing:(void (^)(NSString *line, BOOL *stop))block - - /// Enumerates all the lines in a string. - public func enumerateLines( - invoking body: @escaping (_ line: String, _ stop: inout Bool) -> Void - ) { - _ns.enumerateLines { - (line: String, stop: UnsafeMutablePointer) - in - var stop_ = false - body(line, &stop_) - if stop_ { - stop.pointee = true - } - } - } - - // @property NSStringEncoding fastestEncoding; - - /// The fastest encoding to which the string can be converted without loss - /// of information. - public var fastestEncoding: String.Encoding { - return String.Encoding(rawValue: _ns.fastestEncoding) - } - - // - (BOOL) - // getCString:(char *)buffer - // maxLength:(NSUInteger)maxBufferCount - // encoding:(NSStringEncoding)encoding - - /// Converts the `String`'s content to a given encoding and - /// stores them in a buffer. - /// - Note: will store a maximum of `min(buffer.count, maxLength)` bytes. - public func getCString( - _ buffer: inout [CChar], maxLength: Int, encoding: String.Encoding - ) -> Bool { - return _ns.getCString(&buffer, - maxLength: Swift.min(buffer.count, maxLength), - encoding: encoding.rawValue) - } - - // - (NSUInteger)hash - - /// An unsigned integer that can be used as a hash table address. - public var hash: Int { - return _ns.hash - } - - // - (NSUInteger)lengthOfBytesUsingEncoding:(NSStringEncoding)enc - - /// Returns the number of bytes required to store the - /// `String` in a given encoding. - public func lengthOfBytes(using encoding: String.Encoding) -> Int { - return _ns.lengthOfBytes(using: encoding.rawValue) - } - - // - (NSComparisonResult)localizedCaseInsensitiveCompare:(NSString *)aString - - /// Compares the string and the given string using a case-insensitive, - /// localized, comparison. - public - func localizedCaseInsensitiveCompare< - T : StringProtocol - >(_ aString: T) -> ComparisonResult { - return _ns.localizedCaseInsensitiveCompare(aString._ephemeralString) - } - - // - (NSComparisonResult)localizedCompare:(NSString *)aString - - /// Compares the string and the given string using a localized comparison. - public func localizedCompare< - T : StringProtocol - >(_ aString: T) -> ComparisonResult { - return _ns.localizedCompare(aString._ephemeralString) - } - - /// Compares the string and the given string as sorted by the Finder. - public func localizedStandardCompare< - T : StringProtocol - >(_ string: T) -> ComparisonResult { - return _ns.localizedStandardCompare(string._ephemeralString) - } - - //===--- Omitted for consistency with API review results 5/20/2014 ------===// - // @property long long longLongValue - - // @property (readonly, copy) NSString *localizedLowercase NS_AVAILABLE(10_11, 9_0); - - /// A lowercase version of the string that is produced using the current - /// locale. - @available(macOS 10.11, iOS 9.0, *) - public var localizedLowercase: String { - return _ns.localizedLowercase - } - - // - (NSString *)lowercaseStringWithLocale:(Locale *)locale - - /// Returns a version of the string with all letters - /// converted to lowercase, taking into account the specified - /// locale. - public func lowercased(with locale: Locale?) -> String { - return _ns.lowercased(with: locale) - } - - // - (NSUInteger)maximumLengthOfBytesUsingEncoding:(NSStringEncoding)enc - - /// Returns the maximum number of bytes needed to store the - /// `String` in a given encoding. - public - func maximumLengthOfBytes(using encoding: String.Encoding) -> Int { - return _ns.maximumLengthOfBytes(using: encoding.rawValue) - } - - // @property NSString* precomposedStringWithCanonicalMapping; - - /// A string created by normalizing the string's contents using Form C. - public var precomposedStringWithCanonicalMapping: String { - return _ns.precomposedStringWithCanonicalMapping - } - - // @property NSString * precomposedStringWithCompatibilityMapping; - - /// A string created by normalizing the string's contents using Form KC. - public var precomposedStringWithCompatibilityMapping: String { - return _ns.precomposedStringWithCompatibilityMapping - } - -#if !DEPLOYMENT_RUNTIME_SWIFT - // - (id)propertyList - - /// Parses the `String` as a text representation of a - /// property list, returning an NSString, NSData, NSArray, or - /// NSDictionary object, according to the topmost element. - public func propertyList() -> Any { - return _ns.propertyList() - } - - // - (NSDictionary *)propertyListFromStringsFileFormat - - /// Returns a dictionary object initialized with the keys and - /// values found in the `String`. - public func propertyListFromStringsFileFormat() -> [String : String] { - return _ns.propertyListFromStringsFileFormat() as! [String : String]? ?? [:] - } -#endif - - // - (BOOL)localizedStandardContainsString:(NSString *)str NS_AVAILABLE(10_11, 9_0); - - /// Returns a Boolean value indicating whether the string contains the given - /// string, taking the current locale into account. - /// - /// This is the most appropriate method for doing user-level string searches, - /// similar to how searches are done generally in the system. The search is - /// locale-aware, case and diacritic insensitive. The exact list of search - /// options applied may change over time. - @available(macOS 10.11, iOS 9.0, *) - public func localizedStandardContains< - T : StringProtocol - >(_ string: T) -> Bool { - return _ns.localizedStandardContains(string._ephemeralString) - } - - // @property NSStringEncoding smallestEncoding; - - /// The smallest encoding to which the string can be converted without - /// loss of information. - public var smallestEncoding: String.Encoding { - return String.Encoding(rawValue: _ns.smallestEncoding) - } - - // - (NSString *) - // stringByAddingPercentEncodingWithAllowedCharacters: - // (NSCharacterSet *)allowedCharacters - - /// Returns a new string created by replacing all characters in the string - /// not in the specified set with percent encoded characters. - public func addingPercentEncoding( - withAllowedCharacters allowedCharacters: CharacterSet - ) -> String? { - // FIXME: the documentation states that this method can return nil if the - // transformation is not possible, without going into further details. The - // implementation can only return nil if malloc() returns nil, so in - // practice this is not possible. Still, to be consistent with - // documentation, we declare the method as returning an optional String. - // - // Docs for -[NSString - // stringByAddingPercentEncodingWithAllowedCharacters] don't precisely - // describe when return value is nil - return _ns.addingPercentEncoding(withAllowedCharacters: - allowedCharacters - ) - } - - // - (NSString *)stringByAppendingFormat:(NSString *)format, ... - - /// Returns a string created by appending a string constructed from a given - /// format string and the following arguments. - public func appendingFormat< - T : StringProtocol - >( - _ format: T, _ arguments: CVarArg... - ) -> String { - return _ns.appending( - String(format: format._ephemeralString, arguments: arguments)) - } - - // - (NSString *)stringByAppendingString:(NSString *)aString - - /// Returns a new string created by appending the given string. - // FIXME(strings): shouldn't it be deprecated in favor of `+`? - public func appending< - T : StringProtocol - >(_ aString: T) -> String { - return _ns.appending(aString._ephemeralString) - } - - /// Returns a string with the given character folding options - /// applied. - public func folding( - options: String.CompareOptions = [], locale: Locale? - ) -> String { - return _ns.folding(options: options, locale: locale) - } - - // - (NSString *)stringByPaddingToLength:(NSUInteger)newLength - // withString:(NSString *)padString - // startingAtIndex:(NSUInteger)padIndex - - /// Returns a new string formed from the `String` by either - /// removing characters from the end, or by appending as many - /// occurrences as necessary of a given pad string. - public func padding< - T : StringProtocol - >( - toLength newLength: Int, - withPad padString: T, - startingAt padIndex: Int - ) -> String { - return _ns.padding( - toLength: newLength, - withPad: padString._ephemeralString, - startingAt: padIndex) - } - - // @property NSString* stringByRemovingPercentEncoding; - - /// A new string made from the string by replacing all percent encoded - /// sequences with the matching UTF-8 characters. - public var removingPercentEncoding: String? { - return _ns.removingPercentEncoding - } - - // - (NSString *) - // stringByReplacingCharactersInRange:(NSRange)range - // withString:(NSString *)replacement - - /// Returns a new string in which the characters in a - /// specified range of the `String` are replaced by a given string. - public func replacingCharacters< - T : StringProtocol, R : RangeExpression - >(in range: R, with replacement: T) -> String where R.Bound == Index { - return _ns.replacingCharacters( - in: _toRelativeNSRange(range.relative(to: self)), - with: replacement._ephemeralString) - } - - // - (NSString *) - // stringByReplacingOccurrencesOfString:(NSString *)target - // withString:(NSString *)replacement - // - // - (NSString *) - // stringByReplacingOccurrencesOfString:(NSString *)target - // withString:(NSString *)replacement - // options:(StringCompareOptions)options - // range:(NSRange)searchRange - - /// Returns a new string in which all occurrences of a target - /// string in a specified range of the string are replaced by - /// another given string. - public func replacingOccurrences< - Target : StringProtocol, - Replacement : StringProtocol - >( - of target: Target, - with replacement: Replacement, - options: String.CompareOptions = [], - range searchRange: Range? = nil - ) -> String { - let target = target._ephemeralString - let replacement = replacement._ephemeralString - return (searchRange != nil) || (!options.isEmpty) - ? _ns.replacingOccurrences( - of: target, - with: replacement, - options: options, - range: _toRelativeNSRange( - searchRange ?? startIndex.. String? { - return _ns.replacingPercentEscapes(using: encoding.rawValue) - } -#endif - - // - (NSString *)stringByTrimmingCharactersInSet:(NSCharacterSet *)set - - /// Returns a new string made by removing from both ends of - /// the `String` characters contained in a given character set. - public func trimmingCharacters(in set: CharacterSet) -> String { - return _ns.trimmingCharacters(in: set) - } - - // @property (readonly, copy) NSString *localizedUppercaseString NS_AVAILABLE(10_11, 9_0); - - /// An uppercase version of the string that is produced using the current - /// locale. - @available(macOS 10.11, iOS 9.0, *) - public var localizedUppercase: String { - return _ns.localizedUppercase - } - - // - (NSString *)uppercaseStringWithLocale:(Locale *)locale - - /// Returns a version of the string with all letters - /// converted to uppercase, taking into account the specified - /// locale. - public func uppercased(with locale: Locale?) -> String { - return _ns.uppercased(with: locale) - } - - //===--- Omitted due to redundancy with "utf8" property -----------------===// - // - (const char *)UTF8String - - // - (BOOL) - // writeToFile:(NSString *)path - // atomically:(BOOL)useAuxiliaryFile - // encoding:(NSStringEncoding)enc - // error:(NSError **)error - - /// Writes the contents of the `String` to a file at a given - /// path using a given encoding. - public func write< - T : StringProtocol - >( - toFile path: T, atomically useAuxiliaryFile: Bool, - encoding enc: String.Encoding - ) throws { - try _ns.write( - toFile: path._ephemeralString, - atomically: useAuxiliaryFile, - encoding: enc.rawValue) - } - - // - (BOOL) - // writeToURL:(NSURL *)url - // atomically:(BOOL)useAuxiliaryFile - // encoding:(NSStringEncoding)enc - // error:(NSError **)error - - /// Writes the contents of the `String` to the URL specified - /// by url using the specified encoding. - public func write( - to url: URL, atomically useAuxiliaryFile: Bool, - encoding enc: String.Encoding - ) throws { - try _ns.write( - to: url, atomically: useAuxiliaryFile, encoding: enc.rawValue) - } - - // - (nullable NSString *)stringByApplyingTransform:(NSString *)transform reverse:(BOOL)reverse NS_AVAILABLE(10_11, 9_0); - -#if !DEPLOYMENT_RUNTIME_SWIFT - /// Perform string transliteration. - @available(macOS 10.11, iOS 9.0, *) - public func applyingTransform( - _ transform: StringTransform, reverse: Bool - ) -> String? { - return _ns.applyingTransform(transform, reverse: reverse) - } - - // - (void) - // enumerateLinguisticTagsInRange:(NSRange)range - // scheme:(NSString *)tagScheme - // options:(LinguisticTaggerOptions)opts - // orthography:(Orthography *)orthography - // usingBlock:( - // void (^)( - // NSString *tag, NSRange tokenRange, - // NSRange sentenceRange, BOOL *stop) - // )block - - /// Performs linguistic analysis on the specified string by - /// enumerating the specific range of the string, providing the - /// Block with the located tags. - public func enumerateLinguisticTags< - T : StringProtocol, R : RangeExpression - >( - in range: R, - scheme tagScheme: T, - options opts: NSLinguisticTagger.Options = [], - orthography: NSOrthography? = nil, - invoking body: - (String, Range, Range, inout Bool) -> Void - ) where R.Bound == Index { - let range = range.relative(to: self) - _ns.enumerateLinguisticTags( - in: _toRelativeNSRange(range), - scheme: NSLinguisticTagScheme(rawValue: tagScheme._ephemeralString), - options: opts, - orthography: orthography - ) { - var stop_ = false - body($0!.rawValue, self._toRange($1), self._toRange($2), &stop_) - if stop_ { - $3.pointee = true - } - } - } -#endif - - // - (void) - // enumerateSubstringsInRange:(NSRange)range - // options:(NSStringEnumerationOptions)opts - // usingBlock:( - // void (^)( - // NSString *substring, - // NSRange substringRange, - // NSRange enclosingRange, - // BOOL *stop) - // )block - - /// Enumerates the substrings of the specified type in the specified range of - /// the string. - /// - /// Mutation of a string value while enumerating its substrings is not - /// supported. If you need to mutate a string from within `body`, convert - /// your string to an `NSMutableString` instance and then call the - /// `enumerateSubstrings(in:options:using:)` method. - /// - /// - Parameters: - /// - range: The range within the string to enumerate substrings. - /// - opts: Options specifying types of substrings and enumeration styles. - /// If `opts` is omitted or empty, `body` is called a single time with - /// the range of the string specified by `range`. - /// - body: The closure executed for each substring in the enumeration. The - /// closure takes four arguments: - /// - The enumerated substring. If `substringNotRequired` is included in - /// `opts`, this parameter is `nil` for every execution of the - /// closure. - /// - The range of the enumerated substring in the string that - /// `enumerate(in:options:_:)` was called on. - /// - The range that includes the substring as well as any separator or - /// filler characters that follow. For instance, for lines, - /// `enclosingRange` contains the line terminators. The enclosing - /// range for the first string enumerated also contains any characters - /// that occur before the string. Consecutive enclosing ranges are - /// guaranteed not to overlap, and every single character in the - /// enumerated range is included in one and only one enclosing range. - /// - An `inout` Boolean value that the closure can use to stop the - /// enumeration by setting `stop = true`. - public func enumerateSubstrings< - R : RangeExpression - >( - in range: R, - options opts: String.EnumerationOptions = [], - _ body: @escaping ( - _ substring: String?, _ substringRange: Range, - _ enclosingRange: Range, inout Bool - ) -> Void - ) where R.Bound == Index { - _ns.enumerateSubstrings( - in: _toRelativeNSRange(range.relative(to: self)), options: opts) { - var stop_ = false - - body($0, - self._toRange($1), - self._toRange($2), - &stop_) - - if stop_ { - UnsafeMutablePointer($3).pointee = true - } - } - } - - //===--- Omitted for consistency with API review results 5/20/2014 ------===// - // @property float floatValue; - - // - (BOOL) - // getBytes:(void *)buffer - // maxLength:(NSUInteger)maxBufferCount - // usedLength:(NSUInteger*)usedBufferCount - // encoding:(NSStringEncoding)encoding - // options:(StringEncodingConversionOptions)options - // range:(NSRange)range - // remainingRange:(NSRangePointer)leftover - - /// Writes the given `range` of characters into `buffer` in a given - /// `encoding`, without any allocations. Does not NULL-terminate. - /// - /// - Parameter buffer: A buffer into which to store the bytes from - /// the receiver. The returned bytes are not NUL-terminated. - /// - /// - Parameter maxBufferCount: The maximum number of bytes to write - /// to buffer. - /// - /// - Parameter usedBufferCount: The number of bytes used from - /// buffer. Pass `nil` if you do not need this value. - /// - /// - Parameter encoding: The encoding to use for the returned bytes. - /// - /// - Parameter options: A mask to specify options to use for - /// converting the receiver's contents to `encoding` (if conversion - /// is necessary). - /// - /// - Parameter range: The range of characters in the receiver to get. - /// - /// - Parameter leftover: The remaining range. Pass `nil` If you do - /// not need this value. - /// - /// - Returns: `true` if some characters were converted, `false` otherwise. - /// - /// - Note: Conversion stops when the buffer fills or when the - /// conversion isn't possible due to the chosen encoding. - /// - /// - Note: will get a maximum of `min(buffer.count, maxLength)` bytes. - public func getBytes< - R : RangeExpression - >( - _ buffer: inout [UInt8], - maxLength maxBufferCount: Int, - usedLength usedBufferCount: UnsafeMutablePointer, - encoding: String.Encoding, - options: String.EncodingConversionOptions = [], - range: R, - remaining leftover: UnsafeMutablePointer> - ) -> Bool where R.Bound == Index { - return _withOptionalOutParameter(leftover) { - self._ns.getBytes( - &buffer, - maxLength: Swift.min(buffer.count, maxBufferCount), - usedLength: usedBufferCount, - encoding: encoding.rawValue, - options: options, - range: _toRelativeNSRange(range.relative(to: self)), - remaining: $0) - } - } - - // - (void) - // getLineStart:(NSUInteger *)startIndex - // end:(NSUInteger *)lineEndIndex - // contentsEnd:(NSUInteger *)contentsEndIndex - // forRange:(NSRange)aRange - - /// Returns by reference the beginning of the first line and - /// the end of the last line touched by the given range. - public func getLineStart< - R : RangeExpression - >( - _ start: UnsafeMutablePointer, - end: UnsafeMutablePointer, - contentsEnd: UnsafeMutablePointer, - for range: R - ) where R.Bound == Index { - _withOptionalOutParameter(start) { - start in self._withOptionalOutParameter(end) { - end in self._withOptionalOutParameter(contentsEnd) { - contentsEnd in self._ns.getLineStart( - start, end: end, - contentsEnd: contentsEnd, - for: _toRelativeNSRange(range.relative(to: self))) - } - } - } - } - - // - (void) - // getParagraphStart:(NSUInteger *)startIndex - // end:(NSUInteger *)endIndex - // contentsEnd:(NSUInteger *)contentsEndIndex - // forRange:(NSRange)aRange - - /// Returns by reference the beginning of the first paragraph - /// and the end of the last paragraph touched by the given range. - public func getParagraphStart< - R : RangeExpression - >( - _ start: UnsafeMutablePointer, - end: UnsafeMutablePointer, - contentsEnd: UnsafeMutablePointer, - for range: R - ) where R.Bound == Index { - _withOptionalOutParameter(start) { - start in self._withOptionalOutParameter(end) { - end in self._withOptionalOutParameter(contentsEnd) { - contentsEnd in self._ns.getParagraphStart( - start, end: end, - contentsEnd: contentsEnd, - for: _toRelativeNSRange(range.relative(to: self))) - } - } - } - } - - //===--- Already provided by core Swift ---------------------------------===// - // - (instancetype)initWithString:(NSString *)aString - - //===--- Initializers that can fail dropped for factory functions -------===// - // - (instancetype)initWithUTF8String:(const char *)bytes - - //===--- Omitted for consistency with API review results 5/20/2014 ------===// - // @property NSInteger integerValue; - // @property Int intValue; - - //===--- Omitted by apparent agreement during API review 5/20/2014 ------===// - // @property BOOL absolutePath; - // - (BOOL)isEqualToString:(NSString *)aString - - // - (NSRange)lineRangeForRange:(NSRange)aRange - - /// Returns the range of characters representing the line or lines - /// containing a given range. - public func lineRange< - R : RangeExpression - >(for aRange: R) -> Range where R.Bound == Index { - return _toRange(_ns.lineRange( - for: _toRelativeNSRange(aRange.relative(to: self)))) - } - -#if !DEPLOYMENT_RUNTIME_SWIFT - // - (NSArray *) - // linguisticTagsInRange:(NSRange)range - // scheme:(NSString *)tagScheme - // options:(LinguisticTaggerOptions)opts - // orthography:(Orthography *)orthography - // tokenRanges:(NSArray**)tokenRanges - - /// Returns an array of linguistic tags for the specified - /// range and requested tags within the receiving string. - public func linguisticTags< - T : StringProtocol, R : RangeExpression - >( - in range: R, - scheme tagScheme: T, - options opts: NSLinguisticTagger.Options = [], - orthography: NSOrthography? = nil, - tokenRanges: UnsafeMutablePointer<[Range]>? = nil // FIXME:Can this be nil? - ) -> [String] where R.Bound == Index { - var nsTokenRanges: NSArray? - let result = tokenRanges._withNilOrAddress(of: &nsTokenRanges) { - self._ns.linguisticTags( - in: _toRelativeNSRange(range.relative(to: self)), - scheme: NSLinguisticTagScheme(rawValue: tagScheme._ephemeralString), - options: opts, - orthography: orthography, - tokenRanges: $0) as NSArray - } - - if let nsTokenRanges = nsTokenRanges { - tokenRanges?.pointee = (nsTokenRanges as [AnyObject]).map { - self._toRange($0.rangeValue) - } - } - - return result as! [String] - } - - // - (NSRange)paragraphRangeForRange:(NSRange)aRange - - /// Returns the range of characters representing the - /// paragraph or paragraphs containing a given range. - public func paragraphRange< - R : RangeExpression - >(for aRange: R) -> Range where R.Bound == Index { - return _toRange( - _ns.paragraphRange(for: _toRelativeNSRange(aRange.relative(to: self)))) - } -#endif - - // - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet - // - // - (NSRange) - // rangeOfCharacterFromSet:(NSCharacterSet *)aSet - // options:(StringCompareOptions)mask - // - // - (NSRange) - // rangeOfCharacterFromSet:(NSCharacterSet *)aSet - // options:(StringCompareOptions)mask - // range:(NSRange)aRange - - /// Finds and returns the range in the `String` of the first - /// character from a given character set found in a given range with - /// given options. - public func rangeOfCharacter( - from aSet: CharacterSet, - options mask: String.CompareOptions = [], - range aRange: Range? = nil - ) -> Range? { - return _optionalRange( - _ns.rangeOfCharacter( - from: aSet, - options: mask, - range: _toRelativeNSRange( - aRange ?? startIndex.. Range { - return _toRange( - _ns.rangeOfComposedCharacterSequence(at: _toOffset(anIndex))) - } - - // - (NSRange)rangeOfComposedCharacterSequencesForRange:(NSRange)range - - /// Returns the range in the string of the composed character - /// sequences for a given range. - public func rangeOfComposedCharacterSequences< - R : RangeExpression - >( - for range: R - ) -> Range where R.Bound == Index { - // Theoretically, this will be the identity function. In practice - // I think users will be able to observe differences in the input - // and output ranges due (if nothing else) to locale changes - return _toRange( - _ns.rangeOfComposedCharacterSequences( - for: _toRelativeNSRange(range.relative(to: self)))) - } - - // - (NSRange)rangeOfString:(NSString *)aString - // - // - (NSRange) - // rangeOfString:(NSString *)aString options:(StringCompareOptions)mask - // - // - (NSRange) - // rangeOfString:(NSString *)aString - // options:(StringCompareOptions)mask - // range:(NSRange)aRange - // - // - (NSRange) - // rangeOfString:(NSString *)aString - // options:(StringCompareOptions)mask - // range:(NSRange)searchRange - // locale:(Locale *)locale - - /// Finds and returns the range of the first occurrence of a - /// given string within a given range of the `String`, subject to - /// given options, using the specified locale, if any. - public func range< - T : StringProtocol - >( - of aString: T, - options mask: String.CompareOptions = [], - range searchRange: Range? = nil, - locale: Locale? = nil - ) -> Range? { - let aString = aString._ephemeralString - return _optionalRange( - locale != nil ? _ns.range( - of: aString, - options: mask, - range: _toRelativeNSRange( - searchRange ?? startIndex..(of string: T) -> Range? { - return _optionalRange( - _ns.localizedStandardRange(of: string._ephemeralString)) - } - -#if !DEPLOYMENT_RUNTIME_SWIFT - // - (NSString *) - // stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)encoding - - /// Returns a representation of the `String` using a given - /// encoding to determine the percent escapes necessary to convert - /// the `String` into a legal URL string. - @available(swift, deprecated: 3.0, obsoleted: 4.0, - message: "Use addingPercentEncoding(withAllowedCharacters:) instead, which always uses the recommended UTF-8 encoding, and which encodes for a specific URL component or subcomponent since each URL component or subcomponent has different rules for what characters are valid.") - public func addingPercentEscapes( - using encoding: String.Encoding - ) -> String? { - return _ns.addingPercentEscapes(using: encoding.rawValue) - } -#endif - - //===--- From the 10.10 release notes; not in public documentation ------===// - // No need to make these unavailable on earlier OSes, since they can - // forward trivially to rangeOfString. - - /// Returns `true` if `other` is non-empty and contained within `self` by - /// case-sensitive, non-literal search; otherwise, returns `false`. - /// - /// Equivalent to `self.range(of: other) != nil` - public func contains(_ other: T) -> Bool { - let r = self.range(of: other) != nil - if #available(macOS 10.10, iOS 8.0, *) { - assert(r == _ns.contains(other._ephemeralString)) - } - return r - } - - /// Returns a Boolean value indicating whether the given string is non-empty - /// and contained within this string by case-insensitive, non-literal - /// search, taking into account the current locale. - /// - /// Locale-independent case-insensitive operation, and other needs, can be - /// achieved by calling `range(of:options:range:locale:)`. - /// - /// Equivalent to: - /// - /// range(of: other, options: .caseInsensitiveSearch, - /// locale: Locale.current) != nil - public func localizedCaseInsensitiveContains< - T : StringProtocol - >(_ other: T) -> Bool { - let r = self.range( - of: other, options: .caseInsensitive, locale: Locale.current - ) != nil - if #available(macOS 10.10, iOS 8.0, *) { - assert(r == - _ns.localizedCaseInsensitiveContains(other._ephemeralString)) - } - return r - } -} - -// Deprecated slicing -extension StringProtocol where Index == String.Index { - // - (NSString *)substringFromIndex:(NSUInteger)anIndex - - /// Returns a new string containing the characters of the - /// `String` from the one at a given index to the end. - @available(swift, deprecated: 4.0, - message: "Please use String slicing subscript with a 'partial range from' operator.") - public func substring(from index: Index) -> String { - return _ns.substring(from: _toOffset(index)) - } - - // - (NSString *)substringToIndex:(NSUInteger)anIndex - - /// Returns a new string containing the characters of the - /// `String` up to, but not including, the one at a given index. - @available(swift, deprecated: 4.0, - message: "Please use String slicing subscript with a 'partial range upto' operator.") - public func substring(to index: Index) -> String { - return _ns.substring(to: _toOffset(index)) - } - - // - (NSString *)substringWithRange:(NSRange)aRange - - /// Returns a string object containing the characters of the - /// `String` that lie within a given range. - @available(swift, deprecated: 4.0, - message: "Please use String slicing subscript.") - public func substring(with aRange: Range) -> String { - return _ns.substring(with: _toRelativeNSRange(aRange)) - } -} - -extension StringProtocol { - // - (const char *)fileSystemRepresentation - - /// Returns a file system-specific representation of the `String`. - @available(*, unavailable, message: "Use getFileSystemRepresentation on URL instead.") - public var fileSystemRepresentation: [CChar] { - fatalError("unavailable function can't be called") - } - - // - (BOOL) - // getFileSystemRepresentation:(char *)buffer - // maxLength:(NSUInteger)maxLength - - /// Interprets the `String` as a system-independent path and - /// fills a buffer with a C-string in a format and encoding suitable - /// for use with file-system calls. - /// - Note: will store a maximum of `min(buffer.count, maxLength)` bytes. - @available(*, unavailable, message: "Use getFileSystemRepresentation on URL instead.") - public func getFileSystemRepresentation( - _ buffer: inout [CChar], maxLength: Int) -> Bool { - fatalError("unavailable function can't be called") - } - - //===--- Kept for consistency with API review results 5/20/2014 ---------===// - // We decided to keep pathWithComponents, so keeping this too - // @property NSString lastPathComponent; - - /// Returns the last path component of the `String`. - @available(*, unavailable, message: "Use lastPathComponent on URL instead.") - public var lastPathComponent: String { - fatalError("unavailable function can't be called") - } - - //===--- Renamed by agreement during API review 5/20/2014 ---------------===// - // @property NSUInteger length; - - /// Returns the number of Unicode characters in the `String`. - @available(*, unavailable, - message: "Take the count of a UTF-16 view instead, i.e. str.utf16.count") - public var utf16Count: Int { - fatalError("unavailable function can't be called") - } - - // @property NSArray* pathComponents - - /// Returns an array of NSString objects containing, in - /// order, each path component of the `String`. - @available(*, unavailable, message: "Use pathComponents on URL instead.") - public var pathComponents: [String] { - fatalError("unavailable function can't be called") - } - - // @property NSString* pathExtension; - - /// Interprets the `String` as a path and returns the - /// `String`'s extension, if any. - @available(*, unavailable, message: "Use pathExtension on URL instead.") - public var pathExtension: String { - fatalError("unavailable function can't be called") - } - - // @property NSString *stringByAbbreviatingWithTildeInPath; - - /// Returns a new string that replaces the current home - /// directory portion of the current path with a tilde (`~`) - /// character. - @available(*, unavailable, message: "Use abbreviatingWithTildeInPath on NSString instead.") - public var abbreviatingWithTildeInPath: String { - fatalError("unavailable function can't be called") - } - - // - (NSString *)stringByAppendingPathComponent:(NSString *)aString - - /// Returns a new string made by appending to the `String` a given string. - @available(*, unavailable, message: "Use appendingPathComponent on URL instead.") - public func appendingPathComponent(_ aString: String) -> String { - fatalError("unavailable function can't be called") - } - - // - (NSString *)stringByAppendingPathExtension:(NSString *)ext - - /// Returns a new string made by appending to the `String` an - /// extension separator followed by a given extension. - @available(*, unavailable, message: "Use appendingPathExtension on URL instead.") - public func appendingPathExtension(_ ext: String) -> String? { - fatalError("unavailable function can't be called") - } - - // @property NSString* stringByDeletingLastPathComponent; - - /// Returns a new string made by deleting the last path - /// component from the `String`, along with any final path - /// separator. - @available(*, unavailable, message: "Use deletingLastPathComponent on URL instead.") - public var deletingLastPathComponent: String { - fatalError("unavailable function can't be called") - } - - // @property NSString* stringByDeletingPathExtension; - - /// Returns a new string made by deleting the extension (if - /// any, and only the last) from the `String`. - @available(*, unavailable, message: "Use deletingPathExtension on URL instead.") - public var deletingPathExtension: String { - fatalError("unavailable function can't be called") - } - - // @property NSString* stringByExpandingTildeInPath; - - /// Returns a new string made by expanding the initial - /// component of the `String` to its full path value. - @available(*, unavailable, message: "Use expandingTildeInPath on NSString instead.") - public var expandingTildeInPath: String { - fatalError("unavailable function can't be called") - } - - // - (NSString *) - // stringByFoldingWithOptions:(StringCompareOptions)options - // locale:(Locale *)locale - - @available(*, unavailable, renamed: "folding(options:locale:)") - public func folding( - _ options: String.CompareOptions = [], locale: Locale? - ) -> String { - fatalError("unavailable function can't be called") - } - - // @property NSString* stringByResolvingSymlinksInPath; - - /// Returns a new string made from the `String` by resolving - /// all symbolic links and standardizing path. - @available(*, unavailable, message: "Use resolvingSymlinksInPath on URL instead.") - public var resolvingSymlinksInPath: String { - fatalError("unavailable property") - } - - // @property NSString* stringByStandardizingPath; - - /// Returns a new string made by removing extraneous path - /// components from the `String`. - @available(*, unavailable, message: "Use standardizingPath on URL instead.") - public var standardizingPath: String { - fatalError("unavailable function can't be called") - } - - // - (NSArray *)stringsByAppendingPaths:(NSArray *)paths - - /// Returns an array of strings made by separately appending - /// to the `String` each string in a given array. - @available(*, unavailable, message: "Map over paths with appendingPathComponent instead.") - public func strings(byAppendingPaths paths: [String]) -> [String] { - fatalError("unavailable function can't be called") - } - -} - -// Pre-Swift-3 method names -extension String { - @available(*, unavailable, renamed: "localizedName(of:)") - public static func localizedNameOfStringEncoding( - _ encoding: String.Encoding - ) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, message: "Use fileURL(withPathComponents:) on URL instead.") - public static func pathWithComponents(_ components: [String]) -> String { - fatalError("unavailable function can't be called") - } - - // + (NSString *)pathWithComponents:(NSArray *)components - - /// Returns a string built from the strings in a given array - /// by concatenating them with a path separator between each pair. - @available(*, unavailable, message: "Use fileURL(withPathComponents:) on URL instead.") - public static func path(withComponents components: [String]) -> String { - fatalError("unavailable function can't be called") - } -} - -extension StringProtocol { - - @available(*, unavailable, renamed: "canBeConverted(to:)") - public func canBeConvertedToEncoding(_ encoding: String.Encoding) -> Bool { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "capitalizedString(with:)") - public func capitalizedStringWith(_ locale: Locale?) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "commonPrefix(with:options:)") - public func commonPrefixWith( - _ aString: String, options: String.CompareOptions) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "completePath(into:outputName:caseSensitive:matchesInto:filterTypes:)") - public func completePathInto( - _ outputName: UnsafeMutablePointer? = nil, - caseSensitive: Bool, - matchesInto matchesIntoArray: UnsafeMutablePointer<[String]>? = nil, - filterTypes: [String]? = nil - ) -> Int { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "components(separatedBy:)") - public func componentsSeparatedByCharactersIn( - _ separator: CharacterSet - ) -> [String] { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "components(separatedBy:)") - public func componentsSeparatedBy(_ separator: String) -> [String] { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "cString(usingEncoding:)") - public func cStringUsingEncoding(_ encoding: String.Encoding) -> [CChar]? { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "data(usingEncoding:allowLossyConversion:)") - public func dataUsingEncoding( - _ encoding: String.Encoding, - allowLossyConversion: Bool = false - ) -> Data? { - fatalError("unavailable function can't be called") - } - -#if !DEPLOYMENT_RUNTIME_SWIFT - @available(*, unavailable, renamed: "enumerateLinguisticTags(in:scheme:options:orthography:_:)") - public func enumerateLinguisticTagsIn( - _ range: Range, - scheme tagScheme: String, - options opts: NSLinguisticTagger.Options, - orthography: NSOrthography?, - _ body: - (String, Range, Range, inout Bool) -> Void - ) { - fatalError("unavailable function can't be called") - } -#endif - - @available(*, unavailable, renamed: "enumerateSubstrings(in:options:_:)") - public func enumerateSubstringsIn( - _ range: Range, - options opts: String.EnumerationOptions = [], - _ body: ( - _ substring: String?, _ substringRange: Range, - _ enclosingRange: Range, inout Bool - ) -> Void - ) { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "getBytes(_:maxLength:usedLength:encoding:options:range:remaining:)") - public func getBytes( - _ buffer: inout [UInt8], - maxLength maxBufferCount: Int, - usedLength usedBufferCount: UnsafeMutablePointer, - encoding: String.Encoding, - options: String.EncodingConversionOptions = [], - range: Range, - remainingRange leftover: UnsafeMutablePointer> - ) -> Bool { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "getLineStart(_:end:contentsEnd:for:)") - public func getLineStart( - _ start: UnsafeMutablePointer, - end: UnsafeMutablePointer, - contentsEnd: UnsafeMutablePointer, - forRange: Range - ) { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "getParagraphStart(_:end:contentsEnd:for:)") - public func getParagraphStart( - _ start: UnsafeMutablePointer, - end: UnsafeMutablePointer, - contentsEnd: UnsafeMutablePointer, - forRange: Range - ) { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "lengthOfBytes(using:)") - public func lengthOfBytesUsingEncoding(_ encoding: String.Encoding) -> Int { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "lineRange(for:)") - public func lineRangeFor(_ aRange: Range) -> Range { - fatalError("unavailable function can't be called") - } - -#if !DEPLOYMENT_RUNTIME_SWIFT - @available(*, unavailable, renamed: "linguisticTags(in:scheme:options:orthography:tokenRanges:)") - public func linguisticTagsIn( - _ range: Range, - scheme tagScheme: String, - options opts: NSLinguisticTagger.Options = [], - orthography: NSOrthography? = nil, - tokenRanges: UnsafeMutablePointer<[Range]>? = nil - ) -> [String] { - fatalError("unavailable function can't be called") - } -#endif - - @available(*, unavailable, renamed: "lowercased(with:)") - public func lowercaseStringWith(_ locale: Locale?) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "maximumLengthOfBytes(using:)") - public - func maximumLengthOfBytesUsingEncoding(_ encoding: String.Encoding) -> Int { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "paragraphRange(for:)") - public func paragraphRangeFor(_ aRange: Range) -> Range { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "rangeOfCharacter(from:options:range:)") - public func rangeOfCharacterFrom( - _ aSet: CharacterSet, - options mask: String.CompareOptions = [], - range aRange: Range? = nil - ) -> Range? { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "rangeOfComposedCharacterSequence(at:)") - public - func rangeOfComposedCharacterSequenceAt(_ anIndex: Index) -> Range { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "rangeOfComposedCharacterSequences(for:)") - public func rangeOfComposedCharacterSequencesFor( - _ range: Range - ) -> Range { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "range(of:options:range:locale:)") - public func rangeOf( - _ aString: String, - options mask: String.CompareOptions = [], - range searchRange: Range? = nil, - locale: Locale? = nil - ) -> Range? { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "localizedStandardRange(of:)") - public func localizedStandardRangeOf(_ string: String) -> Range? { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "addingPercentEncoding(withAllowedCharacters:)") - public func addingPercentEncodingWithAllowedCharacters( - _ allowedCharacters: CharacterSet - ) -> String? { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "addingPercentEscapes(using:)") - public func addingPercentEscapesUsingEncoding( - _ encoding: String.Encoding - ) -> String? { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "appendingFormat") - public func stringByAppendingFormat( - _ format: String, _ arguments: CVarArg... - ) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "padding(toLength:with:startingAt:)") - public func byPaddingToLength( - _ newLength: Int, withString padString: String, startingAt padIndex: Int - ) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "replacingCharacters(in:with:)") - public func replacingCharactersIn( - _ range: Range, withString replacement: String - ) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "replacingOccurrences(of:with:options:range:)") - public func replacingOccurrencesOf( - _ target: String, - withString replacement: String, - options: String.CompareOptions = [], - range searchRange: Range? = nil - ) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "replacingPercentEscapes(usingEncoding:)") - public func replacingPercentEscapesUsingEncoding( - _ encoding: String.Encoding - ) -> String? { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "trimmingCharacters(in:)") - public func byTrimmingCharactersIn(_ set: CharacterSet) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "strings(byAppendingPaths:)") - public func stringsByAppendingPaths(_ paths: [String]) -> [String] { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "substring(from:)") - public func substringFrom(_ index: Index) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "substring(to:)") - public func substringTo(_ index: Index) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "substring(with:)") - public func substringWith(_ aRange: Range) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "uppercased(with:)") - public func uppercaseStringWith(_ locale: Locale?) -> String { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "write(toFile:atomically:encoding:)") - public func writeToFile( - _ path: String, atomically useAuxiliaryFile:Bool, - encoding enc: String.Encoding - ) throws { - fatalError("unavailable function can't be called") - } - - @available(*, unavailable, renamed: "write(to:atomically:encoding:)") - public func writeToURL( - _ url: URL, atomically useAuxiliaryFile: Bool, - encoding enc: String.Encoding - ) throws { - fatalError("unavailable function can't be called") - } -} diff --git a/stdlib/public/Darwin/Foundation/NSStringEncodings.swift b/stdlib/public/Darwin/Foundation/NSStringEncodings.swift deleted file mode 100644 index 54cf8a4e0d625..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSStringEncodings.swift +++ /dev/null @@ -1,183 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// FIXME: one day this will be bridged from CoreFoundation and we -// should drop it here. (need support -// for CF bridging) -public var kCFStringEncodingASCII: CFStringEncoding { return 0x0600 } - -extension String { - public struct Encoding : RawRepresentable { - public var rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let ascii = Encoding(rawValue: 1) - public static let nextstep = Encoding(rawValue: 2) - public static let japaneseEUC = Encoding(rawValue: 3) - public static let utf8 = Encoding(rawValue: 4) - public static let isoLatin1 = Encoding(rawValue: 5) - public static let symbol = Encoding(rawValue: 6) - public static let nonLossyASCII = Encoding(rawValue: 7) - public static let shiftJIS = Encoding(rawValue: 8) - public static let isoLatin2 = Encoding(rawValue: 9) - public static let unicode = Encoding(rawValue: 10) - public static let windowsCP1251 = Encoding(rawValue: 11) - public static let windowsCP1252 = Encoding(rawValue: 12) - public static let windowsCP1253 = Encoding(rawValue: 13) - public static let windowsCP1254 = Encoding(rawValue: 14) - public static let windowsCP1250 = Encoding(rawValue: 15) - public static let iso2022JP = Encoding(rawValue: 21) - public static let macOSRoman = Encoding(rawValue: 30) - public static let utf16 = Encoding.unicode - public static let utf16BigEndian = Encoding(rawValue: 0x90000100) - public static let utf16LittleEndian = Encoding(rawValue: 0x94000100) - public static let utf32 = Encoding(rawValue: 0x8c000100) - public static let utf32BigEndian = Encoding(rawValue: 0x98000100) - public static let utf32LittleEndian = Encoding(rawValue: 0x9c000100) - } - - public typealias EncodingConversionOptions = NSString.EncodingConversionOptions - public typealias EnumerationOptions = NSString.EnumerationOptions - public typealias CompareOptions = NSString.CompareOptions -} - -extension String.Encoding : Hashable { - public var hashValue: Int { - // Note: This is effectively the same hashValue definition that - // RawRepresentable provides on its own. We only need to keep this to - // ensure ABI compatibility with 5.0. - return rawValue.hashValue - } - - @_alwaysEmitIntoClient // Introduced in 5.1 - public func hash(into hasher: inout Hasher) { - // Note: `hash(only:)` is only defined here because we also define - // `hashValue`. - // - // In 5.0, `hash(into:)` was resolved to RawRepresentable's functionally - // equivalent definition; we added this definition in 5.1 to make it - // clear this `hash(into:)` isn't synthesized by the compiler. - // (Otherwise someone may be tempted to define it, possibly breaking the - // hash encoding and thus the ABI. RawRepresentable's definition is - // inlinable.) - hasher.combine(rawValue) - } - - public static func ==(lhs: String.Encoding, rhs: String.Encoding) -> Bool { - // Note: This is effectively the same == definition that - // RawRepresentable provides on its own. We only need to keep this to - // ensure ABI compatibility with 5.0. - return lhs.rawValue == rhs.rawValue - } -} - -extension String.Encoding : CustomStringConvertible { - public var description: String { - return String.localizedName(of: self) - } -} - -@available(*, unavailable, renamed: "String.Encoding") -public typealias NSStringEncoding = UInt - -@available(*, unavailable, renamed: "String.Encoding.ascii") -public var NSASCIIStringEncoding: String.Encoding { - return String.Encoding.ascii -} -@available(*, unavailable, renamed: "String.Encoding.nextstep") -public var NSNEXTSTEPStringEncoding: String.Encoding { - return String.Encoding.nextstep -} -@available(*, unavailable, renamed: "String.Encoding.japaneseEUC") -public var NSJapaneseEUCStringEncoding: String.Encoding { - return String.Encoding.japaneseEUC -} -@available(*, unavailable, renamed: "String.Encoding.utf8") -public var NSUTF8StringEncoding: String.Encoding { - return String.Encoding.utf8 -} -@available(*, unavailable, renamed: "String.Encoding.isoLatin1") -public var NSISOLatin1StringEncoding: String.Encoding { - return String.Encoding.isoLatin1 -} -@available(*, unavailable, renamed: "String.Encoding.symbol") -public var NSSymbolStringEncoding: String.Encoding { - return String.Encoding.symbol -} -@available(*, unavailable, renamed: "String.Encoding.nonLossyASCII") -public var NSNonLossyASCIIStringEncoding: String.Encoding { - return String.Encoding.nonLossyASCII -} -@available(*, unavailable, renamed: "String.Encoding.shiftJIS") -public var NSShiftJISStringEncoding: String.Encoding { - return String.Encoding.shiftJIS -} -@available(*, unavailable, renamed: "String.Encoding.isoLatin2") -public var NSISOLatin2StringEncoding: String.Encoding { - return String.Encoding.isoLatin2 -} -@available(*, unavailable, renamed: "String.Encoding.unicode") -public var NSUnicodeStringEncoding: String.Encoding { - return String.Encoding.unicode -} -@available(*, unavailable, renamed: "String.Encoding.windowsCP1251") -public var NSWindowsCP1251StringEncoding: String.Encoding { - return String.Encoding.windowsCP1251 -} -@available(*, unavailable, renamed: "String.Encoding.windowsCP1252") -public var NSWindowsCP1252StringEncoding: String.Encoding { - return String.Encoding.windowsCP1252 -} -@available(*, unavailable, renamed: "String.Encoding.windowsCP1253") -public var NSWindowsCP1253StringEncoding: String.Encoding { - return String.Encoding.windowsCP1253 -} -@available(*, unavailable, renamed: "String.Encoding.windowsCP1254") -public var NSWindowsCP1254StringEncoding: String.Encoding { - return String.Encoding.windowsCP1254 -} -@available(*, unavailable, renamed: "String.Encoding.windowsCP1250") -public var NSWindowsCP1250StringEncoding: String.Encoding { - return String.Encoding.windowsCP1250 -} -@available(*, unavailable, renamed: "String.Encoding.iso2022JP") -public var NSISO2022JPStringEncoding: String.Encoding { - return String.Encoding.iso2022JP -} -@available(*, unavailable, renamed: "String.Encoding.macOSRoman") -public var NSMacOSRomanStringEncoding: String.Encoding { - return String.Encoding.macOSRoman -} -@available(*, unavailable, renamed: "String.Encoding.utf16") -public var NSUTF16StringEncoding: String.Encoding { - return String.Encoding.utf16 -} -@available(*, unavailable, renamed: "String.Encoding.utf16BigEndian") -public var NSUTF16BigEndianStringEncoding: String.Encoding { - return String.Encoding.utf16BigEndian -} -@available(*, unavailable, renamed: "String.Encoding.utf16LittleEndian") -public var NSUTF16LittleEndianStringEncoding: String.Encoding { - return String.Encoding.utf16LittleEndian -} -@available(*, unavailable, renamed: "String.Encoding.utf32") -public var NSUTF32StringEncoding: String.Encoding { - return String.Encoding.utf32 -} -@available(*, unavailable, renamed: "String.Encoding.utf32BigEndian") -public var NSUTF32BigEndianStringEncoding: String.Encoding { - return String.Encoding.utf32BigEndian -} -@available(*, unavailable, renamed: "String.Encoding.utf32LittleEndian") -public var NSUTF32LittleEndianStringEncoding: String.Encoding { - return String.Encoding.utf32LittleEndian -} diff --git a/stdlib/public/Darwin/Foundation/NSTextCheckingResult.swift b/stdlib/public/Darwin/Foundation/NSTextCheckingResult.swift deleted file mode 100644 index 4e9f62cf9bc81..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSTextCheckingResult.swift +++ /dev/null @@ -1,31 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -//===----------------------------------------------------------------------===// -// TextChecking -//===----------------------------------------------------------------------===// - -extension NSTextCheckingResult.CheckingType { - public static var allSystemTypes : NSTextCheckingResult.CheckingType { - return NSTextCheckingResult.CheckingType(rawValue: 0xffffffff) - } - - public static var allCustomTypes : NSTextCheckingResult.CheckingType { - return NSTextCheckingResult.CheckingType(rawValue: 0xffffffff << 32) - } - - public static var allTypes : NSTextCheckingResult.CheckingType { - return NSTextCheckingResult.CheckingType(rawValue: UInt64.max) - } -} diff --git a/stdlib/public/Darwin/Foundation/NSURL.swift b/stdlib/public/Darwin/Foundation/NSURL.swift deleted file mode 100644 index 6dacb8b88fc94..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSURL.swift +++ /dev/null @@ -1,21 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -extension NSURL : _CustomPlaygroundQuickLookable { - @available(*, deprecated, message: "NSURL.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - guard let str = absoluteString else { return .text("Unknown URL") } - return .url(str) - } -} diff --git a/stdlib/public/Darwin/Foundation/NSUndoManager.swift b/stdlib/public/Darwin/Foundation/NSUndoManager.swift deleted file mode 100644 index c0afb2183ba41..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSUndoManager.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -extension UndoManager { - @available(*, unavailable, renamed: "registerUndo(withTarget:handler:)") - public func registerUndoWithTarget(_ target: TargetType, handler: (TargetType) -> Void) { - fatalError("This API has been renamed") - } - - @available(macOS 10.11, iOS 9.0, *) - public func registerUndo(withTarget target: TargetType, handler: @escaping (TargetType) -> Void) { - __NSUndoManagerRegisterWithTargetHandler( self, target) { internalTarget in - handler(internalTarget as! TargetType) - } - } -} diff --git a/stdlib/public/Darwin/Foundation/NSValue.swift.gyb b/stdlib/public/Darwin/Foundation/NSValue.swift.gyb deleted file mode 100644 index 8c09601b52af8..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSValue.swift.gyb +++ /dev/null @@ -1,50 +0,0 @@ -//===--- NSValue.swift - Bridging things in NSValue -----------*- swift -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -%{ -from gyb_foundation_support import ObjectiveCBridgeableImplementationForNSValue -}% - -import CoreGraphics - -${ ObjectiveCBridgeableImplementationForNSValue("NSRange") } -${ ObjectiveCBridgeableImplementationForNSValue("CGRect") } -${ ObjectiveCBridgeableImplementationForNSValue("CGPoint") } -${ ObjectiveCBridgeableImplementationForNSValue("CGVector") } -${ ObjectiveCBridgeableImplementationForNSValue("CGSize") } -${ ObjectiveCBridgeableImplementationForNSValue("CGAffineTransform") } - -extension NSValue { - @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - public func value(of type: StoredType.Type) -> StoredType? { - if StoredType.self is AnyObject.Type { - let encoding = String(cString: objCType) - // some subclasses of NSValue can return @ and the default initialized variant returns ^v for encoding - guard encoding == "^v" || encoding == "@" else { - return nil - } - return nonretainedObjectValue as? StoredType - } else if _isPOD(StoredType.self) { - var storedSize = 0 - var storedAlignment = 0 - NSGetSizeAndAlignment(objCType, &storedSize, &storedAlignment) - guard MemoryLayout.size == storedSize && MemoryLayout.alignment == storedAlignment else { - return nil - } - let allocated = UnsafeMutablePointer.allocate(capacity: 1) - defer { allocated.deallocate() } - getValue(allocated, size: storedSize) - return allocated.pointee - } - return nil - } -} diff --git a/stdlib/public/Darwin/Foundation/Notification.swift b/stdlib/public/Darwin/Foundation/Notification.swift deleted file mode 100644 index 729b59a1d75f8..0000000000000 --- a/stdlib/public/Darwin/Foundation/Notification.swift +++ /dev/null @@ -1,166 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - - -/** - `Notification` encapsulates information broadcast to observers via a `NotificationCenter`. -*/ -public struct Notification : ReferenceConvertible, Equatable, Hashable { - public typealias ReferenceType = NSNotification - - /// A tag identifying the notification. - public var name: Name - - /// An object that the poster wishes to send to observers. - /// - /// Typically this is the object that posted the notification. - public var object: Any? - - /// Storage for values or objects related to this notification. - public var userInfo: [AnyHashable : Any]? - - /// Initialize a new `Notification`. - /// - /// The default value for `userInfo` is nil. - public init(name: Name, object: Any? = nil, userInfo: [AnyHashable : Any]? = nil) { - self.name = name - self.object = object - self.userInfo = userInfo - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(name) - - // FIXME: We should feed `object` to the hasher, too. However, this - // cannot be safely done if its value happens not to be Hashable. - - // FIXME: == compares `userInfo` by bridging it to NSDictionary, so its - // contents should be fully hashed here. However, to prevent stability - // issues with userInfo dictionaries that include non-Hashable values, - // we only feed the keys to the hasher here. - // - // FIXME: This makes for weak hashes. We really should hash everything - // that's compared in ==. - // - // The algorithm below is the same as the one used by Dictionary. - var commutativeKeyHash = 0 - if let userInfo = userInfo { - for (k, _) in userInfo { - var nestedHasher = hasher - nestedHasher.combine(k) - commutativeKeyHash ^= nestedHasher.finalize() - } - } - hasher.combine(commutativeKeyHash) - } - - public var description: String { - return "name = \(name.rawValue), object = \(String(describing: object)), userInfo = \(String(describing: userInfo))" - } - - public var debugDescription: String { - return description - } - - // FIXME: Handle directly via API Notes - public typealias Name = NSNotification.Name - - /// Compare two notifications for equality. - public static func ==(lhs: Notification, rhs: Notification) -> Bool { - if lhs.name.rawValue != rhs.name.rawValue { - return false - } - if let lhsObj = lhs.object { - if let rhsObj = rhs.object { - // FIXME: Converting an arbitrary value to AnyObject won't use a - // stable address when the value needs to be boxed. Therefore, - // this comparison makes == non-reflexive, violating Equatable - // requirements. (rdar://problem/49797185) - if lhsObj as AnyObject !== rhsObj as AnyObject { - return false - } - } else { - return false - } - } else if rhs.object != nil { - return false - } - if lhs.userInfo != nil { - if rhs.userInfo != nil { - // user info must be compared in the object form since the userInfo in swift is not comparable - - // FIXME: This violates reflexivity when userInfo contains any - // non-Hashable values, for the same reason as described above. - return lhs._bridgeToObjectiveC() == rhs._bridgeToObjectiveC() - } else { - return false - } - } else if rhs.userInfo != nil { - return false - } - return true - } -} - -extension Notification: CustomReflectable { - public var customMirror: Mirror { - var children: [(label: String?, value: Any)] = [] - children.append((label: "name", value: self.name.rawValue)) - if let o = self.object { - children.append((label: "object", value: o)) - } - if let u = self.userInfo { - children.append((label: "userInfo", value: u)) - } - let m = Mirror(self, children:children, displayStyle: Mirror.DisplayStyle.class) - return m - } -} - -extension Notification : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSNotification.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSNotification { - return NSNotification(name: name, object: object, userInfo: userInfo) - } - - public static func _forceBridgeFromObjectiveC(_ x: NSNotification, result: inout Notification?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge type") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSNotification, result: inout Notification?) -> Bool { - result = Notification(name: x.name, object: x.object, userInfo: x.userInfo) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSNotification?) -> Notification { - guard let src = source else { return Notification(name: Notification.Name("")) } - return Notification(name: src.name, object: src.object, userInfo: src.userInfo) - } -} - -extension NSNotification : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as Notification) - } -} - diff --git a/stdlib/public/Darwin/Foundation/PersonNameComponents.swift b/stdlib/public/Darwin/Foundation/PersonNameComponents.swift deleted file mode 100644 index 0d69f56544b99..0000000000000 --- a/stdlib/public/Darwin/Foundation/PersonNameComponents.swift +++ /dev/null @@ -1,181 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -@available(macOS 10.11, iOS 9.0, *) -public struct PersonNameComponents : ReferenceConvertible, Hashable, Equatable, _MutableBoxing { - public typealias ReferenceType = NSPersonNameComponents - internal var _handle: _MutableHandle - - public init() { - _handle = _MutableHandle(adoptingReference: NSPersonNameComponents()) - } - - private init(reference: __shared NSPersonNameComponents) { - _handle = _MutableHandle(reference: reference) - } - - /* The below examples all assume the full name Dr. Johnathan Maple Appleseed Esq., nickname "Johnny" */ - - /* Pre-nominal letters denoting title, salutation, or honorific, e.g. Dr., Mr. */ - public var namePrefix: String? { - get { return _handle.map { $0.namePrefix } } - set { _applyMutation { $0.namePrefix = newValue } } - } - - /* Name bestowed upon an individual by one's parents, e.g. Johnathan */ - public var givenName: String? { - get { return _handle.map { $0.givenName } } - set { _applyMutation { $0.givenName = newValue } } - } - - /* Secondary given name chosen to differentiate those with the same first name, e.g. Maple */ - public var middleName: String? { - get { return _handle.map { $0.middleName } } - set { _applyMutation { $0.middleName = newValue } } - } - - /* Name passed from one generation to another to indicate lineage, e.g. Appleseed */ - public var familyName: String? { - get { return _handle.map { $0.familyName } } - set { _applyMutation { $0.familyName = newValue } } - } - - /* Post-nominal letters denoting degree, accreditation, or other honor, e.g. Esq., Jr., Ph.D. */ - public var nameSuffix: String? { - get { return _handle.map { $0.nameSuffix } } - set { _applyMutation { $0.nameSuffix = newValue } } - } - - /* Name substituted for the purposes of familiarity, e.g. "Johnny"*/ - public var nickname: String? { - get { return _handle.map { $0.nickname } } - set { _applyMutation { $0.nickname = newValue } } - } - - /* Each element of the phoneticRepresentation should correspond to an element of the original PersonNameComponents instance. - The phoneticRepresentation of the phoneticRepresentation object itself will be ignored. nil by default, must be instantiated. - */ - public var phoneticRepresentation: PersonNameComponents? { - get { return _handle.map { $0.phoneticRepresentation } } - set { _applyMutation { $0.phoneticRepresentation = newValue } } - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(_handle._uncopiedReference()) - } - - @available(macOS 10.11, iOS 9.0, *) - public static func ==(lhs : PersonNameComponents, rhs: PersonNameComponents) -> Bool { - // Don't copy references here; no one should be storing anything - return lhs._handle._uncopiedReference().isEqual(rhs._handle._uncopiedReference()) - } -} - -@available(macOS 10.11, iOS 9.0, *) -extension PersonNameComponents : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - public var description: String { - return self.customMirror.children.reduce("") { - $0.appending("\($1.label ?? ""): \($1.value) ") - } - } - - public var debugDescription: String { - return self.description - } - - public var customMirror: Mirror { - var c: [(label: String?, value: Any)] = [] - if let r = namePrefix { c.append((label: "namePrefix", value: r)) } - if let r = givenName { c.append((label: "givenName", value: r)) } - if let r = middleName { c.append((label: "middleName", value: r)) } - if let r = familyName { c.append((label: "familyName", value: r)) } - if let r = nameSuffix { c.append((label: "nameSuffix", value: r)) } - if let r = nickname { c.append((label: "nickname", value: r)) } - if let r = phoneticRepresentation { c.append((label: "phoneticRepresentation", value: r)) } - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - -@available(macOS 10.11, iOS 9.0, *) -extension PersonNameComponents : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSPersonNameComponents.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSPersonNameComponents { - return _handle._copiedReference() - } - - public static func _forceBridgeFromObjectiveC(_ personNameComponents: NSPersonNameComponents, result: inout PersonNameComponents?) { - if !_conditionallyBridgeFromObjectiveC(personNameComponents, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ personNameComponents: NSPersonNameComponents, result: inout PersonNameComponents?) -> Bool { - result = PersonNameComponents(reference: personNameComponents) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSPersonNameComponents?) -> PersonNameComponents { - var result: PersonNameComponents? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -@available(macOS 10.11, iOS 9.0, *) -extension NSPersonNameComponents : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as PersonNameComponents) - } -} - -@available(macOS 10.11, iOS 9.0, *) -extension PersonNameComponents : Codable { - private enum CodingKeys : Int, CodingKey { - case namePrefix - case givenName - case middleName - case familyName - case nameSuffix - case nickname - } - - public init(from decoder: Decoder) throws { - self.init() - - let container = try decoder.container(keyedBy: CodingKeys.self) - self.namePrefix = try container.decodeIfPresent(String.self, forKey: .namePrefix) - self.givenName = try container.decodeIfPresent(String.self, forKey: .givenName) - self.middleName = try container.decodeIfPresent(String.self, forKey: .middleName) - self.familyName = try container.decodeIfPresent(String.self, forKey: .familyName) - self.nameSuffix = try container.decodeIfPresent(String.self, forKey: .nameSuffix) - self.nickname = try container.decodeIfPresent(String.self, forKey: .nickname) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - if let np = self.namePrefix { try container.encode(np, forKey: .namePrefix) } - if let gn = self.givenName { try container.encode(gn, forKey: .givenName) } - if let mn = self.middleName { try container.encode(mn, forKey: .middleName) } - if let fn = self.familyName { try container.encode(fn, forKey: .familyName) } - if let ns = self.nameSuffix { try container.encode(ns, forKey: .nameSuffix) } - if let nn = self.nickname { try container.encode(nn, forKey: .nickname) } - } -} diff --git a/stdlib/public/Darwin/Foundation/PlistEncoder.swift b/stdlib/public/Darwin/Foundation/PlistEncoder.swift deleted file mode 100644 index c9e1994dd2c47..0000000000000 --- a/stdlib/public/Darwin/Foundation/PlistEncoder.swift +++ /dev/null @@ -1,1851 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// Plist Encoder -//===----------------------------------------------------------------------===// - -/// `PropertyListEncoder` facilitates the encoding of `Encodable` values into property lists. -// NOTE: older overlays had Foundation.PropertyListEncoder as the ObjC -// name. The two must coexist, so it was renamed. The old name must not -// be used in the new runtime. _TtC10Foundation20_PropertyListEncoder -// is the mangled name for Foundation._PropertyListEncoder. -@_objcRuntimeName(_TtC10Foundation20_PropertyListEncoder) -open class PropertyListEncoder { - - // MARK: - Options - - /// The output format to write the property list data in. Defaults to `.binary`. - open var outputFormat: PropertyListSerialization.PropertyListFormat = .binary - - /// Contextual user-provided information for use during encoding. - open var userInfo: [CodingUserInfoKey : Any] = [:] - - /// Options set on the top-level encoder to pass down the encoding hierarchy. - fileprivate struct _Options { - let outputFormat: PropertyListSerialization.PropertyListFormat - let userInfo: [CodingUserInfoKey : Any] - } - - /// The options set on the top-level encoder. - fileprivate var options: _Options { - return _Options(outputFormat: outputFormat, userInfo: userInfo) - } - - // MARK: - Constructing a Property List Encoder - - /// Initializes `self` with default strategies. - public init() {} - - // MARK: - Encoding Values - - /// Encodes the given top-level value and returns its property list representation. - /// - /// - parameter value: The value to encode. - /// - returns: A new `Data` value containing the encoded property list data. - /// - throws: `EncodingError.invalidValue` if a non-conforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`. - /// - throws: An error if any value throws an error during encoding. - open func encode(_ value: Value) throws -> Data { - let topLevel = try encodeToTopLevelContainer(value) - if topLevel is NSNumber { - throw EncodingError.invalidValue(value, - EncodingError.Context(codingPath: [], - debugDescription: "Top-level \(Value.self) encoded as number property list fragment.")) - } else if topLevel is NSString { - throw EncodingError.invalidValue(value, - EncodingError.Context(codingPath: [], - debugDescription: "Top-level \(Value.self) encoded as string property list fragment.")) - } else if topLevel is NSDate { - throw EncodingError.invalidValue(value, - EncodingError.Context(codingPath: [], - debugDescription: "Top-level \(Value.self) encoded as date property list fragment.")) - } - - do { - return try PropertyListSerialization.data(fromPropertyList: topLevel, format: self.outputFormat, options: 0) - } catch { - throw EncodingError.invalidValue(value, - EncodingError.Context(codingPath: [], debugDescription: "Unable to encode the given top-level value as a property list", underlyingError: error)) - } - } - - /// Encodes the given top-level value and returns its plist-type representation. - /// - /// - parameter value: The value to encode. - /// - returns: A new top-level array or dictionary representing the value. - /// - throws: `EncodingError.invalidValue` if a non-conforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`. - /// - throws: An error if any value throws an error during encoding. - internal func encodeToTopLevelContainer(_ value: Value) throws -> Any { - let encoder = __PlistEncoder(options: self.options) - guard let topLevel = try encoder.box_(value) else { - throw EncodingError.invalidValue(value, - EncodingError.Context(codingPath: [], - debugDescription: "Top-level \(Value.self) did not encode any values.")) - } - - return topLevel - } -} - -// MARK: - __PlistEncoder - -// NOTE: older overlays called this class _PlistEncoder. The two must -// coexist without a conflicting ObjC class name, so it was renamed. -// The old name must not be used in the new runtime. -fileprivate class __PlistEncoder : Encoder { - // MARK: Properties - - /// The encoder's storage. - fileprivate var storage: _PlistEncodingStorage - - /// Options set on the top-level encoder. - fileprivate let options: PropertyListEncoder._Options - - /// The path to the current point in encoding. - fileprivate(set) public var codingPath: [CodingKey] - - /// Contextual user-provided information for use during encoding. - public var userInfo: [CodingUserInfoKey : Any] { - return self.options.userInfo - } - - // MARK: - Initialization - - /// Initializes `self` with the given top-level encoder options. - fileprivate init(options: PropertyListEncoder._Options, codingPath: [CodingKey] = []) { - self.options = options - self.storage = _PlistEncodingStorage() - self.codingPath = codingPath - } - - /// Returns whether a new element can be encoded at this coding path. - /// - /// `true` if an element has not yet been encoded at this coding path; `false` otherwise. - fileprivate var canEncodeNewValue: Bool { - // Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container). - // At the same time, every time a container is requested, a new value gets pushed onto the storage stack. - // If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition. - // - // This means that anytime something that can request a new container goes onto the stack, we MUST push a key onto the coding path. - // Things which will not request containers do not need to have the coding path extended for them (but it doesn't matter if it is, because they will not reach here). - return self.storage.count == self.codingPath.count - } - - // MARK: - Encoder Methods - public func container(keyedBy: Key.Type) -> KeyedEncodingContainer { - // If an existing keyed container was already requested, return that one. - let topContainer: NSMutableDictionary - if self.canEncodeNewValue { - // We haven't yet pushed a container at this level; do so here. - topContainer = self.storage.pushKeyedContainer() - } else { - guard let container = self.storage.containers.last as? NSMutableDictionary else { - preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.") - } - - topContainer = container - } - - let container = _PlistKeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer) - return KeyedEncodingContainer(container) - } - - public func unkeyedContainer() -> UnkeyedEncodingContainer { - // If an existing unkeyed container was already requested, return that one. - let topContainer: NSMutableArray - if self.canEncodeNewValue { - // We haven't yet pushed a container at this level; do so here. - topContainer = self.storage.pushUnkeyedContainer() - } else { - guard let container = self.storage.containers.last as? NSMutableArray else { - preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.") - } - - topContainer = container - } - - return _PlistUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer) - } - - public func singleValueContainer() -> SingleValueEncodingContainer { - return self - } -} - -// MARK: - Encoding Storage and Containers - -fileprivate struct _PlistEncodingStorage { - // MARK: Properties - - /// The container stack. - /// Elements may be any one of the plist types (NSNumber, NSString, NSDate, NSArray, NSDictionary). - private(set) fileprivate var containers: [NSObject] = [] - - // MARK: - Initialization - - /// Initializes `self` with no containers. - fileprivate init() {} - - // MARK: - Modifying the Stack - - fileprivate var count: Int { - return self.containers.count - } - - fileprivate mutating func pushKeyedContainer() -> NSMutableDictionary { - let dictionary = NSMutableDictionary() - self.containers.append(dictionary) - return dictionary - } - - fileprivate mutating func pushUnkeyedContainer() -> NSMutableArray { - let array = NSMutableArray() - self.containers.append(array) - return array - } - - fileprivate mutating func push(container: __owned NSObject) { - self.containers.append(container) - } - - fileprivate mutating func popContainer() -> NSObject { - precondition(!self.containers.isEmpty, "Empty container stack.") - return self.containers.popLast()! - } -} - -// MARK: - Encoding Containers - -fileprivate struct _PlistKeyedEncodingContainer : KeyedEncodingContainerProtocol { - typealias Key = K - - // MARK: Properties - - /// A reference to the encoder we're writing to. - private let encoder: __PlistEncoder - - /// A reference to the container we're writing to. - private let container: NSMutableDictionary - - /// The path of coding keys taken to get to this point in encoding. - private(set) public var codingPath: [CodingKey] - - // MARK: - Initialization - - /// Initializes `self` with the given references. - fileprivate init(referencing encoder: __PlistEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) { - self.encoder = encoder - self.codingPath = codingPath - self.container = container - } - - // MARK: - KeyedEncodingContainerProtocol Methods - - public mutating func encodeNil(forKey key: Key) throws { self.container[key.stringValue] = _plistNullNSString } - public mutating func encode(_ value: Bool, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int8, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int16, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int32, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int64, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt8, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt16, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt32, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt64, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: String, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Float, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Double, forKey key: Key) throws { self.container[key.stringValue] = self.encoder.box(value) } - - public mutating func encode(_ value: T, forKey key: Key) throws { - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - self.container[key.stringValue] = try self.encoder.box(value) - } - - public mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { - let containerKey = key.stringValue - let dictionary: NSMutableDictionary - if let existingContainer = self.container[containerKey] { - precondition( - existingContainer is NSMutableDictionary, - "Attempt to re-encode into nested KeyedEncodingContainer<\(Key.self)> for key \"\(containerKey)\" is invalid: non-keyed container already encoded for this key" - ) - dictionary = existingContainer as! NSMutableDictionary - } else { - dictionary = NSMutableDictionary() - self.container[containerKey] = dictionary - } - - self.codingPath.append(key) - defer { self.codingPath.removeLast() } - - let container = _PlistKeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary) - return KeyedEncodingContainer(container) - } - - public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - let containerKey = key.stringValue - let array: NSMutableArray - if let existingContainer = self.container[containerKey] { - precondition( - existingContainer is NSMutableArray, - "Attempt to re-encode into nested UnkeyedEncodingContainer for key \"\(containerKey)\" is invalid: keyed container/single value already encoded for this key" - ) - array = existingContainer as! NSMutableArray - } else { - array = NSMutableArray() - self.container[containerKey] = array - } - - self.codingPath.append(key) - defer { self.codingPath.removeLast() } - return _PlistUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array) - } - - public mutating func superEncoder() -> Encoder { - return __PlistReferencingEncoder(referencing: self.encoder, at: _PlistKey.super, wrapping: self.container) - } - - public mutating func superEncoder(forKey key: Key) -> Encoder { - return __PlistReferencingEncoder(referencing: self.encoder, at: key, wrapping: self.container) - } -} - -fileprivate struct _PlistUnkeyedEncodingContainer : UnkeyedEncodingContainer { - // MARK: Properties - - /// A reference to the encoder we're writing to. - private let encoder: __PlistEncoder - - /// A reference to the container we're writing to. - private let container: NSMutableArray - - /// The path of coding keys taken to get to this point in encoding. - private(set) public var codingPath: [CodingKey] - - /// The number of elements encoded into the container. - public var count: Int { - return self.container.count - } - - // MARK: - Initialization - - /// Initializes `self` with the given references. - fileprivate init(referencing encoder: __PlistEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) { - self.encoder = encoder - self.codingPath = codingPath - self.container = container - } - - // MARK: - UnkeyedEncodingContainer Methods - - public mutating func encodeNil() throws { self.container.add(_plistNullNSString) } - public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Float) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Double) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) } - - public mutating func encode(_ value: T) throws { - self.encoder.codingPath.append(_PlistKey(index: self.count)) - defer { self.encoder.codingPath.removeLast() } - self.container.add(try self.encoder.box(value)) - } - - public mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer { - self.codingPath.append(_PlistKey(index: self.count)) - defer { self.codingPath.removeLast() } - - let dictionary = NSMutableDictionary() - self.container.add(dictionary) - - let container = _PlistKeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary) - return KeyedEncodingContainer(container) - } - - public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - self.codingPath.append(_PlistKey(index: self.count)) - defer { self.codingPath.removeLast() } - - let array = NSMutableArray() - self.container.add(array) - return _PlistUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array) - } - - public mutating func superEncoder() -> Encoder { - return __PlistReferencingEncoder(referencing: self.encoder, at: self.container.count, wrapping: self.container) - } -} - -extension __PlistEncoder : SingleValueEncodingContainer { - // MARK: - SingleValueEncodingContainer Methods - - private func assertCanEncodeNewValue() { - precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.") - } - - public func encodeNil() throws { - assertCanEncodeNewValue() - self.storage.push(container: _plistNullNSString) - } - - public func encode(_ value: Bool) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int8) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int16) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int32) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Int64) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt8) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt16) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt32) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: UInt64) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: String) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Float) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: Double) throws { - assertCanEncodeNewValue() - self.storage.push(container: self.box(value)) - } - - public func encode(_ value: T) throws { - assertCanEncodeNewValue() - try self.storage.push(container: self.box(value)) - } -} - -// MARK: - Concrete Value Representations - -extension __PlistEncoder { - - /// Returns the given value boxed in a container appropriate for pushing onto the container stack. - fileprivate func box(_ value: Bool) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int8) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int16) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int32) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int64) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Float) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Double) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: String) -> NSObject { return NSString(string: value) } - - fileprivate func box(_ value: T) throws -> NSObject { - return try self.box_(value) ?? NSDictionary() - } - - fileprivate func box_(_ value: T) throws -> NSObject? { - if T.self == Date.self || T.self == NSDate.self { - // PropertyListSerialization handles NSDate directly. - return (value as! NSDate) - } else if T.self == Data.self || T.self == NSData.self { - // PropertyListSerialization handles NSData directly. - return (value as! NSData) - } - - // The value should request a container from the __PlistEncoder. - let depth = self.storage.count - do { - try value.encode(to: self) - } catch let error { - // If the value pushed a container before throwing, pop it back off to restore state. - if self.storage.count > depth { - let _ = self.storage.popContainer() - } - - throw error - } - - // The top container should be a new container. - guard self.storage.count > depth else { - return nil - } - - return self.storage.popContainer() - } -} - -// MARK: - __PlistReferencingEncoder - -/// __PlistReferencingEncoder is a special subclass of __PlistEncoder which has its own storage, but references the contents of a different encoder. -/// It's used in superEncoder(), which returns a new encoder for encoding a superclass -- the lifetime of the encoder should not escape the scope it's created in, but it doesn't necessarily know when it's done being used (to write to the original container). -// NOTE: older overlays called this class _PlistReferencingEncoder. -// The two must coexist without a conflicting ObjC class name, so it -// was renamed. The old name must not be used in the new runtime. -fileprivate class __PlistReferencingEncoder : __PlistEncoder { - // MARK: Reference types. - - /// The type of container we're referencing. - private enum Reference { - /// Referencing a specific index in an array container. - case array(NSMutableArray, Int) - - /// Referencing a specific key in a dictionary container. - case dictionary(NSMutableDictionary, String) - } - - // MARK: - Properties - - /// The encoder we're referencing. - private let encoder: __PlistEncoder - - /// The container reference itself. - private let reference: Reference - - // MARK: - Initialization - - /// Initializes `self` by referencing the given array container in the given encoder. - fileprivate init(referencing encoder: __PlistEncoder, at index: Int, wrapping array: NSMutableArray) { - self.encoder = encoder - self.reference = .array(array, index) - super.init(options: encoder.options, codingPath: encoder.codingPath) - - self.codingPath.append(_PlistKey(index: index)) - } - - /// Initializes `self` by referencing the given dictionary container in the given encoder. - fileprivate init(referencing encoder: __PlistEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) { - self.encoder = encoder - self.reference = .dictionary(dictionary, key.stringValue) - super.init(options: encoder.options, codingPath: encoder.codingPath) - - self.codingPath.append(key) - } - - // MARK: - Coding Path Operations - - fileprivate override var canEncodeNewValue: Bool { - // With a regular encoder, the storage and coding path grow together. - // A referencing encoder, however, inherits its parents coding path, as well as the key it was created for. - // We have to take this into account. - return self.storage.count == self.codingPath.count - self.encoder.codingPath.count - 1 - } - - // MARK: - Deinitialization - - // Finalizes `self` by writing the contents of our storage to the referenced encoder's storage. - deinit { - let value: Any - switch self.storage.count { - case 0: value = NSDictionary() - case 1: value = self.storage.popContainer() - default: fatalError("Referencing encoder deallocated with multiple containers on stack.") - } - - switch self.reference { - case .array(let array, let index): - array.insert(value, at: index) - - case .dictionary(let dictionary, let key): - dictionary[NSString(string: key)] = value - } - } -} - -//===----------------------------------------------------------------------===// -// Plist Decoder -//===----------------------------------------------------------------------===// - -/// `PropertyListDecoder` facilitates the decoding of property list values into semantic `Decodable` types. -// NOTE: older overlays had Foundation.PropertyListDecoder as the ObjC -// name. The two must coexist, so it was renamed. The old name must not -// be used in the new runtime. _TtC10Foundation20_PropertyListDecoder -// is the mangled name for Foundation._PropertyListDecoder. -@_objcRuntimeName(_TtC10Foundation20_PropertyListDecoder) -open class PropertyListDecoder { - // MARK: Options - - /// Contextual user-provided information for use during decoding. - open var userInfo: [CodingUserInfoKey : Any] = [:] - - /// Options set on the top-level encoder to pass down the decoding hierarchy. - fileprivate struct _Options { - let userInfo: [CodingUserInfoKey : Any] - } - - /// The options set on the top-level decoder. - fileprivate var options: _Options { - return _Options(userInfo: userInfo) - } - - // MARK: - Constructing a Property List Decoder - - /// Initializes `self` with default strategies. - public init() {} - - // MARK: - Decoding Values - - /// Decodes a top-level value of the given type from the given property list representation. - /// - /// - parameter type: The type of the value to decode. - /// - parameter data: The data to decode from. - /// - returns: A value of the requested type. - /// - throws: `DecodingError.dataCorrupted` if values requested from the payload are corrupted, or if the given data is not a valid property list. - /// - throws: An error if any value throws an error during decoding. - open func decode(_ type: T.Type, from data: Data) throws -> T { - var format: PropertyListSerialization.PropertyListFormat = .binary - return try decode(type, from: data, format: &format) - } - - /// Decodes a top-level value of the given type from the given property list representation. - /// - /// - parameter type: The type of the value to decode. - /// - parameter data: The data to decode from. - /// - parameter format: The parsed property list format. - /// - returns: A value of the requested type along with the detected format of the property list. - /// - throws: `DecodingError.dataCorrupted` if values requested from the payload are corrupted, or if the given data is not a valid property list. - /// - throws: An error if any value throws an error during decoding. - open func decode(_ type: T.Type, from data: Data, format: inout PropertyListSerialization.PropertyListFormat) throws -> T { - let topLevel: Any - do { - topLevel = try PropertyListSerialization.propertyList(from: data, options: [], format: &format) - } catch { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "The given data was not a valid property list.", underlyingError: error)) - } - - return try decode(type, fromTopLevel: topLevel) - } - - /// Decodes a top-level value of the given type from the given property list container (top-level array or dictionary). - /// - /// - parameter type: The type of the value to decode. - /// - parameter container: The top-level plist container. - /// - returns: A value of the requested type. - /// - throws: `DecodingError.dataCorrupted` if values requested from the payload are corrupted, or if the given data is not a valid property list. - /// - throws: An error if any value throws an error during decoding. - internal func decode(_ type: T.Type, fromTopLevel container: Any) throws -> T { - let decoder = __PlistDecoder(referencing: container, options: self.options) - guard let value = try decoder.unbox(container, as: type) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: [], debugDescription: "The given data did not contain a top-level value.")) - } - - return value - } -} - -// MARK: - __PlistDecoder - -// NOTE: older overlays called this class _PlistDecoder. The two must -// coexist without a conflicting ObjC class name, so it was renamed. -// The old name must not be used in the new runtime. -fileprivate class __PlistDecoder : Decoder { - // MARK: Properties - - /// The decoder's storage. - fileprivate var storage: _PlistDecodingStorage - - /// Options set on the top-level decoder. - fileprivate let options: PropertyListDecoder._Options - - /// The path to the current point in encoding. - fileprivate(set) public var codingPath: [CodingKey] - - /// Contextual user-provided information for use during encoding. - public var userInfo: [CodingUserInfoKey : Any] { - return self.options.userInfo - } - - // MARK: - Initialization - - /// Initializes `self` with the given top-level container and options. - fileprivate init(referencing container: Any, at codingPath: [CodingKey] = [], options: PropertyListDecoder._Options) { - self.storage = _PlistDecodingStorage() - self.storage.push(container: container) - self.codingPath = codingPath - self.options = options - } - - // MARK: - Decoder Methods - - public func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer { - guard !(self.storage.topContainer is NSNull) else { - throw DecodingError.valueNotFound(KeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get keyed decoding container -- found null value instead.")) - } - - guard let topContainer = self.storage.topContainer as? [String : Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: self.storage.topContainer) - } - - let container = _PlistKeyedDecodingContainer(referencing: self, wrapping: topContainer) - return KeyedDecodingContainer(container) - } - - public func unkeyedContainer() throws -> UnkeyedDecodingContainer { - guard !(self.storage.topContainer is NSNull) else { - throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get unkeyed decoding container -- found null value instead.")) - } - - guard let topContainer = self.storage.topContainer as? [Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: self.storage.topContainer) - } - - return _PlistUnkeyedDecodingContainer(referencing: self, wrapping: topContainer) - } - - public func singleValueContainer() throws -> SingleValueDecodingContainer { - return self - } -} - -// MARK: - Decoding Storage - -fileprivate struct _PlistDecodingStorage { - // MARK: Properties - - /// The container stack. - /// Elements may be any one of the plist types (NSNumber, Date, String, Array, [String : Any]). - private(set) fileprivate var containers: [Any] = [] - - // MARK: - Initialization - - /// Initializes `self` with no containers. - fileprivate init() {} - - // MARK: - Modifying the Stack - - fileprivate var count: Int { - return self.containers.count - } - - fileprivate var topContainer: Any { - precondition(!self.containers.isEmpty, "Empty container stack.") - return self.containers.last! - } - - fileprivate mutating func push(container: __owned Any) { - self.containers.append(container) - } - - fileprivate mutating func popContainer() { - precondition(!self.containers.isEmpty, "Empty container stack.") - self.containers.removeLast() - } -} - -// MARK: Decoding Containers - -fileprivate struct _PlistKeyedDecodingContainer : KeyedDecodingContainerProtocol { - typealias Key = K - - // MARK: Properties - - /// A reference to the decoder we're reading from. - private let decoder: __PlistDecoder - - /// A reference to the container we're reading from. - private let container: [String : Any] - - /// The path of coding keys taken to get to this point in decoding. - private(set) public var codingPath: [CodingKey] - - // MARK: - Initialization - - /// Initializes `self` by referencing the given decoder and container. - fileprivate init(referencing decoder: __PlistDecoder, wrapping container: [String : Any]) { - self.decoder = decoder - self.container = container - self.codingPath = decoder.codingPath - } - - // MARK: - KeyedDecodingContainerProtocol Methods - - public var allKeys: [Key] { - return self.container.keys.compactMap { Key(stringValue: $0) } - } - - public func contains(_ key: Key) -> Bool { - return self.container[key.stringValue] != nil - } - - public func decodeNil(forKey key: Key) throws -> Bool { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - guard let value = entry as? String else { - return false - } - - return value == _plistNull - } - - public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Bool.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int.Type, forKey key: Key) throws -> Int { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int8.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int16.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int32.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Int64.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt8.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt16.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt32.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: UInt64.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Float.Type, forKey key: Key) throws -> Float { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - guard let value = try self.decoder.unbox(entry, as: Float.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: Double.Type, forKey key: Key) throws -> Double { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: Double.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: String.Type, forKey key: Key) throws -> String { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: String.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func decode(_ type: T.Type, forKey key: Key) throws -> T { - guard let entry = self.container[key.stringValue] else { - throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")) - } - - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = try self.decoder.unbox(entry, as: type) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead.")) - } - - return value - } - - public func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer { - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = self.container[key.stringValue] else { - throw DecodingError.valueNotFound(KeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get nested keyed container -- no value found for key \"\(key.stringValue)\"")) - } - - guard let dictionary = value as? [String : Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value) - } - - let container = _PlistKeyedDecodingContainer(referencing: self.decoder, wrapping: dictionary) - return KeyedDecodingContainer(container) - } - - public func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - guard let value = self.container[key.stringValue] else { - throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get nested unkeyed container -- no value found for key \"\(key.stringValue)\"")) - } - - guard let array = value as? [Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: value) - } - - return _PlistUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array) - } - - private func _superDecoder(forKey key: __owned CodingKey) throws -> Decoder { - self.decoder.codingPath.append(key) - defer { self.decoder.codingPath.removeLast() } - - let value: Any = self.container[key.stringValue] ?? NSNull() - return __PlistDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options) - } - - public func superDecoder() throws -> Decoder { - return try _superDecoder(forKey: _PlistKey.super) - } - - public func superDecoder(forKey key: Key) throws -> Decoder { - return try _superDecoder(forKey: key) - } -} - -fileprivate struct _PlistUnkeyedDecodingContainer : UnkeyedDecodingContainer { - // MARK: Properties - - /// A reference to the decoder we're reading from. - private let decoder: __PlistDecoder - - /// A reference to the container we're reading from. - private let container: [Any] - - /// The path of coding keys taken to get to this point in decoding. - private(set) public var codingPath: [CodingKey] - - /// The index of the element we're about to decode. - private(set) public var currentIndex: Int - - // MARK: - Initialization - - /// Initializes `self` by referencing the given decoder and container. - fileprivate init(referencing decoder: __PlistDecoder, wrapping container: [Any]) { - self.decoder = decoder - self.container = container - self.codingPath = decoder.codingPath - self.currentIndex = 0 - } - - // MARK: - UnkeyedDecodingContainer Methods - - public var count: Int? { - return self.container.count - } - - public var isAtEnd: Bool { - return self.currentIndex >= self.count! - } - - public mutating func decodeNil() throws -> Bool { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(Any?.self, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - if self.container[self.currentIndex] is NSNull { - self.currentIndex += 1 - return true - } else { - return false - } - } - - public mutating func decode(_ type: Bool.Type) throws -> Bool { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Bool.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int.Type) throws -> Int { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int8.Type) throws -> Int8 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int8.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int16.Type) throws -> Int16 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int16.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int32.Type) throws -> Int32 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int32.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Int64.Type) throws -> Int64 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int64.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt.Type) throws -> UInt { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt8.Type) throws -> UInt8 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt8.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt16.Type) throws -> UInt16 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt16.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt32.Type) throws -> UInt32 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt32.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: UInt64.Type) throws -> UInt64 { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt64.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Float.Type) throws -> Float { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Float.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: Double.Type) throws -> Double { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Double.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: String.Type) throws -> String { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: String.self) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func decode(_ type: T.Type) throws -> T { - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end.")) - } - - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: type) else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_PlistKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead.")) - } - - self.currentIndex += 1 - return decoded - } - - public mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer { - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(KeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get nested keyed container -- unkeyed container is at end.")) - } - - let value = self.container[self.currentIndex] - guard !(value is NSNull) else { - throw DecodingError.valueNotFound(KeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get keyed decoding container -- found null value instead.")) - } - - guard let dictionary = value as? [String : Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value) - } - - self.currentIndex += 1 - let container = _PlistKeyedDecodingContainer(referencing: self.decoder, wrapping: dictionary) - return KeyedDecodingContainer(container) - } - - public mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get nested unkeyed container -- unkeyed container is at end.")) - } - - let value = self.container[self.currentIndex] - guard !(value is NSNull) else { - throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, - DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get keyed decoding container -- found null value instead.")) - } - - guard let array = value as? [Any] else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: value) - } - - self.currentIndex += 1 - return _PlistUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array) - } - - public mutating func superDecoder() throws -> Decoder { - self.decoder.codingPath.append(_PlistKey(index: self.currentIndex)) - defer { self.decoder.codingPath.removeLast() } - - guard !self.isAtEnd else { - throw DecodingError.valueNotFound(Decoder.self, DecodingError.Context(codingPath: self.codingPath, - debugDescription: "Cannot get superDecoder() -- unkeyed container is at end.")) - } - - let value = self.container[self.currentIndex] - self.currentIndex += 1 - return __PlistDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options) - } -} - -extension __PlistDecoder : SingleValueDecodingContainer { - // MARK: SingleValueDecodingContainer Methods - - private func expectNonNull(_ type: T.Type) throws { - guard !self.decodeNil() else { - throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected \(type) but found null value instead.")) - } - } - - public func decodeNil() -> Bool { - guard let string = self.storage.topContainer as? String else { - return false - } - - return string == _plistNull - } - - public func decode(_ type: Bool.Type) throws -> Bool { - try expectNonNull(Bool.self) - return try self.unbox(self.storage.topContainer, as: Bool.self)! - } - - public func decode(_ type: Int.Type) throws -> Int { - try expectNonNull(Int.self) - return try self.unbox(self.storage.topContainer, as: Int.self)! - } - - public func decode(_ type: Int8.Type) throws -> Int8 { - try expectNonNull(Int8.self) - return try self.unbox(self.storage.topContainer, as: Int8.self)! - } - - public func decode(_ type: Int16.Type) throws -> Int16 { - try expectNonNull(Int16.self) - return try self.unbox(self.storage.topContainer, as: Int16.self)! - } - - public func decode(_ type: Int32.Type) throws -> Int32 { - try expectNonNull(Int32.self) - return try self.unbox(self.storage.topContainer, as: Int32.self)! - } - - public func decode(_ type: Int64.Type) throws -> Int64 { - try expectNonNull(Int64.self) - return try self.unbox(self.storage.topContainer, as: Int64.self)! - } - - public func decode(_ type: UInt.Type) throws -> UInt { - try expectNonNull(UInt.self) - return try self.unbox(self.storage.topContainer, as: UInt.self)! - } - - public func decode(_ type: UInt8.Type) throws -> UInt8 { - try expectNonNull(UInt8.self) - return try self.unbox(self.storage.topContainer, as: UInt8.self)! - } - - public func decode(_ type: UInt16.Type) throws -> UInt16 { - try expectNonNull(UInt16.self) - return try self.unbox(self.storage.topContainer, as: UInt16.self)! - } - - public func decode(_ type: UInt32.Type) throws -> UInt32 { - try expectNonNull(UInt32.self) - return try self.unbox(self.storage.topContainer, as: UInt32.self)! - } - - public func decode(_ type: UInt64.Type) throws -> UInt64 { - try expectNonNull(UInt64.self) - return try self.unbox(self.storage.topContainer, as: UInt64.self)! - } - - public func decode(_ type: Float.Type) throws -> Float { - try expectNonNull(Float.self) - return try self.unbox(self.storage.topContainer, as: Float.self)! - } - - public func decode(_ type: Double.Type) throws -> Double { - try expectNonNull(Double.self) - return try self.unbox(self.storage.topContainer, as: Double.self)! - } - - public func decode(_ type: String.Type) throws -> String { - try expectNonNull(String.self) - return try self.unbox(self.storage.topContainer, as: String.self)! - } - - public func decode(_ type: T.Type) throws -> T { - try expectNonNull(type) - return try self.unbox(self.storage.topContainer, as: type)! - } -} - -// MARK: - Concrete Value Representations - -extension __PlistDecoder { - /// Returns the given value unboxed from a container. - fileprivate func unbox(_ value: Any, as type: Bool.Type) throws -> Bool? { - if let string = value as? String, string == _plistNull { return nil } - - if let number = value as? NSNumber { - // TODO: Add a flag to coerce non-boolean numbers into Bools? - if number === kCFBooleanTrue as NSNumber { - return true - } else if number === kCFBooleanFalse as NSNumber { - return false - } - - /* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested: - } else if let bool = value as? Bool { - return bool - */ - - } - - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - fileprivate func unbox(_ value: Any, as type: Int.Type) throws -> Int? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int = number.intValue - guard NSNumber(value: int) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return int - } - - fileprivate func unbox(_ value: Any, as type: Int8.Type) throws -> Int8? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int8 = number.int8Value - guard NSNumber(value: int8) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return int8 - } - - fileprivate func unbox(_ value: Any, as type: Int16.Type) throws -> Int16? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int16 = number.int16Value - guard NSNumber(value: int16) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return int16 - } - - fileprivate func unbox(_ value: Any, as type: Int32.Type) throws -> Int32? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int32 = number.int32Value - guard NSNumber(value: int32) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return int32 - } - - fileprivate func unbox(_ value: Any, as type: Int64.Type) throws -> Int64? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let int64 = number.int64Value - guard NSNumber(value: int64) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return int64 - } - - fileprivate func unbox(_ value: Any, as type: UInt.Type) throws -> UInt? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint = number.uintValue - guard NSNumber(value: uint) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return uint - } - - fileprivate func unbox(_ value: Any, as type: UInt8.Type) throws -> UInt8? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint8 = number.uint8Value - guard NSNumber(value: uint8) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return uint8 - } - - fileprivate func unbox(_ value: Any, as type: UInt16.Type) throws -> UInt16? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint16 = number.uint16Value - guard NSNumber(value: uint16) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return uint16 - } - - fileprivate func unbox(_ value: Any, as type: UInt32.Type) throws -> UInt32? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint32 = number.uint32Value - guard NSNumber(value: uint32) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return uint32 - } - - fileprivate func unbox(_ value: Any, as type: UInt64.Type) throws -> UInt64? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let uint64 = number.uint64Value - guard NSNumber(value: uint64) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return uint64 - } - - fileprivate func unbox(_ value: Any, as type: Float.Type) throws -> Float? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let float = number.floatValue - guard NSNumber(value: float) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return float - } - - fileprivate func unbox(_ value: Any, as type: Double.Type) throws -> Double? { - if let string = value as? String, string == _plistNull { return nil } - - guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - let double = number.doubleValue - guard NSNumber(value: double) == number else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed property list number <\(number)> does not fit in \(type).")) - } - - return double - } - - fileprivate func unbox(_ value: Any, as type: String.Type) throws -> String? { - guard let string = value as? String else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - return string == _plistNull ? nil : string - } - - fileprivate func unbox(_ value: Any, as type: Date.Type) throws -> Date? { - if let string = value as? String, string == _plistNull { return nil } - - guard let date = value as? Date else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - return date - } - - fileprivate func unbox(_ value: Any, as type: Data.Type) throws -> Data? { - if let string = value as? String, string == _plistNull { return nil } - - guard let data = value as? Data else { - throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value) - } - - return data - } - - fileprivate func unbox(_ value: Any, as type: T.Type) throws -> T? { - if type == Date.self || type == NSDate.self { - return try self.unbox(value, as: Date.self) as? T - } else if type == Data.self || type == NSData.self { - return try self.unbox(value, as: Data.self) as? T - } else { - self.storage.push(container: value) - defer { self.storage.popContainer() } - return try type.init(from: self) - } - } -} - -//===----------------------------------------------------------------------===// -// Shared Plist Null Representation -//===----------------------------------------------------------------------===// - -// Since plists do not support null values by default, we will encode them as "$null". -fileprivate let _plistNull = "$null" -fileprivate let _plistNullNSString = NSString(string: _plistNull) - -//===----------------------------------------------------------------------===// -// Shared Key Types -//===----------------------------------------------------------------------===// - -fileprivate struct _PlistKey : CodingKey { - public var stringValue: String - public var intValue: Int? - - public init?(stringValue: String) { - self.stringValue = stringValue - self.intValue = nil - } - - public init?(intValue: Int) { - self.stringValue = "\(intValue)" - self.intValue = intValue - } - - fileprivate init(index: Int) { - self.stringValue = "Index \(index)" - self.intValue = index - } - - fileprivate static let `super` = _PlistKey(stringValue: "super")! -} diff --git a/stdlib/public/Darwin/Foundation/Progress.swift b/stdlib/public/Darwin/Foundation/Progress.swift deleted file mode 100644 index 6164d119e68b2..0000000000000 --- a/stdlib/public/Darwin/Foundation/Progress.swift +++ /dev/null @@ -1,85 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -extension Progress { - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public var estimatedTimeRemaining: TimeInterval? { - get { - guard let v = self.__estimatedTimeRemaining else { return nil } - return v.doubleValue - } - set { - guard let nv = newValue else { - self.__estimatedTimeRemaining = nil - return - } - let v = NSNumber(value: nv) - self.__estimatedTimeRemaining = v - } - } - - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public var throughput: Int? { - get { - guard let v = self.__throughput else { return nil } - return v.intValue - } - set { - guard let nv = newValue else { - self.__throughput = nil - return - } - let v = NSNumber(value: nv) - self.__throughput = v - } - } - - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public var fileTotalCount: Int? { - get { - guard let v = self.__fileTotalCount else { return nil } - return v.intValue - } - set { - guard let nv = newValue else { - self.__fileTotalCount = nil - return - } - let v = NSNumber(value: nv) - self.__fileTotalCount = v - } - } - - @available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public var fileCompletedCount: Int? { - get { - guard let v = self.__fileCompletedCount else { return nil } - return v.intValue - } - set { - guard let nv = newValue else { - self.__fileCompletedCount = nil - return - } - let v = NSNumber(value: nv) - self.__fileCompletedCount = v - } - } - - public func performAsCurrent(withPendingUnitCount unitCount: Int64, using work: () throws -> ReturnType) rethrows -> ReturnType { - becomeCurrent(withPendingUnitCount: unitCount) - defer { resignCurrent() } - return try work() - } -} diff --git a/stdlib/public/Darwin/Foundation/Publishers+KeyValueObserving.swift b/stdlib/public/Darwin/Foundation/Publishers+KeyValueObserving.swift deleted file mode 100644 index df9271fcf2350..0000000000000 --- a/stdlib/public/Darwin/Foundation/Publishers+KeyValueObserving.swift +++ /dev/null @@ -1,210 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -@_exported import Foundation // Clang module -import Combine - -// The following protocol is so that we can reference `Self` in the Publisher -// below. This is based on a trick used in the the standard library's -// implementation of `NSObject.observe(key path)` -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -public protocol _KeyValueCodingAndObservingPublishing {} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension NSObject: _KeyValueCodingAndObservingPublishing {} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension _KeyValueCodingAndObservingPublishing where Self: NSObject { - /// Publish values when the value identified by a KVO-compliant keypath changes. - /// - /// - Parameters: - /// - keyPath: The keypath of the property to publish. - /// - options: Key-value observing options. - /// - Returns: A publisher that emits elements each time the property’s value changes. - public func publisher(for keyPath: KeyPath, - options: NSKeyValueObservingOptions = [.initial, .new]) - -> NSObject.KeyValueObservingPublisher { - return NSObject.KeyValueObservingPublisher(object: self, keyPath: keyPath, options: options) - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension NSObject.KeyValueObservingPublisher { - /// Returns a publisher that emits values when a KVO-compliant property changes. - /// - /// - Returns: A key-value observing publisher. - public func didChange() - -> Publishers.Map, Void> { - return map { _ in () } - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension NSObject { - /// A publisher that emits events when the value of a KVO-compliant property changes. - public struct KeyValueObservingPublisher : Equatable { - public let object: Subject - public let keyPath: KeyPath - public let options: NSKeyValueObservingOptions - - public init( - object: Subject, - keyPath: KeyPath, - options: NSKeyValueObservingOptions - ) { - self.object = object - self.keyPath = keyPath - self.options = options - } - - public static func == ( - lhs: KeyValueObservingPublisher, - rhs: KeyValueObservingPublisher - ) -> Bool { - return lhs.object === rhs.object - && lhs.keyPath == rhs.keyPath - && lhs.options == rhs.options - } - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension NSObject.KeyValueObservingPublisher: Publisher { - public typealias Output = Value - public typealias Failure = Never - - public func receive(subscriber: S) where S.Input == Output, S.Failure == Failure { - let s = NSObject.KVOSubscription(object, keyPath, options, subscriber) - subscriber.receive(subscription: s) - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension NSObject { - private final class KVOSubscription: Subscription, CustomStringConvertible, CustomReflectable, CustomPlaygroundDisplayConvertible { - private var observation: NSKeyValueObservation? // GuardedBy(lock) - private var demand: Subscribers.Demand // GuardedBy(lock) - - // for configurations that care about '.initial' we need to 'cache' the value to account for backpressure, along with whom to send it to - // - // TODO: in the future we might want to consider interjecting a temporary publisher that does this, so that all KVO subscriptions don't incur the cost. - private var receivedInitial: Bool // GuardedBy(lock) - private var last: Value? // GuardedBy(lock) - private var subscriber: AnySubscriber? // GuardedBy(lock) - - private let lock = Lock() - - // This lock can only be held for the duration of downstream callouts - private let downstreamLock = RecursiveLock() - - var description: String { return "KVOSubscription" } - var customMirror: Mirror { - lock.lock() - defer { lock.unlock() } - return Mirror(self, children: [ - "observation": observation as Any, - "demand": demand - ]) - } - var playgroundDescription: Any { return description } - - init( - _ object: Subject, - _ keyPath: KeyPath, - _ options: NSKeyValueObservingOptions, - _ subscriber: S) - where - S.Input == Value, - S.Failure == Never - { - demand = .max(0) - receivedInitial = false - self.subscriber = AnySubscriber(subscriber) - - observation = object.observe( - keyPath, - options: options - ) { [weak self] obj, _ in - guard let self = self else { - return - } - let value = obj[keyPath: keyPath] - self.lock.lock() - if self.demand > 0, let sub = self.subscriber { - self.demand -= 1 - self.lock.unlock() - - self.downstreamLock.lock() - let additional = sub.receive(value) - self.downstreamLock.unlock() - - self.lock.lock() - self.demand += additional - self.lock.unlock() - } else { - // Drop the value, unless we've asked for .initial, and this - // is the first value. - if self.receivedInitial == false && options.contains(.initial) { - self.last = value - self.receivedInitial = true - } - self.lock.unlock() - } - } - } - - deinit { - lock.cleanupLock() - downstreamLock.cleanupLock() - } - - func request(_ d: Subscribers.Demand) { - lock.lock() - demand += d - if demand > 0, let v = last, let sub = subscriber { - demand -= 1 - last = nil - lock.unlock() - - downstreamLock.lock() - let additional = sub.receive(v) - downstreamLock.unlock() - - lock.lock() - demand += additional - } else { - demand -= 1 - last = nil - } - lock.unlock() - } - - func cancel() { - lock.lock() - guard let o = observation else { - lock.unlock() - return - } - lock.unlock() - - observation = nil - subscriber = nil - last = nil - o.invalidate() - } - } -} - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/Publishers+Locking.swift b/stdlib/public/Darwin/Foundation/Publishers+Locking.swift deleted file mode 100644 index 0279f9bdccd45..0000000000000 --- a/stdlib/public/Darwin/Foundation/Publishers+Locking.swift +++ /dev/null @@ -1,117 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -import Darwin - -@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) -extension UnsafeMutablePointer where Pointee == os_unfair_lock_s { - internal init() { - let l = UnsafeMutablePointer.allocate(capacity: 1) - l.initialize(to: os_unfair_lock()) - self = l - } - - internal func cleanupLock() { - deinitialize(count: 1) - deallocate() - } - - internal func lock() { - os_unfair_lock_lock(self) - } - - internal func tryLock() -> Bool { - let result = os_unfair_lock_trylock(self) - return result - } - - internal func unlock() { - os_unfair_lock_unlock(self) - } -} - -@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) -typealias Lock = os_unfair_lock_t - -#if canImport(DarwinPrivate) - -@_implementationOnly import DarwinPrivate - -@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *) -extension UnsafeMutablePointer where Pointee == os_unfair_recursive_lock_s { - internal init() { - let l = UnsafeMutablePointer.allocate(capacity: 1) - l.initialize(to: os_unfair_recursive_lock_s()) - self = l - } - - internal func cleanupLock() { - deinitialize(count: 1) - deallocate() - } - - internal func lock() { - os_unfair_recursive_lock_lock(self) - } - - internal func tryLock() -> Bool { - let result = os_unfair_recursive_lock_trylock(self) - return result - } - - internal func unlock() { - os_unfair_recursive_lock_unlock(self) - } -} - -@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *) -typealias RecursiveLock = os_unfair_recursive_lock_t - -#else - -// Kept in overlay since some builds may not have `DarwinPrivate` but we should have the availability the same -@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *) -internal struct RecursiveLock { - private let lockPtr: UnsafeMutablePointer - - internal init() { - lockPtr = UnsafeMutablePointer.allocate(capacity: 1) - var attr = pthread_mutexattr_t() - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) - pthread_mutex_init(lockPtr, &attr) - } - - internal func cleanupLock() { - pthread_mutex_destroy(lockPtr) - lockPtr.deinitialize(count: 1) - lockPtr.deallocate() - } - - internal func lock() { - pthread_mutex_lock(lockPtr) - } - - internal func tryLock() -> Bool { - return pthread_mutex_trylock(lockPtr) == 0 - } - - internal func unlock() { - pthread_mutex_unlock(lockPtr) - } -} - -#endif - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/Publishers+NotificationCenter.swift b/stdlib/public/Darwin/Foundation/Publishers+NotificationCenter.swift deleted file mode 100644 index 42847ee75b41e..0000000000000 --- a/stdlib/public/Darwin/Foundation/Publishers+NotificationCenter.swift +++ /dev/null @@ -1,183 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -@_exported import Foundation // Clang module -import Combine - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension NotificationCenter { - /// Returns a publisher that emits events when broadcasting notifications. - /// - /// - Parameters: - /// - name: The name of the notification to publish. - /// - object: The object posting the named notification. If `nil`, the publisher emits elements for any object producing a notification with the given name. - /// - Returns: A publisher that emits events when broadcasting notifications. - public func publisher( - for name: Notification.Name, - object: AnyObject? = nil - ) -> NotificationCenter.Publisher { - return NotificationCenter.Publisher(center: self, name: name, object: object) - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension NotificationCenter { - /// A publisher that emits elements when broadcasting notifications. - public struct Publisher: Combine.Publisher { - public typealias Output = Notification - public typealias Failure = Never - - /// The notification center this publisher uses as a source. - public let center: NotificationCenter - /// The name of notifications published by this publisher. - public let name: Notification.Name - /// The object posting the named notification. - public let object: AnyObject? - - /// Creates a publisher that emits events when broadcasting notifications. - /// - /// - Parameters: - /// - center: The notification center to publish notifications for. - /// - name: The name of the notification to publish. - /// - object: The object posting the named notification. If `nil`, the publisher emits elements for any object producing a notification with the given name. - public init(center: NotificationCenter, name: Notification.Name, object: AnyObject? = nil) { - self.center = center - self.name = name - self.object = object - } - - public func receive(subscriber: S) where S.Input == Output, S.Failure == Failure { - subscriber.receive(subscription: Notification.Subscription(center, name, object, subscriber)) - } - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension NotificationCenter.Publisher: Equatable { - public static func == ( - lhs: NotificationCenter.Publisher, - rhs: NotificationCenter.Publisher - ) -> Bool { - return lhs.center === rhs.center - && lhs.name == rhs.name - && lhs.object === rhs.object - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension Notification { - fileprivate final class Subscription: Combine.Subscription, CustomStringConvertible, CustomReflectable, CustomPlaygroundDisplayConvertible - where - S.Input == Notification - { - private let lock = Lock() - - // This lock can only be held for the duration of downstream callouts - private let downstreamLock = RecursiveLock() - - private var demand: Subscribers.Demand // GuardedBy(lock) - - private var center: NotificationCenter? // GuardedBy(lock) - private let name: Notification.Name // Stored only for debug info - private var object: AnyObject? // Stored only for debug info - private var observation: AnyObject? // GuardedBy(lock) - - var description: String { return "NotificationCenter Observer" } - var customMirror: Mirror { - lock.lock() - defer { lock.unlock() } - return Mirror(self, children: [ - "center": center as Any, - "name": name as Any, - "object": object as Any, - "demand": demand - ]) - } - var playgroundDescription: Any { return description } - - init(_ center: NotificationCenter, - _ name: Notification.Name, - _ object: AnyObject?, - _ next: S) - { - self.demand = .max(0) - self.center = center - self.name = name - self.object = object - - self.observation = center.addObserver( - forName: name, - object: object, - queue: nil - ) { [weak self] note in - guard let self = self else { return } - - self.lock.lock() - guard self.observation != nil else { - self.lock.unlock() - return - } - - let demand = self.demand - if demand > 0 { - self.demand -= 1 - } - self.lock.unlock() - - if demand > 0 { - self.downstreamLock.lock() - let additionalDemand = next.receive(note) - self.downstreamLock.unlock() - - if additionalDemand > 0 { - self.lock.lock() - self.demand += additionalDemand - self.lock.unlock() - } - } else { - // Drop it on the floor - } - } - } - - deinit { - lock.cleanupLock() - downstreamLock.cleanupLock() - } - - func request(_ d: Subscribers.Demand) { - lock.lock() - demand += d - lock.unlock() - } - - func cancel() { - lock.lock() - guard let center = self.center, - let observation = self.observation else { - lock.unlock() - return - } - self.center = nil - self.observation = nil - self.object = nil - lock.unlock() - - center.removeObserver(observation) - } - } -} - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/Publishers+Timer.swift b/stdlib/public/Darwin/Foundation/Publishers+Timer.swift deleted file mode 100644 index f4f42d95b948b..0000000000000 --- a/stdlib/public/Darwin/Foundation/Publishers+Timer.swift +++ /dev/null @@ -1,328 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -@_exported import Foundation // Clang module -import Combine - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension Timer { - /// Returns a publisher that repeatedly emits the current date on the given interval. - /// - /// - Parameters: - /// - interval: The time interval on which to publish events. For example, a value of `0.5` publishes an event approximately every half-second. - /// - tolerance: The allowed timing variance when emitting events. Defaults to `nil`, which allows any variance. - /// - runLoop: The run loop on which the timer runs. - /// - mode: The run loop mode in which to run the timer. - /// - options: Scheduler options passed to the timer. Defaults to `nil`. - /// - Returns: A publisher that repeatedly emits the current date on the given interval. - public static func publish( - every interval: TimeInterval, - tolerance: TimeInterval? = nil, - on runLoop: RunLoop, - in mode: RunLoop.Mode, - options: RunLoop.SchedulerOptions? = nil) - -> TimerPublisher - { - return TimerPublisher(interval: interval, runLoop: runLoop, mode: mode, options: options) - } - - /// A publisher that repeatedly emits the current date on a given interval. - public final class TimerPublisher: ConnectablePublisher { - public typealias Output = Date - public typealias Failure = Never - - public let interval: TimeInterval - public let tolerance: TimeInterval? - public let runLoop: RunLoop - public let mode: RunLoop.Mode - public let options: RunLoop.SchedulerOptions? - - private lazy var routingSubscription: RoutingSubscription = { - return RoutingSubscription(parent: self) - }() - - // Stores if a `.connect()` happened before subscription, internally readable for tests - internal var isConnected: Bool { - return routingSubscription.isConnected - } - - /// Creates a publisher that repeatedly emits the current date on the given interval. - /// - /// - Parameters: - /// - interval: The interval on which to publish events. - /// - tolerance: The allowed timing variance when emitting events. Defaults to `nil`, which allows any variance. - /// - runLoop: The run loop on which the timer runs. - /// - mode: The run loop mode in which to run the timer. - /// - options: Scheduler options passed to the timer. Defaults to `nil`. - public init(interval: TimeInterval, tolerance: TimeInterval? = nil, runLoop: RunLoop, mode: RunLoop.Mode, options: RunLoop.SchedulerOptions? = nil) { - self.interval = interval - self.tolerance = tolerance - self.runLoop = runLoop - self.mode = mode - self.options = options - } - - /// Adapter subscription to allow `Timer` to multiplex to multiple subscribers - /// the values produced by a single `TimerPublisher.Inner` - private class RoutingSubscription: Subscription, Subscriber, CustomStringConvertible, CustomReflectable, CustomPlaygroundDisplayConvertible { - typealias Input = Date - typealias Failure = Never - - private typealias ErasedSubscriber = AnySubscriber - - private let lock: Lock - - // Inner is IUP due to init requirements - private var inner: Inner! - private var subscribers: [ErasedSubscriber] = [] - - private var _lockedIsConnected = false - var isConnected: Bool { - get { - lock.lock() - defer { lock.unlock() } - return _lockedIsConnected - } - - set { - lock.lock() - let oldValue = _lockedIsConnected - _lockedIsConnected = newValue - - // Inner will always be non-nil - let inner = self.inner! - lock.unlock() - - guard newValue, !oldValue else { - return - } - inner.enqueue() - } - } - - var description: String { return "Timer" } - var customMirror: Mirror { return inner.customMirror } - var playgroundDescription: Any { return description } - var combineIdentifier: CombineIdentifier { return inner.combineIdentifier } - - init(parent: TimerPublisher) { - self.lock = Lock() - self.inner = Inner(parent, self) - } - - deinit { - lock.cleanupLock() - } - - func addSubscriber(_ sub: S) - where - S.Failure == Failure, - S.Input == Output - { - lock.lock() - subscribers.append(AnySubscriber(sub)) - lock.unlock() - - sub.receive(subscription: self) - } - - func receive(subscription: Subscription) { - lock.lock() - let subscribers = self.subscribers - lock.unlock() - - for sub in subscribers { - sub.receive(subscription: subscription) - } - } - - func receive(_ value: Input) -> Subscribers.Demand { - var resultingDemand: Subscribers.Demand = .max(0) - lock.lock() - let subscribers = self.subscribers - let isConnected = _lockedIsConnected - lock.unlock() - - guard isConnected else { return .none } - - for sub in subscribers { - resultingDemand += sub.receive(value) - } - return resultingDemand - } - - func receive(completion: Subscribers.Completion) { - lock.lock() - let subscribers = self.subscribers - lock.unlock() - - for sub in subscribers { - sub.receive(completion: completion) - } - } - - func request(_ demand: Subscribers.Demand) { - lock.lock() - // Inner will always be non-nil - let inner = self.inner! - lock.unlock() - - inner.request(demand) - } - - func cancel() { - lock.lock() - // Inner will always be non-nil - let inner = self.inner! - _lockedIsConnected = false - self.subscribers = [] - lock.unlock() - - inner.cancel() - } - } - - public func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { - routingSubscription.addSubscriber(subscriber) - } - - public func connect() -> Cancellable { - routingSubscription.isConnected = true - return routingSubscription - } - - private typealias Parent = TimerPublisher - private final class Inner: NSObject, Subscription, CustomReflectable, CustomPlaygroundDisplayConvertible - where - Downstream.Input == Date, - Downstream.Failure == Never - { - private lazy var timer: Timer? = { - let t = Timer( - timeInterval: parent?.interval ?? 0, - target: self, - selector: #selector(timerFired), - userInfo: nil, - repeats: true - ) - - t.tolerance = parent?.tolerance ?? 0 - - return t - }() - - private let lock: Lock - private var downstream: Downstream? - private var parent: Parent? - private var started: Bool - private var demand: Subscribers.Demand - - override var description: String { return "Timer" } - var customMirror: Mirror { - lock.lock() - defer { lock.unlock() } - return Mirror(self, children: [ - "downstream": downstream as Any, - "interval": parent?.interval as Any, - "tolerance": parent?.tolerance as Any - ]) - } - var playgroundDescription: Any { return description } - - init(_ parent: Parent, _ downstream: Downstream) { - self.lock = Lock() - self.parent = parent - self.downstream = downstream - self.started = false - self.demand = .max(0) - super.init() - } - - deinit { - lock.cleanupLock() - } - - func enqueue() { - lock.lock() - guard let t = timer, let parent = self.parent, !started else { - lock.unlock() - return - } - - started = true - lock.unlock() - - parent.runLoop.add(t, forMode: parent.mode) - } - - func cancel() { - lock.lock() - guard let t = timer else { - lock.unlock() - return - } - - // clear out all optionals - downstream = nil - parent = nil - started = false - demand = .max(0) - timer = nil - lock.unlock() - - // cancel the timer - t.invalidate() - } - - func request(_ n: Subscribers.Demand) { - lock.lock() - defer { lock.unlock() } - guard parent != nil else { - return - } - demand += n - } - - @objc - func timerFired(arg: Any) { - lock.lock() - guard let ds = downstream, parent != nil else { - lock.unlock() - return - } - - // This publisher drops events on the floor when there is no space in the subscriber - guard demand > 0 else { - lock.unlock() - return - } - - demand -= 1 - lock.unlock() - - let extra = ds.receive(Date()) - guard extra > 0 else { - return - } - - lock.lock() - demand += extra - lock.unlock() - } - } - } -} - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/Publishers+URLSession.swift b/stdlib/public/Darwin/Foundation/Publishers+URLSession.swift deleted file mode 100644 index 565d0647662a0..0000000000000 --- a/stdlib/public/Darwin/Foundation/Publishers+URLSession.swift +++ /dev/null @@ -1,175 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -@_exported import Foundation // Clang module -import Combine - -// MARK: Data Tasks - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension URLSession { - /// Returns a publisher that wraps a URL session data task for a given URL. - /// - /// The publisher publishes data when the task completes, or terminates if the task fails with an error. - /// - Parameter url: The URL for which to create a data task. - /// - Returns: A publisher that wraps a data task for the URL. - public func dataTaskPublisher( - for url: URL) - -> DataTaskPublisher - { - let request = URLRequest(url: url) - return DataTaskPublisher(request: request, session: self) - } - - /// Returns a publisher that wraps a URL session data task for a given URL request. - /// - /// The publisher publishes data when the task completes, or terminates if the task fails with an error. - /// - Parameter request: The URL request for which to create a data task. - /// - Returns: A publisher that wraps a data task for the URL request. - public func dataTaskPublisher( - for request: URLRequest) - -> DataTaskPublisher - { - return DataTaskPublisher(request: request, session: self) - } - - public struct DataTaskPublisher: Publisher { - public typealias Output = (data: Data, response: URLResponse) - public typealias Failure = URLError - - public let request: URLRequest - public let session: URLSession - - public init(request: URLRequest, session: URLSession) { - self.request = request - self.session = session - } - - public func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { - subscriber.receive(subscription: Inner(self, subscriber)) - } - - private typealias Parent = DataTaskPublisher - private final class Inner: Subscription, CustomStringConvertible, CustomReflectable, CustomPlaygroundDisplayConvertible - where - Downstream.Input == Parent.Output, - Downstream.Failure == Parent.Failure - { - typealias Input = Downstream.Input - typealias Failure = Downstream.Failure - - private let lock: Lock - private var parent: Parent? // GuardedBy(lock) - private var downstream: Downstream? // GuardedBy(lock) - private var demand: Subscribers.Demand // GuardedBy(lock) - private var task: URLSessionDataTask! // GuardedBy(lock) - - var description: String { return "DataTaskPublisher" } - var customMirror: Mirror { - lock.lock() - defer { lock.unlock() } - return Mirror(self, children: [ - "task": task as Any, - "downstream": downstream as Any, - "parent": parent as Any, - "demand": demand, - ]) - } - var playgroundDescription: Any { return description } - - init(_ parent: Parent, _ downstream: Downstream) { - self.lock = Lock() - self.parent = parent - self.downstream = downstream - self.demand = .max(0) - } - - deinit { - lock.cleanupLock() - } - - // MARK: - Upward Signals - func request(_ d: Subscribers.Demand) { - precondition(d > 0, "Invalid request of zero demand") - - lock.lock() - guard let p = parent else { - // We've already been cancelled so bail - lock.unlock() - return - } - - // Avoid issues around `self` before init by setting up only once here - if self.task == nil { - let task = p.session.dataTask( - with: p.request, - completionHandler: handleResponse(data:response:error:) - ) - self.task = task - } - - self.demand += d - let task = self.task! - lock.unlock() - - task.resume() - } - - private func handleResponse(data: Data?, response: URLResponse?, error: Error?) { - lock.lock() - guard demand > 0, - parent != nil, - let ds = downstream - else { - lock.unlock() - return - } - - parent = nil - downstream = nil - - // We clear demand since this is a single shot shape - demand = .max(0) - task = nil - lock.unlock() - - if let response = response, error == nil { - _ = ds.receive((data ?? Data(), response)) - ds.receive(completion: .finished) - } else { - let urlError = error as? URLError ?? URLError(.unknown) - ds.receive(completion: .failure(urlError)) - } - } - - func cancel() { - lock.lock() - guard parent != nil else { - lock.unlock() - return - } - parent = nil - downstream = nil - demand = .max(0) - let task = self.task - self.task = nil - lock.unlock() - task?.cancel() - } - } - } -} - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/ReferenceConvertible.swift b/stdlib/public/Darwin/Foundation/ReferenceConvertible.swift deleted file mode 100644 index ebdd9a969cabe..0000000000000 --- a/stdlib/public/Darwin/Foundation/ReferenceConvertible.swift +++ /dev/null @@ -1,20 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -/// Decorates types which are backed by a Foundation reference type. -/// -/// All `ReferenceConvertible` types are hashable, equatable, and provide description functions. -public protocol ReferenceConvertible : _ObjectiveCBridgeable, CustomStringConvertible, CustomDebugStringConvertible, Hashable { - associatedtype ReferenceType : NSObject, NSCopying -} diff --git a/stdlib/public/Darwin/Foundation/Scanner.swift b/stdlib/public/Darwin/Foundation/Scanner.swift deleted file mode 100644 index f6dcef27fe7ea..0000000000000 --- a/stdlib/public/Darwin/Foundation/Scanner.swift +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) 2019 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -extension CharacterSet { - fileprivate func contains(_ character: Character) -> Bool { - return character.unicodeScalars.allSatisfy(self.contains(_:)) - } -} - -// ----- - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Scanner { - public enum NumberRepresentation { - case decimal // See the %d, %f and %F format conversions. - case hexadecimal // See the %x, %X, %a and %A format conversions. For integers, a leading 0x or 0X is optional; for floating-point numbers, it is required. - } - - public var currentIndex: String.Index { - get { - let string = self.string - var index = string._toUTF16Index(scanLocation) - - var delta = 0 - while index != string.endIndex && index.samePosition(in: string) == nil { - delta += 1 - index = string._toUTF16Index(scanLocation + delta) - } - - return index - } - set { scanLocation = string._toUTF16Offset(newValue) } - } - - fileprivate func _scan - (representation: NumberRepresentation, - scanDecimal: (UnsafeMutablePointer?) -> Bool, - scanHexadecimal: (UnsafeMutablePointer?) -> Bool) -> Integer? { - var value: Integer = .max - - switch representation { - case .decimal: guard scanDecimal(&value) else { return nil } - case .hexadecimal: guard scanHexadecimal(&value) else { return nil } - } - - return value - } - - fileprivate func _scan - (representation: NumberRepresentation, - scanDecimal: (UnsafeMutablePointer?) -> Bool, - scanHexadecimal: (UnsafeMutablePointer?) -> Bool) -> FloatingPoint? { - var value: FloatingPoint = .greatestFiniteMagnitude - - switch representation { - case .decimal: guard scanDecimal(&value) else { return nil } - case .hexadecimal: guard scanHexadecimal(&value) else { return nil } - } - - return value - } - - fileprivate func _scan - (representation: NumberRepresentation, - scanDecimal: (UnsafeMutablePointer?) -> Bool, - overflowingScanHexadecimal: (UnsafeMutablePointer?) -> Bool) -> Integer? { - return _scan(representation: representation, scanDecimal: scanDecimal, scanHexadecimal: { (pointer) -> Bool in - var unsignedValue: OverflowingHexadecimalInteger = .max - guard overflowingScanHexadecimal(&unsignedValue) else { return false } - if unsignedValue <= Integer.max { - pointer?.pointee = Integer(unsignedValue) - } - return true - }) - } - - public func scanInt(representation: NumberRepresentation = .decimal) -> Int? { -#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) - if let value = scanInt64(representation: representation) { - return Int(value) - } -#elseif arch(i386) || arch(arm) || arch(arm64_32) - if let value = scanInt32(representation: representation) { - return Int(value) - } -#else - #error("This architecture isn't known. Add it to the 32-bit or 64-bit line; if the machine word isn't either of those, you need to implement appropriate scanning and handle the potential overflow here.") -#endif - return nil - } - - public func scanInt32(representation: NumberRepresentation = .decimal) -> Int32? { - return _scan(representation: representation, scanDecimal: self.scanInt32(_:), overflowingScanHexadecimal: self.scanHexInt32(_:)) - } - - public func scanInt64(representation: NumberRepresentation = .decimal) -> Int64? { - return _scan(representation: representation, scanDecimal: self.scanInt64(_:), overflowingScanHexadecimal: self.scanHexInt64(_:)) - } - - public func scanUInt64(representation: NumberRepresentation = .decimal) -> UInt64? { - return _scan(representation: representation, scanDecimal: self.scanUnsignedLongLong(_:), scanHexadecimal: self.scanHexInt64(_:)) - } - - public func scanFloat(representation: NumberRepresentation = .decimal) -> Float? { - return _scan(representation: representation, scanDecimal: self.scanFloat(_:), scanHexadecimal: self.scanHexFloat(_:)) - } - - public func scanDouble(representation: NumberRepresentation = .decimal) -> Double? { - return _scan(representation: representation, scanDecimal: self.scanDouble(_:), scanHexadecimal: self.scanHexDouble(_:)) - } - - public func scanDecimal() -> Decimal? { - var value: Decimal = 0 - guard scanDecimal(&value) else { return nil } - return value - } - - - fileprivate var _currentIndexAfterSkipping: String.Index { - guard let skips = charactersToBeSkipped else { return currentIndex } - - let index = string[currentIndex...].firstIndex(where: { !skips.contains($0) }) - return index ?? string.endIndex - } - - public func scanString(_ searchString: String) -> String? { - let currentIndex = _currentIndexAfterSkipping - - guard let substringEnd = string.index(currentIndex, offsetBy: searchString.count, limitedBy: string.endIndex) else { return nil } - - if string.compare(searchString, options: self.caseSensitive ? [] : .caseInsensitive, range: currentIndex ..< substringEnd, locale: self.locale as? Locale) == .orderedSame { - let it = string[currentIndex ..< substringEnd] - self.currentIndex = substringEnd - return String(it) - } else { - return nil - } - } - - public func scanCharacters(from set: CharacterSet) -> String? { - let currentIndex = _currentIndexAfterSkipping - - let substringEnd = string[currentIndex...].firstIndex(where: { !set.contains($0) }) ?? string.endIndex - guard currentIndex != substringEnd else { return nil } - - let substring = string[currentIndex ..< substringEnd] - self.currentIndex = substringEnd - return String(substring) - } - - public func scanUpToString(_ substring: String) -> String? { - guard !substring.isEmpty else { return nil } - let string = self.string - let startIndex = _currentIndexAfterSkipping - - var beginningOfNewString = string.endIndex - var currentSearchIndex = startIndex - - repeat { - guard let range = string.range(of: substring, options: self.caseSensitive ? [] : .caseInsensitive, range: currentSearchIndex ..< string.endIndex, locale: self.locale as? Locale) else { - // If the string isn't found at all, it means it's not in the string. Just take everything to the end. - beginningOfNewString = string.endIndex - break - } - - // range(of:…) can return partial grapheme ranges when dealing with emoji. - // Make sure we take a range only if it doesn't split a grapheme in the string. - if let maybeBeginning = range.lowerBound.samePosition(in: string), - range.upperBound.samePosition(in: string) != nil { - beginningOfNewString = maybeBeginning - break - } - - // If we got here, we need to search again starting from just after the location we found. - currentSearchIndex = range.upperBound - } while beginningOfNewString == string.endIndex && currentSearchIndex < string.endIndex - - guard startIndex != beginningOfNewString else { return nil } - - let foundSubstring = string[startIndex ..< beginningOfNewString] - self.currentIndex = beginningOfNewString - return String(foundSubstring) - } - - public func scanUpToCharacters(from set: CharacterSet) -> String? { - let currentIndex = _currentIndexAfterSkipping - let string = self.string - - let firstCharacterInSet = string[currentIndex...].firstIndex(where: { set.contains($0) }) ?? string.endIndex - guard currentIndex != firstCharacterInSet else { return nil } - self.currentIndex = firstCharacterInSet - return String(string[currentIndex ..< firstCharacterInSet]) - } - - public func scanCharacter() -> Character? { - let currentIndex = _currentIndexAfterSkipping - - let string = self.string - - guard currentIndex != string.endIndex else { return nil } - - let character = string[currentIndex] - self.currentIndex = string.index(after: currentIndex) - return character - } -} diff --git a/stdlib/public/Darwin/Foundation/Schedulers+Date.swift b/stdlib/public/Darwin/Foundation/Schedulers+Date.swift deleted file mode 100644 index 63b6eb0c05939..0000000000000 --- a/stdlib/public/Darwin/Foundation/Schedulers+Date.swift +++ /dev/null @@ -1,33 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -@_exported import Foundation // Clang module -import Combine - -// Date cannot conform to Strideable per rdar://35158274 -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - extension Date /* : Strideable */ { - public typealias Stride = TimeInterval - - public func distance(to other: Date) -> TimeInterval { - return other.timeIntervalSinceReferenceDate - self.timeIntervalSinceReferenceDate - } - - public func advanced(by n: TimeInterval) -> Date { - return self + n - } -} - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ \ No newline at end of file diff --git a/stdlib/public/Darwin/Foundation/Schedulers+OperationQueue.swift b/stdlib/public/Darwin/Foundation/Schedulers+OperationQueue.swift deleted file mode 100644 index 963c83fe2ee4a..0000000000000 --- a/stdlib/public/Darwin/Foundation/Schedulers+OperationQueue.swift +++ /dev/null @@ -1,214 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -@_exported import Foundation // Clang module -import Combine - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension OperationQueue: Scheduler { - /// The scheduler time type used by the operation queue. - public struct SchedulerTimeType: Strideable, Codable, Hashable { - /// The date represented by this type. - public var date: Date - - /// Initializes a operation queue scheduler time with the given date. - /// - /// - Parameter date: The date to represent. - public init(_ date: Date) { - self.date = date - } - - /// Returns the distance to another operation queue scheduler time. - /// - /// - Parameter other: Another operation queue time. - /// - Returns: The time interval between this time and the provided time. - public func distance(to other: OperationQueue.SchedulerTimeType) -> OperationQueue.SchedulerTimeType.Stride { - return OperationQueue.SchedulerTimeType.Stride(floatLiteral: date.distance(to: other.date)) - } - - /// Returns a operation queue scheduler time calculated by advancing this instance’s time by the given interval. - /// - /// - Parameter n: A time interval to advance. - /// - Returns: A operation queue time advanced by the given interval from this instance’s time. - public func advanced(by n: OperationQueue.SchedulerTimeType.Stride) -> OperationQueue.SchedulerTimeType { - return OperationQueue.SchedulerTimeType(date.advanced(by: n.timeInterval)) - } - - /// The interval by which operation queue times advance. - public struct Stride: ExpressibleByFloatLiteral, Comparable, SignedNumeric, Codable, SchedulerTimeIntervalConvertible { - public typealias FloatLiteralType = TimeInterval - public typealias IntegerLiteralType = TimeInterval - public typealias Magnitude = TimeInterval - - /// The value of this time interval in seconds. - public var magnitude: TimeInterval - - /// The value of this time interval in seconds. - public var timeInterval: TimeInterval { - return magnitude - } - - public init(integerLiteral value: TimeInterval) { - magnitude = value - } - - public init(floatLiteral value: TimeInterval) { - magnitude = value - } - - public init(_ timeInterval: TimeInterval) { - magnitude = timeInterval - } - - public init?(exactly source: T) where T: BinaryInteger { - if let d = TimeInterval(exactly: source) { - magnitude = d - } else { - return nil - } - } - - // --- - - public static func < (lhs: Stride, rhs: Stride) -> Bool { - return lhs.magnitude < rhs.magnitude - } - - // --- - - public static func * (lhs: Stride, rhs: Stride) -> Stride { - return Stride(lhs.timeInterval * rhs.timeInterval) - } - - public static func + (lhs: Stride, rhs: Stride) -> Stride { - return Stride(lhs.magnitude + rhs.magnitude) - } - - public static func - (lhs: Stride, rhs: Stride) -> Stride { - return Stride(lhs.magnitude - rhs.magnitude) - } - - // --- - - public static func *= (lhs: inout Stride, rhs: Stride) { - let result = lhs * rhs - lhs = result - } - - public static func += (lhs: inout Stride, rhs: Stride) { - let result = lhs + rhs - lhs = result - } - - public static func -= (lhs: inout Stride, rhs: Stride) { - let result = lhs - rhs - lhs = result - } - - // --- - - public static func seconds(_ s: Int) -> Stride { - return Stride(Double(s)) - } - - public static func seconds(_ s: Double) -> Stride { - return Stride(s) - } - - public static func milliseconds(_ ms: Int) -> Stride { - return Stride(Double(ms) / 1_000.0) - } - - public static func microseconds(_ us: Int) -> Stride { - return Stride(Double(us) / 1_000_000.0) - } - - public static func nanoseconds(_ ns: Int) -> Stride { - return Stride(Double(ns) / 1_000_000_000.0) - } - } - } - - /// Options that affect the operation of the operation queue scheduler. - public struct SchedulerOptions { } - - private final class DelayReadyOperation: Operation, Cancellable { - static var readySchedulingQueue: DispatchQueue = { - return DispatchQueue(label: "DelayReadyOperation") - }() - - var action: (() -> Void)? - var readyFromAfter: Bool - - init(_ action: @escaping() -> Void, after: OperationQueue.SchedulerTimeType) { - self.action = action - readyFromAfter = false - super.init() - let deadline = DispatchTime.now() + after.date.timeIntervalSinceNow - DelayReadyOperation.readySchedulingQueue.asyncAfter(deadline: deadline) { [weak self] in - self?.becomeReady() - } - } - - override func main() { - action!() - action = nil - } - - func becomeReady() { - willChangeValue(for: \.isReady) - readyFromAfter = true - didChangeValue(for: \.isReady) - } - - override var isReady: Bool { - return super.isReady && readyFromAfter - } - } - - public func schedule(options: OperationQueue.SchedulerOptions?, - _ action: @escaping () -> Void) { - let op = BlockOperation(block: action) - addOperation(op) - } - - public func schedule(after date: OperationQueue.SchedulerTimeType, - tolerance: OperationQueue.SchedulerTimeType.Stride, - options: OperationQueue.SchedulerOptions?, - _ action: @escaping () -> Void) { - let op = DelayReadyOperation(action, after: date) - addOperation(op) - } - - public func schedule(after date: OperationQueue.SchedulerTimeType, - interval: OperationQueue.SchedulerTimeType.Stride, - tolerance: OperationQueue.SchedulerTimeType.Stride, - options: OperationQueue.SchedulerOptions?, - _ action: @escaping () -> Void) -> Cancellable { - let op = DelayReadyOperation(action, after: date.advanced(by: interval)) - addOperation(op) - return AnyCancellable(op) - } - - public var now: OperationQueue.SchedulerTimeType { - return OperationQueue.SchedulerTimeType(Date()) - } - - public var minimumTolerance: OperationQueue.SchedulerTimeType.Stride { - return OperationQueue.SchedulerTimeType.Stride(0.0) - } -} - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/Schedulers+RunLoop.swift b/stdlib/public/Darwin/Foundation/Schedulers+RunLoop.swift deleted file mode 100644 index 65b6faff19db1..0000000000000 --- a/stdlib/public/Darwin/Foundation/Schedulers+RunLoop.swift +++ /dev/null @@ -1,199 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -@_exported import Foundation // Clang module -import Combine - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension RunLoop: Scheduler { - /// The scheduler time type used by the run loop. - public struct SchedulerTimeType: Strideable, Codable, Hashable { - /// The date represented by this type. - public var date: Date - - /// Initializes a run loop scheduler time with the given date. - /// - /// - Parameter date: The date to represent. - public init(_ date: Date) { - self.date = date - } - - /// Returns the distance to another run loop scheduler time. - /// - /// - Parameter other: Another dispatch queue time. - /// - Returns: The time interval between this time and the provided time. - public func distance(to other: RunLoop.SchedulerTimeType) -> SchedulerTimeType.Stride { - return Stride(floatLiteral: date.distance(to: other.date)) - } - - /// Returns a run loop scheduler time calculated by advancing this instance’s time by the given interval. - /// - /// - Parameter n: A time interval to advance. - /// - Returns: A dispatch queue time advanced by the given interval from this instance’s time. - public func advanced(by n: SchedulerTimeType.Stride) -> RunLoop.SchedulerTimeType { - return SchedulerTimeType(date.advanced(by: n.timeInterval)) - } - - /// The interval by which run loop times advance. - public struct Stride: ExpressibleByFloatLiteral, Comparable, SignedNumeric, Codable, SchedulerTimeIntervalConvertible { - public typealias FloatLiteralType = TimeInterval - public typealias IntegerLiteralType = TimeInterval - public typealias Magnitude = TimeInterval - - /// The value of this time interval in seconds. - public var magnitude: TimeInterval - - /// The value of this time interval in seconds. - public var timeInterval: TimeInterval { - return magnitude - } - - public init(integerLiteral value: TimeInterval) { - magnitude = value - } - - public init(floatLiteral value: TimeInterval) { - magnitude = value - } - - public init(_ timeInterval: TimeInterval) { - magnitude = timeInterval - } - - public init?(exactly source: T) where T: BinaryInteger { - if let d = TimeInterval(exactly: source) { - magnitude = d - } else { - return nil - } - } - - // --- - - public static func < (lhs: Stride, rhs: Stride) -> Bool { - return lhs.magnitude < rhs.magnitude - } - - // --- - - public static func * (lhs: Stride, rhs: Stride) -> Stride { - return Stride(lhs.timeInterval * rhs.timeInterval) - } - - public static func + (lhs: Stride, rhs: Stride) -> Stride { - return Stride(lhs.magnitude + rhs.magnitude) - } - - public static func - (lhs: Stride, rhs: Stride) -> Stride { - return Stride(lhs.magnitude - rhs.magnitude) - } - - // --- - - public static func *= (lhs: inout Stride, rhs: Stride) { - let result = lhs * rhs - lhs = result - } - - public static func += (lhs: inout Stride, rhs: Stride) { - let result = lhs + rhs - lhs = result - } - - public static func -= (lhs: inout Stride, rhs: Stride) { - let result = lhs - rhs - lhs = result - } - - // --- - - public static func seconds(_ s: Int) -> Stride { - return Stride(Double(s)) - } - - public static func seconds(_ s: Double) -> Stride { - return Stride(s) - } - - public static func milliseconds(_ ms: Int) -> Stride { - return Stride(Double(ms) / 1_000.0) - } - - public static func microseconds(_ us: Int) -> Stride { - return Stride(Double(us) / 1_000_000.0) - } - - public static func nanoseconds(_ ns: Int) -> Stride { - return Stride(Double(ns) / 1_000_000_000.0) - } - } - } - - /// Options that affect the operation of the run loop scheduler. - public struct SchedulerOptions { } - - public func schedule(options: SchedulerOptions?, - _ action: @escaping () -> Void) { - self.perform(action) - } - - public func schedule(after date: SchedulerTimeType, - tolerance: SchedulerTimeType.Stride, - options: SchedulerOptions?, - _ action: @escaping () -> Void) { - let ti = date.date.timeIntervalSince(Date()) - self.perform(#selector(self.runLoopScheduled), with: _CombineRunLoopAction(action), afterDelay: ti) - } - - public func schedule(after date: SchedulerTimeType, - interval: SchedulerTimeType.Stride, - tolerance: SchedulerTimeType.Stride, - options: SchedulerOptions?, - _ action: @escaping () -> Void) -> Cancellable { - let timer = Timer(fire: date.date, interval: interval.timeInterval, repeats: true) { _ in - action() - } - - timer.tolerance = tolerance.timeInterval - self.add(timer, forMode: .default) - - return AnyCancellable(timer.invalidate) - } - - public var now: SchedulerTimeType { - return SchedulerTimeType(Date()) - } - - public var minimumTolerance: SchedulerTimeType.Stride { - return 0.0 - } - - @objc - fileprivate func runLoopScheduled(action: _CombineRunLoopAction) { - action.action() - } -} - -@objc -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -private class _CombineRunLoopAction: NSObject { - let action: () -> Void - - init(_ action: @escaping () -> Void) { - self.action = action - } -} - -#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */ diff --git a/stdlib/public/Darwin/Foundation/String.swift b/stdlib/public/Darwin/Foundation/String.swift deleted file mode 100644 index aa55ab4f460f5..0000000000000 --- a/stdlib/public/Darwin/Foundation/String.swift +++ /dev/null @@ -1,124 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_spi(Foundation) import Swift - -//===----------------------------------------------------------------------===// -// New Strings -//===----------------------------------------------------------------------===// - -// -// Conversion from NSString to Swift's native representation -// - -extension String { - public init(_ cocoaString: NSString) { - self = String(_cocoaString: cocoaString) - } -} - -extension String : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSString { - // This method should not do anything extra except calling into the - // implementation inside core. (These two entry points should be - // equivalent.) - return unsafeBitCast(_bridgeToObjectiveCImpl(), to: NSString.self) - } - - public static func _forceBridgeFromObjectiveC( - _ x: NSString, - result: inout String? - ) { - result = String(x) - } - - public static func _conditionallyBridgeFromObjectiveC( - _ x: NSString, - result: inout String? - ) -> Bool { - self._forceBridgeFromObjectiveC(x, result: &result) - return result != nil - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC( - _ source: NSString? - ) -> String { - // `nil` has historically been used as a stand-in for an empty - // string; map it to an empty string. - if _slowPath(source == nil) { return String() } - return String(source!) - } -} - -extension Substring : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSString { - return String(self)._bridgeToObjectiveC() - } - - public static func _forceBridgeFromObjectiveC( - _ x: NSString, - result: inout Substring? - ) { - let s = String(x) - result = s[...] - } - - public static func _conditionallyBridgeFromObjectiveC( - _ x: NSString, - result: inout Substring? - ) -> Bool { - self._forceBridgeFromObjectiveC(x, result: &result) - return result != nil - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC( - _ source: NSString? - ) -> Substring { - // `nil` has historically been used as a stand-in for an empty - // string; map it to an empty substring. - if _slowPath(source == nil) { return Substring() } - let s = String(source!) - return s[...] - } -} - -extension String: CVarArg {} - -/* - This is on NSObject so that the stdlib can call it in StringBridge.swift - without having to synthesize a receiver (e.g. lookup a class or allocate) - - In the future (once the Foundation overlay can know about SmallString), we - should move the caller of this method up into the overlay and avoid this - indirection. - */ -private extension NSObject { - // The ObjC selector has to start with "new" to get ARC to not autorelease - @_effects(releasenone) - @objc(newTaggedNSStringWithASCIIBytes_:length_:) - func createTaggedString(bytes: UnsafePointer, - count: Int) -> AnyObject? { - //TODO: update this to use _CFStringCreateTaggedPointerString once we can - return CFStringCreateWithBytes( - kCFAllocatorSystemDefault, - bytes, - count, - CFStringBuiltInEncodings.UTF8.rawValue, - false - ) as NSString as NSString? //just "as AnyObject" inserts unwanted bridging - } -} diff --git a/stdlib/public/Darwin/Foundation/TimeZone.swift b/stdlib/public/Darwin/Foundation/TimeZone.swift deleted file mode 100644 index 2674ac7382786..0000000000000 --- a/stdlib/public/Darwin/Foundation/TimeZone.swift +++ /dev/null @@ -1,303 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -@_implementationOnly import _SwiftFoundationOverlayShims - -/** - `TimeZone` defines the behavior of a time zone. Time zone values represent geopolitical regions. Consequently, these values have names for these regions. Time zone values also represent a temporal offset, either plus or minus, from Greenwich Mean Time (GMT) and an abbreviation (such as PST for Pacific Standard Time). - - `TimeZone` provides two static functions to get time zone values: `current` and `autoupdatingCurrent`. The `autoupdatingCurrent` time zone automatically tracks updates made by the user. - - Note that time zone database entries such as "America/Los_Angeles" are IDs, not names. An example of a time zone name is "Pacific Daylight Time". Although many `TimeZone` functions include the word "name", they refer to IDs. - - Cocoa does not provide any API to change the time zone of the computer, or of other applications. - */ -public struct TimeZone : Hashable, Equatable, ReferenceConvertible { - public typealias ReferenceType = NSTimeZone - - private var _wrapped : NSTimeZone - private var _autoupdating : Bool - - /// The time zone currently used by the system. - public static var current : TimeZone { - return TimeZone(adoptingReference: __NSTimeZoneCurrent() as! NSTimeZone, autoupdating: false) - } - - /// The time zone currently used by the system, automatically updating to the user's current preference. - /// - /// If this time zone is mutated, then it no longer tracks the system time zone. - /// - /// The autoupdating time zone only compares equal to itself. - public static var autoupdatingCurrent : TimeZone { - return TimeZone(adoptingReference: __NSTimeZoneAutoupdating() as! NSTimeZone, autoupdating: true) - } - - // MARK: - - // - - /// Returns a time zone initialized with a given identifier. - /// - /// An example identifier is "America/Los_Angeles". - /// - /// If `identifier` is an unknown identifier, then returns `nil`. - public init?(identifier: __shared String) { - if let r = NSTimeZone(name: identifier) { - _wrapped = r - _autoupdating = false - } else { - return nil - } - } - - @available(*, unavailable, renamed: "init(secondsFromGMT:)") - public init(forSecondsFromGMT seconds: Int) { fatalError() } - - /// Returns a time zone initialized with a specific number of seconds from GMT. - /// - /// Time zones created with this never have daylight savings and the offset is constant no matter the date. The identifier and abbreviation do NOT follow the POSIX convention (of minutes-west). - /// - /// - parameter seconds: The number of seconds from GMT. - /// - returns: A time zone, or `nil` if a valid time zone could not be created from `seconds`. - public init?(secondsFromGMT seconds: Int) { - if let r = NSTimeZone(forSecondsFromGMT: seconds) as NSTimeZone? { - _wrapped = r - _autoupdating = false - } else { - return nil - } - } - - /// Returns a time zone identified by a given abbreviation. - /// - /// In general, you are discouraged from using abbreviations except for unique instances such as "GMT". Time Zone abbreviations are not standardized and so a given abbreviation may have multiple meanings--for example, "EST" refers to Eastern Time in both the United States and Australia - /// - /// - parameter abbreviation: The abbreviation for the time zone. - /// - returns: A time zone identified by abbreviation determined by resolving the abbreviation to an identifier using the abbreviation dictionary and then returning the time zone for that identifier. Returns `nil` if there is no match for abbreviation. - public init?(abbreviation: __shared String) { - if let r = NSTimeZone(abbreviation: abbreviation) { - _wrapped = r - _autoupdating = false - } else { - return nil - } - } - - private init(reference: NSTimeZone) { - if __NSTimeZoneIsAutoupdating(reference) { - // we can't copy this or we lose its auto-ness (27048257) - // fortunately it's immutable - _autoupdating = true - _wrapped = reference - } else { - _autoupdating = false - _wrapped = reference.copy() as! NSTimeZone - } - } - - private init(adoptingReference reference: NSTimeZone, autoupdating: Bool) { - // this path is only used for types we do not need to copy (we are adopting the ref) - _wrapped = reference - _autoupdating = autoupdating - } - - // MARK: - - // - - @available(*, unavailable, renamed: "identifier") - public var name: String { fatalError() } - - /// The geopolitical region identifier that identifies the time zone. - public var identifier: String { - return _wrapped.name - } - - @available(*, unavailable, message: "use the identifier instead") - public var data: Data { fatalError() } - - /// The current difference in seconds between the time zone and Greenwich Mean Time. - /// - /// - parameter date: The date to use for the calculation. The default value is the current date. - public func secondsFromGMT(for date: Date = Date()) -> Int { - return _wrapped.secondsFromGMT(for: date) - } - - /// Returns the abbreviation for the time zone at a given date. - /// - /// Note that the abbreviation may be different at different dates. For example, during daylight saving time the US/Eastern time zone has an abbreviation of "EDT." At other times, its abbreviation is "EST." - /// - parameter date: The date to use for the calculation. The default value is the current date. - public func abbreviation(for date: Date = Date()) -> String? { - return _wrapped.abbreviation(for: date) - } - - /// Returns a Boolean value that indicates whether the receiver uses daylight saving time at a given date. - /// - /// - parameter date: The date to use for the calculation. The default value is the current date. - public func isDaylightSavingTime(for date: Date = Date()) -> Bool { - return _wrapped.isDaylightSavingTime(for: date) - } - - /// Returns the daylight saving time offset for a given date. - /// - /// - parameter date: The date to use for the calculation. The default value is the current date. - public func daylightSavingTimeOffset(for date: Date = Date()) -> TimeInterval { - return _wrapped.daylightSavingTimeOffset(for: date) - } - - /// Returns the next daylight saving time transition after a given date. - /// - /// - parameter date: A date. - /// - returns: The next daylight saving time transition after `date`. Depending on the time zone, this function may return a change of the time zone's offset from GMT. Returns `nil` if the time zone of the receiver does not observe daylight savings time as of `date`. - public func nextDaylightSavingTimeTransition(after date: Date) -> Date? { - return _wrapped.nextDaylightSavingTimeTransition(after: date) - } - - /// Returns an array of strings listing the identifier of all the time zones known to the system. - public static var knownTimeZoneIdentifiers : [String] { - return NSTimeZone.knownTimeZoneNames - } - - /// Returns the mapping of abbreviations to time zone identifiers. - public static var abbreviationDictionary : [String : String] { - get { - return NSTimeZone.abbreviationDictionary - } - set { - NSTimeZone.abbreviationDictionary = newValue - } - } - - /// Returns the time zone data version. - public static var timeZoneDataVersion : String { - return NSTimeZone.timeZoneDataVersion - } - - /// Returns the date of the next (after the current instant) daylight saving time transition for the time zone. Depending on the time zone, the value of this property may represent a change of the time zone's offset from GMT. Returns `nil` if the time zone does not currently observe daylight saving time. - public var nextDaylightSavingTimeTransition: Date? { - return _wrapped.nextDaylightSavingTimeTransition - } - - @available(*, unavailable, renamed: "localizedName(for:locale:)") - public func localizedName(_ style: NSTimeZone.NameStyle, locale: Locale?) -> String? { fatalError() } - - /// Returns the name of the receiver localized for a given locale. - public func localizedName(for style: NSTimeZone.NameStyle, locale: Locale?) -> String? { - return _wrapped.localizedName(style, locale: locale) - } - - // MARK: - - - public func hash(into hasher: inout Hasher) { - if _autoupdating { - hasher.combine(false) - } else { - hasher.combine(true) - hasher.combine(_wrapped) - } - } - - public static func ==(lhs: TimeZone, rhs: TimeZone) -> Bool { - if lhs._autoupdating || rhs._autoupdating { - return lhs._autoupdating == rhs._autoupdating - } else { - return lhs._wrapped.isEqual(rhs._wrapped) - } - } -} - -extension TimeZone : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - private var _kindDescription : String { - if self == TimeZone.autoupdatingCurrent { - return "autoupdatingCurrent" - } else if self == TimeZone.current { - return "current" - } else { - return "fixed" - } - } - - public var customMirror : Mirror { - let c: [(label: String?, value: Any)] = [ - ("identifier", identifier), - ("kind", _kindDescription), - ("abbreviation", abbreviation() as Any), - ("secondsFromGMT", secondsFromGMT()), - ("isDaylightSavingTime", isDaylightSavingTime()), - ] - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } - - public var description: String { - return "\(identifier) (\(_kindDescription))" - } - - public var debugDescription : String { - return "\(identifier) (\(_kindDescription))" - } -} - -extension TimeZone : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSTimeZone { - // _wrapped is immutable - return _wrapped - } - - public static func _forceBridgeFromObjectiveC(_ input: NSTimeZone, result: inout TimeZone?) { - if !_conditionallyBridgeFromObjectiveC(input, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: NSTimeZone, result: inout TimeZone?) -> Bool { - result = TimeZone(reference: input) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSTimeZone?) -> TimeZone { - var result: TimeZone? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -extension NSTimeZone : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as TimeZone) - } -} - -extension TimeZone : Codable { - private enum CodingKeys : Int, CodingKey { - case identifier - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let identifier = try container.decode(String.self, forKey: .identifier) - - guard let timeZone = TimeZone(identifier: identifier) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, - debugDescription: "Invalid TimeZone identifier.")) - } - - self = timeZone - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.identifier, forKey: .identifier) - } -} diff --git a/stdlib/public/Darwin/Foundation/URL.swift b/stdlib/public/Darwin/Foundation/URL.swift deleted file mode 100644 index 76b264398e055..0000000000000 --- a/stdlib/public/Darwin/Foundation/URL.swift +++ /dev/null @@ -1,1240 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -/** - URLs to file system resources support the properties defined below. Note that not all property values will exist for all file system URLs. For example, if a file is located on a volume that does not support creation dates, it is valid to request the creation date property, but the returned value will be nil, and no error will be generated. - - Only the fields requested by the keys you pass into the `URL` function to receive this value will be populated. The others will return `nil` regardless of the underlying property on the file system. - - As a convenience, volume resource values can be requested from any file system URL. The value returned will reflect the property value for the volume on which the resource is located. -*/ -public struct URLResourceValues { - fileprivate var _values: [URLResourceKey: Any] - fileprivate var _keys: Set - - public init() { - _values = [:] - _keys = [] - } - - fileprivate init(keys: Set, values: [URLResourceKey: Any]) { - _values = values - _keys = keys - } - - private func contains(_ key: URLResourceKey) -> Bool { - return _keys.contains(key) - } - - private func _get(_ key : URLResourceKey) -> T? { - return _values[key] as? T - } - private func _get(_ key : URLResourceKey) -> Bool? { - return (_values[key] as? NSNumber)?.boolValue - } - private func _get(_ key: URLResourceKey) -> Int? { - return (_values[key] as? NSNumber)?.intValue - } - - private mutating func _set(_ key : URLResourceKey, newValue : __owned Any?) { - _keys.insert(key) - _values[key] = newValue - } - private mutating func _set(_ key : URLResourceKey, newValue : String?) { - _keys.insert(key) - _values[key] = newValue as NSString? - } - private mutating func _set(_ key : URLResourceKey, newValue : [String]?) { - _keys.insert(key) - _values[key] = newValue as NSObject? - } - private mutating func _set(_ key : URLResourceKey, newValue : Date?) { - _keys.insert(key) - _values[key] = newValue as NSDate? - } - private mutating func _set(_ key : URLResourceKey, newValue : URL?) { - _keys.insert(key) - _values[key] = newValue as NSURL? - } - private mutating func _set(_ key : URLResourceKey, newValue : Bool?) { - _keys.insert(key) - if let value = newValue { - _values[key] = NSNumber(value: value) - } else { - _values[key] = nil - } - } - private mutating func _set(_ key : URLResourceKey, newValue : Int?) { - _keys.insert(key) - if let value = newValue { - _values[key] = NSNumber(value: value) - } else { - _values[key] = nil - } - } - - /// A loosely-typed dictionary containing all keys and values. - /// - /// If you have set temporary keys or non-standard keys, you can find them in here. - public var allValues : [URLResourceKey : Any] { - return _values - } - - /// The resource name provided by the file system. - public var name: String? { - get { return _get(.nameKey) } - set { _set(.nameKey, newValue: newValue) } - } - - /// Localized or extension-hidden name as displayed to users. - public var localizedName: String? { return _get(.localizedNameKey) } - - /// True for regular files. - public var isRegularFile: Bool? { return _get(.isRegularFileKey) } - - /// True for directories. - public var isDirectory: Bool? { return _get(.isDirectoryKey) } - - /// True for symlinks. - public var isSymbolicLink: Bool? { return _get(.isSymbolicLinkKey) } - - /// True for the root directory of a volume. - public var isVolume: Bool? { return _get(.isVolumeKey) } - - /// True for packaged directories. - /// - /// - note: You can only set or clear this property on directories; if you try to set this property on non-directory objects, the property is ignored. If the directory is a package for some other reason (extension type, etc), setting this property to false will have no effect. - public var isPackage: Bool? { - get { return _get(.isPackageKey) } - set { _set(.isPackageKey, newValue: newValue) } - } - - /// True if resource is an application. - @available(macOS 10.11, iOS 9.0, *) - public var isApplication: Bool? { return _get(.isApplicationKey) } - -#if os(macOS) - /// True if the resource is scriptable. Only applies to applications. - @available(macOS 10.11, *) - public var applicationIsScriptable: Bool? { return _get(.applicationIsScriptableKey) } -#endif - - /// True for system-immutable resources. - public var isSystemImmutable: Bool? { return _get(.isSystemImmutableKey) } - - /// True for user-immutable resources - public var isUserImmutable: Bool? { - get { return _get(.isUserImmutableKey) } - set { _set(.isUserImmutableKey, newValue: newValue) } - } - - /// True for resources normally not displayed to users. - /// - /// - note: If the resource is a hidden because its name starts with a period, setting this property to false will not change the property. - public var isHidden: Bool? { - get { return _get(.isHiddenKey) } - set { _set(.isHiddenKey, newValue: newValue) } - } - - /// True for resources whose filename extension is removed from the localized name property. - public var hasHiddenExtension: Bool? { - get { return _get(.hasHiddenExtensionKey) } - set { _set(.hasHiddenExtensionKey, newValue: newValue) } - } - - /// The date the resource was created. - public var creationDate: Date? { - get { return _get(.creationDateKey) } - set { _set(.creationDateKey, newValue: newValue) } - } - - /// The date the resource was last accessed. - public var contentAccessDate: Date? { - get { return _get(.contentAccessDateKey) } - set { _set(.contentAccessDateKey, newValue: newValue) } - } - - /// The time the resource content was last modified. - public var contentModificationDate: Date? { - get { return _get(.contentModificationDateKey) } - set { _set(.contentModificationDateKey, newValue: newValue) } - } - - /// The time the resource's attributes were last modified. - public var attributeModificationDate: Date? { return _get(.attributeModificationDateKey) } - - /// Number of hard links to the resource. - public var linkCount: Int? { return _get(.linkCountKey) } - - /// The resource's parent directory, if any. - public var parentDirectory: URL? { return _get(.parentDirectoryURLKey) } - - /// URL of the volume on which the resource is stored. - public var volume: URL? { return _get(.volumeURLKey) } - - /// Uniform type identifier (UTI) for the resource. - public var typeIdentifier: String? { return _get(.typeIdentifierKey) } - - /// User-visible type or "kind" description. - public var localizedTypeDescription: String? { return _get(.localizedTypeDescriptionKey) } - - /// The label number assigned to the resource. - public var labelNumber: Int? { - get { return _get(.labelNumberKey) } - set { _set(.labelNumberKey, newValue: newValue) } - } - - - /// The user-visible label text. - public var localizedLabel: String? { - get { return _get(.localizedLabelKey) } - } - - /// An identifier which can be used to compare two file system objects for equality using `isEqual`. - /// - /// Two object identifiers are equal if they have the same file system path or if the paths are linked to same inode on the same file system. This identifier is not persistent across system restarts. - public var fileResourceIdentifier: (NSCopying & NSCoding & NSSecureCoding & NSObjectProtocol)? { return _get(.fileResourceIdentifierKey) } - - /// An identifier that can be used to identify the volume the file system object is on. - /// - /// Other objects on the same volume will have the same volume identifier and can be compared using for equality using `isEqual`. This identifier is not persistent across system restarts. - public var volumeIdentifier: (NSCopying & NSCoding & NSSecureCoding & NSObjectProtocol)? { return _get(.volumeIdentifierKey) } - - /// The optimal block size when reading or writing this file's data, or nil if not available. - public var preferredIOBlockSize: Int? { return _get(.preferredIOBlockSizeKey) } - - /// True if this process (as determined by EUID) can read the resource. - public var isReadable: Bool? { return _get(.isReadableKey) } - - /// True if this process (as determined by EUID) can write to the resource. - public var isWritable: Bool? { return _get(.isWritableKey) } - - /// True if this process (as determined by EUID) can execute a file resource or search a directory resource. - public var isExecutable: Bool? { return _get(.isExecutableKey) } - - /// The file system object's security information encapsulated in a FileSecurity object. - public var fileSecurity: NSFileSecurity? { - get { return _get(.fileSecurityKey) } - set { _set(.fileSecurityKey, newValue: newValue) } - } - - /// True if resource should be excluded from backups, false otherwise. - /// - /// This property is only useful for excluding cache and other application support files which are not needed in a backup. Some operations commonly made to user documents will cause this property to be reset to false and so this property should not be used on user documents. - public var isExcludedFromBackup: Bool? { - get { return _get(.isExcludedFromBackupKey) } - set { _set(.isExcludedFromBackupKey, newValue: newValue) } - } - -#if os(macOS) - /// The array of Tag names. - public var tagNames: [String]? { return _get(.tagNamesKey) } -#endif - /// The URL's path as a file system path. - public var path: String? { return _get(.pathKey) } - - /// The URL's path as a canonical absolute file system path. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public var canonicalPath: String? { return _get(.canonicalPathKey) } - - /// True if this URL is a file system trigger directory. Traversing or opening a file system trigger will cause an attempt to mount a file system on the trigger directory. - public var isMountTrigger: Bool? { return _get(.isMountTriggerKey) } - - /// An opaque generation identifier which can be compared using `==` to determine if the data in a document has been modified. - /// - /// For URLs which refer to the same file inode, the generation identifier will change when the data in the file's data fork is changed (changes to extended attributes or other file system metadata do not change the generation identifier). For URLs which refer to the same directory inode, the generation identifier will change when direct children of that directory are added, removed or renamed (changes to the data of the direct children of that directory will not change the generation identifier). The generation identifier is persistent across system restarts. The generation identifier is tied to a specific document on a specific volume and is not transferred when the document is copied to another volume. This property is not supported by all volumes. - @available(macOS 10.10, iOS 8.0, *) - public var generationIdentifier: (NSCopying & NSCoding & NSSecureCoding & NSObjectProtocol)? { return _get(.generationIdentifierKey) } - - /// The document identifier -- a value assigned by the kernel to a document (which can be either a file or directory) and is used to identify the document regardless of where it gets moved on a volume. - /// - /// The document identifier survives "safe save" operations; i.e it is sticky to the path it was assigned to (`replaceItem(at:,withItemAt:,backupItemName:,options:,resultingItem:) throws` is the preferred safe-save API). The document identifier is persistent across system restarts. The document identifier is not transferred when the file is copied. Document identifiers are only unique within a single volume. This property is not supported by all volumes. - @available(macOS 10.10, iOS 8.0, *) - public var documentIdentifier: Int? { return _get(.documentIdentifierKey) } - - /// The date the resource was created, or renamed into or within its parent directory. Note that inconsistent behavior may be observed when this attribute is requested on hard-linked items. This property is not supported by all volumes. - @available(macOS 10.10, iOS 8.0, *) - public var addedToDirectoryDate: Date? { return _get(.addedToDirectoryDateKey) } - -#if os(macOS) - /// The quarantine properties as defined in LSQuarantine.h. To remove quarantine information from a file, pass `nil` as the value when setting this property. - @available(macOS 10.10, *) - public var quarantineProperties: [String : Any]? { - get { - let value = _values[.quarantinePropertiesKey] - // If a caller has caused us to stash NSNull in the dictionary (via set), make sure to return nil instead of NSNull - if value is NSNull { - return nil - } else { - return value as? [String : Any] - } - } - set { - // Use NSNull for nil, a special case for deleting quarantine - // properties - _set(.quarantinePropertiesKey, newValue: newValue ?? NSNull()) - } - } -#endif - - /// Returns the file system object type. - public var fileResourceType: URLFileResourceType? { return _get(.fileResourceTypeKey) } - - /// The user-visible volume format. - public var volumeLocalizedFormatDescription : String? { return _get(.volumeLocalizedFormatDescriptionKey) } - - /// Total volume capacity in bytes. - public var volumeTotalCapacity : Int? { return _get(.volumeTotalCapacityKey) } - - /// Total free space in bytes. - public var volumeAvailableCapacity : Int? { return _get(.volumeAvailableCapacityKey) } - -#if os(macOS) || os(iOS) - /// Total available capacity in bytes for "Important" resources, including space expected to be cleared by purging non-essential and cached resources. "Important" means something that the user or application clearly expects to be present on the local system, but is ultimately replaceable. This would include items that the user has explicitly requested via the UI, and resources that an application requires in order to provide functionality. - /// Examples: A video that the user has explicitly requested to watch but has not yet finished watching or an audio file that the user has requested to download. - /// This value should not be used in determining if there is room for an irreplaceable resource. In the case of irreplaceable resources, always attempt to save the resource regardless of available capacity and handle failure as gracefully as possible. - @available(macOS 10.13, iOS 11.0, *) @available(tvOS, unavailable) @available(watchOS, unavailable) - public var volumeAvailableCapacityForImportantUsage: Int64? { return _get(.volumeAvailableCapacityForImportantUsageKey) } - - /// Total available capacity in bytes for "Opportunistic" resources, including space expected to be cleared by purging non-essential and cached resources. "Opportunistic" means something that the user is likely to want but does not expect to be present on the local system, but is ultimately non-essential and replaceable. This would include items that will be created or downloaded without an explicit request from the user on the current device. - /// Examples: A background download of a newly available episode of a TV series that a user has been recently watching, a piece of content explicitly requested on another device, and a new document saved to a network server by the current user from another device. - @available(macOS 10.13, iOS 11.0, *) @available(tvOS, unavailable) @available(watchOS, unavailable) - public var volumeAvailableCapacityForOpportunisticUsage: Int64? { return _get(.volumeAvailableCapacityForOpportunisticUsageKey) } -#endif - - /// Total number of resources on the volume. - public var volumeResourceCount : Int? { return _get(.volumeResourceCountKey) } - - /// true if the volume format supports persistent object identifiers and can look up file system objects by their IDs. - public var volumeSupportsPersistentIDs : Bool? { return _get(.volumeSupportsPersistentIDsKey) } - - /// true if the volume format supports symbolic links. - public var volumeSupportsSymbolicLinks : Bool? { return _get(.volumeSupportsSymbolicLinksKey) } - - /// true if the volume format supports hard links. - public var volumeSupportsHardLinks : Bool? { return _get(.volumeSupportsHardLinksKey) } - - /// true if the volume format supports a journal used to speed recovery in case of unplanned restart (such as a power outage or crash). This does not necessarily mean the volume is actively using a journal. - public var volumeSupportsJournaling : Bool? { return _get(.volumeSupportsJournalingKey) } - - /// true if the volume is currently using a journal for speedy recovery after an unplanned restart. - public var volumeIsJournaling : Bool? { return _get(.volumeIsJournalingKey) } - - /// true if the volume format supports sparse files, that is, files which can have 'holes' that have never been written to, and thus do not consume space on disk. A sparse file may have an allocated size on disk that is less than its logical length. - public var volumeSupportsSparseFiles : Bool? { return _get(.volumeSupportsSparseFilesKey) } - - /// For security reasons, parts of a file (runs) that have never been written to must appear to contain zeroes. true if the volume keeps track of allocated but unwritten runs of a file so that it can substitute zeroes without actually writing zeroes to the media. - public var volumeSupportsZeroRuns : Bool? { return _get(.volumeSupportsZeroRunsKey) } - - /// true if the volume format treats upper and lower case characters in file and directory names as different. Otherwise an upper case character is equivalent to a lower case character, and you can't have two names that differ solely in the case of the characters. - public var volumeSupportsCaseSensitiveNames : Bool? { return _get(.volumeSupportsCaseSensitiveNamesKey) } - - /// true if the volume format preserves the case of file and directory names. Otherwise the volume may change the case of some characters (typically making them all upper or all lower case). - public var volumeSupportsCasePreservedNames : Bool? { return _get(.volumeSupportsCasePreservedNamesKey) } - - /// true if the volume supports reliable storage of times for the root directory. - public var volumeSupportsRootDirectoryDates : Bool? { return _get(.volumeSupportsRootDirectoryDatesKey) } - - /// true if the volume supports returning volume size values (`volumeTotalCapacity` and `volumeAvailableCapacity`). - public var volumeSupportsVolumeSizes : Bool? { return _get(.volumeSupportsVolumeSizesKey) } - - /// true if the volume can be renamed. - public var volumeSupportsRenaming : Bool? { return _get(.volumeSupportsRenamingKey) } - - /// true if the volume implements whole-file flock(2) style advisory locks, and the O_EXLOCK and O_SHLOCK flags of the open(2) call. - public var volumeSupportsAdvisoryFileLocking : Bool? { return _get(.volumeSupportsAdvisoryFileLockingKey) } - - /// true if the volume implements extended security (ACLs). - public var volumeSupportsExtendedSecurity : Bool? { return _get(.volumeSupportsExtendedSecurityKey) } - - /// true if the volume should be visible via the GUI (i.e., appear on the Desktop as a separate volume). - public var volumeIsBrowsable : Bool? { return _get(.volumeIsBrowsableKey) } - - /// The largest file size (in bytes) supported by this file system, or nil if this cannot be determined. - public var volumeMaximumFileSize : Int? { return _get(.volumeMaximumFileSizeKey) } - - /// true if the volume's media is ejectable from the drive mechanism under software control. - public var volumeIsEjectable : Bool? { return _get(.volumeIsEjectableKey) } - - /// true if the volume's media is removable from the drive mechanism. - public var volumeIsRemovable : Bool? { return _get(.volumeIsRemovableKey) } - - /// true if the volume's device is connected to an internal bus, false if connected to an external bus, or nil if not available. - public var volumeIsInternal : Bool? { return _get(.volumeIsInternalKey) } - - /// true if the volume is automounted. Note: do not mistake this with the functionality provided by kCFURLVolumeSupportsBrowsingKey. - public var volumeIsAutomounted : Bool? { return _get(.volumeIsAutomountedKey) } - - /// true if the volume is stored on a local device. - public var volumeIsLocal : Bool? { return _get(.volumeIsLocalKey) } - - /// true if the volume is read-only. - public var volumeIsReadOnly : Bool? { return _get(.volumeIsReadOnlyKey) } - - /// The volume's creation date, or nil if this cannot be determined. - public var volumeCreationDate : Date? { return _get(.volumeCreationDateKey) } - - /// The `URL` needed to remount a network volume, or nil if not available. - public var volumeURLForRemounting : URL? { return _get(.volumeURLForRemountingKey) } - - /// The volume's persistent `UUID` as a string, or nil if a persistent `UUID` is not available for the volume. - public var volumeUUIDString : String? { return _get(.volumeUUIDStringKey) } - - /// The name of the volume - public var volumeName : String? { - get { return _get(.volumeNameKey) } - set { _set(.volumeNameKey, newValue: newValue) } - } - - /// The user-presentable name of the volume - public var volumeLocalizedName : String? { return _get(.volumeLocalizedNameKey) } - - /// true if the volume is encrypted. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public var volumeIsEncrypted : Bool? { return _get(.volumeIsEncryptedKey) } - - /// true if the volume is the root filesystem. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public var volumeIsRootFileSystem : Bool? { return _get(.volumeIsRootFileSystemKey) } - - /// true if the volume supports transparent decompression of compressed files using decmpfs. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public var volumeSupportsCompression : Bool? { return _get(.volumeSupportsCompressionKey) } - - /// true if the volume supports clonefile(2). - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public var volumeSupportsFileCloning : Bool? { return _get(.volumeSupportsFileCloningKey) } - - /// true if the volume supports renamex_np(2)'s RENAME_SWAP option. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public var volumeSupportsSwapRenaming : Bool? { return _get(.volumeSupportsSwapRenamingKey) } - - /// true if the volume supports renamex_np(2)'s RENAME_EXCL option. - @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public var volumeSupportsExclusiveRenaming : Bool? { return _get(.volumeSupportsExclusiveRenamingKey) } - - /// true if the volume supports making files immutable with isUserImmutable or isSystemImmutable. - @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) - public var volumeSupportsImmutableFiles : Bool? { return _get(.volumeSupportsImmutableFilesKey) } - - /// true if the volume supports setting POSIX access permissions with fileSecurity. - @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) - public var volumeSupportsAccessPermissions : Bool? { return _get(.volumeSupportsAccessPermissionsKey) } - - /// true if this item is synced to the cloud, false if it is only a local file. - public var isUbiquitousItem : Bool? { return _get(.isUbiquitousItemKey) } - - /// true if this item has conflicts outstanding. - public var ubiquitousItemHasUnresolvedConflicts : Bool? { return _get(.ubiquitousItemHasUnresolvedConflictsKey) } - - /// true if data is being downloaded for this item. - public var ubiquitousItemIsDownloading : Bool? { return _get(.ubiquitousItemIsDownloadingKey) } - - /// true if there is data present in the cloud for this item. - public var ubiquitousItemIsUploaded : Bool? { return _get(.ubiquitousItemIsUploadedKey) } - - /// true if data is being uploaded for this item. - public var ubiquitousItemIsUploading : Bool? { return _get(.ubiquitousItemIsUploadingKey) } - - /// returns the download status of this item. - public var ubiquitousItemDownloadingStatus : URLUbiquitousItemDownloadingStatus? { return _get(.ubiquitousItemDownloadingStatusKey) } - - /// returns the error when downloading the item from iCloud failed, see the NSUbiquitousFile section in FoundationErrors.h - public var ubiquitousItemDownloadingError : NSError? { return _get(.ubiquitousItemDownloadingErrorKey) } - - /// returns the error when uploading the item to iCloud failed, see the NSUbiquitousFile section in FoundationErrors.h - public var ubiquitousItemUploadingError : NSError? { return _get(.ubiquitousItemUploadingErrorKey) } - - /// returns whether a download of this item has already been requested with an API like `startDownloadingUbiquitousItem(at:) throws`. - @available(macOS 10.10, iOS 8.0, *) - public var ubiquitousItemDownloadRequested : Bool? { return _get(.ubiquitousItemDownloadRequestedKey) } - - /// returns the name of this item's container as displayed to users. - @available(macOS 10.10, iOS 8.0, *) - public var ubiquitousItemContainerDisplayName : String? { return _get(.ubiquitousItemContainerDisplayNameKey) } - -#if os(macOS) || os(iOS) - // true if ubiquitous item is shared. - @available(macOS 10.13, iOS 11.0, *) @available(tvOS, unavailable) @available(watchOS, unavailable) - public var ubiquitousItemIsShared: Bool? { return _get(.ubiquitousItemIsSharedKey) } - - // The current user's role for this shared item, or nil if not shared - @available(macOS 10.13, iOS 11.0, *) @available(tvOS, unavailable) @available(watchOS, unavailable) - public var ubiquitousSharedItemCurrentUserRole: URLUbiquitousSharedItemRole? { return _get(.ubiquitousSharedItemCurrentUserRoleKey) } - - // The permissions for the current user, or nil if not shared. - @available(macOS 10.13, iOS 11.0, *) @available(tvOS, unavailable) @available(watchOS, unavailable) - public var ubiquitousSharedItemCurrentUserPermissions: URLUbiquitousSharedItemPermissions? { return _get(.ubiquitousSharedItemCurrentUserPermissionsKey) } - - // The name components for the owner, or nil if not shared. - @available(macOS 10.13, iOS 11.0, *) @available(tvOS, unavailable) @available(watchOS, unavailable) - public var ubiquitousSharedItemOwnerNameComponents: PersonNameComponents? { return _get(.ubiquitousSharedItemOwnerNameComponentsKey) } - - // The name components for the most recent editor, or nil if not shared. - @available(macOS 10.13, iOS 11.0, *) @available(tvOS, unavailable) @available(watchOS, unavailable) - public var ubiquitousSharedItemMostRecentEditorNameComponents: PersonNameComponents? { return _get(.ubiquitousSharedItemMostRecentEditorNameComponentsKey) } -#endif - -#if !os(macOS) - /// The protection level for this file - @available(iOS 9.0, *) - public var fileProtection : URLFileProtection? { return _get(.fileProtectionKey) } -#endif - - /// Total file size in bytes - /// - /// - note: Only applicable to regular files. - public var fileSize : Int? { return _get(.fileSizeKey) } - - /// Total size allocated on disk for the file in bytes (number of blocks times block size) - /// - /// - note: Only applicable to regular files. - public var fileAllocatedSize : Int? { return _get(.fileAllocatedSizeKey) } - - /// Total displayable size of the file in bytes (this may include space used by metadata), or nil if not available. - /// - /// - note: Only applicable to regular files. - public var totalFileSize : Int? { return _get(.totalFileSizeKey) } - - /// Total allocated size of the file in bytes (this may include space used by metadata), or nil if not available. This can be less than the value returned by `totalFileSize` if the resource is compressed. - /// - /// - note: Only applicable to regular files. - public var totalFileAllocatedSize : Int? { return _get(.totalFileAllocatedSizeKey) } - - /// true if the resource is a Finder alias file or a symlink, false otherwise - /// - /// - note: Only applicable to regular files. - public var isAliasFile : Bool? { return _get(.isAliasFileKey) } - -} - -/** - A URL is a type that can potentially contain the location of a resource on a remote server, the path of a local file on disk, or even an arbitrary piece of encoded data. - - You can construct URLs and access their parts. For URLs that represent local files, you can also manipulate properties of those files directly, such as changing the file's last modification date. Finally, you can pass URLs to other APIs to retrieve the contents of those URLs. For example, you can use the URLSession classes to access the contents of remote resources, as described in URL Session Programming Guide. - - URLs are the preferred way to refer to local files. Most objects that read data from or write data to a file have methods that accept a URL instead of a pathname as the file reference. For example, you can get the contents of a local file URL as `String` by calling `func init(contentsOf:encoding) throws`, or as a `Data` by calling `func init(contentsOf:options) throws`. -*/ -public struct URL : ReferenceConvertible, Equatable { - public typealias ReferenceType = NSURL - private var _url: NSURL - - public typealias BookmarkResolutionOptions = NSURL.BookmarkResolutionOptions - public typealias BookmarkCreationOptions = NSURL.BookmarkCreationOptions - - /// Initialize with string. - /// - /// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string). - public init?(string: __shared String) { - guard !string.isEmpty, let inner = NSURL(string: string) else { return nil } - _url = URL._converted(from: inner) - } - - /// Initialize with string, relative to another URL. - /// - /// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string). - public init?(string: __shared String, relativeTo url: __shared URL?) { - guard !string.isEmpty, let inner = NSURL(string: string, relativeTo: url) else { return nil } - _url = URL._converted(from: inner) - } - - /// Initializes a newly created file URL referencing the local file or directory at path, relative to a base URL. - /// - /// If an empty string is used for the path, then the path is assumed to be ".". - /// - note: This function avoids an extra file system access to check if the file URL is a directory. You should use it if you know the answer already. - @available(macOS 10.11, iOS 9.0, *) - public init(fileURLWithPath path: __shared String, isDirectory: Bool, relativeTo base: __shared URL?) { - _url = URL._converted(from: NSURL(fileURLWithPath: path.isEmpty ? "." : path, isDirectory: isDirectory, relativeTo: base)) - } - - /// Initializes a newly created file URL referencing the local file or directory at path, relative to a base URL. - /// - /// If an empty string is used for the path, then the path is assumed to be ".". - @available(macOS 10.11, iOS 9.0, *) - public init(fileURLWithPath path: __shared String, relativeTo base: __shared URL?) { - _url = URL._converted(from: NSURL(fileURLWithPath: path.isEmpty ? "." : path, relativeTo: base)) - } - - /// Initializes a newly created file URL referencing the local file or directory at path. - /// - /// If an empty string is used for the path, then the path is assumed to be ".". - /// - note: This function avoids an extra file system access to check if the file URL is a directory. You should use it if you know the answer already. - public init(fileURLWithPath path: __shared String, isDirectory: Bool) { - _url = URL._converted(from: NSURL(fileURLWithPath: path.isEmpty ? "." : path, isDirectory: isDirectory)) - } - - /// Initializes a newly created file URL referencing the local file or directory at path. - /// - /// If an empty string is used for the path, then the path is assumed to be ".". - public init(fileURLWithPath path: __shared String) { - _url = URL._converted(from: NSURL(fileURLWithPath: path.isEmpty ? "." : path)) - } - - /// Initializes a newly created URL using the contents of the given data, relative to a base URL. - /// - /// If the data representation is not a legal URL string as ASCII bytes, the URL object may not behave as expected. If the URL cannot be formed then this will return nil. - @available(macOS 10.11, iOS 9.0, *) - public init?(dataRepresentation: __shared Data, relativeTo url: __shared URL?, isAbsolute: Bool = false) { - guard !dataRepresentation.isEmpty else { return nil } - - if isAbsolute { - _url = URL._converted(from: NSURL(absoluteURLWithDataRepresentation: dataRepresentation, relativeTo: url)) - } else { - _url = URL._converted(from: NSURL(dataRepresentation: dataRepresentation, relativeTo: url)) - } - } - - /// Initializes a URL that refers to a location specified by resolving bookmark data. - @available(swift, obsoleted: 4.2) - public init?(resolvingBookmarkData data: __shared Data, options: BookmarkResolutionOptions = [], relativeTo url: __shared URL? = nil, bookmarkDataIsStale: inout Bool) throws { - var stale : ObjCBool = false - _url = URL._converted(from: try NSURL(resolvingBookmarkData: data, options: options, relativeTo: url, bookmarkDataIsStale: &stale)) - bookmarkDataIsStale = stale.boolValue - } - - /// Initializes a URL that refers to a location specified by resolving bookmark data. - @available(swift, introduced: 4.2) - public init(resolvingBookmarkData data: __shared Data, options: BookmarkResolutionOptions = [], relativeTo url: __shared URL? = nil, bookmarkDataIsStale: inout Bool) throws { - var stale : ObjCBool = false - _url = URL._converted(from: try NSURL(resolvingBookmarkData: data, options: options, relativeTo: url, bookmarkDataIsStale: &stale)) - bookmarkDataIsStale = stale.boolValue - } - - /// Creates and initializes an NSURL that refers to the location specified by resolving the alias file at url. If the url argument does not refer to an alias file as defined by the NSURLIsAliasFileKey property, the NSURL returned is the same as url argument. This method fails and returns nil if the url argument is unreachable, or if the original file or directory could not be located or is not reachable, or if the original file or directory is on a volume that could not be located or mounted. The URLBookmarkResolutionWithSecurityScope option is not supported by this method. - @available(macOS 10.10, iOS 8.0, *) - public init(resolvingAliasFileAt url: __shared URL, options: BookmarkResolutionOptions = []) throws { - self.init(reference: try NSURL(resolvingAliasFileAt: url, options: options)) - } - - /// Initializes a newly created URL referencing the local file or directory at the file system representation of the path. File system representation is a null-terminated C string with canonical UTF-8 encoding. - public init(fileURLWithFileSystemRepresentation path: UnsafePointer, isDirectory: Bool, relativeTo baseURL: __shared URL?) { - _url = URL._converted(from: NSURL(fileURLWithFileSystemRepresentation: path, isDirectory: isDirectory, relativeTo: baseURL)) - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(_url) - } - - // MARK: - - - /// Returns the data representation of the URL's relativeString. - /// - /// If the URL was initialized with `init?(dataRepresentation:relativeTo:isAbsolute:)`, the data representation returned are the same bytes as those used at initialization; otherwise, the data representation returned are the bytes of the `relativeString` encoded with UTF8 string encoding. - @available(macOS 10.11, iOS 9.0, *) - public var dataRepresentation: Data { - return _url.dataRepresentation - } - - // MARK: - - - // Future implementation note: - // NSURL (really CFURL, which provides its implementation) has quite a few quirks in its processing of some more esoteric (and some not so esoteric) strings. We would like to move much of this over to the more modern NSURLComponents, but binary compat concerns have made this difficult. - // Hopefully soon, we can replace some of the below delegation to NSURL with delegation to NSURLComponents instead. It cannot be done piecemeal, because otherwise we will get inconsistent results from the API. - - /// Returns the absolute string for the URL. - public var absoluteString: String { - if let string = _url.absoluteString { - return string - } else { - // This should never fail for non-file reference URLs - return "" - } - } - - /// The relative portion of a URL. - /// - /// If `baseURL` is nil, or if the receiver is itself absolute, this is the same as `absoluteString`. - public var relativeString: String { - return _url.relativeString - } - - /// Returns the base URL. - /// - /// If the URL is itself absolute, then this value is nil. - public var baseURL: URL? { - return _url.baseURL - } - - /// Returns the absolute URL. - /// - /// If the URL is itself absolute, this will return self. - public var absoluteURL: URL { - if let url = _url.absoluteURL { - return url - } else { - // This should never fail for non-file reference URLs - return self - } - } - - // MARK: - - - /// Returns the scheme of the URL. - public var scheme: String? { - return _url.scheme - } - - /// Returns true if the scheme is `file:`. - public var isFileURL: Bool { - return _url.isFileURL - } - - // This thing was never really part of the URL specs - @available(*, unavailable, message: "Use `path`, `query`, and `fragment` instead") - public var resourceSpecifier: String { - fatalError() - } - - /// If the URL conforms to RFC 1808 (the most common form of URL), returns the host component of the URL; otherwise it returns nil. - /// - /// - note: This function will resolve against the base `URL`. - public var host: String? { - return _url.host - } - - /// If the URL conforms to RFC 1808 (the most common form of URL), returns the port component of the URL; otherwise it returns nil. - /// - /// - note: This function will resolve against the base `URL`. - public var port: Int? { - return _url.port?.intValue - } - - /// If the URL conforms to RFC 1808 (the most common form of URL), returns the user component of the URL; otherwise it returns nil. - /// - /// - note: This function will resolve against the base `URL`. - public var user: String? { - return _url.user - } - - /// If the URL conforms to RFC 1808 (the most common form of URL), returns the password component of the URL; otherwise it returns nil. - /// - /// - note: This function will resolve against the base `URL`. - public var password: String? { - return _url.password - } - - /// If the URL conforms to RFC 1808 (the most common form of URL), returns the path component of the URL; otherwise it returns an empty string. - /// - /// If the URL contains a parameter string, it is appended to the path with a `;`. - /// - note: This function will resolve against the base `URL`. - /// - returns: The path, or an empty string if the URL has an empty path. - public var path: String { - if let parameterString = _url.parameterString { - if let path = _url.path { - return path + ";" + parameterString - } else { - return ";" + parameterString - } - } else if let path = _url.path { - return path - } else { - return "" - } - } - - /// If the URL conforms to RFC 1808 (the most common form of URL), returns the relative path of the URL; otherwise it returns nil. - /// - /// This is the same as path if baseURL is nil. - /// If the URL contains a parameter string, it is appended to the path with a `;`. - /// - /// - note: This function will resolve against the base `URL`. - /// - returns: The relative path, or an empty string if the URL has an empty path. - public var relativePath: String { - if let parameterString = _url.parameterString { - if let path = _url.relativePath { - return path + ";" + parameterString - } else { - return ";" + parameterString - } - } else if let path = _url.relativePath { - return path - } else { - return "" - } - } - - /// If the URL conforms to RFC 1808 (the most common form of URL), returns the fragment component of the URL; otherwise it returns nil. - /// - /// - note: This function will resolve against the base `URL`. - public var fragment: String? { - return _url.fragment - } - - @available(*, unavailable, message: "use the 'path' property") - public var parameterString: String? { - fatalError() - } - - /// If the URL conforms to RFC 1808 (the most common form of URL), returns the query of the URL; otherwise it returns nil. - /// - /// - note: This function will resolve against the base `URL`. - public var query: String? { - return _url.query - } - - /// Returns true if the URL path represents a directory. - @available(macOS 10.11, iOS 9.0, *) - public var hasDirectoryPath: Bool { - return _url.hasDirectoryPath - } - - /// Passes the URL's path in file system representation to `block`. - /// - /// File system representation is a null-terminated C string with canonical UTF-8 encoding. - /// - note: The pointer is not valid outside the context of the block. - @available(macOS 10.9, iOS 7.0, *) - public func withUnsafeFileSystemRepresentation(_ block: (UnsafePointer?) throws -> ResultType) rethrows -> ResultType { - return try block(_url.fileSystemRepresentation) - } - - // MARK: - - // MARK: Path manipulation - - /// Returns the path components of the URL, or an empty array if the path is an empty string. - public var pathComponents: [String] { - // In accordance with our above change to never return a nil path, here we return an empty array. - return _url.pathComponents ?? [] - } - - /// Returns the last path component of the URL, or an empty string if the path is an empty string. - public var lastPathComponent: String { - return _url.lastPathComponent ?? "" - } - - /// Returns the path extension of the URL, or an empty string if the path is an empty string. - public var pathExtension: String { - return _url.pathExtension ?? "" - } - - /// Returns a URL constructed by appending the given path component to self. - /// - /// - parameter pathComponent: The path component to add. - /// - parameter isDirectory: If `true`, then a trailing `/` is added to the resulting path. - public func appendingPathComponent(_ pathComponent: String, isDirectory: Bool) -> URL { - if let result = _url.appendingPathComponent(pathComponent, isDirectory: isDirectory) { - return result - } else { - // Now we need to do something more expensive - if var c = URLComponents(url: self, resolvingAgainstBaseURL: true) { - let path = (c.path as NSString).appendingPathComponent(pathComponent) - c.path = isDirectory ? path + "/" : path - - if let result = c.url { - return result - } else { - // Couldn't get url from components - // Ultimate fallback: - return self - } - } else { - return self - } - } - } - - /// Returns a URL constructed by appending the given path component to self. - /// - /// - note: This function performs a file system operation to determine if the path component is a directory. If so, it will append a trailing `/`. If you know in advance that the path component is a directory or not, then use `func appendingPathComponent(_:isDirectory:)`. - /// - parameter pathComponent: The path component to add. - public func appendingPathComponent(_ pathComponent: String) -> URL { - if let result = _url.appendingPathComponent(pathComponent) { - return result - } else { - // Now we need to do something more expensive - if var c = URLComponents(url: self, resolvingAgainstBaseURL: true) { - c.path = (c.path as NSString).appendingPathComponent(pathComponent) - - if let result = c.url { - return result - } else { - // Couldn't get url from components - // Ultimate fallback: - return self - } - } else { - // Ultimate fallback: - return self - } - } - } - - /// Returns a URL constructed by removing the last path component of self. - /// - /// This function may either remove a path component or append `/..`. - /// - /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will return the URL unchanged. - public func deletingLastPathComponent() -> URL { - // This is a slight behavior change from NSURL, but better than returning "http://www.example.com../". - guard !path.isEmpty, let result = _url.deletingLastPathComponent.map({ URL(reference: $0 as NSURL) }) else { return self } - return result - } - - /// Returns a URL constructed by appending the given path extension to self. - /// - /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will return the URL unchanged. - /// - /// Certain special characters (for example, Unicode Right-To-Left marks) cannot be used as path extensions. If any of those are contained in `pathExtension`, the function will return the URL unchanged. - /// - parameter pathExtension: The extension to append. - public func appendingPathExtension(_ pathExtension: String) -> URL { - guard !path.isEmpty, let result = _url.appendingPathExtension(pathExtension) else { return self } - return result - } - - /// Returns a URL constructed by removing any path extension. - /// - /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will return the URL unchanged. - public func deletingPathExtension() -> URL { - guard !path.isEmpty, let result = _url.deletingPathExtension.map({ URL(reference: $0 as NSURL) }) else { return self } - return result - } - - /// Appends a path component to the URL. - /// - /// - parameter pathComponent: The path component to add. - /// - parameter isDirectory: Use `true` if the resulting path is a directory. - public mutating func appendPathComponent(_ pathComponent: String, isDirectory: Bool) { - self = appendingPathComponent(pathComponent, isDirectory: isDirectory) - } - - /// Appends a path component to the URL. - /// - /// - note: This function performs a file system operation to determine if the path component is a directory. If so, it will append a trailing `/`. If you know in advance that the path component is a directory or not, then use `func appendingPathComponent(_:isDirectory:)`. - /// - parameter pathComponent: The path component to add. - public mutating func appendPathComponent(_ pathComponent: String) { - self = appendingPathComponent(pathComponent) - } - - /// Appends the given path extension to self. - /// - /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will do nothing. - /// Certain special characters (for example, Unicode Right-To-Left marks) cannot be used as path extensions. If any of those are contained in `pathExtension`, the function will return the URL unchanged. - /// - parameter pathExtension: The extension to append. - public mutating func appendPathExtension(_ pathExtension: String) { - self = appendingPathExtension(pathExtension) - } - - /// Returns a URL constructed by removing the last path component of self. - /// - /// This function may either remove a path component or append `/..`. - /// - /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will do nothing. - public mutating func deleteLastPathComponent() { - self = deletingLastPathComponent() - } - - - /// Returns a URL constructed by removing any path extension. - /// - /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will do nothing. - public mutating func deletePathExtension() { - self = deletingPathExtension() - } - - /// Returns a `URL` with any instances of ".." or "." removed from its path. - public var standardized : URL { - // The NSURL API can only return nil in case of file reference URL, which we should not be - guard let result = _url.standardized.map({ URL(reference: $0 as NSURL) }) else { return self } - return result - } - - /// Standardizes the path of a file URL. - /// - /// If the `isFileURL` is false, this method does nothing. - public mutating func standardize() { - self = self.standardized - } - - /// Standardizes the path of a file URL. - /// - /// If the `isFileURL` is false, this method returns `self`. - public var standardizedFileURL : URL { - // NSURL should not return nil here unless this is a file reference URL, which should be impossible - guard let result = _url.standardizingPath.map({ URL(reference: $0 as NSURL) }) else { return self } - return result - } - - /// Resolves any symlinks in the path of a file URL. - /// - /// If the `isFileURL` is false, this method returns `self`. - public func resolvingSymlinksInPath() -> URL { - // NSURL should not return nil here unless this is a file reference URL, which should be impossible - guard let result = _url.resolvingSymlinksInPath.map({ URL(reference: $0 as NSURL) }) else { return self } - return result - } - - /// Resolves any symlinks in the path of a file URL. - /// - /// If the `isFileURL` is false, this method does nothing. - public mutating func resolveSymlinksInPath() { - self = self.resolvingSymlinksInPath() - } - - // MARK: - Reachability - - /// Returns whether the URL's resource exists and is reachable. - /// - /// This method synchronously checks if the resource's backing store is reachable. Checking reachability is appropriate when making decisions that do not require other immediate operations on the resource, e.g. periodic maintenance of UI state that depends on the existence of a specific document. When performing operations such as opening a file or copying resource properties, it is more efficient to simply try the operation and handle failures. This method is currently applicable only to URLs for file system resources. For other URL types, `false` is returned. - public func checkResourceIsReachable() throws -> Bool { - var error : NSError? - let result = _url.checkResourceIsReachableAndReturnError(&error) - if let e = error { - throw e - } else { - return result - } - } - - /// Returns whether the promised item URL's resource exists and is reachable. - /// - /// This method synchronously checks if the resource's backing store is reachable. Checking reachability is appropriate when making decisions that do not require other immediate operations on the resource, e.g. periodic maintenance of UI state that depends on the existence of a specific document. When performing operations such as opening a file or copying resource properties, it is more efficient to simply try the operation and handle failures. This method is currently applicable only to URLs for file system resources. For other URL types, `false` is returned. - @available(macOS 10.10, iOS 8.0, *) - public func checkPromisedItemIsReachable() throws -> Bool { - var error : NSError? - let result = _url.checkPromisedItemIsReachableAndReturnError(&error) - if let e = error { - throw e - } else { - return result - } - } - - // MARK: - Resource Values - - /// Sets the resource value identified by a given resource key. - /// - /// This method writes the new resource values out to the backing store. Attempts to set a read-only resource property or to set a resource property not supported by the resource are ignored and are not considered errors. This method is currently applicable only to URLs for file system resources. - /// - /// `URLResourceValues` keeps track of which of its properties have been set. Those values are the ones used by this function to determine which properties to write. - public mutating func setResourceValues(_ values: URLResourceValues) throws { - try _url.setResourceValues(values._values) - } - - /// Return a collection of resource values identified by the given resource keys. - /// - /// This method first checks if the URL object already caches the resource value. If so, it returns the cached resource value to the caller. If not, then this method synchronously obtains the resource value from the backing store, adds the resource value to the URL object's cache, and returns the resource value to the caller. The type of the resource value varies by resource property (see resource key definitions). If this method does not throw and the resulting value in the `URLResourceValues` is populated with nil, it means the resource property is not available for the specified resource and no errors occurred when determining the resource property was not available. This method is currently applicable only to URLs for file system resources. - /// - /// When this function is used from the main thread, resource values cached by the URL (except those added as temporary properties) are removed the next time the main thread's run loop runs. `func removeCachedResourceValue(forKey:)` and `func removeAllCachedResourceValues()` also may be used to remove cached resource values. - /// - /// Only the values for the keys specified in `keys` will be populated. - public func resourceValues(forKeys keys: Set) throws -> URLResourceValues { - return URLResourceValues(keys: keys, values: try _url.resourceValues(forKeys: Array(keys))) - } - - /// Sets a temporary resource value on the URL object. - /// - /// Temporary resource values are for client use. Temporary resource values exist only in memory and are never written to the resource's backing store. Once set, a temporary resource value can be copied from the URL object with `func resourceValues(forKeys:)`. The values are stored in the loosely-typed `allValues` dictionary property. - /// - /// To remove a temporary resource value from the URL object, use `func removeCachedResourceValue(forKey:)`. Care should be taken to ensure the key that identifies a temporary resource value is unique and does not conflict with system defined keys (using reverse domain name notation in your temporary resource value keys is recommended). This method is currently applicable only to URLs for file system resources. - public mutating func setTemporaryResourceValue(_ value : Any, forKey key: URLResourceKey) { - _url.setTemporaryResourceValue(value, forKey: key) - } - - /// Removes all cached resource values and all temporary resource values from the URL object. - /// - /// This method is currently applicable only to URLs for file system resources. - public mutating func removeAllCachedResourceValues() { - _url.removeAllCachedResourceValues() - } - - /// Removes the cached resource value identified by a given resource value key from the URL object. - /// - /// Removing a cached resource value may remove other cached resource values because some resource values are cached as a set of values, and because some resource values depend on other resource values (temporary resource values have no dependencies). This method is currently applicable only to URLs for file system resources. - public mutating func removeCachedResourceValue(forKey key: URLResourceKey) { - _url.removeCachedResourceValue(forKey: key) - } - - /// Get resource values from URLs of 'promised' items. - /// - /// A promised item is not guaranteed to have its contents in the file system until you use `FileCoordinator` to perform a coordinated read on its URL, which causes the contents to be downloaded or otherwise generated. Promised item URLs are returned by various APIs, including currently: - /// NSMetadataQueryUbiquitousDataScope - /// NSMetadataQueryUbiquitousDocumentsScope - /// A `FilePresenter` presenting the contents of the directory located by -URLForUbiquitousContainerIdentifier: or a subdirectory thereof - /// - /// The following methods behave identically to their similarly named methods above (`func resourceValues(forKeys:)`, etc.), except that they allow you to get resource values and check for presence regardless of whether the promised item's contents currently exist at the URL. You must use these APIs instead of the normal URL resource value APIs if and only if any of the following are true: - /// You are using a URL that you know came directly from one of the above APIs - /// You are inside the accessor block of a coordinated read or write that used NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly, NSFileCoordinatorWritingForDeleting, NSFileCoordinatorWritingForMoving, or NSFileCoordinatorWritingContentIndependentMetadataOnly - /// - /// Most of the URL resource value keys will work with these APIs. However, there are some that are tied to the item's contents that will not work, such as `contentAccessDateKey` or `generationIdentifierKey`. If one of these keys is used, the method will return a `URLResourceValues` value, but the value for that property will be nil. - @available(macOS 10.10, iOS 8.0, *) - public func promisedItemResourceValues(forKeys keys: Set) throws -> URLResourceValues { - return URLResourceValues(keys: keys, values: try _url.promisedItemResourceValues(forKeys: Array(keys))) - } - - @available(*, unavailable, message: "Use struct URLResourceValues and URL.setResourceValues(_:) instead") - public func setResourceValue(_ value: AnyObject?, forKey key: URLResourceKey) throws { - fatalError() - } - - @available(*, unavailable, message: "Use struct URLResourceValues and URL.setResourceValues(_:) instead") - public func setResourceValues(_ keyedValues: [URLResourceKey : AnyObject]) throws { - fatalError() - } - - @available(*, unavailable, message: "Use struct URLResourceValues and URL.setResourceValues(_:) instead") - public func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer, forKey key: URLResourceKey) throws { - fatalError() - } - - // MARK: - Bookmarks and Alias Files - - /// Returns bookmark data for the URL, created with specified options and resource values. - public func bookmarkData(options: BookmarkCreationOptions = [], includingResourceValuesForKeys keys: Set? = nil, relativeTo url: URL? = nil) throws -> Data { - let result = try _url.bookmarkData(options: options, includingResourceValuesForKeys: keys.flatMap { Array($0) }, relativeTo: url) - return result - } - - /// Returns the resource values for properties identified by a specified array of keys contained in specified bookmark data. If the result dictionary does not contain a resource value for one or more of the requested resource keys, it means those resource properties are not available in the bookmark data. - public static func resourceValues(forKeys keys: Set, fromBookmarkData data: Data) -> URLResourceValues? { - return NSURL.resourceValues(forKeys: Array(keys), fromBookmarkData: data).map { URLResourceValues(keys: keys, values: $0) } - } - - /// Creates an alias file on disk at a specified location with specified bookmark data. bookmarkData must have been created with the URLBookmarkCreationSuitableForBookmarkFile option. bookmarkFileURL must either refer to an existing file (which will be overwritten), or to location in an existing directory. - public static func writeBookmarkData(_ data : Data, to url: URL) throws { - // Options are unused - try NSURL.writeBookmarkData(data, to: url, options: 0) - } - - /// Initializes and returns bookmark data derived from an alias file pointed to by a specified URL. If bookmarkFileURL refers to an alias file created prior to OS X v10.6 that contains Alias Manager information but no bookmark data, this method synthesizes bookmark data for the file. - public static func bookmarkData(withContentsOf url: URL) throws -> Data { - let result = try NSURL.bookmarkData(withContentsOf: url) - return result - } - - /// Given an NSURL created by resolving a bookmark data created with security scope, make the resource referenced by the url accessible to the process. When access to this resource is no longer needed the client must call stopAccessingSecurityScopedResource. Each call to startAccessingSecurityScopedResource must be balanced with a call to stopAccessingSecurityScopedResource (Note: this is not reference counted). - @available(macOS 10.7, iOS 8.0, *) - public func startAccessingSecurityScopedResource() -> Bool { - return _url.startAccessingSecurityScopedResource() - } - - /// Revokes the access granted to the url by a prior successful call to startAccessingSecurityScopedResource. - @available(macOS 10.7, iOS 8.0, *) - public func stopAccessingSecurityScopedResource() { - _url.stopAccessingSecurityScopedResource() - } - - // MARK: - Bridging Support - - /// We must not store an NSURL without running it through this function. This makes sure that we do not hold a file reference URL, which changes the nullability of many NSURL functions. - private static func _converted(from url: NSURL) -> NSURL { - // Future readers: file reference URL here is not the same as playgrounds "file reference" - if url.isFileReferenceURL() { - // Convert to a file path URL, or use an invalid scheme - return (url.filePathURL ?? URL(string: "com-apple-unresolvable-file-reference-url:")!) as NSURL - } else { - return url - } - } - - private init(reference: __shared NSURL) { - _url = URL._converted(from: reference).copy() as! NSURL - } - - private var reference: NSURL { - return _url - } - - public static func ==(lhs: URL, rhs: URL) -> Bool { - return lhs.reference.isEqual(rhs.reference) - } -} - -extension URL : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSURL { - return _url - } - - public static func _forceBridgeFromObjectiveC(_ source: NSURL, result: inout URL?) { - if !_conditionallyBridgeFromObjectiveC(source, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ source: NSURL, result: inout URL?) -> Bool { - result = URL(reference: source) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSURL?) -> URL { - var result: URL? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -extension URL : CustomStringConvertible, CustomDebugStringConvertible { - public var description: String { - return _url.description - } - - public var debugDescription: String { - return _url.debugDescription - } -} - -extension NSURL : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as URL) - } -} - -extension URL : _CustomPlaygroundQuickLookable { - @available(*, deprecated, message: "URL.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: PlaygroundQuickLook { - return .url(absoluteString) - } -} - -extension URL : Codable { - private enum CodingKeys : Int, CodingKey { - case base - case relative - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let relative = try container.decode(String.self, forKey: .relative) - let base = try container.decodeIfPresent(URL.self, forKey: .base) - - guard let url = URL(string: relative, relativeTo: base) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, - debugDescription: "Invalid URL string.")) - } - - self = url - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.relativeString, forKey: .relative) - if let base = self.baseURL { - try container.encode(base, forKey: .base) - } - } -} - -//===----------------------------------------------------------------------===// -// File references, for playgrounds. -//===----------------------------------------------------------------------===// - -extension URL : _ExpressibleByFileReferenceLiteral { - public init(fileReferenceLiteralResourceName name: String) { - self = Bundle.main.url(forResource: name, withExtension: nil)! - } -} - -public typealias _FileReferenceLiteralType = URL diff --git a/stdlib/public/Darwin/Foundation/URLComponents.swift b/stdlib/public/Darwin/Foundation/URLComponents.swift deleted file mode 100644 index 9c7c545f74124..0000000000000 --- a/stdlib/public/Darwin/Foundation/URLComponents.swift +++ /dev/null @@ -1,517 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -/// A structure designed to parse URLs based on RFC 3986 and to construct URLs from their constituent parts. -/// -/// Its behavior differs subtly from the `URL` struct, which conforms to older RFCs. However, you can easily obtain a `URL` based on the contents of a `URLComponents` or vice versa. -public struct URLComponents : ReferenceConvertible, Hashable, Equatable, _MutableBoxing { - public typealias ReferenceType = NSURLComponents - - internal var _handle: _MutableHandle - - /// Initialize with all components undefined. - public init() { - _handle = _MutableHandle(adoptingReference: NSURLComponents()) - } - - /// Initialize with the components of a URL. - /// - /// If resolvingAgainstBaseURL is `true` and url is a relative URL, the components of url.absoluteURL are used. If the url string from the URL is malformed, nil is returned. - public init?(url: __shared URL, resolvingAgainstBaseURL resolve: Bool) { - guard let result = NSURLComponents(url: url, resolvingAgainstBaseURL: resolve) else { return nil } - _handle = _MutableHandle(adoptingReference: result) - } - - /// Initialize with a URL string. - /// - /// If the URLString is malformed, nil is returned. - public init?(string: __shared String) { - guard let result = NSURLComponents(string: string) else { return nil } - _handle = _MutableHandle(adoptingReference: result) - } - - /// Returns a URL created from the NSURLComponents. - /// - /// If the NSURLComponents has an authority component (user, password, host or port) and a path component, then the path must either begin with "/" or be an empty string. If the NSURLComponents does not have an authority component (user, password, host or port) and has a path component, the path component must not start with "//". If those requirements are not met, nil is returned. - public var url: URL? { - return _handle.map { $0.url } - } - - // Returns a URL created from the NSURLComponents relative to a base URL. - /// - /// If the NSURLComponents has an authority component (user, password, host or port) and a path component, then the path must either begin with "/" or be an empty string. If the NSURLComponents does not have an authority component (user, password, host or port) and has a path component, the path component must not start with "//". If those requirements are not met, nil is returned. - public func url(relativeTo base: URL?) -> URL? { - return _handle.map { $0.url(relativeTo: base) } - } - - // Returns a URL string created from the NSURLComponents. If the NSURLComponents has an authority component (user, password, host or port) and a path component, then the path must either begin with "/" or be an empty string. If the NSURLComponents does not have an authority component (user, password, host or port) and has a path component, the path component must not start with "//". If those requirements are not met, nil is returned. - @available(macOS 10.10, iOS 8.0, *) - public var string: String? { - return _handle.map { $0.string } - } - - /// The scheme subcomponent of the URL. - /// - /// The getter for this property removes any percent encoding this component may have (if the component allows percent encoding). Setting this property assumes the subcomponent or component string is not percent encoded and will add percent encoding (if the component allows percent encoding). - /// Attempting to set the scheme with an invalid scheme string will cause an exception. - public var scheme: String? { - get { return _handle.map { $0.scheme } } - set { _applyMutation { $0.scheme = newValue } } - } - - /// The user subcomponent of the URL. - /// - /// The getter for this property removes any percent encoding this component may have (if the component allows percent encoding). Setting this property assumes the subcomponent or component string is not percent encoded and will add percent encoding (if the component allows percent encoding). - /// - /// Warning: IETF STD 66 (rfc3986) says the use of the format "user:password" in the userinfo subcomponent of a URI is deprecated because passing authentication information in clear text has proven to be a security risk. However, there are cases where this practice is still needed, and so the user and password components and methods are provided. - public var user: String? { - get { return _handle.map { $0.user } } - set { _applyMutation { $0.user = newValue } } - } - - /// The password subcomponent of the URL. - /// - /// The getter for this property removes any percent encoding this component may have (if the component allows percent encoding). Setting this property assumes the subcomponent or component string is not percent encoded and will add percent encoding (if the component allows percent encoding). - /// - /// Warning: IETF STD 66 (rfc3986) says the use of the format "user:password" in the userinfo subcomponent of a URI is deprecated because passing authentication information in clear text has proven to be a security risk. However, there are cases where this practice is still needed, and so the user and password components and methods are provided. - public var password: String? { - get { return _handle.map { $0.password } } - set { _applyMutation { $0.password = newValue } } - } - - /// The host subcomponent. - /// - /// The getter for this property removes any percent encoding this component may have (if the component allows percent encoding). Setting this property assumes the subcomponent or component string is not percent encoded and will add percent encoding (if the component allows percent encoding). - public var host: String? { - get { return _handle.map { $0.host } } - set { _applyMutation { $0.host = newValue } } - } - - /// The port subcomponent. - /// - /// The getter for this property removes any percent encoding this component may have (if the component allows percent encoding). Setting this property assumes the subcomponent or component string is not percent encoded and will add percent encoding (if the component allows percent encoding). - /// Attempting to set a negative port number will cause a fatal error. - public var port: Int? { - get { return _handle.map { $0.port?.intValue } } - set { _applyMutation { $0.port = newValue != nil ? newValue as NSNumber? : nil as NSNumber?} } - } - - /// The path subcomponent. - /// - /// The getter for this property removes any percent encoding this component may have (if the component allows percent encoding). Setting this property assumes the subcomponent or component string is not percent encoded and will add percent encoding (if the component allows percent encoding). - public var path: String { - get { - guard let result = _handle.map({ $0.path }) else { return "" } - return result - } - set { - _applyMutation { $0.path = newValue } - } - } - - /// The query subcomponent. - /// - /// The getter for this property removes any percent encoding this component may have (if the component allows percent encoding). Setting this property assumes the subcomponent or component string is not percent encoded and will add percent encoding (if the component allows percent encoding). - public var query: String? { - get { return _handle.map { $0.query } } - set { _applyMutation { $0.query = newValue } } - } - - /// The fragment subcomponent. - /// - /// The getter for this property removes any percent encoding this component may have (if the component allows percent encoding). Setting this property assumes the subcomponent or component string is not percent encoded and will add percent encoding (if the component allows percent encoding). - public var fragment: String? { - get { return _handle.map { $0.fragment } } - set { _applyMutation { $0.fragment = newValue } } - } - - - /// The user subcomponent, percent-encoded. - /// - /// The getter for this property retains any percent encoding this component may have. Setting this properties assumes the component string is already correctly percent encoded. Attempting to set an incorrectly percent encoded string will cause a `fatalError`. Although ';' is a legal path character, it is recommended that it be percent-encoded for best compatibility with `URL` (`String.addingPercentEncoding(withAllowedCharacters:)` will percent-encode any ';' characters if you pass `CharacterSet.urlUserAllowed`). - public var percentEncodedUser: String? { - get { return _handle.map { $0.percentEncodedUser } } - set { _applyMutation { $0.percentEncodedUser = newValue } } - } - - /// The password subcomponent, percent-encoded. - /// - /// The getter for this property retains any percent encoding this component may have. Setting this properties assumes the component string is already correctly percent encoded. Attempting to set an incorrectly percent encoded string will cause a `fatalError`. Although ';' is a legal path character, it is recommended that it be percent-encoded for best compatibility with `URL` (`String.addingPercentEncoding(withAllowedCharacters:)` will percent-encode any ';' characters if you pass `CharacterSet.urlPasswordAllowed`). - public var percentEncodedPassword: String? { - get { return _handle.map { $0.percentEncodedPassword } } - set { _applyMutation { $0.percentEncodedPassword = newValue } } - } - - /// The host subcomponent, percent-encoded. - /// - /// The getter for this property retains any percent encoding this component may have. Setting this properties assumes the component string is already correctly percent encoded. Attempting to set an incorrectly percent encoded string will cause a `fatalError`. Although ';' is a legal path character, it is recommended that it be percent-encoded for best compatibility with `URL` (`String.addingPercentEncoding(withAllowedCharacters:)` will percent-encode any ';' characters if you pass `CharacterSet.urlHostAllowed`). - public var percentEncodedHost: String? { - get { return _handle.map { $0.percentEncodedHost } } - set { _applyMutation { $0.percentEncodedHost = newValue } } - } - - /// The path subcomponent, percent-encoded. - /// - /// The getter for this property retains any percent encoding this component may have. Setting this properties assumes the component string is already correctly percent encoded. Attempting to set an incorrectly percent encoded string will cause a `fatalError`. Although ';' is a legal path character, it is recommended that it be percent-encoded for best compatibility with `URL` (`String.addingPercentEncoding(withAllowedCharacters:)` will percent-encode any ';' characters if you pass `CharacterSet.urlPathAllowed`). - public var percentEncodedPath: String { - get { - guard let result = _handle.map({ $0.percentEncodedPath }) else { return "" } - return result - } - set { - _applyMutation { $0.percentEncodedPath = newValue } - } - } - - /// The query subcomponent, percent-encoded. - /// - /// The getter for this property retains any percent encoding this component may have. Setting this properties assumes the component string is already correctly percent encoded. Attempting to set an incorrectly percent encoded string will cause a `fatalError`. Although ';' is a legal path character, it is recommended that it be percent-encoded for best compatibility with `URL` (`String.addingPercentEncoding(withAllowedCharacters:)` will percent-encode any ';' characters if you pass `CharacterSet.urlQueryAllowed`). - public var percentEncodedQuery: String? { - get { return _handle.map { $0.percentEncodedQuery } } - set { _applyMutation { $0.percentEncodedQuery = newValue } } - } - - /// The fragment subcomponent, percent-encoded. - /// - /// The getter for this property retains any percent encoding this component may have. Setting this properties assumes the component string is already correctly percent encoded. Attempting to set an incorrectly percent encoded string will cause a `fatalError`. Although ';' is a legal path character, it is recommended that it be percent-encoded for best compatibility with `URL` (`String.addingPercentEncoding(withAllowedCharacters:)` will percent-encode any ';' characters if you pass `CharacterSet.urlFragmentAllowed`). - public var percentEncodedFragment: String? { - get { return _handle.map { $0.percentEncodedFragment } } - set { _applyMutation { $0.percentEncodedFragment = newValue } } - } - - @available(macOS 10.11, iOS 9.0, *) - private func _toStringRange(_ r : NSRange) -> Range? { - guard let s = self.string else { return nil } - return Range(r, in: s) - } - - /// Returns the character range of the scheme in the string returned by `var string`. - /// - /// If the component does not exist, nil is returned. - /// - note: Zero length components are legal. For example, the URL string "scheme://:@/?#" has a zero length user, password, host, query and fragment; the URL strings "scheme:" and "" both have a zero length path. - @available(macOS 10.11, iOS 9.0, *) - public var rangeOfScheme: Range? { - return _toStringRange(_handle.map { $0.rangeOfScheme }) - } - - /// Returns the character range of the user in the string returned by `var string`. - /// - /// If the component does not exist, nil is returned. - /// - note: Zero length components are legal. For example, the URL string "scheme://:@/?#" has a zero length user, password, host, query and fragment; the URL strings "scheme:" and "" both have a zero length path. - @available(macOS 10.11, iOS 9.0, *) - public var rangeOfUser: Range? { - return _toStringRange(_handle.map { $0.rangeOfUser }) - } - - /// Returns the character range of the password in the string returned by `var string`. - /// - /// If the component does not exist, nil is returned. - /// - note: Zero length components are legal. For example, the URL string "scheme://:@/?#" has a zero length user, password, host, query and fragment; the URL strings "scheme:" and "" both have a zero length path. - @available(macOS 10.11, iOS 9.0, *) - public var rangeOfPassword: Range? { - return _toStringRange(_handle.map { $0.rangeOfPassword }) - } - - /// Returns the character range of the host in the string returned by `var string`. - /// - /// If the component does not exist, nil is returned. - /// - note: Zero length components are legal. For example, the URL string "scheme://:@/?#" has a zero length user, password, host, query and fragment; the URL strings "scheme:" and "" both have a zero length path. - @available(macOS 10.11, iOS 9.0, *) - public var rangeOfHost: Range? { - return _toStringRange(_handle.map { $0.rangeOfHost }) - } - - /// Returns the character range of the port in the string returned by `var string`. - /// - /// If the component does not exist, nil is returned. - /// - note: Zero length components are legal. For example, the URL string "scheme://:@/?#" has a zero length user, password, host, query and fragment; the URL strings "scheme:" and "" both have a zero length path. - @available(macOS 10.11, iOS 9.0, *) - public var rangeOfPort: Range? { - return _toStringRange(_handle.map { $0.rangeOfPort }) - } - - /// Returns the character range of the path in the string returned by `var string`. - /// - /// If the component does not exist, nil is returned. - /// - note: Zero length components are legal. For example, the URL string "scheme://:@/?#" has a zero length user, password, host, query and fragment; the URL strings "scheme:" and "" both have a zero length path. - @available(macOS 10.11, iOS 9.0, *) - public var rangeOfPath: Range? { - return _toStringRange(_handle.map { $0.rangeOfPath }) - } - - /// Returns the character range of the query in the string returned by `var string`. - /// - /// If the component does not exist, nil is returned. - /// - note: Zero length components are legal. For example, the URL string "scheme://:@/?#" has a zero length user, password, host, query and fragment; the URL strings "scheme:" and "" both have a zero length path. - @available(macOS 10.11, iOS 9.0, *) - public var rangeOfQuery: Range? { - return _toStringRange(_handle.map { $0.rangeOfQuery }) - } - - /// Returns the character range of the fragment in the string returned by `var string`. - /// - /// If the component does not exist, nil is returned. - /// - note: Zero length components are legal. For example, the URL string "scheme://:@/?#" has a zero length user, password, host, query and fragment; the URL strings "scheme:" and "" both have a zero length path. - @available(macOS 10.11, iOS 9.0, *) - public var rangeOfFragment: Range? { - return _toStringRange(_handle.map { $0.rangeOfFragment }) - } - - /// Returns an array of query items for this `URLComponents`, in the order in which they appear in the original query string. - /// - /// Each `URLQueryItem` represents a single key-value pair, - /// - /// Note that a name may appear more than once in a single query string, so the name values are not guaranteed to be unique. If the `URLComponents` has an empty query component, returns an empty array. If the `URLComponents` has no query component, returns nil. - /// - /// The setter combines an array containing any number of `URLQueryItem`s, each of which represents a single key-value pair, into a query string and sets the `URLComponents` query property. Passing an empty array sets the query component of the `URLComponents` to an empty string. Passing nil removes the query component of the `URLComponents`. - /// - /// - note: If a name-value pair in a query is empty (i.e. the query string starts with '&', ends with '&', or has "&&" within it), you get a `URLQueryItem` with a zero-length name and a nil value. If a query's name-value pair has nothing before the equals sign, you get a zero-length name. If a query's name-value pair has nothing after the equals sign, you get a zero-length value. If a query's name-value pair has no equals sign, the query name-value pair string is the name and you get a nil value. - @available(macOS 10.10, iOS 8.0, *) - public var queryItems: [URLQueryItem]? { - get { return _handle.map { $0.queryItems } } - set { _applyMutation { $0.queryItems = newValue } } - } - - /// Returns an array of query items for this `URLComponents`, in the order in which they appear in the original query string. Any percent-encoding in a query item name or value is retained - /// - /// The setter combines an array containing any number of `URLQueryItem`s, each of which represents a single key-value pair, into a query string and sets the `URLComponents` query property. This property assumes the query item names and values are already correctly percent-encoded, and that the query item names do not contain the query item delimiter characters '&' and '='. Attempting to set an incorrectly percent-encoded query item or a query item name with the query item delimiter characters '&' and '=' will cause a `fatalError`. - @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) - public var percentEncodedQueryItems: [URLQueryItem]? { - get { return _handle.map { $0.percentEncodedQueryItems } } - set { _applyMutation { $0.percentEncodedQueryItems = newValue } } - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(_handle._uncopiedReference()) - } - - // MARK: - Bridging - - fileprivate init(reference: __shared NSURLComponents) { - _handle = _MutableHandle(reference: reference) - } - - public static func ==(lhs: URLComponents, rhs: URLComponents) -> Bool { - // Don't copy references here; no one should be storing anything - return lhs._handle._uncopiedReference().isEqual(rhs._handle._uncopiedReference()) - } -} - -extension URLComponents : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - - public var description: String { - if let u = url { - return u.description - } else { - return self.customMirror.children.reduce("") { - $0.appending("\($1.label ?? ""): \($1.value) ") - } - } - } - - public var debugDescription: String { - return self.description - } - - public var customMirror: Mirror { - var c: [(label: String?, value: Any)] = [] - - if let s = self.scheme { c.append((label: "scheme", value: s)) } - if let u = self.user { c.append((label: "user", value: u)) } - if let pw = self.password { c.append((label: "password", value: pw)) } - if let h = self.host { c.append((label: "host", value: h)) } - if let p = self.port { c.append((label: "port", value: p)) } - - c.append((label: "path", value: self.path)) - if #available(macOS 10.10, iOS 8.0, *) { - if let qi = self.queryItems { c.append((label: "queryItems", value: qi)) } - } - if let f = self.fragment { c.append((label: "fragment", value: f)) } - let m = Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - return m - } -} - -extension URLComponents : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSURLComponents.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSURLComponents { - return _handle._copiedReference() - } - - public static func _forceBridgeFromObjectiveC(_ x: NSURLComponents, result: inout URLComponents?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSURLComponents, result: inout URLComponents?) -> Bool { - result = URLComponents(reference: x) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSURLComponents?) -> URLComponents { - guard let src = source else { return URLComponents() } - return URLComponents(reference: src) - } -} - -extension NSURLComponents : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as URLComponents) - } -} - - -/// A single name-value pair, for use with `URLComponents`. -@available(macOS 10.10, iOS 8.0, *) -public struct URLQueryItem : ReferenceConvertible, Hashable, Equatable { - public typealias ReferenceType = NSURLQueryItem - - fileprivate var _queryItem : NSURLQueryItem - - public init(name: __shared String, value: __shared String?) { - _queryItem = NSURLQueryItem(name: name, value: value) - } - - fileprivate init(reference: __shared NSURLQueryItem) { _queryItem = reference.copy() as! NSURLQueryItem } - fileprivate var reference : NSURLQueryItem { return _queryItem } - - public var name : String { - get { return _queryItem.name } - set { _queryItem = NSURLQueryItem(name: newValue, value: value) } - } - - public var value : String? { - get { return _queryItem.value } - set { _queryItem = NSURLQueryItem(name: name, value: newValue) } - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(_queryItem) - } - - @available(macOS 10.10, iOS 8.0, *) - public static func ==(lhs: URLQueryItem, rhs: URLQueryItem) -> Bool { - return lhs._queryItem.isEqual(rhs as NSURLQueryItem) - } -} - -@available(macOS 10.10, iOS 8.0, *) -extension URLQueryItem : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - - public var description: String { - if let v = value { - return "\(name)=\(v)" - } else { - return name - } - } - - public var debugDescription: String { - return self.description - } - - public var customMirror: Mirror { - let c: [(label: String?, value: Any)] = [ - ("name", name), - ("value", value as Any), - ] - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - -@available(macOS 10.10, iOS 8.0, *) -extension URLQueryItem : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSURLQueryItem.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSURLQueryItem { - return _queryItem - } - - public static func _forceBridgeFromObjectiveC(_ x: NSURLQueryItem, result: inout URLQueryItem?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ x: NSURLQueryItem, result: inout URLQueryItem?) -> Bool { - result = URLQueryItem(reference: x) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSURLQueryItem?) -> URLQueryItem { - var result: URLQueryItem? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -@available(macOS 10.10, iOS 8.0, *) -extension NSURLQueryItem : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as URLQueryItem) - } -} - -extension URLComponents : Codable { - private enum CodingKeys : Int, CodingKey { - case scheme - case user - case password - case host - case port - case path - case query - case fragment - } - - public init(from decoder: Decoder) throws { - self.init() - - let container = try decoder.container(keyedBy: CodingKeys.self) - self.scheme = try container.decodeIfPresent(String.self, forKey: .scheme) - self.user = try container.decodeIfPresent(String.self, forKey: .user) - self.password = try container.decodeIfPresent(String.self, forKey: .password) - self.host = try container.decodeIfPresent(String.self, forKey: .host) - self.port = try container.decodeIfPresent(Int.self, forKey: .port) - self.path = try container.decode(String.self, forKey: .path) - self.query = try container.decodeIfPresent(String.self, forKey: .query) - self.fragment = try container.decodeIfPresent(String.self, forKey: .fragment) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeIfPresent(self.scheme, forKey: .scheme) - try container.encodeIfPresent(self.user, forKey: .user) - try container.encodeIfPresent(self.password, forKey: .password) - try container.encodeIfPresent(self.host, forKey: .host) - try container.encodeIfPresent(self.port, forKey: .port) - try container.encode(self.path, forKey: .path) - try container.encodeIfPresent(self.query, forKey: .query) - try container.encodeIfPresent(self.fragment, forKey: .fragment) - } -} diff --git a/stdlib/public/Darwin/Foundation/URLRequest.swift b/stdlib/public/Darwin/Foundation/URLRequest.swift deleted file mode 100644 index 9695aba1fb9e8..0000000000000 --- a/stdlib/public/Darwin/Foundation/URLRequest.swift +++ /dev/null @@ -1,328 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -@available(*, deprecated, message: "Please use the struct type URLRequest") -public typealias MutableURLRequest = NSMutableURLRequest - -public struct URLRequest : ReferenceConvertible, Equatable, Hashable { - public typealias ReferenceType = NSURLRequest - public typealias CachePolicy = NSURLRequest.CachePolicy - public typealias NetworkServiceType = NSURLRequest.NetworkServiceType - - /* - NSURLRequest has a fragile ivar layout that prevents the swift subclass approach here, so instead we keep an always mutable copy - */ - internal var _handle: _MutableHandle - - internal mutating func _applyMutation(_ whatToDo : (NSMutableURLRequest) -> ReturnType) -> ReturnType { - if !isKnownUniquelyReferenced(&_handle) { - let ref = _handle._uncopiedReference() - _handle = _MutableHandle(reference: ref) - } - return whatToDo(_handle._uncopiedReference()) - } - - /// Creates and initializes a URLRequest with the given URL and cache policy. - /// - parameter: url The URL for the request. - /// - parameter: cachePolicy The cache policy for the request. Defaults to `.useProtocolCachePolicy` - /// - parameter: timeoutInterval The timeout interval for the request. See the commentary for the `timeoutInterval` for more information on timeout intervals. Defaults to 60.0 - public init(url: URL, cachePolicy: CachePolicy = .useProtocolCachePolicy, timeoutInterval: TimeInterval = 60.0) { - _handle = _MutableHandle(adoptingReference: NSMutableURLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: timeoutInterval)) - } - - private init(_bridged request: __shared NSURLRequest) { - _handle = _MutableHandle(reference: request.mutableCopy() as! NSMutableURLRequest) - } - - /// The URL of the receiver. - public var url: URL? { - get { - return _handle.map { $0.url } - } - set { - _applyMutation { $0.url = newValue } - } - } - - /// The cache policy of the receiver. - public var cachePolicy: CachePolicy { - get { - return _handle.map { $0.cachePolicy } - } - set { - _applyMutation { $0.cachePolicy = newValue } - } - } - - /// Returns the timeout interval of the receiver. - /// - discussion: The timeout interval specifies the limit on the idle - /// interval allotted to a request in the process of loading. The "idle - /// interval" is defined as the period of time that has passed since the - /// last instance of load activity occurred for a request that is in the - /// process of loading. Hence, when an instance of load activity occurs - /// (e.g. bytes are received from the network for a request), the idle - /// interval for a request is reset to 0. If the idle interval ever - /// becomes greater than or equal to the timeout interval, the request - /// is considered to have timed out. This timeout interval is measured - /// in seconds. - public var timeoutInterval: TimeInterval { - get { - return _handle.map { $0.timeoutInterval } - } - set { - _applyMutation { $0.timeoutInterval = newValue } - } - } - - /// The main document URL associated with this load. - /// - discussion: This URL is used for the cookie "same domain as main - /// document" policy. - public var mainDocumentURL: URL? { - get { - return _handle.map { $0.mainDocumentURL } - } - set { - _applyMutation { $0.mainDocumentURL = newValue } - } - } - - /// The URLRequest.NetworkServiceType associated with this request. - /// - discussion: This will return URLRequest.NetworkServiceType.default for requests that have - /// not explicitly set a networkServiceType - @available(macOS 10.7, iOS 4.0, *) - public var networkServiceType: NetworkServiceType { - get { - return _handle.map { $0.networkServiceType } - } - set { - _applyMutation { $0.networkServiceType = newValue } - } - } - - /// `true` if the receiver is allowed to use the built in cellular radios to - /// satisfy the request, `false` otherwise. - @available(macOS 10.8, iOS 6.0, *) - public var allowsCellularAccess: Bool { - get { - return _handle.map { $0.allowsCellularAccess } - } - set { - _applyMutation { $0.allowsCellularAccess = newValue } - } - } - - /// `true` if the receiver is allowed to use an interface marked as expensive to - /// satify the request, `false` otherwise. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public var allowsExpensiveNetworkAccess: Bool { - get { - return _handle.map { $0.allowsExpensiveNetworkAccess } - } - set { - _applyMutation { $0.allowsExpensiveNetworkAccess = newValue } - } - } - - /// `true` if the receiver is allowed to use an interface marked as constrained to - /// satify the request, `false` otherwise. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public var allowsConstrainedNetworkAccess: Bool { - get { - return _handle.map { $0.allowsConstrainedNetworkAccess } - } - set { - _applyMutation { $0.allowsConstrainedNetworkAccess = newValue } - } - } - - /// The HTTP request method of the receiver. - public var httpMethod: String? { - get { - return _handle.map { $0.httpMethod } - } - set { - _applyMutation { - if let value = newValue { - $0.httpMethod = value - } else { - $0.httpMethod = "GET" - } - } - } - } - - /// A dictionary containing all the HTTP header fields of the - /// receiver. - public var allHTTPHeaderFields: [String : String]? { - get { - return _handle.map { $0.allHTTPHeaderFields } - } - set { - _applyMutation { $0.allHTTPHeaderFields = newValue } - } - } - - /// The value which corresponds to the given header - /// field. Note that, in keeping with the HTTP RFC, HTTP header field - /// names are case-insensitive. - /// - parameter: field the header field name to use for the lookup (case-insensitive). - public func value(forHTTPHeaderField field: String) -> String? { - return _handle.map { $0.value(forHTTPHeaderField: field) } - } - - /// If a value was previously set for the given header - /// field, that value is replaced with the given value. Note that, in - /// keeping with the HTTP RFC, HTTP header field names are - /// case-insensitive. - public mutating func setValue(_ value: String?, forHTTPHeaderField field: String) { - _applyMutation { - $0.setValue(value, forHTTPHeaderField: field) - } - } - - /// This method provides a way to add values to header - /// fields incrementally. If a value was previously set for the given - /// header field, the given value is appended to the previously-existing - /// value. The appropriate field delimiter, a comma in the case of HTTP, - /// is added by the implementation, and should not be added to the given - /// value by the caller. Note that, in keeping with the HTTP RFC, HTTP - /// header field names are case-insensitive. - public mutating func addValue(_ value: String, forHTTPHeaderField field: String) { - _applyMutation { - $0.addValue(value, forHTTPHeaderField: field) - } - } - - /// This data is sent as the message body of the request, as - /// in done in an HTTP POST request. - public var httpBody: Data? { - get { - return _handle.map { $0.httpBody } - } - set { - _applyMutation { $0.httpBody = newValue } - } - } - - /// The stream is returned for examination only; it is - /// not safe for the caller to manipulate the stream in any way. Also - /// note that the HTTPBodyStream and HTTPBody are mutually exclusive - only - /// one can be set on a given request. Also note that the body stream is - /// preserved across copies, but is LOST when the request is coded via the - /// NSCoding protocol - public var httpBodyStream: InputStream? { - get { - return _handle.map { $0.httpBodyStream } - } - set { - _applyMutation { $0.httpBodyStream = newValue } - } - } - - /// `true` if cookies will be sent with and set for this request; otherwise `false`. - public var httpShouldHandleCookies: Bool { - get { - return _handle.map { $0.httpShouldHandleCookies } - } - set { - _applyMutation { $0.httpShouldHandleCookies = newValue } - } - } - - /// `true` if the receiver should transmit before the previous response - /// is received. `false` if the receiver should wait for the previous response - /// before transmitting. - @available(macOS 10.7, iOS 4.0, *) - public var httpShouldUsePipelining: Bool { - get { - return _handle.map { $0.httpShouldUsePipelining } - } - set { - _applyMutation { $0.httpShouldUsePipelining = newValue } - } - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(_handle._uncopiedReference()) - } - - public static func ==(lhs: URLRequest, rhs: URLRequest) -> Bool { - return lhs._handle._uncopiedReference().isEqual(rhs._handle._uncopiedReference()) - } -} - -extension URLRequest : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { - - public var description: String { - return url?.description ?? "url: nil" - } - - public var debugDescription: String { - return self.description - } - - public var customMirror: Mirror { - let c: [(label: String?, value: Any)] = [ - ("url", url as Any), - ("cachePolicy", cachePolicy.rawValue), - ("timeoutInterval", timeoutInterval), - ("mainDocumentURL", mainDocumentURL as Any), - ("networkServiceType", networkServiceType), - ("allowsCellularAccess", allowsCellularAccess), - ("httpMethod", httpMethod as Any), - ("allHTTPHeaderFields", allHTTPHeaderFields as Any), - ("httpBody", httpBody as Any), - ("httpBodyStream", httpBodyStream as Any), - ("httpShouldHandleCookies", httpShouldHandleCookies), - ("httpShouldUsePipelining", httpShouldUsePipelining), - ] - return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct) - } -} - -extension URLRequest : _ObjectiveCBridgeable { - public static func _getObjectiveCType() -> Any.Type { - return NSURLRequest.self - } - - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSURLRequest { - return _handle._copiedReference() - } - - public static func _forceBridgeFromObjectiveC(_ input: NSURLRequest, result: inout URLRequest?) { - result = URLRequest(_bridged: input) - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: NSURLRequest, result: inout URLRequest?) -> Bool { - result = URLRequest(_bridged: input) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSURLRequest?) -> URLRequest { - var result: URLRequest? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -extension NSURLRequest : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as URLRequest) - } -} - diff --git a/stdlib/public/Darwin/Foundation/URLSession.swift b/stdlib/public/Darwin/Foundation/URLSession.swift deleted file mode 100644 index 79cabfc93ded0..0000000000000 --- a/stdlib/public/Darwin/Foundation/URLSession.swift +++ /dev/null @@ -1,69 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension URLSessionWebSocketTask { - public enum Message { - case data(Data) - case string(String) - } - - public func send(_ message: Message, completionHandler: @escaping (Error?) -> Void) { - switch message { - case .data(let data): - __send(__NSURLSessionWebSocketMessage(data: data), completionHandler: completionHandler) - case .string(let string): - __send(__NSURLSessionWebSocketMessage(string: string), completionHandler: completionHandler) - } - } - - public func receive(completionHandler: @escaping (Result) -> Void) { - __receiveMessage { message, error in - switch (message, error) { - case (.some(let message), nil): - switch message.type { - case .data: - completionHandler(.success(.data(message.data!))) - case .string: - completionHandler(.success(.string(message.string!))) - @unknown default: - break - } - case (nil, .some(let error)): - completionHandler(.failure(error)) - case (_, _): - fatalError("Only one of message or error should be nil") - } - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension URLSessionTaskTransactionMetrics { - public var localPort: Int? { - return __localPort as? Int - } - - public var remotePort: Int? { - return __remotePort as? Int - } - - public var negotiatedTLSProtocolVersion: tls_protocol_version_t? { - return (__negotiatedTLSProtocolVersion as? UInt16).flatMap(tls_protocol_version_t.init(rawValue:)) - } - - public var negotiatedTLSCipherSuite: tls_ciphersuite_t? { - return (__negotiatedTLSCipherSuite as? UInt16).flatMap(tls_ciphersuite_t.init(rawValue:)) - } -} diff --git a/stdlib/public/Darwin/Foundation/UUID.swift b/stdlib/public/Darwin/Foundation/UUID.swift deleted file mode 100644 index 1c20efb11f1c7..0000000000000 --- a/stdlib/public/Darwin/Foundation/UUID.swift +++ /dev/null @@ -1,172 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported import Foundation // Clang module -import Darwin.uuid -@_implementationOnly import _SwiftCoreFoundationOverlayShims - -/// Represents UUID strings, which can be used to uniquely identify types, interfaces, and other items. -@available(macOS 10.8, iOS 6.0, *) -public struct UUID : ReferenceConvertible, Hashable, Equatable, CustomStringConvertible { - public typealias ReferenceType = NSUUID - - public private(set) var uuid: uuid_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - - /* Create a new UUID with RFC 4122 version 4 random bytes */ - public init() { - withUnsafeMutablePointer(to: &uuid) { - $0.withMemoryRebound(to: UInt8.self, capacity: 16) { - uuid_generate_random($0) - } - } - } - - private init(reference: __shared NSUUID) { - var bytes: uuid_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - withUnsafeMutablePointer(to: &bytes) { - $0.withMemoryRebound(to: UInt8.self, capacity: 16) { - reference.getBytes($0) - } - } - uuid = bytes - } - - /// Create a UUID from a string such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F". - /// - /// Returns nil for invalid strings. - public init?(uuidString string: __shared String) { - let res = withUnsafeMutablePointer(to: &uuid) { - $0.withMemoryRebound(to: UInt8.self, capacity: 16) { - return uuid_parse(string, $0) - } - } - if res != 0 { - return nil - } - } - - /// Create a UUID from a `uuid_t`. - public init(uuid: uuid_t) { - self.uuid = uuid - } - - /// Returns a string created from the UUID, such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F" - public var uuidString: String { - var bytes: uuid_string_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - return withUnsafePointer(to: uuid) { - $0.withMemoryRebound(to: UInt8.self, capacity: 16) { val in - withUnsafeMutablePointer(to: &bytes) { - $0.withMemoryRebound(to: Int8.self, capacity: 37) { str in - uuid_unparse(val, str) - return String(cString: UnsafePointer(str), encoding: .utf8)! - } - } - } - } - } - - public func hash(into hasher: inout Hasher) { - withUnsafeBytes(of: uuid) { buffer in - hasher.combine(bytes: buffer) - } - } - - public var description: String { - return uuidString - } - - public var debugDescription: String { - return description - } - - // MARK: - Bridging Support - - private var reference: NSUUID { - return withUnsafePointer(to: uuid) { - $0.withMemoryRebound(to: UInt8.self, capacity: 16) { - return NSUUID(uuidBytes: $0) - } - } - } - - public static func ==(lhs: UUID, rhs: UUID) -> Bool { - return withUnsafeBytes(of: rhs.uuid) { (rhsPtr) -> Bool in - return withUnsafeBytes(of: lhs.uuid) { (lhsPtr) -> Bool in - let lhsFirstChunk = lhsPtr.load(fromByteOffset: 0, as: UInt64.self) - let lhsSecondChunk = lhsPtr.load(fromByteOffset: MemoryLayout.size, as: UInt64.self) - let rhsFirstChunk = rhsPtr.load(fromByteOffset: 0, as: UInt64.self) - let rhsSecondChunk = rhsPtr.load(fromByteOffset: MemoryLayout.size, as: UInt64.self) - return ((lhsFirstChunk ^ rhsFirstChunk) | (lhsSecondChunk ^ rhsSecondChunk)) == 0 - } - } - } -} - -extension UUID : CustomReflectable { - public var customMirror: Mirror { - let c : [(label: String?, value: Any)] = [] - let m = Mirror(self, children:c, displayStyle: Mirror.DisplayStyle.struct) - return m - } -} - -extension UUID : _ObjectiveCBridgeable { - @_semantics("convertToObjectiveC") - public func _bridgeToObjectiveC() -> NSUUID { - return reference - } - - public static func _forceBridgeFromObjectiveC(_ x: NSUUID, result: inout UUID?) { - if !_conditionallyBridgeFromObjectiveC(x, result: &result) { - fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") - } - } - - public static func _conditionallyBridgeFromObjectiveC(_ input: NSUUID, result: inout UUID?) -> Bool { - result = UUID(reference: input) - return true - } - - @_effects(readonly) - public static func _unconditionallyBridgeFromObjectiveC(_ source: NSUUID?) -> UUID { - var result: UUID? - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } -} - -extension NSUUID : _HasCustomAnyHashableRepresentation { - // Must be @nonobjc to avoid infinite recursion during bridging. - @nonobjc - public func _toCustomAnyHashable() -> AnyHashable? { - return AnyHashable(self as UUID) - } -} - -extension UUID : Codable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let uuidString = try container.decode(String.self) - - guard let uuid = UUID(uuidString: uuidString) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, - debugDescription: "Attempted to decode UUID from invalid UUID string.")) - } - - self = uuid - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.uuidString) - } -} diff --git a/stdlib/public/Darwin/ObjectiveC/CMakeLists.txt b/stdlib/public/Darwin/ObjectiveC/CMakeLists.txt deleted file mode 100644 index bf77787506cd5..0000000000000 --- a/stdlib/public/Darwin/ObjectiveC/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.4.3) -include("../../../../cmake/modules/StandaloneOverlay.cmake") - -add_swift_target_library(swiftObjectiveC ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY - ObjectiveC.swift - - "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" - - SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}" "-Xfrontend" - "-disable-objc-attr-requires-foundation-module" "${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}" - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - - SWIFT_MODULE_DEPENDS_OSX Darwin # auto-updated - SWIFT_MODULE_DEPENDS_IOS Darwin # auto-updated - SWIFT_MODULE_DEPENDS_TVOS Darwin # auto-updated - SWIFT_MODULE_DEPENDS_WATCHOS Darwin # auto-updated - - DEPLOYMENT_VERSION_OSX ${SWIFTLIB_DEPLOYMENT_VERSION_OBJECTIVEC_OSX} - DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_OBJECTIVEC_IOS} - DEPLOYMENT_VERSION_TVOS ${SWIFTLIB_DEPLOYMENT_VERSION_OBJECTIVEC_TVOS} - DEPLOYMENT_VERSION_WATCHOS ${SWIFTLIB_DEPLOYMENT_VERSION_OBJECTIVEC_WATCHOS} - INSTALL_IN_COMPONENT sdk-overlay -) diff --git a/stdlib/public/Darwin/ObjectiveC/ObjectiveC.swift b/stdlib/public/Darwin/ObjectiveC/ObjectiveC.swift deleted file mode 100644 index 0204b2cee461d..0000000000000 --- a/stdlib/public/Darwin/ObjectiveC/ObjectiveC.swift +++ /dev/null @@ -1,253 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -@_exported -import ObjectiveC -@_implementationOnly import _SwiftObjectiveCOverlayShims - -//===----------------------------------------------------------------------===// -// Objective-C Primitive Types -//===----------------------------------------------------------------------===// - -/// The Objective-C BOOL type. -/// -/// On 64-bit iOS, the Objective-C BOOL type is a typedef of C/C++ -/// bool. Elsewhere, it is "signed char". The Clang importer imports it as -/// ObjCBool. -@frozen -public struct ObjCBool : ExpressibleByBooleanLiteral { -#if (os(macOS) && arch(x86_64)) || (os(iOS) && (arch(i386) || arch(arm))) - // On Intel OS X and 32-bit iOS, Objective-C's BOOL type is a "signed char". - @usableFromInline var _value: Int8 - - @_transparent - init(_ value: Int8) { - self._value = value - } - - @_transparent - public init(_ value: Bool) { - self._value = value ? 1 : 0 - } - -#else - // Everywhere else it is C/C++'s "Bool" - @usableFromInline var _value: Bool - - @_transparent - public init(_ value: Bool) { - self._value = value - } -#endif - - /// The value of `self`, expressed as a `Bool`. - @_transparent - public var boolValue: Bool { -#if (os(macOS) && arch(x86_64)) || (os(iOS) && (arch(i386) || arch(arm))) - return _value != 0 -#else - return _value -#endif - } - - /// Create an instance initialized to `value`. - @_transparent - public init(booleanLiteral value: Bool) { - self.init(value) - } -} - -extension ObjCBool : CustomReflectable { - /// Returns a mirror that reflects `self`. - public var customMirror: Mirror { - return Mirror(reflecting: boolValue) - } -} - -extension ObjCBool : CustomStringConvertible { - /// A textual representation of `self`. - public var description: String { - return self.boolValue.description - } -} - -// Functions used to implicitly bridge ObjCBool types to Swift's Bool type. - -@_transparent -public // COMPILER_INTRINSIC -func _convertBoolToObjCBool(_ x: Bool) -> ObjCBool { - return ObjCBool(x) -} - -@_transparent -public // COMPILER_INTRINSIC -func _convertObjCBoolToBool(_ x: ObjCBool) -> Bool { - return x.boolValue -} - -/// The Objective-C SEL type. -/// -/// The Objective-C SEL type is typically an opaque pointer. Swift -/// treats it as a distinct struct type, with operations to -/// convert between C strings and selectors. -/// -/// The compiler has special knowledge of this type. -@frozen -public struct Selector : ExpressibleByStringLiteral { - var ptr: OpaquePointer - - /// Create a selector from a string. - public init(_ str : String) { - ptr = str.withCString { sel_registerName($0).ptr } - } - - // FIXME: Fast-path this in the compiler, so we don't end up with - // the sel_registerName call at compile time. - /// Create an instance initialized to `value`. - public init(stringLiteral value: String) { - self = sel_registerName(value) - } -} - -extension Selector: Equatable, Hashable { - // Note: The implementations for `==` and `hash(into:)` are synthesized by the - // compiler. The generated implementations use the value of `ptr` as the basis - // for equality. -} - -extension Selector : CustomStringConvertible { - /// A textual representation of `self`. - public var description: String { - return String(_sel: self) - } -} - -extension String { - /// Construct the C string representation of an Objective-C selector. - public init(_sel: Selector) { - // FIXME: This misses the ASCII optimization. - self = String(cString: sel_getName(_sel)) - } -} - -extension Selector : CustomReflectable { - /// Returns a mirror that reflects `self`. - public var customMirror: Mirror { - return Mirror(reflecting: String(_sel: self)) - } -} - -//===----------------------------------------------------------------------===// -// NSZone -//===----------------------------------------------------------------------===// - -@frozen -public struct NSZone { - var pointer: OpaquePointer -} - -// Note: NSZone becomes Zone in Swift 3. -typealias Zone = NSZone - -//===----------------------------------------------------------------------===// -// @autoreleasepool substitute -//===----------------------------------------------------------------------===// - -public func autoreleasepool( - invoking body: () throws -> Result -) rethrows -> Result { - let pool = _swift_objc_autoreleasePoolPush() - defer { - _swift_objc_autoreleasePoolPop(pool) - } - return try body() -} - -//===----------------------------------------------------------------------===// -// Mark YES and NO unavailable. -//===----------------------------------------------------------------------===// - -@available(*, unavailable, message: "Use 'Bool' value 'true' instead") -public var YES: ObjCBool { - fatalError("can't retrieve unavailable property") -} -@available(*, unavailable, message: "Use 'Bool' value 'false' instead") -public var NO: ObjCBool { - fatalError("can't retrieve unavailable property") -} - -//===----------------------------------------------------------------------===// -// NSObject -//===----------------------------------------------------------------------===// - -// NSObject implements Equatable's == as -[NSObject isEqual:] -// NSObject implements Hashable's hashValue as -[NSObject hash] -// FIXME: what about NSObjectProtocol? - -extension NSObject : Equatable, Hashable { - /// Returns a Boolean value indicating whether two values are - /// equal. `NSObject` implements this by calling `lhs.isEqual(rhs)`. - /// - /// Subclasses of `NSObject` can customize Equatable conformance by overriding - /// `isEqual(_:)`. If two objects are equal, they must have the same hash - /// value, so if you override `isEqual(_:)`, make sure you also override the - /// `hash` property. - /// - /// - Parameters: - /// - lhs: A value to compare. - /// - rhs: Another value to compare. - public static func == (lhs: NSObject, rhs: NSObject) -> Bool { - return lhs.isEqual(rhs) - } - - /// The hash value. - /// - /// `NSObject` implements this by returning `self.hash`. - /// - /// `NSObject.hashValue` is not overridable; subclasses can customize hashing - /// by overriding the `hash` property. - /// - /// **Axiom:** `x == y` implies `x.hashValue == y.hashValue` - /// - /// - Note: the hash value is not guaranteed to be stable across - /// different invocations of the same program. Do not persist the - /// hash value across program runs. - @nonobjc - public var hashValue: Int { - return hash - } - - /// Hashes the essential components of this value by feeding them into the - /// given hasher. - /// - /// NSObject implements this by feeding `self.hash` to the hasher. - /// - /// `NSObject.hash(into:)` is not overridable; subclasses can customize - /// hashing by overriding the `hash` property. - public func hash(into hasher: inout Hasher) { - hasher.combine(self.hash) - } - - public func _rawHashValue(seed: Int) -> Int { - return self.hash._rawHashValue(seed: seed) - } -} - -extension NSObject : CVarArg { - /// Transform `self` into a series of machine words that can be - /// appropriately interpreted by C varargs - public var _cVarArgEncoding: [Int] { - _autorelease(self) - return _encodeBitsAsWords(self) - } -} - diff --git a/stdlib/public/Darwin/XCTest/CMakeLists.txt b/stdlib/public/Darwin/XCTest/CMakeLists.txt deleted file mode 100644 index c2a6cef19ab65..0000000000000 --- a/stdlib/public/Darwin/XCTest/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -cmake_minimum_required(VERSION 3.4.3) -include("../../../../cmake/modules/StandaloneOverlay.cmake") - -set(DISABLE_APPLICATION_EXTENSION ON) - -add_swift_target_library(swiftXCTest ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY - XCTestCaseAdditions.mm - XCTest.swift - - SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - - # XCTest has a type called XCTest, which exposes an issue in module - # interfaces -- because types are fully-qualified, the compiler currently - # doesn't disambiguate between XCTest-the-module and XCTest-the-class. - # rdar://48445154 - -Xfrontend -module-interface-preserve-types-as-written - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR - SWIFT_MODULE_DEPENDS ObjectiveC Foundation - FRAMEWORK_DEPENDS Foundation XCTest - DONT_EMBED_BITCODE - - DEPLOYMENT_VERSION_OSX ${SWIFTLIB_DEPLOYMENT_VERSION_XCTEST_OSX} - DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_XCTEST_IOS} - DEPLOYMENT_VERSION_TVOS ${SWIFTLIB_DEPLOYMENT_VERSION_XCTEST_TVOS} - INSTALL_IN_COMPONENT sdk-overlay -) diff --git a/stdlib/public/Darwin/XCTest/XCTest.swift b/stdlib/public/Darwin/XCTest/XCTest.swift deleted file mode 100644 index bb91e654274af..0000000000000 --- a/stdlib/public/Darwin/XCTest/XCTest.swift +++ /dev/null @@ -1,745 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2020 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 -// -//===----------------------------------------------------------------------===// - -// The actual XCTest overlay is not maintained in this repository -- it is -// distributed in Xcode (libXCTestSwiftSupport.dylib), along with -// XCTest.framework itself. -// -// The codebase here builds the obsolete /usr/lib/swift/libswiftXCTest.dylib -// that currently ships in the OS. There is no expectation that that library is -// usable for anything; it only exists to maintain a superficial level of binary -// compatibility with existing apps that happen to link to it by mistake. -// -// rdar://problem/55270944 - -@_exported import XCTest // Clang module - -import CoreGraphics -@_implementationOnly import _SwiftXCTestOverlayShims - -// --- XCTest API Swiftification --- - -extension XCTContext { - - /// Create and run a new activity with provided name and block. - public class func runActivity(named name: String, block: (XCTActivity) throws -> Result) rethrows -> Result { - let context = _XCTContextCurrent() - - if _XCTContextShouldStartActivity(context, XCTActivityTypeUserCreated) { - return try autoreleasepool { - let activity = _XCTContextWillStartActivity(context, name, XCTActivityTypeUserCreated) - defer { - _XCTContextDidFinishActivity(context, activity) - } - return try block(activity) - } - } else { - fatalError("XCTContext.runActivity(named:block:) failed because activities are disallowed in the current configuration.") - } - } -} - -#if os(macOS) -@available(swift 4.0) -@available(macOS 10.11, *) -extension XCUIElement { - /// Types a single key from the XCUIKeyboardKey enumeration with the specified modifier flags. - @nonobjc public func typeKey(_ key: XCUIKeyboardKey, modifierFlags: XCUIElement.KeyModifierFlags) { - // Call the version of the method defined in XCTest.framework. - typeKey(key.rawValue, modifierFlags: modifierFlags) - } -} -#endif - -// --- Failure Formatting --- - -/// Register the failure, expected or unexpected, of the current test case. -func _XCTRegisterFailure(_ expected: Bool, _ condition: String, _ message: @autoclosure () -> String, _ file: StaticString, _ line: UInt) { - // Call the real _XCTFailureHandler. - let test = _XCTCurrentTestCase() - _XCTPreformattedFailureHandler(test, expected, file.description, Int(line), condition, message()) -} - -/// Produce a failure description for the given assertion type. -func _XCTFailureDescription(_ assertionType: _XCTAssertionType, _ formatIndex: Int, _ expressionStrings: CVarArg...) -> String { - // In order to avoid revlock/submission issues between XCTest and the Swift XCTest overlay, - // we are using the convention with _XCTFailureFormat that (formatIndex >= 100) should be - // treated just like (formatIndex - 100), but WITHOUT the expression strings. (Swift can't - // stringify the expressions, only their values.) This way we don't have to introduce a new - // BOOL parameter to this semi-internal function and coordinate that across builds. - // - // Since there's a single bottleneck in the overlay where we invoke _XCTFailureFormat, just - // add the formatIndex adjustment there rather than in all of the individual calls to this - // function. - - return String(format: _XCTFailureFormat(assertionType, formatIndex + 100), arguments: expressionStrings) -} - -// --- Exception Support --- - -/// The Swift-style result of evaluating a block which may throw an exception. -enum _XCTThrowableBlockResult { - case success - case failedWithError(error: Error) - case failedWithException(className: String, name: String, reason: String) - case failedWithUnknownException -} - -/// Asks some Objective-C code to evaluate a block which may throw an exception or error, -/// and if it does consume the exception and return information about it. -func _XCTRunThrowableBlock(_ block: () throws -> Void) -> _XCTThrowableBlockResult { - var blockErrorOptional: Error? - - let exceptionResult = _XCTRunThrowableBlockBridge({ - do { - try block() - } catch { - blockErrorOptional = error - } - }) - - if let blockError = blockErrorOptional { - return .failedWithError(error: blockError) - } else if let exceptionResult = exceptionResult { - - if exceptionResult["type"] == "objc" { - return .failedWithException( - className: exceptionResult["className"]!, - name: exceptionResult["name"]!, - reason: exceptionResult["reason"]!) - } else { - return .failedWithUnknownException - } - } else { - return .success - } -} - -// --- Supported Assertions --- - -public func XCTFail(_ message: String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.fail - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, "" as NSString), message, file, line) -} - -public func XCTAssertNil(_ expression: @autoclosure () throws -> Any?, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.`nil` - - // evaluate the expression exactly once - var expressionValueOptional: Any? - - let result = _XCTRunThrowableBlock { - expressionValueOptional = try expression() - } - - switch result { - case .success: - // test both Optional and value to treat .none and nil as synonymous - var passed: Bool - var expressionValueStr: String = "nil" - if let expressionValueUnwrapped = expressionValueOptional { - passed = false - expressionValueStr = "\(expressionValueUnwrapped)" - } else { - passed = true - } - - if !passed { - // TODO: @auto_string expression - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertNil failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssertNotNil(_ expression: @autoclosure () throws -> Any?, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.notNil - - // evaluate the expression exactly once - var expressionValueOptional: Any? - - let result = _XCTRunThrowableBlock { - expressionValueOptional = try expression() - } - - switch result { - case .success: - // test both Optional and value to treat .none and nil as synonymous - var passed: Bool - var expressionValueStr: String = "nil" - if let expressionValueUnwrapped = expressionValueOptional { - passed = true - expressionValueStr = "\(expressionValueUnwrapped)" - } else { - passed = false - } - - if !passed { - // TODO: @auto_string expression - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertNotNil failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssert(_ expression: @autoclosure () throws -> Bool, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - // XCTAssert is just a cover for XCTAssertTrue. - XCTAssertTrue(try expression(), message(), file: file, line: line) -} - -public func XCTAssertTrue(_ expression: @autoclosure () throws -> Bool, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.`true` - - // evaluate the expression exactly once - var expressionValueOptional: Bool? - - let result = _XCTRunThrowableBlock { - expressionValueOptional = try expression() - } - - switch result { - case .success: - let expressionValue = expressionValueOptional! - - if !expressionValue { - // TODO: @auto_string expression - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertTrue failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssertFalse(_ expression: @autoclosure () throws -> Bool, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.`false` - - // evaluate the expression exactly once - var expressionValueOptional: Bool? - - let result = _XCTRunThrowableBlock { - expressionValueOptional = try expression() - } - - switch result { - case .success: - let expressionValue = expressionValueOptional! - - if expressionValue { - // TODO: @auto_string expression - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertFalse failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssertEqual(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.equal - - // evaluate each expression exactly once - var expressionValue1Optional: T? - var expressionValue2Optional: T? - - let result = _XCTRunThrowableBlock { - expressionValue1Optional = try expression1() - expressionValue2Optional = try expression2() - } - - switch result { - case .success: - let expressionValue1: T = expressionValue1Optional! - let expressionValue2: T = expressionValue2Optional! - - if expressionValue1 != expressionValue2 { - // TODO: @auto_string expression1 - // TODO: @auto_string expression2 - - let expressionValueStr1 = "\(expressionValue1)" - let expressionValueStr2 = "\(expressionValue2)" - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr1 as NSString, expressionValueStr2 as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertEqual failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssertNotEqual(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.notEqual - - // evaluate each expression exactly once - var expressionValue1Optional: T? - var expressionValue2Optional: T? - - let result = _XCTRunThrowableBlock { - expressionValue1Optional = try expression1() - expressionValue2Optional = try expression2() - } - - switch result { - case .success: - let expressionValue1: T = expressionValue1Optional! - let expressionValue2: T = expressionValue2Optional! - - if expressionValue1 == expressionValue2 { - // TODO: @auto_string expression1 - // TODO: @auto_string expression2 - - let expressionValueStr1 = "\(expressionValue1)" - let expressionValueStr2 = "\(expressionValue2)" - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr1 as NSString, expressionValueStr2 as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertNotEqual failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -func _XCTCheckEqualWithAccuracy_Double(_ value1: Double, _ value2: Double, _ accuracy: Double) -> Bool { - return (!value1.isNaN && !value2.isNaN) - && (abs(value1 - value2) <= accuracy) -} - -func _XCTCheckEqualWithAccuracy_Float(_ value1: Float, _ value2: Float, _ accuracy: Float) -> Bool { - return (!value1.isNaN && !value2.isNaN) - && (abs(value1 - value2) <= accuracy) -} - -func _XCTCheckEqualWithAccuracy_CGFloat(_ value1: CGFloat, _ value2: CGFloat, _ accuracy: CGFloat) -> Bool { - return (!value1.isNaN && !value2.isNaN) - && (abs(value1 - value2) <= accuracy) -} - -public func XCTAssertEqual(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.equalWithAccuracy - - // evaluate each expression exactly once - var expressionValue1Optional: T? - var expressionValue2Optional: T? - - let result = _XCTRunThrowableBlock { - expressionValue1Optional = try expression1() - expressionValue2Optional = try expression2() - } - - switch result { - case .success: - let expressionValue1 = expressionValue1Optional! - let expressionValue2 = expressionValue2Optional! - - var equalWithAccuracy: Bool = false - - switch (expressionValue1, expressionValue2, accuracy) { - case let (expressionValue1Double as Double, expressionValue2Double as Double, accuracyDouble as Double): - equalWithAccuracy = _XCTCheckEqualWithAccuracy_Double(expressionValue1Double, expressionValue2Double, accuracyDouble) - - case let (expressionValue1Float as Float, expressionValue2Float as Float, accuracyFloat as Float): - equalWithAccuracy = _XCTCheckEqualWithAccuracy_Float(expressionValue1Float, expressionValue2Float, accuracyFloat) - - case let (expressionValue1CGFloat as CGFloat, expressionValue2CGFloat as CGFloat, accuracyCGFloat as CGFloat): - equalWithAccuracy = _XCTCheckEqualWithAccuracy_CGFloat(expressionValue1CGFloat, expressionValue2CGFloat, accuracyCGFloat) - - default: - // unknown type, fail with prejudice - preconditionFailure("Unsupported floating-point type passed to XCTAssertEqual") - } - - if !equalWithAccuracy { - // TODO: @auto_string expression1 - // TODO: @auto_string expression2 - - let expressionValueStr1 = "\(expressionValue1)" - let expressionValueStr2 = "\(expressionValue2)" - let accuracyStr = "\(accuracy)" - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr1 as NSString, expressionValueStr2 as NSString, accuracyStr as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertEqual failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -@available(*, deprecated, renamed: "XCTAssertEqual(_:_:accuracy:file:line:)") -public func XCTAssertEqualWithAccuracy(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(try expression1(), try expression2(), accuracy: accuracy, message(), file: file, line: line) -} - -func _XCTCheckNotEqualWithAccuracy_Double(_ value1: Double, _ value2: Double, _ accuracy: Double) -> Bool { - return (value1.isNaN || value2.isNaN) - || (abs(value1 - value2) > accuracy) -} - -func _XCTCheckNotEqualWithAccuracy_Float(_ value1: Float, _ value2: Float, _ accuracy: Float) -> Bool { - return (value1.isNaN || value2.isNaN) - || (abs(value1 - value2) > accuracy) -} - -func _XCTCheckNotEqualWithAccuracy_CGFloat(_ value1: CGFloat, _ value2: CGFloat, _ accuracy: CGFloat) -> Bool { - return (value1.isNaN || value2.isNaN) - || (abs(value1 - value2) > accuracy) -} - -public func XCTAssertNotEqual(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.notEqualWithAccuracy - - // evaluate each expression exactly once - var expressionValue1Optional: T? - var expressionValue2Optional: T? - - let result = _XCTRunThrowableBlock { - expressionValue1Optional = try expression1() - expressionValue2Optional = try expression2() - } - - switch result { - case .success: - let expressionValue1 = expressionValue1Optional! - let expressionValue2 = expressionValue2Optional! - - var notEqualWithAccuracy: Bool = false - - switch (expressionValue1, expressionValue2, accuracy) { - case let (expressionValue1Double as Double, expressionValue2Double as Double, accuracyDouble as Double): - notEqualWithAccuracy = _XCTCheckNotEqualWithAccuracy_Double(expressionValue1Double, expressionValue2Double, accuracyDouble) - - case let (expressionValue1Float as Float, expressionValue2Float as Float, accuracyFloat as Float): - notEqualWithAccuracy = _XCTCheckNotEqualWithAccuracy_Float(expressionValue1Float, expressionValue2Float, accuracyFloat) - - case let (expressionValue1CGFloat as CGFloat, expressionValue2CGFloat as CGFloat, accuracyCGFloat as CGFloat): - notEqualWithAccuracy = _XCTCheckNotEqualWithAccuracy_CGFloat(expressionValue1CGFloat, expressionValue2CGFloat, accuracyCGFloat) - - default: - // unknown type, fail with prejudice - preconditionFailure("Unsupported floating-point type passed to XCTAssertNotEqual") - } - - if !notEqualWithAccuracy { - // TODO: @auto_string expression1 - // TODO: @auto_string expression2 - - let expressionValueStr1 = "\(expressionValue1)" - let expressionValueStr2 = "\(expressionValue2)" - let accuracyStr = "\(accuracy)" - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr1 as NSString, expressionValueStr2 as NSString, accuracyStr as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertNotEqual failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -@available(*, deprecated, renamed: "XCTAssertNotEqual(_:_:accuracy:file:line:)") -public func XCTAssertNotEqualWithAccuracy(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, _ accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - XCTAssertNotEqual(try expression1(), try expression2(), accuracy: accuracy, message(), file: file, line: line) -} - -public func XCTAssertGreaterThan(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.greaterThan - - // evaluate each expression exactly once - var expressionValue1Optional: T? - var expressionValue2Optional: T? - - let result = _XCTRunThrowableBlock { - expressionValue1Optional = try expression1() - expressionValue2Optional = try expression2() - } - - switch result { - case .success: - let expressionValue1 = expressionValue1Optional! - let expressionValue2 = expressionValue2Optional! - - if !(expressionValue1 > expressionValue2) { - // TODO: @auto_string expression1 - // TODO: @auto_string expression2 - - let expressionValueStr1 = "\(expressionValue1)" - let expressionValueStr2 = "\(expressionValue2)" - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr1 as NSString, expressionValueStr2 as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertGreaterThan failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssertGreaterThanOrEqual(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) -{ - let assertionType = _XCTAssertionType.greaterThanOrEqual - - // evaluate each expression exactly once - var expressionValue1Optional: T? - var expressionValue2Optional: T? - - let result = _XCTRunThrowableBlock { - expressionValue1Optional = try expression1() - expressionValue2Optional = try expression2() - } - - switch result { - case .success: - let expressionValue1 = expressionValue1Optional! - let expressionValue2 = expressionValue2Optional! - - if !(expressionValue1 >= expressionValue2) { - // TODO: @auto_string expression1 - // TODO: @auto_string expression2 - - let expressionValueStr1 = "\(expressionValue1)" - let expressionValueStr2 = "\(expressionValue2)" - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr1 as NSString, expressionValueStr2 as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertGreaterThanOrEqual failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssertLessThan(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.lessThan - - // evaluate each expression exactly once - var expressionValue1Optional: T? - var expressionValue2Optional: T? - - let result = _XCTRunThrowableBlock { - expressionValue1Optional = try expression1() - expressionValue2Optional = try expression2() - } - - switch result { - case .success: - let expressionValue1 = expressionValue1Optional! - let expressionValue2 = expressionValue2Optional! - - if !(expressionValue1 < expressionValue2) { - // TODO: @auto_string expression1 - // TODO: @auto_string expression2 - - let expressionValueStr1 = "\(expressionValue1)" - let expressionValueStr2 = "\(expressionValue2)" - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr1 as NSString, expressionValueStr2 as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertLessThan failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssertLessThanOrEqual(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) -{ - let assertionType = _XCTAssertionType.lessThanOrEqual - - // evaluate each expression exactly once - var expressionValue1Optional: T? - var expressionValue2Optional: T? - - let result = _XCTRunThrowableBlock { - expressionValue1Optional = try expression1() - expressionValue2Optional = try expression2() - } - - switch result { - case .success: - let expressionValue1 = expressionValue1Optional! - let expressionValue2 = expressionValue2Optional! - - if !(expressionValue1 <= expressionValue2) { - // TODO: @auto_string expression1 - // TODO: @auto_string expression2 - - let expressionValueStr1 = "\(expressionValue1)" - let expressionValueStr2 = "\(expressionValue2)" - - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 0, expressionValueStr1 as NSString, expressionValueStr2 as NSString), message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertLessThanOrEqual failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(false, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -public func XCTAssertThrowsError(_ expression: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line, _ errorHandler: (_ error: Error) -> Void = { _ in }) { - // evaluate expression exactly once - var caughtErrorOptional: Error? - - let result = _XCTRunThrowableBlock { - do { - _ = try expression() - } catch { - caughtErrorOptional = error - } - } - - switch result { - case .success: - if let caughtError = caughtErrorOptional { - errorHandler(caughtError) - } else { - _XCTRegisterFailure(true, "XCTAssertThrowsError failed: did not throw an error", message(), file, line) - } - - case .failedWithError(let error): - _XCTRegisterFailure(false, "XCTAssertThrowsError failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(true, "XCTAssertThrowsError failed: throwing \(reason)", message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, "XCTAssertThrowsError failed: throwing an unknown exception", message(), file, line) - } -} - -public func XCTAssertNoThrow(_ expression: @autoclosure () throws -> T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.noThrow - - let result = _XCTRunThrowableBlock { _ = try expression() } - - switch result { - case .success: - return - - case .failedWithError(let error): - _XCTRegisterFailure(true, "XCTAssertNoThrow failed: threw error \"\(error)\"", message(), file, line) - - case .failedWithException(_, _, let reason): - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 1, reason as NSString), message(), file, line) - - case .failedWithUnknownException: - _XCTRegisterFailure(true, _XCTFailureDescription(assertionType, 2), message(), file, line) - } -} - -#if XCTEST_ENABLE_EXCEPTION_ASSERTIONS -// --- Currently-Unsupported Assertions --- - -public func XCTAssertThrows(_ expression: @autoclosure () -> Any?, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.assertion_Throws - - // FIXME: Unsupported -} - -public func XCTAssertThrowsSpecific(_ expression: @autoclosure () -> Any?, _ exception: Any, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.assertion_ThrowsSpecific - - // FIXME: Unsupported -} - -public func XCTAssertThrowsSpecificNamed(_ expression: @autoclosure () -> Any?, _ exception: Any, _ name: String, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.assertion_ThrowsSpecificNamed - - // FIXME: Unsupported -} - -public func XCTAssertNoThrowSpecific(_ expression: @autoclosure () -> Any?, _ exception: Any, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.assertion_NoThrowSpecific - - // FIXME: Unsupported -} - -public func XCTAssertNoThrowSpecificNamed(_ expression: @autoclosure () -> Any?, _ exception: Any, _ name: String, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) { - let assertionType = _XCTAssertionType.assertion_NoThrowSpecificNamed - - // FIXME: Unsupported -} -#endif diff --git a/stdlib/public/Darwin/XCTest/XCTestCaseAdditions.mm b/stdlib/public/Darwin/XCTest/XCTestCaseAdditions.mm deleted file mode 100644 index 8181dd1d58d3d..0000000000000 --- a/stdlib/public/Darwin/XCTest/XCTestCaseAdditions.mm +++ /dev/null @@ -1,174 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2020 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 -// -//===----------------------------------------------------------------------===// - -// The actual XCTest overlay is not maintained in this repository -- it is -// distributed in Xcode (libXCTestSwiftSupport.dylib), along with -// XCTest.framework itself. -// -// The codebase here builds the obsolete /usr/lib/swift/libswiftXCTest.dylib -// that currently ships in the OS. There is no expectation that that library is -// usable for anything; it only exists to maintain a superficial level of binary -// compatibility with existing apps that happen to link to it by mistake. -// -// rdar://problem/55270944 - -#import -#include "swift/Runtime/Metadata.h" -#include "swift/Demangling/Demangle.h" -#include "swift/Demangling/ManglingUtils.h" -#include "swift/Strings.h" - -// NOTE: This is a temporary workaround. -// XCTestCase needs the unmangled version of a test case name, so let -className -// return the demangled name for a test case class. NSStringFromClass will still -// return the mangled name. - -@interface XCTest (WarningAvoidance) -@property (readonly, copy) NSString *className; -@end - -static char *scanIdentifier(const char *&mangled) -{ - const char *original = mangled; - - { - if (*mangled == '0') goto fail; // length may not be zero - - size_t length = 0; - while (swift::Mangle::isDigit(*mangled)) { - size_t oldlength = length; - length *= 10; - length += *mangled++ - '0'; - if (length <= oldlength) goto fail; // integer overflow - } - - if (length == 0) goto fail; - if (length > strlen(mangled)) goto fail; - - char *result = strndup(mangled, length); - assert(result); - mangled += length; - return result; - } - -fail: - mangled = original; // rewind - return nullptr; -} - - -/// Demangle a mangled class name into module+class. -/// Returns true if the name was successfully decoded. -/// On success, *outModule and *outClass must be freed with free(). -/// FIXME: this should be replaced by a real demangler -static bool demangleSimpleClass(const char *mangledName, - char **outModule, char **outClass) { - char *moduleName = nullptr; - char *className = nullptr; - - { - // Prefix for a mangled class - const char *m = mangledName; - if (0 != strncmp(m, "_TtC", 4)) - goto fail; - m += 4; - - // Module name - if (strncmp(m, "Ss", 2) == 0) { - moduleName = - strndup(swift::STDLIB_NAME.data(), swift::STDLIB_NAME.size()); - assert(moduleName); - m += 2; - } else { - moduleName = scanIdentifier(m); - if (!moduleName) - goto fail; - } - - // Class name - className = scanIdentifier(m); - if (!className) - goto fail; - - // Nothing else - if (strlen(m)) - goto fail; - - *outModule = moduleName; - *outClass = className; - return true; - } - -fail: - if (moduleName) free(moduleName); - if (className) free(className); - *outModule = nullptr; - *outClass = nullptr; - return false; -} - -@implementation XCTestCase (SwiftAdditions) - -- (NSString *)className -{ - NSString *className = [super className]; - - char *modulePart; - char *classPart; - bool ok = demangleSimpleClass([className UTF8String], - &modulePart, &classPart); - if (ok) { - className = [NSString stringWithUTF8String:classPart]; - - free(modulePart); - free(classPart); - } - - return className; -} - -@end - - -// Since Swift doesn't natively support exceptions, but Objective-C code can -// still throw them, use a helper to evaluate a block that may result in an -// exception being thrown that passes back the most important information about -// it. -// -// If no exception is thrown by the block, returns an empty dictionary. - -XCT_EXPORT NSDictionary *_XCTRunThrowableBlockBridge(void (^block)()); - -NSDictionary *_XCTRunThrowableBlockBridge(void (^block)()) -{ - NSDictionary *result = nil; - - @try { - block(); - } - @catch (_XCTestCaseInterruptionException *interruption) { [interruption raise]; } - @catch (NSException *exception) { - result = @{ - @"type": @"objc", - @"className": NSStringFromClass(exception.class), - @"name": exception.name, - @"reason": exception.reason, - }; - } - @catch (...) { - result = @{ - @"type": @"unknown", - }; - } - - return result; -} diff --git a/stdlib/public/Differentiation/CMakeLists.txt b/stdlib/public/Differentiation/CMakeLists.txt index ffdf32aec3dcc..55d0b3189c7b5 100644 --- a/stdlib/public/Differentiation/CMakeLists.txt +++ b/stdlib/public/Differentiation/CMakeLists.txt @@ -41,5 +41,6 @@ add_swift_target_library(swift_Differentiation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPE SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -parse-stdlib + -Xfrontend -requirement-machine=off LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" INSTALL_IN_COMPONENT stdlib) diff --git a/stdlib/public/Distributed/ActorTransport.swift b/stdlib/public/Distributed/ActorTransport.swift new file mode 100644 index 0000000000000..a3dbb3e129669 --- /dev/null +++ b/stdlib/public/Distributed/ActorTransport.swift @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020-2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift +import _Concurrency + +@available(SwiftStdlib 5.5, *) +public protocol ActorTransport: Sendable { + + // ==== --------------------------------------------------------------------- + // - MARK: Resolving actors by identity + + /// Upon decoding of a `AnyActorIdentity` this function will be called + /// on the transport that is set un the Decoder's `userInfo` at decoding time. + /// This user info is to be set by the transport, as it receives messages and + /// attempts decoding them. This way, messages received on a specific transport + /// (which may be representing a connection, or general transport mechanism) + /// is able to utilize local knowledge and type information about what kind of + /// identity to attempt to decode. + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity + + /// Resolve a local or remote actor address to a real actor instance, or throw if unable to. + /// The returned value is either a local actor or proxy to a remote actor. + /// + /// Resolving an actor is called when a specific distributed actors `init(from:)` + /// decoding initializer is invoked. Once the actor's identity is deserialized + /// using the `decodeIdentity(from:)` call, it is fed into this function, which + /// is responsible for resolving the identity to a remote or local actor reference. + /// + /// If the resolve fails, meaning that it cannot locate a local actor managed for + /// this identity, managed by this transport, nor can a remote actor reference + /// be created for this identity on this transport, then this function must throw. + /// + /// If this function returns correctly, the returned actor reference is immediately + /// usable. It may not necessarily imply the strict *existence* of a remote actor + /// the identity was pointing towards, e.g. when a remote system allocates actors + /// lazily as they are first time messaged to, however this should not be a concern + /// of the sending side. + /// + /// Detecting liveness of such remote actors shall be offered / by transport libraries + /// by other means, such as "watching an actor for termination" or similar. + func resolve(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act? + where Act: DistributedActor + + // ==== --------------------------------------------------------------------- + // - MARK: Actor Lifecycle + + /// Create an `ActorIdentity` for the passed actor type. + /// + /// This function is invoked by an distributed actor during its initialization, + /// and the returned address value is stored along with it for the time of its + /// lifetime. + /// + /// The address MUST uniquely identify the actor, and allow resolving it. + /// E.g. if an actor is created under address `addr1` then immediately invoking + /// `transport.resolve(address: addr1, as: Greeter.self)` MUST return a reference + /// to the same actor. + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor + + func actorReady(_ actor: Act) where Act: DistributedActor + + /// Called during actor deinit/destroy. + func resignIdentity(_ id: AnyActorIdentity) + +} diff --git a/stdlib/public/Distributed/AssertDistributed.swift b/stdlib/public/Distributed/AssertDistributed.swift new file mode 100644 index 0000000000000..3855110c56516 --- /dev/null +++ b/stdlib/public/Distributed/AssertDistributed.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +/// Report a call to a _remote function on a distributed (remote) actor, +/// that was not dynamically replaced by some specific ActorTransport library. +@_transparent +public func _missingDistributedActorTransport( + className: StaticString, functionName: StaticString, + file: StaticString, line: UInt, column: UInt +) -> Never { + // This function is marked @_transparent so that it is inlined into the caller + // (the remote function stub), and, depending on the build configuration, + // redundant parameter values (#file etc.) are eliminated, and don't leak + // information about the user's source. + fatalError( + """ + Invoked remote placeholder function '\(functionName)' on remote \ + distributed actor of type '\(className)'. Configure an appropriate \ + 'ActorTransport' for this actor to resolve this error (e.g. by depending \ + on some specific transport library). + """, file: file, line: line) +} \ No newline at end of file diff --git a/stdlib/public/Distributed/CMakeLists.txt b/stdlib/public/Distributed/CMakeLists.txt new file mode 100644 index 0000000000000..e0441a9da68e3 --- /dev/null +++ b/stdlib/public/Distributed/CMakeLists.txt @@ -0,0 +1,43 @@ +#===--- CMakeLists.txt - Concurrency support library ---------------------===# +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2019 - 2020 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 +# +#===----------------------------------------------------------------------===# + +set(swift_distributed_link_libraries + swiftCore) + + +add_swift_target_library(swift_Distributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + AssertDistributed.swift + ActorTransport.swift + DistributedActor.swift + + SWIFT_MODULE_DEPENDS_LINUX Glibc + SWIFT_MODULE_DEPENDS_FREEBSD Glibc + SWIFT_MODULE_DEPENDS_OPENBSD Glibc + SWIFT_MODULE_DEPENDS_CYGWIN Glibc + SWIFT_MODULE_DEPENDS_HAIKU Glibc + SWIFT_MODULE_DEPENDS_WINDOWS CRT + + LINK_LIBRARIES ${swift_distributed_link_libraries} + + C_COMPILE_FLAGS + -Dswift_Distributed_EXPORTS + SWIFT_COMPILE_FLAGS + ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + -parse-stdlib + -Xfrontend -enable-experimental-distributed + -Xfrontend -define-availability + -Xfrontend "SwiftStdlib 5.5:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + + SWIFT_MODULE_DEPENDS _Concurrency + INSTALL_IN_COMPONENT stdlib +) diff --git a/stdlib/public/Distributed/DistributedActor.swift b/stdlib/public/Distributed/DistributedActor.swift new file mode 100644 index 0000000000000..394fcb0d81df2 --- /dev/null +++ b/stdlib/public/Distributed/DistributedActor.swift @@ -0,0 +1,230 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 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 +// +//===----------------------------------------------------------------------===// + +import Swift +import _Concurrency + +// ==== Any Actor ------------------------------------------------------------- + +/// Shared "base" protocol for both (local) `Actor` and (potentially remote) +/// `DistributedActor`. +/// +/// FIXME(distributed): We'd need Actor to also conform to this, but don't want to add that conformance in _Concurrency yet. +@_marker +@available(SwiftStdlib 5.5, *) +public protocol AnyActor: Sendable, AnyObject {} + +// ==== Distributed Actor ----------------------------------------------------- + +/// Common protocol to which all distributed actors conform implicitly. +/// +/// It is not possible to conform to this protocol manually explicitly. +/// Only a 'distributed actor' declaration or protocol with 'DistributedActor' +/// requirement may conform to this protocol. +/// +/// The 'DistributedActor' protocol provides the core functionality of any +/// distributed actor. +@available(SwiftStdlib 5.5, *) +public protocol DistributedActor: + AnyActor, Identifiable, Hashable, Codable { + /// Resolves the passed in `identity` against the `transport`, returning + /// either a local or remote actor reference. + /// + /// The transport will be asked to `resolve` the identity and return either + /// a local instance or request a proxy to be created for this identity. + /// + /// A remote distributed actor reference will forward all invocations through + /// the transport, allowing it to take over the remote messaging with the + /// remote actor instance. + /// + /// - Parameter identity: identity uniquely identifying a, potentially remote, actor in the system + /// - Parameter transport: `transport` which should be used to resolve the `identity`, and be associated with the returned actor +// FIXME: Partially blocked on SE-309, because then we can store ActorIdentity directly +// We want to move to accepting a generic or existential identity here +// static func resolve(_ identity: Identity, using transport: ActorTransport) +// throws -> Self where Identity: ActorIdentity + static func resolve(_ identity: AnyActorIdentity, using transport: ActorTransport) + throws -> Self + + /// The `ActorTransport` associated with this actor. + /// It is immutable and equal to the transport passed in the local/resolve + /// initializer. + /// + /// Conformance to this requirement is synthesized automatically for any + /// `distributed actor` declaration. + nonisolated var actorTransport: ActorTransport { get } // TODO: rename to `transport`? + + /// Logical identity of this distributed actor. + /// + /// Many distributed actor references may be pointing at, logically, the same actor. + /// For example, calling `resolve(address:using:)` multiple times, is not guaranteed + /// to return the same exact resolved actor instance, however all the references would + /// represent logically references to the same distributed actor, e.g. on a different node. + /// + /// An address is always uniquely pointing at a specific actor instance. + /// + /// Conformance to this requirement is synthesized automatically for any + /// `distributed actor` declaration. + nonisolated var id: AnyActorIdentity { get } +} + +// ==== Hashable conformance --------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +extension DistributedActor { + nonisolated public func hash(into hasher: inout Hasher) { + self.id.hash(into: &hasher) + } + + nonisolated public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.id == rhs.id + } +} + +// ==== Codable conformance ---------------------------------------------------- + +extension CodingUserInfoKey { + @available(SwiftStdlib 5.5, *) + public static let actorTransportKey = CodingUserInfoKey(rawValue: "$dist_act_transport")! +} + +@available(SwiftStdlib 5.5, *) +extension DistributedActor { + nonisolated public init(from decoder: Decoder) throws { + guard let transport = decoder.userInfo[.actorTransportKey] as? ActorTransport else { + throw DistributedActorCodingError(message: + "Missing ActorTransport (for key .actorTransportKey) " + + "in Decoder.userInfo, while decoding \(Self.self).") + } + + let id: AnyActorIdentity = try transport.decodeIdentity(from: decoder) + self = try Self.resolve(id, using: transport) + } + + nonisolated public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.id) + } +} + +/******************************************************************************/ +/***************************** Actor Identity *********************************/ +/******************************************************************************/ + +/// Uniquely identifies a distributed actor, and enables sending messages and identifying remote actors. +@available(SwiftStdlib 5.5, *) +public protocol ActorIdentity: Sendable, Hashable, Codable {} + +@available(SwiftStdlib 5.5, *) +public struct AnyActorIdentity: ActorIdentity, @unchecked Sendable, CustomStringConvertible { + public let underlying: Any + @usableFromInline let _hashInto: (inout Hasher) -> () + @usableFromInline let _equalTo: (Any) -> Bool + @usableFromInline let _encodeTo: (Encoder) throws -> () + @usableFromInline let _description: () -> String + + public init(_ identity: ID) where ID: ActorIdentity { + self.underlying = identity + _hashInto = { hasher in identity + .hash(into: &hasher) + } + _equalTo = { other in + guard let otherAnyIdentity = other as? AnyActorIdentity else { + return false + } + guard let rhs = otherAnyIdentity.underlying as? ID else { + return false + } + return identity == rhs + } + _encodeTo = { encoder in + try identity.encode(to: encoder) + } + _description = { () in + "\(identity)" + } + } + + public init(from decoder: Decoder) throws { + let userInfoTransport = decoder.userInfo[.actorTransportKey] + guard let transport = userInfoTransport as? ActorTransport else { + throw DistributedActorCodingError(message: + "ActorTransport not available under the decoder.userInfo") + } + + self = try transport.decodeIdentity(from: decoder) + } + + public func encode(to encoder: Encoder) throws { + try _encodeTo(encoder) + } + + public var description: String { + "\(Self.self)(\(self._description()))" + } + + public func hash(into hasher: inout Hasher) { + _hashInto(&hasher) + } + + public static func == (lhs: AnyActorIdentity, rhs: AnyActorIdentity) -> Bool { + lhs._equalTo(rhs) + } +} + +/******************************************************************************/ +/******************************** Misc ****************************************/ +/******************************************************************************/ + +/// Error protocol to which errors thrown by any `ActorTransport` should conform. +@available(SwiftStdlib 5.5, *) +public protocol ActorTransportError: Error { +} + +@available(SwiftStdlib 5.5, *) +public struct DistributedActorCodingError: ActorTransportError { + public let message: String + + public init(message: String) { + self.message = message + } + + public static func missingTransportUserInfo(_ actorType: Act.Type) -> Self + where Act: DistributedActor { + .init(message: "Missing ActorTransport userInfo while decoding") + } +} + +/******************************************************************************/ +/************************* Runtime Functions **********************************/ +/******************************************************************************/ + +// ==== isRemote / isLocal ----------------------------------------------------- + +@_silgen_name("swift_distributed_actor_is_remote") +func __isRemoteActor(_ actor: AnyObject) -> Bool + +func __isLocalActor(_ actor: AnyObject) -> Bool { + return !__isRemoteActor(actor) +} + +// ==== Proxy Actor lifecycle -------------------------------------------------- + +@_silgen_name("swift_distributedActor_remote_initialize") +func _distributedActorRemoteInitialize(_ actorType: Builtin.RawPointer) -> Any + +/// Called to destroy the default actor instance in an actor. +/// The implementation will call this within the actor's deinit. +/// +/// This will call `actorTransport.resignIdentity(self.id)`. +@_silgen_name("swift_distributedActor_destroy") +func _distributedActorDestroy(_ actor: AnyObject) diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index a7a60a063d9aa..cb6d700ff641d 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -11,6 +11,10 @@ if(NOT BUILD_STANDALONE) list(APPEND darwin_depends copy_apinotes) endif() +set(swiftDarwin_target_sdks ALL_APPLE_PLATFORMS) +if(SWIFT_FREESTANDING_FLAVOR STREQUAL "apple") + set(swiftDarwin_target_sdks ALL_APPLE_PLATFORMS FREESTANDING) +endif() add_swift_target_library(swiftDarwin ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY ${swift_platform_sources} POSIXError.swift @@ -27,11 +31,15 @@ add_swift_target_library(swiftDarwin ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_ ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} -Xfrontend -disable-objc-attr-requires-foundation-module LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - TARGET_SDKS ALL_APPLE_PLATFORMS FREESTANDING + TARGET_SDKS "${swiftDarwin_target_sdks}" INSTALL_IN_COMPONENT sdk-overlay DEPENDS ${darwin_depends}) +set(swiftGlibc_target_sdks ANDROID CYGWIN FREEBSD OPENBSD LINUX HAIKU) +if(SWIFT_FREESTANDING_FLAVOR STREQUAL "linux") + set(swiftGlibc_target_sdks ANDROID CYGWIN FREEBSD OPENBSD LINUX HAIKU FREESTANDING) +endif() add_swift_target_library(swiftGlibc ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY ${swift_platform_sources} POSIXError.swift @@ -44,7 +52,7 @@ add_swift_target_library(swiftGlibc ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_O ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - TARGET_SDKS ANDROID CYGWIN FREEBSD OPENBSD LINUX HAIKU + TARGET_SDKS "${swiftGlibc_target_sdks}" INSTALL_IN_COMPONENT sdk-overlay DEPENDS glibc_modulemap) diff --git a/stdlib/public/Platform/winsdk.modulemap b/stdlib/public/Platform/winsdk.modulemap index fd5fdb30611ea..4cacbf9bdbe90 100644 --- a/stdlib/public/Platform/winsdk.modulemap +++ b/stdlib/public/Platform/winsdk.modulemap @@ -485,6 +485,13 @@ module WinSDK [system] { link "AdvAPI32.Lib" } + + module WinUSB { + header "winusb.h" + export * + + link "winusb.lib" + } // TODO(compnerd) does it make sense to implicitly export this API surface? // It seems to be meant for device drivers. diff --git a/stdlib/public/Reflection/TypeRef.cpp b/stdlib/public/Reflection/TypeRef.cpp index 703fd9889f26a..d89dae57cccc4 100644 --- a/stdlib/public/Reflection/TypeRef.cpp +++ b/stdlib/public/Reflection/TypeRef.cpp @@ -137,6 +137,40 @@ class PrintTypeRef : public TypeRefVisitor { break; } + switch (F->getDifferentiabilityKind().Value) { + case FunctionMetadataDifferentiabilityKind::NonDifferentiable: + break; + + case FunctionMetadataDifferentiabilityKind::Forward: + printField("differentiable", "forward"); + break; + + case FunctionMetadataDifferentiabilityKind::Reverse: + printField("differentiable", "reverse"); + break; + + case FunctionMetadataDifferentiabilityKind::Normal: + printField("differentiable", "normal"); + break; + + case FunctionMetadataDifferentiabilityKind::Linear: + printField("differentiable", "linear"); + break; + } + + if (auto globalActor = F->getGlobalActor()) { + fprintf(file, "\n"); + Indent += 2; + printHeader("global-actor"); + { + Indent += 2; + printRec(globalActor); + fprintf(file, ")"); + Indent -= 2; + } + Indent += 2; + } + fprintf(file, "\n"); Indent += 2; printHeader("parameters"); @@ -165,6 +199,9 @@ class PrintTypeRef : public TypeRefVisitor { break; } + if (flags.isIsolated()) + printHeader("isolated"); + if (flags.isVariadic()) printHeader("variadic"); @@ -599,6 +636,9 @@ class DemanglingForTypeRef wrapInput(Node::Kind::Owned); break; } + if (flags.isIsolated()) { + wrapInput(Node::Kind::Isolated); + } inputs.push_back({input, flags.isVariadic()}); } @@ -663,6 +703,34 @@ class DemanglingForTypeRef result->addChild(resultTy, Dem); auto funcNode = Dem.createNode(kind); + if (auto globalActor = F->getGlobalActor()) { + auto node = Dem.createNode(Node::Kind::GlobalActorFunctionType); + auto globalActorNode = visit(globalActor); + node->addChild(globalActorNode, Dem); + funcNode->addChild(node, Dem); + } + + if (F->getFlags().isDifferentiable()) { + MangledDifferentiabilityKind mangledKind; + switch (F->getDifferentiabilityKind().Value) { +#define CASE(X) case FunctionMetadataDifferentiabilityKind::X: \ + mangledKind = MangledDifferentiabilityKind::X; break; + + CASE(NonDifferentiable) + CASE(Forward) + CASE(Reverse) + CASE(Normal) + CASE(Linear) +#undef CASE + } + + funcNode->addChild( + Dem.createNode( + Node::Kind::DifferentiableFunctionType, + (Node::IndexType)mangledKind), + Dem); + } + if (F->getFlags().isThrowing()) funcNode->addChild(Dem.createNode(Node::Kind::ThrowsAnnotation), Dem); if (F->getFlags().isSendable()) { @@ -992,11 +1060,16 @@ class ThickenMetatype SubstitutedParams.push_back(Param.withType(visit(typeRef))); } + const TypeRef *globalActorType = nullptr; + if (F->getGlobalActor()) + globalActorType = visit(F->getGlobalActor()); + auto SubstitutedResult = visit(F->getResult()); return FunctionTypeRef::create(Builder, SubstitutedParams, SubstitutedResult, F->getFlags(), - F->getDifferentiabilityKind()); + F->getDifferentiabilityKind(), + globalActorType); } const TypeRef * @@ -1114,9 +1187,14 @@ class TypeRefSubstitution auto SubstitutedResult = visit(F->getResult()); + const TypeRef *globalActorType = nullptr; + if (F->getGlobalActor()) + globalActorType = visit(F->getGlobalActor()); + return FunctionTypeRef::create(Builder, SubstitutedParams, SubstitutedResult, F->getFlags(), - F->getDifferentiabilityKind()); + F->getDifferentiabilityKind(), + globalActorType); } const TypeRef * diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index fb7fcc7b8e6b9..13d07b742a00a 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -84,13 +84,22 @@ RemoteRef TypeRefBuilder::readTypeRef(uint64_t remoteAddr) { } /// Load and normalize a mangled name so it can be matched with string equality. -std::string +llvm::Optional TypeRefBuilder::normalizeReflectionName(RemoteRef reflectionName) { // Remangle the reflection name to resolve symbolic references. - if (auto node = demangleTypeRef(reflectionName)) { - auto result = mangleNode(node); - clearNodeFactory(); - return result; + if (auto node = demangleTypeRef(reflectionName, + /*useOpaqueTypeSymbolicReferences*/ false)) { + switch (node->getKind()) { + case Node::Kind::TypeSymbolicReference: + case Node::Kind::ProtocolSymbolicReference: + case Node::Kind::OpaqueTypeDescriptorSymbolicReference: + // Symbolic references cannot be mangled, return a failure. + return {}; + default: + auto result = mangleNode(node); + clearNodeFactory(); + return result; + } } // Fall back to the raw string. @@ -102,7 +111,9 @@ bool TypeRefBuilder::reflectionNameMatches(RemoteRef reflectionName, StringRef searchName) { auto normalized = normalizeReflectionName(reflectionName); - return searchName.equals(normalized); + if (!normalized) + return false; + return searchName.equals(*normalized); } const TypeRef * TypeRefBuilder:: @@ -194,8 +205,8 @@ TypeRefBuilder::getFieldTypeInfo(const TypeRef *TR) { if (!FD->hasMangledTypeName()) continue; auto CandidateMangledName = readTypeRef(FD, FD->MangledTypeName); - auto NormalizedName = normalizeReflectionName(CandidateMangledName); - FieldTypeInfoCache[NormalizedName] = FD; + if (auto NormalizedName = normalizeReflectionName(CandidateMangledName)) + FieldTypeInfoCache[*NormalizedName] = FD; } } diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 67c0276cdefad..055766a11871e 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -17,6 +17,8 @@ extern "C" { SWIFT_REMOTE_MIRROR_LINKAGE unsigned long long swift_reflection_classIsSwiftMask = 2; + +SWIFT_REMOTE_MIRROR_LINKAGE uint32_t swift_reflection_libraryVersion = 1; } #include "swift/Demangling/Demangler.h" @@ -282,6 +284,13 @@ swift_reflection_metadataForObject(SwiftReflectionContextRef ContextRef, return *MetadataAddress; } +swift_reflection_ptr_t +swift_reflection_metadataNominalTypeDescriptor(SwiftReflectionContextRef ContextRef, + swift_reflection_ptr_t MetadataAddress) { + auto Context = ContextRef->nativeContext; + return Context->nominalTypeDescriptorFromMetadata(MetadataAddress); +} + swift_typeref_t swift_reflection_typeRefForInstance(SwiftReflectionContextRef ContextRef, uintptr_t Object) { @@ -571,6 +580,23 @@ int swift_reflection_projectExistential(SwiftReflectionContextRef ContextRef, return Success; } +int swift_reflection_projectExistentialAndUnwrapClass(SwiftReflectionContextRef ContextRef, + swift_addr_t ExistentialAddress, + swift_typeref_t ExistentialTypeRef, + swift_typeref_t *InstanceTypeRef, + swift_addr_t *StartOfInstanceData) { + auto Context = ContextRef->nativeContext; + auto ExistentialTR = reinterpret_cast(ExistentialTypeRef); + auto RemoteExistentialAddress = RemoteAddress(ExistentialAddress); + auto Pair = Context->projectExistentialAndUnwrapClass( + RemoteExistentialAddress, *ExistentialTR); + if (!Pair.hasValue()) + return false; + *InstanceTypeRef = reinterpret_cast(std::get(*Pair)); + *StartOfInstanceData = std::get(*Pair).getAddressData(); + + return true; +} int swift_reflection_projectEnumValue(SwiftReflectionContextRef ContextRef, swift_addr_t EnumAddress, swift_typeref_t EnumTypeRef, @@ -761,4 +787,4 @@ const char *swift_reflection_iterateAsyncTaskAllocations( Call(AllocationPtr, Count, ConvertedChunks.data(), ContextPtr); }); return returnableCString(ContextRef, Error); -} \ No newline at end of file +} diff --git a/stdlib/public/SwiftShims/AppKitOverlayShims.h b/stdlib/public/SwiftShims/AppKitOverlayShims.h deleted file mode 100644 index 245b0041ee05e..0000000000000 --- a/stdlib/public/SwiftShims/AppKitOverlayShims.h +++ /dev/null @@ -1,124 +0,0 @@ -//===--- AppKitOverlayShims.h ---===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2020 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 -// -//===--------------------===// - -#ifndef SWIFT_STDLIB_SHIMS_APPKIT_OVERLAY_H -#define SWIFT_STDLIB_SHIMS_APPKIT_OVERLAY_H - -#include - -#if !TARGET_OS_IPHONE - -@import AppKit; - - -//===--------------------===// -// diffable data source // -//===--------------------===// - -#if __has_feature(nullability) -#pragma clang assume_nonnull begin -#endif - -typedef NSCollectionViewItem * _Nullable(^NSDiffableDataSourceCollectionViewItemProvider)(NSCollectionView*, NSIndexPath *indexPath, id identifier) API_UNAVAILABLE(ios); -typedef NSView * _Nullable(^NSDiffableDataSourceSupplementaryViewProvider)(NSCollectionView *, NSString *kind, NSIndexPath *indexPath) API_UNAVAILABLE(ios); - -@class __NSDiffableDataSourceSnapshot; - -API_AVAILABLE(macos(10.15)) API_UNAVAILABLE(ios) -@interface __NSDiffableDataSource : NSObject - -- (instancetype)initWithNSCollectionView:(NSCollectionView*)nsCollectionView - itemProvider:(NSDiffableDataSourceCollectionViewItemProvider)itemProvider; - -@property(nonatomic,weak,readonly,nullable) NSCollectionView *nsCollectionView; -@property(nonatomic,nullable,copy) NSDiffableDataSourceSupplementaryViewProvider nsSupplementaryViewProvider; - -- (NSString*)description; - -- (instancetype)init NS_UNAVAILABLE; - -@property(nonatomic,readonly) NSInteger numberOfItems; -@property(nonatomic,readonly) NSInteger numberOfSections; -@property(nonatomic,readonly) NSArray *sectionIdentifiers; -@property(nonatomic,readonly) NSArray *itemIdentifiers; - -- (NSInteger)numberOfItemsInSection:(id)sectionIdentifier; -- (NSArray*)itemIdentifiersInSectionWithIdentifier:(id)sectionIdentifier; -- (nullable id)sectionIdentifierForSectionContainingItemIdentifier:(id)identifier; - -- (NSInteger)indexOfItemIdentifier:(id)itemIdentifier; -- (NSInteger)indexOfSectionIdentifier:(id)sectionIdentifier; - -- (void)appendItemsWithIdentifiers:(NSArray*)identifiers; -- (void)appendItemsWithIdentifiers:(NSArray*)identifiers intoSectionWithIdentifier:(id _Nullable)sectionIdentifier; - -- (void)insertItemsWithIdentifiers:(NSArray*)identifiers beforeItemWithIdentifier:(id)itemIdentifier; -- (void)insertItemsWithIdentifiers:(NSArray*)identifiers afterItemWithIdentifier:(id)itemIdentifier; - -- (void)deleteItemsWithIdentifiers:(NSArray*)identifiers; -- (void)deleteAllItems; - -- (void)moveItemWithIdentifier:(id)fromIdentifier beforeItemWithIdentifier:(id)toIdentifier; -- (void)moveItemWithIdentifier:(id)fromIdentifier afterItemWithIdentifier:(id)toIdentifier; - -- (void)reloadItemsWithIdentifiers:(NSArray*)identifiers; - -- (void)appendSectionsWithIdentifiers:(NSArray*)sectionIdentifiers; - -- (void)insertSectionsWithIdentifiers:(NSArray*)sectionIdentifiers beforeSectionWithIdentifier:(id)toSectionIdentifier; -- (void)insertSectionsWithIdentifiers:(NSArray*)sectionIdentifiers afterSectionWithIdentifier:(id)toSectionIdentifier; - -- (void)deleteSectionsWithIdentifiers:(NSArray*)sectionIdentifiers; - -- (void)moveSectionWithIdentifier:(id)fromSectionIdentifier beforeSectionWithIdentifier:(id)toSectionIdentifier; -- (void)moveSectionWithIdentifier:(id)fromSectionIdentifier afterSectionWithIdentifier:(id)toSectionIdentifier; - -- (void)reloadSectionsWithIdentifiers:(NSArray*)sectionIdentifiers; - -- (nullable id)itemIdentifierForIndexPath:(NSIndexPath*)indexPath; -- (nullable NSIndexPath*)indexPathForItemIdentifier:(id)identifier; - -- (__NSDiffableDataSourceSnapshot*)snapshot; -- (__NSDiffableDataSourceSnapshot*)emptySnapshot; -- (void)applyDifferencesFromSnapshot:(__NSDiffableDataSourceSnapshot*)snapshot; -- (void)reloadFromSnapshot:(__NSDiffableDataSourceSnapshot*)snapshot; -- (void)applyDifferencesFromSnapshot:(__NSDiffableDataSourceSnapshot *)snapshot animatingDifferences:(BOOL)animatingDifferences; -- (void)applyDifferencesFromSnapshot:(__NSDiffableDataSourceSnapshot *)snapshot animatingDifferences:(BOOL)animatingDifferences completion:(void(^ _Nullable)(void))completion; - -@property(nonatomic,copy) NSDiffableDataSourceCollectionViewItemProvider collectionViewItemProvider; - -- (NSInteger)_numberOfSectionsForNSCollectionView:(NSCollectionView*)collectionView NS_SWIFT_NAME(_numberOfSectionsForNSCollectionView(_:)); -- (NSInteger)_numberOfItemsInSection:(NSInteger)section nsCollectionView:(NSCollectionView*)collectionView NS_SWIFT_NAME(_numberOfItemsInSection(_:nsCollectionView:)); -- (NSCollectionViewItem *)_itemAtIndexPath:(NSIndexPath*)indexPath nsCollectionView:(NSCollectionView*)collectionView NS_SWIFT_NAME(_itemAtIndexPath(_:nsCollectionView:)); -- (NSView *)_viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath nsCollectionView:(NSCollectionView *)collectionView NS_SWIFT_NAME(_viewForSupplementaryElementOfKind(_:atIndexPath:nsCollectionView:)); - -@end - - -API_AVAILABLE(macos(10.15)) API_UNAVAILABLE(ios) -@interface __NSDiffableDataSourceSnapshot : __NSDiffableDataSource -- (instancetype)init; -@end - -API_AVAILABLE(macos(10.15)) API_UNAVAILABLE(ios) -@interface NSDiffableDataSourceSnapshot() -@property(nonatomic,readonly) __NSDiffableDataSourceSnapshot *impl; -- (instancetype)initWithDataSource:(__NSDiffableDataSource * _Nullable)dataSource; -@end - -#if __has_feature(nullability) -#pragma clang assume_nonnull end -#endif - -#endif // !TARGET_OS_IPHONE - -#endif // SWIFT_STDLIB_SHIMS_APPKIT_OVERLAY_H diff --git a/stdlib/public/SwiftShims/CFCharacterSetShims.h b/stdlib/public/SwiftShims/CFCharacterSetShims.h deleted file mode 100644 index ec6d6a8792c36..0000000000000 --- a/stdlib/public/SwiftShims/CFCharacterSetShims.h +++ /dev/null @@ -1,26 +0,0 @@ -//===--- CFCharacterSetShims.h - Declarations for CF hashing functions ----===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -CF_IMPLICIT_BRIDGING_ENABLED -CF_EXTERN_C_BEGIN -_Pragma("clang assume_nonnull begin") - -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLUserAllowedCharacterSet() API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0)); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPasswordAllowedCharacterSet() API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0)); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLHostAllowedCharacterSet() API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0)); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPathAllowedCharacterSet() API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0)); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLQueryAllowedCharacterSet() API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0)); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLFragmentAllowedCharacterSet() API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0)); - -_Pragma("clang assume_nonnull end") -CF_EXTERN_C_END -CF_IMPLICIT_BRIDGING_DISABLED diff --git a/stdlib/public/SwiftShims/CFHashingShims.h b/stdlib/public/SwiftShims/CFHashingShims.h deleted file mode 100644 index c1761f1b72dbf..0000000000000 --- a/stdlib/public/SwiftShims/CFHashingShims.h +++ /dev/null @@ -1,41 +0,0 @@ -//===--- CFHashingShims.h - Declarations for CF hashing functions ---------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -CF_IMPLICIT_BRIDGING_ENABLED -CF_EXTERN_C_BEGIN -_Pragma("clang assume_nonnull begin") - - -#define _CF_HASHFACTOR 2654435761U - -CF_INLINE CFHashCode __CFHashInt(long i) { - return ((i > 0) ? (CFHashCode)(i) : (CFHashCode)(-i)) * _CF_HASHFACTOR; -} - -CF_INLINE CFHashCode __CFHashDouble(double d) { - double dInt; - if (d < 0) d = -d; - dInt = floor(d+0.5); - CFHashCode integralHash = _CF_HASHFACTOR * (CFHashCode)fmod(dInt, (double)ULONG_MAX); - return (CFHashCode)(integralHash + (CFHashCode)((d - dInt) * ULONG_MAX)); -} - -CF_EXPORT CFHashCode CFHashBytes(uint8_t *_Nullable bytes, CFIndex len); - - -CF_INLINE CFHashCode __CFHashBytes(uint8_t *_Nullable bytes, CFIndex len) { - return CFHashBytes(bytes, len); -} - -_Pragma("clang assume_nonnull end") -CF_EXTERN_C_END -CF_IMPLICIT_BRIDGING_DISABLED diff --git a/stdlib/public/SwiftShims/CMakeLists.txt b/stdlib/public/SwiftShims/CMakeLists.txt index 5ee8a9c920697..0759563a1fe1e 100644 --- a/stdlib/public/SwiftShims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/CMakeLists.txt @@ -22,39 +22,10 @@ set(sources UnicodeShims.h Visibility.h _SwiftConcurrency.h + _SwiftDistributed.h - CoreMediaOverlayShims.h DispatchOverlayShims.h - NetworkOverlayShims.h - OSOverlayShims.h - ObjectiveCOverlayShims.h - SafariServicesOverlayShims.h - AppKitOverlayShims.h - UIKitOverlayShims.h XCTestOverlayShims.h - XPCOverlayShims.h - - CoreFoundationOverlayShims.h - CFCharacterSetShims.h - CFHashingShims.h - - FoundationOverlayShims.h - FoundationShimSupport.h - NSCalendarShims.h - NSCharacterSetShims.h - NSCoderShims.h - NSDataShims.h - NSDictionaryShims.h - NSErrorShims.h - NSFileManagerShims.h - NSIndexPathShims.h - NSIndexSetShims.h - NSKeyedArchiverShims.h - NSLocaleShims.h - NSTimeZoneShims.h - NSUndoManagerShims.h - - ClockKitOverlayShims.h module.modulemap ) diff --git a/stdlib/public/SwiftShims/ClockKitOverlayShims.h b/stdlib/public/SwiftShims/ClockKitOverlayShims.h deleted file mode 100644 index f6f7994b7d224..0000000000000 --- a/stdlib/public/SwiftShims/ClockKitOverlayShims.h +++ /dev/null @@ -1,23 +0,0 @@ -//===--- ClockKitOverlayShims.h --------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 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 -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_STDLIB_SHIMS_CLOCKKITOVERLAY_H -#define SWIFT_STDLIB_SHIMS_CLOCKKITOVERLAY_H - -@import ClockKit; - -@interface CLKTextProvider (OverlaySupport) -+ (CLKTextProvider *)textProviderWithFormat:(NSString *)format arguments:(va_list)arguments API_AVAILABLE(watchos(2.0)) API_UNAVAILABLE(macos, tvos, ios); -@end - -// SWIFT_STDLIB_SHIMS_CLOCKKITOVERLAY_H -#endif diff --git a/stdlib/public/SwiftShims/CoreFoundationOverlayShims.h b/stdlib/public/SwiftShims/CoreFoundationOverlayShims.h deleted file mode 100644 index cddc127cd7ff9..0000000000000 --- a/stdlib/public/SwiftShims/CoreFoundationOverlayShims.h +++ /dev/null @@ -1,21 +0,0 @@ -//===------------------------------------------------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import - -/// - Note: This file is intended to be used solely for the Foundation and CoreFoundation overlays in swift -/// any and all other uses are not supported. The Foundation team reserves the right to alter the contract, -/// calling convention, availability or any other facet of facilities offered in this file. If you use -/// anything from here and your app breaks, expect any bug to be marked as "behaves correctly". - -#import "CFCharacterSetShims.h" -#import "CFHashingShims.h" diff --git a/stdlib/public/SwiftShims/CoreMediaOverlayShims.h b/stdlib/public/SwiftShims/CoreMediaOverlayShims.h deleted file mode 100644 index 0d6ed64a88c2c..0000000000000 --- a/stdlib/public/SwiftShims/CoreMediaOverlayShims.h +++ /dev/null @@ -1,75 +0,0 @@ -//===--- CoreMediaOverlayShims.h ---===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2019 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 contains replacements for the initializers of some CoreMedia -// objects that return the created instance as an out parameter and thus -// cannot be used in Swift as-is. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_STDLIB_SHIMS_COREMEDIA_OVERLAY_H -#define SWIFT_STDLIB_SHIMS_COREMEDIA_OVERLAY_H - -@import CoreMedia; -@import CoreFoundation; - -#if __has_feature(nullability) -#pragma clang assume_nonnull begin -#endif - -// slurped from CoreAudio headers that are sometime private. -typedef UInt32 AudioObjectID; -typedef AudioObjectID AudioDeviceID; - -// slurped from CoreMedia headers; see https://bugs.swift.org/browse/SR-2999 -typedef struct CM_BRIDGED_TYPE(id) OpaqueCMBlockBuffer *CMBlockBufferRef; -typedef struct CM_BRIDGED_TYPE(id) opaqueCMBufferQueue *CMBufferQueueRef; -typedef struct CM_BRIDGED_TYPE(id) OpaqueCMClock* CMClockRef; -typedef const struct CM_BRIDGED_TYPE(id) opaqueCMFormatDescription *CMFormatDescriptionRef; -typedef struct CM_BRIDGED_TYPE(id) opaqueCMSampleBuffer *CMSampleBufferRef; -typedef struct CM_BRIDGED_TYPE(id) opaqueCMSimpleQueue *CMSimpleQueueRef; -typedef struct CM_BRIDGED_TYPE(id) OpaqueCMTimebase* CMTimebaseRef; - -#define INIT_REFERENCING(Type, availability) \ -CM_INLINE CM_RETURNS_RETAINED_PARAMETER Type##Ref CM_NONNULL \ - Type##Retain( \ - Type##Ref CM_NONNULL object) \ - CF_SWIFT_NAME(Type.init(referencing:)) \ - CF_REFINED_FOR_SWIFT \ - availability ;\ -CM_INLINE CM_RETURNS_RETAINED_PARAMETER Type##Ref CM_NONNULL \ - Type##Retain( \ - Type##Ref CM_NONNULL object) \ -{ \ - return (Type##Ref)CFRetain(object);\ -} - -INIT_REFERENCING(CMClock, API_AVAILABLE(macosx(10.8)) API_UNAVAILABLE(ios, tvos, watchos)) - -INIT_REFERENCING(CMBlockBuffer, API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0), watchos(6.0))) - -INIT_REFERENCING(CMBufferQueue, API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0), watchos(6.0))) - -INIT_REFERENCING(CMFormatDescription, API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0), watchos(6.0))) - -INIT_REFERENCING(CMSampleBuffer, API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0), watchos(6.0))) - -INIT_REFERENCING(CMSimpleQueue, API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0), watchos(6.0))) - -INIT_REFERENCING(CMTimebase, API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0), watchos(6.0))) - -#if __has_feature(nullability) -#pragma clang assume_nonnull end -#endif - - -#endif // SWIFT_STDLIB_SHIMS_COREMEDIA_OVERLAY_H diff --git a/stdlib/public/SwiftShims/FoundationOverlayShims.h b/stdlib/public/SwiftShims/FoundationOverlayShims.h deleted file mode 100644 index 8c1692c8ffb8b..0000000000000 --- a/stdlib/public/SwiftShims/FoundationOverlayShims.h +++ /dev/null @@ -1,82 +0,0 @@ -//===------------------------------------------------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import -#import -#import -#import -#import -#import -#import -#import - -#import "FoundationShimSupport.h" -#import "NSCalendarShims.h" -#import "NSCharacterSetShims.h" -#import "NSCoderShims.h" -#import "NSDataShims.h" -#import "NSDictionaryShims.h" -#import "NSErrorShims.h" -#import "NSFileManagerShims.h" -#import "NSIndexPathShims.h" -#import "NSIndexSetShims.h" -#import "NSKeyedArchiverShims.h" -#import "NSLocaleShims.h" -#import "NSTimeZoneShims.h" -#import "NSUndoManagerShims.h" - -typedef struct { - void *_Nonnull memory; - size_t capacity; - _Bool onStack; -} _ConditionalAllocationBuffer; - -static inline _Bool _resizeConditionalAllocationBuffer(_ConditionalAllocationBuffer *_Nonnull buffer, size_t amt) { - size_t amount = malloc_good_size(amt); - if (amount <= buffer->capacity) { return true; } - void *newMemory; - if (buffer->onStack) { - newMemory = malloc(amount); - if (newMemory == NULL) { return false; } - memcpy(newMemory, buffer->memory, buffer->capacity); - buffer->onStack = false; - } else { - newMemory = realloc(buffer->memory, amount); - if (newMemory == NULL) { return false; } - } - if (newMemory == NULL) { return false; } - buffer->memory = newMemory; - buffer->capacity = amount; - return true; -} - -static inline _Bool _withStackOrHeapBuffer(size_t amount, void (__attribute__((noescape)) ^ _Nonnull applier)(_ConditionalAllocationBuffer *_Nonnull)) { - _ConditionalAllocationBuffer buffer; - buffer.capacity = malloc_good_size(amount); - buffer.onStack = (pthread_main_np() != 0 ? buffer.capacity < 2048 : buffer.capacity < 512); - buffer.memory = buffer.onStack ? alloca(buffer.capacity) : malloc(buffer.capacity); - if (buffer.memory == NULL) { return false; } - applier(&buffer); - if (!buffer.onStack) { - free(buffer.memory); - } - return true; -} - -@protocol _NSKVOCompatibilityShim -+ (void)_noteProcessHasUsedKVOSwiftOverlay; -@end - - -// Exported by libswiftCore: -extern bool _swift_isObjCTypeNameSerializable(Class theClass); -extern void _swift_reportToDebugger(uintptr_t flags, const char *message, void *details); diff --git a/stdlib/public/SwiftShims/FoundationShimSupport.h b/stdlib/public/SwiftShims/FoundationShimSupport.h deleted file mode 100644 index 108793e867686..0000000000000 --- a/stdlib/public/SwiftShims/FoundationShimSupport.h +++ /dev/null @@ -1,33 +0,0 @@ -//===--- FoundationShimSupport.h - Helper macros for Foundation overlay ---===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import - -#ifndef NS_NON_BRIDGED -#define NS_NON_BRIDGED(type) NSObject * -#endif - -#ifndef NS_BEGIN_DECLS - -#define NS_BEGIN_DECLS \ - __BEGIN_DECLS \ - NS_ASSUME_NONNULL_BEGIN - -#endif - -#ifndef NS_END_DECLS - -#define NS_END_DECLS \ - NS_ASSUME_NONNULL_END \ - __END_DECLS - -#endif diff --git a/stdlib/public/SwiftShims/NSCalendarShims.h b/stdlib/public/SwiftShims/NSCalendarShims.h deleted file mode 100644 index 802fb3b58dda2..0000000000000 --- a/stdlib/public/SwiftShims/NSCalendarShims.h +++ /dev/null @@ -1,40 +0,0 @@ -//===--- NSCalendarShims.h - Foundation declarations for Calendar overlay -===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE BOOL __NSCalendarIsAutoupdating(NS_NON_BRIDGED(NSCalendar *) calendar) { - static dispatch_once_t onceToken; - static Class autoCalendarClass; - static Class olderAutoCalendarClass; // Pre 10.12/10.0 - dispatch_once(&onceToken, ^{ - autoCalendarClass = (Class)objc_lookUpClass("_NSAutoCalendar"); - olderAutoCalendarClass = (Class)objc_lookUpClass("NSAutoCalendar"); - }); - return (autoCalendarClass && [calendar isKindOfClass:autoCalendarClass]) || (olderAutoCalendarClass && [calendar isKindOfClass:olderAutoCalendarClass]); -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCalendar *) __NSCalendarCreate(NSCalendarIdentifier identifier) { - return [[NSCalendar alloc] initWithCalendarIdentifier:identifier]; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCalendar *) __NSCalendarAutoupdating() { - return [NSCalendar autoupdatingCurrentCalendar]; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCalendar *) __NSCalendarCurrent() { - return [NSCalendar currentCalendar]; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSCharacterSetShims.h b/stdlib/public/SwiftShims/NSCharacterSetShims.h deleted file mode 100644 index f38fbccf2a3ed..0000000000000 --- a/stdlib/public/SwiftShims/NSCharacterSetShims.h +++ /dev/null @@ -1,41 +0,0 @@ -//===--- NSCharacterSetShims.h - Foundation decl. for CharacterSet overlay ===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCharacterSet *) _NSURLComponentsGetURLUserAllowedCharacterSet(void) { - return NSCharacterSet.URLUserAllowedCharacterSet; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCharacterSet *) _NSURLComponentsGetURLPasswordAllowedCharacterSet(void) { - return NSCharacterSet.URLPasswordAllowedCharacterSet; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCharacterSet *) _NSURLComponentsGetURLHostAllowedCharacterSet(void) { - return NSCharacterSet.URLHostAllowedCharacterSet; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCharacterSet *) _NSURLComponentsGetURLPathAllowedCharacterSet(void) { - return NSCharacterSet.URLPathAllowedCharacterSet; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCharacterSet *) _NSURLComponentsGetURLQueryAllowedCharacterSet(void) { - return NSCharacterSet.URLQueryAllowedCharacterSet; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSCharacterSet *) _NSURLComponentsGetURLFragmentAllowedCharacterSet(void) { - return NSCharacterSet.URLFragmentAllowedCharacterSet; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSCoderShims.h b/stdlib/public/SwiftShims/NSCoderShims.h deleted file mode 100644 index 6cb7c91935662..0000000000000 --- a/stdlib/public/SwiftShims/NSCoderShims.h +++ /dev/null @@ -1,49 +0,0 @@ -//===--- NSCoderShims.h - Foundation declarations for NSCoder overlay -----===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE NS_RETURNS_RETAINED _Nullable id __NSCoderDecodeObject(NSCoder *self_, NSError **_Nullable error) { - if (error) { - return [self_ decodeTopLevelObjectAndReturnError:error]; - } else { - return [self_ decodeObject]; - } -} - -NS_INLINE NS_RETURNS_RETAINED _Nullable id __NSCoderDecodeObjectForKey(NSCoder *self_, NSString *key, NSError **_Nullable error) { - if (error) { - return [self_ decodeTopLevelObjectForKey:key error:error]; - } else { - return [self_ decodeObjectForKey:key]; - } -} - -NS_INLINE NS_RETURNS_RETAINED _Nullable id __NSCoderDecodeObjectOfClassForKey(NSCoder *self_, Class cls, NSString *key, NSError **_Nullable error) { - if (error) { - return [self_ decodeTopLevelObjectOfClass:cls forKey:key error:error]; - } else { - return [self_ decodeObjectOfClass:cls forKey:key]; - } -} - -NS_INLINE NS_RETURNS_RETAINED _Nullable id __NSCoderDecodeObjectOfClassesForKey(NSCoder *self_, NS_NON_BRIDGED(NSSet *)_Nullable classes, NSString *key, NSError **_Nullable error) { - if (error) { - return [self_ decodeTopLevelObjectOfClasses:(NSSet *)classes forKey:key error:error]; - } else { - return [self_ decodeObjectOfClasses:(NSSet *)classes forKey:key]; - } -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSDataShims.h b/stdlib/public/SwiftShims/NSDataShims.h deleted file mode 100644 index 770c7d8cc9ef5..0000000000000 --- a/stdlib/public/SwiftShims/NSDataShims.h +++ /dev/null @@ -1,29 +0,0 @@ -//===--- NSDataShims.h - Foundation declarations for Data overlay ---------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -typedef void (^NSDataDeallocator)(void * _Null_unspecified, NSUInteger); -FOUNDATION_EXPORT const NSDataDeallocator NSDataDeallocatorVM; -FOUNDATION_EXPORT const NSDataDeallocator NSDataDeallocatorUnmap; -FOUNDATION_EXPORT const NSDataDeallocator NSDataDeallocatorFree; -FOUNDATION_EXPORT const NSDataDeallocator NSDataDeallocatorNone; - -@interface NSData (FoundationSPI) -- (BOOL)_isCompact API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); -@end - -BOOL __NSDataWriteToURL(NS_NON_BRIDGED(NSData *) _Nonnull data NS_RELEASES_ARGUMENT, NSURL * _Nonnull url NS_RELEASES_ARGUMENT, NSDataWritingOptions writingOptions, NSError **errorPtr); - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSDictionaryShims.h b/stdlib/public/SwiftShims/NSDictionaryShims.h deleted file mode 100644 index 31055e9c76690..0000000000000 --- a/stdlib/public/SwiftShims/NSDictionaryShims.h +++ /dev/null @@ -1,21 +0,0 @@ -//===--- NSDictionaryShims.h - Foundation decl. for Dictionary overlay ----===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE void __NSDictionaryGetObjects(NS_NON_BRIDGED(NSDictionary *)nsDictionary, void *_Nullable objects, void *_Nullable keys, NSUInteger count) { - [(NSDictionary *)nsDictionary getObjects:(__unsafe_unretained id _Nonnull *)(void *)objects andKeys:(__unsafe_unretained id _Nonnull *)(void *)keys count:count]; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSErrorShims.h b/stdlib/public/SwiftShims/NSErrorShims.h deleted file mode 100644 index 4a9547b019916..0000000000000 --- a/stdlib/public/SwiftShims/NSErrorShims.h +++ /dev/null @@ -1,23 +0,0 @@ -//===--- NSErrorShims.h - Foundation declarations for NSError overlay -----===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE void __NSErrorPerformRecoverySelector(_Nullable id delegate, SEL selector, BOOL success, void *_Nullable contextInfo) { - void (*msg)(_Nullable id, SEL, BOOL, void* _Nullable) = - (void(*)(_Nullable id, SEL, BOOL, void* _Nullable))objc_msgSend; - msg(delegate, selector, success, contextInfo); -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSFileManagerShims.h b/stdlib/public/SwiftShims/NSFileManagerShims.h deleted file mode 100644 index 1828e39ac8678..0000000000000 --- a/stdlib/public/SwiftShims/NSFileManagerShims.h +++ /dev/null @@ -1,34 +0,0 @@ -//===--- NSFileManagerShims.h - Foundation decl. for FileManager overlay --===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE NS_RETURNS_RETAINED NSURL *_Nullable __NSFileManagerReplaceItemAtURL(NSFileManager *self_, NSURL *originalItemURL, NSURL *newItemURL, NSString *_Nullable backupItemName, NSFileManagerItemReplacementOptions options, NSError **_Nullable error) { - NSURL *result = nil; - BOOL success = [self_ replaceItemAtURL:originalItemURL withItemAtURL:newItemURL backupItemName:backupItemName options:options resultingItemURL:&result error:error]; - return success ? result : nil; -} - -NS_INLINE NS_RETURNS_RETAINED NSDirectoryEnumerator *_Nullable __NSFileManagerEnumeratorAtURL(NSFileManager *self_, NSURL *url, NSArray *_Nullable keys, NSDirectoryEnumerationOptions options, BOOL (^errorHandler)(NSURL *url, NSError *error) ) { - return [self_ enumeratorAtURL:url includingPropertiesForKeys:keys options:options errorHandler:^(NSURL *url, NSError *error) { - NSURL *realURL = url ?: error.userInfo[NSURLErrorKey]; - if (!realURL) { - NSString *path = error.userInfo[NSFilePathErrorKey]; - realURL = [NSURL fileURLWithPath:path]; - } - return errorHandler(realURL, error); - }]; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSIndexPathShims.h b/stdlib/public/SwiftShims/NSIndexPathShims.h deleted file mode 100644 index 4e60d806beb06..0000000000000 --- a/stdlib/public/SwiftShims/NSIndexPathShims.h +++ /dev/null @@ -1,22 +0,0 @@ -//===--- NSIndexPathShims.h - Found. decl. for IndexPath overl. -*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE NS_NON_BRIDGED(NSIndexPath *)_NSIndexPathCreateFromIndexes(NSUInteger idx1, NSUInteger idx2) NS_RETURNS_RETAINED { - NSUInteger indexes[] = {idx1, idx2}; - return [[NSIndexPath alloc] initWithIndexes:&indexes[0] length:2]; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSIndexSetShims.h b/stdlib/public/SwiftShims/NSIndexSetShims.h deleted file mode 100644 index 09abe89c14fb9..0000000000000 --- a/stdlib/public/SwiftShims/NSIndexSetShims.h +++ /dev/null @@ -1,37 +0,0 @@ -//===--- NSIndexSetShims.h - Foundation declarations for IndexSet overlay -===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -@interface NSIndexSet (NSRanges) -- (NSUInteger)rangeCount; -- (NSRange)rangeAtIndex:(NSUInteger)rangeIndex; -- (NSUInteger)_indexOfRangeContainingIndex:(NSUInteger)value; -@end - -NS_INLINE NSUInteger __NSIndexSetRangeCount(NS_NON_BRIDGED(NSIndexSet *)self_) { - return [(NSIndexSet *)self_ rangeCount]; -} - -NS_INLINE void __NSIndexSetRangeAtIndex(NS_NON_BRIDGED(NSIndexSet *)self_, NSUInteger rangeIndex, NSUInteger *location, NSUInteger *length) { - NSRange result = [(NSIndexSet *)self_ rangeAtIndex:rangeIndex]; - *location = result.location; - *length = result.length; -} - -NS_INLINE NSUInteger __NSIndexSetIndexOfRangeContainingIndex(NS_NON_BRIDGED(NSIndexSet *)self_, NSUInteger index) { - return [(NSIndexSet *)self_ _indexOfRangeContainingIndex:index]; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSKeyedArchiverShims.h b/stdlib/public/SwiftShims/NSKeyedArchiverShims.h deleted file mode 100644 index 6205c72d22815..0000000000000 --- a/stdlib/public/SwiftShims/NSKeyedArchiverShims.h +++ /dev/null @@ -1,33 +0,0 @@ -//===--- NSKeyedArchiverShims.h - Found. decl. for NSKeyedArchiver overlay ===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE NS_RETURNS_RETAINED _Nullable id __NSKeyedUnarchiverUnarchiveObject(id Self_, NS_NON_BRIDGED(NSData *)data, NSError **_Nullable error) { - if (error) { - return [Self_ unarchiveTopLevelObjectWithData:(NSData *)data error:error]; - } else { - return [Self_ unarchiveObjectWithData:(NSData *)data]; - } -} - -NS_INLINE NS_RETURNS_RETAINED id _Nullable __NSKeyedUnarchiverSecureUnarchiveObjectOfClass(Class cls, NSData *data, NSError * _Nullable * _Nullable error) { - return [NSKeyedUnarchiver unarchivedObjectOfClass:cls fromData:data error:error]; -} - -NS_INLINE NS_RETURNS_RETAINED id _Nullable __NSKeyedUnarchiverSecureUnarchiveObjectOfClasses(NS_NON_BRIDGED(NSSet *) classes, NSData *data, NSError * _Nullable * _Nullable error) { - return [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:data error:error]; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSLocaleShims.h b/stdlib/public/SwiftShims/NSLocaleShims.h deleted file mode 100644 index a0f5e5b7df7d8..0000000000000 --- a/stdlib/public/SwiftShims/NSLocaleShims.h +++ /dev/null @@ -1,34 +0,0 @@ -//===--- NSLocaleShims.h - Foundation declarations for Locale overlay -----===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE BOOL __NSLocaleIsAutoupdating(NS_NON_BRIDGED(NSLocale *)locale) { - static dispatch_once_t onceToken; - static Class autoLocaleClass; - dispatch_once(&onceToken, ^{ - autoLocaleClass = (Class)objc_lookUpClass("NSAutoLocale"); - }); - return [locale isKindOfClass:autoLocaleClass]; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSLocale *)__NSLocaleCurrent() { - return [NSLocale currentLocale]; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSLocale *)__NSLocaleAutoupdating() { - return [NSLocale autoupdatingCurrentLocale]; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NSTimeZoneShims.h b/stdlib/public/SwiftShims/NSTimeZoneShims.h deleted file mode 100644 index e68d92d605cd7..0000000000000 --- a/stdlib/public/SwiftShims/NSTimeZoneShims.h +++ /dev/null @@ -1,34 +0,0 @@ -//===--- NSTimeZoneShims.h - Foundation declarations for TimeZone overlay -===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#import "FoundationShimSupport.h" - -NS_BEGIN_DECLS - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSTimeZone *)__NSTimeZoneAutoupdating() { - return [NSTimeZone localTimeZone]; -} - -NS_INLINE NS_RETURNS_RETAINED NS_NON_BRIDGED(NSTimeZone *)__NSTimeZoneCurrent() { - return [NSTimeZone systemTimeZone]; -} - -NS_INLINE BOOL __NSTimeZoneIsAutoupdating(NS_NON_BRIDGED(NSTimeZone *)timeZone) { - static dispatch_once_t onceToken; - static Class autoTimeZoneClass; - dispatch_once(&onceToken, ^{ - autoTimeZoneClass = (Class)objc_lookUpClass("__NSLocalTimeZone"); - }); - return [timeZone isKindOfClass:autoTimeZoneClass]; -} - -NS_END_DECLS diff --git a/stdlib/public/SwiftShims/NetworkOverlayShims.h b/stdlib/public/SwiftShims/NetworkOverlayShims.h deleted file mode 100644 index 4d0234b4cac61..0000000000000 --- a/stdlib/public/SwiftShims/NetworkOverlayShims.h +++ /dev/null @@ -1,101 +0,0 @@ -//===--- NetworkOverlayShims.h ---------------------------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2018 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 -// -//===----------------------------------------------------------------------===// - - -#ifndef SWIFT_STDLIB_SHIMS_NETWORKSHIMS_H -#define SWIFT_STDLIB_SHIMS_NETWORKSHIMS_H - -@import Network; - -#ifdef __OBJC__ -#define SWIFT_NW_RETURNS_RETAINED __attribute__((__ns_returns_retained__)) -#else -#define SWIFT_NW_RETURNS_RETAINED -#endif - -#pragma clang assume_nonnull begin - -static inline uint32_t -_swift_nw_data_transfer_report_all_paths(void) { - return (uint32_t)(-1); -} - -typedef void (^__swift_nw_connection_send_completion_t)(_Nullable nw_error_t error); - -static inline SWIFT_NW_RETURNS_RETAINED nw_content_context_t -_swift_nw_content_context_default_message(void) { - return _nw_content_context_default_message; -} - -static inline SWIFT_NW_RETURNS_RETAINED nw_content_context_t -_swift_nw_content_context_final_message(void) { - return _nw_content_context_final_send; -} - -static inline SWIFT_NW_RETURNS_RETAINED nw_content_context_t -_swift_nw_content_context_default_stream(void) { - return _nw_content_context_default_stream; -} - -static inline void -_swift_nw_connection_send_idempotent(nw_connection_t connection, _Nullable dispatch_data_t content, _Nullable nw_content_context_t context, bool is_complete) { - nw_connection_send(connection, content, context, is_complete, _nw_connection_send_idempotent_content); -} - -static inline void -_swift_nw_connection_send(nw_connection_t connection, _Nullable dispatch_data_t content, nw_content_context_t context, bool is_complete, __swift_nw_connection_send_completion_t completion) { - nw_connection_send(connection, content, context, is_complete, completion); -} - -API_AVAILABLE(macos(10.15)) API_UNAVAILABLE(ios, watchos, tvos) -static inline SWIFT_NW_RETURNS_RETAINED nw_parameters_t -_swift_nw_parameters_create_custom_ip(uint8_t custom_ip_protocol_number) { - nw_parameters_create_custom_ip(custom_ip_protocol_number, _nw_parameters_configure_protocol_default_configuration); -} - -API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)) -_Nullable SWIFT_NW_RETURNS_RETAINED nw_endpoint_t -nw_endpoint_create_unix(const char *path); - -API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)) -_Nullable SWIFT_NW_RETURNS_RETAINED nw_interface_t -nw_endpoint_copy_interface(nw_endpoint_t endpoint); - -API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)) -void -nw_endpoint_set_interface(nw_endpoint_t endpoint, - _Nullable nw_interface_t interface); - -API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)) -_Nullable SWIFT_NW_RETURNS_RETAINED nw_interface_t -nw_interface_create_with_name(const char *interface_name); - -API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)) -_Nullable SWIFT_NW_RETURNS_RETAINED nw_interface_t -nw_interface_create_with_index(uint32_t interface_index); - -API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)) -SWIFT_NW_RETURNS_RETAINED NSData * _Nullable -NWCreateNSDataFromDispatchData(_Nullable dispatch_data_t data); - -API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)) -_Nullable SWIFT_NW_RETURNS_RETAINED dispatch_data_t -NWCreateDispatchDataFromNSData(NSData * _Nullable data); - -API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)) -const char * -nwlog_get_string_for_dns_service_error(int32_t err); - -#pragma clang assume_nonnull end - -#endif // SWIFT_STDLIB_SHIMS_NETWORKSHIMS_H - diff --git a/stdlib/public/SwiftShims/OSOverlayShims.h b/stdlib/public/SwiftShims/OSOverlayShims.h deleted file mode 100644 index 11ec4abd714b1..0000000000000 --- a/stdlib/public/SwiftShims/OSOverlayShims.h +++ /dev/null @@ -1,91 +0,0 @@ -//===--- OSOverlayShims.h ---------------------------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_STDLIB_SHIMS_OS_OVERLAY_H -#define SWIFT_STDLIB_SHIMS_OS_OVERLAY_H - -#include -#include -#include -#include "Visibility.h" - -static inline os_log_t _Nonnull -_swift_os_log_default(void) { - return OS_LOG_DEFAULT; -} - -static inline os_log_t _Nonnull -_swift_os_log_disabled(void) { - return OS_LOG_DISABLED; -} - -static inline const unsigned char * _Nonnull -_swift_os_signpost_points_of_interest(void) { - /* OS_LOG_CATEGORY_POINTS_OF_INTEREST */ - return "PointsOfInterest"; -} - -static inline os_signpost_id_t -_swift_os_signpost_id_exclusive(void) { - /* OS_SIGNPOST_ID_EXCLUSIVE */ - return (os_signpost_id_t)0xEEEEB0B5B2B2EEEE; -} - -static inline os_signpost_id_t -_swift_os_signpost_id_invalid(void) { - /* OS_SIGNPOST_ID_INVALID */ - return (os_signpost_id_t)~0; -} - -static inline os_signpost_id_t -_swift_os_signpost_id_null(void) { - /* OS_SIGNPOST_ID_NULL */ - return (os_signpost_id_t)0; -} - -SWIFT_RUNTIME_STDLIB_INTERNAL -extern const void * _Nullable -_swift_os_log_return_address(void); - -SWIFT_RUNTIME_STDLIB_INTERNAL -extern void -_swift_os_log( - const void * _Nullable dso, - const void * _Nullable ra, - os_log_t _Nonnull h, - os_log_type_t type, - const char * _Nonnull fmt, - va_list args); - -SWIFT_RUNTIME_STDLIB_INTERNAL -extern void -_swift_os_signpost_with_format( - const void * _Nullable dso, - const void * _Nullable ra, - os_log_t _Nonnull h, - os_signpost_type_t spty, - const char * _Nonnull spnm, - os_signpost_id_t spid, - const char * _Nullable fmt, - va_list args); - -SWIFT_RUNTIME_STDLIB_INTERNAL -extern void -_swift_os_signpost( - const void * _Nullable dso, - const void * _Nullable ra, - os_log_t _Nonnull h, - os_signpost_type_t spty, - const char * _Nonnull spnm, - os_signpost_id_t spid); - -#endif // SWIFT_STDLIB_SHIMS_OS_OVERLAY_H diff --git a/stdlib/public/SwiftShims/ObjectiveCOverlayShims.h b/stdlib/public/SwiftShims/ObjectiveCOverlayShims.h deleted file mode 100644 index f63025d90773f..0000000000000 --- a/stdlib/public/SwiftShims/ObjectiveCOverlayShims.h +++ /dev/null @@ -1,26 +0,0 @@ -//===--- ObjectiveCOverlayShims.h -------------------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_STDLIB_SHIMS_OBJECTIVEC_OVERLAY_H -#define SWIFT_STDLIB_SHIMS_OBJECTIVEC_OVERLAY_H - -static inline void *_swift_objc_autoreleasePoolPush() { - extern void *objc_autoreleasePoolPush(); - return objc_autoreleasePoolPush(); -} -static inline void _swift_objc_autoreleasePoolPop(void *pool) { - extern void objc_autoreleasePoolPop(void *); - objc_autoreleasePoolPop(pool); -} - -#endif // SWIFT_STDLIB_SHIMS_OBJECTIVEC_OVERLAY_H - diff --git a/stdlib/public/SwiftShims/SafariServicesOverlayShims.h b/stdlib/public/SwiftShims/SafariServicesOverlayShims.h deleted file mode 100644 index 1925947840a9f..0000000000000 --- a/stdlib/public/SwiftShims/SafariServicesOverlayShims.h +++ /dev/null @@ -1,32 +0,0 @@ -//===--- SafariServicesOverlayShims.h ---------------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_STDLIB_SHIMS_SAFARISERVICES_OVERLAY_H -#define SWIFT_STDLIB_SHIMS_SAFARISERVICES_OVERLAY_H - -#import - -static inline bool _swift_SafariServices_isSafariServicesAvailable(SFSafariServicesVersion version) { - if (version == SFSafariServicesVersion10_0) { - return NULL != &_SFSafariServicesAvailable; - } - - SFSafariServicesVersion* latestVersion = &_SFSafariServicesVersion; - if (NULL == latestVersion) { - return false; - } - - return *latestVersion >= version; -} - -#endif // SWIFT_STDLIB_SHIMS_SAFARISERVICES_OVERLAY_H - diff --git a/stdlib/public/SwiftShims/System.h b/stdlib/public/SwiftShims/System.h index 9656bf69fe1d3..8fe54f6bda56f 100644 --- a/stdlib/public/SwiftShims/System.h +++ b/stdlib/public/SwiftShims/System.h @@ -76,8 +76,8 @@ // This is not ABI per-se but does stay in-sync with LLDB. If it becomes // out-of-sync, then users won't see a friendly diagnostic when inspecting // references past their lifetime. -#define SWIFT_ABI_DEFAULT_REFERENCE_POISON_DEBUG_VALUE_32 0x00000880U -#define SWIFT_ABI_DEFAULT_REFERENCE_POISON_DEBUG_VALUE_64 0x0000000000000880ULL +#define SWIFT_ABI_DEFAULT_REFERENCE_POISON_DEBUG_VALUE_32 0x00000440U +#define SWIFT_ABI_DEFAULT_REFERENCE_POISON_DEBUG_VALUE_64 0x0000000000000440ULL /*********************************** i386 *************************************/ diff --git a/stdlib/public/SwiftShims/UIKitOverlayShims.h b/stdlib/public/SwiftShims/UIKitOverlayShims.h deleted file mode 100644 index bed05075f374c..0000000000000 --- a/stdlib/public/SwiftShims/UIKitOverlayShims.h +++ /dev/null @@ -1,195 +0,0 @@ -//===--- UIKitOverlayShims.h ---===// -// -// 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 -// -//===--------------------===// - -#ifndef SWIFT_STDLIB_SHIMS_UIKIT_OVERLAY_H -#define SWIFT_STDLIB_SHIMS_UIKIT_OVERLAY_H - -@import UIKit; - -#if __has_feature(nullability) -#pragma clang assume_nonnull begin -#endif - -#if TARGET_OS_TV || TARGET_OS_IOS -static inline BOOL _swift_UIKit_UIFocusEnvironmentContainsEnvironment( - id environment, - id otherEnvironment - ) API_AVAILABLE(ios(11.0), tvos(11.0)) { - return [UIFocusSystem environment:environment containsEnvironment:otherEnvironment]; -} -#endif // TARGET_OS_TV || TARGET_OS_IOS - -#if __has_feature(nullability) -#pragma clang assume_nonnull end -#endif - - -//===--------------------===// -// diffable data source // -//===--------------------===// - -#if TARGET_OS_TV || TARGET_OS_IOS - -#if __has_feature(nullability) -#pragma clang assume_nonnull begin -#endif - -typedef UITableViewCell * _Nullable (^UITableViewDiffableDataSourceCellProvider)(UITableView * _Nonnull, NSIndexPath * _Nonnull, id _Nonnull); -typedef UICollectionViewCell * _Nullable (^UICollectionViewDiffableDataSourceCellProvider)(UICollectionView * _Nonnull,NSIndexPath * _Nonnull, id _Nonnull); - -typedef UICollectionViewCell * _Nullable(^UIDiffableDataSourceCollectionViewCellProvider)(UICollectionView*, NSIndexPath *indexPath, id identifier); -typedef UICollectionReusableView * _Nullable(^UIDiffableDataSourceSupplementaryViewProvider)(UICollectionView *, NSString *kind, NSIndexPath *indexPath); - -typedef NSString * _Nonnull(^UIDiffableDataSourceCellReuseIdentifierProvider)(id _Nonnull identifier); -typedef void(^UIDiffableDataSourceCollectionViewCellConfigurationHandler)(__kindof UICollectionViewCell * _Nonnull , id _Nonnull identifier); - -typedef NSString * _Nonnull(^UIDiffableDataSourceSupplementaryViewReuseIdentifierProvider)(NSString * _Nonnull kind, NSIndexPath * _Nonnull indexPath); -typedef void(^UIDiffableDataSourceSupplementaryViewConfigurationHandler)(__kindof UICollectionReusableView * _Nonnull, NSString * _Nonnull kind, NSIndexPath * _Nonnull indexPath); - -typedef UITableViewCell * _Nullable(^UIDiffableDataSourceTableViewCellProvider)(__kindof UITableView * _Nonnull, NSIndexPath * _Nonnull, id _Nonnull identifier); -typedef void(^UIDiffableDataSourceTableViewCellConfigurationHandler)(__kindof UITableViewCell * _Nonnull , id _Nonnull identifier); - -@class __UIDiffableDataSourceSnapshot; - -API_AVAILABLE(ios(13.0), tvos(13.0)) -@interface __UIDiffableDataSource : NSObject - -- (instancetype)initWithCollectionView:(UICollectionView*)collectionView - cellProvider:(UIDiffableDataSourceCollectionViewCellProvider)cellProvider; - -- (instancetype)initWithCollectionView:(UICollectionView *)collectionView - cellProvider:(UIDiffableDataSourceCollectionViewCellProvider)cellProvider - dataSource:(id)dataSource; - -- (instancetype)initWithCollectionView:(UICollectionView*)collectionView - reuseIdentifierProvider:(UIDiffableDataSourceCellReuseIdentifierProvider)cellReuseProvider - cellConfigurationHandler:(UIDiffableDataSourceCollectionViewCellConfigurationHandler)cellConfigurationHandler; - -- (NSString*)description; - -- (instancetype)initWithTableView:(UITableView*)tableView - cellProvider:(UIDiffableDataSourceTableViewCellProvider)cellProvider; - -- (instancetype)initWithTableView:(UITableView*)tableView - reuseIdentifierProvider:(UIDiffableDataSourceCellReuseIdentifierProvider)cellResueProvider - cellConfigurationHandler:(UIDiffableDataSourceTableViewCellConfigurationHandler)cellConfigurationHandler; - -@property(nonatomic) UITableViewRowAnimation tableViewDefaultRowAnimation; -@property(nonatomic,weak,readonly,nullable) UITableView *tableView; -@property(nonatomic,copy) UITableViewDiffableDataSourceCellProvider tableViewCellProvider; - -@property(nonatomic,weak,readonly,nullable) UICollectionView *collectionView; -@property(nonatomic,nullable,copy) UIDiffableDataSourceSupplementaryViewProvider supplementaryViewProvider; - - -- (instancetype)init NS_UNAVAILABLE; - -@property(nonatomic,readonly) NSInteger numberOfItems; -@property(nonatomic,readonly) NSInteger numberOfSections; -@property(nonatomic,readonly) NSArray *sectionIdentifiers; -@property(nonatomic,readonly) NSArray *itemIdentifiers; - -- (NSInteger)numberOfItemsInSection:(id)sectionIdentifier; -- (NSArray*)itemIdentifiersInSectionWithIdentifier:(id)sectionIdentifier; -- (nullable id)sectionIdentifierForSectionContainingItemIdentifier:(id)identifier; - -- (NSInteger)indexOfItemIdentifier:(id)itemIdentifier; -- (NSInteger)indexOfSectionIdentifier:(id)sectionIdentifier; - -- (void)appendItemsWithIdentifiers:(NSArray*)identifiers; -- (void)appendItemsWithIdentifiers:(NSArray*)identifiers intoSectionWithIdentifier:(id _Nullable)sectionIdentifier; - -- (void)insertItemsWithIdentifiers:(NSArray*)identifiers beforeItemWithIdentifier:(id)itemIdentifier; -- (void)insertItemsWithIdentifiers:(NSArray*)identifiers afterItemWithIdentifier:(id)itemIdentifier; - -- (void)deleteItemsWithIdentifiers:(NSArray*)identifiers; -- (void)deleteAllItems; - -- (void)moveItemWithIdentifier:(id)fromIdentifier beforeItemWithIdentifier:(id)toIdentifier; -- (void)moveItemWithIdentifier:(id)fromIdentifier afterItemWithIdentifier:(id)toIdentifier; - -- (void)reloadItemsWithIdentifiers:(NSArray*)identifiers; - -- (void)appendSectionsWithIdentifiers:(NSArray*)sectionIdentifiers; - -- (void)insertSectionsWithIdentifiers:(NSArray*)sectionIdentifiers beforeSectionWithIdentifier:(id)toSectionIdentifier; -- (void)insertSectionsWithIdentifiers:(NSArray*)sectionIdentifiers afterSectionWithIdentifier:(id)toSectionIdentifier; - -- (void)deleteSectionsWithIdentifiers:(NSArray*)sectionIdentifiers; - -- (void)moveSectionWithIdentifier:(id)fromSectionIdentifier beforeSectionWithIdentifier:(id)toSectionIdentifier; -- (void)moveSectionWithIdentifier:(id)fromSectionIdentifier afterSectionWithIdentifier:(id)toSectionIdentifier; - -- (void)reloadSectionsWithIdentifiers:(NSArray*)sectionIdentifiers; - -- (nullable id)itemIdentifierForIndexPath:(NSIndexPath*)indexPath; -- (nullable NSIndexPath*)indexPathForItemIdentifier:(id)identifier; - - -- (__UIDiffableDataSourceSnapshot*)snapshot; -- (__UIDiffableDataSourceSnapshot*)emptySnapshot; -- (void)applyDifferencesFromSnapshot:(__UIDiffableDataSourceSnapshot*)snapshot; -- (void)reloadFromSnapshot:(__UIDiffableDataSourceSnapshot*)snapshot; -- (void)applyDifferencesFromSnapshot:(__UIDiffableDataSourceSnapshot *)snapshot animatingDifferences:(BOOL)animatingDifferences; - - -// deprecated - -- (void)appendSectionWithIdentifier:(id)sectionIdentifier; -- (void)insertSectionWithIdentifier:(id)sectionIdentifier beforeSectionWithIdentifier:(id)toSectionIdentifier; -- (void)insertSectionWithIdentifier:(id)sectionIdentifier afterSectionWithIdentifier:(id)toSectionIdentifier; -- (void)applySnapshot:(__UIDiffableDataSourceSnapshot*)snapshot; - - -@property(nonatomic,nullable,copy) UIDiffableDataSourceSupplementaryViewReuseIdentifierProvider supplementaryReuseIdentifierProvider; -@property(nonatomic,nullable,copy) UIDiffableDataSourceSupplementaryViewConfigurationHandler supplementaryViewConfigurationHandler; - -@property(nonatomic,copy) UICollectionViewDiffableDataSourceCellProvider collectionViewCellProvider; - - -// helpers - -- (NSInteger)_numberOfSectionsForCollectionView:(UICollectionView*)collectionView NS_SWIFT_NAME(_numberOfSectionsForCollectionView(_:)); -- (NSInteger)_numberOfItemsInSection:(NSInteger)section collectionView:(UICollectionView*)collectionView NS_SWIFT_NAME(_numberOfItemsInSection(_:collectionView:)); -- (UICollectionViewCell*)_cellForItemAtIndexPath:(NSIndexPath*)indexPath collectionView:(UICollectionView*)collectionView NS_SWIFT_NAME(_cellForItemAtIndexPath(_:collectionView:)); -- (UICollectionReusableView*)_viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath collectionView:(UICollectionView *)collectionView NS_SWIFT_NAME(_viewForSupplementaryElementOfKind(_:atIndexPath:collectionView:)); - -- (NSInteger)_numberOfSectionsForTableView:(UITableView*)tableView NS_SWIFT_NAME(_numberOfSectionsForTableView(_:)); -- (NSInteger)_numberOfRowsInSection:(NSInteger)section tableView:(UITableView*)tableView NS_SWIFT_NAME(_numberOfRowsInSection(_:tableView:)); -- (UITableViewCell*)_cellForRowAtIndexPath:(NSIndexPath*)indexPath tableView:(UITableView*)tableView NS_SWIFT_NAME(_cellForRowAtIndexPath(_:tableView:)); - -- (NSInteger)_numberOfSectionsForCollectionViewDeprecatedSPI:(UICollectionView*)collectionView NS_SWIFT_NAME(numberOfSections(for:)); -- (NSInteger)_numberOfItemsInSectionDeprecatedSPI:(NSInteger)section collectionView:(UICollectionView*)collectionView NS_SWIFT_NAME(numberOfItems(inSection:collectionView:)); -- (UICollectionViewCell*)_cellForItemAtIndexPathDeprecatedSPI:(NSIndexPath*)indexPath collectionView:(UICollectionView*)collectionView NS_SWIFT_NAME(cellForItem(at:collectionView:)); -- (UICollectionReusableView*)_viewForSupplementaryElementOfKindDeprecatedSPI:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath collectionView:(UICollectionView *)collectionView NS_SWIFT_NAME(viewForSupplementaryElement(ofKind:at:collectionView:)); - -- (NSInteger)_numberOfSectionsForTableViewDeprecatedSPI:(UITableView*)tableView NS_SWIFT_NAME(numberOfSections(for:)); -- (NSInteger)_numberOfRowsInSectionDeprecatedSPI:(NSInteger)section tableView:(UITableView*)tableView NS_SWIFT_NAME(numberOfRows(inSection:tableView:)); -- (UITableViewCell*)_cellForRowAtIndexPathDeprecatedSPI:(NSIndexPath*)indexPath tableView:(UITableView*)tableView NS_SWIFT_NAME(cellForRow(at:tableView:)); - -@end - - -API_AVAILABLE(ios(13.0), tvos(13.0)) -@interface __UIDiffableDataSourceSnapshot : __UIDiffableDataSource -- (instancetype)init; -@end - -#if __has_feature(nullability) -#pragma clang assume_nonnull end -#endif - - -#endif // TARGET_OS_TV || TARGET_OS_IOS - -#endif // SWIFT_STDLIB_SHIMS_UIKIT_OVERLAY_H - diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index deeec91d27dbd..26b4ff37737ea 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -178,6 +178,11 @@ #else #define SWIFT_IMAGE_EXPORTS_swift_Concurrency 0 #endif +#if defined(swift_Distributed_EXPORTS) +#define SWIFT_IMAGE_EXPORTS_swift_Distributed 1 +#else +#define SWIFT_IMAGE_EXPORTS_swift_Distributed 0 +#endif #if defined(swift_Differentiation_EXPORTS) #define SWIFT_IMAGE_EXPORTS_swift_Differentiation 1 #else diff --git a/stdlib/public/SwiftShims/XPCOverlayShims.h b/stdlib/public/SwiftShims/XPCOverlayShims.h deleted file mode 100644 index d3d32d8468c81..0000000000000 --- a/stdlib/public/SwiftShims/XPCOverlayShims.h +++ /dev/null @@ -1,75 +0,0 @@ -//===--- XPCOverlayShims.h --------------------------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_STDLIB_SHIMS_XPC_OVERLAY_H -#define SWIFT_STDLIB_SHIMS_XPC_OVERLAY_H - -@import XPC; - -static inline xpc_type_t -_swift_xpc_get_type(xpc_object_t object) { - return xpc_get_type(object); -} - -static inline xpc_object_t -_swift_xpc_bool_true() { - return XPC_BOOL_TRUE; -} - -static inline xpc_object_t -_swift_xpc_bool_false() { - return XPC_BOOL_FALSE; -} - -#define SWIFT_XPC_TYPE(t) \ - static inline xpc_type_t \ - _swift_xpc_type_##t(void) { \ - return XPC_TYPE_##t; \ - } - -SWIFT_XPC_TYPE(CONNECTION) -SWIFT_XPC_TYPE(ENDPOINT) -SWIFT_XPC_TYPE(NULL) -SWIFT_XPC_TYPE(BOOL) -SWIFT_XPC_TYPE(INT64) -SWIFT_XPC_TYPE(UINT64) -SWIFT_XPC_TYPE(DOUBLE) -SWIFT_XPC_TYPE(DATE) -SWIFT_XPC_TYPE(DATA) -SWIFT_XPC_TYPE(STRING) -SWIFT_XPC_TYPE(UUID) -SWIFT_XPC_TYPE(FD) -SWIFT_XPC_TYPE(SHMEM) -SWIFT_XPC_TYPE(ARRAY) -SWIFT_XPC_TYPE(DICTIONARY) -SWIFT_XPC_TYPE(ERROR) -SWIFT_XPC_TYPE(ACTIVITY) - -#undef SWIFT_XPC_TYPE - -static inline xpc_object_t -_swift_xpc_connection_interrupted(void) { - return XPC_ERROR_CONNECTION_INTERRUPTED; -} - -static inline xpc_object_t -_swift_xpc_connection_invalid(void) { - return XPC_ERROR_CONNECTION_INVALID; -} - -static inline xpc_object_t -_swift_xpc_connection_termination_imminent(void) { - return XPC_ERROR_TERMINATION_IMMINENT; -} - -#endif // SWIFT_STDLIB_SHIMS_XPC_OVERLAY_H - diff --git a/stdlib/public/Darwin/Foundation/Pointers+DataProtocol.swift b/stdlib/public/SwiftShims/_SwiftDistributed.h similarity index 51% rename from stdlib/public/Darwin/Foundation/Pointers+DataProtocol.swift rename to stdlib/public/SwiftShims/_SwiftDistributed.h index 059fe87e197c3..280384321a0fc 100644 --- a/stdlib/public/Darwin/Foundation/Pointers+DataProtocol.swift +++ b/stdlib/public/SwiftShims/_SwiftDistributed.h @@ -1,24 +1,29 @@ -//===----------------------------------------------------------------------===// +//===--- _SwiftDistributed.h - Swift Distributed Support --------*- C++ -*-===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2021 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 // //===----------------------------------------------------------------------===// +// +// Defines types and support functions for the Swift Distributed actors. +// +//===----------------------------------------------------------------------===// +#ifndef SWIFT_DISTRIBUTED_H +#define SWIFT_DISTRIBUTED_H +#ifdef __cplusplus +namespace swift { +extern "C" { +#endif -extension UnsafeRawBufferPointer : DataProtocol { - public var regions: CollectionOfOne { - return CollectionOfOne(self) - } -} +#ifdef __cplusplus +} // extern "C" +} // namespace swift +#endif -extension UnsafeBufferPointer : DataProtocol where Element == UInt8 { - public var regions: CollectionOfOne> { - return CollectionOfOne(self) - } -} +#endif // SWIFT_DISTRIBUTED_H diff --git a/stdlib/public/SwiftShims/module.modulemap b/stdlib/public/SwiftShims/module.modulemap index de62f9c36e91c..273d936cf3085 100644 --- a/stdlib/public/SwiftShims/module.modulemap +++ b/stdlib/public/SwiftShims/module.modulemap @@ -22,70 +22,27 @@ module SwiftShims { export * } +module _SwiftConcurrencyShims { + header "_SwiftConcurrency.h" +} + module SwiftOverlayShims { header "LibcOverlayShims.h" export * } -// Various headers used to build overlays on Apple platforms. + +// Obsolete overlay shims on Apple platforms. // Note: These deliberately do not use "export *" to avoid circularity issues // (overlay -> shims -> framework -> overlay) -// FIXME: These are only needed when building each overlay; they declare no -// types and therefore would not strictly need to be present in an installed -// Swift. -// FIXME: These are not used at all on non-Apple platforms. -module _SwiftDispatchOverlayShims { - header "DispatchOverlayShims.h" -} -module _SwiftObjectiveCOverlayShims { - header "ObjectiveCOverlayShims.h" -} +// NOTE: These are obsolete and should never be imported into any project. +// They are going to be removed in a future compiler release. -module _SwiftOSOverlayShims { - header "OSOverlayShims.h" -} - -module _SwiftSafariServicesOverlayShims { - header "SafariServicesOverlayShims.h" -} - -module _SwiftAppKitOverlayShims { - header "AppKitOverlayShims.h" -} - -module _SwiftUIKitOverlayShims { - header "UIKitOverlayShims.h" +module _SwiftDispatchOverlayShims { + header "DispatchOverlayShims.h" } module _SwiftXCTestOverlayShims { header "XCTestOverlayShims.h" } - -module _SwiftXPCOverlayShims { - header "XPCOverlayShims.h" -} - -module _SwiftCoreFoundationOverlayShims { - header "CoreFoundationOverlayShims.h" -} - -module _SwiftFoundationOverlayShims { - header "FoundationOverlayShims.h" -} - -module _SwiftNetworkOverlayShims { - header "NetworkOverlayShims.h" -} - -module _SwiftClockKitOverlayShims { - header "ClockKitOverlayShims.h" -} - -module _SwiftCoreMediaOverlayShims { - header "CoreMediaOverlayShims.h" -} - -module _SwiftConcurrencyShims { - header "_SwiftConcurrency.h" -} diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index 8b702edd207c0..21ee82f4dc481 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -260,7 +260,7 @@ extension AnyHashable: CustomReflectable { } } -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) extension AnyHashable: _HasCustomAnyHashableRepresentation { } diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index a3d5962daca16..5fb18d7ea6d75 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1604,32 +1604,18 @@ extension Array { _makeMutableAndUnique() let count = _buffer.mutableCount - // Ensure that body can't invalidate the storage or its bounds by - // moving self into a temporary working array. - // NOTE: The stack promotion optimization that keys of the - // "array.withUnsafeMutableBufferPointer" semantics annotation relies on the - // array buffer not being able to escape in the closure. It can do this - // because we swap the array buffer in self with an empty buffer here. Any - // escape via the address of self in the closure will therefore escape the - // empty array. - - var work = Array() - (work, self) = (self, work) - - // Create an UnsafeBufferPointer over work that we can pass to body - let pointer = work._buffer.mutableFirstElementAddress + // Create an UnsafeBufferPointer that we can pass to body + let pointer = _buffer.mutableFirstElementAddress var inoutBufferPointer = UnsafeMutableBufferPointer( start: pointer, count: count) - // Put the working array back before returning. defer { _precondition( inoutBufferPointer.baseAddress == pointer && inoutBufferPointer.count == count, "Array withUnsafeMutableBufferPointer: replacing the buffer is not allowed") - - (work, self) = (self, work) _endMutation() + _fixLifetime(self) } // Invoke the body. @@ -1865,6 +1851,22 @@ extension Array { } } +#if INTERNAL_CHECKS_ENABLED +extension Array { + // This allows us to test the `_copyContents` implementation in + // `_ArrayBuffer`. (It's like `_copyToContiguousArray` but it always makes a + // copy.) + @_alwaysEmitIntoClient + public func _copyToNewArray() -> [Element] { + Array(unsafeUninitializedCapacity: self.count) { buffer, count in + var (it, c) = self._buffer._copyContents(initializing: buffer) + _precondition(it.next() == nil) + count = c + } + } +} +#endif + #if _runtime(_ObjC) // We isolate the bridging of the Cocoa Array -> Swift Array here so that // in the future, we can eagerly bridge the Cocoa array. We need this function diff --git a/stdlib/public/core/ArrayBuffer.swift b/stdlib/public/core/ArrayBuffer.swift index 1f0e0bf5dcdfd..d1d0f0d40bf44 100644 --- a/stdlib/public/core/ArrayBuffer.swift +++ b/stdlib/public/core/ArrayBuffer.swift @@ -108,7 +108,7 @@ extension _ArrayBuffer { if !_isClassOrObjCExistential(Element.self) { return _storage.isUniquelyReferencedUnflaggedNative() } - return _storage.isUniquelyReferencedNative() && _isNative + return _storage.isUniquelyReferencedNative() } /// Returns `true` and puts the buffer in a mutable state iff the buffer's @@ -311,12 +311,20 @@ extension _ArrayBuffer { return UnsafeMutableRawPointer(result).assumingMemoryBound(to: Element.self) } - public __consuming func _copyContents( + @inlinable + internal __consuming func _copyContents( initializing buffer: UnsafeMutableBufferPointer - ) -> (Iterator,UnsafeMutableBufferPointer.Index) { - // This customization point is not implemented for internal types. - // Accidentally calling it would be a catastrophic performance bug. - fatalError("unsupported") + ) -> (Iterator, UnsafeMutableBufferPointer.Index) { + if _fastPath(_isNative) { + let (_, c) = _native._copyContents(initializing: buffer) + return (IndexingIterator(_elements: self, _position: c), c) + } + guard buffer.count > 0 else { return (makeIterator(), 0) } + let ptr = UnsafeMutableRawPointer(buffer.baseAddress)? + .assumingMemoryBound(to: AnyObject.self) + let (_, c) = _nonNative._copyContents( + initializing: UnsafeMutableBufferPointer(start: ptr, count: buffer.count)) + return (IndexingIterator(_elements: self, _position: c), c) } /// Returns a `_SliceBuffer` containing the given sub-range of elements in diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index 3cc8a964c5147..d797b0223a505 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -1239,32 +1239,18 @@ extension ArraySlice { // Ensure unique storage _makeMutableAndUnique() - // Ensure that body can't invalidate the storage or its bounds by - // moving self into a temporary working array. - // NOTE: The stack promotion optimization that keys of the - // "array.withUnsafeMutableBufferPointer" semantics annotation relies on the - // array buffer not being able to escape in the closure. It can do this - // because we swap the array buffer in self with an empty buffer here. Any - // escape via the address of self in the closure will therefore escape the - // empty array. - - var work = ArraySlice() - (work, self) = (self, work) - - // Create an UnsafeBufferPointer over work that we can pass to body - let pointer = work._buffer.firstElementAddress + // Create an UnsafeBufferPointer that we can pass to body + let pointer = _buffer.firstElementAddress var inoutBufferPointer = UnsafeMutableBufferPointer( start: pointer, count: count) - // Put the working array back before returning. defer { _precondition( inoutBufferPointer.baseAddress == pointer && inoutBufferPointer.count == count, "ArraySlice withUnsafeMutableBufferPointer: replacing the buffer is not allowed") - - (work, self) = (self, work) _endMutation() + _fixLifetime(self) } // Invoke the body. @@ -1520,3 +1506,19 @@ extension ArraySlice { extension ArraySlice: Sendable, UnsafeSendable where Element: Sendable { } + +#if INTERNAL_CHECKS_ENABLED +extension ArraySlice { + // This allows us to test the `_copyContents` implementation in + // `_SliceBuffer`. (It's like `_copyToContiguousArray` but it always makes a + // copy.) + @_alwaysEmitIntoClient + public func _copyToNewArray() -> [Element] { + Array(unsafeUninitializedCapacity: self.count) { buffer, count in + var (it, c) = self._buffer._copyContents(initializing: buffer) + _precondition(it.next() == nil) + count = c + } + } +} +#endif diff --git a/stdlib/public/core/BridgeStorage.swift b/stdlib/public/core/BridgeStorage.swift index 4ee6dac4ea5cf..2a7ad6a351005 100644 --- a/stdlib/public/core/BridgeStorage.swift +++ b/stdlib/public/core/BridgeStorage.swift @@ -72,7 +72,7 @@ internal struct _BridgeStorage { @inlinable @inline(__always) internal mutating func isUniquelyReferencedNative() -> Bool { - return _isUnique(&rawValue) + return isNative && _isUnique(&rawValue) } @_alwaysEmitIntoClient diff --git a/stdlib/public/core/Builtin.swift b/stdlib/public/core/Builtin.swift index c0eb399b309c9..b1fc2e5b1f709 100644 --- a/stdlib/public/core/Builtin.swift +++ b/stdlib/public/core/Builtin.swift @@ -346,12 +346,18 @@ internal func _class_getInstancePositiveExtentSize(_ theClass: AnyClass) -> Int } #if INTERNAL_CHECKS_ENABLED -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +// "9999" means: enable if linked with a built library, but not when linked with +// the OS libraries. +// Note: this must not be changed to a "real" OS version. +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) @usableFromInline @_silgen_name("_swift_isImmutableCOWBuffer") internal func _swift_isImmutableCOWBuffer(_ object: AnyObject) -> Bool -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +// "9999" means: enable if linked with a built library, but not when linked with +// the OS libraries. +// Note: this must not be changed to a "real" OS version. +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) @usableFromInline @_silgen_name("_swift_setImmutableCOWBuffer") internal func _swift_setImmutableCOWBuffer(_ object: AnyObject, _ immutable: Bool) -> Bool diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index cd61f42066add..cbee56389fe5a 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -233,7 +233,7 @@ set(swift_core_link_flags "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}") set(swift_core_framework_depends) set(swift_core_private_link_libraries) set(swift_stdlib_compile_flags "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}") -if(SWIFT_PRIMARY_VARIANT_SDK IN_LIST SWIFT_APPLE_PLATFORMS) +if(SWIFT_PRIMARY_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) list(APPEND swift_core_link_flags "-all_load") list(APPEND swift_core_private_link_libraries icucore) else() diff --git a/stdlib/public/core/CocoaArray.swift b/stdlib/public/core/CocoaArray.swift index 9ba41d0ebaa48..ff61076d079a6 100644 --- a/stdlib/public/core/CocoaArray.swift +++ b/stdlib/public/core/CocoaArray.swift @@ -146,5 +146,16 @@ internal struct _CocoaArrayWrapper: RandomAccessCollection { } return result } + + @_alwaysEmitIntoClient + internal __consuming func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator, UnsafeMutableBufferPointer.Index) { + guard buffer.count > 0 else { return (makeIterator(), 0) } + let start = buffer.baseAddress! + let c = Swift.min(self.count, buffer.count) + let end = _copyContents(subRange: 0 ..< c, initializing: start) + return (IndexingIterator(_elements: self, _position: c), c) + } } #endif diff --git a/stdlib/public/core/Collection.swift b/stdlib/public/core/Collection.swift index bb9f026448e0b..12ed227ce3e03 100644 --- a/stdlib/public/core/Collection.swift +++ b/stdlib/public/core/Collection.swift @@ -387,12 +387,11 @@ public protocol Collection: Sequence { /// Returns an iterator over the elements of the collection. override __consuming func makeIterator() -> Iterator - /// A sequence that represents a contiguous subrange of the collection's - /// elements. + /// A collection representing a contiguous subrange of this collection's + /// elements. The subsequence shares indices with the original collection. /// - /// This associated type appears as a requirement in the `Sequence` - /// protocol, but it is restated here with stricter constraints. In a - /// collection, the subsequence should also conform to `Collection`. + /// The default subsequence type for collections that don't define their own + /// is `Slice`. associatedtype SubSequence: Collection = Slice where SubSequence.Index == Index, Element == SubSequence.Element, @@ -1044,6 +1043,18 @@ extension Collection where SubSequence == Slice { } } +extension Collection { + // This unavailable default implementation of `subscript(bounds: Range<_>)` + // prevents incomplete Collection implementations from satisfying the + // protocol through the use of the generic convenience implementation + // `subscript(r: R)`. If that were the case, at + // runtime the generic implementation would call itself + // in an infinite recursion because of the absence of a better option. + @available(*, unavailable) + @_alwaysEmitIntoClient + public subscript(bounds: Range) -> SubSequence { fatalError() } +} + extension Collection where SubSequence == Self { /// Removes and returns the first element of the collection. /// diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index 0ebe11ed885a7..89dcec8c804f6 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -1176,32 +1176,18 @@ extension ContiguousArray { _makeMutableAndUnique() let count = _buffer.mutableCount - // Ensure that body can't invalidate the storage or its bounds by - // moving self into a temporary working array. - // NOTE: The stack promotion optimization that keys of the - // "array.withUnsafeMutableBufferPointer" semantics annotation relies on the - // array buffer not being able to escape in the closure. It can do this - // because we swap the array buffer in self with an empty buffer here. Any - // escape via the address of self in the closure will therefore escape the - // empty array. - - var work = ContiguousArray() - (work, self) = (self, work) - - // Create an UnsafeBufferPointer over work that we can pass to body - let pointer = work._buffer.mutableFirstElementAddress + // Create an UnsafeBufferPointer that we can pass to body + let pointer = _buffer.mutableFirstElementAddress var inoutBufferPointer = UnsafeMutableBufferPointer( start: pointer, count: count) - // Put the working array back before returning. defer { _precondition( inoutBufferPointer.baseAddress == pointer && inoutBufferPointer.count == count, "ContiguousArray withUnsafeMutableBufferPointer: replacing the buffer is not allowed") - - (work, self) = (self, work) _endMutation() + _fixLifetime(self) } // Invoke the body. diff --git a/stdlib/public/core/ContiguousArrayBuffer.swift b/stdlib/public/core/ContiguousArrayBuffer.swift index 90df5c561116d..7e139ee0a5dd8 100644 --- a/stdlib/public/core/ContiguousArrayBuffer.swift +++ b/stdlib/public/core/ContiguousArrayBuffer.swift @@ -13,7 +13,10 @@ import SwiftShims #if INTERNAL_CHECKS_ENABLED -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +// "9999" means: enable if linked with a built library, but not when linked with +// the OS libraries. +// Note: this must not be changed to a "real" OS version. +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) @_silgen_name("swift_COWChecksEnabled") public func _COWChecksEnabled() -> Bool #endif @@ -460,6 +463,9 @@ internal struct _ContiguousArrayBuffer: _ArrayBufferProtocol { @_alwaysEmitIntoClient internal var isImmutable: Bool { get { + // "9999" means: enable if linked with a built library, but not when + // linked with the OS libraries. + // Note: this must not be changed to a "real" OS version. if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { if (_COWChecksEnabled()) { return capacity == 0 || _swift_isImmutableCOWBuffer(_storage) @@ -468,6 +474,9 @@ internal struct _ContiguousArrayBuffer: _ArrayBufferProtocol { return true } nonmutating set { + // "9999" means: enable if linked with a built library, but not when + // linked with the OS libraries. + // Note: this must not be changed to a "real" OS version. if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { if (_COWChecksEnabled()) { // Make sure to not modify the empty array singleton (which has a @@ -489,6 +498,9 @@ internal struct _ContiguousArrayBuffer: _ArrayBufferProtocol { @_alwaysEmitIntoClient internal var isMutable: Bool { + // "9999" means: enable if linked with a built library, but not when + // linked with the OS libraries. + // Note: this must not be changed to a "real" OS version. if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { if (_COWChecksEnabled()) { return !_swift_isImmutableCOWBuffer(_storage) @@ -644,12 +656,17 @@ internal struct _ContiguousArrayBuffer: _ArrayBufferProtocol { return target + initializedCount } - public __consuming func _copyContents( + @inlinable + internal __consuming func _copyContents( initializing buffer: UnsafeMutableBufferPointer - ) -> (Iterator,UnsafeMutableBufferPointer.Index) { - // This customization point is not implemented for internal types. - // Accidentally calling it would be a catastrophic performance bug. - fatalError("unsupported") + ) -> (Iterator, UnsafeMutableBufferPointer.Index) { + guard buffer.count > 0 else { return (makeIterator(), 0) } + let c = Swift.min(self.count, buffer.count) + buffer.baseAddress!.initialize( + from: firstElementAddress, + count: c) + _fixLifetime(owner) + return (IndexingIterator(_elements: self, _position: c), c) } /// Returns a `_SliceBuffer` containing the given `bounds` of values diff --git a/stdlib/public/core/EitherSequence.swift b/stdlib/public/core/EitherSequence.swift index bfdb80ea30919..733e43cd46cc5 100644 --- a/stdlib/public/core/EitherSequence.swift +++ b/stdlib/public/core/EitherSequence.swift @@ -1,14 +1,14 @@ -////===--- _EitherSequence.swift - A sequence type-erasing two sequences -----===// -//// -//// 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 -//// -////===----------------------------------------------------------------------===// +//===--- _EitherSequence.swift - A sequence type-erasing two sequences -----===// +// +// 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 +// +//===----------------------------------------------------------------------===// // Not public stdlib API, currently used in Mirror.children implementation. diff --git a/stdlib/public/core/IntegerTypes.swift.gyb b/stdlib/public/core/IntegerTypes.swift.gyb index 96f21a011976b..b4b2083b9df0d 100644 --- a/stdlib/public/core/IntegerTypes.swift.gyb +++ b/stdlib/public/core/IntegerTypes.swift.gyb @@ -1514,15 +1514,23 @@ ${assignmentOperatorComment(x.operator, True)} % end % dbits = bits*2 -% if bits <= word_bits: +% if bits == 64: + #if !(arch(arm) || arch(i386) || arch(wasm32)) + // On 32b architectures we fall back on the generic implementation, + // because LLVM doesn't know how to codegen the 128b multiply we use. + // + // Note that arm64_32 is a 64b architecture for the purposes of this + // check, because we have a 64x64 -> 128 multiply there (the actual + // ISA is AArch64). +% end /// Returns a tuple containing the high and low parts of the result of /// multiplying this value by the given value. /// /// Use this method to calculate the full result of a product that would /// otherwise overflow. Unlike traditional truncating multiplication, the - /// `multipliedFullWidth(by:)` method returns a tuple - /// containing both the `high` and `low` parts of the product of this value and - /// `other`. The following example uses this method to multiply two `UInt8` + /// `multipliedFullWidth(by:)` method returns a tuple containing both the + /// `high` and `low` parts of the product of this value and `other`. + /// The following example uses this method to multiply two `UInt8` /// values that normally overflow when multiplied: /// /// let x: UInt8 = 100 @@ -1557,6 +1565,8 @@ ${assignmentOperatorComment(x.operator, True)} let high = ${Self}(Builtin.truncOrBitCast_Int${dbits}_Int${bits}(shifted)) return (high: high, low: low) } +% if bits == 64: + #endif % end /// Returns a tuple containing the quotient and remainder of dividing the diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 31ec81b8294b8..db3a02765197e 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -258,7 +258,7 @@ public protocol Numeric: AdditiveArithmetic, ExpressibleByIntegerLiteral { static func *=(lhs: inout Self, rhs: Self) } -/// A type that can represent both positive and negative values. +/// A numeric type with a negation operation. /// /// The `SignedNumeric` protocol extends the operations defined by the /// `Numeric` protocol to include a value's additive inverse. @@ -3502,8 +3502,19 @@ extension UnsignedInteger where Self: FixedWidthInteger { /// An integer type that can represent both positive and negative values. public protocol SignedInteger: BinaryInteger, SignedNumeric { - // These requirements are for the source code compatibility with Swift 3 + // These requirements are needed for binary compatibility; the following: + // + // func foo(_ a: T) -> T + // where T: SignedInteger & FixedWidthInteger { + // a &+ 1 + // } + // + // generated a call to `static Swift.SignedInteger._maskingAdd(A, A) -> A` + // when compiled with Swift 5.5 and earlier. + @available(*, deprecated, message: "Use &+ instead.") static func _maskingAdd(_ lhs: Self, _ rhs: Self) -> Self + + @available(*, deprecated, message: "Use &- instead.") static func _maskingSubtract(_ lhs: Self, _ rhs: Self) -> Self } @@ -3629,49 +3640,48 @@ public func numericCast(_ x: T) -> U { return U(x) } -// FIXME(integers): Absence of &+ causes ambiguity in the code like the -// following: -// func f(_ x: T, _ y: T) { -// var _ = (x &+ (y - 1)) < x -// } -// Compiler output: -// error: ambiguous reference to member '-' -// var _ = (x &+ (y - 1)) < x -// ^ +// Needed to support user-defined types conformance to SignedInteger. +// We need these defaults to exist, but they are not called. extension SignedInteger { - @_transparent + @available(*, deprecated, message: "Use &+ instead.") public static func _maskingAdd(_ lhs: Self, _ rhs: Self) -> Self { fatalError("Should be overridden in a more specific type") } - - @_transparent + + @available(*, deprecated, message: "Use &- instead.") public static func _maskingSubtract(_ lhs: Self, _ rhs: Self) -> Self { fatalError("Should be overridden in a more specific type") } } +// These symbols have to exist for ABI compatibility, but should not be used +// any longer; we want to find the FixedWidthInteger definitions instead. extension SignedInteger where Self: FixedWidthInteger { - // This overload is supposed to break the ambiguity between the - // implementations on SignedInteger and FixedWidthInteger - @_transparent - public static func &+ (lhs: Self, rhs: Self) -> Self { - return _maskingAdd(lhs, rhs) + @available(*, unavailable) + public static func &+(lhs: Self, rhs: Self) -> Self { + lhs.addingReportingOverflow(rhs).partialValue } - - @_transparent + + // This may be called in rare situations by binaries compiled with + // Swift 5.5 and earlier, so we need to keep it around for compatibility. + // We can't mark it unavailable, because then the concrete signed integer + // types in the standard library would not satisfy the protocol requirements. + @available(*, deprecated, message: "Use &+ instead.") public static func _maskingAdd(_ lhs: Self, _ rhs: Self) -> Self { - return lhs.addingReportingOverflow(rhs).partialValue + lhs.addingReportingOverflow(rhs).partialValue } - // This overload is supposed to break the ambiguity between the - // implementations on SignedInteger and FixedWidthInteger - @_transparent - public static func &- (lhs: Self, rhs: Self) -> Self { - return _maskingSubtract(lhs, rhs) + @available(*, unavailable) + public static func &-(lhs: Self, rhs: Self) -> Self { + lhs.subtractingReportingOverflow(rhs).partialValue } - - @_transparent + + // This may be called in rare situations by binaries compiled with + // Swift 5.5 and earlier, so we need to keep it around for compatibility. + // We can't mark it unavailable, because then the concrete signed integer + // types in the standard library would not satisfy the protocol requirements. + @available(*, deprecated, message: "Use &- instead.") public static func _maskingSubtract(_ lhs: Self, _ rhs: Self) -> Self { - return lhs.subtractingReportingOverflow(rhs).partialValue + lhs.subtractingReportingOverflow(rhs).partialValue } } diff --git a/stdlib/public/core/MigrationSupport.swift b/stdlib/public/core/MigrationSupport.swift index 16124058138bc..c4dbf718825d4 100644 --- a/stdlib/public/core/MigrationSupport.swift +++ b/stdlib/public/core/MigrationSupport.swift @@ -237,7 +237,7 @@ extension String { } extension String.UnicodeScalarView: _CustomPlaygroundQuickLookable { - @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UnicodeScalarView.customPlaygroundQuickLook will be removed in Swift 5.0") + @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UnicodeScalarView.customPlaygroundQuickLook will be removed in a future Swift version") public var customPlaygroundQuickLook: _PlaygroundQuickLook { return .text(description) } @@ -256,14 +256,14 @@ public typealias UnicodeScalar = Unicode.Scalar extension String.UTF16View: _CustomPlaygroundQuickLookable { - @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UTF16View.customPlaygroundQuickLook will be removed in Swift 5.0") + @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UTF16View.customPlaygroundQuickLook will be removed in a future Swift version") public var customPlaygroundQuickLook: _PlaygroundQuickLook { return .text(description) } } extension String.UTF8View: _CustomPlaygroundQuickLookable { - @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UTF8View.customPlaygroundQuickLook will be removed in Swift 5.0") + @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "UTF8View.customPlaygroundQuickLook will be removed in a future Swift version") public var customPlaygroundQuickLook: _PlaygroundQuickLook { return .text(description) } @@ -310,7 +310,7 @@ extension Substring { } extension Substring: _CustomPlaygroundQuickLookable { - @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "Substring.customPlaygroundQuickLook will be removed in Swift 5.0") + @available(swift, deprecated: 4.2/*, obsoleted: 5.0*/, message: "Substring.customPlaygroundQuickLook will be removed in a future Swift version") public var customPlaygroundQuickLook: _PlaygroundQuickLook { return String(self).customPlaygroundQuickLook } diff --git a/stdlib/public/core/MutableCollection.swift b/stdlib/public/core/MutableCollection.swift index d998df49acdf8..ee035f7aec7ab 100644 --- a/stdlib/public/core/MutableCollection.swift +++ b/stdlib/public/core/MutableCollection.swift @@ -238,6 +238,7 @@ extension MutableCollection { /// the range must be valid indices of the collection. /// /// - Complexity: O(1) + @available(*, unavailable) @inlinable public subscript(bounds: Range) -> Slice { get { @@ -249,6 +250,19 @@ extension MutableCollection { } } + // This unavailable default implementation of `subscript(bounds: Range<_>)` + // prevents incomplete MutableCollection implementations from satisfying the + // protocol through the use of the generic convenience implementation + // `subscript(r: R)`. If that were the case, at + // runtime the generic implementation would call itself + // in an infinite recursion due to the absence of a better option. + @available(*, unavailable) + @_alwaysEmitIntoClient + public subscript(bounds: Range) -> SubSequence { + get { fatalError() } + set { fatalError() } + } + /// Exchanges the values at the specified indices of the collection. /// /// Both parameters must be valid indices of the collection that are not @@ -269,6 +283,45 @@ extension MutableCollection { } } +extension MutableCollection where SubSequence == Slice { + + /// Accesses a contiguous subrange of the collection's elements. + /// + /// The accessed slice uses the same indices for the same elements as the + /// original collection. Always use the slice's `startIndex` property + /// instead of assuming that its indices start at a particular value. + /// + /// This example demonstrates getting a slice of an array of strings, finding + /// the index of one of the strings in the slice, and then using that index + /// in the original array. + /// + /// var streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"] + /// let streetsSlice = streets[2 ..< streets.endIndex] + /// print(streetsSlice) + /// // Prints "["Channing", "Douglas", "Evarts"]" + /// + /// let index = streetsSlice.firstIndex(of: "Evarts") // 4 + /// streets[index!] = "Eustace" + /// print(streets[index!]) + /// // Prints "Eustace" + /// + /// - Parameter bounds: A range of the collection's indices. The bounds of + /// the range must be valid indices of the collection. + /// + /// - Complexity: O(1) + @inlinable + @_alwaysEmitIntoClient + public subscript(bounds: Range) -> Slice { + get { + _failEarlyRangeCheck(bounds, bounds: startIndex.. Bool { + if count == 0 { + // Fast path that avoids computing the hash of the key. + return false + } return find(key).found } diff --git a/stdlib/public/core/OutputStream.swift b/stdlib/public/core/OutputStream.swift index 41ec5ec0ade15..5ede1f939ea20 100644 --- a/stdlib/public/core/OutputStream.swift +++ b/stdlib/public/core/OutputStream.swift @@ -276,6 +276,26 @@ internal func _getEnumCaseName(_ value: T) -> UnsafePointer? @_silgen_name("swift_OpaqueSummary") internal func _opaqueSummary(_ metadata: Any.Type) -> UnsafePointer? +/// Obtain a fallback raw value for an enum type without metadata; this +/// should be OK for enums from C/C++ until they have metadata (see +/// ). Note that if this turns out to be a Swift Enum +/// with missing metadata, the raw value may be misleading. +@_semantics("optimize.sil.specialize.generic.never") +internal func _fallbackEnumRawValue(_ value: T) -> Int64? { + switch MemoryLayout.size(ofValue: value) { + case 8: + return unsafeBitCast(value, to:Int64.self) + case 4: + return Int64(unsafeBitCast(value, to:Int32.self)) + case 2: + return Int64(unsafeBitCast(value, to:Int16.self)) + case 1: + return Int64(unsafeBitCast(value, to:Int8.self)) + default: + return nil + } +} + /// Do our best to print a value that cannot be printed directly. @_semantics("optimize.sil.specialize.generic.never") internal func _adHocPrint_unlocked( @@ -342,8 +362,14 @@ internal func _adHocPrint_unlocked( } target.write(caseName) } else { - // If the case name is garbage, just print the type name. printTypeName(mirror.subjectType) + // The case name is garbage; this might be a C/C++ enum without + // metadata, so see if we can get a raw value + if let rawValue = _fallbackEnumRawValue(value) { + target.write("(rawValue: ") + _debugPrint_unlocked(rawValue, &target); + target.write(")") + } } if let (_, value) = mirror.children.first { if Mirror(reflecting: value).displayStyle == .tuple { diff --git a/stdlib/public/core/Policy.swift b/stdlib/public/core/Policy.swift index 2606c452d72fc..4152e38ca7e74 100644 --- a/stdlib/public/core/Policy.swift +++ b/stdlib/public/core/Policy.swift @@ -34,6 +34,14 @@ extension Never: Error {} extension Never: Equatable, Comparable, Hashable {} +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +extension Never: Identifiable { + @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) + public var id: Never { + switch self {} + } +} + //===----------------------------------------------------------------------===// // Standardized aliases //===----------------------------------------------------------------------===// @@ -377,7 +385,7 @@ precedencegroup BitwiseShiftPrecedence { // Standard postfix operators. postfix operator ++ postfix operator -- -postfix operator ...: Comparable +postfix operator ... // Optional unwrapping operator is built into the compiler as a part of // postfix expression grammar. @@ -387,42 +395,42 @@ postfix operator ...: Comparable // Standard prefix operators. prefix operator ++ prefix operator -- -prefix operator !: Bool -prefix operator ~: BinaryInteger -prefix operator +: AdditiveArithmetic -prefix operator -: SignedNumeric -prefix operator ...: Comparable -prefix operator ..<: Comparable +prefix operator ! +prefix operator ~ +prefix operator + +prefix operator - +prefix operator ... +prefix operator ..< // Standard infix operators. // "Exponentiative" -infix operator <<: BitwiseShiftPrecedence, BinaryInteger -infix operator &<<: BitwiseShiftPrecedence, FixedWidthInteger -infix operator >>: BitwiseShiftPrecedence, BinaryInteger -infix operator &>>: BitwiseShiftPrecedence, FixedWidthInteger +infix operator <<: BitwiseShiftPrecedence +infix operator &<<: BitwiseShiftPrecedence +infix operator >>: BitwiseShiftPrecedence +infix operator &>>: BitwiseShiftPrecedence // "Multiplicative" -infix operator *: MultiplicationPrecedence, Numeric -infix operator &*: MultiplicationPrecedence, FixedWidthInteger -infix operator /: MultiplicationPrecedence, BinaryInteger, FloatingPoint -infix operator %: MultiplicationPrecedence, BinaryInteger -infix operator &: MultiplicationPrecedence, BinaryInteger +infix operator *: MultiplicationPrecedence +infix operator &*: MultiplicationPrecedence +infix operator /: MultiplicationPrecedence +infix operator %: MultiplicationPrecedence +infix operator &: MultiplicationPrecedence // "Additive" -infix operator +: AdditionPrecedence, AdditiveArithmetic, String, Array, Strideable -infix operator &+: AdditionPrecedence, FixedWidthInteger -infix operator -: AdditionPrecedence, AdditiveArithmetic, Strideable -infix operator &-: AdditionPrecedence, FixedWidthInteger -infix operator |: AdditionPrecedence, BinaryInteger -infix operator ^: AdditionPrecedence, BinaryInteger +infix operator +: AdditionPrecedence +infix operator &+: AdditionPrecedence +infix operator -: AdditionPrecedence +infix operator &-: AdditionPrecedence +infix operator |: AdditionPrecedence +infix operator ^: AdditionPrecedence // FIXME: is this the right precedence level for "..." ? -infix operator ...: RangeFormationPrecedence, Comparable -infix operator ..<: RangeFormationPrecedence, Comparable +infix operator ...: RangeFormationPrecedence +infix operator ..<: RangeFormationPrecedence // The cast operators 'as' and 'is' are hardcoded as if they had the // following attributes: @@ -434,12 +442,12 @@ infix operator ??: NilCoalescingPrecedence // "Comparative" -infix operator <: ComparisonPrecedence, Comparable -infix operator <=: ComparisonPrecedence, Comparable -infix operator >: ComparisonPrecedence, Comparable -infix operator >=: ComparisonPrecedence, Comparable -infix operator ==: ComparisonPrecedence, Equatable -infix operator !=: ComparisonPrecedence, Equatable +infix operator <: ComparisonPrecedence +infix operator <=: ComparisonPrecedence +infix operator >: ComparisonPrecedence +infix operator >=: ComparisonPrecedence +infix operator ==: ComparisonPrecedence +infix operator !=: ComparisonPrecedence infix operator ===: ComparisonPrecedence infix operator !==: ComparisonPrecedence // FIXME: ~= will be built into the compiler. @@ -447,11 +455,11 @@ infix operator ~=: ComparisonPrecedence // "Conjunctive" -infix operator &&: LogicalConjunctionPrecedence, Bool +infix operator &&: LogicalConjunctionPrecedence // "Disjunctive" -infix operator ||: LogicalDisjunctionPrecedence, Bool +infix operator ||: LogicalDisjunctionPrecedence // User-defined ternary operators are not supported. The ? : operator is // hardcoded as if it had the following attributes: @@ -463,21 +471,21 @@ infix operator ||: LogicalDisjunctionPrecedence, Bool // Compound -infix operator *=: AssignmentPrecedence, Numeric -infix operator &*=: AssignmentPrecedence, FixedWidthInteger -infix operator /=: AssignmentPrecedence, BinaryInteger -infix operator %=: AssignmentPrecedence, BinaryInteger -infix operator +=: AssignmentPrecedence, AdditiveArithmetic, String, Array, Strideable -infix operator &+=: AssignmentPrecedence, FixedWidthInteger -infix operator -=: AssignmentPrecedence, AdditiveArithmetic, Strideable -infix operator &-=: AssignmentPrecedence, FixedWidthInteger -infix operator <<=: AssignmentPrecedence, BinaryInteger -infix operator &<<=: AssignmentPrecedence, FixedWidthInteger -infix operator >>=: AssignmentPrecedence, BinaryInteger -infix operator &>>=: AssignmentPrecedence, FixedWidthInteger -infix operator &=: AssignmentPrecedence, BinaryInteger -infix operator ^=: AssignmentPrecedence, BinaryInteger -infix operator |=: AssignmentPrecedence, BinaryInteger +infix operator *=: AssignmentPrecedence +infix operator &*=: AssignmentPrecedence +infix operator /=: AssignmentPrecedence +infix operator %=: AssignmentPrecedence +infix operator +=: AssignmentPrecedence +infix operator &+=: AssignmentPrecedence +infix operator -=: AssignmentPrecedence +infix operator &-=: AssignmentPrecedence +infix operator <<=: AssignmentPrecedence +infix operator &<<=: AssignmentPrecedence +infix operator >>=: AssignmentPrecedence +infix operator &>>=: AssignmentPrecedence +infix operator &=: AssignmentPrecedence +infix operator ^=: AssignmentPrecedence +infix operator |=: AssignmentPrecedence // Workaround for SubTLF: Default // implementations in protocols. Library authors should ensure diff --git a/stdlib/public/core/Random.swift b/stdlib/public/core/Random.swift index f5d5bed319e16..e293344a28789 100644 --- a/stdlib/public/core/Random.swift +++ b/stdlib/public/core/Random.swift @@ -71,6 +71,7 @@ extension RandomNumberGenerator { // unsigned integer will be used, recursing infinitely and probably blowing // the stack. @available(*, unavailable) + @_alwaysEmitIntoClient public mutating func next() -> UInt64 { fatalError() } /// Returns a value from a uniform, independent distribution of binary data. @@ -103,17 +104,9 @@ extension RandomNumberGenerator { upperBound: T ) -> T { _precondition(upperBound != 0, "upperBound cannot be zero.") -#if arch(i386) || arch(arm) || arch(arm64_32) // TODO(FIXME) SR-10912 - let tmp = (T.max % upperBound) + 1 - let range = tmp == upperBound ? 0 : tmp - var random: T = 0 - - repeat { - random = next() - } while random < range - - return random % upperBound -#else + // We use Lemire's "nearly divisionless" method for generating random + // integers in an interval. For a detailed explanation, see: + // https://arxiv.org/abs/1805.10941 var random: T = next() var m = random.multipliedFullWidth(by: upperBound) if m.low < upperBound { @@ -124,7 +117,6 @@ extension RandomNumberGenerator { } } return m.high -#endif } } diff --git a/stdlib/public/core/RangeReplaceableCollection.swift b/stdlib/public/core/RangeReplaceableCollection.swift index 4281657947ae9..dd9f258698827 100644 --- a/stdlib/public/core/RangeReplaceableCollection.swift +++ b/stdlib/public/core/RangeReplaceableCollection.swift @@ -757,6 +757,23 @@ extension RangeReplaceableCollection { self.replaceSubrange(subrange.relative(to: self), with: newElements) } + // This unavailable default implementation of + // `replaceSubrange(_: Range, with: C)` prevents + // incomplete RangeReplaceableCollection implementations from satisfying + // the protocol through the use of the generic convenience implementation + // `replaceSubrange(_: R, with: C)`, + // If that were the case, at runtime the implementation generic over + // `RangeExpression` would call itself in an infinite recursion + // due to the absence of a better option. + @available(*, unavailable) + @_alwaysEmitIntoClient + public mutating func replaceSubrange( + _ subrange: Range, + with newElements: C + ) where C: Collection, C.Element == Element { + fatalError() + } + /// Removes the elements in the specified subrange from the collection. /// /// All the elements following the specified position are moved to close the diff --git a/stdlib/public/core/ReflectionMirror.swift b/stdlib/public/core/ReflectionMirror.swift index 186e694019a14..2f68b9078aa21 100644 --- a/stdlib/public/core/ReflectionMirror.swift +++ b/stdlib/public/core/ReflectionMirror.swift @@ -300,7 +300,7 @@ public func _forEachField( /// and the `_MetadataKind` of the field's type. /// - Returns: `true` if every invocation of `body` returns `true`; otherwise, /// `false`. -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) +@available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) @discardableResult @_spi(Reflection) public func _forEachFieldWithKeyPath( diff --git a/stdlib/public/core/Sendable.swift b/stdlib/public/core/Sendable.swift index c4242bad2730d..21fe2004661d4 100644 --- a/stdlib/public/core/Sendable.swift +++ b/stdlib/public/core/Sendable.swift @@ -1,14 +1,14 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2021 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 source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// /// The Sendable protocol indicates that value of the given type can /// be safely used in concurrent code. diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index 2e341a91887fb..040b7687fba1d 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -1140,6 +1140,13 @@ extension Sequence { @inlinable public __consuming func _copyContents( initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator,UnsafeMutableBufferPointer.Index) { + return _copySequenceContents(initializing: buffer) + } + + @_alwaysEmitIntoClient + internal __consuming func _copySequenceContents( + initializing buffer: UnsafeMutableBufferPointer ) -> (Iterator,UnsafeMutableBufferPointer.Index) { var it = self.makeIterator() guard var ptr = buffer.baseAddress else { return (it,buffer.startIndex) } diff --git a/stdlib/public/core/Slice.swift b/stdlib/public/core/Slice.swift index db826dc5671a1..5556665a03ca6 100644 --- a/stdlib/public/core/Slice.swift +++ b/stdlib/public/core/Slice.swift @@ -229,6 +229,22 @@ extension Slice: Collection { } } +extension Slice { + @_alwaysEmitIntoClient + public __consuming func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator, UnsafeMutableBufferPointer.Index) { + if let (_, copied) = self.withContiguousStorageIfAvailable({ + $0._copyContents(initializing: buffer) + }) { + let position = index(startIndex, offsetBy: copied) + return (Iterator(_elements: self, _position: position), copied) + } + + return _copySequenceContents(initializing: buffer) + } +} + extension Slice: BidirectionalCollection where Base: BidirectionalCollection { @inlinable // generic-performance public func index(before i: Index) -> Index { diff --git a/stdlib/public/core/SliceBuffer.swift b/stdlib/public/core/SliceBuffer.swift index 6da4a339bb7e4..34e30c1817de5 100644 --- a/stdlib/public/core/SliceBuffer.swift +++ b/stdlib/public/core/SliceBuffer.swift @@ -245,12 +245,18 @@ internal struct _SliceBuffer return target + c } - public __consuming func _copyContents( + @inlinable + internal __consuming func _copyContents( initializing buffer: UnsafeMutableBufferPointer - ) -> (Iterator,UnsafeMutableBufferPointer.Index) { - // This customization point is not implemented for internal types. - // Accidentally calling it would be a catastrophic performance bug. - fatalError("unsupported") + ) -> (Iterator, UnsafeMutableBufferPointer.Index) { + _invariantCheck() + guard buffer.count > 0 else { return (makeIterator(), 0) } + let c = Swift.min(self.count, buffer.count) + buffer.baseAddress!.initialize( + from: firstElementAddress, + count: c) + _fixLifetime(owner) + return (IndexingIterator(_elements: self, _position: startIndex + c), c) } /// True, if the array is native and does not need a deferred type check. diff --git a/stdlib/public/core/SmallString.swift b/stdlib/public/core/SmallString.swift index 1cab614140634..df46b4e8bf449 100644 --- a/stdlib/public/core/SmallString.swift +++ b/stdlib/public/core/SmallString.swift @@ -191,11 +191,17 @@ extension _SmallString: RandomAccessCollection, MutableCollection { @inlinable @inline(__always) internal subscript(_ bounds: Range) -> SubSequence { - // TODO(String performance): In-vector-register operation - return self.withUTF8 { utf8 in - let rebased = UnsafeBufferPointer(rebasing: utf8[bounds]) - return _SmallString(rebased)._unsafelyUnwrappedUnchecked + get { + // TODO(String performance): In-vector-register operation + return self.withUTF8 { utf8 in + let rebased = UnsafeBufferPointer(rebasing: utf8[bounds]) + return _SmallString(rebased)._unsafelyUnwrappedUnchecked + } } + // This setter is required for _SmallString to be a valid MutableCollection. + // Since _SmallString is internal and this setter unused, we cheat. + @_alwaysEmitIntoClient set { fatalError() } + @_alwaysEmitIntoClient _modify { fatalError() } } } diff --git a/stdlib/public/core/Sort.swift b/stdlib/public/core/Sort.swift index 6ac5a4bdade32..c5a6eaa3a0cdb 100644 --- a/stdlib/public/core/Sort.swift +++ b/stdlib/public/core/Sort.swift @@ -400,13 +400,13 @@ internal func _merge( // After moving elements, the storage and buffer look like this, where // `x` is uninitialized memory: // - // Storage: [4, 4, 7, 8, 9, 6, x, x, x, x, x] - // ^ ^ ^ - // srcHigh destLow destHigh (past the end) + // Storage: [4, 4, 7, 8, 9, 16, x, x, x, x, x] + // ^ ^ + // srcHigh/destLow destHigh (past the end) // - // Buffer: [8, 8, 10, 12, 15, x, ...] - // ^ ^ - // bufferLow bufferHigh + // Buffer: [8, 8, 10, 12, 15, x, ...] + // ^ ^ + // bufferLow bufferHigh buffer.moveInitialize(from: mid, count: highCount) bufferHigh = bufferLow + highCount @@ -555,7 +555,7 @@ extension UnsafeMutableBufferPointer { // (a) - for all i in 2.. runs[i - 1].count + runs[i].count // (b) - for c = runs.count - 1: - // - runs[i - 1].count > runs[i].count + // - runs[c - 1].count > runs[c].count // // Loop until the invariant is satisified for the top four elements of // `runs`. Because this method is called for every added run, and only diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index 455aa30817c1a..4e8157a9b8197 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -409,9 +409,8 @@ private func _getCocoaStringPointer( if let ascii = stableCocoaASCIIPointer(cfImmutableValue) { return .ascii(ascii) } - if let utf16Ptr = _stdlib_binary_CFStringGetCharactersPtr(cfImmutableValue) { - return .utf16(utf16Ptr) - } + // We could ask for UTF16 here via _stdlib_binary_CFStringGetCharactersPtr, + // but we currently have no use for it return .none } diff --git a/stdlib/public/core/StringCreate.swift b/stdlib/public/core/StringCreate.swift index d30fd0d52b67d..4ea7b11eb0573 100644 --- a/stdlib/public/core/StringCreate.swift +++ b/stdlib/public/core/StringCreate.swift @@ -120,6 +120,7 @@ extension String { ) return result.asString case .error(let initialRange): + defer { _fixLifetime(result) } //This could be optimized to use excess tail capacity return repairUTF8(result.codeUnits, firstKnownBrokenRange: initialRange) } diff --git a/stdlib/public/core/StringProtocol.swift b/stdlib/public/core/StringProtocol.swift index 9523b3db7f360..eac94b5a6ecb7 100644 --- a/stdlib/public/core/StringProtocol.swift +++ b/stdlib/public/core/StringProtocol.swift @@ -43,7 +43,7 @@ public protocol StringProtocol var unicodeScalars: UnicodeScalarView { get } func hasPrefix(_ prefix: String) -> Bool - func hasSuffix(_ prefix: String) -> Bool + func hasSuffix(_ suffix: String) -> Bool func lowercased() -> String func uppercased() -> String diff --git a/stdlib/public/core/UnicodeScalar.swift b/stdlib/public/core/UnicodeScalar.swift index 6ac2d7106124e..22dc501fedb1b 100644 --- a/stdlib/public/core/UnicodeScalar.swift +++ b/stdlib/public/core/UnicodeScalar.swift @@ -337,8 +337,8 @@ extension Unicode.Scalar { /// } @inlinable public init?(_ v: Int) { - if let us = Unicode.Scalar(UInt32(v)) { - self = us + if let exact = UInt32(exactly: v) { + self.init(exact) } else { return nil } diff --git a/stdlib/public/core/UnsafeBufferPointer.swift.gyb b/stdlib/public/core/UnsafeBufferPointer.swift.gyb index 59372435150a9..a8282f87f973e 100644 --- a/stdlib/public/core/UnsafeBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeBufferPointer.swift.gyb @@ -599,26 +599,8 @@ extension Unsafe${Mutable}BufferPointer { @inlinable // unsafe-performance public static func allocate(capacity count: Int) -> UnsafeMutableBufferPointer { - let size = MemoryLayout.stride * count - // For any alignment <= _minAllocationAlignment, force alignment = 0. - // This forces the runtime's "aligned" allocation path so that - // deallocation does not require the original alignment. - // - // The runtime guarantees: - // - // align == 0 || align > _minAllocationAlignment: - // Runtime uses "aligned allocation". - // - // 0 < align <= _minAllocationAlignment: - // Runtime may use either malloc or "aligned allocation". - var align = Builtin.alignof(Element.self) - if Int(align) <= _minAllocationAlignment() { - align = (0)._builtinWordValue - } - let raw = Builtin.allocRaw(size._builtinWordValue, align) - Builtin.bindMemory(raw, count._builtinWordValue, Element.self) - return UnsafeMutableBufferPointer( - start: UnsafeMutablePointer(raw), count: count) + let base = UnsafeMutablePointer.allocate(capacity: count) + return UnsafeMutableBufferPointer(start: base, count: count) } /// Initializes every element in this buffer's memory to a copy of the given value. diff --git a/stdlib/public/core/UnsafePointer.swift b/stdlib/public/core/UnsafePointer.swift index dcf7e529fd7c4..effc4da6617cc 100644 --- a/stdlib/public/core/UnsafePointer.swift +++ b/stdlib/public/core/UnsafePointer.swift @@ -359,7 +359,7 @@ public struct UnsafePointer: _Pointer, Sendable { /// deinitialized is in an *uninitialized* state. Uninitialized memory must be /// initialized before it can be accessed for reading. /// -/// You can use methods like `initialize(to:count:)`, `initialize(from:count:)`, +/// You can use methods like `initialize(repeating:count:)`, `initialize(from:count:)`, /// and `moveInitialize(from:count:)` to initialize the memory referenced by a /// pointer with a value or series of values. /// @@ -635,7 +635,7 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { /// /// Do not assign an instance of a nontrivial type through `pointee` to /// uninitialized memory. Instead, use an initializing method, such as - /// `initialize(to:count:)`. + /// `initialize(repeating:count:)`. @inlinable // unsafe-performance public var pointee: Pointee { @_transparent unsafeAddress { @@ -961,7 +961,7 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { /// /// Do not assign an instance of a nontrivial type through the subscript to /// uninitialized memory. Instead, use an initializing method, such as - /// `initialize(to:count:)`. + /// `initialize(repeating:count:)`. /// /// - Parameter i: The offset from this pointer at which to access an /// instance, measured in strides of the pointer's `Pointee` type. diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index bc5ce0f0c05ca..c01039cb7b9a6 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -145,6 +145,26 @@ extension Unsafe${Mutable}RawBufferPointer: Sequence { public func makeIterator() -> Iterator { return Iterator(_position: _position, _end: _end) } + + /// Copies the elements of `self` to the memory at `destination.baseAddress`, + /// stopping when either `self` or `destination` is exhausted. + /// + /// - Returns: an iterator over any remaining elements of `self` and the + /// number of elements copied. + @inlinable // unsafe-performance + @_alwaysEmitIntoClient + public func _copyContents( + initializing destination: UnsafeMutableBufferPointer + ) -> (Iterator, UnsafeMutableBufferPointer.Index) { + guard let s = _position, let e = _end, e > s, !destination.isEmpty else { + return (makeIterator(), 0) + } + let destinationAddress = destination.baseAddress._unsafelyUnwrappedUnchecked + let d = UnsafeMutableRawPointer(destinationAddress) + let n = Swift.min(destination.count, self.count) + d.copyMemory(from: s, byteCount: n) + return (Iterator(_position: s.advanced(by: n), _end: e), n) + } } extension Unsafe${Mutable}RawBufferPointer: ${Mutable}Collection { @@ -263,15 +283,21 @@ extension Unsafe${Mutable}RawBufferPointer: RandomAccessCollection { } extension Unsafe${Mutable}RawBufferPointer { % if mutable: - /// Returns a newly allocated buffer with the given size, in bytes. + /// Allocates uninitialized memory with the specified size and alignment. /// - /// The memory referenced by the new buffer is allocated, but not - /// initialized. + /// You are in charge of managing the allocated memory. Be sure to deallocate + /// any memory that you manually allocate. + /// + /// The allocated memory is not bound to any specific type and must be bound + /// before performing any typed operations. If you are using the memory for + /// a specific type, allocate memory using the + /// `UnsafeMutablePointerBuffer.allocate(capacity:)` static method instead. /// /// - Parameters: - /// - byteCount: The number of bytes to allocate. + /// - byteCount: The number of bytes to allocate. `byteCount` must not be + /// negative. /// - alignment: The alignment of the new region of allocated memory, in - /// bytes. + /// bytes. `alignment` must be a whole power of 2. /// - Returns: A buffer pointer to a newly allocated region of memory aligned /// to `alignment`. @inlinable @@ -412,6 +438,16 @@ extension Unsafe${Mutable}RawBufferPointer { guard let position = _position else { return } + + if source.withContiguousStorageIfAvailable({ + (buffer: UnsafeBufferPointer) -> Void in + if let base = buffer.baseAddress { + position.copyMemory(from: base, byteCount: buffer.count) + } + }) != nil { + return + } + for (index, byteValue) in source.enumerated() { position.storeBytes( of: byteValue, toByteOffset: index, as: UInt8.self) diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index a15958fc381de..8bb87ea5ed6e4 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -600,7 +600,7 @@ public struct UnsafeMutableRawPointer: _Pointer, Sendable { /// - Parameters: /// - byteCount: The number of bytes to allocate. `byteCount` must not be negative. /// - alignment: The alignment of the new region of allocated memory, in - /// bytes. + /// bytes. `alignment` must be a whole power of 2. /// - Returns: A pointer to a newly allocated region of memory. The memory is /// allocated, but not initialized. @inlinable diff --git a/stdlib/public/runtime/AutoDiffSupport.h b/stdlib/public/runtime/AutoDiffSupport.h index 7df152779e5ee..960ca8daa3ac5 100644 --- a/stdlib/public/runtime/AutoDiffSupport.h +++ b/stdlib/public/runtime/AutoDiffSupport.h @@ -39,16 +39,16 @@ class AutoDiffLinearMapContext : public HeapObject { }; /// Creates a linear map context with a tail-allocated top-level subcontext. -SWIFT_EXPORT_FROM(swift_Differentiation) SWIFT_CC(swift) +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) AutoDiffLinearMapContext *swift_autoDiffCreateLinearMapContext( size_t topLevelSubcontextSize); /// Returns the address of the tail-allocated top-level subcontext. -SWIFT_EXPORT_FROM(swift_Differentiation) SWIFT_CC(swift) +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) void *swift_autoDiffProjectTopLevelSubcontext(AutoDiffLinearMapContext *); /// Allocates memory for a new subcontext. -SWIFT_EXPORT_FROM(swift_Differentiation) SWIFT_CC(swift) +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) void *swift_autoDiffAllocateSubcontext(AutoDiffLinearMapContext *, size_t size); } diff --git a/stdlib/public/runtime/Bincompat.cpp b/stdlib/public/runtime/Bincompat.cpp index 9370431b0550f..10438ccc76502 100644 --- a/stdlib/public/runtime/Bincompat.cpp +++ b/stdlib/public/runtime/Bincompat.cpp @@ -18,7 +18,7 @@ #include // If this is an Apple OS, use the Apple binary compatibility rules -#if __has_include() +#if __has_include() && defined(SWIFT_RUNTIME_OS_VERSIONING) #include #ifndef BINARY_COMPATIBILITY_APPLE #define BINARY_COMPATIBILITY_APPLE 1 diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 70f8d338775e0..fdb5472a6d144 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -45,6 +45,7 @@ set(swift_runtime_sources ExistentialContainer.cpp Float16Support.cpp FoundationSupport.cpp + FunctionReplacement.cpp Heap.cpp HeapObject.cpp ImageInspectionCommon.cpp @@ -65,7 +66,8 @@ set(swift_runtime_sources RefCount.cpp ReflectionMirror.cpp RuntimeInvocationsTracking.cpp - SwiftDtoa.cpp) + SwiftDtoa.cpp + SwiftTLSContext.cpp) # Acknowledge that the following sources are known. set(LLVM_OPTIONAL_SOURCES diff --git a/stdlib/public/runtime/ConcurrencyExclusivity.inc b/stdlib/public/runtime/ConcurrencyExclusivity.inc new file mode 100644 index 0000000000000..da421f6328161 --- /dev/null +++ b/stdlib/public/runtime/ConcurrencyExclusivity.inc @@ -0,0 +1,450 @@ +//===--- ConcurrencyExclusivity.cpp - Exclusivity tracking for concurrency-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 implements the runtime support for dynamically tracking exclusivity +// in tasks. +// +//===----------------------------------------------------------------------===// + +namespace { + +/// High Level Algorithm Description +/// -------------------------------- +/// +/// With the introduction of Concurrency, we add additional requirements to our +/// exclusivity model: +/// +/// * We want tasks to have a consistent exclusivity access set across +/// suspensions/resumptions. This ensures that any exclusive accesses began +/// before a Task suspended are properly flagged after the Task is resumed +/// even if the Task is resumed on a different thread. +/// +/// * If a synchronous code calls a subroutine that creates a set of tasks to +/// perform work and then blocks, we want the runtime to ensure that the tasks +/// respect exclusivity accesses from the outside synchronous code. +/// +/// * We on purpose define exclusive access to the memory from multiple tasks as +/// undefined behavior since that would be an additional feature that needs to +/// be specifically designed in the future. +/// +/// * We assume that an access in synchronous code will never be ended in +/// asynchronous code. +/// +/// * We additional require that our design leaves the exclusivity runtime +/// unaware of any work we are doing here. All it should be aware of is the +/// current thread local access set and adding/removing from that access set. +/// +/// We implement these requirements by reserving two pointers in each Task. The +/// first pointer points at the head access of the linked list of accesses of +/// the Task and the second pointer points at the end of the linked list of +/// accesses of the task. We will for the discussion ahead call the first +/// pointer TaskFirstAccess and the second TaskLastAccess. This allows us to +/// modify the current TLV single linked list to include/remove the tasks’s +/// access by updating a few nodes in the linked list when the task is running +/// and serialize the task’s current access set and restoring to be head the +/// original synchronous access set head when the task is running. This +/// naturally fits a push/pop access set sort of schema where every time a task +/// starts, we push its access set onto the local TLV and then pop it off when +/// the task is suspended. This ensures that the task gets the current +/// synchronous set of accesses and other Tasks do not see the accesses of the +/// specific task providing task isolation. +/// +/// The cases can be described via the following table: +/// +/// +------+--------------------+--------------------+--------------------+ +/// | Case | Live Task Accesses | Live Sync Accesses | Live Task Accesses | +/// | | When Push | When Push | When Pop | +/// |------+--------------------+--------------------+--------------------| +/// | 1 | F | F | F | +/// | 2 | F | F | T | +/// | 3 | F | T | F | +/// | 4 | F | T | T | +/// | 5 | T | F | F | +/// | 6 | T | F | T | +/// | 7 | T | T | F | +/// | 8 | T | T | T | +/// +------+--------------------+--------------------+--------------------+ +/// +/// We mark the end of each title below introducing a case with 3 T/F to enable +/// easy visual matching with the chart +/// +/// Case 1: Task/Sync do not have initial accesses and no Task accesses are +/// created while running (F,F,F) +/// +/// In this case, TBegin and TEnd are both initially nullptr. +/// +/// When we push, we see that the current exclusivity TLV has a null head and +/// leave it so. We set TBegin and TEnd as nullptr while running. +/// +/// When we pop, see that the exclusivity TLV is still nullptr, so we just leave +/// TBegin and TEnd alone still as nullptr. +/// +/// This means that code that does not have any exclusive accesses do not have +/// any runtime impact. +/// +/// Case 2: Task/Sync do not have initial access, but Task accesses are created +/// while running (F, F, T) +/// +/// In this case, TBegin and TEnd are both initially nullptr. +/// +/// When we push, we see that the current exclusivity TLV has a null head. So, +/// we leave TBegin and TEnd as nullptr while the task is running. +/// +/// When we pop, we see that the exclusivity TLV has a non-null head. In that +/// case, we walk the list to find the last node and update TBegin to point at +/// the current head, TEnd to point at that last node, and then set the TLV head +/// to be nullptr. +/// +/// Case 3: Task does not have initial accesses, but Sync does, and new Task +/// accesses are not created while running (F, T, F) +/// +/// In this case, TBegin and TEnd are both initially nullptr. +/// +/// When we push, we look at the TLV and see our initial synchronous thread was +/// tracking accesses. In this case, we leave the TLV pointing at the +/// SyncAccessHead and set TBegin to SyncAccessHead and leave TEnd as nullptr. +/// +/// When we pop, we see that TBegin (which we know has the old synchronous head +/// in it) is equal to the TLV so we know that we did not create any new Task +/// accesses. Then we set TBegin to nullptr and return. NOTE: TEnd is nullptr +/// the entire time in this scenario. +/// +/// Case 4: Task does not have initial accesses, but Sync does, and new Task +/// accesses are created while running (F, T, T) +/// +/// In this case, TBegin and TEnd are both initially nullptr. When we push, we +/// look at the TLV and we see our initial synchronous thread was tracking +/// accesses. In this case, we leave the TLV pointing at the SyncAccessHead and +/// set TBegin to SyncAccessHead and leave TEnd as nullptr. +/// +/// When we pop, we see that the TLV and TBegin differ now. We know that this +/// means that our task introduced new accesses. So, we search down from the +/// head of the AccessSet TLV until we find TBegin . The node before TBegin is +/// our new TEnd pointer. We set TBegin to then have the value of head, TEnd to +/// be the new TEnd pointer, set TEnd’s next to be nullptr and make head the old +/// value of TBegin. +/// +/// Case 5: Task has an initial access set, but Sync does not have initial +/// accesses and no Task accesses exist after running (T,F,F) +/// +/// In this case, TBegin and TEnd are both initially set to non-null values. +/// When we push, we look at the current TLV head and see that the TLV head is +/// nullptr. We then set TLV head to be TBegin and set TBegin to be nullptr to +/// signal the original synchronous TLV head was nullptr. +/// +/// When we pop, we see that TBegin is currently nullptr, so we know the +/// synchronous access set was empty. We also know that despite us starting with +/// a task access set, those accesses must have completed while the task was +/// running since the access set is empty when we pop. +/// +/// Case 6: Task has initial accesses, sync does not have initial accesss, and +/// Task access set is modified while running (T, F, T) +/// +/// In this case, TBegin and TEnd are both initially set to non-null +/// values. When we push, we look at the current TLV head and see that the TLV +/// head is nullptr. We then set TLV head to be TBegin and set TBegin to be +/// nullptr to signal the original synchronous TLV head was nullptr. We have no +/// requirement on TEnd now in this case but set it to nullptr, to track flags +/// if we want to in the future in a different runtime. +/// +/// When we pop, we see that TBegin is currently nullptr, so we know the +/// synchronous access set was empty. We do not have a way to know how/if we +/// modified the Task AccessSet, so we walked the list to find the last node. We +/// then make TBegin head, TEnd the last node, and set the TLV to be nullptr +/// again. +/// +/// Case 7: Task has initial accesses, Sync has initial accesses, and new Task +/// accesses are not created while running (T, T, F) +/// +/// In this case, TBegin and TEnd are both initially set to non-null values. +/// When we push, we look at the current TLV head and see that the TLV head is a +/// valid pointer. We then set TLV head to be the current value of TBegin, make +/// TEnd->next the old head value and stash the old head value into TBegin. We +/// have no requirement on TEnd now in this case. +/// +/// When we pop, we see that TBegin is not nullptr, so we know the synchronous +/// access set had live accesses. We do not have a way to know how/if we +/// modified the Task AccessSet, so we walked the list to find TBegin (which is +/// old sync head). Noting that the predecessor node of old sync head’s node +/// will be the end of the task’s current access set, we set TLV to point at the +/// node we found in TBegin, set TBegin to the current TLV head, set TEnd to +/// that predecessor node of the current TLV head and set TEnd->next to be +/// nullptr. +/// +/// Case 8: Task has initial accesses, Sync does, and Task accesses is modified +/// while running (T, T, T) +/// +/// In this case, TBegin and TEnd are both initially set to non-null values. +/// +/// When we push, we look at the current TLV head and see that the TLV head is +/// a valid pointer. We then set TLV head to be the current value of TBegin, +/// make TEnd->next the old head value and stash the old head value into +/// TBegin. We have no requirement on TEnd now in this case. +/// +/// When we pop, we see that TBegin is not nullptr, so we know the synchronous +/// access set had live accesses. We do not have a way to know how/if we +/// modified the Task AccessSet, so we walked the list to find TBegin (which is +/// old sync head). Noting that the predecessor node of old sync head’s node +/// will be the end of the task’s current access set, we set TLV to point at +/// the node we found in TBegin, set TBegin to the current TLV head, set TEnd +/// to that predecessor node of the current TLV head and set TEnd->next to be +/// nullptr. +struct SwiftTaskThreadLocalContext { + uintptr_t state[2]; + +#ifndef NDEBUG + void dump() { + fprintf(stderr, + " SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): " + "(%p, %p)\n", + (void *)state[0], (void *)state[1]); + } +#endif + + bool hasInitialAccessSet() const { + // If state[0] is nullptr, we have an initial access set. + return bool(state[0]); + } + + Access *getTaskAccessSetHead() const { + return reinterpret_cast(state[0]); + } + + Access *getTaskAccessSetTail() const { + return reinterpret_cast(state[1]); + } + + void setTaskAccessSetHead(Access *newHead) { state[0] = uintptr_t(newHead); } + + void setTaskAccessSetTail(Access *newTail) { state[1] = uintptr_t(newTail); } + +#ifndef NDEBUG + const char *getTaskAddress() const { + // Constant only used when we have an asserts compiler so that we can output + // exactly the header location of the task for FileCheck purposes. + // + // WARNING: This test will fail if the Task ABI changes. When that happens, + // update the offset! + // + // TODO: This probably will need 32 bit help. +#if __POINTER_WIDTH__ == 64 + unsigned taskHeadOffsetFromTaskAccessSet = 128; +#else + unsigned taskHeadOffsetFromTaskAccessSet = 68; +#endif + auto *self = reinterpret_cast(this); + return self - taskHeadOffsetFromTaskAccessSet; + } +#endif +}; + +} // end anonymous namespace + +// See algorithm description on SwiftTaskThreadLocalContext. +void swift::swift_task_enterThreadLocalContext(char *state) { + auto &taskCtx = *reinterpret_cast(state); + auto &tlsCtxAccessSet = SwiftTLSContext::get().accessSet; + +#ifndef NDEBUG + if (isExclusivityLoggingEnabled()) { + withLoggingLock([&]() { + fprintf(stderr, + "Entering Thread Local Context. Before Swizzle. Task: %p\n", + taskCtx.getTaskAddress()); + taskCtx.dump(); + swift_dumpTrackedAccesses(); + }); + } + + auto logEndState = [&] { + if (isExclusivityLoggingEnabled()) { + withLoggingLock([&]() { + fprintf(stderr, + "Entering Thread Local Context. After Swizzle. Task: %p\n", + taskCtx.getTaskAddress()); + taskCtx.dump(); + swift_dumpTrackedAccesses(); + }); + } + }; +#else + // Just a no-op that should inline away. + auto logEndState = [] {}; +#endif + + // First handle all of the cases where our task does not start without an + // initial access set. + // + // Handles push cases 1-4. + if (!taskCtx.hasInitialAccessSet()) { + // In this case, the current synchronous context is not tracking any + // accesses. So the tlsCtx and our initial access set are all nullptr, so we + // can just return early. + // + // Handles push cases 1-2. + if (!tlsCtxAccessSet) { + logEndState(); + return; + } + + // Ok, our task isn't tracking any task specific accesses, but our tlsCtx + // was tracking accesses. Leave the tlsCtx alone at this point and set our + // state's begin access to be tlsCtx head. We leave our access set tail as + // nullptr. + // + // Handles push cases 3-4. + taskCtx.setTaskAccessSetHead(tlsCtxAccessSet.getHead()); + logEndState(); + return; + } + + // At this point, we know that we did have an initial access set. Both access + // set pointers are valid. + // + // Handles push cases 5-8. + + // Now check if our synchronous code had any accesses. If not, we set TBegin, + // TEnd to be nullptr and set the tlsCtx to point to TBegin. + // + // Handles push cases 5-6. + if (!bool(tlsCtxAccessSet)) { + tlsCtxAccessSet = taskCtx.getTaskAccessSetHead(); + taskCtx.setTaskAccessSetHead(nullptr); + taskCtx.setTaskAccessSetTail(nullptr); + logEndState(); + return; + } + + // In this final case, we found that our task had its own access set and our + // tlsCtx did as well. So we then set the Task's head to be the new TLV head, + // set tail->next to point at old head and stash oldhead into the task ctx. + // + // Handles push cases 7-8. + auto *oldHead = tlsCtxAccessSet.getHead(); + auto *tail = taskCtx.getTaskAccessSetTail(); + + tlsCtxAccessSet.setHead(taskCtx.getTaskAccessSetHead()); + tail->setNext(oldHead); + taskCtx.setTaskAccessSetHead(oldHead); + taskCtx.setTaskAccessSetTail(nullptr); + logEndState(); +} + +// See algorithm description on SwiftTaskThreadLocalContext. +void swift::swift_task_exitThreadLocalContext(char *state) { + auto &taskCtx = *reinterpret_cast(state); + auto &tlsCtxAccessSet = SwiftTLSContext::get().accessSet; + +#ifndef NDEBUG + if (isExclusivityLoggingEnabled()) { + withLoggingLock([&]() { + fprintf(stderr, + "Exiting Thread Local Context. Before Swizzle. Task: %p\n", + taskCtx.getTaskAddress()); + taskCtx.dump(); + swift_dumpTrackedAccesses(); + }); + } + + auto logEndState = [&] { + if (isExclusivityLoggingEnabled()) { + withLoggingLock([&]() { + fprintf(stderr, + "Exiting Thread Local Context. After Swizzle. Task: %p\n", + taskCtx.getTaskAddress()); + taskCtx.dump(); + swift_dumpTrackedAccesses(); + }); + } + }; +#else + // If we are not compiling with asserts, just use a simple identity function + // that should be inlined away. + // + // TODO: Can we use defer in the runtime? + auto logEndState = [] {}; +#endif + + // First check our ctx to see if we were tracking a previous synchronous + // head. If we don't then we know that our synchronous thread was not + // initially tracking any accesses. + // + // Handles pop cases 1,2,5,6 + Access *oldHead = taskCtx.getTaskAccessSetHead(); + if (!oldHead) { + // Then check if we are currently tracking an access set in the TLS. If we + // aren't, then we know that either we did not start with a task specific + // access set /or/ we did start but all of those accesses ended while the + // task was running. In either case, when we pushed initially, we set + // TBegin, TEnd to be nullptr already and since oldHead is already nullptr, + // we can just exit. + // + // Handles pop cases 1,5 + if (!tlsCtxAccessSet) { + assert(taskCtx.getTaskAccessSetTail() == nullptr && + "Make sure we set this to nullptr when we pushed"); + logEndState(); + return; + } + + // In this case, we did find that we had live accesses. Since we know we + // did not start with any synchronous accesses, these accesses must all be + // from the given task. So, we first find the tail of the current TLS linked + // list, then set the Task access set head to accessSet, the Task accessSet + // tail to the TLS linked list tail and set tlsCtx.accessSet to nullptr. + // + // Handles pop cases 2,6 + auto *newHead = tlsCtxAccessSet.getHead(); + auto *newTail = tlsCtxAccessSet.getTail(); + assert(newTail && "Failed to find tail?!"); + tlsCtxAccessSet = nullptr; + taskCtx.setTaskAccessSetHead(newHead); + taskCtx.setTaskAccessSetTail(newTail); + logEndState(); + return; + } + + // Otherwise, we know that we /were/ tracking accesses from a previous + // synchronous context. So we need to unmerge our task specific state from the + // exclusivity access set. + // + // Handles pop cases 3,4,7,8. + + // First check if the current head tlsAccess is the same as our oldHead. In + // such a case, we do not have new task accesses to update. So just set task + // access head/tail to nullptr. The end access should be nullptr. + // + // Handles pop cases 3. + if (tlsCtxAccessSet.getHead() == oldHead) { + taskCtx.setTaskAccessSetHead(nullptr); + taskCtx.setTaskAccessSetTail(nullptr); + logEndState(); + return; + } + + // Otherwise, we have task specific accesses that we need to serialize into + // the task's state. We currently can not tell if the Task actually modified + // the task list beyond if the task list is empty. So we have to handle case 7 + // here (unfortunately). + // + // NOTE: If we could tell if the Task modified its access set while running, + // we could perhaps avoid the search for newEnd. + // + // Handles pop cases 4,7,8. + auto *newHead = tlsCtxAccessSet.getHead(); + auto *newEnd = tlsCtxAccessSet.findParentAccess(oldHead); + tlsCtxAccessSet.setHead(oldHead); + newEnd->setNext(nullptr); + taskCtx.setTaskAccessSetHead(newHead); + taskCtx.setTaskAccessSetTail(newEnd); + logEndState(); +} diff --git a/stdlib/public/runtime/Demangle.cpp b/stdlib/public/runtime/Demangle.cpp index 2b14ea7022765..7d3c15a072aa4 100644 --- a/stdlib/public/runtime/Demangle.cpp +++ b/stdlib/public/runtime/Demangle.cpp @@ -499,6 +499,9 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type, wrapInput(Node::Kind::Owned); break; } + if (flags.isIsolated()) { + wrapInput(Node::Kind::Isolated); + } inputs.push_back({input, flags.isVariadic()}); } @@ -564,6 +567,14 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type, result->addChild(resultTy, Dem); auto funcNode = Dem.createNode(kind); + if (func->hasGlobalActor()) { + auto globalActorTypeNode = + _swift_buildDemanglingForMetadata(func->getGlobalActor(), Dem); + NodePointer globalActorNode = + Dem.createNode(Node::Kind::GlobalActorFunctionType); + globalActorNode->addChild(globalActorTypeNode, Dem); + funcNode->addChild(globalActorNode, Dem); + } switch (func->getDifferentiabilityKind().Value) { case FunctionMetadataDifferentiabilityKind::NonDifferentiable: break; diff --git a/stdlib/public/runtime/DynamicCast.cpp b/stdlib/public/runtime/DynamicCast.cpp index a3e6e1b40edc0..2fda966fef140 100644 --- a/stdlib/public/runtime/DynamicCast.cpp +++ b/stdlib/public/runtime/DynamicCast.cpp @@ -1514,41 +1514,62 @@ tryCastToClassExistentialViaSwiftValue( auto destExistentialLocation = reinterpret_cast(destLocation); - // Fail if the target has constraints that make it unsuitable for - // a __SwiftValue box. - // FIXME: We should not have different checks here for - // Obj-C vs non-Obj-C. The _SwiftValue boxes should conform - // to the exact same protocols on both platforms. - bool destIsConstrained = destExistentialType->NumProtocols != 0; - if (destIsConstrained) { -#if SWIFT_OBJC_INTEROP // __SwiftValue is an Obj-C class - if (!findSwiftValueConformances( - destExistentialType, destExistentialLocation->getWitnessTables())) { - return DynamicCastResult::Failure; - } -#else // __SwiftValue is a native class - if (!swift_swiftValueConformsTo(destType, destType)) { + switch (srcType->getKind()) { + case MetadataKind::Class: + case MetadataKind::ObjCClassWrapper: + case MetadataKind::ForeignClass: + // Class references always go directly into + // class existentials; it makes no sense to wrap them. + return DynamicCastResult::Failure; + + case MetadataKind::Metatype: { +#if SWIFT_OBJC_INTEROP + auto metatypePtr = reinterpret_cast(srcValue); + auto metatype = *metatypePtr; + switch (metatype->getKind()) { + case MetadataKind::Class: + case MetadataKind::ObjCClassWrapper: + case MetadataKind::ForeignClass: + // Exclude class metatypes on Darwin, since those are object types and can + // be stored directly. return DynamicCastResult::Failure; + default: + break; } #endif + // Non-class metatypes are never objects, and + // metatypes on non-Darwin are never objects, so + // fall through to box those. + SWIFT_FALLTHROUGH; } + default: { + if (destExistentialType->NumProtocols != 0) { + // The destination is a class-constrained protocol type + // and the source is not a class, so.... + return DynamicCastResult::Failure; + } else { + // This is a simple (unconstrained) `AnyObject` so we can populate + // it by stuffing a non-class instance into a __SwiftValue box #if SWIFT_OBJC_INTEROP - auto object = bridgeAnythingToSwiftValueObject( - srcValue, srcType, takeOnSuccess); - destExistentialLocation->Value = object; - if (takeOnSuccess) { - return DynamicCastResult::SuccessViaTake; - } else { - return DynamicCastResult::SuccessViaCopy; - } + auto object = bridgeAnythingToSwiftValueObject( + srcValue, srcType, takeOnSuccess); + destExistentialLocation->Value = object; + if (takeOnSuccess) { + return DynamicCastResult::SuccessViaTake; + } else { + return DynamicCastResult::SuccessViaCopy; + } # else - // Note: Code below works correctly on both Obj-C and non-Obj-C platforms, - // but the code above is slightly faster on Obj-C platforms. - auto object = _bridgeAnythingToObjectiveC(srcValue, srcType); - destExistentialLocation->Value = object; - return DynamicCastResult::SuccessViaCopy; + // Note: Code below works correctly on both Obj-C and non-Obj-C platforms, + // but the code above is slightly faster on Obj-C platforms. + auto object = _bridgeAnythingToObjectiveC(srcValue, srcType); + destExistentialLocation->Value = object; + return DynamicCastResult::SuccessViaCopy; #endif + } + } + } } static DynamicCastResult diff --git a/stdlib/public/runtime/Enum.cpp b/stdlib/public/runtime/Enum.cpp index a692afbb9d33b..5d5099cd24d32 100644 --- a/stdlib/public/runtime/Enum.cpp +++ b/stdlib/public/runtime/Enum.cpp @@ -148,14 +148,6 @@ void swift::swift_storeEnumTagSinglePayloadGeneric(OpaqueValue *value, numExtraInhabitants, storeExtraInhabitantTag); } -static uint32_t getMultiPayloadEnumTagSinglePayload(const OpaqueValue *value, - uint32_t numExtraCases, - const Metadata *enumType); -static void storeMultiPayloadEnumTagSinglePayload(OpaqueValue *value, - uint32_t index, - uint32_t numExtraCases, - const Metadata *enumType); - void swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType, EnumLayoutFlags layoutFlags, @@ -209,8 +201,9 @@ swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType, // Unconditionally overwrite the enum-tag witnesses. // The compiler does not generate meaningful enum-tag witnesses for // enums in this state. - vwtable->getEnumTagSinglePayload = getMultiPayloadEnumTagSinglePayload; - vwtable->storeEnumTagSinglePayload = storeMultiPayloadEnumTagSinglePayload; + vwtable->getEnumTagSinglePayload = swift_getMultiPayloadEnumTagSinglePayload; + vwtable->storeEnumTagSinglePayload = + swift_storeMultiPayloadEnumTagSinglePayload; vwtable->publishLayout(layout); } @@ -287,19 +280,19 @@ static void storeMultiPayloadExtraInhabitantTag(OpaqueValue *value, storeMultiPayloadTag(value, layout, ~(tag - 1)); } -static uint32_t getMultiPayloadEnumTagSinglePayload(const OpaqueValue *value, - uint32_t numExtraCases, - const Metadata *enumType) { +uint32_t +swift::swift_getMultiPayloadEnumTagSinglePayload(const OpaqueValue *value, + uint32_t numExtraCases, + const Metadata *enumType) { return getEnumTagSinglePayloadImpl(value, numExtraCases, enumType, enumType->vw_size(), enumType->vw_getNumExtraInhabitants(), getMultiPayloadExtraInhabitantTag); } -static void storeMultiPayloadEnumTagSinglePayload(OpaqueValue *value, - uint32_t index, - uint32_t numExtraCases, - const Metadata *enumType) { +void swift::swift_storeMultiPayloadEnumTagSinglePayload( + OpaqueValue *value, uint32_t index, uint32_t numExtraCases, + const Metadata *enumType) { storeEnumTagSinglePayloadImpl(value, index, numExtraCases, enumType, enumType->vw_size(), enumType->vw_getNumExtraInhabitants(), diff --git a/stdlib/public/runtime/EnvironmentVariables.cpp b/stdlib/public/runtime/EnvironmentVariables.cpp index f9106a08b937b..ae8f642adc22c 100644 --- a/stdlib/public/runtime/EnvironmentVariables.cpp +++ b/stdlib/public/runtime/EnvironmentVariables.cpp @@ -122,9 +122,19 @@ OnceToken_t swift::runtime::environment::initializeToken; extern "C" char **environ; #define ENVIRON environ #elif defined(_WIN32) +// `_environ` is DLL-imported unless we are linking against the static C runtime +// (via `/MT` or `/MTd`). +#if defined(_DLL) +extern "C" __declspec(dllimport) char **_environ; +#else extern "C" char **_environ; +#endif +// `_environ` is unavailable in the Windows Runtime environment. +// https://docs.microsoft.com/en-us/cpp/c-runtime-library/environ-wenviron?view=msvc-160 +#if !defined(_WINRT_DLL) #define ENVIRON _environ #endif +#endif #ifdef ENVIRON void swift::runtime::environment::initialize(void *context) { diff --git a/stdlib/public/runtime/EnvironmentVariables.def b/stdlib/public/runtime/EnvironmentVariables.def index 10a4645033c76..e9d27afcfe3e2 100644 --- a/stdlib/public/runtime/EnvironmentVariables.def +++ b/stdlib/public/runtime/EnvironmentVariables.def @@ -62,4 +62,11 @@ VARIABLE(SWIFT_DEBUG_ENABLE_SHARED_CACHE_PROTOCOL_CONFORMANCES, bool, true, #endif +#ifndef NDEBUG + +VARIABLE(SWIFT_DEBUG_RUNTIME_EXCLUSIVITY_LOGGING, bool, false, + "Enable the an asserts runtime to emit logging as it works.") + +#endif + #undef VARIABLE diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index b5a5e467388cb..803ac10f9c52c 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -92,20 +92,18 @@ static bool getSymbolNameAddr(llvm::StringRef libraryName, // providing failure status instead of just returning the original string like // swift demangle. #if defined(_WIN32) - DWORD dwFlags = UNDNAME_COMPLETE; -#if !defined(_WIN64) - dwFlags |= UNDNAME_32_BIT_DECODE; -#endif - static std::mutex mutex; + static StaticMutex mutex; char szUndName[1024]; - DWORD dwResult; + DWORD dwResult = mutex.withLock([&syminfo, &szUndName]() { + DWORD dwFlags = UNDNAME_COMPLETE; +#if !defined(_WIN64) + dwFlags |= UNDNAME_32_BIT_DECODE; +#endif - { - std::lock_guard lock(mutex); - dwResult = UnDecorateSymbolName(syminfo.symbolName.get(), szUndName, - sizeof(szUndName), dwFlags); - } + return UnDecorateSymbolName(syminfo.symbolName.get(), szUndName, + sizeof(szUndName), dwFlags); + }); if (dwResult == TRUE) { symbolName += szUndName; diff --git a/stdlib/public/runtime/Exclusivity.cpp b/stdlib/public/runtime/Exclusivity.cpp index 7750fbae41e8b..0a6630108b726 100644 --- a/stdlib/public/runtime/Exclusivity.cpp +++ b/stdlib/public/runtime/Exclusivity.cpp @@ -16,6 +16,7 @@ // NOTE: This should really be applied in the CMakeLists.txt. However, we do // not have a way to currently specify that at the target specific level yet. + #if defined(_WIN32) #define NOMINMAX #define WIN32_LEAN_AND_MEAN @@ -24,14 +25,16 @@ #include "swift/Runtime/Exclusivity.h" #include "../SwiftShims/Visibility.h" -#include "ThreadLocalStorage.h" +#include "SwiftTLSContext.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Config.h" #include "swift/Runtime/Debug.h" +#include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/Metadata.h" +#include "swift/Runtime/ThreadLocalStorage.h" +#include +#include #include -#include -#include // Pick a return-address strategy #if __GNUC__ @@ -45,6 +48,7 @@ #endif using namespace swift; +using namespace swift::runtime; bool swift::_swift_disableExclusivityChecking = false; @@ -56,6 +60,49 @@ static const char *getAccessName(ExclusivityFlags flags) { } } +// In asserts builds if the environment variable +// SWIFT_DEBUG_RUNTIME_EXCLUSIVITY_LOGGING is set, emit logging information. +#ifndef NDEBUG + +static inline bool isExclusivityLoggingEnabled() { + return runtime::environment::SWIFT_DEBUG_RUNTIME_EXCLUSIVITY_LOGGING(); +} + +static inline void _flockfile_stderr() { +#if defined(_WIN32) + _lock_file(stderr); +#elif defined(__wasi__) + // WebAssembly/WASI doesn't support file locking yet + // https://bugs.swift.org/browse/SR-12097 +#else + flockfile(stderr); +#endif +} + +static inline void _funlockfile_stderr() { +#if defined(_WIN32) + _unlock_file(stderr); +#elif defined(__wasi__) + // WebAssembly/WASI doesn't support file locking yet + // https://bugs.swift.org/browse/SR-12097 +#else + funlockfile(stderr); +#endif +} + +/// Used to ensure that logging printfs are deterministic. +static inline void withLoggingLock(std::function func) { + assert(isExclusivityLoggingEnabled() && + "Should only be called if exclusivity logging is enabled!"); + + _flockfile_stderr(); + func(); + fflush(stderr); + _funlockfile_stderr(); +} + +#endif + SWIFT_ALWAYS_INLINE static void reportExclusivityConflict(ExclusivityFlags oldAction, void *oldPC, ExclusivityFlags newFlags, void *newPC, @@ -105,217 +152,95 @@ static void reportExclusivityConflict(ExclusivityFlags oldAction, void *oldPC, _swift_reportToDebugger(RuntimeErrorFlagFatal, message, &details); } -namespace { - -/// A single access that we're tracking. -/// -/// The following inputs are accepted by the begin_access runtime entry -/// point. This table show the action performed by the current runtime to -/// convert those inputs into stored fields in the Access scratch buffer. -/// -/// Pointer | Runtime | Access | PC | Reported| Access -/// Argument| Behavior | Pointer| Arg | PC | PC -/// -------- ------------- -------- ------- --------- ---------- -/// null | [trap or missing enforcement] -/// nonnull | [nontracked]| null | null | caller | [discard] -/// nonnull | [nontracked]| null | valid | | [discard] -/// nonnull | [tracked] | | null | caller | caller -/// nonnull | [tracked] | | valid | | -/// -/// [nontracked] means that the Access scratch buffer will not be added to the -/// runtime's list of tracked accesses. However, it may be passed to a -/// subsequent call to end_unpaired_access. The null Pointer field then -/// identifies the Access record as nontracked. -/// -/// The runtime owns the contents of the scratch buffer, which is allocated by -/// the compiler but otherwise opaque. The runtime may later reuse the Pointer -/// or PC fields or any spare bits for additional flags, and/or a pointer to -/// out-of-line storage. -struct Access { - void *Pointer; - void *PC; - uintptr_t NextAndAction; - - enum : uintptr_t { - ActionMask = (uintptr_t)ExclusivityFlags::ActionMask, - NextMask = ~ActionMask - }; - - Access *getNext() const { - return reinterpret_cast(NextAndAction & NextMask); - } - - void setNext(Access *next) { - NextAndAction = - reinterpret_cast(next) | (NextAndAction & ActionMask); - } - - ExclusivityFlags getAccessAction() const { - return ExclusivityFlags(NextAndAction & ActionMask); - } - - void initialize(void *pc, void *pointer, Access *next, - ExclusivityFlags action) { - Pointer = pointer; - PC = pc; - NextAndAction = reinterpret_cast(next) | uintptr_t(action); +bool AccessSet::insert(Access *access, void *pc, void *pointer, + ExclusivityFlags flags) { +#ifndef NDEBUG + if (isExclusivityLoggingEnabled()) { + withLoggingLock( + [&]() { fprintf(stderr, "Inserting new access: %p\n", access); }); } -}; - -static_assert(sizeof(Access) <= sizeof(ValueBuffer) && - alignof(Access) <= alignof(ValueBuffer), - "Access doesn't fit in a value buffer!"); - -/// A set of accesses that we're tracking. Just a singly-linked list. -class AccessSet { - Access *Head = nullptr; -public: - constexpr AccessSet() {} - - bool insert(Access *access, void *pc, void *pointer, ExclusivityFlags flags) { - auto action = getAccessAction(flags); - - for (Access *cur = Head; cur != nullptr; cur = cur->getNext()) { - // Ignore accesses to different values. - if (cur->Pointer != pointer) - continue; +#endif + auto action = getAccessAction(flags); - // If both accesses are reads, it's not a conflict. - if (action == ExclusivityFlags::Read && - action == cur->getAccessAction()) - continue; + for (Access *cur = Head; cur != nullptr; cur = cur->getNext()) { + // Ignore accesses to different values. + if (cur->Pointer != pointer) + continue; - // Otherwise, it's a conflict. - reportExclusivityConflict(cur->getAccessAction(), cur->PC, - flags, pc, pointer); + // If both accesses are reads, it's not a conflict. + if (action == ExclusivityFlags::Read && action == cur->getAccessAction()) + continue; - // 0 means no backtrace will be printed. - fatalError(0, "Fatal access conflict detected.\n"); - } - if (!isTracking(flags)) - return false; + // Otherwise, it's a conflict. + reportExclusivityConflict(cur->getAccessAction(), cur->PC, flags, pc, + pointer); - // Insert to the front of the array so that remove tends to find it faster. - access->initialize(pc, pointer, Head, action); - Head = access; - return true; + // 0 means no backtrace will be printed. + fatalError(0, "Fatal access conflict detected.\n"); } - - void remove(Access *access) { - auto cur = Head; - // Fast path: stack discipline. - if (cur == access) { - Head = cur->getNext(); - return; - } - - Access *last = cur; - for (cur = cur->getNext(); cur != nullptr; - last = cur, cur = cur->getNext()) { - assert(last->getNext() == cur); - if (cur == access) { - last->setNext(cur->getNext()); - return; - } + if (!isTracking(flags)) { +#ifndef NDEBUG + if (isExclusivityLoggingEnabled()) { + withLoggingLock([&]() { fprintf(stderr, " Not tracking!\n"); }); } - - swift_unreachable("access not found in set"); +#endif + return false; } + // Insert to the front of the array so that remove tends to find it faster. + access->initialize(pc, pointer, Head, action); + Head = access; #ifndef NDEBUG - /// Only available with asserts. Intended to be used with - /// swift_dumpTrackedAccess(). - void forEach(std::function action) { - for (auto *iter = Head; iter != nullptr; iter = iter->getNext()) { - action(iter); - } + if (isExclusivityLoggingEnabled()) { + withLoggingLock([&]() { + fprintf(stderr, " Tracking!\n"); + swift_dumpTrackedAccesses(); + }); } #endif -}; - -class SwiftTLSContext { -public: - /// The set of tracked accesses. - AccessSet accessSet; - - // The "implicit" boolean parameter which is passed to a dynamically - // replaceable function. - // If true, the original function should be executed instead of the - // replacement function. - bool CallOriginalOfReplacedFunction = false; -}; - -} // end anonymous namespace - -// Each of these cases should define a function with this prototype: -// AccessSets &getAllSets(); - -#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME - -static SwiftTLSContext &getTLSContext() { - static SwiftTLSContext TLSContext; - return TLSContext; -} - -#elif SWIFT_TLS_HAS_RESERVED_PTHREAD_SPECIFIC -// Use the reserved TSD key if possible. - -static SwiftTLSContext &getTLSContext() { - SwiftTLSContext *ctx = static_cast( - SWIFT_THREAD_GETSPECIFIC(SWIFT_RUNTIME_TLS_KEY)); - if (ctx) - return *ctx; - - static OnceToken_t setupToken; - SWIFT_ONCE_F(setupToken, [](void *) { - pthread_key_init_np(SWIFT_RUNTIME_TLS_KEY, [](void *pointer) { - delete static_cast(pointer); - }); - }, nullptr); - - ctx = new SwiftTLSContext(); - SWIFT_THREAD_SETSPECIFIC(SWIFT_RUNTIME_TLS_KEY, ctx); - return *ctx; -} - -#elif __has_feature(cxx_thread_local) -// Second choice is direct language support for thread-locals. - -static thread_local SwiftTLSContext TLSContext; - -static SwiftTLSContext &getTLSContext() { - return TLSContext; + return true; } -#else -// Use the platform thread-local data API. - -static __swift_thread_key_t createSwiftThreadKey() { - __swift_thread_key_t key; - int result = SWIFT_THREAD_KEY_CREATE(&key, [](void *pointer) { - delete static_cast(pointer); - }); +void AccessSet::remove(Access *access) { + assert(Head && "removal from empty AccessSet"); +#ifndef NDEBUG + if (isExclusivityLoggingEnabled()) { + withLoggingLock( + [&]() { fprintf(stderr, "Removing access: %p\n", access); }); + } +#endif + auto cur = Head; + // Fast path: stack discipline. + if (cur == access) { + Head = cur->getNext(); + return; + } - if (result != 0) { - fatalError(0, "couldn't create thread key for exclusivity: %s\n", - strerror(result)); + Access *last = cur; + for (cur = cur->getNext(); cur != nullptr; last = cur, cur = cur->getNext()) { + assert(last->getNext() == cur); + if (cur == access) { + last->setNext(cur->getNext()); + return; + } } - return key; -} -static SwiftTLSContext &getTLSContext() { - static __swift_thread_key_t key = createSwiftThreadKey(); + swift_unreachable("access not found in set"); +} - SwiftTLSContext *ctx = static_cast(SWIFT_THREAD_GETSPECIFIC(key)); - if (!ctx) { - ctx = new SwiftTLSContext(); - SWIFT_THREAD_SETSPECIFIC(key, ctx); +#ifndef NDEBUG +/// Only available with asserts. Intended to be used with +/// swift_dumpTrackedAccess(). +void AccessSet::forEach(std::function action) { + for (auto *iter = Head; iter != nullptr; iter = iter->getNext()) { + action(iter); } - return *ctx; } - #endif +// Each of these cases should define a function with this prototype: +// AccessSets &getAllSets(); + /// Begin tracking a dynamic access. /// /// This may cause a runtime failure if an incompatible access is @@ -338,7 +263,7 @@ void swift::swift_beginAccess(void *pointer, ValueBuffer *buffer, if (!pc) pc = get_return_address(); - if (!getTLSContext().accessSet.insert(access, pc, pointer, flags)) + if (!SwiftTLSContext::get().accessSet.insert(access, pc, pointer, flags)) access->Pointer = nullptr; } @@ -353,31 +278,7 @@ void swift::swift_endAccess(ValueBuffer *buffer) { return; } - getTLSContext().accessSet.remove(access); -} - -char *swift::swift_getFunctionReplacement(char **ReplFnPtr, char *CurrFn) { - char *ReplFn = *ReplFnPtr; - char *RawReplFn = ReplFn; - -#if SWIFT_PTRAUTH - RawReplFn = ptrauth_strip(RawReplFn, ptrauth_key_function_pointer); -#endif - if (RawReplFn == CurrFn) - return nullptr; - - SwiftTLSContext &ctx = getTLSContext(); - if (ctx.CallOriginalOfReplacedFunction) { - ctx.CallOriginalOfReplacedFunction = false; - return nullptr; - } - return ReplFn; -} - -char *swift::swift_getOrigOfReplaceable(char **OrigFnPtr) { - char *OrigFn = *OrigFnPtr; - getTLSContext().CallOriginalOfReplacedFunction = true; - return OrigFn; + SwiftTLSContext::get().accessSet.remove(access); } #ifndef NDEBUG @@ -386,10 +287,18 @@ char *swift::swift_getOrigOfReplaceable(char **OrigFnPtr) { // // This is only intended to be used in the debugger. void swift::swift_dumpTrackedAccesses() { - getTLSContext().accessSet.forEach([](Access *a) { - fprintf(stderr, "Access. Pointer: %p. PC: %p. AccessAction: %s\n", - a->Pointer, a->PC, getAccessName(a->getAccessAction())); + auto &accessSet = SwiftTLSContext::get().accessSet; + if (!accessSet) { + fprintf(stderr, " No Accesses.\n"); + return; + } + accessSet.forEach([](Access *a) { + fprintf(stderr, " Access. Pointer: %p. PC: %p. AccessAction: %s\n", + a->Pointer, a->PC, getAccessName(a->getAccessAction())); }); } #endif + +// Bring in the concurrency-specific exclusivity code. +#include "ConcurrencyExclusivity.inc" diff --git a/stdlib/public/runtime/ExclusivityPrivate.h b/stdlib/public/runtime/ExclusivityPrivate.h new file mode 100644 index 0000000000000..35f0ffa134c2f --- /dev/null +++ b/stdlib/public/runtime/ExclusivityPrivate.h @@ -0,0 +1,139 @@ +//===--- ExclusivityPrivate.h ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_EXCLUSIVITYPRIVATE_H +#define SWIFT_RUNTIME_EXCLUSIVITYPRIVATE_H + +#include "swift/Runtime/Exclusivity.h" +#include "swift/Runtime/Metadata.h" +#include + +namespace swift { +namespace runtime { + +/// A single access that we're tracking. +/// +/// The following inputs are accepted by the begin_access runtime entry +/// point. This table show the action performed by the current runtime to +/// convert those inputs into stored fields in the Access scratch buffer. +/// +/// Pointer | Runtime | Access | PC | Reported| Access +/// Argument| Behavior | Pointer| Arg | PC | PC +/// -------- ------------- -------- ------- --------- ---------- +/// null | [trap or missing enforcement] +/// nonnull | [nontracked]| null | null | caller | [discard] +/// nonnull | [nontracked]| null | valid | | [discard] +/// nonnull | [tracked] | | null | caller | caller +/// nonnull | [tracked] | | valid | | +/// +/// [nontracked] means that the Access scratch buffer will not be added to the +/// runtime's list of tracked accesses. However, it may be passed to a +/// subsequent call to end_unpaired_access. The null Pointer field then +/// identifies the Access record as nontracked. +/// +/// The runtime owns the contents of the scratch buffer, which is allocated by +/// the compiler but otherwise opaque. The runtime may later reuse the Pointer +/// or PC fields or any spare bits for additional flags, and/or a pointer to +/// out-of-line storage. +struct Access { + void *Pointer; + void *PC; + uintptr_t NextAndAction; + + enum : uintptr_t { + ActionMask = (uintptr_t)ExclusivityFlags::ActionMask, + NextMask = ~ActionMask + }; + + Access *getNext() const { + return reinterpret_cast(NextAndAction & NextMask); + } + + void setNext(Access *next) { + NextAndAction = + reinterpret_cast(next) | (NextAndAction & ActionMask); + } + + ExclusivityFlags getAccessAction() const { + return ExclusivityFlags(NextAndAction & ActionMask); + } + + void initialize(void *pc, void *pointer, Access *next, + ExclusivityFlags action) { + Pointer = pointer; + PC = pc; + NextAndAction = reinterpret_cast(next) | uintptr_t(action); + } +}; + +static_assert(sizeof(Access) <= sizeof(ValueBuffer) && + alignof(Access) <= alignof(ValueBuffer), + "Access doesn't fit in a value buffer!"); + +/// A set of accesses that we're tracking. Just a singly-linked list. +/// +/// NOTE: Please keep all implementations of methods of AccessSet within +/// Exclusivity.cpp. We want this to ensure that when compiled in the runtime +/// directly, the definitions of these methods are immediately available in that +/// file for inlining. +class AccessSet { + Access *Head = nullptr; + +public: + constexpr AccessSet() {} + constexpr AccessSet(Access *Head) : Head(Head) {} + + constexpr operator bool() const { return bool(Head); } + constexpr Access *getHead() const { return Head; } + void setHead(Access *newHead) { Head = newHead; } + constexpr bool isHead(Access *access) const { return Head == access; } + + bool insert(Access *access, void *pc, void *pointer, ExclusivityFlags flags); + void remove(Access *access); + + /// Return the parent access of \p childAccess in the list. + Access *findParentAccess(Access *childAccess) const { + auto cur = Head; + Access *last = cur; + for (cur = cur->getNext(); cur != nullptr; + last = cur, cur = cur->getNext()) { + assert(last->getNext() == cur); + if (cur == childAccess) { + return last; + } + } + return nullptr; + } + + Access *getTail() const { + auto cur = Head; + if (!cur) + return nullptr; + + while (auto *next = cur->getNext()) { + cur = next; + } + assert(cur != nullptr); + return cur; + } + +#ifndef NDEBUG + /// Only available with asserts. Intended to be used with + /// swift_dumpTrackedAccess(). + void forEach(std::function action); +#endif +}; + +} // namespace runtime +} // namespace swift + +#endif diff --git a/stdlib/public/runtime/FunctionReplacement.cpp b/stdlib/public/runtime/FunctionReplacement.cpp new file mode 100644 index 0000000000000..4b9cbfd491c07 --- /dev/null +++ b/stdlib/public/runtime/FunctionReplacement.cpp @@ -0,0 +1,41 @@ +//===--- FunctionReplacement.cpp ------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/FunctionReplacement.h" +#include "SwiftTLSContext.h" + +using namespace swift; +using namespace swift::runtime; + +char *swift::swift_getFunctionReplacement(char **ReplFnPtr, char *CurrFn) { + char *ReplFn = *ReplFnPtr; + char *RawReplFn = ReplFn; + +#if SWIFT_PTRAUTH + RawReplFn = ptrauth_strip(RawReplFn, ptrauth_key_function_pointer); +#endif + if (RawReplFn == CurrFn) + return nullptr; + + auto &ctx = SwiftTLSContext::get(); + if (ctx.CallOriginalOfReplacedFunction) { + ctx.CallOriginalOfReplacedFunction = false; + return nullptr; + } + return ReplFn; +} + +char *swift::swift_getOrigOfReplaceable(char **OrigFnPtr) { + char *OrigFn = *OrigFnPtr; + SwiftTLSContext::get().CallOriginalOfReplacedFunction = true; + return OrigFn; +} diff --git a/stdlib/public/runtime/Heap.cpp b/stdlib/public/runtime/Heap.cpp index d235cb907cd1e..e3246b4e0a07d 100644 --- a/stdlib/public/runtime/Heap.cpp +++ b/stdlib/public/runtime/Heap.cpp @@ -21,7 +21,7 @@ #include "../SwiftShims/RuntimeShims.h" #include #include -#if defined(__APPLE__) +#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC #include "swift/Basic/Lazy.h" #include #endif @@ -61,7 +61,7 @@ using namespace swift; static_assert(_swift_MinAllocationAlignment > MALLOC_ALIGN_MASK, "Swift's default alignment must exceed platform malloc mask."); -#if defined(__APPLE__) +#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC static inline malloc_zone_t *DEFAULT_ZONE() { static malloc_zone_t *z = SWIFT_LAZY_CONSTANT(malloc_default_zone()); return z; @@ -88,7 +88,7 @@ void *swift::swift_slowAlloc(size_t size, size_t alignMask) { void *p; // This check also forces "default" alignment to use AlignedAlloc. if (alignMask <= MALLOC_ALIGN_MASK) { -#if defined(__APPLE__) +#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC p = malloc_zone_malloc(DEFAULT_ZONE(), size); #else p = malloc(size); @@ -121,7 +121,7 @@ void *swift::swift_slowAlloc(size_t size, size_t alignMask) { // consistent with allocation with the same alignment. void swift::swift_slowDealloc(void *ptr, size_t bytes, size_t alignMask) { if (alignMask <= MALLOC_ALIGN_MASK) { -#if defined(__APPLE__) +#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC malloc_zone_free(DEFAULT_ZONE(), ptr); #else free(ptr); diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index c6601e6d57907..0a27620622543 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -694,24 +694,22 @@ void swift::swift_rootObjCDealloc(HeapObject *self) { } #endif -#if SWIFT_OBJC_INTEROP -static bool _check_fast_dealloc() { - return dlsym(RTLD_NEXT, "_objc_has_weak_formation_callout") != nullptr; -} -#endif - void swift::swift_deallocClassInstance(HeapObject *object, size_t allocatedSize, size_t allocatedAlignMask) { - #if SWIFT_OBJC_INTEROP // We need to let the ObjC runtime clean up any associated objects or weak // references associated with this object. - const bool fastDeallocSupported = SWIFT_LAZY_CONSTANT(_check_fast_dealloc()); +#if TARGET_OS_SIMULATOR && (__x86_64__ || __i386__) + const bool fastDeallocSupported = false; +#else + const bool fastDeallocSupported = true; +#endif if (!fastDeallocSupported || !object->refCounts.getPureSwiftDeallocation()) { objc_destructInstance((id)object); } #endif + swift_deallocObject(object, allocatedSize, allocatedAlignMask); } diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index d7e50cdc259ee..5a4b1ba245358 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -385,6 +385,7 @@ static GenericMetadataCache &getCache( } #if SWIFT_PTRAUTH && SWIFT_OBJC_INTEROP +// See [NOTE: Dynamic-subclass-KVO] static void swift_objc_classCopyFixupHandler(Class oldClass, Class newClass) { auto oldClassMetadata = reinterpret_cast(oldClass); @@ -1084,6 +1085,7 @@ class FunctionCacheEntry { const Metadata *const *Parameters; const uint32_t *ParameterFlags; const Metadata *Result; + const Metadata *GlobalActor; FunctionTypeFlags getFlags() const { return Flags; } @@ -1107,11 +1109,13 @@ class FunctionCacheEntry { return ParameterFlags::fromIntValue(flags); } + const Metadata *getGlobalActor() const { return GlobalActor; } + friend llvm::hash_code hash_value(const Key &key) { auto hash = llvm::hash_combine( key.Flags.getIntValue(), key.DifferentiabilityKind.getIntValue(), - key.Result); + key.Result, key.GlobalActor); for (unsigned i = 0, e = key.getFlags().getNumParameters(); i != e; ++i) { hash = llvm::hash_combine(hash, key.getParameter(i)); hash = llvm::hash_combine(hash, key.getParameterFlags(i).getIntValue()); @@ -1134,6 +1138,8 @@ class FunctionCacheEntry { return false; if (key.getResult() != Data.ResultType) return false; + if (key.getGlobalActor() != Data.getGlobalActor()) + return false; for (unsigned i = 0, e = key.getFlags().getNumParameters(); i != e; ++i) { if (key.getParameter(i) != Data.getParameter(i)) return false; @@ -1147,7 +1153,7 @@ class FunctionCacheEntry { friend llvm::hash_code hash_value(const FunctionCacheEntry &value) { Key key = {value.Data.Flags, value.Data.getDifferentiabilityKind(), value.Data.getParameters(), value.Data.getParameterFlags(), - value.Data.ResultType}; + value.Data.ResultType, value.Data.getGlobalActor()}; return hash_value(key); } @@ -1167,6 +1173,8 @@ class FunctionCacheEntry { if (flags.isDifferentiable()) size = roundUpToAlignment(size, sizeof(void *)) + sizeof(FunctionMetadataDifferentiabilityKind); + if (flags.hasGlobalActor()) + size = roundUpToAlignment(size, sizeof(void *)) + sizeof(Metadata *); return roundUpToAlignment(size, sizeof(void *)); } }; @@ -1225,9 +1233,12 @@ swift::swift_getFunctionTypeMetadata(FunctionTypeFlags flags, assert(!flags.isDifferentiable() && "Differentiable function type metadata should be obtained using " "'swift_getFunctionTypeMetadataDifferentiable'"); + assert(!flags.hasGlobalActor() + && "Global actor function type metadata should be obtained using " + "'swift_getFunctionTypeMetadataGlobalActor'"); FunctionCacheEntry::Key key = { flags, FunctionMetadataDifferentiabilityKind::NonDifferentiable, parameters, - parameterFlags, result, + parameterFlags, result, nullptr }; return &FunctionTypes.getOrInsert(key).first->Data; } @@ -1237,10 +1248,25 @@ swift::swift_getFunctionTypeMetadataDifferentiable( FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind, const Metadata *const *parameters, const uint32_t *parameterFlags, const Metadata *result) { + assert(!flags.hasGlobalActor() + && "Global actor function type metadata should be obtained using " + "'swift_getFunctionTypeMetadataGlobalActor'"); assert(flags.isDifferentiable()); assert(diffKind.isDifferentiable()); FunctionCacheEntry::Key key = { - flags, diffKind, parameters, parameterFlags, result + flags, diffKind, parameters, parameterFlags, result, nullptr + }; + return &FunctionTypes.getOrInsert(key).first->Data; +} + +const FunctionTypeMetadata * +swift::swift_getFunctionTypeMetadataGlobalActor( + FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind, + const Metadata *const *parameters, const uint32_t *parameterFlags, + const Metadata *result, const Metadata *globalActor) { + assert(flags.hasGlobalActor()); + FunctionCacheEntry::Key key = { + flags, diffKind, parameters, parameterFlags, result, globalActor }; return &FunctionTypes.getOrInsert(key).first->Data; } @@ -1281,6 +1307,8 @@ FunctionCacheEntry::FunctionCacheEntry(const Key &key) { Data.setKind(MetadataKind::Function); Data.Flags = flags; Data.ResultType = key.getResult(); + if (flags.hasGlobalActor()) + *Data.getGlobalActorAddr() = key.getGlobalActor(); if (flags.isDifferentiable()) *Data.getDifferentiabilityKindAddress() = key.getDifferentiabilityKind(); @@ -2552,14 +2580,6 @@ static void initGenericClassObjCName(ClassMetadata *theClass) { } static bool installLazyClassNameHook() { -#if !OBJC_SETHOOK_LAZYCLASSNAMER_DEFINED - using objc_hook_lazyClassNamer = - const char * _Nullable (*)(_Nonnull Class cls); - auto objc_setHook_lazyClassNamer = - (void (*)(objc_hook_lazyClassNamer, objc_hook_lazyClassNamer *)) - dlsym(RTLD_NEXT, "objc_setHook_lazyClassNamer"); -#endif - static objc_hook_lazyClassNamer oldHook; auto myHook = [](Class theClass) -> const char * { ClassMetadata *metadata = (ClassMetadata *)theClass; @@ -2568,14 +2588,12 @@ static bool installLazyClassNameHook() { return oldHook(theClass); }; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability" - if (objc_setHook_lazyClassNamer == nullptr) - return false; - objc_setHook_lazyClassNamer(myHook, &oldHook); -#pragma clang diagnostic pop + if (SWIFT_RUNTIME_WEAK_CHECK(objc_setHook_lazyClassNamer)) { + SWIFT_RUNTIME_WEAK_USE(objc_setHook_lazyClassNamer(myHook, &oldHook)); + return true; + } - return true; + return false; } __attribute__((constructor)) SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE static bool @@ -3049,11 +3067,6 @@ getSuperclassMetadata(ClassMetadata *self, bool allowDependency) { return {MetadataDependency(), second}; } -// Suppress diagnostic about the availability of _objc_realizeClassFromSwift. -// We test availability with a nullptr check, but the compiler doesn't see that. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability-new" - static SWIFT_CC(swift) MetadataDependency _swift_initClassMetadataImpl(ClassMetadata *self, ClassLayoutFlags layoutFlags, @@ -3080,11 +3093,6 @@ _swift_initClassMetadataImpl(ClassMetadata *self, (void)unused; setUpObjCRuntimeGetImageNameFromClass(); }, nullptr); - - // Temporary workaround until objc_loadClassref is in the SDK. - static auto objc_loadClassref = - (Class (*)(void *)) - dlsym(RTLD_NEXT, "objc_loadClassref"); #endif // Copy field offsets, generic arguments and (if necessary) vtable entries @@ -3121,10 +3129,8 @@ _swift_initClassMetadataImpl(ClassMetadata *self, // The compiler enforces that @objc methods in extensions of classes // with resilient ancestry have the correct availability, so it should // be safe to ignore the stub in this case. - if (stub != nullptr && - objc_loadClassref != nullptr && - &_objc_realizeClassFromSwift != nullptr) { - _objc_realizeClassFromSwift((Class) self, const_cast(stub)); + if (stub != nullptr && SWIFT_RUNTIME_WEAK_CHECK(_objc_realizeClassFromSwift)) { + SWIFT_RUNTIME_WEAK_USE(_objc_realizeClassFromSwift((Class) self, const_cast(stub))); } else { swift_instantiateObjCClass(self); } @@ -3136,8 +3142,6 @@ _swift_initClassMetadataImpl(ClassMetadata *self, return MetadataDependency(); } -#pragma clang diagnostic pop - void swift::swift_initClassMetadata(ClassMetadata *self, ClassLayoutFlags layoutFlags, size_t numFields, @@ -3161,12 +3165,6 @@ swift::swift_initClassMetadata2(ClassMetadata *self, #if SWIFT_OBJC_INTEROP -// Suppress diagnostic about the availability of _objc_realizeClassFromSwift. -// We test availability with a nullptr check, but the compiler doesn't see that. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability-new" - - static SWIFT_CC(swift) MetadataDependency _swift_updateClassMetadataImpl(ClassMetadata *self, ClassLayoutFlags layoutFlags, @@ -3174,7 +3172,7 @@ _swift_updateClassMetadataImpl(ClassMetadata *self, const TypeLayout * const *fieldTypes, size_t *fieldOffsets, bool allowDependency) { - bool requiresUpdate = (&_objc_realizeClassFromSwift != nullptr); + bool requiresUpdate = SWIFT_RUNTIME_WEAK_CHECK(_objc_realizeClassFromSwift); // If we're on a newer runtime, we're going to be initializing the // field offset vector. Realize the superclass metadata first, even @@ -3222,7 +3220,7 @@ _swift_updateClassMetadataImpl(ClassMetadata *self, initObjCClass(self, numFields, fieldTypes, fieldOffsets); // See remark above about how this slides field offset globals. - _objc_realizeClassFromSwift((Class)self, (Class)self); + SWIFT_RUNTIME_WEAK_USE(_objc_realizeClassFromSwift((Class)self, (Class)self)); } return MetadataDependency(); @@ -3249,7 +3247,6 @@ swift::swift_updateClassMetadata2(ClassMetadata *self, /*allowDependency*/ true); } -#pragma clang diagnostic pop #endif #ifndef NDEBUG @@ -3493,13 +3490,17 @@ swift::swift_getExistentialMetatypeMetadata(const Metadata *instanceMetadata) { ExistentialMetatypeCacheEntry::ExistentialMetatypeCacheEntry( const Metadata *instanceMetadata) { ExistentialTypeFlags flags; - if (instanceMetadata->getKind() == MetadataKind::Existential) { + switch (instanceMetadata->getKind()) { + case MetadataKind::Existential: flags = static_cast(instanceMetadata) ->Flags; - } else { - assert(instanceMetadata->getKind() == MetadataKind::ExistentialMetatype); + break; + case MetadataKind::ExistentialMetatype: flags = static_cast(instanceMetadata) ->Flags; + break; + default: + assert(false && "expected existential metadata"); } Data.setKind(MetadataKind::ExistentialMetatype); diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index eccc88d6c1c37..115fd8d0720c7 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -673,6 +673,10 @@ _searchTypeMetadataRecords(TypeMetadataPrivateState &T, #define STANDARD_TYPE(KIND, MANGLING, TYPENAME) \ extern "C" const ContextDescriptor DESCRIPTOR_MANGLING(MANGLING, DESCRIPTOR_MANGLING_SUFFIX(KIND)); +// FIXME: When the _Concurrency library gets merged into the Standard Library, +// we will be able to reference those symbols directly as well. +#define STANDARD_TYPE_2(KIND, MANGLING, TYPENAME) + #if !SWIFT_OBJC_INTEROP # define OBJC_INTEROP_STANDARD_TYPE(KIND, MANGLING, TYPENAME) #endif @@ -703,6 +707,9 @@ _findContextDescriptor(Demangle::NodePointer node, if (name.equals(#TYPENAME)) { \ return &DESCRIPTOR_MANGLING(MANGLING, DESCRIPTOR_MANGLING_SUFFIX(KIND)); \ } + // FIXME: When the _Concurrency library gets merged into the Standard Library, + // we will be able to reference those symbols directly as well. +#define STANDARD_TYPE_2(KIND, MANGLING, TYPENAME) #if !SWIFT_OBJC_INTEROP # define OBJC_INTEROP_STANDARD_TYPE(KIND, MANGLING, TYPENAME) #endif @@ -1439,6 +1446,12 @@ class DecodedMetadataBuilder { TypeLookupErrorOr createExistentialMetatypeType( BuiltType instance, llvm::Optional repr = None) const { + if (instance->getKind() != MetadataKind::Existential + && instance->getKind() != MetadataKind::ExistentialMetatype) { + return TYPE_LOOKUP_ERROR_FMT("Tried to build an existential metatype from " + "a type that was neither an existential nor " + "an existential metatype"); + } return swift_getExistentialMetatypeMetadata(instance); } @@ -1480,7 +1493,8 @@ class DecodedMetadataBuilder { createFunctionType( llvm::ArrayRef> params, BuiltType result, FunctionTypeFlags flags, - FunctionMetadataDifferentiabilityKind diffKind) const { + FunctionMetadataDifferentiabilityKind diffKind, + BuiltType globalActorType) const { assert( (flags.isDifferentiable() && diffKind.isDifferentiable()) || (!flags.isDifferentiable() && !diffKind.isDifferentiable())); @@ -1497,15 +1511,22 @@ class DecodedMetadataBuilder { paramFlags.push_back(param.getFlags().getIntValue()); } - return flags.isDifferentiable() - ? swift_getFunctionTypeMetadataDifferentiable( - flags, diffKind, paramTypes.data(), - flags.hasParameterFlags() ? paramFlags.data() : nullptr, - result) - : swift_getFunctionTypeMetadata( - flags, paramTypes.data(), - flags.hasParameterFlags() ? paramFlags.data() : nullptr, - result); + if (globalActorType) + flags = flags.withGlobalActor(true); + + return flags.hasGlobalActor() + ? swift_getFunctionTypeMetadataGlobalActor(flags, diffKind, paramTypes.data(), + flags.hasParameterFlags() ? paramFlags.data() : nullptr, result, + globalActorType) + : flags.isDifferentiable() + ? swift_getFunctionTypeMetadataDifferentiable( + flags, diffKind, paramTypes.data(), + flags.hasParameterFlags() ? paramFlags.data() : nullptr, + result) + : swift_getFunctionTypeMetadata( + flags, paramTypes.data(), + flags.hasParameterFlags() ? paramFlags.data() : nullptr, + result); } TypeLookupErrorOr createImplFunctionType( @@ -1944,23 +1965,9 @@ getObjCClassByMangledName(const char * _Nonnull typeName, __attribute__((constructor)) static void installGetClassHook() { - // FIXME: delete this #if and dlsym once we don't - // need to build with older libobjc headers -#if !OBJC_GETCLASSHOOK_DEFINED - using objc_hook_getClass = BOOL(*)(const char * _Nonnull name, - Class _Nullable * _Nonnull outClass); - auto objc_setHook_getClass = - (void(*)(objc_hook_getClass _Nonnull, - objc_hook_getClass _Nullable * _Nonnull)) - dlsym(RTLD_DEFAULT, "objc_setHook_getClass"); -#endif - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability" - if (&objc_setHook_getClass) { - objc_setHook_getClass(getObjCClassByMangledName, &OldGetClassHook); + if (SWIFT_RUNTIME_WEAK_CHECK(objc_setHook_getClass)) { + SWIFT_RUNTIME_WEAK_USE(objc_setHook_getClass(getObjCClassByMangledName, &OldGetClassHook)); } -#pragma clang diagnostic pop } #endif diff --git a/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.mm b/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.mm index f8cb0c9177dfe..897cdf6a01dcf 100644 --- a/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.mm +++ b/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.mm @@ -28,6 +28,13 @@ #include #include +#if __has_include() +#include +#define APPLE_OS_SYSTEM 1 +#else +#define APPLE_OS_SYSTEM 0 +#endif + // Note: There are more #includes below under "Function patching machinery". // Those are only relevant to the function patching machinery. @@ -69,11 +76,17 @@ typedef BOOL (*objc_hook_getImageName)( const void *descriptor = classAsMetadata->getDescription(); assert(descriptor && "all non-artificial Swift classes should have a descriptor"); +#if APPLE_OS_SYSTEM + // Use a more efficient internal API when building the system libraries + // for Apple OSes. + *outImageName = dyld_image_path_containing_address(descriptor); +#else Dl_info imageInfo = {}; if (!dladdr(descriptor, &imageInfo)) return NO; *outImageName = imageInfo.dli_fname; - return imageInfo.dli_fname != nullptr; +#endif + return *outImageName != nullptr; } return NO; diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index b073de6997ac8..b0d7b7e778c2c 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -268,8 +268,9 @@ class TypeInfo { const ContextDescriptor * _searchConformancesByMangledTypeName(Demangle::NodePointer node); + SWIFT_RUNTIME_EXPORT Demangle::NodePointer _swift_buildDemanglingForMetadata(const Metadata *type, - Demangle::Demangler &Dem); + Demangle::Demangler &Dem); /// Callback used to provide the substitution of a generic parameter /// (described by depth/index) to its metadata. @@ -367,13 +368,15 @@ class TypeInfo { unsigned index) const; }; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" /// Retrieve the type metadata described by the given demangled type name. /// /// \p substGenericParam Function that provides generic argument metadata /// given a particular generic parameter specified by depth/index. /// \p substWitnessTable Function that provides witness tables given a /// particular dependent conformance index. - SWIFT_CC(swift) + SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) TypeLookupErrorOr swift_getTypeByMangledNode( MetadataRequest request, Demangler &demangler, @@ -388,13 +391,14 @@ class TypeInfo { /// given a particular generic parameter specified by depth/index. /// \p substWitnessTable Function that provides witness tables given a /// particular dependent conformance index. - SWIFT_CC(swift) + SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) TypeLookupErrorOr swift_getTypeByMangledName( MetadataRequest request, StringRef typeName, const void * const *arguments, SubstGenericParameterFn substGenericParam, SubstDependentWitnessTableFn substWitnessTable); +#pragma clang diagnostic pop /// Function object that produces substitutions for the generic parameters /// that occur within a mangled name, using the complete set of generic diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 84df8a06e713e..1909fc8541613 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -36,6 +36,23 @@ #if __has_include() #include #define DYLD_EXPECTED_SWIFT_OPTIMIZATIONS_VERSION 1u + +// Redeclare these functions as weak so we can build against a macOS 12 SDK and +// still test on macOS 11. +LLVM_ATTRIBUTE_WEAK +struct _dyld_protocol_conformance_result +_dyld_find_protocol_conformance(const void *protocolDescriptor, + const void *metadataType, + const void *typeDescriptor); + +LLVM_ATTRIBUTE_WEAK +struct _dyld_protocol_conformance_result +_dyld_find_foreign_type_protocol_conformance(const void *protocol, + const char *foreignTypeIdentityStart, + size_t foreignTypeIdentityLength); + +LLVM_ATTRIBUTE_WEAK +uint32_t _dyld_swift_optimizations_version(void); #endif // Set this to 1 to enable logging of calls to the dyld shared cache conformance @@ -147,6 +164,118 @@ const ClassMetadata *TypeReference::getObjCClass(TypeReferenceKind kind) const { } #endif +static MetadataState +tryGetCompleteMetadataNonblocking(const Metadata *metadata) { + return swift_checkMetadataState( + MetadataRequest(MetadataState::Complete, /*isNonBlocking*/ true), + metadata) + .State; +} + +/// Get the superclass of metadata, which may be incomplete. When the metadata +/// is not sufficiently complete, then we fall back to demangling the superclass +/// in the nominal type descriptor, which is slow but works. Return {NULL, +/// MetadataState::Complete} if the metadata is not a class, or has no +/// superclass. +/// +/// If the metadata's current state is known, it may be passed in as +/// knownMetadataState. This saves the cost of retrieving that info separately. +/// +/// When instantiateSuperclassMetadata is true, this function will instantiate +/// superclass metadata when necessary. When false, this will return {NULL, +/// MetadataState::Abstract} to indicate that there's an uninstantiated +/// superclass that was not returned. +static MetadataResponse getSuperclassForMaybeIncompleteMetadata( + const Metadata *metadata, llvm::Optional knownMetadataState, + bool instantiateSuperclassMetadata) { + const ClassMetadata *classMetadata = dyn_cast(metadata); + if (!classMetadata) + return {_swift_class_getSuperclass(metadata), MetadataState::Complete}; + +#if SWIFT_OBJC_INTEROP + // Artificial subclasses are not valid type metadata and + // tryGetCompleteMetadataNonblocking will crash on them. However, they're + // always fully set up, so we can just skip it and fetch the Subclass field. + if (classMetadata->isTypeMetadata() && classMetadata->isArtificialSubclass()) + return {classMetadata->Superclass, MetadataState::Complete}; + + // Pure ObjC classes are already set up, and the code below will not be + // happy with them. + if (!classMetadata->isTypeMetadata()) + return {classMetadata->Superclass, MetadataState::Complete}; +#endif + + MetadataState metadataState; + if (knownMetadataState) + metadataState = *knownMetadataState; + else + metadataState = tryGetCompleteMetadataNonblocking(classMetadata); + + if (metadataState == MetadataState::Complete) { + // The subclass metadata is complete. Fetch and return the superclass. + auto *superMetadata = getMetadataForClass(classMetadata->Superclass); + return {superMetadata, MetadataState::Complete}; + } else if (metadataState == MetadataState::NonTransitiveComplete) { + // The subclass metadata is complete, but, unlike above, not transitively. + // Its Superclass field is valid, so just read that field to get to the + // superclass to proceed to the next step. + auto *superMetadata = getMetadataForClass(classMetadata->Superclass); + auto superState = tryGetCompleteMetadataNonblocking(superMetadata); + return {superMetadata, superState}; + } else if (instantiateSuperclassMetadata) { + // The subclass metadata is either LayoutComplete or Abstract, so the + // Superclass field is not valid. To get to the superclass, make the + // expensive call to getSuperclassMetadata which demangles the superclass + // name from the nominal type descriptor to get the metadata for the + // superclass. + MetadataRequest request(MetadataState::Abstract, + /*non-blocking*/ true); + return getSuperclassMetadata(request, classMetadata); + } else { + // The Superclass field is not valid and the caller did not request + // instantiation. Return a NULL superclass and Abstract to indicate that a + // superclass exists but is not yet instantiated. + return {nullptr, MetadataState::Abstract}; + } +} + +struct MaybeIncompleteSuperclassIterator { + const Metadata *metadata; + llvm::Optional state; + bool instantiateSuperclassMetadata; + + MaybeIncompleteSuperclassIterator(const Metadata *metadata, + bool instantiateSuperclassMetadata) + : metadata(metadata), state(llvm::None), + instantiateSuperclassMetadata(instantiateSuperclassMetadata) {} + + MaybeIncompleteSuperclassIterator &operator++() { + auto response = getSuperclassForMaybeIncompleteMetadata( + metadata, state, instantiateSuperclassMetadata); + metadata = response.Value; + state = response.State; + return *this; + } + + const Metadata *operator*() const { return metadata; } + + bool operator!=(const MaybeIncompleteSuperclassIterator rhs) const { + return metadata != rhs.metadata; + } +}; + +/// Return a range that will iterate over the given metadata and all its +/// superclasses in order. If the metadata is not a class, iteration will +/// provide that metadata and then stop. +iterator_range +iterateMaybeIncompleteSuperclasses(const Metadata *metadata, + bool instantiateSuperclassMetadata) { + return iterator_range( + MaybeIncompleteSuperclassIterator(metadata, + instantiateSuperclassMetadata), + MaybeIncompleteSuperclassIterator(nullptr, false)); +} + /// Take the type reference inside a protocol conformance record and fetch the /// canonical metadata pointer for the type it refers to. /// Returns nil for universal or generic type references. @@ -302,7 +431,7 @@ struct ConformanceState { runtime::bincompat::workaroundProtocolConformanceReverseIteration(); #if USE_DYLD_SHARED_CACHE_CONFORMANCE_TABLES - if (__builtin_available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { if (runtime::environment::SWIFT_DEBUG_ENABLE_SHARED_CACHE_PROTOCOL_CONFORMANCES()) { if (&_dyld_swift_optimizations_version) { if (_dyld_swift_optimizations_version() == @@ -465,18 +594,17 @@ swift::swift_registerProtocolConformances(const ProtocolConformanceRecord *begin /// A return value of `{ false, nullptr }` indicates nothing was cached. static std::pair searchInConformanceCache(const Metadata *type, - const ProtocolDescriptor *protocol) { + const ProtocolDescriptor *protocol, + bool instantiateSuperclassMetadata) { auto &C = Conformances.get(); auto origType = type; auto snapshot = C.Cache.snapshot(); - while (type) { + for (auto type : iterateMaybeIncompleteSuperclasses( + type, instantiateSuperclassMetadata)) { if (auto *Value = snapshot.find(ConformanceCacheKey(type, protocol))) { return {type == origType, Value->getWitnessTable()}; } - - // If there is a superclass, look there. - type = _swift_class_getSuperclass(type); } // We did not find a cache entry. @@ -561,14 +689,12 @@ namespace { /// Retrieve the type that matches the conformance candidate, which may /// be a superclass of the given type. Returns null if this type does not /// match this conformance. - const Metadata *getMatchingType(const Metadata *conformingType) const { - while (conformingType) { - // Check for a match. + const Metadata *getMatchingType(const Metadata *conformingType, + bool instantiateSuperclassMetadata) const { + for (auto conformingType : iterateMaybeIncompleteSuperclasses( + conformingType, instantiateSuperclassMetadata)) { if (matches(conformingType)) return conformingType; - - // Look for a superclass. - conformingType = _swift_class_getSuperclass(conformingType); } return nullptr; @@ -580,7 +706,8 @@ static void validateSharedCacheResults( ConformanceState &C, const Metadata *type, const ProtocolDescriptor *protocol, const WitnessTable *dyldCachedWitnessTable, - const ProtocolConformanceDescriptor *dyldCachedConformanceDescriptor) { + const ProtocolConformanceDescriptor *dyldCachedConformanceDescriptor, + bool instantiateSuperclassMetadata) { #if USE_DYLD_SHARED_CACHE_CONFORMANCE_TABLES if (!C.sharedCacheOptimizationsActive() || !C.validateSharedCacheResults) return; @@ -593,7 +720,7 @@ static void validateSharedCacheResults( continue; ConformanceCandidate candidate(descriptor); - if (candidate.getMatchingType(type)) + if (candidate.getMatchingType(type, instantiateSuperclassMetadata)) conformances.push_back(&descriptor); } } @@ -642,7 +769,8 @@ static void validateSharedCacheResults( static std::tuple findSharedCacheConformance(ConformanceState &C, const Metadata *type, - const ProtocolDescriptor *protocol) { + const ProtocolDescriptor *protocol, + bool instantiateSuperclassMetadata) { #if USE_DYLD_SHARED_CACHE_CONFORMANCE_TABLES const ContextDescriptor *description; llvm::StringRef foreignTypeIdentity; @@ -676,7 +804,8 @@ findSharedCacheConformance(ConformanceState &C, const Metadata *type, dyldResult.value); assert(conformanceDescriptor->getProtocol() == protocol); - assert(ConformanceCandidate{*conformanceDescriptor}.getMatchingType(type)); + assert(ConformanceCandidate{*conformanceDescriptor}.getMatchingType( + type, instantiateSuperclassMetadata)); if (conformanceDescriptor->getGenericWitnessTable()) { SHARED_CACHE_LOG( @@ -727,9 +856,15 @@ findSharedCacheConformance(ConformanceState &C, const Metadata *type, #endif } -static const WitnessTable * -swift_conformsToProtocolImpl(const Metadata *const type, - const ProtocolDescriptor *protocol) { +/// Check if a type conforms to a protocol, possibly instantiating superclasses +/// that have not yet been instantiated. The return value is a pair consisting +/// of the witness table for the conformance (or NULL if no conformance was +/// found), and a boolean indicating whether there are uninstantiated +/// superclasses that were not searched. +static std::pair +swift_conformsToProtocolMaybeInstantiateSuperclasses( + const Metadata *const type, const ProtocolDescriptor *protocol, + bool instantiateSuperclassMetadata) { auto &C = Conformances.get(); const WitnessTable *dyldCachedWitnessTable = nullptr; @@ -739,52 +874,56 @@ swift_conformsToProtocolImpl(const Metadata *const type, // Search the shared cache tables for a conformance for this type, and for // superclasses (if it's a class). if (C.sharedCacheOptimizationsActive()) { - const Metadata *dyldSearchType = type; - do { + for (auto dyldSearchType : iterateMaybeIncompleteSuperclasses( + type, instantiateSuperclassMetadata)) { bool definitiveFailure; std::tie(dyldCachedWitnessTable, dyldCachedConformanceDescriptor, definitiveFailure) = - findSharedCacheConformance(C, dyldSearchType, protocol); + findSharedCacheConformance(C, dyldSearchType, protocol, + instantiateSuperclassMetadata); if (definitiveFailure) - return nullptr; + return {nullptr, false}; - dyldSearchType = _swift_class_getSuperclass(dyldSearchType); - } while (dyldSearchType && !dyldCachedWitnessTable && - !dyldCachedConformanceDescriptor); + if (dyldCachedWitnessTable || dyldCachedConformanceDescriptor) + break; + } validateSharedCacheResults(C, type, protocol, dyldCachedWitnessTable, - dyldCachedConformanceDescriptor); + dyldCachedConformanceDescriptor, + instantiateSuperclassMetadata); // Return a cached result if we got a witness table. We can't do this if // scanSectionsBackwards is set, since a scanned conformance can override a // cached result in that case. if (!C.scanSectionsBackwards) if (dyldCachedWitnessTable) - return dyldCachedWitnessTable; + return {dyldCachedWitnessTable, false}; } // See if we have an authoritative cached conformance. The // ConcurrentReadableHashMap data structure allows us to search the map // concurrently without locking. - auto found = searchInConformanceCache(type, protocol); + auto found = + searchInConformanceCache(type, protocol, instantiateSuperclassMetadata); if (found.first) { // An authoritative negative result can be overridden by a result from dyld. if (!found.second) { if (dyldCachedWitnessTable) - return dyldCachedWitnessTable; + return {dyldCachedWitnessTable, false}; } - return found.second; + return {found.second, false}; } if (dyldCachedConformanceDescriptor) { ConformanceCandidate candidate(*dyldCachedConformanceDescriptor); - auto *matchingType = candidate.getMatchingType(type); + auto *matchingType = + candidate.getMatchingType(type, instantiateSuperclassMetadata); assert(matchingType); auto witness = dyldCachedConformanceDescriptor->getWitnessTable(matchingType); C.cacheResult(type, protocol, witness, /*always cache*/ 0); SHARED_CACHE_LOG("Caching generic conformance to %s found in shared cache", protocol->Name.get()); - return witness; + return {witness, false}; } // Scan conformance records. @@ -800,7 +939,8 @@ swift_conformsToProtocolImpl(const Metadata *const type, // The matching type is exact, so they can't go stale, and we should // always cache them. ConformanceCandidate candidate(descriptor); - if (auto *matchingType = candidate.getMatchingType(type)) { + if (auto *matchingType = + candidate.getMatchingType(type, instantiateSuperclassMetadata)) { auto witness = descriptor.getWitnessTable(matchingType); C.cacheResult(matchingType, protocol, witness, /*always cache*/ 0); foundWitnesses.insert({matchingType, witness}); @@ -827,26 +967,74 @@ swift_conformsToProtocolImpl(const Metadata *const type, // Find the most specific conformance that was scanned. const WitnessTable *foundWitness = nullptr; - const Metadata *searchType = type; - while (!foundWitness && searchType) { - foundWitness = foundWitnesses.lookup(searchType); - - // If there's no entry here, move up to the superclass (if any). - if (!foundWitness) - searchType = _swift_class_getSuperclass(searchType); + const Metadata *foundType = nullptr; + + // Use MaybeIncompleteSuperclassIterator directly so we can examine its final + // state. Complete indicates that we finished normally, Abstract indicates + // that there's an uninstantiated superclass we didn't iterate over. + MaybeIncompleteSuperclassIterator superclassIterator{ + type, instantiateSuperclassMetadata}; + for (; auto searchType = superclassIterator.metadata; ++superclassIterator) { + const WitnessTable *witness = foundWitnesses.lookup(searchType); + if (witness) { + if (!foundType) { + foundWitness = witness; + foundType = searchType; + } else { + swift::warning(RuntimeErrorFlagNone, + "Warning: '%s' conforms to protocol '%s', but it also " + "inherits conformance from '%s'. Relying on a " + "particular conformance is undefined behaviour.\n", + foundType->getDescription()->Name.get(), + protocol->Name.get(), + searchType->getDescription()->Name.get()); + } + } } + // Do not cache negative results if there were uninstantiated superclasses we + // didn't search. They might have a conformance that will be found later. + bool hasUninstantiatedSuperclass = + superclassIterator.state == MetadataState::Abstract; + // If it's for a superclass or if we didn't find anything, then add an // authoritative entry for this type. - if (searchType != type) - C.cacheResult(type, protocol, foundWitness, snapshot.count()); + if (foundType != type) + if (foundWitness || !hasUninstantiatedSuperclass) + C.cacheResult(type, protocol, foundWitness, snapshot.count()); // A negative result can be overridden by a result from dyld. - if (foundWitness) { + if (!foundWitness) { if (dyldCachedWitnessTable) - return dyldCachedWitnessTable; + return {dyldCachedWitnessTable, false}; } - return foundWitness; + return {foundWitness, hasUninstantiatedSuperclass}; +} + +static const WitnessTable * +swift_conformsToProtocolImpl(const Metadata *const type, + const ProtocolDescriptor *protocol) { + const WitnessTable *table; + bool hasUninstantiatedSuperclass; + + // First, try without instantiating any new superclasses. This avoids + // an infinite loop for cases like `class Sub: Super`. In cases like + // that, the conformance must exist on the subclass (or at least somewhere + // in the chain before we get to an uninstantiated superclass) so this search + // will succeed without trying to instantiate Super while it's already being + // instantiated.= + std::tie(table, hasUninstantiatedSuperclass) = + swift_conformsToProtocolMaybeInstantiateSuperclasses( + type, protocol, false /*instantiateSuperclassMetadata*/); + + // If no conformance was found, and there is an uninstantiated superclass that + // was not searched, then try the search again and instantiate all + // superclasses. + if (!table && hasUninstantiatedSuperclass) + std::tie(table, hasUninstantiatedSuperclass) = + swift_conformsToProtocolMaybeInstantiateSuperclasses( + type, protocol, true /*instantiateSuperclassMetadata*/); + return table; } const ContextDescriptor * @@ -864,14 +1052,6 @@ swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) { return nullptr; } -static MetadataState -tryGetCompleteMetadataNonblocking(const Metadata *metadata) { - return swift_checkMetadataState( - MetadataRequest(MetadataState::Complete, /*isNonBlocking*/ true), - metadata) - .State; -} - template bool isSwiftClassMetadataSubclass(const ClassMetadata *subclass, const ClassMetadata *superclass, @@ -879,52 +1059,19 @@ bool isSwiftClassMetadataSubclass(const ClassMetadata *subclass, assert(subclass); assert(superclass); - MetadataState subclassState = tryGetCompleteMetadataNonblocking(subclass); - - do { - if (subclassState == MetadataState::Complete) { - // The subclass metadata is complete. That means not just that its - // Superclass field is valid, but that the Superclass field of the - // referenced class metadata is valid, and the Superclass field of the - // class metadata referenced there, and so on transitively. - // - // Scan the superclass chains in the ClassMetadata looking for a match. - while ((subclass = subclass->Superclass)) { - if (subclass == superclass) - return true; - } - return false; - } - if (subclassState == MetadataState::NonTransitiveComplete) { - // The subclass metadata is complete, but, unlike above, not transitively. - // Its Superclass field is valid, so just read that field to get to the - // superclass to proceed to the next step. - subclass = subclass->Superclass; - if (subclass->isPureObjC()) { - return handleObjc(subclass, superclass); - } - subclassState = tryGetCompleteMetadataNonblocking(subclass); - } else { - // The subclass metadata is either LayoutComplete or Abstract, so the - // Superclass field is not valid. To get to the superclass, make the - // expensive call to getSuperclassMetadata which demangles the superclass - // name from the nominal type descriptor to get the metadata for the - // superclass. - MetadataRequest request(MetadataState::Complete, - /*non-blocking*/ true); - auto response = getSuperclassMetadata(request, subclass); - auto newMetadata = response.Value; - if (auto newSubclass = dyn_cast(newMetadata)) { - subclass = newSubclass; - subclassState = response.State; - } else { - return handleObjc(newMetadata, superclass); - } - } - if (subclass == superclass) + llvm::Optional subclassState = llvm::None; + while (true) { + auto response = getSuperclassForMaybeIncompleteMetadata( + subclass, subclassState, true /*instantiateSuperclassMetadata*/); + if (response.Value == superclass) return true; - } while (subclass); - return false; + if (!response.Value) + return false; + + subclass = dyn_cast(response.Value); + if (!subclass || subclass->isPureObjC()) + return handleObjc(response.Value, superclass); + } } // Whether the provided `subclass` is metadata for a subclass* of the superclass @@ -1118,7 +1265,8 @@ const Metadata *swift::findConformingSuperclass( // Figure out which type we're looking for. ConformanceCandidate candidate(*conformance); - const Metadata *conformingType = candidate.getMatchingType(type); + const Metadata *conformingType = + candidate.getMatchingType(type, true /*instantiateSuperclassMetadata*/); assert(conformingType); return conformingType; } diff --git a/stdlib/public/runtime/SwiftDtoa.cpp b/stdlib/public/runtime/SwiftDtoa.cpp index 29310ea340e60..a1720157ee404 100644 --- a/stdlib/public/runtime/SwiftDtoa.cpp +++ b/stdlib/public/runtime/SwiftDtoa.cpp @@ -40,12 +40,12 @@ /// /// * Fast. It uses only fixed-width integer arithmetic and has /// constant memory requirements. For double-precision values on -/// 64-bit processors, it is competitive with Ryu. For double-precision +/// 64-bit processors, it is competitive with Ryu. For double-precision /// values on 32-bit processors, and higher-precision values on all /// processors, it is considerably faster. /// /// * Always Accurate. Converting the decimal form back to binary -/// will always yield exactly the same value. For the IEEE 754 +/// will always yield exactly the same value. For the IEEE 754 /// formats, the round-trip will produce exactly the same bit /// pattern in memory. /// @@ -125,7 +125,7 @@ static void intervalContainingPowerOf10_Binary32(int p, uint64_t *lower, uint64_ #endif // -// Helpers used by binary32, binary64, float80, and binary128 +// Helpers used by binary32, binary64, float80, and binary128. // #if SWIFT_DTOA_BINARY32_SUPPORT || SWIFT_DTOA_BINARY64_SUPPORT || SWIFT_DTOA_FLOAT80_SUPPORT || SWIFT_DTOA_BINARY128_SUPPORT @@ -782,7 +782,7 @@ size_t swift_dtoa_optimal_binary64_p(const void *d, char *dest, size_t length) // bias. That's because they treat the significand as a // fixed-point number with one bit (the hidden bit) integer // portion. The logic here reconstructs the significand as a - // pure fraction, so we need to accomodate that when + // pure fraction, so we need to accommodate that when // reconstructing the binary exponent. static const int64_t exponentBias = (1 << (exponentBitCount - 1)) - 2; // 1022 @@ -911,14 +911,14 @@ size_t swift_dtoa_optimal_binary64_p(const void *d, char *dest, size_t length) // This ensures accuracy but, as explained in Loitsch' paper, // this carries a risk that there will be a shorter digit // sequence outside of our narrowed interval that we will - // miss. This risk obviously gets lower with increased + // miss. This risk obviously gets lower with increased // precision, but it wasn't until the Errol paper that anyone // had a good way to test whether a particular implementation - // had sufficient precision. That paper shows a way to enumerate + // had sufficient precision. That paper shows a way to enumerate // the worst-case numbers; those numbers that are extremely close // to the mid-points between adjacent floating-point values. // These are the values that might sit just outside of the - // narrowed interval. By testing these values, we can verify + // narrowed interval. By testing these values, we can verify // the correctness of our implementation. // Multiply out the upper midpoint, rounding down... @@ -1202,7 +1202,8 @@ size_t swift_dtoa_optimal_binary64_p(const void *d, char *dest, size_t length) // value 0.1234 and computed u = 0.1257, l = 0.1211. The above // digit generation works with `u`, so produces 0.125. But the // values 0.122, 0.123, and 0.124 are just as short and 0.123 is - // the best choice, since it's closest to the original value. + // therefore the best choice, since it's closest to the original + // value. // We know delta and t are both less than 10.0 here, so we can // shed some excess integer bits to simplify the following: diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 40343953a9156..d63d91810fbae 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -76,7 +76,6 @@ OBJC_EXPORT __attribute__((__weak_import__)) } #if SWIFT_OBJC_INTEROP - /// Replacement for ObjC object_isClass(), which is unavailable on /// deployment targets macOS 10.9 and iOS 7. static bool objcObjectIsClass(id object) { @@ -1352,6 +1351,36 @@ static bool usesNativeSwiftReferenceCounting_nonNull( swift_isUniquelyReferenced_nonNull_native((const HeapObject*)object); } +#if SWIFT_OBJC_INTEROP +// It would be nice to weak link instead of doing this, but we can't do that +// until the new API is in the versions of libobjc that we're linking against. +static bool isUniquelyReferenced(id object) { +#if OBJC_ISUNIQUELYREFERENCED_DEFINED + return objc_isUniquelyReferenced(object); +#else + auto objcIsUniquelyRefd = SWIFT_LAZY_CONSTANT(reinterpret_cast( + dlsym(RTLD_NEXT, "objc_isUniquelyReferenced"))); + + return objcIsUniquelyRefd && objcIsUniquelyRefd(object); +#endif /* OBJC_ISUNIQUELYREFERENCED_DEFINED */ +} +#endif + +bool swift::swift_isUniquelyReferenced_nonNull(const void *object) { + assert(object != nullptr); + +#if SWIFT_OBJC_INTEROP + if (isObjCTaggedPointer(object)) + return false; + + if (!usesNativeSwiftReferenceCounting_nonNull(object)) { + return isUniquelyReferenced(id_const_cast(object)); + } +#endif + return swift_isUniquelyReferenced_nonNull_native( + static_cast(object)); +} + // Given an object reference, return true iff it is non-nil and refers // to a native swift object with strong reference count of 1. bool swift::swift_isUniquelyReferencedNonObjC( @@ -1361,6 +1390,12 @@ static bool usesNativeSwiftReferenceCounting_nonNull( && swift_isUniquelyReferencedNonObjC_nonNull(object); } +// Given an object reference, return true if it is non-nil and refers +// to an ObjC or native swift object with a strong reference count of 1. +bool swift::swift_isUniquelyReferenced(const void *object) { + return object != nullptr && swift_isUniquelyReferenced_nonNull(object); +} + /// Return true if the given bits of a Builtin.BridgeObject refer to a /// native swift object whose strong reference count is 1. bool swift::swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject( @@ -1386,6 +1421,26 @@ static bool usesNativeSwiftReferenceCounting_nonNull( #endif } +/// Return true if the given bits of a Builtin.BridgeObject refer to +/// an object whose strong reference count is 1. +bool swift::swift_isUniquelyReferenced_nonNull_bridgeObject(uintptr_t bits) { + auto bridgeObject = reinterpret_cast(bits); + + if (isObjCTaggedPointer(bridgeObject)) + return false; + + const auto object = toPlainObject_unTagged_bridgeObject(bridgeObject); + +#if SWIFT_OBJC_INTEROP + return !isNonNative_unTagged_bridgeObject(bridgeObject) + ? swift_isUniquelyReferenced_nonNull_native( + (const HeapObject *)object) + : swift_isUniquelyReferenced_nonNull(object); +#else + return swift_isUniquelyReferenced_nonNull_native((const HeapObject *)object); +#endif +} + // Given a non-@objc object reference, return true iff the // object is non-nil and has a strong reference count greather than 1 bool swift::swift_isEscapingClosureAtFileLocation(const HeapObject *object, diff --git a/stdlib/public/runtime/SwiftTLSContext.cpp b/stdlib/public/runtime/SwiftTLSContext.cpp new file mode 100644 index 0000000000000..59657e89df1a5 --- /dev/null +++ b/stdlib/public/runtime/SwiftTLSContext.cpp @@ -0,0 +1,91 @@ +//===--- SwiftTLSContext.cpp ----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "SwiftTLSContext.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/Once.h" +#include "swift/Runtime/ThreadLocalStorage.h" + +using namespace swift; +using namespace swift::runtime; + +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + +SwiftTLSContext &SwiftTLSContext::get() { + static SwiftTLSContext TLSContext; + return TLSContext; +} + +#elif SWIFT_TLS_HAS_RESERVED_PTHREAD_SPECIFIC +// Use the reserved TSD key if possible. + +SwiftTLSContext &SwiftTLSContext::get() { + SwiftTLSContext *ctx = static_cast( + SWIFT_THREAD_GETSPECIFIC(SWIFT_RUNTIME_TLS_KEY)); + if (ctx) + return *ctx; + + static OnceToken_t setupToken; + SWIFT_ONCE_F( + setupToken, + [](void *) { + pthread_key_init_np(SWIFT_RUNTIME_TLS_KEY, [](void *pointer) { + delete static_cast(pointer); + }); + }, + nullptr); + + ctx = new SwiftTLSContext(); + SWIFT_THREAD_SETSPECIFIC(SWIFT_RUNTIME_TLS_KEY, ctx); + return *ctx; +} + +#elif __has_feature(cxx_thread_local) +// Second choice is direct language support for thread-locals. + +namespace { + +static thread_local SwiftTLSContext TLSContext; + +} // anonymous namespace + +SwiftTLSContext &SwiftTLSContext::get() { return TLSContext; } + +#else +// Use the platform thread-local data API. + +static __swift_thread_key_t createSwiftThreadKey() { + __swift_thread_key_t key; + int result = SWIFT_THREAD_KEY_CREATE(&key, [](void *pointer) { + delete static_cast(pointer); + }); + + if (result != 0) { + fatalError(0, "couldn't create thread key for exclusivity: %s\n", + strerror(result)); + } + return key; +} + +static SwiftTLSContext &getTLSContext() { + static __swift_thread_key_t key = createSwiftThreadKey(); + + SwiftTLSContext *ctx = + static_cast(SWIFT_THREAD_GETSPECIFIC(key)); + if (!ctx) { + ctx = new SwiftTLSContext(); + SWIFT_THREAD_SETSPECIFIC(key, ctx); + } + return *ctx; +} + +#endif diff --git a/stdlib/public/runtime/SwiftTLSContext.h b/stdlib/public/runtime/SwiftTLSContext.h new file mode 100644 index 0000000000000..94ffef8e0dcfe --- /dev/null +++ b/stdlib/public/runtime/SwiftTLSContext.h @@ -0,0 +1,38 @@ +//===--- SwiftTLSContext.h ------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_SWIFTTLSCONTEXT_H +#define SWIFT_RUNTIME_SWIFTTLSCONTEXT_H + +#include "ExclusivityPrivate.h" + +namespace swift { +namespace runtime { + +class SwiftTLSContext { +public: + /// The set of tracked accesses. + AccessSet accessSet; + + // The "implicit" boolean parameter which is passed to a dynamically + // replaceable function. + // If true, the original function should be executed instead of the + // replacement function. + bool CallOriginalOfReplacedFunction = false; + + static SwiftTLSContext &get(); +}; + +} // namespace runtime +} // namespace swift + +#endif diff --git a/stdlib/public/stubs/Availability.mm b/stdlib/public/stubs/Availability.mm index 392b0df20b52c..eea81cd98f6ba 100644 --- a/stdlib/public/stubs/Availability.mm +++ b/stdlib/public/stubs/Availability.mm @@ -21,7 +21,6 @@ #include "swift/Runtime/Debug.h" #include #include "../SwiftShims/FoundationShims.h" -#include struct os_system_version_s { unsigned int major; @@ -29,13 +28,12 @@ unsigned int patch; }; +// This is in libSystem, so it's OK to refer to it directly here +extern "C" int os_system_version_get_current_version(struct os_system_version_s * _Nonnull) SWIFT_RUNTIME_WEAK_IMPORT; + static os_system_version_s getOSVersion() { - auto lookup = - (int(*)(struct os_system_version_s * _Nonnull)) - dlsym(RTLD_DEFAULT, "os_system_version_get_current_version"); - struct os_system_version_s vers = { 0, 0, 0 }; - lookup(&vers); + os_system_version_get_current_version(&vers); return vers; } diff --git a/stdlib/public/stubs/CMakeLists.txt b/stdlib/public/stubs/CMakeLists.txt index 7e3ee7cc0ed23..5a8d8e36358f4 100644 --- a/stdlib/public/stubs/CMakeLists.txt +++ b/stdlib/public/stubs/CMakeLists.txt @@ -13,6 +13,7 @@ set(swift_stubs_objc_sources FoundationHelpers.mm OptionalBridgingHelper.mm Reflection.mm + SwiftNativeNSObject.mm SwiftNativeNSXXXBaseARC.m) set(swift_stubs_gyb_sources SwiftNativeNSXXXBase.mm.gyb) @@ -42,7 +43,7 @@ add_swift_target_library(swiftStdlibStubs INSTALL_IN_COMPONENT stdlib) -if("${SWIFT_PRIMARY_VARIANT_SDK}" IN_LIST SWIFT_APPLE_PLATFORMS) +if("${SWIFT_PRIMARY_VARIANT_SDK}" IN_LIST SWIFT_DARWIN_PLATFORMS) set_property(SOURCE SwiftNativeNSXXXBaseARC.m APPEND_STRING PROPERTY COMPILE_FLAGS diff --git a/stdlib/public/stubs/FoundationHelpers.mm b/stdlib/public/stubs/FoundationHelpers.mm index 8df00e63f83e6..39ac539179075 100644 --- a/stdlib/public/stubs/FoundationHelpers.mm +++ b/stdlib/public/stubs/FoundationHelpers.mm @@ -35,10 +35,11 @@ static CFHashCode(*_CFStringHashNSString)(id str); static CFTypeID(*_CFGetTypeID)(CFTypeRef obj); static CFTypeID _CFStringTypeID = 0; -static bool(*dyld_is_objc_constant)(DyldObjCConstantKind kind, - const void *addr); static swift_once_t initializeBridgingFuncsOnce; +extern "C" bool _dyld_is_objc_constant(DyldObjCConstantKind kind, + const void *addr) SWIFT_RUNTIME_WEAK_IMPORT; + static void _initializeBridgingFunctionsImpl(void *ctxt) { auto getStringTypeID = (CFTypeID(*)(void)) @@ -52,11 +53,6 @@ static void _initializeBridgingFunctionsImpl(void *ctxt) { _CFStringHashCString = (CFHashCode(*)(const uint8_t *, CFIndex))dlsym( RTLD_DEFAULT, "CFStringHashCString"); - if (dlsym(RTLD_NEXT, "objc_debug_tag60_permutations") /* tagged constant strings available */) { - dyld_is_objc_constant = (bool(*)(DyldObjCConstantKind, const void *))dlsym( - RTLD_NEXT, - "_dyld_is_objc_constant"); - } } static inline void initializeBridgingFunctions() { @@ -112,9 +108,8 @@ typedef __swift_uint8_t (*getCStringImplPtr)(id, __swift_uint8_t _swift_stdlib_dyld_is_objc_constant_string(const void *addr) { - initializeBridgingFunctions(); - if (!dyld_is_objc_constant) return false; - return dyld_is_objc_constant(dyld_objc_string_kind, addr) ? 1 : 0; + return (SWIFT_RUNTIME_WEAK_CHECK(_dyld_is_objc_constant) + && SWIFT_RUNTIME_WEAK_USE(_dyld_is_objc_constant(dyld_objc_string_kind, addr))) ? 1 : 0; } #endif diff --git a/stdlib/public/Concurrency/SwiftNativeNSObject.mm b/stdlib/public/stubs/SwiftNativeNSObject.mm similarity index 100% rename from stdlib/public/Concurrency/SwiftNativeNSObject.mm rename to stdlib/public/stubs/SwiftNativeNSObject.mm diff --git a/stdlib/public/stubs/ThreadLocalStorage.cpp b/stdlib/public/stubs/ThreadLocalStorage.cpp index 7a335e8e0d144..6ba52847cca5e 100644 --- a/stdlib/public/stubs/ThreadLocalStorage.cpp +++ b/stdlib/public/stubs/ThreadLocalStorage.cpp @@ -13,9 +13,9 @@ #include #include "../SwiftShims/ThreadLocalStorage.h" -#include "../runtime/ThreadLocalStorage.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Debug.h" +#include "swift/Runtime/ThreadLocalStorage.h" SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API void _stdlib_destroyTLS(void *); diff --git a/stdlib/toolchain/CMakeLists.txt b/stdlib/toolchain/CMakeLists.txt index 9cda30ee3e91b..4ef9f878b9c45 100644 --- a/stdlib/toolchain/CMakeLists.txt +++ b/stdlib/toolchain/CMakeLists.txt @@ -54,3 +54,4 @@ add_subdirectory(legacy_layouts) add_subdirectory(Compatibility50) add_subdirectory(Compatibility51) add_subdirectory(CompatibilityDynamicReplacements) +add_subdirectory(CompatibilityConcurrency) diff --git a/stdlib/toolchain/Compatibility50/CMakeLists.txt b/stdlib/toolchain/Compatibility50/CMakeLists.txt index b2db06eb24435..16824da43b5bf 100644 --- a/stdlib/toolchain/Compatibility50/CMakeLists.txt +++ b/stdlib/toolchain/Compatibility50/CMakeLists.txt @@ -4,7 +4,7 @@ add_swift_target_library("${library_name}" STATIC ProtocolConformance.cpp Overrides.cpp - TARGET_SDKS ${SWIFT_APPLE_PLATFORMS} + TARGET_SDKS ${SWIFT_DARWIN_PLATFORMS} C_COMPILE_FLAGS ${CXX_COMPILE_FLAGS} LINK_FLAGS ${CXX_LINK_FLAGS} diff --git a/stdlib/toolchain/Compatibility51/CMakeLists.txt b/stdlib/toolchain/Compatibility51/CMakeLists.txt index 63c0265402fcb..aeea7ae896874 100644 --- a/stdlib/toolchain/Compatibility51/CMakeLists.txt +++ b/stdlib/toolchain/Compatibility51/CMakeLists.txt @@ -5,7 +5,7 @@ add_swift_target_library("${library_name}" STATIC Overrides.cpp ProtocolConformance.cpp - TARGET_SDKS ${SWIFT_APPLE_PLATFORMS} + TARGET_SDKS ${SWIFT_DARWIN_PLATFORMS} C_COMPILE_FLAGS ${CXX_COMPILE_FLAGS} LINK_FLAGS ${CXX_LINK_FLAGS} diff --git a/stdlib/toolchain/CompatibilityConcurrency/CMakeLists.txt b/stdlib/toolchain/CompatibilityConcurrency/CMakeLists.txt new file mode 100644 index 0000000000000..6f08bc91dd550 --- /dev/null +++ b/stdlib/toolchain/CompatibilityConcurrency/CMakeLists.txt @@ -0,0 +1,31 @@ +set(library_name "swiftCompatibilityConcurrency") + +add_swift_target_library("${library_name}" STATIC + CompatibilityConcurrency.cpp + + TARGET_SDKS ${SWIFT_APPLE_PLATFORMS} + + C_COMPILE_FLAGS ${CXX_COMPILE_FLAGS} + LINK_FLAGS ${CXX_LINK_FLAGS} + SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + DEPLOYMENT_VERSION_OSX ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_OSX} + DEPLOYMENT_VERSION_IOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_IOS} + DEPLOYMENT_VERSION_TVOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_TVOS} + DEPLOYMENT_VERSION_WATCHOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS} + + INSTALL_IN_COMPONENT compiler + INSTALL_WITH_SHARED) + +# FIXME: We need a more flexible mechanism to add lipo targets generated by +# add_swift_target_library to the ALL target. Until then this hack is necessary +# to ensure these libraries build. +foreach(sdk ${SWIFT_SDKS}) + set(target_name "${library_name}-${SWIFT_SDK_${sdk}_LIB_SUBDIR}") + if(NOT TARGET "${target_name}") + continue() + endif() + + set_target_properties("${target_name}" + PROPERTIES + EXCLUDE_FROM_ALL FALSE) +endforeach() diff --git a/stdlib/toolchain/CompatibilityConcurrency/CompatibilityConcurrency.cpp b/stdlib/toolchain/CompatibilityConcurrency/CompatibilityConcurrency.cpp new file mode 100644 index 0000000000000..a53ee758470c2 --- /dev/null +++ b/stdlib/toolchain/CompatibilityConcurrency/CompatibilityConcurrency.cpp @@ -0,0 +1,15 @@ +//===--- CompatibilityConcurrency.cpp -------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +// Allow this library to get force-loaded by autolinking +__attribute__((weak, visibility("hidden"))) extern "C" char + _swift_FORCE_LOAD_$_swiftCompatibilityConcurrency = 0; diff --git a/stdlib/toolchain/CompatibilityDynamicReplacements/CMakeLists.txt b/stdlib/toolchain/CompatibilityDynamicReplacements/CMakeLists.txt index 6ff64b9123f23..db63a04992504 100644 --- a/stdlib/toolchain/CompatibilityDynamicReplacements/CMakeLists.txt +++ b/stdlib/toolchain/CompatibilityDynamicReplacements/CMakeLists.txt @@ -3,7 +3,7 @@ set(library_name "swiftCompatibilityDynamicReplacements") add_swift_target_library("${library_name}" STATIC DynamicReplaceable.cpp - TARGET_SDKS ${SWIFT_APPLE_PLATFORMS} + TARGET_SDKS ${SWIFT_DARWIN_PLATFORMS} C_COMPILE_FLAGS ${CXX_COMPILE_FLAGS} LINK_FLAGS ${CXX_LINK_FLAGS} diff --git a/stdlib/toolchain/CompatibilityDynamicReplacements/DynamicReplaceable.cpp b/stdlib/toolchain/CompatibilityDynamicReplacements/DynamicReplaceable.cpp index e2dcc24b6e774..c3467e2bf901e 100644 --- a/stdlib/toolchain/CompatibilityDynamicReplacements/DynamicReplaceable.cpp +++ b/stdlib/toolchain/CompatibilityDynamicReplacements/DynamicReplaceable.cpp @@ -17,9 +17,10 @@ // //===----------------------------------------------------------------------===// -#include "swift/Runtime/Once.h" #include "swift/Runtime/Exclusivity.h" -#include "../../public/runtime/ThreadLocalStorage.h" +#include "swift/Runtime/FunctionReplacement.h" +#include "swift/Runtime/Once.h" +#include "swift/Runtime/ThreadLocalStorage.h" using namespace swift; diff --git a/stdlib/tools/swift-reflection-test/messages.h b/stdlib/tools/swift-reflection-test/messages.h index 4b6950bc614ef..7e91f8e2945f8 100644 --- a/stdlib/tools/swift-reflection-test/messages.h +++ b/stdlib/tools/swift-reflection-test/messages.h @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// static const char *REQUEST_INSTANCE_KIND = "k\n"; +static const char *REQUEST_SHOULD_UNWRAP_CLASS_EXISTENTIAL = "u\n"; static const char *REQUEST_INSTANCE_ADDRESS = "i\n"; static const char *REQUEST_REFLECTION_INFO = "r\n"; static const char *REQUEST_IMAGES = "m\n"; diff --git a/stdlib/tools/swift-reflection-test/swift-reflection-test.c b/stdlib/tools/swift-reflection-test/swift-reflection-test.c index b7d3d840ef246..bea0e353f1f9e 100644 --- a/stdlib/tools/swift-reflection-test/swift-reflection-test.c +++ b/stdlib/tools/swift-reflection-test/swift-reflection-test.c @@ -238,6 +238,17 @@ PipeMemoryReader_receiveInstanceKind(const PipeMemoryReader *Reader) { return KindValue; } +static uint8_t PipeMemoryReader_receiveShouldUnwrapExistential( + const PipeMemoryReader *Reader) { + int WriteFD = PipeMemoryReader_getParentWriteFD(Reader); + write(WriteFD, REQUEST_SHOULD_UNWRAP_CLASS_EXISTENTIAL, 2); + uint8_t ShouldUnwrap = 0; + PipeMemoryReader_collectBytesFromPipe(Reader, &ShouldUnwrap, + sizeof(ShouldUnwrap)); + DEBUG_LOG("Requested if should unwrap class existential is", KindValue); + return ShouldUnwrap; +} + static uintptr_t PipeMemoryReader_receiveInstanceAddress(const PipeMemoryReader *Reader) { int WriteFD = PipeMemoryReader_getParentWriteFD(Reader); @@ -445,24 +456,25 @@ int reflectHeapObject(SwiftReflectionContextRef RC, return 1; } -int reflectExistential(SwiftReflectionContextRef RC, - const PipeMemoryReader Pipe, - swift_typeref_t MockExistentialTR) { +int reflectExistentialImpl( + SwiftReflectionContextRef RC, const PipeMemoryReader Pipe, + swift_typeref_t MockExistentialTR, + int (*ProjectExistentialFn)(SwiftReflectionContextRef, swift_addr_t, + swift_typeref_t, swift_typeref_t *, + swift_addr_t *)) { uintptr_t instance = PipeMemoryReader_receiveInstanceAddress(&Pipe); if (instance == 0) { // Child has no more instances to examine PipeMemoryReader_sendDoneMessage(&Pipe); return 0; } - printf("Instance pointer in child address space: 0x%lx\n", - instance); + printf("Instance pointer in child address space: 0x%lx\n", instance); swift_typeref_t InstanceTypeRef; swift_addr_t StartOfInstanceData = 0; - if (!swift_reflection_projectExistential(RC, instance, MockExistentialTR, - &InstanceTypeRef, - &StartOfInstanceData)) { + if (!ProjectExistentialFn(RC, instance, MockExistentialTR, &InstanceTypeRef, + &StartOfInstanceData)) { printf("swift_reflection_projectExistential failed.\n"); PipeMemoryReader_sendDoneMessage(&Pipe); return 0; @@ -476,10 +488,28 @@ int reflectExistential(SwiftReflectionContextRef RC, swift_reflection_dumpInfoForTypeRef(RC, InstanceTypeRef); printf("\n"); + printf("Start of instance data: 0x%" PRIx64 "\n", StartOfInstanceData); + printf("\n"); + PipeMemoryReader_sendDoneMessage(&Pipe); return 1; } +int reflectExistential(SwiftReflectionContextRef RC, + const PipeMemoryReader Pipe, + swift_typeref_t MockExistentialTR) { + return reflectExistentialImpl(RC, Pipe, MockExistentialTR, + swift_reflection_projectExistential); +} + +int reflectExistentialAndUnwrapClass(SwiftReflectionContextRef RC, + const PipeMemoryReader Pipe, + swift_typeref_t MockExistentialTR) { + return reflectExistentialImpl( + RC, Pipe, MockExistentialTR, + swift_reflection_projectExistentialAndUnwrapClass); +} + int reflectEnum(SwiftReflectionContextRef RC, const PipeMemoryReader Pipe) { static const char Name[] = MANGLING_PREFIX_STR "ypD"; @@ -706,23 +736,38 @@ int doDumpHeapInstance(const char *BinaryFilename) { break; case Existential: { static const char Name[] = MANGLING_PREFIX_STR "ypD"; - swift_typeref_t AnyTR - = swift_reflection_typeRefForMangledTypeName(RC, - Name, sizeof(Name)-1); - - printf("Reflecting an existential.\n"); - if (!reflectExistential(RC, Pipe, AnyTR)) - return EXIT_SUCCESS; + swift_typeref_t AnyTR = swift_reflection_typeRefForMangledTypeName( + RC, Name, sizeof(Name) - 1); + uint8_t ShouldUnwrap = + PipeMemoryReader_receiveShouldUnwrapExistential(&Pipe); + + if (ShouldUnwrap) { + printf("Reflecting an existential and unwrapping class.\n"); + if (!reflectExistentialAndUnwrapClass(RC, Pipe, AnyTR)) + return EXIT_SUCCESS; + } else { + printf("Reflecting an existential.\n"); + if (!reflectExistential(RC, Pipe, AnyTR)) + return EXIT_SUCCESS; + } break; } case ErrorExistential: { static const char ErrorName[] = MANGLING_PREFIX_STR "s5Error_pD"; - swift_typeref_t ErrorTR - = swift_reflection_typeRefForMangledTypeName(RC, - ErrorName, sizeof(ErrorName)-1); - printf("Reflecting an error existential.\n"); - if (!reflectExistential(RC, Pipe, ErrorTR)) - return EXIT_SUCCESS; + swift_typeref_t ErrorTR = swift_reflection_typeRefForMangledTypeName( + RC, ErrorName, sizeof(ErrorName) - 1); + uint8_t ShouldUnwrap = + PipeMemoryReader_receiveShouldUnwrapExistential(&Pipe); + + if (ShouldUnwrap) { + printf("Reflecting an error existential and unwrapping class.\n"); + if (!reflectExistentialAndUnwrapClass(RC, Pipe, ErrorTR)) + return EXIT_SUCCESS; + } else { + printf("Reflecting an error existential.\n"); + if (!reflectExistential(RC, Pipe, ErrorTR)) + return EXIT_SUCCESS; + } break; } case Closure: diff --git a/test/AutoDiff/IRGen/differentiable_function_type.swift b/test/AutoDiff/IRGen/differentiable_function_type.swift index fb8718cbc729d..deb22e5f01de0 100644 --- a/test/AutoDiff/IRGen/differentiable_function_type.swift +++ b/test/AutoDiff/IRGen/differentiable_function_type.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-ir -g %s +// RUN: %target-swift-frontend -emit-ir -g %s -requirement-machine=off import _Differentiation diff --git a/test/AutoDiff/IRGen/loadable_by_address.swift b/test/AutoDiff/IRGen/loadable_by_address.swift index dd8dee769c43f..0d688400f7d4a 100644 --- a/test/AutoDiff/IRGen/loadable_by_address.swift +++ b/test/AutoDiff/IRGen/loadable_by_address.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -c -Xllvm -sil-verify-after-pass=loadable-address %s -// RUN: %target-swift-frontend -emit-sil %s | %FileCheck %s -check-prefix=CHECK-SIL -// RUN: %target-swift-frontend -c -Xllvm -sil-print-after=loadable-address %s 2>&1 | %FileCheck %s -check-prefix=CHECK-LBA-SIL -// RUN: %target-run-simple-swift +// RUN: %target-swift-frontend -c -Xllvm -sil-verify-after-pass=loadable-address %s -requirement-machine=off +// RUN: %target-swift-frontend -emit-sil %s -requirement-machine=off | %FileCheck %s -check-prefix=CHECK-SIL +// RUN: %target-swift-frontend -c -Xllvm -sil-print-after=loadable-address %s -requirement-machine=off 2>&1 | %FileCheck %s -check-prefix=CHECK-LBA-SIL +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // `isLargeLoadableType` depends on the ABI and differs between architectures. diff --git a/test/AutoDiff/IRGen/loadable_by_address_cross_module.swift b/test/AutoDiff/IRGen/loadable_by_address_cross_module.swift index 4141c4127b307..e93d72985fd28 100644 --- a/test/AutoDiff/IRGen/loadable_by_address_cross_module.swift +++ b/test/AutoDiff/IRGen/loadable_by_address_cross_module.swift @@ -1,7 +1,7 @@ // First, check that LBA actually modifies the function, so that this test is useful. -// RUN: %target-swift-frontend -emit-sil %S/Inputs/loadable_by_address_cross_module.swift | %FileCheck %s -check-prefix=CHECK-MODULE-PRE-LBA -// RUN: %target-swift-frontend -c -Xllvm -sil-print-after=loadable-address %S/Inputs/loadable_by_address_cross_module.swift 2>&1 | %FileCheck %s -check-prefix=CHECK-MODULE-POST-LBA +// RUN: %target-swift-frontend -emit-sil %S/Inputs/loadable_by_address_cross_module.swift -requirement-machine=off | %FileCheck %s -check-prefix=CHECK-MODULE-PRE-LBA +// RUN: %target-swift-frontend -c -Xllvm -sil-print-after=loadable-address %S/Inputs/loadable_by_address_cross_module.swift -requirement-machine=off 2>&1 | %FileCheck %s -check-prefix=CHECK-MODULE-POST-LBA // CHECK-MODULE-PRE-LBA: sil {{.*}}LBAModifiedFunction{{.*}} $@convention(method) (Float, LargeLoadableType) -> Float // CHECK-MODULE-POST-LBA: sil {{.*}}LBAModifiedFunction{{.*}} $@convention(method) (Float, @in_constant LargeLoadableType) -> Float @@ -9,14 +9,14 @@ // Compile the module. // RUN: %empty-directory(%t) -// RUN: %target-build-swift-dylib(%t/%target-library-name(external)) %S/Inputs/loadable_by_address_cross_module.swift -emit-module -emit-module-path %t/external.swiftmodule -module-name external +// RUN: %target-build-swift-dylib(%t/%target-library-name(external)) %S/Inputs/loadable_by_address_cross_module.swift -emit-module -emit-module-path %t/external.swiftmodule -module-name external -Xfrontend -requirement-machine=off // Next, check that differentiability_witness_functions in the client get // correctly modified by LBA. -// RUN: %target-swift-frontend -emit-sil -I%t %s -// RUN: %target-swift-frontend -emit-sil -I%t %s | %FileCheck %s -check-prefix=CHECK-CLIENT-PRE-LBA -// RUN: %target-swift-frontend -c -I%t %s -Xllvm -sil-print-after=loadable-address 2>&1 | %FileCheck %s -check-prefix=CHECK-CLIENT-POST-LBA +// RUN: %target-swift-frontend -emit-sil -I%t %s -requirement-machine=off +// RUN: %target-swift-frontend -emit-sil -I%t %s -requirement-machine=off | %FileCheck %s -check-prefix=CHECK-CLIENT-PRE-LBA +// RUN: %target-swift-frontend -c -I%t %s -Xllvm -sil-print-after=loadable-address -requirement-machine=off 2>&1 | %FileCheck %s -check-prefix=CHECK-CLIENT-POST-LBA // CHECK-CLIENT-PRE-LBA: differentiability_witness_function [jvp] [reverse] [parameters 0 1] [results 0] @${{.*}}LBAModifiedFunction{{.*}} : $@convention(method) <τ_0_0> (Float, LargeLoadableType<τ_0_0>) -> Float // CHECK-CLIENT-PRE-LBA: differentiability_witness_function [vjp] [reverse] [parameters 0 1] [results 0] @${{.*}}LBAModifiedFunction{{.*}} : $@convention(method) <τ_0_0> (Float, LargeLoadableType<τ_0_0>) -> Float @@ -26,7 +26,7 @@ // Finally, execute the test. -// RUN: %target-build-swift -I%t -L%t %s -o %t/a.out %target-rpath(%t) -L%t -lexternal +// RUN: %target-build-swift -I%t -L%t %s -o %t/a.out %target-rpath(%t) -L%t -lexternal -Xfrontend -requirement-machine=off // RUN: %target-codesign %t/a.out // RUN: %target-codesign %t/%target-library-name(external) // RUN: %target-run %t/a.out %t/%target-library-name(external) diff --git a/test/AutoDiff/SIL/Parse/sildeclref.sil b/test/AutoDiff/SIL/Parse/sildeclref.sil index fdf3e2ee84ec7..1b29d074e6d8c 100644 --- a/test/AutoDiff/SIL/Parse/sildeclref.sil +++ b/test/AutoDiff/SIL/Parse/sildeclref.sil @@ -1,5 +1,4 @@ -// RUN: %target-sil-opt %s -module-name=sildeclref_parse | %target-sil-opt -module-name=sildeclref_parse | %FileCheck %s - +// RUN: %target-sil-opt %s -module-name=sildeclref_parse -requirement-machine off | %target-sil-opt -module-name=sildeclref_parse -requirement-machine off | %FileCheck %s // Parse AutoDiff derivative SILDeclRefs via `witness_method` and `class_method` instructions. import Swift diff --git a/test/AutoDiff/SILGen/autodiff_builtins.swift b/test/AutoDiff/SILGen/autodiff_builtins.swift index a3e172030f9f3..17acc1cd5f19e 100644 --- a/test/AutoDiff/SILGen/autodiff_builtins.swift +++ b/test/AutoDiff/SILGen/autodiff_builtins.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -parse-stdlib -emit-silgen %s | %FileCheck %s +// RUN: %target-swift-frontend -parse-stdlib -emit-silgen %s -requirement-machine=off | %FileCheck %s import _Differentiation import Swift diff --git a/test/AutoDiff/SILGen/reabstraction.swift b/test/AutoDiff/SILGen/reabstraction.swift index 36cd5cf9a8280..d965bf4dd2966 100644 --- a/test/AutoDiff/SILGen/reabstraction.swift +++ b/test/AutoDiff/SILGen/reabstraction.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -requirement-machine=off | %FileCheck %s import _Differentiation diff --git a/test/AutoDiff/SILGen/vtable.swift b/test/AutoDiff/SILGen/vtable.swift index 882031b8f9c16..723d3460e1f7c 100644 --- a/test/AutoDiff/SILGen/vtable.swift +++ b/test/AutoDiff/SILGen/vtable.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -requirement-machine=off | %FileCheck %s // Test derivative function vtable entries for `@differentiable` class members: // - Methods. diff --git a/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift b/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift index 35217504404f7..f20586d855f4a 100644 --- a/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift +++ b/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -verify %s +// RUN: %target-swift-frontend -emit-sil -verify -requirement-machine=off %s // Test differentiation transform diagnostics. diff --git a/test/AutoDiff/SILOptimizer/generics.swift b/test/AutoDiff/SILOptimizer/generics.swift index 6c1c0ae8d27a3..efab390cdffe4 100644 --- a/test/AutoDiff/SILOptimizer/generics.swift +++ b/test/AutoDiff/SILOptimizer/generics.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-emit-sil -verify %s | %FileCheck %s -check-prefix=CHECK-SIL +// RUN: %target-swift-emit-sil -verify %s -requirement-machine=off | %FileCheck %s -check-prefix=CHECK-SIL import _Differentiation diff --git a/test/AutoDiff/SILOptimizer/property_wrappers.swift b/test/AutoDiff/SILOptimizer/property_wrappers.swift index 4ed910ae5fd0b..f5a03b76912b7 100644 --- a/test/AutoDiff/SILOptimizer/property_wrappers.swift +++ b/test/AutoDiff/SILOptimizer/property_wrappers.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -verify %s %S/Inputs/nontrivial_loadable_type.swift +// RUN: %target-swift-frontend -emit-sil -verify %s %S/Inputs/nontrivial_loadable_type.swift -requirement-machine=off // REQUIRES: asserts // Test property wrapper differentiation coverage for a variety of property diff --git a/test/AutoDiff/SILOptimizer/semantic_member_accessors_sil.swift b/test/AutoDiff/SILOptimizer/semantic_member_accessors_sil.swift index 08b404639641d..3bdc6b4dfb1b7 100644 --- a/test/AutoDiff/SILOptimizer/semantic_member_accessors_sil.swift +++ b/test/AutoDiff/SILOptimizer/semantic_member_accessors_sil.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -Xllvm -sil-print-after=differentiation %s -module-name null -o /dev/null 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -Xllvm -sil-print-after=differentiation %s -module-name null -o /dev/null -requirement-machine=off 2>&1 | %FileCheck %s // Test differentiation of semantic member accessors: // - Stored property accessors. diff --git a/test/AutoDiff/Sema/differentiable_attr_type_checking.swift b/test/AutoDiff/Sema/differentiable_attr_type_checking.swift index 3b5972d912470..989fc279d76e5 100644 --- a/test/AutoDiff/Sema/differentiable_attr_type_checking.swift +++ b/test/AutoDiff/Sema/differentiable_attr_type_checking.swift @@ -400,6 +400,16 @@ let _: @differentiable(reverse) (Float, Float) -> TF_521 = { r, i in TF_521(real: r, imaginary: i) } +// expected-error @+1 {{result type 'TF_521' does not conform to 'Differentiable', but the enclosing function type is '@differentiable'}} +let _: @differentiable(reverse) (_, _) -> TF_521 = { (r: Float, i: Float) in + TF_521(real: r, imaginary: i) +} + +// expected-error @+1 {{result type 'TF_521' does not conform to 'Differentiable', but the enclosing function type is '@differentiable'}} +let _: @differentiable(reverse) (Float, Float) -> _ = { r, i in + TF_521(real: r, imaginary: i) +} + // TF-296: Infer `@differentiable` wrt parameters to be to all parameters that conform to `Differentiable`. @differentiable(reverse) diff --git a/test/AutoDiff/Sema/differentiable_func_type.swift b/test/AutoDiff/Sema/differentiable_func_type.swift index 832a7411460bb..3493f50e16bca 100644 --- a/test/AutoDiff/Sema/differentiable_func_type.swift +++ b/test/AutoDiff/Sema/differentiable_func_type.swift @@ -45,8 +45,7 @@ let _: @differentiable(_linear) (Float) -> Float func test1(_: @differentiable(reverse) (T) -> @differentiable(reverse) (U) -> Float) {} // expected-error @+1 {{result type '(U) -> Float' does not conform to 'Differentiable', but the enclosing function type is '@differentiable'}} func test2(_: @differentiable(reverse) (T) -> (U) -> Float) {} -// expected-error @+2 {{result type 'Int' does not conform to 'Differentiable', but the enclosing function type is '@differentiable'}} -// expected-error @+1 {{result type '@differentiable(reverse) (U) -> Int' does not conform to 'Differentiable', but the enclosing function type is '@differentiable'}} +// expected-error @+1 {{result type 'Int' does not conform to 'Differentiable', but the enclosing function type is '@differentiable'}} func test3(_: @differentiable(reverse) (T) -> @differentiable(reverse) (U) -> Int) {} // expected-error @+1 {{result type '(U) -> Int' does not conform to 'Differentiable', but the enclosing function type is '@differentiable'}} func test4(_: @differentiable(reverse) (T) -> (U) -> Int) {} @@ -189,7 +188,6 @@ extension Vector: Differentiable where T: Differentiable { // expected-note@+1 2 {{found this candidate}} func inferredConformancesGeneric(_: @differentiable(reverse) (Vector) -> Vector) {} -// expected-note @+5 2 {{found this candidate}} // expected-error @+4 {{generic signature requires types 'Vector' and 'Vector.TangentVector' to be the same}} // expected-error @+3 {{generic signature requires types 'Vector' and 'Vector.TangentVector' to be the same}} // expected-error @+2 {{parameter type 'Vector' does not conform to 'Differentiable' and satisfy 'Vector == Vector.TangentVector', but the enclosing function type is '@differentiable(_linear)'}} @@ -203,7 +201,8 @@ func nondiff(x: Vector) -> Vector {} // expected-error @+1 {{no exact matches in call to global function 'inferredConformancesGeneric'}} inferredConformancesGeneric(nondiff) -// expected-error @+1 {{no exact matches in call to global function 'inferredConformancesGenericLinear'}} +// expected-error @+2 {{global function 'inferredConformancesGenericLinear' requires that 'Int' conform to 'Differentiable'}} +// expected-error @+1 {{global function 'inferredConformancesGenericLinear' requires that 'Int' conform to 'Differentiable'}} inferredConformancesGenericLinear(nondiff) func diff(x: Vector) -> Vector {} @@ -228,13 +227,15 @@ extension Linear: Differentiable where T: Differentiable, T == T.TangentVector { // expected-note @+1 2 {{found this candidate}} func inferredConformancesGeneric(_: @differentiable(reverse) (Linear) -> Linear) {} -// expected-note @+1 2 {{found this candidate}} +// expected-note @+2 2 {{where 'T' = 'Int'}} +// expected-note @+1 2 {{where 'U' = 'Int'}} func inferredConformancesGenericLinear(_: @differentiable(_linear) (Linear) -> Linear) {} func nondiff(x: Linear) -> Linear {} // expected-error @+1 {{no exact matches in call to global function 'inferredConformancesGeneric'}} inferredConformancesGeneric(nondiff) -// expected-error @+1 {{no exact matches in call to global function 'inferredConformancesGenericLinear'}} +// expected-error @+2 {{global function 'inferredConformancesGenericLinear' requires that 'Int' conform to 'Differentiable'}} +// expected-error @+1 {{global function 'inferredConformancesGenericLinear' requires that 'Int' conform to 'Differentiable'}} inferredConformancesGenericLinear(nondiff) func diff(x: Linear) -> Linear {} diff --git a/test/AutoDiff/compiler_crashers/pr32302-autodiff-generictypeparamdecl-has-incorrect-depth.swift b/test/AutoDiff/compiler_crashers/pr32302-autodiff-generictypeparamdecl-has-incorrect-depth.swift new file mode 100644 index 0000000000000..ef90ede8285cd --- /dev/null +++ b/test/AutoDiff/compiler_crashers/pr32302-autodiff-generictypeparamdecl-has-incorrect-depth.swift @@ -0,0 +1,58 @@ +// RUN: %empty-directory(%t) +// RUN: not --crash %target-build-swift -emit-module -module-name pr32302 -emit-module-path %t/pr32302.swiftmodule -swift-version 5 -c %S/pr32302-autodiff-generictypeparamdecl-has-incorrect-depth.swift -Xfrontend -requirement-machine=off +// XFAIL: * + +// pr32302 / pr32343 / pr38745 : reproduce assert with _Differentiation where +// ASTVerifier.cpp asserts "GenericTypeParamDecl has incorrect depth" + +import _Differentiation + +public protocol Layer: Differentiable { + associatedtype Input: Differentiable + associatedtype Output: Differentiable + var differentiableVectorView: TangentVector { get } + + @differentiable(reverse) + func callAsFunction(_ input: Input) -> Output +} + +extension Differentiable { + @differentiable(reverse) + public func sequenced(through l1: L1, _ l2: L2) -> L2.Output + where L1.Input == Self, L1.Output == L2.Input { + let o1 = l1(self) + return l2(o1) + } +} + +// GenericTypeParamDecl has incorrect depth +// Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project and the crash backtrace. +// Stack dump: +// 0. Program arguments: /work/software/swift-stocktoolchain/build/ds/swift-linux-x86_64/bin/swift-frontend -frontend -merge-modules -emit-module /tmp/pr32302-autodiff-generictypeparamdecl-has-incorrect-depth-acc95c.swiftmodule -parse-as-library -disable-diagnostic-passes -disable-sil-perf-optzns -target x86_64-unknown-linux-gnu -warn-on-potentially-unavailable-enum-case -disable-objc-interop -module-cache-path /work/software/swift-stocktoolchain/build/ds/swift-linux-x86_64/swift-test-results/x86_64-unknown-linux-gnu/clang-module-cache -swift-version 5 -define-availability "SwiftStdlib 5.5:macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0" -requirement-machine=off -emit-module-doc-path /work/software/swift-stocktoolchain/build/ds/swift-linux-x86_64/test-linux-x86_64/AutoDiff/compiler_crashers/Output/pr32302-autodiff-generictypeparamdecl-has-incorrect-depth.swift.tmp/pr32302.swiftdoc -emit-module-source-info-path /work/software/swift-stocktoolchain/build/ds/swift-linux-x86_64/test-linux-x86_64/AutoDiff/compiler_crashers/Output/pr32302-autodiff-generictypeparamdecl-has-incorrect-depth.swift.tmp/pr32302.swiftsourceinfo -module-name pr32302 -o /work/software/swift-stocktoolchain/build/ds/swift-linux-x86_64/test-linux-x86_64/AutoDiff/compiler_crashers/Output/pr32302-autodiff-generictypeparamdecl-has-incorrect-depth.swift.tmp/pr32302.swiftmodule +// 1. Swift version 5.6-dev (LLVM ba0b85f590c1ba2, Swift 319b3e64aaeb252) +// 2. Compiling with the current language version +// 3. While verifying GenericTypeParamDecl 'τ_1_0' (in module 'pr32302') +// #0 0x000000000632fb13 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /work/software/swift-stocktoolchain/llvm-project/llvm/lib/Support/Unix/Signals.inc:563:13 +// #1 0x000000000632dda0 llvm::sys::RunSignalHandlers() /work/software/swift-stocktoolchain/llvm-project/llvm/lib/Support/Signals.cpp:72:18 +// #2 0x000000000632fe95 SignalHandler(int) /work/software/swift-stocktoolchain/llvm-project/llvm/lib/Support/Unix/Signals.inc:0:3 +// #3 0x00007ffbfc0ab3c0 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x153c0) +// #4 0x00007ffbfbb3618b raise (/lib/x86_64-linux-gnu/libc.so.6+0x4618b) +// #5 0x00007ffbfbb15859 abort (/lib/x86_64-linux-gnu/libc.so.6+0x25859) +// #6 0x0000000002417d95 llvm::MutableArrayRef::operator[](unsigned long) const /work/software/swift-stocktoolchain/llvm-project/llvm/include/llvm/ADT/ArrayRef.h:425:7 +// #7 0x0000000002417d95 (anonymous namespace)::Verifier::verifyCheckedAlways(swift::GenericTypeParamDecl*) /work/software/swift-stocktoolchain/swift/lib/AST/ASTVerifier.cpp:2866:11 +// #8 0x0000000002417d95 swift::GenericTypeParamDecl* (anonymous namespace)::Verifier::dispatchVisitPost(swift::GenericTypeParamDecl*) /work/software/swift-stocktoolchain/swift/lib/AST/ASTVerifier.cpp:426:9 +// #9 0x0000000002417d95 (anonymous namespace)::Verifier::walkToDeclPost(swift::Decl*) /work/software/swift-stocktoolchain/swift/include/swift/AST/DeclNodes.def:159:7 +// #10 0x00000000024214c7 (anonymous namespace)::Traversal::doIt(swift::Decl*) /work/software/swift-stocktoolchain/swift/lib/AST/ASTWalker.cpp:1275:12 +// #11 0x0000000002421433 swift::Decl::walk(swift::ASTWalker&) /work/software/swift-stocktoolchain/swift/lib/AST/ASTWalker.cpp:1909:3 +// #12 0x000000000240c6dd swift::verify(swift::Decl*) /work/software/swift-stocktoolchain/swift/lib/AST/ASTVerifier.cpp:3761:1 +// #13 0x0000000001285157 swift::ModuleFile::verify() const /work/software/swift-stocktoolchain/swift/lib/Serialization/ModuleFile.cpp:1244:3 +// #14 0x00000000011ead9a swift::SerializedModuleLoaderBase::verifyAllModules() /work/software/swift-stocktoolchain/swift/lib/Serialization/SerializedModuleLoader.cpp:1268:39 +// #15 0x000000000236546a swift::ASTContext::verifyAllLoadedModules() const /work/software/swift-stocktoolchain/swift/lib/AST/ASTContext.cpp:1794:21 +// #16 0x000000000058259d performEndOfPipelineActions(swift::CompilerInstance&) /work/software/swift-stocktoolchain/swift/lib/FrontendTool/FrontendTool.cpp:933:37 +// #17 0x000000000057faa7 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) /work/software/swift-stocktoolchain/swift/lib/FrontendTool/FrontendTool.cpp:1252:10 +// #18 0x000000000057ef21 swift::performFrontend(llvm::ArrayRef, char const*, void*, swift::FrontendObserver*) /work/software/swift-stocktoolchain/swift/lib/FrontendTool/FrontendTool.cpp:2154:8 +// #19 0x00000000004b36e7 run_driver(llvm::StringRef, llvm::ArrayRef) /work/software/swift-stocktoolchain/swift/lib/DriverTool/driver.cpp:196:7 +// #20 0x00000000004b3312 swift::mainEntry(int, char const**) /work/software/swift-stocktoolchain/swift/lib/DriverTool/driver.cpp:402:5 +// #21 0x00000000004b2c72 main /work/software/swift-stocktoolchain/swift/tools/driver/driver.cpp:20:3 +// #22 0x00007ffbfbb170b3 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b3) +// #23 0x00000000004b2b7e _start (/work/software/swift-stocktoolchain/build/ds/swift-linux-x86_64/bin/swift-frontend+0x4b2b7e) diff --git a/test/AutoDiff/compiler_crashers_fixed/rdar74087329-debug-scope-trampoline-blocks.swift b/test/AutoDiff/compiler_crashers_fixed/rdar74087329-debug-scope-trampoline-blocks.swift index 3e0bb5a9de761..7423826a470a1 100644 --- a/test/AutoDiff/compiler_crashers_fixed/rdar74087329-debug-scope-trampoline-blocks.swift +++ b/test/AutoDiff/compiler_crashers_fixed/rdar74087329-debug-scope-trampoline-blocks.swift @@ -1,5 +1,5 @@ -// RUN: %target-build-swift %s -// RUN: %target-swift-frontend -c -g -Xllvm -verify-di-holes=true %s +// RUN: %target-build-swift %s -Xfrontend -requirement-machine=off +// RUN: %target-swift-frontend -c -g -Xllvm -verify-di-holes=true %s -requirement-machine=off // rdar://74087329 (DI verification failure with trampoline blocks in VJP) diff --git a/test/AutoDiff/compiler_crashers_fixed/sr12641-silgen-immutable-address-use-verification-failure.swift b/test/AutoDiff/compiler_crashers_fixed/sr12641-silgen-immutable-address-use-verification-failure.swift index ff91b0ece3bf3..7ee112184dffc 100644 --- a/test/AutoDiff/compiler_crashers_fixed/sr12641-silgen-immutable-address-use-verification-failure.swift +++ b/test/AutoDiff/compiler_crashers_fixed/sr12641-silgen-immutable-address-use-verification-failure.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -enable-resilience -emit-sil -verify %s +// RUN: %target-swift-frontend -enable-resilience -emit-sil -verify %s -requirement-machine=off // SR-12641: SILGen verification error regarding `ImmutableAddressUseVerifier` and AutoDiff-generated code. diff --git a/test/AutoDiff/compiler_crashers_fixed/sr12744-unhandled-pullback-indirect-result.swift b/test/AutoDiff/compiler_crashers_fixed/sr12744-unhandled-pullback-indirect-result.swift index ba195b15ec74b..e9ecfc977b6f3 100644 --- a/test/AutoDiff/compiler_crashers_fixed/sr12744-unhandled-pullback-indirect-result.swift +++ b/test/AutoDiff/compiler_crashers_fixed/sr12744-unhandled-pullback-indirect-result.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -verify %s +// RUN: %target-swift-frontend -emit-sil -verify %s -requirement-machine=off // SR-12744: Pullback generation crash for unhandled indirect result. // May be due to inconsistent derivative function type calculation logic in diff --git a/test/AutoDiff/compiler_crashers_fixed/sr13933-vjpcloner-apply-multiple-consuming-users.swift b/test/AutoDiff/compiler_crashers_fixed/sr13933-vjpcloner-apply-multiple-consuming-users.swift index f083d0081ba63..54ed3e981f06b 100644 --- a/test/AutoDiff/compiler_crashers_fixed/sr13933-vjpcloner-apply-multiple-consuming-users.swift +++ b/test/AutoDiff/compiler_crashers_fixed/sr13933-vjpcloner-apply-multiple-consuming-users.swift @@ -1,4 +1,4 @@ -// RUN: %target-build-swift %s +// RUN: %target-build-swift %s -Xfrontend -requirement-machine=off // SR-13933: Fix "multiple consuming users" ownership error caused by // `VJPCloner::visitApply` related to `@differentiable`-function-typed callees. diff --git a/test/AutoDiff/compiler_crashers_fixed/sr14240-symbol-in-ir-file-not-tbd-file.swift b/test/AutoDiff/compiler_crashers_fixed/sr14240-symbol-in-ir-file-not-tbd-file.swift new file mode 100644 index 0000000000000..958a03a278ac4 --- /dev/null +++ b/test/AutoDiff/compiler_crashers_fixed/sr14240-symbol-in-ir-file-not-tbd-file.swift @@ -0,0 +1,26 @@ +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) + +// REQUIRES: executable_test + +// SR-14240: Error: symbol 'powTJfSSpSr' (powTJfSSpSr) is in generated IR file, +// but not in TBD file. + +import _Differentiation + +#if canImport(Darwin) + import Darwin +#elseif canImport(Glibc) + import Glibc +#elseif os(Windows) + import CRT +#else +#error("Unsupported platform") +#endif + +@inlinable +@derivative(of: pow) +func powVJP( + _ base: Double, _ exponent: Double +) -> (value: Double, pullback: (Double) -> (Double, Double)) { + fatalError() +} diff --git a/test/AutoDiff/compiler_crashers_fixed/sr14625-over-consume-in-subset-parameters-thunk.swift b/test/AutoDiff/compiler_crashers_fixed/sr14625-over-consume-in-subset-parameters-thunk.swift new file mode 100644 index 0000000000000..f26549acba853 --- /dev/null +++ b/test/AutoDiff/compiler_crashers_fixed/sr14625-over-consume-in-subset-parameters-thunk.swift @@ -0,0 +1,31 @@ +// RUN: %target-build-swift %s + +// SR-14625: An over-consume in a subset parameters thunk detected after +// enabling OSSA. + +import _Differentiation + +struct Type2: Differentiable { + var test1: Double + + @differentiable(reverse) + public init(test1: Double) { + self.test1 = test1 + } +} + +struct Type1: Differentiable { + var test1: Double + var test3: [Type2] + + @differentiable(reverse) + public init(test1: Double, test3: [Type2]) { + self.test1 = test1 + self.test3 = test3 + } +} + +@differentiable(reverse) +func ingestValue(val1: Double, val2: Double) -> Type1 { + return Type1(test1: val1 * val2, test3: []) +} diff --git a/test/AutoDiff/compiler_crashers_fixed/tf1202-differentiability-witness-dead-function-elimination.swift b/test/AutoDiff/compiler_crashers_fixed/tf1202-differentiability-witness-dead-function-elimination.swift index e0564a9adfe31..8927a18df81a2 100644 --- a/test/AutoDiff/compiler_crashers_fixed/tf1202-differentiability-witness-dead-function-elimination.swift +++ b/test/AutoDiff/compiler_crashers_fixed/tf1202-differentiability-witness-dead-function-elimination.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -emit-module -module-name tf1202 -emit-module-path %t/tf1202.swiftmodule %S/Inputs/tf1202-differentiability-witness-dead-function-elimination.swift -// RUN: %target-build-swift -I%t -emit-module -O %s +// RUN: %target-build-swift -emit-module -module-name tf1202 -emit-module-path %t/tf1202.swiftmodule %S/Inputs/tf1202-differentiability-witness-dead-function-elimination.swift -Xfrontend -requirement-machine=off +// RUN: %target-build-swift -I%t -emit-module -O %s -Xfrontend -requirement-machine=off // TF-1202: test bug where DeadFunctionElimination eliminated the // SILFunction for `func identity` even though a differentiability witness for it diff --git a/test/AutoDiff/compiler_crashers_fixed/tf1232-autodiff-generated-declaration-mangling.swift b/test/AutoDiff/compiler_crashers_fixed/tf1232-autodiff-generated-declaration-mangling.swift index bd76554f6ab30..708bcc50b3e12 100644 --- a/test/AutoDiff/compiler_crashers_fixed/tf1232-autodiff-generated-declaration-mangling.swift +++ b/test/AutoDiff/compiler_crashers_fixed/tf1232-autodiff-generated-declaration-mangling.swift @@ -1,4 +1,6 @@ // RUN: %target-build-swift -g %s +// This test occaisionally fails to link. +// REQUIRES: SR14775 // TF-1232: IRGenDebugInfo crash due to lack of proper mangling for // AutoDiff-generated declarations: linear map structs and branching trace diff --git a/test/AutoDiff/mangling.swift b/test/AutoDiff/mangling.swift index dc12913f7ad58..3fbb4c60bdf3d 100644 --- a/test/AutoDiff/mangling.swift +++ b/test/AutoDiff/mangling.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -enable-experimental-forward-mode-differentiation -module-name=mangling -verify %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -enable-experimental-forward-mode-differentiation -module-name=mangling -verify %s -requirement-machine=off | %FileCheck %s import _Differentiation diff --git a/test/AutoDiff/stdlib/anydifferentiable.swift b/test/AutoDiff/stdlib/anydifferentiable.swift index 8657fb8141ddd..d4a90a49e0a21 100644 --- a/test/AutoDiff/stdlib/anydifferentiable.swift +++ b/test/AutoDiff/stdlib/anydifferentiable.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import _Differentiation diff --git a/test/AutoDiff/stdlib/collection_higher_order_functions.swift b/test/AutoDiff/stdlib/collection_higher_order_functions.swift index 7528233165a9a..5af64bbf08e99 100644 --- a/test/AutoDiff/stdlib/collection_higher_order_functions.swift +++ b/test/AutoDiff/stdlib/collection_higher_order_functions.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import _Differentiation diff --git a/test/AutoDiff/stdlib/derivative_customization.swift b/test/AutoDiff/stdlib/derivative_customization.swift index aa10e2e95ea71..f4db8b3771352 100644 --- a/test/AutoDiff/stdlib/derivative_customization.swift +++ b/test/AutoDiff/stdlib/derivative_customization.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import DifferentiationUnittest diff --git a/test/AutoDiff/stdlib/differential_operators.swift.gyb b/test/AutoDiff/stdlib/differential_operators.swift.gyb index f623eec74a138..316f803c8c769 100644 --- a/test/AutoDiff/stdlib/differential_operators.swift.gyb +++ b/test/AutoDiff/stdlib/differential_operators.swift.gyb @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %gyb %s -o %t/differential_operators.swift -// RUN: %target-build-swift %t/differential_operators.swift -o %t/differential_operators +// RUN: %target-build-swift %t/differential_operators.swift -o %t/differential_operators -Xfrontend -requirement-machine=off // RUN: %target-codesign %t/differential_operators // RUN: %target-run %t/differential_operators // REQUIRES: executable_test diff --git a/test/AutoDiff/stdlib/floating_point.swift.gyb b/test/AutoDiff/stdlib/floating_point.swift.gyb index de94184c4f62f..a6799f3956343 100644 --- a/test/AutoDiff/stdlib/floating_point.swift.gyb +++ b/test/AutoDiff/stdlib/floating_point.swift.gyb @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swiftgyb(-Xfrontend -enable-experimental-forward-mode-differentiation) +// RUN: %target-run-simple-swiftgyb(-Xfrontend -enable-experimental-forward-mode-differentiation -Xfrontend -requirement-machine=off) // REQUIRES: executable_test #if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) diff --git a/test/AutoDiff/stdlib/simd.swift b/test/AutoDiff/stdlib/simd.swift index 581a1e15da15f..2f3fad7e93dc6 100644 --- a/test/AutoDiff/stdlib/simd.swift +++ b/test/AutoDiff/stdlib/simd.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // Would fail due to unavailability of swift_autoDiffCreateLinearMapContext. diff --git a/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb b/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb index e9b397a89256b..bc112ef3520fa 100644 --- a/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb +++ b/test/AutoDiff/stdlib/tgmath_derivatives.swift.gyb @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swiftgyb(-Xfrontend -enable-experimental-forward-mode-differentiation) +// RUN: %target-run-simple-swiftgyb(-Xfrontend -enable-experimental-forward-mode-differentiation -Xfrontend -requirement-machine=off) // REQUIRES: executable_test #if canImport(Darwin) diff --git a/test/AutoDiff/validation-test/Inputs/AutoDiffTypes.swift b/test/AutoDiff/validation-test/Inputs/AutoDiffTypes.swift new file mode 100644 index 0000000000000..536a069c1c1be --- /dev/null +++ b/test/AutoDiff/validation-test/Inputs/AutoDiffTypes.swift @@ -0,0 +1,5 @@ +import _Differentiation + +public struct HasAutoDiffTypes { + public var aFunction: @differentiable(reverse) (Float) -> Float +} diff --git a/test/AutoDiff/validation-test/Inputs/main.swift b/test/AutoDiff/validation-test/Inputs/main.swift new file mode 100644 index 0000000000000..2b7d9680cc58f --- /dev/null +++ b/test/AutoDiff/validation-test/Inputs/main.swift @@ -0,0 +1 @@ +// this file intentionally left blank diff --git a/test/AutoDiff/validation-test/address_only_tangentvector.swift b/test/AutoDiff/validation-test/address_only_tangentvector.swift index 9c703b16a9626..586003b95dcab 100644 --- a/test/AutoDiff/validation-test/address_only_tangentvector.swift +++ b/test/AutoDiff/validation-test/address_only_tangentvector.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // Would fail due to unavailability of swift_autoDiffCreateLinearMapContext. diff --git a/test/AutoDiff/validation-test/array.swift b/test/AutoDiff/validation-test/array.swift index 4ad770f31a1e4..aa9023f044a8b 100644 --- a/test/AutoDiff/validation-test/array.swift +++ b/test/AutoDiff/validation-test/array.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // Would fail due to unavailability of swift_autoDiffCreateLinearMapContext. diff --git a/test/AutoDiff/validation-test/class_differentiation.swift b/test/AutoDiff/validation-test/class_differentiation.swift index bc3f4b926eb4a..1d39688932601 100644 --- a/test/AutoDiff/validation-test/class_differentiation.swift +++ b/test/AutoDiff/validation-test/class_differentiation.swift @@ -1,6 +1,6 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // NOTE: Verify whether forward-mode differentiation crashes. It currently does. -// RUN: not --crash %target-swift-frontend -enable-experimental-forward-mode-differentiation -emit-sil %s +// RUN: not --crash %target-swift-frontend -enable-experimental-forward-mode-differentiation -emit-sil %s -requirement-machine=off // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/control_flow.swift b/test/AutoDiff/validation-test/control_flow.swift index 62a796a79895e..aacac0eff2fa4 100644 --- a/test/AutoDiff/validation-test/control_flow.swift +++ b/test/AutoDiff/validation-test/control_flow.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // FIXME(SR-12741): Enable test for all platforms after debugging diff --git a/test/AutoDiff/validation-test/cross_module_derivative_attr.swift b/test/AutoDiff/validation-test/cross_module_derivative_attr.swift index 455138a0739ab..b6e489842e27e 100644 --- a/test/AutoDiff/validation-test/cross_module_derivative_attr.swift +++ b/test/AutoDiff/validation-test/cross_module_derivative_attr.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift-dylib(%t/%target-library-name(module1)) %S/Inputs/cross_module_derivative_attr/module1/module1.swift %S/Inputs/cross_module_derivative_attr/module1/module1_other_file.swift -emit-module -emit-module-path %t/module1.swiftmodule -module-name module1 -// RUN: %target-build-swift -I%t -L%t %S/Inputs/cross_module_derivative_attr/main/main.swift -o %t/a.out -lmodule1 %target-rpath(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(module1)) %S/Inputs/cross_module_derivative_attr/module1/module1.swift %S/Inputs/cross_module_derivative_attr/module1/module1_other_file.swift -emit-module -emit-module-path %t/module1.swiftmodule -module-name module1 -Xfrontend -requirement-machine=off +// RUN: %target-build-swift -I%t -L%t %S/Inputs/cross_module_derivative_attr/main/main.swift -o %t/a.out -lmodule1 %target-rpath(%t) -Xfrontend -requirement-machine=off // RUN: %target-codesign %t/a.out // RUN: %target-codesign %t/%target-library-name(module1) // RUN: %target-run %t/a.out %t/%target-library-name(module1) diff --git a/test/AutoDiff/validation-test/cross_module_differentiation.swift b/test/AutoDiff/validation-test/cross_module_differentiation.swift index 8f79ca2657ae6..13ca6608d6436 100644 --- a/test/AutoDiff/validation-test/cross_module_differentiation.swift +++ b/test/AutoDiff/validation-test/cross_module_differentiation.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift-dylib(%t/%target-library-name(cross_module_differentiation_other)) %S/Inputs/cross_module_differentiation_other.swift -emit-module -emit-module-path %t/cross_module_differentiation_other.swiftmodule -module-name cross_module_differentiation_other -// RUN: %target-build-swift -I%t -L%t %s -o %t/a.out -lcross_module_differentiation_other %target-rpath(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(cross_module_differentiation_other)) %S/Inputs/cross_module_differentiation_other.swift -emit-module -emit-module-path %t/cross_module_differentiation_other.swiftmodule -module-name cross_module_differentiation_other -Xfrontend -requirement-machine=off +// RUN: %target-build-swift -I%t -L%t %s -o %t/a.out -lcross_module_differentiation_other %target-rpath(%t) -Xfrontend -requirement-machine=off // RUN: %target-codesign %t/a.out // RUN: %target-codesign %t/%target-library-name(cross_module_differentiation_other) // RUN: %target-run %t/a.out %t/%target-library-name(cross_module_differentiation_other) diff --git a/test/AutoDiff/validation-test/custom_derivatives.swift b/test/AutoDiff/validation-test/custom_derivatives.swift index f28a233dd34b6..51c0c6046745c 100644 --- a/test/AutoDiff/validation-test/custom_derivatives.swift +++ b/test/AutoDiff/validation-test/custom_derivatives.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/derivative_registration.swift b/test/AutoDiff/validation-test/derivative_registration.swift index a7513f7c7b288..e51ee301f4891 100644 --- a/test/AutoDiff/validation-test/derivative_registration.swift +++ b/test/AutoDiff/validation-test/derivative_registration.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/differentiable_property.swift b/test/AutoDiff/validation-test/differentiable_property.swift index 024d33ae28983..c813888bed0e9 100644 --- a/test/AutoDiff/validation-test/differentiable_property.swift +++ b/test/AutoDiff/validation-test/differentiable_property.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // An end-to-end test that we can differentiate property accesses, with custom diff --git a/test/AutoDiff/validation-test/differentiable_protocol_requirements.swift b/test/AutoDiff/validation-test/differentiable_protocol_requirements.swift index f2b175f9f703b..d08da3cc34d3f 100644 --- a/test/AutoDiff/validation-test/differentiable_protocol_requirements.swift +++ b/test/AutoDiff/validation-test/differentiable_protocol_requirements.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // Disabled due to test failure with `-O`: SR-13250. diff --git a/test/AutoDiff/validation-test/existential.swift b/test/AutoDiff/validation-test/existential.swift index 21fe3b89c498b..ce4408416bb35 100644 --- a/test/AutoDiff/validation-test/existential.swift +++ b/test/AutoDiff/validation-test/existential.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/forward_mode_array.swift b/test/AutoDiff/validation-test/forward_mode_array.swift index bd90eb9f0fd9c..9375b6d23fded 100644 --- a/test/AutoDiff/validation-test/forward_mode_array.swift +++ b/test/AutoDiff/validation-test/forward_mode_array.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation) +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation -Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/forward_mode_inout.swift b/test/AutoDiff/validation-test/forward_mode_inout.swift index 0d11bbc648599..41c76d0272c20 100644 --- a/test/AutoDiff/validation-test/forward_mode_inout.swift +++ b/test/AutoDiff/validation-test/forward_mode_inout.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation) +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation -Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/forward_mode_simd.swift b/test/AutoDiff/validation-test/forward_mode_simd.swift index 389717a8a8fd9..6829b5cb8ed25 100644 --- a/test/AutoDiff/validation-test/forward_mode_simd.swift +++ b/test/AutoDiff/validation-test/forward_mode_simd.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation) +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation -Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/forward_mode_simple.swift b/test/AutoDiff/validation-test/forward_mode_simple.swift index 990f8323bb8a2..0f3b4d653b0da 100644 --- a/test/AutoDiff/validation-test/forward_mode_simple.swift +++ b/test/AutoDiff/validation-test/forward_mode_simple.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation) +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation -Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/function_type_metadata.swift b/test/AutoDiff/validation-test/function_type_metadata.swift index 460181ca7d27e..3b28ab208a5db 100644 --- a/test/AutoDiff/validation-test/function_type_metadata.swift +++ b/test/AutoDiff/validation-test/function_type_metadata.swift @@ -6,7 +6,7 @@ import _Differentiation var FunctionTypeMetadataTests = TestSuite("FunctionTypeMetadata") -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) { FunctionTypeMetadataTests.test("Reflect differentiable function type") { expectEqual( "@differentiable(reverse) (Swift.Float) -> Swift.Float", @@ -61,20 +61,20 @@ if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { } } -// FIXME(rdar://75916878): Investigate why reflecting differentiable function -// types that contain generic parameters will lose '@differentiable' annotation. -// FunctionTypeMetadataTests.test("Reflect generic differentiable function type") { -// func testGeneric(_ type: T.Type) { -// expectEqual( -// """ -// @differentiable(reverse) (\(String(reflecting: type))) -> \ -// \(String(reflecting: type)) -// """, -// String(reflecting: (@differentiable(reverse) (T) -> T).self)) -// } -// testGeneric(Double.self) -// testGeneric([Float].self) -// testGeneric(Float?.self) -// } +if #available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) { + FunctionTypeMetadataTests.test("Reflect generic differentiable function type") { + func testGeneric(_ type: T.Type) { + expectEqual( + """ + @differentiable(reverse) (\(String(reflecting: type))) -> \ + \(String(reflecting: type)) + """, + String(reflecting: (@differentiable(reverse) (T) -> T).self)) + } + testGeneric(Double.self) + testGeneric([Float].self) + testGeneric(Float?.self) + } +} runAllTests() diff --git a/test/AutoDiff/validation-test/inout_control_flow.swift b/test/AutoDiff/validation-test/inout_control_flow.swift new file mode 100644 index 0000000000000..f55551c31800b --- /dev/null +++ b/test/AutoDiff/validation-test/inout_control_flow.swift @@ -0,0 +1,88 @@ +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) +// REQUIRES: executable_test + +import StdlibUnittest +import _Differentiation + +var InoutControlFlowTests = TestSuite("InoutControlFlow") + +// SR-14218 +struct Model: Differentiable { + var first: Float = 3 + var second: Float = 1 + + mutating func outer() { + inner() + } + + mutating func inner() { + self.second = self.first + + // Dummy no-op if block, required to introduce control flow. + let x = 5 + if x < 50 {} + } +} + +@differentiable(reverse) +func loss(model: Model) -> Float{ + var model = model + model.outer() + return model.second +} + +InoutControlFlowTests.test("MutatingBeforeControlFlow") { + var model = Model() + let grad = gradient(at: model, of: loss) + expectEqual(1, grad.first) + expectEqual(0, grad.second) +} + +// SR-14053 +@differentiable(reverse) +func adjust(model: inout Model, multiplier: Float) { + model.first = model.second * multiplier + + // Dummy no-op if block, required to introduce control flow. + let x = 5 + if x < 50 {} +} + +@differentiable(reverse) +func loss2(model: Model, multiplier: Float) -> Float { + var model = model + adjust(model: &model, multiplier: multiplier) + return model.first +} + +InoutControlFlowTests.test("InoutParameterWithControlFlow") { + var model = Model(first: 1, second: 3) + let grad = gradient(at: model, 5.0, of: loss2) + expectEqual(0, grad.0.first) + expectEqual(5, grad.0.second) +} + +@differentiable(reverse) +func adjust2(multiplier: Float, model: inout Model) { + model.first = model.second * multiplier + + // Dummy no-op if block, required to introduce control flow. + let x = 5 + if x < 50 {} +} + +@differentiable(reverse) +func loss3(model: Model, multiplier: Float) -> Float { + var model = model + adjust2(multiplier: multiplier, model: &model) + return model.first +} + +InoutControlFlowTests.test("LaterInoutParameterWithControlFlow") { + var model = Model(first: 1, second: 3) + let grad = gradient(at: model, 5.0, of: loss3) + expectEqual(0, grad.0.first) + expectEqual(5, grad.0.second) +} + +runAllTests() diff --git a/test/AutoDiff/validation-test/inout_parameters.swift b/test/AutoDiff/validation-test/inout_parameters.swift index 687182bed6bbb..0c6f7075478e6 100644 --- a/test/AutoDiff/validation-test/inout_parameters.swift +++ b/test/AutoDiff/validation-test/inout_parameters.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // Would fail due to unavailability of swift_autoDiffCreateLinearMapContext. diff --git a/test/AutoDiff/validation-test/method.swift b/test/AutoDiff/validation-test/method.swift index b0e757e50dbbd..78613d919de84 100644 --- a/test/AutoDiff/validation-test/method.swift +++ b/test/AutoDiff/validation-test/method.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/optional.swift b/test/AutoDiff/validation-test/optional.swift index 1bb183dc31175..7e772844cc2ce 100644 --- a/test/AutoDiff/validation-test/optional.swift +++ b/test/AutoDiff/validation-test/optional.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test // Test differentiation of `Optional` values and operations. diff --git a/test/AutoDiff/validation-test/optional_property.swift b/test/AutoDiff/validation-test/optional_property.swift index 4e1c30ebbf32b..a0dd4bef56c59 100644 --- a/test/AutoDiff/validation-test/optional_property.swift +++ b/test/AutoDiff/validation-test/optional_property.swift @@ -1,5 +1,5 @@ -// RUN: %target-run-simple-swift -// RUN: %target-swift-emit-sil -Xllvm -debug-only=differentiation -module-name null -o /dev/null 2>&1 %s | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) +// RUN: %target-swift-emit-sil -Xllvm -debug-only=differentiation -module-name null -o /dev/null -requirement-machine=off 2>&1 %s | %FileCheck %s // REQUIRES: executable_test // REQUIRES: asserts diff --git a/test/AutoDiff/validation-test/property_wrappers.swift b/test/AutoDiff/validation-test/property_wrappers.swift index 1a62d5dd0e9dc..8a7c8f391a57d 100644 --- a/test/AutoDiff/validation-test/property_wrappers.swift +++ b/test/AutoDiff/validation-test/property_wrappers.swift @@ -1,6 +1,6 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // TODO(TF-1254): Support and test forward-mode differentiation. -// TODO(TF-1254): %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation) +// TODO(TF-1254): %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation -Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/reabstraction.swift b/test/AutoDiff/validation-test/reabstraction.swift index 25c09d684eb12..cbb1a93d1d0b5 100644 --- a/test/AutoDiff/validation-test/reabstraction.swift +++ b/test/AutoDiff/validation-test/reabstraction.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import _Differentiation diff --git a/test/AutoDiff/validation-test/reflection.swift b/test/AutoDiff/validation-test/reflection.swift new file mode 100644 index 0000000000000..d1acb73b650b8 --- /dev/null +++ b/test/AutoDiff/validation-test/reflection.swift @@ -0,0 +1,16 @@ +// REQUIRES: no_asan +// RUN: %empty-directory(%t) +import _Differentiation + +// RUN: %target-build-swift -Xfrontend -enable-anonymous-context-mangled-names %S/Inputs/AutoDiffTypes.swift -parse-as-library -emit-module -emit-library -module-name TypesToReflect -o %t/%target-library-name(TypesToReflect) -Xfrontend -requirement-machine=off +// RUN: %target-build-swift -Xfrontend -enable-anonymous-context-mangled-names %S/Inputs/AutoDiffTypes.swift %S/Inputs/main.swift -emit-module -emit-executable -module-name TypesToReflect -o %t/TypesToReflect -Xfrontend -requirement-machine=off + +// RUN: %target-swift-reflection-dump -binary-filename %t/%target-library-name(TypesToReflect) | %FileCheck %s +// RUN: %target-swift-reflection-dump -binary-filename %t/TypesToReflect | %FileCheck %s + +// CHECK: FIELDS: +// CHECK: ======= +// CHECK: TypesToReflect.HasAutoDiffTypes +// CHECK: aFunction: @differentiable(reverse) (Swift.Float) -> Swift.Float +// CHECK: (function differentiable=reverse + diff --git a/test/AutoDiff/validation-test/repeated_calls.swift b/test/AutoDiff/validation-test/repeated_calls.swift index 615e8ee6dde24..172090d659a88 100644 --- a/test/AutoDiff/validation-test/repeated_calls.swift +++ b/test/AutoDiff/validation-test/repeated_calls.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/separate_tangent_type.swift b/test/AutoDiff/validation-test/separate_tangent_type.swift index 3770e240777e6..4ba5337334992 100644 --- a/test/AutoDiff/validation-test/separate_tangent_type.swift +++ b/test/AutoDiff/validation-test/separate_tangent_type.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/simple_math.swift b/test/AutoDiff/validation-test/simple_math.swift index 88b33e0ecfeaf..6eea5c0f1f5dd 100644 --- a/test/AutoDiff/validation-test/simple_math.swift +++ b/test/AutoDiff/validation-test/simple_math.swift @@ -1,10 +1,10 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // NOTE(TF-813): verify that enabling forward-mode does not affect reverse-mode. // Temporarily disabled because forward-mode is not at feature parity with reverse-mode. -// UN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation) +// UN: %target-run-simple-swift(-Xfrontend -enable-experimental-forward-mode-differentiation -Xfrontend -requirement-machine=off) -// RUN: %target-swift-frontend -Xllvm -sil-print-after=differentiation %s -emit-sil -o /dev/null -module-name null 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-print-after=differentiation %s -emit-sil -o /dev/null -module-name null -requirement-machine=off 2>&1 | %FileCheck %s // REQUIRES: executable_test diff --git a/test/AutoDiff/validation-test/simple_model.swift b/test/AutoDiff/validation-test/simple_model.swift index 687adf0a28cbc..9400186924cfc 100644 --- a/test/AutoDiff/validation-test/simple_model.swift +++ b/test/AutoDiff/validation-test/simple_model.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/subset_parameters_thunk.swift b/test/AutoDiff/validation-test/subset_parameters_thunk.swift index 6e8bc5943885b..7b9c48a9719b3 100644 --- a/test/AutoDiff/validation-test/subset_parameters_thunk.swift +++ b/test/AutoDiff/validation-test/subset_parameters_thunk.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/AutoDiff/validation-test/superset_adjoint.swift b/test/AutoDiff/validation-test/superset_adjoint.swift index d5a96734c435f..f3845320e3832 100644 --- a/test/AutoDiff/validation-test/superset_adjoint.swift +++ b/test/AutoDiff/validation-test/superset_adjoint.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -requirement-machine=off) // REQUIRES: executable_test import StdlibUnittest diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4795f40cd9558..76dad48476578 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -95,7 +95,8 @@ function(get_test_dependencies SDK result_var_name) llvm-readelf llvm-readobj llvm-strings - not) + not + split-file) endif() if(("${SDK}" STREQUAL "IOS") OR @@ -128,19 +129,25 @@ endfunction() set(LIT "${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py") -# Incremental mode in lit orders test files by the last modification time -# instead of by the path, which is good for CI. In this mode lit also updates -# the mtime on the failed tests, which makes them run first on the -# consecutive execution, which makes local builds fail faster. -set(SWIFT_LIT_ARGS "--incremental" CACHE STRING "Arguments to pass to lit") +set(SWIFT_LIT_ARGS "" CACHE STRING "Arguments to pass to lit") set(SWIFT_LIT_ENVIRONMENT "" CACHE STRING "Environment to use for lit invocations") if(NOT SWIFT_INCLUDE_TOOLS) - list(APPEND SWIFT_LIT_ARGS - "--path=${SWIFT_NATIVE_LLVM_TOOLS_PATH}" - "--path=${SWIFT_NATIVE_CLANG_TOOLS_PATH}" - "--path=${SWIFT_NATIVE_SWIFT_TOOLS_PATH}") + if(SWIFT_RUN_TESTS_WITH_HOST_COMPILER) + precondition(CMAKE_Swift_COMPILER MESSAGE "Can only run tests if a Swift compiler is specified") + get_filename_component(SWIFT_COMPILER_DIR "${CMAKE_Swift_COMPILER}" DIRECTORY) + precondition(SWIFT_COMPILER_DIR) + # We assume that we are building against a toolchain where all tools are + # next to swiftc. + list(APPEND SWIFT_LIT_ARGS + "--path=${SWIFT_COMPILER_DIR}") + else() + list(APPEND SWIFT_LIT_ARGS + "--path=${SWIFT_NATIVE_LLVM_TOOLS_PATH}" + "--path=${SWIFT_NATIVE_CLANG_TOOLS_PATH}" + "--path=${SWIFT_NATIVE_SWIFT_TOOLS_PATH}") + endif() if(SWIFT_BUILD_STDLIB) list(APPEND SWIFT_LIT_ARGS "--param" "test_resource_dir=${SWIFTLIB_DIR}") @@ -157,6 +164,10 @@ if(NOT CMAKE_CFG_INTDIR STREQUAL ".") "--param" "build_mode=${CMAKE_CFG_INTDIR}") endif() +if(SWIFT_TOOLS_ENABLE_LIBSWIFT) + list(APPEND SWIFT_LIT_ARGS "--param" "libswift") +endif() + if (LLVM_USE_SANITIZER STREQUAL "Address") set(SWIFT_ASAN_BUILD TRUE) endif() @@ -170,6 +181,16 @@ normalize_boolean_spelling(SWIFT_BUILD_SYNTAXPARSERLIB) normalize_boolean_spelling(SWIFT_ENABLE_SOURCEKIT_TESTS) normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING) normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY) +normalize_boolean_spelling(SWIFT_BACK_DEPLOY_CONCURRENCY) +normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) +normalize_boolean_spelling(SWIFT_ENABLE_MACCATALYST) +normalize_boolean_spelling(SWIFT_RUN_TESTS_WITH_HOST_COMPILER) +normalize_boolean_spelling(SWIFT_RUNTIME_ENABLE_LEAK_CHECKER) +normalize_boolean_spelling(SWIFT_OPTIMIZED) +normalize_boolean_spelling(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) +normalize_boolean_spelling(SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS) +normalize_boolean_spelling(SWIFT_HAVE_LIBXML2) +normalize_boolean_spelling(SWIFT_INCLUDE_TOOLS) is_build_type_optimized("${SWIFT_STDLIB_BUILD_TYPE}" SWIFT_OPTIMIZED) set(profdata_merge_worker @@ -188,6 +209,7 @@ set(TEST_SUBSETS only_validation only_long only_stress + only_early_swiftdriver ) if(NOT "${COVERAGE_DB}" STREQUAL "") @@ -345,6 +367,14 @@ foreach(SDK ${SWIFT_SDKS}) list(APPEND LIT_ARGS "--param" "concurrency") endif() + if(SWIFT_BACK_DEPLOY_CONCURRENCY) + list(APPEND LIT_ARGS "--param" "back_deploy_concurrency") + endif() + + if(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) + list(APPEND LIT_ARGS "--param" "distributed") + endif() + foreach(test_subset ${TEST_SUBSETS}) set(directories) set(dependencies ${test_dependencies}) @@ -353,6 +383,7 @@ foreach(SDK ${SWIFT_SDKS}) (test_subset STREQUAL "validation") OR (test_subset STREQUAL "only_long") OR (test_subset STREQUAL "only_stress") OR + (test_subset STREQUAL "only_early_swiftdriver") OR (test_subset STREQUAL "all")) list(APPEND directories "${test_bin_dir}") endif() diff --git a/test/Casting/CastTraps.swift.gyb b/test/Casting/CastTraps.swift.gyb index 95c7cbbebfe31..df43c099d6077 100644 --- a/test/Casting/CastTraps.swift.gyb +++ b/test/Casting/CastTraps.swift.gyb @@ -105,7 +105,7 @@ CastTrapsTestSuite.test("${t1}__${t2}") % end protocol P2 {} -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { CastTrapsTestSuite.test("Unexpected null") .crashOutputMatches("Found unexpected null pointer value while trying to cast value of type '") .crashOutputMatches("Foo'") @@ -125,7 +125,7 @@ CastTrapsTestSuite.test("Unexpected null") #if _runtime(_ObjC) -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { CastTrapsTestSuite.test("Unexpected Obj-C null") .crashOutputMatches("Found unexpected null pointer value while trying to cast value of type '") .crashOutputMatches("NSObject'") diff --git a/test/Casting/Casts.swift b/test/Casting/Casts.swift index ac9334da63aeb..d228cc6bfa4c7 100644 --- a/test/Casting/Casts.swift +++ b/test/Casting/Casts.swift @@ -15,11 +15,11 @@ // ----------------------------------------------------------------------------- // RUN: %empty-directory(%t) // -// RUN: %target-build-swift -swift-version 5 -g -Onone -Xfrontend -enable-experimental-concurrency -module-name a %s -o %t/a.swift5.Onone.out +// RUN: %target-build-swift -swift-version 5 -g -Onone -module-name a %s -o %t/a.swift5.Onone.out // RUN: %target-codesign %t/a.swift5.Onone.out // RUN: %target-run %t/a.swift5.Onone.out // -// RUN: %target-build-swift -swift-version 5 -g -O -Xfrontend -enable-experimental-concurrency -module-name a %s -o %t/a.swift5.O.out +// RUN: %target-build-swift -swift-version 5 -g -O -module-name a %s -o %t/a.swift5.O.out // RUN: %target-codesign %t/a.swift5.O.out // RUN: %target-run %t/a.swift5.O.out // @@ -167,7 +167,7 @@ CastsTests.test("Dynamic casts of CF types to protocol existentials (SR-2289)") reason: "This test behaves unpredictably in optimized mode.")) .code { expectTrue(isP(10 as Int)) - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { expectTrue(isP(CFBitVector.makeImmutable(from: [10, 20]))) expectTrue(isP(CFMutableBitVector.makeMutable(from: [10, 20]))) } @@ -195,7 +195,7 @@ CastsTests.test("Casting struct -> Obj-C -> Protocol fails (SR-3871, SR-5590, SR protocol P4552 {} -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { CastsTests.test("Casting Any(Optional(T)) -> Protocol fails (SR-4552)") { struct S: P4552 { let tracker = LifetimeTracked(13) @@ -257,7 +257,7 @@ CastsTests.test("Store Swift metatype in ObjC property and cast back to Any.Type let sValue2 = a.sVar as? Any.Type let objcValue2 = a.objcVar as? Any.Type expectTrue(sValue2 == b) - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { expectTrue(sValue2 == objcValue2) expectTrue(objcValue2 == b) } @@ -303,7 +303,7 @@ CastsTests.test("Casts from @objc Type") { let user = User(name: "Kermit") let exporter: Exporter = UserExporter() - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { expectTrue(exporter.type is User.Type) } expectNotNil(exporter.export(item: user)) @@ -319,7 +319,7 @@ CastsTests.test("Conditional NSNumber -> Bool casts") { #endif // rdar://45217461 ([dynamic casting] [SR-8964]: Type check operator (is) fails for Any! variable holding an Error (struct) value) -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { CastsTests.test("Casts from Any(struct) to Error (SR-8964)") { struct MyError: Error { } @@ -393,7 +393,7 @@ CastsTests.test("Swift Protocol Metatypes don't self-conform") { let a = SwiftProtocol.self // `is P.Protocol` tests whether the argument is a subtype of P. // In particular, the protocol identifier `P.self` is such a subtype. - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { expectNotNil(runtimeCast(a, to: SwiftProtocol.Protocol.self)) // Fixed by rdar://58991956 } expectNotNil(a as? SwiftProtocol.Protocol) @@ -446,7 +446,7 @@ CastsTests.test("Self-conformance for Error.self") @objc protocol ObjCProtocol {} CastsTests.test("ObjC Protocol Metatypes self-conform") { let a = ObjCProtocol.self - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { expectNotNil(runtimeCast(a, to: ObjCProtocol.Protocol.self)) } expectNotNil(a as? ObjCProtocol.Protocol) @@ -472,7 +472,7 @@ CastsTests.test("String/NSString extension compat") { #endif protocol P1999 {} -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { CastsTests.test("Cast Any(Optional(class)) to Protocol type (SR-1999)") { class Foo: P1999 { } @@ -954,4 +954,54 @@ CastsTests.test("Recursive AnyHashable") { expectEqual(s.x, p) } +// SR-14635 (aka rdar://78224322) +#if _runtime(_ObjC) +CastsTests.test("Do not overuse __SwiftValue") { + struct Bar {} + // This used to succeed because of overeager __SwiftValue + // boxing (and __SwiftValue does satisfy NSCopying) + expectFalse(Bar() is NSCopying) + expectFalse(Bar() as Any is NSCopying) + + // This seems unavoidable? + // `Bar() as! AnyObject` gets boxed as a __SwiftValue, + // and __SwiftValue does conform to NSCopying + expectTrue(Bar() as! AnyObject is NSCopying) + + class Foo {} + // Foo does not conform to NSCopying + // (This used to succeed due to over-eager __SwiftValue boxing) + expectFalse(Foo() is NSCopying) + expectFalse(Foo() as Any is NSCopying) + + // A type that really does conform should cast to NSCopying + class Foo2: NSCopying { + func copy(with: NSZone?) -> Any { return self } + } + expectTrue(Foo2() is NSCopying) + expectTrue(Foo2() is AnyObject) +} +#endif + +#if _runtime(_ObjC) +CastsTests.test("Artificial subclass protocol conformance") { + class SwiftClass: NSObject {} + let subclass: AnyClass = objc_allocateClassPair(SwiftClass.self, + "ArtificialSwiftSubclass", 0)! + objc_registerClassPair(subclass) + expectFalse(subclass is P.Type) +} +#endif + +CastsTests.test("Do not overuse __SwiftValue (non-ObjC)") { + struct Bar {} + // This should succeed because this is what __SwiftValue boxing is for + expectTrue(Bar() is AnyObject) + expectTrue(Bar() as Any is AnyObject) + + class Foo {} + // Any class type can be cast to AnyObject + expectTrue(Foo() is AnyObject) +} + runAllTests() diff --git a/test/ClangImporter/Inputs/custom-modules/ObjCIRExtras.h b/test/ClangImporter/Inputs/custom-modules/ObjCIRExtras.h index 32bdc9af0955c..8658bfb4b19db 100644 --- a/test/ClangImporter/Inputs/custom-modules/ObjCIRExtras.h +++ b/test/ClangImporter/Inputs/custom-modules/ObjCIRExtras.h @@ -72,4 +72,11 @@ int global_int SWIFT_NAME(GlobalInt); @compatibility_alias SwiftGenericNameAlias SwiftGenericNameTest; @compatibility_alias SwiftConstrGenericNameAlias SwiftConstrGenericNameTest; +SWIFT_NAME(CircularName.Inner) @interface CircularName : NSObject @end + +SWIFT_NAME(MutuallyCircularNameB.Inner) @interface MutuallyCircularNameA : NSObject @end +SWIFT_NAME(MutuallyCircularNameA.Inner) @interface MutuallyCircularNameB : NSObject @end + +void circularFriends(CircularName*, MutuallyCircularNameA*); + #pragma clang assume_nonnull end diff --git a/test/ClangImporter/Inputs/frameworks/SPIContainer.framework/Headers/SPIContainer.h b/test/ClangImporter/Inputs/frameworks/SPIContainer.framework/Headers/SPIContainer.h new file mode 100644 index 0000000000000..cea9a3ed6fba9 --- /dev/null +++ b/test/ClangImporter/Inputs/frameworks/SPIContainer.framework/Headers/SPIContainer.h @@ -0,0 +1,23 @@ +#import + +#ifdef SPI_AVAILABLE +#undef SPI_AVAILABLE +#define SPI_AVAILABLE API_AVAILABLE +#endif + +#ifdef __SPI_AVAILABLE +#undef __SPI_AVAILABLE +#define __SPI_AVAILABLE API_AVAILABLE +#endif + +SPI_AVAILABLE(macos(10.7)) +@interface SPIInterface1 +@end + +__SPI_AVAILABLE(macos(10.7)) +@interface SPIInterface2 +@end + +@interface SharedInterface + + (NSInteger)foo SPI_AVAILABLE(macos(10.7)); +@end diff --git a/test/ClangImporter/Inputs/frameworks/SPIContainer.framework/Modules/module.modulemap b/test/ClangImporter/Inputs/frameworks/SPIContainer.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..da7b338e1024b --- /dev/null +++ b/test/ClangImporter/Inputs/frameworks/SPIContainer.framework/Modules/module.modulemap @@ -0,0 +1,7 @@ +framework module SPIContainer { + umbrella header "SPIContainer.h" + export * + module * { + export * + } +} diff --git a/test/ClangImporter/MixedSource/broken-modules.swift b/test/ClangImporter/MixedSource/broken-modules.swift index 8dcd47a3f45e6..ed80a7516d8b7 100644 --- a/test/ClangImporter/MixedSource/broken-modules.swift +++ b/test/ClangImporter/MixedSource/broken-modules.swift @@ -18,7 +18,7 @@ import MissingDependencyFromSwift // CHECK-NOT: no such module 'MissingDependencyFromSwift' import MissingDependencyFromClang -// CHECK: {{.+}}/Inputs/broken-modules/MissingDependencyFromClang.h:1:9: error: module 'Dependency' not found +// CHECK: {{.+}}{{/|\\}}Inputs{{/|\\}}broken-modules{{/|\\}}MissingDependencyFromClang.h:1:9: error: module 'Dependency' not found // CHECK: broken-modules.swift:[[@LINE-2]]:8: error: could not build Objective-C module 'MissingDependencyFromClang' // CHECK: error: no such module 'MissingDependencyFromClang' diff --git a/test/ClangImporter/attr-swift_name.swift b/test/ClangImporter/attr-swift_name.swift index f0cb32c55e385..31f6eb56107ae 100644 --- a/test/ClangImporter/attr-swift_name.swift +++ b/test/ClangImporter/attr-swift_name.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t.mcp) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck %s -module-cache-path %t.mcp -disable-named-lazy-member-loading 2>&1 | %FileCheck %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck %s -module-cache-path %t.mcp -disable-named-lazy-member-loading 2>&1 | %FileCheck %s // REQUIRES: objc_interop @@ -15,7 +15,9 @@ func test(_ i: Int) { _ = t.renamedSomeProp _ = type(of: t).renamedClassProp - // We only see these two warnings because Clang can catch the other invalid + _ = circularFriends(_:_:) + + // We only see these five warnings because Clang can catch the other invalid // cases, and marks the attribute as invalid ahead of time. // CHECK: warning: too few parameters in swift_name attribute (expected 2; got 1) @@ -29,4 +31,28 @@ func test(_ i: Int) { // CHECK-NOT: warning: // CHECK: note: please report this issue to the owners of 'ObjCIRExtras' // CHECK-NOT: warning: + + // CHECK: warning: cycle detected while resolving 'CircularName' in swift_name attribute for 'CircularName' + // CHECK: SWIFT_NAME(CircularName.Inner) @interface CircularName : NSObject @end + // CHECK-NOT: {{warning|note}}: + // CHECK: note: please report this issue to the owners of 'ObjCIRExtras' + // CHECK-NOT: warning: + + // CHECK: warning: cycle detected while resolving 'MutuallyCircularNameB' in swift_name attribute for 'MutuallyCircularNameA' + // CHECK: SWIFT_NAME(MutuallyCircularNameB.Inner) @interface MutuallyCircularNameA : NSObject @end + // CHECK-NOT: {{warning|note}}: + // CHECK: note: while resolving 'MutuallyCircularNameA' in swift_name attribute for 'MutuallyCircularNameB' + // CHECK: SWIFT_NAME(MutuallyCircularNameA.Inner) @interface MutuallyCircularNameB : NSObject @end + // CHECK-NOT: {{warning|note}}: + // CHECK: note: please report this issue to the owners of 'ObjCIRExtras' + // CHECK-NOT: warning: + + // CHECK: warning: cycle detected while resolving 'MutuallyCircularNameA' in swift_name attribute for 'MutuallyCircularNameB' + // CHECK: SWIFT_NAME(MutuallyCircularNameA.Inner) @interface MutuallyCircularNameB : NSObject @end + // CHECK-NOT: {{warning|note}}: + // CHECK: note: while resolving 'MutuallyCircularNameB' in swift_name attribute for 'MutuallyCircularNameA' + // CHECK: SWIFT_NAME(MutuallyCircularNameB.Inner) @interface MutuallyCircularNameA : NSObject @end + // CHECK-NOT: {{warning|note}}: + // CHECK: note: please report this issue to the owners of 'ObjCIRExtras' + // CHECK-NOT: warning: } diff --git a/test/ClangImporter/availability_spi_as_unavailable.swift b/test/ClangImporter/availability_spi_as_unavailable.swift new file mode 100644 index 0000000000000..32298523f4961 --- /dev/null +++ b/test/ClangImporter/availability_spi_as_unavailable.swift @@ -0,0 +1,15 @@ +// REQUIRES: OS=macosx +// RUN: %target-swift-frontend -typecheck %s -F %S/Inputs/frameworks -verify + +import SPIContainer + +@_spi(a) public let a: SPIInterface1 +@_spi(a) public let b: SPIInterface2 + +public let c: SPIInterface1 // expected-error{{cannot use class 'SPIInterface1' here; it is an SPI imported from 'SPIContainer'}} +public let d: SPIInterface2 // expected-error{{cannot use class 'SPIInterface2' here; it is an SPI imported from 'SPIContainer'}} + +@inlinable +public func inlinableUsingSPI() { + SharedInterface.foo() // expected-error{{class method 'foo()' cannot be used in an '@inlinable' function because it is an SPI imported from 'SPIContainer'}} +} diff --git a/test/ClangImporter/broken-modules.swift b/test/ClangImporter/broken-modules.swift index bede2c61bdea5..6a8a7207ca7ec 100644 --- a/test/ClangImporter/broken-modules.swift +++ b/test/ClangImporter/broken-modules.swift @@ -23,7 +23,7 @@ import ImportsMissingHeader // CHECK: :1:9: note: in file included from :1: // CHECK-NEXT: #import "{{.*}}ImportsMissingHeader.h" -// CHECK: {{.*}}/Inputs/custom-modules/ImportsMissingHeader.h:1:9: error: 'this-header-does-not-exist.h' file not found +// CHECK: {{.*}}{{/|\\}}Inputs{{/|\\}}custom-modules{{/|\\}}ImportsMissingHeader.h:1:9: error: 'this-header-does-not-exist.h' file not found // CHECK-INDIRECT: :1:9: note: in file included from :1: // CHECK-INDIRECT-NEXT: #import "{{.*}}ImportsMissingHeaderIndirect.h" diff --git a/test/ClangImporter/cxx_interop_ir.swift b/test/ClangImporter/cxx_interop_ir.swift index ba6ab4cf1b3f9..1f6eef7255fe3 100644 --- a/test/ClangImporter/cxx_interop_ir.swift +++ b/test/ClangImporter/cxx_interop_ir.swift @@ -1,4 +1,7 @@ // RUN: %target-swiftxx-frontend -module-name cxx_ir -I %S/Inputs/custom-modules -emit-ir -o - -primary-file %s | %FileCheck %s +// +// We can't yet call member functions correctly on Windows (SR-13129). +// XFAIL: OS=windows-msvc import CXXInterop @@ -40,9 +43,10 @@ func basicMethods(a: UnsafeMutablePointer) -> Int32 { } // CHECK-LABEL: define hidden swiftcc i32 @"$s6cxx_ir17basicMethodsConst1as5Int32VSpySo0D0VG_tF"(i8* %0) -// CHECK: [[THIS_PTR1:%.*]] = bitcast i8* %0 to %TSo7MethodsV* +// CHECK: [[THIS_PTR1:%.*]] = alloca %TSo7MethodsV, align {{4|8}} // CHECK: [[THIS_PTR2:%.*]] = bitcast %TSo7MethodsV* [[THIS_PTR1]] to %class.Methods* -// CHECK: [[RESULT:%.*]] = call {{(signext )?}}i32 @{{_ZNK7Methods17SimpleConstMethodEi|"\?SimpleConstMethod@Methods@@QEBAHH@Z"}}(%class.Methods* [[THIS_PTR2]], i32{{( signext)?}} 3) +// CHECK: [[THIS_PTR3:%.*]] = bitcast %TSo7MethodsV* [[THIS_PTR1]] to %class.Methods* +// CHECK: [[RESULT:%.*]] = call {{(signext )?}}i32 @{{_ZNK7Methods17SimpleConstMethodEi|"\?SimpleConstMethod@Methods@@QEBAHH@Z"}}(%class.Methods* [[THIS_PTR3]], i32{{( signext)?}} 3) // CHECK: ret i32 [[RESULT]] func basicMethodsConst(a: UnsafeMutablePointer) -> Int32 { return a.pointee.SimpleConstMethod(3) diff --git a/test/ClangImporter/duplicate_mainactor.swift b/test/ClangImporter/duplicate_mainactor.swift index 53d5945054f9c..9ae0e6cb68388 100644 --- a/test/ClangImporter/duplicate_mainactor.swift +++ b/test/ClangImporter/duplicate_mainactor.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-concurrency -import-objc-header %S/Inputs/DoubleMainActor.h -emit-module -module-name use %s 2> %t/stderr.txt +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -import-objc-header %S/Inputs/DoubleMainActor.h -emit-module -module-name use %s 2> %t/stderr.txt // RUN: %FileCheck -input-file %t/stderr.txt %s // REQUIRES: concurrency diff --git a/test/ClangImporter/enum-error.swift b/test/ClangImporter/enum-error.swift index b769f96bd92e1..f7de047ea6acf 100644 --- a/test/ClangImporter/enum-error.swift +++ b/test/ClangImporter/enum-error.swift @@ -14,6 +14,8 @@ // RUN: echo '#include "enum-error.h"' > %t.m // RUN: %target-swift-ide-test -source-filename %s -print-header -header-to-print %S/Inputs/enum-error.h -import-objc-header %S/Inputs/enum-error.h -print-regular-comments --cc-args %target-cc-options -fsyntax-only %t.m -I %S/Inputs > %t.txt // RUN: %FileCheck -check-prefix=HEADER %s < %t.txt +// RUN: %target-swift-ide-test -source-filename %s -print-header -header-to-print %S/Inputs/enum-error.h -import-objc-header %S/Inputs/enum-error.h -print-regular-comments --skip-private-stdlib-decls -skip-underscored-stdlib-protocols --cc-args %target-cc-options -fsyntax-only %t.m -I %S/Inputs > %t2.txt +// RUN: %FileCheck -check-prefix=HEADER-NO-PRIVATE %s < %t2.txt import Foundation @@ -121,12 +123,25 @@ class ObjCTest { } #endif -// HEADER: enum Code : Int32, _ErrorCodeProtocol { -// HEADER: init?(rawValue: Int32) -// HEADER: var rawValue: Int32 { get } -// HEADER: typealias _ErrorType = TestError -// HEADER: case TENone -// HEADER: case TEOne -// HEADER: case TETwo +// HEADER: struct TestError : _BridgedStoredNSError { +// HEADER: enum Code : Int32, _ErrorCodeProtocol { +// HEADER: init?(rawValue: Int32) +// HEADER: var rawValue: Int32 { get } +// HEADER: typealias _ErrorType = TestError +// HEADER: case TENone +// HEADER: case TEOne +// HEADER: case TETwo +// HEADER: } // HEADER: } // HEADER: func getErr() -> TestError.Code + +// HEADER-NO-PRIVATE: struct TestError : CustomNSError, Hashable, Error { +// HEADER-NO-PRIVATE: enum Code : Int32, Equatable { +// HEADER-NO-PRIVATE: init?(rawValue: Int32) +// HEADER-NO-PRIVATE: var rawValue: Int32 { get } +// HEADER-NO-PRIVATE: typealias _ErrorType = TestError +// HEADER-NO-PRIVATE: case TENone +// HEADER-NO-PRIVATE: case TEOne +// HEADER-NO-PRIVATE: case TETwo +// HEADER-NO-PRIVATE: } +// HEADER-NO-PRIVATE: } diff --git a/test/ClangImporter/non-modular-include.swift b/test/ClangImporter/non-modular-include.swift index 017ab8fdcb218..164be92a810c0 100644 --- a/test/ClangImporter/non-modular-include.swift +++ b/test/ClangImporter/non-modular-include.swift @@ -2,7 +2,7 @@ // RUN: not %target-swift-frontend -enable-objc-interop -typecheck %/s -I %S/Inputs/non-modular -F %S/Inputs/non-modular 2>&1 | %FileCheck --check-prefix=CHECK -check-prefix CHECK-objc %s // RUN: not %target-swift-frontend -disable-objc-interop -typecheck %/s -I %S/Inputs/non-modular -F %S/Inputs/non-modular 2>&1 | %FileCheck --check-prefix=CHECK -check-prefix CHECK-native %s -// CHECK: {{.+}}/non-modular{{/|\\}}Foo.framework/Headers{{/|\\}}Foo.h:1:10: error: include of non-modular header inside framework module 'Foo' +// CHECK: {{.+}}{{/|\\}}non-modular{{/|\\}}Foo.framework{{/|\\}}Headers{{/|\\}}Foo.h:1:10: error: include of non-modular header inside framework module 'Foo' // CHECK-objc: error: could not build Objective-C module 'Foo' // CHECK-native: error: could not build C module 'Foo' // CHECK-NOT: error diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 451ff074973db..53c7ff6ae6d78 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -enable-experimental-concurrency -enable-experimental-async-handler %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -disable-availability-checking %s -verify // REQUIRES: objc_interop // REQUIRES: concurrency @@ -117,19 +117,18 @@ func globalAsync() async { } actor MySubclassCheckingSwiftAttributes : ProtocolWithSwiftAttributes { func syncMethod() { } // expected-note 2{{calls to instance method 'syncMethod()' from outside of its actor context are implicitly asynchronous}} - func independentMethod() { + nonisolated func independentMethod() { syncMethod() // expected-error{{ctor-isolated instance method 'syncMethod()' can not be referenced from a non-isolated context}} } - func asyncHandlerMethod() { - await globalAsync() // okay because we infer @asyncHandler from the protocol + nonisolated func nonisolatedMethod() { } - func mainActorMethod() { - syncMethod() // expected-error{{actor-isolated instance method 'syncMethod()' can not be referenced from synchronous context of global actor 'MainActor'}} + @MainActor func mainActorMethod() { + syncMethod() // expected-error{{actor-isolated instance method 'syncMethod()' can not be referenced from the main actor}} } - func uiActorMethod() { } + @MainActor func uiActorMethod() { } } // Sendable conformance inference for imported types. diff --git a/test/ClangImporter/objc_async_conformance.swift b/test/ClangImporter/objc_async_conformance.swift index c3d51fb1d6d27..c413dfd2332dc 100644 --- a/test/ClangImporter/objc_async_conformance.swift +++ b/test/ClangImporter/objc_async_conformance.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -disable-availability-checking %s -verify // REQUIRES: objc_interop // REQUIRES: concurrency @@ -20,12 +20,11 @@ extension C2 { } // a version of C2 that requires both sync and async methods (differing only by -// completion handler) in ObjC, is not possible to conform to with 'async' in -// a Swift protocol +// completion handler) in ObjC. class C3 : NSObject, RequiredObserver {} extension C3 { - func hello() -> Bool { true } // expected-note {{'hello()' previously declared here}} - func hello() async -> Bool { true } // expected-error {{invalid redeclaration of 'hello()'}} + func hello() -> Bool { true } + func hello() async -> Bool { true } } // the only way to conform to 'RequiredObserver' in Swift is to not use 'async' diff --git a/test/ClangImporter/objc_async_macos.swift b/test/ClangImporter/objc_async_macos.swift index c8d7964da2839..804e71a64b14c 100644 --- a/test/ClangImporter/objc_async_macos.swift +++ b/test/ClangImporter/objc_async_macos.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -verify // REQUIRES: objc_interop // REQUIRES: concurrency @@ -7,6 +7,7 @@ import Foundation import ObjCConcurrency +@available(macOS 12.0, *) func testSlowServer(slowServer: SlowServer) async throws { _ = try await slowServer.oldAPI(); // expected-error{{'oldAPI()' is unavailable in macOS: APIs deprecated as of macOS 10.14 and earlier are not imported as 'async'}} } diff --git a/test/ClangImporter/objc_nsmanagedobject.swift b/test/ClangImporter/objc_nsmanagedobject.swift index 65b7b77828571..0d760cc5dc14e 100644 --- a/test/ClangImporter/objc_nsmanagedobject.swift +++ b/test/ClangImporter/objc_nsmanagedobject.swift @@ -4,7 +4,7 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -emit-silgen -parse-as-library -o /dev/null -DNO_ERROR %s %S/Inputs/objc_nsmanaged_other.swift // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -emit-silgen -parse-as-library -o /dev/null -DNO_ERROR -primary-file %s %S/Inputs/objc_nsmanaged_other.swift -// REQUIRES: objc_interop, rdar76090045 +// REQUIRES: objc_interop import Foundation import CoreData diff --git a/test/ClangImporter/sdk-protocol-class.swift b/test/ClangImporter/sdk-protocol-class.swift index 3f0ef57f6c82a..9b7f697badcc6 100644 --- a/test/ClangImporter/sdk-protocol-class.swift +++ b/test/ClangImporter/sdk-protocol-class.swift @@ -6,6 +6,7 @@ // RUN: %target-swift-frontend -typecheck %S/Inputs/sdk-protocol-class/os3.swift // REQUIRES: objc_interop +// REQUIRES: rdar61260194 import ObjectiveC diff --git a/test/ClangImporter/serialization-sil.swift b/test/ClangImporter/serialization-sil.swift index 544e1823ab7b2..35a22e84d4a0a 100644 --- a/test/ClangImporter/serialization-sil.swift +++ b/test/ClangImporter/serialization-sil.swift @@ -32,8 +32,7 @@ public func testPartialApply(_ obj: Test) { // CHECK: [[PROP1_TRUE]]([[PROP1_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject): // CHECK: [[PROP1_OBJ_COPY:%.*]] = copy_value [[PROP1_OBJ]] // CHECK: [[PROP1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP1_METHOD]]([[PROP1_OBJ_COPY]]) : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject - // CHECK: [[PROP1_PARTIAL_COPY:%.*]] = copy_value [[PROP1_PARTIAL]] - // CHECK: = apply [[PROP1_PARTIAL_COPY]]() : $@callee_guaranteed () -> @owned AnyObject + // CHECK: = apply [[PROP1_PARTIAL]]() : $@callee_guaranteed () -> @owned AnyObject _ = prop1 } if let prop2 = obj.innerPointerProp { diff --git a/test/ClangImporter/working-directory.swift b/test/ClangImporter/working-directory.swift index e965712270766..782325f8fc9f6 100644 --- a/test/ClangImporter/working-directory.swift +++ b/test/ClangImporter/working-directory.swift @@ -1,4 +1,12 @@ -// RUN: cd %S/Inputs/ && %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -Xcc -I -Xcc custom-modules %s -dump-clang-diagnostics 2>&1 | %FileCheck %s +// RUN: %empty-directory(%t/mcp) + +// Check that equivalent invocations result in the same module hash +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -Xcc -I -Xcc custom-modules -module-cache-path %t/mcp -Xcc -working-directory -Xcc %S/Inputs/ %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -Xcc -I -Xcc %S/Inputs/custom-modules -module-cache-path %t/mcp %s +// RUN: find %t/mcp -name "ObjCParseExtras-*.pcm" | count 1 + +// Check that the working directory is set to the CWD if not explicitly passed +// RUN: cd %S/Inputs/ && %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -Xcc -I -Xcc custom-modules %s -dump-clang-diagnostics -module-cache-path %t/mcp 2>&1 | %FileCheck %s // REQUIRES: objc_interop diff --git a/test/Compatibility/ownership_protocol.swift b/test/Compatibility/ownership_protocol.swift index c8f62b7bf9d69..94af40ed56a2d 100644 --- a/test/Compatibility/ownership_protocol.swift +++ b/test/Compatibility/ownership_protocol.swift @@ -4,14 +4,14 @@ class SomeClass {} protocol P { - // expected-warning@+1 {{'weak' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + // expected-warning@+1 {{'weak' cannot be applied to a property declaration in a protocol; this is an error in Swift 5}} weak var foo: SomeClass? { get set } - // expected-warning@+1 {{'unowned' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + // expected-warning@+1 {{'unowned' cannot be applied to a property declaration in a protocol; this is an error in Swift 5}} unowned var foo2: SomeClass { get set } - // expected-warning@+2 {{'weak' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + // expected-warning@+2 {{'weak' cannot be applied to a property declaration in a protocol; this is an error in Swift 5}} // expected-error@+1 {{'weak' may only be applied to class and class-bound protocol types, not 'Int'}} weak var foo3: Int? { get set } - // expected-warning@+2 {{'unowned' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + // expected-warning@+2 {{'unowned' cannot be applied to a property declaration in a protocol; this is an error in Swift 5}} // expected-error@+1 {{'unowned' may only be applied to class and class-bound protocol types, not 'Int'}} unowned var foo4: Int { get set } } diff --git a/test/Concurrency/Backdeploy/mangling.swift b/test/Concurrency/Backdeploy/mangling.swift new file mode 100644 index 0000000000000..63cd3ea7b16b0 --- /dev/null +++ b/test/Concurrency/Backdeploy/mangling.swift @@ -0,0 +1,71 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -target x86_64-apple-macosx12.0 -module-name main -emit-ir -o %t/new.ir +// RUN: %FileCheck %s --check-prefix=NEW < %t/new.ir +// RUN: %target-swift-frontend %s -target x86_64-apple-macosx10.15 -module-name main -emit-ir -o %t/old.ir -disable-availability-checking +// RUN: %FileCheck %s --check-prefix=OLD < %t/old.ir + +// Check that we add extra type metadata accessors for new kinds of functions +// when back-deploying. These are used instead of using demangling cache +// variables since old runtimes cannot synthesize type metadata based on the +// new mangling. + +// RUN: %target-build-swift -target x86_64-apple-macosx10.15 %s -o %t/test_mangling -Xfrontend -disable-availability-checking +// RUN: %target-run %t/test_mangling + +// REQUIRES: CPU=x86_64 +// REQUIRES: OS=macosx +// REQUIRES: executable_test + +protocol MyProtocol { + associatedtype AssocSendable + associatedtype AssocAsync + associatedtype AssocGlobalActor +} + +typealias SendableFn = @Sendable () -> Void +typealias AsyncFn = () async -> Void +typealias GlobalActorFn = @MainActor () -> Void + +struct MyStruct: MyProtocol { + typealias AssocSendable = SendableFn + typealias AssocAsync = AsyncFn + typealias AssocGlobalActor = GlobalActorFn +} + +func assocSendable(_: T.Type) -> Any.Type { return T.AssocSendable.self } +func assocAsync(_: T.Type) -> Any.Type { return T.AssocAsync.self } +func assocGlobalActor(_: T.Type) -> Any.Type { return T.AssocGlobalActor.self } + +assert(assocSendable(MyStruct.self) == SendableFn.self) +assert(assocAsync(MyStruct.self) == AsyncFn.self) +assert(assocGlobalActor(MyStruct.self) == GlobalActorFn.self) + +// type metadata accessor for @Sendable () -> () +// OLD: define linkonce_odr hidden swiftcc %swift.metadata_response @"$syyYbcMa" +// NEW-NOT: define linkonce_odr hidden swiftcc %swift.metadata_response @"$syyYbcMa" + +// type metadata accessor for () async -> () +// OLD: define linkonce_odr hidden swiftcc %swift.metadata_response @"$syyYacMa" +// NEW-NOT: define linkonce_odr hidden swiftcc %swift.metadata_response @"$syyYacMa" + +// type metadata accessor for @MainActor () -> () +// OLD: define linkonce_odr hidden swiftcc %swift.metadata_response @"$syyScMYccMa" +// NEW-NOT: define linkonce_odr hidden swiftcc %swift.metadata_response @"$syyScMYccMa" + +// OLD: call swiftcc %swift.metadata_response @"$syyYbcMa" +// OLD-NOT: call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$syyYbcMD") + +// NEW-NOT: call swiftcc %swift.metadata_response @"$syyYbcMa" +// NEW: call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$syyYbcMD") + +// OLD: call swiftcc %swift.metadata_response @"$syyYacMa" +// OLD-NOT: %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$syyYacMD") + +// NEW-NOT: call swiftcc %swift.metadata_response @"$syyYacMa" +// NEW: call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$syyYacMD") + +// OLD: call swiftcc %swift.metadata_response @"$syyScMYccMa" +// OLD-NOT: call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$syyScMYccMD") + +// NEW-NOT: call swiftcc %swift.metadata_response @"$syyScMYccMa" +// NEW: call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$syyScMYccMD") diff --git a/test/Concurrency/Inputs/NonStrictModule.swift b/test/Concurrency/Inputs/NonStrictModule.swift new file mode 100644 index 0000000000000..b0e3f1769ccf5 --- /dev/null +++ b/test/Concurrency/Inputs/NonStrictModule.swift @@ -0,0 +1 @@ +public class NonStrictClass { } diff --git a/test/Concurrency/Inputs/OtherActors.swift b/test/Concurrency/Inputs/OtherActors.swift new file mode 100644 index 0000000000000..068a84ab61a4c --- /dev/null +++ b/test/Concurrency/Inputs/OtherActors.swift @@ -0,0 +1,7 @@ +public class SomeClass { } + +public actor OtherModuleActor { + public let a: Int = 1 + public nonisolated let b: Int = 2 + public let c: SomeClass = SomeClass() +} diff --git a/test/Concurrency/Inputs/ShadowsConcur.swift b/test/Concurrency/Inputs/ShadowsConcur.swift index a79623a533d47..b976b6980cc78 100644 --- a/test/Concurrency/Inputs/ShadowsConcur.swift +++ b/test/Concurrency/Inputs/ShadowsConcur.swift @@ -1,5 +1,5 @@ -public struct Task { +public struct UnsafeCurrentTask { public var someProperty : String = "123" public init() { } } diff --git a/test/Concurrency/Inputs/StrictModule.swift b/test/Concurrency/Inputs/StrictModule.swift new file mode 100644 index 0000000000000..94a549cd381fe --- /dev/null +++ b/test/Concurrency/Inputs/StrictModule.swift @@ -0,0 +1 @@ +public struct StrictStruct: Hashable { } diff --git a/test/Concurrency/LLDBDebuggerFunctionActorExtension.swift b/test/Concurrency/LLDBDebuggerFunctionActorExtension.swift index 664b0c771ee64..00044ad118b78 100644 --- a/test/Concurrency/LLDBDebuggerFunctionActorExtension.swift +++ b/test/Concurrency/LLDBDebuggerFunctionActorExtension.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -debugger-support +// RUN: %target-typecheck-verify-swift -disable-availability-checking -debugger-support // REQUIRES: concurrency // This test simulates LLDB's expression evaluator makeing an otherwise illegal diff --git a/test/Concurrency/Reflection/reflect_task.swift b/test/Concurrency/Reflection/reflect_task.swift new file mode 100644 index 0000000000000..ebe2aebe9f500 --- /dev/null +++ b/test/Concurrency/Reflection/reflect_task.swift @@ -0,0 +1,49 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -parse-stdlib -parse-as-library -lswiftSwiftReflectionTest %s -o %t/reflect_task +// RUN: %target-codesign %t/reflect_task + +// RUN: %target-run %target-swift-reflection-test %t/reflect_task | %FileCheck %s --dump-input=fail + +// REQUIRES: reflection_test_support +// REQUIRES: executable_test +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import Swift +import _Concurrency + +import SwiftReflectionTest + +@_silgen_name("swift_task_getCurrent") +func _getCurrentAsyncTask() -> UInt + +func add(_ a: UInt, _ b: UInt) async -> UInt { + if b == 0 { + reflect(asyncTask: _getCurrentAsyncTask()) + // CHECK: Reflecting an async task. + // CHECK: Async task {{0x[0-9a-fA-F]*}} + + // The actual number of chunks we'll get depends on internal implementation + // details that we don't want this test to depend on. We'll just make sure + // we get at least two, and ignore the details. + // CHECK: Allocation block {{0x[0-9a-fA-F]*}} + // CHECK: Chunk at {{0x[0-9a-fA-F]*}} length {{[0-9]*}} kind {{[0-9]*}} + // CHECK: Allocation block {{0x[0-9a-fA-F]*}} + // CHECK: Chunk at {{0x[0-9a-fA-F]*}} length {{[0-9]*}} kind {{[0-9]*}} + return a + } else { + return await add(a, b - 1) + 1 + } +} + +@main struct Main { + static func main() async { + let n = await add(100, 100) + reflect(any: n) + + doneReflecting() + } +} + +// CHECK: Done. diff --git a/test/Concurrency/Runtime/Inputs/objc_async.h b/test/Concurrency/Runtime/Inputs/objc_async.h index c3a267dbb3538..90f953c549110 100644 --- a/test/Concurrency/Runtime/Inputs/objc_async.h +++ b/test/Concurrency/Runtime/Inputs/objc_async.h @@ -8,6 +8,12 @@ @end +@interface MutableButt: Butt +@end + +@interface MutableButt_2Fast2Furious: MutableButt +@end + @interface Farm: NSObject -(void)getDogWithCompletion:(void (^ _Nonnull)(NSInteger))completionHandler diff --git a/test/Concurrency/Runtime/Inputs/objc_async.m b/test/Concurrency/Runtime/Inputs/objc_async.m index 4e9d989920f1f..56506bd9e186b 100644 --- a/test/Concurrency/Runtime/Inputs/objc_async.m +++ b/test/Concurrency/Runtime/Inputs/objc_async.m @@ -14,6 +14,13 @@ - (void)butt:(NSInteger)x completionHandler:(void (^)(NSInteger))handler { @end +@implementation MutableButt: Butt +@end + +@implementation MutableButt_2Fast2Furious: MutableButt +@end + + @implementation Farm -(void)getDogWithCompletion:(void (^ _Nonnull)(NSInteger))completionHandler { @@ -30,7 +37,7 @@ -(void)obtainCat:(void (^ _Nonnull)(NSInteger, NSError* _Nullable))completionHan void scheduleButt(Butt *b, NSString *s) { [b butt: 1738 completionHandler: ^(NSInteger i) { - printf("butt %p named %s occurred at %zd", b, [s UTF8String], (ssize_t)i); + printf("butt %p named %s occurred at %zd\n", b, [s UTF8String], (ssize_t)i); fflush(stdout); }]; } diff --git a/test/Concurrency/Runtime/actor_counters.swift b/test/Concurrency/Runtime/actor_counters.swift index 53294816bd9a3..6ff7794c5a9e7 100644 --- a/test/Concurrency/Runtime/actor_counters.swift +++ b/test/Concurrency/Runtime/actor_counters.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) +// RUN: %target-run-simple-swift( -Xfrontend -sil-verify-all -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,7 +8,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Counter { private var value = 0 private let scratchBuffer: UnsafeMutableBufferPointer @@ -28,10 +28,21 @@ actor Counter { value = value + 1 return current } + + deinit { + for i in 0..] = [] for i in 0.. Int { + print("manage") + return 0 + } + + func other() async -> Int{ + print("other") + return 0 + } +} + + +@available(SwiftStdlib 5.5, *) +func test() { + detach { + let x = await Manager.shared.manage() + print(x) + } + detach { + let x = await Manager.shared.other() + print(x) + } +} + +if #available(SwiftStdlib 5.5, *) { + test() + sleep(30) +} else { + print("manage") + print("0") + print("other") + print("0") +} +// CHECK-DAG: manage +// CHECK-DAG: 0 +// CHECK-DAG: other +// CHECK-DAG: 0 diff --git a/test/Concurrency/Runtime/actor_init.swift b/test/Concurrency/Runtime/actor_init.swift new file mode 100644 index 0000000000000..5ac56394786b9 --- /dev/null +++ b/test/Concurrency/Runtime/actor_init.swift @@ -0,0 +1,72 @@ +// RUN: %target-run-simple-swift(%import-libdispatch -parse-as-library) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: libdispatch + +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +@available(SwiftStdlib 5.5, *) +actor Number { + var val: Int + var task: Task? + + func increment() { + print("did increment") + val += 1 + } + + func fib(n: Int) -> Int { + if n < 2 { + return n + } + return fib(n: n-1) + fib(n: n-2) + } + + init() async { + val = 0 + + task = Task.detached(priority: .high) { await self.increment() } + + // do some synchronous work + let ans = fib(n: 37) + guard ans == 24157817 else { + fatalError("got ans = \(ans). miscomputation?") + } + + // make sure task didn't modify me! + guard val == 0 else { + fatalError("race!") + } + + print("finished init()") + } + + init(iterations: Int) async { + var iter = iterations + repeat { + val = iter + iter -= 1 + } while iter > 0 + } +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + + // CHECK: finished init() + // CHECK-NEXT: did increment + + let n1 = await Number() + await n1.task!.value + + let n2 = await Number(iterations: 1000) + let val = await n2.val + guard val == 1 else { + fatalError("wrong val setting! got \(val)") + } + } +} + diff --git a/test/Concurrency/Runtime/actor_keypaths.swift b/test/Concurrency/Runtime/actor_keypaths.swift index 2e44917b223e9..d1a7f2f7407ad 100644 --- a/test/Concurrency/Runtime/actor_keypaths.swift +++ b/test/Concurrency/Runtime/actor_keypaths.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch) // REQUIRES: executable_test // REQUIRES: concurrency @@ -7,19 +7,34 @@ // UNSUPPORTED: back_deployment_runtime actor Page { - nonisolated let initialNumWords : Int + let initialNumWords : Int - @actorIndependent(unsafe) - var numWords : Int + private let numWordsMem: UnsafeMutablePointer - init(_ words : Int) { - numWords = words + nonisolated + var numWords : Int { + get { numWordsMem.pointee } + set { numWordsMem.pointee = newValue } + } + + private init(withWords words : Int) { initialNumWords = words + numWordsMem = .allocate(capacity: 1) + numWordsMem.initialize(to: words) + } + + convenience init(_ words: Int) { + self.init(withWords: words) + numWords = words + } + + deinit { + numWordsMem.deallocate() } } actor Book { - nonisolated let pages : [Page] + let pages : [Page] init(_ numPages : Int) { var stack : [Page] = [] @@ -29,7 +44,7 @@ actor Book { pages = stack } - @actorIndependent + nonisolated subscript(_ page : Int) -> Page { return pages[page] } diff --git a/test/Concurrency/Runtime/async.swift b/test/Concurrency/Runtime/async.swift index d33aeb84f81d0..102fe0e2578af 100644 --- a/test/Concurrency/Runtime/async.swift +++ b/test/Concurrency/Runtime/async.swift @@ -1,11 +1,12 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch) // REQUIRES: executable_test // REQUIRES: concurrency // REQUIRES: libdispatch -// rdar://76038845 +// rdar://82123254 // UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime import Dispatch import StdlibUnittest @@ -19,11 +20,11 @@ import StdlibUnittest var asyncTests = TestSuite("Async") -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor MyActor { func synchronous() { } - func doSomething(expectedPriority: Task.Priority) { + func doSomething(expectedPriority: TaskPriority) { async { synchronous() // okay to be synchronous assert(Task.currentPriority == expectedPriority) @@ -31,7 +32,7 @@ actor MyActor { } } -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(SwiftStdlib 5.5, *) { let actor = MyActor() asyncTests.test("Detach") { @@ -67,4 +68,3 @@ if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { } runAllTests() - diff --git a/test/Concurrency/Runtime/async_initializer.swift b/test/Concurrency/Runtime/async_initializer.swift index db5dee873130b..a2c09abd56fda 100644 --- a/test/Concurrency/Runtime/async_initializer.swift +++ b/test/Concurrency/Runtime/async_initializer.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -enable-experimental-concurrency %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,7 +8,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor NameGenerator { private var counter = 0 private var prefix : String @@ -19,13 +19,13 @@ actor NameGenerator { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) protocol Person { init() async var name : String { get set } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) class EarthPerson : Person { private static let oracle = NameGenerator("Earthling") @@ -40,7 +40,7 @@ class EarthPerson : Person { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) class NorthAmericaPerson : EarthPerson { private static let oracle = NameGenerator("NorthAmerican") required init() async { @@ -53,7 +53,7 @@ class NorthAmericaPerson : EarthPerson { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) class PrecariousClass { init?(nilIt : Int) async { let _ : Optional = await (detach { nil }).get() @@ -87,7 +87,7 @@ enum Something : Error { case bogus } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct PrecariousStruct { init?(nilIt : Int) async { let _ : Optional = await (detach { nil }).get() @@ -122,7 +122,7 @@ struct PrecariousStruct { // CHECK-NEXT: struct threw // CHECK: done -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct RunIt { static func main() async { let people : [Person] = [ diff --git a/test/Concurrency/Runtime/async_let_fibonacci.swift b/test/Concurrency/Runtime/async_let_fibonacci.swift index f4d7fe352c5bb..6510e1610028e 100644 --- a/test/Concurrency/Runtime/async_let_fibonacci.swift +++ b/test/Concurrency/Runtime/async_let_fibonacci.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) // REQUIRES: executable_test // REQUIRES: concurrency @@ -19,7 +19,7 @@ func fib(_ n: Int) -> Int { return first } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncFib(_ n: Int) async -> Int { if n == 0 || n == 1 { return n @@ -39,7 +39,7 @@ func asyncFib(_ n: Int) async -> Int { return result } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func runFibonacci(_ n: Int) async { let result = await asyncFib(n) @@ -48,7 +48,7 @@ func runFibonacci(_ n: Int) async { assert(result == fib(n)) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await runFibonacci(10) diff --git a/test/Concurrency/Runtime/async_let_throw_completion_order.swift b/test/Concurrency/Runtime/async_let_throw_completion_order.swift new file mode 100644 index 0000000000000..6e6cabab9093e --- /dev/null +++ b/test/Concurrency/Runtime/async_let_throw_completion_order.swift @@ -0,0 +1,33 @@ +// rdar://81481317 +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: libdispatch + +// rdar://82123254 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +struct Bad: Error {} + +class Foo { init() async throws {}; deinit { print("Foo down") } } +class Bar { init() async throws { throw Bad() }; deinit { print("Bar down") } } +class Baz { init() async throws {}; deinit { print("Baz down") } } + +func zim(y: Bar, x: Foo, z: Baz) { print("hooray") } + +@main struct Butt { + + static func main() async { + do { + async let x = Foo() + async let y = Bar() + async let z = Baz() + + return try await zim(y: y, x: x, z: z) + } catch { + // CHECK: oopsie woopsie + print("oopsie woopsie") + } + } +} diff --git a/test/Concurrency/Runtime/async_let_throws.swift b/test/Concurrency/Runtime/async_let_throws.swift index 0c801a4ca52c4..26fc8df9084f5 100644 --- a/test/Concurrency/Runtime/async_let_throws.swift +++ b/test/Concurrency/Runtime/async_let_throws.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -14,7 +14,7 @@ func boom() throws -> Int { throw Boom() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test() async { async let result = boom() @@ -25,7 +25,7 @@ func test() async { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test() diff --git a/test/Concurrency/Runtime/async_properties_actor.swift b/test/Concurrency/Runtime/async_properties_actor.swift index 11405aa9d0071..694f96a7c12d8 100644 --- a/test/Concurrency/Runtime/async_properties_actor.swift +++ b/test/Concurrency/Runtime/async_properties_actor.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -enable-copy-propagation -Xfrontend -enable-experimental-concurrency %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -enable-copy-propagation -Xfrontend -disable-availability-checking %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,6 +8,21 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime +struct Container { + @MainActor static var counter: Int = 10 + @MainActor static var this: Container? + + var noniso: Int = 20 + + static func getCount() async -> Int { + return await counter + } + + static func getValue() async -> Int? { + return await this?.noniso + } +} + @propertyWrapper struct SuccessTracker { private var _stored: Bool @@ -142,6 +157,13 @@ actor Tester { print("done property wrapper test") return true } + + func doContainerTest() async -> Bool { + var total: Int = 0 + total += await Container.getCount() + total += await Container.getValue() ?? 0 + return total == 10 + } } @globalActor @@ -171,6 +193,10 @@ var expectedResult : Bool { fatalError("fail property wrapper test") } + guard await test.doContainerTest() == success else { + fatalError("fail container test") + } + // CHECK: done all testing print("done all testing") } diff --git a/test/Concurrency/Runtime/async_sequence.swift b/test/Concurrency/Runtime/async_sequence.swift index 39099699f9060..65e0fbf881595 100644 --- a/test/Concurrency/Runtime/async_sequence.swift +++ b/test/Concurrency/Runtime/async_sequence.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) // REQUIRES: executable_test // REQUIRES: concurrency @@ -14,27 +14,27 @@ import StdlibUnittest // Utility functions for closure based operators to force them into throwing // and async and throwing async contexts. -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func throwing(_ value: T) throws -> T { return value } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asynchronous(_ value: T) async -> T { return value } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asynchronousThrowing(_ value: T) async throws -> T { return value } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct Failure: Error, Equatable { var value = 1 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func failable( _ results: [Result] ) -> AsyncThrowingMapSequence]>, T> { @@ -42,7 +42,7 @@ func failable( } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension Sequence { @inlinable public var async: AsyncLazySequence { @@ -52,7 +52,7 @@ extension Sequence { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) public struct AsyncLazySequence: AsyncSequence { public typealias Element = S.Element public typealias AsyncIterator = Iterator @@ -85,7 +85,7 @@ public struct AsyncLazySequence: AsyncSequence { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension AsyncSequence { @inlinable public func collect() async rethrows -> [Element] { @@ -98,7 +98,7 @@ extension AsyncSequence { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension AsyncSequence where Element: Equatable { func `throw`(_ error: Error, on element: Element) -> AsyncThrowingMapSequence { return map { (value: Element) throws -> Element in @@ -110,7 +110,7 @@ extension AsyncSequence where Element: Equatable { @main struct Main { static func main() async { - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(SwiftStdlib 5.5, *) { var AsyncLazySequenceTests = TestSuite("AsyncLazySequence") diff --git a/test/Concurrency/Runtime/async_stream.swift b/test/Concurrency/Runtime/async_stream.swift new file mode 100644 index 0000000000000..cd90aa559fb4b --- /dev/null +++ b/test/Concurrency/Runtime/async_stream.swift @@ -0,0 +1,414 @@ +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) + +// REQUIRES: concurrency +// REQUIRES: libdispatch +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib + +// rdar://78109470 +// UNSUPPORTED: back_deployment_runtime + +// https://bugs.swift.org/browse/SR-14466 +// UNSUPPORTED: OS=windows-msvc + +// Race condition +// REQUIRES: rdar78033828 + +import _Concurrency +import StdlibUnittest +import Dispatch + +struct SomeError: Error, Equatable { + var value = Int.random(in: 0..<100) +} + +var tests = TestSuite("AsyncStream") + +@main struct Main { + static func main() async { + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + final class Expectation: UnsafeSendable { + var fulfilled = false + } + + tests.test("yield with no awaiting next") { + let series = AsyncStream(String.self) { continuation in + continuation.yield("hello") + } + } + + tests.test("yield with no awaiting next throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + continuation.yield("hello") + } + } + + tests.test("yield with awaiting next") { + let series = AsyncStream(String.self) { continuation in + continuation.yield("hello") + } + var iterator = series.makeAsyncIterator() + expectEqual(await iterator.next(), "hello") + } + + tests.test("yield with awaiting next throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + continuation.yield("hello") + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("yield with awaiting next 2") { + let series = AsyncStream(String.self) { continuation in + continuation.yield("hello") + continuation.yield("world") + } + var iterator = series.makeAsyncIterator() + expectEqual(await iterator.next(), "hello") + expectEqual(await iterator.next(), "world") + } + + tests.test("yield with awaiting next 2 throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + continuation.yield("hello") + continuation.yield("world") + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("yield with awaiting next 2 and finish") { + let series = AsyncStream(String.self) { continuation in + continuation.yield("hello") + continuation.yield("world") + continuation.finish() + } + var iterator = series.makeAsyncIterator() + expectEqual(await iterator.next(), "hello") + expectEqual(await iterator.next(), "world") + expectEqual(await iterator.next(), nil) + } + + tests.test("yield with awaiting next 2 and finish throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + continuation.yield("hello") + continuation.yield("world") + continuation.finish() + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + expectEqual(try await iterator.next(), nil) + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("yield with awaiting next 2 and throw") { + let thrownError = SomeError() + let series = AsyncThrowingStream(String.self) { continuation in + continuation.yield("hello") + continuation.yield("world") + continuation.finish(throwing: thrownError) + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + try await iterator.next() + expectUnreachable("expected thrown error") + } catch { + if let failure = error as? SomeError { + expectEqual(failure, thrownError) + } else { + expectUnreachable("unexpected error type") + } + } + } + + tests.test("yield with no awaiting next detached") { + let series = AsyncStream(String.self) { continuation in + detach { + continuation.yield("hello") + } + } + } + + tests.test("yield with no awaiting next detached throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + detach { + continuation.yield("hello") + } + } + } + + tests.test("yield with awaiting next detached") { + let series = AsyncStream(String.self) { continuation in + detach { + continuation.yield("hello") + } + } + var iterator = series.makeAsyncIterator() + expectEqual(await iterator.next(), "hello") + } + + tests.test("yield with awaiting next detached throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + detach { + continuation.yield("hello") + } + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("yield with awaiting next 2 detached") { + let series = AsyncStream(String.self) { continuation in + detach { + continuation.yield("hello") + continuation.yield("world") + } + } + var iterator = series.makeAsyncIterator() + expectEqual(await iterator.next(), "hello") + expectEqual(await iterator.next(), "world") + } + + tests.test("yield with awaiting next 2 detached throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + detach { + continuation.yield("hello") + continuation.yield("world") + } + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("yield with awaiting next 2 and finish detached") { + let series = AsyncStream(String.self) { continuation in + detach { + continuation.yield("hello") + continuation.yield("world") + continuation.finish() + } + } + var iterator = series.makeAsyncIterator() + expectEqual(await iterator.next(), "hello") + expectEqual(await iterator.next(), "world") + expectEqual(await iterator.next(), nil) + } + + tests.test("yield with awaiting next 2 and finish detached throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + detach { + continuation.yield("hello") + continuation.yield("world") + continuation.finish() + } + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + expectEqual(try await iterator.next(), nil) + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("yield with awaiting next 2 and throw detached") { + let thrownError = SomeError() + let series = AsyncThrowingStream(String.self) { continuation in + detach { + continuation.yield("hello") + continuation.yield("world") + continuation.finish(throwing: thrownError) + } + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + try await iterator.next() + expectUnreachable("expected thrown error") + } catch { + if let failure = error as? SomeError { + expectEqual(failure, thrownError) + } else { + expectUnreachable("unexpected error type") + } + } + } + + tests.test("yield with awaiting next 2 and finish detached with value after finish") { + let series = AsyncStream(String.self) { continuation in + detach { + continuation.yield("hello") + continuation.yield("world") + continuation.finish() + continuation.yield("This should not be emitted") + } + } + var iterator = series.makeAsyncIterator() + expectEqual(await iterator.next(), "hello") + expectEqual(await iterator.next(), "world") + expectEqual(await iterator.next(), nil) + expectEqual(await iterator.next(), nil) + } + + tests.test("yield with awaiting next 2 and finish detached with value after finish throwing") { + let series = AsyncThrowingStream(String.self) { continuation in + detach { + continuation.yield("hello") + continuation.yield("world") + continuation.finish() + continuation.yield("This should not be emitted") + } + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + expectEqual(try await iterator.next(), nil) + expectEqual(try await iterator.next(), nil) + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("yield with awaiting next 2 and finish detached with throw after finish throwing") { + let thrownError = SomeError() + let series = AsyncThrowingStream(String.self) { continuation in + detach { + continuation.yield("hello") + continuation.yield("world") + continuation.finish() + continuation.finish(throwing: thrownError) + } + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + expectEqual(try await iterator.next(), nil) + expectEqual(try await iterator.next(), nil) + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("yield with awaiting next 2 and finish with throw after finish throwing") { + let thrownError = SomeError() + let series = AsyncThrowingStream(String.self) { continuation in + continuation.yield("hello") + continuation.yield("world") + continuation.finish() + continuation.finish(throwing: thrownError) + } + var iterator = series.makeAsyncIterator() + do { + expectEqual(try await iterator.next(), "hello") + expectEqual(try await iterator.next(), "world") + expectEqual(try await iterator.next(), nil) + expectEqual(try await iterator.next(), nil) + } catch { + expectUnreachable("unexpected error thrown") + } + } + + tests.test("cancellation behavior on deinit with no values being awaited") { + let expectation = Expectation() + + func scopedLifetime(_ expectation: Expectation) { + let series = AsyncStream(String.self) { continuation in + continuation.onTermination = { @Sendable _ in expectation.fulfilled = true } + } + } + + scopedLifetime(expectation) + + expectTrue(expectation.fulfilled) + } + + tests.test("termination behavior on deinit with no values being awaited") { + let expectation = Expectation() + + func scopedLifetime(_ expectation: Expectation) { + let series = AsyncStream(String.self) { continuation in + continuation.onTermination = { @Sendable _ in expectation.fulfilled = true } + continuation.finish() + } + } + + scopedLifetime(expectation) + + expectTrue(expectation.fulfilled) + } + + tests.test("cancellation behavior on deinit with no values being awaited") { + let expectation = Expectation() + + func scopedLifetime(_ expectation: Expectation) { + let series = AsyncStream(String.self) { continuation in + continuation.onTermination = { @Sendable terminal in + switch terminal { + case .cancelled: + expectation.fulfilled = true + default: break + } + } + } + } + + scopedLifetime(expectation) + + expectTrue(expectation.fulfilled) + } + + tests.test("cancellation behavior on deinit with no values being awaited throwing") { + let expectation = Expectation() + + func scopedLifetime(_ expectation: Expectation) { + let series = AsyncThrowingStream(String.self) { continuation in + continuation.onTermination = { @Sendable terminal in + switch terminal { + case .cancelled: + expectation.fulfilled = true + default: break + } + } + } + } + + scopedLifetime(expectation) + + expectTrue(expectation.fulfilled) + } + + await runAllTestsAsync() + } + } +} + + diff --git a/test/Concurrency/Runtime/async_task_async_let_child_cancel.swift b/test/Concurrency/Runtime/async_task_async_let_child_cancel.swift index 68d8dce3a5212..9964a8bda19c6 100644 --- a/test/Concurrency/Runtime/async_task_async_let_child_cancel.swift +++ b/test/Concurrency/Runtime/async_task_async_let_child_cancel.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency @@ -7,7 +7,9 @@ // UNSUPPORTED: back_deployment_runtime // UNSUPPORTED: use_os_stdlib -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +// REQUIRES: rdar_77671328 + +@available(SwiftStdlib 5.5, *) func printWaitPrint(_ int: Int) async -> Int { print("start, cancelled:\(Task.isCancelled), id:\(int)") while !Task.isCancelled { @@ -17,7 +19,7 @@ func printWaitPrint(_ int: Int) async -> Int { return int } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test() async { let h = detach { await printWaitPrint(0) @@ -68,7 +70,7 @@ func test() async { print("exit") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test() diff --git a/test/Concurrency/Runtime/async_task_cancellation_early.swift b/test/Concurrency/Runtime/async_task_cancellation_early.swift index 0b453ed3ac85a..56565465a1e6e 100644 --- a/test/Concurrency/Runtime/async_task_cancellation_early.swift +++ b/test/Concurrency/Runtime/async_task_cancellation_early.swift @@ -1,19 +1,22 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency // REQUIRES: libdispatch +// Temporarily disabled to unblock PR testing: +// REQUIRES: rdar80745964 + // rdar://76038845 // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_detach_cancel_child_early() async { print(#function) // CHECK: test_detach_cancel_child_early - let h: Task.Handle = detach { + let h: Task = Task.detached { async let childCancelled: Bool = { () -> Bool in await Task.sleep(2_000_000_000) return Task.isCancelled @@ -21,21 +24,18 @@ func test_detach_cancel_child_early() async { let xx = await childCancelled print("child, cancelled: \(xx)") // CHECK: child, cancelled: true - let cancelled = Task.isCancelled - print("self, cancelled: \(cancelled )") // CHECK: self, cancelled: true + let cancelled = Task.isCancelled + print("self, cancelled: \(cancelled)") // CHECK: self, cancelled: true return cancelled } - // no sleep here -- this confirms that the child task `x` - // carries the cancelled flag, as it is started from a cancelled task. - h.cancel() print("handle cancel") - let got = try! await h.get() + let got = try! await h.value print("was cancelled: \(got)") // CHECK: was cancelled: true } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_detach_cancel_child_early() diff --git a/test/Concurrency/Runtime/async_task_cancellation_while_running.swift b/test/Concurrency/Runtime/async_task_cancellation_while_running.swift index 60d4955420dfb..f7db2ce79b1e3 100644 --- a/test/Concurrency/Runtime/async_task_cancellation_while_running.swift +++ b/test/Concurrency/Runtime/async_task_cancellation_while_running.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,11 +10,13 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +let seconds: UInt64 = 1_000_000_000 + +@available(SwiftStdlib 5.5, *) func test_detach_cancel_while_child_running() async { - let h: Task.Handle = detach { + let task: Task = Task.detached { async let childCancelled: Bool = { () -> Bool in - await Task.sleep(3_000_000_000) + await Task.sleep(3 * seconds) return Task.isCancelled }() @@ -26,17 +28,74 @@ func test_detach_cancel_while_child_running() async { } // sleep here, i.e. give the task a moment to start running - await Task.sleep(2_000_000_000) + await Task.sleep(2 * seconds) + + task.cancel() + print("task.cancel()") + let got = try! await task.get() + print("was cancelled: \(got)") // CHECK: was cancelled: true +} + +@available(SwiftStdlib 5.5, *) +func test_cancel_while_withTaskCancellationHandler_inflight() async { + let task: Task = Task.detached { + await withTaskCancellationHandler { + await Task.sleep(2 * seconds) + print("operation-1") + await Task.sleep(1 * seconds) + print("operation-2") + return Task.isCancelled + } onCancel: { + print("onCancel") + } + + } + + await Task.sleep(1 * seconds) + + // CHECK: task.cancel() + // CHECK: onCancel + // CHECK: operation-1 + // CHECK: operation-2 + print("task.cancel()") + task.cancel() + let got = try! await task.get() + print("was cancelled: \(got)") // CHECK: was cancelled: true +} + +@available(SwiftStdlib 5.5, *) +func test_cancel_while_withTaskCancellationHandler_onlyOnce() async { + let task: Task = Task.detached { + await withTaskCancellationHandler { + await Task.sleep(2 * seconds) + await Task.sleep(2 * seconds) + await Task.sleep(2 * seconds) + print("operation-done") + return Task.isCancelled + } onCancel: { + print("onCancel") + } + } + + await Task.sleep(1 * seconds) - h.cancel() - print("handle cancel") - let got = try! await h.get() + // CHECK: task.cancel() + // CHECK: onCancel + // onCancel runs only once, even though we attempt to cancel the task many times + // CHECK-NEXT: operation-done + print("task.cancel()") + task.cancel() + task.cancel() + task.cancel() + let got = try! await task.get() print("was cancelled: \(got)") // CHECK: was cancelled: true } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_detach_cancel_while_child_running() + await test_cancel_while_withTaskCancellationHandler_inflight() + await test_cancel_while_withTaskCancellationHandler_onlyOnce() } } diff --git a/test/Concurrency/Runtime/async_task_detach.swift b/test/Concurrency/Runtime/async_task_detach.swift index 21c44d7151985..86b9bb4f58f8f 100644 --- a/test/Concurrency/Runtime/async_task_detach.swift +++ b/test/Concurrency/Runtime/async_task_detach.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -21,7 +21,7 @@ class X { struct Boom: Error {} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_detach() async { let x = X() let h = detach { @@ -33,7 +33,7 @@ func test_detach() async { // CHECK: X: deinit } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_detach_throw() async { let x = X() let h = detach { @@ -51,7 +51,7 @@ func test_detach_throw() async { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_detach() diff --git a/test/Concurrency/Runtime/async_task_handle_cancellation.swift b/test/Concurrency/Runtime/async_task_handle_cancellation.swift index 36e540204e72d..aaa12445df232 100644 --- a/test/Concurrency/Runtime/async_task_handle_cancellation.swift +++ b/test/Concurrency/Runtime/async_task_handle_cancellation.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency @@ -7,14 +7,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -// This test is flaky on VS2017 (unknown reasons) -// UNSUPPORTED: MSVC_VER=15.0 - -// This test is failing on windows. SR-14447. -// -// UNSUPPORTED: OS=windows-msvc - -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { let handle = detach { diff --git a/test/Concurrency/Runtime/async_task_locals_basic.swift b/test/Concurrency/Runtime/async_task_locals_basic.swift index b106b18666798..5062a659636eb 100644 --- a/test/Concurrency/Runtime/async_task_locals_basic.swift +++ b/test/Concurrency/Runtime/async_task_locals_basic.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -17,7 +17,7 @@ final class StringLike: Sendable, CustomStringConvertible { var description: String { value } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) enum TL { @TaskLocal @@ -33,7 +33,7 @@ enum TL { static var clazz: ClassTaskLocal? } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) final class ClassTaskLocal: Sendable { init() { print("clazz init \(ObjectIdentifier(self))") @@ -44,7 +44,17 @@ final class ClassTaskLocal: Sendable { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) +@discardableResult +func printTaskLocalAsync( + _ key: TaskLocal, + _ expected: V? = nil, + file: String = #file, line: UInt = #line +) async -> V? { + printTaskLocal(key, expected, file: file, line: line) +} + +@available(SwiftStdlib 5.5, *) @discardableResult func printTaskLocal( _ key: TaskLocal, @@ -62,17 +72,17 @@ func printTaskLocal( // ==== ------------------------------------------------------------------------ -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func simple() async { printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (0) - await TL.$number.withValue(1) { + TL.$number.withValue(1) { printTaskLocal(TL.$number) // CHECK-NEXT: TaskLocal(defaultValue: 0) (1) } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func simple_deinit() async { - await TL.$clazz.withValue(ClassTaskLocal()) { + TL.$clazz.withValue(ClassTaskLocal()) { // CHECK: clazz init [[C:.*]] printTaskLocal(TL.$clazz) // CHECK: TaskLocal>(defaultValue: nil) (Optional(main.ClassTaskLocal)) } @@ -83,10 +93,10 @@ func simple_deinit() async { struct Boom: Error { let value: String } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func simple_throw() async { do { - try await TL.$clazz.withValue(ClassTaskLocal()) { + try TL.$clazz.withValue(ClassTaskLocal()) { throw Boom(value: "oh no!") } } catch { @@ -95,13 +105,13 @@ func simple_throw() async { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func nested() async { printTaskLocal(TL.$string) // CHECK: TaskLocal(defaultValue: ) () - await TL.$string.withValue("hello") { + TL.$string.withValue("hello") { printTaskLocal(TL.$number) // CHECK-NEXT: TaskLocal(defaultValue: 0) (0) printTaskLocal(TL.$string)// CHECK-NEXT: TaskLocal(defaultValue: ) (hello) - await TL.$number.withValue(2) { + TL.$number.withValue(2) { printTaskLocal(TL.$number) // CHECK-NEXT: TaskLocal(defaultValue: 0) (2) printTaskLocal(TL.$string, "hello") // CHECK: TaskLocal(defaultValue: ) (hello) } @@ -112,14 +122,14 @@ func nested() async { printTaskLocal(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) () } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func nested_allContribute() async { printTaskLocal(TL.$string) // CHECK: TaskLocal(defaultValue: ) () - await TL.$string.withValue("one") { + TL.$string.withValue("one") { printTaskLocal(TL.$string, "one")// CHECK-NEXT: TaskLocal(defaultValue: ) (one) - await TL.$string.withValue("two") { + TL.$string.withValue("two") { printTaskLocal(TL.$string, "two") // CHECK-NEXT: TaskLocal(defaultValue: ) (two) - await TL.$string.withValue("three") { + TL.$string.withValue("three") { printTaskLocal(TL.$string, "three") // CHECK-NEXT: TaskLocal(defaultValue: ) (three) } printTaskLocal(TL.$string, "two") // CHECK-NEXT: TaskLocal(defaultValue: ) (two) @@ -129,14 +139,14 @@ func nested_allContribute() async { printTaskLocal(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) () } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func nested_3_onlyTopContributes() async { printTaskLocal(TL.$string) // CHECK: TaskLocal(defaultValue: ) () - await TL.$string.withValue("one") { + TL.$string.withValue("one") { printTaskLocal(TL.$string)// CHECK-NEXT: TaskLocal(defaultValue: ) (one) - await TL.$number.withValue(2) { + TL.$number.withValue(2) { printTaskLocal(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) - await TL.$number.withValue(3) { + TL.$number.withValue(3) { printTaskLocal(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) } printTaskLocal(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) @@ -146,16 +156,50 @@ func nested_3_onlyTopContributes() async { printTaskLocal(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) () } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) +func nested_3_onlyTopContributesAsync() async { + await printTaskLocalAsync(TL.$string) // CHECK: TaskLocal(defaultValue: ) () + await TL.$string.withValue("one") { + await printTaskLocalAsync(TL.$string)// CHECK-NEXT: TaskLocal(defaultValue: ) (one) + await TL.$number.withValue(2) { + await printTaskLocalAsync(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) + await TL.$number.withValue(3) { + await printTaskLocalAsync(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) + } + await printTaskLocalAsync(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) + } + await printTaskLocalAsync(TL.$string)// CHECK-NEXT: TaskLocal(defaultValue: ) (one) + } + await printTaskLocalAsync(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) () +} + +@available(SwiftStdlib 5.5, *) +func nested_3_onlyTopContributesMixed() async { + await printTaskLocalAsync(TL.$string) // CHECK: TaskLocal(defaultValue: ) () + await TL.$string.withValue("one") { + await printTaskLocalAsync(TL.$string)// CHECK-NEXT: TaskLocal(defaultValue: ) (one) + await TL.$number.withValue(2) { + printTaskLocal(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) + TL.$number.withValue(3) { + printTaskLocal(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) + } + await printTaskLocalAsync(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) (one) + } + await printTaskLocalAsync(TL.$string)// CHECK-NEXT: TaskLocal(defaultValue: ) (one) + } + await printTaskLocalAsync(TL.$string) // CHECK-NEXT: TaskLocal(defaultValue: ) () +} + +@available(SwiftStdlib 5.5, *) func withLocal_body_mustNotEscape() async { var something = "Nice" - await TL.$string.withValue("xxx") { + TL.$string.withValue("xxx") { something = "very nice" } _ = something // silence not used warning } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await simple() @@ -164,5 +208,7 @@ func withLocal_body_mustNotEscape() async { await nested() await nested_allContribute() await nested_3_onlyTopContributes() + await nested_3_onlyTopContributesAsync() + await nested_3_onlyTopContributesMixed() } } diff --git a/test/Concurrency/Runtime/async_task_locals_copy_to_async.swift b/test/Concurrency/Runtime/async_task_locals_copy_to_async.swift new file mode 100644 index 0000000000000..e8465233578ba --- /dev/null +++ b/test/Concurrency/Runtime/async_task_locals_copy_to_async.swift @@ -0,0 +1,165 @@ +// REQUIRES: rdar80824152 +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: libdispatch + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +@available(SwiftStdlib 5.5, *) +enum TL { + @TaskLocal + static var number: Int = 0 + @TaskLocal + static var other: Int = 0 +} + +@available(SwiftStdlib 5.5, *) +@discardableResult +func printTaskLocal( + _ key: TaskLocal, + _ expected: V? = nil, + file: String = #file, line: UInt = #line +) -> V? { + let value = key.get() + print("\(key) (\(value)) at \(file):\(line)") + if let expected = expected { + assert("\(expected)" == "\(value)", + "Expected [\(expected)] but found: \(value), at \(file):\(line)") + } + return expected +} + +// ==== ------------------------------------------------------------------------ + +@available(SwiftStdlib 5.5, *) +func copyTo_async() async { + await TL.$number.withValue(1111) { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (1111) + + await TL.$number.withValue(2222) { + await TL.$other.withValue(9999) { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (2222) + printTaskLocal(TL.$other) // CHECK: TaskLocal(defaultValue: 0) (9999) + let handle = Task { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (2222) + printTaskLocal(TL.$other) // CHECK: TaskLocal(defaultValue: 0) (9999) + TL.$number.withValue(3333) { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (3333) + printTaskLocal(TL.$other) // CHECK: TaskLocal(defaultValue: 0) (9999) + } + } + + _ = await handle.value + } + } + } +} + +@available(SwiftStdlib 5.5, *) +func copyTo_async_noWait() async { + print(#function) + TL.$number.withValue(1111) { + TL.$number.withValue(2222) { + TL.$other.withValue(9999) { + Task { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (2222) + printTaskLocal(TL.$other) // CHECK: TaskLocal(defaultValue: 0) (9999) + TL.$number.withValue(3333) { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (3333) + printTaskLocal(TL.$other) // CHECK: TaskLocal(defaultValue: 0) (9999) + } + } + } + } + } + + let second = UInt64(100_000_000) // ns + await Task.sleep(2 * second) +} + +@available(SwiftStdlib 5.5, *) +class CustomClass { + @TaskLocal + static var current: CustomClass? + + init() { + print("init \(ObjectIdentifier(self))") + } + + deinit { + print("deinit \(ObjectIdentifier(self))") + } +} + +@available(SwiftStdlib 5.5, *) +func test_unstructured_retains() async { + let instance = CustomClass() + CustomClass.$current.withValue(instance) { + print("BEFORE send: \(String(reflecting: CustomClass.current))") + // don't await on the un-structured tasks on purpose, we want to see that the tasks + // themselves keep the object alive even if we don't hold onto them + Task { + print("in async task: \(String(reflecting: CustomClass.current))") + } + Task { + print("in async task: \(String(reflecting: CustomClass.current))") + } + print("AFTER send: \(String(reflecting: CustomClass.current))") + } + + // CHECK: init + // CHECK: BEFORE send: Optional(main.CustomClass) + // CHECK: in async task: Optional(main.CustomClass) + // CHECK: in async task: Optional(main.CustomClass) + // the deinit MUST NOT happen before the async tasks runs + // CHECK: deinit + await Task.sleep(2 * 1_000_000_000) +} + +@available(SwiftStdlib 5.5, *) +func test_unstructured_noValues() async { + await Task { + // no values to copy + }.value +} + +@available(SwiftStdlib 5.5, *) +func downloadImage(from url: String) async throws -> String { + await Task.sleep(10_000) + return "" +} + +@available(SwiftStdlib 5.5, *) +func test_unstructured_noValues_childTasks() async { + @Sendable func work() async throws { + let handle = Task { + try await downloadImage(from: "") + } + } + + // these child tasks have a parent pointer in their task local storage. + // we must not copy it when performing the copyTo for a new unstructured task. + async let one = work() + async let two = work() + async let three = work() + + try! await one + try! await two + try! await three + +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + await copyTo_async() + await copyTo_async_noWait() + await test_unstructured_retains() + await test_unstructured_noValues() + await test_unstructured_noValues_childTasks() + } +} diff --git a/test/Concurrency/Runtime/async_task_locals_copy_to_sync.swift b/test/Concurrency/Runtime/async_task_locals_copy_to_sync.swift new file mode 100644 index 0000000000000..a28a066900d6b --- /dev/null +++ b/test/Concurrency/Runtime/async_task_locals_copy_to_sync.swift @@ -0,0 +1,83 @@ +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: libdispatch + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import Dispatch + +// For sleep +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#endif + +@available(SwiftStdlib 5.5, *) +enum TL { + @TaskLocal + static var number: Int = 0 + @TaskLocal + static var other: Int = 0 +} + +@available(SwiftStdlib 5.5, *) +@discardableResult +func printTaskLocal( + _ key: TaskLocal, + _ expected: V? = nil, + file: String = #file, line: UInt = #line +) -> V? { + let value = key.get() + print("\(key) (\(value)) at \(file):\(line)") + if let expected = expected { + assert("\(expected)" == "\(value)", + "Expected [\(expected)] but found: \(value), at \(file):\(line)") + } + return expected +} + +// ==== ------------------------------------------------------------------------ + +@available(SwiftStdlib 5.5, *) +func copyTo_sync_noWait() { + print(#function) + TL.$number.withValue(1111) { + TL.$number.withValue(2222) { + TL.$other.withValue(9999) { + Task { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (2222) + printTaskLocal(TL.$other) // CHECK: TaskLocal(defaultValue: 0) (9999) + TL.$number.withValue(3333) { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (3333) + printTaskLocal(TL.$other) // CHECK: TaskLocal(defaultValue: 0) (9999) + } + } + } + } + } + + sleep(1) +} + +@available(SwiftStdlib 5.5, *) +func copyTo_sync_noValues() { + Task { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (0) + } + + sleep(1) +} + +/// Similar to tests in `async_task_locals_copy_to_async_ but without any task involved at the top level. +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() { + copyTo_sync_noWait() + copyTo_sync_noValues() + } +} diff --git a/test/Concurrency/Runtime/async_task_locals_groups.swift b/test/Concurrency/Runtime/async_task_locals_groups.swift index 91b89bcad5a4e..2a8cf68cfd54e 100644 --- a/test/Concurrency/Runtime/async_task_locals_groups.swift +++ b/test/Concurrency/Runtime/async_task_locals_groups.swift @@ -1,5 +1,6 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s +// REQUIRES: rdar82092187 // REQUIRES: executable_test // REQUIRES: concurrency // REQUIRES: libdispatch @@ -8,13 +9,13 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) enum TL { @TaskLocal static var number = 0 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @discardableResult func printTaskLocal( _ key: TaskLocal, @@ -32,7 +33,7 @@ func printTaskLocal( // ==== ------------------------------------------------------------------------ -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func groups() async { // no value _ = await withTaskGroup(of: Int.self) { group in @@ -44,7 +45,7 @@ func groups() async { group.spawn { printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (0) // inside the child task, set a value - _ = await TL.$number.withValue(1) { + _ = TL.$number.withValue(1) { printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (1) } printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (0) @@ -78,9 +79,39 @@ func groups() async { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) +func taskInsideGroup() async { + Task { + print("outside") // CHECK: outside + _ = await withTaskGroup(of: Int.self) { group -> Int in + print("in group") // CHECK: in group + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (0) + + for _ in 0..<5 { + Task { + printTaskLocal(TL.$number) + print("some task") + } + } + + return 0 + } + } + + // CHECK: some task + // CHECK: some task + // CHECK: some task + // CHECK: some task + + await Task.sleep(5 * 1_000_000_000) + +// await t.value +} + +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await groups() + await taskInsideGroup() } } diff --git a/test/Concurrency/Runtime/async_task_locals_prevent_illegal_use.swift b/test/Concurrency/Runtime/async_task_locals_prevent_illegal_use.swift index 9c97a4e82e9ad..ad80f962454da 100644 --- a/test/Concurrency/Runtime/async_task_locals_prevent_illegal_use.swift +++ b/test/Concurrency/Runtime/async_task_locals_prevent_illegal_use.swift @@ -1,4 +1,4 @@ -// RUN: %target-fail-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library %import-libdispatch) 2>&1 | %FileCheck %s +// RUN: %target-fail-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) 2>&1 | %FileCheck %s // // // TODO: could not figure out how to use 'not --crash' it never is used with target-run-simple-swift // This test is intended to *crash*, so we're using target-fail-simple-swift @@ -11,7 +11,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) enum TL { @TaskLocal static var number: Int = 2 @@ -19,13 +19,12 @@ enum TL { // ==== ------------------------------------------------------------------------ -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func bindAroundGroupSpawn() async { await TL.$number.withValue(1111) { // ok await withTaskGroup(of: Int.self) { group in - // CHECK: error: task-local: detected illegal task-local value binding at {{.*}}illegal_use.swift:[[# @LINE + 1]] - await TL.$number.withValue(2222) { // bad! + TL.$number.withValue(2222) { // bad! print("Survived, inside withValue!") // CHECK-NOT: Survived, inside withValue! group.spawn { 0 // don't actually perform the read, it would be unsafe. @@ -37,7 +36,7 @@ func bindAroundGroupSpawn() async { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await bindAroundGroupSpawn() diff --git a/test/Concurrency/Runtime/async_task_locals_spawn_let.swift b/test/Concurrency/Runtime/async_task_locals_spawn_let.swift index 5d505774f9730..7b9f936d4d831 100644 --- a/test/Concurrency/Runtime/async_task_locals_spawn_let.swift +++ b/test/Concurrency/Runtime/async_task_locals_spawn_let.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,13 +8,13 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) enum TL { @TaskLocal static var number: Int = 0 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @discardableResult func printTaskLocal( _ key: TaskLocal, @@ -32,7 +32,7 @@ func printTaskLocal( // ==== ------------------------------------------------------------------------ -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func async_let_nested() async { printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (0) async let x1: () = TL.$number.withValue(2) { @@ -54,7 +54,7 @@ func async_let_nested() async { printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (0) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func async_let_nested_skip_optimization() async { async let x1: Int? = TL.$number.withValue(2) { async let x2: Int? = { () async -> Int? in @@ -77,7 +77,7 @@ func async_let_nested_skip_optimization() async { _ = await x1 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await async_let_nested() diff --git a/test/Concurrency/Runtime/async_task_locals_synchronous_bind.swift b/test/Concurrency/Runtime/async_task_locals_synchronous_bind.swift index 6fe2736beb9ce..0cf101117b52e 100644 --- a/test/Concurrency/Runtime/async_task_locals_synchronous_bind.swift +++ b/test/Concurrency/Runtime/async_task_locals_synchronous_bind.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,13 +8,13 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) enum TL { @TaskLocal static var number: Int = 0 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @discardableResult func printTaskLocal( _ key: TaskLocal, @@ -32,35 +32,27 @@ func printTaskLocal( // ==== ------------------------------------------------------------------------ -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -func synchronous_bind() async { +@available(SwiftStdlib 5.5, *) +func synchronous_bind() { func synchronous() { printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (1111) - withUnsafeCurrentTask { task in - guard let task = task else { - fatalError() - } - - task.withTaskLocal(TL.$number, boundTo: 2222) { - printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (2222) - } - - printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (1111) + TL.$number.withValue(2222) { + printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (2222) } printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (1111) } - await TL.$number.withValue(1111) { + TL.$number.withValue(1111) { synchronous() } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { - static func main() async { - await synchronous_bind() + static func main() { + synchronous_bind() } } diff --git a/test/Concurrency/Runtime/async_task_locals_wrapper.swift b/test/Concurrency/Runtime/async_task_locals_wrapper.swift index a804b4437d53c..f08958d0956b4 100644 --- a/test/Concurrency/Runtime/async_task_locals_wrapper.swift +++ b/test/Concurrency/Runtime/async_task_locals_wrapper.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,13 +8,13 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) enum TL { @TaskLocal static var number: Int = 0 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @discardableResult func printTaskLocal( _ key: TaskLocal, @@ -32,7 +32,7 @@ func printTaskLocal( // ==== ------------------------------------------------------------------------ -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func async_let_nested() async { print("TL: \(TL.$number)") @@ -56,7 +56,7 @@ func async_let_nested() async { printTaskLocal(TL.$number) // CHECK: TaskLocal(defaultValue: 0) (0) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func async_let_nested_skip_optimization() async { async let x1: Int? = TL.$number.withValue(2) { async let x2: Int? = { () async -> Int? in @@ -79,7 +79,7 @@ func async_let_nested_skip_optimization() async { _ = await x1 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await async_let_nested() diff --git a/test/Concurrency/Runtime/async_task_priority_current.swift b/test/Concurrency/Runtime/async_task_priority_current.swift index 1fc6222784531..26f0275d305bc 100644 --- a/test/Concurrency/Runtime/async_task_priority_current.swift +++ b/test/Concurrency/Runtime/async_task_priority_current.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck --dump-input=always %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck --dump-input=always %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,27 +10,27 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_detach() async { let a1 = Task.currentPriority - print("a1: \(a1)") // CHECK: a1: unspecified + print("a1: \(a1)") // CHECK: TaskPriority(rawValue: 21) // Note: remember to detach using a higher priority, otherwise a lower one // might be escalated by the get() and we could see `default` in the detached // task. await detach(priority: .userInitiated) { let a2 = Task.currentPriority - print("a2: \(a2)") // CHECK: a2: userInitiated + print("a2: \(a2)") // CHECK: a2: TaskPriority(rawValue: 25) }.get() let a3 = Task.currentPriority - print("a3: \(a3)") // CHECK: a3: unspecified + print("a3: \(a3)") // CHECK: a3: TaskPriority(rawValue: 21) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_multiple_lo_indirectly_escalated() async { @Sendable - func loopUntil(priority: Task.Priority) async { + func loopUntil(priority: TaskPriority) async { while (Task.currentPriority != priority) { await Task.sleep(1_000_000_000) } @@ -59,7 +59,7 @@ func test_multiple_lo_indirectly_escalated() async { print("default done") // CHECK: default done } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_detach() diff --git a/test/Concurrency/Runtime/async_task_sleep.swift b/test/Concurrency/Runtime/async_task_sleep.swift index 5d8204405c514..bc374e332d93f 100644 --- a/test/Concurrency/Runtime/async_task_sleep.swift +++ b/test/Concurrency/Runtime/async_task_sleep.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always // REQUIRES: executable_test // REQUIRES: concurrency // REQUIRES: libdispatch @@ -11,7 +11,7 @@ import _Concurrency // FIXME: should not depend on Dispatch import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static let pause = 500_000_000 // 500ms diff --git a/test/Concurrency/Runtime/async_task_sleep_cancel.swift b/test/Concurrency/Runtime/async_task_sleep_cancel.swift new file mode 100644 index 0000000000000..e4a7c6289fb67 --- /dev/null +++ b/test/Concurrency/Runtime/async_task_sleep_cancel.swift @@ -0,0 +1,114 @@ +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: libdispatch + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import _Concurrency +// FIXME: should not depend on Dispatch +import Dispatch + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static let pause = 500_000_000 // 500ms + + static func main() async { + // CHECK: Starting! + print("Starting!") + await testSleepFinished() + await testSleepMomentary() + await testSleepCancelledBeforeStarted() + await testSleepCancelled() + } + + static func testSleepFinished() async { + // CHECK-NEXT: Testing sleep that completes + print("Testing sleep that completes") + let start = DispatchTime.now() + + // try! will fail if the task got cancelled (which shouldn't happen). + try! await Task.sleep(nanoseconds: UInt64(pause)) + + let stop = DispatchTime.now() + + // assert that at least the specified time passed since calling `sleep` + assert(stop >= (start + .nanoseconds(pause))) + + // CHECK-NEXT: Wakey wakey! + print("Wakey wakey!") + } + + static func testSleepMomentary() async { + // CHECK-NEXT: Testing sleep that completes instantly + print("Testing sleep that completes instantly") + + // try! will fail if the task got cancelled (which shouldn't happen). + try! await Task.sleep(nanoseconds: 0) + + // CHECK-NEXT: Wakey wakey! + print("Wakey wakey!") + } + + static func testSleepCancelledBeforeStarted() async { + // CHECK-NEXT: Testing sleep that gets cancelled before it starts + print("Testing sleep that gets cancelled before it starts") + let sleepyTask = Task { + try await Task.sleep(nanoseconds: UInt64(pause)) + } + + do { + sleepyTask.cancel() + try await sleepyTask.value + + print("Bah, weird scheduling") + } catch is CancellationError { + print("Caught the cancellation error") + } catch { + fatalError("sleep(nanoseconds:) threw some other error: \(error)") + } + + // CHECK: Cancelled! + print("Cancelled!") + } + + static func testSleepCancelled() async { + // CHECK-NEXT: Testing sleep that gets cancelled before it completes + print("Testing sleep that gets cancelled before it completes") + let start = DispatchTime.now() + + let sleepyTask = Task { + try await Task.sleep(nanoseconds: UInt64(pause)) + } + + do { + let waiterTask = Task { + try await sleepyTask.value + } + + let cancellerTask = Task { + await Task.sleep(UInt64(pause / 2)) + sleepyTask.cancel() + } + + try await waiterTask.value + + fatalError("sleep(nanoseconds:) should have thrown CancellationError") + } catch is CancellationError { + // CHECK-NEXT: Caught the cancellation error + print("Caught the cancellation error") + + let stop = DispatchTime.now() + + // assert that we stopped early. + assert(stop < (start + .nanoseconds(pause))) + } catch { + fatalError("sleep(nanoseconds:) threw some other error: \(error)") + } + + // CHECK-NEXT: Cancelled! + print("Cancelled!") + } +} diff --git a/test/Concurrency/Runtime/async_task_yield.swift b/test/Concurrency/Runtime/async_task_yield.swift index 6c5e627a1ab54..510e409302239 100644 --- a/test/Concurrency/Runtime/async_task_yield.swift +++ b/test/Concurrency/Runtime/async_task_yield.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library) // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,12 +8,12 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) protocol Go: Actor { func go(times: Int) async -> Int } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension Go { func go(times: Int) async -> Int { for i in 0...times { @@ -24,30 +24,28 @@ extension Go { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor One: Go {} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Two: Go {} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func yielding() async { let one = One() let two = Two() await withTaskGroup(of: Int.self) { group in - await group.spawn { + group.addTask { await one.go(times: 100) } - await group.spawn { + group.addTask { await two.go(times: 100) } } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await yielding() - // TODO: No idea for a good test for this... Open to ideas? - // CHECK: Two @ 100 } } diff --git a/test/Concurrency/Runtime/async_taskgroup_asynciterator_semantics.swift b/test/Concurrency/Runtime/async_taskgroup_asynciterator_semantics.swift index ce42f96f6fc25..d5dd8713ec683 100644 --- a/test/Concurrency/Runtime/async_taskgroup_asynciterator_semantics.swift +++ b/test/Concurrency/Runtime/async_taskgroup_asynciterator_semantics.swift @@ -1,10 +1,9 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime // UNSUPPORTED: linux -// XFAIL: windows struct Boom: Error {} @@ -12,7 +11,7 @@ func boom() async throws -> Int { throw Boom() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_next() async { let sum = await withThrowingTaskGroup(of: Int.self, returning: Int.self) { group in for n in 1...10 { @@ -43,7 +42,7 @@ func test_taskGroup_next() async { print("result with group.next(): \(sum)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_for_in() async { let sum = await withThrowingTaskGroup(of: Int.self, returning: Int.self) { group in for n in 1...10 { @@ -74,7 +73,7 @@ func test_taskGroup_for_in() async { print("result with for-in: \(sum)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_asyncIterator() async { let sum = await withThrowingTaskGroup(of: Int.self, returning: Int.self) { group in for n in 1...10 { @@ -112,7 +111,7 @@ func test_taskGroup_asyncIterator() async { print("result with async iterator: \(sum)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_contains() async { let sum = await withTaskGroup(of: Int.self, returning: Int.self) { group in for n in 1...4 { @@ -144,7 +143,7 @@ func test_taskGroup_contains() async { print("result with async iterator: \(sum)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_next() diff --git a/test/Concurrency/Runtime/async_taskgroup_cancelAll_only_specific_group.swift b/test/Concurrency/Runtime/async_taskgroup_cancelAll_only_specific_group.swift index c4af0d251cd32..59f105889ae64 100644 --- a/test/Concurrency/Runtime/async_taskgroup_cancelAll_only_specific_group.swift +++ b/test/Concurrency/Runtime/async_taskgroup_cancelAll_only_specific_group.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,14 +10,14 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncEcho(_ value: Int) async -> Int { value } /// Tests that only the specific group we cancelAll on is cancelled, /// and not accidentally all tasks in all groups within the given parent task. -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_cancelAll_onlySpecificGroup() async { async let g1: Int = withTaskGroup(of: Int.self) { group in @@ -79,7 +79,7 @@ func test_taskGroup_cancelAll_onlySpecificGroup() async { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_cancelAll_onlySpecificGroup() diff --git a/test/Concurrency/Runtime/async_taskgroup_cancel_from_inside_child.swift b/test/Concurrency/Runtime/async_taskgroup_cancel_from_inside_child.swift index f1257a920fd18..9627eee837f36 100644 --- a/test/Concurrency/Runtime/async_taskgroup_cancel_from_inside_child.swift +++ b/test/Concurrency/Runtime/async_taskgroup_cancel_from_inside_child.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,7 +10,7 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_cancel_from_inside_child() async { let one = try! await withTaskGroup(of: Int.self, returning: Int.self) { group in await group.next() @@ -45,7 +45,7 @@ func test_taskGroup_cancel_from_inside_child() async { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_cancel_from_inside_child() diff --git a/test/Concurrency/Runtime/async_taskgroup_cancel_parent_affects_group.swift b/test/Concurrency/Runtime/async_taskgroup_cancel_parent_affects_group.swift index c3d2f1688766a..ff1f98dd06e9a 100644 --- a/test/Concurrency/Runtime/async_taskgroup_cancel_parent_affects_group.swift +++ b/test/Concurrency/Runtime/async_taskgroup_cancel_parent_affects_group.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,12 +10,12 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncEcho(_ value: Int) async -> Int { value } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_cancel_parent_affects_group() async { let x = detach { @@ -47,7 +47,7 @@ func test_taskGroup_cancel_parent_affects_group() async { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_cancel_parent_affects_group() diff --git a/test/Concurrency/Runtime/async_taskgroup_cancel_then_completions.swift b/test/Concurrency/Runtime/async_taskgroup_cancel_then_completions.swift index 66e302b96e388..a8884bf379931 100644 --- a/test/Concurrency/Runtime/async_taskgroup_cancel_then_completions.swift +++ b/test/Concurrency/Runtime/async_taskgroup_cancel_then_completions.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,14 +10,14 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncEcho(_ value: Int) async -> Int { value } // FIXME: this is a workaround since (A, B) today isn't inferred to be Sendable // and causes an error, but should be a warning (this year at least) -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct SendableTuple2: Sendable { let first: A let second: B @@ -28,7 +28,7 @@ struct SendableTuple2: Sendable { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_cancel_then_completions() async { // CHECK: test_taskGroup_cancel_then_completions print("before \(#function)") @@ -73,7 +73,7 @@ func test_taskGroup_cancel_then_completions() async { print("result: \(result)") // CHECK: result: 3 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_cancel_then_completions() diff --git a/test/Concurrency/Runtime/async_taskgroup_cancel_then_spawn.swift b/test/Concurrency/Runtime/async_taskgroup_cancel_then_spawn.swift index c6fae83b6c34a..f87d0f770c757 100644 --- a/test/Concurrency/Runtime/async_taskgroup_cancel_then_spawn.swift +++ b/test/Concurrency/Runtime/async_taskgroup_cancel_then_spawn.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input always // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,12 +10,12 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncEcho(_ value: Int) async -> Int { value } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_cancel_then_add() async { // CHECK: test_taskGroup_cancel_then_add print("\(#function)") @@ -37,13 +37,16 @@ func test_taskGroup_cancel_then_add() async { let none = await group.next() print("next second: \(none)") // CHECK: next second: nil - group.spawn { 3 } - print("added third, unconditionally") // CHECK: added third, unconditionally - print("group isCancelled: \(group.isCancelled)") // CHECK: group isCancelled: true - + group.spawn { + print("child task isCancelled: \(Task.isCancelled)") // CHECK: child task isCancelled: true + return 3 + } let three = await group.next()! print("next third: \(three)") // CHECK: next third: 3 + print("added third, unconditionally") // CHECK: added third, unconditionally + print("group isCancelled: \(group.isCancelled)") // CHECK: group isCancelled: true + return one + (none ?? 0) } @@ -52,7 +55,7 @@ func test_taskGroup_cancel_then_add() async { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_cancel_then_add() diff --git a/test/Concurrency/Runtime/async_taskgroup_is_asyncsequence.swift b/test/Concurrency/Runtime/async_taskgroup_is_asyncsequence.swift index cd28a77e89fd4..8ad4a859495ea 100644 --- a/test/Concurrency/Runtime/async_taskgroup_is_asyncsequence.swift +++ b/test/Concurrency/Runtime/async_taskgroup_is_asyncsequence.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,9 +8,8 @@ // UNSUPPORTED: back_deployment_runtime // UNSUPPORTED: linux -// XFAIL: windows -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_is_asyncSequence() async { print(#function) @@ -34,7 +33,7 @@ func test_taskGroup_is_asyncSequence() async { print("result: \(sum)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_throwingTaskGroup_is_asyncSequence() async throws { print(#function) @@ -58,7 +57,7 @@ func test_throwingTaskGroup_is_asyncSequence() async throws { print("result: \(sum)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_is_asyncSequence() diff --git a/test/Concurrency/Runtime/async_taskgroup_is_empty.swift b/test/Concurrency/Runtime/async_taskgroup_is_empty.swift index b189c124622dc..502ba46b3c80d 100644 --- a/test/Concurrency/Runtime/async_taskgroup_is_empty.swift +++ b/test/Concurrency/Runtime/async_taskgroup_is_empty.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,19 +10,19 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncEcho(_ value: Int) async -> Int { value } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_isEmpty() async { print("before all") let result = await withTaskGroup(of: Int.self, returning: Int.self) { group in // CHECK: before add: isEmpty=true print("before add: isEmpty=\(group.isEmpty)") - group.spawn { + group.async { await Task.sleep(2_000_000_000) return await asyncEcho(1) } @@ -44,7 +44,7 @@ func test_taskGroup_isEmpty() async { print("result: \(result)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_isEmpty() diff --git a/test/Concurrency/Runtime/async_taskgroup_next_not_invoked_cancelAll.swift b/test/Concurrency/Runtime/async_taskgroup_next_not_invoked_cancelAll.swift index 48236febade77..c40c568fbb336 100644 --- a/test/Concurrency/Runtime/async_taskgroup_next_not_invoked_cancelAll.swift +++ b/test/Concurrency/Runtime/async_taskgroup_next_not_invoked_cancelAll.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,7 +10,7 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_skipCallingNext_butInvokeCancelAll() async { let numbers = [1, 1] @@ -47,7 +47,7 @@ func test_skipCallingNext_butInvokeCancelAll() async { assert(result == 0) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_skipCallingNext_butInvokeCancelAll() diff --git a/test/Concurrency/Runtime/async_taskgroup_next_not_invoked_without_cancelAll.swift b/test/Concurrency/Runtime/async_taskgroup_next_not_invoked_without_cancelAll.swift index 9b40ea603bb80..66c248665dd75 100644 --- a/test/Concurrency/Runtime/async_taskgroup_next_not_invoked_without_cancelAll.swift +++ b/test/Concurrency/Runtime/async_taskgroup_next_not_invoked_without_cancelAll.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,7 +10,7 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_skipCallingNext() async { let numbers = [1, 1] @@ -43,7 +43,7 @@ func test_skipCallingNext() async { assert(result == 0) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_skipCallingNext() diff --git a/test/Concurrency/Runtime/async_taskgroup_next_on_completed.swift b/test/Concurrency/Runtime/async_taskgroup_next_on_completed.swift index eeed8c84d3a86..44753e6dbfbc9 100644 --- a/test/Concurrency/Runtime/async_taskgroup_next_on_completed.swift +++ b/test/Concurrency/Runtime/async_taskgroup_next_on_completed.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency @@ -10,7 +10,7 @@ import Dispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_sum_nextOnCompleted() async { let numbers = [1, 2, 3, 4, 5] let expected = 15 // FIXME: numbers.reduce(0, +) this hangs? @@ -66,7 +66,7 @@ func test_sum_nextOnCompleted() async { print("result: \(sum)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_sum_nextOnCompleted() diff --git a/test/Concurrency/Runtime/async_taskgroup_next_on_pending.swift b/test/Concurrency/Runtime/async_taskgroup_next_on_pending.swift index 10c018184c084..f1cc52543589e 100644 --- a/test/Concurrency/Runtime/async_taskgroup_next_on_pending.swift +++ b/test/Concurrency/Runtime/async_taskgroup_next_on_pending.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency diff --git a/test/Concurrency/Runtime/async_taskgroup_throw_recover.swift b/test/Concurrency/Runtime/async_taskgroup_throw_recover.swift index 6adf2294a6f11..34689df58ecf9 100644 --- a/test/Concurrency/Runtime/async_taskgroup_throw_recover.swift +++ b/test/Concurrency/Runtime/async_taskgroup_throw_recover.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) | %FileCheck %s --dump-input=always +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s --dump-input=always // REQUIRES: executable_test // REQUIRES: concurrency @@ -7,23 +7,21 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -// UNSUPPORTED: OS=windows-msvc - struct Boom: Error {} struct IgnoredBoom: Error {} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func one() async -> Int { 1 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func boom() async throws -> Int { throw Boom() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_throws() async { let got: Int = try await withThrowingTaskGroup(of: Int.self) { group in - group.spawn { try await boom() } + group.addTask { try await boom() } do { while let r = try await group.next() { @@ -35,19 +33,24 @@ func test_taskGroup_throws() async { let gc = group.isCancelled print("group cancelled: \(gc)") - group.spawn { () async -> Int in + group.addTask { () async -> Int in let c = Task.isCancelled print("task 3 (cancelled: \(c))") return 3 } - guard let third = try! await group.next() else { + switch await group.nextResult() { + case .success(let third): + print("task group returning normally: \(third)") + return third + + case .failure(let error): + fatalError("got an erroneous third result") + + case .none: print("task group failed to get 3") return 0 } - - print("task group returning normally: \(third)") - return third } fatalError("Should have thrown and handled inside the catch block") @@ -63,7 +66,7 @@ func test_taskGroup_throws() async { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_throws() diff --git a/test/Concurrency/Runtime/async_taskgroup_throw_rethrow.swift b/test/Concurrency/Runtime/async_taskgroup_throw_rethrow.swift index e9b85628746b5..1992a0ce0ab63 100644 --- a/test/Concurrency/Runtime/async_taskgroup_throw_rethrow.swift +++ b/test/Concurrency/Runtime/async_taskgroup_throw_rethrow.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -7,17 +7,15 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -// XFAIL: OS=windows-msvc - struct Boom: Error {} struct IgnoredBoom: Error {} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func echo(_ i: Int) async -> Int { i } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func boom() async throws -> Int { throw Boom() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_throws_rethrows() async { do { let got = try await withThrowingTaskGroup(of: Int.self, returning: Int.self) { group in @@ -47,7 +45,7 @@ func test_taskGroup_throws_rethrows() async { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_taskGroup_throws_rethrows() diff --git a/test/Concurrency/Runtime/basic_future.swift b/test/Concurrency/Runtime/basic_future.swift index 1a8937ea8e93a..e7a14ba77a772 100644 --- a/test/Concurrency/Runtime/basic_future.swift +++ b/test/Concurrency/Runtime/basic_future.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) // REQUIRES: executable_test // REQUIRES: concurrency @@ -14,12 +14,12 @@ enum HomeworkError: Error, Equatable { case dogAteIt(String) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func formGreeting(name: String) async -> String { return "Hello \(name) from async world" } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func testSimple( name: String, dogName: String, shouldThrow: Bool, doSuspend: Bool ) async { @@ -72,7 +72,7 @@ func testSimple( } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await testSimple(name: "Ted", dogName: "Hazel", shouldThrow: false, doSuspend: false) diff --git a/test/Concurrency/Runtime/cancellation_handler.swift b/test/Concurrency/Runtime/cancellation_handler.swift index 070461e0248bc..2720c4fab5b69 100644 --- a/test/Concurrency/Runtime/cancellation_handler.swift +++ b/test/Concurrency/Runtime/cancellation_handler.swift @@ -1,17 +1,18 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch) // REQUIRES: concurrency // REQUIRES: executable_test // rdar://76038845 // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -// UNSUPPORTED: OS=windows-msvc // for sleep #if canImport(Darwin) import Darwin #elseif canImport(Glibc) import Glibc +#elseif os(Windows) + import WinSDK #endif class Canary { @@ -20,7 +21,7 @@ class Canary { } } -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(SwiftStdlib 5.5, *) { let task = detach { let canary = Canary() _ = await Task.withCancellationHandler { @@ -30,7 +31,11 @@ if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { } } task.cancel() +#if os(Windows) + Sleep(1 * 1000) +#else sleep(1) +#endif detach { await Task.withCancellationHandler { print("Task was cancelled!") @@ -39,7 +44,11 @@ if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { print("Running the operation...") } } +#if os(Windows) + Sleep(10 * 1000) +#else sleep(10) +#endif } else { // Fake prints to satisfy FileCheck. print("Canary") diff --git a/test/Concurrency/Runtime/checked_continuation.swift b/test/Concurrency/Runtime/checked_continuation.swift index 7f6125d05054f..5c2b5d0354a49 100644 --- a/test/Concurrency/Runtime/checked_continuation.swift +++ b/test/Concurrency/Runtime/checked_continuation.swift @@ -1,12 +1,10 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) // REQUIRES: executable_test // REQUIRES: concurrency // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -// UNSUPPORTED: OS=windows-msvc - import _Concurrency import StdlibUnittest @@ -16,7 +14,7 @@ struct TestError: Error {} static func main() async { var tests = TestSuite("CheckedContinuation") - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(SwiftStdlib 5.5, *) { tests.test("trap on double resume non-throwing continuation") { expectCrashLater() diff --git a/test/Concurrency/Runtime/class_resilience.swift b/test/Concurrency/Runtime/class_resilience.swift index bf5bfa3e00cb2..f7c4000219800 100644 --- a/test/Concurrency/Runtime/class_resilience.swift +++ b/test/Concurrency/Runtime/class_resilience.swift @@ -1,9 +1,9 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_class)) -Xfrontend -enable-experimental-concurrency -enable-library-evolution %S/Inputs/resilient_class.swift -emit-module -emit-module-path %t/resilient_class.swiftmodule -module-name resilient_class +// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_class)) -Xfrontend -disable-availability-checking -enable-library-evolution %S/Inputs/resilient_class.swift -emit-module -emit-module-path %t/resilient_class.swiftmodule -module-name resilient_class // RUN: %target-codesign %t/%target-library-name(resilient_class) -// RUN: %target-build-swift -parse-as-library -Xfrontend -enable-experimental-concurrency %s -lresilient_class -I %t -L %t -o %t/main %target-rpath(%t) +// RUN: %target-build-swift -parse-as-library -Xfrontend -disable-availability-checking %s -lresilient_class -I %t -L %t -o %t/main %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(resilient_class) @@ -16,7 +16,6 @@ // UNSUPPORTED: back_deployment_runtime // XFAIL: windows -// XFAIL: linux // XFAIL: openbsd import StdlibUnittest diff --git a/test/Concurrency/Runtime/custom_executors.swift b/test/Concurrency/Runtime/custom_executors.swift index bdce206f7a84b..794344a2feff4 100644 --- a/test/Concurrency/Runtime/custom_executors.swift +++ b/test/Concurrency/Runtime/custom_executors.swift @@ -1,12 +1,14 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s // REQUIRES: concurrency // REQUIRES: executable_test -// UNSUPPORTED: OS=windows-msvc // UNSUPPORTED: back_deployment_runtime // UNSUPPORTED: use_os_stdlib +// Disabled until test hang can be looked at. +// UNSUPPORTED: OS=windows-msvc + actor Simple { var count = 0 func report() { @@ -17,9 +19,9 @@ actor Simple { actor Custom { var count = 0 - nonisolated let simple = Simple() + let simple = Simple() - @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) + @available(SwiftStdlib 5.5, *) nonisolated var unownedExecutor: UnownedSerialExecutor { print("custom unownedExecutor") return simple.unownedExecutor @@ -33,7 +35,7 @@ actor Custom { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { print("begin") diff --git a/test/Concurrency/Runtime/data_race_detection.swift b/test/Concurrency/Runtime/data_race_detection.swift index a1abfc67bdfeb..0bbdf4603a136 100644 --- a/test/Concurrency/Runtime/data_race_detection.swift +++ b/test/Concurrency/Runtime/data_race_detection.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -enable-actor-data-race-checks %import-libdispatch -parse-as-library) > %t.log 2>&1 +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -enable-actor-data-race-checks %import-libdispatch -parse-as-library) > %t.log 2>&1 // RUN: %FileCheck %s < %t.log // REQUIRES: executable_test diff --git a/test/Concurrency/Runtime/effectful_properties.swift b/test/Concurrency/Runtime/effectful_properties.swift index 5264245852f84..97201ffa91e03 100644 --- a/test/Concurrency/Runtime/effectful_properties.swift +++ b/test/Concurrency/Runtime/effectful_properties.swift @@ -20,7 +20,7 @@ enum BallKind { case KirksandLignature } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) class Specs { // obtains the number of dimples subscript(_ bk : BallKind) -> Int { @@ -37,7 +37,7 @@ class Specs { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Database { var currentData : Specs { get async { @@ -52,21 +52,21 @@ actor Database { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) protocol SphericalObject { var name : String { get async throws } var dimples : Int { get async throws } var description : String { get async throws } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) class Ball : SphericalObject { var name : String { get async throws { throw GeneralError.Todo } } var dimples : Int { get async throws { throw GeneralError.Todo } } var description : String { get async throws { throw GeneralError.Todo } } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) class GolfBall : Ball { private static let db : Database = Database() @@ -110,17 +110,17 @@ class GolfBall : Ball { // CHECK: obtaining specs... // CHECK: this golf ball has 0 dimples -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func printAsBall(_ b : Ball) async { print(try! await b.description) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func printAsAsSphericalObject(_ b : SphericalObject) async { print(try! await b.description) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct RunIt { static func main() async { let balls : [(Bool, Ball)] = [ diff --git a/test/Concurrency/Runtime/exclusivity.swift b/test/Concurrency/Runtime/exclusivity.swift new file mode 100644 index 0000000000000..9f5673314cf54 --- /dev/null +++ b/test/Concurrency/Runtime/exclusivity.swift @@ -0,0 +1,641 @@ +// RUN: %target-run-simple-swift( -parse-as-library) + +// REQUIRES: executable_test +// REQUIRES: concurrency + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// This test makes sure that: +// +// 1. Tasks have separate exclusivity sets. +// 2. Exercise the pushing/popping of access sets from tasks. + +// NOTE: The cases that we are talking about handling below refer to the cases +// documented in Exclusivity.cpp. +// +// NOTE: We test cases that involve custom executors in +// custom_executors_exclusivity.cpp. + +import _Concurrency +import StdlibUnittest + +var global1: Int = 5 +var global2: Int = 6 +var global3: Int = 7 + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(CRT) +import CRT +#endif + +@inlinable +public func debugLog(_ s: String) { + // Only enable this when debugging test failures against an asserts + // runtime. Otherwise, the test is noisy and has non-windows + // dependencies. We need stderr to ensure proper log output interleaving + // with the runtime's own stderr emitted output. +#if DEBUG_LOGGING + fputs(s + "\n", stderr) + fflush(stderr) +#endif +} + +@available(SwiftStdlib 5.5, *) +@main +struct Runner { + @MainActor + @inline(never) + static func withExclusiveAccessAsync(to x: inout T, f: (inout T) async -> U) async -> U { + await f(&x) + } + + @MainActor + @inline(never) + static func withExclusiveAccess(to x: inout T, f: (inout T) -> U) -> U { + f(&x) + } + + @inline(never) + @MainActor + static func doSomething() async { } + + @inline(never) + @Sendable + static func useGlobal(_ x: inout Int) { debugLog("FORCE ACCESS") } + + @MainActor static func main() async { + var exclusivityTests = TestSuite("Async Exclusivity") + + // First make sure that if we do not introduce a new task, we still get + // our expected conflict. + exclusivityTests.test("testSameTaskBlowsUpSinceSameSet") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + + let callee2 = { @MainActor (_ x: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("Value: x: \(x)") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int) async -> () { + debugLog("==> Enter callee1") + + // Second access. Same Task so not ok. + await callee2(&global1) + + debugLog("==> Exit callee1") + + } + + debugLog("==> Enter Main") + // First access begins here. + await callee1(&global1) + debugLog("==> Exit Main") + } + + // Then do a simple test with a single access to make sure that we do + // not hit any sccesses b/c we introduced the Task. + exclusivityTests.test("testDifferentTasksHaveDifferentExclusivityAccessSets") { @MainActor in + let callee2 = { @MainActor (_ x: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int) async -> () { + debugLog("==> Enter callee1") + // This task is what prevents this example from crashing. + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // Second access. Different Task so it is ok. + await callee2(&global1) + debugLog("==> Exit callee1 Closure") + } + await handle.value + debugLog("==> Exit callee1") + } + + debugLog("==> Enter Main") + // First access begins here. + await callee1(&global1) + debugLog("==> Exit Main") + } + + // Make sure we correctly handle cases where we have multiple accesses + // open at the same time. + exclusivityTests.test("testDifferentTasksWith2Accesses") { @MainActor in + let callee2 = { @MainActor (_ x: inout Int, _ y: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int, _ y: inout Int) async -> () { + debugLog("==> Enter callee1") + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // Second access. Different Task so it is ok. + await callee2(&global1, &global2) + debugLog("==> Exit callee1 Closure") + } + await handle.value + debugLog("==> Exit callee1") + } + + debugLog("==> Enter Main") + // First access begins here. + await callee1(&global1, &global2) + debugLog("==> Exit Main") + } + + // Make sure we correctly handle cases where we have multiple accesses + // open at the same time. + exclusivityTests.test("testDifferentTasksWith3Accesses") { @MainActor in + let callee2 = { @MainActor (_ x: inout Int, _ y: inout Int, _ z: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int, _ y: inout Int, _ z: inout Int) async -> () { + debugLog("==> Enter callee1") + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // Second access. Different Task so it is ok. + await callee2(&global1, &global2, &global3) + debugLog("==> Exit callee1 Closure") + } + await handle.value + debugLog("==> Exit callee1") + } + + debugLog("==> Enter Main") + // First access begins here. + await callee1(&global1, &global2, &global3) + debugLog("==> Exit Main") + } + + // Now that we have tested our tests with various numbers of accesses, + // lets make specific tests for each case in Exclusivity.cpp. + // + // Case 1: (F, F, F) - No Live Accesses at Task Start, No Live Sync + // Accesses When Push, No Live Task Accesses when pop. + // + // This case is the case where we do not have any accesses in our code + // at all or if the task cleans up the tasks before it awaits again. We + // test the task cleanup case. + exclusivityTests.test("case1") { @MainActor in + @inline(never) + @Sendable func callee2(_ x: inout Int, _ y: inout Int, _ z: inout Int) -> Void { + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1() async -> () { + debugLog("==> Enter callee1") + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // These accesses end before we await in the task. + do { + callee2(&global1, &global2, &global3) + } + await doSomething() + debugLog("==> Exit callee1 Closure") + } + await handle.value + debugLog("==> Exit callee1") + } + + await callee1() + } + + // In case 2, our task does not start with any live accesses, but it is + // awaited upon after the live access begins. We want to make sure that + // we fail here since we properly restored the callee state. + exclusivityTests.test("case2") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + + let callee2 = { @MainActor (_ x: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int) async -> () { + debugLog("==> Enter callee1") + // This task is what prevents this example from crashing. + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // Second access. Different Task so it is ok. + await callee2(&global1) + debugLog("==> Exit callee1 Closure") + } + await handle.value + + useGlobal(&global1) // We should crash here. + + debugLog("==> Exit callee1") + } + + debugLog("==> Enter Main") + // First access begins here. + await callee1(&global1) + debugLog("==> Exit Main") + } + + // In case 5, our task starts with live accesses, but we finish the + // accesses before we return. So we do not crash. The key thing is the + // access lives over a suspension/resume. + exclusivityTests.test("case5") { @MainActor in + let callee2 = { @MainActor (_ x: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int) async -> () { + debugLog("==> Enter callee1") + // This task is what prevents this example from crashing. + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // Second access. Different Task so it is ok. + await callee2(&global1) + debugLog("==> Exit callee1 Closure") + } + await handle.value + + debugLog("==> Exit callee1") + } + + @MainActor + @inline(never) + func callCallee1() async { + await callee1(&global1) + } + + debugLog("==> Enter Main") + // First access begins here. + await callCallee1() + useGlobal(&global1) // We should not crash here since we cleaned up + // the access in callCallee1 after we returned + // from the await there. + debugLog("==> Exit Main") + } + + // In case 6, our task starts with live accesses, and we only finish + // some of the accesses before we return. So we want to validate that by + // running the same code twice, one testing we can access the pointer we + // can fix up and a second that we can not. + exclusivityTests.test("case6") { @MainActor in + let callee2 = { @MainActor (_ x: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int) async -> () { + debugLog("==> Enter callee1") + // This task is what prevents this example from crashing. + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // Second access. Different Task so it is ok. + await callee2(&global1) + debugLog("==> Exit callee1 Closure") + } + await handle.value + + debugLog("==> Exit callee1") + } + + @MainActor + @inline(never) + func callCallee1() async { + await callee1(&global1) + } + + debugLog("==> Enter Main") + // First access begins here. + await callCallee1() + useGlobal(&global1) // We should not crash here since we cleaned up + // the access in callCallee1 after we returned + // from the await there. + debugLog("==> Exit Main") + } + + // These are additional tests that used to be FileChecked but FileCheck + // was too hard to use in a concurrent context. + exclusivityTests.test("case1") { @MainActor in + @inline(never) + @Sendable func callee2(_ x: inout Int, _ y: inout Int, _ z: inout Int) -> Void { + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1() async -> () { + debugLog("==> Enter callee1") + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + + // These accesses end before we await in the task. + do { + callee2(&global1, &global2, &global3) + } + let handle2 = Task { @MainActor in + debugLog("==> Enter handle2!") + debugLog("==> Exit handle2!") + } + await handle2.value + debugLog("==> Exit callee1 Closure") + } + await handle.value + debugLog("==> Exit callee1") + } + + debugLog("==> Enter 'testCase1'") + await callee1() + debugLog("==> Exit 'testCase1'") + } + + // Case 2: (F, F, T). In case 2, our task does not start with a live access + // and nothing from the outside synchronous context, but does pop with a new + // access. + // + // We use a suspend point and a withExclusiveAccessAsync(to:) to test this. + exclusivityTests.test("case2.filecheck.nocrash") { @MainActor in + debugLog("==> Enter 'testCase2'") + + let handle = Task { @MainActor in + debugLog("==> Inner Handle") + await withExclusiveAccessAsync(to: &global1) { @MainActor (x: inout Int) async -> Void in + let innerTaskHandle = Task { @MainActor in + // Different task, shouldn't crash. + withExclusiveAccess(to: &global1) { _ in + debugLog("==> No crash!") + } + debugLog("==> End Inner Task Handle") + } + // This will cause us to serialize the access to global1. If + // we had an access here, we would crash. + await innerTaskHandle.value + debugLog("==> After") + } + // Accessis over. We shouldn't crash here. + withExclusiveAccess(to: &global1) { _ in + debugLog("==> No crash!") + } + debugLog("==> Inner Handle: After exclusive access") + } + + await handle.value + debugLog("==> After exclusive access") + let handle2 = Task { @MainActor in + debugLog("==> Enter handle2!") + debugLog("==> Exit handle2!") + } + await handle2.value + debugLog("==> Exit 'testCase2'") + } + + exclusivityTests.test("case2.filecheck.crash") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Enter 'testCase2'") + + let handle = Task { @MainActor in + debugLog("==> Inner Handle") + await withExclusiveAccessAsync(to: &global1) { @MainActor (x: inout Int) async -> Void in + let innerTaskHandle = Task { @MainActor in + debugLog("==> End Inner Task Handle") + } + await innerTaskHandle.value + // We will crash here if we properly brought back in the + // access to global1 despite running code on a different + // task. + withExclusiveAccess(to: &global1) { _ in + debugLog("==> Got a crash!") + } + debugLog("==> After") + } + debugLog("==> Inner Handle: After exclusive access") + } + + await handle.value + debugLog("==> After exclusive access") + let handle2 = Task { @MainActor in + debugLog("==> Enter handle2!") + debugLog("==> Exit handle2!") + } + await handle2.value + debugLog("==> Exit 'testCase2'") + } + + // Case 5: (T,F,F). To test case 5, we use with exclusive access to to + // create an exclusivity scope that goes over a suspension point. We are + // interesting in the case where we return after the suspension point. That + // push/pop is going to have our outer task bring in state and end it. + // + // CHECK-LABEL: ==> Enter 'testCase5' + // CHECK: ==> Task: [[TASK:0x[0-9a-f]+]] + // CHECK: Inserting new access: [[LLNODE:0x[a-z0-9]+]] + // CHECK-NEXT: Tracking! + // CHECK-NEXT: Access. Pointer: [[ACCESS:0x[a-z0-9]+]] + // CHECK: Exiting Thread Local Context. Before Swizzle. Task: [[TASK]] + // CHECK-NEXT: SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): (0x0, 0x0) + // CHECK-NEXT: Access. Pointer: [[ACCESS]]. PC: + // CHECK: Exiting Thread Local Context. After Swizzle. Task: [[TASK]] + // CHECK_NEXT: SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): ([[LLNODE]], [[LLNODE]]) + // CHECK_NEXT: No Accesses. + // + // CHECK-NOT: Removing access: + // CHECK: ==> End Inner Task Handle + // CHECK: ==> After + // CHECK: Removing access: [[LLNODE]] + // CHECK: ==> After exclusive access + // CHECK: Exiting Thread Local Context. Before Swizzle. Task: [[TASK]] + // CHECK-NEXT: SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): (0x0, 0x0) + // CHECK-NEXT: No Accesses. + // CHECK: Exiting Thread Local Context. After Swizzle. Task: [[TASK]] + // CHECK-NEXT: SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): (0x0, 0x0) + // CHECK-NEXT: No Accesses. + // + // CHECK: ==> Exit 'testCase5' + exclusivityTests.test("case5.filecheck") { @MainActor in + debugLog("==> Enter 'testCase5'") + + let outerHandle = Task { @MainActor in + await withExclusiveAccessAsync(to: &global1) { @MainActor (x: inout Int) async -> Void in + let innerTaskHandle = Task { @MainActor in + debugLog("==> End Inner Task Handle") + } + await innerTaskHandle.value + debugLog("==> After") + } + debugLog("==> After exclusive access") + let handle2 = Task { @MainActor in + debugLog("==> Enter handle2!") + debugLog("==> Exit handle2!") + } + await handle2.value + } + await outerHandle.value + debugLog("==> Exit 'testCase5'") + } + + exclusivityTests.test("case5.filecheck.crash") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Enter 'testCase5'") + + let outerHandle = Task { @MainActor in + await withExclusiveAccessAsync(to: &global1) { @MainActor (x: inout Int) async -> Void in + let innerTaskHandle = Task { @MainActor in + debugLog("==> End Inner Task Handle") + } + await innerTaskHandle.value + debugLog("==> After") + withExclusiveAccess(to: &global1) { _ in + debugLog("==> Crash here") + } + } + debugLog("==> After exclusive access") + let handle2 = Task { @MainActor in + debugLog("==> Enter handle2!") + debugLog("==> Exit handle2!") + } + await handle2.value + } + await outerHandle.value + debugLog("==> Exit 'testCase5'") + } + + // Case 6: (T, F, T). In case 6, our task starts with live accesses and is + // popped with live accesses. There are no sync accesses. + // + // We test this by looking at the behavior of the runtime after we + // finish executing handle2. In this case, we first check that things + // just work normally and as a 2nd case perform a conflicting access to + // make sure we crash. + exclusivityTests.test("case6.filecheck") { @MainActor in + let outerHandle = Task { @MainActor in + let callee2 = { @MainActor (_ x: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int) async -> () { + debugLog("==> Enter callee1") + // This task is what prevents this example from crashing. + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // Second access. Different Task so it is ok. + await withExclusiveAccessAsync(to: &global1) { + await callee2(&$0) + } + debugLog("==> Exit callee1 Closure") + } + await handle.value + debugLog("==> callee1 after first await") + // Force an await here so we can see that we properly swizzle. + let handle2 = Task { @MainActor in + debugLog("==> Enter handle2!") + debugLog("==> Exit handle2!") + } + await handle2.value + debugLog("==> Exit callee1") + } + + // First access begins here. + await callee1(&global1) + } + debugLog("==> Enter 'testCase6'") + await outerHandle.value + debugLog("==> Exit 'testCase6'") + } + + exclusivityTests.test("case6.filecheck.crash") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + let outerHandle = Task { @MainActor in + let callee2 = { @MainActor (_ x: inout Int) -> Void in + debugLog("==> Enter callee2") + debugLog("==> Exit callee2") + } + + // We add an inline never here to make sure that we do not eliminate + // the dynamic access after inlining. + @MainActor + @inline(never) + func callee1(_ x: inout Int) async -> () { + debugLog("==> Enter callee1") + // This task is what prevents this example from crashing. + let handle = Task { @MainActor in + debugLog("==> Enter callee1 Closure") + // Second access. Different Task so it is ok. + await withExclusiveAccessAsync(to: &global1) { + await callee2(&$0) + } + debugLog("==> Exit callee1 Closure") + } + await handle.value + debugLog("==> callee1 after first await") + // Force an await here so we can see that we properly swizzle. + let handle2 = Task { @MainActor in + debugLog("==> Enter handle2!") + debugLog("==> Exit handle2!") + } + await handle2.value + // Make sure we brought back in the access to x so we crash + // here. + withExclusiveAccess(to: &global1) { _ in + debugLog("==> Will crash here!") + } + debugLog("==> Exit callee1") + } + + // First access begins here. + await callee1(&global1) + } + debugLog("==> Enter 'testCase6'") + await outerHandle.value + debugLog("==> Exit 'testCase6'") + } + + await runAllTestsAsync() + } +} diff --git a/test/Concurrency/Runtime/exclusivity_custom_executors.swift b/test/Concurrency/Runtime/exclusivity_custom_executors.swift new file mode 100644 index 0000000000000..5d40d2209f3e1 --- /dev/null +++ b/test/Concurrency/Runtime/exclusivity_custom_executors.swift @@ -0,0 +1,757 @@ +// RUN: %target-run-simple-swift(-parse-as-library) + +// REQUIRES: concurrency +// REQUIRES: executable_test + +// rdar://76038845 +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: use_os_stdlib + +// Disabled until test hang can be looked at. +// UNSUPPORTED: OS=windows-msvc + +// This test makes sure that we properly save/restore access when we +// synchronously launch a task from a serial executor. The access from the task +// should be merged into the already created access set while it runs and then +// unmerged afterwards. + +import _Concurrency +import StdlibUnittest + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(CRT) +import CRT +#endif + +@inlinable +public func debugLog(_ s: String) { + // Only enable this when debugging test failures against an asserts + // runtime. Otherwise, the test is noisy and has non-windows + // dependencies. We need stderr to ensure proper log output interleaving + // with the runtime's own stderr emitted output. +#if DEBUG_LOGGING + fputs(s + "\n", stderr) + fflush(stderr) +#endif +} + +@inline(never) +public func withExclusiveAccess(to x: inout T, f: (inout T) -> U) -> U { + debugLog("==> Enter 'withExclusiveAccess'") + defer { debugLog("==> Exit 'withExclusiveAccess'") } + return f(&x) +} + +@available(SwiftStdlib 5.5, *) +@MainActor @inline(never) +func withExclusiveAccessAsync(to x: inout T, f: (inout T) async -> U) async -> U { + debugLog("==> Enter 'withExclusiveAccessAsync'") + defer { debugLog("==> Exit 'withExclusiveAccessAsync'") } + return await f(&x) +} + +@available(SwiftStdlib 5.5, *) +public final class MySerialExecutor : SerialExecutor { + public init() { + debugLog("==> MySerialExecutor: Creating MySerialExecutor!") + } + public static var sharedSerialExecutor = MySerialExecutor() + public static var sharedUnownedExecutor: UnownedSerialExecutor { + debugLog("==> MySerialExecutor: Getting Shared Unowned Executor!") + return UnownedSerialExecutor(ordinary: sharedSerialExecutor) + } + + public func enqueue(_ job: UnownedJob) { + debugLog("==> MySerialExecutor: Got an enqueue!") + // This is the exclusive access that we are going to be swizzling + // in/out. + // + // Make sure we have 2x synchronized to test. + withExclusiveAccess(to: &global2) { _ in + withExclusiveAccess(to: &global3) { _ in + debugLog("==> MySerialExecutor: Inside access!") + job._runSynchronously(on: asUnownedSerialExecutor()) + debugLog("==> MySerialExecutor: Inside access after run synchronously!") + } + } + debugLog("==> MySerialExecutor: After access, after run synchronously") + } + + public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + debugLog("==> MySerialExecutor: Getting Unowned Executor!") + return UnownedSerialExecutor(ordinary: self) + } +} + +/// A singleton actor whose executor is equivalent to the main +/// dispatch queue. +@available(SwiftStdlib 5.5, *) +@globalActor public final actor MyMainActor: Executor { + public static let shared = MyMainActor() + public let executor = MySerialExecutor() + + @inlinable + public nonisolated var unownedExecutor: UnownedSerialExecutor { + debugLog("==> MyMainActor: Getting unowned exector!") + return executor.asUnownedSerialExecutor() + } + + @inlinable + public static var sharedUnownedExecutor: UnownedSerialExecutor { + debugLog("==> MyMainActor: Getting shared unowned exector!") + return MySerialExecutor.sharedUnownedExecutor + } + + @inlinable + public nonisolated func enqueue(_ job: UnownedJob) { + debugLog("==> MyMainActor: enqueuing!") + executor.enqueue(job) + } +} + +/// An actor that we use to test that after eliminating the synchronous +/// accesses, we properly deserialize the task access set causing a crash in +/// unownedExecutor. +@available(SwiftStdlib 5.5, *) +@globalActor public final actor MyMainActorWithAccessInUnownedExecAccessor: Executor { + public static let shared = MyMainActorWithAccessInUnownedExecAccessor() + public let executor = MySerialExecutor() + + @inlinable + public nonisolated var unownedExecutor: UnownedSerialExecutor { + debugLog("==> MyMainActorWithAccessInUnownedExecAccessor: Getting unowned exector!") + withExclusiveAccess(to: &global) { _ in debugLog("Crash!") } + return executor.asUnownedSerialExecutor() + } + + @inlinable + public static var sharedUnownedExecutor: UnownedSerialExecutor { + debugLog("==> MyMainActorWithAccessInUnownedExecAccessor: Getting shared unowned exector!") + return MySerialExecutor.sharedUnownedExecutor + } + + @inlinable + public nonisolated func enqueue(_ job: UnownedJob) { + debugLog("==> MyMainActorWithAccessInUnownedExecAccessor: enqueuing!") + executor.enqueue(job) + } +} + +@available(SwiftStdlib 5.5, *) +actor Custom { + var count = 0 + + func report() async { + debugLog("==> Custom: custom.count == \(count)") + count += 1 + } +} + +@available(SwiftStdlib 5.5, *) +@globalActor +struct CustomActor { + static var shared: Custom { + debugLog("==> CustomActor: Getting custom!") + return Custom() + } +} + +public var global: Int = 5 +public var global2: Int = 6 +public var global3: Int = 7 +public var global4: Int = 8 + +@available(SwiftStdlib 5.5, *) +@main +struct Runner { + @MainActor static func main() async { + var exclusivityTests = TestSuite("Async Exclusivity Custom Executors") + + // As a quick sanity test, make sure that the crash doesn't occur if we + // don't have the withExclusiveAccess(to: ) from the case below. + exclusivityTests.test("exclusivityAccessesPropagateFromExecutorIntoTasks NoConflict") { + @MainActor in + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + } + await handle.value + } + + // Make sure that we crash here due to the task forming an access to + // memory that the executor has exclusive access to. + exclusivityTests.test("exclusivityAccessesPropagateFromExecutorIntoTasks Crash") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + withExclusiveAccess(to: &global2) { _ in + debugLog("==> Crash!") + } + debugLog("==> Main: All done!") + } + await handle.value + } + + // If all of the previous tests passed, then we have basic sanity + // done. Lets now test out our cases that involve a live sync access. + // + // We test cases 3,4,7,8 here. The other cases that do not involve a + // custom executor are tested in Runtime/exclusivity.swift. + + // Now that we have tested our tests with various numbers of accesses, + // lets make specific tests for each case in Exclusivity.cpp. + // + // Case 3: (F, T, F) - No Live Accesses at Task Start, Exiting Live Sync + // Accesses When Push, No Live Task Accesses when pop. + // + // This case is the case where we do not have any accesses in our code + // at all or if the task cleans up the tasks before it awaits again. We + // test the task cleanup case. + exclusivityTests.test("case3") { @MainActor in + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + withExclusiveAccess(to: &global) { _ in + debugLog("==> Making sure can push/pop access") + } + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + // Different task without sync so no access issue. + withExclusiveAccess(to: &global) { _ in + debugLog("==> Making sure can push/pop access") + } + withExclusiveAccess(to: &global2) { _ in + debugLog("==> Making sure can push/pop access") + } + } + await handle2.value + } + withExclusiveAccess(to: &global2) { _ in + // Sync accesses should have not been injected yet. + debugLog("==> No crash.") + } + withExclusiveAccess(to: &global) { _ in + // Make sure that global is cleaned up appropriately. + debugLog("==> No crash.") + } + await handle.value + withExclusiveAccess(to: &global2) { _ in + // Sync accesses should have been cleaned up by now. So no + // crash. + debugLog("==> No crash.") + } + withExclusiveAccess(to: &global) { _ in + // Make sure that global is cleaned up appropriately. + debugLog("==> No crash.") + } + } + + // Make sure that we crash when accessing &global. + exclusivityTests.test("case3.crash") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + withExclusiveAccess(to: &global) { _ in + debugLog("==> Making sure can push/pop access") + } + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + // No crash here. + withExclusiveAccess(to: &global) { _ in + debugLog("==> Making sure can push/pop access") + } + withExclusiveAccess(to: &global2) { _ in + debugLog("==> Making sure can push/pop access") + } + } + await handle2.value + // But we should crash here if we restore appropriately. + withExclusiveAccess(to: &global2) { _ in + debugLog("Crash!") + } + } + await handle.value + } + + // Case 4: (F, T, T). In case 4, our task does not start with a live + // access but we have accesses from the outside synchronous context, and + // do add new accesses when we pop. + exclusivityTests.test("case4") { @MainActor in + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsync(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + // Make sure we crash with the sync access despite mixing in the tasks + // accesses. + exclusivityTests.test("case4.execaccess.to_sync") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsync(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + withExclusiveAccess(to: &global2) { _ in debugLog("CRASH!") } + debugLog("==> Making sure can push/pop access") + } + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + // Make sure we do not crash with the sync access despite mixing in the tasks + // accesses. + exclusivityTests.test("case4.no_crash") { @MainActor in + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsync(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + // This test makes sure that despite us going through case4, that we + // properly deserialize the task's access and hit a crash in the + // UnownedExecAccessor. + exclusivityTests.test("case4.crash_due_to_deserialized_task") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Before handle") + let handle = Task { @MyMainActorWithAccessInUnownedExecAccessor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsync(to: &global) { + @MyMainActorWithAccessInUnownedExecAccessor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + // CHECK-LABEL: ==> Enter 'testCase4' + // CHECK: ==> MySerialExecutor: Got an enqueue! + // CHECK-NEXT: Inserting new access: [[SYNC_NODE_1:0x[0-9a-f]+]] + // CHECK-NEXT: Tracking! + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_1:0x[0-9a-f]+]]. PC: + // CHECK: ==> Enter 'withExclusiveAccess' + // CHECK-NEXT: Inserting new access: [[SYNC_NODE_2:0x[0-9a-f]+]] + // CHECK-NEXT: Tracking! + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_2]]. PC: + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_1]]. PC: + // CHECK: ==> Enter 'withExclusiveAccess' + // CHECK: ==> MySerialExecutor: Inside access! + // CHECK: ==> MySerialExecutor: Getting Unowned Executor! + // CHECK-NEXT: Entering Thread Local Context. Before Swizzle. Task: [[TASK:0x[0-9a-f]+]] + // CHECK-NEXT: SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): (0x0, 0x0) + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_2]]. PC: + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_1]]. PC: + // CHECK-NEXT: Entering Thread Local Context. After Swizzle. Task: [[TASK]] + // CHECK-NEXT: SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): ([[SYNC_NODE_2]], 0x0) + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_2]]. PC: + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_1]]. PC: + // CHECK: ==> Main: In handle! + // CHECK: ==> No Crash! + // CHECK: ==> Main: All done! + // CHECK: Inserting new access: [[TASK_NODE:0x[0-9a-f]+]] + // CHECK-NEXT: Tracking! + // CHECK-NEXT: Access. Pointer: [[TASK_ACCESS:0x[0-9a-f]+]]. + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_2]]. PC: + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_1]]. PC: + // CHECK-NEXT: Exiting Thread Local Context. Before Swizzle. Task: [[TASK]] + // CHECK-NEXT: SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): ([[SYNC_NODE_2]], 0x0) + // CHECK-NEXT: Access. Pointer: [[TASK_ACCESS]]. PC: + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_2]]. PC: + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_1]]. PC: + // CHECK-NEXT: Exiting Thread Local Context. After Swizzle. Task: [[TASK]] + // CHECK-NEXT: SwiftTaskThreadLocalContext: (FirstAccess,LastAccess): ([[TASK_NODE]], [[TASK_NODE]]) + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_2]]. PC: + // CHECK-NEXT: Access. Pointer: [[SYNC_ACCESS_1]]. PC: + // CHECK: ==> MySerialExecutor: Inside access after run synchronously! + // CHECK: ==> Exit 'testCase4' + exclusivityTests.test("case4.filecheck") { @MainActor in + debugLog("==> Enter 'testCase4'") + defer { debugLog("==> Exit 'testCase4'") } + + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsync(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + // Case 7. (T, T, F). In case 7, our task starts with live accesses and + // sync accesses, but the live accesses are popped before we return. + @Sendable + @MyMainActor @inline(never) + func withExclusiveAccessAsyncCase7(to x: inout T, f: (inout T) async -> U) async -> U { + debugLog("==> Enter 'withExclusiveAccessAsyncCase7'") + defer { debugLog("==> Exit 'withExclusiveAccessAsyncCase7'") } + let t = Task { @MainActor in + debugLog("==> Task to force serialization of MyMainActor by using MainActor") + } + await t.value + return await f(&x) + } + + exclusivityTests.test("case7") { @MainActor in + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsyncCase7(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + @Sendable + @MyMainActor @inline(never) + func withExclusiveAccessAsyncCase7AccessGlobal(to x: inout T, f: (inout T) async -> U) async -> U { + debugLog("==> Enter 'withExclusiveAccessAsyncCase7'") + defer { debugLog("==> Exit 'withExclusiveAccessAsyncCase7'") } + let t = Task { @MainActor in + debugLog("==> Task to force serialization of MyMainActor by using MainActor") + } + await t.value + + // We should crash here since x should also be global and we + // properly deserialized. + + withExclusiveAccess(to: &global) { _ in } + return await f(&x) + } + + // Validate case7 by crashing due to a Task access <-> Task access conflict + exclusivityTests.test("case7.crash.taskaccess_taskaccess_conflict") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsyncCase7AccessGlobal(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + @Sendable + @MyMainActor @inline(never) + func withExclusiveAccessAsyncCase7AccessGlobal2(to x: inout T, f: (inout T) async -> U) async -> U { + debugLog("==> Enter 'withExclusiveAccessAsyncCase7'") + defer { debugLog("==> Exit 'withExclusiveAccessAsyncCase7'") } + let t = Task { @MainActor in + debugLog("==> Task to force serialization of MyMainActor by using MainActor") + } + await t.value + + // We should crash here since our executor had exclusive access to + // global2. + withExclusiveAccess(to: &global2) { _ in } + return await f(&x) + } + + // Validate case7 by crashing due to a Task access <-> Task access conflict + exclusivityTests.test("case7.crash.syncaccess_taskaccess_conflict") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsyncCase7AccessGlobal2(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + // Case 8. (T, T, T). In case 8, our task starts with live accesses and + // sync accesses, and we have remaining live accesses when we return. + @Sendable + @MyMainActor @inline(never) + func withExclusiveAccessAsyncCase8(to x: inout T, f: (inout T) async -> U) async -> U { + debugLog("==> Enter 'withExclusiveAccessAsyncCase8'") + defer { debugLog("==> Exit 'withExclusiveAccessAsyncCase8'") } + let t = Task { @MainActor in + debugLog("==> Task1 to force serialization of MyMainActor by using MainActor") + } + await t.value + + await f(&x) + + let t2 = Task { @MainActor in + debugLog("==> Task2 to force serialization of MyMainActor by using MainActor") + } + await t2.value + + return await f(&x) + } + + exclusivityTests.test("case8") { @MainActor in + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsyncCase8(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + // Case 8. (T, T, T). In case 8, our task starts with live accesses and + // sync accesses, and we have remaining live accesses when we return. + @Sendable + @MyMainActor @inline(never) + func withExclusiveAccessAsyncCase8Access(to x: inout T, f: (inout T) async -> U) async -> U { + debugLog("==> Enter 'withExclusiveAccessAsyncCase8'") + defer { debugLog("==> Exit 'withExclusiveAccessAsyncCase8'") } + let t = Task { @MainActor in + debugLog("==> Task1 to force serialization of MyMainActor by using MainActor") + } + await t.value + + await f(&x) + + let t2 = Task { @MainActor in + debugLog("==> Task2 to force serialization of MyMainActor by using MainActor") + } + await t2.value + + // This is the time period we are testing works in the positive case. + + return await f(&x) + } + + exclusivityTests.test("case8") { @MainActor in + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsyncCase8(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + @Sendable + @MyMainActor @inline(never) + func withExclusiveAccessAsyncCase8AccessGlobal(to x: inout T, f: (inout T) async -> U) async -> U { + debugLog("==> Enter 'withExclusiveAccessAsyncCase8'") + defer { debugLog("==> Exit 'withExclusiveAccessAsyncCase8'") } + let t = Task { @MainActor in + debugLog("==> Task1 to force serialization of MyMainActor by using MainActor") + } + await t.value + + await f(&x) + + let t2 = Task { @MainActor in + debugLog("==> Task2 to force serialization of MyMainActor by using MainActor") + } + await t2.value + // Make sure we swizzled back in our serialized task state, so we + // crash. + withExclusiveAccess(to: &global) { _ in + debugLog("==> TaskAccess + TaskAccess == Crash!") + } + return await f(&x) + } + + exclusivityTests.test("case8.crash.taskaccess_taskaccess_conflict") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsyncCase8AccessGlobal(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + @Sendable + @MyMainActor @inline(never) + func withExclusiveAccessAsyncCase8AccessGlobal2(to x: inout T, f: (inout T) async -> U) async -> U { + debugLog("==> Enter 'withExclusiveAccessAsyncCase8'") + defer { debugLog("==> Exit 'withExclusiveAccessAsyncCase8'") } + let t = Task { @MainActor in + debugLog("==> Task1 to force serialization of MyMainActor by using MainActor") + } + await t.value + + await f(&x) + + let t2 = Task { @MainActor in + debugLog("==> Task2 to force serialization of MyMainActor by using MainActor") + } + await t2.value + // Make sure we swizzled back in our serialized task state, so we + // crash. + withExclusiveAccess(to: &global2) { _ in + debugLog("==> SyncAccess + TaskAccess == Crash!") + } + return await f(&x) + } + + exclusivityTests.test("case8.crash.syncaccess_taskaccess_conflict") { @MainActor in + expectCrashLater(withMessage: "Fatal access conflict detected") + debugLog("==> Before handle") + let handle = Task { @MyMainActor in + debugLog("==> Main: In handle!") + debugLog("==> No Crash!") + debugLog("==> Main: All done!") + + await withExclusiveAccessAsyncCase8AccessGlobal2(to: &global) { + @MyMainActor (x: inout Int) async -> Void in + debugLog("==> Making sure can push/pop access") + } + + // In order to test that we properly hand off the access, we + // need to await here. + let handle2 = await Task { @CustomActor in + debugLog("==> In inner handle") + } + await handle2.value + } + await handle.value + } + + await runAllTestsAsync() + } +} diff --git a/test/Concurrency/Runtime/executor_deinit1.swift b/test/Concurrency/Runtime/executor_deinit1.swift index 935b62d3e0b17..27f8fb83a8838 100644 --- a/test/Concurrency/Runtime/executor_deinit1.swift +++ b/test/Concurrency/Runtime/executor_deinit1.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -enable-experimental-concurrency %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -9,6 +9,7 @@ // https://bugs.swift.org/browse/SR-14461 // UNSUPPORTED: linux +// REQUIRES: rdar78325660 // doesn't matter that it's bool identity function or not func boolIdentityFn(_ x : Bool) -> Bool { return x } diff --git a/test/Concurrency/Runtime/executor_deinit2.swift b/test/Concurrency/Runtime/executor_deinit2.swift index 7da9fe67809f1..cc01fe08ce0d8 100644 --- a/test/Concurrency/Runtime/executor_deinit2.swift +++ b/test/Concurrency/Runtime/executor_deinit2.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -enable-experimental-concurrency %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -11,7 +11,7 @@ // this needs to match with the check count below. let NUM_TASKS : Int = 100 -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) final class Capture : Sendable { func doSomething() { } deinit { @@ -20,7 +20,7 @@ final class Capture : Sendable { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct App { static func main() async { diff --git a/test/Concurrency/Runtime/executor_deinit3.swift b/test/Concurrency/Runtime/executor_deinit3.swift index ab51198b27819..424b6b7ced2f9 100644 --- a/test/Concurrency/Runtime/executor_deinit3.swift +++ b/test/Concurrency/Runtime/executor_deinit3.swift @@ -1,8 +1,9 @@ -// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -enable-experimental-concurrency %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency // REQUIRES: libdispatch +// REQUIRES: rdar78576626 // rdar://76038845 // UNSUPPORTED: use_os_stdlib @@ -15,7 +16,7 @@ import Glibc #endif -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) class Runner { func run() async { while !Task.isCancelled { @@ -24,10 +25,10 @@ class Runner { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Container { var generation = 0 - var runners = [Int : Task.Handle]() + var runners = [Int : Task]() func build(_ n: Int) { for _ in 0.. Int { return first } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncFib(_ n: Int) async -> Int { if n == 0 || n == 1 { return n @@ -46,7 +46,7 @@ func asyncFib(_ n: Int) async -> Int { return result } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func runFibonacci(_ n: Int) async { var result = await asyncFib(n) @@ -55,7 +55,7 @@ func runFibonacci(_ n: Int) async { assert(result == fib(n)) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await runFibonacci(15) diff --git a/test/Concurrency/Runtime/mainactor.swift b/test/Concurrency/Runtime/mainactor.swift index 683554d8f63af..5180bdfa34dae 100644 --- a/test/Concurrency/Runtime/mainactor.swift +++ b/test/Concurrency/Runtime/mainactor.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -enable-experimental-concurrency %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency diff --git a/test/Concurrency/Runtime/objc_async.swift b/test/Concurrency/Runtime/objc_async.swift index 5eb2ad602e10f..cf51bb34cc0a8 100644 --- a/test/Concurrency/Runtime/objc_async.swift +++ b/test/Concurrency/Runtime/objc_async.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-clang -fobjc-arc %S/Inputs/objc_async.m -c -o %t/objc_async_objc.o -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -Xfrontend -disable-availability-checking -parse-as-library -module-name main -import-objc-header %S/Inputs/objc_async.h %s %t/objc_async_objc.o -o %t/objc_async +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -Xfrontend -disable-availability-checking -parse-as-library -module-name main -import-objc-header %S/Inputs/objc_async.h %s %t/objc_async_objc.o -o %t/objc_async // RUN: %target-run %t/objc_async | %FileCheck %s // REQUIRES: executable_test @@ -11,6 +11,10 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime +// Disable this test because it's flaky without a proper way to make the main +// Swift task await a background queue. +// REQUIRES: rdar77934626 + func buttTest() async { let butt = Butt() let result = await butt.butt(1738) @@ -31,7 +35,14 @@ func farmTest() async { class Clbuttic: Butt { override func butt(_ x: Int) async -> Int { print("called into override") - return 679 + return 219 + } +} + +class Buttertion: MutableButt_2Fast2Furious { + override func butt(_ x: Int, completionHandler: @escaping (Int) -> Void) { + print("called again into override") + completionHandler(20721) } } @@ -48,10 +59,16 @@ class Clbuttic: Butt { await farmTest() // CHECK-NEXT: called into override - // CHECK-NEXT: butt {{.*}} named clbuttic occurred at 679 + // CHECK-NEXT: butt {{.*}} named clbuttic occurred at 219 scheduleButt(Clbuttic(), "clbuttic") - await Task.sleep(500_000) + await Task.sleep(250_000) + + // CHECK-NEXT: called again into override + // CHECK-NEXT: butt {{.*}} named buttertion occurred at 20721 + scheduleButt(Buttertion(), "buttertion") + + await Task.sleep(250_000) } } diff --git a/test/Concurrency/Runtime/protocol_resilience.swift b/test/Concurrency/Runtime/protocol_resilience.swift index cad657570a9f2..06ee0d04cd8a8 100644 --- a/test/Concurrency/Runtime/protocol_resilience.swift +++ b/test/Concurrency/Runtime/protocol_resilience.swift @@ -1,9 +1,9 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_protocol)) -Xfrontend -enable-experimental-concurrency -enable-library-evolution %S/Inputs/resilient_protocol.swift -emit-module -emit-module-path %t/resilient_protocol.swiftmodule -module-name resilient_protocol +// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_protocol)) -Xfrontend -disable-availability-checking -enable-library-evolution %S/Inputs/resilient_protocol.swift -emit-module -emit-module-path %t/resilient_protocol.swiftmodule -module-name resilient_protocol // RUN: %target-codesign %t/%target-library-name(resilient_protocol) -// RUN: %target-build-swift -parse-as-library -Xfrontend -enable-experimental-concurrency %s -lresilient_protocol -I %t -L %t -o %t/main %target-rpath(%t) +// RUN: %target-build-swift -parse-as-library -Xfrontend -disable-availability-checking %s -lresilient_protocol -I %t -L %t -o %t/main %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(resilient_protocol) diff --git a/test/Concurrency/Runtime/reasync.swift b/test/Concurrency/Runtime/reasync.swift index 23c8bcd5e7deb..8a8562eccecbc 100644 --- a/test/Concurrency/Runtime/reasync.swift +++ b/test/Concurrency/Runtime/reasync.swift @@ -1,6 +1,7 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module-path %t/reasync.swiftmodule %S/Inputs/reasync.swift -enable-experimental-concurrency -verify-syntax-tree +// RUN: %target-swift-frontend -emit-module-path %t/reasync.swiftmodule %S/Inputs/reasync.swift -enable-experimental-concurrency -disable-availability-checking -verify-syntax-tree // RUN: %target-build-swift %s -I %t -o %t/main -module-name main +// RUN: %target-codesign %t/main // RUN: %target-run %t/main // REQUIRES: executable_test @@ -64,4 +65,4 @@ ReasyncTests.test("reasyncThrows") { } catch {} } -runAllTests() \ No newline at end of file +runAllTests() diff --git a/test/Concurrency/Runtime/task_creation.swift b/test/Concurrency/Runtime/task_creation.swift new file mode 100644 index 0000000000000..c450649cad8ec --- /dev/null +++ b/test/Concurrency/Runtime/task_creation.swift @@ -0,0 +1,47 @@ +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: libdispatch + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +enum SomeError: Error { + case bad +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + let condition = false + + let t1 = Task { + return 5 + } + + let t2 = Task { () -> Int in + if condition { + throw SomeError.bad + } + + return 7 + } + + let t3 = Task.detached { + return 9 + } + + let t4 = Task.detached { () -> Int in + if condition { + throw SomeError.bad + } + + return 11 + } + + let result = try! await t1.get() + t2.get() + t3.get() + t4.get() + assert(result == 32) + } +} diff --git a/test/Concurrency/Runtime/yielding_continuation.swift b/test/Concurrency/Runtime/yielding_continuation.swift deleted file mode 100644 index a910624d6c86d..0000000000000 --- a/test/Concurrency/Runtime/yielding_continuation.swift +++ /dev/null @@ -1,273 +0,0 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) - -// REQUIRES: executable_test -// REQUIRES: concurrency -// UNSUPPORTED: use_os_stdlib -// UNSUPPORTED: back_deployment_runtime - -// https://bugs.swift.org/browse/SR-14466 -// UNSUPPORTED: OS=windows-msvc - -import _Concurrency -import StdlibUnittest - - -struct SomeError: Error, Equatable { - var value = Int.random(in: 0..<100) -} - -let sleepInterval: UInt64 = 125_000_000 -var tests = TestSuite("YieldingContinuation") - -func forceBeingAsync() async -> Void { } - -@main struct Main { - -static func main() async { - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { - tests.test("yield with no awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self) - expectFalse(continuation.yield("hello")) - await forceBeingAsync() - } - await task.get() - } - - tests.test("yield throwing with no awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self, throwing: Error.self) - expectFalse(continuation.yield(throwing: SomeError())) - await forceBeingAsync() - } - await task.get() - } - - tests.test("yield success result no awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self) - expectFalse(continuation.yield(with: .success("hello"))) - await forceBeingAsync() - } - await task.get() - } - - tests.test("yield failure result no awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self, throwing: Error.self) - expectFalse(continuation.yield(with: .failure(SomeError()))) - await forceBeingAsync() - } - await task.get() - } - - tests.test("yield with awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self) - let t = detach { - let value = await continuation.next() - expectEqual(value, "hello") - } - await Task.sleep(sleepInterval) - expectTrue(continuation.yield("hello")) - await t.get() - } - await task.get() - } - - tests.test("yield result with awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self) - let t = detach { - let value = await continuation.next() - expectEqual(value, "hello") - } - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(with: .success("hello"))) - await t.get() - } - await task.get() - } - - tests.test("yield throwing with awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self, throwing: Error.self) - let failure = SomeError() - let t = detach { - do { - let value = try await continuation.next() - expectUnreachable() - } catch { - if let error = error as? SomeError { - expectEqual(error, failure) - } else { - expectUnreachable() - } - } - } - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(throwing: failure)) - await t.get() - } - await task.get() - } - - tests.test("yield failure with awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self, throwing: Error.self) - let failure = SomeError() - let t = detach { - do { - let value = try await continuation.next() - expectUnreachable() - } catch { - if let error = error as? SomeError { - expectEqual(error, failure) - } else { - expectUnreachable() - } - } - } - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(with: .failure(failure))) - await t.get() - } - await task.get() - } - - tests.test("yield multiple times with awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self) - let t = detach { - let value1 = await continuation.next() - expectEqual(value1, "hello") - let value2 = await continuation.next() - expectEqual(value2, "world") - } - await Task.sleep(sleepInterval) - expectTrue(continuation.yield("hello")) - await Task.sleep(sleepInterval) - expectTrue(continuation.yield("world")) - await t.get() - } - await task.get() - } - - tests.test("yield result multiple times with awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self) - let t = detach { - let value1 = await continuation.next() - expectEqual(value1, "hello") - let value2 = await continuation.next() - expectEqual(value2, "world") - } - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(with: .success("hello"))) - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(with: .success("world"))) - await t.get() - } - await task.get() - } - - tests.test("yield throwing multiple times with awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self, throwing: Error.self) - let failure1 = SomeError() - let failure2 = SomeError() - let t = detach { - do { - let value1 = try await continuation.next() - expectUnreachable() - } catch { - if let error = error as? SomeError { - expectEqual(error, failure1) - } else { - expectUnreachable() - } - } - do { - let value2 = try await continuation.next() - } catch { - if let error = error as? SomeError { - expectEqual(error, failure2) - } else { - expectUnreachable() - } - } - } - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(throwing: failure1)) - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(throwing: failure2)) - await t.get() - } - await task.get() - } - - tests.test("yield failure multiple times with awaiting next") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self, throwing: Error.self) - let failure1 = SomeError() - let failure2 = SomeError() - let t = detach { - do { - let value1 = try await continuation.next() - expectUnreachable() - } catch { - if let error = error as? SomeError { - expectEqual(error, failure1) - } else { - expectUnreachable() - } - } - do { - let value2 = try await continuation.next() - } catch { - if let error = error as? SomeError { - expectEqual(error, failure2) - } else { - expectUnreachable() - } - } - } - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(with: .failure(failure1))) - await Task.sleep(sleepInterval) - expectTrue(continuation.yield(with: .failure(failure2))) - await t.get() - } - await task.get() - } - - tests.test("concurrent value consumption") { - let task = detach { - let continuation = YieldingContinuation(yielding: String.self) - let t1 = detach { - var result = await continuation.next() - expectEqual(result, "hello") - result = await continuation.next() - expectEqual(result, "world") - } - - let t2 = detach { - var result = await continuation.next() - expectEqual(result, "hello") - result = await continuation.next() - expectEqual(result, "world") - } - - await Task.sleep(sleepInterval) - continuation.yield("hello") - await Task.sleep(sleepInterval) - continuation.yield("world") - await t1.get() - await t2.get() - } - await task.get() - } - } - await runAllTestsAsync() - } -} diff --git a/test/Concurrency/actor_call_implicitly_async.swift b/test/Concurrency/actor_call_implicitly_async.swift index 8f0dbb086705a..4dd23da7ed006 100644 --- a/test/Concurrency/actor_call_implicitly_async.swift +++ b/test/Concurrency/actor_call_implicitly_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -warn-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency // REQUIRES: concurrency @@ -13,7 +13,7 @@ func rethrower(_ f : @autoclosure () throws -> Any) rethrows -> Any { func asAutoclosure(_ f : @autoclosure () -> Any) -> Any { return f() } // not a concurrency-safe type -class Box { +class Box { // expected-note 4{{class 'Box' does not conform to the `Sendable` protocol}} var counter : Int = 0 } @@ -33,11 +33,9 @@ actor BankAccount { curBalance = initialDeposit } - // NOTE: this func is accessed through both async and sync calls. - // expected-note@+1 {{calls to instance method 'balance()' from outside of its actor context are implicitly asynchronous}} func balance() -> Int { return curBalance } - // expected-note@+1 2{{calls to instance method 'deposit' from outside of its actor context are implicitly asynchronous}} + // expected-note@+1 {{calls to instance method 'deposit' from outside of its actor context are implicitly asynchronous}} func deposit(_ amount : Int) -> Int { guard amount >= 0 else { return 0 } @@ -231,9 +229,9 @@ func anotherAsyncFunc() async { // expected-note@+1{{calls to instance method 'balance()' from outside of its actor context are implicitly asynchronous}} _ = b.balance() - _ = b.balance // expected-error {{actor-isolated instance method 'balance()' can only be referenced from inside the actor}} + _ = b.balance // expected-error {{actor-isolated instance method 'balance()' can not be partially applied}} - a.owner = "cat" // expected-error{{actor-isolated property 'owner' can only be mutated from inside the actor}} + a.owner = "cat" // expected-error{{actor-isolated property 'owner' can not be mutated from a non-isolated context}} // expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{property access is 'async'}} _ = b.owner _ = await b.owner == "cat" @@ -244,9 +242,9 @@ func anotherAsyncFunc() async { func regularFunc() { let a = BankAccount(initialDeposit: 34) - _ = a.deposit //expected-error{{actor-isolated instance method 'deposit' can only be referenced from inside the actor}} + _ = a.deposit //expected-error{{actor-isolated instance method 'deposit' can not be partially applied}} - _ = a.deposit(1) // expected-error{{actor-isolated instance method 'deposit' can only be referenced from inside the actor}} + _ = a.deposit(1) // expected-error{{actor-isolated instance method 'deposit' can not be referenced from a non-isolated context}} } @@ -361,18 +359,23 @@ actor Calculator { @OrangeActor func doSomething() async { let _ = (await bananaAdd(1))(2) // expected-warning@-1{{cannot call function returning non-sendable type}} + // expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}} let _ = await (await bananaAdd(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}} // expected-warning@-1{{cannot call function returning non-sendable type}} + // expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}} let calc = Calculator() let _ = (await calc.addCurried(1))(2) // expected-warning@-1{{cannot call function returning non-sendable type}} + // expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}} let _ = await (await calc.addCurried(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}} // expected-warning@-1{{cannot call function returning non-sendable type}} + // expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}} let plusOne = await calc.addCurried(await calc.add(0, 1)) // expected-warning@-1{{cannot call function returning non-sendable type}} + // expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}} let _ = plusOne(2) } diff --git a/test/Concurrency/actor_definite_init.swift b/test/Concurrency/actor_definite_init.swift new file mode 100644 index 0000000000000..0859e85248a26 --- /dev/null +++ b/test/Concurrency/actor_definite_init.swift @@ -0,0 +1,406 @@ +// RUN: %target-swift-frontend -parse-as-library -emit-sil -verify %s + +// REQUIRES: concurrency + +// NOTE: Once `-swift-version 6` exists, you should update this test so that +// most of its warnings are expected to be errors. This test intentionally does +// not specify a Swift version, so that once Swift 6 exists, you'll see this +// test break and update it accordingly. -kavon + +enum BogusError: Error { + case blah +} + +@available(SwiftStdlib 5.5, *) +actor Convenient { + var x: Int + var y: Convenient? + + init(val: Int) { + self.x = val + } + + convenience init(bigVal: Int) { + if bigVal < 0 { + self.init(val: 0) + say(msg: "hello from actor!") + } + say(msg: "said this too early!") // expected-error {{'self' used before 'self.init' call or assignment to 'self'}} + self.init(val: bigVal) + + Task { await self.mutateIsolatedState() } + } + + convenience init!(biggerVal1 biggerVal: Int) { + guard biggerVal < 1234567 else { return nil } + self.init(bigVal: biggerVal) + say(msg: "hello?") + } + + @MainActor + convenience init?(biggerVal2 biggerVal: Int) async { + guard biggerVal < 1234567 else { return nil } + self.init(bigVal: biggerVal) + say(msg: "hello?") + await mutateIsolatedState() + } + + convenience init() async { + self.init(val: 10) + await mutateIsolatedState() + } + + init(throwyDesignated val: Int) throws { + guard val > 0 else { throw BogusError.blah } + self.x = 10 + say(msg: "hello?") // expected-warning {{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { self } // expected-warning {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + } + + init(asyncThrowyDesignated val: Int) async throws { + guard val > 0 else { throw BogusError.blah } + self.x = 10 + say(msg: "hello?") + Task { self } + } + + convenience init(throwyConvenient val: Int) throws { + try self.init(throwyDesignated: val) + say(msg: "hello?") + Task { self } + } + + func mutateIsolatedState() { + self.y = self + } + + nonisolated func say(msg: String) { + print(msg) + } +} + +func randomInt() -> Int { return 4 } + +@available(SwiftStdlib 5.5, *) +func callMethod(_ a: MyActor) {} + +@available(SwiftStdlib 5.5, *) +func passInout(_ a: inout T) {} + +@available(SwiftStdlib 5.5, *) +actor MyActor { + var x: Int + var y: Int + var hax: MyActor? + + var computedProp : Int { + get { 0 } + set { } + } + + func helloWorld() {} + + convenience init(ci1 c: Bool) { + self.init(i1: c) + Task { self } + callMethod(self) + } + + init(i1 c: Bool) { + self.x = 0 + _ = self.x + self.y = self.x + + Task { self } // expected-warning{{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-warning{{actor 'self' can only be passed 'inout' from an async initializer}} + + self.x = self.y + self.x = randomInt() + (_, _) = (self.x, self.y) + _ = self.x == 0 + + self.hax = self // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + _ = self.hax + + _ = computedProp // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-warning {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + + init?(i1_nil c: Bool) { + self.x = 0 + guard c else { return nil } + self.y = self.x + + Task { self } // expected-warning{{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-warning{{actor 'self' can only be passed 'inout' from an async initializer}} + + self.x = self.y + + self.hax = self // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + _ = self.hax + + _ = computedProp // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-warning {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + + init!(i1_boom c: Bool) { + self.x = 0 + guard c else { return nil } + self.y = self.x + + Task { self } // expected-warning{{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-warning{{actor 'self' can only be passed 'inout' from an async initializer}} + + self.x = self.y + + self.hax = self // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + _ = self.hax + + _ = computedProp // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-warning{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-warning {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + + @MainActor + init(i2 c: Bool) { + self.x = 0 + self.y = self.x + + Task { self } // expected-warning{{actor 'self' cannot be captured by a closure from a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-warning{{actor 'self' cannot be passed 'inout' from a global-actor isolated initializer}} + + self.x = self.y + + self.hax = self // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + _ = self.hax + + _ = computedProp // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-warning {{actor 'self' cannot be captured by a closure from a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + + init(i3 c: Bool) async { + self.x = 0 + self.y = self.x + + Task { self } + + self.helloWorld() + callMethod(self) + passInout(&self.x) + + self.x = self.y + + self.hax = self + _ = Optional.some(self) + + _ = computedProp + computedProp = 1 + + Task { + _ = await self.hax + await self.helloWorld() + } + } + + @MainActor + init(i4 c: Bool) async { + self.x = 0 + self.y = self.x + + Task { self } // expected-warning{{actor 'self' cannot be captured by a closure from a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-warning{{actor 'self' cannot be passed 'inout' from a global-actor isolated initializer}} + + self.x = self.y + + self.hax = self // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + _ = self.hax + + _ = computedProp // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-warning{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-warning {{actor 'self' cannot be captured by a closure from a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + +} + + +@available(SwiftStdlib 5.5, *) +actor X { + var counter: Int + + init(v1 start: Int) { + self.counter = start + Task { await self.setCounter(start + 1) } // expected-warning {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + if self.counter != start { + fatalError("where's my protection?") + } + } + + func setCounter(_ x : Int) { + self.counter = x + } +} + +struct CardboardBox { + public let item: T +} + + +@available(SwiftStdlib 5.5, *) +var globalVar: EscapeArtist? + +@available(SwiftStdlib 5.5, *) +actor EscapeArtist { + var x: Int + + init(attempt1: Bool) { + self.x = 0 + + globalVar = self // expected-warning {{this use of actor 'self' can only appear in an async initializer}} + Task { await globalVar!.isolatedMethod() } + + if self.x == 0 { + fatalError("race detected.") + } + } + + init(attempt2: Bool) { + self.x = 0 + + let wrapped: EscapeArtist? = .some(self) // expected-warning {{this use of actor 'self' can only appear in an async initializer}} + let selfUnchained = wrapped! + + Task { await selfUnchained.isolatedMethod() } + if self.x == 0 { + fatalError("race detected.") + } + } + + init(attempt3: Bool) { + self.x = 0 + + // expected-warning@+2 {{variable 'unchainedSelf' was never mutated; consider changing to 'let' constant}} + // expected-warning@+1 {{this use of actor 'self' can only appear in an async initializer}} + var unchainedSelf = self + + unchainedSelf.nonisolated() + } + + init(attempt4: Bool) { + self.x = 0 + + let unchainedSelf = self + + unchainedSelf.nonisolated() // expected-warning {{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + let _ = { unchainedSelf.nonisolated() } // expected-warning {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + } + + init(attempt5: Bool) { + self.x = 0 + + let box = CardboardBox(item: self) // expected-warning {{this use of actor 'self' can only appear in an async initializer}} + box.item.nonisolated() + } + + init(attempt6: Bool) { + self.x = 0 + func fn() { + self.nonisolated() + } + fn() // expected-warning {{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + } + + func isolatedMethod() { x += 1 } + nonisolated func nonisolated() {} +} \ No newline at end of file diff --git a/test/Concurrency/actor_definite_init_swift6.swift b/test/Concurrency/actor_definite_init_swift6.swift new file mode 100644 index 0000000000000..b0dde8d06cfa0 --- /dev/null +++ b/test/Concurrency/actor_definite_init_swift6.swift @@ -0,0 +1,404 @@ +// RUN: %target-swift-frontend -parse-as-library -emit-sil -swift-version 6 -verify %s + +// Requires 'asserts' for Swift 6 mode. +// REQUIRES: concurrency && asserts + +enum BogusError: Error { + case blah +} + +@available(SwiftStdlib 5.5, *) +actor Convenient { + var x: Int + var y: Convenient? + + init(val: Int) { + self.x = val + } + + convenience init(bigVal: Int) { + if bigVal < 0 { + self.init(val: 0) + say(msg: "hello from actor!") + } + say(msg: "said this too early!") // expected-error {{'self' used before 'self.init' call or assignment to 'self'}} + self.init(val: bigVal) + + Task { await self.mutateIsolatedState() } + } + + convenience init!(biggerVal1 biggerVal: Int) { + guard biggerVal < 1234567 else { return nil } + self.init(bigVal: biggerVal) + say(msg: "hello?") + } + + @MainActor + convenience init?(biggerVal2 biggerVal: Int) async { + guard biggerVal < 1234567 else { return nil } + self.init(bigVal: biggerVal) + say(msg: "hello?") + await mutateIsolatedState() + } + + convenience init() async { + self.init(val: 10) + await mutateIsolatedState() + } + + init(throwyDesignated val: Int) throws { + guard val > 0 else { throw BogusError.blah } + self.x = 10 + say(msg: "hello?") // expected-error {{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { self } // expected-error {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + } + + init(asyncThrowyDesignated val: Int) async throws { + guard val > 0 else { throw BogusError.blah } + self.x = 10 + say(msg: "hello?") + Task { self } + } + + convenience init(throwyConvenient val: Int) throws { + try self.init(throwyDesignated: val) + say(msg: "hello?") + Task { self } + } + + func mutateIsolatedState() { + self.y = self + } + + nonisolated func say(msg: String) { + print(msg) + } +} + +func randomInt() -> Int { return 4 } + +@available(SwiftStdlib 5.5, *) +func callMethod(_ a: MyActor) {} + +@available(SwiftStdlib 5.5, *) +func passInout(_ a: inout T) {} + +@available(SwiftStdlib 5.5, *) +actor MyActor { + var x: Int + var y: Int + var hax: MyActor? + + var computedProp : Int { + get { 0 } + set { } + } + + func helloWorld() {} + + convenience init(ci1 c: Bool) { + self.init(i1: c) + Task { self } + callMethod(self) + } + + init(i1 c: Bool) { + self.x = 0 + _ = self.x + self.y = self.x + + Task { self } // expected-error{{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-error{{actor 'self' can only be passed 'inout' from an async initializer}} + + self.x = self.y + self.x = randomInt() + (_, _) = (self.x, self.y) + _ = self.x == 0 + + self.hax = self // expected-error{{this use of actor 'self' can only appear in an async initializer}} + _ = self.hax + + _ = computedProp // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-error {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + + init?(i1_nil c: Bool) { + self.x = 0 + guard c else { return nil } + self.y = self.x + + Task { self } // expected-error{{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-error{{actor 'self' can only be passed 'inout' from an async initializer}} + + self.x = self.y + + self.hax = self // expected-error{{this use of actor 'self' can only appear in an async initializer}} + _ = self.hax + + _ = computedProp // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-error {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + + init!(i1_boom c: Bool) { + self.x = 0 + guard c else { return nil } + self.y = self.x + + Task { self } // expected-error{{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-error{{actor 'self' can only be passed 'inout' from an async initializer}} + + self.x = self.y + + self.hax = self // expected-error{{this use of actor 'self' can only appear in an async initializer}} + _ = self.hax + + _ = computedProp // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-error{{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-error {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + + @MainActor + init(i2 c: Bool) { + self.x = 0 + self.y = self.x + + Task { self } // expected-error{{actor 'self' cannot be captured by a closure from a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-error{{actor 'self' cannot be passed 'inout' from a global-actor isolated initializer}} + + self.x = self.y + + self.hax = self // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + _ = self.hax + + _ = computedProp // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-error {{actor 'self' cannot be captured by a closure from a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + + init(i3 c: Bool) async { + self.x = 0 + self.y = self.x + + Task { self } + + self.helloWorld() + callMethod(self) + passInout(&self.x) + + self.x = self.y + + self.hax = self + _ = Optional.some(self) + + _ = computedProp + computedProp = 1 + + Task { + _ = await self.hax + await self.helloWorld() + } + } + + @MainActor + init(i4 c: Bool) async { + self.x = 0 + self.y = self.x + + Task { self } // expected-error{{actor 'self' cannot be captured by a closure from a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + self.helloWorld() // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + callMethod(self) // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + passInout(&self.x) // expected-error{{actor 'self' cannot be passed 'inout' from a global-actor isolated initializer}} + + self.x = self.y + + self.hax = self // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + _ = self.hax + + _ = computedProp // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + computedProp = 1 // expected-error{{this use of actor 'self' cannot appear in a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + Task { // expected-error {{actor 'self' cannot be captured by a closure from a global-actor isolated initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + _ = await self.hax + await self.helloWorld() + } + } + +} + + +@available(SwiftStdlib 5.5, *) +actor X { + var counter: Int + + init(v1 start: Int) { + self.counter = start + Task { await self.setCounter(start + 1) } // expected-error {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + if self.counter != start { + fatalError("where's my protection?") + } + } + + func setCounter(_ x : Int) { + self.counter = x + } +} + +struct CardboardBox { + public let item: T +} + + +@available(SwiftStdlib 5.5, *) +var globalVar: EscapeArtist? // expected-note 2 {{var declared here}} + +@available(SwiftStdlib 5.5, *) +actor EscapeArtist { + var x: Int + + init(attempt1: Bool) { + self.x = 0 + + globalVar = self // expected-error {{this use of actor 'self' can only appear in an async initializer}} + // expected-warning@-1{{reference to var 'globalVar' is not concurrency-safe because it involves shared mutable state}} + Task { await globalVar!.isolatedMethod() } + // expected-warning@-1{{reference to var 'globalVar' is not concurrency-safe because it involves shared mutable state}} + + if self.x == 0 { + fatalError("race detected.") + } + } + + init(attempt2: Bool) { + self.x = 0 + + let wrapped: EscapeArtist? = .some(self) // expected-error {{this use of actor 'self' can only appear in an async initializer}} + let selfUnchained = wrapped! + + Task { await selfUnchained.isolatedMethod() } + if self.x == 0 { + fatalError("race detected.") + } + } + + init(attempt3: Bool) { + self.x = 0 + + // expected-warning@+2 {{variable 'unchainedSelf' was never mutated; consider changing to 'let' constant}} + // expected-error@+1 {{this use of actor 'self' can only appear in an async initializer}} + var unchainedSelf = self + + unchainedSelf.nonisolated() + } + + init(attempt4: Bool) { + self.x = 0 + + let unchainedSelf = self + + unchainedSelf.nonisolated() // expected-error {{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + + let _ = { unchainedSelf.nonisolated() } // expected-error {{actor 'self' can only be captured by a closure from an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + } + + init(attempt5: Bool) { + self.x = 0 + + let box = CardboardBox(item: self) // expected-error {{this use of actor 'self' can only appear in an async initializer}} + box.item.nonisolated() + } + + init(attempt6: Bool) { + self.x = 0 + func fn() { + self.nonisolated() + } + fn() // expected-error {{this use of actor 'self' can only appear in an async initializer}} + // expected-note@-1 {{convenience initializers allow non-isolated use of 'self' once initialized}} + } + + func isolatedMethod() { x += 1 } + nonisolated func nonisolated() {} +} diff --git a/test/Concurrency/actor_inout_isolation.swift b/test/Concurrency/actor_inout_isolation.swift index ff2b762161aba..f4ad62d8c7bcb 100644 --- a/test/Concurrency/actor_inout_isolation.swift +++ b/test/Concurrency/actor_inout_isolation.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency // Verify that we don't allow actor-isolated state to be passed via inout @@ -22,7 +22,7 @@ struct Point { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor TestActor { // expected-note@+1{{mutation of this property is only permitted within the actor}} var position = Point(x: 0, y: 0) @@ -37,15 +37,15 @@ actor TestActor { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func modifyAsynchronously(_ foo: inout Int) async { foo += 1 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) enum Container { static let modifyAsyncValue = modifyAsynchronously } // external function call -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension TestActor { // Can't pass actor-isolated primitive into a function @@ -82,7 +82,7 @@ extension TestActor { } // internal method call -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension TestActor { func modifyByValue(_ other: inout Int) async { other += value1 @@ -95,7 +95,7 @@ extension TestActor { } // external class method call -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) class NonAsyncClass { func modifyOtherAsync(_ other : inout Int) async { // ... @@ -107,7 +107,7 @@ class NonAsyncClass { } // Calling external class/struct async function -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension TestActor { // Can't pass state into async method of another class @@ -136,12 +136,12 @@ extension TestActor { } // Check implicit async testing -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor DifferentActor { func modify(_ state: inout Int) {} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension TestActor { func modify(_ state: inout Int) {} @@ -160,7 +160,7 @@ extension TestActor { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor MyActor { var points: [Point] = [] var int: Int = 0 @@ -186,7 +186,7 @@ actor MyActor { // expected-error@+1{{cannot pass immutable value of type 'Int' as inout argument}} await modifyAsynchronously(&(maybePoint?.z)!) - // expected-error@+2{{actor-isolated property 'position' can only be used 'inout' from inside the actor}} + // expected-error@+2{{actor-isolated property 'position' can not be used 'inout' on a non-isolated actor instance}} // expected-error@+1{{actor-isolated property 'myActor' cannot be passed 'inout' to 'async' function call}} await modifyAsynchronously(&myActor.position.x) } @@ -194,7 +194,7 @@ actor MyActor { // Verify global actor protection -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @globalActor struct MyGlobalActor { static let shared = TestActor() @@ -207,34 +207,34 @@ struct MyGlobalActor { // expected-error@+3{{actor-isolated var 'number' cannot be passed 'inout' to 'async' function call}} // expected-error@+2{{var 'number' isolated to global actor 'MyGlobalActor' can not be used 'inout' from a non-isolated context}} -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { -let _ = detach { await { (_ foo: inout Int) async in foo += 1 }(&number) } +if #available(SwiftStdlib 5.5, *) { +let _ = Task.detached { await { (_ foo: inout Int) async in foo += 1 }(&number) } } // attempt to pass global state owned by the global actor to another async function // expected-error@+2{{actor-isolated var 'number' cannot be passed 'inout' to 'async' function call}} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MyGlobalActor func sneaky() async { await modifyAsynchronously(&number) } // It's okay to pass actor state inout to synchronous functions! func globalSyncFunction(_ foo: inout Int) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MyGlobalActor func globalActorSyncFunction(_ foo: inout Int) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MyGlobalActor func globalActorAsyncOkay() async { globalActorSyncFunction(&number) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MyGlobalActor func globalActorAsyncOkay2() async { globalSyncFunction(&number) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MyGlobalActor func globalActorSyncOkay() { globalSyncFunction(&number) } // Gently unwrap things that are fine -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct Cat { mutating func meow() async { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct Dog { var cat: Cat? diff --git a/test/Concurrency/actor_isolation.swift b/test/Concurrency/actor_isolation.swift index 202bf7acc1a65..9066e804c31df 100644 --- a/test/Concurrency/actor_isolation.swift +++ b/test/Concurrency/actor_isolation.swift @@ -1,32 +1,36 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -enable-experimental-async-handler -warn-concurrency +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking +// RUN: %target-typecheck-verify-swift -I %t -disable-availability-checking -warn-concurrency // REQUIRES: concurrency +import OtherActors + let immutableGlobal: String = "hello" var mutableGlobal: String = "can't touch this" // expected-note 5{{var declared here}} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func globalFunc() { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func acceptClosure(_: () -> T) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func acceptConcurrentClosure(_: @Sendable () -> T) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func acceptEscapingClosure(_: @escaping () -> T) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func acceptEscapingClosure(_: @escaping (String) -> ()) async -> T? { nil } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @discardableResult func acceptAsyncClosure(_: () async -> T) -> T { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func acceptEscapingAsyncClosure(_: @escaping () async -> T) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func acceptInout(_: inout T) {} // ---------------------------------------------------------------------- // Actor state isolation restrictions // ---------------------------------------------------------------------- -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor MySuperActor { var superState: Int = 25 // expected-note {{mutation of this property is only permitted within the actor}} @@ -42,14 +46,14 @@ actor MySuperActor { } } -class Point { +class Point { // expected-note{{class 'Point' does not conform to the `Sendable` protocol}} var x : Int = 0 var y : Int = 0 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor MyActor: MySuperActor { // expected-error{{actor types do not support inheritance}} - nonisolated let immutable: Int = 17 + let immutable: Int = 17 // expected-note@+2 2{{property declared here}} // expected-note@+1 6{{mutation of this property is only permitted within the actor}} var mutable: Int = 71 @@ -58,8 +62,7 @@ actor MyActor: MySuperActor { // expected-error{{actor types do not support inhe // expected-note@+1 4{{property declared here}} var text: [String] = [] - nonisolated let point : Point = Point() // expected-error{{non-isolated let property 'point' has non-Sendable type 'Point'}} - let otherPoint = Point() + let point : Point = Point() @MainActor var name : String = "koala" // expected-note{{property declared here}} @@ -68,50 +71,45 @@ actor MyActor: MySuperActor { // expected-error{{actor types do not support inhe return self.name // expected-error{{property 'name' isolated to global actor 'MainActor' can not be referenced from actor 'MyActor' in a synchronous context}} } - class func synchronousClass() { } + static func synchronousClass() { } static func synchronousStatic() { } - func synchronous() -> String { text.first ?? "nothing" } // expected-note 19{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}} + func synchronous() -> String { text.first ?? "nothing" } // expected-note 9{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}} func asynchronous() async -> String { super.superState += 4 return synchronous() } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Camera { func accessProp(act : MyActor) async -> String { return await act.name } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func checkAsyncPropertyAccess() async { let act = MyActor() let _ : Int = await act.mutable + act.mutable - act.mutable += 1 // expected-error {{actor-isolated property 'mutable' can only be mutated from inside the actor}} + act.mutable += 1 // expected-error {{actor-isolated property 'mutable' can not be mutated from a non-isolated context}} - act.superState += 1 // expected-error {{actor-isolated property 'superState' can only be mutated from inside the actor}} + act.superState += 1 // expected-error {{actor-isolated property 'superState' can not be mutated from a non-isolated context}} - act.text[0].append("hello") // expected-error{{actor-isolated property 'text' can only be mutated from inside the actor}} + act.text[0].append("hello") // expected-error{{actor-isolated property 'text' can not be mutated from a non-isolated context}} // this is not the same as the above, because Array is a value type var arr = await act.text arr[0].append("hello") - act.text.append("no") // expected-error{{actor-isolated property 'text' can only be mutated from inside the actor}} + act.text.append("no") // expected-error{{actor-isolated property 'text' can not be mutated from a non-isolated context}} - act.text[0] += "hello" // expected-error{{actor-isolated property 'text' can only be mutated from inside the actor}} + act.text[0] += "hello" // expected-error{{actor-isolated property 'text' can not be mutated from a non-isolated context}} - _ = act.point - _ = act.otherPoint - // expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} - // expected-note@-2{{property access is 'async'}} - // expected-warning@-3{{cannot use property 'otherPoint' with a non-sendable type 'Point' across actors}} - _ = await act.otherPoint // expected-warning{{cannot use property 'otherPoint' with a non-sendable type 'Point' across actors}} + _ = act.point // expected-warning{{cannot use property 'point' with a non-sendable type 'Point' across actors}} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension MyActor { nonisolated var actorIndependentVar: Int { get { 5 } @@ -124,7 +122,7 @@ extension MyActor { _ = text[0] // expected-error{{actor-isolated property 'text' can not be referenced from a non-isolated context}} _ = synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a non-isolated context}} - // @actorIndependent + // nonisolated _ = actorIndependentFunc(otherActor: self) _ = actorIndependentVar @@ -133,7 +131,7 @@ extension MyActor { _ = self.actorIndependentVar self.actorIndependentVar = 17 - // @actorIndependent on another actor + // nonisolated on another actor _ = otherActor.actorIndependentFunc(otherActor: self) _ = otherActor.actorIndependentVar otherActor.actorIndependentVar = 17 @@ -150,14 +148,14 @@ extension MyActor { _ = mutableGlobal // expected-warning{{reference to var 'mutableGlobal' is not concurrency-safe because it involves shared mutable state}} // Partial application - _ = synchronous // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a non-isolated context}} + _ = synchronous // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} _ = super.superMethod // expected-error{{actor-isolated instance method 'superMethod()' can not be referenced from a non-isolated context}} acceptClosure(synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a non-isolated context}} acceptClosure(self.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a non-isolated context}} - acceptClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}} - acceptEscapingClosure(synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a non-isolated context}}}} - acceptEscapingClosure(self.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a non-isolated}} - acceptEscapingClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}} + acceptClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a non-isolated context}} + acceptEscapingClosure(synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} + acceptEscapingClosure(self.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} + acceptEscapingClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} return 5 } @@ -194,9 +192,9 @@ extension MyActor { _ = otherActor.mutable // expected-error{{expression is 'async' but is not marked with 'await'}}{{9-9=await }} // expected-note@-1{{property access is 'async'}} _ = await otherActor.mutable - otherActor.mutable = 0 // expected-error{{actor-isolated property 'mutable' can only be mutated on 'self'}} - acceptInout(&otherActor.mutable) // expected-error{{actor-isolated property 'mutable' can only be used 'inout' on 'self'}} - // expected-error@+2{{actor-isolated property 'mutable' can only be mutated on 'self'}} + otherActor.mutable = 0 // expected-error{{actor-isolated property 'mutable' can not be mutated on a non-isolated actor instance}} + acceptInout(&otherActor.mutable) // expected-error{{actor-isolated property 'mutable' can not be used 'inout' on a non-isolated actor instance}} + // expected-error@+2{{actor-isolated property 'mutable' can not be mutated on a non-isolated actor instance}} // expected-warning@+1{{no 'async' operations occur within 'await' expression}} await otherActor.mutable = 0 @@ -245,12 +243,12 @@ extension MyActor { _ = otherLocalVar } - _ = self.text[0] // expected-error{{actor-isolated property 'text' cannot be referenced from a concurrent closure}} - _ = self.mutable // expected-error{{actor-isolated property 'mutable' cannot be referenced from a concurrent closure}} - self.mutable = 0 // expected-error{{actor-isolated property 'mutable' cannot be mutated from a concurrent closure}} - acceptInout(&self.mutable) // expected-error{{actor-isolated property 'mutable' cannot be used 'inout' from a concurrent closure}} + _ = self.text[0] // expected-error{{actor-isolated property 'text' can not be referenced from a Sendable closure}} + _ = self.mutable // expected-error{{actor-isolated property 'mutable' can not be referenced from a Sendable closure}} + self.mutable = 0 // expected-error{{actor-isolated property 'mutable' can not be mutated from a Sendable closure}} + acceptInout(&self.mutable) // expected-error{{actor-isolated property 'mutable' can not be used 'inout' from a Sendable closure}} _ = self.immutable - _ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from a concurrent closure}} + _ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a Sendable closure}} _ = localVar // expected-error{{reference to captured var 'localVar' in concurrently-executing code}} localVar = 25 // expected-error{{mutation of captured var 'localVar' in concurrently-executing code}} _ = localConstant @@ -281,8 +279,8 @@ extension MyActor { // Local functions might run concurrently. @Sendable func localFn1() { - _ = self.text[0] // expected-error{{actor-isolated property 'text' cannot be referenced from a concurrent function}} - _ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from a concurrent function}} + _ = self.text[0] // expected-error{{actor-isolated property 'text' can not be referenced from a Sendable function}} + _ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a Sendable function}} _ = localVar // expected-error{{reference to captured var 'localVar' in concurrently-executing code}} localVar = 25 // expected-error{{mutation of captured var 'localVar' in concurrently-executing code}} _ = localConstant @@ -290,8 +288,8 @@ extension MyActor { @Sendable func localFn2() { acceptClosure { - _ = text[0] // expected-error{{actor-isolated property 'text' cannot be referenced from a concurrent function}} - _ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from a concurrent function}} + _ = text[0] // expected-error{{actor-isolated property 'text' can not be referenced from a non-isolated context}} + _ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from a non-isolated context}} _ = localVar // expected-error{{reference to captured var 'localVar' in concurrently-executing code}} localVar = 25 // expected-error{{mutation of captured var 'localVar' in concurrently-executing code}} _ = localConstant @@ -306,14 +304,14 @@ extension MyActor { localVar = 0 // Partial application - _ = synchronous // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} + _ = synchronous _ = super.superMethod acceptClosure(synchronous) acceptClosure(self.synchronous) - acceptClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}} - acceptEscapingClosure(synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} - acceptEscapingClosure(self.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} - acceptEscapingClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}} + acceptClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced on a non-isolated actor instance}} + acceptEscapingClosure(synchronous) + acceptEscapingClosure(self.synchronous) + acceptEscapingClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} acceptAsyncClosure(self.asynchronous) acceptEscapingAsyncClosure(self.asynchronous) @@ -323,35 +321,35 @@ extension MyActor { // ---------------------------------------------------------------------- // Global actor isolation restrictions // ---------------------------------------------------------------------- -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor SomeActor { } @globalActor -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct SomeGlobalActor { static let shared = SomeActor() } @globalActor -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct SomeOtherGlobalActor { static let shared = SomeActor() } @globalActor -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct GenericGlobalActor { static var shared: SomeActor { SomeActor() } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @SomeGlobalActor func onions() {} // expected-note{{calls to global function 'onions()' from outside of its actor context are implicitly asynchronous}} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MainActor func beets() { onions() } // expected-error{{call to global actor 'SomeGlobalActor'-isolated global function 'onions()' in a synchronous main actor-isolated context}} // expected-note@-1{{calls to global function 'beets()' from outside of its actor context are implicitly asynchronous}} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Crystal { // expected-note@+2 {{property declared here}} // expected-note@+1 2 {{mutation of this property is only permitted within the actor}} @@ -380,31 +378,21 @@ actor Crystal { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @SomeGlobalActor func syncGlobalActorFunc() { syncGlobalActorFunc() } // expected-note {{calls to global function 'syncGlobalActorFunc()' from outside of its actor context are implicitly asynchronous}} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @SomeGlobalActor func asyncGlobalActorFunc() async { await asyncGlobalActorFunc() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @SomeOtherGlobalActor func syncOtherGlobalActorFunc() { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @SomeOtherGlobalActor func asyncOtherGlobalActorFunc() async { await syncGlobalActorFunc() await asyncGlobalActorFunc() } -// test global actor funcs that are marked asyncHandler -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -@SomeGlobalActor func goo1() async { - let _ = goo2 - // expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }} - goo2() // expected-note{{calls to global function 'goo2()' from outside of its actor context are implicitly asynchronous}} -} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -@asyncHandler @SomeOtherGlobalActor func goo2() { await goo1() } - -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func testGlobalActorClosures() { let _: Int = acceptAsyncClosure { @SomeGlobalActor in syncGlobalActorFunc() @@ -418,7 +406,7 @@ func testGlobalActorClosures() { acceptConcurrentClosure { @SomeGlobalActor in 5 } // expected-error{{converting function value of type '@SomeGlobalActor @Sendable () -> Int' to '@Sendable () -> Int' loses global actor 'SomeGlobalActor'}} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension MyActor { @SomeGlobalActor func onGlobalActor(otherActor: MyActor) async { // Access to other functions in this actor are okay. @@ -470,11 +458,11 @@ extension MyActor { _ = await super[0] // Accesses on other actors can only reference immutable data or - // call asychronous methods + // call asynchronous methods _ = otherActor.immutable // okay _ = otherActor.synchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}{{9-9=await }} // expected-note@-1{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}} - _ = otherActor.synchronous // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}} + _ = otherActor.synchronous // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} _ = await otherActor.asynchronous() _ = otherActor.text[0] // expected-error{{expression is 'async' but is not marked with 'await'}}{{9-9=await }} // expected-note@-1{{property access is 'async'}} @@ -482,7 +470,7 @@ extension MyActor { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct GenericStruct { @GenericGlobalActor func f() { } // expected-note {{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}} @@ -497,7 +485,7 @@ struct GenericStruct { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension GenericStruct where T == String { @GenericGlobalActor func h2() { @@ -516,7 +504,7 @@ func badNumberUser() { print("The protected number is: \(number)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncBadNumberUser() async { print("The protected number is: \(await number)") } @@ -524,12 +512,12 @@ func asyncBadNumberUser() async { // ---------------------------------------------------------------------- // Non-actor code isolation restrictions // ---------------------------------------------------------------------- -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func testGlobalRestrictions(actor: MyActor) async { let _ = MyActor() // references to sync methods must be fully applied. - _ = actor.synchronous // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced from inside the actor}} + _ = actor.synchronous // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}} _ = actor.asynchronous // any kind of method can be called from outside the actor, so long as it's marked with 'await' @@ -554,7 +542,7 @@ func testGlobalRestrictions(actor: MyActor) async { // expected-note@-1{{subscript access is 'async'}} _ = await actor[0] - // @actorIndependent declarations are permitted. + // nonisolated declarations are permitted. _ = actor.actorIndependentFunc(otherActor: actor) _ = actor.actorIndependentVar actor.actorIndependentVar = 5 @@ -584,7 +572,7 @@ func testGlobalRestrictions(actor: MyActor) async { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func f() { acceptConcurrentClosure { _ = mutableGlobal // expected-warning{{reference to var 'mutableGlobal' is not concurrency-safe because it involves shared mutable state}} @@ -598,7 +586,7 @@ func f() { // ---------------------------------------------------------------------- // Local function isolation restrictions // ---------------------------------------------------------------------- -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func checkLocalFunctions() async { var i = 0 var j = 0 @@ -647,12 +635,12 @@ func checkLocalFunctions() async { // Lazy properties with initializers referencing 'self' // ---------------------------------------------------------------------- -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor LazyActor { var v: Int = 0 // expected-note@-1 6 {{property declared here}} - nonisolated let l: Int = 0 + let l: Int = 0 lazy var l11: Int = { v }() lazy var l12: Int = v @@ -685,7 +673,7 @@ actor LazyActor { } // Infer global actors from context only for instance members. -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MainActor class SomeClassInActor { enum ID: String { case best } @@ -693,7 +681,7 @@ class SomeClassInActor { func inActor() { } // expected-note{{calls to instance method 'inActor()' from outside of its actor context are implicitly asynchronous}} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension SomeClassInActor.ID { func f(_ object: SomeClassInActor) { // expected-note{{add '@MainActor' to make instance method 'f' part of global actor 'MainActor'}} object.inActor() // expected-error{{call to main actor-isolated instance method 'inActor()' in a synchronous nonisolated context}} @@ -701,24 +689,75 @@ extension SomeClassInActor.ID { } // ---------------------------------------------------------------------- -// Initializers +// Initializers (through typechecking only) // ---------------------------------------------------------------------- -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor SomeActorWithInits { var mutableState: Int = 17 var otherMutableState: Int - init() { + init(i1: Bool) { self.mutableState = 42 self.otherMutableState = 17 self.isolated() + self.nonisolated() } - func isolated() { } + init(i2: Bool) async { + self.mutableState = 0 + self.otherMutableState = 1 + + self.isolated() + self.nonisolated() + } + + convenience init(i3: Bool) { + self.init(i1: i3) + self.isolated() // expected-error{{actor-isolated instance method 'isolated()' can not be referenced from a non-isolated context}} + self.nonisolated() + } + + convenience init(i4: Bool) async { + self.init(i1: i4) + await self.isolated() + self.nonisolated() + } + + @MainActor init(i5: Bool) { + self.mutableState = 42 + self.otherMutableState = 17 + + self.isolated() + self.nonisolated() + } + + @MainActor init(i6: Bool) async { + self.mutableState = 42 + self.otherMutableState = 17 + + self.isolated() + self.nonisolated() + } + + @MainActor convenience init(i7: Bool) { + self.init(i1: i7) + self.isolated() // expected-error{{actor-isolated instance method 'isolated()' can not be referenced from the main actor}} + self.nonisolated() + } + + @MainActor convenience init(i8: Bool) async { + self.init(i1: i8) + await self.isolated() + self.nonisolated() + } + + + func isolated() { } // expected-note 2 {{calls to instance method 'isolated()' from outside of its actor context are implicitly asynchronous}} + nonisolated func nonisolated() {} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MainActor class SomeClassWithInits { var mutableState: Int = 17 @@ -746,46 +785,98 @@ class SomeClassWithInits { } func hasDetached() { - detach { + Task.detached { // okay - await self.isolated() // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}} - self.isolated() // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}} + await self.isolated() + self.isolated() // expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} // expected-note@-2{{calls to instance method 'isolated()' from outside of its actor context are implicitly asynchronous}} - print(await self.mutableState) // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}} + print(await self.mutableState) } } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func outsideSomeClassWithInits() { // expected-note 3 {{add '@MainActor' to make global function 'outsideSomeClassWithInits()' part of global actor 'MainActor'}} _ = SomeClassWithInits() // expected-error{{call to main actor-isolated initializer 'init()' in a synchronous nonisolated context}} _ = SomeClassWithInits.shared // expected-error{{static property 'shared' isolated to global actor 'MainActor' can not be referenced from this synchronous context}} SomeClassWithInits.staticIsolated() // expected-error{{call to main actor-isolated static method 'staticIsolated()' in a synchronous nonisolated context}} } +// ---------------------------------------------------------------------- +// nonisolated let and cross-module let +// ---------------------------------------------------------------------- +func testCrossModuleLets(actor: OtherModuleActor) async { + _ = actor.a // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{property access is 'async'}} + _ = await actor.a // okay + _ = actor.b // okay + _ = actor.c // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{property access is 'async'}} + // expected-warning@-2{{cannot use property 'c' with a non-sendable type 'SomeClass' across actors}} + _ = await actor.c // expected-warning{{cannot use property 'c' with a non-sendable type 'SomeClass' across actors}} +} + + // ---------------------------------------------------------------------- // Actor protocols. // ---------------------------------------------------------------------- -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) + +@available(SwiftStdlib 5.5, *) +actor A: Actor { // ok +} + +@available(SwiftStdlib 5.5, *) +class C: Actor, UnsafeSendable { + // expected-error@-1{{non-actor type 'C' cannot conform to the 'Actor' protocol}} + // expected-warning@-2{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}} + nonisolated var unownedExecutor: UnownedSerialExecutor { + fatalError() + } +} + +@available(SwiftStdlib 5.5, *) protocol P: Actor { func f() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension P { func g() { f() } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor MyActorP: P { func f() { } func h() { g() } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) +protocol SP { + static func s() +} + +@available(SwiftStdlib 5.5, *) +actor ASP: SP { + static func s() { } +} + +@available(SwiftStdlib 5.5, *) +protocol SPD { + static func sd() +} +@available(SwiftStdlib 5.5, *) +extension SPD { + static func sd() { } +} + +@available(SwiftStdlib 5.5, *) +actor ASPD: SPD { +} + +@available(SwiftStdlib 5.5, *) func testCrossActorProtocol(t: T) async { await t.f() await t.g() @@ -795,6 +886,8 @@ func testCrossActorProtocol(t: T) async { t.g() // expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }} // expected-note@-2{{calls to instance method 'g()' from outside of its actor context are implicitly asynchronous}} + ASP.s() + ASPD.sd() } // ---------------------------------------------------------------------- @@ -803,7 +896,7 @@ func testCrossActorProtocol(t: T) async { func acceptAsyncSendableClosure(_: @Sendable () async -> T) { } func acceptAsyncSendableClosureInheriting(@_inheritActorContext _: @Sendable () async -> T) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension MyActor { func testSendableAndInheriting() { acceptAsyncSendableClosure { @@ -824,3 +917,66 @@ extension MyActor { } } } + +@available(SwiftStdlib 5.5, *) +@MainActor // expected-note {{'GloballyIsolatedProto' is isolated to global actor 'MainActor' here}} +protocol GloballyIsolatedProto { +} + +// rdar://75849035 - trying to conform an actor to a global-actor isolated protocol should result in an error +func test_conforming_actor_to_global_actor_protocol() { + @available(SwiftStdlib 5.5, *) + actor MyValue : GloballyIsolatedProto {} + // expected-error@-1 {{actor 'MyValue' cannot conform to global actor isolated protocol 'GloballyIsolatedProto'}} +} + +func test_invalid_reference_to_actor_member_without_a_call_note() { + actor A { + func partial() { } + } + + actor Test { + func returnPartial(other: A) async -> () async -> () { + let a = other.partial + // expected-error@-1 {{actor-isolated instance method 'partial()' can not be partially applied}} + return a + } + } +} + +// Actor isolation and "defer" +actor Counter { + var counter: Int = 0 + + func next() -> Int { + defer { + counter = counter + 1 + } + + return counter + } + + func localNext() -> Int { + func doIt() { + counter = counter + 1 + } + doIt() + + return counter + } +} + +/// Superclass checks for global actor-qualified class types. +class C2 { } + +@SomeGlobalActor +class C3: C2 { } // it's okay to add a global actor to a nonisolated class. + +@GenericGlobalActor +class GenericSuper { } + +@GenericGlobalActor<[T]> +class GenericSub1: GenericSuper<[T]> { } + +@GenericGlobalActor +class GenericSub2: GenericSuper<[T]> { } // expected-error{{global actor 'GenericGlobalActor'-isolated class 'GenericSub2' has different actor isolation from global actor 'GenericGlobalActor'-isolated superclass 'GenericSuper'}} diff --git a/test/Concurrency/actor_isolation_cycle.swift b/test/Concurrency/actor_isolation_cycle.swift new file mode 100644 index 0000000000000..d0f7b0e6a1296 --- /dev/null +++ b/test/Concurrency/actor_isolation_cycle.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: concurrency + +public protocol P { + associatedtype T + @MainActor func f(_: T) + @MainActor func g(_: T) +} +public struct S : P { + public func g(_: Int) {} + public func f(_: T) {} +} diff --git a/test/Concurrency/actor_isolation_objc.swift b/test/Concurrency/actor_isolation_objc.swift index bb6177aadfaf6..c8747aafcb13b 100644 --- a/test/Concurrency/actor_isolation_objc.swift +++ b/test/Concurrency/actor_isolation_objc.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -enable-experimental-async-handler +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency // REQUIRES: objc_interop @@ -24,19 +24,15 @@ actor A { // expected-error@+1 {{cannot form key path to actor-isolated property 'computed'}} _ = #keyPath(A.computed) // expected-error{{argument of '#keyPath' refers to non-'@objc' property 'computed'}} - - _ = #keyPath(A.z) } - nonisolated let w: Int = 0 // expected-note{{add '@objc' to expose this property to Objective-C}} + let w: Int = 0 // expected-note{{add '@objc' to expose this property to Objective-C}} var x: Int = 0 // expected-note{{add '@objc' to expose this property to Objective-C}} @objc var y: Int = 0 // expected-note{{add '@objc' to expose this property to Objective-C}} // expected-error@-1{{actor-isolated property 'y' cannot be @objc}} - @objc @actorIndependent(unsafe) var z: Int = 0 - // expected-note@+1 {{add '@objc' to expose this property to Objective-C}} @objc var computed : Int { // expected-error{{actor-isolated property 'computed' cannot be @objc}} get { 120 } @@ -45,13 +41,17 @@ actor A { func f() { } // expected-note{{add '@objc' to expose this instance method to Objective-C}} func g() { } // expected-note{{add '@objc' to expose this instance method to Objective-C}} @objc func h() async { } - - @objc @asyncHandler func i() {} -} - -func outside(a : A) async { - await a.i() // expected-warning {{no 'async' operations occur within 'await' expression}} } +actor Dril: NSObject { + // expected-note@+2 {{add 'async' to function 'postSynchronouslyTo(twitter:)' to make it asynchronous}} + // expected-error@+1 {{actor-isolated instance method 'postSynchronouslyTo(twitter:)' cannot be @objc}} + @objc func postSynchronouslyTo(twitter msg: String) -> Bool { + return true + } - + @MainActor + @objc func postFromMainActorTo(twitter msg: String) -> Bool { + return true + } +} diff --git a/test/Concurrency/actor_isolation_unsafe.swift b/test/Concurrency/actor_isolation_unsafe.swift index 994fb984d7804..b72e68269637b 100644 --- a/test/Concurrency/actor_isolation_unsafe.swift +++ b/test/Concurrency/actor_isolation_unsafe.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency @globalActor @@ -26,7 +26,7 @@ struct S2_P1: P1 { } struct S3_P1: P1 { - @actorIndependent func onMainActor() { } + nonisolated func onMainActor() { } } struct S4_P1: P1 { @@ -34,9 +34,9 @@ struct S4_P1: P1 { } @MainActor(unsafe) -protocol P2 { +protocol P2 { // expected-note{{protocol 'P2' does not conform to the `Sendable` protocol}} func f() // expected-note{{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}} - @actorIndependent func g() + nonisolated func g() } struct S5_P2: P2 { @@ -44,7 +44,8 @@ struct S5_P2: P2 { func g() { } } -@actorIndependent func testP2(x: S5_P2, p2: P2) { +// expected-warning@+1{{cannot pass argument of non-sendable type 'P2' across actors}} +nonisolated func testP2(x: S5_P2, p2: P2) { p2.f() // expected-error{{call to main actor-isolated instance method 'f()' in a synchronous nonisolated context}} p2.g() // OKAY x.f() // expected-error{{call to main actor-isolated instance method 'f()' in a synchronous nonisolated context}} @@ -72,7 +73,7 @@ class C2: C1 { } class C3: C1 { - @actorIndependent override func method() { + nonisolated override func method() { globalSome() // expected-error{{call to global actor 'SomeGlobalActor'-isolated global function 'globalSome()' in a synchronous nonisolated context}} } } @@ -100,3 +101,13 @@ class C7: C2 { globalSome() // okay } } + +@MainActor(unsafe) // expected-note {{'GloballyIsolatedProto' is isolated to global actor 'MainActor' here}} +protocol GloballyIsolatedProto { +} + +// rdar://75849035 - trying to conform an actor to a global-actor isolated protocol should result in an error +func test_conforming_actor_to_global_actor_protocol() { + actor MyValue : GloballyIsolatedProto {} + // expected-error@-1 {{actor 'MyValue' cannot conform to global actor isolated protocol 'GloballyIsolatedProto'}} +} diff --git a/test/Concurrency/actor_keypath_isolation.swift b/test/Concurrency/actor_keypath_isolation.swift index 4424e582b70fa..cf3870de721dc 100644 --- a/test/Concurrency/actor_keypath_isolation.swift +++ b/test/Concurrency/actor_keypath_isolation.swift @@ -1,15 +1,15 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -warn-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency // REQUIRES: concurrency -class Box { +class Box { // expected-note 3{{class 'Box' does not conform to the `Sendable` protocol}} let size : Int = 0 } actor Door { - nonisolated let immutable : Int = 0 + let immutable : Int = 0 let letBox : Box? = nil let letDict : [Int : Box] = [:] - nonisolated let immutableNeighbor : Door? = nil + let immutableNeighbor : Door? = nil var mutableNeighbor : Door? = nil @@ -20,8 +20,6 @@ actor Door { get { 0 } } - @actorIndependent(unsafe) var unsafeIndependent : Int = 0 - @MainActor var globActor_mutable : Int = 0 @MainActor let globActor_immutable : Int = 0 @@ -32,7 +30,7 @@ actor Door { @MainActor subscript(byName: String) -> Int { 0 } - @actorIndependent subscript(byIEEE754: Double) -> Int { 0 } + nonisolated subscript(byIEEE754: Double) -> Int { 0 } } func attemptAccess(_ t : T, _ f : (T) -> V) -> V { @@ -47,7 +45,7 @@ func tryKeyPathsMisc(d : Door) { // in combination with other key paths - _ = (\Door.letBox).appending(path: // expected-error {{cannot form key path to actor-isolated property 'letBox'}} + _ = (\Door.letBox).appending(path: // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}} \Box?.?.size) _ = (\Door.varBox).appending(path: // expected-error {{cannot form key path to actor-isolated property 'varBox'}} @@ -61,9 +59,9 @@ func tryKeyPathsFromAsync() async { } func tryNonSendable() { - _ = \Door.letDict[0] // expected-error {{cannot form key path to actor-isolated property 'letDict'}} + _ = \Door.letDict[0] // expected-warning {{cannot form key path that accesses non-sendable type '[Int : Box]'}} _ = \Door.varDict[0] // expected-error {{cannot form key path to actor-isolated property 'varDict'}} - _ = \Door.letBox!.size // expected-error {{cannot form key path to actor-isolated property 'letBox'}} + _ = \Door.letBox!.size // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}} } func tryKeypaths() { @@ -71,7 +69,6 @@ func tryKeypaths() { _ = \Door.unsafeGlobActor_mutable // okay for now _ = \Door.immutable - _ = \Door.unsafeIndependent _ = \Door.globActor_immutable _ = \Door.[4.2] _ = \Door.immutableNeighbor?.immutableNeighbor?.immutableNeighbor diff --git a/test/Concurrency/actor_keypath_isolation_swift6.swift b/test/Concurrency/actor_keypath_isolation_swift6.swift new file mode 100644 index 0000000000000..de7171e94d5b8 --- /dev/null +++ b/test/Concurrency/actor_keypath_isolation_swift6.swift @@ -0,0 +1,87 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency -swift-version 6 +// REQUIRES: concurrency && asserts + +class Box { // expected-note 3{{class 'Box' does not conform to the `Sendable` protocol}} + let size : Int = 0 +} + +actor Door { + let immutable : Int = 0 + let letBox : Box? = nil + let letDict : [Int : Box] = [:] + let immutableNeighbor : Door? = nil + + + var mutableNeighbor : Door? = nil + var varDict : [Int : Box] = [:] + var mutable : Int = 0 + var varBox : Box = Box() + var getOnlyInt : Int { + get { 0 } + } + + @MainActor var globActor_mutable : Int = 0 + @MainActor let globActor_immutable : Int = 0 + + @MainActor(unsafe) var unsafeGlobActor_mutable : Int = 0 + @MainActor(unsafe) let unsafeGlobActor_immutable : Int = 0 + + subscript(byIndex: Int) -> Int { 0 } + + @MainActor subscript(byName: String) -> Int { 0 } + + nonisolated subscript(byIEEE754: Double) -> Int { 0 } +} + +func attemptAccess(_ t : T, _ f : (T) -> V) -> V { + return f(t) +} + +func tryKeyPathsMisc(d : Door) { + // as a func + _ = attemptAccess(d, \Door.mutable) // expected-error {{cannot form key path to actor-isolated property 'mutable'}} + _ = attemptAccess(d, \Door.immutable) + _ = attemptAccess(d, \Door.immutableNeighbor?.immutableNeighbor) + + // in combination with other key paths + + _ = (\Door.letBox).appending(path: // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}} + \Box?.?.size) + + _ = (\Door.varBox).appending(path: // expected-error {{cannot form key path to actor-isolated property 'varBox'}} + \Box.size) + +} + +func tryKeyPathsFromAsync() async { + _ = \Door.unsafeGlobActor_immutable + _ = \Door.unsafeGlobActor_mutable // expected-error {{cannot form key path to actor-isolated property 'unsafeGlobActor_mutable'}} +} + +func tryNonSendable() { + _ = \Door.letDict[0] // expected-warning {{cannot form key path that accesses non-sendable type '[Int : Box]'}} + _ = \Door.varDict[0] // expected-error {{cannot form key path to actor-isolated property 'varDict'}} + _ = \Door.letBox!.size // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}} +} + +func tryKeypaths() { + _ = \Door.unsafeGlobActor_immutable + _ = \Door.unsafeGlobActor_mutable // expected-error {{cannot form key path to actor-isolated property 'unsafeGlobActor_mutable'}} + + _ = \Door.immutable + _ = \Door.globActor_immutable + _ = \Door.[4.2] + _ = \Door.immutableNeighbor?.immutableNeighbor?.immutableNeighbor + + _ = \Door.varBox // expected-error{{cannot form key path to actor-isolated property 'varBox'}} + _ = \Door.mutable // expected-error{{cannot form key path to actor-isolated property 'mutable'}} + _ = \Door.getOnlyInt // expected-error{{cannot form key path to actor-isolated property 'getOnlyInt'}} + _ = \Door.mutableNeighbor?.mutableNeighbor?.mutableNeighbor // expected-error 3 {{cannot form key path to actor-isolated property 'mutableNeighbor'}} + + let _ : PartialKeyPath = \.mutable // expected-error{{cannot form key path to actor-isolated property 'mutable'}} + let _ : AnyKeyPath = \Door.mutable // expected-error{{cannot form key path to actor-isolated property 'mutable'}} + + _ = \Door.globActor_mutable // expected-error{{cannot form key path to actor-isolated property 'globActor_mutable'}} + _ = \Door.[0] // expected-error{{cannot form key path to actor-isolated subscript 'subscript(_:)'}} + _ = \Door.["hello"] // expected-error {{cannot form key path to actor-isolated subscript 'subscript(_:)'}} +} diff --git a/test/Concurrency/async_cancellation.swift b/test/Concurrency/async_cancellation.swift index 4f162d0cce2d0..9d762d1a149b0 100644 --- a/test/Concurrency/async_cancellation.swift +++ b/test/Concurrency/async_cancellation.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency enum PictureData { @@ -6,12 +6,12 @@ enum PictureData { case failedToLoadImagePlaceholder } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_cancellation_checkCancellation() async throws { try Task.checkCancellation() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_cancellation_guard_isCancelled(_ any: Any) async -> PictureData { guard !Task.isCancelled else { return PictureData.failedToLoadImagePlaceholder @@ -20,26 +20,27 @@ func test_cancellation_guard_isCancelled(_ any: Any) async -> PictureData { return PictureData.value("...") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct SomeFile: Sendable { func close() {} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_cancellation_withTaskCancellationHandler(_ anything: Any) async -> PictureData { - let handle: Task.Handle = detach { + let handle: Task = .init { let file = SomeFile() - return await withTaskCancellationHandler( - handler: { file.close() }) { + return await withTaskCancellationHandler { await test_cancellation_guard_isCancelled(file) + } onCancel: { + file.close() } } handle.cancel() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_cancellation_loop() async -> Int { struct SampleTask { func process() async {} } diff --git a/test/Concurrency/async_computed_property.swift b/test/Concurrency/async_computed_property.swift index bb8e358824686..2062256956aff 100644 --- a/test/Concurrency/async_computed_property.swift +++ b/test/Concurrency/async_computed_property.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency func asyncFunc(_ value: String) async {} diff --git a/test/Concurrency/async_conformance.swift b/test/Concurrency/async_conformance.swift index d8d43493b9072..8128e4a88d48d 100644 --- a/test/Concurrency/async_conformance.swift +++ b/test/Concurrency/async_conformance.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: objc_interop // REQUIRES: concurrency diff --git a/test/Concurrency/async_initializer.swift b/test/Concurrency/async_initializer.swift index f2cee020a35d2..239efda9cc237 100644 --- a/test/Concurrency/async_initializer.swift +++ b/test/Concurrency/async_initializer.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -disable-availability-checking // REQUIRES: concurrency diff --git a/test/Concurrency/async_initializer_objc.swift b/test/Concurrency/async_initializer_objc.swift index 174dda7164ba9..47df7a1359d36 100644 --- a/test/Concurrency/async_initializer_objc.swift +++ b/test/Concurrency/async_initializer_objc.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency // REQUIRES: objc_interop diff --git a/test/Concurrency/async_let_capture.swift b/test/Concurrency/async_let_capture.swift new file mode 100644 index 0000000000000..b0b1b4fc15443 --- /dev/null +++ b/test/Concurrency/async_let_capture.swift @@ -0,0 +1,17 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking +// REQUIRES: concurrency + +func autoclosureCapture(_: @autoclosure () async throws -> Int) async {} +func nonescapingCapture(_: () async throws -> Int) {} +func escapingCapture(_: @escaping () async throws -> Int) {} + +func foo() async { + async let x = 32 + + async let y = x // expected-error{{not supported}} + _ = await y + + await autoclosureCapture(await x)// expected-error{{not supported}} + nonescapingCapture { await x }// expected-error{{not supported}} + escapingCapture { await x }// expected-error{{not supported}} +} diff --git a/test/Concurrency/spawn_let_isolation.swift b/test/Concurrency/async_let_isolation.swift similarity index 58% rename from test/Concurrency/spawn_let_isolation.swift rename to test/Concurrency/async_let_isolation.swift index 9c057cec9f127..b6cfe3a381715 100644 --- a/test/Concurrency/spawn_let_isolation.swift +++ b/test/Concurrency/async_let_isolation.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency actor MyActor { @@ -9,14 +9,14 @@ actor MyActor { func asynchronous() async -> String { synchronous() } func testAsyncLetIsolation() async { - spawn let x = self.synchronous() + async let x = self.synchronous() - spawn let y = await self.asynchronous() + async let y = await self.asynchronous() - spawn let z = synchronous() + async let z = synchronous() var localText = text - spawn let w = localText.removeLast() // expected-error{{mutation of captured var 'localText' in concurrently-executing code}} + async let w = localText.removeLast() // expected-error{{mutation of captured var 'localText' in concurrently-executing code}} _ = await x _ = await y @@ -27,8 +27,8 @@ actor MyActor { func outside() async { let a = MyActor() - spawn let x = a.synchronous() // okay, await is implicit - spawn let y = await a.synchronous() + async let x = a.synchronous() // okay, await is implicit + async let y = await a.synchronous() _ = await x _ = await y } diff --git a/test/Concurrency/async_main.swift b/test/Concurrency/async_main.swift index 1180e7540f2b8..f3aea13eeeca5 100644 --- a/test/Concurrency/async_main.swift +++ b/test/Concurrency/async_main.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -dump-ast -enable-experimental-concurrency -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-AST -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -Xfrontend -parse-as-library %s -o %t_binary +// RUN: %target-swift-frontend -dump-ast -disable-availability-checking -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-AST +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -Xfrontend -parse-as-library %s -o %t_binary // RUN: %target-run %t_binary | %FileCheck %s --check-prefix=CHECK-EXEC // REQUIRES: concurrency diff --git a/test/Concurrency/async_main_no_concurrency.swift b/test/Concurrency/async_main_no_concurrency.swift index 4a65ab18787a2..21465847da717 100644 --- a/test/Concurrency/async_main_no_concurrency.swift +++ b/test/Concurrency/async_main_no_concurrency.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift -parse-as-library -disable-implicit-concurrency-module-import %s -// RUN: %target-typecheck-verify-swift -parse-as-library -enable-experimental-concurrency -parse-stdlib %s +// RUN: %target-typecheck-verify-swift -parse-as-library -disable-availability-checking -disable-implicit-concurrency-module-import %s +// RUN: %target-typecheck-verify-swift -parse-as-library -disable-availability-checking -parse-stdlib %s @main struct Main { // expected-error@+1:22{{'_Concurrency' module not imported, required for async main}} diff --git a/test/Concurrency/async_main_throws_prints_error.swift b/test/Concurrency/async_main_throws_prints_error.swift index cbd033edbd2e1..3226b46d7378e 100644 --- a/test/Concurrency/async_main_throws_prints_error.swift +++ b/test/Concurrency/async_main_throws_prints_error.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -Xfrontend -parse-as-library %s -o %t/main +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -Xfrontend -parse-as-library %s -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main > %t/log 2>&1 || true // RUN: %FileCheck %s < %t/log diff --git a/test/Concurrency/async_overload_filtering.swift b/test/Concurrency/async_overload_filtering.swift new file mode 100644 index 0000000000000..250d8191d152c --- /dev/null +++ b/test/Concurrency/async_overload_filtering.swift @@ -0,0 +1,25 @@ +// RUN: %target-typecheck-verify-swift -debug-constraints 2>%t.err +// RUN: %FileCheck %s < %t.err + +// rdar://77942193 - adding async overload leads to expressions becoming "too complex" + +struct Obj { + func op(_: T) {} + func op(_: Int) {} +} + +// Three overloads of `filter_async` to avoid generic overload optimization + +func filter_async(fn1: () -> T) -> T { fn1() } +func filter_async(fn2: () async -> T) -> T { fatalError() } +func filter_async(_: String) -> Void {} + +var a: String? = nil + +// CHECK: attempting disjunction choice $T0 bound to decl async_overload_filtering.(file).filter_async(fn2:) +// CHECK-NEXT: overload set choice binding $T0 := {{.*}} +// CHECK-NEXT: increasing score due to sync-in-asynchronous +// CHECK-NEXT: solution is worse than the best solution +filter_async { + Obj() +}.op("" + (a ?? "a")) diff --git a/test/Concurrency/async_sequence_syntax.swift b/test/Concurrency/async_sequence_syntax.swift index f5c8d5be58e80..2c0e5b1f44781 100644 --- a/test/Concurrency/async_sequence_syntax.swift +++ b/test/Concurrency/async_sequence_syntax.swift @@ -1,37 +1,37 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency // expected-note@+2{{add 'async' to function 'missingAsync' to make it asynchronous}} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func missingAsync(_ seq: T) throws { for try await _ in seq { } // expected-error{{'async' in a function that does not support concurrency}} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func missingThrows(_ seq: T) async { for try await _ in seq { } // expected-error{{error is not handled because the enclosing function is not declared 'throws'}} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func executeAsync(_ work: () async -> Void) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func execute(_ work: () -> Void) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func missingThrowingInBlock(_ seq: T) { executeAsync { // expected-error{{invalid conversion from throwing function of type '() async throws -> Void' to non-throwing function type '() async -> Void'}} for try await _ in seq { } } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func missingTryInBlock(_ seq: T) { executeAsync { for await _ in seq { } // expected-error{{call can throw, but the error is not handled}} } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func missingAsyncInBlock(_ seq: T) { execute { // expected-error{{cannot pass function of type '() async -> Void' to parameter expecting synchronous function type}} do { @@ -40,7 +40,7 @@ func missingAsyncInBlock(_ seq: T) { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func doubleDiagCheckGeneric(_ seq: T) async { var it = seq.makeAsyncIterator() // expected-note@+2{{call is to 'rethrows' function, but a conformance has a throwing witness}} @@ -48,7 +48,7 @@ func doubleDiagCheckGeneric(_ seq: T) async { let _ = await it.next() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct ThrowingAsyncSequence: AsyncSequence, AsyncIteratorProtocol { typealias Element = Int typealias AsyncIterator = Self @@ -59,7 +59,7 @@ struct ThrowingAsyncSequence: AsyncSequence, AsyncIteratorProtocol { func makeAsyncIterator() -> Self { return self } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func doubleDiagCheckConcrete(_ seq: ThrowingAsyncSequence) async { var it = seq.makeAsyncIterator() // expected-error@+1{{call can throw, but it is not marked with 'try' and the error is not handled}} @@ -67,7 +67,7 @@ func doubleDiagCheckConcrete(_ seq: ThrowingAsyncSequence) async { } // rdar://75274975 -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func forAwaitInsideDoCatch(_ source: Source) async { do { for try await item in source { @@ -76,7 +76,7 @@ func forAwaitInsideDoCatch(_ source: Source) async { } catch {} // no-warning } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func forAwaitWithConcreteType(_ seq: ThrowingAsyncSequence) throws { // expected-note {{add 'async' to function 'forAwaitWithConcreteType' to make it asynchronous}} for try await elt in seq { // expected-error {{'async' in a function that does not support concurrency}} _ = elt diff --git a/test/Concurrency/async_task_groups.swift b/test/Concurrency/async_task_groups.swift index 2cff4210a3aad..fc655edaedeb7 100644 --- a/test/Concurrency/async_task_groups.swift +++ b/test/Concurrency/async_task_groups.swift @@ -1,32 +1,32 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: executable_test // REQUIRES: concurrency // REQUIRES: libdispatch -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncFunc() async -> Int { 42 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncThrowsFunc() async throws -> Int { 42 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncThrowsOnCancel() async throws -> Int { // terrible suspend-spin-loop -- do not do this // only for purposes of demonstration while Task.isCancelled { - await Task.sleep(1_000_000_000) + try? await Task.sleep(nanoseconds: 1_000_000_000) } - throw Task.CancellationError() + throw CancellationError() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_add() async throws -> Int { try await withThrowingTaskGroup(of: Int.self) { group in - group.spawn { + group.addTask { await asyncFunc() } - group.spawn { + group.addTask { await asyncFunc() } @@ -42,18 +42,18 @@ func test_taskGroup_add() async throws -> Int { // MARK: Example group Usages struct Boom: Error {} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func work() async -> Int { 42 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func boom() async throws -> Int { throw Boom() } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func first_allMustSucceed() async throws { let first: Int = try await withThrowingTaskGroup(of: Int.self) { group in - group.spawn { await work() } - group.spawn { await work() } - group.spawn { try await boom() } + group.addTask { await work() } + group.addTask { await work() } + group.addTask { try await boom() } if let first = try await group.next() { return first @@ -66,15 +66,15 @@ func first_allMustSucceed() async throws { // Expected: re-thrown Boom } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func first_ignoreFailures() async throws { @Sendable func work() async -> Int { 42 } @Sendable func boom() async throws -> Int { throw Boom() } let first: Int = try await withThrowingTaskGroup(of: Int.self) { group in - group.spawn { await work() } - group.spawn { await work() } - group.spawn { + group.addTask { await work() } + group.addTask { await work() } + group.addTask { do { return try await boom() } catch { @@ -100,7 +100,7 @@ func first_ignoreFailures() async throws { // ==== ------------------------------------------------------------------------ // MARK: Advanced Custom Task Group Usage -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_taskGroup_quorum_thenCancel() async { // imitates a typical "gather quorum" routine that is typical in distributed systems programming enum Vote { @@ -121,7 +121,7 @@ func test_taskGroup_quorum_thenCancel() async { func gatherQuorum(followers: [Follower]) async -> Bool { try! await withThrowingTaskGroup(of: Vote.self) { group in for follower in followers { - group.spawn { try await follower.vote() } + group.addTask { try await follower.vote() } } defer { @@ -156,7 +156,7 @@ func test_taskGroup_quorum_thenCancel() async { // FIXME: this is a workaround since (A, B) today isn't inferred to be Sendable // and causes an error, but should be a warning (this year at least) -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct SendableTuple2: Sendable { let first: A let second: B @@ -167,7 +167,7 @@ struct SendableTuple2: Sendable { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension Collection where Self: Sendable, Element: Sendable, Self.Index: Sendable { /// Just another example of how one might use task groups. @@ -192,7 +192,7 @@ extension Collection where Self: Sendable, Element: Sendable, Self.Index: Sendab var submitted = 0 func submitNext() async throws { - group.spawn { [submitted,i] in + group.addTask { [submitted,i] in let value = try await transform(self[i]) return SendableTuple2(submitted, value) } diff --git a/test/Concurrency/async_tasks.swift b/test/Concurrency/async_tasks.swift index 7dc374d638b7d..806f24d5c48c2 100644 --- a/test/Concurrency/async_tasks.swift +++ b/test/Concurrency/async_tasks.swift @@ -1,11 +1,11 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func someAsyncFunc() async -> String { "" } struct MyError: Error {} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func someThrowingAsyncFunc() async throws -> String { throw MyError() } // ==== Unsafe Continuations --------------------------------------------------- @@ -27,7 +27,7 @@ func buyVegetables( ) {} // returns 1 or more vegetables or throws an error -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func buyVegetables(shoppingList: [String]) async throws -> [Vegetable] { try await withUnsafeThrowingContinuation { continuation in var veggies: [Vegetable] = [] @@ -43,7 +43,7 @@ func buyVegetables(shoppingList: [String]) async throws -> [Vegetable] { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_unsafeContinuations() async { // the closure should not allow async operations; // after all: if you have async code, just call it directly, without the unsafe continuation @@ -64,7 +64,7 @@ func test_unsafeContinuations() async { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_unsafeThrowingContinuations() async throws { let _: String = try await withUnsafeThrowingContinuation { continuation in continuation.resume(returning: "") @@ -89,24 +89,24 @@ func test_unsafeThrowingContinuations() async throws { // ==== Detached Tasks --------------------------------------------------------- -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_detached() async throws { - let handle = detach() { + let handle = Task.detached { await someAsyncFunc() // able to call async functions } - let result: String = await handle.get() + let result: String = await handle.value _ = result } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_detached_throwing() async -> String { - let handle: Task.Handle = detach() { + let handle: Task = Task.detached { try await someThrowingAsyncFunc() // able to call async functions } do { - return try await handle.get() + return try await handle.value } catch { print("caught: \(error)") } diff --git a/test/Concurrency/async_throwing.swift b/test/Concurrency/async_throwing.swift index 2879af1dbd477..e0b8e6bcc8340 100644 --- a/test/Concurrency/async_throwing.swift +++ b/test/Concurrency/async_throwing.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency // These tests cover various interactions with async functions that are diff --git a/test/Concurrency/await_typo_correction.swift b/test/Concurrency/await_typo_correction.swift index 44f25dcd10255..ddfbca31aa089 100644 --- a/test/Concurrency/await_typo_correction.swift +++ b/test/Concurrency/await_typo_correction.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -parse-as-library +// RUN: %target-typecheck-verify-swift -disable-availability-checking -parse-as-library // REQUIRES: concurrency func asyncFunc() async throws {} @@ -21,9 +21,8 @@ func async() throws { } // expected-error@+1 {{found 'async' in expression; did you mean 'await'?}}{{13-18=await}} let _ = async anotherAsyncFunc() - // Don't emit a diagnostic here related to 'await' + // Don't emit a diagnostic here async let foo = anotherAsyncFunc() - // expected-warning@-1{{'async let' is now 'spawn let'}}{{5-10=spawn}} let _ = await foo // I question the name choice, but it's valid diff --git a/test/Concurrency/builtin_silgen.swift b/test/Concurrency/builtin_silgen.swift new file mode 100644 index 0000000000000..1e680bb62eda4 --- /dev/null +++ b/test/Concurrency/builtin_silgen.swift @@ -0,0 +1,25 @@ +// RUN: %target-swift-frontend -disable-availability-checking %s -parse-as-library -parse-stdlib -emit-sil -o - | %FileCheck %s + +// REQUIRES: concurrency + +import _Concurrency + +@MainActor +func suspend() async {} + +// Builtin.hopToActor should generate a mandator hop_to_executor +// before releaseing the actor and reaching a suspend. +// +// CHECK-LABEL: sil private @$s14builtin_silgen11runDetachedyyFyyYaYbcfU_ : $@convention(thin) @Sendable @async () -> () { +// CHECK: [[ACTOR:%.*]] = apply %1(%0) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK: hop_to_executor [mandatory] [[ACTOR]] : $MainActor +// CHECK: strong_release [[ACTOR]] : $MainActor +// CHECK: apply %{{.*}}() : $@convention(thin) @async () -> () +// CHECK-LABEL: } // end sil function '$sIeghH_ytIeghHr_TR' +@available(SwiftStdlib 5.5, *) +func runDetached() { + Task.detached { + Builtin.hopToActor(MainActor.shared) + await suspend() + } +} diff --git a/test/Concurrency/closure_isolation.swift b/test/Concurrency/closure_isolation.swift index 92e1847477e4e..7fad9a98f5a37 100644 --- a/test/Concurrency/closure_isolation.swift +++ b/test/Concurrency/closure_isolation.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -dump-ast %s -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-frontend -dump-ast %s -disable-availability-checking | %FileCheck %s // REQUIRES: concurrency func acceptClosure(_: () -> T) { } diff --git a/test/Concurrency/concurrency_availability.swift b/test/Concurrency/concurrency_availability.swift new file mode 100644 index 0000000000000..cc54aadeadc72 --- /dev/null +++ b/test/Concurrency/concurrency_availability.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -parse-stdlib -target x86_64-apple-macosx10.14 -typecheck -verify %s +// RUN: %target-swift-frontend -parse-stdlib -target x86_64-apple-macosx11 -typecheck %s +// REQUIRES: OS=macosx + +func f() async { } // expected-error{{concurrency is only available in}} +// expected-note@-1{{add @available}} + +actor A { } // expected-error{{concurrency is only available in}} +// expected-note@-1{{add @available}} + +// Allow this without any availability for Historical Reasons. +public func swift_deletedAsyncMethodError() async { +} diff --git a/test/Concurrency/concurrency_module_shadowing.swift b/test/Concurrency/concurrency_module_shadowing.swift index e487865795458..4b8f356bf518d 100644 --- a/test/Concurrency/concurrency_module_shadowing.swift +++ b/test/Concurrency/concurrency_module_shadowing.swift @@ -1,15 +1,15 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -emit-module-path %t/ShadowsConcur.swiftmodule -module-name ShadowsConcur %S/Inputs/ShadowsConcur.swift -// RUN: %target-typecheck-verify-swift -I %t -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -I %t -disable-availability-checking // REQUIRES: concurrency import ShadowsConcur -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -func f(_ t : Task) -> Bool { +@available(SwiftStdlib 5.5, *) +func f(_ t : UnsafeCurrentTask) -> Bool { return t.someProperty == "123" } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -func g(_: _Concurrency.Task) {} +@available(SwiftStdlib 5.5, *) +func g(_: _Concurrency.UnsafeCurrentTask) {} diff --git a/test/Concurrency/concurrency_warnings.swift b/test/Concurrency/concurrency_warnings.swift index a7d900c2dde3f..cb0b40923a5d8 100644 --- a/test/Concurrency/concurrency_warnings.swift +++ b/test/Concurrency/concurrency_warnings.swift @@ -8,7 +8,7 @@ class GlobalCounter { let rs = GlobalCounter() var globalInt = 17 // expected-note 2{{var declared here}} -class MyError: Error { // expected-warning{{non-final class 'MyError' cannot conform to `Sendable`; use `UnsafeSendable`}} +class MyError: Error { // expected-warning{{non-final class 'MyError' cannot conform to `Sendable`; use `@unchecked Sendable`}} var storage = 0 // expected-warning{{stored property 'storage' of 'Sendable'-conforming class 'MyError' is mutable}} } diff --git a/test/Concurrency/concurrent_value_checking.swift b/test/Concurrency/concurrent_value_checking.swift index aa7a3ee0858a5..c0038ff7018ba 100644 --- a/test/Concurrency/concurrent_value_checking.swift +++ b/test/Concurrency/concurrent_value_checking.swift @@ -1,7 +1,7 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -warn-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency // REQUIRES: concurrency -class NotConcurrent { } +class NotConcurrent { } // expected-note 17{{class 'NotConcurrent' does not conform to the `Sendable` protocol}} // ---------------------------------------------------------------------- // Sendable restriction on actor operations @@ -37,7 +37,7 @@ extension A1 { _ = await self.asynchronous(nil) // Across to a different actor, so Sendable restriction is enforced. - _ = await other.localLet // expected-warning{{cannot use property 'localLet' with a non-sendable type 'NotConcurrent' across actors}} + _ = other.localLet // expected-warning{{cannot use property 'localLet' with a non-sendable type 'NotConcurrent' across actors}} _ = await other.synchronous() // expected-warning{{cannot call function returning non-sendable type 'NotConcurrent?' across actors}} _ = await other.asynchronous(nil) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}} } @@ -67,7 +67,7 @@ func globalAsync(_: NotConcurrent?) async { } func globalTest() async { - let a = await globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}} + let a = globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}} await globalAsync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}} await globalSync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}} } @@ -77,7 +77,7 @@ struct HasSubscript { subscript (i: Int) -> NotConcurrent? { nil } } -class ClassWithGlobalActorInits { +class ClassWithGlobalActorInits { // expected-note 2{{class 'ClassWithGlobalActorInits' does not conform to the `Sendable` protocol}} @SomeGlobalActor init(_: NotConcurrent) { } @@ -88,10 +88,11 @@ class ClassWithGlobalActorInits { @MainActor func globalTestMain(nc: NotConcurrent) async { - let a = await globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}} + let a = globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}} await globalAsync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}} await globalSync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}} _ = await ClassWithGlobalActorInits(nc) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent' across actors}} + // expected-warning@-1{{cannot call function returning non-sendable type 'ClassWithGlobalActorInits' across actors}} _ = await ClassWithGlobalActorInits() // expected-warning{{cannot call function returning non-sendable type 'ClassWithGlobalActorInits' across actors}} } @@ -143,7 +144,7 @@ func testUnsafeSendableInAsync() async { // ---------------------------------------------------------------------- // Sendable restriction on key paths. // ---------------------------------------------------------------------- -class NC: Hashable { +class NC: Hashable { // expected-note 3{{class 'NC' does not conform to the `Sendable` protocol}} func hash(into: inout Hasher) { } static func==(_: NC, _: NC) -> Bool { true } } @@ -156,6 +157,14 @@ func testKeyPaths(dict: [NC: Int], nc: NC) { _ = \HasNC.dict[nc] // expected-warning{{cannot form key path that captures non-sendable type 'NC'}} } +// ---------------------------------------------------------------------- +// Sendable restriction on nonisolated declarations. +// ---------------------------------------------------------------------- +actor ANI { + // FIXME: improve diagnostics to talk about nonisolated + nonisolated let nc = NC() // expected-warning{{cannot use property 'nc' with a non-sendable type 'NC' across actors}} + nonisolated func f() -> NC? { nil } // expected-warning{{cannot call function returning non-sendable type 'NC?' across actors}} +} // ---------------------------------------------------------------------- // Sendable restriction on conformances. @@ -245,11 +254,11 @@ final class C2: Sendable { class C3 { } -class C4: C3, UnsafeSendable { // expected-warning{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}} +class C4: C3, @unchecked Sendable { var y: Int = 0 // okay } -class C5: UnsafeSendable { // expected-warning{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}} +class C5: @unchecked Sendable { var x: Int = 0 // okay } @@ -259,20 +268,39 @@ class C6: C5 { final class C7: Sendable { } -class C9: Sendable { } // expected-error{{non-final class 'C9' cannot conform to `Sendable`; use `UnsafeSendable`}} +class C9: Sendable { } // expected-error{{non-final class 'C9' cannot conform to `Sendable`; use `@unchecked Sendable`}} // ---------------------------------------------------------------------- -// UnsafeSendable disabling checking +// @unchecked Sendable disabling checking // ---------------------------------------------------------------------- -struct S11: UnsafeSendable { // expected-warning{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}} +struct S11: @unchecked Sendable { var nc: NotConcurrent // okay } -struct S12: UnsafeSendable { // expected-warning{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}} +struct S12: @unchecked Sendable { var nc: T // okay } -enum E11: UnsafeSendable { // expected-warning{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}} +enum E11: @unchecked Sendable { + case payload(NotConcurrent) // okay + case other(T) // okay +} + +class C11 { } + +class C12: @unchecked C11 { } // expected-error{{'unchecked' attribute cannot apply to non-protocol type 'C11'}} + +protocol P { } + +protocol Q: @unchecked Sendable { } // expected-error{{'unchecked' attribute only applies in inheritance clauses}} + +typealias TypeAlias1 = @unchecked P // expected-error{{'unchecked' attribute only applies in inheritance clauses}} + + +// ---------------------------------------------------------------------- +// UnsafeSendable historical name +// ---------------------------------------------------------------------- +enum E12: UnsafeSendable { // expected-warning{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}} case payload(NotConcurrent) // okay case other(T) // okay } diff --git a/test/Concurrency/concurrent_value_checking_objc.swift b/test/Concurrency/concurrent_value_checking_objc.swift index c25c32b849d62..f002846e083e2 100644 --- a/test/Concurrency/concurrent_value_checking_objc.swift +++ b/test/Concurrency/concurrent_value_checking_objc.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency // REQUIRES: concurrency // REQUIRES: objc_interop @@ -13,7 +13,7 @@ final class B: NSObject, Sendable { var x: Int = 5 // expected-error{{stored property 'x' of 'Sendable'-conforming class 'B' is mutable}} } -class C { } +class C { } // expected-note{{class 'C' does not conform to the `Sendable` protocol}} final class D: NSObject, Sendable { let c: C = C() // expected-error{{stored property 'c' of 'Sendable'-conforming class 'D' has non-sendable type 'C'}} diff --git a/test/Concurrency/concurrent_value_inference.swift b/test/Concurrency/concurrent_value_inference.swift index a65a01519921a..75ed7d6313f17 100644 --- a/test/Concurrency/concurrent_value_inference.swift +++ b/test/Concurrency/concurrent_value_inference.swift @@ -1,6 +1,7 @@ -// RUN: %target-typecheck-verify-swift -enable-library-evolution +// RUN: %target-typecheck-verify-swift -enable-library-evolution -warn-concurrency +// REQUIRES: concurrency -class C1 { } +class C1 { } // expected-note{{class 'C1' does not conform to the `Sendable` protocol}} final class C2: Sendable { } struct S1 { @@ -9,7 +10,7 @@ struct S1 { var c: C2 } -enum E1 { +enum E1 { // expected-note{{consider making enum 'E1' conform to the 'Sendable' protocol}}{{9-9=: Sendable }} case base indirect case nested(E1) } @@ -21,19 +22,18 @@ enum E2 { struct GS1 { } -struct GS2 { +struct GS2 { // expected-note{{consider making generic struct 'GS2' conform to the 'Sendable' protocol}} var storage: T } func acceptCV(_: T) { } -// expected-note@-1 6{{where 'T' =}} // Example that was triggering circular dependencies. struct Signature { } struct Data { } struct BlockInfo { } -struct Bitcode { +struct Bitcode { // expected-note{{consider making struct 'Bitcode' conform to the 'Sendable' protocol}} let signature: Signature let elements: [BitcodeElement] let blockInfo: [UInt64: BlockInfo] @@ -64,11 +64,11 @@ enum BitcodeElement { // Public structs and enums do not get implicit Sendable unless they // are frozen. -public struct PublicStruct { +public struct PublicStruct { // expected-note{{consider making struct 'PublicStruct' conform to the 'Sendable' protocol}} var i: Int } -public enum PublicEnum { +public enum PublicEnum { // expected-note{{consider making enum 'PublicEnum' conform to the 'Sendable' protocol}} case some } @@ -85,26 +85,40 @@ struct HasFunctions { var cfp: @convention(c) () -> Void } +@available(SwiftStdlib 5.5, *) +@globalActor +actor MyGlobalActor { + static let shared = MyGlobalActor() +} + +@MyGlobalActor +class C3 { } + +class C4: C3 { } + func testCV( - c1: C1, c2: C2, s1: S1, e1: E1, e2: E2, gs1: GS1, gs2: GS2, + c1: C1, c2: C2, c3: C3, c4: C4, s1: S1, e1: E1, e2: E2, + gs1: GS1, gs2: GS2, bc: Bitcode, ps: PublicStruct, pe: PublicEnum, fps: FrozenPublicStruct, fpe: FrozenPublicEnum, hf: HasFunctions ) { - acceptCV(c1) // expected-error{{'C1' conform to 'Sendable'}} + acceptCV(c1) // expected-warning{{type 'C1' does not conform to the 'Sendable' protocol}} acceptCV(c2) + acceptCV(c3) + acceptCV(c4) acceptCV(s1) - acceptCV(e1) // expected-error{{'E1' conform to 'Sendable'}} + acceptCV(e1) // expected-warning{{type 'E1' does not conform to the 'Sendable'}} acceptCV(e2) acceptCV(gs1) - acceptCV(gs2) // expected-error{{'GS2' conform to 'Sendable'}} + acceptCV(gs2) // expected-warning{{type 'GS2' does not conform to the 'Sendable' protocol}} // Not available due to recursive conformance dependencies. - acceptCV(bc) // expected-error{{global function 'acceptCV' requires that 'Bitcode' conform to 'Sendable'}} + acceptCV(bc) // expected-warning{{type 'Bitcode' does not conform to the 'Sendable' protocol}} // Not available due to "public". - acceptCV(ps) // expected-error{{global function 'acceptCV' requires that 'PublicStruct' conform to 'Sendable'}} - acceptCV(pe) // expected-error{{global function 'acceptCV' requires that 'PublicEnum' conform to 'Sendable'}} + acceptCV(ps) // expected-warning{{type 'PublicStruct' does not conform to the 'Sendable' protocol}} + acceptCV(pe) // expected-warning{{type 'PublicEnum' does not conform to the 'Sendable' protocol}} // Public is okay when also @frozen. acceptCV(fps) diff --git a/test/Concurrency/concurrentfunction_capturediagnostics.swift b/test/Concurrency/concurrentfunction_capturediagnostics.swift index 11e6201e4b143..0adcbfae8f63e 100644 --- a/test/Concurrency/concurrentfunction_capturediagnostics.swift +++ b/test/Concurrency/concurrentfunction_capturediagnostics.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-experimental-flow-sensitive-concurrent-captures -verify -emit-sil %s -o - >/dev/null +// RUN: %target-swift-frontend -disable-availability-checking -enable-experimental-flow-sensitive-concurrent-captures -verify -emit-sil %s -o - >/dev/null // REQUIRES: concurrency diff --git a/test/Concurrency/default_actor_definit.swift b/test/Concurrency/default_actor_definit.swift index e2ac037a703c6..229c7db8e52e8 100644 --- a/test/Concurrency/default_actor_definit.swift +++ b/test/Concurrency/default_actor_definit.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil %s -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil %s -disable-availability-checking | %FileCheck %s // REQUIRES: concurrency actor A { diff --git a/test/Concurrency/dispatch_inference.swift b/test/Concurrency/dispatch_inference.swift index aa89d7b430f77..6beb09c35eb21 100644 --- a/test/Concurrency/dispatch_inference.swift +++ b/test/Concurrency/dispatch_inference.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency %import-libdispatch -warn-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking %import-libdispatch -warn-concurrency // REQUIRES: concurrency // REQUIRES: libdispatch diff --git a/test/Concurrency/fail_implicit_concurrency_load.swift b/test/Concurrency/fail_implicit_concurrency_load.swift index 1b0385ce3e3db..1b3f99418e298 100644 --- a/test/Concurrency/fail_implicit_concurrency_load.swift +++ b/test/Concurrency/fail_implicit_concurrency_load.swift @@ -16,6 +16,6 @@ // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json -// RUN: %target-swift-frontend -typecheck %s -explicit-swift-module-map-file %t/inputs/map.json -disable-implicit-swift-modules -enable-experimental-concurrency 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -typecheck %s -explicit-swift-module-map-file %t/inputs/map.json -disable-implicit-swift-modules -disable-availability-checking 2>&1 | %FileCheck %s import Swift // CHECK: warning: unable to perform implicit import of "_Concurrency" module: no such module found diff --git a/test/Concurrency/global_actor_from_ordinary_context.swift b/test/Concurrency/global_actor_from_ordinary_context.swift index 6fcc2245225e9..f4300cd5a2ee5 100644 --- a/test/Concurrency/global_actor_from_ordinary_context.swift +++ b/test/Concurrency/global_actor_from_ordinary_context.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency // provides coverage for rdar://71548470 @@ -31,8 +31,7 @@ actor Alex { func referenceGlobalActor() async { let a = Alex() _ = a.method - // expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} - _ = a.const_memb // expected-note{{property access is 'async'}} + _ = a.const_memb // expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} _ = a.mut_memb // expected-note{{property access is 'async'}} @@ -117,14 +116,12 @@ func fromAsync() async { y() // expected-note{{call is 'async'}} let a = Alex() - let fn = a.method - // expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }} - fn() //expected-note{{calls to let 'fn' from outside of its actor context are implicitly asynchronous}} - // expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} - _ = a.const_memb // expected-note{{property access is 'async'}} - // expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} - _ = a.mut_memb // expected-note{{property access is 'async'}} + fn() // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{calls to let 'fn' from outside of its actor context are implicitly asynchronous}} + _ = a.const_memb + _ = a.mut_memb // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{property access is 'async'}} // expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} _ = a[1] // expected-note{{subscript access is 'async'}} diff --git a/test/Concurrency/global_actor_function_types.swift b/test/Concurrency/global_actor_function_types.swift index bf7286aa4129e..a42cfad34a65e 100644 --- a/test/Concurrency/global_actor_function_types.swift +++ b/test/Concurrency/global_actor_function_types.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency actor SomeActor { } @@ -33,7 +33,7 @@ func someSlowOperation() async -> Int { 5 } func acceptOnSomeGlobalActor(_: @SomeGlobalActor () -> T) { } -func testClosures() async { +func testClosures(i: Int) async { // Global actors on synchronous closures become part of the type let cl1 = { @SomeGlobalActor in onSomeGlobalActor() @@ -46,6 +46,10 @@ func testClosures() async { } let _: Double = cl2 // expected-error{{cannot convert value of type '() async -> Int' to specified type 'Double'}} + let cl3 = { @SomeGlobalActor [i] in + print(i + onSomeGlobalActor()) + } + // okay to be explicit acceptOnSomeGlobalActor { @SomeGlobalActor in onSomeGlobalActor() @@ -142,3 +146,10 @@ func testTypesConcurrencyContext() async { _ = await f1() _ = await f2() } + +// Conversion from main-actor-qualified synchronous to unqualified asynchronous. +func test() { + let _: () async -> Int = { @SomeGlobalActor in + onSomeGlobalActor() + } +} diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift index 7892c0acd672d..84de2be68f223 100644 --- a/test/Concurrency/global_actor_inference.swift +++ b/test/Concurrency/global_actor_inference.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -enable-experimental-async-handler +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency actor SomeActor { } @@ -165,7 +165,7 @@ class C6: P2, P3 { // Global actor checking for overrides // ---------------------------------------------------------------------- actor GenericSuper { - @GenericGlobalActor func method() { } + @GenericGlobalActor func method() { } // expected-note {{overridden declaration is here}} @GenericGlobalActor func method2() { } // expected-note {{overridden declaration is here}} @GenericGlobalActor func method3() { } // expected-note {{overridden declaration is here}} @@ -175,13 +175,14 @@ actor GenericSuper { actor GenericSub : GenericSuper<[T]> { // expected-error{{actor types do not support inheritance}} override func method() { } // expected-note {{calls to instance method 'method()' from outside of its actor context are implicitly asynchronous}} + // expected-error@-1{{instance method overrides a 'final' instance method}} - @GenericGlobalActor override func method2() { } // expected-error{{global actor 'GenericGlobalActor'-isolated instance method 'method2()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}} - nonisolated override func method3() { } // expected-error{{nonisolated instance method 'method3()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}} + @GenericGlobalActor override func method2() { } // expected-error{{instance method overrides a 'final' instance method}} + nonisolated override func method3() { } // expected-error{{instance method overrides a 'final' instance method}} @OtherGlobalActor func testMethod() { - method() // expected-error{{call to global actor 'GenericGlobalActor<[T]>'-isolated instance method 'method()' in a synchronous global actor 'OtherGlobalActor'-isolated context}} - _ = method + method() // expected-error{{actor-isolated instance method 'method()' can not be referenced from global actor 'OtherGlobalActor'}} + _ = method // expected-error{{actor-isolated instance method 'method()' can not be partially applied}} } } @@ -194,9 +195,10 @@ struct Container { } struct OtherContainer { - // Okay to change the global actor in a subclass. + // NOT Okay to change the global actor in a subclass. @GenericGlobalActor<[U]> class Subclass1 : Container<[U]>.Superclass { } @GenericGlobalActor class Subclass2 : Container<[U]>.Superclass { } + // expected-error@-1{{global actor 'GenericGlobalActor'-isolated class 'Subclass2' has different actor isolation from global actor 'GenericGlobalActor'-isolated superclass 'Superclass'}} // Ensure that substitutions work properly when inheriting. class Subclass3 : Container<(U, V)>.Superclass2 { @@ -217,7 +219,7 @@ class SuperclassWithGlobalActors { func j() { } } -@GenericGlobalActor +@GenericGlobalActor // it's okay to add a global actor to nonisolated class SubclassWithGlobalActors : SuperclassWithGlobalActors { override func f() { } // okay: inferred to @GenericGlobalActor @@ -227,19 +229,6 @@ class SubclassWithGlobalActors : SuperclassWithGlobalActors { func onGenericGlobalActorString() { } @GenericGlobalActor func onGenericGlobalActorInt() { } - - @asyncHandler @GenericGlobalActor - override func i() { // okay to differ from superclass because it's an asyncHandler. - onGenericGlobalActorString() - } - - @asyncHandler - override func j() { // okay, isolated to GenericGlobalActor - onGenericGlobalActorString() // okay - - // expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{5-5=await }} - onGenericGlobalActorInt() // expected-note{{calls to instance method 'onGenericGlobalActorInt()' from outside of its actor context are implicitly asynchronous}} - } } // ---------------------------------------------------------------------- @@ -267,21 +256,21 @@ func barSync() { @propertyWrapper @OtherGlobalActor -struct WrapperOnActor { - @actorIndependent(unsafe) private var stored: Wrapped +struct WrapperOnActor { + private var stored: Wrapped nonisolated init(wrappedValue: Wrapped) { stored = wrappedValue } @MainActor var wrappedValue: Wrapped { - get { stored } - set { stored = newValue } + get { } + set { } } @SomeGlobalActor var projectedValue: Wrapped { - get { stored } - set { stored = newValue } + get { } + set { } } } @@ -297,21 +286,21 @@ public struct WrapperOnMainActor { } @propertyWrapper -actor WrapperActor { - @actorIndependent(unsafe) var storage: Wrapped +actor WrapperActor { + var storage: Wrapped init(wrappedValue: Wrapped) { storage = wrappedValue } nonisolated var wrappedValue: Wrapped { - get { storage } - set { storage = newValue } + get { } + set { } } nonisolated var projectedValue: Wrapped { - get { storage } - set { storage = newValue } + get { } + set { } } } @@ -356,24 +345,31 @@ actor WrapperActorBad1 { } @propertyWrapper -actor WrapperActorBad2 { - @actorIndependent(unsafe) var storage: Wrapped +actor WrapperActorBad2 { + var storage: Wrapped init(wrappedValue: Wrapped) { storage = wrappedValue } nonisolated var wrappedValue: Wrapped { - get { storage } - set { storage = newValue } + get { } + set { } } var projectedValue: Wrapped { // expected-error{{'projectedValue' property in property wrapper type 'WrapperActorBad2' cannot be isolated to the actor instance; consider 'nonisolated'}} - get { storage } - set { storage = newValue } + get { } + set { } } } +@propertyWrapper +struct WrapperWithMainActorDefaultInit { + var wrappedValue: Int { fatalError() } + + @MainActor init() {} // expected-note {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}} +} + actor ActorWithWrapper { @WrapperOnActor var synced: Int = 0 // expected-note@-1 3{{property declared here}} @@ -381,12 +377,14 @@ actor ActorWithWrapper { _ = synced // expected-error{{'synced' isolated to global actor}} _ = $synced // expected-error{{'$synced' isolated to global actor}} _ = _synced // expected-error{{'_synced' isolated to global actor}} + + @WrapperWithMainActorDefaultInit var value: Int // expected-error {{call to main actor-isolated initializer 'init()' in a synchronous actor-isolated context}} } } @propertyWrapper -struct WrapperOnSomeGlobalActor { - @actorIndependent(unsafe) private var stored: Wrapped +struct WrapperOnSomeGlobalActor { + private var stored: Wrapped nonisolated init(wrappedValue: Wrapped) { stored = wrappedValue @@ -454,20 +452,20 @@ class UGASubclass2: UGAClass { @propertyWrapper @OtherGlobalActor(unsafe) struct WrapperOnUnsafeActor { - @actorIndependent(unsafe) private var stored: Wrapped + private var stored: Wrapped init(wrappedValue: Wrapped) { stored = wrappedValue } @MainActor(unsafe) var wrappedValue: Wrapped { - get { stored } - set { stored = newValue } + get { } + set { } } @SomeGlobalActor(unsafe) var projectedValue: Wrapped { - get { stored } - set { stored = newValue } + get { } + set { } } } @@ -494,14 +492,13 @@ struct HasWrapperOnUnsafeActor { } // ---------------------------------------------------------------------- -// Actor-independent closures +// Nonisolated closures // ---------------------------------------------------------------------- -@SomeGlobalActor func getGlobal7() -> Int { 7 } // expected-note{{calls to global function 'getGlobal7()' from outside of its actor context are implicitly asynchronous}} +@SomeGlobalActor func getGlobal7() -> Int { 7 } func acceptClosure(_: () -> T) { } @SomeGlobalActor func someGlobalActorFunc() async { acceptClosure { getGlobal7() } // okay - acceptClosure { @actorIndependent in getGlobal7() } // expected-error{{call to global actor 'SomeGlobalActor'-isolated global function 'getGlobal7()' in a synchronous nonisolated context}} } // ---------------------------------------------------------------------- @@ -541,3 +538,17 @@ func acceptAsyncSendableClosureInheriting(@_inheritActorContext _: @Sendable await onlyOnMainActor() // expected-warning{{no 'async' operations occur within 'await' expression}} } } + + +// defer bodies inherit global actor-ness +@MainActor +var statefulThingy: Bool = false + +@MainActor +func useFooInADefer() -> String { + defer { + statefulThingy = true + } + + return "hello" +} diff --git a/test/Concurrency/hop_to_executor_unreachable_code.swift b/test/Concurrency/hop_to_executor_unreachable_code.swift new file mode 100644 index 0000000000000..8f1bf6179fab4 --- /dev/null +++ b/test/Concurrency/hop_to_executor_unreachable_code.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-frontend -disable-availability-checking -emit-sil -verify %s + +// REQUIRES: concurrency + +// Check that the inserted hop-to-executor instructions don't cause a false +// "unreachable code" warning. + +@MainActor +func bye() -> Never { + print("bye") + fatalError() +} + +func testit() async { + await bye() +} + diff --git a/test/Concurrency/isolated_parameters.swift b/test/Concurrency/isolated_parameters.swift new file mode 100644 index 0000000000000..857cc5b9fbaa0 --- /dev/null +++ b/test/Concurrency/isolated_parameters.swift @@ -0,0 +1,104 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency +// REQUIRES: concurrency + +@available(SwiftStdlib 5.5, *) +actor A { + func f() { } +} + +@available(SwiftStdlib 5.5, *) +extension Actor { + func g() { } +} + +@available(SwiftStdlib 5.5, *) +func testA( + a: isolated A, + b: isolated T, + c: isolated Int // expected-error{{'isolated' parameter has non-actor type 'Int'}} +) { + a.f() + a.g() + b.g() +} + +@available(SwiftStdlib 5.5, *) +typealias Fn = (isolated A) -> Void + +@available(SwiftStdlib 5.5, *) +func globalFunc(_ a: A) { } + +@available(SwiftStdlib 5.5, *) +func globalFuncIsolated(_: isolated A) { // expected-note{{calls to global function 'globalFuncIsolated' from outside of its actor context are implicitly asynchronous}} + let _: Int = globalFuncIsolated // expected-error{{cannot convert value of type '(isolated A) -> ()' to specified type 'Int'}} + let _: (A) -> Void = globalFuncIsolated // expected-error{{cannot convert value of type '(isolated A) -> ()' to specified type '(A) -> Void'}} + let _: Fn = globalFunc // okay +} + +@available(SwiftStdlib 5.5, *) +func testIsolatedParamCalls(a: isolated A, b: A) { + globalFunc(a) + globalFunc(b) + + globalFuncIsolated(a) + globalFuncIsolated(b) // expected-error{{call to actor-isolated global function 'globalFuncIsolated' in a synchronous nonisolated context}} +} + +@available(SwiftStdlib 5.5, *) +func testIsolatedParamCallsAsync(a: isolated A, b: A) async { + globalFunc(a) + globalFunc(b) + + globalFuncIsolated(a) + globalFuncIsolated(b) // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{calls to global function 'globalFuncIsolated' from outside of its actor context are implicitly asynchronous}} + await globalFuncIsolated(b) +} + +actor MyActor { + func hello() {} +} + +typealias MyFn = (isolated: Int) -> Void // expected-error {{function types cannot have argument labels; use '_' before 'isolated'}} +typealias MyFnFixed = (_: isolated MyActor) -> Void + +func standalone(_: isolated MyActor) {} +func check() { + let _: MyFnFixed = standalone + let _: MyFnFixed = { (_: isolated MyActor) in () } +} + + +@available(SwiftStdlib 5.5, *) +protocol P { + func f(isolated: MyActor) async + func g(isolated x: MyActor) async + func h(isolated MyActor: isolated MyActor) + func i(isolated: isolated MyActor) + func j(isolated: Int) -> Int + func k(isolated y: Int) -> Int + func l(isolated _: Int) -> Int +} + +@available(SwiftStdlib 5.5, *) +struct S: P { + func f(isolated: MyActor) async { await isolated.hello() } + func g(isolated x: MyActor) async { await x.hello() } + func h(isolated MyActor: isolated MyActor) { i(isolated: MyActor) } + func i(isolated: isolated MyActor) { isolated.hello() } + func j(isolated: Int) -> Int { return isolated } + func k(isolated y: Int) -> Int { return j(isolated: y) } + func l(isolated _: Int) -> Int { return k(isolated: 0) } +} + + +// Redeclaration checking +actor TestActor { + func test() { // expected-note{{'test()' previously declared here}} + } + nonisolated func test() { // expected-error{{invalid redeclaration of 'test()'}} + } +} + +func redecl(_: TestActor) { } // expected-note{{'redecl' previously declared here}} +func redecl(_: isolated TestActor) { } // expected-error{{invalid redeclaration of 'redecl'}} diff --git a/test/Concurrency/objc_async_overload.swift b/test/Concurrency/objc_async_overload.swift index 953fe02d9fd8e..634b90e6d4539 100644 --- a/test/Concurrency/objc_async_overload.swift +++ b/test/Concurrency/objc_async_overload.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-concurrency -typecheck -verify -import-objc-header %S/Inputs/Delegate.h %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-availability-checking -typecheck -verify -import-objc-header %S/Inputs/Delegate.h %s // REQUIRES: concurrency // REQUIRES: objc_interop diff --git a/test/Concurrency/objc_async_protocol_irgen.swift b/test/Concurrency/objc_async_protocol_irgen.swift index e47ce81649fcb..a09b6a0a775ef 100644 --- a/test/Concurrency/objc_async_protocol_irgen.swift +++ b/test/Concurrency/objc_async_protocol_irgen.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-concurrency -import-objc-header %S/Inputs/Delegate.h %s -emit-ir -o - | %FileCheck %s -DALIGNMENT=%target-alignment +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-availability-checking -import-objc-header %S/Inputs/Delegate.h %s -emit-ir -o - | %FileCheck %s -DALIGNMENT=%target-alignment // REQUIRES: concurrency // REQUIRES: objc_interop diff --git a/test/Concurrency/reasync.swift b/test/Concurrency/reasync.swift index 9837deeaed785..702f4524b142c 100644 --- a/test/Concurrency/reasync.swift +++ b/test/Concurrency/reasync.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -disable-availability-checking // REQUIRES: concurrency //// Basic definitions and parsing @@ -149,11 +149,11 @@ func callReasyncWithAutoclosure3() { reasyncWithAutoclosure2("Hello \(world)") } -//// spawn let +//// async let func callReasyncWithAutoclosure4(_: () async -> ()) reasync { await reasyncFunction { - spawn let x = 123 + async let x = 123 _ = await x } diff --git a/test/Concurrency/reasync_protocol.swift b/test/Concurrency/reasync_protocol.swift index 19ae9f484b478..11bed077da397 100644 --- a/test/Concurrency/reasync_protocol.swift +++ b/test/Concurrency/reasync_protocol.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -disable-availability-checking // REQUIRES: concurrency @reasync protocol ReasyncProtocol {} diff --git a/test/Concurrency/reasync_ranking.swift b/test/Concurrency/reasync_ranking.swift index d805a54c47aae..02e1639ac7c31 100644 --- a/test/Concurrency/reasync_ranking.swift +++ b/test/Concurrency/reasync_ranking.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -disable-availability-checking // REQUIRES: concurrency // We don't want 'reasync' overloads to have a higher score in the diff --git a/test/Concurrency/sendable_cycle.swift b/test/Concurrency/sendable_cycle.swift index ca591fac3e409..f3418946cdd51 100644 --- a/test/Concurrency/sendable_cycle.swift +++ b/test/Concurrency/sendable_cycle.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift %S/Inputs/sendable_cycle_other.swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift %S/Inputs/sendable_cycle_other.swift -disable-availability-checking // REQUIRES: concurrency struct Bar { diff --git a/test/Concurrency/sendable_module_checking.swift b/test/Concurrency/sendable_module_checking.swift new file mode 100644 index 0000000000000..a4e6f82e2c785 --- /dev/null +++ b/test/Concurrency/sendable_module_checking.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift +// RUN: %target-swift-frontend -typecheck -disable-availability-checking -I %t 2>&1 %s | %FileCheck %s + +// REQUIRES: concurrency + +import StrictModule +import NonStrictModule + +actor A { + func f() -> [StrictStruct: NonStrictClass] { [:] } +} + +func testA(a: A) async { + _ = await a.f() // CHECK: warning: cannot call function returning non-sendable type '[StrictStruct : NonStrictClass]' across actors}} + // CHECK-NOT: NonStrictClass + // CHECK: note: struct 'StrictStruct' does not conform to the `Sendable` protocol + // CHECK-NOT: NonStrictClass +} diff --git a/test/Concurrency/task_local.swift b/test/Concurrency/task_local.swift index d8a7547d4cf87..56d66f9bc73e9 100644 --- a/test/Concurrency/task_local.swift +++ b/test/Concurrency/task_local.swift @@ -1,7 +1,7 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) struct TL { @TaskLocal static var number: Int = 0 @@ -22,7 +22,7 @@ var global: Int = 0 class NotSendable {} -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test () async { TL.number = 10 // expected-error{{cannot assign to property: 'number' is a get-only property}} TL.$number = 10 // expected-error{{cannot assign value of type 'Int' to type 'TaskLocal'}} diff --git a/test/Concurrency/throwing.swift b/test/Concurrency/throwing.swift index 66e466724615c..536a2ce60245c 100644 --- a/test/Concurrency/throwing.swift +++ b/test/Concurrency/throwing.swift @@ -1,10 +1,10 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) // REQUIRES: executable_test // REQUIRES: concurrency + // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -// XFAIL: OS=windows-msvc import _Concurrency import StdlibUnittest @@ -28,14 +28,14 @@ protocol MP { } class M : MP { - @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) + @available(SwiftStdlib 5.5, *) func throwWithIndirectResult(_ a: P) async throws -> T { throw E.err } } extension MP { - @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) + @available(SwiftStdlib 5.5, *) func l (_ a : P, _ b: P, _ c: P, _ d : P, _ e: P, _ f: P) async throws -> (A, B, C, D, E2, F) { throw E.err } @@ -45,7 +45,7 @@ extension MP { static func main() async { var tests = TestSuite("Async Throw") - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(SwiftStdlib 5.5, *) { tests.test("throwing of naturally direct but indirect reabstration") { let task2 = detach { let m = M() diff --git a/test/Constraints/argument_matching.swift b/test/Constraints/argument_matching.swift index d3985825a0ffe..291d2cd95ca9a 100644 --- a/test/Constraints/argument_matching.swift +++ b/test/Constraints/argument_matching.swift @@ -1153,7 +1153,7 @@ func testUnlabeledParameterBindingPosition() { // expected-error@-2:22 {{missing argument for parameter 'dd' in call}} f(1, xx: 2, dd: 3) - // expected-error@-1 {{incorrect argument labels in call (have '_:xx:dd:', expected '_:_:cc:dd:')}} + // expected-error@-1 {{extraneous argument label 'xx:' in call}} // expected-error@-2:15 {{missing argument for parameter 'cc' in call}} f(xx: 1, 2, cc: 3) @@ -1161,7 +1161,7 @@ func testUnlabeledParameterBindingPosition() { // expected-error@-2:22 {{missing argument for parameter 'dd' in call}} f(xx: 1, 2, dd: 3) - // expected-error@-1 {{incorrect argument labels in call (have 'xx:_:dd:', expected '_:_:cc:dd:')}} + // expected-error@-1 {{extraneous argument label 'xx:' in call}} // expected-error@-2:15 {{missing argument for parameter 'cc' in call}} f(1, xx: 2, cc: 3, dd: 4) @@ -1207,23 +1207,26 @@ func testUnlabeledParameterBindingPosition() { f(1, xx: 2) // expected-error@-1:6 {{missing arguments for parameters 'aa', 'bb' in call}} - // expected-error@-2 {{incorrect argument labels in call (have '_:xx:', expected 'aa:bb:_:_:')}} + // expected-error@-2 {{extraneous argument label 'xx:' in call}} f(xx: 1, 2) - // expected-error@-1 {{incorrect argument labels in call (have 'xx:_:', expected 'aa:bb:_:_:')}} + // expected-error@-1 {{extraneous argument label 'xx:' in call}} // expected-error@-2:6 {{missing arguments for parameters 'aa', 'bb' in call}} f(bb: 1, 2, xx: 3) // expected-error@-1:7 {{missing argument for parameter 'aa' in call}} + // expected-error@-2:6 {{extraneous argument label 'xx:' in call}} f(bb: 1, xx: 2, 3) // expected-error@-1:7 {{missing argument for parameter 'aa' in call}} + // expected-error@-2:6 {{extraneous argument label 'xx:' in call}} f(aa: 1, 2, xx: 3) // expected-error@-1:12 {{missing argument for parameter 'bb' in call}} + // expected-error@-2:6 {{extraneous argument label 'xx:' in call}} f(aa: 1, xx: 2, 3) - // expected-error@-1 {{incorrect argument labels in call (have 'aa:xx:_:', expected 'aa:bb:_:_:')}} + // expected-error@-1 {{extraneous argument label 'xx:' in call}} // expected-error@-2:12 {{missing argument for parameter 'bb' in call}} f(aa: 1, bb: 2, 3, xx: 4) @@ -1639,7 +1642,7 @@ _ = CurriedClass.method3(1, 2) // expected-error {{instance member 'me // expected-error@-1 {{missing argument label 'b:' in call}} CurriedClass.method3(c)(1.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} CurriedClass.method3(c)(1) // expected-error {{missing argument for parameter 'b' in call}} -CurriedClass.method3(c)(c: 1.0) // expected-error {{incorrect argument labels in call (have 'c:', expected '_:b:')}} +CurriedClass.method3(c)(c: 1.0) // expected-error {{incorrect argument label in call (have 'c:', expected 'b:')}} // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} // expected-error@-2 {{missing argument for parameter #1 in call}} @@ -1759,3 +1762,23 @@ func rdar70764991() { bar(str, S.foo) // expected-error {{unnamed argument #1 must precede unnamed argument #2}} {{9-12=}} {{14-14=str}} } } + +func testExtraTrailingClosure() { + func foo() {} + foo() {} // expected-error@:9 {{extra trailing closure passed in call}} + foo {} // expected-error@:7 {{extra trailing closure passed in call}} + foo {} x: {} // expected-error@:7 {{argument passed to call that takes no arguments}} + + func bar(_ x: Int) {} // expected-note 2{{'bar' declared here}} + bar(5) {} // expected-error@:10 {{extra trailing closure passed in call}} + bar(0) {} x: {} // expected-error@:6 {{extra trailing closures at positions #2, #3 in call}} + bar(5, "") {} // expected-error@:6 {{extra arguments at positions #2, #3 in call}} + + func baz(_ fn: () -> Void) {} // expected-note {{'baz' declared here}} + baz {} x: {} // expected-error@:13 {{extra trailing closure passed in call}} + baz({}) {} // expected-error@:11 {{extra trailing closure passed in call}} + baz({}) {} y: {} // expected-error@:6 {{extra trailing closures at positions #2, #3 in call}} + + func qux(x: () -> Void, y: () -> Void, z: () -> Void) {} // expected-note {{'qux(x:y:z:)' declared here}} + qux() {} m: {} y: {} n: {} z: {} o: {} // expected-error@:6 {{extra trailing closures at positions #2, #4, #6 in call}} +} diff --git a/test/Constraints/async.swift b/test/Constraints/async.swift index fac2310bebd2f..1a466eb13488e 100644 --- a/test/Constraints/async.swift +++ b/test/Constraints/async.swift @@ -1,29 +1,39 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift // REQUIRES: concurrency +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func doAsynchronously() async { } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func doSynchronously() { } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func testConversions() async { let _: () -> Void = doAsynchronously // expected-error{{invalid conversion from 'async' function of type '() async -> ()' to synchronous function type '() -> Void'}} let _: () async -> Void = doSynchronously // okay } // Overloading +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) @available(swift, deprecated: 4.0, message: "synchronous is no fun") func overloadedSame(_: Int = 0) -> String { "synchronous" } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func overloadedSame() async -> String { "asynchronous" } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func overloaded() -> String { "synchronous" } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func overloaded() async -> Double { 3.14159 } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) @available(swift, deprecated: 4.0, message: "synchronous is no fun") func overloadedOptDifference() -> String { "synchronous" } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func overloadedOptDifference() async -> String? { nil } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func testOverloadedSync() { _ = overloadedSame() // expected-warning{{synchronous is no fun}} @@ -53,6 +63,7 @@ func testOverloadedSync() { let _: Int = fn4 // expected-error{{value of type '() async -> ()'}} } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func testOverloadedAsync() async { _ = await overloadedSame() // no warning @@ -87,9 +98,12 @@ func testOverloadedAsync() async { let _: Int = fn4 // expected-error{{value of type '() async -> ()'}} } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func takesAsyncClosure(_ closure: () async -> String) -> Int { 0 } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func takesAsyncClosure(_ closure: () -> String) -> String { "" } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func testPassAsyncClosure() { let a = takesAsyncClosure { await overloadedSame() } let _: Double = a // expected-error{{convert value of type 'Int'}} @@ -98,6 +112,7 @@ func testPassAsyncClosure() { let _: Double = b // expected-error{{convert value of type 'String'}} } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) struct FunctionTypes { var syncNonThrowing: () -> Void var syncThrowing: () throws -> Void @@ -120,22 +135,27 @@ struct FunctionTypes { } // Overloading when there is conversion from sync to async. +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func bar(_ f: (Int) -> Int) -> Int { return f(2) } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func bar(_ f: (Int) async -> Int) async -> Int { return await f(2) } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func incrementSync(_ x: Int) -> Int { return x + 1 } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func incrementAsync(_ x: Int) async -> Int { return x + 1 } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func testAsyncWithConversions() async { _ = bar(incrementSync) _ = bar { -$0 } diff --git a/test/Constraints/bridging.swift b/test/Constraints/bridging.swift index bf894ce00e29e..76d2fc0e3c1c4 100644 --- a/test/Constraints/bridging.swift +++ b/test/Constraints/bridging.swift @@ -258,9 +258,9 @@ func rdar19770981(_ s: String, ns: NSString) { _ = ns as String > s // 'as' has lower precedence than '+' so add parens with the fixit: - s + ns // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}}{{9-9= as String}} + s + ns // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{7-7=(}} {{9-9= as String)}} _ = s + (ns as String) - ns + s // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}}{{5-5= as String}} + ns + s // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}} {{3-3=(}} {{5-5= as String)}} _ = (ns as String) + s } diff --git a/test/Constraints/casts.swift b/test/Constraints/casts.swift index dc9861a802c3b..cd8fbf6d7aabf 100644 --- a/test/Constraints/casts.swift +++ b/test/Constraints/casts.swift @@ -535,3 +535,81 @@ protocol PP2: PP1 { } extension Optional: PP1 where Wrapped == PP2 { } nil is PP1 // expected-error {{'nil' requires a contextual type}} + +// SR-15039 +enum ChangeType { + case initial(T) + case delta(previous: T, next: T) + case unset + + var delta: (previous: T?, next: T)? { nil } +} + +extension ChangeType where T == String? { + var foo: String? { return self.delta?.previous as? String } // OK + var bar: String? { self.delta?.next } +} + +// SR-15038 +protocol ExperimentDeserializable { + static func deserializeExperiment(_ value: Any) -> Self? +} + +extension String: ExperimentDeserializable { + static func deserializeExperiment(_ value: Any) -> String? { value as? String } +} + +extension Int: ExperimentDeserializable { + static func deserializeExperiment(_ value: Any) -> Int? { value as? Int } +} + +class Constant { + private init(getUnderlyingValue: @escaping () -> T) { + print(getUnderlyingValue()) + } +} + +struct Thing { + let storage: [String: Any] +} + +extension Constant where T: Sequence, T.Element: ExperimentDeserializable { + static func foo(thing: Thing, defaultValue: T) -> T where T == [U] { + guard let array = thing.storage["foo"] as? [Any] else { + fatalError() + } + + let value = array.map(T.Element.deserializeExperiment) as? [T.Element] ?? defaultValue // OK + return value + } +} + +// Array +func decodeStringOrInt() -> [T] { + let stringWrapped = [String]() + if let values = stringWrapped.map({ $0.isEmpty ? 0 : T($0) }) as? [T] { // OK + return values + } else { + fatalError() + } +} + +// Set +func decodeStringOrIntSet() -> Set { + let stringWrapped = [String]() + if let values = Set(stringWrapped.map({ $0.isEmpty ? 0 : T($0) })) as? Set { // OK + return values + } else { + fatalError() + } +} + +// Dictionary +func decodeStringOrIntDictionary() -> [Int: T] { + let stringWrapped = [String]() + if let values = Dictionary(uniqueKeysWithValues: stringWrapped.map({ $0.isEmpty ? (0, 0) : (0, T($0)) })) as? [Int: T] { // OK + return values + } else { + fatalError() + } +} diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 046b9ac64d04a..1c164b8dc9766 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -252,7 +252,7 @@ struct CC {} func callCC(_ f: (CC) -> U) -> () {} func typeCheckMultiStmtClosureCrash() { - callCC { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} + callCC { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} _ = $0 return 1 } @@ -313,7 +313,7 @@ struct Thing { init?() {} } // This throws a compiler error -let things = Thing().map { thing in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{34-34=-> <#Result#> }} +let things = Thing().map { thing in // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{34-34=-> <#Result#> }} // Commenting out this makes it compile _ = thing return thing @@ -322,7 +322,7 @@ let things = Thing().map { thing in // expected-error {{unable to infer complex // QoI: [Closure return type inference] Swift cannot find members for the result of inlined lambdas with branches func r21675896(file : String) { - let x: String = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{20-20= () -> <#Result#> in }} + let x: String = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{20-20= () -> <#Result#> in }} if true { return "foo" } @@ -349,8 +349,8 @@ takeVoidVoidFn { () -> Void in } // Swift: Incorrect compile error when calling a function inside a closure -func f19997471(_ x: String) {} // expected-note {{candidate expects value of type 'String' for parameter #1}} -func f19997471(_ x: Int) {} // expected-note {{candidate expects value of type 'Int' for parameter #1}} +func f19997471(_ x: String) {} // expected-note {{candidate expects value of type 'String' for parameter #1 (got 'T')}} +func f19997471(_ x: Int) {} // expected-note {{candidate expects value of type 'Int' for parameter #1 (got 'T')}} func someGeneric19997471(_ x: T) { takeVoidVoidFn { @@ -360,7 +360,7 @@ func someGeneric19997471(_ x: T) { // Swift fails to compile: [0].map() { _ in let r = (1,2).0; return r } -[0].map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{5-5=-> <#Result#> }} +[0].map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{5-5=-> <#Result#> }} _ in let r = (1,2).0 return r @@ -408,7 +408,7 @@ func r20789423() { print(p.f(p)()) // expected-error {{cannot convert value of type 'C' to expected argument type 'Int'}} // expected-error@-1:11 {{cannot call value of non-function type '()'}} - let _f = { (v: Int) in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{23-23=-> <#Result#> }} + let _f = { (v: Int) in // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{23-23=-> <#Result#> }} print("a") return "hi" } @@ -499,7 +499,7 @@ struct S_3520 { func sr3520_set_via_closure(_ closure: (inout S, T) -> ()) {} // expected-note {{in call to function 'sr3520_set_via_closure'}} sr3520_set_via_closure({ $0.number1 = $1 }) // expected-error@-1 {{generic parameter 'S' could not be inferred}} -// expected-error@-2 {{unable to infer type of a closure parameter $1 in the current context}} +// expected-error@-2 {{unable to infer type of a closure parameter '$1' in the current context}} // SR-3073: UnresolvedDotExpr in single expression closure @@ -1105,3 +1105,61 @@ struct R_76250381 { // expected-error@-1 {{contextual closure type '(Range.Element) throws -> ()' (aka '(Int) throws -> ()') expects 1 argument, but 3 were used in closure body}} (0..<10).map { x, y, z, w in } // expected-error@-1 {{contextual closure type '(Range.Element) throws -> ()' (aka '(Int) throws -> ()') expects 1 argument, but 4 were used in closure body}} + +// rdar://77022842 - crash due to a missing argument to a ternary operator +func rdar77022842(argA: Bool? = nil, argB: Bool? = nil) { + if let a = argA ?? false, if let b = argB ?? { + // expected-error@-1 {{initializer for conditional binding must have Optional type, not 'Bool'}} + // expected-error@-2 {{cannot convert value of type '() -> ()' to expected argument type 'Bool?'}} + // expected-error@-3 {{expected expression in conditional}} + } // expected-error {{expected '{' after 'if' condition}} +} + +// rdar://76058892 - spurious ambiguity diagnostic +func rdar76058892() { + struct S { + var test: Int = 0 + } + + func test(_: Int) {} + func test(_: () -> String) {} + + func experiment(arr: [S]?) { + test { // expected-error {{contextual closure type '() -> String' expects 0 arguments, but 1 was used in closure body}} + if let arr = arr { + arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}} + } + } + } +} + +// rdar://78917861 - Invalid generic type parameter inference + +func rdar78917861() { + class Cell {} + class MyCell : Cell {} + + class DataCollection { + } + + class MyCollection { + typealias DataType = String + typealias CellType = MyCell + + var data: DataCollection + + init() { + self.data = DataCollection() + } + } + + class Test { + let collection = MyCollection() + + lazy var prop: DataCollection = { + collection.data // Ok + // Since contextual type `DataCollection` doesn't specify generic parameters they have to be inferred + // but that has to wait until the closure is resolved because types can flow both ways + }() + } +} diff --git a/test/Constraints/construction.swift b/test/Constraints/construction.swift index 048667c4e129b..0390e6eb656d1 100644 --- a/test/Constraints/construction.swift +++ b/test/Constraints/construction.swift @@ -16,9 +16,9 @@ enum Z { init() { self = .none } init(_ c: UnicodeScalar) { self = .char(c) } - // expected-note@-1 2 {{candidate expects value of type 'UnicodeScalar' (aka 'Unicode.Scalar') for parameter #1}} + // expected-note@-1 2 {{candidate expects value of type 'UnicodeScalar' (aka 'Unicode.Scalar') for parameter #1 (got 'Z')}} init(_ s: String) { self = .string(s) } - // expected-note@-1 2 {{candidate expects value of type 'String' for parameter #1}} + // expected-note@-1 2 {{candidate expects value of type 'String' for parameter #1 (got 'Z')}} init(_ x: Int, _ y: Int) { self = .point(x, y) } } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 03b34862ac55f..1da8928435a57 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -161,7 +161,7 @@ public func myMap(_ x: T?, _ f: (T) -> U) -> U? { // func rdar20142523() { - myMap(0..<10, { x in // expected-error{{unable to infer complex closure return type; add explicit type to disambiguate}} {{21-21=-> <#Result#> }} {{educational-notes=complex-closure-inference}} + myMap(0..<10, { x in // expected-error{{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{21-21=-> <#Result#> }} {{educational-notes=complex-closure-inference}} () return x }) @@ -296,7 +296,7 @@ func r18800223(_ i : Int) { } // Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back -_ = { $0 } // expected-error {{unable to infer type of a closure parameter $0 in the current context}} +_ = { $0 } // expected-error {{unable to infer type of a closure parameter '$0' in the current context}} @@ -399,9 +399,9 @@ enum Color { static func rainbow() -> Color {} static func overload(a : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(a:)')}} - // expected-note@-1 {{candidate expects value of type 'Int' for parameter #1}} + // expected-note@-1 {{candidate expects value of type 'Int' for parameter #1 (got 'Double')}} static func overload(b : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(b:)')}} - // expected-note@-1 {{candidate expects value of type 'Int' for parameter #1}} + // expected-note@-1 {{candidate expects value of type 'Int' for parameter #1 (got 'Double')}} static func frob(_ a : Int, b : inout Int) -> Color {} static var svar: Color { return .Red } @@ -573,9 +573,17 @@ func r22263468(_ a : String?) { // TODO(diagnostics): This is a regression from diagnosing missing optional unwrap for `a`, we have to // re-think the way errors in tuple elements are detected because it's currently impossible to detect // exactly what went wrong here and aggregate fixes for different elements at the same time. - _ = MyTuple(42, a) // expected-error {{tuple type 'MyTuple' (aka '(Int, String)') is not convertible to tuple type '(Int, String?)'}} + _ = MyTuple(42, a) // expected-error {{tuple type '(Int, String?)' is not convertible to tuple type 'MyTuple' (aka '(Int, String)')}} } +// rdar://71829040 - "ambiguous without more context" error for tuple type mismatch. +func r71829040() { + func object(forKey: String) -> Any? { nil } + + let flags: [String: String] + // expected-error@+1 {{tuple type '(String, Bool)' is not convertible to tuple type '(String, String)'}} + flags = Dictionary(uniqueKeysWithValues: ["keyA", "keyB"].map { ($0, object(forKey: $0) as? Bool ?? false) }) +} // rdar://22470302 - Crash with parenthesized call result. class r22470302Class { @@ -1129,7 +1137,7 @@ func badTypes() { // rdar://34357545 func unresolvedTypeExistential() -> Bool { return (Int.self==_{}) - // expected-error@-1 {{'_' can only appear in a pattern or on the left side of an assignment}} + // expected-error@-1 {{placeholders are not allowed as top-level types}} } do { @@ -1205,6 +1213,47 @@ func voidFuncWithNestedVoidFunc() { } } +func voidFuncWithEffects1() throws { + return 1 + // expected-error@-1 {{unexpected non-void return value in void function}} + // expected-note@-2 {{did you mean to add a return type?}}{{35-35= -> <#Return Type#>}} +} + +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +func voidFuncWithEffects2() async throws { + return 1 + // expected-error@-1 {{unexpected non-void return value in void function}} + // expected-note@-2 {{did you mean to add a return type?}}{{41-41= -> <#Return Type#>}} +} + +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +// expected-error@+1 {{'async' must precede 'throws'}} +func voidFuncWithEffects3() throws async { + return 1 + // expected-error@-1 {{unexpected non-void return value in void function}} + // expected-note@-2 {{did you mean to add a return type?}}{{41-41= -> <#Return Type#>}} +} + +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +func voidFuncWithEffects4() async { + return 1 + // expected-error@-1 {{unexpected non-void return value in void function}} + // expected-note@-2 {{did you mean to add a return type?}}{{34-34= -> <#Return Type#>}} +} + +func voidFuncWithEffects5(_ closure: () throws -> Void) rethrows { + return 1 + // expected-error@-1 {{unexpected non-void return value in void function}} + // expected-note@-2 {{did you mean to add a return type?}}{{65-65= -> <#Return Type#>}} +} + +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +func voidGenericFuncWithEffects(arg: T) async where T: CustomStringConvertible { + return 1 + // expected-error@-1 {{unexpected non-void return value in void function}} + // expected-note@-2 {{did you mean to add a return type?}}{{49-49= -> <#Return Type#>}} +} + // Special cases: These should not offer a note + fix-it func voidFuncExplicitType() -> Void { @@ -1254,7 +1303,7 @@ func f11(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{where ' f11(3, f4) // expected-error {{global function 'f11' requires that 'Int' conform to 'P2'}} let f12: (Int) -> Void = { _ in } // expected-note {{candidate '(Int) -> Void' requires 1 argument, but 2 were provided}} -func f12(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{candidate requires that 'Int' conform to 'P2' (requirement specified as 'T' == 'P2')}} +func f12(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{candidate requires that 'Int' conform to 'P2' (requirement specified as 'T' : 'P2')}} f12(3, f4)// expected-error {{no exact matches in call to global function 'f12'}} // SR-12242 @@ -1366,3 +1415,72 @@ func rdar74696023() { // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} } } + +extension Int { + static var optionalIntMember: Int? { 0 } + static var optionalThrowsMember: Int? { get throws { 0 } } +} + +func testUnwrapFixIts(x: Int?) throws { + let _ = x + 2 // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{11-11=(}} {{12-12= ?? <#default value#>)}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{12-12=!}} + let _ = (x ?? 0) + 2 + + let _ = 2 + x // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{15-15=(}} {{16-16= ?? <#default value#>)}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{16-16=!}} + let _ = 2 + (x ?? 0) + + func foo(y: Int) {} + foo(y: x) // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{11-11= ?? <#default value#>}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{11-11=!}} + foo(y: x ?? 0) + + let _ = x < 2 // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{12-12= ?? <#default value#>}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{12-12=!}} + let _ = x ?? 0 < 2 + + let _ = 2 < x // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{16-16= ?? <#default value#>}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{16-16=!}} + let _ = 2 < x ?? 0 + + let _: Int = (.optionalIntMember) // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{35-35= ?? <#default value#>}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{35-35=!}} + let _: Int = (.optionalIntMember ?? 0) + + let _ = 1 + .optionalIntMember // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{15-15=(}} {{33-33= ?? <#default value#>)}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{33-33=!}} + let _ = 1 + (.optionalIntMember ?? 0) + + let _ = try .optionalThrowsMember + 1 // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{15-15=(}} {{36-36= ?? <#default value#>)}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{36-36=!}} + let _ = try (.optionalThrowsMember ?? 0) + 1 + + let _ = .optionalIntMember?.bitWidth > 0 // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{39-39= ?? <#default value#>}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{11-11=(}} {{39-39=)!}} + let _ = (.optionalIntMember?.bitWidth)! > 0 + let _ = .optionalIntMember?.bitWidth ?? 0 > 0 + + let _ = .random() ? .optionalIntMember : 0 // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{41-41= ?? <#default value#>}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{41-41=!}} + let _ = .random() ? .optionalIntMember ?? 0 : 0 + + let _: Int = try try try .optionalThrowsMember // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{49-49= ?? <#default value#>}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{49-49=!}} + let _: Int = try try try .optionalThrowsMember ?? 0 + + let _: Int = try! .optionalThrowsMember // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{42-42= ?? <#default value#>}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{42-42=!}} + let _: Int = try! .optionalThrowsMember ?? 0 +} diff --git a/test/Constraints/dynamic_lookup.swift b/test/Constraints/dynamic_lookup.swift index 71378006b00d1..f024707ce333c 100644 --- a/test/Constraints/dynamic_lookup.swift +++ b/test/Constraints/dynamic_lookup.swift @@ -433,3 +433,20 @@ func testCallAsFunctionAnyObject(_ x: AnyObject) { x() // expected-error {{cannot call value of non-function type 'AnyObject'}} x.callAsFunction() // Okay. } + +// Note: In Swift >= 6 mode this would become an error. +func test_dynamic_subscript_accepts_type_name_argument() { + @objc class A { + @objc subscript(a: A.Type) -> Int { get { 42 } } + } + + func test(a: AnyObject, optA: AnyObject?) { + let _ = a[A] // expected-warning {{expected member name or constructor call after type name}} + // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{16-16=()}} + // expected-note@-2 {{use '.self' to reference the type object}} {{16-16=.self}} + + let _ = optA?[A] // expected-warning {{expected member name or constructor call after type name}} + // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{20-20=()}} + // expected-note@-2 {{use '.self' to reference the type object}} {{20-20=.self}} + } +} diff --git a/test/Constraints/dynamic_lookup_swift6.swift b/test/Constraints/dynamic_lookup_swift6.swift new file mode 100644 index 0000000000000..29e7aa5e46a00 --- /dev/null +++ b/test/Constraints/dynamic_lookup_swift6.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module %S/Inputs/PrivateObjC.swift -o %t +// RUN: %target-typecheck-verify-swift -swift-version 6 -I %t -verify-ignore-unknown + +// REQUIRES: objc_interop && asserts + +import Foundation +import PrivateObjC + +func test_dynamic_subscript_accepts_type_name_argument() { + @objc class A { + @objc subscript(a: A.Type) -> Int { get { 42 } } + } + + func test(a: AnyObject, optA: AnyObject?) { + let _ = a[A] // expected-error {{expected member name or constructor call after type name}} + // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{16-16=()}} + // expected-note@-2 {{use '.self' to reference the type object}} {{16-16=.self}} + + let _ = optA?[A] // expected-error {{expected member name or constructor call after type name}} + // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{20-20=()}} + // expected-note@-2 {{use '.self' to reference the type object}} {{20-20=.self}} + } +} diff --git a/test/Constraints/enum_cases.swift b/test/Constraints/enum_cases.swift index e567f89496070..8ff8f5a79d446 100644 --- a/test/Constraints/enum_cases.swift +++ b/test/Constraints/enum_cases.swift @@ -171,3 +171,28 @@ struct EnumElementPatternFromContextualType { } } } + +// SR-14408 +enum CompassPoint { + case North(Int) + case South + case East + case West +} + +func isNorth(c : CompassPoint) -> Bool { + // expected-error@+1{{member 'North' expects argument of type 'Int'}} + return c == .North // expected-error {{binary operator '==' cannot be applied to two 'CompassPoint' operands}} + // expected-note@-1 {{binary operator '==' cannot be synthesized for enums with associated values}} +} + +func isNorth2(c : CompassPoint) -> Bool { + // expected-error@+1{{member 'North' expects argument of type 'Int'}} + return .North == c // expected-error {{binary operator '==' cannot be applied to two 'CompassPoint' operands}} + // expected-note@-1 {{binary operator '==' cannot be synthesized for enums with associated values}} +} + +func isSouth(c : CompassPoint) -> Bool { + return c == .South // expected-error {{binary operator '==' cannot be applied to two 'CompassPoint' operands}} + // expected-note@-1 {{binary operator '==' cannot be synthesized for enums with associated values}} +} diff --git a/test/Constraints/function.swift b/test/Constraints/function.swift index 8d7a0d7ff5278..749e513ebafaa 100644 --- a/test/Constraints/function.swift +++ b/test/Constraints/function.swift @@ -245,3 +245,8 @@ func test_passing_noescape_function_ref_to_generic_parameter() { } } } + +// SR-14784 +func SR14784(_ fs: () -> T..., a _ : Int) -> T { + fs.first! // expected-error{{function produces expected type 'T'; did you mean to call it with '()'?}} {{11-11=()}} +} diff --git a/test/Constraints/function_conversion.swift b/test/Constraints/function_conversion.swift index 2d539dcfab264..44ed0bd62f369 100644 --- a/test/Constraints/function_conversion.swift +++ b/test/Constraints/function_conversion.swift @@ -57,9 +57,10 @@ func twoFns(_ f: (Int) -> Int, _ g: @escaping (Int) -> Int) { takesAny(consumeNoEscape) takesAny(consumeEscaping) -var noEscapeParam: ((Int) -> Int) -> () = consumeNoEscape +var noEscapeParam: ((Int) -> Int) -> () = consumeNoEscape // expected-note {{add explicit @escaping to function parameter}}{{21-21=@escaping }} var escapingParam: (@escaping (Int) -> Int) -> () = consumeEscaping -noEscapeParam = escapingParam // expected-error {{converting non-escaping value to '(Int) -> Int' may allow it to escape}} +noEscapeParam = escapingParam // expected-error {{cannot assign value of type '(@escaping (Int) -> Int) -> ()' to type '((Int) -> Int) -> ()'}} +// expected-note@-1{{parameter #0 expects escaping value of type '(Int) -> Int'}} escapingParam = takesAny noEscapeParam = takesAny // expected-error {{converting non-escaping value to 'Any' may allow it to escape}} @@ -80,3 +81,12 @@ func rdar_59703585() { cb = swiftCallback // expected-error@-1 {{cannot assign value of type '(UnsafePointer, UnsafeMutableRawPointer?) -> ()' to type 'Fn?' (aka 'Optional<@convention(c) (Optional>, Optional) -> ()>')}} } + +// SR-14869 +var v1: (inout Float) -> () +v1 = { (_: inout Int) in } +// expected-error@-1{{cannot assign value of type '(inout Int) -> ()' to type '(inout Float) -> ()'}} + +var v2: (Int , inout Float) -> () +v2 = { (_: Int, _: inout Int) in } +// expected-error@-1{{cannot assign value of type '(Int, inout Int) -> ()' to type '(Int, inout Float) -> ()'}} diff --git a/test/Constraints/generics.swift b/test/Constraints/generics.swift index 0326f9617270c..4c847c90daff4 100644 --- a/test/Constraints/generics.swift +++ b/test/Constraints/generics.swift @@ -870,3 +870,63 @@ func test_ternary_operator_with_regular_conformance_to_literal_protocol() { bug(true ? test(0) : test(42)) // Ok - type is `CGFloat` for 0 and 42 } + +// rdar://78623338 - crash due to leftover inactive constraints +func rdar78623338() { + func any(_ sequence: T) -> AnySequence { + // expected-note@-1 {{required by local function 'any' where 'T' = '() -> ReversedCollection<(ClosedRange)>'}} + AnySequence(sequence.makeIterator) + } + + let _ = [ + any(0...3), + // TODO: It should be possible to suggest making a call to `reserved` here but we don't have machinery to do so + // at the moment because there is no way to go from a requirement to the underlying argument/parameter location. + any((1...3).reversed) // expected-error {{type '() -> ReversedCollection<(ClosedRange)>' cannot conform to 'Sequence'}} + // expected-note@-1 {{only concrete types such as structs, enums and classes can conform to protocols}} + ] +} + +// rdar://78781552 - crash in `getFunctionArgApplyInfo` +func rdar78781552() { + struct Test where Data : RandomAccessCollection { + // expected-note@-1 {{where 'Data' = '(((Int) throws -> Bool) throws -> [Int])?'}} + // expected-note@-2 {{'init(data:filter:)' declared here}} + // expected-note@-3 {{'Content' declared as parameter to type 'Test'}} + var data: [Data] + var filter: (Data.Element) -> Content + } + + func test(data: [Int]?) { + Test(data?.filter) + // expected-error@-1 {{generic struct 'Test' requires that '(((Int) throws -> Bool) throws -> [Int])?' conform to 'RandomAccessCollection'}} + // expected-error@-2 {{generic parameter 'Content' could not be inferred}} expected-note@-2 {{explicitly specify the generic arguments to fix this issue}} + // expected-error@-3 {{cannot convert value of type '(((Int) throws -> Bool) throws -> [Int])?' to expected argument type '[(((Int) throws -> Bool) throws -> [Int])?]'}} + // expected-error@-4 {{missing argument for parameter 'filter' in call}} + } +} + +// rdar://79757320 - failured to produce a diagnostic when unresolved dependent member is used in function result position + +protocol R_79757320 { + associatedtype In + associatedtype Out +} + +func rdar79757320() { + struct Value { + init(_: W) {} + + func formatted() -> String { "" } + func formatted(_: T) -> T.Out where T.In == Value { fatalError() } + } + + struct Container { + var value: String + } + + // FIXME: There has to be a way to propagate holes that makes it easy to suppress failures caused by missing members. + _ = Container(value: Value(42).formatted(.S(a: .a, b: .b(0)))) // expected-error {{type 'R_79757320' has no member 'S'}} + // expected-error@-1 {{cannot infer contextual base in reference to member 'a'}} + // expected-error@-2 {{cannot infer contextual base in reference to member 'b'}} +} diff --git a/test/Constraints/keypath.swift b/test/Constraints/keypath.swift index 1bf36dbd65318..84e8ad02917b1 100644 --- a/test/Constraints/keypath.swift +++ b/test/Constraints/keypath.swift @@ -78,9 +78,9 @@ func testVariadicKeypathAsFunc() { takesVariadicFnWithGenericRet(\Array.i) // These are not okay, the KeyPath should have a base that matches the - // internal parameter type of the function, i.e [S]. - let _: (S...) -> Int = \S.i // expected-error {{key path value type 'S' cannot be converted to contextual type '[S]'}} - takesVariadicFnWithGenericRet(\S.i) // expected-error {{key path value type 'S' cannot be converted to contextual type '[S]'}} + // internal parameter type of the function, i.e (S...). + let _: (S...) -> Int = \S.i // expected-error {{key path value type 'S' cannot be converted to contextual type 'S...'}} + takesVariadicFnWithGenericRet(\S.i) // expected-error {{key path value type 'S' cannot be converted to contextual type 'S...'}} } // rdar://problem/54322807 diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index d4d162baf8fdf..2f5bc5ffc7333 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -615,14 +615,13 @@ func rdar50679161() { func rdar_50467583_and_50909555() { - if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) { // rdar://problem/50467583 let _: Set = [Int][] // expected-error {{no 'subscript' candidates produce the expected contextual result type 'Set'}} // expected-error@-1 {{no exact matches in call to subscript}} // expected-note@-2 {{found candidate with type '(Int) -> Int'}} // expected-note@-3 {{found candidate with type '(Range) -> ArraySlice'}} // expected-note@-4 {{found candidate with type '((UnboundedRange_) -> ()) -> ArraySlice'}} - // expected-note@-5 {{found candidate with type '(Range.Index>) -> Slice<[Int]>' (aka '(Range) -> Slice>')}} } // rdar://problem/50909555 @@ -735,3 +734,14 @@ func rdar55369704() { _ = x - Int(s.value) // expected-error {{value of type 'S' has no member 'value'}} } } + +// SR-14533 +struct SR14533 { + var xs: [Int] +} + +func fSR14533(_ s: SR14533) { + for (x1, x2) in zip(s.xs, s.ys) { + // expected-error@-1{{value of type 'SR14533' has no member 'ys'}} + } +} diff --git a/test/Constraints/one_way_closure_params.swift b/test/Constraints/one_way_closure_params.swift index c62bb7256cb7b..d86c05910038f 100644 --- a/test/Constraints/one_way_closure_params.swift +++ b/test/Constraints/one_way_closure_params.swift @@ -3,6 +3,6 @@ func testBasic() { let _: (Float) -> Float = { $0 + 1 } - let _ = { $0 + 1 } // expected-error{{unable to infer type of a closure parameter $0 in the current context}} + let _ = { $0 + 1 } // expected-error{{unable to infer type of a closure parameter '$0' in the current context}} } diff --git a/test/Constraints/overload.swift b/test/Constraints/overload.swift index e894b8a37ee0b..34da8f7e4bda2 100644 --- a/test/Constraints/overload.swift +++ b/test/Constraints/overload.swift @@ -3,9 +3,9 @@ func markUsed(_ t: T) {} func f0(_: Float) -> Float {} -// expected-note@-1 {{candidate expects value of type 'Float' for parameter #1}} +// expected-note@-1 {{candidate expects value of type 'Float' for parameter #1 (got 'X')}} func f0(_: Int) -> Int {} -// expected-note@-1 {{candidate expects value of type 'Int' for parameter #1}} +// expected-note@-1 {{candidate expects value of type 'Int' for parameter #1 (got 'X')}} func f1(_: Int) {} @@ -246,3 +246,14 @@ func test_no_hole_propagation() { return arguments.reduce(0, +) // expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} } } + +// rdar://79672230 - crash due to unsatisfied `: AnyObject` requirement +func rdar79672230() { + struct MyType {} + + func test(_ representation: MyType) -> Bool {} // expected-note {{found candidate with type 'MyType'}} + func test(_ object: inout T) -> Bool where T : AnyObject {} // expected-note {{candidate requires that 'MyType' conform to 'AnyObject' (requirement specified as 'T' : 'AnyObject')}} + + var t: MyType = MyType() + test(&t) // expected-error {{no exact matches in call to local function 'test'}} +} diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index a709fb5f8096e..40577ded3e629 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -230,14 +230,14 @@ func good(_ a: A) -> Int { } func bad(_ a: A) { - a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} + a.map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} let _: EE = $0 return 1 } } func ugly(_ a: A) { - a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} + a.map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} switch $0 { case .A: return 1 @@ -514,3 +514,9 @@ func rdar64157451() { if case .foo(let v as DoeNotExist) = e {} // expected-error {{cannot find type 'DoeNotExist' in scope}} } } + +// rdar://80797176 - circular reference incorrectly diagnosed while reaching for a type of a pattern. +func rdar80797176 () { + for x: Int in [1, 2] where x.bitWidth == 32 { // Ok + } +} diff --git a/test/Constraints/protocols.swift b/test/Constraints/protocols.swift index fdeabbac6dde3..a849ecdc84aae 100644 --- a/test/Constraints/protocols.swift +++ b/test/Constraints/protocols.swift @@ -492,3 +492,30 @@ func test_arg_conformance_with_conditional_reqs(i: Int) { let _: Int?? = simple(overloaded_result()) let _: Int?? = overloaded(overloaded_result()) } + +// rdar://77570994 - regression in type unification for literal collections + +protocol Elt { +} + +extension Int : Elt {} +extension Int64 : Elt {} +extension Dictionary : Elt where Key == String, Value: Elt {} + +struct Object {} + +extension Object : ExpressibleByDictionaryLiteral { + init(dictionaryLiteral elements: (String, Elt)...) { + } +} + +enum E { +case test(cond: Bool, v: Int64) + + var test_prop: Object { + switch self { + case let .test(cond, v): + return ["obj": ["a": v, "b": cond ? 0 : 42]] // Ok + } + } +} diff --git a/test/Constraints/rdar65320500.swift b/test/Constraints/rdar65320500.swift index dd99f9d0906be..25b08ac7aad5e 100644 --- a/test/Constraints/rdar65320500.swift +++ b/test/Constraints/rdar65320500.swift @@ -24,20 +24,20 @@ func test(_: Int) -> Bool { } test_builder { - let totalSeconds = 42000 - test(totalseconds / 3600) // expected-error {{cannot find 'totalseconds' in scope}} + let totalSeconds = 42000 // expected-note {{'totalSeconds' declared here}} + test(totalseconds / 3600) // expected-error {{cannot find 'totalseconds' in scope; did you mean 'totalSeconds'?}} } test_builder { test(doesntExist()) // expected-error {{cannot find 'doesntExist' in scope}} - if let result = doesntExist() { // expected-error {{cannot find 'doesntExist' in scope}} + if let result = doesntExist() { } - if bar = test(42) {} // expected-error {{cannot find 'bar' in scope}} + if bar = test(42) {} - let foo = bar() // expected-error {{cannot find 'bar' in scope}} + let foo = bar() - switch (doesntExist()) { // expected-error {{cannot find 'doesntExist' in scope}} + switch (doesntExist()) { } } diff --git a/test/Constraints/result_builder_availability.swift b/test/Constraints/result_builder_availability.swift index a85cdc112b6d7..7c765ad5aa983 100644 --- a/test/Constraints/result_builder_availability.swift +++ b/test/Constraints/result_builder_availability.swift @@ -18,7 +18,9 @@ enum Either { } @resultBuilder -struct TupleBuilder { // expected-note{{add 'buildLimitedAvailability(_:)' to the result builder 'TupleBuilder' to erase type information for less-available types}}{{22-22=\n static func buildLimitedAvailability(_ component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} +struct TupleBuilder { +// expected-note@-1{{add 'buildLimitedAvailability(_:)' to the result builder 'TupleBuilder' to erase type information for less-available types}}{{22-22=\n static func buildLimitedAvailability(_ component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} +// expected-note@-2{{add 'buildLimitedAvailability(_:)' to the result builder 'TupleBuilder' to erase type information for less-available types}}{{22-22=\n static func buildLimitedAvailability(_ component: <#Component#>) -> <#Component#> {\n <#code#>\n \}}} static func buildBlock(_ t1: T1) -> (T1) { return (t1) } @@ -75,13 +77,32 @@ tuplify(true) { cond in if #available(OSX 10.51, *) { globalFuncAvailableOn10_51() tuplify(false) { cond2 in - if cond, #available(OSX 10.52, *) { // expected-warning{{result builder 'TupleBuilder' does not implement 'buildLimitedAvailability'; this code may crash on earlier versions of the OS}} + if cond, #available(OSX 10.52, *) { + // expected-warning@-1{{result builder 'TupleBuilder' does not implement 'buildLimitedAvailability'; this code may crash on earlier versions of the OS}} cond2 globalFuncAvailableOn10_52() + } else if true { + globalFuncAvailableOn10_52() // expected-error{{'globalFuncAvailableOn10_52()' is only available in macOS 10.52 or newer}} + // expected-note@-1{{add 'if #available' version check}} + } else if false { + globalFuncAvailableOn10_52() // expected-error{{'globalFuncAvailableOn10_52()' is only available in macOS 10.52 or newer}} + // expected-note@-1{{add 'if #available' version check}} } else { globalFuncAvailableOn10_52() // expected-error{{'globalFuncAvailableOn10_52()' is only available in macOS 10.52 or newer}} // expected-note@-1{{add 'if #available' version check}} } + if cond, #unavailable(OSX 10.52) { + // expected-warning@-1{{result builder 'TupleBuilder' does not implement 'buildLimitedAvailability'; this code may crash on earlier versions of the OS}} + cond2 + globalFuncAvailableOn10_52() // expected-error{{'globalFuncAvailableOn10_52()' is only available in macOS 10.52 or newer}} + // expected-note@-1{{add 'if #available' version check}} + } else if true { + globalFuncAvailableOn10_52() + } else if false { + globalFuncAvailableOn10_52() + } else { + globalFuncAvailableOn10_52() + } } } } @@ -136,4 +157,10 @@ tuplifyWithAvailabilityErasure(true) { cond in if cond, #available(OSX 10.52, *) { globalFuncAvailableOn10_52() } + + if cond, #unavailable(OSX 10.52) { + cond + } else { + globalFuncAvailableOn10_52() + } } diff --git a/test/Constraints/result_builder_diags.swift b/test/Constraints/result_builder_diags.swift index 3a5c00f354c6d..1c41314965628 100644 --- a/test/Constraints/result_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -6,7 +6,7 @@ enum Either { } @resultBuilder -struct TupleBuilder { // expected-note 2 {{struct 'TupleBuilder' declared here}} +struct TupleBuilder { // expected-note 3 {{struct 'TupleBuilder' declared here}} static func buildBlock() -> () { } static func buildBlock(_ t1: T1) -> T1 { @@ -99,10 +99,25 @@ func testDiags() { tuplify(true) { _ in 17 let x = 17 - let y: Int // expected-error{{closure containing a declaration cannot be used with result builder 'TupleBuilder'}} + let y: Int // expected-error{{local variable 'y' requires explicit initializer to be used with result builder 'TupleBuilder'}} {{15-15= = <#value#>}} x + 25 } + tuplify(true) { _ in + 17 + let y: Int, z: String + // expected-error@-1 {{local variable 'y' requires explicit initializer to be used with result builder 'TupleBuilder'}} {{15-15= = <#value#>}} + // expected-error@-2 {{local variable 'z' requires explicit initializer to be used with result builder 'TupleBuilder'}} {{26-26= = <#value#>}} + y + 25 + } + + tuplify(true) { _ in + 0 + let x: Int = 0, y: String = "" // Multiple initialized pattern bindings are okay + x + 1 + y + } + // Statements unsupported by the particular builder. tuplifyWithoutIf(true) { if $0 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}} @@ -142,6 +157,12 @@ func testOverloading(name: String) { } } + _ = overloadedTuplify(true) { cond in + if cond { + print(\"hello") // expected-error {{invalid component of Swift key path}} + } + } + let _: A = a1 _ = overloadedTuplify(true) { b in // expected-error {{ambiguous use of 'overloadedTuplify(_:body:)'}} @@ -461,7 +482,7 @@ struct TestConstraintGenerationErrors { func buildTupleClosure() { tuplify(true) { _ in let a = nothing // expected-error {{cannot find 'nothing' in scope}} - String(nothing) // expected-error {{cannot find 'nothing' in scope}} + String(nothing) } } } @@ -646,7 +667,7 @@ struct MyView { @TupleBuilder var invalidSwitchMultiple: some P { switch Optional.some(1) { - case .none: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} + case .none: // expected-error {{'case' label in a 'switch' must have at least one executable statement}} case . // expected-error {{expected ':' after 'case'}} } // expected-error {{expected identifier after '.' expression}} } @@ -721,4 +742,42 @@ struct TuplifiedStructWithInvalidClosure { 42 } } + + @TupleBuilder var nestedErrorsDiagnosedByParser: some Any { + tuplify(true) { _ in + tuplify { _ in + self. // expected-error {{expected member name following '.'}} + } + 42 + } + } +} + +// rdar://65667992 - invalid case in enum causes fallback diagnostic +func test_rdar65667992() { + @resultBuilder + struct Builder { + static func buildBlock(_ t: T) -> T { t } + static func buildEither(first: T) -> T { first } + static func buildEither(second: T) -> T { second } + } + + struct S {} + + enum E { + case set(v: Int, choices: [Int]) + case notSet(choices: [Int]) + } + + struct MyView { + var entry: E + + @Builder var body: S { + switch entry { // expected-error {{type 'E' has no member 'unset'}} + case .set(_, _): S() + case .unset(_): S() + default: S() + } + } + } } diff --git a/test/Constraints/result_builder_invalid_stmts.swift b/test/Constraints/result_builder_invalid_stmts.swift new file mode 100644 index 0000000000000..5de3ccc668b1f --- /dev/null +++ b/test/Constraints/result_builder_invalid_stmts.swift @@ -0,0 +1,66 @@ +// RUN: %target-typecheck-verify-swift +// rdar://81228221 + +@resultBuilder +struct Builder { + static func buildBlock(_ components: Int...) -> Int { 0 } + static func buildEither(first component: Int) -> Int { 0 } + static func buildEither(second component: Int) -> Int { 0 } + static func buildOptional(_ component: Int?) -> Int { 0 } + static func buildArray(_ components: [Int]) -> Int { 0 } +} + +@Builder +func foo(_ x: String) -> Int { + if .random() { + switch x { + case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} + // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} + 0 + default: + 1 + } + } +} + +@Builder +func bar(_ x: String) -> Int { + switch 0 { + case 0: + switch x { + case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} + // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} + 0 + default: + 1 + } + default: + 0 + } +} + +@Builder +func baz(_ x: String) -> Int { + do { + switch x { + case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} + // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} + 0 + default: + 1 + } + } +} + +@Builder +func qux(_ x: String) -> Int { + for _ in 0 ... 0 { + switch x { + case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} + // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} + 0 + default: + 1 + } + } +} diff --git a/test/Constraints/result_builder_invalid_vars.swift b/test/Constraints/result_builder_invalid_vars.swift index 21e8261880b6d..e49cdb600d7e7 100644 --- a/test/Constraints/result_builder_invalid_vars.swift +++ b/test/Constraints/result_builder_invalid_vars.swift @@ -10,17 +10,17 @@ struct DummyBuilder { // expected-note 5 {{struct 'DummyBuilder' declared here}} func dummy(@DummyBuilder _: () -> T) {} dummy { - var computedVar: Int { return 123 } // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}} + var computedVar: Int { return 123 } // expected-error {{cannot declare local computed variable in result builder}} () } dummy { - lazy var lazyVar: Int = 123 // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}} + lazy var lazyVar: Int = 123 // expected-error {{cannot declare local lazy variable in result builder}} () } dummy { - var observedVar: Int = 123 { // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}} + var observedVar: Int = 123 { // expected-error {{cannot declare local observed variable in result builder}} didSet {} } @@ -28,7 +28,7 @@ dummy { } dummy { - var observedVar: Int = 123 { // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}} + var observedVar: Int = 123 { // expected-error {{cannot declare local observed variable in result builder}} willSet {} } @@ -40,7 +40,7 @@ dummy { } dummy { - @Wrapper var wrappedVar: Int = 123 // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}} + @Wrapper var wrappedVar: Int = 123 // expected-error {{cannot declare local wrapped variable in result builder}} () } diff --git a/test/Constraints/result_builder_opaque_result_structural.swift b/test/Constraints/result_builder_opaque_result_structural.swift new file mode 100644 index 0000000000000..7d249dced7894 --- /dev/null +++ b/test/Constraints/result_builder_opaque_result_structural.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-structural-opaque-types -disable-availability-checking + +@resultBuilder +struct TupleBuilder { + static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { + return (t1, t2) + } +} + +protocol Tupled { + associatedtype TupleType + + @TupleBuilder var tuple: TupleType { get } +} + +struct TupleMeStructural: Tupled { + var tuple: (some Any, Int) { + "hello" + 0 + } +} diff --git a/test/Constraints/static_members_on_protocol_in_generic_context.swift b/test/Constraints/static_members_on_protocol_in_generic_context.swift index 5ce49a4eff534..0516e08d92dc4 100644 --- a/test/Constraints/static_members_on_protocol_in_generic_context.swift +++ b/test/Constraints/static_members_on_protocol_in_generic_context.swift @@ -265,24 +265,6 @@ test_combo(.genericWithReqs([42])) test_combo(.genericWithReqs(())) // expected-error@-1 {{contextual member reference to static method 'genericWithReqs' requires 'Self' constraint in the protocol extension}} -protocol Z { - associatedtype T = Int - - static var prop: T { get } -} - -extension Z { - static func method() -> T { fatalError() } -} - -_ = Z.prop -// expected-error@-1 {{member 'prop' cannot be used on value of protocol type 'Z.Protocol'; use a generic constraint instead}} -// expected-error@-2 {{protocol 'Z' can only be used as a generic constraint because it has Self or associated type requirements}} - -_ = Z.method() -// expected-error@-1 {{member 'method' cannot be used on value of protocol type 'Z.Protocol'; use a generic constraint instead}} -// expected-error@-2 {{protocol 'Z' can only be used as a generic constraint because it has Self or associated type requirements}} - protocol TestWithAssoc { associatedtype U } @@ -299,3 +281,47 @@ func test_fixit_with_where_clause() { func test_assoc(_: T) {} test_assoc(.intVar) // expected-error {{contextual member reference to static property 'intVar' requires 'Self' constraint in the protocol extension}} } + +// rdar://77700261 - incorrect warning about assuming non-optional base for unresolved member lookup +struct WithShadowedMember : P {} + +extension WithShadowedMember { + static var warnTest: WithShadowedMember { get { WithShadowedMember() } } +} + +extension P where Self == WithShadowedMember { + static var warnTest: WithShadowedMember { get { fatalError() } } +} + +func test_no_warning_about_optional_base() { + func test(_: WithShadowedMember?) {} + + test(.warnTest) // Ok and no warning even though the `warnTest` name is shadowed +} + +// rdar://78425221 - invalid defaulting of literal argument when base is inferred from protocol + +protocol Style {} + +struct FormatString : ExpressibleByStringInterpolation { + init(stringLiteral: String) {} +} + +struct Number : ExpressibleByIntegerLiteral { + init(integerLiteral: Int) {} +} + +struct TestStyle: Style { + public init(format: FormatString) { + } +} + +extension Style where Self == TestStyle { + static func formattedString(format: FormatString) -> TestStyle { fatalError() } + static func number(_: Number) -> TestStyle { fatalError() } +} + +func acceptStyle(_: S) {} + +acceptStyle(.formattedString(format: "hi")) // Ok +acceptStyle(.number(42)) // Ok diff --git a/test/Constraints/subscript.swift b/test/Constraints/subscript.swift index 5c4c830474867..00db94819bc27 100644 --- a/test/Constraints/subscript.swift +++ b/test/Constraints/subscript.swift @@ -217,14 +217,19 @@ func rdar61084565() { a[] // expected-error {{value of type 'Foo' has no subscripts}} } +// Note: In Swift >= 6 mode this would become an error. func test_subscript_accepts_type_name_argument() { struct A { subscript(a: A.Type) -> Int { get { 42 } } } - func test(a: A?) { - let _ = a?[A] // expected-error {{expected member name or constructor call after type name}} - // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{17-17=()}} - // expected-note@-2 {{use '.self' to reference the type object}} {{17-17=.self}} + func test(a: A, optA: A?) { + let _ = a[A] // expected-warning {{expected member name or constructor call after type name}} + // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{16-16=()}} + // expected-note@-2 {{use '.self' to reference the type object}} {{16-16=.self}} + + let _ = optA?[A] // expected-warning {{expected member name or constructor call after type name}} + // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{20-20=()}} + // expected-note@-2 {{use '.self' to reference the type object}} {{20-20=.self}} } } diff --git a/test/Constraints/subscript_swift6.swift b/test/Constraints/subscript_swift6.swift new file mode 100644 index 0000000000000..a3fcf09ca3c97 --- /dev/null +++ b/test/Constraints/subscript_swift6.swift @@ -0,0 +1,20 @@ +// RUN: %target-typecheck-verify-swift -swift-version 6 + +// REQUIRES: asserts + +// Note: In Swift >= 6 mode this would become an error. +func test_subscript_accepts_type_name_argument() { + struct A { + subscript(a: A.Type) -> Int { get { 42 } } + } + + func test(a: A, optA: A?) { + let _ = a[A] // expected-error {{expected member name or constructor call after type name}} + // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{16-16=()}} + // expected-note@-2 {{use '.self' to reference the type object}} {{16-16=.self}} + + let _ = optA?[A] // expected-error {{expected member name or constructor call after type name}} + // expected-note@-1 {{add arguments after the type to construct a value of the type}} {{20-20=()}} + // expected-note@-2 {{use '.self' to reference the type object}} {{20-20=.self}} + } +} diff --git a/test/Constraints/trailing_closures_objc.swift b/test/Constraints/trailing_closures_objc.swift deleted file mode 100644 index 8cd35f535f5fc..0000000000000 --- a/test/Constraints/trailing_closures_objc.swift +++ /dev/null @@ -1,30 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -// REQUIRES: rdar66110025 -// REQUIRES: objc_interop -// REQUIRES: OS=macosx - -import Foundation -import AVFoundation -import AppKit - -func foo(options: [AVMediaSelectionOption]) { - let menuItems: [NSMenuItem] = options.map { (option: AVMediaSelectionOption) in - NSMenuItem(title: option.displayName, action: #selector(NSViewController.respondToMediaOptionSelection(from:)), keyEquivalent: "") - // expected-error@-1 {{type 'NSViewController' has no member 'respondToMediaOptionSelection(from:)'}} - } -} - -func rdar28004686(a: [IndexPath]) { - _ = a.sorted { (lhs: NSIndexPath, rhs: NSIndexPath) -> Bool in true } - // expected-error@-1 {{cannot convert value of type '(NSIndexPath, NSIndexPath) -> Bool' to expected argument type '(IndexPath, IndexPath) throws -> Bool'}} -} - -class Test: NSObject { - var categories : NSArray? - func rdar28012273() { - let categories = ["hello", "world"] - self.categories = categories.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedDescending } - // expected-error@-1 {{cannot assign value of type '[String]' to type 'NSArray'}} {{121-121= as NSArray}} - } -} diff --git a/test/DebugInfo/ClangModuleBreadcrumbs.swift b/test/DebugInfo/ClangModuleBreadcrumbs.swift index 91b8489600eb5..92e2704268e22 100644 --- a/test/DebugInfo/ClangModuleBreadcrumbs.swift +++ b/test/DebugInfo/ClangModuleBreadcrumbs.swift @@ -7,6 +7,8 @@ import ClangModule.SubModule import OtherClangModule.SubModule +let _ = someFunc(0) + // Check for Clang module breadcrumbs. // CHECK: !DICompileUnit(language: DW_LANG_{{ObjC|C99}},{{.*}} producer: "{{.*}}Swift // CHECK-SAME: ClangModule @@ -16,5 +18,9 @@ import OtherClangModule.SubModule // CHECK-SAME: OtherClangModule // CHECK-SAME: dwoId: +// CHECK: !DICompileUnit(language: DW_LANG_{{ObjC|C99}},{{.*}} producer: "{{.*}}clang +// CHECK-SAME: ClangModule +// CHECK-SAME: dwoId: + // NONE: DICompileUnit({{.*}} // NONE-NOT: DICompileUnit({{.*}}ClangModule diff --git a/test/DebugInfo/Inputs/ClangModule.h b/test/DebugInfo/Inputs/ClangModule.h index e51332fba3522..9727a4154759f 100644 --- a/test/DebugInfo/Inputs/ClangModule.h +++ b/test/DebugInfo/Inputs/ClangModule.h @@ -1,3 +1,5 @@ struct Foo {}; typedef struct Foo s_Foo; + +static __inline__ __attribute__((always_inline)) int someFunc(int x) { return x; } diff --git a/test/DebugInfo/ResilientSize.swift b/test/DebugInfo/ResilientSize.swift new file mode 100644 index 0000000000000..561d29b2f4e5f --- /dev/null +++ b/test/DebugInfo/ResilientSize.swift @@ -0,0 +1,18 @@ + +// RUN: %empty-directory(%t) +// +// Compile the external swift module. +// RUN: %target-swift-frontend -g -emit-module -enable-library-evolution \ +// RUN: -emit-module-path=%t/resilient_protocol.swiftmodule \ +// RUN: -module-name=resilient_protocol %S/../Inputs/resilient_protocol.swift +// +// RUN: %target-swift-frontend -g -I %t -emit-ir %s -o - | %FileCheck %s +import resilient_protocol + +public struct S : OtherResilientProtocol { + public var v : T + public func requirement() -> Int { return 42 } +} + +// Test that this type has no size (instead of an incorrect size of 0). +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "S", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, flags: DIFlagFwdDecl, runtimeLang: DW_LANG_Swift, templateParams: !{{[0-9]+}}, identifier: "$s13ResilientSize1SVyxGD") diff --git a/test/DebugInfo/async-args.swift b/test/DebugInfo/async-args.swift index 8bc223b3e093c..dd41325c8cda8 100644 --- a/test/DebugInfo/async-args.swift +++ b/test/DebugInfo/async-args.swift @@ -1,5 +1,5 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - \ -// RUN: -module-name M -enable-experimental-concurrency \ +// RUN: -module-name M -disable-availability-checking \ // RUN: -parse-as-library | %FileCheck %s // REQUIRES: concurrency diff --git a/test/DebugInfo/async-boxed-arg.swift b/test/DebugInfo/async-boxed-arg.swift index 47c3e79d146f1..f6a689048862e 100644 --- a/test/DebugInfo/async-boxed-arg.swift +++ b/test/DebugInfo/async-boxed-arg.swift @@ -1,8 +1,8 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - -parse-as-library \ -// RUN: -module-name M -enable-experimental-concurrency | %FileCheck %s --dump-input always +// RUN: -module-name M -disable-availability-checking | %FileCheck %s --dump-input always // REQUIRES: concurrency -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension Collection where Element: Sendable { public func f() async throws { return try await withThrowingTaskGroup(of: Element.self) { group in diff --git a/test/DebugInfo/async-direct-arg.swift b/test/DebugInfo/async-direct-arg.swift index 3ff071b4137ab..381c575530788 100644 --- a/test/DebugInfo/async-direct-arg.swift +++ b/test/DebugInfo/async-direct-arg.swift @@ -1,5 +1,5 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - \ -// RUN: -module-name a -enable-experimental-concurrency \ +// RUN: -module-name a -disable-availability-checking \ // RUN: -parse-as-library | %FileCheck %s --check-prefix=CHECK // REQUIRES: concurrency diff --git a/test/DebugInfo/async-let-await.swift b/test/DebugInfo/async-let-await.swift index 97611bbba2a8e..37450e90685b1 100644 --- a/test/DebugInfo/async-let-await.swift +++ b/test/DebugInfo/async-let-await.swift @@ -1,8 +1,7 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - \ -// RUN: -module-name M -enable-experimental-concurrency \ +// RUN: -module-name M -disable-availability-checking \ // RUN: -parse-as-library | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize // REQUIRES: concurrency -// UNSUPPORTED: CPU=arm64e public func getVegetables() async -> [String] { return ["leek", "carrot"] diff --git a/test/DebugInfo/async-lifetime-extension.swift b/test/DebugInfo/async-lifetime-extension.swift index 6a6b2c8bb898a..4006e0bd371d5 100644 --- a/test/DebugInfo/async-lifetime-extension.swift +++ b/test/DebugInfo/async-lifetime-extension.swift @@ -1,10 +1,8 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - \ -// RUN: -module-name a -enable-experimental-concurrency \ +// RUN: -module-name a -disable-availability-checking \ // RUN: | %FileCheck %s --check-prefix=CHECK // REQUIRES: concurrency -// UNSUPPORTED: CPU=arm64e - // Test that lifetime extension preserves a dbg.declare for "n" in the resume // funclet. @@ -12,14 +10,14 @@ // CHECK-NEXT: entryresume.0: // CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[RHS:[0-9]+]], {{.*}}!DIExpression(DW_OP // CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[LHS:[0-9]+]], {{.*}}!DIExpression(DW_OP -// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[N:[0-9]+]], {{.*}}!DIExpression(DW_OP // CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[R:[0-9]+]], {{.*}}!DIExpression(DW_OP +// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[N:[0-9]+]], {{.*}}!DIExpression(DW_OP // CHECK-NOT: {{ ret }} // CHECK: call void asm sideeffect "" // CHECK: ![[RHS]] = !DILocalVariable(name: "rhs" // CHECK: ![[LHS]] = !DILocalVariable(name: "lhs" -// CHECK: ![[N]] = !DILocalVariable(name: "n" // CHECK: ![[R]] = !DILocalVariable(name: "retval" +// CHECK: ![[N]] = !DILocalVariable(name: "n" public func fibo(_ n: Int) async -> Int { var retval = n if retval < 2 { return 1 } diff --git a/test/DebugInfo/async-local-var.swift b/test/DebugInfo/async-local-var.swift index 081054e0e03bb..83aeed47ee242 100644 --- a/test/DebugInfo/async-local-var.swift +++ b/test/DebugInfo/async-local-var.swift @@ -1,10 +1,8 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - \ -// RUN: -module-name a -enable-experimental-concurrency \ +// RUN: -module-name a -disable-availability-checking \ // RUN: | %FileCheck %s --check-prefix=CHECK // REQUIRES: concurrency -// UNSUPPORTED: CPU=arm64e - func getString() async -> String { return "" } diff --git a/test/DebugInfo/debug_info_expression.sil b/test/DebugInfo/debug_info_expression.sil new file mode 100644 index 0000000000000..c7104f045545a --- /dev/null +++ b/test/DebugInfo/debug_info_expression.sil @@ -0,0 +1,69 @@ +// RUN: %target-swift-frontend %s -sil-verify-all -g -emit-sil -o - | %FileCheck --check-prefix=CHECK-SIL %s +// RUN: %target-swift-frontend -disable-debugger-shadow-copies -primary-file %s -emit-ir -g -o - | %FileCheck %s +import Builtin +import Swift + +struct MyStruct { + var x: Builtin.Int64 + var y: Builtin.Int64 +} + +struct SmallStruct { + var z : Builtin.Int64 +} + +sil_scope 1 { loc "file.swift":7:6 parent @test_fragment : $@convention(thin) () -> () } + +// Testing op_fragment w/ debug_value_addr +sil hidden @test_fragment : $@convention(thin) () -> () { +bb0: + %2 = alloc_stack $MyStruct, var, name "my_struct", loc "file.swift":8:9, scope 1 + // CHECK: %[[MY_STRUCT:.+]] = alloca %{{.*}}MyStruct + // CHECK: llvm.dbg.declare(metadata {{.*}}* %[[MY_STRUCT]], metadata ![[VAR_DECL_MD:[0-9]+]] + // CHECK: %[[SMALL_STRUCT:.+]] = alloca %{{.*}}SmallStruct + // CHECK: llvm.dbg.declare(metadata {{.*}}* %[[SMALL_STRUCT]], metadata ![[SMALL_VAR_DECL_MD:[0-9]+]] + %3 = struct_element_addr %2 : $*MyStruct, #MyStruct.x, loc "file.swift":9:17, scope 1 + // CHECK: %[[FIELD_X:.*]] = getelementptr {{.*}} %[[MY_STRUCT]] + // CHECK-SIL: debug_value %{{[0-9]+}} : $*Builtin.Int64 + // CHECK-SIL-SAME: (name "my_struct", loc "file.swift":8:9, scope {{[0-9]+}}) + // CHECK-SIL-SAME type $MyStruct, expr op_deref:op_fragment:#MyStruct.x + debug_value %3 : $*Builtin.Int64, var, (name "my_struct", loc "file.swift":8:9, scope 1), type $MyStruct, expr op_deref:op_fragment:#MyStruct.x, loc "file.swift":9:17, scope 1 + // CHECK: llvm.dbg.value(metadata {{.*}}* %[[FIELD_X]], metadata ![[VAR_DECL_MD]] + // CHECK-SAME: !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 64) + // CHECK-NOT: ), !dbg ![[VAR_DECL_MD]] + + %4 = alloc_stack $SmallStruct, var, name "small_struct", loc "file.swift":10:11, scope 1 + %5 = struct_element_addr %4 : $*SmallStruct, #SmallStruct.z, loc "file.swift":11:13, scope 1 + // CHECK: %[[FIELD_Z:.*]] = getelementptr {{.*}} %[[SMALL_STRUCT]] + // If the fragment covers the whole struct, we're not generating the + // DW_OP_LLVM_fragment part. + // CHECK: llvm.dbg.value(metadata {{.*}}* %[[FIELD_Z]], metadata ![[SMALL_VAR_DECL_MD]] + // CHECK-SAME: !DIExpression(DW_OP_deref) + debug_value %5 : $*Builtin.Int64, var, (name "small_struct", loc "file.swift":10:11, scope 1), type $SmallStruct, expr op_deref:op_fragment:#SmallStruct.z, loc "file.swift":11:13, scope 1 + dealloc_stack %4 : $*SmallStruct + + dealloc_stack %2 : $*MyStruct + %r = tuple() + return %r : $() +} + +sil_scope 2 { loc "file.swift":14:6 parent @test_alloc_stack : $@convention(thin) () -> () } + +// Testing di-expression w/ alloc_stack +sil hidden @test_alloc_stack : $@convention(thin) () -> () { +bb0: + %my_struct = alloc_stack $MyStruct, var, name "my_struct", loc "file.swift":15:9, scope 2 + // CHECK: %[[MY_STRUCT:.+]] = alloca %{{.*}}MyStruct + // CHECK: llvm.dbg.declare(metadata {{.*}}* %[[MY_STRUCT]], metadata ![[VAR_DECL_MD:[0-9]+]] + // CHECK-SIL: alloc_stack $Int, var + // CHECK-SIL-SAME: (name "my_struct", loc "file.swift":15:9, scope {{[0-9]+}}) + // CHECK-SIL-SAME: type $MyStruct, expr op_fragment:#MyStruct.x + %field_x = alloc_stack $Int, var, (name "my_struct", loc "file.swift":15:9, scope 2), type $MyStruct, expr op_fragment:#MyStruct.x, loc "file.swift":16:17, scope 2 + // CHECK: %[[FIELD_X:.+]] = alloca %TSi + // CHECK: llvm.dbg.declare(metadata %TSi* %[[FIELD_X]], metadata ![[VAR_DECL_MD]] + // CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 64) + dealloc_stack %field_x : $*Int + dealloc_stack %my_struct: $*MyStruct + %r = tuple() + return %r : $() +} diff --git a/test/DebugInfo/debug_scope_propagate.swift b/test/DebugInfo/debug_scope_propagate.swift new file mode 100644 index 0000000000000..248a82f79abf6 --- /dev/null +++ b/test/DebugInfo/debug_scope_propagate.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -primary-file %s -g -O -emit-sil | %FileCheck %s + +public enum BenchmarkCategory : String { + case validation + case api, Array, String, Dictionary, Codable, Set, Data, IndexPath, SIMD +} + +// Similar to `DebugInfo/verifier_debug_scope.sil`, in this case the cloner +// should correctly map the scope of debug variable. +// CHECK-LABEL: sil {{.*}} @{{.*}}__derived_enum_equalsySbAC_ACtFZTf4nnd_n +// CHECK: debug_value %{{.*}} : $Builtin.Int{{[0-9]+}}, var, (name "index_a" +// CHECK-SAME: , scope [[VAR_SCOPE:[0-9]+]]), +// CHECK-SAME: , scope [[VAR_SCOPE]] diff --git a/test/DebugInfo/debug_value_addr.swift b/test/DebugInfo/debug_value_addr.swift index 896d8aa23301a..1d6ac143fd080 100644 --- a/test/DebugInfo/debug_value_addr.swift +++ b/test/DebugInfo/debug_value_addr.swift @@ -5,7 +5,7 @@ // instructions. // CHECK-SIL: sil hidden @$s16debug_value_addr4testyyxlF -// CHECK-SIL: debug_value_addr %0 : $*T, let, name "t" +// CHECK-SIL: debug_value %0 : $*T, let, name "t", {{.*}}, expr op_deref // CHECK: define {{.*}}$s16debug_value_addr4testyyxlF // CHECK: entry: diff --git a/test/DebugInfo/debug_variable.sil b/test/DebugInfo/debug_variable.sil new file mode 100644 index 0000000000000..0041930374a7d --- /dev/null +++ b/test/DebugInfo/debug_variable.sil @@ -0,0 +1,25 @@ +// RUN: %target-swiftc_driver -g -emit-ir %s | %FileCheck %s +sil_stage canonical + +import Builtin +import Swift + +sil_scope 2 { loc "simple.swift":1:2 parent @test_debug_value : $@convention(thin) (Int) -> () } + +// SR-14868: Incorrect source location on `llvm.dbg.declare` when the input +// is SIL file. + +// CHECK: @test_debug_value +// CHECK-SAME: !dbg ![[FUNC_DI:[0-9]+]] +sil hidden @test_debug_value : $@convention(thin) (Int) -> () { +bb0(%0 : $Int): + // CHECK: @llvm.dbg.declare(metadata i{{[0-9]+}}* + // CHECK-SAME: metadata ![[VAR_DI:[0-9]+]] + // CHECK-SAME: ), !dbg ![[LOC_DI:[0-9]+]] + debug_value %0 : $Int, let, name "x", argno 1, loc "simple.swift":3:4, scope 2 + %1 = tuple () + return %1 : $() +} + +// CHECK: ![[VAR_DI]] = !DILocalVariable(name: "x", arg: 1 +// CHECK: ![[LOC_DI]] = !DILocation(line: 3, column: 4, scope: ![[FUNC_DI]] diff --git a/test/DebugInfo/enum.swift b/test/DebugInfo/enum.swift index b1894765624e7..c4de4a290e92c 100644 --- a/test/DebugInfo/enum.swift +++ b/test/DebugInfo/enum.swift @@ -59,7 +59,7 @@ public func foo(_ empty : Nothing) { } // CHECK-SAME: {{.*}}identifier: "$s4enum4RoseOyxG{{z?}}D") enum Rose { case MkRose(() -> A, () -> [Rose]) - // DWARF: !DICompositeType({{.*}}name: "Rose",{{.*}}identifier: "$s4enum4RoseOyxGD") + // DWARF: !DICompositeType({{.*}}name: "Rose",{{.*}}flags: DIFlagFwdDecl{{.*}}identifier: "$s4enum4RoseOyxGD") case IORose(() -> Rose) } diff --git a/test/DebugInfo/gsil.swift b/test/DebugInfo/gsil.swift deleted file mode 100644 index 380a3ed48d8cc..0000000000000 --- a/test/DebugInfo/gsil.swift +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -O -gsil -Xllvm -sil-print-debuginfo -emit-ir -o %t/out.ir -// RUN: %FileCheck %s < %t/out.ir -// RUN: %FileCheck %s --check-prefix=CHECK_OUT_SIL < %t/out.ir.gsil_0.sil - -// Second test: check that we don't crash with multi-threaded IRGen -// RUN: %target-swift-frontend -c %s %S/Inputs/testclass.swift -wmo -O -num-threads 1 -gsil -o %t/gsil.o -o %t/testclass.o - -// CHECK: !DIFile(filename: "{{.+}}gsil.swift", directory: "{{.+}}") -// CHECK: [[F:![0-9]+]] = !DIFile(filename: "{{.+}}out.ir.gsil_0.sil", -// CHECK: !DISubprogram(linkageName: "$s3out6testityyF", scope: !{{[0-9]+}}, file: [[F]], line: {{[1-9][0-9]+}}, - -// CHECK_OUT_SIL: sil @$s3out6testityyF : $@convention(thin) () -> () { -public func testit() { - print("Hello") -} - diff --git a/test/DebugInfo/implicit_variable.swift b/test/DebugInfo/implicit_variable.swift new file mode 100644 index 0000000000000..d8468fa042b7d --- /dev/null +++ b/test/DebugInfo/implicit_variable.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend -emit-sil -g %s -o %t.sil +// RUN: %FileCheck %s --input-file=%t.sil +// Test the SILParser / SILPrinter +// RUN: %target-swift-frontend -module-name ImplicitVar -emit-sil -g %t.sil | %FileCheck %s + +struct MyStruct { + // Check if the 'implicit' directive for `self` is properly generated (and parsed) + // CHECK: sil {{.*}} @{{.*}}MyStructV5hello + // CHECK: debug_value %0 : $MyStruct + // CHECK-SAME: let, name "self", argno 1, implicit, loc + func hello() {} +} diff --git a/test/DebugInfo/inlined-generics-basic.swift b/test/DebugInfo/inlined-generics-basic.swift index 4675a705b6914..b1d50a588f7e3 100644 --- a/test/DebugInfo/inlined-generics-basic.swift +++ b/test/DebugInfo/inlined-generics-basic.swift @@ -44,7 +44,7 @@ public class C { // IR-LABEL: define {{.*}} @"$s1A1CC1fyyqd__lF" #sourceLocation(file: "f.swift", line: 1) public func f(_ s: S) { - // SIL: debug_value_addr %0 : $*S, let, name "s", argno 1,{{.*}} scope [[F]] + // SIL: debug_value %0 : $*S, let, name "s", argno 1, expr op_deref, {{.*}} scope [[F]] // SIL: function_ref {{.*}}yes{{.*}} scope [[F1G1]] // SIL: function_ref {{.*}}use{{.*}} scope [[F1G3H]] // IR: dbg.value(metadata %swift.type* %S, metadata ![[MD_1_0:[0-9]+]] @@ -65,8 +65,8 @@ public class C { // IR: call {{.*}}3use #sourceLocation(file: "f.swift", line: 3) g(r) - // IR: dbg.declare({{.*}}, metadata ![[GRS_T:[0-9]+]] - // IR: dbg.declare({{.*}}, metadata ![[GRS_U:[0-9]+]] + // IR: dbg.value({{.*}}, metadata ![[GRS_T:[0-9]+]] + // IR: dbg.value({{.*}}, metadata ![[GRS_U:[0-9]+]] // IR: call {{.*}}3use #sourceLocation(file: "f.swift", line: 4) g((r, s)) diff --git a/test/DebugInfo/sil_based_dbg.swift b/test/DebugInfo/sil_based_dbg.swift new file mode 100644 index 0000000000000..5a6da8ce2f25d --- /dev/null +++ b/test/DebugInfo/sil_based_dbg.swift @@ -0,0 +1,39 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -O -sil-based-debuginfo -Xllvm -sil-print-debuginfo -emit-ir -o %t/out.ir +// RUN: %FileCheck %s < %t/out.ir +// RUN: %FileCheck %s --check-prefix=CHECK_OUT_SIL < %t/out.ir.sil_dbg_0.sil + +// Second test: check that we don't crash with multi-threaded IRGen +// RUN: %target-swift-frontend -c %s %S/Inputs/testclass.swift -wmo -O -num-threads 1 -sil-based-debuginfo -o %t/sil_based_dbg.o -o %t/testclass.o + +// CHECK: !DIFile(filename: "{{.+}}sil_based_dbg.swift", directory: "{{.+}}") +// CHECK: [[F:![0-9]+]] = !DIFile(filename: "{{.+}}out.ir.sil_dbg_0.sil", +// CHECK: !DISubprogram(linkageName: "$s3out6testityyF", scope: !{{[0-9]+}}, file: [[F]], line: {{[1-9][0-9]+}}, + +// CHECK_OUT_SIL: sil @$s3out6testityyF : $@convention(thin) () -> () { +public func testit() { + print("Hello") +} + +// We need to remove the debug scope within alloc_stack's auxiliary debug var info +// in sil-based-dbg mode. +// To create something like `alloc_stack ..., (name "foo", loc ..., scope 0)...` +// as our testing input, we're only running SROA over the input swift code. +// RUN: %target-swift-frontend %s -disable-debugger-shadow-copies -emit-sil -g -o %t/stage1.sil +// RUN: %target-sil-opt -sil-print-debuginfo -access-marker-elim -sroa %t/stage1.sil -o %t/stage2.sil +// The verification shouldn't fail +// RUN: %target-swift-frontend %t/stage2.sil -sil-verify-all -sil-based-debuginfo -g -emit-sil -o %t/out.sil +// RUN: %FileCheck %s --check-prefix=CHECK_DBG_SCOPE < %t/out.sil +struct TheStruct { + var the_member : Int +} +// CHECK_DBG_SCOPE-LABEL: sil {{.*}}test_debug_scope +public func test_debug_scope(val : Int) -> Int { + // CHECK_DBG_SCOPE: alloc_stack $Builtin.Int{{[0-9]+}}, var, (name "the_struct", + // CHECK_DBG_SCOPE-SAME: loc + // The auxiliary debug scope should be removed + // CHECK_DBG_SCOPE-NOT: scope {{[0-9]+}}) + var the_struct = TheStruct(the_member: 0) + the_struct.the_member = val + 13 + return the_struct.the_member +} diff --git a/test/DebugInfo/sroa_mem2reg.sil b/test/DebugInfo/sroa_mem2reg.sil new file mode 100644 index 0000000000000..e265ad1390e78 --- /dev/null +++ b/test/DebugInfo/sroa_mem2reg.sil @@ -0,0 +1,89 @@ +// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa %s | %FileCheck --check-prefix=CHECK-SROA %s +// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa -mem2reg %s -o %t.sil +// RUN: %FileCheck --check-prefix=CHECK-MEM2REG %s --input-file=%t.sil +// RUN: %target-swiftc_driver -Xfrontend -disable-debugger-shadow-copies -g -emit-ir %t.sil | %FileCheck --check-prefix=CHECK-IR %s +// RUN: %target-swiftc_driver -Xfrontend -disable-debugger-shadow-copies -g -c %t.sil -o %t.o +// RUN: %llvm-dwarfdump --debug-info %t.o | %FileCheck --check-prefix=CHECK-DWARF %s +sil_stage canonical + +import Builtin +import Swift + +struct MyStruct { + @_hasStorage var x: Int64 { get set } + @_hasStorage var y: Int64 { get set } + init(x: Int64, y: Int64) +} + +sil_scope 1 { loc "sroa.swift":2:8 parent @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct } + +// MyStruct.init(x:y:) +sil hidden @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct { +bb0(%0 : $Int64, %1 : $Int64, %2 : $@thin MyStruct.Type): + %3 = struct $MyStruct (%0 : $Int64, %1 : $Int64), loc "sroa.swift":2:8, scope 1 + return %3 : $MyStruct, loc "sroa.swift":2:8, scope 1 +} // end sil function 'MyStructInit' + +sil_scope 2 { loc "sroa.swift":7:6 parent @foo : $@convention(thin) (Int64, Int64) -> Int64 } + +// foo(in_x:in_y:) +sil hidden @foo : $@convention(thin) (Int64, Int64) -> Int64 { +bb0(%0 : $Int64, %1 : $Int64): + debug_value %0 : $Int64, let, name "in_x", argno 1, loc "sroa.swift":7:10, scope 2 + debug_value %1 : $Int64, let, name "in_y", argno 2, loc "sroa.swift":7:21, scope 2 + %4 = alloc_stack $MyStruct, var, name "my_struct", loc "sroa.swift":8:9, scope 2 + // Make sure SROA propagate the debug info to the splitted alloc_stack instructions + // CHECK-SROA: alloc_stack $Int64, var + // CHECK-SROA-SAME: (name "my_struct", loc "sroa.swift":8:9 + // CHECK-SROA-SAME: type $*MyStruct, expr op_fragment:#MyStruct.x + // CHECK-SROA-SAME: loc "":0:0 + // CHECK-SROA: alloc_stack $Int64, var + // CHECK-SROA-SAME: (name "my_struct", loc "sroa.swift":8:9 + // CHECK-SROA-SAME: type $*MyStruct, expr op_fragment:#MyStruct.y + // CHECK-SROA-SAME: loc "":0:0 + %5 = metatype $@thin MyStruct.Type, loc "sroa.swift":8:21, scope 2 + %6 = integer_literal $Builtin.Int64, 0, loc "sroa.swift":8:33, scope 2 + %7 = struct $Int64 (%6 : $Builtin.Int64), loc "sroa.swift":8:33, scope 2 + %8 = integer_literal $Builtin.Int64, 0, loc "sroa.swift":8:39, scope 2 + %9 = struct $Int64 (%8 : $Builtin.Int64), loc "sroa.swift":8:39, scope 2 + // function_ref MyStruct.init(x:y:) + %10 = function_ref @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct, loc "sroa.swift":8:21, scope 2 + %11 = apply %10(%7, %9, %5) : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct, loc "sroa.swift":8:21, scope 2 + store %11 to %4 : $*MyStruct, loc "sroa.swift":8:21, scope 2 + %13 = struct_element_addr %4 : $*MyStruct, #MyStruct.x, loc "sroa.swift":9:17, scope 2 + // CHECK-MEM2REG: %[[FIELD_X:[0-9]+]] = struct_extract %[[STRUCT:[0-9]+]] : $MyStruct, #MyStruct.x, loc "sroa.swift":8:21 + store %0 to %13 : $*Int64, loc "sroa.swift":9:17, scope 2 + %15 = struct_element_addr %4 : $*MyStruct, #MyStruct.y, loc "sroa.swift":10:17, scope 2 + // CHECK-MEM2REG: %[[FIELD_Y:[0-9]+]] = struct_extract %[[STRUCT]] : $MyStruct, #MyStruct.y, loc "sroa.swift":8:21 + store %1 to %15 : $*Int64, loc "sroa.swift":10:17, scope 2 + // Make sure the struct fields' SSA values are properly connected to the source variables via op_fragment + // CHECK-MEM2REG: debug_value %[[FIELD_X]] : $Int64, var, (name "my_struct", loc "sroa.swift":8:9, scope 2), type $*MyStruct, expr op_fragment:#MyStruct.x + // CHECK-MEM2REG: debug_value %[[FIELD_Y]] : $Int64, var, (name "my_struct", loc "sroa.swift":8:9, scope 2), type $*MyStruct, expr op_fragment:#MyStruct.y + // CHECK-IR: call void @llvm.dbg.value(metadata i64 %0 + // CHECK-IR-SAME: metadata ![[STRUCT_MD:[0-9]+]] + // CHECK-IR-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 64) + // CHECK-IR: call void @llvm.dbg.value(metadata i64 %1 + // CHECK-IR-SAME: metadata ![[STRUCT_MD]] + // CHECK-IR-SAME: !DIExpression(DW_OP_LLVM_fragment, 64, 64) + // Make sure function arguments' SSA values are also properly connected to the source variables + // CHECK-MEM2REG: debug_value %0 : $Int64, var, (name "my_struct", loc "sroa.swift":8:9, scope 2), type $*MyStruct, expr op_fragment:#MyStruct.x + // CHECK-MEM2REG: debug_value %1 : $Int64, var, (name "my_struct", loc "sroa.swift":8:9, scope 2), type $*MyStruct, expr op_fragment:#MyStruct.y + // CHECK-IR: call void @llvm.dbg.value(metadata i64 %0, metadata ![[ARG1_MD:[0-9]+]] + // CHECK-IR: call void @llvm.dbg.value(metadata i64 %1, metadata ![[ARG2_MD:[0-9]+]] + dealloc_stack %4 : $*MyStruct, loc "sroa.swift":8:9, scope 2 + return %0 : $Int64, loc "sroa.swift":11:5, scope 2 +} // end sil function 'foo' + +// CHECK-IR: ![[STRUCT_MD]] = !DILocalVariable(name: "my_struct" +// CHECK-IR-SAME: line: 8 +// CHECK-IR: ![[ARG1_MD]] = !DILocalVariable(name: "in_x", arg: 1 +// CHECK-IR-SAME: line: 7 +// CHECK-IR: ![[ARG2_MD]] = !DILocalVariable(name: "in_y", arg: 2 +// CHECK-IR-SAME: line: 7 + +// CHECK-DWARF-LABEL: DW_AT_name ("foo") +// CHECK-DWARF: DW_TAG_variable +// CHECK-DWARF: DW_AT_name ("my_struct") +// Shouldn't be marked artificial +// CHECK-DWARF-NOT: DW_AT_artificial (true) +// CHECK-DWARF: DW_TAG_{{.*}} diff --git a/test/DebugInfo/verifier_debug_info_expression.sil b/test/DebugInfo/verifier_debug_info_expression.sil new file mode 100644 index 0000000000000..a5ccaf229e472 --- /dev/null +++ b/test/DebugInfo/verifier_debug_info_expression.sil @@ -0,0 +1,21 @@ +// RUN: not --crash %target-swift-frontend %s -sil-verify-all -g -emit-sil +import Builtin +import Swift + +struct MyStruct { + var x: Builtin.Int64 + var y: Builtin.Int64 +} + +sil_scope 1 { loc "file.swift":7:6 parent @test_fragment : $@convention(thin) () -> () } + +sil hidden @test_fragment : $@convention(thin) () -> () { +bb0: + %2 = alloc_stack $MyStruct, var, name "my_struct", loc "file.swift":8:9, scope 1 + %3 = struct_element_addr %2 : $*MyStruct, #MyStruct.x, loc "file.swift":9:17, scope 1 + // every op_fragment should be the last di-expression operand + debug_value %3 : $*Builtin.Int64, var, name "my_struct", expr op_deref:op_fragment:#MyStruct.y:op_fragment:#MyStruct.x + dealloc_stack %2 : $*MyStruct + %r = tuple() + return %r : $() +} diff --git a/test/DebugInfo/verifier_debug_scope.sil b/test/DebugInfo/verifier_debug_scope.sil new file mode 100644 index 0000000000000..d3a44e6c10bda --- /dev/null +++ b/test/DebugInfo/verifier_debug_scope.sil @@ -0,0 +1,17 @@ +// RUN: not --crash %target-swift-frontend %s -g -emit-sil -sil-verify-all +import Swift + +func foo(_ x: Int) +func bar(_ x: Int) + +sil_scope 1 { loc "file.swift":2:6 parent @foo : $@convention(thin) (Int) -> () } +sil_scope 2 { loc "file.swift":6:2 parent @bar : $@convention(thin) (Int) -> () } + +sil hidden @foo : $@convention(thin) (Int) -> () { +bb0(%0 : $Int): + // The scope of the debug variable needs to have the same root function as the + // debug scope on instruction + debug_value %0 : $Int, let, (name "x", loc "file.swift":8:7, scope 1), loc "file.swift":9:4, scope 2 + %r = tuple() + return %r : $() +} diff --git a/test/Demangle/Inputs/bigtype-demangle.txt b/test/Demangle/Inputs/bigtype-demangle.txt new file mode 100644 index 0000000000000..ba71b56ef15a6 --- /dev/null +++ b/test/Demangle/Inputs/bigtype-demangle.txt @@ -0,0 +1 @@ +type metadata for (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((<>))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) diff --git a/test/Demangle/Inputs/bigtype-objcrt.txt b/test/Demangle/Inputs/bigtype-objcrt.txt new file mode 100644 index 0000000000000..c2c9dd783e828 --- /dev/null +++ b/test/Demangle/Inputs/bigtype-objcrt.txt @@ -0,0 +1 @@ +fatal error: too complex to remangle diff --git a/test/Demangle/Inputs/bigtype-remangle.txt b/test/Demangle/Inputs/bigtype-remangle.txt new file mode 100644 index 0000000000000..c2c9dd783e828 --- /dev/null +++ b/test/Demangle/Inputs/bigtype-remangle.txt @@ -0,0 +1 @@ +fatal error: too complex to remangle diff --git a/test/Demangle/Inputs/bigtype.txt b/test/Demangle/Inputs/bigtype.txt new file mode 100644 index 0000000000000..60a17bc800c77 --- /dev/null +++ b/test/Demangle/Inputs/bigtype.txt @@ -0,0 +1 @@ +$sBf32__t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_t_tN diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index e4f2321e5acd2..dfb9ac57766f9 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -12,8 +12,11 @@ _TtBo ---> Builtin.NativeObject _TtBp ---> Builtin.RawPointer _TtBt ---> Builtin.SILToken _TtBv4Bi8_ ---> Builtin.Vec4xInt8 -_TtBv4Bf16_ ---> Builtin.Vec4xFloat16 +_TtBv4Bf16_ ---> Builtin.Vec4xFPIEEE16 _TtBv4Bp ---> Builtin.Vec4xRawPointer +$sBi8_Bv4_ ---> Builtin.Vec4xInt8 +$sBf16_Bv4_ ---> Builtin.Vec4xFPIEEE16 +$sBpBv4_ ---> Builtin.Vec4xRawPointer _TtSa ---> Swift.Array _TtSb ---> Swift.Bool _TtSc ---> Swift.UnicodeScalar @@ -331,7 +334,7 @@ _$S3BBBBi0602365061_ ---> _$S3BBBBi0602365061_ _$S3BBBBv0602365061_ ---> _$S3BBBBv0602365061_ _T0lxxxmmmTk ---> _T0lxxxmmmTk _TtCF4test11doNotCrash1FT_QuL_8MyClass1 ---> MyClass1 #1 in test.doNotCrash1() -> some -$s3Bar3FooVAA5DrinkVyxGs5Error_pSeRzSERzlyShy4AbcdAHO6MemberVGALSeHPAKSeAAyHC_HCg_ALSEHPAKSEAAyHC_HCg0_Iseggozo_SgWOe ---> outlined consume of @escaping @callee_guaranteed @substituted (@guaranteed Bar.Foo) -> (@owned Bar.Drink, @error @owned Swift.Error) for >? +$s3Bar3FooVAA5DrinkVyxGs5Error_pSeRzSERzlyShy4AbcdAHO6MemberVGALSeHPAKSeAAyHC_HCg_ALSEHPAKSEAAyHC_HCg0_Iseggozo_SgWOe ---> outlined consume of (@escaping @callee_guaranteed @substituted (@guaranteed Bar.Foo) -> (@owned Bar.Drink, @error @owned Swift.Error) for >)? $s4Test5ProtoP8IteratorV10collectionAEy_qd__Gqd___tcfc ---> Test.Proto.Iterator.init(collection: A1) -> Test.Proto.Iterator $s4test3fooV4blahyAA1SV1fQryFQOy_Qo_AHF ---> test.foo.blah(< some>>.0) -> < some>>.0 $S3nix8MystructV1xACyxGx_tcfc7MyaliasL_ayx__GD ---> Myalias #1 in nix.Mystruct.init(x: A) -> nix.Mystruct @@ -403,3 +406,12 @@ $s4test3fooyyS2f_SfYkztYjrXEF ---> test.foo(@differentiable(reverse) (Swift.Floa $s4test3fooyyS2f_SfYkntYjrXEF ---> test.foo(@differentiable(reverse) (Swift.Float, __owned @noDerivative Swift.Float) -> Swift.Float) -> () $s4test3fooyyS2f_SfYktYjrXEF ---> test.foo(@differentiable(reverse) (Swift.Float, @noDerivative Swift.Float) -> Swift.Float) -> () $s4test3fooyyS2f_SfYktYaYbYjrXEF ---> test.foo(@differentiable(reverse) @Sendable (Swift.Float, @noDerivative Swift.Float) async -> Swift.Float) -> () +$sScA ---> Swift.Actor +$sScGySiG ---> Swift.TaskGroup +$s4test10returnsOptyxycSgxyScMYccSglF ---> test.returnsOpt((@Swift.MainActor () -> A)?) -> (() -> A)? +$sSvSgA3ASbIetCyyd_SgSbIetCyyyd_SgD ---> (@escaping @convention(thin) @convention(c) (@unowned Swift.UnsafeMutableRawPointer?, @unowned Swift.UnsafeMutableRawPointer?, @unowned (@escaping @convention(thin) @convention(c) (@unowned Swift.UnsafeMutableRawPointer?, @unowned Swift.UnsafeMutableRawPointer?) -> (@unowned Swift.Bool))?) -> (@unowned Swift.Bool))? +$s4test10returnsOptyxycSgxyScMYccSglF ---> test.returnsOpt((@Swift.MainActor () -> A)?) -> (() -> A)? +$s1t10globalFuncyyAA7MyActorCYiF ---> t.globalFunc(isolated t.MyActor) -> () +$sSIxip6foobarP ---> foobar in Swift.DefaultIndices.subscript : A +$s13__lldb_expr_110$10016c2d8yXZ1B10$10016c2e0LLC ---> __lldb_expr_1.(unknown context at $10016c2d8).(B in $10016c2e0) +$s__TJO ---> $s__TJO diff --git a/test/Demangle/Inputs/objc-getclass.txt b/test/Demangle/Inputs/objc-getclass.txt new file mode 100644 index 0000000000000..5e945f17714ea --- /dev/null +++ b/test/Demangle/Inputs/objc-getclass.txt @@ -0,0 +1,28 @@ +# These were found by fuzzing getObjCClassByMangledName + +# rdar://63485806 +# This results in an abort(), whereas it should be an error; rdar://79725187 +# covers improving error handling; until that's done, disable this test case + +# 3…KySSyGSkySySSGiG3(KˇˇˇˇˇˇˇˇˇˇˇˇˇˇCwKySSiKySS +# SSmSySyySGGSGyGSyySyySySSGGSGyS78iSLccSGSyySSySSGGccLcV1yVS~^§!zzzzzzzzzzzzhzzzzzSLzSEzzzzzzzzzzzzzzzzzxxxxx8K_S0ttnIx4_ +# ˇyySySyySySyGnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnSgZ1laSgSg +# SSx3…KySyySGSSG_S2ItLHPˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇMd7KLPlKSSFTc•OSgS +# 3…KySSiGSeySySSGiGSiySySSGiG3(KˇˇˇˇˇˇˇˇˇˇˇˇˇˇCwKySSiKySS +# 3…KySSiGSyySySSGiG3(KˇˇˇˇˇˇˇˇˇˇˇˇˇˇCwKySSiKySS +# SyySyySSySyyGSyySyyGGSGyGSyySySyySySSGGSGGˇˇˇˇˇS4S_SmˇˇAGmmmmmmmmmtLHPL(LHPTVdLHV + +# rdar://63488139 +1_SxSt_S4KSgS9OSgRSLAPAL + +# rdar://63496478 +BwXp +1TSpXpBOXp +SJSJSFSrSJSKSKSKSKm_tmcXpXpStmcXpXpSE_tmcXpXpmcXpXpStmcXpXpSE_tmcXpBpXp!E_tXpXpStmcXpZpSE_tmcXpXpSE_tmc3 +x_xSx_SxTd_SySyyS6dyGˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBˇˇˇˇˇˇˇˇˇˇXpXpXpf:8–VSBP0 + +# rdar://63410196 +SlSIxip6/XXS*”PLEPÓd}}}}}}} + +# rdar://68449341 +ySfmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmf%mmmmmmmmmmmmmf%w diff --git a/test/Demangle/Inputs/simplified-manglings.txt b/test/Demangle/Inputs/simplified-manglings.txt index 18320c9b08dc9..a419a117135e8 100644 --- a/test/Demangle/Inputs/simplified-manglings.txt +++ b/test/Demangle/Inputs/simplified-manglings.txt @@ -5,7 +5,7 @@ _TtBO ---> Builtin.UnknownObject _TtBo ---> Builtin.NativeObject _TtBp ---> Builtin.RawPointer _TtBv4Bi8_ ---> Builtin.Vec4xInt8 -_TtBv4Bf16_ ---> Builtin.Vec4xFloat16 +_TtBv4Bf16_ ---> Builtin.Vec4xFPIEEE16 _TtBv4Bp ---> Builtin.Vec4xRawPointer _TtSa ---> Array _TtSb ---> Bool diff --git a/test/Demangle/lit.local.cfg b/test/Demangle/lit.local.cfg index a8d933b5bdcfd..f910347d4e877 100644 --- a/test/Demangle/lit.local.cfg +++ b/test/Demangle/lit.local.cfg @@ -1,2 +1,3 @@ # suffixes: A list of file extensions to treat as test files. config.suffixes.add('.test') +config.suffixes.add('.cpp') diff --git a/test/Demangle/objc-getclass.cpp b/test/Demangle/objc-getclass.cpp new file mode 100644 index 0000000000000..58bb4ca9e5ec4 --- /dev/null +++ b/test/Demangle/objc-getclass.cpp @@ -0,0 +1,84 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %s -isysroot %sdk -L%swift_obj_root/lib/swift/%target-sdk-name -lswiftCore -lobjc -o %t/objc-getclass +// RUN: %target-codesign %t/objc-getclass +// RUN: %target-run %t/objc-getclass %S/Inputs/objc-getclass.txt + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +#include +#include +#include +#include +#include +#include + +static BOOL dummyHook(const char * _Nonnull name, + Class _Nullable * _Nonnull outClass) { + return NO; +} + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "usage: objc-getclass \n" + "\n" + "Test demangling class names to get classes.\n"); + return 0; + } + + // Find the class-by-mangled-name hook + objc_hook_getClass getObjCClassByMangledName = NULL; + + if (__builtin_available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *)) { + objc_hook_getClass dummy = NULL; + objc_setHook_getClass(dummyHook, &getObjCClassByMangledName); + objc_setHook_getClass(getObjCClassByMangledName, &dummy); + } else { + fprintf(stderr, "objc-getclass: OS version is too old\n"); + return 0; + } + + // Open the input file + FILE *fp = fopen(argv[1], "rt"); + if (!fp) { + fprintf(stderr, "objc-getclass: unable to open \"%s\" - %s\n", + argv[1], strerror(errno)); + } + + // Input file is a list of manglings; we don't really care what classes they + // resolve to here; this test is about whether or not they actually crash or + // assert. + char *line = NULL; + size_t linecap = 0; + ssize_t linelen = 0; + + while ((linelen = getline(&line, &linecap, fp)) > 0) { + char *mangling = line; + + // Trim whitespace + while (isspace(*mangling)) + ++mangling; + + char *end = line + linelen; + while (end > line && isspace(end[-1])) + --end; + *end = '\0'; + + // Skip comments and blank lines + if (*mangling == '#' || !*mangling) + continue; + + // Try to get a class + Class outClass = nil; + BOOL result = getObjCClassByMangledName(mangling, &outClass); + + if (result) + printf("%s -> %s\n", mangling, class_getName(outClass)); + else + printf("%s not found\n", mangling); + } + + fclose(fp); + + return 0; +} diff --git a/test/Demangle/rdar-82252704.swift b/test/Demangle/rdar-82252704.swift new file mode 100644 index 0000000000000..5c185120d1ec2 --- /dev/null +++ b/test/Demangle/rdar-82252704.swift @@ -0,0 +1,10 @@ +// rdar://82252704 - [SR-15070]: Declaring a class inside a async throws +// function crashes compiler + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -c %s -o %t/test.o + +@available(SwiftStdlib 5.5, *) +func MyFunction() async throws { + class MyClass {} +} diff --git a/test/Demangle/recursion-limit.swift b/test/Demangle/recursion-limit.swift new file mode 100644 index 0000000000000..2b0b977830636 --- /dev/null +++ b/test/Demangle/recursion-limit.swift @@ -0,0 +1,10 @@ +; This is not really a Swift source file: -*- Text -*- + +RUN: swift-demangle < %S/Inputs/bigtype.txt 2>&1 > %t.check +RUN: %diff -u %S/Inputs/bigtype-demangle.txt %t.check + +RUN: swift-demangle -remangle-new < %S/Inputs/bigtype.txt > %t.check 2>&1 || true +RUN: %diff -u %S/Inputs/bigtype-remangle.txt %t.check + +RUN: swift-demangle -remangle-objc-rt < %S/Inputs/bigtype.txt > %t.check 2>&1 || true +RUN: %diff -u %S/Inputs/bigtype-objcrt.txt %t.check diff --git a/test/Demangle/remangle.swift b/test/Demangle/remangle.swift index b75ae3d37645f..b1411bdf7b6de 100644 --- a/test/Demangle/remangle.swift +++ b/test/Demangle/remangle.swift @@ -9,6 +9,17 @@ RUN: diff %t.input %t.output // CHECK: Swift.(Mystruct in _7B40D7ED6632C2BEA2CA3BFFD57E3435) RUN: swift-demangle -remangle-objc-rt '$ss8Mystruct33_7B40D7ED6632C2BEA2CA3BFFD57E3435LLV' | %FileCheck %s +// CHECK-OLD: r in _.?(Swift.UnsafeRawPointer) -> Swift.UnsafeRawPointer +RUN: swift-demangle -remangle-objc-rt '$s1_1?1PSVSVF1rP' | %FileCheck -check-prefix CHECK-OLD %s + +// CHECK-OLD2: Swift.Int.related decl 'B' for protocol self-conformance descriptor for Swift.IteratorProtocol +RUN: swift-demangle -remangle-objc-rt '$sSiStMSLB_p' | %FileCheck -check-prefix CHECK-OLD2 %s + +// CHECK-OLD3: Swift.related decl 'H' for partial apply forwarder +RUN: swift-demangle -remangle-objc-rt '$ssTALHP' | %FileCheck -check-prefix CHECK-OLD3 %s + +// CHECK-OLD4: foobar in Swift.DefaultIndices.subscript : A +RUN: swift-demangle -remangle-objc-rt '$sSIxip6foobarP' | %FileCheck -check-prefix CHECK-OLD4 %s + // CHECK-GENERICEXT: Swift._ContiguousArrayStorage<(extension in Swift):Swift.FlattenSequence>>.Index> RUN: swift-demangle -remangle-objc-rt '$ss23_ContiguousArrayStorageCys15FlattenSequenceVsE5IndexVy24StdlibCollectionUnittest020MinimalBidirectionalH0VyAIySiGG_GGD' | %FileCheck -check-prefix CHECK-GENERICEXT %s - diff --git a/test/Distributed/Inputs/dynamic_replacement_da_decl.swift b/test/Distributed/Inputs/dynamic_replacement_da_decl.swift new file mode 100644 index 0000000000000..b42b4023ada14 --- /dev/null +++ b/test/Distributed/Inputs/dynamic_replacement_da_decl.swift @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import _Distributed + +distributed actor DA { + distributed func hello(other: DA) {} +} + diff --git a/test/Distributed/Inputs/dynamic_replacement_da_extension.swift b/test/Distributed/Inputs/dynamic_replacement_da_extension.swift new file mode 100644 index 0000000000000..160b4fefffd7d --- /dev/null +++ b/test/Distributed/Inputs/dynamic_replacement_da_extension.swift @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import _Distributed + +extension DA { + @_dynamicReplacement(for:_remote_hello(other:)) + nonisolated func _impl_hello(other: DA) async throws {} +} diff --git a/test/Distributed/Runtime/distributed_actor_decode.swift b/test/Distributed/Runtime/distributed_actor_decode.swift new file mode 100644 index 0000000000000..0346176368ed0 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_decode.swift @@ -0,0 +1,218 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor DA: CustomStringConvertible { + nonisolated var description: String { + "DA(\(self.id))" + } +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } + + // Explicit implementations to make our TestEncoder/Decoder simpler + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + self.address = try container.decode(String.self) + print("decode ActorAddress -> \(self)") + } + + func encode(to encoder: Encoder) throws { + print("encode \(self)") + var container = encoder.singleValueContainer() + try container.encode(self.address) + } +} + +@available(SwiftStdlib 5.5, *) +struct FakeTransport: ActorTransport { + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + print("FakeTransport.decodeIdentity from:\(decoder)") + let address = try ActorAddress(from: decoder) + return AnyActorIdentity(address) + } + + func resolve(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act? + where Act: DistributedActor { + print("resolve type:\(actorType), address:\(identity)") + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + let address = ActorAddress(parse: "xxx") + print("assign type:\(actorType), address:\(address)") + return .init(address) + } + + public func actorReady(_ actor: Act) where Act: DistributedActor { + print("ready actor:\(actor), address:\(actor.id)") + } + + func resignIdentity(_ identity: AnyActorIdentity) { + print("resign address:\(identity)") + } +} + +// ==== Test Coding ------------------------------------------------------------ + +@available(SwiftStdlib 5.5, *) +class TestEncoder: Encoder { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + + var data: String? = nil + + init(transport: ActorTransport) { + self.codingPath = [] + self.userInfo = [.actorTransportKey: transport] + } + + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer { + fatalError("Not implemented: \(#function)") + } + + func unkeyedContainer() -> UnkeyedEncodingContainer { + fatalError("Not implemented: \(#function)") + } + + func singleValueContainer() -> SingleValueEncodingContainer { + TestSingleValueEncodingContainer(parent: self) + } + + class TestSingleValueEncodingContainer: SingleValueEncodingContainer { + let parent: TestEncoder + init(parent: TestEncoder) { + self.parent = parent + } + + var codingPath: [CodingKey] = [] + + func encodeNil() throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: Bool) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: String) throws { + + } + func encode(_ value: Double) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: Float) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: Int) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: Int8) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: Int16) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: Int32) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: Int64) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: UInt) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: UInt8) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: UInt16) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: UInt32) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: UInt64) throws { fatalError("Not implemented: \(#function)") } + func encode(_ value: T) throws { + print("encode: \(value)") + if let identity = value as? AnyActorIdentity { + self.parent.data = + (identity.underlying as! ActorAddress).address + } + } + } + + func encode(_ actor: Act) throws -> String { + try actor.encode(to: self) + return self.data! + } +} + +@available(SwiftStdlib 5.5, *) +class TestDecoder: Decoder { + let encoder: TestEncoder + let data: String + + init(encoder: TestEncoder, transport: ActorTransport, data: String) { + self.encoder = encoder + self.userInfo = [.actorTransportKey: transport] + self.data = data + } + + var codingPath: [CodingKey] = [] + var userInfo: [CodingUserInfoKey : Any] + + func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { + fatalError("Not implemented: \(#function)") + } + func unkeyedContainer() throws -> UnkeyedDecodingContainer { + fatalError("Not implemented: \(#function)") + } + func singleValueContainer() throws -> SingleValueDecodingContainer { + TestSingleValueDecodingContainer(parent: self) + } + + class TestSingleValueDecodingContainer: SingleValueDecodingContainer { + let parent: TestDecoder + init(parent: TestDecoder) { + self.parent = parent + } + + var codingPath: [CodingKey] = [] + func decodeNil() -> Bool { fatalError("Not implemented: \(#function)") } + func decode(_ type: Bool.Type) throws -> Bool { fatalError("Not implemented: \(#function)") } + func decode(_ type: String.Type) throws -> String { + print("decode String -> \(self.parent.data)") + return self.parent.data + } + func decode(_ type: Double.Type) throws -> Double { fatalError("Not implemented: \(#function)") } + func decode(_ type: Float.Type) throws -> Float { fatalError("Not implemented: \(#function)") } + func decode(_ type: Int.Type) throws -> Int { fatalError("Not implemented: \(#function)") } + func decode(_ type: Int8.Type) throws -> Int8 { fatalError("Not implemented: \(#function)") } + func decode(_ type: Int16.Type) throws -> Int16 { fatalError("Not implemented: \(#function)") } + func decode(_ type: Int32.Type) throws -> Int32 { fatalError("Not implemented: \(#function)") } + func decode(_ type: Int64.Type) throws -> Int64 { fatalError("Not implemented: \(#function)") } + func decode(_ type: UInt.Type) throws -> UInt { fatalError("Not implemented: \(#function)") } + func decode(_ type: UInt8.Type) throws -> UInt8 { fatalError("Not implemented: \(#function)") } + func decode(_ type: UInt16.Type) throws -> UInt16 { fatalError("Not implemented: \(#function)") } + func decode(_ type: UInt32.Type) throws -> UInt32 { fatalError("Not implemented: \(#function)") } + func decode(_ type: UInt64.Type) throws -> UInt64 { fatalError("Not implemented: \(#function)") } + func decode(_ type: T.Type) throws -> T where T : Decodable { fatalError("Not implemented: \(#function)") } + } +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test() { + let transport = FakeTransport() + + // CHECK: assign type:DA, address:ActorAddress(address: "xxx") + let da = DA(transport: transport) + + // CHECK: encode: AnyActorIdentity(ActorAddress(address: "xxx")) + // CHECK: FakeTransport.decodeIdentity from:main.TestDecoder + let encoder = TestEncoder(transport: transport) + let data = try! encoder.encode(da) + + // CHECK: decode String -> xxx + // CHECK: decode ActorAddress -> ActorAddress(address: "xxx") + let da2 = try! DA(from: TestDecoder(encoder: encoder, transport: transport, data: data)) + + // CHECK: decoded da2: DA(AnyActorIdentity(ActorAddress(address: "xxx"))) + print("decoded da2: \(da2)") +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + test() + } +} diff --git a/test/Distributed/Runtime/distributed_actor_deinit.swift b/test/Distributed/Runtime/distributed_actor_deinit.swift new file mode 100644 index 0000000000000..59480a81a66b9 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_deinit.swift @@ -0,0 +1,163 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// temporary non-support. tracked in rdar://82593574 +// UNSUPPORTED: windows + +import _Distributed + +@available(SwiftStdlib 5.5, *) + actor A {} + +@available(SwiftStdlib 5.5, *) +distributed actor DA { + init(transport: ActorTransport) { + defer { transport.actorReady(self) } + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor DA_userDefined { + init(transport: ActorTransport) { + defer { transport.actorReady(self) } + } + + deinit {} +} + +@available(SwiftStdlib 5.5, *) +distributed actor DA_userDefined2 { + init(transport: ActorTransport) { + defer { transport.actorReady(self) } + } + + deinit { + print("Deinitializing \(self.id)") + return + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor DA_state { + var name = "Hello" + var age = 42 + + init(transport: ActorTransport) { + defer { transport.actorReady(self) } + } + + deinit { + print("Deinitializing \(self.id)") + return + } +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +final class FakeTransport: @unchecked Sendable, ActorTransport { + + var n = 0 + + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + print("decode identity from:\(decoder)") + fatalError("not implemented \(#function)") + } + + func resolve(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act? + where Act: DistributedActor { + print("resolve type:\(actorType), address:\(identity)") + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + n += 1 + let address = ActorAddress(parse: "addr-\(n)") + print("assign type:\(actorType), address:\(address)") + return .init(address) + } + + public func actorReady(_ actor: Act) where Act: DistributedActor { + print("ready actor:\(actor), address:\(actor.id)") + } + + func resignIdentity(_ identity: AnyActorIdentity) { + print("resign address:\(identity)") + } +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test() { + let transport = FakeTransport() + + // no lifecycle things make sense for a normal actor, double check we didn't emit them + print("before A") + _ = A() + print("after A") + // CHECK: before A + // CHECK: after A + + _ = { () -> DA in + DA(transport: transport) + }() + // CHECK: assign type:DA, address:[[ADDRESS:.*]] + // CHECK: ready actor:main.DA, address:AnyActorIdentity(ActorAddress(address: "[[ADDR1:addr-[0-9]]]")) + // CHECK: resign address:AnyActorIdentity(ActorAddress(address: "[[ADDR1]]")) + + _ = { () -> DA_userDefined in + DA_userDefined(transport: transport) + }() + // CHECK: assign type:DA_userDefined, address:[[ADDRESS:.*]] + // CHECK: ready actor:main.DA_userDefined, address:AnyActorIdentity(ActorAddress(address: "[[ADDR2:addr-[0-9]]]")) + // CHECK: resign address:AnyActorIdentity(ActorAddress(address: "[[ADDR2]]")) + + // resign must happen as the _last thing_ after user-deinit completed + _ = { () -> DA_userDefined2 in + DA_userDefined2(transport: transport) + }() + // CHECK: assign type:DA_userDefined2, address:[[ADDRESS:.*]] + // CHECK: ready actor:main.DA_userDefined2, address:AnyActorIdentity(ActorAddress(address: "[[ADDR3:addr-[0-9]]]")) + // CHECK: Deinitializing AnyActorIdentity(ActorAddress(address: "[[ADDR3]]")) + // CHECK-NEXT: resign address:AnyActorIdentity(ActorAddress(address: "[[ADDR3]]")) + + // resign must happen as the _last thing_ after user-deinit completed + _ = { () -> DA_state in + DA_state(transport: transport) + }() + // CHECK: assign type:DA_state, address:[[ADDRESS:.*]] + // CHECK: ready actor:main.DA_state, address:AnyActorIdentity(ActorAddress(address: "[[ADDR4:addr-[0-9]]]")) + // CHECK: Deinitializing AnyActorIdentity(ActorAddress(address: "[[ADDR4]]")) + // CHECK-NEXT: resign address:AnyActorIdentity(ActorAddress(address: "[[ADDR4]]")) + + // a remote actor should not resign it's address, it was never "assigned" it + let address = ActorAddress(parse: "remote-1") + _ = { () -> DA_userDefined2 in + try! DA_userDefined2.resolve(.init(address), using: transport) + }() + // CHECK-NEXT: resolve type:DA_userDefined2, address:AnyActorIdentity(ActorAddress(address: "[[ADDR5:remote-1]]")) + // CHECK-NEXT: Deinitializing +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + test() + } +} diff --git a/test/Distributed/Runtime/distributed_actor_dynamic_remote_func.swift b/test/Distributed/Runtime/distributed_actor_dynamic_remote_func.swift new file mode 100644 index 0000000000000..4f949bb68f58d --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_dynamic_remote_func.swift @@ -0,0 +1,114 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// REQUIRES: rdar78290608 + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor LocalWorker { + distributed func function() async throws -> String { + "local:" + } + + distributed func echo(name: String) async throws -> String { + "local:\(name)" + } +} + +@available(SwiftStdlib 5.5, *) +extension LocalWorker { + @_dynamicReplacement(for: _remote_function()) + // TODO: @_remoteDynamicReplacement(for: function()) - could be a nicer spelling, hiding that we use dynamic under the covers + func _cluster_remote_function() async throws -> String { + "\(#function):" + } + + @_dynamicReplacement(for: _remote_echo(name:)) + // TODO: @_remoteDynamicReplacement(for: hello(name:)) - could be a nicer spelling, hiding that we use dynamic under the covers + func _cluster_remote_echo(name: String) async throws -> String { + "\(#function):\(name)" + } +} + +// ==== Fake Transport --------------------------------------------------------- + + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +struct FakeTransport: ActorTransport { + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + fatalError("not implemented:\(#function)") + } + + func resolve(_ identity: Act.ID, as actorType: Act.Type) + throws -> Act? + where Act: DistributedActor { + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + let id = ActorAddress(parse: "xxx") + print("assign type:\(actorType), id:\(id)") + return .init(id) + } + + func actorReady(_ actor: Act) where Act: DistributedActor { + print("ready actor:\(actor), id:\(actor.id)") + } + + func resignIdentity(_ id: AnyActorIdentity) { + print("ready id:\(id)") + } +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test_local() async throws { + let transport = FakeTransport() + + let worker = LocalWorker(transport: transport) + let x = try await worker.function() + print("call: \(x)") + // CHECK: assign type:LocalWorker, id:[[ADDRESS:.*]] + // CHECK: ready actor:main.LocalWorker, id:AnyActorIdentity([[ADDRESS]]) + // CHECK: call: local: +} + +@available(SwiftStdlib 5.5, *) +func test_remote() async throws { + let address = ActorAddress(parse: "") + let transport = FakeTransport() + + let worker = try LocalWorker(resolve: .init(address), using: transport) + let x = try await worker.function() + print("call: \(x)") + // CHECK: call: _cluster_remote_function(): + + let e = try await worker.echo(name: "Charlie") + print("call: \(e)") + // CHECK: call: _cluster_remote_echo(name:):Charlie +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + try! await test_local() + try! await test_remote() + } +} diff --git a/test/Distributed/Runtime/distributed_actor_identity.swift b/test/Distributed/Runtime/distributed_actor_identity.swift new file mode 100644 index 0000000000000..a3f969b760219 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_identity.swift @@ -0,0 +1,59 @@ +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -Xfrontend -enable-experimental-distributed -parse-as-library) + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import StdlibUnittest +import _Distributed + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity, CustomStringConvertible { + let id: String + var description: Swift.String { + "ActorAddress(id: \(id))" + } +} + +@main struct Main { + static func main() async { + if #available(SwiftStdlib 5.5, *) { + + let ActorIdentityTests = TestSuite("ActorIdentity") + + ActorIdentityTests.test("equality") { + let a = ActorAddress(id: "a") + let b = ActorAddress(id: "b") + + let anyA = AnyActorIdentity(a) + let anyB = AnyActorIdentity(b) + + expectEqual(a, a) + expectEqual(anyA, anyA) + + expectNotEqual(a, b) + expectNotEqual(anyA, anyB) + } + + ActorIdentityTests.test("hash") { + let a = ActorAddress(id: "a") + let b = ActorAddress(id: "b") + + let anyA = AnyActorIdentity(a) + let anyB = AnyActorIdentity(b) + + expectEqual(a.hashValue, a.hashValue) + expectEqual(anyA.hashValue, anyA.hashValue) + + expectNotEqual(a.hashValue, b.hashValue) + expectNotEqual(anyA.hashValue, anyB.hashValue) + } + } + + await runAllTestsAsync() + } +} \ No newline at end of file diff --git a/test/Distributed/Runtime/distributed_actor_init_local.swift b/test/Distributed/Runtime/distributed_actor_init_local.swift new file mode 100644 index 0000000000000..1ece89bddbe04 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_init_local.swift @@ -0,0 +1,76 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// REQUIRES: rdar78290608 + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor LocalWorker { + init(transport: ActorTransport) { + defer { transport.actorReady(self) } // FIXME(distributed): rdar://81783599 this should be injected automatically + } +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address: String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +struct FakeTransport: ActorTransport { + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + fatalError("not implemented:\(#function)") + } + + func resolve(_ identity: AnyActorIdentity, as actorType: Act.Type) + throws -> Act? where Act: DistributedActor { + fatalError("not implemented:\(#function)") + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + let id = ActorAddress(parse: "xxx") + print("assign type:\(actorType), id:\(id)") + return .init(id) + } + + func actorReady(_ actor: Act) where Act: DistributedActor { + print("ready actor:\(actor), id:\(actor.id)") + } + + func resignIdentity(_ id: AnyActorIdentity) { + print("resign id:\(id)") + } +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test() { + let transport = FakeTransport() + + _ = LocalWorker(transport: transport) + // CHECK: assign type:LocalWorker, id:ActorAddress(address: "[[ID:.*]]") + // CHECK: ready actor:main.LocalWorker, id:AnyActorIdentity(ActorAddress(address: "[[ID]]")) + // CHECK: resign id:AnyActorIdentity(ActorAddress(address: "[[ID]]")) +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + test() + } +} diff --git a/test/Distributed/Runtime/distributed_actor_isRemote.swift b/test/Distributed/Runtime/distributed_actor_isRemote.swift new file mode 100644 index 0000000000000..207663f3c1074 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_isRemote.swift @@ -0,0 +1,121 @@ +// RUN: %target-run-simple-swift(-Onone -Xfrontend -g -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// REQUIRES: rdar78290608 + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor SomeSpecificDistributedActor { + distributed func hello() async throws -> String { + "local impl" + } +} + +@available(SwiftStdlib 5.5, *) +extension SomeSpecificDistributedActor { + + @_dynamicReplacement(for: _remote_hello()) + nonisolated func _remote_impl_hello() async throws -> String { + return "remote impl (address: \(self.id))" + } +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct FakeActorID: ActorIdentity { + let id: UInt64 +} + +@available(SwiftStdlib 5.5, *) +enum FakeTransportError: ActorTransportError { + case unsupportedActorIdentity(AnyActorIdentity) +} + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +struct FakeTransport: ActorTransport { + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + fatalError("not implemented:\(#function)") + } + + func resolve(_ identity: AnyActorIdentity, as actorType: Act.Type) + throws -> Act? + where Act: DistributedActor { + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + let id = ActorAddress(parse: "xxx") + print("assignIdentity type:\(actorType), id:\(id)") + return .init(id) + } + + func actorReady(_ actor: Act) where Act: DistributedActor { + print("actorReady actor:\(actor), id:\(actor.id)") + } + + func resignIdentity(_ id: AnyActorIdentity) { + print("resignIdentity id:\(id)") + } +} + +// ==== Execute ---------------------------------------------------------------- + +@_silgen_name("swift_distributed_actor_is_remote") +func __isRemoteActor(_ actor: AnyObject) -> Bool + +func __isLocalActor(_ actor: AnyObject) -> Bool { + return !__isRemoteActor(actor) +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test_remote() async { + let address = ActorAddress(parse: "sact://127.0.0.1/example#1234") + let transport = FakeTransport() + + let local = SomeSpecificDistributedActor(transport: transport) + assert(__isLocalActor(local) == true, "should be local") + assert(__isRemoteActor(local) == false, "should be local") + print("isRemote(local) = \(__isRemoteActor(local))") // CHECK: isRemote(local) = false + print("local.id = \(local.id)") // CHECK: local.id = AnyActorIdentity(ActorAddress(address: "xxx")) + print("local.transport = \(local.actorTransport)") // CHECK: local.transport = FakeTransport() + + // assume it always makes a remote one + let remote = try! SomeSpecificDistributedActor.resolve(.init(address), using: transport) + assert(__isLocalActor(remote) == false, "should be remote") + assert(__isRemoteActor(remote) == true, "should be remote") + print("isRemote(remote) = \(__isRemoteActor(remote))") // CHECK: isRemote(remote) = true + + // Check the id and transport are the right values, and not trash memory + print("remote.id = \(remote.id)") // CHECK: remote.id = AnyActorIdentity(ActorAddress(address: "sact://127.0.0.1/example#1234")) + print("remote.transport = \(remote.actorTransport)") // CHECK: remote.transport = FakeTransport() + + print("done") // CHECK: done +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + await test_remote() + } +} + diff --git a/test/Distributed/Runtime/distributed_actor_local.swift b/test/Distributed/Runtime/distributed_actor_local.swift new file mode 100644 index 0000000000000..404fec5cc8735 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_local.swift @@ -0,0 +1,112 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// REQUIRES: rdar78290608 + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor SomeSpecificDistributedActor { + + distributed func hello() async throws { + print("hello from \(self.id)") + } + + distributed func echo(int: Int) async throws -> Int { + int + } +} + +// ==== Execute ---------------------------------------------------------------- + +@_silgen_name("swift_distributed_actor_is_remote") +func __isRemoteActor(_ actor: AnyObject) -> Bool + +func __isLocalActor(_ actor: AnyObject) -> Bool { + return !__isRemoteActor(actor) +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +struct FakeTransport: ActorTransport { + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + fatalError("not implemented \(#function)") + } + + func resolve(_ identity: Act.ID, as actorType: Act.Type) throws -> Act? + where Act: DistributedActor { + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + .init(ActorAddress(parse: "")) + } + + public func actorReady(_ actor: Act) + where Act: DistributedActor { + print("\(#function):\(actor)") + } + + func resignIdentity(_ id: AnyActorIdentity) {} +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test_initializers() { + let address = ActorAddress(parse: "") + let transport = FakeTransport() + + _ = SomeSpecificDistributedActor(transport: transport) + _ = try! SomeSpecificDistributedActor(resolve: .init(address), using: transport) +} + +@available(SwiftStdlib 5.5, *) +func test_address() { + let transport = FakeTransport() + + let actor = SomeSpecificDistributedActor(transport: transport) + _ = actor.id +} + +@available(SwiftStdlib 5.5, *) +func test_run(transport: FakeTransport) async { + let actor = SomeSpecificDistributedActor(transport: transport) + + print("before") // CHECK: before + try! await actor.hello() + print("after") // CHECK: after +} + +@available(SwiftStdlib 5.5, *) +func test_echo(transport: FakeTransport) async { + let actor = SomeSpecificDistributedActor(transport: transport) + + let echo = try! await actor.echo(int: 42) + print("echo: \(echo)") // CHECK: echo: 42 +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + await test_run(transport: FakeTransport()) + await test_echo(transport: FakeTransport()) + } +} diff --git a/test/Distributed/Runtime/distributed_actor_remote_fieldsDontCrashDeinit.swift b/test/Distributed/Runtime/distributed_actor_remote_fieldsDontCrashDeinit.swift new file mode 100644 index 0000000000000..4f78a0e9174c8 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_remote_fieldsDontCrashDeinit.swift @@ -0,0 +1,106 @@ +// RUN: %target-run-simple-swift(-Onone -Xfrontend -g -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor SomeSpecificDistributedActor { + let name: String + let surname: String + let age: Int + + init(name: String, transport: ActorTransport) { + self.name = name + self.surname = "Surname" + self.age = 42 + } + + deinit { + print("deinit \(self.id)") + } + + distributed func hello() async throws -> String { + "Hello, from \(name)" + } +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct FakeActorID: ActorIdentity { + let id: UInt64 +} + +@available(SwiftStdlib 5.5, *) +enum FakeTransportError: ActorTransportError { + case unsupportedActorIdentity(AnyActorIdentity) +} + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +struct FakeTransport: ActorTransport { + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + fatalError("not implemented:\(#function)") + } + + func resolve(_ identity: AnyActorIdentity, as actorType: Act.Type) + throws -> Act? + where Act: DistributedActor { + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + let id = ActorAddress(parse: "xxx") + print("assignIdentity type:\(actorType), id:\(id)") + return .init(id) + } + + func actorReady(_ actor: Act) where Act: DistributedActor { + print("actorReady actor:\(actor), id:\(actor.id)") + } + + func resignIdentity(_ id: AnyActorIdentity) { + print("resignIdentity id:\(id)") + } +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test_remote() async { + let address = ActorAddress(parse: "sact://127.0.0.1/example#1234") + let transport = FakeTransport() + + var remote: SomeSpecificDistributedActor? = + try! SomeSpecificDistributedActor.resolve(.init(address), using: transport) + // Check the id and transport are the right values, and not trash memory + print("remote.id = \(remote!.id)") // CHECK: remote.id = AnyActorIdentity(ActorAddress(address: "sact://127.0.0.1/example#1234")) + print("remote.transport = \(remote!.actorTransport)") // CHECK: remote.transport = FakeTransport() + + remote = nil // CHECK: deinit AnyActorIdentity(ActorAddress(address: "sact://127.0.0.1/example#1234")) + print("done") // CHECK: done +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + await test_remote() + } +} + diff --git a/test/Distributed/Runtime/distributed_actor_remote_functions.swift b/test/Distributed/Runtime/distributed_actor_remote_functions.swift new file mode 100644 index 0000000000000..a4334a186c6c0 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_remote_functions.swift @@ -0,0 +1,208 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// rdar://77798215 +// UNSUPPORTED: OS=windows-msvc + +// REQUIRES: rdar78290608 + +import _Distributed +import _Concurrency + +struct Boom: Error { + let whoFailed: String + init(_ whoFailed: String) { + self.whoFailed = whoFailed + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor SomeSpecificDistributedActor { + let state: String = "hi there" + + distributed func helloAsyncThrows() async throws -> String { + "local(\(#function))" + } + + distributed func helloAsync() async -> String { + "local(\(#function))" + } + + distributed func helloThrows() throws -> String { + "local(\(#function))" + } + + distributed func hello() -> String { + "local(\(#function))" + } + + // === errors + + distributed func helloThrowsImplBoom() throws -> String { + throw Boom("impl") + } + + distributed func helloThrowsTransportBoom() throws -> String { + "local(\(#function))" + } +} + +@available(SwiftStdlib 5.5, *) +extension SomeSpecificDistributedActor { + @_dynamicReplacement(for:_remote_helloAsyncThrows()) + nonisolated func _remote_impl_helloAsyncThrows() async throws -> String { + "remote(\(#function))" + } + + @_dynamicReplacement(for:_remote_helloAsync()) + nonisolated func _remote_impl_helloAsync() async throws -> String { + "remote(\(#function))" + } + + @_dynamicReplacement(for:_remote_helloThrows()) + nonisolated func _remote_impl_helloThrows() async throws -> String { + "remote(\(#function))" + } + + @_dynamicReplacement(for:_remote_hello()) + nonisolated func _remote_impl_hello() async throws -> String { + "remote(\(#function))" + } + + // === errors + + @_dynamicReplacement(for:_remote_helloThrowsImplBoom()) + nonisolated func _remote_impl_helloThrowsImplBoom() async throws -> String { + "remote(\(#function))" + } + + @_dynamicReplacement(for:_remote_helloThrowsTransportBoom()) + nonisolated func _remote_impl_helloThrowsTransportBoom() async throws -> String { + throw Boom("transport") + } +} + +// ==== Execute ---------------------------------------------------------------- + +@_silgen_name("swift_distributed_actor_is_remote") +func __isRemoteActor(_ actor: AnyObject) -> Bool + +func __isLocalActor(_ actor: AnyObject) -> Bool { + return !__isRemoteActor(actor) +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +struct FakeTransport: ActorTransport { + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + fatalError("not implemented:\(#function)") + } + + func resolve(_ identity: Act.ID, as actorType: Act.Type) + throws -> Act? + where Act: DistributedActor { + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + .init(ActorAddress(parse: "")) + } + + func actorReady(_ actor: Act) where Act: DistributedActor { + } + + func resignIdentity(_ id: AnyActorIdentity) { + } +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test_remote_invoke(address: ActorAddress, transport: ActorTransport) async { + func check(actor: SomeSpecificDistributedActor) async { + let personality = __isRemoteActor(actor) ? "remote" : "local" + + let h1 = try! await actor.helloAsyncThrows() + print("\(personality) - helloAsyncThrows: \(h1)") + + let h2 = try! await actor.helloAsync() + print("\(personality) - helloAsync: \(h2)") + + let h3 = try! await actor.helloThrows() + print("\(personality) - helloThrows: \(h3)") + + let h4 = try! await actor.hello() + print("\(personality) - hello: \(h4)") + + // error throws + if __isRemoteActor(actor) { + do { + _ = try await actor.helloThrowsTransportBoom() + preconditionFailure("helloThrowsTransportBoom: should have thrown") + } catch { + print("\(personality) - helloThrowsTransportBoom: \(error)") + } + } else { + do { + _ = try await actor.helloThrowsImplBoom() + preconditionFailure("helloThrowsImplBoom: Should have thrown") + } catch { + print("\(personality) - helloThrowsImplBoom: \(error)") + } + } + } + + let remote = try! SomeSpecificDistributedActor(resolve: .init(address), using: transport) + assert(__isRemoteActor(remote) == true, "should be remote") + + let local = SomeSpecificDistributedActor(transport: transport) + assert(__isRemoteActor(local) == false, "should be local") + + print("local isRemote: \(__isRemoteActor(local))") + // CHECK: local isRemote: false + await check(actor: local) + // CHECK: local - helloAsyncThrows: local(helloAsyncThrows()) + // CHECK: local - helloAsync: local(helloAsync()) + // CHECK: local - helloThrows: local(helloThrows()) + // CHECK: local - hello: local(hello()) + // CHECK: local - helloThrowsImplBoom: Boom(whoFailed: "impl") + + print("remote isRemote: \(__isRemoteActor(remote))") + // CHECK: remote isRemote: true + await check(actor: remote) + // CHECK: remote - helloAsyncThrows: remote(_remote_impl_helloAsyncThrows()) + // CHECK: remote - helloAsync: remote(_remote_impl_helloAsync()) + // CHECK: remote - helloThrows: remote(_remote_impl_helloThrows()) + // CHECK: remote - hello: remote(_remote_impl_hello()) + // CHECK: remote - helloThrowsTransportBoom: Boom(whoFailed: "transport") + + print(local) + print(remote) +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + let address = ActorAddress(parse: "") + let transport = FakeTransport() + + await test_remote_invoke(address: address, transport: transport) + } +} diff --git a/test/Distributed/Runtime/distributed_actor_remote_retains_transport.swift b/test/Distributed/Runtime/distributed_actor_remote_retains_transport.swift new file mode 100644 index 0000000000000..7432142fc1557 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_remote_retains_transport.swift @@ -0,0 +1,102 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// REQUIRES: rdar78290608 + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor SomeSpecificDistributedActor { + deinit { + print("deinit \(self.id)") + } +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct FakeActorID: ActorIdentity { + let id: UInt64 +} + +@available(SwiftStdlib 5.5, *) +enum FakeTransportError: ActorTransportError { + case unsupportedActorIdentity(AnyActorIdentity) +} + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +final class FakeTransport: ActorTransport { + + deinit { + print("deinit \(self)") + } + + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + fatalError("not implemented:\(#function)") + } + + func resolve(_ identity: AnyActorIdentity, as actorType: Act.Type) + throws -> Act? + where Act: DistributedActor { + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + let id = ActorAddress(parse: "xxx") + print("assignIdentity type:\(actorType), id:\(id)") + return .init(id) + } + + func actorReady(_ actor: Act) where Act: DistributedActor { + print("actorReady actor:\(actor), id:\(actor.id)") + } + + func resignIdentity(_ id: AnyActorIdentity) { + print("resignIdentity id:\(id)") + } +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test_remote() async { + var address = ActorAddress(parse: "sact://127.0.0.1/example#1234") + var transport: ActorTransport? = FakeTransport() + + var remote = try! SomeSpecificDistributedActor.resolve(.init(address), using: transport!) + + transport = nil + print("done") // CHECK: done + + print("remote.id = \(remote.id)") // CHECK: remote.id = AnyActorIdentity(ActorAddress(address: "sact://127.0.0.1/example#1234")) + print("remote.transport = \(remote.actorTransport)") // CHECK: remote.transport = main.FakeTransport + + // only once we exit the function and the remote is released, the transport has no more references + // CHECK: deinit AnyActorIdentity(ActorAddress(address: "sact://127.0.0.1/example#1234")) + // transport must deinit after the last actor using it does deinit + // CHECK: deinit main.FakeTransport +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + await test_remote() + } +} + diff --git a/test/Distributed/Runtime/distributed_no_transport_boom.swift b/test/Distributed/Runtime/distributed_no_transport_boom.swift new file mode 100644 index 0000000000000..f68263795f025 --- /dev/null +++ b/test/Distributed/Runtime/distributed_no_transport_boom.swift @@ -0,0 +1,80 @@ +// RUN: %target-fail-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library %import-libdispatch) 2>&1 | %FileCheck %s +// +// // TODO: could not figure out how to use 'not --crash' it never is used with target-run-simple-swift +// This test is intended to *crash*, so we're using target-fail-simple-swift +// which expects the exit code of the program to be non-zero; +// We then check stderr for the expected error message using filecheck as usual. + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// REQUIRES: rdar78290608 + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor SomeSpecificDistributedActor { + distributed func hello() async throws -> String { + "local impl" + } +} + +// ==== Fake Transport --------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +struct FakeTransport: ActorTransport { + func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity { + fatalError("not implemented:\(#function)") + } + + func resolve(_ identity: Act.ID, as actorType: Act.Type) + throws -> Act? + where Act: DistributedActor { + return nil + } + + func assignIdentity(_ actorType: Act.Type) -> AnyActorIdentity + where Act: DistributedActor { + let id = ActorAddress(parse: "xxx") + print("assign type:\(actorType), id:\(id)") + return .init(id) + } + + func actorReady(_ actor: Act) where Act: DistributedActor { + print("ready actor:\(actor), id:\(actor.id)") + } + + func resignIdentity(_ id: AnyActorIdentity) { + print("ready id:\(id)") + } +} + +// ==== Execute ---------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +func test_remote() async { + let address = ActorAddress(parse: "") + let transport = FakeTransport() + + let remote = try! SomeSpecificDistributedActor(resolve: .init(address), using: transport) + _ = try! await remote.hello() // let it crash! + + // CHECK: SOURCE_DIR/test/Distributed/Runtime/distributed_no_transport_boom.swift:18: Fatal error: Invoked remote placeholder function '_remote_hello' on remote distributed actor of type 'main.SomeSpecificDistributedActor'. Configure an appropriate 'ActorTransport' for this actor to resolve this error (e.g. by depending on some specific transport library). +} + +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + await test_remote() + } +} + diff --git a/test/Distributed/actor_protocols.swift b/test/Distributed/actor_protocols.swift new file mode 100644 index 0000000000000..38651e8fe42ed --- /dev/null +++ b/test/Distributed/actor_protocols.swift @@ -0,0 +1,86 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +// ==== ----------------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +actor A: Actor {} // ok + +@available(SwiftStdlib 5.5, *) +class C: Actor, UnsafeSendable { + // expected-error@-1{{non-actor type 'C' cannot conform to the 'Actor' protocol}} {{1-6=actor}} + // expected-warning@-2{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}} + nonisolated var unownedExecutor: UnownedSerialExecutor { + fatalError() + } +} + +@available(SwiftStdlib 5.5, *) +struct S: Actor { + // expected-error@-1{{non-class type 'S' cannot conform to class protocol 'Actor'}} + nonisolated var unownedExecutor: UnownedSerialExecutor { + fatalError() + } +} + +@available(SwiftStdlib 5.5, *) +struct E: Actor { + // expected-error@-1{{non-class type 'E' cannot conform to class protocol 'Actor'}} + nonisolated var unownedExecutor: UnownedSerialExecutor { + fatalError() + } +} + +// ==== ----------------------------------------------------------------------- + +@available(SwiftStdlib 5.5, *) +distributed actor DA: DistributedActor {} // ok + +@available(SwiftStdlib 5.5, *) +actor A2: DistributedActor { + // expected-error@-1{{non-distributed actor type 'A2' cannot conform to the 'DistributedActor' protocol}} {{1-1=distributed }} + nonisolated var id: AnyActorIdentity { + fatalError() + } + nonisolated var actorTransport: ActorTransport { + fatalError() + } + + init(transport: ActorTransport) { + fatalError() + } + + static func resolve(_ identity: AnyActorIdentity, using transport: ActorTransport) throws -> Self { + fatalError() + } +} + +@available(SwiftStdlib 5.5, *) +class C2: DistributedActor { + // expected-error@-1{{non-actor type 'C2' cannot conform to the 'Actor' protocol}} + // expected-error@-2{{non-final class 'C2' cannot conform to `Sendable`; use `@unchecked Sendable`}} + nonisolated var id: AnyActorIdentity { + fatalError() + } + nonisolated var actorTransport: ActorTransport { + fatalError() + } + + required init(transport: ActorTransport) { + fatalError() + } + static func resolve(_ identity: AnyActorIdentity, using transport: ActorTransport) throws -> Self { + fatalError() + } +} + +@available(SwiftStdlib 5.5, *) +struct S2: DistributedActor { + // expected-error@-1{{non-class type 'S2' cannot conform to class protocol 'DistributedActor'}} + // expected-error@-2{{non-class type 'S2' cannot conform to class protocol 'AnyActor'}} + // expected-error@-3{{type 'S2' does not conform to protocol 'Identifiable'}} +} + diff --git a/test/Distributed/distributed_actor_basic.swift b/test/Distributed/distributed_actor_basic.swift new file mode 100644 index 0000000000000..772a1a8d3cb1e --- /dev/null +++ b/test/Distributed/distributed_actor_basic.swift @@ -0,0 +1,41 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor DA { +} + +@available(SwiftStdlib 5.5, *) +distributed actor First { + distributed func one(second: Second) async throws { + try await second.two(first: self, second: second) + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor Second { + distributed func two(first: First, second: Second) async { + try! await first.one(second: self) + } +} + +// ==== ------------------------------------------------------------------------ + +@available(SwiftStdlib 5.5, *) +extension First { + @_dynamicReplacement (for :_remote_one(second:)) + nonisolated func _impl_one(second: Second) async throws { + fatalError() + } +} + +@available(SwiftStdlib 5.5, *) +extension Second { + @_dynamicReplacement (for :_remote_two(first:second:)) + nonisolated func _impl_two(first: First, second: Second) async throws { + fatalError() + } +} \ No newline at end of file diff --git a/test/Distributed/distributed_actor_cannot_be_downcast_to_actor.swift b/test/Distributed/distributed_actor_cannot_be_downcast_to_actor.swift new file mode 100644 index 0000000000000..aea36f2fef7bb --- /dev/null +++ b/test/Distributed/distributed_actor_cannot_be_downcast_to_actor.swift @@ -0,0 +1,27 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(SwiftStdlib 5.5, *) +extension Actor { + func f() -> String { "Life is Study!" } +} + +@available(SwiftStdlib 5.5, *) +func g(a: A) async { // expected-note{{where 'A' = 'MA'}} + print(await a.f()) +} + +@available(SwiftStdlib 5.5, *) +distributed actor MA { +} + +@available(SwiftStdlib 5.5, *) +func h(ma: MA) async { + // this would have been a bug, a non distributed function might have been called here, + // so we must not allow for it, because if the actor was remote calling a non-distributed func + // would result in a hard crash (as there is no local actor to safely call the function on). + await g(a: ma) // expected-error{{global function 'g(a:)' requires that 'MA' conform to 'Actor'}} +} \ No newline at end of file diff --git a/test/Distributed/distributed_actor_dynamic_replacement.swift b/test/Distributed/distributed_actor_dynamic_replacement.swift new file mode 100644 index 0000000000000..4244d3bb53e8d --- /dev/null +++ b/test/Distributed/distributed_actor_dynamic_replacement.swift @@ -0,0 +1,10 @@ +// RUN: %target-swift-frontend -disable-availability-checking -enable-experimental-distributed -c -enable-batch-mode -module-name foo -primary-file %S/Inputs/dynamic_replacement_da_extension.swift -primary-file %S/Inputs/dynamic_replacement_da_decl.swift + +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +func runtimeReceive(da: DA) async throws { + try await da.hello(other:da) +} diff --git a/test/Distributed/distributed_actor_func_implicitly_async_throws.swift b/test/Distributed/distributed_actor_func_implicitly_async_throws.swift new file mode 100644 index 0000000000000..174fa6e659833 --- /dev/null +++ b/test/Distributed/distributed_actor_func_implicitly_async_throws.swift @@ -0,0 +1,95 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor D { + + func hello() {} // expected-note{{only 'distributed' functions can be called from outside the distributed actor}} + func helloAsync() async {} // expected-note{{only 'distributed' functions can be called from outside the distributed actor}} + func helloAsyncThrows() async throws {} // expected-note{{only 'distributed' functions can be called from outside the distributed actor}} + + distributed func distHello() { } // ok + distributed func distHelloAsync() async { } // ok + distributed func distHelloThrows() throws { } // ok + distributed func distHelloAsyncThrows() async throws { } // ok +} + +@available(SwiftStdlib 5.5, *) +func test_not_distributed_funcs(distributed: D) async { + distributed.hello() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + distributed.helloAsync() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + // expected-error@-1{{expression is 'async' but is not marked with 'await'}} + // expected-note@-2{{call is 'async'}} + // {{expression is 'async' but is not marked with 'await'}}{{7-7=await }} + distributed.helloAsyncThrows() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + // expected-error@-1{{expression is 'async' but is not marked with 'await'}} // TODO: no need to diagnose this, it is impossible to call anyway + // expected-note@-2{{call is 'async'}} + // expected-error@-3{{call can throw, but it is not marked with 'try' and the error is not handled}} // TODO: no need to diagnose this, it is impossible to call anyway +} + +@available(SwiftStdlib 5.5, *) +func test_outside(distributed: D) async throws { + distributed.distHello() // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-error@-1{{call can throw but is not marked with 'try'}} + // expected-note@-2{{calls to instance method 'distHello()' from outside of its actor context are implicitly asynchronous}} + // expected-note@-3{{did you mean to use 'try'?}} + // expected-note@-4{{did you mean to disable error propagation?}} + // expected-note@-5{{did you mean to handle error as optional value?}} + try distributed.distHello() // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{calls to instance method 'distHello()' from outside of its actor context are implicitly asynchronous}} + await distributed.distHello() // expected-error{{call can throw but is not marked with 'try'}} + // expected-note@-1{{did you mean to use 'try'?}} + // expected-note@-2{{did you mean to disable error propagation?}} + // expected-note@-3{{did you mean to handle error as optional value?}} + try await distributed.distHello() // ok + + distributed.distHelloAsync()// expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-error@-1{{call can throw but is not marked with 'try'}} + // expected-note@-2{{calls to instance method 'distHelloAsync()' from outside of its actor context are implicitly asynchronous}} + // expected-note@-3{{did you mean to use 'try'?}} + // expected-note@-4{{did you mean to disable error propagation?}} + // expected-note@-5{{did you mean to handle error as optional value?}} + try distributed.distHelloAsync() // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{calls to instance method 'distHelloAsync()' from outside of its actor context are implicitly asynchronous}} + await distributed.distHelloAsync() // expected-error{{call can throw but is not marked with 'try'}} + // expected-note@-1{{did you mean to use 'try'?}} + // expected-note@-2{{did you mean to disable error propagation?}} + // expected-note@-3{{did you mean to handle error as optional value?}} + try await distributed.distHelloAsync() // ok + + distributed.distHelloThrows() // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-error@-1{{call can throw but is not marked with 'try'}} + // expected-note@-2{{calls to instance method 'distHelloThrows()' from outside of its actor context are implicitly asynchronous}} + // expected-note@-3{{did you mean to use 'try'?}} + // expected-note@-4{{did you mean to disable error propagation?}} + // expected-note@-5{{did you mean to handle error as optional value?}} + try distributed.distHelloThrows() // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{calls to instance method 'distHelloThrows()' from outside of its actor context are implicitly asynchronous}} + await distributed.distHelloThrows() // expected-error{{call can throw but is not marked with 'try'}} + // expected-note@-1{{did you mean to use 'try'?}} + // expected-note@-2{{did you mean to disable error propagation?}} + // expected-note@-3{{did you mean to handle error as optional value?}} + try await distributed.distHelloThrows() // ok + + distributed.distHelloAsyncThrows() // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-error@-1{{call can throw but is not marked with 'try'}} + // expected-note@-2{{calls to instance method 'distHelloAsyncThrows()' from outside of its actor context are implicitly asynchronous}} + // expected-note@-3{{did you mean to use 'try'?}} + // expected-note@-4{{did you mean to disable error propagation?}} + // expected-note@-5{{did you mean to handle error as optional value?}} + try distributed.distHelloAsyncThrows() // expected-error{{expression is 'async' but is not marked with 'await'}} + // expected-note@-1{{calls to instance method 'distHelloAsyncThrows()' from outside of its actor context are implicitly asynchronous}} + await distributed.distHelloAsyncThrows() // expected-error{{call can throw but is not marked with 'try'}} + // expected-note@-1{{did you mean to use 'try'?}} + // expected-note@-2{{did you mean to disable error propagation?}} + // expected-note@-3{{did you mean to handle error as optional value?}} + try await distributed.distHelloAsyncThrows() // ok + + // special: the actorAddress may always be referred to + _ = distributed.id // ok + _ = distributed.actorTransport // ok +} + diff --git a/test/Distributed/distributed_actor_inference.swift b/test/Distributed/distributed_actor_inference.swift new file mode 100644 index 0000000000000..a97d6ed99b4ab --- /dev/null +++ b/test/Distributed/distributed_actor_inference.swift @@ -0,0 +1,106 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +actor SomeActor { } + +// ==== ------------------------------------------------------------------------ +// MARK: Declaring distributed actors +// GOOD: +@available(SwiftStdlib 5.5, *) +distributed actor SomeDistributedActor_0 { } + +// BAD: +@available(SwiftStdlib 5.5, *) +distributed class SomeDistributedActor_1 { } // expected-error{{'distributed' can only be applied to 'actor' definitions, and distributed actor-isolated async functions}} +@available(SwiftStdlib 5.5, *) +distributed struct SomeDistributedActor_2 { } // expected-error{{'distributed' modifier cannot be applied to this declaration}} +@available(SwiftStdlib 5.5, *) +distributed enum SomeDistributedActor_3 { } // expected-error{{'distributed' modifier cannot be applied to this declaration}} + +// ==== ------------------------------------------------------------------------ +// MARK: Declaring distributed functions +// NOTE: not distributed actor, so cannot have any distributed functions + +@available(SwiftStdlib 5.5, *) +struct SomeNotActorStruct_2 { + distributed func nopeAsyncThrows() async throws -> Int { 42 } // expected-error{{'distributed' function can only be declared within 'distributed actor'}} +} + +@available(SwiftStdlib 5.5, *) +class SomeNotActorClass_3 { + distributed func nopeAsyncThrows() async throws -> Int { 42 } // expected-error{{'distributed' function can only be declared within 'distributed actor'}} +} + +@available(SwiftStdlib 5.5, *) +actor SomeNotDistributedActor_4 { + distributed func notInDistActorAsyncThrowing() async throws -> Int { 42 } // expected-error{{'distributed' function can only be declared within 'distributed actor'}} +} + +protocol DP { + distributed func hello() // expected-error{{'distributed' function can only be declared within 'distributed actor'}} +} + +@available(SwiftStdlib 5.5, *) +protocol DPOK: DistributedActor { + distributed func hello() // ok +} + +@available(SwiftStdlib 5.5, *) +protocol DPOK2: DPOK { + distributed func again() // ok +} + +@available(SwiftStdlib 5.5, *) +enum SomeNotActorEnum_5 { + distributed func nopeAsyncThrows() async throws -> Int { 42 } // expected-error{{'distributed' function can only be declared within 'distributed actor'}} +} + +@available(SwiftStdlib 5.5, *) +distributed actor SomeDistributedActor_6 { + distributed func yay() async throws -> Int { 42 } // ok +} + +@available(SwiftStdlib 5.5, *) +distributed actor SomeDistributedActor_7 { + distributed func dont_1() async throws -> Int { 42 } // expected-error{{Distributed function's 'dont_1' remote counterpart '_remote_dont_1' cannot not be implemented manually.}} + distributed func dont_2() async throws -> Int { 42 } // expected-error{{Distributed function's 'dont_2' remote counterpart '_remote_dont_2' cannot not be implemented manually.}} + distributed func dont_3() async throws -> Int { 42 } // expected-error{{Distributed function's 'dont_3' remote counterpart '_remote_dont_3' cannot not be implemented manually.}} +} + +@available(SwiftStdlib 5.5, *) +extension SomeDistributedActor_7 { + + // TODO: we should diagnose a bit more precisely here + + static func _remote_dont_1(actor: SomeDistributedActor_6) async throws -> Int { + fatalError() + } + static func _remote_dont_2(actor: SomeDistributedActor_6) -> Int { + fatalError() + } + static func _remote_dont_3(actor: SomeDistributedActor_6) -> Int { + fatalError() + } + + func _remote_dont_3(actor: SomeDistributedActor_6) -> Int { + fatalError() + } + func _remote_dont_4() -> Int { + fatalError() + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor BadValuesDistributedActor_7 { + distributed var varItNope: Int { 13 } // expected-error{{'distributed' modifier cannot be applied to this declaration}} + distributed let letItNope: Int = 13 // expected-error{{'distributed' modifier cannot be applied to this declaration}} + distributed lazy var lazyVarNope: Int = 13 // expected-error{{'distributed' modifier cannot be applied to this declaration}} + distributed subscript(nope: Int) -> Int { nope * 2 } // expected-error{{'distributed' modifier cannot be applied to this declaration}} + distributed static let staticLetNope: Int = 13 // expected-error{{'distributed' modifier cannot be applied to this declaration}} + distributed static var staticVarNope: Int { 13 } // expected-error{{'distributed' modifier cannot be applied to this declaration}} + distributed static func staticNope() async throws -> Int { 13 } // expected-error{{'distributed' functions cannot be 'static'}} +} + diff --git a/test/Distributed/distributed_actor_initialization.swift b/test/Distributed/distributed_actor_initialization.swift new file mode 100644 index 0000000000000..24f425b0b9424 --- /dev/null +++ b/test/Distributed/distributed_actor_initialization.swift @@ -0,0 +1,66 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor OK0 { } + +@available(SwiftStdlib 5.5, *) +distributed actor OK1 { + var x: Int = 1 + // ok, since all fields are initialized, the constructor can be synthesized +} + +// TODO(distributed): test all the FIXITs in this file + +@available(SwiftStdlib 5.5, *) +distributed actor Bad1 { + init() { + // expected-error@-1 {{designated distributed actor initializer 'init()' is missing required ActorTransport parameter}} + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor Bad12 { + init(x: String) { + // expected-error@-1 {{designated distributed actor initializer 'init(x:)' is missing required ActorTransport parameter}} + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor OK2 { + var x: Int + + init(x: Int, transport: ActorTransport) { // ok + self.x = x + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor Bad2 { + var x: Int = 1 + + init(transport: ActorTransport, too many: ActorTransport) { + // expected-error@-1{{designated distributed actor initializer 'init(transport:too:)' must accept exactly one ActorTransport parameter, found 2}} + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor OK3 { + var x: Int + + init(y: Int, transport: ActorTransport) { + self.x = y + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor OKMulti { + + convenience init(y: Int, transport: ActorTransport) { // ok + self.init(transport: transport) + } + +} diff --git a/test/Distributed/distributed_actor_is_experimental.swift b/test/Distributed/distributed_actor_is_experimental.swift new file mode 100644 index 0000000000000..4a827744effc5 --- /dev/null +++ b/test/Distributed/distributed_actor_is_experimental.swift @@ -0,0 +1,44 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking +// ^^^^ notice the, on purpose, missing '-enable-experimental-distributed' +// REQUIRES: concurrency +// REQUIRES: distributed + +actor SomeActor {} + +@available(SwiftStdlib 5.5, *) +distributed actor DA {} +// expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} + +@available(SwiftStdlib 5.5, *) +distributed actor class DAC {} +// expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} +// expected-error@-2{{keyword 'class' cannot be used as an identifier here}} + +actor A { + func normal() async {} + distributed func dist() {} + // expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} + distributed func distAsync() async {} + // expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} + + distributed var neverOk: String { + // expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} + "vars are not allowed to be distributed *ever* anyway" + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor DA2 { + // expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} + func normal() async {} + distributed func dist() {} + // expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} + distributed func distAsync() async {} + // expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} + + distributed var neverOk: String { + // expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} + "vars are not allowed to be distributed *ever* anyway" + } +} + diff --git a/test/Distributed/distributed_actor_is_experimental_enabled_missing_import.swift b/test/Distributed/distributed_actor_is_experimental_enabled_missing_import.swift new file mode 100644 index 0000000000000..e4f7b9ee37271 --- /dev/null +++ b/test/Distributed/distributed_actor_is_experimental_enabled_missing_import.swift @@ -0,0 +1,37 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-experimental-distributed +// REQUIRES: concurrency +// REQUIRES: distributed + +actor SomeActor {} + +@available(SwiftStdlib 5.5, *) +distributed actor DA {} +// expected-error@-1{{'_Distributed' module not imported, required for 'distributed actor'}} + +@available(SwiftStdlib 5.5, *) +distributed actor class DAC {} +// expected-error@-1{{distributed' can only be applied to 'actor' definitions, and distributed actor-isolated async functions}} +// expected-error@-2{{keyword 'class' cannot be used as an identifier here}} + +actor A { + func normal() async {} + distributed func dist() {} // expected-error{{'distributed' function can only be declared within 'distributed actor'}} + distributed func distAsync() async {} // expected-error{{'distributed' function can only be declared within 'distributed actor'}} + + distributed var neverOk: String { // expected-error{{'distributed' modifier cannot be applied to this declaration}} + "vars are not allowed to be distributed *ever* anyway" + } +} + +@available(SwiftStdlib 5.5, *) +distributed actor DA2 { + // expected-error@-1{{'_Distributed' module not imported, required for 'distributed actor'}} + func normal() async {} + distributed func dist() {} + distributed func distAsync() async {} + + distributed var neverOk: String { // expected-error{{'distributed' modifier cannot be applied to this declaration}} + "vars are not allowed to be distributed *ever* anyway" + } +} + diff --git a/test/Distributed/distributed_actor_is_experimental_with_import.swift b/test/Distributed/distributed_actor_is_experimental_with_import.swift new file mode 100644 index 0000000000000..fde6c2f3df5cb --- /dev/null +++ b/test/Distributed/distributed_actor_is_experimental_with_import.swift @@ -0,0 +1,10 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking +// ^^^^ notice the, on purpose, missing '-enable-experimental-distributed' +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(macOS 12.0, *) +distributed actor A {} +// expected-error@-1{{'distributed' modifier is only valid when experimental distributed support is enabled}} diff --git a/test/Distributed/distributed_actor_isolation.swift b/test/Distributed/distributed_actor_isolation.swift new file mode 100644 index 0000000000000..0aa489977fc9f --- /dev/null +++ b/test/Distributed/distributed_actor_isolation.swift @@ -0,0 +1,174 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(SwiftStdlib 5.5, *) +struct ActorAddress: ActorIdentity { + let address: String + init(parse address : String) { + self.address = address + } +} + +@available(SwiftStdlib 5.5, *) +actor LocalActor_1 { + let name: String = "alice" + var mutable: String = "" + + distributed func nope() { + // expected-error@-1{{'distributed' function can only be declared within 'distributed actor'}} + } +} + +struct NotCodableValue { } + +@available(SwiftStdlib 5.5, *) +distributed actor DistributedActor_1 { + + let name: String = "alice" // expected-note{{distributed actor state is only available within the actor instance}} + var mutable: String = "alice" // expected-note{{distributed actor state is only available within the actor instance}} + var computedMutable: String { + get { + "hey" + } + set { + _ = newValue + } + } + + distributed let letProperty: String = "" // expected-error{{'distributed' modifier cannot be applied to this declaration}} + distributed var varProperty: String = "" // expected-error{{'distributed' modifier cannot be applied to this declaration}} + distributed var computedProperty: String { // expected-error{{'distributed' modifier cannot be applied to this declaration}} + "" + } + + distributed static func distributedStatic() {} // expected-error{{'distributed' functions cannot be 'static'}} + + func hello() {} // expected-note{{only 'distributed' functions can be called from outside the distributed actor}} + func helloAsync() async {} // expected-note{{only 'distributed' functions can be called from outside the distributed actor}} + func helloAsyncThrows() async throws {} // expected-note{{only 'distributed' functions can be called from outside the distributed actor}} + + distributed func distHello() { } // ok + distributed func distHelloAsync() async { } // ok + distributed func distHelloThrows() throws { } // ok + distributed func distHelloAsyncThrows() async throws { } // ok + + distributed func distInt() async throws -> Int { 42 } // ok + distributed func distInt(int: Int) async throws -> Int { int } // ok + distributed func distIntString(int: Int, two: String) async throws -> (String) { "\(int) + \(two)" } // ok + + distributed func dist(notCodable: NotCodableValue) async throws { + // expected-error@-1 {{distributed function parameter 'notCodable' of type 'NotCodableValue' does not conform to 'Codable'}} + } + distributed func distBadReturn(int: Int) async throws -> NotCodableValue { + // expected-error@-1 {{distributed function result type 'NotCodableValue' does not conform to 'Codable'}} + fatalError() + } + + distributed func distReturnGeneric(item: T) async throws -> T { // ok + item + } + distributed func distReturnGenericWhere(item: Int) async throws -> T where T: Codable { // ok + fatalError() + } + distributed func distBadReturnGeneric(int: Int) async throws -> T { + // expected-error@-1 {{distributed function result type 'T' does not conform to 'Codable'}} + fatalError() + } + + distributed func distGenericParam(value: T) async throws { // ok + fatalError() + } + distributed func distGenericParamWhere(value: T) async throws -> T where T: Codable { // ok + value + } + distributed func distBadGenericParam(int: T) async throws { + // expected-error@-1 {{distributed function parameter 'int' of type 'T' does not conform to 'Codable'}} + fatalError() + } + + static func staticFunc() -> String { "" } // ok + +// TODO: should be able to handle a static, global actor isolated function as well +// @MainActor +// static func staticMainActorFunc() -> String { "" } // ok + + static distributed func staticDistributedFunc() -> String { + // expected-error@-1{{'distributed' functions cannot be 'static'}}{10-21=} + fatalError() + } + + func test_inside() async throws { + _ = self.name + _ = self.computedMutable + + _ = try await self.distInt() + _ = try await self.distInt(int: 42) + + self.hello() + _ = await self.helloAsync() + _ = try await self.helloAsyncThrows() + + self.distHello() + await self.distHelloAsync() + try self.distHelloThrows() + try await self.distHelloAsyncThrows() + } +} + +@available(SwiftStdlib 5.5, *) +func test_outside( + local: LocalActor_1, + distributed: DistributedActor_1 +) async throws { + // ==== properties + _ = distributed.id // ok + distributed.id = AnyActorIdentity(ActorAddress(parse: "mock://1.1.1.1:8080/#123121")) // expected-error{{cannot assign to property: 'id' is immutable}}) + + _ = local.name // ok, special case that let constants are okey + let _: String = local.mutable // ok, special case that let constants are okey + _ = distributed.name // expected-error{{distributed actor-isolated property 'name' can only be referenced inside the distributed actor}} + _ = distributed.mutable // expected-error{{distributed actor-isolated property 'mutable' can only be referenced inside the distributed actor}} + + // ==== special properties (@_distributedActorIndependent) + // the distributed actor's special fields may always be referred to + _ = distributed.id + _ = distributed.actorTransport + + // ==== static functions + _ = distributed.staticFunc() // expected-error{{static member 'staticFunc' cannot be used on instance of type 'DistributedActor_1'}} + _ = DistributedActor_1.staticFunc() + + // ==== non-distributed functions + _ = await distributed.hello() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + _ = await distributed.helloAsync() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + _ = try await distributed.helloAsyncThrows() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} +} + +// ==== Protocols and static (non isolated functions) + +@available(SwiftStdlib 5.5, *) +protocol P { + static func hello() -> String +} +@available(SwiftStdlib 5.5, *) +extension P { + static func hello() -> String { "" } +} + +@available(SwiftStdlib 5.5, *) +distributed actor ALL: P { +} + +// ==== Codable parameters and return types ------------------------------------ + +@available(SwiftStdlib 5.5, *) +func test_params( + distributed: DistributedActor_1 +) async throws { + _ = try await distributed.distInt() // ok + _ = try await distributed.distInt(int: 42) // ok + _ = try await distributed.dist(notCodable: .init()) +} diff --git a/test/Distributed/distributed_actor_nonisolated.swift b/test/Distributed/distributed_actor_nonisolated.swift new file mode 100644 index 0000000000000..08313982eaaf6 --- /dev/null +++ b/test/Distributed/distributed_actor_nonisolated.swift @@ -0,0 +1,49 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor DA { + + let local: Int = 42 + // expected-note@-1{{distributed actor state is only available within the actor instance}} + // expected-note@-2{{distributed actor state is only available within the actor instance}} + + nonisolated let nope: Int = 13 + // expected-error@-1{{'nonisolated' can not be applied to distributed actor stored properties}} + + nonisolated var computedNonisolated: Int { + // expected-note@-1{{distributed actor state is only available within the actor instance}} + + // nonisolated computed properties are outside of the actor and as such cannot access local + _ = self.local // expected-error{{distributed actor-isolated property 'local' can only be referenced inside the distributed actor}} + + _ = self.id // ok, special handled and always available + _ = self.actorTransport // ok, special handled and always available + } + + distributed func dist() {} + + nonisolated func access() async throws { + _ = self.id // ok + _ = self.actorTransport // ok + + // self is a distributed actor self is NOT isolated + _ = self.local // expected-error{{distributed actor-isolated property 'local' can only be referenced inside the distributed actor}} + _ = try await self.dist() // ok, was made implicitly throwing and async + _ = self.computedNonisolated // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + } + + nonisolated distributed func nonisolatedDistributed() async { + // expected-error@-1{{function 'nonisolatedDistributed()' cannot be both 'nonisolated' and 'distributed'}}{{3-15=}} + fatalError() + } + + distributed nonisolated func distributedNonisolated() async { + // expected-error@-1{{function 'distributedNonisolated()' cannot be both 'nonisolated' and 'distributed'}}{{15-27=}} + fatalError() + } + +} \ No newline at end of file diff --git a/test/Distributed/distributed_actor_resolve.swift b/test/Distributed/distributed_actor_resolve.swift new file mode 100644 index 0000000000000..f36a5cac282c5 --- /dev/null +++ b/test/Distributed/distributed_actor_resolve.swift @@ -0,0 +1,23 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor Capybara { } + +//@available(SwiftStdlib 5.5, *) +//protocol Wheeker: DistributedActor { } +//@available(SwiftStdlib 5.5, *) +//distributed actor GuineaPing: Wheeker { } + +@available(SwiftStdlib 5.5, *) +func test(identity: AnyActorIdentity, transport: ActorTransport) async throws { + let _: Capybara = try Capybara.resolve(identity, using: transport) + +// TODO: implement resolve being able to be called on a distributed actor protocol +// (yes, normally it is not allowed to call such function... so we need to +// discuss and figure out how we want to expose the resolve of a protocol) +// let c: Wheeker = try Wheeker.resolve(.init(identity), using: transport) +} \ No newline at end of file diff --git a/test/Distributed/distributed_missing_import.swift b/test/Distributed/distributed_missing_import.swift new file mode 100644 index 0000000000000..62a21b3b20c4d --- /dev/null +++ b/test/Distributed/distributed_missing_import.swift @@ -0,0 +1,13 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +actor SomeActor { } + +@available(SwiftStdlib 5.5, *) +distributed actor MissingImportDistributedActor_0 { } +// expected-error@-1{{'_Distributed' module not imported, required for 'distributed actor'}} + +let t: ActorTransport // expected-error{{cannot find type 'ActorTransport' in scope}} +let a: ActorAddress // expected-error{{cannot find type 'ActorAddress' in scope}} + diff --git a/test/Distributed/distributed_protocol_isolation.swift b/test/Distributed/distributed_protocol_isolation.swift new file mode 100644 index 0000000000000..2d1111a674f0a --- /dev/null +++ b/test/Distributed/distributed_protocol_isolation.swift @@ -0,0 +1,103 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -disable-availability-checking +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +// ==== ----------------------------------------------------------------------- +// MARK: Good cases + +@available(SwiftStdlib 5.5, *) +protocol DistProtocol: DistributedActor { + // FIXME(distributed): avoid issuing these warnings, these originate from the call on the DistProtocol where we marked this func as dist isolated, + func local() -> String + // (the note appears a few times, because we misuse the call many times) + // expected-note@-2{{calls to instance method 'local()' from outside of its actor context are implicitly asynchronous}} // FIXME: don't emit this note + // expected-note@-3{{calls to instance method 'local()' from outside of its actor context are implicitly asynchronous}} // FIXME: don't emit this note + // expected-note@-4{{calls to instance method 'local()' from outside of its actor context are implicitly asynchronous}} // FIXME: don't emit this note + + distributed func dist() -> String + distributed func dist(string: String) -> String + + distributed func distAsync() async -> String + distributed func distThrows() throws -> String + distributed func distAsyncThrows() async throws -> String +} + +@available(SwiftStdlib 5.5, *) +distributed actor SpecificDist: DistProtocol { + + nonisolated func local() -> String { "hi" } // expected-note{{only 'distributed' functions can be called from outside the distributed actor}} + + distributed func dist() -> String { "dist!" } + distributed func dist(string: String) -> String { string } + + distributed func distAsync() async -> String { "dist!" } + distributed func distThrows() throws -> String { "dist!" } + distributed func distAsyncThrows() async throws -> String { "dist!" } + + func inside() async throws { + _ = self.local() // ok + + _ = self.dist() // ok + _ = self.dist(string: "") // ok + _ = await self.distAsync() // ok + _ = try self.distThrows() // ok + _ = try await self.distAsyncThrows() // ok + } +} + +@available(SwiftStdlib 5.5, *) +func outside_good(dp: SpecificDist) async throws { + _ = dp.local() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + + _ = try await dp.dist() // implicit async throws + _ = try await dp.dist(string: "") // implicit async throws + _ = try await dp.distAsync() // implicit throws + _ = try await dp.distThrows() // implicit async + _ = try await dp.distAsyncThrows() // ok +} + +@available(SwiftStdlib 5.5, *) +func outside_good_generic(dp: DP) async throws { + _ = dp.local() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + _ = await dp.local() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + // the below warning is expected because we don't apply the "implicitly async" to the not-callable func + // expected-warning@-2{{no 'async' operations occur within 'await' expression}} + + _ = try dp.local() // expected-error{{only 'distributed' functions can be called from outside the distributed actor}} + // the below warning is expected because we don't apply the "implicitly throwing" to the not-callable func + // expected-warning@-2{{no calls to throwing functions occur within 'try' expression}} + + _ = try await dp.dist() // implicit async throws + _ = try await dp.dist(string: "") // implicit async throws + _ = try await dp.distAsync() // implicit throws + _ = try await dp.distThrows() // implicit async + _ = try await dp.distAsyncThrows() // ok +} + +@available(SwiftStdlib 5.5, *) +func outside_good_ext(dp: DP) async throws { + _ = try await dp.dist() // implicit async throws + _ = try await dp.dist(string: "") // implicit async throws + _ = try await dp.distAsync() // implicit throws + _ = try await dp.distThrows() // implicit async + _ = try await dp.distAsyncThrows() // ok +} + +// ==== ----------------------------------------------------------------------- +// MARK: Error cases + +@available(SwiftStdlib 5.5, *) +protocol ErrorCases: DistributedActor { + distributed func unexpectedAsyncThrows() -> String + // expected-note@-1{{protocol requires function 'unexpectedAsyncThrows()' with type '() -> String'; do you want to add a stub?}} +} + +@available(SwiftStdlib 5.5, *) +distributed actor BadGreeter: ErrorCases { + // expected-error@-1{{type 'BadGreeter' does not conform to protocol 'ErrorCases'}} + + distributed func unexpectedAsyncThrows() async throws -> String { "" } + // expected-note@-1{{candidate is 'async', but protocol requirement is not}} +} diff --git a/test/Driver/Dependencies/driver-show-incremental-mutual-fine.swift b/test/Driver/Dependencies/driver-show-incremental-mutual-fine.swift index 53b685510df93..2202cef69a4a8 100644 --- a/test/Driver/Dependencies/driver-show-incremental-mutual-fine.swift +++ b/test/Driver/Dependencies/driver-show-incremental-mutual-fine.swift @@ -16,4 +16,4 @@ // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python.unquoted};%S/Inputs/update-dependencies.py;%swift-dependency-tool" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD: Queuing (initial): {compile: other.o <= other.swift} // CHECK-THIRD-DAG: Queuing because of the initial set: {compile: main.o <= main.swift} -// CHECK-THIRD-DAG: interface of top-level name 'a' in other.swift -> interface of source file main.swiftdeps +// CHECK-THIRD-DAG: interface of top-level name 'a' in other.swift -> interface of source file {{from main.swiftdeps in main.swift|main.swiftdeps}} diff --git a/test/Driver/Dependencies/independent-fine.swift b/test/Driver/Dependencies/independent-fine.swift index e70adbb8ce0c5..2f651472e486b 100644 --- a/test/Driver/Dependencies/independent-fine.swift +++ b/test/Driver/Dependencies/independent-fine.swift @@ -14,7 +14,10 @@ // CHECK-SECOND-NOT: Handled -// RUN: touch -t 201401240006 %t/* +// Don't change the priors mod time. +// RUN: touch -t 201401240006 %t/*.swift +// RUN: touch -t 201401240006 %t/*.swiftdeps +// RUN: touch -t 201401240006 %t/*.json // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python.unquoted};%S/Inputs/update-dependencies.py;%swift-dependency-tool" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240007 %t/main.swift @@ -32,7 +35,10 @@ // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python.unquoted};%S/Inputs/update-dependencies.py;%swift-dependency-tool" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s -// RUN: touch -t 201401240006 %t/* +// Don't change the priors mod time. +// RUN: touch -t 201401240006 %t/*.swift +// RUN: touch -t 201401240006 %t/*.swiftdeps +// RUN: touch -t 201401240006 %t/*.json // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python.unquoted};%S/Inputs/update-dependencies.py;%swift-dependency-tool" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s diff --git a/test/Driver/Dependencies/independent-parseable-fine.swift b/test/Driver/Dependencies/independent-parseable-fine.swift index 281dd4812e68d..e9eac1ff6a4ad 100644 --- a/test/Driver/Dependencies/independent-parseable-fine.swift +++ b/test/Driver/Dependencies/independent-parseable-fine.swift @@ -25,7 +25,8 @@ // CHECK-SECOND-DAG: "{{(.\\/)?}}main.swift" // CHECK-SECOND: {{^}$}} -// RUN: touch -t 201401240006 %t/* +// Don't mess with the priors +// RUN: touch -t 201401240006 %t/*.{swift,swiftdeps,json} // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python.unquoted};%S/Inputs/update-dependencies.py;%swift-dependency-tool" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s @@ -73,6 +74,6 @@ // CHECK-SECOND-MULTI-DAG: "{{(.\\/)?}}other.swift" // CHECK-SECOND-MULTI: {{^}$}} -// RUN: touch -t 201401240006 %t/* +// RUN: touch -t 201401240006 %t/*.{swift,swiftdeps,json} // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python.unquoted};%S/Inputs/update-dependencies.py;%swift-dependency-tool" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s diff --git a/test/Driver/Dependencies/one-way-external-delete-fine.swift b/test/Driver/Dependencies/one-way-external-delete-fine.swift index 35d75ede9df5a..ad69000cfde98 100644 --- a/test/Driver/Dependencies/one-way-external-delete-fine.swift +++ b/test/Driver/Dependencies/one-way-external-delete-fine.swift @@ -13,7 +13,10 @@ // CHECK-SECOND-NOT: Handled -// RUN: touch -t 201401240005 %t/* +// Don't change the .priors mod time +// RUN: touch -t 201401240005 %t/*swift +// RUN: touch -t 201401240005 %t/*swiftdeps +// RUN: touch -t 201401240005 %t/*json // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: rm %t/other1-external @@ -30,7 +33,10 @@ // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python.unquoted};%S/Inputs/update-dependencies.py;%swift-dependency-tool" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s -// RUN: touch -t 201401240005 %t/* +// Don't change the .priors mod time +// RUN: touch -t 201401240005 %t/*swift +// RUN: touch -t 201401240005 %t/*swiftdeps +// RUN: touch -t 201401240005 %t/*json // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: rm %t/main1-external diff --git a/test/Driver/Dependencies/one-way-external-fine.swift b/test/Driver/Dependencies/one-way-external-fine.swift index b2a99cdf46175..05fb7708d7a1b 100644 --- a/test/Driver/Dependencies/one-way-external-fine.swift +++ b/test/Driver/Dependencies/one-way-external-fine.swift @@ -18,8 +18,8 @@ // CHECK-SECOND-NOT: Handled - -// RUN: touch -t 201401240005 %t/* +// Don't change the .priors mod time +// RUN: touch -t 201401240005 %t/*{swift,swiftdeps,json} // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: touch -t 203704010005 %t/other1-external @@ -28,14 +28,16 @@ // CHECK-THIRD-DAG: Handled other.swift // CHECK-THIRD-DAG: Handled main.swift -// RUN: touch -t 201401240005 %t/* +// Don't change the .priors mod time +// RUN: touch -t 201401240005 %t/*{swift,swiftdeps,json} // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: touch -t 203704010005 %t/other2-external // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python.unquoted};%S/Inputs/update-dependencies.py;%swift-dependency-tool" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s -// RUN: touch -t 201401240005 %t/* +// Don't change the .priors mod time +// RUN: touch -t 201401240005 %t/*{swift,swiftdeps,json} // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: touch -t 203704010005 %t/main1-external @@ -45,7 +47,8 @@ // CHECK-FOURTH: Handled main.swift // CHECK-FOURTH-NOT: Handled other.swift -// RUN: touch -t 201401240005 %t/* +// Don't change the .priors mod time +// RUN: touch -t 201401240005 %t/*{swift,swiftdeps,json} // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: touch -t 203704010005 %t/main2-external diff --git a/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json b/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json index 35383d42256e9..35fbf394c1db7 100644 --- a/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json +++ b/test/Driver/Inputs/MacOSX10.15.4.versioned.sdk/SDKSettings.json @@ -1,5 +1,6 @@ { "Version":"10.15.4", + "CanonicalName": "macosx10.15.4", "VersionMap" : { "macOS_iOSMac" : { "10.14.4" : "12.4", diff --git a/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json b/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json index c14072ff3272b..2c910ad6fa6f0 100644 --- a/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json +++ b/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json @@ -1,5 +1,6 @@ { "Version":"10.15", + "CanonicalName": "macosx10.15", "VersionMap" : { "macOS_iOSMac" : { "10.15" : "13.1", diff --git a/test/Driver/autolink-force-load-comdat.swift b/test/Driver/autolink-force-load-comdat.swift deleted file mode 100644 index 74b011897c7d0..0000000000000 --- a/test/Driver/autolink-force-load-comdat.swift +++ /dev/null @@ -1,10 +0,0 @@ -// RUN: %target-swiftc_driver -incremental -autolink-force-load %s 2>&1 | %FileCheck -check-prefix=AUTOLINK_FORCE_LOAD %s -// RUN: %target-swiftc_driver -autolink-force-load -incremental %s 2>&1 | %FileCheck -check-prefix=AUTOLINK_FORCE_LOAD %s - -// MACHO targets do not support COMDAT -// UNSUPPORTED: OS=macosx -// UNSUPPORTED: OS=tvos -// UNSUPPORTED: OS=watchos -// UNSUPPORTED: OS=ios - -// AUTOLINK_FORCE_LOAD-NOT: error: '-autolink-force-load' is not supported with '-incremental' diff --git a/test/Driver/autolink-force-load-no-comdat.swift b/test/Driver/autolink-force-load-no-comdat.swift deleted file mode 100644 index f87d27a0337a8..0000000000000 --- a/test/Driver/autolink-force-load-no-comdat.swift +++ /dev/null @@ -1,8 +0,0 @@ -// RUN: not %swiftc_driver -incremental -autolink-force-load %s 2>&1 | %FileCheck -check-prefix=AUTOLINK_FORCE_LOAD %s -// RUN: not %swiftc_driver -autolink-force-load -incremental %s 2>&1 | %FileCheck -check-prefix=AUTOLINK_FORCE_LOAD %s - -// MACHO targets do not support COMDAT -// REQUIRES: OS=macosx || OS=tvos || OS=watchos || OS=ios - -// AUTOLINK_FORCE_LOAD: error: '-autolink-force-load' is not supported with '-incremental' - diff --git a/test/Driver/darwin-static-library-debug.swift b/test/Driver/darwin-static-library-debug.swift new file mode 100644 index 0000000000000..6478757a217ab --- /dev/null +++ b/test/Driver/darwin-static-library-debug.swift @@ -0,0 +1,14 @@ +// RUN: %swiftc_driver -driver-print-actions -target x86_64-apple-macosx10.15 -g -emit-library -static -o library.a %s 2>&1 | %FileCheck %s -check-prefix CHECK -check-prefix CHECK-STATIC +// RUN: %swiftc_driver -driver-print-actions -target x86_64-apple-macosx10.15 -g -emit-library -o library.a %s 2>&1 | %FileCheck %s -check-prefix CHECK -check-prefix CHECK-SHARED +// RUN: %swiftc_driver -driver-print-actions -target x86_64-apple-macosx10.15 -g -o a.out %s 2>&1 | %FileCheck %s -check-prefix CHECK -check-prefix CHECK-EXEC + +// CHECK: 0: input, "{{.*}}darwin-static-library-debug.swift", swift +// CHECK: 1: compile, {0}, object +// CHECK: 2: merge-module, {1}, swiftmodule +// CHECK-STATIC: 3: static-link, {1, 2}, image +// CHECK-STATIC-NOT: 4: generate-dSYM, {3}, dSYM +// CHECK-SHARED: 3: link, {1, 2}, image +// CHECK-SHARED: 4: generate-dSYM, {3}, dSYM +// CHECK-EXEC: 3: link, {1, 2}, image +// CHECK-EXEC: 4: generate-dSYM, {3}, dSYM + diff --git a/test/Driver/diagnose-new-driver-flags.swift b/test/Driver/diagnose-new-driver-flags.swift new file mode 100644 index 0000000000000..73f55747cb5e4 --- /dev/null +++ b/test/Driver/diagnose-new-driver-flags.swift @@ -0,0 +1,3 @@ +// RUN: %swiftc_driver -emit-module -user-module-version 999.999 %s -### 2>&1 | %FileCheck %s + +// CHECK: warning: option '-user-module-version' is ony supported in swift-driver diff --git a/test/Driver/emit_module_separately.swift b/test/Driver/emit_module_separately.swift new file mode 100644 index 0000000000000..fc4c5dbe2b294 --- /dev/null +++ b/test/Driver/emit_module_separately.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) +// RUN: touch %t/file1.swift %t/file2.swift %t/file3.swift +// RUN: echo 'public func main() {}' >%t/main.swift +// +// RUN: %target-swiftc_driver -driver-skip-execution -c -emit-module -module-name main -driver-print-jobs %s -experimental-emit-module-separately %t/file1.swift %t/file2.swift %t/file3.swift %t/main.swift 2>^1 | %FileCheck -check-prefix NORMAL %s +// RUN: %target-swiftc_driver -driver-skip-execution -c -emit-module -module-name main -driver-print-jobs -incremental %s -experimental-emit-module-separately %t/file1.swift %t/file2.swift %t/file3.swift %t/main.swift 2>^1 | %FileCheck -check-prefix INCREMENTAL %s + +// Just test that we eat this argument. Only the new driver knows what to do +// here. The legacy driver will just fall back to a merge-modules job as usual. +// NORMAL: swift +// NORMAL-NOT: -experimental-emit-module-separately + +// INCREMENTAL: swift +// INCREMENTAL: -merge-modules +// INCREMENTAL-NOT: -experimental-emit-module-separately diff --git a/test/Driver/link-time-opt-darwin-ld-lib.swift b/test/Driver/link-time-opt-darwin-ld-lib.swift index b3998e24d2b78..c82265075b5de 100644 --- a/test/Driver/link-time-opt-darwin-ld-lib.swift +++ b/test/Driver/link-time-opt-darwin-ld-lib.swift @@ -24,3 +24,18 @@ // CHECK-SIMPLE-FULL-macosx: ld // CHECK-SIMPLE-FULL-macosx-DAG: -lto_library {{.+}}/lib/libLTO.dylib // CHECK-SIMPLE-FULL-macosx-DAG: [[OBJECTFILE]] + + +// Check that driver does not see libLTO.dylib as an input + +// RUN: %swiftc_driver -driver-print-jobs %S/../Inputs/empty.swift -lto=llvm-full -lto-library /foo/libLTO.dylib -target x86_64-apple-macosx10.9 | %FileCheck %s --check-prefix=CHECK-SIMPLE-LTO-LIB --check-prefix=CHECK-SIMPLE-LTO-LIB-macosx + +// CHECK-SIMPLE-LTO-LIB: swift +// CHECK-SIMPLE-LTO-LIB-DAG: -emit-bc +// CHECK-SIMPLE-LTO-LIB-DAG: -lto=llvm-full +// CHECK-SIMPLE-LTO-LIB-NOT: -lto-library /foo/libLTO.dylib +// CHECK-SIMPLE-LTO-LIB-DAG: -o [[OBJECTFILE:.*\.bc]] + +// CHECK-SIMPLE-LTO-LIB-macosx: ld +// CHECK-SIMPLE-LTO-LIB-macosx-DAG: -lto_library /foo/libLTO.dylib +// CHECK-SIMPLE-LTO-LIB-macosx-DAG: [[OBJECTFILE]] diff --git a/test/Driver/linker-library-with-space.swift b/test/Driver/linker-library-with-space.swift new file mode 100644 index 0000000000000..b4497301147d6 --- /dev/null +++ b/test/Driver/linker-library-with-space.swift @@ -0,0 +1,18 @@ +// RUN: %swiftc_driver -sdk "" -driver-print-jobs -target x86_64-unknown-linux-gnu -Ffoo -Fsystem car -F cdr -framework bar -Lbaz -lboo -Xlinker -undefined %s 2>&1 > %t.linux.txt +// RUN: %FileCheck -check-prefix LINUX-lib-flag-space %s < %t.linux.txt + +// LINUX-lib-flag-space: swift +// LINUX-lib-flag-space: -o [[OBJECTFILE:.*]] + +// LINUX-lib-flag-space: clang{{(\.exe)?"? }} +// LINUX-lib-flag-space-DAG: -pie +// LINUX-lib-flag-space-DAG: [[OBJECTFILE]] +// LINUX-lib-flag-space-DAG: -lswiftCore +// LINUX-lib-flag-space-DAG: -L [[STDLIB_PATH:[^ ]+(/|\\\\)lib(/|\\\\)swift(/|\\\\)]] +// LINUX-lib-flag-space-DAG: -Xlinker -rpath -Xlinker [[STDLIB_PATH]] +// LINUX-lib-flag-space-DAG: -F foo -iframework car -F cdr +// LINUX-lib-flag-space-DAG: -framework bar +// LINUX-lib-flag-space-DAG: -L baz +// LINUX-lib-flag-space-DAG: -lboo +// LINUX-lib-flag-space-DAG: -Xlinker -undefined +// LINUX-lib-flag-space: -o main diff --git a/test/Driver/linker-tbd.swift b/test/Driver/linker-tbd.swift new file mode 100644 index 0000000000000..5fc174d65c24f --- /dev/null +++ b/test/Driver/linker-tbd.swift @@ -0,0 +1,9 @@ +// REQUIRES: OS=macosx +// Ensure .tbd input files are forwarded to the linker. + +// RUN: %empty-directory(%t) +// RUN: touch %t/foo.tbd +// RUN: %swiftc_driver_plain -driver-print-jobs -target x86_64-apple-macosx10.9 %S/../Inputs/empty.swift %t/foo.tbd | %FileCheck %s + +// CHECK: bin/ld{{"? }} +// CHECK-SAME: foo.tbd diff --git a/test/Driver/linker.swift b/test/Driver/linker.swift index 2282192c66250..4f36bf7eada78 100644 --- a/test/Driver/linker.swift +++ b/test/Driver/linker.swift @@ -1,6 +1,8 @@ // Must be able to run xcrun-return-self.sh // REQUIRES: shell // REQUIRES: rdar65281056 +// FIXME: When this is turned on, please move the test from linker-library-with-space.swift +// to this file and remove that file. // RUN: %swiftc_driver -sdk "" -driver-print-jobs -target x86_64-apple-macosx10.9 %s 2>&1 > %t.simple.txt // RUN: %FileCheck %s < %t.simple.txt // RUN: %FileCheck -check-prefix SIMPLE %s < %t.simple.txt diff --git a/test/Driver/loaded_module_trace_foundation.swift b/test/Driver/loaded_module_trace_foundation.swift index ff285189c6dce..89b1215de4871 100644 --- a/test/Driver/loaded_module_trace_foundation.swift +++ b/test/Driver/loaded_module_trace_foundation.swift @@ -6,17 +6,21 @@ // CHECK: "name":"loaded_module_trace_foundation" // CHECK: "arch":"{{[^"]*}}" // CHECK: "swiftmodules":[ -// CHECK-DAG: "{{[^"]*}}/ObjectiveC.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" -// CHECK-DAG: "{{[^"]*}}/Dispatch.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" + +// Darwin, Swift and SwiftOnoneSupport is expected to be locally built; +// everything else comes from the SDK, built from swiftinterface. + +// CHECK-DAG: "{{[^"]*}}/ObjectiveC.swiftmodule{{(\\/[^"]+[.]swift(module|interface))?}}" +// CHECK-DAG: "{{[^"]*}}/Dispatch.swiftmodule{{(\\/[^"]+[.]swift(module|interface))?}}" // CHECK-DAG: "{{[^"]*}}/Darwin.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" -// CHECK-DAG: "{{[^"]*}}/Foundation.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" +// CHECK-DAG: "{{[^"]*}}/Foundation.swiftmodule{{(\\/[^"]+[.]swift(module|interface))?}}" // CHECK-DAG: "{{[^"]*}}/Swift.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" // CHECK-DAG: "{{[^"]*}}/SwiftOnoneSupport.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" // CHECK: ] // CHECK: "swiftmodulesDetailedInfo":[ // CHECK: { // CHECK-DAG: "name":"Foundation" -// CHECK-DAG: "path":"{{[^"]*}}/Foundation.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" +// CHECK-DAG: "path":"{{[^"]*}}/Foundation.swiftmodule{{(\\/[^"]+[.]swift(module|interface))?}}" // CHECK-DAG: "isImportedDirectly":true // CHECK-DAG: "supportsLibraryEvolution":true // CHECK: } diff --git a/test/Driver/loaded_module_trace_header.swift b/test/Driver/loaded_module_trace_header.swift index 938d4ec47c7d3..aa7ae64f1adbb 100644 --- a/test/Driver/loaded_module_trace_header.swift +++ b/test/Driver/loaded_module_trace_header.swift @@ -8,10 +8,10 @@ // CHECK: "name":"loaded_module_trace_header" // CHECK: "arch":"{{[^"]*}}" // CHECK: "swiftmodules":[ -// CHECK-DAG: "{{[^"]*}}/ObjectiveC.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" -// CHECK-DAG: "{{[^"]*}}/Dispatch.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" +// CHECK-DAG: "{{[^"]*}}/ObjectiveC.swiftmodule{{(\\/[^"]+[.]swift(module|interface))?}}" +// CHECK-DAG: "{{[^"]*}}/Dispatch.swiftmodule{{(\\/[^"]+[.]swift(module|interface))?}}" // CHECK-DAG: "{{[^"]*}}/Darwin.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" -// CHECK-DAG: "{{[^"]*}}/Foundation.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" +// CHECK-DAG: "{{[^"]*}}/Foundation.swiftmodule{{(\\/[^"]+[.]swift(module|interface))?}}" // CHECK-DAG: "{{[^"]*}}/Swift.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" // CHECK-DAG: "{{[^"]*}}/SwiftOnoneSupport.swiftmodule{{(\\/[^"]+[.]swiftmodule)?}}" // CHECK: ] diff --git a/test/Driver/lock_interface.swift b/test/Driver/lock_interface.swift index a15b8fe2220a0..2c51726b19ef7 100644 --- a/test/Driver/lock_interface.swift +++ b/test/Driver/lock_interface.swift @@ -1,3 +1,4 @@ +// REQUIRES: radar82261445 // RUN: %empty-directory(%t) // RUN: %empty-directory(%t/sdk) // RUN: %empty-directory(%t/module-cache-lock) diff --git a/test/Driver/lto-output-mode-clash.swift b/test/Driver/lto-output-mode-clash.swift new file mode 100644 index 0000000000000..3114aec866c93 --- /dev/null +++ b/test/Driver/lto-output-mode-clash.swift @@ -0,0 +1,13 @@ +// REQUIRES: lld_lto + +// RUN: %swiftc_driver -driver-print-jobs %S/../Inputs/empty.swift -lto=llvm-full -static -emit-library -target x86_64-apple-macosx10.9 | %FileCheck %s +// RUN: %swiftc_driver -driver-print-jobs %S/../Inputs/empty.swift -lto=llvm-full -emit-library -target x86_64-apple-macosx10.9 | %FileCheck %s +// RUN: %swiftc_driver -driver-print-jobs %S/../Inputs/empty.swift -lto=llvm-full -c -target x86_64-apple-macosx10.9 | %FileCheck %s +// RUN: %swiftc_driver -driver-print-jobs %S/../Inputs/empty.swift -c -lto=llvm-full -target x86_64-apple-macosx10.9 | %FileCheck %s +// RUN: %swiftc_driver -driver-print-jobs %S/../Inputs/empty.swift -c -lto=llvm-full -emit-bc -target x86_64-apple-macosx10.9 | %FileCheck %s +// RUN: %swiftc_driver -driver-print-jobs %S/../Inputs/empty.swift -emit-bc -c -lto=llvm-full -target x86_64-apple-macosx10.9 | %FileCheck %s + +// CHECK: swift +// CHECK-DAG: -emit-bc +// CHECK-DAG: -lto=llvm-full +// CHECK-DAG: -o [[BITCODEFILE:.*\.bc]] diff --git a/test/Driver/options-repl.swift b/test/Driver/options-repl.swift index 118ebd7b834f9..52646ae073fab 100644 --- a/test/Driver/options-repl.swift +++ b/test/Driver/options-repl.swift @@ -16,7 +16,7 @@ // RUN: %swift_driver -sdk "" -lldb-repl -### | %FileCheck -check-prefix=LLDB %s -// RUN: %swift_driver -sdk "" -lldb-repl -D A -DB -D C -DD -L /path/to/libraries -L /path/to/more/libraries -F /path/to/frameworks -lsomelib -framework SomeFramework -sdk / -I "this folder" -module-name Test -target %target-triple -### | %FileCheck -check-prefix=LLDB-OPTS %s +// RUN: %swift_driver -sdk "" -lldb-repl -D A -DB -D C -DD -L /path/to/libraries -L /path/to/more/libraries -F /path/to/frameworks -lsomelib -l otherlib -framework SomeFramework -sdk / -I "this folder" -module-name Test -target %target-triple -### | %FileCheck -check-prefix=LLDB-OPTS %s // LLDB: lldb{{(\.exe)?"?}} {{"?}}--repl= // LLDB-NOT: -module-name @@ -30,6 +30,7 @@ // LLDB-OPTS-DAG: -L /path/to/more/libraries // LLDB-OPTS-DAG: -F /path/to/frameworks // LLDB-OPTS-DAG: -lsomelib +// LLDB-OPTS-DAG: -lotherlib // LLDB-OPTS-DAG: -framework SomeFramework // LLDB-OPTS-DAG: -I \"this folder\" // LLDB-OPTS: " diff --git a/test/Driver/pipe_round_robin.swift.gyb b/test/Driver/pipe_round_robin.swift.gyb index f3629422b1a5e..fcdc4211a9df5 100644 --- a/test/Driver/pipe_round_robin.swift.gyb +++ b/test/Driver/pipe_round_robin.swift.gyb @@ -1,5 +1,7 @@ // Windows doesn't track/use read() and poll() // UNSUPPORTED: windows +// This test is unreliable on busy machines. +// ALLOW_RETRIES: 5 // RUN: %empty-directory(%t/manyfuncs) // RUN: %empty-directory(%t/stats) // diff --git a/test/Driver/print_target_info.swift b/test/Driver/print_target_info.swift index daedbd64857fe..3151958be1e73 100644 --- a/test/Driver/print_target_info.swift +++ b/test/Driver/print_target_info.swift @@ -25,6 +25,8 @@ // CHECK-IOS: "libraryName": "swiftCompatibility51", // CHECK-IOS: "libraryName": "swiftCompatibilityDynamicReplacements" // CHECK-IOS: "filter": "executable" +// CHECK-IOS: "libraryName": "swiftCompatibilityConcurrency" +// CHECK-IOS: "filter": "all" // CHECK-IOS: ], // CHECK-IOS: "librariesRequireRPath": true // CHECK-IOS: } diff --git a/test/Driver/profiling.swift b/test/Driver/profiling.swift index bdc04e574d6a5..f96cea46e33b6 100644 --- a/test/Driver/profiling.swift +++ b/test/Driver/profiling.swift @@ -51,8 +51,8 @@ // LINUX: -u__llvm_profile_runtime // WINDOWS: clang{{(\.exe)?"? }} -// WINDOWS: lib{{(\\\\|/)}}swift{{(\\\\|/)}}clang{{(\\\\|/)}}lib{{(\\\\|/)}}windows{{(\\\\|/)}}clang_rt.profile-x86_64.lib -// WINDOWS: -u__llvm_profile_runtime +// WINDOWS: -Xlinker -include:__llvm_profile_runtime +// WINDOWS: -lclang_rt.profile // WASI: clang{{(\.exe)?"? }} // WASI: lib{{(\\\\|/)}}swift{{(\\\\|/)}}clang{{(\\\\|/)}}lib{{(\\\\|/)}}wasi{{(\\\\|/)}}libclang_rt.profile-wasm32.a diff --git a/test/Driver/static-stdlib-autolink-linux.swift b/test/Driver/static-stdlib-autolink-linux.swift index c38151b2db1a7..10a718ec254d1 100644 --- a/test/Driver/static-stdlib-autolink-linux.swift +++ b/test/Driver/static-stdlib-autolink-linux.swift @@ -2,6 +2,7 @@ // REQUIRES: static_stdlib // REQUIRES: concurrency // REQUIRES: libdispatch_static +// REQUIRES: rdar80900643 // RUN: %empty-directory(%t) // RUN: echo 'public func asyncFunc() async { print("Hello") }' > %t/asyncModule.swift @@ -18,8 +19,8 @@ // RUN: ldd %t/main | %FileCheck %s --check-prefix=LDD; \ // RUN: fi -// LDD-NOT: libswiftCore.so -// LDD-NOT: libswift_Concurrency.so +// LDD-NOT: libswiftCore.so +// LDD-NOT: libswift_Concurrency.so import asyncModule diff --git a/test/Driver/swift-version-6-asserts.swift b/test/Driver/swift-version-6-asserts.swift new file mode 100644 index 0000000000000..8bbd29fb798ee --- /dev/null +++ b/test/Driver/swift-version-6-asserts.swift @@ -0,0 +1,54 @@ +// Tests temporary -swift-version 6 behavior in compilers with asserts enabled, +// where we allow -swift-version 6 for testing but don't list it as a permitted +// version. + +// REQUIRES: asserts + +// RUN: not %target-swiftc_driver -swift-version 6 -typecheck %s 2>&1 | %FileCheck --check-prefix ERROR_6 %s +// RUN: not %target-swiftc_driver -swift-version 7 -typecheck %s 2>&1 | %FileCheck --check-prefix ERROR_7 %s + +#if swift(>=3) +asdf +// ERROR_6: [[@LINE-1]]:1: error: {{cannot find 'asdf' in scope}} +#else +jkl +#endif + +#if swift(>=3.1) +asdf +// ERROR_6: [[@LINE-1]]:1: error: {{cannot find 'asdf' in scope}} +#else +jkl +#endif + +#if swift(>=4) +asdf +// ERROR_6: [[@LINE-1]]:1: error: {{cannot find 'asdf' in scope}} +#else +jkl +#endif + +#if swift(>=4.1) +asdf +// ERROR_6: [[@LINE-1]]:1: error: {{cannot find 'asdf' in scope}} +#else +jkl +#endif + +#if swift(>=6) +asdf +// ERROR_6: [[@LINE-1]]:1: error: {{cannot find 'asdf' in scope}} +#else +jkl +#endif + +#if swift(>=7) +asdf +#else +jkl +// ERROR_6: [[@LINE-1]]:1: error: {{cannot find 'jkl' in scope}} +#endif + +// ERROR_7: :0: error: invalid value '7' in '-swift-version 7' +// ERROR_7: :0: note: valid arguments to '-swift-version' +// ERROR_7-NOT: '6' diff --git a/test/Driver/swift-version-6-noasserts.swift b/test/Driver/swift-version-6-noasserts.swift new file mode 100644 index 0000000000000..d79016eab9e62 --- /dev/null +++ b/test/Driver/swift-version-6-noasserts.swift @@ -0,0 +1,10 @@ +// Tests temporary -swift-version 6 behavior in compilers with asserts disabled, +// where we don't allow -swift-version 6 to keep it out of release compilers. + +// UNSUPPORTED: asserts + +// RUN: not %target-swiftc_driver -swift-version 6 -typecheck %s 2>&1 | %FileCheck --check-prefix ERROR_6 %s + +// ERROR_6: :0: error: invalid value '6' in '-swift-version 6' +// ERROR_7: :0: note: valid arguments to '-swift-version' +// ERROR_7-NOT: '6' diff --git a/test/Frontend/Inputs/same_filename/A/File.swift b/test/Frontend/Inputs/same_filename/A/File.swift new file mode 100644 index 0000000000000..4e6a6de65314d --- /dev/null +++ b/test/Frontend/Inputs/same_filename/A/File.swift @@ -0,0 +1 @@ +class Foo {} diff --git a/test/Frontend/Inputs/same_filename/B/File.swift b/test/Frontend/Inputs/same_filename/B/File.swift new file mode 100644 index 0000000000000..bf1673a091166 --- /dev/null +++ b/test/Frontend/Inputs/same_filename/B/File.swift @@ -0,0 +1 @@ +class Bar {} diff --git a/test/Frontend/allow-errors.swift b/test/Frontend/allow-errors.swift index 249e57c30f6a0..a1e7a59c1c04d 100644 --- a/test/Frontend/allow-errors.swift +++ b/test/Frontend/allow-errors.swift @@ -1,126 +1,18 @@ // RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/mods) +// RUN: touch %t/empty.swift -// The module should be generated regardless of errors and diagnostic should still be output -// RUN: %target-swift-frontend -verify -emit-module -o %t/errors.swiftmodule -emit-reference-dependencies-path %t/errors.swiftdeps -emit-dependencies-path %t/errors.d -experimental-allow-module-with-compiler-errors -D ERROR_MODULE -primary-file %s -// RUN: llvm-bcanalyzer %t/errors.swiftmodule | %FileCheck -check-prefix=CHECK-BC %s -// CHECK-BC-NOT: UnknownCode -// RUN: ls %t/errors.swiftdeps -// RUN: ls %t/errors.d - -#if ERROR_MODULE -public struct ValidStructInvalidMember { - public var member: String - public let memberMissingType: undefined // expected-error {{cannot find type 'undefined'}} - - public var memberMissingTypeValidSets: undefined { // expected-error {{cannot find type 'undefined'}} - willSet { - print("Setting value \(newValue)") - } - didSet { - print("Set value \(oldValue)") - } - } - public var memberInvalidSets: Int { - willSet { - undefined // expected-error {{cannot find 'undefined'}} - } - didSet { - undefined // expected-error {{cannot find 'undefined'}} - } - } - - public lazy var lazyMemberMissingTypeValidBody: undefined = { // expected-error {{cannot find type 'undefined'}} - return "" - }() - public lazy var lazyMemberInvalidBody: String = { - return undefined // expected-error {{cannot find 'undefined'}} - }() - - public var memberMissingTypeValidGetSets: String { - get { member } - set { member = "" } - } - public var memberInvalidGetSet: String { - get { undefined } // expected-error {{cannot find 'undefined'}} - set { undefined = "" } // expected-error {{cannot find 'undefined'}} - } - - public func funcBadArg(_ arg: undefined? = nil) {} // expected-error {{cannot find type 'undefined'}} -} - -public func validFunc() -> String { "" } - -public func invalidFuncBody() -> ValidStructInvalidMember { - ret // expected-error {{cannot find 'ret'}} -} +// The module should be generated regardless of errors, including .swiftdeps, .d, +// .swiftsourceinfo, etc. Diagnostics should still be output as well -public func invalidFunc() -> undefined {} // expected-error {{cannot find type 'undefined'}} +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errorsmain.partial.swiftmodule -emit-reference-dependencies-path %t/mods/errorsmain.partial.swiftdeps -experimental-allow-module-with-compiler-errors -primary-file %s +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errorsempty.partial.swiftmodule %t/empty.swift +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errors.swiftmodule -experimental-allow-module-with-compiler-errors %t/mods/errorsmain.partial.swiftmodule %t/mods/errorsempty.partial.swiftmodule -emit-module-source-info -emit-module-doc -emit-dependencies-path %t/mods/errors.d -emit-objc-header -emit-objc-header-path %t/mods/errors.h -emit-module-interface-path %t/mods/errors.swiftinterface -emit-tbd-path %t/mods/errors.tbd -extension undefined: undefined {} // expected-error {{cannot find type 'undefined'}} - -class GenericClass {} -class InvalidSuperclass: GenericClass {} // expected-error {{cannot find type 'undefined'}} -#endif - -// RUN: %target-swift-frontend -emit-module -o %t/validUses.swiftmodule -experimental-allow-module-with-compiler-errors -I%t -D VALID_USES %s 2>&1 | %FileCheck -check-prefix=CHECK-VALID %s -// RUN: llvm-bcanalyzer %t/validUses.swiftmodule | %FileCheck -check-prefix=CHECK-BC %s -#if VALID_USES -import errors -func test(s: ValidStructInvalidMember) { - print(s.member) - print(validFunc()) - print(invalidFuncBody()) -} - -// Check SIL diagnostics are still output (no reason not to output SIL since -// there were no errors) -func other() -> Int {} -// CHECK-VALID: allow-errors.swift:[[@LINE-1]]:22: error: missing return in global function expected to return 'Int' -func other2() -> Bool {} -// CHECK-VALID: allow-errors.swift:[[@LINE-1]]:24: error: missing return in global function expected to return 'Bool' -#endif - -// All invalid uses should have no errors in the file itself, all referenced -// invalid declarations should have an error elsewhere (but we don't care what -// that location is) - -// RUN: %target-swift-frontend -emit-module -o %t/invalidTopUse.swiftmodule -experimental-allow-module-with-compiler-errors -I%t -D INVALID_TOP_LEVEL_USE %s 2>&1 | %FileCheck -check-prefix=CHECK-INVALID-TOP %s -// RUN: llvm-bcanalyzer %t/invalidTopUse.swiftmodule | %FileCheck -check-prefix=CHECK-BC %s -#if INVALID_TOP_LEVEL_USE -import errors -func test() { - invalidFunc() -} -// CHECK-INVALID-TOP-NOT: allow-errors.swift:{{.*}} error: -// CHECK-INVALID-TOP: error: allowing deserialization of error type '' in module 'errors' -// CHECK-INVALID-TOP: error: allowing deserialization of invalid declaration 'invalidFunc()' (global function) in module 'errors' -// CHECK-INVALID-TOP-NOT: allow-errors.swift:{{.*}} error: -#endif - -// RUN: %target-swift-frontend -emit-module -o %t/invalidMemberUse.swiftmodule -experimental-allow-module-with-compiler-errors -I%t -D INVALID_MEMBER_USE %s 2>&1 | %FileCheck -check-prefix=CHECK-INVALID-MEMBER %s -// RUN: llvm-bcanalyzer %t/invalidMemberUse.swiftmodule | %FileCheck -check-prefix=CHECK-BC %s -#if INVALID_MEMBER_USE -import errors -func test(s: ValidStructInvalidMember) { - print(s.memberMissingType) -} -// CHECK-INVALID-MEMBER-NOT: allow-errors.swift:{{.*}} error: -// CHECK-INVALID-MEMBER: error: allowing deserialization of error type '' in module 'errors' -// CHECK-INVALID-MEMBER: error: allowing deserialization of invalid declaration '_' (getter) in module 'errors' -// CHECK-INVALID-MEMBER: error: allowing deserialization of invalid declaration 'memberMissingType' (property) in module 'errors' -// CHECK-INVALID-MEMBER-NOT: allow-errors.swift:{{.*}} error: -#endif +// RUN: llvm-bcanalyzer %t/mods/errors.swiftmodule | %FileCheck -check-prefix=CHECK-BC %s +// RUN: ls %t/mods/errorsmain.partial.swiftdeps %t/mods/errors.d %t/mods/errors.swiftsourceinfo %t/mods/errors.swiftdoc %t/mods/errors.h +// RUN: not ls %t/mods/errors.swiftinterface +// RUN: not ls %t/mods/errors.tbd +// CHECK-BC-NOT: UnknownCode -// RUN: %target-swift-frontend -emit-module -o %t/invalidMethodUse.swiftmodule -experimental-allow-module-with-compiler-errors -I%t -D INVALID_METHOD_USE %s 2>&1 | %FileCheck -check-prefix=CHECK-INVALID-METHOD %s -// RUN: llvm-bcanalyzer %t/invalidMethodUse.swiftmodule | %FileCheck -check-prefix=CHECK-BC %s -#if INVALID_METHOD_USE -import errors -func test(s: ValidStructInvalidMember) { - s.funcBadArg() -} -// CHECK-INVALID-METHOD-NOT: allow-errors.swift:{{.*}} error: -// CHECK-INVALID-METHOD: error: allowing deserialization of error type '' in module 'errors' -// CHECK-INVALID-METHOD: error: allowing deserialization of invalid declaration 'arg' (parameter) in module 'errors' -// CHECK-INVALID-METHOD: error: allowing deserialization of invalid declaration 'funcBadArg' (instance method) in module 'errors' -// CHECK-INVALID-METHOD-NOT: allow-errors.swift:{{.*}} error: -#endif +public func invalid() -> undefined {} // expected-error {{cannot find type 'undefined'}} diff --git a/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift index 5372941b605d0..7cb2a892a61cd 100644 --- a/test/Frontend/crash-in-user-code.swift +++ b/test/Frontend/crash-in-user-code.swift @@ -12,6 +12,7 @@ // CHECK: Stack dump: // CHECK-NEXT: Program arguments: // CHECK-NEXT: Swift version +// CHECK-NEXT: Compiling with effective version // CHECK-NEXT: Contents of {{.*}}.filelist.txt: // CHECK-NEXT: --- // CHECK-NEXT: crash-in-user-code.swift diff --git a/test/Frontend/crash.swift b/test/Frontend/crash.swift index a95e6cca6c1b0..cc06e048c5acb 100644 --- a/test/Frontend/crash.swift +++ b/test/Frontend/crash.swift @@ -2,14 +2,24 @@ // RUN: not --crash %target-swift-frontend -typecheck -debug-crash-after-parse -filelist %t.filelist.txt 2>&1 | %FileCheck %s // Check that we see the contents of the input file list in the crash log. +// CHECK-NOT: Compiling with {{.*}} while allowing modules with compiler errors enabled // CHECK-LABEL: Stack dump // CHECK-NEXT: Program arguments: {{.*swift(-frontend)?(c?)(\.exe)?}} // CHECK-NEXT: Swift version +// CHECK-NEXT: Compiling with effective version // CHECK-NEXT: Contents of {{.*}}.filelist.txt: // CHECK-NEXT: --- // CHECK-NEXT: test{{[\\/]}}Frontend{{[\\/]}}crash.swift{{$}} // CHECK-NEXT: --- +// RUN: not --crash %target-swift-frontend -typecheck -debug-crash-after-parse -experimental-allow-module-with-compiler-errors %s 2>&1 | %FileCheck -check-prefix CHECK-ALLOW %s +// CHECK-ALLOW: Program arguments: {{.*}} -experimental-allow-module-with-compiler-errors +// CHECK-ALLOW: Compiling with effective version + +// RUN: not --crash %target-swift-frontend -typecheck -debug-crash-after-parse -experimental-allow-module-with-compiler-errors -swift-version 5 %s 2>&1 | %FileCheck -check-prefix CHECK-CURRENT %s +// CHECK-CURRENT: Program arguments: {{.*}} -experimental-allow-module-with-compiler-errors +// CHECK-CURRENT: Compiling with the current language version + func anchor() {} anchor() diff --git a/test/Frontend/debug-cycles.swift b/test/Frontend/debug-cycles.swift new file mode 100644 index 0000000000000..709eedb3acf23 --- /dev/null +++ b/test/Frontend/debug-cycles.swift @@ -0,0 +1,12 @@ + +// RUN: not %target-swift-frontend -typecheck -debug-cycles %s 2>&1 | %FileCheck %s --match-full-lines --strict-whitespace --color=false + +class Outer2: Outer2.Inner { + class Inner {} +} +// CHECK:===CYCLE DETECTED=== +// CHECK-NEXT: `--TypeCheckSourceFileRequest({{.*}}) +// CHECK-NEXT: `--SuperclassDeclRequest({{.*}}) +// CHECK-NEXT: `--InheritedDeclsReferencedRequest({{.*}}) +// CHECK-NEXT: `--QualifiedLookupRequest({{.*}}) +// CHECK-NEXT: `--SuperclassDeclRequest({{.*}}) (cyclic dependency) diff --git a/test/Frontend/emit-supported-features.swift b/test/Frontend/emit-supported-features.swift index 2a1bceb431ae1..c5be4212b1711 100644 --- a/test/Frontend/emit-supported-features.swift +++ b/test/Frontend/emit-supported-features.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend -emit-supported-features %s | %FileCheck %s // CHECK: "SupportedArguments" +// CHECK: "abi" // CHECK: "emit-module" // CHECK: "LastOption" // CHECK: "SupportedFeatures" diff --git a/test/Frontend/print-sil-debug-info.swift b/test/Frontend/print-sil-debug-info.swift new file mode 100644 index 0000000000000..6cba60632067e --- /dev/null +++ b/test/Frontend/print-sil-debug-info.swift @@ -0,0 +1,13 @@ +// Just a simple test to see whether debug info is printed to SIL +// if any of the '-g' (except '-gnone') is given. + +// RUN: %target-swift-frontend -emit-sil -gnone %s | %FileCheck --check-prefix=CHECK-GNONE %s +// RUN: %target-swift-frontend -emit-sil -gline-tables-only %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -g %s | %FileCheck %s + +func foo(x: Int, y: Int) -> Int { + // CHECK: struct_extract + // CHECK-SAME: loc "{{.*}}.swift":{{[0-9]+}}:{{[0-9]+}}, scope {{[0-9]+}} + // CHECK-GNONE-NOT: loc "{{.*}}.swift":{{[0-9]+}}:{{[0-9]+}}, scope {{[0-9]+}} + x + y +} diff --git a/test/Frontend/same_filename_twice.swift b/test/Frontend/same_filename_twice.swift new file mode 100644 index 0000000000000..ab624734fa328 --- /dev/null +++ b/test/Frontend/same_filename_twice.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) + +// A module should fail to be generated if the same filename is used twice and '-experimental-allow-module-with-compiler-errors' is not passed + +// RUN: not %target-swift-frontend -emit-module -o %t/no_allow_compiler_errors.swiftmodule %S/Inputs/same_filename/A/File.swift %S/Inputs/same_filename/B/File.swift +// RUN: not ls %t/no_allow_compiler_errors.swiftmodule + +// If '-experimental-allow-module-with-compiler-errors' is passed, we should throw an error but still generate a module + +// RUN: %target-swift-frontend -emit-module -experimental-allow-module-with-compiler-errors -o %t/allow_compiler_errors.swiftmodule %S/Inputs/same_filename/A/File.swift %S/Inputs/same_filename/B/File.swift 2>&1 | %FileCheck %s +// RUN: ls %t/allow_compiler_errors.swiftmodule + +// CHECK: filename "File.swift" used twice: diff --git a/test/Generics/Inputs/rdar79564324_other.swift b/test/Generics/Inputs/rdar79564324_other.swift new file mode 100644 index 0000000000000..ded567b2c0a72 --- /dev/null +++ b/test/Generics/Inputs/rdar79564324_other.swift @@ -0,0 +1,9 @@ +public protocol P { + associatedtype A: P +} + +public func bar(from: T) {} + +@inlinable public func foo(from: T, to: U) where T == T.A, U : P, U.A == T { + bar(from: from) +} diff --git a/test/Generics/concrete_same_type_versus_anyobject.swift b/test/Generics/concrete_same_type_versus_anyobject.swift index de063a87970ef..0654fc2315edd 100644 --- a/test/Generics/concrete_same_type_versus_anyobject.swift +++ b/test/Generics/concrete_same_type_versus_anyobject.swift @@ -30,3 +30,13 @@ extension G2 where U == C, U : AnyObject {} extension G2 where U : C, U : AnyObject {} // expected-warning@-1 {{redundant constraint 'U' : 'AnyObject'}} // expected-note@-2 {{constraint 'U' : 'AnyObject' implied here}} + +// Explicit AnyObject conformance vs derived same-type +protocol P { + associatedtype A where A == C +} + +// CHECK-LABEL: Generic signature: +func explicitAnyObjectIsRedundant(_: T) where T.A : AnyObject {} +// expected-warning@-1 {{redundant constraint 'T.A' : 'AnyObject'}} +// expected-note@-2 {{constraint 'T.A' : 'AnyObject' implied here}} diff --git a/test/Generics/concrete_type_property_of_suffix.swift b/test/Generics/concrete_type_property_of_suffix.swift new file mode 100644 index 0000000000000..c080298060b79 --- /dev/null +++ b/test/Generics/concrete_type_property_of_suffix.swift @@ -0,0 +1,28 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify + +protocol P { + associatedtype T where T == U? + associatedtype U +} + +func sameType(_: T.Type, _: T.Type) {} + +func foo(_: X, _: Y) { + // X.T is Optional. + sameType(X.T.self, X.U?.self) + + // Y.T is Optional. + sameType(Y.T.self, Y.U?.self) +} + +protocol Q { + associatedtype T where T == Self? +} + +func foo(_: X, _: Y) { + // X.T is Optional. + sameType(X.T.self, X?.self) + + // Y.T is Optional. + sameType(Y.T.self, Y?.self) +} diff --git a/test/Generics/conditional_requirement_inference.swift b/test/Generics/conditional_requirement_inference.swift index 92b523a9115ba..8ff770ffb5500 100644 --- a/test/Generics/conditional_requirement_inference.swift +++ b/test/Generics/conditional_requirement_inference.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift -// RUN: not %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift -requirement-machine=on +// RUN: not %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine=on %s 2>&1 | %FileCheck %s // Valid example diff --git a/test/Generics/derived_via_concrete.swift b/test/Generics/derived_via_concrete.swift new file mode 100644 index 0000000000000..98dd81cf2b69b --- /dev/null +++ b/test/Generics/derived_via_concrete.swift @@ -0,0 +1,57 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -debug-generic-signatures -typecheck %s 2>&1 | %FileCheck %s + +protocol P {} +class C {} + +class X : U {} +class Y : V {} +class Z : W {} + +protocol U { + associatedtype T : P +} + +protocol V { + associatedtype T : C +} + +protocol W { + associatedtype T : AnyObject +} + +// CHECK: Generic signature: , B : P> +func derivedViaConcreteX1(_: A, _: B) + where A : U, A : X {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'U'}} +// expected-note@-2 {{conformance constraint 'A' : 'U' implied here}} + +// CHECK: Generic signature: , B : P> +func derivedViaConcreteX2(_: A, _: B) + where A : U, B : P, A : X {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'U'}} +// expected-note@-2 {{conformance constraint 'A' : 'U' implied here}} + +// CHECK: Generic signature: , B : C> +func derivedViaConcreteY1(_: A, _: B) + where A : V, A : Y {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'V'}} +// expected-note@-2 {{conformance constraint 'A' : 'V' implied here}} + +// CHECK: Generic signature: , B : C> +func derivedViaConcreteY2(_: A, _: B) + where A : V, B : C, A : Y {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'V'}} +// expected-note@-2 {{conformance constraint 'A' : 'V' implied here}} + +// CHECK: Generic signature: , B : AnyObject> +func derivedViaConcreteZ1(_: A, _: B) + where A : W, A : Z {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'W'}} +// expected-note@-2 {{conformance constraint 'A' : 'W' implied here}} + +// CHECK: Generic signature: , B : AnyObject> +func derivedViaConcreteZ2(_: A, _: B) + where A : W, B : AnyObject, A : Z {} +// expected-warning@-1 {{redundant conformance constraint 'A' : 'W'}} +// expected-note@-2 {{conformance constraint 'A' : 'W' implied here}} diff --git a/test/Generics/function_defs.swift b/test/Generics/function_defs.swift index 5b83dba7d8c0c..8b5d8499ebfa3 100644 --- a/test/Generics/function_defs.swift +++ b/test/Generics/function_defs.swift @@ -34,7 +34,7 @@ func min(_ x: T, y: T) -> T { //===----------------------------------------------------------------------===// func existential(_ t1: T, t2: T, u: U) { - var eqComp : EqualComparable = t1 // expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}} + var eqComp : EqualComparable = t1 // Ok eqComp = u if t1.isEqual(eqComp) {} // expected-error{{cannot convert value of type 'EqualComparable' to expected argument type 'T'}} if eqComp.isEqual(t2) {} // expected-error{{member 'isEqual' cannot be used on value of protocol type 'EqualComparable'; use a generic constraint instead}} @@ -49,20 +49,11 @@ func otherExistential(_ t1: T) { otherEqComp = t1 // expected-error{{value of type 'T' does not conform to 'OtherEqualComparable' in assignment}} _ = otherEqComp - var otherEqComp2 : OtherEqualComparable // expected-error{{protocol 'OtherEqualComparable' can only be used as a generic constraint}} + var otherEqComp2 : OtherEqualComparable // Ok otherEqComp2 = t1 // expected-error{{value of type 'T' does not conform to 'OtherEqualComparable' in assignment}} _ = otherEqComp2 - _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{value of type 'T' does not conform to 'EqualComparable & OtherEqualComparable' in coercion}} expected-error{{protocol 'OtherEqualComparable' can only be used as a generic constraint}} expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}} -} - -protocol Runcible { - func runce(_ x: A) - func spoon(_ x: Self) -} - -func testRuncible(_ x: Runcible) { // expected-error{{protocol 'Runcible' can only be used as a generic constraint}} - x.runce(5) + _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{value of type 'T' does not conform to 'EqualComparable & OtherEqualComparable' in coercion}} } //===----------------------------------------------------------------------===// diff --git a/test/Generics/generic_objc_superclass.swift b/test/Generics/generic_objc_superclass.swift new file mode 100644 index 0000000000000..308ffdc5f963e --- /dev/null +++ b/test/Generics/generic_objc_superclass.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift %clang-importer-sdk -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +// REQUIRES: objc_interop + +import Foundation + +class Generic : NSObject {} + +func foo, U>(_: T, _: U) { + _ = T.self + _ = U.self +} + +// CHECK-LABEL: Requirement machine for <τ_0_0, τ_0_1 where τ_0_0 : Generic<τ_0_1>> +// CHECK-NEXT: Rewrite system: { +// CHECK-NEXT: - τ_0_0.[superclass: Generic<τ_0_0> with <τ_0_1>] => τ_0_0 +// CHECK-NEXT: - τ_0_0.[layout: AnyObject] => τ_0_0 +// CHECK-NEXT: } +// CHECK-NEXT: Property map: { +// CHECK-NEXT: τ_0_0 => { layout: AnyObject superclass: [superclass: Generic<τ_0_0> with <τ_0_1>] } +// CHECK-NEXT: } diff --git a/test/Generics/gsb_canonical_type_bug_1.swift b/test/Generics/gsb_canonical_type_bug_1.swift new file mode 100644 index 0000000000000..792f582db4fdc --- /dev/null +++ b/test/Generics/gsb_canonical_type_bug_1.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-emit-silgen %s -requirement-machine=on | %FileCheck %s + +// The GSB computes an incorrect canonical type here, which caused a SILGen assert. + +protocol P1 { + associatedtype A +} + +protocol P2 { + associatedtype A +} + +extension P1 where Self: P2 { + func foo(_: A, _: T) { } +} + +// CHECK-LABEL: sil hidden [ossa] @$s24gsb_canonical_type_bug_12P1PA2A2P2RzrlE3fooyy1AACQz_qd__tlF : $@convention(method) (@in_guaranteed Self.A, @in_guaranteed T, @in_guaranteed Self) -> () diff --git a/test/Generics/gsb_canonical_type_bug_2.swift b/test/Generics/gsb_canonical_type_bug_2.swift new file mode 100644 index 0000000000000..810743c1022f4 --- /dev/null +++ b/test/Generics/gsb_canonical_type_bug_2.swift @@ -0,0 +1,26 @@ +// RUN: %target-swift-emit-silgen %s -requirement-machine=on | %FileCheck %s +// RUN: %target-swift-emit-silgen %s -requirement-machine=off | %FileCheck %s --check-prefix=CHECK-GSB +// RUN: not --crash %target-swift-emit-silgen %s -requirement-machine=verify + +protocol P1 { + associatedtype A +} + +protocol P2 { + associatedtype A +} + +protocol P3 { + associatedtype B + associatedtype C : P3 where C.C == C, C.B == B +} + +extension P2 { + func foo(_: T, _: T.A) where T.B == T, T.A == A { + _ = A.self + _ = T.A.self + } +} + +// CHECK-LABEL: sil hidden [ossa] @$s24gsb_canonical_type_bug_22P2PAAE3fooyyqd___1AQztAA2P1Rd__AA2P3Rd__1BAaHPQyd__Rsd__AeaGPQyd__AFRSlF : $@convention(method) (@in_guaranteed T, @in_guaranteed Self.A, @in_guaranteed Self) -> () +// CHECK-GSB-LABEL: sil hidden [ossa] @$s24gsb_canonical_type_bug_22P2PAAE3fooyyqd___1AAA2P1PQyd__tAaFRd__AA2P3Rd__1BAaIPQyd__Rsd__AhERtzlF : $@convention(method) (@in_guaranteed T, @in_guaranteed T.A, @in_guaranteed Self) -> () diff --git a/test/Generics/gsb_canonical_type_bug_3.swift b/test/Generics/gsb_canonical_type_bug_3.swift new file mode 100644 index 0000000000000..200f428f20d3a --- /dev/null +++ b/test/Generics/gsb_canonical_type_bug_3.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-emit-silgen %s -requirement-machine=on | %FileCheck %s + +// The GSB computes an incorrect canonical type for bar() which causes +// SILGen to assert. + +public protocol P1 { + associatedtype A +} + +public protocol P2 { + associatedtype A +} + +public protocol P3 : P1, P2 { + typealias B = A +} + +public protocol P4 {} + +func bar(x: T, y: T.B, _: X) {} + +// CHECK-LABEL: sil hidden [ossa] @$s24gsb_canonical_type_bug_33bar1x1y_yx_1AAA2P1PQzq_tAA2P3RzAA2P4Rzr0_lF : $@convention(thin) (@in_guaranteed T, @in_guaranteed T.A, @in_guaranteed X) -> () diff --git a/test/Generics/member_type_of_superclass_bound.swift b/test/Generics/member_type_of_superclass_bound.swift new file mode 100644 index 0000000000000..881faf2ab9bf4 --- /dev/null +++ b/test/Generics/member_type_of_superclass_bound.swift @@ -0,0 +1,24 @@ +// RUN: %target-swift-emit-silgen %s -requirement-machine=verify + +// The substituted type of SS.x.x is computed by taking the type of S.x, +// which is T.T in the generic signature , and then +// canonicalizing it in the generic signature , U>. +// +// The latter generic signature does not have a conformance requirement T : P, +// but the superclass bound C of T conforms to P concretely; make sure that +// the requirement machine's getCanonicalTypeInContext() can handle this. +public protocol P { + associatedtype T +} + +public class C : P {} + +public struct S { + public var x: T.T +} + +public struct SS, U> { + public var x: S +} + + diff --git a/test/Generics/rdar62903491.swift b/test/Generics/rdar62903491.swift index 19736d59784b1..7fc6b3a6f6831 100644 --- a/test/Generics/rdar62903491.swift +++ b/test/Generics/rdar62903491.swift @@ -5,7 +5,7 @@ protocol P { associatedtype X : P } -// Anything that mentions 'T : P' minimizes to 'U : P'. +// Anything that mentions 'T : P' and 'U : P' minimizes to 'U : P'. // expected-warning@+2 {{redundant conformance constraint 'U' : 'P'}} // expected-note@+1 {{conformance constraint 'U' : 'P' implied here}} @@ -31,6 +31,8 @@ func oneProtocol4(_: T, _: U) where U : P, T.X == U, T : P, U.X == T {} // CHECK-LABEL: oneProtocol4 // CHECK: Generic signature: +// Anything that only mentions 'T : P' minimizes to 'T : P'. + func oneProtocol5(_: T, _: U) where T : P, T.X == U, U.X == T {} // CHECK-LABEL: oneProtocol5 // CHECK: Generic signature: @@ -39,14 +41,12 @@ func oneProtocol6(_: T, _: U) where T.X == U, U.X == T, T : P {} // CHECK-LABEL: oneProtocol6 // CHECK: Generic signature: -// Anything that mentions 'U : P' but not 'T : P' minimizes to 'U : P'. +// Anything that only mentions 'U : P' minimizes to 'U : P'. -// FIXME: Need to emit warning here too func oneProtocol7(_: T, _: U) where U : P, T.X == U, U.X == T {} // CHECK-LABEL: oneProtocol7 // CHECK: Generic signature: -// FIXME: Need to emit warning here too func oneProtocol8(_: T, _: U) where T.X == U, U.X == T, U : P {} // CHECK-LABEL: oneProtocol8 // CHECK: Generic signature: diff --git a/test/Generics/rdar74890907.swift b/test/Generics/rdar74890907.swift new file mode 100644 index 0000000000000..cf3be77a324e5 --- /dev/null +++ b/test/Generics/rdar74890907.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +protocol P1 { + associatedtype A : P2 +} + +protocol P2 { + associatedtype A +} + +// CHECK: Generic signature: +func testTU(_: T, _: U) where T.A : P1, T.A.A == U, U.A : P1, U.A.A == T {} +// expected-warning@-1 {{redundant conformance constraint 'U' : 'P2'}} +// expected-note@-2 {{conformance constraint 'U' : 'P2' implied here}} + +// CHECK: Generic signature: +func testU(_: T, _: U) where T.A : P1, T.A.A == U, U.A : P1, U.A.A == T {} + +// CHECK: Generic signature: +func testT(_: T, _: U) where T.A : P1, T.A.A == U, U.A : P1, U.A.A == T {} diff --git a/test/Generics/rdar75656022.swift b/test/Generics/rdar75656022.swift index 88160472487d3..aa3d8c46f0503 100644 --- a/test/Generics/rdar75656022.swift +++ b/test/Generics/rdar75656022.swift @@ -48,9 +48,9 @@ struct OriginalExampleWithWarning where A : P2, B : P2, A.T == B.T { init(_: C) where C : P1, D : P1, // expected-warning {{redundant conformance constraint 'D' : 'P1'}} - C.T : P1, // expected-warning {{redundant conformance constraint 'C.T' : 'P1'}} - A == S1>, // expected-note {{conformance constraint 'D' : 'P1' implied here}} - // expected-note@-1 {{conformance constraint 'C.T' : 'P1' implied here}} + C.T : P1, // expected-warning {{redundant conformance constraint 'D' : 'P1'}} + // expected-note@-1 {{conformance constraint 'D' : 'P1' implied here}} + A == S1>, C.T == D, E == D.T { } } @@ -72,7 +72,6 @@ struct WithoutBogusGenericParametersWithWarning where A : P2, B : P2, A.T where C : P1, C.T : P1, // expected-warning {{redundant conformance constraint 'C.T' : 'P1'}} A == S1> {} - // expected-note@-1 {{conformance constraint 'C.T' : 'P1' implied here}} } // Same as above but without unnecessary generic parameters @@ -82,4 +81,4 @@ struct WithoutBogusGenericParametersWithoutWarning where A : P2, B : P2, A init(_: C) where C : P1, A == S1> {} -} \ No newline at end of file +} diff --git a/test/Generics/rdar77462797.swift b/test/Generics/rdar77462797.swift new file mode 100644 index 0000000000000..5890a68e17bd2 --- /dev/null +++ b/test/Generics/rdar77462797.swift @@ -0,0 +1,38 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +protocol P {} + +protocol Q { + associatedtype T : P +} + +class S : Q {} + +struct G where X : Q { + // no redundancies + func foo1() where X == S {} + // CHECK: Generic signature: , Y : P> + + // 'Y : P' is redundant, but only via an inferred requirement from S, + // so we should not diagnose it + func foo2() where X == S, Y : P {} + // CHECK: Generic signature: , Y : P> + + // 'X : Q' is redundant + func foo3() where X : Q, X == S, Y : P {} + // expected-warning@-1 {{redundant conformance constraint 'X' : 'Q'}} + // expected-note@-2 {{conformance constraint 'X' : 'Q' implied here}} + // CHECK: Generic signature: , Y : P> + + // 'T.T : P' is redundant + func foo4(_: T) where X == S, T.T : P {} + // expected-warning@-1 {{redundant conformance constraint 'T.T' : 'P'}} + // expected-note@-2 {{conformance constraint 'T.T' : 'P' implied here}} + // CHECK: Generic signature: , Y : P, T : Q> +} + +func foo(_: X, _: Y) where X : Q, X : S, Y : P {} +// expected-warning@-1 {{redundant conformance constraint 'X' : 'Q'}} +// expected-note@-2 {{conformance constraint 'X' : 'Q' implied here}} +// CHECK: Generic signature: , Y : P> \ No newline at end of file diff --git a/test/Generics/rdar77807692.swift b/test/Generics/rdar77807692.swift new file mode 100644 index 0000000000000..dd0f55ec0b338 --- /dev/null +++ b/test/Generics/rdar77807692.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +protocol P1 { + associatedtype T : Hashable +} + +struct S1 {} + +extension S1 : P1 where Value : P1 { + typealias T = Value.T +} + +protocol P2 { + associatedtype Value: P1 +} + +struct S2 where Y.Value == X { + // CHECK-LABEL: Generic signature: , Y : P2, T : P1, Y.Value == S1> + init(_: T) where X == S1 { } +} diff --git a/test/Generics/rdar79564324.swift b/test/Generics/rdar79564324.swift new file mode 100644 index 0000000000000..c37e7ea5f54c1 --- /dev/null +++ b/test/Generics/rdar79564324.swift @@ -0,0 +1,40 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module %S/Inputs/rdar79564324_other.swift -emit-module-path %t/rdar79564324_other.swiftmodule -requirement-machine=on -dump-requirement-machine 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -I %t -requirement-machine=on + +import rdar79564324_other + +public func test(_ t: T) where T == T.A { + foo(from: t, to: t) +} + +// foo(from:to:) has minimal signature . +// +// The GSB had trouble re-building after deserialization because of the two +// requirements 'T == T.A' and 'T.A == U.A'. +// +// What should happen is that these two imply that 'T == U.A', and 'U : P' +// implies the existence of 'U.A' and thus the conformance of T to P. +// +// Instead what happens is that 'T == T.A' and 'T.A == U.A' both get delayed +// because T does not yet have a nested type A, so it gets stuck. +// +// The rewrite system handles this correctly though: + +// CHECK-LABEL: Requirement machine for <τ_0_0, τ_0_1 where τ_0_0 == τ_0_0.A, τ_0_1 : P, τ_0_0.A == τ_0_1.A> +// CHECK-NEXT: Rewrite system: { +// CHECK-NEXT: - [P].A => [P:A] +// CHECK-NEXT: - [P:A].[P] => [P:A] +// CHECK-NEXT: - τ_0_0.[P:A] => τ_0_0 +// CHECK-NEXT: - τ_0_1.[P] => τ_0_1 +// CHECK-NEXT: - τ_0_1.[P:A] => τ_0_0 +// CHECK-NEXT: - [P:A].A => [P:A].[P:A] +// CHECK-NEXT: - τ_0_0.[P] => τ_0_0 +// CHECK-NEXT: - τ_0_1.A => τ_0_0 +// CHECK-NEXT: - τ_0_0.A => τ_0_0 +// CHECK-NEXT: } +// CHECK-NEXT: Property map: { +// CHECK-NEXT: [P:A] => { conforms_to: [P] } +// CHECK-NEXT: τ_0_1 => { conforms_to: [P] } +// CHECK-NEXT: τ_0_0 => { conforms_to: [P] } +// CHECK-NEXT: } \ No newline at end of file diff --git a/test/Generics/rdar79570734.swift b/test/Generics/rdar79570734.swift new file mode 100644 index 0000000000000..5ded09ef4cd97 --- /dev/null +++ b/test/Generics/rdar79570734.swift @@ -0,0 +1,20 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +public protocol P1 { + associatedtype A +} + +public protocol P2 {} + +public struct S1: P1 { + public typealias A = S2 +} + +public struct S2: P2 {} + +// CHECK-LABEL: Generic signature: +public struct G where Y == X.A {} + +// CHECK-LABEL: Generic signature: +public extension G where X == S1 {} diff --git a/test/Generics/rdar80503090.swift b/test/Generics/rdar80503090.swift new file mode 100644 index 0000000000000..ab3360771131c --- /dev/null +++ b/test/Generics/rdar80503090.swift @@ -0,0 +1,37 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +protocol P { + associatedtype T where T == Self +} + +protocol Q : P {} + +extension P { + func missing() {} +} + +extension P where T : Q { + // CHECK-LABEL: Generic signature: + func test() { + missing() + } +} + +class C : P {} + +extension P where T : C { + // CHECK-LABEL: Generic signature: + func test() { + missing() + } +} + +struct S : P {} + +extension P where T == S { + // CHECK-LABEL: Generic signature: + func test() { + missing() + } +} \ No newline at end of file diff --git a/test/Generics/recursive_conformances.swift b/test/Generics/recursive_conformances.swift new file mode 100644 index 0000000000000..391343baa48c4 --- /dev/null +++ b/test/Generics/recursive_conformances.swift @@ -0,0 +1,61 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify + +// Make sure the requirement machine can compute a confluent +// completion in examples where we merge two associated types +// with the same name, both having recursive conformance +// requirements. + +// Merged two cycles of length 1: +// +// P1 -> P1 -> P1 -> ... +// P2 -> P2 -> P2 -> ... +protocol P1 { + associatedtype T : P1 +} + +protocol P2 { + associatedtype T : P2 +} + +struct S {} + +// Merged a cycle of length 2 with a cycle of length 3 +// +// P1a -> P1b -> P1a -> P1b -> P1a -> P1b -> ... +// P2a -> P2b -> P2c -> P2a -> P2b -> P2c -> ... +protocol P1a { + associatedtype T : P1b +} + +protocol P1b { + associatedtype T : P1a +} + +protocol P2a { + associatedtype T : P2b +} + +protocol P2b { + associatedtype T : P2c +} + +protocol P2c { + associatedtype T : P2a +} + +struct SS {} + +// Merged two cycles of length 1 via an inherited associated type +protocol Base { + associatedtype T : Base // expected-note {{'T' declared here}} +} + +// Base -> Base -> Base -> ... +// Derived1 -> Derived1 -> Derived1 -> ... +protocol Derived1 : Base { + associatedtype T : Derived1 // expected-warning {{redeclaration of associated type 'T' from protocol 'Base' is better expressed as a 'where' clause on the protocol}} +} + +// Base -> Base -> Base -> ... +// Derived2 -> Derived2 -> Derived2 -> ... +protocol Derived2 : Base where T : Derived2 {} diff --git a/test/Generics/redundant_protocol_refinement.swift b/test/Generics/redundant_protocol_refinement.swift new file mode 100644 index 0000000000000..c4777630bfe2c --- /dev/null +++ b/test/Generics/redundant_protocol_refinement.swift @@ -0,0 +1,24 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +protocol Base {} +protocol Middle : Base {} +protocol Derived : Middle, Base {} +// expected-note@-1 {{conformance constraint 'Self' : 'Base' implied here}} +// expected-warning@-2 {{redundant conformance constraint 'Self' : 'Base'}} + +protocol P1 {} + +protocol P2 { + associatedtype Assoc: P1 +} + +// no warning here +protocol Good: P2, P1 where Assoc == Self {} +// CHECK-LABEL: Requirement signature: + +// missing refinement of 'P1' +protocol Bad: P2 where Assoc == Self {} +// expected-warning@-1 {{protocol 'Bad' should be declared to refine 'P1' due to a same-type constraint on 'Self'}} +// expected-note@-2 {{conformance constraint 'Self' : 'P1' implied here}} +// CHECK-LABEL: Requirement signature: \ No newline at end of file diff --git a/test/Generics/requirement_inference.swift b/test/Generics/requirement_inference.swift index e69ee8d5d64c2..53bc04a755ae0 100644 --- a/test/Generics/requirement_inference.swift +++ b/test/Generics/requirement_inference.swift @@ -363,6 +363,7 @@ struct X26 : P26 { // CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.A == X26<τ_0_0.B>, τ_0_0.B : X3> protocol P27a { associatedtype A: P26 // expected-warning{{redundant conformance constraint 'Self.A' : 'P26'}} + associatedtype B: X3 where A == X26 // expected-note{{conformance constraint 'Self.A' : 'P26' implied here}} } diff --git a/test/Generics/runaway_conformance_access_path.swift b/test/Generics/runaway_conformance_access_path.swift new file mode 100644 index 0000000000000..095fa3c9c5de4 --- /dev/null +++ b/test/Generics/runaway_conformance_access_path.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -emit-ir -verify %s + +// Reduced from swift-futures project in the source compatibility suite. + +public protocol FutureProtocol: FutureConvertible where FutureType == Self { + associatedtype Output +} + +public protocol FutureConvertible { + associatedtype FutureType: FutureProtocol +} + +func takesFuture(_: T.Type) {} + +public struct FutureHolder { + // CHECK-LABEL: Generic signature: + init(_: U) where U.FutureType == T { + takesFuture(T.self) + } +} diff --git a/test/Generics/sr13884.swift b/test/Generics/sr13884.swift index 2bb99307b29f0..720fdc6ab29f0 100644 --- a/test/Generics/sr13884.swift +++ b/test/Generics/sr13884.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -// RUN: %target-swift-frontend -emit-ir -debug-generic-signatures %s 2>&1 | %FileCheck %s +// RUN: not %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s public protocol P { associatedtype A : Q where A.B == Self @@ -27,8 +27,10 @@ public class D : Q { public func takesBoth1(_: T) {} // expected-warning@-1 {{redundant conformance constraint 'T' : 'P'}} // expected-note@-2 {{conformance constraint 'T' : 'P' implied here}} +// expected-error@-3 {{same-type requirement makes generic parameter 'T' non-generic}} // CHECK-LABEL: Generic signature: public func takesBoth2(_: U) {} // expected-warning@-1 {{redundant conformance constraint 'U' : 'P'}} // expected-note@-2 {{conformance constraint 'U' : 'P' implied here}} +// expected-error@-3 {{same-type requirement makes generic parameter 'U' non-generic}} diff --git a/test/Generics/sr14510.swift b/test/Generics/sr14510.swift new file mode 100644 index 0000000000000..c7e2452ef2b64 --- /dev/null +++ b/test/Generics/sr14510.swift @@ -0,0 +1,35 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -debug-generic-signatures -typecheck %s 2>&1 | %FileCheck %s + +// CHECK-LABEL: Requirement signature: +public protocol Adjoint { + associatedtype Dual: Adjoint where Self.Dual.Dual == Self +} + +// CHECK-LABEL: Requirement signature: +public protocol Diffable { + associatedtype Patch +} + +// CHECK-LABEL: Requirement signature: +public protocol AdjointDiffable: Adjoint & Diffable +where Self.Patch: Adjoint, Self.Dual: AdjointDiffable, + Self.Patch.Dual == Self.Dual.Patch { +} + +// This is another example used to hit the same problem. Any one of the three +// conformance requirements 'A : P', 'B : P' or 'C : P' can be dropped and +// proven from the other two, but dropping two or more conformance requirements +// leaves us with an invalid signature. +// +// Note that this minimization is still not quite correct; the requirement +// 'Self.B.C == Self.A.A.A' is unnecessary. + +// CHECK-LABEL: Requirement signature: +protocol P { + associatedtype A : P where A == B.C + associatedtype B : P where B == A.C + // expected-note@-1 {{conformance constraint 'Self.C' : 'P' implied here}} + associatedtype C : P where C == A.B + // expected-warning@-1 {{redundant conformance constraint 'Self.C' : 'P'}} +} \ No newline at end of file diff --git a/test/Generics/sr14580.swift b/test/Generics/sr14580.swift new file mode 100644 index 0000000000000..49831c3bfacd2 --- /dev/null +++ b/test/Generics/sr14580.swift @@ -0,0 +1,32 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s + +public protocol ScalarProtocol: ScalarMultiplicative where Self == Scalar { +} + +public protocol ScalarMultiplicative { + associatedtype Scalar: ScalarProtocol +} + +public protocol MapReduceArithmetic: ScalarMultiplicative, Collection where Element: ScalarMultiplicative {} + +public protocol Tensor: MapReduceArithmetic where Element == Scalar { +} + +// FIXME: The same-type requirement canonicalization still produces bugus results here, but +// at least we don't crash. + +// CHECK-LABEL: Requirement signature: +public protocol ColorModel: Tensor where Scalar == Double { + associatedtype Float16Components: ColorComponents where Float16Components.Model == Self, Float16Components.Scalar == Double + associatedtype Float32Components: ColorComponents where Float32Components.Model == Self, Float32Components.Scalar == Double +} + +public protocol ColorComponents: Tensor { + associatedtype Model: ColorModel +} + +extension Double : ScalarMultiplicative {} +extension Double : ScalarProtocol { + public typealias Scalar = Self +} diff --git a/test/Generics/superclass_constraint.swift b/test/Generics/superclass_constraint.swift index 4e8b8d131657f..2b1436b222a56 100644 --- a/test/Generics/superclass_constraint.swift +++ b/test/Generics/superclass_constraint.swift @@ -213,3 +213,16 @@ _ = G() // expected-error {{'G' requires that 'Base & P' inherit from func badClassConstrainedType(_: G) {} // expected-error@-1 {{'G' requires that 'Base & P' inherit from 'Base'}} + +// Reduced from CoreStore in source compat suite +public protocol Pony {} + +public class Teddy: Pony {} + +public struct Paddock {} + +public struct Barn { + // CHECK-DAG: Barn.foo@ + // CHECK: Generic signature: + public func foo(_: S, _: Barn, _: Paddock) {} +} diff --git a/test/Generics/unbound.swift b/test/Generics/unbound.swift index 8f57d930ee13c..88e30dc03954e 100644 --- a/test/Generics/unbound.swift +++ b/test/Generics/unbound.swift @@ -91,3 +91,16 @@ func makeInner() -> Outer.Middle.Inner { var innerProperty: Outer.Middle.Inner = makeInner() // expected-error@-1 {{reference to generic type 'Outer' requires arguments in <...>}} +// Some nested generic cases +struct OuterStruct { // expected-note 2{{generic type 'OuterStruct' declared here}} + struct InnerStruct {} // expected-note {{generic type 'InnerStruct' declared here}} +} + +func nested(_: OuterStruct.InnerStruct) {} +// expected-error@-1 {{reference to generic type 'OuterStruct' requires arguments in <...>}} + +func nested(_: OuterStruct.InnerStruct) {} +// expected-error@-1 {{reference to generic type 'OuterStruct' requires arguments in <...>}} + +func nested(_: OuterStruct.InnerStruct) {} +// expected-error@-1 {{reference to generic type 'OuterStruct.InnerStruct' requires arguments in <...>}} diff --git a/test/Generics/unify_associated_types.swift b/test/Generics/unify_associated_types.swift new file mode 100644 index 0000000000000..02bfd0ddef976 --- /dev/null +++ b/test/Generics/unify_associated_types.swift @@ -0,0 +1,38 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +struct Foo {} + +protocol P1 { + associatedtype X +} + +protocol P1a { + associatedtype T : P1 +} + +protocol P2 { + associatedtype X +} + +protocol P2a { + associatedtype T : P2 +} + +struct MergeTest {} + +// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1a, τ_0_0 : P2a> { +// CHECK-LABEL: Rewrite system: { +// CHECK: - τ_0_0.[P2a:T] => τ_0_0.[P1a&P2a:T] +// CHECK: - τ_0_0.[P1a:T] => τ_0_0.[P1a&P2a:T] +// CHECK: - [P1a&P2a:T].[P2:X] => [P1a&P2a:T].[P1&P2:X] +// CHECK: - τ_0_0.[P1a&P2a:T].[P1:X] => τ_0_0.[P1a&P2a:T].[P1&P2:X] +// CHECK: - [P1a&P2a:T].[P1:X] => [P1a&P2a:T].[P1&P2:X] +// CHECK: } +// CHECK: Property map: { +// CHECK: [P1a:T] => { conforms_to: [P1] } +// CHECK: [P2a:T] => { conforms_to: [P2] } +// CHECK: τ_0_0 => { conforms_to: [P1a P2a] } +// CHECK: [P1a&P2a:T] => { conforms_to: [P1 P2] } +// CHECK: } +// CHECK: } + diff --git a/test/Generics/unify_concrete_types_1.swift b/test/Generics/unify_concrete_types_1.swift new file mode 100644 index 0000000000000..b2d5f8ff1eda6 --- /dev/null +++ b/test/Generics/unify_concrete_types_1.swift @@ -0,0 +1,32 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +struct Foo {} + +protocol P1 { + associatedtype X where X == Foo + associatedtype Y1 + associatedtype Z1 +} + +protocol P2 { + associatedtype X where X == Foo + associatedtype Y2 + associatedtype Z2 +} + +struct MergeTest { + func foo1(x: G.Y1) -> G.Y2 { return x } + func foo2(x: G.Z1) -> G.Z2 { return x } +} + +// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> { +// CHECK-LABEL: Rewrite system: { +// CHECK: - τ_0_0.[P2:Y2] => τ_0_0.[P1:Y1] +// CHECK: - τ_0_0.[P2:Z2] => τ_0_0.[P1:Z1] +// CHECK: } +// CHECK-LABEL: Property map: { +// CHECK: [P1:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <[P1:Y1], [P1:Z1]>] } +// CHECK: [P2:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <[P2:Y2], [P2:Z2]>] } +// CHECK: τ_0_0 => { conforms_to: [P1 P2] } +// CHECK: τ_0_0.[P1&P2:X] => { concrete_type: [concrete: Foo<τ_0_0, τ_0_1> with <τ_0_0.[P2:Y2], τ_0_0.[P2:Z2]>] } +// CHECK: } diff --git a/test/Generics/unify_concrete_types_2.swift b/test/Generics/unify_concrete_types_2.swift new file mode 100644 index 0000000000000..147825112436d --- /dev/null +++ b/test/Generics/unify_concrete_types_2.swift @@ -0,0 +1,39 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +struct Foo {} + +protocol P1 { + associatedtype X where X == Foo + associatedtype Y1 + associatedtype Z1 +} + +protocol P1a { + associatedtype T : P1 +} + +protocol P2 { + associatedtype X + associatedtype Y2 + associatedtype Z2 +} + +protocol P2a { + associatedtype T : P2 where T.X == Foo +} + +struct MergeTest { + func foo1(x: G.T.Y1) -> G.T.Y2 { return x } + func foo2(x: G.T.Z1) -> G.T.Z2 { return x } +} + +// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1a, τ_0_0 : P2a> { +// CHECK-LABEL: Rewrite system: { +// CHECK: - τ_0_0.[P2a:T] => τ_0_0.[P1a&P2a:T] +// CHECK: - τ_0_0.[P1a:T] => τ_0_0.[P1a&P2a:T] +// CHECK: - [P1a&P2a:T].[P2:X] => [P1a&P2a:T].[P1&P2:X] +// CHECK: - [P1a&P2a:T].[P1:X] => [P1a&P2a:T].[P1&P2:X] +// CHECK: - τ_0_0.[P1a&P2a:T].[P2:Y2] => τ_0_0.[P1a&P2a:T].[P1:Y1] +// CHECK: - τ_0_0.[P1a&P2a:T].[P2:Z2] => τ_0_0.[P1a&P2a:T].[P1:Z1] +// CHECK: } +// CHECK: } diff --git a/test/Generics/unify_nested_types_1.swift b/test/Generics/unify_nested_types_1.swift new file mode 100644 index 0000000000000..cd0ed49e5c7b4 --- /dev/null +++ b/test/Generics/unify_nested_types_1.swift @@ -0,0 +1,26 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +protocol P1 { + associatedtype T : P1 +} + +protocol P2 { + associatedtype T where T == Int +} + +extension Int : P1 { + public typealias T = Int +} + +struct G {} + +// Since G.T.T == G.T.T.T == G.T.T.T.T = ... = Int, we tie off the +// recursion by introducing a same-type requirement G.T.T => G.T. + +// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> { +// CHECK-LABEL: Rewrite system: { +// CHECK: - τ_0_0.[P1&P2:T].[concrete: Int] => τ_0_0.[P1&P2:T] +// CHECK: - [P1&P2:T].T => [P1&P2:T].[P1:T] +// CHECK: - τ_0_0.[P1&P2:T].[P1:T] => τ_0_0.[P1&P2:T] +// CHECK: } +// CHECK: } diff --git a/test/Generics/unify_nested_types_2.swift b/test/Generics/unify_nested_types_2.swift new file mode 100644 index 0000000000000..82704e88f0509 --- /dev/null +++ b/test/Generics/unify_nested_types_2.swift @@ -0,0 +1,31 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +protocol P1 { + associatedtype T : P1 +} + +protocol P2 { + associatedtype T where T == X + associatedtype U +} + +extension Int : P1 { + public typealias T = Int +} + +struct X : P1 { + typealias T = X +} + +struct G {} + +// Since G.T.T == G.T.T.T == G.T.T.T.T = ... = X, we tie off the +// recursion by introducing a same-type requirement G.T.T => G.T. + +// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> { +// CHECK-LABEL: Rewrite system: { +// CHECK: - τ_0_0.[P1&P2:T].[concrete: X<τ_0_0> with <τ_0_0.[P2:U]>] => τ_0_0.[P1&P2:T] +// CHECK: - [P1&P2:T].T => [P1&P2:T].[P1:T] +// CHECK: - τ_0_0.[P1&P2:T].[P1:T] => τ_0_0.[P1&P2:T] +// CHECK: } +// CHECK: } diff --git a/test/Generics/unify_nested_types_3.swift b/test/Generics/unify_nested_types_3.swift new file mode 100644 index 0000000000000..0d6227327aea8 --- /dev/null +++ b/test/Generics/unify_nested_types_3.swift @@ -0,0 +1,38 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +protocol P1 { + associatedtype T : P1 +} + +protocol P2 { + associatedtype T +} + +struct X : P2 { + typealias T = X +} + +protocol P2a { + associatedtype T : P2 +} + +protocol P3a { + associatedtype T where T == X + associatedtype U : P1 +} + +struct G {} + +// X.T has a DependentMemberType in it; make sure that we build the +// correct substitution schema. + +// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P2a, τ_0_0 : P3a> +// CHECK-LABEL: Rewrite system: { +// CHECK: - τ_0_0.[P2a&P3a:T].[concrete: X<τ_0_0> with <τ_0_0.[P3a:U]>] => τ_0_0.[P2a&P3a:T] +// CHECK: - τ_0_0.[P2a&P3a:T].[P2:T].[concrete: X<τ_0_0> with <τ_0_0.[P3a:U].[P1:T]>] => τ_0_0.[P2a&P3a:T].[P2:T] +// CHECK: } +// CHECK-LABEL: Property map: { +// CHECK: τ_0_0.[P2a&P3a:T] => { conforms_to: [P2] concrete_type: [concrete: X<τ_0_0> with <τ_0_0.[P3a:U]>] } +// CHECK: τ_0_0.[P2a&P3a:T].[P2:T] => { concrete_type: [concrete: X<τ_0_0> with <τ_0_0.[P3a:U].[P1:T]>] } +// CHECK: } +// CHECK: } diff --git a/test/Generics/unify_nested_types_4.swift b/test/Generics/unify_nested_types_4.swift new file mode 100644 index 0000000000000..984a3b418c9cf --- /dev/null +++ b/test/Generics/unify_nested_types_4.swift @@ -0,0 +1,56 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +protocol P1 { + associatedtype A : P1 + associatedtype B : P1 +} + +struct S1 : P1 { + typealias A = S1 + typealias B = S2 +} + +struct S2 : P1 { + typealias A = S2 + typealias B = S1 +} + +protocol P2 { + associatedtype A where A == S1 + associatedtype B where B == S2 +} + +struct G {} + +// T.A and T.B become concrete, which produces the following series of +// concretized nested types: +// +// T.A.[concrete: S1] +// T.B.[concrete: S2] +// T.A.A.[concrete: S1] +// T.A.B.[concrete: S2] +// T.B.A.[concrete: S2] +// T.B.B.[concrete: S1] +// ... +// +// This would normally go on forever, but since S1 and S2 are not generic, +// we solve this by merging the repeated types with T.A or T.B: +// +// T.A.A => T.A +// T.A.B => T.B +// T.B.A => T.B +// T.B.B => T.A +// ... + +// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> +// CHECK-LABEL: Rewrite system: { +// CHECK: - τ_0_0.[P1&P2:A].[P1:A] => τ_0_0.[P1&P2:A] +// CHECK: - τ_0_0.[P1&P2:A].[P1:B] => τ_0_0.[P1&P2:B] +// CHECK: - τ_0_0.[P1&P2:B].[P1:A] => τ_0_0.[P1&P2:B] +// CHECK: - τ_0_0.[P1&P2:B].[P1:B] => τ_0_0.[P1&P2:A] +// CHECK: } +// CHECK-LABEL: Property map: { +// CHECK: τ_0_0.[P1&P2:A] => { conforms_to: [P1] concrete_type: [concrete: S1] } +// CHECK: τ_0_0.[P1&P2:B] => { conforms_to: [P1] concrete_type: [concrete: S2] } +// CHECK: } +// CHECK: } diff --git a/test/Generics/unify_nested_types_5.swift b/test/Generics/unify_nested_types_5.swift new file mode 100644 index 0000000000000..1325e3e3bc642 --- /dev/null +++ b/test/Generics/unify_nested_types_5.swift @@ -0,0 +1,26 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify + +protocol P { + associatedtype A : P +} + +class C : P { + typealias A = C +} + +protocol P1 { + associatedtype T : P +} + +protocol P2 { + associatedtype T where T : C + associatedtype U +} + +func eq(_: T, _: T) {} + +struct G { + func foo(_ x: T.T.A, _ y: C) { + eq(x, y) + } +} diff --git a/test/Generics/unify_superclass_types_1.swift b/test/Generics/unify_superclass_types_1.swift new file mode 100644 index 0000000000000..17c62ed08825e --- /dev/null +++ b/test/Generics/unify_superclass_types_1.swift @@ -0,0 +1,28 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=verify -dump-requirement-machine 2>&1 | %FileCheck %s + +class Base {} +class Derived : Base { + func derivedMethod() {} +} + +protocol P : Base {} + +func takesDerived(_: Derived) {} + +extension P where Self : Derived { + func passesDerived() { derivedMethod() } +} + +// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : Derived, τ_0_0 : P> +// CHECK-NEXT: Rewrite system: { +// CHECK-NEXT: - [P].[superclass: Base] => [P] +// CHECK-NEXT: - [P].[layout: _NativeClass] => [P] +// CHECK-NEXT: - τ_0_0.[superclass: Derived] => τ_0_0 +// CHECK-NEXT: - τ_0_0.[layout: _NativeClass] => τ_0_0 +// CHECK-NEXT: - τ_0_0.[P] => τ_0_0 +// CHECK-NEXT: - τ_0_0.[superclass: Base] => τ_0_0 +// CHECK-NEXT: } +// CHECK-NEXT: Property map: { +// CHECK-NEXT: [P] => { layout: _NativeClass superclass: [superclass: Base] } +// CHECK-NEXT: τ_0_0 => { conforms_to: [P] layout: _NativeClass superclass: [superclass: Derived] } +// CHECK-NEXT: } diff --git a/test/Generics/unify_superclass_types_2.swift b/test/Generics/unify_superclass_types_2.swift new file mode 100644 index 0000000000000..4d926eeb69e41 --- /dev/null +++ b/test/Generics/unify_superclass_types_2.swift @@ -0,0 +1,47 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=on -dump-requirement-machine 2>&1 | %FileCheck %s + +// Note: The GSB fails this test, because it doesn't implement unification of +// superclass type constructor arguments. + +class Generic {} + +protocol P1 { + associatedtype X : Generic + associatedtype A1 + associatedtype B1 +} + +protocol P2 { + associatedtype X : Generic + associatedtype A2 + associatedtype B2 +} + +func sameType(_: T.Type, _: T.Type) {} + +func takesGenericIntString(_: Generic.Type) {} + +func unifySuperclassTest(_: T) { + sameType(T.A1.self, String.self) + sameType(T.A2.self, Int.self) + sameType(T.B1.self, T.B2.self) + takesGenericIntString(T.X.self) +} + +// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> +// CHECK-NEXT: Rewrite system: { +// CHECK: - τ_0_0.[P1&P2:X].[superclass: Generic<τ_0_0, String, τ_0_1> with <τ_0_0.[P2:A2], τ_0_0.[P2:B2]>] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Generic with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2] +// CHECK-NEXT: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1] +// CHECK-NEXT: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1] +// CHECK: } +// CHECK-NEXT: Property map: { +// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Generic with <[P1:A1], [P1:B1]>] } +// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<τ_0_0, String, τ_0_1> with <[P2:A2], [P2:B2]>] } +// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] } +// CHECK-NEXT: τ_0_0.[P1&P2:X] => { layout: _NativeClass superclass: [superclass: Generic with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] } +// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] } +// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] } +// CHECK-NEXT: } \ No newline at end of file diff --git a/test/Generics/unify_superclass_types_3.swift b/test/Generics/unify_superclass_types_3.swift new file mode 100644 index 0000000000000..e3d943f7198e7 --- /dev/null +++ b/test/Generics/unify_superclass_types_3.swift @@ -0,0 +1,49 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=on -dump-requirement-machine 2>&1 | %FileCheck %s + +// Note: The GSB fails this test, because it doesn't implement unification of +// superclass type constructor arguments. + +class Generic {} + +class Derived : Generic {} + +protocol P1 { + associatedtype X : Derived + associatedtype A1 + associatedtype B1 +} + +protocol P2 { + associatedtype X : Generic + associatedtype A2 + associatedtype B2 +} + +func sameType(_: T.Type, _: T.Type) {} + +func takesDerivedString(_: Derived.Type) {} + +func unifySuperclassTest(_: T) { + sameType(T.A1.self, String.self) + sameType(T.A2.self, Int.self) + sameType(T.B1.self, T.B2.self) + takesDerivedString(T.X.self) +} + +// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> +// CHECK-NEXT: Rewrite system: { +// CHECK: - τ_0_0.[P1&P2:X].[superclass: Generic<τ_0_0, String, τ_0_1> with <τ_0_0.[P2:A2], τ_0_0.[P2:B2]>] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Derived<τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2] +// CHECK-NEXT: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1] +// CHECK-NEXT: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1] +// CHECK-NEXT: } +// CHECK-NEXT: Property map: { +// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0, τ_0_1> with <[P1:A1], [P1:B1]>] } +// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<τ_0_0, String, τ_0_1> with <[P2:A2], [P2:B2]>] } +// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] } +// CHECK-NEXT: τ_0_0.[P1&P2:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0, τ_0_1> with <τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] } +// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] } +// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] } +// CHECK-NEXT: } diff --git a/test/Generics/unify_superclass_types_4.swift b/test/Generics/unify_superclass_types_4.swift new file mode 100644 index 0000000000000..865da3b5fc2d3 --- /dev/null +++ b/test/Generics/unify_superclass_types_4.swift @@ -0,0 +1,49 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine=on -dump-requirement-machine 2>&1 | %FileCheck %s + +// Note: The GSB fails this test, because it doesn't implement unification of +// superclass type constructor arguments. + +class Base {} + +protocol Q { + associatedtype T +} + +class Derived : Base {} + +protocol P1 { + associatedtype X : Base + associatedtype A1 +} + +protocol P2 { + associatedtype X : Derived + associatedtype A2 : Q +} + +func sameType(_: T.Type, _: T.Type) {} + +func takesBase(_: Base.Type, _: U.Type) {} + +func takesDerived(_: Derived.Type, _: U.Type) {} + +func unifySuperclassTest(_: T) { + sameType(T.A1.self, T.A2.T.self) + takesBase(T.X.self, T.A1.self) + takesDerived(T.X.self, T.A2.self) +} + +// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> +// CHECK-NEXT: Rewrite system: { +// CHECK: - τ_0_0.[P1&P2:X].[superclass: Derived<τ_0_0> with <τ_0_0.[P2:A2]>] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P1&P2:X].[layout: _NativeClass] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P1&P2:X].[superclass: Base<τ_0_0> with <τ_0_0.[P1:A1]>] => τ_0_0.[P1&P2:X] +// CHECK-NEXT: - τ_0_0.[P2:A2].[Q:T] => τ_0_0.[P1:A1] +// CHECK-NEXT: } +// CHECK-NEXT: Property map: { +// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Base<τ_0_0> with <[P1:A1]>] } +// CHECK-NEXT: [P2:A2] => { conforms_to: [Q] } +// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0> with <[P2:A2]>] } +// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] } +// CHECK-NEXT: τ_0_0.[P1&P2:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0> with <τ_0_0.[P2:A2]>] } +// CHECK-NEXT: } \ No newline at end of file diff --git a/test/IDE/Inputs/complete_sourcefileinfo/MyModule1-modified.swift b/test/IDE/Inputs/complete_sourcefileinfo/MyModule1-modified.swift new file mode 100644 index 0000000000000..933b765200fa6 --- /dev/null +++ b/test/IDE/Inputs/complete_sourcefileinfo/MyModule1-modified.swift @@ -0,0 +1,11 @@ +/// Added comment. +public struct MyStruct { + public var propertyInType: Int { 1 } + + /// Added comment. + public func funcInType(x: Int) { + // Added function body. + print("DEBUG") + } +} + diff --git a/test/IDE/Inputs/complete_sourcefileinfo/MyModule1.swift b/test/IDE/Inputs/complete_sourcefileinfo/MyModule1.swift new file mode 100644 index 0000000000000..5aa9a8406bcb1 --- /dev/null +++ b/test/IDE/Inputs/complete_sourcefileinfo/MyModule1.swift @@ -0,0 +1,4 @@ +public struct MyStruct { + public var propertyInType: Int { 1 } + public func funcInType(x: Int) {} +} diff --git a/test/IDE/Inputs/complete_sourcefileinfo/MyModule2-modified.swift b/test/IDE/Inputs/complete_sourcefileinfo/MyModule2-modified.swift new file mode 100644 index 0000000000000..a5024aeb51132 --- /dev/null +++ b/test/IDE/Inputs/complete_sourcefileinfo/MyModule2-modified.swift @@ -0,0 +1,6 @@ +public extension MyStruct { + var propertyInExtension: String { "" } + func funcInExtension() -> String { "" } + + func addedMethod() -> MyStruct { return self } +} diff --git a/test/IDE/Inputs/complete_sourcefileinfo/MyModule2.swift b/test/IDE/Inputs/complete_sourcefileinfo/MyModule2.swift new file mode 100644 index 0000000000000..0a559be412f1d --- /dev/null +++ b/test/IDE/Inputs/complete_sourcefileinfo/MyModule2.swift @@ -0,0 +1,4 @@ +public extension MyStruct { + var propertyInExtension: String { "" } + func funcInExtension() -> String { "" } +} diff --git a/test/IDE/coloring_comments.swift b/test/IDE/coloring_comments.swift index 4e503face3f17..7efcf53d72d42 100644 --- a/test/IDE/coloring_comments.swift +++ b/test/IDE/coloring_comments.swift @@ -76,6 +76,11 @@ func test5() -> Int { // CHECK: // http://whatever.com/what-ever // http://whatever.com/what-ever +// service://example.com +// sip://example.com +// CHECK: // service://example.com +// CHECK: // sip://example.com + /// Brief. /// /// Simple case. diff --git a/test/IDE/complete_actorisolation.swift b/test/IDE/complete_actorisolation.swift index 4fa0c7d108b33..597e5a4cff22e 100644 --- a/test/IDE/complete_actorisolation.swift +++ b/test/IDE/complete_actorisolation.swift @@ -1,5 +1,5 @@ // REQUIRES: concurrency -// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -enable-experimental-concurrency +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -warn-concurrency class MyNonSendable {} struct MySendable {} diff --git a/test/IDE/complete_after_self.swift b/test/IDE/complete_after_self.swift index de77f70a22efa..40980c73fde29 100644 --- a/test/IDE/complete_after_self.swift +++ b/test/IDE/complete_after_self.swift @@ -139,8 +139,8 @@ class ThisDerived1 : ThisBase1 { self.#^CONVENIENCE_SELF_DOT_1?check=CONVENIENCE_SELF_DOT_1;check=COMMON_SELF_DOT_1^# // CONVENIENCE_SELF_DOT_1: Begin completions, 20 items // CONVENIENCE_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init()[#ThisDerived1#]; name=init() -// CONVENIENCE_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ThisDerived1#]; name=init(a: Int) -// CONVENIENCE_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#conv: Int#})[#ThisDerived1#]; name=init(conv: Int) +// CONVENIENCE_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ThisDerived1#]; name=init(a:) +// CONVENIENCE_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#conv: Int#})[#ThisDerived1#]; name=init(conv:) // CONVENIENCE_SELF_DOT_1: End completions } @@ -273,8 +273,8 @@ extension ThisDerived1 { self.#^EXTENSION_CONSTRUCTOR_SELF_DOT_1?check=COMMON_SELF_DOT_1;check=EXTENSION_CONSTRUCTOR_SELF_DOT_1^# // EXTENSION_CONSTRUCTOR_SELF_DOT_1: Begin completions, 20 items // EXTENSION_CONSTRUCTOR_SELF_DOT_1: Decl[Constructor]/CurrNominal: init()[#ThisDerived1#]; name=init() -// EXTENSION_CONSTRUCTOR_SELF_DOT_1: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ThisDerived1#]; name=init(a: Int) -// EXTENSION_CONSTRUCTOR_SELF_DOT_1: Decl[Constructor]/CurrNominal: init({#someExtensionArg: Int#})[#ThisDerived1#]; name=init(someExtensionArg: Int) +// EXTENSION_CONSTRUCTOR_SELF_DOT_1: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ThisDerived1#]; name=init(a:) +// EXTENSION_CONSTRUCTOR_SELF_DOT_1: Decl[Constructor]/CurrNominal: init({#someExtensionArg: Int#})[#ThisDerived1#]; name=init(someExtensionArg:) // EXTENSION_CONSTRUCTOR_SELF_DOT_1: End completions } @@ -287,7 +287,7 @@ struct S1 { // STRUCT_CONSTRUCTOR_SELF_DOT_1: Begin completions, 4 items // STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Keyword[self]/CurrNominal: self[#S1#]; name=self // STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init()[#S1#]; name=init() -// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#})[#S1#]; name=init(x: Int) +// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#})[#S1#]; name=init(x:) // STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: f()[#Void#]; name=f() // STRUCT_CONSTRUCTOR_SELF_DOT_1: End completions let s: S1 diff --git a/test/IDE/complete_after_super.swift b/test/IDE/complete_after_super.swift index aafe2a6991405..ea5b6c51cd43a 100644 --- a/test/IDE/complete_after_super.swift +++ b/test/IDE/complete_after_super.swift @@ -123,7 +123,7 @@ class SuperDerivedA : SuperBaseA { init() { super#^CONSTRUCTOR_SUPER_NO_DOT_1?check=COMMON_BASE_A_NO_DOT;check=CONSTRUCTOR_SUPER_NO_DOT_1^# // CONSTRUCTOR_SUPER_NO_DOT_1: Begin completions, 8 items -// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[Constructor]/ExprSpecific: .init()[#SuperBaseA#]{{; name=.+$}} +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[Constructor]/CurrNominal/Flair[SuperChain]: .init()[#SuperBaseA#]{{; name=.+$}} // CONSTRUCTOR_SUPER_NO_DOT_1: End completions } @@ -145,13 +145,13 @@ class SuperDerivedA : SuperBaseA { init (a: Float) { super.init#^CONSTRUCTOR_SUPER_INIT_1^# // CONSTRUCTOR_SUPER_INIT_1: Begin completions -// CONSTRUCTOR_SUPER_INIT_1-DAG: Decl[Constructor]/CurrNominal: ()[#SuperBaseA#]; name=() +// CONSTRUCTOR_SUPER_INIT_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#SuperBaseA#]; name=() // CONSTRUCTOR_SUPER_INIT_1: End completions } init (b: Float) { super.init(#^CONSTRUCTOR_SUPER_INIT_PAREN_1^# // CONSTRUCTOR_SUPER_INIT_PAREN_1: Begin completions, 1 items -// CONSTRUCTOR_SUPER_INIT_PAREN_1: Decl[Constructor]/CurrNominal: ['('][')'][#SuperBaseA#]; name= +// CONSTRUCTOR_SUPER_INIT_PAREN_1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#SuperBaseA#]; name= // CONSTRUCTOR_SUPER_INIT_PAREN_1: End completions } @@ -291,7 +291,7 @@ class SuperDerivedB : SuperBaseB { init() { super#^CONSTRUCTOR_SUPER_NO_DOT_2?check=COMMON_BASE_B_NO_DOT;check=CONSTRUCTOR_SUPER_NO_DOT_2^# // CONSTRUCTOR_SUPER_NO_DOT_2: Begin completions, 10 items -// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/ExprSpecific: .init()[#SuperBaseB#]{{; name=.+$}} +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal/Flair[SuperChain]: .init()[#SuperBaseB#]{{; name=.+$}} // CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal: .init({#a: Double#})[#SuperBaseB#]{{; name=.+$}} // CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal: .init({#int: Int#})[#SuperBaseB#]{{; name=.+$}} // CONSTRUCTOR_SUPER_NO_DOT_2: End completions @@ -302,7 +302,7 @@ class SuperDerivedB : SuperBaseB { // CONSTRUCTOR_SUPER_DOT_2: Begin completions, 9 items // CONSTRUCTOR_SUPER_DOT_2-DAG: Decl[Constructor]/CurrNominal: init()[#SuperBaseB#]{{; name=.+$}} // CONSTRUCTOR_SUPER_DOT_2-DAG: Decl[Constructor]/CurrNominal: init({#a: Double#})[#SuperBaseB#]{{; name=.+$}} -// CONSTRUCTOR_SUPER_DOT_2-DAG: Decl[Constructor]/ExprSpecific: init({#int: Int#})[#SuperBaseB#]{{; name=.+$}} +// CONSTRUCTOR_SUPER_DOT_2-DAG: Decl[Constructor]/CurrNominal/Flair[SuperChain]: init({#int: Int#})[#SuperBaseB#]{{; name=.+$}} // CONSTRUCTOR_SUPER_DOT_2: End completions } @@ -365,7 +365,7 @@ class SemanticContextDerived1 : SemanticContextBase1 { super.#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2?check=SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2;check=NO_SUPER_DECLS^# // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2: Begin completions // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[Constructor]/CurrNominal: init()[#SemanticContextBase1#]{{; name=.+$}} -// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[Constructor]/ExprSpecific: init({#a: Int#})[#SemanticContextBase1#]{{; name=.+$}} +// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[Constructor]/CurrNominal/Flair[SuperChain]: init({#a: Int#})[#SemanticContextBase1#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: End completions @@ -379,7 +379,7 @@ class SemanticContextDerived1 : SemanticContextBase1 { super.#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4?check=SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4;check=NO_SUPER_DECLS;check=NO_CONSTRUCTORS^# // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4: Begin completions -// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: Decl[InstanceMethod]/ExprSpecific: instanceFunc1()[#Void#]{{; name=.+$}} +// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[SuperChain]: instanceFunc1()[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: End completions } @@ -387,7 +387,7 @@ class SemanticContextDerived1 : SemanticContextBase1 { super.#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5?check=SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5;check=NO_SUPER_DECLS;check=NO_CONSTRUCTORS^# // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5: Begin completions // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}} -// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5-NEXT: Decl[InstanceMethod]/ExprSpecific: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} +// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[SuperChain]: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5-NEXT: End completions } } @@ -417,6 +417,37 @@ class Closures : SuperBaseA { } } +class SuperBaseC { + init(x: Int) {} + func foo() {} + func bar() {} +} +class SuperDerivedC1: SuperBaseC {} +class SuperDerivedC2: SuperDerivedC1 { + init(x: Int) { + super.#^OVERRIDE_2HOP_INIT1^# +// OVERRIDE_2HOP_INIT1: Begin completions, 3 items +// OVERRIDE_2HOP_INIT1-DAG: Decl[Constructor]/CurrNominal/Flair[SuperChain]: init({#x: Int#})[#SuperDerivedC1#]; +// OVERRIDE_2HOP_INIT1-DAG: Decl[InstanceMethod]/Super: foo()[#Void#]; +// OVERRIDE_2HOP_INIT1-DAG: Decl[InstanceMethod]/Super: bar()[#Void#]; +// OVERRIDE_2HOP_INIT1: End completions + } + init(y: Int) { + super.#^OVERRIDE_2HOP_INIT2^# +// OVERRIDE_2HOP_INIT2: Begin completions, 3 items +// OVERRIDE_2HOP_INIT2-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#})[#SuperDerivedC1#]; +// OVERRIDE_2HOP_INIT2-DAG: Decl[InstanceMethod]/Super: foo()[#Void#]; +// OVERRIDE_2HOP_INIT2-DAG: Decl[InstanceMethod]/Super: bar()[#Void#]; +// OVERRIDE_2HOP_INIT2: End completions + } + override func foo() { + super.#^OVERRIDE_2HOP_METHOD^# +// OVERRIDE_2HOP_METHOD: Begin completions, 2 items +// OVERRIDE_2HOP_METHOD-DAG: Decl[InstanceMethod]/Super/Flair[SuperChain]: foo()[#Void#]; +// OVERRIDE_2HOP_METHOD-DAG: Decl[InstanceMethod]/Super: bar()[#Void#]; +// OVERRIDE_2HOP_METHOD: End completions + } +} //===--- Code completion for 'super' keyword itself. diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index c96e75542b504..628e333dcf1c4 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -286,33 +286,33 @@ func testMissingArgs() { trailing(x: 2) { .#^MISSINGARG_TRAILING^# } // MISSINGARG_INLINE: Begin completions, 14 items - // MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#Foo#]; name=foo - // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#]; name=hash(self: Foo) - // MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#Bar#]; name=bar - // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bar#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bar) - // MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bop[#Bop#]; name=bop - // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bop#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bop) - // MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bix[#Bix#]; name=bix - // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bix#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bix) - // MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: boy[#Boy#]; name=boy - // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Boy#})[#(into: inout Hasher) -> Void#]; name=hash(self: Boy) - // MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: blu[#Blu#]; name=blu - // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Blu#})[#(into: inout Hasher) -> Void#]; name=hash(self: Blu) - // MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#Baz#]; name=baz - // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Baz#})[#(into: inout Hasher) -> Void#]; name=hash(self: Baz) + // MISSINGARG_INLINE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: foo[#Foo#]; name=foo + // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_INLINE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#Bar#]; name=bar + // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bar#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_INLINE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bop[#Bop#]; name=bop + // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bop#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_INLINE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bix[#Bix#]; name=bix + // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bix#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_INLINE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: boy[#Boy#]; name=boy + // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Boy#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_INLINE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: blu[#Blu#]; name=blu + // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Blu#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_INLINE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: baz[#Baz#]; name=baz + // MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Baz#})[#(into: inout Hasher) -> Void#]; name=hash(:) // MISSINGARG_INLINE: End completions // MISSINGARG_TRAILING: Begin completions, 10 items - // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#Foo#]; name=foo - // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#]; name=hash(self: Foo) - // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#Bar#]; name=bar - // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bar#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bar) - // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bop[#Bop#]; name=bop - // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bop#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bop) - // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bix[#Bix#]; name=bix - // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bix#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bix) - // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: boy[#Boy#]; name=boy - // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Boy#})[#(into: inout Hasher) -> Void#]; name=hash(self: Boy) + // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: foo[#Foo#]; name=foo + // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#Bar#]; name=bar + // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bar#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bop[#Bop#]; name=bop + // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bop#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bix[#Bix#]; name=bix + // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bix#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // MISSINGARG_TRAILING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: boy[#Boy#]; name=boy + // MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Boy#})[#(into: inout Hasher) -> Void#]; name=hash(:) // MISSINGARG_TRAILING: End completions } @@ -453,7 +453,7 @@ func testBestSolutionFilter() { } // BEST_SOLUTION_FILTER: Begin completions -// BEST_SOLUTION_FILTER-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: enumElem[#Enum123#]{{; name=.+$}} +// BEST_SOLUTION_FILTER-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: enumElem[#Enum123#]{{; name=.+$}} // BEST_SOLUTION_FILTER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Enum123#})[#(into: inout Hasher) -> Void#]{{; name=.+$}} // BEST_SOLUTION_FILTER-DAG: Keyword[nil]/None/Erase[1]/TypeRelation[Identical]: nil[#Enum123?#]{{; name=.+$}} // BEST_SOLUTION_FILTER-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional#]{{; name=.+$}} diff --git a/test/IDE/complete_annotation.swift b/test/IDE/complete_annotation.swift index c1e1f8c78f302..57b890c6a19b8 100644 --- a/test/IDE/complete_annotation.swift +++ b/test/IDE/complete_annotation.swift @@ -31,8 +31,8 @@ func testGlobal() { } // GLOBAL_EXPR: Begin completions // GLOBAL_EXPR-DAG: Decl[Struct]/CurrModule: MyStruct; typename=MyStruct; -// GLOBAL_EXPR-DAG: Keyword[class]/None: class; typename=; -// GLOBAL_EXPR-DAG: Keyword[enum]/None: enum; typename=; +// GLOBAL_EXPR-DAG: Keyword[class]/None/Flair[RareKeyword]: class; typename=; +// GLOBAL_EXPR-DAG: Keyword[enum]/None/Flair[RareKeyword]: enum; typename=; // GLOBAL_EXPR-DAG: Keyword[if]/None: if; typename=; // GLOBAL_EXPR-DAG: Keyword[guard]/None: guard; typename=; // GLOBAL_EXPR-DAG: Keyword[try]/None: try; typename=; @@ -99,12 +99,12 @@ func testImplicitMember() -> MyStruct { } // EXPR_IMPLICITMEMBER: Begin completions, 7 items // EXPR_IMPLICITMEMBER-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init(x: Int); typename=MyStruct; -// EXPR_IMPLICITMEMBER-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: instance; typename=MyStruct; +// EXPR_IMPLICITMEMBER-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: instance; typename=MyStruct; // EXPR_IMPLICITMEMBER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: labelNameParamName(_ self: MyStruct); typename=(label: (inout Int) throws -> MyStruct) -> Void; // EXPR_IMPLICITMEMBER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: labelName(_ self: MyStruct); typename=(label: (@autoclosure () -> Int) -> Int) -> Void; // EXPR_IMPLICITMEMBER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: sameName(_ self: MyStruct); typename=(label: inout Int) -> Void; // EXPR_IMPLICITMEMBER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: paramName(_ self: MyStruct); typename=(Int) -> Void; -// EXPR_IMPLICITMEMBER-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: create(x: Int); typename=MyStruct; +// EXPR_IMPLICITMEMBER-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: create(x: Int); typename=MyStruct; // EXPR_IMPLICITMEMBER: End completions func testArgument() -> MyStruct { @@ -112,7 +112,7 @@ func testArgument() -> MyStruct { foo(x: 1, #^CALLARG^# } // CALLARG: Begin completions, 1 items -// CALLARG-DAG: Pattern/ExprSpecific: y: Int; typename=Int +// CALLARG-DAG: Pattern/Local/Flair[ArgLabels]: y: Int; typename=Int // CALLARG: End completions struct TestArchetypeAnnotations { @@ -125,8 +125,8 @@ func testArchetypeAnnotations(arg: TestArchetypeAnnotations) { } // GENERIC: Begin completions, 3 items // GENERIC-DAG: Keyword[self]/CurrNominal: self; typename=TestArchetypeAnnotations<T>; name=self -// GENERIC-DAG: Decl[InstanceMethod]/CurrNominal: foo1(u: U, t: T); typename=Void; name=foo1(u: U, t: T) -// GENERIC-DAG: Decl[InstanceMethod]/CurrNominal: foo2(s: Sequence, elt: Sequence.Element); typename=Void; name=foo2(s: Sequence, elt: Sequence.Element) +// GENERIC-DAG: Decl[InstanceMethod]/CurrNominal: foo1(u: U, t: T); typename=Void; name=foo1(u:t:) +// GENERIC-DAG: Decl[InstanceMethod]/CurrNominal: foo2(s: Sequence, elt: Sequence.Element); typename=Void; name=foo2(s:elt:) // GENERIC: End completions struct TestGenericParamAnnotations { diff --git a/test/IDE/complete_assignment.swift b/test/IDE/complete_assignment.swift index 2e3b33bab2680..b36b537acab86 100644 --- a/test/IDE/complete_assignment.swift +++ b/test/IDE/complete_assignment.swift @@ -128,18 +128,18 @@ func f2() { } // ASSIGN_5: Begin completions, 3 items -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case2[#D1#]; name=case2 -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case1[#D1#]; name=case1 -// ASSIGN_5-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): D1#})[#(into: inout Hasher) -> Void#]; name=hash(self: D1) +// ASSIGN_5-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: case2[#D1#]; name=case2 +// ASSIGN_5-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: case1[#D1#]; name=case1 +// ASSIGN_5-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): D1#})[#(into: inout Hasher) -> Void#]; name=hash(:) func f6() { var d : D2 d = .#^ASSIGN_6^# } // ASSIGN_6: Begin completions, 3 items -// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case3[#D2#]; name=case3 -// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case4[#D2#]; name=case4 -// ASSIGN_6-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): D2#})[#(into: inout Hasher) -> Void#]; name=hash(self: D2) +// ASSIGN_6-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: case3[#D2#]; name=case3 +// ASSIGN_6-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: case4[#D2#]; name=case4 +// ASSIGN_6-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): D2#})[#(into: inout Hasher) -> Void#]; name=hash(:) func f7 (C : C2) { var i : Int diff --git a/test/IDE/complete_async_context.swift b/test/IDE/complete_async_context.swift index b22690ac2d5d2..7b6f458106994 100644 --- a/test/IDE/complete_async_context.swift +++ b/test/IDE/complete_async_context.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -enable-experimental-concurrency +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t // REQUIRES: concurrency diff --git a/test/IDE/complete_asyncannotation.swift b/test/IDE/complete_asyncannotation.swift index 12f943ab010a0..04ef1c1c12f55 100644 --- a/test/IDE/complete_asyncannotation.swift +++ b/test/IDE/complete_asyncannotation.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -enable-experimental-concurrency +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t // REQUIRES: concurrency @@ -19,55 +19,55 @@ struct HasAsyncMembers { func testGlobalFuncAsync() async { globalFuncAsync#^CHECK_globalFuncAsync^# // CHECK_globalFuncAsync: Begin completions -// CHECK_globalFuncAsync-DAG: Decl[FreeFunction]/CurrModule: ()[' async'][#Void#]; name=() async +// CHECK_globalFuncAsync-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ()[' async'][#Void#]; name=(){{$}} // CHECK_globalFuncAsync: End completions } func testGlobalFuncAsyncThrows() async { globalFuncAsyncThrows#^CHECK_globalFuncAsyncThrows^# // CHECK_globalFuncAsyncThrows: Begin completions -// CHECK_globalFuncAsyncThrows-DAG: Decl[FreeFunction]/CurrModule: ()[' async'][' throws'][#Void#]; name=() async throws +// CHECK_globalFuncAsyncThrows-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ()[' async'][' throws'][#Void#]; name=(){{$}} // CHECK_globalFuncAsyncThrows: End completions } func testGlobalFuncAsyncRethrows() async { globalFuncAsyncRethrows#^CHECK_globalFuncAsyncRethrows^# // CHECK_globalFuncAsyncRethrows: Begin completions -// CHECK_globalFuncAsyncRethrows-DAG: Decl[FreeFunction]/CurrModule: ({#(x): () async throws -> ()##() async throws -> ()#})[' async'][' rethrows'][#Void#]; name=(x: () async throws -> ()) async rethrows +// CHECK_globalFuncAsyncRethrows-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ({#(x): () async throws -> ()##() async throws -> ()#})[' async'][' rethrows'][#Void#]; name=(:){{$}} // CHECK_globalFuncAsyncRethrows: End completions } func testAsyncMembers(_ x: HasAsyncMembers) async { x.#^CHECK_members^# // CHECK_members: Begin completions -// CHECK_members-DAG: Decl[InstanceMethod]/CurrNominal: memberAsync()[' async'][#Void#]; name=memberAsync() async -// CHECK_members-DAG: Decl[InstanceMethod]/CurrNominal: memberAsyncThrows()[' async'][' throws'][#Void#]; name=memberAsyncThrows() async throws -// CHECK_members-DAG: Decl[InstanceMethod]/CurrNominal: memberAsyncRethrows {|}[' async'][' rethrows'][#Void#]; name=memberAsyncRethrows { code } async rethrows -// CHECK_members-DAG: Decl[InstanceMethod]/CurrNominal: memberAsyncRethrows({#(x): () async throws -> ()##() async throws -> ()#})[' async'][' rethrows'][#Void#]; name=memberAsyncRethrows(x: () async throws -> ()) async rethrows +// CHECK_members-DAG: Decl[InstanceMethod]/CurrNominal: memberAsync()[' async'][#Void#]; name=memberAsync(){{$}} +// CHECK_members-DAG: Decl[InstanceMethod]/CurrNominal: memberAsyncThrows()[' async'][' throws'][#Void#]; name=memberAsyncThrows(){{$}} +// CHECK_members-DAG: Decl[InstanceMethod]/CurrNominal: memberAsyncRethrows {|}[' async'][' rethrows'][#Void#]; name=memberAsyncRethrows{{$}} +// CHECK_members-DAG: Decl[InstanceMethod]/CurrNominal: memberAsyncRethrows({#(x): () async throws -> ()##() async throws -> ()#})[' async'][' rethrows'][#Void#]; name=memberAsyncRethrows(:){{$}} // CHECK_members: End completions } func testMemberAsync(_ x: HasAsyncMembers) async { x.memberAsync#^CHECK_memberAsync^# // CHECK_memberAsync: Begin completions -// CHECK_memberAsync-DAG: Decl[InstanceMethod]/CurrNominal: ()[' async'][#Void#]; name=() async +// CHECK_memberAsync-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ()[' async'][#Void#]; name=(){{$}} // CHECK_memberAsync: End completions } func testMemberAsyncThrows(_ x: HasAsyncMembers) async { x.memberAsyncThrows#^CHECK_memberAsyncThrows^# // CHECK_memberAsyncThrows: Begin completions -// CHECK_memberAsyncThrows-DAG: Decl[InstanceMethod]/CurrNominal: ()[' async'][' throws'][#Void#]; name=() async throws +// CHECK_memberAsyncThrows-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ()[' async'][' throws'][#Void#]; name=(){{$}} // CHECK_memberAsyncThrows: End completions } func testMemberAsyncRethrows(_ x: HasAsyncMembers) async { x.memberAsyncRethrows#^CHECK_memberAsyncRethrows^# // CHECK_memberAsyncRethrows: Begin completions -// CHECK_memberAsyncRethrows-DAG: Decl[InstanceMethod]/CurrNominal: ({#(x): () async throws -> ()##() async throws -> ()#})[' async'][' rethrows'][#Void#]; name=(x: () async throws -> ()) async rethrows +// CHECK_memberAsyncRethrows-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(x): () async throws -> ()##() async throws -> ()#})[' async'][' rethrows'][#Void#]; name=(:){{$}} // CHECK_memberAsyncRethrows: End completions } func testAsyncIntiializers() async { HasAsyncMembers(#^CHECK_initializers^# // CHECK_initializers: Begin completions -// CHECK_initializers-DAG: Decl[Constructor]/CurrNominal: ['('][')'][' async'][#HasAsyncMembers#]; name= async -// CHECK_initializers-DAG: Decl[Constructor]/CurrNominal: ['(']{#withAsync: Int#}[')'][' async'][#HasAsyncMembers#]; name=withAsync: Int async -// CHECK_initializers-DAG: Decl[Constructor]/CurrNominal: ['(']{#withAsyncThrows: Int#}[')'][' async'][' throws'][#HasAsyncMembers#]; name=withAsyncThrows: Int async throws -// CHECK_initializers-DAG: Decl[Constructor]/CurrNominal: ['(']{#withAsyncRethrows: () async throws -> Void##() async throws -> Void#}[')'][' async'][' rethrows'][#HasAsyncMembers#]; name=withAsyncRethrows: () async throws -> Void async rethrows +// CHECK_initializers-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][' async'][#HasAsyncMembers#]; name={{$}} +// CHECK_initializers-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#withAsync: Int#}[')'][' async'][#HasAsyncMembers#]; name=withAsync:{{$}} +// CHECK_initializers-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#withAsyncThrows: Int#}[')'][' async'][' throws'][#HasAsyncMembers#]; name=withAsyncThrows:{{$}} +// CHECK_initializers-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#withAsyncRethrows: () async throws -> Void##() async throws -> Void#}[')'][' async'][' rethrows'][#HasAsyncMembers#]; name=withAsyncRethrows:{{$}} // CHECK_initializers: End completions } diff --git a/test/IDE/complete_at_eof_in_call_1.swift b/test/IDE/complete_at_eof_in_call_1.swift index f5522362cd298..65f4a2594cee3 100644 --- a/test/IDE/complete_at_eof_in_call_1.swift +++ b/test/IDE/complete_at_eof_in_call_1.swift @@ -4,7 +4,7 @@ // Don't add any tests at the end of the file! // // A: Begin completions -// A-DAG: Decl[FreeFunction]/CurrModule: ['(']{#(x): Int#}[')'][#Void#]{{; name=.+$}} +// A-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#(x): Int#}[')'][#Void#]{{; name=.+$}} // A: End completions func f(_ x: Int) {} f(#^A^# diff --git a/test/IDE/complete_at_eof_in_call_no_newline_1.swift b/test/IDE/complete_at_eof_in_call_no_newline_1.swift index 2fbed5bd3cecc..65f4a2594cee3 100644 --- a/test/IDE/complete_at_eof_in_call_no_newline_1.swift +++ b/test/IDE/complete_at_eof_in_call_no_newline_1.swift @@ -4,7 +4,7 @@ // Don't add any tests at the end of the file! // // A: Begin completions -// A-DAG: Decl[FreeFunction]/CurrModule: ['(']{#(x): Int#}[')'][#Void#]{{; name=.+$}} +// A-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#(x): Int#}[')'][#Void#]{{; name=.+$}} // A: End completions func f(_ x: Int) {} -f(#^A^# \ No newline at end of file +f(#^A^# diff --git a/test/IDE/complete_at_top_level.swift b/test/IDE/complete_at_top_level.swift index f2885ed8ae5f4..02a29518be9ef 100644 --- a/test/IDE/complete_at_top_level.swift +++ b/test/IDE/complete_at_top_level.swift @@ -42,8 +42,8 @@ fooObject#^TYPE_CHECKED_EXPR_1^# // TYPE_CHECKED_EXPR_1: Begin completions // TYPE_CHECKED_EXPR_1-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_1-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} -// TYPE_CHECKED_EXPR_1-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]; name== FooStruct -// TYPE_CHECKED_EXPR_1-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]; name=self +// TYPE_CHECKED_EXPR_1-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]{{; name=.+$}} +// TYPE_CHECKED_EXPR_1-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]{{; name=.+$}} // TYPE_CHECKED_EXPR_1-NEXT: End completions func resyncParser2() {} @@ -55,8 +55,8 @@ fooObject#^TYPE_CHECKED_EXPR_2^# // TYPE_CHECKED_EXPR_2: Begin completions // TYPE_CHECKED_EXPR_2-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_2-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} -// TYPE_CHECKED_EXPR_2-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]; name== FooStruct -// TYPE_CHECKED_EXPR_2-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]; name=self +// TYPE_CHECKED_EXPR_2-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]{{; name=.+$}} +// TYPE_CHECKED_EXPR_2-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]{{; name=.+$}} // TYPE_CHECKED_EXPR_2-NEXT: End completions func resyncParser3() {} @@ -65,8 +65,8 @@ fooObject#^TYPE_CHECKED_EXPR_3^#.bar // TYPE_CHECKED_EXPR_3: Begin completions // TYPE_CHECKED_EXPR_3-NEXT: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_3-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} -// TYPE_CHECKED_EXPR_3-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]; name== FooStruct -// TYPE_CHECKED_EXPR_3-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]; name=self +// TYPE_CHECKED_EXPR_3-NEXT: BuiltinOperator/None: = {#FooStruct#}[#Void#]{{; name=.+$}} +// TYPE_CHECKED_EXPR_3-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct#]{{; name=.+$}} // TYPE_CHECKED_EXPR_3-NEXT: End completions func resyncParser4() {} @@ -125,9 +125,9 @@ func resyncParser7() {} var topLevelVar2 = FooStruct#^TOP_LEVEL_VAR_INIT_2^# // TOP_LEVEL_VAR_INIT_2: Begin completions // TOP_LEVEL_VAR_INIT_2-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(self): FooStruct#})[#(Int) -> Void#]{{; name=.+$}} -// TOP_LEVEL_VAR_INIT_2-NEXT: Decl[Constructor]/CurrNominal: ()[#FooStruct#]{{; name=.+$}} -// TOP_LEVEL_VAR_INIT_2-NEXT: Decl[Constructor]/CurrNominal: ({#instanceVar: Int#})[#FooStruct#]{{; name=.+$}} -// TOP_LEVEL_VAR_INIT_2-NEXT: Decl[Constructor]/CurrNominal: ()[#FooStruct#]{{; name=.+$}} +// TOP_LEVEL_VAR_INIT_2-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooStruct#]{{; name=.+$}} +// TOP_LEVEL_VAR_INIT_2-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#instanceVar: Int#})[#FooStruct#]{{; name=.+$}} +// TOP_LEVEL_VAR_INIT_2-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooStruct#]{{; name=.+$}} // TOP_LEVEL_VAR_INIT_2-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct.Type#]; name=self // TOP_LEVEL_VAR_INIT_2-NEXT: Keyword/CurrNominal: .Type[#FooStruct.Type#]; name=Type // TOP_LEVEL_VAR_INIT_2-NEXT: End completions @@ -327,7 +327,7 @@ func resyncParserB14() {} var stringInterp = "\(#^STRING_INTERP_3?check=STRING_INTERP^#)" _ = "" + "\(#^STRING_INTERP_4?check=STRING_INTERP^#)" + "" // STRING_INTERP: Begin completions -// STRING_INTERP-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: ['(']{#(value): T#}[')'][#Void#]; +// STRING_INTERP-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]/IsSystem: ['(']{#(value): T#}[')'][#Void#]; // STRING_INTERP-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: FooStruct[#FooStruct#]; name=FooStruct // STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Invalid]: fooFunc1()[#Void#]; // STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule: optStr()[#String?#]; diff --git a/test/IDE/complete_at_top_level_library.swift b/test/IDE/complete_at_top_level_library.swift new file mode 100644 index 0000000000000..234a387116322 --- /dev/null +++ b/test/IDE/complete_at_top_level_library.swift @@ -0,0 +1,166 @@ +// RUN: %empty-directory(%t.ccp) + +// Repeat twice to ensure completion caches works correctly. i.e. no sticky flairs +// RUN: %target-swift-ide-test -code-completion -source-filename %s -completion-cache-path=%t.ccp -code-completion-token=TOPLEVEL -parse-as-library | %FileCheck %s -check-prefix=LIBRARY +// RUN: %target-swift-ide-test -code-completion -source-filename %s -completion-cache-path=%t.ccp -code-completion-token=TOPLEVEL | %FileCheck %s -check-prefix=SCRIPT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -completion-cache-path=%t.ccp -code-completion-token=TOPLEVEL -parse-as-library | %FileCheck %s -check-prefix=LIBRARY +// RUN: %target-swift-ide-test -code-completion -source-filename %s -completion-cache-path=%t.ccp -code-completion-token=TOPLEVEL | %FileCheck %s -check-prefix=SCRIPT + +struct MyStruct {} +protocol MyProtocol {} + +#^TOPLEVEL^# + +// LIBRARY: Begin completions +// LIBRARY-DAG: Keyword[associatedtype]/None: associatedtype; name=associatedtype +// LIBRARY-DAG: Keyword[class]/None/Flair[CommonKeyword]: class; name=class +// LIBRARY-DAG: Keyword[deinit]/None: deinit; name=deinit +// LIBRARY-DAG: Keyword[enum]/None/Flair[CommonKeyword]: enum; name=enum +// LIBRARY-DAG: Keyword[extension]/None/Flair[CommonKeyword]: extension; name=extension +// LIBRARY-DAG: Keyword[func]/None: func; name=func +// LIBRARY-DAG: Keyword[import]/None: import; name=import +// LIBRARY-DAG: Keyword[init]/None: init; name=init +// LIBRARY-DAG: Keyword[inout]/None: inout; name=inout +// LIBRARY-DAG: Keyword[operator]/None: operator; name=operator +// LIBRARY-DAG: Keyword[precedencegroup]/None: precedencegroup; name=precedencegroup +// LIBRARY-DAG: Keyword[protocol]/None/Flair[CommonKeyword]: protocol; name=protocol +// LIBRARY-DAG: Keyword[struct]/None/Flair[CommonKeyword]: struct; name=struct +// LIBRARY-DAG: Keyword[subscript]/None: subscript; name=subscript +// LIBRARY-DAG: Keyword[typealias]/None: typealias; name=typealias +// LIBRARY-DAG: Keyword[fileprivate]/None: fileprivate; name=fileprivate +// LIBRARY-DAG: Keyword[internal]/None: internal; name=internal +// LIBRARY-DAG: Keyword[private]/None: private; name=private +// LIBRARY-DAG: Keyword[public]/None: public; name=public +// LIBRARY-DAG: Keyword[static]/None: static; name=static +// LIBRARY-DAG: Keyword/None: final; name=final +// LIBRARY-DAG: Keyword/None: required; name=required +// LIBRARY-DAG: Keyword/None: optional; name=optional +// LIBRARY-DAG: Keyword/None: lazy; name=lazy +// LIBRARY-DAG: Keyword/None: dynamic; name=dynamic +// LIBRARY-DAG: Keyword/None: infix; name=infix +// LIBRARY-DAG: Keyword/None: prefix; name=prefix +// LIBRARY-DAG: Keyword/None: postfix; name=postfix +// LIBRARY-DAG: Keyword/None: mutating; name=mutating +// LIBRARY-DAG: Keyword/None: nonmutating; name=nonmutating +// LIBRARY-DAG: Keyword/None: convenience; name=convenience +// LIBRARY-DAG: Keyword/None: override; name=override +// LIBRARY-DAG: Keyword/None: open; name=open +// LIBRARY-DAG: Keyword/None: weak; name=weak +// LIBRARY-DAG: Keyword/None: unowned; name=unowned +// LIBRARY-DAG: Keyword/None: indirect; name=indirect +// LIBRARY-DAG: Keyword/None: nonisolated; name=nonisolated +// LIBRARY-DAG: Keyword[defer]/None/Flair[ExprAtFileScope]: defer; name=defer +// LIBRARY-DAG: Keyword[if]/None/Flair[ExprAtFileScope]: if; name=if +// LIBRARY-DAG: Keyword[guard]/None/Flair[ExprAtFileScope]: guard; name=guard +// LIBRARY-DAG: Keyword[do]/None/Flair[ExprAtFileScope]: do; name=do +// LIBRARY-DAG: Keyword[repeat]/None/Flair[ExprAtFileScope]: repeat; name=repeat +// LIBRARY-DAG: Keyword[else]/None/Flair[ExprAtFileScope]: else; name=else +// LIBRARY-DAG: Keyword[for]/None/Flair[ExprAtFileScope]: for; name=for +// LIBRARY-DAG: Keyword[in]/None/Flair[ExprAtFileScope]: in; name=in +// LIBRARY-DAG: Keyword[while]/None/Flair[ExprAtFileScope]: while; name=while +// LIBRARY-DAG: Keyword[break]/None/Flair[ExprAtFileScope]: break; name=break +// LIBRARY-DAG: Keyword[continue]/None/Flair[ExprAtFileScope]: continue; name=continue +// LIBRARY-DAG: Keyword[fallthrough]/None/Flair[ExprAtFileScope]: fallthrough; name=fallthrough +// LIBRARY-DAG: Keyword[switch]/None/Flair[ExprAtFileScope]: switch; name=switch +// LIBRARY-DAG: Keyword[let]/None: let; name=let +// LIBRARY-DAG: Keyword[var]/None: var; name=var +// LIBRARY-DAG: Keyword[try]/None/Flair[ExprAtFileScope]: try; name=try +// LIBRARY-DAG: Keyword[try]/None/Flair[ExprAtFileScope]: try!; name=try! +// LIBRARY-DAG: Keyword[try]/None/Flair[ExprAtFileScope]: try?; name=try? +// LIBRARY-DAG: Keyword/None/Flair[ExprAtFileScope]: await; name=await +// LIBRARY-DAG: Literal[Integer]/None/Flair[ExprAtFileScope]: 0[#Int#]; name=0 +// LIBRARY-DAG: Literal[Boolean]/None/Flair[ExprAtFileScope]: true[#Bool#]; name=true +// LIBRARY-DAG: Literal[Boolean]/None/Flair[ExprAtFileScope]: false[#Bool#]; name=false +// LIBRARY-DAG: Literal[Nil]/None/Flair[ExprAtFileScope]: nil; name=nil +// LIBRARY-DAG: Literal[String]/None/Flair[ExprAtFileScope]: "{#(abc)#}"[#String#]; name="" +// LIBRARY-DAG: Literal[Array]/None/Flair[ExprAtFileScope]: [{#(values)#}][#Array#]; name=[] +// LIBRARY-DAG: Literal[Dictionary]/None/Flair[ExprAtFileScope]: [{#(key)#}: {#(value)#}][#Dictionary#]; name=[: ] +// LIBRARY-DAG: Literal[Tuple]/None/Flair[ExprAtFileScope]: ({#(values)#}); name=() +// LIBRARY-DAG: Keyword[#fileID]/None/Flair[ExprAtFileScope]: #fileID[#String#]; name=#fileID +// LIBRARY-DAG: Keyword[#file]/None/Flair[ExprAtFileScope]: #file[#String#]; name=#file +// LIBRARY-DAG: Keyword[#filePath]/None/Flair[ExprAtFileScope]: #filePath[#String#]; name=#filePath +// LIBRARY-DAG: Keyword[#function]/None/Flair[ExprAtFileScope]: #function[#String#]; name=#function +// LIBRARY-DAG: Keyword[#line]/None/Flair[ExprAtFileScope]: #line[#Int#]; name=#line +// LIBRARY-DAG: Keyword[#column]/None/Flair[ExprAtFileScope]: #column[#Int#]; name=#column +// LIBRARY-DAG: Keyword[#dsohandle]/None/Flair[ExprAtFileScope]: #dsohandle[#UnsafeRawPointer#]; name=#dsohandle +// LIBRARY-DAG: Decl[Struct]/CurrModule/Flair[ExprAtFileScope]: MyStruct[#MyStruct#]; name=MyStruct +// LIBRARY-DAG: Decl[Protocol]/CurrModule/Flair[RareType,ExprAtFileScope]: MyProtocol[#MyProtocol#]; name=MyProtocol +// LIBRARY-DAG: Decl[Struct]/OtherModule[Swift]/Flair[ExprAtFileScope]/IsSystem: Int[#Int#]; name=Int +// LIBRARY: End completions + +// SCRIPT: Begin completions +// SCRIPT-DAG: Keyword[associatedtype]/None: associatedtype; name=associatedtype +// SCRIPT-DAG: Keyword[class]/None: class; name=class +// SCRIPT-DAG: Keyword[deinit]/None: deinit; name=deinit +// SCRIPT-DAG: Keyword[enum]/None: enum; name=enum +// SCRIPT-DAG: Keyword[extension]/None: extension; name=extension +// SCRIPT-DAG: Keyword[func]/None: func; name=func +// SCRIPT-DAG: Keyword[import]/None: import; name=import +// SCRIPT-DAG: Keyword[init]/None: init; name=init +// SCRIPT-DAG: Keyword[inout]/None: inout; name=inout +// SCRIPT-DAG: Keyword[operator]/None: operator; name=operator +// SCRIPT-DAG: Keyword[precedencegroup]/None: precedencegroup; name=precedencegroup +// SCRIPT-DAG: Keyword[protocol]/None: protocol; name=protocol +// SCRIPT-DAG: Keyword[struct]/None: struct; name=struct +// SCRIPT-DAG: Keyword[subscript]/None: subscript; name=subscript +// SCRIPT-DAG: Keyword[typealias]/None: typealias; name=typealias +// SCRIPT-DAG: Keyword[fileprivate]/None: fileprivate; name=fileprivate +// SCRIPT-DAG: Keyword[internal]/None: internal; name=internal +// SCRIPT-DAG: Keyword[private]/None: private; name=private +// SCRIPT-DAG: Keyword[public]/None: public; name=public +// SCRIPT-DAG: Keyword[static]/None: static; name=static +// SCRIPT-DAG: Keyword/None: final; name=final +// SCRIPT-DAG: Keyword/None: required; name=required +// SCRIPT-DAG: Keyword/None: optional; name=optional +// SCRIPT-DAG: Keyword/None: lazy; name=lazy +// SCRIPT-DAG: Keyword/None: dynamic; name=dynamic +// SCRIPT-DAG: Keyword/None: infix; name=infix +// SCRIPT-DAG: Keyword/None: prefix; name=prefix +// SCRIPT-DAG: Keyword/None: postfix; name=postfix +// SCRIPT-DAG: Keyword/None: mutating; name=mutating +// SCRIPT-DAG: Keyword/None: nonmutating; name=nonmutating +// SCRIPT-DAG: Keyword/None: convenience; name=convenience +// SCRIPT-DAG: Keyword/None: override; name=override +// SCRIPT-DAG: Keyword/None: open; name=open +// SCRIPT-DAG: Keyword/None: weak; name=weak +// SCRIPT-DAG: Keyword/None: unowned; name=unowned +// SCRIPT-DAG: Keyword/None: indirect; name=indirect +// SCRIPT-DAG: Keyword/None: nonisolated; name=nonisolated +// SCRIPT-DAG: Keyword[defer]/None: defer; name=defer +// SCRIPT-DAG: Keyword[if]/None: if; name=if +// SCRIPT-DAG: Keyword[guard]/None: guard; name=guard +// SCRIPT-DAG: Keyword[do]/None: do; name=do +// SCRIPT-DAG: Keyword[repeat]/None: repeat; name=repeat +// SCRIPT-DAG: Keyword[else]/None: else; name=else +// SCRIPT-DAG: Keyword[for]/None: for; name=for +// SCRIPT-DAG: Keyword[in]/None: in; name=in +// SCRIPT-DAG: Keyword[while]/None: while; name=while +// SCRIPT-DAG: Keyword[break]/None: break; name=break +// SCRIPT-DAG: Keyword[continue]/None: continue; name=continue +// SCRIPT-DAG: Keyword[fallthrough]/None: fallthrough; name=fallthrough +// SCRIPT-DAG: Keyword[switch]/None: switch; name=switch +// SCRIPT-DAG: Keyword[let]/None: let; name=let +// SCRIPT-DAG: Keyword[var]/None: var; name=var +// SCRIPT-DAG: Keyword[try]/None: try; name=try +// SCRIPT-DAG: Keyword[try]/None: try!; name=try! +// SCRIPT-DAG: Keyword[try]/None: try?; name=try? +// SCRIPT-DAG: Keyword/None: await; name=await +// SCRIPT-DAG: Literal[Integer]/None: 0[#Int#]; name=0 +// SCRIPT-DAG: Literal[Boolean]/None: true[#Bool#]; name=true +// SCRIPT-DAG: Literal[Boolean]/None: false[#Bool#]; name=false +// SCRIPT-DAG: Literal[Nil]/None: nil; name=nil +// SCRIPT-DAG: Literal[String]/None: "{#(abc)#}"[#String#]; name="" +// SCRIPT-DAG: Literal[Array]/None: [{#(values)#}][#Array#]; name=[] +// SCRIPT-DAG: Literal[Dictionary]/None: [{#(key)#}: {#(value)#}][#Dictionary#]; name=[: ] +// SCRIPT-DAG: Literal[Tuple]/None: ({#(values)#}); name=() +// SCRIPT-DAG: Keyword[#fileID]/None: #fileID[#String#]; name=#fileID +// SCRIPT-DAG: Keyword[#file]/None: #file[#String#]; name=#file +// SCRIPT-DAG: Keyword[#filePath]/None: #filePath[#String#]; name=#filePath +// SCRIPT-DAG: Keyword[#function]/None: #function[#String#]; name=#function +// SCRIPT-DAG: Keyword[#line]/None: #line[#Int#]; name=#line +// SCRIPT-DAG: Keyword[#column]/None: #column[#Int#]; name=#column +// SCRIPT-DAG: Keyword[#dsohandle]/None: #dsohandle[#UnsafeRawPointer#]; name=#dsohandle +// SCRIPT-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct +// SCRIPT-DAG: Decl[Protocol]/CurrModule/Flair[RareType]: MyProtocol[#MyProtocol#]; name=MyProtocol +// SCRIPT-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: Int[#Int#]; name=Int +// SCRIPT: End completions diff --git a/test/IDE/complete_cache.swift b/test/IDE/complete_cache.swift index bea95625aad7e..aed1137804641 100644 --- a/test/IDE/complete_cache.swift +++ b/test/IDE/complete_cache.swift @@ -57,13 +57,13 @@ import ctypes @_private(sourceFile: "AppKit.swift") import AppKit // CLANG_CTYPES: Begin completions -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/keyword[Foo1, Struct1]: FooStruct1[#FooStruct1#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/keyword[Foo2]: FooStruct2[#FooStruct2#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/recommended[Foo2, Foo1]: FooStruct3[#FooStruct3#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/recommendedover[Foo3, Foo2]: FooStruct4[#FooStruct4#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct1[#FooStruct1#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct2[#FooStruct2#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct3[#FooStruct3#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct4[#FooStruct4#]{{; name=.+$}} // CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct5[#FooStruct5#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/recommendedover[ro1, ro2, ro3, ro4]/recommended[r1, r2, r3]/keyword[k1, k2, k3, k4]: FooStruct6[#FooStruct6#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[TypeAlias]/OtherModule[ctypes]/IsSystem/keyword[Foo2]: FooStructTypedef1[#FooStruct2#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct6[#FooStruct6#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[TypeAlias]/OtherModule[ctypes]/IsSystem: FooStructTypedef1[#FooStruct2#]{{; name=.+$}} // CLANG_CTYPES: End completions // CLANG_MACROS: Begin completions diff --git a/test/IDE/complete_cache_notrecommended.swift b/test/IDE/complete_cache_notrecommended.swift new file mode 100644 index 0000000000000..e8ca1c2abab87 --- /dev/null +++ b/test/IDE/complete_cache_notrecommended.swift @@ -0,0 +1,73 @@ +// REQUIRES: concurrency + +// BEGIN MyModule.swift + +public actor MyActor { + public init() {} + public func actorMethod() -> Int { 1 } + + @available(*, deprecated) + public func deprecatedMethod() {} +} + +public func globalAsyncFunc() async -> Int { 1 } + +@available(*, deprecated) +public func deprecatedFunc() {} + +// BEGIN App.swift +import MyModule + +func testSync() -> Int{ + #^GLOBAL_IN_SYNC^# +// FIXME: 'globalAsyncFunc()' *should* be "NotRecommended" because it's 'async' +// The curently behavior is due to completion cache. We should remember +// 'async'-ness in the cache. (rdar://78317170) + +// GLOBAL_IN_SYNC: Begin completions +// GLOBAL_IN_SYNC-DAG: Decl[FreeFunction]/OtherModule[MyModule]: globalAsyncFunc()[' async'][#Int#]; +// GLOBAL_IN_SYNC-DAG: Decl[FreeFunction]/OtherModule[MyModule]/NotRecommended: deprecatedFunc()[#Void#]; +// GLOBAL_IN_SYNC-DAG: Decl[Class]/OtherModule[MyModule]: MyActor[#MyActor#]; +// GLOBAL_IN_SYNC: End completions +} +func testAsync() async -> Int { + #^GLOBAL_IN_ASYNC^# +// GLOBAL_IN_ASYNC: Begin completions +// GLOBAL_IN_ASYNC-DAG: Decl[FreeFunction]/OtherModule[MyModule]: globalAsyncFunc()[' async'][#Int#]; +// GLOBAL_IN_ASYNC-DAG: Decl[FreeFunction]/OtherModule[MyModule]/NotRecommended: deprecatedFunc()[#Void#]; +// GLOBAL_IN_ASYNC-DAG: Decl[Class]/OtherModule[MyModule]: MyActor[#MyActor#]; +// GLOBAL_IN_ASYNC: End completions +} +func testSyncMember(obj: MyActor) -> Int { + obj.#^MEMBER_IN_SYNC^# +// MEMBER_IN_SYNC: Begin completions, 4 items +// MEMBER_IN_SYNC-DAG: Keyword[self]/CurrNominal: self[#MyActor#]; +// MEMBER_IN_SYNC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Identical]: actorMethod()[' async'][#Int#]; +// MEMBER_IN_SYNC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedMethod()[' async'][#Void#]; +// MEMBER_IN_SYNC-DAG: Decl[InstanceVar]/CurrNominal: unownedExecutor[#UnownedSerialExecutor#]; +// MEMBER_IN_SYNC: End completions +} + +func testSyncMember(obj: MyActor) async -> Int { + obj.#^MEMBER_IN_ASYNC^# +// MEMBER_IN_ASYNC: Begin completions, 4 items +// MEMBER_IN_ASYNC-DAG: Keyword[self]/CurrNominal: self[#MyActor#]; +// MEMBER_IN_ASYNC-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: actorMethod()[' async'][#Int#]; +// MEMBER_IN_ASYNC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedMethod()[' async'][#Void#]; +// MEMBER_IN_ASYNC-DAG: Decl[InstanceVar]/CurrNominal: unownedExecutor[#UnownedSerialExecutor#]; +// MEMBER_IN_ASYNC: End completions +} + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %empty-directory(%t/Modules) +// RUN: %target-swift-frontend -emit-module -module-name MyModule -o %t/Modules %t/MyModule.swift -disable-availability-checking + +// RUN: %empty-directory(%t/output) +// RUN: %empty-directory(%t/ccp) +// RUN: %empty-directory(%t/mcp) + +// NOTE: Doing twice is to ensure that the completion cache is used. +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/App.swift -filecheck %raw-FileCheck -completion-output-dir %t/output -I %t/Modules -completion-cache-path %t/ccp -module-cache-path %t/mcp +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/App.swift -filecheck %raw-FileCheck -completion-output-dir %t/output -I %t/Modules -completion-cache-path %t/ccp -module-cache-path %t/mcp diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 33474c15207a7..e89d07500ae36 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -76,18 +76,18 @@ class C1 { } // ARG-NAME1: Begin completions, 2 items -// ARG-NAME1-DAG: Pattern/ExprSpecific: {#b1: Int?#}[#Int?#]; -// ARG-NAME1-DAG: Pattern/ExprSpecific: {#b2: Int?#}[#Int?#]; +// ARG-NAME1-DAG: Pattern/Local/Flair[ArgLabels]: {#b1: Int?#}[#Int?#]; +// ARG-NAME1-DAG: Pattern/Local/Flair[ArgLabels]: {#b2: Int?#}[#Int?#]; // ARG-NAME2: Begin completions, 1 items -// ARG-NAME2-DAG: Pattern/ExprSpecific: {#b: Int#}[#Int#]; +// ARG-NAME2-DAG: Pattern/Local/Flair[ArgLabels]: {#b: Int#}[#Int#]; // ARG-NAME3: Begin completions, 1 items -// ARG-NAME3-DAG: Pattern/ExprSpecific: {#b: String?#}[#String?#]; +// ARG-NAME3-DAG: Pattern/Local/Flair[ArgLabels]: {#b: String?#}[#String?#]; // ARG-NAME4: Begin completions, 2 items -// ARG-NAME4-DAG: Pattern/ExprSpecific: {#b1: String#}[#String#]; -// ARG-NAME4-DAG: Pattern/ExprSpecific: {#b2: String#}[#String#]; +// ARG-NAME4-DAG: Pattern/Local/Flair[ArgLabels]: {#b1: String#}[#String#]; +// ARG-NAME4-DAG: Pattern/Local/Flair[ArgLabels]: {#b2: String#}[#String#]; // ARG-NAME4: End completions // EXPECT_OINT: Begin completions @@ -98,6 +98,11 @@ class C1 { // EXPECT_OINT-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: oi2[#Int?#]; name=oi2 // EXPECT_OINT-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: oi1[#Int?#]; name=oi1 // EXPECT_OINT-DAG: Decl[GlobalVar]/CurrModule: os1[#String?#]; name=os1 +// EXPECT_OINT-DAG: Keyword[try]/None: try; name=try +// EXPECT_OINT-DAG: Keyword[try]/None: try!; name=try! +// EXPECT_OINT-DAG: Keyword[try]/None: try?; name=try? +// EXPECT_OINT-DAG: Keyword/None: await; name=await +// EXPECT_OINT-NOT: Keyword[super] // EXPECT_OINT: End completions // EXPECT_INT: Begin completions @@ -112,6 +117,11 @@ class C1 { // EXPECT_INT-DAG: Decl[GlobalVar]/CurrModule: oi1[#Int?#]; name=oi1 // EXPECT_INT-DAG: Decl[GlobalVar]/CurrModule: os2[#String?#]; name=os2 // EXPECT_INT-DAG: Decl[GlobalVar]/CurrModule: oi2[#Int?#]; name=oi2 +// EXPECT_INT-DAG: Keyword[try]/None: try; name=try +// EXPECT_INT-DAG: Keyword[try]/None: try!; name=try! +// EXPECT_INT-DAG: Keyword[try]/None: try?; name=try? +// EXPECT_INT-DAG: Keyword/None: await; name=await +// EXPECT_INT-NOT: Keyword[super] // EXPECT_INT: End completions class C2 { @@ -140,6 +150,11 @@ class C2 { // EXPECT_OSTRING-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Identical]: ostringGen()[#String?#]; name=ostringGen() // EXPECT_OSTRING-DAG: Decl[GlobalVar]/CurrModule: i1[#Int#]; name=i1 // EXPECT_OSTRING-DAG: Decl[GlobalVar]/CurrModule: i2[#Int#]; name=i2 +// EXPECT_OSTRING-DAG: Keyword[try]/None: try; name=try +// EXPECT_OSTRING-DAG: Keyword[try]/None: try!; name=try! +// EXPECT_OSTRING-DAG: Keyword[try]/None: try?; name=try? +// EXPECT_OSTRING-DAG: Keyword/None: await; name=await +// EXPECT_OSTRING-NOT: Keyword[super] // EXPECT_OSTRING: End completions // EXPECT_STRING: Begin completions @@ -151,6 +166,11 @@ class C2 { // EXPECT_STRING-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s2[#String#]; name=s2 // EXPECT_STRING-DAG: Decl[GlobalVar]/CurrModule: os1[#String?#]; name=os1 // EXPECT_STRING-DAG: Decl[GlobalVar]/CurrModule: os2[#String?#]; name=os2 +// EXPECT_STRING-DAG: Keyword[try]/None: try; name=try +// EXPECT_STRING-DAG: Keyword[try]/None: try!; name=try! +// EXPECT_STRING-DAG: Keyword[try]/None: try?; name=try? +// EXPECT_STRING-DAG: Keyword/None: await; name=await +// EXPECT_STRING-NOT: Keyword[super] // EXPECT_STRING: End completions func foo2(_ a : C1, b1 : C2) {} @@ -215,15 +235,15 @@ class C3 { // NEGATIVE_OVERLOAD4-NOT: Decl[Class]{{.*}} C2 // OVERLOAD5: Begin completions -// OVERLOAD5-DAG: Decl[FreeFunction]/CurrModule: ['(']{#(a): C1#}, {#b1: C2#}[')'][#Void#]; name=a: C1, b1: C2 -// OVERLOAD5-DAG: Decl[FreeFunction]/CurrModule: ['(']{#(a): C2#}, {#b2: C1#}[')'][#Void#]; name=a: C2, b2: C1 +// OVERLOAD5-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#(a): C1#}, {#b1: C2#}[')'][#Void#]; name=:b1: +// OVERLOAD5-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#(a): C2#}, {#b2: C1#}[')'][#Void#]; name=:b2: // OVERLOAD5-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: C1I[#C1#]; name=C1I // OVERLOAD5-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: C2I[#C2#]; name=C2I // OVERLOAD5: End completions // OVERLOAD6: Begin completions -// OVERLOAD6-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(a1): C1#}, {#b1: C2#}[')'][#Void#]; name=a1: C1, b1: C2 -// OVERLOAD6-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#a2: C2#}, {#b2: C1#}[')'][#Void#]; name=a2: C2, b2: C1 +// OVERLOAD6-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(a1): C1#}, {#b1: C2#}[')'][#Void#]; name=:b1: +// OVERLOAD6-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#a2: C2#}, {#b2: C1#}[')'][#Void#]; name=a2:b2: // OVERLOAD6-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: C1I[#C1#]; name=C1I // OVERLOAD6-DAG: Decl[InstanceVar]/CurrNominal: C2I[#C2#]; name=C2I // OVERLOAD6: End completions @@ -240,7 +260,7 @@ extension C3 { } // HASERROR1: Begin completions -// HASERROR1-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#a1: C1#}, {#b1: <>#}[')'][#Int#]; +// HASERROR1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#a1: C1#}, {#b1: <>#}[')'][#Int#]; // HASERROR1: End completions // HASERROR2: Begin completions @@ -249,7 +269,7 @@ extension C3 { // HASERROR2: End completions // HASERROR3: Begin completions -// HASERROR3-DAG: Pattern/ExprSpecific: {#b1: <>#}[#<>#]; +// HASERROR3-DAG: Pattern/Local/Flair[ArgLabels]: {#b1: <>#}[#<>#]; // HASERROR3: End completions // HASERROR4: Begin completions @@ -316,36 +336,41 @@ class C4 { // MEMBER1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntOpGen()[#Int?#]; name=IntOpGen() // MEMBER1-DAG: Decl[InstanceMethod]/CurrNominal: StringGen()[#String#]; name=StringGen() // MEMBER1-DAG: Decl[InstanceMethod]/CurrNominal: StringOpGen()[#String?#]; name=StringOpGen() -// MEMBER1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: IntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=IntTaker(i1: Int, i2: Int) -// MEMBER1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: StringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=StringTaker(s1: String, s2: String) +// MEMBER1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: IntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=IntTaker(:i2:) +// MEMBER1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: StringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=StringTaker(:s2:) +// MEMBER1-NOT: Keyword[try]/None: try; name=try +// MEMBER1-NOT: Keyword[try]/None: try!; name=try! +// MEMBER1-NOT: Keyword[try]/None: try?; name=try? +// MEMBER1-NOT: Keyword/None: await; name=await +// MEMBER1-NOT: Keyword[super] // MEMBER2: Begin completions // MEMBER2-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#]; name=IntGen() // MEMBER2-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#]; name=IntOpGen() // MEMBER2-DAG: Decl[InstanceMethod]/CurrNominal: StringGen()[#String#]; name=StringGen() // MEMBER2-DAG: Decl[InstanceMethod]/CurrNominal: StringOpGen()[#String?#]; name=StringOpGen() -// MEMBER2-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: IntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=IntTaker(i1: Int, i2: Int) -// MEMBER2-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: StringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=StringTaker(s1: String, s2: String) +// MEMBER2-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: IntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=IntTaker(:i2:) +// MEMBER2-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: StringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=StringTaker(:s2:) // MEMBER3: Begin completions // MEMBER3-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#]; name=IntGen() // MEMBER3-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#]; name=IntOpGen() // MEMBER3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: StringGen()[#String#]; name=StringGen() // MEMBER3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: StringOpGen()[#String?#]; name=StringOpGen() -// MEMBER3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: IntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=IntTaker(i1: Int, i2: Int) -// MEMBER3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: StringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=StringTaker(s1: String, s2: String) +// MEMBER3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: IntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=IntTaker(:i2:) +// MEMBER3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: StringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=StringTaker(:s2:) // MEMBER4: Begin completions // MEMBER4-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#]; name=IntGen() // MEMBER4-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#]; name=IntOpGen() // MEMBER4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: StringGen()[#String#]; name=StringGen() // MEMBER4-DAG: Decl[InstanceMethod]/CurrNominal: StringOpGen()[#String?#]; name=StringOpGen() -// MEMBER4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: IntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=IntTaker(i1: Int, i2: Int) -// MEMBER4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: StringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=StringTaker(s1: String, s2: String) +// MEMBER4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: IntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=IntTaker(:i2:) +// MEMBER4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: StringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=StringTaker(:s2:) // MEMBER7: Begin completions // MEMBER7-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: removeAll()[#Void#]; name=removeAll() -// MEMBER7-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: removeAll({#keepingCapacity: Bool#})[#Void#]; name=removeAll(keepingCapacity: Bool) +// MEMBER7-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: removeAll({#keepingCapacity: Bool#})[#Void#]; name=removeAll(keepingCapacity:) // MEMBER7-DAG: Decl[InstanceVar]/CurrNominal/IsSystem/TypeRelation[Convertible]: count[#Int#]; name=count // MEMBER7-DAG: Decl[InstanceVar]/CurrNominal/IsSystem/TypeRelation[Convertible]: capacity[#Int#]; name=capacity @@ -354,8 +379,8 @@ class C4 { // MEMBER8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: InternalIntOpGen()[#Int?#]; name=InternalIntOpGen() // MEMBER8-DAG: Decl[InstanceMethod]/CurrNominal: InternalStringGen()[#String#]; name=InternalStringGen() // MEMBER8-DAG: Decl[InstanceMethod]/CurrNominal: InternalStringOpGen()[#String?#]; name=InternalStringOpGen() -// MEMBER8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: InternalIntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=InternalIntTaker(i1: Int, i2: Int) -// MEMBER8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: InternalStringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=InternalStringTaker(s1: String, s2: String) +// MEMBER8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: InternalIntTaker({#(i1): Int#}, {#i2: Int#})[#Void#]; name=InternalIntTaker(:i2:) +// MEMBER8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: InternalStringTaker({#(s1): String#}, {#s2: String#})[#Void#]; name=InternalStringTaker(:s2:) // FARG6: Begin completions // FARG6-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: InternalIntGen()[#Int#] @@ -365,11 +390,21 @@ class C4 { // FARG6-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: InternalIntTaker({#(i1): Int#}, {#i2: Int#})[#Void#] // FARG6-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: InternalStringTaker({#(s1): String#}, {#s2: String#})[#Void#] +class C5 {} +class C6 : C5 { + func f1() { + foo(3, b: #^ARGSUPER?check=EXPECT-SUPER^#) + } +} + +// EXPECT-SUPER: Begin completions +// EXPECT-SUPER-DAG: Keyword[super]/CurrNominal: super[#C5#]; name=super + func firstArg(arg1 arg1: Int, arg2: Int) {} func testArg1Name1() { firstArg(#^FIRST_ARG_NAME_1?check=FIRST_ARG_NAME_PATTERN^# } -// FIRST_ARG_NAME_PATTERN: Decl[FreeFunction]/CurrModule: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#]; +// FIRST_ARG_NAME_PATTERN: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#]; func testArg2Name1() { firstArg(#^FIRST_ARG_NAME_2?check=FIRST_ARG_NAME_PATTERN^#) } @@ -417,7 +452,7 @@ public func fopen() -> TestBoundGeneric1! { fatalError() } func other() { _ = fopen(#^CALLARG_IUO^#) // CALLARG_IUO: Begin completions, 1 items -// CALLARG_IUO: Decl[FreeFunction]/CurrModule: ['('][')'][#TestBoundGeneric1!#]; name= +// CALLARG_IUO: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['('][')'][#TestBoundGeneric1!#]; name= // CALLARG_IUO: End completions } @@ -439,7 +474,7 @@ func curry(_ f: @escaping (T1, T2) -> R) -> (T1) -> (T2) -> R { return { t1 in { t2 in f(#^NESTED_CLOSURE^#, t2) } } // NESTED_CLOSURE: Begin completions // FIXME: Should be '/TypeRelation[Invalid]: t2[#T2#]' - // NESTED_CLOSURE: Decl[LocalVar]/Local: t2[#_#]; name=t2 + // NESTED_CLOSURE: Decl[LocalVar]/Local: t2; name=t2 // NESTED_CLOSURE: Decl[LocalVar]/Local: t1[#T1#]; name=t1 } @@ -468,10 +503,10 @@ func testTupleShuffle() { // SHUFFLE_2-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s2[#String#]; name=s2 // SHUFFLE_3: Begin completions, 4 items -// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo -// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar -// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz -// SHUFFLE_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: SimpleEnum) +// SHUFFLE_3-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo +// SHUFFLE_3-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar +// SHUFFLE_3-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz +// SHUFFLE_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(:) class HasSubscript { @@ -485,23 +520,28 @@ func testSubscript(obj: HasSubscript, intValue: Int, strValue: String) { // SUBSCRIPT_1-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: i2[#Int#]; name=i2 // SUBSCRIPT_1-DAG: Decl[GlobalVar]/CurrModule: s1[#String#]; name=s1 // SUBSCRIPT_1-DAG: Decl[GlobalVar]/CurrModule: s2[#String#]; name=s2 +// SUBSCRIPT_1-DAG: Keyword[try]/None: try; name=try +// SUBSCRIPT_1-DAG: Keyword[try]/None: try!; name=try! +// SUBSCRIPT_1-DAG: Keyword[try]/None: try?; name=try? +// SUBSCRIPT_1-DAG: Keyword/None: await; name=await +// SUBSCRIPT_1-NOT: Keyword[super] let _ = obj[.#^SUBSCRIPT_1_DOT^# // SUBSCRIPT_1_DOT: Begin completions // SUBSCRIPT_1_DOT-NOT: i1 // SUBSCRIPT_1_DOT-NOT: s1 -// SUBSCRIPT_1_DOT-DAG: Decl[StaticVar]/ExprSpecific/IsSystem/TypeRelation[Identical]: max[#Int#]; name=max -// SUBSCRIPT_1_DOT-DAG: Decl[StaticVar]/ExprSpecific/IsSystem/TypeRelation[Identical]: min[#Int#]; name=min +// SUBSCRIPT_1_DOT-DAG: Decl[StaticVar]/Super/Flair[ExprSpecific]/IsSystem/TypeRelation[Identical]: max[#Int#]; name=max +// SUBSCRIPT_1_DOT-DAG: Decl[StaticVar]/Super/Flair[ExprSpecific]/IsSystem/TypeRelation[Identical]: min[#Int#]; name=min let _ = obj[42, #^SUBSCRIPT_2^# // SUBSCRIPT_2: Begin completions, 1 items -// SUBSCRIPT_2-NEXT: Pattern/ExprSpecific: {#default: String#}[#String#]; +// SUBSCRIPT_2-NEXT: Pattern/Local/Flair[ArgLabels]: {#default: String#}[#String#]; let _ = obj[42, .#^SUBSCRIPT_2_DOT^# // Note: we still provide completions despite the missing label - there's a fixit to add it in later. // SUBSCRIPT_2_DOT: Begin completions // SUBSCRIPT_2_DOT: Decl[Constructor]/CurrNominal/IsSystem/TypeRelation[Identical]: init()[#String#]; name=init() -// SUBSCRIPT_2_DOT: Decl[Constructor]/CurrNominal/IsSystem/TypeRelation[Identical]: init({#(c): Character#})[#String#]; name=init(c: Character) +// SUBSCRIPT_2_DOT: Decl[Constructor]/CurrNominal/IsSystem/TypeRelation[Identical]: init({#(c): Character#})[#String#]; name=init(:) let _ = obj[42, default: #^SUBSCRIPT_3^# // SUBSCRIPT_3: Begin completions @@ -509,13 +549,18 @@ func testSubscript(obj: HasSubscript, intValue: Int, strValue: String) { // SUBSCRIPT_3-DAG: Decl[GlobalVar]/CurrModule: i2[#Int#]; name=i2 // SUBSCRIPT_3-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s1[#String#]; name=s1 // SUBSCRIPT_3-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s2[#String#]; name=s2 +// SUBSCRIPT_3-DAG: Keyword[try]/None: try; name=try +// SUBSCRIPT_3-DAG: Keyword[try]/None: try!; name=try! +// SUBSCRIPT_3-DAG: Keyword[try]/None: try?; name=try? +// SUBSCRIPT_3-DAG: Keyword/None: await; name=await +// SUBSCRIPT_3-NOT: Keyword[super] let _ = obj[42, default: .#^SUBSCRIPT_3_DOT^# // SUBSCRIPT_3_DOT: Begin completions // SUBSCRIPT_3_DOT-NOT: i1 // SUBSCRIPT_3_DOT-NOT: s1 // SUBSCRIPT_3_DOT-DAG: Decl[Constructor]/CurrNominal/IsSystem/TypeRelation[Identical]: init()[#String#]; name=init() -// SUBSCRIPT_3_DOT-DAG: Decl[Constructor]/CurrNominal/IsSystem/TypeRelation[Identical]: init({#(c): Character#})[#String#]; name=init(c: Character) +// SUBSCRIPT_3_DOT-DAG: Decl[Constructor]/CurrNominal/IsSystem/TypeRelation[Identical]: init({#(c): Character#})[#String#]; name=init(:) } @@ -545,8 +590,8 @@ class TestImplicitlyCurriedSelf { TestImplicitlyCurriedSelf.foo(#^CURRIED_SELF_3?check=CURRIED_SELF_1^# // CURRIED_SELF_1: Begin completions, 2 items -// CURRIED_SELF_1-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(self): TestImplicitlyCurriedSelf#}[')'][#(Int) -> ()#]{{; name=.+$}} -// CURRIED_SELF_1-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(self): TestImplicitlyCurriedSelf#}[')'][#(Int, Int) -> ()#]{{; name=.+$}} +// CURRIED_SELF_1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(self): TestImplicitlyCurriedSelf#}[')'][#(Int) -> ()#]{{; name=.+$}} +// CURRIED_SELF_1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(self): TestImplicitlyCurriedSelf#}[')'][#(Int, Int) -> ()#]{{; name=.+$}} // CURRIED_SELF_1: End completions } } @@ -564,70 +609,70 @@ class TestStaticMemberCall { func testStaticMemberCall() { let _ = TestStaticMemberCall.create1(#^STATIC_METHOD_AFTERPAREN_1^#) // STATIC_METHOD_AFTERPAREN_1: Begin completions, 1 items -// STATIC_METHOD_AFTERPAREN_1: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int +// STATIC_METHOD_AFTERPAREN_1: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: // STATIC_METHOD_AFTERPAREN_1: End completions let _ = TestStaticMemberCall.create2(#^STATIC_METHOD_AFTERPAREN_2^#) // STATIC_METHOD_AFTERPAREN_2: Begin completions -// STATIC_METHOD_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#(arg1): Int#}[')'][#TestStaticMemberCall#]; -// STATIC_METHOD_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#(arg1): Int#}, {#arg2: Int#}, {#arg3: Int#}, {#arg4: Int#}[')'][#TestStaticMemberCall#]; +// STATIC_METHOD_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#(arg1): Int#}[')'][#TestStaticMemberCall#]; +// STATIC_METHOD_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#(arg1): Int#}, {#arg2: Int#}, {#arg3: Int#}, {#arg4: Int#}[')'][#TestStaticMemberCall#]; // STATIC_METHOD_AFTERPAREN_2-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem/TypeRelation[Identical]: Int[#Int#]; // STATIC_METHOD_AFTERPAREN_2-DAG: Literal[Integer]/None/TypeRelation[Identical]: 0[#Int#]; // STATIC_METHOD_AFTERPAREN_2: End completions let _ = TestStaticMemberCall.create2(1, #^STATIC_METHOD_SECOND^#) // STATIC_METHOD_SECOND: Begin completions, 3 items -// STATIC_METHOD_SECOND: Pattern/ExprSpecific: {#arg2: Int#}[#Int#]; -// STATIC_METHOD_SECOND: Pattern/ExprSpecific: {#arg3: Int#}[#Int#]; -// STATIC_METHOD_SECOND: Pattern/ExprSpecific: {#arg4: Int#}[#Int#]; +// STATIC_METHOD_SECOND: Pattern/Local/Flair[ArgLabels]: {#arg2: Int#}[#Int#]; +// STATIC_METHOD_SECOND: Pattern/Local/Flair[ArgLabels]: {#arg3: Int#}[#Int#]; +// STATIC_METHOD_SECOND: Pattern/Local/Flair[ArgLabels]: {#arg4: Int#}[#Int#]; // STATIC_METHOD_SECOND: End completions let _ = TestStaticMemberCall.create2(1, arg3: 2, #^STATIC_METHOD_SKIPPED^#) // STATIC_METHOD_SKIPPED: Begin completions, 1 item -// STATIC_METHOD_SKIPPED: Pattern/ExprSpecific: {#arg4: Int#}[#Int#]; +// STATIC_METHOD_SKIPPED: Pattern/Local/Flair[ArgLabels]: {#arg4: Int#}[#Int#]; // STATIC_METHOD_SKIPPED: End completions let _ = TestStaticMemberCall.createOverloaded(#^STATIC_METHOD_OVERLOADED^#) // STATIC_METHOD_OVERLOADED: Begin completions, 2 items -// STATIC_METHOD_OVERLOADED-DAG: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int -// STATIC_METHOD_OVERLOADED-DAG: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: String#}[')'][#String#]; name=arg1: String +// STATIC_METHOD_OVERLOADED-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: +// STATIC_METHOD_OVERLOADED-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: String#}[')'][#String#]; name=arg1: // STATIC_METHOD_OVERLOADED: End completions } func testImplicitMember() { let _: TestStaticMemberCall = .create1(#^IMPLICIT_MEMBER_AFTERPAREN_1^#) // IMPLICIT_MEMBER_AFTERPAREN_1: Begin completions, 1 items -// IMPLICIT_MEMBER_AFTERPAREN_1: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int +// IMPLICIT_MEMBER_AFTERPAREN_1: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: // IMPLICIT_MEMBER_AFTERPAREN_1: End completions let _: TestStaticMemberCall = .create2(#^IMPLICIT_MEMBER_AFTERPAREN_2^#) // IMPLICIT_MEMBER_AFTERPAREN_2: Begin completions -// IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#(arg1): Int#}[')'][#TestStaticMemberCall#]; -// IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#(arg1): Int#}, {#arg2: Int#}, {#arg3: Int#}, {#arg4: Int#}[')'][#TestStaticMemberCall#]; +// IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#(arg1): Int#}[')'][#TestStaticMemberCall#]; +// IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#(arg1): Int#}, {#arg2: Int#}, {#arg3: Int#}, {#arg4: Int#}[')'][#TestStaticMemberCall#]; // IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem/TypeRelation[Identical]: Int[#Int#]; // IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Literal[Integer]/None/TypeRelation[Identical]: 0[#Int#]; // IMPLICIT_MEMBER_AFTERPAREN_2: End completions let _: TestStaticMemberCall? = .create1(#^IMPLICIT_MEMBER_AFTERPAREN_3^#) // IMPLICIT_MEMBER_AFTERPAREN_3: Begin completions, 1 items -// IMPLICIT_MEMBER_AFTERPAREN_3: Decl[StaticMethod]/CurrNominal/TypeRelation[Convertible]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int +// IMPLICIT_MEMBER_AFTERPAREN_3: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: // IMPLICIT_MEMBER_AFTERPAREN_3: End completions let _: TestStaticMemberCall = .create2(1, #^IMPLICIT_MEMBER_SECOND^#) // IMPLICIT_MEMBER_SECOND: Begin completions, 3 items -// IMPLICIT_MEMBER_SECOND: Pattern/ExprSpecific: {#arg2: Int#}[#Int#]; -// IMPLICIT_MEMBER_SECOND: Pattern/ExprSpecific: {#arg3: Int#}[#Int#]; -// IMPLICIT_MEMBER_SECOND: Pattern/ExprSpecific: {#arg4: Int#}[#Int#]; +// IMPLICIT_MEMBER_SECOND: Pattern/Local/Flair[ArgLabels]: {#arg2: Int#}[#Int#]; +// IMPLICIT_MEMBER_SECOND: Pattern/Local/Flair[ArgLabels]: {#arg3: Int#}[#Int#]; +// IMPLICIT_MEMBER_SECOND: Pattern/Local/Flair[ArgLabels]: {#arg4: Int#}[#Int#]; // IMPLICIT_MEMBER_SECOND: End completions let _: TestStaticMemberCall = .create2(1, arg3: 2, #^IMPLICIT_MEMBER_SKIPPED^#) // IMPLICIT_MEMBER_SKIPPED: Begin completions, 1 item -// IMPLICIT_MEMBER_SKIPPED: Pattern/ExprSpecific: {#arg4: Int#}[#Int#]; +// IMPLICIT_MEMBER_SKIPPED: Pattern/Local/Flair[ArgLabels]: {#arg4: Int#}[#Int#]; // IMPLICIT_MEMBER_SKIPPED: End completions let _: TestStaticMemberCall = .createOverloaded(#^IMPLICIT_MEMBER_OVERLOADED^#) // IMPLICIT_MEMBER_OVERLOADED: Begin completions, 2 items -// IMPLICIT_MEMBER_OVERLOADED: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int -// IMPLICIT_MEMBER_OVERLOADED: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: String#}[')'][#String#]; name=arg1: String +// IMPLICIT_MEMBER_OVERLOADED: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: +// IMPLICIT_MEMBER_OVERLOADED: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: String#}[')'][#String#]; name=arg1: // IMPLICIT_MEMBER_OVERLOADED: End completions } func testImplicitMemberInArrayLiteral() { @@ -681,7 +726,7 @@ struct Wrap { func testGenricMethodOnGenericOfArchetype(value: Wrap) { value.method(#^ARCHETYPE_GENERIC_1^#) // ARCHETYPE_GENERIC_1: Begin completions -// ARCHETYPE_GENERIC_1: Decl[InstanceMethod]/CurrNominal: ['(']{#(fn): (Wrapped) -> U##(Wrapped) -> U#}[')'][#Wrap#]; +// ARCHETYPE_GENERIC_1: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(fn): (Wrapped) -> U##(Wrapped) -> U#}[')'][#Wrap#]; } struct TestHasErrorAutoclosureParam { @@ -691,7 +736,7 @@ struct TestHasErrorAutoclosureParam { func test() { hasErrorAutoclosureParam(#^PARAM_WITH_ERROR_AUTOCLOSURE^# // PARAM_WITH_ERROR_AUTOCLOSURE: Begin completions, 1 items -// PARAM_WITH_ERROR_AUTOCLOSURE: Decl[InstanceMethod]/CurrNominal: ['(']{#value: <>#}[')'][#Void#]; +// PARAM_WITH_ERROR_AUTOCLOSURE: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#value: <>#}[')'][#Void#]; // PARAM_WITH_ERROR_AUTOCLOSURE: End completions } } @@ -705,9 +750,9 @@ struct MyType { func testTypecheckedOverloaded(value: MyType) { value.overloaded(#^TYPECHECKED_OVERLOADED^#) // TYPECHECKED_OVERLOADED: Begin completions -// TYPECHECKED_OVERLOADED-DAG: Decl[InstanceMethod]/CurrNominal: ['('][')'][#Void#]; -// TYPECHECKED_OVERLOADED-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(int): Int#}[')'][#Void#]; -// TYPECHECKED_OVERLOADED-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#name: String#}, {#value: String#}[')'][#Void#]; +// TYPECHECKED_OVERLOADED-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['('][')'][#Void#]; +// TYPECHECKED_OVERLOADED-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(int): Int#}[')'][#Void#]; +// TYPECHECKED_OVERLOADED-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#name: String#}, {#value: String#}[')'][#Void#]; // TYPECHECKED_OVERLOADED: End completions } @@ -718,40 +763,40 @@ func testTypecheckedTypeExpr() { MyType(#^TYPECHECKED_TYPEEXPR^# } // TYPECHECKED_TYPEEXPR: Begin completions -// TYPECHECKED_TYPEEXPR: Decl[Constructor]/CurrNominal: ['(']{#arg1: String#}, {#arg2: _#}[')'][#MyType<_>#]; name=arg1: String, arg2: _ -// TYPECHECKED_TYPEEXPR: Decl[Constructor]/CurrNominal: ['(']{#(intVal): Int#}[')'][#MyType#]; name=intVal: Int +// TYPECHECKED_TYPEEXPR: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: String#}, {#arg2: _#}[')'][#MyType<_>#]; name=arg1:arg2: +// TYPECHECKED_TYPEEXPR: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#(intVal): Int#}[')'][#MyType#]; name=: // TYPECHECKED_TYPEEXPR: End completions func testPamrameterFlags(_: Int, inoutArg: inout Int, autoclosureArg: @autoclosure () -> Int, iuoArg: Int!, variadicArg: Int...) { var intVal = 1 testPamrameterFlags(intVal, #^ARG_PARAMFLAG_INOUT^#) // ARG_PARAMFLAG_INOUT: Begin completions, 1 items -// ARG_PARAMFLAG_INOUT-DAG: Pattern/ExprSpecific: {#inoutArg: &Int#}[#inout Int#]; name=inoutArg: +// ARG_PARAMFLAG_INOUT-DAG: Pattern/Local/Flair[ArgLabels]: {#inoutArg: &Int#}[#inout Int#]; name=inoutArg: // ARG_PARAMFLAG_INOUT: End completions testPamrameterFlags(intVal, inoutArg: &intVal, #^ARG_PARAMFLAG_AUTOCLOSURE^#) // ARG_PARAMFLAG_AUTOCLOSURE: Begin completions, 1 items -// ARG_PARAMFLAG_AUTOCLOSURE-DAG: Pattern/ExprSpecific: {#autoclosureArg: Int#}[#Int#]; +// ARG_PARAMFLAG_AUTOCLOSURE-DAG: Pattern/Local/Flair[ArgLabels]: {#autoclosureArg: Int#}[#Int#]; // ARG_PARAMFLAG_AUTOCLOSURE: End completions testPamrameterFlags(intVal, inoutArg: &intVal, autoclosureArg: intVal, #^ARG_PARAMFLAG_IUO^#) // ARG_PARAMFLAG_IUO: Begin completions, 1 items -// ARG_PARAMFLAG_IUO-DAG: Pattern/ExprSpecific: {#iuoArg: Int?#}[#Int?#]; +// ARG_PARAMFLAG_IUO-DAG: Pattern/Local/Flair[ArgLabels]: {#iuoArg: Int?#}[#Int?#]; // ARG_PARAMFLAG_IUO: End completions testPamrameterFlags(intVal, inoutArg: &intVal, autoclosureArg: intVal, iuoArg: intVal, #^ARG_PARAMFLAG_VARIADIC^#) // ARG_PARAMFLAG_VARIADIC: Begin completions, 1 items -// ARG_PARAMFLAG_VARIADIC-DAG: Pattern/ExprSpecific: {#variadicArg: Int...#}[#Int#]; +// ARG_PARAMFLAG_VARIADIC-DAG: Pattern/Local/Flair[ArgLabels]: {#variadicArg: Int...#}[#Int#]; // ARG_PARAMFLAG_VARIADIC: End completions } func testTupleElement(arg: (SimpleEnum, SimpleEnum)) { testTupleElement(arg: (.foo, .#^TUPLEELEM_1^#)) // TUPLEELEM_1: Begin completions, 4 items -// TUPLEELEM_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo -// TUPLEELEM_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar -// TUPLEELEM_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz -// TUPLEELEM_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: SimpleEnum) +// TUPLEELEM_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo +// TUPLEELEM_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar +// TUPLEELEM_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz +// TUPLEELEM_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(:) // TUPLEELEM_1: End completions testTupleElement(arg: (.foo, .bar, .#^TUPLEELEM_2^#)) // TUPLEELEM_2-NOT: Begin completions @@ -768,23 +813,23 @@ func testKeyPathThunkInBase() { foo(\.value).testFunc(.#^KEYPATH_THUNK_BASE^#) // KEYPATH_THUNK_BASE: Begin completions, 4 items -// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo -// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar -// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz -// KEYPATH_THUNK_BASE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: SimpleEnum) +// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo +// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar +// KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz +// KEYPATH_THUNK_BASE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(:) // KEYPATH_THUNK_BASE: End completions } func testVariadic(_ arg: Any..., option1: Int = 0, option2: String = 1) { testVariadic(#^VARIADIC_1^#) // VARIADIC_1: Begin completions -// VARIADIC_1-DAG: Decl[FreeFunction]/CurrModule: ['(']{#(arg): Any...#}, {#option1: Int#}, {#option2: String#}[')'][#Void#]; +// VARIADIC_1-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#(arg): Any...#}, {#option1: Int#}, {#option2: String#}[')'][#Void#]; // VARIADIC_1-DAG: Decl[GlobalVar]/CurrModule: i1[#Int#]; // VARIADIC_1: End completions testVariadic(1, #^VARIADIC_2^#) // VARIADIC_2: Begin completions -// VARIADIC_2-DAG: Pattern/ExprSpecific: {#option1: Int#}[#Int#]; -// VARIADIC_2-DAG: Pattern/ExprSpecific: {#option2: String#}[#String#]; +// VARIADIC_2-DAG: Pattern/Local/Flair[ArgLabels]: {#option1: Int#}[#Int#]; +// VARIADIC_2-DAG: Pattern/Local/Flair[ArgLabels]: {#option2: String#}[#String#]; // VARIADIC_2-DAG: Decl[GlobalVar]/CurrModule: i1[#Int#]; // VARIADIC_2: End completions testVariadic(1, 2, #^VARIADIC_3?check=VARIADIC_2^#) @@ -797,7 +842,7 @@ func testLabelsInSelfDotInit() { convenience init() { self.init(a: 1, #^LABEL_IN_SELF_DOT_INIT^#) // LABEL_IN_SELF_DOT_INIT: Begin completions, 1 item -// LABEL_IN_SELF_DOT_INIT-DAG: Pattern/ExprSpecific: {#b: Int#}[#Int#] +// LABEL_IN_SELF_DOT_INIT-DAG: Pattern/Local/Flair[ArgLabels]: {#b: Int#}[#Int#] // LABEL_IN_SELF_DOT_INIT: End completions } } @@ -810,7 +855,7 @@ func testMissingRequiredParameter() { func test(c: C) { c.foo(y: 1, #^MISSING_REQUIRED_PARAM^#) // MISSING_REQUIRED_PARAM: Begin completions, 1 item -// MISSING_REQUIRED_PARAM-DAG: Pattern/ExprSpecific: {#z: Int#}[#Int#] +// MISSING_REQUIRED_PARAM-DAG: Pattern/Local/Flair[ArgLabels]: {#z: Int#}[#Int#] // MISSING_REQUIRED_PARAM: End completions } } @@ -822,7 +867,7 @@ func testAfterVariadic() { func test(c: C) { c.foo(x: 10, 20, 30, y: 40, #^NAMED_PARAMETER_WITH_LEADING_VARIADIC^#) // NAMED_PARAMETER_WITH_LEADING_VARIADIC: Begin completions, 1 item -// NAMED_PARAMETER_WITH_LEADING_VARIADIC-DAG: Pattern/ExprSpecific: {#z: Int#}[#Int#] +// NAMED_PARAMETER_WITH_LEADING_VARIADIC-DAG: Pattern/Local/Flair[ArgLabels]: {#z: Int#}[#Int#] // NAMED_PARAMETER_WITH_LEADING_VARIADIC: End completions } } @@ -831,25 +876,25 @@ func testClosurePlaceholderContainsInternalParameterNamesIfPresentInSiganture() func sort(callback: (_ left: Int, _ right: Int) -> Bool) {} sort(#^CLOSURE_PARAM_WITH_INTERNAL_NAME^#) // CLOSURE_PARAM_WITH_INTERNAL_NAME: Begin completions, 1 item -// CLOSURE_PARAM_WITH_INTERNAL_NAME-DAG: Decl[FreeFunction]/Local: ['(']{#callback: (Int, Int) -> Bool##(_ left: Int, _ right: Int) -> Bool#}[')'][#Void#]; +// CLOSURE_PARAM_WITH_INTERNAL_NAME-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]: ['(']{#callback: (Int, Int) -> Bool##(_ left: Int, _ right: Int) -> Bool#}[')'][#Void#]; // CLOSURE_PARAM_WITH_INTERNAL_NAME: End completions func sortWithParensAroundClosureType(callback: ((_ left: Int, _ right: Int) -> Bool)) {} sortWithParensAroundClosureType(#^CLOSURE_PARAM_WITH_PARENS^#) // CLOSURE_PARAM_WITH_PARENS: Begin completions, 1 item -// CLOSURE_PARAM_WITH_PARENS-DAG: Decl[FreeFunction]/Local: ['(']{#callback: ((Int, Int) -> Bool)##(_ left: Int, _ right: Int) -> Bool#}[')'][#Void#]; +// CLOSURE_PARAM_WITH_PARENS-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]: ['(']{#callback: ((Int, Int) -> Bool)##(_ left: Int, _ right: Int) -> Bool#}[')'][#Void#]; // CLOSURE_PARAM_WITH_PARENS: End completions func sortWithOptionalClosureType(callback: ((_ left: Int, _ right: Int) -> Bool)?) {} sortWithOptionalClosureType(#^OPTIONAL_CLOSURE_PARAM^#) // OPTIONAL_CLOSURE_PARAM: Begin completions, 1 item -// OPTIONAL_CLOSURE_PARAM-DAG: Decl[FreeFunction]/Local: ['(']{#callback: ((Int, Int) -> Bool)?##(_ left: Int, _ right: Int) -> Bool#}[')'][#Void#]; +// OPTIONAL_CLOSURE_PARAM-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]: ['(']{#callback: ((Int, Int) -> Bool)?##(_ left: Int, _ right: Int) -> Bool#}[')'][#Void#]; // OPTIONAL_CLOSURE_PARAM: End completions func sortWithEscapingClosureType(callback: @escaping (_ left: Int, _ right: Int) -> Bool) {} sortWithEscapingClosureType(#^ESCAPING_OPTIONAL_CLOSURE_PARAM^#) // ESCAPING_OPTIONAL_CLOSURE_PARAM: Begin completions, 1 item -// ESCAPING_OPTIONAL_CLOSURE_PARAM-DAG: Decl[FreeFunction]/Local: ['(']{#callback: (Int, Int) -> Bool##(_ left: Int, _ right: Int) -> Bool#}[')'][#Void#]; +// ESCAPING_OPTIONAL_CLOSURE_PARAM-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]: ['(']{#callback: (Int, Int) -> Bool##(_ left: Int, _ right: Int) -> Bool#}[')'][#Void#]; // ESCAPING_OPTIONAL_CLOSURE_PARAM: End completions } @@ -857,7 +902,7 @@ func testClosurePlaceholderPrintsTypesOnlyIfNoInternalParameterNamesExist() { func sort(callback: (Int, Int) -> Bool) {} sort(#^COMPLETE_CLOSURE_PARAM_WITHOUT_INTERNAL_NAMES^#) // COMPLETE_CLOSURE_PARAM_WITHOUT_INTERNAL_NAMES: Begin completions, 1 item -// COMPLETE_CLOSURE_PARAM_WITHOUT_INTERNAL_NAMES-DAG: Decl[FreeFunction]/Local: ['(']{#callback: (Int, Int) -> Bool##(Int, Int) -> Bool#}[')'][#Void#]; +// COMPLETE_CLOSURE_PARAM_WITHOUT_INTERNAL_NAMES-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]: ['(']{#callback: (Int, Int) -> Bool##(Int, Int) -> Bool#}[')'][#Void#]; // COMPLETE_CLOSURE_PARAM_WITHOUT_INTERNAL_NAMES: End completions } @@ -876,17 +921,17 @@ func testCompleteLabelAfterVararg() { private func test(value: Rdar76355192) { value.test("test", xArg: #^COMPLETE_AFTER_VARARG^#) // COMPLETE_AFTER_VARARG: Begin completions - // COMPLETE_AFTER_VARARG-NOT: Pattern/ExprSpecific: {#yArg: Foo...#}[#Foo#]; - // COMPLETE_AFTER_VARARG-NOT: Pattern/ExprSpecific: {#zArg: Foo...#}[#Foo#]; + // COMPLETE_AFTER_VARARG-NOT: Pattern/Local/Flair[ArgLabels]: {#yArg: Foo...#}[#Foo#]; + // COMPLETE_AFTER_VARARG-NOT: Pattern/Local/Flair[ArgLabels]: {#zArg: Foo...#}[#Foo#]; // COMPLETE_AFTER_VARARG: End completions value.test("test", xArg: .bar, #^COMPLETE_AFTER_VARARG_WITH_PREV_PARAM^#) // COMPLETE_AFTER_VARARG_WITH_PREV_PARAM: Begin completions - // COMPLETE_AFTER_VARARG_WITH_PREV_PARAM-DAG: Pattern/ExprSpecific: {#yArg: Foo...#}[#Foo#]; - // COMPLETE_AFTER_VARARG_WITH_PREV_PARAM-DAG: Pattern/ExprSpecific: {#zArg: Foo...#}[#Foo#]; + // COMPLETE_AFTER_VARARG_WITH_PREV_PARAM-DAG: Pattern/Local/Flair[ArgLabels]: {#yArg: Foo...#}[#Foo#]; + // COMPLETE_AFTER_VARARG_WITH_PREV_PARAM-DAG: Pattern/Local/Flair[ArgLabels]: {#zArg: Foo...#}[#Foo#]; // COMPLETE_AFTER_VARARG_WITH_PREV_PARAM: End completions value.test("test", xArg: .bar, .#^COMPLETE_MEMBER_IN_VARARG^#) // COMPLETE_MEMBER_IN_VARARG: Begin completions, 2 items - // COMPLETE_MEMBER_IN_VARARG-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#Foo#]; + // COMPLETE_MEMBER_IN_VARARG-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#Foo#]; // COMPLETE_MEMBER_IN_VARARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#]; // COMPLETE_MEMBER_IN_VARARG: End completions } @@ -899,25 +944,25 @@ func testCompleteLabelAfterVararg() { value.test(foo, #^COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG^#) // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG: Begin completions // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: foo[#Foo#]; - // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG-DAG: Pattern/ExprSpecific: {#yArg: Baz#}[#Baz#]; + // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG-DAG: Pattern/Local/Flair[ArgLabels]: {#yArg: Baz#}[#Baz#]; // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG: End completions // The leading dot completion tests that have picked the right type for the argument value.test(foo, .#^COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG_DOT^#) // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG_DOT: Begin completions, 2 items - // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG_DOT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#Foo#]; + // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG_DOT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#Foo#]; // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG_DOT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#]; // COMPLETE_VARARG_FOLLOWED_BY_NORMAL_ARG_DOT: End completions value.test(foo, yArg: #^COMPLETE_ARG_AFTER_VARARG^#) // COMPLETE_ARG_AFTER_VARARG: Begin completions // COMPLETE_ARG_AFTER_VARARG-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: baz[#Baz#]; - // COMPLETE_ARG_AFTER_VARARG-NOT: Pattern/ExprSpecific: {#yArg: Baz#}[#Baz#]; + // COMPLETE_ARG_AFTER_VARARG-NOT: Pattern/Local/Flair[ArgLabels]: {#yArg: Baz#}[#Baz#]; // COMPLETE_ARG_AFTER_VARARG: End completions value.test(foo, yArg: .#^COMPLETE_ARG_AFTER_VARARG_DOT^#) // COMPLETE_ARG_AFTER_VARARG_DOT: Begin completions, 2 items - // COMPLETE_ARG_AFTER_VARARG_DOT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bazCase[#Baz#]; + // COMPLETE_ARG_AFTER_VARARG_DOT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bazCase[#Baz#]; // COMPLETE_ARG_AFTER_VARARG_DOT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Baz#})[#(into: inout Hasher) -> Void#]; // COMPLETE_ARG_AFTER_VARARG_DOT: End completions } @@ -930,6 +975,43 @@ func testGenericConstructor() { _ = TextField(label: "Encoded", #^GENERIC_INITIALIZER^#) // GENERIC_INITIALIZER: Begin completions, 1 item -// GENERIC_INITIALIZER-DAG: Pattern/ExprSpecific: {#text: String#}[#String#] +// GENERIC_INITIALIZER-DAG: Pattern/Local/Flair[ArgLabels]: {#text: String#}[#String#] // GENERIC_INITIALIZER: End completions } + +struct Rdar77867723 { + enum Horizontal { case east, west } + enum Vertical { case up, down } + func fn(aaa: Horizontal = .east, bbb: Vertical = .up) {} + func fn(ccc: Vertical = .up, ddd: Horizontal = .west) {} + func test1 { + self.fn(ccc: .up, #^OVERLOAD_LABEL1^#) +// OVERLOAD_LABEL1: Begin completions, 1 items +// OVERLOAD_LABEL1-DAG: Pattern/Local/Flair[ArgLabels]: {#ddd: Horizontal#}[#Horizontal#]; +// OVERLOAD_LABEL1: End completions + } + func test2 { + self.fn(eee: .up, #^OVERLOAD_LABEL2^#) +// OVERLOAD_LABEL2: Begin completions, 2 items +// OVERLOAD_LABEL2-DAG: Pattern/Local/Flair[ArgLabels]: {#bbb: Vertical#}[#Vertical#]; +// OVERLOAD_LABEL2-DAG: Pattern/Local/Flair[ArgLabels]: {#ddd: Horizontal#}[#Horizontal#]; +// OVERLOAD_LABEL2: End completions + } +} + +struct SR14737 { + init(arg1: T, arg2: Bool) {} +} +extension SR14737 where T == Int { + init(arg1: T, onlyInt: Bool) {} +} +func test_SR14737() { + invalidCallee { + SR14737(arg1: true, #^GENERIC_INIT_IN_INVALID^#) +// FIXME: 'onlyInt' shouldn't be offered because 'arg1' is Bool. +// GENERIC_INIT_IN_INVALID: Begin completions, 2 items +// GENERIC_INIT_IN_INVALID-DAG: Pattern/Local/Flair[ArgLabels]: {#arg2: Bool#}[#Bool#]; +// GENERIC_INIT_IN_INVALID-DAG: Pattern/Local/Flair[ArgLabels]: {#onlyInt: Bool#}[#Bool#]; +// GENERIC_INIT_IN_INVALID: End completions + } +} diff --git a/test/IDE/complete_call_as_function.swift b/test/IDE/complete_call_as_function.swift index 20e588b3a40b4..0ce66ae630564 100644 --- a/test/IDE/complete_call_as_function.swift +++ b/test/IDE/complete_call_as_function.swift @@ -22,7 +22,7 @@ func testCallAsFunction(add: Adder, addTy: Adder.Type) { let _ = add#^INSTANCE_NO_DOT^#; // INSTANCE_NO_DOT: Begin completions, 3 items // INSTANCE_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal: .callAsFunction({#x: Int#}, {#y: Int#})[#Int#]; -// INSTANCE_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal: ({#x: Int#}, {#y: Int#})[#Int#]; +// INSTANCE_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#x: Int#}, {#y: Int#})[#Int#]; // INSTANCE_NO_DOT-DAG: Keyword[self]/CurrNominal: .self[#Adder#]; // INSTANCE_NO_DOT: End completions @@ -34,12 +34,12 @@ func testCallAsFunction(add: Adder, addTy: Adder.Type) { let _ = add(#^INSTANCE_PAREN^#) // INSTANCE_PAREN: Begin completions, 1 items -// INSTANCE_PAREN-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#x: Int#}, {#y: Int#}[')'][#Int#]; +// INSTANCE_PAREN-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#x: Int#}, {#y: Int#}[')'][#Int#]; // INSTANCE_PAREN: End completions let _ = add(x: 12, #^INSTANCE_ARG2^#) // INSTANCE_ARG2: Begin completions, 1 items -// INSTANCE_ARG2: Pattern/ExprSpecific: {#y: Int#}[#Int#]; +// INSTANCE_ARG2: Pattern/Local/Flair[ArgLabels]: {#y: Int#}[#Int#]; // INSTANCE_ARG2: End completions let _ = addTy#^METATYPE_NO_DOT^#; @@ -67,7 +67,7 @@ func testCallAsFunction(add: Adder, addTy: Adder.Type) { let _ = Adder#^TYPEEXPR_NO_DOT^#; // TYPEEXPR_NO_DOT: Begin completions, 4 items // TYPEEXPR_NO_DOT-NOT: {#x: Int#}, {#y: Int#} -// TYPEEXPR_NO_DOT-DAG: Decl[Constructor]/CurrNominal: ({#base: Int#})[#Adder#]; name=(base: Int) +// TYPEEXPR_NO_DOT-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#base: Int#})[#Adder#]; // TYPEEXPR_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal: .callAsFunction({#(self): Adder#})[#(x: Int, y: Int) -> Int#]; // TYPEEXPR_NO_DOT-DAG: Keyword[self]/CurrNominal: .self[#Adder.Type#]; // TYPEEXPR_NO_DOT-DAG: Keyword/CurrNominal: .Type[#Adder.Type#]; @@ -85,7 +85,7 @@ func testCallAsFunction(add: Adder, addTy: Adder.Type) { let _ = Adder(#^TYPEEXPR_PAREN^#) // TYPEEXPR_PAREN: Begin completions, 1 items // TYPEEXPR_PAREN-NOT: {#x: Int#}, {#y: Int#} -// TYPEEXPR_PAREN-DAG: Decl[Constructor]/CurrNominal: ['(']{#base: Int#}[')'][#Adder#]; +// TYPEEXPR_PAREN-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#base: Int#}[')'][#Adder#]; // TYPEEXPR_PAREN: End completions } @@ -98,19 +98,19 @@ struct Functor { func testCallAsFunctionOverloaded(fn: Functor) { fn(#^OVERLOADED_PAREN^#) //OVERLOADED_PAREN: Begin completions, 2 items -//OVERLOADED_PAREN-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#h: Functor.Horizontal#}, {#v: Functor.Vertical#}[')'][#Void#]; -//OVERLOADED_PAREN-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#v: Functor.Vertical#}, {#h: Functor.Horizontal#}[')'][#Void#]; +//OVERLOADED_PAREN-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#h: Functor.Horizontal#}, {#v: Functor.Vertical#}[')'][#Void#]; +//OVERLOADED_PAREN-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#v: Functor.Vertical#}, {#h: Functor.Horizontal#}[')'][#Void#]; //OVERLOADED_PAREN: End completions fn(h: .left, #^OVERLOADED_ARG2_LABEL^#) //OVERLOADED_ARG2_LABEL: Begin completions, 1 item -//OVERLOADED_ARG2_LABEL-DAG: Pattern/ExprSpecific: {#v: Functor.Vertical#}[#Functor.Vertical#]; +//OVERLOADED_ARG2_LABEL-DAG: Pattern/Local/Flair[ArgLabels]: {#v: Functor.Vertical#}[#Functor.Vertical#]; //OVERLOADED_ARG2_LABEL: End completions fn(h: .left, v: .#^OVERLOADED_ARG2_VALUE^#) //OVERLOADED_ARG2_VALUE: Begin completions, 3 items -//OVERLOADED_ARG2_VALUE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: up[#Functor.Vertical#]; -//OVERLOADED_ARG2_VALUE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: down[#Functor.Vertical#]; +//OVERLOADED_ARG2_VALUE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: up[#Functor.Vertical#]; +//OVERLOADED_ARG2_VALUE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: down[#Functor.Vertical#]; //OVERLOADED_ARG2_VALUE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Functor.Vertical#})[#(into: inout Hasher) -> Void#]; //OVERLOADED_ARG2_VALUE: End completions } diff --git a/test/IDE/complete_call_pattern_heuristics.swift b/test/IDE/complete_call_pattern_heuristics.swift index 7b1958646a413..5e6cc1387eba4 100644 --- a/test/IDE/complete_call_pattern_heuristics.swift +++ b/test/IDE/complete_call_pattern_heuristics.swift @@ -27,7 +27,7 @@ func testConstructor() { // CONSTRUCTOR: Begin completions // CONSTRUCTOR-NOT: Pattern/{{.*}} // CONSTRUCTOR-NOT: Decl[Constructor] -// CONSTRUCTOR: Pattern/ExprSpecific: {#a: Int#}[#Int#] +// CONSTRUCTOR: Pattern/Local/Flair[ArgLabels]: {#a: Int#}[#Int#] // CONSTRUCTOR-NOT: Pattern/{{.*}} // CONSTRUCTOR-NOT: Decl[Constructor] // CONSTRUCTOR: End completions @@ -38,7 +38,7 @@ func testArg2Name3() { firstArg(#^LABELED_FIRSTARG^#, // LABELED_FIRSTARG: Begin completions // LABELED_FIRSTARG-NOT: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#]; -// LABELED_FIRSTARG-DAG: Pattern/ExprSpecific: {#arg1: Int#}[#Int#]; +// LABELED_FIRSTARG-DAG: Pattern/Local/Flair[ArgLabels]: {#arg1: Int#}[#Int#]; // LABELED_FIRSTARG-NOT: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#]; // LABELED_FIRSTARG: End completions diff --git a/test/IDE/complete_concurrency_keyword.swift b/test/IDE/complete_concurrency_keyword.swift index 5fc6fb4baae45..3786a25933047 100644 --- a/test/IDE/complete_concurrency_keyword.swift +++ b/test/IDE/complete_concurrency_keyword.swift @@ -22,7 +22,7 @@ enum Namespace { func testFunc() { #^STMT^# // STMT: Begin completions -// STMT-DAG: Keyword/None: actor; name=actor +// STMT-DAG: Keyword/None/Flair[RareKeyword]: actor; name=actor // STMT-DAG: Keyword/None: await; name=await // STMT: End completion } diff --git a/test/IDE/complete_concurrency_specifier.swift b/test/IDE/complete_concurrency_specifier.swift index bd9a15f10e844..81feb4925ff2b 100644 --- a/test/IDE/complete_concurrency_specifier.swift +++ b/test/IDE/complete_concurrency_specifier.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -enable-experimental-concurrency +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t // REQUIRES: concurrency @@ -43,3 +43,12 @@ _ = { () #^CLOSURE_WITHARROW?check=SPECIFIER^# -> Void in } _ = { () async #^CLOSURE_WITHASYNCARROW?check=SPECIFIER_WITHASYNC^# -> Void in } _ = { () throws #^CLOSURE_WITHTHROWSARROW?check=SPECIFIER_WITHTHROWS^# -> Void in } _ = { () async throws #^CLOSURE_WITHASYNCTHROWSARROW?check=SPECIFIER_WITHASYNCTHROWS^# -> Void in } + +_ = { arg #^CLOSURE2?check=SPECIFIER^# in } +_ = { arg async #^CLOSURE2_WITHASYNC?check=SPECIFIER_WITHASYNC^# in } +_ = { arg throws #^CLOSURE2_WITHTHROWS?check=SPECIFIER_WITHTHROWS^# in } +_ = { arg async throws #^CLOSURE2_WITHAASYNCTHROWS?check=SPECIFIER_WITHASYNCTHROWS^# in } +_ = { arg #^CLOSURE2_WITHARROW?check=SPECIFIER^# -> Void in } +_ = { arg async #^CLOSURE2_WITHASYNCARROW?check=SPECIFIER_WITHASYNC^# -> Void in } +_ = { arg throws #^CLOSURE2_WITHTHROWSARROW?check=SPECIFIER_WITHTHROWS^# -> Void in } +_ = { arg async throws #^CLOSURE2_WITHASYNCTHROWSARROW?check=SPECIFIER_WITHASYNCTHROWS^# -> Void in } diff --git a/test/IDE/complete_constrained.swift b/test/IDE/complete_constrained.swift index f25b9c9150eea..60f722dc08a8d 100644 --- a/test/IDE/complete_constrained.swift +++ b/test/IDE/complete_constrained.swift @@ -64,11 +64,11 @@ func foo(s: MyStruct) { let _ = s.#^MYSTRUCT_INT_DOT^# // MYSTRUCT_INT_DOT: Begin completions, 7 items // MYSTRUCT_INT_DOT-DAG: Keyword[self]/CurrNominal: self[#MyStruct#]; name=self -// MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: methodWithConstrainedGenericParam({#x: SomeProto#})[#Int#]; name=methodWithConstrainedGenericParam(x: SomeProto) +// MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: methodWithConstrainedGenericParam({#x: SomeProto#})[#Int#]; name=methodWithConstrainedGenericParam(x:) // MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: concreteExt_TEqInt_None()[#Int#]; name=concreteExt_TEqInt_None() -// MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: concreteExt_None_TEqInt({#(x): U#})[#Int#]; name=concreteExt_None_TEqInt(x: U) +// MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: concreteExt_None_TEqInt({#(x): U#})[#Int#]; name=concreteExt_None_TEqInt(:) // MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: protoExt_AssocEqInt_None()[#Int#]; name=protoExt_AssocEqInt_None() -// MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: protoExt_None_AssocEqInt({#(x): U#})[#Int#]; name=protoExt_None_AssocEqInt(x: U) +// MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: protoExt_None_AssocEqInt({#(x): U#})[#Int#]; name=protoExt_None_AssocEqInt(:) // MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: conditional_Int()[#Int#]; name=conditional_Int() // MYSTRUCT_INT_DOT: End completions @@ -77,14 +77,14 @@ func foo(s: MyStruct) { // META_MYSTRUCT_INT_DOT-DAG: Keyword[self]/CurrNominal: self[#MyStruct.Type#]; name=self // META_MYSTRUCT_INT_DOT-DAG: Keyword/CurrNominal: Type[#MyStruct.Type#]; name=Type // META_MYSTRUCT_INT_DOT-DAG: Decl[TypeAlias]/CurrNominal: Assoc[#T#]; name=Assoc -// META_MYSTRUCT_INT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#int: U#})[#MyStruct#]; name=init(int: U) -// META_MYSTRUCT_INT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#withConstrainedGenericParam: SomeProto#})[#MyStruct#]; name=init(withConstrainedGenericParam: SomeProto) -// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: methodWithConstrainedGenericParam({#(self): MyStruct#})[#(x: SomeProto) -> Int#]; name=methodWithConstrainedGenericParam(self: MyStruct) -// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: concreteExt_TEqInt_None({#(self): MyStruct#})[#() -> Int#]; name=concreteExt_TEqInt_None(self: MyStruct) -// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: concreteExt_None_TEqInt({#(self): MyStruct#})[#(U) -> Int#]; name=concreteExt_None_TEqInt(self: MyStruct) -// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: protoExt_AssocEqInt_None({#(self): MyStruct#})[#() -> Int#]; name=protoExt_AssocEqInt_None(self: MyStruct) -// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: protoExt_None_AssocEqInt({#(self): MyStruct#})[#(U) -> Int#]; name=protoExt_None_AssocEqInt(self: MyStruct) -// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: conditional_Int({#(self): MyStruct#})[#() -> Int#]; name=conditional_Int(self: MyStruct) +// META_MYSTRUCT_INT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#int: U#})[#MyStruct#]; name=init(int:) +// META_MYSTRUCT_INT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#withConstrainedGenericParam: SomeProto#})[#MyStruct#]; name=init(withConstrainedGenericParam:) +// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: methodWithConstrainedGenericParam({#(self): MyStruct#})[#(x: SomeProto) -> Int#]; name=methodWithConstrainedGenericParam(:) +// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: concreteExt_TEqInt_None({#(self): MyStruct#})[#() -> Int#]; name=concreteExt_TEqInt_None(:) +// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: concreteExt_None_TEqInt({#(self): MyStruct#})[#(U) -> Int#]; name=concreteExt_None_TEqInt(:) +// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: protoExt_AssocEqInt_None({#(self): MyStruct#})[#() -> Int#]; name=protoExt_AssocEqInt_None(:) +// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: protoExt_None_AssocEqInt({#(self): MyStruct#})[#(U) -> Int#]; name=protoExt_None_AssocEqInt(:) +// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/Super: conditional_Int({#(self): MyStruct#})[#() -> Int#]; name=conditional_Int(:) // META_MYSTRUCT_INT_DOT: End completions } @@ -118,10 +118,10 @@ struct Vegetarian: EatsFruit, EatsVegetables { } func testVegetarian(chef: Chef) { chef.cook(.#^CONDITIONAL_OVERLOAD_ARG^#) // CONDITIONAL_OVERLOAD_ARG: Begin completions, 4 items -// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: apple[#Fruit#]; name=apple -// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Fruit#})[#(into: inout Hasher) -> Void#]; name=hash(self: Fruit) -// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: broccoli[#Vegetable#]; name=broccoli -// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Vegetable#})[#(into: inout Hasher) -> Void#]; name=hash(self: Vegetable) +// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: apple[#Fruit#]; name=apple +// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Fruit#})[#(into: inout Hasher) -> Void#]; name=hash(:) +// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: broccoli[#Vegetable#]; name=broccoli +// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Vegetable#})[#(into: inout Hasher) -> Void#]; name=hash(:) // CONDITIONAL_OVERLOAD_ARG: End completions var chefMeta: Chef.Type = Chef.self @@ -131,8 +131,8 @@ func testVegetarian(chef: Chef) { // Note: 'eat' is from an inapplicable constrained extension. We complete as if the user intends to addess that later // (e.g. by adding the missing 'Meat' conformance to 'Vegetarian' - clearly not the intention here - but replace 'Meat' with 'Equatable'). // CONDITIONAL_INAPPLICABLE_ARG: Begin completions, 2 items -// CONDITIONAL_INAPPLICABLE_ARG-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: chicken[#Meat#]; name=chicken -// CONDITIONAL_INAPPLICABLE_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Meat#})[#(into: inout Hasher) -> Void#]; name=hash(self: Meat) +// CONDITIONAL_INAPPLICABLE_ARG-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: chicken[#Meat#]; name=chicken +// CONDITIONAL_INAPPLICABLE_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Meat#})[#(into: inout Hasher) -> Void#]; name=hash(:) // CONDITIONAL_INAPPLICABLE_ARG: End completions } diff --git a/test/IDE/complete_constructor.swift b/test/IDE/complete_constructor.swift index 59f85263bf690..3457516e33f73 100644 --- a/test/IDE/complete_constructor.swift +++ b/test/IDE/complete_constructor.swift @@ -13,7 +13,7 @@ struct ImplicitConstructors1 { func testImplicitConstructors1() { ImplicitConstructors1#^IMPLICIT_CONSTRUCTORS_1^# // IMPLICIT_CONSTRUCTORS_1: Begin completions, 3 items -// IMPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal: ()[#ImplicitConstructors1#]{{; name=.+$}} +// IMPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#ImplicitConstructors1#]{{; name=.+$}} // IMPLICIT_CONSTRUCTORS_1-DAG: Keyword[self]/CurrNominal: .self[#ImplicitConstructors1.Type#]; name=self // IMPLICIT_CONSTRUCTORS_1-DAG: Keyword/CurrNominal: .Type[#ImplicitConstructors1.Type#]; name=Type // IMPLICIT_CONSTRUCTORS_1: End completions @@ -21,7 +21,7 @@ func testImplicitConstructors1() { func testImplicitConstructors1P() { ImplicitConstructors1(#^IMPLICIT_CONSTRUCTORS_1P^# // IMPLICIT_CONSTRUCTORS_1P: Begin completions, 1 items -// IMPLICIT_CONSTRUCTORS_1P: Decl[Constructor]/CurrNominal: ['('][')'][#ImplicitConstructors1#]; name= +// IMPLICIT_CONSTRUCTORS_1P: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#ImplicitConstructors1#]; name= // IMPLICIT_CONSTRUCTORS_1P: End completions } @@ -32,9 +32,9 @@ struct ImplicitConstructors2 { func testImplicitConstructors2() { ImplicitConstructors2#^IMPLICIT_CONSTRUCTORS_2^# // IMPLICIT_CONSTRUCTORS_2: Begin completions, 5 items -// IMPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ()[#ImplicitConstructors2#]{{; name=.+$}} -// IMPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ({#instanceVar: Int#})[#ImplicitConstructors2#]{{; name=.+$}} -// IMPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ()[#ImplicitConstructors2#]{{; name=.+$}} +// IMPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#ImplicitConstructors2#]{{; name=.+$}} +// IMPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#instanceVar: Int#})[#ImplicitConstructors2#]{{; name=.+$}} +// IMPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#ImplicitConstructors2#]{{; name=.+$}} // IMPLICIT_CONSTRUCTORS_2-DAG: Keyword[self]/CurrNominal: .self[#ImplicitConstructors2.Type#]; name=self // IMPLICIT_CONSTRUCTORS_2-DAG: Keyword/CurrNominal: .Type[#ImplicitConstructors2.Type#]; name=Type // IMPLICIT_CONSTRUCTORS_2: End completions @@ -42,9 +42,9 @@ func testImplicitConstructors2() { func testImplicitConstructors2P() { ImplicitConstructors2(#^IMPLICIT_CONSTRUCTORS_2P^# // IMPLICIT_CONSTRUCTORS_2P: Begin completions, 3 items -// IMPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal: ['('][')'][#ImplicitConstructors2#]; name= -// IMPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal: ['(']{#instanceVar: Int#}[')'][#ImplicitConstructors2#]{{; name=.+$}} -// IMPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal: ['('][')'][#ImplicitConstructors2#]; name= +// IMPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#ImplicitConstructors2#]; name= +// IMPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#instanceVar: Int#}[')'][#ImplicitConstructors2#]{{; name=.+$}} +// IMPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#ImplicitConstructors2#]; name= // IMPLICIT_CONSTRUCTORS_2P: End completions } @@ -57,9 +57,9 @@ struct ExplicitConstructors1 { func testExplicitConstructors1() { ExplicitConstructors1#^EXPLICIT_CONSTRUCTORS_1^# // EXPLICIT_CONSTRUCTORS_1: Begin completions, 5 items -// EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal: ()[#ExplicitConstructors1#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#})[#ExplicitConstructors1#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#}, {#b: Float#})[#ExplicitConstructors1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#ExplicitConstructors1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#a: Int#})[#ExplicitConstructors1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#a: Int#}, {#b: Float#})[#ExplicitConstructors1#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_1-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructors1.Type#]; name=self // EXPLICIT_CONSTRUCTORS_1-DAG: Keyword/CurrNominal: .Type[#ExplicitConstructors1.Type#]; name=Type // EXPLICIT_CONSTRUCTORS_1: End completions @@ -67,18 +67,18 @@ func testExplicitConstructors1() { func testExplicitConstructors1P() { ExplicitConstructors1(#^EXPLICIT_CONSTRUCTORS_1P^# // EXPLICIT_CONSTRUCTORS_1P: Begin completions -// EXPLICIT_CONSTRUCTORS_1P-NEXT: Decl[Constructor]/CurrNominal: ['('][')'][#ExplicitConstructors1#]; name= -// EXPLICIT_CONSTRUCTORS_1P-NEXT: Decl[Constructor]/CurrNominal: ['(']{#a: Int#}[')'][#ExplicitConstructors1#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_1P-NEXT: Decl[Constructor]/CurrNominal: ['(']{#a: Int#}, {#b: Float#}[')'][#ExplicitConstructors1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_1P-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#ExplicitConstructors1#]; name= +// EXPLICIT_CONSTRUCTORS_1P-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}[')'][#ExplicitConstructors1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_1P-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}, {#b: Float#}[')'][#ExplicitConstructors1#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_1P-NEXT: End completions } ExplicitConstructors1#^EXPLICIT_CONSTRUCTORS_2^# // EXPLICIT_CONSTRUCTORS_2: Begin completions, 5 items -// EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ()[#ExplicitConstructors1#] -// EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#})[#ExplicitConstructors1#] -// EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#}, {#b: Float#})[#ExplicitConstructors1#] +// EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#ExplicitConstructors1#] +// EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#a: Int#})[#ExplicitConstructors1#] +// EXPLICIT_CONSTRUCTORS_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#a: Int#}, {#b: Float#})[#ExplicitConstructors1#] // EXPLICIT_CONSTRUCTORS_2-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructors1.Type#]; name=self // EXPLICIT_CONSTRUCTORS_2-DAG: Keyword/CurrNominal: .Type[#ExplicitConstructors1.Type#]; name=Type // EXPLICIT_CONSTRUCTORS_2: End completions @@ -86,8 +86,8 @@ ExplicitConstructors1#^EXPLICIT_CONSTRUCTORS_2^# ExplicitConstructors1(#^EXPLICIT_CONSTRUCTORS_2P^# // EXPLICIT_CONSTRUCTORS_2P: Begin completions -// EXPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal: ['(']{#a: Int#}[')'][#ExplicitConstructors1#] -// EXPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal: ['(']{#a: Int#}, {#b: Float#}[')'][#ExplicitConstructors1#] +// EXPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}[')'][#ExplicitConstructors1#] +// EXPLICIT_CONSTRUCTORS_2P-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}, {#b: Float#}[')'][#ExplicitConstructors1#] // EXPLICIT_CONSTRUCTORS_2P: End completions @@ -100,8 +100,8 @@ struct ExplicitConstructors3 { func testExplicitConstructors3P() { ExplicitConstructors3(#^EXPLICIT_CONSTRUCTORS_3P^# // EXPLICIT_CONSTRUCTORS_3P: Begin completions -// EXPLICIT_CONSTRUCTORS_3P-DAG: Decl[Constructor]/CurrNominal: ['(']{#(a): Int#}[')'][#ExplicitConstructors3#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_3P-DAG: Decl[Constructor]/CurrNominal: ['(']{#a: Int#}, {#b: Float#}[')'][#ExplicitConstructors3#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_3P-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#(a): Int#}[')'][#ExplicitConstructors3#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_3P-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}, {#b: Float#}[')'][#ExplicitConstructors3#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_3P: End completions } @@ -114,8 +114,8 @@ struct ExplicitConstructorsSelector1 { func testExplicitConstructorsSelector1() { ExplicitConstructorsSelector1#^EXPLICIT_CONSTRUCTORS_SELECTOR_1^# // EXPLICIT_CONSTRUCTORS_SELECTOR_1: Begin completions, 4 items -// EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Decl[Constructor]/CurrNominal: ({#int: Int#})[#ExplicitConstructorsSelector1#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Decl[Constructor]/CurrNominal: ({#int: Int#}, {#andFloat: Float#})[#ExplicitConstructorsSelector1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#int: Int#})[#ExplicitConstructorsSelector1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#int: Int#}, {#andFloat: Float#})[#ExplicitConstructorsSelector1#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructorsSelector1.Type#]; name=self // EXPLICIT_CONSTRUCTORS_SELECTOR_1-DAG: Keyword/CurrNominal: .Type[#ExplicitConstructorsSelector1.Type#]; name=Type // EXPLICIT_CONSTRUCTORS_SELECTOR_1: End completions @@ -142,10 +142,10 @@ struct ExplicitConstructorsSelector2 { func testExplicitConstructorsSelector2() { ExplicitConstructorsSelector2#^EXPLICIT_CONSTRUCTORS_SELECTOR_2^# // EXPLICIT_CONSTRUCTORS_SELECTOR_2: Begin completions, 6 items -// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal: ({#noArgs: ()#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal: ({#(a): Int#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal: ({#(a): Int#}, {#withFloat: Float#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal: ({#int: Int#}, {#(b): Float#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#noArgs: ()#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#(a): Int#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#(a): Int#}, {#withFloat: Float#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#int: Int#}, {#(b): Float#})[#ExplicitConstructorsSelector2#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructorsSelector2.Type#]; name=self // EXPLICIT_CONSTRUCTORS_SELECTOR_2-DAG: Keyword/CurrNominal: .Type[#ExplicitConstructorsSelector2.Type#]; name=Type // EXPLICIT_CONSTRUCTORS_SELECTOR_2: End completions @@ -173,8 +173,8 @@ func testExplicitConstructorsBaseDerived1() { ExplicitConstructorsDerived1#^EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1^# } // EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1: Begin completions, 4 items -// EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Decl[Constructor]/CurrNominal: ()[#ExplicitConstructorsDerived1#]{{; name=.+$}} -// EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#})[#ExplicitConstructorsDerived1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#ExplicitConstructorsDerived1#]{{; name=.+$}} +// EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#a: Int#})[#ExplicitConstructorsDerived1#]{{; name=.+$}} // EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Keyword[self]/CurrNominal: .self[#ExplicitConstructorsDerived1.Type#]; name=self // EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1-DAG: Keyword/CurrNominal: .Type[#ExplicitConstructorsDerived1.Type#]; name=Type // EXPLICIT_CONSTRUCTORS_BASE_DERIVED_1: End completions @@ -218,7 +218,7 @@ func testGetInitFromMetatype4() { type(of: a).#^INIT_FROM_METATYPE4^# } -// INIT_FROM_METATYPE4: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ExplicitConstructorsDerived2#]; name=init(a: Int) +// INIT_FROM_METATYPE4: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ExplicitConstructorsDerived2#]{{; name=.+$}} // INIT_FROM_METATYPE4-NOT: Decl[Constructor]/CurrNominal: init()[#ExplicitConstructorsDerived2#]{{; name=.+$}} struct ExplicitConstructorsDerived3 { @@ -234,16 +234,16 @@ struct ExplicitConstructorsDerived3 { func testHaveRParen1() { ImplicitConstructors1(#^HAVE_RPAREN_1^#) // HAVE_RPAREN_1: Begin completions, 1 items -// HAVE_RPAREN_1: Decl[Constructor]/CurrNominal: ['('][')'][#ImplicitConstructors1#]; name= +// HAVE_RPAREN_1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#ImplicitConstructors1#]; name= // HAVE_RPAREN_1: End completions } func testHaveRParen2() { ImplicitConstructors2(#^HAVE_RPAREN_2^#) // HAVE_RPAREN_2: Begin completions, 3 items -// HAVE_RPAREN_2-DAG: Decl[Constructor]/CurrNominal: ['('][')'][#ImplicitConstructors2#]; name= -// HAVE_RPAREN_2-DAG: Decl[Constructor]/CurrNominal: ['(']{#instanceVar: Int#}[')'][#ImplicitConstructors2#]{{; name=.+$}} -// HAVE_RPAREN_2-DAG: Decl[Constructor]/CurrNominal: ['('][')'][#ImplicitConstructors2#]; name= +// HAVE_RPAREN_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#ImplicitConstructors2#]; name= +// HAVE_RPAREN_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#instanceVar: Int#}[')'][#ImplicitConstructors2#]{{; name=.+$}} +// HAVE_RPAREN_2-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#ImplicitConstructors2#]; name= // HAVE_RPAREN_2: End completions } @@ -265,8 +265,8 @@ func testHasDefaultInitFromProtocol() { ConformsToProtDefaultInit.#^DEFAULT_INIT_FROM_PROT_DOT?check=DEFAULT_INIT_FROM_PROT^# ConformsToProtDefaultInit(#^DEFAULT_INIT_FROM_PROT_PAREN?check=DEFAULT_INIT_FROM_PROT^# -// DEFAULT_INIT_FROM_PROT-DAG: Decl[Constructor]/CurrNominal: {{.+}}{#bar: Int#} -// DEFAULT_INIT_FROM_PROT-DAG: Decl[Constructor]/Super: {{.+}}{#foo: Int#} +// DEFAULT_INIT_FROM_PROT-DAG: Decl[Constructor]/CurrNominal{{.*}}: {{.+}}{#bar: Int#} +// DEFAULT_INIT_FROM_PROT-DAG: Decl[Constructor]/Super{{.*}}: {{.+}}{#foo: Int#} } class WithAlias1 { @@ -277,14 +277,14 @@ typealias Alias1 = WithAlias1 func testWithAlias1() { Alias1#^WITH_ALIAS_1^# } -// WITH_ALIAS_1: Decl[Constructor]/CurrNominal: ({#working: Int#})[#Alias1#]; +// WITH_ALIAS_1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#working: Int#})[#Alias1#]; struct ClosureInInit1 { struct S {init(_: Int) {}} var prop1: S = { return S(#^CLOSURE_IN_INIT_1^# } -// CLOSURE_IN_INIT_1: Decl[Constructor]/CurrNominal{{(/TypeRelation\[Identical\])?}}: ['(']{#Int#}[')'][#S#]; +// CLOSURE_IN_INIT_1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]{{(/TypeRelation\[Identical\])?}}: ['(']{#Int#}[')'][#S#]; var prop2: S = { return S(#^CLOSURE_IN_INIT_2?check=CLOSURE_IN_INIT_1^# }() @@ -312,9 +312,9 @@ public class AvailableTest { func testAvailable() { let _ = AvailableTest(#^AVAILABLE_1^# // AVAILABLE_1: Begin completions, 3 items -// AVAILABLE_1-DAG: Decl[Constructor]/CurrNominal: ['(']{#opt: Int#}[')'][#AvailableTest?#]; name=opt: Int -// AVAILABLE_1-DAG: Decl[Constructor]/CurrNominal: ['(']{#normal1: Int#}[')'][#AvailableTest#]; name=normal1: Int -// AVAILABLE_1-DAG: Decl[Constructor]/CurrNominal: ['(']{#normal2: Int#}[')'][#AvailableTest#]; name=normal2: Int +// AVAILABLE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#opt: Int#}[')'][#AvailableTest?#]; name=opt: +// AVAILABLE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#normal1: Int#}[')'][#AvailableTest#]; name=normal1: +// AVAILABLE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#normal2: Int#}[')'][#AvailableTest#]; name=normal2: // AVAILABLE_1: End completions let _ = AvailableTest.init(#^AVAILABLE_2?check=AVAILABLE_1^# @@ -336,16 +336,16 @@ func testDependentTypeInClosure() { let _ = DependentTypeInClosure(#^DEPENDENT_IN_CLOSURE_1^#) // DEPENDENT_IN_CLOSURE_1: Begin completions -// DEPENDENT_IN_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal: ['(']{#(arg): _#}, {#fn: (_.Content) -> Void##(_.Content) -> Void#}[')'][#DependentTypeInClosure<_>#]; -// DEPENDENT_IN_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal: ['(']{#arg: _#}, {#fn: () -> _.Content##() -> _.Content#}[')'][#DependentTypeInClosure<_>#]; +// DEPENDENT_IN_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#(arg): _#}, {#fn: (_.Content) -> Void##(_.Content) -> Void#}[')'][#DependentTypeInClosure<_>#]; +// DEPENDENT_IN_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg: _#}, {#fn: () -> _.Content##() -> _.Content#}[')'][#DependentTypeInClosure<_>#]; // DEPENDENT_IN_CLOSURE_1: End completions let _ = DependentTypeInClosure.#^DEPENDENT_IN_CLOSURE_2^# // DEPENDENT_IN_CLOSURE_2: Begin completions, 4 items // DEPENDENT_IN_CLOSURE_2-DAG: Keyword[self]/CurrNominal: self[#DependentTypeInClosure<_>.Type#]; name=self // DEPENDENT_IN_CLOSURE_2-DAG: Keyword/CurrNominal: Type[#DependentTypeInClosure<_>.Type#]; name=Type -// DEPENDENT_IN_CLOSURE_2-DAG: Decl[Constructor]/CurrNominal: init({#(arg): _#}, {#fn: (_.Content) -> Void##(_.Content) -> Void#})[#DependentTypeInClosure<_>#]; name=init(arg: _, fn: (_.Content) -> Void) -// DEPENDENT_IN_CLOSURE_2-DAG: Decl[Constructor]/CurrNominal: init({#arg: _#}, {#fn: () -> _.Content##() -> _.Content#})[#DependentTypeInClosure<_>#]; name=init(arg: _, fn: () -> _.Content) +// DEPENDENT_IN_CLOSURE_2-DAG: Decl[Constructor]/CurrNominal: init({#(arg): _#}, {#fn: (_.Content) -> Void##(_.Content) -> Void#})[#DependentTypeInClosure<_>#]; name=init(:fn:) +// DEPENDENT_IN_CLOSURE_2-DAG: Decl[Constructor]/CurrNominal: init({#arg: _#}, {#fn: () -> _.Content##() -> _.Content#})[#DependentTypeInClosure<_>#]; name=init(arg:fn:) // DEPENDENT_IN_CLOSURE_2: End completions } struct InitWithUnresolved where Data.Content: Comparable { @@ -357,7 +357,7 @@ extension InitWithUnresolved where Self.Data: Comparable { func testInitWithUnresolved() { let _ = InitWithUnresolved(#^INIT_WITH_UNRESOLVEDTYPE_1^# // INIT_WITH_UNRESOLVEDTYPE_1: Begin completions, 2 items -// INIT_WITH_UNRESOLVEDTYPE_1-DAG: Decl[Constructor]/CurrNominal: ['(']{#arg: _#}, {#fn: (_.Content) -> Void##(_.Content) -> Void#}[')'][#InitWithUnresolved<_>#]; -// INIT_WITH_UNRESOLVEDTYPE_1-DAG: Decl[Constructor]/CurrNominal: ['(']{#arg2: _#}[')'][#InitWithUnresolved<_>#]; +// INIT_WITH_UNRESOLVEDTYPE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg: _#}, {#fn: (_.Content) -> Void##(_.Content) -> Void#}[')'][#InitWithUnresolved<_>#]; +// INIT_WITH_UNRESOLVEDTYPE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg2: _#}[')'][#InitWithUnresolved<_>#]; // INIT_WITH_UNRESOLVEDTYPE_1: End completions } diff --git a/test/IDE/complete_crashes.swift b/test/IDE/complete_crashes.swift index 971b820219ae4..8bbb565f0b892 100644 --- a/test/IDE/complete_crashes.swift +++ b/test/IDE/complete_crashes.swift @@ -165,7 +165,7 @@ func rdar22834017() { Foo(#^RDAR_22834017^#) } // RDAR_22834017: Begin completions, 1 items -// RDAR_22834017: Decl[Constructor]/CurrNominal: ['(']{#a: <>#}, {#b: <>#}, {#c: <>#}[')'][#Foo#]; +// RDAR_22834017: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: <>#}, {#b: <>#}, {#c: <>#}[')'][#Foo#]; // RDAR_22834017: End completions // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RDAR_23173692 | %FileCheck %s -check-prefix=RDAR_23173692 @@ -224,8 +224,8 @@ func foo_38149042(bar: Bar_38149042) { // RDAR_38149042: Begin completions // RDAR_38149042-DAG: Decl[InstanceVar]/CurrNominal: .x[#Int#]; name=x // RDAR_38149042-DAG: Keyword[self]/CurrNominal: .self[#Baz_38149042#]; name=self -// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']=== {#AnyObject?#}[#Bool#]; name==== AnyObject? -// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']!== {#AnyObject?#}[#Bool#]; name=!== AnyObject? +// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']=== {#AnyObject?#}[#Bool#]; name==== +// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']!== {#AnyObject?#}[#Bool#]; name=!== // RDAR_38149042: End completions // rdar://problem/38272904 @@ -291,7 +291,7 @@ public final class IntStore { } } // RDAR_41232519: Begin completions -// RDAR_41232519: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']+ {#Int#}[#Int#]; name=+ Int +// RDAR_41232519: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']+ {#Int#}[#Int#]; name=+ // RDAR_41232519: End completions // rdar://problem/28188259 @@ -300,7 +300,7 @@ func test_28188259(x: ((Int) -> Void) -> Void) { x({_ in }#^RDAR_28188259^#) } // RDAR_28188259: Begin completions -// RDAR_28188259-DAG: Pattern/CurrModule: ({#Int#})[#Void#]; name=(Int) +// RDAR_28188259-DAG: Pattern/CurrModule/Flair[ArgLabels]: ({#Int#})[#Void#]; name=() // RDAR_28188259-DAG: Keyword[self]/CurrNominal: .self[#(Int) -> ()#]; name=self // RDAR_28188259: End completions @@ -389,3 +389,23 @@ struct StructWithCallAsFunction: HasCallAsFunctionRequirement { } // CRASH_CALL_AS_FUNCTION: Begin completion // CRASH_CALL_AS_FUNCTION: End completions + +// rdar://80635105 +protocol P_80635105 { + associatedtype T +} +struct S_80635105 {} +extension S_80635105 : P_80635105 {} +extension P_80635105 { + func foo(_ x: U.T) where U == Self.T {} +} + +// RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_80635105 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_80635105 +func test_80635105() { + let fn = { x in + S_80635105.#^RDAR_80635105^# + // RDAR_80635105: Begin completions + // RDAR_80635105: Decl[InstanceMethod]/Super: foo({#(self): S_80635105<_>#})[#(P_80635105.T) -> Void#]; name=foo + // RDAR_80635105: End completions + } +} diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index ba2a9f4594a97..9b7fda53c7167 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -92,6 +92,7 @@ struct MyStruct {} // KEYWORD3-NEXT: Keyword/None: usableFromInline[#Class Attribute#]; name=usableFromInline // KEYWORD3-NEXT: Keyword/None: propertyWrapper[#Class Attribute#]; name=propertyWrapper // KEYWORD3-NEXT: Keyword/None: resultBuilder[#Class Attribute#]; name=resultBuilder +// KEYWORD3-NEXT: Keyword/None: globalActor[#Class Attribute#]; name=globalActor // KEYWORD3-NEXT: End completions @#^KEYWORD3_2^#IB class C2 {} @@ -108,6 +109,7 @@ struct MyStruct {} // KEYWORD4-NEXT: Keyword/None: frozen[#Enum Attribute#]; name=frozen // KEYWORD4-NEXT: Keyword/None: propertyWrapper[#Enum Attribute#]; name=propertyWrapper // KEYWORD4-NEXT: Keyword/None: resultBuilder[#Enum Attribute#]; name=resultBuilder +// KEYWORD4-NEXT: Keyword/None: globalActor[#Enum Attribute#]; name=globalActor // KEYWORD4-NEXT: End completions @@ -121,6 +123,7 @@ struct MyStruct {} // KEYWORD5-NEXT: Keyword/None: frozen[#Struct Attribute#]; name=frozen // KEYWORD5-NEXT: Keyword/None: propertyWrapper[#Struct Attribute#]; name=propertyWrapper // KEYWORD5-NEXT: Keyword/None: resultBuilder[#Struct Attribute#]; name=resultBuilder +// KEYWORD5-NEXT: Keyword/None: globalActor[#Struct Attribute#]; name=globalActor // KEYWORD5-NEXT: End completions @#^ON_GLOBALVAR^# var globalVar diff --git a/test/IDE/complete_declname.swift b/test/IDE/complete_declname.swift index bac56ea84bab4..82361731197e3 100644 --- a/test/IDE/complete_declname.swift +++ b/test/IDE/complete_declname.swift @@ -64,7 +64,7 @@ struct MyStruct : P { typealias #^TYPEALIASNAME_CONFORMANCE^# // TYPEALIASNAME_CONFORMANCE: Begin completions, 1 items -// TYPEALIASNAME_CONFORMANCE-NEXT: Decl[AssociatedType]/Super: Assoc = {#(Type)#}; name=Assoc = Type +// TYPEALIASNAME_CONFORMANCE-NEXT: Decl[AssociatedType]/Super: Assoc = {#(Type)#}; name=Assoc = // TYPEALIASNAME_CONFORMANCE-NEXT: End completions } struct MyStruct2: P { diff --git a/test/IDE/complete_default_arguments.swift b/test/IDE/complete_default_arguments.swift index abd2197d2a23a..b47ed8bcfb45f 100644 --- a/test/IDE/complete_default_arguments.swift +++ b/test/IDE/complete_default_arguments.swift @@ -66,16 +66,16 @@ func testDefaultArgs2() { freeFuncWithDefaultArgs1#^DEFAULT_ARGS_2^# } // DEFAULT_ARGS_2: Begin completions -// DEFAULT_ARGS_2-DAG: Decl[FreeFunction]/CurrModule: ({#(a): Int#})[#Void#]{{; name=.+$}} -// DEFAULT_ARGS_2-DAG: Decl[FreeFunction]/CurrModule: ({#(a): Int#}, {#b: Int#})[#Void#]{{; name=.+$}} +// DEFAULT_ARGS_2-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ({#(a): Int#})[#Void#]{{; name=.+$}} +// DEFAULT_ARGS_2-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ({#(a): Int#}, {#b: Int#})[#Void#]{{; name=.+$}} // DEFAULT_ARGS_2: End completions func testDefaultArgs3() { freeFuncWithDefaultArgs3#^DEFAULT_ARGS_3^# } // DEFAULT_ARGS_3: Begin completions -// DEFAULT_ARGS_3-DAG: Decl[FreeFunction]/CurrModule: ()[#Void#]{{; name=.+$}} -// DEFAULT_ARGS_3-DAG: Decl[FreeFunction]/CurrModule: ({#a: Int#})[#Void#]{{; name=.+$}} +// DEFAULT_ARGS_3-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ()[#Void#]{{; name=.+$}} +// DEFAULT_ARGS_3-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ({#a: Int#})[#Void#]{{; name=.+$}} // DEFAULT_ARGS_3: End completions func testDefaultArgs4(_ x: A) { @@ -90,8 +90,8 @@ func testDefaultArgs5(_ x: A) { x.methodWithDefaultArgs1#^DEFAULT_ARGS_5^# } // DEFAULT_ARGS_5: Begin completions -// DEFAULT_ARGS_5-DAG: Decl[InstanceMethod]/CurrNominal: ()[#Void#]{{; name=.+$}} -// DEFAULT_ARGS_5-DAG: Decl[InstanceMethod]/CurrNominal: ({#a: Int#})[#Void#]{{; name=.+$}} +// DEFAULT_ARGS_5-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ()[#Void#]{{; name=.+$}} +// DEFAULT_ARGS_5-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#a: Int#})[#Void#]{{; name=.+$}} // DEFAULT_ARGS_5: End completions func testDefaultArgs6() { @@ -107,8 +107,8 @@ func testDefaultArgs7() { B#^DEFAULT_ARGS_7^# } // DEFAULT_ARGS_7: Begin completions -// DEFAULT_ARGS_7-DAG: Decl[Constructor]/CurrNominal: ()[#B#]{{; name=.+$}} -// DEFAULT_ARGS_7-DAG: Decl[Constructor]/CurrNominal: ({#a: Int#})[#B#]{{; name=.+$}} +// DEFAULT_ARGS_7-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#B#]{{; name=.+$}} +// DEFAULT_ARGS_7-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#a: Int#})[#B#]{{; name=.+$}} // DEFAULT_ARGS_7: End completions func testDefaultArgs8(_ x: C1) { diff --git a/test/IDE/complete_diagnostics.swift b/test/IDE/complete_diagnostics.swift new file mode 100644 index 0000000000000..f9f29b65ff358 --- /dev/null +++ b/test/IDE/complete_diagnostics.swift @@ -0,0 +1,112 @@ +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %empty-directory(%t/Modules) +// RUN: %target-swift-frontend -emit-module -I %t/Modules -module-name OtherModule -o %t/Modules %t/OtherModule.swift +// RUN: %target-swift-frontend -emit-module -I %t/Modules -module-name MyModule -o %t/Modules %t/MyModule.swift + +// RUN: %empty-directory(%t/output) +// RUN: %empty-directory(%t/ccp) +// RUN: %empty-directory(%t/mcp) + +// NOTE: Doing twice is to ensure that the completion cache is used. +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/App.swift -filecheck %raw-FileCheck -completion-output-dir %t/output -I %t/Modules -completion-cache-path %t/ccp -module-cache-path %t/mcp +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/App.swift -filecheck %raw-FileCheck -completion-output-dir %t/output -I %t/Modules -completion-cache-path %t/ccp -module-cache-path %t/mcp + +// BEGIN UnrelatedModule.swift + +public func declInUnRelatedModule() {} + +// BEGIN OtherModule.swift + +public func declInOtherModule() {} + +// BEGIN MyModule.swift + +@_exported import OtherModule + +public struct Foo { + @available(*, deprecated) + public func deprecatedUnconditional() {} + + @available(macOS, deprecated) + public func deprecatedPlatformUnconditional() {} + + @available(macOS, deprecated: 10.4) + public func deprecatedPlatformVersion() {} + + @available(macOS, deprecated: 10.4, message: "this is a \"message\"") + public func deprecatedPlatformVersionMessage() {} + + @available(macOS, deprecated: 10.4, renamed: "renamedName") + public func deprecatedPlatformVersionRenamed() {} + + @available(macOS, deprecated: 10.4, message: "this is a message", renamed: "renamedName") + public func deprecatedPlatformVersionMessageRenamed() {} + + @available(swift, deprecated: 3.2) + public var deprecatedVar: Int { 1 } + + @available(macOS, deprecated: 987.6) + public func softDeprecatedPlatformVersion() {} + + @available(macOS, deprecated: 765.5, message: "this is a \"message\"") + public func softDeprecatedPlatformVersionMessage() {} + + @available(swift, deprecated: 1000.1) + public var softDeprecatedVar: Int { 1 } + + @available(macOS, deprecated: 654.3, renamed: "renamedName") + public func softDeprecatedPlatformVersionRenamed() {} + + @available(macOS, deprecated: 100000.0) + public func futureDeprecatedPlatformVersion() {} + + @available(macOS, deprecated: 100000.0, message: "this is a \"message\"") + public func futureDeprecatedPlatformVersionMessage() {} + + @available(macOS, deprecated: 100000.0, renamed: "renamedName") + public func futureDeprecatedPlatformVersionRenamed() {} +} + +// BEGIN App.swift + +import MyModule +import #^IMPORT^#; +// IMPORT: Begin completions +// IMPORT-DAG: Decl[Module]/None/NotRecommended: MyModule[#Module#]; name=MyModule; diagnostics=warning:module 'MyModule' is already imported{{$}} +// IMPORT-DAG: Decl[Module]/None/NotRecommended: OtherModule[#Module#]; name=OtherModule; diagnostics=note:module 'OtherModule' is already imported via another module import{{$}} +// IMPORT-DAG: Decl[Module]/None/NotRecommended: Swift[#Module#]; name=Swift; diagnostics=warning:module 'Swift' is already imported{{$}} +// IMPORT: End completions + +func test(foo: Foo) { + foo.#^MEMBER^# +// MEMBER: Begin completions +// MEMBER-DAG: Keyword[self]/CurrNominal: self[#Foo#]; name=self +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedUnconditional()[#Void#]; name=deprecatedUnconditional(); diagnostics=warning:'deprecatedUnconditional()' is deprecated{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedPlatformUnconditional()[#Void#]; name=deprecatedPlatformUnconditional(); diagnostics=warning:'deprecatedPlatformUnconditional()' is deprecated in macOS{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedPlatformVersion()[#Void#]; name=deprecatedPlatformVersion(); diagnostics=warning:'deprecatedPlatformVersion()' was deprecated in macOS 10.4{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedPlatformVersionMessage()[#Void#]; name=deprecatedPlatformVersionMessage(); diagnostics=warning:'deprecatedPlatformVersionMessage()' was deprecated in macOS 10.4: this is a "message"{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedPlatformVersionRenamed()[#Void#]; name=deprecatedPlatformVersionRenamed(); diagnostics=warning:'deprecatedPlatformVersionRenamed()' was deprecated in macOS 10.4: renamed to 'renamedName'{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedPlatformVersionMessageRenamed()[#Void#]; name=deprecatedPlatformVersionMessageRenamed(); diagnostics=warning:'deprecatedPlatformVersionMessageRenamed()' was deprecated in macOS 10.4: this is a message{{$}} +// MEMBER-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: deprecatedVar[#Int#]; name=deprecatedVar; diagnostics=warning:'deprecatedVar' is deprecated{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: softDeprecatedPlatformVersion()[#Void#]; name=softDeprecatedPlatformVersion(); diagnostics=warning:'softDeprecatedPlatformVersion()' will be deprecated in macOS 987.6{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: softDeprecatedPlatformVersionMessage()[#Void#]; name=softDeprecatedPlatformVersionMessage(); diagnostics=warning:'softDeprecatedPlatformVersionMessage()' will be deprecated in macOS 765.5: this is a "message"{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: softDeprecatedPlatformVersionRenamed()[#Void#]; name=softDeprecatedPlatformVersionRenamed(); diagnostics=warning:'softDeprecatedPlatformVersionRenamed()' will be deprecated in macOS 654.3: renamed to 'renamedName'{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: futureDeprecatedPlatformVersion()[#Void#]; name=futureDeprecatedPlatformVersion(); diagnostics=warning:'futureDeprecatedPlatformVersion()' will be deprecated in a future version of macOS{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: futureDeprecatedPlatformVersionMessage()[#Void#]; name=futureDeprecatedPlatformVersionMessage(); diagnostics=warning:'futureDeprecatedPlatformVersionMessage()' will be deprecated in a future version of macOS: this is a "message"{{$}} +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: futureDeprecatedPlatformVersionRenamed()[#Void#]; name=futureDeprecatedPlatformVersionRenamed(); diagnostics=warning:'futureDeprecatedPlatformVersionRenamed()' will be deprecated in a future version of macOS: renamed to 'renamedName'{{$}} +// MEMBER-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: softDeprecatedVar[#Int#]; name=softDeprecatedVar; diagnostics=warning:'softDeprecatedVar' will be deprecated in a future version{{$}} +// MEMBER: End completions +} + +func testOwnGetter() { + var valueInOwnGetter: Int { + #^GETTER^# + } +// GETTER: Begin completions +// GETTER-DAG: Decl[LocalVar]/Local/NotRecommended/TypeRelation[Identical]: valueInOwnGetter[#Int#]; name=valueInOwnGetter; diagnostics=warning:attempting to access 'valueInOwnGetter' within its own getter{{$}} +// GETTER: End completions +} diff --git a/test/IDE/complete_diagnostics_concurrency.swift b/test/IDE/complete_diagnostics_concurrency.swift new file mode 100644 index 0000000000000..257155d61c449 --- /dev/null +++ b/test/IDE/complete_diagnostics_concurrency.swift @@ -0,0 +1,32 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t/output -warn-concurrency + +func asyncFunc() async {} +func syncFunc() {} + +struct MySendable {} +class MyNonSendable {} + + +actor MyActor { + func receiveSendable(arg: MySendable) {} + func receiveNonSendable(arg: MyNonSendable) {} +} + +func testAsyncContext() { + #^SYNC_CONTEXT^# +// SYNC_CONTEXT: Begin completions +// SYNC_CONTEXT-DAG: Decl[FreeFunction]/CurrModule/NotRecommended: asyncFunc()[' async'][#Void#]; name=asyncFunc(); diagnostics=error:async 'asyncFunc()' used in a context that does not support concurrency{{$}} +// SYNC_CONTEXT-DAG: Decl[FreeFunction]/CurrModule: syncFunc()[#Void#]; name=syncFunc(){{$}} +// SYNC_CONTEXT: End completions +} + +func testActor(obj: MyActor) async { + obj.#^ACTOR^# +// ACTOR: Begin completions +// ACTOR-DAG: Decl[InstanceMethod]/CurrNominal: receiveSendable({#arg: MySendable#})[' async'][#Void#]; name=receiveSendable(arg:){{$}} +// ACTOR-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: receiveNonSendable({#arg: MyNonSendable#})[' async'][#Void#]; name=receiveNonSendable(arg:); diagnostics=warning:actor-isolated 'receiveNonSendable(arg:)' should only be referenced from inside the actor{{$}} +// ACTOR: End completions +} diff --git a/test/IDE/complete_doc_keyword.swift b/test/IDE/complete_doc_keyword.swift deleted file mode 100644 index 318a461632752..0000000000000 --- a/test/IDE/complete_doc_keyword.swift +++ /dev/null @@ -1,110 +0,0 @@ -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE1 | %FileCheck %s -check-prefix=TYPE1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MEMBER1 | %FileCheck %s -check-prefix=MEMBER1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MEMBER2 | %FileCheck %s -check-prefix=MEMBER2 - -/** - - keyword: C1, Class - - recommended: C2 -*/ -class C1 { - /** - - keyword: v1, Int - - recommended: v2 - */ - var v1 : Int = 0 - - /** - - keyword: v2, Int - - recommendedover: v1 - */ - var v2 : Int = 0 - - /** - - keyword: f1, func - - recommended: f2 - */ - func f1() {} - - /** - - keyword: f2, func - - recommendedover: f1 - */ - func f2() {} -} - -/** - - keyword: C2, Class - - recommendedover: C1 -*/ -class C2 {} - -/** - - keyword: S1, Struct - - recommendedover: S2 -*/ -struct S1 {} - -/** - - keyword: S2, Struct - - recommended: S1 -*/ -struct S2 {} - -/** - - keyword: E1, Enum - - recommended: E2 -*/ -enum E1{} - -/** - - keyword: E2, Enum - - recommendedover: E1 -*/ -enum E2{} - -/** - - keyword: S3, Struct -*/ -struct S3 { - /** - - nonmutatingvariant: fooing - */ - mutating func foo() {} - - /** - - mutatingvariant: foo - */ - func fooing() -> S3 {} -} - -func foo1() { - #^TYPE1^# -// TYPE1: Begin completions -// TYPE1-DAG: Decl[Class]/CurrModule/keyword[C1, Class]/recommended[C2]: C1[#C1#] -// TYPE1-DAG: Decl[Struct]/CurrModule/keyword[S1, Struct]/recommendedover[S2]: S1[#S1#] -// TYPE1-DAG: Decl[Enum]/CurrModule/keyword[E2, Enum]/recommendedover[E1]: E2[#E2#] -// TYPE1-DAG: Decl[Struct]/CurrModule/keyword[S2, Struct]/recommended[S1]: S2[#S2#] -// TYPE1-DAG: Decl[Class]/CurrModule/keyword[C2, Class]/recommendedover[C1]: C2[#C2#] -// TYPE1-DAG: Decl[Enum]/CurrModule/keyword[E1, Enum]/recommended[E2]: E1[#E1#] -// TYPE1-DAG: Decl[Struct]/CurrModule/keyword[S3, Struct]: S3[#S3#] -} - -func foo2() { - let c = C1() - c.#^MEMBER1^# -// MEMBER1: Begin completions -// MEMBER1-NEXT: Keyword[self]/CurrNominal: self[#C1#]; name=self -// MEMBER1-NEXT: Decl[InstanceVar]/CurrNominal/keyword[v1, Int]/recommended[v2]: v1[#Int#] -// MEMBER1-NEXT: Decl[InstanceVar]/CurrNominal/keyword[v2, Int]/recommendedover[v1]: v2[#Int#] -// MEMBER1-NEXT: Decl[InstanceMethod]/CurrNominal/keyword[f1, func]/recommended[f2]: f1()[#Void#] -// MEMBER1-NEXT: Decl[InstanceMethod]/CurrNominal/keyword[f2, func]/recommendedover[f1]: f2()[#Void#] -} - -func foo3() { - let s = S3() - s.#^MEMBER2^# -// MEMBER2: Begin completions -// MEMBER2-NEXT: Keyword[self]/CurrNominal: self[#S3#]; name=self -// MEMBER2-NEXT: Decl[InstanceMethod]/CurrNominal/nonmutatingvariant[fooing]: foo()[#Void#] -// MEMBER2-NEXT: Decl[InstanceMethod]/CurrNominal/mutatingvariant[foo]: fooing()[#S3#] -} diff --git a/test/IDE/complete_dynamic_lookup.swift b/test/IDE/complete_dynamic_lookup.swift index 1dba037ded74b..3901662dac353 100644 --- a/test/IDE/complete_dynamic_lookup.swift +++ b/test/IDE/complete_dynamic_lookup.swift @@ -463,7 +463,7 @@ func testAnyObject11_(_ dl: AnyObject) { dl.returnsObjcClass!(#^DL_FUNC_NAME_PAREN_1^# } // DL_FUNC_NAME_PAREN_1: Begin completions -// DL_FUNC_NAME_PAREN_1-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(i): Int#}[')'][#TopLevelObjcClass#]{{; name=.+$}} +// DL_FUNC_NAME_PAREN_1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(i): Int#}[')'][#TopLevelObjcClass#]{{; name=.+$}} // DL_FUNC_NAME_PAREN_1: End completions func testAnyObject12(_ dl: AnyObject) { @@ -475,7 +475,7 @@ func testAnyObject13(_ dl: AnyObject) { dl.returnsObjcClass!#^DL_FUNC_NAME_BANG_1^# } // DL_FUNC_NAME_BANG_1: Begin completions -// DL_FUNC_NAME_BANG_1-NEXT: Decl[InstanceMethod]/CurrNominal: ({#(i): Int#})[#TopLevelObjcClass#] +// DL_FUNC_NAME_BANG_1-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(i): Int#})[#TopLevelObjcClass#] // DL_FUNC_NAME_BANG_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> TopLevelObjcClass#]; name=self // DL_FUNC_NAME_BANG_1-NEXT: End completions diff --git a/test/IDE/complete_enum_elements.swift b/test/IDE/complete_enum_elements.swift index dc90f216ccf5c..096c0e2482a08 100644 --- a/test/IDE/complete_enum_elements.swift +++ b/test/IDE/complete_enum_elements.swift @@ -14,9 +14,9 @@ enum FooEnum: CaseIterable { } // FOO_ENUM_TYPE_CONTEXT: Begin completions -// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Foo1[#FooEnum#]{{; name=.+$}} -// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Foo2[#FooEnum#]{{; name=.+$}} -// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: .alias1[#FooEnum#]; name=alias1 +// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: .Foo1[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: .Foo2[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: .alias1[#FooEnum#]; name=alias1 // FOO_ENUM_TYPE_CONTEXT: End completions // FOO_ENUM_NO_DOT: Begin completions @@ -53,10 +53,10 @@ enum FooEnum: CaseIterable { // FOO_ENUM_DOT_CONTEXT-NEXT: End completions // FOO_ENUM_DOT_ELEMENTS: Begin completions, 6 items -// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Foo1[#FooEnum#]{{; name=.+$}} -// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Foo2[#FooEnum#]{{; name=.+$}} -// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: alias1[#FooEnum#]; name=alias1 -// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): FooEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: FooEnum) +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Foo1[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Foo2[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: alias1[#FooEnum#]; name=alias1 +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): FooEnum#})[#(into: inout Hasher) -> Void#]; name=hash(:) // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[TypeAlias]/CurrNominal: AllCases[#[FooEnum]#]; name=AllCases // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]; name=allCases // FOO_ENUM_DOT_ELEMENTS-NEXT: End completions @@ -202,15 +202,15 @@ enum QuxEnum : Int { } // QUX_ENUM_TYPE_CONTEXT: Begin completions -// QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Qux1[#QuxEnum#]{{; name=.+$}} -// QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Qux2[#QuxEnum#]{{; name=.+$}} +// QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: .Qux1[#QuxEnum#]{{; name=.+$}} +// QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: .Qux2[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_TYPE_CONTEXT: End completions // QUX_ENUM_NO_DOT: Begin completions, 7 items // QUX_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Qux1[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Qux2[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_NO_DOT-NEXT: Decl[TypeAlias]/CurrNominal: .RawValue[#Int#]{{; name=.+$}} -// QUX_ENUM_NO_DOT-NEXT: Decl[Constructor]/CurrNominal: ({#rawValue: Int#})[#QuxEnum?#]{{; name=.+$}} +// QUX_ENUM_NO_DOT-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#rawValue: Int#})[#QuxEnum?#]{{; name=.+$}} // QUX_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/Super/IsSystem: .hash({#(self): QuxEnum#})[#(into: inout Hasher) -> Void#]{{; name=.+$}} // QUX_ENUM_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#QuxEnum.Type#]; name=self // QUX_ENUM_NO_DOT-NEXT: Keyword/CurrNominal: .Type[#QuxEnum.Type#]; name=Type @@ -364,19 +364,19 @@ func testWithInvalid1() { // UNRESOLVED_1: Begin completions // UNRESOLVED_1-NOT: Baz // UNRESOLVED_1-NOT: Bar -// UNRESOLVED_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux1[#QuxEnum#]; name=Qux1 -// UNRESOLVED_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux2[#QuxEnum#]; name=Qux2 +// UNRESOLVED_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Qux1[#QuxEnum#]; name=Qux1 +// UNRESOLVED_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Qux2[#QuxEnum#]; name=Qux2 // UNRESOLVED_1-NOT: Okay } func testUnqualified1(x: QuxEnum) { _ = x == .Qux1 || x == .#^UNRESOLVED_2^#Qux2 // UNRESOLVED_2: Begin completions - // UNRESOLVED_2-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux1[#QuxEnum#]; name=Qux1 - // UNRESOLVED_2-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux2[#QuxEnum#]; name=Qux2 + // UNRESOLVED_2-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Qux1[#QuxEnum#]; name=Qux1 + // UNRESOLVED_2-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Qux2[#QuxEnum#]; name=Qux2 // UNRESOLVED_2-DAG: Decl[TypeAlias]/CurrNominal: RawValue[#Int#]; name=RawValue - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#QuxEnum?#]; name=init(rawValue: Int) - // UNRESOLVED_2-DAG: Decl[InstanceMethod]/Super/IsSystem/TypeRelation[Invalid]: hash({#(self): QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: QuxEnum) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#QuxEnum?#]; name=init(rawValue:) + // UNRESOLVED_2-DAG: Decl[InstanceMethod]/Super/IsSystem/TypeRelation[Invalid]: hash({#(self): QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(:) // UNRESOLVED_2: End completions _ = (x == .Qux1#^UNRESOLVED_3^#) @@ -384,3 +384,116 @@ func testUnqualified1(x: QuxEnum) { // UNRESOLVED_3: End completions } + +struct SomeStruct: Equatable { + let structMember: String +} + +enum OtherEnum { + case otherEnumCase + + static var otherStaticMember: OtherEnum { fatalError() } +} + +enum PlainEnum { + case caseWithAssociatedType(SomeStruct) + + static var staticMember: PlainEnum { fatalError() } +} + +func completeEquatableEnum(x: PlainEnum) { + switch x { + case .#^PATTERN_MATCH_PLAIN_ENUM^# +// We shouldn't suggest staticMember here because PlainEnum isn't equatable and thus doesn't have a ~= operator defined between instances +// PATTERN_MATCH_PLAIN_ENUM: Begin completions, 1 items +// PATTERN_MATCH_PLAIN_ENUM-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: caseWithAssociatedType({#SomeStruct#})[#PlainEnum#]; +// PATTERN_MATCH_PLAIN_ENUM: End completions + } +} + +enum EnumWithCustomPatternMatchingOperator { + case caseWithAssociatedType(SomeStruct) + + static var staticMember: EnumWithCustomPatternMatchingOperator { fatalError() } +} + +func ~=(pattern: OtherEnum, value: EnumWithCustomPatternMatchingOperator) -> Bool { + return true +} + +func completeEnumWithCustomPatternMatchingOperator(x: EnumWithCustomPatternMatchingOperator) { + switch x { + case .#^PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING^# +// We should be suggesting static members of `OtherEnum`, because we can match it to `EnumWithCustomPatternMatchingOperator` using the custom pattern match operator. +// We should also suggest enum cases from `EnumWithCustomPatternMatchingOperator` whose pattern matching doesn't go through any `~=` operator. +// We shouldn't suggest `staticMember` because `EnumWithCustomPatternMatchingOperator` doesn`t have `~=` defined between two of its instances. +// PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING: Begin completions, 4 items +// PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: otherEnumCase[#OtherEnum#]; +// PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: otherStaticMember[#OtherEnum#]; +// PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): OtherEnum#})[#(into: inout Hasher) -> Void#]; +// PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: caseWithAssociatedType({#SomeStruct#})[#EnumWithCustomPatternMatchingOperator#]; +// PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING: End completions + } +} + +func completeEnumAssociatedTypeWithCustomPatternMatchingOperator(x: EnumWithCustomPatternMatchingOperator) { + switch x { + case .caseWithAssociatedType(.#^COMPLETE_ASSOCIATED_TYPE_WITH_CUSTOM_PATTERN_MATCHING^#) +// COMPLETE_ASSOCIATED_TYPE_WITH_CUSTOM_PATTERN_MATCHING: Begin completions, 1 items +// COMPLETE_ASSOCIATED_TYPE_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init({#structMember: String#})[#SomeStruct#]; +// COMPLETE_ASSOCIATED_TYPE_WITH_CUSTOM_PATTERN_MATCHING: End completions + } +} + +enum EquatableEnum: Equatable { + case caseWithAssociatedType(SomeStruct) + + static var staticMember: EnumWithCustomPatternMatchingOperator { fatalError() } +} + +func completeEquatableEnum(x: EquatableEnum) { + switch x { + case .#^PATTERN_MATCH_EQUATABLE_ENUM^# +// Suggest all static members of `EquatableEnum` because we can pattern match through the `~=` operator between two equatable types defined in the stdlib. +// PATTERN_MATCH_EQUATABLE_ENUM: Begin completions, 2 items +// PATTERN_MATCH_EQUATABLE_ENUM-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: caseWithAssociatedType({#SomeStruct#})[#EquatableEnum#]; +// PATTERN_MATCH_EQUATABLE_ENUM-DAG: Decl[StaticVar]/CurrNominal: staticMember[#EnumWithCustomPatternMatchingOperator#]; +// PATTERN_MATCH_EQUATABLE_ENUM: End completions + } +} + +enum EquatableEnumWithCustomPatternMatchingOperator: Equatable { + case caseWithAssociatedType(SomeStruct) + + static var staticMember: EnumWithCustomPatternMatchingOperator { fatalError() } +} + +func ~=(pattern: OtherEnum, value: EquatableEnumWithCustomPatternMatchingOperator) -> Bool { + return true +} + +func completeEnumWithCustomPatternMatchingOperator(x: EquatableEnumWithCustomPatternMatchingOperator) { + switch x { + case .#^PATTERN_MATCH_EQUATABLE_ENUM_WITH_CUSTOM_PATTERN_MATCHING^# +// Same as PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING but also suggest static members on `EquatableEnumWithCustomPatternMatchingOperator` because its `Equatable` +// PATTERN_MATCH_EQUATABLE_ENUM_WITH_CUSTOM_PATTERN_MATCHING: Begin completions, 5 items +// PATTERN_MATCH_EQUATABLE_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: otherEnumCase[#OtherEnum#]; +// PATTERN_MATCH_EQUATABLE_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: otherStaticMember[#OtherEnum#]; +// PATTERN_MATCH_EQUATABLE_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): OtherEnum#})[#(into: inout Hasher) -> Void#]; +// PATTERN_MATCH_EQUATABLE_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: caseWithAssociatedType({#SomeStruct#})[#EquatableEnumWithCustomPatternMatchingOperator#]; +// PATTERN_MATCH_EQUATABLE_ENUM_WITH_CUSTOM_PATTERN_MATCHING-DAG: Decl[StaticVar]/CurrNominal: staticMember[#EnumWithCustomPatternMatchingOperator#]; +// PATTERN_MATCH_EQUATABLE_ENUM_WITH_CUSTOM_PATTERN_MATCHING: End completions + } +} + +enum WithNestedEnum { + case bar(PlainEnum) +} + +func completeInNestedPatternMatching(x: WithNestedEnum) { + switch x { + case .bar(.#^NESTED_ENUM_PATTERN_MATCHING^#) +} +// NESTED_ENUM_PATTERN_MATCHING: Begin completions, 1 items +// NESTED_ENUM_PATTERN_MATCHING-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: caseWithAssociatedType({#SomeStruct#})[#PlainEnum#]; +// NESTED_ENUM_PATTERN_MATCHING: End completions diff --git a/test/IDE/complete_enum_unresolved_dot_argument_labels.swift b/test/IDE/complete_enum_unresolved_dot_argument_labels.swift new file mode 100644 index 0000000000000..cad2f79bd602e --- /dev/null +++ b/test/IDE/complete_enum_unresolved_dot_argument_labels.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=COMPLETE | %FileCheck %s + +enum DragState { + case inactive + case dragging(translation: Int) +} + +func foo() { + var state = DragState.inactive + state = .dragging(#^COMPLETE^# +} + +// CHECK: Begin completions, 1 item +// CHECK: Pattern/CurrModule/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#translation: Int#}[')'][#DragState#]; +// CHECK: End completions diff --git a/test/IDE/complete_escaped_keyword.swift b/test/IDE/complete_escaped_keyword.swift index ed9062b7f1617..30550da8184da 100644 --- a/test/IDE/complete_escaped_keyword.swift +++ b/test/IDE/complete_escaped_keyword.swift @@ -26,33 +26,33 @@ enum MyEnum { let _ = #^STATIC_PRIMARY^# // STATIC_PRIMARY: Begin completion // STATIC_PRIMARY-DAG: Decl[LocalVar]/Local: self[#MyEnum.Type#]; name=self -// STATIC_PRIMARY-DAG: Decl[EnumElement]/CurrNominal: `class`({#struct: String#})[#MyEnum#]; name=`class`(struct: String) -// STATIC_PRIMARY-DAG: Decl[EnumElement]/CurrNominal: `let`({#var: String#})[#MyEnum#]; name=`let`(var: String) -// STATIC_PRIMARY-DAG: Decl[StaticMethod]/CurrNominal: `public`({#private: String#})[#Int#]; name=`public`(private: String) -// STATIC_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(self: MyEnum) -// STATIC_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `if`({#(self): MyEnum#})[#(else: String) -> Int#]; name=`if`(self: MyEnum) +// STATIC_PRIMARY-DAG: Decl[EnumElement]/CurrNominal: `class`({#struct: String#})[#MyEnum#]; name=`class`(struct:) +// STATIC_PRIMARY-DAG: Decl[EnumElement]/CurrNominal: `let`({#var: String#})[#MyEnum#]; name=`let`(var:) +// STATIC_PRIMARY-DAG: Decl[StaticMethod]/CurrNominal: `public`({#private: String#})[#Int#]; name=`public`(private:) +// STATIC_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(:) +// STATIC_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `if`({#(self): MyEnum#})[#(else: String) -> Int#]; name=`if`(:) // STATIC_PRIMARY: End completion let _ = self#^STATIC_SELF_NODOT^# // STATIC_SELF_NODOT: Begin completions // STATIC_SELF_NODOT-DAG: Keyword[self]/CurrNominal: .self[#MyEnum.Type#]; name=self -// STATIC_SELF_NODOT-DAG: Decl[EnumElement]/CurrNominal: .class({#struct: String#})[#MyEnum#]; name=class(struct: String) -// STATIC_SELF_NODOT-DAG: Decl[EnumElement]/CurrNominal: .let({#var: String#})[#MyEnum#]; name=let(var: String) -// STATIC_SELF_NODOT-DAG: Decl[Constructor]/CurrNominal: .init({#init: String#})[#MyEnum#]; name=init(init: String) -// STATIC_SELF_NODOT-DAG: Decl[StaticMethod]/CurrNominal: .public({#private: String#})[#Int#]; name=public(private: String) -// STATIC_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .`init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(self: MyEnum) -// STATIC_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .if({#(self): MyEnum#})[#(else: String) -> Int#]; name=if(self: MyEnum) +// STATIC_SELF_NODOT-DAG: Decl[EnumElement]/CurrNominal: .class({#struct: String#})[#MyEnum#]; name=class(struct:) +// STATIC_SELF_NODOT-DAG: Decl[EnumElement]/CurrNominal: .let({#var: String#})[#MyEnum#]; name=let(var:) +// STATIC_SELF_NODOT-DAG: Decl[Constructor]/CurrNominal: .init({#init: String#})[#MyEnum#]; name=init(init:) +// STATIC_SELF_NODOT-DAG: Decl[StaticMethod]/CurrNominal: .public({#private: String#})[#Int#]; name=public(private:) +// STATIC_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .`init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(:) +// STATIC_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .if({#(self): MyEnum#})[#(else: String) -> Int#]; name=if(:) // STATIC_SELF_NODOT: End completion let _ = self.#^STATIC_SELF_DOT^# // STATIC_SELF_DOT: Begin completions // STATIC_SELF_DOT-DAG: Keyword[self]/CurrNominal: self[#MyEnum.Type#]; name=self -// STATIC_SELF_DOT-DAG: Decl[EnumElement]/CurrNominal: class({#struct: String#})[#MyEnum#]; name=class(struct: String) -// STATIC_SELF_DOT-DAG: Decl[EnumElement]/CurrNominal: let({#var: String#})[#MyEnum#]; name=let(var: String) -// STATIC_SELF_DOT-DAG: Decl[Constructor]/CurrNominal: init({#init: String#})[#MyEnum#]; name=init(init: String) -// STATIC_SELF_DOT-DAG: Decl[StaticMethod]/CurrNominal: public({#private: String#})[#Int#]; name=public(private: String) -// STATIC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(self: MyEnum) -// STATIC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: if({#(self): MyEnum#})[#(else: String) -> Int#]; name=if(self: MyEnum) +// STATIC_SELF_DOT-DAG: Decl[EnumElement]/CurrNominal: class({#struct: String#})[#MyEnum#]; name=class(struct:) +// STATIC_SELF_DOT-DAG: Decl[EnumElement]/CurrNominal: let({#var: String#})[#MyEnum#]; name=let(var:) +// STATIC_SELF_DOT-DAG: Decl[Constructor]/CurrNominal: init({#init: String#})[#MyEnum#]; name=init(init:) +// STATIC_SELF_DOT-DAG: Decl[StaticMethod]/CurrNominal: public({#private: String#})[#Int#]; name=public(private:) +// STATIC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(:) +// STATIC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: if({#(self): MyEnum#})[#(else: String) -> Int#]; name=if(:) // STATIC_SELF_DOT: End completion let _ = meta#^META_NODOT^# @@ -68,14 +68,14 @@ enum MyEnum { // INSTANCE_PRIMARY-DAG: Decl[LocalVar]/Local: self[#MyEnum#]; name=self // INSTANCE_PRIMARY-DAG: Decl[InstanceVar]/CurrNominal: self[#Int#]; name=self // FIXME: ^ This is shadowed. We should hide this. -// INSTANCE_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#deinit: String#})[#Int#]; name=`init`(deinit: String) -// INSTANCE_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `if`({#else: String#})[#Int#]; name=`if`(else: String) +// INSTANCE_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#deinit: String#})[#Int#]; name=`init`(deinit:) +// INSTANCE_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `if`({#else: String#})[#Int#]; name=`if`(else:) // INSTANCE_PRIMARY: End completion let _ = self#^INSTANCE_SELF_NODOT^# // INSTANCE_SELF_NODOT: Begin completions -// INSTANCE_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .`init`({#deinit: String#})[#Int#]; name=`init`(deinit: String) -// INSTANCE_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .if({#else: String#})[#Int#]; name=if(else: String) +// INSTANCE_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .`init`({#deinit: String#})[#Int#]; name=`init`(deinit:) +// INSTANCE_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .if({#else: String#})[#Int#]; name=if(else:) // INSTANCE_SELF_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .`self`[#Int#]; name=`self` // INSTANCE_SELF_NODOT-DAG: Keyword[self]/CurrNominal: .self[#MyEnum#]; name=self @@ -83,8 +83,8 @@ enum MyEnum { let _ = self.#^INSTANCE_SELF_DOT^# // INSTANCE_SELF_DOT: Begin completions -// INSTANCE_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#deinit: String#})[#Int#]; name=`init`(deinit: String) -// INSTANCE_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: if({#else: String#})[#Int#]; name=if(else: String) +// INSTANCE_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#deinit: String#})[#Int#]; name=`init`(deinit:) +// INSTANCE_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: if({#else: String#})[#Int#]; name=if(else:) // INSTANCE_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal: `self`[#Int#]; name=`self` // INSTANCE_SELF_DOT-DAG: Keyword[self]/CurrNominal: self[#MyEnum#]; name=self // INSTANCE_SELF_DOT: End completions diff --git a/test/IDE/complete_exception.swift b/test/IDE/complete_exception.swift index 89f02c18a5d2b..104922dd13362 100644 --- a/test/IDE/complete_exception.swift +++ b/test/IDE/complete_exception.swift @@ -92,7 +92,7 @@ func test002() { // THROW1-DAG: Decl[Class]/CurrModule: Error3[#Error3#]; name=Error3{{$}} // THROW1-DAG: Decl[Class]/CurrModule: Error2[#Error2#]; name=Error2{{$}} // THROW1-DAG: Decl[Class]/CurrModule: Error1[#Error1#]; name=Error1{{$}} -// THROW1-DAG: Decl[Protocol]/CurrModule: ErrorPro1[#ErrorPro1#]; name=ErrorPro1{{$}} +// THROW1-DAG: Decl[Protocol]/CurrModule/Flair[RareType]: ErrorPro1[#ErrorPro1#]; name=ErrorPro1{{$}} // THROW1-DAG: Decl[FreeFunction]/CurrModule: getError1()[#Error1#]{{; name=.+$}} // THROW1-DAG: Decl[FreeFunction]/CurrModule: getNSError()[#NSError#]{{; name=.+$}} @@ -108,8 +108,8 @@ func test002() { func test003() { do {} catch Error4.#^CATCH2^# // CATCH2: Begin completions -// CATCH2: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E1[#Error4#]{{; name=.+$}} -// CATCH2: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]{{; name=.+$}} +// CATCH2: Decl[EnumElement]/CurrNominal: E1[#Error4#]{{; name=.+$}} +// CATCH2: Decl[EnumElement]/CurrNominal: E2({#Int32#})[#Error4#]{{; name=.+$}} // CATCH2: End completions } @@ -124,7 +124,7 @@ func test004() { func test005() { do {} catch Error4.E2#^CATCH3^# // CATCH3: Begin completions -// CATCH3: Pattern/CurrModule: ({#Int32#})[#Error4#]{{; name=.+$}} +// CATCH3: Pattern/CurrModule/Flair[ArgLabels]: ({#Int32#})[#Error4#]{{; name=.+$}} // CATCH3: End completions } @@ -225,7 +225,7 @@ func test014() { // NSERROR_DOT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: code[#Int#]; name=code // NSERROR_DOT-DAG: Decl[InstanceVar]/Super: hashValue[#Int#]; name=hashValue // NSERROR_DOT-DAG: Decl[InstanceMethod]/Super/IsSystem: myClass()[#AnyClass!#]; name=myClass() -// NSERROR_DOT-DAG: Decl[InstanceMethod]/Super/IsSystem: isEqual({#(other): NSObject!#})[#Bool#]; name=isEqual(other: NSObject!) +// NSERROR_DOT-DAG: Decl[InstanceMethod]/Super/IsSystem: isEqual({#(other): NSObject!#})[#Bool#]; name=isEqual(:) // NSERROR_DOT-DAG: Decl[InstanceVar]/Super/IsSystem: hash[#Int#]; name=hash // NSERROR_DOT-DAG: Decl[InstanceVar]/Super/IsSystem: description[#String#]; name=description // NSERROR_DOT: End completions diff --git a/test/IDE/complete_expr_after_paren.swift b/test/IDE/complete_expr_after_paren.swift index e927dcbfb418e..a2bbbcae3b61d 100644 --- a/test/IDE/complete_expr_after_paren.swift +++ b/test/IDE/complete_expr_after_paren.swift @@ -40,20 +40,20 @@ class MyClass: Base, MyProtocol { func testConstructer() { MyClass(#^INITIALIZER^#) // INITIALIZER: Begin completions, 4 items -// INITIALIZER-DAG: Decl[Constructor]/CurrNominal: ['(']{#init1: Int#}[')'][#MyClass#]; -// INITIALIZER-DAG: Decl[Constructor]/CurrNominal: ['(']{#init2: Int#}[')'][#MyClass#]; -// INITIALIZER-DAG: Decl[Constructor]/CurrNominal: ['(']{#init3: Int#}[')'][#MyClass#]; -// INITIALIZER-DAG: Decl[Constructor]/CurrNominal: ['(']{#init4: Int#}[')'][#MyClass#]; +// INITIALIZER-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#init1: Int#}[')'][#MyClass#]; +// INITIALIZER-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#init2: Int#}[')'][#MyClass#]; +// INITIALIZER-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#init3: Int#}[')'][#MyClass#]; +// INITIALIZER-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#init4: Int#}[')'][#MyClass#]; // INITIALIZER: End completions } func testMethod(obj: MyClass) { obj.method(#^METHOD^#) // METHOD: Begin completions, 4 items -// METHOD-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method1: Int#}[')'][#Void#]; -// METHOD-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method2: Int#}[')'][#Void#]; -// METHOD-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method3: Int#}[')'][#Void#]; -// METHOD-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method4: Int#}[')'][#Void#]; +// METHOD-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#method1: Int#}[')'][#Void#]; +// METHOD-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#method2: Int#}[')'][#Void#]; +// METHOD-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#method3: Int#}[')'][#Void#]; +// METHOD-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#method4: Int#}[')'][#Void#]; // METHOD: End completions } @@ -73,8 +73,8 @@ struct MyStruct: HasUnavailable { func testUnavailable(val: MyStruct) { val.method(#^AVAILABILITY^#) // AVAILABILITY: Begin completions, 2 items -// AVAILABILITY-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method2: Int#}[')'][#Void#]; -// AVAILABILITY-DAG: Decl[InstanceMethod]/Super: ['(']{#method1: Int#}[')'][#Void#]; +// AVAILABILITY-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#method2: Int#}[')'][#Void#]; +// AVAILABILITY-DAG: Decl[InstanceMethod]/Super/Flair[ArgLabels]: ['(']{#method1: Int#}[')'][#Void#]; // AVAILABILITY: End completions } @@ -85,7 +85,7 @@ struct TestStatic { func testStaticFunc() { TestStatic.method(#^STATIC^#) // STATIC: Begin completions -// STATIC-DAG: Decl[StaticMethod]/CurrNominal: ['(']{#(self): TestStatic#}[')'][#() -> Void#]; -// STATIC-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(self): TestStatic#}[')'][#() -> Void#]; +// STATIC-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(self): TestStatic#}[')'][#() -> Void#]; +// STATIC-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(self): TestStatic#}[')'][#() -> Void#]; // STATIC: End completions } diff --git a/test/IDE/complete_expr_postfix_begin.swift b/test/IDE/complete_expr_postfix_begin.swift index 3c08e88a17767..33f1da94365fb 100644 --- a/test/IDE/complete_expr_postfix_begin.swift +++ b/test/IDE/complete_expr_postfix_begin.swift @@ -33,7 +33,7 @@ typealias FooTypealias = Int // COMMON-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}} // COMMON-DAG: Decl[Enum]/CurrModule: FooEnum[#FooEnum#]{{; name=.+$}} // COMMON-DAG: Decl[Class]/CurrModule: FooClass[#FooClass#]{{; name=.+$}} -// COMMON-DAG: Decl[Protocol]/CurrModule: FooProtocol[#FooProtocol#]{{; name=.+$}} +// COMMON-DAG: Decl[Protocol]/CurrModule/Flair[RareType]: FooProtocol[#FooProtocol#]{{; name=.+$}} // COMMON-DAG: Decl[TypeAlias]/CurrModule{{(/TypeRelation\[Identical\])?}}: FooTypealias[#Int#]{{; name=.+$}} // COMMON-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}} // COMMON-DAG: Keyword[try]/None: try{{; name=.+$}} diff --git a/test/IDE/complete_flair_cache.swift b/test/IDE/complete_flair_cache.swift new file mode 100644 index 0000000000000..d8d0f780a380e --- /dev/null +++ b/test/IDE/complete_flair_cache.swift @@ -0,0 +1,46 @@ +// %empty-directory(%t) +// %empty-directory(%t.ccp) + +// Repeat twice to ensure the cache file is not affected by the flair. +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -parse-as-library -filecheck %raw-FileCheck -completion-output-dir %t -completion-cache-path=%t.ccp +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -parse-as-library -filecheck %raw-FileCheck -completion-output-dir %t -completion-cache-path=%t.ccp + +struct MyStruct {} +protocol MyProtocol {} + +func testType() { + let a: #^TYPE^# +// TYPE: Begin completions +// TYPE-NOT: Keyword[import] +// TYPE-NOT: Keyword[struct] +// TYPE-NOT: Keyword[defer] +// TYPE-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct +// TYPE-DAG: Decl[Protocol]/CurrModule: MyProtocol[#MyProtocol#]; name=MyProtocol +// TYPE-DAG: Decl[Protocol]/OtherModule[Swift]/IsSystem: IteratorProtocol[#IteratorProtocol#]; name=IteratorProtocol +// TYPE-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: Int[#Int#]; name=Int +// TYPE: End completions +} + +func testType() { + #^EXPR^# +// EXPR: Begin completions +// EXPR-DAG: Keyword[import]/None/Flair[RareKeyword]: import; name=import +// EXPR-DAG: Keyword[struct]/None/Flair[RareKeyword]: struct; name=struct +// EXPR-DAG: Keyword[defer]/None: defer; name=defer +// EXPR-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct +// EXPR-DAG: Decl[Protocol]/CurrModule/Flair[RareType]: MyProtocol[#MyProtocol#]; name=MyProtocol +// EXPR-DAG: Decl[Protocol]/OtherModule[Swift]/Flair[RareType]/IsSystem: IteratorProtocol[#IteratorProtocol#]; name=IteratorProtocol +// EXPR-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: Int[#Int#]; name=Int +// EXPR: End completions +} + +#^TOPLEVEL^# +// TOPLEVEL: Begin completions +// TOPLEVEL-DAG: Keyword[import]/None: import; name=import +// TOPLEVEL-DAG: Keyword[struct]/None/Flair[CommonKeyword]: struct; name=struct +// TOPLEVEL-DAG: Keyword[defer]/None/Flair[ExprAtFileScope]: defer; name=defer +// TOPLEVEL-DAG: Decl[Struct]/CurrModule/Flair[ExprAtFileScope]: MyStruct[#MyStruct#]; name=MyStruct +// TOPLEVEL-DAG: Decl[Protocol]/CurrModule/Flair[RareType,ExprAtFileScope]: MyProtocol[#MyProtocol#]; name=MyProtocol +// TOPLEVEL-DAG: Decl[Protocol]/OtherModule[Swift]/Flair[RareType,ExprAtFileScope]/IsSystem: IteratorProtocol[#IteratorProtocol#]; name=IteratorProtocol +// TOPLEVEL-DAG: Decl[Struct]/OtherModule[Swift]/Flair[ExprAtFileScope]/IsSystem: Int[#Int#]; name=Int +// TOPLEVEL: End completions diff --git a/test/IDE/complete_from_clang_framework.swift b/test/IDE/complete_from_clang_framework.swift index 4e8ffebcde5ff..3c2c3dfbe96f2 100644 --- a/test/IDE/complete_from_clang_framework.swift +++ b/test/IDE/complete_from_clang_framework.swift @@ -78,8 +78,8 @@ func testSwiftCompletions(foo: SwiftStruct) { // CLANG_FOO-DAG: Decl[FreeFunction]/OtherModule[Foo]: fooFuncWithComment3()[#Void#]{{; name=.+$}} // CLANG_FOO-DAG: Decl[FreeFunction]/OtherModule[Foo]: fooFuncWithComment4()[#Void#]{{; name=.+$}} // CLANG_FOO-DAG: Decl[FreeFunction]/OtherModule[Foo]: fooFuncWithComment5()[#Void#]{{; name=.+$}} -// CLANG_FOO-DAG: Decl[Protocol]/OtherModule[Foo]: FooProtocolBase[#FooProtocolBase#]{{; name=.+$}} -// CLANG_FOO-DAG: Decl[Protocol]/OtherModule[Foo]: FooProtocolDerived[#FooProtocolDerived#]{{; name=.+$}} +// CLANG_FOO-DAG: Decl[Protocol]/OtherModule[Foo]/Flair[RareType]: FooProtocolBase[#FooProtocolBase#]{{; name=.+$}} +// CLANG_FOO-DAG: Decl[Protocol]/OtherModule[Foo]/Flair[RareType]: FooProtocolDerived[#FooProtocolDerived#]{{; name=.+$}} // CLANG_FOO-DAG: Decl[Class]/OtherModule[Foo]: FooClassBase[#FooClassBase#]{{; name=.+$}} // CLANG_FOO-DAG: Decl[Class]/OtherModule[Foo]: FooClassDerived[#FooClassDerived#]{{; name=.+$}} // CLANG_FOO-DAG: Decl[GlobalVar]/OtherModule[Foo]: FOO_MACRO_1[#Int32#]{{; name=.+$}} @@ -198,9 +198,9 @@ func testCompleteModuleQualifiedFoo2() { // CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo.FooSub]: .FooSubEnum1Y[#FooSubEnum1#]{{; name=.+$}} // CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo.FooSub]: .FooSubUnnamedEnumeratorA1[#Int#]{{; name=.+$}} // CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .fooIntVar[#Int32#]{{; name=.+$}} -// CLANG_QUAL_FOO_2-DAG: Decl[Protocol]/OtherModule[Foo]: ._InternalProt[#_InternalProt#] -// CLANG_QUAL_FOO_2-DAG: Decl[Protocol]/OtherModule[Foo]: .FooProtocolBase[#FooProtocolBase#]{{; name=.+$}} -// CLANG_QUAL_FOO_2-DAG: Decl[Protocol]/OtherModule[Foo]: .FooProtocolDerived[#FooProtocolDerived#]{{; name=.+$}} +// CLANG_QUAL_FOO_2-DAG: Decl[Protocol]/OtherModule[Foo]/Flair[RareType]: ._InternalProt[#_InternalProt#] +// CLANG_QUAL_FOO_2-DAG: Decl[Protocol]/OtherModule[Foo]/Flair[RareType]: .FooProtocolBase[#FooProtocolBase#]{{; name=.+$}} +// CLANG_QUAL_FOO_2-DAG: Decl[Protocol]/OtherModule[Foo]/Flair[RareType]: .FooProtocolDerived[#FooProtocolDerived#]{{; name=.+$}} // CLANG_QUAL_FOO_2-DAG: Decl[Struct]/OtherModule[Foo]: .FooEnum1[#FooEnum1#]{{; name=.+$}} // CLANG_QUAL_FOO_2-DAG: Decl[Struct]/OtherModule[Foo]: .FooEnum2[#FooEnum2#]{{; name=.+$}} // CLANG_QUAL_FOO_2-DAG: Decl[Struct]/OtherModule[Foo]: .FooEnum3[#FooEnum3#]{{; name=.+$}} @@ -230,7 +230,7 @@ func testCompleteModuleQualifiedBar1() { func testCompleteFunctionCall1() { fooFunc1#^FUNCTION_CALL_1^# // FUNCTION_CALL_1: Begin completions -// FUNCTION_CALL_1-NEXT: Decl[FreeFunction]/OtherModule[Foo]: ({#(a): Int32#})[#Int32#]{{; name=.+$}} +// FUNCTION_CALL_1-NEXT: Decl[FreeFunction]/OtherModule[Foo]/Flair[ArgLabels]: ({#(a): Int32#})[#Int32#]{{; name=.+$}} // FUNCTION_CALL_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int32) -> Int32#]; name=self // FUNCTION_CALL_1-NEXT: End completions } @@ -238,7 +238,7 @@ func testCompleteFunctionCall1() { func testCompleteFunctionCall2() { fooFunc1AnonymousParam#^FUNCTION_CALL_2^# // FUNCTION_CALL_2: Begin completions -// FUNCTION_CALL_2-NEXT: Decl[FreeFunction]/OtherModule[Foo]: ({#Int32#})[#Int32#]{{; name=.+$}} +// FUNCTION_CALL_2-NEXT: Decl[FreeFunction]/OtherModule[Foo]/Flair[ArgLabels]: ({#Int32#})[#Int32#]{{; name=.+$}} // FUNCTION_CALL_2-NEXT: Keyword[self]/CurrNominal: .self[#(Int32) -> Int32#]; name=self // FUNCTION_CALL_2-NEXT: End completions } @@ -246,8 +246,8 @@ func testCompleteFunctionCall2() { func testCompleteStructMembers1() { FooStruct1#^CLANG_STRUCT_MEMBERS_1^# // CLANG_STRUCT_MEMBERS_1: Begin completions -// CLANG_STRUCT_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal: ()[#FooStruct1#]{{; name=.+$}} -// CLANG_STRUCT_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal: ({#x: Int32#}, {#y: Double#})[#FooStruct1#]{{; name=.+$}} +// CLANG_STRUCT_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooStruct1#]{{; name=.+$}} +// CLANG_STRUCT_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#x: Int32#}, {#y: Double#})[#FooStruct1#]{{; name=.+$}} // CLANG_STRUCT_MEMBERS_1-NEXT: Keyword[self]/CurrNominal: .self[#FooStruct1.Type#]; name=self // CLANG_STRUCT_MEMBERS_1-NEXT: Keyword/CurrNominal: .Type[#FooStruct1.Type#]; name=Type // CLANG_STRUCT_MEMBERS_1-NEXT: End completions @@ -261,12 +261,12 @@ func testCompleteClassMembers1() { // CLANG_CLASS_MEMBERS_1-NEXT: Decl[InstanceMethod]/CurrNominal: .fooBaseInstanceFunc0({#(self): FooClassBase#})[#() -> Void#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_1-NEXT: Decl[StaticMethod]/CurrNominal: .fooBaseInstanceFunc1({#(anObject): Any!#})[#FooClassBase!#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_1-NEXT: Decl[InstanceMethod]/CurrNominal: .fooBaseInstanceFunc1({#(self): FooClassBase#})[#(Any?) -> FooClassBase?#]{{; name=.+$}} -// CLANG_CLASS_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal: ()[#FooClassBase!#] -// CLANG_CLASS_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal: ({#float: Float#})[#FooClassBase!#]{{; name=.+$}} +// CLANG_CLASS_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooClassBase!#] +// CLANG_CLASS_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#float: Float#})[#FooClassBase!#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_1-NEXT: Decl[StaticMethod]/CurrNominal: .fooBaseInstanceFuncOverridden()[#Void#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_1-NEXT: Decl[InstanceMethod]/CurrNominal: .fooBaseInstanceFuncOverridden({#(self): FooClassBase#})[#() -> Void#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_1-NEXT: Decl[StaticMethod]/CurrNominal: .fooBaseClassFunc0()[#Void#]{{; name=.+$}} -// CLANG_CLASS_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal: ({#(x): Int32#})[#FooClassBase!#] +// CLANG_CLASS_MEMBERS_1-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#(x): Int32#})[#FooClassBase!#] // CLANG_CLASS_MEMBERS_1-NEXT: Decl[StaticMethod]/CurrNominal: ._internalMeth3()[#Any!#] // CLANG_CLASS_MEMBERS_1-NEXT: Decl[InstanceMethod]/CurrNominal: ._internalMeth3({#(self): FooClassBase#})[#() -> Any?#] // CLANG_CLASS_MEMBERS_1-NEXT: Decl[StaticMethod]/CurrNominal: ._internalMeth2()[#Any!#] @@ -289,8 +289,8 @@ func testCompleteClassMembers2() { // CLANG_CLASS_MEMBERS_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooInstanceFunc2({#(self): FooClassDerived#})[#(Int32, withB: Int32) -> Void#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooBaseInstanceFuncOverridden({#(self): FooClassDerived#})[#() -> Void#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_2-NEXT: Decl[StaticMethod]/CurrNominal: .fooClassFunc0()[#Void#]{{; name=.+$}} -// CLANG_CLASS_MEMBERS_2-NEXT: Decl[Constructor]/CurrNominal: ()[#FooClassDerived!#] -// CLANG_CLASS_MEMBERS_2-NEXT: Decl[Constructor]/CurrNominal: ({#float: Float#})[#FooClassDerived!#]{{; name=.+$}} +// CLANG_CLASS_MEMBERS_2-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooClassDerived!#] +// CLANG_CLASS_MEMBERS_2-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#float: Float#})[#FooClassDerived!#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooProtoFunc({#(self): FooClassDerived#})[#() -> Void#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooProtoFuncWithExtraIndentation1({#(self): FooClassDerived#})[#() -> Void#]{{; name=.+$}} // CLANG_CLASS_MEMBERS_2-NEXT: Decl[InstanceMethod]/CurrNominal: .fooProtoFuncWithExtraIndentation2({#(self): FooClassDerived#})[#() -> Void#]{{; name=.+$}} diff --git a/test/IDE/complete_from_clang_importer_framework.swift b/test/IDE/complete_from_clang_importer_framework.swift index 7ec195825abf7..8412b5c21269c 100644 --- a/test/IDE/complete_from_clang_importer_framework.swift +++ b/test/IDE/complete_from_clang_importer_framework.swift @@ -12,13 +12,13 @@ import ctypes import Darwin // CLANG_CTYPES: Begin completions -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/keyword[Foo1, Struct1]: FooStruct1[#FooStruct1#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/keyword[Foo2]: FooStruct2[#FooStruct2#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/recommended[Foo2, Foo1]: FooStruct3[#FooStruct3#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/recommendedover[Foo3, Foo2]: FooStruct4[#FooStruct4#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct1[#FooStruct1#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct2[#FooStruct2#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct3[#FooStruct3#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct4[#FooStruct4#]{{; name=.+$}} // CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct5[#FooStruct5#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem/recommendedover[ro1, ro2, ro3, ro4]/recommended[r1, r2, r3]/keyword[k1, k2, k3, k4]: FooStruct6[#FooStruct6#]{{; name=.+$}} -// CLANG_CTYPES-DAG: Decl[TypeAlias]/OtherModule[ctypes]/IsSystem/keyword[Foo2]: FooStructTypedef1[#FooStruct2#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[Struct]/OtherModule[ctypes]/IsSystem: FooStruct6[#FooStruct6#]{{; name=.+$}} +// CLANG_CTYPES-DAG: Decl[TypeAlias]/OtherModule[ctypes]/IsSystem: FooStructTypedef1[#FooStruct2#]{{; name=.+$}} // CLANG_CTYPES: End completions // CLANG_MACROS: Begin completions @@ -60,7 +60,7 @@ func testClangMember1() { var FS = FooStruct1() FS.#^CLANG_MEMBER1^# // CLANG_MEMBERS1: Begin completions, 3 items -// CLANG_MEMBERS1-DAG: Decl[InstanceVar]/CurrNominal/IsSystem/keyword[x, Struct1]/recommended[y]: x[#Int32#]{{; name=.+$}} -// CLANG_MEMBERS1-DAG: Decl[InstanceVar]/CurrNominal/IsSystem/keyword[y, Struct1]/recommendedover[x]: y[#Double#]{{; name=.+$}} +// CLANG_MEMBERS1-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: x[#Int32#]{{; name=.+$}} +// CLANG_MEMBERS1-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: y[#Double#]{{; name=.+$}} // CLANG_MEMBERS1-DAG: Keyword[self]/CurrNominal: self[#FooStruct1#]; name=self } diff --git a/test/IDE/complete_from_stdlib.swift b/test/IDE/complete_from_stdlib.swift index 414c909efbd86..cf99e20408760 100644 --- a/test/IDE/complete_from_stdlib.swift +++ b/test/IDE/complete_from_stdlib.swift @@ -103,8 +103,8 @@ func testArchetypeReplacement3 (_ a : [Int]) { // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceVar]/Super/IsSystem: first[#Int?#] // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: map({#(transform): (Int) throws -> T##(Int) throws -> T#})[' rethrows'][#[T]#] // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: dropLast({#(k): Int#})[#ArraySlice#] -// PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: elementsEqual({#(other): Sequence#}, {#by: (Int, Sequence.Element) throws -> Bool##(Int, Sequence.Element) throws -> Bool#})[' rethrows'][#Bool#]; name=elementsEqual(other: Sequence, by: (Int, Sequence.Element) throws -> Bool) rethrows -// PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: elementsEqual({#(other): Sequence#})[#Bool#]; name=elementsEqual(other: Sequence) +// PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: elementsEqual({#(other): Sequence#}, {#by: (Int, Sequence.Element) throws -> Bool##(Int, Sequence.Element) throws -> Bool#})[' rethrows'][#Bool#]; name=elementsEqual(:by:) +// PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: elementsEqual({#(other): Sequence#})[#Bool#]; name=elementsEqual(:) protocol P2 { diff --git a/test/IDE/complete_from_swift_module.swift b/test/IDE/complete_from_swift_module.swift index 3f193f19844ca..4ee36295edb3a 100644 --- a/test/IDE/complete_from_swift_module.swift +++ b/test/IDE/complete_from_swift_module.swift @@ -103,16 +103,16 @@ func testCompleteModuleQualified3() { foo_swift_module.BarGenericSwiftStruct1#^MODULE_QUALIFIED_3^# } // MODULE_QUALIFIED_3: Begin completions -// MODULE_QUALIFIED_3-NEXT: Decl[Constructor]/CurrNominal: ({#t: _#})[#BarGenericSwiftStruct1<_>#]; name=(t: _) -// MODULE_QUALIFIED_3-NEXT: Decl[InstanceMethod]/CurrNominal: .bar1InstanceFunc({#(self): BarGenericSwiftStruct1<_>#})[#() -> Void#]; name=bar1InstanceFunc(self: BarGenericSwiftStruct1<_>) +// MODULE_QUALIFIED_3-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#t: _#})[#BarGenericSwiftStruct1<_>#]; name=(t:) +// MODULE_QUALIFIED_3-NEXT: Decl[InstanceMethod]/CurrNominal: .bar1InstanceFunc({#(self): BarGenericSwiftStruct1<_>#})[#() -> Void#]; name=bar1InstanceFunc(:) // MODULE_QUALIFIED_3: End completions func testCompleteModuleQualified4() { foo_swift_module.BarGenericSwiftStruct2#^MODULE_QUALIFIED_4^# } // MODULE_QUALIFIED_4: Begin completions -// MODULE_QUALIFIED_4-NEXT: Decl[Constructor]/CurrNominal: ({#t: _#}, {#u: _#})[#BarGenericSwiftStruct2<_, _>#]; name=(t: _, u: _) -// MODULE_QUALIFIED_4-NEXT: Decl[InstanceMethod]/CurrNominal: .bar2InstanceFunc({#(self): BarGenericSwiftStruct2<_, _>#})[#() -> Void#]; name=bar2InstanceFunc(self: BarGenericSwiftStruct2<_, _>) +// MODULE_QUALIFIED_4-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#t: _#}, {#u: _#})[#BarGenericSwiftStruct2<_, _>#]; name=(t:u:) +// MODULE_QUALIFIED_4-NEXT: Decl[InstanceMethod]/CurrNominal: .bar2InstanceFunc({#(self): BarGenericSwiftStruct2<_, _>#})[#() -> Void#]; name=bar2InstanceFunc(:) // MODULE_QUALIFIED_4-NEXT: Keyword[self]/CurrNominal: .self[#BarGenericSwiftStruct2<_, _>.Type#]; name=self // MODULE_QUALIFIED_4-NEXT: Keyword/CurrNominal: .Type[#BarGenericSwiftStruct2<_, _>.Type#]; name=Type // MODULE_QUALIFIED_4-NEXT: End completions diff --git a/test/IDE/complete_from_swiftonly_systemmodule.swift b/test/IDE/complete_from_swiftonly_systemmodule.swift index 94d81f0fb840d..fdd1d5bbf2787 100644 --- a/test/IDE/complete_from_swiftonly_systemmodule.swift +++ b/test/IDE/complete_from_swiftonly_systemmodule.swift @@ -73,6 +73,6 @@ func test(value: SomeValue) { let _ = SomeValue(#^INITIALIZER^# // INITIALIZER: Begin completions, 1 items -// INITIALIZER-DAG: Decl[Constructor]/CurrNominal/IsSystem: ['(']{#public: Int#}[')'][#SomeValue#]; +// INITIALIZER-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]/IsSystem: ['(']{#public: Int#}[')'][#SomeValue#]; // INITIALIZER: End completions } diff --git a/test/IDE/complete_generic_optional.swift b/test/IDE/complete_generic_optional.swift index b2f7f47a21f19..47a8042b246aa 100644 --- a/test/IDE/complete_generic_optional.swift +++ b/test/IDE/complete_generic_optional.swift @@ -12,6 +12,6 @@ struct Foo { let x: Foo? = Foo() x.#^FOO_OPTIONAL_1^# // FOO_OPTIONAL_1: Begin completions, 7 items -// FOO_OPTIONAL_1-DAG: Decl[InstanceMethod]/CurrNominal/Erase[1]: ?.myFunction({#(foobar): Bar#})[#Void#]; name=myFunction(foobar: Bar) +// FOO_OPTIONAL_1-DAG: Decl[InstanceMethod]/CurrNominal/Erase[1]: ?.myFunction({#(foobar): Bar#})[#Void#]; name=myFunction(:) // FOO_OPTIONAL_1-DAG: Keyword[self]/CurrNominal: self[#Foo?#]; name=self // FOO_OPTIONAL_1: End completions diff --git a/test/IDE/complete_generic_param.swift b/test/IDE/complete_generic_param.swift index f7cb6466e660e..44679f61c13b0 100644 --- a/test/IDE/complete_generic_param.swift +++ b/test/IDE/complete_generic_param.swift @@ -6,6 +6,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INHERIT6 | %FileCheck %s -check-prefix=INHERIT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_TYPE_PARAM // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SECOND_GENERIC_TYPE_PARAM | %FileCheck %s -check-prefix=GENERIC_TYPE_PARAM +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_PARAM_ON_NESTED_TYPE_GLOBAL_VAR | %FileCheck %s -check-prefix=GENERIC_PARAM_ON_NESTED_TYPE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_PARAM_ON_NESTED_TYPE_LOCAL_VAR | %FileCheck %s -check-prefix=GENERIC_PARAM_ON_NESTED_TYPE class C1{} protocol P1{} @@ -45,3 +47,19 @@ _ = Sr14432() // GENERIC_TYPE_PARAM: Begin completions // GENERIC_TYPE_PARAM-DAG: Decl[Class]/CurrModule: C1[#C1#]; // GENERIC_TYPE_PARAM: End completions + +struct Sr14627 { + struct Nested { + init() { fatalError() } + } +} + +var sr14627_globalVar = Sr14627.Nested< #^GENERIC_PARAM_ON_NESTED_TYPE_GLOBAL_VAR^#>() + +func someFunction() { + var sr14627_localVar = Sr14627.Nested< #^GENERIC_PARAM_ON_NESTED_TYPE_LOCAL_VAR^#>() +} + +// GENERIC_PARAM_ON_NESTED_TYPE: Begin completions +// GENERIC_PARAM_ON_NESTED_TYPE-DAG: Decl[Struct]/CurrModule: Sr14627[#Sr14627#]; +// GENERIC_PARAM_ON_NESTED_TYPE: End completions diff --git a/test/IDE/complete_global_actorisolation.swift b/test/IDE/complete_global_actorisolation.swift new file mode 100644 index 0000000000000..c5d85621bec7b --- /dev/null +++ b/test/IDE/complete_global_actorisolation.swift @@ -0,0 +1,176 @@ +// REQUIRES: concurrency +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +class MyNonSendable {} +struct MySendable {} + +@globalActor +actor MyGlobalActor { + static var shared = MyGlobalActor() +} + +@globalActor +actor MyOtherGlobalActor { + static var shared = MyOtherGlobalActor() +} + +@MyGlobalActor func globalFuncOnGlobalActor() {} + +func takeClosure(_: () async -> T) {} + +var otherInstanceOfMyClass = MyClass() + +class MyClass { + @MyGlobalActor func funcOnGlobalActor() -> Int { return 0 } + @MyOtherGlobalActor func funcOnOtherGlobalActor() -> Int { return 0 } + func funcSync() -> Int { return 0 } + + @MyGlobalActor func nonSenableFuncOnGlobalActor(arg: MyNonSendable) -> Int { return 0 } + @MyOtherGlobalActor func nonSenableFuncOnOtherGlobalActor(arg: MyNonSendable) -> Int { return 0 } + + @MyGlobalActor var varOnGlobalActor: Int = 0 + @MyOtherGlobalActor var varOnOtherGlobalActor: Int = 0 + var varSync: Int = 0 + + @MyGlobalActor subscript(onGlobalActor onGlobalActor: Int) -> Int { get { 1 } set { } } + @MyOtherGlobalActor subscript(onOtherGlobalActor onOtherGlobalActor: Int) -> Int { get { 1 } set { } } + subscript(sync sync: Int) -> Int { get { 1 } set { } } +} + +extension MyClass { + @MyGlobalActor func testOnGlobalActor() { + let _ = #^IN_FUNC_ON_GLOBAL_ACTOR^# +// IN_FUNC_ON_GLOBAL_ACTOR: Begin completions +// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal: funcOnGlobalActor()[#Int#]; name=funcOnGlobalActor() +// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() +// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal: funcSync()[#Int#]; name=funcSync() +// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal: nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[#Int#]; name=nonSenableFuncOnGlobalActor(arg:) +// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg:) +// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceVar]/CurrNominal: varOnGlobalActor[#Int#]; name=varOnGlobalActor +// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor +// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceVar]/CurrNominal: varSync[#Int#]; name=varSync +// IN_FUNC_ON_GLOBAL_ACTOR: End completions + + let _ = self.#^IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^# +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT: Begin completions +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: funcOnGlobalActor()[#Int#]; name=funcOnGlobalActor() +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: funcSync()[#Int#]; name=funcSync() +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[#Int#]; name=nonSenableFuncOnGlobalActor(arg:) +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg:) +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal: varOnGlobalActor[#Int#]; name=varOnGlobalActor +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal: varSync[#Int#]; name=varSync +// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT: End completions + + let _ = self#^IN_FUNC_ON_GLOBAL_ACTOR_NODOT^# +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT: Begin completions +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .funcOnGlobalActor()[#Int#]; name=funcOnGlobalActor() +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .funcSync()[#Int#]; name=funcSync() +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[#Int#]; name=nonSenableFuncOnGlobalActor(arg:) +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg:) +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .varOnGlobalActor[#Int#]; name=varOnGlobalActor +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: .varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .varSync[#Int#]; name=varSync +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[Subscript]/CurrNominal: [{#onGlobalActor: Int#}][#Int#]; name=[onGlobalActor:] +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[Subscript]/CurrNominal/NotRecommended: [{#onOtherGlobalActor: Int#}][' async'][#Int#]; name=[onOtherGlobalActor:] +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[Subscript]/CurrNominal: [{#sync: Int#}][#Int#]; name=[sync:] +// IN_FUNC_ON_GLOBAL_ACTOR_NODOT: End completions + + let _ = otherInstanceOfMyClass.#^IN_FUNC_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^# + let _ = otherInstanceOfMyClass#^IN_FUNC_ON_GLOBAL_ACTOR_OTHER_NODOT?check=IN_FUNC_ON_GLOBAL_ACTOR_NODOT^# + } + + func testInSyncFunc() { + let _ = #^IN_SYNC_FUNC^# +// IN_SYNC_FUNC: Begin completions +// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnGlobalActor()[' async'][#Int#]; name=funcOnGlobalActor() +// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() +// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal: funcSync()[#Int#]; name=funcSync() +// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnGlobalActor(arg:) +// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg:) +// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnGlobalActor[#Int#][' async']; name=varOnGlobalActor +// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor +// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceVar]/CurrNominal: varSync[#Int#]; name=varSync +// IN_SYNC_FUNC: End completions + + let _ = self.#^IN_SYNC_FUNC_SELF_DOT^# +// IN_SYNC_FUNC_SELF_DOT: Begin completions +// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnGlobalActor()[' async'][#Int#]; name=funcOnGlobalActor() +// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() +// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: funcSync()[#Int#]; name=funcSync() +// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnGlobalActor(arg:) +// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg:) +// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnGlobalActor[#Int#][' async']; name=varOnGlobalActor +// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor +// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal: varSync[#Int#]; name=varSync +// IN_SYNC_FUNC_SELF_DOT: End completions + + let _ = self#^IN_SYNC_FUNC_NODOT^# +// IN_SYNC_FUNC_NODOT: Begin completions +// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .funcOnGlobalActor()[' async'][#Int#]; name=funcOnGlobalActor() +// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() +// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .funcSync()[#Int#]; name=funcSync() +// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnGlobalActor(arg:) +// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg:) +// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: .varOnGlobalActor[#Int#][' async']; name=varOnGlobalActor +// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: .varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor +// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .varSync[#Int#]; name=varSync +// IN_SYNC_FUNC_NODOT-DAG: Decl[Subscript]/CurrNominal/NotRecommended: [{#onGlobalActor: Int#}][' async'][#Int#]; name=[onGlobalActor:] +// IN_SYNC_FUNC_NODOT-DAG: Decl[Subscript]/CurrNominal/NotRecommended: [{#onOtherGlobalActor: Int#}][' async'][#Int#]; name=[onOtherGlobalActor:] +// IN_SYNC_FUNC_NODOT-DAG: Decl[Subscript]/CurrNominal: [{#sync: Int#}][#Int#]; name=[sync:] +// IN_SYNC_FUNC_NODOT: End completions + + let _ = otherInstanceOfMyClass.#^IN_SYNC_FUNC_OTHER_DOT?check=IN_SYNC_FUNC_SELF_DOT^# + let _ = otherInstanceOfMyClass#^IN_SYNC_FUNC_OTHER_NODOT?check=IN_SYNC_FUNC_NODOT^# + } + + func testInGlobalActorClosure() { + _ = { @MyGlobalActor () -> Void in + let _ = otherInstanceOfMyClass.#^IN_CLOSURE_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^# + } + } + + func testInGlobalActorClosureWithoutExplicitAttribute() { + let callback: @MyGlobalActor () -> Void + callback = { + let _ = otherInstanceOfMyClass.#^IN_CLOSURE_ON_GLOBAL_ACTOR_WITHOUT_EXPLICIT_LABEL_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^# + } + } + + @MyGlobalActor func testInClosureInGlobalActorFunc() { + _ = { () -> Void in + let _ = otherInstanceOfMyClass.#^IN_CLOSURE_IN_FUNC_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^# + } + } + + func testInClosureNestedInClosureOnGlobalActorFunc() { + _ = { @MyGlobalActor () -> Void in + _ = { () -> Void in + let _ = otherInstanceOfMyClass.#^IN_CLOSURE_NESTED_IN_CLOSURE_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^# + } + } + } + + func testInLocalFunc() { + @MyGlobalActor func localFunc() { + let _ = otherInstanceOfMyClass.#^IN_LOCAL_FUNC_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^# + } + } + + @MyGlobalActor func testInNestedSingleExpressionClosure() { + takeClosure { + takeClosure { + otherInstanceOfMyClass.#^IN_NESTED_SINGLE_EXPRESSION_CLOSURE_ON_GLBOAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^# + } + } + } +} + +actor ActorTests { + func testInActor() { + let _ = otherInstanceOfMyClass.#^IN_ACTOR_OTHER_DOT?check=IN_SYNC_FUNC_SELF_DOT^# + let _ = otherInstanceOfMyClass#^IN_ACTOR_OTHER_NODOT?check=IN_SYNC_FUNC_NODOT^# + } +} diff --git a/test/IDE/complete_globalactorunsafe.swift b/test/IDE/complete_globalactorunsafe.swift new file mode 100644 index 0000000000000..16c93ff8daff6 --- /dev/null +++ b/test/IDE/complete_globalactorunsafe.swift @@ -0,0 +1,104 @@ +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t +// REQUIRES: concurrency + +// SAFE_NOTREC: Begin completions, 2 items +// SAFE_NOTREC-DAG: Keyword[self]/CurrNominal: self[#SafelyIsolatedCls#]; +// SAFE_NOTREC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method()[' async'][#Void#]; +// SAFE_NOTREC: End completions + +// UNSAFE_NOTREC: Begin completions, 2 items +// UNSAFE_NOTREC-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#]; +// UNSAFE_NOTREC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method()[' async'][#Void#]; +// UNSAFE_NOTREC: End completions + +// SAFE_OK: Begin completions, 2 items +// SAFE_OK-DAG: Keyword[self]/CurrNominal: self[#SafelyIsolatedCls#]; +// SAFE_OK-DAG: Decl[InstanceMethod]/CurrNominal: method()[' async'][#Void#]; +// SAFE_OK: End completions + +// UNSAFE_OK: Begin completions, 2 items +// UNSAFE_OK-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#]; +// UNSAFE_OK-DAG: Decl[InstanceMethod]/CurrNominal: method()[' async'][#Void#]; +// UNSAFE_OK: End completions + +// UNSAFE_OK_SYNC: Begin completions, 2 items +// UNSAFE_OK_SYNC-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#]; +// UNSAFE_OK_SYNC-DAG: Decl[InstanceMethod]/CurrNominal: method()[#Void#]; +// UNSAFE_OK_SYNC: End completions + +@globalActor +actor MyGlobalActor { + static let shared = MyGlobalActor() +} + +@MyGlobalActor(unsafe) +class UnsafelyIsolatedCls { + func method() +} + +@MyGlobalActor +class SafelyIsolatedCls { + func method() +} + +class TestNormal { + func testSync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + s.#^IN_METHOD_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_METHOD_SYNC_UNSAFE?check=UNSAFE_OK_SYNC^# + } + func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async { + s.#^IN_METHOD_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_METHOD_ASYNC_UNSAFE?check=UNSAFE_OK^# + } +} + +func testSync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + s.#^IN_FUNC_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_FUNC_SYNC_UNSAFE?check=UNSAFE_OK_SYNC^# +} +func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async { + s.#^IN_FUNC_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_FUNC_ASYNC_UNSAFE?check=UNSAFE_OK^# +} + +func testClosure(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + func receiverSync(fn: () -> Void) {} + func receiverAsync(fn: () async -> Void) {} + + receiverSync { + dummy; + s.#^IN_CLOSURE_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_CLOSURE_SYNC_UNSAFE?check=UNSAFE_OK_SYNC^# + } + + receiverAsync { + dummy; + s.#^IN_CLOSURE_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_CLOSURE_ASYNC_UNSAFE?check=UNSAFE_OK^# + } +} + +actor TestActor { + func test(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + s.#^IN_ACTOR_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_ACTOR_SYNC_UNSAFE?check=UNSAFE_NOTREC^# + } + + func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async { + s.#^IN_ACTOR_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_ACTOR_ASYNC_UNSAFE?check=UNSAFE_OK^# + } +} + +@MainActor +class TestMainActorIsolated { + func test(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + s.#^IN_GLOBALACTOR_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_GLOBALACTOR_SYNC_UNSAFE?check=UNSAFE_NOTREC^# + } + + func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async { + s.#^IN_GLOBALACTOR_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_GLOBALACTOR_ASYNC_UNSAFE?check=UNSAFE_OK^# + } +} diff --git a/test/IDE/complete_globalactorunsafe_strict.swift b/test/IDE/complete_globalactorunsafe_strict.swift new file mode 100644 index 0000000000000..a35b7561df6b3 --- /dev/null +++ b/test/IDE/complete_globalactorunsafe_strict.swift @@ -0,0 +1,104 @@ +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -warn-concurrency +// REQUIRES: concurrency + +// SAFE_NOTREC: Begin completions, 2 items +// SAFE_NOTREC-DAG: Keyword[self]/CurrNominal: self[#SafelyIsolatedCls#]; +// SAFE_NOTREC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method()[' async'][#Void#]; +// SAFE_NOTREC: End completions + +// UNSAFE_NOTREC: Begin completions, 2 items +// UNSAFE_NOTREC-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#]; +// UNSAFE_NOTREC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method()[' async'][#Void#]; +// UNSAFE_NOTREC: End completions + +// SAFE_OK: Begin completions, 2 items +// SAFE_OK-DAG: Keyword[self]/CurrNominal: self[#SafelyIsolatedCls#]; +// SAFE_OK-DAG: Decl[InstanceMethod]/CurrNominal: method()[' async'][#Void#]; +// SAFE_OK: End completions + +// UNSAFE_OK: Begin completions, 2 items +// UNSAFE_OK-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#]; +// UNSAFE_OK-DAG: Decl[InstanceMethod]/CurrNominal: method()[' async'][#Void#]; +// UNSAFE_OK: End completions + +// UNSAFE_OK_SYNC: Begin completions, 2 items +// UNSAFE_OK_SYNC-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#]; +// UNSAFE_OK_SYNC-DAG: Decl[InstanceMethod]/CurrNominal: method()[#Void#]; +// UNSAFE_OK_SYNC: End completions + +@globalActor +actor MyGlobalActor { + static let shared = MyGlobalActor() +} + +@MyGlobalActor(unsafe) +class UnsafelyIsolatedCls { + func method() +} + +@MyGlobalActor +class SafelyIsolatedCls { + func method() +} + +class TestNormal { + func testSync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + s.#^IN_METHOD_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_METHOD_SYNC_UNSAFE?check=UNSAFE_NOTREC^# + } + func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async { + s.#^IN_METHOD_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_METHOD_ASYNC_UNSAFE?check=UNSAFE_OK^# + } +} + +func testSync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + s.#^IN_FUNC_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_FUNC_SYNC_UNSAFE?check=UNSAFE_NOTREC^# +} +func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async { + s.#^IN_FUNC_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_FUNC_ASYNC_UNSAFE?check=UNSAFE_OK^# +} + +func testClosure(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + func receiverSync(fn: () -> Void) {} + func receiverAsync(fn: () async -> Void) {} + + receiverSync { + dummy; + s.#^IN_CLOSURE_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_CLOSURE_SYNC_UNSAFE?check=UNSAFE_NOTREC^# + } + + receiverAsync { + dummy; + s.#^IN_CLOSURE_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_CLOSURE_ASYNC_UNSAFE?check=UNSAFE_OK^# + } +} + +actor TestActor { + func test(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + s.#^IN_ACTOR_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_ACTOR_SYNC_UNSAFE?check=UNSAFE_NOTREC^# + } + + func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async { + s.#^IN_ACTOR_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_ACTOR_ASYNC_UNSAFE?check=UNSAFE_OK^# + } +} + +@MainActor +class TestMainActorIsolated { + func test(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) { + s.#^IN_GLOBALACTOR_SYNC_SAFE?check=SAFE_NOTREC^# + u.#^IN_GLOBALACTOR_SYNC_UNSAFE?check=UNSAFE_NOTREC^# + } + + func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async { + s.#^IN_GLOBALACTOR_ASYNC_SAFE?check=SAFE_OK^# + u.#^IN_GLOBALACTOR_ASYNC_UNSAFE?check=UNSAFE_OK^# + } +} diff --git a/test/IDE/complete_in_closures.swift b/test/IDE/complete_in_closures.swift index 8b3139b1b3ae9..3a3e914af8afd 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -// ERROR_COMMON: found code completion token -// ERROR_COMMON-NOT: Begin completions +// EMTPY: Token +// EMPTY-NOT: Begin completions //===--- Helper types that are used in this test @@ -194,7 +194,7 @@ acceptsListAndTrailingClosureTVoid( func getInt() -> Int? { return 0 } func testAcceptsTrailingClosureInt1() { - acceptsTrailingClosureFooVoid { #^IN_TRAILING_CLOSURE_16?check=WITH_GLOBAL_DECLS^# in + acceptsTrailingClosureFooVoid { #^IN_TRAILING_CLOSURE_16?check=EMPTY^# in if let myvar = getInt() { } } @@ -312,8 +312,8 @@ func testIIFE() { }() } // IN_IIFE_1: Begin completions -// IN_IIFE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: north[#SomeEnum#] -// IN_IIFE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: south[#SomeEnum#] +// IN_IIFE_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: north[#SomeEnum#] +// IN_IIFE_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: south[#SomeEnum#] extension Error { var myErrorNumber: Int { return 0 } @@ -339,7 +339,7 @@ var foo = { // DECL_IN_CLOSURE_IN_TOPLEVEL_INIT-DAG: Keyword[self]/CurrNominal: self[#String#]; name=self // DECL_IN_CLOSURE_IN_TOPLEVEL_INIT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: count[#Int#]; name=count // DECL_IN_CLOSURE_IN_TOPLEVEL_INIT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: unicodeScalars[#String.UnicodeScalarView#]; name=unicodeScalars - // DECL_IN_CLOSURE_IN_TOPLEVEL_INIT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: hasPrefix({#(prefix): String#})[#Bool#]; name=hasPrefix(prefix: String) + // DECL_IN_CLOSURE_IN_TOPLEVEL_INIT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: hasPrefix({#(prefix): String#})[#Bool#]; name=hasPrefix(:) // DECL_IN_CLOSURE_IN_TOPLEVEL_INIT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: utf16[#String.UTF16View#]; name=utf16 // DECL_IN_CLOSURE_IN_TOPLEVEL_INIT-DAG: Decl[InstanceMethod]/Super/IsSystem: dropFirst()[#Substring#]; name=dropFirst() // DECL_IN_CLOSURE_IN_TOPLEVEL_INIT: End completions @@ -363,11 +363,36 @@ func testInsideTernaryClosureReturn(test: Bool) -> [String] { // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .description[#String#]; name=description // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .isWhitespace[#Bool#]; name=isWhitespace // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: .uppercased()[#String#]; name=uppercased() - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']... {#String.Element#}[#ClosedRange#]; name=... String.Element - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']< {#Character#}[#Bool#]; name=< Character - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']>= {#String.Element#}[#Bool#]; name=>= String.Element - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']== {#Character#}[#Bool#]; name=== Character + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']... {#String.Element#}[#ClosedRange#]; name=... + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']< {#Character#}[#Bool#]; name=< + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']>= {#String.Element#}[#Bool#]; name=>= + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']== {#Character#}[#Bool#]; name=== // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Keyword[self]/CurrNominal: .self[#String.Element#]; name=self // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT: End completions } } + +func testSignature() { + func accept(_: () -> T) {} + + accept { #^PARAM_BARE_1?check=EMPTY^# in } + accept { #^PARAM_BARE_2?check=EMPTY^#, arg2 in } + accept { arg1, #^PARAM_BARE_3?check=EMPTY^# in } + + accept { (#^PARAM_PAREN_1?check=EMPTY^#) in } + accept { (#^PARAM_PAREN_2?check=EMPTY^#, arg2) in } + accept { (arg1, #^PARAM_PAREN_3?check=EMPTY^#) in } + + accept { (arg1: #^PARAMTYPE_1?check=WITH_GLOBAL_DECLS^#) in } + accept { (arg1: Int, arg2: #^PARAMTYPE_2?check=WITH_GLOBAL_DECLS^#) in } + + accept { [#^CAPTURE_1?check=WITH_GLOBAL_DECLS^#] in } + accept { [weak #^CAPTURE_2?check=WITH_GLOBAL_DECLS^#] in } + accept { [#^CAPTURE_3?check=EMPTY^# = capture] in } + accept { [weak #^CAPTURE_4?check=EMPTY^# = capture] in } + + accept { () -> #^RESULTTYPE_1?check=WITH_GLOBAL_DECLS^# in } + accept { arg1, arg2 -> #^RESULTTYPE_2?check=WITH_GLOBAL_DECLS^# in } + + // NOTE: For effects specifiers completion (e.g. '() -> Void') see test/IDE/complete_concurrency_specifier.swift +} diff --git a/test/IDE/complete_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift new file mode 100644 index 0000000000000..6192a00c789f7 --- /dev/null +++ b/test/IDE/complete_in_result_builder.swift @@ -0,0 +1,174 @@ +// RUN %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +@resultBuilder +struct TupleBuilder { + static func buildBlock() -> () { } + + static func buildBlock(_ t1: T) -> T { + return t1 + } + + static func buildBlock(_ t1: T, _ t2: T) -> (T, T) { + return (t1, t2) + } + static func buildBlock(_ t1: T, _ t2: T, _ t3: T) -> (T, T, T) { + return (t1, t2, t3) + } +} + +func buildStringTuple(@TupleBuilder x: () -> Result) {} + +enum StringFactory { + static func makeString(x: String) -> String { return x } +} + +enum Letters { + case a + case b + case c +} + +let MyConstantString = "MyConstant" +let MyConstantBool = true + +func testGlobalLookup() { + @TupleBuilder var x1 { + #^GLOBAL_LOOKUP^# + // GLOBAL_LOOKUP: Begin completions + // GLOBAL_LOOKUP: Decl[GlobalVar]/CurrModule: MyConstantString[#String#]; + // GLOBAL_LOOKUP: End completions + } + + @TupleBuilder var x2 { + if true { + #^GLOBAL_LOOKUP_IN_IF_BODY?check=GLOBAL_LOOKUP^# + } + } + + @TupleBuilder var x3 { + if { + #^GLOBAL_LOOKUP_IN_IF_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP^# + } + } + + @TupleBuilder var x4 { + guard else { + #^GLOBAL_LOOKUP_IN_GUARD_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP^# + } + } + + @TupleBuilder var x5 { + "hello: \(#^GLOBAL_LOOKUP_IN_STRING_LITERAL^#)" +// GLOBAL_LOOKUP_IN_STRING_LITERAL: Begin completions +// GLOBAL_LOOKUP_IN_STRING_LITERAL: Decl[GlobalVar]/CurrModule/TypeRelation[Convertible]: MyConstantString[#String#]; +// GLOBAL_LOOKUP_IN_STRING_LITERAL: End completions + } + + @TupleBuilder var x5 { + if #^GLOBAL_LOOKUP_IN_IF_CONDITION^# { +// GLOBAL_LOOKUP_IN_IF_CONDITION: Begin completions +// GLOBAL_LOOKUP_IN_IF_CONDITION: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: MyConstantBool[#Bool#]; name=MyConstantBool +// GLOBAL_LOOKUP_IN_IF_CONDITION: End completions + } + } +} + +func testStaticMemberLookup() { + @TupleBuilder var x1 { + StringFactory.#^COMPLETE_STATIC_MEMBER^# + // COMPLETE_STATIC_MEMBER: Begin completions + // COMPLETE_STATIC_MEMBER: Decl[StaticMethod]/CurrNominal: makeString({#x: String#})[#String#]; + // COMPLETE_STATIC_MEMBER: End completions + } + + @TupleBuilder var x2 { + if true { + StringFactory.#^COMPLETE_STATIC_MEMBER_IN_IF_BODY?check=COMPLETE_STATIC_MEMBER^# + } + } + + @TupleBuilder var x3 { + "hello: \(StringFactory.#^COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL?check=COMPLETE_STATIC_MEMBER;xfail=rdar78015646^#)" + } +} + +struct FooStruct { + var instanceVar : Int + init(_: Int = 0) { } + func boolGen() -> Bool { return false } + func intGen() -> Int { return 1 } +} + +func testPatternMatching() { + @TupleBuilder var x1 { + let x = Letters.b + if case .#^COMPLETE_PATTERN_MATCHING_IN_IF?check=COMPLETE_CASE^# = x { +// COMPLETE_CASE: Begin completions +// COMPLETE_CASE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: a[#Letters#]; +// COMPLETE_CASE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: b[#Letters#]; +// COMPLETE_CASE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: c[#Letters#]; +// COMPLETE_CASE: End completions + } + } + + @TupleBuilder var x2 { + let x = Letters.a + switch x { + case .#^COMPLETE_CASE_IN_SWITCH?check=COMPLETE_CASE^#: + break + } + } + + @TupleBuilder var x3 { + let x: FooStruct? = FooStruct() + guard case .#^GUARD_CASE_PATTERN_1?check=OPTIONAL_FOOSTRUCT^# = x {} + // OPTIONAL_FOOSTRUCT: Begin completions, 2 items + // OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional#]; name=none + // OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: some({#FooStruct#})[#Optional#]; name=some() + // OPTIONAL_FOOSTRUCT: End completions + } + + @TupleBuilder var x4 { + let x: FooStruct? = FooStruct() + guard case .#^GUARD_CASE_PATTERN_2?check=OPTIONAL_FOOSTRUCT^#some() = x {} + } +} + +func testCompleteFunctionArgumentLabels() { + @TupleBuilder var x1 { + StringFactory.makeString(#^FUNCTION_ARGUMENT_LABEL^#) + // FUNCTION_ARGUMENT_LABEL: Begin completions, 1 item + // FUNCTION_ARGUMENT_LABEL: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#x: String#}[')'][#String#]; + // FUNCTION_ARGUMENT_LABEL: End completions + } +} + +func testCompleteFunctionArgument() { + @TupleBuilder var x1 { + StringFactory.makeString(x: #^ARGUMENT_LOOKUP^#) + // ARGUMENT_LOOKUP: Begin completions + // ARGUMENT_LOOKUP: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: MyConstantString[#String#]; + // ARGUMENT_LOOKUP: End completions + } + + @TupleBuilder var x2 { + if true { + StringFactory.makeString(x: #^ARGUMENT_LOOKUP_IN_IF_BODY?check=ARGUMENT_LOOKUP^#) + } + } +} + +func testCompleteErrorTypeInCatch() { + enum Error4 : Error { + case E1 + case E2(Int32) + } + @TupleBuilder var x1 { + do {} catch Error4.#^CATCH2^# + } +// CATCH2: Begin completions +// CATHC2-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E1[#Error4#]; name=E1 +// CATHC2-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]; name=E2(Int32) +// CATCH2: End completions +} diff --git a/test/IDE/complete_init.swift b/test/IDE/complete_init.swift index e7882e535902b..48a1e7a268b94 100644 --- a/test/IDE/complete_init.swift +++ b/test/IDE/complete_init.swift @@ -144,5 +144,5 @@ typealias CAlias = C var CAliasInstance = CAlias(#^ALIAS_CONSTRUCTOR_0^# // rdar://18586415 -// ALIAS_CONSTRUCTOR_0: Decl[Constructor]/CurrNominal: ['(']{#x: A#}[')'][#CAlias#]; -// ALIAS_CONSTRUCTOR_0: Decl[Constructor]/CurrNominal: ['(']{#y: A#}[')'][#CAlias#]; +// ALIAS_CONSTRUCTOR_0: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#x: A#}[')'][#CAlias#]; +// ALIAS_CONSTRUCTOR_0: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#y: A#}[')'][#CAlias#]; diff --git a/test/IDE/complete_init_inherited.swift b/test/IDE/complete_init_inherited.swift index 404737a94fe5e..d1088c1a42824 100644 --- a/test/IDE/complete_init_inherited.swift +++ b/test/IDE/complete_init_inherited.swift @@ -15,9 +15,9 @@ class A { } // TEST_A: Begin completions -// TEST_A-NEXT: Decl[Constructor]/CurrNominal: ({#int: Int#})[#A#]{{; name=.+$}} -// TEST_A-NEXT: Decl[Constructor]/CurrNominal: ({#double: Double#})[#A#]{{; name=.+$}} -// TEST_A-NEXT: Decl[Constructor]/CurrNominal: ({#float: Float#})[#A#]{{; name=.+$}} +// TEST_A-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#int: Int#})[#A#]{{; name=.+$}} +// TEST_A-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#double: Double#})[#A#]{{; name=.+$}} +// TEST_A-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#float: Float#})[#A#]{{; name=.+$}} // TEST_A-NEXT: Keyword[self]/CurrNominal: .self[#A.Type#]; name=self // TEST_A-NEXT: Keyword/CurrNominal: .Type[#A.Type#]; name=Type // TEST_A-NEXT: End completions @@ -29,9 +29,9 @@ class B : A { } // TEST_B: Begin completions -// TEST_B-NEXT: Decl[Constructor]/CurrNominal: ({#int: Int#})[#B#]{{; name=.+$}} -// TEST_B-NEXT: Decl[Constructor]/CurrNominal: ({#double: Double#})[#B#]{{; name=.+$}} -// TEST_B-NEXT: Decl[Constructor]/Super: ({#float: Float#})[#A#]{{; name=.+$}} +// TEST_B-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#int: Int#})[#B#]{{; name=.+$}} +// TEST_B-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#double: Double#})[#B#]{{; name=.+$}} +// TEST_B-NEXT: Decl[Constructor]/Super/Flair[ArgLabels]: ({#float: Float#})[#A#]{{; name=.+$}} // TEST_B-NEXT: Keyword[self]/CurrNominal: .self[#B.Type#]; name=self // TEST_B-NEXT: Keyword/CurrNominal: .Type[#B.Type#]; name=Type // TEST_B-NEXT: End completions @@ -47,8 +47,8 @@ class C : B { } // TEST_C: Begin completions -// TEST_C-NEXT: Decl[Constructor]/CurrNominal: ({#int: Int#})[#C#]{{; name=.+$}} -// TEST_C-NEXT: Decl[Constructor]/CurrNominal: ({#c: C#})[#C#]{{; name=.+$}} +// TEST_C-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#int: Int#})[#C#]{{; name=.+$}} +// TEST_C-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#c: C#})[#C#]{{; name=.+$}} // TEST_C-NEXT: Keyword[self]/CurrNominal: .self[#C.Type#]; name=self // TEST_C-NEXT: Keyword/CurrNominal: .Type[#C.Type#]; name=Type // TEST_C-NEXT: End completions @@ -65,20 +65,20 @@ class D : C { } // TEST_D: Begin completions -// TEST_D-NEXT: Decl[Constructor]/CurrNominal: ({#d: D#})[#D#]{{; name=.+$}} -// TEST_D-NEXT: Decl[Constructor]/CurrNominal: ({#int: Int#})[#D#]{{; name=.+$}} -// TEST_D-NEXT: Decl[Constructor]/Super: ({#c: C#})[#C#]{{; name=.+$}} +// TEST_D-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#d: D#})[#D#]{{; name=.+$}} +// TEST_D-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#int: Int#})[#D#]{{; name=.+$}} +// TEST_D-NEXT: Decl[Constructor]/Super/Flair[ArgLabels]: ({#c: C#})[#C#]{{; name=.+$}} // TEST_D-NEXT: Keyword[self]/CurrNominal: .self[#D.Type#]; name=self // TEST_D-NEXT: Keyword/CurrNominal: .Type[#D.Type#]; name=Type // TEST_D-NEXT: End completions -// TEST_D_DOT: Decl[Constructor]/CurrNominal: init({#d: D#})[#D#]; name=init(d: D) -// TEST_D_DOT-NEXT: Decl[Constructor]/CurrNominal: init({#int: Int#})[#D#]; name=init(int: Int) -// TEST_D_DOT-NEXT: Decl[Constructor]/Super: init({#c: C#})[#C#]; name=init(c: C) +// TEST_D_DOT: Decl[Constructor]/CurrNominal: init({#d: D#})[#D#]; name=init(d:) +// TEST_D_DOT-NEXT: Decl[Constructor]/CurrNominal: init({#int: Int#})[#D#]; name=init(int:) +// TEST_D_DOT-NEXT: Decl[Constructor]/Super: init({#c: C#})[#C#]; name=init(c:) -// TEST_D_PAREN: Decl[Constructor]/CurrNominal: ['(']{#d: D#}[')'][#D#]; name=d: D -// TEST_D_PAREN-NEXT: Decl[Constructor]/CurrNominal: ['(']{#int: Int#}[')'][#D#]; name=int: Int -// TEST_D_PAREN-NEXT: Decl[Constructor]/Super: ['(']{#c: C#}[')'][#C#]; name=c: C +// TEST_D_PAREN: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#d: D#}[')'][#D#]; name=d: +// TEST_D_PAREN-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#int: Int#}[')'][#D#]; name=int: +// TEST_D_PAREN-NEXT: Decl[Constructor]/Super/Flair[ArgLabels]: ['(']{#c: C#}[')'][#C#]; name=c: func testA() { A#^TEST_A^# @@ -108,8 +108,8 @@ class R74233797Derived : R74233797Base { func testR74233797() { R74233797Derived(#^METATYPE_CONVERSION^#) // METATYPE_CONVERSION: Begin completions -// METATYPE_CONVERSION-DAG: Decl[Constructor]/CurrNominal: ['(']{#sub: Bool#}[')'][#R74233797Derived#]; -// METATYPE_CONVERSION-DAG: Decl[Constructor]/CurrNominal: ['('][')'][#R74233797Derived#]; -// METATYPE_CONVERSION-DAG: Decl[Constructor]/Super: ['(']{#(test): Bool#}[')'][#R74233797Base#]; +// METATYPE_CONVERSION-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#sub: Bool#}[')'][#R74233797Derived#]; +// METATYPE_CONVERSION-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#R74233797Derived#]; +// METATYPE_CONVERSION-DAG: Decl[Constructor]/Super/Flair[ArgLabels]: ['(']{#(test): Bool#}[')'][#R74233797Base#]; // METATYPE_CONVERSION: End completions } diff --git a/test/IDE/complete_inout.swift b/test/IDE/complete_inout.swift new file mode 100644 index 0000000000000..b0c2c53492ec1 --- /dev/null +++ b/test/IDE/complete_inout.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +struct Bar() { + init?(withInout: inout Int) {} + init?(withPointer: UnsafePointer) {} +} + +struct Foo { + var myInt: Int + + func bar() { + let context = Bar(wihtInout: &self.#^COMPLETE_INOUT?check=CHECK^#) + let context = Bar(withPointer: &self.#^COMPLETE_POINTER?check=CHECK^#) + } +} + +// CHECK: Decl[InstanceVar]/CurrNominal: myInt[#Int#]; name=myInt \ No newline at end of file diff --git a/test/IDE/complete_invalid_result_builder.swift b/test/IDE/complete_invalid_result_builder.swift index c5e67f37d4543..117495476b737 100644 --- a/test/IDE/complete_invalid_result_builder.swift +++ b/test/IDE/complete_invalid_result_builder.swift @@ -55,6 +55,6 @@ test { } // MYENUM_MEMBERS: Begin completions -// MYENUM_MEMBERS-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: a[#MyEnum#]; name=a -// MYENUM_MEMBERS-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: b[#MyEnum#]; name=b +// MYENUM_MEMBERS-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: a[#MyEnum#]; name=a +// MYENUM_MEMBERS-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: b[#MyEnum#]; name=b // MYENUM_MEMBERS: End completions diff --git a/test/IDE/complete_keywords.swift b/test/IDE/complete_keywords.swift index ccfbf964052bd..a7a8748e3f544 100644 --- a/test/IDE/complete_keywords.swift +++ b/test/IDE/complete_keywords.swift @@ -26,6 +26,7 @@ // KW_DECL-DAG: Keyword/None: override{{; name=.+$}} // KW_DECL-DAG: Keyword/None: postfix{{; name=.+$}} // KW_DECL-DAG: Keyword/None: prefix{{; name=.+$}} +// KW_DECL_DAG: Keyword[precedencegroup]: precedencegroup{{; name=.+$}} // KW_DECL-DAG: Keyword[private]/None: private{{; name=.+$}} // KW_DECL-DAG: Keyword[protocol]/None: protocol{{; name=.+$}} // KW_DECL-DAG: Keyword[public]/None: public{{; name=.+$}} @@ -39,38 +40,185 @@ // KW_DECL-DAG: Keyword/None: weak{{; name=.+$}} // KW_DECL: End completions +// KW_DECL_PROTOCOL: Begin completions +// KW_DECL_PROTOCOL-DAG: Keyword[class]/None/Flair[RareKeyword]: class{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: convenience{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[deinit]/None: deinit{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: dynamic{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[enum]/None/Flair[RareKeyword]: enum{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[extension]/None/Flair[RareKeyword]: extension{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: final{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[func]/None: func{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[import]/None/Flair[RareKeyword]: import{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: infix{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[init]/None: init{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[internal]/None: internal{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: lazy{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[let]/None: let{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: mutating{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: nonmutating{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[operator]/None/Flair[RareKeyword]: operator{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: optional{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: override{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: postfix{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: prefix{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[precedencegroup]/None/Flair[RareKeyword]: precedencegroup{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[private]/None: private{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[protocol]/None/Flair[RareKeyword]: protocol{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[public]/None: public{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: required{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[static]/None: static{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[struct]/None/Flair[RareKeyword]: struct{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[subscript]/None: subscript{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[typealias]/None: typealias{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: unowned{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword[var]/None: var{{; name=.+$}} +// KW_DECL_PROTOCOL-DAG: Keyword/None: weak{{; name=.+$}} +// KW_DECL_PROTOCOL: End completions + +// KW_DECL_TYPECONTEXT: Begin completions +// KW_DECL_TYPECONTEXT-DAG: Keyword[class]/None: class{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: convenience{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[deinit]/None: deinit{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: dynamic{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[enum]/None: enum{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[extension]/None/Flair[RareKeyword]: extension{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: final{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[func]/None: func{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[import]/None/Flair[RareKeyword]: import{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: infix{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[init]/None: init{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[internal]/None: internal{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: lazy{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[let]/None: let{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: mutating{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: nonmutating{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[operator]/None/Flair[RareKeyword]: operator{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: optional{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: override{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: postfix{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: prefix{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[precedencegroup]/None/Flair[RareKeyword]: precedencegroup{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[private]/None: private{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[protocol]/None/Flair[RareKeyword]: protocol{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[public]/None: public{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: required{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[static]/None: static{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[struct]/None: struct{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[subscript]/None: subscript{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[typealias]/None: typealias{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: unowned{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword[var]/None: var{{; name=.+$}} +// KW_DECL_TYPECONTEXT-DAG: Keyword/None: weak{{; name=.+$}} +// KW_DECL_TYPECONTEXT: End completions + + +// KW_DECL_STMT_TOPLEVEL: Begin completions +// +// Declaration keywords. +// +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[class]/None: class{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: convenience{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[deinit]/None: deinit{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: dynamic{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[enum]/None: enum{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[extension]/None: extension{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: final{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[func]/None: func{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[import]/None: import{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: infix{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[init]/None: init{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[internal]/None: internal{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: lazy{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[let]/None: let{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: mutating{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: nonmutating{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[operator]/None: operator{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: optional{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: override{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: postfix{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[precedencegroup]/None: precedencegroup{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: prefix{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[private]/None: private{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[protocol]/None: protocol{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[public]/None: public{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: required{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[static]/None: static{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[struct]/None: struct{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[subscript]/None: subscript{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[typealias]/None: typealias{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: unowned{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[var]/None: var{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword/None: weak{{; name=.+$}} +// +// Statement keywords. +// +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[if]/None: if{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[do]/None: do{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[else]/None: else{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[for]/None: for{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[in]/None: in{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[while]/None: while{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[break]/None: break{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[continue]/None: continue{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[fallthrough]/None: fallthrough{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[switch]/None: switch{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[case]/None: case{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[default]/None: default{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[where]/None: where{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[catch]/None: catch{{; name=.+$}} +// +// Misc. +// +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[throw]/None: throw{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[try]/None: try{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[try]/None: try!{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[try]/None: try?{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[#function]/None{{(/TypeRelation\[Identical\])?}}: #function[#String#]{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[#file]/None{{(/TypeRelation\[Identical\])?}}: #file[#String#]{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[#line]/None{{(/TypeRelation\[Identical\])?}}: #line[#Int#]{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Keyword[#column]/None{{(/TypeRelation\[Identical\])?}}: #column[#Int#]{{; name=.+$}} +// +// Literals +// +// KW_DECL_STMT_TOPLEVEL-DAG: Literal[Boolean]/None: false[#Bool#]{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Literal[Boolean]/None: true[#Bool#]{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL-DAG: Literal[Nil]/None: nil{{; name=.+$}} +// KW_DECL_STMT_TOPLEVEL: End completions + // KW_DECL_STMT: Begin completions // // Declaration keywords. // -// KW_DECL_STMT-DAG: Keyword[class]/None: class{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: convenience{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[deinit]/None: deinit{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: dynamic{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[enum]/None: enum{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[extension]/None: extension{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: final{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[class]/None/Flair[RareKeyword]: class{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: convenience{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[deinit]/None/Flair[RareKeyword]: deinit{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: dynamic{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[enum]/None/Flair[RareKeyword]: enum{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[extension]/None/Flair[RareKeyword]: extension{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: final{{; name=.+$}} // KW_DECL_STMT-DAG: Keyword[func]/None: func{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[import]/None: import{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: infix{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[init]/None: init{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[internal]/None: internal{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: lazy{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[import]/None/Flair[RareKeyword]: import{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: infix{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[init]/None/Flair[RareKeyword]: init{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[internal]/None/Flair[RareKeyword]: internal{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: lazy{{; name=.+$}} // KW_DECL_STMT-DAG: Keyword[let]/None: let{{; name=.+$}} // KW_DECL_STMT-DAG: Keyword/None: mutating{{; name=.+$}} // KW_DECL_STMT-DAG: Keyword/None: nonmutating{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[operator]/None: operator{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: optional{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: override{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: postfix{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: prefix{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[private]/None: private{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[protocol]/None: protocol{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[public]/None: public{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword/None: required{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[static]/None: static{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[struct]/None: struct{{; name=.+$}} -// KW_DECL_STMT-DAG: Keyword[subscript]/None: subscript{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[operator]/None/Flair[RareKeyword]: operator{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: optional{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: override{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: postfix{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[precedencegroup]/None/Flair[RareKeyword]: precedencegroup{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: prefix{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[private]/None/Flair[RareKeyword]: private{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[protocol]/None/Flair[RareKeyword]: protocol{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[public]/None/Flair[RareKeyword]: public{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword/None/Flair[RareKeyword]: required{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[static]/None/Flair[RareKeyword]: static{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[struct]/None/Flair[RareKeyword]: struct{{; name=.+$}} +// KW_DECL_STMT-DAG: Keyword[subscript]/None/Flair[RareKeyword]: subscript{{; name=.+$}} // KW_DECL_STMT-DAG: Keyword[typealias]/None: typealias{{; name=.+$}} // KW_DECL_STMT-DAG: Keyword/None: unowned{{; name=.+$}} // KW_DECL_STMT-DAG: Keyword[var]/None: var{{; name=.+$}} @@ -154,7 +302,7 @@ // KW_EXPR_NEG-NOT: Keyword{{.*}}break // KW_EXPR_NEG: End completions -#^TOP_LEVEL_1?check=KW_DECL_STMT;check=KW_NO_RETURN^# +#^TOP_LEVEL_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^# for _ in 1...10 { #^TOP_LEVEL_2?check=KW_DECL_STMT;check=KW_NO_RETURN^# @@ -225,43 +373,43 @@ struct InInit { } struct InStruct { - #^IN_NOMINAL_DECL_1?check=KW_DECL^# + #^IN_NOMINAL_DECL_1?check=KW_DECL_TYPECONTEXT^# } enum InEnum { - #^IN_NOMINAL_DECL_2?check=KW_DECL^# + #^IN_NOMINAL_DECL_2?check=KW_DECL_TYPECONTEXT^# } class InClass { - #^IN_NOMINAL_DECL_3?check=KW_DECL^# + #^IN_NOMINAL_DECL_3?check=KW_DECL_TYPECONTEXT^# } protocol InProtocol { - #^IN_NOMINAL_DECL_4?check=KW_DECL^# + #^IN_NOMINAL_DECL_4?check=KW_DECL_PROTOCOL^# } struct AfterOtherKeywords1 { - public #^IN_NOMINAL_DECL_5?check=KW_DECL^# + public #^IN_NOMINAL_DECL_5?check=KW_DECL_TYPECONTEXT^# } struct AfterOtherKeywords2 { - mutating #^IN_NOMINAL_DECL_6?check=KW_DECL^# + mutating #^IN_NOMINAL_DECL_6?check=KW_DECL_TYPECONTEXT^# } class AfterOtherKeywords3 { - override #^IN_NOMINAL_DECL_7?check=KW_DECL^# + override #^IN_NOMINAL_DECL_7?check=KW_DECL_TYPECONTEXT^# } class AfterOtherKeywords4 { - public override #^IN_NOMINAL_DECL_8?check=KW_DECL^# + public override #^IN_NOMINAL_DECL_8?check=KW_DECL_TYPECONTEXT^# } extension InStruct { - #^IN_NOMINAL_DECL_9?check=KW_DECL^# + #^IN_NOMINAL_DECL_9?check=KW_DECL_TYPECONTEXT^# } extension InProtocol { - #^IN_NOMINAL_DECL_10?check=KW_DECL^# + #^IN_NOMINAL_DECL_10?check=KW_DECL_TYPECONTEXT^# } class SuperSuperClass { @@ -344,3 +492,15 @@ func testContextualType() { // CONTEXT_STATICSTRING-DAG: Keyword[#dsohandle]/None: #dsohandle[#UnsafeRawPointer#]; name=#dsohandle // CONTEXT_STATICSTRING: End completions } + +class Base { + func foo() {} +} +class Derivied: Base { + override func foo() { + #^OVERRIDE^# +// OVERRIDE: Begin completions +// OVERRIDE-DAG: Keyword[super]/CurrNominal/Flair[CommonKeyword]: super[#Base#]; name=super +// OVERRIDE: End completions + } +} diff --git a/test/IDE/complete_literal.swift b/test/IDE/complete_literal.swift index 836885cee0f92..c8342ce22915e 100644 --- a/test/IDE/complete_literal.swift +++ b/test/IDE/complete_literal.swift @@ -41,16 +41,16 @@ } // LITERAL4: Begin completions -// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: withCString({#(body): (UnsafePointer) throws -> Result##(UnsafePointer) throws -> Result#})[' rethrows'][#Result#]; name=withCString(body: (UnsafePointer) throws -> Result) rethrows{{$}} +// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: withCString({#(body): (UnsafePointer) throws -> Result##(UnsafePointer) throws -> Result#})[' rethrows'][#Result#]; name=withCString(:){{$}} // FIXME: we should show the qualified String.Index type. // rdar://problem/20788802 // LITERAL4-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: startIndex[#String.Index#]; name=startIndex{{$}} // LITERAL4-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: endIndex[#String.Index#]; name=endIndex{{$}} -// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: append({#(c): Character#})[#Void#]; name=append(c: Character){{$}} -// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: append({#contentsOf: Sequence#})[#Void#]; name=append(contentsOf: Sequence){{$}} -// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: insert({#contentsOf: Collection#}, {#at: String.Index#})[#Void#]; name=insert(contentsOf: Collection, at: String.Index){{$}} -// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: remove({#at: String.Index#})[#Character#]; name=remove(at: String.Index){{$}} +// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: append({#(c): Character#})[#Void#]; name=append(:){{$}} +// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: append({#contentsOf: Sequence#})[#Void#]; name=append(contentsOf:){{$}} +// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: insert({#contentsOf: Collection#}, {#at: String.Index#})[#Void#]; name=insert(contentsOf:at:){{$}} +// LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: remove({#at: String.Index#})[#Character#]; name=remove(at:){{$}} // LITERAL4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: lowercased()[#String#]; name=lowercased(){{$}} func giveMeAString() -> Int { diff --git a/test/IDE/complete_modulename.swift b/test/IDE/complete_modulename.swift new file mode 100644 index 0000000000000..c58c9dfe7eb6a --- /dev/null +++ b/test/IDE/complete_modulename.swift @@ -0,0 +1,72 @@ +// BEGIN _Helper.swift +public struct HelperTy {} +public func helperFunc() {} + +// BEGIN MyModule.swift +@_exported import _Helper + +public struct MyModuleTy {} +public func myModuleFunc() {} + +// BEGIN _Explicit.swift +public struct HiddenTy {} +public func hiddenFunc() {} + +// BEGIN App.swift + +import MyModule +import _Explicit + +func test() { + let _ = #^EXPR^# + + func test() -> #^TYPE^# +} + +// EXPR: Begin completion + +// EXPR-NOT: _Concurrency[#Module#] +// EXPR-NOT: SwiftShims[#Module#] +// EXPR-NOT: SwiftOnoneSupport[#Module#] +// EXPR-NOT: _Helper[#Module#] + +// EXPR-DAG: Decl[Module]/None: swift_ide_test[#Module#]; name=swift_ide_test +// EXPR-DAG: Decl[Module]/None/IsSystem: Swift[#Module#]; name=Swift +// EXPR-DAG: Decl[Module]/None: MyModule[#Module#]; name=MyModule +// EXPR-DAG: Decl[Module]/None: _Explicit[#Module#]; name=_Explicit +// EXPR-DAG: Decl[Struct]/OtherModule[MyModule]: MyModuleTy[#MyModuleTy#]; name=MyModuleTy +// EXPR-DAG: Decl[Struct]/OtherModule[_Explicit]: HiddenTy[#HiddenTy#]; name=HiddenTy +// EXPR-DAG: Decl[Struct]/OtherModule[_Helper]: HelperTy[#HelperTy#]; name=HelperTy +// EXPR-DAG: Decl[FreeFunction]/OtherModule[MyModule]: myModuleFunc()[#Void#]; name=myModuleFunc() +// EXPR-DAG: Decl[FreeFunction]/OtherModule[_Explicit]: hiddenFunc()[#Void#]; name=hiddenFunc() +// EXPR-DAG: Decl[FreeFunction]/OtherModule[_Helper]: helperFunc()[#Void#]; name=helperFunc() + +// EXPR: End completions + +// TYPE: Begin completion + +// TYPE-NOT: _Concurrency[#Module#] +// TYPE-NOT: SwiftShims[#Module#] +// TYPE-NOT: SwiftOnoneSupport[#Module#] +// TYPE-NOT: _Helper[#Module#] + +// TYPE-DAG: Decl[Module]/None: swift_ide_test[#Module#]; name=swift_ide_test +// TYPE-DAG: Decl[Module]/None/IsSystem: Swift[#Module#]; name=Swift +// TYPE-DAG: Decl[Module]/None: MyModule[#Module#]; name=MyModule +// TYPE-DAG: Decl[Module]/None: _Explicit[#Module#]; name=_Explicit +// TYPE-DAG: Decl[Struct]/OtherModule[MyModule]: MyModuleTy[#MyModuleTy#]; name=MyModuleTy +// TYPE-DAG: Decl[Struct]/OtherModule[_Explicit]: HiddenTy[#HiddenTy#]; name=HiddenTy +// TYPE-DAG: Decl[Struct]/OtherModule[_Helper]: HelperTy[#HelperTy#]; name=HelperTy + +// TYPE: End completions + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %empty-directory(%t/Modules) +// RUN: %target-swift-frontend -emit-module -module-name _Helper -o %t/Modules %t/_Helper.swift +// RUN: %target-swift-frontend -emit-module -module-name MyModule -o %t/Modules %t/MyModule.swift -I %t/Modules +// RUN: %target-swift-frontend -emit-module -module-name _Explicit -o %t/Modules %t/_Explicit.swift -I %t/Modules + +// RUN: %empty-directory(%t/Out) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/App.swift -filecheck %raw-FileCheck -completion-output-dir %t/Out -I %t/Modules diff --git a/test/IDE/complete_multibracestmt.swift b/test/IDE/complete_multibracestmt.swift index 2761cc9aff4fb..b0c8300f74508 100644 --- a/test/IDE/complete_multibracestmt.swift +++ b/test/IDE/complete_multibracestmt.swift @@ -33,7 +33,7 @@ func test(pred: Bool) { } // CHECK: Begin completions, 3 items -// CHECK-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#E#]; name=bar -// CHECK-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#E#]; name=foo -// CHECK-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): E#})[#(into: inout Hasher) -> Void#]; name=hash(self: E) +// CHECK-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#E#]; name=bar +// CHECK-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: foo[#E#]; name=foo +// CHECK-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): E#})[#(into: inout Hasher) -> Void#]; name=hash(:) // CHECK: End completions diff --git a/test/IDE/complete_multifile.swift b/test/IDE/complete_multifile.swift index 76ad114431658..b6003899171c7 100644 --- a/test/IDE/complete_multifile.swift +++ b/test/IDE/complete_multifile.swift @@ -51,7 +51,7 @@ struct HasWrapped { func testStructDefaultInit() { Point(#^POINT_PAREN^# // POINT_PAREN: Begin completions, 1 items -// POINT_PAREN-DAG: Decl[Constructor]/CurrNominal: ['(']{#x: Int#}, {#y: Int#}[')'][#Point#]; +// POINT_PAREN-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#x: Int#}, {#y: Int#}[')'][#Point#]; // POINT_PAREN: End completions func sync() {} Point.#^POINT_DOT^# diff --git a/test/IDE/complete_multiple_trailingclosure.swift b/test/IDE/complete_multiple_trailingclosure.swift index 71f4d4fe9f5a7..b365478be0431 100644 --- a/test/IDE/complete_multiple_trailingclosure.swift +++ b/test/IDE/complete_multiple_trailingclosure.swift @@ -26,11 +26,11 @@ func testGlobalFunc() { { 1 } #^GLOBALFUNC_SAMELINE^# #^GLOBALFUNC_NEWLINE^# // GLOBALFUNC_SAMELINE: Begin completions, 1 items -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; // GLOBALFUNC_SAMELINE: End completions // GLOBALFUNC_NEWLINE: Begin completions -// FIXME-GLOBALFUNC_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; +// FIXME-GLOBALFUNC_NEWLINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; // GLOBALFUNC_NEWLINE: End completions globalFunc1() @@ -55,15 +55,15 @@ func testMethod(value: MyStruct) { } #^METHOD_SAMELINE^# #^METHOD_NEWLINE^# // METHOD_SAMELINE: Begin completions, 4 items -// METHOD_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)? {|}#}[#(() -> String)?#]; +// METHOD_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: (() -> String)? {|}#}[#(() -> String)?#]; // METHOD_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .enumFunc()[#Void#]; // METHOD_SAMELINE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']+ {#SimpleEnum#}[#SimpleEnum#]; // METHOD_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#SimpleEnum#]; // METHOD_SAMELINE: End completions // METHOD_NEWLINE: Begin completions -// FIXME-METHOD_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)? {|}#}[#(() -> String)?#]; -// METHOD_NEWLINE-DAG: Keyword[class]/None: class; +// FIXME-METHOD_NEWLINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: (() -> String)? {|}#}[#(() -> String)?#]; +// METHOD_NEWLINE-DAG: Keyword[class]/None/Flair[RareKeyword]: class; // METHOD_NEWLINE-DAG: Keyword[if]/None: if; // METHOD_NEWLINE-DAG: Keyword[try]/None: try; // METHOD_NEWLINE-DAG: Decl[LocalVar]/Local: value[#MyStruct#]; name=value @@ -86,16 +86,16 @@ func testOverloadedInit() { #^INIT_OVERLOADED_NEWLINE^# // INIT_OVERLOADED_SAMELINE: Begin completions, 4 items -// INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; -// INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {|}#}[#() -> String#]; +// INIT_OVERLOADED_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; +// INIT_OVERLOADED_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn3: () -> String {|}#}[#() -> String#]; // INIT_OVERLOADED_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; // INIT_OVERLOADED_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#TestStruct#]; // INIT_OVERLOADED_SAMELINE: End completions // INIT_OVERLOADED_NEWLINE: Begin completions -// FIXME-INIT_OVERLOADED_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; -// FIXME-INIT_OVERLOADED_NEWLINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {|}#}[#() -> String#]; -// INIT_OVERLOADED_NEWLINE-DAG: Keyword[class]/None: class; +// FIXME-INIT_OVERLOADED_NEWLINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; +// FIXME-INIT_OVERLOADED_NEWLINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn3: () -> String {|}#}[#() -> String#]; +// INIT_OVERLOADED_NEWLINE-DAG: Keyword[class]/None/Flair[RareKeyword]: class; // INIT_OVERLOADED_NEWLINE-DAG: Keyword[if]/None: if; // INIT_OVERLOADED_NEWLINE-DAG: Keyword[try]/None: try; // INIT_OVERLOADED_NEWLINE-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct @@ -113,16 +113,16 @@ func testOptionalInit() { #^INIT_OPTIONAL_NEWLINE^# // INIT_OPTIONAL_SAMELINE: Begin completions, 4 items -// INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; -// INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {|}#}[#() -> String#]; +// INIT_OPTIONAL_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; +// INIT_OPTIONAL_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn3: () -> String {|}#}[#() -> String#]; // INIT_OPTIONAL_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; // INIT_OPTIONAL_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#TestStruct2#]; // INIT_OPTIONAL_SAMELINE: End completions // INIT_OPTIONAL_NEWLINE: Begin completions -// FIXME-INIT_OPTIONAL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; -// FIXME-INIT_OPTIONAL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {|}#}[#() -> String#]; -// INIT_OPTIONAL_NEWLINE-DAG: Keyword[class]/None: class; +// FIXME-INIT_OPTIONAL_NEWLINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; +// FIXME-INIT_OPTIONAL_NEWLINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn3: () -> String {|}#}[#() -> String#]; +// INIT_OPTIONAL_NEWLINE-DAG: Keyword[class]/None/Flair[RareKeyword]: class; // INIT_OPTIONAL_NEWLINE-DAG: Keyword[if]/None: if; // INIT_OPTIONAL_NEWLINE-DAG: Keyword[try]/None: try; // INIT_OPTIONAL_NEWLINE-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct @@ -141,11 +141,11 @@ func testOptionalInit() { #^INIT_REQUIRED_NEWLINE_1^# // INIT_REQUIRED_SAMELINE_1: Begin completions, 1 items -// INIT_REQUIRED_SAMELINE_1-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; +// INIT_REQUIRED_SAMELINE_1-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; // INIT_REQUIRED_SAMELINE_1: End completions // INIT_REQUIRED_NEWLINE_1: Begin completions -// FIXME-INIT_REQUIRED_NEWLINE_1-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; +// FIXME-INIT_REQUIRED_NEWLINE_1-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; // INIT_REQUIRED_NEWLINE_1: End completions // missing 'fn3'. @@ -157,11 +157,11 @@ func testOptionalInit() { #^INIT_REQUIRED_NEWLINE_2^# // INIT_REQUIRED_SAMELINE_2: Begin completions, 1 items -// INIT_REQUIRED_SAMELINE_2-DAG: Pattern/ExprSpecific: {#fn3: () -> String {|}#}[#() -> String#]; +// INIT_REQUIRED_SAMELINE_2-DAG: Pattern/Local/Flair[ArgLabels]: {#fn3: () -> String {|}#}[#() -> String#]; // INIT_REQUIRED_SAMELINE_2: End completions // INIT_REQUIRED_NEWLINE_2: Begin completions -// FIXME-INIT_REQUIRED_NEWLINE_2-DAG: Pattern/ExprSpecific: {#fn3: () -> String {|}#}[#() -> String#]; +// FIXME-INIT_REQUIRED_NEWLINE_2-DAG: Pattern/Local/Flair[ArgLabels]: {#fn3: () -> String {|}#}[#() -> String#]; // INIT_REQUIRED_NEWLINE_2: End completions // Call is completed. @@ -182,7 +182,7 @@ func testOptionalInit() { // INIT_REQUIRED_NEWLINE_3: Begin completions // INIT_REQUIRED_NEWLINE_3-NOT: name=fn2 // INIT_REQUIRED_NEWLINE_3-NOT: name=fn3 -// INIT_REQUIRED_NEWLINE_3-DAG: Keyword[class]/None: class; +// INIT_REQUIRED_NEWLINE_3-DAG: Keyword[class]/None/Flair[RareKeyword]: class; // INIT_REQUIRED_NEWLINE_3-DAG: Keyword[if]/None: if; // INIT_REQUIRED_NEWLINE_3-DAG: Keyword[try]/None: try; // INIT_REQUIRED_NEWLINE_3-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct @@ -207,7 +207,7 @@ func testFallbackPostfix() { // the stage at which we complete argument labels, so we can't rule it out for // now (SR-14450). // INIT_FALLBACK_1: Begin completions, 3 items -// INIT_FALLBACK_1-DAG: Pattern/ExprSpecific: {#arg3: () -> _ {|}#}[#() -> _#] +// INIT_FALLBACK_1-DAG: Pattern/Local/Flair[ArgLabels]: {#arg3: () -> _ {|}#}[#() -> _#] // INIT_FALLBACK_1-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; // INIT_FALLBACK_1-DAG: Keyword[self]/CurrNominal: .self[#MyStruct4#]; // INIT_FALLBACK_1: End completions @@ -230,14 +230,14 @@ struct TestNominalMember: P { #^MEMBERDECL_NEWLINE^# // MEMBERDECL_SAMELINE: Begin completions, 4 items -// MEMBERDECL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)? {|}#}[#(() -> String)?#]; name=fn2: (() -> String)? +// MEMBERDECL_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: (() -> String)? {|}#}[#(() -> String)?#]; name=fn2: // MEMBERDECL_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .enumFunc()[#Void#]; name=enumFunc() -// MEMBERDECL_SAMELINE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']+ {#SimpleEnum#}[#SimpleEnum#]; name=+ SimpleEnum +// MEMBERDECL_SAMELINE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']+ {#SimpleEnum#}[#SimpleEnum#]; name=+ // MEMBERDECL_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#SimpleEnum#]; name=self // MEMBERDECL_SAMELINE: End completions // MEMBERDECL_NEWLINE: Begin completions -// FIXME-MEMBERDECL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)? {|}#}[#(() -> String)?#]; name=fn2: (() -> String)? +// FIXME-MEMBERDECL_NEWLINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: (() -> String)? {|}#}[#(() -> String)?#]; name=fn2: // MEMBERDECL_NEWLINE-DAG: Keyword[enum]/None: enum; name=enum // MEMBERDECL_NEWLINE-DAG: Keyword[func]/None: func; name=func // MEMBERDECL_NEWLINE-DAG: Keyword[private]/None: private; name=private @@ -254,8 +254,8 @@ func testInitializedVarDecl() { #^INITIALIZED_VARDECL_NEWLINE^# // INITIALIZED_VARDECL_SAMELINE: Begin completions, 4 items // INITIALIZED_VARDECL_SAMELINE-NOT: localVal -// INITIALIZED_VARDECL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {|}#}[#() -> String#]; -// INITIALIZED_VARDECL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {|}#}[#() -> String#]; +// INITIALIZED_VARDECL_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> String {|}#}[#() -> String#]; +// INITIALIZED_VARDECL_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn3: () -> String {|}#}[#() -> String#]; // INITIALIZED_VARDECL_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; // INITIALIZED_VARDECL_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#TestStruct#]; // INITIALIZED_VARDECL_SAMELINE-NOT: localVal diff --git a/test/IDE/complete_multiple_trailingclosure_signatures.swift b/test/IDE/complete_multiple_trailingclosure_signatures.swift index b4d5ee7e4e454..2a5bd0ba1a18b 100644 --- a/test/IDE/complete_multiple_trailingclosure_signatures.swift +++ b/test/IDE/complete_multiple_trailingclosure_signatures.swift @@ -16,13 +16,13 @@ func test() { { 1 } #^GLOBALFUNC_SAMELINE^# // GLOBALFUNC_SAMELINE: Begin completions -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> Void {|}#}[#() -> Void#]; -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: (Int) -> Void {<#Int#> in|}#}[#(Int) -> Void#]; -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn4: (Int, String) -> Void {<#Int#>, <#String#> in|}#}[#(Int, String) -> Void#]; -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn5: (Int, String) -> Int {<#Int#>, <#String#> in|}#}[#(Int, String) -> Int#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn2: () -> Void {|}#}[#() -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn3: (Int) -> Void {<#Int#> in|}#}[#(Int) -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn4: (Int, String) -> Void {<#Int#>, <#String#> in|}#}[#(Int, String) -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn5: (Int, String) -> Int {<#Int#>, <#String#> in|}#}[#(Int, String) -> Int#]; // FIXME: recover names -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn6: (Int, String) -> Int {a, b in|}#}[#(Int, String) -> Int#]; -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn7: (inout Int) -> Void {<#inout Int#> in|}#}[#(inout Int) -> Void#]; -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn8: (Int...) -> Void {<#Int...#> in|}#}[#(Int...) -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn6: (Int, String) -> Int {a, b in|}#}[#(Int, String) -> Int#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn7: (inout Int) -> Void {<#inout Int#> in|}#}[#(inout Int) -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn8: (Int...) -> Void {<#Int...#> in|}#}[#(Int...) -> Void#]; // GLOBALFUNC_SAMELINE: End completions } diff --git a/test/IDE/complete_operators.swift b/test/IDE/complete_operators.swift index d77b275ab7f41..525f2b071ec4c 100644 --- a/test/IDE/complete_operators.swift +++ b/test/IDE/complete_operators.swift @@ -155,7 +155,7 @@ func testInfix7(x: S2?) { // S2_INFIX_OPTIONAL: Begin completions // S2_INFIX_OPTIONAL-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#{{.*}}#}[#Bool#] // S2_INFIX_OPTIONAL-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#{{.*}}#}[#Bool#] -// S2_INFIX_OPTIONAL-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: ?? {#S2#}[#S2#]; name=?? S2 +// S2_INFIX_OPTIONAL-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: ?? {#S2#}[#S2#]; name=?? // S2_INFIX_OPTIONAL: End completions // The equality operators don't come from equatable. // NEGATIVE_S2_INFIX_OPTIONAL-NOT: == {#S2 @@ -192,7 +192,7 @@ func testInfix11() { } // INFIX_11: Begin completions, 3 items -// INFIX_11-DAG: Decl[Constructor]/CurrNominal: ()[#S2#]; name=() +// INFIX_11-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#S2#]; name=() // INFIX_11-DAG: Keyword[self]/CurrNominal: .self[#S2.Type#]; name=self // INFIX_11-DAG: Keyword/CurrNominal: .Type[#S2.Type#]; name=Type // INFIX_11: End completions @@ -220,7 +220,7 @@ func testInfix15() { } // INFIX_15: Begin completions, 4 items // INFIX_15-NEXT: Decl[AssociatedType]/CurrNominal: .T; name=T -// INFIX_15-NEXT: Decl[InstanceMethod]/CurrNominal: .foo({#(self): P#})[#() -> S2#]; name=foo(self: P) +// INFIX_15-NEXT: Decl[InstanceMethod]/CurrNominal: .foo({#(self): P#})[#() -> S2#]; name=foo(:) // INFIX_15-NEXT: Keyword[self]/CurrNominal: .self[#T.Type#]; name=self // INFIX_15-NEXT: Keyword/CurrNominal: .Type[#T.Type#]; name=Type // INFIX_15: End completions @@ -230,7 +230,7 @@ func testInfix16() { } // INFIX_16: Begin completions, 2 items -// INFIX_16-NEXT: Decl[InstanceMethod]/CurrNominal: ({#(self): P#})[#() -> S2#]; name=(self: P) +// INFIX_16-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(self): P#})[#() -> S2#]; name=(:) // INFIX_16-NEXT: Keyword[self]/CurrNominal: .self[#(T) -> () -> S2#]; name=self // INFIX_16: End completions @@ -239,12 +239,12 @@ func testInfix17(x: Void) { } // VOID_OPERATORS: Begin completions -// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#()#}[#Bool#]; name=!= () -// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#()#}[#Bool#]; name=== () -// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#()#}[#Bool#]; name=<= () -// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#()#}[#Bool#]; name=>= () -// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#()#}[#Bool#]; name=< () -// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#()#}[#Bool#]; name=> () +// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#()#}[#Bool#]; name=!= +// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#()#}[#Bool#]; name=== +// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#()#}[#Bool#]; name=<= +// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#()#}[#Bool#]; name=>= +// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#()#}[#Bool#]; name=< +// VOID_OPERATORS-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#()#}[#Bool#]; name=> // VOID_OPERATORS: End completions func testInfix18(x: (S2, S2) { @@ -274,7 +274,7 @@ func testInfix22() { E.B#^INFIX_22^# } // INFIX_22: Begin completions, 2 items -// INFIX_22-NEXT: Pattern/CurrModule: ({#S2#})[#E#]; name=(S2) +// INFIX_22-NEXT: Pattern/CurrModule/Flair[ArgLabels]: ({#S2#})[#E#]; name=() // INFIX_22: End completions func testSpace(x: S2) { diff --git a/test/IDE/complete_override.swift b/test/IDE/complete_override.swift index c400204dcdc52..e15d1b45940e8 100644 --- a/test/IDE/complete_override.swift +++ b/test/IDE/complete_override.swift @@ -428,8 +428,8 @@ class C1 : P1 { } // ASSOC_TYPE1: Begin completions, 2 items -// ASSOC_TYPE1: Decl[AssociatedType]/Super: typealias T2 = {#(Type)#}; name=T2 = Type -// ASSOC_TYPE1: Decl[AssociatedType]/Super: typealias T3 = {#(Type)#}; name=T3 = Type +// ASSOC_TYPE1: Decl[AssociatedType]/Super: typealias T2 = {#(Type)#}; name=T2 = +// ASSOC_TYPE1: Decl[AssociatedType]/Super: typealias T3 = {#(Type)#}; name=T3 = class Deprecated1 { @available(*, deprecated) @@ -585,7 +585,7 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER1-DAG: Decl[InstanceVar]/Super: override var varDecl: Int; name=varDecl: Int // MODIFIER1-DAG: Decl[Constructor]/Super: override init(x: Int) {|}; name=init(x: Int) // MODIFIER1-DAG: Decl[Constructor]/Super: required init(a: Int) {|}; name=required init(a: Int) -// MODIFIER1-DAG: Decl[AssociatedType]/Super: typealias Assoc = {#(Type)#}; name=Assoc = Type +// MODIFIER1-DAG: Decl[AssociatedType]/Super: typealias Assoc = {#(Type)#}; name=Assoc = // MODIFIER1: End completions // MODIFIER2: Begin completions, 6 items @@ -610,7 +610,7 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER5: End completions // MODIFIER6: Begin completions, 1 items -// MODIFIER6-DAG: Decl[AssociatedType]/Super: Assoc = {#(Type)#}; name=Assoc = Type +// MODIFIER6-DAG: Decl[AssociatedType]/Super: Assoc = {#(Type)#}; name=Assoc = // MODIFIER6: End completions // MODIFIER7: Begin completions, 8 items @@ -757,3 +757,13 @@ class SynthesizedConformance5: SynthesizedConformance2 { // OVERRIDE_SYNTHESIZED_5-DAG: Decl[Constructor]/Super: required init(from decoder: Decoder) throws {|}; // FIXME: 'required init(from decoder: Decoder)' is suggested twice } + +protocol ProtocolSr14687 { + var value: Int { get } +} +struct StructSr14687: ProtocolSr14687 { + let foo = val, #^MULTI_VAR_DECL_OVERRIDE^# +// MULTI_VAR_DECL_OVERRIDE: Begin completions, 1 items +// MULTI_VAR_DECL_OVERRIDE-DAG: Decl[InstanceVar]/Super: value: Int; +// MULTI_VAR_DECL_OVERRIDE: End completions +} diff --git a/test/IDE/complete_pound_directive.swift b/test/IDE/complete_pound_directive.swift index 89705a8df2400..690ceb8dcaf62 100644 --- a/test/IDE/complete_pound_directive.swift +++ b/test/IDE/complete_pound_directive.swift @@ -15,11 +15,11 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONDITION_GLOBAL_2 -D FOO -D BAR | %FileCheck %s -check-prefix=CONDITION -check-prefix=WITHFLAG // POUND_DIRECTIVE: Begin completions, 7 items -// POUND_DIRECTIVE-DAG: Keyword[#sourceLocation]/None: sourceLocation(file: {#String#}, line: {#Int#}); name=sourceLocation(file: String, line: Int) -// POUND_DIRECTIVE-DAG: Keyword[#warning]/None: warning("{#(message)#}"); name=warning("message") -// POUND_DIRECTIVE-DAG: Keyword[#error]/None: error("{#(message)#}"); name=error("message") -// POUND_DIRECTIVE-DAG: Keyword[#if]/None: if {#(condition)#}; name=if condition -// POUND_DIRECTIVE-DAG: Keyword[#elseif]/None: elseif {#(condition)#}; name=elseif condition +// POUND_DIRECTIVE-DAG: Keyword[#sourceLocation]/None: sourceLocation(file: {#String#}, line: {#Int#}); name=sourceLocation(file:line:) +// POUND_DIRECTIVE-DAG: Keyword[#warning]/None: warning("{#(message)#}"); name=warning("") +// POUND_DIRECTIVE-DAG: Keyword[#error]/None: error("{#(message)#}"); name=error("") +// POUND_DIRECTIVE-DAG: Keyword[#if]/None: if {#(condition)#}; name=if +// POUND_DIRECTIVE-DAG: Keyword[#elseif]/None: elseif {#(condition)#}; name=elseif // POUND_DIRECTIVE-DAG: Keyword[#else]/None: else; name=else // POUND_DIRECTIVE-DAG: Keyword[#endif]/None: endif; name=endif @@ -47,20 +47,20 @@ class C { // CONDITION: Begin completions // CONDITION-NOT: globalVar -// CONDITION-DAG: Pattern/ExprSpecific: os({#(name)#}); name=os(name) -// CONDITION-DAG: Pattern/ExprSpecific: arch({#(name)#}); name=arch(name) -// CONDITION-DAG: Pattern/ExprSpecific: canImport({#(module)#}); name=canImport(module) -// CONDITION-DAG: Pattern/ExprSpecific: targetEnvironment(simulator); name=targetEnvironment(simulator) -// CONDITION-DAG: Pattern/ExprSpecific: swift(>={#(version)#}); name=swift(>=version) -// CONDITION-DAG: Pattern/ExprSpecific: swift(<{#(version)#}); name=swift(={#(version)#}); name=compiler(>=version) -// CONDITION-DAG: Pattern/ExprSpecific: compiler(<{#(version)#}); name=compiler(={#(version)#}); name=swift(>=) +// CONDITION-DAG: Pattern/CurrModule/Flair[ExprSpecific]: swift(<{#(version)#}); name=swift(<) +// CONDITION-DAG: Pattern/CurrModule/Flair[ExprSpecific]: compiler(>={#(version)#}); name=compiler(>=) +// CONDITION-DAG: Pattern/CurrModule/Flair[ExprSpecific]: compiler(<{#(version)#}); name=compiler(<) // CONDITION-DAG: Keyword[true]/None: true[#Bool#]; name=true // CONDITION-DAG: Keyword[false]/None: false[#Bool#]; name=false // CONDITION-NOT: globalVar -// WITHFLAG: Keyword/ExprSpecific: FOO; name=FOO -// WITHFLAG: Keyword/ExprSpecific: BAR; name=BAR +// WITHFLAG: Keyword/CurrModule/Flair[ExprSpecific]: FOO; name=FOO +// WITHFLAG: Keyword/CurrModule/Flair[ExprSpecific]: BAR; name=BAR // NOFLAG-NOT: FOO // NOFLAG-NOT: BAR diff --git a/test/IDE/complete_pound_keypath.swift b/test/IDE/complete_pound_keypath.swift index 6dc428a36344a..b341f9fe441bc 100644 --- a/test/IDE/complete_pound_keypath.swift +++ b/test/IDE/complete_pound_keypath.swift @@ -67,7 +67,7 @@ func completeInKeyPath6() { // CHECK-AFTER_POUND-NOT: keyPath -// CHECK-KEYPATH_ARG: Keyword/None/TypeRelation[Identical]: #keyPath({#@objc property sequence#})[#String#]; name=#keyPath(@objc property sequence) +// CHECK-KEYPATH_ARG: Keyword/None/TypeRelation[Identical]: #keyPath({#@objc property sequence#})[#String#]; name=#keyPath() // CHECK-IN_KEYPATH: Decl[InstanceVar]/CurrNominal: prop1[#String#]; name=prop1 // CHECK-IN_KEYPATH: Decl[InstanceVar]/CurrNominal: prop2[#ObjCClass?#]; name=prop2 diff --git a/test/IDE/complete_pound_selector.swift b/test/IDE/complete_pound_selector.swift index 3d80ab7275a0d..3f3aa9335e70f 100644 --- a/test/IDE/complete_pound_selector.swift +++ b/test/IDE/complete_pound_selector.swift @@ -72,12 +72,12 @@ class Subclass : NSObject { // CHECK-AFTER_POUND-NOT: selector -// CHECK-AFTER_POUND: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *) +// CHECK-AFTER_POUND: Keyword/Local/Flair[ExprSpecific]: available({#Platform...#}, *); name=available(*) -// CHECK-CONTEXT_SELECTOR: Keyword/None/TypeRelation[Identical]: #selector({#@objc method#})[#Selector#]; name=#selector(@objc method) +// CHECK-CONTEXT_SELECTOR: Keyword/None/TypeRelation[Identical]: #selector({#@objc method#})[#Selector#]; name=#selector() -// CHECK-SELECTOR_BASIC: Keyword/None: getter: {#@objc property#}; name=getter: @objc property -// CHECK-SELECTOR_BASIC: Keyword/None: setter: {#@objc property#}; name=setter: @objc property +// CHECK-SELECTOR_BASIC: Keyword/None: getter: {#@objc property#}; name=getter: +// CHECK-SELECTOR_BASIC: Keyword/None: setter: {#@objc property#}; name=setter: // CHECK-IN_SELECTOR-NOT: getter: // CHECK-IN_SELECTOR: Decl[Constructor]/CurrNominal/IsSystem: {{.?}}init[#(NSObject.Type) -> () -> NSObject#]; name=init diff --git a/test/IDE/complete_pound_statement.swift b/test/IDE/complete_pound_statement.swift index 3298340ed33c5..4f69ae8f49b05 100644 --- a/test/IDE/complete_pound_statement.swift +++ b/test/IDE/complete_pound_statement.swift @@ -53,5 +53,5 @@ class C3 { } } -// AVAILABLE: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *) +// AVAILABLE: Keyword/Local/Flair[ExprSpecific]: available({#Platform...#}, *); name=available(*) // AVAILABLE1-NOT: available({#Platform...#}, *) diff --git a/test/IDE/complete_property_delegate.swift b/test/IDE/complete_property_delegate.swift index 83cd65924f61b..26b5554804776 100644 --- a/test/IDE/complete_property_delegate.swift +++ b/test/IDE/complete_property_delegate.swift @@ -72,7 +72,7 @@ func paramTest(@Lazzzy arg: MyMember) { // PARAM-DAG: Decl[LocalVar]/Local: arg[#MyMember#]; name=arg // PARAM-DAG: Decl[LocalVar]/Local: $arg[#String#]; name=$arg // PARAM-DAG: Decl[LocalVar]/Local: _arg[#Lazzzy#]; name=_arg -// PARAM-DAG: Decl[FreeFunction]/CurrModule: paramTest({#arg: MyMember#})[#Void#]; name=paramTest(arg: MyMember) +// PARAM-DAG: Decl[FreeFunction]/CurrModule: paramTest({#arg: MyMember#})[#Void#]; name=paramTest(arg:) // PARAM: End completions } func closureTest() { @@ -92,7 +92,7 @@ func localTest() { // LOCAL-DAG: Decl[LocalVar]/Local: local[#MyMember#]; name=local // LOCAL-DAG: Decl[LocalVar]/Local: $local[#String#]; name=$local // LOCAL-DAG: Decl[LocalVar]/Local: _local[#Lazzzy#]; name=_local -// LOCAL-DAG: Decl[FreeFunction]/CurrModule: paramTest({#arg: MyMember#})[#Void#]; name=paramTest(arg: MyMember) +// LOCAL-DAG: Decl[FreeFunction]/CurrModule: paramTest({#arg: MyMember#})[#Void#]; name=paramTest(arg:) // LOCAL: End completions } diff --git a/test/IDE/complete_property_delegate_attribute.swift b/test/IDE/complete_property_delegate_attribute.swift index 89f106ca58d4d..86f9d378f67db 100644 --- a/test/IDE/complete_property_delegate_attribute.swift +++ b/test/IDE/complete_property_delegate_attribute.swift @@ -21,8 +21,8 @@ struct TestStruct { @MyStruct(#^AFTER_PAREN^# var test1 // AFTER_PAREN: Begin completions, 2 items -// AFTER_PAREN-DAG: Decl[Constructor]/CurrNominal: ['(']{#wrappedValue: MyEnum#}[')'][#MyStruct#]; name=wrappedValue: MyEnum -// AFTER_PAREN-DAG: Decl[Constructor]/CurrNominal: ['(']{#arg1: MyEnum#}, {#arg2: Int#}[')'][#MyStruct#]; name=arg1: MyEnum, arg2: Int +// AFTER_PAREN-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#wrappedValue: MyEnum#}[')'][#MyStruct#]; name=wrappedValue: +// AFTER_PAREN-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: MyEnum#}, {#arg2: Int#}[')'][#MyStruct#]; name=arg1:arg2: // AFTER_PAREN: End completions @MyStruct(arg1: #^ARG_MyEnum_NODOT^# @@ -35,8 +35,8 @@ struct TestStruct { @MyStruct(arg1: .#^ARG_MyEnum_DOT^# var test3 // ARG_MyEnum_DOT: Begin completions, 3 items -// ARG_MyEnum_DOT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: east[#MyEnum#]; name=east -// ARG_MyEnum_DOT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: west[#MyEnum#]; name=west +// ARG_MyEnum_DOT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: east[#MyEnum#]; name=east +// ARG_MyEnum_DOT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: west[#MyEnum#]; name=west // ARG_MyEnum_DOT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): MyEnum#})[#(into: inout Hasher) -> Void#]; // ARG_MyEnum_DOT: End completions diff --git a/test/IDE/complete_protocol_typealias.swift b/test/IDE/complete_protocol_typealias.swift new file mode 100644 index 0000000000000..373f0e9a817bc --- /dev/null +++ b/test/IDE/complete_protocol_typealias.swift @@ -0,0 +1,65 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +protocol MyProto { + typealias Content = Int +} +func testSimpleInTypeCompletion() -> MyProto.#^SIMPLE_IN_TYPE_COMPLETION^# {} +// SIMPLE_IN_TYPE_COMPLETION: Begin completions, 3 items +// SIMPLE_IN_TYPE_COMPLETION-DAG: Decl[TypeAlias]/CurrNominal: Content[#Int#]; +// SIMPLE_IN_TYPE_COMPLETION-DAG: Keyword/None: Protocol[#MyProto.Protocol#]; +// SIMPLE_IN_TYPE_COMPLETION-DAG: Keyword/None: Type[#MyProto.Type#]; +// SIMPLE_IN_TYPE_COMPLETION: End completions + +func testUnconstrainedUnresolvedMember() { + let _: MyProto = .#^UNCONSTRAINED_UNRESOLVED_MEMBER^# +// UNCONSTRAINED_UNRESOLVED_MEMBER: Begin completions, 1 item +// UNCONSTRAINED_UNRESOLVED_MEMBER-DAG: Decl[TypeAlias]/CurrNominal: Content[#Int#]; +// UNCONSTRAINED_UNRESOLVED_MEMBER: End completions +} + +protocol MyOtherProto { + associatedtype MyAssocType +} +extension MyOtherProto where MyAssocType == String { + typealias Content = Int +} + +// `Content` is actually accessible on `MyOtherProto` here, but that seems more like a bug of the language than a feature, so we don't want to promote it in code completion. +func testConstrainedInTypeCompletion() -> MyOtherProto.#^CONSTRAINED_IN_TYPE_COMPLETION^# {} +// CONSTRAINED_IN_TYPE_COMPLETION: Begin completions, 3 items +// CONSTRAINED_IN_TYPE_COMPLETION-DAG: Decl[AssociatedType]/CurrNominal: MyAssocType; +// CONSTRAINED_IN_TYPE_COMPLETION-DAG: Keyword/None: Protocol[#MyOtherProto.Protocol#]; +// CONSTRAINED_IN_TYPE_COMPLETION-DAG: Keyword/None: Type[#MyOtherProto.Type#]; +// CONSTRAINED_IN_TYPE_COMPLETION: End completions + +func testConstrainedUnresolvedMember() { + let _: MyOtherProto = .#^CONSTRAINED_UNRESOLVED_MEMBER^# +// CONSTRAINED_UNRESOLVED_MEMBER: Begin completions, 1 item +// CONSTRAINED_UNRESOLVED_MEMBER-DAG: Decl[AssociatedType]/CurrNominal: MyAssocType; +// CONSTRAINED_UNRESOLVED_MEMBER: End completions +} + +protocol ProtoWithGenericTypealias { + typealias Storage = Array +} +func testGenericInTypeCompletion() -> ProtoWithGenericTypealias.#^GENERIC_IN_TYPE_COMPLETION^# {} +// GENERIC_IN_TYPE_COMPLETION: Begin completions, 3 items +// GENERIC_IN_TYPE_COMPLETION-DAG: Decl[TypeAlias]/CurrNominal: Storage[#Array#]; +// GENERIC_IN_TYPE_COMPLETION-DAG: Keyword/None: Protocol[#ProtoWithGenericTypealias.Protocol#]; +// GENERIC_IN_TYPE_COMPLETION-DAG: Keyword/None: Type[#ProtoWithGenericTypealias.Type#]; +// GENERIC_IN_TYPE_COMPLETION: End completions + +func testGenericUnresolvedMember() { + let _: ProtoWithGenericTypealias = .#^GENERIC_UNRESOLVED_MEMBER^# +// GENERIC_UNRESOLVED_MEMBER: Begin completions, 1 item +// GENERIC_UNRESOLVED_MEMBER-DAG: Decl[TypeAlias]/CurrNominal: Storage[#Array#]; +// GENERIC_UNRESOLVED_MEMBER: End completions +} + +struct ConformingType: MyProto { + func foo(content: #^GLOBAL_COMPLETE_IN_CONFORMING_TYPE^#) {} +// GLOBAL_COMPLETE_IN_CONFORMING_TYPE: Begin completions +// GLOBAL_COMPLETE_IN_CONFORMING_TYPE: Decl[TypeAlias]/Super: Content[#Int#]; +// GLOBAL_COMPLETE_IN_CONFORMING_TYPE: End completions +} \ No newline at end of file diff --git a/test/IDE/complete_rdar75200217.swift b/test/IDE/complete_rdar75200217.swift index 0eb6ea220027a..2f73a2d291e6c 100644 --- a/test/IDE/complete_rdar75200217.swift +++ b/test/IDE/complete_rdar75200217.swift @@ -31,5 +31,5 @@ func deserializeName(_ data: Array, flag: Bool) { } // CHECK: Begin completions -// CHECK-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: utf8[#Encoding#]; +// CHECK-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: utf8[#Encoding#]; // CHECK: End completions diff --git a/test/IDE/complete_single_expression_return.swift b/test/IDE/complete_single_expression_return.swift index eb0a5306655f0..79cf78d23da3e 100644 --- a/test/IDE/complete_single_expression_return.swift +++ b/test/IDE/complete_single_expression_return.swift @@ -53,7 +53,7 @@ struct TestSingleExprClosureRetUnresolved { // TestSingleExprClosureRetUnresolved: Begin completions // TestSingleExprClosureRetUnresolved-NOT: notMine -// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; +// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprClosureRetUnresolved-NOT: notMine // TestSingleExprClosureRetUnresolved: End completions } @@ -108,7 +108,7 @@ struct TestSingleExprClosureUnresolved { } // TestSingleExprClosureUnresolved: Begin completions // TestSingleExprClosureUnresolved-NOT: notMine -// TestSingleExprClosureUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; +// TestSingleExprClosureUnresolved: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprClosureUnresolved-NOT: notMine // TestSingleExprClosureUnresolved: End completions @@ -223,7 +223,7 @@ struct TestSingleExprFuncUnresolved { // TestSingleExprFuncUnresolved: Begin completions // TestSingleExprFuncUnresolved-NOT: notMine -// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; +// TestSingleExprFuncUnresolved: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprFuncUnresolved-NOT: notMine // TestSingleExprFuncUnresolved: End completions } @@ -333,7 +333,7 @@ struct TestSingleExprAccessorUnresolved { // TestSingleExprAccessorUnresolved: Begin completions // TestSingleExprAccessorUnresolved-NOT: notMine -// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; +// TestSingleExprAccessorUnresolved: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprAccessorUnresolved-NOT: notMine // TestSingleExprAccessorUnresolved: End completions } @@ -485,7 +485,7 @@ struct TestSingleExprSubscriptUnresolved { // TestSingleExprSubscriptUnresolved: Begin completions // TestSingleExprSubscriptUnresolved-NOT: notMine -// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; +// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprSubscriptUnresolved-NOT: notMine // TestSingleExprSubscriptUnresolved: End completions } @@ -590,7 +590,7 @@ enum TopLevelEnum { case foo } -// TopLevelEnum: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#TopLevelEnum#]; +// TopLevelEnum: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: foo[#TopLevelEnum#]; var testAccessorUnresolvedTopLevel: TopLevelEnum { .#^testAccessorUnresolvedTopLevel?check=TopLevelEnum^# diff --git a/test/IDE/complete_sourcefileinfo.swift b/test/IDE/complete_sourcefileinfo.swift new file mode 100644 index 0000000000000..cecc15b14207b --- /dev/null +++ b/test/IDE/complete_sourcefileinfo.swift @@ -0,0 +1,71 @@ +// RUN: %empty-directory(%t) + +// RUN: %empty-directory(%t/Sources) +// RUN: %empty-directory(%t/Modules) +// RUN: cp %S/Inputs/complete_sourcefileinfo/MyModule1.swift %t/Sources/MyModule1.swift +// RUN: cp %S/Inputs/complete_sourcefileinfo/MyModule2.swift %t/Sources/MyModule2.swift +// RUN: %target-swiftc_driver -emit-module -o %t/Modules/MyModule.swiftmodule %t/Sources/MyModule1.swift %t/Sources/MyModule2.swift +// RUN: test -f %t/Modules/MyModule.swiftsourceinfo + + +// RUN: %empty-directory(%/result) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t/result -code-completion-sourcefileinfo -I %t/Modules + +// RUN: cp %S/Inputs/complete_sourcefileinfo/MyModule1-modified.swift %t/Sources/MyModule1.swift +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t/result -code-completion-sourcefileinfo -I %t/Modules + +// RUN: cp %S/Inputs/complete_sourcefileinfo/MyModule2-modified.swift %t/Sources/MyModule2.swift +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-sourcefileinfo -I %t/Modules -code-completion-token=GLOBAL | %FileCheck %s --check-prefix GLOBAL_MOD +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-sourcefileinfo -I %t/Modules -code-completion-token=MEMBER | %FileCheck %s --check-prefix MEMBER_MOD + +import MyModule + +var globalValue: Int = 1 + +func test() { + #^GLOBAL^# +} +// GLOBAL-LABEL: Known module source files +// GLOBAL-DAG: + {{.*}}{{[/\\]}}test{{[/\\]}}IDE{{[/\\]}}complete_sourcefileinfo.swift +// GLOBAL-DAG: + {{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// GLOBAL-DAG: + {{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule2.swift +// GLOBAL-LABEL: Begin completions +// GLOBAL-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: String[#String#]; name=String{{$}} +// GLOBAL-DAG: Decl[Struct]/OtherModule[MyModule]: MyStruct[#MyStruct#]; name=MyStruct; source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// GLOBAL-DAG: Decl[GlobalVar]/CurrModule: globalValue[#Int#]; name=globalValue; source={{.*}}{{[/\\]}}test{{[/\\]}}IDE{{[/\\]}}complete_sourcefileinfo.swift + +// GLOBAL_MOD-LABEL: Known module source files +// GLOBAL_MOD-DAG: + {{.*}}{{[/\\]}}test{{[/\\]}}IDE{{[/\\]}}complete_sourcefileinfo.swift +// GLOBAL_MOD-DAG: + {{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// GLOBAL_MOD-DAG: - {{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule2.swift +// GLOBAL_MOD-LABEL: Begin completions +// GLOBAL_MOD-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: String[#String#]; name=String{{$}} +// GLOBAL_MOD-DAG: Decl[Struct]/OtherModule[MyModule]: MyStruct[#MyStruct#]; name=MyStruct; source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// GLOBAL_MOD-DAG: Decl[GlobalVar]/CurrModule: globalValue[#Int#]; name=globalValue; source={{.*}}{{[/\\]}}test{{[/\\]}}IDE{{[/\\]}}complete_sourcefileinfo.swift + +func test(val: MyStruct) { + val.#^MEMBER^# +} +// MEMBER-LABEL: Known module source files +// MEMBER-DAG: + {{.*}}{{[/\\]}}test{{[/\\]}}IDE{{[/\\]}}complete_sourcefileinfo.swift +// MEMBER-DAG: + {{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// MEMBER-DAG: + {{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule2.swift +// MEMBER-LABEL: Begin completions, 5 items +// MEMBER-DAG: Keyword[self]/CurrNominal: self[#MyStruct#]; name=self{{$}} +// MEMBER-DAG: Decl[InstanceVar]/CurrNominal: propertyInType[#Int#]; name=propertyInType; source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal: funcInType({#x: Int#})[#Void#]; name=funcInType(x:); source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// MEMBER-DAG: Decl[InstanceVar]/CurrNominal: propertyInExtension[#String#]; name=propertyInExtension; source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule2.swift +// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal: funcInExtension()[#String#]; name=funcInExtension(); source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule2.swift +// MEMBER: End completions + +// MEMBER_MOD-LABEL: Known module source files +// MEMBER_MOD-DAG: + {{.*}}{{[/\\]}}test{{[/\\]}}IDE{{[/\\]}}complete_sourcefileinfo.swift +// MEMBER_MOD-DAG: + {{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// MEMBER_MOD-DAG: - {{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule2.swift +// MEMBER_MOD-LABEL: Begin completions, 5 items +// MEMBER_MOD-DAG: Keyword[self]/CurrNominal: self[#MyStruct#]; name=self{{$}} +// MEMBER_MOD-DAG: Decl[InstanceVar]/CurrNominal: propertyInType[#Int#]; name=propertyInType; source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// MEMBER_MOD-DAG: Decl[InstanceMethod]/CurrNominal: funcInType({#x: Int#})[#Void#]; name=funcInType(x:); source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule1.swift +// MEMBER_MOD-DAG: Decl[InstanceVar]/CurrNominal: propertyInExtension[#String#]; name=propertyInExtension; source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule2.swift +// MEMBER_MOD-DAG: Decl[InstanceMethod]/CurrNominal: funcInExtension()[#String#]; name=funcInExtension(); source={{.*}}{{[/\\]}}Sources{{[/\\]}}MyModule2.swift +// MEMBER_MOD: End completions diff --git a/test/IDE/complete_sr13271.swift b/test/IDE/complete_sr13271.swift index 27ac725cefd3e..075da64fd371e 100644 --- a/test/IDE/complete_sr13271.swift +++ b/test/IDE/complete_sr13271.swift @@ -48,7 +48,7 @@ func test() { .#^B^# } // B: Begin completions, 2 items -// B-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: baz[#B#]; name=baz +// B-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: baz[#B#]; name=baz // B-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#B#]; name=init() // B: End completions } diff --git a/test/IDE/complete_sr14711.swift b/test/IDE/complete_sr14711.swift new file mode 100644 index 0000000000000..c906d85ebdd83 --- /dev/null +++ b/test/IDE/complete_sr14711.swift @@ -0,0 +1,41 @@ +// RUN: %swift-ide-test --code-completion --source-filename %s --code-completion-token=COMPLETE | %FileCheck %s + +struct TodaySectionEditView { + var sections: [SectionHeaderView2] = [] + @ViewBuilder2 var body: some View2 { + ForEach2(sections, id: \.text) { section in + Text2("") + Text2(section.text) + .#^COMPLETE^#font() + } + } +} + +protocol View2 {} + +extension View2 { + func font() -> some View2 { fatalError() } +} + +@resultBuilder public struct ViewBuilder2 { + static func buildBlock() -> Never { fatalError() } + static func buildBlock(_ content: Content) -> Content where Content : View2 { fatalError() } + static func buildBlock(_ c0: C0, _ c1: C1) -> C0 where C0 : View2, C1 : View2 { fatalError() } +} + +struct Text2: View2 { + init(_ s: String) {} +} + +struct SectionHeaderView2 { + let text: String = "" +} + +public struct ForEach2: View2 where Data : RandomAccessCollection { + init(_ data: Data, id: KeyPath, @ViewBuilder2 content: @escaping (Data.Element) -> Content) {} +} + +// CHECK: Begin completions, 2 items +// CHECK-NEXT: Keyword[self]/CurrNominal: self[#Text2#]; +// CHECK-NEXT: Decl[InstanceMethod]/Super: font()[#View2#]; +// CHECK-NEXT: End completions diff --git a/test/IDE/complete_sr14916.swift b/test/IDE/complete_sr14916.swift new file mode 100644 index 0000000000000..1948a820d6284 --- /dev/null +++ b/test/IDE/complete_sr14916.swift @@ -0,0 +1,29 @@ +// RUN: %swift-ide-test -code-completion -code-completion-token COMPLETE -source-filename %s | %FileCheck %s + +struct Foo { + var bar: Int +} + +protocol View2 {} +struct EmptyView: View2 {} + +@resultBuilder public struct ViewBuilder2 { + public static func buildBlock(_ content: EmptyView) -> EmptyView { fatalError() } +} + +public struct List2 { + public init(selection: Int?, @ViewBuilder2 content: () -> EmptyView) + public init(selection: String?, @ViewBuilder2 content: () -> EmptyView) +} + +func foo(kp: (Foo) -> String) {} + +func foo() { + List2 { + foo(kp: \.self#^COMPLETE^#) +// CHECK: Begin completions, 1 items +// CHECK-NEXT: Decl[InstanceVar]/CurrNominal: .bar[#Int#]; +// CHECK-NEXT: End completions + } + .unknownMethod() +} \ No newline at end of file diff --git a/test/IDE/complete_stmt_controlling_expr.swift b/test/IDE/complete_stmt_controlling_expr.swift index 0727a3d3ec5a7..83bd80d2e2e58 100644 --- a/test/IDE/complete_stmt_controlling_expr.swift +++ b/test/IDE/complete_stmt_controlling_expr.swift @@ -142,22 +142,43 @@ func testRepeatWhile2(_ fooObject: FooStruct) { } while localFooObject.#^COND_DO_WHILE_2?check=COND-WITH-RELATION1^# } -func testCStyleForInit1(_ fooObject: FooStruct) { +func testForeachPattern1(_ fooObject: FooStruct) { var localInt = 42 var localFooObject = FooStruct(localInt) - for #^C_STYLE_FOR_INIT_1?check=COND_NONE^# + for #^FOREACH_PATTERN_1^# +// FOREACH_PATTERN_1: Begin completions, 4 items +// FOREACH_PATTERN_1-DAG: Keyword[try]/None: try; name=try +// FOREACH_PATTERN_1-DAG: Keyword/None: await; name=await +// FOREACH_PATTERN_1-DAG: Keyword[var]/None: var; name=var +// FOREACH_PATTERN_1-DAG: Keyword[case]/None: case; name=case +// FOREACH_PATTERN_1: End completions } -func testCStyleForInit2(_ fooObject: FooStruct) { +func testForeachPattern2(_ fooObject: FooStruct) { var localInt = 42 var localFooObject = FooStruct(localInt) - for #^C_STYLE_FOR_INIT_2?check=COND_COMMON^#; + for try #^FOREACH_PATTERN_2^# +// FOREACH_PATTERN_2: Begin completions, 3 items +// FOREACH_PATTERN_2-DAG: Keyword/None: await; name=await +// FOREACH_PATTERN_2-DAG: Keyword[var]/None: var; name=var +// FOREACH_PATTERN_2-DAG: Keyword[case]/None: case; name=case +// FOREACH_PATTERN_2: End completions } -func testCStyleForInit3(_ fooObject: FooStruct) { +func testForeachPattern3(_ fooObject: FooStruct) { var localInt = 42 var localFooObject = FooStruct(localInt) - for #^C_STYLE_FOR_INIT_3?check=COND_COMMON^# ; + for try await #^FOREACH_PATTERN_3^# +// FOREACH_PATTERN_3: Begin completions, 2 items +// FOREACH_PATTERN_3-DAG: Keyword[var]/None: var; name=var +// FOREACH_PATTERN_3-DAG: Keyword[case]/None: case; name=case +// FOREACH_PATTERN_3: End completions +} + +func testForeachPattern4(_ fooObject: FooStruct) { + var localInt = 42 + var localFooObject = FooStruct(localInt) + for var #^FOREACH_PATTERN_4?check=COND_NONE^# } func testCStyleForCond1(_ fooObject: FooStruct) { @@ -400,7 +421,7 @@ func testSwitchCaseWhereExprIJ1(_ fooObject: FooStruct) { enum A { case aaa } enum B { case bbb } // UNRESOLVED_B-NOT: aaa -// UNRESOLVED_B: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bbb[#B#]; name=bbb +// UNRESOLVED_B: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bbb[#B#]; name=bbb // UNRESOLVED_B-NOT: aaa struct AA { @@ -522,17 +543,7 @@ func testGuardCase(x:FooStruct?) { // FOOSTRUCT_LOCALVAL-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Convertible\])?}}: boundVal[#FooStruct#]; // FOOSTRUCT_LOCALVAL: End completions -// OPTIONAL_FOOSTRUCT: Begin completions, 9 items -// OPTIONAL_FOOSTRUCT-DAG: Keyword[nil]/None/Erase[1]/TypeRelation[Identical]: nil[#FooStruct?#]; name=nil +// OPTIONAL_FOOSTRUCT: Begin completions, 2 items // OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional#]; name=none -// OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: some({#FooStruct#})[#Optional#]; name=some(FooStruct) -// FIXME: 'FooStruct' members should not be shown. -// OPTIONAL_FOOSTRUCT-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init()[#FooStruct#]; name=init() -// OPTIONAL_FOOSTRUCT-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init({#Int#})[#FooStruct#]; name=init(Int) -// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal: boolGen({#(self): FooStruct#})[#() -> Bool#]; name=boolGen(self: FooStruct) -// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal: intGen({#(self): FooStruct#})[#() -> Int#]; name=intGen(self: FooStruct) -// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional#})[#((FooStruct) throws -> U) -> U?#]; name=map(self: Optional) -// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional#})[#((FooStruct) throws -> U?) -> U?#]; name=flatMap(self: Optional) -// OPTIONAL_FOOSTRUCT-NOT: init({#(some): -// OPTIONAL_FOOSTRUCT-NOT: init({#nilLiteral: +// OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: some({#FooStruct#})[#Optional#]; name=some() // OPTIONAL_FOOSTRUCT: End completions diff --git a/test/IDE/complete_string_interpolation.swift b/test/IDE/complete_string_interpolation.swift index ead21a2cbe4c6..cb84e41530aa2 100644 --- a/test/IDE/complete_string_interpolation.swift +++ b/test/IDE/complete_string_interpolation.swift @@ -34,24 +34,24 @@ var messenger = Messenger() func testMessenger(intVal: Int, fltVal: Float) { messenger.send(" \(intVal, format: .#^OVERLOAD_INT^#) ") // OVERLOAD_INT: Begin completions, 3 items -// OVERLOAD_INT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: decimal[#MsgInterpolation.IntFormat#]; -// OVERLOAD_INT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: hex[#MsgInterpolation.IntFormat#]; +// OVERLOAD_INT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: decimal[#MsgInterpolation.IntFormat#]; +// OVERLOAD_INT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: hex[#MsgInterpolation.IntFormat#]; // OVERLOAD_INT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): MsgInterpolation.IntFormat#})[#(into: inout Hasher) -> Void#]; // OVERLOAD_INT: End completions messenger.send(" \(5, format: .#^OVERLOAD_INTLITERAL^#, extraneousArg: 10) ") // OVERLOAD_INTLITERAL: Begin completions, 5 items -// OVERLOAD_INTLITERAL-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: decimal[#MsgInterpolation.IntFormat#]; -// OVERLOAD_INTLITERAL-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: hex[#MsgInterpolation.IntFormat#]; +// OVERLOAD_INTLITERAL-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: decimal[#MsgInterpolation.IntFormat#]; +// OVERLOAD_INTLITERAL-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: hex[#MsgInterpolation.IntFormat#]; // OVERLOAD_INTLITERAL-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): MsgInterpolation.IntFormat#})[#(into: inout Hasher) -> Void#]; -// OVERLOAD_INTLITERAL-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: precision({#Int#})[#MsgInterpolation.FloatFormat#]; -// OVERLOAD_INTLITERAL-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: hex[#MsgInterpolation.FloatFormat#]; +// OVERLOAD_INTLITERAL-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: precision({#Int#})[#MsgInterpolation.FloatFormat#]; +// OVERLOAD_INTLITERAL-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: hex[#MsgInterpolation.FloatFormat#]; // OVERLOAD_INTLITERAL: End completions messenger.send(" \(fltVal, format: .#^OVERLOAD_FLT^#) ") messenger.send(" \(5.0, format: .#^OVERLOAD_FLTLITERAL^#) ") // OVERLOAD_FLT: Begin completions, 2 items -// OVERLOAD_FLT-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: precision({#Int#})[#MsgInterpolation.FloatFormat#]; -// OVERLOAD_FLT-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: hex[#MsgInterpolation.FloatFormat#]; +// OVERLOAD_FLT-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: precision({#Int#})[#MsgInterpolation.FloatFormat#]; +// OVERLOAD_FLT-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: hex[#MsgInterpolation.FloatFormat#]; // OVERLOAD_FLT: End completions } diff --git a/test/IDE/complete_subscript.swift b/test/IDE/complete_subscript.swift index dfb74fafc1c0b..0e4c527fad4c5 100644 --- a/test/IDE/complete_subscript.swift +++ b/test/IDE/complete_subscript.swift @@ -31,27 +31,27 @@ func test1() { let _ = MyStruct #^METATYPE_UNRESOLVED^# // METATYPE_UNRESOLVED: Begin completions, 4 items // METATYPE_UNRESOLVED-DAG: Decl[Subscript]/CurrNominal: [{#(x): Int#}, {#static: _#}][#MyStruct<_>#]; -// METATYPE_UNRESOLVED-DAG: Decl[Constructor]/CurrNominal: ()[#MyStruct<_>#]; +// METATYPE_UNRESOLVED-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#MyStruct<_>#]; // METATYPE_UNRESOLVED-DAG: Keyword[self]/CurrNominal: .self[#MyStruct<_>.Type#]; // METATYPE_UNRESOLVED-DAG: Keyword/CurrNominal: .Type[#MyStruct<_>.Type#]; // METATYPE_UNRESOLVED: End completions let _ = MyStruct[#^METATYPE_UNRESOLVED_BRACKET^# // METATYPE_UNRESOLVED_BRACKET: Begin completions -// METATYPE_UNRESOLVED_BRACKET-DAG: Decl[Subscript]/CurrNominal: ['[']{#(x): Int#}, {#static: _#}[']'][#MyStruct<_>#]; +// METATYPE_UNRESOLVED_BRACKET-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(x): Int#}, {#static: _#}[']'][#MyStruct<_>#]; // METATYPE_UNRESOLVED_BRACKET: End completions let _ = MyStruct #^METATYPE_INT^# // METATYPE_INT: Begin completions, 4 items // METATYPE_INT-DAG: Decl[Subscript]/CurrNominal: [{#(x): Int#}, {#static: Int#}][#MyStruct#]; -// METATYPE_INT-DAG: Decl[Constructor]/CurrNominal: ()[#MyStruct#]; +// METATYPE_INT-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#MyStruct#]; // METATYPE_INT-DAG: Keyword[self]/CurrNominal: .self[#MyStruct.Type#]; // METATYPE_INT-DAG: Keyword/CurrNominal: .Type[#MyStruct.Type#]; // METATYPE_INT: End completions let _ = MyStruct[#^METATYPE_INT_BRACKET^# // METATYPE_INT_BRACKET: Begin completions -// METATYPE_INT_BRACKET-DAG: Decl[Subscript]/CurrNominal: ['[']{#(x): Int#}, {#static: Int#}[']'][#MyStruct#]; +// METATYPE_INT_BRACKET-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(x): Int#}, {#static: Int#}[']'][#MyStruct#]; // METATYPE_INT_BRACKET: End completions let _ = MyStruct()#^INSTANCE_INT^# @@ -62,22 +62,22 @@ func test1() { let _ = MyStruct()[#^INSTANCE_INT_BRACKET^# // INSTANCE_INT_BRACKET: Begin completions -// INSTANCE_INT_BRACKET-DAG: Decl[Subscript]/CurrNominal: ['[']{#(x): Int#}, {#instance: Int#}[']'][#Int#]; -// INSTANCE_INT_BRACKET-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; +// INSTANCE_INT_BRACKET-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(x): Int#}, {#instance: Int#}[']'][#Int#]; +// INSTANCE_INT_BRACKET-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // INSTANCE_INT_BRACKET: End completions } func test2(value: MyStruct) { let _ = MyStruct#^METATYPE_ARCHETYPE^# // METATYPE_ARCHETYPE: Begin completions, 4 items // METATYPE_ARCHETYPE-DAG: Decl[Subscript]/CurrNominal: [{#(x): Int#}, {#static: U#}][#MyStruct#]; -// METATYPE_ARCHETYPE-DAG: Decl[Constructor]/CurrNominal: ()[#MyStruct#]; +// METATYPE_ARCHETYPE-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#MyStruct#]; // METATYPE_ARCHETYPE-DAG: Keyword[self]/CurrNominal: .self[#MyStruct.Type#]; // METATYPE_ARCHETYPE-DAG: Keyword/CurrNominal: .Type[#MyStruct.Type#]; // METATYPE_ARCHETYPE: End completions let _ = MyStruct[#^METATYPE_ARCHETYPE_BRACKET^# // METATYPE_ARCHETYPE_BRACKET: Begin completions -// METATYPE_ARCHETYPE_BRACKET-DAG: Decl[Subscript]/CurrNominal: ['[']{#(x): Int#}, {#static: U#}[']'][#MyStruct#]; +// METATYPE_ARCHETYPE_BRACKET-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(x): Int#}, {#static: U#}[']'][#MyStruct#]; // METATYPE_ARCHETYPE_BRACKET: End completions let _ = value #^INSTANCE_ARCHETYPE^# @@ -88,18 +88,18 @@ func test2(value: MyStruct) { let _ = value[#^INSTANCE_ARCHETYPE_BRACKET^# // INSTANCE_ARCHETYPE_BRACKET: Begin completions -// INSTANCE_ARCHETYPE_BRACKET-DAG: Decl[Subscript]/CurrNominal: ['[']{#(x): Int#}, {#instance: U#}[']'][#Int#]; -// INSTANCE_ARCHETYPE_BRACKET-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; +// INSTANCE_ARCHETYPE_BRACKET-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(x): Int#}, {#instance: U#}[']'][#Int#]; +// INSTANCE_ARCHETYPE_BRACKET-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // INSTANCE_ARCHETYPE_BRACKET: End completions let _ = MyStruct[42, #^METATYPE_LABEL^# // METATYPE_LABEL: Begin completions, 1 items -// METATYPE_LABEL-DAG: Pattern/ExprSpecific: {#static: U#}[#U#]; +// METATYPE_LABEL-DAG: Pattern/Local/Flair[ArgLabels]: {#static: U#}[#U#]; // METATYPE_LABEL: End completions let _ = value[42, #^INSTANCE_LABEL^# // INSTANCE_LABEL: Begin completions, 1 items -// INSTANCE_LABEL-DAG: Pattern/ExprSpecific: {#instance: U#}[#U#]; +// INSTANCE_LABEL-DAG: Pattern/Local/Flair[ArgLabels]: {#instance: U#}[#U#]; // INSTANCE_LABEL: End completions } @@ -114,28 +114,28 @@ class Derived: Base { func testInstance() { let _ = self[#^SELF_IN_INSTANCEMETHOD^#] // SELF_IN_INSTANCEMETHOD: Begin completions, 3 items -// SELF_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#derivedInstance: Int#}[']'][#Int#]; -// SELF_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/Super: ['[']{#instance: Int#}[']'][#Int#]; -// SELF_IN_INSTANCEMETHOD-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath#}[']'][#Value#]; +// SELF_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#derivedInstance: Int#}[']'][#Int#]; +// SELF_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/Super/Flair[ArgLabels]: ['[']{#instance: Int#}[']'][#Int#]; +// SELF_IN_INSTANCEMETHOD-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; // SELF_IN_INSTANCEMETHOD: End completions let _ = super[#^SUPER_IN_INSTANCEMETHOD^#] // SUPER_IN_INSTANCEMETHOD: Begin completions, 2 items -// SUPER_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#instance: Int#}[']'][#Int#]; -// SUPER_IN_INSTANCEMETHOD-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath#}[']'][#Value#]; +// SUPER_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#instance: Int#}[']'][#Int#]; +// SUPER_IN_INSTANCEMETHOD-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; // SUPER_IN_INSTANCEMETHOD: End completions } static func testStatic() { let _ = self[#^SELF_IN_STATICMETHOD^#] // SELF_IN_STATICMETHOD: Begin completions, 2 items -// SELF_IN_STATICMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#derivedStatic: Int#}[']'][#Int#]; -// SELF_IN_STATICMETHOD-DAG: Decl[Subscript]/Super: ['[']{#static: Int#}[']'][#Int#]; +// SELF_IN_STATICMETHOD-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#derivedStatic: Int#}[']'][#Int#]; +// SELF_IN_STATICMETHOD-DAG: Decl[Subscript]/Super/Flair[ArgLabels]: ['[']{#static: Int#}[']'][#Int#]; // SELF_IN_STATICMETHOD: End completions let _ = super[#^SUPER_IN_STATICMETHOD^#] // SUPER_IN_STATICMETHOD: Begin completions, 1 items -// SUPER_IN_STATICMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#static: Int#}[']'][#Int#]; +// SUPER_IN_STATICMETHOD-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#static: Int#}[']'][#Int#]; // SUPER_IN_STATICMETHOD: End completions } } @@ -146,14 +146,14 @@ struct MyStruct1 { func testSubscriptCallSig(val: MyStruct1) { val[#^LABELED_SUBSCRIPT^# // LABELED_SUBSCRIPT: Begin completions, 2 items -// LABELED_SUBSCRIPT-DAG: Decl[Subscript]/CurrNominal: ['[']{#idx1: Int#}, {#idx2: Comparable#}[']'][#Int!#]; -// LABELED_SUBSCRIPT-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; +// LABELED_SUBSCRIPT-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#idx1: Int#}, {#idx2: Comparable#}[']'][#Int!#]; +// LABELED_SUBSCRIPT-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // LABELED_SUBSCRIPT: End completions } func testSubcscriptTuple(val: (x: Int, String)) { val[#^TUPLE^#] // TUPLE: Begin completions, 1 items -// TUPLE-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath<(x: Int, String), Value>#}[']'][#Value#]; +// TUPLE-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath<(x: Int, String), Value>#}[']'][#Value#]; // TUPLE: End completions } diff --git a/test/IDE/complete_swift_key_path.swift b/test/IDE/complete_swift_key_path.swift index 4935b1007a464..8340122ee2186 100644 --- a/test/IDE/complete_swift_key_path.swift +++ b/test/IDE/complete_swift_key_path.swift @@ -32,6 +32,13 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_FUNC_INOUT | %FileCheck %s -check-prefix=PERSONTYPE-DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_FUNC_VARIADIC | %FileCheck %s -check-prefix=ARRAYTYPE-DOT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_KEY_PATH_BASE | %FileCheck %s -check-prefix=PERSONTYPE-DOT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_KEY_PATH_RESULT | %FileCheck %s -check-prefix=PERSONTYPE-DOT + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=COMPLETE_AFTER_SELF | %FileCheck %s -check-prefix=OBJ-NODOT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_RESULT_BUILDER | %FileCheck %s -check-prefix=PERSONTYPE-DOT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_MULTI_STMT_CLOSURE | %FileCheck %s -check-prefix=PERSONTYPE-DOT + class Person { var name: String var friends: [Person] = [] @@ -50,7 +57,7 @@ let _ = \Person#^TYPE_NODOT^# // PERSONTYPE-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: .friends[#[Person]#]; name=friends // PERSONTYPE-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: .bestFriend[#Person?#]; name=bestFriend // PERSONTYPE-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: .itself[#Person#]; name=itself -// PERSONTYPE-NODOT-NEXT: Decl[Subscript]/CurrNominal: .[{#(index): Int#}][#Int#]; name=[index: Int] +// PERSONTYPE-NODOT-NEXT: Decl[Subscript]/CurrNominal: .[{#(index): Int#}][#Int#]; name=[:] let _ = \Person.#^TYPE_DOT^# // PERSONTYPE-DOT: Begin completions, 5 items @@ -58,20 +65,20 @@ let _ = \Person.#^TYPE_DOT^# // PERSONTYPE-DOT-NEXT: Decl[InstanceVar]/CurrNominal: friends[#[Person]#]; name=friends // PERSONTYPE-DOT-NEXT: Decl[InstanceVar]/CurrNominal: bestFriend[#Person?#]; name=bestFriend // PERSONTYPE-DOT-NEXT: Decl[InstanceVar]/CurrNominal: itself[#Person#]; name=itself -// PERSONTYPE-DOT-NEXT: Decl[Subscript]/CurrNominal: [{#(index): Int#}][#Int#]; name=[index: Int] +// PERSONTYPE-DOT-NEXT: Decl[Subscript]/CurrNominal: [{#(index): Int#}][#Int#]; name=[:] let _ = \Person.friends#^ARRAY_NODOT^# // ARRAY-NODOT: Begin completions -// ARRAY-NODOT-DAG: Decl[Subscript]/CurrNominal/IsSystem: [{#(index): Int#}][#Person#]; name=[index: Int] +// ARRAY-NODOT-DAG: Decl[Subscript]/CurrNominal/IsSystem: [{#(index): Int#}][#Person#]; name=[:] // ARRAY-NODOT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .count[#Int#]; name=count // ARRAY-NODOT-DAG: Decl[InstanceVar]/Super/IsSystem: .first[#Person?#]; name=first let _ = \Person.friends.#^ARRAY_DOT^# // ARRAY-DOT: Begin completions -// ARRAY-DOT-NOT: Decl[Subscript]/CurrNominal/IsSystem: [{#(index): Int#}][#Element#]; name=[Int] +// ARRAY-DOT-NOT: Decl[Subscript]/CurrNominal/IsSystem: [{#(index): Int#}][#Element#]; name=[] // ARRAY-DOT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: count[#Int#]; name=count // ARRAY-DOT-DAG: Decl[InstanceVar]/Super/IsSystem: first[#Person?#]; name=first -// ARRAY-DOT-NOT: Decl[Subscript]/CurrNominal/IsSystem: [{#(index): Int#}][#Element#]; name=[Int] +// ARRAY-DOT-NOT: Decl[Subscript]/CurrNominal/IsSystem: [{#(index): Int#}][#Element#]; name=[] let _ = \Person.friends[0]#^OBJ_NODOT^# // OBJ-NODOT: Begin completions, 5 items @@ -79,7 +86,7 @@ let _ = \Person.friends[0]#^OBJ_NODOT^# // OBJ-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: .friends[#[Person]#]; name=friends // OBJ-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: .bestFriend[#Person?#]; name=bestFriend // OBJ-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: .itself[#Person#]; name=itself -// OBJ-NODOT-NEXT: Decl[Subscript]/CurrNominal: [{#(index): Int#}][#Int#]; name=[index: Int] +// OBJ-NODOT-NEXT: Decl[Subscript]/CurrNominal: [{#(index): Int#}][#Int#]; name=[:] let _ = \Person.friends[0].#^OBJ_DOT^# // OBJ-DOT: Begin completions, 4 items @@ -94,7 +101,7 @@ let _ = \Person.bestFriend#^OPTIONAL_NODOT^# // OPTIONAL-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: ?.friends[#[Person]#]; name=friends // OPTIONAL-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: ?.bestFriend[#Person?#]; name=bestFriend // OPTIONAL-NODOT-NEXT: Decl[InstanceVar]/CurrNominal: ?.itself[#Person#]; name=itself -// OPTIONAL-NODOT-NEXT: Decl[Subscript]/CurrNominal: ?[{#(index): Int#}][#Int#]; name=[index: Int] +// OPTIONAL-NODOT-NEXT: Decl[Subscript]/CurrNominal: ?[{#(index): Int#}][#Int#]; name=[:] // OPTIONAL-NODOT: Decl[InstanceVar]/CurrNominal/IsSystem: .unsafelyUnwrapped[#Person#]; name=unsafelyUnwrapped let _ = \Person.bestFriend.#^OPTIONAL_DOT^# @@ -119,13 +126,13 @@ let _ = \Person.bestFriend?.itself.#^CHAIN_DOT^# let _ = \[Person]#^ARRAYTYPE_NODOT^# // ARRAYTYPE-NODOT: Begin completions -// ARRAYTYPE-NODOT-DAG: Decl[Subscript]/CurrNominal/IsSystem: .[{#(index): Int#}][#Person#]; name=[index: Int] +// ARRAYTYPE-NODOT-DAG: Decl[Subscript]/CurrNominal/IsSystem: .[{#(index): Int#}][#Person#]; name=[:] // ARRAYTYPE-NODOT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .count[#Int#]; name=count // ARRAYTYPE-NODOT-DAG: Decl[InstanceVar]/Super/IsSystem: .first[#Person?#]; name=first let _ = \[Person].#^ARRAYTYPE_DOT^# // ARRAYTYPE-DOT: Begin completions -// ARRAYTYPE-DOT-DAG: Decl[Subscript]/CurrNominal/IsSystem: [{#(index): Int#}][#Person#]; name=[index: Int] +// ARRAYTYPE-DOT-DAG: Decl[Subscript]/CurrNominal/IsSystem: [{#(index): Int#}][#Person#]; name=[:] // ARRAYTYPE-DOT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: count[#Int#]; name=count // ARRAYTYPE-DOT-DAG: Decl[InstanceVar]/Super/IsSystem: first[#Person?#]; name=first @@ -178,3 +185,46 @@ func testKeyPathAsFunctions(wrapped: Wrap) { let _ = wrapped.variadic(\.#^CONTEXT_FUNC_VARIADIC^#) // Same as ARRAYTYPE_DOT. } + +func genericKeyPathBase(to keyPath: ReferenceWritableKeyPath, on object: Root) { + genericKeyPathBase(to: \.#^GENERIC_KEY_PATH_BASE^#, on: Person()) + // Same as TYPE_DOT. +} + +func genericKeyPathResult(id: KeyPath) { + genericKeyPathResult(\.#^GENERIC_KEY_PATH_RESULT^#) + // Same as TYPE_DOT. +} + +func completeAfterSelf(people: [Person]) { + people.map(\.self#^COMPLETE_AFTER_SELF^#) +} + +func inResultBuilder() { + protocol View2 {} + + @resultBuilder public struct ViewBuilder2 { + public static func buildBlock(_ content: Content) -> Content where Content : View2 { fatalError() } + public static func buildIf(_ content: Content?) -> Content? where Content : View2 { fatalError() } + } + + struct VStack2: View2 { + init(@ViewBuilder2 view: () -> Content) {} + } + + @ViewBuilder2 var body: some View2 { + VStack2 { + if true { + var people: [Person] = [] + people.map(\.#^IN_RESULT_BUILDER^#) + } + } + } +} + +func inMultiStmtClosure(closure: () -> Void) { + inMultiStmtClosure { + var people: [Person] = [] + people.map(\.#^IN_MULTI_STMT_CLOSURE^#) + } +} diff --git a/test/IDE/complete_swift_key_path_optional_root.swift b/test/IDE/complete_swift_key_path_optional_root.swift index c80822c601c4f..473ba14063191 100644 --- a/test/IDE/complete_swift_key_path_optional_root.swift +++ b/test/IDE/complete_swift_key_path_optional_root.swift @@ -24,7 +24,7 @@ let _ : KeyPath = \.#^TYPE_INFER_DOT_OPTIONAL^# // PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.friends[#[Person]#]; name=friends // PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.bestFriend[#Person?#]; name=bestFriend // PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.itself[#Person#]; name=itself -// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[Subscript]/CurrNominal: ?[{#(index): Int#}][#Int#]; name=[index: Int] +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[Subscript]/CurrNominal: ?[{#(index): Int#}][#Int#]; name=[:] // PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: unsafelyUnwrapped[#Person#]; name=unsafelyUnwrapped // PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: optMember[#String#]; name=optMember // PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: debugDescription[#String#]; name=debugDescription @@ -36,7 +36,7 @@ let _ : KeyPath = \Person?.#^TYPE_DOT_OPTIONAL^# // PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.friends[#[Person]#]; name=friends // PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.bestFriend[#Person?#]; name=bestFriend // PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.itself[#Person#]; name=itself -// PERSONTYPE-DOT-OPT-NEXT: Decl[Subscript]/CurrNominal: ?[{#(index): Int#}][#Int#]; name=[index: Int] +// PERSONTYPE-DOT-OPT-NEXT: Decl[Subscript]/CurrNominal: ?[{#(index): Int#}][#Int#]; name=[:] // PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: unsafelyUnwrapped[#Person#]; name=unsafelyUnwrapped // PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: optMember[#String#]; name=optMember // PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: debugDescription[#String#]; name=debugDescription @@ -48,7 +48,7 @@ let _ : KeyPath = \Person?. #^TYPE_DOT_OPTIONAL_SPACE^# // PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/Erase[1]: ?.friends[#[Person]#]; name=friends // PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/Erase[1]: ?.bestFriend[#Person?#]; name=bestFriend // PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/Erase[1]: ?.itself[#Person#]; name=itself -// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[Subscript]/CurrNominal/Erase[1]: ?[{#(index): Int#}][#Int#]; name=[index: Int] +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[Subscript]/CurrNominal/Erase[1]: ?[{#(index): Int#}][#Int#]; name=[:] // PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: unsafelyUnwrapped[#Person#]; name=unsafelyUnwrapped // PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal: optMember[#String#]; name=optMember // PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: debugDescription[#String#]; name=debugDescription diff --git a/test/IDE/complete_type.swift b/test/IDE/complete_type.swift index ca8a1f8c6fcc2..895ade7aabdcc 100644 --- a/test/IDE/complete_type.swift +++ b/test/IDE/complete_type.swift @@ -43,6 +43,15 @@ typealias FooTypealias = Int // WITH_GLOBAL_TYPES-DAG: Decl[TypeAlias]/CurrModule: FooTypealias[#Int#]{{; name=.+$}} // WITH_GLOBAL_TYPES: End completions +// WITH_GLOBAL_TYPES_EXPR: Begin completions +// Global completions at expression position +// WITH_GLOBAL_TYPES_EXPR-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}} +// WITH_GLOBAL_TYPES_EXPR-DAG: Decl[Enum]/CurrModule: FooEnum[#FooEnum#]{{; name=.+$}} +// WITH_GLOBAL_TYPES_EXPR-DAG: Decl[Class]/CurrModule: FooClass[#FooClass#]{{; name=.+$}} +// WITH_GLOBAL_TYPES_EXPR-DAG: Decl[Protocol]/CurrModule/Flair[RareType]: FooProtocol[#FooProtocol#]{{; name=.+$}} +// WITH_GLOBAL_TYPES_EXPR-DAG: Decl[TypeAlias]/CurrModule: FooTypealias[#Int#]{{; name=.+$}} +// WITH_GLOBAL_TYPES_EXPR: End completions + // GLOBAL_NEGATIVE-NOT: fooObject // GLOBAL_NEGATIVE-NOT: fooFunc @@ -337,7 +346,8 @@ struct TypeInStructInheritance2 : , #^TYPE_IN_STRUCT_INHERITANCE_2?check=WITH_GL struct TypeInStructInheritance3 : FooProtocol, #^TYPE_IN_STRUCT_INHERITANCE_3?check=WITH_GLOBAL_TYPES;check=GLOBAL_NEGATIVE^# -struct TypeInStructInheritance4 : FooProtocol., #^TYPE_IN_STRUCT_INHERITANCE_4?check=WITH_GLOBAL_TYPES^# +// FIXME: 'check' shold be 'WITH_GLOBAL_TYPES' +struct TypeInStructInheritance4 : FooProtocol., #^TYPE_IN_STRUCT_INHERITANCE_4?check=WITH_GLOBAL_TYPES_EXPR^# struct TypeInStructInheritance5 : #^TYPE_IN_STRUCT_INHERITANCE_5?check=WITH_GLOBAL_TYPES;check=GLOBAL_NEGATIVE^# { } @@ -348,7 +358,8 @@ struct TypeInStructInheritance6 : , #^TYPE_IN_STRUCT_INHERITANCE_6?check=WITH_GL struct TypeInStructInheritance7 : FooProtocol, #^TYPE_IN_STRUCT_INHERITANCE_7?check=WITH_GLOBAL_TYPES;check=GLOBAL_NEGATIVE^# { } -struct TypeInStructInheritance8 : FooProtocol., #^TYPE_IN_STRUCT_INHERITANCE_8?check=WITH_GLOBAL_TYPES^# { +// FIXME: 'check' shold be 'WITH_GLOBAL_TYPES' +struct TypeInStructInheritance8 : FooProtocol., #^TYPE_IN_STRUCT_INHERITANCE_8?check=WITH_GLOBAL_TYPES_EXPR^# { } //===--- @@ -779,3 +790,24 @@ struct ContainExtension { // EXTENSION_INHERITANCE-DAG: Decl[TypeAlias]/CurrNominal: ProtoAlias[#FooProtocol#]; // EXTENSION_INHERITANCE-DAG: Keyword/None: Type[#HasProtoAlias.Type#]; // EXTENSION_INHERITANCE: End completions + +var _: (() -> #^IN_POSTFIX_BASE_1?check=WITH_GLOBAL_TYPES^#)? +var _: (() -> #^IN_POSTFIX_BASE_2?check=WITH_GLOBAL_TYPES^#)! +var _: (() -> #^IN_POSTFIX_BASE_3?check=WITH_GLOBAL_TYPES^#)[1] +var _: (() -> #^IN_POSTFIX_BASE_4?check=WITH_GLOBAL_TYPES^#).Protocol +var _: (() -> #^IN_POSTFIX_BASE_5?check=WITH_GLOBAL_TYPES^#).Type + +struct HaveNested { + struct Nested {} +} + +var _: HaveNested.#^IN_POSTFIX_BASE_MEMBER_1?check=POSTFIX_BASE_MEMBER^#? +var _: HaveNested.#^IN_POSTFIX_BASE_MEMBER_2?check=POSTFIX_BASE_MEMBER^#! +var _: HaveNested.#^IN_POSTFIX_BASE_MEMBER_3?check=POSTFIX_BASE_MEMBER^#[1] +var _: HaveNested.#^IN_POSTFIX_BASE_MEMBER_4?check=POSTFIX_BASE_MEMBER^#.Protocol +var _: HaveNested.#^IN_POSTFIX_BASE_MEMBER_5?check=POSTFIX_BASE_MEMBER^#.Type + +// POSTFIX_BASE_MEMBER: Begin completions, 2 items +// POSTFIX_BASE_MEMBER-DAG: Decl[Struct]/CurrNominal: Nested[#HaveNested.Nested#]; +// POSTFIX_BASE_MEMBER-DAG: Keyword/None: Type[#HaveNested.Type#]; +// POSTFIX_BASE_MEMBER: End completions diff --git a/test/IDE/complete_type_attribute.swift b/test/IDE/complete_type_attribute.swift new file mode 100644 index 0000000000000..3ca780fb21428 --- /dev/null +++ b/test/IDE/complete_type_attribute.swift @@ -0,0 +1,43 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +// TYPEATTR: Begin completions +// TYPEATTR-NOT: myIntValue +// TYPEATTR-DAG: Keyword/None: autoclosure[#Type Attribute#]; name=autoclosure +// TYPEATTR-DAG: Keyword/None: convention(swift)[#Type Attribute#]; name=convention(swift) +// TYPEATTR-DAG: Keyword/None: convention(block)[#Type Attribute#]; name=convention(block) +// TYPEATTR-DAG: Keyword/None: convention(c)[#Type Attribute#]; name=convention(c) +// TYPEATTR-DAG: Keyword/None: convention(thin)[#Type Attribute#]; name=convention(thin) +// TYPEATTR-DAG: Keyword/None: escaping[#Type Attribute#]; name=escaping +// TYPEATTR-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct +// TYPEATTR-NOT: myIntValue +// TYPEATTR: End completions + +struct MyStruct {} + +var myIntValue: Int = 1 + +func foo() -> @#^FUNC_RESULT?check=TYPEATTR^# {} + +func foo(x: @#^FUNC_PARAM?check=TYPEATTR^#) {} + +func foo(x: (Int, @#^CLOSURE_PARAM?check=TYPEATTR^#) -> Void) {} + +func foo(x: (Int) -> @#^CLOSURE_RESULT?check=TYPEATTR^#) {} +func foo(x: Array<@#^GENERIC_ARGS?check=TYPEATTR^#>) {} + +func foo() where T.Something == @#^WHERE_CLAUSE?check=TYPEATTR^# {} + +func test() { + let value: @#^VARDECL?check=TYPEATTR^# +} +func test() { + let value: @Sendable @#^MULTIPLE?check=TYPEATTR^# +} +func test() { + typealias A = @#^TYPEALIAS?check=TYPEATTR^# +} +func test(thing: Any) { + let _ = thing as? @#^CAST?check=TYPEATTR^# +} + diff --git a/test/IDE/complete_uninferred_generic.swift b/test/IDE/complete_uninferred_generic.swift index 5ae930730aaff..f2ea318141a5d 100644 --- a/test/IDE/complete_uninferred_generic.swift +++ b/test/IDE/complete_uninferred_generic.swift @@ -13,6 +13,6 @@ struct S2 : P1 { } _ = S2()#^UNINFERRED^# -// UNINFERRED: Decl[Subscript]/Super: [{#(v0): T#}][#Int#]; name=[v0: T] -// UNINFERRED: Decl[Subscript]/Super: [{#(v0): T#}][#<>#]; name=[v0: T] +// UNINFERRED: Decl[Subscript]/Super: [{#(v0): T#}][#Int#]; name=[:] +// UNINFERRED: Decl[Subscript]/Super: [{#(v0): T#}][#<>#]; name=[:] // UNINFERRED: Keyword[self]/CurrNominal: .self[#S2<_>#]; name=self diff --git a/test/IDE/complete_unresolved_chains.swift b/test/IDE/complete_unresolved_chains.swift index f1d2930ce7e17..0d76a217c48ed 100644 --- a/test/IDE/complete_unresolved_chains.swift +++ b/test/IDE/complete_unresolved_chains.swift @@ -48,7 +48,7 @@ func testChains() { // UNRESOLVED_CHAIN_2-DAG: Decl[InstanceVar]/CurrNominal: chainStruct2[#ChainStruct2#]; name=chainStruct2 // UNRESOLVED_CHAIN_2-DAG: Decl[InstanceMethod]/CurrNominal: chainStruct2Func()[#ChainStruct2#]; name=chainStruct2Func() // UNRESOLVED_CHAIN_2-DAG: Decl[InstanceVar]/CurrNominal: hashValue[#Int#]; name=hashValue -// UNRESOLVED_CHAIN_2-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#into: &Hasher#})[#Void#]; name=hash(into: &Hasher) +// UNRESOLVED_CHAIN_2-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#into: &Hasher#})[#Void#]; name=hash(into:) // UNRESOLVED_CHAIN_2: End completions // UNRESOLVED_CHAIN_3: Begin completions, 5 items diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 5655a10887505..0cc2bb602ed85 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -98,9 +98,9 @@ class C2 { // UNRESOLVED_1: Begin completions // UNRESOLVED_1-NOT: SomeEnum1 // UNRESOLVED_1-NOT: SomeEnum2 -// UNRESOLVED_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option1[#SomeOptions1#]; name=Option1 -// UNRESOLVED_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option2[#SomeOptions1#]; name=Option2 -// UNRESOLVED_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option3[#SomeOptions1#]; name=Option3 +// UNRESOLVED_1-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option1[#SomeOptions1#]; name=Option1 +// UNRESOLVED_1-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option2[#SomeOptions1#]; name=Option2 +// UNRESOLVED_1-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option3[#SomeOptions1#]; name=Option3 // UNRESOLVED_1-DAG: Decl[StaticVar]/CurrNominal: NotOption[#Int#]; name=NotOption // UNRESOLVED_1-NOT: NotStaticOption } @@ -115,9 +115,9 @@ class C3 { // UNRESOLVED_2: Begin completions // UNRESOLVED_2-NOT: SomeEnum1 // UNRESOLVED_2-NOT: SomeEnum2 -// UNRESOLVED_2-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option4[#SomeOptions2#]; name=Option4 -// UNRESOLVED_2-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option5[#SomeOptions2#]; name=Option5 -// UNRESOLVED_2-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option6[#SomeOptions2#]; name=Option6 +// UNRESOLVED_2-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option4[#SomeOptions2#]; name=Option4 +// UNRESOLVED_2-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option5[#SomeOptions2#]; name=Option5 +// UNRESOLVED_2-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option6[#SomeOptions2#]; name=Option6 // UNRESOLVED_2-NOT: Not } @@ -145,15 +145,15 @@ class C4 { // Exhaustive to make sure we don't include `SomeOptions1`, `SomeOptions2`, `none` or `some` entries. // UNRESOLVED_3: Begin completions, 3 items -// UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: North[#SomeEnum1#]; name=North -// UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: South[#SomeEnum1#]; name=South -// UNRESOLVED_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(self: SomeEnum1) +// UNRESOLVED_3-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: North[#SomeEnum1#]; name=North +// UNRESOLVED_3-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: South[#SomeEnum1#]; name=South +// UNRESOLVED_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) // UNRESOLVED_3: End completions // Exhaustive to make sure we don't include `init({#(some):` or `init({#nilLiteral:` entries // UNRESOLVED_3_OPT: Begin completions, 9 items -// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: North[#SomeEnum1#]; -// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: South[#SomeEnum1#]; +// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; +// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; // UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; // UNRESOLVED_3_OPT-DAG: Keyword[nil]/None/Erase[1]/TypeRelation[Identical]: nil[#SomeEnum1?#]; name=nil // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional#]; name=none @@ -165,8 +165,8 @@ class C4 { // Exhaustive to make sure we don't include `init({#(some):` or `init({#nilLiteral:` entries // UNRESOLVED_3_OPTOPTOPT: Begin completions, 9 items -// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: North[#SomeEnum1#]; -// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: South[#SomeEnum1#]; +// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; +// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; // UNRESOLVED_3_OPTOPTOPT-DAG: Keyword[nil]/None/Erase[1]/TypeRelation[Identical]: nil[#SomeEnum1???#]; name=nil // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional#]; name=none @@ -186,13 +186,13 @@ extension Optional where Wrapped == Somewhere { func testOptionalWithCustomExtension() { var _: Somewhere? = .#^UNRESOLVED_OPT_4^# // UNRESOLVED_OPT_4: Begin completions, 11 items -// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: earth[#Somewhere#]; -// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: mars[#Somewhere#]; +// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: earth[#Somewhere#]; +// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: mars[#Somewhere#]; // UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Somewhere#})[#(into: inout Hasher) -> Void#]; // UNRESOLVED_OPT_4-DAG: Keyword[nil]/None/Erase[1]/TypeRelation[Identical]: nil[#Somewhere?#]; name=nil // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional#]; name=none // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: some({#Somewhere#})[#Optional#]; -// UNRESOLVED_OPT_4-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init({#str: String#})[#Optional#]; name=init(str: String) +// UNRESOLVED_OPT_4-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init({#str: String#})[#Optional#]; name=init(str:) // UNRESOLVED_OPT_4-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: nowhere[#Optional#]; name=nowhere // UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional#})[#((Somewhere) throws -> U) -> U?#]; // UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional#})[#((Somewhere) throws -> U?) -> U?#]; @@ -229,9 +229,9 @@ Container.EnumTaker1(.#^UNRESOLVED_20?check=UNRESOLVED_3^# func parserSync() {} // UNRESOLVED_4: Begin completions -// UNRESOLVED_4-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option1[#SomeOptions1#]; name=Option1 -// UNRESOLVED_4-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option2[#SomeOptions1#]; name=Option2 -// UNRESOLVED_4-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option3[#SomeOptions1#]; name=Option3 +// UNRESOLVED_4-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option1[#SomeOptions1#]; name=Option1 +// UNRESOLVED_4-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option2[#SomeOptions1#]; name=Option2 +// UNRESOLVED_4-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option3[#SomeOptions1#]; name=Option3 // UNRESOLVED_4-NOT: Option4 // UNRESOLVED_4-NOT: Option5 // UNRESOLVED_4-NOT: Option6 @@ -307,8 +307,8 @@ func testAvail1(_ x: EnumAvail1) { } // ENUM_AVAIL_1: Begin completions, 3 items // ENUM_AVAIL_1-NOT: AAA -// ENUM_AVAIL_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: aaa[#EnumAvail1#]; -// ENUM_AVAIL_1-DAG: Decl[EnumElement]/ExprSpecific/NotRecommended/TypeRelation[Identical]: BBB[#EnumAvail1#]; +// ENUM_AVAIL_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: aaa[#EnumAvail1#]; +// ENUM_AVAIL_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/NotRecommended/TypeRelation[Identical]: BBB[#EnumAvail1#]; // ENUM_AVAIL_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): EnumAvail1#})[#(into: inout Hasher) -> Void#]; // ENUM_AVAIL_1-NOT: AAA // ENUM_AVAIL_1: End completions @@ -318,8 +318,8 @@ func testAvail2(_ x: OptionsAvail1) { } // OPTIONS_AVAIL_1: Begin completions // OPTIONS_AVAIL_1-NOT: AAA -// OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: aaa[#OptionsAvail1#]; -// OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/ExprSpecific/NotRecommended/TypeRelation[Identical]: BBB[#OptionsAvail1#]; +// OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: aaa[#OptionsAvail1#]; +// OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/NotRecommended/TypeRelation[Identical]: BBB[#OptionsAvail1#]; // OPTIONS_AVAIL_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init({#rawValue: Int#})[#OptionsAvail1#] // OPTIONS_AVAIL_1-NOT: AAA // OPTIONS_AVAIL_1: End completions @@ -334,7 +334,7 @@ func testWithLiteral1() { let s: S _ = s.takeEnum(thing: .#^WITH_LITERAL_1^#, other: 1.0) // WITH_LITERAL_1: Begin completions, 2 items -// WITH_LITERAL_1-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myCase[#S.MyEnum#]; +// WITH_LITERAL_1-NEXT: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: myCase[#S.MyEnum#]; // WITH_LITERAL_1-NEXT: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): S.MyEnum#})[#(into: inout Hasher) -> Void#]; // WITH_LITERAL_1-NEXT: End completions } @@ -359,7 +359,7 @@ func testWithLiteral3() { func test(s: S) { _ = s.takeEnum(thing: .#^WITH_LITERAL_3^#, other: 1.0) // WITH_LITERAL_3: Begin completions, 2 items -// WITH_LITERAL_3-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myCase[#MyEnum#]; +// WITH_LITERAL_3-NEXT: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: myCase[#MyEnum#]; // WITH_LITERAL_3-NEXT: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): MyEnum#})[#(into: inout Hasher) -> Void#]; // WITH_LITERAL_3-NEXT: End completions } @@ -376,9 +376,9 @@ func enumFromOtherFile() -> EnumFromOtherFile { return .#^OTHER_FILE_1^# // Don't crash. } // OTHER_FILE_1: Begin completions -// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: b({#String#})[#EnumFromOtherFile#]; -// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: a({#Int#})[#EnumFromOtherFile#]; -// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: c[#EnumFromOtherFile#]; +// OTHER_FILE_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: b({#String#})[#EnumFromOtherFile#]; +// OTHER_FILE_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: a({#Int#})[#EnumFromOtherFile#]; +// OTHER_FILE_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: c[#EnumFromOtherFile#]; // OTHER_FILE_1: End completions struct NonOptSet { @@ -397,11 +397,11 @@ func testNonOptSet() { x = .#^NON_OPT_SET_1^# } // NON_OPT_SET_1: Begin completions, 6 items -// NON_OPT_SET_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: a[#NonOptSet#] +// NON_OPT_SET_1-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: a[#NonOptSet#] // NON_OPT_SET_1-DAG: Decl[StaticVar]/CurrNominal: wrongType[#Int#]; // NON_OPT_SET_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init({#x: Int#}, {#y: Int#})[#NonOptSet#] // NON_OPT_SET_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#NonOptSet#] -// NON_OPT_SET_1-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: b()[#NonOptSet#] +// NON_OPT_SET_1-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: b()[#NonOptSet#] // NON_OPT_SET_1-DAG: Decl[InstanceMethod]/CurrNominal: notStatic({#(self): NonOptSet#})[#() -> NonOptSet#]; // NON_OPT_SET_1: End completions @@ -420,8 +420,8 @@ func testInStringInterpolation() { let y = "enum: \(.#^STRING_INTERPOLATION_INVALID?check=NOCRASH^#)" // Dont'crash. } // STRING_INTERPOLATION_1: Begin completions -// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#MyEnum#]; -// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#MyEnum#]; +// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: foo[#MyEnum#]; +// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#MyEnum#]; // STRING_INTERPOLATION_1: End completions class BaseClass { @@ -439,18 +439,22 @@ struct AnotherTy: MyProtocol {} func testSubType() { var _: BaseClass = .#^SUBTYPE_1^# } -// SUBTYPE_1: Begin completions, 4 items -// SUBTYPE_1-NOT: Concrete1( +// SUBTYPE_1: Begin completions, 6 items // SUBTYPE_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#BaseClass#]; // SUBTYPE_1-DAG: Decl[Class]/CurrNominal/TypeRelation[Convertible]: SubClass[#BaseClass.SubClass#]; // SUBTYPE_1-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: subInstance[#BaseClass.SubClass#]; // SUBTYPE_1-DAG: Decl[Constructor]/CurrNominal: init({#failable: Void#})[#BaseClass?#]; +// SUBTYPE_1-DAG: Decl[TypeAlias]/Super/TypeRelation[Identical]: Concrete1[#BaseClass#]; +// SUBTYPE_1-DAG: Decl[TypeAlias]/Super: Concrete2[#AnotherTy#]; // SUBTYPE_1: End completions func testMemberTypealias() { var _: MyProtocol = .#^SUBTYPE_2^# } -// SUBTYPE_2-NOT: Begin completions +// SUBTYPE_2: Begin completions, 2 items +// SUBTYPE_2-DAG: Decl[TypeAlias]/CurrNominal/TypeRelation[Convertible]: Concrete1[#BaseClass#]; +// SUBTYPE_2-DAG: Decl[TypeAlias]/CurrNominal/TypeRelation[Convertible]: Concrete2[#AnotherTy#]; +// SUBTYPE_2: End completions enum Generic { case contains(content: T) @@ -471,15 +475,14 @@ switch Generic.empty { case let .#^GENERIC_4?check=GENERIC_1_INT^# } // GENERIC_1_INT: Begin completions -// GENERIC_1_INT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: contains({#content: Int#})[#Generic#]; -// GENERIC_1_INT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: empty[#Generic#]; -// GENERIC_1_INT-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: create({#Int#})[#Generic#]; +// GENERIC_1_INT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: contains({#content: Int#})[#Generic#]; +// GENERIC_1_INT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: empty[#Generic#]; // GENERIC_1_INT: End completions // GENERIC_1_U: Begin completions -// GENERIC_1_U-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: contains({#content: U#})[#Generic#]; -// GENERIC_1_U-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: empty[#Generic#]; -// GENERIC_1_U-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: create({#U#})[#Generic#]; +// GENERIC_1_U-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: contains({#content: U#})[#Generic#]; +// GENERIC_1_U-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: empty[#Generic#]; +// GENERIC_1_U-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: create({#U#})[#Generic#]; // GENERIC_1_U: End completions struct HasCreator { @@ -505,11 +508,11 @@ struct HasOverloaded { func testOverload(val: HasOverloaded) { let _ = val.takeEnum(.#^OVERLOADED_METHOD_1^#) // OVERLOADED_METHOD_1: Begin completions, 6 items -// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: South[#SomeEnum1#]; name=South -// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: North[#SomeEnum1#]; name=North +// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: South[#SomeEnum1#]; name=South +// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: North[#SomeEnum1#]; name=North // OVERLOADED_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; -// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: East[#SomeEnum2#]; name=East -// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: West[#SomeEnum2#]; name=West +// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: East[#SomeEnum2#]; name=East +// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: West[#SomeEnum2#]; name=West // OVERLOADED_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum2#})[#(into: inout Hasher) -> Void#]; // OVERLOADED_METHOD_1: End completions @@ -520,14 +523,14 @@ func testOverload(val: HasOverloaded) { // Same as OVERLOADED_METHOD_1. } -protocol HasStatic { +protocol HasStatic: Equatable { static var instance: Self { get } } func receiveHasStatic(x: T) {} func testingGenericParam1(x: inout T, fn: (T) -> Void) -> T { x = .#^GENERICPARAM_1^# // GENERICPARAM_1: Begin completions, 1 items -// GENERICPARAM_1: Decl[StaticVar]/{{ExprSpecific|CurrNominal}}/TypeRelation[Identical]: instance[#HasStatic#]; name=instance +// GENERICPARAM_1: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: instance[#HasStatic#]; name=instance // GENERICPARAM_1: End completions /* Parser sync. */; @@ -669,11 +672,11 @@ func testClosureReturnTypeForOverloaded() { .#^OVERLOADED_CLOSURE_RETURN^# } // OVERLOADED_CLOSURE_RETURN: Begin completions, 6 items -// OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: South[#SomeEnum1#]; -// OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: North[#SomeEnum1#]; +// OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: South[#SomeEnum1#]; +// OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: North[#SomeEnum1#]; // OVERLOADED_CLOSURE_RETURN-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; -// OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: East[#SomeEnum2#]; -// OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: West[#SomeEnum2#]; +// OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: East[#SomeEnum2#]; +// OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: West[#SomeEnum2#]; // OVERLOADED_CLOSURE_RETURN-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): SomeEnum2#})[#(into: inout Hasher) -> Void#]; // OVERLOADED_CLOSURE_RETURN: End completions } @@ -706,8 +709,8 @@ func testSameType() { testSugarType(.#^SUGAR_TYPE^# // Ensure results aren't duplicated. // SUGAR_TYPE: Begin completions, 9 items -// SUGAR_TYPE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: South[#SomeEnum1#]; -// SUGAR_TYPE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: North[#SomeEnum1#]; +// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: South[#SomeEnum1#]; +// SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: North[#SomeEnum1#]; // SUGAR_TYPE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; // SUGAR_TYPE-DAG: Keyword[nil]/None/Erase[1]/TypeRelation[Identical]: nil[#SomeEnum1?#]; // SUGAR_TYPE-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional#]; diff --git a/test/IDE/complete_value_expr.swift b/test/IDE/complete_value_expr.swift index f3b3424da4cb8..cb02877933e1f 100644 --- a/test/IDE/complete_value_expr.swift +++ b/test/IDE/complete_value_expr.swift @@ -253,7 +253,7 @@ var fooObject: FooStruct // FOO_STRUCT_DOT-NEXT: Decl[StaticMethod]/CurrNominal: overloadedStaticFunc2({#(x): Int#})[#Int#]{{; name=.+$}} // FOO_STRUCT_DOT-NEXT: Decl[StaticMethod]/CurrNominal: overloadedStaticFunc2({#(x): Double#})[#Int#]{{; name=.+$}} // FOO_STRUCT_DOT-NEXT: Decl[Constructor]/CurrNominal: init()[#FooStruct#]; name=init(){{$}} -// FOO_STRUCT_DOT-NEXT: Decl[Constructor]/CurrNominal: init({#lazyInstanceVar: Int?#}, {#instanceVar: Int#})[#FooStruct#]; name=init(lazyInstanceVar: Int?, instanceVar: Int){{$}} +// FOO_STRUCT_DOT-NEXT: Decl[Constructor]/CurrNominal: init({#lazyInstanceVar: Int?#}, {#instanceVar: Int#})[#FooStruct#]; name=init(lazyInstanceVar:instanceVar:){{$}} // FOO_STRUCT_DOT-NEXT: Decl[Constructor]/CurrNominal: init()[#FooStruct#]; name=init(){{$}} // FOO_STRUCT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: extFunc0({#(self): &FooStruct#})[#() -> Void#]{{; name=.+$}} // FOO_STRUCT_DOT-NEXT: Decl[StaticVar]/CurrNominal: extStaticProp[#Int#]{{; name=.+$}} @@ -301,9 +301,9 @@ var fooObject: FooStruct // FOO_STRUCT_NO_DOT-NEXT: Decl[StaticMethod]/CurrNominal: .overloadedStaticFunc1()[#Double#]{{; name=.+$}} // FOO_STRUCT_NO_DOT-NEXT: Decl[StaticMethod]/CurrNominal: .overloadedStaticFunc2({#(x): Int#})[#Int#]{{; name=.+$}} // FOO_STRUCT_NO_DOT-NEXT: Decl[StaticMethod]/CurrNominal: .overloadedStaticFunc2({#(x): Double#})[#Int#]{{; name=.+$}} -// FOO_STRUCT_NO_DOT-NEXT: Decl[Constructor]/CurrNominal: ()[#FooStruct#]{{; name=.+$}} -// FOO_STRUCT_NO_DOT-NEXT: Decl[Constructor]/CurrNominal: ({#lazyInstanceVar: Int?#}, {#instanceVar: Int#})[#FooStruct#]{{; name=.+$}} -// FOO_STRUCT_NO_DOT-NEXT: Decl[Constructor]/CurrNominal: ()[#FooStruct#]{{; name=.+$}} +// FOO_STRUCT_NO_DOT-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooStruct#]{{; name=.+$}} +// FOO_STRUCT_NO_DOT-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#lazyInstanceVar: Int?#}, {#instanceVar: Int#})[#FooStruct#]{{; name=.+$}} +// FOO_STRUCT_NO_DOT-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooStruct#]{{; name=.+$}} // FOO_STRUCT_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .extFunc0({#(self): &FooStruct#})[#() -> Void#]{{; name=.+$}} // FOO_STRUCT_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .extStaticProp[#Int#]{{; name=.+$}} // FOO_STRUCT_NO_DOT-NEXT: Decl[StaticMethod]/CurrNominal: .extStaticFunc0()[#Void#]{{; name=.+$}} @@ -373,37 +373,37 @@ func testMetatypeCompletionsWithoutDot() { func testImplicitlyCurriedFunc(_ fs: inout FooStruct) { FooStruct.instanceFunc0(&fs)#^IMPLICITLY_CURRIED_FUNC_0^# // IMPLICITLY_CURRIED_FUNC_0: Begin completions -// IMPLICITLY_CURRIED_FUNC_0-NEXT: Decl[InstanceMethod]/CurrNominal: ()[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_FUNC_0-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ()[#Void#]{{; name=.+$}} // IMPLICITLY_CURRIED_FUNC_0-NEXT: Keyword[self]/CurrNominal: .self[#() -> ()#]; name=self // IMPLICITLY_CURRIED_FUNC_0-NEXT: End completions FooStruct.instanceFunc1(&fs)#^IMPLICITLY_CURRIED_FUNC_1^# // IMPLICITLY_CURRIED_FUNC_1: Begin completions -// IMPLICITLY_CURRIED_FUNC_1-NEXT: Decl[InstanceMethod]/CurrNominal: ({#(a): Int#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_FUNC_1-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(a): Int#})[#Void#]{{; name=.+$}} // IMPLICITLY_CURRIED_FUNC_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> ()#]; name=self // IMPLICITLY_CURRIED_FUNC_1-NEXT: End completions FooStruct.instanceFunc2(&fs)#^IMPLICITLY_CURRIED_FUNC_2^# // IMPLICITLY_CURRIED_FUNC_2: Begin completions -// IMPLICITLY_CURRIED_FUNC_2-NEXT: Decl[InstanceMethod]/CurrNominal: ({#(a): Int#}, {#b: &Double#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_FUNC_2-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(a): Int#}, {#b: &Double#})[#Void#]{{; name=.+$}} // IMPLICITLY_CURRIED_FUNC_2-NEXT: Keyword[self]/CurrNominal: .self[#(Int, inout Double) -> ()#]; name=self // IMPLICITLY_CURRIED_FUNC_2-NEXT: End completions FooStruct.varargInstanceFunc0(&fs)#^IMPLICITLY_CURRIED_VARARG_FUNC_0^# // IMPLICITLY_CURRIED_VARARG_FUNC_0: Begin completions -// IMPLICITLY_CURRIED_VARARG_FUNC_0-NEXT: Decl[InstanceMethod]/CurrNominal: ({#(v): Int...#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_VARARG_FUNC_0-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(v): Int...#})[#Void#]{{; name=.+$}} // IMPLICITLY_CURRIED_VARARG_FUNC_0-NEXT: Keyword[self]/CurrNominal: .self[#(Int...) -> ()#]; name=self // IMPLICITLY_CURRIED_VARARG_FUNC_0-NEXT: End completions FooStruct.varargInstanceFunc1(&fs)#^IMPLICITLY_CURRIED_VARARG_FUNC_1^# // IMPLICITLY_CURRIED_VARARG_FUNC_1: Begin completions -// IMPLICITLY_CURRIED_VARARG_FUNC_1-NEXT: Decl[InstanceMethod]/CurrNominal: ({#(a): Float#}, {#v: Int...#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_VARARG_FUNC_1-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(a): Float#}, {#v: Int...#})[#Void#]{{; name=.+$}} // IMPLICITLY_CURRIED_VARARG_FUNC_1-NEXT: Keyword[self]/CurrNominal: .self[#(Float, Int...) -> ()#]; name=self // IMPLICITLY_CURRIED_VARARG_FUNC_1-NEXT: End completions FooStruct.varargInstanceFunc2(&fs)#^IMPLICITLY_CURRIED_VARARG_FUNC_2^# // IMPLICITLY_CURRIED_VARARG_FUNC_2: Begin completions -// IMPLICITLY_CURRIED_VARARG_FUNC_2-NEXT: Decl[InstanceMethod]/CurrNominal: ({#(a): Float#}, {#b: Double#}, {#v: Int...#})[#Void#]{{; name=.+$}} +// IMPLICITLY_CURRIED_VARARG_FUNC_2-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(a): Float#}, {#b: Double#}, {#v: Int...#})[#Void#]{{; name=.+$}} // IMPLICITLY_CURRIED_VARARG_FUNC_2-NEXT: Keyword[self]/CurrNominal: .self[#(Float, Double, Int...) -> ()#]; name=self // IMPLICITLY_CURRIED_VARARG_FUNC_2-NEXT: End completions @@ -539,7 +539,7 @@ func testInsideFunctionCall1() { var a = FooStruct() a.instanceFunc0(#^INSIDE_FUNCTION_CALL_1^# // INSIDE_FUNCTION_CALL_1: Begin completions, 1 items -// INSIDE_FUNCTION_CALL_1: Decl[InstanceMethod]/CurrNominal: ['('][')'][#Void#]; name= +// INSIDE_FUNCTION_CALL_1: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['('][')'][#Void#]; name= // INSIDE_FUNCTION_CALL_1: End completions } @@ -547,7 +547,7 @@ func testInsideFunctionCall2() { var a = FooStruct() a.instanceFunc1(#^INSIDE_FUNCTION_CALL_2^# // INSIDE_FUNCTION_CALL_2: Begin completions -// INSIDE_FUNCTION_CALL_2-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(a): Int#}[')'][#Void#]{{; name=.+$}} +// INSIDE_FUNCTION_CALL_2-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(a): Int#}[')'][#Void#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_2-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_2: End completions } @@ -561,7 +561,7 @@ func testInsideFunctionCall4() { var a = FooStruct() a.instanceFunc2(#^INSIDE_FUNCTION_CALL_4^# // INSIDE_FUNCTION_CALL_4: Begin completions -// INSIDE_FUNCTION_CALL_4-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(a): Int#}, {#b: &Double#}[')'][#Void#]{{; name=.+$}} +// INSIDE_FUNCTION_CALL_4-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(a): Int#}, {#b: &Double#}[')'][#Void#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_4-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_4: End completions } @@ -569,7 +569,7 @@ func testInsideFunctionCall4() { func testInsideFunctionCall5() { FooStruct().instanceFunc2(42, #^INSIDE_FUNCTION_CALL_5^# // INSIDE_FUNCTION_CALL_5: Begin completions -// INSIDE_FUNCTION_CALL_5-DAG: Pattern/ExprSpecific: {#b: &Double#}[#inout Double#]; +// INSIDE_FUNCTION_CALL_5-DAG: Pattern/Local/Flair[ArgLabels]: {#b: &Double#}[#inout Double#]; // INSIDE_FUNCTION_CALL_5: End completions } @@ -577,7 +577,7 @@ func testInsideFunctionCall6() { var a = FooStruct() a.instanceFunc7(#^INSIDE_FUNCTION_CALL_6^# // INSIDE_FUNCTION_CALL_6: Begin completions -// INSIDE_FUNCTION_CALL_6-NEXT: Decl[InstanceMethod]/CurrNominal: ['(']{#a: Int#}[')'][#Void#]{{; name=.+$}} +// INSIDE_FUNCTION_CALL_6-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Int#}[')'][#Void#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_6-NEXT: End completions } @@ -585,21 +585,21 @@ func testInsideFunctionCall7() { var a = FooStruct() a.instanceFunc8(#^INSIDE_FUNCTION_CALL_7^# // INSIDE_FUNCTION_CALL_7: Begin completions -// INSIDE_FUNCTION_CALL_7: Decl[InstanceMethod]/CurrNominal: ['(']{#(a): (Int, Int)#}[')'][#Void#]{{; name=.+$}} +// INSIDE_FUNCTION_CALL_7: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(a): (Int, Int)#}[')'][#Void#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_7: End completions } func testInsideFunctionCall8(_ x: inout FooStruct) { x.instanceFunc0(#^INSIDE_FUNCTION_CALL_8^#) // INSIDE_FUNCTION_CALL_8: Begin completions -// INSIDE_FUNCTION_CALL_8: Decl[InstanceMethod]/CurrNominal: ['('][')'][#Void#]; name= +// INSIDE_FUNCTION_CALL_8: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['('][')'][#Void#]; name= // INSIDE_FUNCTION_CALL_8: End completions } func testInsideFunctionCall9(_ x: inout FooStruct) { x.instanceFunc1(#^INSIDE_FUNCTION_CALL_9^#) // Annotated ')' // INSIDE_FUNCTION_CALL_9: Begin completions -// INSIDE_FUNCTION_CALL_9-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(a): Int#}[')'][#Void#]{{; name=.+$}} +// INSIDE_FUNCTION_CALL_9-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(a): Int#}[')'][#Void#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_9-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_9: End completions } @@ -607,14 +607,14 @@ func testInsideFunctionCall10(_ x: inout FooStruct) { x.instanceFunc2(#^INSIDE_FUNCTION_CALL_10^#) // Annotated ')' // INSIDE_FUNCTION_CALL_10: Begin completions -// INSIDE_FUNCTION_CALL_10-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(a): Int#}, {#b: &Double#}[')'][#Void#]{{; name=.+$}} +// INSIDE_FUNCTION_CALL_10-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(a): Int#}, {#b: &Double#}[')'][#Void#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_10-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_10: End completions } func testInsideFunctionCall11(_ x: inout FooStruct) { x.instanceFunc2(#^INSIDE_FUNCTION_CALL_11?check=INSIDE_FUNCTION_CALL_4^#, // INSIDE_FUNCTION_CALL_11-NOT: Pattern/{{.*}}:{{.*}}({{.*}}{#Int#} -// INSIDE_FUNCTION_CALL_11B: Decl[InstanceMethod]/CurrNominal: ['(']{#Int#}, {#b: &Double#}[')'][#Void#]; +// INSIDE_FUNCTION_CALL_11B: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#Int#}, {#b: &Double#}[')'][#Void#]; } func testInsideFunctionCall12(_ x: inout FooStruct) { x.instanceFunc2(#^INSIDE_FUNCTION_CALL_12?check=INSIDE_FUNCTION_CALL_4^#<#placeholder#> @@ -625,7 +625,7 @@ func testInsideVarargFunctionCall1() { var a = FooStruct() a.varargInstanceFunc0(#^INSIDE_VARARG_FUNCTION_CALL_1^# // INSIDE_VARARG_FUNCTION_CALL_1: Begin completions -// INSIDE_VARARG_FUNCTION_CALL_1-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(v): Int...#}[')'][#Void#]{{; name=.+$}} +// INSIDE_VARARG_FUNCTION_CALL_1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(v): Int...#}[')'][#Void#]{{; name=.+$}} // INSIDE_VARARG_FUNCTION_CALL_1-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}} // INSIDE_VARARG_FUNCTION_CALL_1: End completions } @@ -656,7 +656,7 @@ func testInsideOverloadedFunctionCall1() { func testInsideFunctionCallOnClassInstance1(_ a: FooClass) { a.fooClassInstanceFunc1(#^INSIDE_FUNCTION_CALL_ON_CLASS_INSTANCE_1^# // INSIDE_FUNCTION_CALL_ON_CLASS_INSTANCE_1: Begin completions -// INSIDE_FUNCTION_CALL_ON_CLASS_INSTANCE_1-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(a): Int#}[')'][#Void#]{{; name=.+$}} +// INSIDE_FUNCTION_CALL_ON_CLASS_INSTANCE_1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(a): Int#}[')'][#Void#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_ON_CLASS_INSTANCE_1-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}} // INSIDE_FUNCTION_CALL_ON_CLASS_INSTANCE_1: End completions } @@ -672,14 +672,14 @@ var funcTypeVarsObject: FuncTypeVars func testFuncTypeVars() { funcTypeVarsObject.funcVar1#^VF1^# // VF1: Begin completions -// VF1-NEXT: Pattern/CurrModule: ()[#Double#]{{; name=.+$}} +// VF1-NEXT: Pattern/CurrModule/Flair[ArgLabels]: ()[#Double#]{{; name=.+$}} // VF1-NEXT: BuiltinOperator/None: = {#() -> Double##() -> Double#}[#Void#] // VF1-NEXT: Keyword[self]/CurrNominal: .self[#() -> Double#]; name=self // VF1-NEXT: End completions funcTypeVarsObject.funcVar2#^VF2^# // VF2: Begin completions -// VF2-NEXT: Pattern/CurrModule: ({#Int#})[#Double#]{{; name=.+$}} +// VF2-NEXT: Pattern/CurrModule/Flair[ArgLabels]: ({#Int#})[#Double#]{{; name=.+$}} // VF2-NEXT: BuiltinOperator/None: = {#(Int) -> Double##(_ a: Int) -> Double#}[#Void#] // VF2-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> Double#]; name=self // VF2-NEXT: End completions @@ -947,8 +947,8 @@ func testFuncParenPattern1(_ fpp: FuncParenPattern) { func testFuncParenPattern2(_ fpp: FuncParenPattern) { FuncParenPattern#^FUNC_PAREN_PATTERN_2^# // FUNC_PAREN_PATTERN_2: Begin completions -// FUNC_PAREN_PATTERN_2-NEXT: Decl[Constructor]/CurrNominal: ({#Int#})[#FuncParenPattern#]{{; name=.+$}} -// FUNC_PAREN_PATTERN_2-NEXT: Decl[Constructor]/CurrNominal: ({#(Int, Int)#})[#FuncParenPattern#]{{; name=.+$}} +// FUNC_PAREN_PATTERN_2-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#Int#})[#FuncParenPattern#]{{; name=.+$}} +// FUNC_PAREN_PATTERN_2-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#(Int, Int)#})[#FuncParenPattern#]{{; name=.+$}} // FUNC_PAREN_PATTERN_2-NEXT: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(self): &FuncParenPattern#})[#(Int) -> Void#]{{; name=.+$}} // FUNC_PAREN_PATTERN_2-NEXT: Keyword[self]/CurrNominal: .self[#FuncParenPattern.Type#]; name=self // FUNC_PAREN_PATTERN_2-NEXT: Keyword/CurrNominal: .Type[#FuncParenPattern.Type#]; name=Type @@ -958,7 +958,7 @@ func testFuncParenPattern2(_ fpp: FuncParenPattern) { func testFuncParenPattern3(_ fpp: inout FuncParenPattern) { fpp.instanceFunc#^FUNC_PAREN_PATTERN_3^# // FUNC_PAREN_PATTERN_3: Begin completions -// FUNC_PAREN_PATTERN_3-NEXT: Decl[InstanceMethod]/CurrNominal: ({#Int#})[#Void#]{{; name=.+$}} +// FUNC_PAREN_PATTERN_3-NEXT: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#Int#})[#Void#]{{; name=.+$}} // FUNC_PAREN_PATTERN_3-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> ()#]; name=self // FUNC_PAREN_PATTERN_3-NEXT: End completions } @@ -1018,8 +1018,8 @@ func testResolveGenericParams1() { FooGenericStruct#^RESOLVE_GENERIC_PARAMS_1_STATIC^# // RESOLVE_GENERIC_PARAMS_1_STATIC: Begin completions -// RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[Constructor]/CurrNominal: ()[#FooGenericStruct#]; name=() -// RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[Constructor]/CurrNominal: ({#t: FooStruct#})[#FooGenericStruct#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooGenericStruct#]; name=() +// RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#t: FooStruct#})[#FooGenericStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(self): &FooGenericStruct#})[#(FooStruct) -> Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(self): &FooGenericStruct#})[#(FooStruct) -> FooStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_1_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(self): &FooGenericStruct#})[#(U) -> U#]{{; name=.+$}} @@ -1046,8 +1046,8 @@ func testResolveGenericParams2(_ foo: Foo) { FooGenericStruct#^RESOLVE_GENERIC_PARAMS_2_STATIC^# // RESOLVE_GENERIC_PARAMS_2_STATIC: Begin completions -// RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[Constructor]/CurrNominal: ()[#FooGenericStruct#]; name=() -// RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[Constructor]/CurrNominal: ({#t: FooProtocol#})[#FooGenericStruct#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooGenericStruct#]; name=() +// RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#t: FooProtocol#})[#FooGenericStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(self): &FooGenericStruct#})[#(FooProtocol) -> Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(self): &FooGenericStruct#})[#(FooProtocol) -> FooProtocol#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_2_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(self): &FooGenericStruct#})[#(U) -> U#]{{; name=.+$}} @@ -1076,8 +1076,8 @@ struct TestResolveGenericParams3_4 { FooGenericStruct#^RESOLVE_GENERIC_PARAMS_3_STATIC^# // RESOLVE_GENERIC_PARAMS_3_STATIC: Begin completions, 12 items -// RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[Constructor]/CurrNominal: ()[#FooGenericStruct#]; name=() -// RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[Constructor]/CurrNominal: ({#t: FooStruct#})[#FooGenericStruct#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooGenericStruct#]; name=() +// RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#t: FooStruct#})[#FooGenericStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(self): &FooGenericStruct#})[#(FooStruct) -> Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(self): &FooGenericStruct#})[#(FooStruct) -> FooStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_3_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(self): &FooGenericStruct#})[#(U) -> U#]{{; name=.+$}} @@ -1104,8 +1104,8 @@ struct TestResolveGenericParams3_4 { FooGenericStruct#^RESOLVE_GENERIC_PARAMS_4_STATIC^# // RESOLVE_GENERIC_PARAMS_4_STATIC: Begin completions -// RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[Constructor]/CurrNominal: ()[#FooGenericStruct#]; name=() -// RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[Constructor]/CurrNominal: ({#t: T#})[#FooGenericStruct#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooGenericStruct#]; name=() +// RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#t: T#})[#FooGenericStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(self): &FooGenericStruct#})[#(T) -> Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(self): &FooGenericStruct#})[#(T) -> T#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_4_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(self): &FooGenericStruct#})[#(U) -> U#]{{; name=.+$}} @@ -1132,8 +1132,8 @@ struct TestResolveGenericParams3_4 { FooGenericStruct#^RESOLVE_GENERIC_PARAMS_5_STATIC^# // RESOLVE_GENERIC_PARAMS_5_STATIC: Begin completions -// RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[Constructor]/CurrNominal: ()[#FooGenericStruct#]; name=() -// RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[Constructor]/CurrNominal: ({#t: U#})[#FooGenericStruct#]{{; name=.+$}} +// RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#FooGenericStruct#]; name=() +// RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#t: U#})[#FooGenericStruct#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooVoidInstanceFunc1({#(self): &FooGenericStruct#})[#(U) -> Void#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooTInstanceFunc1({#(self): &FooGenericStruct#})[#(U) -> U#]{{; name=.+$}} // RESOLVE_GENERIC_PARAMS_5_STATIC-NEXT: Decl[InstanceMethod]/CurrNominal: .fooUInstanceFunc1({#(self): &FooGenericStruct#})[#(U) -> U#]{{; name=.+$}} @@ -1477,34 +1477,34 @@ func checkOverrideInclusion2(_ arg: Override3) { // CHECK_NODUP_RESTATED_REQ_NODOT: Begin completions, 6 items // CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: .foo()[#Void#]; name=foo() // CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: .roo({#arg1: Int#})[#Void#]; -// CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[Subscript]/{{Super|CurrNominal}}: [{#(arg): Bool#}][#Bool#]; name=[arg: Bool] +// CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[Subscript]/{{Super|CurrNominal}}: [{#(arg): Bool#}][#Bool#]; name=[:] // CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[InstanceVar]/{{Super|CurrNominal}}: .doo[#Int#]; name=doo // CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: .roo({#arg2: Int#})[#Void#]; // CHECK_NODUP_RESTATED_REQ_NODOT: End completions // CHECK_NODUP_RESTATED_REQ_TYPE1: Begin completions, 6 items -// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/Super: roo({#(self): NoDupReq6#})[#(arg1: Int) -> Void#]; name=roo(self: NoDupReq6) -// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/CurrNominal: foo({#(self): NoDupReq6#})[#() -> Void#]; name=foo(self: NoDupReq6) -// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq6#})[#(arg2: Int) -> Void#]; name=roo(self: NoDupReq6) +// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/Super: roo({#(self): NoDupReq6#})[#(arg1: Int) -> Void#]; name=roo(:) +// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/CurrNominal: foo({#(self): NoDupReq6#})[#() -> Void#]; name=foo(:) +// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq6#})[#(arg2: Int) -> Void#]; name=roo(:) // CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[AssociatedType]/CurrNominal: E; name=E // CHECK_NODUP_RESTATED_REQ_TYPE1: End completions // CHECK_NODUP_RESTATED_REQ_TYPE2: Begin completions, 6 items -// CHECK_NODUP_RESTATED_REQ_TYPE2: Decl[InstanceMethod]/CurrNominal: foo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#() -> Void#]; name=foo(self: NoDupReq1 & NoDupReq2 & NoDupReq3) -// CHECK_NODUP_RESTATED_REQ_TYPE2: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#(arg1: Int) -> Void#]; name=roo(self: NoDupReq1 & NoDupReq2 & NoDupReq3) +// CHECK_NODUP_RESTATED_REQ_TYPE2: Decl[InstanceMethod]/CurrNominal: foo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#() -> Void#]; name=foo(:) +// CHECK_NODUP_RESTATED_REQ_TYPE2: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#(arg1: Int) -> Void#]; name=roo(:) // CHECK_NODUP_RESTATED_REQ_TYPE2: Decl[AssociatedType]/CurrNominal: E; name=E -// CHECK_NODUP_RESTATED_REQ_TYPE2: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#(arg2: Int) -> Void#]; name=roo(self: NoDupReq1 & NoDupReq2 & NoDupReq3) +// CHECK_NODUP_RESTATED_REQ_TYPE2: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#(arg2: Int) -> Void#]; name=roo(:) // CHECK_NODUP_RESTATED_REQ_TYPE2: End completions // CHECK_NODUP_RESTATED_REQ_TYPE3: Begin completions, 6 items -// CHECK_NODUP_RESTATED_REQ_TYPE3: Decl[InstanceMethod]/CurrNominal: foo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#() -> Void#]; name=foo(self: NoDupReq1 & NoDupReq2 & NoDupReq3) -// CHECK_NODUP_RESTATED_REQ_TYPE3: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#(arg1: Int) -> Void#]; name=roo(self: NoDupReq1 & NoDupReq2 & NoDupReq3) +// CHECK_NODUP_RESTATED_REQ_TYPE3: Decl[InstanceMethod]/CurrNominal: foo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#() -> Void#]; name=foo(:) +// CHECK_NODUP_RESTATED_REQ_TYPE3: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#(arg1: Int) -> Void#]; name=roo(:) // CHECK_NODUP_RESTATED_REQ_TYPE3: Decl[AssociatedType]/CurrNominal: E; name=E -// CHECK_NODUP_RESTATED_REQ_TYPE3: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#(arg2: Int) -> Void#]; name=roo(self: NoDupReq1 & NoDupReq2 & NoDupReq3) +// CHECK_NODUP_RESTATED_REQ_TYPE3: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq1 & NoDupReq2 & NoDupReq3#})[#(arg2: Int) -> Void#]; name=roo(:) // CHECK_NODUP_RESTATED_REQ_TYPE3: End completions -// CHECK_PROT_OVERRIDES: Decl[InstanceMethod]/{{Super|CurrNominal}}: foo({#(arg): NoDupReq1#})[#Void#]; name=foo(arg: NoDupReq1) -// CHECK_PROT_OVERRIDES: Decl[InstanceMethod]/{{Super|CurrNominal}}: foo({#(arg): NoDupReq2#})[#Void#]; name=foo(arg: NoDupReq2) +// CHECK_PROT_OVERRIDES: Decl[InstanceMethod]/{{Super|CurrNominal}}: foo({#(arg): NoDupReq1#})[#Void#]; name=foo(:) +// CHECK_PROT_OVERRIDES: Decl[InstanceMethod]/{{Super|CurrNominal}}: foo({#(arg): NoDupReq2#})[#Void#]; name=foo(:) struct OnlyMe {} protocol P4 { @@ -1617,7 +1617,7 @@ func testProtExtInit1() { } // PROTOCOL_EXT_INIT_1: Begin completions -// PROTOCOL_EXT_INIT_1: Decl[Constructor]/{{Super|CurrNominal}}: ['(']{#x: Int#}[')'][#Concrete1#]{{; name=.+$}} +// PROTOCOL_EXT_INIT_1: Decl[Constructor]/{{Super|CurrNominal}}/Flair[ArgLabels]: ['(']{#x: Int#}[')'][#Concrete1#]{{; name=.+$}} // PROTOCOL_EXT_INIT_1: End completions func testProtExtInit2() { @@ -1631,10 +1631,10 @@ func testProtExtInit2() { sTy.init(#^PROTOCOL_EXT_INIT_5^# } -// PROTOCOL_EXT_INIT_2: Decl[Constructor]/CurrNominal: ['(']{#x: Int#}[')'][#P4#]{{; name=.+$}} +// PROTOCOL_EXT_INIT_2: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#x: Int#}[')'][#P4#]{{; name=.+$}} // PROTOCOL_EXT_INIT_3: Decl[Constructor]/CurrNominal: init({#x: Int#})[#P4#]{{; name=.+$}} -// PROTOCOL_EXT_INIT_4: Decl[Constructor]/CurrNominal: ({#x: Int#})[#P4#]{{; name=.+$}} -// PROTOCOL_EXT_INIT_5: Decl[Constructor]/CurrNominal: ['(']{#x: Int#}[')'][#P4#]{{; name=.+$}} +// PROTOCOL_EXT_INIT_4: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ({#x: Int#})[#P4#]{{; name=.+$}} +// PROTOCOL_EXT_INIT_5: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#x: Int#}[')'][#P4#]{{; name=.+$}} extension P4 where Self.T == OnlyMe { final func test1() { @@ -1707,7 +1707,7 @@ func testDeDuped(_ x: dedupS) { // PROTOCOL_EXT_DEDUP_1: Begin completions, 5 items // PROTOCOL_EXT_DEDUP_1: Decl[InstanceMethod]/CurrNominal: .foo()[#Int#]; name=foo() // PROTOCOL_EXT_DEDUP_1: Decl[InstanceVar]/CurrNominal: .bar[#Int#]; name=bar -// PROTOCOL_EXT_DEDUP_1: Decl[Subscript]/CurrNominal: [{#(x): Int#}][#Int#]; name=[x: Int] +// PROTOCOL_EXT_DEDUP_1: Decl[Subscript]/CurrNominal: [{#(x): Int#}][#Int#]; name=[:] // PROTOCOL_EXT_DEDUP_1: Decl[InstanceVar]/Super: .bar[#Int#]; name=bar // PROTOCOL_EXT_DEDUP_1: Keyword[self]/CurrNominal: .self[#dedupS#]; name=self // PROTOCOL_EXT_DEDUP_1: End completions @@ -1717,7 +1717,7 @@ func testDeDuped2(_ x: dedupP) { // PROTOCOL_EXT_DEDUP_2: Begin completions, 5 items // PROTOCOL_EXT_DEDUP_2: Decl[InstanceMethod]/CurrNominal: .foo()[#dedupP.T#]; name=foo() // PROTOCOL_EXT_DEDUP_2: Decl[InstanceVar]/CurrNominal: .bar[#dedupP.T#]; name=bar -// PROTOCOL_EXT_DEDUP_2: Decl[Subscript]/CurrNominal: [{#(x): dedupP.T#}][#dedupP.T#]; name=[x: dedupP.T] +// PROTOCOL_EXT_DEDUP_2: Decl[Subscript]/CurrNominal: [{#(x): dedupP.T#}][#dedupP.T#]; name=[:] // PROTOCOL_EXT_DEDUP_2: Keyword[self]/CurrNominal: .self[#dedupP#]; name=self // PROTOCOL_EXT_DEDUP_2: End completions } @@ -1726,7 +1726,7 @@ func testDeDuped3(_ x: T) { // PROTOCOL_EXT_DEDUP_3: Begin completions, 5 items // PROTOCOL_EXT_DEDUP_3: Decl[InstanceMethod]/CurrNominal: .foo()[#Int#]; name=foo() // PROTOCOL_EXT_DEDUP_3: Decl[InstanceVar]/CurrNominal: .bar[#Int#]; name=bar -// PROTOCOL_EXT_DEDUP_3: Decl[Subscript]/CurrNominal: [{#(x): Int#}][#Int#]; name=[x: Int] +// PROTOCOL_EXT_DEDUP_3: Decl[Subscript]/CurrNominal: [{#(x): Int#}][#Int#]; name=[:] // PROTOCOL_EXT_DEDUP_3: Keyword[self]/CurrNominal: .self[#T#]; name=self // PROTOCOL_EXT_DEDUP_3: End completions } @@ -1746,14 +1746,14 @@ func testThrows001() { globalFuncThrows#^THROWS1^# // THROWS1: Begin completions -// THROWS1: Decl[FreeFunction]/CurrModule: ()[' throws'][#Void#]; name=() throws +// THROWS1: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ()[' throws'][#Void#]; name=() // THROWS1: End completions } func testThrows002() { globalFuncRethrows#^THROWS2^# // THROWS2: Begin completions -// THROWS2: Decl[FreeFunction]/CurrModule: ({#(x): () throws -> ()##() throws -> ()#})[' rethrows'][#Void#]; name=(x: () throws -> ()) rethrows +// THROWS2: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ({#(x): () throws -> ()##() throws -> ()#})[' rethrows'][#Void#]; name=(:) // THROWS2: End completions } func testThrows003(_ x: HasThrowingMembers) { @@ -1767,19 +1767,19 @@ func testThrows003(_ x: HasThrowingMembers) { func testThrows004(_ x: HasThrowingMembers) { x.memberThrows#^MEMBER_THROWS2^# // MEMBER_THROWS2: Begin completions -// MEMBER_THROWS2: Decl[InstanceMethod]/CurrNominal: ()[' throws'][#Void#]; name=() throws +// MEMBER_THROWS2: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ()[' throws'][#Void#]; name=() // MEMBER_THROWS2: End completions } func testThrows005(_ x: HasThrowingMembers) { x.memberRethrows#^MEMBER_THROWS3^# // MEMBER_THROWS3: Begin completions -// MEMBER_THROWS3: Decl[InstanceMethod]/CurrNominal: ({#(x): () throws -> ()##() throws -> ()#})[' rethrows'][#Void#]; name=(x: () throws -> ()) rethrows +// MEMBER_THROWS3: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(x): () throws -> ()##() throws -> ()#})[' rethrows'][#Void#]; name=(:) // MEMBER_THROWS3: End completions } func testThrows006() { HasThrowingMembers(#^INIT_THROWS1^# // INIT_THROWS1: Begin completions -// INIT_THROWS1: Decl[Constructor]/CurrNominal: ['(']{#x: () throws -> ()##() throws -> ()#}[')'][' rethrows'][#HasThrowingMembers#] +// INIT_THROWS1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#x: () throws -> ()##() throws -> ()#}[')'][' rethrows'][#HasThrowingMembers#] // INIT_THROWS1: End completions } @@ -1872,7 +1872,7 @@ func testKeyword(cat: Cat) { // KEYWORD_2: Begin completions // KEYWORD_2-DAG: Decl[InstanceVar]/CurrNominal: .prop1[#String#]; name=prop1 // KEYWORD_2-DAG: Decl[InstanceVar]/CurrNominal: .prop2[#String#]; name=prop2 -// KEYWORD_2-DAG: BuiltinOperator/None: = {#Cat.Inner#}[#Void#]; name== Cat.Inner +// KEYWORD_2-DAG: BuiltinOperator/None: = {#Cat.Inner#}[#Void#]; name== // KEYWORD_2: End completions let _ = cat.class.#^KEYWORD_3^# @@ -1986,7 +1986,7 @@ func wrapSuccess(_ onSuccess: @escaping (T) -> Void) -> (T, Bool) -> Void { func testWrapSuccess(promise: Int, seal: Resolver) { wrapSuccess(seal.fulfill)#^COMPLETE_CALL_RESULT^# // COMPLETE_CALL_RESULT: Begin completions - // COMPLETE_CALL_RESULT: Pattern/CurrModule: ({#Void#}, {#Bool#})[#Void#]; name=(Void, Bool) + // COMPLETE_CALL_RESULT: Pattern/CurrModule/Flair[ArgLabels]: ({#Void#}, {#Bool#})[#Void#]; name=() // COMPLETE_CALL_RESULT: End completions } @@ -2034,10 +2034,10 @@ func testProtocolMetatype(protoProto: MetaProto.Protocol, protoType: MetaProto.T // PROTOCOLMETA_3-DAG: Keyword[self]/CurrNominal: self[#MetaProto.Type#]; name=self // PROTOCOLMETA_3-DAG: Decl[StaticMethod]/CurrNominal: staticFunc()[#Int#]; name=staticFunc() // PROTOCOLMETA_3-DAG: Decl[StaticVar]/CurrNominal: staticVar[#Int#]; name=staticVar -// PROTOCOLMETA_3-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc({#(self): MetaProto#})[#() -> Int#]; name=instanceFunc(self: MetaProto) +// PROTOCOLMETA_3-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc({#(self): MetaProto#})[#() -> Int#]; name=instanceFunc(:) // PROTOCOLMETA_3-DAG: Decl[StaticMethod]/CurrNominal: staticFuncExtension()[#Int#]; name=staticFuncExtension() // PROTOCOLMETA_3-DAG: Decl[StaticVar]/CurrNominal: staticVarExtension[#Int#]; name=staticVarExtension -// PROTOCOLMETA_3-DAG: Decl[InstanceMethod]/CurrNominal: instanceFuncExtension({#(self): MetaProto#})[#() -> Int#]; name=instanceFuncExtension(self: MetaProto) +// PROTOCOLMETA_3-DAG: Decl[InstanceMethod]/CurrNominal: instanceFuncExtension({#(self): MetaProto#})[#() -> Int#]; name=instanceFuncExtension(:) // PROTOCOLMETA_3: End completions } diff --git a/test/IDE/complete_vararg.swift b/test/IDE/complete_vararg.swift index 6a1dfb13183df..0332c743601fe 100644 --- a/test/IDE/complete_vararg.swift +++ b/test/IDE/complete_vararg.swift @@ -54,17 +54,17 @@ func testFreeFunc() { freeFunc2(#^FREE_FUNC_2^# } // FREE_FUNC_1: Begin completions, 1 items -// FREE_FUNC_1: Decl[FreeFunction]/CurrModule: ['(']{#x: Int...#}[')'][#Void#]{{; name=.+$}} +// FREE_FUNC_1: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#x: Int...#}[')'][#Void#]{{; name=.+$}} // FREE_FUNC_1: End completions // FREE_FUNC_2: Begin completions, 1 items -// FREE_FUNC_2: Decl[FreeFunction]/CurrModule: ['(']{#x: Int#}, {#y: Int...#}[')'][#Void#]{{; name=.+$}} +// FREE_FUNC_2: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#x: Int#}, {#y: Int...#}[')'][#Void#]{{; name=.+$}} // FREE_FUNC_2: End completions func testInit() { let c =C(#^INIT_1^# } // INIT_1: Begin completions, 1 items -// INIT_1: Decl[Constructor]/CurrNominal: ['(']{#x: Int...#}[')'][#C#]{{; name=.+$}} +// INIT_1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#x: Int...#}[')'][#C#]{{; name=.+$}} // INIT_1: End completions func testMethod() { @@ -72,10 +72,10 @@ func testMethod() { obj.method2(#^METHOD_2^# } // METHOD_1: Begin completions, 1 items -// METHOD_1: Decl[InstanceMethod]/CurrNominal: ['(']{#x: Int...#}[')'][#Void#]{{; name=.+$}} +// METHOD_1: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#x: Int...#}[')'][#Void#]{{; name=.+$}} // METHOD_1: End completions // METHOD_2: Begin completions, 1 items -// METHOD_2: Decl[InstanceMethod]/CurrNominal: ['(']{#x: Int#}, {#y: Int...#}[')'][#Void#]{{; name=.+$}} +// METHOD_2: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#x: Int#}, {#y: Int...#}[')'][#Void#]{{; name=.+$}} // METHOD_2: End completions func testSubscript() { @@ -89,7 +89,7 @@ func testGenericFreeFunc() { genericFreeFunc1(#^GENERIC_FREE_FUNC_1^# } // GENERIC_FREE_FUNC_1: Begin completions, 1 items -// GENERIC_FREE_FUNC_1: Decl[FreeFunction]/CurrModule: ['(']{#t: T...#}[')'][#Void#]{{; name=.+$}} +// GENERIC_FREE_FUNC_1: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#t: T...#}[')'][#Void#]{{; name=.+$}} // GENERIC_FREE_FUNC_1: End completions @@ -97,5 +97,5 @@ func testInterestingType() { interestingType1(#^INTERESTING_TYPE_1^# } // INTERESTING_TYPE_1: Begin completions, 1 items -// INTERESTING_TYPE_1: Decl[FreeFunction]/CurrModule: ['(']{#x: (Int, (Int, String))...#}[')'][#Void#]{{; name=.+$}} +// INTERESTING_TYPE_1: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#x: (Int, (Int, String))...#}[')'][#Void#]{{; name=.+$}} // INTERESTING_TYPE_1: End completions diff --git a/test/IDE/complete_with_closure_param.swift b/test/IDE/complete_with_closure_param.swift index 4e144b9e5ec5f..6642d8cc7cb27 100644 --- a/test/IDE/complete_with_closure_param.swift +++ b/test/IDE/complete_with_closure_param.swift @@ -16,7 +16,7 @@ struct FooStruct { FooStruct().#^COMPLETE^# // CHECK: Begin completions, 8 items -// CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod1({#(callback): (Int, Int) -> Void##(Int, Int) -> Void#})[#Void#]; name=instanceMethod1(callback: (Int, Int) -> Void){{$}} +// CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod1({#(callback): (Int, Int) -> Void##(Int, Int) -> Void#})[#Void#]; name=instanceMethod1(:){{$}} // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod2({#(callback): ((Int, Int) -> Void)?##(Int, Int) -> Void#})[#Void#]{{; name=.+$}} // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod3({#(callback): ((Int, Int) -> Void)??##(Int, Int) -> Void#})[#Void#]{{; name=.+$}} // CHECK-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod4({#(callback): ((Int, Int) -> Void)!##(Int, Int) -> Void#})[#Void#]{{; name=.+$}} diff --git a/test/IDE/complete_with_trailing_closure.swift b/test/IDE/complete_with_trailing_closure.swift new file mode 100644 index 0000000000000..0ab752b7d7ba3 --- /dev/null +++ b/test/IDE/complete_with_trailing_closure.swift @@ -0,0 +1,30 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +public struct ItemWrapper { + let content: Int +} + +struct MyArray { + func map(transform: (ItemWrapper) -> Int) {} +} + +func sink(receiveValue: (MyArray) -> Void) { + fatalError() +} + +func foo() { + sink { items in + let a = items.#^COMPLETE_WITHOUT_SPACE?check=CHECK^#map{ $0.content } + let b = items.#^COMPLETE_WITH_SPACE?check=CHECK^# map{ $0.content } + let c = items.#^COMPLETE_WITH_SPACE_AND_PARENS?check=CHECK^# map({ $0.content }) + let d = items.#^COMPLETE_WITHOUT_SPACE_BUT_PARENS?check=CHECK^#map({ $0.content }) + let e = items.#^COMPLETE_WITHOUT_MAP?check=CHECK^# { $0.content } + let f = items.#^COMPLETE_WITHOUT_MAP_BUT_PARENS?check=CHECK^# ({ $0.content }) + } +} + +// CHECK: Begin completions, 2 items +// CHECK-DAG: Keyword[self]/CurrNominal: self[#MyArray#]; +// CHECK-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: map({#transform: (ItemWrapper) -> Int##(ItemWrapper) -> Int#})[#Void#]; +// CHECK: End completions diff --git a/test/IDE/conforming-methods-generic.swift b/test/IDE/conforming-methods-generic.swift new file mode 100644 index 0000000000000..9dfe909328e3a --- /dev/null +++ b/test/IDE/conforming-methods-generic.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token=CM1 -module-name MyModule -conforming-methods-expected-types '$s8MyModule6TargetPD' | %FileCheck %s -check-prefix=SI +// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token=CM2 -module-name MyModule -conforming-methods-expected-types '$s8MyModule6TargetPD' | %FileCheck %s -check-prefix=SF + +protocol Target {} +struct Concrete : Target {} + +struct S { + func returnsAnything() -> U { fatalError() } +} + +extension S where T == Int { + func returnsConcrete(_ x: U) -> Concrete { fatalError() } +} + +func test(si: S, sf: S) { + si.#^CM1^# + // SI: -----BEGIN CONFORMING METHOD LIST----- + // SI-NEXT: - TypeName: S + // SI-NEXT: - Members: + // SI-NEXT: - Name: returnsConcrete(_:) + // SI-NEXT: TypeName: Concrete + // SI-NEXT: -----END CONFORMING METHOD LIST----- + + sf.#^CM2^# + // SF: -----BEGIN CONFORMING METHOD LIST----- + // SF-NEXT: - TypeName: S + // SF-NEXT: - Members: [] + // SF-NEXT: -----END CONFORMING METHOD LIST----- +} diff --git a/test/IDE/conforming-methods-rdar77259607.swift b/test/IDE/conforming-methods-rdar77259607.swift new file mode 100644 index 0000000000000..8f97136a47ecf --- /dev/null +++ b/test/IDE/conforming-methods-rdar77259607.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token=CC + +protocol MyView {} + +extension MyView { + func foo() -> Content? { + return nil#^CC^# + } +} diff --git a/test/IDE/import_as_member.swift b/test/IDE/import_as_member.swift index 7ebf8b218ea5d..7953a592d10d1 100644 --- a/test/IDE/import_as_member.swift +++ b/test/IDE/import_as_member.swift @@ -9,8 +9,8 @@ // RUN: %target-typecheck-verify-swift -I %S/Inputs/custom-modules -// rdar://77558075 -// UNSUPPORTED: OS=tvos && CPU=x86_64 +// Assertion failed: (I != F.TypeRemap.end() && "Invalid index into type index remap")) +// REQUIRES: rdar70691386 // PRINT: struct Struct1 { // PRINT-NEXT: var x: Double diff --git a/test/IDE/print_ast_named_opaque_return.swift b/test/IDE/print_ast_named_opaque_return.swift new file mode 100644 index 0000000000000..4bfe0e6827fd8 --- /dev/null +++ b/test/IDE/print_ast_named_opaque_return.swift @@ -0,0 +1,14 @@ +func f0() -> () { +} +func f1() -> Int { +} +func f2() -> Int { +} + +// RUN: %target-swift-ide-test -print-ast-typechecked -enable-experimental-named-opaque-types -source-filename %s | %FileCheck %s -check-prefix=CHECK1 +// CHECK1: {{^}}func f0() {{{$}} +// CHECK1: {{^}}}{{$}} +// CHECK1: {{^}}func f1() -> Int {{{$}} +// CHECK1: {{^}}}{{$}} +// CHECK1: {{^}}func f2() -> Int {{{$}} +// CHECK1: {{^}}}{{$}} \ No newline at end of file diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift index 6eaa694f576fb..cc0d50de00f6a 100644 --- a/test/IDE/print_clang_objc_async.swift +++ b/test/IDE/print_clang_objc_async.swift @@ -9,61 +9,61 @@ import _Concurrency // CHECK-LABEL: class SlowServer : NSObject, ServiceProvider { -// CHECK: @completionHandlerAsync("doSomethingSlow(_:)", completionHandlerIndex: 1) +// CHECK: @available(*, renamed: "doSomethingSlow(_:)") // CHECK-NEXT: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func doSomethingSlow(_ operation: String) async -> Int -// CHECK: @completionHandlerAsync("doSomethingDangerous(_:)", completionHandlerIndex: 1) +// CHECK: @available(*, renamed: "doSomethingDangerous(_:)") // CHECK-NEXT: func doSomethingDangerous(_ operation: String, completionHandler handler: ((String?, Error?) -> Void)? = nil) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func doSomethingDangerous(_ operation: String) async throws -> String -// CHECK: @completionHandlerAsync("checkAvailability()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "checkAvailability()") // CHECK-NEXT: func checkAvailability(completionHandler: @escaping (Bool) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func checkAvailability() async -> Bool -// CHECK: @completionHandlerAsync("anotherExample()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "anotherExample()") // CHECK-NEXT: func anotherExample(completionBlock block: @escaping (String) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func anotherExample() async -> String -// CHECK: @completionHandlerAsync("finalExample()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "finalExample()") // CHECK-NEXT: func finalExampleWithReply(to block: @escaping (String) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func finalExample() async -> String -// CHECK: @completionHandlerAsync("replyingOperation(_:)", completionHandlerIndex: 1) +// CHECK: @available(*, renamed: "replyingOperation(_:)") // CHECK-NEXT: func replyingOperation(_ operation: String, replyTo block: @escaping (String) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func replyingOperation(_ operation: String) async -> String -// CHECK: @completionHandlerAsync("findAnswer()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "findAnswer()") // CHECK-NEXT: func findAnswer(completionHandler handler: @escaping (String?, Error?) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func findAnswer() async throws -> String -// CHECK: @completionHandlerAsync("findAnswerFailingly()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "findAnswerFailingly()") // CHECK-NEXT: func findAnswerFailingly(completionHandler handler: @escaping (String?, Error?) -> Void) throws // CHECK-NEXT: @discardableResult // CHECK-NEXT: func findAnswerFailingly() async throws -> String -// CHECK: @completionHandlerAsync("findQAndA()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "findQAndA()") // CHECK-NEXT: func findQAndA(completionHandler handler: @escaping (String?, String?, Error?) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func findQAndA() async throws -> (String?, String) -// CHECK: @completionHandlerAsync("findQuestionableAnswers()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "findQuestionableAnswers()") // CHECK-NEXT: func findQuestionableAnswers(completionHandler handler: @escaping CompletionHandler) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func findQuestionableAnswers() async throws -> (String, String?) -// CHECK: @completionHandlerAsync("doSomethingFun(_:)", completionHandlerIndex: 1) +// CHECK: @available(*, renamed: "doSomethingFun(_:)") // CHECK-NEXT: func doSomethingFun(_ operation: String, then completionHandler: @escaping () -> Void) // CHECK-NEXT: func doSomethingFun(_ operation: String) async -// CHECK: @completionHandlerAsync("doSomethingConflicted(_:)", completionHandlerIndex: 1) +// CHECK: @available(*, renamed: "doSomethingConflicted(_:)") // CHECK-NEXT: func doSomethingConflicted(_ operation: String, completionHandler handler: @escaping (Int) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func doSomethingConflicted(_ operation: String) async -> Int @@ -73,12 +73,12 @@ import _Concurrency // CHECK: func dance(_ step: String) async -> String // CHECK: func __leap(_ height: Int) async -> String -// CHECK: @completionHandlerAsync("runOnMainThread()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "runOnMainThread()") // CHECK-NEXT: func runOnMainThread(completionHandler completion: (@MainActor (String) -> Void)? = nil) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func runOnMainThread() async -> String -// CHECK: @completionHandlerAsync("asyncImportSame(_:)", completionHandlerIndex: 1) +// CHECK: @available(*, renamed: "asyncImportSame(_:)") // CHECK-NEXT: func asyncImportSame(_ operation: String, completionHandler handler: @escaping (Int) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func asyncImportSame(_ operation: String) async -> Int @@ -97,14 +97,14 @@ import _Concurrency // CHECK-NEXT: {{^[}]$}} // CHECK-LABEL: protocol ProtocolWithSwiftAttributes { -// CHECK-NEXT: @actorIndependent func independentMethod() -// CHECK-NEXT: func asyncHandlerMethod() +// CHECK-NEXT: nonisolated func independentMethod() +// CHECK-NEXT: nonisolated func nonisolatedMethod() // CHECK-NEXT: {{^}} @objc @MainActor func mainActorMethod() // CHECK-NEXT: {{^}} @objc @MainActor func uiActorMethod() // CHECK-NEXT: {{^}} @objc optional func missingAtAttributeMethod() // CHECK-NEXT: {{^[}]$}} -// CHECK: {{^}}@actorIndependent(unsafe) var MAGIC_NUMBER: Int32 { get } +// CHECK: {{^}}nonisolated var MAGIC_NUMBER: Int32 { get } // CHECK: func doSomethingConcurrently(_ block: @Sendable () -> Void) diff --git a/test/IDE/print_clang_objc_effectful_properties.swift b/test/IDE/print_clang_objc_effectful_properties.swift index 6d05bc83219f8..4fc2fbb1f3770 100644 --- a/test/IDE/print_clang_objc_effectful_properties.swift +++ b/test/IDE/print_clang_objc_effectful_properties.swift @@ -3,32 +3,39 @@ // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -print-implicit-attrs -source-filename %s -module-to-print=EffectfulProperties -function-definitions=false > %t/EffectfulProperties.printed.txt // RUN: %FileCheck -input-file %t/EffectfulProperties.printed.txt %s +// REQUIRES: concurrency // REQUIRES: objc_interop // CHECK-LABEL: class EffProps : NSObject { -// CHECK: func getDogWithCompletion(_ completionHandler: @escaping (NSObject) -> Void) -// CHECK: var doggo: NSObject { get async } +// CHECK: @available(*, renamed: "getter:doggo()") +// CHECK-NEXT: func getDogWithCompletion(_ completionHandler: @escaping (NSObject) -> Void) +// CHECK: var doggo: NSObject { get async } -// CHECK: func obtainCat(_ completionHandler: @escaping (NSObject?, Error?) -> Void) +// CHECK: @available(*, renamed: "getter:catto()") +// CHECK-NEXT: func obtainCat(_ completionHandler: @escaping (NSObject?, Error?) -> Void) // CHECK-NEXT: var catto: NSObject? { get async throws } -// CHECK: func checkAvailability(completionHandler: @escaping (Bool) -> Void) +// CHECK: @available(*, renamed: "getter:available()") +// CHECK-NEXT: func checkAvailability(completionHandler: @escaping (Bool) -> Void) // CHECK-NEXT: var available: Bool { get async } // CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "checkAvailability(completionHandler:)") // CHECK-NEXT: func checkAvailabilityWithCompletionHandler(_ completionHandler: @escaping (Bool) -> Void) -// CHECK: func returnNothing(completion completionHandler: @escaping () -> Void) +// CHECK: @available(*, renamed: "getter:touch()") +// CHECK-NEXT: func returnNothing(completion completionHandler: @escaping () -> Void) // CHECK-NEXT: var touch: Void { get async } // CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "returnNothing(completion:)") // CHECK-NEXT: func returnNothingWithCompletion(_ completionHandler: @escaping () -> Void) -// CHECK: func nullableHandler(_ completion: ((String) -> Void)? = nil) +// CHECK: @available(*, renamed: "getter:fromNullableHandler()") +// CHECK-NEXT: func nullableHandler(_ completion: ((String) -> Void)? = nil) // CHECK-NEXT: var fromNullableHandler: String { get async } -// CHECK: func getMainDog(_ completion: @escaping (String) -> Void) +// CHECK: @available(*, renamed: "getter:mainDogProp()") +// CHECK-NEXT: func getMainDog(_ completion: @escaping (String) -> Void) // CHECK-NEXT: var mainDogProp: String { get async } -// CHECK: @completionHandlerAsync("regularMainDog()", completionHandlerIndex: 0) +// CHECK: @available(*, renamed: "regularMainDog()") // CHECK-NEXT: func regularMainDog(_ completion: @escaping (String) -> Void) // CHECK-NEXT: @discardableResult // CHECK-NEXT: func regularMainDog() async -> String diff --git a/test/IDE/print_objc_concurrency_interface.swift b/test/IDE/print_objc_concurrency_interface.swift index b2204f015b8b1..70caed315754a 100644 --- a/test/IDE/print_objc_concurrency_interface.swift +++ b/test/IDE/print_objc_concurrency_interface.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -print-interface -source-filename %s -module-to-print=ObjCConcurrency -function-definitions=false -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -print-interface -source-filename %s -module-to-print=ObjCConcurrency -function-definitions=false | %FileCheck %s // REQUIRES: objc_interop // REQUIRES: concurrency @@ -8,7 +8,7 @@ import _Concurrency // CHECK-LABEL: class SlowServer : NSObject, ServiceProvider { -// rdar://76685011: Make sure we don't print @completionHandlerAsync in generated interfaces. -// CHECK-NOT: @completionHandlerAsync +// rdar://76685011: Make sure we don't print implicit @available in generated interfaces. +// CHECK-NOT: @available // CHECK: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void) // CHECK: func doSomethingSlow(_ operation: String) async -> Int diff --git a/test/IDE/print_synthesized_extensions.swift b/test/IDE/print_synthesized_extensions.swift index f1786c2396eed..01585a220a89f 100644 --- a/test/IDE/print_synthesized_extensions.swift +++ b/test/IDE/print_synthesized_extensions.swift @@ -393,7 +393,7 @@ extension C : P8 {} public class F {} extension F : P8 {} -// CHECK16: extension F where T : D { +// CHECK16: public class F<T> where T : print_synthesized_extensions.D { // CHECK16-NEXT: public func bar() // CHECK16-NEXT: } diff --git a/test/IDE/print_synthesized_extensions_superclass.swift b/test/IDE/print_synthesized_extensions_superclass.swift new file mode 100644 index 0000000000000..e83de3f9e8489 --- /dev/null +++ b/test/IDE/print_synthesized_extensions_superclass.swift @@ -0,0 +1,132 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module-path %t/print_synthesized_extensions_superclass.swiftmodule -emit-module-doc -emit-module-doc-path %t/print_synthesized_extensions_superclass.swiftdoc %s +// RUN: %target-swift-ide-test -print-module -synthesize-extension -print-interface -no-empty-line-between-members -module-to-print=print_synthesized_extensions_superclass -I %t -source-filename=%s | %FileCheck %s + +public class Base {} +public class Middle : Base {} +public class Most : Middle {} + +public protocol P { + associatedtype T + associatedtype U +} + +public extension P where T : Base { + func withBase() {} +} + +public extension P where T : Middle { + func withMiddleAbstract() {} +} + +public extension P where T : Middle { + func withMiddleConcrete() {} +} + +public extension P where T : Most { + func withMost() {} +} + +// CHECK-LABEL: public struct S1 : print_synthesized_extensions_superclass.P { +// CHECK-NEXT: public typealias T = print_synthesized_extensions_superclass.Base +// CHECK-NEXT: public typealias U = Int +// CHECK-NEXT: public func withBase() +// CHECk-NEXT: } + +public struct S1 : P { + public typealias T = Base + public typealias U = Int +} + +// CHECK-LABEL: public struct S2 : print_synthesized_extensions_superclass.P { +// CHECK-NEXT: public typealias T = print_synthesized_extensions_superclass.Middle +// CHECK-NEXT: public typealias U = Int +// CHECK-NEXT: public func withBase() +// CHECk-NEXT: public func withMiddleAbstract() +// CHECk-NEXT: public func withMiddleConcrete() +// CHECk-NEXT: } + +public struct S2 : P { + public typealias T = Middle + public typealias U = Int +} + +// CHECK-LABEL: public struct S3 : print_synthesized_extensions_superclass.P { +// CHECK-NEXT: public typealias T = print_synthesized_extensions_superclass.Middle +// CHECK-NEXT: public typealias U = String +// CHECK-NEXT: public func withBase() +// CHECK-NEXT: public func withMiddleAbstract() +// CHECK-NEXT: } + +public struct S3 : P { + public typealias T = Middle + public typealias U = String +} + +// CHECK-LABEL: public struct S4 : print_synthesized_extensions_superclass.P { +// CHECK-NEXT: public typealias T = print_synthesized_extensions_superclass.Most +// CHECK-NEXT: public typealias U = Int +// CHECK-NEXT: public func withBase() +// CHECK-NEXT: public func withMiddleAbstract() +// CHECK-NEXT: public func withMiddleConcrete() +// CHECK-NEXT: public func withMost() +// CHECK-NEXT: } + +public struct S4 : P { + public typealias T = Most + public typealias U = Int +} + +// CHECK-LABEL: public struct S5 : print_synthesized_extensions_superclass.P { +// CHECK-NEXT: public typealias T = print_synthesized_extensions_superclass.Most +// CHECK-NEXT: public typealias U = String +// CHECK-NEXT: public func withBase() +// CHECK-NEXT: public func withMiddleConcrete() +// CHECK-NEXT: public func withMost() +// CHECK-NEXT: } + +public struct S5 : P { + public typealias T = Most + public typealias U = String +} + +// CHECK-LABEL: public struct S6 : print_synthesized_extensions_superclass.P where T : print_synthesized_extensions_superclass.Base { +// CHECK-NEXT: public func withBase() +// CHECK-NEXT: } + +// CHECK-LABEL: extension S6 where T : Middle { +// CHECK-NEXT: public func withMiddleAbstract() +// CHECK-NEXT: } + +// CHECK-LABEL: extension S6 where T : Middle { +// CHECK-NEXT: public func withMiddleConcrete() +// CHECK-NEXT: } + +// CHECK-LABEL: extension S6 where T : Most { +// CHECK-NEXT: public func withMost() +// CHECK-NEXT: } + +public struct S6 : P where T : Base {} + +// CHECK-LABEL: public struct S7 : print_synthesized_extensions_superclass.P where T : print_synthesized_extensions_superclass.Middle { +// CHECK-NEXT: public func withBase() +// CHECK-NEXT: public func withMiddleAbstract() +// CHECK-NEXT: } + +// CHECK-LABEL: extension S7 where T : Middle { +// CHECK-NEXT: public func withMiddleConcrete() +// CHECK-NEXT: } + +// CHECK-LABEL: extension S7 where T : Most { +// CHECK-NEXT: public func withMost() +// CHECK-NEXT: } + +public struct S7 : P where T : Middle {} + +// CHECK-LABEL: public struct S8 : print_synthesized_extensions_superclass.P where T : print_synthesized_extensions_superclass.Most { +// CHECK-NEXT: public func withBase() +// CHECK-NEXT: public func withMiddleConcrete() +// CHECK-NEXT: public func withMost() +// CHECK-NEXT: } + +public struct S8 : P where T : Most {} \ No newline at end of file diff --git a/test/IDE/structure.swift b/test/IDE/structure.swift index f77798697ef0e..293ea300c9fdc 100644 --- a/test/IDE/structure.swift +++ b/test/IDE/structure.swift @@ -221,7 +221,7 @@ class SubscriptTest { // CHECK: return 0 // CHECK: } // CHECK: set(value) { - // CHECK: print(value) + // CHECK: print(value) // CHECK: } } @@ -319,16 +319,16 @@ thirdCall(""" }()) """) """) -// CHECK: thirdCall(""" +// CHECK: thirdCall(""" // CHECK-NEXT: \(""" // CHECK-NEXT: \({ // CHECK-NEXT: return a() // CHECK-NEXT: }()) // CHECK-NEXT: """) -// CHECK-NEXT: """) +// CHECK-NEXT: """) fourthCall(a: @escaping () -> Int) // CHECK: fourthCall(a: @escaping () -> Int) -// CHECK: foo { [unowned self, x] in _ } +// CHECK: foo { [unowned self, x] in _ } foo { [unowned self, x] in _ } diff --git a/test/IRGen/Inputs/ObjectiveC.swift b/test/IRGen/Inputs/ObjectiveC.swift index f455271717cab..6add95fe53564 100644 --- a/test/IRGen/Inputs/ObjectiveC.swift +++ b/test/IRGen/Inputs/ObjectiveC.swift @@ -1,6 +1,7 @@ // This is an overlay Swift module. @_exported import ObjectiveC +@frozen public struct ObjCBool : CustomStringConvertible { #if (os(macOS) && arch(x86_64)) || (os(iOS) && (arch(i386) || arch(arm) || targetEnvironment(macCatalyst))) @@ -34,6 +35,7 @@ public struct ObjCBool : CustomStringConvertible { } } +@frozen public struct Selector { private var ptr : OpaquePointer } diff --git a/test/IRGen/Inputs/opaque_result_type_private_typemetadata2.swift b/test/IRGen/Inputs/opaque_result_type_private_typemetadata2.swift new file mode 100644 index 0000000000000..6edef8ba7b5a4 --- /dev/null +++ b/test/IRGen/Inputs/opaque_result_type_private_typemetadata2.swift @@ -0,0 +1,10 @@ +public protocol P { } + +extension Int : P {} + +struct Container { + // This opaque result type is private to this file and its type metadata is + // not accessible from another TU. Therefore, Container's fields are not ABI + // accessible from another TU. + private let mem : some P = 5 +} diff --git a/test/IRGen/Inputs/usr/include/Gizmo.h b/test/IRGen/Inputs/usr/include/Gizmo.h index d1c04f719c4c1..01522479d4945 100644 --- a/test/IRGen/Inputs/usr/include/Gizmo.h +++ b/test/IRGen/Inputs/usr/include/Gizmo.h @@ -166,3 +166,6 @@ __attribute__((swift_name("OuterType.InnerType"))) @interface ObjcGenericClass<__covariant SectionType> @end + +@interface FungingArray > : NSObject +@end diff --git a/test/IRGen/Inputs/weak_availability_opaque_result_type_helper.swift b/test/IRGen/Inputs/weak_availability_opaque_result_type_helper.swift new file mode 100644 index 0000000000000..bfa17f04f0be7 --- /dev/null +++ b/test/IRGen/Inputs/weak_availability_opaque_result_type_helper.swift @@ -0,0 +1,12 @@ +public protocol P { + func blah() +} + +public struct S : P { + public func blah() {} +} + +extension P { + @available(macOS 100, *) + @_weakLinked public func someAPI() -> some P { return S() } +} diff --git a/test/IRGen/Inputs/weak_import_opaque_result_type_helper.swift b/test/IRGen/Inputs/weak_import_opaque_result_type_helper.swift new file mode 100644 index 0000000000000..e72ffad678df6 --- /dev/null +++ b/test/IRGen/Inputs/weak_import_opaque_result_type_helper.swift @@ -0,0 +1,11 @@ +public protocol P { + func blah() +} + +public struct S : P { + public func blah() {} +} + +extension P { + @_weakLinked public func someAPI() -> some P { return S() } +} diff --git a/test/IRGen/actor_class.swift b/test/IRGen/actor_class.swift index 7296d35497cf8..6c744e3b7652a 100644 --- a/test/IRGen/actor_class.swift +++ b/test/IRGen/actor_class.swift @@ -1,9 +1,9 @@ -// RUN: %target-swift-frontend -emit-ir %s -swift-version 5 -enable-experimental-concurrency | %IRGenFileCheck %s +// RUN: %target-swift-frontend -emit-ir %s -swift-version 5 -disable-availability-checking | %IRGenFileCheck %s // REQUIRES: concurrency // CHECK: %T11actor_class7MyClassC = type <{ %swift.refcounted, %swift.defaultactor, %TSi }> -// CHECK: %swift.defaultactor = type { [10 x i8*] } +// CHECK: %swift.defaultactor = type { [12 x i8*] } // CHECK-objc-LABEL: @"$s11actor_class7MyClassCMm" = global // CHECK-objc-SAME: @"OBJC_METACLASS_$__TtCs12_SwiftObject{{(.ptrauth)?}}" @@ -15,13 +15,13 @@ // Flags: uses Swift refcounting // CHECK-SAME: i32 2, // Instance size -// CHECK-64-SAME: i32 104, -// CHECK-32-SAME: i32 52, + // CHECK-64-SAME: i32 120, +// CHECK-32-SAME: i32 60, // Alignment mask // CHECK-64-SAME: i16 15, // CHECK-32-SAME: i16 7, // Field offset for 'x' -// CHECK-objc-SAME: [[INT]] {{48|96}}, +// CHECK-objc-SAME: [[INT]] {{56|112}}, // Type descriptor. // CHECK-LABEL: @"$s11actor_class9ExchangerCMn" = {{.*}}constant diff --git a/test/IRGen/actor_class_forbid_objc_assoc_objects.swift b/test/IRGen/actor_class_forbid_objc_assoc_objects.swift index e089bc58c9976..c38e0fb92a5f4 100644 --- a/test/IRGen/actor_class_forbid_objc_assoc_objects.swift +++ b/test/IRGen/actor_class_forbid_objc_assoc_objects.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -disable-availability-checking -emit-ir %s | %FileCheck %s // REQUIRES: concurrency // REQUIRES: objc_interop diff --git a/test/IRGen/actor_class_objc.swift b/test/IRGen/actor_class_objc.swift index a0d336ec64f7c..21c2e2c6d9299 100644 --- a/test/IRGen/actor_class_objc.swift +++ b/test/IRGen/actor_class_objc.swift @@ -1,16 +1,14 @@ -// RUN: %target-swift-frontend -emit-ir %s -swift-version 5 -enable-experimental-concurrency | %IRGenFileCheck %s +// RUN: %target-swift-frontend -emit-ir %s -swift-version 5 -disable-availability-checking | %IRGenFileCheck %s // REQUIRES: concurrency // REQUIRES: objc_interop import Foundation // CHECK: %T16actor_class_objc7MyClassC = type <{ %swift.refcounted, %swift.defaultactor, %TSi }> -// CHECK: %swift.defaultactor = type { [10 x i8*] } +// CHECK: %swift.defaultactor = type { [12 x i8*] } // CHECK-LABEL: @"OBJC_METACLASS_$__TtC16actor_class_objc7MyClass" = global // Metaclass is an instance of the root class. -// CHECK-SAME: %objc_class* {{.*}}@"OBJC_METACLASS_$_NSObject{{(.ptrauth)?}}" -// Metaclass superclass is the metaclass of the superclass. // CHECK-SAME: %objc_class* {{.*}}@"OBJC_METACLASS_$_SwiftNativeNSObject{{(.ptrauth)?}}" // CHECK: @"$s16actor_class_objc7MyClassCMf" = internal global @@ -20,18 +18,18 @@ import Foundation // Flags: uses Swift refcounting // CHECK-SAME: i32 2, // Instance size -// CHECK-64-SAME: i32 104, -// CHECK-32-SAME: i32 52, +// CHECK-64-SAME: i32 120, +// CHECK-32-SAME: i32 60, // Alignment mask // CHECK-64-SAME: i16 15, // CHECK-32-SAME: i16 7, // Field offset for 'x' -// CHECK-64-SAME: i64 96, -// CHECK-32-SAME: i32 48, +// CHECK-64-SAME: i64 112, +// CHECK-32-SAME: i32 56, -public actor MyClass: NSObject { +@objc public actor MyClass { public var x: Int - public override init() { self.x = 0 } + public init() { self.x = 0 } } // CHECK-LABEL: define {{.*}} @"$s16actor_class_objc7MyClassC1xSivg" diff --git a/test/IRGen/associated_type_witness.swift b/test/IRGen/associated_type_witness.swift index 2fa5fc429cab5..4b68d306936ea 100644 --- a/test/IRGen/associated_type_witness.swift +++ b/test/IRGen/associated_type_witness.swift @@ -50,8 +50,8 @@ struct WithUniversal : Assocked { // Witness table for GenericWithUniversal : Assocked. // GLOBAL-LABEL: @"$s23associated_type_witness20GenericWithUniversalVyxGAA8AssockedAAWP" = hidden global [4 x i8*] [ // GLOBAL-SAME: @"$s23associated_type_witness20GenericWithUniversalVyxGAA8AssockedAAMc" -// GLOBAL-SAME: @"associated conformance 23associated_type_witness20GenericWithUniversalVyxGAA8AssockedAA5Assoc_AA1P" -// GLOBAL-SAME: @"associated conformance 23associated_type_witness20GenericWithUniversalVyxGAA8AssockedAA5Assoc_AA1Q" +// GLOBAL-SAME: @"associated conformance 23associated_type_witness20GenericWithUniversalVyxGAA8AssockedAA5AssocAaEP_AA1P" +// GLOBAL-SAME: @"associated conformance 23associated_type_witness20GenericWithUniversalVyxGAA8AssockedAA5AssocAaEP_AA1Q" // GLOBAL-SAME: @"symbolic{{.*}}23associated_type_witness9UniversalV" // GLOBAL-SAME: ] struct GenericWithUniversal : Assocked { @@ -88,8 +88,8 @@ struct Pair : P, Q {} // Generic witness table pattern for Computed : Assocked. // GLOBAL-LABEL: @"$s23associated_type_witness8ComputedVyxq_GAA8AssockedAAWp" = internal global [4 x i8*] [ // GLOBAL-SAME: @"$s23associated_type_witness8ComputedVyxq_GAA8AssockedAAMc" -// GLOBAL-SAME: @"associated conformance 23associated_type_witness8ComputedVyxq_GAA8AssockedAA5Assoc_AA1P" -// GLOBAL-SAME: @"associated conformance 23associated_type_witness8ComputedVyxq_GAA8AssockedAA5Assoc_AA1Q" +// GLOBAL-SAME: @"associated conformance 23associated_type_witness8ComputedVyxq_GAA8AssockedAA5AssocAaEP_AA1P" +// GLOBAL-SAME: @"associated conformance 23associated_type_witness8ComputedVyxq_GAA8AssockedAA5AssocAaEP_AA1Q" // GLOBAL-SAME: @"symbolic{{.*}}23associated_type_witness4PairV{{.*}}" // GLOBAL-SAME: ] diff --git a/test/IRGen/assoctypepath.swift b/test/IRGen/assoctypepath.swift index 983f8ee46b0ee..949c738b1e55f 100644 --- a/test/IRGen/assoctypepath.swift +++ b/test/IRGen/assoctypepath.swift @@ -17,7 +17,7 @@ protocol P { protocol Q: P where A: Z {} protocol R: Q where A.ZZ: Y {} -// CHECK: define {{.*}} @"$s13assoctypepath1SVyxGAA1RAA1A_2ZZAA1YPWT" +// CHECK: define {{.*}} @"$s13assoctypepath1SVyxGAA1RAA1AAA1PP_2ZZAA1ZPAA1YPWT" struct S: R where T: Z, T.ZZ: Y { typealias A = T diff --git a/test/IRGen/async.swift b/test/IRGen/async.swift index defb28677b10a..84b6d3c7ae4c8 100644 --- a/test/IRGen/async.swift +++ b/test/IRGen/async.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-experimental-concurrency | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize +// RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-availability-checking | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize // REQUIRES: concurrency @@ -13,15 +13,18 @@ public func h(_: @Sendable (Int) -> Int) { } public class SomeClass {} -@_silgen_name("swift_task_future_wait") -public func task_future_wait(_ task: __owned SomeClass) async throws -> Int +//@_silgen_name("swift_task_future_wait") +//public func task_future_wait(_ task: __owned SomeClass) async throws -> Int + +@_silgen_name("swift_task_future_wait_throwing") +public func _taskFutureGetThrowing(_ task: SomeClass) async throws -> T // CHECK: define{{.*}} swift{{(tail)?}}cc void @"$s5async8testThisyyAA9SomeClassCnYaF"(%swift.context* swiftasync %0{{.*}} -// CHECK-64: call swiftcc i8* @swift_task_alloc(i64 48) -// CHECK: {{(must)?}}tail call swift{{(tail)?}}cc void @swift_task_future_wait( +// CHECK-NOT: @swift_task_alloc +// CHECK: {{(must)?}}tail call swift{{(tail)?}}cc void @swift_task_future_wait_throwing(%swift.opaque* {{.*}}, %swift.context* {{.*}}, %T5async9SomeClassC* {{.*}}, i8* {{.*}}, %swift.context* {{.*}}) public func testThis(_ task: __owned SomeClass) async { do { - let _ = try await task_future_wait(task) + let _ : Int = try await _taskFutureGetThrowing(task) } catch _ { print("error") } diff --git a/test/IRGen/async/Inputs/resilient_actor.swift b/test/IRGen/async/Inputs/resilient_actor.swift deleted file mode 100644 index de0b62a65156e..0000000000000 --- a/test/IRGen/async/Inputs/resilient_actor.swift +++ /dev/null @@ -1,8 +0,0 @@ -open actor ResilientBaseActor { - public init() {} -} - -@_fixed_layout -open actor FixedBaseActor { - public init() {} -} diff --git a/test/IRGen/async/async_calls_verifier.sil b/test/IRGen/async/async_calls_verifier.sil index c266c89b0dd02..2dd88a3bfe292 100644 --- a/test/IRGen/async/async_calls_verifier.sil +++ b/test/IRGen/async/async_calls_verifier.sil @@ -1,4 +1,4 @@ -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -Xllvm -verify-continue-on-failure=true -parse-sil %s -emit-sil -I %t -L %t -o /dev/null 2>&1 | %FileCheck %s +// RUN: %target-build-swift -Xllvm -verify-continue-on-failure=true -parse-sil %s -emit-sil -I %t -L %t -o /dev/null 2>&1 | %FileCheck %s // REQUIRES: asserts // REQUIRES: concurrency diff --git a/test/IRGen/async/builtin_executor.sil b/test/IRGen/async/builtin_executor.sil index 50bb6124bed4f..72d0e4f5daf74 100644 --- a/test/IRGen/async/builtin_executor.sil +++ b/test/IRGen/async/builtin_executor.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -enable-experimental-concurrency -primary-file %s -module-name=test -disable-llvm-optzns -disable-swift-specific-llvm-optzns -emit-ir -sil-verify-all | %IRGenFileCheck %s +// RUN: %target-swift-frontend -primary-file %s -module-name=test -disable-llvm-optzns -disable-swift-specific-llvm-optzns -emit-ir -sil-verify-all | %IRGenFileCheck %s // REQUIRES: concurrency @@ -54,7 +54,7 @@ bb0(%0 : $MyDefaultActor): sil @test_build_custom_actor : $(@guaranteed MyCustomActor) -> Builtin.Executor { bb0(%0 : $MyCustomActor): // CHECK: [[T0:%.*]] = ptrtoint %T4test13MyCustomActorC* %0 to [[INT]] - // CHECK-NEXT: [[T1:%.*]] = call i8** @"$s4test13MyCustomActorCACs14SerialExecutorAAWl"() + // CHECK-NEXT: [[T1:%.*]] = call i8** @"$s4test13MyCustomActorCACScfAAWl"() // CHECK-NEXT: [[T2:%.*]] = ptrtoint i8** [[T1]] to [[INT]] // CHECK: [[ONE:%.*]] = insertvalue { [[INT]], [[INT]] } undef, [[INT]] [[T0]], 0 // CHECK-NEXT: [[TWO:%.*]] = insertvalue { [[INT]], [[INT]] } [[ONE]], [[INT]] [[T2]], 1 diff --git a/test/IRGen/async/builtins.sil b/test/IRGen/async/builtins.sil index 6c18eb2e5513a..df0f15dbd317e 100644 --- a/test/IRGen/async/builtins.sil +++ b/test/IRGen/async/builtins.sil @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-objc-interop -primary-file %s -emit-ir -sil-verify-all | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc,CHECK-%target-ptrsize -// RUN: %target-swift-frontend -enable-experimental-concurrency -disable-objc-interop -primary-file %s -emit-ir -sil-verify-all | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native,CHECK-%target-ptrsize +// RUN: %target-swift-frontend -enable-objc-interop -primary-file %s -emit-ir -sil-verify-all | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc,CHECK-%target-ptrsize +// RUN: %target-swift-frontend -disable-objc-interop -primary-file %s -emit-ir -sil-verify-all | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native,CHECK-%target-ptrsize // REQUIRES: concurrency // XFAIL: CPU=arm64e @@ -32,28 +32,51 @@ bb0(%0 : @guaranteed $Builtin.NativeObject): } // CHECK-LABEL: define hidden swift{{(tail)?}}cc void @launch_future -sil hidden [ossa] @launch_future : $@convention(method) (Int, @guaranteed @async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for , @in_guaranteed T) -> () { -bb0(%0 : $Int, %1: @guaranteed $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for , %3: $*T): - %5 = copy_value %1 : $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for - // CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* returned [[FN_CONTEXT:%2]]) +sil hidden [ossa] @launch_future : $@convention(method) (Int, Optional, @guaranteed @async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for , @in_guaranteed T) -> () { +bb0(%0 : $Int, %1 : $Optional, %2: @guaranteed $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for , %4: $*T): + %5 = copy_value %2 : $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for + // CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* returned [[FN_CONTEXT:%3]]) %9 = metatype $@thick T.Type %10 = init_existential_metatype %9 : $@thick T.Type, $@thick Any.Type - // CHECK: [[NEW_TASK_AND_CONTEXT:%.*]] = call swift{{(tail)?}}cc %swift.async_task_and_context @swift_task_create_future( - %20 = builtin "createAsyncTaskFuture"(%0 : $Int, %10 : $@thick Any.Type, %5 : $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + // CHECK: [[NEW_TASK_AND_CONTEXT:%.*]] = call swift{{(tail)?}}cc %swift.async_task_and_context @swift_task_create( + %20 = builtin "createAsyncTask"(%0 : $Int, %10 : $@thick Any.Type, %5 : $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) destroy_value %20 : $(Builtin.NativeObject, Builtin.RawPointer) %21 = tuple () return %21 : $() } +// CHECK-LABEL: define hidden swift{{(tail)?}}cc void @launch_future_in_group +// CHECK: [[OPTIONS:%.*]] = alloca %swift.task_group_task_option +// CHECK: [[BASE_GEP:%.*]] = getelementptr inbounds %swift.task_group_task_option, %swift.task_group_task_option* [[OPTIONS]], i32 0, i32 0 +// CHECK: [[FLAGS_GEP:%.*]] = getelementptr inbounds %swift.task_option, %swift.task_option* [[BASE_GEP]], i32 0, i32 0 +// CHECK: store [[INT]] 1, [[INT]]* [[FLAGS_GEP]], align 1 +// CHECK: [[PARENT_GEP:%.*]] = getelementptr inbounds %swift.task_option, %swift.task_option* [[BASE_GEP]], i32 0, i32 1 +// CHECK: store [[INT]] 0, [[INT]]* [[PARENT_GEP]], align 1 +// CHECK: [[GROUP_GEP:%.*]] = getelementptr inbounds %swift.task_group_task_option, %swift.task_group_task_option* [[OPTIONS]], i32 0, i32 1 +// CHECK: store i8* %0, i8** [[GROUP_GEP]], align 1 +// CHECK: [[OPTIONS_PTR:%.*]] = ptrtoint %swift.task_group_task_option* [[OPTIONS]] to [[INT]] +// CHECK: call swiftcc %swift.async_task_and_context @swift_task_create([[INT]] %3, [[INT]] [[OPTIONS_PTR]], +sil hidden @launch_future_in_group : $@convention(thin) (Builtin.RawPointer, @guaranteed @async @callee_guaranteed () -> (Int, @error Error), Int) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $@async @callee_guaranteed () -> (Int, @error Error), %3: $Int): + %6 = metatype $@thick Int.Type + %7 = init_existential_metatype %6 : $@thick Int.Type, $@thick Any.Type + strong_retain %1 : $@async @callee_guaranteed () -> (Int, @error Error) + %9 = builtin "createAsyncTaskInGroup"(%3 : $Int, %0 : $Builtin.RawPointer, %7 : $@thick Any.Type, %1 : $@async @callee_guaranteed () -> (Int, @error Error)) : $(Builtin.NativeObject, Builtin.RawPointer) + %10 = tuple_extract %9 : $(Builtin.NativeObject, Builtin.RawPointer), 0 + strong_release %10 : $Builtin.NativeObject + %12 = tuple () + return %12 : $() +} + // CHECK-LABEL: define hidden swift{{(tail)?}}cc void @launch_void_future -sil hidden [ossa] @launch_void_future : $@convention(method) (Int, @guaranteed @async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>) -> () { -bb0(%0 : $Int, %1: @guaranteed $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>): - %5 = copy_value %1 : $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()> - // CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* returned [[FN_CONTEXT:%2]]) +sil hidden [ossa] @launch_void_future : $@convention(method) (Int, Optional, @guaranteed @async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>) -> () { +bb0(%0 : $Int, %1 : $Optional, %2: @guaranteed $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>): + %5 = copy_value %2 : $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()> + // CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* returned [[FN_CONTEXT:%3]]) %8 = metatype $@thick ().Type // user: %9 %9 = init_existential_metatype %8 : $@thick ().Type, $@thick Any.Type // user: %10 - // CHECK: [[NEW_TASK_AND_CONTEXT:%.*]] = call swift{{(tail)?}}cc %swift.async_task_and_context @swift_task_create_future( - %20 = builtin "createAsyncTaskFuture"<()>(%0 : $Int, %9 : $@thick Any.Type, %5 : $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>) : $(Builtin.NativeObject, Builtin.RawPointer) + // CHECK: [[NEW_TASK_AND_CONTEXT:%.*]] = call swift{{(tail)?}}cc %swift.async_task_and_context @swift_task_create( + %20 = builtin "createAsyncTask"<()>(%0 : $Int, %9 : $@thick Any.Type, %5 : $@async @callee_owned @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>) : $(Builtin.NativeObject, Builtin.RawPointer) destroy_value %20 : $(Builtin.NativeObject, Builtin.RawPointer) %21 = tuple () return %21 : $() @@ -63,7 +86,7 @@ bb0(%0 : $Int, %1: @guaranteed $@async @callee_owned @substituted <τ_0_0> () -> sil hidden [ossa] @resume_nonthrowing_continuation : $(@in Builtin.NativeObject, Builtin.RawUnsafeContinuation) -> () { bb0(%0 : $*Builtin.NativeObject, %1 : $Builtin.RawUnsafeContinuation): // CHECK: [[CONT:%.*]] = bitcast i8* %1 to %swift.task* - // CHECK-NEXT: [[CONTEXT_SLOT:%.*]] = getelementptr inbounds %swift.task, %swift.task* [[CONT]], i32 0, i32 5 + // CHECK-NEXT: [[CONTEXT_SLOT:%.*]] = getelementptr inbounds %swift.task, %swift.task* [[CONT]], i32 0, i32 8 // CHECK-NEXT: [[CONTEXT_OPAQUE:%.*]] = load %swift.context*, %swift.context** [[CONTEXT_SLOT]], align // CHECK-NEXT: [[CONTEXT:%.*]] = bitcast %swift.context* [[CONTEXT_OPAQUE]] to %swift.continuation_context* // CHECK-NEXT: [[RESULT_ADDR_SLOT:%.*]] = getelementptr inbounds %swift.continuation_context, %swift.continuation_context* [[CONTEXT]], i32 0, i32 3 @@ -82,7 +105,7 @@ bb0(%0 : $*Builtin.NativeObject, %1 : $Builtin.RawUnsafeContinuation): sil hidden [ossa] @resume_throwing_continuation : $(@in Builtin.NativeObject, Builtin.RawUnsafeContinuation) -> () { bb0(%0 : $*Builtin.NativeObject, %1 : $Builtin.RawUnsafeContinuation): // CHECK: [[CONT:%.*]] = bitcast i8* %1 to %swift.task* - // CHECK-NEXT: [[CONTEXT_SLOT:%.*]] = getelementptr inbounds %swift.task, %swift.task* [[CONT]], i32 0, i32 5 + // CHECK-NEXT: [[CONTEXT_SLOT:%.*]] = getelementptr inbounds %swift.task, %swift.task* [[CONT]], i32 0, i32 8 // CHECK-NEXT: [[CONTEXT_OPAQUE:%.*]] = load %swift.context*, %swift.context** [[CONTEXT_SLOT]], align // CHECK-NEXT: [[CONTEXT:%.*]] = bitcast %swift.context* [[CONTEXT_OPAQUE]] to %swift.continuation_context* // CHECK-NEXT: [[RESULT_ADDR_SLOT:%.*]] = getelementptr inbounds %swift.continuation_context, %swift.continuation_context* [[CONTEXT]], i32 0, i32 3 @@ -114,12 +137,13 @@ bb0: // CHECK: [[TASKGROUP:%.*]] = alloca [32 x i8*], align 16 // CHECK: [[T0:%.*]] = bitcast [32 x i8*]* [[TASKGROUP]] to i8* // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[T0]]) - // CHECK-NEXT: call swiftcc void @swift_taskGroup_initialize(i8* [[T0]]) - %0 = builtin "createTaskGroup"() : $Builtin.RawPointer + // CHECK-NEXT: call swiftcc void @swift_taskGroup_initialize(i8* [[T0]], %swift.type* {{.*}}) + %0 = metatype $@thin Builtin.Int32.Type + %1 = builtin "createTaskGroup"(%0: $@thin Builtin.Int32.Type) : $Builtin.RawPointer // CHECK-NEXT: call swiftcc void @swift_taskGroup_destroy(i8* [[T0]]) // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[T0]]) - builtin "destroyTaskGroup"(%0 : $Builtin.RawPointer) : $() + builtin "destroyTaskGroup"(%1 : $Builtin.RawPointer) : $() %21 = tuple () return %21 : $() diff --git a/test/IRGen/async/class_resilience.swift b/test/IRGen/async/class_resilience.swift index 8cf4d668400ff..3a4c65646e13a 100644 --- a/test/IRGen/async/class_resilience.swift +++ b/test/IRGen/async/class_resilience.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-experimental-concurrency -enable-library-evolution -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class %S/Inputs/resilient_class.swift -// RUN: %target-swift-frontend -I %t -emit-ir -enable-experimental-concurrency -enable-library-evolution %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s +// RUN: %target-swift-frontend -emit-module -disable-availability-checking -enable-library-evolution -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class %S/Inputs/resilient_class.swift +// RUN: %target-swift-frontend -I %t -emit-ir -disable-availability-checking -enable-library-evolution %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s // REQUIRES: concurrency import resilient_class diff --git a/test/IRGen/async/debug.swift b/test/IRGen/async/debug.swift index 41be572af3dd4..5c470d942deda 100644 --- a/test/IRGen/async/debug.swift +++ b/test/IRGen/async/debug.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-experimental-concurrency -g | %FileCheck %s +// RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-availability-checking -g | %FileCheck %s // REQUIRES: concurrency // Don't assert on dynamically sized variables. diff --git a/test/IRGen/async/default_actor.swift b/test/IRGen/async/default_actor.swift index 4e960b31e6be1..a886a41de4e1c 100644 --- a/test/IRGen/async/default_actor.swift +++ b/test/IRGen/async/default_actor.swift @@ -1,6 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-experimental-concurrency -enable-library-evolution -emit-module-path=%t/resilient_actor.swiftmodule -module-name=resilient_actor %S/Inputs/resilient_actor.swift -// RUN: %target-swift-frontend -I %t -emit-ir -enable-experimental-concurrency -enable-library-evolution %s | %IRGenFileCheck %s +// RUN: %target-swift-frontend -I %t -emit-ir -disable-availability-checking -enable-library-evolution %s | %IRGenFileCheck %s // REQUIRES: concurrency // CHECK: @"$s13default_actor1ACMn" = hidden constant @@ -8,8 +7,6 @@ // 0x81810050: the same, but using a singleton metadata initialization // CHECK-SAME: i32 {{-2122317744|-2122252208}}, -import resilient_actor - // CHECK-LABEL: define hidden swiftcc void @"$s13default_actor1ACfD"(%T13default_actor1AC* swiftself %0) // CHECK-NOT: ret void // CHECK: call swiftcc void @swift_defaultActor_deallocate( diff --git a/test/IRGen/async/get_async_continuation.sil b/test/IRGen/async/get_async_continuation.sil index 94587625f9ee0..11794c0dfe125 100644 --- a/test/IRGen/async/get_async_continuation.sil +++ b/test/IRGen/async/get_async_continuation.sil @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-objc-interop -primary-file %s -emit-ir -sil-verify-all -disable-llvm-optzns -disable-swift-specific-llvm-optzns | %IRGenFileCheck %s -// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-objc-interop -primary-file %s -emit-ir -sil-verify-all +// RUN: %target-swift-frontend -enable-objc-interop -primary-file %s -emit-ir -sil-verify-all -disable-llvm-optzns -disable-swift-specific-llvm-optzns | %IRGenFileCheck %s +// RUN: %target-swift-frontend -enable-objc-interop -primary-file %s -emit-ir -sil-verify-all // REQUIRES: concurrency @@ -58,22 +58,7 @@ bb0: // CHECK: call swiftcc void @not_async_test() // Arrive at the await_async_continuation point. -// CHECK: [[synchronization_addr_before_await:%.*]] = getelementptr inbounds %swift.continuation_context, %swift.continuation_context* [[cont_context]], i32 0, i32 1 -// CHECK: [[first_at_sync_pt:%.*]] = cmpxchg [[INT]]* [[synchronization_addr_before_await]], {{(i64|i32)}} 0, {{(i64|i32)}} 1 release acquire -// CHECK: [[first_at_sync_pt_bool:%.*]] = extractvalue { {{(i64|i32)}}, i1 } [[first_at_sync_pt]], 1 -// CHECK: br i1 [[first_at_sync_pt_bool]], label %await.async.abort, label %await.async.resume - -// Abort if we are the first to arrive at the await/or continuation point -- -// we must wait on the other to arrive. -// CHECK: await.async.abort: -// CHECK: br label %coro.end - -// CHECK: coro.end: -// CHECK: call i1 (i8*, i1, ...) @llvm.coro.end.async( -// CHECK: unreachable - -// CHECK: await.async.resume: -// CHECK: call { i8* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async{{.*}}({{.*}} @__swift_async_resume_project_context +// CHECK: [[suspend:%.*]] = call { i8* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8s(i32 0, i8* [[resume_intrinsic]], i8* bitcast (i8* (i8*)* @__swift_async_resume_project_context to i8*), i8* bitcast (void (%swift.continuation_context*)* @__swift_continuation_await_point to i8*), %swift.continuation_context* [[cont_context]]) // CHECK: [[result_addr_addr:%.*]] = getelementptr inbounds %swift.continuation_context, %swift.continuation_context* [[cont_context]], i32 0, i32 3 // CHECK: [[result_addr:%.*]] = load %swift.opaque*, %swift.opaque** [[result_addr_addr]] // CHECK: [[typed_result_addr:%.*]] = bitcast %swift.opaque* [[result_addr]] to i32* @@ -81,7 +66,16 @@ bb0: // CHECK: br label %[[result_bb:[0-9]+]] // CHECK: [[result_bb]]: -// CHECK: phi i32 [ [[result_value]], %await.async.resume ] +// CHECK: phi i32 [ [[result_value]], %entry ] + +// CHECK: define {{.*}} void @__swift_continuation_await_point(%swift.continuation_context* %0) +// CHECK: {{musttail call swifttailcc|tail call swiftcc}} void @swift_continuation_await(%swift.continuation_context* %0) +// CHECK-NEXT: ret void + +// CHECK: define {{.*}} void @__swift_suspend_dispatch_1(i8* %0, %swift.context* %1) +// CHECK-NOT: define +// CHECK: tail call swift{{(tail)?}}cc void %{{.*}}(%swift.context* swiftasync %1) +// CHECK-NEXT: ret void sil @async_continuation : $@async () -> () { entry: diff --git a/test/IRGen/async/hop_to_executor.sil b/test/IRGen/async/hop_to_executor.sil index 29f20ac13cab5..f0d2adf4f8170 100644 --- a/test/IRGen/async/hop_to_executor.sil +++ b/test/IRGen/async/hop_to_executor.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -enable-experimental-concurrency -primary-file %s -module-name=test -disable-llvm-optzns -disable-swift-specific-llvm-optzns -emit-ir -sil-verify-all | %IRGenFileCheck %s +// RUN: %target-swift-frontend -primary-file %s -module-name=test -disable-llvm-optzns -disable-swift-specific-llvm-optzns -emit-ir -sil-verify-all | %IRGenFileCheck %s // REQUIRES: concurrency diff --git a/test/IRGen/async/partial_apply_forwarder.sil b/test/IRGen/async/partial_apply_forwarder.sil index ecc7e185c5b75..fe491a4356b35 100644 --- a/test/IRGen/async/partial_apply_forwarder.sil +++ b/test/IRGen/async/partial_apply_forwarder.sil @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -enable-experimental-concurrency -g -enable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc -// RUN: %target-swift-frontend -enable-experimental-concurrency -g -disable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native +// RUN: %target-swift-frontend -g -enable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc +// RUN: %target-swift-frontend -g -disable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native // REQUIRES: concurrency diff --git a/test/IRGen/async/protocol_resilience.swift b/test/IRGen/async/protocol_resilience.swift index c0e51b49e18e8..95fd7f36d3d4d 100644 --- a/test/IRGen/async/protocol_resilience.swift +++ b/test/IRGen/async/protocol_resilience.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-experimental-concurrency -g -enable-library-evolution -emit-module-path=%t/resilient_protocol.swiftmodule -module-name=resilient_protocol %S/Inputs/resilient_protocol.swift -// RUN: %target-swift-frontend -I %t -emit-ir -enable-experimental-concurrency -g -enable-library-evolution %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s +// RUN: %target-swift-frontend -emit-module -disable-availability-checking -g -enable-library-evolution -emit-module-path=%t/resilient_protocol.swiftmodule -module-name=resilient_protocol %S/Inputs/resilient_protocol.swift +// RUN: %target-swift-frontend -I %t -emit-ir -disable-availability-checking -g -enable-library-evolution %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s // REQUIRES: concurrency import resilient_protocol diff --git a/test/IRGen/async/run-call-class-witnessmethod-void-to-void.swift b/test/IRGen/async/run-call-class-witnessmethod-void-to-void.swift index 4c3e66ed20c98..ae04d4c648c8f 100644 --- a/test/IRGen/async/run-call-class-witnessmethod-void-to-void.swift +++ b/test/IRGen/async/run-call-class-witnessmethod-void-to-void.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -emit-ir -module-name main | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -o %t/main +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -emit-ir -module-name main | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/IRGen/async/run-call-classinstance-int64-to-void.sil b/test/IRGen/async/run-call-classinstance-int64-to-void.sil index 125ac6139d9fb..c44a948442625 100644 --- a/test/IRGen/async/run-call-classinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-int64-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-classinstance-void-to-void.sil b/test/IRGen/async/run-call-classinstance-void-to-void.sil index 8c9112edb20c0..fcef25112fc6a 100644 --- a/test/IRGen/async/run-call-classinstance-void-to-void.sil +++ b/test/IRGen/async/run-call-classinstance-void-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-dynamic-void_to_void.swift b/test/IRGen/async/run-call-dynamic-void_to_void.swift index 70a20b83af6e6..67edfbc8590fc 100644 --- a/test/IRGen/async/run-call-dynamic-void_to_void.swift +++ b/test/IRGen/async/run-call-dynamic-void_to_void.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -emit-ir -parse-as-library -module-name main | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -o %t/main %target-rpath(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -emit-ir -parse-as-library -module-name main | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -o %t/main %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/IRGen/async/run-call-existential-to-void.sil b/test/IRGen/async/run-call-existential-to-void.sil index 6710fc6f12667..e88b448a49e9b 100644 --- a/test/IRGen/async/run-call-existential-to-void.sil +++ b/test/IRGen/async/run-call-existential-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-generic-to-generic.sil b/test/IRGen/async/run-call-generic-to-generic.sil index 24e713583fd95..6acfa2c464d35 100644 --- a/test/IRGen/async/run-call-generic-to-generic.sil +++ b/test/IRGen/async/run-call-generic-to-generic.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-generic-to-void.swift b/test/IRGen/async/run-call-generic-to-void.swift index 26327cea000ee..29cdf017d2e59 100644 --- a/test/IRGen/async/run-call-generic-to-void.swift +++ b/test/IRGen/async/run-call-generic-to-void.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -emit-ir | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -o %t/main +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -emit-ir | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil index d6961b155ae14..8ceaa48303fd4 100644 --- a/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil +++ b/test/IRGen/async/run-call-genericEquatable-x2-to-bool.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-int64-and-int64-to-void.sil b/test/IRGen/async/run-call-int64-and-int64-to-void.sil index 4250a80792b8a..06fd3f9d0e86f 100644 --- a/test/IRGen/async/run-call-int64-and-int64-to-void.sil +++ b/test/IRGen/async/run-call-int64-and-int64-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-int64-to-void.sil b/test/IRGen/async/run-call-int64-to-void.sil index cf20c9c8c1d81..61e732a49b11f 100644 --- a/test/IRGen/async/run-call-int64-to-void.sil +++ b/test/IRGen/async/run-call-int64-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift b/test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift index 131eaa269b648..65e3935a8e639 100644 --- a/test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift +++ b/test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift-dylib(%t/%target-library-name(NonresilientClass)) %S/Inputs/class_open-1instance-void_to_void.swift -Xfrontend -enable-experimental-concurrency -g -module-name NonresilientClass -emit-module -emit-module-path %t/NonresilientClass.swiftmodule +// RUN: %target-build-swift-dylib(%t/%target-library-name(NonresilientClass)) %S/Inputs/class_open-1instance-void_to_void.swift -Xfrontend -disable-availability-checking -g -module-name NonresilientClass -emit-module -emit-module-path %t/NonresilientClass.swiftmodule // RUN: %target-codesign %t/%target-library-name(NonresilientClass) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %S/Inputs/class_open-1instance-void_to_void.swift -emit-ir -I %t -L %t -lNonresilientClass -parse-as-library -module-name main | %FileCheck %S/Inputs/class_open-1instance-void_to_void.swift --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -o %t/main -I %t -L %t -lNonresilientClass %target-rpath(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %S/Inputs/class_open-1instance-void_to_void.swift -emit-ir -I %t -L %t -lNonresilientClass -parse-as-library -module-name main | %FileCheck %S/Inputs/class_open-1instance-void_to_void.swift --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -o %t/main -I %t -L %t -lNonresilientClass %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(NonresilientClass) | %FileCheck %s diff --git a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil index ab6214205af92..20d69ad3e4745 100644 --- a/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call-protocolextension_instance-void-to-int64.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil index 2c93799ca1eff..02b80a99f525e 100644 --- a/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call-protocolwitness_instance-void-to-int64.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-resilient-classinstance-void-to-void.sil b/test/IRGen/async/run-call-resilient-classinstance-void-to-void.sil index 0acd358104625..b348451f9a429 100644 --- a/test/IRGen/async/run-call-resilient-classinstance-void-to-void.sil +++ b/test/IRGen/async/run-call-resilient-classinstance-void-to-void.sil @@ -1,10 +1,10 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift-dylib(%t/%target-library-name(ResilientClass)) %S/Inputs/class-1instance-void_to_void.swift -Xfrontend -enable-experimental-concurrency -g -module-name ResilientClass -emit-module -emit-module-path %t/ResilientClass.swiftmodule +// RUN: %target-build-swift-dylib(%t/%target-library-name(ResilientClass)) %S/Inputs/class-1instance-void_to_void.swift -Xfrontend -disable-availability-checking -g -module-name ResilientClass -emit-module -emit-module-path %t/ResilientClass.swiftmodule // RUN: %target-codesign %t/%target-library-name(ResilientClass) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim -lResilientClass | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims -lResilientClass %target-rpath(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim -lResilientClass | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims -lResilientClass %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) %t/%target-library-name(ResilientClass) | %FileCheck %s diff --git a/test/IRGen/async/run-call-resilient-protocolinstance-void-to-void.swift b/test/IRGen/async/run-call-resilient-protocolinstance-void-to-void.swift index 8194c8bc2b60d..75fd612151d10 100644 --- a/test/IRGen/async/run-call-resilient-protocolinstance-void-to-void.swift +++ b/test/IRGen/async/run-call-resilient-protocolinstance-void-to-void.swift @@ -1,10 +1,10 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift-dylib(%t/%target-library-name(ResilientProtocol)) %S/Inputs/protocol-1instance-void_to_void.swift -Xfrontend -enable-experimental-concurrency -g -module-name ResilientProtocol -emit-module -emit-module-path %t/ResilientProtocol.swiftmodule +// RUN: %target-build-swift-dylib(%t/%target-library-name(ResilientProtocol)) %S/Inputs/protocol-1instance-void_to_void.swift -Xfrontend -disable-availability-checking -g -module-name ResilientProtocol -emit-module -emit-module-path %t/ResilientProtocol.swiftmodule // RUN: %target-codesign %t/%target-library-name(ResilientProtocol) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -emit-ir -I %t -L %t -lPrintShim -lResilientProtocol -parse-as-library -module-name main | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -o %t/main -I %t -L %t -lPrintShims -lResilientProtocol %target-rpath(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -emit-ir -I %t -L %t -lPrintShim -lResilientProtocol -parse-as-library -module-name main | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -o %t/main -I %t -L %t -lPrintShims -lResilientProtocol %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) %t/%target-library-name(ResilientProtocol) | %FileCheck %s diff --git a/test/IRGen/async/run-call-struct-instance_generic-mutating-generic_1-to-generic_1.swift b/test/IRGen/async/run-call-struct-instance_generic-mutating-generic_1-to-generic_1.swift index 5e6c41e5d3b9b..cfec768804072 100644 --- a/test/IRGen/async/run-call-struct-instance_generic-mutating-generic_1-to-generic_1.swift +++ b/test/IRGen/async/run-call-struct-instance_generic-mutating-generic_1-to-generic_1.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -emit-ir -parse-as-library -module-name main | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -o %t/main %target-rpath(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -emit-ir -parse-as-library -module-name main | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -o %t/main %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/IRGen/async/run-call-struct_five_bools-to-void.sil b/test/IRGen/async/run-call-struct_five_bools-to-void.sil index 2703265760304..39e41b4c07730 100644 --- a/test/IRGen/async/run-call-struct_five_bools-to-void.sil +++ b/test/IRGen/async/run-call-struct_five_bools-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-structinstance-int64-to-void.sil b/test/IRGen/async/run-call-structinstance-int64-to-void.sil index 816cb7fcbf979..f662f9182fa1a 100644 --- a/test/IRGen/async/run-call-structinstance-int64-to-void.sil +++ b/test/IRGen/async/run-call-structinstance-int64-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil index 9a8727be7f8b3..bc23df8d1f1f5 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil index bb1e4cc950d7b..86e1eadc8d917 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-nothrow_call-sync-throw.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil index bb77126150f7a..132e647d95a3e 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-async-throw.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil index 70f73da77ab00..65d3edf47f3e2 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-nothrow_call-async-throw.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil index bc49f2e42e7d5..886a6a6f824c1 100644 --- a/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil +++ b/test/IRGen/async/run-call-void-throws-to-int-throwing_call-sync-throw.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-to-existential.sil b/test/IRGen/async/run-call-void-to-existential.sil index 545c03b5a3373..ae1ddacf82bd2 100644 --- a/test/IRGen/async/run-call-void-to-existential.sil +++ b/test/IRGen/async/run-call-void-to-existential.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-to-int64-and-int64.sil b/test/IRGen/async/run-call-void-to-int64-and-int64.sil index d8a834ba00173..8200aea30e5c2 100644 --- a/test/IRGen/async/run-call-void-to-int64-and-int64.sil +++ b/test/IRGen/async/run-call-void-to-int64-and-int64.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-to-int64.swift b/test/IRGen/async/run-call-void-to-int64.swift index 6852e410ed801..87e6d89100553 100644 --- a/test/IRGen/async/run-call-void-to-int64.swift +++ b/test/IRGen/async/run-call-void-to-int64.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -emit-ir | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -o %t/main +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -emit-ir | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/IRGen/async/run-call-void-to-struct_large.sil b/test/IRGen/async/run-call-void-to-struct_large.sil index 52234b480fb1a..5eca1078483da 100644 --- a/test/IRGen/async/run-call-void-to-struct_large.sil +++ b/test/IRGen/async/run-call-void-to-struct_large.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil index b26bf0691fcb4..e93cad19c7ca9 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-generic-to-int64-and-generic.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil index fb07920f09dfc..2a4ddab6631de 100644 --- a/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil +++ b/test/IRGen/async/run-call_generic-protocolwitness_instance-void-to-int64.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-convertfunction-int64-to-void.sil b/test/IRGen/async/run-convertfunction-int64-to-void.sil index 682c26afee730..f3540c6e74aef 100644 --- a/test/IRGen/async/run-convertfunction-int64-to-void.sil +++ b/test/IRGen/async/run-convertfunction-int64-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-class-to-void.sil b/test/IRGen/async/run-partialapply-capture-class-to-void.sil index 69c8d357e5bfb..da18c59b3cd41 100644 --- a/test/IRGen/async/run-partialapply-capture-class-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-class-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-classinstance-to-void.sil b/test/IRGen/async/run-partialapply-capture-classinstance-to-void.sil index bd4a1ea9aedb2..679bfcdf9bbd4 100644 --- a/test/IRGen/async/run-partialapply-capture-classinstance-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-classinstance-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil b/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil index af570ada8e0b3..204d42f972f37 100644 --- a/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-generic_conformer-and-generic-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil index 45679204f84a7..1327a2a05e9b1 100644 --- a/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil +++ b/test/IRGen/async/run-partialapply-capture-inout-generic-and-in-generic-to-generic.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-int64-int64-throws-to-int64.sil b/test/IRGen/async/run-partialapply-capture-int64-int64-throws-to-int64.sil index 42862f2ed4ce6..9ea005c6cd306 100644 --- a/test/IRGen/async/run-partialapply-capture-int64-int64-throws-to-int64.sil +++ b/test/IRGen/async/run-partialapply-capture-int64-int64-throws-to-int64.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil index c890bdf26bdd3..04511668b55f7 100644 --- a/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil +++ b/test/IRGen/async/run-partialapply-capture-int64-int64-to-int64.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil b/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil index 1b5c9b40b94e6..cb1e4a42d30cd 100644 --- a/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil +++ b/test/IRGen/async/run-partialapply-capture-int64-to-generic.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil b/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil index fa921fbf4adf7..b97c3dc260493 100644 --- a/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil +++ b/test/IRGen/async/run-partialapply-capture-struct_classinstance_classinstance-and-int64-to-int64.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil b/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil index d23b577af1b1a..e9f0daa734f91 100644 --- a/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil +++ b/test/IRGen/async/run-partialapply-capture-structgeneric_classinstance_to_struct_and_error.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil b/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil index a56048302dd66..9b40517869ec4 100644 --- a/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-structgeneric_polymorphic_constrained-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil b/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil index 99a547f4e1dc6..cb53e5d708b9a 100644 --- a/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-type_structgeneric_polymorphic_constrained-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil b/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil index 48e4d9f94bba7..e6eeb30f25b80 100644 --- a/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil +++ b/test/IRGen/async/run-partialapply-capture-type_thin-and-classinstance-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/run-structinstance_generic-void-to-void-constrained.swift b/test/IRGen/async/run-structinstance_generic-void-to-void-constrained.swift index cee2bfaf92276..208650224cb76 100644 --- a/test/IRGen/async/run-structinstance_generic-void-to-void-constrained.swift +++ b/test/IRGen/async/run-structinstance_generic-void-to-void-constrained.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -emit-ir -parse-as-library -module-name main | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g %s -parse-as-library -module-name main -o %t/main %target-rpath(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -emit-ir -parse-as-library -module-name main | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -g %s -parse-as-library -module-name main -o %t/main %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/IRGen/async/run-switch-executor.swift b/test/IRGen/async/run-switch-executor.swift index a1508334deac1..d0c0424a9425e 100644 --- a/test/IRGen/async/run-switch-executor.swift +++ b/test/IRGen/async/run-switch-executor.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -g -parse-as-library -module-name main -o %t/main +// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s -g -parse-as-library -module-name main -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/IRGen/async/run-thintothick-int64-to-void.sil b/test/IRGen/async/run-thintothick-int64-to-void.sil index 983d2d906095a..ee94a585ca1c0 100644 --- a/test/IRGen/async/run-thintothick-int64-to-void.sil +++ b/test/IRGen/async/run-thintothick-int64-to-void.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule // RUN: %target-codesign %t/%target-library-name(PrintShims) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) +// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL +// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t) // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s diff --git a/test/IRGen/async/throwing.swift b/test/IRGen/async/throwing.swift index 9183a050f1f64..9a4c7e41c77f1 100644 --- a/test/IRGen/async/throwing.swift +++ b/test/IRGen/async/throwing.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -parse-as-library -Xfrontend -enable-experimental-concurrency -g %s -module-name main -o %t/main +// RUN: %target-build-swift -parse-as-library -Xfrontend -disable-availability-checking -g %s -module-name main -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/IRGen/async/unreachable.swift b/test/IRGen/async/unreachable.swift index 463338077d24f..78509adbf7a88 100644 --- a/test/IRGen/async/unreachable.swift +++ b/test/IRGen/async/unreachable.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -primary-file %s -g -emit-ir -enable-experimental-concurrency -disable-llvm-optzns -disable-swift-specific-llvm-optzns | %FileCheck %s +// RUN: %target-swift-frontend -primary-file %s -g -emit-ir -disable-availability-checking -disable-llvm-optzns -disable-swift-specific-llvm-optzns | %FileCheck %s // REQUIRES: concurrency // CHECK: call i1 (i8*, i1, ...) @llvm.coro.end.async diff --git a/test/IRGen/async/weak_availability.swift b/test/IRGen/async/weak_availability.swift new file mode 100644 index 0000000000000..5fa99561e04c6 --- /dev/null +++ b/test/IRGen/async/weak_availability.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -enable-implicit-dynamic -target %target-cpu-apple-macosx10.14 -Onone -emit-ir %s | %FileCheck --check-prefix=MAYBE-AVAILABLE %s +// REQUIRES: OS=macosx && CPU=x86_64 + +@available(macOS 12.0, *) +public func f(_ s: S) async throws -> Any.Type { + for try await _ in s { } + + typealias Fn = @MainActor () -> S.Element + return Fn.self +} + +// MAYBE-AVAILABLE: @"$sScI4next7ElementQzSgyYaKFTjTu" = extern_weak global +// MAYBE-AVAILABLE: declare{{.*}} extern_weak{{.*}} @swift_getFunctionTypeMetadataGlobalActor diff --git a/test/IRGen/async_dynamic_replacement.swift b/test/IRGen/async_dynamic_replacement.swift new file mode 100644 index 0000000000000..6f1a0ea5594db --- /dev/null +++ b/test/IRGen/async_dynamic_replacement.swift @@ -0,0 +1,25 @@ +// RUN: %target-swift-frontend %s -emit-ir -disable-availability-checking -disable-objc-interop | %FileCheck %s + +// Windows does not do swiftailcc +// XFAIL: OS=windows-msvc + +// REQUIRES: concurrency + +public dynamic func number() async -> Int { + return 100 +} + +@_dynamicReplacement(for: number()) +internal func _replacement_number() async -> Int { + return 200 +} + +// rdar://78284346 - Dynamic replacement should use musttail +// for tail calls from swifttailcc to swifttailcc +// CHECK-LABEL: define {{.*}} swifttailcc void @"$s25async_dynamic_replacement01_C7_numberSiyYaFTI" +// CHECK: musttail call swifttailcc void +// CHECK-NEXT: ret void + +public func calls_number() async -> Int { + await number() +} diff --git a/test/IRGen/autolink-coff-x86.swift b/test/IRGen/autolink-coff-x86.swift index a771134bf109f..bcc7561552eaf 100644 --- a/test/IRGen/autolink-coff-x86.swift +++ b/test/IRGen/autolink-coff-x86.swift @@ -11,7 +11,7 @@ import module #endif // CHECK-GNU-IR: @_swift1_autolink_entries = private constant [9 x i8] c"-lmodule\00", section ".swift1_autolink_entries", align 8 -// CHECK-GNU-IR: @llvm.used = appending global [{{.*}} x i8*] [{{.*}}i8* getelementptr inbounds ([9 x i8], [9 x i8]* @_swift1_autolink_entries, i32 0, i32 0){{.*}}], section "llvm.metadata", align 8 +// CHECK-GNU-IR: @llvm.used = appending global [{{.*}} x i8*] [{{.*}}i8* getelementptr inbounds ([9 x i8], [9 x i8]* @_swift1_autolink_entries, i32 0, i32 0){{.*}}], section "llvm.metadata" // CHECK-GNU-ASM: .section .swift1_autolink_entries{{.*}} // CHECK-GNU-ASM: .asciz "-lmodule" diff --git a/test/IRGen/autolink-force-link.swift b/test/IRGen/autolink-force-link.swift index 9c876fd2d9de9..6561a28db5aad 100644 --- a/test/IRGen/autolink-force-link.swift +++ b/test/IRGen/autolink-force-link.swift @@ -3,27 +3,25 @@ // RUN: %swift -disable-legacy-type-info -target x86_64-apple-macosx10.9 -parse-stdlib -autolink-force-load -module-name TEST -module-link-name TEST -emit-ir %s %S/../Inputs/empty.swift | %FileCheck -check-prefix=CHECK-WMO %s // CHECK-WMO: source_filename = "" -// CHECK-WMO: define void @"_swift_FORCE_LOAD_$_TEST"() +// CHECK-WMO: @"_swift_FORCE_LOAD_$_TEST" // CHECK-WMO-NOT: source_filename // RUN: %swift -disable-legacy-type-info -target x86_64-apple-macosx10.9 -parse-stdlib -autolink-force-load -module-name TEST -module-link-name TEST -emit-ir -num-threads 1 %s %S/../Inputs/empty.swift | %FileCheck -check-prefix=CHECK-WMO-THREADED %s // CHECK-WMO-THREADED: source_filename = "" -// CHECK-WMO-THREADED: define void @"_swift_FORCE_LOAD_$_TEST"() +// CHECK-WMO-THREADED: @"_swift_FORCE_LOAD_$_TEST" // CHECK-WMO-THREADED: source_filename = "" -// CHECK-WMO-THREADED-NOT: _swift_FORCE_LOAD_$_TEST -// CHECK-WMO-THREADED-NOT: source_filename - +// CHECK-WMO-THREADED: @"_swift_FORCE_LOAD_$_TEST" // RUN: %swift -disable-legacy-type-info -target x86_64-apple-macosx10.9 -parse-stdlib -autolink-force-load -module-name TEST -module-link-name TEST -emit-ir -primary-file %s %S/../Inputs/empty.swift | %FileCheck -check-prefix=CHECK-SINGLE-FILE-FIRST %s // RUN: %swift -disable-legacy-type-info -target x86_64-apple-macosx10.9 -parse-stdlib -autolink-force-load -module-name TEST -module-link-name TEST -emit-ir %S/../Inputs/empty.swift -primary-file %s | %FileCheck -check-prefix=CHECK-SINGLE-FILE-SECOND %s // CHECK-SINGLE-FILE-FIRST: source_filename = "" -// CHECK-SINGLE-FILE-FIRST: define void @"_swift_FORCE_LOAD_$_TEST"() +// CHECK-SINGLE-FILE-FIRST: @"_swift_FORCE_LOAD_$_TEST" // CHECK-SINGLE-FILE-FIRST-NOT: source_filename // CHECK-SINGLE-FILE-SECOND: source_filename = "" -// CHECK-SINGLE-FILE-SECOND-NOT: _swift_FORCE_LOAD_$_TEST +// CHECK-SINGLE-FILE-SECOND: @"_swift_FORCE_LOAD_$_TEST" // CHECK-SINGLE-FILE-SECOND-NOT: source_filename diff --git a/test/IRGen/autolink-runtime-compatibility-arm64-macos.swift b/test/IRGen/autolink-runtime-compatibility-arm64-macos.swift index 48e9c61955e44..09726555833b7 100644 --- a/test/IRGen/autolink-runtime-compatibility-arm64-macos.swift +++ b/test/IRGen/autolink-runtime-compatibility-arm64-macos.swift @@ -1,3 +1,4 @@ +// REQUIRES: rdar81115750 // REQUIRES: CPU=arm64,OS=macosx // Doesn't autolink compatibility library because target OS doesn't need it diff --git a/test/IRGen/autolink-runtime-compatibility.swift b/test/IRGen/autolink-runtime-compatibility.swift index f917f2115481a..98151e47075bd 100644 --- a/test/IRGen/autolink-runtime-compatibility.swift +++ b/test/IRGen/autolink-runtime-compatibility.swift @@ -1,8 +1,8 @@ // REQUIRES: OS=macosx,CPU=x86_64 // Doesn't autolink compatibility library because autolinking is disabled -// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -target %target-cpu-apple-macosx10.9 -disable-autolinking-runtime-compatibility -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s -// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version 5.0 -disable-autolinking-runtime-compatibility -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s +// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -target %target-cpu-apple-macosx10.9 -disable-autolinking-runtime-compatibility -disable-autolinking-runtime-compatibility-concurrency -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s +// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version 5.0 -disable-autolinking-runtime-compatibility -disable-autolinking-runtime-compatibility-concurrency -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s // Doesn't autolink compatibility library because runtime compatibility library is disabled // RUN: %target-swift-frontend -runtime-compatibility-version none -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s @@ -16,8 +16,10 @@ // Autolinks because compatibility library was explicitly asked for // RUN: %target-swift-frontend -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -runtime-compatibility-version 5.1 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-51 %s +// RUN: %target-swift-frontend -runtime-compatibility-version 5.5 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-55 %s // RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.1 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-51 %s +// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.24 -runtime-compatibility-version 5.5 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD-55 %s public func foo() {} @@ -25,20 +27,38 @@ public func foo() {} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility50"} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility51"} // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibilityDynamicReplacements"} +// NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibilityConcurrency"} // FORCE-LOAD: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility50" // FORCE-LOAD: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibilityConcurrency" // FORCE-LOAD-DAG: [[AUTOLINK_SWIFT_COMPAT:![0-9]+]] = !{!"-lswiftCompatibility50"} // FORCE-LOAD-DAG: !{!"-lswiftCompatibility51"} // FORCE-LOAD-DAG: !{!"-lswiftCompatibilityDynamicReplacements"} +// FORCE-LOAD-DAG: !{!"-lswiftCompatibilityConcurrency"} // FORCE-LOAD-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" -// FORCE-LOAD-51: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-51-DAG: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-51-DAG: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibilityConcurrency" // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" // FORCE-LOAD-51-DAG: [[AUTOLINK_SWIFT_COMPAT:![0-9]+]] = !{!"-lswiftCompatibility51"} -// FORCE-LOAD-51-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} +// FORCE-LOAD-51-DAG: [[AUTOLINK_SWIFT_COMPAT_CONCURRENCY:![0-9]+]] = !{!"-lswiftCompatibilityConcurrency"} +// FORCE-LOAD-51-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]], {{.*}}[[AUTOLINK_SWIFT_COMPAT_CONCURRENCY]]{{[,}]}} // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" // FORCE-LOAD-51-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" + +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-55-DAG: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibilityConcurrency" +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" +// FORCE-LOAD-55-DAG: [[AUTOLINK_SWIFT_COMPAT_CONCURRENCY:![0-9]+]] = !{!"-lswiftCompatibilityConcurrency"} +// FORCE-LOAD-55-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT_CONCURRENCY]]{{[,}]}} +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" +// FORCE-LOAD-55-NOT: @"_swift_FORCE_LOAD_$_swiftCompatibility51" diff --git a/test/IRGen/autolink_elf.swift b/test/IRGen/autolink_elf.swift index 974383e41e6b0..06caf06d81a47 100644 --- a/test/IRGen/autolink_elf.swift +++ b/test/IRGen/autolink_elf.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %swift -disable-legacy-type-info -target x86_64-unknown-linux-gnu -emit-module -parse-stdlib -o %t -module-name Empty -module-link-name swiftEmpty %S/../Inputs/empty.swift +// RUN: %swift -disable-legacy-type-info -target x86_64-unknown-linux-gnu -emit-module -parse-stdlib -o %t -module-name Empty -module-link-name swiftEmpty -public-autolink-library anotherLib %S/../Inputs/empty.swift // RUN: %swift -disable-legacy-type-info -target x86_64-unknown-linux-gnu %s -I %t -parse-stdlib -disable-objc-interop -module-name main -emit-ir -o - | %FileCheck %s // REQUIRES: CODEGENERATOR=X86 @@ -9,6 +9,6 @@ import Empty // Check that on ELF targets autolinking information is emitted and marked // as used. -// CHECK-DAG: @_swift1_autolink_entries = private constant [13 x i8] c"-lswiftEmpty\00", section ".swift1_autolink_entries", align 8 -// CHECK-DAG: @llvm.used = appending global [{{.*}} x i8*] [{{.*}}i8* getelementptr inbounds ([13 x i8], [13 x i8]* @_swift1_autolink_entries, i32 0, i32 0){{.*}}], section "llvm.metadata", align 8 +// CHECK-DAG: @_swift1_autolink_entries = private constant [26 x i8] c"-lswiftEmpty\00-lanotherLib\00", section ".swift1_autolink_entries", align 8 +// CHECK-DAG: @llvm.used = appending global [{{.*}} x i8*] [{{.*}}i8* getelementptr inbounds ([26 x i8], [26 x i8]* @_swift1_autolink_entries, i32 0, i32 0){{.*}}], section "llvm.metadata" diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index 9e47d80cfefbe..786eede225e47 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -168,7 +168,7 @@ class SuperSub : SuperBase { // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s22big_types_corner_cases10MUseStructV16superclassMirrorAA03BigF0VSgvg"(%T22big_types_corner_cases9BigStructVSg* noalias nocapture sret({{.*}}) %0, %T22big_types_corner_cases10MUseStructV* noalias nocapture swiftself dereferenceable({{.*}}) %1) // CHECK: [[ALLOC:%.*]] = alloca %T22big_types_corner_cases9BigStructVSg // CHECK: [[LOAD:%.*]] = load %swift.refcounted*, %swift.refcounted** %.callInternalLet.data -// CHECK: call swiftcc void %7(%T22big_types_corner_cases9BigStructVSg* noalias nocapture sret({{.*}}) [[ALLOC]], %swift.refcounted* swiftself [[LOAD]]) +// CHECK: call swiftcc void %{{[0-9]+}}(%T22big_types_corner_cases9BigStructVSg* noalias nocapture sret({{.*}}) [[ALLOC]], %swift.refcounted* swiftself [[LOAD]]) // CHECK: ret void public struct MUseStruct { var x = BigStruct() diff --git a/test/IRGen/big_types_coroutine.sil b/test/IRGen/big_types_coroutine.sil index febd5abdda9d3..50861efc2e7c6 100644 --- a/test/IRGen/big_types_coroutine.sil +++ b/test/IRGen/big_types_coroutine.sil @@ -97,3 +97,24 @@ bb2: abort_apply %4 unwind } + +// CHECK-LABEL: sil @test_yield_and_retain +// CHECK: [[S:%[0-9]+]] = alloc_stack $BigStruct +// CHECK: copy_addr [take] %0 to [initialization] [[S]] +// CHECK: retain_value_addr [[S]] +// CHECK: yield [[S]] : $*BigStruct +// CHECK: // end sil function 'test_yield_and_retain' +sil @test_yield_and_retain : $@convention(thin) @yield_once (@in_guaranteed BigStruct) -> @yields BigStruct { +entry(%0 : $*BigStruct): + %big = load %0 : $*BigStruct + retain_value %big : $BigStruct + yield %big : $BigStruct, resume resume, unwind unwind + +resume: + %ret = tuple () + return %ret : $() + +unwind: + unwind +} + diff --git a/test/IRGen/builtins.swift b/test/IRGen/builtins.swift index c192a6516bbe9..e7e35cb788a54 100644 --- a/test/IRGen/builtins.swift +++ b/test/IRGen/builtins.swift @@ -635,10 +635,10 @@ func acceptsBuiltinNativeObject(_ ref: inout Builtin.NativeObject?) {} // native // CHECK-LABEL: define hidden {{.*}}i1 @"$s8builtins8isUniqueyBi1_BoSgzF"({{%.*}}* nocapture dereferenceable({{.*}}) %0) {{.*}} { // CHECK-NEXT: entry: -// CHECK-NEXT: bitcast [[BUILTIN_NATIVE_OBJECT_TY]]* %0 to %swift.refcounted** -// CHECK-NEXT: load %swift.refcounted*, %swift.refcounted** %1 -// CHECK-NEXT: call i1 @swift_isUniquelyReferenced_native(%swift.refcounted* %2) -// CHECK-NEXT: ret i1 %3 +// CHECK: %[[BITCAST_RC:.+]] = bitcast [[BUILTIN_NATIVE_OBJECT_TY]]* %0 to %swift.refcounted** +// CHECK: %[[LD_RC:.+]] = load %swift.refcounted*, %swift.refcounted** %[[BITCAST_RC]] +// CHECK-NEXT: %[[RET:.+]] = call i1 @swift_isUniquelyReferenced_native(%swift.refcounted* %[[LD_RC]]) +// CHECK-NEXT: ret i1 %[[RET]] func isUnique(_ ref: inout Builtin.NativeObject?) -> Bool { return Builtin.isUnique(&ref) } @@ -646,9 +646,9 @@ func isUnique(_ ref: inout Builtin.NativeObject?) -> Bool { // native nonNull // CHECK-LABEL: define hidden {{.*}}i1 @"$s8builtins8isUniqueyBi1_BozF"(%swift.refcounted** nocapture dereferenceable({{.*}}) %0) {{.*}} { // CHECK-NEXT: entry: -// CHECK-NEXT: load %swift.refcounted*, %swift.refcounted** %0 -// CHECK-NEXT: call i1 @swift_isUniquelyReferenced_nonNull_native(%swift.refcounted* %1) -// CHECK-NEXT: ret i1 %2 +// CHECK: %[[LD_RC:.+]] = load %swift.refcounted*, %swift.refcounted** %0 +// CHECK: %[[RET:.+]] = call i1 @swift_isUniquelyReferenced_nonNull_native(%swift.refcounted* %[[LD_RC]]) +// CHECK-NEXT: ret i1 %[[RET]] func isUnique(_ ref: inout Builtin.NativeObject) -> Bool { return Builtin.isUnique(&ref) } @@ -659,10 +659,10 @@ func acceptsAnyObject(_ ref: inout Builtin.AnyObject?) {} // ObjC // CHECK-LABEL: define hidden {{.*}}i1 @"$s8builtins8isUniqueyBi1_yXlSgzF"({{%.*}}* nocapture dereferenceable({{.*}}) %0) {{.*}} { // CHECK-NEXT: entry: -// CHECK-NEXT: [[ADDR:%.+]] = getelementptr inbounds [[OPTIONAL_ANYOBJECT_TY]], [[OPTIONAL_ANYOBJECT_TY]]* %0, i32 0, i32 0 +// CHECK: [[ADDR:%.+]] = getelementptr inbounds [[OPTIONAL_ANYOBJECT_TY]], [[OPTIONAL_ANYOBJECT_TY]]* %0, i32 0, i32 0 // CHECK-NEXT: [[CASTED:%.+]] = bitcast {{.+}}* [[ADDR]] to [[UNKNOWN_OBJECT:%objc_object|%swift\.refcounted]]** // CHECK-NEXT: [[REF:%.+]] = load [[UNKNOWN_OBJECT]]*, [[UNKNOWN_OBJECT]]** [[CASTED]] -// CHECK-objc-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferencedNonObjC([[UNKNOWN_OBJECT]]* [[REF]]) +// CHECK-objc-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced{{(NonObjC)?}}([[UNKNOWN_OBJECT]]* [[REF]]) // CHECK-native-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced_native([[UNKNOWN_OBJECT]]* [[REF]]) // CHECK-NEXT: ret i1 [[RESULT]] func isUnique(_ ref: inout Builtin.AnyObject?) -> Bool { @@ -673,9 +673,9 @@ func isUnique(_ ref: inout Builtin.AnyObject?) -> Bool { // CHECK-LABEL: define hidden {{.*}}i1 @"$s8builtins8isUniqueyBi1_yXlzF" // CHECK-SAME: (%AnyObject* nocapture dereferenceable({{.*}}) %0) {{.*}} { // CHECK-NEXT: entry: -// CHECK-NEXT: [[ADDR:%.+]] = getelementptr inbounds %AnyObject, %AnyObject* %0, i32 0, i32 0 -// CHECK-NEXT: [[REF:%.+]] = load [[UNKNOWN_OBJECT]]*, [[UNKNOWN_OBJECT]]** [[ADDR]] -// CHECK-objc-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferencedNonObjC_nonNull([[UNKNOWN_OBJECT]]* [[REF]]) +// CHECK: [[ADDR:%.+]] = getelementptr inbounds %AnyObject, %AnyObject* %0, i32 0, i32 0 +// CHECK: [[REF:%.+]] = load [[UNKNOWN_OBJECT]]*, [[UNKNOWN_OBJECT]]** [[ADDR]] +// CHECK-objc-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced{{(NonObjC)?}}_nonNull([[UNKNOWN_OBJECT]]* [[REF]]) // CHECK-native-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced_nonNull_native([[UNKNOWN_OBJECT]]* [[REF]]) // CHECK-NEXT: ret i1 [[RESULT]] func isUnique(_ ref: inout Builtin.AnyObject) -> Bool { @@ -685,9 +685,9 @@ func isUnique(_ ref: inout Builtin.AnyObject) -> Bool { // BridgeObject nonNull // CHECK-LABEL: define hidden {{.*}}i1 @"$s8builtins8isUniqueyBi1_BbzF"(%swift.bridge** nocapture dereferenceable({{.*}}) %0) {{.*}} { // CHECK-NEXT: entry: -// CHECK-NEXT: load %swift.bridge*, %swift.bridge** %0 -// CHECK-NEXT: call i1 @swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject(%swift.bridge* %1) -// CHECK-NEXT: ret i1 %2 +// CHECK: %[[LD:.+]] = load %swift.bridge*, %swift.bridge** %0 +// CHECK: %[[RET:.+]] = call i1 @swift_isUniquelyReferenced{{(NonObjC)?}}_nonNull_bridgeObject(%swift.bridge* %[[LD]]) +// CHECK-NEXT: ret i1 %[[RET]] func isUnique(_ ref: inout Builtin.BridgeObject) -> Bool { return Builtin.isUnique(&ref) } @@ -701,10 +701,10 @@ func assumeTrue(_ x: Builtin.Int1) { // BridgeObject nonNull // CHECK-LABEL: define hidden {{.*}}i1 @"$s8builtins15isUnique_nativeyBi1_BbzF"(%swift.bridge** nocapture dereferenceable({{.*}}) %0) {{.*}} { // CHECK-NEXT: entry: -// CHECK-NEXT: bitcast %swift.bridge** %0 to %swift.refcounted** -// CHECK-NEXT: load %swift.refcounted*, %swift.refcounted** %1 -// CHECK-NEXT: call i1 @swift_isUniquelyReferenced_nonNull_native(%swift.refcounted* %2) -// CHECK-NEXT: ret i1 %3 +// CHECK: %[[BC:.+]] = bitcast %swift.bridge** %0 to %swift.refcounted** +// CHECK: %[[LD:.+]] = load %swift.refcounted*, %swift.refcounted** %[[BC]] +// CHECK-NEXT: %[[RET:.+]] = call i1 @swift_isUniquelyReferenced_nonNull_native(%swift.refcounted* %[[LD]]) +// CHECK-NEXT: ret i1 %[[RET]] func isUnique_native(_ ref: inout Builtin.BridgeObject) -> Bool { return Builtin.isUnique_native(&ref) } diff --git a/test/IRGen/debug_poison.swift b/test/IRGen/debug_poison.swift index 9c4ec9c48d920..252d636ec1874 100644 --- a/test/IRGen/debug_poison.swift +++ b/test/IRGen/debug_poison.swift @@ -37,7 +37,7 @@ private func useOptionalK(_: K?) -> Int { // CHECK: store %T12debug_poison1KC* [[REF]], %T12debug_poison1KC** %b.debug // CHECK: [[Y:%.*]] = call {{.*}} [[INT]] @"$s12debug_poison4use{{.*}}"(%T12debug_poison1KC* [[REF]]) // CHECK: call void {{.*}} @swift_release {{.*}} [[REF]] -// CHECK: store %T12debug_poison1KC* inttoptr ([[INT]] 2176 to %T12debug_poison1KC*), %T12debug_poison1KC** %b.debug +// CHECK: store %T12debug_poison1KC* inttoptr ([[INT]] 1088 to %T12debug_poison1KC*), %T12debug_poison1KC** %b.debug // CHECK: store [[INT]] [[Y]], [[INT]]* %y.debug // CHECK: call {{.*}} void @"$s12debug_poison6useIntyySiF"([[INT]] [[Y]]) public func testPoisonRef() { @@ -56,7 +56,7 @@ public func testPoisonRef() { // CHECK: [[Y:%.*]] = call {{.*}} [[INT]] @"$s12debug_poison12useOptionalK{{.*}}"([[INT]] [[REF]]) // CHECK: call void @swift_release // CHECK: [[NIL:%.*]] = icmp eq [[INT]] [[REF]], 0 -// CHECK: [[POISON:%.*]] = select i1 [[NIL]], [[INT]] [[REF]], [[INT]] 2176 +// CHECK: [[POISON:%.*]] = select i1 [[NIL]], [[INT]] [[REF]], [[INT]] 1088 // CHECK: store [[INT]] [[POISON]], [[INT]]* %b.debug // CHECK: store [[INT]] [[Y]], [[INT]]* %y.debug // CHECK: call {{.*}} void @"$s12debug_poison6useIntyySiF"([[INT]] [[Y]]) @@ -80,7 +80,7 @@ public func testPoisonOptionalRef() { // CHECK: call {{.*}} void @"$s12debug_poison6useAnyyyypF"( // CHECK: call void @swift_{{unknownObjectRelease|release}}(%[[REFTY]]* [[REF]]) #1 // CHECK: [[GEP1:%.*]] = getelementptr inbounds %T12debug_poison1PP, %T12debug_poison1PP* %b.debug, i32 0, i32 0 -// CHECK: store %[[REFTY]]* inttoptr ([[INT]] 2176 to %[[REFTY]]*), %[[REFTY]]** [[GEP1]] +// CHECK: store %[[REFTY]]* inttoptr ([[INT]] 1088 to %[[REFTY]]*), %[[REFTY]]** [[GEP1]] // CHECK: call {{.*}} void @"$s12debug_poison7useNoneyyF"() public func testPoisonExistential() { let b: P = D() @@ -102,7 +102,7 @@ public func testPoisonExistential() { // CHECK: call {{.*}} void @"$s12debug_poison6useAnyyyypF"( // CHECK: call void @swift_{{unknownObjectRelease|release}}(%[[REFTY]]* [[REF]]) #1 // CHECK: [[GEP1:%.*]] = getelementptr inbounds %T12debug_poison1Q_Xl, %T12debug_poison1Q_Xl* %b.debug, i32 0, i32 0 -// CHECK: store %[[REFTY]]* inttoptr ([[INT]] 2176 to %[[REFTY]]*), %[[REFTY]]** [[GEP1]] +// CHECK: store %[[REFTY]]* inttoptr ([[INT]] 1088 to %[[REFTY]]*), %[[REFTY]]** [[GEP1]] // CHECK: call {{.*}} void @"$s12debug_poison7useNoneyyF"() public func testPoisonComposite() { let b: Q & AnyObject = E() diff --git a/test/IRGen/debug_scope.sil b/test/IRGen/debug_scope.sil new file mode 100644 index 0000000000000..fbfe197018cee --- /dev/null +++ b/test/IRGen/debug_scope.sil @@ -0,0 +1,27 @@ +// RUN: %target-swiftc_driver -g -emit-ir %s | %FileCheck %s +sil_stage canonical + +import Builtin +import Swift + +// SR-14856: IRGen couldn't handle `SILLocation` with `FilenameAndLocationKind` +// storage kind. + +sil_scope 2 { loc "the_file.swift":2:6 parent @foo : $@convention(thin) () -> () } +sil_scope 3 { loc "the_file.swift":3:5 parent 2 } + +// It shouldn't crash +// CHECK: @foo +// CHECK-SAME: !dbg ![[FUNC_DI:[0-9]+]] +sil hidden @foo : $@convention(thin) () -> () { +bb0: + %2 = integer_literal $Builtin.Int64, 17, loc "the_file.swift":3:12, scope 3 + %3 = tuple () + return %3 : $() +} + +// Debug info should be correctly generated +// CHECK: distinct !DILexicalBlock(scope: ![[FUNC_DI]] +// CHECK-SAME: file: ![[FILE_DI:[0-9]+]] +// CHECK-SAME: line: 3, column: 5 +// CHECK: ![[FILE_DI]] = !DIFile(filename: "the_file.swift" diff --git a/test/IRGen/distributed_actor.swift b/test/IRGen/distributed_actor.swift new file mode 100644 index 0000000000000..4919d04fbb22a --- /dev/null +++ b/test/IRGen/distributed_actor.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -emit-ir %s -swift-version 5 -enable-experimental-distributed | %IRGenFileCheck %s +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +// Type descriptor. +// CHECK-LABEL: @"$s17distributed_actor7MyActorC0B9Transport12_Distributed0dE0_pvpWvd" + +@available(SwiftStdlib 5.5, *) +public distributed actor MyActor { + // nothing +} diff --git a/test/IRGen/dynamic_replaceable_coroutine.swift b/test/IRGen/dynamic_replaceable_coroutine.swift new file mode 100644 index 0000000000000..262b37807b95c --- /dev/null +++ b/test/IRGen/dynamic_replaceable_coroutine.swift @@ -0,0 +1,31 @@ +// RUN: %target-swift-frontend -module-name A -enable-implicit-dynamic -emit-ir %s | %FileCheck %s + + +extension Int { + public struct Thing { + var _int: Int + init(_ int: Int) { + self._int = int + } + } + + public var thing: Thing { + get { Thing(self) } + // Make sure the initialization of `thing` is after the dynamic replacement + // check. Coro splitting does not like memsets before the coro.begin. + + // CHECK: define{{.*}} swiftcc { i8*, %TSi1AE5ThingV* } @"$sSi1AE5thingSiAAE5ThingVvM" + // CHECK: call i8* @swift_getFunctionReplacement + // CHECK: br + // CHECK: original_entry: + // CHECK: [[FRAMEPTR:%.*]] = bitcast i8* %0 to + // CHECK: [[THING:%.*]] = getelementptr inbounds {{.*}}* [[FRAMEPTR]], i32 0 + // CHECK: [[THING2:%.*]] = bitcast %TSi1AE5ThingV* [[THING]] to i8* + // CHECK: call void @llvm.memset{{.*}}(i8* {{.*}} [[THING2]] + // CHECK: ret + _modify { + var thing = Thing(self) + yield &thing + } + } +} diff --git a/test/IRGen/dynamic_self.sil b/test/IRGen/dynamic_self.sil index 27d0e04d7b82a..551be825b98bc 100644 --- a/test/IRGen/dynamic_self.sil +++ b/test/IRGen/dynamic_self.sil @@ -14,7 +14,7 @@ protocol P { // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @_TF12dynamic_self23testExistentialDispatchFT1pPS_1P__T_ sil @_TF12dynamic_self23testExistentialDispatchFT1pPS_1P__T_ : $@convention(thin) (@in P) -> () { bb0(%0 : $*P): - debug_value_addr %0 : $*P, let, name "p" // id: %1 + debug_value %0 : $*P, let, name "p", expr op_deref // id: %1 %2 = alloc_stack $P // users: %3, %4, %12 copy_addr %0 to [initialization] %2 : $*P // id: %3 // CHECK: call %swift.opaque* diff --git a/test/IRGen/enum_resilience.swift b/test/IRGen/enum_resilience.swift index 7ea8f243f6531..7c27e4021c2ef 100644 --- a/test/IRGen/enum_resilience.swift +++ b/test/IRGen/enum_resilience.swift @@ -165,7 +165,7 @@ public func constructResilientEnumPayload(_ s: Size) -> Medium { // CHECK-NEXT: call void @llvm.lifetime.start.p0i8({{(i32|i64)}} -1, i8* [[ALLOCA]]) // CHECK-NEXT: [[ENUM_STORAGE:%.*]] = bitcast i8* [[ALLOCA]] to %swift.opaque* -// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 2 +// CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 2 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]] // CHECK-NEXT: [[WITNESS_FN:%initializeWithCopy]] = bitcast i8* [[WITNESS]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* // CHECK-arm64e-NEXT: ptrtoint i8** [[WITNESS_ADDR]] to i64 diff --git a/test/IRGen/frozen_protocols.swift b/test/IRGen/frozen_protocols.swift index 42b997b9c4b72..e3de260859d3f 100644 --- a/test/IRGen/frozen_protocols.swift +++ b/test/IRGen/frozen_protocols.swift @@ -43,7 +43,7 @@ public struct ConformsToFrozenProtocol : FrozenProtocol { // CHECK: [[ADDR:%.*]] = getelementptr inbounds i8*, i8** %T.OtherFrozenProtocol, i32 1 // CHECK: [[FN:%.*]] = load i8*, i8** [[ADDR]] // CHECK: [[CAST:%.*]] = bitcast i8* [[FN]] to void (%swift.opaque*, %swift.type*, i8**)* -// CHECK: call swiftcc void %3(%swift.opaque* noalias nocapture swiftself %0, %swift.type* %T, i8** %T.OtherFrozenProtocol) +// CHECK: call swiftcc void [[CAST]](%swift.opaque* noalias nocapture swiftself %0, %swift.type* %T, i8** %T.OtherFrozenProtocol) // CHECK: ret void // @_fixed_layout protocols still emit method dispatch thunks though, which diff --git a/test/IRGen/generic_tuples.swift b/test/IRGen/generic_tuples.swift index 647ecfdf0d7bc..9d3a8037b9a32 100644 --- a/test/IRGen/generic_tuples.swift +++ b/test/IRGen/generic_tuples.swift @@ -25,7 +25,7 @@ func dup(_ x: T) -> (T, T) { var x = x; return (x,x) } // CHECK: [[X_TMP:%.*]] = bitcast i8* [[X_ALLOCA]] to %swift.opaque* // Debug info shadow copy. // CHECK-NEXT: store i8* [[X_ALLOCA]] -// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 2 +// CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 2 // CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]], align 8 // CHECK-NEXT: [[INITIALIZE_WITH_COPY:%.*]] = bitcast i8* [[WITNESS]] to [[OPAQUE]]* ([[OPAQUE]]*, [[OPAQUE]]*, [[TYPE]]*)* // CHECK-NEXT: [[X:%.*]] = call [[OPAQUE]]* [[INITIALIZE_WITH_COPY]]([[OPAQUE]]* noalias [[X_TMP]], [[OPAQUE]]* noalias {{.*}}, [[TYPE]]* %T) diff --git a/test/IRGen/global_actor_function_types.sil b/test/IRGen/global_actor_function_types.sil new file mode 100644 index 0000000000000..2ffe5cc6b8878 --- /dev/null +++ b/test/IRGen/global_actor_function_types.sil @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend -emit-ir -o - -primary-file %s | %FileCheck %s --check-prefix CHECK --check-prefix CHECK-%target-cpu +// REQUIRES: concurrency + +import Swift +import _Concurrency + +func globalActorMetatype(_: T.Type) -> Any.Type { + typealias Fn = @MainActor () -> T + return Fn.self +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.type* @"$s4test19globalActorMetatypeyypXpxmlF" +// CHECK: [[MAIN_ACTOR_RESPONSE:%[0-9]+]] = call swiftcc %swift.metadata_response @"$sScMMa"(i{{32|64}} 255) +// CHECK-NEXT: [[MAIN_ACTOR_METADATA:%[0-9]+]] = extractvalue %swift.metadata_response [[MAIN_ACTOR_RESPONSE]], 0 +// CHECK: call %swift.type* @swift_getFunctionTypeMetadataGlobalActor(i{{32|64}} 335544320, i{{32|64}} 0, %swift.type** null, i32* null, %swift.type* %T, %swift.type* [[MAIN_ACTOR_METADATA]]) +sil [ossa] @$s4test19globalActorMetatypeyypXpxmlF : $@convention(thin) (@thick T.Type) -> @thick Any.Type { +bb0(%0 : $@thick T.Type): + %2 = metatype $@thin (@MainActor () -> T).Type + %3 = metatype $@thick (@MainActor () -> T).Type + %4 = init_existential_metatype %3 : $@thick (@MainActor () -> T).Type, $@thick Any.Type + return %4 : $@thick Any.Type +} diff --git a/test/IRGen/jit_metadata_table.swift b/test/IRGen/jit_metadata_table.swift new file mode 100644 index 0000000000000..800a5e2e00c8d --- /dev/null +++ b/test/IRGen/jit_metadata_table.swift @@ -0,0 +1,10 @@ +// RUN: %target-swift-frontend %s -emit-ir -use-jit | %FileCheck %s + + +class A{} + +// Check that only one copy of the type metadata table is emitted. +// CHECK: @"\01l_type_metadata_table" +// CHECK-NOT: @"\01l_type_metadata_table.1" + + diff --git a/test/IRGen/lifetime.sil b/test/IRGen/lifetime.sil index e40bce795462e..c1548218960bf 100644 --- a/test/IRGen/lifetime.sil +++ b/test/IRGen/lifetime.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -gnone -emit-ir %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -disable-debugger-shadow-copies -gnone -emit-ir %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s -DINT=i%target-ptrsize // CHECK: [[OPAQUE:%swift.opaque]] = type opaque // CHECK: [[TYPE:%swift.type]] = type diff --git a/test/IRGen/lit.local.cfg b/test/IRGen/lit.local.cfg index 0597958565874..5434575037c21 100644 --- a/test/IRGen/lit.local.cfg +++ b/test/IRGen/lit.local.cfg @@ -2,12 +2,12 @@ config.substitutions = list(config.substitutions) config.substitutions.insert(0, ('%build-irgen-test-overlays', - '%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -o %t -sdk %S/Inputs %S/Inputs/ObjectiveC.swift && ' - '%target-swift-frontend -enable-objc-interop -emit-module -o %t -sdk %S/Inputs %S/Inputs/Foundation.swift -I %t')) + '%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -enable-library-evolution -o %t -sdk %S/Inputs %S/Inputs/ObjectiveC.swift && ' + '%target-swift-frontend -enable-objc-interop -emit-module -enable-library-evolution -o %t -sdk %S/Inputs %S/Inputs/Foundation.swift -I %t')) config.substitutions.insert(0, ('%build-irgen-test-overlays\(mock-sdk-directory: ([^)]+)\)', - SubstituteCaptures(r'%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -o %t -sdk \1 \1/ObjectiveC.swift && ' - r'%target-swift-frontend -enable-objc-interop -emit-module -o %t -sdk \1 \1/Foundation.swift -I %t'))) + SubstituteCaptures(r'%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -enable-library-evolution -o %t -sdk \1 \1/ObjectiveC.swift && ' + r'%target-swift-frontend -enable-objc-interop -emit-module -enable-library-evolution -o %t -sdk \1 \1/Foundation.swift -I %t'))) def get_target_os(): import re diff --git a/test/IRGen/objc.swift b/test/IRGen/objc.swift index 87f14a01c206a..51863c5c34b98 100644 --- a/test/IRGen/objc.swift +++ b/test/IRGen/objc.swift @@ -145,7 +145,7 @@ class WeakObjC { // CHECK: i32 1, !"Objective-C Version", i32 2} // CHECK: i32 1, !"Objective-C Image Info Version", i32 0} // CHECK: i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"} -// 84215552 == (5 << 24) | (5 << 16) | (7 << 8). -// 5 and 5 is the current major.minor version. 7 is the Swift ABI version. -// CHECK: i32 4, !"Objective-C Garbage Collection", i32 84215552} +// 84281088 == (5 << 24) | (6 << 16) | (7 << 8). +// 5 and 6 is the current major.minor version. 7 is the Swift ABI version. +// CHECK: i32 4, !"Objective-C Garbage Collection", i32 84281088} // CHECK: i32 1, !"Swift Version", i32 7} diff --git a/test/IRGen/objc_async_metadata.swift b/test/IRGen/objc_async_metadata.swift index 01dbbcb9f0ec9..20d69f385dc48 100644 --- a/test/IRGen/objc_async_metadata.swift +++ b/test/IRGen/objc_async_metadata.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %build-irgen-test-overlays -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -enable-experimental-concurrency %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -disable-availability-checking %s -emit-ir | %FileCheck %s // REQUIRES: OS=macosx // REQUIRES: concurrency diff --git a/test/IRGen/objc_enum.swift b/test/IRGen/objc_enum.swift new file mode 100644 index 0000000000000..e76cff5a0c275 --- /dev/null +++ b/test/IRGen/objc_enum.swift @@ -0,0 +1,30 @@ +// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s + +// REQUIRES: CPU=x86_64 + +@objc public enum ExportedToObjC: Int32 { + case Foo = -1, Bar, Bas +} + +// CHECK-LABEL: define{{( protected)?( dllexport)?}} swiftcc i32 @"$s9objc_enum0a1_B7_injectAA14ExportedToObjCOyF"() +// CHECK: ret i32 -1 +public func objc_enum_inject() -> ExportedToObjC { + return .Foo +} + +// CHECK-LABEL: define{{( protected)?( dllexport)?}} swiftcc i64 @"$s9objc_enum0a1_B7_switchySiAA14ExportedToObjCOF"(i32 %0) +// CHECK: switch i32 %0, label {{%.*}} [ +// CHECK: i32 -1, label {{%.*}} +// CHECK: i32 0, label {{%.*}} +// CHECK: i32 1, label {{%.*}} +public func objc_enum_switch(_ x: ExportedToObjC) -> Int { + switch x { + case .Foo: + return 0 + case .Bar: + return 1 + case .Bas: + return 2 + } +} + diff --git a/test/IRGen/objc_extensions.swift b/test/IRGen/objc_extensions.swift index 6d4df434ae06f..8f065b4cfa361 100644 --- a/test/IRGen/objc_extensions.swift +++ b/test/IRGen/objc_extensions.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %build-irgen-test-overlays // RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -disable-objc-attr-requires-foundation-module -emit-module %S/Inputs/objc_extension_base.swift -o %t -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -g | %FileCheck %s // REQUIRES: CPU=x86_64 // REQUIRES: objc_interop @@ -195,3 +195,52 @@ class SwiftSubGizmo : SwiftBaseGizmo { } } +@inline(never) func opaquePrint(_ value: Any) { print(value) } + +/* + * Check that we can extend ObjC generics and use both the type and metatype of + * the generic parameter. Specifically, we're checking that we emit debug info + * if we look up the existential bound, and that we derive the argument to + * `opaquePrint(_:)` from the actual parameter, not just the fixed metadata. + */ +extension FungingArray { + // CHECK-LABEL: define {{.*}} @"$sSo12FungingArrayC15objc_extensionsEyAByxGxcfC" + // CHECK-SAME: (%objc_object* %0, %swift.type* swiftself %1) + // CHECK: @__swift_instantiateConcreteTypeFromMangledName{{.*}}@"$sSo9NSFunging_XlMD"{{.*}}!dbg + + // CHECK-LABEL: define {{.*}} @"$sSo12FungingArrayC15objc_extensionsEyAByxGxcfc" + // CHECK-SAME: (%objc_object* %0, %TSo12FungingArrayC* swiftself %1) + // CHECK: [[ALLOCA:%[^, =]+]] = alloca %Any, align 8 + // CHECK: @__swift_instantiateConcreteTypeFromMangledName{{.*}}@"$sSo9NSFunging_XlMD"{{.*}}!dbg + // CHECK: {{%[^, =]+}} = getelementptr inbounds %Any, %Any* [[ALLOCA]], i32 0, i32 0 + // CHECK: [[ANYBUF:%[^, =]+]] = getelementptr inbounds %Any, %Any* [[ALLOCA]], i32 0, i32 0 + // CHECK: [[BUFPTR:%[^, =]+]] = {{.*}} [[ANYBUF]] + // CHECK: [[BUF_0:%[^, =]+]] = {{.*}} [[BUFPTR]] + // CHECK: store {{.*}} %0, {{.*}} [[BUF_0]] + // CHECK: call swiftcc void @"$s15objc_extensions11opaquePrintyyypF"(%Any* {{.*}} [[ALLOCA]]) + @objc public convenience init(_ elem: Element) { + opaquePrint(elem) + self.init() + } + + // CHECK-LABEL: define {{.*}} @"$sSo12FungingArrayC15objc_extensionsE7pinningAByxGxm_tcfC" + // CHECK-SAME: (%swift.type* %0, %swift.type* swiftself %1) + // CHECK: @__swift_instantiateConcreteTypeFromMangledName{{.*}}@"$sSo9NSFunging_XlMD"{{.*}}!dbg + + // CHECK-LABEL: define {{.*}} @"$sSo12FungingArrayC15objc_extensionsE7pinningAByxGxm_tcfc" + // CHECK-SAME: (%swift.type* %0, %TSo12FungingArrayC* swiftself %1) + // CHECK: [[ALLOCA:%[^, =]+]] = alloca %Any, align 8 + // CHECK: @__swift_instantiateConcreteTypeFromMangledName{{.*}}@"$sSo9NSFunging_XlMD"{{.*}}!dbg + // CHECK: [[OBJC_CLASS:%[^, =]+]] = call %objc_class* @swift_getObjCClassFromMetadata(%swift.type* %0) + // CHECK: [[OBJC_CLASS_OBJ:%[^, =]+]] = bitcast %objc_class* [[OBJC_CLASS]] + // CHECK: {{%[^, =]+}} = getelementptr inbounds %Any, %Any* [[ALLOCA]], i32 0, i32 0 + // CHECK: [[ANYBUF:%[^, =]+]] = getelementptr inbounds %Any, %Any* [[ALLOCA]], i32 0, i32 0 + // CHECK: [[BUFPTR:%[^, =]+]] = {{.*}} [[ANYBUF]] + // CHECK: [[BUF_0:%[^, =]+]] = {{.*}} [[BUFPTR]] + // CHECK: store {{.*}} [[OBJC_CLASS_OBJ]], {{.*}} [[BUF_0]] + // CHECK: call swiftcc void @"$s15objc_extensions11opaquePrintyyypF"(%Any* {{.*}} [[ALLOCA]]) + @objc public convenience init(pinning: Element.Type) { + opaquePrint(pinning as AnyObject) + self.init() + } +} diff --git a/test/IRGen/objc_extensions_jit.swift b/test/IRGen/objc_extensions_jit.swift new file mode 100644 index 0000000000000..44f93b4eab886 --- /dev/null +++ b/test/IRGen/objc_extensions_jit.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop %s -emit-ir -disable-objc-attr-requires-foundation-module -use-jit | %FileCheck %s + +import Foundation +import objc_generics + +extension NSString { + func fn() {} +} + +extension GenericClass { + @objc func fn() {} +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private void @runtime_registration +// CHECK-NOT: @__swift_instantiateConcreteTypeFromMangledName +// CHECK: ret void diff --git a/test/IRGen/objc_ns_enum.swift b/test/IRGen/objc_ns_enum.swift index 26ad01f27a8dc..133f4e47f9ce4 100644 --- a/test/IRGen/objc_ns_enum.swift +++ b/test/IRGen/objc_ns_enum.swift @@ -99,28 +99,6 @@ use_metadata(NSRuncingOptions.mince) case Foo = -1, Bar, Bas } -// CHECK-LABEL: define hidden swiftcc i64 @"$s12objc_ns_enum0a1_C7_injectAA14ExportedToObjCOyF"() -// CHECK: ret i64 -1 -func objc_enum_inject() -> ExportedToObjC { - return .Foo -} - -// CHECK-LABEL: define hidden swiftcc i64 @"$s12objc_ns_enum0a1_C7_switchySiAA14ExportedToObjCOF"(i64 %0) -// CHECK: switch i64 %0, label {{%.*}} [ -// CHECK: i64 -1, label {{%.*}} -// CHECK: i64 0, label {{%.*}} -// CHECK: i64 1, label {{%.*}} -func objc_enum_switch(_ x: ExportedToObjC) -> Int { - switch x { - case .Foo: - return 0 - case .Bar: - return 1 - case .Bas: - return 2 - } -} - @objc class ObjCEnumMethods : NSObject { // CHECK: define internal void @"$s12objc_ns_enum15ObjCEnumMethodsC0C2InyyAA010ExportedToD1COFTo"([[OBJC_ENUM_METHODS:.*]]* %0, i8* %1, i64 %2) @objc dynamic func enumIn(_ x: ExportedToObjC) {} diff --git a/test/IRGen/opaque_result_type_associated_type_conformance_path.swift b/test/IRGen/opaque_result_type_associated_type_conformance_path.swift index d530815c361f6..c6e938912d33a 100644 --- a/test/IRGen/opaque_result_type_associated_type_conformance_path.swift +++ b/test/IRGen/opaque_result_type_associated_type_conformance_path.swift @@ -14,7 +14,7 @@ struct Foo: P { func foo(_ x: T) -> some Tubb { return x } } -// CHECK-LABEL: define {{.*}} @"$s030opaque_result_type_associated_C17_conformance_path3FooVyxGAA1PAA1B_AA4ButtPWT" +// CHECK-LABEL: define {{.*}} @"$s030opaque_result_type_associated_C17_conformance_path3FooVyxGAA1PAA1AAaEP_AA4ButtPWT" // CHECK: [[TUBB_CONFORMANCE:%.*]] = call swiftcc i8** @swift_getOpaqueTypeConformance({{.*}}, i{{.*}} 1) // CHECK: [[BUTT_CONFORMANCE_ADDR:%.*]] = getelementptr {{.*}} [[TUBB_CONFORMANCE]], i32 1 // CHECK: [[BUTT_CONFORMANCE_LOAD:%.*]] = load {{.*}} [[BUTT_CONFORMANCE_ADDR]] diff --git a/test/IRGen/opaque_result_type_private_typemetadata.swift b/test/IRGen/opaque_result_type_private_typemetadata.swift new file mode 100644 index 0000000000000..4dccadcefc591 --- /dev/null +++ b/test/IRGen/opaque_result_type_private_typemetadata.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -primary-file %s %S/Inputs/opaque_result_type_private_typemetadata2.swift | %FileCheck %s + +// Container's fields are not ABI accessible so copying Container must use its +// metadata instead of exploding its fields. + +// CHECK: define{{.*}} swiftcc void @"$s39opaque_result_type_private_typemetadata4doItyyF"() +// CHECK-NOT: ret void +// CHECK: call {{.*}} @"$s39opaque_result_type_private_typemetadata9ContainerVMa"( +// CHECK: ret void + +public func doIt() { + var x = Container() + var y = x +} diff --git a/test/IRGen/partial_apply_run_generic_method1.sil b/test/IRGen/partial_apply_run_generic_method1.sil index d58a7bd1b1117..3e4d594ea53d6 100644 --- a/test/IRGen/partial_apply_run_generic_method1.sil +++ b/test/IRGen/partial_apply_run_generic_method1.sil @@ -61,7 +61,7 @@ sil hidden [ossa] @generic_function : $@convention(method) (@in_guaranteed T bb0(%0 : $*T, %1 : $*T, %2 : @guaranteed $C): - debug_value_addr %1 : $*T, let, name "t", argno 1 + debug_value %1 : $*T, let, name "t", argno 1 , expr op_deref debug_value %2 : $C, let, name "self", argno 2 copy_addr %1 to [initialization] %0 : $*T %6 = tuple () diff --git a/test/IRGen/prespecialized-metadata/enum-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/enum-inmodule-1argument-1distinct_use.swift index 2828be76bfd98..72586cfcf6fb2 100644 --- a/test/IRGen/prespecialized-metadata/enum-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/enum-inmodule-1argument-1distinct_use.swift @@ -6,21 +6,21 @@ // UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$s4main5ValueOySiGWV" = linkonce_odr hidden constant %swift.enum_vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|[^@]+@__swift_memcpy[0-9]+_[0-9]+[^\)]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_noop_void_return{{[^\)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOySiGwet{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOySiGwst{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwCP{{[^\)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwxx{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwcp{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwca{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwtk{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwta{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwet{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwst{{[^)]+}} to i8*), // CHECK-SAME: [[INT]] [[ALIGNMENT]], // CHECK-SAME: [[INT]] [[ALIGNMENT]], // CHECK-SAME: i32 {{[0-9]+}}, // CHECK-SAME: i32 0, -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOySiGwug{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOySiGwup{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOySiGwui{{[^)]+}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwug{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwup{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwui{{[^)]+}} to i8*) // CHECK-SAME: }, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueOySiGMf" = linkonce_odr hidden constant <{ // CHECK-SAME: i8**, diff --git a/test/IRGen/prespecialized-metadata/enum-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/enum-inmodule-2argument-1distinct_use.swift index 521d05a20a7e2..510e59106a0cc 100644 --- a/test/IRGen/prespecialized-metadata/enum-inmodule-2argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/enum-inmodule-2argument-1distinct_use.swift @@ -5,39 +5,39 @@ // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios -// CHECK: @"$s4main5ValueOyS2iGWV" = linkonce_odr hidden constant %swift.enum_vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|[^@]+@__swift_memcpy[0-9]+_[0-9]+[^\)]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_noop_void_return{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS2iGwet{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS2iGwst{{[^)]+}} to i8*), -// CHECK-SAME: [[INT]] {{[0-9]+}}, -// CHECK-SAME: [[INT]] {{[0-9]+}}, -// CHECK-SAME: i32 {{[0-9]+}}, -// CHECK-SAME: i32 {{[0-9]+}}, -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS2iGwug{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS2iGwup{{[^)]+}} to i8*), -// CHECK-SAME i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS2iGwui{{[^)]+}} to i8*) +// CHECK: @"$s4main5ValueOyS2iGWV" = linkonce_odr hidden constant %swift.enum_vwtable { +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwCP{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwxx{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwcp{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwca{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwtk{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwta{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@swift_getMultiPayloadEnumTagSinglePayload{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@swift_storeMultiPayloadEnumTagSinglePayload{{[^)]*}} to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwug{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwup{{[^)]+}} to i8*), +// CHECK-SAME i8* bitcast ({{[^@]+}}@"$s4main5ValueOwui{{[^)]+}} to i8*) // CHECK-SAME: }, align [[ALIGNMENT]] -// CHECK: @"$s4main5ValueOyS2iGMf" = linkonce_odr hidden constant <{ -// CHECK-SAME: i8**, -// CHECK-SAME: [[INT]], -// CHECK-SAME: %swift.type_descriptor*, -// CHECK-SAME: %swift.type*, -// CHECK-SAME: i64 -// CHECK-SAME: }> <{ -// CHECK-SAME: i8** getelementptr inbounds (%swift.enum_vwtable, %swift.enum_vwtable* @"$s4main5ValueOyS2iGWV", i32 0, i32 0), -// CHECK-SAME: [[INT]] 513, +// CHECK: @"$s4main5ValueOyS2iGMf" = linkonce_odr hidden constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// CHECK-SAME: i8** getelementptr inbounds (%swift.enum_vwtable, %swift.enum_vwtable* @"$s4main5ValueOyS2iGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 513, // CHECK-SAME: %swift.type_descriptor* bitcast ( // CHECK-SAME: {{.*}}$s4main5ValueOMn{{.*}} to %swift.type_descriptor* -// CHECK-SAME: ), -// CHECK-SAME: %swift.type* @"$sSiN", -// CHECK-SAME: %swift.type* @"$sSiN", -// CHECK-SAME: [[INT]] {{16|8}}, -// CHECK-SAME: i64 3 +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: [[INT]] {{16|8}}, +// CHECK-SAME: i64 3 // CHECK-SAME: }>, align [[ALIGNMENT]] enum Value { case first(First) diff --git a/test/IRGen/prespecialized-metadata/enum-inmodule-3argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/enum-inmodule-3argument-1distinct_use.swift index 2766f04c79e80..c5491b7af64b2 100644 --- a/test/IRGen/prespecialized-metadata/enum-inmodule-3argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/enum-inmodule-3argument-1distinct_use.swift @@ -5,22 +5,22 @@ // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios -// CHECK: @"$s4main5ValueOyS3iGWV" = linkonce_odr hidden constant %swift.enum_vwtable { -// CHECK-SAME: i8* bitcast ({{([^@]+@"\$s4main5ValueOyS3iGwCP[^\)]+ to i8\*|[^@]+@__swift_memcpy[0-9]+_[0-9]+[^\)]+ to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_noop_void_return{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS3iGwet{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS3iGwst{{[^)]+}} to i8*), +// CHECK: @"$s4main5ValueOyS3iGWV" = linkonce_odr hidden constant %swift.enum_vwtable { +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwCP{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwxx{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwcp{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwca{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwtk{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwta{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@swift_getMultiPayloadEnumTagSinglePayload{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@swift_storeMultiPayloadEnumTagSinglePayload{{[^)]*}} to i8*), // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS3iGwug{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS3iGwup{{[^)]+}} to i8*), -// CHECK-SAME i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS3iGwui{{[^)]+}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwug{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwup{{[^)]+}} to i8*), +// CHECK-SAME i8* bitcast ({{[^@]+}}@"$s4main5ValueOwui{{[^)]+}} to i8*), // CHECK-SAME: }, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueOyS3iGMf" = linkonce_odr hidden constant <{ // CHECK-SAME: i8**, diff --git a/test/IRGen/prespecialized-metadata/enum-inmodule-4argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/enum-inmodule-4argument-1distinct_use.swift index 7ffd336e32aa6..a6f7349a34577 100644 --- a/test/IRGen/prespecialized-metadata/enum-inmodule-4argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/enum-inmodule-4argument-1distinct_use.swift @@ -6,21 +6,21 @@ // UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$s4main5ValueOyS4iGWV" = linkonce_odr hidden constant %swift.enum_vwtable { -// CHECK-SAME: i8* bitcast ({{([^@]+@"\$s4main5ValueOyS4iGwCP+[^\)]+ to i8\*|[^@]+@__swift_memcpy[0-9]+_[0-9]+[^\)]+ to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_noop_void_return{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS4iGwet{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS4iGwst{{[^)]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwCP{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@swift_getMultiPayloadEnumTagSinglePayload{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@swift_storeMultiPayloadEnumTagSinglePayload{{[^)]*}} to i8*), // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS4iGwug{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS4iGwup{{[^)]+}} to i8*), -// CHECK-SAME i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS4iGwui{{[^)]+}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwug{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwup{{[^)]*}} to i8*) +// CHECK-SAME i8* bitcast ({{[^@]+}}@"$s4main5ValueOwui{{[^)]*}} to i8*) // CHECK-SAME: }, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueOyS4iGMf" = linkonce_odr hidden constant <{ // CHECK-SAME: i8**, diff --git a/test/IRGen/prespecialized-metadata/enum-inmodule-5argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/enum-inmodule-5argument-1distinct_use.swift index a2d50a902fd7a..76fe53bd65076 100644 --- a/test/IRGen/prespecialized-metadata/enum-inmodule-5argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/enum-inmodule-5argument-1distinct_use.swift @@ -5,38 +5,38 @@ // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios -// CHECK: @"$s4main5ValueOyS5iGWV" = linkonce_odr hidden constant %swift.enum_vwtable { -// CHECK-SAME: i8* bitcast ({{([^@]+@"\$s4main5ValueOyS5iGwCP+[^\)]+ to i8\*|[^@]+@__swift_memcpy[0-9]+_[0-9]+[^)]+ to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_noop_void_return{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[0-9]+}}_{{[0-9]+}}{{[^)]*}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS5iGwet{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS5iGwst{{[^)]+}} to i8*), -// CHECK-SAME: [[INT]] {{[0-9]+}}, -// CHECK-SAME: [[INT]] {{[0-9]+}}, -// CHECK-SAME: i32 {{[0-9]+}}, -// CHECK-SAME: i32 {{[0-9]+}}, -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS5iGwug{{[^)]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS5iGwup{{[^)]+}} to i8*), -// CHECK-SAME i8* bitcast ({{[^@]+}}@"$s4main5ValueOyS5iGwui{{[^)]+}} to i8*) +// CHECK: @"$s4main5ValueOyS5iGWV" = linkonce_odr hidden constant %swift.enum_vwtable { +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@swift_getMultiPayloadEnumTagSinglePayload{{[^)]*}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@swift_storeMultiPayloadEnumTagSinglePayload{{[^)]*}} to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwug{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueOwup{{[^)]*}} to i8*) +// CHECK-SAME i8* bitcast ({{[^@]+}}@"$s4main5ValueOwui{{[^)]*}} to i8*) // CHECK-SAME: }, align [[ALIGNMENT]] -// CHECK: @"$s4main5ValueOyS5iGMf" = linkonce_odr hidden constant <{ -// CHECK-SAME: i8**, -// CHECK-SAME: [[INT]], -// CHECK-SAME: %swift.type_descriptor*, -// CHECK-SAME: %swift.type*, -// CHECK-SAME: i64 -// CHECK-SAME: }> <{ -// CHECK-SAME: i8** getelementptr inbounds (%swift.enum_vwtable, %swift.enum_vwtable* @"$s4main5ValueOyS5iGWV", i32 0, i32 0), -// CHECK-SAME: [[INT]] 513, +// CHECK: @"$s4main5ValueOyS5iGMf" = linkonce_odr hidden constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// CHECK-SAME: i8** getelementptr inbounds (%swift.enum_vwtable, %swift.enum_vwtable* @"$s4main5ValueOyS5iGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 513, // CHECK-SAME: %swift.type_descriptor* bitcast ( // CHECK-SAME: {{.*}}$s4main5ValueOMn{{.*}} to %swift.type_descriptor* -// CHECK-SAME: ), -// CHECK-SAME: %swift.type* @"$sSiN", -// CHECK-SAME: [[INT]] {{40|20}}, -// CHECK-SAME: i64 3 +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: [[INT]] {{40|20}}, +// CHECK-SAME: i64 3 // CHECK-SAME: }>, align [[ALIGNMENT]] enum Value { case first(First) diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift index 6801326a44ee3..bd3fcaf63b0a3 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift @@ -6,14 +6,14 @@ // UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$s4main5ValueVySSGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSGwCP{{[^@]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSGwxx{{[^@]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSGwcp{{[^@]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSGwca{{[^@]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSGwta{{[^@]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSGwet{{[^@]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSGwst{{[^@]* to i8\*}}), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift index 368ffb0dff8d7..68772b9511475 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift @@ -41,14 +41,14 @@ // CHECK-SAME: }>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVySSGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{.+}}$s4main5ValueVySSGwCP{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSGwxx{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSGwcp{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSGwca{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSGwta{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSGwet{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSGwst{{[^[:space:]]+ to i8\*}}), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift index c9ae70a18ed77..1b7f8a3b22eb3 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift @@ -6,14 +6,14 @@ // UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*|{ i8\*, i32, i64, i64 }\* @__swift_memcpy[0-9]+_[0-9]+.ptrauth to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVyS2iGwet{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVyS2iGwst{{[^[:space:]]* to i8\*}}), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift index 4af71c508c0b9..fb6815229f953 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift @@ -6,14 +6,14 @@ // UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|[^@]+@__swift_memcpy[^[:space:]]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySdSiGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySdSiGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -25,14 +25,14 @@ // CHECK: @"$s4main5ValueVySdSiGMf" = linkonce_odr hidden constant {{.+}}$s4main5ValueVMn{{.+}} to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|[^@]+@__swift_memcpy[^[:space:]]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVyS2iGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVyS2iGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift index 84278107267ea..d4b3affcb04a8 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift @@ -6,14 +6,14 @@ // UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwCP{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwxx{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwcp{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwca{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwta{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -25,14 +25,14 @@ // CHECK: @"$s4main5ValueVySSSdGMf" = linkonce_odr hidden constant {{.+}}$s4main5ValueVMn{{.+}} to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|[^@]+@__swift_memcpy[^[:space:]]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVy{{[^[:space:]]* to i8\*}}), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -44,14 +44,14 @@ // CHECK: @"$s4main5ValueVySdSiGMf" = linkonce_odr hidden constant {{.+}}$s4main5ValueVMn{{.+}} to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|[^@]+@__swift_memcpy[^[:space:]]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVyS2iGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVyS2iGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAMEL [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift index 9863581aa3c84..aa83763989b4b 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift @@ -6,14 +6,14 @@ // UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$s4main5ValueVys5UInt8VSSGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVys5UInt8VSSGwCP{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVys5UInt8VSSGwxx{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVys5UInt8VSSGwcp{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVys5UInt8VSSGwca{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVys5UInt8VSSGwta{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVys5UInt8VSSGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVys5UInt8VSSGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -25,14 +25,14 @@ // CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = linkonce_odr hidden constant {{.+}}$s4main5ValueVMn{{.+}} to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwCP{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwxx{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwcp{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwca{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwta{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySSSdGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -44,14 +44,14 @@ // CHECK: @"$s4main5ValueVySSSdGMf" = linkonce_odr hidden constant {{.+}}$s4main5ValueVMn{{.+}} to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|[^@]+@__swift_memcpy[^[:space:]]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySdSiGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVySdSiGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -63,14 +63,14 @@ // CHECK: @"$s4main5ValueVySdSiGMf" = linkonce_odr hidden constant {{.+}}$s4main5ValueVMn{{.+}} to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|[^@]+@__swift_memcpy[^[:space:]]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVyS2iGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVyS2iGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift index 4da7bf527af80..723f806e9d0f4 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift @@ -5,15 +5,53 @@ // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios +// CHECK: @"$s4main5ValueVys4Int8Vs5UInt8VGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVys4Int8Vs5UInt8VGMf" = linkonce_odr hidden constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32, +// CHECK-SAME: i32, +// CHECK-SAME: i64 +// CHECK-SAME:}> <{ +// CHECK-SAME: i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys4Int8Vs5UInt8VGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast ({{.+}}$s4main5ValueVMn{{.+}} to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$ss4Int8VN", +// CHECK-SAME: %swift.type* @"$ss5UInt8VN", +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1, +// CHECK-SAME: i64 3 +// CHECK-SAME:}>, align [[ALIGNMENT]] + + // CHECK: @"$s4main5ValueVys5UInt8VSSGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVys5UInt8VSSGwCP{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVys5UInt8VSSGwxx{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVys5UInt8VSSGwcp{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVys5UInt8VSSGwca{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVys5UInt8VSSGwta{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVys5UInt8VSSGwet{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVys5UInt8VSSGwst{{[^[:space:]]* to i8\*}}), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -43,14 +81,14 @@ // CHECK-SAME:}>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSSdGwCP{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSSdGwxx{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSSdGwcp{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSSdGwca{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSSdGwta{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSSdGwet{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySSSdGwst{{[^[:space:]]* to i8\*}}), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -80,14 +118,14 @@ // CHECK-SAME:}>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|[^@]*@__swift_memcpy[^@]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK_SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySdSiGwet{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVySdSiGwst{{[^[:space:]]* to i8\*}}), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, @@ -117,14 +155,14 @@ // CHECK-SAME:}>, align [[ALIGNMENT]] // CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|[^@]*@__swift_memcpy[^@]* to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_memcpy{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVyS2iGwet{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@"$s4main5ValueVyS2iGwst{{[^[:space:]]* to i8\*}}), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift index 3296707dca40b..1556dc37eab8e 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift @@ -6,14 +6,14 @@ // UNSUPPORTED: CPU=armv7s && OS=ios // CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGWV" = linkonce_odr hidden constant %swift.vwtable { -// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|[^@]+@__swift_memcpy[^[:space:]]+ to i8\*)}}), -// CHECK-SAME: i8* bitcast ({{[^@]*}}@__swift_noop_void_return{{[^[:space:]]* to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@__swift_memcpy{{[^[:space:]]+ to i8\*}}), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVySS_SiSdGwet{{[^@]+}} to i8*), -// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVySS_SiSdGwst{{[^@]+}} to i8*), +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVwCP{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVwxx{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVwcp{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVwca{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVwtk{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVwta{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVwet{{[^)]*}} to i8*) +// CHECK-SAME: i8* bitcast ({{[^@]+}}@"$s4main9NamespaceC5ValueVwst{{[^)]*}} to i8*) // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: [[INT]] {{[0-9]+}}, // CHECK-SAME: i32 {{[0-9]+}}, diff --git a/test/IRGen/protocol_extensions.sil b/test/IRGen/protocol_extensions.sil index 0433471d7a0dd..1315d4dcf32a9 100644 --- a/test/IRGen/protocol_extensions.sil +++ b/test/IRGen/protocol_extensions.sil @@ -16,7 +16,7 @@ extension P1 { // CHECK-LABEL: define hidden swiftcc void @_TFP19protocol_extensions2P16extP1aUS0___fQPS0_FT_T_ sil hidden @_TFP19protocol_extensions2P16extP1aUS0___fQPS0_FT_T_ : $@convention(method) (@in Self) -> () { bb0(%0 : $*Self): - debug_value_addr %0 : $*Self, let, name "self" // id: %1 + debug_value %0 : $*Self, let, name "self", expr op_deref // id: %1 %2 = witness_method $Self, #P1.reqP1a : $@convention(witness_method: P1) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> () // user: %3 %3 = apply %2(%0) : $@convention(witness_method: P1) <τ_0_0 where τ_0_0 : P1> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*Self // id: %4 @@ -27,7 +27,7 @@ bb0(%0 : $*Self): // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @_TFP19protocol_extensions2P16extP1bUS0___fQPS0_FT_T_ sil @_TFP19protocol_extensions2P16extP1bUS0___fQPS0_FT_T_ : $@convention(method) (@in Self) -> () { bb0(%0 : $*Self): - debug_value_addr %0 : $*Self, let, name "self" // id: %1 + debug_value %0 : $*Self, let, name "self", expr op_deref // id: %1 // function_ref protocol_extensions.P1.extP1a (protocol_extensions.P1.Self)() -> () %2 = function_ref @_TFP19protocol_extensions2P16extP1aUS0___fQPS0_FT_T_ : $@convention(method) <τ_0_0 where τ_0_0 : P1> (@in τ_0_0) -> () // user: %5 %3 = alloc_stack $Self // users: %4, %5, %6 diff --git a/test/IRGen/retroactive_conformance_path.swift b/test/IRGen/retroactive_conformance_path.swift index 3c596cc1f08a5..1fc7f0bd223b2 100644 --- a/test/IRGen/retroactive_conformance_path.swift +++ b/test/IRGen/retroactive_conformance_path.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -module-name=test %s -o %t/a.out +// RUN: %target-build-swift -module-name=test %s -o %t/a.out -requirement-machine=verify // RUN: %target-run %t/a.out | %FileCheck %s // REQUIRES: executable_test // REQUIRES: CPU=arm64 || CPU=x86_64 diff --git a/test/IRGen/retroactive_conformance_path_2.swift b/test/IRGen/retroactive_conformance_path_2.swift new file mode 100644 index 0000000000000..44ef7f846278f --- /dev/null +++ b/test/IRGen/retroactive_conformance_path_2.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-frontend -emit-ir -g -primary-file %s -requirement-machine=verify + +public struct TestType { } + +extension Array: Error where Element: Error { } + +public struct GenericType { + public func test(_ value: TestType<[T]>) { } +} diff --git a/test/IRGen/same_type_constraints.swift b/test/IRGen/same_type_constraints.swift index 17ba3f5161332..9c80850adf806 100644 --- a/test/IRGen/same_type_constraints.swift +++ b/test/IRGen/same_type_constraints.swift @@ -67,7 +67,7 @@ where Self : CodingType, print(Self.ValueType.self) } -// OSIZE: define internal swiftcc i8** @"$s21same_type_constraints12GenericKlazzCyxq_GAA1EAA4Data_AA0F4TypePWT"(%swift.type* readnone %"GenericKlazz.Data", %swift.type* nocapture readonly %"GenericKlazz", i8** nocapture readnone %"GenericKlazz.E") [[ATTRS:#[0-9]+]] { +// OSIZE: define internal swiftcc i8** @"$s21same_type_constraints12GenericKlazzCyxq_GAA1EAA4DataAaEP_AA0F4TypePWT"(%swift.type* readnone %"GenericKlazz.Data", %swift.type* nocapture readonly %"GenericKlazz", i8** nocapture readnone %"GenericKlazz.E") [[ATTRS:#[0-9]+]] { // OSIZE: [[ATTRS]] = {{{.*}}noinline // Check that same-typing two generic parameters together lowers correctly. diff --git a/test/IRGen/struct_resilience.swift b/test/IRGen/struct_resilience.swift index f9402692d497f..40c4400a255e4 100644 --- a/test/IRGen/struct_resilience.swift +++ b/test/IRGen/struct_resilience.swift @@ -63,7 +63,7 @@ public func functionWithResilientTypesSize(_ s: __owned Size, f: (__owned Size) // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s17struct_resilience35functionWithResilientTypesRectangleyy010resilient_A00G0VF"(%T16resilient_struct9RectangleV* noalias nocapture %0) public func functionWithResilientTypesRectangle(_ r: Rectangle) { // CHECK: entry: -// CHECK-NEXT: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s16resilient_struct9RectangleVMa"([[INT]] 0) +// CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s16resilient_struct9RectangleVMa"([[INT]] 0) // CHECK-NEXT: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i32* // CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds i32, i32* [[METADATA_ADDR]], [[INT]] [[IDX:2|4|6]] @@ -202,7 +202,7 @@ public func resilientAny(s : ResilientWeakRef) { // CHECK: entry: // CHECK: [[ANY:%.*]] = alloca %Any // CHECK: [[META:%.*]] = call swiftcc %swift.metadata_response @"$s16resilient_struct16ResilientWeakRefVMa"([[INT]] 0) -// CHECK: [[META2:%.*]] = extractvalue %swift.metadata_response %3, 0 +// CHECK: [[META2:%.*]] = extractvalue %swift.metadata_response [[META]], 0 // CHECK: [[TYADDR:%.*]] = getelementptr inbounds %Any, %Any* [[ANY]], i32 0, i32 1 // CHECK: store %swift.type* [[META2]], %swift.type** [[TYADDR]] // CHECK: [[BITCAST:%.*]] = bitcast %Any* [[ANY]] to %__opaque_existential_type_0* diff --git a/test/IRGen/unused.sil b/test/IRGen/unused.sil index 8b0d06fb0e7a3..52f0d14711662 100644 --- a/test/IRGen/unused.sil +++ b/test/IRGen/unused.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version none -primary-file %s -emit-ir | %FileCheck -check-prefix CHECK -check-prefix NEGATIVE -check-prefix CHECK-%target-object-format %s +// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-concurrency -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version none -primary-file %s -emit-ir | %FileCheck -check-prefix CHECK -check-prefix NEGATIVE -check-prefix CHECK-%target-object-format %s // REQUIRES: CPU=x86_64 @@ -55,8 +55,8 @@ bb0(%0 : $Int32, %1 : $UnsafeMutablePointer> // CHECK-macho: @"\01l_entry_point" = private constant { i32 } { i32 trunc (i64 sub (i64 ptrtoint (i32 (i32, i8**)* @main to i64), i64 ptrtoint ({ i32 }* @"\01l_entry_point" to i64)) to i32) }, section "__TEXT, __swift5_entry, regular, no_dead_strip", align 4 // CHECK-elf: @"\01l_entry_point" = private constant { i32 } { i32 trunc (i64 sub (i64 ptrtoint (i32 (i32, i8**)* @main to i64), i64 ptrtoint ({ i32 }* @"\01l_entry_point" to i64)) to i32) }, section "swift5_entry", align 4 -// CHECK-macho: @llvm.used = appending global [4 x i8*] [i8* bitcast (void ()* @frieda to i8*), i8* bitcast (i32 (i32, i8**)* @main to i8*), i8* bitcast ({ i32 }* @"\01l_entry_point" to i8*), i8* bitcast (i16* @__swift_reflection_version to i8*)], section "llvm.metadata", align 8 -// CHECK-elf: @llvm.used = appending global [5 x i8*] [i8* bitcast (void ()* @frieda to i8*), i8* bitcast (i32 (i32, i8**)* @main to i8*), i8* bitcast ({ i32 }* @"\01l_entry_point" to i8*), i8* bitcast (i16* @__swift_reflection_version to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @_swift1_autolink_entries, i32 0, i32 0)], section "llvm.metadata", align 8 +// CHECK-macho: @llvm.used = appending global [4 x i8*] [i8* bitcast (void ()* @frieda to i8*), i8* bitcast (i32 (i32, i8**)* @main to i8*), i8* bitcast ({ i32 }* @"\01l_entry_point" to i8*), i8* bitcast (i16* @__swift_reflection_version to i8*)], section "llvm.metadata" +// CHECK-elf: @llvm.used = appending global [5 x i8*] [i8* bitcast (void ()* @frieda to i8*), i8* bitcast (i32 (i32, i8**)* @main to i8*), i8* bitcast ({ i32 }* @"\01l_entry_point" to i8*), i8* bitcast (i16* @__swift_reflection_version to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @_swift1_autolink_entries, i32 0, i32 0)], section "llvm.metadata" // CHECK: define linkonce_odr hidden swiftcc void @qux() // CHECK: define hidden swiftcc void @fred() diff --git a/test/IRGen/weak_availability_opaque_result_type.swift b/test/IRGen/weak_availability_opaque_result_type.swift new file mode 100644 index 0000000000000..2c068d6719115 --- /dev/null +++ b/test/IRGen/weak_availability_opaque_result_type.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/weak_availability_opaque_result_type_helper.swiftmodule -parse-as-library %S/Inputs/weak_availability_opaque_result_type_helper.swift -enable-library-evolution +// RUN: %target-swift-frontend -disable-type-layout -primary-file %s -I %t -emit-ir | %FileCheck %s + +// REQUIRES: OS=macosx + +import weak_availability_opaque_result_type_helper + +func useWeakImportedOpaqueResultType(_ p: T) { + if #available(macOS 100, *) { + p.someAPI().blah() + } +} + +// CHECK-LABEL: @"$s43weak_availability_opaque_result_type_helper1PPAAE7someAPIQryFQOMQ" = extern_weak global %swift.type_descriptor +// CHECK-LABEL: declare extern_weak {{.+}} void @"$s43weak_availability_opaque_result_type_helper1PPAAE7someAPIQryF" diff --git a/test/IRGen/weak_import_opaque_result_type.swift b/test/IRGen/weak_import_opaque_result_type.swift new file mode 100644 index 0000000000000..f333a8997461b --- /dev/null +++ b/test/IRGen/weak_import_opaque_result_type.swift @@ -0,0 +1,14 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/weak_import_opaque_result_type_helper.swiftmodule -parse-as-library %S/Inputs/weak_import_opaque_result_type_helper.swift -enable-library-evolution -disable-availability-checking +// RUN: %target-swift-frontend -disable-type-layout -disable-availability-checking -primary-file %s -I %t -emit-ir | %FileCheck %s + +// UNSUPPORTED: OS=windows-msvc + +import weak_import_opaque_result_type_helper + +func useWeakImportedOpaqueResultType(_ p: T) { + p.someAPI().blah() +} + +// CHECK-LABEL: @"$s37weak_import_opaque_result_type_helper1PPAAE7someAPIQryFQOMQ" = extern_weak global %swift.type_descriptor +// CHECK-LABEL: declare extern_weak {{.+}} void @"$s37weak_import_opaque_result_type_helper1PPAAE7someAPIQryF" diff --git a/test/ImportResolution/disable-implicit-concurrency-module-import.swift b/test/ImportResolution/disable-implicit-concurrency-module-import.swift index ea386c6c25f02..0d39307f3953b 100644 --- a/test/ImportResolution/disable-implicit-concurrency-module-import.swift +++ b/test/ImportResolution/disable-implicit-concurrency-module-import.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -disable-implicit-concurrency-module-import +// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import // REQUIRES: concurrency diff --git a/test/Incremental/Inputs/autolinking/AutolinkingTest.framework/AutolinkingTest b/test/Incremental/Inputs/autolinking/AutolinkingTest.framework/AutolinkingTest new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/Incremental/Inputs/autolinking/AutolinkingTest.framework/Headers/AutolinkingTest.h b/test/Incremental/Inputs/autolinking/AutolinkingTest.framework/Headers/AutolinkingTest.h new file mode 100644 index 0000000000000..aa2153b3bb514 --- /dev/null +++ b/test/Incremental/Inputs/autolinking/AutolinkingTest.framework/Headers/AutolinkingTest.h @@ -0,0 +1,3 @@ +struct Test { + int value; +}; diff --git a/test/Incremental/Inputs/autolinking/AutolinkingTest.framework/Modules/module.modulemap b/test/Incremental/Inputs/autolinking/AutolinkingTest.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..ad5b042c590b0 --- /dev/null +++ b/test/Incremental/Inputs/autolinking/AutolinkingTest.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module AutolinkingTest { + umbrella header "AutolinkingTest.h" + export * + module * { export * } +} diff --git a/test/Incremental/Inputs/autolinking/autolinking-other.swift b/test/Incremental/Inputs/autolinking/autolinking-other.swift new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/test/Incremental/Inputs/autolinking/autolinking-other.swift @@ -0,0 +1 @@ + diff --git a/test/Incremental/Inputs/autolinking/ofm.json b/test/Incremental/Inputs/autolinking/ofm.json new file mode 100644 index 0000000000000..fd598466a11c0 --- /dev/null +++ b/test/Incremental/Inputs/autolinking/ofm.json @@ -0,0 +1,18 @@ +{ + "autolinking.swift": { + "object": "./autolinking.o", + "swift-dependencies": "./autolinking.swiftdeps" + }, + "autolinking-overlay.swift": { + "object": "./autolinking-overlay.o", + "swift-dependencies": "./autolinking-overlay.swiftdeps" + }, + "autolinking-other.swift": { + "object": "./autolinking-other.o", + "swift-dependencies": "./autolinking-other.swiftdeps" + }, + "": { + "swift-dependencies": "./autolinking~buildrecord.swiftdeps" + } +} + diff --git a/test/Incremental/Verifier/single-file-private/AnyObject.swift b/test/Incremental/Verifier/single-file-private/AnyObject.swift index dc761650082ec..7c8a8f94145c5 100644 --- a/test/Incremental/Verifier/single-file-private/AnyObject.swift +++ b/test/Incremental/Verifier/single-file-private/AnyObject.swift @@ -8,22 +8,41 @@ // REQUIRES: objc_interop // RUN: %empty-directory(%t) -// RUN: %{python} %S/../gen-output-file-map.py -o %t %S -// RUN: cd %t && %target-swiftc_driver -typecheck -output-file-map %t/output.json -incremental -module-name main -verify-incremental-dependencies %s +// RUN: cd %t && %target-swift-frontend(mock-sdk: %clang-importer-sdk) -c -module-name main -verify-incremental-dependencies -primary-file %s -o /dev/null import Foundation // expected-provides {{LookupFactory}} // expected-provides {{NSObject}} +// expected-provides {{Selector}} +// expected-provides {{Bool}} +// expected-provides {{==}} +// expected-provides {{Equatable}} +// expected-provides {{Hasher}} +// expected-provides {{_ObjectiveCBridgeable}} +// expected-provides{{Hashable}} +// expected-member {{ObjectiveC.NSObject.NSObject}} // expected-superclass {{ObjectiveC.NSObject}} // expected-conformance {{ObjectiveC.NSObjectProtocol}} -// expected-conformance {{Foundation._KeyValueCodingAndObserving}} -// expected-conformance {{Foundation._KeyValueCodingAndObservingPublishing}} +// expected-member {{ObjectiveC.NSObjectProtocol.NSObject}} +// expected-member {{ObjectiveC.NSObject.Bool}} // expected-conformance {{Swift.Hashable}} // expected-conformance {{Swift.Equatable}} // expected-conformance {{Swift.CustomDebugStringConvertible}} -// expected-conformance {{Swift.CVarArg}} // expected-conformance {{Swift.CustomStringConvertible}} +// expected-conformance {{Swift.LosslessStringConvertible}} +// expected-conformance {{Swift.Sequence}} +// expected-conformance {{Swift.Comparable}} +// expected-conformance {{Swift.ExpressibleByArrayLiteral}} +// expected-conformance {{Swift.TextOutputStream}} +// expected-conformance {{Swift.Encodable}} +// expected-conformance {{Swift.Decodable}} +// expected-conformance {{Swift.SIMDStorage}} +// expected-conformance {{Swift.TextOutputStreamable}} +// expected-conformance {{Swift.AdditiveArithmetic}} +// expected-conformance {{Swift.ExpressibleByStringInterpolation}} +// expected-conformance {{Swift.ExpressibleByExtendedGraphemeClusterLiteral}} +// expected-conformance {{Swift.SignedNumeric}} // expected-member {{Swift._ExpressibleByBuiltinIntegerLiteral.init}} @objc private class LookupFactory: NSObject { // expected-provides {{AssignmentPrecedence}} @@ -41,7 +60,9 @@ import Foundation @objc func someMethod() {} // expected-member {{ObjectiveC.NSObject.init}} + // expected-member {{ObjectiveC.NSObject.deinit}} // expected-member {{ObjectiveC.NSObjectProtocol.init}} + // expected-member {{ObjectiveC.NSObjectProtocol.deinit}} // expected-member {{main.LookupFactory.init}} // expected-member {{main.LookupFactory.deinit}} // expected-member {{main.LookupFactory.someMember}} @@ -57,7 +78,39 @@ import Foundation // expected-member {{Swift.Hashable.callAsFunction}} // expected-member {{Swift.Encodable.callAsFunction}} // expected-member {{Swift.Decodable.callAsFunction}} -// expected-member {{Foundation._OptionalForKVO.callAsFunction}} + +// expected-member {{Swift.Hashable._rawHashValue}} +// expected-member {{ObjectiveC.NSObject.hash}} +// expected-member {{Swift.Equatable.hashValue}} +// expected-member{{Swift.Hashable.hashValue}} +// expected-member{{Swift.Hashable.hash}} +// expected-member{{ObjectiveC.NSObjectProtocol.==}} +// expected-member {{ObjectiveC.NSObjectProtocol.hashValue}} +// expected-member {{ObjectiveC.NSObjectProtocol.Hasher}} +// expected-member {{Swift.Equatable._rawHashValue}} +// expected-member {{ObjectiveC.NSObject.hashValue}} +// expected-member {{ObjectiveC.NSObjectProtocol.Bool}} +// expected-member {{ObjectiveC.NSObject.==}} +// expected-member {{Swift.Equatable.==}} +// expected-member {{ObjectiveC.NSObject.Hasher}} +// expected-member {{ObjectiveC.NSObjectProtocol.hash}} + +// expected-member {{Swift.Hashable.deinit}} +// expected-member {{Swift.Equatable.deinit}} + +// expected-conformance {{Swift.Strideable}} +// expected-conformance {{Swift.Collection}} +// expected-conformance {{Swift.BidirectionalCollection}} +// expected-conformance {{Swift.ExpressibleByUnicodeScalarLiteral}} +// expected-conformance {{Swift.ExpressibleByStringLiteral}} +// expected-conformance {{Swift.CustomReflectable}} +// expected-conformance {{Swift.ExpressibleByIntegerLiteral}} +// expected-conformance {{Swift.Numeric}} + +// expected-member {{Swift.Hashable.==}} +// expected-member {{Swift.Equatable.hash}} +// expected-member {{ObjectiveC.NSObject._rawHashValue}} +// expected-member {{ObjectiveC.NSObjectProtocol._rawHashValue}} // expected-provides {{AnyObject}} func lookupOnAnyObject(object: AnyObject) { // expected-provides {{lookupOnAnyObject}} @@ -66,17 +119,13 @@ func lookupOnAnyObject(object: AnyObject) { // expected-provides {{lookupOnAnyOb } // expected-member {{Swift.Hashable.someMethod}} -// expected-member {{Foundation._KeyValueCodingAndObserving.someMethod}} -// expected-member {{Foundation._KeyValueCodingAndObservingPublishing.someMethod}} // expected-member {{Swift.Equatable.someMethod}} -// expected-member {{Swift.CVarArg.someMethod}} -// expected-member {{Swift.CustomStringConvertible.someMethod}} -// expected-member {{Swift.CustomDebugStringConvertible.someMethod}} // expected-member {{Swift.Equatable.someMember}} -// expected-member{{Swift.CVarArg.someMember}} -// expected-member{{Foundation._KeyValueCodingAndObservingPublishing.someMember}} -// expected-member{{Foundation._KeyValueCodingAndObserving.someMember}} -// expected-member{{Swift.CustomDebugStringConvertible.someMember}} -// expected-member{{Swift.CustomStringConvertible.someMember}} -// expected-member{{Swift.Hashable.someMember}} -// expected-member{{Swift.Sendable.callAsFunction}} +// expected-member {{Swift.Hashable.someMember}} +// expected-member {{Swift.Sendable.callAsFunction}} +// expected-member {{ObjectiveC.NSObject.someMethodWithDeprecatedOptions}} +// expected-member {{ObjectiveC.NSObject.someMethodWithPotentiallyUnavailableOptions}} +// expected-member {{ObjectiveC.NSObject.someMethodWithUnavailableOptions}} +// expected-member {{ObjectiveC.NSObjectProtocol.someMethodWithUnavailableOptions}} +// expected-member {{ObjectiveC.NSObjectProtocol.someMethodWithPotentiallyUnavailableOptions}} +// expected-member {{ObjectiveC.NSObjectProtocol.someMethodWithDeprecatedOptions}} diff --git a/test/Incremental/autolinking-overlay.swift b/test/Incremental/autolinking-overlay.swift new file mode 100644 index 0000000000000..bf76451fa2ed5 --- /dev/null +++ b/test/Incremental/autolinking-overlay.swift @@ -0,0 +1,19 @@ +// This test shares inputs with its cousin autolinking.swift +// This one specifically tests an overlay-esque build. + +// RUN: %empty-directory(%t) +// RUN: cp %s %t +// RUN: cp -r %S/Inputs/autolinking/* %t +// RUN: cd %t + +// RUN: %target-build-swift-dylib(%t/%target-library-name(AutolinkingTest)) -autolink-force-load -module-link-name swiftAutolinkingTest -incremental -driver-show-incremental -module-name AutolinkingTest -output-file-map ofm.json -F %t -import-underlying-module autolinking-overlay.swift autolinking-other.swift + +// Make sure `swift_FORCE_LOAD_$_swiftAutolinkingTest` appears in all objects +// RUN: llvm-readobj -symbols -coff-exports %t/autolinking-overlay.o | %FileCheck %s +// RUN: llvm-readobj -symbols -coff-exports %t/autolinking-other.o | %FileCheck %s +// RUN: llvm-readobj -symbols -coff-exports %t/%target-library-name(AutolinkingTest) | %FileCheck %s + +// Emulate an overlay build by importing content from the underlying module. +extension Test { } + +// CHECK: _swift_FORCE_LOAD_$_swiftAutolinkingTest diff --git a/test/Incremental/autolinking.swift b/test/Incremental/autolinking.swift new file mode 100644 index 0000000000000..3537c96726ce5 --- /dev/null +++ b/test/Incremental/autolinking.swift @@ -0,0 +1,16 @@ +// This test shares inputs with its cousin autolinking-overlay.swift +// This one specifically tests the bare minimum: two empty swift files. + +// RUN: %empty-directory(%t) +// RUN: cp %s %t +// RUN: cp -r %S/Inputs/autolinking/* %t +// RUN: cd %t + +// RUN: %target-build-swift-dylib(%t/%target-library-name(AutolinkingTest)) -autolink-force-load -module-link-name swiftAutolinkingTest -incremental -driver-show-incremental -module-name AutolinkingTest -output-file-map ofm.json autolinking.swift autolinking-other.swift + +// Make sure `swift_FORCE_LOAD_$_swiftAutolinkingTest` appears in all objects +// RUN: llvm-readobj -symbols -coff-exports %t/autolinking.o | %FileCheck %s +// RUN: llvm-readobj -symbols -coff-exports %t/autolinking-other.o | %FileCheck %s +// RUN: llvm-readobj -symbols -coff-exports %t/%target-library-name(AutolinkingTest) | %FileCheck %s + +// CHECK: _swift_FORCE_LOAD_$_swiftAutolinkingTest diff --git a/test/Index/index_curry_thunk.swift b/test/Index/index_curry_thunk.swift new file mode 100644 index 0000000000000..ae6b72f8530d4 --- /dev/null +++ b/test/Index/index_curry_thunk.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s + +struct SomeStruct { + func simple(_ someClosure: () -> Void) { } +} + +func test(s: SomeStruct) { + s.simple { } + // CHECK: [[@LINE-1]]:5 | instance-method/Swift | simple(_:) | s:14swift_ide_test10SomeStructV6simpleyyyyXEF | Ref,Call,RelRec,RelCall,RelCont | rel: 2 + (((s).simple)) { } + // CHECK: [[@LINE-1]]:9 | instance-method/Swift | simple(_:) | s:14swift_ide_test10SomeStructV6simpleyyyyXEF | Ref,Call,RelRec,RelCall,RelCont | rel: 2 +} diff --git a/test/Index/kinds_objc.swift b/test/Index/kinds_objc.swift index a64d6a15b7b27..9842968c0e022 100644 --- a/test/Index/kinds_objc.swift +++ b/test/Index/kinds_objc.swift @@ -1,6 +1,8 @@ // RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s // REQUIRES: objc_interop +import Foundation + @objc class TargetForIBAction {} // CHECK: [[@LINE-1]]:13 | class/Swift | TargetForIBAction | [[TargetForIBAction_USR:.*]] | Def | @objc class TargetForIBSegueAction {} diff --git a/test/Index/local.swift b/test/Index/local.swift index eaa5b370bb636..94d63f893ca31 100644 --- a/test/Index/local.swift +++ b/test/Index/local.swift @@ -4,17 +4,17 @@ func foo(a: Int, b: Int, c: Int) { let x = a + b // LOCAL: [[@LINE-1]]:9 | variable(local)/Swift | x | [[x_USR:.*]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-2]]:9 | variable(local)/Swift | x | [[x_USR:.*]] | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-2]]:9 | variable(local)/Swift | x | {{.*}} | Def,RelChild | rel: 1 let y = x + c // LOCAL: [[@LINE-1]]:9 | variable(local)/Swift | y | [[y_USR:.*]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-2]]:9 | variable(local)/Swift | y | [[y_USR:.*]] | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-2]]:9 | variable(local)/Swift | y | {{.*}} | Def,RelChild | rel: 1 // LOCAL: [[@LINE-3]]:13 | variable(local)/Swift | x | [[x_USR]] | Ref,Read,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-4]]:13 | variable(local)/Swift | x | [[x_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-4]]:13 | variable(local)/Swift | x | {{.*}} | Ref,Read,RelCont | rel: 1 struct LocalStruct { // LOCAL: [[@LINE-1]]:12 | struct(local)/Swift | LocalStruct | [[LocalStruct_USR:.*]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-2]]:12 | struct(local)/Swift | LocalStruct | [[LocalStruct_USR:.*]] | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-2]]:12 | struct(local)/Swift | LocalStruct | {{.*}} | Def,RelChild | rel: 1 let member = 2 // LOCAL: [[@LINE-1]]:13 | instance-property(local)/Swift | member | {{.*}} | Def,RelChild | rel: 1 @@ -24,21 +24,21 @@ func foo(a: Int, b: Int, c: Int) { enum LocalEnum { // LOCAL: [[@LINE-1]]:10 | enum(local)/Swift | LocalEnum | [[LocalEnum_USR:.*]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-2]]:10 | enum(local)/Swift | LocalEnum | [[LocalEnum_USR:.*]] | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-2]]:10 | enum(local)/Swift | LocalEnum | {{.*}} | Def,RelChild | rel: 1 case foo(x: LocalStruct) // LOCAL: [[@LINE-1]]:14 | enumerator(local)/Swift | foo(x:) | [[LocalEnum_foo_USR:.*]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-2]]:14 | enumerator(local)/Swift | foo(x:) | [[LocalEnum_foo_USR:.*]] | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-2]]:14 | enumerator(local)/Swift | foo(x:) | {{.*}} | Def,RelChild | rel: 1 // LOCAL: [[@LINE-3]]:21 | struct(local)/Swift | LocalStruct | [[LocalStruct_USR]] | Ref,RelCont | rel: 1 } let _ = LocalEnum.foo(x: LocalStruct()) // LOCAL: [[@LINE-1]]:13 | enum(local)/Swift | LocalEnum | [[LocalEnum_USR]] | Ref,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-2]]:13 | enum(local)/Swift | LocalEnum | [[LocalEnum_USR]] | Ref,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-2]]:13 | enum(local)/Swift | LocalEnum | {{.*}} | Ref,RelCont | rel: 1 // LOCAL: [[@LINE-3]]:23 | enumerator(local)/Swift | foo(x:) | [[LocalEnum_foo_USR]] | Ref,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-4]]:23 | enumerator(local)/Swift | foo(x:) | [[LocalEnum_foo_USR]] | Ref,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-4]]:23 | enumerator(local)/Swift | foo(x:) | {{.*}} | Ref,RelCont | rel: 1 // LOCAL: [[@LINE-5]]:30 | struct(local)/Swift | LocalStruct | [[LocalStruct_USR]] | Ref,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-6]]:30 | struct(local)/Swift | LocalStruct | [[LocalStruct_USR]] | Ref,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-6]]:30 | struct(local)/Swift | LocalStruct | {{.*}} | Ref,RelCont | rel: 1 } @@ -47,34 +47,34 @@ func bar(arg: Int?) { case let .some(x) where x == 0: // LOCAL: [[@LINE-1]]:18 | variable(local)/Swift | x | [[x_USR:.*]] | Def,RelChild | rel: 1 // LOCAL: [[@LINE-2]]:27 | variable(local)/Swift | x | [[x_USR]] | Ref,Read,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-3]]:18 | variable(local)/Swift | x | [[x_USR:.*]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-4]]:27 | variable(local)/Swift | x | [[x_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-3]]:18 | variable(local)/Swift | x | {{.*}} | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-4]]:27 | variable(local)/Swift | x | {{.*}} | Ref,Read,RelCont | rel: 1 print(x) // LOCAL: [[@LINE-1]]:11 | variable(local)/Swift | x | [[x_USR]] | Ref,Read,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-2]]:11 | variable(local)/Swift | x | [[x_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-2]]:11 | variable(local)/Swift | x | {{.*}} | Ref,Read,RelCont | rel: 1 case let .some(x) where x == 1, // LOCAL: [[@LINE-1]]:18 | variable(local)/Swift | x | [[x2_USR:.*]] | Def,RelChild | rel: 1 // LOCAL: [[@LINE-2]]:27 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-3]]:18 | variable(local)/Swift | x | [[x2_USR:.*]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-4]]:27 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-3]]:18 | variable(local)/Swift | x | {{.*}} | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-4]]:27 | variable(local)/Swift | x | {{.*}} | Ref,Read,RelCont | rel: 1 let .some(x) where x == 2: // LOCAL: [[@LINE-1]]:18 | variable(local)/Swift | x | [[x2_USR]] | Def,RelChild | rel: 1 // LOCAL: [[@LINE-2]]:27 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-3]]:18 | variable(local)/Swift | x | [[x2_USR]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-4]]:27 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-3]]:18 | variable(local)/Swift | x | {{.*}} | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-4]]:27 | variable(local)/Swift | x | {{.*}} | Ref,Read,RelCont | rel: 1 print(x) // LOCAL: [[@LINE-1]]:11 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-2]]:11 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-2]]:11 | variable(local)/Swift | x | {{.*}} | Ref,Read,RelCont | rel: 1 fallthrough case let .some(x) where x == 3: // LOCAL: [[@LINE-1]]:18 | variable(local)/Swift | x | [[x2_USR]] | Def,RelChild | rel: 1 // LOCAL: [[@LINE-2]]:27 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-1]]:18 | variable(local)/Swift | x | [[x2_USR]] | Def,RelChild | rel: 1 - // CHECK-NOT: [[@LINE-2]]:27 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-1]]:18 | variable(local)/Swift | x | {{.*}} | Def,RelChild | rel: 1 + // CHECK-NOT: [[@LINE-2]]:27 | variable(local)/Swift | x | {{.*}} | Ref,Read,RelCont | rel: 1 print(x) // LOCAL: [[@LINE-1]]:11 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 - // CHECK-NOT: [[@LINE-1]]:11 | variable(local)/Swift | x | [[x2_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK-NOT: [[@LINE-1]]:11 | variable(local)/Swift | x | {{.*}} | Ref,Read,RelCont | rel: 1 default: break } diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index 209977c3f2cff..5f799f16a5dc0 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -6,6 +6,9 @@ #define MAIN_ACTOR __attribute__((__swift_attr__("@MainActor"))) #define MAIN_ACTOR_UNSAFE __attribute__((__swift_attr__("@_unsafeMainActor"))) +#define NS_EXTENSIBLE_STRING_ENUM __attribute__((swift_wrapper(struct))); +typedef NSString *Flavor NS_EXTENSIBLE_STRING_ENUM; + @protocol ServiceProvider @property(readonly) NSArray *allOperations; -(void)allOperationsWithCompletionHandler:(void (^)(NSArray *))completion; @@ -24,7 +27,7 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res -(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); -(void)findQAndAWithCompletionHandler:(void (^)(NSString *_Nullable_result, NSString *_Nullable answer, NSError * _Nullable))handler; -(void)findQuestionableAnswersWithCompletionHandler:(CompletionHandler)handler; --(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler; +-(void)doSomethingFun:(NSString *)operation then:(void (^)())completionHandler; -(void)getFortuneAsynchronouslyWithCompletionHandler:(void (^)(NSString *_Nullable, NSError * _Nullable))handler; -(void)getMagicNumberAsynchronouslyWithSeed:(NSInteger)seed completionHandler:(void (^)(NSInteger, NSError * _Nullable))handler; @property(readwrite) void (^completionHandler)(NSInteger); @@ -57,6 +60,13 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res // rdar://73798726 - (void)getSomeObjectWithCompletionHandler:(nullable void (^)(NSObject *_Nullable x, NSError *_Nullable error))handler; +- (void)performVoid2VoidWithCompletion:(void (^ _Nonnull)(void (^ _Nonnull)(void)))completion; +- (void)performId2VoidWithCompletion:(void (^ _Nonnull)(void (^ _Nonnull)(id _Nonnull)))completion; +- (void)performId2IdWithCompletion:(void (^ _Nonnull)(id _Nonnull (^ _Nonnull)(id _Nonnull)))completion; +- (void)performNSString2NSStringWithCompletion:(void (^ _Nonnull)(NSString * _Nonnull (^ _Nonnull)(NSString * _Nonnull)))completion; +- (void)performNSString2NSStringNSStringWithCompletion:(void (^ _Nonnull)(NSString * _Nonnull (^ _Nonnull)(NSString * _Nonnull), NSString * _Nonnull))completion; +- (void)performId2VoidId2VoidWithCompletion:(void (^ _Nonnull)(void (^ _Nonnull)(id _Nonnull), void (^ _Nonnull)(id _Nonnull)))completion; + -(void)oldAPIWithCompletionHandler:(void (^ _Nonnull)(NSString *_Nullable, NSError *_Nullable))handler __attribute__((availability(macosx, deprecated=10.14))); -(void)someAsyncMethodWithBlock:(void (^ _Nonnull)(NSString *_Nullable, NSError *_Nullable))completionHandler; @@ -77,6 +87,12 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res -(void)asyncImportSame:(NSString *)operation replyTo:(void (^)(NSInteger))handler __attribute__((swift_async(none))); -(void)overridableButRunsOnMainThreadWithCompletionHandler:(MAIN_ACTOR_UNSAFE void (^ _Nullable)(NSString *))completion; +- (void)obtainClosureWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)getIceCreamFlavorWithCompletionHandler: + (void (^)(Flavor flavor, NSError *__nullable error))completionHandler; @end @protocol RefrigeratorDelegate @@ -96,12 +112,12 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res @protocol ProtocolWithSwiftAttributes -(void)independentMethod __attribute__((__swift_attr__("@actorIndependent"))); --(void)asyncHandlerMethod __attribute__((__swift_attr__("@asyncHandler"))); +-(void)nonisolatedMethod __attribute__((__swift_attr__("nonisolated"))); -(void)mainActorMethod __attribute__((__swift_attr__("@MainActor"))); -(void)uiActorMethod __attribute__((__swift_attr__("@UIActor"))); @optional --(void)missingAtAttributeMethod __attribute__((__swift_attr__("asyncHandler"))); +-(void)missingAtAttributeMethod __attribute__((__swift_attr__("MainActor"))); @end @protocol OptionalObserver diff --git a/test/Inputs/conditional_conformance_recursive.swift b/test/Inputs/conditional_conformance_recursive.swift index fab1de6d01c30..c094a239802df 100644 --- a/test/Inputs/conditional_conformance_recursive.swift +++ b/test/Inputs/conditional_conformance_recursive.swift @@ -19,7 +19,7 @@ protocol P3: P2 where A: P3 { } extension Wrapper: P3 where T: P3 { } // associated type witness table accessor for A : P2 in Wrapper: P2 -// CHECK-LABEL: define internal swiftcc i8** @"$s33conditional_conformance_recursive7WrapperVyxGAA2P2A2aERzrl1A_AaEPWT" +// CHECK-LABEL: define internal swiftcc i8** @"$s33conditional_conformance_recursive7WrapperVyxGAA2P2A2aERzrl1AAA2P1P_AaEPWT" // CHECK: [[CONDITIONAL_REQ_BUFFER:%.*]] = alloca [1 x i8**] // CHECK: [[FIRST_REQ:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* [[CONDITIONAL_REQ_BUFFER]] // CHECK: call i8** @swift_getWitnessTable diff --git a/test/InterfaceHash/added_function.swift b/test/InterfaceHash/added_function.swift index d50c48547fcc3..4f137877f048d 100644 --- a/test/InterfaceHash/added_function.swift +++ b/test/InterfaceHash/added_function.swift @@ -4,6 +4,11 @@ // RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash +/// We should generate an interface hash for emit-module-separately jobs even +/// with no primaries. +// RUN: %target-swift-frontend -dump-interface-hash %t/b.swift -experimental-skip-non-inlinable-function-bodies-without-types 2> %t/b-emit-module.hash +// RUN: cmp %t/b.hash %t/b-emit-module.hash + // BEGIN a.swift func f() {} diff --git a/test/Interop/Cxx/class/Inputs/module.modulemap b/test/Interop/Cxx/class/Inputs/module.modulemap index 648d9085b8588..45e4c110406e6 100644 --- a/test/Interop/Cxx/class/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/Inputs/module.modulemap @@ -48,6 +48,11 @@ module MemberVariables { requires cplusplus } +module MutableMembers { + header "mutable-members.h" + requires cplusplus +} + module ProtocolConformance { header "protocol-conformance.h" requires cplusplus diff --git a/test/Interop/Cxx/class/Inputs/mutable-members.h b/test/Interop/Cxx/class/Inputs/mutable-members.h new file mode 100644 index 0000000000000..9ebe807afb55b --- /dev/null +++ b/test/Interop/Cxx/class/Inputs/mutable-members.h @@ -0,0 +1,21 @@ +#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_MUTABLE_MEMBERS_H +#define TEST_INTEROP_CXX_CLASS_INPUTS_MUTABLE_MEMBERS_H + +struct HasPublicMutableMember { + mutable int a = 0; + + int foo() const { + a++; + return a; + } +}; + +struct HasPrivateMutableMember { +private: + mutable int a = 0; + +public: + void bar() const { a++; } +}; + +#endif // TEST_INTEROP_CXX_CLASS_INPUTS_MUTABLE_MEMBERS_H diff --git a/test/Interop/Cxx/class/inline-function-codegen/Inputs/module.modulemap b/test/Interop/Cxx/class/inline-function-codegen/Inputs/module.modulemap index 0b7935f926738..7f544f3b9e97e 100644 --- a/test/Interop/Cxx/class/inline-function-codegen/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/inline-function-codegen/Inputs/module.modulemap @@ -1,31 +1,39 @@ module ConstructorCallsFunction { header "constructor-calls-function.h" + requires cplusplus } module ConstructorCallsFunctionFromNestedStruct { header "constructor-calls-function-from-nested-struct.h" + requires cplusplus } module ConstructorCallsMethod { header "constructor-calls-method.h" + requires cplusplus } module FieldInitCallsFunction { header "field-init-calls-function.h" + requires cplusplus } module MethodCallsFunction { header "method-calls-function.h" + requires cplusplus } module MethodCallsMethod { header "method-calls-method.h" + requires cplusplus } module MethodCallsMethodFromNestedStruct { header "method-calls-method-from-nested-struct.h" + requires cplusplus } module StaticVarInitCallsFunction { header "static-var-init-calls-function.h" + requires cplusplus } diff --git a/test/Interop/Cxx/class/mutable-members-module-interface.swift b/test/Interop/Cxx/class/mutable-members-module-interface.swift new file mode 100644 index 0000000000000..1a100acf5bf1f --- /dev/null +++ b/test/Interop/Cxx/class/mutable-members-module-interface.swift @@ -0,0 +1,10 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=MutableMembers -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: struct HasPublicMutableMember { +// CHECK: var a: Int32 +// CHECK: mutating func foo() -> Int32 +// CHECK: } + +// CHECK: struct HasPrivateMutableMember { +// CHECK: mutating func bar() +// CHECK: } diff --git a/test/Interop/Cxx/class/mutable-members-typechecker.swift b/test/Interop/Cxx/class/mutable-members-typechecker.swift new file mode 100644 index 0000000000000..70035037a12a8 --- /dev/null +++ b/test/Interop/Cxx/class/mutable-members-typechecker.swift @@ -0,0 +1,6 @@ +// RUN: %target-typecheck-verify-swift -I %S/Inputs -enable-cxx-interop + +import MutableMembers + +let obj = HasPublicMutableMember(a: 42) // expected-note {{change 'let' to 'var' to make it mutable}} +let i = obj.foo() // expected-error {{cannot use mutating member on immutable value: 'obj' is a 'let' constant}} diff --git a/test/Interop/Cxx/class/mutable-members.swift b/test/Interop/Cxx/class/mutable-members.swift new file mode 100644 index 0000000000000..38c02e3904d40 --- /dev/null +++ b/test/Interop/Cxx/class/mutable-members.swift @@ -0,0 +1,16 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop) +// +// REQUIRES: executable_test + +import StdlibUnittest +import MutableMembers + +var MembersTestSuite = TestSuite("MembersTestSuite") + +MembersTestSuite.test("MutableMembers") { + var obj = HasPublicMutableMember(a: 1) + expectEqual(obj.foo(), 2) + expectEqual(obj.foo(), 3) +} + +runAllTests() diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift index 5e0c03c94b63c..b7c2af710568c 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift @@ -73,7 +73,7 @@ public func testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() // CHECK: [[VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* %0, i32 0, i32 0 // CHECK: [[VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL]], i32 0, i32 0 // CHECK: [[LHS:%.*]] = load i32, i32* [[VAL_VAL]] -// CHECK: [[OUT:%.*]] = icmp eq i32 %1, 42 +// CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42 // CHECK: ret i1 [[OUT]] public func test(obj: StructWithCopyConstructorAndValue) -> Bool { return obj.value == 42 diff --git a/test/Interop/Cxx/namespace/Inputs/classes.h b/test/Interop/Cxx/namespace/Inputs/classes.h index 0fe119af460df..ce4436455b793 100644 --- a/test/Interop/Cxx/namespace/Inputs/classes.h +++ b/test/Interop/Cxx/namespace/Inputs/classes.h @@ -40,4 +40,35 @@ struct BasicStruct { }; } // namespace ClassesNS3 +namespace GlobalAliasToNS1 = ClassesNS1; + +namespace ClassesNS4 { +namespace AliasToGlobalNS1 = ::ClassesNS1; +namespace AliasToGlobalNS2 = ::ClassesNS1::ClassesNS2; + +namespace ClassesNS5 { +struct BasicStruct {}; +} // namespace ClassesNS5 + +namespace AliasToInnerNS5 = ClassesNS5; +namespace AliasToNS2 = ClassesNS1::ClassesNS2; + +namespace AliasChainToNS1 = GlobalAliasToNS1; +namespace AliasChainToNS2 = AliasChainToNS1::ClassesNS2; +} // namespace ClassesNS4 + +namespace ClassesNS5 { +struct BasicStruct {}; +namespace AliasToAnotherNS5 = ClassesNS4::ClassesNS5; + +namespace ClassesNS5 { +struct BasicStruct {}; +namespace AliasToNS5NS5 = ClassesNS5; +} // namespace ClassesNS5 + +namespace AliasToGlobalNS5 = ::ClassesNS5; +namespace AliasToLocalNS5 = ClassesNS5; +namespace AliasToNS5 = ::ClassesNS5::ClassesNS5; +} // namespace ClassesNS5 + #endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_CLASSES_H diff --git a/test/Interop/Cxx/namespace/Inputs/module.modulemap b/test/Interop/Cxx/namespace/Inputs/module.modulemap index 3fef2a43d5de6..442a7083014b0 100644 --- a/test/Interop/Cxx/namespace/Inputs/module.modulemap +++ b/test/Interop/Cxx/namespace/Inputs/module.modulemap @@ -1,5 +1,6 @@ module Classes { header "classes.h" + requires cplusplus } module ClassesSecondHeader { @@ -7,22 +8,43 @@ module ClassesSecondHeader { // these headers should be added to the correct module: SR-14214. header "classes.h" header "classes-second-header.h" + requires cplusplus } module FreeFunctions { header "free-functions.h" + requires cplusplus } module FreeFunctionsSecondHeader { header "free-functions.h" header "free-functions-second-header.h" + requires cplusplus +} + +module Submodules { + module SubmoduleA { + header "submodule-a.h" + requires cplusplus + } + module SubmoduleB { + header "submodule-b.h" + requires cplusplus + } } module Templates { header "templates.h" + requires cplusplus } module TemplatesSecondHeader { header "templates.h" header "templates-second-header.h" + requires cplusplus +} + +module TemplatesWithForwardDecl { + header "templates-with-forward-decl.h" + requires cplusplus } diff --git a/test/Interop/Cxx/namespace/Inputs/submodule-a.h b/test/Interop/Cxx/namespace/Inputs/submodule-a.h new file mode 100644 index 0000000000000..6c8dd60754dcf --- /dev/null +++ b/test/Interop/Cxx/namespace/Inputs/submodule-a.h @@ -0,0 +1,16 @@ +#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_A_H +#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_A_H + +namespace NS1 { + +namespace NS2 { + +struct BasicDeepA {}; + +} // namespace NS2 + +struct BasicA {}; + +} // namespace NS1 + +#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_A_H diff --git a/test/Interop/Cxx/namespace/Inputs/submodule-b.h b/test/Interop/Cxx/namespace/Inputs/submodule-b.h new file mode 100644 index 0000000000000..695eb2bf9dd10 --- /dev/null +++ b/test/Interop/Cxx/namespace/Inputs/submodule-b.h @@ -0,0 +1,16 @@ +#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_B_H +#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_B_H + +namespace NS1 { + +namespace NS2 { + +struct BasicDeepB {}; + +} // namespace NS2 + +struct BasicB {}; + +} // namespace NS1 + +#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_SUBMODULE_B_H diff --git a/test/Interop/Cxx/namespace/Inputs/templates-with-forward-decl.h b/test/Interop/Cxx/namespace/Inputs/templates-with-forward-decl.h new file mode 100644 index 0000000000000..ea865c35ad380 --- /dev/null +++ b/test/Interop/Cxx/namespace/Inputs/templates-with-forward-decl.h @@ -0,0 +1,37 @@ +#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_TEMPLATES_WITH_FORWARD_DECL_H +#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_TEMPLATES_WITH_FORWARD_DECL_H + +namespace NS1 { + +template +struct Decl; + +typedef Decl di; + +} // namespace NS1 + +namespace NS1 { + +template +struct ForwardDeclared; + +template +struct Decl { + typedef T MyInt; + ForwardDeclared fwd; + const static MyInt intValue = -1; +}; + +template +const typename Decl::MyInt Decl::intValue; + +} // namespace NS1 + +namespace NS1 { + +template +struct ForwardDeclared {}; + +} // namespace NS1 + +#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_TEMPLATES_WITH_FORWARD_DECL_H diff --git a/test/Interop/Cxx/namespace/classes-irgen.swift b/test/Interop/Cxx/namespace/classes-irgen.swift index a2ffeb1da20c9..ecc14ef33cfbb 100644 --- a/test/Interop/Cxx/namespace/classes-irgen.swift +++ b/test/Interop/Cxx/namespace/classes-irgen.swift @@ -6,6 +6,7 @@ import Classes // CHECK: call i8* @{{_ZN10ClassesNS111BasicStruct11basicMemberEv|"\?basicMember@BasicStruct@ClassesNS1@@QEAAPEBDXZ"}}(%"struct.ClassesNS1::BasicStruct"* // CHECK: call i8* @{{_ZN10ClassesNS110ClassesNS211BasicStruct11basicMemberEv|"\?basicMember@BasicStruct@ClassesNS2@ClassesNS1@@QEAAPEBDXZ"}}(%"struct.ClassesNS1::ClassesNS2::BasicStruct"* // CHECK: call i8* @{{_ZN10ClassesNS311BasicStruct11basicMemberEv|"\?basicMember@BasicStruct@ClassesNS3@@QEAAPEBDXZ"}}(%"struct.ClassesNS3::BasicStruct"* +// CHECK: call i8* @{{_ZN10ClassesNS111BasicStruct11basicMemberEv|"\?basicMember@BasicStruct@ClassesNS1@@QEAAPEBDXZ"}}(%"struct.ClassesNS1::BasicStruct"* // CHECK: ret void public func basicTests() { var basicStructInst = ClassesNS1.BasicStruct() @@ -16,6 +17,9 @@ public func basicTests() { var siblingBasicStruct = ClassesNS3.BasicStruct() siblingBasicStruct.basicMember() + + var basicStructViaAlias = ClassesNS4.AliasToGlobalNS1.BasicStruct() + basicStructViaAlias.basicMember() } // CHECK-LABEL: define {{.*}}void @"$s4main15forwardDeclaredyyF"() diff --git a/test/Interop/Cxx/namespace/classes-module-interface.swift b/test/Interop/Cxx/namespace/classes-module-interface.swift index ea1db04ab7706..6fb5252496ca1 100644 --- a/test/Interop/Cxx/namespace/classes-module-interface.swift +++ b/test/Interop/Cxx/namespace/classes-module-interface.swift @@ -1,7 +1,7 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=Classes -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s // CHECK-NOT: extension -// CHECK: extension ClassesNS1.ClassesNS2 { +// CHECK: extension ClassesNS1 { // CHECK: struct BasicStruct { // CHECK: init() // CHECK: mutating func basicMember() -> UnsafePointer! @@ -13,7 +13,7 @@ // CHECK: } // CHECK-NOT: extension -// CHECK: extension ClassesNS1 { +// CHECK: extension ClassesNS1.ClassesNS2 { // CHECK: struct BasicStruct { // CHECK: init() // CHECK: mutating func basicMember() -> UnsafePointer! @@ -32,3 +32,34 @@ // CHECK: } // CHECK: } // CHECK-NOT: extension + +// CHECK: typealias GlobalAliasToNS1 = ClassesNS1 +// CHECK: extension ClassesNS4 { +// CHECK: typealias AliasToGlobalNS1 = ClassesNS1 +// CHECK: typealias AliasToGlobalNS2 = ClassesNS1.ClassesNS2 +// CHECK: typealias AliasToInnerNS5 = ClassesNS4.ClassesNS5 +// CHECK: typealias AliasToNS2 = ClassesNS1.ClassesNS2 +// CHECK: typealias AliasChainToNS1 = ClassesNS1 +// CHECK: typealias AliasChainToNS2 = ClassesNS1.ClassesNS2 +// CHECK: } +// CHECK: extension ClassesNS4.ClassesNS5 { +// CHECK: struct BasicStruct { +// CHECK: init() +// CHECK: } +// CHECK: } +// CHECK: extension ClassesNS5 { +// CHECK: struct BasicStruct { +// CHECK: init() +// CHECK: } +// CHECK: typealias AliasToAnotherNS5 = ClassesNS4.ClassesNS5 +// CHECK: typealias AliasToGlobalNS5 = ClassesNS5 +// CHECK: typealias AliasToLocalNS5 = ClassesNS5.ClassesNS5 +// CHECK: typealias AliasToNS5 = ClassesNS5.ClassesNS5 +// CHECK: } +// CHECK: extension ClassesNS5.ClassesNS5 { +// CHECK: struct BasicStruct { +// CHECK: init() +// CHECK: } +// CHECK: typealias AliasToNS5NS5 = ClassesNS5.ClassesNS5 +// CHECK: } +// CHECK-NOT: extension diff --git a/test/Interop/Cxx/namespace/classes.swift b/test/Interop/Cxx/namespace/classes.swift index ce7838e4905db..8ccdc40699ed0 100644 --- a/test/Interop/Cxx/namespace/classes.swift +++ b/test/Interop/Cxx/namespace/classes.swift @@ -20,6 +20,10 @@ NamespacesTestSuite.test("Basic classes") { var siblingBasicStruct = ClassesNS3.BasicStruct() let siblingMemberCString = siblingBasicStruct.basicMember() expectEqual(String(cString: siblingMemberCString!), "ClassesNS3::BasicStruct::basicMember") + + var basicStructViaAlias = ClassesNS4.AliasToGlobalNS1.BasicStruct() + let basicMemberViaAliasCString = basicStructViaAlias.basicMember() + expectEqual(String(cString: basicMemberViaAliasCString!), "ClassesNS1::BasicStruct::basicMember") } NamespacesTestSuite.test("Forward declared classes") { diff --git a/test/Interop/Cxx/namespace/free-functions-module-interface.swift b/test/Interop/Cxx/namespace/free-functions-module-interface.swift index c9ddfa985e37c..166f06e2719cd 100644 --- a/test/Interop/Cxx/namespace/free-functions-module-interface.swift +++ b/test/Interop/Cxx/namespace/free-functions-module-interface.swift @@ -1,24 +1,36 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=FreeFunctions -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s // CHECK-NOT: extension +// CHECK: extension FunctionsNS1 { +// CHECK: static func basicFunctionTopLevel() -> UnsafePointer! +// CHECK: static func definedOutOfLine() -> UnsafePointer! +// CHECK: static func forwardDeclared() -> UnsafePointer! +// CHECK: } +// CHECK-NOT: extension + +// CHECK: extension FunctionsNS1.FunctionsNS2 { +// CHECK: static func basicFunctionSecondLevel() -> UnsafePointer! +// CHECK: } +// CHECK-NOT: extension + // CHECK: extension FunctionsNS1.FunctionsNS2.FunctionsNS3 { // CHECK: static func basicFunctionLowestLevel() -> UnsafePointer! // CHECK: } // CHECK-NOT: extension +// CHECK: extension FunctionsNS1 { +// CHECK: static func definedInDefs() -> UnsafePointer! +// CHECK: } +// CHECK-NOT: extension + // CHECK: extension FunctionsNS1 { // CHECK: static func sameNameInChild() -> UnsafePointer! // CHECK: static func sameNameInSibling() -> UnsafePointer! -// CHECK: static func definedInDefs() -> UnsafePointer! -// CHECK: static func forwardDeclared() -> UnsafePointer! -// CHECK: static func basicFunctionTopLevel() -> UnsafePointer! -// CHECK: static func definedOutOfLine() -> UnsafePointer! // CHECK: } // CHECK-NOT: extension // CHECK: extension FunctionsNS1.FunctionsNS2 { // CHECK: static func sameNameInChild() -> UnsafePointer! -// CHECK: static func basicFunctionSecondLevel() -> UnsafePointer! // CHECK: } // CHECK-NOT: extension diff --git a/test/Interop/Cxx/namespace/free-functions-second-header-module-interface.swift b/test/Interop/Cxx/namespace/free-functions-second-header-module-interface.swift index 2d396c914e375..39e1c92a310d1 100644 --- a/test/Interop/Cxx/namespace/free-functions-second-header-module-interface.swift +++ b/test/Interop/Cxx/namespace/free-functions-second-header-module-interface.swift @@ -2,6 +2,5 @@ // TODO: This file doesn't really test anything because functions need not be defined. // CHECK: extension FunctionsNS1 { -// CHECK-NOT: extension // CHECK: static func definedInDefs() -> UnsafePointer! // CHECK: } diff --git a/test/Interop/Cxx/namespace/submodules-module-interface.swift b/test/Interop/Cxx/namespace/submodules-module-interface.swift new file mode 100644 index 0000000000000..e348b8396d235 --- /dev/null +++ b/test/Interop/Cxx/namespace/submodules-module-interface.swift @@ -0,0 +1,28 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=Submodules.SubmoduleA -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s -check-prefix=CHECK-A +// RUN: %target-swift-ide-test -print-module -module-to-print=Submodules.SubmoduleB -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s -check-prefix=CHECK-B + +// CHECK-A-NOT: extension +// CHECK-A: extension NS1 { +// CHECK-A: struct BasicA { +// CHECK-A: } +// CHECK-A: } +// CHECK-A-NOT: extension + +// CHECK-A: extension NS1.NS2 { +// CHECK-A: struct BasicDeepA { +// CHECK-A: } +// CHECK-A: } +// CHECK-A-NOT: extension + +// CHECK-B-NOT: extension +// CHECK-B: extension NS1 { +// CHECK-B: struct BasicB { +// CHECK-B: } +// CHECK-B: } +// CHECK-B-NOT: extension + +// CHECK-B: extension NS1.NS2 { +// CHECK-B: struct BasicDeepB { +// CHECK-B: } +// CHECK-B: } +// CHECK-B-NOT: extension diff --git a/test/Interop/Cxx/namespace/templates-module-interface.swift b/test/Interop/Cxx/namespace/templates-module-interface.swift index 1474992840787..c789de3dfd134 100644 --- a/test/Interop/Cxx/namespace/templates-module-interface.swift +++ b/test/Interop/Cxx/namespace/templates-module-interface.swift @@ -8,16 +8,11 @@ // CHECK: mutating func basicMember() -> UnsafePointer! // CHECK: } // CHECK: typealias BasicClassTemplateChar = TemplatesNS1.__CxxTemplateInstN12TemplatesNS118BasicClassTemplateIcEE -// CHECK: typealias UseTemplate = TemplatesNS4.__CxxTemplateInstN12TemplatesNS417HasSpecializationIcEE -// CHECK: typealias ForwardDeclaredClassTemplateChar = TemplatesNS1.TemplatesNS2.__CxxTemplateInstN12TemplatesNS112TemplatesNS228ForwardDeclaredClassTemplateIcEE // CHECK: } -// CHECK: typealias ForwardDeclaredClassTemplateOutOfLineChar = TemplatesNS1.TemplatesNS2.__CxxTemplateInstN12TemplatesNS112TemplatesNS237ForwardDeclaredClassTemplateOutOfLineIcEE // CHECK-NOT: extension // CHECK: extension TemplatesNS1.TemplatesNS2 { -// CHECK: typealias BasicClassTemplateChar = TemplatesNS1.TemplatesNS3.__CxxTemplateInstN12TemplatesNS112TemplatesNS318BasicClassTemplateIcEE -// CHECK: static func takesClassTemplateFromSibling(_: TemplatesNS1.TemplatesNS2.BasicClassTemplateChar) -> UnsafePointer! // CHECK: static func forwardDeclaredFunctionTemplate(_: T) -> UnsafePointer! // CHECK: struct __CxxTemplateInstN12TemplatesNS112TemplatesNS228ForwardDeclaredClassTemplateIcEE { // CHECK: init() @@ -31,6 +26,16 @@ // CHECK: } // CHECK-NOT: extension +// CHECK: extension TemplatesNS1 { +// CHECK: typealias ForwardDeclaredClassTemplateChar = TemplatesNS1.TemplatesNS2.__CxxTemplateInstN12TemplatesNS112TemplatesNS228ForwardDeclaredClassTemplateIcEE +// CHECK: } +// CHECK: typealias ForwardDeclaredClassTemplateOutOfLineChar = TemplatesNS1.TemplatesNS2.__CxxTemplateInstN12TemplatesNS112TemplatesNS237ForwardDeclaredClassTemplateOutOfLineIcEE + +// CHECK: extension TemplatesNS1.TemplatesNS2 { +// CHECK: typealias BasicClassTemplateChar = TemplatesNS1.TemplatesNS3.__CxxTemplateInstN12TemplatesNS112TemplatesNS318BasicClassTemplateIcEE +// CHECK: static func takesClassTemplateFromSibling(_: TemplatesNS1.TemplatesNS2.BasicClassTemplateChar) -> UnsafePointer! +// CHECK: } + // CHECK: extension TemplatesNS4 { // CHECK: struct __CxxTemplateInstN12TemplatesNS417HasSpecializationIcEE { // CHECK: init() @@ -39,4 +44,7 @@ // CHECK: init() // CHECK: } // CHECK: } -// CHECK-NOT: extension + +// CHECK: extension TemplatesNS1 { +// CHECK: typealias UseTemplate = TemplatesNS4.__CxxTemplateInstN12TemplatesNS417HasSpecializationIcEE +// CHECK: } diff --git a/test/Interop/Cxx/namespace/templates-second-header-module-interface.swift b/test/Interop/Cxx/namespace/templates-second-header-module-interface.swift index bc16357ce4f39..8a11627531c0b 100644 --- a/test/Interop/Cxx/namespace/templates-second-header-module-interface.swift +++ b/test/Interop/Cxx/namespace/templates-second-header-module-interface.swift @@ -1,7 +1,6 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=TemplatesSecondHeader -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s // CHECK: extension TemplatesNS1 { -// CHECK-NOT: extension // CHECK: static func basicFunctionTemplateDefinedInDefs(_: T) -> UnsafePointer! // CHECK: struct __CxxTemplateInstN12TemplatesNS131BasicClassTemplateDefinedInDefsIcEE { // CHECK: init() diff --git a/test/Interop/Cxx/namespace/templates-with-forward-decl-module-interface.swift b/test/Interop/Cxx/namespace/templates-with-forward-decl-module-interface.swift new file mode 100644 index 0000000000000..82cc2120da5cb --- /dev/null +++ b/test/Interop/Cxx/namespace/templates-with-forward-decl-module-interface.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=TemplatesWithForwardDecl -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: extension NS1 { +// CHECK: struct __CxxTemplateInstN3NS14DeclIiEE { +// CHECK: typealias MyInt = Int32 +// CHECK: var fwd: NS1.__CxxTemplateInstN3NS115ForwardDeclaredIiEE +// CHECK: static let intValue: NS1.__CxxTemplateInstN3NS14DeclIiEE.MyInt +// CHECK: } +// CHECK: } diff --git a/test/Interop/Cxx/operators/Inputs/member-inline.h b/test/Interop/Cxx/operators/Inputs/member-inline.h index 9fc144993364e..7f3b745568dd3 100644 --- a/test/Interop/Cxx/operators/Inputs/member-inline.h +++ b/test/Interop/Cxx/operators/Inputs/member-inline.h @@ -147,7 +147,11 @@ struct IntArrayByVal { struct NonTrivialIntArrayByVal { NonTrivialIntArrayByVal(int first) { values[0] = first; } - NonTrivialIntArrayByVal(const NonTrivialIntArrayByVal &other) {} + NonTrivialIntArrayByVal(const NonTrivialIntArrayByVal &other) { + for (int i = 0; i < 5; i++) + values[i] = other.values[i]; + } + int operator[](int x) const { return values[x]; } // For testing purposes. diff --git a/test/Interop/Cxx/operators/Inputs/member-out-of-line.h b/test/Interop/Cxx/operators/Inputs/member-out-of-line.h index 54444af213d6b..c4948222a2758 100644 --- a/test/Interop/Cxx/operators/Inputs/member-out-of-line.h +++ b/test/Interop/Cxx/operators/Inputs/member-out-of-line.h @@ -31,7 +31,10 @@ struct ReadWriteIntArray { struct NonTrivialIntArrayByVal { NonTrivialIntArrayByVal(int first) { values[0] = first; } - NonTrivialIntArrayByVal(const NonTrivialIntArrayByVal &other) {} + NonTrivialIntArrayByVal(const NonTrivialIntArrayByVal &other) { + for (int i = 0; i < 5; i++) + values[i] = other.values[i]; + } int operator[](int x); // For testing purposes. diff --git a/test/Interop/Cxx/operators/member-inline-module-interface.swift b/test/Interop/Cxx/operators/member-inline-module-interface.swift index 3fdad7a74ef58..4f6b7fedabc73 100644 --- a/test/Interop/Cxx/operators/member-inline-module-interface.swift +++ b/test/Interop/Cxx/operators/member-inline-module-interface.swift @@ -20,26 +20,26 @@ // CHECK: struct ReadWriteIntArray { // CHECK: struct NestedIntArray { // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer +// CHECK: func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer -// CHECK: subscript(x: Int32) -> Int32 { mutating get } +// CHECK: subscript(x: Int32) -> Int32 { get } // CHECK: } // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer +// CHECK: func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer // CHECK: @available(*, unavailable, message: "use subscript") // CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer -// CHECK: subscript(x: Int32) -> Int32 { mutating get set } +// CHECK: subscript(x: Int32) -> Int32 // CHECK: } // CHECK: struct ReadOnlyIntArray { // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer +// CHECK: func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer -// CHECK: subscript(x: Int32) -> Int32 { mutating get } +// CHECK: subscript(x: Int32) -> Int32 { get } // CHECK: } @@ -53,7 +53,7 @@ // CHECK: struct DifferentTypesArray { // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer +// CHECK: func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer // CHECK: @available(*, unavailable, message: "use subscript") // CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer @@ -62,14 +62,14 @@ // CHECK: mutating func __operatorSubscript(_ x: Bool) -> UnsafeMutablePointer // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Bool) -> UnsafePointer +// CHECK: func __operatorSubscriptConst(_ x: Bool) -> UnsafePointer // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Double) -> UnsafePointer +// CHECK: func __operatorSubscriptConst(_ x: Double) -> UnsafePointer -// CHECK: subscript(x: Int32) -> Int32 { mutating get set } -// CHECK: subscript(x: Bool) -> Bool { mutating get set } -// CHECK: subscript(x: Double) -> Double { mutating get } +// CHECK: subscript(x: Int32) -> Int32 +// CHECK: subscript(x: Bool) -> Bool +// CHECK: subscript(x: Double) -> Double { get } // CHECK: } @@ -80,9 +80,9 @@ // CHECK: mutating func __operatorSubscript(_ i: Int32) -> UnsafeMutablePointer // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ i: Int32) -> UnsafePointer +// CHECK: func __operatorSubscriptConst(_ i: Int32) -> UnsafePointer -// CHECK: subscript(i: Int32) -> Double { mutating get set } +// CHECK: subscript(i: Int32) -> Double // CHECK: } // CHECK: typealias TemplatedDoubleArray = __CxxTemplateInst14TemplatedArrayIdE @@ -93,29 +93,29 @@ // CHECK: struct IntArrayByVal { // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32 -// CHECK: subscript(x: Int32) -> Int32 { mutating get } +// CHECK: func __operatorSubscriptConst(_ x: Int32) -> Int32 +// CHECK: subscript(x: Int32) -> Int32 { get } // CHECK: } // CHECK: struct NonTrivialIntArrayByVal { // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32 -// CHECK: subscript(x: Int32) -> Int32 { mutating get } +// CHECK: func __operatorSubscriptConst(_ x: Int32) -> Int32 +// CHECK: subscript(x: Int32) -> Int32 { get } // CHECK: } // CHECK: struct DifferentTypesArrayByVal { // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32 +// CHECK: func __operatorSubscriptConst(_ x: Int32) -> Int32 // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Bool) -> Bool +// CHECK: func __operatorSubscriptConst(_ x: Bool) -> Bool // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Double) -> Double +// CHECK: func __operatorSubscriptConst(_ x: Double) -> Double // CHECK: subscript(x: Int32) -> Int32 { mutating get } // CHECK: subscript(x: Bool) -> Bool { mutating get } -// CHECK: subscript(x: Double) -> Double { mutating get } +// CHECK: subscript(x: Double) -> Double { get } // CHECK: } @@ -157,8 +157,8 @@ // CHECK: } // CHECK: struct ConstOpPtrByVal { // CHECK: @available(*, unavailable, message: "use subscript") -// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer! -// CHECK: subscript(x: Int32) -> UnsafePointer! { mutating get } +// CHECK: func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer! +// CHECK: subscript(x: Int32) -> UnsafePointer! { get } // CHECK: } // CHECK: struct ConstPtrByVal { // CHECK: @available(*, unavailable, message: "use subscript") diff --git a/test/Interop/Cxx/operators/member-inline-silgen.swift b/test/Interop/Cxx/operators/member-inline-silgen.swift index 6e4574eb276aa..ec6d5a888c6b0 100644 --- a/test/Interop/Cxx/operators/member-inline-silgen.swift +++ b/test/Interop/Cxx/operators/member-inline-silgen.swift @@ -33,22 +33,18 @@ public func call(_ wrapper: inout AddressOnlyIntWrapper) -> Int32 { wrapper() } // CHECK: sil [clang AddressOnlyIntWrapper.callAsFunction] [[NAME]] : $@convention(c) (@inout AddressOnlyIntWrapper) -> Int32 -public func index(_ arr: inout ReadOnlyIntArray, _ arg: Int32) -> Int32 { arr[arg] } +public func index(_ arr: ReadOnlyIntArray, _ arg: Int32) -> Int32 { arr[arg] } -// CHECK: sil @$s4main5indexys5Int32VSo16ReadOnlyIntArrayVz_ADtF : $@convention(thin) (@inout ReadOnlyIntArray, Int32) -> Int32 { +// CHECK: sil @$s4main5indexys5Int32VSo16ReadOnlyIntArrayV_ADtF : $@convention(thin) (@in_guaranteed ReadOnlyIntArray, Int32) -> Int32 { // CHECK: bb0([[ARR:%.*]] : $*ReadOnlyIntArray, [[INDEX:%.*]] : $Int32): -// CHECK: [[ARRACCESS:%.*]] = begin_access [modify] [static] [[ARR]] : $*ReadOnlyIntArray -// CHECK: [[ARRACCESS2:%.*]] = begin_access [modify] [static] [[ARRACCESS]] : $*ReadOnlyIntArray -// CHECK: [[OP:%.*]] = function_ref [[READCLASSNAME:@(_ZNK16ReadOnlyIntArrayixEi|\?\?AReadOnlyIntArray@@QEBAAEBHH@Z)]] : $@convention(c) (@inout ReadOnlyIntArray, Int32) -> UnsafePointer -// CHECK: [[PTR:%.*]] = apply [[OP]]([[ARRACCESS2]], [[INDEX]]) : $@convention(c) (@inout ReadOnlyIntArray, Int32) -> UnsafePointer -// CHECK: } // end sil function '$s4main5indexys5Int32VSo16ReadOnlyIntArrayVz_ADtF' +// CHECK: [[OP:%.*]] = function_ref [[READCLASSNAME:@(_ZNK16ReadOnlyIntArrayixEi|\?\?AReadOnlyIntArray@@QEBAAEBHH@Z)]] : $@convention(c) (@in ReadOnlyIntArray, Int32) -> UnsafePointer +// CHECK: [[PTR:%.*]] = apply [[OP]]([[ARRACCESS:%.*]], [[INDEX]]) : $@convention(c) (@in ReadOnlyIntArray, Int32) -> UnsafePointer +// CHECK: } // end sil function '$s4main5indexys5Int32VSo16ReadOnlyIntArrayV_ADtF' -// CHECK: sil shared [transparent] @$sSo16ReadOnlyIntArrayVys5Int32VADcig : $@convention(method) (Int32, @inout ReadOnlyIntArray) -> Int32 { +// CHECK: sil shared [transparent] @$sSo16ReadOnlyIntArrayVys5Int32VADcig : $@convention(method) (Int32, @in_guaranteed ReadOnlyIntArray) -> Int32 { // CHECK: bb0([[INDEX:%.*]] : $Int32, [[SELF:%.*]] : $*ReadOnlyIntArray): -// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[SELF]] : $*ReadOnlyIntArray -// CHECK: [[OP:%.*]] = function_ref [[READCLASSNAME]] : $@convention(c) (@inout ReadOnlyIntArray, Int32) -> UnsafePointer -// CHECK: [[PTR:%.*]] = apply [[OP]]([[SELFACCESS]], [[INDEX]]) : $@convention(c) (@inout ReadOnlyIntArray, Int32) -> UnsafePointer -// CHECK: end_access [[SELFACCESS]] : $*ReadOnlyIntArray +// CHECK: [[OP:%.*]] = function_ref [[READCLASSNAME]] : $@convention(c) (@in ReadOnlyIntArray, Int32) -> UnsafePointer +// CHECK: [[PTR:%.*]] = apply [[OP]]([[SELFACCESS:%.*]], [[INDEX]]) : $@convention(c) (@in ReadOnlyIntArray, Int32) -> UnsafePointer // CHECK: [[PTR2:%.*]] = struct_extract [[PTR]] : $UnsafePointer, #UnsafePointer._rawValue // CHECK: pointer_to_address [[PTR2]] : $Builtin.RawPointer to [strict] $*Int32 // CHECK: } // end sil function '$sSo16ReadOnlyIntArrayVys5Int32VADcig' @@ -77,18 +73,14 @@ public func index(_ arr: inout NonTrivialIntArrayByVal, _ arg: Int32, _ val: Int // CHECK: sil @$s4main5indexys5Int32VSo23NonTrivialIntArrayByValVz_A2DtF : $@convention(thin) (@inout NonTrivialIntArrayByVal, Int32, Int32) -> Int32 { // CHECK: bb0([[ARR:%.*]] : $*NonTrivialIntArrayByVal, [[INDEX:%.*]] : $Int32, [[NEWVALUE:%.*]] : $Int32): -// CHECK: [[ARRACCESS:%.*]] = begin_access [modify] [static] [[ARR]] : $*NonTrivialIntArrayByVal -// CHECK: [[ARRACCESS2:%.*]] = begin_access [modify] [static] [[ARRACCESS]] : $*NonTrivialIntArrayByVal -// CHECK: [[OP:%.*]] = function_ref [[READWRITECLASSNAMEBYVAL:@(_ZNK23NonTrivialIntArrayByValixEi|\?\?ANonTrivialIntArrayByVal@@QEBAHH@Z)]] : $@convention(c) (@inout NonTrivialIntArrayByVal, Int32) -> Int32 -// CHECK: [[PTR:%.*]] = apply [[OP]]([[ARRACCESS2]], [[INDEX]]) : $@convention(c) (@inout NonTrivialIntArrayByVal, Int32) -> Int32 +// CHECK: [[OP:%.*]] = function_ref [[READWRITECLASSNAMEBYVAL:@(_ZNK23NonTrivialIntArrayByValixEi|\?\?ANonTrivialIntArrayByVal@@QEBAHH@Z)]] : $@convention(c) (@in NonTrivialIntArrayByVal, Int32) -> Int32 +// CHECK: [[PTR:%.*]] = apply [[OP]]([[ARRACCESS:%.*]], [[INDEX]]) : $@convention(c) (@in NonTrivialIntArrayByVal, Int32) -> Int32 // CHECK: } // end sil function '$s4main5indexys5Int32VSo23NonTrivialIntArrayByValVz_A2DtF' -// CHECK: sil shared [transparent] @$sSo23NonTrivialIntArrayByValVys5Int32VADcig : $@convention(method) (Int32, @inout NonTrivialIntArrayByVal) -> Int32 { +// CHECK: sil shared [transparent] @$sSo23NonTrivialIntArrayByValVys5Int32VADcig : $@convention(method) (Int32, @in_guaranteed NonTrivialIntArrayByVal) -> Int32 { // CHECK: bb0([[NEWVALUE:%.*]] : $Int32, [[INDEX:%.*]] : $*NonTrivialIntArrayByVal): -// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[INDEX]] : $*NonTrivialIntArrayByVal -// CHECK: [[OP:%.*]] = function_ref [[READWRITECLASSNAMEBYVAL]] : $@convention(c) (@inout NonTrivialIntArrayByVal, Int32) -> Int32 -// CHECK: [[PTR:%.*]] = apply [[OP]]([[SELFACCESS]], [[NEWVALUE]]) : $@convention(c) (@inout NonTrivialIntArrayByVal, Int32) -> Int32 -// CHECK: end_access [[SELFACCESS]] : $*NonTrivialIntArrayByVal +// CHECK: [[OP:%.*]] = function_ref [[READWRITECLASSNAMEBYVAL]] : $@convention(c) (@in NonTrivialIntArrayByVal, Int32) -> Int32 +// CHECK: [[PTR:%.*]] = apply [[OP]]([[SELFACCESS:%.*]], [[NEWVALUE]]) : $@convention(c) (@in NonTrivialIntArrayByVal, Int32) -> Int32 // CHECK: } // end sil function '$sSo23NonTrivialIntArrayByValVys5Int32VADcig public func index(_ arr: inout PtrByVal, _ arg: Int32, _ val: Int32) -> Int32 { arr[arg]![0] } @@ -142,21 +134,17 @@ public func index(_ arr: inout PtrToPtr, _ arg: Int32, _ val: Int32) -> Int32 { // CHECK: end_access [[SELFACCESS]] : $*PtrToPtr // CHECK: } // end sil function '$sSo05PtrToA0VySpySpys5Int32VGSgGSgADcig -public func index(_ arr: inout ConstOpPtrByVal, _ arg: Int32, _ val: Int32) -> Int32 { arr[arg]![0] } -// CHECK: sil @$s4main5indexys5Int32VSo15ConstOpPtrByValVz_A2DtF : $@convention(thin) (@inout ConstOpPtrByVal, Int32, Int32) -> Int32 { -// CHECK: bb0([[ARR:%.*]] : $*ConstOpPtrByVal, [[INDEX:%.*]] : $Int32, [[NEWVALUE:%.*]] : $Int32): -// CHECK: [[ARRACCESS:%.*]] = begin_access [modify] [static] [[ARR]] : $*ConstOpPtrByVal -// CHECK: [[ARRACCESS2:%.*]] = begin_access [modify] [static] [[ARRACCESS]] : $*ConstOpPtrByVal -// CHECK: [[OP:%.*]] = function_ref [[CONSTOPPTRBYVAL:@(_ZNK15ConstOpPtrByValixEi|\?\?AConstOpPtrByVal@@QEBAPEBHH@Z)]] : $@convention(c) (@inout ConstOpPtrByVal, Int32) -> Optional> -// CHECK: [[PTR:%.*]] = apply [[OP]]([[ARRACCESS2]], [[INDEX]]) : $@convention(c) (@inout ConstOpPtrByVal, Int32) -> Optional> -// CHECK: } // end sil function '$s4main5indexys5Int32VSo15ConstOpPtrByValVz_A2DtF' - -// CHECK: sil shared [transparent] @$sSo15ConstOpPtrByValVySPys5Int32VGSgADcig : $@convention(method) (Int32, @inout ConstOpPtrByVal) -> Optional> { -// CHECK: bb0([[NEWVALUE:%.*]] : $Int32, [[INDEX:%.*]] : $*ConstOpPtrByVal): -// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[INDEX]] : $*ConstOpPtrByVal -// CHECK: [[OP:%.*]] = function_ref [[CONSTOPPTRBYVAL]] : $@convention(c) (@inout ConstOpPtrByVal, Int32) -> Optional> -// CHECK: [[PTR:%.*]] = apply [[OP]]([[SELFACCESS]], [[NEWVALUE]]) : $@convention(c) (@inout ConstOpPtrByVal, Int32) -> Optional> -// CHECK: end_access [[SELFACCESS]] : $*ConstOpPtrByVal +public func index(_ arr: ConstOpPtrByVal, _ arg: Int32, _ val: Int32) -> Int32 { arr[arg]![0] } +// CHECK: sil @$s4main5indexys5Int32VSo15ConstOpPtrByValV_A2DtF : $@convention(thin) (ConstOpPtrByVal, Int32, Int32) -> Int32 { +// CHECK: bb0([[ARR:%.*]] : $ConstOpPtrByVal, [[INDEX:%.*]] : $Int32, [[NEWVALUE:%.*]] : $Int32): +// CHECK: [[OP:%.*]] = function_ref [[CONSTOPPTRBYVAL:@(_ZNK15ConstOpPtrByValixEi|\?\?AConstOpPtrByVal@@QEBAPEBHH@Z)]] : $@convention(c) (@in ConstOpPtrByVal, Int32) -> Optional> +// CHECK: [[PTR:%.*]] = apply [[OP]]([[ARRACCESS2:%.*]], [[INDEX]]) : $@convention(c) (@in ConstOpPtrByVal, Int32) -> Optional> +// CHECK: } // end sil function '$s4main5indexys5Int32VSo15ConstOpPtrByValV_A2DtF' + +// CHECK: sil shared [transparent] @$sSo15ConstOpPtrByValVySPys5Int32VGSgADcig : $@convention(method) (Int32, ConstOpPtrByVal) -> Optional> { +// CHECK: bb0([[NEWVALUE:%.*]] : $Int32, [[INDEX:%.*]] : $ConstOpPtrByVal): +// CHECK: [[OP:%.*]] = function_ref [[CONSTOPPTRBYVAL]] : $@convention(c) (@in ConstOpPtrByVal, Int32) -> Optional> +// CHECK: [[PTR:%.*]] = apply [[OP]]([[SELFACCESS:%.*]], [[NEWVALUE]]) : $@convention(c) (@in ConstOpPtrByVal, Int32) -> Optional> // CHECK: } // end sil function '$sSo15ConstOpPtrByValVySPys5Int32VGSgADcig public func index(_ arr: inout ConstPtrByVal, _ arg: Int32, _ val: Int32) -> Int32 { arr[arg]![0] } @@ -176,12 +164,12 @@ public func index(_ arr: inout ConstPtrByVal, _ arg: Int32, _ val: Int32) -> Int // CHECK: end_access [[SELFACCESS]] : $*ConstPtrByVal // CHECK: } // end sil function '$sSo13ConstPtrByValVySPys5Int32VGSgADcig -// CHECK: sil [clang ReadOnlyIntArray.__operatorSubscriptConst] [[READCLASSNAME]] : $@convention(c) (@inout ReadOnlyIntArray, Int32) -> UnsafePointer +// CHECK: sil [clang ReadOnlyIntArray.__operatorSubscriptConst] [[READCLASSNAME]] : $@convention(c) (@in ReadOnlyIntArray, Int32) -> UnsafePointer // CHECK: sil [clang ReadWriteIntArray.__operatorSubscript] [[READWRITECLASSNAME]] : $@convention(c) (@inout ReadWriteIntArray, Int32) -> UnsafeMutablePointer -// CHECK: sil [clang NonTrivialIntArrayByVal.__operatorSubscriptConst] [[READWRITECLASSNAMEBYVAL]] : $@convention(c) (@inout NonTrivialIntArrayByVal, Int32) -> Int32 +// CHECK: sil [clang NonTrivialIntArrayByVal.__operatorSubscriptConst] [[READWRITECLASSNAMEBYVAL]] : $@convention(c) (@in NonTrivialIntArrayByVal, Int32) -> Int32 // CHECK: sil [clang PtrByVal.__operatorSubscript] [[PTRBYVAL]] : $@convention(c) (@inout PtrByVal, Int32) -> Optional> // CHECK: sil [clang RefToPtr.__operatorSubscript] [[REFTOPTR]] : $@convention(c) (@inout RefToPtr, Int32) -> UnsafeMutablePointer>> // CHECK: sil [clang PtrToPtr.__operatorSubscript] [[PTRTOPTR]] : $@convention(c) (@inout PtrToPtr, Int32) -> Optional>>> -// CHECK: sil [clang ConstOpPtrByVal.__operatorSubscriptConst] [[CONSTOPPTRBYVAL]] : $@convention(c) (@inout ConstOpPtrByVal, Int32) -> Optional> +// CHECK: sil [clang ConstOpPtrByVal.__operatorSubscriptConst] [[CONSTOPPTRBYVAL]] : $@convention(c) (@in ConstOpPtrByVal, Int32) -> Optional> // CHECK: sil [clang ConstPtrByVal.__operatorSubscriptConst] [[CONSTPTRBYVAL]] : $@convention(c) (@inout ConstPtrByVal, Int32) -> Optional> diff --git a/test/Interop/Cxx/operators/member-inline.swift b/test/Interop/Cxx/operators/member-inline.swift index 78292432444f0..4cbb16b711245 100644 --- a/test/Interop/Cxx/operators/member-inline.swift +++ b/test/Interop/Cxx/operators/member-inline.swift @@ -56,7 +56,7 @@ OperatorsTestSuite.test("ReadWriteIntArray.subscript (inline)") { } OperatorsTestSuite.test("ReadOnlyIntArray.subscript (inline)") { - var arr = ReadOnlyIntArray(1) + let arr = ReadOnlyIntArray(1) let result0 = arr[0] let result2 = arr[2] @@ -80,7 +80,7 @@ OperatorsTestSuite.test("WriteOnlyIntArray.subscript (inline)") { } OperatorsTestSuite.test("DifferentTypesArray.subscript (inline)") { - var arr = DifferentTypesArray() + let arr = DifferentTypesArray() let resultInt: Int32 = arr[2] let resultDouble: Double = arr[0.1] diff --git a/test/Interop/Cxx/operators/member-out-of-line-silgen.swift b/test/Interop/Cxx/operators/member-out-of-line-silgen.swift index 263dee35fbc6e..f857620f27635 100644 --- a/test/Interop/Cxx/operators/member-out-of-line-silgen.swift +++ b/test/Interop/Cxx/operators/member-out-of-line-silgen.swift @@ -5,12 +5,10 @@ import MemberOutOfLine -public func add(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> LoadableIntWrapper { lhs + rhs } +public func add(_ lhs: LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> LoadableIntWrapper { lhs + rhs } -// CHECK: bb0([[LHS:%.*]] : $*LoadableIntWrapper, [[RHS:%.*]] : $LoadableIntWrapper): -// CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] [[LHS]] : $*LoadableIntWrapper -// CHECK: [[FUNC:%.*]] = function_ref [[NAME:@_ZNK18LoadableIntWrapperplES_]] : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper -// CHECK: apply [[FUNC]]([[ACCESS]], [[RHS]]) : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper -// CHECK: end_access [[ACCESS]] : $*LoadableIntWrapper +// CHECK: bb0([[LHS:%.*]] : $LoadableIntWrapper, [[RHS:%.*]] : $LoadableIntWrapper): +// CHECK: [[FUNC:%.*]] = function_ref [[NAME:@_ZNK18LoadableIntWrapperplES_]] : $@convention(c) (@in LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper +// CHECK: apply [[FUNC]]([[ACCESS:%.*]], [[RHS]]) : $@convention(c) (@in LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper -// CHECK: sil [clang LoadableIntWrapper."+"] [[NAME]] : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper +// CHECK: sil [clang LoadableIntWrapper."+"] [[NAME]] : $@convention(c) (@in LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper diff --git a/test/Interop/Cxx/operators/member-out-of-line.swift b/test/Interop/Cxx/operators/member-out-of-line.swift index 6f9d92e8a6495..ef1e50e7a2c1b 100644 --- a/test/Interop/Cxx/operators/member-out-of-line.swift +++ b/test/Interop/Cxx/operators/member-out-of-line.swift @@ -15,7 +15,7 @@ import StdlibUnittest var OperatorsTestSuite = TestSuite("Operators") OperatorsTestSuite.test("LoadableIntWrapper.plus (out-of-line)") { - var lhs = LoadableIntWrapper(value: 42) + let lhs = LoadableIntWrapper(value: 42) let rhs = LoadableIntWrapper(value: 23) let result = lhs + rhs @@ -24,7 +24,7 @@ OperatorsTestSuite.test("LoadableIntWrapper.plus (out-of-line)") { } OperatorsTestSuite.test("LoadableIntWrapper.call (out-of-line)") { - var wrapper = LoadableIntWrapper(value: 42) + let wrapper = LoadableIntWrapper(value: 42) let resultNoArgs = wrapper() let resultOneArg = wrapper(23) @@ -36,7 +36,7 @@ OperatorsTestSuite.test("LoadableIntWrapper.call (out-of-line)") { } OperatorsTestSuite.test("AddressOnlyIntWrapper.call (out-of-line)") { - var wrapper = AddressOnlyIntWrapper(42) + let wrapper = AddressOnlyIntWrapper(42) let resultNoArgs = wrapper() let resultOneArg = wrapper(23) diff --git a/test/Interop/Cxx/operators/non-member-inline-typechecker.swift b/test/Interop/Cxx/operators/non-member-inline-typechecker.swift index b0ed9d6429d99..56068cc2eff88 100644 --- a/test/Interop/Cxx/operators/non-member-inline-typechecker.swift +++ b/test/Interop/Cxx/operators/non-member-inline-typechecker.swift @@ -2,8 +2,8 @@ import NonMemberInline -var lhs = LoadableIntWrapper(value: 42) -var rhs = LoadableIntWrapper(value: 23) +let lhs = LoadableIntWrapper(value: 42) +let rhs = LoadableIntWrapper(value: 23) let resultPlus = lhs + rhs let resultMinus = lhs - rhs diff --git a/test/Interop/Cxx/static/Inputs/static-member-var.h b/test/Interop/Cxx/static/Inputs/static-member-var.h index e71129b828475..bd5a996741b30 100644 --- a/test/Interop/Cxx/static/Inputs/static-member-var.h +++ b/test/Interop/Cxx/static/Inputs/static-member-var.h @@ -37,6 +37,12 @@ class WithConstexprStaticMember { constexpr static float definedInlineFromMethod = getFloatValue(); }; +class WithStaticAndInstanceMember { +public: + int myInstance; + static int myStatic; +}; + class ClassA { public: static int notUniqueName; diff --git a/test/Interop/Cxx/static/static-member-var-module-interface.swift b/test/Interop/Cxx/static/static-member-var-module-interface.swift new file mode 100644 index 0000000000000..b4c1d885592e4 --- /dev/null +++ b/test/Interop/Cxx/static/static-member-var-module-interface.swift @@ -0,0 +1,8 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=StaticMemberVar -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: struct WithStaticAndInstanceMember { +// CHECK-NEXT: static var myStatic: Int32 +// CHECK-NEXT: var myInstance: Int32 +// CHECK-NEXT: init() +// CHECK-NEXT: init(myInstance: Int32) +// CHECK-NEXT: } diff --git a/test/Interop/Cxx/static/static-member-var.swift b/test/Interop/Cxx/static/static-member-var.swift index 3cd6ea9c267d1..cacff419ce9f2 100644 --- a/test/Interop/Cxx/static/static-member-var.swift +++ b/test/Interop/Cxx/static/static-member-var.swift @@ -90,4 +90,9 @@ StaticMemberVarTestSuite.test("no-collisions") { expectEqual(169, ClassB.notUniqueName) } +StaticMemberVarTestSuite.test("init-struct-with-static-member") { + let obj = WithStaticAndInstanceMember(myInstance: 123) + expectEqual(123, obj.myInstance) +} + runAllTests() diff --git a/test/Interop/Cxx/templates/canonical-types-module-interface.swift b/test/Interop/Cxx/templates/canonical-types-module-interface.swift index 735e2dafe5257..a90bcff190293 100644 --- a/test/Interop/Cxx/templates/canonical-types-module-interface.swift +++ b/test/Interop/Cxx/templates/canonical-types-module-interface.swift @@ -4,13 +4,13 @@ // CHECK-NEXT: var t: IntWrapper // CHECK-NEXT: init() // CHECK-NEXT: init(t: IntWrapper) -// CHECK-NEXT: mutating func getValuePlusArg(_ arg: Int32) -> Int32 +// CHECK-NEXT: func getValuePlusArg(_ arg: Int32) -> Int32 // CHECK-NEXT: } // CHECK-NEXT: struct IntWrapper { // CHECK-NEXT: var value: Int32 // CHECK-NEXT: init() // CHECK-NEXT: init(value: Int32) -// CHECK-NEXT: mutating func getValue() -> Int32 +// CHECK-NEXT: func getValue() -> Int32 // CHECK-NEXT: } // CHECK-NEXT: typealias WrappedMagicNumberA = __CxxTemplateInst12MagicWrapperI10IntWrapperE // CHECK-NEXT: typealias WrappedMagicNumberB = __CxxTemplateInst12MagicWrapperI10IntWrapperE diff --git a/test/Interop/Cxx/templates/member-templates-module-interface.swift b/test/Interop/Cxx/templates/member-templates-module-interface.swift index 173e05e7510b4..6b8025da8c1bb 100644 --- a/test/Interop/Cxx/templates/member-templates-module-interface.swift +++ b/test/Interop/Cxx/templates/member-templates-module-interface.swift @@ -6,8 +6,8 @@ // CHECK: mutating func addAll(_ a: Int32, _ b: T, _ c: U) -> Int32 // CHECK: mutating func passThrough(_ val: T) -> T // CHECK: mutating func passThroughConst(_ val: T) -> T -// CHECK: mutating func passThroughOnConst(_ val: T) -> T -// CHECK: mutating func passThroughConstOnConst(_ val: T) -> T +// CHECK: func passThroughOnConst(_ val: T) -> T +// CHECK: func passThroughConstOnConst(_ val: T) -> T // CHECK: mutating func doNothingConstRef(_ val: UnsafePointer) // CHECK: mutating func make42Ref(_ val: UnsafeMutablePointer) // CHECK: } diff --git a/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift b/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift index 0f2b7f7b57c6e..5db63dc785cbe 100644 --- a/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift +++ b/test/Interop/Cxx/templates/not-pre-defined-class-template-module-interface.swift @@ -4,12 +4,12 @@ // CHECK-NEXT: var t: IntWrapper // CHECK-NEXT: init() // CHECK-NEXT: init(t: IntWrapper) -// CHECK-NEXT: mutating func getValuePlusArg(_ arg: Int32) -> Int32 +// CHECK-NEXT: func getValuePlusArg(_ arg: Int32) -> Int32 // CHECK-NEXT: } // CHECK-NEXT: struct IntWrapper { // CHECK-NEXT: var value: Int32 // CHECK-NEXT: init() // CHECK-NEXT: init(value: Int32) -// CHECK-NEXT: mutating func getValue() -> Int32 +// CHECK-NEXT: func getValue() -> Int32 // CHECK-NEXT: } // CHECK-NEXT: typealias MagicallyWrappedIntWithoutDefinition = __CxxTemplateInst12MagicWrapperI10IntWrapperE diff --git a/test/Interop/Cxx/templates/partially-pre-defined-class-template-silgen.swift b/test/Interop/Cxx/templates/partially-pre-defined-class-template-silgen.swift index a65be03154590..f6054e9014cd2 100644 --- a/test/Interop/Cxx/templates/partially-pre-defined-class-template-silgen.swift +++ b/test/Interop/Cxx/templates/partially-pre-defined-class-template-silgen.swift @@ -4,7 +4,7 @@ import PartiallyPreDefinedClassTemplate public func getWrappedMagicInt() -> CInt { let myInt = IntWrapper(value: 21) - var magicInt = PartiallyPreDefinedMagicallyWrappedInt(t: myInt) + let magicInt = PartiallyPreDefinedMagicallyWrappedInt(t: myInt) return magicInt.getValuePlusArg(32) } @@ -13,9 +13,9 @@ public func getWrappedMagicInt() -> CInt { // CHECK: [[INT_WRAPPER:%.*]] = struct $IntWrapper ([[_:%.*]] : $Int32) // CHECK: [[_:%.*]] = struct $__CxxTemplateInst12MagicWrapperI10IntWrapperE ([[INT_WRAPPER]] : $IntWrapper) // CHECK: // function_ref {{_ZNK12MagicWrapperI10IntWrapperE15getValuePlusArgEi|\?getValuePlusArg@\?\$MagicWrapper@UIntWrapper@@@@QEBAHH@Z}} -// CHECK: [[_:%.*]] = function_ref @{{_ZNK12MagicWrapperI10IntWrapperE15getValuePlusArgEi|\?getValuePlusArg@\?\$MagicWrapper@UIntWrapper@@@@QEBAHH@Z}} : $@convention(c) (@inout __CxxTemplateInst12MagicWrapperI10IntWrapperE, Int32) -> Int32 +// CHECK: [[_:%.*]] = function_ref @{{_ZNK12MagicWrapperI10IntWrapperE15getValuePlusArgEi|\?getValuePlusArg@\?\$MagicWrapper@UIntWrapper@@@@QEBAHH@Z}} : $@convention(c) (@in __CxxTemplateInst12MagicWrapperI10IntWrapperE, Int32) -> Int32 // CHECK: // {{_ZNK12MagicWrapperI10IntWrapperE15getValuePlusArgEi|\?getValuePlusArg@\?\$MagicWrapper@UIntWrapper@@@@QEBAHH@Z}} // CHECK: MagicWrapper::getValuePlusArg -// CHECK: sil [clang __CxxTemplateInst12MagicWrapperI10IntWrapperE.getValuePlusArg] @{{_ZNK12MagicWrapperI10IntWrapperE15getValuePlusArgEi|\?getValuePlusArg@\?\$MagicWrapper@UIntWrapper@@@@QEBAHH@Z}} : $@convention(c) (@inout __CxxTemplateInst12MagicWrapperI10IntWrapperE, Int32) -> Int32 +// CHECK: sil [clang __CxxTemplateInst12MagicWrapperI10IntWrapperE.getValuePlusArg] @{{_ZNK12MagicWrapperI10IntWrapperE15getValuePlusArgEi|\?getValuePlusArg@\?\$MagicWrapper@UIntWrapper@@@@QEBAHH@Z}} : $@convention(c) (@in __CxxTemplateInst12MagicWrapperI10IntWrapperE, Int32) -> Int32 diff --git a/test/Interop/Cxx/templates/using-directive-module-interface.swift b/test/Interop/Cxx/templates/using-directive-module-interface.swift index cee692e4dc45c..97ef9d83463e2 100644 --- a/test/Interop/Cxx/templates/using-directive-module-interface.swift +++ b/test/Interop/Cxx/templates/using-directive-module-interface.swift @@ -4,12 +4,12 @@ // CHECK-NEXT: var t: IntWrapper // CHECK-NEXT: init() // CHECK-NEXT: init(t: IntWrapper) -// CHECK-NEXT: mutating func getValuePlusArg(_ arg: Int32) -> Int32 +// CHECK-NEXT: func getValuePlusArg(_ arg: Int32) -> Int32 // CHECK-NEXT: } // CHECK-NEXT: struct IntWrapper { // CHECK-NEXT: var value: Int32 // CHECK-NEXT: init() // CHECK-NEXT: init(value: Int32) -// CHECK-NEXT: mutating func getValue() -> Int32 +// CHECK-NEXT: func getValue() -> Int32 // CHECK-NEXT: } // CHECK-NEXT: typealias UsingWrappedMagicNumber = __CxxTemplateInst12MagicWrapperI10IntWrapperE diff --git a/test/Interop/Cxx/union/Inputs/anonymous-union-partly-invalid.h b/test/Interop/Cxx/union/Inputs/anonymous-union-partly-invalid.h new file mode 100644 index 0000000000000..a913d3189f02d --- /dev/null +++ b/test/Interop/Cxx/union/Inputs/anonymous-union-partly-invalid.h @@ -0,0 +1,16 @@ +@class C; +@interface C +{} +@end + +struct S { + union { + C *t; + char c; + }; + S(const S &s) {} + ~S() { } + int f() { return 42; } +}; + +S *getSPtr(); diff --git a/test/Interop/Cxx/union/Inputs/module.modulemap b/test/Interop/Cxx/union/Inputs/module.modulemap new file mode 100644 index 0000000000000..86e6509a27989 --- /dev/null +++ b/test/Interop/Cxx/union/Inputs/module.modulemap @@ -0,0 +1,4 @@ +module AnonymousUnionPartlyInvalid { + header "anonymous-union-partly-invalid.h" + requires cplusplus +} diff --git a/test/Interop/Cxx/union/anonymous-union-partly-invalid-module-interface.swift b/test/Interop/Cxx/union/anonymous-union-partly-invalid-module-interface.swift new file mode 100644 index 0000000000000..f4e4ec7a755ee --- /dev/null +++ b/test/Interop/Cxx/union/anonymous-union-partly-invalid-module-interface.swift @@ -0,0 +1,8 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=AnonymousUnionPartlyInvalid -I %S/Inputs -source-filename=x -enable-cxx-interop -enable-objc-interop | %FileCheck %s + +// CHECK: class C { +// CHECK-NEXT: } +// CHECK-NEXT: struct S { +// CHECK-NEXT: mutating func f() -> Int32 +// CHECK-NEXT: } +// CHECK-NEXT: func getSPtr() -> UnsafeMutablePointer! diff --git a/test/Interop/Cxx/union/anonymous-union-partly-invalid.swift b/test/Interop/Cxx/union/anonymous-union-partly-invalid.swift new file mode 100644 index 0000000000000..1462b0cab6060 --- /dev/null +++ b/test/Interop/Cxx/union/anonymous-union-partly-invalid.swift @@ -0,0 +1,13 @@ +// RUN: %swift -I %S/Inputs -enable-cxx-interop -enable-objc-interop -emit-ir %s | %FileCheck %s + +import AnonymousUnionPartlyInvalid + +let sPtr = getSPtr() +let a = sPtr![0].f() + +// CHECK: i32 @main +// CHECK-NEXT: entry: +// CHECK-NEXT: bitcast +// CHECK-NEXT: call %struct.S +// CHECK-NEXT: ptrtoint %struct.S + diff --git a/test/Interpreter/Inputs/rdar80847020.h b/test/Interpreter/Inputs/rdar80847020.h new file mode 100644 index 0000000000000..ea58e8fcaad81 --- /dev/null +++ b/test/Interpreter/Inputs/rdar80847020.h @@ -0,0 +1,9 @@ +#import + +#pragma clang assume_nonnull begin + +@interface Clazz : NSObject +-(void)doSomethingMultiResultFlaggyWithCompletionHandler:(void (^)(BOOL, NSString *_Nullable, NSError *_Nullable, NSString *_Nullable))completionHandler __attribute__((swift_async_error(zero_argument, 1))); +@end + +#pragma clang assume_nonnull end diff --git a/test/Interpreter/Inputs/rdar80847020.m b/test/Interpreter/Inputs/rdar80847020.m new file mode 100644 index 0000000000000..0e78ad771fe0d --- /dev/null +++ b/test/Interpreter/Inputs/rdar80847020.m @@ -0,0 +1,11 @@ +#include "rdar80847020.h" + +#pragma clang assume_nonnull begin + +@implementation Clazz +-(void)doSomethingMultiResultFlaggyWithCompletionHandler:(void (^)(BOOL, NSString *_Nullable, NSError *_Nullable, NSString *_Nullable))completionHandler __attribute__((swift_async_error(zero_argument, 1))) { + completionHandler(YES, @"hi", NULL, @"bye"); +} +@end + +#pragma clang assume_nonnull end diff --git a/test/Interpreter/Inputs/resilient_async.swift b/test/Interpreter/Inputs/resilient_async.swift new file mode 100644 index 0000000000000..bb52fbc1751c8 --- /dev/null +++ b/test/Interpreter/Inputs/resilient_async.swift @@ -0,0 +1,9 @@ +public protocol Problem : class { +} + +public extension Problem { +} + +public func callGenericWitness (_ t: T) async -> Int { + return 0 +} diff --git a/test/Interpreter/Inputs/resilient_async2.swift b/test/Interpreter/Inputs/resilient_async2.swift new file mode 100644 index 0000000000000..820886fa5ef64 --- /dev/null +++ b/test/Interpreter/Inputs/resilient_async2.swift @@ -0,0 +1,13 @@ +public protocol Problem : class { + func theProblem() async -> Int +} + +public extension Problem { + func theProblem() async -> Int { + return 1 + } +} + +public func callGenericWitness (_ t: T) async -> Int { + return await t.theProblem() +} diff --git a/test/Interpreter/SDK/check_class_for_archiving.swift b/test/Interpreter/SDK/check_class_for_archiving.swift index cbfd9bc21572b..c1d8c72370d63 100644 --- a/test/Interpreter/SDK/check_class_for_archiving.swift +++ b/test/Interpreter/SDK/check_class_for_archiving.swift @@ -64,13 +64,13 @@ suite.test("NSKeyedUnarchiver") { } // Disable negative tests on older OSes because of rdar://problem/50504765 -if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { +if #available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) { suite.test("PrivateClass") { expectNotEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(PrivateClass.self, operation: op)) } } -if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { // Generic classes and nested classes were considered to have unstable names // in earlier releases. suite.test("GenericClass") { diff --git a/test/Interpreter/SDK/check_class_for_archiving_log.swift b/test/Interpreter/SDK/check_class_for_archiving_log.swift index 4e5810959485c..64ee1dda0fdbf 100644 --- a/test/Interpreter/SDK/check_class_for_archiving_log.swift +++ b/test/Interpreter/SDK/check_class_for_archiving_log.swift @@ -13,7 +13,7 @@ import Foundation // A tricky way to make the FileCheck tests conditional on the OS version. -if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { +if #available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) { print("-check-prefix=CHECK") } else { // Disable the checks for older OSes because of rdar://problem/50504765 diff --git a/test/Interpreter/SDK/objc_bridge_cast.swift b/test/Interpreter/SDK/objc_bridge_cast.swift index f28d381353a5d..1bdd2389c9472 100644 --- a/test/Interpreter/SDK/objc_bridge_cast.swift +++ b/test/Interpreter/SDK/objc_bridge_cast.swift @@ -2,6 +2,7 @@ // REQUIRES: executable_test // REQUIRES: objc_interop +// REQUIRES: rdar80079617 // Test dynamic casts that bridge value types through the runtime. diff --git a/test/Interpreter/SDK/objc_dealloc.swift b/test/Interpreter/SDK/objc_dealloc.swift index 9e30d171238dd..488e9f749643b 100644 --- a/test/Interpreter/SDK/objc_dealloc.swift +++ b/test/Interpreter/SDK/objc_dealloc.swift @@ -2,6 +2,7 @@ // REQUIRES: executable_test // REQUIRES: objc_interop +// REQUIRES: rdar80079617 import Foundation diff --git a/test/Interpreter/SDK/objc_swift3_deprecated_objc_inference.swift b/test/Interpreter/SDK/objc_swift3_deprecated_objc_inference.swift index 70f5dd5b71bdb..73c4d9a613e79 100644 --- a/test/Interpreter/SDK/objc_swift3_deprecated_objc_inference.swift +++ b/test/Interpreter/SDK/objc_swift3_deprecated_objc_inference.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift -swift-version 4 -Xfrontend -enable-swift3-objc-inference %s -o %t/a.out +// RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out 2>&1 | %FileCheck %s -check-prefix=CHECK_WARNINGS // RUN: env %env-SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT=0 %target-run %t/a.out 2>&1 | %FileCheck %s -check-prefix=CHECK_NOTHING @@ -45,15 +46,15 @@ DeprecatedObjCInferenceTestSuite.test("messagingObjCInference") { // CHECK_CRASH: ---Begin fputs("---Begin\n", stderr) - // CHECK_WARNINGS: .swift:29:3: implicit Objective-C entrypoint -[a.MyClass foo] - // CHECK_CRASH: .swift:29:3: implicit Objective-C entrypoint -[a.MyClass foo] + // CHECK_WARNINGS: .swift:30:3: implicit Objective-C entrypoint -[a.MyClass foo] + // CHECK_CRASH: .swift:30:3: implicit Objective-C entrypoint -[a.MyClass foo] x.perform(Selector(fooSel)) - // CHECK_WARNINGS-NOT: .swift:29:3: implicit Objective-C entrypoint -[a.MyClass foo] + // CHECK_WARNINGS-NOT: .swift:30:3: implicit Objective-C entrypoint -[a.MyClass foo] x.perform(Selector(fooSel)) - // CHECK_WARNINGS: .swift:30:3: implicit Objective-C entrypoint +[a.MyClass bar] + // CHECK_WARNINGS: .swift:31:3: implicit Objective-C entrypoint +[a.MyClass bar] type(of: x).perform(Selector(barSel)) - // CHECK_WARNINGS-NOT: .swift:30:3: implicit Objective-C entrypoint +[a.MyClass bar] + // CHECK_WARNINGS-NOT: .swift:31:3: implicit Objective-C entrypoint +[a.MyClass bar] type(of: x).perform(Selector(barSel)) // CHECK_NOTHING-NEXT: ---End diff --git a/test/Interpreter/SDK/protocol_lookup_foreign.swift b/test/Interpreter/SDK/protocol_lookup_foreign.swift index ae864b08efc1e..6a27ed14a3d9a 100644 --- a/test/Interpreter/SDK/protocol_lookup_foreign.swift +++ b/test/Interpreter/SDK/protocol_lookup_foreign.swift @@ -42,7 +42,7 @@ ProtocolLookupForeign.test("NSPoint") { } ProtocolLookupForeign.test("CFSet") { - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { expectEqual("CFSet", fooify(CFSetCreate(kCFAllocatorDefault, nil, 0, nil)!)) } } diff --git a/test/Interpreter/actor_class_forbid_objc_assoc_objects.swift b/test/Interpreter/actor_class_forbid_objc_assoc_objects.swift index d5f2f30b5e99e..fb0700d115e6e 100644 --- a/test/Interpreter/actor_class_forbid_objc_assoc_objects.swift +++ b/test/Interpreter/actor_class_forbid_objc_assoc_objects.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swiftc_driver -Xfrontend -enable-experimental-concurrency %s -o %t/out +// RUN: %target-swiftc_driver -Xfrontend -disable-availability-checking %s -o %t/out +// RUN: %target-codesign %t/out // RUN: %target-run %t/out // REQUIRES: concurrency diff --git a/test/Interpreter/actor_subclass_metatypes.swift b/test/Interpreter/actor_subclass_metatypes.swift index bfc8a7cddc824..8491531dc2133 100644 --- a/test/Interpreter/actor_subclass_metatypes.swift +++ b/test/Interpreter/actor_subclass_metatypes.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swiftc_driver -Xfrontend -enable-experimental-concurrency %s -o %t/out +// RUN: %target-swiftc_driver -Xfrontend -disable-availability-checking %s -o %t/out +// RUN: %target-codesign %t/out // RUN: %target-run %t/out // REQUIRES: concurrency diff --git a/test/Interpreter/async.swift b/test/Interpreter/async.swift index d76bc2997715a..ffbff1999c4b2 100644 --- a/test/Interpreter/async.swift +++ b/test/Interpreter/async.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -parse-as-library -module-name main -o %t/main +// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s -parse-as-library -module-name main -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/Interpreter/async_dynamic.swift b/test/Interpreter/async_dynamic.swift index 98989704e6f9f..ef7de7000c9b8 100644 --- a/test/Interpreter/async_dynamic.swift +++ b/test/Interpreter/async_dynamic.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -parse-as-library -module-name main -o %t/main +// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s -parse-as-library -module-name main -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/Interpreter/async_dynamic_replacement.swift b/test/Interpreter/async_dynamic_replacement.swift index 66e2409f44209..7353c395d91d3 100644 --- a/test/Interpreter/async_dynamic_replacement.swift +++ b/test/Interpreter/async_dynamic_replacement.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -parse-as-library -module-name main -o %t/main +// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s -parse-as-library -module-name main -o %t/main // RUN: %target-codesign %t/main // RUN: %target-run %t/main | %FileCheck %s diff --git a/test/Interpreter/async_fib.swift b/test/Interpreter/async_fib.swift new file mode 100644 index 0000000000000..104b0bd111ce2 --- /dev/null +++ b/test/Interpreter/async_fib.swift @@ -0,0 +1,60 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s -parse-as-library -module-name main -o %t/main +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: concurrency +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: OS=windows-msvc + +var gg = 0 + +@inline(never) +public func identity(_ x: T) -> T { + gg += 1 + return x +} + +actor Actor { + var x: Int = 0 + init(x: Int) { self.x = x } + + @inline(never) + func get(_ i: Int ) async -> Int { + return i + x + } +} + +// Used to crash with an stack overflow with m >= 18 +let m = 22 + +@inline(never) +func asyncFib(_ n: Int, _ a1: Actor, _ a2: Actor) async -> Int { + if n == 0 { + return await a1.get(n) + } + if n == 1 { + return await a2.get(n) + } + + let first = await asyncFib(n-2, a1, a2) + let second = await asyncFib(n-1, a1, a2) + + let result = first + second + + return result +} + +@main struct Main { + static func main() async { + let a1 = Actor(x: 0) + let a2 = Actor(x: 0) + _ = await asyncFib(identity(m), a1, a2) + + // CHECK: result: 0 + await print("result: \(a1.x)"); + await print(a2.x) + } +} diff --git a/test/Interpreter/async_resilience.swift b/test/Interpreter/async_resilience.swift new file mode 100644 index 0000000000000..9ba58ab0f544f --- /dev/null +++ b/test/Interpreter/async_resilience.swift @@ -0,0 +1,31 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_async)) -enable-library-evolution %S/Inputs/resilient_async.swift -emit-module -emit-module-path %t/resilient_async.swiftmodule -Xfrontend -disable-availability-checking -module-name resilient_async +// RUN: %target-codesign %t/%target-library-name(resilient_async) + +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -parse-as-library %s -lresilient_async -I %t -L %t -o %t/main %target-rpath(%t) +// RUN: %target-codesign %t/main + +// Introduce a defaulted protocol method. +// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_async)) -enable-library-evolution -Xfrontend -disable-availability-checking %S/Inputs/resilient_async2.swift -emit-module -emit-module-path %t/resilient_async.swiftmodule -module-name resilient_async +// RUN: %target-codesign %t/%target-library-name(resilient_async) + +// RUN: %target-run %t/main %t/%target-library-name(resilient_async) + +// REQUIRES: executable_test +// REQUIRES: concurrency +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import resilient_async + +class Impl : Problem {} + +@main struct Main { + static func main() async { + let i = Impl() + // This used to crash. + let r = await callGenericWitness(i) + assert(r == 1) + } +} diff --git a/test/Interpreter/indirect_enum.swift b/test/Interpreter/indirect_enum.swift index 003bda788195d..cbba08c5128d6 100644 --- a/test/Interpreter/indirect_enum.swift +++ b/test/Interpreter/indirect_enum.swift @@ -5,9 +5,6 @@ // REQUIRES: executable_test // REQUIRES: asan_runtime -// rdar://problem/47367694 tracks re-enabling this test for backward deployment. -// UNSUPPORTED: remote_run - // Make sure that we do not use-after-free here. class Box { diff --git a/test/Interpreter/objc_extensions.swift b/test/Interpreter/objc_extensions.swift index 5489626b78f21..f02b85b2664a9 100644 --- a/test/Interpreter/objc_extensions.swift +++ b/test/Interpreter/objc_extensions.swift @@ -62,3 +62,27 @@ let z: AnyObject = NSObject() // CHECK: 123 print(z.sillyMethod()) + +// Category on an ObjC generic class using a type-pinning parameter, including +// a cast to an existential metatype +@objc protocol CacheNameDefaulting { + static var defaultCacheName: String { get } +} +extension OuterClass.InnerClass: CacheNameDefaulting { + static var defaultCacheName: String { "InnerClasses" } +} + +extension NSCache { + @objc convenience init(ofObjects objectTy: ObjectType.Type, forKeys keyTy: KeyType.Type) { + self.init() + + if let defaultNameTy = objectTy as? CacheNameDefaulting.Type { + self.name = defaultNameTy.defaultCacheName + } + } +} + +let cache = NSCache(ofObjects: OuterClass.InnerClass.self, forKeys: NSString.self) + +// CHECK: InnerClasses +print(cache.name) diff --git a/test/Interpreter/objc_extensions_jit.swift b/test/Interpreter/objc_extensions_jit.swift index b3a6f32f82ed5..e9068ade50950 100644 --- a/test/Interpreter/objc_extensions_jit.swift +++ b/test/Interpreter/objc_extensions_jit.swift @@ -1,3 +1,4 @@ +// REQUIRES: rdar80923668 // RUN: %target-jit-run %S/objc_extensions.swift | %FileCheck %S/objc_extensions.swift // REQUIRES: executable_test // REQUIRES: objc_interop diff --git a/test/Interpreter/process_arguments.swift b/test/Interpreter/process_arguments.swift index 5be68551a0b51..9f65fa76ac8f1 100644 --- a/test/Interpreter/process_arguments.swift +++ b/test/Interpreter/process_arguments.swift @@ -1,7 +1,7 @@ -// RUN: %swift -interpret %s | %FileCheck %s -check-prefix=CHECK-NONE -// RUN: %swift -interpret %s -Onone -g | %FileCheck %s -check-prefix=CHECK-NONE -// RUN: %swift -interpret %s -Onone -g -- | %FileCheck %s -check-prefix=CHECK-NONE -// RUN: %swift -interpret %s -Onone -g -- a b c | %FileCheck %s -check-prefix=CHECK-THREE +// RUN: %target-jit-run %s | %FileCheck %s -check-prefix=CHECK-NONE +// RUN: %target-jit-run %s -Onone -g | %FileCheck %s -check-prefix=CHECK-NONE +// RUN: %target-jit-run %s -Onone -g -- | %FileCheck %s -check-prefix=CHECK-NONE +// RUN: %target-jit-run %s -Onone -g -- a b c | %FileCheck %s -check-prefix=CHECK-THREE // REQUIRES: swift_interpreter diff --git a/test/Interpreter/protocol_lookup_jit.swift b/test/Interpreter/protocol_lookup_jit.swift index 9ed8d8115fae3..49c20bd26fbc1 100644 --- a/test/Interpreter/protocol_lookup_jit.swift +++ b/test/Interpreter/protocol_lookup_jit.swift @@ -1,5 +1,5 @@ // Test protocol_lookup.swift in JIT mode. -// RUN: %swift -interpret %S/protocol_lookup.swift | %FileCheck %S/protocol_lookup.swift +// RUN: %target-jit-run %S/protocol_lookup.swift | %FileCheck %S/protocol_lookup.swift // REQUIRES: executable_test // REQUIRES: swift_interpreter diff --git a/test/Interpreter/rdar80847020.swift b/test/Interpreter/rdar80847020.swift new file mode 100644 index 0000000000000..e9a95d0793706 --- /dev/null +++ b/test/Interpreter/rdar80847020.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar80847020.m -I %S/Inputs -c -o %t/rdar80847020.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar80847020.h -Xlinker %t/rdar80847020.o -parse-as-library %s -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: objc_interop +// REQUIRES: concurrency + +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +func run(_ s: Clazz) async throws { + let res: (String, String) = try await s.doSomethingMultiResultFlaggy() + // CHECK: ("hi", "bye") + print(res) +} + +@main struct Main { + static func main() async throws { + try await run(Clazz()) + } +} diff --git a/test/Interpreter/sr10941.swift b/test/Interpreter/sr10941.swift new file mode 100644 index 0000000000000..f412eb7bfa91c --- /dev/null +++ b/test/Interpreter/sr10941.swift @@ -0,0 +1,35 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out +// REQUIRES: executable_test + +// https://bugs.swift.org/browse/SR-10941 + +public protocol SupportedPropertyType { } + +public protocol TypedSupportedType: SupportedPropertyType where FactoryType.BuildType == Self { + associatedtype FactoryType: TypedSupportedTypeFactory +} + +public protocol TypedSupportedTypeFactory { + associatedtype BuildType: SupportedPropertyType +} + +public class Factory: TypedSupportedTypeFactory { +} + +public struct ContentMode : TypedSupportedType { + public typealias FactoryType = Factory +} + +func bar(_: T.Type) {} + +func baz(_ t: T.Type) { + bar(t) +} + +// This used to recurse infinitely because the base witness table +// for 'ContentMode : SupportedPropertyType' was incorrectly +// minimized away. +baz(ContentMode.self) diff --git a/test/LLVMPasses/basic.ll b/test/LLVMPasses/basic.ll index 423d0eeecb708..66330ca18b9c4 100644 --- a/test/LLVMPasses/basic.ll +++ b/test/LLVMPasses/basic.ll @@ -369,6 +369,59 @@ bb1: ret void } +; CHECK-LABEL: @releasemotion_forwarding +; CHECK-NOT: swift_retain +; CHECK-NOT: swift_release +; CHECK: call void @user(%swift.refcounted* %P) +; CHECK: ret +define void @releasemotion_forwarding(%swift.refcounted* %P, i8* %O, %swift.bridge* %B) { +entry: + %res = tail call %swift.refcounted* @swift_retain(%swift.refcounted* %P) + tail call void @swift_release(%swift.refcounted* %res) nounwind + call void @user(%swift.refcounted* %res) nounwind + ret void +} + +; CHECK-LABEL: @retainmotion_forwarding +; CHECK: store %swift.refcounted* %P, %swift.refcounted** %R, align 4 +; CHECK-NOT: swift_retain +; CHECK-NOT: swift_release +; CHECK: ret +define void @retainmotion_forwarding(%swift.refcounted* %P, %swift.refcounted** %R, %swift.bridge* %B) { +entry: + %res = tail call %swift.refcounted* @swift_retain(%swift.refcounted* %P) + store %swift.refcounted* %res, %swift.refcounted** %R, align 4 + call void @swift_bridgeObjectRelease(%swift.bridge* %B) + tail call void @swift_release(%swift.refcounted* %res) nounwind + ret void +} + +; CHECK-LABEL: @unknownreleasemotion_forwarding +; CHECK-NOT: swift_unknownObjectRetain +; CHECK-NOT: swift_unknownObjectRelease +; CHECK: call void @user(%swift.refcounted* %P) +; CHECK: ret +define void @unknownreleasemotion_forwarding(%swift.refcounted* %P, i8* %O, %swift.bridge* %B) { +entry: + %res = tail call %swift.refcounted* @swift_unknownObjectRetain(%swift.refcounted* %P) + tail call void @swift_unknownObjectRelease(%swift.refcounted* %res) nounwind + call void @user(%swift.refcounted* %res) nounwind + ret void +} + +; CHECK-LABEL: @unknownretainmotion_forwarding +; CHECK: store %swift.refcounted* %P, %swift.refcounted** %R, align 4 +; CHECK-NOT: swift_unknownObjectRetain +; CHECK-NOT: swift_unknownObjectRelease +; CHECK: ret +define void @unknownretainmotion_forwarding(%swift.refcounted* %P, %swift.refcounted** %R, %swift.bridge* %B) { +entry: + %res = tail call %swift.refcounted* @swift_unknownObjectRetain(%swift.refcounted* %P) + store %swift.refcounted* %res, %swift.refcounted** %R, align 4 + call void @swift_bridgeObjectRelease(%swift.bridge* %B) + tail call void @swift_unknownObjectRelease(%swift.refcounted* %res) nounwind + ret void +} !llvm.dbg.cu = !{!1} !llvm.module.flags = !{!4} diff --git a/test/Migrator/always_remove_old_remap_file.swift b/test/Migrator/always_remove_old_remap_file.swift index 0c7515fdce38a..45ca6e2261757 100644 --- a/test/Migrator/always_remove_old_remap_file.swift +++ b/test/Migrator/always_remove_old_remap_file.swift @@ -1,13 +1,13 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/input.swift -// RUN: %target-swift-frontend -c -update-code -primary-file %t/input.swift -emit-migrated-file-path %t/always_remove_old_remap_file.result -emit-remap-file-path %t/always_remove_old_remap_file.remap -o /dev/null +// RUN: %target-swift-frontend -c -update-code -primary-file %t/input.swift -emit-migrated-file-path %t/always_remove_old_remap_file.result -emit-remap-file-path %t/always_remove_old_remap_file.remap %api_diff_data_dir -o /dev/null %api_diff_data_dir // RUN: ls %t/always_remove_old_remap_file.remap // Simulate leaving behind code that can't build in Swift 4: // RUN: echo asdfads >> %t/input.swift // Migrate again. This should delete the old remap file. -// RUN: not %target-swift-frontend -c -update-code -primary-file %t/input.swift -emit-migrated-file-path %t/always_remove_old_remap_file.result -emit-remap-file-path %t/always_remove_old_remap_file.remap -o /dev/null +// RUN: not %target-swift-frontend -c -update-code -primary-file %t/input.swift -emit-migrated-file-path %t/always_remove_old_remap_file.result -emit-remap-file-path %t/always_remove_old_remap_file.remap %api_diff_data_dir -o /dev/null %api_diff_data_dir // RUN: not ls %t/always_remove_old_remap_file.remap diff --git a/test/Migrator/fixit_flatMap.swift b/test/Migrator/fixit_flatMap.swift index aec01c03059ee..8bb4fc8eb87b6 100644 --- a/test/Migrator/fixit_flatMap.swift +++ b/test/Migrator/fixit_flatMap.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -typecheck %s -swift-version 4 -// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t.result -swift-version 4 +// RUN: %target-swift-frontend -typecheck %s -swift-version 4 %api_diff_data_dir +// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t.result -swift-version 4 %api_diff_data_dir // RUN: diff -u %s.expected %t.result // REQUIRES: VENDOR=apple diff --git a/test/Migrator/fixit_flatMap.swift.expected b/test/Migrator/fixit_flatMap.swift.expected index 05aa9df00519c..40ad11d3e7389 100644 --- a/test/Migrator/fixit_flatMap.swift.expected +++ b/test/Migrator/fixit_flatMap.swift.expected @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -typecheck %s -swift-version 4 -// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t.result -swift-version 4 +// RUN: %target-swift-frontend -typecheck %s -swift-version 4 %api_diff_data_dir +// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t.result -swift-version 4 %api_diff_data_dir // RUN: diff -u %s.expected %t.result // REQUIRES: VENDOR=apple diff --git a/test/Migrator/insert_replace_fixit.swift b/test/Migrator/insert_replace_fixit.swift index 6d383077ee987..8e123a9441a4e 100644 --- a/test/Migrator/insert_replace_fixit.swift +++ b/test/Migrator/insert_replace_fixit.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/result.swift -swift-version 4 +// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/result.swift -swift-version 4 %api_diff_data_dir // RUN: %diff -u %s.expected %t/result.swift import TestMyTime diff --git a/test/Migrator/insert_replace_fixit.swift.expected b/test/Migrator/insert_replace_fixit.swift.expected index e49ecaaaf68f5..7f05a3b48482a 100644 --- a/test/Migrator/insert_replace_fixit.swift.expected +++ b/test/Migrator/insert_replace_fixit.swift.expected @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/result.swift -swift-version 4 +// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/result.swift -swift-version 4 %api_diff_data_dir // RUN: %diff -u %s.expected %t/result.swift import TestMyTime diff --git a/test/Migrator/no_ast_passes_after_swift4.swift b/test/Migrator/no_ast_passes_after_swift4.swift index 606dd1ca5685d..1d62a75259d99 100644 --- a/test/Migrator/no_ast_passes_after_swift4.swift +++ b/test/Migrator/no_ast_passes_after_swift4.swift @@ -1,7 +1,7 @@ // REQUIRES: objc_interop -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/no_ast_passes_after_swift4.swift.result -emit-remap-file-path %t/no_ast_passes_after_swift4.swift.remap -o /dev/null -swift-version 4.2 +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/no_ast_passes_after_swift4.swift.result -emit-remap-file-path %t/no_ast_passes_after_swift4.swift.remap -o /dev/null -swift-version 4.2 %api_diff_data_dir // RUN: diff -u %S/no_ast_passes_after_swift4.swift.expected %t/no_ast_passes_after_swift4.swift.result -// RUN: %target-swift-frontend -typecheck -F %S/mock-sdk -swift-version 4 %t/no_ast_passes_after_swift4.swift.result +// RUN: %target-swift-frontend -typecheck -F %S/mock-sdk -swift-version 4 %t/no_ast_passes_after_swift4.swift.result %api_diff_data_dir import Bar func foo() -> FooComparisonResult { diff --git a/test/Migrator/no_ast_passes_after_swift4.swift.expected b/test/Migrator/no_ast_passes_after_swift4.swift.expected index 606dd1ca5685d..1d62a75259d99 100644 --- a/test/Migrator/no_ast_passes_after_swift4.swift.expected +++ b/test/Migrator/no_ast_passes_after_swift4.swift.expected @@ -1,7 +1,7 @@ // REQUIRES: objc_interop -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/no_ast_passes_after_swift4.swift.result -emit-remap-file-path %t/no_ast_passes_after_swift4.swift.remap -o /dev/null -swift-version 4.2 +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/no_ast_passes_after_swift4.swift.result -emit-remap-file-path %t/no_ast_passes_after_swift4.swift.remap -o /dev/null -swift-version 4.2 %api_diff_data_dir // RUN: diff -u %S/no_ast_passes_after_swift4.swift.expected %t/no_ast_passes_after_swift4.swift.result -// RUN: %target-swift-frontend -typecheck -F %S/mock-sdk -swift-version 4 %t/no_ast_passes_after_swift4.swift.result +// RUN: %target-swift-frontend -typecheck -F %S/mock-sdk -swift-version 4 %t/no_ast_passes_after_swift4.swift.result %api_diff_data_dir import Bar func foo() -> FooComparisonResult { diff --git a/test/Migrator/no_extraneous_argument_labels.swift b/test/Migrator/no_extraneous_argument_labels.swift index 98b2a5399e84c..a444b643ba74d 100644 --- a/test/Migrator/no_extraneous_argument_labels.swift +++ b/test/Migrator/no_extraneous_argument_labels.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -typecheck %s -swift-version 4 -// RUN: %empty-directory(%t) && %target-swift-frontend -c -primary-file %s -emit-migrated-file-path %t/no_extraneous_argument_labels.result -swift-version 4 -o /dev/null +// RUN: %target-swift-frontend -typecheck %s -swift-version 4 %api_diff_data_dir +// RUN: %empty-directory(%t) && %target-swift-frontend -c -primary-file %s -emit-migrated-file-path %t/no_extraneous_argument_labels.result -swift-version 4 -o /dev/null %api_diff_data_dir // RUN: %diff -u %s.expected %t/no_extraneous_argument_labels.result -// RUN: %target-swift-frontend -typecheck %s.expected -swift-version 5 +// RUN: %target-swift-frontend -typecheck %s.expected -swift-version 5 %api_diff_data_dir func foo(_ oc: [String]) { var args: [String] = [] diff --git a/test/Migrator/no_extraneous_argument_labels.swift.expected b/test/Migrator/no_extraneous_argument_labels.swift.expected index 98b2a5399e84c..a444b643ba74d 100644 --- a/test/Migrator/no_extraneous_argument_labels.swift.expected +++ b/test/Migrator/no_extraneous_argument_labels.swift.expected @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -typecheck %s -swift-version 4 -// RUN: %empty-directory(%t) && %target-swift-frontend -c -primary-file %s -emit-migrated-file-path %t/no_extraneous_argument_labels.result -swift-version 4 -o /dev/null +// RUN: %target-swift-frontend -typecheck %s -swift-version 4 %api_diff_data_dir +// RUN: %empty-directory(%t) && %target-swift-frontend -c -primary-file %s -emit-migrated-file-path %t/no_extraneous_argument_labels.result -swift-version 4 -o /dev/null %api_diff_data_dir // RUN: %diff -u %s.expected %t/no_extraneous_argument_labels.result -// RUN: %target-swift-frontend -typecheck %s.expected -swift-version 5 +// RUN: %target-swift-frontend -typecheck %s.expected -swift-version 5 %api_diff_data_dir func foo(_ oc: [String]) { var args: [String] = [] diff --git a/test/Migrator/no_var_to_let.swift b/test/Migrator/no_var_to_let.swift index 092cc793fdafb..d166d2d10854e 100644 --- a/test/Migrator/no_var_to_let.swift +++ b/test/Migrator/no_var_to_let.swift @@ -1,8 +1,8 @@ -// RUN: %target-swift-frontend -typecheck %s -swift-version 4 -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/no_var_to_let.swift.result -swift-version 4 -o /dev/null -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/no_var_to_let.swift.result -swift-version 4 -o /dev/null +// RUN: %target-swift-frontend -typecheck %s -swift-version 4 %api_diff_data_dir +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/no_var_to_let.swift.result -swift-version 4 -o /dev/null %api_diff_data_dir +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/no_var_to_let.swift.result -swift-version 4 -o /dev/null %api_diff_data_dir // RUN: %diff -u %s %t/no_var_to_let.swift.result -// RUN: %target-swift-frontend -typecheck %s -swift-version 5 +// RUN: %target-swift-frontend -typecheck %s -swift-version 5 %api_diff_data_dir // Note that the diff run line indicates that there should be no change. diff --git a/test/Migrator/null_migration.swift b/test/Migrator/null_migration.swift index 641e28d51bc10..a4eb7f5fde1f6 100644 --- a/test/Migrator/null_migration.swift +++ b/test/Migrator/null_migration.swift @@ -1,4 +1,4 @@ -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/migrated_null_migration.swift -emit-remap-file-path %t/null_migration.remap -o /dev/null +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/migrated_null_migration.swift -emit-remap-file-path %t/null_migration.remap -o /dev/null %api_diff_data_dir // RUN: %diff -u %s %t/migrated_null_migration.swift // This file tests that, if all migration passes are no-op, diff --git a/test/Migrator/optional_try_migration.swift b/test/Migrator/optional_try_migration.swift index b3a85218cf3c5..4d3c2d83215de 100644 --- a/test/Migrator/optional_try_migration.swift +++ b/test/Migrator/optional_try_migration.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -c -swift-version 4 -primary-file %s -emit-migrated-file-path %t/optional_try_migration.result.swift +// RUN: %target-swift-frontend -c -swift-version 4 -primary-file %s -emit-migrated-file-path %t/optional_try_migration.result.swift %api_diff_data_dir // RUN: %diff -u %S/optional_try_migration.swift.expected %t/optional_try_migration.result.swift func fetchOptInt() throws -> Int? { diff --git a/test/Migrator/optional_try_migration.swift.expected b/test/Migrator/optional_try_migration.swift.expected index dcb519949eab2..9d8a1f33f0569 100644 --- a/test/Migrator/optional_try_migration.swift.expected +++ b/test/Migrator/optional_try_migration.swift.expected @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -c -swift-version 4 -primary-file %s -emit-migrated-file-path %t/optional_try_migration.result.swift +// RUN: %target-swift-frontend -c -swift-version 4 -primary-file %s -emit-migrated-file-path %t/optional_try_migration.result.swift %api_diff_data_dir // RUN: %diff -u %S/optional_try_migration.swift.expected %t/optional_try_migration.result.swift func fetchOptInt() throws -> Int? { diff --git a/test/Migrator/post_fixit_pass.swift b/test/Migrator/post_fixit_pass.swift index a7249882c06be..95a44dfaa96bc 100644 --- a/test/Migrator/post_fixit_pass.swift +++ b/test/Migrator/post_fixit_pass.swift @@ -1,4 +1,4 @@ -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/post_fixit_pass.swift.result -o /dev/null -F %S/mock-sdk -swift-version 4 +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/post_fixit_pass.swift.result -o /dev/null -F %S/mock-sdk -swift-version 4 %api_diff_data_dir // RUN: %diff -u %S/post_fixit_pass.swift.expected %t/post_fixit_pass.swift.result #if swift(>=4.2) diff --git a/test/Migrator/post_fixit_pass.swift.expected b/test/Migrator/post_fixit_pass.swift.expected index 28a53ade51d09..45c39188b0ccc 100644 --- a/test/Migrator/post_fixit_pass.swift.expected +++ b/test/Migrator/post_fixit_pass.swift.expected @@ -1,4 +1,4 @@ -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/post_fixit_pass.swift.result -o /dev/null -F %S/mock-sdk -swift-version 4 +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/post_fixit_pass.swift.result -o /dev/null -F %S/mock-sdk -swift-version 4 %api_diff_data_dir // RUN: %diff -u %S/post_fixit_pass.swift.expected %t/post_fixit_pass.swift.result #if swift(>=4.2) diff --git a/test/Migrator/rdar31892850.swift b/test/Migrator/rdar31892850.swift index b8c861cc0a772..61c0aa2a97b7b 100644 --- a/test/Migrator/rdar31892850.swift +++ b/test/Migrator/rdar31892850.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -typecheck -primary-file %s -module-cache-path %t/mcp -emit-remap-file-path %t/edits.remap -swift-version 4 +// RUN: %target-swift-frontend -typecheck -primary-file %s -module-cache-path %t/mcp -emit-remap-file-path %t/edits.remap -swift-version 4 %api_diff_data_dir // RUN: %FileCheck %s -input-file=%t/edits.remap enum SomeStringEnum : String { @@ -15,12 +15,12 @@ func foo() { // CHECK:[ // CHECK: { // CHECK: "file": "{{.*}}rdar31892850.swift", -// CHECK: "offset": 325, -// CHECK: "text": "SomeStringEnum(rawValue: " +// CHECK-NEXT: "offset": 344, +// CHECK-NEXT: "text": "SomeStringEnum(rawValue: " // CHECK: }, // CHECK: { // CHECK: "file": "{{.*}}rdar31892850.swift", -// CHECK: "offset": 329, -// CHECK: "text": ") ?? <#default value#>" +// CHECK-NEXT: "offset": 348, +// CHECK-NEXT: "text": ") ?? <#default value#>" // CHECK: } // CHECK:] diff --git a/test/Migrator/remove_override.swift b/test/Migrator/remove_override.swift index 397dedbe7bf61..98a33c3dac30c 100644 --- a/test/Migrator/remove_override.swift +++ b/test/Migrator/remove_override.swift @@ -1,6 +1,6 @@ // REQUIRES: OS=macosx // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -c -update-code -swift-version 4 -disable-migrator-fixits -primary-file %s -emit-migrated-file-path %t/remove_override.result.swift -o %t/rename-func-decl.swift.remap +// RUN: %target-swift-frontend -c -update-code -swift-version 4 -disable-migrator-fixits -primary-file %s -emit-migrated-file-path %t/remove_override.result.swift -o %t/rename-func-decl.swift.remap %api_diff_data_dir // RUN: diff -u %S/remove_override.swift.expected %t/remove_override.result.swift import AppKit diff --git a/test/Migrator/remove_override.swift.expected b/test/Migrator/remove_override.swift.expected index db3f7ad7ec9b3..75c0bc0093734 100644 --- a/test/Migrator/remove_override.swift.expected +++ b/test/Migrator/remove_override.swift.expected @@ -1,6 +1,6 @@ // REQUIRES: OS=macosx // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -c -update-code -swift-version 4 -disable-migrator-fixits -primary-file %s -emit-migrated-file-path %t/remove_override.result.swift -o %t/rename-func-decl.swift.remap +// RUN: %target-swift-frontend -c -update-code -swift-version 4 -disable-migrator-fixits -primary-file %s -emit-migrated-file-path %t/remove_override.result.swift -o %t/rename-func-decl.swift.remap %api_diff_data_dir // RUN: diff -u %S/remove_override.swift.expected %t/remove_override.result.swift import AppKit diff --git a/test/Migrator/stdlib_rename.swift b/test/Migrator/stdlib_rename.swift index d5ae2b3f56b38..6a39bb2b5ae45 100644 --- a/test/Migrator/stdlib_rename.swift +++ b/test/Migrator/stdlib_rename.swift @@ -1,7 +1,7 @@ // REQUIRES: objc_interop -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/stdlib_rename.swift.result -emit-remap-file-path %t/stdlib_rename.swift.remap -o /dev/null +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/stdlib_rename.swift.result -emit-remap-file-path %t/stdlib_rename.swift.remap -o /dev/null %api_diff_data_dir // RUN: diff -u %S/stdlib_rename.swift.expected %t/stdlib_rename.swift.result -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/stdlib_rename.swift.result -emit-remap-file-path %t/stdlib_rename.swift.remap -o /dev/null -swift-version 4.2 +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/stdlib_rename.swift.result -emit-remap-file-path %t/stdlib_rename.swift.remap -o /dev/null -swift-version 4.2 %api_diff_data_dir // RUN: diff -u %S/stdlib_rename.swift.expected %t/stdlib_rename.swift.result func test1(_ a: [String], s: String) { @@ -10,4 +10,4 @@ func test1(_ a: [String], s: String) { } func test2(_ s: String, c: Character) { _ = s.index(of: c) -} \ No newline at end of file +} diff --git a/test/Migrator/stdlib_rename.swift.expected b/test/Migrator/stdlib_rename.swift.expected index 04dadba8cf63e..5174e069f1cff 100644 --- a/test/Migrator/stdlib_rename.swift.expected +++ b/test/Migrator/stdlib_rename.swift.expected @@ -1,7 +1,7 @@ // REQUIRES: objc_interop -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/stdlib_rename.swift.result -emit-remap-file-path %t/stdlib_rename.swift.remap -o /dev/null +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/stdlib_rename.swift.result -emit-remap-file-path %t/stdlib_rename.swift.remap -o /dev/null %api_diff_data_dir // RUN: diff -u %S/stdlib_rename.swift.expected %t/stdlib_rename.swift.result -// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/stdlib_rename.swift.result -emit-remap-file-path %t/stdlib_rename.swift.remap -o /dev/null -swift-version 4.2 +// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/stdlib_rename.swift.result -emit-remap-file-path %t/stdlib_rename.swift.remap -o /dev/null -swift-version 4.2 %api_diff_data_dir // RUN: diff -u %S/stdlib_rename.swift.expected %t/stdlib_rename.swift.result func test1(_ a: [String], s: String) { @@ -10,4 +10,4 @@ func test1(_ a: [String], s: String) { } func test2(_ s: String, c: Character) { _ = s.firstIndex(of: c) -} \ No newline at end of file +} diff --git a/test/Migrator/swift_version_change.swift b/test/Migrator/swift_version_change.swift index 535151fc8a527..031910de388b5 100644 --- a/test/Migrator/swift_version_change.swift +++ b/test/Migrator/swift_version_change.swift @@ -1,5 +1,5 @@ // REQUIRES: OS=macosx -// RUN: %empty-directory(%t) && %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t/dummpy.result -swift-version 4 -// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t/dummpy.result -swift-version 5 +// RUN: %empty-directory(%t) && %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t/dummpy.result -swift-version 4 %api_diff_data_dir +// RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t/dummpy.result -swift-version 5 %api_diff_data_dir -func foo() {} \ No newline at end of file +func foo() {} diff --git a/test/Misc/effectful_properties_keypath.swift b/test/Misc/effectful_properties_keypath.swift index 5954c09d5c80d..27405251e35ca 100644 --- a/test/Misc/effectful_properties_keypath.swift +++ b/test/Misc/effectful_properties_keypath.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: objc_interop diff --git a/test/ModuleInterface/BadStdlib.swiftinterface b/test/ModuleInterface/BadStdlib.swiftinterface index d185c8febe875..f5375dabdfccb 100644 --- a/test/ModuleInterface/BadStdlib.swiftinterface +++ b/test/ModuleInterface/BadStdlib.swiftinterface @@ -9,7 +9,7 @@ // other things.) // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: -sdk %/S/Inputs/BadStdlib.sdk -module-cache-path %/t/module-cache -resource-dir %/S/Inputs/BadStdlib.sdk) -compile-module-from-interface -o %/t/BadStdlib.swiftmodule %s -verify -verify-additional-file %/S/Inputs/BadStdlib.sdk/usr/lib/swift/Swift.swiftmodule/x86_64-apple-macos.swiftinterface +// RUN: %target-swift-frontend(mock-sdk: -sdk %/S/Inputs/BadStdlib.sdk -module-cache-path %/t/module-cache -resource-dir %/S/Inputs/BadStdlib.sdk) -compile-module-from-interface -o %/t/BadStdlib.swiftmodule %s -verify -verify-additional-file %/S/Inputs/BadStdlib.sdk/usr/lib/swift/Swift.swiftmodule/x86_64-apple-macos.swiftinterface -verify-ignore-unknown import ClangMod diff --git a/test/ModuleInterface/DerivedWitnesses.swiftinterface b/test/ModuleInterface/DerivedWitnesses.swiftinterface new file mode 100644 index 0000000000000..8aeab56a97e05 --- /dev/null +++ b/test/ModuleInterface/DerivedWitnesses.swiftinterface @@ -0,0 +1,44 @@ +// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s + +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 5.5 (swiftlang-1300.0.29.102 clang-1300.0.28.1) +// swift-module-flags: -target x86_64-apple-macosx11.0 -enable-objc-interop -enable-library-evolution -module-name DerivedWitnesses +import Swift + +public enum HasSynthesizedEquals : Int { + case x + case y + + public static func == (a: HasSynthesizedEquals, b: HasSynthesizedEquals) -> Bool + public func hash(into hasher: inout Hasher) + public var hashValue: Int { + get + } + + public init?(rawValue: Int) + public typealias RawValue = Int + public var rawValue: Int { + get + } +} + +public enum UsesDefaultEquals : Int { + case x + case y + + public init?(rawValue: Int) + public typealias RawValue = Int + public var rawValue: Int { + get + } +} + +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [ossa] @$s16DerivedWitnesses20HasSynthesizedEqualsOSQAASQ2eeoiySbx_xtFZTW : $@convention(witness_method: Equatable) (@in_guaranteed HasSynthesizedEquals, @in_guaranteed HasSynthesizedEquals, @thick HasSynthesizedEquals.Type) -> Bool { +// CHECK: bb0(%0 : $*HasSynthesizedEquals, %1 : $*HasSynthesizedEquals, %2 : $@thick HasSynthesizedEquals.Type): +// CHECK: function_ref @$s16DerivedWitnesses20HasSynthesizedEqualsO2eeoiySbAC_ACtFZ : $@convention(method) (HasSynthesizedEquals, HasSynthesizedEquals, @thin HasSynthesizedEquals.Type) -> Bool +// CHECK: return + +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [ossa] @$s16DerivedWitnesses17UsesDefaultEqualsOSQAASQ2eeoiySbx_xtFZTW : $@convention(witness_method: Equatable) (@in_guaranteed UsesDefaultEquals, @in_guaranteed UsesDefaultEquals, @thick UsesDefaultEquals.Type) -> Bool { +// CHECK: bb0(%0 : $*UsesDefaultEquals, %1 : $*UsesDefaultEquals, %2 : $@thick UsesDefaultEquals.Type): +// CHECK: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF : $@convention(thin) <τ_0_0 where τ_0_0 : RawRepresentable, τ_0_0.RawValue : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> Bool +// CHECK: return diff --git a/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..92c4dce4ed978 --- /dev/null +++ b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64-apple-macos.swiftinterface @@ -0,0 +1,6 @@ +// swift-interface-format-version: 1.0 +// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) +// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name DummyFramework +import Swift + +mess_mess_mess diff --git a/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64e-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64e-apple-macos.swiftinterface new file mode 100644 index 0000000000000..906cc149b691f --- /dev/null +++ b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64e-apple-macos.swiftinterface @@ -0,0 +1,6 @@ +// swift-interface-format-version: 1.0 +// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) +// swift-module-flags: -target arm64e-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name DummyFramework +import Swift + +mess_mess_mess diff --git a/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64e.swiftinterface b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64e.swiftinterface new file mode 100644 index 0000000000000..ccf9de9931273 --- /dev/null +++ b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/arm64e.swiftinterface @@ -0,0 +1,4 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 5.2 +// swift-module-flags: -target arm64e-apple-macos10.14 -enable-library-evolution -swift-version 5 -module-name DummyFramework +import Swift diff --git a/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/x86_64-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/x86_64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..1c3f144ff56d8 --- /dev/null +++ b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/x86_64-apple-macos.swiftinterface @@ -0,0 +1,6 @@ +// swift-interface-format-version: 1.0 +// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) +// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name DummyFramework +import Swift + +mess_mess_mess diff --git a/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/x86_64.swiftinterface b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/x86_64.swiftinterface new file mode 100644 index 0000000000000..d0e9567d4c322 --- /dev/null +++ b/test/ModuleInterface/Inputs/DummyFramework.framework/Modules/DummyFramework.swiftmodule/x86_64.swiftinterface @@ -0,0 +1,4 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 5.2 +// swift-module-flags: -target x86_64-apple-macos10.14 -enable-library-evolution -swift-version 5 -module-name DummyFramework +import Swift diff --git a/test/ModuleInterface/Inputs/MeowActor.swift b/test/ModuleInterface/Inputs/MeowActor.swift index 55cce93f0aaac..68307a5888db7 100644 --- a/test/ModuleInterface/Inputs/MeowActor.swift +++ b/test/ModuleInterface/Inputs/MeowActor.swift @@ -1,9 +1,9 @@ -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @globalActor public final class MeowActor { public static let shared = _Impl() public actor _Impl { - @actorIndependent + nonisolated public func enqueue(_ job: UnownedJob) { // DOES NOTHING! :) } diff --git a/test/ModuleInterface/Inputs/OldActor.swiftinterface b/test/ModuleInterface/Inputs/OldActor.swiftinterface new file mode 100644 index 0000000000000..a25eace7ffa17 --- /dev/null +++ b/test/ModuleInterface/Inputs/OldActor.swiftinterface @@ -0,0 +1,15 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -enable-library-evolution -module-name OldActor +import Swift +import _Concurrency + +#if compiler(>=5.3) && $Actors +@available(SwiftStdlib 5.5, *) +public actor Monk { + public init() + deinit + public func method() + // Lacks an unownedExecutor property +} +#endif + diff --git a/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/arm64-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/arm64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..7729f20517c35 --- /dev/null +++ b/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/arm64-apple-macos.swiftinterface @@ -0,0 +1,4 @@ +// swift-interface-format-version: 1.0 +// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) +// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name DummyFramework +import Swift diff --git a/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/arm64e-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/arm64e-apple-macos.swiftinterface new file mode 100644 index 0000000000000..f9c9d99058f36 --- /dev/null +++ b/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/arm64e-apple-macos.swiftinterface @@ -0,0 +1,4 @@ +// swift-interface-format-version: 1.0 +// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) +// swift-module-flags: -target arm64e-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name DummyFramework +import Swift diff --git a/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/x86_64-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/x86_64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..ba33d8c44c680 --- /dev/null +++ b/test/ModuleInterface/Inputs/alternative-interfaces/DummyFramework.swiftmodule/x86_64-apple-macos.swiftinterface @@ -0,0 +1,4 @@ +// swift-interface-format-version: 1.0 +// swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) +// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name DummyFramework +import Swift diff --git a/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/OtherModule.swiftmodule/x86_64-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/OtherModule.swiftmodule/x86_64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..aa3c983177371 --- /dev/null +++ b/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/OtherModule.swiftmodule/x86_64-apple-macos.swiftinterface @@ -0,0 +1,4 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -target x86_64-apple-macos10.9 -module-name OtherModule -O + +import Swift diff --git a/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/Swift.swiftmodule/x86_64-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/Swift.swiftmodule/x86_64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..5cf660135de85 --- /dev/null +++ b/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/Swift.swiftmodule/x86_64-apple-macos.swiftinterface @@ -0,0 +1,4 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -target x86_64-apple-macos10.9 -module-name Swift -parse-stdlib + +public struct Int {} diff --git a/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/_Concurrency.swiftmodule/x86_64-apple-macos.swiftinterface b/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/_Concurrency.swiftmodule/x86_64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..56ea9f68ff419 --- /dev/null +++ b/test/ModuleInterface/Inputs/stdlib_rebuild/usr/lib/swift/_Concurrency.swiftmodule/x86_64-apple-macos.swiftinterface @@ -0,0 +1,4 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -target x86_64-apple-macos10.9 -module-name _Concurrency -parse-stdlib + +@frozen public enum Task {} diff --git a/test/ModuleInterface/access-filter.swift b/test/ModuleInterface/access-filter.swift index db1d1d26cb699..2551434831cc8 100644 --- a/test/ModuleInterface/access-filter.swift +++ b/test/ModuleInterface/access-filter.swift @@ -53,7 +53,7 @@ public protocol PublicProto { func requirement() } // CHECK-NEXT: {{^[}]$}} -// CHECK: extension PublicProto {{[{]$}} +// CHECK: extension AccessFilter.PublicProto {{[{]$}} extension PublicProto { // CHECK: public func publicMethod(){{$}} public func publicMethod() {} @@ -64,7 +64,7 @@ extension PublicProto { @usableFromInline internal func ufiMethod() {} } // CHECK: {{^[}]$}} -// CHECK: {{^}}extension PublicProto {{[{]$}} +// CHECK: {{^}}extension AccessFilter.PublicProto {{[{]$}} public extension PublicProto { // CHECK: public func publicExtPublicMethod(){{$}} func publicExtPublicMethod() {} @@ -96,7 +96,7 @@ internal protocol UFIProto { func requirement() } // CHECK-NEXT: {{^[}]$}} -// CHECK: extension UFIProto {{[{]$}} +// CHECK: extension AccessFilter.UFIProto {{[{]$}} extension UFIProto { // CHECK: public func publicMethod(){{$}} public func publicMethod() {} @@ -107,7 +107,7 @@ extension UFIProto { @usableFromInline internal func ufiMethod() {} } // CHECK: {{^[}]$}} -// CHECK: extension PublicStruct {{[{]$}} +// CHECK: extension AccessFilter.PublicStruct {{[{]$}} extension PublicStruct { // CHECK: @_hasInitialValue public static var secretlySettable: Swift.Int { // CHECK-NEXT: get @@ -120,7 +120,7 @@ extension InternalStruct_BAD: PublicProto { internal static var dummy: Int { return 0 } } -// CHECK: extension UFIStruct : AccessFilter.PublicProto {{[{]$}} +// CHECK: extension AccessFilter.UFIStruct : AccessFilter.PublicProto {{[{]$}} extension UFIStruct: PublicProto { // CHECK-NEXT: @usableFromInline // CHECK-NEXT: internal typealias Assoc = Swift.Int @@ -167,12 +167,12 @@ class InternalClass_BAD { // CHECK: public struct GenericStruct public struct GenericStruct {} -// CHECK: extension GenericStruct where T == AccessFilter.PublicStruct {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.PublicStruct {{[{]$}} extension GenericStruct where T == AccessFilter.PublicStruct { // CHECK-NEXT: public func constrainedToPublicStruct(){{$}} public func constrainedToPublicStruct() {} } // CHECK-NEXT: {{^[}]$}} -// CHECK: extension GenericStruct where T == AccessFilter.UFIStruct {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.UFIStruct {{[{]$}} extension GenericStruct where T == AccessFilter.UFIStruct { // CHECK-NEXT: @usableFromInline{{$}} // CHECK-NEXT: internal func constrainedToUFIStruct(){{$}} @@ -182,12 +182,12 @@ extension GenericStruct where T == InternalStruct_BAD { @usableFromInline internal func constrainedToInternalStruct_BAD() {} } -// CHECK: extension GenericStruct where T == AccessFilter.PublicStruct {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.PublicStruct {{[{]$}} extension GenericStruct where PublicStruct == T { // CHECK-NEXT: public func constrainedToPublicStruct2(){{$}} public func constrainedToPublicStruct2() {} } // CHECK-NEXT: {{^[}]$}} -// CHECK: extension GenericStruct where T == AccessFilter.UFIStruct {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.UFIStruct {{[{]$}} extension GenericStruct where UFIStruct == T { // CHECK-NEXT: @usableFromInline{{$}} // CHECK-NEXT: internal func constrainedToUFIStruct2(){{$}} @@ -197,12 +197,12 @@ extension GenericStruct where InternalStruct_BAD == T { @usableFromInline internal func constrainedToInternalStruct2_BAD() {} } -// CHECK: extension GenericStruct where T : AccessFilter.PublicProto {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T : AccessFilter.PublicProto {{[{]$}} extension GenericStruct where T: PublicProto { // CHECK-NEXT: public func constrainedToPublicProto(){{$}} public func constrainedToPublicProto() {} } // CHECK-NEXT: {{^[}]$}} -// CHECK: extension GenericStruct where T : AccessFilter.UFIProto {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T : AccessFilter.UFIProto {{[{]$}} extension GenericStruct where T: UFIProto { // CHECK-NEXT: @usableFromInline{{$}} // CHECK-NEXT: internal func constrainedToUFIProto(){{$}} @@ -212,12 +212,12 @@ extension GenericStruct where T: InternalProto_BAD { @usableFromInline internal func constrainedToInternalProto_BAD() {} } -// CHECK: extension GenericStruct where T : AccessFilter.PublicClass {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T : AccessFilter.PublicClass {{[{]$}} extension GenericStruct where T: PublicClass { // CHECK-NEXT: public func constrainedToPublicClass(){{$}} public func constrainedToPublicClass() {} } // CHECK-NEXT: {{^[}]$}} -// CHECK: extension GenericStruct where T : AccessFilter.UFIClass {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T : AccessFilter.UFIClass {{[{]$}} extension GenericStruct where T: UFIClass { // CHECK-NEXT: @usableFromInline{{$}} // CHECK-NEXT: internal func constrainedToUFIClass(){{$}} @@ -227,7 +227,7 @@ extension GenericStruct where T: InternalClass_BAD { @usableFromInline internal func constrainedToInternalClass_BAD() {} } -// CHECK: extension GenericStruct where T : AnyObject {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T : AnyObject {{[{]$}} extension GenericStruct where T: AnyObject { // CHECK-NEXT: public func constrainedToAnyObject(){{$}} public func constrainedToAnyObject() {} @@ -245,12 +245,12 @@ internal typealias InternalAlias_BAD = PublicAliasBase internal typealias ReallyInternalAlias_BAD = ReallyInternalAliasBase_BAD -// CHECK: extension GenericStruct where T == AccessFilter.PublicAlias {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.PublicAlias {{[{]$}} extension GenericStruct where T == PublicAlias { // CHECK-NEXT: public func constrainedToPublicAlias(){{$}} public func constrainedToPublicAlias() {} } // CHECK-NEXT: {{^[}]$}} -// CHECK: extension GenericStruct where T == AccessFilter.UFIAlias {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.UFIAlias {{[{]$}} extension GenericStruct where T == UFIAlias { // CHECK-NEXT: @usableFromInline{{$}} // CHECK-NEXT: internal func constrainedToUFIAlias(){{$}} @@ -275,7 +275,7 @@ extension GenericStruct: PublicProto where T: InternalProto_BAD { public struct MultiGenericStruct {} -// CHECK: extension MultiGenericStruct where First == AccessFilter.PublicStruct, Second == AccessFilter.PublicStruct {{[{]$}} +// CHECK: extension AccessFilter.MultiGenericStruct where First == AccessFilter.PublicStruct, Second == AccessFilter.PublicStruct {{[{]$}} extension MultiGenericStruct where First == PublicStruct, Second == PublicStruct { // CHECK-NEXT: public func publicPublic(){{$}} public func publicPublic() {} diff --git a/test/ModuleInterface/actor_isolation.swift b/test/ModuleInterface/actor_isolation.swift index 04b8939fd2fc5..3f14550346c73 100644 --- a/test/ModuleInterface/actor_isolation.swift +++ b/test/ModuleInterface/actor_isolation.swift @@ -1,6 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test -enable-experimental-concurrency %s // RUN: %FileCheck %s < %t/Test.swiftinterface +// RUN: %FileCheck %s -check-prefix SYNTHESIZED < %t/Test.swiftinterface // RUN: %target-swift-frontend -typecheck-module-from-interface -module-name Test %t/Test.swiftinterface // RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -disable-objc-attr-requires-foundation-module -emit-module-interface-path %t/TestFromModule.swiftinterface -module-name Test -enable-experimental-concurrency @@ -10,11 +11,15 @@ // REQUIRES: concurrency // CHECK: public actor SomeActor + +@available(SwiftStdlib 5.5, *) public actor SomeActor { - @actorIndependent func maine() { } + nonisolated func maine() { } } // CHECK: @globalActor public struct SomeGlobalActor + +@available(SwiftStdlib 5.5, *) @globalActor public struct SomeGlobalActor { public static let shared = SomeActor() @@ -22,6 +27,8 @@ public struct SomeGlobalActor { // CHECK: @{{(Test.)?}}SomeGlobalActor public protocol P1 // CHECK-NEXT: @{{(Test.)?}}SomeGlobalActor func method() + +@available(SwiftStdlib 5.5, *) @SomeGlobalActor public protocol P1 { func method() @@ -29,12 +36,66 @@ public protocol P1 { // CHECK: class C1 // CHECK-NEXT: @{{(Test.)?}}SomeGlobalActor public func method() + +@available(SwiftStdlib 5.5, *) public class C1: P1 { public func method() { } } + +@available(SwiftStdlib 5.5, *) @SomeGlobalActor public class C2 { } // CHECK: @{{(Test.)?}}SomeGlobalActor public class C2 + +@available(SwiftStdlib 5.5, *) public class C3: C2 { } + +// CHECK: public class C4 : Swift.UnsafeSendable + +@available(SwiftStdlib 5.5, *) +public class C4: UnsafeSendable { } + +// CHECK: public class C5 : @unchecked Swift.Sendable + +@available(SwiftStdlib 5.5, *) +public class C5: @unchecked Sendable { } + + +@available(SwiftStdlib 5.5, *) +public class C6 { } + +// CHECK: extension {{(Test.)?}}C6 : @unchecked Swift.Sendable + +@available(SwiftStdlib 5.5, *) +extension C6: @unchecked Sendable { } + + +@available(SwiftStdlib 5.5, *) +public class C7 { } + +// CHECK: extension {{(Test.)?}}C7 : Swift.UnsafeSendable + +@available(SwiftStdlib 5.5, *) +extension C7: UnsafeSendable { } + + +@available(SwiftStdlib 5.5, *) +public protocol P2 { + @SomeGlobalActor func method() +} + + +// CHECK: class {{(Test.)?}}C8 : {{(Test.)?}}P2 { +@available(SwiftStdlib 5.5, *) +public class C8 : P2 { + // CHECK: @{{(Test.)?}}SomeGlobalActor public func method() + public func method() {} +} + +// FIXME: Work around a bug where module printing depends on the "synthesized" +// bit in conformances which is not serialized and not present in the textual +// form. + +// SYNTHESIZED: extension Test.C2 : Swift.Sendable {} diff --git a/test/ModuleInterface/actor_objc.swift b/test/ModuleInterface/actor_objc.swift index 05a1b61ddca9f..de5eece395a57 100644 --- a/test/ModuleInterface/actor_objc.swift +++ b/test/ModuleInterface/actor_objc.swift @@ -1,9 +1,9 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test -enable-experimental-concurrency %s +// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test %s // RUN: %FileCheck %s < %t/Test.swiftinterface -// RUN: %target-swift-frontend -typecheck-module-from-interface -module-name Test %t/Test.swiftinterface +// RUN: %target-swift-frontend -typecheck-module-from-interface -module-name Test %t/Test.swiftinterface -// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -disable-objc-attr-requires-foundation-module -emit-module-interface-path %t/TestFromModule.swiftinterface -module-name Test -enable-experimental-concurrency +// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -disable-objc-attr-requires-foundation-module -emit-module-interface-path %t/TestFromModule.swiftinterface -module-name Test // RUN: %FileCheck %s < %t/TestFromModule.swiftinterface // RUN: %target-swift-frontend -typecheck-module-from-interface -module-name Test %t/TestFromModule.swiftinterface @@ -12,7 +12,9 @@ import Foundation -// CHECK-LABEL: @objc @_inheritsConvenienceInitializers public actor SomeActor : ObjectiveC.NSObject { -// CHECK: @objc override dynamic public init() +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers +// CHECK: public actor SomeActor : ObjectiveC.NSObject { +// CHECK: @objc override public init() +@available(SwiftStdlib 5.5, *) public actor SomeActor: NSObject { } diff --git a/test/ModuleInterface/actor_old_compat.swift b/test/ModuleInterface/actor_old_compat.swift new file mode 100644 index 0000000000000..8cf7e2e313441 --- /dev/null +++ b/test/ModuleInterface/actor_old_compat.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/OldActor.framework/Modules/OldActor.swiftmodule +// RUN: %target-swift-frontend -emit-module -module-name OldActor %S/Inputs/OldActor.swiftinterface -o %t/OldActor.framework/Modules/OldActor.swiftmodule/%module-target-triple.swiftmodule +// RUN: %target-swift-frontend -F %t -disable-availability-checking -typecheck -verify %s + +// RUNX: cp -r %S/Inputs/OldActor.framework %t/ +// RUNX: %{python} %S/../CrossImport/Inputs/rewrite-module-triples.py %t %module-target-triple + +// REQUIRES: concurrency + +import OldActor + +@available(SwiftStdlib 5.5, *) +extension Monk { + public func test() async { + method() + } +} \ No newline at end of file diff --git a/test/ModuleInterface/actor_protocol.swift b/test/ModuleInterface/actor_protocol.swift index 3f8766b22806a..ffd0f76fc8bca 100644 --- a/test/ModuleInterface/actor_protocol.swift +++ b/test/ModuleInterface/actor_protocol.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -typecheck -enable-library-evolution -enable-experimental-concurrency -enable-experimental-async-handler -emit-module-interface-path %t/Library.swiftinterface -module-name Library %s +// RUN: %target-swift-frontend -typecheck -enable-library-evolution -disable-availability-checking -emit-module-interface-path %t/Library.swiftinterface -module-name Library %s // RUN: %FileCheck --check-prefix CHECK-EXTENSION %s <%t/Library.swiftinterface // RUN: %FileCheck --check-prefix CHECK %s <%t/Library.swiftinterface // REQUIRES: concurrency @@ -12,47 +12,47 @@ // CHECK-EXTENSION-NOT: extension {{.+}} : _Concurrency.Actor // CHECK: public actor PlainActorClass { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) public actor PlainActorClass { - @actorIndependent public func enqueue(_ job: UnownedJob) { } + nonisolated public func enqueue(_ job: UnownedJob) { } } // CHECK: public actor ExplicitActorClass : _Concurrency.Actor { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) public actor ExplicitActorClass : Actor { - @actorIndependent public func enqueue(_ job: UnownedJob) { } + nonisolated public func enqueue(_ job: UnownedJob) { } } // CHECK: public actor EmptyActor { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) public actor EmptyActor {} -// CHECK: actor public class EmptyActorClass { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -public actor class EmptyActorClass {} +// CHECK: public actor EmptyActorClass { +@available(SwiftStdlib 5.5, *) +public actor EmptyActorClass {} // CHECK: public protocol Cat : _Concurrency.Actor { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) public protocol Cat : Actor { func mew() } // CHECK: public actor HouseCat : Library.Cat { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) public actor HouseCat : Cat { - @asyncHandler public func mew() {} - @actorIndependent public func enqueue(_ job: UnownedJob) { } + nonisolated public func mew() {} + nonisolated public func enqueue(_ job: UnownedJob) { } } // CHECK: public protocol ToothyMouth { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) public protocol ToothyMouth { func chew() } // CHECK: public actor Lion : Library.ToothyMouth, _Concurrency.Actor { -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) public actor Lion : ToothyMouth, Actor { - @asyncHandler public func chew() {} - @actorIndependent public func enqueue(_ job: UnownedJob) { } + nonisolated public func chew() {} + nonisolated public func enqueue(_ job: UnownedJob) { } } diff --git a/test/ModuleInterface/available-attr-no-collapse.swift b/test/ModuleInterface/available-attr-no-collapse.swift new file mode 100644 index 0000000000000..7b17fb062b0cc --- /dev/null +++ b/test/ModuleInterface/available-attr-no-collapse.swift @@ -0,0 +1,8 @@ +// RUN: %empty-directory(%t) +// RUN: echo '@available(macOS 12.0, iOS 15.0, macCatalyst 15.0, *)' > %t/Foo.swift +// RUN: echo 'public struct Foo {}' >> %t/Foo.swift + +// RUN: %target-swift-frontend -emit-module -emit-module-interface-path %t/Foo.swiftinterface -enable-library-evolution %t/Foo.swift +// RUN: %FileCheck %s < %t/Foo.swiftinterface + +// CHECK: macCatalyst diff --git a/test/ModuleInterface/build-alternative-interface-explicit.swift b/test/ModuleInterface/build-alternative-interface-explicit.swift new file mode 100644 index 0000000000000..9deb9a3224909 --- /dev/null +++ b/test/ModuleInterface/build-alternative-interface-explicit.swift @@ -0,0 +1,14 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/sources) +// RUN: %empty-directory(%t/inputs) +// RUN: %empty-directory(%t/alternative-inputs) +// RUN: %empty-directory(%t/module-cache) +// RUN: %empty-directory(%t/outputs) + +// RUN: echo "public func foo() {}" > %t/sources/Foo.swift +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/inputs/Foo.swiftinterface %t/sources/Foo.swift -module-name Foo -disable-implicit-concurrency-module-import -enable-library-evolution -module-cache-path %t/module-cache -I %t/inputs -swift-version 5 +// RUN: cp %t/inputs/Foo.swiftinterface %t/alternative-inputs/Foo.swiftinterface +// RUN: echo "mess_mess_mess" >> %t/inputs/Foo.swiftinterface +// RUN: not %target-swift-frontend -compile-module-from-interface -module-name Foo %t/inputs/Foo.swiftinterface -o %t/outputs/Foo.swiftmodule + +// RUN: %target-swift-frontend -compile-module-from-interface -module-name Foo %t/inputs/Foo.swiftinterface -o %t/outputs/Foo.swiftmodule -backup-module-interface-path %t/alternative-inputs diff --git a/test/ModuleInterface/build-alternative-interface-framework.swift b/test/ModuleInterface/build-alternative-interface-framework.swift new file mode 100644 index 0000000000000..9af94416facf8 --- /dev/null +++ b/test/ModuleInterface/build-alternative-interface-framework.swift @@ -0,0 +1,10 @@ +// REQUIRES: VENDOR=apple + +// Required DummyFramework imported is only built for macOS supported archs (x86_64, arm64, arm64e) +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t/module-cache) +// RUN: not %target-swift-frontend-typecheck -disable-implicit-concurrency-module-import -target %target-cpu-apple-macosx13.0 -F %S/Inputs %s -module-cache-path %t/module-cache +// RUN: %target-swift-frontend-typecheck -disable-implicit-concurrency-module-import -target %target-cpu-apple-macosx13.0 -F %S/Inputs %s -module-cache-path %t/module-cache -backup-module-interface-path %S/Inputs/alternative-interfaces + +import DummyFramework diff --git a/test/ModuleInterface/build-alternative-interface.swift b/test/ModuleInterface/build-alternative-interface.swift new file mode 100644 index 0000000000000..3cae254ce79a3 --- /dev/null +++ b/test/ModuleInterface/build-alternative-interface.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/sources) +// RUN: %empty-directory(%t/inputs) +// RUN: %empty-directory(%t/alternative-inputs) +// RUN: %empty-directory(%t/module-cache) + +// RUN: echo "public func foo() {}" > %t/sources/Foo.swift +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/inputs/Foo.swiftinterface %t/sources/Foo.swift -module-name Foo -disable-implicit-concurrency-module-import -enable-library-evolution -module-cache-path %t/module-cache -I %t/inputs -swift-version 5 +// RUN: cp %t/inputs/Foo.swiftinterface %t/alternative-inputs/Foo.swiftinterface + +// RUN: echo "import Foo" > %t/sources/Bar.swift +// RUN: echo "public func foo() {}" >> %t/sources/Bar.swift +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/inputs/Bar.swiftinterface %t/sources/Bar.swift -module-name Bar -disable-implicit-concurrency-module-import -enable-library-evolution -module-cache-path %t/module-cache -I %t/inputs -swift-version 5 +// RUN: cp %t/inputs/Bar.swiftinterface %t/alternative-inputs/Bar.swiftinterface + +// RUN: echo "import Bar" > %t/sources/FooBar.swift +// RUN: echo "public func foo() {}" >> %t/sources/FooBar.swift +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/inputs/FooBar.swiftinterface %t/sources/FooBar.swift -module-name FooBar -disable-implicit-concurrency-module-import -enable-library-evolution -module-cache-path %t/module-cache -I %t/inputs -swift-version 5 + +// RUN: echo "messmessmess" >> %t/inputs/Foo.swiftinterface +// RUN: echo "messmessmess" >> %t/inputs/Bar.swiftinterface + +// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import -I %t/inputs -backup-module-interface-path %t/alternative-inputs -module-cache-path %t/module-cache + +// RUN: touch -t 201401240005 %t/inputs/Bar.swiftinterface +// RUN: %target-swift-frontend-typecheck -disable-implicit-concurrency-module-import -I %t/inputs -backup-module-interface-path %t/alternative-inputs -module-cache-path %t/module-cache -Rmodule-interface-rebuild %s &> %t/remarks.txt +// RUN: %FileCheck --input-file %t/remarks.txt %s --check-prefix=CHECK-REBUILD +// CHECK-REBUILD: remark: rebuilding module 'FooBar' from interface +// CHECK-REBUILD: remark: rebuilding module 'Bar' from interface + +// RUN: %target-swift-frontend-typecheck -disable-implicit-concurrency-module-import -I %t/inputs -backup-module-interface-path %t/alternative-inputs -module-cache-path %t/module-cache -Rmodule-interface-rebuild %s &> %t/no-remarks.txt +// RUN: echo "additional" >> %t/no-remarks.txt +// RUN: %FileCheck --input-file %t/no-remarks.txt %s --check-prefix=CHECK-REBUILD-NOT +// CHECK-REBUILD-NOT-NOT: remark + +import FooBar +import Foo +import Bar diff --git a/test/ModuleInterface/concurrency.swift b/test/ModuleInterface/concurrency.swift index e6eb954f009a6..9d3355a13bc96 100644 --- a/test/ModuleInterface/concurrency.swift +++ b/test/ModuleInterface/concurrency.swift @@ -4,10 +4,12 @@ // REQUIRES: concurrency #if LIBRARY +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) public func fn() async { fatalError() } +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) public func reasyncFn(_: () async -> ()) reasync { fatalError() } @@ -17,6 +19,7 @@ public func reasyncFn(_: () async -> ()) reasync { #else import Library +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func callFn() async { await fn() } diff --git a/test/ModuleInterface/conformances.swift b/test/ModuleInterface/conformances.swift index 7368e91cf5480..3987335e48ccc 100644 --- a/test/ModuleInterface/conformances.swift +++ b/test/ModuleInterface/conformances.swift @@ -33,45 +33,45 @@ public struct A1: PublicProto, PrivateProto {} // NEGATIVE-NOT: extension conformances.A2 public struct A2: PrivateProto, PublicProto {} // CHECK: public struct A3 { -// CHECK-END: extension A3 : conformances.PublicProto {} +// CHECK-END: extension conformances.A3 : conformances.PublicProto {} public struct A3: PublicProto & PrivateProto {} // CHECK: public struct A4 { -// CHECK-END: extension A4 : conformances.PublicProto {} +// CHECK-END: extension conformances.A4 : conformances.PublicProto {} public struct A4: PrivateProto & PublicProto {} public protocol PublicBaseProto {} private protocol PrivateSubProto: PublicBaseProto {} // CHECK: public struct B1 { -// CHECK-END: extension B1 : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.B1 : conformances.PublicBaseProto {} public struct B1: PrivateSubProto {} // CHECK: public struct B2 : conformances.PublicBaseProto { // NEGATIVE-NOT: extension conformances.B2 public struct B2: PublicBaseProto, PrivateSubProto {} // CHECK: public struct B3 { -// CHECK-END: extension B3 : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.B3 : conformances.PublicBaseProto {} public struct B3: PublicBaseProto & PrivateSubProto {} // CHECK: public struct B4 : conformances.PublicBaseProto { -// NEGATIVE-NOT: extension B4 { +// NEGATIVE-NOT: extension conformances.B4 { // NEGATIVE-NOT: extension conformances.B4 public struct B4: PublicBaseProto {} extension B4: PrivateSubProto {} // CHECK: public struct B5 { -// CHECK: extension B5 : conformances.PublicBaseProto { -// NEGATIVE-NOT: extension conformances.B5 +// CHECK: extension conformances.B5 : conformances.PublicBaseProto { +// NEGATIVE-NOT: extension B5 public struct B5: PrivateSubProto {} extension B5: PublicBaseProto {} // CHECK: public struct B6 { -// NEGATIVE-NOT: extension B6 { -// CHECK: extension B6 : conformances.PublicBaseProto { -// NEGATIVE-NOT: extension conformances.B6 +// NEGATIVE-NOT: extension conformances.B6 : conformances.PrivateSubProto +// NEGATIVE-NOT: extension conformances.B6 { +// CHECK: extension conformances.B6 : conformances.PublicBaseProto { public struct B6 {} extension B6: PrivateSubProto {} extension B6: PublicBaseProto {} // CHECK: public struct B7 { -// CHECK: extension B7 : conformances.PublicBaseProto { -// NEGATIVE-NOT: extension B7 { -// NEGATIVE-NOT: extension conformances.B7 +// CHECK: extension conformances.B7 : conformances.PublicBaseProto { +// NEGATIVE-NOT: extension conformances.B7 : conformances.PrivateSubProto { +// NEGATIVE-NOT: extension conformances.B7 { public struct B7 {} extension B7: PublicBaseProto {} extension B7: PrivateSubProto {} @@ -92,7 +92,7 @@ public protocol ConditionallyConformedAgain {} extension OuterGeneric: ConditionallyConformed where T: PrivateProto {} extension OuterGeneric: ConditionallyConformedAgain where T == PrivateProto {} -// CHECK-END: extension OuterGeneric.Inner : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.OuterGeneric.Inner : conformances.PublicBaseProto {} // CHECK-END: @available(*, unavailable) // CHECK-END-NEXT: extension conformances.OuterGeneric.Inner : conformances.ConditionallyConformed, conformances.ConditionallyConformedAgain where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {} extension OuterGeneric.Inner: ConditionallyConformed where T: PrivateProto {} @@ -101,14 +101,14 @@ extension OuterGeneric.Inner: ConditionallyConformedAgain where T == PrivateProt private protocol AnotherPrivateSubProto: PublicBaseProto {} // CHECK: public struct C1 { -// CHECK-END: extension C1 : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.C1 : conformances.PublicBaseProto {} public struct C1: PrivateSubProto, AnotherPrivateSubProto {} // CHECK: public struct C2 { -// CHECK-END: extension C2 : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.C2 : conformances.PublicBaseProto {} public struct C2: PrivateSubProto & AnotherPrivateSubProto {} // CHECK: public struct C3 { -// NEGATIVE-NOT: extension C3 { -// CHECK-END: extension C3 : conformances.PublicBaseProto {} +// NEGATIVE-NOT: extension conformances.C3 { +// CHECK-END: extension conformances.C3 : conformances.PublicBaseProto {} public struct C3: PrivateSubProto {} extension C3: AnotherPrivateSubProto {} @@ -122,20 +122,21 @@ public struct D1: PublicSubProto, PrivateSubProto {} // NEGATIVE-NOT: extension conformances.D2 public struct D2: PrivateSubProto, PublicSubProto {} // CHECK: public struct D3 { -// CHECK-END: extension D3 : conformances.PublicBaseProto {} -// CHECK-END: extension D3 : conformances.PublicSubProto {} +// CHECK-END: extension conformances.D3 : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.D3 : conformances.PublicSubProto {} public struct D3: PrivateSubProto & PublicSubProto {} // CHECK: public struct D4 { -// CHECK-END: extension D4 : conformances.APublicSubProto {} -// CHECK-END: extension D4 : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.D4 : conformances.APublicSubProto {} +// CHECK-END: extension conformances.D4 : conformances.PublicBaseProto {} public struct D4: APublicSubProto & PrivateSubProto {} // CHECK: public struct D5 { -// CHECK: extension D5 : conformances.PublicSubProto { -// NEGATIVE-NOT: extension conformances.D5 +// NEGATIVE-NOT: extension conformances.D5 : conformances.PrivateSubProto { +// NEGATIVE-NOT: extension conformances.D5 { +// CHECK: extension conformances.D5 : conformances.PublicSubProto { public struct D5: PrivateSubProto {} extension D5: PublicSubProto {} // CHECK: public struct D6 : conformances.PublicSubProto { -// NEGATIVE-NOT: extension D6 { +// NEGATIVE-NOT: extension conformances.D6 { // NEGATIVE-NOT: extension conformances.D6 public struct D6: PublicSubProto {} extension D6: PrivateSubProto {} @@ -143,27 +144,27 @@ extension D6: PrivateSubProto {} private typealias PrivateProtoAlias = PublicProto // CHECK: public struct E1 { -// CHECK-END: extension E1 : conformances.PublicProto {} +// CHECK-END: extension conformances.E1 : conformances.PublicProto {} public struct E1: PrivateProtoAlias {} private typealias PrivateSubProtoAlias = PrivateSubProto // CHECK: public struct F1 { -// CHECK-END: extension F1 : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.F1 : conformances.PublicBaseProto {} public struct F1: PrivateSubProtoAlias {} private protocol ClassConstrainedProto: PublicProto, AnyObject {} public class G1: ClassConstrainedProto {} // CHECK: public class G1 { -// CHECK-END: extension G1 : conformances.PublicProto {} +// CHECK-END: extension conformances.G1 : conformances.PublicProto {} public class Base {} private protocol BaseConstrainedProto: Base, PublicProto {} public class H1: Base, ClassConstrainedProto {} // CHECK: public class H1 : conformances.Base { -// CHECK-END: extension H1 : conformances.PublicProto {} +// CHECK-END: extension conformances.H1 : conformances.PublicProto {} public struct MultiGeneric {} extension MultiGeneric: PublicProto where U: PrivateProto {} @@ -197,32 +198,32 @@ public struct CoolTVType: PrivateSubProto {} // CHECK: public struct CoolTVType { // CHECK-END: @available(iOS, unavailable) // CHECK-END-NEXT: @available(macOS, unavailable) -// CHECK-END-NEXT: extension CoolTVType : conformances.PublicBaseProto {} +// CHECK-END-NEXT: extension conformances.CoolTVType : conformances.PublicBaseProto {} @available(macOS 10.99, *) public struct VeryNewMacType: PrivateSubProto {} // CHECK: public struct VeryNewMacType { // CHECK-END: @available(macOS 10.99, *) -// CHECK-END-NEXT: extension VeryNewMacType : conformances.PublicBaseProto {} +// CHECK-END-NEXT: extension conformances.VeryNewMacType : conformances.PublicBaseProto {} public struct VeryNewMacProto {} @available(macOS 10.98, *) extension VeryNewMacProto: PrivateSubProto {} // CHECK: public struct VeryNewMacProto { // CHECK-END: @available(macOS 10.98, *) -// CHECK-END-NEXT: extension VeryNewMacProto : conformances.PublicBaseProto {} +// CHECK-END-NEXT: extension conformances.VeryNewMacProto : conformances.PublicBaseProto {} public struct PrivateProtoConformer {} extension PrivateProtoConformer : PrivateProto { public var member: Int { return 0 } } // CHECK: public struct PrivateProtoConformer { -// CHECK: extension PrivateProtoConformer { +// CHECK: extension conformances.PrivateProtoConformer { // CHECK-NEXT: public var member: Swift.Int { // CHECK-NEXT: get // CHECK-NEXT: } // CHECK-NEXT: {{^}$}} -// NEGATIVE-NOT: extension conformances.PrivateProtoConformer +// NEGATIVE-NOT: extension conformances.PrivateProtoConformer : conformances.PrivateProto { // NEGATIVE-NOT: extension {{(Swift.)?}}Bool{{.+}}Hashable // NEGATIVE-NOT: extension {{(Swift.)?}}Bool{{.+}}Equatable @@ -238,7 +239,7 @@ public struct NestedAvailabilityOuter { // CHECK-END: @available(macOS 10.97, iOS 23, *) // CHECK-END: @available(tvOS, unavailable) -// CHECK-END: extension NestedAvailabilityOuter.Inner : conformances.PublicBaseProto {} +// CHECK-END: extension conformances.NestedAvailabilityOuter.Inner : conformances.PublicBaseProto {} // CHECK-END: @usableFromInline diff --git a/test/ModuleInterface/consuming.swift b/test/ModuleInterface/consuming.swift new file mode 100644 index 0000000000000..550aea98a5773 --- /dev/null +++ b/test/ModuleInterface/consuming.swift @@ -0,0 +1,27 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/missing) +// RUN: %empty-directory(%t/inputs) +// RUN: %target-swift-frontend -emit-module-path %t/missing/Foo.swiftmodule -enable-library-evolution -emit-module-interface-path %t/inputs/Foo.swiftinterface -enable-objc-interop -disable-objc-attr-requires-foundation-module -module-name Foo %s +// RUN: %FileCheck --input-file %t/inputs/Foo.swiftinterface %s + +// RUN: touch %t/Bar.swift +// RUN: echo "import Foo" > %t/Bar.swift +// RUN: echo "let f = Field()" >> %t/Bar.swift + +// RUN: %target-swift-frontend -emit-module-path %t/Bar.swiftmodule -enable-library-evolution -enable-objc-interop -disable-objc-attr-requires-foundation-module -module-name Bar %t/Bar.swift -I %t/inputs + + +import Swift + +public struct Field { + public init() {} + public var area: Int { + __consuming get { return 1 } + _modify { + var a = 1 + yield &a + } + } +} + +// CHECK: __consuming get diff --git a/test/ModuleInterface/disable-availability-checking.swift b/test/ModuleInterface/disable-availability-checking.swift new file mode 100644 index 0000000000000..b0381c17276f7 --- /dev/null +++ b/test/ModuleInterface/disable-availability-checking.swift @@ -0,0 +1,16 @@ +// REQUIRES: OS=macosx && CPU=x86_64 +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/inputs) +// RUN: %empty-directory(%t/modulecache) +// RUN: echo "// swift-interface-format-version: 1.0" > %t/inputs/Foo.swiftinterface +// RUN: echo "// swift-module-flags: -module-name Foo" >> %t/inputs/Foo.swiftinterface +// RUN: echo "@available(macOS 11, *)" >> %t/inputs/Foo.swiftinterface +// RUN: echo "public class Foo {}" >> %t/inputs/Foo.swiftinterface +// RUN: echo "public extension Foo { public func f() {} }" >> %t/inputs/Foo.swiftinterface + +// RUN: not %target-swift-frontend -emit-module-path %t/Bar.swiftmodule -enable-library-evolution -enable-objc-interop -disable-objc-attr-requires-foundation-module -module-name Bar %s -I %t/inputs -target x86_64-apple-macos10.9 -module-cache-path %t/modulecache + +// RUN: %target-swift-frontend -emit-module-path %t/Bar.swiftmodule -enable-library-evolution -enable-objc-interop -disable-objc-attr-requires-foundation-module -module-name Bar %s -I %t/inputs -target x86_64-apple-macos10.9 -disable-availability-checking -module-cache-path %t/modulecache + +import Foo + diff --git a/test/ModuleInterface/effectful_properties.swift b/test/ModuleInterface/effectful_properties.swift index 78336fcd82181..7258f4d998403 100644 --- a/test/ModuleInterface/effectful_properties.swift +++ b/test/ModuleInterface/effectful_properties.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -typecheck -swift-version 5 -enable-library-evolution -emit-module-interface-path %t.swiftinterface %s -module-name EffProps +// RUN: %target-swift-frontend -typecheck -swift-version 5 -enable-library-evolution -emit-module-interface-path %t.swiftinterface %s -module-name EffProps -disable-availability-checking // RUN: %FileCheck %s < %t.swiftinterface public struct MyStruct {} @@ -58,4 +58,4 @@ public enum E { public protocol P { var books: Int { get async } subscript(_ x: Int) -> Int { get throws } -} \ No newline at end of file +} diff --git a/test/ModuleInterface/emit-abi-descriptor.swift b/test/ModuleInterface/emit-abi-descriptor.swift new file mode 100644 index 0000000000000..530d5e5b110ec --- /dev/null +++ b/test/ModuleInterface/emit-abi-descriptor.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/Foo.swiftmodule) +// RUN: %empty-directory(%t/ResourceDir/%target-sdk-name/prebuilt-modules/Foo.swiftmodule) +// RUN: echo "public func foo() {}" > %t/Foo.swift + +// RUN: %target-swift-frontend -emit-module %t/Foo.swift -module-name Foo -emit-module-interface-path %t/Foo.swiftinterface +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftinterface -o %t/Foo.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo.json + +// RUN: %FileCheck %s < %t/Foo.json + +// CHECK: "kind": "Root" +// CHECK-NEXT: "name": "TopLevel" +// CHECK-NEXT: "printedName": "TopLevel" diff --git a/test/ModuleInterface/empty-extension.swift b/test/ModuleInterface/empty-extension.swift index e51251e588a06..fd32d130a0fc1 100644 --- a/test/ModuleInterface/empty-extension.swift +++ b/test/ModuleInterface/empty-extension.swift @@ -50,10 +50,10 @@ extension IOIImportedType { // CHECK-NOT: funcForAnotherOS extension NormalImportedType : PublicProto {} -// CHECK: extension NormalImportedType +// CHECK: extension Lib.NormalImportedType extension ExportedType : PublicProto {} -// CHECK: extension ExportedType +// CHECK: extension IndirectLib.ExportedType extension NormalImportedType { #if os(macOS) diff --git a/test/ModuleInterface/features.swift b/test/ModuleInterface/features.swift index a146dcc33ca11..a75ff89e434e0 100644 --- a/test/ModuleInterface/features.swift +++ b/test/ModuleInterface/features.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -typecheck -swift-version 5 -module-name FeatureTest -emit-module-interface-path - -enable-library-evolution -enable-experimental-concurrency %s | %FileCheck %s +// RUN: %target-swift-frontend -typecheck -swift-version 5 -module-name FeatureTest -emit-module-interface-path - -enable-library-evolution -disable-availability-checking %s | %FileCheck %s // REQUIRES: concurrency // REQUIRES: concurrency @@ -20,7 +20,7 @@ public actor MyActor { } // CHECK: #if compiler(>=5.3) && $Actors -// CHECK-NEXT: extension MyActor +// CHECK-NEXT: extension FeatureTest.MyActor public extension MyActor { // CHECK-NOT: $Actors // CHECK: testFunc @@ -34,35 +34,26 @@ public extension MyActor { // CHECK-NEXT: #endif public func globalAsync() async { } -// CHECK: #if compiler(>=5.3) && $MarkerProtocol -// CHECK-NEXT: public protocol MP { +// CHECK: @_marker public protocol MP { // CHECK-NEXT: } -// CHECK-NEXT: #else -// CHECK-NEXT: public typealias MP = Any -// CHECK-NEXT: #endif @_marker public protocol MP { } -// CHECK: #if compiler(>=5.3) && $MarkerProtocol -// CHECK-NEXT: @_marker public protocol MP2 : FeatureTest.MP { +// CHECK: @_marker public protocol MP2 : FeatureTest.MP { // CHECK-NEXT: } -// CHECK-NEXT: #else -// CHECK-NEXT: public typealias MP2 = Any -// CHECK-NEXT: #endif @_marker public protocol MP2: MP { } -// CHECK: #if compiler(>=5.3) && $MarkerProtocol -// CHECK-NEXT: public protocol MP3 : AnyObject, FeatureTest.MP { +// CHECK-NOT: #if compiler(>=5.3) && $MarkerProtocol +// CHECK: public protocol MP3 : AnyObject, FeatureTest.MP { // CHECK-NEXT: } public protocol MP3: AnyObject, MP { } -// CHECK: #if compiler(>=5.3) && $MarkerProtocol -// CHECK-NEXT: extension MP2 { +// CHECK: extension FeatureTest.MP2 { // CHECK-NEXT: func inMP2 extension MP2 { public func inMP2() { } } -// CHECK: class OldSchool { +// CHECK: class OldSchool : FeatureTest.MP { public class OldSchool: MP { // CHECK: #if compiler(>=5.3) && $AsyncAwait // CHECK-NEXT: takeClass() @@ -70,7 +61,7 @@ public class OldSchool: MP { public func takeClass() async { } } -// CHECK: class OldSchool2 { +// CHECK: class OldSchool2 : FeatureTest.MP { public class OldSchool2: MP { // CHECK: #if compiler(>=5.3) && $AsyncAwait // CHECK-NEXT: takeClass() @@ -113,13 +104,13 @@ public struct IsRP: RP { // CHECK-NEXT: public func acceptsRP public func acceptsRP(_: T) { } -// CHECK: #if compiler(>=5.3) && $MarkerProtocol -// CHECK-NEXT: extension Array : FeatureTest.MP where Element : FeatureTest.MP { +// CHECK-NOT: #if compiler(>=5.3) && $MarkerProtocol +// CHECK: extension Swift.Array : FeatureTest.MP where Element : FeatureTest.MP { extension Array: FeatureTest.MP where Element : FeatureTest.MP { } // CHECK: } -// CHECK: #if compiler(>=5.3) && $MarkerProtocol -// CHECK-NEXT: extension OldSchool : Swift.UnsafeSendable { +// CHECK-NOT: #if compiler(>=5.3) && $MarkerProtocol +// CHECK: extension FeatureTest.OldSchool : Swift.UnsafeSendable { extension OldSchool: UnsafeSendable { } // CHECK-NEXT: } @@ -151,9 +142,4 @@ public func stage(with actor: MyActor) { } // CHECK-NEXT: #endif public func asyncIsh(@_inheritActorContext operation: @Sendable @escaping () async -> Void) { } -// CHECK-NOT: extension MyActor : Swift.Sendable - -// CHECK: #if compiler(>=5.3) && $MarkerProtocol -// CHECK-NEXT: extension OldSchool : FeatureTest.MP { -// CHECK-NEXT: #endif - +// CHECK-NOT: extension FeatureTest.MyActor : Swift.Sendable diff --git a/test/ModuleInterface/fixed-layout-property-initializers.swift b/test/ModuleInterface/fixed-layout-property-initializers.swift index 3f75ecf019e83..54df8177b7eb4 100644 --- a/test/ModuleInterface/fixed-layout-property-initializers.swift +++ b/test/ModuleInterface/fixed-layout-property-initializers.swift @@ -1,15 +1,15 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t.swiftinterface %s -// RUN: %FileCheck %s --check-prefix FROMSOURCE --check-prefix NONRESILIENT --check-prefix COMMON < %t.swiftinterface +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t/Test.swiftinterface %s -module-name Test +// RUN: %FileCheck %s --check-prefix FROMSOURCE --check-prefix NONRESILIENT --check-prefix COMMON < %t/Test.swiftinterface -// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t-resilient.swiftinterface -enable-library-evolution %s -// RUN: %FileCheck %s --check-prefix FROMSOURCE --check-prefix RESILIENT --check-prefix COMMON < %t-resilient.swiftinterface +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t/TestResilient.swiftinterface -enable-library-evolution %s -module-name TestResilient +// RUN: %FileCheck %s --check-prefix FROMSOURCE --check-prefix RESILIENT --check-prefix COMMON < %t/TestResilient.swiftinterface -// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule %t.swiftinterface -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule %t/Test.swiftinterface -disable-objc-attr-requires-foundation-module // RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -module-name Test -emit-module-interface-path - | %FileCheck %s --check-prefix FROMMODULE --check-prefix NONRESILIENT --check-prefix COMMON -// RUN: %target-swift-frontend -emit-module -o %t/TestResilient.swiftmodule -enable-library-evolution %t-resilient.swiftinterface -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend -emit-module -o %t/TestResilient.swiftmodule -enable-library-evolution %t/TestResilient.swiftinterface -disable-objc-attr-requires-foundation-module // RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/TestResilient.swiftmodule -module-name TestResilient -enable-library-evolution -emit-module-interface-path - | %FileCheck %s --check-prefix FROMMODULE --check-prefix RESILIENT --check-prefix COMMON // COMMON: @frozen public struct MyStruct { diff --git a/test/ModuleInterface/global-actor.swift b/test/ModuleInterface/global-actor.swift index 6be1f88c73e3d..5663d7730b110 100644 --- a/test/ModuleInterface/global-actor.swift +++ b/test/ModuleInterface/global-actor.swift @@ -3,18 +3,18 @@ import MeowActor -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @MeowActor func doMeow() {} -// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-library-evolution -emit-module -o %t/MeowActor.swiftmodule %S/Inputs/MeowActor.swift -// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-silgen %s -I %t | %FileCheck --check-prefix CHECK-RESILIENT %s +// RUN: %target-swift-frontend -disable-availability-checking -enable-library-evolution -emit-module -o %t/MeowActor.swiftmodule %S/Inputs/MeowActor.swift +// RUN: %target-swift-frontend -disable-availability-checking -emit-silgen %s -I %t | %FileCheck --check-prefix CHECK-RESILIENT %s // CHECK-RESILIENT: metatype $@thick MeowActor.Type -// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module -o %t/MeowActor.swiftmodule %S/Inputs/MeowActor.swift -// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-silgen %s -I %t | %FileCheck --check-prefix CHECK-FRAGILE %s +// RUN: %target-swift-frontend -disable-availability-checking -emit-module -o %t/MeowActor.swiftmodule %S/Inputs/MeowActor.swift +// RUN: %target-swift-frontend -disable-availability-checking -emit-silgen %s -I %t | %FileCheck --check-prefix CHECK-FRAGILE %s // CHECK-FRAGILE: metatype $@thin MeowActor.Type -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func someFunc() async { await doMeow() } \ No newline at end of file diff --git a/test/ModuleInterface/ignorable-compiler-flags.swift b/test/ModuleInterface/ignorable-compiler-flags.swift new file mode 100644 index 0000000000000..af6bd1d2d989e --- /dev/null +++ b/test/ModuleInterface/ignorable-compiler-flags.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/textual) +// RUN: %empty-directory(%t/module-cache) + +// RUN: echo "// swift-interface-format-version: 1.0" > %t/textual/Foo.swiftinterface +// RUN: echo "// swift-module-flags: -swift-version 5 -enable-library-evolution -module-name Foo" >> %t/textual/Foo.swiftinterface + +// RUN: %target-swift-ide-test -print-module-metadata -module-to-print Foo -I %t/textual -source-filename %s -module-cache-path %t/module-cache | %FileCheck %s --check-prefix=USER-MODULE-PRINT-NOT + +// RUN: echo "// swift-module-flags-ignorable: -enable-library-evolution -user-module-version 13.13 -future-flag1 3 -future-flag2 abc -future-flag3 /tmp/t.swift /tmp/u.swift -tbd-install_name=aaa" >> %t/textual/Foo.swiftinterface + +// RUN: %target-swift-ide-test -print-module-metadata -module-to-print Foo -I %t/textual -source-filename %s -module-cache-path %t/module-cache | %FileCheck %s --check-prefix=USER-MODULE-PRINT + +// USER-MODULE-PRINT-NOT-NOT: user module version: 13.13.0.0 +// USER-MODULE-PRINT: user module version: 13.13.0.0 diff --git a/test/ModuleInterface/indirect-conformance-implementation-only.swift b/test/ModuleInterface/indirect-conformance-implementation-only.swift new file mode 100644 index 0000000000000..2c2e2812094a4 --- /dev/null +++ b/test/ModuleInterface/indirect-conformance-implementation-only.swift @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-frontend -emit-module %t/SecretLib.swift -o %t/SecretLib.swiftmodule +// RUN: %target-swift-frontend -emit-module %t/Client.swift -I %t -emit-module-interface-path %t/Client.swiftinterface -enable-library-evolution -swift-version 5 + +/// The indirect conformance of `s` to `_p` should not be printed. (rdar://78718838) +// RUN: cat %t/Client.swiftinterface | %FileCheck %s +// CHECK-NOT: SecretLib + +// BEGIN SecretLib.swift +public protocol _p {} +public struct _s : _p {} + +// BEGIN Client.swift +@_implementationOnly import SecretLib +protocol p : _p {} +public struct s : p {} +public func test(s1 : s) {} diff --git a/test/ModuleInterface/infer-arch-from-file.swift b/test/ModuleInterface/infer-arch-from-file.swift new file mode 100644 index 0000000000000..07c0d1dccc1ee --- /dev/null +++ b/test/ModuleInterface/infer-arch-from-file.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/Bar.swiftmodule) +// RUN: echo "// swift-interface-format-version: 1.0" > %t/arm64.swiftinterface +// RUN: echo "// swift-module-flags: -module-name arm64 -target arm64e-apple-macos11.0" >> %t/arm64.swiftinterface + +import arm64 + +// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t -target arm64-apple-macos11.0 +// RUN: %FileCheck %s < %t/deps.json + +// CHECK-NOT: arm64e-apple-macos11.0 diff --git a/test/ModuleInterface/preserve-type-repr.swift b/test/ModuleInterface/preserve-type-repr.swift index 909623086bff6..79a146a15d4ad 100644 --- a/test/ModuleInterface/preserve-type-repr.swift +++ b/test/ModuleInterface/preserve-type-repr.swift @@ -1,5 +1,30 @@ -// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -module-name PreferTypeRepr -module-interface-preserve-types-as-written | %FileCheck %s --check-prefix PREFER -// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -module-name PreferTypeRepr | %FileCheck %s --check-prefix DONTPREFER +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -typecheck -enable-library-evolution -emit-module-interface-path %t/External.swiftinterface -module-name External -DEXTERNAL +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -module-name PreferTypeRepr -module-interface-preserve-types-as-written -I %t | %FileCheck %s --check-prefix PREFER +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -module-name PreferTypeRepr -I %t | %FileCheck %s --check-prefix DONTPREFER + +#if EXTERNAL +public struct Toy { + public init() {} +} + +public struct GenericToy { + public init() {} +} +#else +import External + +// PREFER: extension Toy +// DONTPREFER: extension External.Toy +extension Toy { + public static var new: Toy { Toy() } +} + +// PREFER: extension GenericToy { +// DONTPREFER: extension External.GenericToy { +extension GenericToy { + public static var new: GenericToy { GenericToy() } +} public protocol Pet {} @@ -32,3 +57,4 @@ extension My where T: Pet { // PREFER: public func isNoMore(_ pet: Ex) -> Bool // DONTPREFER: public func isNoMore(_ pet: PreferTypeRepr.Ex) -> Swift.Bool public func isNoMore(_ pet: Ex) -> Bool {} +#endif diff --git a/test/ModuleInterface/skip-override-spi.swift b/test/ModuleInterface/skip-override-spi.swift new file mode 100644 index 0000000000000..26f185cf56914 --- /dev/null +++ b/test/ModuleInterface/skip-override-spi.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/inputs) +// RUN: %empty-directory(%t/modulecache) +// RUN: echo "public class HideyHole { @_spi(Private) public init() {} }" > %t/Foo.swift +// RUN: echo "public class StashyCache: HideyHole {}" >> %t/Foo.swift + +// RUN: %target-swift-frontend -emit-module -emit-module-interface-path %t/inputs/Foo.swiftinterface %t/Foo.swift -module-name Foo + +// RUN: %target-swift-frontend -emit-module-path %t/Bar.swiftmodule -enable-library-evolution -enable-objc-interop -disable-objc-attr-requires-foundation-module -module-name Bar %s -I %t/inputs -disable-availability-checking -module-cache-path %t/modulecache + +import Foo \ No newline at end of file diff --git a/test/ModuleInterface/stdlib_rebuild.swift b/test/ModuleInterface/stdlib_rebuild.swift new file mode 100644 index 0000000000000..9c872e963c9eb --- /dev/null +++ b/test/ModuleInterface/stdlib_rebuild.swift @@ -0,0 +1,27 @@ +// RUN: %empty-directory(%t.mcps) + +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs/stdlib_rebuild -resource-dir %S/Inputs/stdlib_rebuild -module-cache-path %t.mcps/rebuild-remarks-off) -typecheck %s 2>&1 | %FileCheck -check-prefixes SLOW-DIAG,ALL %s +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs/stdlib_rebuild -resource-dir %S/Inputs/stdlib_rebuild -module-cache-path %t.mcps/rebuild-remarks-off) -typecheck %s 2>&1 | %FileCheck -check-prefixes ALL --allow-empty %s +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs/stdlib_rebuild -resource-dir %S/Inputs/stdlib_rebuild -module-cache-path %t.mcps/rebuild-remarks-off) -D OTHER_IMPORT -typecheck %s 2>&1 | %FileCheck -check-prefixes ALL --allow-empty %s + +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs/stdlib_rebuild -resource-dir %S/Inputs/stdlib_rebuild -module-cache-path %t.mcps/rebuild-remarks-on) -Rmodule-interface-rebuild -typecheck %s 2>&1 | %FileCheck -check-prefixes NORMAL-DIAG,ALL %s +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs/stdlib_rebuild -resource-dir %S/Inputs/stdlib_rebuild -module-cache-path %t.mcps/rebuild-remarks-on) -Rmodule-interface-rebuild -typecheck %s 2>&1 | %FileCheck -check-prefixes ALL --allow-empty %s + +// Our test directory only contains interfaces for x86_64-apple-macos. +// REQUIRES: CPU=x86_64 +// REQUIRES: OS=macosx + +#if OTHER_IMPORT +import OtherModule +#endif + +func fn(_: Int) {} + +// SLOW-DIAG: remark: did not find a prebuilt standard library for target '{{.*}}' compatible with this Swift compiler; building it may take a few minutes, but it should only happen once for this combination of compiler and target + +// NORMAL-DIAG-DAG: remark: rebuilding module 'Swift' from interface '{{.*}}' +// NORMAL-DIAG-DAG: remark: rebuilding module '_Concurrency' from interface '{{.*}}' + +// Used even when one of the above ones is also used, since the diagnostics should only be emitted once +// ALL-NOT: remark: did not find a prebuilt standard library +// ALL-NOT: remark: rebuilding module '{{Swift|_Concurrency}}' from interface '{{.*}}' diff --git a/test/ModuleInterface/swift_build_sdk_interfaces/ignore-non-stdlib-failures.test-sh b/test/ModuleInterface/swift_build_sdk_interfaces/ignore-non-stdlib-failures.test-sh index 5747b3cfe0da7..566a3ab8606c1 100644 --- a/test/ModuleInterface/swift_build_sdk_interfaces/ignore-non-stdlib-failures.test-sh +++ b/test/ModuleInterface/swift_build_sdk_interfaces/ignore-non-stdlib-failures.test-sh @@ -1,3 +1,6 @@ +# Timing out in CI +REQUIRES: rdar78483379 + RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -v -o %t/output | %FileCheck %s CHECK-DAG: # (FAIL) {{.+}}{{\\|/}}Bad.swiftinterface diff --git a/test/ModuleInterface/swift_build_sdk_interfaces/xfail-logs.test-sh b/test/ModuleInterface/swift_build_sdk_interfaces/xfail-logs.test-sh index 51849fcf2431a..2bdea2cb3c3d6 100644 --- a/test/ModuleInterface/swift_build_sdk_interfaces/xfail-logs.test-sh +++ b/test/ModuleInterface/swift_build_sdk_interfaces/xfail-logs.test-sh @@ -1,3 +1,6 @@ +# Timing out in CI +REQUIRES: rdar78318467 + RUN: %empty-directory(%t) RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -o %t/output -log-path %t/logs | %FileCheck %s RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/logs/Bad-Bad-err.txt diff --git a/test/ModuleInterface/synthesized.swift b/test/ModuleInterface/synthesized.swift index acc58c310d212..0d7f33b4abd4b 100644 --- a/test/ModuleInterface/synthesized.swift +++ b/test/ModuleInterface/synthesized.swift @@ -38,21 +38,21 @@ public enum NoRawValueWithExplicitHashable { case a, b, c } // CHECK: {{^}$}} -// CHECK-LABEL: extension NoRawValueWithExplicitHashable : Swift.Hashable { +// CHECK-LABEL: extension synthesized.NoRawValueWithExplicitHashable : Swift.Hashable { extension NoRawValueWithExplicitHashable : Hashable { // CHECK-NEXT: public func foo() public func foo() {} } // CHECK: {{^}$}} -// CHECK: extension HasRawValue : Swift.Equatable {} -// CHECK: extension HasRawValue : Swift.Hashable {} -// CHECK: extension HasRawValue : Swift.RawRepresentable {} +// CHECK: extension synthesized.HasRawValue : Swift.Equatable {} +// CHECK: extension synthesized.HasRawValue : Swift.Hashable {} +// CHECK: extension synthesized.HasRawValue : Swift.RawRepresentable {} -// CHECK: extension ObjCEnum : Swift.Equatable {} -// CHECK: extension ObjCEnum : Swift.Hashable {} -// CHECK: extension ObjCEnum : Swift.RawRepresentable {} +// CHECK: extension synthesized.ObjCEnum : Swift.Equatable {} +// CHECK: extension synthesized.ObjCEnum : Swift.Hashable {} +// CHECK: extension synthesized.ObjCEnum : Swift.RawRepresentable {} -// CHECK: extension NoRawValueWithExplicitEquatable : Swift.Hashable {} +// CHECK: extension synthesized.NoRawValueWithExplicitEquatable : Swift.Hashable {} // NEGATIVE-NOT: extension {{.+}}NoRawValueWithExplicitEquatable : Swift.Equatable // NEGATIVE-NOT: NoRawValueWithExplicitHashable : Swift.Equatable diff --git a/test/ModuleInterface/type-shadowing.swift b/test/ModuleInterface/type-shadowing.swift new file mode 100644 index 0000000000000..e8ce8505e72b4 --- /dev/null +++ b/test/ModuleInterface/type-shadowing.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck %s -swift-version 5 -enable-library-evolution -emit-module-interface-path %t/Bakery.swiftinterface -DBAKERY -module-name Bakery +// RUN: %target-swift-frontend -typecheck %s -swift-version 5 -enable-library-evolution -emit-module-interface-path %t/Home.swiftinterface -DHOME -module-name Home -I %t +// RUN: %FileCheck %s < %t/Home.swiftinterface + +#if BAKERY +public class Cake { + public init(_: ()) {} +} +#endif + +#if HOME +import Bakery + +struct Cake { + public init() {} +} + +extension Bakery.Cake { + public convenience init() {} +} +#endif + +// CHECK: extension Bakery.Cake diff --git a/test/ModuleInterface/user-module-version.swift b/test/ModuleInterface/user-module-version.swift index 1a0e3334dd604..eada790b7827d 100644 --- a/test/ModuleInterface/user-module-version.swift +++ b/test/ModuleInterface/user-module-version.swift @@ -3,7 +3,7 @@ // RUN: %empty-directory(%t/binary) // RUN: %empty-directory(%t/module-cache) -// RUN: %target-swift-frontend -emit-module %s -module-name Foo -swift-version 5 -disable-implicit-concurrency-module-import -user-module-version 113.33 -emit-module-interface-path %t/textual/Foo.swiftinterface -enable-library-evolution -emit-module-path %t/binary/Foo.swiftmodule +// RUN: %target-swift-frontend -emit-module %s -module-name Foo -swift-version 5 -disable-implicit-concurrency-module-import -user-module-version 113.33.44.55.66.77 -emit-module-interface-path %t/textual/Foo.swiftinterface -enable-library-evolution -emit-module-path %t/binary/Foo.swiftmodule // RUN: %FileCheck %s --check-prefix=INTERFACE-FLAG < %t/textual/Foo.swiftinterface @@ -15,6 +15,6 @@ // RUN: %target-swift-frontend -compile-module-from-interface %t/textual/Foo.swiftinterface -o %t/binary/Foo.swiftmodule // RUN: %target-swift-ide-test -print-module-metadata -module-to-print Foo -I %t/binary -source-filename %s | %FileCheck %s --check-prefix=USER-MODULE-PRINT -// INTERFACE-FLAG: -user-module-version 113.33 +// INTERFACE-FLAG: -user-module-version 113.33.44.55 -// USER-MODULE-PRINT: user module version: 113.33 +// USER-MODULE-PRINT: user module version: 113.33.44.55 diff --git a/test/ModuleInterface/where-clause.swift b/test/ModuleInterface/where-clause.swift index ebc664e748211..c8a29075dd25e 100644 --- a/test/ModuleInterface/where-clause.swift +++ b/test/ModuleInterface/where-clause.swift @@ -44,10 +44,10 @@ public struct Holder { // CHECK-NEXT: } } -// CHECK-NEXT: extension Holder.Transform where Value == Swift.Int { +// CHECK-NEXT: extension main.Holder.Transform where Value == Swift.Int { extension Holder.Transform where Value == Int { // CHECK-NEXT: public func negate(_ holder: main.Holder) -> Result{{$}} public func negate(_ holder: Holder) -> Result { return transform(Holder(value: -holder.value)) } -} \ No newline at end of file +} diff --git a/test/Parse/async-syntax.swift b/test/Parse/async-syntax.swift index 340c00aa71403..b7ce5c4e5df8c 100644 --- a/test/Parse/async-syntax.swift +++ b/test/Parse/async-syntax.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -verify-syntax-tree +// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-syntax-tree // REQUIRES: concurrency diff --git a/test/Parse/async.swift b/test/Parse/async.swift index 621ec8ec5f1ae..64bf95f54c2a8 100644 --- a/test/Parse/async.swift +++ b/test/Parse/async.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency @@ -75,7 +75,7 @@ func testAwaitExpr() async { func getIntSomeday() async -> Int { 5 } func testAsyncLet() async { - spawn let x = await getIntSomeday() + async let x = await getIntSomeday() _ = await x } diff --git a/test/Parse/availability_query.swift b/test/Parse/availability_query.swift index a6f91497402dc..79c786801cb9d 100644 --- a/test/Parse/availability_query.swift +++ b/test/Parse/availability_query.swift @@ -12,9 +12,9 @@ let x = #available(OSX 10.51, *) // expected-error {{#available may only be use (#available(OSX 10.51, *) ? 1 : 0) // expected-error {{#available may only be used as condition of an}} -if !#available(OSX 10.52, *) { // expected-error {{#available may only be used as condition of an}} +if !#available(OSX 10.52, *) { // expected-error {{#available cannot be used as an expression, did you mean to use '#unavailable'?}} {{4-15=#unavailable}} } -if let _ = Optional(5), !#available(OSX 10.52, *) { // expected-error {{#available may only be used as condition}} +if let _ = Optional(5), !#available(OSX 10.52, *) { // expected-error {{#available cannot be used as an expression, did you mean to use '#unavailable'?}} {{25-36=#unavailable}} } if #available(OSX 10.51, *) && #available(OSX 10.52, *) { // expected-error {{expected ',' joining parts of a multi-clause condition}} {{28-31=,}} @@ -96,8 +96,6 @@ if #available(OSX 10.51 || iOS 8.0) {// expected-error {{'||' cannot be used in if #available(OSX >= 10.51, *) { // expected-error {{version comparison not needed}} {{19-22=}} } -// Following a "let" condition with #available is incorrectly rejected - // Bool then #available. if 1 != 2, #available(iOS 8.0, *) {} diff --git a/test/Parse/availability_query_unavailability.swift b/test/Parse/availability_query_unavailability.swift new file mode 100644 index 0000000000000..4bae69051a6ec --- /dev/null +++ b/test/Parse/availability_query_unavailability.swift @@ -0,0 +1,127 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: OS=macosx +// This file is mostly an inverted version of availability_query.swift +if #unavailable(OSX 10.51) { +} + +// Disallow explicit wildcards. +if #unavailable(OSX 10.51, *) {} // expected-error {{platform wildcard '*' is always implicit in #unavailable}} {{28-29=}} +// Disallow use as an expression. +if (#unavailable(OSX 10.51)) {} // expected-error {{#unavailable may only be used as condition of an 'if', 'guard'}} +let x = #unavailable(OSX 10.51) // expected-error {{#unavailable may only be used as condition of}} +(#unavailable(OSX 10.51) ? 1 : 0) // expected-error {{#unavailable may only be used as condition of an}} +if !#unavailable(OSX 10.52) { // expected-error {{#unavailable may only be used as condition of an}} +} +if let _ = Optional(5), !#unavailable(OSX 10.52) { // expected-error {{#unavailable may only be used as condition}} +} + +if #unavailable(OSX 10.51) && #unavailable(OSX 10.52) { // expected-error {{expected ',' joining parts of a multi-clause condition}} {{27-30=,}} +} + + +if #unavailable { // expected-error {{expected availability condition}} expected-error {{closure expression is unused}} expected-error {{top-level statement cannot begin with a closure expression}} expected-note {{did you mean to use a 'do' statement?}} {{17-17=do }} +} + +if #unavailable( { // expected-error {{expected platform name}} expected-error {{expected ')'}} expected-note {{to match this opening '('}} +} + +if #unavailable() { // expected-error {{expected platform name}} +} + +if #unavailable(OSX { // expected-error {{expected version number}} expected-error {{expected ')'}} expected-note {{to match this opening '('}} +} + +if #unavailable(OSX) { // expected-error {{expected version number}} +} + +if #unavailable(OSX 10.51 { // expected-error {{expected ')'}} expected-note {{to match this opening '('}} +} + +if #unavailable(iDishwasherOS 10.51) { // expected-warning {{unrecognized platform name 'iDishwasherOS'}} +} + +if #unavailable(iDishwasherOS 10.51) { // expected-warning {{unrecognized platform name 'iDishwasherOS'}} +} + +if #unavailable(OSX 10.51, OSX 10.52) { // expected-error {{version for 'macOS' already specified}} +} + +if #unavailable(OSX 10.51, iOS 8.0, *) { } // expected-error {{platform wildcard '*' is always implicit in #unavailable}} {{37-38=}} +if #unavailable(iOS 8.0) { +} + +if #unavailable(iOSApplicationExtension, unavailable) { // expected-error 2{{expected version number}} +} + +// Should this be a valid spelling since `#unvailable(*)` cannot be written? +if #unavailable() { // expected-error {{expected platform name}} +} + +if #unavailable(OSX 10 { // expected-error {{expected ')' in availability query}} expected-note {{to match this opening '('}} +} + +// Multiple platforms +if #unavailable(OSX 10.51, iOS 8.0) { +} + + +if #unavailable(OSX 10.51, { // expected-error {{expected platform name}} // expected-error {{expected ')'}} expected-note {{to match this opening '('}} +} + +if #unavailable(OSX 10.51,) { // expected-error {{expected platform name}} +} + +if #unavailable(OSX 10.51, iOS { // expected-error {{expected version number}} // expected-error {{expected ')'}} expected-note {{to match this opening '('}} +} + +if #unavailable(OSX 10.51, iOS 8.0, iDishwasherOS 10.51) { // expected-warning {{unrecognized platform name 'iDishwasherOS'}} +} + +if #unavailable(iDishwasherOS 10.51, OSX 10.51) { // expected-warning {{unrecognized platform name 'iDishwasherOS'}} +} + +if #unavailable(OSX 10.51 || iOS 8.0) {// expected-error {{'||' cannot be used in an availability condition}} +} + +// Emit Fix-It removing un-needed >=, for the moment. +if #unavailable(OSX >= 10.51) { // expected-error {{version comparison not needed}} {{21-24=}} +} + +// Bool then #unavailable. +if 1 != 2, #unavailable(iOS 8.0) {} + +// Pattern then #unavailable(iOS 8.0) { +if case 42 = 42, #unavailable(iOS 8.0) {} +if let _ = Optional(42), #unavailable(iOS 8.0) {} + +// Allow "macOS" as well. +if #unavailable(macOS 10.51) { +} + +// Prevent availability and unavailability being present in the same statement. +if #unavailable(macOS 10.51), #available(macOS 10.52, *) { // expected-error {{#available and #unavailable cannot be in the same statement}} +} +if #available(macOS 10.51, *), #unavailable(macOS 10.52) { // expected-error {{#available and #unavailable cannot be in the same statement}} +} +if #available(macOS 10.51, *), #available(macOS 10.55, *), #unavailable(macOS 10.53) { // expected-error {{#available and #unavailable cannot be in the same statement}} +} +if #unavailable(macOS 10.51), #unavailable(macOS 10.55), #available(macOS 10.53, *) { // expected-error {{#available and #unavailable cannot be in the same statement}} +} +if case 42 = 42, #available(macOS 10.51, *), #unavailable(macOS 10.52) { // expected-error {{#available and #unavailable cannot be in the same statement}} +} +if #available(macOS 10.51, *), case 42 = 42, #unavailable(macOS 10.52) { // expected-error {{#available and #unavailable cannot be in the same statement}} +} + +// Allow availabiility and unavailability to mix if they are not in the same statement. +if #unavailable(macOS 11) { + if #available(macOS 10, *) { } +} +if #available(macOS 10, *) { + if #unavailable(macOS 11) { } +} + +// Diagnose wrong spellings of unavailability +if #available(*) == false { // expected-error {{#available cannot be used as an expression, did you mean to use '#unavailable'?}} {{4-14=#unavailable}} {{18-27=}} +} +if !#available(*) { // expected-error {{#available cannot be used as an expression, did you mean to use '#unavailable'?}} {{4-15=#unavailable}} +} \ No newline at end of file diff --git a/test/Parse/effectful_properties.swift b/test/Parse/effectful_properties.swift index 4146ef231869a..f19bb303da40a 100644 --- a/test/Parse/effectful_properties.swift +++ b/test/Parse/effectful_properties.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking struct MyProps { var prop1 : Int { @@ -166,4 +166,4 @@ protocol BadP { var prop4 : Int { get reasync bogus set } var prop5 : Int { get throws async } // expected-error {{'async' must precede 'throws'}} -} \ No newline at end of file +} diff --git a/test/Parse/foreach_async.swift b/test/Parse/foreach_async.swift index 9c8309b54b642..dd1a4f00c249e 100644 --- a/test/Parse/foreach_async.swift +++ b/test/Parse/foreach_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency import _Concurrency diff --git a/test/Parse/operator_decl.swift b/test/Parse/operator_decl.swift index 3cd52b66232ff..ca6f918d0eebd 100644 --- a/test/Parse/operator_decl.swift +++ b/test/Parse/operator_decl.swift @@ -63,7 +63,7 @@ infix operator =#= infix operator +++= infix operator *** : A -infix operator --- : ; +infix operator --- : ; // expected-error {{expected precedence group name after ':' in operator declaration}} precedencegroup { // expected-error {{expected identifier after 'precedencegroup'}} associativity: right diff --git a/test/Parse/operator_decl_designated_types.swift b/test/Parse/operator_decl_designated_types.swift index 0a236141abef7..ad8c78be2aa6d 100644 --- a/test/Parse/operator_decl_designated_types.swift +++ b/test/Parse/operator_decl_designated_types.swift @@ -19,55 +19,70 @@ protocol InfixMagicOperatorProtocol { } prefix operator ^^ : PrefixMagicOperatorProtocol +// expected-warning@-1 {{designated types are no longer used by the compiler; please remove the designated type list from this operator declaration}} {{20-49=}} infix operator <*< : MediumPrecedence, InfixMagicOperatorProtocol +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{39-67=}} postfix operator ^^ : PostfixMagicOperatorProtocol +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{21-51=}} infix operator ^*^ prefix operator *^^ postfix operator ^^* infix operator **>> : UndeclaredPrecedence -// expected-error@-1 {{unknown precedence group 'UndeclaredPrecedence'}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{21-43=}} infix operator **+> : MediumPrecedence, UndeclaredProtocol -// expected-error@-1 {{cannot find type 'UndeclaredProtocol' in scope}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{39-59=}} prefix operator *+*> : MediumPrecedence -// expected-error@-1 {{cannot find type 'MediumPrecedence' in scope}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{22-40=}} postfix operator ++*> : MediumPrecedence -// expected-error@-1 {{cannot find type 'MediumPrecedence' in scope}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{23-41=}} prefix operator *++> : UndeclaredProtocol -// expected-error@-1 {{cannot find type 'UndeclaredProtocol' in scope}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{22-42=}} postfix operator +*+> : UndeclaredProtocol -// expected-error@-1 {{cannot find type 'UndeclaredProtocol' in scope}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{23-43=}} struct Struct {} class Class {} infix operator *>*> : Struct +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{21-29=}} infix operator >**> : Class +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{21-28=}} prefix operator **>> : Struct +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{22-30=}} prefix operator *>*> : Class +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{22-29=}} postfix operator >*>* : Struct +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{23-31=}} postfix operator >>** : Class +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{23-30=}} infix operator <*<<< : MediumPrecedence, & -// expected-error@-1 {{expected designated type in operator declaration}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{41-44=}} infix operator **^^ : MediumPrecedence // expected-note {{previous operator declaration here}} infix operator **^^ : InfixMagicOperatorProtocol // expected-error {{operator redeclared}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{21-50=}} infix operator ^%*%^ : MediumPrecedence, Struct, Class +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{40-55=}} infix operator ^%*%% : Struct, Class +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{30-37=}} +// expected-warning@-2 {{designated types are no longer used by the compiler}} {{22-30=}} prefix operator %^*^^ : Struct, Class +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{23-38=}} postfix operator ^^*^% : Struct, Class +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{24-39=}} prefix operator %%*^^ : LowPrecedence, Class -// expected-error@-1{{cannot find type 'LowPrecedence' in scope}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{23-45=}} postfix operator ^^*%% : MediumPrecedence, Class -// expected-error@-1{{cannot find type 'MediumPrecedence' in scope}} +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{24-49=}} -// expected-error@+1 {{trailing comma in operator declaration}} infix operator <*<>*> : AdditionPrecedence, +// expected-warning@-1 {{designated types are no longer used by the compiler}} {{43-44=}} diff --git a/test/Parse/switch.swift b/test/Parse/switch.swift index ce5dd22aeb56e..83bc62ee7dcd9 100644 --- a/test/Parse/switch.swift +++ b/test/Parse/switch.swift @@ -72,13 +72,13 @@ default: // Multiple cases per case block switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} -case 0: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +case 0: // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{8-8= break}} case 1: x = 0 } switch x { -case 0: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +case 0: // expected-error{{'case' label in a 'switch' must have at least one executable statement}} {{8-8= break}} default: x = 0 } @@ -86,13 +86,13 @@ default: switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} case 0: x = 0 -case 1: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +case 1: // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{8-8= break}} } switch x { case 0: x = 0 -default: // expected-error {{'default' label in a 'switch' should have at least one executable statement}} {{9-9= break}} +default: // expected-error {{'default' label in a 'switch' must have at least one executable statement}} {{9-9= break}} } switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} @@ -131,13 +131,13 @@ switch x { // expected-error{{'switch' statement body must have at least one 'ca } switch x { -default: // expected-error{{'default' label in a 'switch' should have at least one executable statement}} {{9-9= break}} +default: // expected-error{{'default' label in a 'switch' must have at least one executable statement}} {{9-9= break}} case 0: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} x = 0 } switch x { -default: // expected-error{{'default' label in a 'switch' should have at least one executable statement}} {{9-9= break}} +default: // expected-error{{'default' label in a 'switch' must have at least one executable statement}} {{9-9= break}} default: // expected-error{{additional 'case' blocks cannot appear after the 'default' block of a 'switch'}} x = 0 } @@ -148,11 +148,11 @@ default where x == 0: // expected-error{{'default' cannot be used with a 'where' } switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} -case 0: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +case 0: // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{8-8= break}} } switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} -case 0: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +case 0: // expected-error{{'case' label in a 'switch' must have at least one executable statement}} {{8-8= break}} case 1: x = 0 } @@ -160,7 +160,7 @@ case 1: switch x { // expected-error {{switch must be exhaustive}} expected-note{{do you want to add a default clause?}} case 0: x = 0 -case 1: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{8-8= break}} +case 1: // expected-error{{'case' label in a 'switch' must have at least one executable statement}} {{8-8= break}} } @@ -206,15 +206,15 @@ case (_, 2), (var a, _): // expected-error {{'a' must be bound in every pattern} case (var a, 2), (1, var b): // expected-error {{'a' must be bound in every pattern}} expected-error {{'b' must be bound in every pattern}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} () -case (var a, 2): // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{17-17= break}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} +case (var a, 2): // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{17-17= break}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} case (1, _): () -case (_, 2): // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{13-13= break}} +case (_, 2): // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{13-13= break}} case (1, var a): // expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} () -case (var a, 2): // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{17-17= break}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} +case (var a, 2): // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{17-17= break}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} case (1, var b): // expected-warning {{variable 'b' was never used; consider replacing with '_' or removing it}} () @@ -238,7 +238,7 @@ case (var a, var b), (var b, var a): // expected-warning {{variable 'a' was neve // expected-warning@-2 {{case is already handled by previous patterns; consider removing it}} () -case (_, 2): // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{13-13= break}} +case (_, 2): // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{13-13= break}} case (1, _): () } @@ -353,13 +353,13 @@ func f1(x: String, y: Whichever) { switch Whatever.Thing { -case .Thing: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{13-13= break}} +case .Thing: // expected-error{{'case' label in a 'switch' must have at least one executable statement}} {{13-13= break}} @unknown case _: x = 0 } switch Whatever.Thing { -case .Thing: // expected-error{{'case' label in a 'switch' should have at least one executable statement}} {{13-13= break}} +case .Thing: // expected-error{{'case' label in a 'switch' must have at least one executable statement}} {{13-13= break}} @unknown default: x = 0 } @@ -367,13 +367,13 @@ case .Thing: // expected-error{{'case' label in a 'switch' should have at least switch Whatever.Thing { case .Thing: x = 0 -@unknown case _: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{17-17= break}} +@unknown case _: // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{17-17= break}} } switch Whatever.Thing { case .Thing: x = 0 -@unknown default: // expected-error {{'default' label in a 'switch' should have at least one executable statement}} {{18-18= break}} +@unknown default: // expected-error {{'default' label in a 'switch' must have at least one executable statement}} {{18-18= break}} } diff --git a/test/Parse/trailing_closures.swift b/test/Parse/trailing_closures.swift index 3c8fcc73fe6e3..ed103eae3cfdb 100644 --- a/test/Parse/trailing_closures.swift +++ b/test/Parse/trailing_closures.swift @@ -82,7 +82,7 @@ func test_multiple_trailing_syntax_without_labels() { fn {} g: {} // Ok - fn {} _: {} // expected-error {{missing argument labels 'f:g:' in call}} + fn {} _: {} // expected-error {{missing argument label 'g:' in call}} {{9-10=g}} {{none}} fn {} g: <#T##() -> Void#> // expected-error {{editor placeholder in source file}} @@ -93,28 +93,15 @@ func test_multiple_trailing_syntax_without_labels() { func mixed_args_1(a: () -> Void, _: () -> Void) {} func mixed_args_2(_: () -> Void, a: () -> Void, _: () -> Void) {} // expected-note {{'mixed_args_2(_:a:_:)' declared here}} - mixed_args_1 - {} - _: {} + mixed_args_1 {} _: {} - mixed_args_1 - {} // expected-error {{incorrect argument labels in call (have '_:a:', expected 'a:_:')}} - a: {} + mixed_args_1 {} a: {} // expected-error@:16 {{extraneous argument label 'a:' in call}} {{19-20=_}} {{none}} - mixed_args_2 - {} - a: {} - _: {} + mixed_args_2 {} a: {} _: {} - mixed_args_2 - {} // expected-error {{missing argument for parameter 'a' in call}} - _: {} + mixed_args_2 {} _: {} // expected-error@:18 {{missing argument for parameter 'a' in call}} {{18-18= a: <#() -> Void#>}} {{none}} - // FIXME: not a good diagnostic - mixed_args_2 - {} // expected-error {{missing argument label 'a:' in call}} - _: {} - _: {} + mixed_args_2 {} _: {} _: {} // expected-error@:16 {{missing argument label 'a:' in call}} {{19-20=a}} {{none}} } func produce(fn: () -> Int?, default d: () -> Int) -> Int { // expected-note {{declared here}} @@ -129,7 +116,7 @@ func f() -> Int { 42 } // This should be interpreted as a trailing closure, instead of being // interpreted as a computed property with undesired initial value. struct TrickyTest { - var x : Int = f () { // expected-error {{argument passed to call that takes no arguments}} + var x : Int = f () { // expected-error {{extra trailing closure passed in call}} 3 } } diff --git a/test/Parse/versioned_canimport.swift b/test/Parse/versioned_canimport.swift index 3599dd107b44b..750dc54bdf780 100644 --- a/test/Parse/versioned_canimport.swift +++ b/test/Parse/versioned_canimport.swift @@ -56,3 +56,21 @@ func canImportVersioned() { let h = 1 // expected-warning {{initialization of immutable value 'h' was never used; consider replacing with assignment to '_' or removing it}} #endif } + +func canImportVersionedString() { +#if canImport(Foo, _version: "113.331") + let a = 1 +#endif + +#if canImport(Foo, _version: "113.3000") + let b = 1 +#endif + +#if canImport(Foo, _version: "4") + let d = 1 // expected-warning {{initialization of immutable value 'd' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Foo, _version: "113.33") + let e = 1 // expected-warning {{initialization of immutable value 'e' was never used; consider replacing with assignment to '_' or removing it}} +#endif +} diff --git a/test/PrintAsObjC/async.swift b/test/PrintAsObjC/async.swift index 6de2c30df46f6..2e6d32923e25b 100644 --- a/test/PrintAsObjC/async.swift +++ b/test/PrintAsObjC/async.swift @@ -3,15 +3,15 @@ // RUN: %empty-directory(%t) // FIXME: BEGIN -enable-source-import hackaround -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/CoreGraphics.swift -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/Foundation.swift -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/AppKit.swift +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift -disable-objc-attr-requires-foundation-module -disable-availability-checking +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/CoreGraphics.swift -disable-availability-checking +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/Foundation.swift -disable-availability-checking +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/AppKit.swift -disable-availability-checking // FIXME: END -enable-source-import hackaround // REQUIRES: concurrency -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -parse-as-library %s -typecheck -I %S/Inputs/custom-modules -emit-objc-header-path %t/async.h -import-objc-header %S/../Inputs/empty.h -enable-experimental-concurrency -typecheck +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -parse-as-library %s -typecheck -I %S/Inputs/custom-modules -emit-objc-header-path %t/async.h -import-objc-header %S/../Inputs/empty.h -typecheck -disable-availability-checking // RUN: %FileCheck %s < %t/async.h // RUN: %check-in-clang -I %S/Inputs/custom-modules/ %t/async.h @@ -19,13 +19,13 @@ import Foundation // CHECK-LABEL: @interface BarClass : NSObject @objc @objcMembers class BarClass: NSObject { - // CHECK: (void)doSomethingBigWithCompletionHandler:(void (^)(NSInteger))completionHandler; + // CHECK: (void)doSomethingBigWithCompletionHandler:(void (^ _Nonnull)(NSInteger))completionHandler; func doSomethingBig() async -> Int { 0 } - // CHECK: - (void)longRunningWithString:(NSString * _Nonnull)string completionHandler:(void (^)(BarClass * _Nullable, NSError * _Nullable))completionHandler; + // CHECK: - (void)longRunningWithString:(NSString * _Nonnull)string completionHandler:(void (^ _Nonnull)(BarClass * _Nullable, NSError * _Nullable))completionHandler; func longRunning(string: String) async throws -> BarClass { return self } - // CHECK: - (void)magicTupleReturnWithCompletionHandler:(void (^)(BarClass * _Nonnull, NSInteger))completionHandler; + // CHECK: - (void)magicTupleReturnWithCompletionHandler:(void (^ _Nonnull)(BarClass * _Nonnull, NSInteger))completionHandler; func magicTupleReturn() async -> (BarClass, Int) { return (self, 0) } } // CHECK: @end diff --git a/test/PrintAsObjC/bridged-known-types.swift b/test/PrintAsObjC/bridged-known-types.swift new file mode 100644 index 0000000000000..63326e67b80a9 --- /dev/null +++ b/test/PrintAsObjC/bridged-known-types.swift @@ -0,0 +1,29 @@ +// Test that known types that conform to `_ObjectiveCBridgeable` import or +// forward-declare based on the Clang type in their known type mapping, not +// their bridged type. +// +// This is particularly important for `CGFloat`, which has a native Swift decl +// in the CoreGraphics overlay that shadows the imported Clang decl, so relying +// solely on whether or not the decl has a Clang node is not sufficient. + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck %s -parse-as-library -emit-objc-header-path %t/swift.h +// RUN: %FileCheck %s < %t/swift.h + +// REQUIRES: objc_interop + +import CoreGraphics +import Foundation + +// CHECK: @import CoreGraphics; + +// CHECK-NOT: @class NSNumber; + +// CHECK-LABEL: @interface Test : NSObject{{$}} +public class Test: NSObject { + // CHECK-NEXT: - (CGFloat)level + @objc public func level() -> CGFloat { 9000.0 } + // CHECK-NEXT: - (BOOL)isEnabled + @objc public func isEnabled() -> Bool { true } + // CHECK-NEXT: init +} // CHECK-NEXT: @end diff --git a/test/PrintAsObjC/error.swift b/test/PrintAsObjC/error.swift index 6bcea648c4e21..9b48070db5514 100644 --- a/test/PrintAsObjC/error.swift +++ b/test/PrintAsObjC/error.swift @@ -1,18 +1,23 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module-path %t/error.swiftmodule -emit-objc-header-path %t/error.h -experimental-allow-module-with-compiler-errors %s -// RUN: %FileCheck %s < %t/error.h +// RUN: %FileCheck --input-file %t/error.h %s // RUN: %check-in-clang %t/error.h // REQUIRES: objc_interop import Foundation +// TODO: Ideally we'd output invalid decls regardless (so that they could eg. be used in code +// completion), but we avoid doing so for now to prevent crashes. Revisit later to handle a possibly +// invalid AST while printing the ObjectiveC header - see SR-15088. + @objc class ErrorClass: NSObject { // CHECK: @interface ErrorClass @objc let member: Int // CHECK: @property {{.*}} NSInteger member; + @objc let invalidMember: undefined - // TODO: Not output on invalid type + // TODO: Missing @objc func method() {} // CHECK: - (void)method; @@ -33,10 +38,10 @@ import Foundation } @objc func invalidRet() -> undefined {} - // CHECK: - (/* error */id)invalidRet + // TODO: Missing @objc func invalidParams(a: undefined) {} - // TODO: Not output with invalid parameters + // TODO: Missing @objc(invalid::) func invalidRenamedMethod() {} @@ -55,3 +60,6 @@ import Foundation @objc class InvalidParent: undefined {} // CHECK: @interface InvalidParent + +// Used to crash during sorting due to assumptions regarding the Decl kind +@objc class ErrorClass: NSObject {} diff --git a/test/Prototypes/BigInt.swift b/test/Prototypes/BigInt.swift index 2399f9fbef5fd..624dd7fafd667 100644 --- a/test/Prototypes/BigInt.swift +++ b/test/Prototypes/BigInt.swift @@ -12,6 +12,7 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift -swift-version 4 -o %t/a.out %s +// RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out // REQUIRES: executable_test // REQUIRES: CPU=x86_64 diff --git a/test/Reflection/Inputs/ConcurrencyTypes.swift b/test/Reflection/Inputs/ConcurrencyTypes.swift new file mode 100644 index 0000000000000..58b52d40bfed0 --- /dev/null +++ b/test/Reflection/Inputs/ConcurrencyTypes.swift @@ -0,0 +1,9 @@ +@available(SwiftStdlib 5.5, *) +public actor SomeActor { +} + +@available(SwiftStdlib 5.5, *) +public struct UsesConcurrency { + public var mainActorFunction: @MainActor () -> Void + public var actorIsolatedFunction: (isolated SomeActor) -> Void +} diff --git a/test/Reflection/typeref_decoding_concurrency.swift b/test/Reflection/typeref_decoding_concurrency.swift new file mode 100644 index 0000000000000..53489cbdd111f --- /dev/null +++ b/test/Reflection/typeref_decoding_concurrency.swift @@ -0,0 +1,37 @@ +// REQUIRES: no_asan +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: libdispatch + +// REQUIRES: CPU=x86_64 +// REQUIRES: OS=macosx + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift -Xfrontend -enable-anonymous-context-mangled-names %S/Inputs/ConcurrencyTypes.swift -parse-as-library -emit-module -emit-library -module-name TypesToReflect -o %t/%target-library-name(TypesToReflect) -target x86_64-apple-macosx12.0 +// RUN: %target-build-swift -Xfrontend -enable-anonymous-context-mangled-names %S/Inputs/ConcurrencyTypes.swift %S/Inputs/main.swift -emit-module -emit-executable -module-name TypesToReflect -o %t/TypesToReflect -target x86_64-apple-macosx12.0 + +// For macOS versions before 12.0, the mangling for concurrency-related +// types cannot be used to create type metadata. + +// RUN: %target-swift-reflection-dump -binary-filename %t/%target-library-name(TypesToReflect) | %FileCheck %s +// RUN: %target-swift-reflection-dump -binary-filename %t/TypesToReflect | %FileCheck %s + +// CHECK: FIELDS: +// CHECK: ======= +// CHECK: TypesToReflect.UsesConcurrency +// CHECK: ------------------ +// CHECK: mainActorFunction: @Swift.MainActor () -> () +// CHECK: (function +// CHECK: (global-actor +// CHECK: (class Swift.MainActor)) + +// CHECK: actorIsolatedFunction: (isolated TypesToReflect.SomeActor) -> () +// CHECK: (function +// CHECK: (parameters +// CHECK: isolated +// CHECK: (class TypesToReflect.SomeActor)) diff --git a/test/RemoteAST/existentials_objc.swift b/test/RemoteAST/existentials_objc.swift index 00d97e57e6f9c..34854c8d68481 100644 --- a/test/RemoteAST/existentials_objc.swift +++ b/test/RemoteAST/existentials_objc.swift @@ -3,6 +3,11 @@ // REQUIRES: swift-remoteast-test // REQUIRES: objc_interop +// This is an interpreter test that cannot use swift-darwin-postprocess.py to +// work around the DYLD_LIBRARY_LOAD bug in recent dylds. We need to find an +// alternative workaround for it, such as bumping this test's deployment target. +// REQUIRES: rdar78933143 + import Foundation @_silgen_name("printDynamicTypeAndAddressForExistential") @@ -45,4 +50,4 @@ class ClassError : NSError { // CHECK: ClassError printDynamicTypeAndAddressForExistential(ClassError() as Error) -stopRemoteAST() \ No newline at end of file +stopRemoteAST() diff --git a/test/RemoteAST/objc_classes.swift b/test/RemoteAST/objc_classes.swift index d1c715613c531..bd3a1c6f25e24 100644 --- a/test/RemoteAST/objc_classes.swift +++ b/test/RemoteAST/objc_classes.swift @@ -3,6 +3,11 @@ // REQUIRES: swift-remoteast-test // REQUIRES: objc_interop +// This is an interpreter test that cannot use swift-darwin-postprocess.py to +// work around the DYLD_LIBRARY_LOAD bug in recent dylds. We need to find an +// alternative workaround for it, such as bumping this test's deployment target. +// REQUIRES: rdar78933143 + import Foundation @_silgen_name("printMetadataType") diff --git a/unittests/Reflection/RemoteMirrorInterop/test.m b/test/RemoteMirror/Inputs/interop.m similarity index 70% rename from unittests/Reflection/RemoteMirrorInterop/test.m rename to test/RemoteMirror/Inputs/interop.m index e625e9c829831..ca78a59ecab66 100644 --- a/unittests/Reflection/RemoteMirrorInterop/test.m +++ b/test/RemoteMirror/Inputs/interop.m @@ -19,12 +19,55 @@ #import #import #import +#import +#import +#import #import "SwiftRemoteMirrorLegacyInterop.h" +// dirname() is *not* safe - it may modify the input string; so instead just +// copy anything up to the last slash. +char *safe_dirname(const char *path) { + const char *slash = strrchr(path, '/'); + if (!slash) + return strdup("."); + size_t len = slash - path; + char *result = (char *)malloc(len + 1); + memcpy(result, path, len); + result[len] = '\0'; + return result; +} void *Load(char *path) { + const char *libpath = getenv("DYLD_LIBRARY_PATH"); + char *ourlibpath = NULL; + const char *oldlibpath = NULL; + + if (libpath && path[0] == '/') { + // If DYLD_LIBRARY_PATH is set, and the path we've been given is absolute, + // then prepend the directory part of the path we've been given to it. + char *libdir = safe_dirname(path); + size_t dirlen = strlen(libdir); + if (asprintf(&ourlibpath, "%s:%s", libdir, oldlibpath) < 0) { + fprintf(stderr, "Unable to form new DYLD_LIBRARY_PATH!\n"); + exit(1); + } + free(libdir); + + oldlibpath = ourlibpath + dirlen + 1; + + setenv("DYLD_LIBRARY_PATH", ourlibpath, 1); + } + + // Actually open the dylib void *Handle = dlopen(path, RTLD_LOCAL); + + if (ourlibpath) { + // Reset DYLD_LIBRARY_PATH, if we changed it + setenv("DYLD_LIBRARY_PATH", oldlibpath, 1); + free(ourlibpath); + } + if (Handle == NULL) { fprintf(stderr, "loading %s: %s\n", path, dlerror()); exit(1); @@ -163,8 +206,40 @@ int main(int argc, char **argv) { for (unsigned i = 0; i < TypeInfo.NumFields; ++i) { swift_childinfo_interop_t ChildInfo = swift_reflection_interop_childOfInstance( Context, Obj, i); - printf(" [%u]: %s Offset:%u Kind:%u\n", i, - ChildInfo.Name, ChildInfo.Offset, ChildInfo.Kind); + char *TypeName = swift_reflection_interop_copyDemangledNameForTypeRef(Context, ChildInfo.TR); + + printf(" [%u]: %s Offset:%u Kind:%u Type:%s ", i, + ChildInfo.Name, ChildInfo.Offset, ChildInfo.Kind, TypeName); + + + switch (ChildInfo.Kind) { + case SWIFT_BUILTIN: + printf("(Builtin value not displayed)\n"); + break; + case SWIFT_NO_PAYLOAD_ENUM: + { + int CaseNdx; + + if (swift_reflection_interop_projectEnumValue(Context, + Obj + ChildInfo.Offset, + ChildInfo.TR, + &CaseNdx)) { + swift_childinfo_interop_t CaseInfo + = swift_reflection_interop_childOfTypeRef(Context, ChildInfo.TR, + CaseNdx); + + printf("Value: %s (%d)\n", CaseInfo.Name, CaseNdx); + } else { + printf("Value: unknown\n"); + } + } + break; + default: + printf("(Value not displayed)\n"); + break; + } + + free(TypeName); } } else { printf("Unknown typeinfo!\n"); diff --git a/unittests/Reflection/RemoteMirrorInterop/test.py b/test/RemoteMirror/Inputs/interop.py similarity index 96% rename from unittests/Reflection/RemoteMirrorInterop/test.py rename to test/RemoteMirror/Inputs/interop.py index 06d46367becef..ee8543eb795c8 100755 --- a/unittests/Reflection/RemoteMirrorInterop/test.py +++ b/test/RemoteMirror/Inputs/interop.py @@ -45,12 +45,12 @@ def libPath(path): '-I', '../../../include/', '-o', '/tmp/test', '-Wall', '-Wextra', - '-g', 'test.m']) + '-g', 'interop.m']) # Build a test library with each Swift compiler passed in. for i, (swiftc, swiftlib) in enumerate(zip(swiftcs, swiftlibs)): subprocess.check_call( - ['xcrun', swiftc, '-emit-library', 'test.swift', + ['xcrun', swiftc, '-emit-library', 'interop.swift', '-o', os.path.join('/tmp', 'libtest' + str(i) + '.dylib'), '-Xlinker', '-rpath', '-Xlinker', swiftlib]) diff --git a/unittests/Reflection/RemoteMirrorInterop/test.swift b/test/RemoteMirror/Inputs/interop.swift similarity index 71% rename from unittests/Reflection/RemoteMirrorInterop/test.swift rename to test/RemoteMirror/Inputs/interop.swift index da7959c2c5605..f5af657b4f6b5 100644 --- a/unittests/Reflection/RemoteMirrorInterop/test.swift +++ b/test/RemoteMirror/Inputs/interop.swift @@ -3,9 +3,16 @@ public func test() -> UInt { return unsafeBitCast(c, to: UInt.self) } +enum E { +case one +case two +case three +} + class C { let x = "123" let y = 456 + let e = E.two } let c = C() diff --git a/test/RemoteMirror/README.md b/test/RemoteMirror/README.md new file mode 100644 index 0000000000000..c163604309447 --- /dev/null +++ b/test/RemoteMirror/README.md @@ -0,0 +1,7 @@ +Remote Mirror Tests +=================== + +This directory contains tests for the remote mirror code. Note in particular +that the interop test that runs from here only tests the version of Swift that +we are currently building. There is a script in the Inputs folder, +"interop.py", that can be used to test multiple versions together. diff --git a/test/RemoteMirror/interop.swift b/test/RemoteMirror/interop.swift new file mode 100644 index 0000000000000..d802fe764e7ed --- /dev/null +++ b/test/RemoteMirror/interop.swift @@ -0,0 +1,22 @@ +// REQUIRES: OS=macosx +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: remote_run + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %S/Inputs/interop.swift -emit-library -module-name InteropTest -o %t/%target-library-name(InteropTest) +// RUN: %target-clang %target-sanitizer-opt %S/Inputs/interop.m -framework Foundation -I %S/../../include/swift/SwiftRemoteMirror -I %S/../../include/ -o %t/InteropTest +// RUN: %target-codesign %t/%target-library-name(InteropTest) +// RUN: %target-codesign %t/InteropTest +// RUN: %target-run %t/InteropTest %t/%target-library-name(InteropTest) %platform-module-dir/%target-library-name(swiftRemoteMirror) | %FileCheck %s + +// CHECK: Kind:13 Size:{{[0-9]+}} Alignment:{{[0-9]+}} Stride:{{[0-9]+}} NumFields:0 +// CHECK-NEXT: Demangled name: InteropTest.C +// CHECK-NEXT: Original metadata: 0x{{[0-9A-Fa-f]+}} +// CHECK-NEXT: Looked up metadata: Metadata=0x{{[0-9A-Fa-f]+}} Library={{[0-9]+}} +// CHECK-NEXT: Kind:17 Size:{{[0-9]+}} Alignment:{{[0-9]+}} Stride:{{[0-9]+}} NumFields:3 +// CHECK-NEXT: [0]: x Offset:{{[0-9]+}} Kind:4 Type:Swift.String (Value not displayed) +// CHECK-NEXT: [1]: y Offset:{{[0-9]+}} Kind:4 Type:Swift.Int (Value not displayed) +// CHECK-NEXT: [2]: e Offset:{{[0-9]+}} Kind:5 Type:InteropTest.E Value: two (1) + diff --git a/test/Runtime/allocator_sanity.swift b/test/Runtime/allocator_sanity.swift new file mode 100644 index 0000000000000..532cad078f447 --- /dev/null +++ b/test/Runtime/allocator_sanity.swift @@ -0,0 +1,27 @@ +// RUN: %target-run-simple-swiftgyb + +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import StdlibUnittest + +var AllocationsTestSuite = TestSuite("Allocations") + +AllocationsTestSuite.test("absurd.allocation.misaligned") { + expectCrashLater() + let mustFail = UnsafeMutableRawPointer.allocate(byteCount: 1024, + alignment: 19) + expectUnreachable() + _ = mustFail +} + +AllocationsTestSuite.test("absurd.allocation.gargantuan") { + expectCrashLater() + let mustFail = UnsafeMutableRawPointer.allocate(byteCount: Int.max, + alignment: 0) + expectUnreachable() + _ = mustFail +} + +runAllTests() diff --git a/test/Runtime/concurrentTypeByName.swift b/test/Runtime/concurrentTypeByName.swift new file mode 100644 index 0000000000000..ff77409e6c66a --- /dev/null +++ b/test/Runtime/concurrentTypeByName.swift @@ -0,0 +1,1061 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +// REQUIRES: radar82364236 + +import StdlibUnittest +import SwiftPrivateThreadExtras + +protocol P {} +class GenericWrapper {} + +let ConcurrentTypeByNameTests = TestSuite("ConcurrentTypeByName") + +// We're testing for a race in looking up types by name, when there's a protocol +// constraint on a generic argument, and that constraint is fulfilled by a +// superclass. This race is more easily exposed when the superclass's metadata +// takes a long time to fill out, so we make it have a ton of properties here. +class Super: P { + var val0: T? + var val1: T? + var val2: T? + var val3: T? + var val4: T? + var val5: T? + var val6: T? + var val7: T? + var val8: T? + var val9: T? + var val10: T? + var val11: T? + var val12: T? + var val13: T? + var val14: T? + var val15: T? + var val16: T? + var val17: T? + var val18: T? + var val19: T? + var val20: T? + var val21: T? + var val22: T? + var val23: T? + var val24: T? + var val25: T? + var val26: T? + var val27: T? + var val28: T? + var val29: T? + var val30: T? + var val31: T? + var val32: T? + var val33: T? + var val34: T? + var val35: T? + var val36: T? + var val37: T? + var val38: T? + var val39: T? + var val40: T? + var val41: T? + var val42: T? + var val43: T? + var val44: T? + var val45: T? + var val46: T? + var val47: T? + var val48: T? + var val49: T? + var val50: T? + var val51: T? + var val52: T? + var val53: T? + var val54: T? + var val55: T? + var val56: T? + var val57: T? + var val58: T? + var val59: T? + var val60: T? + var val61: T? + var val62: T? + var val63: T? + var val64: T? + var val65: T? + var val66: T? + var val67: T? + var val68: T? + var val69: T? + var val70: T? + var val71: T? + var val72: T? + var val73: T? + var val74: T? + var val75: T? + var val76: T? + var val77: T? + var val78: T? + var val79: T? + var val80: T? + var val81: T? + var val82: T? + var val83: T? + var val84: T? + var val85: T? + var val86: T? + var val87: T? + var val88: T? + var val89: T? + var val90: T? + var val91: T? + var val92: T? + var val93: T? + var val94: T? + var val95: T? + var val96: T? + var val97: T? + var val98: T? + var val99: T? + var val100: T? + var val101: T? + var val102: T? + var val103: T? + var val104: T? + var val105: T? + var val106: T? + var val107: T? + var val108: T? + var val109: T? + var val110: T? + var val111: T? + var val112: T? + var val113: T? + var val114: T? + var val115: T? + var val116: T? + var val117: T? + var val118: T? + var val119: T? + var val120: T? + var val121: T? + var val122: T? + var val123: T? + var val124: T? + var val125: T? + var val126: T? + var val127: T? + var val128: T? + var val129: T? + var val130: T? + var val131: T? + var val132: T? + var val133: T? + var val134: T? + var val135: T? + var val136: T? + var val137: T? + var val138: T? + var val139: T? + var val140: T? + var val141: T? + var val142: T? + var val143: T? + var val144: T? + var val145: T? + var val146: T? + var val147: T? + var val148: T? + var val149: T? + var val150: T? + var val151: T? + var val152: T? + var val153: T? + var val154: T? + var val155: T? + var val156: T? + var val157: T? + var val158: T? + var val159: T? + var val160: T? + var val161: T? + var val162: T? + var val163: T? + var val164: T? + var val165: T? + var val166: T? + var val167: T? + var val168: T? + var val169: T? + var val170: T? + var val171: T? + var val172: T? + var val173: T? + var val174: T? + var val175: T? + var val176: T? + var val177: T? + var val178: T? + var val179: T? + var val180: T? + var val181: T? + var val182: T? + var val183: T? + var val184: T? + var val185: T? + var val186: T? + var val187: T? + var val188: T? + var val189: T? + var val190: T? + var val191: T? + var val192: T? + var val193: T? + var val194: T? + var val195: T? + var val196: T? + var val197: T? + var val198: T? + var val199: T? + var val200: T? + var val201: T? + var val202: T? + var val203: T? + var val204: T? + var val205: T? + var val206: T? + var val207: T? + var val208: T? + var val209: T? + var val210: T? + var val211: T? + var val212: T? + var val213: T? + var val214: T? + var val215: T? + var val216: T? + var val217: T? + var val218: T? + var val219: T? + var val220: T? + var val221: T? + var val222: T? + var val223: T? + var val224: T? + var val225: T? + var val226: T? + var val227: T? + var val228: T? + var val229: T? + var val230: T? + var val231: T? + var val232: T? + var val233: T? + var val234: T? + var val235: T? + var val236: T? + var val237: T? + var val238: T? + var val239: T? + var val240: T? + var val241: T? + var val242: T? + var val243: T? + var val244: T? + var val245: T? + var val246: T? + var val247: T? + var val248: T? + var val249: T? + var val250: T? + var val251: T? + var val252: T? + var val253: T? + var val254: T? + var val255: T? + var val256: T? + var val257: T? + var val258: T? + var val259: T? + var val260: T? + var val261: T? + var val262: T? + var val263: T? + var val264: T? + var val265: T? + var val266: T? + var val267: T? + var val268: T? + var val269: T? + var val270: T? + var val271: T? + var val272: T? + var val273: T? + var val274: T? + var val275: T? + var val276: T? + var val277: T? + var val278: T? + var val279: T? + var val280: T? + var val281: T? + var val282: T? + var val283: T? + var val284: T? + var val285: T? + var val286: T? + var val287: T? + var val288: T? + var val289: T? + var val290: T? + var val291: T? + var val292: T? + var val293: T? + var val294: T? + var val295: T? + var val296: T? + var val297: T? + var val298: T? + var val299: T? + var val300: T? + var val301: T? + var val302: T? + var val303: T? + var val304: T? + var val305: T? + var val306: T? + var val307: T? + var val308: T? + var val309: T? + var val310: T? + var val311: T? + var val312: T? + var val313: T? + var val314: T? + var val315: T? + var val316: T? + var val317: T? + var val318: T? + var val319: T? + var val320: T? + var val321: T? + var val322: T? + var val323: T? + var val324: T? + var val325: T? + var val326: T? + var val327: T? + var val328: T? + var val329: T? + var val330: T? + var val331: T? + var val332: T? + var val333: T? + var val334: T? + var val335: T? + var val336: T? + var val337: T? + var val338: T? + var val339: T? + var val340: T? + var val341: T? + var val342: T? + var val343: T? + var val344: T? + var val345: T? + var val346: T? + var val347: T? + var val348: T? + var val349: T? + var val350: T? + var val351: T? + var val352: T? + var val353: T? + var val354: T? + var val355: T? + var val356: T? + var val357: T? + var val358: T? + var val359: T? + var val360: T? + var val361: T? + var val362: T? + var val363: T? + var val364: T? + var val365: T? + var val366: T? + var val367: T? + var val368: T? + var val369: T? + var val370: T? + var val371: T? + var val372: T? + var val373: T? + var val374: T? + var val375: T? + var val376: T? + var val377: T? + var val378: T? + var val379: T? + var val380: T? + var val381: T? + var val382: T? + var val383: T? + var val384: T? + var val385: T? + var val386: T? + var val387: T? + var val388: T? + var val389: T? + var val390: T? + var val391: T? + var val392: T? + var val393: T? + var val394: T? + var val395: T? + var val396: T? + var val397: T? + var val398: T? + var val399: T? + var val400: T? + var val401: T? + var val402: T? + var val403: T? + var val404: T? + var val405: T? + var val406: T? + var val407: T? + var val408: T? + var val409: T? + var val410: T? + var val411: T? + var val412: T? + var val413: T? + var val414: T? + var val415: T? + var val416: T? + var val417: T? + var val418: T? + var val419: T? + var val420: T? + var val421: T? + var val422: T? + var val423: T? + var val424: T? + var val425: T? + var val426: T? + var val427: T? + var val428: T? + var val429: T? + var val430: T? + var val431: T? + var val432: T? + var val433: T? + var val434: T? + var val435: T? + var val436: T? + var val437: T? + var val438: T? + var val439: T? + var val440: T? + var val441: T? + var val442: T? + var val443: T? + var val444: T? + var val445: T? + var val446: T? + var val447: T? + var val448: T? + var val449: T? + var val450: T? + var val451: T? + var val452: T? + var val453: T? + var val454: T? + var val455: T? + var val456: T? + var val457: T? + var val458: T? + var val459: T? + var val460: T? + var val461: T? + var val462: T? + var val463: T? + var val464: T? + var val465: T? + var val466: T? + var val467: T? + var val468: T? + var val469: T? + var val470: T? + var val471: T? + var val472: T? + var val473: T? + var val474: T? + var val475: T? + var val476: T? + var val477: T? + var val478: T? + var val479: T? + var val480: T? + var val481: T? + var val482: T? + var val483: T? + var val484: T? + var val485: T? + var val486: T? + var val487: T? + var val488: T? + var val489: T? + var val490: T? + var val491: T? + var val492: T? + var val493: T? + var val494: T? + var val495: T? + var val496: T? + var val497: T? + var val498: T? + var val499: T? + var val500: T? + var val501: T? + var val502: T? + var val503: T? + var val504: T? + var val505: T? + var val506: T? + var val507: T? + var val508: T? + var val509: T? + var val510: T? + var val511: T? + var val512: T? + var val513: T? + var val514: T? + var val515: T? + var val516: T? + var val517: T? + var val518: T? + var val519: T? + var val520: T? + var val521: T? + var val522: T? + var val523: T? + var val524: T? + var val525: T? + var val526: T? + var val527: T? + var val528: T? + var val529: T? + var val530: T? + var val531: T? + var val532: T? + var val533: T? + var val534: T? + var val535: T? + var val536: T? + var val537: T? + var val538: T? + var val539: T? + var val540: T? + var val541: T? + var val542: T? + var val543: T? + var val544: T? + var val545: T? + var val546: T? + var val547: T? + var val548: T? + var val549: T? + var val550: T? + var val551: T? + var val552: T? + var val553: T? + var val554: T? + var val555: T? + var val556: T? + var val557: T? + var val558: T? + var val559: T? + var val560: T? + var val561: T? + var val562: T? + var val563: T? + var val564: T? + var val565: T? + var val566: T? + var val567: T? + var val568: T? + var val569: T? + var val570: T? + var val571: T? + var val572: T? + var val573: T? + var val574: T? + var val575: T? + var val576: T? + var val577: T? + var val578: T? + var val579: T? + var val580: T? + var val581: T? + var val582: T? + var val583: T? + var val584: T? + var val585: T? + var val586: T? + var val587: T? + var val588: T? + var val589: T? + var val590: T? + var val591: T? + var val592: T? + var val593: T? + var val594: T? + var val595: T? + var val596: T? + var val597: T? + var val598: T? + var val599: T? + var val600: T? + var val601: T? + var val602: T? + var val603: T? + var val604: T? + var val605: T? + var val606: T? + var val607: T? + var val608: T? + var val609: T? + var val610: T? + var val611: T? + var val612: T? + var val613: T? + var val614: T? + var val615: T? + var val616: T? + var val617: T? + var val618: T? + var val619: T? + var val620: T? + var val621: T? + var val622: T? + var val623: T? + var val624: T? + var val625: T? + var val626: T? + var val627: T? + var val628: T? + var val629: T? + var val630: T? + var val631: T? + var val632: T? + var val633: T? + var val634: T? + var val635: T? + var val636: T? + var val637: T? + var val638: T? + var val639: T? + var val640: T? + var val641: T? + var val642: T? + var val643: T? + var val644: T? + var val645: T? + var val646: T? + var val647: T? + var val648: T? + var val649: T? + var val650: T? + var val651: T? + var val652: T? + var val653: T? + var val654: T? + var val655: T? + var val656: T? + var val657: T? + var val658: T? + var val659: T? + var val660: T? + var val661: T? + var val662: T? + var val663: T? + var val664: T? + var val665: T? + var val666: T? + var val667: T? + var val668: T? + var val669: T? + var val670: T? + var val671: T? + var val672: T? + var val673: T? + var val674: T? + var val675: T? + var val676: T? + var val677: T? + var val678: T? + var val679: T? + var val680: T? + var val681: T? + var val682: T? + var val683: T? + var val684: T? + var val685: T? + var val686: T? + var val687: T? + var val688: T? + var val689: T? + var val690: T? + var val691: T? + var val692: T? + var val693: T? + var val694: T? + var val695: T? + var val696: T? + var val697: T? + var val698: T? + var val699: T? + var val700: T? + var val701: T? + var val702: T? + var val703: T? + var val704: T? + var val705: T? + var val706: T? + var val707: T? + var val708: T? + var val709: T? + var val710: T? + var val711: T? + var val712: T? + var val713: T? + var val714: T? + var val715: T? + var val716: T? + var val717: T? + var val718: T? + var val719: T? + var val720: T? + var val721: T? + var val722: T? + var val723: T? + var val724: T? + var val725: T? + var val726: T? + var val727: T? + var val728: T? + var val729: T? + var val730: T? + var val731: T? + var val732: T? + var val733: T? + var val734: T? + var val735: T? + var val736: T? + var val737: T? + var val738: T? + var val739: T? + var val740: T? + var val741: T? + var val742: T? + var val743: T? + var val744: T? + var val745: T? + var val746: T? + var val747: T? + var val748: T? + var val749: T? + var val750: T? + var val751: T? + var val752: T? + var val753: T? + var val754: T? + var val755: T? + var val756: T? + var val757: T? + var val758: T? + var val759: T? + var val760: T? + var val761: T? + var val762: T? + var val763: T? + var val764: T? + var val765: T? + var val766: T? + var val767: T? + var val768: T? + var val769: T? + var val770: T? + var val771: T? + var val772: T? + var val773: T? + var val774: T? + var val775: T? + var val776: T? + var val777: T? + var val778: T? + var val779: T? + var val780: T? + var val781: T? + var val782: T? + var val783: T? + var val784: T? + var val785: T? + var val786: T? + var val787: T? + var val788: T? + var val789: T? + var val790: T? + var val791: T? + var val792: T? + var val793: T? + var val794: T? + var val795: T? + var val796: T? + var val797: T? + var val798: T? + var val799: T? + var val800: T? + var val801: T? + var val802: T? + var val803: T? + var val804: T? + var val805: T? + var val806: T? + var val807: T? + var val808: T? + var val809: T? + var val810: T? + var val811: T? + var val812: T? + var val813: T? + var val814: T? + var val815: T? + var val816: T? + var val817: T? + var val818: T? + var val819: T? + var val820: T? + var val821: T? + var val822: T? + var val823: T? + var val824: T? + var val825: T? + var val826: T? + var val827: T? + var val828: T? + var val829: T? + var val830: T? + var val831: T? + var val832: T? + var val833: T? + var val834: T? + var val835: T? + var val836: T? + var val837: T? + var val838: T? + var val839: T? + var val840: T? + var val841: T? + var val842: T? + var val843: T? + var val844: T? + var val845: T? + var val846: T? + var val847: T? + var val848: T? + var val849: T? + var val850: T? + var val851: T? + var val852: T? + var val853: T? + var val854: T? + var val855: T? + var val856: T? + var val857: T? + var val858: T? + var val859: T? + var val860: T? + var val861: T? + var val862: T? + var val863: T? + var val864: T? + var val865: T? + var val866: T? + var val867: T? + var val868: T? + var val869: T? + var val870: T? + var val871: T? + var val872: T? + var val873: T? + var val874: T? + var val875: T? + var val876: T? + var val877: T? + var val878: T? + var val879: T? + var val880: T? + var val881: T? + var val882: T? + var val883: T? + var val884: T? + var val885: T? + var val886: T? + var val887: T? + var val888: T? + var val889: T? + var val890: T? + var val891: T? + var val892: T? + var val893: T? + var val894: T? + var val895: T? + var val896: T? + var val897: T? + var val898: T? + var val899: T? + var val900: T? + var val901: T? + var val902: T? + var val903: T? + var val904: T? + var val905: T? + var val906: T? + var val907: T? + var val908: T? + var val909: T? + var val910: T? + var val911: T? + var val912: T? + var val913: T? + var val914: T? + var val915: T? + var val916: T? + var val917: T? + var val918: T? + var val919: T? + var val920: T? + var val921: T? + var val922: T? + var val923: T? + var val924: T? + var val925: T? + var val926: T? + var val927: T? + var val928: T? + var val929: T? + var val930: T? + var val931: T? + var val932: T? + var val933: T? + var val934: T? + var val935: T? + var val936: T? + var val937: T? + var val938: T? + var val939: T? + var val940: T? + var val941: T? + var val942: T? + var val943: T? + var val944: T? + var val945: T? + var val946: T? + var val947: T? + var val948: T? + var val949: T? + var val950: T? + var val951: T? + var val952: T? + var val953: T? + var val954: T? + var val955: T? + var val956: T? + var val957: T? + var val958: T? + var val959: T? + var val960: T? + var val961: T? + var val962: T? + var val963: T? + var val964: T? + var val965: T? + var val966: T? + var val967: T? + var val968: T? + var val969: T? + var val970: T? + var val971: T? + var val972: T? + var val973: T? + var val974: T? + var val975: T? + var val976: T? + var val977: T? + var val978: T? + var val979: T? + var val980: T? + var val981: T? + var val982: T? + var val983: T? + var val984: T? + var val985: T? + var val986: T? + var val987: T? + var val988: T? + var val989: T? + var val990: T? + var val991: T? + var val992: T? + var val993: T? + var val994: T? + var val995: T? + var val996: T? + var val997: T? + var val998: T? + var val999: T? + var val1000: T? + var val1001: T? + var val1002: T? + var val1003: T? + var val1004: T? + var val1005: T? + var val1006: T? + var val1007: T? + var val1008: T? + var val1009: T? + var val1010: T? + var val1011: T? + var val1012: T? + var val1013: T? + var val1014: T? + var val1015: T? + var val1016: T? + var val1017: T? + var val1018: T? + var val1019: T? + var val1020: T? + var val1021: T? + var val1022: T? + var val1023: T? +} +class Sub: Super {} + +ConcurrentTypeByNameTests.test("concurrent _typeByName") { + if #available(SwiftStdlib 5.5, *) { + func printTypeByName() { + print(_typeByName("4main14GenericWrapperCyAA3SubCG")! as Any) + } + + let (createRet1, tid1) = _stdlib_thread_create_block(printTypeByName, ()) + let (createRet2, tid2) = _stdlib_thread_create_block(printTypeByName, ()) + expectEqual(0, createRet1) + expectEqual(0, createRet2) + _ = _stdlib_thread_join(tid1!, Void.self) + _ = _stdlib_thread_join(tid2!, Void.self) + } +} + +runAllTests() diff --git a/test/Runtime/demangleToMetadata.swift b/test/Runtime/demangleToMetadata.swift index c006d8a007e7f..58025b6bfb9bd 100644 --- a/test/Runtime/demangleToMetadata.swift +++ b/test/Runtime/demangleToMetadata.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -parse-stdlib %s -module-name main -o %t/a.out +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -parse-stdlib %s -module-name main -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out // REQUIRES: executable_test @@ -9,6 +9,7 @@ import Swift import StdlibUnittest +import _Concurrency let DemangleToMetadataTests = TestSuite("DemangleToMetadata") @@ -52,6 +53,15 @@ func f2_variadic_inout(x: ()..., y: inout ()) { } func f1_escaping(_: @escaping (Int) -> Float) { } func f1_autoclosure(_: @autoclosure () -> Float) { } func f1_escaping_autoclosure(_: @autoclosure @escaping () -> Float) { } +func f1_mainactor(_: @MainActor () -> Float) { } + +func globalActorMetatypeFn(_: T.Type) -> Any.Type { + typealias Fn = @MainActor () -> T + return Fn.self +} + +@available(SwiftStdlib 5.5, *) +func f1_actor(_: (isolated Actor) -> Void) { } DemangleToMetadataTests.test("function types") { // Conventions @@ -98,6 +108,22 @@ DemangleToMetadataTests.test("function types") { // Autoclosure expectEqual(type(of: f1_autoclosure), _typeByName("ySfyXKc")!) expectEqual(type(of: f1_escaping_autoclosure), _typeByName("ySfyXAc")!) + + // MainActor + expectEqual(type(of: f1_mainactor), _typeByName("ySfyScMYcXEc")!) + expectEqual( + "(@MainActor () -> Float) -> ()", + String(describing: _typeByName("ySfyScMYcXEc")!)) + typealias MainActorFn = @MainActor () -> Float + expectEqual(MainActorFn.self, _typeByName("SfyScMYcc")!) + expectEqual(MainActorFn.self, globalActorMetatypeFn(Float.self)) + + // isolated parameters + if #available(SwiftStdlib 5.5, *) { + expectEqual(type(of: f1_actor), _typeByName("yyScA_pYiXEc")!) + typealias IsolatedFn = ((isolated Actor) -> Void) -> Void + expectEqual(IsolatedFn.self, type(of: f1_actor)) + } } DemangleToMetadataTests.test("metatype types") { @@ -489,6 +515,11 @@ if #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { } } +if #available(SwiftStdlib 5.5, *) { + DemangleToMetadataTests.test("Concurrency standard substitutions") { + expectEqual(TaskGroup.self, _typeByName("ScGySiG")!) + } +} runAllTests() diff --git a/test/Runtime/superclass_constraint_metadata_resilient_superclass2_future.swift b/test/Runtime/superclass_constraint_metadata_resilient_superclass2_future.swift index 0983614e2dd97..222fd44a9b093 100644 --- a/test/Runtime/superclass_constraint_metadata_resilient_superclass2_future.swift +++ b/test/Runtime/superclass_constraint_metadata_resilient_superclass2_future.swift @@ -1,4 +1,3 @@ -// REQUIRES: rdar61345988 // RUN: %empty-directory(%t) // RUN: %target-build-swift -emit-library -enable-library-evolution -module-name Framework -module-link-name Framework %S/Inputs/public_struct_with_generic_arg_swift_class_constrained.swift -o %t/%target-library-name(Framework) -emit-module-path %t/Framework.swiftmodule -target %module-target-future // RUN: %target-codesign %t/libFramework.dylib @@ -11,9 +10,9 @@ // RUN: %target-run %t/main | %FileCheck %S/Inputs/print_subclass/main.swift // REQUIRES: executable_test -// REQUIRES: rdar61345988 // REQUIRES: OS=macosx // UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime import Swift import Framework diff --git a/test/Runtime/superclass_constraint_metadata_resilient_superclass_future.swift b/test/Runtime/superclass_constraint_metadata_resilient_superclass_future.swift index c0371ce6d6c3b..d857c512fbe7b 100644 --- a/test/Runtime/superclass_constraint_metadata_resilient_superclass_future.swift +++ b/test/Runtime/superclass_constraint_metadata_resilient_superclass_future.swift @@ -1,4 +1,3 @@ -// REQUIRES: rdar61345988 // RUN: %empty-directory(%t) // RUN: %target-build-swift -emit-library -enable-library-evolution -module-name Framework -module-link-name Framework %S/Inputs/public_struct_with_generic_arg_swift_class_constrained.swift -o %t/%target-library-name(Framework) -emit-module-path %t/Framework.swiftmodule -target %module-target-future // RUN: %target-codesign %t/libFramework.dylib @@ -8,9 +7,9 @@ // RUN: %target-run %t/main | %FileCheck %S/Inputs/print_subclass/main.swift // REQUIRES: executable_test -// REQUIRES: rdar61345988 // REQUIRES: OS=macosx // UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime import Swift import Framework diff --git a/test/SIL/Distributed/distributed_actor_default_deinit_sil.swift b/test/SIL/Distributed/distributed_actor_default_deinit_sil.swift new file mode 100644 index 0000000000000..46d19d015a042 --- /dev/null +++ b/test/SIL/Distributed/distributed_actor_default_deinit_sil.swift @@ -0,0 +1,45 @@ +// RUN: %target-swift-frontend -O -primary-file %s -emit-sil -enable-experimental-distributed | %FileCheck %s --dump-input=fail +// REQUIRES: concurrency + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor SimpleEmptyDistributedActor { +} + +// ==== ------------------------------------------------------------------------ +// ==== deinit must invoke transport.resignIdentity() when it is local + +// CHECK: // SimpleEmptyDistributedActor.deinit +// sil hidden [available 12.0] @$s36distributed_actor_default_deinit_sil27SimpleEmptyDistributedActorCfd : $@convention(method) (@guaranteed SimpleEmptyDistributedActor) -> @owned Builtin.NativeObject { +// CHECK: [[SELF:%[0-9]+]] "self" +// CHECK: bb0(%0 : $SimpleEmptyDistributedActor): +// CHECK: debug_value %0 : $SimpleEmptyDistributedActor, let, name "self", argno 1 +// CHECK: [[ID_ADDR:%[0-9]+]] = ref_element_addr %0 : $SimpleEmptyDistributedActor, #SimpleEmptyDistributedActor.id +// CHECK: [[TRANSPORT_ADDR:%[0-9]+]] = ref_element_addr %0 : $SimpleEmptyDistributedActor, #SimpleEmptyDistributedActor.actorTransport +// CHECK: %4 = init_existential_ref %0 : $SimpleEmptyDistributedActor : $SimpleEmptyDistributedActor, $AnyObject +// CHECK: // function_ref swift_distributed_actor_is_remote +// CHECK: %5 = function_ref @swift_distributed_actor_is_remote : $@convention(thin) (@guaranteed AnyObject) -> Bool +// CHECK: %6 = apply %5(%4) : $@convention(thin) (@guaranteed AnyObject) -> Bool +// CHECK: %7 = struct_extract %6 : $Bool, #Bool._value +// CHECK: cond_br %7, [[REMOTE_BB:bb[0-9]+]], [[LOCAL_BB:bb[0-9]+]] + +// If local... +// CHECK: [[LOCAL_BB]]: +// CHECK: %9 = open_existential_addr immutable_access %3 : $*ActorTransport to $*@opened({{.*}}) ActorTransport +// CHECK: %10 = witness_method $@opened({{.*}}) ActorTransport, #ActorTransport.resignIdentity : (Self) -> (AnyActorIdentity) -> (), %9 : $*@opened({{.*}}) ActorTransport : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport> (@in_guaranteed AnyActorIdentity, @in_guaranteed τ_0_0) -> () +// CHECK: %11 = apply %10<@opened({{.*}}) ActorTransport>(%2, %9) : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport> (@in_guaranteed AnyActorIdentity, @in_guaranteed τ_0_0) -> () +// CHECK: br bb3 + +// If remote... +// CHECK: [[REMOTE_BB]]: +// CHECK: br bb3 + +// Finish up the destroying... +// CHECK: bb3: +// CHECK: destroy_addr [[TRANSPORT_ADDR]] : $*ActorTransport +// CHECK: destroy_addr [[ID_ADDR]] : $*AnyActorIdentity +// CHECK: [[_:%[0-9]+]] = builtin "destroyDefaultActor"(%0 : $SimpleEmptyDistributedActor) : $() +// CHECK: [[SELF:%[0-9]+]] = unchecked_ref_cast %0 : $SimpleEmptyDistributedActor to $Builtin.NativeObject +// CHECK: return [[SELF]] : $Builtin.NativeObject +// CHECK: } // end sil function '$s36distributed_actor_default_deinit_sil27SimpleEmptyDistributedActorCfd' diff --git a/test/SIL/Distributed/distributed_actor_default_init_sil.swift b/test/SIL/Distributed/distributed_actor_default_init_sil.swift new file mode 100644 index 0000000000000..d580cdcf35e15 --- /dev/null +++ b/test/SIL/Distributed/distributed_actor_default_init_sil.swift @@ -0,0 +1,37 @@ +// RUN: %target-swift-frontend -O -primary-file %s -emit-sil -enable-experimental-distributed | %FileCheck %s +// REQUIRES: concurrency + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor SimpleEmptyDistributedActor { +} + +// CHECK: // SimpleEmptyDistributedActor.init(transport:) +// CHECK: sil hidden{{.*}} @$s34distributed_actor_default_init_sil27SimpleEmptyDistributedActorC9transportAC01_H00I9Transport_p_tcfc : $@convention(method) (@in ActorTransport, @owned SimpleEmptyDistributedActor) -> @owned SimpleEmptyDistributedActor { +// CHECK: // %0 "transport" // users: %15, %7, %6, %2 +// CHECK: // %1 "self" // users: %5, %12, %4, %16, %3 +// CHECK: bb0(%0 : $*ActorTransport, %1 : $SimpleEmptyDistributedActor): +// CHECK: debug_value %0 : $*ActorTransport, let, name "transport", argno 1, implicit, expr op_deref // id: %2 +// CHECK: debug_value %1 : $SimpleEmptyDistributedActor, let, name "self", argno 2, implicit // id: %3 +// CHECK: %4 = builtin "initializeDefaultActor"(%1 : $SimpleEmptyDistributedActor) : $() + +// Store the transport +// CHECK: %5 = ref_element_addr %1 : $SimpleEmptyDistributedActor, #SimpleEmptyDistributedActor.actorTransport // user: %6 +// CHECK: copy_addr %0 to [initialization] %5 : $*ActorTransport // id: %6 + +// Assign an identity +// CHECK: %7 = open_existential_addr immutable_access %0 : $*ActorTransport to $*@opened("{{.*}}") ActorTransport // users: %11, %11, %9 +// CHECK: %8 = metatype $@thick SimpleEmptyDistributedActor.Type // user: %11 +// CHECK: %9 = witness_method $@opened("{{.*}}") ActorTransport, #ActorTransport.assignIdentity : (Self) -> (Act.Type) -> AnyActorIdentity, %7 : $*@opened("{{.*}}") ActorTransport : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport><τ_1_0 where τ_1_0 : DistributedActor> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> @out AnyActorIdentity // type-defs: %7; user: %11 +// CHECK: %10 = alloc_stack $AnyActorIdentity // users: %14, %13, %11 +// CHECK: %11 = apply %9<@opened("{{.*}}") ActorTransport, SimpleEmptyDistributedActor>(%10, %8, %7) : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport><τ_1_0 where τ_1_0 : DistributedActor> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> @out AnyActorIdentity // type-defs: %7 + +// Store the identity +// CHECK: %12 = ref_element_addr %1 : $SimpleEmptyDistributedActor, #SimpleEmptyDistributedActor.id // user: %13 +// CHECK: copy_addr [take] %10 to [initialization] %12 : $*AnyActorIdentity // id: %13 + +// CHECK: dealloc_stack %10 : $*AnyActorIdentity // id: %14 +// CHECK: destroy_addr %0 : $*ActorTransport // id: %15 +// CHECK: return %1 : $SimpleEmptyDistributedActor // id: %16 +// CHECK: } // end sil function '$s34distributed_actor_default_init_sil27SimpleEmptyDistributedActorC9transportAC01_H00I9Transport_p_tcfc' diff --git a/test/SIL/Distributed/distributed_actor_remote_deinit_sil.swift b/test/SIL/Distributed/distributed_actor_remote_deinit_sil.swift new file mode 100644 index 0000000000000..0624c78eb9d1d --- /dev/null +++ b/test/SIL/Distributed/distributed_actor_remote_deinit_sil.swift @@ -0,0 +1,68 @@ +// RUN: %target-swift-frontend -O -primary-file %s -emit-sil -enable-experimental-distributed | %FileCheck %s --dump-input=fail +// REQUIRES: concurrency + +import _Distributed + +class SomeClass {} + +@available(SwiftStdlib 5.5, *) +distributed actor SimpleEmptyDistributedActor { + let localOnlyField: SomeClass + + init(field: SomeClass, transport: ActorTransport) { + self.localOnlyField = field + } +} + +// ==== ------------------------------------------------------------------------ +// ==== Check that a normal local only actor is left unchanged + +// ==== deinit must have the extra "if remote..." path emitted for the +// distributed actor only. That path will not attempt deallocating the +// `localOnly...` fields, since they were never initialized and have no storage. + +// CHECK: // SimpleEmptyDistributedActor.deinit +// CHECK: sil hidden{{.*}} @$s35distributed_actor_remote_deinit_sil27SimpleEmptyDistributedActorCfd : $@convention(method) (@guaranteed SimpleEmptyDistributedActor) -> @owned Builtin.NativeObject { +// CHECK: // [[SELF:%[0-9]+]] "self" +// CHECK: bb0(%0 : $SimpleEmptyDistributedActor): +// CHECK-NEXT: debug_value [[SELF]] : $SimpleEmptyDistributedActor, let, name "self", argno 1, implicit +// CHECK-NEXT: [[IDENTITY_ADDR:%[0-9]+]] = ref_element_addr %0 : $SimpleEmptyDistributedActor, #SimpleEmptyDistributedActor.id +// CHECK-NEXT: [[TRANSPORT_ADDR:%[0-9]+]] = ref_element_addr %0 : $SimpleEmptyDistributedActor, #SimpleEmptyDistributedActor.actorTransport +// CHECK: [[SELF_1:%[0-9]+]] = init_existential_ref %0 : $SimpleEmptyDistributedActor : $SimpleEmptyDistributedActor, $AnyObject +// CHECK: // function_ref swift_distributed_actor_is_remote +// CHECK: [[IS_REMOTE_FN_1:%[0-9]+]] = function_ref @swift_distributed_actor_is_remote : $@convention(thin) (@guaranteed AnyObject) -> Bool +// CHECK: [[IS_REMOTE_FN_RES_1:%[0-9]+]] = apply [[IS_REMOTE_FN_1]]([[SELF_1]]) : $@convention(thin) (@guaranteed AnyObject) -> Bool +// CHECK: [[IS_REMOTE_BOOL_1:%[0-9]+]] = struct_extract [[IS_REMOTE_FN_RES_1]] : $Bool, #Bool._value +// CHECK: cond_br [[IS_REMOTE_BOOL_1]], bb2, bb1 + +// CHECK: bb1: // Preds: bb0 +// CHECK: %9 = open_existential_addr immutable_access %3 : $*ActorTransport to $*@opened("{{.*}}") ActorTransport // users: %11, %11, %10 +// CHECK: %10 = witness_method $@opened("{{.*}}") ActorTransport, #ActorTransport.resignIdentity : (Self) -> (AnyActorIdentity) -> (), %9 : $*@opened("{{.*}}") ActorTransport : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport> (@in_guaranteed AnyActorIdentity, @in_guaranteed τ_0_0) -> () // type-defs: %9; user: %11 +// CHECK: %11 = apply %10<@opened("{{.*}}") ActorTransport>(%2, %9) : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport> (@in_guaranteed AnyActorIdentity, @in_guaranteed τ_0_0) -> () // type-defs: %9 +// CHECK: br bb3 // id: %12 + +// CHECK: bb2: // Preds: bb0 +// CHECK: br bb3 // id: %13 + +// CHECK: bb3: // Preds: bb1 bb2 +// CHECK: %14 = init_existential_ref %0 : $SimpleEmptyDistributedActor : $SimpleEmptyDistributedActor, $AnyObject // user: %15 +// CHECK: %15 = apply %5(%14) : $@convention(thin) (@guaranteed AnyObject) -> Bool // user: %16 +// CHECK: %16 = struct_extract %15 : $Bool, #Bool._value // user: %17 +// CHECK: cond_br %16, bb4, bb5 // id: %17 + +// CHECK: bb4: // Preds: bb3 +// CHECK: br bb6 // id: %18 + +// CHECK: bb5: // Preds: bb3 +// CHECK: %19 = ref_element_addr %0 : $SimpleEmptyDistributedActor, #SimpleEmptyDistributedActor.localOnlyField // user: %20 +// CHECK: %20 = load %19 : $*SomeClass // user: %21 +// CHECK: strong_release %20 : $SomeClass // id: %21 +// CHECK: br bb6 // id: %22 + +// CHECK: bb6: // Preds: bb5 bb4 +// CHECK: destroy_addr %3 : $*ActorTransport // id: %23 +// CHECK: destroy_addr %2 : $*AnyActorIdentity // id: %24 +// CHECK: %25 = builtin "destroyDefaultActor"(%0 : $SimpleEmptyDistributedActor) : $() +// CHECK: %26 = unchecked_ref_cast %0 : $SimpleEmptyDistributedActor to $Builtin.NativeObject // user: %27 +// CHECK: return %26 : $Builtin.NativeObject // id: %27 +// CHECK: } // end sil function '$s35distributed_actor_remote_deinit_sil27SimpleEmptyDistributedActorCfd' \ No newline at end of file diff --git a/test/SIL/Distributed/distributed_actor_remote_deinit_sil_normal.swift b/test/SIL/Distributed/distributed_actor_remote_deinit_sil_normal.swift new file mode 100644 index 0000000000000..d7c87cfd44e18 --- /dev/null +++ b/test/SIL/Distributed/distributed_actor_remote_deinit_sil_normal.swift @@ -0,0 +1,32 @@ +// RUN: %target-swift-frontend -O -primary-file %s -emit-sil -enable-experimental-distributed | %FileCheck %s --dump-input=fail +// REQUIRES: concurrency + +import _Distributed + +class SomeClass {} + +@available(SwiftStdlib 5.5, *) +actor SimpleActor { + let someFieldInLocalActor: SomeClass + init(field: SomeClass) { + self.someFieldInLocalActor = field + } +} + + +// ==== ------------------------------------------------------------------------ +// ==== Check that a normal local only actor is left unchanged + +// CHECK: // SimpleActor.deinit +// CHECK: sil hidden{{.*}} @$s42distributed_actor_remote_deinit_sil_normal11SimpleActorCfd : $@convention(method) (@guaranteed SimpleActor) -> @owned Builtin.NativeObject { +// CHECK: // %0 "self" // users: %6, %5, %2, %1 +// CHECK: bb0(%0 : $SimpleActor): +// CHECK: debug_value %0 : $SimpleActor, let, name "self", argno 1, implicit +// CHECK: %2 = ref_element_addr %0 : $SimpleActor, #SimpleActor.someFieldInLocalActor +// CHECK: %3 = load %2 : $*SomeClass // user: %4 +// CHECK: strong_release %3 : $SomeClass // id: %4 +// CHECK: %5 = builtin "destroyDefaultActor"(%0 : $SimpleActor) : $() +// CHECK: %6 = unchecked_ref_cast %0 : $SimpleActor to $Builtin.NativeObject // user: %7 +// CHECK: return %6 : $Builtin.NativeObject // id: %7 +// CHECK: } // end sil function '$s42distributed_actor_remote_deinit_sil_normal11SimpleActorCfd' + diff --git a/test/SIL/Distributed/distributed_actor_user_transport_init_sil.swift b/test/SIL/Distributed/distributed_actor_user_transport_init_sil.swift new file mode 100644 index 0000000000000..aa30feb8e3d37 --- /dev/null +++ b/test/SIL/Distributed/distributed_actor_user_transport_init_sil.swift @@ -0,0 +1,74 @@ +// RUN: %target-swift-frontend -O -primary-file %s -emit-sil -enable-experimental-distributed | %FileCheck %s --dump-input=fail +// REQUIRES: concurrency + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor SimpleUserDefinedInitDistributedActor { + init(kappa transport: ActorTransport, other: Int) {} + init(other: Int, theTransport: ActorTransport) {} +} + +// CHECK: // SimpleUserDefinedInitDistributedActor.init(kappa:other:) +// CHECK: sil hidden{{.*}} @$s41distributed_actor_user_transport_init_sil37SimpleUserDefinedInitDistributedActorC5kappa5otherAC01_K00L9Transport_p_Sitcfc : $@convention(method) (@in ActorTransport, Int, @owned SimpleUserDefinedInitDistributedActor) -> @owned SimpleUserDefinedInitDistributedActor { +// CHECK: // %0 "transport" // users: %17, %9, %8, %3 +// CHECK: // %1 "other" // user: %4 +// CHECK: // %2 "self" // users: %7, %14, %6, %18, %5 +// CHECK: bb0(%0 : $*ActorTransport, %1 : $Int, %2 : $SimpleUserDefinedInitDistributedActor): +// CHECK: debug_value %0 : $*ActorTransport, let, name "transport", argno 1, expr op_deref // id: %3 +// CHECK: debug_value %1 : $Int, let, name "other", argno 2 // id: %4 +// CHECK: debug_value %2 : $SimpleUserDefinedInitDistributedActor, let, name "self", argno 3, implicit // id: %5 +// CHECK: %6 = builtin "initializeDefaultActor"(%2 : $SimpleUserDefinedInitDistributedActor) : $() + +// Store the transport +// CHECK: %7 = ref_element_addr %2 : $SimpleUserDefinedInitDistributedActor, #SimpleUserDefinedInitDistributedActor.actorTransport // user: %8 +// CHECK: copy_addr %0 to [initialization] %7 : $*ActorTransport // id: %8 + +// Assign the identity +// CHECK: %9 = open_existential_addr immutable_access %0 : $*ActorTransport to $*@opened("{{.*}}") ActorTransport // users: %13, %13, %11 +// CHECK: %10 = metatype $@thick SimpleUserDefinedInitDistributedActor.Type // user: %13 +// CHECK: %11 = witness_method $@opened("{{.*}}") ActorTransport, #ActorTransport.assignIdentity : (Self) -> (Act.Type) -> AnyActorIdentity, %9 : $*@opened("{{.*}}") ActorTransport : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport><τ_1_0 where τ_1_0 : DistributedActor> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> @out AnyActorIdentity // type-defs: %9; user: %13 +// CHECK: %12 = alloc_stack $AnyActorIdentity // users: %16, %15, %13 +// CHECK: %13 = apply %11<@opened("{{.*}}") ActorTransport, SimpleUserDefinedInitDistributedActor>(%12, %10, %9) : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport><τ_1_0 where τ_1_0 : DistributedActor> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> @out AnyActorIdentity // type-defs: %9 + +// Store the identity +// CHECK: %14 = ref_element_addr %2 : $SimpleUserDefinedInitDistributedActor, #SimpleUserDefinedInitDistributedActor.id // user: %15 +// CHECK: copy_addr [take] %12 to [initialization] %14 : $*AnyActorIdentity // id: %15 +// CHECK: dealloc_stack %12 : $*AnyActorIdentity // id: %16 +// CHECK: destroy_addr %0 : $*ActorTransport // id: %17 +// CHECK: return %2 : $SimpleUserDefinedInitDistributedActor // id: %18 +// CHECK: } // end sil function '$s41distributed_actor_user_transport_init_sil37SimpleUserDefinedInitDistributedActorC5kappa5otherAC01_K00L9Transport_p_Sitcfc' + + +// Even if the transport is in another position, we still locate it by the type +// CHECK: // SimpleUserDefinedInitDistributedActor.init(other:theTransport:) +// CHECK: sil hidden{{.*}} @$s41distributed_actor_user_transport_init_sil37SimpleUserDefinedInitDistributedActorC5other12theTransportACSi_01_K00lO0_ptcfc : $@convention(method) (Int, @in ActorTransport, @owned SimpleUserDefinedInitDistributedActor) -> @owned SimpleUserDefinedInitDistributedActor { +// CHECK: // %0 "other" // user: %3 +// CHECK: // %1 "theTransport" // users: %17, %9, %8, %4 +// CHECK: // %2 "self" // users: %7, %14, %6, %18, %5 +// CHECK: bb0(%0 : $Int, %1 : $*ActorTransport, %2 : $SimpleUserDefinedInitDistributedActor): +// CHECK: debug_value %0 : $Int, let, name "other", argno 1 // id: %3 +// CHECK: debug_value %1 : $*ActorTransport, let, name "theTransport", argno 2, expr op_deref // id: %4 +// CHECK: debug_value %2 : $SimpleUserDefinedInitDistributedActor, let, name "self", argno 3, implicit // id: %5 +// CHECK: %6 = builtin "initializeDefaultActor"(%2 : $SimpleUserDefinedInitDistributedActor) : $() + +// Store the transport +// CHECK: %7 = ref_element_addr %2 : $SimpleUserDefinedInitDistributedActor, #SimpleUserDefinedInitDistributedActor.actorTransport // user: %8 +// CHECK: copy_addr %1 to [initialization] %7 : $*ActorTransport // id: %8 + +// Assign an identity +// CHECK: %9 = open_existential_addr immutable_access %1 : $*ActorTransport to $*@opened("{{.*}}") ActorTransport // users: %13, %13, %11 +// CHECK: %10 = metatype $@thick SimpleUserDefinedInitDistributedActor.Type // user: %13 +// CHECK: %11 = witness_method $@opened("{{.*}}") ActorTransport, #ActorTransport.assignIdentity : (Self) -> (Act.Type) -> AnyActorIdentity, %9 : $*@opened("{{.*}}") ActorTransport : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport><τ_1_0 where τ_1_0 : DistributedActor> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> @out AnyActorIdentity // type-defs: %9; user: %13 +// CHECK: %12 = alloc_stack $AnyActorIdentity // users: %16, %15, %13 +// CHECK: %13 = apply %11<@opened("{{.*}}") ActorTransport, SimpleUserDefinedInitDistributedActor>(%12, %10, %9) : $@convention(witness_method: ActorTransport) <τ_0_0 where τ_0_0 : ActorTransport><τ_1_0 where τ_1_0 : DistributedActor> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> @out AnyActorIdentity // type-defs: %9 + +// Store the identity +// CHECK: %14 = ref_element_addr %2 : $SimpleUserDefinedInitDistributedActor, #SimpleUserDefinedInitDistributedActor.id // user: %15 +// CHECK: copy_addr [take] %12 to [initialization] %14 : $*AnyActorIdentity // id: %15 +// CHECK: dealloc_stack %12 : $*AnyActorIdentity // id: %16 +// CHECK: destroy_addr %1 : $*ActorTransport // id: %17 + +// While in AST the return was "return null" after SILGen we properly return the self +// CHECK: return %2 : $SimpleUserDefinedInitDistributedActor // id: %18 +// CHECK: } // end sil function '$s41distributed_actor_user_transport_init_sil37SimpleUserDefinedInitDistributedActorC5other12theTransportACSi_01_K00lO0_ptcfc' diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index 3f9092b90534c..f4a494071ceb5 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -1271,7 +1271,7 @@ sil @debug_value : $@convention(thin) (Int, @in P, AnyObject) -> () { bb0(%0 : $Int, %1 : $*P, %2 : $AnyObject): debug_value %0 : $Int // CHECK: debug_value %0 : $Int debug_value [poison] %2 : $AnyObject // CHECK: debug_value [poison] %2 : $AnyObject - debug_value_addr %1 : $*P // CHECK: debug_value_addr %1 : $*P + debug_value %1 : $*P, expr op_deref // CHECK: debug_value %1 : $*P, expr op_deref unreachable } diff --git a/test/SIL/Parser/borrow.sil b/test/SIL/Parser/borrow.sil index 7e5328f0c2473..19d94d4165ec9 100644 --- a/test/SIL/Parser/borrow.sil +++ b/test/SIL/Parser/borrow.sil @@ -27,3 +27,17 @@ bb0(%0 : $*Builtin.NativeObject, %1 : @guaranteed $Builtin.NativeObject): %4 = tuple() return %4 : $() } + +class C {} + +// CHECK-LABEL: sil [ossa] @foo +// CHECK: begin_borrow [defined] {{%[^,]+}} +// CHECK-LABEL: } // end sil function 'foo' +sil [ossa] @foo : $@convention(thin) () -> () { + %instance = alloc_ref $C + %guaranteed_c = begin_borrow [defined] %instance : $C + end_borrow %guaranteed_c : $C + destroy_value %instance : $C + %res = tuple () + return %res : $() +} diff --git a/test/SIL/Parser/concurrency.sil b/test/SIL/Parser/concurrency.sil index bcb92f46939ab..bd88699e73a1a 100644 --- a/test/SIL/Parser/concurrency.sil +++ b/test/SIL/Parser/concurrency.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-objc-interop -enable-experimental-concurrency -enable-sil-verify-all=true %s | %target-sil-opt -enable-objc-interop -enable-experimental-concurrency -enable-sil-verify-all=true | %FileCheck %s +// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all=true %s | %target-sil-opt -enable-objc-interop -enable-sil-verify-all=true | %FileCheck %s // REQUIRES: concurrency sil_stage raw // CHECK: sil_stage raw @@ -13,6 +13,8 @@ sil @test_hop_to_executor : $@convention(thin) (@guaranteed Actor) -> () { bb0(%0 : $Actor): // CHECK: hop_to_executor %0 : $Actor hop_to_executor %0 : $Actor + // CHECK: hop_to_executor [mandatory] %0 : $Actor + hop_to_executor [mandatory] %0 : $Actor %2 = tuple () return %2 : $() } diff --git a/test/SIL/Parser/debug_info.sil b/test/SIL/Parser/debug_info.sil new file mode 100644 index 0000000000000..964de42751cc6 --- /dev/null +++ b/test/SIL/Parser/debug_info.sil @@ -0,0 +1,26 @@ +// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo %s | %FileCheck %s +sil_stage canonical + +import Builtin +import Swift + +struct TheStruct { + var data : Builtin.Int64 + init() +} + +sil_scope 1 { parent @struct_debug_info_test : $@convention(thin) (TheStruct) -> TheStruct } + +// SR-14814: Make sure the `forwarding` directive being optional in the presence +// of debug info directives (i.e. `loc` and `scope`) + +// CHECK-LABEL: sil [transparent] @struct_debug_info_test : +sil [transparent] @struct_debug_info_test : $@convention(thin) (TheStruct) -> TheStruct { +bb0(%0 : $TheStruct): + // CHECK: %1 = struct_extract %0 : $TheStruct, #TheStruct.data, loc "input.swift":3:4, scope 1 + %1 = struct_extract %0 : $TheStruct, #TheStruct.data, loc "input.swift":3:4, scope 1 + // CHECK: %2 = struct $TheStruct (%1 : $Builtin.Int64), loc "input.swift":5:6, scope 1 + %2 = struct $TheStruct (%1 : $Builtin.Int64), loc "input.swift":5:6, scope 1 + return %2 : $TheStruct, loc "input.swift":7:8, scope 1 +} +// CHECK: } // end sil function 'struct_debug_info_test' diff --git a/test/SIL/Parser/undef.sil b/test/SIL/Parser/undef.sil index a0d6780369c6e..3545ed9431343 100644 --- a/test/SIL/Parser/undef.sil +++ b/test/SIL/Parser/undef.sil @@ -48,8 +48,8 @@ bb0: // CHECK: debug_value undef : $() debug_value undef : $() - // CHECK: debug_value_addr undef : $*() - debug_value_addr undef : $*() + // CHECK: debug_value undef : $*(), expr op_deref + debug_value undef : $*(), expr op_deref // Accessing memory diff --git a/test/SIL/Serialization/borrow.sil b/test/SIL/Serialization/borrow.sil index fc1e7f5863f5c..cdb8725d165f2 100644 --- a/test/SIL/Serialization/borrow.sil +++ b/test/SIL/Serialization/borrow.sil @@ -29,3 +29,17 @@ bb0(%0 : $*Builtin.NativeObject, %1 : @guaranteed $Builtin.NativeObject): %4 = tuple() return %4 : $() } + +class C {} + +// CHECK-LABEL: sil [ossa] @defined_borrow_test +// CHECK: begin_borrow [defined] {{%[^,]+}} +// CHECK-LABEL: } // end sil function 'defined_borrow_test' +sil [ossa] @defined_borrow_test : $@convention(thin) () -> () { + %instance = alloc_ref $C + %guaranteed_c = begin_borrow [defined] %instance : $C + end_borrow %guaranteed_c : $C + destroy_value %instance : $C + %res = tuple () + return %res : $() +} diff --git a/test/SIL/Serialization/deserialize_coregraphics.swift b/test/SIL/Serialization/deserialize_coregraphics.swift deleted file mode 100644 index 3da0d49f40248..0000000000000 --- a/test/SIL/Serialization/deserialize_coregraphics.swift +++ /dev/null @@ -1,7 +0,0 @@ -// Make sure that we can deserialize CoreGraphics. -// RUN: %target-sil-opt %platform-sdk-overlay-dir/CoreGraphics.swiftmodule/%target-swiftmodule-name -module-name CoreGraphics > /dev/null -// RUN: llvm-bcanalyzer %platform-sdk-overlay-dir/CoreGraphics.swiftmodule/%target-swiftmodule-name | %FileCheck %s - -// REQUIRES: objc_interop - -// CHECK-NOT: Unknown diff --git a/test/SIL/Serialization/deserialize_foundation.sil b/test/SIL/Serialization/deserialize_foundation.sil deleted file mode 100644 index 1a9de216aba26..0000000000000 --- a/test/SIL/Serialization/deserialize_foundation.sil +++ /dev/null @@ -1,7 +0,0 @@ -// Make sure that we can deserialize foundation. -// RUN: %target-sil-opt %platform-sdk-overlay-dir/Foundation.swiftmodule/%target-swiftmodule-name -module-name Foundation > /dev/null -// RUN: llvm-bcanalyzer %platform-sdk-overlay-dir/Foundation.swiftmodule/%target-swiftmodule-name | %FileCheck %s - -// REQUIRES: objc_interop - -// CHECK-NOT: Unknown{{Code|Block}} diff --git a/test/SIL/Serialization/deserialize_objectivec.sil b/test/SIL/Serialization/deserialize_objectivec.sil deleted file mode 100644 index fa7ab9f3d3d3e..0000000000000 --- a/test/SIL/Serialization/deserialize_objectivec.sil +++ /dev/null @@ -1,7 +0,0 @@ -// Make sure that we can deserialize Objective-C. -// RUN: %target-sil-opt %platform-sdk-overlay-dir/ObjectiveC.swiftmodule/%target-swiftmodule-name -module-name ObjectiveC > /dev/null -// RUN: llvm-bcanalyzer %platform-sdk-overlay-dir/ObjectiveC.swiftmodule/%target-swiftmodule-name | %FileCheck %s - -// REQUIRES: objc_interop - -// CHECK-NOT: Unknown diff --git a/test/SIL/cloning.sil b/test/SIL/cloning.sil index 390532d4f0e99..1f1b84375d812 100644 --- a/test/SIL/cloning.sil +++ b/test/SIL/cloning.sil @@ -28,3 +28,20 @@ bb0: return %2 : $() } +sil [ossa] [always_inline] @callee_begin_borrow_defined : $@convention(thin) () -> () { + %instance = alloc_ref $X + %guaranteed_c = begin_borrow [defined] %instance : $X + end_borrow %guaranteed_c : $X + destroy_value %instance : $X + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @caller_begin_borrow_defined +// CHECK: begin_borrow [defined] +// CHECK-LABEL: } // end sil function 'caller_begin_borrow_defined' +sil [ossa] @caller_begin_borrow_defined : $@convention(thin) () -> () { + %callee_begin_borrow_defined = function_ref @callee_begin_borrow_defined : $@convention(thin) () -> () + %res = apply %callee_begin_borrow_defined() : $@convention(thin) () -> () + return %res : $() +} diff --git a/test/SIL/concurrentclosure_capture_verify_canonical_addressonly.sil b/test/SIL/concurrentclosure_capture_verify_canonical_addressonly.sil index c016effa90e6d..9f667380ae320 100644 --- a/test/SIL/concurrentclosure_capture_verify_canonical_addressonly.sil +++ b/test/SIL/concurrentclosure_capture_verify_canonical_addressonly.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-experimental-concurrency %s +// RUN: %target-sil-opt %s // REQUIRES: concurrency @@ -47,7 +47,7 @@ sil @$s37concurrentfunction_capturediagnostics1FCfD : $@convention(method) (@own // This is address only so we shouldn't crash. sil hidden [ossa] @$s37concurrentfunction_capturediagnostics20testCaseAddressOnly21iyx_tAA6MyProtRzlF : $@convention(thin) (@in_guaranteed T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "i", argno 1 // id: %1 + debug_value %0 : $*T, let, name "i", argno 1, expr op_deref // id: %1 %2 = alloc_stack $F, var, name "f2" // users: %34, %33, %7 %3 = metatype $@thick F.Type // user: %5 // function_ref F.__allocating_init() diff --git a/test/SIL/concurrentclosure_capture_verify_canonical_loadable.sil b/test/SIL/concurrentclosure_capture_verify_canonical_loadable.sil index 8186d3fd6fd50..ca9365662f498 100644 --- a/test/SIL/concurrentclosure_capture_verify_canonical_loadable.sil +++ b/test/SIL/concurrentclosure_capture_verify_canonical_loadable.sil @@ -1,4 +1,4 @@ -// RUN: not --crash %target-sil-opt -enable-experimental-concurrency %s +// RUN: not --crash %target-sil-opt %s // REQUIRES: concurrency diff --git a/test/SIL/concurrentclosure_capture_verify_raw.sil b/test/SIL/concurrentclosure_capture_verify_raw.sil index d8007fa563a6d..265f335d1c76e 100644 --- a/test/SIL/concurrentclosure_capture_verify_raw.sil +++ b/test/SIL/concurrentclosure_capture_verify_raw.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-experimental-concurrency %s +// RUN: %target-sil-opt %s // REQUIRES: concurrency @@ -80,7 +80,7 @@ sil @$s37concurrentfunction_capturediagnostics1FCfD : $@convention(method) (@own // This is address only so we shouldn't crash. sil hidden [ossa] @$s37concurrentfunction_capturediagnostics20testCaseAddressOnly21iyx_tAA6MyProtRzlF : $@convention(thin) (@in_guaranteed T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "i", argno 1 // id: %1 + debug_value %0 : $*T, let, name "i", argno 1, expr op_deref // id: %1 %2 = alloc_stack $F, var, name "f2" // users: %34, %33, %7 %3 = metatype $@thick F.Type // user: %5 // function_ref F.__allocating_init() diff --git a/test/SIL/memory_lifetime.sil b/test/SIL/memory_lifetime.sil index 0a3a9f3e65ffa..ea1dce88822d2 100644 --- a/test/SIL/memory_lifetime.sil +++ b/test/SIL/memory_lifetime.sil @@ -464,6 +464,17 @@ bb0(%0 : @owned $T): return %r : $() } +sil [ossa] @test_store_enum_tuple : $@convention(thin) (Int) -> () { +bb0(%0 : $Int): + %2 = enum $Optional, #Optional.none!enumelt + %3 = tuple (%0 : $Int, %2 : $Optional) + %8 = alloc_stack $(Int, Optional) + store %3 to [trivial] %8 : $*(Int, Optional) + dealloc_stack %8 : $*(Int, Optional) + %13 = tuple () + return %13 : $() +} + sil [ossa] @test_select_enum_addr : $@convention(thin) (@in_guaranteed Optional) -> Builtin.Int1 { bb0(%0 : $*Optional): %1 = integer_literal $Builtin.Int1, -1 diff --git a/test/SIL/memory_lifetime_failures.sil b/test/SIL/memory_lifetime_failures.sil index aa4dbf3c70fac..d07a28dcad65e 100644 --- a/test/SIL/memory_lifetime_failures.sil +++ b/test/SIL/memory_lifetime_failures.sil @@ -538,3 +538,32 @@ bb0(%0 : $Bool): %10 = tuple () return %10 : $() } + +// MemoryLifetimeVerifier does not detect an error here due to reborrows +sil [ossa] @test_load_borrow1 : $@convention(thin) (@in Optional) -> () { +bb0(%0 : $*Optional): + destroy_addr %0 : $*Optional + %1 = load_borrow %0 : $*Optional + br bb1(%1 : $Optional) + +bb1(%3 : @guaranteed $Optional): + end_borrow %3 : $Optional + br bb2 + +bb2: + %r = tuple () + return %r : $() +} + +// CHECK: SIL memory lifetime failure in @test_load_borrow2: memory is not initialized, but should +sil [ossa] @test_load_borrow2 : $@convention(thin) (@in Optional) -> () { +bb0(%0 : $*Optional): + destroy_addr %0 : $*Optional + %1 = load_borrow %0 : $*Optional + end_borrow %1 : $Optional + br bb1 + +bb1: + %r = tuple () + return %r : $() +} diff --git a/test/SIL/ownership-verifier/borrow_and_concurrency.sil b/test/SIL/ownership-verifier/borrow_and_concurrency.sil index a13aeef216906..0421c507018b6 100644 --- a/test/SIL/ownership-verifier/borrow_and_concurrency.sil +++ b/test/SIL/ownership-verifier/borrow_and_concurrency.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-experimental-concurrency -o /dev/null %s +// RUN: %target-sil-opt -o /dev/null %s // REQUIRES: concurrency diff --git a/test/SIL/ownership-verifier/borrow_scope_introducing_operands_positive.sil b/test/SIL/ownership-verifier/borrow_scope_introducing_operands_positive.sil index ffb79bb7dac1c..894a6bf56e565 100644 --- a/test/SIL/ownership-verifier/borrow_scope_introducing_operands_positive.sil +++ b/test/SIL/ownership-verifier/borrow_scope_introducing_operands_positive.sil @@ -7,6 +7,10 @@ import Builtin +class C {} + +sil @getOwnedC : $@convention(thin) () -> (@owned C) + sil [ossa] @coroutine_callee : $@yield_once (@guaranteed Builtin.NativeObject) -> () { bb0(%0 : @guaranteed $Builtin.NativeObject): yield (), resume bb1, unwind bb2 @@ -172,3 +176,25 @@ bb3: %r = tuple () return %r : $() } + +// Test a reborrow on the same branch as the consume. +sil [ossa] @testReborrow : $@convention(thin) (@owned C) -> () { +bb0(%0 : @owned $C): + cond_br undef, bb1, bb2 +bb1: + destroy_value %0 : $C + %f = function_ref @getOwnedC : $@convention(thin) () -> (@owned C) + %owned1 = apply %f() : $@convention(thin) () -> (@owned C) + %copy1 = copy_value %owned1 : $C + %borrow1 = begin_borrow %copy1 : $C + destroy_value %owned1 : $C + br bb3(%borrow1 : $C, %copy1 : $C) +bb2: + %borrow2 = begin_borrow %0 : $C + br bb3(%borrow2 : $C, %0 : $C) +bb3(%borrow3 : @guaranteed $C, %copy3 : @owned $C): + end_borrow %borrow3 : $C + destroy_value %copy3 : $C + %result = tuple () + return %result : $() +} diff --git a/test/SIL/ownership-verifier/interior_pointer.sil b/test/SIL/ownership-verifier/interior_pointer.sil index 2ed9e3a5c221f..4b52eb148f5d1 100644 --- a/test/SIL/ownership-verifier/interior_pointer.sil +++ b/test/SIL/ownership-verifier/interior_pointer.sil @@ -103,6 +103,18 @@ bb0(%0 : @owned $Box, %1 : $*Int): return %4 : $() } +// CHECK-NOT: Function: 'isunique_test' +sil [ossa] @isunique_test : $@convention(thin) (@owned Box, @in Int) -> () { +bb0(%0 : @owned $Box, %1 : $*Int): + %2 = begin_borrow %0 : $Box + %3 = ref_element_addr %2 : $Box, #Box.t + %4 = is_unique %3 : $*T + end_borrow %2 : $Box + destroy_value %0 : $Box + %5 = tuple () + return %5 : $() +} + // CHECK-LABEL: Error#: 0. Begin Error in Function: 'store_borrow_result_used_outside_of_borrow_lifetime' // CHECK-NEXT: Found outside of lifetime use?! // CHECK-NEXT: Value: %1 = begin_borrow %0 : $Builtin.NativeObject // users: %4, %3 diff --git a/test/SIL/ownership-verifier/load_borrow_invalidation_test.sil b/test/SIL/ownership-verifier/load_borrow_invalidation_test.sil index d45072fa0b90a..247f722096efa 100644 --- a/test/SIL/ownership-verifier/load_borrow_invalidation_test.sil +++ b/test/SIL/ownership-verifier/load_borrow_invalidation_test.sil @@ -410,3 +410,21 @@ bb0(%0 : $*Builtin.NativeObject): return %2 : $Bool } +sil [ossa] @test_valuemetatype : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + %3a = value_metatype $@thick Builtin.NativeObject.Type, %1 : $*Builtin.NativeObject + %2 = load_borrow %1 : $*Builtin.NativeObject + %gUser = function_ref @guaranteedUser : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %gUser(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + %3 = address_to_pointer %1 : $*Builtin.NativeObject to $Builtin.RawPointer + %4 = mark_dependence %3 : $Builtin.RawPointer on %1 : $*Builtin.NativeObject + %rawPointerUser = function_ref @useRawPointer : $@convention(thin) (Builtin.RawPointer) -> () + apply %rawPointerUser(%4) : $@convention(thin) (Builtin.RawPointer) -> () + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} diff --git a/test/SILGen/Inputs/mangle_conformance_access_path_helper.swift b/test/SILGen/Inputs/mangle_conformance_access_path_helper.swift new file mode 100644 index 0000000000000..89501bb86530b --- /dev/null +++ b/test/SILGen/Inputs/mangle_conformance_access_path_helper.swift @@ -0,0 +1,13 @@ +public protocol P { + associatedtype T +} + +public protocol Q { + associatedtype U +} + +public protocol R { + associatedtype U : Q where U.U : P +} + +public struct G {} diff --git a/test/SILGen/Inputs/opaque_result_type_nested_optional_other.swift b/test/SILGen/Inputs/opaque_result_type_nested_optional_other.swift new file mode 100644 index 0000000000000..5164ab0e01d81 --- /dev/null +++ b/test/SILGen/Inputs/opaque_result_type_nested_optional_other.swift @@ -0,0 +1,14 @@ +public protocol P { + associatedtype A +} + +public func bar(_: T) -> T.A { + fatalError() +} + +public struct S : P {} + +public func foo() -> some P { + return S() +} + diff --git a/test/SILGen/accessors_testing.swift b/test/SILGen/accessors_testing.swift new file mode 100644 index 0000000000000..ed3241181b94d --- /dev/null +++ b/test/SILGen/accessors_testing.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-emit-silgen -parse-as-library -module-name accessors %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -enable-testing -module-name accessors %s | %FileCheck %s + +// rdar://78523318: Ensure that private(set) accessors for internal or more +// visible properties have hidden linkage, because other code inside the module +// needs to reference the setter to form a key path. + +public struct Foo { + // CHECK-LABEL: sil hidden [ossa] @$s9accessors3FooV6internSivs + private(set) internal var intern: Int { + get { return 0 } + set {} + } + // CHECK-LABEL: sil hidden [ossa] @$s9accessors3FooV3pubSivs + private(set) public var pub: Int { + get { return 0 } + set {} + } + + public mutating func exercise() { + _ = intern + _ = pub + intern = 0 + pub = 0 + } +} + diff --git a/test/SILGen/address_only_types.swift b/test/SILGen/address_only_types.swift index c092abe03c889..8aa26c4b727f6 100644 --- a/test/SILGen/address_only_types.swift +++ b/test/SILGen/address_only_types.swift @@ -16,7 +16,7 @@ protocol Unloadable { // CHECK-LABEL: sil hidden [ossa] @$s18address_only_types0a1_B9_argument{{[_0-9a-zA-Z]*}}F func address_only_argument(_ x: Unloadable) { // CHECK: bb0([[XARG:%[0-9]+]] : $*Unloadable): - // CHECK: debug_value_addr [[XARG]] + // CHECK: debug_value [[XARG]] {{.*}} expr op_deref // CHECK-NEXT: tuple // CHECK-NEXT: return } @@ -31,7 +31,7 @@ func address_only_ignored_argument(_: Unloadable) { // CHECK-LABEL: sil hidden [ossa] @$s18address_only_types0a1_B7_return{{[_0-9a-zA-Z]*}}F func address_only_return(_ x: Unloadable, y: Int) -> Unloadable { // CHECK: bb0([[RET:%[0-9]+]] : $*Unloadable, [[XARG:%[0-9]+]] : $*Unloadable, [[YARG:%[0-9]+]] : $Builtin.Int64): - // CHECK-NEXT: debug_value_addr [[XARG]] : $*Unloadable, let, name "x" + // CHECK-NEXT: debug_value [[XARG]] : $*Unloadable, let, name "x", {{.*}} expr op_deref // CHECK-NEXT: debug_value [[YARG]] : $Builtin.Int64, let, name "y" // CHECK-NEXT: copy_addr [[XARG]] to [initialization] [[RET]] // CHECK-NEXT: [[VOID:%[0-9]+]] = tuple () @@ -115,7 +115,7 @@ func address_only_call_1_ignore_return() { // CHECK-LABEL: sil hidden [ossa] @$s18address_only_types0a1_B7_call_2{{[_0-9a-zA-Z]*}}F func address_only_call_2(_ x: Unloadable) { // CHECK: bb0([[XARG:%[0-9]+]] : $*Unloadable): - // CHECK: debug_value_addr [[XARG]] : $*Unloadable + // CHECK: debug_value [[XARG]] : $*Unloadable, {{.*}} expr op_deref some_address_only_function_2(x) // CHECK: [[FUNC:%[0-9]+]] = function_ref @$s18address_only_types05some_a1_B11_function_2{{[_0-9a-zA-Z]*}}F // CHECK: apply [[FUNC]]([[XARG]]) @@ -197,7 +197,7 @@ func address_only_assignment_from_temp_to_property() { // CHECK-LABEL: sil hidden [ossa] @$s18address_only_types0a1_B31_assignment_from_lv_to_property{{[_0-9a-zA-Z]*}}F func address_only_assignment_from_lv_to_property(_ v: Unloadable) { // CHECK: bb0([[VARG:%[0-9]+]] : $*Unloadable): - // CHECK: debug_value_addr [[VARG]] : $*Unloadable + // CHECK: debug_value [[VARG]] : $*Unloadable, {{.*}} expr op_deref // CHECK: [[TEMP:%[0-9]+]] = alloc_stack $Unloadable // CHECK: copy_addr [[VARG]] to [initialization] [[TEMP]] // CHECK: [[SETTER:%[0-9]+]] = function_ref @$s18address_only_types11global_propAA10Unloadable_pvs diff --git a/test/SILGen/addressors.swift b/test/SILGen/addressors.swift index 680d393078513..b06cde98a4ef8 100644 --- a/test/SILGen/addressors.swift +++ b/test/SILGen/addressors.swift @@ -203,7 +203,7 @@ struct D : Subscriptable { // SILGEN: bb0([[VALUE:%.*]] : $Int32, [[I:%.*]] : $Int32, [[SELF:%.*]] : $*D): // SILGEN: debug_value [[VALUE]] : $Int32 // SILGEN: debug_value [[I]] : $Int32 -// SILGEN: debug_value_addr [[SELF]] +// SILGEN: debug_value [[SELF]]{{.*}} expr op_deref // SILGEN: [[ACCESS:%.*]] = begin_access [modify] [unknown] [[SELF]] : $*D // SILGEN: [[T0:%.*]] = function_ref @$s10addressors1DVys5Int32VAEciau{{.*}} // SILGEN: [[PTR:%.*]] = apply [[T0]]([[I]], [[ACCESS]]) diff --git a/test/SILGen/async_builtins.swift b/test/SILGen/async_builtins.swift index 42f86efcfc3ac..a5b995003a580 100644 --- a/test/SILGen/async_builtins.swift +++ b/test/SILGen/async_builtins.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency -parse-stdlib -sil-verify-all | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -disable-availability-checking -parse-stdlib -sil-verify-all | %FileCheck %s // REQUIRES: concurrency import Swift @@ -18,22 +18,22 @@ public struct X { // CHECK-LABEL: sil hidden [ossa] @$s4test1XV12launchFutureyyxlF : $@convention(method) (@in_guaranteed T, X) -> () func launchFuture(_ value: T) { - // CHECK: builtin "createAsyncTaskFuture"([[ZERO:%.*]] : $Int, [[FN:%.*]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) - let task = Builtin.createAsyncTaskFuture(0) { () async throws -> T in + // CHECK: builtin "createAsyncTask"([[ZERO:%.*]] : $Int, [[FN:%.*]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + _ = Builtin.createAsyncTask(0) { () async throws -> T in return value } } - // CHECK-LABEL: sil hidden [ossa] @$s4test1XV16launchGroupChildyyxlF : $@convention(method) (@in_guaranteed T, X) -> () { - func launchGroupChild(_ value: T) { - // CHECK: builtin "createAsyncTaskGroupFuture"([[ZERO:%.*]] : $Int, [[NIL:%.*]] : $Optional, [[FN:%.*]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) - let task = Builtin.createAsyncTaskGroupFuture(0, nil) { () async throws -> T in + // CHECK-LABEL: sil hidden [ossa] @$s4test1XV16launchGroupChild_5groupyx_BptlF : $@convention(method) (@in_guaranteed T, Builtin.RawPointer, X) -> () { + func launchGroupChild(_ value: T, group: Builtin.RawPointer) { + // CHECK: builtin "createAsyncTaskInGroup"([[ZERO:%.*]] : $Int, [[GROUP:%.*]] : $Builtin.RawPointer, [[FN:%.*]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + _ = Builtin.createAsyncTaskInGroup(0, group) { () async throws -> T in return value } } public func launchRocker(closure: @escaping () async throws -> T) { - _ = Builtin.createAsyncTaskFuture(0, closure) + _ = Builtin.createAsyncTask(0, closure) } } diff --git a/test/SILGen/async_conversion.swift b/test/SILGen/async_conversion.swift index 8684c3af1ae0e..6a624f4c7cdad 100644 --- a/test/SILGen/async_conversion.swift +++ b/test/SILGen/async_conversion.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -disable-availability-checking | %FileCheck %s // REQUIRES: concurrency func f(_: Int, _: String) -> String? { nil } diff --git a/test/SILGen/async_handler.swift b/test/SILGen/async_handler.swift deleted file mode 100644 index 2545a863a68b4..0000000000000 --- a/test/SILGen/async_handler.swift +++ /dev/null @@ -1,59 +0,0 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency -enable-experimental-async-handler | %FileCheck %s -// REQUIRES: concurrency - -func take(_ t: T) async { - print(t) -} - -// CHECK-LABEL: sil [ossa] @$s4test13simpleHandleryySiF : $@convention(thin) (Int) -> () { -// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test13simpleHandleryySiYaF : $@convention(thin) @async (Int) -> () -// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]](%0) : $@convention(thin) @async (Int) -> () -// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$ss16_runAsyncHandler9operationyyyYac_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> () -// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> () -// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> () -// CHECK: } // end sil function '$s4test13simpleHandleryySiF' -@asyncHandler -public func simpleHandler(_ i: Int) { - await take(i) -} - -// CHECK-LABEL: sil [ossa] @$s4test20nonTrivialArgHandleryySSF : $@convention(thin) (@guaranteed String) -> () { -// CHECK: [[COPY:%[0-9]+]] = copy_value %0 : $String -// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test20nonTrivialArgHandleryySSYaF : $@convention(thin) @async (@guaranteed String) -> () -// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]]([[COPY]]) : $@convention(thin) @async (@guaranteed String) -> () -// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$ss16_runAsyncHandler9operationyyyYac_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> () -// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> () -// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> () -// CHECK: } // end sil function '$s4test20nonTrivialArgHandleryySSF' -@asyncHandler -public func nonTrivialArgHandler(_ s: String) { - await take(s) -} - -// CHECK-LABEL: sil [ossa] @$s4test14genericHandleryyxlF : $@convention(thin) (@in_guaranteed T) -> () { -// CHECK: [[TMP:%[0-9]+]] = alloc_stack $T -// CHECK: copy_addr %0 to [initialization] [[TMP]] : $*T -// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test14genericHandleryyxYalF : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () -// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]]([[TMP]]) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () -// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$ss16_runAsyncHandler9operationyyyYac_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> () -// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> () -// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> () -// CHECK: } // end sil function '$s4test14genericHandleryyxlF' -@asyncHandler -public func genericHandler(_ t: T) { - await take(t) -} - -public struct Mystruct { - // CHECK-LABEL: sil [ossa] @$s4test8MystructV13memberHandleryySiF : $@convention(method) (Int, Mystruct) -> () { - // CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test8MystructV13memberHandleryySiYaF : $@convention(method) @async (Int, Mystruct) -> () - // CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]](%0, %1) : $@convention(method) @async (Int, Mystruct) -> () - // CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$ss16_runAsyncHandler9operationyyyYac_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> () - // CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> () - // CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> () - // CHECK: } // end sil function '$s4test8MystructV13memberHandleryySiF' - @asyncHandler - public func memberHandler(_ i: Int) { - await take(i) - } -} diff --git a/test/SILGen/async_handler_witness_table.swift b/test/SILGen/async_handler_witness_table.swift deleted file mode 100644 index 0b8e6b638e85b..0000000000000 --- a/test/SILGen/async_handler_witness_table.swift +++ /dev/null @@ -1,18 +0,0 @@ -// RUN: %target-swift-emit-silgen -sdk %S/Inputs -I %S/Inputs -enable-source-import %s -enable-experimental-concurrency -enable-experimental-async-handler> %t.out -// RUN: %FileCheck %s < %t.out - -// REQUIRES: objc_interop -// REQUIRES: concurrency - -import gizmo - -func hashEm(_ x: H) {} - -public class A { - @asyncHandler - public func f() { - hashEm(NSRuncingOptions.mince) - } -} - -// CHECK: sil_witness_table shared [serialized] NSRuncingOptions: Hashable module gizmo diff --git a/test/SILGen/async_initializer.swift b/test/SILGen/async_initializer.swift index 5d1ef73254690..886dfc4469fd3 100644 --- a/test/SILGen/async_initializer.swift +++ b/test/SILGen/async_initializer.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name initializers -swift-version 5 -enable-experimental-concurrency | %FileCheck --enable-var-scope %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name initializers -swift-version 5 -disable-availability-checking | %FileCheck %s --enable-var-scope --implicit-check-not=hop_to_executor // REQUIRES: concurrency // CHECK: protocol Person { @@ -57,8 +57,6 @@ enum MyEnum { actor MyActor { // CHECK-DAG: sil hidden [ossa] @$s12initializers7MyActorCACyYacfc : $@convention(method) @async (@owned MyActor) -> @owned MyActor - // CHECK-NOT: hop_to_executor - // CHECK-DAG: } // end sil function '$s12initializers7MyActorCACyYacfc' init() async {} } @@ -153,4 +151,48 @@ func makeDog() async { // CHECK: } // end sil function '$s12initializers8makeBirbyyYaF' func makeBirb() async { _ = await Birb(name: "Chirpy") -} \ No newline at end of file +} + +actor SomeActor { + var x: Int = 0 + + // NOTE: during SILGen, we don't expect any hop_to_executors in here. + // The implicit check-not covers that for us. The hops are inserted later. + init() async {} + + // CHECK-LABEL: sil hidden [ossa] @$s12initializers9SomeActorC10someMethodyyYaF : $@convention(method) @async (@guaranteed SomeActor) -> () { + // CHECK: hop_to_executor {{%[0-9]+}} : $SomeActor + // CHECK: } // end sil function '$s12initializers9SomeActorC10someMethodyyYaF' + func someMethod() async {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s12initializers9makeActorAA04SomeC0CyYaF : $@convention(thin) @async () -> @owned SomeActor { +// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor +// CHECK: [[INIT:%[0-9]+]] = function_ref @$s12initializers9SomeActorCACyYacfC : $@convention(method) @async (@thick SomeActor.Type) -> @owned SomeActor +// CHECK: {{%[0-9]+}} = apply [[INIT]]({{%[0-9]+}}) : $@convention(method) @async (@thick SomeActor.Type) -> @owned SomeActor +// CHECK: hop_to_executor {{%[0-9]+}} : $MainActor +// CHECK: } // end sil function '$s12initializers9makeActorAA04SomeC0CyYaF' +@MainActor +func makeActor() async -> SomeActor { + return await SomeActor() +} + +// None of the below calls are expected to have a hop before or after the call. + +func makeActorFromGeneric() async -> SomeActor { + return await SomeActor() +} + +func callActorMethodFromGeneric(a: SomeActor) async { + await a.someMethod() +} + +@available(SwiftStdlib 5.5, *) +func makeActorInTask() async { + Task.detached { await SomeActor() } +} + +@available(SwiftStdlib 5.5, *) +func callActorMethodInTask(a: SomeActor) async { + Task.detached { await a.someMethod() } +} diff --git a/test/SILGen/async_let.swift b/test/SILGen/async_let.swift index ef6eb68a941cb..62bb2fc575248 100644 --- a/test/SILGen/async_let.swift +++ b/test/SILGen/async_let.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency -parse-stdlib -sil-verify-all | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -disable-availability-checking -parse-stdlib -sil-verify-all | %FileCheck %s --dump-input always // REQUIRES: concurrency import Swift @@ -16,24 +16,18 @@ enum SomeError: Error { // CHECK-LABEL: sil hidden [ossa] @$s4test0A11AsyncLetIntSiyYaF : $@convention(thin) @async () -> Int func testAsyncLetInt() async -> Int { - // CHECK: [[I:%.*]] = mark_uninitialized [var] %0 - // CHECK: [[CLOSURE:%.*]] = function_ref @$s4test0A11AsyncLetIntSiyYaFSiyYaYbcfu_ : $@convention(thin) @Sendable @async () -> Int - // CHECK: [[THICK_CLOSURE:%.*]] = thin_to_thick_function [[CLOSURE]] : $@convention(thin) @Sendable @async () -> Int to $@Sendable @async @callee_guaranteed () -> Int - // CHECK: [[REABSTRACT_THUNK:%.*]] = function_ref @$sSiIeghHd_Sis5Error_pIegHrzo_TR : $@convention(thin) @async (@guaranteed @Sendable @async @callee_guaranteed () -> Int) -> (@out Int, @error Error) - // CHECK: [[REABSTRACT_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT_THUNK]]([[THICK_CLOSURE]]) : $@convention(thin) @async (@guaranteed @Sendable @async @callee_guaranteed () -> Int) -> (@out Int, @error Error) - // CHECK: [[ESCAPING_CLOSURE:%.*]] = convert_function [[REABSTRACT_CLOSURE]] : $@async @callee_guaranteed () -> (@out Int, @error Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for - // CHECK: [[CLOSURE_ARG:%.*]] = convert_escape_to_noescape [not_guaranteed] [[ESCAPING_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for to $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for - // CHECK: [[ASYNC_LET_START:%.*]] = builtin "startAsyncLet"([[CLOSURE_ARG]] : $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $Builtin.RawPointer + // CHECK: [[ASYNC_LET_START:%.*]] = builtin "startAsyncLetWithLocalBuffer"({{.*}}, [[BUFFER:%[0-9]+]] : $Builtin.RawPointer) async let i = await getInt() - // CHECK: [[ASYNC_LET_GET:%.*]] = function_ref @swift_asyncLet_wait : $@convention(thin) @async <τ_0_0> (Builtin.RawPointer) -> @out τ_0_0 - // CHECK: [[INT_RESULT:%.*]] = alloc_stack $Int - // CHECK: apply [[ASYNC_LET_GET]]([[INT_RESULT]], [[ASYNC_LET_START]]) : $@convention(thin) @async <τ_0_0> (Builtin.RawPointer) -> @out τ_0_0 - // CHECK: [[INT_RESULT_VALUE:%.*]] = load [trivial] [[INT_RESULT]] : $*Int - // CHECK: assign [[INT_RESULT_VALUE]] to [[I]] : $*Int + // CHECK: [[ASYNC_LET_GET:%.*]] = function_ref @swift_asyncLet_get + // CHECK: apply [[ASYNC_LET_GET]]([[ASYNC_LET_START]], [[BUFFER]]) + // CHECK: [[ADDR:%.*]] = pointer_to_address [[BUFFER]] : $Builtin.RawPointer to [strict] [invariant] $*Int + // CHECK: [[INT_RESULT_VALUE:%.*]] = load [trivial] [[ADDR]] : $*Int return await i - // CHECK: [[ASYNC_LET_END:%.*]] = builtin "endAsyncLet"([[ASYNC_LET_START]] : $Builtin.RawPointer) : $() + // CHECK: [[FINISH:%.*]] = function_ref @swift_asyncLet_finish + // CHECK: apply [[FINISH]]([[ASYNC_LET_START]], [[BUFFER]]) + // CHECK: builtin "endAsyncLetLifetime"([[ASYNC_LET_START]] : $Builtin.RawPointer) } func testAsyncLetWithThrows(cond: Bool) async throws -> String { @@ -51,29 +45,32 @@ func testAsyncLetWithThrows(cond: Bool) async throws -> String { func testAsyncLetThrows() async throws -> String { async let s = try await getStringThrowingly() - // CHECK: [[ASYNC_LET_WAIT_THROWING:%.*]] = function_ref @swift_asyncLet_wait_throwing : $@convention(thin) @async <τ_0_0> (Builtin.RawPointer) -> (@out τ_0_0, @error Error) - // CHECK: try_apply [[ASYNC_LET_WAIT_THROWING]] + // CHECK: [[ASYNC_LET_GET_THROWING:%.*]] = function_ref @swift_asyncLet_get_throwing + // CHECK: try_apply [[ASYNC_LET_GET_THROWING]] return try await s } // CHECK-LABEL: sil hidden [ossa] @$s4test0A14DecomposeAwait4condSiSb_tYaF : $@convention(thin) @async (Bool) -> Int { func testDecomposeAwait(cond: Bool) async -> Int { - // CHECK: [[I_VAR:%.*]] = alloc_stack $Int, let, name "i" - // CHECK: [[I:%.*]] = mark_uninitialized [var] [[I_VAR]] : $*Int - // CHECK: [[S_VAR:%.*]] = alloc_stack $String, let, name "s" - // CHECK: [[S:%.*]] = mark_uninitialized [var] [[S_VAR]] : $*String + // CHECK: [[ASYNC_LET_START:%.*]] = builtin "startAsyncLetWithLocalBuffer"<(Int, String)>({{.*}}, [[BUFFER:%[0-9]+]] : $Builtin.RawPointer) async let (i, s) = await getIntAndString() if cond { - // CHECK: [[ASYNC_LET_GET:%.*]] = function_ref @swift_asyncLet_wait : $@convention(thin) @async <τ_0_0> (Builtin.RawPointer) -> @out τ_0_0 - // CHECK: [[TUPLE_RESULT:%.*]] = alloc_stack $(Int, String) - // CHECK: apply [[ASYNC_LET_GET]]<(Int, String)>([[TUPLE_RESULT]], {{%.*}}) : $@convention(thin) @async <τ_0_0> (Builtin.RawPointer) -> @out τ_0_0 - // CHECK: [[TUPLE_RESULT_VAL:%.*]] = load [take] [[TUPLE_RESULT]] : $*(Int, String) - // CHECK: ([[FIRST_VAL:%.*]], [[SECOND_VAL:%.*]]) = destructure_tuple [[TUPLE_RESULT_VAL]] : $(Int, String) - // CHECK: assign [[FIRST_VAL]] to [[I]] : $*Int - // CHECK: assign [[SECOND_VAL]] to [[S]] : $*String + // CHECK: [[ASYNC_LET_GET:%.*]] = function_ref @swift_asyncLet_get + // CHECK: apply [[ASYNC_LET_GET]]([[ASYNC_LET_START]], [[BUFFER]]) + // CHECK: [[ADDR:%.*]] = pointer_to_address [[BUFFER]] : $Builtin.RawPointer to [strict] [invariant] $*(Int, String) + // CHECK: [[ELT:%.*]] = tuple_element_addr [[ADDR]] : $*(Int, String), 1 + // CHECK: load [copy] [[ELT]] : $*String return await Int(s)! } + // CHECK: [[ASYNC_LET_GET:%.*]] = function_ref @swift_asyncLet_get + // CHECK: apply [[ASYNC_LET_GET]]([[ASYNC_LET_START]], [[BUFFER]]) + // CHECK: [[ADDR:%.*]] = pointer_to_address [[BUFFER]] : $Builtin.RawPointer to [strict] [invariant] $*(Int, String) + // CHECK: [[ELT:%.*]] = tuple_element_addr [[ADDR]] : $*(Int, String), 0 + // CHECK: load [trivial] [[ELT]] : $*Int return await i + // CHECK: [[FINISH:%.*]] = function_ref @swift_asyncLet_finish + // CHECK: apply [[FINISH]]([[ASYNC_LET_START]], [[BUFFER]]) + // CHECK: builtin "endAsyncLetLifetime"([[ASYNC_LET_START]] : $Builtin.RawPointer) } diff --git a/test/SILGen/async_vtable_thunk.swift b/test/SILGen/async_vtable_thunk.swift index 014efc254776f..da553549467bc 100644 --- a/test/SILGen/async_vtable_thunk.swift +++ b/test/SILGen/async_vtable_thunk.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -disable-availability-checking | %FileCheck %s // REQUIRES: concurrency class BaseClass { diff --git a/test/SILGen/availability_query.swift b/test/SILGen/availability_query.swift index 6cae310c96d7b..2e165c6099475 100644 --- a/test/SILGen/availability_query.swift +++ b/test/SILGen/availability_query.swift @@ -10,9 +10,21 @@ // CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 8 // CHECK: [[FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 // CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK-NOT: {{.*}}integer_literal $Builtin.Int1, -1 +// CHECK-NOT: builtin "xor_Int1"{{.*}} if #available(OSX 10.53.8, iOS 7.1, *) { } +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 53 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 8 +// CHECK: [[FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[MINUSONE:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK: [[QUERY_INVERSION:%.*]] = builtin "xor_Int1"([[QUERY_RESULT]] : $Builtin.Int1, [[MINUSONE]] : $Builtin.Int1) : $Builtin.Int1 +if #unavailable(OSX 10.53.8, iOS 7.1) { +} + // CHECK: [[TRUE:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK: cond_br [[TRUE]] // Since we are compiling for an unmentioned platform (OS X), we check against the minimum @@ -20,12 +32,20 @@ if #available(OSX 10.53.8, iOS 7.1, *) { if #available(iOS 7.1, *) { } +// CHECK: [[FALSE:%.*]] = integer_literal $Builtin.Int1, 0 +// CHECK: cond_br [[FALSE]] +// Since we are compiling for an unmentioned platform (OS X), we check against the minimum +// deployment target, which is 10.50 +if #unavailable(iOS 7.1) { +} // CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 // CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 52 // CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 // CHECK: [[QUERY_FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 // CHECK: [[QUERY_RESULT:%.*]] = apply [[QUERY_FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK-NOT: {{.*}}integer_literal $Builtin.Int1, -1 +// CHECK-NOT: builtin "xor_Int1"{{.*}} if #available(OSX 10.52, *) { } @@ -34,17 +54,51 @@ if #available(OSX 10.52, *) { // CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 // CHECK: [[QUERY_FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 // CHECK: [[QUERY_RESULT:%.*]] = apply [[QUERY_FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[MINUSONE:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK: [[QUERY_INVERSION:%.*]] = builtin "xor_Int1"([[QUERY_RESULT]] : $Builtin.Int1, [[MINUSONE]] : $Builtin.Int1) : $Builtin.Int1 +if #unavailable(OSX 10.52) { +} + +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 52 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[QUERY_FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[QUERY_FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK-NOT: {{.*}}integer_literal $Builtin.Int1, -1 +// CHECK-NOT: builtin "xor_Int1"{{.*}} if #available(macOS 10.52, *) { } +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 52 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[QUERY_FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[QUERY_FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[MINUSONE:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK: [[QUERY_INVERSION:%.*]] = builtin "xor_Int1"([[QUERY_RESULT]] : $Builtin.Int1, [[MINUSONE]] : $Builtin.Int1) : $Builtin.Int1 +if #unavailable(macOS 10.52) { +} + // CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 // CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 0 // CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 // CHECK: [[QUERY_FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 // CHECK: [[QUERY_RESULT:%.*]] = apply [[QUERY_FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK-NOT: {{.*}}integer_literal $Builtin.Int1, -1 +// CHECK-NOT: builtin "xor_Int1"{{.*}} if #available(OSX 10, *) { } +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[QUERY_FUNC:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[QUERY_RESULT:%.*]] = apply [[QUERY_FUNC]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[MINUSONE:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK: [[QUERY_INVERSION:%.*]] = builtin "xor_Int1"([[QUERY_RESULT]] : $Builtin.Int1, [[MINUSONE]] : $Builtin.Int1) : $Builtin.Int1 +if #unavailable(OSX 10) { +} + // CHECK: } func doThing() {} @@ -54,7 +108,14 @@ func testUnreachableVersionAvailable(condition: Bool) { doThing() // no-warning return } else { - doThing() // FIXME-warning {{will never be executed}} + doThing() // no-warning + } + + if #unavailable(OSX 10.0) { + doThing() // no-warning + } else { + doThing() // no-warning + return } if true { @@ -73,6 +134,13 @@ func testUnreachablePlatformAvailable(condition: Bool) { doThing() // no-warning } + if #unavailable(iOS 7.1) { + doThing() // no-warning + } else { + doThing() // no-warning + return + } + if true { doThing() // no-warning } @@ -87,5 +155,10 @@ func testUnreachablePlatformAvailableGuard() { return } + guard #unavailable(iOS 7.1) else { + doThing() // no-warning + return + } + doThing() // no-warning } diff --git a/test/SILGen/capture_order.swift b/test/SILGen/capture_order.swift index 75713fa150cc2..02ad4fd917d1b 100644 --- a/test/SILGen/capture_order.swift +++ b/test/SILGen/capture_order.swift @@ -165,3 +165,23 @@ class rdar40600800 { } } } + +// Make sure we can't capture an uninitialized 'var' box, either. +func SR14747() { + func g() -> Int { // expected-error {{closure captures 'r' before it is declared}} + _ = r // expected-note {{captured here}} + return 5 + } + var r = g() // expected-note {{captured value declared here}} + // expected-warning@-1 {{variable 'r' was never mutated; consider changing to 'let' constant}} +} + +class class77933460 {} + +func func77933460() { + var obj: class77933460 = { obj }() + // expected-error@-1 {{closure captures 'obj' before it is declared}} + // expected-note@-2 {{captured here}} + // expected-note@-3 {{captured value declared here}} + // expected-warning@-4 {{variable 'obj' was never mutated; consider changing to 'let' constant}} +} diff --git a/test/SILGen/check_executor.swift b/test/SILGen/check_executor.swift index 5dc129948a83c..eae0b9e682582 100644 --- a/test/SILGen/check_executor.swift +++ b/test/SILGen/check_executor.swift @@ -1,6 +1,6 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency -enable-actor-data-race-checks | %FileCheck --enable-var-scope %s --check-prefix=CHECK-RAW -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency -enable-actor-data-race-checks > %t.sil -// RUN: %target-sil-opt -enable-sil-verify-all %t.sil -lower-hop-to-actor -enable-experimental-concurrency | %FileCheck --enable-var-scope %s --check-prefix=CHECK-CANONICAL +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -disable-availability-checking -enable-actor-data-race-checks | %FileCheck --enable-var-scope %s --check-prefix=CHECK-RAW +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -disable-availability-checking -enable-actor-data-race-checks > %t.sil +// RUN: %target-sil-opt -enable-sil-verify-all %t.sil -lower-hop-to-actor | %FileCheck --enable-var-scope %s --check-prefix=CHECK-CANONICAL // REQUIRES: concurrency import Swift @@ -32,13 +32,6 @@ public actor MyActor { } } - // CHECK-RAW: sil private [ossa] @$s4test7MyActorCfdSiycfU_ - // CHECK-RAW-NOT: extract_executor - // CHECK-RAW: return [[VALUE:%.*]] : $Int - deinit { - takeClosure { self.counter } - } - // CHECK-RAW-LABEL: sil private [ossa] @$s4test7MyActorC0A10UnsafeMainyyFSiycfU_ // CHECK-RAW-NOT: _checkExpectedExecutor // CHECK-RAW: onMainActor diff --git a/test/SILGen/concurrent_functions.swift b/test/SILGen/concurrent_functions.swift index 8066a1e18b664..aac52527feefa 100644 --- a/test/SILGen/concurrent_functions.swift +++ b/test/SILGen/concurrent_functions.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency -enable-experimental-flow-sensitive-concurrent-captures | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -disable-availability-checking -enable-experimental-flow-sensitive-concurrent-captures | %FileCheck %s // REQUIRES: concurrency func acceptsConcurrent(_: @escaping @Sendable () -> Int) { } diff --git a/test/SILGen/concurrent_prologue.swift b/test/SILGen/concurrent_prologue.swift index a3d8488b865fb..86af5befa6108 100644 --- a/test/SILGen/concurrent_prologue.swift +++ b/test/SILGen/concurrent_prologue.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name a -swift-version 5 -enable-experimental-concurrency -Xllvm -sil-print-debuginfo -emit-verbose-sil -parse-as-library | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name a -swift-version 5 -disable-availability-checking -Xllvm -sil-print-debuginfo -emit-verbose-sil -parse-as-library | %FileCheck %s // REQUIRES: concurrency // Test that the async dispatch code in the prologue has auto-generated debug diff --git a/test/SILGen/default_arguments.swift b/test/SILGen/default_arguments.swift index cd27973da2f78..98efd29f3ffe1 100644 --- a/test/SILGen/default_arguments.swift +++ b/test/SILGen/default_arguments.swift @@ -434,6 +434,23 @@ func testCallableWithDefault(_ x: CallableWithDefault) { x(y: 5) } +enum E { + // CHECK-LABEL: sil hidden [ossa] @$s17default_arguments1EO6ResultV4name9platformsAESS_SaySiGtcfcfA0_ : $@convention(thin) () -> @owned Array + struct Result { + var name: String + var platforms: [Int] = [] + } + + // CHECK-LABEL: sil hidden [ossa] @$s17default_arguments1EO4testyyFZ : $@convention(method) (@thin E.Type) -> () + static func test() { + // CHECK: function_ref @$s17default_arguments1EO6ResultV4name9platformsAESS_SaySiGtcfcfA0_ : $@convention(thin) () -> @owned Array + // CHECK: function_ref @$s17default_arguments1EO4testyyFZAC6ResultVSS_SaySiGtcfu_ : $@convention(thin) (@guaranteed String, @guaranteed Array) -> @owned E.Result + + // CHECK-LABEL: sil private [ossa] @$s17default_arguments1EO4testyyFZAC6ResultVSS_SaySiGtcfu_ : $@convention(thin) (@guaranteed String, @guaranteed Array) -> @owned E.Result + var result = Self.Result(name: "") + } +} + // FIXME: Arguably we shouldn't allow calling a constructor like this, as // we usually require the user write an explicit '.init'. struct WeirdUMEInitCase { diff --git a/test/SILGen/default_arguments_local.swift b/test/SILGen/default_arguments_local.swift index d2d804f1e8a6a..2d1f4613944b0 100644 --- a/test/SILGen/default_arguments_local.swift +++ b/test/SILGen/default_arguments_local.swift @@ -70,3 +70,16 @@ class ArtClass { inner() } + +// CHECK-LABEL: sil hidden [ossa] @$s23default_arguments_local5outeryyxlF : $@convention(thin) (@in_guaranteed T) -> () +func outer(_: T) { + // CHECK-LABEL: sil private [ossa] @$s23default_arguments_local5outeryyxlF5innerL_yylF : $@convention(thin) () -> () + func inner() { print(T.self) } + + // default argument 0 of hasDefault #1 (x:) in outer(_:) + // CHECK-LABEL: sil private [ossa] @$s23default_arguments_local5outeryyxlF10hasDefaultL_1xySi_tlFfA_ : $@convention(thin) () -> Int + + // CHECK-LABEL: sil private [ossa] @$s23default_arguments_local5outeryyxlF10hasDefaultL_1xySi_tlF : $@convention(thin) (Int) -> () + func hasDefault(x: Int = { inner(); return 3 }()) {} + hasDefault() +} diff --git a/test/SILGen/designated_init_inheritance_with_where_clause.swift b/test/SILGen/designated_init_inheritance_with_where_clause.swift index f36d51855b2d6..18ba714679645 100644 --- a/test/SILGen/designated_init_inheritance_with_where_clause.swift +++ b/test/SILGen/designated_init_inheritance_with_where_clause.swift @@ -20,3 +20,32 @@ public class Pony : Horse { // CHECK-LABEL: sil [serialized] [exact_self_class] [ossa] @$s45designated_init_inheritance_with_where_clause4PonyCACyxGycAA12DomesticatedRzrlufC : $@convention(method) (@thick Pony.Type) -> @owned Pony { // CHECK-LABEL: sil [ossa] @$s45designated_init_inheritance_with_where_clause4PonyCACyxGycAA12DomesticatedRzrlufc : $@convention(method) (@owned Pony) -> @owned Pony { } + +public class Barn { + init(_: T) where T : AnyObject {} + init(_: T, _: U) where T : Domesticated, U : Ungulate {} +} + +public class BigBarn : Barn { + // CHECK-LABEL: sil hidden [ossa] @$s45designated_init_inheritance_with_where_clause7BigBarnCyACyXlcfc : $@convention(method) (@owned AnyObject, @owned BigBarn) -> @owned BigBarn { +} + +public struct Cat : Domesticated {} +public struct Sheep : Ungulate {} + +public class SmallBarn : Barn { + // CHECK-LABEL: sil hidden [ossa] @$s45designated_init_inheritance_with_where_clause9SmallBarnCyAcA3CatV_xtcAA8UngulateRzlufc : $@convention(method) (Cat, @in U, @owned SmallBarn) -> @owned SmallBarn { + // CHECK-LABEL: sil private [thunk] [ossa] @$s45designated_init_inheritance_with_where_clause9SmallBarnCyAcA3CatV_xtcAA8UngulateRzlufCAA0H0CyAHyxGx_qd__tcAA12DomesticatedRzAaFRd__lufCTV : $@convention(method) <τ_0_0 where τ_0_0 : Ungulate> (@in Cat, @in τ_0_0, @thick SmallBarn.Type) -> @owned SmallBarn { +} + +// CHECK-LABEL: sil_vtable [serialized] BigBarn { +// CHECK-NEXT: #Barn.init!allocator: (Barn.Type) -> (T) -> Barn : @$s45designated_init_inheritance_with_where_clause7BigBarnCyACyXlcfC [override] // BigBarn.__allocating_init(_:) +// CHECK-NEXT: #Barn.init!allocator: (Barn.Type) -> (T, U) -> Barn : @$s45designated_init_inheritance_with_where_clause4BarnCyACyxGx_qd__tcAA12DomesticatedRzAA8UngulateRd__lufC [inherited] // Barn.__allocating_init(_:_:) +// CHECK-NEXT: #BigBarn.deinit!deallocator: @$s45designated_init_inheritance_with_where_clause7BigBarnCfD // BigBarn.__deallocating_deinit +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable [serialized] SmallBarn { +// CHECK-NEXT: #Barn.init!allocator: (Barn.Type) -> (T) -> Barn : @$s45designated_init_inheritance_with_where_clause4BarnCyACyxGxcRlzClufC [inherited] // Barn.__allocating_init(_:) +// CHECK-NEXT: (Barn.Type) -> (T, U) -> Barn : @$s45designated_init_inheritance_with_where_clause9SmallBarnCyAcA3CatV_xtcAA8UngulateRzlufCAA0H0CyAHyxGx_qd__tcAA12DomesticatedRzAaFRd__lufCTV [override] // vtable thunk for Barn.__allocating_init(_:_:) dispatching to SmallBarn.__allocating_init(_:_:) +// CHECK-NEXT: #SmallBarn.deinit!deallocator: @$s45designated_init_inheritance_with_where_clause9SmallBarnCfD // SmallBarn.__deallocating_deinit +// CHECK-NEXT: } \ No newline at end of file diff --git a/test/SILGen/didset_oldvalue_not_accessed_silgen.swift b/test/SILGen/didset_oldvalue_not_accessed_silgen.swift index 380ad23108fd8..5a5e91a5df05a 100644 --- a/test/SILGen/didset_oldvalue_not_accessed_silgen.swift +++ b/test/SILGen/didset_oldvalue_not_accessed_silgen.swift @@ -7,7 +7,7 @@ class Foo { var value: T { // CHECK-LABEL: sil private [ossa] @$s35didset_oldvalue_not_accessed_silgen3FooC5valuexvW : $@convention(method) (@guaranteed Foo) -> () // CHECK: debug_value %0 : $Foo, let, name "self", argno {{[0-9]+}} - // CHECK-NOT: debug_value_addr %0 : $*T, let, name "oldValue", argno {{[0-9]+}} + // CHECK-NOT: debug_value %0 : $*T, let, name "oldValue", argno {{[0-9]+}}, {{.*}} expr op_deref didSet { print("didSet called!") } } @@ -20,7 +20,7 @@ let foo = Foo(value: "Hello") // Foo.value.setter // // CHECK-LABEL: sil hidden [ossa] @$s35didset_oldvalue_not_accessed_silgen3FooC5valuexvs : $@convention(method) (@in T, @guaranteed Foo) -> () -// CHECK: debug_value_addr [[VALUE:%.*]] : $*T, let, name "value", argno {{[0-9+]}} +// CHECK: debug_value [[VALUE:%.*]] : $*T, let, name "value", argno {{[0-9+]}}, {{.*}} expr op_deref // CHECK-NEXT: debug_value [[SELF:%.*]] : $Foo, let, name "self", argno {{[0-9+]}} // CHECK-NEXT: [[ALLOC_STACK:%.*]] = alloc_stack $T // CHECK-NEXT: copy_addr [[VALUE]] to [initialization] [[ALLOC_STACK]] : $*T diff --git a/test/SILGen/effectful_properties.swift b/test/SILGen/effectful_properties.swift index a5dd96908aa1f..c3848ed98aa09 100644 --- a/test/SILGen/effectful_properties.swift +++ b/test/SILGen/effectful_properties.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name accessors -swift-version 5 | %FileCheck --enable-var-scope %s +// RUN: %target-swift-frontend -emit-silgen -disable-availability-checking %s -module-name accessors -swift-version 5 | %FileCheck --enable-var-scope %s class C { // CHECK-DAG: sil hidden [ossa] @$s9accessors1CC16prop_asyncThrowsSivg : $@convention(method) @async (@guaranteed C) -> (Int, @error Error) { diff --git a/test/SILGen/enum.swift b/test/SILGen/enum.swift index 00b1b21cada77..5a9ca073cdb66 100644 --- a/test/SILGen/enum.swift +++ b/test/SILGen/enum.swift @@ -219,3 +219,23 @@ func sr7799_1(bar: SR7799??) { default: print("default") } } + +// Make sure that we handle enum, tuple initialization composed +// correctly. Previously, we leaked down a failure path due to us misusing +// scopes. +enum rdar81817725 { + case localAddress + case setOption(Int, Any) + + static func takeAny(_:Any) -> Bool { return true } + + static func testSwitchCleanup(syscall: rdar81817725, expectedLevel: Int, + valueMatcher: (Any) -> Bool) + throws -> Bool { + if case .setOption(expectedLevel, let value) = syscall { + return rdar81817725.takeAny(value) + } else { + return false + } + } +} diff --git a/test/SILGen/enum_raw_representable_objc.swift b/test/SILGen/enum_raw_representable_objc.swift index 20b6e59161c0a..292446d3d11ae 100644 --- a/test/SILGen/enum_raw_representable_objc.swift +++ b/test/SILGen/enum_raw_representable_objc.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-emit-silgen -emit-sorted-sil -enable-objc-interop -disable-objc-attr-requires-foundation-module %s | %FileCheck %s -// RUN: %target-swift-emit-silgen -emit-sorted-sil -enable-objc-interop -disable-objc-attr-requires-foundation-module -enable-library-evolution %s | %FileCheck -check-prefix=CHECK-RESILIENT %s +// RUN: %target-swift-emit-silgen -emit-sorted-sil %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -emit-sorted-sil -enable-library-evolution %s | %FileCheck -check-prefix=CHECK-RESILIENT %s #if os(Windows) && arch(x86_64) @objc public enum CLike: Int32 { diff --git a/test/SILGen/expressions.swift b/test/SILGen/expressions.swift index e37e4a85bfba6..5b556e8062259 100644 --- a/test/SILGen/expressions.swift +++ b/test/SILGen/expressions.swift @@ -545,7 +545,7 @@ func dontLoadIgnoredLValueForceUnwrap(_ a: inout NonTrivialStruct?) -> NonTrivia } // CHECK-LABEL: dontLoadIgnoredLValueForceUnwrap // CHECK: bb0(%0 : $*Optional): -// CHECK-NEXT: debug_value_addr %0 +// CHECK-NEXT: debug_value %0{{.*}} expr op_deref // CHECK-NEXT: [[READ:%[0-9]+]] = begin_access [read] [unknown] %0 // CHECK-NEXT: switch_enum_addr [[READ]] : $*Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1 // CHECK: bb1: @@ -561,7 +561,7 @@ func dontLoadIgnoredLValueDoubleForceUnwrap(_ a: inout NonTrivialStruct??) -> No } // CHECK-LABEL: dontLoadIgnoredLValueDoubleForceUnwrap // CHECK: bb0(%0 : $*Optional>): -// CHECK-NEXT: debug_value_addr %0 +// CHECK-NEXT: debug_value %0{{.*}} expr op_deref // CHECK-NEXT: [[READ:%[0-9]+]] = begin_access [read] [unknown] %0 // CHECK-NEXT: switch_enum_addr [[READ]] : $*Optional>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1 // CHECK: bb1: @@ -582,7 +582,7 @@ func loadIgnoredLValueForceUnwrap(_ a: inout NonTrivialStruct) -> NonTrivialStru } // CHECK-LABEL: loadIgnoredLValueForceUnwrap // CHECK: bb0(%0 : $*NonTrivialStruct): -// CHECK-NEXT: debug_value_addr %0 +// CHECK-NEXT: debug_value %0{{.*}} expr op_deref // CHECK-NEXT: [[READ:%[0-9]+]] = begin_access [read] [unknown] %0 // CHECK-NEXT: [[BORROW:%[0-9]+]] = load_borrow [[READ]] // CHECK-NEXT: // function_ref NonTrivialStruct.x.getter @@ -603,7 +603,7 @@ func loadIgnoredLValueThroughForceUnwrap(_ a: inout NonTrivialStruct?) -> NonTri } // CHECK-LABEL: loadIgnoredLValueThroughForceUnwrap // CHECK: bb0(%0 : $*Optional): -// CHECK-NEXT: debug_value_addr %0 +// CHECK-NEXT: debug_value %0{{.*}} expr op_deref // CHECK-NEXT: [[READ:%[0-9]+]] = begin_access [read] [unknown] %0 // CHECK-NEXT: switch_enum_addr [[READ]] : $*Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1 // CHECK: bb1: @@ -629,7 +629,7 @@ func evaluateIgnoredKeyPathExpr(_ s: inout NonTrivialStruct, _ kp: WritableKeyPa } // CHECK-LABEL: evaluateIgnoredKeyPathExpr // CHECK: bb0(%0 : $*NonTrivialStruct, %1 : @guaranteed $WritableKeyPath): -// CHECK-NEXT: debug_value_addr %0 +// CHECK-NEXT: debug_value %0{{.*}} expr op_deref // CHECK-NEXT: debug_value %1 // CHECK-NEXT: [[KP_TEMP:%[0-9]+]] = copy_value %1 // CHECK-NEXT: [[S_READ:%[0-9]+]] = begin_access [read] [unknown] %0 diff --git a/test/SILGen/foreach_async.swift b/test/SILGen/foreach_async.swift index 715d34fffcc5b..269fa9073e794 100644 --- a/test/SILGen/foreach_async.swift +++ b/test/SILGen/foreach_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-emit-silgen %s -module-name foreach_async -swift-version 5 -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-emit-silgen %s -module-name foreach_async -swift-version 5 -disable-availability-checking | %FileCheck %s // REQUIRES: concurrency ////////////////// diff --git a/test/SILGen/generic_tuples.swift b/test/SILGen/generic_tuples.swift index ccc306cebfaa6..76cafffd77381 100644 --- a/test/SILGen/generic_tuples.swift +++ b/test/SILGen/generic_tuples.swift @@ -5,7 +5,7 @@ func dup(_ x: T) -> (T, T) { return (x,x) } // CHECK-LABEL: sil hidden [ossa] @$s14generic_tuples3dup{{[_0-9a-zA-Z]*}}F // CHECK: ([[RESULT_0:%.*]] : $*T, [[RESULT_1:%.*]] : $*T, [[XVAR:%.*]] : $*T): -// CHECK-NEXT: debug_value_addr [[XVAR]] : $*T, let, name "x" +// CHECK-NEXT: debug_value [[XVAR]] : $*T, let, name "x", {{.*}} expr op_deref // CHECK-NEXT: copy_addr [[XVAR]] to [initialization] [[RESULT_0]] // CHECK-NEXT: copy_addr [[XVAR]] to [initialization] [[RESULT_1]] // CHECK-NEXT: [[T0:%.*]] = tuple () diff --git a/test/SILGen/global_actor_function_mangling.swift b/test/SILGen/global_actor_function_mangling.swift index 88901046d61d4..38d9203dd4b34 100644 --- a/test/SILGen/global_actor_function_mangling.swift +++ b/test/SILGen/global_actor_function_mangling.swift @@ -1,8 +1,8 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 | %FileCheck %s // REQUIRES: concurrency // Declarations don't mangle global actor types. -// CHECK: @$s4test10returnsOptyxycSgAClF +// CHECK: @$s4test10returnsOptyxycSgxyScMYccSglF func returnsOpt(_ fn: (@MainActor () -> R)?) -> (() -> R)? { typealias Fn = (() -> R)? return unsafeBitCast(fn, to: Fn.self) diff --git a/test/SILGen/guaranteed_closure_context.swift b/test/SILGen/guaranteed_closure_context.swift index 5cf3aa1391644..480f1d9fe46d4 100644 --- a/test/SILGen/guaranteed_closure_context.swift +++ b/test/SILGen/guaranteed_closure_context.swift @@ -63,7 +63,6 @@ func guaranteed_captures() { // CHECK-NOT: copy_value [[MUTABLE_RETAINABLE_BOX]] // CHECK-NOT: copy_value [[MUTABLE_ADDRESS_ONLY_BOX]] // CHECK-NOT: copy_value [[IMMUTABLE_RETAINABLE]] - // CHECK-NOT: destroy_value [[IMMUTABLE_AO_BOX]] escape(captureEverything) } diff --git a/test/SILGen/hop_to_executor.swift b/test/SILGen/hop_to_executor.swift index 62807eaa77d0a..f723f753c6c56 100644 --- a/test/SILGen/hop_to_executor.swift +++ b/test/SILGen/hop_to_executor.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency | %FileCheck --enable-var-scope %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -disable-availability-checking | %FileCheck --enable-var-scope %s --implicit-check-not 'hop_to_executor {{%[0-9]+}}' // REQUIRES: concurrency @@ -45,7 +45,14 @@ actor MyActor { await callee(p) } + ///// MyActor.testClosure() + // CHECK: sil hidden [ossa] @$s4test7MyActorC0A7ClosureSiyYaF : $@convention(method) @async (@guaranteed MyActor) -> Int { + // CHECK: hop_to_executor [[A:%[0-9]+]] : $MyActor + // CHECK: {{%[0-9]+}} = apply + // CHECK: hop_to_executor [[A]] : $MyActor + // CHECK: } // end sil function '$s4test7MyActorC0A7ClosureSiyYaF' + ///// closure #1 in MyActor.testClosure() // CHECK-LABEL: sil private [ossa] @$s4test7MyActorC0A7ClosureSiyYaFSiyYaXEfU_ : $@convention(thin) @async (@guaranteed MyActor) -> Int { // CHECK: [[COPIED_SELF:%[0-9]+]] = copy_value %0 : $MyActor // CHECK: [[BORROWED_SELF:%[0-9]+]] = begin_borrow [[COPIED_SELF]] : $MyActor @@ -86,6 +93,14 @@ struct GlobalActor { func testGlobalActor() async { } +///// testGlobalActorWithClosure() +// CHECK: sil hidden [ossa] @$s4test0A22GlobalActorWithClosureyyYaF : $@convention(thin) @async () -> () { +// CHECK: hop_to_executor [[A:%[0-9]+]] : $MyActor +// CHECK: {{%[0-9]+}} = apply +// CHECK: hop_to_executor [[A]] : $MyActor +// CHECK: // end sil function '$s4test0A22GlobalActorWithClosureyyYaF' + +///// closure #1 in testGlobalActorWithClosure() // CHECK-LABEL: sil private [ossa] @$s4test0A22GlobalActorWithClosureyyYaFyyYaXEfU_ : $@convention(thin) @async () -> () { // CHECK: [[F:%[0-9]+]] = function_ref @$s4test11GlobalActorV6sharedAA02MyC0Cvau : $@convention(thin) () -> Builtin.RawPointer // CHECK: [[P:%[0-9]+]] = apply [[F]]() : $@convention(thin) () -> Builtin.RawPointer @@ -100,23 +115,24 @@ func testGlobalActorWithClosure() async { await { () async in }() } +actor MyGenericActor {} + @globalActor struct GenericGlobalActorWithGetter { - static var shared: MyActor { return MyActor() } + static var shared: MyGenericActor { return MyGenericActor() } } // CHECK-LABEL: sil hidden [ossa] @$s4test0A28GenericGlobalActorWithGetteryyYaF : $@convention(thin) @async () -> () { // CHECK: [[MT:%[0-9]+]] = metatype $@thin GenericGlobalActorWithGetter.Type -// CHECK: [[F:%[0-9]+]] = function_ref @$s4test28GenericGlobalActorWithGetterV6sharedAA02MyD0CvgZ : $@convention(method) <τ_0_0> (@thin GenericGlobalActorWithGetter<τ_0_0>.Type) -> @owned MyActor -// CHECK: [[A:%[0-9]+]] = apply [[F]]([[MT]]) : $@convention(method) <τ_0_0> (@thin GenericGlobalActorWithGetter<τ_0_0>.Type) -> @owned MyActor -// CHECK: [[B:%[0-9]+]] = begin_borrow [[A]] : $MyActor -// CHECK: hop_to_executor [[B]] : $MyActor +// CHECK: [[F:%[0-9]+]] = function_ref @$s4test28GenericGlobalActorWithGetterV6sharedAA02MybD0CyxGvgZ +// CHECK: [[A:%[0-9]+]] = apply [[F]]([[MT]]) +// CHECK: [[B:%[0-9]+]] = begin_borrow [[A]] : $MyGenericActor +// CHECK: hop_to_executor [[B]] : $MyGenericActor // CHECK: } // end sil function '$s4test0A28GenericGlobalActorWithGetteryyYaF' @GenericGlobalActorWithGetter func testGenericGlobalActorWithGetter() async { } - actor RedActorImpl { // CHECK-LABEL: sil hidden [ossa] @$s4test12RedActorImplC5helloyySiF : $@convention(method) (Int, @guaranteed RedActorImpl) -> () { // CHECK-NOT: hop_to_executor @@ -128,14 +144,11 @@ actor BlueActorImpl { // CHECK-LABEL: sil hidden [ossa] @$s4test13BlueActorImplC4poke6personyAA03RedcD0C_tYaF : $@convention(method) @async (@guaranteed RedActorImpl, @guaranteed BlueActorImpl) -> () { // CHECK: bb0([[RED:%[0-9]+]] : @guaranteed $RedActorImpl, [[BLUE:%[0-9]+]] : @guaranteed $BlueActorImpl): // CHECK: hop_to_executor [[BLUE]] : $BlueActorImpl -// CHECK-NOT: hop_to_executor // CHECK: [[INTARG:%[0-9]+]] = apply {{%[0-9]+}}({{%[0-9]+}}, {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK-NOT: hop_to_executor -// CHECK: [[METH:%[0-9]+]] = class_method [[RED]] : $RedActorImpl, #RedActorImpl.hello : (RedActorImpl) -> (Int) -> (), $@convention(method) (Int, @guaranteed RedActorImpl) -> () +// CHECK: [[METH:%[0-9]+]] = class_method [[RED]] : $RedActorImpl, #RedActorImpl.hello : (isolated RedActorImpl) -> (Int) -> (), $@convention(method) (Int, @guaranteed RedActorImpl) -> () // CHECK: hop_to_executor [[RED]] : $RedActorImpl // CHECK-NEXT: {{%[0-9]+}} = apply [[METH]]([[INTARG]], [[RED]]) : $@convention(method) (Int, @guaranteed RedActorImpl) -> () // CHECK-NEXT: hop_to_executor [[BLUE]] : $BlueActorImpl -// CHECK-NOT: hop_to_executor // CHECK: } // end sil function '$s4test13BlueActorImplC4poke6personyAA03RedcD0C_tYaF' func poke(person red : RedActorImpl) async { await red.hello(42) @@ -147,7 +160,7 @@ actor BlueActorImpl { // CHECK: [[RED:%[0-9]+]] = apply {{%[0-9]+}}({{%[0-9]+}}) : $@convention(method) (@thick RedActorImpl.Type) -> @owned RedActorImpl // CHECK: [[REDBORROW:%[0-9]+]] = begin_borrow [[RED]] : $RedActorImpl // CHECK: [[INTARG:%[0-9]+]] = apply {{%[0-9]+}}({{%[0-9]+}}, {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: [[METH:%[0-9]+]] = class_method [[REDBORROW]] : $RedActorImpl, #RedActorImpl.hello : (RedActorImpl) -> (Int) -> (), $@convention(method) (Int, @guaranteed RedActorImpl) -> () +// CHECK: [[METH:%[0-9]+]] = class_method [[REDBORROW]] : $RedActorImpl, #RedActorImpl.hello : (isolated RedActorImpl) -> (Int) -> (), $@convention(method) (Int, @guaranteed RedActorImpl) -> () // CHECK: hop_to_executor [[REDBORROW]] : $RedActorImpl // CHECK-NEXT: = apply [[METH]]([[INTARG]], [[REDBORROW]]) : $@convention(method) (Int, @guaranteed RedActorImpl) -> () // CHECK-NEXT: hop_to_executor [[BLUE]] : $BlueActorImpl @@ -202,21 +215,18 @@ struct BlueActor { // CHECK: destroy_value [[R]] : $RedActorImpl // CHECK: end_borrow [[BLUEEXE]] : $BlueActorImpl // CHECK: destroy_value [[B]] : $BlueActorImpl -// CHECK-NOT: hop_to_executor // CHECK: } // end sil function '$s4test6blueFnyyYaF' @BlueActor func blueFn() async { await redFn(100) } // CHECK-LABEL: sil hidden [ossa] @$s4test20unspecifiedAsyncFuncyyYaF : $@convention(thin) @async () -> () { -// CHECK-NOT: hop_to_executor // CHECK: [[BORROW:%[0-9]+]] = begin_borrow {{%[0-9]+}} : $RedActorImpl // CHECK-NEXT: [[PREV_EXEC:%.*]] = builtin "getCurrentExecutor"() // CHECK-NEXT: hop_to_executor [[BORROW]] : $RedActorImpl // CHECK-NEXT: {{%[0-9]+}} = apply {{%[0-9]+}}({{%[0-9]+}}) : $@convention(thin) (Int) -> () // CHECK-NEXT: hop_to_executor [[PREV_EXEC]] // CHECK-NEXT: end_borrow [[BORROW]] : $RedActorImpl -// CHECK-NOT: hop_to_executor // CHECK: } // end sil function '$s4test20unspecifiedAsyncFuncyyYaF' func unspecifiedAsyncFunc() async { await redFn(200) @@ -224,10 +234,8 @@ func unspecifiedAsyncFunc() async { // CHECK-LABEL: sil hidden [ossa] @$s4test27anotherUnspecifiedAsyncFuncyyAA12RedActorImplCYaF : $@convention(thin) @async (@guaranteed RedActorImpl) -> () { // CHECK: bb0([[RED:%[0-9]+]] : @guaranteed $RedActorImpl): -// CHECK-NOT: hop_to_executor // CHECK: [[INTARG:%[0-9]+]] = apply {{%[0-9]+}}({{%[0-9]+}}, {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK-NOT: hop_to_executor -// CHECK: [[METH:%[0-9]+]] = class_method [[RED]] : $RedActorImpl, #RedActorImpl.hello : (RedActorImpl) -> (Int) -> (), $@convention(method) (Int, @guaranteed RedActorImpl) -> () +// CHECK: [[METH:%[0-9]+]] = class_method [[RED]] : $RedActorImpl, #RedActorImpl.hello : (isolated RedActorImpl) -> (Int) -> (), $@convention(method) (Int, @guaranteed RedActorImpl) -> () // CHECK-NEXT: [[PREV_EXEC:%.*]] = builtin "getCurrentExecutor"() // CHECK-NEXT: hop_to_executor [[RED]] : $RedActorImpl // CHECK-NEXT: {{%[0-9]+}} = apply [[METH]]([[INTARG]], [[RED]]) : $@convention(method) (Int, @guaranteed RedActorImpl) -> () @@ -237,7 +245,7 @@ func anotherUnspecifiedAsyncFunc(_ red : RedActorImpl) async { await red.hello(12); } -// CHECK-LABEL: sil hidden [ossa] @$s4test0A20GlobalActorFuncValueyyyyXEYaF +// CHECK-LABEL: sil hidden [ossa] @$s4test0A20GlobalActorFuncValueyyyyAA03RedC0VYcXEYaF // CHECK: function_ref @$s4test8RedActorV6sharedAA0bC4ImplCvgZ // CHECK: hop_to_executor [[RED:%[0-9]+]] : $RedActorImpl // CHECK-NEXT: apply @@ -262,3 +270,122 @@ extension MyActor { } } } + +func acceptAsyncClosure(_: () async -> Void) { } +func acceptAsyncClosure2(_: (T) async -> T) { } + +public actor MyPublicActor {} + +@globalActor +public struct PublicGlobalActor { + public static var shared: MyPublicActor = MyPublicActor() +} + +@globalActor +private struct PrivateGlobalActor { + static var shared: MyActor = MyActor() +} + +func testGlobalActorClosure() { + // CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] [ossa] @$sIeg_IegH_TR4test11GlobalActorVTU + // CHECK: [[OLD_EXEC:%.*]] = builtin "getCurrentExecutor" + // CHECK: hop_to_executor {{%.*}} : $MyActor + // CHECK: apply %0() + // CHECK: hop_to_executor [[OLD_EXEC]] + let ga: @GlobalActor () -> () = { @GlobalActor in print(5) } + acceptAsyncClosure(ga) + + // CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] [ossa] @$sIeg_IegH_TR4test8RedActorVTU + // CHECK: [[OLD_EXEC:%.*]] = builtin "getCurrentExecutor" + // CHECK: hop_to_executor {{%.*}} : $RedActor + // CHECK: apply %0() + // CHECK: hop_to_executor [[OLD_EXEC]] + let ra: @RedActor () -> () = { @RedActor in print(5) } + acceptAsyncClosure(ra) + + // CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] [ossa] @$sIeg_IegH_TR4test9BlueActorVTU + // CHECK: [[OLD_EXEC:%.*]] = builtin "getCurrentExecutor" + // CHECK: hop_to_executor {{%.*}} : $BlueActor + // CHECK: apply %0() + // CHECK: hop_to_executor [[OLD_EXEC]] + let ba: @BlueActor () -> () = { @BlueActor in print(5) } + acceptAsyncClosure(ba) + + // CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] [ossa] @$sIeg_IegH_TR4test18PrivateGlobalActor{{.*}}VTU + // CHECK: [[OLD_EXEC:%.*]] = builtin "getCurrentExecutor" + // CHECK: hop_to_executor {{%.*}} : $MyActor + // CHECK: apply %0() + // CHECK: hop_to_executor [[OLD_EXEC]] + let pga: @PrivateGlobalActor () -> () = { @PrivateGlobalActor in print(5) } + acceptAsyncClosure(pga) + + // CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] [ossa] @$sIeg_IegH_TR4test17PublicGlobalActorVTU + // CHECK: [[OLD_EXEC:%.*]] = builtin "getCurrentExecutor" + // CHECK: hop_to_executor {{%.*}} : $MyPublicActor + // CHECK: apply %0() + // CHECK: hop_to_executor [[OLD_EXEC]] + let pbga: @PublicGlobalActor () -> () = { @PublicGlobalActor in print(5) } + acceptAsyncClosure(pbga) + + // CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] [ossa] @$sIeg_IegH_TR4test28GenericGlobalActorWithGetterVySiGTU + // CHECK: [[OLD_EXEC:%.*]] = builtin "getCurrentExecutor" + // CHECK: hop_to_executor {{%.*}} : $MyGenericActor + // CHECK: apply %0() + // CHECK: hop_to_executor [[OLD_EXEC]] + let ggai: @GenericGlobalActorWithGetter () -> () + = { @GenericGlobalActorWithGetter in print(5) } + acceptAsyncClosure(ggai) + + // CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] [ossa] @$sIeg_IegH_TR4test28GenericGlobalActorWithGetterVySSGTU + // CHECK: [[OLD_EXEC:%.*]] = builtin "getCurrentExecutor" + // CHECK: hop_to_executor {{%.*}} : $MyGenericActor + // CHECK: apply %0() + // CHECK: hop_to_executor [[OLD_EXEC]] + let ggas: @GenericGlobalActorWithGetter () -> () + = { @GenericGlobalActorWithGetter in print(5) } + acceptAsyncClosure(ggas) +} + +func testGenericGlobalActorClosure(_: T) { + // CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] [ossa] @$sxxIegnr_xxIegHnr_lTR4test28GenericGlobalActorWithGetterVyxGTU + // CHECK: [[OLD_EXEC:%.*]] = builtin "getCurrentExecutor" + // CHECK: hop_to_executor {{%.*}} : $MyGenericActor + // CHECK: apply %2(%0, %1) + // CHECK: hop_to_executor [[OLD_EXEC]] + let ggat: @GenericGlobalActorWithGetter (T) -> T + = { @GenericGlobalActorWithGetter x in x } + acceptAsyncClosure2(ggat) +} + +func acceptIsolatedParam(_: Int, _: isolated MyActor, _: Double) { } + +extension MyActor { + nonisolated func otherIsolated(_: Int, _: isolated MyActor, _: Double) { } +} + +// CHECK: sil hidden [ossa] @$s4test0A26ImplicitAsyncIsolatedParam1i1d5actor10otherActorySi_SdAA02MyH0CAHtYaF +func testImplicitAsyncIsolatedParam( + i: Int, d: Double, actor: MyActor, otherActor: MyActor +) async { + // CHECK: [[FN1:%.*]] = function_ref @$s4test19acceptIsolatedParamyySi_AA7MyActorCYiSdtF + // CHECK-NEXT: [[CURRENT:%.*]] = builtin "getCurrentExecutor"() : $Optional + // CHECK-NEXT: hop_to_executor %2 : $MyActor + // CHECK-NEXT: apply [[FN1]](%0, %2, %1) : $@convention(thin) (Int, @guaranteed MyActor, Double) -> () + // CHECK-NEXT: hop_to_executor [[CURRENT]] : $Optional + await acceptIsolatedParam(i, actor, d) + + // CHECK: [[FN2:%.*]] = function_ref @$s4test7MyActorC13otherIsolatedyySi_ACYiSdtF : $@convention(method) (Int, @guaranteed MyActor, Double, @guaranteed MyActor) -> () + // CHECK-NEXT: [[CURRENT:%.*]] = builtin "getCurrentExecutor"() : $Optional + // CHECK-NEXT: hop_to_executor %2 : $MyActor + // CHECK-NEXT: apply [[FN2]](%0, %2, %1, %3) : $@convention(method) (Int, @guaranteed MyActor, Double, @guaranteed MyActor) -> () + // CHECK-NEXT: hop_to_executor [[CURRENT]] : $Optional + await otherActor.otherIsolated(i, actor, d) +} + +// CHECK-LABEL: sil hidden [ossa] @$s4test22asyncWithIsolatedParam1i5actor1dySi_AA7MyActorCYiSdtYaF : $@convention(thin) @async (Int, @guaranteed MyActor, Double) -> () +func asyncWithIsolatedParam(i: Int, actor: isolated MyActor, d: Double) async { + // CHECK: [[ACTOR:%.*]] = copy_value %1 : $MyActor + // CHECK-NEXT: [[BORROWED:%.*]] = begin_borrow [[ACTOR]] : $MyActor + // CHECK-NEXT: hop_to_executor [[BORROWED]] : $MyActor + // CHECK-NEXT: end_borrow [[BORROWED]] : $MyActor +} diff --git a/test/SILGen/hop_to_executor_async_prop.swift b/test/SILGen/hop_to_executor_async_prop.swift index 4b4e3e6c9e998..f38e0880219ac 100644 --- a/test/SILGen/hop_to_executor_async_prop.swift +++ b/test/SILGen/hop_to_executor_async_prop.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency | %FileCheck --enable-var-scope %s +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -disable-availability-checking | %FileCheck --enable-var-scope %s --implicit-check-not 'hop_to_executor {{%[0-9]+}}' // REQUIRES: concurrency @propertyWrapper @@ -83,7 +83,7 @@ struct GlobalCat { // CHECK: bb0([[CAT:%[0-9]+]] : @guaranteed $Cat): // CHECK: [[PREV_EXEC:%.*]] = builtin "getCurrentExecutor" // CHECK: hop_to_executor [[CAT]] : $Cat -// CHECK: [[CAT_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.computedSweater!getter : (Cat) -> () -> Sweater, $@convention(method) (@guaranteed Cat) -> @owned Sweater +// CHECK: [[CAT_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.computedSweater!getter : (isolated Cat) -> () -> Sweater, $@convention(method) (@guaranteed Cat) -> @owned Sweater // CHECK: [[SWEATER1_REF:%[0-9]+]] = apply [[CAT_GETTER]]([[CAT]]) : $@convention(method) (@guaranteed Cat) -> @owned Sweater // CHECK: hop_to_executor [[PREV_EXEC]] // CHECK: [[SWEATER1:%[0-9]+]] = begin_borrow [[SWEATER1_REF]] : $Sweater @@ -96,7 +96,7 @@ struct GlobalCat { // CHECK: [[PREV_EXEC:%.*]] = builtin "getCurrentExecutor" // CHECK: hop_to_executor [[CAT2]] : $Cat // CHECK: [[CAT2_FOR_LOAD:%[0-9]+]] = begin_borrow [[CAT2_REF]] : $Cat -// CHECK: [[CAT2_GETTER:%[0-9]+]] = class_method [[CAT2_FOR_LOAD]] : $Cat, #Cat.computedSweater!getter : (Cat) -> () -> Sweater, $@convention(method) (@guaranteed Cat) -> @owned Sweater +// CHECK: [[CAT2_GETTER:%[0-9]+]] = class_method [[CAT2_FOR_LOAD]] : $Cat, #Cat.computedSweater!getter : (isolated Cat) -> () -> Sweater, $@convention(method) (@guaranteed Cat) -> @owned Sweater // CHECK: [[SWEATER2_OWNER:%[0-9]+]] = apply [[CAT2_GETTER]]([[CAT2_FOR_LOAD]]) : $@convention(method) (@guaranteed Cat) -> @owned Sweater // CHECK: end_borrow [[CAT2_FOR_LOAD]] : $Cat // CHECK: end_borrow [[CAT2]] : $Cat @@ -147,9 +147,9 @@ actor Dog { // CHECK: [[GLOBAL_BOOL_ACCESS:%[0-9]+]] = begin_access [read] [dynamic] [[GLOBAL_BOOL_ADDR]] : $*Bool // CHECK: [[THE_BOOL:%[0-9]+]] = load [trivial] [[GLOBAL_BOOL_ACCESS]] : $*Bool // CHECK: end_access [[GLOBAL_BOOL_ACCESS]] : $*Bool - // CHECK: end_borrow [[BORROWED_CAT]] : $Cat // CHECK: hop_to_executor [[SELF]] : $Dog + // CHECK: end_borrow [[BORROWED_CAT]] : $Cat // CHECK: destroy_value [[CAT]] : $Cat // CHECK: return [[THE_BOOL]] : $Bool // CHECK: } // end sil function '$s4test3DogC15accessGlobalVarSbyYaF' @@ -176,7 +176,7 @@ actor Dog { // CHECK: [[BORROWED_BIRB:%[0-9]+]] = begin_borrow [[BIRB]] : $Birb // CHECK: hop_to_executor [[BORROWED_BIRB]] : $Birb // CHECK: [[BORROWED_BIRB_FOR_LOAD:%[0-9]+]] = begin_borrow [[BIRB]] : $Birb - // CHECK: [[FEATHER_GETTER:%[0-9]+]] = class_method [[BORROWED_BIRB_FOR_LOAD]] : $Birb, #Birb.feathers!getter : (Birb) -> () -> Int, $@convention(method) (@guaranteed Birb) -> Int + // CHECK: [[FEATHER_GETTER:%[0-9]+]] = class_method [[BORROWED_BIRB_FOR_LOAD]] : $Birb, #Birb.feathers!getter : (isolated Birb) -> () -> Int, $@convention(method) (@guaranteed Birb) -> Int // CHECK: [[THE_INT:%[0-9]+]] = apply [[FEATHER_GETTER]]([[BORROWED_BIRB_FOR_LOAD]]) : $@convention(method) (@guaranteed Birb) -> Int // CHECK: end_borrow [[BORROWED_BIRB_FOR_LOAD]] : $Birb // CHECK: end_borrow [[BORROWED_BIRB]] : $Birb @@ -195,13 +195,13 @@ actor Dog { // CHECK: bb0([[CAT:%[0-9]+]] : @guaranteed $Cat, [[SELF:%[0-9]+]] : @guaranteed $Dog): // CHECK: hop_to_executor [[SELF]] : $Dog // CHECK: hop_to_executor [[CAT]] : $Cat - // CHECK: [[CAT_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.bestFriend!getter : (Cat) -> () -> Birb, $@convention(method) (@guaranteed Cat) -> @owned Birb + // CHECK: [[CAT_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.bestFriend!getter : (isolated Cat) -> () -> Birb, $@convention(method) (@guaranteed Cat) -> @owned Birb // CHECK: [[BIRB_REF:%[0-9]+]] = apply [[CAT_GETTER]]([[CAT]]) : $@convention(method) (@guaranteed Cat) -> @owned Birb // CHECK: hop_to_executor [[SELF]] : $Dog // CHECK: [[BIRB:%[0-9]+]] = begin_borrow [[BIRB_REF]] : $Birb // CHECK: hop_to_executor [[BIRB]] : $Birb // CHECK: [[BIRB_FOR_LOAD:%[0-9]+]] = begin_borrow [[BIRB_REF]] : $Birb - // CHECK: [[BIRB_GETTER:%[0-9]+]] = class_method [[BIRB_FOR_LOAD]] : $Birb, #Birb.feathers!getter : (Birb) -> () -> Int, $@convention(method) (@guaranteed Birb) -> Int + // CHECK: [[BIRB_GETTER:%[0-9]+]] = class_method [[BIRB_FOR_LOAD]] : $Birb, #Birb.feathers!getter : (isolated Birb) -> () -> Int, $@convention(method) (@guaranteed Birb) -> Int // CHECK: [[THE_INT:%[0-9]+]] = apply [[BIRB_GETTER]]([[BIRB_FOR_LOAD]]) : $@convention(method) (@guaranteed Birb) -> Int // CHECK: end_borrow [[BIRB_FOR_LOAD]] : $Birb // CHECK: end_borrow [[BIRB]] : $Birb @@ -222,7 +222,7 @@ actor Dog { // CHECK: hop_to_executor [[CAT_BORROW_FOR_HOP]] : $Cat // CHECK: [[CAT_BORROW_FOR_LOAD:%[0-9]+]] = begin_borrow [[CAT_REF]] : $Cat - // CHECK: [[GETTER:%[0-9]+]] = class_method [[CAT_BORROW_FOR_LOAD]] : $Cat, #Cat.storedBool!getter : (Cat) -> () -> Bool, $@convention(method) (@guaranteed Cat) -> Bool + // CHECK: [[GETTER:%[0-9]+]] = class_method [[CAT_BORROW_FOR_LOAD]] : $Cat, #Cat.storedBool!getter : (isolated Cat) -> () -> Bool, $@convention(method) (@guaranteed Cat) -> Bool // CHECK: [[THE_BOOL:%[0-9]+]] = apply [[GETTER]]([[CAT_BORROW_FOR_LOAD]]) : $@convention(method) (@guaranteed Cat) -> Bool // CHECK: end_borrow [[CAT_BORROW_FOR_LOAD]] : $Cat // CHECK: end_borrow [[CAT_BORROW_FOR_HOP]] : $Cat @@ -244,7 +244,7 @@ actor Dog { // CHECK: hop_to_executor [[CAT_BORROW_FOR_HOP]] : $Cat // CHECK: [[CAT_BORROW_FOR_LOAD:%[0-9]+]] = begin_borrow [[CAT_REF]] : $Cat - // CHECK: [[FRIEND_GETTER:%[0-9]+]] = class_method [[CAT_BORROW_FOR_LOAD]] : $Cat, #Cat.friend!getter : (Cat) -> () -> Cat, $@convention(method) (@guaranteed Cat) -> @owned Cat + // CHECK: [[FRIEND_GETTER:%[0-9]+]] = class_method [[CAT_BORROW_FOR_LOAD]] : $Cat, #Cat.friend!getter : (isolated Cat) -> () -> Cat, $@convention(method) (@guaranteed Cat) -> @owned Cat // CHECK: [[FRIEND_REF:%[0-9]+]] = apply [[FRIEND_GETTER]]([[CAT_BORROW_FOR_LOAD]]) : $@convention(method) (@guaranteed Cat) -> @owned Cat // CHECK: end_borrow [[CAT_BORROW_FOR_LOAD]] : $Cat // CHECK: end_borrow [[CAT_BORROW_FOR_HOP]] : $Cat @@ -255,7 +255,7 @@ actor Dog { // CHECK: hop_to_executor [[FRIEND_BORROW_FOR_HOP]] : $Cat // CHECK: [[FRIEND_BORROW_FOR_LOAD:%[0-9]+]] = begin_borrow [[FRIEND_REF]] : $Cat - // CHECK: [[BOOL_GETTER:%[0-9]+]] = class_method [[FRIEND_BORROW_FOR_LOAD]] : $Cat, #Cat.storedBool!getter : (Cat) -> () -> Bool, $@convention(method) (@guaranteed Cat) -> Bool + // CHECK: [[BOOL_GETTER:%[0-9]+]] = class_method [[FRIEND_BORROW_FOR_LOAD]] : $Cat, #Cat.storedBool!getter : (isolated Cat) -> () -> Bool, $@convention(method) (@guaranteed Cat) -> Bool // CHECK: [[THE_BOOL:%[0-9]+]] = apply [[BOOL_GETTER]]([[FRIEND_BORROW_FOR_LOAD]]) : $@convention(method) (@guaranteed Cat) -> Bool // CHECK: end_borrow [[FRIEND_BORROW_FOR_LOAD]] : $Cat // CHECK: end_borrow [[FRIEND_BORROW_FOR_HOP]] : $Cat @@ -275,7 +275,7 @@ actor Dog { // CHECK: [[INTEGER1:%[0-9]+]] = apply {{%[0-9]+}}({{%[0-9]+}}, {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int // CHECK: hop_to_executor [[CAT]] : $Cat - // CHECK: [[SUBSCRIPT_FN:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.subscript!getter : (Cat) -> (Int) -> Cat, $@convention(method) (Int, @guaranteed Cat) -> @owned Cat + // CHECK: [[SUBSCRIPT_FN:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.subscript!getter : (isolated Cat) -> (Int) -> Cat, $@convention(method) (Int, @guaranteed Cat) -> @owned Cat // CHECK: [[OTHER_CAT:%[0-9]+]] = apply [[SUBSCRIPT_FN]]([[INTEGER1]], [[CAT]]) : $@convention(method) (Int, @guaranteed Cat) -> @owned Cat // CHECK: hop_to_executor [[DOG]] : $Dog @@ -294,7 +294,7 @@ actor Dog { // CHECK: hop_to_executor [[RVALUE_CAT]] : $Cat // CHECK: [[RVALUE_CAT_FOR_LOAD:%[0-9]+]] = begin_borrow [[RVALUE_CAT_REF]] : $Cat - // CHECK: [[RVALUE_CAT_SUBSCRIPT:%[0-9]+]] = class_method [[RVALUE_CAT_FOR_LOAD]] : $Cat, #Cat.subscript!getter : (Cat) -> (Int) -> Cat, $@convention(method) (Int, @guaranteed Cat) -> @owned Cat + // CHECK: [[RVALUE_CAT_SUBSCRIPT:%[0-9]+]] = class_method [[RVALUE_CAT_FOR_LOAD]] : $Cat, #Cat.subscript!getter : (isolated Cat) -> (Int) -> Cat, $@convention(method) (Int, @guaranteed Cat) -> @owned Cat // CHECK: [[FIRST_CAT_REF:%[0-9]+]] = apply [[RVALUE_CAT_SUBSCRIPT]]([[INT_ONE]], [[RVALUE_CAT_FOR_LOAD]]) : $@convention(method) (Int, @guaranteed Cat) -> @owned Cat // CHECK: end_borrow [[RVALUE_CAT_FOR_LOAD]] : $Cat // CHECK: end_borrow [[RVALUE_CAT]] : $Cat @@ -307,7 +307,7 @@ actor Dog { // CHECK: hop_to_executor [[FIRST_CAT]] : $Cat // CHECK: [[FIRST_CAT_FOR_LOAD:%[0-9]+]] = begin_borrow [[FIRST_CAT_REF]] : $Cat - // CHECK: [[FIRST_CAT_SUBSCRIPT:%[0-9]+]] = class_method [[FIRST_CAT_FOR_LOAD]] : $Cat, #Cat.subscript!getter : (Cat) -> (Int) -> Cat, $@convention(method) (Int, @guaranteed Cat) -> @owned Cat + // CHECK: [[FIRST_CAT_SUBSCRIPT:%[0-9]+]] = class_method [[FIRST_CAT_FOR_LOAD]] : $Cat, #Cat.subscript!getter : (isolated Cat) -> (Int) -> Cat, $@convention(method) (Int, @guaranteed Cat) -> @owned Cat // CHECK: [[SECOND_CAT_REF:%[0-9]+]] = apply [[FIRST_CAT_SUBSCRIPT]]([[INT_TWO]], [[FIRST_CAT_FOR_LOAD]]) : $@convention(method) (Int, @guaranteed Cat) -> @owned Cat // CHECK: end_borrow [[FIRST_CAT_FOR_LOAD]] : $Cat // CHECK: end_borrow [[FIRST_CAT]] : $Cat @@ -330,7 +330,7 @@ actor Dog { // CHECK: hop_to_executor [[CAT]] : $Cat // CHECK: [[CAT_FOR_LOAD:%[0-9]+]] = begin_borrow [[CAT_REF:%[0-9]+]] : $Cat - // CHECK: [[GETTER:%[0-9]+]] = class_method [[CAT_FOR_LOAD]] : $Cat, #Cat.storedBool!getter : (Cat) -> () -> Bool, $@convention(method) (@guaranteed Cat) -> Bool + // CHECK: [[GETTER:%[0-9]+]] = class_method [[CAT_FOR_LOAD]] : $Cat, #Cat.storedBool!getter : (isolated Cat) -> () -> Bool, $@convention(method) (@guaranteed Cat) -> Bool // CHECK: [[THE_BOOL:%[0-9]+]] = apply [[GETTER]]([[CAT_FOR_LOAD]]) : $@convention(method) (@guaranteed Cat) -> Bool // CHECK: end_borrow [[CAT_FOR_LOAD]] : $Cat // CHECK: end_borrow [[CAT]] : $Cat @@ -348,7 +348,7 @@ actor Dog { // CHECK: hop_to_executor [[SELF]] : $Dog // CHECK: hop_to_executor [[CAT]] : $Cat - // CHECK: [[CAT_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.computedSweater!getter : (Cat) -> () -> Sweater, $@convention(method) (@guaranteed Cat) -> @owned Sweater + // CHECK: [[CAT_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.computedSweater!getter : (isolated Cat) -> () -> Sweater, $@convention(method) (@guaranteed Cat) -> @owned Sweater // CHECK: [[SWEATER1_REF:%[0-9]+]] = apply [[CAT_GETTER]]([[CAT]]) : $@convention(method) (@guaranteed Cat) -> @owned Sweater // CHECK: hop_to_executor [[SELF]] : $Dog @@ -361,7 +361,7 @@ actor Dog { // CHECK: hop_to_executor [[CAT2]] : $Cat // CHECK: [[CAT2_FOR_LOAD:%[0-9]+]] = begin_borrow [[CAT2_REF]] : $Cat - // CHECK: [[CAT2_GETTER:%[0-9]+]] = class_method [[CAT2_FOR_LOAD]] : $Cat, #Cat.computedSweater!getter : (Cat) -> () -> Sweater, $@convention(method) (@guaranteed Cat) -> @owned Sweater + // CHECK: [[CAT2_GETTER:%[0-9]+]] = class_method [[CAT2_FOR_LOAD]] : $Cat, #Cat.computedSweater!getter : (isolated Cat) -> () -> Sweater, $@convention(method) (@guaranteed Cat) -> @owned Sweater // CHECK: [[SWEATER2_OWNER:%[0-9]+]] = apply [[CAT2_GETTER]]([[CAT2_FOR_LOAD]]) : $@convention(method) (@guaranteed Cat) -> @owned Sweater // CHECK: end_borrow [[CAT2_FOR_LOAD]] : $Cat // CHECK: end_borrow [[CAT2]] : $Cat @@ -380,13 +380,13 @@ actor Dog { // CHECK: bb0([[CAT:%[0-9]+]] : @guaranteed $Cat, [[SELF:%[0-9]+]] : @guaranteed $Dog): // CHECK: hop_to_executor [[SELF]] : $Dog // CHECK: hop_to_executor [[CAT]] : $Cat - // CHECK: [[CAT_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.friend!getter : (Cat) -> () -> Cat, $@convention(method) (@guaranteed Cat) -> @owned Cat + // CHECK: [[CAT_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.friend!getter : (isolated Cat) -> () -> Cat, $@convention(method) (@guaranteed Cat) -> @owned Cat // CHECK: [[FRIEND1_REF:%[0-9]+]] = apply [[CAT_GETTER]]([[CAT]]) : $@convention(method) (@guaranteed Cat) -> @owned Cat // CHECK: hop_to_executor [[SELF]] : $Dog // CHECK: [[FRIEND1:%[0-9]+]] = begin_borrow [[FRIEND1_REF]] : $Cat // CHECK: hop_to_executor [[FRIEND1]] : $Cat // CHECK: [[FRIEND1_FOR_LOAD:%[0-9]+]] = begin_borrow [[FRIEND1_REF]] : $Cat - // CHECK: [[FRIEND1_GETTER:%[0-9]+]] = class_method [[FRIEND1_FOR_LOAD]] : $Cat, #Cat.friend!getter : (Cat) -> () -> Cat, $@convention(method) (@guaranteed Cat) -> @owned Cat + // CHECK: [[FRIEND1_GETTER:%[0-9]+]] = class_method [[FRIEND1_FOR_LOAD]] : $Cat, #Cat.friend!getter : (isolated Cat) -> () -> Cat, $@convention(method) (@guaranteed Cat) -> @owned Cat // CHECK: [[FRIEND2_REF:%[0-9]+]] = apply [[FRIEND1_GETTER]]([[FRIEND1_FOR_LOAD]]) : $@convention(method) (@guaranteed Cat) -> @owned Cat // CHECK: end_borrow [[FRIEND1_FOR_LOAD]] : $Cat // CHECK: end_borrow [[FRIEND1]] : $Cat @@ -405,7 +405,7 @@ actor Dog { // CHECK: [[FRIEND1_STACK:%[0-9]+]] = alloc_stack $Optional // CHECK: hop_to_executor [[CAT]] : $Cat - // CHECK: [[MAYBE_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.maybeFriend!getter : (Cat) -> () -> Cat?, $@convention(method) (@guaranteed Cat) -> @owned Optional + // CHECK: [[MAYBE_GETTER:%[0-9]+]] = class_method [[CAT]] : $Cat, #Cat.maybeFriend!getter : (isolated Cat) -> () -> Cat?, $@convention(method) (@guaranteed Cat) -> @owned Optional // CHECK: [[MAYBE_FRIEND:%[0-9]+]] = apply [[MAYBE_GETTER]]([[CAT]]) : $@convention(method) (@guaranteed Cat) -> @owned Optional // CHECK: store [[MAYBE_FRIEND]] to [init] [[FRIEND1_STACK]] : $*Optional @@ -420,7 +420,7 @@ actor Dog { // CHECK: [[FRIEND1:%[0-9]+]] = begin_borrow [[FRIEND1_REF]] : $Cat // CHECK: hop_to_executor [[FRIEND1]] : $Cat // CHECK: [[FRIEND1_FOR_LOAD:%[0-9]+]] = begin_borrow [[FRIEND1_REF]] : $Cat - // CHECK: [[FRIEND1_GETTER:%[0-9]+]] = class_method [[FRIEND1_FOR_LOAD]] : $Cat, #Cat.friend!getter : (Cat) -> () -> Cat, $@convention(method) (@guaranteed Cat) -> @owned Cat + // CHECK: [[FRIEND1_GETTER:%[0-9]+]] = class_method [[FRIEND1_FOR_LOAD]] : $Cat, #Cat.friend!getter : (isolated Cat) -> () -> Cat, $@convention(method) (@guaranteed Cat) -> @owned Cat // CHECK: [[FRIEND2_REF:%[0-9]+]] = apply [[FRIEND1_GETTER]]([[FRIEND1_FOR_LOAD]]) : $@convention(method) (@guaranteed Cat) -> @owned Cat // CHECK: end_borrow [[FRIEND1_FOR_LOAD]] : $Cat // CHECK: end_borrow [[FRIEND1]] : $Cat @@ -437,3 +437,210 @@ actor Dog { } } // END OF DOG ACTOR +class Point { + var pt: (Int, Int) = (0, 0) +} + +@MainActor +var globalCircle: ((Int, Int)?, Float) = (nil, 1.1) + +struct Container { + @MainActor static var counter: Int = 10 + @MainActor static var this: Container? + @MainActor static var staticCircle: ((Int, Int)?, Float) = (nil, 2.1) + + var noniso: Int = 20 + + @GlobalCat var iso: Float = 1.0 + @GlobalCat var isoRef: CatBox = CatBox() + + // CHECK-LABEL: sil hidden [ossa] @$s4test9ContainerV12accessTuple1SfyYaF : $@convention(method) @async (@guaranteed Container) -> Float { + // CHECK: hop_to_executor {{%[0-9]+}} : $MainActor + // CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [dynamic] {{%[0-9]+}} : $*(Optional<(Int, Int)>, Float) + // CHECK: [[ADDR:%[0-9]+]] = tuple_element_addr [[ACCESS]] : $*(Optional<(Int, Int)>, Float), 1 + // CHECK: {{%[0-9]+}} = load [trivial] [[ADDR]] : $*Float + // CHECK: end_access [[ACCESS]] : $*(Optional<(Int, Int)>, Float) + // CHECK: hop_to_executor {{%[0-9]+}} : $Optional + // CHECK: } // end sil function '$s4test9ContainerV12accessTuple1SfyYaF' + func accessTuple1() async -> Float { + return await globalCircle.1 + } + + // CHECK-LABEL: sil hidden [ossa] @$s4test9ContainerV12accessTuple2SiSgyYaFZ : $@convention(method) @async (@thin Container.Type) -> Optional { + // CHECK: hop_to_executor {{%[0-9]+}} : $MainActor + // CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [dynamic] {{%[0-9]+}} : $*(Optional<(Int, Int)>, Float) + // CHECK: [[ADDR:%[0-9]+]] = tuple_element_addr [[ACCESS]] : $*(Optional<(Int, Int)>, Float), 0 + // CHECK: switch_enum_addr [[SCRUTINEE:%[0-9]+]] : $*Optional<(Int, Int)>, case #Optional.some!enumelt: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[CRASH_BB:bb[0-9]+]] + + // CHECK: [[CRASH_BB]]: + // CHECK-NOT: hop_to_executor {{%[0-9]+}} + // CHECK: unreachable + + // CHECK: [[SOME_BB]]: + // CHECK: [[TUPLE_ADDR:%[0-9]+]] = unchecked_take_enum_data_addr [[SCRUTINEE]] : $*Optional<(Int, Int)>, #Optional.some!enumelt + // CHECK: [[ELM_ADDR:%[0-9]+]] = tuple_element_addr [[TUPLE_ADDR]] : $*(Int, Int), 0 + // CHECK: {{%[0-9]+}} = load [trivial] [[ELM_ADDR]] : $*Int + // CHECK: end_access [[ACCESS]] : $*(Optional<(Int, Int)>, Float) + // CHECK: hop_to_executor {{%[0-9]+}} : $Optional + // CHECK: } // end sil function '$s4test9ContainerV12accessTuple2SiSgyYaFZ' + static func accessTuple2() async -> Int? { + return await globalCircle.0!.0 + } + + // CHECK-LABEL: sil hidden [ossa] @$s4test9ContainerV12accessTuple3SfyYaF : $@convention(method) @async (@guaranteed Container) -> Float { + // CHECK: hop_to_executor {{%[0-9]+}} : $MainActor + // CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [dynamic] {{%[0-9]+}} : $*(Optional<(Int, Int)>, Float) + // CHECK: [[ADDR:%[0-9]+]] = tuple_element_addr [[ACCESS]] : $*(Optional<(Int, Int)>, Float), 1 + // CHECK: {{%[0-9]+}} = load [trivial] [[ADDR]] : $*Float + // CHECK: end_access [[ACCESS]] : $*(Optional<(Int, Int)>, Float) + // CHECK: hop_to_executor {{%[0-9]+}} : $Optional + // CHECK: } // end sil function '$s4test9ContainerV12accessTuple3SfyYaF' + func accessTuple3() async -> Float { + return await Container.staticCircle.1 + } + + // CHECK-LABEL: sil hidden [ossa] @$s4test9ContainerV12accessTuple4SiSgyYaFZ : $@convention(method) @async (@thin Container.Type) -> Optional { + // CHECK: hop_to_executor {{%[0-9]+}} : $MainActor + // CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [dynamic] {{%[0-9]+}} : $*(Optional<(Int, Int)>, Float) + // CHECK: [[ADDR:%[0-9]+]] = tuple_element_addr [[ACCESS]] : $*(Optional<(Int, Int)>, Float), 0 + // CHECK: switch_enum_addr [[SCRUTINEE:%[0-9]+]] : $*Optional<(Int, Int)>, case #Optional.some!enumelt: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[CRASH_BB:bb[0-9]+]] + + // CHECK: [[CRASH_BB]]: + // CHECK-NOT: hop_to_executor {{%[0-9]+}} + // CHECK: unreachable + + // CHECK: [[SOME_BB]]: + // CHECK: [[TUPLE_ADDR:%[0-9]+]] = unchecked_take_enum_data_addr [[SCRUTINEE]] : $*Optional<(Int, Int)>, #Optional.some!enumelt + // CHECK: [[ELM_ADDR:%[0-9]+]] = tuple_element_addr [[TUPLE_ADDR]] : $*(Int, Int), 0 + // CHECK: {{%[0-9]+}} = load [trivial] [[ELM_ADDR]] : $*Int + // CHECK: end_access [[ACCESS]] : $*(Optional<(Int, Int)>, Float) + // CHECK: hop_to_executor {{%[0-9]+}} : $Optional + // CHECK: } // end sil function '$s4test9ContainerV12accessTuple4SiSgyYaFZ' + static func accessTuple4() async -> Int? { + return await Container.staticCircle.0!.0 + } + + + // CHECK-LABEL: sil hidden [ossa] @$s4test9ContainerV8getCountSiyYaFZ : $@convention(method) @async (@thin Container.Type) -> Int { + // CHECK: hop_to_executor {{%[0-9]+}} : $MainActor + // CHECK: {{%[0-9]+}} = begin_access [read] [dynamic] {{%[0-9]+}} : $*Int + // CHECK: {{%[0-9]+}} = load [trivial] {{%[0-9]+}} : $*Int + // CHECK: hop_to_executor {{%[0-9]+}} : $Optional + // CHECK: } // end sil function '$s4test9ContainerV8getCountSiyYaFZ' + static func getCount() async -> Int { + return await counter + } + + + // CHECK-LABEL: sil hidden [ossa] @$s4test9ContainerV8getValueSiSgyYaFZ : $@convention(method) @async (@thin Container.Type) -> Optional { + // CHECK: bb0(%0 : $@thin Container.Type): + // CHECK: [[MAIN:%[0-9]+]] = begin_borrow {{%[0-9]+}} : $MainActor + // CHECK: [[PREV:%[0-9]+]] = builtin "getCurrentExecutor"() : $Optional + // CHECK: hop_to_executor [[MAIN]] : $MainActor + // CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [dynamic] {{%[0-9]+}} : $*Optional + // CHECK: cond_br {{%[0-9]+}}, [[TRUE_BB:bb[0-9]+]], [[FALSE_BB:bb[0-9]+]] + // + // CHECK: [[TRUE_BB]]: + // CHECK: {{%[0-9]+}} = load [trivial] {{%[0-9]+}} : $*Int + // CHECK: end_access [[ACCESS]] : $*Optional + // CHECK: hop_to_executor [[PREV]] : $Optional + // + // CHECK: [[FALSE_BB]]: + // CHECK: end_access [[ACCESS]] : $*Optional + // CHECK: hop_to_executor [[PREV]] : $Optional + // + // CHECK: } // end sil function '$s4test9ContainerV8getValueSiSgyYaFZ' + static func getValue() async -> Int? { + return await this?.noniso + } + + // CHECK-LABEL: sil hidden [ossa] @$s4test9ContainerV10getOrCrashSfyYaFZ : $@convention(method) @async (@thin Container.Type) -> Float { + // CHECK: bb0({{%[0-9]+}} : $@thin Container.Type): + // CHECK: [[MAIN:%[0-9]+]] = begin_borrow {{%[0-9]+}} : $MainActor + // CHECK: [[PREV:%[0-9]+]] = builtin "getCurrentExecutor"() : $Optional + // CHECK: hop_to_executor [[MAIN]] : $MainActor + // CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [dynamic] {{%[0-9]+}} : $*Optional + // CHECK: switch_enum_addr %11 : $*Optional, case #Optional.some!enumelt: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[CRASH_BB:bb[0-9]+]] + // + // CHECK: [[CRASH_BB]]: + // CHECK-NOT: hop_to_executor {{%[0-9]+}} + // CHECK: unreachable + // + // CHECK: [[SOME_BB]]: + // CHECK: [[DATA_ADDR:%[0-9]+]] = unchecked_take_enum_data_addr %11 : $*Optional, #Optional.some!enumelt + // CHECK: [[ELEM_ADDR:%[0-9]+]] = struct_element_addr %22 : $*Container, #Container.iso + // CHECK: [[PREV_AGAIN:%[0-9]+]] = builtin "getCurrentExecutor"() : $Optional + // CHECK: hop_to_executor {{%[0-9]+}} : $Cat + // CHECK: {{%[0-9]+}} = load [trivial] [[ELEM_ADDR]] : $*Float + // CHECK: hop_to_executor [[PREV]] : $Optional + // CHECK: hop_to_executor [[PREV_AGAIN]] : $Optional + // CHECK: } // end sil function '$s4test9ContainerV10getOrCrashSfyYaFZ' + static func getOrCrash() async -> Float { + return await this!.iso + } + + // CHECK-LABEL: sil hidden [ossa] @$s4test9ContainerV13getRefOrCrashAA6CatBoxCyYaFZ : $@convention(method) @async (@thin Container.Type) -> @owned CatBox { + // CHECK: bb0({{%[0-9]+}} : $@thin Container.Type): + // CHECK: [[MAIN:%[0-9]+]] = begin_borrow {{%[0-9]+}} : $MainActor + // CHECK: [[PREV:%[0-9]+]] = builtin "getCurrentExecutor"() : $Optional + // CHECK: hop_to_executor [[MAIN]] : $MainActor + // CHECK: [[ACCESS:%[0-9]+]] = begin_access [read] [dynamic] {{%[0-9]+}} : $*Optional + // CHECK: switch_enum_addr %11 : $*Optional, case #Optional.some!enumelt: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[CRASH_BB:bb[0-9]+]] + // + // CHECK: [[CRASH_BB]]: + // CHECK-NOT: hop_to_executor {{%[0-9]+}} + // CHECK: unreachable + // + // CHECK: [[SOME_BB]]: + // CHECK: [[DATA_ADDR:%[0-9]+]] = unchecked_take_enum_data_addr %11 : $*Optional, #Optional.some!enumelt + // CHECK: [[ELEM_ADDR:%[0-9]+]] = struct_element_addr %22 : $*Container, #Container.iso + // CHECK: [[PREV_AGAIN:%[0-9]+]] = builtin "getCurrentExecutor"() : $Optional + // CHECK: hop_to_executor {{%[0-9]+}} : $Cat + // CHECK: {{%[0-9]+}} = load [copy] [[ELEM_ADDR]] : $*CatBox + // CHECK: hop_to_executor [[PREV]] : $Optional + // CHECK: hop_to_executor [[PREV_AGAIN]] : $Optional + // CHECK: } // end sil function '$s4test9ContainerV13getRefOrCrashAA6CatBoxCyYaFZ' + static func getRefOrCrash() async -> CatBox { + return await this!.isoRef + } +} + + +@propertyWrapper +struct StateObject { + @MainActor(unsafe) + var wrappedValue: ObjectType { + fatalError() + } + init(wrappedValue: ObjectType) {} +} + +final private actor Coordinactor { + var someValue: Int? +} + +struct Blah { + @StateObject private var coordinator = Coordinactor() + + // closure #1 in Blah.test() + // CHECK-LABEL: sil private [ossa] @$s4test4BlahVAAyyFyyYaYbcfU_ : $@convention(thin) @Sendable @async (Blah) -> () { + // CHECK: hop_to_executor {{%[0-9]+}} : $MainActor + // CHECK: [[ACTOR_OBJ_RAW:%[0-9]+]] = apply {{%[0-9]+}}({{%[0-9]+}}) : $@convention(method) (Blah) -> @owned Coordinactor + // CHECK: hop_to_executor {{%[0-9]+}} : $Optional + // CHECK: [[ACTOR_OBJ:%[0-9]+]] = begin_borrow [[ACTOR_OBJ_RAW]] : $Coordinactor + // CHECK: [[VAL:%[0-9]+]] = ref_element_addr [[ACTOR_OBJ]] : $Coordinactor, #Coordinactor.someValue + // CHECK: hop_to_executor [[ACTOR_OBJ]] + // CHECK: [[VAL_ACCESS:%[0-9]+]] = begin_access [read] [dynamic] [[VAL]] : $*Optional + // CHECK: {{%[0-9]+}} = load [trivial] %17 : $*Optional + // CHECK: end_access %17 : $*Optional + // CHECK: hop_to_executor {{%[0-9]+}} : $Optional + // CHECK: } // end sil function '$s4test4BlahVAAyyFyyYaYbcfU_' + @available(SwiftStdlib 5.5, *) + func test() { + Task.detached { + if await coordinator.someValue == nil { + fatalError() + } + } + } +} \ No newline at end of file diff --git a/test/SILGen/isolated_parameters.swift b/test/SILGen/isolated_parameters.swift new file mode 100644 index 0000000000000..9c4719afbe939 --- /dev/null +++ b/test/SILGen/isolated_parameters.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 | %FileCheck %s +// REQUIRES: concurrency + +@available(SwiftStdlib 5.5, *) +public actor A { + // CHECK: sil{{.*}} [ossa] @$s4test1AC6methodyyF + public func method() { } +} + +// CHECK: sil{{.*}} [ossa] @$s4test13takesIsolatedyyAA1ACYiF +@available(SwiftStdlib 5.5, *) +public func takesIsolated(_: isolated A) { } diff --git a/test/SILGen/lazy_property_with_observers.swift b/test/SILGen/lazy_property_with_observers.swift index 63ef988e8a401..6a2ce76abc313 100644 --- a/test/SILGen/lazy_property_with_observers.swift +++ b/test/SILGen/lazy_property_with_observers.swift @@ -140,7 +140,7 @@ foo1.bar = 2 // CHECK-LABEL: sil hidden [ossa] @$s28lazy_property_with_observers4Foo1V3barSivs : $@convention(method) (Int, @inout Foo1) -> () { // CHECK: bb0([[VALUE:%.*]] : $Int, [[FOO1:%.*]] : $*Foo1): // CHECK-NEXT: debug_value [[VALUE]] : $Int, let, name "value", argno 1 -// CHECK-NEXT: debug_value_addr [[FOO1]] : $*Foo1, var, name "self", argno 2 +// CHECK-NEXT: debug_value [[FOO1]] : $*Foo1, var, name "self", argno 2, {{.*}} expr op_deref // CHECK-NEXT: [[BEGIN_ACCESS:%.*]] = begin_access [modify] [unknown] %1 : $*Foo1 // CHECK-NEXT: // function_ref Foo1.bar.getter // CHECK-NEXT: [[GETTER:%.*]] = function_ref @$s28lazy_property_with_observers4Foo1V3barSivg : $@convention(method) (@inout Foo1) -> Int diff --git a/test/SILGen/let_decls.swift b/test/SILGen/let_decls.swift index 398c7a9a90503..3095da62df102 100644 --- a/test/SILGen/let_decls.swift +++ b/test/SILGen/let_decls.swift @@ -254,7 +254,7 @@ func test_weird_property(_ v : WeirdPropertyTest, i : Int) -> Int { // CHECK-LABEL: sil hidden [ossa] @{{.*}}generic_identity // CHECK: bb0(%0 : $*T, %1 : $*T): -// CHECK-NEXT: debug_value_addr %1 : $*T +// CHECK-NEXT: debug_value %1 : $*T, {{.*}} expr op_deref // CHECK-NEXT: copy_addr %1 to [initialization] %0 : $*T // CHECK-NOT: destroy_addr %1 // CHECK: } // end sil function '{{.*}}generic_identity{{.*}}' @@ -286,7 +286,7 @@ protocol SimpleProtocol { // CHECK-LABEL: sil hidden [ossa] @{{.*}}testLetProtocolBases // CHECK: bb0(%0 : $*SimpleProtocol): func testLetProtocolBases(_ p : SimpleProtocol) { - // CHECK-NEXT: debug_value_addr + // CHECK-NEXT: debug_value {{.*}} expr op_deref // CHECK-NEXT: open_existential_addr // CHECK-NEXT: witness_method // CHECK-NEXT: apply @@ -305,7 +305,7 @@ func testLetProtocolBases(_ p : SimpleProtocol) { // CHECK-LABEL: sil hidden [ossa] @{{.*}}testLetArchetypeBases // CHECK: bb0(%0 : $*T): func testLetArchetypeBases(_ p : T) { - // CHECK-NEXT: debug_value_addr + // CHECK-NEXT: debug_value {{.*}} expr op_deref // CHECK-NEXT: witness_method $T // CHECK-NEXT: apply p.doSomethingGreat() @@ -321,7 +321,7 @@ func testLetArchetypeBases(_ p : T) { // CHECK-LABEL: sil hidden [ossa] @{{.*}}testDebugValue // CHECK: bb0(%0 : $Int, %1 : $*SimpleProtocol): // CHECK-NEXT: debug_value %0 : $Int, let, name "a" -// CHECK-NEXT: debug_value_addr %1 : $*SimpleProtocol, let, name "b" +// CHECK-NEXT: debug_value %1 : $*SimpleProtocol, let, name "b", {{.*}} expr op_deref func testDebugValue(_ a : Int, b : SimpleProtocol) -> Int { // CHECK-NEXT: debug_value %0 : $Int, let, name "x" @@ -439,7 +439,7 @@ struct GenericStruct { } // CHECK-LABEL: sil hidden [ossa] @{{.*}}GenericStructV4getA{{.*}} : $@convention(method) (@in_guaranteed GenericStruct) -> @out T // CHECK: bb0(%0 : $*T, %1 : $*GenericStruct): - // CHECK-NEXT: debug_value_addr %1 : $*GenericStruct, let, name "self" + // CHECK-NEXT: debug_value %1 : $*GenericStruct, let, name "self", {{.*}} expr op_deref // CHECK-NEXT: %3 = struct_element_addr %1 : $*GenericStruct, #GenericStruct.a // CHECK-NEXT: copy_addr %3 to [initialization] %0 : $*T // CHECK-NEXT: %5 = tuple () @@ -451,7 +451,7 @@ struct GenericStruct { // CHECK-LABEL: sil hidden [ossa] @{{.*}}GenericStructV4getB{{.*}} : $@convention(method) (@in_guaranteed GenericStruct) -> Int // CHECK: bb0([[SELF_ADDR:%.*]] : $*GenericStruct): - // CHECK-NEXT: debug_value_addr [[SELF_ADDR]] : $*GenericStruct, let, name "self" + // CHECK-NEXT: debug_value [[SELF_ADDR]] : $*GenericStruct, let, name "self", {{.*}} expr op_deref // CHECK-NEXT: [[PROJ_ADDR:%.*]] = struct_element_addr [[SELF_ADDR]] : $*GenericStruct, #GenericStruct.b // CHECK-NEXT: [[PROJ_VAL:%.*]] = load [trivial] [[PROJ_ADDR]] : $*Int // CHECK-NOT: destroy_addr [[SELF]] : $*GenericStruct diff --git a/test/SILGen/mangle_conformance_access_path.swift b/test/SILGen/mangle_conformance_access_path.swift new file mode 100644 index 0000000000000..d8168f9c641a5 --- /dev/null +++ b/test/SILGen/mangle_conformance_access_path.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module %S/Inputs/mangle_conformance_access_path_helper.swift -emit-module-path %t/mangle_conformance_access_path_helper.swiftmodule +// RUN: %target-swift-frontend -emit-silgen %s -I %t -requirement-machine=verify | %FileCheck %s + +import mangle_conformance_access_path_helper + +struct GG {} + +// This is a retroactive conformance. +extension G : P where T : P {} + +// The mangling of GG> will contain a conformance access path +// for (Y.U.U : P). This path is (Y : R)(Self.U.U : P). The 'Self.U.U' +// uses the short mangling if 'Self.U' only conforms to a single +// protocol. However, this check was being performed in the original +// generic signature , and not the generic signature +// for the protocol R, which is . + +// CHECK-LABEL: sil hidden [ossa] @$s30mangle_conformance_access_path3fooyyAA2GGVy0a1_b1_c1_D7_helper1GVy1U_AHQY_GAjE1PAAq_AE1RHD1_AH_AHQZAeKHA2__HCg_G_xq_tAeLR_r0_lF : $@convention(thin) (GG>, @in_guaranteed X, @in_guaranteed Y) -> () +func foo(_: GG>, _: X, _: Y) {} diff --git a/test/SILGen/objc_access_notes.swift b/test/SILGen/objc_access_notes.swift index 997314c62aa96..665c3389daa1b 100644 --- a/test/SILGen/objc_access_notes.swift +++ b/test/SILGen/objc_access_notes.swift @@ -10,8 +10,8 @@ import gizmo import ansible class Hoozit : Gizmo { - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this instance method}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this instance method, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} func typical(_ x: Int, y: Gizmo) -> Gizmo { return y } // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC7typical_1ySo5GizmoCSi_AGtFTo : $@convention(objc_method) (Int, Gizmo, Hoozit) -> @autoreleased Gizmo { // CHECK: bb0([[X:%.*]] : $Int, [[Y:%.*]] : @unowned $Gizmo, [[THIS:%.*]] : @unowned $Hoozit): @@ -56,8 +56,8 @@ class Hoozit : Gizmo { // CHECK-NEXT: } // NS_RETURNS_RETAINED by family (-copy) - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this instance method}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this instance method, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} func copyFoo() -> Gizmo { return self } // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC7copyFooSo5GizmoCyFTo : $@convention(objc_method) (Hoozit) -> @owned Gizmo // CHECK: bb0([[THIS:%.*]] : @unowned $Hoozit): @@ -72,8 +72,8 @@ class Hoozit : Gizmo { // CHECK-NEXT: } // NS_RETURNS_RETAINED by family (-mutableCopy) - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this instance method}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this instance method, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} func mutableCopyFoo() -> Gizmo { return self } // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC14mutableCopyFooSo5GizmoCyFTo : $@convention(objc_method) (Hoozit) -> @owned Gizmo // CHECK: bb0([[THIS:%.*]] : @unowned $Hoozit): @@ -90,8 +90,8 @@ class Hoozit : Gizmo { // NS_RETURNS_RETAINED by family (-copy). This is different from Swift's // normal notion of CamelCase, but it's what Clang does, so we should match // it. - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this instance method}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this instance method, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} func copy8() -> Gizmo { return self } // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC5copy8So5GizmoCyFTo : $@convention(objc_method) (Hoozit) -> @owned Gizmo // CHECK: bb0([[THIS:%.*]] : @unowned $Hoozit): @@ -106,8 +106,8 @@ class Hoozit : Gizmo { // CHECK-NEXT: } // NS_RETURNS_RETAINED by family (-copy) - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this instance method}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc(copyDuplicate) }} + // expected-remark@+2 {{implicitly added '@objc(copyDuplicate)' to this instance method, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc(copyDuplicate)' explicitly to silence this warning}} {{3-3=@objc(copyDuplicate) }} func makeDuplicate() -> Gizmo { return self } // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC13makeDuplicateSo5GizmoCyFTo : $@convention(objc_method) (Hoozit) -> @owned Gizmo // CHECK: bb0([[THIS:%.*]] : @unowned $Hoozit): @@ -123,8 +123,8 @@ class Hoozit : Gizmo { // Override the normal family conventions to make this non-consuming and // returning at +0. - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this instance method}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this instance method, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} func initFoo() -> Gizmo { return self } // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC7initFooSo5GizmoCyFTo : $@convention(objc_method) (Hoozit) -> @autoreleased Gizmo // CHECK: bb0([[THIS:%.*]] : @unowned $Hoozit): @@ -138,8 +138,8 @@ class Hoozit : Gizmo { // CHECK-NEXT: return [[RES]] // CHECK-NEXT: } - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this property}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this property, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} var typicalProperty: Gizmo // -- getter // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC15typicalPropertySo5GizmoCvgTo : $@convention(objc_method) (Hoozit) -> @autoreleased Gizmo { @@ -190,8 +190,8 @@ class Hoozit : Gizmo { // CHECK: } // end sil function '$s11objc_thunks6HoozitC15typicalPropertySo5GizmoCvs' // NS_RETURNS_RETAINED getter by family (-copy) - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this property}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this property, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} var copyProperty: Gizmo // -- getter // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC12copyPropertySo5GizmoCvgTo : $@convention(objc_method) (Hoozit) -> @owned Gizmo { @@ -239,8 +239,8 @@ class Hoozit : Gizmo { // CHECK: destroy_value [[ARG1]] // CHECK: } // end sil function '$s11objc_thunks6HoozitC12copyPropertySo5GizmoCvs' - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this property}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this property, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} var roProperty: Gizmo { return self } // -- getter // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC10roPropertySo5GizmoCvgTo : $@convention(objc_method) (Hoozit) -> @autoreleased Gizmo { @@ -258,8 +258,8 @@ class Hoozit : Gizmo { // -- no setter // CHECK-NOT: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC10roPropertySo5GizmoCvsTo - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this property}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this property, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} var rwProperty: Gizmo { get { return self @@ -283,8 +283,8 @@ class Hoozit : Gizmo { // CHECK-NEXT: return // CHECK-NEXT: } - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this property}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this property, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} var copyRWProperty: Gizmo { get { return self @@ -319,8 +319,8 @@ class Hoozit : Gizmo { // CHECK-NEXT: return // CHECK-NEXT: } - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this property}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this property, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} var initProperty: Gizmo // -- getter // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC12initPropertySo5GizmoCvgTo : $@convention(objc_method) (Hoozit) -> @autoreleased Gizmo { @@ -349,14 +349,14 @@ class Hoozit : Gizmo { // CHECK-NEXT: return // CHECK-NEXT: } - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this property}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this property, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} var propComputed: Gizmo { - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this getter}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{5-5=@objc(initPropComputedGetter) }} + // expected-remark@+2 {{implicitly added '@objc(initPropComputedGetter)' to this getter, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc(initPropComputedGetter)' explicitly to silence this warning}} {{5-5=@objc(initPropComputedGetter) }} get { return self } - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this setter}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{5-5=@objc(initPropComputedSetter:) }} + // expected-remark@+2 {{implicitly added '@objc(initPropComputedSetter:)' to this setter, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc(initPropComputedSetter:)' explicitly to silence this warning}} {{5-5=@objc(initPropComputedSetter:) }} set {} } // -- getter @@ -410,8 +410,8 @@ class Hoozit : Gizmo { } // Subscript - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this subscript}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this subscript, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} subscript (i: Int) -> Hoozit { // Getter // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitCyACSicigTo : $@convention(objc_method) (Int, Hoozit) -> @autoreleased Hoozit @@ -458,7 +458,7 @@ class Wotsit : Gizmo { // CHECK-NEXT: } @objc(plain) func plain() { } - // expected-remark@-2 {{access note for fancy test suite changes the '@objc' name of this instance method to 'fancy', but source code specifies 'plain'; the access note will be ignored}} + // expected-remark@-1 {{ignored access note: did not change Objective-C name of this instance method from 'plain' to 'fancy', even though it was specified by access note for fancy test suite}} func generic(_ x: U) {} @@ -505,10 +505,8 @@ class Wotsit : Gizmo { extension Hoozit { // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC3intACSi_tcfcTo : $@convention(objc_method) (Int, @owned Hoozit) -> @owned Hoozit convenience init(int i: Int) { self.init(bellsOn: i) } - // expected-remark@-1 {{access note for fancy test suite adds attribute 'objc' to this initializer}} - // expected-note@-2 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} - // expected-remark@-3 {{access note for fancy test suite adds modifier 'dynamic' to this initializer}} - // expected-note@-4 {{add modifier explicitly to silence this warning}} {{3-3=dynamic }} + // expected-remark@-1 {{implicitly added '@objc dynamic' to this initializer, as specified by access note for fancy test suite}} + // expected-note@-2 {{add '@objc dynamic' explicitly to silence this warning}} {{3-3=@objc dynamic }} // CHECK-LABEL: sil hidden [ossa] @$s11objc_thunks6HoozitC6doubleACSd_tcfC : $@convention(method) (Double, @thick Hoozit.Type) -> @owned Hoozit convenience init(double d: Double) { @@ -517,8 +515,8 @@ extension Hoozit { other() } - // expected-remark@+2 {{access note for fancy test suite adds attribute 'objc' to this instance method}} - // expected-note@+1 {{add attribute explicitly to silence this warning}} {{3-3=@objc }} + // expected-remark@+2 {{implicitly added '@objc' to this instance method, as specified by access note for fancy test suite}} + // expected-note@+1 {{add '@objc' explicitly to silence this warning}} {{3-3=@objc }} func foof() {} // CHECK-LABEL: sil hidden [thunk] [ossa] @$s11objc_thunks6HoozitC4foofyyFTo : $@convention(objc_method) (Hoozit) -> () { diff --git a/test/SILGen/objc_actor.swift b/test/SILGen/objc_actor.swift new file mode 100644 index 0000000000000..c6e17f95bcb64 --- /dev/null +++ b/test/SILGen/objc_actor.swift @@ -0,0 +1,34 @@ +// RUN: %target-swift-frontend -emit-silgen %s -swift-version 5 -disable-availability-checking | %FileCheck %s +// REQUIRES: concurrency +// REQUIRES: objc_interop + +// rdar://80863853 - For an actor inheriting from NSObject and using '@objc' +// should have the same effect: the effective superclass is SwiftNativeNSObject +// (see 945011d39f8b271b8906bd509aac3aa954f4fc57) not NSObject. +// Check that we don't treat any case as an ObjC class. + +import Foundation + +public actor MyClass1: NSObject { + public var x: Int + public init(_ x: Int) { self.x = x } +} + +// CHECK: alloc_ref $MyClass1 +// CHECK-NOT: alloc_ref [objc] $MyClass1 + +@objc public actor MyClass2 { + public var x: Int + public init(_ x: Int) { self.x = x } +} + +// CHECK: alloc_ref $MyClass2 +// CHECK-NOT: alloc_ref [objc] $MyClass2 + +@objc public actor MyClass3: NSObject { + public var x: Int + public init(_ x: Int) { self.x = x } +} + +// CHECK: alloc_ref $MyClass3 +// CHECK-NOT: alloc_ref [objc] $MyClass3 diff --git a/test/SILGen/objc_async.swift b/test/SILGen/objc_async.swift index 12d9a52d996e0..3fad6864861f8 100644 --- a/test/SILGen/objc_async.swift +++ b/test/SILGen/objc_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -disable-availability-checking %s -verify | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s // REQUIRES: concurrency // REQUIRES: objc_interop @@ -8,6 +8,8 @@ import ObjCConcurrency // CHECK-LABEL: sil {{.*}}@${{.*}}14testSlowServer func testSlowServer(slowServer: SlowServer) async throws { // CHECK: [[RESUME_BUF:%.*]] = alloc_stack $Int + // CHECK: [[STRINGINIT:%.*]] = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : + // CHECK: [[ARG:%.*]] = apply [[STRINGINIT]] // CHECK: [[METHOD:%.*]] = objc_method {{.*}} $@convention(objc_method) (NSString, @convention(block) (Int) -> (), SlowServer) -> () // CHECK: [[CONT:%.*]] = get_async_continuation_addr Int, [[RESUME_BUF]] // CHECK: [[WRAPPED:%.*]] = struct $UnsafeContinuation ([[CONT]] : $Builtin.RawUnsafeContinuation) @@ -16,10 +18,14 @@ func testSlowServer(slowServer: SlowServer) async throws { // CHECK: store [[WRAPPED]] to [trivial] [[CONT_SLOT]] // CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[INT_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation, Int) -> () // CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]] {{.*}}, invoke [[BLOCK_IMPL]] - // CHECK: apply [[METHOD]]({{.*}}, [[BLOCK]], %0) + // CHECK: apply [[METHOD]]([[ARG]], [[BLOCK]], %0) + // CHECK: [[COPY:%.*]] = copy_value [[ARG]] + // CHECK: destroy_value [[ARG]] // CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]] // CHECK: [[RESUME]]: // CHECK: [[RESULT:%.*]] = load [trivial] [[RESUME_BUF]] + // CHECK: fix_lifetime [[COPY]] + // CHECK: destroy_value [[COPY]] // CHECK: dealloc_stack [[RESUME_BUF]] let _: Int = await slowServer.doSomethingSlow("mail") @@ -70,6 +76,19 @@ func testSlowServer(slowServer: SlowServer) async throws { let _: NSObject? = try await slowServer.stopRecording() let _: NSObject = try await slowServer.someObject() + let _: () -> Void = await slowServer.performVoid2Void() + let _: (Any) -> Void = await slowServer.performId2Void() + let _: (Any) -> Any = await slowServer.performId2Id() + let _: (String) -> String = await slowServer.performNSString2NSString() + + let _: ((String) -> String, String) = await slowServer.performNSString2NSStringNSString() + let _: ((Any) -> Void, (Any) -> Void) = await slowServer.performId2VoidId2Void() + + let _: String = try await slowServer.findAnswerFailingly() + + let _: () -> Void = try await slowServer.obtainClosure() + + let _: Flavor = try await slowServer.iceCreamFlavor() } func testGeneric(x: GenericObject) async throws { @@ -155,3 +174,30 @@ func testGeneric2(x: GenericObject, y: U) async throws { // CHECK: [[RESULT_1_BUF:%.*]] = tuple_element_addr [[RESULT_BUF]] {{.*}}, 1 // CHECK: store %2 to [trivial] [[RESULT_1_BUF]] +// CHECK-LABEL: sil {{.*}}@${{.*}}22testSlowServerFromMain +@MainActor +func testSlowServerFromMain(slowServer: SlowServer) async throws { + // CHECK: hop_to_executor %6 : $MainActor + // CHECK: [[RESUME_BUF:%.*]] = alloc_stack $Int + // CHECK: [[STRINGINIT:%.*]] = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : + // CHECK: [[ARG:%.*]] = apply [[STRINGINIT]] + // CHECK: [[METHOD:%.*]] = objc_method {{.*}} $@convention(objc_method) (NSString, @convention(block) (Int) -> (), SlowServer) -> () + // CHECK: [[CONT:%.*]] = get_async_continuation_addr Int, [[RESUME_BUF]] + // CHECK: [[WRAPPED:%.*]] = struct $UnsafeContinuation ([[CONT]] : $Builtin.RawUnsafeContinuation) + // CHECK: [[BLOCK_STORAGE:%.*]] = alloc_stack $@block_storage UnsafeContinuation + // CHECK: [[CONT_SLOT:%.*]] = project_block_storage [[BLOCK_STORAGE]] + // CHECK: store [[WRAPPED]] to [trivial] [[CONT_SLOT]] + // CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[INT_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation, Int) -> () + // CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]] {{.*}}, invoke [[BLOCK_IMPL]] + // CHECK: apply [[METHOD]]([[ARG]], [[BLOCK]], %0) + // CHECK: [[COPY:%.*]] = copy_value [[ARG]] + // CHECK: destroy_value [[ARG]] + // CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]] + // CHECK: [[RESUME]]: + // CHECK: [[RESULT:%.*]] = load [trivial] [[RESUME_BUF]] + // CHECK: fix_lifetime [[COPY]] + // CHECK: destroy_value [[COPY]] + // CHECK: hop_to_executor %6 : $MainActor + // CHECK: dealloc_stack [[RESUME_BUF]] + let _: Int = await slowServer.doSomethingSlow("mail") +} diff --git a/test/SILGen/objc_async_from_swift.swift b/test/SILGen/objc_async_from_swift.swift index 2bcaaee7bd8c4..dc3b9e19ef24d 100644 --- a/test/SILGen/objc_async_from_swift.swift +++ b/test/SILGen/objc_async_from_swift.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -disable-availability-checking %s -verify | %FileCheck --implicit-check-not=hop_to_executor --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s // REQUIRES: concurrency // REQUIRES: objc_interop @@ -108,3 +108,50 @@ class SlowServerlet: SlowServer { } } +@globalActor actor FooActor { + static var shared = FooActor() +} + +@FooActor +class ActorConstrained: NSObject { + // ActorConstrained.foo() + // CHECK-LABEL: sil hidden [ossa] @$s{{.*}}16ActorConstrainedC3foo{{.*}} : $@convention(method) @async (@guaranteed ActorConstrained) -> Bool { + // CHECK: hop_to_executor {{%.*}} : $FooActor + + // @objc ActorConstrained.foo() + // CHECK-LABEL: sil hidden [thunk] [ossa] @$s{{.*}}16ActorConstrainedC3foo{{.*}}To : $@convention(objc_method) (@convention(block) (Bool) -> (), ActorConstrained) -> () { + // CHECK: [[ASYNC_CLOS:%[0-9]+]] = function_ref @$s{{.*}}16ActorConstrainedC3foo{{.*}}U_To : $@convention(thin) @Sendable @async (@convention(block) (Bool) -> (), ActorConstrained) -> () + // CHECK: [[PRIMED_CLOS:%[0-9]+]] = partial_apply [callee_guaranteed] [[ASYNC_CLOS]]( + // CHECK: [[TASK_RUNNER:%[0-9]+]] = function_ref @$ss29_runTaskForBridgedAsyncMethodyyyyYaYbcnF + // CHECK: apply [[TASK_RUNNER]]([[PRIMED_CLOS]]) + + // @objc closure #1 in ActorConstrained.foo() + // CHECK-LABEL: sil shared [thunk] [ossa] @$s{{.*}}16ActorConstrainedC3foo{{.*}}U_To : $@convention(thin) @Sendable @async (@convention(block) (Bool) -> (), ActorConstrained) -> () { + // CHECK: hop_to_executor {{%.*}} : $FooActor + @objc func foo() async -> Bool { + return true + } +} + + +actor Dril: NSObject { + // Dril.postTo(twitter:) + // CHECK-LABEL: sil hidden [ossa] @$s{{.*}}4DrilC6postTo7twitter{{.*}} : $@convention(method) @async (@guaranteed String, @guaranteed Dril) -> Bool { + // CHECK: hop_to_executor {{%.*}} : $Dril + + // @objc Dril.postTo(twitter:) + // CHECK-LABEL: sil hidden [thunk] [ossa] @$s{{.*}}4DrilC6postTo7twitter{{.*}}To : $@convention(objc_method) (NSString, @convention(block) (Bool) -> (), Dril) -> () { + // CHECK: [[ASYNC_CLOS:%[0-9]+]] = function_ref @$s{{.*}}4DrilC6postTo7twitter{{.*}}U_To : $@convention(thin) @Sendable @async (NSString, @convention(block) (Bool) -> (), Dril) -> () + // CHECK: [[PRIMED_CLOS:%[0-9]+]] = partial_apply [callee_guaranteed] [[ASYNC_CLOS]]( + // CHECK: [[TASK_RUNNER:%[0-9]+]] = function_ref @$ss29_runTaskForBridgedAsyncMethodyyyyYaYbcnF + // CHECK: apply [[TASK_RUNNER]]([[PRIMED_CLOS]]) + @objc func postTo(twitter msg: String) async -> Bool { + return true + } + + // this is known to not emit a hop in the objc thunk (rdar://80972126) + @MainActor + @objc func postFromMainActorTo(twitter msg: String) -> Bool { + return true + } +} diff --git a/test/SILGen/objc_bridging_any.swift b/test/SILGen/objc_bridging_any.swift index f65af5357fd1c..c0e4601b4fb92 100644 --- a/test/SILGen/objc_bridging_any.swift +++ b/test/SILGen/objc_bridging_any.swift @@ -30,14 +30,14 @@ func passingToId(receiver: NSIdLover, // CHECK: debug_value [[OBJECT:%.*]] : $AnyObject // CHECK: debug_value [[CLASS_GENERIC:%.*]] : $T // CHECK: debug_value [[CLASS_EXISTENTIAL:%.*]] : $CP - // CHECK: debug_value_addr [[GENERIC:%.*]] : $*U - // CHECK: debug_value_addr [[EXISTENTIAL:%.*]] : $*P + // CHECK: debug_value [[GENERIC:%.*]] : $*U, {{.*}} expr op_deref + // CHECK: debug_value [[EXISTENTIAL:%.*]] : $*P, {{.*}} expr op_deref // CHECK: debug_value [[ERROR:%.*]] : $Error - // CHECK: debug_value_addr [[ANY:%.*]] : $*Any + // CHECK: debug_value [[ANY:%.*]] : $*Any, {{.*}} expr op_deref // CHECK: debug_value [[KNOWN_UNBRIDGED:%.*]] : $KnownUnbridged // CHECK: debug_value [[OPT_STRING:%.*]] : $Optional // CHECK: debug_value [[OPT_NSSTRING:%.*]] : $Optional - // CHECK: debug_value_addr [[OPT_ANY:%.*]] : $*Optional + // CHECK: debug_value [[OPT_ANY:%.*]] : $*Optional, {{.*}} expr op_deref // CHECK: [[STRING_COPY:%.*]] = copy_value [[STRING]] // CHECK: [[BRIDGE_STRING:%.*]] = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF diff --git a/test/SILGen/objc_effectful_properties.swift b/test/SILGen/objc_effectful_properties.swift index 7bafdcd025fe9..b193d46027137 100644 --- a/test/SILGen/objc_effectful_properties.swift +++ b/test/SILGen/objc_effectful_properties.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules %s -verify | %FileCheck --enable-var-scope --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -disable-availability-checking -I %S/Inputs/custom-modules %s -verify | %FileCheck --enable-var-scope --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s // REQUIRES: concurrency // REQUIRES: objc_interop @@ -60,4 +60,4 @@ func testMainActorMethod(eff : EffProps) async { // CHECK-NOT: hop_to_executor // CHECK: } // end sil function '${{.*}}19testMainActorMethod let _ = await eff.regularMainDog() -} \ No newline at end of file +} diff --git a/test/SILGen/observers.swift b/test/SILGen/observers.swift index 6b6c6d4503fa1..8c47381327adf 100644 --- a/test/SILGen/observers.swift +++ b/test/SILGen/observers.swift @@ -30,7 +30,7 @@ public struct DidSetWillSetTests { willSet(newA) { // CHECK: bb0(%0 : $Int, %1 : $*DidSetWillSetTests): // CHECK-NEXT: debug_value %0 - // CHECK-NEXT: debug_value_addr %1 : $*DidSetWillSetTests + // CHECK-NEXT: debug_value %1 : $*DidSetWillSetTests, {{.*}} expr op_deref takeInt(a) @@ -64,7 +64,7 @@ public struct DidSetWillSetTests { // CHECK-LABEL: sil private [ossa] @$s9observers010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vW didSet { // CHECK: bb0(%0 : $*DidSetWillSetTests): - // CHECK-NEXT: debug_value_addr %0 : $*DidSetWillSetTests + // CHECK-NEXT: debug_value %0 : $*DidSetWillSetTests, {{.*}} expr op_deref takeInt(a) @@ -102,7 +102,7 @@ public struct DidSetWillSetTests { // CHECK-LABEL: sil [ossa] @$s9observers010DidSetWillC5TestsV1aSivs : $@convention(method) (Int, @inout DidSetWillSetTests) -> () { // CHECK: bb0([[NEWVALUE:%.*]] : $Int, %1 : $*DidSetWillSetTests): // CHECK-NEXT: debug_value [[NEWVALUE]] : $Int, let, name "value", argno 1 - // CHECK-NEXT: debug_value_addr %1 + // CHECK-NEXT: debug_value %1{{.*}} expr op_deref // CHECK-NEXT: [[MODIFY_ONE:%.*]] = begin_access [modify] [unknown] %1 : $*DidSetWillSetTests // CHECK-NEXT: // function_ref observers.DidSetWillSetTests.a.willset : Swift.Int @@ -313,7 +313,7 @@ func propertyWithDidSetTakingOldValue() { // CHECK: bb0([[ARG1:%.*]] : $Int, [[ARG2:%.*]] : @guaranteed ${ var Int }): // CHECK-NEXT: debug_value [[ARG1]] : $Int, let, name "value", argno 1 // CHECK-NEXT: [[ARG2_PB:%.*]] = project_box [[ARG2]] -// CHECK-NEXT: debug_value_addr [[ARG2_PB]] : $*Int, var, name "p", argno 2 +// CHECK-NEXT: debug_value [[ARG2_PB]] : $*Int, var, name "p", argno 2, expr op_deref // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] [[ARG2_PB]] // CHECK-NEXT: [[ARG2_PB_VAL:%.*]] = load [trivial] [[READ]] : $*Int // CHECK-NEXT: end_access [[READ]] diff --git a/test/SILGen/opaque_result_type_nested_optional.swift b/test/SILGen/opaque_result_type_nested_optional.swift new file mode 100644 index 0000000000000..0ec92953a6cde --- /dev/null +++ b/test/SILGen/opaque_result_type_nested_optional.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module %S/Inputs/opaque_result_type_nested_optional_other.swift -emit-module-path %t/opaque_result_type_nested_optional_other.swiftmodule -disable-availability-checking +// RUN: %target-swift-emit-silgen %s -I %t | %FileCheck %s + +import opaque_result_type_nested_optional_other + +_ = bar(foo()) + +// CHECK-LABEL: sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +// CHECK: [[OUTER:%.*]] = alloc_stack $Optional +// CHECK: [[UNUSED:%.*]] = alloc_stack $S> +// CHECK: [[INNER:%.*]] = alloc_stack $S> +// CHECK: [[FN:%.*]] = function_ref @$s40opaque_result_type_nested_optional_other3bary1AQzxAA1PRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> @out τ_0_0.A +// CHECK: apply [[FN]]>([[OUTER]], [[INNER]]) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> @out τ_0_0.A +// CHECK: return diff --git a/test/SILGen/optional-cast.swift b/test/SILGen/optional-cast.swift index 98de702d5f672..692abe5130ef0 100644 --- a/test/SILGen/optional-cast.swift +++ b/test/SILGen/optional-cast.swift @@ -174,7 +174,7 @@ func opt_to_opt_reference(_ x : C!) -> C? { return x } // CHECK-LABEL: sil hidden [ossa] @$s4main07opt_to_B12_addressOnly{{[_0-9a-zA-Z]*}}F // CHECK: bb0(%0 : $*Optional, %1 : $*Optional): -// CHECK-NEXT: debug_value_addr %1 : $*Optional, let, name "x" +// CHECK-NEXT: debug_value %1 : $*Optional, let, name "x", {{.*}} expr op_deref // CHECK-NEXT: copy_addr %1 to [initialization] %0 // CHECK-NOT: destroy_addr %1 func opt_to_opt_addressOnly(_ x : T!) -> T? { return x } diff --git a/test/SILGen/property_wrapper_coroutine.swift b/test/SILGen/property_wrapper_coroutine.swift index 0dd8f7cd479eb..05ad982af6f70 100644 --- a/test/SILGen/property_wrapper_coroutine.swift +++ b/test/SILGen/property_wrapper_coroutine.swift @@ -36,7 +36,7 @@ _ = state1.someValues // CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_coroutine5StateV6valuesSaySSGvM : $@yield_once @convention(method) (@inout State) -> @yields @inout Array { // CHECK: bb0([[STATE:%.*]] : $*State): -// CHECK: debug_value_addr [[STATE]] : $*State, var, name "self", argno {{.*}} +// CHECK: debug_value [[STATE]] : $*State, var, name "self", argno {{.*}}, {{.*}} expr op_deref // CHECK: [[BEGIN_ACCESS:%.*]] = begin_access [modify] [unknown] [[STATE]] : $*State // CHECK: [[BACKING_ADDR:%.*]] = struct_element_addr [[BEGIN_ACCESS]] : $*State, #State._values // CHECK: [[VALUE_ADDR:%.*]] = struct_element_addr [[BACKING_ADDR]] : $*TestWrapper>, #TestWrapper.wrappedValue diff --git a/test/SILGen/property_wrapper_local.swift b/test/SILGen/property_wrapper_local.swift index 6e14f9def0351..e806ef50ec79d 100644 --- a/test/SILGen/property_wrapper_local.swift +++ b/test/SILGen/property_wrapper_local.swift @@ -173,3 +173,56 @@ func testLocalReference(count: Int) { // setter of value #1 in testLocalReference(count:) // CHECK: sil private [ossa] @$s22property_wrapper_local18testLocalReference5countySi_tF5valueL_Sivs : $@convention(thin) (Int, @guaranteed { var BoundedNumber }) -> () } + +func takesAutoclosure(_: @autoclosure () -> Int) {} + +// CHECK-LABEL: sil hidden [ossa] @$s22property_wrapper_local12testCapturesyyF : $@convention(thin) () -> () +func testCaptures() { + @Wrapper var value = 10 + takesAutoclosure(value) + // implicit closure #1 in testCaptures() + // CHECK-LABEL: sil private [transparent] [ossa] @$s22property_wrapper_local12testCapturesyyFSiyXEfu_ : $@convention(thin) (@guaranteed { var Wrapper }) -> Int + + let _: () -> Void = { + _ = value + value = 100 + } + // closure #1 in testCaptures() + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local12testCapturesyyFyycfU_ : $@convention(thin) (@guaranteed { var Wrapper }) -> () +} + +@propertyWrapper +struct DefaultInit { + var wrappedValue: Int + + // CHECK-LABEL: sil hidden [ossa] @$s22property_wrapper_local11DefaultInitVACycfC : $@convention(method) (@thin DefaultInit.Type) -> DefaultInit + init() { + self.wrappedValue = 0 + } + + // CHECK-LABEL: sil hidden [ossa] @$s22property_wrapper_local11DefaultInitV5valueACSi_tcfC : $@convention(method) (Int, @thin DefaultInit.Type) -> DefaultInit + init(value: Int) { + self.wrappedValue = value + } +} + +@propertyWrapper +struct DefaultWrappedValue { + // CHECK-LABEL: sil hidden [ossa] @$s22property_wrapper_local19DefaultWrappedValueVACycfC : $@convention(method) (@thin DefaultWrappedValue.Type) -> DefaultWrappedValue + var wrappedValue: Int = 10 +} + +// CHECK-LABEL: sil hidden [ossa] @$s22property_wrapper_local20testLocalDefaultInityyF : $@convention(thin) () -> () +func testLocalDefaultInit() { + // CHECK: function_ref @$s22property_wrapper_local11DefaultInitVACycfC : $@convention(method) (@thin DefaultInit.Type) -> DefaultInit + @DefaultInit var x: Int + + // CHECK: function_ref @$s22property_wrapper_local11DefaultInitV5valueACSi_tcfC : $@convention(method) (Int, @thin DefaultInit.Type) -> DefaultInit + @DefaultInit(value: 10) var z: Int + + // CHECK: function_ref @$s22property_wrapper_local11DefaultInitVACycfC : $@convention(method) (@thin DefaultInit.Type) -> DefaultInit + @DefaultInit() var y: Int + + // CHECK: function_ref @$s22property_wrapper_local19DefaultWrappedValueVACycfC : $@convention(method) (@thin DefaultWrappedValue.Type) -> DefaultWrappedValue + @DefaultWrappedValue var w: Int +} diff --git a/test/SILGen/property_wrapper_parameter.swift b/test/SILGen/property_wrapper_parameter.swift index f98dce2dbc710..7210af32c6100 100644 --- a/test/SILGen/property_wrapper_parameter.swift +++ b/test/SILGen/property_wrapper_parameter.swift @@ -1,4 +1,7 @@ -// RUN: %target-swift-emit-silgen %s | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t -enable-library-evolution %S/Inputs/def_structA.swift +// RUN: %target-swift-emit-silgen %s -I %t | %FileCheck %s +import def_structA public struct Projection { public var wrappedValue: T @@ -23,18 +26,18 @@ public struct Wrapper { } } +// property wrapper backing initializer of value #1 in testSimpleWrapperParameter(value:) +// CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper + +// property wrapper init from projected value of value #1 in testSimpleWrapperParameter(value:) +// CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfW : $@convention(thin) (Projection) -> Wrapper + // CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tF : $@convention(thin) (Wrapper) -> () public func testSimpleWrapperParameter(@Wrapper value: Int) { _ = value _ = _value _ = $value - // property wrapper backing initializer of value #1 in testSimpleWrapperParameter(value:) - // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper - - // property wrapper init from projected value of value #1 in testSimpleWrapperParameter(value:) - // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfW : $@convention(thin) (Projection) -> Wrapper - // getter of $value #1 in testSimpleWrapperParameter(value:) // CHECK: sil private [ossa] @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tF6$valueL_AA10ProjectionVySiGvg : $@convention(thin) (Wrapper) -> Projection @@ -49,16 +52,20 @@ func simpleWrapperParameterCaller(projection: Projection) { testSimpleWrapperParameter($value: projection) // CHECK: function_ref @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfW : $@convention(thin) (Projection) -> Wrapper + + var x: Int = 10 + testSimpleWrapperParameter(value: x) + // CHECK: function_ref @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper } -// CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlF : $@convention(thin) (@in_guaranteed Wrapper) -> () -public func testGenericWrapper(@Wrapper value: T) { +// property wrapper backing initializer of value #1 in testGenericWrapper(value:) +// CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfP : $@convention(thin) (@in T) -> @out Wrapper - // property wrapper backing initializer of value #1 in testGenericWrapper(value:) - // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfP : $@convention(thin) (@in T) -> @out Wrapper +// property wrapper init from projected value of value #1 in testGenericWrapper(value:) +// CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfW : $@convention(thin) (@in Projection) -> @out Wrapper - // property wrapper init from projected value of value #1 in testGenericWrapper(value:) - // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfW : $@convention(thin) (@in Projection) -> @out Wrapper +// CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlF : $@convention(thin) (@in_guaranteed Wrapper) -> () +public func testGenericWrapper(@Wrapper value: T) { } // CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter20genericWrapperCaller10projectionyAA10ProjectionVySiG_tF : $@convention(thin) (Projection) -> () @@ -68,6 +75,112 @@ func genericWrapperCaller(projection: Projection) { testGenericWrapper($value: projection) // CHECK: function_ref @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfW : $@convention(thin) <τ_0_0> (@in Projection<τ_0_0>) -> @out Wrapper<τ_0_0> + + var x: Int = 10 + testGenericWrapper(value: x) + // CHECK: function_ref @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfP : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out Wrapper<τ_0_0> +} + +@propertyWrapper +struct ImplementationDetail { + var wrappedValue: T + + // CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter20ImplementationDetailV12wrappedValueACyxGx_tcfC : $@convention(method) (@in T, @thin ImplementationDetail.Type) -> @out ImplementationDetail + init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } +} + + +struct TestStructInit { + // property wrapper backing initializer of number #1 in TestStructInit.init(number:message:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14TestStructInitV6number7messageAcA7WrapperVySiG_SStcfcADL_SivpfP : $@convention(thin) (Int) -> Wrapper + + // property wrapper init from projected value of number #1 in TestStructInit.init(number:message:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14TestStructInitV6number7messageAcA7WrapperVySiG_SStcfcADL_SivpfW : $@convention(thin) (Projection) -> Wrapper + + // CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter14TestStructInitV6number7messageAcA7WrapperVySiG_SStcfC : $@convention(method) (Wrapper, @owned String, @thin TestStructInit.Type) -> TestStructInit + init(@Wrapper number: Int, @ImplementationDetail message: String) { + // CHECK: debug_value %0 : $Wrapper, let, name "_number" + // CHECK: debug_value %1 : $String, let, name "message" + // CHECK: alloc_stack $ImplementationDetail + // CHECK" function_ref @$s26property_wrapper_parameter20ImplementationDetailV12wrappedValueACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin ImplementationDetail<τ_0_0>.Type) -> @out ImplementationDetail<τ_0_0> + + _ = number + _ = _number + + _ = message + _ = _message + + // getter of number #1 in TestStructInit.init(number:message:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14TestStructInitV6number7messageAcA7WrapperVySiG_SStcfcADL_Sivg : $@convention(thin) (Wrapper) -> Int + + // getter of message #1 in TestStructInit.init(number:message:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14TestStructInitV6number7messageAcA7WrapperVySiG_SStcfcAEL_SSvg : $@convention(thin) (@guaranteed ImplementationDetail) -> @owned String + } +} + +class TestClassInit { + // property wrapper backing initializer of number #1 in TestClassInit.init(number:message:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter13TestClassInitC6number7messageAcA7WrapperVySiG_SStcfcADL_SivpfP : $@convention(thin) (Int) -> Wrapper + + // property wrapper init from projected value of number #1 in TestClassInit.init(number:message:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter13TestClassInitC6number7messageAcA7WrapperVySiG_SStcfcADL_SivpfW : $@convention(thin) (Projection) -> Wrapper + + // TestClassInit.__allocating_init(number:message:) + // CHECK-LABEL: sil hidden [exact_self_class] [ossa] @$s26property_wrapper_parameter13TestClassInitC6number7messageAcA7WrapperVySiG_SStcfC : $@convention(method) (Wrapper, @owned String, @thick TestClassInit.Type) -> @owned TestClassInit + // CHECK-NOT: alloc_stack $ImplementationDetail + + // CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter13TestClassInitC6number7messageAcA7WrapperVySiG_SStcfc : $@convention(method) (Wrapper, @owned String, @owned TestClassInit) -> @owned TestClassInit + init(@Wrapper number: Int, @ImplementationDetail message: String) { + // CHECK: debug_value %0 : $Wrapper, let, name "_number" + // CHECK: debug_value %1 : $String, let, name "message" + // CHECK: alloc_stack $ImplementationDetail + + _ = number + _ = _number + + _ = message + _ = _message + + // getter of number #1 in TestClassInit.init(number:message:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter13TestClassInitC6number7messageAcA7WrapperVySiG_SStcfcADL_Sivg : $@convention(thin) (Wrapper) -> Int + + // getter of message #1 in TestClassInit.init(number:message:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter13TestClassInitC6number7messageAcA7WrapperVySiG_SStcfcAEL_SSvg : $@convention(thin) (@guaranteed ImplementationDetail) -> @owned String + } +} + +@propertyWrapper +public struct AutoClosureWrapper { + public var wrappedValue: T + + // CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter18AutoClosureWrapperV12wrappedValueACyxGxyXK_tcfC : $@convention(method) (@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for , @thin AutoClosureWrapper.Type) -> @out AutoClosureWrapper + public init(wrappedValue: @autoclosure () -> T) { + self.wrappedValue = wrappedValue() + } + + public var projectedValue: Projection { + Projection(wrappedValue: wrappedValue) + } + + public init(projectedValue: Projection) { + self.wrappedValue = projectedValue.wrappedValue + } +} + +// property wrapper backing initializer of value #1 in testAutoClosureWrapper(value:) +// CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter22testAutoClosureWrapper5valueyAA0efG0VyxG_tlFACL_xvpfP : $@convention(thin) (@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for ) -> @out AutoClosureWrapper +// CHECK: function_ref @$s26property_wrapper_parameter18AutoClosureWrapperV12wrappedValueACyxGxyXK_tcfC : $@convention(method) <τ_0_0> (@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <τ_0_0>, @thin AutoClosureWrapper<τ_0_0>.Type) -> @out AutoClosureWrapper<τ_0_0> + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter22testAutoClosureWrapper5valueyAA0efG0VyxG_tlF : $@convention(thin) (@in_guaranteed AutoClosureWrapper) -> () +func testAutoClosureWrapper(@AutoClosureWrapper value: T) { +} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter24autoClosureWrapperCalleryyF : $@convention(thin) () -> () +func autoClosureWrapperCaller() { + testAutoClosureWrapper(value: 10) + // CHECK: function_ref @$s26property_wrapper_parameter22testAutoClosureWrapper5valueyAA0efG0VyxG_tlFACL_xvpfP : $@convention(thin) <τ_0_0> (@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <τ_0_0>) -> @out AutoClosureWrapper<τ_0_0> } // CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyF : $@convention(thin) () -> () @@ -205,6 +318,56 @@ func testImplicitPropertyWrapper(projection: ProjectionWrapper) { // CHECK: sil private [ossa] @$s26property_wrapper_parameter27testImplicitPropertyWrapper10projectionyAA010ProjectionG0VySiG_tFSi_AFtAFcfu0_Si_AFtAFcfU0_5valueL_Sivg : $@convention(thin) (ProjectionWrapper) -> Int } +protocol P {} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlF : $@convention(thin) (@in_guaranteed T) -> () +func genericContext(_: T) where T: P { + let _: (ProjectionWrapper) -> Void = { $value in } + + // implicit closure #1 in genericContext(_:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlFyAA17ProjectionWrapperVySiGcfu_ : $@convention(thin) (ProjectionWrapper) -> () + + // This property wrapper generator function should _not_ have a generic signature, + // because the closure doesn't have one. + + // property wrapper init from projected value of $value #1 in closure #1 in implicit closure #1 in genericContext(_:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlFyAA17ProjectionWrapperVySiGcfu_yAFcfU_6$valueL_AFvpfW : $@convention(thin) (ProjectionWrapper) -> ProjectionWrapper + + let _: (ProjectionWrapper) -> T = { $value in + fatalError() + } + + // implicit closure #2 in genericContext(_:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlFxAA17ProjectionWrapperVySiGcfu0_ : $@convention(thin) (ProjectionWrapper) -> @out T + + // This property wrapper generator function _should_ have a generic signature, because + // the closure does have one. + + // property wrapper init from projected value of $value #1 in closure #2 in implicit closure #2 in genericContext(_:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlFxAA17ProjectionWrapperVySiGcfu0_xAFcfU0_6$valueL_AFvpfW : $@convention(thin) (ProjectionWrapper) -> ProjectionWrapper + + // property wrapper backing initializer of a #1 in inner #1 (a:) in genericContext(_:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlF5innerL_1ayAA7WrapperVySiG_tAaCRzlFAEL_SivpfP : $@convention(thin) (Int) -> Wrapper + + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlF5innerL_1ayAA7WrapperVySiG_tAaCRzlF : $@convention(thin) (Wrapper) -> () + func inner(@Wrapper a: Int) {} + + inner(a: 1) + + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlF5innerL0_yyAaCRzlF : $@convention(thin) () -> () + func inner() { _ = T.self } + + // property wrapper backing initializer of b #1 in inner #3 (b:) in genericContext(_:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlF5innerL1_1byAA7WrapperVySiG_tAaCRzlFAEL_SivpfP : $@convention(thin) (Int) -> Wrapper + + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter14genericContextyyxAA1PRzlF5innerL1_1byAA7WrapperVySiG_tAaCRzlF : $@convention(thin) (Wrapper) -> () + func inner(@Wrapper b: Int) { + inner() + } + + inner(b: 1) +} + @propertyWrapper public struct PublicWrapper { public var wrappedValue: T @@ -222,24 +385,24 @@ public struct PublicWrapper { } } +// property wrapper backing initializer of value #1 in publicFunc(value:) +// CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter10publicFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfP : $@convention(thin) (@owned String) -> @owned PublicWrapper + +// property wrapper init from projected value of value #1 in publicFunc(value:) +// CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter10publicFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfW : $@convention(thin) (@owned PublicWrapper) -> @owned PublicWrapper + // CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter10publicFunc5valueyAA13PublicWrapperVySSG_tF : $@convention(thin) (@guaranteed PublicWrapper) -> () public func publicFunc(@PublicWrapper value: String) { - // property wrapper backing initializer of value #1 in publicFunc(value:) - // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter10publicFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfP : $@convention(thin) (@owned String) -> @owned PublicWrapper - - // property wrapper init from projected value of value #1 in publicFunc(value:) - // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter10publicFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfW : $@convention(thin) (@owned PublicWrapper) -> @owned PublicWrapper } -// CHECK-LABEL: sil [serialized] [ossa] @$s26property_wrapper_parameter13inlinableFunc5valueyAA13PublicWrapperVySSG_tF : $@convention(thin) (@guaranteed PublicWrapper) -> () -@inlinable func inlinableFunc(@PublicWrapper value: String) { - // property wrapper backing initializer of value #1 in inlinableFunc(value:) - // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter13inlinableFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfP : $@convention(thin) (@owned String) -> @owned PublicWrapper - - // property wrapper init from projected value of value #1 in inlinableFunc(value:) - // CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter13inlinableFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfW : $@convention(thin) (@owned PublicWrapper) -> @owned PublicWrapper +// property wrapper backing initializer of value #1 in inlinableFunc(value:) +// CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter13inlinableFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfP : $@convention(thin) (@owned String) -> @owned PublicWrapper +// property wrapper init from projected value of value #1 in inlinableFunc(value:) +// CHECK: sil non_abi [serialized] [ossa] @$s26property_wrapper_parameter13inlinableFunc5valueyAA13PublicWrapperVySSG_tFACL_SSvpfW : $@convention(thin) (@owned PublicWrapper) -> @owned PublicWrapper +// CHECK-LABEL: sil [serialized] [ossa] @$s26property_wrapper_parameter13inlinableFunc5valueyAA13PublicWrapperVySSG_tF : $@convention(thin) (@guaranteed PublicWrapper) -> () +@inlinable func inlinableFunc(@PublicWrapper value: String) { _ = publicFunc(value:) // implicit closure #1 in inlinableFunc(value:) @@ -283,3 +446,56 @@ func testNonmutatingSetterSynthesis(@NonmutatingSetter value: Int) { // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter30testNonmutatingSetterSynthesis5valueyAA0eF0VySiG_tFSiAFcfu_SiAFcfU_ACL_Sivs : $@convention(thin) (Int, NonmutatingSetter) -> () // CHECK: function_ref @$s26property_wrapper_parameter17NonmutatingSetterV12wrappedValuexvs : $@convention(method) <τ_0_0> (@in τ_0_0, NonmutatingSetter<τ_0_0>) -> () } + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter38testImplicitWrapperWithResilientStructyyF : $@convention(thin) () -> () +func testImplicitWrapperWithResilientStruct() { + let _: (ProjectionWrapper) -> Void = { $value in } + + // implicit closure #1 in testImplicitWrapperWithResilientStruct() + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter38testImplicitWrapperWithResilientStructyyFyAA010ProjectionF0Vy11def_structA1AVGcfu_ : $@convention(thin) (@in_guaranteed ProjectionWrapper) -> () + // CHECK: [[P:%.*]] = alloc_stack $ProjectionWrapper + // CHECK: copy_addr %0 to [initialization] [[P]] + // CHECK: [[I:%.*]] = function_ref @$s26property_wrapper_parameter38testImplicitWrapperWithResilientStructyyFyAA010ProjectionF0Vy11def_structA1AVGcfu_yAHcfU_6$valueL_AHvpfW : $@convention(thin) (@in ProjectionWrapper) -> @out ProjectionWrapper + // CHECK: apply [[I]]({{.*}}, [[P]]) : $@convention(thin) (@in ProjectionWrapper) -> @out ProjectionWrapper + + // property wrapper init from projected value of $value #1 in closure #1 in implicit closure #1 in testImplicitWrapperWithResilientStruct() + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter38testImplicitWrapperWithResilientStructyyFyAA010ProjectionF0Vy11def_structA1AVGcfu_yAHcfU_6$valueL_AHvpfW : $@convention(thin) (@in ProjectionWrapper) -> @out ProjectionWrapper +} + +func takesAutoclosure(_: @autoclosure () -> Int) {} + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter12testCaptures3ref5valueySi_AA7WrapperVySiGtF : $@convention(thin) (Int, Wrapper) -> () +func testCaptures(@ClassWrapper ref: Int, @Wrapper value: Int) { + takesAutoclosure(ref) + // implicit closure #1 in testCaptures(ref:value:) + // CHECK-LABEL: sil private [transparent] [ossa] @$s26property_wrapper_parameter12testCaptures3ref5valueySi_AA7WrapperVySiGtFSiyXEfu_ : $@convention(thin) (@guaranteed ClassWrapper) -> Int + + let _: () -> Void = { + _ = ref + ref = 100 + } + // closure #1 in testCaptures(ref:value:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter12testCaptures3ref5valueySi_AA7WrapperVySiGtFyycfU_ : $@convention(thin) (@guaranteed ClassWrapper) -> () + + let _: () -> Projection = { $value } + // closure #2 in testCaptures(ref:value:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter12testCaptures3ref5valueySi_AA7WrapperVySiGtFAA10ProjectionVySiGycfU0_ : $@convention(thin) (Wrapper) -> Projection + + let _: (ProjectionWrapper) -> Void = { $x in + _ = { x } + _ = { $x } + } + // Make sure there are 4 closures here with the right arguments + + // implicit closure #2 in testCaptures(ref:value:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter12testCaptures3ref5valueySi_AA7WrapperVySiGtFyAA010ProjectionH0VySiGcfu0_ : $@convention(thin) (ProjectionWrapper) -> () + + // closure #3 in implicit closure #2 in testCaptures(ref:value:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter12testCaptures3ref5valueySi_AA7WrapperVySiGtFyAA010ProjectionH0VySiGcfu0_yAJcfU1_ : $@convention(thin) (ProjectionWrapper) -> () + + // closure #1 in closure #2 in implicit closure #2 in testCaptures(ref:value:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter12testCaptures3ref5valueySi_AA7WrapperVySiGtFyAA010ProjectionH0VySiGcfu0_yAJcfU1_SiycfU_ : $@convention(thin) (ProjectionWrapper) -> Int + + // closure #2 in closure #2 in implicit closure #2 in testCaptures(ref:value:) + // CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter12testCaptures3ref5valueySi_AA7WrapperVySiGtFyAA010ProjectionH0VySiGcfu0_yAJcfU1_AJycfU0_ : $@convention(thin) (ProjectionWrapper) -> ProjectionWrapper +} diff --git a/test/SILGen/property_wrappers.swift b/test/SILGen/property_wrappers.swift index f71702d33d1fa..c464525203500 100644 --- a/test/SILGen/property_wrappers.swift +++ b/test/SILGen/property_wrappers.swift @@ -967,3 +967,18 @@ struct TestAutoclosureComposition { // CHECK-LABEL: sil_vtable [serialized] TestMyWrapper // CHECK: #TestMyWrapper.$useMyWrapper!getter + +@propertyWrapper +struct AutoclosureWrapper { + init(wrappedValue: @autoclosure () -> T) { + self.wrappedValue = wrappedValue() + } + var wrappedValue: T +} + +struct TestReabstractableWrappedValue { + struct S { } + + @AutoclosureWrapper var v: S = S() + init() where T1 == Int { } +} diff --git a/test/SILGen/reabstract.swift b/test/SILGen/reabstract.swift index 2ea1a34ca76b2..a480cfc9b961a 100644 --- a/test/SILGen/reabstract.swift +++ b/test/SILGen/reabstract.swift @@ -85,14 +85,13 @@ func testInoutOpaque(_ c: C, i: Int) { // CHECK: function_ref @$s10reabstract1CCSiIegly_ACSiytIeglnr_TR // CHECK: partial_apply // CHECK: store -// CHECK: load -// CHECK: function_ref @$s10reabstract1CCSiytIeglnr_ACSiIegly_TR -// CHECK: partial_apply -// CHECK: apply +// CHECK: [[CLOSURE:%.*]] = struct_extract {{.*}}, #Box.t +// CHECK: [[CLOSURE1:%.*]] = copy_value [[CLOSURE]] +// CHECK: [[CLOSURE2:%.*]] = begin_borrow [[CLOSURE1]] +// CHECK: apply [[CLOSURE2]] // CHECK: } // end sil function '$s10reabstract15testInoutOpaque_1iyAA1CC_SitF' // CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @$s10reabstract1CCSiIegly_ACSiytIeglnr_TR : $@convention(thin) (@inout C, @in_guaranteed Int, @guaranteed @callee_guaranteed (@inout C, Int) -> ()) -> @out () { -// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @$s10reabstract1CCSiytIeglnr_ACSiIegly_TR : $@convention(thin) (@inout C, Int, @guaranteed @callee_guaranteed (@inout C, @in_guaranteed Int) -> @out ()) -> () { func closureTakingOptional(_ fn: (Int?) -> ()) {} closureTakingOptional({ (_: Any) -> () in }) @@ -107,7 +106,6 @@ closureTakingOptional({ (_: Any) -> () in }) func evenLessFun(_ s: __shared C, _ o: __owned C) {} // CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @$s10reabstract1CCACIeggx_A2CytIegnir_TR : $@convention(thin) (@in_guaranteed C, @in C, @guaranteed @callee_guaranteed (@guaranteed C, @owned C) -> ()) -> @out () -// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @$s10reabstract1CCACytIegnir_A2CIeggx_TR : $@convention(thin) (@guaranteed C, @owned C, @guaranteed @callee_guaranteed (@in_guaranteed C, @in C) -> @out ()) -> () func testSharedOwnedOpaque(_ s: C, o: C) { let box = Box(t: evenLessFun) box.t(s, o) diff --git a/test/SILGen/reasync.swift b/test/SILGen/reasync.swift index 30892ea5775b5..7b214db41ca49 100644 --- a/test/SILGen/reasync.swift +++ b/test/SILGen/reasync.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-silgen %s -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-frontend -emit-silgen %s -enable-experimental-concurrency -disable-availability-checking | %FileCheck %s // REQUIRES: concurrency // CHECK-LABEL: sil hidden [ossa] @$s7reasync0A8FunctionyyyyYaXEYaF : $@convention(thin) @async (@noescape @async @callee_guaranteed () -> ()) -> () { diff --git a/test/SILGen/same_type_abstraction.swift b/test/SILGen/same_type_abstraction.swift index 8cd6a2411d474..1ed05e15044fc 100644 --- a/test/SILGen/same_type_abstraction.swift +++ b/test/SILGen/same_type_abstraction.swift @@ -12,7 +12,8 @@ struct S1 {} struct S2 {} // CHECK-LABEL: sil hidden [ossa] @$s21same_type_abstraction28callClosureWithConcreteTypes{{[_0-9a-zA-Z]*}}F -// CHECK: function_ref @$s{{.*}}TR : +// CHECK-NOT: function_ref @$s{{.*}}TR : +// CHECK: apply {{.*}} : ${{.*}}(@in_guaranteed func callClosureWithConcreteTypes(x: Abstracted, arg: S1) -> S2 where T.Assoc == S1, U.Assoc == S2 { return x.closure(arg) } diff --git a/test/SILGen/sil_locations.swift b/test/SILGen/sil_locations.swift index 129fa33bc0f65..bf93823aa55e8 100644 --- a/test/SILGen/sil_locations.swift +++ b/test/SILGen/sil_locations.swift @@ -417,3 +417,29 @@ func testWhile() { } + +// Check that the sil location of keypath getter/setter functions is +// marked as autogenerated. +struct Struct { + var structProperty: InnerStruct { + get { InnerStruct() } + set(newStruct) { } + } + + struct InnerStruct {} +} + +func testKeyPathGetterSetterAutogen() -> Struct.InnerStruct { + let kp = \Struct.structProperty + var s = Struct() + let innerS = Struct.InnerStruct() + + s[keyPath: kp] = innerS + return s[keyPath: kp] + // Autogenerated keypath getter + // CHECK-LABEL: sil shared [thunk] [ossa] @$s13sil_locations6StructV14structProperty{{[_0-9a-zA-Z]*}}TK + // CHECK: load {{.*}} loc "":0:0{{.*}}:auto_gen + // Autogenerated keypath setter + // CHECK-LABEL: sil shared [thunk] [ossa] @$s13sil_locations6StructV14structProperty{{[_0-9a-zA-Z]*}}Tk + // CHECK: load {{.*}} loc "":0:0{{.*}}:auto_gen +} diff --git a/test/SILGen/switch_multiple_entry_address_only.swift b/test/SILGen/switch_multiple_entry_address_only.swift index 507986eb13e0a..7718086965fc3 100644 --- a/test/SILGen/switch_multiple_entry_address_only.swift +++ b/test/SILGen/switch_multiple_entry_address_only.swift @@ -91,7 +91,7 @@ func multipleLabelsVar(e: E) { // CHECK-NEXT: br bb3 // CHECK: bb3: - // CHECK-NEXT: debug_value_addr [[X_PHI]] : $*Any, var, name "x" + // CHECK-NEXT: debug_value [[X_PHI]] : $*Any, var, name "x" // CHECK-NEXT: [[ANY_BOX:%.*]] = alloc_box ${ var Any }, var, name "x" // CHECK-NEXT: [[BOX_PAYLOAD:%.*]] = project_box [[ANY_BOX]] : ${ var Any }, 0 // CHECK-NEXT: copy_addr [take] [[X_PHI]] to [initialization] [[BOX_PAYLOAD]] diff --git a/test/SILGen/unmanaged.swift b/test/SILGen/unmanaged.swift index 9cc6016a7b5f2..4b98735303ec7 100644 --- a/test/SILGen/unmanaged.swift +++ b/test/SILGen/unmanaged.swift @@ -35,7 +35,7 @@ func get(holder holder: inout Holder) -> C { } // CHECK-LABEL:sil hidden @$s9unmanaged3get6holderAA1CCAA6HolderVz_tF : $@convention(thin) (@inout Holder) -> @owned C // CHECK: bb0([[ADDR:%.*]] : $*Holder): -// CHECK-NEXT: debug_value_addr %0 : $*Holder, var, name "holder", argno 1 +// CHECK-NEXT: debug_value %0 : $*Holder, var, name "holder", argno 1, expr op_deref // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [static] [[ADDR]] : $*Holder // CHECK-NEXT: [[T0:%.*]] = struct_element_addr [[READ]] : $*Holder, #Holder.value // CHECK-NEXT: [[T1:%.*]] = load [[T0]] : $*@sil_unmanaged C diff --git a/test/SILGen/unmanaged_ownership.swift b/test/SILGen/unmanaged_ownership.swift index ee69cfeaeff56..f415ae2903713 100644 --- a/test/SILGen/unmanaged_ownership.swift +++ b/test/SILGen/unmanaged_ownership.swift @@ -46,7 +46,7 @@ func get(holder holder: inout Holder) -> C { } // CHECK-LABEL: sil hidden [ossa] @$ss3get6holders1CCs6HolderVz_tF : $@convention(thin) (@inout Holder) -> @owned C { // CHECK: bb0([[ADDR:%.*]] : $*Holder): -// CHECK-NEXT: debug_value_addr %0 : $*Holder, var, name "holder", argno 1 +// CHECK-NEXT: debug_value %0 : $*Holder, var, name "holder", argno 1, expr op_deref // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] [[ADDR]] : $*Holder // CHECK-NEXT: [[T0:%.*]] = struct_element_addr [[READ]] : $*Holder, #Holder.value // CHECK-NEXT: [[T1:%.*]] = load [trivial] [[T0]] : $*@sil_unmanaged C diff --git a/test/SILGen/unreachable_code.swift b/test/SILGen/unreachable_code.swift index ed016b71105f9..d89d06ed9c867 100644 --- a/test/SILGen/unreachable_code.swift +++ b/test/SILGen/unreachable_code.swift @@ -154,4 +154,5 @@ func sr13639() -> Int { // CHECK: sil private @$s16unreachable_code7sr13639SiyF3FooL_V7fooFuncyyF : $@convention(method) (Foo) -> () func fooFunc() {} } + func appendix() {} // no-warning } diff --git a/test/SILGen/weak.swift b/test/SILGen/weak.swift index 94aad290d526e..3bbf4929dccbf 100644 --- a/test/SILGen/weak.swift +++ b/test/SILGen/weak.swift @@ -55,7 +55,7 @@ func test0(c c: C) { // CHECK-LABEL: sil private [ossa] @$s4weak19testClosureOverWeakyyFSiycfU_ : $@convention(thin) (@guaranteed { var @sil_weak Optional }) -> Int { // CHECK: bb0(%0 : @guaranteed ${ var @sil_weak Optional }): // CHECK-NEXT: %1 = project_box %0 -// CHECK-NEXT: debug_value_addr %1 : $*@sil_weak Optional, var, name "bC", argno 1 +// CHECK-NEXT: debug_value %1 : $*@sil_weak Optional, var, name "bC", argno 1, expr op_deref // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] %1 // CHECK-NEXT: [[VAL:%.*]] = load_weak [[READ]] : $*@sil_weak Optional // CHECK-NEXT: end_access [[READ]] diff --git a/test/SILGen/witness-modify-requirement-with-base-class-modify.swift b/test/SILGen/witness-modify-requirement-with-base-class-modify.swift new file mode 100644 index 0000000000000..6186720b48a86 --- /dev/null +++ b/test/SILGen/witness-modify-requirement-with-base-class-modify.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +public protocol P { + associatedtype A +} + +public class Base { + public var foo: T.A? +} + +public struct S {} + +public protocol Q { + var foo: S? {set get} +} + +public class Derived : Base, Q where T.A == S {} + +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [ossa] @$s4main7DerivedCyxGAA1QA2aEP3fooAA1SVSgvgTW : $@convention(witness_method: Q) <τ_0_0 where τ_0_0 : P, τ_0_0.A == S> (@in_guaranteed Derived<τ_0_0>) -> Optional { +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [ossa] @$s4main7DerivedCyxGAA1QA2aEP3fooAA1SVSgvsTW : $@convention(witness_method: Q) <τ_0_0 where τ_0_0 : P, τ_0_0.A == S> (Optional, @inout Derived<τ_0_0>) -> () { +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [ossa] @$s4main7DerivedCyxGAA1QA2aEP3fooAA1SVSgvMTW : $@yield_once @convention(witness_method: Q) <τ_0_0 where τ_0_0 : P, τ_0_0.A == S> @substituted <τ_0_0> (@inout τ_0_0) -> @yields @inout Optional for > { + +// CHECK-LABEL: sil_witness_table [serialized] Derived: Q module main { +// CHECK-NEXT: method #Q.foo!getter: (Self) -> () -> S? : @$s4main7DerivedCyxGAA1QA2aEP3fooAA1SVSgvgTW +// CHECK-NEXT: method #Q.foo!setter: (inout Self) -> (S?) -> () : @$s4main7DerivedCyxGAA1QA2aEP3fooAA1SVSgvsTW +// CHECK-NEXT: method #Q.foo!modify: (inout Self) -> () -> () : @$s4main7DerivedCyxGAA1QA2aEP3fooAA1SVSgvMTW +// CHECK-NEXT: } \ No newline at end of file diff --git a/test/SILOptimizer/Inputs/cross-module/c-module.c b/test/SILOptimizer/Inputs/cross-module/c-module.c new file mode 100644 index 0000000000000..808c26c020781 --- /dev/null +++ b/test/SILOptimizer/Inputs/cross-module/c-module.c @@ -0,0 +1,7 @@ + +#include "c-module.h" + +long privateCFunc() { + return 123; +} + diff --git a/test/SILOptimizer/Inputs/cross-module/c-module.h b/test/SILOptimizer/Inputs/cross-module/c-module.h new file mode 100644 index 0000000000000..ef27e8d9c2114 --- /dev/null +++ b/test/SILOptimizer/Inputs/cross-module/c-module.h @@ -0,0 +1,3 @@ + +long privateCFunc(); + diff --git a/test/SILOptimizer/Inputs/cross-module-objc.swift b/test/SILOptimizer/Inputs/cross-module/cross-module-objc.swift similarity index 100% rename from test/SILOptimizer/Inputs/cross-module-objc.swift rename to test/SILOptimizer/Inputs/cross-module/cross-module-objc.swift diff --git a/test/SILOptimizer/Inputs/cross-module.swift b/test/SILOptimizer/Inputs/cross-module/cross-module.swift similarity index 94% rename from test/SILOptimizer/Inputs/cross-module.swift rename to test/SILOptimizer/Inputs/cross-module/cross-module.swift index 68d5c0b8aba72..09a38be6e1cee 100644 --- a/test/SILOptimizer/Inputs/cross-module.swift +++ b/test/SILOptimizer/Inputs/cross-module/cross-module.swift @@ -1,5 +1,6 @@ import Submodule @_implementationOnly import PrivateSubmodule +@_implementationOnly import PrivateCModule private enum PE { case A @@ -268,11 +269,19 @@ public func callUnrelated(_ t: T) -> T { return t } -public func callImplementationOnly(_ t: T) -> T { +public func callImplementationOnlyType(_ t: T) -> T { let p = PrivateStr(i: 27) print(p.test()) return t } +public func callImplementationOnlyFunc(_ t: T) -> Int { + return privateFunc() +} + +public func callCImplementationOnly(_ t: T) -> Int { + return Int(privateCFunc()) +} + public let globalLet = 529387 diff --git a/test/SILOptimizer/Inputs/cross-private-submodule.swift b/test/SILOptimizer/Inputs/cross-module/cross-private-submodule.swift similarity index 75% rename from test/SILOptimizer/Inputs/cross-private-submodule.swift rename to test/SILOptimizer/Inputs/cross-module/cross-private-submodule.swift index f43a8e65e2ee8..2de51eafa7664 100644 --- a/test/SILOptimizer/Inputs/cross-private-submodule.swift +++ b/test/SILOptimizer/Inputs/cross-module/cross-private-submodule.swift @@ -12,3 +12,6 @@ public struct PrivateStr { } } +public func privateFunc() -> Int { + return 40 +} diff --git a/test/SILOptimizer/Inputs/cross-submodule.swift b/test/SILOptimizer/Inputs/cross-module/cross-submodule.swift similarity index 100% rename from test/SILOptimizer/Inputs/cross-submodule.swift rename to test/SILOptimizer/Inputs/cross-module/cross-submodule.swift diff --git a/test/SILOptimizer/Inputs/cross-module/module.modulemap b/test/SILOptimizer/Inputs/cross-module/module.modulemap new file mode 100644 index 0000000000000..ef50f56508135 --- /dev/null +++ b/test/SILOptimizer/Inputs/cross-module/module.modulemap @@ -0,0 +1,3 @@ +module PrivateCModule { + header "c-module.h" +} diff --git a/test/SILOptimizer/abcopt_large_cfg.sil.gyb b/test/SILOptimizer/abcopt_large_cfg.sil.gyb new file mode 100644 index 0000000000000..a2d2db24ef130 --- /dev/null +++ b/test/SILOptimizer/abcopt_large_cfg.sil.gyb @@ -0,0 +1,84 @@ +// RUN: %empty-directory(%t) +// RUN: %gyb %s > %t/main.sil + +// Check that the optimization does not crash due to a stack overflow. + +// RUN: %target-sil-opt -sil-verify-none -abcopts %t/main.sil | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +struct ArrayIntBuffer { + var storage : Builtin.NativeObject +} + +struct ArrayInt{ + var buffer : ArrayIntBuffer +} + +sil [ossa] @take_array : $@convention(thin) (@inout ArrayInt) -> () { +bb0(%0 : $*ArrayInt): + unreachable +} + + +sil public_external [ossa] [_semantics "array.check_subscript"] @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken { +bb0(%0: $Int32, %1: $Bool, %2: @owned $ArrayInt): + unreachable +} + +// CHECK-LABEL: sil [ossa] @test_very_deep_domtree : + +// Currently there is nothing ot check here, because the optimization bails in +// this case. +// In future we might check that even with a deep domtree it can hoist the check. + +// CHECK-LABEL: } // end sil function 'test_very_deep_domtree' +sil [ossa] @test_very_deep_domtree : $@convention(thin) (Int32, @inout ArrayInt) -> Int32 { +bb0(%0 : $Int32, %1 : $*ArrayInt): + %%2 = integer_literal $Builtin.Int1, -1 + %%3 = struct $Bool (%2 : $Builtin.Int1) + %%4 = struct_extract %0 : $Int32, #Int32._value + %%5 = integer_literal $Builtin.Int32, 0 + br bb1(%5 : $Builtin.Int32) + +bb1(%10 : $Builtin.Int32): + +% for i in range(50000): + br bb${i+2} +bb${i+2}: +% end + + br bb200000 + +bb200000: + %%11 = struct $Int32 (%10 : $Builtin.Int32) + %%12 = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken + %%13 = load [copy] %1 : $*ArrayInt + %%17 = apply %12(%11, %3, %13) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken + %%18 = integer_literal $Builtin.Int32, 1 + %%19 = integer_literal $Builtin.Int1, -1 + %%20 = builtin "sadd_with_overflow_Int32"(%10 : $Builtin.Int32, %18 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) + %%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0 + %%22 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 1 + cond_fail %22 : $Builtin.Int1, "" + %%24 = builtin "cmp_eq_Int32"(%21 : $Builtin.Int32, %4 : $Builtin.Int32) : $Builtin.Int1 + cond_br %24, bb200002, bb200001 + +bb200001: + br bb1(%21 : $Builtin.Int32) + +bb200002: + %%27 = function_ref @take_array : $@convention(thin) (@inout ArrayInt) -> () + %%28 = apply %27(%1) : $@convention(thin) (@inout ArrayInt) -> () + %%29 = load [copy] %1 : $*ArrayInt + %%30 = apply %12(%11, %3, %29) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken + %%33 = struct $Int32 (%21 : $Builtin.Int32) + return %33 : $Int32 +} + + + diff --git a/test/SILOptimizer/access_enforcement_opts.sil b/test/SILOptimizer/access_enforcement_opts.sil index 5f644b6a3411d..77589d4cb6873 100644 --- a/test/SILOptimizer/access_enforcement_opts.sil +++ b/test/SILOptimizer/access_enforcement_opts.sil @@ -564,7 +564,7 @@ sil private @$s17enforce_with_opts24testInoutWriteEscapeReadyyFyycfU_ : $@conven // %0 bb0(%0 : ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = begin_access [read] [dynamic] %1 : $*Int64 %4 = load %3 : $*Int64 end_access %3 : $*Int64 @@ -641,7 +641,7 @@ sil private @$s17enforce_with_opts020testInoutWriteEscapeF0yyFyycfU_ : $@convent // %0 bb0(%0 : ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = integer_literal $Builtin.Int64, 42 %4 = struct $Int64 (%3 : $Builtin.Int64) %5 = begin_access [modify] [dynamic] %1 : $*Int64 @@ -696,7 +696,7 @@ sil private @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyycfU_ : $@ // %0 bb0(%0 : ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = begin_access [read] [dynamic] %1 : $*Int64 %4 = load %3 : $*Int64 end_access %3 : $*Int64 @@ -716,7 +716,7 @@ sil private @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyyXEfU0_ : // %0 // %1 bb0(%0 : $*Int64, %1 : $@callee_guaranteed () -> ()): - debug_value_addr %0 : $*Int64, var, name "x", argno 1 + debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2 %4 = begin_access [read] [dynamic] %0 : $*Int64 %5 = tuple () @@ -772,7 +772,7 @@ sil private @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyycfU_ : // %0 bb0(%0 : ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = integer_literal $Builtin.Int64, 7 %4 = struct $Int64 (%3 : $Builtin.Int64) %5 = begin_access [modify] [dynamic] %1 : $*Int64 @@ -788,7 +788,7 @@ bb0(%0 : ${ var Int64 }): // CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_' sil private @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () { bb0(%0 : $*Int64, %1 : $@callee_guaranteed () -> ()): - debug_value_addr %0 : $*Int64, var, name "x", argno 1 + debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2 %4 = begin_access [read] [dynamic] %0 : $*Int64 %5 = tuple () @@ -844,7 +844,7 @@ sil private @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyy // %0 bb0(%0 : ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = begin_access [read] [dynamic] %1 : $*Int64 %4 = load %3 : $*Int64 end_access %3 : $*Int64 @@ -864,7 +864,7 @@ sil private @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyy // %0 // %1 bb0(%0 : $*Int64, %1 : $@callee_guaranteed () -> ()): - debug_value_addr %0 : $*Int64, var, name "x", argno 1 + debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2 %4 = begin_access [modify] [dynamic] %0 : $*Int64 %5 = tuple () @@ -918,7 +918,7 @@ sil private @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyc // %0 bb0(%0 : ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = integer_literal $Builtin.Int64, 7 %4 = struct $Int64 (%3 : $Builtin.Int64) %5 = begin_access [modify] [dynamic] %1 : $*Int64 @@ -934,7 +934,7 @@ bb0(%0 : ${ var Int64 }): // CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_' sil private @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () { bb0(%0 : $*Int64, %1 : $@callee_guaranteed () -> ()): - debug_value_addr %0 : $*Int64, var, name "x", argno 1 + debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2 %4 = begin_access [modify] [dynamic] %0 : $*Int64 %5 = tuple () diff --git a/test/SILOptimizer/access_enforcement_opts_ossa.sil b/test/SILOptimizer/access_enforcement_opts_ossa.sil index 2b28bd1ced012..dd2ec4798463b 100644 --- a/test/SILOptimizer/access_enforcement_opts_ossa.sil +++ b/test/SILOptimizer/access_enforcement_opts_ossa.sil @@ -569,7 +569,7 @@ sil private [ossa] @$s17enforce_with_opts24testInoutWriteEscapeReadyyFyycfU_ : $ // %0 bb0(%0 : @guaranteed ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = begin_access [read] [dynamic] %1 : $*Int64 %4 = load [trivial] %3 : $*Int64 end_access %3 : $*Int64 @@ -646,7 +646,7 @@ sil private [ossa] @$s17enforce_with_opts020testInoutWriteEscapeF0yyFyycfU_ : $@ // %0 bb0(%0 : @guaranteed ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = integer_literal $Builtin.Int64, 42 %4 = struct $Int64 (%3 : $Builtin.Int64) %5 = begin_access [modify] [dynamic] %1 : $*Int64 @@ -701,7 +701,7 @@ sil private [ossa] @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyycf // %0 bb0(%0 : @guaranteed ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = begin_access [read] [dynamic] %1 : $*Int64 %4 = load [trivial] %3 : $*Int64 end_access %3 : $*Int64 @@ -721,7 +721,7 @@ sil private [ossa] @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyyXE // %0 // %1 bb0(%0 : $*Int64, %1 : @guaranteed $@callee_guaranteed () -> ()): - debug_value_addr %0 : $*Int64, var, name "x", argno 1 + debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2 %4 = begin_access [read] [dynamic] %0 : $*Int64 %5 = tuple () @@ -777,7 +777,7 @@ sil private [ossa] @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyy // %0 bb0(%0 : @guaranteed ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = integer_literal $Builtin.Int64, 7 %4 = struct $Int64 (%3 : $Builtin.Int64) %5 = begin_access [modify] [dynamic] %1 : $*Int64 @@ -793,7 +793,7 @@ bb0(%0 : @guaranteed ${ var Int64 }): // CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_' sil private [ossa] @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () { bb0(%0 : $*Int64, %1 : @guaranteed $@callee_guaranteed () -> ()): - debug_value_addr %0 : $*Int64, var, name "x", argno 1 + debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2 %4 = begin_access [read] [dynamic] %0 : $*Int64 %5 = tuple () @@ -849,7 +849,7 @@ sil private [ossa] @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosu // %0 bb0(%0 : @guaranteed ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = begin_access [read] [dynamic] %1 : $*Int64 %4 = load [trivial] %3 : $*Int64 end_access %3 : $*Int64 @@ -869,7 +869,7 @@ sil private [ossa] @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosu // %0 // %1 bb0(%0 : $*Int64, %1 : @guaranteed $@callee_guaranteed () -> ()): - debug_value_addr %0 : $*Int64, var, name "x", argno 1 + debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2 %4 = begin_access [modify] [dynamic] %0 : $*Int64 %5 = tuple () @@ -923,7 +923,7 @@ sil private [ossa] @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7Closur // %0 bb0(%0 : @guaranteed ${ var Int64 }): %1 = project_box %0 : ${ var Int64 }, 0 - debug_value_addr %1 : $*Int64, var, name "x", argno 1 + debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref %3 = integer_literal $Builtin.Int64, 7 %4 = struct $Int64 (%3 : $Builtin.Int64) %5 = begin_access [modify] [dynamic] %1 : $*Int64 @@ -939,7 +939,7 @@ bb0(%0 : @guaranteed ${ var Int64 }): // CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_' sil private [ossa] @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () { bb0(%0 : $*Int64, %1 : @guaranteed $@callee_guaranteed () -> ()): - debug_value_addr %0 : $*Int64, var, name "x", argno 1 + debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2 %4 = begin_access [modify] [dynamic] %0 : $*Int64 %5 = tuple () @@ -1817,3 +1817,18 @@ bb0(%0 : @guaranteed $Klass, %1 : $*Int64): return %14 : $Builtin.NativeObject } +class Bar { + func foo() +} + +sil [ossa] @$testEndLifetime : $@convention(method) (@owned Bar, @in Bar) -> () { +bb0(%0 : @owned $Bar, %1 : $*Bar): + %b = begin_access [modify] [dynamic] %1 : $*Bar + end_access %b : $*Bar + %f = objc_method %0 : $Bar, #Bar.foo!foreign : (Bar) -> () -> (), $@convention(objc_method) (Bar) -> () + %c = apply %f(%0) : $@convention(objc_method) (Bar) -> () + end_lifetime %0 : $Bar + destroy_addr %1 : $*Bar + %r = tuple () + return %r : $() +} diff --git a/test/SILOptimizer/access_marker_verify.swift b/test/SILOptimizer/access_marker_verify.swift index 9b6e06027636d..e6c1fc218ad6d 100644 --- a/test/SILOptimizer/access_marker_verify.swift +++ b/test/SILOptimizer/access_marker_verify.swift @@ -442,8 +442,8 @@ func testEnumPattern(ie: IndirectEnum) -> Bool { // CHECK: switch_enum %{{.*}} : $IndirectEnum, case #IndirectEnum.V!enumelt: [[BBV:bb.*]], default bb // CHECK: [[BBV]](%{{.*}} : @owned ${ var Int }): // CHECK: [[PROJ:%.*]] = project_box -// CHECK-NOT: begin_access -// CHECK: load [trivial] [[PROJ]] : $*Int +// CHECK: [[ACCESS:%.*]] = begin_access [read] [unsafe] [[PROJ]] +// CHECK: load [trivial] [[ACCESS]] : $*Int // CHECK-LABEL: } // end sil function '$s20access_marker_verify15testEnumPattern2ieSbAA08IndirectE0O_tF' // --- enum LValue. diff --git a/test/SILOptimizer/accessed_storage_analysis.sil b/test/SILOptimizer/accessed_storage_analysis.sil index af3f4dc5ff1c9..f539ca2b93aa6 100644 --- a/test/SILOptimizer/accessed_storage_analysis.sil +++ b/test/SILOptimizer/accessed_storage_analysis.sil @@ -67,7 +67,7 @@ bb0(%0 : $Int): } // CHECK-LABEL: @readIdentifiedBoxArg -// CHECK: [read] Argument %0 = argument of bb0 : ${ var Int } +// CHECK: [read] Box %0 = argument of bb0 : ${ var Int } sil @readIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }) -> Int { bb0(%0 : ${ var Int }): %1 = project_box %0 : ${ var Int }, 0 @@ -78,7 +78,7 @@ bb0(%0 : ${ var Int }): } // CHECK-LABEL: @writeIdentifiedBoxArg -// CHECK: [modify] Argument %0 = argument of bb0 : ${ var Int } +// CHECK: [modify] Box %0 = argument of bb0 : ${ var Int } sil @writeIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> () { bb0(%0 : ${ var Int }, %1 : $Int): %2 = project_box %0 : ${ var Int }, 0 @@ -90,7 +90,7 @@ bb0(%0 : ${ var Int }, %1 : $Int): } // CHECK-LABEL: @readWriteIdentifiedBoxArg -// CHECK: [modify] Argument %0 = argument of bb0 : ${ var Int } +// CHECK: [modify] Box %0 = argument of bb0 : ${ var Int } sil @readWriteIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> Int { bb0(%0 : ${ var Int }, %1 : $Int): %2 = function_ref @writeIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> () @@ -634,7 +634,7 @@ enum IndirectEnum { } // CHECK-LABEL: @readUnidentifiedArgCaller -// CHECK: unidentified accesses: read +// CHECK: Box %{{.*}} = argument of bb2 : ${ var Int } sil @readUnidentifiedArgCaller : $@convention(thin) (@guaranteed IndirectEnum, Int) -> Int { bb0(%0 : $IndirectEnum, %1 : $Int): switch_enum %0 : $IndirectEnum, case #IndirectEnum.V!enumelt: bb2, default bb1 diff --git a/test/SILOptimizer/accessed_storage_analysis_ossa.sil b/test/SILOptimizer/accessed_storage_analysis_ossa.sil index 8ea21fe6fc6b6..f32189a19319a 100644 --- a/test/SILOptimizer/accessed_storage_analysis_ossa.sil +++ b/test/SILOptimizer/accessed_storage_analysis_ossa.sil @@ -67,7 +67,7 @@ bb0(%0 : $Int): } // CHECK-LABEL: @readIdentifiedBoxArg -// CHECK: [read] Argument %0 = argument of bb0 : ${ var Int } +// CHECK: [read] Box %0 = argument of bb0 : ${ var Int } sil [ossa] @readIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }) -> Int { bb0(%0 : @guaranteed ${ var Int }): %1 = project_box %0 : ${ var Int }, 0 @@ -78,7 +78,7 @@ bb0(%0 : @guaranteed ${ var Int }): } // CHECK-LABEL: @writeIdentifiedBoxArg -// CHECK: [modify] Argument %0 = argument of bb0 : ${ var Int } +// CHECK: [modify] Box %0 = argument of bb0 : ${ var Int } sil [ossa] @writeIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> () { bb0(%0 : @guaranteed ${ var Int }, %1 : $Int): %2 = project_box %0 : ${ var Int }, 0 @@ -90,7 +90,7 @@ bb0(%0 : @guaranteed ${ var Int }, %1 : $Int): } // CHECK-LABEL: @readWriteIdentifiedBoxArg -// CHECK: [modify] Argument %0 = argument of bb0 : ${ var Int } +// CHECK: [modify] Box %0 = argument of bb0 : ${ var Int } sil [ossa] @readWriteIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> Int { bb0(%0 : @guaranteed ${ var Int }, %1 : $Int): %2 = function_ref @writeIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> () @@ -639,7 +639,7 @@ enum IndirectEnum { } // CHECK-LABEL: @readUnidentifiedArgCaller -// CHECK: unidentified accesses: read +// CHECK: [read] Box %{{.*}} = argument of bb2 : ${ var Int } sil [ossa] @readUnidentifiedArgCaller : $@convention(thin) (@guaranteed IndirectEnum, Int) -> Int { bb0(%0 : @guaranteed $IndirectEnum, %1 : $Int): switch_enum %0 : $IndirectEnum, case #IndirectEnum.V!enumelt: bb2, default bb1 diff --git a/test/SILOptimizer/accesspath_uses_ossa.sil b/test/SILOptimizer/accesspath_uses_ossa.sil index c71758aaf9258..8cf24b69b3a54 100644 --- a/test/SILOptimizer/accesspath_uses_ossa.sil +++ b/test/SILOptimizer/accesspath_uses_ossa.sil @@ -416,13 +416,13 @@ enum IntTEnum { // CHECK: Storage: Argument %0 = argument of bb0 : $*IntTEnum // CHECK: Path: () // CHECK: Exact Uses { -// CHECK-NEXT: debug_value_addr %0 : $*IntTEnum, let, name "self", argno 1 +// CHECK-NEXT: debug_value %0 : $*IntTEnum, let, name "self", argno 1, expr op_deref // CHECK-NEXT: Path: () // CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum // CHECK-NEXT: Path: () // CHECK-NEXT: } // CHECK: Overlapping Uses { -// CHECK-NEXT: debug_value_addr %0 : $*IntTEnum, let, name "self", argno 1 +// CHECK-NEXT: debug_value %0 : $*IntTEnum, let, name "self", argno 1, expr op_deref // CHECK-NEXT: Path: () // CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum // CHECK-NEXT: Path: () @@ -566,7 +566,7 @@ enum IntTEnum { // CHECK: } sil hidden [ossa] @testEnumUses : $@convention(method) (@in_guaranteed IntTEnum) -> Int { bb0(%0 : $*IntTEnum): - debug_value_addr %0 : $*IntTEnum, let, name "self", argno 1 + debug_value %0 : $*IntTEnum, let, name "self", argno 1, expr op_deref %2 = alloc_stack $IntTEnum copy_addr %0 to [initialization] %2 : $*IntTEnum switch_enum_addr %2 : $*IntTEnum, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2 @@ -943,3 +943,73 @@ bb0(%0 : @owned $AnyObject): dealloc_stack %1 : $*Optional return %5 : $Optional } + +class Klass { +} + +struct NonTrivialStruct { + var klass: Klass +} + +sil [ossa] @get_unsafe_struct : $@convention(thin) () -> UnsafeMutablePointer + +// TODO: Since builtin "truncOrBitCast_Int64_Word" is on a literal, we should be able to get an access path of (#0) +// CHECK-LABEL: @test_accesspath_offset1 +// CHECK: ###For MemOp: %8 = load_borrow %7 : $*Klass +// CHECK: Unidentified %3 = struct_extract %2 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Base: %3 = struct_extract %2 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Storage: Unidentified %3 = struct_extract %2 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue +// CHECK: Path: (@Unknown,#0) +// CHECK: Exact Uses { +// CHECK: } +// CHECK: Inner Uses { +// CHECK: } +// CHECK: Overlapping Uses { +// CHECK: %8 = load_borrow %7 : $*Klass +// CHECK: Path: (@Unknown,#0) +// CHECK: } +sil [ossa] @test_accesspath_offset1 : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int64, 0 + %1 = function_ref @get_unsafe_struct : $@convention(thin) () -> UnsafeMutablePointer + %2 = apply %1() : $@convention(thin) () -> UnsafeMutablePointer + %3 = struct_extract %2 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue + %4 = builtin "truncOrBitCast_Int64_Word"(%0 : $Builtin.Int64) : $Builtin.Word + %5 = pointer_to_address %3 : $Builtin.RawPointer to [strict] $*NonTrivialStruct + %6 = index_addr %5 : $*NonTrivialStruct, %4 : $Builtin.Word + %7 = struct_element_addr %6 : $*NonTrivialStruct, #NonTrivialStruct.klass + %8 = load_borrow %7 : $*Klass + end_borrow %8 : $Klass + %ret = tuple () + return %ret : $() +} + +// CHECK-LABEL: @test_accesspath_offset2 +// CHECK: ###For MemOp: %8 = load_borrow %7 : $*Klass // user: %9 +// CHECK: Unidentified %3 = struct_extract %2 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue // user: %5 +// CHECK: Base: %3 = struct_extract %2 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue // user: %5 +// CHECK: Storage: Unidentified %3 = struct_extract %2 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue // user: %5 +// CHECK: Path: (@Unknown,#0) +// CHECK: Exact Uses { +// CHECK: } +// CHECK: Inner Uses { +// CHECK: } +// CHECK: Overlapping Uses { +// CHECK: %8 = load_borrow %7 : $*Klass // user: %9 +// CHECK: Path: (@Unknown,#0) +// CHECK: } +sil [ossa] @test_accesspath_offset2 : $@convention(thin) (Builtin.Int64) -> () { +bb0(%0 : $Builtin.Int64): + %1 = function_ref @get_unsafe_struct : $@convention(thin) () -> UnsafeMutablePointer + %2 = apply %1() : $@convention(thin) () -> UnsafeMutablePointer + %3 = struct_extract %2 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue + %4 = builtin "truncOrBitCast_Int64_Word"(%0 : $Builtin.Int64) : $Builtin.Word + %5 = pointer_to_address %3 : $Builtin.RawPointer to [strict] $*NonTrivialStruct + %6 = index_addr %5 : $*NonTrivialStruct, %4 : $Builtin.Word + %7 = struct_element_addr %6 : $*NonTrivialStruct, #NonTrivialStruct.klass + %8 = load_borrow %7 : $*Klass + end_borrow %8 : $Klass + %ret = tuple () + return %ret : $() +} + diff --git a/test/SILOptimizer/address_lowering.sil b/test/SILOptimizer/address_lowering.sil index e63619d1a7171..c4338e72b28eb 100644 --- a/test/SILOptimizer/address_lowering.sil +++ b/test/SILOptimizer/address_lowering.sil @@ -154,9 +154,9 @@ bb0(%0 : $T): // CHECK: %[[PREV1:.*]] = alloc_stack $T // CHECK: %[[ARG2:.*]] = alloc_stack $T // CHECK: %[[PREV2:.*]] = alloc_stack $T -// CHECK: debug_value_addr %0 : $*T, var, name "t", argno 1 -// CHECK: debug_value_addr %1 : $*T, var, name "u", argno 2 -// CHECK: debug_value_addr %2 : $*T +// CHECK: debug_value %0 : $*T, var, name "t", argno 1, expr op_deref +// CHECK: debug_value %1 : $*T, var, name "u", argno 2, expr op_deref +// CHECK: debug_value %2 : $*T, {{.*}} expr op_deref // CHECK: copy_addr %2 to [initialization] %[[ARG1]] : $*T // CHECK: copy_addr [take] %0 to [initialization] %[[PREV1]] : $*T // CHECK: copy_addr [take] %[[ARG1]] to [initialization] %0 : $*T @@ -175,8 +175,8 @@ bb0(%0 : $T): // CHECK-LABEL: } // end sil function 'f050_storeinout' sil @f050_storeinout : $@convention(thin) (@inout T, @inout T, @in T) -> () { bb0(%0 : $*T, %1 : $*T, %2 : $T): - debug_value_addr %0 : $*T, var, name "t", argno 1 - debug_value_addr %1 : $*T, var, name "u", argno 2 + debug_value %0 : $*T, var, name "t", argno 1, expr op_deref + debug_value %1 : $*T, var, name "u", argno 2, expr op_deref debug_value %2 : $T, let, name "x", argno 3 %6 = copy_value %2 : $T %7 = load %0 : $*T diff --git a/test/SILOptimizer/address_projection.sil b/test/SILOptimizer/address_projection.sil index 5cd6b25db11f0..9cd4240929a52 100644 --- a/test/SILOptimizer/address_projection.sil +++ b/test/SILOptimizer/address_projection.sil @@ -145,9 +145,9 @@ bb0(%0 : $T): // CHECK: bb0(%0 : $*T, %1 : $*T, %2 : $*T): // CHECK: %[[PREV1:.*]] = alloc_stack $T // CHECK: %[[PREV2:.*]] = alloc_stack $T -// CHECK: debug_value_addr %0 : $*T, var, name "t", argno 1 -// CHECK: debug_value_addr %1 : $*T, var, name "u", argno 2 -// CHECK: debug_value_addr %2 : $*T +// CHECK: debug_value %0 : $*T, var, name "t", argno 1, expr op_deref +// CHECK: debug_value %1 : $*T, var, name "u", argno 2, expr op_deref +// CHECK: debug_value %2 : $*T, {{.*}} expr op_deref // CHECK: copy_addr [take] %0 to [initialization] %[[PREV1]] : $*T // CHECK: copy_addr %2 to [initialization] %0 : $*T // CHECK: destroy_addr %[[PREV1]] : $*T @@ -162,8 +162,8 @@ bb0(%0 : $T): // CHECK-LABEL: } // end sil function 'f050_storeinout' sil @f050_storeinout : $@convention(thin) (@inout T, @inout T, @in T) -> () { bb0(%0 : $*T, %1 : $*T, %2 : $T): - debug_value_addr %0 : $*T, var, name "t", argno 1 - debug_value_addr %1 : $*T, var, name "u", argno 2 + debug_value %0 : $*T, var, name "t", argno 1, expr op_deref + debug_value %1 : $*T, var, name "u", argno 2, expr op_deref debug_value %2 : $T, let, name "x", argno 3 %6 = copy_value %2 : $T %7 = load %0 : $*T diff --git a/test/SILOptimizer/allocbox_to_stack.sil b/test/SILOptimizer/allocbox_to_stack.sil index 0f4fae6441386..9e8cb50a95831 100644 --- a/test/SILOptimizer/allocbox_to_stack.sil +++ b/test/SILOptimizer/allocbox_to_stack.sil @@ -502,8 +502,8 @@ bb0(%0 : $@callee_guaranteed () -> @out U): sil @callWithAutoclosure : $@convention(thin) (@in T) -> () { // CHECK: bb0 bb0(%0 : $*T): - // CHECK: debug_value_addr - debug_value_addr %0 : $*T + // CHECK: debug_value {{.*}} expr op_deref + debug_value %0 : $*T, expr op_deref // CHECK: function_ref @mightApply %2 = function_ref @mightApply : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> () %3 = function_ref @closure_to_specialize : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 @@ -530,8 +530,8 @@ bb0(%0 : $*T): sil @callWithAutoclosure_2 : $@convention(thin) (@in T) -> () { // CHECK: bb0 bb0(%0 : $*T): - // CHECK: debug_value_addr - debug_value_addr %0 : $*T + // CHECK: debug_value {{.*}} expr op_deref + debug_value %0 : $*T, expr op_deref // CHECK: function_ref @mightApply %2 = function_ref @mightApply : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> () %3 = function_ref @closure_to_specialize : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 @@ -618,7 +618,7 @@ sil shared @specialized : $@convention(method) (Int, @in S) -> Bool { // CHECK: bb0 bb0(%0 : $Int, %1 : $*S): debug_value %0 : $Int - debug_value_addr %1 : $*S + debug_value %1 : $*S, expr op_deref %4 = function_ref @outer : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool %5 = function_ref @closure1 : $@convention(thin) (Int, @owned <τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } ) -> Bool %6 = alloc_box $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } @@ -636,7 +636,7 @@ sil @unspecialized : $@convention(method) (Int, @in S) -> // CHECK: bb0 bb0(%0 : $Int, %1 : $*S): debug_value %0 : $Int - debug_value_addr %1 : $*S + debug_value %1 : $*S, expr op_deref %4 = function_ref @outer : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool %5 = function_ref @closure1 : $@convention(thin) <τ_0_0 where τ_0_0 : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <τ_0_0>) -> Bool %6 = alloc_box $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } @@ -735,7 +735,7 @@ bb0(%0 : $<τ_0_0> { var τ_0_0 } ): sil hidden [noinline] @consume : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref destroy_addr %0 : $*T %3 = tuple () return %3 : $() diff --git a/test/SILOptimizer/allocbox_to_stack_ownership.sil b/test/SILOptimizer/allocbox_to_stack_ownership.sil index c32afdda6cac2..e3e5be4835dc8 100644 --- a/test/SILOptimizer/allocbox_to_stack_ownership.sil +++ b/test/SILOptimizer/allocbox_to_stack_ownership.sil @@ -501,8 +501,8 @@ bb0(%0 : @guaranteed $@callee_guaranteed () -> @out U): sil [ossa] @callWithAutoclosure : $@convention(thin) (@in T) -> () { // CHECK: bb0 bb0(%0 : $*T): - // CHECK: debug_value_addr - debug_value_addr %0 : $*T + // CHECK: debug_value {{.*}} expr op_deref + debug_value %0 : $*T, expr op_deref // CHECK: function_ref @mightApply %2 = function_ref @mightApply : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> () %3 = function_ref @closure_to_specialize : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 @@ -528,8 +528,8 @@ bb0(%0 : $*T): sil [ossa] @callWithAutoclosure_2 : $@convention(thin) (@in T) -> () { // CHECK: bb0 bb0(%0 : $*T): - // CHECK: debug_value_addr - debug_value_addr %0 : $*T + // CHECK: debug_value {{.*}} expr op_deref + debug_value %0 : $*T, expr op_deref // CHECK: function_ref @mightApply %2 = function_ref @mightApply : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> () %3 = function_ref @closure_to_specialize : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 @@ -616,7 +616,7 @@ sil shared [ossa] @specialized : $@convention(method) (Int, @in S) -> Bool { // CHECK: bb0 bb0(%0 : $Int, %1 : $*S): debug_value %0 : $Int - debug_value_addr %1 : $*S + debug_value %1 : $*S, expr op_deref %4 = function_ref @outer : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool %5 = function_ref @closure1 : $@convention(thin) (Int, @owned <τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } ) -> Bool %6 = alloc_box $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } @@ -634,7 +634,7 @@ sil [ossa] @unspecialized : $@convention(method) (Int, @in S // CHECK: bb0 bb0(%0 : $Int, %1 : $*S): debug_value %0 : $Int - debug_value_addr %1 : $*S + debug_value %1 : $*S, expr op_deref %4 = function_ref @outer : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool %5 = function_ref @closure1 : $@convention(thin) <τ_0_0 where τ_0_0 : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <τ_0_0>) -> Bool %6 = alloc_box $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } @@ -738,7 +738,7 @@ bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } ): sil hidden [noinline] @consume : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref destroy_addr %0 : $*T %3 = tuple () return %3 : $() diff --git a/test/SILOptimizer/array_element_propagation_ossa.sil b/test/SILOptimizer/array_element_propagation_ossa.sil index 1aceb4488c4b3..63c91d288444e 100644 --- a/test/SILOptimizer/array_element_propagation_ossa.sil +++ b/test/SILOptimizer/array_element_propagation_ossa.sil @@ -251,3 +251,37 @@ sil [ossa] @append_contentsOf_int : $@convention(thin) () -> () { return %19 : $() } +// Ignore an invalid negative index without crashing. +// +// CHECK-LABEL: sil [ossa] @negative_index +// CHECK: store %{{[0-9]+}} to [trivial] +// CHECK: [[GETF:%.*]] = function_ref @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray) -> MyInt +// CHECK: apply [[GETF]](%15, %17, %19, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray) -> MyInt +// CHECK-LABEL: // end sil function 'negative_index' +sil [ossa] @negative_index : $@convention(thin) () -> () { +bb0: + %0 = function_ref @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject // user: %3 + %1 = integer_literal $Builtin.Int64, 1 + %2 = struct $MyInt (%1 : $Builtin.Int64) + %3 = apply %0() : $@convention(thin) () -> @owned AnyObject + %4 = metatype $@thin MyArray.Type + %5 = function_ref @adoptStorage : $@convention(thin) (@owned AnyObject, MyInt, @thin MyArray.Type) -> @owned (MyArray, UnsafeMutablePointer) + %6 = apply %5(%3, %2, %4) : $@convention(thin) (@owned AnyObject, MyInt, @thin MyArray.Type) -> @owned (MyArray, UnsafeMutablePointer) + (%7, %8) = destructure_tuple %6 : $(MyArray, UnsafeMutablePointer) + %9 = struct_extract %8 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue + %10 = pointer_to_address %9 : $Builtin.RawPointer to [strict] $*MyInt + %11 = integer_literal $Builtin.Int64, 0 + %12 = struct $MyInt (%11 : $Builtin.Int64) + store %12 to [trivial] %10 : $*MyInt + %14 = integer_literal $Builtin.Int64, -1 + %15 = struct $MyInt (%14 : $Builtin.Int64) + %16 = function_ref @hoistableIsNativeTypeChecked : $@convention(method) (@guaranteed MyArray) -> MyBool + %17 = apply %16(%7) : $@convention(method) (@guaranteed MyArray) -> MyBool + %18 = function_ref @checkSubscript : $@convention(method) (MyInt, MyBool, @guaranteed MyArray) -> _MyDependenceToken + %19 = apply %18(%12, %17, %7) : $@convention(method) (MyInt, MyBool, @guaranteed MyArray) -> _MyDependenceToken + %20 = function_ref @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray) -> MyInt + %21 = apply %20(%15, %17, %19, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray) -> MyInt + destroy_value %7 : $MyArray + %23 = tuple () + return %23 : $() +} \ No newline at end of file diff --git a/test/SILOptimizer/opt_remark/opt_remark_generator_semantics.swift b/test/SILOptimizer/assemblyvision_remark/attributes.swift similarity index 84% rename from test/SILOptimizer/opt_remark/opt_remark_generator_semantics.swift rename to test/SILOptimizer/assemblyvision_remark/attributes.swift index ab39837098cb2..dd9b55479e804 100644 --- a/test/SILOptimizer/opt_remark/opt_remark_generator_semantics.swift +++ b/test/SILOptimizer/assemblyvision_remark/attributes.swift @@ -23,7 +23,7 @@ public func forceOptRemark() { // expected-note @-2 {{of 'x'}} } -@_semantics("optremark.sil-opt-remark-gen") +@_semantics("optremark.sil-assembly-vision-remark-gen") @inline(never) public func forceOptRemark2() { let x = getGlobal() @@ -55,7 +55,7 @@ public func allocateInlineCallee3() -> Klass { } @_semantics("optremark.sil-inliner") -@_semantics("optremark.sil-opt-remark-gen") +@_semantics("optremark.sil-assembly-vision-remark-gen") public func mix1() -> (Klass, Klass) { let x = getGlobal() return (x, Klass()) // expected-remark {{Pure call. Always profitable to inline "main.Klass.__allocating_init()"}} @@ -68,7 +68,7 @@ public func mix2() -> (Klass, Klass) { return (x, Klass()) // expected-remark {{Pure call. Always profitable to inline "main.Klass.__allocating_init()"}} } -@_semantics("optremark.sil-opt-remark-gen") +@_semantics("optremark.sil-assembly-vision-remark-gen") public func mix3() -> (Klass, Klass) { let x = getGlobal() return (x, Klass()) // expected-remark {{heap allocated ref of type 'Klass'}} @@ -85,3 +85,10 @@ public func mix5() -> (Klass, Klass) { let x = getGlobal() return (x, Klass()) } + +@_assemblyVision +public func mix4a() -> (Klass, Klass) { + let x = getGlobal() + return (x, Klass()) // expected-remark {{Pure call. Always profitable to inline "main.Klass.__allocating_init()"}} + // expected-remark @-1 {{heap allocated ref of type 'Klass'}} +} diff --git a/test/SILOptimizer/opt_remark/opt_remark_generator.sil b/test/SILOptimizer/assemblyvision_remark/basic.sil similarity index 72% rename from test/SILOptimizer/opt_remark/opt_remark_generator.sil rename to test/SILOptimizer/assemblyvision_remark/basic.sil index 4e10c7fd151ac..1b10cdf9cbca6 100644 --- a/test/SILOptimizer/opt_remark/opt_remark_generator.sil +++ b/test/SILOptimizer/assemblyvision_remark/basic.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -sil-opt-remark-ignore-always-infer -optremarkgen-declless-debugvalue-use-sildebugvar-info -sil-opt-remark-generator -sil-remarks-missed=sil-opt-remark-gen -verify %s -o /dev/null +// RUN: %target-sil-opt -sil-opt-remark-ignore-always-infer -assemblyvisionremarkgen-declless-debugvalue-use-sildebugvar-info -assembly-vision-remark-generator -sil-remarks-missed=sil-assembly-vision-remark-gen -verify %s -o /dev/null sil_stage canonical @@ -31,6 +31,14 @@ struct StructWithOwnerAndState { var state: TrivialState } +struct _SwiftEmptyArrayStorage { + var nonTrivialField: Klass = Klass() +} + +class EmptyArrayStorage { + var state: TrivialState = .first +} + /////////// // Tests // /////////// @@ -84,3 +92,17 @@ bb0(%0 : $StructWithOwner, %1 : $TrivialState): // expected-note @-3 {{of 'x'}} return %3 : $Klass } + +// Please keep these next to this test to make the note on the decl not far from its check. +var swiftEmptyArrayStorage: _SwiftEmptyArrayStorage +sil_global hidden @$s20opt_remark_generator22swiftEmptyArrayStorageAA06_SwiftefG0Vvp : $_SwiftEmptyArrayStorage + +sil @retainOnEmptyArrayStorage : $@convention(thin) () -> @owned EmptyArrayStorage { +bb0: + %0 = global_addr @$s20opt_remark_generator22swiftEmptyArrayStorageAA06_SwiftefG0Vvp : $*_SwiftEmptyArrayStorage + %1 = address_to_pointer %0 : $*_SwiftEmptyArrayStorage to $Builtin.RawPointer + %2 = raw_pointer_to_ref %1 : $Builtin.RawPointer to $EmptyArrayStorage + strong_retain %2 : $EmptyArrayStorage // expected-remark {{retain of type 'EmptyArrayStorage'}} + // expected-note @-9 {{of 'swiftEmptyArrayStorage'}} + return %2 : $EmptyArrayStorage +} diff --git a/test/SILOptimizer/opt_remark/opt_remark_generator.swift b/test/SILOptimizer/assemblyvision_remark/basic.swift similarity index 97% rename from test/SILOptimizer/opt_remark/opt_remark_generator.swift rename to test/SILOptimizer/assemblyvision_remark/basic.swift index 38f157cf549fb..d5da5289fdb63 100644 --- a/test/SILOptimizer/opt_remark/opt_remark_generator.swift +++ b/test/SILOptimizer/assemblyvision_remark/basic.swift @@ -1,4 +1,4 @@ -// RUN: %target-swiftc_driver -O -Rpass-missed=sil-opt-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xfrontend -enable-copy-propagation -emit-sil %s -o /dev/null -Xfrontend -verify +// RUN: %target-swiftc_driver -O -Rpass-missed=sil-assembly-vision-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xfrontend -enable-copy-propagation -emit-sil %s -o /dev/null -Xfrontend -verify // REQUIRES: optimized_stdlib,swift_stdlib_no_asserts public class Klass { @@ -15,6 +15,8 @@ public func getGlobal() -> Klass { // expected-note @-5:12 {{of 'global'}} // expected-remark @-2:12 {{begin exclusive access to value of type 'Klass'}} // expected-note @-7:12 {{of 'global'}} + // expected-remark @-4:12 {{end exclusive access to value of type 'Klass'}} + // expected-note @-9:12 {{of 'global'}} } // Make sure that the retain msg is at the beginning of the print and the diff --git a/test/SILOptimizer/opt_remark/opt_remark_generator_yaml.swift b/test/SILOptimizer/assemblyvision_remark/basic_yaml.swift similarity index 60% rename from test/SILOptimizer/opt_remark/opt_remark_generator_yaml.swift rename to test/SILOptimizer/assemblyvision_remark/basic_yaml.swift index a43a9934b2e48..2ddafa94395cb 100644 --- a/test/SILOptimizer/opt_remark/opt_remark_generator_yaml.swift +++ b/test/SILOptimizer/assemblyvision_remark/basic_yaml.swift @@ -1,4 +1,4 @@ -// RUN: %target-swiftc_driver -O -Rpass-missed=sil-opt-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xfrontend -enable-copy-propagation -emit-sil %s -o /dev/null -Xfrontend -verify +// RUN: %target-swiftc_driver -O -Rpass-missed=sil-assembly-vision-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xfrontend -enable-copy-propagation -emit-sil %s -o /dev/null -Xfrontend -verify // RUN: %empty-directory(%t) // RUN: %target-swiftc_driver -wmo -O -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xfrontend -enable-copy-propagation -emit-sil -save-optimization-record=yaml -save-optimization-record-path %t/note.yaml %s -o /dev/null && %FileCheck --input-file=%t/note.yaml %s @@ -6,15 +6,15 @@ // REQUIRES: optimized_stdlib,swift_stdlib_no_asserts // This file is testing out the basic YAML functionality to make sure that it -// works without burdening opt_remark_generator_yaml.swift with having to update all +// works without burdening basic_yaml.swift with having to update all // of the yaml test cases everytime new code is added. public class Klass {} // CHECK: --- !Missed -// CHECK-NEXT: Pass: sil-opt-remark-gen +// CHECK-NEXT: Pass: sil-assembly-vision-remark-gen // CHECK-NEXT: Name: sil.memory -// CHECK-NEXT: DebugLoc: { File: '{{.*}}opt_remark_generator_yaml.swift', +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', // CHECK-NEXT: Line: [[# @LINE + 7 ]], Column: 21 } // CHECK-NEXT: Function: main // CHECK-NEXT: Args: @@ -25,24 +25,39 @@ public class Klass {} public var global = Klass() // expected-remark {{heap allocated ref of type 'Klass'}} // CHECK: --- !Missed -// CHECK-NEXT: Pass: sil-opt-remark-gen +// CHECK-NEXT: Pass: sil-assembly-vision-remark-gen // CHECK-NEXT: Name: sil.memory -// CHECK-NEXT: DebugLoc: { File: '{{.*}}opt_remark_generator_yaml.swift', -// CHECK-NEXT: Line: [[# @LINE + 27 ]], Column: 12 } +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', +// CHECK-NEXT: Line: [[# @LINE + 42 ]], Column: 12 } // CHECK-NEXT: Function: 'getGlobal()' // CHECK-NEXT: Args: // CHECK-NEXT: - String: 'begin exclusive access to value of type ''' // CHECK-NEXT: - ValueType: Klass // CHECK-NEXT: - String: '''' // CHECK-NEXT: - InferredValue: 'of ''global''' -// CHECK-NEXT: DebugLoc: { File: '{{.*}}opt_remark_generator_yaml.swift', +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', // CHECK-NEXT: Line: [[# @LINE - 14 ]], Column: 12 } // CHECK-NEXT: ... // // CHECK: --- !Missed -// CHECK-NEXT: Pass: sil-opt-remark-gen +// CHECK-NEXT: Pass: sil-assembly-vision-remark-gen // CHECK-NEXT: Name: sil.memory -// CHECK-NEXT: DebugLoc: { File: '{{.*}}opt_remark_generator_yaml.swift', +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', +// CHECK-NEXT: Line: [[# @LINE + 27 ]], Column: 12 } +// CHECK-NEXT: Function: 'getGlobal()' +// CHECK-NEXT: Args: +// CHECK-NEXT: - String: 'end exclusive access to value of type ''' +// CHECK-NEXT: - ValueType: Klass +// CHECK-NEXT: - String: '''' +// CHECK-NEXT: - InferredValue: 'of ''global''' +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', +// CHECK-NEXT: Line: [[# @LINE - 29 ]], Column: 12 } +// CHECK-NEXT: ... +// +// CHECK: --- !Missed +// CHECK-NEXT: Pass: sil-assembly-vision-remark-gen +// CHECK-NEXT: Name: sil.memory +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', // CHECK-NEXT: Line: [[# @LINE + 12]], Column: 5 } // CHECK-NEXT: Function: 'getGlobal()' // CHECK-NEXT: Args: @@ -50,21 +65,24 @@ public var global = Klass() // expected-remark {{heap allocated ref of type 'Kla // CHECK-NEXT: - ValueType: Klass // CHECK-NEXT: - String: '''' // CHECK-NEXT: - InferredValue: 'of ''global''' -// CHECK-NEXT: DebugLoc: { File: '{{.*}}opt_remark_generator_yaml.swift', -// CHECK-NEXT: Line: [[# @LINE - 29 ]], Column: 12 } +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', +// CHECK-NEXT: Line: [[# @LINE - 44 ]], Column: 12 } // CHECK-NEXT: ... @inline(never) public func getGlobal() -> Klass { return global // expected-remark @:5 {{retain of type 'Klass'}} - // expected-note @-34:12 {{of 'global'}} + // expected-note @-49:12 {{of 'global'}} // expected-remark @-2 {{begin exclusive access to value of type 'Klass'}} - // expected-note @-36:12 {{of 'global'}} + // expected-note @-51:12 {{of 'global'}} + // NOTE: We really want the end access at :18, not :12. TODO Fix this! + // expected-remark @-5 {{end exclusive access to value of type 'Klass'}} + // expected-note @-54:12 {{of 'global'}} } // CHECK: --- !Missed -// CHECK-NEXT: Pass: sil-opt-remark-gen +// CHECK-NEXT: Pass: sil-assembly-vision-remark-gen // CHECK-NEXT: Name: sil.memory -// CHECK-NEXT: DebugLoc: { File: '{{.*}}opt_remark_generator_yaml.swift', +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', // CHECK-NEXT: Line: [[# @LINE + 23]], Column: 11 } // CHECK-NEXT: Function: 'useGlobal()' // CHECK-NEXT: Args: @@ -73,9 +91,9 @@ public func getGlobal() -> Klass { // CHECK-NEXT: - String: '''' // CHECK-NEXT: ... // CHECK-NEXT: --- !Missed -// CHECK-NEXT: Pass: sil-opt-remark-gen +// CHECK-NEXT: Pass: sil-assembly-vision-remark-gen // CHECK-NEXT: Name: sil.memory -// CHECK-NEXT: DebugLoc: { File: '{{.*}}opt_remark_generator_yaml.swift', +// CHECK-NEXT: DebugLoc: { File: '{{.*}}basic_yaml.swift', // CHECK-NEXT: Line: [[# @LINE + 12]], Column: 12 } // CHECK-NEXT: Function: 'useGlobal()' // CHECK-NEXT: Args: diff --git a/test/SILOptimizer/opt_remark/cast_remarks.swift b/test/SILOptimizer/assemblyvision_remark/cast_remarks.swift similarity index 98% rename from test/SILOptimizer/opt_remark/cast_remarks.swift rename to test/SILOptimizer/assemblyvision_remark/cast_remarks.swift index 7a84a1e5684bd..1be5ac2a2e09a 100644 --- a/test/SILOptimizer/opt_remark/cast_remarks.swift +++ b/test/SILOptimizer/assemblyvision_remark/cast_remarks.swift @@ -1,4 +1,4 @@ -// RUN: %target-swiftc_driver -O -Rpass-missed=sil-opt-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil %s -o /dev/null -Xfrontend -verify +// RUN: %target-swiftc_driver -O -Rpass-missed=sil-assembly-vision-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil %s -o /dev/null -Xfrontend -verify // REQUIRES: optimized_stdlib // REQUIRES: swift_stdlib_no_asserts diff --git a/test/SILOptimizer/opt_remark/cast_remarks_objc.swift b/test/SILOptimizer/assemblyvision_remark/cast_remarks_objc.swift similarity index 98% rename from test/SILOptimizer/opt_remark/cast_remarks_objc.swift rename to test/SILOptimizer/assemblyvision_remark/cast_remarks_objc.swift index 43aa6f1728d5f..e53845d9f4457 100644 --- a/test/SILOptimizer/opt_remark/cast_remarks_objc.swift +++ b/test/SILOptimizer/assemblyvision_remark/cast_remarks_objc.swift @@ -1,4 +1,4 @@ -// RUN: %target-swiftc_driver -O -Rpass-missed=sil-opt-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil %s -o /dev/null -Xfrontend -verify +// RUN: %target-swiftc_driver -O -Rpass-missed=sil-assembly-vision-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil %s -o /dev/null -Xfrontend -verify // REQUIRES: objc_interop // REQUIRES: optimized_stdlib diff --git a/test/SILOptimizer/assemblyvision_remark/chacha.swift b/test/SILOptimizer/assemblyvision_remark/chacha.swift new file mode 100644 index 0000000000000..1398e2de1813d --- /dev/null +++ b/test/SILOptimizer/assemblyvision_remark/chacha.swift @@ -0,0 +1,49 @@ +// RUN: %target-swiftc_driver -Osize -emit-sil %s -o /dev/null -Xfrontend -verify +// REQUIRES: optimized_stdlib,swift_stdlib_no_asserts + +// An extraction from the benchmark ChaCha20 that we were not ignoring +// dealloc_stack and other end scope instructions. + +enum ChaCha20 { } + +extension ChaCha20 { + @inline(never) + public static func encrypt(bytes: inout Bytes, key: Key, nonce: Nonce, initialCounter: UInt32 = 0) where Bytes.Element == UInt8, Key.Element == UInt8, Nonce.Element == UInt8 { + print("I am lost...") + } +} + +@inline(never) +func checkResult(_ plaintext: [UInt8]) { + precondition(plaintext.first! == 6 && plaintext.last! == 254) + var hash: UInt64 = 0 + for byte in plaintext { + // rotate + hash = (hash &<< 8) | (hash &>> (64 - 8)) + hash ^= UInt64(byte) + } + precondition(hash == 0xa1bcdb217d8d14e4) +} + +@_semantics("optremark.sil-assembly-vision-remark-gen") +public func run_ChaCha(_ N: Int) { + let key = Array(repeating: UInt8(1), count: 32) + let nonce = Array(repeating: UInt8(2), count: 12) + + var checkedtext = Array(repeating: UInt8(0), count: 1024) + ChaCha20.encrypt(bytes: &checkedtext, key: key, nonce: nonce) + checkResult(checkedtext) // expected-remark {{release of type '}} + // expected-note @-3 {{of 'checkedtext}} + + var plaintext = Array(repeating: UInt8(0), count: 30720) + for _ in 1...N { + ChaCha20.encrypt(bytes: &plaintext, key: key, nonce: nonce) + print(plaintext.first!) // expected-remark @:11 {{heap allocated ref of type '}} + // expected-remark @-1:27 {{release of type '}} + } +} // expected-remark {{release of type '}} + // expected-note @-7 {{of 'plaintext}} + // expected-remark @-2 {{release of type '}} + // expected-note @-16 {{of 'nonce}} + // expected-remark @-4 {{release of type '}} + // expected-note @-19 {{of 'key}} diff --git a/test/SILOptimizer/opt_remark/opt_remark_generator_force_emit_implicit_autogen.swift b/test/SILOptimizer/assemblyvision_remark/force_emit_implicit_autogen.swift similarity index 72% rename from test/SILOptimizer/opt_remark/opt_remark_generator_force_emit_implicit_autogen.swift rename to test/SILOptimizer/assemblyvision_remark/force_emit_implicit_autogen.swift index 9c49cd002cf51..6e121bae99da1 100644 --- a/test/SILOptimizer/opt_remark/opt_remark_generator_force_emit_implicit_autogen.swift +++ b/test/SILOptimizer/assemblyvision_remark/force_emit_implicit_autogen.swift @@ -1,4 +1,4 @@ -// RUN: %target-swiftc_driver -O -Rpass-missed=sil-opt-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil %s -o /dev/null -Xfrontend -verify -Xllvm -optremarkgen-visit-implicit-autogen-funcs=1 +// RUN: %target-swiftc_driver -O -Rpass-missed=sil-assembly-vision-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil %s -o /dev/null -Xfrontend -verify -Xllvm -assemblyvisionremarkgen-visit-implicit-autogen-funcs=1 // From the constructor. class Klass {} // expected-remark {{heap allocated ref of type 'Klass'}} diff --git a/test/SILOptimizer/assemblyvision_remark/nominal_type_attributes.swift b/test/SILOptimizer/assemblyvision_remark/nominal_type_attributes.swift new file mode 100644 index 0000000000000..51ca28bdf1aad --- /dev/null +++ b/test/SILOptimizer/assemblyvision_remark/nominal_type_attributes.swift @@ -0,0 +1,51 @@ +// RUN: %target-swiftc_driver -Osize -emit-sil %s -o /dev/null -Xfrontend -verify +// REQUIRES: optimized_stdlib,swift_stdlib_no_asserts + +// Make sure we emit remarks on nominal types + +@inline(never) +func callPrint(_ s: String) { print(s) } + +var global: String = "123" + +@_assemblyVision +struct Struct { + func printMe() { + callPrint(global) // expected-remark {{begin exclusive access to value of type '}} + // expected-note @-6 {{of 'global'}} + // expected-remark @-2 {{end exclusive access to value of type '}} + // expected-note @-8 {{of 'global'}} + // expected-remark @-4 {{retain of type '}} + // expected-note @-10 {{of 'global'}} + // expected-remark @-6 {{release of type '}} + // expected-note @-12 {{of 'global}} + } +} + +// Negative test +struct Struct2 { + func callPrintMe() { + callPrint(global) + } +} + +@_assemblyVision +enum Enum { + func callPrintMe() { + callPrint(global) // expected-remark {{begin exclusive access to value of type '}} + // expected-note @-27 {{of 'global'}} + // expected-remark @-2 {{end exclusive access to value of type '}} + // expected-note @-29 {{of 'global'}} + // expected-remark @-4 {{retain of type '}} + // expected-note @-31 {{of 'global'}} + // expected-remark @-6 {{release of type '}} + // expected-note @-33 {{of 'global}} + } +} + +// Negative test +enum Enum2 { + func callPrintMe() { + callPrint("I am callPrinting 1") + } +} diff --git a/test/SILOptimizer/basic-callee-printer.sil b/test/SILOptimizer/basic-callee-printer.sil index d4afd330fabcf..57e979ecd171b 100644 --- a/test/SILOptimizer/basic-callee-printer.sil +++ b/test/SILOptimizer/basic-callee-printer.sil @@ -438,7 +438,7 @@ bb0(%0 : $*private_proto_private_class): // CHECK: private_proto_1_private_class_witness sil private @call_through_private_proto_1 : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %2 = witness_method $T, #private_proto_1.theMethod : $@convention(witness_method: private_proto_1) <τ_0_0 where τ_0_0 : private_proto_1> (@in_guaranteed τ_0_0) -> () %3 = apply %2(%0) : $@convention(witness_method: private_proto_1) <τ_0_0 where τ_0_0 : private_proto_1> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*T @@ -482,7 +482,7 @@ bb0(%0 : $*private_proto_internal_class): // CHECK: private_proto_2_internal_class_witness sil private @call_through_private_proto_2 : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %2 = witness_method $T, #private_proto_2.theMethod : $@convention(witness_method: private_proto_2) <τ_0_0 where τ_0_0 : private_proto_2> (@in_guaranteed τ_0_0) -> () %3 = apply %2(%0) : $@convention(witness_method: private_proto_2) <τ_0_0 where τ_0_0 : private_proto_2> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*T @@ -524,7 +524,7 @@ bb0(%0 : $*private_proto_public_class): // CHECK: private_proto_3_public_class_witness sil private @call_through_private_proto_3 : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %2 = witness_method $T, #private_proto_3.theMethod : $@convention(witness_method: private_proto_3) <τ_0_0 where τ_0_0 : private_proto_3> (@in_guaranteed τ_0_0) -> () %3 = apply %2(%0) : $@convention(witness_method: private_proto_3) <τ_0_0 where τ_0_0 : private_proto_3> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*T @@ -566,7 +566,7 @@ bb0(%0 : $*private_proto_public_class_private_method): // CHECK: private_proto_4_public_class_private_method_witness sil private @call_through_private_proto_4 : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %2 = witness_method $T, #private_proto_4.theMethod : $@convention(witness_method: private_proto_4) <τ_0_0 where τ_0_0 : private_proto_4> (@in_guaranteed τ_0_0) -> () %3 = apply %2(%0) : $@convention(witness_method: private_proto_4) <τ_0_0 where τ_0_0 : private_proto_4> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*T diff --git a/test/SILOptimizer/c_string_optimization.swift b/test/SILOptimizer/c_string_optimization.swift index 5a8cef8ee059e..f859063c77d6d 100644 --- a/test/SILOptimizer/c_string_optimization.swift +++ b/test/SILOptimizer/c_string_optimization.swift @@ -32,5 +32,25 @@ public func testStringConstantForCFunction() { puts("Hello " + "world!") } +// CHECK-LABEL: sil [noinline] @$s4test0A17TypeInterpolationyyF +// CHECK-NOT: apply +// CHECK: [[L:%[0-9]+]] = string_literal utf8 "String" +// CHECK-NOT: apply +// CHECK: [[P:%[0-9]+]] = struct $UnsafePointer ([[L]] : $Builtin.RawPointer) +// CHECK-NOT: apply +// CHECK: [[O:%[0-9]+]] = enum $Optional>, #Optional.some!enumelt, [[P]] +// CHECK-NOT: apply +// CHECK: [[F:%[0-9]+]] = function_ref @puts +// CHECK: apply [[F]]([[O]]) +// CHECK: } // end sil function '$s4test0A17TypeInterpolationyyF' +@inline(never) +public func testTypeInterpolation() { + puts("\(String.self)") +} + // CHECK-OUTPUT: Hello world! testStringConstantForCFunction() + +// CHECK-OUTPUT: String +testTypeInterpolation() + diff --git a/test/SILOptimizer/closure-lifetime-fixup.sil b/test/SILOptimizer/closure-lifetime-fixup.sil index 86f51a668fd5c..c9dade879e8d6 100644 --- a/test/SILOptimizer/closure-lifetime-fixup.sil +++ b/test/SILOptimizer/closure-lifetime-fixup.sil @@ -178,3 +178,33 @@ bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass): %42 = tuple () return %42 : $() } + +sil @simpleClosure : $@convention(thin) () -> () + +// Don't crash. +// CHECK-LABEL: sil [ossa] @testIteratorInvalidation +// CHECK: [[C:%.*]] = thin_to_thick_function +// CHECK: [[CC:%.*]] = copy_value [[C]] +// CHECK: [[NE:%.*]] = convert_escape_to_noescape [[CC]] +// CHECK: br bb3([[NE:%.*]] +// CHECK: } // end sil function 'testIteratorInvalidation' +sil [ossa] @testIteratorInvalidation : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () { +bb0(%0 : $@noescape @callee_guaranteed () -> ()): + %2 = function_ref @simpleClosure : $@convention(thin) () -> () + %3 = thin_to_thick_function %2 : $@convention(thin) () -> () to $@callee_guaranteed () -> () + cond_br undef, bb1, bb2 + +bb1: + br bb3(%0 : $@noescape @callee_guaranteed () -> ()) + +bb2: + %11 = convert_escape_to_noescape [not_guaranteed] %3 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> () + br bb3(%11 : $@noescape @callee_guaranteed () -> ()) + + +bb3(%13 : $@noescape @callee_guaranteed () -> ()): + %15 = apply %13() : $@noescape @callee_guaranteed () -> () + %16 = tuple () + return %16 : $() +} + diff --git a/test/SILOptimizer/closure_lifetime_fixup_concurrency.swift b/test/SILOptimizer/closure_lifetime_fixup_concurrency.swift index f1ae52ea36ef1..9f13f32fc6aab 100644 --- a/test/SILOptimizer/closure_lifetime_fixup_concurrency.swift +++ b/test/SILOptimizer/closure_lifetime_fixup_concurrency.swift @@ -1,13 +1,13 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -sil-verify-all -enable-experimental-concurrency -emit-sil -disable-copy-propagation -I %t -o - | %FileCheck %s +// RUN: %target-swift-frontend %s -sil-verify-all -disable-availability-checking -emit-sil -disable-copy-propagation -I %t -o - | %FileCheck %s // REQUIRES: concurrency // CHECK-LABEL: sil @$s34closure_lifetime_fixup_concurrency12testAsyncLetyS2SYaF : $@convention(thin) @async (@guaranteed String) -> @owned String { // CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [on_stack] // CHECK: [[MD:%.*]] = mark_dependence [[PA]] // CHECK: [[CONV:%.*]] = convert_function [[MD]] -// CHECK: [[BAL:%.*]] = builtin "startAsyncLet"([[CONV]] -// CHECK: builtin "endAsyncLet"([[BAL]] : $Builtin.RawPointer, [[MD]] +// CHECK: [[BAL:%.*]] = builtin "startAsyncLetWithLocalBuffer"([[OPT:%.+]] : $Optional, [[CONV]] +// CHECK: builtin "endAsyncLetLifetime"([[BAL]] : $Builtin.RawPointer, [[MD]] // CHECK: } // end sil function '$s34closure_lifetime_fixup_concurrency12testAsyncLetyS2SYaF' public func testAsyncLet(_ n: String) async -> String { diff --git a/test/SILOptimizer/closure_specialize_loop.swift b/test/SILOptimizer/closure_specialize_loop.swift new file mode 100644 index 0000000000000..4ce12d8c50c59 --- /dev/null +++ b/test/SILOptimizer/closure_specialize_loop.swift @@ -0,0 +1,16 @@ +// RUN: %{python} %S/../Inputs/timeout.py 10 %target-swift-frontend -O -parse-as-library %s -emit-sil | %FileCheck %s + +public func callit() { + testit { false } +} + +// Check if the compiler terminates and does not full into an infinite optimization +// loop between the ClosureSpecializer and CapturePropagation. + +// CHECK-LABEL: sil @$s23closure_specialize_loop6testit1cySbyc_tF +public func testit(c: @escaping () -> Bool) { + if c() { + testit { !c() } + } +} + diff --git a/test/SILOptimizer/constant_evaluator_skip_test.sil b/test/SILOptimizer/constant_evaluator_skip_test.sil index 83a52daea731c..0062cbf0ec9e0 100644 --- a/test/SILOptimizer/constant_evaluator_skip_test.sil +++ b/test/SILOptimizer/constant_evaluator_skip_test.sil @@ -92,7 +92,7 @@ bb0: %9 = integer_literal $Builtin.Int32, 0 %10 = builtin "cmp_slt_Int32"(%8 : $Builtin.Int32, %9 : $Builtin.Int32) : $Builtin.Int1 cond_br %10, bb2, bb3 - // CHECK: {{.*}}:[[@LINE-1]]:{{.*}}: note: branch depends on non-constant value produced by an unevaluated instructions + // CHECK-LABEL: {{.*}}:94:{{.*}}: remark: branch depends on non-constant value produced by an unevaluated instructions // CHECK: {{.*}}: note: result of an unevaluated instruction is not a constant bb2: br bb4 diff --git a/test/SILOptimizer/constant_propagation_casts_ossa.sil b/test/SILOptimizer/constant_propagation_casts_ossa.sil new file mode 100644 index 0000000000000..71cd890ee61e8 --- /dev/null +++ b/test/SILOptimizer/constant_propagation_casts_ossa.sil @@ -0,0 +1,96 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -diagnostic-constant-propagation | %FileCheck %s +sil_stage canonical + +//import Swift + +class Klass { +} + +class SubKlass : Klass { +} + +class NoSubKlass { +} + +sil [noinline] @blackhole1 : $@convention(thin) (@guaranteed SubKlass) -> () +sil [noinline] @blackhole2 : $@convention(thin) (@guaranteed NoSubKlass) -> () + +// CHECK-LABEL: sil [ossa] @test_guaranteed_cast_opt1 : +// CHECK-NOT: checked_cast_br +// CHECK: upcast +// CHECK-LABEL: } // end sil function 'test_guaranteed_cast_opt1' +sil [ossa] @test_guaranteed_cast_opt1 : $@convention(thin) (@owned SubKlass) -> () { +bb0(%0 : @owned $SubKlass): + %borrow = begin_borrow %0 : $SubKlass + checked_cast_br %borrow : $SubKlass to Klass, bb1, bb2 + +bb1(%val1 : @guaranteed $Klass): + %copy = copy_value %val1: $Klass + end_borrow %borrow : $SubKlass + destroy_value %copy : $Klass + br bb3 + +bb2(%val2 : @guaranteed $SubKlass): + end_borrow %borrow : $SubKlass + br bb3 + +bb3: + destroy_value %0 : $SubKlass + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @test_guaranteed_cast_opt2 : +// CHECK-NOT: checked_cast_br +// CHECK: upcast +// CHECK-LABEL: } // end sil function 'test_guaranteed_cast_opt2' +sil [ossa] @test_guaranteed_cast_opt2 : $@convention(thin) (@owned SubKlass) -> () { +bb0(%0 : @owned $SubKlass): + %borrow = begin_borrow %0 : $SubKlass + checked_cast_br %borrow : $SubKlass to Klass, bb1, bb2 + +bb1(%val1 : @guaranteed $Klass): + %copy = copy_value %val1: $Klass + end_borrow %borrow : $SubKlass + destroy_value %copy : $Klass + br bb3 + +bb2(%val2 : @guaranteed $SubKlass): + %func = function_ref @blackhole1 : $@convention(thin) (@guaranteed SubKlass) -> () + apply %func(%val2) : $@convention(thin) (@guaranteed SubKlass) -> () + end_borrow %borrow : $SubKlass + br bb3 + +bb3: + destroy_value %0 : $SubKlass + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @test_guaranteed_cast_opt3 : +// CHECK-NOT: checked_cast_br +// CHECK: br bb2 +// CHECK-LABEL: } // end sil function 'test_guaranteed_cast_opt3' +sil [ossa] @test_guaranteed_cast_opt3 : $@convention(thin) (@owned NoSubKlass) -> () { +bb0(%0 : @owned $NoSubKlass): + %borrow = begin_borrow %0 : $NoSubKlass + checked_cast_br %borrow : $NoSubKlass to Klass, bb1, bb2 + +bb1(%val1 : @guaranteed $Klass): + %copy = copy_value %val1: $Klass + end_borrow %borrow : $NoSubKlass + destroy_value %copy : $Klass + br bb3 + +bb2(%val2 : @guaranteed $NoSubKlass): + %func = function_ref @blackhole2 : $@convention(thin) (@guaranteed NoSubKlass) -> () + apply %func(%val2) : $@convention(thin) (@guaranteed NoSubKlass) -> () + end_borrow %borrow : $NoSubKlass + br bb3 + +bb3: + destroy_value %0 : $NoSubKlass + %r = tuple () + return %r : $() +} + diff --git a/test/SILOptimizer/constant_propagation_ownership.sil b/test/SILOptimizer/constant_propagation_ownership.sil index 210a9d2a4c4fe..124a234f0eb10 100644 --- a/test/SILOptimizer/constant_propagation_ownership.sil +++ b/test/SILOptimizer/constant_propagation_ownership.sil @@ -1108,7 +1108,7 @@ bb0(%0 : $*Error, %1 : $*E1): // CHECK-LABEL: sil [ossa] @always_fail_protocolmeta_to_concretemeta_checkedcastbr : $@convention(thin) (@thin P.Protocol) -> Builtin.Int1 { // CHECK: bb0( // CHECK-NEXT: metatype -// CHECK-NEXT: br bb2( +// CHECK-NEXT: br bb2 // CHECK: } // end sil function 'always_fail_protocolmeta_to_concretemeta_checkedcastbr' sil [ossa] @always_fail_protocolmeta_to_concretemeta_checkedcastbr : $@convention(thin) (@thin P.Protocol) -> Builtin.Int1 { bb0(%0 : $@thin P.Protocol): @@ -1131,7 +1131,7 @@ bb3(%11 : $Builtin.Int1): // CHECK-LABEL: sil [ossa] @always_succeed_subtoparent : $@convention(thin) (@owned AnSubObject) -> Builtin.Int1 { // CHECK: bb0( // CHECK-NEXT: upcast -// CHECK-NEXT: br bb1( +// CHECK-NEXT: br bb1 // CHECK: } // end sil function 'always_succeed_subtoparent' sil [ossa] @always_succeed_subtoparent : $@convention(thin) (@owned AnSubObject) -> Builtin.Int1 { bb0(%0 : @owned $AnSubObject): diff --git a/test/SILOptimizer/constant_propagation_stdlib.swift b/test/SILOptimizer/constant_propagation_stdlib.swift index 744d7223c1084..fe579d3dcfcfe 100644 --- a/test/SILOptimizer/constant_propagation_stdlib.swift +++ b/test/SILOptimizer/constant_propagation_stdlib.swift @@ -12,7 +12,7 @@ public struct MyInt { // CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF' // CHECK-O-LABEL: sil @$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF : $@convention(thin) (MyInt) -> Builtin.Int1 { // CHECK-O: bb0( -// CHECK-O-NEXT: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK-O: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-O-NEXT: return [[RESULT]] // CHECK-O-NEXT: } // end sil function '$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF' public func isConcrete_true(_ x: MyInt) -> Builtin.Int1 { @@ -85,8 +85,8 @@ public func isConcrete_concrete_caller(_ x: MyInt) -> Builtin.Int1 { // CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF' // CHECK-O-LABEL: sil @$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF : $@convention(thin) (MyInt) -> (Builtin.Int1, Builtin.Int1, Builtin.Int1) { // CHECK-O: bb0( -// CHECK-O-NEXT: [[VALUE:%.*]] = integer_literal $Builtin.Int1, -1 -// CHECK-O-NEXT: [[RESULT:%.*]] = tuple ([[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1) +// CHECK-O: [[VALUE:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK-O: [[RESULT:%.*]] = tuple ([[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1) // CHECK-O-NEXT: return [[RESULT]] // CHECK-O-NEXT: } // end sil function '$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF' public func main(x: MyInt) -> (Builtin.Int1, Builtin.Int1, Builtin.Int1) { diff --git a/test/SILOptimizer/copy_propagation.sil b/test/SILOptimizer/copy_propagation.sil index 4e171a0a1df21..ec6c7ca228f90 100644 --- a/test/SILOptimizer/copy_propagation.sil +++ b/test/SILOptimizer/copy_propagation.sil @@ -32,6 +32,7 @@ sil [ossa] @getOwnedC : $@convention(thin) () -> (@owned C) sil [ossa] @takeOwnedC : $@convention(thin) (@owned C) -> () sil [ossa] @takeOwnedCTwice : $@convention(thin) (@owned C, @owned C) -> () sil [ossa] @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> () +sil [ossa] @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> () // -O ignores this because there's no copy_value // -Onone hoists the destroy and adds a poison flag. @@ -342,6 +343,8 @@ bb0: br bb1 bb1: + %f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> () + %call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> () destroy_value %copy : $AnyObject destroy_value %obj : $AnyObject destroy_value %box : ${ var AnyObject } @@ -392,6 +395,8 @@ bb1: br bb2 bb2: + %f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> () + %call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> () destroy_value %copy : $AnyObject destroy_value %obj : $AnyObject destroy_value %box : ${ var AnyObject } @@ -435,6 +440,8 @@ bb0: br bb1 bb1: + %f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> () + %call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> () destroy_value %copy : $AnyObject destroy_value %obj : $AnyObject destroy_value %box : ${ var AnyObject } @@ -484,6 +491,8 @@ bb1: br bb2 bb2: + %f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> () + %call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> () destroy_value %copy : $AnyObject destroy_value %obj : $AnyObject destroy_value %box : ${ var AnyObject } @@ -527,6 +536,8 @@ bb0: br bb1 bb1: + %f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> () + %call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> () destroy_value %copy : $AnyObject destroy_value %obj : $AnyObject destroy_value %box : ${ var AnyObject } @@ -577,6 +588,8 @@ bb1: br bb2 bb2: + %f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> () + %call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> () destroy_value %copy : $AnyObject destroy_value %obj : $AnyObject destroy_value %box : ${ var AnyObject } @@ -622,6 +635,8 @@ bb0: br bb1 bb1: + %f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> () + %call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> () destroy_value %obj : $AnyObject destroy_value %box : ${ var AnyObject } %v = tuple () diff --git a/test/SILOptimizer/copy_propagation_borrow.sil b/test/SILOptimizer/copy_propagation_borrow.sil index 955769ce84a18..830540a2a8fee 100644 --- a/test/SILOptimizer/copy_propagation_borrow.sil +++ b/test/SILOptimizer/copy_propagation_borrow.sil @@ -10,6 +10,8 @@ sil_stage canonical import Builtin +typealias AnyObject = Builtin.AnyObject + class B { } class C { @@ -21,24 +23,36 @@ sil [ossa] @getOwnedC : $@convention(thin) () -> (@owned C) sil [ossa] @takeOwnedC : $@convention(thin) (@owned C) -> () sil [ossa] @takeOwnedCTwice : $@convention(thin) (@owned C, @owned C) -> () sil [ossa] @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> () +sil [ossa] @accessRawP : $@convention(method) (Builtin.RawPointer) -> () struct NativeObjectPair { var obj1 : Builtin.NativeObject var obj2 : Builtin.NativeObject } -class Klass {} +struct HasObject { + var object: C +} struct HasObjectAndInt { - var object: Klass + var object: C var value: Builtin.Int64 } - struct Wrapper { var hasObject: HasObjectAndInt } +struct MultiWrapper { + var hasObject0: HasObjects + var hasObject1: HasObjects +} + +struct HasObjects { + var object0: C + var object1: C +} + struct UInt64 { @_hasStorage public var _value: Builtin.Int64 { get set } init(_value: Builtin.Int64) @@ -130,6 +144,27 @@ bb0(%0 : @guaranteed $NativeObjectPair): // Test consolidateBorrowScope // ============================================================================= +// Test a basic copy with an outer use. +// +// CHECK-LABEL: sil [ossa] @testBorrowOuterUse : $@convention(thin) (@owned C) -> () { +// CHECK: bb0(%0 : @owned $C): +// CHECK-NOT: begin_borrow +// CHECK-NOT: copy +// CHECK: apply %{{.*}}(%0) : $@convention(thin) (@owned C) -> () +// CHECK-NOT: destroy +// CHECK: } // end sil function 'testBorrowOuterUse' +sil [ossa] @testBorrowOuterUse : $@convention(thin) (@owned C) -> () { +bb0(%0 : @owned $C): + %1 = begin_borrow %0 : $C + %copy = copy_value %1 : $C + end_borrow %1 : $C + %f1 = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> () + %call2 = apply %f1(%copy) : $@convention(thin) (@owned C) -> () + destroy_value %0 : $C + %99 = tuple () + return %99 : $() +} + // Remove nested borrow scopes for multiple levels of struct_extracts. // // CHECK-LABEL: sil [ossa] @testMultiBlockBorrow : $@convention(thin) (@guaranteed C) -> () { @@ -431,6 +466,77 @@ bb3: return %0 : $C } +// Test a nested borrow scope that has different inner vs. outer +// properties on different branches. If any one of the inner borrow's +// uses are outside the outer borrow, then all of them need to be +// considered "outer uses". Otherwise, when we cleanup the lifetime of +// the new outer copy, it won't cover then entire inner borrow scope. +// +// CHECK-LABEL: sil [ossa] @testNestedBorrowInsideAndOutsideUse : $@convention(thin) () -> () { +// CHECK: [[ALLOC:%.*]] = alloc_ref $C +// CHECK: [[B1:%.*]] = begin_borrow [[ALLOC]] : $C +// CHECK-NOT: borrow +// CHECK: bb1: +// CHECK-NEXT: end_borrow [[B1]] : $C +// CHECK-NEXT: destroy_value [[ALLOC]] : $C +// CHECK-NEXT: br bb3 +// CHECK: bb2: +// CHECK-NEXT: end_borrow %1 : $C +// CHECK-NEXT: destroy_value %0 : $C +// CHECK-NEXT: br bb3 +// CHECK: bb3: +// CHECK-NOT: destroy +// CHECK-LABEL: } // end sil function 'testNestedBorrowInsideAndOutsideUse' +sil [ossa] @testNestedBorrowInsideAndOutsideUse : $@convention(thin) () -> () { +bb0: + %alloc = alloc_ref $C + %borrow1 = begin_borrow %alloc : $C + %copy1 = copy_value %borrow1 : $C + %borrow2 = begin_borrow %copy1 : $C + %addr = ref_element_addr %borrow2 : $C, #C.a + cond_br undef, bb1, bb2 +bb1: + // inside use + end_borrow %borrow2 : $C + destroy_value %copy1 : $C + end_borrow %borrow1 : $C + br bb3 +bb2: + end_borrow %borrow1 : $C + // outside use + end_borrow %borrow2 : $C + destroy_value %copy1 : $C + br bb3 +bb3: + destroy_value %alloc : $C + %99 = tuple () + return %99 : $() +} + +// The ref_to_unmanaged escapes the pointer. consolidateBorrowScopes +// needs to bail-out even though it's already recursively processing a +// forwarding operation (destructure_struct). +// +// CHECK-LABEL: sil [ossa] @testEscapingForward : $@convention(method) (@guaranteed HasObject) -> () { +// CHECK: begin_borrow %0 : $HasObject +// CHECK: copy_value +// CHECK: destructure_struct +// CHECK: end_borrow +// CHECK: ref_to_unmanaged +// CHECK: destroy_value +// CHECK-LABEL: } // end sil function 'testEscapingForward' +sil [ossa] @testEscapingForward : $@convention(method) (@guaranteed HasObject) -> () { +bb0(%0 : @guaranteed $HasObject): + %1 = begin_borrow %0 : $HasObject + %2 = copy_value %1 : $HasObject + %3 = destructure_struct %2 : $HasObject + end_borrow %1 : $HasObject + %5 = ref_to_unmanaged %3 : $C to $@sil_unmanaged C + destroy_value %3 : $C + %7 = tuple () + return %7 : $() +} + // ============================================================================= // Test reborrows // ============================================================================= @@ -572,15 +678,13 @@ bb3(%borrowphi : @guaranteed $C): // end nested borrow // middle copy -- when consolidating borrow scope, // its operand is replace with a copy of borrowphi -// then removeed when borrowphi's copy is canonicalized +// then removed when borrowphi's copy is canonicalized // end borrowphi -// outer copy -- removeed when borrowphi's copy is canonicalized +// outer copy -- removed when borrowphi's copy is canonicalized // // CHECK-LABEL: sil [ossa] @testNestedReborrowOutsideUse : $@convention(thin) () -> () { // CHECK: [[ALLOC:%.*]] = alloc_ref $C // CHECK: bb3([[BORROWPHI:%.*]] : @guaranteed $C): -// CHECK-NEXT: [[COPY:%.*]] = copy_value [[BORROWPHI]] : $C -// CHECK-NEXT: destroy_value [[COPY]] : $C // CHECK-NEXT: begin_borrow [[BORROWPHI]] : $C // CHECK-NOT: copy // CHECK: end_borrow @@ -653,17 +757,29 @@ bb3(%borrow3 : @guaranteed $C, %copy3 : @owned $C): return %result : $() } +// ============================================================================= +// Test hoisting forwarding instructions out of borrow scopes +// ============================================================================= + // Test conversion from struct_extract to destructure. // +// TODO: Remove the inner borrow scope becaues its outer value is +// already guaranteed and it only has instantaneous uses. Then shrink +// the outer borrow scope, hoist the destructure, rewrite the destroy +// to be of the destructured valud, and finally also remove the dead +// outer borrow scope. See rdar://79149830 (Shrink borrow scopes in +// CanonicalizeBorrowScope) +// // CHECK-LABEL: sil [ossa] @testDestructureConversion : $@convention(thin) (@owned Wrapper) -> () { // CHECK: bb0(%0 : @owned $Wrapper): // CHECK-NOT: copy -// CHECK: [[SPLIT:%.*]] = destructure_struct %0 : $Wrapper +// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $Wrapper +// CHECK: [[SPLIT:%.*]] = destructure_struct [[BORROW]] : $Wrapper // CHECK: [[BORROWINNER:%.*]] = begin_borrow [[SPLIT]] : $HasObjectAndInt // CHECK: debug_value [[BORROWINNER]] : $HasObjectAndInt, let, name "self", argno 1 // CHECK: struct_extract [[BORROWINNER]] : $HasObjectAndInt, #HasObjectAndInt.value // CHECK: end_borrow [[BORROWINNER]] : $HasObjectAndInt -// CHECK: destroy_value [[SPLIT]] : $HasObjectAndInt +// CHECK: destroy_value %0 : $Wrapper // CHECK-LABEL: } // end sil function 'testDestructureConversion' sil [ossa] @testDestructureConversion : $@convention(thin) (@owned Wrapper) -> () { bb0(%0 : @owned $Wrapper): @@ -684,15 +800,263 @@ bb0(%0 : @owned $Wrapper): return %99 : $() } -// FIXME: This borrow scope should also be removed: -// %11 = begin_borrow %10 : $_StringObject // users: %17, %16, %13, %11 +// Test two inner forwards with one inner and one outer use. +// Check that the destroy is removed after sinking the destructure. +// +// CHECK-LABEL: sil [ossa] @testForwardBorrow1 : $@convention(thin) (@owned Wrapper) -> () { +// CHECK: bb0(%0 : @owned $Wrapper): +// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow %0 : $Wrapper +// CHECK-NEXT: [[DSIN1:%.*]] = destructure_struct [[BORROW]] : $Wrapper +// CHECK-NEXT: ([[DSIN2:%.*]], %{{.*}}) = destructure_struct [[DSIN1]] : $HasObjectAndInt +// CHECK: apply %{{.*}}([[DSIN2]]) : $@convention(thin) (@guaranteed C) -> () +// CHECK-NEXT: end_borrow [[BORROW]] : $Wrapper +// CHECK: [[DSOUT1:%.*]] = destructure_struct %0 : $Wrapper +// CHECK-NEXT: ([[DSOUT2:%.*]], %{{.*}}) = destructure_struct [[DSOUT1]] : $HasObjectAndInt +// CHECK: apply %{{.*}}([[DSOUT2]]) : $@convention(thin) (@owned C) -> () +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK-LABEL: } // end sil function 'testForwardBorrow1' +sil [ossa] @testForwardBorrow1 : $@convention(thin) (@owned Wrapper) -> () { +bb0(%0 : @owned $Wrapper): + %1 = begin_borrow %0 : $Wrapper + %2 = destructure_struct %1 : $Wrapper + (%3, %4) = destructure_struct %2 : $HasObjectAndInt + %copy3 = copy_value %3 : $C + %f1 = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> () + %call1 = apply %f1(%3) : $@convention(thin) (@guaranteed C) -> () + end_borrow %1 : $Wrapper + %f2 = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> () + %call2 = apply %f2(%copy3) : $@convention(thin) (@owned C) -> () + destroy_value %0 : $Wrapper + %99 = tuple () + return %99 : $() +} + +// Test two inner forwards where the copy has both and inner and outer uses. +// +// CHECK-LABEL: sil [ossa] @testForwardBorrow2 : $@convention(thin) (@owned Wrapper) -> () { +// CHECK: bb0(%0 : @owned $Wrapper): +// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow %0 : $Wrapper +// CHECK-NEXT: [[DSIN1:%.*]] = destructure_struct [[BORROW]] : $Wrapper +// CHECK-NEXT: ([[DSIN2:%.*]], %{{.*}}) = destructure_struct [[DSIN1]] : $HasObjectAndInt +// CHECK: apply %{{.*}}([[DSIN2]]) : $@convention(thin) (@guaranteed C) -> () +// CHECK-NEXT: end_borrow [[BORROW]] : $Wrapper +// CHECK: [[DSOUT1:%.*]] = destructure_struct %0 : $Wrapper +// CHECK-NEXT: ([[DSOUT2:%.*]], %{{.*}}) = destructure_struct [[DSOUT1]] : $HasObjectAndInt +// CHECK: apply %{{.*}}([[DSOUT2]]) : $@convention(thin) (@owned C) -> () +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK-LABEL: } // end sil function 'testForwardBorrow2' +sil [ossa] @testForwardBorrow2 : $@convention(thin) (@owned Wrapper) -> () { +bb0(%0 : @owned $Wrapper): + %1 = begin_borrow %0 : $Wrapper + %2 = destructure_struct %1 : $Wrapper + (%3, %4) = destructure_struct %2 : $HasObjectAndInt + %copy3 = copy_value %3 : $C + %f1 = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> () + %call1 = apply %f1(%copy3) : $@convention(thin) (@guaranteed C) -> () + end_borrow %1 : $Wrapper + %f2 = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> () + %call2 = apply %f2(%copy3) : $@convention(thin) (@owned C) -> () + destroy_value %0 : $Wrapper + %99 = tuple () + return %99 : $() +} + +// Test two inner forwards with no inner use. +// Remove the borrow and inner forwards. +// +// CHECK-LABEL: sil [ossa] @testForwardBorrow3 : $@convention(thin) (@owned Wrapper) -> () { +// CHECK: bb0(%0 : @owned $Wrapper): +// CHECK-NOT: borrow +// CHECK-NOT: copy +// CHECK: [[DSOUT1:%.*]] = destructure_struct %0 : $Wrapper +// CHECK-NEXT: ([[DSOUT2:%.*]], %{{.*}}) = destructure_struct [[DSOUT1]] : $HasObjectAndInt +// CHECK-NEXT: apply %{{.*}}([[DSOUT2]]) : $@convention(thin) (@owned C) -> () +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK-LABEL: } // end sil function 'testForwardBorrow3' +sil [ossa] @testForwardBorrow3 : $@convention(thin) (@owned Wrapper) -> () { +bb0(%0 : @owned $Wrapper): + %1 = begin_borrow %0 : $Wrapper + %2 = destructure_struct %1 : $Wrapper + (%3, %4) = destructure_struct %2 : $HasObjectAndInt + %copy3 = copy_value %3 : $C + end_borrow %1 : $Wrapper + %f = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> () + %call = apply %f(%copy3) : $@convention(thin) (@owned C) -> () + destroy_value %0 : $Wrapper + %99 = tuple () + return %99 : $() +} + +// Test an inner forwards with two results where both results are owned +// but one has no outer uses. +// Need to create two new destroys in this case. +// +// +// CHECK-LABEL: sil [ossa] @testForwardBorrow4 : $@convention(thin) (@owned MultiWrapper) -> () { +// CHECK: bb0(%0 : @owned $MultiWrapper): +// CHECK-NEXT: (%1, %2) = destructure_struct %0 : $MultiWrapper +// CHECK-NEXT: destroy_value %2 : $HasObjects +// CHECK-NEXT: ([[VAL:%.*]], %5) = destructure_struct %1 : $HasObjects +// CHECK-NEXT: destroy_value %5 : $C +// CHECK-NOT: borrow +// CHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@owned C) -> () +// CHECK-NOT: destroy +// CHECK-LABEL: } // end sil function 'testForwardBorrow4' +sil [ossa] @testForwardBorrow4 : $@convention(thin) (@owned MultiWrapper) -> () { +bb0(%0 : @owned $MultiWrapper): + %1 = begin_borrow %0 : $MultiWrapper + (%2, %3) = destructure_struct %1 : $MultiWrapper + (%4, %5) = destructure_struct %2 : $HasObjects + %copy = copy_value %4 : $C + end_borrow %1 : $MultiWrapper + %f2 = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> () + %call = apply %f2(%copy) : $@convention(thin) (@owned C) -> () + destroy_value %0 : $MultiWrapper + %99 = tuple () + return %99 : $() +} + +// Test a nested forwarding. +// Remove the copy within the outer borrow scope. +// Remove the copy outside the borrow scopes before the final destructure. +// +// TODO: Remove the remaining copy+destructure. It's only use is a +// borrow before it is destructured again later. This should be done +// by the SemanticARC pass that normally combines a copied live range +// with its source live range when the guaranteed uses are within the +// outer scope. But that analysis needs to "see through" destructures. +// +// CHECK-LABEL: sil shared [ossa] @testForwardBorrow5 : $@convention(method) (@owned HasObjectAndInt) -> () { +// CHECK: bb0(%0 : @owned $HasObjectAndInt): +// CHECK-NEXT: %1 = copy_value %0 : $HasObjectAndInt +// CHECK-NEXT: (%2, %3) = destructure_struct %1 : $HasObjectAndInt +// CHECK-NEXT: %4 = begin_borrow %2 : $C +// CHECK-NEXT: %5 = ref_tail_addr %4 : $C, $Builtin.Int8 +// CHECK-NEXT: %6 = index_addr %5 : $*Builtin.Int8, undef : $Builtin.Word +// CHECK-NEXT: %7 = address_to_pointer %6 : $*Builtin.Int8 to $Builtin.RawPointer +// CHECK-NEXT: end_borrow %4 : $C +// CHECK-NEXT: destroy_value %2 : $C +// CHECK: apply %{{.*}}(%7) : $@convention(method) (Builtin.RawPointer) -> () +// CHECK-NEXT: ([[OBJ:%.*]], %{{.*}}) = destructure_struct %0 : $HasObjectAndInt +// CHECK-NEXT: [[EXIS:%.*]] = init_existential_ref [[OBJ]] : $C : $C, $AnyObject +// CHECK-NEXT: fix_lifetime [[EXIS]] : $AnyObject +// CHECK-NEXT: destroy_value [[EXIS]] : $AnyObject +// CHECK-NOT: destroy +// CHECK-LABEL: } // end sil function 'testForwardBorrow5' +sil shared [ossa] @testForwardBorrow5 : $@convention(method) (@owned HasObjectAndInt) -> () { +bb0(%0 : @owned $HasObjectAndInt): + %1 = begin_borrow %0 : $HasObjectAndInt + %2 = struct_extract %1 : $HasObjectAndInt, #HasObjectAndInt.object + %3 = copy_value %2 : $C + %4 = begin_borrow %3 : $C + %5 = ref_tail_addr %4 : $C, $Builtin.Int8 + end_borrow %1 : $HasObjectAndInt + %7 = index_addr %5 : $*Builtin.Int8, undef : $Builtin.Word + %8 = address_to_pointer %7 : $*Builtin.Int8 to $Builtin.RawPointer + end_borrow %4 : $C + destroy_value %3 : $C + // function_ref accessRawP + %11 = function_ref @accessRawP : $@convention(method) (Builtin.RawPointer) -> () + %12 = apply %11(%8) : $@convention(method) (Builtin.RawPointer) -> () + %13 = copy_value %0 : $HasObjectAndInt + (%14, %15) = destructure_struct %13 : $HasObjectAndInt + %16 = init_existential_ref %14 : $C : $C, $AnyObject + fix_lifetime %16 : $AnyObject + destroy_value %16 : $AnyObject + destroy_value %0 : $HasObjectAndInt + %20 = tuple () + return %20 : $() +} + +// Test removing copies within an outer borrow scope with no outer uses. +// +// TODO: The redundant borrow scope should be removed by a SemanticARC pass. +// +// CHECK-LABEL: sil [ossa] @testBorrowCopy : $@convention(thin) (@guaranteed C) -> Int64 { +// CHECK: bb0(%0 : @guaranteed $C): +// CHECK-NEXT: %1 = begin_borrow %0 : $C +// CHECK-NEXT: %2 = begin_borrow %1 : $C +// CHECK-NEXT: %3 = ref_element_addr %2 : $C, #C.a +// CHECK-NEXT: %4 = load [trivial] %3 : $*Int64 +// CHECK-NEXT: end_borrow %2 : $C +// CHECK-NEXT: end_borrow %1 : $C +// CHECK-NEXT: return %4 : $Int64 +// CHECK-LABEL: } // end sil function 'testBorrowCopy' +sil [ossa] @testBorrowCopy : $@convention(thin) (@guaranteed C) -> Int64 { +bb0(%0 : @guaranteed $C): + %borrow = begin_borrow %0 : $C + %copy = copy_value %borrow : $C + %borrow2 = begin_borrow %copy : $C + %adr = ref_element_addr %borrow2 : $C, #C.a + %val = load [trivial] %adr : $*Int64 + end_borrow %borrow2 : $C + destroy_value %copy : $C + end_borrow %borrow : $C + return %val : $Int64 +} + +// Test removing copies of a guaranteed argument. +// +// CHECK-LABEL: sil [ossa] @testCopyArg : $@convention(thin) (@guaranteed C) -> Int64 { +// CHECK: bb0(%0 : @guaranteed $C): +// CHECK-NEXT: %1 = begin_borrow %0 : $C +// CHECK-NEXT: %2 = ref_element_addr %1 : $C, #C.a +// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64 +// CHECK-NEXT: end_borrow %1 : $C +// CHECK-NEXT: return %3 : $Int64 +// CHECK-LABEL: } // end sil function 'testCopyArg' +sil [ossa] @testCopyArg : $@convention(thin) (@guaranteed C) -> Int64 { +bb0(%0 : @guaranteed $C): + %copy = copy_value %0 : $C + %borrow2 = begin_borrow %copy : $C + %adr = ref_element_addr %borrow2 : $C, #C.a + %val = load [trivial] %adr : $*Int64 + end_borrow %borrow2 : $C + destroy_value %copy : $C + return %val : $Int64 +} + +// TODO: Remove this copy inside a borrow scope by shrinking the scope . +// rdar://79149830 (Shrink borrow scopes in CanonicalizeBorrowScope) +// +// Note: shrinking borrow scopes needs to happen even when most borrow +// scopes are eliminated because some "defined" borrow scopes cannot be removed. +// +// CHECK-LABEL: sil [ossa] @testUselessBorrow : $@convention(thin) (@owned HasObject) -> () { +// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $HasObject +// CHECK: [[ELT:%.*]] = destructure_struct [[BORROW]] : $HasObject +// CHECK: [[CP:%.*]] = copy_value [[ELT]] : $C +// CHECK: apply %{{.*}}([[CP]]) : $@convention(thin) (@owned C) -> () +// CHECK: end_borrow [[BORROW]] : $HasObject +// CHECK: destroy_value %0 : $HasObject +// CHECK-LABEL: } // end sil function 'testUselessBorrow' +sil [ossa] @testUselessBorrow : $@convention(thin) (@owned HasObject) -> () { +bb0(%0 : @owned $HasObject): + %borrow = begin_borrow %0 : $HasObject + %object = struct_extract %borrow : $HasObject, #HasObject.object + %copy = copy_value %object : $C + %f = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> () + %call = apply %f(%copy) : $@convention(thin) (@owned C) -> () + end_borrow %borrow : $HasObject + destroy_value %0 : $HasObject + %20 = tuple () + return %20 : $() +} + +// Test a more complicated/realistic example of useless borrows +// +// TODO: Remove this copy inside a borrow scope by shrinking the scope . +// rdar://79149830 (Shrink borrow scopes in CanonicalizeBorrowScope) // // It only has one trivial struct_extract. But copy propagation // currently only removes completely dead borrows. // Removing this borrow scope will unblock removal of the final remaining copy_value: // %_ = copy_value %_ : $String.UTF16View // -// CHECK-LABEL: sil [ossa] @testUselessBorrow : $@convention(thin) (@owned String) -> () { +// CHECK-LABEL: sil [ossa] @testUselessBorrowString : $@convention(thin) (@owned String) -> () { // CHECK: bb0(%0 : @owned $String): // CHECK-NEXT: [[DESTRUCTURE:%.*]] = destructure_struct %0 : $String // CHECK-NEXT: [[UTF16:%.*]] = struct $String.UTF16View ([[DESTRUCTURE]] : $_StringGuts) @@ -725,8 +1089,8 @@ bb0(%0 : @owned $Wrapper): // CHECK: br bb1 // CHECK: bb8: // CHECK-NEXT: destroy_value [[UTF16]] : $String.UTF16View -// CHECK-LABEL: } // end sil function 'testUselessBorrow' -sil [ossa] @testUselessBorrow : $@convention(thin) (@owned String) -> () { +// CHECK-LABEL: } // end sil function 'testUselessBorrowString' +sil [ossa] @testUselessBorrowString : $@convention(thin) (@owned String) -> () { bb0(%0 : @owned $String): %1 = begin_borrow %0 : $String %2 = struct_extract %1 : $String, #String._guts @@ -776,3 +1140,23 @@ bb8: %30 = tuple () return %30 : $() } // end sil function 'testUselessBorrow' + +// Test recursively visiting a guaranteed borrow that is copied within +// the borrow scope and used by a debug_value. +// The copy_value will be deleted when processing the struct. +// +// CHECK-LABEL: sil [ossa] @testCopiedDebugValue : $@convention(method) (@guaranteed String) -> @owned String.UTF16View { +// CHECK: [[DS:%.*]] = destructure_struct %0 : $String +// CHECK-NEXT: [[ST:%.*]] = struct $String.UTF16View ([[DS]] : $_StringGuts) +// CHECK-NEXT: [[CP:%.*]] = copy_value [[ST]] : $String.UTF16View +// CHECK-NEXT: return [[CP]] : $String.UTF16View +// CHECK-LABEL: } // end sil function 'testCopiedDebugValue' +sil [ossa] @testCopiedDebugValue : $@convention(method) (@guaranteed String) -> @owned String.UTF16View { +bb0(%0 : @guaranteed $String): + debug_value %0 : $String, let, name "self", argno 1 + %2 = destructure_struct %0 : $String + %3 = copy_value %2 : $_StringGuts + debug_value %3 : $_StringGuts, let, name "guts" + %5 = struct $String.UTF16View (%3 : $_StringGuts) + return %5 : $String.UTF16View +} diff --git a/test/SILOptimizer/copy_propagation_opaque.sil b/test/SILOptimizer/copy_propagation_opaque.sil index 4419d129a4bdc..972a782c1c7d1 100644 --- a/test/SILOptimizer/copy_propagation_opaque.sil +++ b/test/SILOptimizer/copy_propagation_opaque.sil @@ -139,7 +139,7 @@ bb3(%11 : @owned $T): // // The non-consuming use now uses the original value. // CHECK-DEBUG-NEXT: debug_value %0 : $T -// CHECK-NEXT: debug_value_addr %1 : $*T +// CHECK-NEXT: debug_value %1 : $*T, expr op_deref // // The original destroy is deleted with optimizations enabled. // CHECK-DEBUG-NEXT: destroy_value %0 : $T @@ -152,7 +152,7 @@ bb0(%arg : @owned $T, %addr : $*T): debug_value %copy : $T store %copy to [assign] %addr : $*T debug_value %arg : $T - debug_value_addr %addr : $*T + debug_value %addr : $*T, expr op_deref destroy_value %arg : $T %v = tuple () return %v : $() @@ -394,7 +394,7 @@ bb0(%arg : @owned $T): // CHECK-TRACE-LABEL: *** CopyPropagation: testBorrowCopy // CHECK-TRACE: Outer copy [[OUTERCOPY:%.*]] = copy_value %0 : $T // CHECK-TRACE: Use of outer copy destroy_value -// CHECK-TRACE: Removing %{{.*}} = copy_value +// CHECK-TRACE: Deleting %{{.*}} = copy_value // CHECK-TRACE: Removing destroy_value [[OUTERCOPY]] : $T // CHECK-TRACE: Removing [[OUTERCOPY]] = copy_value %0 : $T // diff --git a/test/SILOptimizer/copyforward_ossa.sil b/test/SILOptimizer/copyforward_ossa.sil index 6caeea6f75be3..7c4670266e3be 100644 --- a/test/SILOptimizer/copyforward_ossa.sil +++ b/test/SILOptimizer/copyforward_ossa.sil @@ -45,7 +45,7 @@ bb2: // Preds: bb0 bb3: // Preds: bb1 bb2 copy_addr [take] %2 to [initialization] %0 : $*T // id: %17 %18 = tuple () // user: %20 - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref dealloc_stack %2 : $*T // id: %19 return %18 : $() // id: %20 } @@ -56,14 +56,13 @@ bb3: // Preds: bb1 bb2 // CHECK-LABEL: } // end sil function 'forward_init' sil hidden [ossa] @forward_init : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %l1 = alloc_stack $T copy_addr %0 to [initialization] %l1 : $*T %f1 = function_ref @f_in : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () %c1 = apply %f1(%l1) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () dealloc_stack %l1 : $*T - // forwardPropagateCopy should cleanup debug_value_addr. See rdar://66000188 - // debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref destroy_addr %0 : $*T %r1 = tuple () return %r1 : $() @@ -75,13 +74,13 @@ bb0(%0 : $*T): // CHECK-LABEL: } // end sil function 'forward_noinit' sil hidden [ossa] @forward_noinit : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %l1 = alloc_stack $T %f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0 %c1 = apply %f1(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0 copy_addr %0 to %l1 : $*T - debug_value_addr %l1 : $*T - debug_value_addr %0 : $*T + debug_value %l1 : $*T, expr op_deref + debug_value %0 : $*T, expr op_deref %f2 = function_ref @f_in : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () %c2 = apply %f2(%l1) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () dealloc_stack %l1 : $*T @@ -96,7 +95,7 @@ bb0(%0 : $*T): // CHECK-LABEL: } // end sil function 'forward_takeinit' sil hidden [ossa] @forward_takeinit : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %l1 = alloc_stack $T copy_addr [take] %0 to [initialization] %l1 : $*T %f1 = function_ref @f_in : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () @@ -112,7 +111,7 @@ bb0(%0 : $*T): // CHECK-LABEL: } // end sil function 'forward_takenoinit' sil hidden [ossa] @forward_takenoinit : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %l1 = alloc_stack $T %f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0 %c1 = apply %f1(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0 @@ -133,11 +132,10 @@ bb0(%0 : $*T): %l1 = alloc_stack $T %f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0 %c1 = apply %f1(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0 - debug_value_addr %l1 : $*T + debug_value %l1 : $*T, expr op_deref copy_addr %l1 to [initialization] %0 : $*T - debug_value_addr %0 : $*T - // backwardPropagateCopy should cleanup debug_value_addr. See rdar://66000188 - // debug_value_addr %l1 : $*T + debug_value %0 : $*T, expr op_deref + debug_value %l1 : $*T, expr op_deref destroy_addr %l1 : $*T dealloc_stack %l1 : $*T %t = tuple () @@ -171,9 +169,9 @@ bb0(%0 : $*T): %l1 = alloc_stack $T %f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0 %c1 = apply %f1(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0 - debug_value_addr %l1 : $*T + debug_value %l1 : $*T, expr op_deref copy_addr [take] %l1 to [initialization] %0 : $*T - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref dealloc_stack %l1 : $*T %t = tuple () return %t : $() @@ -538,7 +536,7 @@ public struct S { // CHECK-LABEL: sil [ossa] @deadtemp : // CHECK: %[[G:.*]] = struct_element_addr %0 : $*S, #S.g // CHECK-NOT: copy_addr -// CHECK-NOT: debug_value_addr +// CHECK-NOT: debug_value {{.*}} expr op_deref // CHECK: %[[F:.*]] = struct_element_addr %0 : $*S, #S.f // CHECK: copy_addr %[[G]] to %[[F]] : $*T // CHECK-LABEL: } // end sil function 'deadtemp' @@ -547,7 +545,7 @@ bb0(%0 : $*S): %1 = struct_element_addr %0 : $*S, #S.g %2 = alloc_stack $T copy_addr %1 to [initialization] %2 : $*T - debug_value_addr %2 : $*T + debug_value %2 : $*T, expr op_deref %4 = struct_element_addr %0 : $*S, #S.f copy_addr [take] %2 to %4 : $*T dealloc_stack %2 : $*T diff --git a/test/SILOptimizer/cow_opts.sil b/test/SILOptimizer/cow_opts.sil index c3b8d9d8d8208..6575f10180fc6 100644 --- a/test/SILOptimizer/cow_opts.sil +++ b/test/SILOptimizer/cow_opts.sil @@ -36,6 +36,7 @@ sil @test_simple : $@convention(thin) (@owned Buffer) -> (Int, Builtin.Int1, @ow bb0(%0 : $Buffer): %e = end_cow_mutation %0 : $Buffer %addr = ref_element_addr [immutable] %e : $Buffer, #Buffer.i + debug_value %e : $Buffer, var, name "x" %i = load %addr : $*Int (%u, %b) = begin_cow_mutation %e : $Buffer %t = tuple (%i : $Int, %u : $Builtin.Int1, %b : $Buffer) diff --git a/test/SILOptimizer/cowarray_opt.sil b/test/SILOptimizer/cowarray_opt.sil index 77873a8536090..eca05d73474eb 100644 --- a/test/SILOptimizer/cowarray_opt.sil +++ b/test/SILOptimizer/cowarray_opt.sil @@ -70,7 +70,7 @@ sil @unknown : $@convention(thin) () -> () // CHECK: } // end sil function 'simple_hoist' sil @simple_hoist : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): - debug_value_addr %0 : $*MyArray + debug_value %0 : $*MyArray, expr op_deref %2 = load %0 : $*MyArray br bb1 @@ -224,7 +224,7 @@ bb2: // CHECK: bb3: sil @dont_hoist_if_executed_conditionally : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> MyArray { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): - debug_value_addr %0 : $*MyArray + debug_value %0 : $*MyArray, expr op_deref %2 = load %0 : $*MyArray br bb1(%2 : $MyArray) diff --git a/test/SILOptimizer/cowarray_opt_ossa.sil b/test/SILOptimizer/cowarray_opt_ossa.sil index 024d56a8a43ce..7dcba769fe6b7 100644 --- a/test/SILOptimizer/cowarray_opt_ossa.sil +++ b/test/SILOptimizer/cowarray_opt_ossa.sil @@ -70,7 +70,7 @@ sil [ossa] @unknown : $@convention(thin) () -> () // CHECK: } // end sil function 'simple_hoist' sil [ossa] @simple_hoist : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): - debug_value_addr %0 : $*MyArray + debug_value %0 : $*MyArray, expr op_deref %2 = load [copy] %0 : $*MyArray br bb1 @@ -283,7 +283,7 @@ bb2: // CHECK-LABEL: } // end sil function 'dont_hoist_if_executed_conditionally' sil [ossa] @dont_hoist_if_executed_conditionally : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> @owned MyArray { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): - debug_value_addr %0 : $*MyArray + debug_value %0 : $*MyArray, expr op_deref %2 = load [copy] %0 : $*MyArray br bb1(%2 : $MyArray) diff --git a/test/SILOptimizer/cross-module-optimization-objc.swift b/test/SILOptimizer/cross-module-optimization-objc.swift index 8d3129a3c1735..bf692cf641501 100644 --- a/test/SILOptimizer/cross-module-optimization-objc.swift +++ b/test/SILOptimizer/cross-module-optimization-objc.swift @@ -1,7 +1,7 @@ // First test: functional correctness // RUN: %empty-directory(%t) -// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Test.swiftmodule -module-name=Test -I%t %S/Inputs/cross-module-objc.swift -c -o %t/test.o +// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Test.swiftmodule -module-name=Test -I%t %S/Inputs/cross-module/cross-module-objc.swift -c -o %t/test.o // RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -c -o %t/main.o // RUN: %target-swiftc_driver %t/main.o %t/test.o -o %t/a.out // RUN: %target-codesign %t/a.out diff --git a/test/SILOptimizer/cross-module-optimization.swift b/test/SILOptimizer/cross-module-optimization.swift index 7ccf14386faa3..e8934eafcb059 100644 --- a/test/SILOptimizer/cross-module-optimization.swift +++ b/test/SILOptimizer/cross-module-optimization.swift @@ -1,18 +1,19 @@ // First test: functional correctness // RUN: %empty-directory(%t) -// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Submodule.swiftmodule -module-name=Submodule %S/Inputs/cross-submodule.swift -c -o %t/submodule.o -// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/PrivateSubmodule.swiftmodule -module-name=PrivateSubmodule %S/Inputs/cross-private-submodule.swift -c -o %t/privatesubmodule.o -// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Test.swiftmodule -module-name=Test -I%t %S/Inputs/cross-module.swift -c -o %t/test.o +// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Submodule.swiftmodule -module-name=Submodule %S/Inputs/cross-module/cross-submodule.swift -c -o %t/submodule.o +// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/PrivateSubmodule.swiftmodule -module-name=PrivateSubmodule %S/Inputs/cross-module/cross-private-submodule.swift -c -o %t/privatesubmodule.o +// RUN: %target-clang -c --language=c %S/Inputs/cross-module/c-module.c -o %t/c-module.o +// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Test.swiftmodule -module-name=Test -I%t -I%S/Inputs/cross-module %S/Inputs/cross-module/cross-module.swift -c -o %t/test.o // RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -c -o %t/main.o -// RUN: %target-swiftc_driver %t/main.o %t/test.o %t/submodule.o %t/privatesubmodule.o -o %t/a.out +// RUN: %target-swiftc_driver %t/main.o %t/test.o %t/submodule.o %t/privatesubmodule.o %t/c-module.o -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT // Check if it also works if the main module is compiled with -Onone: // RUN: %target-build-swift -Onone -wmo -module-name=Main -I%t %s -c -o %t/main-onone.o -// RUN: %target-swiftc_driver %t/main-onone.o %t/test.o %t/submodule.o %t/privatesubmodule.o -o %t/a.out +// RUN: %target-swiftc_driver %t/main-onone.o %t/test.o %t/submodule.o %t/privatesubmodule.o %t/c-module.o -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT @@ -146,8 +147,14 @@ func testGlobal() { @inline(never) func testImplementationOnly() { // CHECK-OUTPUT: 27 - // CHECK-SIL2: function_ref @$s4Test22callImplementationOnlyyxxlF - print(callImplementationOnly(27)) + // CHECK-SIL2: function_ref @$s4Test26callImplementationOnlyTypeyxxlF + print(callImplementationOnlyType(27)) + // CHECK-OUTPUT: 40 + // CHECK-SIL2: function_ref @$s4Test26callImplementationOnlyFuncySixlF + print(callImplementationOnlyFunc(0)) + // CHECK-OUTPUT: 123 + // CHECK-SIL2: function_ref @$s4Test23callCImplementationOnlyySixlF + print(callCImplementationOnly(0)) // CHECK-SIL2: } // end sil function '$s4Main22testImplementationOnlyyyF' } diff --git a/test/SILOptimizer/cse.sil b/test/SILOptimizer/cse.sil index afa0be95da856..758a4242643ce 100644 --- a/test/SILOptimizer/cse.sil +++ b/test/SILOptimizer/cse.sil @@ -1125,7 +1125,7 @@ protocol Pingable { func ping() } // CHECK: return sil hidden @CSE_Existential_Simple : $@convention(thin) (@in Pingable) -> () { bb0(%0 : $*Pingable): - debug_value_addr %0 : $*Pingable // let x // id: %1 + debug_value %0 : $*Pingable, expr op_deref // let x // id: %1 %2 = open_existential_addr immutable_access %0 : $*Pingable to $*@opened("1E467EB8-D5C5-11E5-8C0E-A82066121073") Pingable // users: %3, %4 %3 = witness_method $@opened("1E467EB8-D5C5-11E5-8C0E-A82066121073") Pingable, #Pingable.ping, %2 : $*@opened("1E467EB8-D5C5-11E5-8C0E-A82066121073") Pingable : $@convention(witness_method: Pingable) <τ_0_0 where τ_0_0 : Pingable> (@in_guaranteed τ_0_0) -> () // user: %4 %4 = apply %3<@opened("1E467EB8-D5C5-11E5-8C0E-A82066121073") Pingable>(%2) : $@convention(witness_method: Pingable) <τ_0_0 where τ_0_0 : Pingable> (@in_guaranteed τ_0_0) -> () @@ -1140,7 +1140,7 @@ bb0(%0 : $*Pingable): // CHECK-LABEL: CSE_Existential_Calls_With_Control_Flow sil hidden @CSE_Existential_Calls_With_Control_Flow : $@convention(thin) (@in Pingable, Int) -> () { bb0(%0 : $*Pingable, %1 : $Int): - debug_value_addr %0 : $*Pingable // let x // id: %2 + debug_value %0 : $*Pingable, expr op_deref // let x // id: %2 debug_value %1 : $Int // let y // id: %3 %4 = open_existential_addr immutable_access %0 : $*Pingable to $*@opened("75F1B81A-D6CB-11E5-9470-A82066121073") Pingable // users: %5, %6 %5 = witness_method $@opened("75F1B81A-D6CB-11E5-9470-A82066121073") Pingable, #Pingable.ping, %4 : $*@opened("75F1B81A-D6CB-11E5-9470-A82066121073") Pingable : $@convention(witness_method: Pingable) <τ_0_0 where τ_0_0 : Pingable> (@in_guaranteed τ_0_0) -> () // user: %6 diff --git a/test/SILOptimizer/cse_apply_ossa.sil b/test/SILOptimizer/cse_apply_ossa.sil index 3713a24328181..eac5f22c51e3c 100644 --- a/test/SILOptimizer/cse_apply_ossa.sil +++ b/test/SILOptimizer/cse_apply_ossa.sil @@ -122,11 +122,11 @@ bb0(%0 : $Int64): return %14 : $Int64 } -// CHECK-LABEL: sil [ossa] @dont_cse_retain_only_apply : -// CHECK: %{{[0-9]+}} = apply -// CHECK: %{{[0-9]+}} = apply -// CHECK-LABEL:} // end sil function 'dont_cse_retain_only_apply' -sil [ossa] @dont_cse_retain_only_apply : $@convention(thin) () -> () { +// CHECK-LABEL: sil [ossa] @cse_retain_only_apply : +// CHECK: apply +// CHECK-NOT: apply +// CHECK-LABEL:} // end sil function 'cse_retain_only_apply' +sil [ossa] @cse_retain_only_apply : $@convention(thin) () -> () { bb0: %f = function_ref @retain_only : $@convention(thin) () -> @owned XX %a1 = apply %f() : $@convention(thin) () -> @owned XX @@ -169,7 +169,7 @@ bb0(%0 : @guaranteed $Klass): // CHECK-LABEL: sil [ossa] @apply_nontrivial_test2 : // CHECK: apply -// CHECK: apply +// CHECK-NOT: apply // CHECK-LABEL:} // end sil function 'apply_nontrivial_test2' sil [ossa] @apply_nontrivial_test2 : $@convention(thin) (@guaranteed Klass) -> () { bb0(%0 : @guaranteed $Klass): diff --git a/test/SILOptimizer/cse_ossa.sil b/test/SILOptimizer/cse_ossa.sil index 4b8f2e9dc76a8..44d8fd21da2c6 100644 --- a/test/SILOptimizer/cse_ossa.sil +++ b/test/SILOptimizer/cse_ossa.sil @@ -4,7 +4,9 @@ sil_stage canonical import Builtin import Swift -class B { } +class B { + func foo() +} class E : B { } class C {} @@ -1025,7 +1027,7 @@ protocol Pingable { func ping() } // CHECK-LABEL: } // end sil function 'CSE_Existential_Simple' sil hidden [ossa] @CSE_Existential_Simple : $@convention(thin) (@in Pingable) -> () { bb0(%0 : $*Pingable): - debug_value_addr %0 : $*Pingable // let x + debug_value %0 : $*Pingable, expr op_deref // let x %2 = open_existential_addr immutable_access %0 : $*Pingable to $*@opened("1E467EB8-D5C5-11E5-8C0E-A82066121073") Pingable %3 = witness_method $@opened("1E467EB8-D5C5-11E5-8C0E-A82066121073") Pingable, #Pingable.ping, %2 : $*@opened("1E467EB8-D5C5-11E5-8C0E-A82066121073") Pingable : $@convention(witness_method: Pingable) <τ_0_0 where τ_0_0 : Pingable> (@in_guaranteed τ_0_0) -> () %4 = apply %3<@opened("1E467EB8-D5C5-11E5-8C0E-A82066121073") Pingable>(%2) : $@convention(witness_method: Pingable) <τ_0_0 where τ_0_0 : Pingable> (@in_guaranteed τ_0_0) -> () @@ -1040,7 +1042,7 @@ bb0(%0 : $*Pingable): // CHECK-LABEL: sil hidden [ossa] @CSE_Existential_Calls_With_Control_Flow : sil hidden [ossa] @CSE_Existential_Calls_With_Control_Flow : $@convention(thin) (@in Pingable, Int) -> () { bb0(%0 : $*Pingable, %1 : $Int): - debug_value_addr %0 : $*Pingable // let x + debug_value %0 : $*Pingable, expr op_deref // let x debug_value %1 : $Int // let y %4 = open_existential_addr immutable_access %0 : $*Pingable to $*@opened("75F1B81A-D6CB-11E5-9470-A82066121073") Pingable %5 = witness_method $@opened("75F1B81A-D6CB-11E5-9470-A82066121073") Pingable, #Pingable.ping, %4 : $*@opened("75F1B81A-D6CB-11E5-9470-A82066121073") Pingable : $@convention(witness_method: Pingable) <τ_0_0 where τ_0_0 : Pingable> (@in_guaranteed τ_0_0) -> () @@ -1223,3 +1225,95 @@ bb0(%0 : $*Builtin.Int64, %1 : @guaranteed $Builtin.NativeObject): destroy_value %copy : $Builtin.NativeObject return %6 : $(Builtin.Int64, Builtin.Int64) } + +final class LazyKlass { + final var lazyProperty: Int64 { get set } + @_hasStorage @_hasInitialValue final var lazyPropertyStorage : Int64? { get set } + init() +} + +sil private [lazy_getter] [noinline] @lazy_class_getter_nonossa : $@convention(method) (@guaranteed LazyKlass) -> Int64 { +bb0(%0 : $LazyKlass): + %2 = ref_element_addr %0 : $LazyKlass, #LazyKlass.lazyPropertyStorage + %3 = load %2 : $*Optional + switch_enum %3 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 + +bb1(%5 : $Int64): + br bb3(%5 : $Int64) + +bb2: + %9 = integer_literal $Builtin.Int64, 27 + %10 = struct $Int64 (%9 : $Builtin.Int64) + %12 = enum $Optional, #Optional.some!enumelt, %10 : $Int64 + store %12 to %2 : $*Optional + br bb3(%10 : $Int64) + +bb3(%15 : $Int64): + return %15 : $Int64 +} + +sil private [lazy_getter] [noinline] [ossa] @lazy_class_getter_ossa : $@convention(method) (@guaranteed LazyKlass) -> Int64 { +bb0(%0 : @guaranteed $LazyKlass): + %2 = ref_element_addr %0 : $LazyKlass, #LazyKlass.lazyPropertyStorage + %3 = load [trivial] %2 : $*Optional + switch_enum %3 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 + +bb1(%5 : $Int64): + br bb3(%5 : $Int64) + +bb2: + %9 = integer_literal $Builtin.Int64, 27 + %10 = struct $Int64 (%9 : $Builtin.Int64) + %12 = enum $Optional, #Optional.some!enumelt, %10 : $Int64 + store %12 to [trivial] %2 : $*Optional + br bb3(%10 : $Int64) + +bb3(%15 : $Int64): + return %15 : $Int64 +} + +// CHECK-LABEL: sil [ossa] @dont_cse_nonossa_getter : +// CHECK: [[GETTER:%[0-9]+]] = function_ref @lazy_class_getter_nonossa +// CHECK: apply [[GETTER]] +// CHECK: apply [[GETTER]] +// CHECK: } // end sil function 'dont_cse_nonossa_getter' +sil [ossa] @dont_cse_nonossa_getter : $@convention(thin) (@guaranteed LazyKlass) -> (Int64, Int64) { +bb0(%0 : @guaranteed $LazyKlass): + %getter = function_ref @lazy_class_getter_nonossa : $@convention(method) (@guaranteed LazyKlass) -> Int64 + %call1 = apply %getter(%0) : $@convention(method) (@guaranteed LazyKlass) -> Int64 + %call2 = apply %getter(%0) : $@convention(method) (@guaranteed LazyKlass) -> Int64 + %res = tuple (%call1 : $Int64, %call2 : $Int64) + return %res : $(Int64, Int64) +} + +// CHECK-LABEL: sil @cse_ossa_getter : +// CHECK: [[GETTER:%[0-9]+]] = function_ref @lazy_class_getter_ossa +// CHECK: apply [[GETTER]] +// CHECK-NOT: apply [[GETTER]] +// CHECK: } // end sil function 'cse_ossa_getter' +sil @cse_ossa_getter : $@convention(thin) (@guaranteed LazyKlass) -> (Int64, Int64) { +bb0(%0 : $LazyKlass): + %1 = function_ref @lazy_class_getter_ossa : $@convention(method) (@guaranteed LazyKlass) -> Int64 + %2 = apply %1(%0) : $@convention(method) (@guaranteed LazyKlass) -> Int64 + %3 = apply %1(%0) : $@convention(method) (@guaranteed LazyKlass) -> Int64 + %4 = tuple (%2 : $Int64, %3 : $Int64) + return %4 : $(Int64, Int64) +} + +// CHECK-LABEL: sil [ossa] @test_end_lifetime : +// CHECK: upcast +// CHECK-NOT: upcast +// CHECK: } // end sil function 'test_end_lifetime' +sil [ossa] @test_end_lifetime : $@convention(method) (@owned E) -> () { +bb0(%0 : @owned $E): + %copy = copy_value %0 : $E + %u1 = upcast %0 : $E to $B + %f = objc_method %u1 : $B, #B.foo!foreign : (B) -> () -> (), $@convention(objc_method) (B) -> () + %c1 = apply %f(%u1) : $@convention(objc_method) (B) -> () + end_lifetime %u1 : $B + %u2 = upcast %copy : $E to $B + %c2 = apply %f(%u2) : $@convention(objc_method) (B) -> () + end_lifetime %u2 : $B + %r = tuple () + return %r : $() +} diff --git a/test/SILOptimizer/cse_ossa_nontrivial.sil b/test/SILOptimizer/cse_ossa_nontrivial.sil index 4e42350d219f5..f86028fc61e24 100644 --- a/test/SILOptimizer/cse_ossa_nontrivial.sil +++ b/test/SILOptimizer/cse_ossa_nontrivial.sil @@ -830,3 +830,66 @@ bb0(%0 : @guaranteed $Klass): return %6 : $() } +class TestKlass { + var testStruct: NonTrivialStruct +} + +sil [ossa] @use_klass : $@convention(thin) (@guaranteed Klass) -> () + +// Currently ownership rauw does not handle this case +// CHECK-LABEL: sil [ossa] @cse_ossa_accesspathclonetest : +// CHECK: struct_element_addr +// CHECK: struct_element_addr +// CHECK-LABEL: } // end sil function 'cse_ossa_accesspathclonetest' +sil [ossa] @cse_ossa_accesspathclonetest : $@convention(thin) (@guaranteed TestKlass) -> () { +bb0(%0 : @guaranteed $TestKlass): + %1 = ref_element_addr %0 : $TestKlass, #TestKlass.testStruct + %2 = begin_access [modify] [dynamic] %1 : $*NonTrivialStruct + %3 = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.val + %4 = load_borrow %3 : $*Klass + %f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> () + %c = apply %f(%4) : $@convention(thin) (@guaranteed Klass) -> () + end_borrow %4 : $Klass + %3a = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.val + %4a = load_borrow %3a : $*Klass + %ca = apply %f(%4a) : $@convention(thin) (@guaranteed Klass) -> () + end_borrow %4a : $Klass + end_access %2 : $*NonTrivialStruct + %139 = tuple () + return %139 : $() +} + +struct X { + var i: Int64 +} + +sil_global hidden @globalX : $X + +sil [ossa] @use_address : $@convention(thin) (@in_guaranteed X) -> () + +// CHECK-LABEL: sil [ossa] @cse_ossa_validrauwfollowinvalidrauw : +// CHECK: global_addr +// CHECK-NOT: global_addr +// CHECK-LABEL: } // end sil function 'cse_ossa_validrauwfollowinvalidrauw' +sil [ossa] @cse_ossa_validrauwfollowinvalidrauw : $@convention(thin) (@guaranteed TestKlass) -> () { +bb0(%0 : @guaranteed $TestKlass): + %1 = function_ref @use_address : $@convention(thin) (@in_guaranteed X) -> () + %2 = ref_element_addr %0 : $TestKlass, #TestKlass.testStruct + %3 = begin_access [modify] [dynamic] %2 : $*NonTrivialStruct + %4 = struct_element_addr %3 : $*NonTrivialStruct, #NonTrivialStruct.val + %5 = load_borrow %4 : $*Klass + %6 = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Klass) -> () + end_borrow %5 : $Klass + %9 = global_addr @globalX : $*X + %10 = apply %1(%9) : $@convention(thin) (@in_guaranteed X) -> () + %11 = struct_element_addr %3 : $*NonTrivialStruct, #NonTrivialStruct.val + %12 = load_borrow %11 : $*Klass + %13 = apply %6(%12) : $@convention(thin) (@guaranteed Klass) -> () + end_borrow %12 : $Klass + %15 = global_addr @globalX : $*X + %16 = apply %1(%15) : $@convention(thin) (@in_guaranteed X) -> () + end_access %3 : $*NonTrivialStruct + %18 = tuple () + return %18 : $() +} diff --git a/test/SILOptimizer/dead_alloc_elim.sil b/test/SILOptimizer/dead_alloc_elim.sil index 9a61c73e34d89..971d0bf52e348 100644 --- a/test/SILOptimizer/dead_alloc_elim.sil +++ b/test/SILOptimizer/dead_alloc_elim.sil @@ -164,7 +164,6 @@ sil @trivial_destructor_load : $@convention(thin) () -> () { // // CHECK-LABEL: sil @trivial_destructor_store_into : $@convention(thin) () -> () { // CHECK: bb0 -// CHECK-NEXT: integer_literal // CHECK-NEXT: tuple // CHECK-NEXT: return sil @trivial_destructor_store_into : $@convention(thin) () -> () { diff --git a/test/SILOptimizer/dead_alloc_stack.swift b/test/SILOptimizer/dead_alloc_stack.swift index b807c42c24f2e..457253f8a3a34 100644 --- a/test/SILOptimizer/dead_alloc_stack.swift +++ b/test/SILOptimizer/dead_alloc_stack.swift @@ -21,6 +21,7 @@ func g(_ x : T) -> Bool { // CHECK-LABEL: sil @$s16dead_alloc_stack6testitySbAA1XVF // CHECK: bb0({{.*}}): +// CHECK-NEXT: debug_value // CHECK-NEXT: integer_literal // CHECK-NEXT: struct // CHECK-NEXT: return diff --git a/test/SILOptimizer/dead_array_elim.swift b/test/SILOptimizer/dead_array_elim.swift index d2399c07599b5..c3359d82ebcae 100644 --- a/test/SILOptimizer/dead_array_elim.swift +++ b/test/SILOptimizer/dead_array_elim.swift @@ -59,8 +59,9 @@ func testDeadArrayElimWithAddressOnlyValues(x: T, y: T) { // CHECK-LABEL: sil hidden @$s15dead_array_elim31testDeadArrayAfterOptimizationsySiSSF // CHECK: bb0(%0 : $String): -// CHECK-NEXT: debug_value {{.*}} name "stringParameter" +// CHECK-NEXT: debug_value // CHECK-NEXT: integer_literal $Builtin.Int{{[0-9]+}}, 21 +// CHECK-NEXT: debug_value // CHECK-NEXT: struct $Int // CHECK-NEXT: return // CHECK: } // end sil function '$s15dead_array_elim31testDeadArrayAfterOptimizationsySiSSF' @@ -78,4 +79,46 @@ func testDeadArrayAfterOptimizations(_ stringParameter: String) -> Int { return sum } +// CHECK-LABEL: sil hidden @$s15dead_array_elim15testNestedArraySiyF +// CHECK: bb0: +// CHECK-NEXT: integer_literal $Builtin.Int{{[0-9]+}}, 3 +// CHECK-NEXT: debug_value +// CHECK-NEXT: struct $Int +// CHECK-NEXT: return +// CHECK: } // end sil function '$s15dead_array_elim15testNestedArraySiyF' +func testNestedArray() -> Int { + struct Str { + var sa: [String] + var s: String? = nil + var b: Bool = true + + static func opt1(_ sa: [String], _ s: String?) -> Self { + return .init(sa: sa, s: s, b: true) + } + + static func opt2(_ s1: String, _ s2: String? = nil) -> Self { + return .opt1([s1], s2) + } + + static func opt3(_ sa: [String], _ s: String) -> Self { + return .init(sa: sa, s: s, b: false) + } + } + + let strArr: [Str] = [ + .opt1(["1", "2"], "3"), + .opt3(["4", "5"], "6"), + + .opt2("7"), + .opt2("8"), + ] + + var num = 0 + for s in strArr { + if s.b { + num += 1 + } + } + return num +} diff --git a/test/SILOptimizer/dead_bridging_code.swift b/test/SILOptimizer/dead_bridging_code.swift index 519e633a02a8a..260f43b25f4f9 100644 --- a/test/SILOptimizer/dead_bridging_code.swift +++ b/test/SILOptimizer/dead_bridging_code.swift @@ -9,7 +9,8 @@ import Foundation class Myclass : NSObject { // CHECK-LABEL: sil hidden [thunk] @$s4test7MyclassC3fooyySSFTo -// CHECK-NEXT: bb0(%0 : $NSString, %1 : $Myclass): +// CHECK: bb0(%0 : $NSString, %1 : $Myclass): +// CHECK-NEXT: debug_value // CHECK-NEXT: tuple () // CHECK-NEXT: return @objc func foo(_ s: String) { diff --git a/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil b/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil index 766c95d8a9afb..6e37fa0939560 100644 --- a/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil +++ b/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil @@ -611,3 +611,44 @@ bb3: return %9999 : $() } +// CHECK-LABEL: sil [ossa] @dce_deadendlifetime1 : +// CHECK-NOT: end_lifetime +// CHECK-LABEL: } // end sil function 'dce_deadendlifetime1' +sil [ossa] @dce_deadendlifetime1 : $@convention(thin) () -> () { +bb0: + cond_br undef, bb1, bb2 + +bb1: + br bb3(undef : $Klass) + +bb2: + br bb3(undef : $Klass) + +bb3(%2 : @owned $Klass): + end_lifetime %2 : $Klass + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @dce_deadendlifetime2 : +// CHECK-NOT: end_lifetime +// CHECK-LABEL: } // end sil function 'dce_deadendlifetime2' +sil [ossa] @dce_deadendlifetime2 : $@convention(thin) () -> () { +bb0: + cond_br undef, bb1, bb2 + +bb1: + br bb3(undef : $Klass) + +bb2: + br bb3(undef : $Klass) + +bb3(%2 : @owned $Klass): + br bb4(%2 : $Klass) + +bb4(%3 : @owned $Klass): + end_lifetime %3 : $Klass + %res = tuple () + return %res : $() +} + diff --git a/test/SILOptimizer/dead_store_elim.sil b/test/SILOptimizer/dead_store_elim.sil index d04204b7c87fd..bc118afa5c7e2 100644 --- a/test/SILOptimizer/dead_store_elim.sil +++ b/test/SILOptimizer/dead_store_elim.sil @@ -366,21 +366,20 @@ bb2: return %9999 : $() } -// We cannot remove the local store as the debug_value_addr could -// be turned to a debug_value and thus act as a read on the memory -// location.. +// We cannot remove the local store as the debug_value w/ address value +// could be promoted and thus act as a read on the memory location.. // // CHECK-LABEL: blocking_debug_value_addr_on_dead_store // CHECK: bb0 // CHECK: {{ store}} -// CHECK: debug_value_addr +// CHECK: debug_value %{{.*}} : $*Int sil hidden @blocking_debug_value_addr_on_dead_store : $@convention(thin) () -> () { bb0: %0 = alloc_stack $Int, var, name "a" %1 = integer_literal $Builtin.Int64, 1 %2 = struct $Int (%1 : $Builtin.Int64) store %2 to %0 : $*Int - debug_value_addr %0 : $*Int + debug_value %0 : $*Int %4 = tuple () dealloc_stack %0 : $*Int return %4 : $() diff --git a/test/SILOptimizer/definite-init-convert-to-escape.swift b/test/SILOptimizer/definite-init-convert-to-escape.swift index f5e1b788a6224..af25e56f3f882 100644 --- a/test/SILOptimizer/definite-init-convert-to-escape.swift +++ b/test/SILOptimizer/definite-init-convert-to-escape.swift @@ -9,7 +9,7 @@ import Foundation -// Make sure that we keep the escaping closures alive accross the ultimate call. +// Make sure that we keep the escaping closures alive across the ultimate call. // CHECK-LABEL: sil @$s1A19bridgeNoescapeBlock5optFn0D3Fn2yySSSgcSg_AFtF // CHECK: bb0 // CHECK: retain_value %0 @@ -39,7 +39,7 @@ public func bridgeNoescapeBlock( optFn: ((String?) -> ())?, optFn2: ((String?) - @_silgen_name("_returnOptionalEscape") public func returnOptionalEscape() -> (() ->())? -// Make sure that we keep the escaping closure alive accross the ultimate call. +// Make sure that we keep the escaping closure alive across the ultimate call. // CHECK-LABEL: sil @$s1A19bridgeNoescapeBlockyyF : $@convention(thin) () -> () { // CHECK: bb0: diff --git a/test/SILOptimizer/definite_init_actor.swift b/test/SILOptimizer/definite_init_actor.swift new file mode 100644 index 0000000000000..a9d7d54d2fcbf --- /dev/null +++ b/test/SILOptimizer/definite_init_actor.swift @@ -0,0 +1,245 @@ +// RUN: %target-swift-frontend -module-name test -disable-availability-checking -swift-version 5 -sil-verify-all -emit-sil %s | %FileCheck --enable-var-scope --implicit-check-not='hop_to_executor' %s + +// REQUIRES: concurrency + + enum ActingError : Error { + case forgotLine + case smuggledValue(T) + } + +func neverReturn() -> Never { fatalError("quit!") } + +actor BoringActor { + + // CHECK-LABEL: sil hidden @$s4test11BoringActorCACyYacfc : $@convention(method) @async (@owned BoringActor) -> @owned BoringActor { + // CHECK: bb0([[SELF:%[0-9]+]] : $BoringActor): + // CHECK: initializeDefaultActor + // CHECK-NEXT: hop_to_executor [[SELF]] + // CHECK: } // end sil function '$s4test11BoringActorCACyYacfc' + init() async {} + + // CHECK-LABEL: sil hidden @$s4test11BoringActorC4sizeACSi_tYacfc : $@convention(method) @async (Int, @owned BoringActor) -> @owned BoringActor { + // CHECK: bb0({{%[0-9]+}} : $Int, [[SELF:%[0-9]+]] : $BoringActor): + // CHECK: initializeDefaultActor + // CHECK-NEXT: hop_to_executor [[SELF]] + // CHECK: } // end sil function '$s4test11BoringActorC4sizeACSi_tYacfc' + init(size: Int) async { + var sz = size + while sz > 0 { + print(".") + sz -= 1 + } + print("done!") + } + + @MainActor + init(mainActor: Void) async {} + + // CHECK-LABEL: sil hidden @$s4test11BoringActorC6crashyACSgyt_tYacfc : $@convention(method) @async (@owned BoringActor) -> @owned Optional { + // CHECK: bb0([[SELF:%[0-9]+]] : $BoringActor): + // CHECK: initializeDefaultActor + // CHECK-NEXT: hop_to_executor [[SELF]] + // CHECK: } // end sil function '$s4test11BoringActorC6crashyACSgyt_tYacfc' + init!(crashy: Void) async { return nil } + + // CHECK-LABEL: sil hidden @$s4test11BoringActorC5nillyACSgSi_tYacfc : $@convention(method) @async (Int, @owned BoringActor) -> @owned Optional { + // CHECK: bb0({{%[0-9]+}} : $Int, [[SELF:%[0-9]+]] : $BoringActor): + // CHECK: initializeDefaultActor + // CHECK-NEXT: hop_to_executor [[SELF]] + // CHECK: } // end sil function '$s4test11BoringActorC5nillyACSgSi_tYacfc' + init?(nilly: Int) async { + guard nilly > 0 else { return nil } + } +} + + actor SingleVarActor { + var myVar: Int + + // CHECK-LABEL: sil hidden @$s4test14SingleVarActorCACyYacfc : $@convention(method) @async (@owned SingleVarActor) -> @owned SingleVarActor { + // CHECK: bb0([[SELF:%[0-9]+]] : $SingleVarActor): + // CHECK: store {{%[0-9]+}} to [[ACCESS:%[0-9]+]] + // CHECK-NEXT: end_access [[ACCESS]] + // CHECK-NEXT: hop_to_executor [[SELF]] : $SingleVarActor + // CHECK: store {{%[0-9]+}} to {{%[0-9]+}} + // CHECK: } // end sil function '$s4test14SingleVarActorCACyYacfc' + init() async { + myVar = 0 + myVar = 1 + } + + // CHECK-LABEL: sil hidden @$s4test14SingleVarActorC10iterationsACSi_tYacfc : $@convention(method) @async (Int, @owned SingleVarActor) -> @owned SingleVarActor { + // CHECK: bb0({{%[0-9]+}} : $Int, [[SELF:%[0-9]+]] : $SingleVarActor): + // CHECK: [[MYVAR_REF:%[0-9]+]] = ref_element_addr [[SELF]] : $SingleVarActor, #SingleVarActor.myVar + // CHECK: [[MYVAR:%[0-9]+]] = begin_access [modify] [dynamic] [[MYVAR_REF]] : $*Int + // CHECK: store {{%[0-9]+}} to [[MYVAR]] : $*Int + // CHECK-NEXT: end_access [[MYVAR]] + // CHECK-NEXT: hop_to_executor [[SELF]] : $SingleVarActor + // CHECK: } // end sil function '$s4test14SingleVarActorC10iterationsACSi_tYacfc' + init(iterations: Int) async { + var iter = iterations + repeat { + myVar = 0 + iter -= 1 + } while iter > 0 + } + + // CHECK-LABEL: sil hidden @$s4test14SingleVarActorC2b12b2ACSb_SbtYacfc : $@convention(method) @async (Bool, Bool, @owned SingleVarActor) -> @owned SingleVarActor { + // CHECK: bb0({{%[0-9]+}} : $Bool, {{%[0-9]+}} : $Bool, [[SELF:%[0-9]+]] : $SingleVarActor): + + // CHECK: store {{%[0-9]+}} to [[A1:%[0-9]+]] : $*Int + // CHECK-NEXT: end_access [[A1]] + // CHECK-NEXT: hop_to_executor [[SELF]] : $SingleVarActor + + // CHECK: store {{%[0-9]+}} to [[A2:%[0-9]+]] : $*Int + // CHECK-NEXT: end_access [[A2]] + // CHECK-NEXT: hop_to_executor [[SELF]] : $SingleVarActor + + // CHECK: store {{%[0-9]+}} to [[A3:%[0-9]+]] : $*Int + // CHECK-NEXT: end_access [[A3]] + // CHECK-NEXT: hop_to_executor [[SELF]] : $SingleVarActor + + // CHECK: } // end sil function '$s4test14SingleVarActorC2b12b2ACSb_SbtYacfc' + init(b1: Bool, b2: Bool) async { + if b1 { + if b2 { + myVar = 0 + } + myVar = 1 + } + myVar = 2 + } + } + +actor DefaultInit { + var x: DefaultInit? + var y: String = "" + var z: ActingError = .smuggledValue(5) + + // CHECK-LABEL: sil hidden @$s4test11DefaultInitCACyYacfc : $@convention(method) @async (@owned DefaultInit) -> @owned DefaultInit { + // CHECK: bb0([[SELF:%[0-9]+]] : $DefaultInit): + // CHECK: store {{%[0-9]+}} to {{%[0-9]+}} : $*ActingError + // CHECK-NEXT: hop_to_executor [[SELF]] : $DefaultInit + // CHECK: } // end sil function '$s4test11DefaultInitCACyYacfc' + init() async {} + + // CHECK-LABEL: sil hidden @$s4test11DefaultInitC5nillyACSgSb_tYacfc : $@convention(method) @async (Bool, @owned DefaultInit) -> @owned Optional { + // CHECK: bb0({{%[0-9]+}} : $Bool, [[SELF:%[0-9]+]] : $DefaultInit): + // CHECK: store {{%[0-9]+}} to {{%[0-9]+}} : $*ActingError + // CHECK-NEXT: hop_to_executor [[SELF]] : $DefaultInit + // CHECK: } // end sil function '$s4test11DefaultInitC5nillyACSgSb_tYacfc' + init?(nilly: Bool) async { + guard nilly else { return nil } + } + + init(sync: Void) {} + @MainActor init(mainActorSync: Void) {} +} + +actor MultiVarActor { + var firstVar: Int + var secondVar: Float + + // CHECK-LABEL: sil hidden @$s4test13MultiVarActorC10doNotThrowACSb_tYaKcfc : $@convention(method) @async (Bool, @owned MultiVarActor) -> (@owned MultiVarActor, @error Error) { + // CHECK: bb0({{%[0-9]+}} : $Bool, [[SELF:%[0-9]+]] : $MultiVarActor): + // CHECK: [[REF:%[0-9]+]] = ref_element_addr [[SELF]] : $MultiVarActor, #MultiVarActor.firstVar + // CHECK: [[VAR:%[0-9]+]] = begin_access [modify] [dynamic] [[REF]] : $*Int + // CHECK: store {{%[0-9]+}} to [[VAR]] : $*Int + // CHECK-NEXT: end_access [[VAR]] + // CHECK-NEXT: hop_to_executor %1 : $MultiVarActor + // CHECK: } // end sil function '$s4test13MultiVarActorC10doNotThrowACSb_tYaKcfc' + init(doNotThrow: Bool) async throws { + secondVar = 0 + guard doNotThrow else { throw ActingError.forgotLine } + firstVar = 1 + } + + // CHECK-LABEL: sil hidden @$s4test13MultiVarActorC10noSuccCaseACSb_tYacfc : $@convention(method) @async (Bool, @owned MultiVarActor) -> @owned MultiVarActor { + // CHECK: store {{%[0-9]+}} to [[A1:%[0-9]+]] : $*Int + // CHECK-NEXT: end_access [[A1]] + // CHECK-NEXT: hop_to_executor {{%[0-9]+}} : $MultiVarActor + + // CHECK: store {{%[0-9]+}} to [[A2:%[0-9]+]] : $*Int + // CHECK-NEXT: end_access [[A2]] + // CHECK-NEXT: hop_to_executor {{%[0-9]+}} : $MultiVarActor + // CHECK: } // end sil function '$s4test13MultiVarActorC10noSuccCaseACSb_tYacfc' + init(noSuccCase: Bool) async { + secondVar = 0 + if noSuccCase { + firstVar = 1 + } + firstVar = 2 + } + + // CHECK-LABEL: sil hidden @$s4test13MultiVarActorC10noPredCaseACSb_tYacfc : $@convention(method) @async (Bool, @owned MultiVarActor) -> @owned MultiVarActor { + // CHECK: store {{%[0-9]+}} to [[ACCESS:%[0-9]+]] : $*Int + // CHECK-NEXT: end_access [[ACCESS]] + // CHECK-NEXT: hop_to_executor {{%[0-9]+}} : $MultiVarActor + // CHECK: } // end sil function '$s4test13MultiVarActorC10noPredCaseACSb_tYacfc' + init(noPredCase: Bool) async { + secondVar = 0 + firstVar = 1 + if noPredCase { + print("hello") + } + print("hi") + } + + + // Some cases where we do NOT expect a hop to be emitted because they do + // not fully-initialize `self`. The implicit check-not flag on this test + // ensures that any unexpected hops are caught. + + init?(doesNotFullyInit1: Bool) async { + firstVar = 1 + return nil + } + + init(doesNotFullyInit2: Bool) async { + firstVar = 1 + fatalError("I give up!") + } + + init(doesNotFullyInit3: Bool) async throws { + firstVar = 1 + throw ActingError.forgotLine + } + + init?(doesNotFullyInit4: Bool) async { + firstVar = 1 + neverReturn() + } +} + +@available(SwiftStdlib 5.5, *) +actor TaskMaster { + var task: Task? + + func sayHello() { print("hello") } + + ////// for the initializer + // CHECK-LABEL: @$s4test10TaskMasterCACyYacfc : $@convention(method) @async (@owned TaskMaster) -> @owned TaskMaster { + // CHECK: [[ELM:%[0-9]+]] = ref_element_addr [[SELF:%[0-9]+]] : $TaskMaster, #TaskMaster.task + // CHECK: [[NIL:%[0-9]+]] = enum $Optional>, #Optional.none!enumelt + // CHECK: store [[NIL]] to [[ELM]] : $*Optional> + // CHECK-NEXT: hop_to_executor [[SELF]] : $TaskMaster + // CHECK: } // end sil function '$s4test10TaskMasterCACyYacfc' + init() async { + + ////// for the closure + // CHECK-LABEL: sil private @$s4test10TaskMasterCACyYacfcyyYaYbcfU_ : $@convention(thin) @Sendable @async (@guaranteed TaskMaster) -> () { + // CHECK: hop_to_executor {{%[0-9]+}} : $TaskMaster + // CHECK: } // end sil function '$s4test10TaskMasterCACyYacfcyyYaYbcfU_' + task = Task.detached { await self.sayHello() } + } +} + +actor SomeActor { + var x: Int = 0 + + // CHECK-LABEL: sil hidden @$s4test9SomeActorCACyYacfc : $@convention(method) @async (@owned SomeActor) -> @owned SomeActor { + // CHECK-NOT: begin_access + // CHECK: store {{%[0-9]+}} to {{%[0-9]+}} : $*Int + // CHECK-NEXT: hop_to_executor {{%[0-9]+}} : $SomeActor + // CHECK: } // end sil function '$s4test9SomeActorCACyYacfc' + init() async {} +} diff --git a/test/SILOptimizer/definite_init_closures.sil b/test/SILOptimizer/definite_init_closures.sil index 15edae024a091..18c566d219966 100644 --- a/test/SILOptimizer/definite_init_closures.sil +++ b/test/SILOptimizer/definite_init_closures.sil @@ -45,7 +45,7 @@ bb0(%0 : $Bool): sil private [transparent] [ossa] @implicit_closure_struct : $@convention(thin) (@inout_aliasable SimpleStruct) -> (Bool, @error Error) { bb0(%0 : $*SimpleStruct): - debug_value_addr %0 : $*SimpleStruct, var, name "self", argno 2 + debug_value %0 : $*SimpleStruct, var, name "self", argno 2, expr op_deref %3 = begin_access [read] [static] %0 : $*SimpleStruct %4 = struct_element_addr %3 : $*SimpleStruct, #SimpleStruct.x %5 = load [trivial] %4 : $*Bool diff --git a/test/SILOptimizer/definite_init_closures.swift b/test/SILOptimizer/definite_init_closures.swift index 71511aee71bc0..8e7896d3c930e 100644 --- a/test/SILOptimizer/definite_init_closures.swift +++ b/test/SILOptimizer/definite_init_closures.swift @@ -69,4 +69,15 @@ struct Generic { } } +class Base { } + +class Derived : Base { + var x: Bool + var y: Bool + + init(_: Int) { + x = false + y = true || x + } +} diff --git a/test/SILOptimizer/definite_init_diagnostics.swift b/test/SILOptimizer/definite_init_diagnostics.swift index d1681ea2bb32b..8aa2ee1a9e33c 100644 --- a/test/SILOptimizer/definite_init_diagnostics.swift +++ b/test/SILOptimizer/definite_init_diagnostics.swift @@ -106,7 +106,7 @@ func test2() { weak var w1 : SomeClass? _ = w1 // ok: default-initialized - // expected-warning@+4 {{weak reference will always be nil because the referenced object is deallocated here}} + // Note: with -enable-copy-propagation, we also expect: {{weak reference will always be nil because the referenced object is deallocated here}} // expected-warning@+3 {{instance will be immediately deallocated because variable 'w2' is 'weak'}} // expected-note@+2 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@+1 {{'w2' declared here}} @@ -1336,7 +1336,7 @@ func testDontDiagnoseUnownedImmediateDeallocationThroughStrong() { weak var c1: SomeClass? do { let tmp = SomeClass() - c1 = tmp // expected-warning {{weak reference will always be nil because the referenced object is deallocated here}} + c1 = tmp // Note: with -enable-copy-propagation, we also expect: {{weak reference will always be nil because the referenced object is deallocated here}} } unowned let c2: SomeClass @@ -1347,7 +1347,7 @@ func testDontDiagnoseUnownedImmediateDeallocationThroughStrong() { weak var c3: SomeClass? let c3Tmp = SomeClass() - c3 = c3Tmp // expected-warning {{weak reference will always be nil because the referenced object is deallocated here}} + c3 = c3Tmp // Note: with -enable-copy-propagation, we also expect: {{weak reference will always be nil because the referenced object is deallocated here}} unowned let c4: SomeClass let c4Tmp = SomeClass() diff --git a/test/SILOptimizer/destroy_hoisting.sil b/test/SILOptimizer/destroy_hoisting.sil index 5ce36b0544465..212586871c174 100644 --- a/test/SILOptimizer/destroy_hoisting.sil +++ b/test/SILOptimizer/destroy_hoisting.sil @@ -301,7 +301,7 @@ bb5: // CHECK: } // end sil function 'test_debug_value' sil [ossa] @test_debug_value : $@convention(method) (@inout S2, @owned X, @owned S, @inout E) -> () { bb0(%0 : $*S2, %1 : @owned $X, %2 : @owned $S, %3 : $*E): - debug_value_addr %0 : $*S2, var, name "self", argno 1 + debug_value %0 : $*S2, var, name "self", argno 1, expr op_deref br bb1 bb1: @@ -348,3 +348,104 @@ bb0(%0 : $*X): return %res : $() } +// CHECK-LABEL: sil [ossa] @test_switch_enum_addr : +// CHECK-NOT: destroy_addr +// CHECK: switch_enum_addr +// CHECK: } // end sil function 'test_switch_enum_addr' +sil [ossa] @test_switch_enum_addr : $@convention(thin) (@in TwoCases) -> () { +bb0(%0 : $*TwoCases): + %2 = alloc_stack $TwoCases + copy_addr %0 to [initialization] %2 : $*TwoCases + switch_enum_addr %2 : $*TwoCases, case #TwoCases.A!enumelt: bb1, case #TwoCases.B!enumelt: bb2 + +bb1: + destroy_addr %2 : $*TwoCases + dealloc_stack %2 : $*TwoCases + br bb3 + +bb2: + destroy_addr %2 : $*TwoCases + dealloc_stack %2 : $*TwoCases + br bb3 + +bb3: + destroy_addr %0 : $*TwoCases + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @test_load_borrow1 : +// CHECK-NOT: destroy_addr +// CHECK: load_borrow +// CHECK-LABEL: } // end sil function 'test_load_borrow1' +sil [ossa] @test_load_borrow1 : $@convention(thin) (@in TwoCases) -> () { +bb0(%0 : $*TwoCases): + %2 = alloc_stack $TwoCases + copy_addr %0 to [initialization] %2 : $*TwoCases + %ld = load_borrow %2 : $*TwoCases + br bb1(%ld : $TwoCases) + +bb1(%newld : @guaranteed $TwoCases): + end_borrow %newld : $TwoCases + br bb2 + +bb2: + destroy_addr %2 : $*TwoCases + dealloc_stack %2 : $*TwoCases + br bb3 + +bb3: + destroy_addr %0 : $*TwoCases + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @test_load_borrow2 : +// CHECK-NOT: destroy_addr +// CHECK: load_borrow +// CHECK-LABEL: } // end sil function 'test_load_borrow2' +sil [ossa] @test_load_borrow2 : $@convention(thin) (@in TwoCases) -> () { +bb0(%0 : $*TwoCases): + %2 = alloc_stack $TwoCases + copy_addr %0 to [initialization] %2 : $*TwoCases + %ld = load_borrow %2 : $*TwoCases + cond_br undef, bb1, bb2 + +bb1: + end_borrow %ld : $TwoCases + br bb3 + +bb2: + br bb2a(%ld : $TwoCases) + +bb2a(%newld : @guaranteed $TwoCases): + end_borrow %newld : $TwoCases + br bb3 + +bb3: + destroy_addr %2 : $*TwoCases + dealloc_stack %2 : $*TwoCases + br bb3 + +bb4: + destroy_addr %0 : $*TwoCases + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @test_begin_access : +// CHECK-NOT: destroy_addr +// CHECK: begin_access +// CHECK-LABEL: } // end sil function 'test_begin_access' +sil [ossa] @test_begin_access : $@convention(thin) (@in X) -> () { +bb0(%0 : $*X): + %a = begin_access [read] [dynamic] %0 : $*X + br bb1 + +bb1: + end_access %a : $*X + destroy_addr %0 : $*X + %r = tuple () + return %r : $() +} + diff --git a/test/SILOptimizer/di_property_wrappers.swift b/test/SILOptimizer/di_property_wrappers.swift index 82156bc208ca6..4d501be66cc9a 100644 --- a/test/SILOptimizer/di_property_wrappers.swift +++ b/test/SILOptimizer/di_property_wrappers.swift @@ -92,6 +92,11 @@ final class IntClass { } wrapped = 27 } + + convenience init(withConvenienceInit x: Int) { + self.init() + self.wrapped = x + } } struct RefStruct { @@ -210,6 +215,13 @@ func testIntClass() { let t5 = IntClass(dynamic: true) // CHECK-NEXT: 27 print(t5.wrapped) + + // CHECK-NEXT: .. init 42 + // CHECK-NEXT: .. set 27 + // CHECK-NEXT: .. set 123 + let t6 = IntClass(withConvenienceInit: 123) + // CHECK-NEXT: 123 + print(t6.wrapped) } func testRefStruct() { diff --git a/test/SILOptimizer/diagnose_lifetime_issues.sil b/test/SILOptimizer/diagnose_lifetime_issues.sil new file mode 100644 index 0000000000000..8523b9d78353f --- /dev/null +++ b/test/SILOptimizer/diagnose_lifetime_issues.sil @@ -0,0 +1,121 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -diagnose-lifetime-issues -o /dev/null -verify + +import Builtin +import Swift + +enum FakeOptional { +case none +case some(T) +} + +class Delegate { + func foo() { } +} + +class MyDelegate : Delegate {} + +final class Container { + weak var delegate: Delegate? + var strongRef: Delegate + + func callDelegate() + + func strongDelegate(d: Delegate) +} + +class WeakCycle { + weak var c: WeakCycle? +} + +sil [ossa] @$s24diagnose_lifetime_issues10MyDelegateCACycfC : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate +sil [ossa] @$s24diagnose_lifetime_issues14strongDelegate1dyAA0D0C_tF : $@convention(method) (@guaranteed Delegate, @guaranteed Container) -> () + +// Test a warning for a reference that is copied, cast, and assigned +// to an enum before it is assigned to a weak reference. +sil [ossa] @testCastWarn : $@convention(thin) (@guaranteed Container) -> () { +bb0(%0 : @guaranteed $Container): + debug_value %0 : $Container, let, name "container", argno 1 + %2 = metatype $@thick MyDelegate.Type + // function_ref MyDelegate.__allocating_init() + %3 = function_ref @$s24diagnose_lifetime_issues10MyDelegateCACycfC : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate + + // This is the owned allocation. + %4 = apply %3(%2) : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate + %11 = copy_value %4 : $MyDelegate + + // This upcast+enum is not an escape. + %12 = upcast %11 : $MyDelegate to $Delegate + %13 = enum $Optional, #Optional.some!enumelt, %12 : $Delegate + %14 = ref_element_addr %0 : $Container, #Container.delegate + %15 = begin_access [modify] [dynamic] %14 : $*@sil_weak Optional + + // This is the weak assignment. + store_weak %13 to %15 : $*@sil_weak Optional // expected-warning {{weak reference will always be nil because the referenced object is deallocated here}} + destroy_value %13 : $Optional + end_access %15 : $*@sil_weak Optional + destroy_value %4 : $MyDelegate + %20 = tuple () + return %20 : $() +} + +// Test that a reference that escapes within a borrow scope has no warning. +sil hidden [ossa] @testBorrowNoWarn : $@convention(thin) (@guaranteed Container) -> () { +// %0 "container" +bb0(%0 : @guaranteed $Container): + debug_value %0 : $Container, let, name "container", argno 1 + %2 = metatype $@thick MyDelegate.Type + // function_ref MyDelegate.__allocating_init() + %3 = function_ref @$s24diagnose_lifetime_issues10MyDelegateCACycfC : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate + + // This is the owned allocation. + %4 = apply %3(%2) : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate + debug_value %4 : $MyDelegate, let, name "delegate" + %6 = begin_borrow %4 : $MyDelegate + %7 = upcast %6 : $MyDelegate to $Delegate + // function_ref Container.strongDelegate(d:) + %8 = function_ref @$s24diagnose_lifetime_issues14strongDelegate1dyAA0D0C_tF : $@convention(method) (@guaranteed Delegate, @guaranteed Container) -> () + + // This apply is an escape. + %9 = apply %8(%7, %0) : $@convention(method) (@guaranteed Delegate, @guaranteed Container) -> () + end_borrow %6 : $MyDelegate + %11 = copy_value %4 : $MyDelegate + %12 = upcast %11 : $MyDelegate to $Delegate + %13 = enum $Optional, #Optional.some!enumelt, %12 : $Delegate + %14 = ref_element_addr %0 : $Container, #Container.delegate + %15 = begin_access [modify] [dynamic] %14 : $*@sil_weak Optional + + // This is the weak assignment. + store_weak %13 to %15 : $*@sil_weak Optional + destroy_value %13 : $Optional + end_access %15 : $*@sil_weak Optional + destroy_value %4 : $MyDelegate + %20 = tuple () + return %20 : $() +} + +// Helper for testReturnsAfterStore +sil hidden [ossa] @testStoresWeakly : $@convention(method) (@owned WeakCycle) -> @owned WeakCycle { +bb0(%0 : @owned $WeakCycle): + %18 = begin_borrow %0 : $WeakCycle + %19 = copy_value %0 : $WeakCycle + %20 = enum $Optional, #Optional.some!enumelt, %19 : $WeakCycle + %21 = ref_element_addr %18 : $WeakCycle, #WeakCycle.c + %22 = begin_access [modify] [dynamic] %21 : $*@sil_weak Optional + store_weak %20 to %22 : $*@sil_weak Optional + destroy_value %20 : $Optional + end_access %22 : $*@sil_weak Optional + end_borrow %18 : $WeakCycle + %27 = copy_value %0 : $WeakCycle + destroy_value %0 : $WeakCycle + return %27 : $WeakCycle +} + +// Test no warning for a value returned after a weak store. +sil hidden [exact_self_class] [ossa] @testReturnsAfterStore : $@convention(method) (@thick WeakCycle.Type) -> @owned WeakCycle { +bb0(%0 : $@thick WeakCycle.Type): + %1 = alloc_ref $WeakCycle + + %2 = function_ref @testStoresWeakly : $@convention(method) (@owned WeakCycle) -> @owned WeakCycle + %3 = apply %2(%1) : $@convention(method) (@owned WeakCycle) -> @owned WeakCycle + return %3 : $WeakCycle +} diff --git a/test/SILOptimizer/diagnose_lifetime_issues.swift b/test/SILOptimizer/diagnose_lifetime_issues.swift index c0b1e352c6a34..77f0cef8efce0 100644 --- a/test/SILOptimizer/diagnose_lifetime_issues.swift +++ b/test/SILOptimizer/diagnose_lifetime_issues.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -enable-copy-propagation %s -o /dev/null -verify class Delegate { func foo() { } diff --git a/test/SILOptimizer/diagnose_lifetime_issues_objc.swift b/test/SILOptimizer/diagnose_lifetime_issues_objc.swift index 25adfaa0d802d..3b456a3730cc9 100644 --- a/test/SILOptimizer/diagnose_lifetime_issues_objc.swift +++ b/test/SILOptimizer/diagnose_lifetime_issues_objc.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil %s -enable-copy-propagation -o /dev/null -verify // REQUIRES: objc_interop import Foundation diff --git a/test/SILOptimizer/diagnose_unreachable.swift b/test/SILOptimizer/diagnose_unreachable.swift index a9ce68c7f7d4c..9d42cb105e375 100644 --- a/test/SILOptimizer/diagnose_unreachable.swift +++ b/test/SILOptimizer/diagnose_unreachable.swift @@ -470,3 +470,48 @@ extension Collection { } } +// rdar://80415811 +// Incorrect unreachable code diagnostic for keypath to empty Enum caused by +// the generated keypath getter function having an uninhabited return type. +// The getter function was previously tied to the source location of the +// corresponding keypath expression, which caused it to be diagnosed as +// user code. The getter and setter functions now have an autogenerated +// source location so we should not expect an unreachable code warning until +// the keypath expression is actually used. +struct StructWithNeverProp { + var property: Never { + fatalError() + } +} + +func keypathToEmptyEnum() -> Never { + // Check that the generated keypath getter function for this property + // does not trigger an unreachable code warning here. + let kp = \StructWithNeverProp.property // no warning + let s = StructWithNeverProp() + // Emit a diagnostic here becase the keypath is actually used. + let prop = s[keyPath: kp] + // expected-warning@-1 {{will never be executed}} \ + // expected-note {{a call to a never-returning function}} \ + // expected-warning {{constant 'prop' inferred to have type 'Never', which is an enum with no cases}} \ + // expected-note {{add an explicit type annotation to silence this warning}} + return prop +} + +struct OuterStruct { + public let innerEnum: InnerEnum + public enum InnerEnum { } +} + +@dynamicMemberLookup +public enum DynamicLookupEnum { + subscript(dynamicMember keyPath: KeyPath) -> T { + fatalError() + } +} + +func keypathWithDynamicLookup() { + // Check that we still don't diagnose the keypath getter as unreachable + // when used in conjuction with a dynamicMemberLookup enum. + let _ = \DynamicLookupEnum.innerEnum // no warning +} diff --git a/test/SILOptimizer/existential_transform.swift b/test/SILOptimizer/existential_transform.swift index 51ae264f77b83..c84496282a7d3 100644 --- a/test/SILOptimizer/existential_transform.swift +++ b/test/SILOptimizer/existential_transform.swift @@ -196,7 +196,7 @@ internal class KK : PP { // CHECK-LABEL: sil hidden [noinline] @$s21existential_transform13wrap_inout_cp1aSiAA2PP_pz_tF : $@convention(thin) (@inout PP) -> Int { // CHECK: bb0(%0 : $*PP): -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: load // CHECK: [[O1:%.*]] = open_existential_ref // CHECK: witness_method $@opened("{{.*}}") PP, #PP.foo : (Self) -> () -> Int, %3 : $@opened("{{.*}}PP : $@convention(witness_method: PP) <τ_0_0 where τ_0_0 : PP> (@guaranteed τ_0_0) -> Int @@ -350,9 +350,9 @@ struct MyStruct : Foo { // CHECK: alloc_stack // CHECK: init_existential_addr // CHECK: copy_addr -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: open_existential_addr -// CHECK: witness_method +// CHECK: witness_method // CHECK: apply // CHECK: dealloc_stack // CHECK: return @@ -382,7 +382,7 @@ class RC: RP { // CHECK: copy_addr // CHECK: debug_value // CHECK: debug_value -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: struct_extract // CHECK: struct_extract // CHECK: integer_literal diff --git a/test/SILOptimizer/existential_transform_extras.sil b/test/SILOptimizer/existential_transform_extras.sil index 2309e9aafec1d..68b8d77af661a 100644 --- a/test/SILOptimizer/existential_transform_extras.sil +++ b/test/SILOptimizer/existential_transform_extras.sil @@ -60,7 +60,7 @@ bb0: // CHECK-LABEL: sil public_external [serialized] @$s7dealloc20wrap_foo_ncp_another1aSiAA1P_pz_tF : $@convention(thin) (@inout P) -> Int32 { // CHECK: bb0(%0 : $*P): -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: alloc_stack // CHECK: copy_addr // CHECK: open_existential_addr @@ -72,7 +72,7 @@ bb0: // CHECK-LABEL: } // end sil function '$s7dealloc20wrap_foo_ncp_another1aSiAA1P_pz_tF' sil public_external [serialized] @$s7dealloc20wrap_foo_ncp_another1aSiAA1P_pz_tF : $@convention(thin) (@inout P) -> Int32 { bb0(%0 : $*P): - debug_value_addr %0 : $*P, var, name "a", argno 1 + debug_value %0 : $*P, var, name "a", argno 1, expr op_deref %2 = alloc_stack $P copy_addr %0 to [initialization] %2 : $*P %4 = open_existential_addr immutable_access %2 : $*P to $*@opened("EE9F89E4-ECF4-11E8-8DDF-D0817AD4059B") P @@ -94,7 +94,7 @@ sil_global hidden [let] @$global_var : $P // CHECK-LABEL: sil hidden [noinline] @$helper : $@convention(thin) (@in P) -> Int32 { // CHECK: bb0(%0 : $*P): -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: alloc_stack // CHECK: copy_addr // CHECK: destroy_addr @@ -106,7 +106,7 @@ sil_global hidden [let] @$global_var : $P // CHECK-LABEL: } // end sil function '$helper' sil hidden [noinline] @$helper : $@convention(thin) (@in P) -> Int32 { bb0(%0 : $*P): - debug_value_addr %0 : $*P, var, name "a", argno 1 + debug_value %0 : $*P, var, name "a", argno 1, expr op_deref %4 = alloc_stack $P copy_addr %0 to [initialization] %4 : $*P destroy_addr %0 : $*P @@ -169,8 +169,8 @@ bb3: // CHECK-LABEL: } // end sil function '$s7dealloc12wrap_foo_ncp1a1bSiAA1P_pz_AaE_pztFTf4ee_n' sil hidden [noinline] @$s7dealloc12wrap_foo_ncp1a1bSiAA1P_pz_AaE_pztF : $@convention(thin) (@in P, @in P) -> Int32 { bb0(%0 : $*P, %1 : $*P): - debug_value_addr %0 : $*P, var, name "a", argno 1 - debug_value_addr %1 : $*P, var, name "b", argno 2 + debug_value %0 : $*P, var, name "a", argno 1, expr op_deref + debug_value %1 : $*P, var, name "b", argno 2, expr op_deref %4 = alloc_stack $P copy_addr %0 to [initialization] %4 : $*P destroy_addr %0 : $*P diff --git a/test/SILOptimizer/existential_transform_extras_ossa.sil b/test/SILOptimizer/existential_transform_extras_ossa.sil index b1e0d03230119..d773ffa0c90f4 100644 --- a/test/SILOptimizer/existential_transform_extras_ossa.sil +++ b/test/SILOptimizer/existential_transform_extras_ossa.sil @@ -80,7 +80,7 @@ bb0: // CHECK-LABEL: sil public_external [serialized] [ossa] @$s7dealloc20wrap_foo_ncp_another1aSiAA1P_pz_tF : $@convention(thin) (@inout P) -> Int32 { // CHECK: bb0(%0 : $*P): -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: alloc_stack // CHECK: copy_addr // CHECK: open_existential_addr @@ -92,7 +92,7 @@ bb0: // CHECK-LABEL: } // end sil function '$s7dealloc20wrap_foo_ncp_another1aSiAA1P_pz_tF' sil public_external [ossa] [serialized] @$s7dealloc20wrap_foo_ncp_another1aSiAA1P_pz_tF : $@convention(thin) (@inout P) -> Int32 { bb0(%0 : $*P): - debug_value_addr %0 : $*P, var, name "a", argno 1 + debug_value %0 : $*P, var, name "a", argno 1, expr op_deref %2 = alloc_stack $P copy_addr %0 to [initialization] %2 : $*P %4 = open_existential_addr immutable_access %2 : $*P to $*@opened("EE9F89E4-ECF4-11E8-8DDF-D0817AD4059B") P @@ -114,7 +114,7 @@ sil_global hidden [let] @$global_var : $P // CHECK-LABEL: sil hidden [noinline] [ossa] @$helper : $@convention(thin) (@in P) -> Int32 { // CHECK: bb0(%0 : $*P): -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: alloc_stack // CHECK: copy_addr // CHECK: destroy_addr @@ -126,7 +126,7 @@ sil_global hidden [let] @$global_var : $P // CHECK-LABEL: } // end sil function '$helper' sil hidden [ossa] [noinline] @$helper : $@convention(thin) (@in P) -> Int32 { bb0(%0 : $*P): - debug_value_addr %0 : $*P, var, name "a", argno 1 + debug_value %0 : $*P, var, name "a", argno 1, expr op_deref %4 = alloc_stack $P copy_addr %0 to [initialization] %4 : $*P destroy_addr %0 : $*P @@ -190,8 +190,8 @@ bb3: // CHECK-LABEL: } // end sil function '$s7dealloc12wrap_foo_ncp1a1bSiAA1P_pz_AaE_pztFTf4ee_n' sil hidden [ossa] [noinline] @$s7dealloc12wrap_foo_ncp1a1bSiAA1P_pz_AaE_pztF : $@convention(thin) (@in P, @in P) -> Int32 { bb0(%0 : $*P, %1 : $*P): - debug_value_addr %0 : $*P, var, name "a", argno 1 - debug_value_addr %1 : $*P, var, name "b", argno 2 + debug_value %0 : $*P, var, name "a", argno 1, expr op_deref + debug_value %1 : $*P, var, name "b", argno 2, expr op_deref %4 = alloc_stack $P copy_addr %0 to [initialization] %4 : $*P destroy_addr %0 : $*P diff --git a/test/SILOptimizer/existential_transform_soletype.swift b/test/SILOptimizer/existential_transform_soletype.swift index 37af5f774754f..a477d58285163 100644 --- a/test/SILOptimizer/existential_transform_soletype.swift +++ b/test/SILOptimizer/existential_transform_soletype.swift @@ -19,7 +19,7 @@ internal class SCC: SPP { // CHECK-LABEL: sil hidden [noinline] @$s30existential_transform_soletype4opt11bs5Int32VAA3SPP_p_tF : $@convention(thin) (@in_guaranteed SPP) -> Int32 { // CHECK: bb0(%0 : $*SPP): -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: function_ref @$s30existential_transform_soletype4opt21bs5Int32VAA3SPP_p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SPP> (@in_guaranteed τ_0_0) -> Int32 // user: %4 // CHECK: open_existential_addr // CHECK: apply @@ -31,9 +31,9 @@ internal class SCC: SPP { // CHECK: alloc_stack // CHECK: init_existential_addr // CHECK: copy_addr -// CHECK: debug_value_addr +// CHECK: debug_value {{.*}} expr op_deref // CHECK: open_existential_addr -// CHECK: witness_method +// CHECK: witness_method // CHECK: apply // CHECK: dealloc_stack // CHECK: return diff --git a/test/SILOptimizer/function_order.sil b/test/SILOptimizer/function_order.sil index b441f8c23ba15..3704c960de06d 100644 --- a/test/SILOptimizer/function_order.sil +++ b/test/SILOptimizer/function_order.sil @@ -365,7 +365,7 @@ bb0(%0 : $*private_proto_private_class): sil private @call_through_private_proto_1 : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %2 = witness_method $T, #private_proto_1.theMethod : $@convention(witness_method: private_proto_1) <τ_0_0 where τ_0_0 : private_proto_1> (@in_guaranteed τ_0_0) -> () %3 = apply %2(%0) : $@convention(witness_method: private_proto_1) <τ_0_0 where τ_0_0 : private_proto_1> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*T @@ -393,7 +393,7 @@ bb0(%0 : $*private_proto_internal_class): sil private @call_through_private_proto_2 : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %2 = witness_method $T, #private_proto_2.theMethod : $@convention(witness_method: private_proto_2) <τ_0_0 where τ_0_0 : private_proto_2> (@in_guaranteed τ_0_0) -> () %3 = apply %2(%0) : $@convention(witness_method: private_proto_2) <τ_0_0 where τ_0_0 : private_proto_2> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*T @@ -423,7 +423,7 @@ bb0(%0 : $*private_proto_public_class): sil private @call_through_private_proto_3 : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %2 = witness_method $T, #private_proto_3.theMethod : $@convention(witness_method: private_proto_3) <τ_0_0 where τ_0_0 : private_proto_3> (@in_guaranteed τ_0_0) -> () %3 = apply %2(%0) : $@convention(witness_method: private_proto_3) <τ_0_0 where τ_0_0 : private_proto_3> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*T @@ -451,7 +451,7 @@ bb0(%0 : $*private_proto_public_class_private_method): sil private @call_through_private_proto_4 : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref %2 = witness_method $T, #private_proto_4.theMethod : $@convention(witness_method: private_proto_4) <τ_0_0 where τ_0_0 : private_proto_4> (@in_guaranteed τ_0_0) -> () %3 = apply %2(%0) : $@convention(witness_method: private_proto_4) <τ_0_0 where τ_0_0 : private_proto_4> (@in_guaranteed τ_0_0) -> () destroy_addr %0 : $*T diff --git a/test/SILOptimizer/immortal-arc-elimination.sil b/test/SILOptimizer/immortal-arc-elimination.sil new file mode 100644 index 0000000000000..63578eb6e2dba --- /dev/null +++ b/test/SILOptimizer/immortal-arc-elimination.sil @@ -0,0 +1,52 @@ +// RUN: %sil-opt -enable-sil-verify-all %s -sil-combine | %FileCheck %s +// Note: Intentionally not using %target-sil-opt, because we need at least a +// swift 5.1 deployment target. + +// REQUIRES: libswift + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +sil_global public_external @_swiftEmptyArrayStorage : $_SwiftEmptyArrayStorage + + +// CHECK-LABEL: sil @testEmptyArraySingleton +// CHECK: global_addr +// CHECK-NOT: retain +// CHECK: } // end sil function 'testEmptyArraySingleton' +sil @testEmptyArraySingleton : $@convention(thin) () -> @owned Builtin.BridgeObject { +bb0: + %0 = global_addr @_swiftEmptyArrayStorage : $*_SwiftEmptyArrayStorage + %1 = address_to_pointer %0 : $*_SwiftEmptyArrayStorage to $Builtin.RawPointer + %2 = raw_pointer_to_ref %1 : $Builtin.RawPointer to $__EmptyArrayStorage + %3 = unchecked_ref_cast %2 : $__EmptyArrayStorage to $Builtin.BridgeObject + strong_retain %3 : $Builtin.BridgeObject + return %3 : $Builtin.BridgeObject +} + + +sil_global private @staticArray : $_ContiguousArrayStorage = { + %0 = integer_literal $Builtin.Int64, 0 + %1 = struct $UInt (%0 : $Builtin.Int64) + %2 = struct $Int (%0 : $Builtin.Int64) + %3 = struct $_SwiftArrayBodyStorage (%2 : $Int, %1 : $UInt) + %4 = struct $_ArrayBody (%3 : $_SwiftArrayBodyStorage) + %initval = object $_ContiguousArrayStorage (%4 : $_ArrayBody) +} + +// CHECK-LABEL: sil @testGlobalValue +// CHECK: global_value +// CHECK-NOT: retain +// CHECK: } // end sil function 'testGlobalValue' +sil @testGlobalValue : $@convention(thin) () -> @owned Builtin.BridgeObject { +bb0: + %0 = global_value @staticArray : $_ContiguousArrayStorage + %1 = unchecked_ref_cast %0 : $_ContiguousArrayStorage to $Builtin.BridgeObject + strong_retain %1 : $Builtin.BridgeObject + return %1 : $Builtin.BridgeObject +} + + diff --git a/test/SILOptimizer/immortal-arc-elimination.swift b/test/SILOptimizer/immortal-arc-elimination.swift new file mode 100644 index 0000000000000..0e0c5b961a017 --- /dev/null +++ b/test/SILOptimizer/immortal-arc-elimination.swift @@ -0,0 +1,59 @@ +// RUN: %target-swift-frontend -primary-file %s -O -sil-verify-all -Xllvm -sil-disable-pass=function-signature-opts -module-name=test -O -target %target-cpu-apple-macos10.14 -emit-sil | %FileCheck --check-prefix=CHECK-SWIFT4x %s +// RUN: %target-swift-frontend -primary-file %s -O -sil-verify-all -Xllvm -sil-disable-pass=function-signature-opts -module-name=test -O -target %target-cpu-apple-macos10.15 -emit-sil | %FileCheck --check-prefix=CHECK-SWIFT50 %s + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -Xllvm -sil-disable-pass=function-signature-opts -module-name=test %s -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT + +// REQUIRES: OS=macosx +// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib +// REQUIRES: libswift + +// Check that the optimizer can remove "unbalanced" retains for immortal objects. +// But only with a Swift 5.1 runtime (which supports immortal objects). + +// CHECK-SWIFT4x-LABEL: sil hidden [noinline] @$s4test10emptyArraySaySiGyF +// CHECK-SWIFT4x: global_addr +// CHECK-SWIFT4x: retain +// CHECK-SWIFT4x: } // end sil function '$s4test10emptyArraySaySiGyF' + +// CHECK-SWIFT50-LABEL: sil hidden [noinline] @$s4test10emptyArraySaySiGyF +// CHECK-SWIFT50: global_addr +// CHECK-SWIFT50-NOT: retain +// CHECK-SWIFT50: } // end sil function '$s4test10emptyArraySaySiGyF' +@inline(never) +func emptyArray() -> [Int] { + let x = [Int]() + return x +} + +// CHECK-SWIFT4x-LABEL: sil hidden [noinline] @$s4test13constantArraySaySiGyF +// CHECK-SWIFT4x: global_value +// CHECK-SWIFT4x: retain +// CHECK-SWIFT4x: } // end sil function '$s4test13constantArraySaySiGyF' + +// CHECK-SWIFT50-LABEL: sil hidden [noinline] @$s4test13constantArraySaySiGyF +// CHECK-SWIFT50: global_value +// CHECK-SWIFT50-NOT: retain +// CHECK-SWIFT50: } // end sil function '$s4test13constantArraySaySiGyF' +@inline(never) +func constantArray() -> [Int] { + return [1, 2, 3] +} + +func testit() { + // CHECK-OUTPUT: [] + // CHECK-OUTPUT: [1, 2, 3] + // CHECK-OUTPUT: [] + // CHECK-OUTPUT: [1, 2, 3] + // CHECK-OUTPUT: [] + // CHECK-OUTPUT: [1, 2, 3] + for _ in 0..<3 { + print(emptyArray()) + print(constantArray()) + } +} + +testit() + + diff --git a/test/SILOptimizer/infinite_recursion.swift b/test/SILOptimizer/infinite_recursion.swift index da9d10b77765d..ebe3aec58f1d9 100644 --- a/test/SILOptimizer/infinite_recursion.swift +++ b/test/SILOptimizer/infinite_recursion.swift @@ -103,6 +103,18 @@ func multipleArgsNoWarning(_ x : Int, _ y : Int) { } } +struct A {} +struct B {} + +func deadEndBlockInElseBranch(_ x : Int) { + if x != 0 { + deadEndBlockInElseBranch(x - 1) // no warning + } else { + _ = unsafeBitCast(A(), to: B.self) + } +} + + struct Str { var x = 27 diff --git a/test/SILOptimizer/invalid_escaping_captures.swift b/test/SILOptimizer/invalid_escaping_captures.swift index 8cbc9b467203c..f22158c46c6a2 100644 --- a/test/SILOptimizer/invalid_escaping_captures.swift +++ b/test/SILOptimizer/invalid_escaping_captures.swift @@ -255,4 +255,23 @@ func badNoEscapeCaptureThroughVar(_ fn: () -> ()) { takesEscaping { // expected-error {{escaping closure captures non-escaping value}} myFunc() } -} \ No newline at end of file +} + +@inline(never) +func takeNoEscapeReturnGetter(f: ()->()->Int64) -> ()->Int64 { return f() } + +// Test that invalid escaping capture diagnostics are run on nested +// closures before exclusivity diagnostics are run. Exclusivity +// diagnostics need to inspect all referenced closures that capture +// inouts. Also ensure that exclusivity diagnostics does not crash +// when verifying that a closure that captures inout does not escape +// as long as a previous diagnostic error is present. +struct TestInoutEscapeInClosure { + var someValue: Int64 = 0 + mutating func testInoutEscapeInClosure() -> () -> Int64 { + return takeNoEscapeReturnGetter { + return { return someValue } // expected-error {{escaping closure captures mutating 'self' parameter}} + // expected-note@-1 {{captured here}} + } + } +} diff --git a/test/SILOptimizer/licm.sil b/test/SILOptimizer/licm.sil index 378139f604990..ad5b7c2f1d594 100644 --- a/test/SILOptimizer/licm.sil +++ b/test/SILOptimizer/licm.sil @@ -1318,3 +1318,82 @@ bb3: %outer = tuple (%extract0 : $Int64, %inner: $(Int64, Int64)) return %outer : $(Int64, (Int64, Int64)) } + +class C {} + +// This won't be hoisted because we can't find a base to check if it is invariant +// CHECK-LABEL: sil @testLoopInvariantStoreNoBase1 : +// CHECK: bb6: +// CHECK-NOT: store +// CHECK-LABEL: } // end sil function 'testLoopInvariantStoreNoBase1' +sil @testLoopInvariantStoreNoBase1 : $@convention(thin) (Builtin.BridgeObject, Double) -> () { +bb0(%0 : $Builtin.BridgeObject, %1 : $Double): + cond_br undef, bb1, bb2 + +bb1: + %2 = unchecked_ref_cast %0 : $Builtin.BridgeObject to $C + %3 = ref_tail_addr [immutable] %2 : $C, $Double + %4 = address_to_pointer %3 : $*Double to $Builtin.RawPointer + br bb3(%4 : $Builtin.RawPointer) + +bb2: + %6 = unchecked_ref_cast %0 : $Builtin.BridgeObject to $C + %7 = ref_tail_addr [immutable] %6 : $C, $Double + %8 = address_to_pointer %7 : $*Double to $Builtin.RawPointer + br bb3(%8 : $Builtin.RawPointer) + +bb3(%9 : $Builtin.RawPointer): + br bb4 + +bb4: + %11 = pointer_to_address %9 : $Builtin.RawPointer to [strict] $*Double + store %1 to %11 : $*Double + cond_br undef, bb5, bb6 + +bb5: + br bb4 + +bb6: + %15 = tuple () + return %15 : $() +} + +// This won't be hoisted because we can't find a base to check if it is invariant +// CHECK-LABEL: sil @testLoopInvariantStoreNoBase2 : +// CHECK: bb6: +// CHECK-NOT: store +// CHECK-LABEL: } // end sil function 'testLoopInvariantStoreNoBase2' +sil @testLoopInvariantStoreNoBase2 : $@convention(thin) (Builtin.BridgeObject, Double) -> () { +bb0(%0 : $Builtin.BridgeObject, %1 : $Double): + cond_br undef, bb1, bb2 + +bb1: + %2 = unchecked_ref_cast %0 : $Builtin.BridgeObject to $C + %3 = ref_tail_addr [immutable] %2 : $C, $Double + %4 = address_to_pointer %3 : $*Double to $Builtin.RawPointer + br bb3(%4 : $Builtin.RawPointer) + +bb2: + %6 = unchecked_ref_cast %0 : $Builtin.BridgeObject to $C + %7 = ref_tail_addr [immutable] %6 : $C, $Double + %8 = address_to_pointer %7 : $*Double to $Builtin.RawPointer + br bb3(%8 : $Builtin.RawPointer) + +bb3(%9 : $Builtin.RawPointer): + br bb4 + +bb4: + %11 = pointer_to_address %9 : $Builtin.RawPointer to [strict] $*Double + %12 = integer_literal $Builtin.Word, 1 + %13 = index_addr %11 : $*Double, %12 : $Builtin.Word + store %1 to %13 : $*Double + cond_br undef, bb5, bb6 + +bb5: + br bb4 + +bb6: + %15 = tuple () + return %15 : $() +} + diff --git a/test/SILOptimizer/licm_exclusivity.sil b/test/SILOptimizer/licm_exclusivity.sil index 39deb412efdb5..f629b810dee30 100644 --- a/test/SILOptimizer/licm_exclusivity.sil +++ b/test/SILOptimizer/licm_exclusivity.sil @@ -29,10 +29,10 @@ sil hidden_external @globalAddressor : $@convention(thin) () -> Builtin.RawPoint // CHECK-LABEL: sil @hoist_access_with_conflict : $@convention(thin) () -> () { // CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X // CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X +// CHECK-NEXT: load // CHECK-NEXT: br bb1 // CHECK: apply -// CHECK: load -// CHECK: cond_br +// CHECK-NEXT: cond_br // CHECK: bb2 // CHECK: end_access [[BEGIN]] // CHECK-LABEL: } // end sil function 'hoist_access_with_conflict' @@ -89,9 +89,9 @@ bb2: // CHECK-LABEL: sil @hoist_access_with_may_release : $@convention(thin) () -> () { // CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X // CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X +// CHECK-NEXT: load // CHECK-NEXT: br bb1 // CHECK: apply -// CHECK: load // CHECK: cond_br // CHECK: bb2 // CHECK: end_access [[BEGIN]] @@ -153,10 +153,10 @@ bb2: // CHECK-LABEL: sil @hoist_access_static : $@convention(thin) () -> () { // CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X // CHECK: [[BEGIN:%.*]] = begin_access [read] [static] [[GLOBAL]] : $*X +// CHECK-NEXT: load // CHECK-NEXT: br bb1 // CHECK: apply -// CHECK: load -// CHECK: cond_br +// CHECK-NEXT: cond_br // CHECK: bb2 // CHECK: end_access [[BEGIN]] // CHECK-LABEL: } // end sil function 'hoist_access_static' diff --git a/test/SILOptimizer/load_borrow_verify.sil b/test/SILOptimizer/load_borrow_verify.sil index 366302f3cfe4b..c54fed86b3346 100644 --- a/test/SILOptimizer/load_borrow_verify.sil +++ b/test/SILOptimizer/load_borrow_verify.sil @@ -134,3 +134,22 @@ bb0(%0 : @owned $K): %r = tuple () return %r : $() } + +sil [ossa] @test_end_lifetime : $@convention(thin) (@owned K) -> () { +bb0(%0 : @owned $K): + %1 = begin_borrow %0 : $K + %2 = ref_element_addr %1 : $K, #K.x + %3 = begin_access [read] [dynamic] %2 : $*B + %4 = load_borrow %3 : $*B + end_borrow %4 : $B + end_access %3 : $*B + %cast = unchecked_ref_cast %1 : $K to $Builtin.NativeObject + %conv1 = unchecked_ownership_conversion %cast : $Builtin.NativeObject, @guaranteed to @owned + end_borrow %1 : $K + end_lifetime %0 : $K + %conv2 = unchecked_ref_cast %conv1 : $Builtin.NativeObject to $K + dealloc_ref %conv2 : $K + %r = tuple () + return %r : $() +} + diff --git a/test/SILOptimizer/lower_hop_to_actor.sil b/test/SILOptimizer/lower_hop_to_actor.sil index 00b43e3febd06..445461070c253 100644 --- a/test/SILOptimizer/lower_hop_to_actor.sil +++ b/test/SILOptimizer/lower_hop_to_actor.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -lower-hop-to-actor -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -lower-hop-to-actor | %FileCheck %s // REQUIRES: concurrency diff --git a/test/SILOptimizer/mandatory_inlining.sil b/test/SILOptimizer/mandatory_inlining.sil index 98ed930c6ceea..d65de2b346300 100644 --- a/test/SILOptimizer/mandatory_inlining.sil +++ b/test/SILOptimizer/mandatory_inlining.sil @@ -770,8 +770,8 @@ bb0(%0 : $C): // CHECK-LABEL: sil [transparent] @inner : $@convention(thin) (@in T1, @in T2) -> @out T2 { sil [transparent] @inner : $@convention(thin) (@in T1, @in T2) -> @out T2 { bb0(%0 : $*T2, %1 : $*T1, %2 : $*T2): - debug_value_addr %1 : $*T1 - debug_value_addr %2 : $*T2 + debug_value %1 : $*T1, expr op_deref + debug_value %2 : $*T2, expr op_deref copy_addr [take] %2 to [initialization] %0 : $*T2 destroy_addr %1 : $*T1 %7 = tuple () @@ -784,7 +784,7 @@ bb0(%0 : $*T2, %1 : $*T1, %2 : $*T2): sil [transparent] @middle : $@convention(thin) (Int, @in T) -> @out T { bb0(%0 : $*T, %1 : $Int, %2 : $*T): debug_value %1 : $Int - debug_value_addr %2 : $*T + debug_value %2 : $*T, expr op_deref // CHECK-NOT: [[REF:%[a-zA-Z0-9]+]] = function_ref @inner %5 = function_ref @inner : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> @out τ_0_1 %6 = alloc_stack $Int @@ -851,7 +851,7 @@ bb0(%0 : $Foo): // CHECK-LABEL: sil [transparent] @identity sil [transparent] @identity : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): - debug_value_addr %1 : $*T + debug_value %1 : $*T, expr op_deref copy_addr [take] %1 to [initialization] %0 : $*T %4 = tuple () // CHECK: return @@ -861,7 +861,7 @@ bb0(%0 : $*T, %1 : $*T): // CHECK-LABEL: sil @applyIdentity sil @applyIdentity : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): - debug_value_addr %1 : $*T + debug_value %1 : $*T, expr op_deref %3 = function_ref @identity : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0 %4 = alloc_stack $T copy_addr %1 to [initialization] %4 : $*T @@ -877,7 +877,7 @@ bb0(%0 : $*T, %1 : $*T): // CHECK-LABEL: sil [transparent] @partial sil [transparent] @partial : $@convention(thin) (@in T, @owned @callee_owned (@in T) -> @out U) -> @out U { bb0(%0 : $*U, %1 : $*T, %2 : $@callee_owned (@in T) -> @out U): - debug_value_addr %1 : $*T + debug_value %1 : $*T, expr op_deref debug_value %2 : $@callee_owned (@in T) -> @out U strong_retain %2 : $@callee_owned (@in T) -> @out U %6 = alloc_stack $T diff --git a/test/SILOptimizer/mandatory_inlining_ownership.sil b/test/SILOptimizer/mandatory_inlining_ownership.sil index f9efe7759fbfc..0f87864e124b5 100644 --- a/test/SILOptimizer/mandatory_inlining_ownership.sil +++ b/test/SILOptimizer/mandatory_inlining_ownership.sil @@ -120,13 +120,11 @@ bb0(%0 : @guaranteed $@callee_guaranteed (@owned Builtin.NativeObject) -> @owned // CHECK: [[ARG_COPY_2:%.*]] = copy_value [[ARG_COPY]] // CHECK: [[PAI:%.*]] = partial_apply [[FN]]([[ARG_COPY]]) // CHECK: [[ARG_COPY_3:%.*]] = copy_value [[ARG]] -// CHECK: [[PAI_COPY:%.*]] = copy_value [[PAI]] // CHECK: [[FN2:%.*]] = function_ref @nativeobject_plus : // CHECK: [[RESULT:%.*]] = apply [[FN2]]([[ARG_COPY_3]], [[ARG_COPY_2]]) -// CHECK: destroy_value [[PAI_COPY]] -// CHECK: [[PAI_COPY_2:%.*]] = copy_value [[PAI]] +// CHECK: [[PAI_COPY:%.*]] = copy_value [[PAI]] // CHECK: [[OPAQUE_FN:%.*]] = function_ref @partial_apply_user -// CHECK: apply [[OPAQUE_FN]]([[PAI_COPY_2]]) +// CHECK: apply [[OPAQUE_FN]]([[PAI_COPY]]) // CHECK: destroy_value [[PAI]] // CHECK: destroy_value [[ARG]] // CHECK: return [[RESULT]] diff --git a/test/SILOptimizer/mandatory_inlining_ownership2.sil b/test/SILOptimizer/mandatory_inlining_ownership2.sil index faa2d3fe8b5a4..707d6455a7d49 100644 --- a/test/SILOptimizer/mandatory_inlining_ownership2.sil +++ b/test/SILOptimizer/mandatory_inlining_ownership2.sil @@ -510,13 +510,11 @@ bb0(%0 : @owned $@callee_owned (@owned Builtin.NativeObject) -> @owned Builtin.N // CHECK: [[ARG_COPY_2:%.*]] = copy_value [[ARG_COPY]] // CHECK: [[PAI:%.*]] = partial_apply [[FN]]([[ARG_COPY]]) // CHECK: [[ARG_COPY_3:%.*]] = copy_value [[ARG]] -// CHECK: [[PAI_COPY:%.*]] = copy_value [[PAI]] // CHECK: [[FN2:%.*]] = function_ref @nativeobject_plus : // CHECK: [[RESULT:%.*]] = apply [[FN2]]([[ARG_COPY_3]], [[ARG_COPY_2]]) -// CHECK: destroy_value [[PAI_COPY]] -// CHECK: [[PAI_COPY_2:%.*]] = copy_value [[PAI]] +// CHECK: [[PAI_COPY:%.*]] = copy_value [[PAI]] // CHECK: [[OPAQUE_FN:%.*]] = function_ref @partial_apply_user -// CHECK: apply [[OPAQUE_FN]]([[PAI_COPY_2]]) +// CHECK: apply [[OPAQUE_FN]]([[PAI_COPY]]) // CHECK: destroy_value [[PAI]] // CHECK: destroy_value [[ARG]] // CHECK: return [[RESULT]] @@ -781,8 +779,8 @@ bb0(%0 : @owned $C): // CHECK-LABEL: sil [transparent] [ossa] @inner : $@convention(thin) (@in T1, @in T2) -> @out T2 { sil [transparent] [ossa] @inner : $@convention(thin) (@in T1, @in T2) -> @out T2 { bb0(%0 : $*T2, %1 : $*T1, %2 : $*T2): - debug_value_addr %1 : $*T1 - debug_value_addr %2 : $*T2 + debug_value %1 : $*T1, expr op_deref + debug_value %2 : $*T2, expr op_deref copy_addr [take] %2 to [initialization] %0 : $*T2 destroy_addr %1 : $*T1 %7 = tuple () @@ -795,7 +793,7 @@ bb0(%0 : $*T2, %1 : $*T1, %2 : $*T2): sil [transparent] [ossa] @middle : $@convention(thin) (Int, @in T) -> @out T { bb0(%0 : $*T, %1 : $Int, %2 : $*T): debug_value %1 : $Int - debug_value_addr %2 : $*T + debug_value %2 : $*T, expr op_deref // CHECK-NOT: [[REF:%[a-zA-Z0-9]+]] = function_ref @inner %5 = function_ref @inner : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> @out τ_0_1 %6 = alloc_stack $Int @@ -868,7 +866,7 @@ bb0(%0 : @owned $Foo): // CHECK-LABEL: sil [transparent] [ossa] @identity sil [transparent] [ossa] @identity : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): - debug_value_addr %1 : $*T + debug_value %1 : $*T, expr op_deref copy_addr [take] %1 to [initialization] %0 : $*T %4 = tuple () // CHECK: return @@ -878,7 +876,7 @@ bb0(%0 : $*T, %1 : $*T): // CHECK-LABEL: sil [ossa] @applyIdentity sil [ossa] @applyIdentity : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): - debug_value_addr %1 : $*T + debug_value %1 : $*T, expr op_deref %3 = function_ref @identity : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0 %4 = alloc_stack $T copy_addr %1 to [initialization] %4 : $*T @@ -898,7 +896,7 @@ bb0(%0 : $*T, %1 : $*T): // CHECK: } // end sil function 'partial' sil [transparent] [ossa] @partial : $@convention(thin) (@in T, @owned @callee_owned (@in T) -> @out U) -> @out U { bb0(%0 : $*U, %1 : $*T, %2 : @owned $@callee_owned (@in T) -> @out U): - debug_value_addr %1 : $*T + debug_value %1 : $*T, expr op_deref debug_value %2 : $@callee_owned (@in T) -> @out U %2copy = copy_value %2 : $@callee_owned (@in T) -> @out U %6 = alloc_stack $T diff --git a/test/SILOptimizer/mandatory_inlining_reasync.swift b/test/SILOptimizer/mandatory_inlining_reasync.swift index 9ee65817d8292..13d32d64c16ba 100644 --- a/test/SILOptimizer/mandatory_inlining_reasync.swift +++ b/test/SILOptimizer/mandatory_inlining_reasync.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -enable-experimental-concurrency %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -enable-experimental-concurrency -disable-availability-checking %s | %FileCheck %s // REQUIRES: concurrency @_transparent diff --git a/test/SILOptimizer/mem-behavior-cache-bug.sil b/test/SILOptimizer/mem-behavior-cache-bug.sil index b9dba86a6b881..47e50022e449b 100644 --- a/test/SILOptimizer/mem-behavior-cache-bug.sil +++ b/test/SILOptimizer/mem-behavior-cache-bug.sil @@ -30,7 +30,7 @@ sil [ossa] @callee : $@convention(method) (@in_guaranteed Complex, Bool, @thin Complex.Type) -> @out Complex { bb0(%0 : $*Complex, %1 : $*Complex, %2 : $Bool, %3 : $@thin Complex.Type): - debug_value_addr %1 : $*Complex, let, name "z", argno 1 + debug_value %1 : $*Complex, let, name "z", argno 1, expr op_deref debug_value %2 : $Bool, let, name "b", argno 2 debug_value %3 : $@thin Complex.Type, let, name "self", argno 3 %7 = alloc_stack $RealType, let, name "θ" diff --git a/test/SILOptimizer/mem-behavior.sil b/test/SILOptimizer/mem-behavior.sil index e651b29aca41c..8bd234bffc2f6 100644 --- a/test/SILOptimizer/mem-behavior.sil +++ b/test/SILOptimizer/mem-behavior.sil @@ -12,6 +12,8 @@ class X { init() } +class Derived : X { } + class C { @_hasStorage @_hasInitialValue final let prop: Builtin.Int32 { get } } @@ -556,6 +558,134 @@ bb0(%0 : $*Builtin.Int32, %1 : $Builtin.Int32): return %r : $() } +// CHECK-LABEL: @testReadAccessOfClassProperty +// CHECK: PAIR #3. +// CHECK-NEXT: %5 = apply %4() : $@convention(thin) () -> () +// CHECK-NEXT: %1 = ref_element_addr %0 : $X, #X.a +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #4. +// CHECK-NEXT: %5 = apply %4() : $@convention(thin) () -> () +// CHECK-NEXT: %2 = begin_access [read] [dynamic] %1 : $*Int32 +// CHECK-NEXT: r=1,w=0 +// CHECK: PAIR #8. +// CHECK-NEXT: %7 = apply %4() : $@convention(thin) () -> () +// CHECK-NEXT: %2 = begin_access [read] [dynamic] %1 : $*Int32 +// CHECK-NEXT: r=1,w=1 +sil [ossa] @testReadAccessOfClassProperty : $@convention(thin) (@guaranteed X) -> Int32 { +bb0(%0 : @guaranteed $X): + %1 = ref_element_addr %0 : $X, #X.a + %2 = begin_access [read] [dynamic] %1 : $*Int32 + %3 = load [trivial] %2 : $*Int32 + %4 = function_ref @nouser_func : $@convention(thin) () -> () + %5 = apply %4() : $@convention(thin) () -> () + end_access %2 : $*Int32 + %7 = apply %4() : $@convention(thin) () -> () + return %3 : $Int32 +} + +// CHECK-LABEL: @testModifyAccessOfClassProperty +// CHECK: PAIR #3. +// CHECK-NEXT: %6 = apply %5() : $@convention(thin) () -> () +// CHECK-NEXT: %2 = ref_element_addr %0 : $X, #X.a +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #4. +// CHECK-NEXT: %6 = apply %5() : $@convention(thin) () -> () +// CHECK-NEXT: %3 = begin_access [modify] [dynamic] %2 : $*Int32 +// CHECK-NEXT: r=1,w=1 +sil [ossa] @testModifyAccessOfClassProperty : $@convention(thin) (@guaranteed X, Int32) -> () { +bb0(%0 : @guaranteed $X, %1 : $Int32): + %2 = ref_element_addr %0 : $X, #X.a + %3 = begin_access [modify] [dynamic] %2 : $*Int32 + store %1 to [trivial] %3 : $*Int32 + %5 = function_ref @nouser_func : $@convention(thin) () -> () + %6 = apply %5() : $@convention(thin) () -> () + end_access %3 : $*Int32 + %8 = tuple () + return %8 : $() +} + +// CHECK-LABEL: @testImmutableRefWithGuaranteedParam +// CHECK: PAIR #1. +// CHECK-NEXT: %4 = apply %3() : $@convention(thin) () -> () +// CHECK-NEXT: %1 = ref_element_addr [immutable] %0 : $X, #X.a +// CHECK-NEXT: r=1,w=0 +sil [ossa] @testImmutableRefWithGuaranteedParam : $@convention(thin) (@guaranteed X) -> Int32 { +bb0(%0 : @guaranteed $X): + %1 = ref_element_addr [immutable] %0 : $X, #X.a + %3 = load [trivial] %1 : $*Int32 + %5 = function_ref @nouser_func : $@convention(thin) () -> () + %6 = apply %5() : $@convention(thin) () -> () + return %3 : $Int32 +} + +// CHECK-LABEL: @testImmutableRefWithBorrow +// CHECK: PAIR #1. +// CHECK-NEXT: %5 = apply %4() : $@convention(thin) () -> () +// CHECK-NEXT: %2 = ref_element_addr [immutable] %1 : $X, #X.a +// CHECK-NEXT: r=1,w=0 +// CHECK: PAIR #3. +// CHECK-NEXT: %7 = apply %4() : $@convention(thin) () -> () +// CHECK-NEXT: %2 = ref_element_addr [immutable] %1 : $X, #X.a +// CHECK-NEXT: r=1,w=1 +sil [ossa] @testImmutableRefWithBorrow : $@convention(thin) (@owned X) -> Int32 { +bb0(%0 : @owned $X): + %1 = begin_borrow %0 : $X + %2 = ref_element_addr [immutable] %1 : $X, #X.a + %3 = load [trivial] %2 : $*Int32 + %4 = function_ref @nouser_func : $@convention(thin) () -> () + %5 = apply %4() : $@convention(thin) () -> () + end_borrow %1 : $X + %7 = apply %4() : $@convention(thin) () -> () + destroy_value %0 : $X + return %3 : $Int32 +} + +// CHECK-LABEL: @testImmutableRefWithControlFlow +// CHECK: PAIR #0. +// CHECK-NEXT: %2 = apply %1() : $@convention(thin) () -> () +// CHECK-NEXT: %4 = ref_element_addr [immutable] %3 : $X, #X.a +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #2. +// CHECK-NEXT: %6 = apply %1() : $@convention(thin) () -> () +// CHECK-NEXT: %4 = ref_element_addr [immutable] %3 : $X, #X.a +// CHECK-NEXT: r=1,w=0 +// CHECK: PAIR #4. +// CHECK-NEXT: %9 = apply %1() : $@convention(thin) () -> () +// CHECK-NEXT: %4 = ref_element_addr [immutable] %3 : $X, #X.a +// CHECK-NEXT: r=1,w=1 +// CHECK: PAIR #5. +// CHECK-NEXT: %11 = apply %1() : $@convention(thin) () -> () +// CHECK-NEXT: %4 = ref_element_addr [immutable] %3 : $X, #X.a +// CHECK-NEXT: r=1,w=0 +// CHECK: PAIR #7. +// CHECK-NEXT: %14 = apply %1() : $@convention(thin) () -> () +// CHECK-NEXT: %4 = ref_element_addr [immutable] %3 : $X, #X.a +// CHECK-NEXT: r=1,w=1 +sil [ossa] @testImmutableRefWithControlFlow : $@convention(thin) (@owned X) -> Int32 { +bb0(%0 : @owned $X): + %1 = function_ref @nouser_func : $@convention(thin) () -> () + %2 = apply %1() : $@convention(thin) () -> () + %3 = begin_borrow %0 : $X + %4 = ref_element_addr [immutable] %3 : $X, #X.a + %5 = load [trivial] %4 : $*Int32 + %6 = apply %1() : $@convention(thin) () -> () + cond_br undef, bb1, bb2 +bb1: + end_borrow %3 : $X + %9 = apply %1() : $@convention(thin) () -> () + br bb4 +bb2: + %11 = apply %1() : $@convention(thin) () -> () + br bb3 +bb3: + end_borrow %3 : $X + %14 = apply %1() : $@convention(thin) () -> () + br bb4 +bb4: + destroy_value %0 : $X + return %5 : $Int32 +} + // CHECK-LABEL: @testLoadTake // CHECK: PAIR #0. // CHECK-NEXT: %2 = load [take] %0 : $*C @@ -606,10 +736,9 @@ struct Int64Wrapper { // CHECK-LABEL: @testNestedAccessWithInterposedProjection // CHECK: PAIR #2. -// CHECK: %1 = begin_access [modify] [static] %0 : $*Int64Wrapper // users: %7, %2 -// CHECK: %3 = begin_access [read] [static] %2 : $*Int64 // users: %6, %4 -// CHECK: r=1,w=1 -// CHECK: PAIR #3. +// CHECK-NEXT: %1 = begin_access [modify] [static] %0 : $*Int64Wrapper // users: %7, %2 +// CHECK-NEXT: %3 = begin_access [read] [static] %2 : $*Int64 // users: %6, %4 +// CHECK-NEXT: r=1,w=1 sil @testNestedAccessWithInterposedProjection : $@convention(thin) (@inout Int64Wrapper) -> () { bb0(%0 : $*Int64Wrapper): %1 = begin_access [modify] [static] %0 : $*Int64Wrapper diff --git a/test/SILOptimizer/mem2reg.sil b/test/SILOptimizer/mem2reg.sil index 0bbaecf00b39e..5d9c62b2c0d2f 100644 --- a/test/SILOptimizer/mem2reg.sil +++ b/test/SILOptimizer/mem2reg.sil @@ -178,16 +178,16 @@ bb0(%0 : $*()): return %3 : $() } -// CHECK-LABEL: sil @mem2reg_debug_value_addr +// CHECK-LABEL: sil @mem2reg_debug_value // CHECK-NOT: alloc_stack -// CHECK-NOT: debug_value_addr +// CHECK-NOT: debug_value {{.*}} expr op_deref // CHECK: debug_value %0 // CHECK: return -sil @mem2reg_debug_value_addr : $@convention(thin) (Int) -> Int { +sil @mem2reg_debug_value : $@convention(thin) (Int) -> Int { bb0(%0 : $Int): %1 = alloc_stack $Int store %0 to %1 : $*Int - debug_value_addr %1 : $*Int + debug_value %1 : $*Int, expr op_deref %2 = load %1 : $*Int dealloc_stack %1 : $*Int return %2 : $Int @@ -327,8 +327,8 @@ sil @no_real_uses : $@convention(thin) () -> () { bb0: // CHECK-NOT: alloc_stack %0 = alloc_stack $Builtin.Int32 - // CHECK-NOT: debug_value_addr - debug_value_addr %0 : $*Builtin.Int32, let, name "x", argno 1 + // CHECK-NOT: debug_value {{.*}} expr op_deref + debug_value %0 : $*Builtin.Int32, let, name "x", argno 1, expr op_deref // CHECK-NOT: dealloc_stack dealloc_stack %0 : $*Builtin.Int32 // CHECK: [[VAL:%.*]] = tuple () @@ -466,11 +466,11 @@ bb0(%0 : $Optional): return %4 : $() } -// check no dead args are passed to bb3 +// dead args okay in non-ossa // CHECK-LABEL: sil @multi_basic_block_use_on_one_path : // CHECK-NOT: alloc_stack -// CHECK: bb3: -// CHECK-LABEL: } // end sil function 'multi_basic_block_use_on_one_path' +// CHECK: br bb3(undef : $Klass) +// CHECK: bb3([[dead_arg:%.*]]): sil @multi_basic_block_use_on_one_path : $@convention(thin) (@owned Klass) -> () { bb0(%0 : $Klass): %1 = alloc_stack $Klass diff --git a/test/SILOptimizer/mem2reg_ossa.sil b/test/SILOptimizer/mem2reg_ossa.sil index 9ccfc15d70f91..66166e783df72 100644 --- a/test/SILOptimizer/mem2reg_ossa.sil +++ b/test/SILOptimizer/mem2reg_ossa.sil @@ -178,16 +178,16 @@ bb0(%0 : $*()): return %3 : $() } -// CHECK-LABEL: sil [ossa] @mem2reg_debug_value_addr : +// CHECK-LABEL: sil [ossa] @mem2reg_debug_value : // CHECK-NOT: alloc_stack -// CHECK-NOT: debug_value_addr +// CHECK-NOT: debug_value {{.*}} expr op_deref // CHECK: debug_value %0 -// CHECK-LABEL: } // end sil function 'mem2reg_debug_value_addr' -sil [ossa] @mem2reg_debug_value_addr : $@convention(thin) (Int) -> Int { +// CHECK-LABEL: } // end sil function 'mem2reg_debug_value' +sil [ossa] @mem2reg_debug_value : $@convention(thin) (Int) -> Int { bb0(%0 : $Int): %1 = alloc_stack $Int store %0 to [trivial] %1 : $*Int - debug_value_addr %1 : $*Int + debug_value %1 : $*Int, expr op_deref %2 = load [trivial] %1 : $*Int dealloc_stack %1 : $*Int return %2 : $Int @@ -327,8 +327,8 @@ sil [ossa] @no_real_uses : $@convention(thin) () -> () { bb0: // CHECK-NOT: alloc_stack %0 = alloc_stack [dynamic_lifetime] $Builtin.Int32 - // CHECK-NOT: debug_value_addr - debug_value_addr %0 : $*Builtin.Int32, let, name "x", argno 1 + // CHECK-NOT: debug_value {{.*}} expr op_deref + debug_value %0 : $*Builtin.Int32, let, name "x", argno 1, expr op_deref // CHECK-NOT: dealloc_stack dealloc_stack %0 : $*Builtin.Int32 %1 = tuple () diff --git a/test/SILOptimizer/mem2reg_ossa_nontrivial.sil b/test/SILOptimizer/mem2reg_ossa_nontrivial.sil index 219afb86168df..3db25869b706d 100644 --- a/test/SILOptimizer/mem2reg_ossa_nontrivial.sil +++ b/test/SILOptimizer/mem2reg_ossa_nontrivial.sil @@ -26,13 +26,32 @@ struct LargeCodesizeStruct { var s5: SmallCodesizeStruct } +public enum NonTrivialEnum { + case some1(Klass) + case some2(NonTrivialStruct) +} + +struct NonTrivialStruct { + var val:Klass +} + +public enum FakeOptional { + case some(T) + case none +} + +sil [ossa] @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct +sil [ossa] @get_nontrivialenum : $@convention(thin) () -> @owned NonTrivialEnum +sil [ossa] @get_optionalnontrivialstruct : $@convention(thin) () -> @owned FakeOptional +sil [ossa] @take_nontrivialstruct : $@convention(thin) (@owned NonTrivialStruct) -> () + /////////// // Tests // /////////// sil [noinline] [ossa] @blackhole : $@convention(thin) (@in_guaranteed T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "t", argno 1 + debug_value %0 : $*T, let, name "t", argno 1, expr op_deref %2 = tuple () return %2 : $() } @@ -434,9 +453,9 @@ bb3: // CHECK-LABEL: sil [ossa] @multi_asi_basic_block_with_phiarg : // CHECK-NOT: alloc_stack // CHECK-LABEL: bb1: -// CHECK: br bb3(%1 : $Klass, %0 : $Klass) -// CHECK-LABEL: bb2: // CHECK: br bb3(%0 : $Klass, %1 : $Klass) +// CHECK-LABEL: bb2: +// CHECK: br bb3(%1 : $Klass, %0 : $Klass) // CHECK-LABEL: } // end sil function 'multi_asi_basic_block_with_phiarg' sil [ossa] @multi_asi_basic_block_with_phiarg : $@convention(thin) (@owned Klass, @owned Klass) -> () { bb0(%0 : @owned $Klass, %1 : @owned $Klass): @@ -543,16 +562,16 @@ bb3: return %res : $() } -// CHECK-LABEL: sil [ossa] @mem2reg_debug_value_addr : +// CHECK-LABEL: sil [ossa] @mem2reg_debug_value : // CHECK-NOT: alloc_stack -// CHECK-NOT: debug_value_addr +// CHECK-NOT: debug_value {{.*}} expr op_deref // CHECK: debug_value %0 -// CHECK-LABEL: } // end sil function 'mem2reg_debug_value_addr' -sil [ossa] @mem2reg_debug_value_addr : $@convention(thin) (@owned Klass) -> @owned Klass { +// CHECK-LABEL: } // end sil function 'mem2reg_debug_value' +sil [ossa] @mem2reg_debug_value : $@convention(thin) (@owned Klass) -> @owned Klass { bb0(%0 : @owned $Klass): %1 = alloc_stack $Klass store %0 to [init] %1 : $*Klass - debug_value_addr %1 : $*Klass + debug_value %1 : $*Klass, expr op_deref %2 = load [take] %1 : $*Klass dealloc_stack %1 : $*Klass return %2 : $Klass @@ -663,8 +682,8 @@ bb0: %0 = alloc_stack $Klass %local = alloc_ref $Klass store %local to [init] %0 : $*Klass - // CHECK-NOT: debug_value_addr - debug_value_addr %0 : $*Klass + // CHECK-NOT: debug_value {{.*}} expr op_deref + debug_value %0 : $*Klass, expr op_deref destroy_addr %0 : $*Klass // CHECK-NOT: dealloc_stack dealloc_stack %0 : $*Klass @@ -698,7 +717,7 @@ bb0(%0 : @owned $Klass): debug_value %0 : $Klass %2 = alloc_stack $Klass store %0 to [init] %2 : $*Klass - debug_value_addr %2 : $*Klass + debug_value %2 : $*Klass, expr op_deref %5 = load [take] %2 : $*Klass destroy_value %5 : $Klass dealloc_stack %2 : $*Klass @@ -755,3 +774,342 @@ bbret(%new : @owned $Klass): return %new : $Klass } +// CHECK-LABEL: sil [ossa] @test_dynamiclifetime1 : +// CHECK: alloc_stack [dynamic_lifetime] +// CHECK-LABEL: } // end sil function 'test_dynamiclifetime1' +sil [ossa] @test_dynamiclifetime1 : $@convention(thin) () -> () { +bb0: + %2 = alloc_stack $Builtin.Int1 + %3 = alloc_stack [dynamic_lifetime] $NonTrivialStruct + %4 = integer_literal $Builtin.Int1, 0 + store %4 to [trivial] %2 : $*Builtin.Int1 + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + %func = function_ref @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct + %val = apply %func() : $@convention(thin) () -> @owned NonTrivialStruct + %27 = integer_literal $Builtin.Int1, -1 + store %27 to [trivial] %2 : $*Builtin.Int1 + store %val to [init] %3 : $*NonTrivialStruct + br bb3 + +bb3: + %32 = load [trivial] %2 : $*Builtin.Int1 + cond_br %32, bb4, bb5 + +bb4: + %34 = load [take] %3 : $*NonTrivialStruct + destroy_value %34 : $NonTrivialStruct + br bb6 + +bb5: + br bb6 + +bb6: + dealloc_stack %3 : $*NonTrivialStruct + dealloc_stack %2 : $*Builtin.Int1 + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @test_dynamiclifetime2 : +// CHECK: alloc_stack [dynamic_lifetime] +// CHECK-LABEL: } // end sil function 'test_dynamiclifetime2' +sil [ossa] @test_dynamiclifetime2 : $@convention(thin) () -> () { +bb0: + %2 = alloc_stack $Builtin.Int1 + %3 = alloc_stack [dynamic_lifetime] $NonTrivialStruct + %4 = integer_literal $Builtin.Int1, 0 + store %4 to [trivial] %2 : $*Builtin.Int1 + %func1 = function_ref @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct + %val1 = apply %func1() : $@convention(thin) () -> @owned NonTrivialStruct + store %val1 to [init] %3 : $*NonTrivialStruct + %ld1 = load [take] %3 : $*NonTrivialStruct + %func2 = function_ref @take_nontrivialstruct : $@convention(thin) (@owned NonTrivialStruct) -> () + apply %func2(%ld1) : $@convention(thin) (@owned NonTrivialStruct) -> () + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + %val = apply %func1() : $@convention(thin) () -> @owned NonTrivialStruct + %27 = integer_literal $Builtin.Int1, -1 + store %27 to [trivial] %2 : $*Builtin.Int1 + store %val to [init] %3 : $*NonTrivialStruct + br bb3 + +bb3: + %32 = load [trivial] %2 : $*Builtin.Int1 + cond_br %32, bb4, bb5 + +bb4: + %ld2 = load [take] %3 : $*NonTrivialStruct + destroy_value %ld2 : $NonTrivialStruct + br bb6 + +bb5: + br bb6 + +bb6: + dealloc_stack %3 : $*NonTrivialStruct + dealloc_stack %2 : $*Builtin.Int1 + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @test_deadphi1 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'test_deadphi1' +sil [ossa] @test_deadphi1 : $@convention(thin) () -> () { +bb0: + br bb1 + +bb1: + cond_br undef, bb1a, bb6 + +bb1a: + br bb2 + +bb2: + %stk = alloc_stack $NonTrivialStruct + cond_br undef, bb3, bb4 + +bb3: + %func = function_ref @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct + %val = apply %func() : $@convention(thin) () -> @owned NonTrivialStruct + store %val to [init] %stk : $*NonTrivialStruct + %lval1 = load [copy] %stk : $*NonTrivialStruct + destroy_value %lval1 : $NonTrivialStruct + %lval2 = load [take] %stk : $*NonTrivialStruct + destroy_value %lval2 : $NonTrivialStruct + dealloc_stack %stk : $*NonTrivialStruct + cond_br undef, bb3a, bb3b + +bb3a: + br bb1 + +bb3b: + br bb5 + +bb4: + dealloc_stack %stk : $*NonTrivialStruct + unreachable + +bb5: + br bb2 + +bb6: + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @test_deadphi2 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'test_deadphi2' +sil [ossa] @test_deadphi2 : $@convention(thin) () -> () { +bb0: + %0 = alloc_stack $NonTrivialStruct + br bb1 + +bb1: + cond_br undef, bb2, bb3 + +bb2: + %3 = function_ref @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct + %4 = apply %3() : $@convention(thin) () -> @owned NonTrivialStruct + store %4 to [init] %0 : $*NonTrivialStruct + cond_br undef, bb4, bb5 + +bb3: + dealloc_stack %0 : $*NonTrivialStruct + br bb7 + +bb4: + %lval1 = load [take] %0 : $*NonTrivialStruct + destroy_value %lval1 : $NonTrivialStruct + dealloc_stack %0 : $*NonTrivialStruct + br bb7 + +bb5: + %lval2 = load [take] %0 : $*NonTrivialStruct + destroy_value %lval2 : $NonTrivialStruct + dealloc_stack %0 : $*NonTrivialStruct + br bb7 + +bb6: + %17 = tuple () + return %17 : $() + +bb7: + br bb6 +} + +// CHECK-LABEL: sil [ossa] @test_deadphi3 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'test_deadphi3' +sil [ossa] @test_deadphi3 : $@convention(thin) (@owned NonTrivialEnum) -> () { +bb0(%0 : @owned $NonTrivialEnum): + %1 = alloc_stack $NonTrivialStruct + switch_enum %0 : $NonTrivialEnum, case #NonTrivialEnum.some1!enumelt: bb1, case #NonTrivialEnum.some2!enumelt: bb5 + +bb1(%3 : @owned $Klass): + destroy_value %3 : $Klass + %5 = function_ref @get_optionalnontrivialstruct : $@convention(thin) () -> @owned FakeOptional + %6 = apply %5() : $@convention(thin) () -> @owned FakeOptional + switch_enum %6 : $FakeOptional, case #FakeOptional.some!enumelt: bb4, case #FakeOptional.none!enumelt: bb2 + +bb2: + br bb3 + +bb3: + unreachable + +bb4(%10 : @owned $NonTrivialStruct): + store %10 to [init] %1 : $*NonTrivialStruct + br bb9 + +bb5(%13 : @owned $NonTrivialStruct): + destroy_value %13 : $NonTrivialStruct + %15 = function_ref @get_optionalnontrivialstruct : $@convention(thin) () -> @owned FakeOptional + %16 = apply %15() : $@convention(thin) () -> @owned FakeOptional + switch_enum %16 : $FakeOptional, case #FakeOptional.some!enumelt: bb8, case #FakeOptional.none!enumelt: bb6 + +bb6: + br bb7 + +bb7: + unreachable + +bb8(%20 : @owned $NonTrivialStruct): + store %20 to [init] %1 : $*NonTrivialStruct + br bb9 + +bb9: + %23 = function_ref @get_nontrivialenum : $@convention(thin) () -> @owned NonTrivialEnum + %24 = apply %23() : $@convention(thin) () -> @owned NonTrivialEnum + switch_enum %24 : $NonTrivialEnum, case #NonTrivialEnum.some1!enumelt: bb10, case #NonTrivialEnum.some2!enumelt: bb11 + +bb10(%26 : @owned $Klass): + %27 = load [copy] %1 : $*NonTrivialStruct + destroy_value %27 : $NonTrivialStruct + destroy_value %26 : $Klass + br bb12 + +bb11(%31 : @owned $NonTrivialStruct): + %32 = load [copy] %1 : $*NonTrivialStruct + destroy_value %32 : $NonTrivialStruct + destroy_value %31 : $NonTrivialStruct + br bb12 + +bb12: + %36 = load [take] %1 : $*NonTrivialStruct + destroy_value %36 : $NonTrivialStruct + dealloc_stack %1 : $*NonTrivialStruct + %39 = tuple () + return %39 : $() +} + +enum KlassOptional { + case some(Klass) + case none +} + +sil @return_optional_or_error : $@convention(thin) () -> (@owned KlassOptional, @error Error) + +// CHECK-LABEL: sil [ossa] @test_deadphi4 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'test_deadphi4' +sil [ossa] @test_deadphi4 : $@convention(thin) (@owned KlassOptional) -> () { +bb0(%0 : @owned $KlassOptional): + %stk = alloc_stack $KlassOptional + switch_enum %0 : $KlassOptional, case #KlassOptional.some!enumelt: bb2, case #KlassOptional.none!enumelt: bb1 + +bb1: + dealloc_stack %stk : $*KlassOptional + br bb7 + +bb2(%some : @owned $Klass): + destroy_value %some : $Klass + %19 = function_ref @return_optional_or_error : $@convention(thin) () -> (@owned KlassOptional, @error Error) + try_apply %19() : $@convention(thin) () -> (@owned KlassOptional, @error Error), normal bb3, error bb8 + +bb3(%callresult : @owned $KlassOptional): + store %callresult to [init] %stk : $*KlassOptional + %29 = load [take] %stk : $*KlassOptional + switch_enum %29 : $KlassOptional, case #KlassOptional.some!enumelt: bb5, case #KlassOptional.none!enumelt: bb4 + +bb4: + dealloc_stack %stk : $*KlassOptional + br bb7 + +bb5(%33 : @owned $Klass): + destroy_value %33 : $Klass + dealloc_stack %stk : $*KlassOptional + br bb6 + +bb6: + %39 = tuple () + return %39 : $() + +bb7: + br bb6 + +bb8(%err : @owned $Error): + unreachable +} + +// CHECK-LABEL: sil [ossa] @test_deadphi5 : +// CHECK-NOT: alloc_stack +// CHECK-LABEL: } // end sil function 'test_deadphi5' +sil [ossa] @test_deadphi5 : $@convention(thin) () -> () { +bb0: + %stk = alloc_stack $NonTrivialStruct + cond_br undef, bb2, bb1 + +bb1: + %func1 = function_ref @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct + %val1 = apply %func1() : $@convention(thin) () -> @owned NonTrivialStruct + store %val1 to [init] %stk : $*NonTrivialStruct + br bb5 + +bb2: + %func2 = function_ref @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct + %val2 = apply %func2() : $@convention(thin) () -> @owned NonTrivialStruct + store %val2 to [init] %stk : $*NonTrivialStruct + cond_br undef, bb3, bb4 + +bb3: + br bb5 + +bb4: + %lval1 = load [take] %stk : $*NonTrivialStruct + destroy_value %lval1 : $NonTrivialStruct + dealloc_stack %stk : $*NonTrivialStruct + br bb9 + +bb5: + cond_br undef, bb6, bb7 + +bb6: + %lval2 = load [take] %stk : $*NonTrivialStruct + destroy_value %lval2 : $NonTrivialStruct + br bb8 + +bb7: + %lval3 = load [take] %stk : $*NonTrivialStruct + destroy_value %lval3 : $NonTrivialStruct + br bb8 + +bb8: + dealloc_stack %stk : $*NonTrivialStruct + br bb9 + +bb9: + %res = tuple () + return %res : $() +} + diff --git a/test/SILOptimizer/mem2reg_resilient.sil b/test/SILOptimizer/mem2reg_resilient.sil index 6df88cc0b7b4d..faf44aefab174 100644 --- a/test/SILOptimizer/mem2reg_resilient.sil +++ b/test/SILOptimizer/mem2reg_resilient.sil @@ -8,7 +8,7 @@ public struct ResilientStruct { var x: AnyObject } -// CHECK-LABEL: sil @mem2reg_debug_value_addr : +// CHECK-LABEL: sil @mem2reg_debug_value : // CHECK: bb0(%0 : $*ResilientStruct): // CHECK-NEXT: %1 = load %0 // CHECK-NEXT: retain_value %1 @@ -16,14 +16,14 @@ public struct ResilientStruct { // CHECK-NEXT: release_value %1 // CHECK-NEXT: tuple () // CHECK-NEXT: return {{%.*}} : $() -// CHECK: } // end sil function 'mem2reg_debug_value_addr' -sil @mem2reg_debug_value_addr : $@convention(thin) (@in_guaranteed ResilientStruct) -> () { +// CHECK: } // end sil function 'mem2reg_debug_value' +sil @mem2reg_debug_value : $@convention(thin) (@in_guaranteed ResilientStruct) -> () { bb0(%0 : $*ResilientStruct): %1 = alloc_stack $ResilientStruct %2 = load %0 : $*ResilientStruct retain_value %2 : $ResilientStruct store %2 to %1 : $*ResilientStruct - debug_value_addr %1 : $*ResilientStruct + debug_value %1 : $*ResilientStruct, expr op_deref %3 = load %1 : $*ResilientStruct destroy_addr %1 : $*ResilientStruct dealloc_stack %1 : $*ResilientStruct diff --git a/test/SILOptimizer/mem2reg_resilient_ossa.sil b/test/SILOptimizer/mem2reg_resilient_ossa.sil index 25ee0ef094308..02f8910b19625 100644 --- a/test/SILOptimizer/mem2reg_resilient_ossa.sil +++ b/test/SILOptimizer/mem2reg_resilient_ossa.sil @@ -8,18 +8,18 @@ public struct ResilientStruct { var x: AnyObject } -// CHECK-LABEL: sil [ossa] @mem2reg_debug_value_addr : +// CHECK-LABEL: sil [ossa] @mem2reg_debug_value : // CHECK: bb0(%0 : $*ResilientStruct): // CHECK-NEXT: %1 = load [copy] %0 : $*ResilientStruct // CHECK-NEXT: debug_value %1 : $ResilientStruct // CHECK-NEXT: destroy_value %1 : $ResilientStruct -// CHECK-LABEL: } // end sil function 'mem2reg_debug_value_addr' -sil [ossa] @mem2reg_debug_value_addr : $@convention(thin) (@in_guaranteed ResilientStruct) -> () { +// CHECK-LABEL: } // end sil function 'mem2reg_debug_value' +sil [ossa] @mem2reg_debug_value : $@convention(thin) (@in_guaranteed ResilientStruct) -> () { bb0(%0 : $*ResilientStruct): %1 = alloc_stack $ResilientStruct %2 = load [copy] %0 : $*ResilientStruct store %2 to [init] %1 : $*ResilientStruct - debug_value_addr %1 : $*ResilientStruct + debug_value %1 : $*ResilientStruct, expr op_deref %3 = load [take] %1 : $*ResilientStruct destroy_value %3 : $ResilientStruct dealloc_stack %1 : $*ResilientStruct diff --git a/test/SILOptimizer/mem2reg_unreachable.sil b/test/SILOptimizer/mem2reg_unreachable.sil index b179411f4d6b4..569eaa0d78913 100644 --- a/test/SILOptimizer/mem2reg_unreachable.sil +++ b/test/SILOptimizer/mem2reg_unreachable.sil @@ -47,7 +47,7 @@ bb0: unreachable bb1: - debug_value_addr %0 : $*S, let, name "newvalue", argno 1 + debug_value %0 : $*S, let, name "newvalue", argno 1, expr op_deref br bb2 bb2: diff --git a/test/SILOptimizer/mem2reg_unreachable_ossa.sil b/test/SILOptimizer/mem2reg_unreachable_ossa.sil index c6cda0dcd2553..4f2fb67ac8308 100644 --- a/test/SILOptimizer/mem2reg_unreachable_ossa.sil +++ b/test/SILOptimizer/mem2reg_unreachable_ossa.sil @@ -57,7 +57,7 @@ bb0: unreachable bb1: - debug_value_addr %0 : $*S, let, name "newvalue", argno 1 + debug_value %0 : $*S, let, name "newvalue", argno 1, expr op_deref br bb2 bb2: diff --git a/test/SILOptimizer/opaque_values_mandatory.sil b/test/SILOptimizer/opaque_values_mandatory.sil index b70063317edd4..baff8f69b52c0 100644 --- a/test/SILOptimizer/opaque_values_mandatory.sil +++ b/test/SILOptimizer/opaque_values_mandatory.sil @@ -1,12 +1,9 @@ // RUN: %target-sil-opt -diagnostics -enable-sil-opaque-values %s | \ -// RUN: %target-sil-opt -Onone-performance -enable-sil-opaque-values -emit-sorted-sil -sil-disable-pass=mandatory-copy-propagation | \ +// RUN: %target-sil-opt -Onone-performance -enable-sil-verify-all \ +// RUN: -enable-sil-opaque-values -emit-sorted-sil \ +// RUN: -enable-ossa-modules -enable-copy-propagation | \ // RUN: %FileCheck %s -// Using -sil-disable-pass=mandatory-copy-propagation to pattern match -// against older SIL output. At least until -enable-copy-propagation -// has been around long enough in the same form to be worth rewriting -// CHECK lines. - import Builtin sil_stage raw diff --git a/test/SILOptimizer/optimize_hop_to_executor.sil b/test/SILOptimizer/optimize_hop_to_executor.sil index 263b7e856b18e..903fff750b5dc 100644 --- a/test/SILOptimizer/optimize_hop_to_executor.sil +++ b/test/SILOptimizer/optimize_hop_to_executor.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -optimize-hop-to-executor -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -optimize-hop-to-executor | %FileCheck %s // REQUIRES: concurrency @@ -239,3 +239,42 @@ bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor): return %r : $() } +// CHECK-LABEL: sil [ossa] @redundantMandatoryHop : $@convention(method) @async (@guaranteed MyActor) -> () { +// CHECK: bb0(%0 : @guaranteed $MyActor): +// CHECK-NEXT: hop_to_executor %0 +// CHECK: apply +// CHECK: hop_to_executor [mandatory] %0 +// CHECK: } // end sil function 'redundantMandatoryHop' +sil [ossa] @redundantMandatoryHop : $@convention(method) @async (@guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor): + hop_to_executor %0 : $MyActor + %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () + apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () + hop_to_executor [mandatory] %0 : $MyActor + apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @simpleMandatoryHop : $@convention(method) @async (@guaranteed MyActor) -> () { +// CHECK: hop_to_executor [mandatory] +// CHECK: } // end sil function 'simpleMandatoryHop' +sil [ossa] @simpleMandatoryHop : $@convention(method) @async (@guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor): + hop_to_executor [mandatory] %0 : $MyActor + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @simpleMandatoryHopAndSuspend : $@convention(method) @async (@guaranteed MyActor) -> () { +// CHECK: hop_to_executor [mandatory] +// CHECK: apply +// CHECK: } // end sil function 'simpleMandatoryHopAndSuspend' +sil [ossa] @simpleMandatoryHopAndSuspend : $@convention(method) @async (@guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor): + hop_to_executor [mandatory] %0 : $MyActor + %async_f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () + apply %async_f() : $@convention(thin) @async () -> () + %r = tuple () + return %r : $() +} diff --git a/test/SILOptimizer/optional_of_existential.swift b/test/SILOptimizer/optional_of_existential.swift index 892d376759666..71b4362c25588 100644 --- a/test/SILOptimizer/optional_of_existential.swift +++ b/test/SILOptimizer/optional_of_existential.swift @@ -17,10 +17,11 @@ struct X : Q { // CHECK-LABEL: sil hidden @$s4test1XV0A2MeSiSgvg : $@convention(method) (X) -> Optional { // CHECK: bb0(%0 : $X): +// CHECK-NEXT: debug_value // CHECK-NEXT: integer_literal ${{.*}}, 0 // CHECK-NEXT: struct $Int -// CHECK-NEXT: enum $Optional, #Optional.some!enumelt -// CHECK-NEXT: return %3 : $Optional +// CHECK-NEXT: %[[ENUM:[0-9]+]] = enum $Optional, #Optional.some!enumelt +// CHECK-NEXT: return %[[ENUM]] : $Optional // CHECK-NEXT: } // end sil function '$s4test1XV0A2MeSiSgvg' var testMe: Int? { z } } diff --git a/test/SILOptimizer/outliner.swift b/test/SILOptimizer/outliner.swift index 6e8e8b3b87953..695d4d8573525 100644 --- a/test/SILOptimizer/outliner.swift +++ b/test/SILOptimizer/outliner.swift @@ -136,7 +136,7 @@ public func testOutlining() { // CHECK: %15 = function_ref @$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ : $@convention(method) (@guaranteed Optional, @thin String.Type) -> @owned String // CHECK: %16 = metatype $@thin String.Type // CHECK: %17 = apply %15(%9, %16) : $@convention(method) (@guaranteed Optional, @thin String.Type) -> @owned String -// CHECK: release_value %9 : $Optional // id: %18 +// CHECK: release_value %9 : $Optional // CHECK: %19 = enum $Optional, #Optional.some!enumelt, %17 : $String // CHECK: br bb3(%19 : $Optional) // diff --git a/test/SILOptimizer/pointer_conversion.swift b/test/SILOptimizer/pointer_conversion.swift index f62d706affc31..e0739ea265e67 100644 --- a/test/SILOptimizer/pointer_conversion.swift +++ b/test/SILOptimizer/pointer_conversion.swift @@ -56,11 +56,12 @@ public func testMutableArray() { // CHECK: [[POINTER:%.+]] = struct $UnsafeMutableRawPointer ( // CHECK-NEXT: [[DEP_POINTER:%.+]] = mark_dependence [[POINTER]] : $UnsafeMutableRawPointer on {{.*}} : $__ContiguousArrayStorageBase // CHECK: [[FN:%.+]] = function_ref @takesMutableRawPointer - // CHECK-NEXT: apply [[FN]]([[DEP_POINTER]]) - // CHECK-NOT: release + // CHECK: strong_retain {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} + // CHECK: apply [[FN]]([[DEP_POINTER]]) // CHECK-NOT: {{^bb[0-9]+:}} // CHECK: strong_release {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} - // CHECK-NEXT: dealloc_stack {{%.+}} : $*Array + // CHECK: strong_release {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} + // CHECK: dealloc_stack {{%.+}} : $*Array // CHECK-NEXT: [[EMPTY:%.+]] = tuple () // CHECK-NEXT: return [[EMPTY]] } @@ -73,11 +74,12 @@ public func testMutableArrayToOptional() { // CHECK-NEXT: [[DEP_POINTER:%.+]] = mark_dependence [[POINTER]] : $UnsafeMutableRawPointer on {{.*}} : $__ContiguousArrayStorageBase // CHECK-NEXT: [[OPT_POINTER:%.+]] = enum $Optional, #Optional.some!enumelt, [[DEP_POINTER]] // CHECK: [[FN:%.+]] = function_ref @takesOptMutableRawPointer - // CHECK-NEXT: apply [[FN]]([[OPT_POINTER]]) - // CHECK-NOT: release + // CHECK: strong_retain {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} + // CHECK: apply [[FN]]([[OPT_POINTER]]) // CHECK-NOT: {{^bb[0-9]+:}} // CHECK: strong_release {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} - // CHECK-NEXT: dealloc_stack {{%.+}} : $*Array + // CHECK: strong_release {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} + // CHECK: dealloc_stack {{%.+}} : $*Array // CHECK-NEXT: [[EMPTY:%.+]] = tuple () // CHECK-NEXT: return [[EMPTY]] } diff --git a/test/SILOptimizer/predictable_memaccess_opts.sil b/test/SILOptimizer/predictable_memaccess_opts.sil index d97d0b98fb34f..f24387b883acd 100644 --- a/test/SILOptimizer/predictable_memaccess_opts.sil +++ b/test/SILOptimizer/predictable_memaccess_opts.sil @@ -1968,9 +1968,9 @@ bb0(%0 : $MyInt, %1 : @guaranteed $Foo, %2 : @guaranteed $Baz): %8 = alloc_stack $Baz %9 = copy_value %2 : $Baz store %9 to [init] %8 : $*Baz - debug_value_addr %3 : $*MyInt, var, name "x", argno 1 - debug_value_addr %5 : $*Foo, var, name "y", argno 2 - debug_value_addr %8 : $*Baz, var, name "z", argno 3 + debug_value %3 : $*MyInt, var, name "x", argno 1, expr op_deref + debug_value %5 : $*Foo, var, name "y", argno 2, expr op_deref + debug_value %8 : $*Baz, var, name "z", argno 3, expr op_deref %14 = load [trivial] %3 : $*MyInt %15 = load [copy] %5 : $*Foo diff --git a/test/SILOptimizer/predictable_memopt_ownership.sil b/test/SILOptimizer/predictable_memopt_ownership.sil index 08acda8180db6..2281dd746ab4c 100644 --- a/test/SILOptimizer/predictable_memopt_ownership.sil +++ b/test/SILOptimizer/predictable_memopt_ownership.sil @@ -1367,9 +1367,9 @@ bb0(%0 : $MyInt, %1 : @guaranteed $Foo, %2 : @guaranteed $Baz): %8 = alloc_stack $Baz %9 = copy_value %2 : $Baz store %9 to [init] %8 : $*Baz - debug_value_addr %3 : $*MyInt, var, name "x", argno 1 - debug_value_addr %5 : $*Foo, var, name "y", argno 2 - debug_value_addr %8 : $*Baz, var, name "z", argno 3 + debug_value %3 : $*MyInt, var, name "x", argno 1, expr op_deref + debug_value %5 : $*Foo, var, name "y", argno 2, expr op_deref + debug_value %8 : $*Baz, var, name "z", argno 3, expr op_deref %14 = load [trivial] %3 : $*MyInt %15 = load [copy] %5 : $*Foo diff --git a/test/SILOptimizer/redundant_load_elim_nontrivial_ossa.sil b/test/SILOptimizer/redundant_load_elim_nontrivial_ossa.sil index 3234dcacd3853..f67667d3fc07d 100644 --- a/test/SILOptimizer/redundant_load_elim_nontrivial_ossa.sil +++ b/test/SILOptimizer/redundant_load_elim_nontrivial_ossa.sil @@ -78,11 +78,13 @@ final class NewHalfOpenRangeGenerator : NewRangeGenerator1 { sil_global @total_klass : $Klass sil_global @total_nontrivialstruct : $NonTrivialStruct +sil @use : $@convention(thin) (Builtin.Int32) -> () sil @use_Klass : $@convention(thin) (@owned Klass) -> () sil @use_nontrivialstruct : $@convention(thin) (@owned NonTrivialStruct) -> () sil @use_a : $@convention(thin) (@owned A) -> () sil @use_twofield : $@convention(thin) (@owned TwoField) -> () sil @init_twofield : $@convention(thin) (@thin TwoField.Type) -> @owned TwoField +sil @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct // We have a bug in the old projection code which this test case exposes. // Make sure its handled properly in the new projection. @@ -1051,7 +1053,6 @@ bb8: unreachable } - // CHECK-LABEL: @infinite_loop_and_unreachable : // CHECK: [[V:%[0-9]+]] = load [copy] // CHECK: [[C1:%[0-9]+]] = copy_value [[V]] @@ -1078,3 +1079,171 @@ bb3: unreachable } +// CHECK-LABEL: @test_available_value1 : +// CHECK: load [trivial] +// CHECK-NOT: load [trivial] +// CHECK: } // end sil function 'test_available_value1' +sil [ossa] @test_available_value1 : $@convention(thin) (@in Builtin.Int32) -> () { + +bb0(%0 : $*Builtin.Int32): + %1 = load [trivial] %0 : $*Builtin.Int32 + + %2 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () + %3 = apply %2(%1) : $@convention(thin) (Builtin.Int32) -> () + cond_br undef, bb2, bb3 + +bb1: + cond_br undef, bb9, bb10 + +bb2: + br bb4 + +bb3: + br bb8 + +bb4: + br bb5 + +bb5: + + %9 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () + %10 = apply %9(%1) : $@convention(thin) (Builtin.Int32) -> () + cond_br undef, bb6, bb7 + +bb6: + br bb4 + +bb7: + br bb8 + +bb8: + br bb1 + +bb9: + br bb11 + +bb10: + br bb11 + +bb11: + %17 = tuple () + return %17 : $() +} + +sil [ossa] @test_available_value2 : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { +bb0(%0 : $Builtin.Int32, %1 : $Builtin.Int32): + %2 = alloc_stack $Builtin.Int32 + cond_br undef, bb1, bb2 + +bb1: + br bb3(%0 : $Builtin.Int32) + +bb2: + br bb3(%1 : $Builtin.Int32) + + +bb3(%6 : $Builtin.Int32): + store %6 to [trivial] %2 : $*Builtin.Int32 + cond_br undef, bb5, bb6 + +bb4: + cond_br undef, bb12, bb13 + +bb5: + br bb7 + +bb6: + br bb11 + +bb7: + br bb8 + +bb8: + + %13 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () + %14 = apply %13(%6) : $@convention(thin) (Builtin.Int32) -> () + cond_br undef, bb9, bb10 + +bb9: + br bb7 + +bb10: + br bb11 + +bb11: + br bb4 + +bb12: + br bb14 + +bb13: + br bb14 + +bb14: + dealloc_stack %2 : $*Builtin.Int32 + %22 = tuple () + return %22 : $() +} + +// CHECK-LABEL: @test_available_value3 : +// CHECK-NOT: load +// CHECK: } // end sil function 'test_available_value3' +sil [ossa] @test_available_value3 : $@convention(method) (@owned NonTrivialStruct) -> () { +bb0(%0 : @owned $NonTrivialStruct): + %1 = alloc_stack $NonTrivialStruct + store %0 to [init] %1 : $*NonTrivialStruct + br bb1 + +bb1: + cond_br undef, bb2, bb3 + +bb2: + %5 = function_ref @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStruct + %6 = apply %5() : $@convention(thin) () -> @owned NonTrivialStruct + store %6 to [assign] %1 : $*NonTrivialStruct + cond_br undef, bb4, bb13 + +bb3: + unreachable + +bb4: + cond_br undef, bb5, bb12 + +bb5: + br bb6 + +bb6: + br bb7 + +bb7: + cond_br undef, bb8, bb11 + +bb8: + cond_br undef, bb9, bb10 + +bb9: + %15 = load [take] %1 : $*NonTrivialStruct + destroy_value %15 : $NonTrivialStruct + dealloc_stack %1 : $*NonTrivialStruct + br bb14 + +bb10: + br bb6 + +bb11: + br bb1 + +bb12: + br bb1 + +bb13: + %22 = load [take] %1 : $*NonTrivialStruct + destroy_value %22 : $NonTrivialStruct + dealloc_stack %1 : $*NonTrivialStruct + br bb14 + +bb14: + %26 = tuple () + return %26 : $() +} + diff --git a/test/SILOptimizer/semantic-arc-opts-loadcopy-to-loadborrow.sil b/test/SILOptimizer/semantic-arc-opts-loadcopy-to-loadborrow.sil index 1f5e57cac17bd..1b4b0c849cadb 100644 --- a/test/SILOptimizer/semantic-arc-opts-loadcopy-to-loadborrow.sil +++ b/test/SILOptimizer/semantic-arc-opts-loadcopy-to-loadborrow.sil @@ -13,6 +13,7 @@ import Builtin ////////////////// typealias AnyObject = Builtin.AnyObject +protocol Error {} enum MyNever {} enum FakeOptional { @@ -1450,3 +1451,51 @@ bb1: bb2: unreachable } + +// Make sure that we ignore type dependent operands when walking through the use +// list to determine if we are writing to a piece of memory. +protocol HasClassProperty { + var c: Klass { get set } +} + +struct HasClassPropertyWrapper { + var h: HasClassProperty + var otherProperty: Builtin.NativeObject +} + +sil @use_hasclassproperty : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error) + +// Make sure we don't crash on this code. We used to crash by not ignoring the +// type dependent operand usage of %2. +// +// TODO: We should be able to convert %5 to a load_borrow since we know that %1 +// and %4 do not alias. When that is done, update the test case appropriately. +// +// CHECK-LABEL: sil [ossa] @use_class_property_wrapper : $@convention(thin) (@inout HasClassPropertyWrapper) -> () { +// CHECK: load [copy] +// CHECK: } // end sil function 'use_class_property_wrapper' +sil [ossa] @use_class_property_wrapper : $@convention(thin) (@inout HasClassPropertyWrapper) -> () { +bb0(%0 : $*HasClassPropertyWrapper): + %1 = struct_element_addr %0 : $*HasClassPropertyWrapper, #HasClassPropertyWrapper.h + %2 = open_existential_addr mutable_access %1 : $*HasClassProperty to $*@opened("85AB1D00-DF62-11EB-A413-ACDE48001122") HasClassProperty + %3 = function_ref @use_hasclassproperty : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error) + %4 = struct_element_addr %0 : $*HasClassPropertyWrapper, #HasClassPropertyWrapper.otherProperty + %5 = load [copy] %4 : $*Builtin.NativeObject + %f2 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %f2(%5) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + try_apply %3<@opened("85AB1D00-DF62-11EB-A413-ACDE48001122") HasClassProperty>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error), normal bb1, error bb2 + +bb1(%str : @owned $Builtin.NativeObject): + destroy_value %str : $Builtin.NativeObject + destroy_value %5 : $Builtin.NativeObject + br bb3 + +bb2(%error : @owned $Error): + destroy_value %error : $Error + destroy_value %5 : $Builtin.NativeObject + br bb3 + +bb3: + %9999 = tuple() + return %9999 : $() +} diff --git a/test/SILOptimizer/sil_combine_apply_ossa.sil b/test/SILOptimizer/sil_combine_apply_ossa.sil index 4da05825adca0..176354a314082 100644 --- a/test/SILOptimizer/sil_combine_apply_ossa.sil +++ b/test/SILOptimizer/sil_combine_apply_ossa.sil @@ -1,4 +1,6 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -sil-combine -sil-combine-disable-alloc-stack-opts | %FileCheck %s +// RUN: %target-sil-opt -enable-ossa-modules -enable-sil-verify-all %s -sil-combine -sil-combine-disable-alloc-stack-opts | %FileCheck %s + +// -enable-ossa-modules happens to enable copy propagation during sil-combine, which these tests rely on. import Swift import Builtin @@ -516,10 +518,10 @@ sil [ossa] @guaranteed_closure : $@convention(thin) (@guaranteed C) -> () // CHECK-LABEL: sil [ossa] @test_guaranteed_closure // CHECK: bb0([[ARG:%.*]] : @guaranteed $C): -// CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: [[F:%.*]] = function_ref @guaranteed_closure +// CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[F]]([[ARG_COPY]]) -// CHECK: apply [[F]]([[ARG]]) +// CHECK: apply [[F]](%0) // Don't release the closure context -- it is @callee_guaranteed. // CHECK-NOT: destroy_value [[C]] : $@callee_guaranteed () -> () // CHECK-NOT: destroy_value [[C]] : $@callee_guaranteed () -> () diff --git a/test/SILOptimizer/sil_combine_ossa.sil b/test/SILOptimizer/sil_combine_ossa.sil index dfef85df1f402..b691dd5fcd46b 100644 --- a/test/SILOptimizer/sil_combine_ossa.sil +++ b/test/SILOptimizer/sil_combine_ossa.sil @@ -1,5 +1,6 @@ // RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -sil-combine | %FileCheck %s // RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -sil-combine -generic-specializer | %FileCheck %s --check-prefix=CHECK_FORWARDING_OWNERSHIP_KIND +// RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -sil-combine -enable-copy-propagation // Declare this SIL to be canonical because some tests break raw SIL // conventions. e.g. address-type block args. -enforce-exclusivity=none is also @@ -772,14 +773,18 @@ sil [ossa] @unbalanced_closure : $@convention(thin) (@guaranteed B) -> () // CHECK-NOT: partial_apply // Check that the arguments of the closure are released after its last use // CHECK-NEXT: destroy_value %0 : $B -// CHECK-NEXT: unreachable +// CHECK-NEXT: tuple +// CHECK-NEXT: return // CHECK: } // end sil function 'partial_apply_unbalanced_retain_release' sil [ossa] @partial_apply_unbalanced_retain_release : $@convention(thin) (@owned B) -> () { bb0(%0 : @owned $B): %1 = function_ref @unbalanced_closure : $@convention(thin) (@guaranteed B) -> () %2 = partial_apply %1(%0) : $@convention(thin) (@guaranteed B) -> () %2a = copy_value %2 : $@callee_owned () -> () - unreachable + destroy_value %2 : $@callee_owned () -> () + destroy_value %2a : $@callee_owned () -> () + %99 = tuple () + return %99 : $() } class C1 {} @@ -5083,3 +5088,38 @@ bb0(%0 : @guaranteed $Function): destroy_value %6 : $Optional<@callee_guaranteed (MyInt) -> Double> return %13 : $Double } + +sil [reabstraction_thunk] @thunk : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () + +// CHECK-LABEL: sil [ossa] @test_partial_apply_apply_opt1 : +// CHECK-NOT: partial_apply +// CHECK: } // end sil function 'test_partial_apply_apply_opt1' +sil [ossa] @test_partial_apply_apply_opt1 : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () { +bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass): + %c1 = copy_value %0 : $Klass + %c2 = copy_value %1 : $Klass + %f1 = function_ref @thunk : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () + %p1 = partial_apply [callee_guaranteed] %f1(%c1, %c2) : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () + %r = apply %p1() : $@callee_guaranteed () -> () + destroy_value %p1 : $@callee_guaranteed () -> () + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: sil [ossa] @test_partial_apply_apply_opt2 : +// CHECK-NOT: partial_apply +// CHECK: } // end sil function 'test_partial_apply_apply_opt2' +sil [ossa] @test_partial_apply_apply_opt2 : $@convention(thin) (@owned Klass, @owned Klass) -> () { +bb0(%0 : @owned $Klass, %1 : @owned $Klass): + %c1 = copy_value %0 : $Klass + %c2 = copy_value %1 : $Klass + %f1 = function_ref @thunk : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () + %p1 = partial_apply [callee_guaranteed] %f1(%c1, %c2) : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () + %r = apply %p1() : $@callee_guaranteed () -> () + destroy_value %p1 : $@callee_guaranteed () -> () + destroy_value %0 : $Klass + destroy_value %1 : $Klass + %7 = tuple () + return %7 : $() +} + diff --git a/test/SILOptimizer/sil_combiner_concrete_prop_all_args.sil b/test/SILOptimizer/sil_combiner_concrete_prop_all_args.sil index 2a5d01d3b9bc3..805c69b2fbf66 100644 --- a/test/SILOptimizer/sil_combiner_concrete_prop_all_args.sil +++ b/test/SILOptimizer/sil_combiner_concrete_prop_all_args.sil @@ -185,13 +185,13 @@ bb0(%0 : $*SomeNoClassProtocol): // CHECK-LABEL: sil hidden [noinline] @$s21existential_transform3ncpyyF : $@convention(thin) () -> () { // CHECK: bb0: -// CHECK: %0 = alloc_ref $SomeNoClass -// CHECK: debug_value %0 : $SomeNoClass, let, name "self", argno 1 -// CHECK: %2 = function_ref @$s21existential_transform12wrap_foo_ncp1as5Int32VAA19SomeNoClassProtocol_p_tFTf4e_n4main0ghI0C_Tg5 : $@convention(thin) (@guaranteed SomeNoClass) -> Int32 -// CHECK: %3 = apply %2(%0) : $@convention(thin) (@guaranteed SomeNoClass) -> Int32 -// CHECK: strong_release %0 : $SomeNoClass -// CHECK: %5 = tuple () -// CHECK: return %5 : $() +// CHECK: %[[ALLOCREF:[0-9]+]] = alloc_ref $SomeNoClass +// CHECK: debug_value %[[ALLOCREF]] : $SomeNoClass, let, name "self", argno 1 +// CHECK: %[[FUNC:[0-9]+]] = function_ref @$s21existential_transform12wrap_foo_ncp1as5Int32VAA19SomeNoClassProtocol_p_tFTf4e_n4main0ghI0C_Tg5 : $@convention(thin) (@guaranteed SomeNoClass) -> Int32 +// CHECK: %[[RES:[0-9]+]] = apply %[[FUNC]](%[[ALLOCREF]]) : $@convention(thin) (@guaranteed SomeNoClass) -> Int32 +// CHECK: strong_release %[[ALLOCREF]] : $SomeNoClass +// CHECK: %[[RET:[0-9]+]] = tuple () +// CHECK: return %[[RET]] : $() // CHECK-LABEL: } // end sil function '$s21existential_transform3ncpyyF' sil hidden [noinline] @$s21existential_transform3ncpyyF : $@convention(thin) () -> () { bb0: @@ -309,13 +309,13 @@ bb0(%0 : $*SomeNoClassProtocolComp & SomeOtherNoClassProtocolComp): // CHECK-LABEL: sil hidden [noinline] @$s21existential_transform4ncpcyyF : $@convention(thin) () -> () { // CHECK: bb0: -// CHECK: %0 = alloc_ref $SomeNoClassComp -// CHECK: debug_value %0 : $SomeNoClassComp, let, name "self", argno 1 -// CHECK: %2 = function_ref @$s21existential_transform25wrap_no_foo_bar_comp_ncpc1as5Int32VAA23SomeNoClassProtocolComp_AA0j5OtherklmN0p_tFTf4e_n4main0jklN0C_Tg5 : $@convention(thin) (@guaranteed SomeNoClassComp) -> Int32 -// CHECK: %3 = apply %2(%0) : $@convention(thin) (@guaranteed SomeNoClassComp) -> Int32 -// CHECK: strong_release %0 : $SomeNoClassComp -// CHECK: %5 = tuple () -// CHECK: return %5 : $() +// CHECK: %[[ALLOCREF:[0-9]+]] = alloc_ref $SomeNoClassComp +// CHECK: debug_value %[[ALLOCREF]] : $SomeNoClassComp, let, name "self", argno 1 +// CHECK: %[[FUNC:[0-9]+]] = function_ref @$s21existential_transform25wrap_no_foo_bar_comp_ncpc1as5Int32VAA23SomeNoClassProtocolComp_AA0j5OtherklmN0p_tFTf4e_n4main0jklN0C_Tg5 : $@convention(thin) (@guaranteed SomeNoClassComp) -> Int32 +// CHECK: %[[RES:[0-9]+]] = apply %[[FUNC]](%[[ALLOCREF]]) : $@convention(thin) (@guaranteed SomeNoClassComp) -> Int32 +// CHECK: strong_release %[[ALLOCREF]] : $SomeNoClassComp +// CHECK: %[[RET:[0-9]+]] = tuple () +// CHECK: return %[[RET]] : $() // CHECK-LABEL: } // end sil function '$s21existential_transform4ncpcyyF' sil hidden [noinline] @$s21existential_transform4ncpcyyF : $@convention(thin) () -> () { bb0: @@ -381,7 +381,7 @@ bb0(%0 : $KK): sil hidden [noinline] @$s21existential_transform13wrap_inout_cp1as5Int32VAA2PP_pz_tF : $@convention(thin) (@inout PP) -> Int32 { bb0(%0 : $*PP): - debug_value_addr %0 : $*PP, var, name "a", argno 1 + debug_value %0 : $*PP, var, name "a", argno 1, expr op_deref %2 = load %0 : $*PP %3 = open_existential_ref %2 : $PP to $@opened("CC969B02-AC7C-11E8-B742-D0817AD4059B") PP %4 = witness_method $@opened("CC969B02-AC7C-11E8-B742-D0817AD4059B") PP, #PP.foo : (Self) -> () -> Int32, %3 : $@opened("CC969B02-AC7C-11E8-B742-D0817AD4059B") PP : $@convention(witness_method: PP) <τ_0_0 where τ_0_0 : PP> (@guaranteed τ_0_0) -> Int32 diff --git a/test/SILOptimizer/simplify-cfg-debugonly.sil b/test/SILOptimizer/simplify-cfg-debugonly.sil index f9e5fa58b172f..30a251de7540b 100644 --- a/test/SILOptimizer/simplify-cfg-debugonly.sil +++ b/test/SILOptimizer/simplify-cfg-debugonly.sil @@ -8,7 +8,7 @@ import Swift import Builtin import Swift -protocol OtherKlass : class {} +protocol OtherKlass : AnyObject {} class Klass {} diff --git a/test/SILOptimizer/simplify-cfg-stress-test.sil b/test/SILOptimizer/simplify-cfg-stress-test.sil index 8484d05411360..49fd6a8f5fb46 100644 --- a/test/SILOptimizer/simplify-cfg-stress-test.sil +++ b/test/SILOptimizer/simplify-cfg-stress-test.sil @@ -10,7 +10,7 @@ sil @do_something : $@convention(thin) () -> () sil @getit : $@convention(thin) (Int64, Int64) -> Int64 sil @sink : $@convention(thin) (Int64, Int64, Int64, Int64) -> () -// Check if SimplifyCFG can optimize this function in reasonabl time. +// Check if SimplifyCFG can optimize this function in reasonable time. // CHECK-LABEL: sil @testit diff --git a/test/SILOptimizer/simplify_bb_args.sil b/test/SILOptimizer/simplify_bb_args.sil new file mode 100644 index 0000000000000..7cda62038ce44 --- /dev/null +++ b/test/SILOptimizer/simplify_bb_args.sil @@ -0,0 +1,77 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-bb-args -sroa-bb-args -enable-ossa-simplify-cfg -enable-ossa-rewriteterminator | %FileCheck %s + +// OSSA form of simplify_cfg_args_crash.sil. + +// Depends on the temporary test options: -enable-ossa-simplify-cfg -enable-ossa-rewriteterminator +// +// REQUIRES: EnableOSSASimplifyCFG + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +enum E { + case A + case B +} + +// Check that we don't crash in simplifyToSelectValue (rdar://problem/20037686) + +// CHECK-LABEL: @test +sil public [ossa] @test : $@convention(thin) (Builtin.Int64, @inout E) -> () { + +bb0(%0 : $Builtin.Int64, %x : $*E): + %1 = integer_literal $Builtin.Int64, 0 + %2 = builtin "cmp_eq_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) : $Builtin.Int1 // user: %473 + cond_br %2, bb3a, bb1 + +bb1: + %4 = enum $E, #E.A!enumelt + br bb2(%4 : $E) + +bb2(%6 : $E): + store %6 to [trivial] %x : $*E + br bb3 + +bb3a: + br bb3 + +bb3: // Preds: bb0 bb409 + %8 = tuple () // user: %4307 + return %8 : $() // id: %4307 +} + +// Verify that we do not crash in argument splitting (rdar://problem/25008398). + +class C { + @_hasStorage let x: Builtin.Int32 + init() +} + +struct Pair { + @_hasStorage let first: C + @_hasStorage let second: C +} + +// CHECK-LABEL: @simplify_args_crash +sil [ossa] @simplify_args_crash : $@convention(thin) (@guaranteed Pair) -> () { +bb0(%1 : @guaranteed $Pair): + // CHECK: [[SECOND:%.*]] = struct_extract %0 : $Pair, #Pair.second + // CHECK: [[FIRST:%.*]] = struct_extract %0 : $Pair, #Pair.first + // CHECK: br bb1([[FIRST]] : $C, [[SECOND]] : $C) + %2 = begin_borrow %1 : $Pair + br bb1(%2 : $Pair) + +// CHECK: bb1([[FIRST2:%.*]] : $C, [[SECOND2:%.*]] : $C): +bb1(%3 : @guaranteed $Pair): + // CHECK: [[STRUCT:%.*]] = struct $Pair ([[FIRST2]] : $C, [[SECOND2]] : $C) + // CHECK: [[SECOND3:%.*]] = struct_extract [[STRUCT]] : $Pair, #Pair.second + // CHECK: [[FIRST3:%.*]] = struct_extract [[STRUCT]] : $Pair, #Pair.first + // CHECK: br bb1([[FIRST3]] : $C, [[SECOND3]] : $C) + br bb2 + +bb2: + br bb1(%3 : $Pair) +} diff --git a/test/SILOptimizer/simplify_cfg.sil b/test/SILOptimizer/simplify_cfg.sil index 72c6aa4bd5ac4..40ce7f6522b41 100644 --- a/test/SILOptimizer/simplify_cfg.sil +++ b/test/SILOptimizer/simplify_cfg.sil @@ -1,5 +1,4 @@ // RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s -// FIXME: Update for select_enum change. // Declare this SIL to be canonical because some tests break raw SIL // conventions. e.g. address-type block args. -enforce-exclusivity=none is also diff --git a/test/SILOptimizer/simplify_cfg_address_phi.sil b/test/SILOptimizer/simplify_cfg_address_phi.sil index a6ac46ac17ee9..6b0d094fe5649 100644 --- a/test/SILOptimizer/simplify_cfg_address_phi.sil +++ b/test/SILOptimizer/simplify_cfg_address_phi.sil @@ -135,7 +135,7 @@ bb5: return %val : $Builtin.Int32 } -// Test that debug_value_addr is not unnecessarilly lost during address projection sinking. +// Test that debug_value is not unnecessarilly lost during address projection sinking. public class CC { let r : R init(_ _r: R) { r = _r } @@ -144,10 +144,10 @@ public class CC { sil @useAny : $@convention(thin) (@in_guaranteed V) -> () // CHECK-LABEL: sil @testDebugValue : $@convention(method) (@in_guaranteed S, @guaranteed CC, Bool) -> () { -// CHECK: debug_value_addr %0 : $*S, let, name "u" +// CHECK: debug_value %0 : $*S, let, name "u", expr op_deref // CHECK: apply %{{.*}}(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () // CHECK: [[FIELD:%.*]] = ref_element_addr %1 : $CC, #CC.r -// CHECK: debug_value_addr [[FIELD]] : $*R, let, name "u" +// CHECK: debug_value [[FIELD]] : $*R, let, name "u", expr op_deref // CHECK: apply %{{.*}}([[FIELD]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () // CHECK-LABEL: } // end sil function 'testDebugValue' sil @testDebugValue : $@convention(method) (@in_guaranteed S, @guaranteed CC, Bool) -> () { @@ -156,18 +156,18 @@ bb0(%0 : $*S, %1 : $CC, %2 : $Bool): cond_br %bool, bb1, bb2 bb1: - debug_value_addr %0 : $*S, let, name "u" + debug_value %0 : $*S, let, name "u", expr op_deref %f1 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () %call1 = apply %f1(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () br bb2 bb2: %field = ref_element_addr %1 : $CC, #CC.r - debug_value_addr %field : $*R, let, name "t" + debug_value %field : $*R, let, name "t", expr op_deref cond_br %bool, bb3, bb4 bb3: - debug_value_addr %field : $*R, let, name "u" + debug_value %field : $*R, let, name "u", expr op_deref %f2 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () %call2 = apply %f2(%field) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () br bb4 diff --git a/test/SILOptimizer/simplify_cfg_and_combine.sil b/test/SILOptimizer/simplify_cfg_and_combine.sil index 533e0e6cc05d0..1440ad9e06f05 100644 --- a/test/SILOptimizer/simplify_cfg_and_combine.sil +++ b/test/SILOptimizer/simplify_cfg_and_combine.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg -sil-combine -jumpthread-simplify-cfg -sil-combine | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg -sil-combine -simplify-cfg -sil-combine | %FileCheck %s // These require both SimplifyCFG and SILCombine diff --git a/test/SILOptimizer/simplify_cfg_and_combine_ossa.sil b/test/SILOptimizer/simplify_cfg_and_combine_ossa.sil new file mode 100644 index 0000000000000..89ea79df34e0e --- /dev/null +++ b/test/SILOptimizer/simplify_cfg_and_combine_ossa.sil @@ -0,0 +1,285 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg -enable-ossa-simplify-cfg -sil-combine -simplify-cfg -sil-combine | %FileCheck %s + +// These tests require both SimplifyCFG and SILCombine + +sil_stage canonical + +import Builtin +import Swift + +sil @external_f1 : $@convention(thin) () -> () +sil @external_f2 : $@convention(thin) () -> () +sil @external_f3 : $@convention(thin) () -> () +sil @external_f4 : $@convention(thin) () -> () + +// CHECK-LABEL: sil [ossa] @select_enum_dominance_simplification : $@convention(thin) (Optional) -> () { +// CHECK-NOT: external_f2 +// CHECK-NOT: external_f4 +// CHECK: bb3: +// CHECK-NEXT: tuple +// CHECK-NEXT: return +sil [ossa] @select_enum_dominance_simplification : $@convention(thin) (Optional) -> () { +bb0(%0 : $Optional): + %t = integer_literal $Builtin.Int1, 1 + %f = integer_literal $Builtin.Int1, 0 + %1 = select_enum %0 : $Optional, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1 + cond_br %1, bb1, bb2 + +bb1: + %2 = select_enum %0 : $Optional, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1 + cond_br %2, bb3, bb4 + +bb2: + %3 = select_enum %0 : $Optional, case #Optional.some!enumelt: %f, case #Optional.none!enumelt: %t : $Builtin.Int1 + cond_br %3, bb5, bb6 + +bb3: + %f1 = function_ref @external_f1 : $@convention(thin) () -> () + apply %f1() : $@convention(thin) () -> () + br bb7 + +bb4: + %f2 = function_ref @external_f2 : $@convention(thin) () -> () + apply %f2() : $@convention(thin) () -> () + br bb7 + +bb5: + %f3 = function_ref @external_f3 : $@convention(thin) () -> () + apply %f3() : $@convention(thin) () -> () + br bb7 + +bb6: + %f4 = function_ref @external_f4 : $@convention(thin) () -> () + apply %f4() : $@convention(thin) () -> () + br bb7 + +bb7: + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @switch_enum_dominates_select_enum : $@convention(thin) (Optional) -> () { +// CHECK-NOT: external_f2 +// CHECK: bb3: +// CHECK-NEXT: tuple +// CHECK-NEXT: return +sil [ossa] @switch_enum_dominates_select_enum : $@convention(thin) (Optional) -> () { +bb0(%0 : $Optional): + %t = integer_literal $Builtin.Int1, 1 + %f = integer_literal $Builtin.Int1, 0 + switch_enum %0 : $Optional, case #Optional.none!enumelt: bb4, case #Optional.some!enumelt: bb1 + +bb1(%arg1 : $Int32): + %c = select_enum %0 : $Optional, case #Optional.none!enumelt: %f, case #Optional.some!enumelt: %t : $Builtin.Int1 + cond_br %c, bb2, bb3 + +bb2: + %f1 = function_ref @external_f1 : $@convention(thin) () -> () + apply %f1() : $@convention(thin) () -> () + br bb5 + +bb3: + %f2 = function_ref @external_f2 : $@convention(thin) () -> () + apply %f2() : $@convention(thin) () -> () + br bb5 + +bb4: + %f3 = function_ref @external_f3 : $@convention(thin) () -> () + apply %f3() : $@convention(thin) () -> () + br bb5 + +bb5: + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @switch_enum_dominates_select_enum2 : $@convention(thin) (Optional) -> Builtin.Int32 { +// CHECK-DAG: [[L2:%[0-9]+]] = integer_literal {{.*}}, 2 +// CHECK-DAG: [[L1:%[0-9]+]] = integer_literal {{.*}}, 1 +// CHECK: [[R:%[0-9]+]] = select_enum %0 : $Optional, case #Optional.none!enumelt: [[L2]], case #Optional.some!enumelt: [[L1]] +// CHECK-NEXT: return [[R]] +sil [ossa] @switch_enum_dominates_select_enum2 : $@convention(thin) (Optional) -> Builtin.Int32 { +bb0(%0 : $Optional): + %i1 = integer_literal $Builtin.Int32, 1 + %i0 = integer_literal $Builtin.Int32, 0 + switch_enum %0 : $Optional, case #Optional.none!enumelt: bb2, case #Optional.some!enumelt: bb1 + +bb1(%arg1 : $Int32): + %c = select_enum %0 : $Optional, case #Optional.none!enumelt: %i0, case #Optional.some!enumelt: %i1 : $Builtin.Int32 + br bb3(%c : $Builtin.Int32) + +bb2: + %i2 = integer_literal $Builtin.Int32, 2 + br bb3(%i2 : $Builtin.Int32) + +bb3(%r : $Builtin.Int32): + return %r : $Builtin.Int32 +} + + +// CHECK-LABEL: sil [ossa] @cond_br_dominates_cond_fail : $@convention(thin) (Builtin.Int1) -> () { +// CHECK: bb0(%0 : $Builtin.Int1): +// CHECK-NEXT: tuple +// CHECK-NEXT: return +sil [ossa] @cond_br_dominates_cond_fail : $@convention(thin) (Builtin.Int1) -> () { +bb0(%0 : $Builtin.Int1): + cond_br %0, bb2a, bb1 + +bb1: + cond_fail %0 : $Builtin.Int1 + br bb2 + +bb2a: + br bb2 + +bb2: + %r = tuple() + return %r : $() +} + +/// CHECK-LABEL: sil [ossa] @select_enum_dominates_switch_enum : $@convention(thin) (Int32) -> Int32 { +/// The select_enum dominates the switch_enum and knows exactly which element will be +/// selected. So this test ensures we can remove the switch_enum +/// CHECK-NOT: switch_enum +/// CHECK: return +sil [ossa] @select_enum_dominates_switch_enum : $@convention(thin) (Int32) -> Int32 { +bb0(%0 : $Int32): + %1 = integer_literal $Builtin.Int32, 0 // users: %5, %9, %9 + %2 = integer_literal $Builtin.Int1, -1 // users: %7, %37 + %3 = struct_extract %0 : $Int32, #Int32._value // users: %5, %13 + %5 = builtin "cmp_sle_Int32"(%1 : $Builtin.Int32, %3 : $Builtin.Int32) : $Builtin.Int1 // user: %7 + %7 = builtin "xor_Int1"(%5 : $Builtin.Int1, %2 : $Builtin.Int1) : $Builtin.Int1 // user: %8 + cond_fail %7 : $Builtin.Int1 // id: %8 + br bb1(%1 : $Builtin.Int32, %1 : $Builtin.Int32) // id: %9 + +bb1(%10 : $Builtin.Int32, %11 : $Builtin.Int32): // Preds: bb0 bb6 + %13 = builtin "cmp_eq_Int32"(%11 : $Builtin.Int32, %3 : $Builtin.Int32) : $Builtin.Int1 // user: %14 + cond_br %13, bb2, bb4 // id: %14 + +bb2: // Preds: bb1 + %15 = enum $Optional, #Optional.none!enumelt // user: %16 + br bb3(%11 : $Builtin.Int32, %15 : $Optional) // id: %16 + +bb3(%17 : $Builtin.Int32, %18 : $Optional): // Preds: bb2 bb4 + %t = integer_literal $Builtin.Int1, 1 + %f = integer_literal $Builtin.Int1, 0 + %19 = select_enum %18 : $Optional, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1 + cond_br %19, bb5, bb8 // id: %20 + +bb4: // Preds: bb1 + %21 = integer_literal $Builtin.Int32, 1 // user: %24 + %23 = integer_literal $Builtin.Int1, 0 // user: %24 + %24 = builtin "sadd_with_overflow_Int32"(%11 : $Builtin.Int32, %21 : $Builtin.Int32, %23 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) // user: %25 + %25 = tuple_extract %24 : $(Builtin.Int32, Builtin.Int1), 0 // user: %28 + %26 = struct $Int32 (%11 : $Builtin.Int32) // user: %27 + %27 = enum $Optional, #Optional.some!enumelt, %26 : $Int32 // user: %28 + br bb3(%25 : $Builtin.Int32, %27 : $Optional) // id: %28 + +bb5: // Preds: bb3 + switch_enum %18 : $Optional, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb7 // id: %29 + +bb6(%arg6 : $Int32): // Preds: bb5 + %30 = unchecked_enum_data %18 : $Optional, #Optional.some!enumelt // user: %31 + %31 = struct_extract %30 : $Int32, #Int32._value // user: %34 + %33 = integer_literal $Builtin.Int1, 0 // user: %34 + %34 = builtin "smul_with_overflow_Int32"(%10 : $Builtin.Int32, %31 : $Builtin.Int32, %33 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) // user: %35 + %35 = tuple_extract %34 : $(Builtin.Int32, Builtin.Int1), 0 // user: %36 + br bb1(%35 : $Builtin.Int32, %17 : $Builtin.Int32) // id: %36 + +bb7: // Preds: bb5 + cond_fail %2 : $Builtin.Int1 // id: %37 + unreachable // id: %38 + +bb8: // Preds: bb3 + %39 = struct $Int32 (%10 : $Builtin.Int32) // user: %40 + return %39 : $Int32 // id: %40 +} + +/// CHECK-LABEL: sil [ossa] @select_enum_dominates_switch_enum2 : $@convention(thin) (Int32) -> Int32 { +/// The select_enum dominates the switch_enum and knows exactly which element will be +/// selected. +/// In this case, the switch is reached when the select_enum is false. Given that the switch +/// only has 2 elements, we know that the other element must be selected. +/// CHECK-NOT: switch_enum +/// CHECK: return +sil [ossa] @select_enum_dominates_switch_enum2 : $@convention(thin) (Int32) -> Int32 { +bb0(%0 : $Int32): + %1 = integer_literal $Builtin.Int32, 0 // users: %5, %9, %9 + %2 = integer_literal $Builtin.Int1, -1 // users: %7, %37 + %3 = struct_extract %0 : $Int32, #Int32._value // users: %5, %13 + %5 = builtin "cmp_sle_Int32"(%1 : $Builtin.Int32, %3 : $Builtin.Int32) : $Builtin.Int1 // user: %7 + %7 = builtin "xor_Int1"(%5 : $Builtin.Int1, %2 : $Builtin.Int1) : $Builtin.Int1 // user: %8 + cond_fail %7 : $Builtin.Int1 // id: %8 + br bb1(%1 : $Builtin.Int32, %1 : $Builtin.Int32) // id: %9 + +bb1(%10 : $Builtin.Int32, %11 : $Builtin.Int32): // Preds: bb0 bb6 + %13 = builtin "cmp_eq_Int32"(%11 : $Builtin.Int32, %3 : $Builtin.Int32) : $Builtin.Int1 // user: %14 + cond_br %13, bb2, bb4 // id: %14 + +bb2: // Preds: bb1 + %15 = enum $Optional, #Optional.none!enumelt // user: %16 + br bb3(%11 : $Builtin.Int32, %15 : $Optional) // id: %16 + +bb3(%17 : $Builtin.Int32, %18 : $Optional): // Preds: bb2 bb4 + %t = integer_literal $Builtin.Int1, 1 + %f = integer_literal $Builtin.Int1, 0 + %19 = select_enum %18 : $Optional, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1 + cond_br %19, bb8, bb5 // id: %20 + +bb4: // Preds: bb1 + %21 = integer_literal $Builtin.Int32, 1 // user: %24 + %23 = integer_literal $Builtin.Int1, 0 // user: %24 + %24 = builtin "sadd_with_overflow_Int32"(%11 : $Builtin.Int32, %21 : $Builtin.Int32, %23 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) // user: %25 + %25 = tuple_extract %24 : $(Builtin.Int32, Builtin.Int1), 0 // user: %28 + %26 = struct $Int32 (%11 : $Builtin.Int32) // user: %27 + %27 = enum $Optional, #Optional.some!enumelt, %26 : $Int32 // user: %28 + br bb3(%25 : $Builtin.Int32, %27 : $Optional) // id: %28 + +bb5: // Preds: bb3 + switch_enum %18 : $Optional, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb7 // id: %29 + +bb6(%arg6 : $Int32): // Preds: bb5 + %31 = struct_extract %arg6 : $Int32, #Int32._value // user: %34 + %33 = integer_literal $Builtin.Int1, 0 // user: %34 + %34 = builtin "smul_with_overflow_Int32"(%10 : $Builtin.Int32, %31 : $Builtin.Int32, %33 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) // user: %35 + %35 = tuple_extract %34 : $(Builtin.Int32, Builtin.Int1), 0 // user: %36 + br bb1(%35 : $Builtin.Int32, %17 : $Builtin.Int32) // id: %36 + +bb7: // Preds: bb5 + cond_fail %2 : $Builtin.Int1 // id: %37 + unreachable // id: %38 + +bb8: // Preds: bb3 + %39 = struct $Int32 (%10 : $Builtin.Int32) // user: %40 + return %39 : $Int32 // id: %40 +} + +// CHECK-LABEL: @switch_enum_dominates_switch_enum_noarg +// CHECK: bb0(%0 : $Optional): +// CHECK-NEXT: %1 = integer_literal $Builtin.Int32, 1 +// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 3 +// CHECK-NEXT: %3 = select_enum %0 : $Optional, case #Optional.none!enumelt: %1, case #Optional.some!enumelt: %2 +// CHECK-NEXT: return %3 +sil @switch_enum_dominates_switch_enum_noarg : $@convention(thin) (Optional) -> Builtin.Int32 { +bb0(%0 : $Optional): + switch_enum %0 : $Optional, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2 + +bb1: + %i1 = integer_literal $Builtin.Int32, 1 + br bb5(%i1 : $Builtin.Int32) + +bb2: + switch_enum %0 : $Optional, case #Optional.none!enumelt: bb3, case #Optional.some!enumelt: bb4 + +bb3: + %i2 = integer_literal $Builtin.Int32, 2 + br bb5(%i2 : $Builtin.Int32) + +bb4: + %i3 = integer_literal $Builtin.Int32, 3 + br bb5(%i3 : $Builtin.Int32) + +bb5(%r : $Builtin.Int32): + return %r : $Builtin.Int32 +} + diff --git a/test/SILOptimizer/simplify_cfg_args_ossa.sil b/test/SILOptimizer/simplify_cfg_args_ossa.sil index 866a37370a4fd..a896f109e3a21 100644 --- a/test/SILOptimizer/simplify_cfg_args_ossa.sil +++ b/test/SILOptimizer/simplify_cfg_args_ossa.sil @@ -1,10 +1,12 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s sil_stage raw import Builtin import Swift +class A {} + sil_global private @globalinit_token11 : $Builtin.Word sil_global private @globalinit_token120 : $Builtin.Word diff --git a/test/SILOptimizer/simplify_cfg_args_ossa_disabled.sil b/test/SILOptimizer/simplify_cfg_args_ossa_disabled.sil index e6f09819ca841..79d8c3bf3acf7 100644 --- a/test/SILOptimizer/simplify_cfg_args_ossa_disabled.sil +++ b/test/SILOptimizer/simplify_cfg_args_ossa_disabled.sil @@ -1,10 +1,10 @@ -// RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s +// +// REQUIRES: EnableOSSASimplifyCFG // // These tests case are converted to OSSA form, but aren't yet -// optimized in OSSA mode. Move them to simplify_cfg_ossa.sil when +// optimized in OSSA mode. Move them to simplify_cfg_args_ossa.sil when // they are enabled. -// -// REQUIRES: disabled class A {} diff --git a/test/SILOptimizer/simplify_cfg_checkcast.sil b/test/SILOptimizer/simplify_cfg_checkcast.sil new file mode 100644 index 0000000000000..45f1166a6d9f6 --- /dev/null +++ b/test/SILOptimizer/simplify_cfg_checkcast.sil @@ -0,0 +1,467 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg -enable-ossa-simplify-cfg -enable-ossa-rewriteterminator | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg -enable-ossa-simplify-cfg -enable-ossa-rewriteterminator -debug-only=sil-simplify-cfg 2>&1 | %FileCheck %s --check-prefix=CHECK-TRACE +// +// REQUIRES: EnableOSSASimplifyCFG + +// FIXME: When OSSA optimization is complete, convert XCHECK to CHECK lines + +// FIXME: which of these tests actually require -jumpthread-simplify-cfg instead of -simplify-cfg? + +sil_stage canonical + +import Builtin +import Swift + +class Klass { + var a: Int + deinit + init() +} + +protocol OtherKlass : AnyObject {} + +sil [ossa] @consume_klass : $@convention(thin) (@owned Klass) -> () +sil [ossa] @use_klass : $@convention(thin) (@guaranteed Klass) -> () +sil [ossa] @get_klass : $@convention(thin) () -> @owned Klass + +sil [ossa] @unknown : $@convention(thin) () -> () + +struct KlassWrapper { + var k: Klass +} + +class Base { + @inline(never) func inner() + func middle() + func outer() +} +class Derived : Base { + override func inner() + @inline(never) final override func middle() +} + +class Final : Derived { +} + +sil [ossa] @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () +sil [ossa] @_TFC3ccb4Base6middlefS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () + +// CHECK-LABEL: sil [ossa] @redundant_checked_cast_br +sil [ossa] @redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () { +bb0(%0 : @guaranteed $Base): +// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + %1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// CHECK: checked_cast_br [exact] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] + checked_cast_br [exact] %0 : $Base to Base, bb2, bb7 + +// CHECK: bb1 +bb1: + %3 = tuple () + return %3 : $() + +bb2(%5 : @guaranteed $Base): +// CHECK: [[SUCCESS]] + %7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// XCHECK-NOT: checked_cast_br + checked_cast_br [exact] %0 : $Base to Base, bb3, bb5 +// CHECK: [[INNER:%.*]] = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () +// CHECK: apply [[INNER]] +// CHECK: br bb1 + +bb3(%9 : @guaranteed $Base): +// CHECK: [[FAIL]] +// CHECK-NOT: function-ref +// CHECK: apply [[METHOD]] + + %10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () + %11 = apply %10(%0) : $@convention(method) (@guaranteed Base) -> () + br bb4 + +bb4: + %13 = tuple () + br bb6(%13 : $()) + +bb5(%defaultBB2 : @guaranteed $Base): + %15 = apply %7(%0) : $@convention(method) (@guaranteed Base) -> () + br bb4 + +bb6(%17 : $()): + br bb1 + +bb7(%defaultBB0 : @guaranteed $Base): + %19 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> () + br bb1 +} + +// CHECK-LABEL: sil [ossa] @redundant_checked_cast_br_owned +sil [ossa] @redundant_checked_cast_br_owned : $@convention(method) (@guaranteed Base) -> () { +// CHECK: [[COPY:%.*]] = copy_value %0 +// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// CHECK: checked_cast_br [exact] [[COPY]] : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] +bb0(%0 : @guaranteed $Base): + %copy = copy_value %0 : $Base + %1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + checked_cast_br [exact] %copy : $Base to Base, bb2, bb7 + +// CHECK: bb1 +bb1: + %3 = tuple () + return %3 : $() + +// CHECK: [[SUCCESS]]([[SUCCESSARG:%.*]] : @owned $Base) +// XCHECK-NOT: checked_cast_br +// XCHECK: [[INNER:%.*]] = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () +// XCHECK: apply [[INNER]]([[SUCCESSARG]]) +// CHECK: br bb1 +bb2(%5 : @owned $Base): + %7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + checked_cast_br [exact] %5 : $Base to Base, bb3, bb5 + +// CHECK: [[FAIL]]([[FAILARG:%.*]] : @owned $Base) +// CHECK-NOT: function-ref +// CHECK: apply [[METHOD]]([[FAILARG]]) +bb3(%9 : @owned $Base): + %10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () + %11 = apply %10(%9) : $@convention(method) (@guaranteed Base) -> () + destroy_value %9 : $Base + br bb4 + +bb4: + %13 = tuple () + br bb6(%13 : $()) + +bb5(%defaultBB2 : @owned $Base): + %15 = apply %7(%defaultBB2) : $@convention(method) (@guaranteed Base) -> () + destroy_value %defaultBB2 : $Base + br bb4 + +bb6(%17 : $()): + br bb1 + +bb7(%defaultBB0 : @owned $Base): + %19 = apply %1(%defaultBB0) : $@convention(method) (@guaranteed Base) -> () + destroy_value %defaultBB0 : $Base + br bb1 +} + +// CHECK-LABEL: sil [ossa] @not_redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () { +sil [ossa] @not_redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () { +bb0(%0 : @guaranteed $Base): +// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + %1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// CHECK: checked_cast_br [exact] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] + checked_cast_br [exact] %0 : $Base to Base, bb2, bb7 + +// CHECK: bb1: +// CHECK: tuple () +// CHECK: return + +bb1: + %3 = tuple () + return %3 : $() + +bb2(%5 : @guaranteed $Base): +// CHECK: [[SUCCESS]] +// CHECK: [[METHOD2:%.*]] = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + %7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + %8 = apply %7(%0) : $@convention(method) (@guaranteed Base) -> () + br bb4 + +bb3(%9 : @guaranteed $Derived): + %10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () + %11 = apply %10(%0) : $@convention(method) (@guaranteed Base) -> () + br bb4 + +bb4: + %13 = tuple () + br bb6(%13 : $()) + +bb5(%9a : @guaranteed $Base): + %14 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + %15 = apply %14(%0) : $@convention(method) (@guaranteed Base) -> () + br bb4 + +bb6(%17 : $()): + br bb1 + +bb7(%10a : @guaranteed $Base): +// CHECK: checked_cast_br [exact] %0 : $Base to Derived + checked_cast_br [exact] %0 : $Base to Derived, bb3, bb5 +} + +// CHECK-LABEL: sil [ossa] @failing_checked_cast_br +sil [ossa] @failing_checked_cast_br : $@convention(method) (@guaranteed Base) -> () { +bb0(%0 : @guaranteed $Base): +// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + %1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// CHECK: checked_cast_br [exact] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] + checked_cast_br [exact] %0 : $Base to Base, bb2, bb7 + +// CHECK-LABEL: bb1 +bb1: + %3 = tuple () + return %3 : $() + +bb2(%5 : @guaranteed $Base): +// CHECK: [[SUCCESS]]([[SUCCESSARG:%.*]] : @guaranteed $Base) +// CHECK: [[METHOD2:%.*]] = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + %7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// XCHECK-NOT: checked_cast_br [exact] %0 : $Base to Derived +// XCHECK: apply [[METHOD2]]([[SUCCESSARG]]) +// Check that checked_cast_br [exact] was replaced by a branch to the failure BB of the checked_cast_br. +// This is because bb2 is reached via the success branch of the checked_cast_br [exact] from bb0. +// It means that the exact dynamic type of %0 is $Base. Thus it cannot be $Derived. +// CHECK: br bb1 + checked_cast_br [exact] %5 : $Base to Derived, bb3, bb5 + +bb3(%9 : @guaranteed $Derived): + %10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () + %11 = apply %10(%0) : $@convention(method) (@guaranteed Base) -> () + br bb4 + +bb4: + %13 = tuple () + br bb6(%13 : $()) + +bb5(%9o : @guaranteed $Base): + %15 = apply %7(%9o) : $@convention(method) (@guaranteed Base) -> () + br bb4 + +bb6(%17 : $()): + br bb1 + +bb7(%anotherDefaultPayload : @guaranteed $Base): + %19 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> () + br bb1 +} + +// CHECK-LABEL: sil [ossa] @failing_checked_cast_br_owned +sil [ossa] @failing_checked_cast_br_owned : $@convention(method) (@guaranteed Base) -> () { +// CHECK: [[COPY:%.*]] = copy_value %0 +// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// CHECK: checked_cast_br [exact] [[COPY]] : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] +bb0(%0 : @guaranteed $Base): + %copy = copy_value %0 : $Base + %1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + checked_cast_br [exact] %copy : $Base to Base, bb2, bb7 + +// CHECK-LABEL: bb1 +bb1: + %3 = tuple () + return %3 : $() + +// CHECK: [[SUCCESS]]([[SUCCESSARG:%.*]] : @owned $Base) +// CHECK: [[METHOD2:%.*]] = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// XCHECK-NOT: checked_cast_br [exact] %0 : $Base to Derived +// XCHECK: apply [[METHOD2]]([[SUCCESSARG]]) +// Check that checked_cast_br [exact] was replaced by a branch to the failure BB of the checked_cast_br. +// This is because bb2 is reached via the success branch of the checked_cast_br [exact] from bb0. +// It means that the exact dynamic type of %0 is $Base. Thus it cannot be $Derived. +// CHECK: br bb1 +bb2(%5 : @owned $Base): + %7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + checked_cast_br [exact] %5 : $Base to Derived, bb3, bb5 + +bb3(%9 : @owned $Derived): + %upcast = upcast %9 : $Derived to $Base + %10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () + %11 = apply %10(%upcast) : $@convention(method) (@guaranteed Base) -> () + destroy_value %upcast : $Base + br bb4 + +bb4: + %13 = tuple () + br bb6(%13 : $()) + +bb5(%9o : @owned $Base): + %15 = apply %7(%9o) : $@convention(method) (@guaranteed Base) -> () + destroy_value %9o : $Base + br bb4 + +bb6(%17 : $()): + br bb1 + +bb7(%anotherDefaultPayload : @owned $Base): + %19 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> () + destroy_value %anotherDefaultPayload : $Base + br bb1 +} + +sil [ossa] @unknown2 : $@convention(thin) () -> () + +// CHECK-LABEL: no_checked_cast_br_threading_with_alloc_ref_stack +// CHECK: checked_cast_br +// CHECK: apply +// CHECK: apply +// CHECK: checked_cast_br +// CHECK: apply +// CHECK: apply +// CHECK: return +sil [ossa] @no_checked_cast_br_threading_with_alloc_ref_stack : $@convention(method) (@guaranteed Base) -> () { +bb0(%0 : @guaranteed $Base): + %fu = function_ref @unknown : $@convention(thin) () -> () + %fu2 = function_ref @unknown2 : $@convention(thin) () -> () + checked_cast_br [exact] %0 : $Base to Base, bb1, bb2 + +bb1(%1 : @guaranteed $Base): + apply %fu() : $@convention(thin) () -> () + br bb3 + +bb2(%1a : @guaranteed $Base): + apply %fu2() : $@convention(thin) () -> () + br bb3 + +bb3: + %a = alloc_ref [stack] $Base + checked_cast_br [exact] %0 : $Base to Base, bb4, bb5 + +bb4(%2 : @guaranteed $Base): + apply %fu() : $@convention(thin) () -> () + br bb6 + +bb5(%2a : @guaranteed $Base): + apply %fu2() : $@convention(thin) () -> () + br bb6 + +bb6: + dealloc_ref [stack] %a : $Base + %r = tuple() + return %r : $() +} + +// Test a redundant checked_cast_br that has success, failure paths, and unknown paths. +// +// TODO: this is currently a bailout. +// +//!!! CHECKME +sil [ossa] @redundant_checked_cast_br_joined_success_fail_unknown : $@convention(method) (@guaranteed Base) -> () { +bb0(%0 : @guaranteed $Base): + %middle = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () + checked_cast_br [exact] %0 : $Base to Base, bb1, bb4 + +bb1(%successBB0 : @guaranteed $Base): + cond_br undef, bb2, bb3 + +bb2: + %successBB0call2 = apply %middle(%successBB0) : $@convention(method) (@guaranteed Base) -> () + %successBB0borrow2 = begin_borrow %successBB0 : $Base + br bb8(%successBB0borrow2 : $Base) + +bb3: + %successBB0call3 = apply %middle(%successBB0) : $@convention(method) (@guaranteed Base) -> () + %successBB0borrow3 = begin_borrow %successBB0 : $Base + br bb7(%successBB0borrow3 : $Base) + +bb4(%failBB0 : @guaranteed $Base): + cond_br undef, bb5, bb6 + +bb5: + %failBB0call5 = apply %middle(%failBB0) : $@convention(method) (@guaranteed Base) -> () + %failBB0borrow5 = begin_borrow %failBB0 : $Base + br bb7(%failBB0borrow5 : $Base) + +bb6: + %failBB0call6 = apply %middle(%failBB0) : $@convention(method) (@guaranteed Base) -> () + %failBB0borrow6 = begin_borrow %failBB0 : $Base + br bb8(%failBB0borrow6 : $Base) + +bb7(%unknown : @guaranteed $Base): + %unknownCall = apply %middle(%unknown) : $@convention(method) (@guaranteed Base) -> () + br bb8(%unknown : $Base) + +bb8(%joined : @guaranteed $Base): + %joinedCall = apply %middle(%joined) : $@convention(method) (@guaranteed Base) -> () + checked_cast_br [exact] %joined : $Base to Base, bb9, bb10 + +bb9(%successBB7 : @guaranteed $Base): + %successBB7call8 = apply %middle(%successBB7) : $@convention(method) (@guaranteed Base) -> () + br bb11 + +bb10(%failBB7 : @guaranteed $Base): + %failBB7call8 = apply %middle(%failBB7) : $@convention(method) (@guaranteed Base) -> () + br bb11 + +bb11: + end_borrow %joined : $Base + %20 = tuple () + return %20 : $() + +} + +// Verify that checked-cast jump-threading kicks in and generates verifiable SIL. +// +// CHECK-TRACE-LABEL: ### Run SimplifyCFG on $testCheckCastJumpThread +// XCHECK-TRACE: Condition is the same if reached over {{.*}} parent @$testCheckCastJumpThread : $@convention(thin) (@guaranteed Klass) -> @owned OtherKlass } +// XCHECK-TRACE-NEXT: bb3(%{{.*}} : $OtherKlass): +// XCHECK-TRACE-NEXT: br bb5(%{{.*}} : $Klass) +sil shared [ossa] @$testCheckCastJumpThread : $@convention(thin) (@guaranteed Klass) -> @owned OtherKlass { +bb0(%0 : @guaranteed $Klass): + %1 = function_ref @get_klass : $@convention(thin) () -> @owned Klass + %2 = integer_literal $Builtin.Int64, 1 + %3 = apply %1() : $@convention(thin) () -> @owned Klass + %4 = copy_value %3 : $Klass + checked_cast_br %3 : $Klass to OtherKlass, bb1, bb2 + +bb1(%5 : $OtherKlass): + destroy_value %5 : $OtherKlass + %6 = integer_literal $Builtin.Int1, -1 + br bb3(%6 : $Builtin.Int1) + +bb2(%7 : @owned $Klass): + destroy_value %7 : $Klass + %8 = integer_literal $Builtin.Int1, 0 + br bb3(%8 : $Builtin.Int1) + +bb3(%10 : $Builtin.Int1): + cond_br %10, bb5, bb6 + +bb4: + unreachable + +bb5: + br bb7(%4 : $Klass) + +bb6: + destroy_value %4 : $Klass + br bb10(%2 : $Builtin.Int64) + +bb7(%16 : @owned $Klass): + checked_cast_br %16 : $Klass to OtherKlass, bb9, bb8 + +bb8(%18 : $Klass): + destroy_value %18 : $Klass + br bb4 + +bb9(%20 : $OtherKlass): + return %20 : $OtherKlass + +bb10(%22 : $Builtin.Int64): + %23 = apply %1() : $@convention(thin) () -> @owned Klass + %24 = copy_value %23 : $Klass + checked_cast_br %23 : $Klass to OtherKlass, bb11, bb12 + +bb11(%25 : $OtherKlass): + %26 = integer_literal $Builtin.Int1, -1 + br bb13(%26 : $Builtin.Int1) + +bb12(%27 : @owned $Klass): + destroy_value %27 : $Klass + %28 = integer_literal $Builtin.Int1, 0 + br bb13(%28 : $Builtin.Int1) + +bb13(%30 : $Builtin.Int1): + cond_br %30, bb14, bb15 + +bb14: + br bb7(%24 : $Klass) + +bb15: + destroy_value %24 : $Klass + cond_br undef, bb16, bb17 + +bb16: + br bb4 + +bb17: + br bb10(undef : $Builtin.Int64) +} diff --git a/test/SILOptimizer/simplify_cfg_dom_jumpthread.sil b/test/SILOptimizer/simplify_cfg_dom_jumpthread.sil new file mode 100644 index 0000000000000..a64797d759958 --- /dev/null +++ b/test/SILOptimizer/simplify_cfg_dom_jumpthread.sil @@ -0,0 +1,468 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s + +// Test dominator-based jump-threading with OSSA. This requires +// -jumpthread-simplify-cfg to enable dominator-based +// jump-threading. + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +// Includes an OSSA form of a test from simplify_cfg_opaque.sil +// ...along with new OSSA test cases. + +class C { + @_hasStorage @_hasInitialValue var field: Int { get set } + init() + + func method() -> Any + func f() -> FakeOptional +} + +sil @getC : $@convention(thin) () -> C + +// Test multiple uses and cloned allocation. +// +// project_box and struct_extract_addr will be sunk into three +// different blocks, but only once per block. +struct S { + @_hasStorage @_hasInitialValue var x: Int { get set } + init(x: Int = 0) + init() +} +sil @doNothing : $@convention(thin) (@inout Int) -> () + +enum FakeOptional { +case some(T) +case none +} + +struct T { + let s: S +} + +enum E { + case A + case B + case C +} + +class Base { } + +class Derived1 : Base { } + +class Derived2 : Base { } + +// Test that jump threading sinks a +// ref_tail_addr->index_addr->struct_element_addr chain and generates +// a phi for the index_addr's index operand. +// +// The retain on separate paths followed by a merged release, and +// target block with a conditional branch are necessary just to get +// jump threading to kick in. +// +// CHECK-LABEL: sil @testJumpThreadIndex : $@convention(thin) (__ContiguousArrayStorageBase, Builtin.Int64) -> Builtin.Int32 { +// CHECK: bb0(%0 : $__ContiguousArrayStorageBase, %1 : $Builtin.Int64): +// CHECK: cond_br undef, bb2, bb1 +// CHECK: bb1: +// CHECK: apply +// CHECK: strong_retain +// CHECK: strong_release +// CHECK: [[IDX2:%.*]] = builtin "truncOrBitCast_Int64_Word"(%1 : $Builtin.Int64) : $Builtin.Word +// CHECK: br bb3([[IDX2]] : $Builtin.Word) +// CHECK: bb2: +// CHECK: apply +// CHECK: strong_retain +// CHECK: strong_release +// CHECK: [[IDX1:%.*]] = builtin "truncOrBitCast_Int64_Word"(%1 : $Builtin.Int64) : $Builtin.Word +// CHECK: br bb3([[IDX1]] : $Builtin.Word) +// CHECK: bb3([[PHI:%.*]] : $Builtin.Word): +// CHECK: [[TAIL:%.*]] = ref_tail_addr %0 : $__ContiguousArrayStorageBase, $Int32 +// CHECK: [[ELT:%.*]] = index_addr [[TAIL]] : $*Int32, %14 : $Builtin.Word +// CHECK: [[ADR:%.*]] = struct_element_addr [[ELT]] : $*Int32, #Int32._value +// CHECK: load [[ADR]] : $*Builtin.Int32 +// CHECK: cond_br undef, bb4, bb5 +// CHECK-LABEL: } // end sil function 'testJumpThreadIndex' +sil @testJumpThreadIndex : $@convention(thin) (__ContiguousArrayStorageBase, Builtin.Int64) -> Builtin.Int32 { +bb0(%0 : $__ContiguousArrayStorageBase, %1 : $Builtin.Int64): + %f = function_ref @getC : $@convention(thin) () -> C + cond_br undef, bb1, bb2 + +bb1: + %c1 = apply %f() : $@convention(thin) ()->C + strong_retain %c1 : $C + br bb3(%c1 : $C) + +bb2: + %c2 = apply %f() : $@convention(thin) ()->C + strong_retain %c2 : $C + br bb3(%c2 : $C) + +bb3(%arg : $C): + strong_release %arg : $C + %tail = ref_tail_addr %0 : $__ContiguousArrayStorageBase, $Int32 + %idx = builtin "truncOrBitCast_Int64_Word"(%1 : $Builtin.Int64) : $Builtin.Word + %elt = index_addr %tail : $*Int32, %idx : $Builtin.Word + %adr = struct_element_addr %elt : $*Int32, #Int32._value + br bb4 + +bb4: + %val = load %adr : $*Builtin.Int32 + cond_br undef, bb4, bb5 + +bb5: + return %val : $Builtin.Int32 +} + +// CHECK-LABEL: sil @testMultiUse : $@convention(method) (Bool, @inout Int) -> () { +// CHECK: bb0(%0 : $Bool, %1 : $*Int): +// CHECK: cond_br %{{.*}}, bb1, bb2 +// CHECK: bb1: +// CHECK: apply %{{.*}}(%1) : $@convention(thin) (@inout Int) -> () +// CHECK: [[ALLOC2:%.*]] = alloc_box ${ var S }, var, name "s" +// CHECK: [[PROJ2:%.*]] = project_box [[ALLOC2]] : ${ var S }, 0 +// CHECK: [[ADR2:%.*]] = struct_element_addr [[PROJ2]] : $*S, #S.x +// CHECK: store %{{.*}} to [[ADR2]] : $*Int +// CHECK: apply %{{.*}}([[ADR2]]) : $@convention(thin) (@inout Int) -> () +// CHECK: br bb3([[ALLOC2]] : ${ var S }) +// CHECK: bb2: +// CHECK: [[ALLOC1:%.*]] = alloc_box ${ var S }, var, name "s" +// CHECK: [[PROJ1:%.*]] = project_box [[ALLOC1]] : ${ var S }, 0 +// CHECK: [[ADR1:%.*]] = struct_element_addr [[PROJ1]] : $*S, #S.x +// CHECK: store %{{.*}} to [[ADR1]] : $*Int +// CHECK: br bb3([[ALLOC1]] : ${ var S }) +// CHECK: bb3([[BOXARG:%.*]] : ${ var S }): +// CHECK: [[PROJ3:%.*]] = project_box [[BOXARG]] : ${ var S }, 0 +// CHECK: [[ADR3:%.*]] = struct_element_addr [[PROJ3]] : $*S, #S.x +// CHECK: apply %{{.*}}([[ADR3]]) : $@convention(thin) (@inout Int) -> () +// CHECK: release_value [[BOXARG]] : ${ var S } +// CHECK-LABEL: } // end sil function 'testMultiUse' +sil @testMultiUse : $@convention(method) (Bool, @inout Int) -> () { +bb0(%0 : $Bool, %1 : $*Int): + %bool = struct_extract %0 : $Bool, #Bool._value + cond_br %bool, bb1, bb2 + +bb1: + %f1 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call1 = apply %f1(%1) : $@convention(thin) (@inout Int) -> () + br bb3 + +bb2: + br bb3 + +bb3: + %box3 = alloc_box ${ var S }, var, name "s" + %proj3 = project_box %box3 : ${ var S }, 0 + %adr3 = struct_element_addr %proj3 : $*S, #S.x + cond_br %bool, bb4, bb5 + +bb4: + %i4 = load %1 : $*Int + store %i4 to %adr3 : $*Int + %f2 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call2 = apply %f2(%adr3) : $@convention(thin) (@inout Int) -> () + br bb6 + +bb5: + %i5 = load %1 : $*Int + store %i5 to %adr3 : $*Int + br bb6 + +bb6: + %f6 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call6 = apply %f6(%adr3) : $@convention(thin) (@inout Int) -> () + release_value %box3 : ${ var S } + %z = tuple () + return %z : $() +} + +// CHECK-LABEL: sil @test_jump_threading +// CHECK: bb5(%{{[0-9]+}} : $Builtin.Int64): +// CHECK-NEXT: br bb1 +sil @test_jump_threading : $@convention(thin) (Builtin.Int1) -> () { +bb0(%0 : $Builtin.Int1): + cond_br %0, bb2, bb3 + +// Blocks are handled from last to first. Block bb1 is placed here so that its argument +// is not optimized before jump threading is done in bb2 and bb3. +bb1(%i4 : $Builtin.Int64): + %f3 = function_ref @get_condition : $@convention(thin) (Builtin.Int64) -> Builtin.Int1 + %c1 = apply %f3(%i3) : $@convention(thin) (Builtin.Int64) -> Builtin.Int1 + %i5 = integer_literal $Builtin.Int64, 27 + cond_br %c1, bb1(%i5 : $Builtin.Int64), bb5 + +bb2: + %f1 = function_ref @get_int1 : $@convention(thin) () -> Builtin.Int64 + %i1 = apply %f1() : $@convention(thin) () -> Builtin.Int64 + br bb4(%i1 : $Builtin.Int64) + +bb3: + %f2 = function_ref @get_int1 : $@convention(thin) () -> Builtin.Int64 + %i2 = apply %f2() : $@convention(thin) () -> Builtin.Int64 + br bb4(%i2 : $Builtin.Int64) + +// Jump threading must not be done for this block because the argument %i3 is also +// used in bb1. +bb4(%i3 : $Builtin.Int64): + br bb1(%i3 : $Builtin.Int64) + +bb5: + %r1 = tuple () + return %r1 : $() + +} + +sil @get_int1 : $@convention(thin) () -> Builtin.Int64 +sil @get_int2 : $@convention(thin) () -> Builtin.Int64 +sil @get_condition : $@convention(thin) (Builtin.Int64) -> Builtin.Int1 + + +public final class AA { +} +public final class BB { + @_hasStorage internal weak final var n: BB! + @_hasStorage internal final var o: AA! +} + +// Test that SimplifyCFG does not hang when compiling an infinite loop with switch_enum. +// CHECK-LABEL: test_inifite_loop +sil hidden @test_inifite_loop : $@convention(method) (@owned BB) -> () { +bb0(%0 : $BB): + %31 = enum $Optional, #Optional.some!enumelt, %0 : $BB + br bb4(%31 : $Optional) + +bb4(%36 : $Optional): + switch_enum %36 : $Optional, case #Optional.some!enumelt: bb6, default bb5 + +bb5: + br bb7 + +bb6: + %39 = unchecked_enum_data %36 : $Optional, #Optional.some!enumelt + %40 = ref_element_addr %39 : $BB, #BB.o + %41 = load %40 : $*Optional + release_value %41 : $Optional + br bb7 + +bb7: + switch_enum %36 : $Optional, case #Optional.none!enumelt: bb8, case #Optional.some!enumelt: bb9 + +bb8: + br bb4(%36 : $Optional) + +bb9: + %48 = unchecked_enum_data %36 : $Optional, #Optional.some!enumelt + %49 = ref_element_addr %48 : $BB, #BB.n + %50 = load_weak %49 : $*@sil_weak Optional + release_value %36 : $Optional + switch_enum %50 : $Optional, case #Optional.some!enumelt: bb11, case #Optional.none!enumelt: bb10 + +bb10: + br bb4(%50 : $Optional) + +bb11: + %54 = unchecked_enum_data %50 : $Optional, #Optional.some!enumelt + %55 = ref_to_raw_pointer %54 : $BB to $Builtin.RawPointer + %56 = ref_to_raw_pointer %0 : $BB to $Builtin.RawPointer + %57 = builtin "cmp_eq_RawPointer"(%55 : $Builtin.RawPointer, %56 : $Builtin.RawPointer) : $Builtin.Int1 + cond_br %57, bb13, bb12 + +bb12: + br bb4(%50 : $Optional) + +bb13: + release_value %50 : $Optional + strong_release %0 : $BB + %65 = tuple () + return %65 : $() +} + +sil @some_function : $@convention(thin) (AA) -> Optional + +// Another test for checking that SimplifyCFG does not hang. +// CHECK-LABEL: test_other_infinite_loop +sil hidden @test_other_infinite_loop : $@convention(method) (@owned AA) -> () { +bb0(%5 : $AA): + strong_retain %5 : $AA + %6 = enum $Optional, #Optional.some!enumelt, %5 : $AA + br bb1(%6 : $Optional) + +bb1(%8 : $Optional): + retain_value %8 : $Optional + switch_enum %8 : $Optional, case #Optional.some!enumelt: bb3, default bb2 + +bb2: + release_value %8 : $Optional + br bb6 + +bb3: + cond_br undef, bb4, bb5 + +bb4: + %85 = tuple () + return %85 : $() + +bb5: + br bb6 + +bb6: + switch_enum %8 : $Optional, case #Optional.none!enumelt: bb7, default bb8 + +bb7: + br bb9(%8 : $Optional) + +bb8: + %23 = unchecked_enum_data %8 : $Optional, #Optional.some!enumelt + strong_retain %23 : $AA + %25 = function_ref @some_function : $@convention(thin) (AA) -> Optional + %26 = apply %25(%23) : $@convention(thin) (AA) -> Optional + strong_release %23 : $AA + br bb9(%26 : $Optional) + +bb9(%29 : $Optional): + release_value %8 : $Optional + br bb1(%29 : $Optional) + +} + +// ----------------------------------------------------------------------------- +// Test jump-threading through a non-pure address producer. +// +// BB3 cannot (currently) be cloned because the block cloner does not +// know how to sink address producers unless they are pure address +// projections. init_existential_addr is not a pure projection. It's +// address is transitively used outside bb3 via %17 = +// tuple_element_addr. Test that cloning is inhibited. If cloning did +// happen, then it would either need to sink init_existential_addr, or +// SSA would be incorrectly updated. + +// Make BB3 is not jump-threaded. And init_existential_addr is not cloned +// +// CHECK-LABEL: sil hidden @nonPureAddressProducer : $@convention(method) (@guaranteed C) -> @out Any { +// CHECK: bb0(%0 : $*Any, %1 : $C): +// CHECK: switch_enum %{{.*}} : $FakeOptional, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2 +// CHECK: bb3(%{{.*}} : $FakeOptional): +// CHECK: init_existential_addr %0 : $*Any, $(FakeOptional, FakeOptional) +// CHECK: switch_enum %{{.*}} : $FakeOptional, case #FakeOptional.some!enumelt: bb4, case #FakeOptional.none!enumelt: bb6 +// CHECK-LABEL: } // end sil function 'nonPureAddressProducer' +sil hidden @nonPureAddressProducer : $@convention(method) (@guaranteed C) -> @out Any { +bb0(%0 : $*Any, %1 : $C): + %3 = class_method %1 : $C, #C.f : (C) -> () -> FakeOptional, $@convention(method) (@guaranteed C) -> FakeOptional + %4 = apply %3(%1) : $@convention(method) (@guaranteed C) -> FakeOptional + switch_enum %4 : $FakeOptional, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2 + +bb1: + %7 = integer_literal $Builtin.Int64, 1 + %8 = struct $Int64 (%7 : $Builtin.Int64) + %9 = enum $FakeOptional, #FakeOptional.some!enumelt, %8 : $Int64 + br bb3(%9 : $FakeOptional) + +bb2: + %11 = enum $FakeOptional, #FakeOptional.none!enumelt + br bb3(%11 : $FakeOptional) + +bb3(%13 : $FakeOptional): + %15 = init_existential_addr %0 : $*Any, $(FakeOptional, FakeOptional) + %16 = tuple_element_addr %15 : $*(FakeOptional, FakeOptional), 0 + %17 = tuple_element_addr %15 : $*(FakeOptional, FakeOptional), 1 + store %13 to %16 : $*FakeOptional + switch_enum %4 : $FakeOptional, case #FakeOptional.some!enumelt: bb4, case #FakeOptional.none!enumelt: bb6 + +bb4(%20 : $T): + %21 = struct_extract %20 : $T, #T.s + %22 = enum $FakeOptional, #FakeOptional.some!enumelt, %21 : $S + store %22 to %17 : $*FakeOptional + br bb5 + +bb5: + %25 = tuple () + return %25 : $() + +bb6: + %27 = enum $FakeOptional, #FakeOptional.none!enumelt + store %27 to %17 : $*FakeOptional + br bb5 +} + +// ----------------------------------------------------------------------------- +// Test select_enum correctness +// ----------------------------------------------------------------------------- + +// Two select_enum instructions must not be considered as the same "condition", +// even if they have the same enum operand. +// This test checks that SimplifyCFG does not remove a dominated terminator with +// such a condition. + +// CHECK-LABEL: sil @test_checked_cast_br +// CHECK: select_enum +// CHECK: checked_cast_br +// CHECK: integer_literal $Builtin.Int64, 1 +// CHECK: select_enum +// CHECK: checked_cast_br +// CHECK: integer_literal $Builtin.Int64, 2 +// CHECK: integer_literal $Builtin.Int64, 3 +// CHECK: return +sil @test_checked_cast_br : $@convention(thin) (E, @owned Base, @owned Base, @owned Base, @owned Base) -> Builtin.Int64 { +bb0(%0 : $E, %1 : $Base, %2 : $Base, %3 : $Base, %4 : $Base): + %s1 = select_enum %0 : $E, case #E.A!enumelt: %1, default %2 : $Base + checked_cast_br %s1 : $Base to Derived1, bb1, bb2 + +bb1(%a1 : $Derived1): + %i1 = integer_literal $Builtin.Int64, 1 + br bb5(%i1 : $Builtin.Int64) + +bb2: + %s2 = select_enum %0 : $E, case #E.B!enumelt: %3, default %4 : $Base + checked_cast_br %s2 : $Base to Derived1, bb3, bb4 + +bb3(%a2 : $Derived1): + %i2 = integer_literal $Builtin.Int64, 2 + br bb5(%i2 : $Builtin.Int64) + +bb4: + %i3 = integer_literal $Builtin.Int64, 3 + br bb5(%i3 : $Builtin.Int64) + +bb5(%a3 : $Builtin.Int64): + return %a3 : $Builtin.Int64 +} + +// CHECK-LABEL: sil @test_cond_br +// CHECK: select_enum +// CHECK: cond_br +// CHECK: integer_literal $Builtin.Int64, 1 +// CHECK: select_enum +// CHECK: cond_br +// CHECK: integer_literal $Builtin.Int64, 2 +// CHECK: integer_literal $Builtin.Int64, 3 +// CHECK: return +sil @test_cond_br : $@convention(thin) (E, @owned Base, @owned Base, @owned Base, @owned Base) -> Builtin.Int64 { +bb0(%0 : $E, %1 : $Base, %2 : $Base, %3 : $Base, %4 : $Base): + %t1 = integer_literal $Builtin.Int1, -1 + %f1 = integer_literal $Builtin.Int1, 0 + %s1 = select_enum %0 : $E, case #E.A!enumelt: %t1, default %f1 : $Builtin.Int1 + cond_br %s1, bb1, bb2 + +bb1: + %i1 = integer_literal $Builtin.Int64, 1 + br bb5(%i1 : $Builtin.Int64) + +bb2: + %s2 = select_enum %0 : $E, case #E.B!enumelt: %t1, default %f1 : $Builtin.Int1 + cond_br %s2, bb3, bb4 + +bb3: + %i2 = integer_literal $Builtin.Int64, 2 + br bb5(%i2 : $Builtin.Int64) + +bb4: + %i3 = integer_literal $Builtin.Int64, 3 + br bb5(%i3 : $Builtin.Int64) + +bb5(%a3 : $Builtin.Int64): + return %a3 : $Builtin.Int64 +} + +// ----------------------------------------------------------------------------- diff --git a/test/SILOptimizer/simplify_cfg_dominators.sil b/test/SILOptimizer/simplify_cfg_dominators.sil new file mode 100644 index 0000000000000..f5ad9bedfd64d --- /dev/null +++ b/test/SILOptimizer/simplify_cfg_dominators.sil @@ -0,0 +1,94 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s + +// Tests dominator-based simplification with OSSA. This does not +// require -jumpthread-simplify-cfg, which enabled jump-threading +// within dominator-based simplification. + +// Includes the OSSA form of tests from simplify_cfg_unique_values.sil. + +sil_stage canonical + +import Builtin +import Swift + +enum DupCaseEnum { + case firstCase + case secondCase +} + +// CHECK-LABEL: sil @performSwitch : $@convention(thin) (Int64, @thin DupCaseEnum.Type) -> DupCaseEnum { +// CHECK: bb0(%0 : $Int64, %1 : $@thin DupCaseEnum.Type): +// CHECK: select_value +// CHECK-NEXT: return +sil @performSwitch : $@convention(thin) (Int64, @thin DupCaseEnum.Type) -> DupCaseEnum { +// %0 // users: %9, %5, %3, %2 +bb0(%0 : $Int64, %1 : $@thin DupCaseEnum.Type): + %4 = integer_literal $Builtin.Int64, 0 // user: %6 + %5 = struct_extract %0 : $Int64, #Int64._value // user: %6 + %6 = builtin "cmp_eq_Int64"(%4 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int1 // users: %10, %7 + cond_br %6, bb6, bb1 // id: %7 + +bb1: // Preds: bb0 + br bb2 // id: %8 + +bb2: // Preds: bb1 + cond_br %6, bb5, bb3 // id: %10 + +bb3: // Preds: bb2 + br bb4 // id: %11 + +bb4: // Preds: bb3 + %12 = enum $DupCaseEnum, #DupCaseEnum.secondCase!enumelt // user: %13 + br bb7(%12 : $DupCaseEnum) // id: %13 + +bb5: // Preds: bb2 + %14 = enum $DupCaseEnum, #DupCaseEnum.firstCase!enumelt // user: %15 + br bb7(%14 : $DupCaseEnum) // id: %15 + +bb6: // Preds: bb0 + %16 = enum $DupCaseEnum, #DupCaseEnum.firstCase!enumelt // user: %17 + br bb7(%16 : $DupCaseEnum) // id: %17 + +// %18 // user: %19 +bb7(%18 : $DupCaseEnum): // Preds: bb6 bb5 bb4 + return %18 : $DupCaseEnum // id: %19 +} + +// CHECK-LABEL: sil @performSwitch_bail_out : $@convention(thin) (Int64, @thin DupCaseEnum.Type) -> DupCaseEnum { +// CHECK: bb0(%0 : $Int64, %1 : $@thin DupCaseEnum.Type): +// CHECK-NOT: select_value +// CHECK-NOT: br bb1 +// CHECK: cond_br +sil @performSwitch_bail_out : $@convention(thin) (Int64, @thin DupCaseEnum.Type) -> DupCaseEnum { +// %0 // users: %9, %5, %3, %2 +bb0(%0 : $Int64, %1 : $@thin DupCaseEnum.Type): + %4 = integer_literal $Builtin.Int64, 0 // user: %6 + %5 = struct_extract %0 : $Int64, #Int64._value // user: %6 + %6 = builtin "cmp_eq_Int64"(%4 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int1 // users: %10, %7 + cond_br %6, bb6, bb1 // id: %7 + +bb1: // Preds: bb0 + br bb2 // id: %8 + +bb2: // Preds: bb1 + cond_br %6, bb5, bb3 // id: %10 + +bb3: // Preds: bb2 + br bb4 // id: %11 + +bb4: // Preds: bb3 + %12 = enum $DupCaseEnum, #DupCaseEnum.secondCase!enumelt // user: %13 + br bb7(%12 : $DupCaseEnum) // id: %13 + +bb5: // Preds: bb2 + %14 = enum $DupCaseEnum, #DupCaseEnum.secondCase!enumelt // user: %15 + br bb7(%14 : $DupCaseEnum) // id: %15 + +bb6: // Preds: bb0 + %16 = enum $DupCaseEnum, #DupCaseEnum.firstCase!enumelt // user: %17 + br bb7(%16 : $DupCaseEnum) // id: %17 + +// %18 // user: %19 +bb7(%18 : $DupCaseEnum): // Preds: bb6 bb5 bb4 + return %18 : $DupCaseEnum // id: %19 +} diff --git a/test/SILOptimizer/simplify_cfg_opaque.sil b/test/SILOptimizer/simplify_cfg_opaque.sil index afb42a31a806a..d67deb744e215 100644 --- a/test/SILOptimizer/simplify_cfg_opaque.sil +++ b/test/SILOptimizer/simplify_cfg_opaque.sil @@ -1,4 +1,8 @@ -// RUN: %target-sil-opt -enable-sil-opaque-values -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-opaque-values -enable-sil-verify-all %s -jumpthread-simplify-cfg -enable-ossa-rewriteterminator | %FileCheck %s + +// REQUIRES: EnableOSSASimplifyCFG + +// FIXME: Convert XCHECK to CHECK sil_stage canonical @@ -6,31 +10,43 @@ protocol P {} // Test jump threading into a destination block terminated by checked_cast_value_br. // -// CHECK-LABEL: sil @testJumpThread : $@convention(thin) (Any, Any) -> () { -// CHECK: bb0(%0 : $Any, %1 : $Any): +// CHECK-LABEL: sil [ossa] @testJumpThread : $@convention(thin) (@owned Any, @owned Any) -> () { +// CHECK: bb0(%0 : @owned $Any, %1 : @owned $Any): // CHECK: cond_br undef, bb1, bb2 // CHECK: bb1: -// CHECK: retain_value %0 : $Any -// CHECK: release_value %0 : $Any -// CHECK: checked_cast_value_br Any in %0 : $Any to P -// CHECK: bb2: -// CHECK: checked_cast_value_br Any in %1 : $Any to P +// CHECK: destroy_value %0 : $Any +// CHECK: destroy_value %1 : $Any +// XCHECK: checked_cast_value_br Any in %0 : $Any to P +// XCHECK: bb2: +// XCHECK: destroy_value %0 : $Any +// XCHECK: destroy_value %1 : $Any +// XCHECK: checked_cast_value_br Any in %1 : $Any to P // CHECK-LABEL: } // end sil function 'testJumpThread' -sil @testJumpThread : $@convention(thin) (Any, Any) -> () { -bb0(%0 : $Any, %1 : $Any): +sil [ossa] @testJumpThread : $@convention(thin) (@owned Any, @owned Any) -> () { +bb0(%0 : @owned $Any, %1 : @owned $Any): cond_br undef, bb1, bb2 + bb1: - retain_value %0 : $Any // force jump-threading - br bb6(%0 : $Any) + %2 = copy_value %0 : $Any // force jump-threading? + br bb6(%2 : $Any) + bb2: - br bb6(%1 : $Any) -bb6(%any : $Any): - release_value %any : $Any // force jump-threading + %3 = copy_value %1 : $Any + br bb6(%3 : $Any) + +bb6(%any : @owned $Any): + destroy_value %0 : $Any + destroy_value %1 : $Any checked_cast_value_br Any in %any : $Any to P, bb7, bb8 + bb7(%p : $P): + destroy_value %p : $P br bb9 -bb8: + +bb8(%fail : $Any): + destroy_value %fail : $Any br bb9 + bb9: %999 = tuple () return %999 : $() diff --git a/test/SILOptimizer/simplify_cfg_ossa.sil b/test/SILOptimizer/simplify_cfg_ossa.sil index 22037b6b8ecb2..3a77cf1357dba 100644 --- a/test/SILOptimizer/simplify_cfg_ossa.sil +++ b/test/SILOptimizer/simplify_cfg_ossa.sil @@ -1,4 +1,13 @@ -// RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s +// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all %s -simplify-cfg -enable-ossa-simplify-cfg | %FileCheck %s + +// OSSA form of tests from simplify_cfg.sil and simplify_cfg_simple.sil. +// +// Test miscellaneous SimplifyCFG optimization from simplifyBlocks. +// +// Does not include: +// - tryJumpThreading. +// - dominatorBasedSimplify +// - CheckedCastBranchJumpThreading // Declare this SIL to be canonical because some tests break raw SIL // conventions. e.g. address-type block args. -enforce-exclusivity=none is also @@ -41,10 +50,35 @@ struct FakeBool { sil [ossa] @dummy : $@convention(thin) () -> FakeBool +struct NonTrivial { + var object: AnyObject +} + /////////// // Tests // /////////// +// CHECK-LABEL: sil @simple_test : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK: } // end sil function 'simple_test' +sil @simple_test : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int1, -1 + cond_br %0, bb1, bb2 + +bb1: + br bb3 + +bb2: + br bb3 + +bb3: + %9999 = tuple () + return %9999 : $() +} + // CHECK-LABEL: sil [ossa] @test_dead_block : // CHECK-NEXT: bb0: // CHECK-NEXT: unreachable @@ -534,9 +568,6 @@ bb1: br bb1 } -import Builtin -import Swift - // CHECK-LABEL: @dead_loop // CHECK-NOT: br bb sil [ossa] @dead_loop : $@convention(thin) () -> () { @@ -1695,24 +1726,24 @@ bb16: br bb8(%65 : $TestEnm) } -sil [ossa] @print : $@convention(thin) (@guaranteed String) -> () -sil [ossa] @yield_string : $@yield_once @convention(thin) () -> @yields @guaranteed String +sil [ossa] @print : $@convention(thin) (@guaranteed NonTrivial) -> () +sil [ossa] @yield_string : $@yield_once @convention(thin) () -> @yields @guaranteed NonTrivial -sil [ossa] @dont_clone_begin_apply : $(Builtin.Int1, @guaranteed String) -> () { -bb0(%condition : $Builtin.Int1, %arg : @guaranteed $String): - %print = function_ref @print : $@convention(thin) (@guaranteed String) -> () +sil [ossa] @dont_clone_begin_apply : $(Builtin.Int1, @guaranteed NonTrivial) -> () { +bb0(%condition : $Builtin.Int1, %arg : @guaranteed $NonTrivial): + %print = function_ref @print : $@convention(thin) (@guaranteed NonTrivial) -> () cond_br %condition, bb1, bb2 bb1: - apply %print(%arg) : $@convention(thin) (@guaranteed String) -> () + apply %print(%arg) : $@convention(thin) (@guaranteed NonTrivial) -> () br bb3 bb2: br bb3 bb3: - %yield_string = function_ref @yield_string : $@yield_once @convention(thin) () -> @yields @guaranteed String - (%yield, %token) = begin_apply %yield_string() : $@yield_once @convention(thin) () -> @yields @guaranteed String + %yield_string = function_ref @yield_string : $@yield_once @convention(thin) () -> @yields @guaranteed NonTrivial + (%yield, %token) = begin_apply %yield_string() : $@yield_once @convention(thin) () -> @yields @guaranteed NonTrivial cond_br %condition, bb4, bb5 bb4: - apply %print(%yield) : $@convention(thin) (@guaranteed String) -> () + apply %print(%yield) : $@convention(thin) (@guaranteed NonTrivial) -> () br bb6 bb5: br bb6 diff --git a/test/SILOptimizer/simplify_cfg_ossa_disabled.sil b/test/SILOptimizer/simplify_cfg_ossa_disabled.sil index d0c55fb0beb71..3986c3f47a8ca 100644 --- a/test/SILOptimizer/simplify_cfg_ossa_disabled.sil +++ b/test/SILOptimizer/simplify_cfg_ossa_disabled.sil @@ -1,10 +1,18 @@ // RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s // // These tests case are converted to OSSA form, but aren't yet -// optimized in OSSA mode. Move them to simplify_cfg_ossa.sil when -// they are enabled. +// optimized in OSSA mode. Move them to one of these files when +// they are enabled: +// - simplify_cfg_ossa.sil +// - simplify_cfg_simplejumpthread.sil +// - simplify_cfg_checkcast.sil +// - simplify_cfg_domjumpthread.sil // -// REQUIRES: disabled +// REQUIRES: EnableOSSASimplifyCFG + +sil_stage canonical + +import Builtin class C {} @@ -49,60 +57,6 @@ bb6(%14 : $FakeBool): return %14 : $FakeBool } -// func testThread2(a : Int32) -> Int32 { -// enum b = (a ? _true : _false) -// if b == _true { return 42 } else { return 17 } -// - -/// CHECK-LABEL: sil [ossa] @testThread2 -/// CHECK: bb0([[COND:%.*]] : {{.*}}): -/// CHECK: cond_br [[COND]], bb1, bb2 -/// CHECK: bb1: -/// CHECK: integer_literal $Builtin.Int64, 42 -/// CHeCK: br bb3 -/// CHECK: bb2: -/// CHECK: integer_literal $Builtin.Int64, 17 -/// CHECK: br bb3 -/// CHECK: bb3 -/// CHECK: return - -sil [ossa] @testThread2 : $@convention(thin) (Builtin.Int1) -> Int64 { -bb0(%0 : $Builtin.Int1): - %t = integer_literal $Builtin.Int1, 1 - %f = integer_literal $Builtin.Int1, 0 - cond_br %0, bb1, bb2 - -bb1: // Preds: bb0 - %4 = enum $BoolLike, #BoolLike.true_!enumelt // user: %5 - br bb3(%4 : $BoolLike) // id: %5 - -bb2: // Preds: bb0 - %8 = enum $BoolLike, #BoolLike.false_!enumelt // user: %9 - br bb3(%8 : $BoolLike) // id: %9 - -bb3(%6 : $BoolLike): // Preds: bb3 bb1 - %100 = select_enum %6 : $BoolLike, case #BoolLike.true_!enumelt: %t, case #BoolLike.false_!enumelt: %f : $Builtin.Int1 - br bb4 // id: %7 - -bb4: // Preds: bb2 - cond_br %100, bb5, bb6 // id: %10 - -bb5: // Preds: bb4 - %11 = metatype $@thin Int64.Type - %12 = integer_literal $Builtin.Int64, 42 // user: %13 - %13 = struct $Int64 (%12 : $Builtin.Int64) // user: %14 - br bb7(%13 : $Int64) // id: %14 - -bb6: // Preds: bb4 - %15 = metatype $@thin Int64.Type - %16 = integer_literal $Builtin.Int64, 17 // user: %17 - %17 = struct $Int64 (%16 : $Builtin.Int64) // user: %18 - br bb7(%17 : $Int64) // id: %18 - -bb7(%19 : $Int64): // Preds: bb6 bb5 - return %19 : $Int64 // id: %21 -} - // func testThread3(a : Int32) -> Int32 { // (enum b, val) = (a ? (_true, 16) : (_false, 17)) // if b == true { return 42 } else { return v } } diff --git a/test/SILOptimizer/simplify_cfg_simple_jumpthread.sil b/test/SILOptimizer/simplify_cfg_simple_jumpthread.sil new file mode 100644 index 0000000000000..3ad12eaa3cc7f --- /dev/null +++ b/test/SILOptimizer/simplify_cfg_simple_jumpthread.sil @@ -0,0 +1,123 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s + +// Tests tryJumpThreading with OSSA. This does not require +// -jumpthread-simplify-cfg, which is only for dominator-based +// jump-threading. + +// Includes the OSSA form of tests from simplify_cfg_simple.sil, +// simplify_cfg_opaque.sil, and simplify_cfg_address_phi.sil. +// ...and new OSSA test cases. + +sil_stage canonical + +import Builtin +import Swift + +class C { + @_hasStorage @_hasInitialValue var field: Int { get set } + init() +} + +sil @getC : $@convention(thin) () -> C + +protocol P {} + +// Test that debug_value is not unnecessarilly lost during address projection sinking. +public class CC { + let r : R + init(_ _r: R) { r = _r } +} + +sil @useAny : $@convention(thin) (@in_guaranteed V) -> () + +// Test that jump threading sinks a ref_element_addr, generating a +// non-address phi for its operand. +// +// The retain on separate paths followed by a merged release, and +// target block with a conditional branch are necessary just to get +// jump threading to kick in. +// +// CHECK-LABEL: sil @testJumpThreadRefEltLoop : $@convention(thin) () -> () { +// CHECK: bb0 +// CHECK: function_ref @getC : $@convention(thin) () -> C +// CHECK: cond_br undef, bb1, bb2 +// CHECK: bb1: +// CHECK: [[C1:%.*]] = apply %0() : $@convention(thin) () -> C +// CHECK: strong_retain [[C1]] : $C +// CHECK: strong_release [[C1]] : $C +// CHECK: br bb3([[C1]] : $C) +// CHECK: bb2: +// CHECK: [[C2:%.*]] = apply %0() : $@convention(thin) () -> C +// CHECK: strong_retain [[C2]] : $C +// CHECK: strong_release [[C2]] : $C +// CHECK: br bb3([[C2]] : $C) +// CHECK: bb3([[PHI:%.*]] : $C): +// CHECK: [[ADR:%.*]] = ref_element_addr [[PHI]] : $C, #C.field +// CHECK: begin_access [read] [dynamic] [[ADR]] : $*Int +// CHECK: load +// CHECK: end_access +// CHECK-LABEL: } // end sil function 'testJumpThreadRefEltLoop' +sil @testJumpThreadRefEltLoop : $@convention(thin) () -> () { +bb0: + %f = function_ref @getC : $@convention(thin) () -> C + cond_br undef, bb1, bb2 + +bb1: + %c1 = apply %f() : $@convention(thin) ()->C + strong_retain %c1 : $C + br bb3(%c1 : $C) + +bb2: + %c2 = apply %f() : $@convention(thin) ()->C + strong_retain %c2 : $C + br bb3(%c2 : $C) + +bb3(%arg : $C): + strong_release %arg : $C + %18 = ref_element_addr %arg : $C, #C.field + br bb4 + +bb4: + %19 = begin_access [read] [dynamic] %18 : $*Int + %20 = load %19 : $*Int + end_access %19 : $*Int + cond_br undef, bb4, bb5 + +bb5: + %z = tuple () + return %z : $() +} + +// CHECK-LABEL: sil @testDebugValue : $@convention(method) (@in_guaranteed S, @guaranteed CC, Bool) -> () { +// CHECK: debug_value %0 : $*S, let, name "u", expr op_deref +// CHECK: apply %{{.*}}(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () +// CHECK: [[FIELD:%.*]] = ref_element_addr %1 : $CC, #CC.r +// CHECK: debug_value [[FIELD]] : $*R, let, name "u", expr op_deref +// CHECK: apply %{{.*}}([[FIELD]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () +// CHECK-LABEL: } // end sil function 'testDebugValue' +sil @testDebugValue : $@convention(method) (@in_guaranteed S, @guaranteed CC, Bool) -> () { +bb0(%0 : $*S, %1 : $CC, %2 : $Bool): + %bool = struct_extract %2 : $Bool, #Bool._value + cond_br %bool, bb1, bb2 + +bb1: + debug_value %0 : $*S, let, name "u", expr op_deref + %f1 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %call1 = apply %f1(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + br bb2 + +bb2: + %field = ref_element_addr %1 : $CC, #CC.r + debug_value %field : $*R, let, name "t", expr op_deref + cond_br %bool, bb3, bb4 + +bb3: + debug_value %field : $*R, let, name "u", expr op_deref + %f2 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %call2 = apply %f2(%field) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + br bb4 + +bb4: + %z = tuple () + return %z : $() +} diff --git a/test/SILOptimizer/simplify_cfg_stress_ossa.sil b/test/SILOptimizer/simplify_cfg_stress_ossa.sil new file mode 100644 index 0000000000000..3a597ae7b39aa --- /dev/null +++ b/test/SILOptimizer/simplify_cfg_stress_ossa.sil @@ -0,0 +1,1084 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift + + +sil @do_something : $@convention(thin) () -> () +sil @getit : $@convention(thin) (Int64, Int64) -> Int64 +sil @sink : $@convention(thin) (Int64, Int64, Int64, Int64) -> () + +// Check if SimplifyCFG can optimize this function in reasonable time. + +// CHECK-LABEL: sil [ossa] @testit + +// Check if there are not too many basic blocks generated. +// CHECK-NOT: bb1000: + +sil [ossa] @testit : $@convention(thin) (Int64, Int64, Int64, Int64, Int64, Int64) -> () { +bb0(%0 : $Int64, %1 : $Int64, %2 : $Int64, %3 : $Int64, %4 : $Int64, %5 : $Int64): + %12 = integer_literal $Builtin.Int64, 2 + %13 = struct_extract %0 : $Int64, #Int64._value + %14 = builtin "cmp_eq_Int64"(%13 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + cond_br %14, bb1, bb4 + +bb1: + %16 = integer_literal $Builtin.Int64, 1 + %17 = struct_extract %1 : $Int64, #Int64._value + %18 = builtin "cmp_eq_Int64"(%17 : $Builtin.Int64, %16 : $Builtin.Int64) : $Builtin.Int1 + cond_br %18, bb2, bb3 + +bb2: + %20 = integer_literal $Builtin.Int1, -1 + br bb5(%20 : $Builtin.Int1) + +bb3: + %22 = builtin "cmp_eq_Int64"(%17 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + br bb5(%22 : $Builtin.Int1) + +bb4: + %24 = integer_literal $Builtin.Int1, 0 + br bb5(%24 : $Builtin.Int1) + + +bb5(%26 : $Builtin.Int1): + %27 = struct $Bool (%26 : $Builtin.Int1) + %29 = integer_literal $Builtin.Int64, 1 + %30 = builtin "cmp_eq_Int64"(%13 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %30, bb6, bb9 + +bb6: + %32 = struct_extract %1 : $Int64, #Int64._value + %33 = builtin "cmp_eq_Int64"(%32 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %33, bb7, bb8 + +bb7: + %35 = integer_literal $Builtin.Int1, -1 + br bb10(%35 : $Builtin.Int1) + +bb8: + %37 = builtin "cmp_eq_Int64"(%32 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + br bb10(%37 : $Builtin.Int1) + +bb9: + %39 = integer_literal $Builtin.Int1, 0 + br bb10(%39 : $Builtin.Int1) + +bb10(%41 : $Builtin.Int1): + %42 = struct $Bool (%41 : $Builtin.Int1) + %44 = struct_extract %1 : $Int64, #Int64._value + %45 = builtin "cmp_eq_Int64"(%44 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + %46 = integer_literal $Builtin.Int64, 3 + %47 = builtin "cmp_eq_Int64"(%44 : $Builtin.Int64, %46 : $Builtin.Int64) : $Builtin.Int1 + %48 = struct_extract %3 : $Int64, #Int64._value + %49 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %49, bb12, bb11 + +bb11: + br bb15 + +bb12: + %52 = integer_literal $Builtin.Int64, 0 + %53 = struct_extract %2 : $Int64, #Int64._value + %54 = builtin "cmp_eq_Int64"(%53 : $Builtin.Int64, %52 : $Builtin.Int64) : $Builtin.Int1 + cond_br %54, bb14, bb13 + +bb13: + br bb15 + +bb14: + %57 = builtin "cmp_slt_Int64"(%44 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + %58 = integer_literal $Builtin.Int1, -1 + %59 = builtin "xor_Int1"(%57 : $Builtin.Int1, %58 : $Builtin.Int1) : $Builtin.Int1 + br bb16(%59 : $Builtin.Int1) + +bb15: + %61 = integer_literal $Builtin.Int1, 0 + br bb16(%61 : $Builtin.Int1) + +bb16(%63 : $Builtin.Int1): + %64 = struct $Bool (%63 : $Builtin.Int1) + %66 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + cond_br %66, bb18, bb17 + +bb17: + br bb21 + +bb18: + %69 = integer_literal $Builtin.Int64, 0 + %70 = struct_extract %2 : $Int64, #Int64._value + %71 = builtin "cmp_eq_Int64"(%70 : $Builtin.Int64, %69 : $Builtin.Int64) : $Builtin.Int1 + cond_br %71, bb20, bb19 + +bb19: + br bb21 + +bb20: + %74 = builtin "cmp_slt_Int64"(%44 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + %75 = integer_literal $Builtin.Int1, -1 + %76 = builtin "xor_Int1"(%74 : $Builtin.Int1, %75 : $Builtin.Int1) : $Builtin.Int1 + br bb22(%76 : $Builtin.Int1) + +bb21: + %78 = integer_literal $Builtin.Int1, 0 + br bb22(%78 : $Builtin.Int1) + +bb22(%80 : $Builtin.Int1): + %81 = struct $Bool (%80 : $Builtin.Int1) + %83 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %46 : $Builtin.Int64) : $Builtin.Int1 + cond_br %83, bb24, bb23 + +bb23: + br bb27 + +bb24: + %86 = integer_literal $Builtin.Int64, 0 + %87 = struct_extract %2 : $Int64, #Int64._value + %88 = builtin "cmp_eq_Int64"(%87 : $Builtin.Int64, %86 : $Builtin.Int64) : $Builtin.Int1 + cond_br %88, bb26, bb25 + +bb25: + br bb27 + +bb26: + %91 = builtin "cmp_slt_Int64"(%44 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + %92 = integer_literal $Builtin.Int1, -1 + %93 = builtin "xor_Int1"(%91 : $Builtin.Int1, %92 : $Builtin.Int1) : $Builtin.Int1 + br bb28(%93 : $Builtin.Int1) + +bb27: + %95 = integer_literal $Builtin.Int1, 0 + br bb28(%95 : $Builtin.Int1) + +bb28(%97 : $Builtin.Int1): + %98 = struct $Bool (%97 : $Builtin.Int1) + %100 = struct_extract %2 : $Int64, #Int64._value + %101 = builtin "cmp_eq_Int64"(%100 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %101, bb29, bb30 + +bb29: + br bb31(%49 : $Builtin.Int1) + +bb30: + %104 = integer_literal $Builtin.Int1, 0 + br bb31(%104 : $Builtin.Int1) + +bb31(%106 : $Builtin.Int1): + %107 = struct $Bool (%106 : $Builtin.Int1) + %109 = builtin "cmp_eq_Int64"(%100 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + cond_br %109, bb32, bb33 + +bb32: + br bb34(%66 : $Builtin.Int1) + +bb33: + %112 = integer_literal $Builtin.Int1, 0 + br bb34(%112 : $Builtin.Int1) + +bb34(%114 : $Builtin.Int1): + %115 = struct $Bool (%114 : $Builtin.Int1) + %117 = builtin "cmp_eq_Int64"(%100 : $Builtin.Int64, %46 : $Builtin.Int64) : $Builtin.Int1 + cond_br %117, bb35, bb36 + +bb35: + br bb37(%83 : $Builtin.Int1) + +bb36: + %120 = integer_literal $Builtin.Int1, 0 + br bb37(%120 : $Builtin.Int1) + +bb37(%122 : $Builtin.Int1): + %123 = struct $Bool (%122 : $Builtin.Int1) + cond_br %101, bb39, bb38 + +bb38: + br bb42 + +bb39: + %127 = integer_literal $Builtin.Int64, 0 + %128 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %127 : $Builtin.Int64) : $Builtin.Int1 + cond_br %128, bb41, bb40 + +bb40: + br bb42 + +bb41: + %131 = struct_extract %4 : $Int64, #Int64._value + %132 = builtin "cmp_eq_Int64"(%131 : $Builtin.Int64, %127 : $Builtin.Int64) : $Builtin.Int1 + br bb43(%132 : $Builtin.Int1) + +bb42: + %134 = integer_literal $Builtin.Int1, 0 + br bb43(%134 : $Builtin.Int1) + + +bb43(%136 : $Builtin.Int1): + %137 = struct $Bool (%136 : $Builtin.Int1) + cond_br %109, bb45, bb44 + +bb44: + br bb48 + +bb45: + %141 = integer_literal $Builtin.Int64, 0 + %142 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %141 : $Builtin.Int64) : $Builtin.Int1 + cond_br %142, bb47, bb46 + +bb46: + br bb48 + +bb47: + %145 = struct_extract %4 : $Int64, #Int64._value + %146 = builtin "cmp_eq_Int64"(%145 : $Builtin.Int64, %141 : $Builtin.Int64) : $Builtin.Int1 + br bb49(%146 : $Builtin.Int1) + +bb48: + %148 = integer_literal $Builtin.Int1, 0 + br bb49(%148 : $Builtin.Int1) + + +bb49(%150 : $Builtin.Int1): + %151 = struct $Bool (%150 : $Builtin.Int1) + cond_br %117, bb51, bb50 + +bb50: + br bb54 + +bb51: + %155 = integer_literal $Builtin.Int64, 0 + %156 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %155 : $Builtin.Int64) : $Builtin.Int1 + cond_br %156, bb53, bb52 + +bb52: + br bb54 + +bb53: + %159 = struct_extract %4 : $Int64, #Int64._value + %160 = builtin "cmp_eq_Int64"(%159 : $Builtin.Int64, %155 : $Builtin.Int64) : $Builtin.Int1 + br bb55(%160 : $Builtin.Int1) + +bb54: + %162 = integer_literal $Builtin.Int1, 0 + br bb55(%162 : $Builtin.Int1) + + +bb55(%164 : $Builtin.Int1): + %165 = struct $Bool (%164 : $Builtin.Int1) + %167 = struct_extract %4 : $Int64, #Int64._value + %168 = builtin "cmp_eq_Int64"(%167 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + %169 = builtin "cmp_eq_Int64"(%167 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + %170 = builtin "cmp_eq_Int64"(%167 : $Builtin.Int64, %46 : $Builtin.Int64) : $Builtin.Int1 + cond_br %14, bb57, bb56 + +bb56: + br bb66 + +bb57: + %173 = integer_literal $Builtin.Int64, 0 + %174 = builtin "cmp_eq_Int64"(%44 : $Builtin.Int64, %173 : $Builtin.Int64) : $Builtin.Int1 + cond_br %174, bb59, bb58 + +bb58: + br bb66 + +bb59: + %177 = builtin "cmp_eq_Int64"(%100 : $Builtin.Int64, %173 : $Builtin.Int64) : $Builtin.Int1 + cond_br %177, bb61, bb60 + +bb60: + br bb66 + +bb61: + %180 = builtin "cmp_eq_Int64"(%167 : $Builtin.Int64, %173 : $Builtin.Int64) : $Builtin.Int1 + cond_br %180, bb63, bb62 + +bb62: + br bb66 + +bb63: + %183 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %173 : $Builtin.Int64) : $Builtin.Int1 + cond_br %183, bb65, bb64 + +bb64: + br bb66 + +bb65: + %186 = struct_extract %5 : $Int64, #Int64._value + %187 = builtin "cmp_eq_Int64"(%186 : $Builtin.Int64, %173 : $Builtin.Int64) : $Builtin.Int1 + br bb67(%187 : $Builtin.Int1) + +bb66: + %189 = integer_literal $Builtin.Int1, 0 + br bb67(%189 : $Builtin.Int1) + +bb67(%191 : $Builtin.Int1): + %192 = struct $Bool (%191 : $Builtin.Int1) + cond_br %30, bb69, bb68 + +bb68: + br bb78 + +bb69: + %196 = integer_literal $Builtin.Int64, 0 + %197 = builtin "cmp_eq_Int64"(%44 : $Builtin.Int64, %196 : $Builtin.Int64) : $Builtin.Int1 + cond_br %197, bb71, bb70 + +bb70: + br bb78 + +bb71: + %200 = builtin "cmp_eq_Int64"(%100 : $Builtin.Int64, %196 : $Builtin.Int64) : $Builtin.Int1 + cond_br %200, bb73, bb72 + +bb72: + br bb78 + +bb73: + %203 = builtin "cmp_eq_Int64"(%167 : $Builtin.Int64, %196 : $Builtin.Int64) : $Builtin.Int1 + cond_br %203, bb75, bb74 + +bb74: + br bb78 + +bb75: + %206 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %196 : $Builtin.Int64) : $Builtin.Int1 + cond_br %206, bb77, bb76 + +bb76: + br bb78 + +bb77: + %209 = struct_extract %5 : $Int64, #Int64._value + %210 = builtin "cmp_eq_Int64"(%209 : $Builtin.Int64, %196 : $Builtin.Int64) : $Builtin.Int1 + br bb79(%210 : $Builtin.Int1) + +bb78: + %212 = integer_literal $Builtin.Int1, 0 + br bb79(%212 : $Builtin.Int1) + + +bb79(%214 : $Builtin.Int1): + %215 = struct $Bool (%214 : $Builtin.Int1) + %217 = builtin "cmp_slt_Int64"(%44 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %217, bb80, bb81 + +bb80: + br bb88 + +bb81: + %220 = integer_literal $Builtin.Int64, 0 + %221 = builtin "cmp_eq_Int64"(%100 : $Builtin.Int64, %220 : $Builtin.Int64) : $Builtin.Int1 + cond_br %221, bb83, bb82 + +bb82: + br bb88 + +bb83: + %224 = builtin "cmp_eq_Int64"(%167 : $Builtin.Int64, %220 : $Builtin.Int64) : $Builtin.Int1 + cond_br %224, bb85, bb84 + +bb84: + br bb88 + +bb85: + %227 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %220 : $Builtin.Int64) : $Builtin.Int1 + cond_br %227, bb87, bb86 + +bb86: + br bb88 + +bb87: + %230 = struct_extract %5 : $Int64, #Int64._value + %231 = builtin "cmp_eq_Int64"(%230 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + %232 = integer_literal $Builtin.Int1, -1 + %233 = builtin "xor_Int1"(%231 : $Builtin.Int1, %232 : $Builtin.Int1) : $Builtin.Int1 + br bb89(%233 : $Builtin.Int1) + +bb88: + %235 = integer_literal $Builtin.Int1, 0 + br bb89(%235 : $Builtin.Int1) + + +bb89(%237 : $Builtin.Int1): + %238 = struct $Bool (%237 : $Builtin.Int1) + cond_br %217, bb98, bb90 + +bb90: + %241 = builtin "cmp_slt_Int64"(%100 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %241, bb92, bb91 + +bb91: + br bb96 + +bb92: + %244 = builtin "cmp_slt_Int64"(%167 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %244, bb94, bb93 + +bb93: + br bb96 + +bb94: + %247 = builtin "cmp_slt_Int64"(%48 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %247, bb97, bb95 + +bb95: + br bb96 + +bb96: + %250 = integer_literal $Builtin.Int1, -1 + br bb99(%250 : $Builtin.Int1) + +bb97: + %252 = struct_extract %5 : $Int64, #Int64._value + %253 = builtin "cmp_eq_Int64"(%252 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + br bb99(%253 : $Builtin.Int1) + +bb98: + %255 = integer_literal $Builtin.Int1, 0 + br bb99(%255 : $Builtin.Int1) + +bb99(%257 : $Builtin.Int1): + %258 = struct $Bool (%257 : $Builtin.Int1) + %260 = struct_extract %5 : $Int64, #Int64._value + %261 = builtin "cmp_eq_Int64"(%260 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %261, bb100, bb106 + +bb100: + %263 = builtin "cmp_slt_Int64"(%100 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %263, bb102, bb101 + +bb101: + br bb104 + +bb102: + %266 = builtin "cmp_slt_Int64"(%48 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %266, bb105, bb103 + +bb103: + br bb104 + +bb104: + %269 = integer_literal $Builtin.Int1, -1 + br bb107(%269 : $Builtin.Int1) + +bb105: + %271 = builtin "cmp_slt_Int64"(%167 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + %272 = integer_literal $Builtin.Int1, -1 + %273 = builtin "xor_Int1"(%271 : $Builtin.Int1, %272 : $Builtin.Int1) : $Builtin.Int1 + br bb107(%273 : $Builtin.Int1) + +bb106: + %275 = integer_literal $Builtin.Int1, 0 + br bb107(%275 : $Builtin.Int1) + + +bb107(%277 : $Builtin.Int1): + %278 = struct $Bool (%277 : $Builtin.Int1) + %280 = builtin "cmp_eq_Int64"(%260 : $Builtin.Int64, %12 : $Builtin.Int64) : $Builtin.Int1 + cond_br %280, bb108, bb109 + +bb108: + %282 = integer_literal $Builtin.Int1, -1 + %283 = builtin "xor_Int1"(%217 : $Builtin.Int1, %282 : $Builtin.Int1) : $Builtin.Int1 + br bb110(%283 : $Builtin.Int1) + +bb109: + %285 = integer_literal $Builtin.Int1, 0 + br bb110(%285 : $Builtin.Int1) + + +bb110(%287 : $Builtin.Int1): + %288 = struct $Bool (%287 : $Builtin.Int1) + %290 = builtin "cmp_eq_Int64"(%260 : $Builtin.Int64, %46 : $Builtin.Int64) : $Builtin.Int1 + cond_br %290, bb112, bb111 + +bb111: + br bb121 + +bb112: + %293 = builtin "cmp_eq_Int64"(%13 : $Builtin.Int64, %46 : $Builtin.Int64) : $Builtin.Int1 + cond_br %293, bb114, bb113 + +bb113: + br bb121 + +bb114: + %296 = integer_literal $Builtin.Int64, 0 + %297 = builtin "cmp_eq_Int64"(%44 : $Builtin.Int64, %296 : $Builtin.Int64) : $Builtin.Int1 + cond_br %297, bb116, bb115 + +bb115: + br bb121 + +bb116: + %300 = builtin "cmp_eq_Int64"(%100 : $Builtin.Int64, %296 : $Builtin.Int64) : $Builtin.Int1 + cond_br %300, bb118, bb117 + +bb117: + br bb121 + +bb118: + %303 = builtin "cmp_eq_Int64"(%167 : $Builtin.Int64, %296 : $Builtin.Int64) : $Builtin.Int1 + cond_br %303, bb120, bb119 + +bb119: + br bb121 + +bb120: + %306 = builtin "cmp_eq_Int64"(%48 : $Builtin.Int64, %296 : $Builtin.Int64) : $Builtin.Int1 + br bb122(%306 : $Builtin.Int1) + +bb121: + %308 = integer_literal $Builtin.Int1, 0 + br bb122(%308 : $Builtin.Int1) + + +bb122(%310 : $Builtin.Int1): + %311 = struct $Bool (%310 : $Builtin.Int1) + br bb148(undef : $Bool) + +bb123: + br bb148(undef : $Bool) + +bb124: + br bb148(undef : $Bool) + +bb125: + br bb148(undef : $Bool) + +bb126: + br bb148(undef : $Bool) + +bb127: + br bb129(undef : $Bool) + +bb128: + br bb129(undef : $Bool) + +bb129(%320 : $Bool): + br bb148(undef : $Bool) + +bb130: + br bb148(undef : $Bool) + +bb131: + br bb148(undef : $Bool) + +bb132: + br bb148(undef : $Bool) + +bb133: + br bb148(undef : $Bool) + +bb134: + br bb136(undef : $Bool) + +bb135: + br bb136(undef : $Bool) + +bb136(%328 : $Bool): + br bb148(undef : $Bool) + +bb137: + br bb148(undef : $Bool) + +bb138: + br bb148(undef : $Bool) + +bb139: + br bb148(undef : $Bool) + +bb140: + br bb148(undef : $Bool) + +bb141: + br bb143(undef : $Bool) + +bb142: + br bb143(undef : $Bool) + +bb143(%336 : $Bool): + br bb148(undef : $Bool) + +bb144: + br bb148(undef : $Bool) + +bb145: + br bb148(undef : $Bool) + +bb146: + br bb148(undef : $Bool) + +bb147: + br bb148(undef : $Bool) + +bb148(%342 : $Bool): + cond_br %47, bb150, bb149 + +bb149: + br bb153 + +bb150: + cond_br %280, bb152, bb151 + +bb151: + br bb153 + +bb152: + %347 = integer_literal $Builtin.Int64, 5 + %348 = struct $Int64 (%347 : $Builtin.Int64) + %349 = integer_literal $Builtin.Int64, 0 + %350 = struct $Int64 (%349 : $Builtin.Int64) + + %351 = function_ref @getit : $@convention(thin) (Int64, Int64) -> Int64 + %352 = apply %351(%348, %350) : $@convention(thin) (Int64, Int64) -> Int64 + br bb183(%352 : $Int64) + +bb153: + cond_br %47, bb154, bb155 + +bb154: + %355 = integer_literal $Builtin.Int64, -2 + %356 = struct $Int64 (%355 : $Builtin.Int64) + %357 = integer_literal $Builtin.Int64, -9 + %358 = struct $Int64 (%357 : $Builtin.Int64) + + %359 = function_ref @getit : $@convention(thin) (Int64, Int64) -> Int64 + %360 = apply %359(%356, %358) : $@convention(thin) (Int64, Int64) -> Int64 + br bb183(%360 : $Int64) + +bb155: + cond_br %14, bb157, bb156 + +bb156: + br bb160 + +bb157: + cond_br %45, bb159, bb158 + +bb158: + br bb160 + +bb159: + %366 = integer_literal $Builtin.Int64, 5 + %367 = struct $Int64 (%366 : $Builtin.Int64) + %368 = integer_literal $Builtin.Int64, -7 + %369 = struct $Int64 (%368 : $Builtin.Int64) + + %370 = function_ref @getit : $@convention(thin) (Int64, Int64) -> Int64 + %371 = apply %370(%367, %369) : $@convention(thin) (Int64, Int64) -> Int64 + br bb183(%371 : $Int64) + +bb160: + cond_br %30, bb162, bb161 + +bb161: + br bb165 + +bb162: + cond_br %45, bb164, bb163 + +bb163: + br bb165 + +bb164: + %377 = integer_literal $Builtin.Int64, -5 + %378 = struct $Int64 (%377 : $Builtin.Int64) + %379 = integer_literal $Builtin.Int64, -3 + %380 = struct $Int64 (%379 : $Builtin.Int64) + + %381 = function_ref @getit : $@convention(thin) (Int64, Int64) -> Int64 + %382 = apply %381(%378, %380) : $@convention(thin) (Int64, Int64) -> Int64 + br bb183(%382 : $Int64) + +bb165: + cond_br %30, bb167, bb166 + +bb166: + br bb170 + +bb167: + %386 = builtin "cmp_eq_Int64"(%44 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %386, bb169, bb168 + +bb168: + br bb170 + +bb169: + %389 = integer_literal $Builtin.Int64, -12 + %390 = struct $Int64 (%389 : $Builtin.Int64) + %391 = integer_literal $Builtin.Int64, -5 + %392 = struct $Int64 (%391 : $Builtin.Int64) + + %393 = function_ref @getit : $@convention(thin) (Int64, Int64) -> Int64 + %394 = apply %393(%390, %392) : $@convention(thin) (Int64, Int64) -> Int64 + br bb183(%394 : $Int64) + +bb170: + cond_br %14, bb172, bb171 + +bb171: + br bb175 + +bb172: + %398 = builtin "cmp_eq_Int64"(%44 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %398, bb174, bb173 + +bb173: + br bb175 + +bb174: + %401 = integer_literal $Builtin.Int64, -8 + %402 = struct $Int64 (%401 : $Builtin.Int64) + %403 = integer_literal $Builtin.Int64, -7 + %404 = struct $Int64 (%403 : $Builtin.Int64) + + %405 = function_ref @getit : $@convention(thin) (Int64, Int64) -> Int64 + %406 = apply %405(%402, %404) : $@convention(thin) (Int64, Int64) -> Int64 + br bb183(%406 : $Int64) + +bb175: + cond_br %30, bb177, bb176 + +bb176: + br bb182 + +bb177: + %410 = builtin "cmp_eq_Int64"(%44 : $Builtin.Int64, %29 : $Builtin.Int64) : $Builtin.Int1 + cond_br %410, bb179, bb178 + +bb178: + br bb182 + +bb179: + cond_br %280, bb181, bb180 + +bb180: + br bb182 + +bb181: + %415 = integer_literal $Builtin.Int64, -8 + %416 = struct $Int64 (%415 : $Builtin.Int64) + %417 = integer_literal $Builtin.Int64, -7 + %418 = struct $Int64 (%417 : $Builtin.Int64) + + %419 = function_ref @getit : $@convention(thin) (Int64, Int64) -> Int64 + %420 = apply %419(%416, %418) : $@convention(thin) (Int64, Int64) -> Int64 + br bb183(%420 : $Int64) + +bb182: + %422 = integer_literal $Builtin.Int64, 0 + %423 = struct $Int64 (%422 : $Builtin.Int64) + + %424 = function_ref @getit : $@convention(thin) (Int64, Int64) -> Int64 + %425 = apply %424(%423, %423) : $@convention(thin) (Int64, Int64) -> Int64 + br bb183(%425 : $Int64) + + +bb183(%427 : $Int64): + br bb190(undef : $Bool) + +bb184: + br bb190(undef : $Bool) + +bb185: + br bb190(undef : $Bool) + +bb186: + br bb190(undef : $Bool) + +bb187: + br bb190(undef : $Bool) + +bb188: + br bb190(undef : $Bool) + +bb189: + br bb190(undef : $Bool) + +bb190(%436 : $Bool): + %437 = integer_literal $Builtin.Int64, 150 + %438 = struct $Int64 (%437 : $Builtin.Int64) + + %439 = function_ref @sink : $@convention(thin) (Int64, Int64, Int64, Int64) -> () + %440 = apply %439(%427, %427, %438, %438) : $@convention(thin) (Int64, Int64, Int64, Int64) -> () + %441 = struct_extract %215 : $Bool, #Bool._value + cond_br %441, bb192, bb191 + +bb191: + br bb193 + +bb192: + + %444 = function_ref @do_something : $@convention(thin) () -> () + %445 = apply %444() : $@convention(thin) () -> () + br bb193 + +bb193: + %447 = struct_extract %238 : $Bool, #Bool._value + cond_br %447, bb195, bb194 + +bb194: + br bb196 + +bb195: + + %450 = function_ref @do_something : $@convention(thin) () -> () + %451 = apply %450() : $@convention(thin) () -> () + br bb196 + +bb196: + %453 = struct_extract %42 : $Bool, #Bool._value + cond_br %453, bb198, bb197 + +bb197: + br bb199 + +bb198: + + %456 = function_ref @do_something : $@convention(thin) () -> () + %457 = apply %456() : $@convention(thin) () -> () + br bb199 + +bb199: + %459 = struct_extract %258 : $Bool, #Bool._value + cond_br %459, bb201, bb200 + +bb200: + br bb202 + +bb201: + + %462 = function_ref @do_something : $@convention(thin) () -> () + %463 = apply %462() : $@convention(thin) () -> () + br bb202 + +bb202: + cond_br %45, bb204, bb203 + +bb203: + br bb205 + +bb204: + %467 = function_ref @do_something : $@convention(thin) () -> () + %468 = apply %467() : $@convention(thin) () -> () + br bb205 + +bb205: + cond_br %47, bb207, bb206 + +bb206: + br bb208 + +bb207: + %472 = function_ref @do_something : $@convention(thin) () -> () + %473 = apply %472() : $@convention(thin) () -> () + br bb208 + +bb208: + %475 = struct_extract %137 : $Bool, #Bool._value + cond_br %475, bb210, bb209 + +bb209: + br bb211 + +bb210: + %478 = function_ref @do_something : $@convention(thin) () -> () + %479 = apply %478() : $@convention(thin) () -> () + br bb211 + +bb211: + %481 = struct_extract %151 : $Bool, #Bool._value + cond_br %481, bb213, bb212 + +bb212: + br bb214 + +bb213: + %484 = function_ref @do_something : $@convention(thin) () -> () + %485 = apply %484() : $@convention(thin) () -> () + br bb214 + +bb214: + %487 = struct_extract %165 : $Bool, #Bool._value + cond_br %487, bb216, bb215 + +bb215: + br bb217 + +bb216: + %490 = function_ref @do_something : $@convention(thin) () -> () + %491 = apply %490() : $@convention(thin) () -> () + br bb217 + +bb217: + cond_br %168, bb219, bb218 + +bb218: + br bb220 + +bb219: + %495 = function_ref @do_something : $@convention(thin) () -> () + %496 = apply %495() : $@convention(thin) () -> () + br bb220 + +bb220: + cond_br %169, bb222, bb221 + +bb221: + br bb223 + +bb222: + %500 = function_ref @do_something : $@convention(thin) () -> () + %501 = apply %500() : $@convention(thin) () -> () + br bb223 + +bb223: + cond_br %170, bb225, bb224 + +bb224: + br bb226 + +bb225: + %505 = function_ref @do_something : $@convention(thin) () -> () + %506 = apply %505() : $@convention(thin) () -> () + br bb226 + +bb226: + %508 = struct_extract %64 : $Bool, #Bool._value + cond_br %508, bb228, bb227 + +bb227: + br bb229 + +bb228: + %511 = function_ref @do_something : $@convention(thin) () -> () + %512 = apply %511() : $@convention(thin) () -> () + br bb229 + +bb229: + %514 = struct_extract %81 : $Bool, #Bool._value + cond_br %514, bb231, bb230 + +bb230: + br bb232 + +bb231: + %517 = function_ref @do_something : $@convention(thin) () -> () + %518 = apply %517() : $@convention(thin) () -> () + br bb232 + +bb232: + %520 = struct_extract %98 : $Bool, #Bool._value + cond_br %520, bb234, bb233 + +bb233: + br bb235 + +bb234: + %523 = function_ref @do_something : $@convention(thin) () -> () + %524 = apply %523() : $@convention(thin) () -> () + br bb235 + +bb235: + %526 = struct_extract %107 : $Bool, #Bool._value + cond_br %526, bb237, bb236 + +bb236: + br bb238 + +bb237: + %529 = function_ref @do_something : $@convention(thin) () -> () + %530 = apply %529() : $@convention(thin) () -> () + br bb238 + +bb238: + %532 = struct_extract %115 : $Bool, #Bool._value + cond_br %532, bb240, bb239 + +bb239: + br bb241 + +bb240: + %535 = function_ref @do_something : $@convention(thin) () -> () + %536 = apply %535() : $@convention(thin) () -> () + br bb241 + +bb241: + %538 = struct_extract %123 : $Bool, #Bool._value + cond_br %538, bb243, bb242 + +bb242: + br bb244 + +bb243: + %541 = function_ref @do_something : $@convention(thin) () -> () + %542 = apply %541() : $@convention(thin) () -> () + br bb244 + +bb244: + %544 = struct_extract %192 : $Bool, #Bool._value + cond_br %544, bb246, bb245 + +bb245: + br bb247 + +bb246: + %547 = function_ref @do_something : $@convention(thin) () -> () + %548 = apply %547() : $@convention(thin) () -> () + br bb247 + +bb247: + %550 = struct_extract %27 : $Bool, #Bool._value + cond_br %550, bb249, bb248 + +bb248: + br bb250 + +bb249: + %553 = function_ref @do_something : $@convention(thin) () -> () + %554 = apply %553() : $@convention(thin) () -> () + br bb250 + +bb250: + %556 = struct_extract %278 : $Bool, #Bool._value + cond_br %556, bb252, bb251 + +bb251: + br bb253 + +bb252: + %559 = function_ref @do_something : $@convention(thin) () -> () + %560 = apply %559() : $@convention(thin) () -> () + br bb253 + +bb253: + %562 = struct_extract %288 : $Bool, #Bool._value + cond_br %562, bb255, bb254 + +bb254: + br bb256 + +bb255: + %565 = function_ref @do_something : $@convention(thin) () -> () + %566 = apply %565() : $@convention(thin) () -> () + br bb256 + +bb256: + %568 = struct_extract %311 : $Bool, #Bool._value + cond_br %568, bb258, bb257 + +bb257: + br bb259 + +bb258: + %571 = function_ref @do_something : $@convention(thin) () -> () + %572 = apply %571() : $@convention(thin) () -> () + br bb259 + +bb259: + %574 = tuple () + return %574 : $() +} diff --git a/test/SILOptimizer/simplify_cfg_trivial_jumpthread.sil b/test/SILOptimizer/simplify_cfg_trivial_jumpthread.sil new file mode 100644 index 0000000000000..f5f8a2bdf4978 --- /dev/null +++ b/test/SILOptimizer/simplify_cfg_trivial_jumpthread.sil @@ -0,0 +1,135 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg -enable-ossa-simplify-cfg | %FileCheck %s + +// Tests tryJumpThreading with OSSA for trivial branch and terminator +// arguments. This does not require -jumpthread-simplify-cfg, which +// is only for dominator-based jump-threading. + +sil_stage canonical + +import Builtin +import Swift + +internal enum Enum { + case one + case two +} + +enum BoolLike { case true_, false_ } + +// Test that SimplifyCFG::simplifyBlocks, tryJumpThreading does not +// perform unbounded loop peeling. +// +// rdar://73357726 ([SR-14068]: Compiling with optimisation runs indefinitely for grpc-swift) +// CHECK-LABEL: sil [ossa] @testInfinitePeeling : $@convention(method) (Builtin.Int64, Enum) -> () { +// +// There is only one switch_enum blocks, and it is no longer in a loop. +// CHECK: bb0(%0 : $Builtin.Int64, %1 : $Enum): +// CHECK: switch_enum %1 : $Enum, case #Enum.one!enumelt: bb4, case #Enum.two!enumelt: bb1 +// CHECK: bb1: +// CHECK: br bb5 +// CHECK: bb2: +// CHECK: br bb6 +// +// CHECK: bb3: +// CHECK: br bb5 +// +// This is the cond_br block after jump-threading. +// CHECK: bb4: +// CHECK: cond_br %{{.*}}, bb3, bb2 +// +// The original loop has been removed here. +// CHECK: bb5: +// CHECK: br bb6 +// +// CHECK: bb6: +// CHECK: return +// CHECK-LABEL: } // end sil function 'testInfinitePeeling' +sil [ossa] @testInfinitePeeling : $@convention(method) (Builtin.Int64, Enum) -> () { +bb0(%0 : $Builtin.Int64, %1 : $Enum): + %2 = integer_literal $Builtin.Int64, 99999999 + br bb1(%0 : $Builtin.Int64, %1 : $Enum) + +bb1(%4 : $Builtin.Int64, %5 : $Enum): + switch_enum %5 : $Enum, case #Enum.one!enumelt: bb4, default bb5 + +bb2(%7 : $Builtin.Int64, %8 : $Enum): + %9 = builtin "cmp_slt_Int64"(%2 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1 + cond_br %9, bb3, bb6 + +bb3: + br bb1(%7 : $Builtin.Int64, %8 : $Enum) + +bb4: + %12 = integer_literal $Builtin.Int64, 1 + %13 = integer_literal $Builtin.Int1, -1 + %14 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %12 : $Builtin.Int64, %13 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %15 = tuple_extract %14 : $(Builtin.Int64, Builtin.Int1), 0 + %16 = enum $Enum, #Enum.two!enumelt + br bb2(%15 : $Builtin.Int64, %16 : $Enum) + +bb5(%17 : $Enum): + br bb2(%2 : $Builtin.Int64, %17 : $Enum) + +bb6: + %19 = tuple () + return %19 : $() +} + +class C { + @_hasStorage @_hasInitialValue var field: Int { get set } + init() +} + +// func testThread2(a : Int32) -> Int32 { +// enum b = (a ? _true : _false) +// if b == _true { return 42 } else { return 17 } +// + +/// CHECK-LABEL: sil [ossa] @testThread2 +/// CHECK: bb0([[COND:%.*]] : {{.*}}): +/// CHECK: cond_br [[COND]], bb1, bb2 +/// CHECK: bb1: +/// CHECK: integer_literal $Builtin.Int64, 42 +/// CHeCK: br bb3 +/// CHECK: bb2: +/// CHECK: integer_literal $Builtin.Int64, 17 +/// CHECK: br bb3 +/// CHECK: bb3 +/// CHECK: return + +sil [ossa] @testThread2 : $@convention(thin) (Builtin.Int1) -> Int64 { +bb0(%0 : $Builtin.Int1): + %t = integer_literal $Builtin.Int1, 1 + %f = integer_literal $Builtin.Int1, 0 + cond_br %0, bb1, bb2 + +bb1: // Preds: bb0 + %4 = enum $BoolLike, #BoolLike.true_!enumelt // user: %5 + br bb3(%4 : $BoolLike) // id: %5 + +bb2: // Preds: bb0 + %8 = enum $BoolLike, #BoolLike.false_!enumelt // user: %9 + br bb3(%8 : $BoolLike) // id: %9 + +bb3(%6 : $BoolLike): // Preds: bb3 bb1 + %100 = select_enum %6 : $BoolLike, case #BoolLike.true_!enumelt: %t, case #BoolLike.false_!enumelt: %f : $Builtin.Int1 + br bb4 // id: %7 + +bb4: // Preds: bb2 + cond_br %100, bb5, bb6 // id: %10 + +bb5: // Preds: bb4 + %11 = metatype $@thin Int64.Type + %12 = integer_literal $Builtin.Int64, 42 // user: %13 + %13 = struct $Int64 (%12 : $Builtin.Int64) // user: %14 + br bb7(%13 : $Int64) // id: %14 + +bb6: // Preds: bb4 + %15 = metatype $@thin Int64.Type + %16 = integer_literal $Builtin.Int64, 17 // user: %17 + %17 = struct $Int64 (%16 : $Builtin.Int64) // user: %18 + br bb7(%17 : $Int64) // id: %18 + +bb7(%19 : $Int64): // Preds: bb6 bb5 + return %19 : $Int64 // id: %21 +} diff --git a/test/SILOptimizer/simplify_cfg_tryapply_ossa.sil b/test/SILOptimizer/simplify_cfg_tryapply.sil similarity index 99% rename from test/SILOptimizer/simplify_cfg_tryapply_ossa.sil rename to test/SILOptimizer/simplify_cfg_tryapply.sil index b36e3322f6d0c..3627829678d4e 100644 --- a/test/SILOptimizer/simplify_cfg_tryapply_ossa.sil +++ b/test/SILOptimizer/simplify_cfg_tryapply.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s // Declare this SIL to be canonical because some tests break raw SIL // conventions. e.g. address-type block args. -enforce-exclusivity=none is also diff --git a/test/SILOptimizer/simplify_cfg_unique_values.sil b/test/SILOptimizer/simplify_cfg_unique_values.sil index d13e3b8caa6d9..0f0102d5d6c9f 100644 --- a/test/SILOptimizer/simplify_cfg_unique_values.sil +++ b/test/SILOptimizer/simplify_cfg_unique_values.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s sil_stage canonical diff --git a/test/SILOptimizer/simplify_switch_enum_objc_ossa.sil b/test/SILOptimizer/simplify_switch_enum_objc_ossa.sil new file mode 100644 index 0000000000000..bf5830ce4fed4 --- /dev/null +++ b/test/SILOptimizer/simplify_switch_enum_objc_ossa.sil @@ -0,0 +1,457 @@ +// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s + +// Just make sure that we do not infinite loop when compiling without block merging. +// +// RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -simplify-cfg -simplify-cfg-simplify-unconditional-branches=0 + +// REQUIRES: objc_interop + +import Swift +import Foundation + +class Test : NSObject { + @objc var other : Test? { get set } +} + + +// CHECK-LABEL: sil hidden @one_call : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +// CHECK: bb0([[ARG0:%.*]] : $Optional, [[ARG1:%.*]] : $Optional): +// CHECK: switch_enum [[ARG0]] : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb3 +// CHECK: bb1([[ARG0_PAYLOAD:%.*]] : $Test): +// CHECK: [[ARG1_PAYLOAD:%.*]] = unchecked_ref_cast [[ARG1]] : $Optional to $Test +// CHECK: [[GETTER:%.*]] = objc_method [[ARG1_PAYLOAD]] : $Test, #Test.other!getter.foreign +// CHECK: [[RESULT:%.*]] = apply [[GETTER]]([[ARG1_PAYLOAD]]) +// CHECK: [[SETTER:%.*]] = objc_method [[ARG0_PAYLOAD]] : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () +// CHECK: apply [[SETTER]]([[RESULT]], [[ARG0_PAYLOAD]]) +// CHECK: br bb2 +// CHECK: bb2: +// CHECK: return +// CHECK: bb3: +// CHECK: br bb2 +// CHECK: } + +sil hidden @one_call : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +bb0(%0 : $Optional, %1 : $Optional): + retain_value %0 : $Optional + switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb6 + +bb1(%6 : $Test): + retain_value %1 : $Optional + switch_enum %1 : $Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5 + +bb2(%9 : $Test): + %10 = objc_method %9 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %11 = apply %10(%9) : $@convention(objc_method) (Test) -> @autoreleased Optional + strong_release %9 : $Test + br bb3(%11 : $Optional) + +bb3(%14 : $Optional): + %15 = objc_method %6 : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () + %16 = apply %15(%14, %6) : $@convention(objc_method) (Optional, Test) -> () + release_value %14 : $Optional + strong_release %6 : $Test + %19 = tuple () + br bb4 + +bb4: + %23 = tuple () + return %23 : $() + +bb5: + %25 = enum $Optional, #Optional.none!enumelt + br bb3(%25 : $Optional) + +bb6: + br bb4 +} + +// CHECK-LABEL: sil hidden @one_call2 : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +// CHECK: bb0([[ARG0:%.*]] : $Optional, [[ARG1:%.*]] : $Optional): +// CHECK: switch_enum [[ARG0]] : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb3 +// CHECK: bb1([[ARG0_PAYLOAD:%.*]] : $Test): +// CHECK: [[ARG1_PAYLOAD:%.*]] = unchecked_ref_cast [[ARG1]] : $Optional to $Test +// CHECK: [[GETTER:%.*]] = objc_method [[ARG1_PAYLOAD]] : $Test, #Test.other!getter.foreign +// CHECK: [[RESULT:%.*]] = apply [[GETTER]]([[ARG1_PAYLOAD]]) +// CHECK: [[SETTER:%.*]] = objc_method [[ARG0_PAYLOAD]] : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () +// CHECK: apply [[SETTER]]([[RESULT]], [[ARG0_PAYLOAD]]) +// CHECK: br bb2 +// CHECK: bb2: +// CHECK: return +// CHECK: bb3: +// CHECK: br bb2 +// CHECK: } + +sil hidden @one_call2 : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +bb0(%0 : $Optional, %1 : $Optional): + retain_value %0 : $Optional + switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb6 + +bb1(%6 : $Test): + retain_value %1 : $Optional + switch_enum %1 : $Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5 + +bb2: + %9 = unchecked_enum_data %1 : $Optional, #Optional.some!enumelt + %10 = objc_method %9 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %11 = apply %10(%9) : $@convention(objc_method) (Test) -> @autoreleased Optional + strong_release %9 : $Test + br bb3(%11 : $Optional) + +bb3(%14 : $Optional): + %15 = objc_method %6 : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () + %16 = apply %15(%14, %6) : $@convention(objc_method) (Optional, Test) -> () + release_value %14 : $Optional + strong_release %6 : $Test + %19 = tuple () + br bb4 + +bb4: + %23 = tuple () + return %23 : $() + +bb5: + %25 = enum $Optional, #Optional.none!enumelt + br bb3(%25 : $Optional) + +bb6: + br bb4 +} +sil @some_sideeffect : $@convention(thin) () -> () + +// CHECK-LABEL: sil hidden @sideeffect +// CHECK: switch_enum +// CHECK: switch_enum +// CHECK: } +sil hidden @sideeffect : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +bb0(%0 : $Optional, %1 : $Optional): + retain_value %0 : $Optional + switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb6 + +bb1(%6 : $Test): + retain_value %1 : $Optional + switch_enum %1 : $Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5 + +bb2(%9 : $Test): + %10 = objc_method %9 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %11 = apply %10(%9) : $@convention(objc_method) (Test) -> @autoreleased Optional + strong_release %9 : $Test + %12 = function_ref @some_sideeffect : $@convention(thin) () -> () + apply %12() : $@convention(thin) () -> () + br bb3(%11 : $Optional) + +bb3(%14 : $Optional): + %15 = objc_method %6 : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () + %16 = apply %15(%14, %6) : $@convention(objc_method) (Optional, Test) -> () + release_value %14 : $Optional + strong_release %6 : $Test + %19 = tuple () + br bb4 + +bb4: + %23 = tuple () + return %23 : $() + +bb5: + %25 = enum $Optional, #Optional.none!enumelt + br bb3(%25 : $Optional) + +bb6: + br bb4 +} + +// CHECK-LABEL: sil hidden @sideeffect2 +// CHECK: switch_enum +// CHECK: switch_enum +// CHECK: } +sil hidden @sideeffect2 : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +bb0(%0 : $Optional, %1 : $Optional): + retain_value %0 : $Optional + switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb6 + +bb1(%6 : $Test): + retain_value %1 : $Optional + switch_enum %1 : $Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5 + +bb2(%9 : $Test): + %10 = objc_method %9 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %11 = apply %10(%9) : $@convention(objc_method) (Test) -> @autoreleased Optional + strong_release %9 : $Test + br bb3(%11 : $Optional) + +bb3(%14 : $Optional): + %15 = objc_method %6 : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () + %16 = apply %15(%14, %6) : $@convention(objc_method) (Optional, Test) -> () + release_value %14 : $Optional + strong_release %6 : $Test + %19 = tuple () + br bb4 + +bb4: + %23 = tuple () + return %23 : $() + +bb5: + %12 = function_ref @some_sideeffect : $@convention(thin) () -> () + apply %12() : $@convention(thin) () -> () + %25 = enum $Optional, #Optional.none!enumelt + br bb3(%25 : $Optional) + +bb6: + br bb4 +} + +// CHECK-LABEL: sil hidden @not_on_optional +// CHECK: switch_enum +// CHECK: switch_enum +// CHECK: } +sil hidden @not_on_optional : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +bb0(%0 : $Optional, %1 : $Optional): + retain_value %0 : $Optional + switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb6 + +bb1(%6 : $Test): + retain_value %1 : $Optional + switch_enum %1 : $Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5 + +bb2(%9 : $Test): + %10 = objc_method %9 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %11 = apply %10(%6) : $@convention(objc_method) (Test) -> @autoreleased Optional + strong_release %9 : $Test + br bb3(%11 : $Optional) + +bb3(%14 : $Optional): + %15 = objc_method %6 : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () + %16 = apply %15(%14, %6) : $@convention(objc_method) (Optional, Test) -> () + release_value %14 : $Optional + strong_release %6 : $Test + %19 = tuple () + br bb4 + +bb4: + %23 = tuple () + return %23 : $() + +bb5: + %25 = enum $Optional, #Optional.none!enumelt + br bb3(%25 : $Optional) + +bb6: + br bb4 +} + +// CHECK-LABEL: sil hidden @forwards_wrong_value +// CHECK: switch_enum +// CHECK: switch_enum +// CHECK: } +sil hidden @forwards_wrong_value : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +bb0(%0 : $Optional, %1 : $Optional): + retain_value %0 : $Optional + switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb6 + +bb1(%6 : $Test): + retain_value %1 : $Optional + switch_enum %1 : $Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5 + +bb2(%9 : $Test): + %10 = objc_method %9 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %11 = apply %10(%9) : $@convention(objc_method) (Test) -> @autoreleased Optional + strong_release %9 : $Test + br bb3(%0 : $Optional) + +bb3(%14 : $Optional): + %15 = objc_method %6 : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () + %16 = apply %15(%14, %6) : $@convention(objc_method) (Optional, Test) -> () + release_value %14 : $Optional + strong_release %6 : $Test + %19 = tuple () + br bb4 + +bb4: + %23 = tuple () + return %23 : $() + +bb5: + %25 = enum $Optional, #Optional.none!enumelt + br bb3(%25 : $Optional) + +bb6: + br bb4 +} + +// CHECK-LABEL: sil hidden @forwards_wrong_value2 +// CHECK: switch_enum +// CHECK: switch_enum +// CHECK: } +sil hidden @forwards_wrong_value2 : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +bb0(%0 : $Optional, %1 : $Optional): + retain_value %0 : $Optional + switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb6 + +bb1(%6 : $Test): + retain_value %1 : $Optional + switch_enum %1 : $Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5 + +bb2(%9 : $Test): + %10 = objc_method %9 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %11 = apply %10(%9) : $@convention(objc_method) (Test) -> @autoreleased Optional + strong_release %9 : $Test + br bb3(%11 : $Optional) + +bb3(%14 : $Optional): + %15 = objc_method %6 : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () + %16 = apply %15(%14, %6) : $@convention(objc_method) (Optional, Test) -> () + release_value %14 : $Optional + strong_release %6 : $Test + %19 = tuple () + br bb4 + +bb4: + %23 = tuple () + return %23 : $() + +bb5: + br bb3(%0 : $Optional) + +bb6: + br bb4 +} + +// CHECK-LABEL: sil hidden @two_chained_calls +// CHECK: bb0([[ARG0:%.*]] : $Optional, [[ARG1:%.*]] : $Optional): +// CHECK: switch_enum [[ARG0]] +// CHECK: bb1([[ARG0_PAYLOAD:%.*]] : $Test): +// CHECK: [[PAYLOAD:%.*]] = unchecked_ref_cast [[ARG1]] +// CHECK: [[METH:%.*]] = objc_method [[PAYLOAD]] : $Test, #Test.other!getter.foreign +// CHECK: [[RESULT:%.*]] = apply [[METH]]([[PAYLOAD]]) +// CHECK: [[PAYLOAD2:%.*]] = unchecked_ref_cast [[RESULT]] +// CHECK: [[METH2:%.*]] = objc_method [[PAYLOAD2]] : $Test, #Test.other!getter.foreign +// CHECK: [[RESULT2:%.*]] = apply [[METH2]]([[PAYLOAD2]]) +// CHECK: [[SETTER:%.*]] = objc_method [[ARG0_PAYLOAD]] : $Test, #Test.other!setter.foreign +// CHECK: apply [[SETTER]]([[RESULT2]], [[ARG0_PAYLOAD]]) : $@convention(objc_method) (Optional, Test) -> () +// CHECK: br bb2 +// CHECK: bb2: +// CHECK: return +// CHECK: bb3: +// CHECK: br bb2 +// CHECK: } + +sil hidden @two_chained_calls : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () { +bb0(%0 : $Optional, %1 : $Optional): + retain_value %0 : $Optional + switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb9 + +bb1(%6 : $Test): + retain_value %1 : $Optional + switch_enum %1 : $Optional, case #Optional.some!enumelt: bb3, case #Optional.none!enumelt: bb2 + +bb2: + br bb8 + +bb3(%10 : $Test): + %11 = objc_method %10 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %12 = apply %11(%10) : $@convention(objc_method) (Test) -> @autoreleased Optional + switch_enum %12 : $Optional, case #Optional.none!enumelt: bb4, case #Optional.some!enumelt: bb5 + +bb4: + release_value %12 : $Optional + strong_release %10 : $Test + br bb8 + +bb5: + %17 = unchecked_enum_data %12 : $Optional, #Optional.some!enumelt + strong_retain %17 : $Test + release_value %12 : $Optional + strong_release %10 : $Test + %21 = objc_method %17 : $Test, #Test.other!getter.foreign : (Test) -> () -> Test?, $@convention(objc_method) (Test) -> @autoreleased Optional + %22 = apply %21(%17) : $@convention(objc_method) (Test) -> @autoreleased Optional + strong_release %17 : $Test + br bb6(%22 : $Optional) + +bb6(%25 : $Optional): + %26 = objc_method %6 : $Test, #Test.other!setter.foreign : (Test) -> (Test?) -> (), $@convention(objc_method) (Optional, Test) -> () + %27 = apply %26(%25, %6) : $@convention(objc_method) (Optional, Test) -> () + release_value %25 : $Optional + strong_release %6 : $Test + br bb7 + +bb7: + %31 = tuple () + return %31 : $() + +bb8: + %33 = enum $Optional, #Optional.none!enumelt + br bb6(%33 : $Optional) + +bb9: + br bb7 +} + +sil @infinite_loop_get_optional_nsobject : $@convention(thin) () -> @autoreleased Optional +sil @infinite_loop_get_superview : $@convention(thin) (NSObject) -> @autoreleased Optional + +// Just make sure we do not infinite loop here when looking for successors. +// CHECK-LABEL: sil @infinite_loop_1 : $@convention(thin) () -> @owned Optional { +// CHECK: } // end sil function 'infinite_loop_1' +sil @infinite_loop_1 : $@convention(thin) () -> @owned Optional { +bb0: + %13 = function_ref @infinite_loop_get_optional_nsobject : $@convention(thin) () -> @autoreleased Optional + %14 = apply %13() : $@convention(thin) () -> @autoreleased Optional + br bb1(%14 : $Optional) + +bb1(%16 : $Optional): + retain_value %16 : $Optional + switch_enum %16 : $Optional, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5 + +bb2(%19 : $NSObject): + release_value %16 : $Optional + %21 = enum $Optional, #Optional.none!enumelt + release_value %16 : $Optional + return %21 : $Optional + +bb3: + %24 = unchecked_enum_data %16 : $Optional, #Optional.some!enumelt + strong_retain %24 : $NSObject + %26 = function_ref @infinite_loop_get_superview : $@convention(thin) (NSObject) -> @autoreleased Optional + %27 = apply %26(%24) : $@convention(thin) (NSObject) -> @autoreleased Optional + strong_release %24 : $NSObject + release_value %16 : $Optional + br bb1(%27 : $Optional) + +bb4: + release_value %16 : $Optional + br bb1(%16 : $Optional) + +bb5: + release_value %16 : $Optional + switch_enum %16 : $Optional, case #Optional.none!enumelt: bb4, default bb3 +} + +// Just make sure that we do not infinite loop here. +// +// CHECK-LABEL: sil @infinite_loop_2 : $@convention(thin) () -> @owned Optional { +// CHECK: } // end sil function 'infinite_loop_2' +sil @infinite_loop_2 : $@convention(thin) () -> @owned Optional { +bb0: + %0 = function_ref @infinite_loop_get_optional_nsobject : $@convention(thin) () -> @autoreleased Optional + %1 = apply %0() : $@convention(thin) () -> @autoreleased Optional + br bb1(%1 : $Optional) + +bb1(%3 : $Optional): + switch_enum %3 : $Optional, case #Optional.some!enumelt: bb3, case #Optional.none!enumelt: bb2 + +bb2: + br bb5(%3 : $Optional) + +bb3(%7 : $NSObject): + br bb4(%3 : $Optional) + +bb4(%9 : $Optional): + %11 = enum $Optional, #Optional.none!enumelt + return %11 : $Optional + +bb5(%14 : $Optional): + br bb6 + +bb6: + br bb5(%14 : $Optional) +} diff --git a/test/SILOptimizer/specialize.sil b/test/SILOptimizer/specialize.sil index c4a1a051cc7c5..7880e2e09fb05 100644 --- a/test/SILOptimizer/specialize.sil +++ b/test/SILOptimizer/specialize.sil @@ -73,7 +73,7 @@ bb0: sil [noinline] @XXX_init : $@convention(thin) (@in T, @thin XXX.Type) -> @out XXX { bb0(%0 : $*XXX, %1 : $*T, %2 : $@thin XXX.Type): %3 = alloc_stack $XXX, var, name "sf" // users: %7, %11, %13 - debug_value_addr %1 : $*T, let, name "t" // id: %4 + debug_value %1 : $*T, let, name "t", expr op_deref // id: %4 %5 = alloc_stack $T // users: %6, %8, %9 copy_addr %1 to [initialization] %5 : $*T // id: %6 %7 = struct_element_addr %3 : $*XXX, #XXX.m_t // user: %8 @@ -89,7 +89,7 @@ bb0(%0 : $*XXX, %1 : $*T, %2 : $@thin XXX.Type): // specialize.XXX.foo (@inout specialize.XXX)(t : A) -> Swift.Int32 sil [noinline] @XXX_foo : $@convention(method) (@in T, @inout XXX) -> Int32 { bb0(%0 : $*T, %1 : $*XXX): - debug_value_addr %0 : $*T, let, name "t" // id: %2 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %2 %3 = alloc_stack $T // users: %4, %6, %7 copy_addr %0 to [initialization] %3 : $*T // id: %4 %5 = struct_element_addr %1 : $*XXX, #XXX.m_t // user: %6 @@ -190,7 +190,7 @@ bb0(%0 : $*T, %1 : $@callee_owned () -> @out T): // specialize.getGenericClosure (t : A) -> () -> A sil @getGenericClosure : $@convention(thin) (@in T) -> @owned @callee_owned () -> @out T { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "t" // id: %1 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %1 // function_ref specialize.(getGenericClosure (t : A) -> () -> A).(tmp #1) (())A %2 = function_ref @getGenericClosure_closure : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 // user: %5 %3 = alloc_box $<τ_0_0> { var τ_0_0 } // users: %4, %5, %5 @@ -334,7 +334,7 @@ bb0(%0 : $*C): // test4.boo (A) -> (Swift.Int32, B) -> Swift.Int32 sil hidden [noinline] @boo : $@convention(thin) (@in U) -> @owned @callee_owned (Int32, @in T) -> Int32 { bb0(%0 : $*U): - debug_value_addr %0 : $*U, let, name "y" // id: %1 + debug_value %0 : $*U, let, name "y", expr op_deref // id: %1 // function_ref test4.(boo (A) -> (Swift.Int32, B) -> Swift.Int32).(closure #1) %2 = function_ref @boo_closure : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (Int32, @in τ_0_1, @owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> Int32 // user: %5 %3 = alloc_box $<τ_0_0> { var τ_0_0 } // users: %4, %5, %5 @@ -350,7 +350,7 @@ sil shared [noinline] @boo_closure : $@convention(thin) (Int3 bb0(%0 : $Int32, %1 : $*T, %2 : $<τ_0_0> { var τ_0_0 } ): %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 debug_value %0 : $Int32, let, name "x" // id: %4 - debug_value_addr %1 : $*T, let, name "z" // id: %5 + debug_value %1 : $*T, let, name "z", expr op_deref // id: %5 %6 = witness_method $U, #P.get : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int32 // user: %7 %7 = apply %6(%3) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int32 // user: %8 %8 = struct_extract %7 : $Int32, #Int32._value // user: %11 @@ -383,8 +383,8 @@ bb0(%0 : $Int32, %1 : $Int32): // test4.foo (A, B) -> (Swift.Int32, Swift.Float) -> Swift.Int32 sil hidden [noinline] @foo : $@convention(thin) (@in T, @in U) -> @owned @callee_owned (Int32, Float) -> Int32 { bb0(%0 : $*T, %1 : $*U): - debug_value_addr %0 : $*T, let, name "x" // id: %2 - debug_value_addr %1 : $*U, let, name "y" // id: %3 + debug_value %0 : $*T, let, name "x", expr op_deref // id: %2 + debug_value %1 : $*U, let, name "y", expr op_deref // id: %3 // function_ref test4.boo (A) -> (Swift.Int32, B) -> Swift.Int32 %4 = function_ref @boo : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in τ_0_0) -> @owned @callee_owned (Int32, @in τ_0_1) -> Int32 // user: %7 %5 = alloc_stack $U // users: %6, %7, %10 @@ -412,7 +412,7 @@ bb0(%0 : $Int32, %1 : $Float, %2 : $@callee_owned (Int32, @in Float) -> Int32): // test4.gen1 (A) -> (Swift.Int32) -> Swift.Int32 sil hidden [noinline] @gen1 : $@convention(thin) (@in T) -> @owned @callee_owned (Int32) -> Int32 { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "x" // id: %1 + debug_value %0 : $*T, let, name "x", expr op_deref // id: %1 // function_ref test4.(gen1 (A) -> (Swift.Int32) -> Swift.Int32).(closure #1) %2 = function_ref @gen1_closure : $@convention(thin) <τ_0_0 where τ_0_0 : P> (Int32, @owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> Int32 // user: %5 %3 = alloc_box $<τ_0_0> { var τ_0_0 } // users: %4, %5, %5 diff --git a/test/SILOptimizer/specialize_opaque_result_types2.sil b/test/SILOptimizer/specialize_opaque_result_types2.sil index d304e6b3410b9..4ee89011af349 100644 --- a/test/SILOptimizer/specialize_opaque_result_types2.sil +++ b/test/SILOptimizer/specialize_opaque_result_types2.sil @@ -25,7 +25,7 @@ sil @getGenericClosure_closure : $@convention(thin) (@owned <τ_0_0> { var sil [noinline] @getGenericClosure : $@convention(thin) (@in T) -> @owned @callee_owned () -> @out T { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "t" // id: %1 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %1 %2 = function_ref @getGenericClosure_closure : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 // user: %5 %3 = alloc_box $<τ_0_0> { var τ_0_0 } // users: %4, %5, %5 %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 diff --git a/test/SILOptimizer/specialize_ossa.sil b/test/SILOptimizer/specialize_ossa.sil index fd08111f54602..a0faab80e48e9 100644 --- a/test/SILOptimizer/specialize_ossa.sil +++ b/test/SILOptimizer/specialize_ossa.sil @@ -58,7 +58,7 @@ struct XXX { sil [ossa] [noinline] @XXX_init : $@convention(thin) (@in T, @thin XXX.Type) -> @out XXX { bb0(%0 : $*XXX, %1 : $*T, %2 : $@thin XXX.Type): %3 = alloc_stack $XXX, var, name "sf" // users: %7, %11, %13 - debug_value_addr %1 : $*T, let, name "t" // id: %4 + debug_value %1 : $*T, let, name "t", expr op_deref // id: %4 %5 = alloc_stack $T // users: %6, %8, %9 copy_addr %1 to [initialization] %5 : $*T // id: %6 %7 = struct_element_addr %3 : $*XXX, #XXX.m_t // user: %8 @@ -74,7 +74,7 @@ bb0(%0 : $*XXX, %1 : $*T, %2 : $@thin XXX.Type): // specialize.XXX.foo (@inout specialize.XXX)(t : A) -> Swift.Int32 sil [ossa] [noinline] @XXX_foo : $@convention(method) (@in T, @inout XXX) -> Int32 { bb0(%0 : $*T, %1 : $*XXX): - debug_value_addr %0 : $*T, let, name "t" // id: %2 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %2 %3 = alloc_stack $T // users: %4, %6, %7 copy_addr %0 to [initialization] %3 : $*T // id: %4 %5 = struct_element_addr %1 : $*XXX, #XXX.m_t // user: %6 @@ -89,7 +89,7 @@ bb0(%0 : $*T, %1 : $*XXX): sil [ossa] [noinline] @XXX_init_guaranteed : $@convention(thin) (@in_guaranteed T, @thin XXX.Type) -> @out XXX { bb0(%0 : $*XXX, %1 : $*T, %2 : $@thin XXX.Type): %3 = alloc_stack $XXX, var, name "sf" // users: %7, %11, %13 - debug_value_addr %1 : $*T, let, name "t" // id: %4 + debug_value %1 : $*T, let, name "t", expr op_deref // id: %4 %5 = alloc_stack $T // users: %6, %8, %9 copy_addr %1 to [initialization] %5 : $*T // id: %6 %7 = struct_element_addr %3 : $*XXX, #XXX.m_t // user: %8 @@ -104,7 +104,7 @@ bb0(%0 : $*XXX, %1 : $*T, %2 : $@thin XXX.Type): // specialize.XXX.foo (@inout specialize.XXX)(t : A) -> Swift.Int32 sil [ossa] [noinline] @XXX_foo_guaranteed : $@convention(method) (@in_guaranteed T, @inout XXX) -> Int32 { bb0(%0 : $*T, %1 : $*XXX): - debug_value_addr %0 : $*T, let, name "t" // id: %2 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %2 %3 = alloc_stack $T // users: %4, %6, %7 copy_addr %0 to [initialization] %3 : $*T // id: %4 %5 = struct_element_addr %1 : $*XXX, #XXX.m_t // user: %6 @@ -251,7 +251,7 @@ bb0(%0 : $*T, %1 : @owned $@callee_owned () -> @out T): // specialize.getGenericClosure (t : A) -> () -> A sil [ossa] @getGenericClosure : $@convention(thin) (@in T) -> @owned @callee_owned () -> @out T { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "t" // id: %1 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %1 // function_ref specialize.(getGenericClosure (t : A) -> () -> A).(tmp #1) (())A %2 = function_ref @getGenericClosure_closure : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0 // user: %5 %3 = alloc_box $<τ_0_0> { var τ_0_0 } @@ -384,7 +384,7 @@ bb0(%0 : $*C): // test4.boo (A) -> (Swift.Int32, B) -> Swift.Int32 sil hidden [ossa] [noinline] @boo : $@convention(thin) (@in U) -> @owned @callee_owned (Int32, @in T) -> Int32 { bb0(%0 : $*U): - debug_value_addr %0 : $*U, let, name "y" // id: %1 + debug_value %0 : $*U, let, name "y", expr op_deref // id: %1 // function_ref test4.(boo (A) -> (Swift.Int32, B) -> Swift.Int32).(closure #1) %2 = function_ref @boo_closure : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (Int32, @in τ_0_1, @owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> Int32 // user: %5 %3 = alloc_box $<τ_0_0> { var τ_0_0 } // users: %4, %5, %5 @@ -400,7 +400,7 @@ sil shared [ossa] [noinline] @boo_closure : $@convention(thin) { var τ_0_0 } ): %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 debug_value %0 : $Int32, let, name "x" // id: %4 - debug_value_addr %1 : $*T, let, name "z" // id: %5 + debug_value %1 : $*T, let, name "z", expr op_deref // id: %5 %6 = witness_method $U, #P.get : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int32 // user: %7 %7 = apply %6(%3) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int32 // user: %8 %8 = struct_extract %7 : $Int32, #Int32._value // user: %11 @@ -433,8 +433,8 @@ bb0(%0 : $Int32, %1 : $Int32): // test4.foo (A, B) -> (Swift.Int32, Swift.Float) -> Swift.Int32 sil hidden [ossa] [noinline] @foo : $@convention(thin) (@in T, @in U) -> @owned @callee_owned (Int32, Float) -> Int32 { bb0(%0 : $*T, %1 : $*U): - debug_value_addr %0 : $*T, let, name "x" // id: %2 - debug_value_addr %1 : $*U, let, name "y" // id: %3 + debug_value %0 : $*T, let, name "x", expr op_deref // id: %2 + debug_value %1 : $*U, let, name "y", expr op_deref // id: %3 // function_ref test4.boo (A) -> (Swift.Int32, B) -> Swift.Int32 %4 = function_ref @boo : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : P> (@in τ_0_0) -> @owned @callee_owned (Int32, @in τ_0_1) -> Int32 // user: %7 %5 = alloc_stack $U // users: %6, %7, %10 @@ -462,7 +462,7 @@ bb0(%0 : $Int32, %1 : $Float, %2 : @owned $@callee_owned (Int32, @in Float) -> I // test4.gen1 (A) -> (Swift.Int32) -> Swift.Int32 sil hidden [ossa] [noinline] @gen1 : $@convention(thin) (@in T) -> @owned @callee_owned (Int32) -> Int32 { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "x" // id: %1 + debug_value %0 : $*T, let, name "x", expr op_deref // id: %1 // function_ref test4.(gen1 (A) -> (Swift.Int32) -> Swift.Int32).(closure #1) %2 = function_ref @gen1_closure : $@convention(thin) <τ_0_0 where τ_0_0 : P> (Int32, @owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> Int32 // user: %5 %3 = alloc_box $<τ_0_0> { var τ_0_0 } // users: %4, %5, %5 diff --git a/test/SILOptimizer/specialize_recursive_generics.sil b/test/SILOptimizer/specialize_recursive_generics.sil index 1e3ba41bfa3e6..c45e6dae134f6 100644 --- a/test/SILOptimizer/specialize_recursive_generics.sil +++ b/test/SILOptimizer/specialize_recursive_generics.sil @@ -14,7 +14,7 @@ import Swift // CHECK: return sil [noinline] @_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_ : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "t" // id: %1 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %1 // function_ref specialize_recursive_generics.recursive_generics (A) -> () %2 = function_ref @_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_ : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () // user: %5 %3 = alloc_stack $T // users: %4, %5, %6 @@ -39,8 +39,8 @@ bb0(%0 : $*T): sil [noinline] @_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_ : $@convention(thin) (@in T, @in U) -> () { bb0(%0 : $*T, %1 : $*U): - debug_value_addr %0 : $*T, let, name "t" // id: %2 - debug_value_addr %1 : $*U, let, name "u" // id: %3 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %2 + debug_value %1 : $*U, let, name "u", expr op_deref // id: %3 // function_ref specialize_recursive_generics.recursive_generics_with_different_substitutions (A, B) -> () %4 = function_ref @_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_ : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> () // user: %9 %5 = alloc_stack $U // users: %6, %9, %11 diff --git a/test/SILOptimizer/specialize_recursive_generics_ossa.sil b/test/SILOptimizer/specialize_recursive_generics_ossa.sil index e14d3b676fb7a..3d8b74f8f194c 100644 --- a/test/SILOptimizer/specialize_recursive_generics_ossa.sil +++ b/test/SILOptimizer/specialize_recursive_generics_ossa.sil @@ -14,7 +14,7 @@ import Swift // CHECK: return sil [noinline] [ossa] @_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_ : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): - debug_value_addr %0 : $*T, let, name "t" // id: %1 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %1 // function_ref specialize_recursive_generics.recursive_generics (A) -> () %2 = function_ref @_TF29specialize_recursive_generics18recursive_genericsU__FQ_T_ : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () // user: %5 %3 = alloc_stack $T // users: %4, %5, %6 @@ -39,8 +39,8 @@ bb0(%0 : $*T): sil [noinline] [ossa] @_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_ : $@convention(thin) (@in T, @in U) -> () { bb0(%0 : $*T, %1 : $*U): - debug_value_addr %0 : $*T, let, name "t" // id: %2 - debug_value_addr %1 : $*U, let, name "u" // id: %3 + debug_value %0 : $*T, let, name "t", expr op_deref // id: %2 + debug_value %1 : $*U, let, name "u", expr op_deref // id: %3 // function_ref specialize_recursive_generics.recursive_generics_with_different_substitutions (A, B) -> () %4 = function_ref @_TF29specialize_recursive_generics47recursive_generics_with_different_substitutionsU___FTQ_Q0__T_ : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> () // user: %9 %5 = alloc_stack $U // users: %6, %9, %11 diff --git a/test/SILOptimizer/specialize_unconditional_checked_cast.swift b/test/SILOptimizer/specialize_unconditional_checked_cast.swift index 996ede34e7d39..f19a713a28a6e 100644 --- a/test/SILOptimizer/specialize_unconditional_checked_cast.swift +++ b/test/SILOptimizer/specialize_unconditional_checked_cast.swift @@ -206,8 +206,8 @@ ConcreteToArchetypeConvertUInt8(t: b, t2: f) // x -> x where x is not a class. // CHECK-LABEL: sil shared [noinline] @$s37specialize_unconditional_checked_cast31ConcreteToArchetypeConvertUInt8{{[_0-9a-zA-Z]*}}3Not{{.*}}Tg5 : $@convention(thin) (NotUInt8, NotUInt8) -> NotUInt8 { // CHECK: bb0(%0 : $NotUInt8, %1 : $NotUInt8): -// CHECK-NEXT: debug_value %0 -// CHECK-NEXT: return %0 +// CHECK: debug_value %0 +// CHECK: return %0 // x -> y where x is not a class but y is a class. // CHECK-LABEL: sil shared [noinline] @$s37specialize_unconditional_checked_cast31ConcreteToArchetypeConvertUInt8{{[_0-9a-zA-Z]*}}FAA1CC_Tg5 : $@convention(thin) (NotUInt8, @guaranteed C) -> @owned C { @@ -260,7 +260,7 @@ ConcreteToArchetypeConvertC(t: c, t2: e) // x -> y where x and y are unrelated classes. // CHECK-LABEL: sil shared [noinline] @$s37specialize_unconditional_checked_cast27ConcreteToArchetypeConvertC{{[_0-9a-zA-Z]*}}FAA1EC_Tg5 : $@convention(thin) (@guaranteed C, @guaranteed E) -> @owned E { // CHECK: bb0(%0 : $C, %1 : $E): -// CHECK-NEXT: builtin "int_trap" +// CHECK: builtin "int_trap" // CHECK-NEXT: unreachable // CHECK-NEXT: } diff --git a/test/SILOptimizer/sr-5068.sil b/test/SILOptimizer/sr-5068.sil index 82fd0acb66c71..36981e82749b4 100644 --- a/test/SILOptimizer/sr-5068.sil +++ b/test/SILOptimizer/sr-5068.sil @@ -100,7 +100,7 @@ bb3: // Preds: bb1 %70 = alloc_stack $ReadonlyExternalFramework // users: %106, %99, %73, %107, %71 copy_addr %0 to [initialization] %70 : $*ReadonlyExternalFramework // id: %71 destroy_addr %0 : $*ReadonlyExternalFramework // id: %72 - debug_value_addr %70 : $*ReadonlyExternalFramework, let, name "ExternalFramework", argno 1 // id: %73 + debug_value %70 : $*ReadonlyExternalFramework, let, name "ExternalFramework", argno 1, expr op_deref // id: %73 %75 = integer_literal $Builtin.Word, 1 // user: %78 %76 = integer_literal $Builtin.Int64, 1 // user: %77 %77 = struct $Int (%76 : $Builtin.Int64) // user: %81 diff --git a/test/SILOptimizer/sroa_ossa.sil b/test/SILOptimizer/sroa_ossa.sil index 92080d24ad89c..a335eac4bbc6e 100644 --- a/test/SILOptimizer/sroa_ossa.sil +++ b/test/SILOptimizer/sroa_ossa.sil @@ -526,3 +526,63 @@ bb0(%0 : @owned $NotTrivial, %1 : @owned $NotTrivial): %6 = tuple () return %6 : $() } + +class Klass { +} + +struct NonTrivialStruct { + var x : Klass + var y : Klass + var z : Klass +} + +sil @get_klasstriple : $@convention(thin) () -> (@owned Klass, @owned Klass, @owned Klass) + +// CHECK-LABEL: sil [ossa] @test_dynamiclifetime : +// CHECK: alloc_stack [dynamic_lifetime] $Klass +// CHECK: alloc_stack [dynamic_lifetime] $Klass +// CHECK: alloc_stack [dynamic_lifetime] $Klass +// CHECK-LABEL: } // end sil function 'test_dynamiclifetime' +sil [ossa] @test_dynamiclifetime : $@convention(thin) () -> () { +bb0: + %2 = alloc_stack $Builtin.Int1 + %3 = alloc_stack [dynamic_lifetime] $NonTrivialStruct + %4 = integer_literal $Builtin.Int1, 0 + store %4 to [trivial] %2 : $*Builtin.Int1 + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + %func = function_ref @get_klasstriple : $@convention(thin) () -> (@owned Klass, @owned Klass, @owned Klass) + %r = apply %func() : $@convention(thin) () -> (@owned Klass, @owned Klass, @owned Klass) + (%v1, %v2, %v3) = destructure_tuple %r : $(Klass, Klass, Klass) + %27 = integer_literal $Builtin.Int1, -1 + store %27 to [trivial] %2 : $*Builtin.Int1 + %f1 = struct_element_addr %3 : $*NonTrivialStruct, #NonTrivialStruct.x + store %v1 to [init] %f1 : $*Klass + %f2 = struct_element_addr %3 : $*NonTrivialStruct, #NonTrivialStruct.y + store %v2 to [init] %f2 : $*Klass + %f3 = struct_element_addr %3 : $*NonTrivialStruct, #NonTrivialStruct.z + store %v3 to [init] %f3 : $*Klass + br bb3 + +bb3: + %32 = load [trivial] %2 : $*Builtin.Int1 + cond_br %32, bb4, bb5 + +bb4: + %34 = load [take] %3 : $*NonTrivialStruct + destroy_value %34 : $NonTrivialStruct + br bb6 + +bb5: + br bb6 + +bb6: + dealloc_stack %3 : $*NonTrivialStruct + dealloc_stack %2 : $*Builtin.Int1 + %res = tuple () + return %res : $() +} diff --git a/test/SILOptimizer/string_optimization.sil b/test/SILOptimizer/string_optimization.sil index 05b269ea395ad..9357799475ed8 100644 --- a/test/SILOptimizer/string_optimization.sil +++ b/test/SILOptimizer/string_optimization.sil @@ -245,8 +245,10 @@ bb0: %6 = metatype $@thin String.Type %7 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String %8 = apply %7(%3, %4, %5, %6) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %d = struct $DefaultStringInterpolation (%8 : $String) + %e = struct_extract %d : $DefaultStringInterpolation, #DefaultStringInterpolation._storage %9 = function_ref @string_getUTF8CString : $@convention(method) (@guaranteed String) -> @owned ContiguousArray - %10 = apply %9(%8) : $@convention(method) (@guaranteed String) -> @owned ContiguousArray + %10 = apply %9(%e) : $@convention(method) (@guaranteed String) -> @owned ContiguousArray %11 = struct_extract %10 : $ContiguousArray, #ContiguousArray._buffer %12 = struct_extract %11 : $_ContiguousArrayBuffer, #_ContiguousArrayBuffer._storage %13 = ref_tail_addr %12 : $__ContiguousArrayStorageBase, $Int8 @@ -254,3 +256,34 @@ bb0: return %14 : $Builtin.RawPointer } +// CHECK-LABEL: sil @test_interpolated_cstring +// CHECK: apply +// CHECK: [[S:%[0-9]+]] = string_literal utf8 "a" +// CHECK: return [[S]] +// CHECK: } // end sil function 'test_interpolated_cstring' +sil @test_interpolated_cstring : $@convention(thin) () -> Builtin.RawPointer { +bb0: + %3 = string_literal utf8 "a" + %4 = integer_literal $Builtin.Word, 1 + %5 = integer_literal $Builtin.Int1, -1 + %6 = metatype $@thin String.Type + %7 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %8 = apply %7(%3, %4, %5, %6) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %d = struct $DefaultStringInterpolation (%8 : $String) + %f = function_ref @$sSS19stringInterpolationSSs013DefaultStringB0V_tcfC : $@convention(method) (@owned DefaultStringInterpolation, @thin String.Type) -> @owned String + %a = apply %f(%d, %6) : $@convention(method) (@owned DefaultStringInterpolation, @thin String.Type) -> @owned String + %9 = function_ref @string_getUTF8CString : $@convention(method) (@guaranteed String) -> @owned ContiguousArray + %10 = apply %9(%a) : $@convention(method) (@guaranteed String) -> @owned ContiguousArray + %11 = struct_extract %10 : $ContiguousArray, #ContiguousArray._buffer + %12 = struct_extract %11 : $_ContiguousArrayBuffer, #_ContiguousArrayBuffer._storage + %13 = ref_tail_addr %12 : $__ContiguousArrayStorageBase, $Int8 + %14 = address_to_pointer %13 : $*Int8 to $Builtin.RawPointer + return %14 : $Builtin.RawPointer +} + +sil public_external [readonly] @$sSS19stringInterpolationSSs013DefaultStringB0V_tcfC : $@convention(method) (@owned DefaultStringInterpolation, @thin String.Type) -> @owned String { +bb0(%0 : $DefaultStringInterpolation, %1 : $@thin String.Type): + %2 = struct_extract %0 : $DefaultStringInterpolation, #DefaultStringInterpolation._storage + return %2 : $String +} + diff --git a/test/SILOptimizer/strip_debug_info.sil b/test/SILOptimizer/strip_debug_info.sil index 351d37f79669e..22371c1a8e6f7 100644 --- a/test/SILOptimizer/strip_debug_info.sil +++ b/test/SILOptimizer/strip_debug_info.sil @@ -11,7 +11,7 @@ import Builtin // CHECK-NEXT: return sil @test : $@convention(thin) (@inout T, Builtin.Int64) -> () { bb0(%0 : $*T, %1 : $Builtin.Int64): - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref debug_value %1 : $Builtin.Int64 %2 = tuple () return %2 : $() diff --git a/test/SILOptimizer/temp_rvalue_opt_ossa.sil b/test/SILOptimizer/temp_rvalue_opt_ossa.sil index 64ef98b3370a2..0a76accfbf81d 100644 --- a/test/SILOptimizer/temp_rvalue_opt_ossa.sil +++ b/test/SILOptimizer/temp_rvalue_opt_ossa.sil @@ -1386,3 +1386,27 @@ bb0(%0 : @owned $Klass): return %105 : $Klass } +// CHECK-LABEL: sil [ossa] @test_yield +// CHECK: [[TA:%[0-9]+]] = ref_tail_addr +// CHECK-NOT: copy_addr +// CHECK: yield [[TA]] +// CHECK: } // end sil function 'test_yield' +sil [ossa] @test_yield : $@yield_once @convention(thin) (@guaranteed Klass) -> @yields @in_guaranteed Two { +bb0(%0 : @guaranteed $Klass): + %1 = alloc_stack $Two + %2 = ref_tail_addr [immutable] %0 : $Klass, $Two + copy_addr %2 to [initialization] %1 : $*Two + yield %1 : $*Two, resume bb1, unwind bb2 + +bb1: + destroy_addr %1 : $*Two + dealloc_stack %1 : $*Two + %90 = tuple () + return %90 : $() + +bb2: + destroy_addr %1 : $*Two + dealloc_stack %1 : $*Two + unwind +} + diff --git a/test/SILOptimizer/templvalueopt_ossa.sil b/test/SILOptimizer/templvalueopt_ossa.sil index e8c7c56c817f4..3f4ef3f48bb2c 100644 --- a/test/SILOptimizer/templvalueopt_ossa.sil +++ b/test/SILOptimizer/templvalueopt_ossa.sil @@ -285,14 +285,14 @@ bb0(%0 :@owned $Child): // CHECK-LABEL: sil [ossa] @cleanup_debuginsts : // CHECK: copy_addr [take] -// CHECK-NOT: debug_value_addr +// CHECK-NOT: debug_value {{.*}} expr op_deref // CHECK-LABEL: } // end sil function 'cleanup_debuginsts' sil [ossa] @cleanup_debuginsts : $@convention(thin) (@in T) -> () { bb0(%0 : $*T): %2 = alloc_stack $T - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref copy_addr %0 to [initialization] %2 : $*T - debug_value_addr %0 : $*T + debug_value %0 : $*T, expr op_deref destroy_addr %0 : $*T destroy_addr %2 : $*T dealloc_stack %2 : $*T diff --git a/test/SPI/experimental_spi_imports_swiftinterface.swift b/test/SPI/experimental_spi_imports_swiftinterface.swift index 35d715424d387..e571b394daacc 100644 --- a/test/SPI/experimental_spi_imports_swiftinterface.swift +++ b/test/SPI/experimental_spi_imports_swiftinterface.swift @@ -29,5 +29,5 @@ extension IOIPublicStruct { public func foo() {} } -// CHECK-PUBLIC-NOT: IOIPublicStruct -// CHECK-PRIVATE: @_spi{{.*}} extension IOIPublicStruct +// CHECK-PUBLIC-NOT: ExperimentalImported.IOIPublicStruct +// CHECK-PRIVATE: @_spi{{.*}} extension ExperimentalImported.IOIPublicStruct diff --git a/test/SPI/private_swiftinterface.swift b/test/SPI/private_swiftinterface.swift index 019f7e58e9f85..8415510788096 100644 --- a/test/SPI/private_swiftinterface.swift +++ b/test/SPI/private_swiftinterface.swift @@ -73,8 +73,8 @@ public func foo() {} } @_spi(MySPI) public extension SPIClassLocal { -// CHECK-PRIVATE: @_spi(MySPI) extension SPIClassLocal -// CHECK-PUBLIC-NOT: extension SPIClassLocal +// CHECK-PRIVATE: @_spi(MySPI) extension {{.+}}.SPIClassLocal +// CHECK-PUBLIC-NOT: extension {{.+}}.SPIClassLocal @_spi(MySPI) func extensionMethod() {} // CHECK-PRIVATE: @_spi(MySPI) public func extensionMethod @@ -110,8 +110,8 @@ private class PrivateClassLocal {} // CHECK-PUBLIC-NOT: useOfSPITypeOk @_spi(LocalSPI) extension SPIClass { - // CHECK-PRIVATE: @_spi(LocalSPI) extension SPIClass - // CHECK-PUBLIC-NOT: SPIClass + // CHECK-PRIVATE: @_spi(LocalSPI) extension SPIHelper.SPIClass + // CHECK-PUBLIC-NOT: SPIHelper.SPIClass @_spi(LocalSPI) public func extensionSPIMethod() {} // CHECK-PRIVATE: @_spi(LocalSPI) public func extensionSPIMethod() @@ -182,7 +182,7 @@ private protocol PrivateConstraint {} @_spi(LocalSPI) extension PublicType: SPIProto2 where T: SPIProto2 {} -// CHECK-PRIVATE: extension PublicType : {{.*}}.SPIProto2 where T : {{.*}}.SPIProto2 +// CHECK-PRIVATE: extension {{.*}}.PublicType : {{.*}}.SPIProto2 where T : {{.*}}.SPIProto2 // CHECK-PUBLIC-NOT: _ConstraintThatIsNotPartOfTheAPIOfThisLibrary public protocol LocalPublicProto {} diff --git a/test/Sanitizers/asan/asan.swift b/test/Sanitizers/asan/asan.swift index 4e238fb63bbe8..2c8a67bd5c2f5 100644 --- a/test/Sanitizers/asan/asan.swift +++ b/test/Sanitizers/asan/asan.swift @@ -12,9 +12,6 @@ // REQUIRES: executable_test // REQUIRES: asan_runtime -// rdar://problem/47367694 tracks re-enabling this test for backward deployment. -// UNSUPPORTED: remote_run - // Make sure we can handle swifterror. LLVM's address sanitizer pass needs to // ignore swifterror addresses. diff --git a/test/Sanitizers/asan/recover.swift b/test/Sanitizers/asan/recover.swift index b37530705969e..ee879e0f7dee5 100644 --- a/test/Sanitizers/asan/recover.swift +++ b/test/Sanitizers/asan/recover.swift @@ -2,7 +2,7 @@ // REQUIRES: asan_runtime // UNSUPPORTED: windows -// Check with recovery instrumentation and runtime option to continue execution. +// Check with recovery instrumentation and the runtime option to continue execution. // RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -sanitize-recover=address -import-objc-header %S/asan_interface.h -emit-ir -o %t.asan_recover.ll // RUN: %FileCheck -check-prefix=CHECK-IR -input-file=%t.asan_recover.ll %s // RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -sanitize-recover=address -import-objc-header %S/asan_interface.h -o %t_asan_recover @@ -29,7 +29,7 @@ // CHECK-IR-NOT: call {{.+}} @memcpy // FIXME: We need this so we can flush stdout but this won't -// work on other Platforms (e.g. Windows). +// work on other Platforms (e.g. Microsoft Windows). #if canImport(Glibc) import Glibc #else @@ -66,7 +66,7 @@ __asan_poison_memory_region(UnsafeMutableRawPointer(x), size) // print(x) // ``` // -// However, this generated code that called into memcpy rather than performing +// However, this generated code that's called into memcpy rather than performing // a direct read which meant that ASan caught an issue via its interceptors // rather than from instrumentation, which does not test the right thing here. // @@ -88,7 +88,7 @@ fflush(stdout) // CHECK-RECOVER-STDOUT: Read first element:0 // Second error -// NOTE: Very loose regex is to accomodate if name demangling +// NOTE: Very loose regex is to accommodate if name demangling // fails. rdar://problem/57235673 // CHECK-RECOVER-STDERR: AddressSanitizer: use-after-poison // CHECK-RECOVER-STDERR: #0 0x{{.+}} in {{.*}}foo{{.*}} diff --git a/test/Sanitizers/symbolication.swift b/test/Sanitizers/symbolication.swift index a7930b2e12def..4b2949e973240 100644 --- a/test/Sanitizers/symbolication.swift +++ b/test/Sanitizers/symbolication.swift @@ -5,6 +5,10 @@ // REQUIRES: asan_runtime // REQUIRES: VENDOR=apple +// rdar://80274830 ([Swift CI] Sanitizer report symbolication fails because we fail to start atos, sanbox issue?) +// REQUIRES: 80274830 +// Might be related/same issue as below + // rdar://75365575 (Failing to start atos external symbolizer) // UNSUPPORTED: OS=watchos diff --git a/test/Sanitizers/tsan/actor_counters.swift b/test/Sanitizers/tsan/actor_counters.swift index 354de842ce834..7304648a7bb42 100644 --- a/test/Sanitizers/tsan/actor_counters.swift +++ b/test/Sanitizers/tsan/actor_counters.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library -sanitize=thread) +// RUN: %target-run-simple-swift( %import-libdispatch -parse-as-library -sanitize=thread) // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,13 +8,7 @@ // UNSUPPORTED: linux // UNSUPPORTED: windows -#if canImport(Darwin) -import Darwin -#elseif canImport(Glibc) -import Glibc -#endif - -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Counter { private var value = 0 private let scratchBuffer: UnsafeMutableBufferPointer @@ -43,7 +37,7 @@ actor Counter { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func worker(identity: Int, counters: [Counter], numIterations: Int) async { for i in 0.. Int { var first = 0 var second = 1 @@ -26,7 +26,7 @@ func fib(_ n: Int) -> Int { return first } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func asyncFib(_ n: Int) async -> Int { if n == 0 || n == 1 { return n @@ -46,7 +46,7 @@ func asyncFib(_ n: Int) async -> Int { return result } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func runFibonacci(_ n: Int) async { let result = await asyncFib(n) @@ -55,7 +55,7 @@ func runFibonacci(_ n: Int) async { assert(result == fib(n)) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await runFibonacci(10) diff --git a/test/Sanitizers/tsan/async_taskgroup_next.swift b/test/Sanitizers/tsan/async_taskgroup_next.swift index d7071025483e5..8690ccb20693c 100644 --- a/test/Sanitizers/tsan/async_taskgroup_next.swift +++ b/test/Sanitizers/tsan/async_taskgroup_next.swift @@ -1,4 +1,7 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library -sanitize=thread) +// RUN: %target-run-simple-swift( %import-libdispatch -parse-as-library -sanitize=thread) + +// Segfaulted in CI on TSan bot. rdar://78264164 +// REQUIRES: rdar78264164 // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,7 +11,7 @@ var scratchBuffer: UnsafeMutableBufferPointer = .allocate(capacity: 1000) -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func completeFastOrSlow(n: Int) async -> Int { if n % 2 == 0 { await Task.sleep(2_000_000_000) @@ -18,7 +21,7 @@ func completeFastOrSlow(n: Int) async -> Int { return n } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func test_sum_nextOnCompletedOrPending() async { scratchBuffer.initialize(repeating: 0) @@ -54,7 +57,7 @@ func test_sum_nextOnCompletedOrPending() async { assert(sum == expected, "Expected: \(expected), got: \(sum)") } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await test_sum_nextOnCompletedOrPending() diff --git a/test/Sanitizers/tsan/basic_future.swift b/test/Sanitizers/tsan/basic_future.swift index bcaab52c246b9..3dcba91bee908 100644 --- a/test/Sanitizers/tsan/basic_future.swift +++ b/test/Sanitizers/tsan/basic_future.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library -sanitize=thread) +// RUN: %target-run-simple-swift( %import-libdispatch -parse-as-library -sanitize=thread) // REQUIRES: executable_test // REQUIRES: concurrency @@ -18,12 +18,12 @@ enum HomeworkError: Error, Equatable { case dogAteIt(String) } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func formGreeting(name: String) async -> String { return "Hello \(name) from async world" } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func testSimple( name: String, dogName: String, shouldThrow: Bool, doSuspend: Bool ) async { @@ -76,7 +76,7 @@ func testSimple( } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) @main struct Main { static func main() async { await testSimple(name: "Ted", dogName: "Hazel", shouldThrow: false, doSuspend: false) diff --git a/test/Sanitizers/tsan/mainactor.swift b/test/Sanitizers/tsan/mainactor.swift index a52ee98b57087..4eee14fed4006 100644 --- a/test/Sanitizers/tsan/mainactor.swift +++ b/test/Sanitizers/tsan/mainactor.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -enable-experimental-concurrency %import-libdispatch -sanitize=thread) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking %import-libdispatch -sanitize=thread) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency diff --git a/test/Sanitizers/tsan/objc_async.swift b/test/Sanitizers/tsan/objc_async.swift index c86b81a9615ed..50ee8ce60b5b4 100644 --- a/test/Sanitizers/tsan/objc_async.swift +++ b/test/Sanitizers/tsan/objc_async.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-clang -fsanitize=thread %S/Inputs/objc_async.m -c -o %t/objc_async_objc.o -// RUN: %target-build-swift -sanitize=thread -O -Xfrontend -enable-experimental-concurrency -parse-as-library -module-name main -import-objc-header %S/Inputs/objc_async.h %s %t/objc_async_objc.o -o %t/objc_async +// RUN: %target-build-swift -sanitize=thread -O -Xfrontend -disable-availability-checking -parse-as-library -module-name main -import-objc-header %S/Inputs/objc_async.h %s %t/objc_async_objc.o -o %t/objc_async // RUN: %target-run %t/objc_async | %FileCheck %s // REQUIRES: executable_test diff --git a/test/Sanitizers/tsan/racy_actor_counters.swift b/test/Sanitizers/tsan/racy_actor_counters.swift index 81c4e6626202b..0512fcc5eab05 100644 --- a/test/Sanitizers/tsan/racy_actor_counters.swift +++ b/test/Sanitizers/tsan/racy_actor_counters.swift @@ -1,4 +1,4 @@ -// RUN: %target-swiftc_driver %s -Xfrontend -enable-experimental-concurrency -parse-as-library %import-libdispatch -target %sanitizers-target-triple -g -sanitize=thread -o %t +// RUN: %target-swiftc_driver %s -parse-as-library %import-libdispatch -target %sanitizers-target-triple -g -sanitize=thread -o %t // RUN: %target-codesign %t // RUN: env %env-TSAN_OPTIONS="abort_on_error=0" not %target-run %t 2>&1 | %swift-demangle --simplified | %FileCheck %s @@ -17,7 +17,7 @@ var globalCounterValue = 0 -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Counter { func next() -> Int { let current = globalCounterValue @@ -26,7 +26,7 @@ actor Counter { } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func worker(identity: Int, counters: [Counter], numIterations: Int) async { for _ in 0..&1 | %swift-demangle --simplified | %FileCheck %s @@ -10,6 +10,10 @@ // rdar://76038845 // UNSUPPORTED: use_os_stdlib +// rdar://80274830 ([Swift CI] Sanitizer report symbolication fails because we fail to start atos, sanbox issue?) +// REQUIRES: 80274830 +// Might be related/same issue as below + // rdar://75365575 (Failing to start atos external symbolizer) // UNSUPPORTED: OS=watchos diff --git a/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/arm64-apple-macos.swiftinterface b/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/arm64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..2f914831b845d --- /dev/null +++ b/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/arm64-apple-macos.swiftinterface @@ -0,0 +1,5 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name XWithTarget -target arm64-apple-macosx10.9 +import Swift +@_exported import X +public func overlayFuncX() { } diff --git a/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/arm64e-apple-macos.swiftinterface b/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/arm64e-apple-macos.swiftinterface new file mode 100644 index 0000000000000..ea10233aa4cd7 --- /dev/null +++ b/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/arm64e-apple-macos.swiftinterface @@ -0,0 +1,5 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name XWithTarget -target arm64e-apple-macosx10.9 +import Swift +@_exported import X +public func overlayFuncX() { } diff --git a/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/x86_64-apple-macos.swiftinterface b/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/x86_64-apple-macos.swiftinterface new file mode 100644 index 0000000000000..dbffe1bbdeb2f --- /dev/null +++ b/test/ScanDependencies/Inputs/Swift/XWithTarget.swiftmodule/x86_64-apple-macos.swiftinterface @@ -0,0 +1,5 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name XWithTarget -target x86_64-apple-macosx10.9 +import Swift +@_exported import X +public func overlayFuncX() { } diff --git a/test/ScanDependencies/Inputs/SwiftDifferent/A.swiftinterface b/test/ScanDependencies/Inputs/SwiftDifferent/A.swiftinterface new file mode 100644 index 0000000000000..a5d04bec9041c --- /dev/null +++ b/test/ScanDependencies/Inputs/SwiftDifferent/A.swiftinterface @@ -0,0 +1,5 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name A +import Swift + +public func FuncAA() { } diff --git a/test/ScanDependencies/bin_mod_import.swift b/test/ScanDependencies/bin_mod_import.swift index 360a8c2309c1c..ef03f2f9562db 100644 --- a/test/ScanDependencies/bin_mod_import.swift +++ b/test/ScanDependencies/bin_mod_import.swift @@ -8,7 +8,10 @@ import EWrapper // RUN: %target-swift-frontend -compile-module-from-interface %S/Inputs/Swift/EWrapper.swiftinterface -o %t/EWrapper.swiftmodule -I %t // Step 3: scan dependency should give us the binary module and a textual swift dependency from it // RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t -// Step 4: Verify +// RUN: %FileCheck %s < %t/deps.json + +// Step 4: Ensure that round-trip serialization does not affect result +// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization %s -o %t/deps.json -I %t // RUN: %FileCheck %s < %t/deps.json // CHECK: "modulePath": "{{.*}}EWrapper.swiftmodule" diff --git a/test/ScanDependencies/binary_module_only.swift b/test/ScanDependencies/binary_module_only.swift index 488c95cc526ed..a2bb7178f5e7f 100644 --- a/test/ScanDependencies/binary_module_only.swift +++ b/test/ScanDependencies/binary_module_only.swift @@ -23,3 +23,7 @@ import Foo // Step 3: scan dependencies, pointed only at the binary module file should detect it as a swiftBinaryModule kind of dependency // RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -I %t/binaryModuleOnly -emit-dependencies -emit-dependencies-path %t/deps.d -sdk %t -prebuilt-module-cache-path %t/ResourceDir/%target-sdk-name/prebuilt-modules // RUN: %FileCheck %s -check-prefix=BINARY_MODULE_ONLY < %t/deps.json + +// Step 4: Ensure that round-trip serialization does not affect result +// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization %s -o %t/deps.json -I %t/binaryModuleOnly -emit-dependencies -emit-dependencies-path %t/deps.d -sdk %t -prebuilt-module-cache-path %t/ResourceDir/%target-sdk-name/prebuilt-modules +// RUN: %FileCheck %s -check-prefix=BINARY_MODULE_ONLY < %t/deps.json diff --git a/test/ScanDependencies/can_import_placeholder.swift b/test/ScanDependencies/can_import_placeholder.swift index a04e103bfe76b..8fb9900d5a123 100644 --- a/test/ScanDependencies/can_import_placeholder.swift +++ b/test/ScanDependencies/can_import_placeholder.swift @@ -11,8 +11,10 @@ // RUN: echo "}]" >> %/t/inputs/map.json // RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 +// RUN: %FileCheck %s < %t/deps.json -// Check the contents of the JSON output +// Ensure that round-trip serialization does not affect result +// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 // RUN: %FileCheck %s < %t/deps.json // REQUIRES: executable_test @@ -45,4 +47,3 @@ import SomeExternalModule // CHECK-NEXT: "swift": "_Concurrency" // CHECK-NEXT: } // CHECK-NEXT: ], - diff --git a/test/ScanDependencies/can_import_with_map.swift b/test/ScanDependencies/can_import_with_map.swift index ef99ad15e192e..3df7d64b2a632 100644 --- a/test/ScanDependencies/can_import_with_map.swift +++ b/test/ScanDependencies/can_import_with_map.swift @@ -25,6 +25,11 @@ // RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json // RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_Distributed\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/distributed_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json // RUN: %target-swift-frontend -typecheck %s -explicit-swift-module-map-file %t/inputs/map.json -disable-implicit-swift-modules diff --git a/test/ScanDependencies/clang-target.swift b/test/ScanDependencies/clang-target.swift new file mode 100644 index 0000000000000..f319ebe15cb1b --- /dev/null +++ b/test/ScanDependencies/clang-target.swift @@ -0,0 +1,22 @@ +// REQUIRES: VENDOR=apple +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t.module-cache) +// RUN: %target-swift-frontend -emit-module -o %t.foo.swiftmodule -module-cache-path %t.module-cache -I %S/Inputs/CHeaders -I %S/Inputs/Swift %s -target %target-cpu-apple-macosx10.14 + +// Without -clang-target, we build two X.pcm +// RUN: find %t.module-cache -name "X-*.pcm" | count 2 + +// RUN: %empty-directory(%t.module-cache) +// RUN: %target-swift-frontend -emit-module -o %t.foo.swiftmodule -module-cache-path %t.module-cache -I %S/Inputs/CHeaders -I %S/Inputs/Swift %s -target %target-cpu-apple-macosx10.14 -clang-target %target-cpu-apple-macosx10.14 + +// With -clang-target, we build one X.pcm +// RUN: find %t.module-cache -name "X-*.pcm" | count 1 +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t.module-cache %s -o %t.deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -target %target-cpu-apple-macosx10.14 -clang-target %target-cpu-apple-macosx10.14 + +// RUN: %FileCheck %s < %t.deps.json + +// CHECK: "-clang-target" +// CHECK-NEXT: "{{.*}}-apple-macosx10.14" +import X +import XWithTarget diff --git a/test/ScanDependencies/explicit-framework-irgen.swift b/test/ScanDependencies/explicit-framework-irgen.swift index 1720251f204b6..a1f9bca25c3ef 100644 --- a/test/ScanDependencies/explicit-framework-irgen.swift +++ b/test/ScanDependencies/explicit-framework-irgen.swift @@ -26,6 +26,11 @@ // RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json // RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_Distributed\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/distributed_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json // RUN: %target-swift-frontend -emit-object -emit-module -disable-implicit-swift-modules -explicit-swift-module-map-file %t/inputs/map.json -o %t/explicit-framework-irgen.o %s diff --git a/test/ScanDependencies/explicit-module-map-forwarding-module.swift b/test/ScanDependencies/explicit-module-map-forwarding-module.swift index 9b9a5d40e6519..26bafeabfd3fc 100644 --- a/test/ScanDependencies/explicit-module-map-forwarding-module.swift +++ b/test/ScanDependencies/explicit-module-map-forwarding-module.swift @@ -35,6 +35,11 @@ // RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json // RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_Distributed\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/distributed_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json // RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -enable-swiftsourceinfo -source-filename %s -explicit-swift-module-map-file %t/inputs/map.json | %FileCheck %s diff --git a/test/ScanDependencies/include-sdk-in-command.swift b/test/ScanDependencies/include-sdk-in-command.swift new file mode 100644 index 0000000000000..eefe5a51c21fb --- /dev/null +++ b/test/ScanDependencies/include-sdk-in-command.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -sdk %t/mysecretsdk.sdk + +// Check the contents of the JSON output +// RUN: %FileCheck %s < %t/deps.json + +func foo() { print(1) } + +// CHECK: "-sdk", +// CHECK-NEXT: mysecretsdk.sdk diff --git a/test/ScanDependencies/local_cache_consistency.swift b/test/ScanDependencies/local_cache_consistency.swift new file mode 100644 index 0000000000000..c969a648b89cb --- /dev/null +++ b/test/ScanDependencies/local_cache_consistency.swift @@ -0,0 +1,26 @@ +// REQUIRES: executable_test +// REQUIRES: objc_interop +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/clang-module-cache + +// Run the scanner once, ensuring CoreFoundation dependencies are as expected +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -swift-version 4 +// RUN: %FileCheck %s < %t/deps.json + +import CoreFoundation + +// CHECK: "clang": "CoreFoundation" + +// CHECK: "directDependencies": [ +// CHECK: { +// CHECK: "clang": "Darwin" +// CHECK: }, +// CHECK: { +// CHECK: "clang": "Dispatch" +// CHECK: } +// CHECK: ], + +// Make sure the transitive dependency on os_object is present +// CHECK: "clang": "os_object" diff --git a/test/ScanDependencies/module_deps.swift b/test/ScanDependencies/module_deps.swift index 0ebb03a94fdcc..c9c46e435991e 100644 --- a/test/ScanDependencies/module_deps.swift +++ b/test/ScanDependencies/module_deps.swift @@ -1,9 +1,9 @@ // RUN: %empty-directory(%t) // RUN: mkdir -p %t/clang-module-cache -// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 // Check the contents of the JSON output -// RUN: %FileCheck %s < %t/deps.json +// RUN: %FileCheck -check-prefix CHECK_NO_CLANG_TARGET %s < %t/deps.json // Check the contents of the JSON output // RUN: %FileCheck %s -check-prefix CHECK-NO-SEARCH-PATHS < %t/deps.json @@ -20,6 +20,15 @@ // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/deps.json +// Ensure that round-trip serialization does not affect result +// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 +// RUN: %FileCheck -check-prefix CHECK_NO_CLANG_TARGET %s < %t/deps.json + +// Ensure that scanning with `-clang-target` makes sure that Swift modules' respecitve PCM-dependency-build-argument sets do not contain target triples. +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps_clang_target.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -clang-target %target-cpu-apple-macosx10.14 +// Check the contents of the JSON output +// RUN: %FileCheck -check-prefix CHECK_CLANG_TARGET %s < %t/deps_clang_target.json + // REQUIRES: executable_test // REQUIRES: objc_interop @@ -174,10 +183,13 @@ import SubE // CHECK: "-swift-version" // CHECK: "5" // CHECK: ], -// CHECK" "extraPcmArgs": [ -// CHECK" "-target", -// CHECK" "-fapinotes-swift-version=5" -// CHECK" ] +// CHECK_NO_CLANG_TARGET: "extraPcmArgs": [ +// CHECK_NO_CLANG_TARGET-NEXT: "-Xcc", +// CHECK_NO_CLANG_TARGET-NEXT: "-target", +// CHECK_CLANG_TARGET: "extraPcmArgs": [ +// CHECK_CLANG_TARGET-NEXT: "-Xcc", +// CHECK_CLANG_TARGET-NEXT: "-fapinotes-swift-version={{.*}}" +// CHECK_CLANG_TARGET-NEXT: ] /// --------Swift module Swift // CHECK-LABEL: "modulePath": "Swift.swiftmodule", @@ -201,7 +213,6 @@ import SubE /// --------Clang module SwiftShims // CHECK-LABEL: "modulePath": "SwiftShims.pcm", -// CHECK-NO-SEARCH-PATHS-NOT: "-sdk" // CHECK-NO-SEARCH-PATHS-NOT: "-prebuilt-module-cache-path" // Check make-style dependencies diff --git a/test/ScanDependencies/module_deps_cache_reuse.swift b/test/ScanDependencies/module_deps_cache_reuse.swift new file mode 100644 index 0000000000000..6e42f5fec7d46 --- /dev/null +++ b/test/ScanDependencies/module_deps_cache_reuse.swift @@ -0,0 +1,197 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/clang-module-cache + +// Run the scanner once, emitting the serialized scanner cache +// RUN: %target-swift-frontend -scan-dependencies -Rdependency-scan-cache -serialize-dependency-scan-cache -dependency-scan-cache-path %t/cache.moddepcache -module-cache-path %t/clang-module-cache %s -o %t/deps_initial.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 2>&1 | %FileCheck %s -check-prefix CHECK-REMARK-SAVE + +// Run the scanner again, but now re-using previously-serialized cache +// RUN: %target-swift-frontend -scan-dependencies -Rdependency-scan-cache -load-dependency-scan-cache -dependency-scan-cache-path %t/cache.moddepcache -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 2>&1 | %FileCheck %s -check-prefix CHECK-REMARK-LOAD + +// Check the contents of the JSON output +// RUN: %FileCheck %s < %t/deps.json + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import C +import E +import G +import SubE + +// CHECK-REMARK-SAVE: remark: Serializing module scanning dependency cache to: +// CHECK-REMARK-LOAD: remark: Re-using serialized module scanning dependency cache from: + +// CHECK: "mainModuleName": "deps" + +/// --------Main module +// CHECK-LABEL: "modulePath": "deps.swiftmodule", +// CHECK-NEXT: sourceFiles +// CHECK-NEXT: module_deps_cache_reuse.swift +// CHECK-NEXT: ], +// CHECK-NEXT: "directDependencies": [ +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "A" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang": "C" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "E" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "F" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "G" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "SubE" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "Swift" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "SwiftOnoneSupport" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "_Concurrency" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "_cross_import_E" +// CHECK-NEXT: } +// CHECK-NEXT: ], + +// CHECK: "extraPcmArgs": [ +// CHECK-NEXT: "-Xcc", +// CHECK-NEXT: "-target", +// CHECK-NEXT: "-Xcc", +// CHECK: "-fapinotes-swift-version=4" +// CHECK-NOT: "error: cannot open Swift placeholder dependency module map from" +// CHECK: "bridgingHeader": +// CHECK-NEXT: "path": +// CHECK-SAME: Bridging.h + +// CHECK-NEXT: "sourceFiles": +// CHECK-NEXT: Bridging.h +// CHECK-NEXT: BridgingOther.h + +// CHECK: "moduleDependencies": [ +// CHECK-NEXT: "F" +// CHECK-NEXT: ] + +/// --------Swift module A +// CHECK-LABEL: "modulePath": "A.swiftmodule", + +// CHECK: directDependencies +// CHECK-NEXT: { +// CHECK-NEXT: "clang": "A" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "Swift" +// CHECK-NEXT: }, + +/// --------Clang module C +// CHECK-LABEL: "modulePath": "C.pcm", + +// CHECK: "sourceFiles": [ +// CHECK-DAG: module.modulemap +// CHECK-DAG: C.h + +// CHECK: directDependencies +// CHECK-NEXT: { +// CHECK-NEXT: "clang": "B" + +// CHECK: "moduleMapPath" +// CHECK-SAME: module.modulemap + +// CHECK: "contextHash" +// CHECK-SAME: "{{.*}}" + +// CHECK: "commandLine": [ +// CHECK-NEXT: "-frontend" +// CHECK-NEXT: "-only-use-extra-clang-opts" +// CHECK-NEXT: "-Xcc" +// CHECK-NEXT: "clang" +// CHECK-NEXT: "-Xcc" +// CHECK-NEXT: "-fsyntax-only", +// CHECK: "-fsystem-module", +// CHECK-NEXT: "-emit-pcm", +// CHECK-NEXT: "-module-name", +// CHECK-NEXT: "C" + +/// --------Swift module E +// CHECK: "swift": "E" +// CHECK-LABEL: modulePath": "E.swiftmodule" +// CHECK: "directDependencies" +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "Swift" + +// CHECK: "moduleInterfacePath" +// CHECK-SAME: E.swiftinterface + +/// --------Swift module F +// CHECK: "modulePath": "F.swiftmodule", +// CHECK-NEXT: "sourceFiles": [ +// CHECK-NEXT: ], +// CHECK-NEXT: "directDependencies": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang": "F" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "Swift" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "SwiftOnoneSupport" +// CHECK-NEXT: } +// CHECK-NEXT: ], + +/// --------Swift module G +// CHECK-LABEL: "modulePath": "G.swiftmodule" +// CHECK: "directDependencies" +// CHECK-NEXT: { +// CHECK-NEXT: "clang": "G" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "Swift" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "SwiftOnoneSupport" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "details": { + +// CHECK: "contextHash": "{{.*}}", +// CHECK: "commandLine": [ +// CHECK: "-compile-module-from-interface" +// CHECK: "-target" +// CHECK: "-module-name" +// CHECK: "G" +// CHECK: "-swift-version" +// CHECK: "5" +// CHECK: ], +// CHECK" "extraPcmArgs": [ +// CHECK" "-target", +// CHECK" "-fapinotes-swift-version=5" +// CHECK" ] + +/// --------Swift module Swift +// CHECK-LABEL: "modulePath": "Swift.swiftmodule", + +// CHECK: directDependencies +// CHECK-NEXT: { +// CHECK-NEXT: "clang": "SwiftShims" + +/// --------Clang module B +// CHECK-LABEL: "modulePath": "B.pcm" + +// CHECK-NEXT: sourceFiles +// CHECK-DAG: module.modulemap +// CHECK-DAG: B.h + +// CHECK: directDependencies +// CHECK-NEXT: { +// CHECK-NEXT: "clang": "A" +// CHECK-NEXT: } + +/// --------Clang module SwiftShims +// CHECK-LABEL: "modulePath": "SwiftShims.pcm", diff --git a/test/ScanDependencies/module_deps_cross_import_overlay.swift b/test/ScanDependencies/module_deps_cross_import_overlay.swift index 8998196c0156a..951517430976e 100644 --- a/test/ScanDependencies/module_deps_cross_import_overlay.swift +++ b/test/ScanDependencies/module_deps_cross_import_overlay.swift @@ -1,10 +1,13 @@ // RUN: %empty-directory(%t) // RUN: mkdir -p %t/clang-module-cache // RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 - // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json +// Ensure that round-trip serialization does not affect result +// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 +// RUN: %FileCheck %s < %t/deps.json + // REQUIRES: executable_test // REQUIRES: objc_interop diff --git a/test/ScanDependencies/module_deps_different_paths_no_reuse.swift b/test/ScanDependencies/module_deps_different_paths_no_reuse.swift new file mode 100644 index 0000000000000..dd260173eccab --- /dev/null +++ b/test/ScanDependencies/module_deps_different_paths_no_reuse.swift @@ -0,0 +1,50 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/clang-module-cache + +// This test ensures that subsequent invocations of the dependency scanner that re-use previous cache state do not re-use cache entries that contain modules found outside of the current scanner invocation's search paths. + +// Run the scanner once, emitting the serialized scanner cache, with one set of search paths +// RUN: %target-swift-frontend -scan-dependencies -serialize-dependency-scan-cache -dependency-scan-cache-path %t/cache.moddepcache -module-cache-path %t/clang-module-cache %s -o %t/deps_initial.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 +// RUN: %FileCheck -input-file %t/deps_initial.json %s -check-prefix CHECK-INITIAL-SCAN + +// Run the scanner again, but now re-using previously-serialized cache and using a different search path for Swift modules +// RUN: %target-swift-frontend -scan-dependencies -load-dependency-scan-cache -dependency-scan-cache-path %t/cache.moddepcache -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/SwiftDifferent -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 +// RUN: %FileCheck -input-file %t/deps.json %s -check-prefix CHECK-DIFFERENT + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import A + +// CHECK-INITIAL-SCAN: "modulePath": "A.swiftmodule", +// CHECK-INITIAL-SCAN-NEXT: "sourceFiles": [ +// CHECK-INITIAL-SCAN-NEXT: ], +// CHECK-INITIAL-SCAN-NEXT: "directDependencies": [ +// CHECK-INITIAL-SCAN-NEXT: { +// CHECK-INITIAL-SCAN-NEXT: "clang": "A" +// CHECK-INITIAL-SCAN-NEXT: }, +// CHECK-INITIAL-SCAN-NEXT: { +// CHECK-INITIAL-SCAN-NEXT: "swift": "Swift" +// CHECK-INITIAL-SCAN-NEXT: }, +// CHECK-INITIAL-SCAN-NEXT: { +// CHECK-INITIAL-SCAN-NEXT: "swift": "SwiftOnoneSupport" +// CHECK-INITIAL-SCAN-NEXT: } +// CHECK-INITIAL-SCAN-NEXT: ], +// CHECK-INITIAL-SCAN-NEXT: "details": { +// CHECK-INITIAL-SCAN-NEXT: "swift": { +// CHECK-INITIAL-SCAN-NEXT: "moduleInterfacePath": "{{.*}}/Swift/A.swiftinterface", + +// CHECK-DIFFERENT: "modulePath": "A.swiftmodule", +// CHECK-DIFFERENT-NEXT: "sourceFiles": [ +// CHECK-DIFFERENT-NEXT: ], +// CHECK-DIFFERENT-NEXT: "directDependencies": [ +// CHECK-DIFFERENT-NEXT: { +// CHECK-DIFFERENT-NEXT: "swift": "Swift" +// CHECK-DIFFERENT-NEXT: }, +// CHECK-DIFFERENT-NEXT: { +// CHECK-DIFFERENT-NEXT: "swift": "SwiftOnoneSupport" +// CHECK-DIFFERENT-NEXT: } +// CHECK-DIFFERENT-NEXT: ], +// CHECK-DIFFERENT-NEXT: "details": { +// CHECK-DIFFERENT-NEXT: "swift": { +// CHECK-DIFFERENT-NEXT: "moduleInterfacePath": "{{.*}}/SwiftDifferent/A.swiftinterface", diff --git a/test/ScanDependencies/module_deps_external.swift b/test/ScanDependencies/module_deps_external.swift index cc0144a0fed64..f896911d0d9a9 100644 --- a/test/ScanDependencies/module_deps_external.swift +++ b/test/ScanDependencies/module_deps_external.swift @@ -27,6 +27,10 @@ // RUN: %target-codesign %t/main // RUN: %target-run %t/main %t/deps.json +// Ensure that round-trip serialization does not affect result +// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 +// RUN: %FileCheck %s < %t/deps.json + // REQUIRES: executable_test // REQUIRES: objc_interop import SomeExternalModule diff --git a/test/ScanDependencies/module_framework.swift b/test/ScanDependencies/module_framework.swift index 21fa3d95efb4f..bbd0933efe143 100644 --- a/test/ScanDependencies/module_framework.swift +++ b/test/ScanDependencies/module_framework.swift @@ -1,10 +1,14 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -swift-version 4 -Xcc -Xclang - // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json + +// Ensure that round-trip serialization does not affect result +// RUN: %target-swift-frontend -scan-dependencies -test-dependency-scan-cache-serialization %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -swift-version 4 -Xcc -Xclang +// RUN: %FileCheck %s < %t/deps.json + // REQUIRES: OS=macosx - + import CryptoKit // CHECK: "mainModuleName": "deps" diff --git a/test/ScanDependencies/placholder_overlay_deps.swift b/test/ScanDependencies/placholder_overlay_deps.swift index cf8d3f0fc8b60..350af9c0c5732 100644 --- a/test/ScanDependencies/placholder_overlay_deps.swift +++ b/test/ScanDependencies/placholder_overlay_deps.swift @@ -7,7 +7,8 @@ // RUN: echo "\"modulePath\": \"%/t/inputs/Darwin.swiftmodule\"," >> %/t/inputs/map.json // RUN: echo "\"docPath\": \"%/t/inputs/Darwin.swiftdoc\"," >> %/t/inputs/map.json // RUN: echo "\"sourceInfoPath\": \"%/t/inputs/Darwin.swiftsourceinfo\"," >> %/t/inputs/map.json -// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json +// RUN: echo "\"isSystem\": true" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json // RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json diff --git a/test/Sema/Inputs/availability_enum_case_other.swift b/test/Sema/Inputs/availability_enum_case_other.swift new file mode 100644 index 0000000000000..1cd333082e032 --- /dev/null +++ b/test/Sema/Inputs/availability_enum_case_other.swift @@ -0,0 +1,4 @@ +public enum Horse { + @available(macOS 100, *) + case kevin(Int) +} diff --git a/test/Sema/Inputs/conformance_availability_overlapping_other.swift b/test/Sema/Inputs/conformance_availability_overlapping_other.swift new file mode 100644 index 0000000000000..b86450b58b7b1 --- /dev/null +++ b/test/Sema/Inputs/conformance_availability_overlapping_other.swift @@ -0,0 +1,15 @@ +public protocol P {} + +public struct HasUnavailableConformance {} + +@available(*, unavailable) +extension HasUnavailableConformance : P {} + +public struct HasConditionallyAvailableConformance {} + +@available(macOS 100, *) +extension HasConditionallyAvailableConformance : P {} + +public struct HasAlwaysAvailableConformance {} + +extension HasAlwaysAvailableConformance : P {} diff --git a/test/Sema/Inputs/extra.accessnotes b/test/Sema/Inputs/extra.accessnotes index ac85573ba26fb..84b433c193f60 100644 --- a/test/Sema/Inputs/extra.accessnotes +++ b/test/Sema/Inputs/extra.accessnotes @@ -5,3 +5,9 @@ Notes: - Name: 'fn()' CorinthianLeather: 'rich' # expected-remark@-1 {{ignored invalid content in access notes file: unknown key 'CorinthianLeather'}} + +# These notes should apply, or attempt to apply, despite the other problems. +- Name: 'Extant.good(_:)' + ObjC: true +- Name: 'Extant.bad(_:)' + ObjC: true diff --git a/test/Sema/access-notes-invalid.swift b/test/Sema/access-notes-invalid.swift index f541c55ae33ec..b8e516cf4e6dd 100644 --- a/test/Sema/access-notes-invalid.swift +++ b/test/Sema/access-notes-invalid.swift @@ -5,6 +5,31 @@ // RUN: %target-typecheck-verify-swift -access-notes-path %S/Inputs/extra.accessnotes -verify-additional-file %S/Inputs/extra.accessnotes +// RUN: %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes -Raccess-note=none 2>&1 | %FileCheck --check-prefixes=GOOD-IGNORE,BAD-IGNORE %s +// RUN: %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes -Raccess-note=failures 2>&1 | %FileCheck --check-prefixes=GOOD-IGNORE,BAD-REMARK %s +// RUN: %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes -Raccess-note=all 2>&1 | %FileCheck --check-prefixes=GOOD-REMARK,BAD-REMARK %s +// RUN: not %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes -Raccess-note=all-validate 2>&1 | %FileCheck --check-prefixes=GOOD-REMARK,BAD-ERROR %s + +// Default should be 'all'. +// RUN: %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes 2>&1 | %FileCheck --check-prefixes=GOOD-REMARK,BAD-REMARK %s + +class Extant { + func good(_: Int) {} // expected-remark * {{}} expected-note * {{}} + // GOOD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-1]]:{{[0-9]+}}: remark: implicitly added '@objc' to this instance method, as specified by access note for Access notes containing future, unknown syntax + // GOOD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-2]]:{{[0-9]+}}: note: add '@objc' explicitly to silence this warning + // GOOD-REMARK-DAG: access-notes-invalid.swift:[[@LINE-3]]:{{[0-9]+}}: remark: implicitly added '@objc' to this instance method, as specified by access note for Access notes containing future, unknown syntax + // GOOD-REMARK-DAG: access-notes-invalid.swift:[[@LINE-4]]:{{[0-9]+}}: note: add '@objc' explicitly to silence this warning + + func bad(_: Int?) {} // expected-remark * {{}} + // BAD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-1]]:{{[0-9]+}}: remark: ignored access note: method cannot be marked @objc by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax + // BAD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-2]]:{{[0-9]+}}: error: ignored access note: method cannot be marked @objc by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax + // BAD-REMARK-DAG: access-notes-invalid.swift:[[@LINE-3]]:{{[0-9]+}}: remark: ignored access note: method cannot be marked @objc by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax + // BAD-ERROR-DAG: access-notes-invalid.swift:[[@LINE-4]]:{{[0-9]+}}: error: ignored access note: method cannot be marked @objc by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax +} + // FIXME: Should diagnose multiple access notes for the same decl // FIXME: Should diagnose access notes that don't match a decl in the source code + +// Only valid on platforms where @objc is valid. +// REQUIRES: objc_interop diff --git a/test/Sema/availability_and_delayed_parsing.swift b/test/Sema/availability_and_delayed_parsing.swift new file mode 100644 index 0000000000000..4c272f719f326 --- /dev/null +++ b/test/Sema/availability_and_delayed_parsing.swift @@ -0,0 +1,49 @@ +/// Check for reliable availability checking in inlinable code even when +/// skipping some function bodies. rdar://82269657 + +// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts %s -target x86_64-apple-macos10.10 2>&1 \ +// RUN: | %FileCheck %s --check-prefixes TRC-API,TRC-INLINABLE,TRC-WITHTYPES,TRC-FULL +// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts %s -target x86_64-apple-macos10.10 -experimental-skip-non-inlinable-function-bodies-without-types 2>&1 \ +// RUN: | %FileCheck %s --check-prefixes TRC-API,TRC-INLINABLE,TRC-WITHTYPES,TRC-FULL-NOT +// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts %s -target x86_64-apple-macos10.10 -experimental-skip-non-inlinable-function-bodies 2>&1 \ +// RUN: | %FileCheck %s --check-prefixes TRC-API,TRC-INLINABLE,TRC-WITHTYPES-NOT,TRC-FULL-NOT +// RUN: %target-swift-frontend -typecheck -dump-type-refinement-contexts %s -target x86_64-apple-macos10.10 -experimental-skip-all-function-bodies 2>&1 \ +// RUN: | %FileCheck %s --check-prefixes TRC-API,TRC-INLINABLE-NOT,TRC-WITHTYPES-NOT,TRC-FULL-NOT + +// REQUIRES: OS=macosx + +@available(macOS 10.12, *) +public func foo() { } +// TRC-API: (root versions=[10.10.0,+Inf) +// TRC-API: (decl versions=[10.12,+Inf) decl=foo() + +@inlinable public func inlinableFunc() { + if #available(macOS 10.12, *) { + foo() + } +} +// TRC-INLINABLE: (condition_following_availability versions=[10.12,+Inf) +// TRC-INLINABLE: (if_then versions=[10.12,+Inf) +// TRC-INLINABLE-NOT-NOT: (condition_following_availability versions=[10.12,+Inf) +// TRC-INLINABLE-NOT-NOT: (if_then versions=[10.12,+Inf) + +public func funcWithType() { + struct S {} + if #available(macOS 10.13, *) { + foo() + } +} +// TRC-WITHTYPES: (condition_following_availability versions=[10.13,+Inf) +// TRC-WITHTYPES: (if_then versions=[10.13,+Inf) +// TRC-WITHTYPES-NOT-NOT: (condition_following_availability versions=[10.13,+Inf) +// TRC-WITHTYPES-NOT-NOT: (if_then versions=[10.13,+Inf) + +public func funcSkippable() { + if #available(macOS 10.14, *) { + foo() + } +} +// TRC-FULL: (condition_following_availability versions=[10.14,+Inf) +// TRC-FULL: (if_then versions=[10.14,+Inf) +// TRC-FULL-NOT-NOT: (condition_following_availability versions=[10.14,+Inf) +// TRC-FULL-NOT-NOT: (if_then versions=[10.14,+Inf) diff --git a/test/Sema/availability_define.swift b/test/Sema/availability_define.swift index d00abe95a2880..a8ccb163f3d6d 100644 --- a/test/Sema/availability_define.swift +++ b/test/Sema/availability_define.swift @@ -56,6 +56,13 @@ func client() { onMacOS10_11() } + if #unavailable(_iOS9Aligned) { + onMacOS10_11() // expected-error {{is only available in macOS 10.11 or newer}} + // expected-note @-1 {{add 'if #available' version check}} + } else { + onMacOS10_11() + } + if #available(_unknownMacro, *) { } // expected-error {{expected version number}} } @@ -64,6 +71,9 @@ public func forbidMacrosInInlinableCode() { if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #unavailable(_iOS9Aligned) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #unavailable(_iOS9, _macOS10_11) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #unavailable(iOS 9.0, _macOS10_11, tvOS 9.0) { } // expected-error {{availability macro cannot be used in inlinable global function}} } @_alwaysEmitIntoClient @@ -71,4 +81,7 @@ public func forbidMacrosInInlinableCode1() { if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #unavailable(_iOS9Aligned) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #unavailable(_iOS9, _macOS10_11) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #unavailable(iOS 9.0, _macOS10_11, tvOS 9.0) { } // expected-error {{availability macro cannot be used in inlinable global function}} } diff --git a/test/Sema/availability_define_parsing.swift b/test/Sema/availability_define_parsing.swift index 291515794b47a..0560abbb0da92 100644 --- a/test/Sema/availability_define_parsing.swift +++ b/test/Sema/availability_define_parsing.swift @@ -3,7 +3,7 @@ // RUN: -define-availability ":a b c d" \ // RUN: -define-availability "_justAName" \ // RUN: -define-availability "_brokenPlatforms:spaceOS 10.11" \ -// RUN: -define-availability "_refuseWildcard:iOS 13.0, *" \ +// RUN: -define-availability "_refuseWildcard:iOS 13.0, *, macOS 11.0" \ // RUN: -define-availability "_duplicateVersion 1.0:iOS 13.0" \ // RUN: -define-availability "_duplicateVersion 1.0:iOS 13.0" \ // RUN: 2>&1 | %FileCheck %s diff --git a/test/Sema/availability_enum_case.swift b/test/Sema/availability_enum_case.swift new file mode 100644 index 0000000000000..da1f2e601ced3 --- /dev/null +++ b/test/Sema/availability_enum_case.swift @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift -emit-module %S/Inputs/availability_enum_case_other.swift -emit-module-interface-path %t/availability_enum_case_other.swiftinterface -swift-version 5 -enable-library-evolution +// RUN: %target-typecheck-verify-swift -I %t + +// RUN: %target-build-swift -emit-module %S/Inputs/availability_enum_case_other.swift -emit-module-interface-path %t/availability_enum_case_other.swiftinterface -swift-version 5 -enable-library-evolution -whole-module-optimization +// RUN: %target-typecheck-verify-swift -I %t + +// REQUIRES: OS=macosx + +import availability_enum_case_other + +func ride(horse: Horse) { + // expected-note@-1 {{add @available attribute to enclosing global function}} + + _ = Horse.kevin + // expected-error@-1 {{'kevin' is only available in macOS 100 or newer}} + // expected-note@-2 {{add 'if #available' version check}} +} diff --git a/test/Sema/availability_implicitly_unnecessary.swift b/test/Sema/availability_implicitly_unnecessary.swift new file mode 100644 index 0000000000000..6fbc0d2b7edd1 --- /dev/null +++ b/test/Sema/availability_implicitly_unnecessary.swift @@ -0,0 +1,9 @@ +// RUN: %target-typecheck-verify-swift -verify -target %target-cpu-apple-macosx11.2 -disable-objc-attr-requires-foundation-module +// REQUIRES: OS=macosx + +@available(macOS 11.0, *) +class Foo { + func foo() { + if #available(macOS 11.1, *) {} + } +} diff --git a/test/Sema/availability_unnecessary.swift b/test/Sema/availability_unnecessary.swift new file mode 100644 index 0000000000000..a01b6293e34fc --- /dev/null +++ b/test/Sema/availability_unnecessary.swift @@ -0,0 +1,14 @@ +// Ensure that the `unnecessary check` availability warning is emitted when unnecessary due to +// scope's explicit annotation + +// RUN: %target-typecheck-verify-swift -verify -target %target-cpu-apple-macosx11.2 -disable-objc-attr-requires-foundation-module +// REQUIRES: OS=macosx + +@available(macOS 11.1, *) +class Foo { + // expected-note@-1 {{enclosing scope here}} + func foo() { + // expected-warning@+1 {{unnecessary check for 'macOS'; enclosing scope ensures guard will always be true}} + if #available(macOS 11.0, *) {} + } +} diff --git a/test/Sema/availability_versions.swift b/test/Sema/availability_versions.swift index d57accb0c5008..70834960c8a53 100644 --- a/test/Sema/availability_versions.swift +++ b/test/Sema/availability_versions.swift @@ -1385,7 +1385,7 @@ protocol ProtocolWithRequirementMentioningUnavailable { protocol HasMethodF { associatedtype T - func f(_ p: T) // expected-note 4{{protocol requirement here}} + func f(_ p: T) // expected-note 3{{protocol requirement here}} } class TriesToConformWithFunctionIntroducedOn10_51 : HasMethodF { @@ -1499,17 +1499,14 @@ extension TakesClassAvailableOn10_51_B : HasTakesClassAvailableOn10_51 { } -// We do not want potential unavailability to play a role in picking a witness for a -// protocol requirement. Rather, the witness should be chosen, regardless of its -// potential unavailability, and then it should be diagnosed if it is less available -// than the protocol requires. -class TestAvailabilityDoesNotAffectWitnessCandidacy : HasMethodF { - // Test that we choose the more specialized witness even though it is - // less available than the protocol requires and there is a less specialized - // witness that has suitable availability. +// We want conditional availability to play a role in picking a witness for a +// protocol requirement. +class TestAvailabilityAffectsWitnessCandidacy : HasMethodF { + // Test that we choose the less specialized witness, because the more specialized + // witness is conditionally unavailable. @available(OSX, introduced: 10.51) - func f(_ p: Int) { } // expected-error {{protocol 'HasMethodF' requires 'f' to be available in macOS 10.50.0 and newer}} + func f(_ p: Int) { } func f(_ p: T) { } } diff --git a/test/Sema/conformance_availability_disambiguate.swift b/test/Sema/conformance_availability_disambiguate.swift new file mode 100644 index 0000000000000..cfaa7606bfeb2 --- /dev/null +++ b/test/Sema/conformance_availability_disambiguate.swift @@ -0,0 +1,43 @@ +// RUN: %target-swift-frontend -emit-sil -verify %s | %FileCheck %s + +// REQUIRES: OS=macosx + +public protocol Potato { + func eat() +} + +public protocol Fried {} + +extension Potato { + public func eat() {} +} + +@available(macOS 100, *) +extension Potato where Self : Fried { + public func eat() {} +} + +// We ought to pick the unconstrained Potato.eat(), not +// the one from the extension, because the extension has +// narrower availability than the conformance. +public struct CurlyFries : Potato, Fried {} + +public struct TaterTots {} + +// This conformance on the other hand should use the +// constrained Potato.eat(), since the generic signature +// is more specific than the unconstrained protocol +// extension. +@available(macOS 100, *) +extension TaterTots : Potato, Fried {} + +// We FileCheck the SILGen output to verify that the correct +// witnesses were chosen above. + +// CHECK-LABEL: sil shared [transparent] [thunk] @$s37conformance_availability_disambiguate10CurlyFriesVAA6PotatoA2aDP3eatyyFTW : $@convention(witness_method: Potato) (@in_guaranteed CurlyFries) -> () { +// CHECK: function_ref @$s37conformance_availability_disambiguate6PotatoPAAE3eatyyF : $@convention(method) <τ_0_0 where τ_0_0 : Potato> (@in_guaranteed τ_0_0) -> () +// CHECK: return + +// sil shared [transparent] [thunk] @$s37conformance_availability_disambiguate9TaterTotsVAA6PotatoA2aDP3eatyyFTW : $@convention(witness_method: Potato) (@in_guaranteed TaterTots) -> () { +// CHECK: function_ref @$s37conformance_availability_disambiguate6PotatoPA2A5FriedRzrlE3eatyyF : $@convention(method) <τ_0_0 where τ_0_0 : Fried, τ_0_0 : Potato> (@in_guaranteed τ_0_0) -> () +// CHECK: return \ No newline at end of file diff --git a/test/Sema/conformance_availability_overlapping.swift b/test/Sema/conformance_availability_overlapping.swift new file mode 100644 index 0000000000000..e3a69f2282cb9 --- /dev/null +++ b/test/Sema/conformance_availability_overlapping.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module %S/Inputs/conformance_availability_overlapping_other.swift -emit-module-path %t/conformance_availability_overlapping_other.swiftmodule +// RUN: %target-typecheck-verify-swift -I %t + +// REQUIRES: OS=macosx + +import conformance_availability_overlapping_other + +extension HasUnavailableConformance : P {} + +extension HasConditionallyAvailableConformance : P {} + +extension HasAlwaysAvailableConformance : P {} +// expected-warning@-1 {{conformance of 'HasAlwaysAvailableConformance' to protocol 'P' was already stated in the type's module 'conformance_availability_overlapping_other'}} + +struct G {} + +// None of these should produce a warning about an unavailable conformance. +func usesConformance(_: G) {} +func usesConformance(_: G) {} +func usesConformance(_: G) {} \ No newline at end of file diff --git a/test/Sema/diag_ambiguous_overloads.swift b/test/Sema/diag_ambiguous_overloads.swift index f76457697e6d5..1aa56b1b0bece 100644 --- a/test/Sema/diag_ambiguous_overloads.swift +++ b/test/Sema/diag_ambiguous_overloads.swift @@ -136,19 +136,15 @@ array.withUnsafeBufferPointer { // expected-error@-1 {{no exact matches in call to initializer}} // expected-note@-2 {{candidate expects value of type 'UnsafeRawPointer' for parameter #1}} // expected-note@-3 {{candidate expects value of type 'UnsafeMutableRawPointer' for parameter #1}} - // expected-note@-4 {{candidate expects value of type 'OpaquePointer' for parameter #1}} - // expected-note@-5 {{candidate expects value of type 'Builtin.RawPointer' for parameter #1}} UnsafeRawPointer($0) as UnsafeBufferPointer // expected-error {{cannot convert value of type 'UnsafeRawPointer' to type 'UnsafeBufferPointer' in coercion}} // expected-error@-1 {{no exact matches in call to initializer}} // expected-note@-2 {{found candidate with type '(UnsafeRawPointer) -> UnsafeRawPointer'}} - // expected-note@-3 {{found candidate with type '(Builtin.RawPointer) -> UnsafeRawPointer'}} - // expected-note@-4 {{found candidate with type '(OpaquePointer) -> UnsafeRawPointer'}} - // expected-note@-5 {{found candidate with type '(UnsafeMutableRawPointer) -> UnsafeRawPointer'}} + // expected-note@-3 {{found candidate with type '(UnsafeMutableRawPointer) -> UnsafeRawPointer'}} } -func SR12689_1(_ u: Int) -> String { "" } // expected-note {{found this candidate}} expected-note {{candidate expects value of type 'Int' for parameter #1}} -func SR12689_1(_ u: String) -> Double { 0 } // expected-note {{found this candidate}} expected-note {{candidate expects value of type 'String' for parameter #1}} +func SR12689_1(_ u: Int) -> String { "" } // expected-note {{found this candidate}} expected-note {{candidate expects value of type 'Int' for parameter #1 (got 'Double')}} +func SR12689_1(_ u: String) -> Double { 0 } // expected-note {{found this candidate}} expected-note {{candidate expects value of type 'String' for parameter #1 (got 'Double')}} func SR12689_2(_ u: Int) {} SR12689_2(SR12689_1(1 as Double)) // expected-error {{no exact matches in call to global function 'SR12689_1'}} diff --git a/test/Sema/diag_constantness_check.swift b/test/Sema/diag_constantness_check.swift index b3e4dbf1e28e0..f03830d7adaa5 100644 --- a/test/Sema/diag_constantness_check.swift +++ b/test/Sema/diag_constantness_check.swift @@ -361,3 +361,66 @@ func testConstructorAnnotation(x: Int) { let _ = ConstructorTest(x) // expected-error@-1 {{argument must be an integer literal}} } + +// Test closure expressions + +func funcAcceptingClosure(_ x: () -> T) -> T { + return x() +} + +func normalFunction() {} + +@_semantics("oslog.requires_constant_arguments") +func constantArgumentFunctionReturningIntCollection(_ constArg: Int) -> Array { + return [constArg, constArg, constArg] +} + +@_semantics("oslog.requires_constant_arguments") +func constantArgumentFunctionReturningInt(_ constArg: Int) -> Int { + return constArg +} + +func testCallsWithinClosures(s: String, x: Int) { + funcAcceptingClosure { + constantArgumentFunction(s) + // expected-error@-1 {{argument must be a string literal}} + } + funcAcceptingClosure { + constantArgumentFunction(s) + // expected-error@-1 {{argument must be a string literal}} + constantArgumentFunction(s) + // expected-error@-1 {{argument must be a string literal}} + } + funcAcceptingClosure { + funcAcceptingClosure { + constantArgumentFunction(s) + // expected-error@-1 {{argument must be a string literal}} + } + } + funcAcceptingClosure { + normalFunction() + funcAcceptingClosure { + constantArgumentFunction(s) + // expected-error@-1 {{argument must be a string literal}} + } + } + let _ = + funcAcceptingClosure { + constantArgumentFunctionReturningIntCollection(x) + // expected-error@-1 {{argument must be an integer literal}} + } + .filter { $0 > 0 } + .map { $0 + 1 } + let _ = + funcAcceptingClosure { + constantArgumentFunctionReturningInt(x) + // expected-error@-1 {{argument must be an integer literal}} + } + 10 * x + let _ = { constantArgumentFunctionReturningIntCollection(x) } + // expected-error@-1 {{argument must be an integer literal}} + funcAcceptingClosure { + constantArgumentFunction(1) + constantArgumentFunction("string literal") + constantArgumentFunction("string with a single interpolation \(x)") + } +} diff --git a/test/Sema/diag_constantness_check_os_log.swift b/test/Sema/diag_constantness_check_os_log.swift index aba696db441bb..72594c3c13a8e 100644 --- a/test/Sema/diag_constantness_check_os_log.swift +++ b/test/Sema/diag_constantness_check_os_log.swift @@ -185,3 +185,44 @@ func testLogMessageWrappingDiagnostics() { _osLogTestHelper(nonConstantFunction("key", fallback: "A literal message")) // expected-error@-1{{argument must be a string interpolation}} } + +// Test closure expressions + +func funcAcceptingClosure(_ x: () -> T) -> T { + return x() +} + +func normalFunction() {} + +func testCallsWithinClosures(formatOpt: OSLogIntegerFormatting) { + funcAcceptingClosure { + _osLogTestHelper("Minimum integer value: \(Int.min, format: formatOpt)") + // expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}} + } + funcAcceptingClosure { + _osLogTestHelper("Minimum integer value: \(Int.min, format: formatOpt)") + // expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}} + _osLogTestHelper("Maximum integer value: \(Int.max, format: formatOpt)") + // expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}} + } + funcAcceptingClosure { + funcAcceptingClosure { + _osLogTestHelper("Minimum integer value: \(Int.min, format: formatOpt)") + // expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}} + } + } + funcAcceptingClosure { + normalFunction() + funcAcceptingClosure { + _osLogTestHelper("Minimum integer value: \(Int.min, format: formatOpt)") + // expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}} + } + } + funcAcceptingClosure { + _osLogTestHelper("Minimum integer value: \(Int.min, format: .hex)") + } + funcAcceptingClosure { + _osLogTestHelper("Minimum integer value: \(Int.min, format: .hex)") + _osLogTestHelper("Maximum integer value: \(Int.max, privacy: .public)") + } +} diff --git a/test/Sema/diag_invalid_interpolation_4_2.swift b/test/Sema/diag_invalid_interpolation_4_2.swift new file mode 100644 index 0000000000000..1e135ed1bef45 --- /dev/null +++ b/test/Sema/diag_invalid_interpolation_4_2.swift @@ -0,0 +1,15 @@ +// RUN: %target-typecheck-verify-swift -swift-version 4.2 + +var x = 0 +_ = "\(&x)" +// expected-error@-1 {{'&' used with non-inout argument of type 'Int'}} + +_ = "\(y: &x)" +// expected-error@-1 {{'&' used with non-inout argument of type 'Int'}} +// expected-warning@-2 {{labeled interpolations will not be ignored in Swift 5}} +// expected-note@-3 {{remove 'y' label to keep current behavior}} + +_ = "\(x, y: &x)" +// expected-error@-1 {{use of extraneous '&'}} +// expected-warning@-2 {{interpolating multiple values will not form a tuple in Swift 5}} +// expected-note@-3 {{insert parentheses to keep current behavior}} diff --git a/test/Sema/diag_mismatched_magic_literals_swift6.swift b/test/Sema/diag_mismatched_magic_literals_swift6.swift index 763f21d70eeee..9931714432ff7 100644 --- a/test/Sema/diag_mismatched_magic_literals_swift6.swift +++ b/test/Sema/diag_mismatched_magic_literals_swift6.swift @@ -1,6 +1,10 @@ // The future "Swift 6 mode" behavior is staged in behind `-enable-experimental-concise-pound-file`. // RUN: %target-typecheck-verify-swift -enable-experimental-concise-pound-file +// And is also available in Swift 6 mode on asserts compilers. +// RUN: %target-typecheck-verify-swift -swift-version 6 +// REQUIRES: asserts + func callee(file: String = #file) {} // expected-note {{'file' declared here}} func callee(fileID: String = #fileID) {} // expected-note {{'fileID' declared here}} func callee(filePath: String = #filePath) {} // expected-note 2 {{'filePath' declared here}} diff --git a/test/Sema/exhaustive_switch_huge.swift.gyb b/test/Sema/exhaustive_switch_huge.swift.gyb new file mode 100644 index 0000000000000..25c68d4ccf2fa --- /dev/null +++ b/test/Sema/exhaustive_switch_huge.swift.gyb @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) +// RUN: %gyb %s -o %t/exhaustive_switch_huge.swift +// RUN: %target-swift-frontend -typecheck -verify %t/exhaustive_switch_huge.swift + +% case_limit = 10000 + +// Make sure the exhaustiveness checker can check an unreasonable amount of +// enum cases in a reasonable amount of time. +enum E { +% for i in range(case_limit): + case x${i} +% end +} + +switch E.x1 { +% for i in range(case_limit): + case .x${i} : break +% end +} diff --git a/test/Sema/existential_nested_type.swift b/test/Sema/existential_nested_type.swift deleted file mode 100644 index 8189415c1f70e..0000000000000 --- a/test/Sema/existential_nested_type.swift +++ /dev/null @@ -1,24 +0,0 @@ -// RUN: %target-swift-frontend -typecheck -verify -swift-version 4 %s - -// rdar://29605388 -- Swift 3 admitted opening an existential type with -// associated types in one case. - -// See test/IRGen/existential_nested_type.swift for the Swift 3 test. - -protocol HasAssoc { - associatedtype A -} - -enum MyError : Error { - case bad(Any) -} - -func checkIt(_ js: Any) throws { - switch js { - case let dbl as HasAssoc: // expected-error {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} - throw MyError.bad(dbl) - - default: - fatalError("wrong") - } -} diff --git a/test/Sema/implementation-only-import-suggestion.swift b/test/Sema/implementation-only-import-suggestion.swift index 00bbb06a233a6..6d8620286b108 100644 --- a/test/Sema/implementation-only-import-suggestion.swift +++ b/test/Sema/implementation-only-import-suggestion.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) // REQUIRES: VENDOR=apple +// REQUIRES: OS=macosx /// Prepare the SDK. // RUN: cp -r %S/Inputs/public-private-sdk %t/sdk @@ -20,6 +21,11 @@ // RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ // RUN: -library-level spi -D PUBLIC_IMPORTS +/// The driver should also accept the flag and pass it along. +// RUN: %swiftc_driver -typecheck -sdk %t/sdk -module-cache-path %t %s \ +// RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ +// RUN: -library-level spi -D PUBLIC_IMPORTS + /// Expect no warnings when building a client with some other library level. // RUN: %target-swift-frontend -typecheck -sdk %t/sdk -module-cache-path %t %s \ // RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ diff --git a/test/Sema/placeholder_type.swift b/test/Sema/placeholder_type.swift new file mode 100644 index 0000000000000..76ce4a13517e2 --- /dev/null +++ b/test/Sema/placeholder_type.swift @@ -0,0 +1,218 @@ +// RUN: %target-typecheck-verify-swift + +let x: _ = 0 // expected-error {{placeholders are not allowed as top-level types}} +let x2 = x +let dict1: [_: Int] = ["hi": 0] +let dict2: [Character: _] = ["h": 0] + +let arr = [_](repeating: "hi", count: 3) + +func foo(_ arr: [_] = [0]) {} // expected-error {{type placeholder not allowed here}} + +let foo = _.foo // expected-error {{placeholders are not allowed as top-level types}} +let zero: _ = .zero // expected-error {{placeholders are not allowed as top-level types}} + +struct S { + var x: T +} + +var s1: S<_> = .init(x: 0) +var s2 = S<_>(x: 0) + +let losslessStringConverter = Double.init as (String) -> _? + +let optInt: _? = 0 +let implicitOptInt: _! = 0 + +let func1: (_) -> Double = { (x: Int) in 0.0 } +let func2: (Int) -> _ = { x in 0.0 } +let func3: (_) -> _ = { (x: Int) in 0.0 } +let func4: (_, String) -> _ = { (x: Int, y: String) in 0.0 } +let func5: (_, String) -> _ = { (x: Int, y: Double) in 0.0 } // expected-error {{cannot convert value of type '(Int, Double) -> Double' to specified type '(_, String) -> _'}} + +let type: _.Type = Int.self +let type2: Int.Type.Type = _.Type.self + +struct MyType1 { + init(t: T, mt2: MyType2) where U == MyType2 {} +} + +struct MyType2 { + init(t: T) {} +} + +let _: MyType2<_> = .init(t: "c" as Character) +let _: MyType1<_, MyType2<_>> = .init(t: "s" as Character, mt2: .init(t: "c" as Character)) + +func dictionary(ofType: [K: V].Type) -> [K: V] { [:] } + +let _: [String: _] = dictionary(ofType: [_: Int].self) +let _: [_: _] = dictionary(ofType: [String: Int].self) +let _: [String: Int] = dictionary(ofType: _.self) // expected-error {{placeholders are not allowed as top-level types}} + +let _: @convention(c) _ = { 0 } // expected-error {{@convention attribute only applies to function types}} +// expected-error@-1 {{placeholders are not allowed as top-level types}} +let _: @convention(c) (_) -> _ = { (x: Double) in 0 } +let _: @convention(c) (_) -> Int = { (x: Double) in 0 } + +struct NonObjc {} + +let _: @convention(c) (_) -> Int = { (x: NonObjc) in 0 } // expected-error {{'(NonObjc) -> Int' is not representable in Objective-C, so it cannot be used with '@convention(c)'}} + +func overload() -> Int? { 0 } +func overload() -> String { "" } + +let _: _? = overload() +let _ = overload() as _? + +struct Bar +where T: ExpressibleByIntegerLiteral, U: ExpressibleByIntegerLiteral { + var t: T + var u: U + func frobnicate() -> Bar { + return Bar(t: 42, u: 42) + } +} + +extension Bar { + func frobnicate2() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}} + return Bar(t: 42, u: 42) + } + func frobnicate3() -> Bar { + return Bar<_, _>(t: 42, u: 42) + } + func frobnicate4() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}} + return Bar<_, _>(t: 42, u: 42) + } + func frobnicate5() -> Bar<_, U> { // expected-error {{type placeholder not allowed here}} + return Bar(t: 42, u: 42) + } + func frobnicate6() -> Bar { + return Bar<_, U>(t: 42, u: 42) + } + func frobnicate7() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}} + return Bar<_, U>(t: 42, u: 42) + } + func frobnicate8() -> Bar<_, U> { // expected-error {{type placeholder not allowed here}} + return Bar<_, _>(t: 42, u: 42) + } +} + +// FIXME: We should probably have better diagnostics for these situations--the user probably meant to use implicit member syntax +let _: Int = _() // expected-error {{placeholders are not allowed as top-level types}} +let _: () -> Int = { _() } // expected-error {{unable to infer closure type in the current context}} expected-error {{placeholders are not allowed as top-level types}} +let _: Int = _.init() // expected-error {{placeholders are not allowed as top-level types}} +let _: () -> Int = { _.init() } // expected-error {{unable to infer closure type in the current context}} expected-error {{placeholders are not allowed as top-level types}} + +func returnsInt() -> Int { _() } // expected-error {{placeholders are not allowed as top-level types}} +func returnsIntClosure() -> () -> Int { { _() } } // expected-error {{unable to infer closure type in the current context}} expected-error {{placeholders are not allowed as top-level types}} +func returnsInt2() -> Int { _.init() } // expected-error {{placeholders are not allowed as top-level types}} +func returnsIntClosure2() -> () -> Int { { _.init() } } // expected-error {{unable to infer closure type in the current context}} expected-error {{placeholders are not allowed as top-level types}} + +let _: Int.Type = _ // expected-error {{'_' can only appear in a pattern or on the left side of an assignment}} +let _: Int.Type = _.self // expected-error {{placeholders are not allowed as top-level types}} + +struct SomeSuperLongAndComplexType {} +func getSomething() -> SomeSuperLongAndComplexType? { .init() } +let something: _! = getSomething() + +extension Array where Element == Int { + static var staticMember: Self { [] } + static func staticFunc() -> Self { [] } + + var member: Self { [] } + func method() -> Self { [] } +} + +extension Array { + static var otherStaticMember: Self { [] } +} + +let _ = [_].staticMember +let _ = [_].staticFunc() +let _ = [_].otherStaticMember.member +let _ = [_].otherStaticMember.method() + +func f(x: Any, arr: [Int]) { + // FIXME: Better diagnostics here. Maybe we should suggest replacing placeholders with 'Any'? + + if x is _ {} // expected-error {{placeholders are not allowed as top-level types}} + if x is [_] {} // expected-error {{type of expression is ambiguous without more context}} + if x is () -> _ {} // expected-error {{type of expression is ambiguous without more context}} + if let y = x as? _ {} // expected-error {{placeholders are not allowed as top-level types}} + if let y = x as? [_] {} // expected-error {{type of expression is ambiguous without more context}} + if let y = x as? () -> _ {} // expected-error {{type of expression is ambiguous without more context}} + let y1 = x as! _ // expected-error {{placeholders are not allowed as top-level types}} + let y2 = x as! [_] // expected-error {{type of expression is ambiguous without more context}} + let y3 = x as! () -> _ // expected-error {{type of expression is ambiguous without more context}} + + switch x { + case is _: break // expected-error {{type placeholder not allowed here}} + case is [_]: break // expected-error {{type placeholder not allowed here}} + case is () -> _: break // expected-error {{type placeholder not allowed here}} + case let y as _: break // expected-error {{type placeholder not allowed here}} + case let y as [_]: break // expected-error {{type placeholder not allowed here}} + case let y as () -> _: break // expected-error {{type placeholder not allowed here}} + } + + if arr is _ {} // expected-error {{placeholders are not allowed as top-level types}} + if arr is [_] {} // expected-error {{type of expression is ambiguous without more context}} + if arr is () -> _ {} // expected-error {{type of expression is ambiguous without more context}} + if let y = arr as? _ {} // expected-error {{placeholders are not allowed as top-level types}} + if let y = arr as? [_] {} // expected-error {{type of expression is ambiguous without more context}} + if let y = arr as? () -> _ {} // expected-error {{type of expression is ambiguous without more context}} + let y1 = arr as! _ // expected-error {{placeholders are not allowed as top-level types}} + let y2 = arr as! [_] // expected-error {{type of expression is ambiguous without more context}} + let y3 = arr as! () -> _ // expected-error {{type of expression is ambiguous without more context}} + + switch arr { + case is _: break // expected-error {{type placeholder not allowed here}} + case is [_]: break // expected-error {{type placeholder not allowed here}} + case is () -> _: break // expected-error {{type placeholder not allowed here}} + case let y as _: break // expected-error {{type placeholder not allowed here}} + case let y as [_]: break // expected-error {{type placeholder not allowed here}} + case let y as () -> _: break // expected-error {{type placeholder not allowed here}} + } +} + +protocol Publisher { + associatedtype Output + associatedtype Failure +} + +struct Just: Publisher { + typealias Failure = Never +} + +struct SetFailureType: Publisher {} + +extension Publisher { + func setFailureType(to: T.Type) -> SetFailureType { + return .init() + } +} + +let _: SetFailureType = Just().setFailureType(to: _.self) // expected-error {{placeholders are not allowed as top-level types}} +let _: SetFailureType = Just().setFailureType(to: [_].self) +let _: SetFailureType Double> = Just().setFailureType(to: ((_) -> _).self) +let _: SetFailureType = Just().setFailureType(to: (_, _).self) + +// TODO: Better error message here? Would be nice if we could point to the placeholder... +let _: SetFailureType = Just().setFailureType(to: _.self).setFailureType(to: String.self) // expected-error {{placeholders are not allowed as top-level types}} + +let _: (_) = 0 as Int // expected-error {{placeholders are not allowed as top-level types}} +let _: Int = 0 as (_) // expected-error {{placeholders are not allowed as top-level types}} + +_ = (1...10) + .map { + ( + $0, + ( + "\($0)", + $0 > 5 + ) + ) + } + .map { (intValue, x: (_, boolValue: _)) in + x.boolValue ? intValue : 0 + } diff --git a/test/Sema/property_wrapper_parameter.swift b/test/Sema/property_wrapper_parameter.swift index f47aa967e52a9..e4c8ca4a122cc 100644 --- a/test/Sema/property_wrapper_parameter.swift +++ b/test/Sema/property_wrapper_parameter.swift @@ -125,3 +125,9 @@ func testResultBuilderWithImplicitWrapper(@ProjectionWrapper value: String) { $value } } + +func takesWrapperClosure(_: ProjectionWrapper<[S]>, closure: (ProjectionWrapper>) -> Void) {} + +func testGenericPropertyWrapper(@ProjectionWrapper wrappers: [S]) { + takesWrapperClosure($wrappers) { $wrapper in } +} diff --git a/test/Sema/property_wrapper_parameter_invalid.swift b/test/Sema/property_wrapper_parameter_invalid.swift index 87e6f99580dc8..6075549e467a8 100644 --- a/test/Sema/property_wrapper_parameter_invalid.swift +++ b/test/Sema/property_wrapper_parameter_invalid.swift @@ -205,3 +205,30 @@ struct OptionalWrapper { // expected-note {{'Value' declared as parameter // expected-error@+2 {{generic parameter 'Value' could not be inferred}} expected-note@+2 {{}} // expected-error@+1 {{property type 'Int' does not match 'wrappedValue' type 'Value?'}} func testWrappedValueMismatch(@OptionalWrapper value: Int) {} + +@propertyWrapper +struct ProjectionWrapper { + var wrappedValue: Value + var projectedValue: Self { self } + init(projectedValue: Self) { self = projectedValue } +} + +func testInvalidWrapperInference() { + struct S { + static func test(_ keyPath: KeyPath) {} // expected-note {{'test' declared here}} + } + + // expected-error@+1 {{trailing closure passed to parameter of type 'KeyPath' that does not accept a closure}} + S.test { $value in } + // expected-error@+1 {{cannot convert value of type '(_) -> ()' to expected argument type 'KeyPath'}} + S.test({ $value in }) + + func testGenericClosure(_ closure: T) {} + // expected-error@+1 {{unable to infer type of a closure parameter '$value' in the current context}} + testGenericClosure { $value in } + testGenericClosure { ($value: ProjectionWrapper) in } // okay + + func testExtraParameter(_ closure: () -> Void) {} + // expected-error@+1 {{contextual closure type '() -> Void' expects 0 arguments, but 1 was used in closure body}} + testExtraParameter { $value in } +} diff --git a/test/Serialization/AllowErrors/invalid-alias.swift b/test/Serialization/AllowErrors/invalid-alias.swift new file mode 100644 index 0000000000000..c1062d29a319f --- /dev/null +++ b/test/Serialization/AllowErrors/invalid-alias.swift @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) + +// RUN: touch %t/empty.swift +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-frontend -verify -emit-module -o %t/errors.partial.swiftmodule -module-name errors -experimental-allow-module-with-compiler-errors %t/errors.swift +// RUN: %target-swift-frontend -emit-module -o %t/errorsempty.partial.swiftmodule -module-name errors %t/empty.swift + +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/errors.swiftmodule -experimental-allow-module-with-compiler-errors %t/errors.partial.swiftmodule %t/errorsempty.partial.swiftmodule + +// RUN: %target-swift-frontend -emit-module -o %t/mods/uses.swiftmodule -experimental-allow-module-with-compiler-errors -I %t/mods %t/uses.swift 2>&1 | %FileCheck -check-prefix=CHECK-USES %s + +// BEGIN errors.swift +typealias AnAlias = undefined // expected-error {{cannot find type 'undefined'}} + +// BEGIN uses.swift +import errors +func test(a: AnAlias) {} +// CHECK-USES-NOT: cannot find type 'AnAlias' in scope diff --git a/test/Serialization/AllowErrors/invalid-deinit.swift b/test/Serialization/AllowErrors/invalid-deinit.swift new file mode 100644 index 0000000000000..f49ea87c9090b --- /dev/null +++ b/test/Serialization/AllowErrors/invalid-deinit.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) + +// Serialize and deserialize a deinit with SourceFile context to make sure we +// don't crash +// RUN: %target-swift-frontend -verify -module-name errors -emit-module -o %t/errors.swiftmodule -experimental-allow-module-with-compiler-errors %s +// RUN: %target-swift-ide-test -print-module -module-to-print=errors -source-filename=x -I %t -allow-compiler-errors + +// Also check it wasn't serialized +// RUN: llvm-bcanalyzer -dump %t/errors.swiftmodule | %FileCheck %s +// CHECK-NOT: DESTRUCTOR_DECL + +struct Foo {} + +@discardableResult // expected-error{{'@discardableResult' attribute cannot be applied to this declaration}} +deinit {} // expected-error{{deinitializers may only be declared within a class}} + +func foo() -> Foo { return Foo() } + +// Make sure @discardableResult isn't added to `foo`, which could be possible +// if the deinit is partially serialized +foo() // expected-warning{{result of call to 'foo()' is unused}} diff --git a/test/Serialization/AllowErrors/invalid-inheritance.swift b/test/Serialization/AllowErrors/invalid-inheritance.swift new file mode 100644 index 0000000000000..fe5bd4e711d47 --- /dev/null +++ b/test/Serialization/AllowErrors/invalid-inheritance.swift @@ -0,0 +1,52 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/mods) + +// RUN: touch %t/empty.swift +// RUN: %{python} %utils/split_file.py -o %t %s + +// Errors often only occur during merging, hence creating an empty module here +// RUN: %target-swift-frontend -verify -module-name errors -emit-module -o %t/mods/errorsmain.partial.swiftmodule -experimental-allow-module-with-compiler-errors %t/errors.swift +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errorsempty.partial.swiftmodule %t/empty.swift +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errors.swiftmodule -experimental-allow-module-with-compiler-errors %t/mods/errorsmain.partial.swiftmodule %t/mods/errorsempty.partial.swiftmodule + +// RUN: %target-swift-frontend -emit-module -o %t/mods/uses.swiftmodule -experimental-allow-module-with-compiler-errors -I %t/mods %t/uses.swift 2>&1 | %FileCheck -check-prefix=CHECK-USES %s + +// BEGIN errors.swift +public protocol SomeProto: undefined {} // expected-error {{cannot find type 'undefined'}} +public class SomeClass: undefined {} // expected-error {{cannot find type 'undefined'}} +public struct SomeStruct: undefined {} // expected-error {{cannot find type 'undefined'}} +public enum SomeEnum: undefined { // expected-error {{cannot find type 'undefined'}} + case a +} + +public class GenericClass {} +public class InvalidGenericSuperclass: GenericClass {} // expected-error {{cannot find type 'undefined'}} + +extension SomeClass: undefined {} // expected-error {{cannot find type 'undefined'}} +extension SomeStruct: undefined {} // expected-error {{cannot find type 'undefined'}} +extension SomeEnum: undefined {} // expected-error {{cannot find type 'undefined'}} + +extension undefined {} // expected-error {{cannot find type 'undefined'}} +extension undefined: undefined {} // expected-error {{cannot find type 'undefined'}} +extension undefined: SomeProto {} // expected-error {{cannot find type 'undefined'}} + +public extension undefined { // expected-error {{cannot find type 'undefined' in scope}} + protocol SomeProtoInner: undefined {} // expected-error {{cannot find type 'undefined' in scope}} + // TODO: Why don't these have errors? + class SomeClassInner: undefined {} + struct SomeStructInner: undefined {} + enum SomeEnumInner: undefined { // expected-error {{cannot find type 'undefined' in scope}} + case a + } + class InvalidGenericSuperclassInner: GenericClass {} // expected-error {{cannot find type 'undefined' in scope}} +} + + +// BEGIN uses.swift +import errors +func test(p: SomeProto, c: SomeClass, s: SomeStruct, e: SomeEnum, g: InvalidGenericSuperclass) {} +// CHECK-USES-NOT: cannot find type 'SomeProto' in scope +// CHECK-USES-NOT: cannot find type 'SomeClass' in scope +// CHECK-USES-NOT: cannot find type 'SomeStruct' in scope +// CHECK-USES-NOT: cannot find type 'SomeEnum' in scope +// CHECK-USES-NOT: cannot find type 'InvalidGenericSuperclass' in scope diff --git a/test/Serialization/AllowErrors/invalid-members.swift b/test/Serialization/AllowErrors/invalid-members.swift new file mode 100644 index 0000000000000..f9030dec218a6 --- /dev/null +++ b/test/Serialization/AllowErrors/invalid-members.swift @@ -0,0 +1,72 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/mods) + +// RUN: touch %t/empty.swift +// RUN: %{python} %utils/split_file.py -o %t %s + +// Errors often only occur during merging, hence creating an empty module here +// RUN: %target-swift-frontend -verify -module-name errors -emit-module -o %t/mods/errorsmain.partial.swiftmodule -experimental-allow-module-with-compiler-errors %t/errors.swift +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errorsempty.partial.swiftmodule %t/empty.swift +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errors.swiftmodule -experimental-allow-module-with-compiler-errors %t/mods/errorsmain.partial.swiftmodule %t/mods/errorsempty.partial.swiftmodule + +// RUN: %target-swift-frontend -emit-module -o %t/mods/uses.swiftmodule -experimental-allow-module-with-compiler-errors -I %t/mods %t/uses.swift 2>&1 | %FileCheck -check-prefix=CHECK-USES %s + +// BEGIN errors.swift +public struct ValidStructInvalidMember { + public var member: String + public let memberMissingType: undefined // expected-error {{cannot find type 'undefined'}} + + public var memberMissingTypeValidSets: undefined { // expected-error {{cannot find type 'undefined'}} + willSet { + print("Setting value \(newValue)") + } + didSet { + print("Set value \(oldValue)") + } + } + public var memberInvalidSets: Int { + willSet { + undefined // expected-error {{cannot find 'undefined'}} + } + didSet { + undefined // expected-error {{cannot find 'undefined'}} + } + } + + public lazy var lazyMemberMissingTypeValidBody: undefined = { // expected-error {{cannot find type 'undefined'}} + return "" + }() + public lazy var lazyMemberInvalidBody: String = { + return undefined // expected-error {{cannot find 'undefined'}} + }() + + public var memberMissingTypeValidGetSets: String { + get { member } + set { member = "" } + } + public var memberInvalidGetSet: String { + get { undefined } // expected-error {{cannot find 'undefined'}} + set { undefined = "" } // expected-error {{cannot find 'undefined'}} + } + + public func someFunc() {} + public func funcBadArg(_ arg: undefined? = nil) {} // expected-error {{cannot find type 'undefined'}} +} + + +// BEGIN uses.swift +import errors + +func test(s: ValidStructInvalidMember) { + s.someFunc() + s.BOOOP() + print(s.member) + + s.funcBadArg() + print(s.memberMissingType) +} + +// CHECK-USES-NOT: has no member 'someFunc' +// CHECK-USES-NOT: has no member 'member' +// CHECK-USES-NOT: has no member 'funcBadArg' +// CHECK-USES-NOT: has no member 'memberMissingType' diff --git a/test/Serialization/AllowErrors/invalid-top.swift b/test/Serialization/AllowErrors/invalid-top.swift new file mode 100644 index 0000000000000..07ba3cc70a601 --- /dev/null +++ b/test/Serialization/AllowErrors/invalid-top.swift @@ -0,0 +1,57 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/mods) + +// RUN: touch %t/empty.swift +// RUN: %{python} %utils/split_file.py -o %t %s + +// Errors often only occur during merging, hence creating an empty module here +// RUN: %target-swift-frontend -verify -module-name errors -emit-module -o %t/mods/errorsmain.partial.swiftmodule -experimental-allow-module-with-compiler-errors %t/errors.swift +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errorsempty.partial.swiftmodule %t/empty.swift +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errors.swiftmodule -experimental-allow-module-with-compiler-errors %t/mods/errorsmain.partial.swiftmodule %t/mods/errorsempty.partial.swiftmodule + +// RUN: %target-swift-frontend -emit-module -o %t/mods/validUses.swiftmodule -experimental-allow-module-with-compiler-errors -I%t/mods %t/valid-uses.swift 2>&1 | %FileCheck -check-prefix=CHECK-VALID %s + +// RUN: %target-swift-frontend -emit-module -o %t/mods/invalidUses.swiftmodule -experimental-allow-module-with-compiler-errors -I%t/mods %t/invalid-uses.swift 2>&1 | %FileCheck -check-prefix=CHECK-INVALID %s + +// BEGIN errors.swift +public struct ValidType {} + +public func validFunc() -> ValidType { return ValidType() } + +public func invalidFuncBody() -> ValidType { + ret // expected-error {{cannot find 'ret'}} +} + +public func invalidFuncRet() -> undefined {} // expected-error {{cannot find type 'undefined'}} + + +// BEGIN valid-uses.swift +import errors + +func test(s: ValidType) { + print(validFunc()) + print(invalidFuncBody()) +} + +// Check SIL diagnostics are still output (no reason not to output SIL since +// there were no errors) +func other() -> Int {} +// CHECK-VALID: valid-uses.swift:10:22: error: missing return +func other2() -> Bool {} +// CHECK-VALID: valid-uses.swift:12:24: error: missing return + + +// BEGIN invalid-uses.swift +import errors + +func test() { + invalidFuncRet() +} + +// All invalid uses should have no errors in the file itself - all referenced +// invalid declarations should have an error elsewhere (but we don't care what +// that location is) +// CHECK-INVALID-NOT: invalid-uses.swift:{{.*}} error: +// CHECK-INVALID: error: allowing deserialization of error type '' in module 'errors' +// CHECK-INVALID: error: allowing deserialization of invalid declaration 'invalidFuncRet()' (global function) in module 'errors' +// CHECK-INVALID-NOT: invalid-uses.swift:{{.*}} error: diff --git a/test/Serialization/AllowErrors/invalid-xref.swift b/test/Serialization/AllowErrors/invalid-xref.swift new file mode 100644 index 0000000000000..24ad46e56b4e7 --- /dev/null +++ b/test/Serialization/AllowErrors/invalid-xref.swift @@ -0,0 +1,44 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/mods) + +// RUN: touch %t/empty.swift +// RUN: %{python} %utils/split_file.py -o %t %s + +// We're going to swap A and B around to cause an invalid xref +// RUN: %target-swift-frontend -emit-module -o %t/mods/A.swiftmodule -module-name A %t/lib.swift +// RUN: %target-swift-frontend -emit-module -o %t/mods/B.swiftmodule -module-name B %t/empty.swift + +// Compile using SomeType from A +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errorsmain.partial.swiftmodule -I %t/mods %t/errors.swift +// Empty module so we can do a merge modules step +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errorsempty.partial.swiftmodule %t/empty.swift + +// Swap A and B +// RUN: %target-swift-frontend -emit-module -o %t/mods/A.swiftmodule -module-name A %t/empty.swift +// RUN: %target-swift-frontend -emit-module -o %t/mods/B.swiftmodule -module-name B %t/lib.swift + +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/mods/errors.swiftmodule -experimental-allow-module-with-compiler-errors %t/mods/errorsmain.partial.swiftmodule %t/mods/errorsempty.partial.swiftmodule + +// Expect this to crash without allowing errors (we should never get into a +// situation where merge modules is run with MissingMemberDecls) +// RUN: not --crash %target-swift-frontend -module-name errors -emit-module -o %t/mods/errors.swiftmodule %t/mods/errorsmain.partial.swiftmodule %t/mods/errorsempty.partial.swiftmodule + +// Needed for the "crash" test. +// REQUIRES: asserts + +// BEGIN lib.swift +public struct SomeType { + public init() {} +} + + +// BEGIN errors.swift +import A +import B + +public class SomeClass { + public let member: SomeType + public init(member: SomeType) { + self.member = member + } +} diff --git a/test/Serialization/AllowErrors/removed-decls.swift b/test/Serialization/AllowErrors/removed-decls.swift new file mode 100644 index 0000000000000..d9b988d1d2ccd --- /dev/null +++ b/test/Serialization/AllowErrors/removed-decls.swift @@ -0,0 +1,102 @@ +// REQUIRES: objc_interop + +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/swiftmods %t/objcmods %t/objc +// RUN: %{python} %utils/split_file.py -o %t %s + +// Create a module A, then B that depends on A, replace A with an empty module, +// and then try make a C that depends on B + +// RUN: %target-swift-frontend -module-name A -emit-module -o %t/swiftmods/A.swiftmodule %t/a.swift +// RUN: %target-swift-frontend -module-name B -emit-module -o %t/swiftmods/B.swiftmodule -I %t/swiftmods %t/b.swift +// RUN: %target-swift-frontend -module-name A -emit-module -o %t/swiftmods/A.swiftmodule %t/bada.swift +// RUN: %target-swift-frontend -wmo -module-name C -emit-module -o %t/swiftmods/C.swiftmodule -I %t/swiftmods -experimental-allow-module-with-compiler-errors -index-store-path %t/idx %t/c.swift +// RUN: not --crash %target-swift-frontend -module-name C -emit-module -o %t/swiftmods/C.swiftmodule -I %t/swiftmods %t/c.swift + +// Now do a similar thing but just use different headers instead (ie. to test +// loading from a PCM rather than swiftmodule) + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -module-name B -emit-module -o %t/objcmods/B.swiftmodule -I %t/objc -module-cache-path %t/mcp %t/b.swift +// RUN: mv %t/objc/bada.h %t/objc/a.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -module-name C -emit-module -o %t/objcmods/C.swiftmodule -I %t/objcmods -I %t/objc -module-cache-path %t/mcp -experimental-allow-module-with-compiler-errors -index-store-path %t/idx %t/c.swift +// RUN: not --crash %target-swift-frontend(mock-sdk: %clang-importer-sdk) -module-name C -emit-module -o %t/objcmods/C.swiftmodule -I %t/objcmods -I %t/objc -module-cache-path %t/mcp %t/c.swift + +// BEGIN a.swift +public protocol ProtoA {} +public protocol MissingProto {} +open class MissingClass: ProtoA {} + +// BEGIN bada.swift +public protocol ProtoA {} + +// BEGIN objc/a.h +@import Foundation; +@protocol ProtoA +@end +@protocol MissingProto +@end +@interface MissingClass +@end + +// BEGIN objc/bada.h +@import Foundation; +@protocol ProtoA +@end + +// BEGIN objc/module.modulemap +module A { + header "a.h" +} + +// BEGIN b.swift +import A + +public protocol ProtoB: MissingProto {} +open class ClassB: MissingProto {} +open class InheritingClassB: MissingClass {} + +public protocol ATProto { + associatedtype Value +} +public protocol ReqProto: ATProto { + static func thing(_ value: Value) +} +extension ReqProto where Value: ProtoA { + public static func thing(_ value: Value) {} +} +public enum EnumB: ReqProto { + public typealias Value = MissingClass + case a +} + +// BEGIN c.swift +import B + +func useB(p: ProtoB, c: ClassB, i: InheritingClassB, e: EnumB) { + print("p:\(p) c:\(c) i:\(i) e:\(e)") + EnumB.thing(i) +} + +public protocol ProtoC: ProtoB {} +public class ClassC: ClassB {} +public class ClassC2: InheritingClassB {} + +public struct AllAsMembers { + let p: ProtoB + let c: ClassB + let i: InheritingClassB + let e: EnumB +} + +extension ProtoB { + func newProtoBMethod() {} +} +extension ClassB { + func newClassBMethod() {} +} +extension InheritingClassB { + func newInheritingClassBMethod() {} +} +extension EnumB { + func newEnumBMethod() {} +} diff --git a/test/Serialization/Inputs/actor_bar.swift b/test/Serialization/Inputs/actor_bar.swift new file mode 100644 index 0000000000000..81551d4e53b64 --- /dev/null +++ b/test/Serialization/Inputs/actor_bar.swift @@ -0,0 +1 @@ +public actor Bar {} diff --git a/test/Serialization/Inputs/def_concurrency.sil b/test/Serialization/Inputs/def_concurrency.sil index 266782eb53c6b..0c489762b6acc 100644 --- a/test/Serialization/Inputs/def_concurrency.sil +++ b/test/Serialization/Inputs/def_concurrency.sil @@ -13,6 +13,8 @@ sil [transparent] [serialized] @test_hop_to_executor_actor : $@convention(thin) bb0(%0 : $Act): // CHECK: hop_to_executor %0 : $Act hop_to_executor %0 : $Act + // CHECK: hop_to_executor [mandatory] %0 : $Act + hop_to_executor [mandatory] %0 : $Act %2 = tuple () return %2 : $() } diff --git a/test/Serialization/Inputs/def_distributed.swift b/test/Serialization/Inputs/def_distributed.swift new file mode 100644 index 0000000000000..f82c8c5ac2bfa --- /dev/null +++ b/test/Serialization/Inputs/def_distributed.swift @@ -0,0 +1,8 @@ +import _Distributed + +@available(SwiftStdlib 5.5, *) +public distributed actor DA { + public distributed func doSomethingDistributed(param: String) async -> Int { + return 0 + } +} diff --git a/test/Serialization/Inputs/def_transparent.swift b/test/Serialization/Inputs/def_transparent.swift index 5059531f47c88..dd5cd54925a51 100644 --- a/test/Serialization/Inputs/def_transparent.swift +++ b/test/Serialization/Inputs/def_transparent.swift @@ -2,10 +2,10 @@ return x } -@_transparent public func testBuiltin() -> Int32 { +@_transparent public func testBuiltin() -> (Int32, String) { var y: Int32 = 300 var z = "foo" - return y + return (y, z) } @_transparent public func standalone_function(x x: Int32, y: Int32) -> Int32 { diff --git a/test/Serialization/Recovery/crash-recovery.swift b/test/Serialization/Recovery/crash-recovery.swift index 6b5c6784d8c01..b49bcc665b8dd 100644 --- a/test/Serialization/Recovery/crash-recovery.swift +++ b/test/Serialization/Recovery/crash-recovery.swift @@ -13,9 +13,9 @@ public class Sub: Base { } // CHECK-CRASH: error: fatal error encountered while reading from module 'Lib'; please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project -// CHECK-CRASH: note: module 'Lib' full misc version is -// CHECK-CRASH-4-NOT: note: compiling as -// CHECK-CRASH-4_2: note: compiling as Swift 4.2, with 'Lib' built as Swift 4.1.50 -// CHECK-CRASH-LABEL: *** DESERIALIZATION FAILURE (please include this section in any bug report) *** -// CHECK-CRASH: could not find 'disappearingMethod()' in parent class +// CHECK-CRASH-4: Compiling with effective version 4.1.50 +// CHECK-CRASH-4_2: Compiling with effective version 4.2 // CHECK-CRASH: While loading members for 'Sub' (in module 'Lib') +// CHECK-CRASH-LABEL: *** DESERIALIZATION FAILURE *** +// CHECK-CRASH: module 'Lib' with full misc version {{.*}}4.1.50 +// CHECK-CRASH: could not find 'disappearingMethod()' in parent class diff --git a/test/Serialization/Recovery/crash-xref.swift b/test/Serialization/Recovery/crash-xref.swift index 34b0c13592d1a..d8a0bd59d877f 100644 --- a/test/Serialization/Recovery/crash-xref.swift +++ b/test/Serialization/Recovery/crash-xref.swift @@ -1,36 +1,53 @@ /// Test xref error description by removing a type from a module after building /// a client. // RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/partials) +// RUN: %empty-directory(%t/normal) +// RUN: %empty-directory(%t/errors) /// Compile module A with a type and an empty module B. -// RUN: %target-swift-frontend %s -emit-module-path %t/A.swiftmodule -module-name A -D LIB -// RUN: %target-swift-frontend %s -emit-module-path %t/B.swiftmodule -module-name B +// RUN: %target-swift-frontend %s -emit-module-path %t/partials/A.swiftmodule -module-name A -D LIB +// RUN: %target-swift-frontend %s -emit-module-path %t/partials/B.swiftmodule -module-name B + +/// Compile a client using the type from A. +// RUN: %target-swift-frontend %s -emit-module-path %t/normal/Client.swiftmodule -module-name Client -D CLIENT -I %t/partials +// RUN: %target-swift-frontend %s -emit-module-path %t/errors/Client.swiftmodule -module-name Client -D CLIENT -I %t/partials -experimental-allow-module-with-compiler-errors + +/// Swap A and B around! A is now empty and B defines the type. +// RUN: %target-swift-frontend %s -emit-module-path %t/partials/A.swiftmodule -module-name A +// RUN: %target-swift-frontend %s -emit-module-path %t/partials/B.swiftmodule -module-name B -D LIB + +/// Read from the client to get an xref error to the type missing from A. +// RUN: not --crash %target-swift-frontend -emit-sil %t/normal/Client.swiftmodule -module-name Client -I %t/partials -disable-deserialization-recovery 2> %t/normal_stderr +// RUN: not --crash %target-swift-frontend -emit-sil %t/errors/Client.swiftmodule -module-name Client -I %t/partials -disable-deserialization-recovery 2> %t/error_stderr + +// RUN: cat %t/normal_stderr | %FileCheck %s -check-prefixes=NORMALFAILURE +// NORMALFAILURE-LABEL: *** DESERIALIZATION FAILURE *** +// NORMALFAILURE-NEXT: module 'Client' with full misc version {{.*}}' +// NORMALFAILURE-NEXT: Could not deserialize type for 'foo()' +// NORMALFAILURE-NEXT: Caused by: top-level value not found +// NORMALFAILURE-NEXT: Cross-reference to module 'A' +// NORMALFAILURE-NEXT: ... SomeType +// NORMALFAILURE-NEXT: Notes: +// NORMALFAILURE-NEXT: * There is a matching 'SomeType' in module 'B'. If this is imported from clang, please make sure the header is part of a single clang module. + +// RUN: cat %t/error_stderr | %FileCheck %s -check-prefixes=ALLOWFAILURE +// ALLOWFAILURE-LABEL: *** DESERIALIZATION FAILURE *** +// ALLOWFAILURE-NEXT: module 'Client' with full misc version {{.*}}' (built with -experimental-allow-module-with-compiler-errors) +// ALLOWFAILURE-NEXT: Could not deserialize type for 'foo()' +// ALLOWFAILURE-NEXT: Caused by: top-level value not found +// ALLOWFAILURE-NEXT: Cross-reference to module 'A' +// ALLOWFAILURE-NEXT: ... SomeType +// ALLOWFAILURE-NEXT: Notes: +// ALLOWFAILURE-NEXT: * There is a matching 'SomeType' in module 'B'. If this is imported from clang, please make sure the header is part of a single clang module. + #if LIB public struct SomeType { public init() {} } - -/// Compile a client using the type from A. -// RUN: %target-swift-frontend %s -emit-module-path %t/Client.swiftmodule -module-name Client -D CLIENT -I %t #elseif CLIENT import A import B public func foo() -> A.SomeType { fatalError() } - -/// Swap A and B around! A is now empty and B defines the type. -// RUN: %target-swift-frontend %s -emit-module-path %t/A.swiftmodule -module-name A -// RUN: %target-swift-frontend %s -emit-module-path %t/B.swiftmodule -module-name B -D LIB #endif // Empty - -/// Read from the client to get an xref error to the type missing from A. -// RUN: not --crash %target-swift-frontend -emit-sil %t/Client.swiftmodule -module-name Client -I %t -disable-deserialization-recovery 2> %t/stderr - -// RUN: cat %t/stderr | %FileCheck %s -// CHECK: *** DESERIALIZATION FAILURE (please include this section in any bug report) *** -// CHECK-NEXT: Could not deserialize type for 'foo()' -// CHECK-NEXT: Caused by: top-level value not found -// CHECK-NEXT: Cross-reference to module 'A' -// CHECK-NEXT: ... SomeType -// CHECK-NEXT: Notes: -// CHECK-NEXT: * There is a matching 'SomeType' in module 'B'. If this is imported from clang, please make sure the header is part of a single clang module. diff --git a/test/Serialization/Recovery/implementation-only-missing.swift b/test/Serialization/Recovery/implementation-only-missing.swift index d309ee302fe20..5e5ad130a386a 100644 --- a/test/Serialization/Recovery/implementation-only-missing.swift +++ b/test/Serialization/Recovery/implementation-only-missing.swift @@ -12,8 +12,9 @@ //// The client app should build OK without the private module. Removing the //// private module is superfluous but makes sure that it's not somehow loaded. // RUN: rm %t/private_lib.swiftmodule -// RUN: %target-swift-frontend -typecheck -DCLIENT_APP -primary-file %s -I %t -index-system-modules -index-store-path %t -// RUN: %target-swift-frontend -emit-sil -DCLIENT_APP -primary-file %s -I %t -module-name client +// RUN: %target-swift-frontend -typecheck -DCLIENT_APP %s -I %t -index-system-modules -index-store-path %t +// RUN: %target-swift-frontend -typecheck -DCLIENT_APP %s -I %t -D FAIL_TYPECHECK -verify +// RUN: %target-swift-frontend -emit-sil -DCLIENT_APP %s -I %t -module-name client //// Printing the public module should not crash when checking for overrides of //// methods from the private module. @@ -42,10 +43,14 @@ public protocol HiddenProtocol { associatedtype Value } +public protocol HiddenProtocol2 {} + public protocol HiddenProtocolWithOverride { func hiddenOverride() } +public class HiddenClass {} + #elseif PUBLIC_LIB @_implementationOnly import private_lib @@ -91,6 +96,19 @@ public struct PublicStructConformsToHiddenProtocol: RefinesHiddenProtocol { public init() { } } +public class SomeClass { + func funcUsingIoiType(_ a: HiddenClass) {} +} + +// Check that we recover from a reference to an implementation-only +// imported type in a protocol composition. rdar://78631465 +protocol CompositionMemberInheriting : HiddenProtocol2 {} +protocol CompositionMemberSimple {} +protocol InheritingFromComposition : CompositionMemberInheriting & CompositionMemberSimple {} +struct StructInheritingFromComposition : CompositionMemberInheriting & CompositionMemberSimple {} +class ClassInheritingFromComposition : CompositionMemberInheriting & CompositionMemberSimple {} +protocol InheritingFromCompositionDirect : CompositionMemberSimple & HiddenProtocol2 {} + #elseif CLIENT_APP import public_lib @@ -101,4 +119,12 @@ print(s.nonWrappedVar) var p = PublicStructConformsToHiddenProtocol() print(p) +#if FAIL_TYPECHECK + // Access to a missing member on an AnyObject triggers a typo correction + // that looks at *all* class members. rdar://79427805 + class ClassUnrelatedToSomeClass {} + var something = ClassUnrelatedToSomeClass() as AnyObject + something.triggerTypoCorrection = 123 // expected-error {{value of type 'AnyObject' has no member 'triggerTypoCorrection'}} +#endif + #endif diff --git a/test/Serialization/Recovery/types-5-to-4.swift b/test/Serialization/Recovery/types-5-to-4.swift index a64544c1d7c7b..eae0b0914439c 100644 --- a/test/Serialization/Recovery/types-5-to-4.swift +++ b/test/Serialization/Recovery/types-5-to-4.swift @@ -16,8 +16,8 @@ import Lib func requiresConformance(_: B_RequiresConformance) {} func requiresConformance(_: B_RequiresConformance) {} -class Sub: Base {} // expected-error {{cannot inherit from class 'Base' (compiled with Swift 5.5) because it has overridable members that could not be loaded in Swift 4.1.50}} -class Impl: Proto {} // expected-error {{type 'Impl' cannot conform to protocol 'Proto' (compiled with Swift 5.5) because it has requirements that could not be loaded in Swift 4.1.50}} +class Sub: Base {} // expected-error {{cannot inherit from class 'Base' (compiled with Swift 5.6) because it has overridable members that could not be loaded in Swift 4.1.50}} +class Impl: Proto {} // expected-error {{type 'Impl' cannot conform to protocol 'Proto' (compiled with Swift 5.6) because it has requirements that could not be loaded in Swift 4.1.50}} #else // TEST diff --git a/test/Serialization/actor-attr-query-cycle.swift b/test/Serialization/actor-attr-query-cycle.swift new file mode 100644 index 0000000000000..be3f0623f6488 --- /dev/null +++ b/test/Serialization/actor-attr-query-cycle.swift @@ -0,0 +1,14 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -emit-module -o %t/a.swiftmodule -emit-module-source-info-path %t/a.swiftsourceinfo -primary-file %s %S/Inputs/actor_bar.swift -module-name Foo -disable-availability-checking +// RUN: %target-swift-frontend -emit-module -o %t/b.swiftmodule -emit-module-source-info-path %t/b.swiftsourceinfo -primary-file %S/Inputs/actor_bar.swift %s -module-name Foo -disable-availability-checking +// RUN: %target-swift-frontend -merge-modules -emit-module -o %t/Foo.swiftmodule -emit-module-source-info-path %t/Foo.swiftsourceinfo %t/a.swiftmodule %t/b.swiftmodule -module-name Foo -disable-availability-checking + +// REQUIRES: concurrency + +extension Bar { + @MainActor + func bar() async throws -> Int { + return 42 + } +} diff --git a/test/Serialization/async.swift b/test/Serialization/async.swift index d0275a936788b..eac7d6456d37d 100644 --- a/test/Serialization/async.swift +++ b/test/Serialization/async.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %empty-directory(%t-scratch) -// RUN: %target-swift-frontend -emit-module -o %t-scratch/def_async~partial.swiftmodule -primary-file %S/Inputs/def_async.swift -module-name def_async -enable-experimental-concurrency -// RUN: %target-swift-frontend -merge-modules -emit-module -parse-as-library -enable-testing %t-scratch/def_async~partial.swiftmodule -module-name def_async -o %t/def_async.swiftmodule -enable-experimental-concurrency -// RUN: %target-swift-frontend -typecheck -I%t -verify %s -verify-ignore-unknown -enable-experimental-concurrency +// RUN: %target-swift-frontend -emit-module -o %t-scratch/def_async~partial.swiftmodule -primary-file %S/Inputs/def_async.swift -module-name def_async -disable-availability-checking +// RUN: %target-swift-frontend -merge-modules -emit-module -parse-as-library -enable-testing %t-scratch/def_async~partial.swiftmodule -module-name def_async -o %t/def_async.swiftmodule -disable-availability-checking +// RUN: %target-swift-frontend -typecheck -I%t -verify %s -verify-ignore-unknown -disable-availability-checking // REQUIRES: concurrency diff --git a/test/Serialization/async_initializers.swift b/test/Serialization/async_initializers.swift index b3ca0457c69c0..fa4f289bf8e84 100644 --- a/test/Serialization/async_initializers.swift +++ b/test/Serialization/async_initializers.swift @@ -1,8 +1,7 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module-path %t/a.swiftmodule -module-name a %s +// RUN: %target-swift-frontend -disable-availability-checking -emit-module-path %t/a.swiftmodule -module-name a %s +// RUN: llvm-bcanalyzer -dump %t/a.swiftmodule | %FileCheck --implicit-check-not UnknownCode %s // RUN: %target-swift-ide-test -print-module -module-to-print a -source-filename x -I %t | %FileCheck -check-prefix MODULE-CHECK %s -// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module-path %t/b.swiftmodule -module-name a %t/a.swiftmodule -// RUN: cmp -s %t/a.swiftmodule %t/b.swiftmodule // REQUIRES: concurrency @@ -14,7 +13,6 @@ // MODULE-CHECK: actor A { // MODULE-CHECK-NEXT: init() async - actor A { init() async {} } @@ -25,20 +23,18 @@ class C { init() async {} } +// MODULE-CHECK: enum E { +// MODULE-CHECK-NEXT: case nothing +// MODULE-CHECK-NEXT: init() async +enum E { + case nothing + init() async { + self = .nothing + } +} + // MODULE-CHECK: struct S { // MODULE-CHECK-NEXT: init() async struct S { init() async {} } - -// ignore-----MODULE-CHECK: enum E { -// ignore-----MODULE-CHECK-NEXT: case nothing -// ignore-----MODULE-CHECK-NEXT: init() async - -// FIXME: until rdar://76678907 is fixed, this won't work. -// enum E { -// case nothing -// init() async { -// self = .nothing -// } -// } diff --git a/test/Serialization/attr-actorindependent.swift b/test/Serialization/attr-actorindependent.swift deleted file mode 100644 index 25abd5696a588..0000000000000 --- a/test/Serialization/attr-actorindependent.swift +++ /dev/null @@ -1,45 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module-path %t/a.swiftmodule -module-name a %s -// RUN: llvm-bcanalyzer -dump %t/a.swiftmodule | %FileCheck -check-prefix BC-CHECK %s -// RUN: %target-swift-ide-test -print-module -module-to-print a -source-filename x -I %t | %FileCheck -check-prefix MODULE-CHECK %s -// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module-path %t/b.swiftmodule -module-name a %t/a.swiftmodule -// RUN: cmp -s %t/a.swiftmodule %t/b.swiftmodule - -// REQUIRES: concurrency - -/////////// -// This test checks for correct serialization & deserialization of -// @actorIndependent and @actorIndependent(unsafe) - -// look for correct annotation after first deserialization's module print: - -// MODULE-CHECK: actor UnsafeCounter { -// MODULE-CHECK-NEXT: @actorIndependent(unsafe) var storage: Int -// MODULE-CHECK-NEXT: @actorIndependent var count: Int -// MODULE-CHECK-NEXT: var actorCount: Int -// MODULE-CHECK-NEXT: init() -// MODULE-CHECK-NEXT: } - -// and look for unsafe and safe versions of decl in BC dump: - -// BC-CHECK-NOT: UnknownCode -// BC-CHECK: -// BC-CHECK: - - -actor UnsafeCounter { - - @actorIndependent(unsafe) - private var storage : Int = 0 - - @actorIndependent - var count : Int { - get { storage } - set { storage = newValue } - } - - var actorCount : Int { - get { storage } - set { storage = newValue } - } -} diff --git a/test/Serialization/attr-nonisolated.swift b/test/Serialization/attr-nonisolated.swift new file mode 100644 index 0000000000000..f274966a5ea89 --- /dev/null +++ b/test/Serialization/attr-nonisolated.swift @@ -0,0 +1,34 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -disable-availability-checking -emit-module-path %t/a.swiftmodule -module-name a %s +// RUN: llvm-bcanalyzer -dump %t/a.swiftmodule | %FileCheck --check-prefix BC-CHECK --implicit-check-not UnknownCode %s +// RUN: %target-swift-ide-test -print-module -module-to-print a -source-filename x -I %t | %FileCheck --check-prefix MODULE-CHECK %s + +// REQUIRES: concurrency + +/////////// +// This test checks for correct serialization & deserialization of +// nonisolated + +// look for correct annotation after first deserialization's module print: + +// MODULE-CHECK: actor UnsafeCounter { +// MODULE-CHECK-NEXT: var storage: Int +// MODULE-CHECK-NEXT: nonisolated var count: Int +// MODULE-CHECK-NEXT: init() +// MODULE-CHECK-NEXT: } + +// and look for nonisolated + +// BC-CHECK: + + +actor UnsafeCounter { + + private var storage : Int = 0 + + nonisolated + var count : Int { + get { 0 } + set { } + } +} diff --git a/test/Serialization/autolinking.swift b/test/Serialization/autolinking.swift index 5cf99c40b4a69..fdde3623e6998 100644 --- a/test/Serialization/autolinking.swift +++ b/test/Serialization/autolinking.swift @@ -1,12 +1,14 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -parse-stdlib -o %t -module-name someModule -module-link-name module %S/../Inputs/empty.swift // RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version none -emit-ir -lmagic %s -I %t > %t/out.txt +// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-concurrency -runtime-compatibility-version none -emit-ir -lmagic %s -I %t > %t/out.txt // RUN: %FileCheck %s < %t/out.txt // RUN: %FileCheck -check-prefix=NO-FORCE-LOAD %s < %t/out.txt // RUN: %empty-directory(%t/someModule.framework/Modules/someModule.swiftmodule) // RUN: mv %t/someModule.swiftmodule %t/someModule.framework/Modules/someModule.swiftmodule/%target-swiftmodule-name // RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version none -emit-ir -lmagic %s -F %t > %t/framework.txt +// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-concurrency -runtime-compatibility-version none -emit-ir -lmagic %s -F %t > %t/framework.txt // RUN: %FileCheck -check-prefix=FRAMEWORK %s < %t/framework.txt // RUN: %FileCheck -check-prefix=NO-FORCE-LOAD %s < %t/framework.txt @@ -19,9 +21,16 @@ // RUN: %FileCheck -check-prefix NO-FORCE-LOAD-CLIENT %s < %t/force-load.txt // RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version none -emit-ir -parse-stdlib -module-name someModule -module-link-name module %S/../Inputs/empty.swift | %FileCheck --check-prefix=NO-FORCE-LOAD %s +// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-concurrency -runtime-compatibility-version none -emit-ir -parse-stdlib -module-name someModule -module-link-name module %S/../Inputs/empty.swift | %FileCheck --check-prefix=NO-FORCE-LOAD %s // RUN: %target-swift-frontend -runtime-compatibility-version none -emit-ir -parse-stdlib -module-name someModule -module-link-name module %S/../Inputs/empty.swift -autolink-force-load | %FileCheck --check-prefix=FORCE-LOAD %s // RUN: %target-swift-frontend -runtime-compatibility-version none -emit-ir -parse-stdlib -module-name someModule -module-link-name 0module %S/../Inputs/empty.swift -autolink-force-load | %FileCheck --check-prefix=FORCE-LOAD-HEX %s +// RUN: %target-swift-frontend -emit-module -parse-stdlib -o %t -module-name someModule -module-link-name module %S/../Inputs/empty.swift -public-autolink-library anotherLib +// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version none -emit-ir -lmagic %s -I %t > %t/public-autolink.txt +// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-concurrency -runtime-compatibility-version none -emit-ir -lmagic %s -I %t > %t/public-autolink.txt +// RUN: %FileCheck %s < %t/public-autolink.txt +// RUN: %FileCheck -check-prefix=PUBLIC-DEP %s < %t/public-autolink.txt + // Linux uses a different autolinking mechanism, based on // swift-autolink-extract. This file tests the Darwin mechanism. // UNSUPPORTED: autolink-extract @@ -40,12 +49,8 @@ import someModule // NO-FORCE-LOAD-NOT: FORCE_LOAD // NO-FORCE-LOAD-NOT -lmodule // NO-FORCE-LOAD-NOT -lmagic -// FORCE-LOAD: define{{( dllexport)?}} void @"_swift_FORCE_LOAD_$_module"() {{(#[0-9]+)?( comdat)?}} { -// FORCE-LOAD: ret void -// FORCE-LOAD: } -// FORCE-LOAD-HEX: define{{( dllexport)?}} void @"_swift_FORCE_LOAD_$306d6f64756c65"() {{(#[0-9]+)?( comdat)?}} { -// FORCE-LOAD-HEX: ret void -// FORCE-LOAD-HEX: } +// FORCE-LOAD: @"_swift_FORCE_LOAD_$_module" +// FORCE-LOAD-HEX: @"_swift_FORCE_LOAD_$306d6f64756c65" // NO-FORCE-LOAD-CLIENT-NOT: FORCE_LOAD // FORCE-LOAD-CLIENT: @"_swift_FORCE_LOAD_$_module_$_autolinking" = weak_odr hidden constant void ()* @"_swift_FORCE_LOAD_$_module" @@ -55,3 +60,7 @@ import someModule // FORCE-LOAD-CLIENT-macho: declare extern_weak {{(dllimport )?}}void @"_swift_FORCE_LOAD_$_module"() // FORCE-LOAD-CLIENT-COFF: declare extern {{(dllimport )?}}void @"_swift_FORCE_LOAD_$_module"() +// PUBLIC-DEP: !llvm.linker.options = !{ +// PUBLIC-DEP-DAG: !{{[0-9]+}} = !{!{{"-lmagic"|"/DEFAULTLIB:magic.lib"}}} +// PUBLIC-DEP-DAG: !{{[0-9]+}} = !{!{{"-lmodule"|"/DEFAULTLIB:module.lib"}}} +// PUBLIC-DEP-DAG: !{{[0-9]+}} = !{!{{"-lanotherLib"|"/DEFAULTLIB:anotherLib.lib"}}} diff --git a/test/Serialization/concurrency_sil.swift b/test/Serialization/concurrency_sil.swift index 3076c39de2184..7d7e0067735f2 100644 --- a/test/Serialization/concurrency_sil.swift +++ b/test/Serialization/concurrency_sil.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -emit-module -Xfrontend -enable-experimental-concurrency -Xfrontend -disable-diagnostic-passes -whole-module-optimization -Xfrontend -enable-objc-interop -o %t/def_concurrency.swiftmodule %S/Inputs/def_concurrency.sil +// RUN: %target-build-swift -emit-module -Xfrontend -disable-availability-checking -Xfrontend -disable-diagnostic-passes -whole-module-optimization -Xfrontend -enable-objc-interop -o %t/def_concurrency.swiftmodule %S/Inputs/def_concurrency.sil // RUN: llvm-bcanalyzer %t/def_concurrency.swiftmodule | %FileCheck %s // RUN: %target-build-swift -emit-sil -I %t %s -o %t/concurrency_sil.sil // RUN: %target-sil-opt -I %t %t/concurrency_sil.sil -performance-linker | %FileCheck %S/Inputs/def_concurrency.sil diff --git a/test/Serialization/distributed.swift b/test/Serialization/distributed.swift new file mode 100644 index 0000000000000..1154e8b21281c --- /dev/null +++ b/test/Serialization/distributed.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t-scratch) +// RUN: echo "---- STEP 1 ---------------------------------------------------" +// RUN: %target-swift-frontend -emit-module -o %t-scratch/def_distributed~partial.swiftmodule -primary-file %S/Inputs/def_distributed.swift -module-name def_distributed -disable-availability-checking -enable-experimental-distributed +// RUN: echo "---- STEP 2 ---------------------------------------------------" +// RUN: %target-swift-frontend -merge-modules -emit-module -parse-as-library -enable-testing %t-scratch/def_distributed~partial.swiftmodule -module-name def_distributed -o %t/def_distributed.swiftmodule -disable-availability-checking -enable-experimental-distributed +// RUN: echo "---- STEP 3 ---------------------------------------------------" +// RUN: %target-swift-frontend -typecheck -I%t -verify %s -verify-ignore-unknown -disable-availability-checking -enable-experimental-distributed + +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed +import def_distributed + +func testDoSomethingDistributed(transport: ActorTransport) { + let _: DA = DA(transport: transport) +} + +extension DA { + @_dynamicReplacement(for:_remote_doSomethingDistributed(param:)) + func _impl_doSomethingDistributed(param: String) async throws -> Int { + return 2222 + } +} diff --git a/test/Serialization/effectful_properties.swift b/test/Serialization/effectful_properties.swift index e5a0ce287ad84..c6b34e72b1f5e 100644 --- a/test/Serialization/effectful_properties.swift +++ b/test/Serialization/effectful_properties.swift @@ -1,8 +1,7 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module-path %t/a.swiftmodule -module-name a %s +// RUN: %target-swift-frontend -emit-module-path %t/a.swiftmodule -module-name a %s -disable-availability-checking +// RUN: llvm-bcanalyzer -dump %t/a.swiftmodule | %FileCheck --implicit-check-not UnknownCode %s // RUN: %target-swift-ide-test -print-module -module-to-print a -source-filename x -I %t | %FileCheck -check-prefix MODULE-CHECK %s -// RUN: %target-swift-frontend -emit-module-path %t/b.swiftmodule -module-name a %t/a.swiftmodule -// RUN: cmp -s %t/a.swiftmodule %t/b.swiftmodule /////////// // This test checks for correct serialization & deserialization of diff --git a/test/Serialization/load-invalid-sourceinfo.swift b/test/Serialization/load-invalid-sourceinfo.swift index f28645c54b6ed..37dc18cf47914 100644 --- a/test/Serialization/load-invalid-sourceinfo.swift +++ b/test/Serialization/load-invalid-sourceinfo.swift @@ -14,4 +14,4 @@ // RUN: echo -n 'abcde' > %t/empty.swiftsourceinfo // RUN: %target-swift-frontend -typecheck -I %t %s -verify -import empty // expected-warning{{unable to use malformed module source info}} +import empty // expected-warning{{is either malformed or generated by a different Swift version}} diff --git a/test/Serialization/objc_async.swift b/test/Serialization/objc_async.swift index 83c42a69b9fd8..f8f696e0a930b 100644 --- a/test/Serialization/objc_async.swift +++ b/test/Serialization/objc_async.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %empty-directory(%t-scratch) -// RUN: %target-swift-frontend -emit-module -o %t-scratch/def_objc_async~partial.swiftmodule -primary-file %S/Inputs/def_objc_async.swift -module-name def_objc_async -enable-experimental-concurrency -// RUN: %target-swift-frontend -merge-modules -emit-module -parse-as-library -enable-testing %t-scratch/def_objc_async~partial.swiftmodule -module-name def_objc_async -o %t/def_objc_async.swiftmodule -enable-experimental-concurrency -// RUN: %target-swift-frontend -typecheck -I%t -verify %s -enable-experimental-concurrency +// RUN: %target-swift-frontend -emit-module -o %t-scratch/def_objc_async~partial.swiftmodule -primary-file %S/Inputs/def_objc_async.swift -module-name def_objc_async -disable-availability-checking +// RUN: %target-swift-frontend -merge-modules -emit-module -parse-as-library -enable-testing %t-scratch/def_objc_async~partial.swiftmodule -module-name def_objc_async -o %t/def_objc_async.swiftmodule -disable-availability-checking +// RUN: %target-swift-frontend -typecheck -I%t -verify %s -disable-availability-checking // REQUIRES: concurrency // REQUIRES: objc_interop diff --git a/test/Serialization/operator.swift b/test/Serialization/operator.swift index 8e96d8907f781..59c2583573e1a 100644 --- a/test/Serialization/operator.swift +++ b/test/Serialization/operator.swift @@ -2,8 +2,9 @@ // RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/def_operator.swift // RUN: llvm-bcanalyzer %t/def_operator.swiftmodule | %FileCheck %s // RUN: %target-swift-frontend -typecheck -I%t %s -// RUN: %target-swift-frontend -interpret -I %t -DINTERP %s | %FileCheck --check-prefix=OUTPUT %s - +// +// Run with the interpreter using the proper filecheck pattern. +// RUN: %target-jit-run -I %t -DINTERP %s | %FileCheck --check-prefix=OUTPUT %s // REQUIRES: swift_interpreter // FIXME: iOS doesn't work because this test needs the interpreter to handle diff --git a/test/Serialization/transparent.swift b/test/Serialization/transparent.swift index 875fd3b0b7640..947b4ca01e14a 100644 --- a/test/Serialization/transparent.swift +++ b/test/Serialization/transparent.swift @@ -15,21 +15,25 @@ import def_transparent // SIL: store [[BOOL]] to [[RAW]] : $*Bool var raw = testTransparent(x: false) -// SIL: [[TMP:%.+]] = global_addr @$s11transparent3tmps5Int32Vvp : $*Int32 +// SIL: [[TMP:%.+]] = global_addr @$s11transparent3tmps5Int32V_SStvp : $*(Int32, String) +// SIL: [[TMP1:%.+]] = tuple_element_addr [[TMP]] : $*(Int32, String), 0 +// SIL: [[TMP2:%.+]] = tuple_element_addr [[TMP]] : $*(Int32, String), 1 // SIL: [[VALUE:%.+]] = integer_literal $Builtin.Int32, 300 // SIL: [[INT:%.+]] = struct $Int32 ([[VALUE]] : $Builtin.Int32) -// SIL: store [[INT]] to [[TMP]] : $*Int32 +// SIL: [[STR:%.*]] = apply %{{.*}} : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String +// SIL: store [[INT]] to [[TMP1]] : $*Int32 +// SIL: store [[STR]] to [[TMP2]] : $*String var tmp = testBuiltin() // SIL-LABEL: sil public_external [transparent] @$s15def_transparent15testTransparent1xS2b_tF : $@convention(thin) (Bool) -> Bool { // SIL: bb0(%0 : $Bool): // SIL: return %0 : $Bool -// SIL-LABEL: sil public_external [transparent] @$s15def_transparent11testBuiltins5Int32VyF : $@convention(thin) () -> Int32 { +// SIL-LABEL: sil public_external [transparent] @$s15def_transparent11testBuiltins5Int32V_SStyF : $@convention(thin) () -> (Int32, @owned String) { // SIL: bb0: // SIL: integer_literal $Builtin.Int32, 300 // SIL: string_literal utf8 "foo" -// SIL: return %{{.*}} : $Int32 +// SIL: return %{{.*}} : $(Int32, String) // SIL-LABEL: sil public_external [transparent] @$s15def_transparent7test_bryyF : $@convention(thin) () -> () { // SIL: bb{{.*}}: diff --git a/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPointExtensions.swift b/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPointExtensions.swift index 37dfbabc2c165..987854cdcab1c 100644 --- a/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPointExtensions.swift +++ b/test/SourceKit/CodeComplete/Inputs/parseable-interface/MyPointExtensions.swift @@ -1,4 +1,4 @@ -import MyPoint +import MyPointModule public extension MyPoint { var magnitude: Double { diff --git a/test/SourceKit/CodeComplete/complete_annotateddescription.swift.result b/test/SourceKit/CodeComplete/complete_annotateddescription.swift.result index c5c0db722ab51..9ed83735b8143 100644 --- a/test/SourceKit/CodeComplete/complete_annotateddescription.swift.result +++ b/test/SourceKit/CodeComplete/complete_annotateddescription.swift.result @@ -2,26 +2,26 @@ key.results: [ { key.kind: source.lang.swift.decl.function.subscript, - key.name: "[label:]", - key.sourcetext: "[label: <#T##Int#>]", - key.description: "[label param: Int]", + key.name: "[:]", + key.sourcetext: "[<#T##param: Int##Int#>]", + key.description: "[_ param: Int]", key.typename: "Int", key.context: source.codecompletion.context.thisclass, key.typerelation: source.codecompletion.typerelation.unknown, key.num_bytes_to_erase: 0, - key.associated_usrs: "s:29complete_annotateddescription8MyStructV5labelS2i_tcip", + key.associated_usrs: "s:29complete_annotateddescription8MyStructVyS2icip", key.modulename: "complete_annotateddescription" }, { key.kind: source.lang.swift.decl.function.subscript, - key.name: "[:]", - key.sourcetext: "[<#T##param: Int##Int#>]", - key.description: "[_ param: Int]", + key.name: "[label:]", + key.sourcetext: "[label: <#T##Int#>]", + key.description: "[label param: Int]", key.typename: "Int", key.context: source.codecompletion.context.thisclass, key.typerelation: source.codecompletion.typerelation.unknown, key.num_bytes_to_erase: 0, - key.associated_usrs: "s:29complete_annotateddescription8MyStructVyS2icip", + key.associated_usrs: "s:29complete_annotateddescription8MyStructV5labelS2i_tcip", key.modulename: "complete_annotateddescription" }, { diff --git a/test/SourceKit/CodeComplete/complete_object_literals.swift b/test/SourceKit/CodeComplete/complete_object_literals.swift new file mode 100644 index 0000000000000..c75ef0934df25 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_object_literals.swift @@ -0,0 +1,58 @@ +func test(color: String) { + +} + +// RUN: %sourcekitd-test \ +// RUN: -req=complete -pos=2:1 %s -- %s == \ +// RUN: -req=complete -pos=2:1 -req-opts=includeobjectliterals=1 %s -- %s == \ +// RUN: -req=complete -pos=2:1 -req-opts=includeobjectliterals=0 %s -- %s == \ +// RUN: -req=complete -pos=2:1 %s -- %s \ +// RUN: | tee %t.out | %FileCheck --check-prefix=CHECK1 %s + +// CHECK1-LABEL: key.results: [ +// CHECK1: { +// CHECK1: key.kind: source.lang.swift.literal.color, +// CHECK1: key.name: "#colorLiteral(red:green:blue:alpha:)", +// CHECK1: key.sourcetext: "#colorLiteral(red: <#T##Float#>, green: <#T##Float#>, blue: <#T##Float#>, alpha: <#T##Float#>)", +// CHECK1: key.description: "#colorLiteral(red: Float, green: Float, blue: Float, alpha: Float)", +// CHECK1: }, +// CHECK1: { +// CHECK1: key.kind: source.lang.swift.literal.image, +// CHECK1: key.name: "#imageLiteral(resourceName:)", +// CHECK1: key.sourcetext: "#imageLiteral(resourceName: <#T##String#>)", +// CHECK1: key.description: "#imageLiteral(resourceName: String)", +// CHECK1: }, + +// CHECK1-LABEL: key.results: [ +// CHECK1: { +// CHECK1: key.kind: source.lang.swift.literal.color, +// CHECK1: key.name: "#colorLiteral(red:green:blue:alpha:)", +// CHECK1: key.sourcetext: "#colorLiteral(red: <#T##Float#>, green: <#T##Float#>, blue: <#T##Float#>, alpha: <#T##Float#>)", +// CHECK1: key.description: "#colorLiteral(red: Float, green: Float, blue: Float, alpha: Float)", +// CHECK1: }, +// CHECK1: { +// CHECK1: key.kind: source.lang.swift.literal.image, +// CHECK1: key.name: "#imageLiteral(resourceName:)", +// CHECK1: key.sourcetext: "#imageLiteral(resourceName: <#T##String#>)", +// CHECK1: key.description: "#imageLiteral(resourceName: String)", +// CHECK1: }, + +// CHECK1-LABEL: key.results: [ +// CHECK1-NOT: source.lang.swift.literal.color +// CHECK1-NOT: colorLiteral +// CHECK1-NOT: source.lang.swift.literal.image, +// CHECK1-NOT: imageLiteral + +// CHECK1-LABEL: key.results: [ +// CHECK1: { +// CHECK1: key.kind: source.lang.swift.literal.color, +// CHECK1: key.name: "#colorLiteral(red:green:blue:alpha:)", +// CHECK1: key.sourcetext: "#colorLiteral(red: <#T##Float#>, green: <#T##Float#>, blue: <#T##Float#>, alpha: <#T##Float#>)", +// CHECK1: key.description: "#colorLiteral(red: Float, green: Float, blue: Float, alpha: Float)", +// CHECK1: }, +// CHECK1: { +// CHECK1: key.kind: source.lang.swift.literal.image, +// CHECK1: key.name: "#imageLiteral(resourceName:)", +// CHECK1: key.sourcetext: "#imageLiteral(resourceName: <#T##String#>)", +// CHECK1: key.description: "#imageLiteral(resourceName: String)", +// CHECK1: }, diff --git a/test/SourceKit/CodeComplete/complete_override.swift.response b/test/SourceKit/CodeComplete/complete_override.swift.response index e1dd6fbdff8de..71246d8f2acd7 100644 --- a/test/SourceKit/CodeComplete/complete_override.swift.response +++ b/test/SourceKit/CodeComplete/complete_override.swift.response @@ -10,6 +10,16 @@ key.typerelation: source.codecompletion.typerelation.notapplicable, key.num_bytes_to_erase: 0 }, + { + key.kind: source.lang.swift.keyword, + key.name: "async", + key.sourcetext: "async", + key.description: "async", + key.typename: "", + key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.notapplicable, + key.num_bytes_to_erase: 0 + }, { key.kind: source.lang.swift.keyword, key.name: "class", @@ -184,6 +194,16 @@ key.typerelation: source.codecompletion.typerelation.notapplicable, key.num_bytes_to_erase: 0 }, + { + key.kind: source.lang.swift.keyword, + key.name: "isolated", + key.sourcetext: "isolated", + key.description: "isolated", + key.typename: "", + key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.notapplicable, + key.num_bytes_to_erase: 0 + }, { key.kind: source.lang.swift.keyword, key.name: "lazy", @@ -214,6 +234,16 @@ key.typerelation: source.codecompletion.typerelation.notapplicable, key.num_bytes_to_erase: 0 }, + { + key.kind: source.lang.swift.keyword, + key.name: "nonisolated", + key.sourcetext: "nonisolated", + key.description: "nonisolated", + key.typename: "", + key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.notapplicable, + key.num_bytes_to_erase: 0 + }, { key.kind: source.lang.swift.keyword, key.name: "nonmutating", diff --git a/test/SourceKit/CodeComplete/complete_sequence_rdar75358153.swift b/test/SourceKit/CodeComplete/complete_sequence_rdar75358153.swift new file mode 100644 index 0000000000000..3ec697084c20e --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_rdar75358153.swift @@ -0,0 +1,47 @@ +// BEGIN t1.swift +enum E { + case foo, bar +} +func foo(_ arg: E) {} +func test(val: E) { + foo(.bar) +} + +// BEGIN t2.swift +enum E { + case foo, bar +} +func foo(_ arg: E) {} +func test(val: E) { + foo(.bar) + if v +} + +// BEGIN dummy.swift + +// rdar://75358153 +// Previously, completing inside single expression body, then completing inside +// *non* single expression body in the same function caused a crash because the +// "has single expression" flag didn't cleard. This file tests the scenario not +// to regress again. + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %sourcekitd-test \ +// RUN: -req=complete -pos=6:8 -text-input %t/t1.swift %t/t.swift -- %t/t.swift == \ +// RUN: -req=complete -pos=7:6 -text-input %t/t2.swift %t/t.swift -- %t/t.swift \ +// RUN: | %FileCheck %s + +// CHECK-LABEL: key.results: [ +// CHECK-DAG: key.description: "bar", +// CHECK-DAG: key.description: "foo", +// CHECK-DAG: key.description: "hash(self: E)", +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: key.results: [ +// CHECK-DAG: key.description: "val", +// CHECK-DAG: key.description: "foo(arg: E)", +// CHECK: ], +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_sort_order.swift b/test/SourceKit/CodeComplete/complete_sort_order.swift index 1cc2f62c1ff36..8a60ad39a78b4 100644 --- a/test/SourceKit/CodeComplete/complete_sort_order.swift +++ b/test/SourceKit/CodeComplete/complete_sort_order.swift @@ -13,15 +13,14 @@ func test() { // RUN: %FileCheck -check-prefix=NAME_UNSORTED %s < %t.orig.off // RUN: not %diff -u %t.orig %t.orig.off -// Make sure the order is as below, foo(Int) should come before foo(String). -// NAME_SORTED: key.description: "#column" -// NAME_SORTED: key.description: "foo(a: Int)" -// NAME_SORTED-NOT: key.description -// NAME_SORTED: key.description: "foo(a: String)" -// NAME_SORTED-NOT: key.description -// NAME_SORTED: key.description: "foo(b: Int)" -// NAME_SORTED: key.description: "test()" -// NAME_SORTED: key.description: "x" +// NAME_SORTED: key.name: "#column" +// NAME_SORTED: key.name: "foo(a:)" +// NAME_SORTED-NOT: key.name: +// NAME_SORTED: key.name: "foo(a:)" +// NAME_SORTED-NOT: key.name: +// NAME_SORTED: key.name: "foo(b:)" +// NAME_SORTED: key.name: "test()" +// NAME_SORTED: key.name: "x" // NAME_UNSORTED-DAG: key.description: "x" // NAME_UNSORTED-DAG: key.description: "foo(a: String)" @@ -77,7 +76,7 @@ func test5() { // STMT_1-LABEL: Results for filterText: ret [ // STMT_1-NEXT: return // STMT_1-NEXT: retLocal -// STMT_1-NEXT: repeat +// STMT_1: repeat // STMT_1: ] // STMT_1-LABEL: Results for filterText: retur [ // STMT_1-NEXT: return diff --git a/test/SourceKit/CodeComplete/complete_swiftinterface.swift b/test/SourceKit/CodeComplete/complete_swiftinterface.swift index 258d16228bf92..8a1b429e63db2 100644 --- a/test/SourceKit/CodeComplete/complete_swiftinterface.swift +++ b/test/SourceKit/CodeComplete/complete_swiftinterface.swift @@ -2,7 +2,7 @@ // RUN: %empty-directory(%t/modulecache) // 1) Build .swiftinterface files for MyPoint and MyExtensions, using a non-default module cache path -// RUN: %target-swift-frontend -emit-module-interface-path %t/MyPoint.swiftinterface -module-name MyPoint -emit-module -o /dev/null %S/Inputs/parseable-interface/MyPoint.swift +// RUN: %target-swift-frontend -emit-module-interface-path %t/MyPointModule.swiftinterface -module-name MyPointModule -emit-module -o /dev/null %S/Inputs/parseable-interface/MyPoint.swift // RUN: %target-swift-frontend -emit-module-interface-path %t/MyPointExtensions.swiftinterface -module-name MyPointExtensions -emit-module -o /dev/null -module-cache-path %t/modulecache -I %t %S/Inputs/parseable-interface/MyPointExtensions.swift // RUN: %empty-directory(%t/modulecache) @@ -12,7 +12,7 @@ // 3) Check completion again with a warm module cache // RUN: %target-swift-ide-test -code-completion -code-completion-token=MEMBER -source-filename %s -I %t | %FileCheck %s -import MyPoint +import MyPointModule import MyPointExtensions let x = MyPoint(x: 1, y: 10.5) diff --git a/test/SourceKit/CodeComplete/complete_type_match.swift b/test/SourceKit/CodeComplete/complete_type_match.swift index 2d7088f6b8e44..3d5908924a86f 100644 --- a/test/SourceKit/CodeComplete/complete_type_match.swift +++ b/test/SourceKit/CodeComplete/complete_type_match.swift @@ -14,32 +14,32 @@ func takeIntOpt(x: Int, y: Int?) func takeString(x: Int, y: String) func takeAny(x: Int, y: Any) -takeInt(1, y: #^TOP_LEVEL_0^#) +takeInt(x: 1, y: #^TOP_LEVEL_0^#) // TOP_LEVEL_0-NOT: nil // TOP_LEVEL_0: valueZ // TOP_LEVEL_0: Int // TOP_LEVEL_0: valueA // TOP_LEVEL_0: valueS -takeString(1, y: #^TOP_LEVEL_1^#) +takeString(x: 1, y: #^TOP_LEVEL_1^#) // TOP_LEVEL_1: valueS // TOP_LEVEL_1: String // TOP_LEVEL_1: valueA // TOP_LEVEL_1: valueZ -takeAny(1, y: #^TOP_LEVEL_2^#) +takeAny(x: 1, y: #^TOP_LEVEL_2^#) // TOP_LEVEL_2: valueA // TOP_LEVEL_2: valueS // TOP_LEVEL_2: valueZ -takeIntOpt(1, y: #^TOP_LEVEL_3^#) +takeIntOpt(x: 1, y: #^TOP_LEVEL_3^#) // TOP_LEVEL_3: nil // TOP_LEVEL_3: valueZ // TOP_LEVEL_3: valueA // TOP_LEVEL_3: valueS func testCrossContext(x: Int, y: String, z: Any) { - takeInt(1, y: #^CROSS_CONTEXT_0^#) + takeInt(x: 1, y: #^CROSS_CONTEXT_0^#) } // CROSS_CONTEXT_0: x // CROSS_CONTEXT_0: valueZ @@ -56,7 +56,7 @@ struct FromMethod { } func testFromMethod(x: FromMethod) { - takeInt(1, y: x.#^FROM_METHOD_0^#) + takeInt(x: 1, y: x.#^FROM_METHOD_0^#) } // FROM_METHOD_0: valueZ() // FROM_METHOD_0: valueA() diff --git a/test/SourceKit/CodeExpand/code-expand-rdar77665805.swift b/test/SourceKit/CodeExpand/code-expand-rdar77665805.swift new file mode 100644 index 0000000000000..5482defe16157 --- /dev/null +++ b/test/SourceKit/CodeExpand/code-expand-rdar77665805.swift @@ -0,0 +1,18 @@ +enum E { case foo, bar } +func foo(x: (E) -> Void) {} +func test() { + foo(x: <#T##(E) -> Void#>) +} + +// RUN: %sourcekitd-test \ +// RUN: -req=open %s -- %s == \ +// RUN: -req=edit -offset=0 -length=53 -replace="" -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %s -- %s == \ +// RUN: -req=expand-placeholder -offset=23 -length=18 %s \ +// RUN: | %FileCheck %s + +// CHECK: { +// CHECK: key.offset: 19, +// CHECK: key.length: 23, +// CHECK: key.sourcetext: " { <#E#> in\n<#code#>\n}" +// CHECK: } + diff --git a/test/SourceKit/CompileNotifications/arg-parsing.swift b/test/SourceKit/CompileNotifications/arg-parsing.swift index 7dc654d7d5ac5..769e86c3b39f3 100644 --- a/test/SourceKit/CompileNotifications/arg-parsing.swift +++ b/test/SourceKit/CompileNotifications/arg-parsing.swift @@ -12,6 +12,7 @@ // ARG_PARSE_0: { // ARG_PARSE_0: key.filepath: "", // ARG_PARSE_0: key.severity: source.diagnostic.severity.error, +// ARG_PARSE_0: key.id: "error_unknown_arg", // ARG_PARSE_0: key.description: "unknown argument: '-no-such-arg'" // ARG_PARSE_0: } // ARG_PARSE_0: ] @@ -34,6 +35,7 @@ // ARG_PARSE_1: { // ARG_PARSE_1: key.filepath: "", // ARG_PARSE_1: key.severity: source.diagnostic.severity.error, +// ARG_PARSE_1: key.id: "error_unknown_arg", // ARG_PARSE_1: key.description: "unknown argument: '-no-such-arg'" // ARG_PARSE_1: } // ARG_PARSE_1: ] @@ -54,6 +56,7 @@ // ARG_PARSE_2: { // ARG_PARSE_2: key.filepath: "", // ARG_PARSE_2: key.severity: source.diagnostic.severity.error, +// ARG_PARSE_2: key.id: "error_no_input_files", // ARG_PARSE_2: key.description: "no input files" // ARG_PARSE_2: } // ARG_PARSE_2: ] diff --git a/test/SourceKit/CompileNotifications/diagnostics.swift b/test/SourceKit/CompileNotifications/diagnostics.swift index f8cc6cd7caa67..473d4c6206294 100644 --- a/test/SourceKit/CompileNotifications/diagnostics.swift +++ b/test/SourceKit/CompileNotifications/diagnostics.swift @@ -11,6 +11,7 @@ // PARSE-NEXT: key.column: 6 // PARSE-NEXT: key.filepath: "{{.*}}parse-error.swift" // PARSE-NEXT: key.severity: source.diagnostic.severity.error +// PARSE-NEXT: key.id: "number_cant_start_decl_name" // PARSE-NEXT: key.description: "function name // PARSE-NEXT: } // PARSE-NEXT: ] @@ -23,6 +24,7 @@ // PARSE-WITH-SOURCELOCATION-NEXT: key.column: 6 // PARSE-WITH-SOURCELOCATION-NEXT: key.filepath: "custom.swuft" // PARSE-WITH-SOURCELOCATION-NEXT: key.severity: source.diagnostic.severity.error +// PARSE-WITH-SOURCELOCATION-NEXT: key.id: "number_cant_start_decl_name" // PARSE-WITH-SOURCELOCATION-NEXT: key.description: "function name // PARSE-WITH-SOURCELOCATION-NEXT: } // PARSE-WITH-SOURCELOCATION-NEXT: ] @@ -38,6 +40,7 @@ // SEMA-NEXT: key.column: 5 // SEMA-NEXT: key.filepath: "{{.*}}sema-error.swift" // SEMA-NEXT: key.severity: source.diagnostic.severity.error +// SEMA-NEXT: key.id: "cannot_find_in_scope" // SEMA-NEXT: key.description: "cannot find '{{.*}}' in scope // SEMA-NEXT: key.ranges: [ @@ -49,6 +52,7 @@ // CLANG_IMPORTER-NEXT: key.column: // CLANG_IMPORTER-NEXT: key.filepath: "<{{.*}}>" // CLANG_IMPORTER-NEXT: key.severity: source.diagnostic.severity.error, +// CLANG_IMPORTER-NEXT: key.id: "error_from_clang" // CLANG_IMPORTER-NEXT: key.description: {{.*}}not found // RUN: %sourcekitd-test -req=track-compiles == -req=sema %s -- %s -Xcc -ivfsoverlay -Xcc /doesnotexist | %FileCheck %s -check-prefix=CLANG_IMPORTER_UNKNOWN @@ -58,6 +62,7 @@ // CLANG_IMPORTER_UNKNOWN-NEXT: key.filepath: "" // CLANG_IMPORTER_UNKNOWN-NEXT: key.severity: source.diagnostic.severity.error, // CLANG_IMPORTER_UNKNOWN-NEXT: key.offset: 0 +// CLANG_IMPORTER_UNKNOWN-NEXT: key.id: "error_from_clang" // CLANG_IMPORTER_UNKNOWN-NEXT: key.description: "virtual filesystem{{.*}}not found // Note: we're missing the "compiler is in code completion mode" diagnostic, @@ -75,6 +80,7 @@ // INVALID_ARG-NEXT: key.filepath: "" // INVALID_ARG-NEXT: key.severity: source.diagnostic.severity.error, // INVALID_ARG-NEXT: key.offset: 0 +// INVALID_ARG-NEXT: key.id: "{{error_from_clang|error_unknown_arg}}" // INVALID_ARG-NEXT: key.description: "unknown argument // Ignore the spurious -wmo + -enable-batch-mode warning. diff --git a/test/SourceKit/CursorInfo/Inputs/invalid_compiler_args/A/File.swift b/test/SourceKit/CursorInfo/Inputs/invalid_compiler_args/A/File.swift new file mode 100644 index 0000000000000..4e6a6de65314d --- /dev/null +++ b/test/SourceKit/CursorInfo/Inputs/invalid_compiler_args/A/File.swift @@ -0,0 +1 @@ +class Foo {} diff --git a/test/SourceKit/CursorInfo/Inputs/invalid_compiler_args/B/File.swift b/test/SourceKit/CursorInfo/Inputs/invalid_compiler_args/B/File.swift new file mode 100644 index 0000000000000..bf1673a091166 --- /dev/null +++ b/test/SourceKit/CursorInfo/Inputs/invalid_compiler_args/B/File.swift @@ -0,0 +1 @@ +class Bar {} diff --git a/test/SourceKit/CursorInfo/cursor_after_edit.swift b/test/SourceKit/CursorInfo/cursor_after_edit.swift new file mode 100644 index 0000000000000..0ceb0cec5efc1 --- /dev/null +++ b/test/SourceKit/CursorInfo/cursor_after_edit.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) + +// RUN: touch %t/empty.swift +// RUN: echo "func foo() {}" >> %t/func.swift + +// Edit previously did not update the syntax info. Cursor info was using its +// buffer to calculate line and column (before rdar://78161348). +// RUN: %sourcekitd-test \ +// RUN: -req=open -text-input %t/empty.swift %t/func.swift -- %t/func.swift == \ +// RUN: -req=edit -offset=0 -length=0 -replace="func foo() {}" -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/func.swift -- %t/func.swift == \ +// RUN: -req=cursor -offset=5 %t/func.swift -- %t/func.swift diff --git a/test/SourceKit/CursorInfo/cursor_info_concurrency.swift b/test/SourceKit/CursorInfo/cursor_info_concurrency.swift index ecb39673e537e..725ed0bc068cd 100644 --- a/test/SourceKit/CursorInfo/cursor_info_concurrency.swift +++ b/test/SourceKit/CursorInfo/cursor_info_concurrency.swift @@ -7,14 +7,6 @@ func test(act: MyActor) async throws { try await act.asyncFunc {} } -public actor class MyActorClass { - public func asyncFunc(fn: () async -> Void) async throws {} -} - -func test(act: MyActorClass) async throws { - try await act.asyncFunc {} -} - // BEGIN App.swift import MyModule @@ -28,27 +20,21 @@ func test(act: MyActor) async throws { // RUN: %{python} %utils/split_file.py -o %t %s // RUN: %empty-directory(%t/Modules) -// RUN: %target-swift-frontend -emit-module -o %t/Modules/MyModule.swiftmodule -module-name MyModule %t/MyModule.swift -enable-experimental-concurrency +// RUN: %target-swift-frontend -emit-module -o %t/Modules/MyModule.swiftmodule -module-name MyModule %t/MyModule.swift -disable-availability-checking -// RUN: %sourcekitd-test -req=cursor -pos=1:15 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple -Xfrontend -enable-experimental-concurrency | %FileCheck -check-prefix=ACTOR %s -// RUN: %sourcekitd-test -req=cursor -pos=2:15 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple -Xfrontend -enable-experimental-concurrency | %FileCheck -check-prefix=FUNC %s -// RUN: %sourcekitd-test -req=cursor -pos=5:16 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple -Xfrontend -enable-experimental-concurrency | %FileCheck -check-prefix=ACTOR %s -// RUN: %sourcekitd-test -req=cursor -pos=6:19 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple -Xfrontend -enable-experimental-concurrency | %FileCheck -check-prefix=FUNC %s - -// RUN: %sourcekitd-test -req=cursor -pos=9:20 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple -Xfrontend -enable-experimental-concurrency | %FileCheck -check-prefix=CLASSACTOR %s -// RUN: %sourcekitd-test -req=cursor -pos=13:16 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple -Xfrontend -enable-experimental-concurrency | %FileCheck -check-prefix=CLASSACTOR %s +// RUN: %sourcekitd-test -req=cursor -pos=1:15 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple | %FileCheck -check-prefix=ACTOR %s +// RUN: %sourcekitd-test -req=cursor -pos=2:15 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple | %FileCheck -check-prefix=FUNC %s +// RUN: %sourcekitd-test -req=cursor -pos=5:16 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple | %FileCheck -check-prefix=ACTOR %s +// RUN: %sourcekitd-test -req=cursor -pos=6:19 %t/MyModule.swift -- %t/MyModule.swift -target %target-triple | %FileCheck -check-prefix=FUNC %s // ACTOR: public actor MyActor // ACTOR: public actor MyActor -// CLASSACTOR: public actor class MyActorClass -// CLASSACTOR: public actor class MyActorClass - // FUNC: public func asyncFunc(fn: () async -> Void) async throws // FUNC: public func asyncFunc(fn: () async -> Void) async throws -// RUN: %sourcekitd-test -req=cursor -pos=3:16 %t/App.swift -- %t/App.swift -target %target-triple -I %t/Modules -Xfrontend -enable-experimental-concurrency | %FileCheck -check-prefix=ACTOR_XMOD %s -// RUN: %sourcekitd-test -req=cursor -pos=4:19 %t/App.swift -- %t/App.swift -target %target-triple -I %t/Modules -Xfrontend -enable-experimental-concurrency | %FileCheck -check-prefix=FUNC_XMOD %s +// RUN: %sourcekitd-test -req=cursor -pos=3:16 %t/App.swift -- %t/App.swift -target %target-triple -I %t/Modules | %FileCheck -check-prefix=ACTOR_XMOD %s +// RUN: %sourcekitd-test -req=cursor -pos=4:19 %t/App.swift -- %t/App.swift -target %target-triple -I %t/Modules | %FileCheck -check-prefix=FUNC_XMOD %s // ACTOR_XMOD: actor MyActor // ACTOR_XMOD: actor MyActor diff --git a/test/SourceKit/CursorInfo/cursor_relatedname.swift b/test/SourceKit/CursorInfo/cursor_relatedname.swift new file mode 100644 index 0000000000000..cc2c8877c70f7 --- /dev/null +++ b/test/SourceKit/CursorInfo/cursor_relatedname.swift @@ -0,0 +1,32 @@ +struct S { + mutating func foo(x: Int) {} + __consuming func foo(x: String) {} +} + +// RUN: %sourcekitd-test -req=cursor -pos=2:19 %s -- %s -module-name MyMod | %FileCheck -check-prefix=CHECK1 %s + +// CHECK1: source.lang.swift.decl.function.method.instance (2:19-2:30) +// CHECK1: foo(x:) +// CHECK1: s:5MyMod1SV3foo1xySi_tF +// CHECK1: source.lang.swift +// CHECK1: (inout S) -> (Int) -> () +// CHECK1: $s1xySi_tcD +// CHECK1: mutating func foo(x: Int) +// CHECK1: mutating func foo(x: Int) +// CHECK1: RELATED BEGIN +// CHECK1: foo(x: String) +// CHECK1: RELATED END + +// RUN: %sourcekitd-test -req=cursor -pos=3:22 %s -- %s -module-name MyMod | %FileCheck -check-prefix=CHECK2 %s + +// CHECK2: source.lang.swift.decl.function.method.instance (3:22-3:36) +// CHECK2: foo(x:) +// CHECK2: s:5MyMod1SV3foo1xySS_tF +// CHECK2: source.lang.swift +// CHECK2: (__owned S) -> (String) -> () +// CHECK2: $s1xySS_tcD +// CHECK2: func foo(x: String) +// CHECK2: func foo(x: String) +// CHECK2: RELATED BEGIN +// CHECK2: foo(x: Int) +// CHECK2: RELATED END diff --git a/test/SourceKit/CursorInfo/cursor_symbol_graph.swift b/test/SourceKit/CursorInfo/cursor_symbol_graph.swift index ea88769281546..fca7007c94949 100644 --- a/test/SourceKit/CursorInfo/cursor_symbol_graph.swift +++ b/test/SourceKit/CursorInfo/cursor_symbol_graph.swift @@ -323,7 +323,8 @@ enum MyEnum { // CHECKFOO: "vendor": // CHECKFOO: } // CHECKFOO: }, -// CHECKFOO: "relationships": [], +// CHECKFOO: "relationships": [ +// CHECKFOO: "target": "s:s8SendableP", // CHECKFOO: "symbols": [ // CHECKFOO: { // CHECKFOO: "accessLevel": "internal", diff --git a/test/SourceKit/CursorInfo/injected_vfs.swift b/test/SourceKit/CursorInfo/injected_vfs.swift index 44e3a461c9c50..1ecc0eda2046b 100644 --- a/test/SourceKit/CursorInfo/injected_vfs.swift +++ b/test/SourceKit/CursorInfo/injected_vfs.swift @@ -10,7 +10,7 @@ func foo( // CHECK-CMODULE: key.kind: source.lang.swift.ref.struct // CHECK-CMODULE: key.name: "StructDefinedInCModule" -// CHECK-CMODULE: key.filepath: "{{.*}}/CModule{{/|\\\\}}CModule.h" +// CHECK-CMODULE: key.filepath: "{{.*}}{{/|\\\\}}CModule{{/|\\\\}}CModule.h" // CHECK-SWIFTMODULE-REF: key.kind: source.lang.swift.ref.struct // CHECK-SWIFTMODULE-REF: key.name: "StructDefinedInSwiftModule" diff --git a/test/SourceKit/CursorInfo/invalid_compiler_args.swift b/test/SourceKit/CursorInfo/invalid_compiler_args.swift new file mode 100644 index 0000000000000..07a47f66463a6 --- /dev/null +++ b/test/SourceKit/CursorInfo/invalid_compiler_args.swift @@ -0,0 +1,12 @@ +// We should not fail if two distinct file have the same name - this is only an issue in CodeGen +// RUN: %sourcekitd-test -req=cursor -pos=1:7 %S/Inputs/invalid_compiler_args/A/File.swift -- %S/Inputs/invalid_compiler_args/A/File.swift %S/Inputs/invalid_compiler_args/B/File.swift | %FileCheck %s + +// We can't do anything if the requested file is not in the compiler arguments +// RUN: not %sourcekitd-test -req=cursor -pos=1:7 %S/Inputs/invalid_compiler_args/A/File.swift -- +// RUN: not %sourcekitd-test -req=cursor -pos=1:7 %S/Inputs/invalid_compiler_args/A/File.swift -- %S/Inputs/invalid_compiler_args/B/File.swift + +// Specifying a file twice should just ignore one of them +// RUN: %sourcekitd-test -req=cursor -pos=1:7 %S/Inputs/invalid_compiler_args/A/File.swift -- %S/Inputs/invalid_compiler_args/A/File.swift %S/Inputs/invalid_compiler_args/A/File.swift + + +// CHECK: source.lang.swift.decl.class diff --git a/test/SourceKit/CursorInfo/rdar_18677108-2.swift.response b/test/SourceKit/CursorInfo/rdar_18677108-2.swift.response index 7afe9c239a441..d15ad239acbbe 100644 --- a/test/SourceKit/CursorInfo/rdar_18677108-2.swift.response +++ b/test/SourceKit/CursorInfo/rdar_18677108-2.swift.response @@ -4,6 +4,7 @@ key.column: 1, key.filepath: rdar_18677108-2-a.swift, key.severity: source.diagnostic.severity.error, + key.id: "expected_rparen_expr_list", key.description: "expected ')' in expression list", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.diagnostics: [ @@ -12,6 +13,7 @@ key.column: 46, key.filepath: rdar_18677108-2-a.swift, key.severity: source.diagnostic.severity.note, + key.id: "opening_paren", key.description: "to match this opening '('" } ] @@ -21,6 +23,7 @@ key.column: 1, key.filepath: rdar_18677108-2-a.swift, key.severity: source.diagnostic.severity.error, + key.id: "expected_rbrace_in_brace_stmt", key.description: "expected '}' at end of brace statement", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.diagnostics: [ @@ -29,6 +32,7 @@ key.column: 3, key.filepath: rdar_18677108-2-a.swift, key.severity: source.diagnostic.severity.note, + key.id: "opening_brace", key.description: "to match this opening '{'" } ] @@ -38,6 +42,7 @@ key.column: 1, key.filepath: rdar_18677108-2-a.swift, key.severity: source.diagnostic.severity.error, + key.id: "expected_rbrace_class", key.description: "expected '}' in class", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.diagnostics: [ @@ -46,6 +51,7 @@ key.column: 1, key.filepath: rdar_18677108-2-a.swift, key.severity: source.diagnostic.severity.note, + key.id: "opening_brace", key.description: "to match this opening '{'" } ] diff --git a/test/SourceKit/DocSupport/Inputs/async/async.h b/test/SourceKit/DocSupport/Inputs/async/async.h new file mode 100644 index 0000000000000..06f225c53e567 --- /dev/null +++ b/test/SourceKit/DocSupport/Inputs/async/async.h @@ -0,0 +1,10 @@ +@import Foundation; + +@interface AsyncImports : NSObject + +-(void)methodWithCompletion:(void (^)(void))completionHandler; + +-(void)propWithCompletion:(void (^)(BOOL))completionHandler + __attribute__((swift_async_name("getter:asyncProp()"))); + +@end diff --git a/test/SourceKit/DocSupport/Inputs/async/module.modulemap b/test/SourceKit/DocSupport/Inputs/async/module.modulemap new file mode 100644 index 0000000000000..b41b85437afd7 --- /dev/null +++ b/test/SourceKit/DocSupport/Inputs/async/module.modulemap @@ -0,0 +1,3 @@ +module "asyncmod" { + header "async.h" +} diff --git a/test/SourceKit/DocSupport/doc_async.swift b/test/SourceKit/DocSupport/doc_async.swift index 2c38a398a1035..05016c2a665c7 100644 --- a/test/SourceKit/DocSupport/doc_async.swift +++ b/test/SourceKit/DocSupport/doc_async.swift @@ -1,56 +1,88 @@ -// RUN: %empty-directory(%t.mod) -// RUN: %swift -enable-experimental-concurrency -emit-module -o %t.mod/async.swiftmodule %s -parse-as-library -emit-module-doc-path %t.mod/async.swiftdoc -// RUN: %sourcekitd-test -req=doc-info -module async -- -I %t.mod | %FileCheck %s - // REQUIRES: concurrency +// REQUIRES: objc_interop + +// RUN: %sourcekitd-test -req=doc-info %s -- -module-name asyncmod -target %target-triple | %FileCheck %s + +// RUN: %sourcekitd-test -req=doc-info -module asyncmod -- -target %target-triple %clang-importer-sdk-nosource -I %S/Inputs/async/ | %FileCheck %s --check-prefix=CHECK-OBJC +// CHECK-OBJC: key.name: "method()", +// CHECK-OBJC: key.usr: "c:objc(cs)AsyncImports(im)methodWithCompletion:" +// CHECK-OBJC-NOT: }, +// CHECK-OBJC: key.is_async: 1 +// CHECK-OBJC: }, + +// CHECK-OBJC: key.name: "asyncProp", +// CHECK-OBJC: key.usr: "c:objc(cs)AsyncImports(im)propWithCompletion:" +// CHECK-OBJC: key.fully_annotated_decl: +// CHECK-OBJC-NEXT: key.is_async: 1 +// CHECK-OBJC: } public protocol AsyncProto { + // CHECK: key.usr: "s:8asyncmod10AsyncProtoP05protoB4PropSivp" + // CHECK-NOT: }, + // CHECK: key.is_async: 1 + // CHECK: }, + var protoAsyncProp: Int { get async } + // CHECK: key.usr: "s:8asyncmod10AsyncProtoP08protoNonB4PropSivp" + // CHECK-NOT: key.is_async: 1 + // CHECK: }, + var protoNonAsyncProp: Int { get } + func protoAsyncFunc() async - // CHECK: key.usr: "s:5async10AsyncProtoP05protoB4FuncyyYaF" + // CHECK: key.usr: "s:8asyncmod10AsyncProtoP05protoB4FuncyyYaF" // CHECK-NOT: } // CHECK: key.is_async: 1 // CHECK: } func protoNonAsyncFunc() - // CHECK: key.usr: "s:5async10AsyncProtoP08protoNonB4FuncyyF" + // CHECK: key.usr: "s:8asyncmod10AsyncProtoP08protoNonB4FuncyyF" // CHECK-NOT: key.is_async: 1 // CHECK: } } public struct AsyncStruct: AsyncProto { + // CHECK: key.usr: "s:8asyncmod11AsyncStructV05protoB4PropSivp" + // CHECK-NOT: }, + // CHECK: key.is_async: 1 + // CHECK: }, + public var protoAsyncProp: Int { get async { return 1 } } + // CHECK: key.usr: "s:8asyncmod11AsyncStructV08protoNonB4PropSivp" + // CHECK-NOT: key.is_async: 1 + // CHECK: }, + public var protoNonAsyncProp: Int + public func structAsyncFunc() async { } - // CHECK: key.usr: "s:5async11AsyncStructV06structB4FuncyyYaF" + // CHECK: key.usr: "s:8asyncmod11AsyncStructV06structB4FuncyyYaF" // CHECK-NOT: } // CHECK: key.is_async: 1 // CHECK: } public func structNonAsyncFunc() { } - // CHECK: key.usr: "s:5async11AsyncStructV09structNonB4FuncyyF" + // CHECK: key.usr: "s:8asyncmod11AsyncStructV09structNonB4FuncyyF" // CHECK-NOT: key.is_async: 1 // CHECK: } public func protoAsyncFunc() async { } - // CHECK: key.usr: "s:5async11AsyncStructV05protoB4FuncyyYaF" + // CHECK: key.usr: "s:8asyncmod11AsyncStructV05protoB4FuncyyYaF" // CHECK-NOT: } // CHECK: key.conforms // CHECK: { - // CHECK: key.usr: "s:5async10AsyncProtoP05protoB4FuncyyYaF" + // CHECK: key.usr: "s:8asyncmod10AsyncProtoP05protoB4FuncyyYaF" // CHECK-NOT: } // CHECK: key.is_async: 1 // CHECK: } // CHECK: key.is_async: 1 // CHECK: } public func protoNonAsyncFunc() { } - // CHECK: key.usr: "s:5async11AsyncStructV08protoNonB4FuncyyF" + // CHECK: key.usr: "s:8asyncmod11AsyncStructV08protoNonB4FuncyyF" // CHECK-NOT: key.is_async: 1 // CHECK: } } public func topLevelAsyncFunc() async { } -// CHECK: key.usr: "s:5async17topLevelAsyncFuncyyYaF" +// CHECK: key.usr: "s:8asyncmod17topLevelAsyncFuncyyYaF" // CHECK-NOT: } // CHECK: key.is_async: 1 // CHECK: } public func topLevelNonAsyncFunc() { } -// CHECK: key.usr: "s:5async20topLevelNonAsyncFuncyyF" +// CHECK: key.usr: "s:8asyncmod20topLevelNonAsyncFuncyyF" // CHECK-NOT: key.is_async: 1 // CHECK: } diff --git a/test/SourceKit/DocSupport/doc_clang_module.swift.response b/test/SourceKit/DocSupport/doc_clang_module.swift.response index 1a99231f2b298..7e26f01555b79 100644 --- a/test/SourceKit/DocSupport/doc_clang_module.swift.response +++ b/test/SourceKit/DocSupport/doc_clang_module.swift.response @@ -6481,7 +6481,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.extension.class, key.offset: 5964, key.length: 61, - key.fully_annotated_decl: "extension FooClassBase", + key.fully_annotated_decl: "extension FooClassBase", key.extends: { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", @@ -6502,7 +6502,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.extension.class, key.offset: 6026, key.length: 97, - key.fully_annotated_decl: "extension FooClassBase", + key.fully_annotated_decl: "extension FooClassBase", key.extends: { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", @@ -6531,7 +6531,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.extension.class, key.offset: 6124, key.length: 61, - key.fully_annotated_decl: "extension FooClassBase", + key.fully_annotated_decl: "extension FooClassBase", key.extends: { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", diff --git a/test/SourceKit/DocSupport/doc_error_domain.swift b/test/SourceKit/DocSupport/doc_error_domain.swift index 2cb2b47f400ca..fea0303a171a3 100644 --- a/test/SourceKit/DocSupport/doc_error_domain.swift +++ b/test/SourceKit/DocSupport/doc_error_domain.swift @@ -3,8 +3,8 @@ // RUN: -sdk %sdk | %sed_clean > %t.response // RUN: %FileCheck -input-file=%t.response %s -// CHECK: struct MyError { -// CHECK: enum Code : Int32 { +// CHECK: struct MyError : CustomNSError, Hashable, Error { +// CHECK: enum Code : Int32, Equatable { // CHECK: case errFirst // CHECK: case errSecond // CHECK: } diff --git a/test/SourceKit/DocSupport/doc_objc_concurrency.swift b/test/SourceKit/DocSupport/doc_objc_concurrency.swift new file mode 100644 index 0000000000000..1ad9d7be1806c --- /dev/null +++ b/test/SourceKit/DocSupport/doc_objc_concurrency.swift @@ -0,0 +1,22 @@ +// REQUIRES: objc_interop +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +// RUN: %sourcekitd-test -req=doc-info %S/../Inputs/concurrency/gen_concurrency.swift -- -target %target-triple -I %t -Xfrontend -enable-experimental-concurrency | %FileCheck %s --check-prefix=SWIFT-DOC + +// Never output @available (even if explicitly written by the user). + +// SWIFT-DOC: key.fully_annotated_decl: "func foo(_ operation: String, completionHandler handler: @escaping (Int) -> Void)" + +// SWIFT-DOC: key.fully_annotated_decl: "func foo(_ operation: String) async -> Int" + + +// RUN: %sourcekitd-test -req=doc-info -module ConcurrencyHeader -- -Xfrontend -enable-objc-interop -Xfrontend -enable-experimental-concurrency -I %S/../Inputs/concurrency/ -sdk %clang-importer-sdk | %FileCheck %s --check-prefix=OBJC-DOC + +// Especially if the @available was implicitly added to an imported Clang decl +// (rdar://76685011). + +// OBJC-DOC: key.fully_annotated_decl: "func method(withHandler operation: String!, completionHandler handler: ((Int) -> Void)!)" + +// OBJC-DOC: key.fully_annotated_decl: "func method(withHandler operation: String!) async -> Int" diff --git a/test/SourceKit/DocSupport/doc_source_file.swift.response b/test/SourceKit/DocSupport/doc_source_file.swift.response index e9a54af27c68a..ab0fc2471b062 100644 --- a/test/SourceKit/DocSupport/doc_source_file.swift.response +++ b/test/SourceKit/DocSupport/doc_source_file.swift.response @@ -1960,7 +1960,7 @@ key.kind: source.lang.swift.decl.extension.class, key.offset: 649, key.length: 112, - key.fully_annotated_decl: "extension CC : Prot", + key.fully_annotated_decl: "extension CC : Prot", key.conforms: [ { key.kind: source.lang.swift.ref.protocol, diff --git a/test/SourceKit/DocSupport/doc_swift_module.swift.response b/test/SourceKit/DocSupport/doc_swift_module.swift.response index 907b351df77af..ef37e11ac5319 100644 --- a/test/SourceKit/DocSupport/doc_swift_module.swift.response +++ b/test/SourceKit/DocSupport/doc_swift_module.swift.response @@ -2028,7 +2028,7 @@ func shouldPrintAnyAsKeyword(x x: Any) key.doc.full_as_xml: "@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)\nextension C1some comments", key.offset: 473, key.length: 37, - key.fully_annotated_decl: "extension C1", + key.fully_annotated_decl: "extension C1", key.extends: { key.kind: source.lang.swift.ref.class, key.name: "C1", @@ -2071,7 +2071,7 @@ func shouldPrintAnyAsKeyword(x x: Any) key.kind: source.lang.swift.decl.extension.class, key.offset: 512, key.length: 105, - key.fully_annotated_decl: "extension C1 : P4", + key.fully_annotated_decl: "extension C1 : P4", key.conforms: [ { key.kind: source.lang.swift.ref.protocol, @@ -2311,7 +2311,7 @@ func shouldPrintAnyAsKeyword(x x: Any) key.kind: source.lang.swift.decl.extension.protocol, key.offset: 1075, key.length: 54, - key.fully_annotated_decl: "extension P", + key.fully_annotated_decl: "extension P", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "P", @@ -2415,7 +2415,7 @@ func shouldPrintAnyAsKeyword(x x: Any) key.kind: source.lang.swift.decl.extension.protocol, key.offset: 1314, key.length: 53, - key.fully_annotated_decl: "extension P6", + key.fully_annotated_decl: "extension P6", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "P6", @@ -2478,7 +2478,7 @@ func shouldPrintAnyAsKeyword(x x: Any) key.kind: source.lang.swift.decl.extension.protocol, key.offset: 1473, key.length: 79, - key.fully_annotated_decl: "extension Prot", + key.fully_annotated_decl: "extension Prot", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "Prot", @@ -2522,7 +2522,7 @@ func shouldPrintAnyAsKeyword(x x: Any) ], key.offset: 1554, key.length: 63, - key.fully_annotated_decl: "extension Prot where Self.Element == Int", + key.fully_annotated_decl: "extension Prot where Self.Element == Int", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "Prot", diff --git a/test/SourceKit/DocSupport/doc_swift_module1.swift.response b/test/SourceKit/DocSupport/doc_swift_module1.swift.response index 49b9fd2b0259c..662df987b140b 100644 --- a/test/SourceKit/DocSupport/doc_swift_module1.swift.response +++ b/test/SourceKit/DocSupport/doc_swift_module1.swift.response @@ -780,7 +780,7 @@ extension Dictionary.Keys where Key : cake1.P1 { key.kind: source.lang.swift.decl.extension.protocol, key.offset: 186, key.length: 35, - key.fully_annotated_decl: "extension InitProto", + key.fully_annotated_decl: "extension InitProto", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "InitProto", @@ -959,7 +959,7 @@ extension Dictionary.Keys where Key : cake1.P1 { key.kind: source.lang.swift.decl.extension.protocol, key.offset: 531, key.length: 118, - key.fully_annotated_decl: "extension P2", + key.fully_annotated_decl: "extension P2", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "P2", @@ -1038,7 +1038,7 @@ extension Dictionary.Keys where Key : cake1.P1 { ], key.offset: 651, key.length: 64, - key.fully_annotated_decl: "extension P2 where Self : P3", + key.fully_annotated_decl: "extension P2 where Self : P3", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "P2", @@ -1091,7 +1091,7 @@ extension Dictionary.Keys where Key : cake1.P1 { ], key.offset: 757, key.length: 45, - key.fully_annotated_decl: "extension Dictionary.Keys", + key.fully_annotated_decl: "extension Dictionary.Keys", key.extends: { key.kind: source.lang.swift.ref.struct, key.name: "Keys", @@ -1120,7 +1120,7 @@ extension Dictionary.Keys where Key : cake1.P1 { ], key.offset: 804, key.length: 66, - key.fully_annotated_decl: "extension Dictionary.Keys where Key : P1", + key.fully_annotated_decl: "extension Dictionary.Keys where Key : P1", key.extends: { key.kind: source.lang.swift.ref.struct, key.name: "Keys", diff --git a/test/SourceKit/DocSupport/doc_swift_module_class_extension.swift.response b/test/SourceKit/DocSupport/doc_swift_module_class_extension.swift.response index a8a3a3056cce9..c657a7be509d8 100644 --- a/test/SourceKit/DocSupport/doc_swift_module_class_extension.swift.response +++ b/test/SourceKit/DocSupport/doc_swift_module_class_extension.swift.response @@ -26,14 +26,11 @@ class E { } class F where T : module_with_class_extension.D { -} -extension F : module_with_class_extension.P8 { + func bar() } -extension F where T : D { - - func bar() +extension F : module_with_class_extension.P8 { } protocol P8 { @@ -270,193 +267,162 @@ extension P8 where Self.T : module_with_class_extension.E { }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 328, + key.offset: 330, + key.length: 4 + }, + { + key.kind: source.lang.swift.syntaxtype.identifier, + key.offset: 335, + key.length: 3 + }, + { + key.kind: source.lang.swift.syntaxtype.keyword, + key.offset: 344, key.length: 9 }, { key.kind: source.lang.swift.ref.class, key.name: "F", key.usr: "s:27module_with_class_extension1FC", - key.offset: 338, + key.offset: 354, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 342, + key.offset: 358, key.length: 27 }, { key.kind: source.lang.swift.ref.protocol, key.name: "P8", key.usr: "s:27module_with_class_extension2P8P", - key.offset: 370, + key.offset: 386, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 378, - key.length: 9 - }, - { - key.kind: source.lang.swift.ref.class, - key.name: "F", - key.usr: "s:27module_with_class_extension1FC", - key.offset: 388, - key.length: 1 - }, - { - key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 390, - key.length: 5 - }, - { - key.kind: source.lang.swift.ref.generic_type_param, - key.name: "T", - key.usr: "s:27module_with_class_extension1FC1Txmfp", - key.offset: 396, - key.length: 1 - }, - { - key.kind: source.lang.swift.ref.class, - key.name: "D", - key.usr: "s:27module_with_class_extension1DC", - key.offset: 400, - key.length: 1 - }, - { - key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 409, - key.length: 4 - }, - { - key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 414, - key.length: 3 - }, - { - key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 423, + key.offset: 394, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 432, + key.offset: 403, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 442, + key.offset: 413, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 457, + key.offset: 428, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 462, + key.offset: 433, key.length: 9 }, { key.kind: source.lang.swift.ref.protocol, key.name: "P8", key.usr: "s:27module_with_class_extension2P8P", - key.offset: 472, + key.offset: 443, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 475, + key.offset: 446, key.length: 5 }, { key.kind: source.lang.swift.ref.generic_type_param, key.name: "Self", key.usr: "s:27module_with_class_extension2P8PA2A1DC1TRczrlE4Selfxmfp", - key.offset: 481, + key.offset: 452, key.length: 4 }, { key.kind: source.lang.swift.ref.associatedtype, key.name: "T", key.usr: "s:27module_with_class_extension2P8P1TQa", - key.offset: 486, + key.offset: 457, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 490, + key.offset: 461, key.length: 27 }, { key.kind: source.lang.swift.ref.class, key.name: "D", key.usr: "s:27module_with_class_extension1DC", - key.offset: 518, + key.offset: 489, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 527, + key.offset: 498, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 532, + key.offset: 503, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 541, + key.offset: 512, key.length: 9 }, { key.kind: source.lang.swift.ref.protocol, key.name: "P8", key.usr: "s:27module_with_class_extension2P8P", - key.offset: 551, + key.offset: 522, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 554, + key.offset: 525, key.length: 5 }, { key.kind: source.lang.swift.ref.generic_type_param, key.name: "Self", key.usr: "s:27module_with_class_extension2P8PA2A1EC1TRczrlE4Selfxmfp", - key.offset: 560, + key.offset: 531, key.length: 4 }, { key.kind: source.lang.swift.ref.associatedtype, key.name: "T", key.usr: "s:27module_with_class_extension2P8P1TQa", - key.offset: 565, + key.offset: 536, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 569, + key.offset: 540, key.length: 27 }, { key.kind: source.lang.swift.ref.class, key.name: "E", key.usr: "s:27module_with_class_extension1EC", - key.offset: 597, + key.offset: 568, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 606, + key.offset: 577, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 611, + key.offset: 582, key.length: 3 } ] @@ -483,7 +449,7 @@ extension P8 where Self.T : module_with_class_extension.E { ], key.offset: 62, key.length: 48, - key.fully_annotated_decl: "extension C : P8", + key.fully_annotated_decl: "extension C : P8", key.conforms: [ { key.kind: source.lang.swift.ref.protocol, @@ -506,7 +472,7 @@ extension P8 where Self.T : module_with_class_extension.E { ], key.offset: 112, key.length: 87, - key.fully_annotated_decl: "extension C where T : D", + key.fully_annotated_decl: "extension C where T : D", key.extends: { key.kind: source.lang.swift.ref.class, key.name: "C", @@ -590,8 +556,19 @@ extension P8 where Self.T : module_with_class_extension.E { } ], key.offset: 272, - key.length: 54, - key.fully_annotated_decl: "class F<T> where T : D" + key.length: 70, + key.fully_annotated_decl: "class F<T> where T : D", + key.entities: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "bar()", + key.usr: "s:27module_with_class_extension2P8PA2A1DC1TRczrlE3baryyF::SYNTHESIZED::s:27module_with_class_extension1FC", + key.original_usr: "s:27module_with_class_extension2P8PA2A1DC1TRczrlE3baryyF", + key.offset: 330, + key.length: 10, + key.fully_annotated_decl: "func bar()" + } + ] }, { key.kind: source.lang.swift.decl.extension.class, @@ -605,9 +582,9 @@ extension P8 where Self.T : module_with_class_extension.E { key.description: "T : D" } ], - key.offset: 328, + key.offset: 344, key.length: 48, - key.fully_annotated_decl: "extension F : P8", + key.fully_annotated_decl: "extension F : P8", key.conforms: [ { key.kind: source.lang.swift.ref.protocol, @@ -621,38 +598,11 @@ extension P8 where Self.T : module_with_class_extension.E { key.usr: "s:27module_with_class_extension1FC" } }, - { - key.kind: source.lang.swift.decl.extension.class, - key.generic_requirements: [ - { - key.description: "T : D" - } - ], - key.offset: 378, - key.length: 43, - key.fully_annotated_decl: "extension F where T : D", - key.extends: { - key.kind: source.lang.swift.ref.class, - key.name: "F", - key.usr: "s:27module_with_class_extension1FC" - }, - key.entities: [ - { - key.kind: source.lang.swift.decl.function.method.instance, - key.name: "bar()", - key.usr: "s:27module_with_class_extension2P8PA2A1DC1TRczrlE3baryyF::SYNTHESIZED::s:27module_with_class_extension1FC", - key.original_usr: "s:27module_with_class_extension2P8PA2A1DC1TRczrlE3baryyF", - key.offset: 409, - key.length: 10, - key.fully_annotated_decl: "func bar()" - } - ] - }, { key.kind: source.lang.swift.decl.protocol, key.name: "P8", key.usr: "s:27module_with_class_extension2P8P", - key.offset: 423, + key.offset: 394, key.length: 37, key.fully_annotated_decl: "protocol P8", key.entities: [ @@ -660,7 +610,7 @@ extension P8 where Self.T : module_with_class_extension.E { key.kind: source.lang.swift.decl.associatedtype, key.name: "T", key.usr: "s:27module_with_class_extension2P8P1TQa", - key.offset: 442, + key.offset: 413, key.length: 16, key.fully_annotated_decl: "associatedtype T" } @@ -673,9 +623,9 @@ extension P8 where Self.T : module_with_class_extension.E { key.description: "Self.T : D" } ], - key.offset: 462, + key.offset: 433, key.length: 77, - key.fully_annotated_decl: "extension P8 where Self.T : D", + key.fully_annotated_decl: "extension P8 where Self.T : D", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "P8", @@ -686,7 +636,7 @@ extension P8 where Self.T : module_with_class_extension.E { key.kind: source.lang.swift.decl.function.method.instance, key.name: "bar()", key.usr: "s:27module_with_class_extension2P8PA2A1DC1TRczrlE3baryyF", - key.offset: 527, + key.offset: 498, key.length: 10, key.fully_annotated_decl: "func bar()" } @@ -699,9 +649,9 @@ extension P8 where Self.T : module_with_class_extension.E { key.description: "Self.T : E" } ], - key.offset: 541, + key.offset: 512, key.length: 77, - key.fully_annotated_decl: "extension P8 where Self.T : E", + key.fully_annotated_decl: "extension P8 where Self.T : E", key.extends: { key.kind: source.lang.swift.ref.protocol, key.name: "P8", @@ -712,7 +662,7 @@ extension P8 where Self.T : module_with_class_extension.E { key.kind: source.lang.swift.decl.function.method.instance, key.name: "baz()", key.usr: "s:27module_with_class_extension2P8PA2A1EC1TRczrlE3bazyyF", - key.offset: 606, + key.offset: 577, key.length: 10, key.fully_annotated_decl: "func baz()" } diff --git a/test/SourceKit/DocSupport/doc_system_module_underscored.swift.response b/test/SourceKit/DocSupport/doc_system_module_underscored.swift.response index 193977d629b5e..69b177d042cbb 100644 --- a/test/SourceKit/DocSupport/doc_system_module_underscored.swift.response +++ b/test/SourceKit/DocSupport/doc_system_module_underscored.swift.response @@ -1057,7 +1057,7 @@ protocol Other1 { ], key.offset: 209, key.length: 185, - key.fully_annotated_decl: "extension A : _UnderscoredProto2 where T == String", + key.fully_annotated_decl: "extension A : _UnderscoredProto2 where T == String", key.conforms: [ { key.kind: source.lang.swift.ref.protocol, @@ -1527,7 +1527,7 @@ protocol Other1 { ], key.offset: 1393, key.length: 59, - key.fully_annotated_decl: "extension D : _SomeProto where T : Equatable", + key.fully_annotated_decl: "extension D : _SomeProto where T : Equatable", key.conforms: [ { key.kind: source.lang.swift.ref.protocol, diff --git a/test/SourceKit/DocumentStructure/structure.swift.empty.response b/test/SourceKit/DocumentStructure/structure.swift.empty.response index ec58636238a6f..062c5d749a91d 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.empty.response +++ b/test/SourceKit/DocumentStructure/structure.swift.empty.response @@ -1643,7 +1643,18 @@ key.nameoffset: 2669, key.namelength: 5, key.bodyoffset: 2675, - key.bodylength: 8 + key.bodylength: 8, + key.substructure: [ + { + key.kind: source.lang.swift.expr.argument, + key.offset: 2675, + key.length: 8, + key.nameoffset: 0, + key.namelength: 0, + key.bodyoffset: 2675, + key.bodylength: 8 + } + ] }, { key.kind: source.lang.swift.decl.protocol, @@ -1743,6 +1754,7 @@ key.line: 71, key.column: 5, key.severity: source.diagnostic.severity.error, + key.id: "getset_nontrivial_pattern", key.description: "getter/setter can only be defined for a single variable", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -1750,6 +1762,7 @@ key.line: 77, key.column: 1, key.severity: source.diagnostic.severity.error, + key.id: "c_style_for_stmt_removed", key.description: "C-style for statement has been removed in Swift 3", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.ranges: [ @@ -1763,6 +1776,7 @@ key.line: 116, key.column: 1, key.severity: source.diagnostic.severity.error, + key.id: "expected_keyword_in_decl", key.description: "expected 'func' keyword in instance method declaration", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.fixits: [ @@ -1777,6 +1791,7 @@ key.line: 143, key.column: 12, key.severity: source.diagnostic.severity.error, + key.id: "initializer_decl_wrong_scope", key.description: "initializers may only be declared within a type", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -1784,6 +1799,7 @@ key.line: 147, key.column: 1, key.severity: source.diagnostic.severity.error, + key.id: "destructor_decl_outside_class", key.description: "deinitializers may only be declared within a class", key.diagnostic_stage: source.diagnostic.stage.swift.parse } diff --git a/test/SourceKit/DocumentStructure/structure.swift.foobar.response b/test/SourceKit/DocumentStructure/structure.swift.foobar.response index 56f7646264ae6..eb0b3f7dd495a 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.foobar.response +++ b/test/SourceKit/DocumentStructure/structure.swift.foobar.response @@ -1643,7 +1643,18 @@ key.nameoffset: 2669, key.namelength: 5, key.bodyoffset: 2675, - key.bodylength: 8 + key.bodylength: 8, + key.substructure: [ + { + key.kind: source.lang.swift.expr.argument, + key.offset: 2675, + key.length: 8, + key.nameoffset: 0, + key.namelength: 0, + key.bodyoffset: 2675, + key.bodylength: 8 + } + ] }, { key.kind: source.lang.swift.decl.protocol, @@ -1744,6 +1755,7 @@ key.column: 5, key.filepath: "-foobar", key.severity: source.diagnostic.severity.error, + key.id: "getset_nontrivial_pattern", key.description: "getter/setter can only be defined for a single variable", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -1752,6 +1764,7 @@ key.column: 1, key.filepath: "-foobar", key.severity: source.diagnostic.severity.error, + key.id: "c_style_for_stmt_removed", key.description: "C-style for statement has been removed in Swift 3", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.ranges: [ @@ -1766,6 +1779,7 @@ key.column: 1, key.filepath: "-foobar", key.severity: source.diagnostic.severity.error, + key.id: "expected_keyword_in_decl", key.description: "expected 'func' keyword in instance method declaration", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.fixits: [ @@ -1781,6 +1795,7 @@ key.column: 12, key.filepath: "-foobar", key.severity: source.diagnostic.severity.error, + key.id: "initializer_decl_wrong_scope", key.description: "initializers may only be declared within a type", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -1789,6 +1804,7 @@ key.column: 1, key.filepath: "-foobar", key.severity: source.diagnostic.severity.error, + key.id: "destructor_decl_outside_class", key.description: "deinitializers may only be declared within a class", key.diagnostic_stage: source.diagnostic.stage.swift.parse } diff --git a/test/SourceKit/DocumentStructure/structure.swift.invalid.response b/test/SourceKit/DocumentStructure/structure.swift.invalid.response index cf49d70c0f369..1c1b0d481942d 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.invalid.response +++ b/test/SourceKit/DocumentStructure/structure.swift.invalid.response @@ -130,6 +130,7 @@ key.column: 7, key.filepath: invalid.swift, key.severity: source.diagnostic.severity.error, + key.id: "number_cant_start_decl_name", key.description: "class name can only start with a letter or underscore, not a number", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -138,6 +139,7 @@ key.column: 1, key.filepath: invalid.swift, key.severity: source.diagnostic.severity.error, + key.id: "statement_begins_with_closure", key.description: "top-level statement cannot begin with a closure expression", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -146,6 +148,7 @@ key.column: 1, key.filepath: invalid.swift, key.severity: source.diagnostic.severity.error, + key.id: "expected_closure_rbrace", key.description: "expected '}' at end of closure", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.diagnostics: [ @@ -154,6 +157,7 @@ key.column: 1, key.filepath: invalid.swift, key.severity: source.diagnostic.severity.note, + key.id: "opening_brace", key.description: "to match this opening '{'" } ] diff --git a/test/SourceKit/DocumentStructure/structure.swift.response b/test/SourceKit/DocumentStructure/structure.swift.response index 411d00f2fdb88..95234d3fcfeeb 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.response +++ b/test/SourceKit/DocumentStructure/structure.swift.response @@ -1643,7 +1643,18 @@ key.nameoffset: 2669, key.namelength: 5, key.bodyoffset: 2675, - key.bodylength: 8 + key.bodylength: 8, + key.substructure: [ + { + key.kind: source.lang.swift.expr.argument, + key.offset: 2675, + key.length: 8, + key.nameoffset: 0, + key.namelength: 0, + key.bodyoffset: 2675, + key.bodylength: 8 + } + ] }, { key.kind: source.lang.swift.decl.protocol, @@ -1744,6 +1755,7 @@ key.column: 5, key.filepath: main.swift, key.severity: source.diagnostic.severity.error, + key.id: "getset_nontrivial_pattern", key.description: "getter/setter can only be defined for a single variable", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -1752,6 +1764,7 @@ key.column: 1, key.filepath: main.swift, key.severity: source.diagnostic.severity.error, + key.id: "c_style_for_stmt_removed", key.description: "C-style for statement has been removed in Swift 3", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.ranges: [ @@ -1766,6 +1779,7 @@ key.column: 1, key.filepath: main.swift, key.severity: source.diagnostic.severity.error, + key.id: "expected_keyword_in_decl", key.description: "expected 'func' keyword in instance method declaration", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.fixits: [ @@ -1781,6 +1795,7 @@ key.column: 12, key.filepath: main.swift, key.severity: source.diagnostic.severity.error, + key.id: "initializer_decl_wrong_scope", key.description: "initializers may only be declared within a type", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -1789,6 +1804,7 @@ key.column: 1, key.filepath: main.swift, key.severity: source.diagnostic.severity.error, + key.id: "destructor_decl_outside_class", key.description: "deinitializers may only be declared within a class", key.diagnostic_stage: source.diagnostic.stage.swift.parse } diff --git a/test/SourceKit/Indexing/index_constructors.swift b/test/SourceKit/Indexing/index_constructors.swift index ef71b77ad0b3b..c460b83b053b7 100644 --- a/test/SourceKit/Indexing/index_constructors.swift +++ b/test/SourceKit/Indexing/index_constructors.swift @@ -1,3 +1,4 @@ +// REQUIRES: rdar79416986 // REQUIRES: objc_interop // RUN: %sourcekitd-test -req=index %s -- %s %S/Inputs/index_constructors_other.swift -module-name index_constructors -Xfrontend -disable-implicit-concurrency-module-import | %sed_clean > %t.response diff --git a/test/SourceKit/Indexing/index_constructors.swift.response b/test/SourceKit/Indexing/index_constructors.swift.response index 186a1efaea342..05b79b8a9391d 100644 --- a/test/SourceKit/Indexing/index_constructors.swift.response +++ b/test/SourceKit/Indexing/index_constructors.swift.response @@ -8,13 +8,6 @@ } ], key.entities: [ - { - key.kind: source.lang.swift.ref.module, - key.name: "Foundation", - key.usr: "c:@M@Foundation", - key.line: 6, - key.column: 8 - }, { key.kind: source.lang.swift.decl.class, key.name: "HorseObject", diff --git a/test/SourceKit/InterfaceGen/Inputs/gen_concurrency.swift b/test/SourceKit/Inputs/concurrency/gen_concurrency.swift similarity index 72% rename from test/SourceKit/InterfaceGen/Inputs/gen_concurrency.swift rename to test/SourceKit/Inputs/concurrency/gen_concurrency.swift index 3fdc5c75fc622..856f88f184f63 100644 --- a/test/SourceKit/InterfaceGen/Inputs/gen_concurrency.swift +++ b/test/SourceKit/Inputs/concurrency/gen_concurrency.swift @@ -1,5 +1,5 @@ class ClassWithAsyncAndHandler { - @completionHandlerAsync("foo(_:)", completionHandlerIndex: 1) + @available(*, renamed: "foo(_:)") func foo(_ operation: String, completionHandler handler: @escaping (Int) -> Void) {} func foo(_ operation: String) async -> Int { 0 } } diff --git a/test/SourceKit/InterfaceGen/Inputs/header_concurrency.h b/test/SourceKit/Inputs/concurrency/header_concurrency.h similarity index 100% rename from test/SourceKit/InterfaceGen/Inputs/header_concurrency.h rename to test/SourceKit/Inputs/concurrency/header_concurrency.h diff --git a/test/SourceKit/Inputs/concurrency/module.map b/test/SourceKit/Inputs/concurrency/module.map new file mode 100644 index 0000000000000..84a5591ca6aab --- /dev/null +++ b/test/SourceKit/Inputs/concurrency/module.map @@ -0,0 +1,3 @@ +module ConcurrencyHeader { + header "header_concurrency.h" +} diff --git a/test/SourceKit/InterfaceGen/gen_objc_concurrency.swift b/test/SourceKit/InterfaceGen/gen_objc_concurrency.swift index 9b312914742de..395812389b6ef 100644 --- a/test/SourceKit/InterfaceGen/gen_objc_concurrency.swift +++ b/test/SourceKit/InterfaceGen/gen_objc_concurrency.swift @@ -3,18 +3,18 @@ // RUN: %empty-directory(%t) -// RUN: %sourcekitd-test -req=interface-gen %S/Inputs/gen_concurrency.swift -- %S/Inputs/gen_concurrency.swift -target %target-triple -I %t -Xfrontend -enable-experimental-concurrency | %FileCheck %s --check-prefix=SWIFT-GEN-INTERFACE +// RUN: %sourcekitd-test -req=interface-gen %S/../Inputs/concurrency/gen_concurrency.swift -- %S/../Inputs/concurrency/gen_concurrency.swift -target %target-triple -I %t | %FileCheck %s --check-prefix=SWIFT-GEN-INTERFACE -// Make sure we print @completionHandlerAsync when it was explicitly written by the user. +// Make sure we print @available when it was explicitly written by the user. // SWIFT-GEN-INTERFACE-LABEL: class ClassWithAsyncAndHandler { -// SWIFT-GEN-INTERFACE: @completionHandlerAsync("foo(_:)", completionHandlerIndex: 1) +// SWIFT-GEN-INTERFACE: @available(*, renamed: "foo(_:)") // SWIFT-GEN-INTERFACE-NEXT: internal func foo(_ operation: String, completionHandler handler: @escaping (Int) -> Void) // SWIFT-GEN-INTERFACE: internal func foo(_ operation: String) async -> Int -// RUN: %sourcekitd-test -req=interface-gen -using-swift-args -header %S/Inputs/header_concurrency.h -- %s -Xfrontend -enable-objc-interop -Xfrontend -enable-experimental-concurrency -import-objc-header %S/Inputs/header_concurrency.h -sdk %clang-importer-sdk | %FileCheck %s --check-prefix=OBJC-GEN-INTERFACE +// RUN: %sourcekitd-test -req=interface-gen -using-swift-args -header %S/../Inputs/concurrency/header_concurrency.h -- %s -Xfrontend -enable-objc-interop -import-objc-header %S/../Inputs/concurrency/header_concurrency.h -sdk %clang-importer-sdk | %FileCheck %s --check-prefix=OBJC-GEN-INTERFACE -// But don't print @completionHandlerAsync if it was implicitly added to an imported Clang decl (rdar://76685011). +// But don't print @available if it was implicitly added to an imported Clang decl (rdar://76685011). // OBJC-GEN-INTERFACE-LABEL: class ClassWithHandlerMethod { -// OBJC-GEN-INTERFACE-NOT: @completionHandlerAsync +// OBJC-GEN-INTERFACE-NOT: @available // OBJC-GEN-INTERFACE: func method(withHandler operation: String!, completionHandler handler: ((Int) -> Void)!) // OBJC-GEN-INTERFACE: func method(withHandler operation: String!) async -> Int diff --git a/test/SourceKit/Misc/compiler_version.swift b/test/SourceKit/Misc/compiler_version.swift index 545e742b0b280..c907b9bfb6d47 100644 --- a/test/SourceKit/Misc/compiler_version.swift +++ b/test/SourceKit/Misc/compiler_version.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=compiler-version | %FileCheck %s // CHECK: key.version_major: 5 -// CHECK: key.version_minor: 5 +// CHECK: key.version_minor: 6 // CHECK: key.version_patch: 0 diff --git a/test/SourceKit/Misc/load-module-with-errors.swift b/test/SourceKit/Misc/load-module-with-errors.swift index 1a6caee049929..3d4266162240d 100644 --- a/test/SourceKit/Misc/load-module-with-errors.swift +++ b/test/SourceKit/Misc/load-module-with-errors.swift @@ -63,7 +63,7 @@ func testInvalidTopLevelCompletion() { // INVALID-TOP-DAG: Decl[GlobalVar]/OtherModule[errors]: invalidGlobalClosureType[#() -> ()#]; // INVALID-TOP-DAG: Decl[Class]/OtherModule[errors]: InvalidClassSub1[#InvalidClassSub1#]; // INVALID-TOP-DAG: Decl[Class]/OtherModule[errors]: InvalidClassSub2[#InvalidClassSub2#]; - // INVALID-TOP-DAG: Decl[Protocol]/OtherModule[errors]: InvalidProtocol[#InvalidProtocol#]; + // INVALID-TOP-DAG: Decl[Protocol]/OtherModule[errors]/Flair[RareType]: InvalidProtocol[#InvalidProtocol#]; // INVALID-TOP-DAG: Decl[FreeFunction]/OtherModule[errors]: invalidFuncThrows()[' throws'][#<>#]; // INVALID-TOP: End completions } @@ -76,7 +76,7 @@ func testInvalidTopLevelCompletion() { // RUN: %target-swift-frontend -merge-modules -emit-module -experimental-allow-module-with-compiler-errors %t/errors.a.swiftmodule %t/errors.b.swiftmodule %t/errors.c.swiftmodule -module-name errors -o %t/errors.swiftmodule // Read the module back in to make sure it can be deserialized -// RUN: %target-swift-ide-test -print-module -source-filename dummy -module-to-print errors -I %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module -source-filename dummy -module-to-print errors -I %t -allow-compiler-errors | %FileCheck %s // CHECK: typealias InvalidAlias = <> // CHECK: class InvalidClass : <>, InvalidProtocol // CHECK: var classMemberA: <> @@ -123,22 +123,22 @@ func testInvalidTopLevelCompletion() { // CHECK: func typeUsesFunc // Check completions -// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t-completions -I %t +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t-completions -I %t -allow-compiler-errors // Check cursor info for the various symbols -// RUN: %sourcekitd-test -req=cursor -pos=4:3 %s -- -I %t -target %target-triple %s | %FileCheck %s -check-prefix=CHECK-GLOBAL +// RUN: %sourcekitd-test -req=cursor -pos=4:3 %s -- -Xfrontend -experimental-allow-module-with-compiler-errors -I %t -target %target-triple %s | %FileCheck %s -check-prefix=CHECK-GLOBAL // CHECK-GLOBAL: source.lang.swift.ref.var.global // CHECK-GLOBAL: invalidGlobalMissingInit -// RUN: %sourcekitd-test -req=cursor -pos=8:3 %s -- -I %t -target %target-triple %s | %FileCheck %s -check-prefix=CHECK-FUNC +// RUN: %sourcekitd-test -req=cursor -pos=8:3 %s -- -Xfrontend -experimental-allow-module-with-compiler-errors -I %t -target %target-triple %s | %FileCheck %s -check-prefix=CHECK-FUNC // CHECK-FUNC: source.lang.swift.ref.function.free // CHECK-FUNC: invalidPartialFunc -// RUN: %sourcekitd-test -req=cursor -pos=12:12 %s -- -I %t -target %target-triple %s | %FileCheck %s -check-prefix=CHECK-STRUCT +// RUN: %sourcekitd-test -req=cursor -pos=12:12 %s -- -Xfrontend -experimental-allow-module-with-compiler-errors -I %t -target %target-triple %s | %FileCheck %s -check-prefix=CHECK-STRUCT // CHECK-STRUCT: source.lang.swift.ref.struct // CHECK-STRUCT: InvalidStruct // Currently doesn't work for any members with invalid types, even within the same module: rdar://71514163 -// RUN: %sourcekitd-test -req=cursor -pos=13:7 %s -- -I %t -target %target-triple %s | not %FileCheck %s -check-prefix=CHECK-MEMBER +// RUN: %sourcekitd-test -req=cursor -pos=13:7 %s -- -Xfrontend -experimental-allow-module-with-compiler-errors -I %t -target %target-triple %s | not %FileCheck %s -check-prefix=CHECK-MEMBER // CHECK-MEMBER: source.lang.swift.ref.var.instance // CHECK-MEMBER: memberB diff --git a/test/SourceKit/Misc/stats.swift b/test/SourceKit/Misc/stats.swift index 34bc164e504ce..41eca42a53ac2 100644 --- a/test/SourceKit/Misc/stats.swift +++ b/test/SourceKit/Misc/stats.swift @@ -2,6 +2,7 @@ func foo() {} // RUN: %sourcekitd-test -req=syntax-map %s == -req=stats | %FileCheck %s -check-prefix=SYNTAX_1 +// SYNTAX_1: {{.*}} source.statistic.instruction-count // SYNTAX_1: 2 {{.*}} source.statistic.num-requests // SYNTAX_1: 0 {{.*}} source.statistic.num-semantic-requests // SYNTAX_1: 0 {{.*}} source.statistic.num-ast-builds diff --git a/test/SourceKit/NameTranslation/swiftnames.swift b/test/SourceKit/NameTranslation/swiftnames.swift index 31ae5e05ca6f8..9deee5f14082e 100644 --- a/test/SourceKit/NameTranslation/swiftnames.swift +++ b/test/SourceKit/NameTranslation/swiftnames.swift @@ -56,40 +56,40 @@ class C3: NSObject { } // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s -// RUN: %sourcekitd-test -req=translate -swift-name '`foo`(`a`:b:c:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s -// RUN: %sourcekitd-test -req=translate -swift-name '`foo(`a:b:c:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW1 %s -// RUN: %sourcekitd-test -req=translate -swift-name "bar(x:y:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKFEWER1 %s -// RUN: %sourcekitd-test -req=translate -swift-name "bar(::)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING1 %s -// RUN: %sourcekitd-test -req=translate -swift-name 'bar(`:`:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING1 %s -// RUN: %sourcekitd-test -req=translate -swift-name "(x:y:z:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING2 %s -// RUN: %sourcekitd-test -req=translate -swift-name '`(x:y:z:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING2 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s -// RUN: %sourcekitd-test -req=translate -swift-name '`foo`(a1:`b1`:c1:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo(_:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK3 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:c2:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK4 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:_:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK5 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo2(a:b:c:)" -pos=12:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK6 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo2(_:_:_:)" -pos=12:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK7 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK8 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW8 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1(a:b:c:)" -pos=14:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK-DIAG %s -// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=1:8 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK9 %s - -// RUN: %sourcekitd-test -req=translate -swift-name "init(a1:b2:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK10 %s -// RUN: %sourcekitd-test -req=translate -swift-name "init(_:_:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK11 %s -// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK9 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:_:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK12 %s - -// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=27:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s -// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=27:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s -// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=41:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK14 %s -// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=41:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK14 %s -// RUN: %sourcekitd-test -req=translate -swift-name "C3" -pos=48:8 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK15 %s -// RUN: %sourcekitd-test -req=translate -swift-name "bar(_:other:)" -pos=51:36 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK16 %s -// RUN: %sourcekitd-test -req=translate -swift-name "zoo(m:)" -pos=55:14 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK17 %s -// RUN: %sourcekitd-test -req=translate -swift-name "zoo(m:)" -pos=55:14 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW17 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=translate -swift-name '`foo`(`a`:b:c:)' -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=translate -swift-name '`foo(`a:b:c:)' -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -print-raw-response -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW1 %s +// RUN: %sourcekitd-test -req=translate -swift-name "bar(x:y:)" -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKFEWER1 %s +// RUN: %sourcekitd-test -req=translate -swift-name "bar(::)" -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING1 %s +// RUN: %sourcekitd-test -req=translate -swift-name 'bar(`:`:)' -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING1 %s +// RUN: %sourcekitd-test -req=translate -swift-name "(x:y:z:)" -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING2 %s +// RUN: %sourcekitd-test -req=translate -swift-name '`(x:y:z:)' -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING2 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:b1:c1:)" -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -swift-name '`foo`(a1:`b1`:c1:)' -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(_:b1:c1:)" -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK3 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:c2:)" -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK4 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:_:)" -pos=11:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK5 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo2(a:b:c:)" -pos=12:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK6 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo2(_:_:_:)" -pos=12:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK7 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK8 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -print-raw-response -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW8 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1(a:b:c:)" -pos=14:11 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK-DIAG %s +// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=1:8 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK9 %s + +// RUN: %sourcekitd-test -req=translate -swift-name "init(a1:b2:)" -pos=10:16 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK10 %s +// RUN: %sourcekitd-test -req=translate -swift-name "init(_:_:)" -pos=10:16 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK11 %s +// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=10:16 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK9 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:_:)" -pos=10:16 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK12 %s + +// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=27:10 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s +// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=27:10 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s +// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=41:10 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK14 %s +// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=41:10 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK14 %s +// RUN: %sourcekitd-test -req=translate -swift-name "C3" -pos=48:8 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK15 %s +// RUN: %sourcekitd-test -req=translate -swift-name "bar(_:other:)" -pos=51:36 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK16 %s +// RUN: %sourcekitd-test -req=translate -swift-name "zoo(m:)" -pos=55:14 %s -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK17 %s +// RUN: %sourcekitd-test -req=translate -swift-name "zoo(m:)" -pos=55:14 %s -print-raw-response -- -Xfrontend -disable-objc-attr-requires-foundation-module -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW17 %s // CHECK-DIAG: // CHECK1: fooWithA:b:c: diff --git a/test/SourceKit/Refactoring/basic.swift b/test/SourceKit/Refactoring/basic.swift index 92ba3e4a9d27e..022d9c5659614 100644 --- a/test/SourceKit/Refactoring/basic.swift +++ b/test/SourceKit/Refactoring/basic.swift @@ -116,9 +116,12 @@ HasInitWithDefaultArgs(y: 45, z: 89) func `hasBackticks`(`x`: Int) {} `hasBackticks`(`x`:2) -func hasAsyncAlternative(completion: (String?, Error?) -> Void) { } -func hasCallToAsyncAlternative() { - hasAsyncAlternative { str, err in print(str!) } +struct ConvertAsync { + func hasAsyncAlternative(completion: @escaping (String?, Error?) -> Void) { } +} +func hasCallToAsyncAlternative(c: ConvertAsync) { + ((((c)).hasAsyncAlternative)) { str, err in print(str!) } + c.hasAsyncAlternative() { str, err in print(str!) } } // RUN: %sourcekitd-test -req=cursor -pos=3:1 -end-pos=5:13 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK1 @@ -129,6 +132,7 @@ func hasCallToAsyncAlternative() { // CHECK1-NEXT: ACTIONS END // RUN: %sourcekitd-test -req=cursor -pos=1:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK2 +// RUN: %sourcekitd-test -req=cursor -offset=16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK2 // RUN: %sourcekitd-test -req=cursor -pos=12:8 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK3 // RUN: %sourcekitd-test -req=cursor -pos=21:5 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK4 @@ -166,12 +170,9 @@ func hasCallToAsyncAlternative() { // RUN: %sourcekitd-test -req=cursor -pos=117:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL // RUN: %sourcekitd-test -req=cursor -pos=117:17 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL -// RUN: %sourcekitd-test -req=cursor -pos=119:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-ASYNC -// RUN: %sourcekitd-test -req=cursor -pos=121:3 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC - -// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT // RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT +// RUN: %sourcekitd-test -req=cursor -pos=54:10 -end-pos=54:22 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-LOCAL // RUN: %sourcekitd-test -req=cursor -pos=54:12 -end-pos=54:22 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-SELF-RENAME1 // RUN: %sourcekitd-test -req=cursor -pos=54:23 -end-pos=54:33 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-SELF-RENAME2 // RUN: %sourcekitd-test -req=cursor -pos=54:34 -end-pos=54:44 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-SELF-RENAME3 @@ -183,6 +184,17 @@ func hasCallToAsyncAlternative() { // RUN: %sourcekitd-test -req=cursor -pos=72:5 -end-pos=72:11 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT // RUN: %sourcekitd-test -req=cursor -pos=78:3 -end-pos=78:9 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT +// RUN: %sourcekitd-test -req=cursor -pos=120:8 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-ASYNC +// RUN: %sourcekitd-test -req=cursor -pos=123:11 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC +// RUN: %sourcekitd-test -req=cursor -pos=123:11 -end-pos=123:30 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC +// RUN: %sourcekitd-test -req=cursor -pos=123:3 -end-pos=123:30 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC +// RUN: %sourcekitd-test -req=cursor -pos=123:3 -end-pos=123:60 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC +// RUN: %sourcekitd-test -req=cursor -pos=123:3 -end-pos=123:46 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC +// RUN: %sourcekitd-test -req=cursor -pos=123:3 -end-pos=123:58 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC +// RUN: %sourcekitd-test -req=cursor -pos=124:3 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC +// RUN: %sourcekitd-test -req=cursor -pos=124:3 -end-pos=124:26 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC +// RUN: %sourcekitd-test -req=cursor -pos=124:3 -end-pos=124:54 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC + // CHECK-NORENAME-NOT: Global Rename // CHECK-NORENAME-NOT: Local Rename @@ -244,16 +256,20 @@ func hasCallToAsyncAlternative() { // CHECK-ASYNC-NEXT: Convert Function to Async // CHECK-ASYNC-NEXT: source.refactoring.kind.add.async-alternative // CHECK-ASYNC-NEXT: Add Async Alternative +// CHECK-ASYNC-NEXT: source.refactoring.kind.add.async-wrapper +// CHECK-ASYNC-NEXT: Add Async Wrapper // CHECK-ASYNC-NOT: source.refactoring.kind.convert.call-to-async // CHECK-ASYNC: ACTIONS END // CHECK-CALLASYNC: ACTIONS BEGIN -// CHECK-ASYNC-NOT: source.refactoring.kind.add.async-alternative -// CHECK-ASYNC-NOT: source.refactoring.kind.convert.func-to-async +// CHECK-CALLASYNC-NOT: source.refactoring.kind.add.async-alternative +// CHECK-CALLASYNC-NOT: source.refactoring.kind.convert.func-to-async +// CHECK-CALLASYNC-NOT: source.refactoring.kind.add.async-wrapper // CHECK-CALLASYNC: source.refactoring.kind.convert.call-to-async // CHECK-CALLASYNC-NEXT: Convert Call to Async Alternative -// CHECK-ASYNC-NOT: source.refactoring.kind.add.async-alternative -// CHECK-ASYNC-NOT: source.refactoring.kind.convert.func-to-async +// CHECK-CALLASYNC-NOT: source.refactoring.kind.add.async-alternative +// CHECK-CALLASYNC-NOT: source.refactoring.kind.convert.func-to-async +// CHECK-CALLASYNC-NOT: source.refactoring.kind.add.async-wrapper // CHECK-CALLASYNC: ACTIONS END // REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/rename-objc.swift b/test/SourceKit/Refactoring/rename-objc.swift index b9266c2b24116..61a53da1f6341 100644 --- a/test/SourceKit/Refactoring/rename-objc.swift +++ b/test/SourceKit/Refactoring/rename-objc.swift @@ -8,14 +8,11 @@ func foo1() { // RUN: %sourcekitd-test -req=cursor -pos=4:30 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK1 // RUN: %sourcekitd-test -req=cursor -pos=4:30 -length=3 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK1 // RUN: %sourcekitd-test -req=cursor -pos=4:20 -length=15 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK1 -// RUN: %sourcekitd-test -req=cursor -pos=4:20 -length=16 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK2 +// RUN: %sourcekitd-test -req=cursor -pos=4:20 -length=16 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK1 // CHECK1: ACTIONS BEGIN // CHECK1-NEXT: source.refactoring.kind.rename.global // CHECK1-NEXT: Global Rename // CHECK1-NEXT: cannot rename a Clang symbol from its Swift reference -// CHECK2: ACTIONS BEGIN -// CHECK2-NEXT: ACTIONS END - // REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/expand-default.swift b/test/SourceKit/Refactoring/semantic-refactoring/expand-default.swift index a3a2780239959..2e564cbd88d1c 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/expand-default.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/expand-default.swift @@ -10,7 +10,7 @@ func foo(e : E) { } // RUN: %empty-directory(%t.result) -// RUN: %sourcekitd-test -req=expand-default -pos=7:7 %s -- %s > %t.result/expand-default.swift.expected +// RUN: %sourcekitd-test -req=refactoring.expand.default -pos=7:7 %s -- %s > %t.result/expand-default.swift.expected // RUN: %diff -u %S/expand-default.swift.expected %t.result/expand-default.swift.expected // REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/extract-func-default.swift b/test/SourceKit/Refactoring/semantic-refactoring/extract-func-default.swift index e313ae3910519..d2b6999e8e50e 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/extract-func-default.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/extract-func-default.swift @@ -5,7 +5,7 @@ func foo() -> Int { } // RUN: %empty-directory(%t.result) -// RUN: %sourcekitd-test -req=extract-func -pos=2:1 -end-pos 4:11 %s -- %s > %t.result/extract-func-default.swift.expected +// RUN: %sourcekitd-test -req=refactoring.extract.function -pos=2:1 -end-pos 4:11 %s -- %s > %t.result/extract-func-default.swift.expected // RUN: %diff -u %S/extract-func-default.swift.expected %t.result/extract-func-default.swift.expected // FIXME: Fails on linux with assertion: "!GlibcModuleMapPath.empty()"" failed diff --git a/test/SourceKit/Refactoring/semantic-refactoring/extract-func-with-args.swift b/test/SourceKit/Refactoring/semantic-refactoring/extract-func-with-args.swift index bcf6559b0d430..446eba24feccd 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/extract-func-with-args.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/extract-func-with-args.swift @@ -5,7 +5,7 @@ func foo() -> Int { } // RUN: %empty-directory(%t.result) -// RUN: %sourcekitd-test -req=extract-func -pos=3:1 -end-pos 3:12 -name new_name %s -- %s > %t.result/extract-func-with-args.swift.expected +// RUN: %sourcekitd-test -req=refactoring.extract.function -pos=3:1 -end-pos 3:12 -name new_name %s -- %s > %t.result/extract-func-with-args.swift.expected // RUN: %diff -u %S/extract-func-with-args.swift.expected %t.result/extract-func-with-args.swift.expected // FIXME: Fails on linux with assertion: "!GlibcModuleMapPath.empty()"" failed diff --git a/test/SourceKit/Refactoring/semantic-refactoring/extract-func.swift b/test/SourceKit/Refactoring/semantic-refactoring/extract-func.swift index 0c1ccb29c697b..f7ea7d3a7ad6a 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/extract-func.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/extract-func.swift @@ -5,7 +5,7 @@ func foo() -> Int { } // RUN: %empty-directory(%t.result) -// RUN: %sourcekitd-test -req=extract-func -pos=2:1 -end-pos 4:11 -name new_name %s -- %s > %t.result/extract-func.swift.expected +// RUN: %sourcekitd-test -req=refactoring.extract.function -pos=2:1 -end-pos 4:11 -name new_name %s -- %s > %t.result/extract-func.swift.expected // RUN: %diff -u %S/extract-func.swift.expected %t.result/extract-func.swift.expected // FIXME: Fails on linux with assertion: "!GlibcModuleMapPath.empty()"" failed diff --git a/test/SourceKit/Refactoring/semantic-refactoring/extract-repeated-expression.swift b/test/SourceKit/Refactoring/semantic-refactoring/extract-repeated-expression.swift index c1b1b76c959f5..279faf780c362 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/extract-repeated-expression.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/extract-repeated-expression.swift @@ -5,7 +5,7 @@ func foo() -> Int { } // RUN: %empty-directory(%t.result) -// RUN: %sourcekitd-test -req=extract-repeated -pos=3:11 -end-pos 3:12 -name new_name %s -- %s > %t.result/extract-repeated-expression.swift.expected +// RUN: %sourcekitd-test -req=refactoring.extract.expr.repeated -pos=3:11 -end-pos 3:12 -name new_name %s -- %s > %t.result/extract-repeated-expression.swift.expected // RUN: %diff -u %S/extract-repeated-expression.swift.expected %t.result/extract-repeated-expression.swift.expected // REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/fill-stub.swift b/test/SourceKit/Refactoring/semantic-refactoring/fill-stub.swift index 1167f624b0acd..e333ad2cda9a8 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/fill-stub.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/fill-stub.swift @@ -5,7 +5,7 @@ protocol P { class C1 : P {} // RUN: %empty-directory(%t.result) -// RUN: %sourcekitd-test -req=fill-stub -pos=5:8 %s -- %s > %t.result/fill-stub.swift.expected +// RUN: %sourcekitd-test -req=refactoring.fillstub -pos=5:8 %s -- %s > %t.result/fill-stub.swift.expected // RUN: %diff -u %S/fill-stub.swift.expected %t.result/fill-stub.swift.expected // REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/line-col-conversion.swift b/test/SourceKit/Refactoring/semantic-refactoring/line-col-conversion.swift index 02aca45a35144..3e6b056e915d3 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/line-col-conversion.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/line-col-conversion.swift @@ -1,4 +1,4 @@ -// RUN: %sourcekitd-test -req=local-rename -pos=4:7 -name new_name %s -- %s +// RUN: %sourcekitd-test -req=refactoring.rename.local -pos=4:7 -name new_name %s -- %s var Foo: Int { - var missingNewlineAtEndOfFile \ No newline at end of file + var missingNewlineAtEndOfFile diff --git a/test/SourceKit/Refactoring/semantic-refactoring/local-rename.swift b/test/SourceKit/Refactoring/semantic-refactoring/local-rename.swift index fb3604cc8c0cf..79da8edc63a02 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/local-rename.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/local-rename.swift @@ -12,7 +12,7 @@ func foo() { } // RUN: %empty-directory(%t.result) -// RUN: %sourcekitd-test -req=local-rename -pos=2:8 -name new_name %s -- %s > %t.result/local-rename.swift.expected +// RUN: %sourcekitd-test -req=refactoring.rename.local -pos=2:8 -name new_name %s -- %s > %t.result/local-rename.swift.expected // RUN: %diff -u %S/local-rename.swift.expected %t.result/local-rename.swift.expected // RUN: %sourcekitd-test -req=find-local-rename-ranges -pos=2:8 %s -- %s > %t.result/local-rename-ranges.swift.expected // RUN: %diff -u %S/local-rename-ranges.swift.expected %t.result/local-rename-ranges.swift.expected diff --git a/test/SourceKit/Refactoring/semantic-refactoring/localize-string.swift b/test/SourceKit/Refactoring/semantic-refactoring/localize-string.swift index f90110c09d5b3..26af2a166eca1 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/localize-string.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/localize-string.swift @@ -1,7 +1,7 @@ // REQUIRES: OS=macosx || OS=linux-gnu func foo() -> String { - // RUN: %sourcekitd-test -req=localize-string -pos=%(line+1):10 %s -- %s | %FileCheck %s --check-prefix=CHECK-BASIC + // RUN: %sourcekitd-test -req=refactoring.localize.string -pos=%(line+1):10 %s -- %s | %FileCheck %s --check-prefix=CHECK-BASIC return "abc" // CHECK-BASIC: source.edit.kind.active: // CHECK-BASIC: [[# @LINE-2]]:10-[[# @LINE-2]]:10 "NSLocalizedString(" @@ -11,7 +11,7 @@ func foo() -> String { #sourceLocation(file: "someFile.swift", line: 20) func bar() -> String { - // RUN: %sourcekitd-test -req=localize-string -pos=%(line+1):10 %s -- %s | %FileCheck %s --check-prefix=CHECK-DIRECTIVE + // RUN: %sourcekitd-test -req=refactoring.localize.string -pos=%(line+1):10 %s -- %s | %FileCheck %s --check-prefix=CHECK-DIRECTIVE return "abc" // CHECK-DIRECTIVE: [[# @LINE-1]]:10-[[# @LINE-1]]:10 } diff --git a/test/SourceKit/Sema/edit_nowait.swift b/test/SourceKit/Sema/edit_nowait.swift index cb1585c388499..0ad478b31f4b8 100644 --- a/test/SourceKit/Sema/edit_nowait.swift +++ b/test/SourceKit/Sema/edit_nowait.swift @@ -1,9 +1,9 @@ // RUN: %empty-directory(%t) -// RUN: echo "" > %t/t.swift +// RUN: echo "func f() { f() }" > %t/t.swift // RUN: %sourcekitd-test \ -// RUN: -req=open %t/t.swift -- %t/t.swift == \ -// RUN: -req=edit -offset=0 -replace="func foo() { warn("") }" -length=0 -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/t.swift -print-raw-response == \ +// RUN: -req=open -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/t.swift -print-raw-response -- %t/t.swift == \ +// RUN: -req=edit -offset=0 -replace="func foo() { warn("") }" -length=16 -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/t.swift -print-raw-response == \ // RUN: -req=edit -offset=13 -replace="print" -length=5 -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/t.swift -print-raw-response \ // RUN: | %FileCheck --check-prefix=EDIT_NOWAIT %s @@ -11,16 +11,26 @@ // EDIT_NOWAIT-NEXT: } // EDIT_NOWAIT-NEXT: { // EDIT_NOWAIT-NEXT: } +// EDIT_NOWAIT-NEXT: { +// EDIT_NOWAIT-NEXT: } // RUN: %sourcekitd-test \ -// RUN: %sourcekitd-test \ -// RUN: -req=open %t/t.swift -- %t/t.swift == \ -// RUN: -req=edit -offset=0 -replace="func foo() { warn("") }" -length=0 -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/t.swift == \ +// RUN: -req=open -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/t.swift -- %t/t.swift == \ +// RUN: -req=print-annotations %t/t.swift == \ +// RUN: -req=edit -offset=0 -replace="func foo() { warn("") }" -length=16 -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/t.swift == \ // RUN: -req=edit -offset=13 -replace="print" -length=4 -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/t.swift == \ -// RUN: -req=print-annotations %s \ +// RUN: -req=print-annotations %t/t.swift \ // RUN: | %FileCheck --check-prefix=ANNOTATION %s -// ANNOTATION: [ +// ANNOTATION-LABEL: [ +// ANNOTATION-NEXT: { +// ANNOTATION-NEXT: key.kind: source.lang.swift.ref.function.free, +// ANNOTATION-NEXT: key.offset: 11, +// ANNOTATION-NEXT: key.length: 1 +// ANNOTATION-NEXT: } +// ANNOTATION-NEXT: ] + +// ANNOTATION-LABEL: [ // ANNOTATION-NEXT: { // ANNOTATION-NEXT: key.kind: source.lang.swift.ref.function.free, // ANNOTATION-NEXT: key.offset: 13, diff --git a/test/SourceKit/Sema/enum-toraw/enum-toraw.swift.response b/test/SourceKit/Sema/enum-toraw/enum-toraw.swift.response index c4ad11cfcc2be..944a9ea3f0857 100644 --- a/test/SourceKit/Sema/enum-toraw/enum-toraw.swift.response +++ b/test/SourceKit/Sema/enum-toraw/enum-toraw.swift.response @@ -18,6 +18,7 @@ key.column: 1, key.filepath: t2.swift, key.severity: source.diagnostic.severity.error, + key.id: "missing_return_decl", key.description: "missing return in global function expected to return 'Bool'", key.diagnostic_stage: source.diagnostic.stage.swift.sema } diff --git a/test/SourceKit/Sema/main.swift b/test/SourceKit/Sema/main.swift index ecccd7ca51f05..ac805bdc3b857 100644 --- a/test/SourceKit/Sema/main.swift +++ b/test/SourceKit/Sema/main.swift @@ -6,6 +6,7 @@ // RUN: %sourcekitd-test -req=sema %S/Inputs/top_level.swift -- %s %S/Inputs/top_level.swift -module-name main | %FileCheck %s -check-prefix=TOP_LEVEL_ERROR // TOP_LEVEL_ERROR: key.filepath: {{.*}}top_level.swift // TOP_LEVEL_ERROR-NEXT: key.severity: source.diagnostic.severity.error, +// TOP_LEVEL_ERROR-NEXT: key.id: "illegal_top_level_expr" // TOP_LEVEL_ERROR-NEXT: key.description: {{.*}}top level print("hi") // Top-level code. diff --git a/test/SourceKit/Sema/oslog.swift b/test/SourceKit/Sema/oslog.swift new file mode 100644 index 0000000000000..77cb5c3f06eb1 --- /dev/null +++ b/test/SourceKit/Sema/oslog.swift @@ -0,0 +1,36 @@ +// REQUIRES: VENDOR=apple + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-build-swift -emit-module -module-name Lib -o %t -Xfrontend -experimental-allow-module-with-compiler-errors -Xfrontend -experimental-skip-all-function-bodies %t/lib.swift +// RUN: %sourcekitd-test -req=sema %t/main.swift -- %t/main.swift -I%t -sdk %sdk -Xfrontend -experimental-allow-module-with-compiler-errors | %FileCheck %s +// CHECK-NOT: oslog_invalid_log_message + +// BEGIN lib.swift +import os + +public struct Foo { + public let prop: String + public init() { self.prop = "boop" } +} + +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension OSLogInterpolation { + @_optimize(none) + @_transparent + @_semantics("oslog.requires_constant_arguments") + public mutating func appendInterpolation(_ value: @autoclosure @escaping () -> Foo) { + let v = value() + appendInterpolation(v.prop) + } +} + +// BEGIN main.swift +import os +import Lib + +if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { + let logger = Logger() + logger.log("Log a foo: \(Foo())") +} diff --git a/test/SourceKit/Sema/placeholders.swift.placeholders.response b/test/SourceKit/Sema/placeholders.swift.placeholders.response index 052c7ae3e6fce..f336792915de9 100644 --- a/test/SourceKit/Sema/placeholders.swift.placeholders.response +++ b/test/SourceKit/Sema/placeholders.swift.placeholders.response @@ -5,6 +5,7 @@ key.column: 19, key.filepath: placeholders.swift, key.severity: source.diagnostic.severity.error, + key.id: "cannot_find_type_in_scope", key.description: "cannot find type '<#OtherClass#>' in scope", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.ranges: [ @@ -19,6 +20,7 @@ key.column: 19, key.filepath: placeholders.swift, key.severity: source.diagnostic.severity.error, + key.id: "foreach_sequence_does_not_conform_to_expected_protocol", key.description: "for-in loop requires '()' to conform to 'Sequence'", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.ranges: [ @@ -33,6 +35,7 @@ key.column: 5, key.filepath: placeholders.swift, key.severity: source.diagnostic.severity.warning, + key.id: "type_inferred_to_undesirable_type", key.description: "constant 'myArray' inferred to have type '[()]', which may be unexpected", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.diagnostics: [ @@ -41,6 +44,7 @@ key.column: 5, key.filepath: placeholders.swift, key.severity: source.diagnostic.severity.note, + key.id: "add_explicit_type_annotation_to_silence", key.description: "add an explicit type annotation to silence this warning", key.fixits: [ { diff --git a/test/SourceKit/Sema/sema_build_session.swift b/test/SourceKit/Sema/sema_build_session.swift index c9f93bb47eca8..4eb19a8d2aaed 100644 --- a/test/SourceKit/Sema/sema_build_session.swift +++ b/test/SourceKit/Sema/sema_build_session.swift @@ -12,6 +12,7 @@ func test() { _ = swiftFunc() } +// REQUIRES: radar82364901 // UNSUPPORTED: OS=windows-msvc // ----------------------------------------------------------------------------- @@ -44,14 +45,19 @@ func test() { // CHECK_SYSTEM_2-LABEL: ## THREE // CHECK_SYSTEM_2: key.severity: source.diagnostic.severity.error, +// CHECK_SYSTEM_2-NEXT: key.id: "cannot_find_in_scope", // CHECK_SYSTEM_2-NEXT: key.description: "cannot find 'fooFunc' in scope", // CHECK_SYSTEM_2: key.severity: source.diagnostic.severity.error, +// CHECK_SYSTEM_2-NEXT: key.id: "cannot_find_in_scope", // CHECK_SYSTEM_2-NEXT: key.description: "cannot find 'fooSubFunc' in scope", // CHECK_SYSTEM_2: key.severity: source.diagnostic.severity.error, +// CHECK_SYSTEM_2-NEXT: key.id: "cannot_find_in_scope", // CHECK_SYSTEM_2-NEXT: key.description: "cannot find 'fooHelperFunc' in scope", // CHECK_SYSTEM_2: key.severity: source.diagnostic.severity.error, +// CHECK_SYSTEM_2-NEXT: key.id: "cannot_find_in_scope", // CHECK_SYSTEM_2-NEXT: key.description: "cannot find 'fooHelperSubFunc' in scope", // CHECK_SYSTEM_2: key.severity: source.diagnostic.severity.error, +// CHECK_SYSTEM_2-NEXT: key.id: "cannot_find_in_scope", // CHECK_SYSTEM_2-NEXT: key.description: "cannot find 'fooHelperExplicitFunc' in scope", // ----------------------------------------------------------------------------- @@ -78,8 +84,10 @@ func test() { // CHECK_USER-LABEL: ## TWO // CHECK_USER-NOT: key.severity: // CHECK_USER: key.severity: source.diagnostic.severity.error, +// CHECK_USER-NEXT: key.id: "cannot_find_in_scope", // CHECK_USER-NEXT: key.description: "cannot find 'fooFunc' in scope", // CHECK_USER: key.severity: source.diagnostic.severity.error, +// CHECK_USER-NEXT: key.id: "cannot_find_in_scope", // CHECK_USER-NEXT: key.description: "cannot find 'fooSubFunc' in scope", // CHECK_USER-NOT: key.severity: diff --git a/test/SourceKit/Sema/sema_module.swift b/test/SourceKit/Sema/sema_module.swift index 47a89162f7c00..14f3d226c8a63 100644 --- a/test/SourceKit/Sema/sema_module.swift +++ b/test/SourceKit/Sema/sema_module.swift @@ -3,4 +3,5 @@ Swift.String Swift // CHECK: key.kind: source.lang.swift.ref.struct, // CHECK: key.severity: source.diagnostic.severity.error, +// CHECK-NEXT: key.id: "value_of_metatype_type", // CHECK-NEXT: key.description: "expected member name or constructor call after type name", diff --git a/test/SourceKit/Sema/sema_playground.swift.response b/test/SourceKit/Sema/sema_playground.swift.response index e661df6a239c4..141f57eafb344 100644 --- a/test/SourceKit/Sema/sema_playground.swift.response +++ b/test/SourceKit/Sema/sema_playground.swift.response @@ -11,6 +11,7 @@ key.column: 5, key.filepath: sema_playground.swift, key.severity: source.diagnostic.severity.error, + key.id: "dollar_identifier_decl", key.description: "cannot declare entity named '$blah'; the '$' prefix is reserved for implicitly-synthesized declarations", key.diagnostic_stage: source.diagnostic.stage.swift.sema } diff --git a/test/SourceKit/Sema/sema_symlink.swift.response b/test/SourceKit/Sema/sema_symlink.swift.response index bc811ddb1dcbd..40a200ef71893 100644 --- a/test/SourceKit/Sema/sema_symlink.swift.response +++ b/test/SourceKit/Sema/sema_symlink.swift.response @@ -12,6 +12,7 @@ key.column: 16, key.filepath: real.swift, key.severity: source.diagnostic.severity.error, + key.id: "cannot_find_in_scope", key.description: "cannot find 'goo' in scope", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.ranges: [ diff --git a/test/SourceKit/SyntaxMapData/diags.swift.response b/test/SourceKit/SyntaxMapData/diags.swift.response index 8a4c43e087465..bba7bbfef6ebb 100644 --- a/test/SourceKit/SyntaxMapData/diags.swift.response +++ b/test/SourceKit/SyntaxMapData/diags.swift.response @@ -5,6 +5,7 @@ key.column: 10, key.filepath: parse_error.swift, key.severity: source.diagnostic.severity.error, + key.id: "func_decl_without_paren", key.description: "expected '(' in argument list of function declaration", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.fixits: [ @@ -20,6 +21,7 @@ key.column: 8, key.filepath: parse_error.swift, key.severity: source.diagnostic.severity.error, + key.id: "cannot_find_type_in_scope", key.description: "cannot find type 'Undeclared' in scope", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.ranges: [ @@ -36,6 +38,7 @@ key.column: 13, key.filepath: parse_error.swift, key.severity: source.diagnostic.severity.error, + key.id: "func_decl_without_paren", key.description: "expected '(' in argument list of function declaration", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.fixits: [ @@ -51,6 +54,7 @@ key.column: 8, key.filepath: parse_error.swift, key.severity: source.diagnostic.severity.error, + key.id: "cannot_find_type_in_scope", key.description: "cannot find type 'Undeclared' in scope", key.diagnostic_stage: source.diagnostic.stage.swift.sema, key.ranges: [ diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response index d0497be7f7c88..202f504155d6c 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response +++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response @@ -15,6 +15,7 @@ key.column: 1, key.filepath: syntaxmap-edit-del.swift, key.severity: source.diagnostic.severity.error, + key.id: "extra_rbrace", key.description: "extraneous '}' at top level", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.fixits: [ @@ -64,6 +65,7 @@ key.column: 19, key.filepath: syntaxmap-edit-del.swift, key.severity: source.diagnostic.severity.error, + key.id: "lex_unexpected_block_comment_end", key.description: "unexpected end of block comment", key.diagnostic_stage: source.diagnostic.stage.swift.parse }, @@ -72,6 +74,7 @@ key.column: 1, key.filepath: syntaxmap-edit-del.swift, key.severity: source.diagnostic.severity.error, + key.id: "expected_decl", key.description: "expected declaration", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.diagnostics: [ @@ -80,6 +83,7 @@ key.column: 8, key.filepath: syntaxmap-edit-del.swift, key.severity: source.diagnostic.severity.note, + key.id: "note_in_decl_extension", key.description: "in declaration of 'Foo'" } ] diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 index 4d92f160c569b..274ffc116948d 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 +++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 @@ -15,6 +15,7 @@ key.column: 1, key.filepath: syntaxmap-edit-del.swift, key.severity: source.diagnostic.severity.error, + key.id: "extra_rbrace", key.description: "extraneous '}' at top level", key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.fixits: [ diff --git a/test/SourceKit/VariableType/basic.swift b/test/SourceKit/VariableType/basic.swift new file mode 100644 index 0000000000000..133b25bc83951 --- /dev/null +++ b/test/SourceKit/VariableType/basic.swift @@ -0,0 +1,35 @@ +let x: Int = 3 +let y = "abc" + +var foo = ["abc" + "def"] + +struct A { + let x: String = "" + let y = "" +} + +class B { + var x = 4.0 + var y = [A]() + var z: [Int: Int] = [:] + var w: (Int) -> Int { { $0 * 2 } } +} + +func foo() { + var local = 5 +} + +let `else` = 3 + +// RUN: %sourcekitd-test -req=collect-var-type %s -- %s | %FileCheck %s +// CHECK: (1:5, 1:6): Int (explicit type: 1) +// CHECK: (2:5, 2:6): String (explicit type: 0) +// CHECK: (4:5, 4:8): [String] (explicit type: 0) +// CHECK: (7:7, 7:8): String (explicit type: 1) +// CHECK: (8:7, 8:8): String (explicit type: 0) +// CHECK: (12:7, 12:8): Double (explicit type: 0) +// CHECK: (13:7, 13:8): [A] (explicit type: 0) +// CHECK: (14:7, 14:8): [Int : Int] (explicit type: 1) +// CHECK: (15:7, 15:8): (Int) -> Int (explicit type: 1) +// CHECK: (19:7, 19:12): Int (explicit type: 0) +// CHECK: (22:5, 22:11): Int (explicit type: 0) diff --git a/test/SourceKit/VariableType/case.swift b/test/SourceKit/VariableType/case.swift new file mode 100644 index 0000000000000..abd1c338645a4 --- /dev/null +++ b/test/SourceKit/VariableType/case.swift @@ -0,0 +1,21 @@ +if case let x? = Optional.some(5) {} +if case let y: Int? = Optional.some(5) {} +guard case let z: String? = Optional.some("a") else { fatalError() } +while case let w: String? = Optional.some("b") {} + +enum Pair { + case pair(U, V) +} + +switch Pair.pair(Optional.some(0), "test") { +case .pair(let u, var v): + break +} + +// RUN: %sourcekitd-test -req=collect-var-type %s -- %s | %FileCheck %s +// CHECK: (1:13, 1:14): Int (explicit type: 0) +// CHECK: (2:13, 2:14): Int? (explicit type: 1) +// CHECK: (3:16, 3:17): String? (explicit type: 1) +// CHECK: (4:16, 4:17): String? (explicit type: 1) +// CHECK: (11:16, 11:17): Int? (explicit type: 0) +// CHECK: (11:23, 11:24): String (explicit type: 0) diff --git a/test/SourceKit/VariableType/error.swift b/test/SourceKit/VariableType/error.swift new file mode 100644 index 0000000000000..5dc572e4c69fc --- /dev/null +++ b/test/SourceKit/VariableType/error.swift @@ -0,0 +1,6 @@ +let x = "an error type" + 3 +let y = "not an error type" + +// RUN: %sourcekitd-test -req=collect-var-type %s -- %s | %FileCheck %s +// CHECK-NOT: (1:5, 1:6) +// CHECK: (2:5, 2:6): String (explicit type: 0) diff --git a/test/SourceKit/VariableType/if-let.swift b/test/SourceKit/VariableType/if-let.swift new file mode 100644 index 0000000000000..ebedb6c325e3d --- /dev/null +++ b/test/SourceKit/VariableType/if-let.swift @@ -0,0 +1,17 @@ +guard var opt: Int = .some(23) else { fatalError() } + +func foo() { + guard let x = .some("abc") else { return } + guard let y: String = .some("def") else { return } + + if let z: Int = .some(4) {} + + while var w: Int = .some(5) {} +} + +// RUN: %sourcekitd-test -req=collect-var-type %s -- %s | %FileCheck %s +// CHECK: (1:11, 1:14): Int (explicit type: 1) +// CHECK: (4:13, 4:14): String (explicit type: 0) +// CHECK: (5:13, 5:14): String (explicit type: 1) +// CHECK: (7:10, 7:11): Int (explicit type: 1) +// CHECK: (9:13, 9:14): Int (explicit type: 1) diff --git a/test/SourceKit/VariableType/inout.swift b/test/SourceKit/VariableType/inout.swift new file mode 100644 index 0000000000000..f0988ad50beac --- /dev/null +++ b/test/SourceKit/VariableType/inout.swift @@ -0,0 +1,10 @@ +func x(_ param: inout Int) -> Int { + param = 4 +} + +let z = { (param: inout String) in } + +// RUN: %sourcekitd-test -req=collect-var-type %s -- %s | %FileCheck %s +// CHECK: (1:10, 1:15): Int (explicit type: 1) +// CHECK: (5:5, 5:6): (inout String) -> () (explicit type: 0) +// CHECK: (5:12, 5:17): String (explicit type: 1) diff --git a/test/SourceKit/VariableType/params.swift b/test/SourceKit/VariableType/params.swift new file mode 100644 index 0000000000000..fd946dc3d4916 --- /dev/null +++ b/test/SourceKit/VariableType/params.swift @@ -0,0 +1,20 @@ +func x(_ param: Int) -> Int { + param +} + +let y: (String) -> Void = { param in } + +let z = { (param: String) in + param.count +} + +let w: (String, Int) -> Void = { (_, x) in } + +// RUN: %sourcekitd-test -req=collect-var-type %s -- %s | %FileCheck %s +// CHECK: (1:10, 1:15): Int (explicit type: 1) +// CHECK: (5:5, 5:6): (String) -> Void (explicit type: 1) +// CHECK: (5:29, 5:34): String (explicit type: 0) +// CHECK: (7:5, 7:6): (String) -> Int (explicit type: 0) +// CHECK: (7:12, 7:17): String (explicit type: 1) +// CHECK: (11:35, 11:36): String (explicit type: 0) +// CHECK: (11:38, 11:39): Int (explicit type: 0) diff --git a/test/SourceKit/VariableType/ranged.swift b/test/SourceKit/VariableType/ranged.swift new file mode 100644 index 0000000000000..09f4d27c7ff1e --- /dev/null +++ b/test/SourceKit/VariableType/ranged.swift @@ -0,0 +1,10 @@ +let x = 1 +let y = "abc" +let z: String = "def" +var w = 4 + +// RUN: %sourcekitd-test -req=collect-var-type -pos=2:1 -end-pos=4:1 %s -- %s | %FileCheck %s +// CHECK-NOT: (1:5, 1:6) +// CHECK: (2:5, 2:6): String (explicit type: 0) +// CHECK: (3:5, 3:6): String (explicit type: 1) +// CHECK-NOT: (4:5, 4:6) diff --git a/test/SourceKit/VariableType/unnamed.swift b/test/SourceKit/VariableType/unnamed.swift new file mode 100644 index 0000000000000..babc3595e9fda --- /dev/null +++ b/test/SourceKit/VariableType/unnamed.swift @@ -0,0 +1,10 @@ +let = 2 +let : Int = 4 + +let x: (String) -> Void = { (: String) in } + +// RUN: %sourcekitd-test -req=collect-var-type %s -- %s | %FileCheck %s +// CHECK: +// CHECK-NEXT: (4:5, 4:6): (String) -> Void (explicit type: 1) +// CHECK-NEXT: + diff --git a/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/Headers/ObjcProperty.h b/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/Headers/ObjcProperty.h new file mode 100644 index 0000000000000..fc5f45932c0eb --- /dev/null +++ b/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/Headers/ObjcProperty.h @@ -0,0 +1,12 @@ +@import Foundation; + +@class Foo; + +@interface Foo : NSObject + +@property (nullable, strong, nonatomic) NSDate *today; + +- (void)selectDate:(nullable NSDate *)date; + +@end + diff --git a/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/Modules/ObjcProperty.swiftmodule/.keep b/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/Modules/ObjcProperty.swiftmodule/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/ObjcProperty b/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/ObjcProperty new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/module.map b/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/module.map new file mode 100644 index 0000000000000..4ef75dd43304a --- /dev/null +++ b/test/SymbolGraph/ClangImporter/Inputs/ObjcProperty/ObjcProperty.framework/module.map @@ -0,0 +1,4 @@ +framework module ObjcProperty { + header "ObjcProperty.h" + export * +} diff --git a/test/SymbolGraph/ClangImporter/ObjcProperty.swift b/test/SymbolGraph/ClangImporter/ObjcProperty.swift new file mode 100644 index 0000000000000..918ef59c36fd7 --- /dev/null +++ b/test/SymbolGraph/ClangImporter/ObjcProperty.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/ObjcProperty/ObjcProperty.framework %t +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module -o %t/ObjcProperty.framework/Modules/ObjcProperty.swiftmodule/%target-swiftmodule-name -import-underlying-module -F %t -module-name ObjcProperty -disable-objc-attr-requires-foundation-module %s +// RUN: %target-swift-symbolgraph-extract -sdk %clang-importer-sdk -module-name ObjcProperty -F %t -output-dir %t -pretty-print -v +// RUN: %FileCheck %s --input-file %t/ObjcProperty.symbols.json + +// REQUIRES: objc_interop + +import Foundation + +public enum SwiftEnum {} + +// CHECK: "precise": "c:objc(cs)Foo(py)today" diff --git a/test/SymbolGraph/Module/CrossImport.swift b/test/SymbolGraph/Module/CrossImport.swift index 2ff600c5d3ff4..6de0a865df9de 100644 --- a/test/SymbolGraph/Module/CrossImport.swift +++ b/test/SymbolGraph/Module/CrossImport.swift @@ -4,7 +4,10 @@ // RUN: %target-build-swift %s -module-name _A_B -I %t -emit-module -emit-module-path %t/ // RUN: cp -r %S/Inputs/CrossImport/A.swiftcrossimport %t/ // RUN: %target-swift-symbolgraph-extract -module-name A -I %t -pretty-print -output-dir %t -// RUN: %FileCheck %s --input-file %t/A@B@_A_B.symbols.json +// RUN: %FileCheck %s --input-file %t/_A_B@A.symbols.json --check-prefix CHECK-MOD +// RUN: %FileCheck %s --input-file %t/_A_B@A.symbols.json --check-prefix CHECK-A +// RUN: %FileCheck %s --input-file %t/_A_B@B.symbols.json --check-prefix CHECK-MOD +// RUN: %FileCheck %s --input-file %t/_A_B@B.symbols.json --check-prefix CHECK-B @_exported import A import B @@ -15,7 +18,26 @@ extension A { } } -// CHECK: module -// CHECK-NEXT: "name": "A" -// CHECK-NEXT: bystanders -// CHECK-NEXT: B +public struct LocalStruct {} + +extension LocalStruct { + public func someFunc() {} +} + +extension B { + public func untransmogrify() -> A { + return A(x: self.y) + } +} + +// CHECK-MOD: module +// CHECK-MOD-NEXT: "name": "A" +// CHECK-MOD-NEXT: bystanders +// CHECK-MOD-NEXT: B + +// CHECK-A-NOT: s:1BAAV4_A_BE14untransmogrify1AAEVyF +// CHECK-A-DAG: s:1AAAV4_A_BE12transmogrify1BAEVyF +// CHECK-A-DAG: s:4_A_B11LocalStructV +// CHECK-A-DAG: s:4_A_B11LocalStructV8someFuncyyF + +// CHECK-B: s:1BAAV4_A_BE14untransmogrify1AAEVyF diff --git a/test/SymbolGraph/Module/Inputs/CrossImport/A.swift b/test/SymbolGraph/Module/Inputs/CrossImport/A.swift index 4486465d6c03f..4ae4fe5c4eed6 100644 --- a/test/SymbolGraph/Module/Inputs/CrossImport/A.swift +++ b/test/SymbolGraph/Module/Inputs/CrossImport/A.swift @@ -1,3 +1,7 @@ public struct A { public var x: Int + + public init(x: Int) { + self.x = x + } } diff --git a/test/SymbolGraph/Relationships/DefaultImplementationOf/Inputs/RemoteP.swift b/test/SymbolGraph/Relationships/DefaultImplementationOf/Inputs/RemoteP.swift new file mode 100644 index 0000000000000..d02f31fa46b14 --- /dev/null +++ b/test/SymbolGraph/Relationships/DefaultImplementationOf/Inputs/RemoteP.swift @@ -0,0 +1,3 @@ +public protocol RemoteP { + func someFunc() +} diff --git a/test/SymbolGraph/Relationships/DefaultImplementationOf/Remote.swift b/test/SymbolGraph/Relationships/DefaultImplementationOf/Remote.swift new file mode 100644 index 0000000000000..a4ef70eb75282 --- /dev/null +++ b/test/SymbolGraph/Relationships/DefaultImplementationOf/Remote.swift @@ -0,0 +1,26 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %S/Inputs/RemoteP.swift -module-name RemoteP -emit-module -emit-module-path %t/ +// RUN: %target-build-swift %s -module-name Remote -emit-module -emit-module-path %t/ -I %t +// RUN: %target-swift-symbolgraph-extract -module-name Remote -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/Remote.symbols.json +// RUN: %FileCheck %s --input-file %t/Remote.symbols.json --check-prefix MEMBER + +import RemoteP + +public protocol LocalP: RemoteP {} + +public extension LocalP { + func someFunc() {} +} + +// default implementations that are for protocols in a different module should have a `memberOf` +// relation linking them to a local symbol, if one exists + +// CHECK: "kind": "defaultImplementationOf" +// CHECK-NEXT: "source": "s:6Remote6LocalPPAAE8someFuncyyF" +// CHECK-NEXT: "target": "s:7RemotePAAP8someFuncyyF" +// CHECK-NEXT: "targetFallback": "RemoteP.RemoteP.someFunc()" + +// MEMBER: "kind": "memberOf" +// MEMBER-NEXT: "source": "s:6Remote6LocalPPAAE8someFuncyyF" +// MEMBER-NEXT: "target": "s:6Remote6LocalPP" diff --git a/test/SymbolGraph/Relationships/Synthesized/InheritedDocs.swift b/test/SymbolGraph/Relationships/Synthesized/InheritedDocs.swift index 38097441d6c19..d662c22120c99 100644 --- a/test/SymbolGraph/Relationships/Synthesized/InheritedDocs.swift +++ b/test/SymbolGraph/Relationships/Synthesized/InheritedDocs.swift @@ -6,12 +6,18 @@ // RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes IMPL // RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes BONUS // RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes BONUS-DOCS +// RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes EXTRA +// RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes LOCAL +// RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes SUPER // RUN: %target-swift-symbolgraph-extract -module-name InheritedDocs -I %t -pretty-print -output-dir %t -skip-inherited-docs // RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes CHECK,SKIP // RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes IMPL // RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes BONUS // RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes BONUS-SKIP +// RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes EXTRA +// RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes LOCAL +// RUN: %FileCheck %s --input-file %t/InheritedDocs.symbols.json --check-prefixes SUPER // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -module-name InheritedDocs -emit-module -emit-module-path %t/InheritedDocs.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -skip-inherited-docs @@ -46,10 +52,39 @@ // BONUS-NEXT: "identifier": "s:13InheritedDocs1PPAAE9bonusFuncyyF" // BONUS-NEXT: "displayName": "P.bonusFunc()" +// synthesized symbols that don't have docs to inherit still need to have the sourceOrigin field + +// EXTRA: "source": "s:13InheritedDocs1PPAAE9extraFuncyyF::SYNTHESIZED::s:13InheritedDocs1SV" +// EXTRA-NEXT: "target": "s:13InheritedDocs1SV" +// EXTRA-NEXT: "sourceOrigin" +// EXTRA-NEXT: "identifier": "s:13InheritedDocs1PPAAE9extraFuncyyF" +// EXTRA-NEXT: "displayName": "P.extraFunc()" + +// local implementations of a local protocol still need to a relation to that protocol + +// LOCAL: "source": "s:13InheritedDocs1SV9localFuncyyF" +// LOCAL-NEXT: "target": "s:13InheritedDocs1SV" +// LOCAL-NEXT: "sourceOrigin" +// LOCAL-NEXT: "identifier": "s:13InheritedDocs1PP9localFuncyyF" +// LOCAL-NEXT: "displayName": "P.localFunc()" + +// ...both with and without docs + +// SUPER: "source": "s:13InheritedDocs1SV9superFuncyyF" +// SUPER-NEXT: "target": "s:13InheritedDocs1SV" +// SUPER-NEXT: "sourceOrigin" +// SUPER-NEXT: "identifier": "s:13InheritedDocs1PP9superFuncyyF" +// SUPER-NEXT: "displayName": "P.superFunc()" + /// Protocol P public protocol P { /// Some Function func someFunc() + + /// It's a local function! + func localFunc() + + func superFunc() } public extension P { @@ -57,7 +92,12 @@ public extension P { /// Bonus docs! func bonusFunc() {} + + func extraFunc() {} // no docs, but still needs sourceOrigin } public struct S: P { + public func localFunc() {} + + public func superFunc() {} } diff --git a/test/SymbolGraph/Relationships/Synthesized/Inputs/RemoteP.swift b/test/SymbolGraph/Relationships/Synthesized/Inputs/RemoteP.swift new file mode 100644 index 0000000000000..9c5450816ed49 --- /dev/null +++ b/test/SymbolGraph/Relationships/Synthesized/Inputs/RemoteP.swift @@ -0,0 +1,14 @@ +/// Some Protocol +public protocol P { + func someFunc() + + /// This one has docs! + func otherFunc() + + func bonusFunc() +} + +public extension P { + /// Extra default docs! + func extraFunc() {} +} diff --git a/test/SymbolGraph/Relationships/Synthesized/RemoteInheritedDocs.swift b/test/SymbolGraph/Relationships/Synthesized/RemoteInheritedDocs.swift new file mode 100644 index 0000000000000..3d5c55ed7b55f --- /dev/null +++ b/test/SymbolGraph/Relationships/Synthesized/RemoteInheritedDocs.swift @@ -0,0 +1,71 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %S/Inputs/RemoteP.swift -module-name RemoteP -emit-module -emit-module-path %t/RemoteP.swiftmodule -emit-module-source-info-path %t/RemoteP.swiftsourceinfo -emit-module-doc-path %t/RemoteP.swiftdoc +// RUN: %target-swift-frontend %s -module-name RemoteInheritedDocs -emit-module -emit-module-path %t/RemoteInheritedDocs.swiftmodule -emit-module-source-info-path %t/RemoteInheritedDocs.swiftsourceinfo -emit-module-doc-path %t/RemoteInheritedDocs.swiftdoc -I %t + +// RUN: %target-swift-symbolgraph-extract -module-name RemoteInheritedDocs -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix SOME +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix OTHER +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix BONUS +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix INHERIT +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix LOCAL +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix OVERRIDE + +// RUN: %target-swift-symbolgraph-extract -module-name RemoteInheritedDocs -I %t -pretty-print -output-dir %t -skip-inherited-docs +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix SOME +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix OTHER +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix BONUS +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix SKIP +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix LOCAL +// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix OVERRIDE + +// SOME: "source": "s:19RemoteInheritedDocs1SV8someFuncyyF" +// SOME-NEXT: "target": "s:19RemoteInheritedDocs1SV" +// SOME-NEXT: "sourceOrigin" +// SOME-NEXT: "identifier": "s:7RemoteP1PP8someFuncyyF" +// SOME-NEXT: "displayName": "P.someFunc()" + +// OTHER: "source": "s:19RemoteInheritedDocs1SV9otherFuncyyF" +// OTHER-NEXT: "target": "s:19RemoteInheritedDocs1SV" +// OTHER-NEXT: "sourceOrigin" +// OTHER-NEXT: "identifier": "s:7RemoteP1PP9otherFuncyyF" +// OTHER-NEXT: "displayName": "P.otherFunc()" + +// BONUS: "source": "s:19RemoteInheritedDocs1SV9bonusFuncyyF" +// BONUS-NEXT: "target": "s:19RemoteInheritedDocs1SV" +// BONUS-NEXT: "sourceOrigin" +// BONUS-NEXT: "identifier": "s:7RemoteP1PP9bonusFuncyyF" +// BONUS-NEXT: "displayName": "P.bonusFunc()" + +// INHERIT: This one has docs! +// SKIP-NOT: This one has docs! + +// LOCAL: Local docs override! + +// OVERRIDE-NOT: Extra default docs! +// OVERRIDE-NOT: Extension override! + +import RemoteP + +// The `RemoteP.P` protocol has three methods: `someFunc` and `bonusFunc` don't have docs upstream, +// but `otherFunc` does. Regardless, each one needs a `sourceOrigin` field connecting it to +// upstream. + +// `RemoteP.P` also has an extension with a default implementation for `extraFunc` that does have +// docs, but overriding it here should prevent those from appearing + +public struct S: P { + public func someFunc() {} + + public func otherFunc() {} + + /// Local docs override! + public func bonusFunc() {} + + public func extraFunc() {} +} + +public extension P { + /// Extension override! + func someFunc() {} +} + diff --git a/test/SymbolGraph/Relationships/Synthesized/SuperclassImplementation.swift b/test/SymbolGraph/Relationships/Synthesized/SuperclassImplementation.swift index 36736121fe5c4..f65e1f49f5dcd 100644 --- a/test/SymbolGraph/Relationships/Synthesized/SuperclassImplementation.swift +++ b/test/SymbolGraph/Relationships/Synthesized/SuperclassImplementation.swift @@ -3,15 +3,24 @@ // RUN: %target-swift-symbolgraph-extract -module-name SuperclassImplementation -I %t -pretty-print -output-dir %t // RUN: %FileCheck %s --input-file %t/SuperclassImplementation.symbols.json +// This test references code that has been removed. The implementation that synthesized superclass +// methods was inconsistent; it failed to generate symbols for superclasses from another module. +// From a symbol-relation perspective, the `inheritsFrom` relation from a subclass to its superclass +// still exists, which already implies that all of the superclass's methods are available on the +// subclass. The synthesized methods for subclasses were removed to provide consistency between +// superclasses from the same module and those from a different one. If the implementation is +// brought back, ensure that it consistently adds synthesized methods for superclasses from +// different modules. + public class Base { public init() {} public func foo() {} } public class Derived: Base { - // CHECK-DAG: "precise": "s:24SuperclassImplementation4BaseC3fooyyF::SYNTHESIZED::s:24SuperclassImplementation7DerivedC" + // CHECK-NOT: "precise": "s:24SuperclassImplementation4BaseC3fooyyF::SYNTHESIZED::s:24SuperclassImplementation7DerivedC" } public class DerivedDerived: Derived { - // CHECK-DAG: "precise": "s:24SuperclassImplementation4BaseC3fooyyF::SYNTHESIZED::s:24SuperclassImplementation07DerivedC0C" + // CHECK-NOT: "precise": "s:24SuperclassImplementation4BaseC3fooyyF::SYNTHESIZED::s:24SuperclassImplementation07DerivedC0C" } diff --git a/test/SymbolGraph/Symbols/Mixins/SPI.swift b/test/SymbolGraph/Symbols/Mixins/SPI.swift new file mode 100644 index 0000000000000..4612d7d93e1e9 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/SPI.swift @@ -0,0 +1,30 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SPI -emit-module -emit-module-path %t/SPI.swiftmodule -include-spi-symbols +// RUN: %target-swift-symbolgraph-extract -module-name SPI -I %t -pretty-print -output-dir %t -include-spi-symbols +// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix SPI +// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix SPIDOC + +// RUN: %target-swift-symbolgraph-extract -module-name SPI -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix NOSPI + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SPI -emit-module -emit-module-path %t/SPI.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -include-spi-symbols -v +// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix SPI-COMPILE +// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix SPIDOC + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SPI -emit-module -emit-module-path %t/SPI.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ +// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix NOSPI-COMPILE + +/// This is some struct, there +@_spi(SPI) public struct SomeStruct {} + +// SPI: "precise": "s:3SPI10SomeStructV" +// SPI: "spi": true + +// NOSPI-NOT: "precise": "s:3SPI10SomeStructV" + +// SPI-COMPILE: s:3SPI10SomeStructV +// NOSPI-COMPILE-NOT: s:3SPI10SomeStructV + +// SPIDOC: This is some struct, there diff --git a/test/Syntax/Inputs/serialize_distributed_actor.json b/test/Syntax/Inputs/serialize_distributed_actor.json new file mode 100644 index 0000000000000..a4e2e16c5c231 --- /dev/null +++ b/test/Syntax/Inputs/serialize_distributed_actor.json @@ -0,0 +1,434 @@ +{ + "kind": "SourceFile", + "layout": [ + { + "kind": "CodeBlockItemList", + "layout": [ + { + "kind": "CodeBlockItem", + "layout": [ + { + "kind": "ClassDecl", + "layout": [ + null, + { + "kind": "ModifierList", + "layout": [ + { + "kind": "DeclModifier", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "distributed" + }, + "leadingTrivia": "\/\/ RUN: %swift-syntax-test -input-source-filename %s -serialize-raw-tree > %t\n\/\/ RUN: diff %t %S\/Inputs\/serialize_distributed_actor.json -u\n\n", + "trailingTrivia": " ", + "presence": "Present" + }, + null, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "contextual_keyword", + "text": "actor" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "tokenKind": { + "kind": "identifier", + "text": "DA" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + null, + null, + null, + { + "kind": "MemberDeclBlock", + "layout": [ + { + "tokenKind": { + "kind": "l_brace" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "MemberDeclList", + "layout": [ + { + "kind": "MemberDeclListItem", + "layout": [ + { + "kind": "FunctionDecl", + "layout": [ + null, + { + "kind": "ModifierList", + "layout": [ + { + "kind": "DeclModifier", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "distributed" + }, + "leadingTrivia": "\n ", + "trailingTrivia": " ", + "presence": "Present" + }, + null, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "kw_func" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "tokenKind": { + "kind": "identifier", + "text": "hello" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null, + { + "kind": "FunctionSignature", + "layout": [ + { + "kind": "ParameterClause", + "layout": [ + { + "tokenKind": { + "kind": "l_paren" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "FunctionParameterList", + "layout": [ + { + "kind": "FunctionParameter", + "layout": [ + null, + { + "tokenKind": { + "kind": "identifier", + "text": "name" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null, + { + "tokenKind": { + "kind": "colon" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "kind": "SimpleTypeIdentifier", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "String" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + null, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_paren" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + null, + { + "kind": "ReturnClause", + "layout": [ + { + "tokenKind": { + "kind": "arrow" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "kind": "SimpleTypeIdentifier", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "String" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + { + "kind": "CodeBlock", + "layout": [ + { + "tokenKind": { + "kind": "l_brace" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "CodeBlockItemList", + "layout": [ + { + "kind": "CodeBlockItem", + "layout": [ + { + "kind": "StringLiteralExpr", + "layout": [ + null, + { + "tokenKind": { + "kind": "string_quote" + }, + "leadingTrivia": "\n ", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "StringLiteralSegments", + "layout": [ + { + "kind": "StringSegment", + "layout": [ + { + "tokenKind": { + "kind": "string_segment", + "text": "Hello " + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "kind": "ExpressionSegment", + "layout": [ + { + "tokenKind": { + "kind": "backslash" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null, + { + "tokenKind": { + "kind": "l_paren" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "TupleExprElementList", + "layout": [ + { + "kind": "TupleExprElement", + "layout": [ + null, + null, + { + "kind": "IdentifierExpr", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "name" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "string_interpolation_anchor" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "kind": "StringSegment", + "layout": [ + { + "tokenKind": { + "kind": "string_segment", + "text": "!" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "string_quote" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_brace" + }, + "leadingTrivia": "\n ", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_brace" + }, + "leadingTrivia": "\n", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "eof", + "text": "" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" +} diff --git a/test/Syntax/Inputs/serialize_main_actor.json b/test/Syntax/Inputs/serialize_main_actor.json new file mode 100644 index 0000000000000..f8a0dd358d09e --- /dev/null +++ b/test/Syntax/Inputs/serialize_main_actor.json @@ -0,0 +1,707 @@ +{ + "kind": "SourceFile", + "layout": [ + { + "kind": "CodeBlockItemList", + "layout": [ + { + "kind": "CodeBlockItem", + "layout": [ + { + "kind": "StructDecl", + "layout": [ + null, + null, + { + "tokenKind": { + "kind": "kw_struct" + }, + "leadingTrivia": "\/\/ RUN: %swift-syntax-test -input-source-filename %s -serialize-raw-tree > %t\n\/\/ RUN: diff %t %S\/Inputs\/serialize_main_actor.json -u\n\n", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "tokenKind": { + "kind": "identifier", + "text": "Foo" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + null, + null, + null, + { + "kind": "MemberDeclBlock", + "layout": [ + { + "tokenKind": { + "kind": "l_brace" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "MemberDeclList", + "layout": [ + { + "kind": "MemberDeclListItem", + "layout": [ + { + "kind": "InitializerDecl", + "layout": [ + null, + null, + { + "tokenKind": { + "kind": "kw_init" + }, + "leadingTrivia": "\n ", + "trailingTrivia": "", + "presence": "Present" + }, + null, + null, + { + "kind": "ParameterClause", + "layout": [ + { + "tokenKind": { + "kind": "l_paren" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "FunctionParameterList", + "layout": [ + { + "kind": "FunctionParameter", + "layout": [ + null, + { + "tokenKind": { + "kind": "kw__" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "tokenKind": { + "kind": "identifier", + "text": "foo" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "tokenKind": { + "kind": "colon" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "kind": "AttributedType", + "layout": [ + null, + { + "kind": "AttributeList", + "layout": [ + { + "kind": "CustomAttribute", + "layout": [ + { + "tokenKind": { + "kind": "at_sign" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "SimpleTypeIdentifier", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "MainActor" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + null, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "kind": "FunctionType", + "layout": [ + { + "tokenKind": { + "kind": "l_paren" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "TupleTypeElementList", + "layout": [], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_paren" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + null, + null, + { + "tokenKind": { + "kind": "arrow" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "kind": "SimpleTypeIdentifier", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "Void" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_paren" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + null, + { + "kind": "CodeBlock", + "layout": [ + { + "tokenKind": { + "kind": "l_brace" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "CodeBlockItemList", + "layout": [], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_brace" + }, + "leadingTrivia": "\n \n ", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_brace" + }, + "leadingTrivia": "\n", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + null + ], + "presence": "Present" + }, + { + "kind": "CodeBlockItem", + "layout": [ + { + "kind": "StructDecl", + "layout": [ + null, + null, + { + "tokenKind": { + "kind": "kw_struct" + }, + "leadingTrivia": "\n\n", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "tokenKind": { + "kind": "identifier", + "text": "Bar" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + null, + null, + null, + { + "kind": "MemberDeclBlock", + "layout": [ + { + "tokenKind": { + "kind": "l_brace" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "MemberDeclList", + "layout": [ + { + "kind": "MemberDeclListItem", + "layout": [ + { + "kind": "VariableDecl", + "layout": [ + null, + null, + { + "tokenKind": { + "kind": "kw_var" + }, + "leadingTrivia": "\n ", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "kind": "PatternBindingList", + "layout": [ + { + "kind": "PatternBinding", + "layout": [ + { + "kind": "IdentifierPattern", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "body" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "kind": "TypeAnnotation", + "layout": [ + { + "tokenKind": { + "kind": "colon" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "kind": "SimpleTypeIdentifier", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "Foo" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + { + "kind": "CodeBlock", + "layout": [ + { + "tokenKind": { + "kind": "l_brace" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "CodeBlockItemList", + "layout": [ + { + "kind": "CodeBlockItem", + "layout": [ + { + "kind": "FunctionCallExpr", + "layout": [ + { + "kind": "IdentifierExpr", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "Foo" + }, + "leadingTrivia": "\n ", + "trailingTrivia": " ", + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + null, + { + "kind": "TupleExprElementList", + "layout": [], + "presence": "Present" + }, + null, + { + "kind": "ClosureExpr", + "layout": [ + { + "tokenKind": { + "kind": "l_brace" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + { + "kind": "ClosureSignature", + "layout": [ + { + "kind": "AttributeList", + "layout": [ + { + "kind": "CustomAttribute", + "layout": [ + { + "tokenKind": { + "kind": "at_sign" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "SimpleTypeIdentifier", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "MainActor" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + null, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + null, + null, + null, + null, + { + "tokenKind": { + "kind": "kw_in" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "kind": "CodeBlockItemList", + "layout": [ + { + "kind": "CodeBlockItem", + "layout": [ + { + "kind": "FunctionCallExpr", + "layout": [ + { + "kind": "IdentifierExpr", + "layout": [ + { + "tokenKind": { + "kind": "identifier", + "text": "print" + }, + "leadingTrivia": "\n ", + "trailingTrivia": "", + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "l_paren" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "TupleExprElementList", + "layout": [ + { + "kind": "TupleExprElement", + "layout": [ + null, + null, + { + "kind": "StringLiteralExpr", + "layout": [ + null, + { + "tokenKind": { + "kind": "string_quote" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + { + "kind": "StringLiteralSegments", + "layout": [ + { + "kind": "StringSegment", + "layout": [ + { + "tokenKind": { + "kind": "string_segment", + "text": "Hi" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "string_quote" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_paren" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + }, + null, + null + ], + "presence": "Present" + }, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_brace" + }, + "leadingTrivia": "\n ", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + }, + null + ], + "presence": "Present" + }, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_brace" + }, + "leadingTrivia": "\n ", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "r_brace" + }, + "leadingTrivia": "\n", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + null, + null + ], + "presence": "Present" + } + ], + "presence": "Present" + }, + { + "tokenKind": { + "kind": "eof", + "text": "" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "presence": "Present" + } + ], + "presence": "Present" +} diff --git a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index dadf4be47f2a3..16ea17f9feb9e 100644 --- a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -599,8 +599,22 @@ func foo() abc \##(foo)"## foo() -#if true +#if COND1 .bar?()! +#elseif COND2 + #if true + .call() + #elseif true + #if true + .other + #endif + #else + .before() + #if true + .after() + #endif + #endif + .member #else .baz() {} #endif diff --git a/test/Syntax/round_trip_concurrency.swift b/test/Syntax/round_trip_concurrency.swift index 4bdd4b2139a85..30582b715c92c 100644 --- a/test/Syntax/round_trip_concurrency.swift +++ b/test/Syntax/round_trip_concurrency.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %round-trip-syntax-test --swift-syntax-test %swift-syntax-test --file %s -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor Counter { private var value = 0 private let scratchBuffer: UnsafeMutableBufferPointer @@ -24,7 +24,7 @@ actor Counter { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func worker(identity: Int, counters: [Counter], numIterations: Int) async { for i in 0.. %t +// RUN: diff %t %S/Inputs/serialize_distributed_actor.json -u + +distributed actor DA { + distributed func hello(name: String) -> String { + "Hello \(name)!" + } +} \ No newline at end of file diff --git a/test/Syntax/serialize_main_actor.swift b/test/Syntax/serialize_main_actor.swift new file mode 100644 index 0000000000000..0d56b9b7aeda7 --- /dev/null +++ b/test/Syntax/serialize_main_actor.swift @@ -0,0 +1,16 @@ +// RUN: %swift-syntax-test -input-source-filename %s -serialize-raw-tree > %t +// RUN: diff %t %S/Inputs/serialize_main_actor.json -u + +struct Foo { + init(_ foo: @MainActor () -> Void) { + + } +} + +struct Bar { + var body: Foo { + Foo { @MainActor in + print("Hi") + } + } +} \ No newline at end of file diff --git a/test/TBD/async-function-pointer.swift b/test/TBD/async-function-pointer.swift index e780f8f3b8cb7..40b9084554b69 100644 --- a/test/TBD/async-function-pointer.swift +++ b/test/TBD/async-function-pointer.swift @@ -1,11 +1,6 @@ // REQUIRES: VENDOR=apple // REQUIRES: concurrency -// RUN: %target-swift-frontend -emit-ir %s -enable-experimental-concurrency -enable-experimental-async-handler -validate-tbd-against-ir=all -module-name test | %FileCheck %s - -// CHECK: @"$s4test6testityyYaFTu" = hidden global %swift.async_func_pointer - -@asyncHandler -public func testit() { } +// RUN: %target-swift-frontend -emit-ir %s -disable-availability-checking -validate-tbd-against-ir=all -module-name test | %FileCheck %s // CHECK: @barTu = global %swift.async_func_pointer @_silgen_name("bar") diff --git a/test/TBD/async-handler.swift b/test/TBD/async-handler.swift deleted file mode 100644 index aa7602e78a8b3..0000000000000 --- a/test/TBD/async-handler.swift +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-experimental-async-handler -emit-ir -o/dev/null -parse-as-library -module-name test -validate-tbd-against-ir=all %s - -// REQUIRES: VENDOR=apple -// REQUIRES: concurrency - -open class TestClass { - @asyncHandler - internal func publicFunc() { } - - @asyncHandler - internal func internalFunc() { } -} - -@asyncHandler -public func globalFunc() { } diff --git a/test/TBD/linker-directives.swift b/test/TBD/linker-directives.swift index 6a6c5c4eaa23b..7082385c158b0 100644 --- a/test/TBD/linker-directives.swift +++ b/test/TBD/linker-directives.swift @@ -17,9 +17,3 @@ public func toast() {} // CHECK-HAS-NOT-NOT: $ld$hide$os10.15$_$s10ToasterKit5toastyyF // CHECK-HAS-NOT-NOT: $ld$hide$os10.7$_$s10ToasterKit5toastyyF - -// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd -emit-ldadd-cfile-path %t/ldAdd.c -module-name AppKit -// RUN: %FileCheck %s --check-prefix CHECK-C-SYMBOL < %t/ldAdd.c - -// CHECK-C-SYMBOL: $ld$add$os10.8$_$s10ToasterKit5toastyyF -// CHECK-C-SYMBOL: $ld$add$os10.14$_$s10ToasterKit5toastyyF diff --git a/test/TBD/previous-install-name-map.swift b/test/TBD/previous-install-name-map.swift deleted file mode 100644 index 25bfff72ebb11..0000000000000 --- a/test/TBD/previous-install-name-map.swift +++ /dev/null @@ -1,13 +0,0 @@ -// REQUIRES: VENDOR=apple -// REQUIRES: OS=macosx - -// RUN: %empty-directory(%t) - -// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd -previous-module-installname-map-file %S/Inputs/install-name-map.json >& %t/remark.txt -// RUN: %FileCheck %s < %t/remark.txt - -// CHECK: remark: default previous install name for Foo is /System/default -// CHECK: remark: previous install name for Foo in macOS is /System/MacOS -// CHECK: remark: previous install name for Foo in iOS is /System/Others -// CHECK: remark: previous install name for Foo in tvOS is /System/Others -// CHECK: remark: previous install name for Foo in watchOS is /System/Others diff --git a/test/TBD/rdar80485869.swift b/test/TBD/rdar80485869.swift new file mode 100644 index 0000000000000..51b5593fa028a --- /dev/null +++ b/test/TBD/rdar80485869.swift @@ -0,0 +1,7 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -emit-module-path %t/module -emit-tbd -enable-testing -disable-availability-checking + +public actor Tom { + init() async { + } +} diff --git a/test/TBD/rdar82045176.swift b/test/TBD/rdar82045176.swift new file mode 100644 index 0000000000000..e0150054fca48 --- /dev/null +++ b/test/TBD/rdar82045176.swift @@ -0,0 +1,6 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(first)) %s -emit-module -module-name thing -emit-tbd -enable-testing -Xfrontend -disable-availability-checking -Xfrontend -validate-tbd-against-ir=all + +class Test { + init() async { } +} diff --git a/test/TypeDecoder/concurrency.swift b/test/TypeDecoder/concurrency.swift index 60dbc0492e74c..456a338c2d7c7 100644 --- a/test/TypeDecoder/concurrency.swift +++ b/test/TypeDecoder/concurrency.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -emit-executable %s -g -o %t/concurrency -emit-module -Xfrontend -enable-experimental-concurrency +// RUN: %target-build-swift -emit-executable %s -g -o %t/concurrency -emit-module // RUN: sed -ne '/\/\/ *DEMANGLE-TYPE: /s/\/\/ *DEMANGLE-TYPE: *//p' < %s > %t/input // RUN: %lldb-moduleimport-test-with-sdk %t/concurrency -type-from-mangled=%t/input | %FileCheck %s --check-prefix=CHECK-TYPE diff --git a/test/TypeRoundTrip/Inputs/RoundTrip/RoundTrip.cpp b/test/TypeRoundTrip/Inputs/RoundTrip/RoundTrip.cpp new file mode 100644 index 0000000000000..3387d6c49f2ed --- /dev/null +++ b/test/TypeRoundTrip/Inputs/RoundTrip/RoundTrip.cpp @@ -0,0 +1,74 @@ +#include "swift/ABI/Metadata.h" +#include "swift/Demangling/Demangle.h" +#include "swift/Reflection/TypeRefBuilder.h" +#include "swift/Remote/MetadataReader.h" +#include "swift/Remote/InProcessMemoryReader.h" + +#include "Private.h" + +#include + +using namespace swift; + +static std::string nameForMetadata(const Metadata *md) +{ + Demangle::__runtime::StackAllocatedDemangler<1024> dem; + auto nodeTree = _swift_buildDemanglingForMetadata(md, dem); + if (!nodeTree) + return ""; + + std::string result = Demangle::__runtime::nodeToString(nodeTree); + return result; +} + +extern "C" SWIFT_CC(swift) void roundTripType(const Metadata *md) { + // Get a name for it + const std::string mdName = ::nameForMetadata(md); + + // Convert it to a Node tree + Demangle::__runtime::StackAllocatedDemangler<1024> dem; + auto nodeTree = _swift_buildDemanglingForMetadata(md, dem); + + // Mangle that + std::string mangledName = Demangle::__runtime::mangleNode(nodeTree); + + // Look up the result + auto result = swift_getTypeByMangledName(MetadataState::Abstract, + mangledName, + nullptr, + [](unsigned, unsigned){ return nullptr; }, + [](const Metadata *, unsigned) { return nullptr; }); + if (result.isError()) { + auto err = result.getError(); + char *errStr = err->copyErrorString(); + printf("FAIL: %s (%p) -> %s -> ERROR %s\n", + mdName.c_str(), md, mangledName.c_str(), errStr); + err->freeErrorString(errStr); + nodeTree->dump(); + + result = swift_getTypeByMangledNode(MetadataState::Abstract, + dem, + nodeTree, + nullptr, + [](unsigned, unsigned){ return nullptr; }, + [](const Metadata *, unsigned) { return nullptr; }); + if (result.isError()) { + err = result.getError(); + char *errStr = err->copyErrorString(); + printf("=> Also failed on node: %s\n", errStr); + err->freeErrorString(errStr); + } + return; + } + + const Metadata *md2 = result.getType().getMetadata(); + + std::string md2Name = ""; + + if (md2) + md2Name = ::nameForMetadata(md2); + + printf("%s: %s (%p) -> %s -> %s (%p)\n", + md == md2 ? "PASS" : "FAIL", + mdName.c_str(), md, mangledName.c_str(), md2Name.c_str(), md2); +} diff --git a/test/TypeRoundTrip/Inputs/RoundTrip/RoundTrip.swift b/test/TypeRoundTrip/Inputs/RoundTrip/RoundTrip.swift new file mode 100644 index 0000000000000..2f19159539389 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/RoundTrip/RoundTrip.swift @@ -0,0 +1,2 @@ +@_silgen_name("roundTripType") +public func roundTripType(_: Any.Type) diff --git a/test/TypeRoundTrip/Inputs/testcases/builtins.swift b/test/TypeRoundTrip/Inputs/testcases/builtins.swift new file mode 100644 index 0000000000000..94ab27b7628f4 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/builtins.swift @@ -0,0 +1,19 @@ +import RoundTrip + +public func test() { + roundTripType(Int.self) + roundTripType(UInt.self) + + roundTripType(Float.self) + roundTripType(Double.self) + + roundTripType(Int8.self) + roundTripType(Int16.self) + roundTripType(Int32.self) + roundTripType(Int64.self) + + roundTripType(UInt8.self) + roundTripType(UInt16.self) + roundTripType(UInt32.self) + roundTripType(UInt64.self) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/concurrency.swift b/test/TypeRoundTrip/Inputs/testcases/concurrency.swift new file mode 100644 index 0000000000000..af7d2855e71cf --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/concurrency.swift @@ -0,0 +1,6 @@ +import RoundTrip + +public func test() { + roundTripType(Array<(Int) async -> ()>.self) + roundTripType(Array<(Int) async throws -> ()>.self) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/existentials.swift b/test/TypeRoundTrip/Inputs/testcases/existentials.swift new file mode 100644 index 0000000000000..6e8c159dad043 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/existentials.swift @@ -0,0 +1,35 @@ +import RoundTrip + +protocol P {} +protocol Q {} +class C {} +class D : C, P, Q {} + +public func test() { + roundTripType(Any.self) + roundTripType(AnyObject.self) + roundTripType(P.self) + roundTripType((C & P).self) + roundTripType((P & AnyObject).self) + roundTripType((P & Q).self) + roundTripType((C & P & Q).self) + roundTripType((P & Q & AnyObject).self) + + roundTripType(Any.Type.self) + roundTripType(AnyObject.Type.self) + roundTripType(P.Type.self) + roundTripType((C & P).Type.self) + roundTripType((P & AnyObject).Type.self) + roundTripType((P & Q).Type.self) + roundTripType((C & P & Q).Type.self) + roundTripType((P & Q & AnyObject).Type.self) + + roundTripType(Any.Protocol.self) + roundTripType(AnyObject.Protocol.self) + roundTripType(P.Protocol.self) + roundTripType((C & P).Protocol.self) + roundTripType((P & AnyObject).Protocol.self) + roundTripType((P & Q).Protocol.self) + roundTripType((C & P & Q).Protocol.self) + roundTripType((P & Q & AnyObject).Protocol.self) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/extensions.swift b/test/TypeRoundTrip/Inputs/testcases/extensions.swift new file mode 100644 index 0000000000000..1fbca27585f61 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/extensions.swift @@ -0,0 +1,34 @@ +import RoundTrip + +struct Concrete {} + +extension Concrete { + struct Nested {} +} + +struct Generic {} + +protocol Proto {} + +struct Foo : Proto {} + +class Bar {} + +extension Generic where T : Proto { + struct Nested1 {} +} + +extension Generic where T == Int { + struct Nested2 {} +} + +extension Generic where T: AnyObject { + struct NestedViaAnyObject {} +} + +public func test() { + roundTripType(Concrete.Nested.self) + roundTripType(Generic.Nested1.self) + roundTripType(Generic.Nested2.self) + roundTripType(Generic.NestedViaAnyObject.self) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/function_types.swift b/test/TypeRoundTrip/Inputs/testcases/function_types.swift new file mode 100644 index 0000000000000..11b042c4803d0 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/function_types.swift @@ -0,0 +1,14 @@ +import RoundTrip + +class Class {} + +let fn: (Int, Class, __owned Class, Any, inout Int) -> (Int, Class, Any) = { + _, _, _, _, _ in fatalError() +} + +let fn2: () throws -> () = {} + +public func test() { + roundTripType(type(of: fn)) + roundTripType(type(of: fn2)) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/generics.swift b/test/TypeRoundTrip/Inputs/testcases/generics.swift new file mode 100644 index 0000000000000..b8a57119bf0d8 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/generics.swift @@ -0,0 +1,84 @@ +import RoundTrip + +protocol First { + associatedtype Assoc : First + + // Just to confuse things -- a method with the same name as an + // associated type + func Assoc(_: Int) -> Int +} + +protocol Second { + associatedtype Assoc : Second +} + +struct OuterFirst { + struct Inner { + func method(a: A, b: B, c: C, d: D) { + do { + let _: (A, A.Assoc, A.Assoc.Assoc) -> () = { _, _, _ in } + } + do { + let _: (B, B.Assoc, B.Assoc.Assoc) -> () = { _, _, _ in } + } + do { + let _: (C, C.Assoc, C.Assoc.Assoc) -> () = { _, _, _ in } + } + do { + let _: (D, D.Assoc, D.Assoc.Assoc) -> () = { _, _, _ in } + } + } + } +} + +struct OuterBoth { + struct Inner { + func method(a: A, b: B, c: C, d: D) { + do { + let _: (A, A.Assoc, A.Assoc.Assoc) -> () = { _, _, _ in } + } + do { + let _: (B, B.Assoc, B.Assoc.Assoc) -> () = { _, _, _ in } + } + do { + let _: (C, C.Assoc, C.Assoc.Assoc) -> () = { _, _, _ in } + } + do { + let _: (D, D.Assoc, D.Assoc.Assoc) -> () = { _, _, _ in } + } + } + } +} + +struct F1: First { + typealias Assoc = F2 + + func Assoc(_ x: Int) -> Int { return x + 1 } +} + +struct F2: First { + typealias Assoc = F1 + + func Assoc(_ x: Int) -> Int { return x * 2 } +} + +struct FS1: First, Second { + typealias Assoc = FS2 + + func Assoc(_ x: Int) -> Int { return x - 1 } +} + +struct FS2: First, Second { + typealias Assoc = FS1 + + func Assoc(_ x: Int) -> Int { return x / 2 } +} + +public func test() { + roundTripType(OuterFirst.self) + roundTripType(OuterBoth.self) + roundTripType(OuterFirst.Inner.self) + roundTripType(OuterBoth.Inner.self) + roundTripType(type(of:OuterFirst.Inner.method)) + roundTripType(type(of:OuterBoth.Inner.method)) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/nominal_types.swift b/test/TypeRoundTrip/Inputs/testcases/nominal_types.swift new file mode 100644 index 0000000000000..bbcb24809d327 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/nominal_types.swift @@ -0,0 +1,121 @@ +import RoundTrip + +struct Outer { + enum Inner { + case a + init() { fatalError() } + } + enum GenericInner { + case a + init() { fatalError() } + } +} + +enum GenericOuter { + case a + init() { fatalError() } + + struct Inner {} + struct GenericInner {} + struct InnerWhere where T == GenericOuter {} +} + +protocol P {} + +struct ImplementsP: P {} + +struct Constrained {} + +func generic(_: Constrained) {} + + +protocol STSTagProtocol {} +struct STSOuter : STSTagProtocol {} + +enum STSContainer { + class Superclass {} + class Subclass: Superclass where T == STSOuter { + class ExtraNested: Superclass {} + class ExtraNestedWhere: Superclass where U: Subclass {} + } + + class GenericSuperclass {} + class Subclass2: GenericSuperclass where T == STSOuter {} + + class Subclass3: Superclass where T == U.Element {} + + class MoreNesting { + class Subclass: Superclass where T == STSOuter {} + } + + struct Fields where T == STSOuter { + var x: T? + var y: U? + } + + enum Cases where T == STSOuter { + case a(T) + case b(U) + } +} + +// A new type with an easily-recognizable, easily-strippable suffix character. +enum STSContainer℠ { + class Superclass {} + class GenericSuperclass {} +} +extension STSContainer℠ where T == STSOuter { + class Subclass: Superclass { + class ExtraNested: Superclass {} + } + + class Subclass2: GenericSuperclass {} + + class MoreNesting { + class Subclass: Superclass {} + } + + struct Fields { + var x: T? + var y: U? + } + + enum Cases { + case a(T) + case b(U) + } +} + +public func test() { + roundTripType(Outer.self) + roundTripType(Outer.Inner.self) + roundTripType(Outer.GenericInner.self) + roundTripType(Outer.GenericInner.self) + + roundTripType(GenericOuter.Inner.self) + roundTripType(GenericOuter.GenericInner.self) + roundTripType(GenericOuter, Bool>.InnerWhere.self) + + roundTripType(GenericOuter.self) + + roundTripType(Constrained.self) + + roundTripType(STSContainer.Subclass.self) + roundTripType(STSContainer℠.Subclass.self) + + roundTripType(STSContainer.Subclass2.self) + roundTripType(STSContainer℠.Subclass2.self) + + roundTripType(STSContainer.Subclass3>.self) + roundTripType(STSContainer.Subclass.ExtraNested.self) + roundTripType(STSContainer.Subclass.Subclass>.ExtraNestedWhere.self) + roundTripType(STSContainer℠.Subclass.ExtraNested.self) + roundTripType(STSContainer.MoreNesting.Subclass.self) + roundTripType(STSContainer℠.MoreNesting.Subclass.self) + + roundTripType(STSContainer.Fields.self) + roundTripType(STSContainer℠.Fields.self) + + roundTripType(STSContainer.Cases.self) + roundTripType(STSContainer℠.Cases.self) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/objc_classes.swift b/test/TypeRoundTrip/Inputs/testcases/objc_classes.swift new file mode 100644 index 0000000000000..8aaed7bb68f0c --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/objc_classes.swift @@ -0,0 +1,31 @@ +// We need Objective-C support for this test +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + +import Foundation +import RoundTrip + +@objc protocol OurObjCProtocol {} +class OurObjCClass: NSObject, OurObjCProtocol {} + +public func test() { + roundTripType(NSSet.self) + roundTripType(NSFastEnumeration.self) + roundTripType(OurObjCProtocol.self) + roundTripType(NSCache.self) + roundTripType(PropertyListSerialization.WriteOptions.self) + + roundTripType(NSSet.Type.self) + roundTripType(NSFastEnumeration.Type.self) + roundTripType(OurObjCProtocol.Type.self) + roundTripType(NSCache.Type.self) + roundTripType(PropertyListSerialization.WriteOptions.Type.self) + + roundTripType(NSFastEnumeration.Protocol.self) + roundTripType(OurObjCProtocol.Protocol.self) +} + +#else +public func test() { + print("No Objective-C support, so skipping this test") +} +#endif diff --git a/test/TypeRoundTrip/Inputs/testcases/opaque_return_type.swift b/test/TypeRoundTrip/Inputs/testcases/opaque_return_type.swift new file mode 100644 index 0000000000000..af20d42d536f5 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/opaque_return_type.swift @@ -0,0 +1,32 @@ +import RoundTrip + +protocol P {} +extension Int: P {} + +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +func foo() -> some P { return 0 } + +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +var prop: some P { return 0 } + +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +func bar() -> some Sequence { return [] } + +struct G {} + +extension G where T == Int { + @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) + var baz: some P { return 0 } +} + +public func test() { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { + roundTripType(type(of:foo)) + roundTripType(type(of:prop)) + roundTripType(type(of:bar)) + roundTripType(type(of:G().baz)) + roundTripType(type(of:bar()).Element.self) + } else { + print("Skipped as there is no support for `some Foo` syntax") + } +} diff --git a/test/TypeRoundTrip/Inputs/testcases/reference_storage.swift b/test/TypeRoundTrip/Inputs/testcases/reference_storage.swift new file mode 100644 index 0000000000000..022afc4a47fea --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/reference_storage.swift @@ -0,0 +1,15 @@ +import RoundTrip + +class Class {} + +let c = Class() + +weak var weakVar: Class? = c +unowned let unownedVar: Class = c +unowned(unsafe) let unmanagedVar: Class = c + +public func test() { + roundTripType(type(of: weakVar)) + roundTripType(type(of: unownedVar)) + roundTripType(type(of: unmanagedVar)) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/simd.swift b/test/TypeRoundTrip/Inputs/testcases/simd.swift new file mode 100644 index 0000000000000..f8f8dafbc7ab9 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/simd.swift @@ -0,0 +1,99 @@ +import RoundTrip + +public func test() { + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) + + roundTripType(SIMD2.self) + roundTripType(SIMD3.self) + roundTripType(SIMD4.self) + roundTripType(SIMD8.self) + roundTripType(SIMD16.self) + roundTripType(SIMD32.self) + roundTripType(SIMD64.self) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/structural_types.swift b/test/TypeRoundTrip/Inputs/testcases/structural_types.swift new file mode 100644 index 0000000000000..9124ff3c8717a --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/structural_types.swift @@ -0,0 +1,63 @@ +import RoundTrip + +func ownedString(_ s: __owned String) -> () { +} + +func variadic(_ ints: Int...) -> () { +} + +public func test() { + roundTripType((() -> ()).self) + roundTripType(((inout String) -> ()).self) + roundTripType(((Int, Float) -> ()).self) + roundTripType(((inout Int, Float) -> ()).self) + roundTripType(((inout Int, inout Float) -> ()).self) + roundTripType(((Int, inout Float) -> ()).self) + roundTripType(((Int, inout String, Float) -> ()).self) + roundTripType(((inout Int, String, inout Float, Double) -> ()).self) + roundTripType(((String, Int, Double, Float) -> ()).self) + roundTripType(((Int, Float) -> ()).self) + roundTripType(((Int, Float, Int) -> ()).self) + roundTripType((Int.Type, x: Float, Int).self) + roundTripType((x: Int, Float, y: Int.Type).self) + roundTripType(((@escaping () -> ()) -> ()).self) + roundTripType(Array<@convention(c) () -> ()>.self) + + // @convention(block) requires Objective-C support + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + roundTripType(Array<(@escaping @convention(block) () -> (), @convention(block) () -> ()) -> ()>.self) + #endif + + roundTripType(Int.Type.self) + roundTripType(((inout String) -> ()).Type.self) + roundTripType(((Int, Float) -> ()).Type.self) + roundTripType(((inout Int, Float) -> ()).Type.self) + roundTripType(((inout Int, inout Float) -> ()).Type.self) + roundTripType(((Int, inout Float) -> ()).Type.self) + roundTripType(((Int, inout String, Float) -> ()).Type.self) + roundTripType(((inout Int, String, inout Float, Double) -> ()).Type.self) + roundTripType(((String, Int, Double, Float) -> ()).Type.self) + roundTripType(((Int, Float) -> ()).Type.self) + roundTripType(((Int, Float, Int) -> ()).Type.self) + roundTripType((Int.Type, x: Float, Int).Type.self) + roundTripType((x: Int, Float, y: Int.Type).Type.self) + roundTripType(((@escaping () -> ()) -> ()).Type.self) + roundTripType(Array<@convention(c) () -> ()>.Type.self) + + // @convention(block) requires Objective-C support + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + roundTripType(Array<(@escaping @convention(block) () -> (), @convention(block) () -> ()) -> ()>.Type.self) + #endif + + // rdar://81587763: [SR-15025]: Function type syntax doesn't accept variadics + // or __owned + // + //roundTripType(((__owned String) -> ()).self) + //roundTripType(((__owned String) -> ()).Type.self) + roundTripType(type(of: ownedString)) + roundTripType(type(of:type(of: ownedString))) + //roundTripType(((Int...) -> ()).self) + //roundTripType(((Int...) -> ()).Type.self) + roundTripType(type(of: variadic)) + roundTripType(type(of:type(of: variadic))) +} diff --git a/test/TypeRoundTrip/Inputs/testcases/typealiases.swift b/test/TypeRoundTrip/Inputs/testcases/typealiases.swift new file mode 100644 index 0000000000000..479dbb3417fa9 --- /dev/null +++ b/test/TypeRoundTrip/Inputs/testcases/typealiases.swift @@ -0,0 +1,44 @@ +import RoundTrip + +typealias Alias = Int + +struct Outer { + typealias Alias = Int + + struct Inner { + typealias Alias = Int + } +} + +struct GenericOuter { + typealias Alias = Int + + struct Inner { + typealias Alias = Int + } +} + +protocol Proto { + typealias Alias = Int +} + +extension Proto { + typealias OtherAlias = Int +} + +extension GenericOuter where T : Proto { + typealias ConditionalAlias = Int +} + +struct Conforms : Proto {} + +public func test() { + roundTripType(Alias.self) + roundTripType(Outer.Alias.self) + roundTripType(Outer.Inner.Alias.self) + roundTripType(GenericOuter.Alias.self) + roundTripType(GenericOuter.Inner.Alias.self) + roundTripType(Proto.Alias.self) + roundTripType(Proto.OtherAlias.self) + roundTripType(GenericOuter.ConditionalAlias.self) +} diff --git a/test/TypeRoundTrip/round-trip.swift b/test/TypeRoundTrip/round-trip.swift new file mode 100644 index 0000000000000..ecac9c6150bc1 --- /dev/null +++ b/test/TypeRoundTrip/round-trip.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang -std=c++14 %target-rtti-opt %target-pic-opt %target-msvc-runtime-opt -DSWIFT_INLINE_NAMESPACE=__runtime -g -c %S/Inputs/RoundTrip/RoundTrip.cpp -I%swift_obj_root/include -I%swift_src_root/include -I%swift_src_root/stdlib/include -I%swift_src_root/stdlib/public/runtime -I%llvm_src_root/include -I%llvm_obj_root/include -o %t/RoundTrip.o +// RUN: %target-build-swift -g -static -emit-module-path %t/RoundTrip.swiftmodule -emit-module -emit-library -module-name RoundTrip -o %t/%target-static-library-name(RoundTrip) %S/Inputs/RoundTrip/RoundTrip.swift %t/RoundTrip.o +// RUN: echo "// AUTOGENERATED" > %t/all-tests.swift +// RUN: for module in %S/Inputs/testcases/*.swift; do modname=$(basename $module .swift); echo "import $modname" >> %t/all-tests.swift; done +// RUN: echo "func runAllTests() throws {" >> %t/all-tests.swift +// RUN: for module in %S/Inputs/testcases/*.swift; do modname=$(basename $module .swift); %target-build-swift -g -static -emit-module-path %t/$modname.swiftmodule -emit-module -emit-library -module-name $modname -o %t/%target-static-library-name($modname) -I%t -L%t $module -lRoundTrip; echo " print(\"--- $modname\")" >> %t/all-tests.swift; echo " $modname.test()" >> %t/all-tests.swift; echo " print(\"\")" >> %t/all-tests.swift; echo "-l$modname" >> %t/link.txt; done +// RUN: echo "}" >> %t/all-tests.swift +// RUN: %target-build-swift -g -I%t -o %t/round-trip %s %t/all-tests.swift -L%t %target-cxx-lib $(cat %t/link.txt) -lm -lRoundTrip -lswiftReflection +// RUN: %target-codesign %t/round-trip +// RUN: %target-run %t/round-trip | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: shell +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// CHECK-NOT: FAIL + +@main +struct Test { + static func main() throws { + try runAllTests() + } +} diff --git a/test/Unit/lit.cfg b/test/Unit/lit.cfg index 99713d42a7e95..575f4bce0a5a5 100644 --- a/test/Unit/lit.cfg +++ b/test/Unit/lit.cfg @@ -63,7 +63,7 @@ elif swift_test_subset == 'only_stress': # Currently those tests are very fast so it doesn't matter much. pass else: - lit_config.fatal("Unknown test mode %r" % swift_test_subset) + lit_config.fatal("Unknown test subset %r" % swift_test_subset) # test_source_root: The root path where tests are located. # test_exec_root: The root path where tests should be run. @@ -77,3 +77,21 @@ config.test_format = lit.formats.GoogleTest(config.build_mode, 'Tests') # module cache for all those tests that do not set that explicitly if 'XDG_CACHE_HOME' in os.environ: config.environment['XDG_CACHE_HOME'] = os.environ['XDG_CACHE_HOME'] + +# Find the resource directory. Assume it's near the swift compiler if not set. +test_resource_dir = lit_config.params.get('test_resource_dir') +if not test_resource_dir: + test_resource_dir = config.swiftlib_dir + +if 'use_os_stdlib' not in lit_config.params: + # Ensure we load the libraries from the just built compiler + # when building on Darwin + # This is needed in addition to what is done in `add_swift_unittest` + # to handle dependencies of the dylibs loaded by `SwiftRuntimeTests` + # (for which we cannot run `swift-rpathize.py`) + config.environment['DYLD_LIBRARY_PATH'] = os.path.join(test_resource_dir, 'macosx') + +if 'use_os_stdlib' in lit_config.params: + # Runtime tests are failing in back-deployment due to missing _Concurrency. + # rdar://78139218 + config.excludes += ['runtime'] diff --git a/test/Unit/lit.site.cfg.in b/test/Unit/lit.site.cfg.in index fd3031617e37a..ca000f6066171 100644 --- a/test/Unit/lit.site.cfg.in +++ b/test/Unit/lit.site.cfg.in @@ -7,8 +7,10 @@ config.llvm_libs_dir = "@LLVM_LIBS_DIR@" config.build_mode = lit_config.params.get('build_mode', "@SWIFT_BUILD_MODE@") config.swift_obj_root = "@SWIFT_BINARY_DIR@" config.target_triple = "@TARGET_TRIPLE@" +config.swiftlib_dir = "@LIT_SWIFTLIB_DIR@" config.swift_test_results_dir = \ lit_config.params.get("swift_test_results_dir", "@SWIFT_TEST_RESULTS_DIR@") +config.lit_site_config_folder = os.path.dirname(os.path.realpath(__file__)) config.coverage_mode = "@SWIFT_ANALYZE_CODE_COVERAGE@" diff --git a/test/api-digester/Outputs/Cake-abi.txt b/test/api-digester/Outputs/Cake-abi.txt index 551226740f9a0..7010f162d7149 100644 --- a/test/api-digester/Outputs/Cake-abi.txt +++ b/test/api-digester/Outputs/Cake-abi.txt @@ -1,7 +1,17 @@ /* Generic Signature Changes */ +cake: Constructor S1.init(_:) has mangled name changing from 'cake.S1.init(Swift.Int) -> cake.S1' to 'cake.S1.init(Swift.Double) -> cake.S1' +cake: Func C1.foo1() has mangled name changing from 'static cake.C1.foo1() -> ()' to 'cake.C1.foo1() -> ()' +cake: Func C1.foo2(_:) has mangled name changing from 'cake.C1.foo2(Swift.Int) -> ()' to 'cake.C1.foo2(() -> ()) -> ()' cake: Func P1.P1Constraint() has generic signature change from to +cake: Func P1.P1Constraint() has mangled name changing from '(extension in cake):cake.P1< where A: cake.P2>.P1Constraint() -> ()' to '(extension in cake):cake.P1.P1Constraint() -> ()' +cake: Func S1.foo3() has mangled name changing from 'cake.S1.foo3() -> ()' to 'static cake.S1.foo3() -> ()' +cake: Func S1.foo5(x:y:) has mangled name changing from 'cake.S1.foo5(x: Swift.Int, y: Swift.Int) -> ()' to 'cake.S1.foo5(x: Swift.Int, y: Swift.Int, z: Swift.Int) -> ()' +cake: Func Somestruct2.foo1(_:) has mangled name changing from 'static cake.Somestruct2.foo1(cake.C3) -> ()' to 'static cake.NSSomestruct2.foo1(cake.C1) -> ()' +cake: Func ownershipChange(_:_:) has mangled name changing from 'cake.ownershipChange(inout Swift.Int, __shared Swift.Int) -> ()' to 'cake.ownershipChange(Swift.Int, __owned Swift.Int) -> ()' +cake: Func returnFunctionTypeOwnershipChange() has mangled name changing from 'cake.returnFunctionTypeOwnershipChange() -> (cake.C1) -> ()' to 'cake.returnFunctionTypeOwnershipChange() -> (__owned cake.C1) -> ()' cake: Protocol P3 has generic signature change from to +cake: Struct Somestruct2 has mangled name changing from 'cake.Somestruct2' to 'cake.NSSomestruct2' /* RawRepresentable Changes */ @@ -53,15 +63,19 @@ cake: Accessor fixedLayoutStruct2.BecomeFixedBinaryOrder.Modify() is a new API w cake: Accessor fixedLayoutStruct2.BecomeFixedBinaryOrder.Set() is a new API without @available attribute cake: Class C0 is a new API without @available attribute cake: Class C8 is a new API without @available attribute +cake: Constructor AddingNewDesignatedInit.init(_:) is a new API without @available attribute cake: Constructor C1.init(_:) is a new API without @available attribute cake: Constructor ClassWithMissingDesignatedInits.init() is a new API without @available attribute cake: Constructor SubclassWithMissingDesignatedInits.init() is a new API without @available attribute cake: Enum IceKind is now without @frozen cake: EnumElement FrozenKind.AddedCase is a new API without @available attribute +cake: EnumElement FutureKind.FineToAdd is a new API without @available attribute cake: Func C1.foo1() is now not static cake: Func FinalFuncContainer.NewFinalFunc() is now with final cake: Func FinalFuncContainer.NoLongerFinalFunc() is now without final cake: Func Float.floatHigher() is a new API without @available attribute +cake: Func FutureKind.==(_:_:) is a new API without @available attribute +cake: Func FutureKind.hash(into:) is a new API without @available attribute cake: Func HasMutatingMethodClone.foo() has self access kind changing from Mutating to NonMutating cake: Func RequiementChanges.addedFunc() is a new API without @available attribute cake: Func S1.foo1() has self access kind changing from NonMutating to Mutating @@ -72,6 +86,7 @@ cake: Protocol P4 is a new API without @available attribute cake: Struct C6 is now with @frozen cake: Var C1.CIIns1 changes from weak to strong cake: Var C1.CIIns2 changes from strong to weak +cake: Var FutureKind.hashValue is a new API without @available attribute cake: Var RequiementChanges.addedVar is a new API without @available attribute cake: Var fixedLayoutStruct.$__lazy_storage_$_lazy_d is a new API without @available attribute cake: Var fixedLayoutStruct.c is a new API without @available attribute @@ -107,6 +122,5 @@ cake: Var RequiementChanges.addedVar has been added as a protocol requirement /* Class Inheritance Change */ cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 -cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class diff --git a/test/api-digester/Outputs/apinotes-diags.txt b/test/api-digester/Outputs/apinotes-diags.txt index 7fc6938e7ffe3..b9db649487959 100644 --- a/test/api-digester/Outputs/apinotes-diags.txt +++ b/test/api-digester/Outputs/apinotes-diags.txt @@ -15,6 +15,7 @@ APINotesTest(APINotesTest.h): TypeAlias CatAttributeName has been removed APINotesTest(APINotesTest.h): Protocol SwiftTypeWithMethodLeft has been renamed to Protocol SwiftTypeWithMethodRight APINotesTest(APINotesTest.h): Var OldType.oldMember has been renamed to Var NewType.newMember APINotesTest(APINotesTest.h): Var globalAttributeName has been renamed to Var AnimalAttributeName.globalAttributeName +APINotesTest: Import Foundation has been renamed to Import objc_generics /* Type Changes */ APINotesTest(APINotesTest.h): Constructor Cat.init(name:) has return type change from APINotesTest.Cat to APINotesTest.Cat? diff --git a/test/api-digester/Outputs/apinotes-migrator-gen-revert.json b/test/api-digester/Outputs/apinotes-migrator-gen-revert.json index 3709410ff5d11..ff0d898e9df12 100644 --- a/test/api-digester/Outputs/apinotes-migrator-gen-revert.json +++ b/test/api-digester/Outputs/apinotes-migrator-gen-revert.json @@ -1,4 +1,15 @@ [ + { + "DiffItemKind": "CommonDiffItem", + "NodeKind": "Import", + "NodeAnnotation": "Rename", + "ChildIndex": "0", + "LeftUsr": "", + "LeftComment": "objc_generics", + "RightUsr": "", + "RightComment": "Foundation", + "ModuleName": "APINotesTest" + }, { "DiffItemKind": "CommonDiffItem", "NodeKind": "Var", diff --git a/test/api-digester/Outputs/apinotes-migrator-gen.json b/test/api-digester/Outputs/apinotes-migrator-gen.json index b0c6eb4d837e7..1cdde964dfb86 100644 --- a/test/api-digester/Outputs/apinotes-migrator-gen.json +++ b/test/api-digester/Outputs/apinotes-migrator-gen.json @@ -1,4 +1,15 @@ [ + { + "DiffItemKind": "CommonDiffItem", + "NodeKind": "Import", + "NodeAnnotation": "Rename", + "ChildIndex": "0", + "LeftUsr": "", + "LeftComment": "Foundation", + "RightUsr": "", + "RightComment": "objc_generics", + "ModuleName": "APINotesTest" + }, { "DiffItemKind": "CommonDiffItem", "NodeKind": "Var", diff --git a/test/api-digester/Outputs/cake-abi.json b/test/api-digester/Outputs/cake-abi.json index 4383c86fa2239..9c73629cdd6bd 100644 --- a/test/api-digester/Outputs/cake-abi.json +++ b/test/api-digester/Outputs/cake-abi.json @@ -3,6 +3,30 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "SwiftOnoneSupport", + "printedName": "SwiftOnoneSupport", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "_Concurrency", + "printedName": "_Concurrency", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "cake", + "printedName": "cake", + "declKind": "Import", + "moduleName": "cake", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "P1", @@ -1248,10 +1272,6 @@ "moduleName": "cake", "genericSig": "<τ_0_0 where τ_0_0 : cake.PSuper>", "sugared_genericSig": "", - "intro_Macosx": "10.15", - "intro_iOS": "13", - "intro_tvOS": "13", - "intro_watchOS": "6", "funcSelfKind": "NonMutating" } ], diff --git a/test/api-digester/Outputs/cake.json b/test/api-digester/Outputs/cake.json index 05f683713217a..c2195cc677257 100644 --- a/test/api-digester/Outputs/cake.json +++ b/test/api-digester/Outputs/cake.json @@ -3,6 +3,30 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "SwiftOnoneSupport", + "printedName": "SwiftOnoneSupport", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "_Concurrency", + "printedName": "_Concurrency", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "cake", + "printedName": "cake", + "declKind": "Import", + "moduleName": "cake", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "P1", @@ -1135,10 +1159,6 @@ "usr": "s:4cake6PSuperPAAE9futureFooyyF", "moduleName": "cake", "genericSig": "", - "intro_Macosx": "10.15", - "intro_iOS": "13", - "intro_tvOS": "13", - "intro_watchOS": "6", "funcSelfKind": "NonMutating" } ], diff --git a/test/api-digester/Outputs/clang-module-dump.txt b/test/api-digester/Outputs/clang-module-dump.txt index 3f928e1abbc71..2e858d64ec1c3 100644 --- a/test/api-digester/Outputs/clang-module-dump.txt +++ b/test/api-digester/Outputs/clang-module-dump.txt @@ -3,6 +3,16 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "ObjectiveC", + "printedName": "ObjectiveC", + "declKind": "Import", + "moduleName": "Foo", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "AnotherObjcProt", @@ -136,24 +146,6 @@ "name": "NSObjectProtocol", "printedName": "NSObjectProtocol", "usr": "c:objc(pl)NSObject" - }, - { - "kind": "Conformance", - "name": "Equatable", - "printedName": "Equatable", - "usr": "s:SQ" - }, - { - "kind": "Conformance", - "name": "Hashable", - "printedName": "Hashable", - "usr": "s:SH" - }, - { - "kind": "Conformance", - "name": "CVarArg", - "printedName": "CVarArg", - "usr": "s:s7CVarArgP" } ] }, @@ -412,24 +404,6 @@ "name": "NSObjectProtocol", "printedName": "NSObjectProtocol", "usr": "c:objc(pl)NSObject" - }, - { - "kind": "Conformance", - "name": "Equatable", - "printedName": "Equatable", - "usr": "s:SQ" - }, - { - "kind": "Conformance", - "name": "Hashable", - "printedName": "Hashable", - "usr": "s:SH" - }, - { - "kind": "Conformance", - "name": "CVarArg", - "printedName": "CVarArg", - "usr": "s:s7CVarArgP" } ] }, @@ -592,24 +566,6 @@ "name": "NSObjectProtocol", "printedName": "NSObjectProtocol", "usr": "c:objc(pl)NSObject" - }, - { - "kind": "Conformance", - "name": "Equatable", - "printedName": "Equatable", - "usr": "s:SQ" - }, - { - "kind": "Conformance", - "name": "Hashable", - "printedName": "Hashable", - "usr": "s:SH" - }, - { - "kind": "Conformance", - "name": "CVarArg", - "printedName": "CVarArg", - "usr": "s:s7CVarArgP" } ] } diff --git a/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected b/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected index bb3e2ef96d64d..c835c9f94e721 100644 --- a/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected +++ b/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected @@ -1,4 +1,12 @@ +Accessor MutableCollection.subscript(_:).Get() has generic signature change from to > +Accessor MutableCollection.subscript(_:).Set() has generic signature change from to > Protocol CodingKey has added inherited protocol Sendable Protocol CodingKey has generic signature change from to Protocol Error has added inherited protocol Sendable Protocol Error has generic signature change from to +Subscript MutableCollection.subscript(_:) has generic signature change from to > + +// Not actually a source break; the typechecker will find these operations on +// FixedWidthInteger instead. +Func SignedInteger.&+(_:_:) has been removed +Func SignedInteger.&-(_:_:) has been removed diff --git a/test/api-digester/diff-demangled-name.swift b/test/api-digester/diff-demangled-name.swift new file mode 100644 index 0000000000000..3a5d8290508d4 --- /dev/null +++ b/test/api-digester/diff-demangled-name.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: echo "public func foo() {}" > %t/Foo.swift + +// RUN: echo "public protocol P { associatedtype A }" > %t/Foo-1.swift +// RUN: echo "public extension P { public func f() where A == Int {} }" >> %t/Foo-1.swift + +// RUN: echo "public protocol P { associatedtype A }" > %t/Foo-2.swift +// RUN: echo "public extension P where A == Int { public func f() {} }" >> %t/Foo-2.swift + +// RUN: %target-swift-frontend -emit-module %t/Foo-1.swift -module-name Foo -emit-module-interface-path %t/Foo1.swiftinterface +// RUN: %target-swift-frontend -emit-module %t/Foo-2.swift -module-name Foo -emit-module-interface-path %t/Foo2.swiftinterface + +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo1.swiftinterface -o %t/Foo1.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo1.json + +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo2.swiftinterface -o %t/Foo2.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo2.json + +// RUN: %api-digester -diagnose-sdk -print-module --input-paths %t/Foo1.json -input-paths %t/Foo2.json -abi -o %t/result.txt + +// RUN: %FileCheck %s < %t/result.txt + +// CHECK: Foo: Func P.f() has mangled name changing from '(extension in Foo):Foo.P.f< where A.A == Swift.Int>() -> ()' to '(extension in Foo):Foo.P< where A.A == Swift.Int>.f() -> ()' diff --git a/test/api-digester/serialized-diagnostics.swift b/test/api-digester/serialized-diagnostics.swift index bcb867560e465..c0bf6f53e5481 100644 --- a/test/api-digester/serialized-diagnostics.swift +++ b/test/api-digester/serialized-diagnostics.swift @@ -5,6 +5,8 @@ // RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -emit-module -o %t/color.swiftmodule %S/Inputs/cake_baseline/color.swift -parse-as-library -enable-library-evolution %clang-importer-sdk-nosource -module-name color // RUN: not %api-digester -diagnose-sdk -serialize-diagnostics-path %t/result.dia -empty-baseline -I %t %clang-importer-sdk-nosource -module color -abi // RUN: c-index-test -read-diagnostics %t/result.dia 2>&1 | %FileCheck %s -check-prefix CHECK-DIA +// RUN: %api-digester -diagnose-sdk -serialize-diagnostics-path %t/result.dia -empty-baseline -I %t %clang-importer-sdk-nosource -module color -abi -disable-fail-on-error +// RUN: c-index-test -read-diagnostics %t/result.dia 2>&1 | %FileCheck %s -check-prefix CHECK-DIA // Ensure the 'api-digester-breaking-change' category is included in the serialized diagnostics file. // CHECK-DIA: error: ABI breakage: enum Color is a new API without @available attribute [] [api-digester-breaking-change] diff --git a/test/api-digester/stability-stdlib-abi-without-asserts.test b/test/api-digester/stability-stdlib-abi-without-asserts.test index e7c803dea7c0d..fa265a1538466 100644 --- a/test/api-digester/stability-stdlib-abi-without-asserts.test +++ b/test/api-digester/stability-stdlib-abi-without-asserts.test @@ -70,5 +70,13 @@ Protocol CodingKey has added inherited protocol Sendable Protocol CodingKey has generic signature change from to Protocol Error has added inherited protocol Sendable Protocol Error has generic signature change from to +Enum Never has added a conformance to an existing protocol Identifiable + +// These haven't actually been removed; they are simply marked unavailable. +// This seems to be a false positive in the ABI checker. This is not an ABI +// break because the symbols are still present, and is not a source break +// because FixedWidthInteger still has these operations. +Func SignedInteger.&+(_:_:) has been removed +Func SignedInteger.&-(_:_:) has been removed // *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.) diff --git a/test/attr/Inputs/access-note-gen.py b/test/attr/Inputs/access-note-gen.py index cf5a5521e0aa5..776c5f99758ec 100644 --- a/test/attr/Inputs/access-note-gen.py +++ b/test/attr/Inputs/access-note-gen.py @@ -25,8 +25,8 @@ def main(): sys.exit(1) with io.open(sys.argv[1], mode='r', encoding='utf8') as input_file, \ - io.open(sys.argv[2], mode='w', encoding='utf8') as output_file, \ - io.open(sys.argv[3], mode='w', encoding='utf8') as access_notes_file: + io.open(sys.argv[2], mode='w', encoding='utf8') as output_file, \ + io.open(sys.argv[3], mode='w', encoding='utf8') as access_notes_file: # Add header to access notes file access_notes_file.write(u"""\ @@ -78,7 +78,10 @@ def offsetify(*offsets): offset_re_fragment) """Matches expected-error and its offset.""" -expected_error_re = re.compile(r'expected-error' + offset_re_fragment) +expected_error_re = re.compile(r'expected-error' + offset_re_fragment + + r'\s*(\d*\s*)\{\{' + + r'([^}\\]*(?:(?:\}?\\.|\}[^}])[^}\\]*)*)' + + r'\}\}') """Matches the string 'marked @objc'.""" marked_objc_re = re.compile(r'marked @objc') @@ -87,15 +90,20 @@ def offsetify(*offsets): fixit_re = re.compile(r'{{\d+-\d+=[^}]*}}') -def adjust_comments(offset, comment_str): +def adjust_comments(offset, inserted_attr, comment_str): """Replace expected-errors with expected-remarks, and make other adjustments to diagnostics so that they reflect access notes.""" + prefix = u"{{ignored access note: " + suffix = u"; did not implicitly add '" + inserted_attr + "' to this }}" + adjusted = expected_other_diag_re.sub(lambda m: u"expected-" + m.group(1) + offsetify(offset, m.group(2)), comment_str) adjusted = expected_error_re.sub(lambda m: u"expected-remark" + - offsetify(offset, m.group(1)), + offsetify(offset, m.group(1)) + " " + + m.group(2) + prefix + m.group(3) + + suffix, adjusted) adjusted = marked_objc_re.sub(u"marked @objc by an access note", adjusted) adjusted = fixit_re.sub(u"{{none}}", adjusted) @@ -107,10 +115,13 @@ def adjust_comments(offset, comment_str): # Writing attrs to access notes # -def move_at_objc_to_access_note(access_notes_file, arg, offset, access_note_name): +def move_at_objc_to_access_note(access_notes_file, arg, maybe_bad, offset, + access_note_name): """Write an @objc attribute into an access notes file, then return the string that will replace the attribute and trailing comment.""" + is_bad = (maybe_bad == "bad-") + access_notes_file.write(u""" - Name: '{}' ObjC: true""".format(access_note_name)) @@ -122,9 +133,19 @@ def move_at_objc_to_access_note(access_notes_file, arg, offset, access_note_name if offset is None: offset = 1 - return u"// access-note-adjust" + offsetify(offset) + u" [attr moved] " + \ - u"expected-remark{{access note for fancy tests adds attribute 'objc' to " + \ - u"this }} expected-note{{add attribute explicitly to silence this warning}}" + inserted_attr = u"@objc" + if arg: + inserted_attr += u"(" + arg + u")" + + replacement = u"// access-note-adjust" + offsetify(offset) + \ + u"{{" + inserted_attr + "}} [attr moved] " + + if not is_bad: + replacement += u"expected-remark{{implicitly added '" + inserted_attr + \ + u"' to this }} expected-note{{add '" + inserted_attr + \ + u"' explicitly to silence this warning}}" + + return replacement # @@ -132,15 +153,15 @@ def move_at_objc_to_access_note(access_notes_file, arg, offset, access_note_name # """Matches '@objc(foo) // access-note-move{{access-note-name}}' - or '@objc // access-note-move{{access-note-name}}'""" + or '@objc // bad-access-note-move{{access-note-name}}'""" access_note_move_re = re.compile(r'@objc(?:\(([\w:]+)\))?[ \t]*' + - r'//[ \t]*access-note-move' + + r'//[ \t]*(bad-)?access-note-move' + offset_re_fragment + r'\{\{([^}]*)\}\}') -"""Matches // access-note-adjust """ +"""Matches // access-note-adjust{{@objc}} """ adjust_comment_re = re.compile(r'//[ \t]*access-note-adjust' + offset_re_fragment + - r'(.*)') + r'\{\{([^}]*)\}\}[ \t]*(.*)') def replacer(fn, *args): diff --git a/test/attr/actorindependent.swift b/test/attr/actorindependent.swift deleted file mode 100644 index f1a670217e475..0000000000000 --- a/test/attr/actorindependent.swift +++ /dev/null @@ -1,101 +0,0 @@ -// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency - -// REQUIRES: concurrency - -@actorIndependent func globalFunction() { } - -@actorIndependent var globalComputedProperty1: Int { 17 } - -@actorIndependent var globalComputedProperty2: Int { - get { 17 } - set { } -} - -// expected-error@+1{{'@actorIndependent' can not be applied to stored properties}} -@actorIndependent var globalStoredProperty: Int = 17 - -struct X { - @actorIndependent - static var staticProperty1: Int { - return 5 - } - - @actorIndependent - static var staticProperty2: Int { - get { 5 } - set { } - } - - // expected-error@+1{{'@actorIndependent' can not be applied to stored properties}} - @actorIndependent - static var storedStaticProperty: Int = 17 -} - -class C { - @actorIndependent - var property3: Int { 5 } - - @actorIndependent - func f() { } -} - -actor A { - var property: Int = 5 - - // expected-error@+1{{'@actorIndependent' can not be applied to stored properties}} - @actorIndependent - var property2: Int = 5 - - @actorIndependent - var property3: Int { 5 } - - @actorIndependent - var property4: Int { - get { 5 } - set { } - } - - @actorIndependent - static var staticProperty1: Int { - return 5 - } - - @actorIndependent - static var staticProperty2: Int { - get { 5 } - set { } - } - - @actorIndependent init() { } - - @actorIndependent - func synchronousFunc() { } - - @actorIndependent - func asynchronousFunc() async { } - - @actorIndependent - subscript(index: Int) -> String { "\(index)" } - - @actorIndependent static func staticFunc() { } -} - -actor FromProperty { - // expected-note@+2 1{{mutation of this property is only permitted within the actor}} - // expected-note@+1 2{{property declared here}} - var counter : Int = 0 - - // expected-error@+2{{actor-isolated property 'counter' can not be referenced from a non-isolated context}} - @actorIndependent - var halfCounter : Int { counter / 2 } - - @actorIndependent - var ticks : Int { - // expected-error@+1{{actor-isolated property 'counter' can not be referenced from a non-isolated context}} - get { counter } - // expected-error@+1{{actor-isolated property 'counter' can not be mutated from a non-isolated context}} - set { counter = newValue } - } -} - -@actorIndependent extension FromProperty { } diff --git a/test/attr/asynchandler.swift b/test/attr/asynchandler.swift deleted file mode 100644 index 2042ce34e98c6..0000000000000 --- a/test/attr/asynchandler.swift +++ /dev/null @@ -1,75 +0,0 @@ -// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency -enable-experimental-async-handler - -// REQUIRES: concurrency - -func globalAsyncFunction() async -> Int { 0 } - -@asyncHandler func asyncHandler1() { - // okay, it's an async context - let _ = await globalAsyncFunction() -} - -@asyncHandler func asyncHandler2(fn: @autoclosure @escaping () async -> Int ) { - // okay, it's an async context -} - -@asyncHandler func asyncHandler3(fn: @escaping () -> Int) { - // okay, it's an async context -} - -@asyncHandler -func asyncHandlerBad1() -> Int { 0 } -// expected-error@-1{{'@asyncHandler' function can only return 'Void'}} - -@asyncHandler -func asyncHandlerBad2() async { } -// expected-error@-1{{'@asyncHandler' function cannot be 'async' itself}}{{25-31=}} - -@asyncHandler -func asyncHandlerBad3() throws { } -// expected-error@-1{{'@asyncHandler' function cannot throw}}{{25-32=}} - -@asyncHandler -func asyncHandlerBad4(result: inout Int) { } -// expected-error@-1{{'inout' parameter is not allowed in '@asyncHandler' function}} - -@asyncHandler -func asyncHandlerBad5(result: () -> Int) { } -// expected-error@-1{{non-escaping closure parameter is not allowed in '@asyncHandler' function}} - -actor X { - @asyncHandler func asyncHandlerMethod() { } - - @asyncHandler init() { } - // expected-error@-1{{@asyncHandler may only be used on 'func' declarations}} -} - -// Inference of @asyncHandler -protocol P { - @asyncHandler func callback() -} - -extension X: P { - func callback() { - // okay, it's an async context - let _ = await globalAsyncFunction() - } -} - -class Y: P { - // @asyncHandler is not inferred for classes - - func callback() { - // expected-note@-1{{add 'async' to function 'callback()' to make it asynchronous}} {{18-18= async}} - // expected-note@-2{{add '@asyncHandler' to function 'callback()' to create an implicit asynchronous context}} {{3-3=@asyncHandler }} - - // okay, it's an async context - let _ = await globalAsyncFunction() // expected-error{{'async' call in a function that does not support concurrency}} - } -} - -struct Z { - @asyncHandler - mutating func asyncHandlerMethodBad1() { } - // expected-error@-1{{'@asyncHandler' function cannot be 'mutating'}}{{3-12=}} -} diff --git a/test/attr/asynchandler_noconcurrency.swift b/test/attr/asynchandler_noconcurrency.swift deleted file mode 100644 index 94a6fab001d88..0000000000000 --- a/test/attr/asynchandler_noconcurrency.swift +++ /dev/null @@ -1,4 +0,0 @@ -// RUN: %target-swift-frontend -typecheck -verify %s - -@asyncHandler func asyncHandler1() { } -// expected-error@-1{{'asyncHandler' attribute is only valid when experimental concurrency is enabled}} diff --git a/test/attr/attr_availability.swift b/test/attr/attr_availability.swift index 5f84f9512ab81..8579fa8104f00 100644 --- a/test/attr/attr_availability.swift +++ b/test/attr/attr_availability.swift @@ -1128,3 +1128,106 @@ class UnavailableNoArgsSubclassInit: UnavailableNoArgsSuperclassInit { // expected-error@-1 {{'init()' is unavailable}} // expected-note@-2 {{call to unavailable initializer 'init()' from superclass 'UnavailableNoArgsSuperclassInit' occurs implicitly at the end of this initializer}} } + +struct TypeWithTrailingClosures { + func twoTrailingClosures(a: () -> Void, b: () -> Void) {} + func threeTrailingClosures(a: () -> Void, b: () -> Void, c: () -> Void) {} + func threeUnlabeledTrailingClosures(_ a: () -> Void, _ b: () -> Void, _ c: () -> Void) {} + func variadicTrailingClosures(a: (() -> Void)..., b: Int = 0, c: Int = 0) {} +} + +@available(*, deprecated, renamed: "TypeWithTrailingClosures.twoTrailingClosures(self:a:b:)") +func twoTrailingClosures(_ x: TypeWithTrailingClosures, a: () -> Void, b: () -> Void) {} + +@available(*, deprecated, renamed: "TypeWithTrailingClosures.twoTrailingClosures(self:a:b:)") +func twoTrailingClosuresWithDefaults(x: TypeWithTrailingClosures, y: Int = 0, z: Int = 0, a: () -> Void, b: () -> Void) {} + +@available(*, deprecated, renamed: "TypeWithTrailingClosures.threeTrailingClosures(self:a:b:c:)") +func threeTrailingClosures(_ x: TypeWithTrailingClosures, a: () -> Void, b: () -> Void, c: () -> Void) {} + +@available(*, deprecated, renamed: "TypeWithTrailingClosures.threeTrailingClosures(self:a:b:c:)") +func threeTrailingClosuresDiffLabels(_: TypeWithTrailingClosures, x: () -> Void, y: () -> Void, z: () -> Void) {} + +@available(*, deprecated, renamed: "TypeWithTrailingClosures.threeUnlabeledTrailingClosures(self:_:_:_:)") +func threeTrailingClosuresRemoveLabels(_ x: TypeWithTrailingClosures, a: () -> Void, b: () -> Void, c: () -> Void) {} + +@available(*, deprecated, renamed: "TypeWithTrailingClosures.variadicTrailingClosures(self:a:b:c:)") +func variadicTrailingClosures(_ x: TypeWithTrailingClosures, a: (() -> Void)...) {} + +func testMultipleTrailingClosures(_ x: TypeWithTrailingClosures) { + twoTrailingClosures(x) {} b: {} // expected-warning {{'twoTrailingClosures(_:a:b:)' is deprecated: replaced by instance method 'TypeWithTrailingClosures.twoTrailingClosures(a:b:)'}} + // expected-note@-1 {{use 'TypeWithTrailingClosures.twoTrailingClosures(a:b:)' instead}} {{3-22=x.twoTrailingClosures}} {{23-24=}} {{none}} + x.twoTrailingClosures() {} b: {} + + twoTrailingClosuresWithDefaults(x: x) {} b: {} // expected-warning {{'twoTrailingClosuresWithDefaults(x:y:z:a:b:)' is deprecated: replaced by instance method 'TypeWithTrailingClosures.twoTrailingClosures(a:b:)'}} + // expected-note@-1 {{use 'TypeWithTrailingClosures.twoTrailingClosures(a:b:)' instead}} {{3-34=x.twoTrailingClosures}} {{35-39=}} {{none}} + x.twoTrailingClosures() {} b: {} + + threeTrailingClosures(x, a: {}) {} c: {} // expected-warning {{'threeTrailingClosures(_:a:b:c:)' is deprecated: replaced by instance method 'TypeWithTrailingClosures.threeTrailingClosures(a:b:c:)'}} + // expected-note@-1 {{use 'TypeWithTrailingClosures.threeTrailingClosures(a:b:c:)' instead}} {{3-24=x.threeTrailingClosures}} {{25-28=}} {{none}} + x.threeTrailingClosures(a: {}) {} c: {} + + threeTrailingClosuresDiffLabels(x, x: {}) {} z: {} // expected-warning {{'threeTrailingClosuresDiffLabels(_:x:y:z:)' is deprecated: replaced by instance method 'TypeWithTrailingClosures.threeTrailingClosures(a:b:c:)'}} + // expected-note@-1 {{use 'TypeWithTrailingClosures.threeTrailingClosures(a:b:c:)' instead}} {{3-34=x.threeTrailingClosures}} {{35-38=}} {{38-39=a}} {{48-49=c}} {{none}} + x.threeTrailingClosures(a: {}) {} c: {} + + threeTrailingClosuresRemoveLabels(x, a: {}) {} c: {} // expected-warning {{'threeTrailingClosuresRemoveLabels(_:a:b:c:)' is deprecated: replaced by instance method 'TypeWithTrailingClosures.threeUnlabeledTrailingClosures(_:_:_:)'}} + // expected-note@-1 {{use 'TypeWithTrailingClosures.threeUnlabeledTrailingClosures(_:_:_:)' instead}} {{3-36=x.threeUnlabeledTrailingClosures}} {{37-40=}} {{40-43=}} {{50-51=_}} {{none}} + x.threeUnlabeledTrailingClosures({}) {} _: {} + + variadicTrailingClosures(x) {} _: {} _: {} // expected-warning {{'variadicTrailingClosures(_:a:)' is deprecated: replaced by instance method 'TypeWithTrailingClosures.variadicTrailingClosures(a:b:c:)'}} + // expected-note@-1 {{use 'TypeWithTrailingClosures.variadicTrailingClosures(a:b:c:)' instead}} {{3-27=x.variadicTrailingClosures}} {{28-29=}} {{none}} + x.variadicTrailingClosures() {} _: {} _: {} +} + +struct UnavailableSubscripts { + @available(*, unavailable, renamed: "subscript(new:)") + subscript(old index: Int) -> Int { 3 } // expected-note * {{'subscript(old:)' has been explicitly marked unavailable here}} + @available(*, unavailable, renamed: "subscript(new:)") + func getValue(old: Int) -> Int { 3 } // expected-note * {{'getValue(old:)' has been explicitly marked unavailable here}} + + subscript(new index: Int) -> Int { 3 } + + @available(*, unavailable, renamed: "getAValue(new:)") + subscript(getAValue index: Int) -> Int { 3 } // expected-note * {{'subscript(getAValue:)' has been explicitly marked unavailable here}} + func getAValue(new: Int) -> Int { 3 } + + @available(*, unavailable, renamed: "subscript(arg1:arg2:arg3:)") + subscript(_ argg1: Int, _ argg2: Int, _ argg3: Int) -> Int { 3 } // expected-note * {{'subscript(_:_:_:)' has been explicitly marked unavailable here}} + + @available(*, deprecated, renamed: "subscript(arg1:arg2:arg3:)") + subscript(argg1 argg1: Int, argg2 argg2: Int, argg3 argg3: Int) -> Int { 3 } + + @available(*, deprecated, renamed: "subscript(arg1:arg2:arg3:)") + subscript(only1 only1: Int, only2 only2: Int) -> Int { 3 } + + subscript(arg1 arg1: Int, arg2 arg2: Int, arg3 arg3: Int) -> Int { 3 } + + @available(*, deprecated, renamed: "subscriptTo(_:)") + subscript(to to: Int) -> Int { 3 } + func subscriptTo(_ index: Int) -> Int { 3 } + + func testUnavailableSubscripts(_ x: UnavailableSubscripts) { + _ = self[old: 3] // expected-error {{'subscript(old:)' has been renamed to 'subscript(new:)'}} {{14-17=new}} + _ = x[old: 3] // expected-error {{'subscript(old:)' has been renamed to 'subscript(new:)'}} {{11-14=new}} + + _ = self.getValue(old: 3) // expected-error {{'getValue(old:)' has been renamed to 'subscript(new:)'}} {{14-22=[}} {{29-30=]}} {{23-26=new}} + _ = getValue(old: 3) // expected-error {{'getValue(old:)' has been renamed to 'subscript(new:)'}} {{9-9=self}} {{9-17=[}} {{24-25=]}} {{18-21=new}} + _ = x.getValue(old: 3) // expected-error {{'getValue(old:)' has been renamed to 'subscript(new:)'}} {{11-19=[}} {{26-27=]}} {{20-23=new}} + + _ = self[getAValue: 3] // expected-error {{'subscript(getAValue:)' has been renamed to 'getAValue(new:)'}} {{13-14=.getAValue(}} {{26-27=)}} {{14-23=new}} + _ = x[getAValue: 3] // expected-error {{'subscript(getAValue:)' has been renamed to 'getAValue(new:)'}} {{10-11=.getAValue(}} {{23-24=)}} {{11-20=new}} + + _ = self[argg1: 3, argg2: 3, argg3: 3] // expected-warning {{'subscript(argg1:argg2:argg3:)' is deprecated: renamed to 'subscript(arg1:arg2:arg3:)'}} // expected-note {{use 'subscript(arg1:arg2:arg3:)' instead}} {{14-19=arg1}} {{24-29=arg2}} {{34-39=arg3}} + _ = x[argg1: 3, argg2: 3, argg3: 3] // expected-warning {{'subscript(argg1:argg2:argg3:)' is deprecated: renamed to 'subscript(arg1:arg2:arg3:)'}} // expected-note {{use 'subscript(arg1:arg2:arg3:)' instead}} {{11-16=arg1}} {{21-26=arg2}} {{31-36=arg3}} + + // Different number of parameters emit no fixit + _ = self[only1: 3, only2: 3] // expected-warning {{'subscript(only1:only2:)' is deprecated: renamed to 'subscript(arg1:arg2:arg3:)'}} // expected-note {{use 'subscript(arg1:arg2:arg3:)' instead}} {{none}} + + _ = self[3, 3, 3] // expected-error {{'subscript(_:_:_:)' has been renamed to 'subscript(arg1:arg2:arg3:)'}} {{14-14=arg1: }} {{17-17=arg2: }} {{20-20=arg3: }} + _ = x[3, 3, 3] // expected-error {{'subscript(_:_:_:)' has been renamed to 'subscript(arg1:arg2:arg3:)'}} {{11-11=arg1: }} {{14-14=arg2: }} {{17-17=arg3: }} + + _ = self[to: 3] // expected-warning {{'subscript(to:)' is deprecated: renamed to 'subscriptTo(_:)'}} // expected-note {{use 'subscriptTo(_:)' instead}} {{13-14=.subscriptTo(}} {{19-20=)}} {{14-18=}} + _ = x[to: 3] // expected-warning {{'subscript(to:)' is deprecated: renamed to 'subscriptTo(_:)'}} // expected-note {{use 'subscriptTo(_:)' instead}} {{10-11=.subscriptTo(}} {{16-17=)}} {{11-15=}} + } +} \ No newline at end of file diff --git a/test/attr/attr_availability_async_rename.swift b/test/attr/attr_availability_async_rename.swift new file mode 100644 index 0000000000000..66855960c73af --- /dev/null +++ b/test/attr/attr_availability_async_rename.swift @@ -0,0 +1,378 @@ +// REQUIRES: concurrency +// REQUIRES: objc_interop + +// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-ignore-unknown -I %S/Inputs/custom-modules +// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-ignore-unknown -parse-as-library -I %S/Inputs/custom-modules + +import ObjcAsync + +// The following should match to a corresponding decl + +@available(*, renamed: "asyncFunc(_:)") +func goodFunc1(value: String, completionHandler: @escaping (Int) -> Void) {} +@available(*, renamed: "asyncFunc(_:)") +func goodFunc2(value: String, completionHandler: @escaping (Int) -> Void) {} +// expected-note@+1 4 {{'asyncFunc' declared here}} +func asyncFunc(_ text: String) async -> Int { } + +// Ambiguous but only one is async +@available(*, renamed: "overloaded()") +func asyncOnlyOverload(completionHandler: @escaping () -> Void) { } +func overloaded() { } +// expected-note@+1 {{'overloaded()' declared here}} +func overloaded() async { } + +// Renamed decl is ambiguous but the params only match a single case +@available(*, renamed: "overloadedAsyncFunc(value:)") +func nonAmbiguousFunc(value: Int, handler: @escaping () -> Void) {} +// expected-note@+1 {{'overloadedAsyncFunc(value:)' declared here}} +func overloadedAsyncFunc(value: Int) async {} +func overloadedAsyncFunc(value: String) async {} + +// More parameters in async but they have defaults and different labels +@available(*, renamed: "defaultedParamsStart(newArg:arg:)") +func defaultedParamsStart(arg: Int, completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'defaultedParamsStart(newArg:arg:)' declared here}} +func defaultedParamsStart(newArg: String = "", arg: Int) async { } + +@available(*, renamed: "defaultedParamsStart2(newArg:arg:)") +func defaultedParamsStart2(arg: Int, completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'defaultedParamsStart2(newArg:arg:)' declared here}} +func defaultedParamsStart2(newArg: Int = 0, arg: Int) async { } + +@available(*, renamed: "defaultedParamsMiddle(arg1:newArg:arg2:)") +func defaultedParamsMiddle(arg1: Int, arg2: Int, completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'defaultedParamsMiddle(arg1:newArg:arg2:)' declared here}} +func defaultedParamsMiddle(arg1: Int, newArg: String = "", arg2: Int) async { } + +@available(*, renamed: "defaultedParamsMiddle2(arg1:newArg:arg2:)") +func defaultedParamsMiddle2(arg1: Int, arg2: Int, completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'defaultedParamsMiddle2(arg1:newArg:arg2:)' declared here}} +func defaultedParamsMiddle2(arg1: Int, newArg: Int = 0, arg2: Int) async { } + +@available(*, renamed: "defaultedParamsEnd(arg:newArg:)") +func defaultedParamsEnd(arg: Int, completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'defaultedParamsEnd(arg:newArg:)' declared here}} +func defaultedParamsEnd(arg: Int, newArg: String = "") async { } + +@available(*, renamed: "defaultedParamsEnd2(arg:newArg:)") +func defaultedParamsEnd2(arg: Int, completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'defaultedParamsEnd2(arg:newArg:)' declared here}} +func defaultedParamsEnd2(arg: Int, newArg: Int = 0) async { } + +@available(*, renamed: "defaultedParamsEnd3(newArg:arg:)") +func defaultedParamsEnd3(arg: Int, completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'defaultedParamsEnd3(newArg:arg:)' declared here}} +func defaultedParamsEnd3(newArg: Int, arg: String = "") async { } + +@available(*, renamed: "defaultedParamsEnd4(newArg:arg:)") +func defaultedParamsEnd4(arg: Int, completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'defaultedParamsEnd4(newArg:arg:)' declared here}} +func defaultedParamsEnd4(newArg: Int, arg: Int = 0) async { } + +@available(*, deprecated) +@available(macOS, introduced: 12, renamed: "manyAttrsOld()") +@available(*, renamed: "manyAttrsNew()") +@available(*, renamed: "manyAttrsNewOther()") +@available(macOS, deprecated: 12, renamed: "manyAttrsOld()") +@available(*, deprecated) +func manyAttrs(completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'manyAttrsNew()' declared here}} +func manyAttrsNew() async { } + +@available(macOS, introduced: 12, renamed: "platformOnlyNew()") +func platformOnly(completionHandler: @escaping () -> Void) { } +// expected-note@+1 {{'platformOnlyNew()' declared here}} +func platformOnlyNew() async { } + +struct AnotherStruct { + var otherInstanceProp: Int { get async { 1 } } +} + +struct SomeStruct { + @available(*, renamed: "structFunc") + func structFunc(continuation: @escaping () -> Void) { } + + // expected-note@+1{{'structFunc()' declared here}} + func structFunc() async { } + + @available(*, renamed: "staticStructFunc") + static func staticStructFunc(completionHandler: @escaping () -> Void) { } + + // expected-note@+1 2 {{'staticStructFunc()' declared here}} + static func staticStructFunc() async { } + + // expected-note@+1 3 {{'getter:instanceProp()' declared here}} + var instanceProp: Int { get async { 1 } } + var regInstanceProp: Int { get { 1 } set { } } + // expected-note@+1 {{'getter:classProp()' declared here}} + static var classProp: Int { get async { 1 } } + + @available(*, renamed: "getter:instanceProp()") + func instanceGetter(completion: @escaping (Int) -> Void) { } + @available(*, renamed: "getter:classProp()") + static func classGetter(completion: @escaping (Int) -> Void) { } + @available(*, renamed: "getter:instanceProp(a:b:)") + func argsIgnored(completion: @escaping (Int) -> Void) { } + @available(*, renamed: "getter:DoesNotExist.instanceProp()") + func baseIgnored(completion: @escaping (Int) -> Void) { } + + @available(*, renamed: "instanceProp()") + func noPrefix(completion: @escaping (Int) -> Void) { } + @available(*, renamed: "getter:instanceProp()") + func argMismatch(arg: Int, completion: @escaping (Int) -> Void) { } + @available(*, renamed: "setter:regInstanceProp(newValue:)") + func instanceSetter(arg: Int, completion: @escaping (Int) -> Void) { } + + @available(*, renamed: "getter:AnotherStruct.otherInstanceProp()") + func otherInstance(completion: @escaping (Int) -> Void) { } +} + + +// The following should *not* match to a corresponding decl + +// Renamed decl doesn't match any function +@available(*, renamed: "asyncFunc()") +func badFunc(value: String, completionHandler: @escaping (Int) -> Void) {} + +// Not a completion handler +@available(*, renamed: "notCompletionRenamed()") +func notCompletion() {} +func notCompletionRenamed() async {} + +// Corresponding function isn't async +@available(*, renamed: "completionNotAsyncRenamed()") +func completionNotAsync(completionHandler: @escaping () -> Void) {} +func completionNotAsyncRenamed() {} + +// Renamed decl is ambiguous and there's multiple matches +@available(*, renamed: "asyncFuncDifferentParamNames") +func ambiguousFunc(value: Int, handler: @escaping () -> Void) {} +func asyncFuncDifferentParamNames(value: Int) async {} +func asyncFuncDifferentParamNames(value2: Int) async {} + +// Renamed decl doesn't have enough params +@available(*, renamed: "fewerParamsFunc()") +func fewerParamsFunc(value: Int, handler: @escaping () -> Void) {} +func fewerParamsFunc() async {} + +// Renamed decl has more params +@available(*, renamed: "moreParamsFunc()") +func moreParamsFunc(handler: @escaping () -> Void) {} +func moreParamsFunc(value: Int) async {} + +// Renamed decl params types don't match +@available(*, renamed: "noMatchingParamsIntFunc(value:)") +func noMatchingParamsFunc(value: Character, handler: @escaping () -> Void) {} +func noMatchingParamsIntFunc(value: Int) async {} + +// Matching function isn't async +@available(*, renamed: "noMatchingSyncFunc(value:)") +func noMatchingAsyncFunc(value: Int, handler: @escaping () -> Void) {} +func noMatchingSyncFunc(value: Int) {} + +@available(*, renamed: "sameLabelsDifferentOrder(arg2:arg:)") +func sameLabelsDifferentOrder(arg: Int, arg2: String, completionHandler: @escaping () -> Void) { } +func sameLabelsDifferentOrder(arg2: String, arg: Int) async { } + +@available(*, renamed: "handlerNotRemoved(newArg:completionHandler:)") +func handlerNotRemoved(arg: Int, completionHandler: @escaping () -> Void) {} +func handlerNotRemoved(newArg: Int, completionHandler: @escaping () -> Void) async {} + +// Extra arguments. Even though there's defaults, they match the previous +// labels so they shouldn't be skipped. Thus the functions do not match. +@available(*, renamed: "defaultedParamsStartBad(arg:newArg:)") +func defaultedParamsStartBad(arg: Int, completionHandler: @escaping () -> Void) { } +func defaultedParamsStartBad(arg: String = "", newArg: Int) async { } + +@available(*, renamed: "defaultedParamsStartBad2(arg:newArg:)") +func defaultedParamsStartBad2(arg: Int, completionHandler: @escaping () -> Void) { } +func defaultedParamsStartBad2(arg: Int = 0, newArg: Int) async { } + +@available(*, renamed: "defaultedParamsMiddleBad(arg1:arg2:newArg:)") +func defaultedParamsMiddleBad(arg1: Int, arg2: Int, completionHandler: @escaping () -> Void) { } +func defaultedParamsMiddleBad(arg1: Int, arg2: String = "", newArg: Int) async { } + +@available(*, renamed: "defaultedParamsMiddleBad2(arg1:arg2:newArg:)") +func defaultedParamsMiddleBad2(arg1: Int, arg2: Int, completionHandler: @escaping () -> Void) { } +func defaultedParamsMiddleBad2(arg1: Int, arg2: Int = 0, newArg: Int) async { } + + +// Suggest using async alternative function in async context + +func asyncContext(t: HandlerTest) async { + // expected-warning@+1{{consider using asynchronous alternative function}} + goodFunc1(value: "Hello") { _ in } + + let _ = { + // No warning or error since we're in a sync context here + goodFunc1(value: "Hello") { _ in } + } + + let _ = { () async -> () in + let _ = await asyncFunc("Hello World") + // expected-warning@+1{{consider using asynchronous alternative function}} + goodFunc1(value: "Hello") { _ in } + } + + let _ = await asyncFunc("World") + + // expected-warning@+1{{consider using asynchronous alternative function}} + asyncOnlyOverload() { } + // expected-warning@+1{{consider using asynchronous alternative function}} + nonAmbiguousFunc(value: 1) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + defaultedParamsStart(arg: 1) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + defaultedParamsStart2(arg: 1) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + defaultedParamsMiddle(arg1: 1, arg2: 2) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + defaultedParamsMiddle2(arg1: 1, arg2: 2) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + defaultedParamsEnd(arg: 1) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + defaultedParamsEnd2(arg: 1) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + defaultedParamsEnd3(arg: 1) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + defaultedParamsEnd4(arg: 1) { } + // expected-warning@+1{{consider using asynchronous alternative function}} + manyAttrs() { } + // expected-warning@+1{{consider using asynchronous alternative function}} + platformOnly() { } + + // These don't get the warning because we failed to resolve the name to a + // single async decl + badFunc(value: "Hello") { _ in } + notCompletion() + completionNotAsync() { } + ambiguousFunc(value: 1) { } + fewerParamsFunc(value: 1) { } + moreParamsFunc() { } + noMatchingParamsFunc(value: "c") { } + noMatchingAsyncFunc(value: 1) { } + sameLabelsDifferentOrder(arg: 1, arg2: "") { } + handlerNotRemoved(arg: 1) { } + defaultedParamsStartBad(arg: 1) { } + defaultedParamsStartBad2(arg: 1) { } + defaultedParamsMiddleBad(arg1: 1, arg2: 2) { } + defaultedParamsMiddleBad2(arg1: 1, arg2: 2) { } + + // expected-warning@+1{{consider using asynchronous alternative function}} + t.simple { _ in } + _ = await t.simple() + + // expected-warning@+1{{consider using asynchronous alternative function}} + t.simpleArg(1) { _ in } + _ = await t.simpleArg(1) + + // expected-warning@+1{{consider using asynchronous alternative function}} + t.alias { _ in } + _ = await t.alias() + + // expected-warning@+1{{consider using asynchronous alternative function}} + t.error { _, _ in } + _ = try! await t.error() + + // expected-warning@+1{{consider using asynchronous alternative function}} + try! t.removedError { _, _ in } + _ = try! await t.removedError() + + // expected-warning@+1{{consider using asynchronous alternative function}} + t.asyncImportSame(1, completionHandler: { _ in }) + _ = await t.asyncImportSame(1) + + // Marked with swift_async(none), so shouldn't have a warning about using it + t.asyncImportSame(1, replyTo: { _ in }) +} + +func syncContext(t: HandlerTest) { + goodFunc1(value: "Hello") { _ in } + t.simple { _ in } + t.simpleArg(1) { _ in } + t.alias { _ in } + t.error { _, _ in } + try! t.removedError { _, _ in } + t.asyncImportSame(1, completionHandler: { _ in }) + t.asyncImportSame(1, replyTo: { _ in }) +} + +let asyncGlobalClosure = { () async -> () in + // expected-warning@+1{{consider using asynchronous alternative function}} + goodFunc1(value: "neat") { _ in } +} + +class ClassCallingAsyncStuff { + struct NestedStruct { + @available(*, renamed: "structFunc()") + func structCompFunc(handler: @escaping () -> ()) { } + + // expected-note@+1{{'structFunc()' declared here}} + func structFunc() async {} + } + + // expected-note@+1 4 {{'asyncFunc()' declared here}} + func asyncFunc() async {} + + @available(*, renamed: "asyncFunc()") + func compHandlerFunc(handler: @escaping () -> ()) {} + + @available(*, renamed: "asyncFunc()") + func compAsyncHandlerFunc(handler: @escaping () async -> ()) {} + + func async1() async { + // expected-warning@+1{{consider using asynchronous alternative function}} + goodFunc1(value: "hi") { _ in } + + // expected-warning@+1{{consider using asynchronous alternative function}} + compAsyncHandlerFunc() { [self] () async -> () in + // expected-warning@+1{{consider using asynchronous alternative function}} + compAsyncHandlerFunc() { [self] () async -> () in + // expected-warning@+1{{consider using asynchronous alternative function}} + compHandlerFunc() { print("foo") } + } + } + } + + func instanceFunc(other: ClassCallingAsyncStuff) async { + // expected-error@+1{{cannot find 'c' in scope}} + c.compHandlerFunc() { } + + // expected-warning@+1{{consider using asynchronous alternative function}} + other.compHandlerFunc() { } + } + + func structFunc(other: NestedStruct) async { + // expected-warning@+1{{consider using asynchronous alternative function}} + other.structCompFunc() { } + } + + func structFunc(other: SomeStruct) async { + // expected-warning@+1{{consider using asynchronous alternative function}} + other.structFunc() { } + + // expected-warning@+1{{consider using asynchronous alternative function}} + SomeStruct.staticStructFunc { } + + // expected-warning@+1{{consider using asynchronous alternative function}} + type(of: other).staticStructFunc { } + + // expected-warning@+1{{consider using asynchronous alternative function}} + other.instanceGetter { _ in } + // expected-warning@+1{{consider using asynchronous alternative function}} + other.argsIgnored { _ in } + // expected-warning@+1{{consider using asynchronous alternative function}} + other.baseIgnored { _ in } + // expected-warning@+1{{consider using asynchronous alternative function}} + SomeStruct.classGetter { _ in } + + other.noPrefix { _ in } + other.argMismatch(arg: 1) { _ in } + other.instanceSetter(arg: 1) { _ in } + other.otherInstance { _ in } + } + + // no warning + let funFunc = goodFunc1 +} diff --git a/test/attr/attr_availability_swift.swift b/test/attr/attr_availability_swift.swift index 546fef4a5f4f5..877ad7c430924 100644 --- a/test/attr/attr_availability_swift.swift +++ b/test/attr/attr_availability_swift.swift @@ -15,6 +15,12 @@ func baz() { } else { print("no") } + + if #unavailable(swift 4) { // expected-error {{Swift language version checks not allowed in #unavailable}} + print("no") + } else { + print("yes") + } } @available(swift, introduced: 3.0.1, obsoleted: 3.0.2, message: "tiny bug") diff --git a/test/attr/attr_availability_unavailability.swift b/test/attr/attr_availability_unavailability.swift new file mode 100644 index 0000000000000..db9b169a72c20 --- /dev/null +++ b/test/attr/attr_availability_unavailability.swift @@ -0,0 +1,61 @@ +// RUN: %target-typecheck-verify-swift -parse-as-library +// REQUIRES: OS=macosx +@available(macOS 998.0, *) +@discardableResult +func foo() -> Int { return 0 } + +@available(macOS 999.0, *) +@discardableResult +func bar() -> Int { return 0 } + +// Verify that #unavailable is the opposite of #available. +// expected-note@+1 *{{add @available attribute to enclosing global function}} +func testUnavailable() { + if #unavailable(macOS 998.0) { + foo() // expected-error{{'foo()' is only available in macOS 998.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + bar() // expected-error{{'bar()' is only available in macOS 999.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } else { + foo() + bar() // expected-error{{'bar()' is only available in macOS 999.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + if #unavailable(macOS 999.0) { + foo() + bar() // expected-error{{'bar()' is only available in macOS 999.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } else { + foo() + bar() + } + } +} + +// Verify that #unavailable doesn't complain about useless specs. +// expected-note@+1 *{{add @available attribute to enclosing global function}} +func testUnavailableDoesntWarnUselessSpecs() { + if #unavailable(macOS 998.0), #unavailable(macOS 999.0) { + foo() // expected-error{{'foo()' is only available in macOS 998.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + bar() // expected-error{{'bar()' is only available in macOS 999.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } else { + foo() + bar() + } +} + +// Verify that #unavailable refines the availability of all "else" paths. +// expected-note@+1 *{{add @available attribute to enclosing global function}} +func testUnavailableExpandAllElsePaths() { + if #unavailable(macOS 998.0) { + foo() // expected-error{{'foo()' is only available in macOS 998.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } else if 1 == 2 { + foo() + } else if 1 == 3 { + foo() + } else { + foo() + } +} \ No newline at end of file diff --git a/test/attr/attr_cdecl_async.swift b/test/attr/attr_cdecl_async.swift index 77df8548d2a13..fffb285da4203 100644 --- a/test/attr/attr_cdecl_async.swift +++ b/test/attr/attr_cdecl_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-objc-interop -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -enable-objc-interop -disable-availability-checking // REQUIRES: concurrency diff --git a/test/attr/attr_completionhandlerasync.swift b/test/attr/attr_completionhandlerasync.swift deleted file mode 100644 index 2106ebdbfb2c5..0000000000000 --- a/test/attr/attr_completionhandlerasync.swift +++ /dev/null @@ -1,270 +0,0 @@ -// REQUIRES: concurrency -// REQUIRES: objc_interop - -// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -enable-experimental-concurrency -I %S/Inputs/custom-modules -// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -enable-experimental-concurrency -parse-as-library -I %S/Inputs/custom-modules - -import ObjcAsync - -// =================== -// Parsing -// =================== - -// expected-note@+1 4 {{'asyncFunc' declared here}} -func asyncFunc(_ text: String) async -> Int { } - -@completionHandlerAsync("asyncFunc(_:)", completionHandlerIndex: 1) -func goodFunc1(value: String, completionHandler: @escaping (Int) -> Void) {} - -@completionHandlerAsync("asyncFunc(_:)") -func goodFunc2(value: String, completionHandler: @escaping (Int) -> Void) {} - -// expected-error@+1{{no corresponding async function named 'asyncFunc()'}} -@completionHandlerAsync("asyncFunc()") -func badFunc(value: String, completionHandler: @escaping (Int) -> Void) {} - -// expected-error@+1:24{{expected '(' in 'completionHandlerAsync' attribute}} -@completionHandlerAsync -func func1() {} - -// expected-error@+1:25{{argument of 'completionHandlerAsync' attribute must be an identifier or full function name}} -@completionHandlerAsync("not+identifier") -func func2() {} - -// expected-error@+1:25{{argument of 'completionHandlerAsync' attribute must be an identifier or full function name}} -@completionHandlerAsync("$dollarname") -func func3() {} - -// expected-error@+1:25{{argument of 'completionHandlerAsync' attribute must be an identifier or full function name}} -@completionHandlerAsync("TypePrefix.func") -func func4() {} - -// expected-error@+1{{argument of 'completionHandlerAsync' cannot be an interpolated string literal}} -@completionHandlerAsync("interpreted \()") -func func5() {} - -@completionHandlerAsync("foo" // expected-error{{expected ')' in 'completionHandlerAsync' attribute}} -func func6() {} - -@completionHandlerAsync("foo", completionHandlerIndex: 2 // expected-error@:57{{expected ')' in 'completionHandlerAsync'}} -func func7() {} - -// expected-error@+1{{'@completionHandlerAsync' attribute cannot be applied to this declaration}} -@completionHandlerAsync("foo", completionHandlerIndex: 0) -protocol SomeProto { - // expected-error@+1:27{{no corresponding async function named 'protoFunc'}} - @completionHandlerAsync("protoFunc", completionHandlerIndex: 0) - func protoFunc(continuation: @escaping () -> Void) -} - -// expected-error@+1{{'@completionHandlerAsync' attribute cannot be applied to this declaration}} -@completionHandlerAsync("foo", completionHandlerIndex: 0) -struct SomeStruct: SomeProto { - // expected-error@+1:27{{no corresponding async function named 'protoFunc'}} - @completionHandlerAsync("protoFunc", completionHandlerIndex: 0) - func protoFunc(continuation: @escaping () -> Void) {} - - // expected-note@+1{{'structFunc()' declared here}} - func structFunc() async { } - - @completionHandlerAsync("structFunc", completionHandlerIndex: 0) - func structFunc(continuation: @escaping () -> Void) { } - - static func staticStructFunc() async { } - - @completionHandlerAsync("staticStructFunc", completionHandlerIndex: 0) - static func staticStructFunc(completionHandler: @escaping () -> Void) { } -} - -// expected-error@+1 {{'@completionHandlerAsync' attribute cannot be applied to this declaration}} -@completionHandlerAsync("foo", completionHandlerIndex: 0) -class SomeClass: SomeProto { - // expected-error@+1:27{{no corresponding async function named 'protoFunc'}} - @completionHandlerAsync("protoFunc", completionHandlerIndex: 0) - func protoFunc(continuation: @escaping () -> Void) { } - - func classFunc() async { } - - @completionHandlerAsync("classFunc", completionHandlerIndex: 0) - func classFunc(completionHandler: @escaping () -> Void) { } -} - -// =================== -// Typechecking -// =================== - -// expected-error@+1:2{{'@completionHandlerAsync' should be attached to a non-async completion-handler function}} -@completionHandlerAsync("asyncFunc") -func typecheckFunc1() async {} // expected-note@:23{{function declared async}} - -// expected-error@+1:2{{'@completionHandlerAsync' should be attached to a non-async completion-handler function}} -@completionHandlerAsync("betterFunc") -func typecheckFunc2() {} - -// expected-error@+2:2{{'@completionHandlerAsync' should be attached to a non-async completion-handler function}} -// expected-note@+2:55{{'String' is not a function type}} -@completionHandlerAsync("foo", completionHandlerIndex: 1) -func typecheckFunc3(value: String, completionHandler: String) {} - -// expected-error@+2:2{{'@completionHandlerAsync' should be attached to a non-async completion-handler function}} -// expected-note@+2:55{{'String' is not a function type}} -@completionHandlerAsync("foo") -func typecheckFunc4(value: String, completionHandler: String) {} - -// expected-error@+2:2{{'@completionHandlerAsync' should be attached to a non-async completion-handler function}} -// expected-note@+2:33{{completion handler must return 'Void'}} -@completionHandlerAsync("betterFunc(param:)") -func typecheckFunc5(value: Int, completionHandler: @escaping (Int) -> Int) {} - -// expected-error@+1:56{{completion handler index out of range of the function parameters}} -@completionHandlerAsync("foo", completionHandlerIndex: 2) -func typecheckFunc6(value: Int) { } - -// expected-error@+3:2{{'@completionHandlerAsync' should be attached to a non-async completion-handler function}} -// expected-note@+3:21{{completion handler must return 'Void'}} -// expected-note@+2:21{{completion handler must be '@escaping'}} -@completionHandlerAsync("foo", completionHandlerIndex: 0) -func typecheckFunc7(handler: () -> Int) {} - -// expected-error@+3:2{{'@completionHandlerAsync' should be attached to a non-async completion-handler function}} -// expected-note@+3:21{{completion handler must be '@escaping'}} -// expected-note@+2:30{{completion handler must not be '@autoclosure'}} -@completionHandlerAsync("foo") -func typecheckFunc8(handler: @autoclosure () -> ()) {} - -// =================== -// Decl assignment -// =================== - -// expected-error@+1:25{{no corresponding async function named 'functionThatDoesntExist'}} -@completionHandlerAsync("functionThatDoesntExist(_:)") -func typecheckFunc8(handler: @escaping () -> Void) {} - -// These two have the same decl name, so they are ambiguous -func matchingAsyncFunc(value: Int) async {} // expected-note{{'matchingAsyncFunc(value:)' declared here}} -func matchingAsyncFunc(value: String) async {} // expected-note{{'matchingAsyncFunc(value:)' declared here}} - -// expected-error@+1:25{{ambiguous '@completionHandlerAsync' async function 'matchingAsyncFunc(value:)'}} -@completionHandlerAsync("matchingAsyncFunc(value:)") -func typecheckFunc9(handler: @escaping () -> Void) {} - -// Suggest using async alternative function in async context - -func asyncContext(t: HandlerTest) async { - // expected-warning@+1:3{{consider using asynchronous alternative function}} - goodFunc1(value: "Hello") { _ in } - - let _ = { - // No warning or error since we're in a sync context here - goodFunc1(value: "Hello") { _ in } - } - - let _ = { () async -> () in - let _ = await asyncFunc("Hello World") - // expected-warning@+1{{consider using asynchronous alternative function}} - goodFunc1(value: "Hello") { _ in } - } - - let _ = await asyncFunc("World") - - // This doesn't get the warning because the completionHandlerAsync failed to - // resolve the decl name - badFunc(value: "Hello") { _ in } - - // expected-warning@+1{{consider using asynchronous alternative function}} - t.simple { _ in } - _ = await t.simple() - - // expected-warning@+1{{consider using asynchronous alternative function}} - t.simpleArg(1) { _ in } - _ = await t.simpleArg(1) - - // expected-warning@+1{{consider using asynchronous alternative function}} - t.alias { _ in } - _ = await t.alias() - - // expected-warning@+1{{consider using asynchronous alternative function}} - t.error { _, _ in } - _ = try! await t.error() - - // expected-warning@+1{{consider using asynchronous alternative function}} - try! t.removedError { _, _ in } - _ = try! await t.removedError() - - // expected-warning@+1{{consider using asynchronous alternative function}} - t.asyncImportSame(1, completionHandler: { _ in }) - _ = await t.asyncImportSame(1) - - // Marked with swift_async(none), so shouldn't have a warning about using it - t.asyncImportSame(1, replyTo: { _ in }) -} - -func syncContext(t: HandlerTest) { - goodFunc1(value: "Hello") { _ in } - t.simple { _ in } - t.simpleArg(1) { _ in } - t.alias { _ in } - t.error { _, _ in } - try! t.removedError { _, _ in } - t.asyncImportSame(1, completionHandler: { _ in }) - t.asyncImportSame(1, replyTo: { _ in }) -} - -let asyncGlobalClosure = { () async -> () in - // expected-warning@+1:3{{consider using asynchronous alternative function}} - goodFunc1(value: "neat") { _ in } -} - -class ClassCallingAsyncStuff { - struct NestedStruct { - @completionHandlerAsync("structFunc()") - func structCompFunc(handler: @escaping () -> ()) { } - - // expected-note@+1{{'structFunc()' declared here}} - func structFunc() async {} - } - - // expected-note@+1 4 {{'asyncFunc()' declared here}} - func asyncFunc() async {} - - @completionHandlerAsync("asyncFunc()") - func compHandlerFunc(handler: @escaping () -> ()) {} - - @completionHandlerAsync("asyncFunc()") - func compAsyncHandlerFunc(handler: @escaping () async -> ()) {} - - func async1() async { - // expected-warning@+1{{consider using asynchronous alternative function}} - goodFunc1(value: "hi") { _ in } - - // expected-warning@+1{{consider using asynchronous alternative function}} - compAsyncHandlerFunc() { [self] () async -> () in - // expected-warning@+1{{consider using asynchronous alternative function}} - compAsyncHandlerFunc() { [self] () async -> () in - // expected-warning@+1{{consider using asynchronous alternative function}} - compHandlerFunc() { print("foo") } - } - } - } - - func instanceFunc(other: ClassCallingAsyncStuff) async { - // expected-error@+1{{cannot find 'c' in scope}} - c.compHandlerFunc() { } - - // expected-warning@+1{{consider using asynchronous alternative function}} - other.compHandlerFunc() { } - } - - func structFunc(other: NestedStruct) async { - // expected-warning@+1{{consider using asynchronous alternative function}} - other.structCompFunc() { } - } - - func structFunc(other: SomeStruct) async { - // expected-warning@+1{{consider using asynchronous alternative function}} - other.structFunc() { } - } - - // no warning - let funFunc = goodFunc1 -} diff --git a/test/attr/attr_completionhandlerasync_con_disabled.swift b/test/attr/attr_completionhandlerasync_con_disabled.swift deleted file mode 100644 index a869b4d8b879f..0000000000000 --- a/test/attr/attr_completionhandlerasync_con_disabled.swift +++ /dev/null @@ -1,5 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -// expected-error@+1{{'completionHandlerAsync' attribute is only valid when experimental concurrency is enabled}} -@completionHandlerAsync("foobar", completionHandlerIndex: 1) -func func2() {} diff --git a/test/attr/attr_escaping.swift b/test/attr/attr_escaping.swift index f9c2e69fa21cb..f4b0588fc41f7 100644 --- a/test/attr/attr_escaping.swift +++ b/test/attr/attr_escaping.swift @@ -146,15 +146,15 @@ func takesNoEscapeFunction(fn: () -> ()) { // expected-note {{parameter 'fn' is class FooClass { - var stored : Optional<(()->Int)->Void> = nil + var stored : Optional<(()->Int)->Void> = nil // expected-note {{add explicit @escaping to function parameter #0}} {{26-26=@escaping }} var computed : (()->Int)->Void { get { return stored! } set(newValue) { stored = newValue } // ok } var computedEscaping : (@escaping ()->Int)->Void { get { return stored! } - set(newValue) { stored = newValue } // expected-error{{assigning non-escaping parameter 'newValue' to an @escaping closure}} - // expected-note@-1 {{parameter 'newValue' is implicitly non-escaping}} + set(newValue) { stored = newValue } // expected-error{{cannot assign value of type '(@escaping () -> Int) -> Void' to type '(() -> Int) -> Void'}} + // expected-note@-1{{parameter #0 expects escaping value of type '() -> Int'}} } } @@ -230,3 +230,40 @@ extension SR_9760 { // SR-9178 func foo(_ x: @escaping T) {} // expected-error 1{{@escaping attribute only applies to function types}} + +// SR-14720 +var global: ((() -> Void) -> Void)? = nil // expected-note {{add explicit @escaping to function parameter #0}} {{15-15=@escaping }} + +class SR14720 { + let ok: (@escaping () -> Void) -> Void // OK + let callback: (() -> Void) -> Void // expected-note {{add explicit @escaping to function parameter #0}} {{18-18=@escaping }} + let callback1: (() -> Void, () -> Void) -> Void // expected-note {{add explicit @escaping to function parameter #1}} {{31-31=@escaping }} + let callbackAuto: (@autoclosure () -> Void) -> Void // expected-note {{add explicit @escaping to function parameter #0}} {{34-34= @escaping}} + let callbackOpt: ((() -> Void) -> Void)? // expected-note{{add explicit @escaping to function parameter #0}} {{22-22=@escaping }} + + init(f: @escaping (@escaping() -> Void) -> Void) { + self.callback = f // expected-error{{cannot assign value of type '(@escaping () -> Void) -> Void' to type '(() -> Void) -> Void'}} + // expected-note@-1{{parameter #0 expects escaping value of type '() -> Void'}} + self.ok = f // Ok + } + + init(af: @escaping (@escaping() -> Void) -> Void) { + self.callbackOpt = af // expected-error{{cannot assign value of type '(@escaping () -> Void) -> Void' to type '(() -> Void) -> Void'}} + // expected-note@-1{{parameter #0 expects escaping value of type '() -> Void'}} + } + + init(ag: @escaping (@escaping() -> Void) -> Void) { + global = ag // expected-error{{cannot assign value of type '(@escaping () -> Void) -> Void' to type '(() -> Void) -> Void'}} + // expected-note@-1{{parameter #0 expects escaping value of type '() -> Void'}} + } + + init(a: @escaping (@escaping () -> Void) -> Void) { + self.callbackAuto = a // expected-error{{cannot assign value of type '(@escaping () -> Void) -> Void' to type '(@autoclosure () -> Void) -> Void'}} + // expected-note@-1{{parameter #0 expects escaping value of type '() -> Void'}} + } + + init(f: @escaping (() -> Void, @escaping() -> Void) -> Void) { + self.callback1 = f // expected-error{{cannot assign value of type '(() -> Void, @escaping () -> Void) -> Void' to type '(() -> Void, () -> Void) -> Void'}} + // expected-note@-1{{parameter #1 expects escaping value of type '() -> Void'}} + } +} diff --git a/test/attr/attr_noescape.swift b/test/attr/attr_noescape.swift index 3084238520a5a..1b32f1df5f4f2 100644 --- a/test/attr/attr_noescape.swift +++ b/test/attr/attr_noescape.swift @@ -91,12 +91,12 @@ class SomeClass { let _: () -> Void = { // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let inner2 = { foo() } // expected-warning {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} let _ = inner2 func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} let _ = multi2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + doesEscape { foo() } // expected-warning {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} @@ -104,12 +104,12 @@ class SomeClass { doesEscape { //expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let inner2 = { foo() } // expected-warning {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} _ = multi2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + doesEscape { foo() } // expected-warning {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index 6fc79b06edcd5..35163728f701d 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -92,15 +92,15 @@ class FáncyName {} @objc(FancyName) extension FáncyName {} -@objc // access-note-move{{subject_globalVar}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} +@objc // bad-access-note-move{{subject_globalVar}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} var subject_globalVar: Int var subject_getterSetter: Int { - @objc // access-note-move{{getter:subject_getterSetter()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{getter:subject_getterSetter()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} get { return 0 } - @objc // access-note-move{{setter:subject_getterSetter()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{setter:subject_getterSetter()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} set { } } @@ -116,8 +116,8 @@ var subject_global_observingAccessorsVar1: Int = 0 { class subject_getterSetter1 { var instanceVar1: Int { - @objc // access-note-move{{getter:subject_getterSetter1.instanceVar1()}} - get { // access-note-adjust expected-error {{'@objc' getter for non-'@objc' property}} + @objc // bad-access-note-move{{getter:subject_getterSetter1.instanceVar1()}} expected-error {{'@objc' getter for non-'@objc' property}} {{5-11=}} + get { return 0 } } @@ -126,18 +126,18 @@ class subject_getterSetter1 { get { return 0 } - @objc // access-note-move{{setter:subject_getterSetter1.instanceVar2()}} - set { // access-note-adjust expected-error {{'@objc' setter for non-'@objc' property}} + @objc // bad-access-note-move{{setter:subject_getterSetter1.instanceVar2()}} expected-error {{'@objc' setter for non-'@objc' property}} {{5-11=}} + set { } } var instanceVar3: Int { - @objc // access-note-move{{getter:subject_getterSetter1.instanceVar3()}} - get { // access-note-adjust expected-error {{'@objc' getter for non-'@objc' property}} + @objc // bad-access-note-move{{getter:subject_getterSetter1.instanceVar3()}} expected-error {{'@objc' getter for non-'@objc' property}} {{5-11=}} + get { return 0 } - @objc // access-note-move{{setter:subject_getterSetter1.instanceVar3()}} - set { // access-note-adjust expected-error {{'@objc' setter for non-'@objc' property}} + @objc // bad-access-note-move{{setter:subject_getterSetter1.instanceVar3()}} expected-error {{'@objc' setter for non-'@objc' property}} {{5-11=}} + set { } } @@ -159,7 +159,7 @@ class subject_staticVar1 { class var staticVar2: Int { return 42 } } -@objc // access-note-move{{subject_freeFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{1-7=}} +@objc // bad-access-note-move{{subject_freeFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{1-7=}} func subject_freeFunc() { @objc // expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} var subject_localVar: Int @@ -170,7 +170,7 @@ func subject_freeFunc() { } } -@objc // access-note-move{{subject_genericFunc(t:)}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{1-7=}} +@objc // bad-access-note-move{{subject_genericFunc(t:)}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{1-7=}} func subject_genericFunc(t: T) { @objc // expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} var subject_localVar: Int @@ -183,27 +183,27 @@ func subject_genericFunc(t: T) { func subject_funcParam(a: @objc Int) { // expected-error {{attribute can only be applied to declarations, not types}} {{1-1=@objc }} {{27-33=}} } -@objc // access-note-move{{subject_struct}} expected-error {{'@objc' attribute cannot be applied to this declaration}} {{1-7=}} +@objc // bad-access-note-move{{subject_struct}} expected-error {{'@objc' attribute cannot be applied to this declaration}} {{1-7=}} struct subject_struct { - @objc // access-note-move{{subject_struct.subject_instanceVar}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{subject_struct.subject_instanceVar}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} var subject_instanceVar: Int - @objc // access-note-move{{subject_struct.init()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{subject_struct.init()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} init() {} - @objc // access-note-move{{subject_struct.subject_instanceFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{subject_struct.subject_instanceFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} func subject_instanceFunc() {} } -@objc // access-note-move{{subject_genericStruct}} expected-error {{'@objc' attribute cannot be applied to this declaration}} {{1-7=}} +@objc // bad-access-note-move{{subject_genericStruct}} expected-error {{'@objc' attribute cannot be applied to this declaration}} {{1-7=}} struct subject_genericStruct { - @objc // access-note-move{{subject_genericStruct.subject_instanceVar}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{subject_genericStruct.subject_instanceVar}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} var subject_instanceVar: Int - @objc // access-note-move{{subject_genericStruct.init()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{subject_genericStruct.init()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} init() {} - @objc // access-note-move{{subject_genericStruct.subject_instanceFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{subject_genericStruct.subject_instanceFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} func subject_instanceFunc() {} } @@ -225,7 +225,7 @@ class subject_class1 { // no-error class subject_class2 : Protocol_Class1, PlainProtocol { // no-error } -@objc // access-note-move{{subject_genericClass}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{1-7=}} +@objc // bad-access-note-move{{subject_genericClass}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{1-7=}} class subject_genericClass { @objc // access-note-move{{subject_genericClass.subject_instanceVar}} var subject_instanceVar: Int // no-error @@ -237,7 +237,7 @@ class subject_genericClass { func subject_instanceFunc() {} // no_error } -@objc // access-note-move{{subject_genericClass2}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc'}} {{1-7=}} +@objc // bad-access-note-move{{subject_genericClass2}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{1-7=}} class subject_genericClass2 : Class_ObjC1 { @objc // access-note-move{{subject_genericClass2.subject_instanceVar}} var subject_instanceVar: Int // no-error @@ -250,50 +250,50 @@ class subject_genericClass2 : Class_ObjC1 { } extension subject_genericClass where T : Hashable { - @objc // access-note-move{{subject_genericClass.prop}} - var prop: Int { return 0 } // access-note-adjust expected-error{{members of constrained extensions cannot be declared @objc}} + @objc // bad-access-note-move{{subject_genericClass.prop}} + var prop: Int { return 0 } // access-note-adjust{{@objc}} expected-error{{members of constrained extensions cannot be declared @objc}} } extension subject_genericClass { - @objc // access-note-move{{subject_genericClass.extProp}} - var extProp: Int { return 0 } // access-note-adjust expected-error{{extensions of generic classes cannot contain '@objc' members}} + @objc // bad-access-note-move{{subject_genericClass.extProp}} + var extProp: Int { return 0 } // access-note-adjust{{@objc}} expected-error{{extensions of generic classes cannot contain '@objc' members}} - @objc // access-note-move{{subject_genericClass.extFoo()}} - func extFoo() {} // access-note-adjust expected-error{{extensions of generic classes cannot contain '@objc' members}} + @objc // bad-access-note-move{{subject_genericClass.extFoo()}} + func extFoo() {} // access-note-adjust{{@objc}} expected-error{{extensions of generic classes cannot contain '@objc' members}} } @objc // access-note-move{{subject_enum}} enum subject_enum: Int { - @objc // access-note-move{{subject_enum.subject_enumElement1}} expected-error {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} {{3-9=}} + @objc // bad-access-note-move{{subject_enum.subject_enumElement1}} expected-error {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} {{3-9=}} case subject_enumElement1 @objc(subject_enumElement2) // access-note-move{{subject_enum.subject_enumElement2}} case subject_enumElement2 - // Fake for access notes: @objc(subject_enumElement3) // access-note-move@+2{{subject_enum.subject_enumElement4}} - @objc(subject_enumElement3) // access-note-move{{subject_enum.subject_enumElement3}} expected-error {{'@objc' enum case declaration defines multiple enum cases with the same Objective-C name}}{{3-31=}} + // Fake for access notes: @objc(subject_enumElement3) // bad-access-note-move@+2{{subject_enum.subject_enumElement4}} + @objc(subject_enumElement3) // bad-access-note-move{{subject_enum.subject_enumElement3}} expected-error {{'@objc' enum case declaration defines multiple enum cases with the same Objective-C name}}{{3-31=}} case subject_enumElement3, subject_enumElement4 // Becuase of the fake access-note-move above, we expect to see extra diagnostics when we run this test with both explicit @objc attributes *and* access notes: - // expected-remark@-2 * {{'@objc' enum case declaration defines multiple enum cases with the same Objective-C name}} expected-remark@-2 * {{access note for fancy tests adds attribute 'objc' to this enum case}} expected-note@-2 * {{add attribute explicitly to silence this warning}} + // expected-remark@-2 * {{'@objc' enum case declaration defines multiple enum cases with the same Objective-C name}} expected-note@-2 *{{attribute 'objc' was added by access note for fancy tests}} - // Fake for access notes: @objc // access-note-move@+2{{subject_enum.subject_enumElement6}} - @objc // access-note-move{{subject_enum.subject_enumElement5}} expected-error {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} {{3-9=}} + // Fake for access notes: @objc // bad-access-note-move@+2{{subject_enum.subject_enumElement6}} + @objc // bad-access-note-move{{subject_enum.subject_enumElement5}} expected-error {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} {{3-9=}} case subject_enumElement5, subject_enumElement6 // Becuase of the fake access-note-move above, we expect to see extra diagnostics when we run this test with both explicit @objc attributes *and* access notes: - // expected-remark@-2 * {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} expected-remark@-2 * {{access note for fancy tests adds attribute 'objc' to this enum case}} expected-note@-2 * {{add attribute explicitly to silence this warning}} + // expected-remark@-2 * {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} expected-note@-2 *{{attribute 'objc' was added by access note for fancy tests}} @nonobjc // expected-error {{'@nonobjc' attribute cannot be applied to this declaration}} case subject_enumElement7 - @objc // access-note-move{{subject_enum.init()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{subject_enum.init()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} init() {} - @objc // access-note-move{{subject_enum.subject_instanceFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} + @objc // bad-access-note-move{{subject_enum.subject_instanceFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} func subject_instanceFunc() {} } enum subject_enum2 { - @objc(subject_enum2Element1) // access-note-move{{subject_enum2.subject_enumElement1}} expected-error{{'@objc' enum case is not allowed outside of an '@objc' enum}}{{3-32=}} + @objc(subject_enum2Element1) // bad-access-note-move{{subject_enum2.subject_enumElement1}} expected-error{{'@objc' enum case is not allowed outside of an '@objc' enum}}{{3-32=}} case subject_enumElement1 } @@ -324,13 +324,13 @@ protocol subject_protocol5 : Protocol_Class1 {} // expected-error {{@objc protoc protocol subject_protocol6 : Protocol_ObjC1 {} protocol subject_containerProtocol1 { - @objc // access-note-move{{subject_containerProtocol1.subject_instanceVar}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{subject_containerProtocol1.subject_instanceVar}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} var subject_instanceVar: Int { get } - @objc // access-note-move{{subject_containerProtocol1.subject_instanceFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{subject_containerProtocol1.subject_instanceFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} func subject_instanceFunc() - @objc // access-note-move{{subject_containerProtocol1.subject_staticFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{subject_containerProtocol1.subject_staticFunc()}} expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} static func subject_staticFunc() } @@ -383,7 +383,7 @@ protocol subject_containerObjCProtocol2 { } protocol nonObjCProtocol { - @objc // access-note-move{{nonObjCProtocol.objcRequirement()}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{nonObjCProtocol.objcRequirement()}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} func objcRequirement() } @@ -404,43 +404,43 @@ class ConcreteContext3 { @objc // access-note-move{{ConcreteContext3.dynamicSelf1_()}} func dynamicSelf1_() -> Self { return self } - @objc // access-note-move{{ConcreteContext3.genericParams()}} - func genericParams() -> [T] { return [] } // access-note-adjust expected-error{{method cannot be marked @objc because it has generic parameters}} + @objc // bad-access-note-move{{ConcreteContext3.genericParams()}} + func genericParams() -> [T] { return [] } // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked @objc because it has generic parameters}} - @objc // access-note-move{{ConcreteContext3.returnObjCProtocolMetatype()}} - func returnObjCProtocolMetatype() -> NSCoding.Protocol { return NSCoding.self } // access-note-adjust expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.returnObjCProtocolMetatype()}} + func returnObjCProtocolMetatype() -> NSCoding.Protocol { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} typealias AnotherNSCoding = NSCoding typealias MetaNSCoding1 = NSCoding.Protocol typealias MetaNSCoding2 = AnotherNSCoding.Protocol - @objc // access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype1()}} - func returnObjCAliasProtocolMetatype1() -> AnotherNSCoding.Protocol { return NSCoding.self } // access-note-adjust expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype1()}} + func returnObjCAliasProtocolMetatype1() -> AnotherNSCoding.Protocol { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} - @objc // access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype2()}} - func returnObjCAliasProtocolMetatype2() -> MetaNSCoding1 { return NSCoding.self } // access-note-adjust expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype2()}} + func returnObjCAliasProtocolMetatype2() -> MetaNSCoding1 { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} - @objc // access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype3()}} - func returnObjCAliasProtocolMetatype3() -> MetaNSCoding2 { return NSCoding.self } // access-note-adjust expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype3()}} + func returnObjCAliasProtocolMetatype3() -> MetaNSCoding2 { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} typealias Composition = NSCopying & NSCoding - @objc // access-note-move{{ConcreteContext3.returnCompositionMetatype1()}} - func returnCompositionMetatype1() -> Composition.Protocol { return Composition.self } // access-note-adjust expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.returnCompositionMetatype1()}} + func returnCompositionMetatype1() -> Composition.Protocol { return Composition.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} - @objc // access-note-move{{ConcreteContext3.returnCompositionMetatype2()}} - func returnCompositionMetatype2() -> (NSCopying & NSCoding).Protocol { return (NSCopying & NSCoding).self } // access-note-adjust expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.returnCompositionMetatype2()}} + func returnCompositionMetatype2() -> (NSCopying & NSCoding).Protocol { return (NSCopying & NSCoding).self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because its result type cannot be represented in Objective-C}} typealias NSCodingExistential = NSCoding.Type - @objc // access-note-move{{ConcreteContext3.inoutFunc(a:)}} - func inoutFunc(a: inout Int) {} // access-note-adjust expected-error{{method cannot be marked @objc because inout parameters cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.inoutFunc(a:)}} + func inoutFunc(a: inout Int) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because inout parameters cannot be represented in Objective-C}} - @objc // access-note-move{{ConcreteContext3.metatypeOfExistentialMetatypePram1(a:)}} - func metatypeOfExistentialMetatypePram1(a: NSCodingExistential.Protocol) {} // access-note-adjust expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.metatypeOfExistentialMetatypePram1(a:)}} + func metatypeOfExistentialMetatypePram1(a: NSCodingExistential.Protocol) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} - @objc // access-note-move{{ConcreteContext3.metatypeOfExistentialMetatypePram2(a:)}} - func metatypeOfExistentialMetatypePram2(a: NSCoding.Type.Protocol) {} // access-note-adjust expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + @objc // bad-access-note-move{{ConcreteContext3.metatypeOfExistentialMetatypePram2(a:)}} + func metatypeOfExistentialMetatypePram2(a: NSCoding.Type.Protocol) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} } func genericContext1(_: T) { @@ -467,10 +467,10 @@ func genericContext1(_: T) { } class GenericContext2 { - @objc // access-note-move{{GenericContext2.subject_inGenericContext}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{3-9=}} + @objc // bad-access-note-move{{GenericContext2.subject_inGenericContext}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{3-9=}} class subject_inGenericContext {} - @objc // access-note-move{{GenericContext2.subject_inGenericContext2}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc'}} {{3-9=}} + @objc // bad-access-note-move{{GenericContext2.subject_inGenericContext2}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{3-9=}} class subject_inGenericContext2 : Class_ObjC1 {} @objc // access-note-move{{GenericContext2.f()}} @@ -479,10 +479,10 @@ class GenericContext2 { class GenericContext3 { class MoreNested { - @objc // access-note-move{{GenericContext3.MoreNested.subject_inGenericContext}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{5-11=}} + @objc // bad-access-note-move{{GenericContext3.MoreNested.subject_inGenericContext}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{5-11=}} class subject_inGenericContext {} - @objc // access-note-move{{GenericContext3.MoreNested.subject_inGenericContext2}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc'}} {{5-11=}} + @objc // bad-access-note-move{{GenericContext3.MoreNested.subject_inGenericContext2}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{5-11=}} class subject_inGenericContext2 : Class_ObjC1 {} @objc // access-note-move{{GenericContext3.MoreNested.f()}} @@ -491,11 +491,11 @@ class GenericContext3 { } class GenericContext4 { - @objc // access-note-move{{GenericContext4.foo()}} - func foo() where T: Hashable { } // access-note-adjust expected-error {{instance method cannot be marked @objc because it has a 'where' clause}} + @objc // bad-access-note-move{{GenericContext4.foo()}} + func foo() where T: Hashable { } // access-note-adjust{{@objc}} expected-error {{instance method cannot be marked @objc because it has a 'where' clause}} } -@objc // access-note-move{{ConcreteSubclassOfGeneric}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{1-7=}} +@objc // bad-access-note-move{{ConcreteSubclassOfGeneric}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{1-7=}} class ConcreteSubclassOfGeneric : GenericContext3 {} extension ConcreteSubclassOfGeneric { @@ -503,7 +503,7 @@ extension ConcreteSubclassOfGeneric { func foo() {} // okay } -@objc // access-note-move{{ConcreteSubclassOfGeneric2}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc'}} {{1-7=}} +@objc // bad-access-note-move{{ConcreteSubclassOfGeneric2}} expected-error{{generic subclasses of '@objc' classes cannot have an explicit '@objc' because they are not directly visible from Objective-C}} {{1-7=}} class ConcreteSubclassOfGeneric2 : subject_genericClass2 {} extension ConcreteSubclassOfGeneric2 { @@ -596,65 +596,65 @@ class subject_subscriptGeneric { } class subject_subscriptInvalid1 { - @objc // access-note-move{{subject_subscriptInvalid1.subscript(_:)}} - class subscript(_ i: Int) -> AnyObject? { // access-note-adjust expected-error {{class subscript cannot be marked @objc}} + @objc // bad-access-note-move{{subject_subscriptInvalid1.subscript(_:)}} + class subscript(_ i: Int) -> AnyObject? { // access-note-adjust{{@objc}} expected-error {{class subscript cannot be marked @objc}} return nil } } class subject_subscriptInvalid2 { - @objc // access-note-move{{subject_subscriptInvalid2.subscript(_:)}} + @objc // bad-access-note-move{{subject_subscriptInvalid2.subscript(_:)}} subscript(a: PlainClass) -> Int { - // access-note-adjust expected-error@-1 {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{classes not annotated with @objc cannot be represented in Objective-C}} get { return 0 } } } class subject_subscriptInvalid3 { - @objc // access-note-move{{subject_subscriptInvalid3.subscript(_:)}} - subscript(a: PlainClass.Type) -> Int { // access-note-adjust expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{subject_subscriptInvalid3.subscript(_:)}} + subscript(a: PlainClass.Type) -> Int { // access-note-adjust{{@objc}} expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} get { return 0 } } } class subject_subscriptInvalid4 { - @objc // access-note-move{{subject_subscriptInvalid4.subscript(_:)}} - subscript(a: PlainStruct) -> Int { // access-note-adjust expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{subject_subscriptInvalid4.subscript(_:)}} + subscript(a: PlainStruct) -> Int { // access-note-adjust{{@objc}} expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-1{{Swift structs cannot be represented in Objective-C}} get { return 0 } } } class subject_subscriptInvalid5 { - @objc // access-note-move{{subject_subscriptInvalid5.subscript(_:)}} - subscript(a: PlainEnum) -> Int { // access-note-adjust expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{subject_subscriptInvalid5.subscript(_:)}} + subscript(a: PlainEnum) -> Int { // access-note-adjust{{@objc}} expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-1{{enums cannot be represented in Objective-C}} get { return 0 } } } class subject_subscriptInvalid6 { - @objc // access-note-move{{subject_subscriptInvalid6.subscript(_:)}} - subscript(a: PlainProtocol) -> Int { // access-note-adjust expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{subject_subscriptInvalid6.subscript(_:)}} + subscript(a: PlainProtocol) -> Int { // access-note-adjust{{@objc}} expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-1{{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} get { return 0 } } } class subject_subscriptInvalid7 { - @objc // access-note-move{{subject_subscriptInvalid7.subscript(_:)}} - subscript(a: Protocol_Class1) -> Int { // access-note-adjust expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{subject_subscriptInvalid7.subscript(_:)}} + subscript(a: Protocol_Class1) -> Int { // access-note-adjust{{@objc}} expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-1{{protocol-constrained type containing protocol 'Protocol_Class1' cannot be represented in Objective-C}} get { return 0 } } } class subject_subscriptInvalid8 { - @objc // access-note-move{{subject_subscriptInvalid8.subscript(_:)}} - subscript(a: Protocol_Class1 & Protocol_Class2) -> Int { // access-note-adjust expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{subject_subscriptInvalid8.subscript(_:)}} + subscript(a: Protocol_Class1 & Protocol_Class2) -> Int { // access-note-adjust{{@objc}} expected-error {{subscript cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-1{{protocol-constrained type containing protocol 'Protocol_Class1' cannot be represented in Objective-C}} get { return 0 } } } class subject_propertyInvalid1 { - @objc // access-note-move{{subject_propertyInvalid1.plainStruct}} - let plainStruct = PlainStruct() // access-note-adjust expected-error {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + @objc // bad-access-note-move{{subject_propertyInvalid1.plainStruct}} + let plainStruct = PlainStruct() // access-note-adjust{{@objc}} expected-error {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-1{{Swift structs cannot be represented in Objective-C}} } @@ -703,63 +703,63 @@ class infer_instanceFunc1 { func func7(a: PlainClass) {} // CHECK-LABEL: {{^}} func func7(a: PlainClass) { - @objc // access-note-move{{infer_instanceFunc1.func7_(a:)}} + @objc // bad-access-note-move{{infer_instanceFunc1.func7_(a:)}} func func7_(a: PlainClass) {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{classes not annotated with @objc cannot be represented in Objective-C}} func func7m(a: PlainClass.Type) {} // CHECK-LABEL: {{^}} func func7m(a: PlainClass.Type) { - @objc // access-note-move{{infer_instanceFunc1.func7m_(a:)}} + @objc // bad-access-note-move{{infer_instanceFunc1.func7m_(a:)}} func func7m_(a: PlainClass.Type) {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} func func8() -> PlainClass {} // CHECK-LABEL: {{^}} func func8() -> PlainClass { - @objc // access-note-move{{infer_instanceFunc1.func8_()}} + @objc // bad-access-note-move{{infer_instanceFunc1.func8_()}} func func8_() -> PlainClass {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} // expected-note@-2 {{classes not annotated with @objc cannot be represented in Objective-C}} func func8m() -> PlainClass.Type {} // CHECK-LABEL: {{^}} func func8m() -> PlainClass.Type { - @objc // access-note-move{{infer_instanceFunc1.func8m_()}} + @objc // bad-access-note-move{{infer_instanceFunc1.func8m_()}} func func8m_() -> PlainClass.Type {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} func func9(a: PlainStruct) {} // CHECK-LABEL: {{^}} func func9(a: PlainStruct) { - @objc // access-note-move{{infer_instanceFunc1.func9_(a:)}} + @objc // bad-access-note-move{{infer_instanceFunc1.func9_(a:)}} func func9_(a: PlainStruct) {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} func func10() -> PlainStruct {} // CHECK-LABEL: {{^}} func func10() -> PlainStruct { - @objc // access-note-move{{infer_instanceFunc1.func10_()}} + @objc // bad-access-note-move{{infer_instanceFunc1.func10_()}} func func10_() -> PlainStruct {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} func func11(a: PlainEnum) {} // CHECK-LABEL: {{^}} func func11(a: PlainEnum) { - @objc // access-note-move{{infer_instanceFunc1.func11_(a:)}} + @objc // bad-access-note-move{{infer_instanceFunc1.func11_(a:)}} func func11_(a: PlainEnum) {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{non-'@objc' enums cannot be represented in Objective-C}} func func12(a: PlainProtocol) {} // CHECK-LABEL: {{^}} func func12(a: PlainProtocol) { - @objc // access-note-move{{infer_instanceFunc1.func12_(a:)}} + @objc // bad-access-note-move{{infer_instanceFunc1.func12_(a:)}} func func12_(a: PlainProtocol) {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} func func13(a: Class_ObjC1) {} @@ -771,9 +771,9 @@ class infer_instanceFunc1 { func func14(a: Protocol_Class1) {} // CHECK-LABEL: {{^}} func func14(a: Protocol_Class1) { - @objc // access-note-move{{infer_instanceFunc1.func14_(a:)}} + @objc // bad-access-note-move{{infer_instanceFunc1.func14_(a:)}} func func14_(a: Protocol_Class1) {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'Protocol_Class1' cannot be represented in Objective-C}} func func15(a: Protocol_ObjC1) {} @@ -863,19 +863,20 @@ class infer_instanceFunc1 { func func_TupleStyle2a(a: Int, b: Int, c: Int) {} // Check that we produce diagnostics for every parameter and return type. - @objc // access-note-move{{infer_instanceFunc1.func_MultipleDiags(a:b:)}} + @objc // bad-access-note-move{{infer_instanceFunc1.func_MultipleDiags(a:b:)}} func func_MultipleDiags(a: PlainStruct, b: PlainEnum) -> Any {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter 1 cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter 1 cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} - // access-note-adjust expected-error@-3 {{method cannot be marked @objc because the type of the parameter 2 cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-3 {{method cannot be marked @objc because the type of the parameter 2 cannot be represented in Objective-C}} // expected-note@-4 {{non-'@objc' enums cannot be represented in Objective-C}} + // Produces an extra: expected-note@-5 * {{attribute 'objc' was added by access note for fancy tests}} @objc // access-note-move{{infer_instanceFunc1.func_UnnamedParam1(_:)}} func func_UnnamedParam1(_: Int) {} // no-error - @objc // access-note-move{{infer_instanceFunc1.func_UnnamedParam2(_:)}} + @objc // bad-access-note-move{{infer_instanceFunc1.func_UnnamedParam2(_:)}} func func_UnnamedParam2(_: PlainStruct) {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} @objc // access-note-move{{infer_instanceFunc1.func_varParam1(a:)}} @@ -939,9 +940,9 @@ class infer_instanceVar1 { // CHECK: @objc var instanceVar2: Int // CHECK: {{^}} var instanceVar3: PlainProtocol - @objc // access-note-move{{infer_instanceVar1.instanceVar1_}} + @objc // bad-access-note-move{{infer_instanceVar1.instanceVar1_}} var (instanceVar1_, instanceVar2_): (Int, PlainProtocol) - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} // Fake for access notes: @objc // access-note-move@-3{{infer_instanceVar1.instanceVar2_}} @@ -1015,17 +1016,17 @@ class infer_instanceVar1 { var var_tuple1: () // CHECK-LABEL: {{^}} @_hasInitialValue var var_tuple1: () - @objc // access-note-move{{infer_instanceVar1.var_tuple1_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_tuple1_}} var var_tuple1_: () - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{empty tuple type cannot be represented in Objective-C}} var var_tuple2: Void // CHECK-LABEL: {{^}} @_hasInitialValue var var_tuple2: Void - @objc // access-note-move{{infer_instanceVar1.var_tuple2_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_tuple2_}} var var_tuple2_: Void - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{empty tuple type cannot be represented in Objective-C}} var var_tuple3: (Int) @@ -1037,9 +1038,9 @@ class infer_instanceVar1 { var var_tuple4: (Int, Int) // CHECK-LABEL: {{^}} var var_tuple4: (Int, Int) - @objc // access-note-move{{infer_instanceVar1.var_tuple4_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_tuple4_}} var var_tuple4_: (Int, Int) - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{tuples cannot be represented in Objective-C}} //===--- Stdlib integer types. @@ -1068,33 +1069,33 @@ class infer_instanceVar1 { var var_PlainClass: PlainClass // CHECK-LABEL: {{^}} var var_PlainClass: PlainClass - @objc // access-note-move{{infer_instanceVar1.var_PlainClass_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_PlainClass_}} var var_PlainClass_: PlainClass - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{classes not annotated with @objc cannot be represented in Objective-C}} var var_PlainStruct: PlainStruct // CHECK-LABEL: {{^}} var var_PlainStruct: PlainStruct - @objc // access-note-move{{infer_instanceVar1.var_PlainStruct_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_PlainStruct_}} var var_PlainStruct_: PlainStruct - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} var var_PlainEnum: PlainEnum // CHECK-LABEL: {{^}} var var_PlainEnum: PlainEnum - @objc // access-note-move{{infer_instanceVar1.var_PlainEnum_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_PlainEnum_}} var var_PlainEnum_: PlainEnum - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{non-'@objc' enums cannot be represented in Objective-C}} var var_PlainProtocol: PlainProtocol // CHECK-LABEL: {{^}} var var_PlainProtocol: PlainProtocol - @objc // access-note-move{{infer_instanceVar1.var_PlainProtocol_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_PlainProtocol_}} var var_PlainProtocol_: PlainProtocol - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} var var_ClassObjC: Class_ObjC1 @@ -1106,9 +1107,9 @@ class infer_instanceVar1 { var var_ProtocolClass: Protocol_Class1 // CHECK-LABEL: {{^}} var var_ProtocolClass: Protocol_Class1 - @objc // access-note-move{{infer_instanceVar1.var_ProtocolClass_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ProtocolClass_}} var var_ProtocolClass_: Protocol_Class1 - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'Protocol_Class1' cannot be represented in Objective-C}} var var_ProtocolObjC: Protocol_ObjC1 @@ -1121,30 +1122,30 @@ class infer_instanceVar1 { var var_PlainClassMetatype: PlainClass.Type // CHECK-LABEL: {{^}} var var_PlainClassMetatype: PlainClass.Type - @objc // access-note-move{{infer_instanceVar1.var_PlainClassMetatype_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_PlainClassMetatype_}} var var_PlainClassMetatype_: PlainClass.Type - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_PlainStructMetatype: PlainStruct.Type // CHECK-LABEL: {{^}} var var_PlainStructMetatype: PlainStruct.Type - @objc // access-note-move{{infer_instanceVar1.var_PlainStructMetatype_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_PlainStructMetatype_}} var var_PlainStructMetatype_: PlainStruct.Type - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_PlainEnumMetatype: PlainEnum.Type // CHECK-LABEL: {{^}} var var_PlainEnumMetatype: PlainEnum.Type - @objc // access-note-move{{infer_instanceVar1.var_PlainEnumMetatype_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_PlainEnumMetatype_}} var var_PlainEnumMetatype_: PlainEnum.Type - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_PlainExistentialMetatype: PlainProtocol.Type // CHECK-LABEL: {{^}} var var_PlainExistentialMetatype: PlainProtocol.Type - @objc // access-note-move{{infer_instanceVar1.var_PlainExistentialMetatype_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_PlainExistentialMetatype_}} var var_PlainExistentialMetatype_: PlainProtocol.Type - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ClassObjCMetatype: Class_ObjC1.Type // CHECK-LABEL: @objc var var_ClassObjCMetatype: Class_ObjC1.Type @@ -1155,9 +1156,9 @@ class infer_instanceVar1 { var var_ProtocolClassMetatype: Protocol_Class1.Type // CHECK-LABEL: {{^}} var var_ProtocolClassMetatype: Protocol_Class1.Type - @objc // access-note-move{{infer_instanceVar1.var_ProtocolClassMetatype_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ProtocolClassMetatype_}} var var_ProtocolClassMetatype_: Protocol_Class1.Type - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ProtocolObjCMetatype1: Protocol_ObjC1.Type // CHECK-LABEL: @objc var var_ProtocolObjCMetatype1: Protocol_ObjC1.Type @@ -1185,57 +1186,57 @@ class infer_instanceVar1 { var var_Existential1: PlainProtocol // CHECK-LABEL: {{^}} var var_Existential1: PlainProtocol - @objc // access-note-move{{infer_instanceVar1.var_Existential1_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_Existential1_}} var var_Existential1_: PlainProtocol - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} var var_Existential2: PlainProtocol & PlainProtocol // CHECK-LABEL: {{^}} var var_Existential2: PlainProtocol - @objc // access-note-move{{infer_instanceVar1.var_Existential2_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_Existential2_}} var var_Existential2_: PlainProtocol & PlainProtocol - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} var var_Existential3: PlainProtocol & Protocol_Class1 // CHECK-LABEL: {{^}} var var_Existential3: PlainProtocol & Protocol_Class1 - @objc // access-note-move{{infer_instanceVar1.var_Existential3_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_Existential3_}} var var_Existential3_: PlainProtocol & Protocol_Class1 - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} var var_Existential4: PlainProtocol & Protocol_ObjC1 // CHECK-LABEL: {{^}} var var_Existential4: PlainProtocol & Protocol_ObjC1 - @objc // access-note-move{{infer_instanceVar1.var_Existential4_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_Existential4_}} var var_Existential4_: PlainProtocol & Protocol_ObjC1 - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} var var_Existential5: Protocol_Class1 // CHECK-LABEL: {{^}} var var_Existential5: Protocol_Class1 - @objc // access-note-move{{infer_instanceVar1.var_Existential5_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_Existential5_}} var var_Existential5_: Protocol_Class1 - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'Protocol_Class1' cannot be represented in Objective-C}} var var_Existential6: Protocol_Class1 & Protocol_Class2 // CHECK-LABEL: {{^}} var var_Existential6: Protocol_Class1 & Protocol_Class2 - @objc // access-note-move{{infer_instanceVar1.var_Existential6_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_Existential6_}} var var_Existential6_: Protocol_Class1 & Protocol_Class2 - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'Protocol_Class1' cannot be represented in Objective-C}} var var_Existential7: Protocol_Class1 & Protocol_ObjC1 // CHECK-LABEL: {{^}} var var_Existential7: Protocol_Class1 & Protocol_ObjC1 - @objc // access-note-move{{infer_instanceVar1.var_Existential7_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_Existential7_}} var var_Existential7_: Protocol_Class1 & Protocol_ObjC1 - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'Protocol_Class1' cannot be represented in Objective-C}} var var_Existential8: Protocol_ObjC1 @@ -1380,7 +1381,7 @@ class infer_instanceVar1 { var var_CFunctionPointer_1: @convention(c) () -> () // CHECK-LABEL: @objc var var_CFunctionPointer_invalid_1: Int var var_CFunctionPointer_invalid_1: @convention(c) Int // expected-error {{@convention attribute only applies to function types}} - // CHECK-LABEL: {{^}} var var_CFunctionPointer_invalid_2: @convention(c) (PlainStruct) -> Int + // CHECK-LABEL: {{^}} var var_CFunctionPointer_invalid_2: <> var var_CFunctionPointer_invalid_2: @convention(c) (PlainStruct) -> Int // expected-error {{'(PlainStruct) -> Int' is not representable in Objective-C, so it cannot be used with '@convention(c)'}} // Confusing diagnostic for @convention(c) throws @@ -1550,16 +1551,16 @@ class infer_instanceVar1 { var var_ArrayType3: [PlainStruct] // CHECK-LABEL: {{^}} var var_ArrayType3: [PlainStruct] - @objc // access-note-move{{infer_instanceVar1.var_ArrayType3_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ArrayType3_}} var var_ArrayType3_: [PlainStruct] - // access-note-adjust expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ArrayType4: [(AnyObject) -> AnyObject] // no-error // CHECK-LABEL: {{^}} var var_ArrayType4: [(AnyObject) -> AnyObject] - @objc // access-note-move{{infer_instanceVar1.var_ArrayType4_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ArrayType4_}} var var_ArrayType4_: [(AnyObject) -> AnyObject] - // access-note-adjust expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ArrayType5: [Protocol_ObjC1] // CHECK-LABEL: {{^}} @objc var var_ArrayType5: [Protocol_ObjC1] @@ -1576,23 +1577,23 @@ class infer_instanceVar1 { var var_ArrayType7: [PlainClass] // CHECK-LABEL: {{^}} var var_ArrayType7: [PlainClass] - @objc // access-note-move{{infer_instanceVar1.var_ArrayType7_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ArrayType7_}} var var_ArrayType7_: [PlainClass] - // access-note-adjust expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ArrayType8: [PlainProtocol] // CHECK-LABEL: {{^}} var var_ArrayType8: [PlainProtocol] - @objc // access-note-move{{infer_instanceVar1.var_ArrayType8_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ArrayType8_}} var var_ArrayType8_: [PlainProtocol] - // access-note-adjust expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ArrayType9: [Protocol_ObjC1 & PlainProtocol] // CHECK-LABEL: {{^}} var var_ArrayType9: [PlainProtocol & Protocol_ObjC1] - @objc // access-note-move{{infer_instanceVar1.var_ArrayType9_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ArrayType9_}} var var_ArrayType9_: [Protocol_ObjC1 & PlainProtocol] - // access-note-adjust expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ArrayType10: [Protocol_ObjC1 & Protocol_ObjC2] // CHECK-LABEL: {{^}} @objc var var_ArrayType10: [Protocol_ObjC1 & Protocol_ObjC2] @@ -1610,16 +1611,16 @@ class infer_instanceVar1 { var var_ArrayType13: [Any?] // CHECK-LABEL: {{^}} var var_ArrayType13: [Any?] - @objc // access-note-move{{infer_instanceVar1.var_ArrayType13_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ArrayType13_}} var var_ArrayType13_: [Any?] - // access-note-adjust expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ArrayType15: [AnyObject?] // CHECK-LABEL: {{^}} var var_ArrayType15: [AnyObject?] - @objc // access-note-move{{infer_instanceVar1.var_ArrayType15_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ArrayType15_}} var var_ArrayType15_: [AnyObject?] - // access-note-adjust expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} var var_ArrayType16: [[@convention(block) (AnyObject) -> AnyObject]] // no-error // CHECK-LABEL: {{^}} @objc var var_ArrayType16: {{\[}}[@convention(block) (AnyObject) -> AnyObject]] @@ -1630,9 +1631,9 @@ class infer_instanceVar1 { var var_ArrayType17: [[(AnyObject) -> AnyObject]] // no-error // CHECK-LABEL: {{^}} var var_ArrayType17: {{\[}}[(AnyObject) -> AnyObject]] - @objc // access-note-move{{infer_instanceVar1.var_ArrayType17_}} + @objc // bad-access-note-move{{infer_instanceVar1.var_ArrayType17_}} var var_ArrayType17_: [[(AnyObject) -> AnyObject]] - // access-note-adjust expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error @-1{{property cannot be marked @objc because its type cannot be represented in Objective-C}} } @objc // access-note-move{{ObjCBase}} @@ -1651,67 +1652,67 @@ class infer_instanceVar2< var var_GP_Unconstrained: GP_Unconstrained // CHECK-LABEL: {{^}} var var_GP_Unconstrained: GP_Unconstrained - @objc // access-note-move{{infer_instanceVar2.var_GP_Unconstrained_}} + @objc // bad-access-note-move{{infer_instanceVar2.var_GP_Unconstrained_}} var var_GP_Unconstrained_: GP_Unconstrained - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} var var_GP_PlainClass: GP_PlainClass // CHECK-LABEL: {{^}} var var_GP_PlainClass: GP_PlainClass - @objc // access-note-move{{infer_instanceVar2.var_GP_PlainClass_}} + @objc // bad-access-note-move{{infer_instanceVar2.var_GP_PlainClass_}} var var_GP_PlainClass_: GP_PlainClass - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} var var_GP_PlainProtocol: GP_PlainProtocol // CHECK-LABEL: {{^}} var var_GP_PlainProtocol: GP_PlainProtocol - @objc // access-note-move{{infer_instanceVar2.var_GP_PlainProtocol_}} + @objc // bad-access-note-move{{infer_instanceVar2.var_GP_PlainProtocol_}} var var_GP_PlainProtocol_: GP_PlainProtocol - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} var var_GP_Class_ObjC: GP_Class_ObjC // CHECK-LABEL: {{^}} var var_GP_Class_ObjC: GP_Class_ObjC - @objc // access-note-move{{infer_instanceVar2.var_GP_Class_ObjC_}} + @objc // bad-access-note-move{{infer_instanceVar2.var_GP_Class_ObjC_}} var var_GP_Class_ObjC_: GP_Class_ObjC - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} var var_GP_Protocol_Class: GP_Protocol_Class // CHECK-LABEL: {{^}} var var_GP_Protocol_Class: GP_Protocol_Class - @objc // access-note-move{{infer_instanceVar2.var_GP_Protocol_Class_}} + @objc // bad-access-note-move{{infer_instanceVar2.var_GP_Protocol_Class_}} var var_GP_Protocol_Class_: GP_Protocol_Class - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} var var_GP_Protocol_ObjC: GP_Protocol_ObjC // CHECK-LABEL: {{^}} var var_GP_Protocol_ObjC: GP_Protocol_ObjC - @objc // access-note-move{{infer_instanceVar2.var_GP_Protocol_ObjCa}} + @objc // bad-access-note-move{{infer_instanceVar2.var_GP_Protocol_ObjCa}} var var_GP_Protocol_ObjCa: GP_Protocol_ObjC - // access-note-adjust expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} func func_GP_Unconstrained(a: GP_Unconstrained) {} // CHECK-LABEL: {{^}} func func_GP_Unconstrained(a: GP_Unconstrained) { - @objc // access-note-move{{infer_instanceVar2.func_GP_Unconstrained_(a:)}} + @objc // bad-access-note-move{{infer_instanceVar2.func_GP_Unconstrained_(a:)}} func func_GP_Unconstrained_(a: GP_Unconstrained) {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} - @objc // access-note-move{{infer_instanceVar2.func_GP_Unconstrained_()}} + @objc // bad-access-note-move{{infer_instanceVar2.func_GP_Unconstrained_()}} func func_GP_Unconstrained_() -> GP_Unconstrained {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} - @objc // access-note-move{{infer_instanceVar2.func_GP_Class_ObjC__()}} + @objc // bad-access-note-move{{infer_instanceVar2.func_GP_Class_ObjC__()}} func func_GP_Class_ObjC__() -> GP_Class_ObjC {} - // access-note-adjust expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked @objc because its result type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} } @@ -1835,8 +1836,7 @@ protocol infer_protocol5 : Protocol_ObjC1, Protocol_Class1 { class C { // Don't crash. - @objc // access-note-move{{C.foo(x:)}} - func foo(x: Undeclared) {} // expected-error {{cannot find type 'Undeclared' in scope}} + @objc func foo(x: Undeclared) {} // expected-error {{cannot find type 'Undeclared' in scope}} @IBAction func myAction(sender: Undeclared) {} // expected-error {{cannot find type 'Undeclared' in scope}} @IBSegueAction func myAction(coder: Undeclared, sender: Undeclared) -> Undeclared {fatalError()} // expected-error {{cannot find type 'Undeclared' in scope}} expected-error {{cannot find type 'Undeclared' in scope}} expected-error {{cannot find type 'Undeclared' in scope}} } @@ -2003,18 +2003,18 @@ extension PlainClass { func badlyNamed(_: Int, y: Int) {} } -@objc(Class:) // access-note-move{{BadClass1}} expected-error{{'@objc' class must have a simple name}}{{12-13=}} +@objc(Class:) // bad-access-note-move{{BadClass1}} expected-error{{'@objc' class must have a simple name}}{{12-13=}} class BadClass1 { } -@objc(Protocol:) // access-note-move{{BadProto1}} expected-error{{'@objc' protocol must have a simple name}}{{15-16=}} +@objc(Protocol:) // bad-access-note-move{{BadProto1}} expected-error{{'@objc' protocol must have a simple name}}{{15-16=}} protocol BadProto1 { } -@objc(Enum:) // access-note-move{{BadEnum1}} expected-error{{'@objc' enum must have a simple name}}{{11-12=}} +@objc(Enum:) // bad-access-note-move{{BadEnum1}} expected-error{{'@objc' enum must have a simple name}}{{11-12=}} enum BadEnum1: Int { case X } @objc // access-note-move{{BadEnum2}} enum BadEnum2: Int { - @objc(X:) // access-note-move{{BadEnum2.X}} expected-error{{'@objc' enum case must have a simple name}}{{10-11=}} + @objc(X:) // bad-access-note-move{{BadEnum2.X}} expected-error{{'@objc' enum case must have a simple name}}{{10-11=}} case X } @@ -2022,32 +2022,32 @@ class BadClass2 { @objc(realDealloc) // expected-error{{'@objc' deinitializer cannot have a name}} deinit { } - @objc(badprop:foo:wibble:) // access-note-move{{BadClass2.badprop}} expected-error{{'@objc' property must have a simple name}}{{16-28=}} + @objc(badprop:foo:wibble:) // bad-access-note-move{{BadClass2.badprop}} expected-error{{'@objc' property must have a simple name}}{{16-28=}} var badprop: Int = 5 - @objc(foo) // access-note-move{{BadClass2.subscript(_:)}} expected-error{{'@objc' subscript cannot have a name; did you mean to put the name on the getter or setter?}} + @objc(foo) // bad-access-note-move{{BadClass2.subscript(_:)}} expected-error{{'@objc' subscript cannot have a name; did you mean to put the name on the getter or setter?}} subscript (i: Int) -> Int { get { return i } } - @objc(foo) // access-note-move{{BadClass2.noArgNamesOneParam(x:)}} expected-error{{'@objc' method name provides names for 0 arguments, but method has one parameter}} + @objc(foo) // bad-access-note-move{{BadClass2.noArgNamesOneParam(x:)}} expected-error{{'@objc' method name provides names for 0 arguments, but method has one parameter}} func noArgNamesOneParam(x: Int) { } - @objc(foo) // access-note-move{{BadClass2.noArgNamesOneParam2(_:)}} expected-error{{'@objc' method name provides names for 0 arguments, but method has one parameter}} + @objc(foo) // bad-access-note-move{{BadClass2.noArgNamesOneParam2(_:)}} expected-error{{'@objc' method name provides names for 0 arguments, but method has one parameter}} func noArgNamesOneParam2(_: Int) { } - @objc(foo) // access-note-move{{BadClass2.noArgNamesTwoParams(_:y:)}} expected-error{{'@objc' method name provides names for 0 arguments, but method has 2 parameters}} + @objc(foo) // bad-access-note-move{{BadClass2.noArgNamesTwoParams(_:y:)}} expected-error{{'@objc' method name provides names for 0 arguments, but method has 2 parameters}} func noArgNamesTwoParams(_: Int, y: Int) { } - @objc(foo:) // access-note-move{{BadClass2.oneArgNameTwoParams(_:y:)}} expected-error{{'@objc' method name provides one argument name, but method has 2 parameters}} + @objc(foo:) // bad-access-note-move{{BadClass2.oneArgNameTwoParams(_:y:)}} expected-error{{'@objc' method name provides one argument name, but method has 2 parameters}} func oneArgNameTwoParams(_: Int, y: Int) { } - @objc(foo:) // access-note-move{{BadClass2.oneArgNameNoParams()}} expected-error{{'@objc' method name provides one argument name, but method has 0 parameters}} + @objc(foo:) // bad-access-note-move{{BadClass2.oneArgNameNoParams()}} expected-error{{'@objc' method name provides one argument name, but method has 0 parameters}} func oneArgNameNoParams() { } - @objc(foo:) // access-note-move{{BadClass2.init()}} expected-error{{'@objc' initializer name provides one argument name, but initializer has 0 parameters}} + @objc(foo:) // bad-access-note-move{{BadClass2.init()}} expected-error{{'@objc' initializer name provides one argument name, but initializer has 0 parameters}} init() { } var _prop = 5 @@ -2060,10 +2060,10 @@ class BadClass2 { } var prop2: Int { - @objc(property) // access-note-move{{getter:BadClass2.prop2()}} - get { return _prop } // access-note-adjust expected-error{{'@objc' getter for non-'@objc' property}} - @objc(setProperty:) // access-note-move{{setter:BadClass2.prop2()}} - set { _prop = newValue } // access-note-adjust expected-error{{'@objc' setter for non-'@objc' property}} + @objc(property) // bad-access-note-move{{getter:BadClass2.prop2()}} expected-error{{'@objc' getter for non-'@objc' property}} {{5-21=}} + get { return _prop } + @objc(setProperty:) // bad-access-note-move{{setter:BadClass2.prop2()}} expected-error{{'@objc' setter for non-'@objc' property}} {{5-25=}} + set { _prop = newValue } } var prop3: Int { @@ -2098,14 +2098,14 @@ class Super { } class Sub1 : Super { - @objc(foo) // access-note-move{{Sub1.foo}} expected-error{{Objective-C property has a different name from the property it overrides ('foo' vs. 'renamedFoo')}}{{9-12=renamedFoo}} + @objc(foo) // bad-access-note-move{{Sub1.foo}} expected-error{{Objective-C property has a different name from the property it overrides ('foo' vs. 'renamedFoo')}}{{9-12=renamedFoo}} override var foo: Int { get { return 5 } } override func process(i: Int?) -> Int { } // expected-error{{method cannot be an @objc override because the type of the parameter cannot be represented in Objective-C}} } class Sub2 : Super { - @objc // access-note-move{{Sub2.foo}} + @objc // bad-access-note-move{{Sub2.foo}} -- @objc is already implied by overriding an @objc attribute, so access notes shouldn't emit a remark override var foo: Int { get { return 5 } } } @@ -2119,7 +2119,7 @@ class Sub4 : Super { } class Sub5 : Super { - @objc(wrongFoo) // access-note-move{{Sub5.foo}} expected-error{{Objective-C property has a different name from the property it overrides ('wrongFoo' vs. 'renamedFoo')}} {{9-17=renamedFoo}} + @objc(wrongFoo) // bad-access-note-move{{Sub5.foo}} expected-error{{Objective-C property has a different name from the property it overrides ('wrongFoo' vs. 'renamedFoo')}} {{9-17=renamedFoo}} override var foo: Int { get { return 5 } } } @@ -2134,19 +2134,19 @@ class ClosureArguments { @objc // access-note-move{{ClosureArguments.foo(f:)}} func foo(f: (Int) -> ()) {} // CHECK: @objc func bar - @objc // access-note-move{{ClosureArguments.bar(f:)}} - func bar(f: (NotObjCEnum) -> NotObjCStruct) {} // access-note-adjust expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + @objc // bad-access-note-move{{ClosureArguments.bar(f:)}} + func bar(f: (NotObjCEnum) -> NotObjCStruct) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} // CHECK: @objc func bas - @objc // access-note-move{{ClosureArguments.bas(f:)}} - func bas(f: (NotObjCEnum) -> ()) {} // access-note-adjust expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + @objc // bad-access-note-move{{ClosureArguments.bas(f:)}} + func bas(f: (NotObjCEnum) -> ()) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} // CHECK: @objc func zim - @objc // access-note-move{{ClosureArguments.zim(f:)}} - func zim(f: () -> NotObjCStruct) {} // access-note-adjust expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + @objc // bad-access-note-move{{ClosureArguments.zim(f:)}} + func zim(f: () -> NotObjCStruct) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} // CHECK: @objc func zang - @objc // access-note-move{{ClosureArguments.zang(f:)}} - func zang(f: (NotObjCEnum, NotObjCStruct) -> ()) {} // access-note-adjust expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} - @objc // access-note-move{{ClosureArguments.zangZang(f:)}} - func zangZang(f: (Int...) -> ()) {} // access-note-adjust expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + @objc // bad-access-note-move{{ClosureArguments.zang(f:)}} + func zang(f: (NotObjCEnum, NotObjCStruct) -> ()) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + @objc // bad-access-note-move{{ClosureArguments.zangZang(f:)}} + func zangZang(f: (Int...) -> ()) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} // CHECK: @objc func fooImplicit func fooImplicit(f: (Int) -> ()) {} // CHECK: {{^}} func barImplicit @@ -2211,24 +2211,24 @@ class Load3 { // Members of protocol extensions cannot be @objc extension PlainProtocol { - @objc // access-note-move{{PlainProtocol.property}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{PlainProtocol.property}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} var property: Int { return 5 } - @objc // access-note-move{{PlainProtocol.subscript(_:)}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{PlainProtocol.subscript(_:)}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} subscript(x: Int) -> Class_ObjC1 { return Class_ObjC1() } - @objc // access-note-move{{PlainProtocol.fun()}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{PlainProtocol.fun()}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} func fun() { } } extension Protocol_ObjC1 { - @objc // access-note-move{{Protocol_ObjC1.property}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{Protocol_ObjC1.property}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} var property: Int { return 5 } - @objc // access-note-move{{Protocol_ObjC1.subscript(_:)}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{Protocol_ObjC1.subscript(_:)}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} subscript(x: Int) -> Class_ObjC1 { return Class_ObjC1() } - @objc // access-note-move{{Protocol_ObjC1.fun()}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} + @objc // bad-access-note-move{{Protocol_ObjC1.fun()}} expected-error{{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} func fun() { } } @@ -2267,37 +2267,37 @@ class ClassThrows1 { // Errors - @objc // access-note-move{{ClassThrows1.methodReturnsOptionalObjCClass()}} - func methodReturnsOptionalObjCClass() throws -> Class_ObjC1? { return nil } // access-note-adjust expected-error{{throwing method cannot be marked @objc because it returns a value of optional type 'Class_ObjC1?'; 'nil' indicates failure to Objective-C}} + @objc // bad-access-note-move{{ClassThrows1.methodReturnsOptionalObjCClass()}} + func methodReturnsOptionalObjCClass() throws -> Class_ObjC1? { return nil } // access-note-adjust{{@objc}} expected-error{{throwing method cannot be marked @objc because it returns a value of optional type 'Class_ObjC1?'; 'nil' indicates failure to Objective-C}} - @objc // access-note-move{{ClassThrows1.methodReturnsOptionalArray()}} - func methodReturnsOptionalArray() throws -> [String]? { return nil } // access-note-adjust expected-error{{throwing method cannot be marked @objc because it returns a value of optional type '[String]?'; 'nil' indicates failure to Objective-C}} + @objc // bad-access-note-move{{ClassThrows1.methodReturnsOptionalArray()}} + func methodReturnsOptionalArray() throws -> [String]? { return nil } // access-note-adjust{{@objc}} expected-error{{throwing method cannot be marked @objc because it returns a value of optional type '[String]?'; 'nil' indicates failure to Objective-C}} - @objc // access-note-move{{ClassThrows1.methodReturnsInt()}} - func methodReturnsInt() throws -> Int { return 0 } // access-note-adjust expected-error{{throwing method cannot be marked @objc because it returns a value of type 'Int'; return 'Void' or a type that bridges to an Objective-C class}} + @objc // bad-access-note-move{{ClassThrows1.methodReturnsInt()}} + func methodReturnsInt() throws -> Int { return 0 } // access-note-adjust{{@objc}} expected-error{{throwing method cannot be marked @objc because it returns a value of type 'Int'; return 'Void' or a type that bridges to an Objective-C class}} - @objc // access-note-move{{ClassThrows1.methodAcceptsThrowingFunc(fn:)}} + @objc // bad-access-note-move{{ClassThrows1.methodAcceptsThrowingFunc(fn:)}} func methodAcceptsThrowingFunc(fn: (String) throws -> Int) { } - // access-note-adjust expected-error@-1{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2{{throwing function types cannot be represented in Objective-C}} - @objc // access-note-move{{ClassThrows1.init(radians:)}} - init?(radians: Double) throws { } // access-note-adjust expected-error{{a failable and throwing initializer cannot be marked @objc because 'nil' indicates failure to Objective-C}} + @objc // bad-access-note-move{{ClassThrows1.init(radians:)}} + init?(radians: Double) throws { } // access-note-adjust{{@objc}} expected-error{{a failable and throwing initializer cannot be marked @objc because 'nil' indicates failure to Objective-C}} - @objc // access-note-move{{ClassThrows1.init(string:)}} - init!(string: String) throws { } // access-note-adjust expected-error{{a failable and throwing initializer cannot be marked @objc because 'nil' indicates failure to Objective-C}} + @objc // bad-access-note-move{{ClassThrows1.init(string:)}} + init!(string: String) throws { } // access-note-adjust{{@objc}} expected-error{{a failable and throwing initializer cannot be marked @objc because 'nil' indicates failure to Objective-C}} - @objc // access-note-move{{ClassThrows1.fooWithErrorEnum1(x:)}} + @objc // bad-access-note-move{{ClassThrows1.fooWithErrorEnum1(x:)}} func fooWithErrorEnum1(x: ErrorEnum) {} - // access-note-adjust expected-error@-1{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2{{non-'@objc' enums cannot be represented in Objective-C}} // CHECK: {{^}} func fooWithErrorEnum2(x: ErrorEnum) func fooWithErrorEnum2(x: ErrorEnum) {} - @objc // access-note-move{{ClassThrows1.fooWithErrorProtocolComposition1(x:)}} + @objc // bad-access-note-move{{ClassThrows1.fooWithErrorProtocolComposition1(x:)}} func fooWithErrorProtocolComposition1(x: Error & Protocol_ObjC1) { } - // access-note-adjust expected-error@-1{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2{{protocol-constrained type containing 'Error' cannot be represented in Objective-C}} // CHECK: {{^}} func fooWithErrorProtocolComposition2(x: Error & Protocol_ObjC1) @@ -2338,11 +2338,11 @@ class ImplicitClassThrows1 { // CHECK: {{^}} func methodReturnsBridgedValueType() throws -> NSRange func methodReturnsBridgedValueType() throws -> NSRange { return NSRange() } - @objc // access-note-move{{ImplicitClassThrows1.methodReturnsBridgedValueType2()}} + @objc // bad-access-note-move{{ImplicitClassThrows1.methodReturnsBridgedValueType2()}} func methodReturnsBridgedValueType2() throws -> NSRange { return NSRange() } - // access-note-adjust expected-error@-3{{throwing method cannot be marked @objc because it returns a value of type 'NSRange' (aka '_NSRange'); return 'Void' or a type that bridges to an Objective-C class}} + // access-note-adjust{{@objc}} expected-error@-3{{throwing method cannot be marked @objc because it returns a value of type 'NSRange' (aka '_NSRange'); return 'Void' or a type that bridges to an Objective-C class}} // CHECK: {{^}} @objc func methodReturnsError() throws -> Error func methodReturnsError() throws -> Error { return ErrorEnum.failed } @@ -2366,33 +2366,33 @@ class SubclassImplicitClassThrows1 : ImplicitClassThrows1 { class ThrowsRedecl1 { @objc // access-note-move{{ThrowsRedecl1.method1(_:error:)}} func method1(_ x: Int, error: Class_ObjC1) { } // expected-note{{declared here}} - @objc // access-note-move{{ThrowsRedecl1.method1(_:)}} - func method1(_ x: Int) throws { } // expected-error {{with Objective-C selector 'method1:error:'}} + @objc // bad-access-note-move{{ThrowsRedecl1.method1(_:)}} + func method1(_ x: Int) throws { } // access-note-adjust{{@objc}} expected-error {{method 'method1' with Objective-C selector 'method1:error:' conflicts with method 'method1(_:error:)' with the same Objective-C selector}} @objc // access-note-move{{ThrowsRedecl1.method2AndReturnError(_:)}} func method2AndReturnError(_ x: Int) { } // expected-note{{declared here}} - @objc // access-note-move{{ThrowsRedecl1.method2()}} - func method2() throws { } // expected-error {{with Objective-C selector 'method2AndReturnError:'}} + @objc // bad-access-note-move{{ThrowsRedecl1.method2()}} + func method2() throws { } // access-note-adjust{{@objc}} expected-error {{method 'method2()' with Objective-C selector 'method2AndReturnError:' conflicts with method 'method2AndReturnError' with the same Objective-C selector}} @objc // access-note-move{{ThrowsRedecl1.method3(_:error:closure:)}} func method3(_ x: Int, error: Int, closure: @escaping (Int) -> Int) { } // expected-note{{declared here}} - @objc // access-note-move{{ThrowsRedecl1.method3(_:closure:)}} - func method3(_ x: Int, closure: (Int) -> Int) throws { } // expected-error {{with Objective-C selector 'method3:error:closure:'}} + @objc // bad-access-note-move{{ThrowsRedecl1.method3(_:closure:)}} + func method3(_ x: Int, closure: (Int) -> Int) throws { } // access-note-adjust{{@objc}} expected-error {{method 'method3(_:closure:)' with Objective-C selector 'method3:error:closure:' conflicts with method 'method3(_:error:closure:)' with the same Objective-C selector}} @objc(initAndReturnError:) // access-note-move{{ThrowsRedecl1.initMethod1(error:)}} func initMethod1(error: Int) { } // expected-note{{declared here}} - @objc // access-note-move{{ThrowsRedecl1.init()}} - init() throws { } // expected-error {{with Objective-C selector 'initAndReturnError:'}} + @objc // bad-access-note-move{{ThrowsRedecl1.init()}} + init() throws { } // access-note-adjust{{@objc}} expected-error {{initializer 'init()' with Objective-C selector 'initAndReturnError:' conflicts with method 'initMethod1(error:)' with the same Objective-C selector}} @objc(initWithString:error:) // access-note-move{{ThrowsRedecl1.initMethod2(string:error:)}} func initMethod2(string: String, error: Int) { } // expected-note{{declared here}} - @objc // access-note-move{{ThrowsRedecl1.init(string:)}} - init(string: String) throws { } // expected-error {{with Objective-C selector 'initWithString:error:'}} + @objc // bad-access-note-move{{ThrowsRedecl1.init(string:)}} + init(string: String) throws { } // access-note-adjust{{@objc}} expected-error {{initializer 'init(string:)' with Objective-C selector 'initWithString:error:' conflicts with method 'initMethod2(string:error:)' with the same Objective-C selector}} @objc(initAndReturnError:fn:) // access-note-move{{ThrowsRedecl1.initMethod3(error:fn:)}} func initMethod3(error: Int, fn: @escaping (Int) -> Int) { } // expected-note{{declared here}} - @objc // access-note-move{{ThrowsRedecl1.init(fn:)}} - init(fn: (Int) -> Int) throws { } // expected-error {{with Objective-C selector 'initAndReturnError:fn:'}} + @objc // bad-access-note-move{{ThrowsRedecl1.init(fn:)}} + init(fn: (Int) -> Int) throws { } // access-note-adjust{{@objc}} expected-error {{initializer 'init(fn:)' with Objective-C selector 'initAndReturnError:fn:' conflicts with method 'initMethod3(error:fn:)' with the same Objective-C selector}} } class ThrowsObjCName { @@ -2402,10 +2402,10 @@ class ThrowsObjCName { @objc(method5AndReturnError:x:closure:) // access-note-move{{ThrowsObjCName.method5(x:closure:)}} func method5(x: Int, closure: @escaping (Int) -> Int) throws { } - @objc(method6) // access-note-move{{ThrowsObjCName.method6()}} expected-error{{@objc' method name provides names for 0 arguments, but method has one parameter (the error parameter)}} + @objc(method6) // bad-access-note-move{{ThrowsObjCName.method6()}} expected-error{{'@objc' method name provides names for 0 arguments, but method has one parameter (the error parameter)}} func method6() throws { } - @objc(method7) // access-note-move{{ThrowsObjCName.method7(x:)}} expected-error{{@objc' method name provides names for 0 arguments, but method has 2 parameters (including the error parameter)}} + @objc(method7) // bad-access-note-move{{ThrowsObjCName.method7(x:)}} expected-error{{'@objc' method name provides names for 0 arguments, but method has 2 parameters (including the error parameter)}} func method7(x: Int) throws { } // CHECK-DUMP: func_decl{{.*}}"method8(_:fn1:fn2:)"{{.*}}foreign_error=ZeroResult,unowned,param=2,paramtype=Optional>>,resulttype=ObjCBool @@ -2432,7 +2432,7 @@ protocol ProtocolThrowsObjCName { } class ConformsToProtocolThrowsObjCName1 : ProtocolThrowsObjCName { - @objc // access-note-move{{ConformsToProtocolThrowsObjCName1.doThing(_:)}} + @objc // bad-access-note-move{{ConformsToProtocolThrowsObjCName1.doThing(_:)}} -- @objc inherited, so no remarks func doThing(_ x: String) throws -> String { return x } // okay } @@ -2651,7 +2651,7 @@ protocol SR_9035_P { // SR-12801: Make sure we reject an @objc generic subscript. class SR12801 { - @objc // access-note-move{{SR12801.subscript(_:)}} + @objc // bad-access-note-move{{SR12801.subscript(_:)}} subscript(foo : [T]) -> Int { return 0 } - // access-note-adjust expected-error@-1 {{subscript cannot be marked @objc because it has generic parameters}} + // access-note-adjust{{@objc}} expected-error@-1 {{subscript cannot be marked @objc because it has generic parameters}} } diff --git a/test/attr/attr_objc_async.swift b/test/attr/attr_objc_async.swift index 40c66b888c6bc..b4211f2ddc079 100644 --- a/test/attr/attr_objc_async.swift +++ b/test/attr/attr_objc_async.swift @@ -1,6 +1,6 @@ -// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify -verify-ignore-unknown %s -swift-version 5 -enable-source-import -I %S/Inputs -enable-experimental-concurrency -warn-concurrency -// RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -source-filename %s -function-definitions=true -prefer-type-repr=false -print-implicit-attrs=true -explode-pattern-binding-decls=true -disable-objc-attr-requires-foundation-module -swift-version 5 -enable-source-import -I %S/Inputs -enable-experimental-concurrency | %FileCheck %s -// RUN: not %target-swift-frontend -typecheck -dump-ast -disable-objc-attr-requires-foundation-module %s -swift-version 5 -enable-source-import -I %S/Inputs -enable-experimental-concurrency -warn-concurrency > %t.ast +// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify -verify-ignore-unknown %s -swift-version 5 -enable-source-import -I %S/Inputs -disable-availability-checking -warn-concurrency +// RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -source-filename %s -function-definitions=true -prefer-type-repr=false -print-implicit-attrs=true -explode-pattern-binding-decls=true -disable-objc-attr-requires-foundation-module -swift-version 5 -enable-source-import -I %S/Inputs | %FileCheck %s +// RUN: not %target-swift-frontend -typecheck -dump-ast -disable-objc-attr-requires-foundation-module %s -swift-version 5 -enable-source-import -I %S/Inputs -disable-availability-checking -warn-concurrency > %t.ast // RUN: %FileCheck -check-prefix CHECK-DUMP %s < %t.ast // REQUIRES: objc_interop // REQUIRES: concurrency @@ -47,13 +47,11 @@ actor MyActor { @objc nonisolated func synchronousGood() { } } -// CHECK: actor class MyActor2 actor class MyActor2 { } -// expected-warning@-1{{'actor class' has been renamed to 'actor'}}{{7-13=}} +// expected-error@-1 {{keyword 'class' cannot be used as an identifier here}} // CHECK: @objc actor MyObjCActor @objc actor MyObjCActor: NSObject { } -// CHECK: @objc actor class MyObjCActor2 @objc actor class MyObjCActor2: NSObject {} -// expected-warning@-1{{'actor class' has been renamed to 'actor'}}{{13-19=}} +// expected-error@-1 {{keyword 'class' cannot be used as an identifier here}} diff --git a/test/attr/attr_override.swift b/test/attr/attr_override.swift index e234a294b9e02..cec2a90967ee9 100644 --- a/test/attr/attr_override.swift +++ b/test/attr/attr_override.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -swift-version 5 +// RUN: %target-typecheck-verify-swift -swift-version 5 -requirement-machine=verify @override // expected-error {{'override' can only be specified on class members}} {{1-11=}} expected-error {{'override' is a declaration modifier, not an attribute}} {{1-2=}} func virtualAttributeCanNotBeUsedInSource() {} @@ -545,21 +545,21 @@ class SR_4206_DerivedConcrete_2: SR_4206_BaseGeneric_2 { // Base class generic w/ method generic, derived class generic w/ method generic but different requirement class SR_4206_BaseGeneric_3 { - func foo(arg: T) {} // expected-note {{overridden declaration is here}} + func foo(arg: T) {} } class SR_4206_DerivedGeneric_3: SR_4206_BaseGeneric_3 { - override func foo(arg: T) {} // expected-error {{overridden method 'foo' has generic signature which is incompatible with base method's generic signature ; expected generic signature to be }} + override func foo(arg: T) {} // expected-error {{method does not override any method from its superclass}} } // Base class not generic w/ method generic, derived class not generic w/ method generic but different requirement class SR_4206_BaseConcrete_4 { - func foo(arg: T) {} // expected-note {{overridden declaration is here}} + func foo(arg: T) {} } class SR_4206_DerivedConcrete_4: SR_4206_BaseConcrete_4 { - override func foo(arg: T) {} // expected-error {{overridden method 'foo' has generic signature which is incompatible with base method's generic signature ; expected generic signature to be }} + override func foo(arg: T) {} // expected-error {{method does not override any method from its superclass}} } // Base class not generic w/ method generic, derived class not generic w/ method generic but removed requirement @@ -575,22 +575,22 @@ class SR_4206_DerivedConcrete_5: SR_4206_BaseConcrete_5 { // Base class not generic w/ method generic, derived class generic w/ method generic but different requirement class SR_4206_BaseConcrete_6 { - func foo(arg: T) {} // expected-note {{overridden declaration is here}} + func foo(arg: T) {} } class SR_4206_DerivedGeneric_6: SR_4206_BaseConcrete_6 { - override func foo(arg: T) {} // expected-error {{overridden method 'foo' has generic signature which is incompatible with base method's generic signature ; expected generic signature to be }} + override func foo(arg: T) {} // expected-error {{method does not override any method from its superclass}} } // Contextual where clauses on non-generic members class SR_4206_Base_7 { - func foo1() where T: SR_4206_Protocol_1 {} // expected-note {{overridden declaration is here}} + func foo1() where T: SR_4206_Protocol_1 {} func foo2() where T: SR_4206_Protocol_1 {} } class SR_4206_Derived_7: SR_4206_Base_7 { - override func foo1() where T: SR_4206_Protocol_2 {} // expected-error {{overridden method 'foo1' has generic signature which is incompatible with base method's generic signature ; expected generic signature to be }} + override func foo1() where T: SR_4206_Protocol_2 {} // expected-error {{method does not override any method from its superclass}} override func foo2() {} // OK } @@ -624,10 +624,10 @@ class SR_4206_Derived_9: SR_4206_Base_9 { // Override with constraint on a non-inherited generic param class SR_4206_Base_10 { - func foo() where T: SR_4206_Protocol_1 {} // expected-note {{overridden declaration is here}} + func foo() where T: SR_4206_Protocol_1 {} } class SR_4206_Derived_10: SR_4206_Base_10 { - override func foo() where U: SR_4206_Protocol_1 {} // expected-error {{overridden method 'foo' has generic signature which is incompatible with base method's generic signature ; expected generic signature to be }} + override func foo() where U: SR_4206_Protocol_1 {} // expected-error {{method does not override any method from its superclass}} } // Override with return type specialization @@ -710,3 +710,19 @@ public extension SR_11740_Base where F: SR_11740_Q { extension SR_11740_Derived where F: SR_11740_P { public static func foo(_: A) {} } + +// Make sure we don't crash on generic requirement mismatch +protocol P3 {} + +protocol P4 { + associatedtype T +} + +class Base { + func method(_: T) {} + func method(_: T) where T.T : P3 {} +} + +class Derived: Base { + override func method(_: T) {} +} diff --git a/test/attr/attr_specialize.swift b/test/attr/attr_specialize.swift index f2f8a964aa8e2..32fe8e292b0ba 100644 --- a/test/attr/attr_specialize.swift +++ b/test/attr/attr_specialize.swift @@ -95,7 +95,7 @@ struct FloatElement : HasElt { typealias Element = Float } @_specialize(where T == FloatElement) -@_specialize(where T == IntElement) // expected-error{{'T.Element' cannot be equal to both 'IntElement.Element' (aka 'Int') and 'Float'}} +@_specialize(where T == IntElement) // FIXME e/xpected-error{{'T.Element' cannot be equal to both 'IntElement.Element' (aka 'Int') and 'Float'}} func sameTypeRequirement(_ t: T) where T.Element == Float {} @_specialize(where T == Sub) diff --git a/test/attr/global_actor.swift b/test/attr/global_actor.swift index 63d12061d244f..1bf7f192b9613 100644 --- a/test/attr/global_actor.swift +++ b/test/attr/global_actor.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency +// RUN: %target-swift-frontend -typecheck -verify %s -disable-availability-checking // REQUIRES: concurrency actor SomeActor { } @@ -20,35 +20,37 @@ struct GenericGlobalActor { // Ill-formed global actors. @globalActor -open class GA2 { // expected-error{{global actor 'GA2' requires a static property 'shared' that produces an actor instance}}{{17-17=\n public static let shared = <#actor instance#>}} +final class GA2 { // expected-error{{type 'GA2' does not conform to protocol 'GlobalActor'}} } @globalActor -struct GA3 { // expected-error{{global actor 'GA3' requires a static property 'shared' that produces an actor instance}} - let shared = SomeActor() // expected-note{{'shared' property in global actor is not 'static'}}{{3-3=static }} +struct GA3 { // expected-error{{type 'GA3' does not conform to protocol 'GlobalActor'}} + let shared = SomeActor() } @globalActor -struct GA4 { // expected-error{{global actor 'GA4' requires a static property 'shared' that produces an actor instance}} - private static let shared = SomeActor() // expected-note{{'shared' property has more restrictive access (private) than its global actor (internal)}}{{3-11=}} +struct GA4 { + private static let shared = SomeActor() // expected-error{{property 'shared' must be as accessible as its enclosing type because it matches a requirement in protocol 'GlobalActor'}} + // expected-note@-1{{mark the static property as 'internal' to satisfy the requirement}} } @globalActor -open class GA5 { // expected-error{{global actor 'GA5' requires a static property 'shared' that produces an actor instance}} - static let shared = SomeActor() // expected-note{{'shared' property has more restrictive access (internal) than its global actor (public)}}{{3-3=public}} +open class GA5 { // expected-error{{non-final class 'GA5' cannot be a global actor}} + static let shared = SomeActor() // expected-error{{property 'shared' must be declared public because it matches a requirement in public protocol 'GlobalActor'}} + // expected-note@-1{{mark the static property as 'public' to satisfy the requirement}} } @globalActor -struct GA6 { // expected-error{{global actor 'GA6' requires a static property 'shared' that produces an actor instance}} +struct GA6 { // expected-error{{type 'GA6' does not conform to protocol 'GlobalActor'}} } extension GA6 where T: Equatable { - static var shared: SomeActor { SomeActor() } // expected-note{{'shared' property in global actor cannot be in a constrained extension}} + static var shared: SomeActor { SomeActor() } } @globalActor -class GA7 { // expected-error{{global actor 'GA7' requires a static property 'shared' that produces an actor instance}} - static let shared = 5 // expected-note{{'shared' property type 'Int' does not conform to the 'Actor' protocol}} +final class GA7 { // expected-error{{type 'GA7' does not conform to protocol 'GlobalActor'}} + static let shared = 5 // expected-note{{candidate would match and infer 'ActorType' = 'Int' if 'Int' conformed to 'Actor'}} } // ----------------------------------------------------------------------- @@ -96,5 +98,5 @@ struct Container { // Redundant attributes // ----------------------------------------------------------------------- extension SomeActor { - @GA1 nonisolated func conflict1() { } // expected-error{{instance method 'conflict1()' has multiple actor-isolation attributes ('nonisolated' and 'GA1')}} + @GA1 nonisolated func conflict1() { } // expected-error 3{{instance method 'conflict1()' has multiple actor-isolation attributes ('nonisolated' and 'GA1')}} } diff --git a/test/decl/async/objc.swift b/test/decl/async/objc.swift index 0db971f7e241d..2e24f6574660c 100644 --- a/test/decl/async/objc.swift +++ b/test/decl/async/objc.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify %s -swift-version 5 -enable-experimental-concurrency -// RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -source-filename %s -function-definitions=true -prefer-type-repr=false -print-implicit-attrs=true -explode-pattern-binding-decls=true -disable-objc-attr-requires-foundation-module -swift-version 5 -enable-experimental-concurrency | %FileCheck %s +// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify %s -swift-version 5 -disable-availability-checking +// RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -source-filename %s -function-definitions=true -prefer-type-repr=false -print-implicit-attrs=true -explode-pattern-binding-decls=true -disable-objc-attr-requires-foundation-module -swift-version 5 | %FileCheck %s // REQUIRES: objc_interop // REQUIRES: concurrency import Foundation diff --git a/test/decl/circularity.swift b/test/decl/circularity.swift index 7ecbf7271982c..4cd53f05c7d4a 100644 --- a/test/decl/circularity.swift +++ b/test/decl/circularity.swift @@ -84,8 +84,12 @@ open class G1 { class C3: G1, P { // expected-error@-1 {{type 'C3' does not conform to protocol 'P'}} // expected-error@-2 {{cannot find type 'A' in scope}} + // expected-note@-3 {{through reference here}} override func run(a: A) {} // expected-error@-1 {{method does not override any method from its superclass}} + // expected-error@-2 {{circular reference}} + // expected-note@-3 2 {{through reference here}} + // expected-note@-4 {{while resolving type 'A'}} } // Another case that triggers circular override checking. diff --git a/test/decl/class/actor/basic.swift b/test/decl/class/actor/basic.swift index 36168aab1d4b9..ef46dbaaec7d7 100644 --- a/test/decl/class/actor/basic.swift +++ b/test/decl/class/actor/basic.swift @@ -1,28 +1,50 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency actor MyActor { } -class MyActorSubclass1: MyActor { } // expected-error{{actor types do not support inheritance}} -// expected-error@-1{{non-final class 'MyActorSubclass1' cannot conform to `Sendable`; use `UnsafeSendable`}} +class MyActorSubclass1: MyActor { } +// expected-error@-1{{actor types do not support inheritance}} +// expected-error@-2{{type 'MyActorSubclass1' cannot conform to the 'Actor' protocol}} +// expected-error@-3{{non-final class 'MyActorSubclass1' cannot conform to `Sendable`; use `@unchecked Sendable`}} actor MyActorSubclass2: MyActor { } // expected-error{{actor types do not support inheritance}} -// expected-warning@+1{{'actor class' has been renamed to 'actor'}}{{7-13=}} +// expected-error@+1{{keyword 'class' cannot be used as an identifier here}} actor class MyActorClass { } -class NonActor { } +class NonActor { } // expected-note{{overridden declaration is here}} actor NonActorSubclass : NonActor { } // expected-error{{actor types do not support inheritance}} +// expected-error@-1{{actor-isolated initializer 'init()' has different actor isolation from nonisolated overridden declaration}} -// expected-warning@+1{{'actor class' has been renamed to 'actor'}}{{14-20=}} +// expected-error@+1{{keyword 'class' cannot be used as an identifier here}} public actor class BobHope {} -// expected-warning@+1{{'actor class' has been renamed to 'actor'}}{{14-19=actor}}{{1-7=}} +// expected-error@+1{{keyword 'public' cannot be used as an identifier here}} actor public class BarbraStreisand {} -// expected-warning@+2{{'actor class' has been renamed to 'actor'}}{{14-21=}} -// expected-error@+1{{'actor' may only be used on 'class' declarations}} +// expected-error@+1{{keyword 'struct' cannot be used as an identifier here}} public actor struct JulieAndrews {} -// expected-warning@+2{{'actor class' has been renamed to 'actor'}}{{14-18=actor}}{{1-7=}} -// expected-error@+1{{'actor' may only be used on 'class' declarations}} +// expected-error@+1{{keyword 'public' cannot be used as an identifier here}} actor public enum TomHanks {} + +open actor A1 { } // expected-error{{only classes and overridable class members can be declared 'open'; use 'public'}} + +actor A2 { + required init() { } // expected-error{{'required' initializer in non-class type 'A2'}} + open func f() { } // expected-error{{only classes and overridable class members can be declared 'open'; use 'public'}} + + final func g() { } // okay for now + class func h() { } // expected-error{{class methods are only allowed within classes; use 'static' to declare a static method}} + static func i() { } // okay + + class var someProp: Int { 0 } // expected-error{{class properties are only allowed within classes; use 'static' to declare a static property}} +} + +extension A2 { + class func h2() { } // expected-error{{class methods are only allowed within classes; use 'static' to declare a static method}} + static func i2() { } // okay + + class subscript(i: Int) -> Int { i } // expected-error{{class subscripts are only allowed within classes; use 'static' to declare a static subscript}} + static subscript(s: String) -> String { s } +} diff --git a/test/decl/class/actor/conformance.swift b/test/decl/class/actor/conformance.swift index ac5787bde18c5..1770dbe2aefb1 100644 --- a/test/decl/class/actor/conformance.swift +++ b/test/decl/class/actor/conformance.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -enable-experimental-async-handler +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency @@ -20,8 +20,6 @@ protocol SyncProtocol { func syncMethodA() - func syncMethodB() - func syncMethodC() -> Int subscript (index: Int) -> String { get } @@ -42,11 +40,6 @@ actor OtherActor: SyncProtocol { func syncMethodA() { } // expected-error@-1{{actor-isolated instance method 'syncMethodA()' cannot be used to satisfy a protocol requirement}} // expected-note@-2{{add 'nonisolated' to 'syncMethodA()' to make this instance method not isolated to the actor}}{{3-3=nonisolated }} - // expected-note@-3{{add '@asyncHandler' to function 'syncMethodA()' to create an implicit asynchronous context}}{{3-3=@asyncHandler }} - - // Async handlers are okay. - @asyncHandler - func syncMethodB() { } // nonisolated methods are okay. // FIXME: Consider suggesting nonisolated if this didn't match. @@ -60,3 +53,29 @@ actor OtherActor: SyncProtocol { static func staticMethod() { } static var staticProperty: Int = 17 } + +protocol Initializers { + init() + init(string: String) + init(int: Int) async +} + +protocol SelfReqs { + func withBells() async -> Self +} + +actor A1: Initializers, SelfReqs { + init() { } + init(string: String) { } + init(int: Int) async { } + + func withBells() async -> A1 { self } +} + +actor A2: Initializers { + init() { } + init(string: String) { } + init(int: Int) { } + + func withBells() async -> A2 { self } +} diff --git a/test/decl/class/actor/global_actor_conformance.swift b/test/decl/class/actor/global_actor_conformance.swift index cee2a5c35ca4a..5470f048e72c5 100644 --- a/test/decl/class/actor/global_actor_conformance.swift +++ b/test/decl/class/actor/global_actor_conformance.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -enable-experimental-async-handler +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency actor SomeActor { } @@ -42,15 +42,3 @@ class C1 : P1, P2 { @GenericGlobalActor func asyncMethod2() async { } @GlobalActor func asyncMethod3() async { } } - - -class C2: P1 { - typealias Assoc = Int - - // Okay: we can ignore the mismatch in global actor types for 'asyncHandler' - // methods. - @asyncHandler func method1() { } - @asyncHandler func method2() { } - @asyncHandler func method3() { } - @asyncHandler func method4() { } -} diff --git a/test/decl/class/actor/noconcurrency.swift b/test/decl/class/actor/noconcurrency.swift deleted file mode 100644 index f30311a20784a..0000000000000 --- a/test/decl/class/actor/noconcurrency.swift +++ /dev/null @@ -1,8 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -actor C { - nonisolated func f() { } // expected-error{{'nonisolated' modifier is only valid when experimental concurrency is enabled}} -} - - - diff --git a/test/decl/class/effectful_properties.swift b/test/decl/class/effectful_properties.swift index bfe2449080b55..3ecaeebacbea7 100644 --- a/test/decl/class/effectful_properties.swift +++ b/test/decl/class/effectful_properties.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking enum E : Error { case NotAvailable diff --git a/test/decl/class/effectful_properties_objc.swift b/test/decl/class/effectful_properties_objc.swift index 12a619595b0c2..92cf762c106b9 100644 --- a/test/decl/class/effectful_properties_objc.swift +++ b/test/decl/class/effectful_properties_objc.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: objc_interop @@ -14,4 +14,4 @@ class ProblematicObjC { @objc subscript(_ i : Int) -> Int { // expected-error{{subscript with 'throws' or 'async' is not representable in Objective-C}} get throws { throw E.NotAvailable } } -} \ No newline at end of file +} diff --git a/test/decl/ext/protocol.swift b/test/decl/ext/protocol.swift index 860bcbcfbc5ba..74e82eedae194 100644 --- a/test/decl/ext/protocol.swift +++ b/test/decl/ext/protocol.swift @@ -197,8 +197,8 @@ extension AnotherBazProtocol where BazValue: AnotherBazProtocol {} // ok, does n // Protocol extensions with additional requirements // ---------------------------------------------------------------------------- extension P4 where Self.AssocP4 : P1 { -// expected-note@-1 {{candidate requires that 'Int' conform to 'P1' (requirement specified as 'Self.AssocP4' == 'P1')}} -// expected-note@-2 {{candidate requires that 'S4aHelper' conform to 'P1' (requirement specified as 'Self.AssocP4' == 'P1')}} +// expected-note@-1 {{candidate requires that 'Int' conform to 'P1' (requirement specified as 'Self.AssocP4' : 'P1')}} +// expected-note@-2 {{candidate requires that 'S4aHelper' conform to 'P1' (requirement specified as 'Self.AssocP4' : 'P1')}} func extP4a() { acceptsP1(reqP4a()) } diff --git a/test/decl/func/async.swift b/test/decl/func/async.swift index 74ba2790f7ad2..13772db96686f 100644 --- a/test/decl/func/async.swift +++ b/test/decl/func/async.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency @@ -6,8 +6,8 @@ func redecl1() async { } // expected-note{{previously declared here}} func redecl1() async throws { } // expected-error{{invalid redeclaration of 'redecl1()'}} -func redecl2() -> String { "" } // expected-note{{previously declared here}} -func redecl2() async -> String { "" } // expected-error{{invalid redeclaration of 'redecl2()'}} +func redecl2() -> String { "" } // okay +func redecl2() async -> String { "" } // okay // Override checking diff --git a/test/decl/init/basic_init.swift b/test/decl/init/basic_init.swift index 227602498a164..6b07ac0e47d62 100644 --- a/test/decl/init/basic_init.swift +++ b/test/decl/init/basic_init.swift @@ -22,15 +22,15 @@ class InitClass { @objc dynamic init(bar: Int) {} } class InitSubclass: InitClass {} -// expected-note@-1{{'init(baz:)' previously overridden here}} -// expected-note@-2{{'init(bar:)' previously overridden here}} +// expected-note@-1{{implicit initializer 'init(baz:)' declared here}} +// expected-note@-2{{implicit initializer 'init(bar:)' declared here}} extension InitSubclass { convenience init(arg: Bool) {} // expected-error{{non-@objc initializer 'init(arg:)' declared in 'InitClass' cannot be overridden from extension}} convenience override init(baz: Int) {} - // expected-error@-1 {{'init(baz:)' has already been overridden}} + // expected-error@-1 {{initializer 'init(baz:)' with Objective-C selector 'initWithBaz:' conflicts with implicit initializer 'init(baz:)' with the same Objective-C selector}} // expected-error@-2 {{cannot override a non-dynamic class declaration from an extension}} convenience override init(bar: Int) {} - // expected-error@-1 {{'init(bar:)' has already been overridden}} + // expected-error@-1 {{initializer 'init(bar:)' with Objective-C selector 'initWithBar:' conflicts with implicit initializer 'init(bar:)' with the same Objective-C selector}} } struct InitStruct { diff --git a/test/decl/nested/protocol.swift b/test/decl/nested/protocol.swift index 2ccc15b74e9a3..ab0427513619a 100644 --- a/test/decl/nested/protocol.swift +++ b/test/decl/nested/protocol.swift @@ -31,8 +31,7 @@ protocol OuterProtocol { struct ConformsToOuterProtocol : OuterProtocol { typealias Hen = Int - func f() { let _ = InnerProtocol.self } - // expected-error@-1 {{protocol 'InnerProtocol' can only be used as a generic constraint because it has Self or associated type requirements}} + func f() { let _ = InnerProtocol.self } // Ok } protocol Racoon { diff --git a/test/decl/nested/type_in_function.swift b/test/decl/nested/type_in_function.swift index 7fa1c325d4b60..3908dad939434 100644 --- a/test/decl/nested/type_in_function.swift +++ b/test/decl/nested/type_in_function.swift @@ -135,20 +135,6 @@ func genericFunction(t: T) { // expected-error@-2 {{'Second' inherits from itself}} } -// Spurious "Self or associated type requirements" diagnostic. -protocol ProtoWithAssocType { - associatedtype T = Int -} - -func freeFunction() { - struct ConformingType : ProtoWithAssocType { - typealias T = Int - - func method() -> ProtoWithAssocType {} - // expected-error@-1 {{can only be used as a generic constraint because it has Self or associated type requirements}} - } -} - // Superclass lookup archetype vs interface type mixup class Generic { struct Nested {} diff --git a/test/decl/objc_redeclaration.swift b/test/decl/objc_redeclaration.swift index 61c7ef250d376..95c46187acc4f 100644 --- a/test/decl/objc_redeclaration.swift +++ b/test/decl/objc_redeclaration.swift @@ -60,5 +60,11 @@ extension DummyClass { @objc func nsstringProperty2() -> Int { return 0 } // expected-error{{method 'nsstringProperty2()' with Objective-C selector 'nsstringProperty2' conflicts with getter for 'nsstringProperty2' with the same Objective-C selector}} } +open class MyObject: NSObject {} // expected-note{{implicit initializer 'init()' declared here}} + +extension MyObject { + public override convenience init() {} // expected-error{{initializer 'init()' with Objective-C selector 'init' conflicts with implicit initializer 'init()' with the same Objective-C selector}} +} + // FIXME: Remove -verify-ignore-unknown. // :0: error: unexpected note produced: 'nsstringProperty2' previously declared here diff --git a/test/decl/protocol/conforms/actor_derived.swift b/test/decl/protocol/conforms/actor_derived.swift new file mode 100644 index 0000000000000..0c271b264c702 --- /dev/null +++ b/test/decl/protocol/conforms/actor_derived.swift @@ -0,0 +1,30 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: concurrency + +@available(SwiftStdlib 5.5, *) +actor A1: Hashable { + nonisolated func hash(into hasher: inout Hasher) { } + static func ==(lhs: A1, rhs: A1) -> Bool { true } +} + +@available(SwiftStdlib 5.5, *) +actor A2: Hashable { + nonisolated var hashValue: Int { 0 } // expected-warning{{'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'A2' to 'Hashable' by implementing 'hash(into:)' instead}} + static func ==(lhs: A2, rhs: A2) -> Bool { true } +} + + +@available(SwiftStdlib 5.5, *) +@MainActor +class C1: Hashable { + nonisolated func hash(into hasher: inout Hasher) { } + nonisolated static func ==(lhs: C1, rhs: C1) -> Bool { true } +} + +@available(SwiftStdlib 5.5, *) +@MainActor +class C2: Hashable { + nonisolated var hashValue: Int { 0 } // expected-warning{{'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'C2' to 'Hashable' by implementing 'hash(into:)' instead}} + nonisolated static func ==(lhs: C2, rhs: C2) -> Bool { true } +} + diff --git a/test/decl/protocol/conforms/inherited.swift b/test/decl/protocol/conforms/inherited.swift index 75c4c5de1aa21..33e50e5ee418b 100644 --- a/test/decl/protocol/conforms/inherited.swift +++ b/test/decl/protocol/conforms/inherited.swift @@ -167,13 +167,13 @@ class B : A { } func testB(_ b: B) { - var _: P1 = b // expected-error{{has Self or associated type requirements}} - var _: P4 = b // expected-error{{has Self or associated type requirements}} + var _: P1 = b + var _: P4 = b var _: P5 = b var _: P6 = b - var _: P7 = b // expected-error{{has Self or associated type requirements}} - var _: P8 = b // okay - var _: P9 = b // expected-error{{has Self or associated type requirements}} + var _: P7 = b + var _: P8 = b + var _: P9 = b } // Class A5 conforms to P5 in an inheritable manner. diff --git a/test/decl/protocol/conforms/objc_async.swift b/test/decl/protocol/conforms/objc_async.swift index 48c61fe07ed35..a281e8030252e 100644 --- a/test/decl/protocol/conforms/objc_async.swift +++ b/test/decl/protocol/conforms/objc_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify -verify-ignore-unknown +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -disable-availability-checking %s -verify -verify-ignore-unknown // REQUIRES: objc_interop // REQUIRES: concurrency diff --git a/test/decl/protocol/effectful_properties.swift b/test/decl/protocol/effectful_properties.swift index e5ef0a4dc283a..c94f6d66b1f56 100644 --- a/test/decl/protocol/effectful_properties.swift +++ b/test/decl/protocol/effectful_properties.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking ///// // This test is focused on checking protocol conformance and constraint checking diff --git a/test/decl/protocol/effectful_properties_objc.swift b/test/decl/protocol/effectful_properties_objc.swift index a91e5fc56ee1b..011669d79a213 100644 --- a/test/decl/protocol/effectful_properties_objc.swift +++ b/test/decl/protocol/effectful_properties_objc.swift @@ -1,14 +1,14 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: objc_interop ///////// // check for disallowed attributes in protocols @objc protocol Tea { - var temperature : Double { get throws } // expected-error{{property with 'throws' or 'async' is not representable in Objective-C}} - subscript(_ d : Double) -> Bool { get async throws } // expected-error{{subscript with 'throws' or 'async' is not representable in Objective-C}} + var temperature : Double { get throws } // expected-error{{property with 'throws' or 'async' is not representable in Objective-C}} expected-note{{inferring '@objc' because the declaration is a member of an '@objc' protocol}} + subscript(_ d : Double) -> Bool { get async throws } // expected-error{{subscript with 'throws' or 'async' is not representable in Objective-C}} expected-note{{inferring '@objc' because the declaration is a member of an '@objc' protocol}} // NOTE: this seems counter-intuitive, but TSPL says @nonobjc applies to // members that are representable in ObjC, and this is not representable. - @nonobjc var sugar : Bool { get async } // expected-error{{property with 'throws' or 'async' is not representable in Objective-C}} -} \ No newline at end of file + @nonobjc var sugar : Bool { get async } // expected-error{{property with 'throws' or 'async' is not representable in Objective-C}} expected-note{{inferring '@objc' because the declaration is a member of an '@objc' protocol}} +} diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index a6924033a8145..6689695102b62 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -102,10 +102,10 @@ struct DoesNotConform : Up { // Circular protocols protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error {{protocol 'CircleMiddle' refines itself}} -// expected-note@-1 2 {{protocol 'CircleMiddle' declared here}} -protocol CircleStart : CircleEnd { func circle_start() } // expected-error 2 {{protocol 'CircleStart' refines itself}} +// expected-note@-1 {{protocol 'CircleMiddle' declared here}} +protocol CircleStart : CircleEnd { func circle_start() } // expected-error {{protocol 'CircleStart' refines itself}} // expected-note@-1 {{protocol 'CircleStart' declared here}} -protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 3 {{protocol 'CircleEnd' declared here}} +protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 2 {{protocol 'CircleEnd' declared here}} protocol CircleEntry : CircleTrivial { } protocol CircleTrivial : CircleTrivial { } // expected-error {{protocol 'CircleTrivial' refines itself}} @@ -263,55 +263,6 @@ struct WrongIsEqual : IsEqualComparable { // expected-error{{type 'WrongIsEqual' func isEqual(other: Int) -> Bool {} // expected-note{{candidate has non-matching type '(Int) -> Bool'}} } -//===----------------------------------------------------------------------===// -// Using values of existential type. -//===----------------------------------------------------------------------===// - -func existentialSequence(_ e: Sequence) { // expected-error{{has Self or associated type requirements}} - var x = e.makeIterator() // expected-error{{member 'makeIterator' cannot be used on value of protocol type 'Sequence'; use a generic constraint instead}} - x.next() - x.nonexistent() -} - -protocol HasSequenceAndStream { - associatedtype R : IteratorProtocol, Sequence - func getR() -> R -} - -func existentialSequenceAndStreamType(_ h: HasSequenceAndStream) { // expected-error{{has Self or associated type requirements}} - // FIXME: Crummy diagnostics. - var x = h.getR() // expected-error{{member 'getR' cannot be used on value of protocol type 'HasSequenceAndStream'; use a generic constraint instead}} - x.makeIterator() - x.next() - - x.nonexistent() -} - -//===----------------------------------------------------------------------===// -// Subscripting -//===----------------------------------------------------------------------===// -protocol IntIntSubscriptable { - subscript (i: Int) -> Int { get } -} - -protocol IntSubscriptable { - associatedtype Element - subscript (i: Int) -> Element { get } -} - -struct DictionaryIntInt { - subscript (i: Int) -> Int { - get { - return i - } - } -} - -func testSubscripting(_ iis: IntIntSubscriptable, i_s: IntSubscriptable) { // expected-error{{has Self or associated type requirements}} - var i: Int = iis[17] - var i2 = i_s[17] // expected-error{{member 'subscript' cannot be used on value of protocol type 'IntSubscriptable'; use a generic constraint instead}} -} - //===----------------------------------------------------------------------===// // Static methods //===----------------------------------------------------------------------===// @@ -467,7 +418,7 @@ protocol ShouldntCrash { // rdar://problem/18168866 protocol FirstProtocol { - // expected-warning@+1 {{'weak' should not be applied to a property declaration in a protocol and will be disallowed in future versions}} + // expected-warning@+1 {{'weak' cannot be applied to a property declaration in a protocol; this is an error in Swift 5}} weak var delegate : SecondProtocol? { get } // expected-error{{'weak' must not be applied to non-class-bound 'SecondProtocol'; consider adding a protocol conformance that has a class bound}} } @@ -488,18 +439,18 @@ func g(_ x : T) { class C3 : P1 {} // expected-error{{type 'C3' does not conform to protocol 'P1'}} func h(_ x : T) { - _ = x as P1 // expected-error{{protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements}} + _ = x as P1 } func i(_ x : T?) -> Bool { - return x is P1 // expected-error{{protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements}} + return x is P1 // FIXME: Bogus diagnostic. See SR-11920. // expected-warning@-2 {{checking a value with optional type 'T?' against dynamic type 'P1' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}} } func j(_ x : C1) -> Bool { - return x is P1 // expected-error{{protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements}} + return x is P1 } func k(_ x : C1?) -> Bool { - return x is P1 // expected-error{{protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements}} + return x is P1 } diff --git a/test/decl/protocol/protocols_with_self_or_assoc_reqs.swift b/test/decl/protocol/protocols_with_self_or_assoc_reqs.swift new file mode 100644 index 0000000000000..05d6410ead2fa --- /dev/null +++ b/test/decl/protocol/protocols_with_self_or_assoc_reqs.swift @@ -0,0 +1,523 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking + +//===----------------------------------------------------------------------===// +// Use of protocols with Self or associated type requirements +//===----------------------------------------------------------------------===// + +struct G { + class Inner {} + struct InnerG {} +} +class C {} + +protocol P1 { + associatedtype Q + + // Methods + func covariantSelfSimple() -> Self + func covariantSelfComplex(_: (Self) -> Void, + _: (Self?) -> Void, + _: (Array) -> Void, + _: (Array?>) -> Void + ) -> [String : () -> (Self, Self)] + func covariantAssocSimple() -> Q + func covariantAssocComplex(_: (Q) -> Void, + _: (Q?) -> Void, + _: (Array) -> Void, + _: (Array?>) -> Void + ) -> [String : () -> (Q, Q)] + + func contravariantSelf1(_: Self?) + func contravariantSelf2(_: () -> Self) + func contravariantSelf3(_: Array<() -> Self>) + func contravariantSelf4(_: [String : () -> Self]) + func contravariantSelf5(_: () -> (Self, Self)) + func contravariantSelf6(_: ((Self) -> Void) -> Void) + func contravariantSelf7() -> (Self) -> Void + func contravariantSelf8() -> Array<((Self) -> Void)?> + func contravariantSelf9(_: [String : (() -> Self)?]) + func contravariantSelf10() -> (Array<[String : Self??]>) -> Void + func contravariantAssoc1(_: Q?) + func contravariantAssoc2(_: () -> Q) + func contravariantAssoc3(_: Array<() -> Q>) + func contravariantAssoc4(_: [String : () -> Q]) + func contravariantAssoc5(_: () -> (Q, Q)) + func contravariantAssoc6(_: ((Q) -> Void) -> Void) + func contravariantAssoc7() -> (Q) -> Void + func contravariantAssoc8() -> Array<((Q) -> Void)?> + func contravariantAssoc9(_: [String : (() -> Q)?]) + func contravariantAssoc10() -> (Array<[String : Q??]>) -> Void + + func invariantSelf1(_: inout Self) + func invariantSelf2(_: (inout Self) -> Void) + func invariantSelf3(_: inout Array<() -> Self>) + func invariantSelf4(_: G) + func invariantSelf5() -> G + func invariantSelf6() -> G.Inner + func invariantSelf7(_: (G) -> Void) + func invariantSelf8(_: G<(Self) -> Void>) + func invariantSelf9(_: G<() -> Self>) + func invariantSelf10(_: P1 & C) + func invariantSelf11() -> G.InnerG + func invariantAssoc1(_: inout Q) + func invariantAssoc2(_: (inout Q) -> Void) + func invariantAssoc3(_: inout Array<() -> Q>) + func invariantAssoc4(_: G) + func invariantAssoc5() -> G + func invariantAssoc6() -> G.Inner + func invariantAssoc7(_: (G) -> Void) + func invariantAssoc8(_: G<(Q) -> Void>) + func invariantAssoc9(_: G<() -> Q>) + func invariantAssoc10(_: P1 & C) + func invariantAssoc11() -> G.InnerG + + // Properties + var covariantSelfPropSimple: Self { get } + var covariantSelfPropComplex: ( + _: (Self) -> Void, + _: (Self?) -> Void, + _: (Array) -> Void, + _: (Array?>) -> Void + ) -> [String : () -> (Self, Self)] { get } + var covariantAssocPropSimple: Q { get } + var covariantAssocPropComplex: ( + _: (Q) -> Void, + _: (Q?) -> Void, + _: (Array) -> Void, + _: (Array?>) -> Void + ) -> [String : () -> (Q, Q)] { get } + + var contravariantSelfProp1: (Self?) -> Void { get } + var contravariantSelfProp2: (() -> Self) -> Void { get } + var contravariantSelfProp3: (Array<() -> Self>) -> Void { get } + var contravariantSelfProp4: ([String : () -> Self]) -> Void { get } + var contravariantSelfProp5: (() -> (Self, Self)) -> Void { get } + var contravariantSelfProp6: (((Self) -> Void) -> Void) -> Void { get } + var contravariantSelfProp7: (Self) -> Void { get } + var contravariantSelfProp8: Array<((Self) -> Void)?> { get } + var contravariantSelfProp9: ([String : (() -> Self)?]) -> Void { get } + var contravariantSelfProp10: (Array<[String : Self??]>) -> Void { get } + var contravariantAssocProp1: (Q?) -> Void { get } + var contravariantAssocProp2: (() -> Q) -> Void { get } + var contravariantAssocProp3: (Array<() -> Q>) -> Void { get } + var contravariantAssocProp4: ([String : () -> Q]) -> Void { get } + var contravariantAssocProp5: (() -> (Q, Q)) -> Void { get } + var contravariantAssocProp6: (((Q) -> Void) -> Void) -> Void { get } + var contravariantAssocProp7: (Q) -> Void { get } + var contravariantAssocProp8: Array<((Q) -> Void)?> { get } + var contravariantAssocProp9: ([String : (() -> Q)?]) -> Void { get } + var contravariantAssocProp10: (Array<[String : Q??]>) -> Void { get } + + var invariantSelfProp1: (inout Self) -> Void { get } + var invariantSelfProp2: ((inout Self) -> Void) -> Void { get } + var invariantSelfProp3: (inout Array<() -> Self>) -> Void { get } + var invariantSelfProp4: (G) -> Void { get } + var invariantSelfProp5: G { get } + var invariantSelfProp6: G.Inner { get } + var invariantSelfProp7: ((G) -> Void) -> Void { get } + var invariantSelfProp8: (G<(Self) -> Void>) -> Void { get } + var invariantSelfProp9: (G<() -> Self>) -> Void { get } + var invariantSelfProp10: (P1 & C) -> Void { get } + var invariantSelfProp11: G.InnerG { get } + var invariantAssocProp1: (inout Q) -> Void { get } + var invariantAssocProp2: ((inout Q) -> Void) -> Void { get } + var invariantAssocProp3: (inout Array<() -> Q>) -> Void { get } + var invariantAssocProp4: (G) -> Void { get } + var invariantAssocProp5: G { get } + var invariantAssocProp6: G.Inner { get } + var invariantAssocProp7: ((G) -> Void) { get } + var invariantAssocProp8: (G<(Q) -> Void>) { get } + var invariantAssocProp9: (G<() -> Q>) -> Void { get } + var invariantAssocProp10: (P1 & C) -> Void { get } + var invariantAssocProp11: G.InnerG { get } + + // Subscripts + subscript(covariantSelfSubscriptSimple _: Void) -> Self { get } + subscript(covariantSelfSubscriptComplex _: (Self) -> Void, + _: (Self?) -> Void, + _: (Array) -> Void, + _: (Array?>) -> Void + ) -> [String : () -> (Self, Self)] { get } + subscript(covariantAssocSubscriptSimple _: Void) -> Q { get } + subscript(covariantAssocSubscriptComplex _: (Q) -> Void, + _: (Q?) -> Void, + _: (Array) -> Void, + _: (Array?>) -> Void + ) -> [String : () -> (Q, Q)] { get } + + subscript(contravariantSelfSubscript1 _: Self?) -> Void { get } + subscript(contravariantSelfSubscript2 _: () -> Self) -> Void { get } + subscript(contravariantSelfSubscript3 _: Array<() -> Self>) -> Void { get } + subscript(contravariantSelfSubscript4 _: [String : () -> Self]) -> Void { get } + subscript(contravariantSelfSubscript5 _: () -> (Self, Self)) -> Void { get } + subscript(contravariantSelfSubscript6 _: ((Self) -> Void) -> Void) -> Void { get } + subscript(contravariantSelfSubscript7 _: Void) -> (Self) -> Void { get } + subscript(contravariantSelfSubscript8 _: Void) -> Array<((Self) -> Void)?> { get } + subscript(contravariantSelfSubscript9 _: [String : (() -> Self)?]) -> Void { get } + subscript(contravariantSelfSubscript10 _: Void) -> (Array<[String : Self??]>) -> Void { get } + subscript(contravariantAssocSubscript1 _: Q?) -> Void { get } + subscript(contravariantAssocSubscript2 _: () -> Q) -> Void { get } + subscript(contravariantAssocSubscript3 _: Array<() -> Q>) -> Void { get } + subscript(contravariantAssocSubscript4 _: [String : () -> Q]) -> Void { get } + subscript(contravariantAssocSubscript5 _: () -> (Q, Q)) -> Void { get } + subscript(contravariantAssocSubscript6 _: ((Q) -> Void) -> Void) -> Void { get } + subscript(contravariantAssocSubscript7 _: Void) -> (Q) -> Void { get } + subscript(contravariantAssocSubscript8 _: Void) -> Array<((Q) -> Void)?> { get } + subscript(contravariantAssocSubscript9 _: [String : (() -> Q)?]) -> Void { get } + subscript(contravariantAssocSubscript10 _: Void) -> (Array<[String : Q??]>) -> Void { get } + + subscript(invariantSelfSubscript1 _: G) -> Void { get } + subscript(invariantSelfSubscript2 _: Void) -> G { get } + subscript(invariantSelfSubscript3 _: Void) -> G.Inner { get } + subscript(invariantSelfSubscript4 _: (G) -> Void) -> Void { get } + subscript(invariantSelfSubscript5 _: G<(Self) -> Void>) -> Void { get } + subscript(invariantSelfSubscript6 _: G<() -> Self>) -> Void { get } + subscript(invariantSelfSubscript7 _: P1 & C) -> Void { get } + subscript(invariantSelfSubscript8 _: Void) -> G.InnerG { get } + subscript(invariantAssocSubscript1 _: G) -> Void { get } + subscript(invariantAssocSubscript2 _: Void) -> G { get } + subscript(invariantAssocSubscript3 _: Void) -> G.Inner { get } + subscript(invariantAssocSubscript4 _: (G) -> Void) -> Void { get } + subscript(invariantAssocSubscript5 _: G<(Q) -> Void>) -> Void { get } + subscript(invariantAssocSubscript6 _: G<() -> Q>) -> Void { get } + subscript(invariantAssocSubscript7 _: P1 & C) -> Void { get } + subscript(invariantAssocSubscript8 _: Void) -> G.InnerG { get } +} +@available(macOS 10.15, *) +extension P1 { + func invariantSelf1_1() -> some P1 { self } + var invariantSelfProp1_1: some P1 { self } + subscript(invariantSelfSubscript1_1: Void) -> some P1 { self } +} + +do { + func testP1(arg: P1) { + _ = arg.covariantSelfSimple() // ok + let _: P1 = arg.covariantSelfSimple() // ok + _ = arg.covariantSelfComplex({ _ in }, { _ in }, { _ in }, { _ in }) // ok + let _: [String : () -> (P1, P1)] = arg.covariantSelfComplex( + { (x: P1) in }, + { (x: P1?) in }, + { (x: Array) in }, + { (x: Array?>) in } + ) // ok + arg.covariantAssocSimple // expected-error {{member 'covariantAssocSimple' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.covariantAssocComplex({ _ in }, { _ in }, { _ in }, { _ in }) // expected-error {{member 'covariantAssocComplex' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: expected-error@-1 {{unable to infer type of a closure parameter '_' in the current context}} + + _ = arg.covariantSelfPropSimple // ok + let _: P1 = arg.covariantSelfPropSimple // ok + _ = arg.covariantSelfPropComplex // ok + let _: ( + _: (P1) -> Void, + _: (P1?) -> Void, + _: (Array) -> Void, + _: (Array?>) -> Void + ) -> [String : () -> (P1, P1)] = arg.covariantSelfPropComplex // ok + arg.covariantAssocPropSimple // expected-error {{member 'covariantAssocPropSimple' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.covariantAssocPropComplex // expected-error {{member 'covariantAssocPropComplex' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + + _ = arg[covariantSelfSubscriptSimple: ()] // ok + let _: P1 = arg[covariantSelfSubscriptSimple: ()] // ok + _ = arg[covariantSelfSubscriptComplex: { _ in }, { _ in }, { _ in }, { _ in }] // ok + let _: [String : () -> (P1, P1)] = arg[ + covariantSelfSubscriptComplex: { (x: P1) in }, + { (x: P1?) in }, + { (x: Array) in }, + { (x: Array?>) in } + ] // ok + arg[covariantAssocSubscriptSimple: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[covariantAssocSubscriptComplex: { _ in }, { _ in }, { _ in }, { _ in }] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + + arg.contravariantSelf1(0) // expected-error {{member 'contravariantSelf1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: Silence these since we cannot make use of the member anyway. + // expected-error@-2 {{argument type 'Int' does not conform to expected type 'P1'}} + arg.contravariantSelf2(0) // expected-error {{member 'contravariantSelf2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '() -> P1'}} + arg.contravariantSelf3(0) // expected-error {{member 'contravariantSelf3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Array<() -> P1>'}} + arg.contravariantSelf4(0) // expected-error {{member 'contravariantSelf4' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '[String : () -> P1]'}} + arg.contravariantSelf5(0) // expected-error {{member 'contravariantSelf5' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '() -> (P1, P1)'}} + arg.contravariantSelf6(0) // expected-error {{member 'contravariantSelf6' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '((P1) -> Void) -> Void'}} + arg.contravariantSelf7() // expected-error {{member 'contravariantSelf7' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelf8() // expected-error {{member 'contravariantSelf8' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelf9(0) // expected-error {{member 'contravariantSelf9' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '[String : (() -> P1)?]'}} + arg.contravariantSelf10() // expected-error {{member 'contravariantSelf10' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssoc1(0) // expected-error {{member 'contravariantAssoc1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: Silence these since we cannot make use of the member anyway. + // expected-error@-2 {{cannot convert value of type 'Int' to expected argument type 'P1.Q?'}} + arg.contravariantAssoc2(0) // expected-error {{member 'contravariantAssoc2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '() -> P1.Q'}} + arg.contravariantAssoc3(0) // expected-error {{member 'contravariantAssoc3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Array<() -> P1.Q>'}} + arg.contravariantAssoc4(0) // expected-error {{member 'contravariantAssoc4' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '[String : () -> P1.Q]'}} + arg.contravariantAssoc5(0) // expected-error {{member 'contravariantAssoc5' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '() -> (P1.Q, P1.Q)'}} + arg.contravariantAssoc6(0) // expected-error {{member 'contravariantAssoc6' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '((P1.Q) -> Void) -> Void'}} + arg.contravariantAssoc7() // expected-error {{member 'contravariantAssoc7' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssoc8() // expected-error {{member 'contravariantAssoc8' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssoc9(0) // expected-error {{member 'contravariantAssoc9' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '[String : (() -> P1.Q)?]'}} + arg.contravariantAssoc10() // expected-error {{member 'contravariantAssoc10' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + + + arg.invariantSelf1(0) // expected-error {{member 'invariantSelf1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: Silence these since we cannot make use of the member anyway. + // expected-error@-2 {{argument type 'Int' does not conform to expected type 'P1'}} + if #available(macOS 10.15, *) { + arg.invariantSelf1_1() // expected-error {{member 'invariantSelf1_1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + } + arg.invariantSelf2(0) // expected-error {{member 'invariantSelf2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '(inout P1) -> Void'}} + arg.invariantSelf3(0) // expected-error {{member 'invariantSelf3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Array<() -> P1>'}} + arg.invariantSelf4(0) // expected-error {{member 'invariantSelf4' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G'}} + arg.invariantSelf5() // expected-error {{member 'invariantSelf5' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelf6() // expected-error {{member 'invariantSelf6' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelf7(0) // expected-error {{member 'invariantSelf7' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '(G) -> Void'}} + arg.invariantSelf8(0) // expected-error {{member 'invariantSelf8' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G<(P1) -> Void>'}} + arg.invariantSelf9(0) // expected-error {{member 'invariantSelf9' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G<() -> P1>'}} + arg.invariantSelf10(0) // expected-error {{member 'invariantSelf10' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'C'}} + arg.invariantSelf11() // expected-error {{member 'invariantSelf11' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssoc1(0) // expected-error {{member 'invariantAssoc1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: Silence these since we cannot make use of the member anyway. + // expected-error@-2 {{cannot convert value of type 'Int' to expected argument type 'P1.Q'}} + arg.invariantAssoc2(0) // expected-error {{member 'invariantAssoc2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '(inout P1.Q) -> Void'}} + arg.invariantAssoc3(0) // expected-error {{member 'invariantAssoc3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Array<() -> P1.Q>'}} + arg.invariantAssoc4(0) // expected-error {{member 'invariantAssoc4' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G'}} + arg.invariantAssoc5() // expected-error {{member 'invariantAssoc5' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssoc6() // expected-error {{member 'invariantAssoc6' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssoc7(0) // expected-error {{member 'invariantAssoc7' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '(G) -> Void'}} + arg.invariantAssoc8(0) // expected-error {{member 'invariantAssoc8' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G<(P1.Q) -> Void>'}} + arg.invariantAssoc9(0) // expected-error {{member 'invariantAssoc9' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G<() -> P1.Q>'}} + arg.invariantAssoc10(0) // expected-error {{member 'invariantAssoc10' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'C'}} + arg.invariantAssoc11() // expected-error {{member 'invariantAssoc11' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + + arg.contravariantSelfProp1 // expected-error {{member 'contravariantSelfProp1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp2 // expected-error {{member 'contravariantSelfProp2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp3 // expected-error {{member 'contravariantSelfProp3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp4 // expected-error {{member 'contravariantSelfProp4' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp5 // expected-error {{member 'contravariantSelfProp5' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp6 // expected-error {{member 'contravariantSelfProp6' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp7 // expected-error {{member 'contravariantSelfProp7' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp8 // expected-error {{member 'contravariantSelfProp8' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp9 // expected-error {{member 'contravariantSelfProp9' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantSelfProp10 // expected-error {{member 'contravariantSelfProp10' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp1 // expected-error {{member 'contravariantAssocProp1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp2 // expected-error {{member 'contravariantAssocProp2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp3 // expected-error {{member 'contravariantAssocProp3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp4 // expected-error {{member 'contravariantAssocProp4' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp5 // expected-error {{member 'contravariantAssocProp5' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp6 // expected-error {{member 'contravariantAssocProp6' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp7 // expected-error {{member 'contravariantAssocProp7' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp8 // expected-error {{member 'contravariantAssocProp8' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp9 // expected-error {{member 'contravariantAssocProp9' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.contravariantAssocProp10 // expected-error {{member 'contravariantAssocProp10' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + + arg.invariantSelfProp1 // expected-error {{member 'invariantSelfProp1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + if #available(macOS 10.15, *) { + arg.invariantSelfProp1_1 // expected-error {{member 'invariantSelfProp1_1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + } + arg.invariantSelfProp2 // expected-error {{member 'invariantSelfProp2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp3 // expected-error {{member 'invariantSelfProp3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp4 // expected-error {{member 'invariantSelfProp4' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp5 // expected-error {{member 'invariantSelfProp5' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp6 // expected-error {{member 'invariantSelfProp6' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp7 // expected-error {{member 'invariantSelfProp7' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp8 // expected-error {{member 'invariantSelfProp8' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp9 // expected-error {{member 'invariantSelfProp9' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp10 // expected-error {{member 'invariantSelfProp10' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantSelfProp11 // expected-error {{member 'invariantSelfProp11' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp1 // expected-error {{member 'invariantAssocProp1' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp2 // expected-error {{member 'invariantAssocProp2' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp3 // expected-error {{member 'invariantAssocProp3' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp4 // expected-error {{member 'invariantAssocProp4' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp5 // expected-error {{member 'invariantAssocProp5' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp6 // expected-error {{member 'invariantAssocProp6' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp7 // expected-error {{member 'invariantAssocProp7' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp8 // expected-error {{member 'invariantAssocProp8' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp9 // expected-error {{member 'invariantAssocProp9' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp10 // expected-error {{member 'invariantAssocProp10' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg.invariantAssocProp11 // expected-error {{member 'invariantAssocProp11' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + + arg[contravariantSelfSubscript1: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: Silence these since we cannot make use of the member anyway. + // expected-error@-2 {{argument type 'Int' does not conform to expected type 'P1'}} + arg[contravariantSelfSubscript2: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '() -> P1'}} + arg[contravariantSelfSubscript3: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Array<() -> P1>'}} + arg[contravariantSelfSubscript4: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '[String : () -> P1]'}} + arg[contravariantSelfSubscript5: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '() -> (P1, P1)'}} + arg[contravariantSelfSubscript6: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '((P1) -> Void) -> Void'}} + arg[contravariantSelfSubscript7: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[contravariantSelfSubscript8: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[contravariantSelfSubscript9: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '[String : (() -> P1)?]'}} + arg[contravariantSelfSubscript10: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[contravariantAssocSubscript1: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: Silence these since we cannot make use of the member anyway. + // expected-error@-2 {{cannot convert value of type 'Int' to expected argument type 'P1.Q?'}} + arg[contravariantAssocSubscript2: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '() -> P1.Q'}} + arg[contravariantAssocSubscript3: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Array<() -> P1.Q>'}} + arg[contravariantAssocSubscript4: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '[String : () -> P1.Q]'}} + arg[contravariantAssocSubscript5: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '() -> (P1.Q, P1.Q)'}} + arg[contravariantAssocSubscript6: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '((P1.Q) -> Void) -> Void'}} + arg[contravariantAssocSubscript7: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[contravariantAssocSubscript8: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[contravariantAssocSubscript9: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '[String : (() -> P1.Q)?]'}} + arg[contravariantAssocSubscript10: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + + arg[invariantSelfSubscript1: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: Silence these since we cannot make use of the member anyway. + // expected-error@-2 {{cannot convert value of type 'Int' to expected argument type 'G'}} + arg[invariantSelfSubscript2: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[invariantSelfSubscript3: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[invariantSelfSubscript4: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '(G) -> Void'}} + arg[invariantSelfSubscript5: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G<(P1) -> Void>'}} + arg[invariantSelfSubscript6: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G<() -> P1>'}} + arg[invariantSelfSubscript7: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'C'}} + arg[invariantSelfSubscript8: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[invariantAssocSubscript1: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // FIXME: Silence these since we cannot make use of the member anyway. + // expected-error@-2 {{cannot convert value of type 'Int' to expected argument type 'G'}} + arg[invariantAssocSubscript2: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[invariantAssocSubscript3: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + arg[invariantAssocSubscript4: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '(G) -> Void'}} + arg[invariantAssocSubscript5: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G<(P1.Q) -> Void>'}} + arg[invariantAssocSubscript6: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'G<() -> P1.Q>'}} + arg[invariantAssocSubscript7: 0] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'C'}} + arg[invariantAssocSubscript8: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1'; use a generic constraint instead}} + } +} + +protocol P1_TypeMemberOnInstanceAndViceVersa { + static func static_covariantSelfMethod() -> Self + static var static_covariantSelfProp: Self { get } + static subscript(static_covariantSelfSubscript _: Void) -> Self { get } + + static func static_invariantSelfMethod() -> G + static var static_invariantSelfProp: G { get } + static subscript(static_invariantSelfSubscript _: Void) -> G { get } + + func covariantSelfMethod() -> Self + + func invariantSelfMethod() -> G + var invariantSelfProp: G { get } + subscript(invariantSelfSubscript _: Void) -> G { get } +} +do { + // Test that invalid reference errors prevail over unsupported existential + // member accesses. + func test(protoMeta: P1_TypeMemberOnInstanceAndViceVersa.Protocol, + existMeta: P1_TypeMemberOnInstanceAndViceVersa.Type, + instance: P1_TypeMemberOnInstanceAndViceVersa) { + // P1_TypeMemberOnInstanceAndViceVersa.Protocol + protoMeta.static_invariantSelfMethod() // expected-error {{static member 'static_invariantSelfMethod' cannot be used on protocol metatype 'P1_TypeMemberOnInstanceAndViceVersa.Protocol'}} + protoMeta.static_invariantSelfProp // expected-error {{static member 'static_invariantSelfProp' cannot be used on protocol metatype 'P1_TypeMemberOnInstanceAndViceVersa.Protocol'}} + protoMeta[static_invariantSelfSubscript: ()] // expected-error {{static member 'subscript' cannot be used on protocol metatype 'P1_TypeMemberOnInstanceAndViceVersa.Protocol'}} + _ = protoMeta.covariantSelfMethod // ok + protoMeta.invariantSelfMethod // expected-error {{member 'invariantSelfMethod' cannot be used on value of protocol type 'P1_TypeMemberOnInstanceAndViceVersa.Protocol'; use a generic constraint instead}} + protoMeta.invariantSelfProp // expected-error {{instance member 'invariantSelfProp' cannot be used on type 'P1_TypeMemberOnInstanceAndViceVersa'}} + protoMeta[invariantSelfSubscript: ()] // expected-error {{instance member 'subscript' cannot be used on type 'P1_TypeMemberOnInstanceAndViceVersa'}} + + // P1_TypeMemberOnInstanceAndViceVersa.Type + _ = existMeta.static_covariantSelfMethod // ok + _ = existMeta.static_covariantSelfProp // ok + _ = existMeta[static_covariantSelfSubscript: ()] // ok + existMeta.static_invariantSelfMethod // expected-error {{member 'static_invariantSelfMethod' cannot be used on value of protocol type 'P1_TypeMemberOnInstanceAndViceVersa.Type'; use a generic constraint instead}} + existMeta.static_invariantSelfProp // expected-error {{member 'static_invariantSelfProp' cannot be used on value of protocol type 'P1_TypeMemberOnInstanceAndViceVersa.Type'; use a generic constraint instead}} + existMeta[static_invariantSelfSubscript: ()] // expected-error {{member 'subscript' cannot be used on value of protocol type 'P1_TypeMemberOnInstanceAndViceVersa.Type'; use a generic constraint instead}} + existMeta.invariantSelfMethod // expected-error {{instance member 'invariantSelfMethod' cannot be used on type 'P1_TypeMemberOnInstanceAndViceVersa'}} + existMeta.invariantSelfProp // expected-error {{instance member 'invariantSelfProp' cannot be used on type 'P1_TypeMemberOnInstanceAndViceVersa'}} + existMeta[invariantSelfSubscript: ()] // expected-error {{instance member 'subscript' cannot be used on type 'P1_TypeMemberOnInstanceAndViceVersa'}} + + // P1_TypeMemberOnInstanceAndViceVersa + instance.static_invariantSelfMethod // expected-error {{static member 'static_invariantSelfMethod' cannot be used on instance of type 'P1_TypeMemberOnInstanceAndViceVersa'}} + instance.static_invariantSelfProp // expected-error {{static member 'static_invariantSelfProp' cannot be used on instance of type 'P1_TypeMemberOnInstanceAndViceVersa'}} + instance[static_invariantSelfSubscript: ()] // expected-error {{static member 'subscript' cannot be used on instance of type 'P1_TypeMemberOnInstanceAndViceVersa'}} + } +} + +// Settable storage members with a 'Self' result type may not be used with an +// existential base. +protocol P2 { + subscript() -> Self { get set } + + var prop: Self { get set } +} +func takesP2(p2: P2) { + _ = p2[] + // expected-error@-1{{member 'subscript' cannot be used on value of protocol type 'P2'; use a generic constraint instead}} + _ = p2.prop + // expected-error@-1{{member 'prop' cannot be used on value of protocol type 'P2'; use a generic constraint instead}} +} + +protocol MiscTestsProto { + associatedtype Assoc + func runce(_: A) + func spoon(_: Self) + + associatedtype R : IteratorProtocol, Sequence + func getR() -> R + + subscript(intToAssoc _: Int) -> Assoc { get } + subscript(intToInt _: Int) -> Int { get } +} +do { + func miscTests(_ arg: MiscTestsProto) { // ok + arg.runce(5) + + do { + // FIXME: Crummy diagnostics. + var x = arg.getR() // expected-error{{member 'getR' cannot be used on value of protocol type 'MiscTestsProto'; use a generic constraint instead}} + x.makeIterator() + x.next() + x.nonexistent() + } + + var _: Int = arg[intToInt: 17] + _ = arg[intToAssoc: 17] // expected-error{{member 'subscript' cannot be used on value of protocol type 'MiscTestsProto'; use a generic constraint instead}} + } + + func existentialSequence(_ e: Sequence) { + var x = e.makeIterator() // expected-error{{member 'makeIterator' cannot be used on value of protocol type 'Sequence'; use a generic constraint instead}} + x.next() + x.nonexistent() + } +} diff --git a/test/decl/protocol/protocols_with_self_or_assoc_reqs_executable.swift b/test/decl/protocol/protocols_with_self_or_assoc_reqs_executable.swift new file mode 100644 index 0000000000000..94ebc2f2eb6ff --- /dev/null +++ b/test/decl/protocol/protocols_with_self_or_assoc_reqs_executable.swift @@ -0,0 +1,113 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest + +let Tests = TestSuite(#file) + +protocol P { + associatedtype Assoc = Self + + func getString() -> String + + func covariantSelfSimple() -> Self + func covariantSelfArray() -> Array + func covariantSelfDictionary() -> [String : Self] + func covariantSelfClosure(_: (Self) -> Void) + + var covariantSelfPropSimple: Self { get } + var covariantSelfPropArray: Array { get } + var covariantSelfPropDictionary: [String : Self] { get } + var covariantSelfPropClosure: ((Self) -> Void) -> Void { get } + + subscript(covariantSelfSubscriptSimple _: Void) -> Self { get } + subscript(covariantSelfSubscriptArray _: Void) -> Array { get } + subscript(covariantSelfSubscriptDictionary _: Void) -> [String : Self] { get } + subscript(covariantSelfSubscriptClosure _: (Self) -> Void) -> Void { get } +} +extension P { + func covariantSelfSimple() -> Self { self } + func covariantSelfArray() -> Array { [self] } + func covariantSelfDictionary() -> [String : Self] { [#file : self] } + func covariantSelfClosure(_ arg: (Self) -> Void) { arg(self) } + + var covariantSelfPropSimple: Self { self } + var covariantSelfPropArray: Array { [self] } + var covariantSelfPropDictionary: [String : Self] { [#file : self] } + var covariantSelfPropClosure: ((Self) -> Void) -> Void { { $0(self) } } + + subscript(covariantSelfSubscriptSimple _: Void) -> Self { self } + subscript(covariantSelfSubscriptArray _: Void) -> Array { [self] } + subscript(covariantSelfSubscriptDictionary _: Void) -> [String : Self] { [#file : self] } + subscript(covariantSelfSubscriptClosure arg: (Self) -> Void) -> Void { arg(self) } +} + +Tests.test("Basic") { + let collection: Collection = [0, 0, 0] + + expectEqual(3, collection.count) +} + +// FIXME: Teach the devirtualizer how to handle calls to requirements with covariant `Self` nested +// inside known-covariant stdlib types such as an array or dictionary. +@_optimize(none) +func convariantSelfErasureTest() { + struct S: P { + static let str = "Success" + func getString() -> String { Self.str } + } + + let p: P = S() + + // Partial Application + do { + let covariantSelfSimplePartialApp = p.covariantSelfSimple + let covariantSelfArrayPartialApp = p.covariantSelfArray + let covariantSelfDictionaryPartialApp = p.covariantSelfDictionary + let covariantSelfClosurePartialApp = p.covariantSelfClosure + + expectEqual(S.str, covariantSelfSimplePartialApp().getString()) + expectEqual(S.str, covariantSelfArrayPartialApp().first.unsafelyUnwrapped.getString()) + expectEqual(S.str, covariantSelfDictionaryPartialApp()[#file].unsafelyUnwrapped.getString()) + covariantSelfClosurePartialApp { expectEqual(S.str, $0.getString()) } + } + + // Instance method reference on metatype + do { + let covariantSelfSimpleRef = P.covariantSelfSimple + let covariantSelfArrayRef = P.covariantSelfArray + let covariantSelfDictionaryRef = P.covariantSelfDictionary + let covariantSelfClosureRef = P.covariantSelfClosure + + expectEqual(S.str, covariantSelfSimpleRef(p)().getString()) + expectEqual(S.str, covariantSelfArrayRef(p)().first.unsafelyUnwrapped.getString()) + expectEqual(S.str, covariantSelfDictionaryRef(p)()[#file].unsafelyUnwrapped.getString()) + covariantSelfClosureRef(p)({ expectEqual(S.str, $0.getString()) }) + } + + // Regular calls + expectEqual(S.str, p.covariantSelfSimple().getString()) + expectEqual(S.str, p.covariantSelfArray().first.unsafelyUnwrapped.getString()) + expectEqual(S.str, p.covariantSelfDictionary()[#file].unsafelyUnwrapped.getString()) + p.covariantSelfClosure { expectEqual(S.str, $0.getString()) } + + expectEqual(S.str, p.covariantSelfPropSimple.getString()) + expectEqual(S.str, p.covariantSelfPropArray.first.unsafelyUnwrapped.getString()) + expectEqual(S.str, p.covariantSelfPropDictionary[#file].unsafelyUnwrapped.getString()) + p.covariantSelfPropClosure { expectEqual(S.str, $0.getString()) } + + expectEqual(S.str, p[covariantSelfSubscriptSimple: ()].getString()) + expectEqual(S.str, p[covariantSelfSubscriptArray: ()].first.unsafelyUnwrapped.getString()) + expectEqual(S.str, p[covariantSelfSubscriptDictionary: ()][#file].unsafelyUnwrapped.getString()) + p[covariantSelfSubscriptClosure: { expectEqual(S.str, $0.getString()) }] + + expectEqual(S.str, (S() as P).getString()) + + expectEqual(true, p is P) + expectEqual(true, S() is P) +} + + +Tests.test("Covariant 'Self' erasure", convariantSelfErasureTest) + +runAllTests() diff --git a/test/decl/protocol/recursive_requirement.swift b/test/decl/protocol/recursive_requirement.swift index fd6ee93d412ae..c99dadbb6b4bd 100644 --- a/test/decl/protocol/recursive_requirement.swift +++ b/test/decl/protocol/recursive_requirement.swift @@ -91,7 +91,7 @@ protocol AsExistentialB { } protocol AsExistentialAssocTypeA { - var delegate : AsExistentialAssocTypeB? { get } // expected-error {{protocol 'AsExistentialAssocTypeB' can only be used as a generic constraint because it has Self or associated type requirements}} + var delegate : AsExistentialAssocTypeB? { get } } protocol AsExistentialAssocTypeB { func aMethod(_ object : AsExistentialAssocTypeA) @@ -103,7 +103,7 @@ protocol AsExistentialAssocTypeAgainA { associatedtype Bar } protocol AsExistentialAssocTypeAgainB { - func aMethod(_ object : AsExistentialAssocTypeAgainA) // expected-error {{protocol 'AsExistentialAssocTypeAgainA' can only be used as a generic constraint because it has Self or associated type requirements}} + func aMethod(_ object : AsExistentialAssocTypeAgainA) } // SR-547 @@ -124,5 +124,3 @@ protocol B { func observeChangeOfProperty(_ property: BC, observable: BA) } - - diff --git a/test/decl/protocol/req/associated_type_inference_proto_ext.swift b/test/decl/protocol/req/associated_type_inference_proto_ext.swift new file mode 100644 index 0000000000000..24ecd49df56dc --- /dev/null +++ b/test/decl/protocol/req/associated_type_inference_proto_ext.swift @@ -0,0 +1,30 @@ +// RUN: %target-typecheck-verify-swift + +struct Pair { + var a: A + var b: B +} + +extension Pair: Codable where A: Codable, B: Codable {} + +extension Pair: Collection where A == B { +// expected-error@-1 {{conditional conformance of type 'Pair' to protocol 'Collection' does not imply conformance to inherited protocol 'Sequence'}} +// expected-note@-2 {{did you mean to explicitly state the conformance like 'extension Pair: Sequence where ...'?}} + typealias Element = A + + var startIndex: Int { return 0 } + var endIndex: Int { return 2 } + + subscript(index: Int) -> Element { + switch index { + case 0: return self.a + case 1: return self.b + default: fatalError("Index out of bounds.") + } + } + + func index(after i: Int) -> Int { + precondition(i < endIndex, "Can't advance beyond endIndex") + return i + 1 + } +} diff --git a/test/decl/protocol/req/dynamic_self.swift b/test/decl/protocol/req/dynamic_self.swift index ef4f33a100b1d..758a301ef6f22 100644 --- a/test/decl/protocol/req/dynamic_self.swift +++ b/test/decl/protocol/req/dynamic_self.swift @@ -86,39 +86,3 @@ enum EError : P { // expected-error{{type 'EError' does not conform to protocol subscript() -> Int { 0 } // expected-note{{candidate has non-matching type '() -> Int'}} func f() -> Int { 0 } // expected-note{{candidate has non-matching type '() -> Int'}} } - - -// Settable storage declaration requirements with a 'Self' result type may not -// be used with an existential base. -protocol P2 { - subscript() -> Self { get set } -} -protocol P3 { - var prop: Self { get set } -} -protocol P4 { - subscript() -> T where T.Element == Self { get set } -} -func takesP2P3P4(p2: P2, p3: P3, p4: P4) { } -// expected-error@-1{{protocol 'P2' can only be used as a generic constraint because it has Self or associated type requirements}} -// expected-error@-2{{protocol 'P3' can only be used as a generic constraint because it has Self or associated type requirements}} - -protocol P5 { -} -extension P5 { - var prop: Self { - get { self } - set { } - } - - subscript() -> Self { - get { self } - set { } - } -} -func takesP5(p5: P5) { - _ = p5[] - // expected-error@-1{{member 'subscript' cannot be used on value of protocol type 'P5'; use a generic constraint instead}} - _ = p5.prop - // expected-error@-1{{member 'prop' cannot be used on value of protocol type 'P5'; use a generic constraint instead}} -} diff --git a/test/decl/protocol/req/recursion.swift b/test/decl/protocol/req/recursion.swift index f9d9761ee5077..8d5e7c45db9bd 100644 --- a/test/decl/protocol/req/recursion.swift +++ b/test/decl/protocol/req/recursion.swift @@ -49,12 +49,13 @@ public struct S where A.T == S { // expected-error {{circular reference // expected-note@-3 {{while resolving type 'S'}} func f(a: A.T) { g(a: id(t: a)) // `a` has error type which is diagnosed as circular reference + // expected-error@-1 {{conflicting arguments to generic parameter 'T' ('A.T' (associated type of protocol 'P') vs. 'S')}} _ = A.T.self } func g(a: S) { f(a: id(t: a)) - // expected-error@-1 {{cannot convert value of type 'S' to expected argument type 'A.T'}} + // expected-error@-1 {{conflicting arguments to generic parameter 'T' ('S' vs. 'A.T' (associated type of protocol 'P'))}} _ = S.self } diff --git a/test/decl/protocol/req/unavailable.swift b/test/decl/protocol/req/unavailable.swift index f30f66a9778a5..7d116b0c758a1 100644 --- a/test/decl/protocol/req/unavailable.swift +++ b/test/decl/protocol/req/unavailable.swift @@ -40,3 +40,24 @@ struct ConformsToP2 { } extension ConformsToP2: P {} // expected-error{{type 'ConformsToP2' does not conform to protocol 'P'}} // expected-error@-1 {{unavailable instance method 'foo(bar:)' was used to satisfy a requirement of protocol 'P'}} + + +// Include message string from @available attribute if provided +protocol Unavail { + associatedtype T + func req() // expected-note {{requirement 'req()' declared here}} +} +extension Unavail { + func req() {} +} +extension Unavail where T == Self { + @available(*, unavailable, message: "write it yourself") func req() {} // expected-note {{'req()' declared here}} +} + +struct NonSelfT: Unavail { + typealias T = Int +} +struct SelfT: Unavail { // expected-error {{type 'SelfT' does not conform to protocol 'Unavail'}} + // expected-error@-1 {{unavailable instance method 'req()' was used to satisfy a requirement of protocol 'Unavail': write it yourself}} + typealias T = SelfT +} diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift index 3c5666995275c..2b23c880b23ba 100644 --- a/test/decl/protocol/special/Actor.swift +++ b/test/decl/protocol/special/Actor.swift @@ -1,32 +1,32 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift // REQUIRES: concurrency -// Synthesis of for actores. +// Synthesis of conformances for actors. -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor A1 { var x: Int = 17 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor A2: Actor { var x: Int = 17 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor A3: Actor { var x: Int = 17 } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor A4: A1 { // expected-error{{actor types do not support inheritance}} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor A5: A2 { // expected-error{{actor types do not support inheritance}} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor A6: A1, Actor { // expected-error{{redundant conformance of 'A6' to protocol 'Actor'}} // expected-note@-1{{'A6' inherits conformance to protocol 'Actor' from superclass here}} // expected-error@-2{{actor types do not support inheritance}} @@ -34,38 +34,45 @@ actor A6: A1, Actor { // expected-error{{redundant conformance of 'A6' to protoc // Explicitly satisfying the requirement. -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) actor A7 { // Okay: satisfy the requirement explicitly nonisolated func enqueue(_ job: UnownedJob) { } } // A non-actor can conform to the Actor protocol, if it does it properly. -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -class C1: Actor { // expected-error{{non-final class 'C1' cannot conform to `Sendable`; use `UnsafeSendable`}} +@available(SwiftStdlib 5.5, *) +class C1: Actor { + // expected-error@-1{{non-actor type 'C1' cannot conform to the 'Actor' protocol}} + // expected-error@-2{{non-final class 'C1' cannot conform to `Sendable`; use `@unchecked Sendable`}} nonisolated var unownedExecutor: UnownedSerialExecutor { fatalError("") } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -class C2: Actor { // expected-error{{non-final class 'C2' cannot conform to `Sendable`; use `UnsafeSendable`}} +@available(SwiftStdlib 5.5, *) +class C2: Actor { + // expected-error@-1{{non-actor type 'C2' cannot conform to the 'Actor' protocol}} + // expected-error@-2{{non-final class 'C2' cannot conform to `Sendable`; use `@unchecked Sendable`}} // FIXME: this should be an isolation violation var unownedExecutor: UnownedSerialExecutor { fatalError("") } } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) -class C3: Actor { // expected-error{{non-final class 'C3' cannot conform to `Sendable`; use `UnsafeSendable`}} expected-error {{type 'C3' does not conform to protocol 'Actor'}} +@available(SwiftStdlib 5.5, *) +class C3: Actor { + // expected-error@-1{{type 'C3' does not conform to protocol 'Actor'}} + // expected-error@-2{{non-actor type 'C3' cannot conform to the 'Actor' protocol}} + // expected-error@-3{{non-final class 'C3' cannot conform to `Sendable`; use `@unchecked Sendable`}} nonisolated func enqueue(_ job: UnownedJob) { } } // Make sure the conformances actually happen. -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func acceptActor(_: T.Type) { } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func testConformance() { acceptActor(A1.self) acceptActor(A2.self) diff --git a/test/decl/protocol/special/DistributedActor.swift b/test/decl/protocol/special/DistributedActor.swift new file mode 100644 index 0000000000000..3db8b1582e111 --- /dev/null +++ b/test/decl/protocol/special/DistributedActor.swift @@ -0,0 +1,54 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-distributed -verify-ignore-unknown +// REQUIRES: concurrency +// REQUIRES: distributed + +import _Distributed + +@available(SwiftStdlib 5.5, *) +distributed actor D1 { + var x: Int = 17 +} + +@available(SwiftStdlib 5.5, *) +distributed actor D2 { + // expected-error@-1{{actor 'D2' has no initializers}} + let actorTransport: String + // expected-error@-1{{property 'actorTransport' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} + // expected-error@-2{{invalid redeclaration of synthesized implementation for protocol requirement 'actorTransport'}} + // expected-note@-3{{stored property 'actorTransport' without initial value prevents synthesized initializers}} +} + +@available(SwiftStdlib 5.5, *) +distributed actor D3 { + var id: Int { 0 } + // expected-error@-1{{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} + // expected-error@-2{{invalid redeclaration of synthesized implementation for protocol requirement 'id'}} +} + +@available(SwiftStdlib 5.5, *) +distributed actor D4 { + // expected-error@-1{{actor 'D4' has no initializers}} + let actorTransport: String + // expected-error@-1{{invalid redeclaration of synthesized implementation for protocol requirement 'actorTransport'}} + // expected-error@-2{{property 'actorTransport' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} + // expected-note@-3{{stored property 'actorTransport' without initial value prevents synthesized initializers}} + let id: AnyActorIdentity + // expected-error@-1{{actor-isolated property 'id' cannot be used to satisfy a protocol requirement}} + // expected-error@-2{{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} + // expected-error@-3{{actor-isolated property 'id' cannot be used to satisfy a protocol requirement}} + // expected-note@-4{{stored property 'id' without initial value prevents synthesized initializers}} +} + +// ==== Tests ------------------------------------------------------------------ + +// Make sure the conformances have been added implicitly. +@available(SwiftStdlib 5.5, *) +func acceptDistributedActor(_: Act.Type) { } +@available(SwiftStdlib 5.5, *) +func acceptAnyActor(_: Act.Type) { } + +@available(SwiftStdlib 5.5, *) +func testConformance() { + acceptDistributedActor(D1.self) + acceptAnyActor(D1.self) +} diff --git a/test/decl/protocol/special/Sendable.swift b/test/decl/protocol/special/Sendable.swift new file mode 100644 index 0000000000000..3fb603a09be32 --- /dev/null +++ b/test/decl/protocol/special/Sendable.swift @@ -0,0 +1,31 @@ +// RUN: %target-typecheck-verify-swift -warn-concurrency + +func acceptSendable(_: T) { } + +class NotSendable { } // expected-note 2{{class 'NotSendable' does not conform to the `Sendable` protocol}} + +func testSendableBuiltinConformances( + i: Int, ns: NotSendable, sf: @escaping @Sendable () -> Void, + nsf: @escaping () -> Void, mt: NotSendable.Type, + cf: @escaping @convention(c) () -> Void, + funSendable: [(Int, @Sendable () -> Void, NotSendable.Type)?], + funNotSendable: [(Int, () -> Void, NotSendable.Type)?] +) { + acceptSendable(()) + acceptSendable(mt) + acceptSendable(sf) + acceptSendable(cf) + acceptSendable((i, sf, mt)) + acceptSendable((i, label: sf)) + acceptSendable(funSendable) + + // Complaints about missing Sendable conformances + acceptSendable((i, ns)) // expected-warning{{type 'NotSendable' does not conform to the 'Sendable' protocol}} + acceptSendable(nsf) // expected-warning{{type '() -> Void' does not conform to the 'Sendable' protocol}} + // expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}} + acceptSendable((nsf, i)) // expected-warning{{type '() -> Void' does not conform to the 'Sendable' protocol}} + // expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}} + acceptSendable(funNotSendable) // expected-warning{{type '() -> Void' does not conform to the 'Sendable' protocol}} + // expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}} + acceptSendable((i, ns)) // expected-warning{{type 'NotSendable' does not conform to the 'Sendable' protocol}} +} diff --git a/test/decl/protocol/special/Sendable_swift6.swift b/test/decl/protocol/special/Sendable_swift6.swift new file mode 100644 index 0000000000000..c7f9a8289d45d --- /dev/null +++ b/test/decl/protocol/special/Sendable_swift6.swift @@ -0,0 +1,33 @@ +// RUN: %target-typecheck-verify-swift -warn-concurrency -swift-version 6 + +// REQUIRES: asserts + +func acceptSendable(_: T) { } + +class NotSendable { } // expected-note 2{{class 'NotSendable' does not conform to the `Sendable` protocol}} + +func testSendableBuiltinConformances( + i: Int, ns: NotSendable, sf: @escaping @Sendable () -> Void, + nsf: @escaping () -> Void, mt: NotSendable.Type, + cf: @escaping @convention(c) () -> Void, + funSendable: [(Int, @Sendable () -> Void, NotSendable.Type)?], + funNotSendable: [(Int, () -> Void, NotSendable.Type)?] +) { + acceptSendable(()) + acceptSendable(mt) + acceptSendable(sf) + acceptSendable(cf) + acceptSendable((i, sf, mt)) + acceptSendable((i, label: sf)) + acceptSendable(funSendable) + + // Complaints about missing Sendable conformances + acceptSendable((i, ns)) // expected-error{{type 'NotSendable' does not conform to the 'Sendable' protocol}} + acceptSendable(nsf) // expected-error{{type '() -> Void' does not conform to the 'Sendable' protocol}} + // expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}} + acceptSendable((nsf, i)) // expected-error{{type '() -> Void' does not conform to the 'Sendable' protocol}} + // expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}} + acceptSendable(funNotSendable) // expected-error{{type '() -> Void' does not conform to the 'Sendable' protocol}} + // expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}} + acceptSendable((i, ns)) // expected-error{{type 'NotSendable' does not conform to the 'Sendable' protocol}} +} diff --git a/test/decl/protocol/special/coding/enum_codable_exclude_element.swift b/test/decl/protocol/special/coding/enum_codable_exclude_element.swift new file mode 100644 index 0000000000000..8dc748792d842 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_codable_exclude_element.swift @@ -0,0 +1,10 @@ +// RUN: %target-typecheck-verify-swift + +enum EnumWithExcludedElement : Codable { + case x + case y + + enum CodingKeys: CodingKey { + case x + } +} diff --git a/test/decl/protocol/special/coding/enum_coding_key.swift b/test/decl/protocol/special/coding/enum_coding_key.swift index 2671708804382..bcdd5a642feee 100644 --- a/test/decl/protocol/special/coding/enum_coding_key.swift +++ b/test/decl/protocol/special/coding/enum_coding_key.swift @@ -43,7 +43,7 @@ enum Int8Key : Int8, CodingKey { // expected-error {{type 'Int8Key' does not con struct StructKey : CodingKey { // expected-error {{type 'StructKey' does not conform to protocol 'CodingKey'}} } -// Classes conforming to CodingKey should not get implict derived conformance. +// Classes conforming to CodingKey should not get implicit derived conformance. class ClassKey : CodingKey { //expected-error {{type 'ClassKey' does not conform to protocol 'CodingKey'}} } diff --git a/test/decl/typealias/fully_constrained.swift b/test/decl/typealias/fully_constrained.swift new file mode 100644 index 0000000000000..2f84c36a78ceb --- /dev/null +++ b/test/decl/typealias/fully_constrained.swift @@ -0,0 +1,64 @@ +// RUN: %target-typecheck-verify-swift + +struct OtherGeneric {} + +struct Generic { + // FIXME: Should work with 'T' as well + typealias NonGeneric = Int where T == Int + + typealias Unbound = OtherGeneric where T == Int + typealias Generic = OtherGeneric where T == Int +} + +extension Generic where T == Int { + // FIXME: Should work with 'T' as well + typealias NonGenericInExtension = Int + + typealias UnboundInExtension = OtherGeneric + typealias GenericInExtension = OtherGeneric +} + +func use(_: Generic.NonGeneric, + _: Generic.Unbound, + _: Generic.Generic, + _: Generic.NonGenericInExtension, + _: Generic.UnboundInExtension, + _: Generic.GenericInExtension) { + + // FIXME: Get these working too +#if false + let _ = Generic.NonGeneric.self + let _ = Generic.Unbound.self + let _ = Generic.Generic.self + + let _ = Generic.NonGenericInExtension.self + let _ = Generic.UnboundInExtension.self + let _ = Generic.GenericInExtension.self + + let _: Generic.NonGeneric = 123 + let _: Generic.NonGenericInExtension = 123 +#endif + + let _: Generic.Unbound = OtherGeneric() + let _: Generic.Generic = OtherGeneric() + + let _: Generic.UnboundInExtension = OtherGeneric() + let _: Generic.GenericInExtension = OtherGeneric() +} + +struct Use { + let a1: Generic.NonGeneric + let b1: Generic.Unbound + let c1: Generic.Generic + let a2: Generic.NonGenericInExtension + let b2: Generic.UnboundInExtension + let c2: Generic.GenericInExtension +} + +extension Generic.NonGeneric {} +extension Generic.Unbound {} +extension Generic.Generic {} + +extension Generic.NonGenericInExtension {} +extension Generic.UnboundInExtension {} +extension Generic.GenericInExtension {} diff --git a/test/decl/typealias/on_constrained_protocol.swift b/test/decl/typealias/on_constrained_protocol.swift new file mode 100644 index 0000000000000..a2e8010ae1f5b --- /dev/null +++ b/test/decl/typealias/on_constrained_protocol.swift @@ -0,0 +1,54 @@ +// RUN: %target-typecheck-verify-swift + +// typealias on constrained extension + +protocol ConstrainedTypealias { + associatedtype MyAssocType +} +extension ConstrainedTypealias where MyAssocType == String { // expected-note {{requirement specified as 'Self.MyAssocType' == 'String' [with Self = Self]}} (from useConstrainedTypealiasInExtension) + typealias Content = String +} +extension ConstrainedTypealias where MyAssocType == Int { + func useConstrainedTypealiasInExtension() -> Content {} // expected-error {{'Self.Content' (aka 'String') requires the types 'Int' and 'String' be equivalent}} +} +func useTypealiasOnConstrainedExtension() -> ConstrainedTypealias.Content {} + +// define different typealiases on differently constrained extensions + +protocol DoubleOverloadedTypealias { + associatedtype MyAssocType +} +extension DoubleOverloadedTypealias where MyAssocType == String { // expected-note {{requirement specified as 'Self.MyAssocType' == 'String' [with Self = Self]}} (from useDoubleOverloadedTypealiasInExtension) + typealias Content = String // expected-note {{found candidate with type 'String'}} (from useDoubleOverloadedTypealias) +} +extension DoubleOverloadedTypealias where MyAssocType == Int { + typealias Content = Int // expected-note {{found candidate with type 'Int'}} (from useDoubleOverloadedTypealias) + func useDoubleOverloadedTypealiasInExtension() -> Content {} // expected-error {{'Self.Content' (aka 'String') requires the types 'Int' and 'String' be equivalent}} +} +func useDoubleOverloadedTypealias() -> DoubleOverloadedTypealias.Content {} // expected-error {{ambiguous type name 'Content' in 'DoubleOverloadedTypealias'}} + +// define the same typealias on differently constrained extensions + +protocol DoubleOverloadedSameTypealias { + associatedtype MyAssocType +} +extension DoubleOverloadedSameTypealias where MyAssocType == String { // expected-note {{requirement specified as 'Self.MyAssocType' == 'String' [with Self = Self]}} (from useDoubleOverloadedSameTypealiasInExtension) + typealias Content = Int +} +extension DoubleOverloadedSameTypealias where MyAssocType == Int { + typealias Content = Int + func useDoubleOverloadedSameTypealiasInExtension() -> Content {} // expected-error {{'Self.Content' (aka 'Int') requires the types 'Int' and 'String' be equivalent}} +} +func useDoubleOverloadedSameTypealias() -> DoubleOverloadedSameTypealias.Content {} + +// Overload associatedtype with typealias (SR-8274) + +protocol MarkerProtocol {} +protocol ProtocolWithAssoctype { + associatedtype MyAssocType // expected-note {{found this candidate}} (from useAssocTypeInExtension) expected-note {{found candidate with type 'Self.MyAssocType'}} (from useAssocTypeOutsideExtension) +} +extension ProtocolWithAssoctype where Self: MarkerProtocol { + typealias MyAssocType = Int // expected-note {{found this candidate}} (from useAssocTypeInExtension) expected-note {{found candidate with type 'Int'}} (from useAssocTypeOutsideExtension) + func useAssocTypeInExtension() -> MyAssocType {} // expected-error {{'MyAssocType' is ambiguous for type lookup in this context}} +} +func useAssocTypeOutsideExtension() -> ProtocolWithAssoctype.MyAssocType {} // expected-error {{ambiguous type name 'MyAssocType' in 'ProtocolWithAssoctype'}} diff --git a/test/decl/var/spawn_let.swift b/test/decl/var/async_let.swift similarity index 55% rename from test/decl/var/spawn_let.swift rename to test/decl/var/async_let.swift index 3365d76c6de16..f1cd692ebe1b3 100644 --- a/test/decl/var/spawn_let.swift +++ b/test/decl/var/async_let.swift @@ -1,25 +1,25 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency func test() async { - spawn let x = 1 // okay + async let x = 1 // okay _ = await x } struct X { - spawn let x = 1 // expected-error{{'spawn let' can only be used on local declarations}} + async let x = 1 // expected-error{{'async let' can only be used on local declarations}} // FIXME: expected-error@-1{{'async' call cannot occur in a property initializer}} } func testAsyncFunc() async { - spawn let (z1, z2) = (2, 3) - spawn let (_, _) = (2, 3) - spawn let x2 = 1 + async let (z1, z2) = (2, 3) + async let (_, _) = (2, 3) + async let x2 = 1 - spawn var x = 17 // expected-error{{'spawn' can only be used with 'let' declarations}}{{9-12=let}} - spawn let (_, _) = (1, 2), y2 = 7 // expected-error{{'spawn let' requires at least one named variable}} - spawn let y: Int // expected-error{{'spawn let' binding requires an initializer expression}} + async var x = 17 // expected-error{{'async' can only be used with 'let' declarations}}{{9-12=let}} + async let (_, _) = (1, 2), y2 = 7 // expected-error{{'async let' requires at least one named variable}} + async let y: Int // expected-error{{'async let' binding requires an initializer expression}} _ = await x _ = y _ = await z1 @@ -34,12 +34,12 @@ func chopVegetables() async throws -> [String] { [] } func marinateMeat() async -> String { "MEAT" } func cook() async throws { - spawn let veggies = try await chopVegetables(), meat = await marinateMeat() + async let veggies = try await chopVegetables(), meat = await marinateMeat() _ = try await veggies _ = await meat } func testInterpolation() async { - spawn let x = "\(12345)" - _ = await x + async let y = "\(12345)" + _ = await y } diff --git a/test/decl/var/default_init.swift b/test/decl/var/default_init.swift index d21ee17ad1a5c..97d06a10d163c 100644 --- a/test/decl/var/default_init.swift +++ b/test/decl/var/default_init.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -typecheck -parse-as-library %s -verify -swift-version 5 +// RUN: %target-swift-frontend -typecheck -parse-as-library %s -verify -swift-version 5 -disable-availability-checking // Default initialization of variables. @@ -51,3 +51,12 @@ func testBadDefaultInit() { _ = NotInitializableOptionalStruct() // expected-error {{missing argument for parameter 'opt' in call}} _ = NotInitializableOptionalClass() // expected-error {{'NotInitializableOptionalClass' cannot be constructed because it has no accessible initializers}} } + +// expected-error@+1{{actor 'NotInitializableActor' has no initializers}} +actor NotInitializableActor { + + // expected-note@+1{{stored property 'a' without initial value prevents synthesized initializers}} + var a: Int + // expected-note@+1{{stored property 'b' without initial value prevents synthesized initializers}} + var b: Float +} diff --git a/test/decl/var/effectful_properties_global.swift b/test/decl/var/effectful_properties_global.swift index 93473f8f42f9b..1a6c16f5e05ab 100644 --- a/test/decl/var/effectful_properties_global.swift +++ b/test/decl/var/effectful_properties_global.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking var intAsyncProp : Int { get async { 0 } diff --git a/test/decl/var/effectful_property_wrapper.swift b/test/decl/var/effectful_property_wrapper.swift index 2ed0f9e9c10be..ab2cdcd65a46a 100644 --- a/test/decl/var/effectful_property_wrapper.swift +++ b/test/decl/var/effectful_property_wrapper.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking // Currently, we don't support having property wrappers that are effectful. // Eventually we'd like to add this. @@ -38,4 +38,4 @@ struct S { @NeedlessIntWrapper var throwingProp : Int { get throws { 0 } } -} \ No newline at end of file +} diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index f03f26be737d0..c3809a5655089 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -58,13 +58,13 @@ struct WrapperAcceptingAutoclosure { @propertyWrapper struct MissingValue { } -// expected-error@-1{{property wrapper type 'MissingValue' does not contain a non-static property named 'wrappedValue'}} {{educational-notes=property-wrapper-requirements}} +// expected-error@-1{{property wrapper type 'MissingValue' does not contain a non-static property named 'wrappedValue'}} {{educational-notes=property-wrapper-requirements}}{{25-25=var wrappedValue: <#Value#>}} @propertyWrapper struct StaticValue { static var wrappedValue: Int = 17 } -// expected-error@-3{{property wrapper type 'StaticValue' does not contain a non-static property named 'wrappedValue'}} +// expected-error@-3{{property wrapper type 'StaticValue' does not contain a non-static property named 'wrappedValue'}}{{21-21=var wrappedValue: <#Value#>}} // expected-error@+1{{'@propertyWrapper' attribute cannot be applied to this declaration}} diff --git a/test/decl/var/property_wrappers_opaque.swift b/test/decl/var/property_wrappers_opaque.swift index 966b259f318c1..85dce03ae936c 100644 --- a/test/decl/var/property_wrappers_opaque.swift +++ b/test/decl/var/property_wrappers_opaque.swift @@ -3,7 +3,7 @@ protocol P { } @propertyWrapper -struct WrapperWithDefaultInit { +struct WrapperWithDefaultInit { // expected-note3{{'T' declared as parameter to type 'WrapperWithDefaultInit'}} private var stored: T? var wrappedValue: T { @@ -18,10 +18,10 @@ struct WrapperWithDefaultInit { // FB7699647 - crash with opaque result type and property wrappers. struct FB7699647 { - @WrapperWithDefaultInit var property: some P // expected-error{{property declares an opaque return type, but cannot infer the underlying type from its initializer expression}} - @WrapperWithDefaultInit() var otherProperty: some P // expected-error{{property declares an opaque return type, but cannot infer the underlying type from its initializer expression}} + @WrapperWithDefaultInit var property: some P // expected-error{{generic parameter 'T' could not be inferred}} expected-error{{property declares an opaque return type, but cannot infer the underlying type from its initializer expression}} + @WrapperWithDefaultInit() var otherProperty: some P // expected-error{{generic parameter 'T' could not be inferred}} } struct FB7699647b { - @WrapperWithDefaultInit var property: some P // expected-error{{property declares an opaque return type, but cannot infer the underlying type from its initializer expression}} + @WrapperWithDefaultInit var property: some P // expected-error{{generic parameter 'T' could not be inferred}} expected-error{{property declares an opaque return type, but cannot infer the underlying type from its initializer expression}} } diff --git a/test/decl/var/usage.swift b/test/decl/var/usage.swift index 9f89092728c4f..b87ef4461cf6a 100644 --- a/test/decl/var/usage.swift +++ b/test/decl/var/usage.swift @@ -330,8 +330,44 @@ func test(_ a : Int?, b : Any) { if let x = b as? Int { // expected-warning {{value 'x' was defined but never used; consider replacing with boolean test}} {{6-14=}} {{16-19=is}} } - // SR-1112 + // SR-14646. Special case, turn this into an 'is' test with optional value. + let bb: Any? = 3 + if let bbb = bb as? Int {} // expected-warning {{value 'bbb' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{19-22=is}} + if let bbb = (bb) as? Int {} // expected-warning {{value 'bbb' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{21-24=is}} + + func aa() -> Any? { return 1 } + if let aaa = aa() as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{21-24=is}} + if let aaa = (aa()) as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{23-26=is}} + + func bb() -> Any { return 1 } + if let aaa = aa() as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{21-24=is}} + if let aaa = (aa()) as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{23-26=is}} + + func throwingAA() throws -> Any? { return 1 } + do { + if let aaa = try! throwingAA() as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{8-18=}} {{36-39=is}} + if let aaa = (try! throwingAA()) as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{8-18=}} {{38-41=is}} + if let aaa = try throwingAA() as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{8-18=}} {{35-38=is}} + if let aaa = (try throwingAA()) as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{8-18=}} {{37-40=is}} + } catch { } + if let aaa = try? throwingAA() as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{6-16=(}} {{41-41=) != nil}} + if let aaa = (try? throwingAA()) as? Int {} // expected-warning {{value 'aaa' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{36-39=is}} + + func throwingBB() throws -> Any { return 1 } + do { + if let bbb = try! throwingBB() as? Int {} // expected-warning {{value 'bbb' was defined but never used; consider replacing with boolean test}} {{8-18=}} {{36-39=is}} + if let bbb = (try! throwingBB()) as? Int {} // expected-warning {{value 'bbb' was defined but never used; consider replacing with boolean test}} {{8-18=}} {{38-41=is}} + if let bbb = try throwingBB() as? Int {} // expected-warning {{value 'bbb' was defined but never used; consider replacing with boolean test}} {{8-18=}} {{35-38=is}} + if let bbb = (try throwingBB()) as? Int {} // expected-warning {{value 'bbb' was defined but never used; consider replacing with boolean test}} {{8-18=}} {{37-40=is}} + } catch { } + if let bbb = try? throwingBB() as? Int {} // expected-warning {{value 'bbb' was defined but never used; consider replacing with boolean test}} {{6-16=(}} {{41-41=) != nil}} + if let bbb = (try? throwingBB()) as? Int {} // expected-warning {{value 'bbb' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{36-39=is}} + + let cc: (Any?, Any) = (1, 2) + if let (cc1, cc2) = cc as? (Int, Int) {} // expected-warning {{immutable value 'cc1' was never used; consider replacing with '_' or removing it}} expected-warning {{immutable value 'cc2' was never used; consider replacing with '_' or removing it}} + + // SR-1112 let xxx: Int? = 0 if let yyy = xxx { } // expected-warning{{with boolean test}} {{6-16=}} {{19-19= != nil}} @@ -356,7 +392,7 @@ let optionalString: String? = "check" if let string = optionalString {} // expected-warning {{value 'string' was defined but never used; consider replacing with boolean test}} {{4-17=}} {{31-31= != nil}} let optionalAny: Any? = "check" -if let string = optionalAny as? String {} // expected-warning {{value 'string' was defined but never used; consider replacing with boolean test}} {{4-17=(}} {{39-39=) != nil}} +if let string = optionalAny as? String {} // expected-warning {{value 'string' was defined but never used; consider replacing with boolean test}} {{4-17=}} {{29-32=is}} // Due to the complexities of global variable tracing, these will not generate warnings let unusedVariable = "" @@ -504,3 +540,9 @@ func testVariablesBoundInPatterns() { break } } +// Tests fix to SR-14646 +func testUselessCastWithInvalidParam(foo: Any?) -> Int { + class Foo { } + if let bar = foo as? Foo { return 42 } // expected-warning {{value 'bar' was defined but never used; consider replacing with boolean test}} {{6-16=}} {{20-23=is}} + else { return 54 } +} diff --git a/test/diagnostics/multi-module-diagnostics.swift b/test/diagnostics/multi-module-diagnostics.swift index ffe1f06c68860..175eae17d638b 100644 --- a/test/diagnostics/multi-module-diagnostics.swift +++ b/test/diagnostics/multi-module-diagnostics.swift @@ -59,6 +59,14 @@ open class SubClass: ParentClass { // CHECK-OUTOFDATE-NOT: moda.ParentClass:{{.*}}: note: // CHECK-OUTOFDATE: moda.swift:{{.*}}: note: +// Underlying file is empty, the locations are now completely invalid (ie. not +// within the buffer). Make sure there's no crash and that we fallback to using +// the generated source. +// RUN: rm %t/moda.swift +// RUN: touch %t/moda.swift +// RUN: not %target-swift-frontend -typecheck -I %t/mods -D MODB %s 2>&1 | %FileCheck -check-prefix=CHECK-EMPTY %s +// CHECK-EMPTY: moda.ParentClass:{{.*}}: note: + // The file and line from a location directive should be used whether or not it // exists - the actual source still comes from the original file, so that's what // matters in terms of whether generated code is used or not diff --git a/test/diagnostics/pretty-printed-diagnostics.swift b/test/diagnostics/pretty-printed-diagnostics.swift index 667baca978981..b110ecfa45c23 100644 --- a/test/diagnostics/pretty-printed-diagnostics.swift +++ b/test/diagnostics/pretty-printed-diagnostics.swift @@ -128,7 +128,7 @@ foo(b: // CHECK: [[#LINE-1]] | // CHECK: [[#LINE]] | let x = { () -> Result in // CHECK: | +++++++++++++++++ -// CHECK: | ^ error: unable to infer complex closure return type; add explicit type to disambiguate +// CHECK: | ^ error: cannot infer return type for closure with multiple statements; add explicit type to disambiguate // CHECK: [[#LINE+1]] | let y = 1 // CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:8 @@ -152,7 +152,7 @@ foo(b: // CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:20 // CHECK: [[#LINE-1]] | // CHECK: [[#LINE]] | let 👍👍👍 = { -// CHECK: | --> error: unable to infer complex closure return type; add explicit type to disambiguate [insert ' () -> <#Result#> in '] +// CHECK: | --> error: cannot infer return type for closure with multiple statements; add explicit type to disambiguate [insert ' () -> <#Result#> in '] // CHECK: [[#LINE+1]] | let y = 1 // CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:5 diff --git a/test/expr/capture/local_lazy.swift b/test/expr/capture/local_lazy.swift new file mode 100644 index 0000000000000..8fe4171f64bb4 --- /dev/null +++ b/test/expr/capture/local_lazy.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-frontend -emit-silgen -verify %s > /dev/null +// RUN: %target-swift-frontend -dump-ast %s | %FileCheck %s + +struct S { + func foo() -> Int { + // Make sure the decl context for the autoclosure passed to ?? is deep + // enough that it can 'see' the capture of $0 from the outer closure. + // CHECK-LABEL: (lazy_initializer_expr + // CHECK: (closure_expr + // CHECK: location={{.*}}local_lazy.swift:[[@LINE+3]] + // CHECK: (autoclosure_expr implicit + // CHECK: captures=($0 + lazy var nest: (Int) -> Int = { Optional.none ?? $0 } + return nest(1) + } +} + +extension S { + func bar() -> Int { + // CHECK-LABEL: (lazy_initializer_expr + // CHECK: (closure_expr + // CHECK: location={{.*}}local_lazy.swift:[[@LINE+3]] + // CHECK: (autoclosure_expr implicit + // CHECK: captures=($0 + lazy var nest: (Int) -> Int = { Optional.none ?? $0 } + return nest(1) + } +} + diff --git a/test/expr/closure/anonymous.swift b/test/expr/closure/anonymous.swift index 566fa0cc1bcc8..3a9caf47f7537 100644 --- a/test/expr/closure/anonymous.swift +++ b/test/expr/closure/anonymous.swift @@ -32,7 +32,7 @@ func variadic() { // FIXME: Problem here is related to multi-statement closure body not being type-checked together with // enclosing context. We could have inferred `$0` to be `[Int]` if `let` was a part of constraint system. takesVariadicGeneric({let _: [Int] = $0}) - // expected-error@-1 {{unable to infer type of a closure parameter $0 in the current context}} + // expected-error@-1 {{unable to infer type of a closure parameter '$0' in the current context}} takesVariadicIntInt({_ = $0; takesIntArray($1)}) takesVariadicIntInt({_ = $0; let _: [Int] = $1}) diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 9da2a79f6c75e..2c69f56609d9d 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -172,7 +172,7 @@ class ExplicitSelfRequiredTest { doStuff({ [unowned(unsafe) self] in x+1 }) doStuff({ [unowned self = self] in x+1 }) doStuff({ x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} - doVoidStuff({ doStuff({ x+1 })}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ doStuff({ x+1 })}) // expected-warning {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} doVoidStuff({ x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{19-19=self.}} doVoidStuff({ _ = "\(x)"}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} doVoidStuff({ [y = self] in x += 1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} @@ -403,7 +403,7 @@ Void(0) // expected-error{{argument passed to call that takes no arguments}} _ = {0} // "multi-statement closures require an explicit return type" should be an error not a note -let samples = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} +let samples = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} if (i > 10) { return true } else { return false } }() @@ -485,7 +485,7 @@ func lvalueCapture(c: GenericClass) { } // Don't expose @lvalue-ness in diagnostics. -let closure = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} +let closure = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} var helper = true return helper } @@ -544,7 +544,7 @@ class SR14120 { func test2() { doVoidStuff { [self] in doVoidStuff { - // expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}} + // expected-warning@+3 {{call to method 'operation' in closure requires explicit use of 'self'}} // expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} // expected-note@+1 {{reference 'self.' explicitly}} operation() @@ -580,7 +580,7 @@ class SR14120 { doVoidStuff { [self] in doVoidStuff { [self] in doVoidStuff { - // expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}} + // expected-warning@+3 {{call to method 'operation' in closure requires explicit use of 'self'}} // expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} // expected-note@+1 {{reference 'self.' explicitly}} operation() @@ -589,3 +589,108 @@ class SR14120 { } } } + +// SR-14678 +func call(_ : Int, _ f: () -> (T, Int)) -> (T, Int) { + f() +} + +func testSR14678() -> (Int, Int) { + call(1) { // expected-error {{cannot convert return expression of type '((), Int)' to return type '(Int, Int)'}} + (print("hello"), 0) + } +} + +func testSR14678_Optional() -> (Int, Int)? { + call(1) { // expected-error {{cannot convert return expression of type '((), Int)' to return type '(Int, Int)'}} + (print("hello"), 0) + } +} + +// SR-13239 +func callit(_ f: () -> T) -> T { + f() +} + +func callitArgs(_ : Int, _ f: () -> T) -> T { + f() +} + +func callitArgsFn(_ : Int, _ f: () -> () -> T) -> T { + f()() +} + +func callitGenericArg(_ a: T, _ f: () -> T) -> T { + f() +} + +func callitTuple(_ : Int, _ f: () -> (T, Int)) -> T { + f().0 +} + +func callitVariadic(_ fs: () -> T...) -> T { + fs.first!() +} + +func testSR13239_Tuple() -> Int { + // expected-error@+2{{conflicting arguments to generic parameter 'T' ('()' vs. 'Int')}} + // expected-note@+1:3{{generic parameter 'T' inferred as 'Int' from context}} + callitTuple(1) { // expected-note@:18{{generic parameter 'T' inferred as '()' from closure return expression}} + (print("hello"), 0) + } +} + +func testSR13239() -> Int { + // expected-error@+2{{conflicting arguments to generic parameter 'T' ('()' vs. 'Int')}} + // expected-note@+1:3{{generic parameter 'T' inferred as 'Int' from context}} + callit { // expected-note@:10{{generic parameter 'T' inferred as '()' from closure return expression}} + print("hello") + } +} + +func testSR13239_Args() -> Int { + // expected-error@+2{{conflicting arguments to generic parameter 'T' ('()' vs. 'Int')}} + // expected-note@+1:3{{generic parameter 'T' inferred as 'Int' from context}} + callitArgs(1) { // expected-note@:17{{generic parameter 'T' inferred as '()' from closure return expression}} + print("hello") + } +} + +func testSR13239_ArgsFn() -> Int { + // expected-error@+2{{conflicting arguments to generic parameter 'T' ('()' vs. 'Int')}} + // expected-note@+1:3{{generic parameter 'T' inferred as 'Int' from context}} + callitArgsFn(1) { // expected-note@:19{{generic parameter 'T' inferred as '()' from closure return expression}} + { print("hello") } + } +} + +func testSR13239MultiExpr() -> Int { + callit { + print("hello") + return print("hello") // expected-error {{cannot convert return expression of type '()' to return type 'Int'}} + } +} + +func testSR13239_GenericArg() -> Int { + // Generic argument is inferred as Int from first argument literal, so no conflict in this case. + callitGenericArg(1) { + print("hello") // expected-error {{cannot convert value of type '()' to closure result type 'Int'}} + } +} + +func testSR13239_Variadic() -> Int { + // expected-error@+2{{conflicting arguments to generic parameter 'T' ('()' vs. 'Int')}} + // expected-note@+1:3{{generic parameter 'T' inferred as 'Int' from context}} + callitVariadic({ // expected-note@:18{{generic parameter 'T' inferred as '()' from closure return expression}} + print("hello") + }) +} + +func testSR13239_Variadic_Twos() -> Int { + // expected-error@+1{{cannot convert return expression of type '()' to return type 'Int'}} + callitVariadic({ + print("hello") + }, { + print("hello") + }) +} diff --git a/test/expr/closure/closures_swift6.swift b/test/expr/closure/closures_swift6.swift new file mode 100644 index 0000000000000..2dd42e1721703 --- /dev/null +++ b/test/expr/closure/closures_swift6.swift @@ -0,0 +1,71 @@ +// RUN: %target-typecheck-verify-swift -swift-version 6 +// REQUIRES: asserts + +func doStuff(_ fn : @escaping () -> Int) {} +func doVoidStuff(_ fn : @escaping () -> ()) {} +func doVoidStuffNonEscaping(_ fn: () -> ()) {} + +class ExplicitSelfRequiredTest { + var x = 42 + func method() { + doVoidStuff({ doStuff({ x+1 })}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + } +} + +class SR14120 { + func operation() {} + + func test1() { + doVoidStuff { [self] in + operation() + } + } + + func test2() { + doVoidStuff { [self] in + doVoidStuff { + // expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}} + // expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} + // expected-note@+1 {{reference 'self.' explicitly}} + operation() + } + } + } + + func test3() { + doVoidStuff { [self] in + doVoidStuff { [self] in + operation() + } + } + } + + func test4() { + doVoidStuff { [self] in + doVoidStuff { + self.operation() + } + } + } + + func test5() { + doVoidStuff { [self] in + doVoidStuffNonEscaping { + operation() + } + } + } + + func test6() { + doVoidStuff { [self] in + doVoidStuff { [self] in + doVoidStuff { + // expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}} + // expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} + // expected-note@+1 {{reference 'self.' explicitly}} + operation() + } + } + } + } +} diff --git a/test/expr/closure/nested_inner_closures.swift b/test/expr/closure/nested_inner_closures.swift index 2bd7bc311a9c9..998c422f0027e 100644 --- a/test/expr/closure/nested_inner_closures.swift +++ b/test/expr/closure/nested_inner_closures.swift @@ -5,3 +5,4 @@ assert({ () -> Bool in }(), "") var x = ({ () -> String in return "s" })() +var y = ((({ () -> String in return "s" })))() diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index eba513dd6bab0..3d17d191ef876 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -865,7 +865,7 @@ func r20802757(_ z: inout Int = &g20802757) { // expected-error {{cannot provide print(z) } -_ = _.foo // expected-error {{'_' can only appear in a pattern or on the left side of an assignment}} +_ = _.foo // expected-error {{placeholders are not allowed as top-level types}} // wrong arg list crashing sourcekit func r22211854() { diff --git a/test/expr/postfix/call/forward_trailing_closure.swift b/test/expr/postfix/call/forward_trailing_closure.swift index 2b60aaa12f468..a6de7ddd5dd9f 100644 --- a/test/expr/postfix/call/forward_trailing_closure.swift +++ b/test/expr/postfix/call/forward_trailing_closure.swift @@ -1,4 +1,5 @@ -// RUN: %target-typecheck-verify-swift -disable-fuzzy-forward-scan-trailing-closure-matching +// RUN: %target-typecheck-verify-swift -swift-version 6 +// REQUIRES: asserts func forwardMatch1( a: Int = 0, b: Int = 17, closure1: (Int) -> Int = { $0 }, c: Int = 42, diff --git a/test/expr/postfix/call/forward_trailing_closure_errors.swift b/test/expr/postfix/call/forward_trailing_closure_errors.swift index 912765f4b8b9a..2683b69490225 100644 --- a/test/expr/postfix/call/forward_trailing_closure_errors.swift +++ b/test/expr/postfix/call/forward_trailing_closure_errors.swift @@ -1,4 +1,5 @@ -// RUN: %target-typecheck-verify-swift -disable-fuzzy-forward-scan-trailing-closure-matching +// RUN: %target-typecheck-verify-swift -swift-version 6 +// REQUIRES: asserts func forwardMatchWithGeneric( // expected-note{{'forwardMatchWithGeneric(closure1:closure2:)' declared here}} closure1: T, diff --git a/test/expr/unary/async_await.swift b/test/expr/unary/async_await.swift index fdd5b3428bd13..45dd802f10907 100644 --- a/test/expr/unary/async_await.swift +++ b/test/expr/unary/async_await.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency -verify-syntax-tree +// RUN: %target-swift-frontend -typecheck -verify %s -verify-syntax-tree -disable-availability-checking // REQUIRES: concurrency @@ -158,7 +158,7 @@ func validAsyncFunction() async throws { _ = try await throwingAndAsync() } -// spawn let checking +// Async let checking func mightThrow() throws { } func getIntUnsafely() throws -> Int { 0 } @@ -169,27 +169,27 @@ extension Error { } func testAsyncLet() async throws { - spawn let x = await getInt() + async let x = await getInt() print(x) // expected-error{{expression is 'async' but is not marked with 'await'}} - // expected-note@-1:9{{reference to spawn let 'x' is 'async'}} + // expected-note@-1:9{{reference to async let 'x' is 'async'}} print(await x) do { try mightThrow() - } catch let e where e.number == x { // expected-error{{spawn let 'x' cannot be referenced in a catch guard expression}} + } catch let e where e.number == x { // expected-error{{async let 'x' cannot be referenced in a catch guard expression}} } catch { } - spawn let x1 = getIntUnsafely() // okay, try is implicit here + async let x1 = getIntUnsafely() // okay, try is implicit here - spawn let x2 = getInt() // okay, await is implicit here + async let x2 = getInt() // okay, await is implicit here - spawn let x3 = try getIntUnsafely() - spawn let x4 = try! getIntUnsafely() - spawn let x5 = try? getIntUnsafely() + async let x3 = try getIntUnsafely() + async let x4 = try! getIntUnsafely() + async let x5 = try? getIntUnsafely() - _ = await x1 // expected-error{{reading 'spawn let' can throw but is not marked with 'try'}} + _ = await x1 // expected-error{{reading 'async let' can throw but is not marked with 'try'}} _ = await x2 _ = try await x3 _ = await x4 @@ -198,9 +198,9 @@ func testAsyncLet() async throws { // expected-note@+1 4{{add 'async' to function 'testAsyncLetOutOfAsync()' to make it asynchronous}} {{30-30= async}} func testAsyncLetOutOfAsync() { - spawn let x = 1 // expected-error{{'spawn let' in a function that does not support concurrency}} + async let x = 1 // expected-error{{'async let' in a function that does not support concurrency}} // FIXME: expected-error@-1{{'async' call in a function that does not support concurrency}} - _ = await x // expected-error{{'spawn let' in a function that does not support concurrency}} - _ = x // expected-error{{'spawn let' in a function that does not support concurrency}} + _ = await x // expected-error{{'async let' in a function that does not support concurrency}} + _ = x // expected-error{{'async let' in a function that does not support concurrency}} } diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 97eddad88045d..511794f047670 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -180,14 +180,10 @@ func testKeyPath(sub: Sub, optSub: OptSub, var m = [\A.property, \A.[sub], \A.optProperty!] expect(&m, toHaveType: Exactly<[PartialKeyPath]>.self) - // \.optProperty returns an optional of Prop and `\.[sub]` returns `A` - // expected-error@+2 {{key path value type 'Prop?' cannot be converted to contextual type 'Prop'}} - // expected-error@+1 {{key path value type 'A' cannot be converted to contextual type 'Prop'}} + // \.optProperty returns an optional of Prop and `\.[sub]` returns `A`, all this unifies into `[PartialKeyPath]` var n = [\A.property, \.optProperty, \.[sub], \.optProperty!] expect(&n, toHaveType: Exactly<[PartialKeyPath]>.self) - // FIXME: shouldn't be ambiguous - // expected-error@+1{{ambiguous}} let _: [PartialKeyPath] = [\.property, \.optProperty, \.[sub], \.optProperty!] var o = [\A.property, \C.value] @@ -1054,6 +1050,16 @@ func testSyntaxErrors() { _ = \A.a!; } +// SR-14644 +func sr14644() { + _ = \Int.byteSwapped.signum() // expected-error {{invalid component of Swift key path}} + _ = \Int.byteSwapped.init() // expected-error {{invalid component of Swift key path}} + _ = \Int // expected-error {{key path must have at least one component}} + _ = \Int? // expected-error {{key path must have at least one component}} + _ = \Int. // expected-error {{invalid component of Swift key path}} + // expected-error@-1 {{expected member name following '.'}} +} + // SR-13364 - keypath missing optional crashes compiler: "Inactive constraints left over?" func sr13364() { let _: KeyPath = \.utf8.count // expected-error {{no exact matches in reference to property 'count'}} @@ -1111,3 +1117,65 @@ func test_kp_as_function_mismatch() { _ = a.filter(\String.filterOut) // expected-error{{key path value type '(String) throws -> Bool' cannot be converted to contextual type 'Bool'}} } + +func test_partial_keypath_inference() { + // rdar://problem/34144827 + + struct S { var i: Int = 0 } + enum E { case A(pkp: PartialKeyPath) } + + _ = E.A(pkp: \.i) // Ok + + // rdar://problem/36472188 + + class ThePath { + var isWinding:Bool? + } + + func walk(aPath: T, forKey: PartialKeyPath) {} + func walkThePath(aPath: ThePath, forKey: PartialKeyPath) {} + + func test(path: ThePath) { + walkThePath(aPath: path, forKey: \.isWinding) // Ok + walk(aPath: path, forKey: \.isWinding) // Ok + } +} + +// SR-14499 +struct SR14499_A { } +struct SR14499_B { } + +func sr14499() { + func reproduceA() -> [(SR14499_A, SR14499_B)] { + [ + (true, .init(), SR14499_B.init()) // expected-error {{cannot infer contextual base in reference to member 'init'}} + ] + .filter(\.0) // expected-error {{value of type 'Any' has no member '0'}} + // expected-note@-1 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} + .prefix(3) + .map { ($0.1, $0.2) } // expected-error {{value of type 'Any' has no member '1'}} expected-error{{value of type 'Any' has no member '2'}} + // expected-note@-1 2 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} + } + + func reproduceB() -> [(SR14499_A, SR14499_B)] { + [ + (true, SR14499_A.init(), .init()) // expected-error {{cannot infer contextual base in reference to member 'init'}} + ] + .filter(\.0) // expected-error {{value of type 'Any' has no member '0'}} + // expected-note@-1 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} + .prefix(3) + .map { ($0.1, $0.2) } // expected-error {{value of type 'Any' has no member '1'}} expected-error{{value of type 'Any' has no member '2'}} + // expected-note@-1 2 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} + } + + func reproduceC() -> [(SR14499_A, SR14499_B)] { + [ + (true, .init(), .init()) // expected-error 2 {{cannot infer contextual base in reference to member 'init'}} + ] + .filter(\.0) // expected-error {{value of type 'Any' has no member '0'}} + // expected-note@-1 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} + .prefix(3) + .map { ($0.1, $0.2) } // expected-error {{value of type 'Any' has no member '1'}} expected-error{{value of type 'Any' has no member '2'}} + // expected-note@-1 2 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} + } +} diff --git a/test/lit.cfg b/test/lit.cfg index b99addce2314a..971c94543e5a6 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -2,7 +2,7 @@ # # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2021 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 @@ -81,12 +81,16 @@ def darwin_get_watchos_sim_vers(): return [float(v.strip('watchOS')) for v in watchos_version_str] # Returns the "prefix" command that should be prepended to the command line to -# run an executable compiled for iOS or AppleTV simulator. -def get_simulator_command(run_os, run_cpu, sdk_path): - (name, vers, build) = darwin_get_sdk_version(sdk_path) +# run an executable compiled for an iOS, tvOS, or watchOS simulator. +def get_simulator_command(run_os, run_cpu): + mac_ver_tuple = tuple(int(v) for v in + platform.mac_ver()[0].replace('.', ' ').split()) if run_os == 'ios': if run_cpu == "i386": - if min(darwin_get_ios_sim_vers()) > 10.3: + if mac_ver_tuple >= (11,): + print("ERROR: The 32-bit iOS simulator is unavailable on macOS 11.0+") + sys.exit(1) + elif min(darwin_get_ios_sim_vers()) > 10.3: print("ERROR: Your system does not have a 32-bit iOS simulator installed.") print("INFO: 1. Install iOS 10.3 or older simulator (Xcode -> Preferences -> Components -> Simulators).") print("INFO: 2. Create a 32-bit iPhone 5 device. Run:") @@ -95,14 +99,14 @@ def get_simulator_command(run_os, run_cpu, sdk_path): else: return "simctl spawn --standalone 'iPhone 5'" else: - return "simctl spawn --standalone 'iPhone 8'" + return "simctl spawn --standalone 'iPhone 12'" elif run_os == 'tvos': return "simctl spawn --standalone 'Apple TV'" elif run_os == 'watchos': if run_cpu == "i386": if min(darwin_get_watchos_sim_vers()) > 6.2: print("ERROR: Your system does not have a 32-bit watchOS simulator installed.") - print("INFO: 1. Install watchOS 6.2 or older simulator (Xcode -> Preferences -> Components -> Simulators).") + print("INFO: 1. Install watchOS 6.2.1 simulator (Xcode -> Preferences -> Components -> Simulators).") print("INFO: 2. Create a 32-bit watchOS device. Run:") print("INFO: $ xcrun simctl create 'Apple Watch Series 2 - 42mm' com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-2-42mm com.apple.CoreSimulator.SimRuntime.watchOS-6-2") sys.exit(1) @@ -203,7 +207,7 @@ if lit_config.params.get('disable_unittests', None) is not None: # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(__file__) -# test_exec_root: The root path where tests should be run. +# swift_obj_root: The path to the swift build root. swift_obj_root = getattr(config, 'swift_obj_root', None) # cmake. The path to the cmake executable we used to configure swift. @@ -250,6 +254,9 @@ native_swift_tools_path = lit_config.params.get('native_swift_tools_path') if native_swift_tools_path is not None: append_to_env_path(native_swift_tools_path) +if lit_config.params.get('libswift', None) is not None: + config.available_features.add('libswift') + ### # Discover the Swift binaries to use. @@ -399,6 +406,11 @@ swift_version = lit_config.params.get('swift-version', lit_config.note('Compiling with -swift-version ' + swift_version) config.swift_test_options = '-swift-version ' + swift_version +# Define a macro for the next release OS version. +swift_stdlib_macro = '\'SwiftStdlib 5.5:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0\'' +config.swift_frontend_test_options += ' -define-availability ' + swift_stdlib_macro +config.swift_driver_test_options += ' -Xfrontend -define-availability -Xfrontend ' + swift_stdlib_macro + differentiable_programming = lit_config.params.get('differentiable_programming', None) if differentiable_programming is not None: config.available_features.add('differentiable_programming') @@ -647,6 +659,10 @@ elif swift_test_subset == 'only_stress': config.available_features.add("stress_test") config.limit_to_features.add("stress_test") config.limit_to_features.discard("executable_test") +elif swift_test_subset == 'only_early_swiftdriver': + # Point this subset at a driver-specific set of tests. These are the known reduced subset + # of tests to verify the basic functionality of the standalone (early) swift-driver. + config.test_source_root = os.path.join(config.test_source_root, 'Driver', 'Dependencies') else: lit_config.fatal("Unknown test mode %r" % swift_test_subset) @@ -656,6 +672,14 @@ if 'swift_evolve' in lit_config.params: if not 'swift_driver' in lit_config.params: config.available_features.add("cplusplus_driver") +# Check if we need to run lit tests using the legacy driver or the new driver +# The default for existing test runs is to use the legacy driver. +# The new driver is tested separately. +if swift_test_subset != 'only_early_swiftdriver' and\ + os.environ.get('SWIFT_FORCE_TEST_NEW_DRIVER') is None: + config.environment['SWIFT_USE_OLD_DRIVER'] = '1' + config.environment['SWIFT_AVOID_WARNING_USING_OLD_DRIVER'] = '1' + # Enable benchmark testing when the binary is found (has fully qualified path). if config.benchmark_o != 'Benchmark_O': config.available_features.add('benchmark') @@ -663,6 +687,11 @@ if config.benchmark_o != 'Benchmark_O': # Add substitutions for the run target triple, CPU, OS, and pointer size. config.substitutions.append(('%target-triple', config.variant_triple)) +# Keep track of which sanitizers need to be enabled (matters for some tests) +config.sanitizers = [] +if 'asan' in config.available_features: + config.sanitizers.append('address') + if run_vendor == 'apple': if run_os == 'maccatalyst': config.stable_abi_triple = '%s-%s-ios13.0-macabi' % (run_cpu, run_vendor) @@ -847,7 +876,13 @@ def use_interpreter_for_simple_runs(): target_specific_module_triple = config.variant_triple target_future = target_specific_module_triple -config.target_run = "" +config.target_run = '' +config.target_rtti_opt = '-fno-rtti' +config.target_pic_opt = '' +config.target_cxx_lib = '-lc++' +config.target_msvc_runtime_opt = '' +config.target_static_library_prefix = 'lib' +config.target_static_library_suffix = '.a' if run_vendor == 'apple': target_specific_module_triple = '{}-apple-{}'.format( @@ -858,7 +893,10 @@ if run_vendor == 'apple': config.target_object_format = "macho" config.target_shared_library_prefix = 'lib' config.target_shared_library_suffix = ".dylib" - config.target_codesign = "codesign -f -s -" + if 'use_os_stdlib' not in lit_config.params: + config.target_codesign = make_path(config.swift_utils, "swift-darwin-postprocess.py") + else: + config.target_codesign = "codesign -f -s -" config.target_runtime = "objc" config.available_features.add('libdispatch') @@ -940,6 +978,8 @@ if run_vendor == 'apple': ("%s %s %s -F %r -toolchain-stdlib-rpath " + "-Xlinker -rpath -Xlinker %r " + "-Xlinker -rpath -Xlinker /usr/lib/swift " + + # This ensures LC_LOAD_DYLIB rewrite will succeed in %target-codesign (rdar://7885126): + "-Xlinker -headerpad_max_install_names " + "%s %s %s %s") % (xcrun_prefix, config.swiftc, target_options, extra_frameworks_dir, @@ -979,6 +1019,8 @@ if run_vendor == 'apple': config.target_build_swift = ( ("%s %s %s -F %r -toolchain-stdlib-rpath %s " + "-Xlinker -rpath -Xlinker /usr/lib/swift " + + # This ensures LC_LOAD_DYLIB rewrite will succeed in %target-codesign (rdar://7885126): + "-Xlinker -headerpad_max_install_names " + " %s %s %s") % (xcrun_prefix, config.swiftc, target_options, extra_frameworks_dir, @@ -994,7 +1036,7 @@ if run_vendor == 'apple': # segmentation faults) config.target_run = ( "%s %s " % - (xcrun_prefix, get_simulator_command(run_os, run_cpu, config.variant_sdk))) + (xcrun_prefix, get_simulator_command(run_os, run_cpu))) (sw_vers_name, sw_vers_vers, sw_vers_build) = \ darwin_get_sdk_version(config.variant_sdk) @@ -1031,6 +1073,8 @@ if run_vendor == 'apple': ("%s %s %s %s -F %r -toolchain-stdlib-rpath " + "-Xlinker -rpath -Xlinker %r " + "-Xlinker -rpath -Xlinker /usr/lib/swift " + # This ensures LC_LOAD_DYLIB rewrite will succeed in %target-codesign (rdar://7885126): + + "-Xlinker -headerpad_max_install_names " + "%s %s %s %s " + "-F %r -Xlinker -rpath -Xlinker %r") % (xcrun_prefix, config.swiftc, target_options, @@ -1104,6 +1148,8 @@ if run_vendor == 'apple': target_options_for_mock_sdk_after config.target_swiftc_driver = ( ("%s %s -toolchain-stdlib-rpath %s " + + # This ensures LC_LOAD_DYLIB rewrite will succeed in %target-codesign (rdar://7885126): + "-Xlinker -headerpad_max_install_names " + "-Xlinker -rpath -Xlinker /usr/lib/swift %s ")% (xcrun_prefix, config.swiftc, target_options, config.swift_driver_test_options)) config.target_clang = ( @@ -1158,9 +1204,16 @@ elif run_os in ['windows-msvc']: config.target_object_format = 'coff' config.target_shared_library_prefix = '' config.target_shared_library_suffix = '.dll' + config.target_static_library_prefix = '' + config.target_static_library_suffix = '.lib' config.target_sdk_name = 'windows' config.target_runtime = 'native' config.target_cc_options = "" + config.target_rtti_opt = "-frtti" + config.target_cxx_lib = "" + config.target_msvc_runtime_opt = '-%s -D_MT' % config.swift_stdlib_msvc_runtime + if 'D' in config.swift_stdlib_msvc_runtime: + config.target_msvc_runtime_opt += ' -D_DLL' config.target_build_swift = \ ('%r -target %s %s %s %s %s -libc %s' % \ @@ -1230,6 +1283,10 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'openbsd', 'windows- (kIsAndroid and run_os in ['linux-android', 'linux-androideabi'])): # Running lit and the compiler on Android itself is more like running on Linux, # ie the NDK and adb aren't needed, so use this instead. + + config.target_cxx_lib = "-lstdc++" + config.target_pic_opt = "-fPIC" + # Linux/FreeBSD/Cygwin/Android if run_os == 'windows-cygnus': lit_config.note("Testing Cygwin " + config.variant_triple) @@ -1372,6 +1429,9 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': result = kwards["aarch64"] return result + config.target_cxx_lib = "-lstdc++" + config.target_pic_opt = "-fPIC" + ndk_platform_tuple = get_architecture_value(armv7="armeabi-v7a", aarch64="arm64-v8a") ndk_platform_triple = get_architecture_value(armv7="arm-linux-androideabi", @@ -1594,6 +1654,13 @@ config.substitutions.append(('%concurrency_module', concurrency_module)) config.substitutions.append(('%/concurrency_module', '/'.join(os.path.normpath(concurrency_module).split(os.sep)))) +# Add 'distributed_module' as the path to the _Distributed .swiftmodule file +distributed_module = os.path.join(stdlib_dir, "_Distributed.swiftmodule", + target_specific_module_triple + ".swiftmodule") +config.substitutions.append(('%distributed_module', distributed_module)) +config.substitutions.append(('%/distributed_module', + '/'.join(os.path.normpath(distributed_module).split(os.sep)))) + # Different OS's require different prefixes for the environment variables to be # propagated to the calling contexts. # In order to make tests OS-agnostic, names of environment variables should be @@ -1770,12 +1837,6 @@ if sftp_server_path: config.substitutions.append(('%sftp-server', sftp_server_path or 'no-sftp-server')) -subst_target_jit_run = "" -if 'swift_interpreter' in config.available_features: - subst_target_jit_run = ( - "%s -interpret %s" % - (config.target_swift_frontend, sdk_overlay_link_path)) - subst_target_repl_run_simple_swift = "" if 'swift_repl' in config.available_features: subst_target_repl_run_simple_swift = ( @@ -1848,12 +1909,17 @@ if not kIsWindows: else: config.available_features.add('use_os_stdlib') os_stdlib_path = '' + concurrency_back_deploy_path = '' if run_vendor == 'apple': #If we get swift-in-the-OS for non-Apple platforms, add a condition here os_stdlib_path = "/usr/lib/swift" if run_os == 'maccatalyst': os_stdlib_path = "/System/iOSSupport/usr/lib/swift:/usr/lib/swift" - all_stdlib_path = os.path.pathsep.join((os_stdlib_path, target_stdlib_path)) + if 'back_deploy_concurrency' in config.available_features: + concurrency_back_deploy_path = os.path.join(os.path.dirname(swift_obj_root), os.path.basename(swift_obj_root).replace("swift-", "backdeployconcurrency-"), 'lib', 'swift-5.5', run_os) + + all_stdlib_path = os.path.pathsep.join((os_stdlib_path, concurrency_back_deploy_path, target_stdlib_path)) + lit_config.note('Testing with the standard libraries coming from the OS ' + all_stdlib_path) config.target_run = ( "/usr/bin/env " @@ -1862,6 +1928,25 @@ if not kIsWindows: "SIMCTL_CHILD_DYLD_LIBRARY_PATH='{0}' " # Simulator option .format(all_stdlib_path, libdispatch_path)) + config.target_run +# When running with the JIT on Darwin, force the usage of the just built stdlib +# when running the frontend. This is because we currently JIT in process +# resulting in weirdness around swift-frontend wanting to link against the host +# stdlib and the exec swift code wanting to link against the target stdlib. For +# now, we work around this by just saying in that case we will force +# swift-frontend to use the just built runtime so we are consistent. The only +# potential problem is that a bug in the just built runtime may now cause these +# tests to fail. +subst_target_jit_prefix = "" +if platform.system() == 'Darwin' and config.target_run: + subst_target_jit_prefix = config.target_run + +subst_target_jit_run = "" +if 'swift_interpreter' in config.available_features: + subst_target_jit_run = ( + "%s %s -interpret %s" % + (subst_target_jit_prefix, + config.target_swift_frontend, sdk_overlay_link_path)) + if not getattr(config, 'target_run_simple_swift', None): config.target_run_simple_swift_parameterized = SubstituteCaptures( r"%%empty-directory(%%t) && " @@ -1955,6 +2040,12 @@ config.substitutions.append(('%target-swiftxx-frontend', '%s -enable-cxx-interop config.substitutions.append(('%target-runtime', config.target_runtime)) +config.substitutions.append(('%target-rtti-opt', config.target_rtti_opt)) +config.substitutions.append(('%target-cxx-lib', config.target_cxx_lib)) +config.substitutions.append(('%target-pic-opt', config.target_pic_opt)) +config.substitutions.append(('%target-msvc-runtime-opt', + config.target_msvc_runtime_opt)) + config.substitutions.append(('%target-typecheck-verify-swift', config.target_parse_verify_swift)) config.substitutions.append(('%target-swift-emit-silgen\(mock-sdk:([^)]+)\)', @@ -1996,6 +2087,13 @@ if hasattr(config, 'target_cc_options'): else: config.substitutions.append(('%target-cc-options', '')) +# Build an -fsanitize= argument (used by some tests) +if config.sanitizers: + config.substitutions.append(('%target-sanitizer-opt', + '-fsanitize=' + ','.join(config.sanitizers))) +else: + config.substitutions.append(('%target-sanitizer-opt', '')) + # WORKAROUND(rdar://53507844): On some macOS versions, we see flaky failures in # tests which create a hard link to an executable and immediately invoke it. # Work around this by always copying on Darwin. @@ -2051,10 +2149,14 @@ config.substitutions.append(('%target-swift-reflection-test', config.target_swif config.substitutions.append(('%target-swift-reflection-dump', '{} {} {}'.format(config.swift_reflection_dump, '-arch', run_cpu))) config.substitutions.append(('%target-swiftc_driver', config.target_swiftc_driver)) + config.substitutions.append(('%target-swift-remoteast-test-with-sdk', - '%s -sdk %r' % - (config.swift_remoteast_test, config.variant_sdk))) -config.substitutions.append(('%target-swift-remoteast-test', config.swift_remoteast_test)) + '%s %s -sdk %r' % + (subst_target_jit_prefix, + config.swift_remoteast_test, config.variant_sdk))) +config.substitutions.append(('%target-swift-remoteast-test', + '%s %s' % (subst_target_jit_prefix, + config.swift_remoteast_test))) if hasattr(config, 'target_swift_autolink_extract'): config.available_features.add('autolink-extract') @@ -2089,6 +2191,10 @@ config.substitutions.insert(0, ('%target-library-name\(([^)]+)\)', SubstituteCaptures(r'%s\1%s' % ( escape_for_substitute_captures(config.target_shared_library_prefix), escape_for_substitute_captures(config.target_shared_library_suffix))))) +config.substitutions.insert(0, ('%target-static-library-name\(([^)]+)\)', + SubstituteCaptures(r'%s\1%s' % ( + escape_for_substitute_captures(config.target_static_library_prefix), + escape_for_substitute_captures(config.target_static_library_suffix))))) config.substitutions.append(('%target-rpath\(([^)]+)\)', SubstituteCaptures(config.target_add_rpath))) @@ -2126,6 +2232,27 @@ config.substitutions.append(('%raw-FileCheck', shell_quote(config.filecheck))) config.substitutions.append(('%import-libdispatch', getattr(config, 'import_libdispatch', ''))) config.substitutions.append(('%import-static-libdispatch', getattr(config, 'import_libdispatch_static', ''))) +# Disabe COW sanity checks in the swift runtime by default. +# (But it's required to set this environment variable to something) +config.environment['SWIFT_DEBUG_ENABLE_COW_CHECKS'] = 'false' + +# Add this to the command which runs an executable to enable COW checks in the swift runtime. +config.substitutions.append(('%enable-cow-checking', TARGET_ENV_PREFIX + 'SWIFT_DEBUG_ENABLE_COW_CHECKS=true;')) + +# We add an expansion to ensure that migrator tests can search for the api diff +# data dir from the host compiler toolchain instead of the resource dir of the +# stdlib. Since we are using the host compiler, we would not have built any +# tools meaning that we would not have generated the -api-diff-data-dir. +# +# In the case where we are not building against the host compiler, this just +# expands to the empty string. +api_diff_data_dir_flag = '' +if hasattr(config, 'testing_against_host_compiler'): + migrator_dir = os.path.join(os.path.dirname(config.swiftc), + os.pardir, 'lib', 'swift', 'migrator') + api_diff_data_dir_flag = ' -api-diff-data-dir {} '.format(migrator_dir) +config.substitutions.append(('%api_diff_data_dir', api_diff_data_dir_flag)) + if config.lldb_build_root != "": lldb_python_path = get_lldb_python_path(config.lldb_build_root) lldb_python_interpreter = get_lldb_python_interpreter(config.lldb_build_root) @@ -2150,9 +2277,6 @@ config.environment[TARGET_ENV_PREFIX + 'SWIFT_DETERMINISTIC_HASHING'] = '1' # Enable malloc scribble during tests by default. config.environment[TARGET_ENV_PREFIX + 'SWIFT_DEBUG_ENABLE_MALLOC_SCRIBBLE'] = 'YES' -# Enable COW sanity checks in the swift runtime by default. -config.environment['SWIFT_DEBUG_ENABLE_COW_CHECKS'] = 'true' - # Run lsb_release on the target to be tested and return the results. def linux_get_lsb_release(): lsb_release_path = '/usr/bin/lsb_release' diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 51723d0c11059..c421f626e77a9 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -60,6 +60,12 @@ config.darwin_enable_maccatalyst = "@SWIFT_ENABLE_MACCATALYST@" == "TRUE" config.darwin_maccatalyst_build_flavor = "@BUILD_FLAVOR@" config.darwin_osx_variant_suffix = "@DEFAULT_OSX_VARIANT_SUFFIX@" +# If we are not testing against the host compiler, we are testing against the +# just built compiler. Add that as a feature so we can conditionally mark tests +# as requiring a just_built_compiler. +config.testing_against_host_compiler = \ + "@SWIFT_RUN_TESTS_WITH_HOST_COMPILER@" == "TRUE" + # Please remember to handle empty strings and/or unset variables correctly. if "@SWIFT_ASAN_BUILD@" == "TRUE": @@ -128,6 +134,10 @@ if "@SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING@" == "TRUE": config.available_features.add('differentiable_programming') if "@SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY@" == "TRUE": config.available_features.add('concurrency') +if "@SWIFT_BACK_DEPLOY_CONCURRENCY@" == "TRUE": + config.available_features.add('back_deploy_concurrency') +if "@SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED@" == "TRUE": + config.available_features.add('distributed') # Let the main config do the real work. if config.test_exec_root is None: diff --git a/test/multifile/Inputs/protocol-conformance-redundant-def.swift b/test/multifile/Inputs/protocol-conformance-redundant-def.swift new file mode 100644 index 0000000000000..0bdd9edb2ad0f --- /dev/null +++ b/test/multifile/Inputs/protocol-conformance-redundant-def.swift @@ -0,0 +1,7 @@ +public protocol Hello { + func hello() +} + +open class Super { + public init() {} +} diff --git a/test/multifile/Inputs/protocol-conformance-redundant-ext.swift b/test/multifile/Inputs/protocol-conformance-redundant-ext.swift new file mode 100644 index 0000000000000..84c34532ddb20 --- /dev/null +++ b/test/multifile/Inputs/protocol-conformance-redundant-ext.swift @@ -0,0 +1,7 @@ +import Def + +extension Super : Hello { + public func hello() { + print("Hello") + } +} diff --git a/test/multifile/batch-mode-llvmIRHash-consistency.swift b/test/multifile/batch-mode-llvmIRHash-consistency.swift deleted file mode 100644 index 82951b01f6d52..0000000000000 --- a/test/multifile/batch-mode-llvmIRHash-consistency.swift +++ /dev/null @@ -1,22 +0,0 @@ -// Ensure that the LLVMIR hash of the 2nd compilation in batch mode -// is consistent no matter if the first one generates code or not. - -// RUN: %empty-directory(%t) -// RUN: echo 'public enum E: Error {}' >%t/main.swift -// RUN: echo >%t/other.swift - -// RUN: cd %t; %target-swift-frontend -c -g -enable-batch-mode -module-name main -primary-file ./main.swift -primary-file other.swift - -// RUN: cp %t/main{,-old}.o -// RUN: cp %t/other{,-old}.o -// RUN: echo 'public enum E: Error {}' >%t/main.swift -// RUN: echo >%t/other.swift - -// RUN: cd %t; %target-swift-frontend -c -g -enable-batch-mode -module-name main -primary-file ./main.swift -primary-file other.swift - -// Ensure that code generation was not performed for other.swift - -// RUN: ls -1t %t | %FileCheck %s - -// CHECK: other.swift -// CHECK: other.o diff --git a/test/multifile/protocol-conformance-redundant.swift b/test/multifile/protocol-conformance-redundant.swift new file mode 100644 index 0000000000000..789af73ad9227 --- /dev/null +++ b/test/multifile/protocol-conformance-redundant.swift @@ -0,0 +1,39 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(Def)) -module-name Def -emit-module -emit-module-path %t/Def.swiftmodule %S/Inputs/protocol-conformance-redundant-def.swift +// RUN: %target-build-swift-dylib(%t/%target-library-name(Ext)) -module-name Ext -emit-module -emit-module-path %t/Ext.swiftmodule -I%t -L%t -lDef %S/Inputs/protocol-conformance-redundant-ext.swift +// RUN: %target-build-swift -I%t -L%t -lDef -o %t/main %target-rpath(%t) %s +// RUN: %target-codesign %t/main %t/%target-library-name(Def) %t/%target-library-name(Ext) +// RUN: %target-run %t/main %t/%target-library-name(Def) %t/%target-library-name(Ext) 2>&1 | %FileCheck %s + +// REQUIRES: executable_test +// XFAIL: windows + +// CHECK: Warning: 'Sub' conforms to protocol 'Hello', but it also inherits conformance from 'Super'. Relying on a particular conformance is undefined behaviour. +// CHECK: Hello + +import StdlibUnittest + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#else +#error("Unsupported platform") +#endif + +import Def + +let dylibPath = CommandLine.arguments.last! +let openRes = dlopen(dylibPath, RTLD_NOW|RTLD_LOCAL) +assert(openRes != nil, "unable to open extension dylib") + +class Sub : Super, Hello { + func hello() { + print("Hello") + } +} + +let s = Sub() as AnyObject as! Hello + +s.hello() + diff --git a/test/refactoring/ConvertAsync/Inputs/convert_bool_objc.h b/test/refactoring/ConvertAsync/Inputs/convert_bool_objc.h new file mode 100644 index 0000000000000..5a460647eee6d --- /dev/null +++ b/test/refactoring/ConvertAsync/Inputs/convert_bool_objc.h @@ -0,0 +1,13 @@ +@import Foundation; + +@interface ClassWithHandlerMethods ++ (void)firstBoolFlagSuccess:(NSString *_Nonnull)str + completion:(void (^_Nonnull)(NSString *_Nullable, BOOL, BOOL, + NSError *_Nullable))handler + __attribute__((swift_async_error(zero_argument, 2))); + ++ (void)secondBoolFlagFailure:(NSString *_Nonnull)str + completion:(void (^_Nonnull)(NSString *_Nullable, BOOL, BOOL, + NSError *_Nullable))handler + __attribute__((swift_async_error(nonzero_argument, 3))); +@end diff --git a/test/refactoring/ConvertAsync/Inputs/module.map b/test/refactoring/ConvertAsync/Inputs/module.map new file mode 100644 index 0000000000000..de9ffa919461b --- /dev/null +++ b/test/refactoring/ConvertAsync/Inputs/module.map @@ -0,0 +1,3 @@ +module ConvertBoolObjC { + header "convert_bool_objc.h" +} diff --git a/test/refactoring/ConvertAsync/async_attribute_added.swift b/test/refactoring/ConvertAsync/async_attribute_added.swift new file mode 100644 index 0000000000000..23d74787b97ba --- /dev/null +++ b/test/refactoring/ConvertAsync/async_attribute_added.swift @@ -0,0 +1,31 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SIMPLE %s +func simple(completion: @escaping (String) -> Void) { } +// SIMPLE: async_attribute_added.swift [[# @LINE-1]]:1 -> [[# @LINE-1]]:1 +// SIMPLE-NEXT: @available(*, renamed: "simple()") +// SIMPLE-EMPTY: +// SIMPLE-NEXT: async_attribute_added.swift [[# @LINE-4]]:53 -> [[# @LINE-4]]:56 +// SIMPLE-NEXT: { +// SIMPLE-NEXT: Task { +// SIMPLE-NEXT: let result = await simple() +// SIMPLE-NEXT: completion(result) +// SIMPLE-NEXT: } +// SIMPLE-NEXT: } +// SIMPLE-EMPTY: +// SIMPLE-NEXT: async_attribute_added.swift [[# @LINE-12]]:56 -> [[# @LINE-12]]:56 +// SIMPLE-EMPTY: +// SIMPLE-EMPTY: +// SIMPLE-EMPTY: +// SIMPLE-NEXT: async_attribute_added.swift [[# @LINE-16]]:56 -> [[# @LINE-16]]:56 +// SIMPLE-NEXT: func simple() async -> String { } + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):5 | %FileCheck -check-prefix=OTHER-ARGS %s +func otherArgs(first: Int, second: String, completion: @escaping (String) -> Void) { } +// OTHER-ARGS: @available(*, renamed: "otherArgs(first:second:)") + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):5 | %FileCheck -check-prefix=EMPTY-NAMES %s +func emptyNames(first: Int, _ second: String, completion: @escaping (String) -> Void) { } +// EMPTY-NAMES: @available(*, renamed: "emptyNames(first:_:)") diff --git a/test/refactoring/ConvertAsync/basic.swift b/test/refactoring/ConvertAsync/basic.swift index 71ca714e3f5b7..eb26940a4a507 100644 --- a/test/refactoring/ConvertAsync/basic.swift +++ b/test/refactoring/ConvertAsync/basic.swift @@ -1,3 +1,5 @@ +// REQUIRES: concurrency + // RUN: %empty-directory(%t) enum CustomError: Error { @@ -12,32 +14,32 @@ typealias NestedAliasCallback = SomeCallback // 1. Check various functions for having/not having async alternatives // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+4):1 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+3):6 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):12 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):13 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -func simple(completion: (String) -> Void) { } +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+3):6 | %FileCheck -check-prefix=ASYNC-SIMPLE %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):12 | %FileCheck -check-prefix=ASYNC-SIMPLE %s +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):20 | %FileCheck -check-prefix=ASYNC-SIMPLE %s +func simple(/*cs*/ completion: @escaping (String) -> Void /*ce*/) { } // ASYNC-SIMPLE: basic.swift [[# @LINE-1]]:1 -> [[# @LINE-1]]:1 -// ASYNC-SIMPLE-NEXT: @available(*, deprecated, message: "Prefer async alternative instead") +// ASYNC-SIMPLE-NEXT: @available(*, renamed: "simple()") // ASYNC-SIMPLE-EMPTY: -// ASYNC-SIMPLE-NEXT: basic.swift [[# @LINE-4]]:43 -> [[# @LINE-4]]:46 +// ASYNC-SIMPLE-NEXT: basic.swift [[# @LINE-4]]:67 -> [[# @LINE-4]]:70 // ASYNC-SIMPLE-NEXT: { -// ASYNC-SIMPLE-NEXT: async { +// ASYNC-SIMPLE-NEXT: Task { // ASYNC-SIMPLE-NEXT: let result = await simple() // ASYNC-SIMPLE-NEXT: completion(result) // ASYNC-SIMPLE-NEXT: } // ASYNC-SIMPLE-NEXT: } // ASYNC-SIMPLE-EMPTY: -// ASYNC-SIMPLE-NEXT: basic.swift [[# @LINE-12]]:46 -> [[# @LINE-12]]:46 +// ASYNC-SIMPLE-NEXT: basic.swift [[# @LINE-12]]:70 -> [[# @LINE-12]]:70 // ASYNC-SIMPLE-EMPTY: // ASYNC-SIMPLE-EMPTY: // ASYNC-SIMPLE-EMPTY: -// ASYNC-SIMPLE-NEXT: basic.swift [[# @LINE-16]]:46 -> [[# @LINE-16]]:46 +// ASYNC-SIMPLE-NEXT: basic.swift [[# @LINE-16]]:70 -> [[# @LINE-16]]:70 // ASYNC-SIMPLE-NEXT: func simple() async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-SIMPLENOLABEL %s -func simpleWithoutLabel(_ completion: (String) -> Void) { } +func simpleWithoutLabel(_ completion: @escaping (String) -> Void) { } // ASYNC-SIMPLENOLABEL: { -// ASYNC-SIMPLENOLABEL-NEXT: async { +// ASYNC-SIMPLENOLABEL-NEXT: Task { // ASYNC-SIMPLENOLABEL-NEXT: let result = await simpleWithoutLabel() // ASYNC-SIMPLENOLABEL-NEXT: completion(result) // ASYNC-SIMPLENOLABEL-NEXT: } @@ -45,19 +47,19 @@ func simpleWithoutLabel(_ completion: (String) -> Void) { } // ASYNC-SIMPLENOLABEL: func simpleWithoutLabel() async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-SIMPLEWITHARG %s -func simpleWithArg(a: Int, completion: (String) -> Void) { } +func simpleWithArg(/*c1s*/ a: Int /*c1e*/, /*c2s*/ completion: @escaping (String) -> Void /*c2e*/) { } // ASYNC-SIMPLEWITHARG: { -// ASYNC-SIMPLEWITHARG-NEXT: async { +// ASYNC-SIMPLEWITHARG-NEXT: Task { // ASYNC-SIMPLEWITHARG-NEXT: let result = await simpleWithArg(a: a) // ASYNC-SIMPLEWITHARG-NEXT: completion(result) // ASYNC-SIMPLEWITHARG-NEXT: } // ASYNC-SIMPLEWITHARG-NEXT: } -// ASYNC-SIMPLEWITHARG: func simpleWithArg(a: Int) async -> String { } +// ASYNC-SIMPLEWITHARG: func simpleWithArg(/*c1s*/ a: Int /*c1e*/) async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-MULTIPLERESULTS %s -func multipleResults(completion: (String, Int) -> Void) { } +func multipleResults(completion: @escaping (String, Int) -> Void) { } // ASYNC-MULTIPLERESULTS: { -// ASYNC-MULTIPLERESULTS-NEXT: async { +// ASYNC-MULTIPLERESULTS-NEXT: Task { // ASYNC-MULTIPLERESULTS-NEXT: let result = await multipleResults() // ASYNC-MULTIPLERESULTS-NEXT: completion(result.0, result.1) // ASYNC-MULTIPLERESULTS-NEXT: } @@ -65,9 +67,9 @@ func multipleResults(completion: (String, Int) -> Void) { } // ASYNC-MULTIPLERESULTS: func multipleResults() async -> (String, Int) { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-NONOPTIONALERROR %s -func nonOptionalError(completion: (String, Error) -> Void) { } +func nonOptionalError(completion: @escaping (String, Error) -> Void) { } // ASYNC-NONOPTIONALERROR: { -// ASYNC-NONOPTIONALERROR-NEXT: async { +// ASYNC-NONOPTIONALERROR-NEXT: Task { // ASYNC-NONOPTIONALERROR-NEXT: let result = await nonOptionalError() // ASYNC-NONOPTIONALERROR-NEXT: completion(result.0, result.1) // ASYNC-NONOPTIONALERROR-NEXT: } @@ -75,9 +77,9 @@ func nonOptionalError(completion: (String, Error) -> Void) { } // ASYNC-NONOPTIONALERROR: func nonOptionalError() async -> (String, Error) { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-NOPARAMS %s -func noParams(completion: () -> Void) { } +func noParams(completion: @escaping () -> Void) { } // ASYNC-NOPARAMS: { -// ASYNC-NOPARAMS-NEXT: async { +// ASYNC-NOPARAMS-NEXT: Task { // ASYNC-NOPARAMS-NEXT: await noParams() // ASYNC-NOPARAMS-NEXT: completion() // ASYNC-NOPARAMS-NEXT: } @@ -85,9 +87,9 @@ func noParams(completion: () -> Void) { } // ASYNC-NOPARAMS: func noParams() async { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ERROR %s -func error(completion: (String?, Error?) -> Void) { } +func error(completion: @escaping (String?, Error?) -> Void) { } // ASYNC-ERROR: { -// ASYNC-ERROR-NEXT: async { +// ASYNC-ERROR-NEXT: Task { // ASYNC-ERROR-NEXT: do { // ASYNC-ERROR-NEXT: let result = try await error() // ASYNC-ERROR-NEXT: completion(result, nil) @@ -98,9 +100,9 @@ func error(completion: (String?, Error?) -> Void) { } // ASYNC-ERROR: func error() async throws -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ERRORONLY %s -func errorOnly(completion: (Error?) -> Void) { } +func errorOnly(completion: @escaping (Error?) -> Void) { } // ASYNC-ERRORONLY: { -// ASYNC-ERRORONLY-NEXT: async { +// ASYNC-ERRORONLY-NEXT: Task { // ASYNC-ERRORONLY-NEXT: do { // ASYNC-ERRORONLY-NEXT: try await errorOnly() // ASYNC-ERRORONLY-NEXT: completion(nil) @@ -112,30 +114,37 @@ func errorOnly(completion: (Error?) -> Void) { } // ASYNC-ERRORONLY: func errorOnly() async throws { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ERRORNONOPTIONALRESULT %s -func errorNonOptionalResult(completion: (String, Error?) -> Void) { } -// We cannot convert the deprecated non-async method to call the async method because we can't synthesize the non-optional completion param. Smoke check for some keywords that would indicate we rewrote the body. -// ASYNC-ERRORNONOPTIONALRESULT-NOT: detach -// ASYNC-ERRORNONOPTIONALRESULT-NOT: await +func errorNonOptionalResult(completion: @escaping (String, Error?) -> Void) { } +// ASYNC-ERRORNONOPTIONALRESULT: { +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: Task { +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: do { +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: let result = try await errorNonOptionalResult() +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: completion(result, nil) +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: } catch { +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: completion(<#String#>, error) +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: } +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: } +// ASYNC-ERRORNONOPTIONALRESULT-NEXT: } // ASYNC-ERRORNONOPTIONALRESULT: func errorNonOptionalResult() async throws -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-CUSTOMERROR %s -func customError(completion: (String?, CustomError?) -> Void) { } +func customError(completion: @escaping (String?, CustomError?) -> Void) { } // ASYNC-CUSTOMERROR: { -// ASYNC-CUSTOMERROR-NEXT: async { +// ASYNC-CUSTOMERROR-NEXT: Task { // ASYNC-CUSTOMERROR-NEXT: do { // ASYNC-CUSTOMERROR-NEXT: let result = try await customError() // ASYNC-CUSTOMERROR-NEXT: completion(result, nil) // ASYNC-CUSTOMERROR-NEXT: } catch { -// ASYNC-CUSTOMERROR-NEXT: completion(nil, error as! CustomError) +// ASYNC-CUSTOMERROR-NEXT: completion(nil, (error as! CustomError)) // ASYNC-CUSTOMERROR-NEXT: } // ASYNC-CUSTOMERROR-NEXT: } // ASYNC-CUSTOMERROR-NEXT: } // ASYNC-CUSTOMERROR: func customError() async throws -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ALIAS %s -func alias(completion: SomeCallback) { } +func alias(completion: @escaping SomeCallback) { } // ASYNC-ALIAS: { -// ASYNC-ALIAS-NEXT: async { +// ASYNC-ALIAS-NEXT: Task { // ASYNC-ALIAS-NEXT: let result = await alias() // ASYNC-ALIAS-NEXT: completion(result) // ASYNC-ALIAS-NEXT: } @@ -143,9 +152,9 @@ func alias(completion: SomeCallback) { } // ASYNC-ALIAS: func alias() async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-NESTEDALIAS %s -func nestedAlias(completion: NestedAliasCallback) { } +func nestedAlias(completion: @escaping NestedAliasCallback) { } // ASYNC-NESTEDALIAS: { -// ASYNC-NESTEDALIAS-NEXT: async { +// ASYNC-NESTEDALIAS-NEXT: Task { // ASYNC-NESTEDALIAS-NEXT: let result = await nestedAlias() // ASYNC-NESTEDALIAS-NEXT: completion(result) // ASYNC-NESTEDALIAS-NEXT: } @@ -153,9 +162,9 @@ func nestedAlias(completion: NestedAliasCallback) { } // ASYNC-NESTEDALIAS: func nestedAlias() async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-SIMPLERESULT %s -func simpleResult(completion: (Result) -> Void) { } +func simpleResult(completion: @escaping (Result) -> Void) { } // ASYNC-SIMPLERESULT: { -// ASYNC-SIMPLERESULT-NEXT: async { +// ASYNC-SIMPLERESULT-NEXT: Task { // ASYNC-SIMPLERESULT-NEXT: let result = await simpleResult() // ASYNC-SIMPLERESULT-NEXT: completion(.success(result)) // ASYNC-SIMPLERESULT-NEXT: } @@ -163,9 +172,9 @@ func simpleResult(completion: (Result) -> Void) { } // ASYNC-SIMPLERESULT: func simpleResult() async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ERRORRESULT %s -func errorResult(completion: (Result) -> Void) { } +func errorResult(completion: @escaping (Result) -> Void) { } // ASYNC-ERRORRESULT: { -// ASYNC-ERRORRESULT-NEXT: async { +// ASYNC-ERRORRESULT-NEXT: Task { // ASYNC-ERRORRESULT-NEXT: do { // ASYNC-ERRORRESULT-NEXT: let result = try await errorResult() // ASYNC-ERRORRESULT-NEXT: completion(.success(result)) @@ -177,9 +186,9 @@ func errorResult(completion: (Result) -> Void) { } // ASYNC-ERRORRESULT: func errorResult() async throws -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-CUSTOMERRORRESULT %s -func customErrorResult(completion: (Result) -> Void) { } +func customErrorResult(completion: @escaping (Result) -> Void) { } // ASYNC-CUSTOMERRORRESULT: { -// ASYNC-CUSTOMERRORRESULT-NEXT: async { +// ASYNC-CUSTOMERRORRESULT-NEXT: Task { // ASYNC-CUSTOMERRORRESULT-NEXT: do { // ASYNC-CUSTOMERRORRESULT-NEXT: let result = try await customErrorResult() // ASYNC-CUSTOMERRORRESULT-NEXT: completion(.success(result)) @@ -191,9 +200,9 @@ func customErrorResult(completion: (Result) -> Void) { } // ASYNC-CUSTOMERRORRESULT: func customErrorResult() async throws -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ALIASRESULT %s -func aliasedResult(completion: SomeResultCallback) { } +func aliasedResult(completion: @escaping SomeResultCallback) { } // ASYNC-ALIASRESULT: { -// ASYNC-ALIASRESULT-NEXT: async { +// ASYNC-ALIASRESULT-NEXT: Task { // ASYNC-ALIASRESULT-NEXT: do { // ASYNC-ALIASRESULT-NEXT: let result = try await aliasedResult() // ASYNC-ALIASRESULT-NEXT: completion(.success(result)) @@ -205,9 +214,9 @@ func aliasedResult(completion: SomeResultCallback) { } // ASYNC-ALIASRESULT: func aliasedResult() async throws -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MANY %s -func many(_ completion: (String, Int) -> Void) { } +func many(_ completion: @escaping (String, Int) -> Void) { } // MANY: { -// MANY-NEXT: async { +// MANY-NEXT: Task { // MANY-NEXT: let result = await many() // MANY-NEXT: completion(result.0, result.1) // MANY-NEXT: } @@ -215,9 +224,9 @@ func many(_ completion: (String, Int) -> Void) { } // MANY: func many() async -> (String, Int) { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-SINGLE %s -func optionalSingle(completion: (String?) -> Void) { } +func optionalSingle(completion: @escaping (String?) -> Void) { } // OPTIONAL-SINGLE: { -// OPTIONAL-SINGLE-NEXT: async { +// OPTIONAL-SINGLE-NEXT: Task { // OPTIONAL-SINGLE-NEXT: let result = await optionalSingle() // OPTIONAL-SINGLE-NEXT: completion(result) // OPTIONAL-SINGLE-NEXT: } @@ -225,9 +234,9 @@ func optionalSingle(completion: (String?) -> Void) { } // OPTIONAL-SINGLE: func optionalSingle() async -> String? { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MANY-OPTIONAL %s -func manyOptional(_ completion: (String?, Int?) -> Void) { } +func manyOptional(_ completion: @escaping (String?, Int?) -> Void) { } // MANY-OPTIONAL: { -// MANY-OPTIONAL-NEXT: async { +// MANY-OPTIONAL-NEXT: Task { // MANY-OPTIONAL-NEXT: let result = await manyOptional() // MANY-OPTIONAL-NEXT: completion(result.0, result.1) // MANY-OPTIONAL-NEXT: } @@ -235,9 +244,9 @@ func manyOptional(_ completion: (String?, Int?) -> Void) { } // MANY-OPTIONAL: func manyOptional() async -> (String?, Int?) { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED %s -func mixed(_ completion: (String?, Int) -> Void) { } +func mixed(_ completion: @escaping (String?, Int) -> Void) { } // MIXED: { -// MIXED-NEXT: async { +// MIXED-NEXT: Task { // MIXED-NEXT: let result = await mixed() // MIXED-NEXT: completion(result.0, result.1) // MIXED-NEXT: } @@ -245,14 +254,23 @@ func mixed(_ completion: (String?, Int) -> Void) { } // MIXED: func mixed() async -> (String?, Int) { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-OPTIONAL-ERROR %s -func mixedOptionalError(_ completion: (String?, Int, Error?) -> Void) { } -// MIXED-OPTIONAL-ERROR-NOT: async { +func mixedOptionalError(_ completion: @escaping (String?, Int, Error?) -> Void) { } +// MIXED-OPTIONAL-ERROR: { +// MIXED-OPTIONAL-ERROR-NEXT: Task { +// MIXED-OPTIONAL-ERROR-NEXT: do { +// MIXED-OPTIONAL-ERROR-NEXT: let result = try await mixedOptionalError() +// MIXED-OPTIONAL-ERROR-NEXT: completion(result.0, result.1, nil) +// MIXED-OPTIONAL-ERROR-NEXT: } catch { +// MIXED-OPTIONAL-ERROR-NEXT: completion(nil, <#Int#>, error) +// MIXED-OPTIONAL-ERROR-NEXT: } +// MIXED-OPTIONAL-ERROR-NEXT: } +// MIXED-OPTIONAL-ERROR-NEXT: } // MIXED-OPTIONAL-ERROR: func mixedOptionalError() async throws -> (String, Int) { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-ERROR %s -func mixedError(_ completion: (String?, Int, Error) -> Void) { } +func mixedError(_ completion: @escaping (String?, Int, Error) -> Void) { } // MIXED-ERROR: { -// MIXED-ERROR-NEXT: async { +// MIXED-ERROR-NEXT: Task { // MIXED-ERROR-NEXT: let result = await mixedError() // MIXED-ERROR-NEXT: completion(result.0, result.1, result.2) // MIXED-ERROR-NEXT: } @@ -260,9 +278,9 @@ func mixedError(_ completion: (String?, Int, Error) -> Void) { } // MIXED-ERROR: func mixedError() async -> (String?, Int, Error) { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=GENERIC %s -func generic(completion: (T, R) -> Void) { } +func generic(completion: @escaping (T, R) -> Void) { } // GENERIC: { -// GENERIC-NEXT: async { +// GENERIC-NEXT: Task { // GENERIC-NEXT: let result: (T, R) = await generic() // GENERIC-NEXT: completion(result.0, result.1) // GENERIC-NEXT: } @@ -270,9 +288,9 @@ func generic(completion: (T, R) -> Void) { } // GENERIC: func generic() async -> (T, R) { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=GENERIC-RESULT %s -func genericResult(completion: (T?, Error?) -> Void) where T: Numeric { } +func genericResult(completion: @escaping (T?, Error?) -> Void) where T: Numeric { } // GENERIC-RESULT: { -// GENERIC-RESULT-NEXT: async { +// GENERIC-RESULT-NEXT: Task { // GENERIC-RESULT-NEXT: do { // GENERIC-RESULT-NEXT: let result: T = try await genericResult() // GENERIC-RESULT-NEXT: completion(result, nil) @@ -285,23 +303,23 @@ func genericResult(completion: (T?, Error?) -> Void) where T: Numeric { } // FIXME: This doesn't compile after refactoring because we aren't using the generic argument `E` in the async method (SR-14560) // RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=GENERIC-ERROR %s -func genericError(completion: (String?, E?) -> Void) where E: Error { } +func genericError(completion: @escaping (String?, E?) -> Void) where E: Error { } // GENERIC-ERROR: { -// GENERIC-ERROR-NEXT: async { +// GENERIC-ERROR-NEXT: Task { // GENERIC-ERROR-NEXT: do { // GENERIC-ERROR-NEXT: let result: String = try await genericError() // GENERIC-ERROR-NEXT: completion(result, nil) // GENERIC-ERROR-NEXT: } catch { -// GENERIC-ERROR-NEXT: completion(nil, error as! E) +// GENERIC-ERROR-NEXT: completion(nil, (error as! E)) // GENERIC-ERROR-NEXT: } // GENERIC-ERROR-NEXT: } // GENERIC-ERROR-NEXT: } // GENERIC-ERROR: func genericError() async throws -> String where E: Error { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OTHER-NAME %s -func otherName(execute: (String) -> Void) { } +func otherName(execute: @escaping (String) -> Void) { } // OTHER-NAME: { -// OTHER-NAME-NEXT: async { +// OTHER-NAME-NEXT: Task { // OTHER-NAME-NEXT: let result = await otherName() // OTHER-NAME-NEXT: execute(result) // OTHER-NAME-NEXT: } @@ -309,9 +327,9 @@ func otherName(execute: (String) -> Void) { } // OTHER-NAME: func otherName() async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DEFAULT_ARGS %s -func defaultArgs(a: Int, b: Int = 10, completion: (String) -> Void) { } +func defaultArgs(a: Int, b: Int = 10, completion: @escaping (String) -> Void) { } // DEFAULT_ARGS: { -// DEFAULT_ARGS-NEXT: async { +// DEFAULT_ARGS-NEXT: Task { // DEFAULT_ARGS-NEXT: let result = await defaultArgs(a: a, b: b) // DEFAULT_ARGS-NEXT: completion(result) // DEFAULT_ARGS-NEXT: } @@ -331,21 +349,21 @@ struct MyStruct { init() { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 - init(completion: (String) -> Void) { } + init(completion: @escaping (String) -> Void) { } func retSelf() -> MyStruct { return self } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):10 | %FileCheck -check-prefix=MODIFIERS %s - public func publicMember(completion: (String) -> Void) { } + public func publicMember(completion: @escaping (String) -> Void) { } // MODIFIERS: public func publicMember() async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=STATIC %s - static func staticMember(completion: (String) -> Void) { } + static func staticMember(completion: @escaping (String) -> Void) { } // STATIC: static func staticMember() async -> String { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):11 | %FileCheck -check-prefix=DEPRECATED %s @available(*, deprecated, message: "Deprecated") - private func deprecated(completion: (String) -> Void) { } + private func deprecated(completion: @escaping (String) -> Void) { } // DEPRECATED: @available(*, deprecated, message: "Deprecated") // DEPRECATED-NEXT: private func deprecated() async -> String { } } @@ -354,64 +372,57 @@ func retStruct() -> MyStruct { return MyStruct() } protocol MyProtocol { // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=PROTO-MEMBER %s // RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=PROTO-MEMBER %s - func protoMember(completion: (String) -> Void) + func protoMember(completion: @escaping (String) -> Void) // PROTO-MEMBER: func protoMember() async -> String{{$}} } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-COMPLETION %s +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-COMPLETION %s func nonCompletion(a: Int) { } // NON-COMPLETION: func nonCompletion(a: Int) async { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-RESULTS %s -func multipleResults(completion: (Result, Result) -> Void) { } -// MULTIPLE-RESULTS: func multipleResults(completion: (Result, Result) -> Void) async { } +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-ESCAPING-COMPLETION %s +func nonEscapingCompletion(completion: (Int) -> Void) { } +// NON-ESCAPING-COMPLETION: func nonEscapingCompletion(completion: (Int) -> Void) async { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NOT-LAST %s -func completionNotLast(completion: (String) -> Void, a: Int) { } -// NOT-LAST: func completionNotLast(completion: (String) -> Void, a: Int) async { } +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-RESULTS %s +func multipleResults(completion: @escaping (Result, Result) -> Void) { } +// MULTIPLE-RESULTS: func multipleResults(completion: @escaping (Result, Result) -> Void) async { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -func nonVoid(completion: (String) -> Void) -> Int { return 0 } +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-VOID %s +func nonVoid(completion: @escaping (String) -> Void) -> Int { return 0 } +// NON-VOID: func nonVoid(completion: @escaping (String) -> Void) async -> Int { return 0 } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=COMPLETION-NON-VOID %s -func completionNonVoid(completion: (String) -> Int) -> Void { } -// COMPLETION-NON-VOID: func completionNonVoid(completion: (String) -> Int) async -> Void { } +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=COMPLETION-NON-VOID %s +func completionNonVoid(completion: @escaping (String) -> Int) -> Void { } +// COMPLETION-NON-VOID: func completionNonVoid(completion: @escaping (String) -> Int) async -> Void { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 -func alreadyThrows(completion: (String) -> Void) throws { } +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ALREADY-THROWS %s +func alreadyThrows(completion: @escaping (String) -> Void) throws { } +// ALREADY-THROWS: func alreadyThrows(completion: @escaping (String) -> Void) async throws { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=AUTO-CLOSURE %s -func noParamAutoclosure(completion: @autoclosure () -> Void) { } -// AUTO-CLOSURE: func noParamAutoclosure(completion: @autoclosure () -> Void) async { } +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=AUTO-CLOSURE %s +func noParamAutoclosure(completion: @escaping @autoclosure () -> Void) { } +// AUTO-CLOSURE: func noParamAutoclosure(completion: @escaping @autoclosure () -> Void) async { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix BLOCK-CONVENTION %s -func blockConvention(completion: @convention(block) () -> Void) { } +func blockConvention(completion: @escaping @convention(block) () -> Void) { } // BLOCK-CONVENTION: func blockConvention() async { } // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix C-CONVENTION %s -func cConvention(completion: @convention(c) () -> Void) { } +func cConvention(completion: @escaping @convention(c) () -> Void) { } // C-CONVENTION: func cConvention() async { } -// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-HANDLER %s -func voidCompletion(completion: (Void) -> Void) {} -// VOID-HANDLER: { -// VOID-HANDLER-NEXT: async { -// VOID-HANDLER-NEXT: await voidCompletion() -// VOID-HANDLER-NEXT: completion(()) -// VOID-HANDLER-NEXT: } -// VOID-HANDLER-NEXT: } -// VOID-HANDLER: func voidCompletion() async {} - // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix OPT-VOID-AND-ERROR-HANDLER %s -func optVoidAndErrorCompletion(completion: (Void?, Error?) -> Void) {} +func optVoidAndErrorCompletion(completion: @escaping (Void?, Error?) -> Void) {} // OPT-VOID-AND-ERROR-HANDLER: { -// OPT-VOID-AND-ERROR-HANDLER-NEXT: async { +// OPT-VOID-AND-ERROR-HANDLER-NEXT: Task { // OPT-VOID-AND-ERROR-HANDLER-NEXT: do { // OPT-VOID-AND-ERROR-HANDLER-NEXT: try await optVoidAndErrorCompletion() // OPT-VOID-AND-ERROR-HANDLER-NEXT: completion((), nil) @@ -423,9 +434,9 @@ func optVoidAndErrorCompletion(completion: (Void?, Error?) -> Void) {} // OPT-VOID-AND-ERROR-HANDLER: func optVoidAndErrorCompletion() async throws {} // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix TOO-MUCH-VOID-AND-ERROR-HANDLER %s -func tooMuchVoidAndErrorCompletion(completion: (Void?, Void?, Error?) -> Void) {} +func tooMuchVoidAndErrorCompletion(completion: @escaping (Void?, Void?, Error?) -> Void) {} // TOO-MUCH-VOID-AND-ERROR-HANDLER: { -// TOO-MUCH-VOID-AND-ERROR-HANDLER-NEXT: async { +// TOO-MUCH-VOID-AND-ERROR-HANDLER-NEXT: Task { // TOO-MUCH-VOID-AND-ERROR-HANDLER-NEXT: do { // TOO-MUCH-VOID-AND-ERROR-HANDLER-NEXT: try await tooMuchVoidAndErrorCompletion() // TOO-MUCH-VOID-AND-ERROR-HANDLER-NEXT: completion((), (), nil) @@ -437,9 +448,9 @@ func tooMuchVoidAndErrorCompletion(completion: (Void?, Void?, Error?) -> Void) { // TOO-MUCH-VOID-AND-ERROR-HANDLER: func tooMuchVoidAndErrorCompletion() async throws {} // RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-PROPER-AND-ERROR-HANDLER %s -func tooVoidProperAndErrorCompletion(completion: (Void?, String?, Error?) -> Void) {} +func tooVoidProperAndErrorCompletion(completion: @escaping (Void?, String?, Error?) -> Void) {} // VOID-PROPER-AND-ERROR-HANDLER: { -// VOID-PROPER-AND-ERROR-HANDLER-NEXT: async { +// VOID-PROPER-AND-ERROR-HANDLER-NEXT: Task { // VOID-PROPER-AND-ERROR-HANDLER-NEXT: do { // VOID-PROPER-AND-ERROR-HANDLER-NEXT: let result = try await tooVoidProperAndErrorCompletion() // VOID-PROPER-AND-ERROR-HANDLER-NEXT: completion((), result.1, nil) @@ -450,17 +461,41 @@ func tooVoidProperAndErrorCompletion(completion: (Void?, String?, Error?) -> Voi // VOID-PROPER-AND-ERROR-HANDLER-NEXT: } // VOID-PROPER-AND-ERROR-HANDLER: func tooVoidProperAndErrorCompletion() async throws -> (Void, String) {} -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-HANDLER %s -func voidAndErrorCompletion(completion: (Void, Error?) -> Void) {} -// VOID-AND-ERROR-HANDLER-NOT: async { +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-HANDLER %s +func voidAndErrorCompletion(completion: @escaping (Void, Error?) -> Void) {} +// VOID-AND-ERROR-HANDLER: { +// VOID-AND-ERROR-HANDLER-NEXT: Task { +// VOID-AND-ERROR-HANDLER-NEXT: do { +// VOID-AND-ERROR-HANDLER-NEXT: try await voidAndErrorCompletion() +// VOID-AND-ERROR-HANDLER-NEXT: completion((), nil) +// VOID-AND-ERROR-HANDLER-NEXT: } catch { +// VOID-AND-ERROR-HANDLER-NEXT: completion((), error) +// VOID-AND-ERROR-HANDLER-NEXT: } +// VOID-AND-ERROR-HANDLER-NEXT: } +// VOID-AND-ERROR-HANDLER-NEXT: } // VOID-AND-ERROR-HANDLER: func voidAndErrorCompletion() async throws {} +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-RESULT-HANDLER %s +func voidResultCompletion(completion: @escaping (Result) -> Void) {} +// VOID-RESULT-HANDLER: { +// VOID-RESULT-HANDLER-NEXT: Task { +// VOID-RESULT-HANDLER-NEXT: do { +// VOID-RESULT-HANDLER-NEXT: try await voidResultCompletion() +// VOID-RESULT-HANDLER-NEXT: completion(.success(())) +// VOID-RESULT-HANDLER-NEXT: } catch { +// VOID-RESULT-HANDLER-NEXT: completion(.failure(error)) +// VOID-RESULT-HANDLER-NEXT: } +// VOID-RESULT-HANDLER-NEXT: } +// VOID-RESULT-HANDLER-NEXT: } +// VOID-RESULT-HANDLER: func voidResultCompletion() async throws { + + // 2. Check that the various ways to call a function (and the positions the // refactoring is called from) are handled correctly class MyClass {} -func simpleClassParam(completion: (MyClass) -> Void) { } +func simpleClassParam(completion: @escaping (MyClass) -> Void) { } // TODO: We cannot check that the refactored code compiles because 'simple' and // friends aren't refactored when only invoking the refactoring on this function. @@ -471,9 +506,8 @@ func simpleClassParam(completion: (MyClass) -> Void) { } // before the 'RUN' lines were removed, thus pointing past the end of the // rewritten buffer. -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefixes=CONVERT-FUNC,CALL,CALL-NOLABEL,CALL-WRAPPED,TRAILING,TRAILING-PARENS,TRAILING-WRAPPED,CALL-ARG,MANY-CALL,MEMBER-CALL,MEMBER-CALL2,MEMBER-PARENS,EMPTY-CAPTURE,CAPTURE,DEFAULT-ARGS-MISSING,DEFAULT-ARGS-CALL,BLOCK-CONVENTION-CALL,C-CONVENTION-CALL,VOID-AND-ERROR-CALL,VOID-AND-ERROR-CALL2,VOID-AND-ERROR-CALL3,VOID-AND-ERROR-CALL4 %s -func testCalls() { -// CONVERT-FUNC: {{^}}func testCalls() async { +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CALL %s +func testSimple() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+4):3 | %FileCheck -check-prefix=CALL %s // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):10 // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):24 @@ -482,177 +516,373 @@ func testCalls() { // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):5 print("with label") }) - // CALL: let str = await simple(){{$}} - // CALL-NEXT: {{^}}print("with label") +} +// CALL: let str = await simple(){{$}} +// CALL-NEXT: // +// CALL-NEXT: {{^}} print("with label") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CALL-NOLABEL %s +func testSimpleWithoutLabel() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=CALL-NOLABEL %s simpleWithoutLabel({ str in print("without label") }) - // CALL-NOLABEL: let str = await simpleWithoutLabel(){{$}} - // CALL-NOLABEL-NEXT: {{^}}print("without label") +} +// CALL-NOLABEL: let str = await simpleWithoutLabel(){{$}} +// CALL-NOLABEL-NEXT: {{^}}print("without label") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CALL-WRAPPED %s +func testWrapped() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=CALL-WRAPPED %s // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):5 | %FileCheck -check-prefix=CALL-WRAPPED %s ((simple))(completion: { str in print("wrapped call") }) - // CALL-WRAPPED: let str = await ((simple))(){{$}} - // CALL-WRAPPED-NEXT: {{^}}print("wrapped call") +} +// CALL-WRAPPED: let str = await ((simple))(){{$}} +// CALL-WRAPPED-NEXT: {{^}}print("wrapped call") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=TRAILING %s +func testTrailing() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=TRAILING %s // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):12 simple { str in print("trailing") } - // TRAILING: let str = await simple(){{$}} - // TRAILING-NEXT: {{^}}print("trailing") +} +// TRAILING: let str = await simple(){{$}} +// TRAILING-NEXT: {{^}}print("trailing") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=TRAILING-PARENS %s +func testTrailingParens() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=TRAILING-PARENS %s simple() { str in print("trailing with parens") } - // TRAILING-PARENS: let str = await simple(){{$}} - // TRAILING-PARENS-NEXT: {{^}}print("trailing with parens") +} +// TRAILING-PARENS: let str = await simple(){{$}} +// TRAILING-PARENS-NEXT: {{^}}print("trailing with parens") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefixes=TRAILING-WRAPPED %s +func testTrailingWrapped() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):5 | %FileCheck -check-prefix=TRAILING-WRAPPED %s ((simple)) { str in print("trailing with wrapped call") } - // TRAILING-WRAPPED: let str = await ((simple))(){{$}} - // TRAILING-WRAPPED-NEXT: {{^}}print("trailing with wrapped call") +} +// TRAILING-WRAPPED: let str = await ((simple))(){{$}} +// TRAILING-WRAPPED-NEXT: {{^}}print("trailing with wrapped call") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CALL-ARG %s +func testCallArg() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):3 | %FileCheck -check-prefix=CALL-ARG %s // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):17 // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):20 simpleWithArg(a: 10) { str in print("with arg") } - // CALL-ARG: let str = await simpleWithArg(a: 10){{$}} - // CALL-ARG-NEXT: {{^}}print("with arg") +} +// CALL-ARG: let str = await simpleWithArg(a: 10){{$}} +// CALL-ARG-NEXT: {{^}}print("with arg") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefixes=MANY-CALL %s +func testMany() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANY-CALL %s many { str, num in print("many") } - // MANY-CALL: let (str, num) = await many(){{$}} - // MANY-CALL-NEXT: {{^}}print("many") +} +// MANY-CALL: let (str, num) = await many(){{$}} +// MANY-CALL-NEXT: {{^}}print("many") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MEMBER-CALL %s +func testMember() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):15 | %FileCheck -check-prefix=MEMBER-CALL %s // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MEMBER-CALL %s retStruct().publicMember { str in print("call on member") } - // MEMBER-CALL: let str = await retStruct().publicMember(){{$}} - // MEMBER-CALL-NEXT: {{^}}print("call on member") +} +// MEMBER-CALL: let str = await retStruct().publicMember(){{$}} +// MEMBER-CALL-NEXT: {{^}}print("call on member") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MEMBER-CALL2 %s +func testMember2() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):25 | %FileCheck -check-prefix=MEMBER-CALL2 %s retStruct().retSelf().publicMember { str in print("call on member 2") } - // MEMBER-CALL2: let str = await retStruct().retSelf().publicMember(){{$}} - // MEMBER-CALL2-NEXT: {{^}}print("call on member 2") +} +// MEMBER-CALL2: let str = await retStruct().retSelf().publicMember(){{$}} +// MEMBER-CALL2-NEXT: {{^}}print("call on member 2") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MEMBER-PARENS %s +func testMemberParens() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):3 | %FileCheck -check-prefix=MEMBER-PARENS %s // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):5 | %FileCheck -check-prefix=MEMBER-PARENS %s // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):15 | %FileCheck -check-prefix=MEMBER-PARENS %s (((retStruct().retSelf()).publicMember)) { str in print("call on member parens") } - // MEMBER-PARENS: let str = await (((retStruct().retSelf()).publicMember))(){{$}} - // MEMBER-PARENS-NEXT: {{^}}print("call on member parens") +} +// MEMBER-PARENS: let str = await (((retStruct().retSelf()).publicMember))(){{$}} +// MEMBER-PARENS-NEXT: {{^}}print("call on member parens") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefixes=SKIP-ASSIGN-FUNC %s +func testSkipAssign() { // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):13 let _: Void = simple { str in print("assigned") } - // CONVERT-FUNC: let _: Void = simple { str in{{$}} - // CONVERT-FUNC-NEXT: print("assigned"){{$}} - // CONVERT-FUNC-NEXT: }{{$}} +} +// SKIP-ASSIGN-FUNC: {{^}}func testSkipAssign() async { +// SKIP-ASSIGN-FUNC: let _: Void = simple { str in{{$}} +// SKIP-ASSIGN-FUNC-NEXT: print("assigned"){{$}} +// SKIP-ASSIGN-FUNC-NEXT: }{{$}} + +// Same as noParamAutoclosure defined above, but used just for the test below. +// This avoids a compiler error when converting noParamAutoclosure to async. +func noParamAutoclosure2(completion: @escaping @autoclosure () -> Void) {} +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefixes=SKIP-AUTOCLOSURE-FUNC %s +func testSkipAutoclosure() { // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 - noParamAutoclosure(completion: print("autoclosure")) - // CONVERT-FUNC: noParamAutoclosure(completion: print("autoclosure")){{$}} + noParamAutoclosure2(completion: print("autoclosure")) +} +// SKIP-AUTOCLOSURE-FUNC: {{^}}func testSkipAutoclosure() async { +// SKIP-AUTOCLOSURE-FUNC: noParamAutoclosure2(completion: print("autoclosure")){{$}} +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=EMPTY-CAPTURE %s +func testEmptyCapture() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=EMPTY-CAPTURE %s simple { [] str in print("closure with empty capture list") } - // EMPTY-CAPTURE: let str = await simple(){{$}} - // EMPTY-CAPTURE-NEXT: {{^}}print("closure with empty capture list") +} +// EMPTY-CAPTURE: let str = await simple(){{$}} +// EMPTY-CAPTURE-NEXT: {{^}}print("closure with empty capture list") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CAPTURE %s +func testCapture() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=CAPTURE %s let myClass = MyClass() simpleClassParam { [unowned myClass] str in print("closure with capture list \(myClass)") } - // CAPTURE: let str = await simpleClassParam(){{$}} - // CAPTURE-NEXT: {{^}}print("closure with capture list \(myClass)") +} +// CAPTURE: let str = await simpleClassParam(){{$}} +// CAPTURE-NEXT: {{^}}print("closure with capture list \(myClass)") - // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=OTHER-DIRECT %s +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefixes=NOT-HANDLER-FUNC %s +func testNotCompletionHandler() { + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOT-HANDLER %s otherName(execute: { str in print("otherName") }) - // OTHER-DIRECT: let str = await otherName(){{$}} - // OTHER-DIRECT-NEXT: {{^}}print("otherName") - // CONVERT-FUNC: otherName(execute: { str in{{$}} - // CONVERT-FUNC-NEXT: print("otherName"){{$}} - // CONVERT-FUNC-NEXT: }){{$}} - +} +// NOT-HANDLER-FUNC: otherName(execute: { str in{{$}} +// NOT-HANDLER-FUNC-NEXT: print("otherName"){{$}} +// NOT-HANDLER-FUNC-NEXT: }){{$}} +// NOT-HANDLER: let str = await otherName(){{$}} +// NOT-HANDLER-NEXT: {{^}}print("otherName") + +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DEFAULT-ARGS-MISSING %s +func testDefaultArgsMissing() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DEFAULT-ARGS-MISSING %s defaultArgs(a: 1) { str in print("defaultArgs missing") } - // DEFAULT-ARGS-MISSING: let str = await defaultArgs(a: 1){{$}} - // DEFAULT-ARGS-MISSING-NEXT: {{^}}print("defaultArgs missing") +} +// DEFAULT-ARGS-MISSING: let str = await defaultArgs(a: 1){{$}} +// DEFAULT-ARGS-MISSING-NEXT: {{^}}print("defaultArgs missing") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DEFAULT-ARGS-CALL %s +func testDefaultArgs() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DEFAULT-ARGS-CALL %s defaultArgs(a: 1, b: 2) { str in print("defaultArgs") } - // DEFAULT-ARGS-CALL: let str = await defaultArgs(a: 1, b: 2){{$}} - // DEFAULT-ARGS-CALL-NEXT: {{^}}print("defaultArgs") +} +// DEFAULT-ARGS-CALL: let str = await defaultArgs(a: 1, b: 2){{$}} +// DEFAULT-ARGS-CALL-NEXT: {{^}}print("defaultArgs") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=BLOCK-CONVENTION-CALL %s +func testBlockConvention() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BLOCK-CONVENTION-CALL %s blockConvention { print("blockConvention") } - // BLOCK-CONVENTION-CALL: await blockConvention(){{$}} - // BLOCK-CONVENTION-CALL-NEXT: {{^}}print("blockConvention") +} +// BLOCK-CONVENTION-CALL: await blockConvention(){{$}} +// BLOCK-CONVENTION-CALL-NEXT: {{^}}print("blockConvention") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=C-CONVENTION-CALL %s +func testCConvention() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=C-CONVENTION-CALL %s cConvention { print("cConvention") } - // C-CONVENTION-CALL: await cConvention(){{$}} - // C-CONVENTION-CALL-NEXT: {{^}}print("cConvention") +} +// C-CONVENTION-CALL: await cConvention(){{$}} +// C-CONVENTION-CALL-NEXT: {{^}}print("cConvention") +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-AND-ERROR-CALL %s +func testVoidAndError() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=VOID-AND-ERROR-CALL %s optVoidAndErrorCompletion { v, err in - print("opt void and error completion \(v)") + print("opt void and error completion \(String(describing: v))") } - // VOID-AND-ERROR-CALL: try await optVoidAndErrorCompletion(){{$}} - // VOID-AND-ERROR-CALL-NEXT: {{^}}print("opt void and error completion \(<#v#>)"){{$}} +} +// VOID-AND-ERROR-CALL: try await optVoidAndErrorCompletion(){{$}} +// VOID-AND-ERROR-CALL-NEXT: {{^}}print("opt void and error completion \(String(describing: <#v#>))"){{$}} +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-AND-ERROR-CALL2 %s +func testVoidAndError2() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=VOID-AND-ERROR-CALL2 %s optVoidAndErrorCompletion { _, err in print("opt void and error completion 2") } - // VOID-AND-ERROR-CALL2: try await optVoidAndErrorCompletion(){{$}} - // VOID-AND-ERROR-CALL2-NEXT: {{^}}print("opt void and error completion 2"){{$}} +} +// VOID-AND-ERROR-CALL2: try await optVoidAndErrorCompletion(){{$}} +// VOID-AND-ERROR-CALL2-NEXT: {{^}}print("opt void and error completion 2"){{$}} +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-AND-ERROR-CALL3 %s +func testVoidAndError3() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=VOID-AND-ERROR-CALL3 %s tooMuchVoidAndErrorCompletion { v, v1, err in print("void and error completion 3") } - // VOID-AND-ERROR-CALL3: try await tooMuchVoidAndErrorCompletion(){{$}} - // VOID-AND-ERROR-CALL3-NEXT: {{^}}print("void and error completion 3"){{$}} +} +// VOID-AND-ERROR-CALL3: try await tooMuchVoidAndErrorCompletion(){{$}} +// VOID-AND-ERROR-CALL3-NEXT: {{^}}print("void and error completion 3"){{$}} +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-AND-ERROR-CALL4 %s +func testVoidAndError4() { // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=VOID-AND-ERROR-CALL4 %s voidAndErrorCompletion { v, err in print("void and error completion \(v)") } - // VOID-AND-ERROR-CALL4: try await voidAndErrorCompletion(){{$}} - // VOID-AND-ERROR-CALL4-NEXT: {{^}}print("void and error completion \(<#v#>)"){{$}} } -// CONVERT-FUNC: {{^}}} +// VOID-AND-ERROR-CALL4: try await voidAndErrorCompletion(){{$}} +// VOID-AND-ERROR-CALL4-NEXT: {{^}}print("void and error completion \(<#v#>)"){{$}} + +func testPreserveComments() { + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=PRESERVE-COMMENTS %s + simpleWithArg(/*hello*/ a: /*a*/5) { str in + // b1 + // b2 + print("1") + // c + print("2") /* + d1 + d2 + */ + if .random() { + // e + } + /* f1 */ + /* f2 */} // don't pick this up +} +// PRESERVE-COMMENTS: let str = await simpleWithArg(/*hello*/ a: /*a*/5) +// PRESERVE-COMMENTS-NEXT: // b1 +// PRESERVE-COMMENTS-NEXT: // b2 +// PRESERVE-COMMENTS-NEXT: print("1") +// PRESERVE-COMMENTS-NEXT: // c +// PRESERVE-COMMENTS-NEXT: print("2") +// PRESERVE-COMMENTS-NEXT: /* +// PRESERVE-COMMENTS-NEXT: d1 +// PRESERVE-COMMENTS-NEXT: d2 +// PRESERVE-COMMENTS-NEXT: */ +// PRESERVE-COMMENTS-NEXT: if .random() { +// PRESERVE-COMMENTS-NEXT: // e +// PRESERVE-COMMENTS-NEXT: } +// PRESERVE-COMMENTS-NEXT: /* f1 */ +// PRESERVE-COMMENTS-NEXT: /* f2 */{{$}} +// PRESERVE-COMMENTS-NOT: }{{$}} + +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=PRESERVE-COMMENTS-ERROR %s +func testPreserveComments2() { + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=PRESERVE-COMMENTS-ERROR %s + errorOnly { err in + // a + if err != nil { + // b + print("oh no") // c + /* d */ + return /* e */ + } + if err != nil { + // f + print("fun") + // g + } + // h + print("good times") // i + } +} +// PRESERVE-COMMENTS-ERROR: do { +// PRESERVE-COMMENTS-ERROR-NEXT: try await errorOnly() +// PRESERVE-COMMENTS-ERROR-NEXT: // a +// PRESERVE-COMMENTS-ERROR-NEXT: // h +// PRESERVE-COMMENTS-ERROR-NEXT: print("good times") +// PRESERVE-COMMENTS-ERROR-NEXT: // i +// PRESERVE-COMMENTS-ERROR: } catch let err { +// PRESERVE-COMMENTS-ERROR-NEXT: // b +// PRESERVE-COMMENTS-ERROR-NEXT: print("oh no") +// PRESERVE-COMMENTS-ERROR-NEXT: // c +// PRESERVE-COMMENTS-ERROR-NEXT: /* d */ +// PRESERVE-COMMENTS-ERROR-NEXT: /* e */ +// PRESERVE-COMMENTS-ERROR-NEXT: // f +// PRESERVE-COMMENTS-ERROR-NEXT: print("fun") +// PRESERVE-COMMENTS-ERROR-NEXT: // g +// PRESERVE-COMMENTS-ERROR-NEXT: {{ }} +// PRESERVE-COMMENTS-ERROR-NEXT: } + +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=PRESERVE-TRAILING-COMMENT-FN %s +func testPreserveComments3() { + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=PRESERVE-TRAILING-COMMENT-CALL %s + simple { s in + print(s) + } + // make sure we pickup this trailing comment if we're converting the function, but not the call +} +// PRESERVE-TRAILING-COMMENT-FN: func testPreserveComments3() async { +// PRESERVE-TRAILING-COMMENT-FN-NEXT: // +// PRESERVE-TRAILING-COMMENT-FN-NEXT: let s = await simple() +// PRESERVE-TRAILING-COMMENT-FN-NEXT: print(s) +// PRESERVE-TRAILING-COMMENT-FN-NEXT: // make sure we pickup this trailing comment if we're converting the function, but not the call +// PRESERVE-TRAILING-COMMENT-FN-NEXT: } + +// PRESERVE-TRAILING-COMMENT-CALL: let s = await simple() +// PRESERVE-TRAILING-COMMENT-CALL-NEXT: print(s) +// PRESERVE-TRAILING-COMMENT-CALL-NOT: // make sure we pickup this trailing comment if we're converting the function, but not the call + +class TestConvertFunctionWithCallToFunctionsWithSpecialName { + required init() {} + subscript(index: Int) -> Int { return index } + +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):3 + static func example() -> Self { + let x = self.init() + _ = x[1] + return x + } +} + +// rdar://78781061 +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOR-IN-WHERE %s +func testForInWhereRefactoring() { + let arr: [String] = [] + for str in arr where str.count != 0 { + simple { res in + print(res) + } + } +} +// FOR-IN-WHERE: func testForInWhereRefactoring() async { +// FOR-IN-WHERE-NEXT: let arr: [String] = [] +// FOR-IN-WHERE-NEXT: for str in arr where str.count != 0 { +// FOR-IN-WHERE-NEXT: let res = await simple() +// FOR-IN-WHERE-NEXT: print(res) +// FOR-IN-WHERE-NEXT: } +// FOR-IN-WHERE-NEXT: } diff --git a/test/refactoring/ConvertAsync/check_compiles.swift b/test/refactoring/ConvertAsync/check_compiles.swift new file mode 100644 index 0000000000000..8df2156b6c1f6 --- /dev/null +++ b/test/refactoring/ConvertAsync/check_compiles.swift @@ -0,0 +1,16 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +func simple(completion: @escaping () -> Void) { } +func anything() -> Bool { return true } + +// RUN: %swift-frontend -typecheck %s +// RUN: not %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 +func cannotCompile() { + simple { + if anything() { + return + } + } +} diff --git a/test/refactoring/ConvertAsync/convert_async_attributed_renames.swift b/test/refactoring/ConvertAsync/convert_async_attributed_renames.swift new file mode 100644 index 0000000000000..12ef404c262aa --- /dev/null +++ b/test/refactoring/ConvertAsync/convert_async_attributed_renames.swift @@ -0,0 +1,243 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +@available(*, renamed: "simple2") +func simple(_ completion: @escaping (String) -> Void) { } +@available(*, renamed: "simple2") +func nonCompletionName(_ random: @escaping (String) -> Void) { } +func simple2() async -> String { return "" } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 | %FileCheck -check-prefix=SIMPLERENAMED %s +func simpleRenamed() { + // preserve me + simple { str in + print(str) + } + // and me + nonCompletionName { str in + print(str) + } +} +// SIMPLERENAMED: func simpleRenamed() async { +// SIMPLERENAMED-NEXT: // preserve me +// SIMPLERENAMED-NEXT: let str = await simple2() +// SIMPLERENAMED-NEXT: print(str) +// SIMPLERENAMED-NEXT: // and me +// SIMPLERENAMED-NEXT: let str1 = await simple2() +// SIMPLERENAMED-NEXT: print(str1) +// SIMPLERENAMED-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 | %FileCheck -check-prefix=RENAMEDCOLLISION %s +func renamedCollision() { + simple { simple2 in + print(simple2) + } +} +// RENAMEDCOLLISION: func renamedCollision() async { +// RENAMEDCOLLISION-NEXT: let simple21 = await simple2() +// RENAMEDCOLLISION-NEXT: print(simple21) +// RENAMEDCOLLISION-NEXT: } + +@available(*, renamed: "simpleArg2") +func simpleArgRenamed(arg: String, _ random: @escaping (String) -> Void) { } +@available(*, renamed: "simpleArg2") +func completionFirstArg(random: @escaping (String) -> Void, arg: String) { } +func simpleArg2(newArg: String) async -> String { return "" } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 | %FileCheck -check-prefix=ARGRENAMED %s +func argRenamed() { + (simpleArgRenamed)(arg: "foo") { str in + print(str) + } + (completionFirstArg)(random: { str in + print(str) + }, arg: "foo") +} +// ARGRENAMED: func argRenamed() async { +// ARGRENAMED-NEXT: let str = await (simpleArg2)(newArg: "foo") +// ARGRENAMED-NEXT: print(str) +// ARGRENAMED-NEXT: let str1 = await (simpleArg2)(newArg: "foo") +// ARGRENAMED-NEXT: print(str1) +// ARGRENAMED-NEXT: } + +@available(*, renamed: "multiHandlers2") +func multiHandlers(arg: String, handler1: @escaping (String) -> Void, handler2: @escaping (String) -> Void) { } +func multiHandlers2(newArg: String, newHandler: @escaping (String) -> Void) async -> String { return "" } + +@available(*, renamed: "multiHandlersWithTrailing2") +func multiHandlersWithTrailing(arg: String, handler1: @escaping (String) -> Void, handler2: @escaping (String) -> Void, other: String) { } +func multiHandlersWithTrailing2(newArg: String, newHandler: @escaping (String) -> Void, other: String) async -> String { return "" } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 | %FileCheck -check-prefix=MULTIPLEHANDLERS %s +func multipleHandlers() { + multiHandlers(arg: "foo", handler1: { str1 in + print(str1) + }, handler2: { str2 in + print(str2) + }) + multiHandlers(arg: "foo", handler1: { str3 in + print(str3) + }) { str4 in + print(str4) + } + multiHandlers(arg: "foo") { str5 in + print(str5) + } handler2: { str6 in + print(str6) + } + multiHandlersWithTrailing(arg: "foo", handler1: { str7 in + print(str7) + }, handler2: { str8 in + print(str8) + }, other: "bar") +} +// MULTIPLEHANDLERS: func multipleHandlers() async { +// MULTIPLEHANDLERS-NEXT: let str2 = await multiHandlers2(newArg: "foo", newHandler: { str1 in +// MULTIPLEHANDLERS-NEXT: print(str1) +// MULTIPLEHANDLERS-NEXT: }) +// MULTIPLEHANDLERS-NEXT: print(str2) +// MULTIPLEHANDLERS-NEXT: let str4 = await multiHandlers2(newArg: "foo", newHandler: { str3 in +// MULTIPLEHANDLERS-NEXT: print(str3) +// MULTIPLEHANDLERS-NEXT: }) +// MULTIPLEHANDLERS-NEXT: print(str4) +// MULTIPLEHANDLERS-NEXT: let str6 = await multiHandlers2(newArg: "foo", newHandler: { str5 in +// MULTIPLEHANDLERS-NEXT: print(str5) +// MULTIPLEHANDLERS-NEXT: }) +// MULTIPLEHANDLERS-NEXT: print(str6) +// MULTIPLEHANDLERS-NEXT: let str8 = await multiHandlersWithTrailing2(newArg: "foo", newHandler: { str7 in +// MULTIPLEHANDLERS-NEXT: print(str7) +// MULTIPLEHANDLERS-NEXT: }, other: "bar") +// MULTIPLEHANDLERS-NEXT: print(str8) +// MULTIPLEHANDLERS-NEXT: } + +@available(*, renamed: "defaultedParamsStartNew(newArg:newArg1:)") +func defaultedParamsStart(arg1: Int, completionHandler: @escaping (String) -> Void) { } +func defaultedParamsStartNew(newArg: Int = 0, newArg1: Int) async -> String { return "" } + +@available(*, renamed: "defaultedParamsMiddleNew(newArg1:newArg:newArg2:)") +func defaultedParamsMiddle(arg1: Int, arg2: Int, completionHandler: @escaping (String) -> Void) { } +func defaultedParamsMiddleNew(newArg1: Int, newArg: Int = 0, newArg2: Int) async -> String { return "" } + +@available(*, renamed: "defaultedParamsEndNew(newArg1:newArg:)") +func defaultedParamsEnd(arg1: Int, completionHandler: @escaping (String) -> Void) { } +func defaultedParamsEndNew(newArg1: Int, newArg: Int = 0) async -> String { return "" } + +@available(*, renamed: "defaultedSameLabelNew(newArg1:arg1:newArg2:)") +func defaultedSameLabel(arg1: Int, completionHandler: @escaping (String) -> Void) { } +func defaultedSameLabelNew(newArg1: Int = 0, arg1: Int = 0, newArg2: Int = 0) async -> String { return "" } + +@available(*, renamed: "unlabelledArgNew(newArg1:_:newArg2:)") +func unlabelledArg(_ arg1: Int, completionHandler: @escaping (String) -> Void) { } +func unlabelledArgNew(newArg1: Int = 0, _ arg1: Int = 0, newArg2: Int = 0) async -> String { return "" } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 | %FileCheck -check-prefix=DEFAULTEDPARAMS %s +func defaultedParams() { + defaultedParamsStart(arg1: 1) { str in + print(str) + } + defaultedParamsMiddle(arg1: 1, arg2: 2) { str in + print(str) + } + defaultedParamsEnd(arg1: 1) { str in + print(str) + } + defaultedSameLabel(arg1: 1) { str in + print(str) + } + unlabelledArg(1) { str in + print(str) + } +} +// DEFAULTEDPARAMS: func defaultedParams() async { +// DEFAULTEDPARAMS-NEXT: let str = await defaultedParamsStartNew(newArg1: 1) +// DEFAULTEDPARAMS-NEXT: print(str) +// DEFAULTEDPARAMS-NEXT: let str1 = await defaultedParamsMiddleNew(newArg1: 1, newArg2: 2) +// DEFAULTEDPARAMS-NEXT: print(str1) +// DEFAULTEDPARAMS-NEXT: let str2 = await defaultedParamsEndNew(newArg1: 1) +// DEFAULTEDPARAMS-NEXT: print(str2) +// DEFAULTEDPARAMS-NEXT: let str3 = await defaultedSameLabelNew(arg1: 1) +// DEFAULTEDPARAMS-NEXT: print(str3) +// DEFAULTEDPARAMS-NEXT: let str4 = await unlabelledArgNew(1) +// DEFAULTEDPARAMS-NEXT: print(str4) +// DEFAULTEDPARAMS-NEXT: } + +struct SomeStruct { + var instanceProp: String { get async { "" } } + static var classProp: String { get async { "" } } + + @available(*, renamed: "simple2") + func simple(_ completion: @escaping (String) -> Void) { } + func simple2() async -> String { return "" } + + @available(*, renamed: "getter:instanceProp()") + func instanceGetter(_ completion: @escaping (String) -> Void) { } + @available(*, renamed: "getter:classProp()") + static func classGetter(_ completion: @escaping (String) -> Void) { } +} + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 | %FileCheck -check-prefix=MEMBERS %s +func members(s: SomeStruct) { + s.simple { str in + print(str) + } + (((s).simple)) { str in + print(str) + } + s.instanceGetter { str in + print(str) + } + (((s).instanceGetter)) { str in + print(str) + } + SomeStruct.classGetter { str in + print(str) + } + (((SomeStruct).classGetter)) { str in + print(str) + } +} +// MEMBERS: func members(s: SomeStruct) async { +// MEMBERS-NEXT: let str = await s.simple2() +// MEMBERS-NEXT: print(str) +// MEMBERS-NEXT: let str1 = await (((s).simple2))() +// MEMBERS-NEXT: print(str1) +// MEMBERS-NEXT: let str2 = await s.instanceProp +// MEMBERS-NEXT: print(str2) +// MEMBERS-NEXT: let str3 = await (((s).instanceProp)) +// MEMBERS-NEXT: print(str3) +// MEMBERS-NEXT: let str4 = await SomeStruct.classProp +// MEMBERS-NEXT: print(str4) +// MEMBERS-NEXT: let str5 = await (((SomeStruct).classProp)) +// MEMBERS-NEXT: print(str5) +// MEMBERS-NEXT: } + +@available(*, renamed: "nomatch") +func badRename(_ completion: @escaping (String) -> Void) { } +@available(*, renamed: "nomatch") +func badRenameUnlabelled(_ arg: Int, _ completion: @escaping (String) -> Void) { } +@available(*, renamed: "badRename2Async(arg:newArg:)") +func badRename2(arg: Int, completionHandler: @escaping (String) -> Void) { } +func badRename2Async(arg: Int = 0, newArg: Int) async -> String { return "" } + +// Won't compile since there are no corresponding async functions +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 | %FileCheck -check-prefix=FALLBACK %s +func fallback() { + badRename { str in + print(str) + } + badRenameUnlabelled(1) { str in + print(str) + } + badRename2(arg: 1) { str in + print(str) + } +} +// FALLBACK: func fallback() async { +// FALLBACK-NEXT: let str = await badRename() +// FALLBACK-NEXT: print(str) +// FALLBACK-NEXT: let str1 = await badRenameUnlabelled(1) +// FALLBACK-NEXT: print(str1) +// FALLBACK-NEXT: let str2 = await badRename2(arg: 1) +// FALLBACK-NEXT: print(str2) +// FALLBACK-NEXT: } diff --git a/test/refactoring/ConvertAsync/convert_async_renames.swift b/test/refactoring/ConvertAsync/convert_async_renames.swift new file mode 100644 index 0000000000000..3ee3dda300b6f --- /dev/null +++ b/test/refactoring/ConvertAsync/convert_async_renames.swift @@ -0,0 +1,605 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +func simple(_ completion: @escaping (String) -> Void) { } +func simple() async -> String { } + +func simpleArg(arg: String, _ completion: @escaping (String) -> Void) { } +func simpleArg(arg: String) async -> String { } + +func simpleErr(arg: String, _ completion: @escaping (String?, Error?) -> Void) { } +func simpleErr(arg: String) async throws -> String { } + +func whatever() -> Bool { return true } + +// Ideally we wouldn't rename anything here since it's correct as is, but the +// collector picks up the param `str` use in the if condition. +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):6 | %FileCheck -check-prefix=LOCALREDECL %s +func localRedecl(str: String?) { + if let str = str { + print(str) + } + let str = "str" + print(str) +} +// LOCALREDECL: func localRedecl(str: String?) async { +// LOCALREDECL-NEXT: if let str = str { +// LOCALREDECL-NEXT: print(str) +// LOCALREDECL-NEXT: } +// LOCALREDECL-NEXT: let str1 = "str" +// LOCALREDECL-NEXT: print(str1) + +// Again, ideally wouldn't rename as the use of `str` is above the hoisted call. +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefixes=SHADOWUNUSEDPARAM %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):3 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func shadowUnusedParam(str: String) { + print(str) + simple { str in + print(str) + } +} +// SHADOWUNUSEDPARAM: func shadowUnusedParam(str: String) async { +// SHADOWUNUSEDPARAM-NEXT: print(str) +// SHADOWUNUSEDPARAM-NEXT: let str1 = await simple() +// SHADOWUNUSEDPARAM-NEXT: print(str1) + +// HOISTED-SIMPLE-CALL: let str = await simple() +// HOISTED-SIMPLE-CALL-NEXT: print(str) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=SHADOWUSEDPARAM %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):3 | %FileCheck -check-prefix=RENAMED-SIMPLE-CALL %s +func shadowUsedParam(str: String) { + print(str) + simple { str in + print(str) + } + print(str) +} +// SHADOWUSEDPARAM: func shadowUsedParam(str: String) async { +// SHADOWUSEDPARAM-NEXT: print(str) +// SHADOWUSEDPARAM-NEXT: let str1 = await simple() +// SHADOWUSEDPARAM-NEXT: print(str1) +// SHADOWUSEDPARAM-NEXT: print(str) + +// RENAMED-SIMPLE-CALL: let str1 = await simple() +// RENAMED-SIMPLE-CALL-NEXT: print(str1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=NESTEDBEFORE %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+5):5 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func nestedBefore() { + let str = "str" + print(str) + if whatever() { + simple { str in + print(str) + } + } +} +// NESTEDBEFORE: func nestedBefore() async { +// NESTEDBEFORE-NEXT: let str = "str" +// NESTEDBEFORE-NEXT: print(str) +// NESTEDBEFORE-NEXT: if whatever() { +// NESTEDBEFORE-NEXT: let str = await simple() +// NESTEDBEFORE-NEXT: print(str) +// NESTEDBEFORE-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=NESTEDAFTER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):5 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func nestedAfter() { + if whatever() { + simple { str in + print(str) + } + } + let str = "str" + print(str) +} +// NESTEDAFTER: func nestedAfter() async { +// NESTEDAFTER-NEXT: if whatever() { +// NESTEDAFTER-NEXT: let str = await simple() +// NESTEDAFTER-NEXT: print(str) +// NESTEDAFTER-NEXT: } +// NESTEDAFTER-NEXT: let str = "str" +// NESTEDAFTER-NEXT: print(str) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=NESTED-DECL-BEFORE %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+4):5 | %FileCheck -check-prefix=RENAMED-SIMPLE-CALL %s +func nestedDeclBefore() { + if whatever() { + let str = "str" + simple { str in + print(str) + } + } +} +// NESTED-DECL-BEFORE: func nestedDeclBefore() async { +// NESTED-DECL-BEFORE-NEXT: if whatever() { +// NESTED-DECL-BEFORE-NEXT: let str = "str" +// NESTED-DECL-BEFORE-NEXT: let str1 = await simple() +// NESTED-DECL-BEFORE-NEXT: print(str1) +// NESTED-DECL-BEFORE-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=NESTED-DECL-AFTER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):5 | %FileCheck -check-prefix=RENAMED-SIMPLE-CALL %s +func nestedDeclAfter() { + if whatever() { + simple { str in + print(str) + } + let str = "str" + } +} +// NESTED-DECL-AFTER: func nestedDeclAfter() async { +// NESTED-DECL-AFTER-NEXT: if whatever() { +// NESTED-DECL-AFTER-NEXT: let str = await simple() +// NESTED-DECL-AFTER-NEXT: print(str) +// NESTED-DECL-AFTER-NEXT: let str1 = "str" +// NESTED-DECL-AFTER-NEXT: } + +// Ideally wouldn't rename, but is for the same reason as before +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=NESTED-USE-BEFORE %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+5):5 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func nestedUseBefore() { + let str = "str" + if whatever() { + print(str) + simple { str in + print(str) + } + } +} +// NESTED-USE-BEFORE: func nestedUseBefore() async { +// NESTED-USE-BEFORE-NEXT: let str = "str" +// NESTED-USE-BEFORE-NEXT: if whatever() { +// NESTED-USE-BEFORE-NEXT: print(str) +// NESTED-USE-BEFORE-NEXT: let str1 = await simple() +// NESTED-USE-BEFORE-NEXT: print(str1) +// NESTED-USE-BEFORE-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=NESTED-USE-AFTER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+4):5 | %FileCheck -check-prefix=RENAMED-SIMPLE-CALL %s +func nestedUseAfter() { + let str = "str" + if whatever() { + simple { str in + print(str) + } + print(str) + } +} +// NESTED-USE-AFTER: func nestedUseAfter() async { +// NESTED-USE-AFTER-NEXT: let str = "str" +// NESTED-USE-AFTER-NEXT: if whatever() { +// NESTED-USE-AFTER-NEXT: let str1 = await simple() +// NESTED-USE-AFTER-NEXT: print(str1) +// NESTED-USE-AFTER-NEXT: print(str) +// NESTED-USE-AFTER-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=REDECLBEFORE %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+4):3 | %FileCheck -check-prefix=RENAMED-SIMPLE-CALL %s +func redeclBefore() { + let str = "do not redecl" + print(str) + simple { str in + print(str) + } +} +// REDECLBEFORE: func redeclBefore() async { +// REDECLBEFORE-NEXT: let str = "do not redecl" +// REDECLBEFORE-NEXT: print(str) +// REDECLBEFORE-NEXT: let str1 = await simple() +// REDECLBEFORE-NEXT: print(str1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=GUARDREDECLBEFORE %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+6):3 | %FileCheck -check-prefix=RENAMED-SIMPLE-CALL %s +func guardRedeclBefore(arg: String?) { + guard let str = arg else { + return + } + print(str) + simple { str in + print(str) + } +} +// GUARDREDECLBEFORE: func guardRedeclBefore(arg: String?) async { +// GUARDREDECLBEFORE-NEXT: guard let str = arg else { +// GUARDREDECLBEFORE-NEXT: return +// GUARDREDECLBEFORE-NEXT: } +// GUARDREDECLBEFORE-NEXT: print(str) +// GUARDREDECLBEFORE-NEXT: let str1 = await simple() +// GUARDREDECLBEFORE-NEXT: print(str1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=IFDECLBEFORE %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+5):3 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func ifDeclBefore(arg: String?) { + if let str = arg { + print(str) + } + simple { str in + print(str) + } +} +// IFDECLBEFORE: func ifDeclBefore(arg: String?) async { +// IFDECLBEFORE-NEXT: if let str = arg { +// IFDECLBEFORE-NEXT: print(str) +// IFDECLBEFORE-NEXT: } +// IFDECLBEFORE-NEXT: let str = await simple() +// IFDECLBEFORE-NEXT: print(str) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=REDECLAFTER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=RENAMED-SIMPLE-CALL %s +func redeclAfter() { + simple { str in + print(str) + } + let str = "do not redecl" + print(str) +} +// REDECLAFTER: func redeclAfter() async { +// REDECLAFTER-NEXT: let str = await simple() +// REDECLAFTER-NEXT: print(str) +// REDECLAFTER-NEXT: let str1 = "do not redecl" +// REDECLAFTER-NEXT: print(str1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=GUARDREDECLAFTER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=RENAMED-SIMPLE-CALL %s +func guardRedeclAfter(arg: String?) { + simple { str in + print(str) + } + guard let str = arg else { + return + } + print(str) +} +// GUARDREDECLAFTER: func guardRedeclAfter(arg: String?) async { +// GUARDREDECLAFTER-NEXT: let str = await simple() +// GUARDREDECLAFTER-NEXT: print(str) +// GUARDREDECLAFTER-NEXT: guard let str1 = arg else { +// GUARDREDECLAFTER-NEXT: return +// GUARDREDECLAFTER-NEXT: } +// GUARDREDECLAFTER-NEXT: print(str1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=IFDECLAFTER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func ifDeclAfter(arg: String?) { + simple { str in + print(str) + } + if let str = arg { + print(str) + } +} +// IFDECLAFTER: func ifDeclAfter(arg: String?) async { +// IFDECLAFTER-NEXT: let str = await simple() +// IFDECLAFTER-NEXT: print(str) +// IFDECLAFTER-NEXT: if let str = arg { +// IFDECLAFTER-NEXT: print(str) +// IFDECLAFTER-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=REDECLINNER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=REDECLINNER %s +func redeclInner() { + simple { str in + simpleArg(arg: str) { other in + let str = other + print(str) + } + } +} +// REDECLINNER: let str = await simple() +// REDECLINNER-NEXT: let other = await simpleArg(arg: str) +// REDECLINNER-NEXT: let str1 = other +// REDECLINNER-NEXT: print(str1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=DECLINNER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=DECLINNER %s +func declInner() { + simple { str in + simpleArg(arg: str) { other in + if other == "anything" { + let str = other + print(str) + } + } + } +} +// DECLINNER: let str = await simple() +// DECLINNER-NEXT: let other = await simpleArg(arg: str) +// DECLINNER-NEXT: if other == "anything" { +// DECLINNER-NEXT: let str = other +// DECLINNER-NEXT: print(str) +// DECLINNER-NEXT: } + +// TODO: `throws` isn't added to the function declaration +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=REDECLHOISTED %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=REDECLHOISTED %s +func redeclInnerHoisted() { + simple { str in + simpleErr(arg: str) { other, err in + if let other = other { + let str = other + print(str) + } + } + } +} +// REDECLHOISTED: let str = await simple() +// REDECLHOISTED-NEXT: let other = try await simpleErr(arg: str) +// REDECLHOISTED-NEXT: let str1 = other +// REDECLHOISTED-NEXT: print(str1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=SHADOWINNER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=SHADOWINNER %s +func shadowInner() { + simple { str in + simpleArg(arg: str) { str in + print(str) + } + } +} +// SHADOWINNER: let str = await simple() +// SHADOWINNER-NEXT: let str1 = await simpleArg(arg: str) +// SHADOWINNER-NEXT: print(str1) + +// TODO: `throws` isn't added to the function declaration +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=SHADOWINNERBIND %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=SHADOWINNERBIND %s +func shadowInnerBind() { + simple { str in + simpleErr(arg: str) { other, err in + if let str = other { + print(str) + } + } + } +} +// SHADOWINNERBIND: let str = await simple() +// SHADOWINNERBIND-NEXT: let str1 = try await simpleErr(arg: str) +// SHADOWINNERBIND-NEXT: print(str1) + +// TODO: `throws` isn't added to the function declaration +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=SHADOWNAMEEXISTS %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=SHADOWNAMEEXISTS %s +func shadowNameAlreadyExists() { + simple { str in + simpleErr(arg: str) { str, err in + let str1 = "str1" + print(str1) + if let str1 = str { + print(str1) + } + if let str2 = str { + print(str2) + } + } + } +} +// SHADOWNAMEEXISTS: let str = await simple() +// SHADOWNAMEEXISTS-NEXT: let str1 = try await simpleErr(arg: str) +// SHADOWNAMEEXISTS-NEXT: let str11 = "str1" +// SHADOWNAMEEXISTS-NEXT: print(str11) +// SHADOWNAMEEXISTS-NEXT: print(str1) +// SHADOWNAMEEXISTS-NEXT: print(str1) + +func shadowsUsedDecl() { + let inOuter: String = "str" + print(inOuter) + // TODO: `throws` isn't added to the function declaration + // RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):8 | %FileCheck -check-prefix=SHADOWOUTERUSED %s + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):10 | %FileCheck -check-prefix=SHADOWOUTERUSED %s + func inner() { + simpleErr(arg: "str") { inCall, err in + if inCall != nil { + let inOuter = inCall! + print(inOuter) + } + print(inOuter) + } + } +} +// SHADOWOUTERUSED: let inCall = try await simpleErr(arg: "str") +// SHADOWOUTERUSED-NEXT: let inOuter1 = inCall +// SHADOWOUTERUSED-NEXT: print(inOuter1) +// SHADOWOUTERUSED-NEXT: print(inOuter) + +func shadowsUnusedDecl() { + let inOuter: String = "str" + print(inOuter) + // TODO: `throws` isn't added to the function declaration + // RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):8 | %FileCheck -check-prefix=SHADOWOUTERUNUSED %s + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):10 | %FileCheck -check-prefix=SHADOWOUTERUNUSED %s + func inner() { + simpleErr(arg: "str") { inCall, err in + if inCall != nil { + let inOuter = inCall! + print(inOuter) + } + } + } +} +// SHADOWOUTERUNUSED: let inCall = try await simpleErr(arg: "str") +// SHADOWOUTERUNUSED-NEXT: let inOuter = inCall +// SHADOWOUTERUNUSED-NEXT: print(inOuter) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=IGNORE-SCOPED-BEFORE %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+16):3 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func ignoreScopedBefore(arg: String?, args: [String]) { + if let str = arg { + print(str) + } + for str in args { + print(str) + } + var check = arg + while let str = check { + check = str + } + do { + let str = arg! + print(str) + } + simple { str in + print(str) + } +} +// IGNORE-SCOPED-BEFORE: if let str = arg { +// IGNORE-SCOPED-BEFORE-NEXT: print(str) +// IGNORE-SCOPED-BEFORE-NEXT: } +// IGNORE-SCOPED-BEFORE-NEXT: for str in args { +// IGNORE-SCOPED-BEFORE-NEXT: print(str) +// IGNORE-SCOPED-BEFORE-NEXT: } +// IGNORE-SCOPED-BEFORE-NEXT: var check = arg +// IGNORE-SCOPED-BEFORE-NEXT: while let str = check { +// IGNORE-SCOPED-BEFORE-NEXT: check = str +// IGNORE-SCOPED-BEFORE-NEXT: } +// IGNORE-SCOPED-BEFORE-NEXT: do { +// IGNORE-SCOPED-BEFORE-NEXT: let str = arg! +// IGNORE-SCOPED-BEFORE-NEXT: print(str) +// IGNORE-SCOPED-BEFORE-NEXT: } +// IGNORE-SCOPED-BEFORE-NEXT: let str = await simple() +// IGNORE-SCOPED-BEFORE-NEXT: print(str) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=IGNORE-SCOPED-AFTER %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func ignoreScopedAfter(arg: String?, args: [String]) { + simple { str in + print(str) + } + if let str = arg { + print(str) + } + for str in args { + print(str) + } + var check = arg + while let str = check { + check = str + } + do { + let str = arg! + print(str) + } +} +// IGNORE-SCOPED-AFTER: let str = await simple() +// IGNORE-SCOPED-AFTER-NEXT: print(str) +// IGNORE-SCOPED-AFTER-NEXT: if let str = arg { +// IGNORE-SCOPED-AFTER-NEXT: print(str) +// IGNORE-SCOPED-AFTER-NEXT: } +// IGNORE-SCOPED-AFTER-NEXT: for str in args { +// IGNORE-SCOPED-AFTER-NEXT: print(str) +// IGNORE-SCOPED-AFTER-NEXT: } +// IGNORE-SCOPED-AFTER-NEXT: var check = arg +// IGNORE-SCOPED-AFTER-NEXT: while let str = check { +// IGNORE-SCOPED-AFTER-NEXT: check = str +// IGNORE-SCOPED-AFTER-NEXT: } +// IGNORE-SCOPED-AFTER-NEXT: do { +// IGNORE-SCOPED-AFTER-NEXT: let str = arg! +// IGNORE-SCOPED-AFTER-NEXT: print(str) +// IGNORE-SCOPED-AFTER-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefixes=TYPE-BEFORE,TYPE-BEFORE-CALL %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):3 | %FileCheck -check-prefix=TYPE-BEFORE-CALL %s +func typeBefore() { + struct Foo {} + simple { Foo in + print(Foo) + } +} +// TYPE-BEFORE: struct Foo {} +// TYPE-BEFORE-CALL: let Foo1 = await simple() +// TYPE-BEFORE-CALL-NEXT: print(Foo1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefixes=FUNC-BEFORE,FUNC-BEFORE-CALL %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):3 | %FileCheck -check-prefix=FUNC-BEFORE-CALL %s +func funcBefore() { + func foo() {} + simple { foo in + print(foo) + } +} +// FUNC-BEFORE: func foo() {} +// FUNC-BEFORE-CALL: let foo1 = await simple() +// FUNC-BEFORE-CALL-NEXT: print(foo1) + +enum SomeEnum { + case foo(String) + case bar(String) + case baz(String) +} + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):6 | %FileCheck -check-prefix=CASE-SCOPES %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+15):5 | %FileCheck -check-prefix=HOISTED-SIMPLE-CALL %s +func caseScopes() { + switch SomeEnum.foo("a") { + case .foo(let arg): + simple { str in + print(str) + } + case .bar(let str): + simple { str in + print(str) + } + case .baz(let arg): + simple { str in + print(str) + } + simple { str in + print(str) + } + } +} +// CASE-SCOPES: func caseScopes() async { +// CASE-SCOPES-NEXT: switch SomeEnum.foo("a") { +// CASE-SCOPES-NEXT: case .foo(let arg): +// CASE-SCOPES-NEXT: let str = await simple() +// CASE-SCOPES-NEXT: print(str) +// CASE-SCOPES-NEXT: case .bar(let str): +// CASE-SCOPES-NEXT: let str = await simple() +// CASE-SCOPES-NEXT: print(str) +// CASE-SCOPES-NEXT: case .baz(let arg): +// CASE-SCOPES-NEXT: let str = await simple() +// CASE-SCOPES-NEXT: print(str) +// CASE-SCOPES-NEXT: let str1 = await simple() +// CASE-SCOPES-NEXT: print(str1) +// CASE-SCOPES-NEXT: } +// CASE-SCOPES-NEXT: } + +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=TOP-LEVEL-VAR %s +let inFile = "inFile" +simple { inFile in + print(inFile) +} +// TOP-LEVEL-VAR: let inFile1 = await simple() +// TOP-LEVEL-VAR-NEXT: print(inFile1) + +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=TOP-LEVEL-FUNC %s +func fileFunc() {} +simple { fileFunc in + print(fileFunc) +} +// TOP-LEVEL-FUNC: let fileFunc1 = await simple() +// TOP-LEVEL-FUNC-NEXT: print(fileFunc1) + +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=TOP-LEVEL-PROTO %s +protocol FileProto {} +simple { FileProto in + print(FileProto) +} +// TOP-LEVEL-PROTO: let FileProto1 = await simple() +// TOP-LEVEL-PROTO-NEXT: print(FileProto1) + +// The following results in two TopLevelCodeDecls each with their own BraceStmt, +// we want to make sure that we still find the `someGlobal` reference and thus +// rename the `someGlobal` closure arg. +let someGlobal = "someGlobal" +func between1() {} +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=TOP-LEVEL-REFERENCE %s +simple { someGlobal in + print(someGlobal) +} +func between2() {} +print(someGlobal) +// TOP-LEVEL-REFERENCE: let someGlobal1 = await simple() +// TOP-LEVEL-REFERENCE-NEXT: print(someGlobal1) diff --git a/test/refactoring/ConvertAsync/convert_async_wrapper.swift b/test/refactoring/ConvertAsync/convert_async_wrapper.swift new file mode 100644 index 0000000000000..2f5f9b57ad2d2 --- /dev/null +++ b/test/refactoring/ConvertAsync/convert_async_wrapper.swift @@ -0,0 +1,182 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +enum CustomError : Error { + case e +} + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 -enable-experimental-concurrency | %FileCheck -check-prefix=FOO1 %s +func foo1(_ completion: @escaping () -> Void) {} +// FOO1: convert_async_wrapper.swift [[# @LINE-1]]:1 -> [[# @LINE-1]]:1 +// FOO1-NEXT: @available(*, renamed: "foo1()") +// FOO1-EMPTY: +// FOO1-NEXT: convert_async_wrapper.swift [[# @LINE-4]]:49 -> [[# @LINE-4]]:49 +// FOO1-EMPTY: +// FOO1-EMPTY: +// FOO1-EMPTY: +// FOO1-NEXT: convert_async_wrapper.swift [[# @LINE-8]]:49 -> [[# @LINE-8]]:49 +// FOO1-NEXT: func foo1() async { +// FOO1-NEXT: return await withCheckedContinuation { continuation in +// FOO1-NEXT: foo1() { +// FOO1-NEXT: continuation.resume(returning: ()) +// FOO1-NEXT: } +// FOO1-NEXT: } +// FOO1-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 -enable-experimental-concurrency | %FileCheck -check-prefix=FOO2 %s +func foo2(arg: String, _ completion: @escaping (String) -> Void) {} +// FOO2: convert_async_wrapper.swift [[# @LINE-1]]:1 -> [[# @LINE-1]]:1 +// FOO2-NEXT: @available(*, renamed: "foo2(arg:)") +// FOO2-EMPTY: +// FOO2-NEXT: convert_async_wrapper.swift [[# @LINE-4]]:68 -> [[# @LINE-4]]:68 +// FOO2-EMPTY: +// FOO2-EMPTY: +// FOO2-EMPTY: +// FOO2-NEXT: convert_async_wrapper.swift [[# @LINE-8]]:68 -> [[# @LINE-8]]:68 +// FOO2: func foo2(arg: String) async -> String { +// FOO2-NEXT: return await withCheckedContinuation { continuation in +// FOO2-NEXT: foo2(arg: arg) { result in +// FOO2-NEXT: continuation.resume(returning: result) +// FOO2-NEXT: } +// FOO2-NEXT: } +// FOO2-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO3 %s +func foo3(arg: String, _ arg2: Int, _ completion: @escaping (String?) -> Void) {} + +// FOO3: func foo3(arg: String, _ arg2: Int) async -> String? { +// FOO3-NEXT: return await withCheckedContinuation { continuation in +// FOO3-NEXT: foo3(arg: arg, arg2) { result in +// FOO3-NEXT: continuation.resume(returning: result) +// FOO3-NEXT: } +// FOO3-NEXT: } +// FOO3-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO4 %s +func foo4(_ completion: @escaping (Error?) -> Void) {} + +// FOO4: func foo4() async throws { +// FOO4-NEXT: return try await withCheckedThrowingContinuation { continuation in +// FOO4-NEXT: foo4() { error in +// FOO4-NEXT: if let error = error { +// FOO4-NEXT: continuation.resume(throwing: error) +// FOO4-NEXT: return +// FOO4-NEXT: } +// FOO4-NEXT: continuation.resume(returning: ()) +// FOO4-NEXT: } +// FOO4-NEXT: } +// FOO4-NEXT: } + + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO5 %s +func foo5(_ completion: @escaping (Error) -> Void) {} + +// FOO5: func foo5() async -> Error { +// FOO5-NEXT: return await withCheckedContinuation { continuation in +// FOO5-NEXT: foo5() { result in +// FOO5-NEXT: continuation.resume(returning: result) +// FOO5-NEXT: } +// FOO5-NEXT: } +// FOO5-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO6 %s +func foo6(_ completion: @escaping (String?, Error?) -> Void) {} + +// FOO6: func foo6() async throws -> String { +// FOO6-NEXT: return try await withCheckedThrowingContinuation { continuation in +// FOO6-NEXT: foo6() { result, error in +// FOO6-NEXT: if let error = error { +// FOO6-NEXT: continuation.resume(throwing: error) +// FOO6-NEXT: return +// FOO6-NEXT: } +// FOO6-NEXT: guard let result = result else { +// FOO6-NEXT: fatalError("Expected non-nil result 'result' for nil error") +// FOO6-NEXT: } +// FOO6-NEXT: continuation.resume(returning: result) +// FOO6-NEXT: } +// FOO6-NEXT: } +// FOO6-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO7 %s +func foo7(_ completion: @escaping (String?, Int, Error?) -> Void) {} + +// FOO7: func foo7() async throws -> (String, Int) { +// FOO7-NEXT: return try await withCheckedThrowingContinuation { continuation in +// FOO7-NEXT: foo7() { result1, result2, error in +// FOO7-NEXT: if let error = error { +// FOO7-NEXT: continuation.resume(throwing: error) +// FOO7-NEXT: return +// FOO7-NEXT: } +// FOO7-NEXT: guard let result1 = result1 else { +// FOO7-NEXT: fatalError("Expected non-nil result 'result1' for nil error") +// FOO7-NEXT: } +// FOO7-NEXT: continuation.resume(returning: (result1, result2)) +// FOO7-NEXT: } +// FOO7-NEXT: } +// FOO7-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO8 %s +func foo8(_ completion: @escaping (String?, Int?, Error?) -> Void) {} + +// FOO8: func foo8() async throws -> (String, Int) { +// FOO8-NEXT: return try await withCheckedThrowingContinuation { continuation in +// FOO8-NEXT: foo8() { result1, result2, error in +// FOO8-NEXT: if let error = error { +// FOO8-NEXT: continuation.resume(throwing: error) +// FOO8-NEXT: return +// FOO8-NEXT: } +// FOO8-NEXT: guard let result1 = result1 else { +// FOO8-NEXT: fatalError("Expected non-nil result 'result1' for nil error") +// FOO8-NEXT: } +// FOO8-NEXT: guard let result2 = result2 else { +// FOO8-NEXT: fatalError("Expected non-nil result 'result2' for nil error") +// FOO8-NEXT: } +// FOO8-NEXT: continuation.resume(returning: (result1, result2)) +// FOO8-NEXT: } +// FOO8-NEXT: } +// FOO8-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO9 %s +func foo9(_ completion: @escaping (Result) -> Void) {} + +// FOO9: func foo9() async throws -> String { +// FOO9-NEXT: return try await withCheckedThrowingContinuation { continuation in +// FOO9-NEXT: foo9() { result in +// FOO9-NEXT: continuation.resume(with: result) +// FOO9-NEXT: } +// FOO9-NEXT: } +// FOO9-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO10 %s +func foo10(arg: Int, _ completion: @escaping (Result<(String, Int), Error>) -> Void) {} + +// FOO10: func foo10(arg: Int) async throws -> (String, Int) { +// FOO10-NEXT: return try await withCheckedThrowingContinuation { continuation in +// FOO10-NEXT: foo10(arg: arg) { result in +// FOO10-NEXT: continuation.resume(with: result) +// FOO10-NEXT: } +// FOO10-NEXT: } +// FOO10-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO11 %s +func foo11(completion: @escaping (Result) -> Void) {} + +// FOO11: func foo11() async -> String { +// FOO11-NEXT: return await withCheckedContinuation { continuation in +// FOO11-NEXT: foo11() { result in +// FOO11-NEXT: continuation.resume(with: result) +// FOO11-NEXT: } +// FOO11-NEXT: } +// FOO11-NEXT: } + +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FOO12 %s +func foo12(completion: @escaping (Result) -> Void) {} + +// FOO12: func foo12() async throws -> String { +// FOO12-NEXT: return try await withCheckedThrowingContinuation { continuation in +// FOO12-NEXT: foo12() { result in +// FOO12-NEXT: continuation.resume(with: result) +// FOO12-NEXT: } +// FOO12-NEXT: } +// FOO12-NEXT: } diff --git a/test/refactoring/ConvertAsync/convert_bool.swift b/test/refactoring/ConvertAsync/convert_bool.swift new file mode 100644 index 0000000000000..bc32654b47b5b --- /dev/null +++ b/test/refactoring/ConvertAsync/convert_bool.swift @@ -0,0 +1,464 @@ +// REQUIRES: objc_interop +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) +// RUN: %build-clang-importer-objc-overlays + +import Foundation +import ConvertBoolObjC + +func boolWithErr() async throws -> Bool { true } +func boolWithErr(completion: @escaping (Bool, Error?) -> Void) {} + +func multipleBoolWithErr() async throws -> (String, Bool, Bool) { ("", true, true) } +func multipleBoolWithErr(completion: @escaping (String?, Bool, Bool, Error?) -> Void) {} + +func optionalBoolWithErr() async throws -> (String, Bool, Bool) { ("", true, true) } +func optionalBoolWithErr(completion: @escaping (String?, Bool?, Bool, Error?) -> Void) {} + +func testConvertBool() async throws { + // All 7 of the below should generate the same refactoring. + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if !b { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if b { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if !b && err != nil { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if b && err != nil { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if err != nil && b == false { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if b == true && err == nil { + } else { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR %s + boolWithErr { b, err in + if !b && err == nil { + } else { + fatalError("oh no \(err!)") + } + print("not err") + } + + // BOOL-WITH-ERR: do { + // BOOL-WITH-ERR-NEXT: let b = try await boolWithErr() + // BOOL-WITH-ERR-NEXT: print("not err") + // BOOL-WITH-ERR-NEXT: } catch let err { + // BOOL-WITH-ERR-NEXT: fatalError("oh no \(err)") + // BOOL-WITH-ERR-NEXT: } + + // These 3 should both generate the same refactoring. + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s + boolWithErr { success, err in + if success == true && err == nil { + print("hi") + } else { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s + boolWithErr { success, err in + if success && err == nil { + print("hi") + } else { + fatalError("oh no \(err!)") + } + print("not err") + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR2 %s + boolWithErr { success, err in + if err == nil { + print("hi") + } else if !success { + fatalError("oh no \(err!)") + } + print("not err") + } + + // BOOL-WITH-ERR2: do { + // BOOL-WITH-ERR2-NEXT: let success = try await boolWithErr() + // BOOL-WITH-ERR2-NEXT: print("hi") + // BOOL-WITH-ERR2-NEXT: print("not err") + // BOOL-WITH-ERR2-NEXT: } catch let err { + // BOOL-WITH-ERR2-NEXT: fatalError("oh no \(err)") + // BOOL-WITH-ERR2-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR3 %s + boolWithErr { failure, err in + if failure { + print("a \(err!)") + } else if .random() { + print("b") + } else { + print("c") + } + } + + // BOOL-WITH-ERR3: do { + // BOOL-WITH-ERR3-NEXT: let failure = try await boolWithErr() + // BOOL-WITH-ERR3-NEXT: if .random() { + // BOOL-WITH-ERR3-NEXT: print("b") + // BOOL-WITH-ERR3-NEXT: } else { + // BOOL-WITH-ERR3-NEXT: print("c") + // BOOL-WITH-ERR3-NEXT: } + // BOOL-WITH-ERR3-NEXT: } catch let err { + // BOOL-WITH-ERR3-NEXT: print("a \(err)") + // BOOL-WITH-ERR3-NEXT: } + + // Don't handle the below example as the force unwrap of err takes place under a different condition. + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE %s + boolWithErr { success, err in + if !success { + if err != nil { + fatalError("oh no \(err!)") + } + } + if !success { + _ = err != nil ? fatalError("oh no \(err!)") : fatalError("some worries") + } + print("not err") + } + + // BOOL-DONT-HANDLE: let success = try await boolWithErr() + // BOOL-DONT-HANDLE-NEXT: if !success { + // BOOL-DONT-HANDLE-NEXT: if <#err#> != nil { + // BOOL-DONT-HANDLE-NEXT: fatalError("oh no \(<#err#>!)") + // BOOL-DONT-HANDLE-NEXT: } + // BOOL-DONT-HANDLE-NEXT: } + // BOOL-DONT-HANDLE-NEXT: if !success { + // BOOL-DONT-HANDLE-NEXT: _ = <#err#> != nil ? fatalError("oh no \(<#err#>!)") : fatalError("some worries") + // BOOL-DONT-HANDLE-NEXT: } + // BOOL-DONT-HANDLE-NEXT: print("not err") + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE2 %s + boolWithErr { success, err in + if !success { + func doThings() { + fatalError("oh no \(err!)") + } + doThings() + } + if !success { + let doThings = { + fatalError("oh no \(err!)") + } + doThings() + } + if !success { + while err != nil { + fatalError("oh no \(err!)") + } + } + if !success { + for _: Int in [] { + fatalError("oh no \(err!)") + } + } + print("not err") + } + + // FIXME: The 'err' in doThings() should become a placeholder (rdar://78509286). + // BOOL-DONT-HANDLE2: let success = try await boolWithErr() + // BOOL-DONT-HANDLE2-NEXT: if !success { + // BOOL-DONT-HANDLE2-NEXT: func doThings() { + // BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(err!)") + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: doThings() + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: if !success { + // BOOL-DONT-HANDLE2-NEXT: let doThings = { + // BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: doThings() + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: if !success { + // BOOL-DONT-HANDLE2-NEXT: while <#err#> != nil { + // BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: if !success { + // BOOL-DONT-HANDLE2-NEXT: for _: Int in [] { + // BOOL-DONT-HANDLE2-NEXT: fatalError("oh no \(<#err#>!)") + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: } + // BOOL-DONT-HANDLE2-NEXT: print("not err") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE3 %s + boolWithErr { success, err in + if !success { + fatalError("oh no maybe \(String(describing: err))") + } + print("not err") + } + + // err is not force unwrapped, so don't handle. + + // BOOL-DONT-HANDLE3: let success = try await boolWithErr() + // BOOL-DONT-HANDLE3-NEXT: if !success { + // BOOL-DONT-HANDLE3-NEXT: fatalError("oh no maybe \(String(describing: <#err#>))") + // BOOL-DONT-HANDLE3-NEXT: } + // BOOL-DONT-HANDLE3-NEXT: print("not err") + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-DONT-HANDLE4 %s + boolWithErr { failure, err in + if failure { + print("a") + } else if .random() { + print("b \(err!)") + } else { + print("c") + } + } + + // Don't handle the case where the err unwrap occurs in an unrelated else if + // clause. + + // BOOL-DONT-HANDLE4: let failure = try await boolWithErr() + // BOOL-DONT-HANDLE4-NEXT: if failure { + // BOOL-DONT-HANDLE4-NEXT: print("a") + // BOOL-DONT-HANDLE4-NEXT: } else if .random() { + // BOOL-DONT-HANDLE4-NEXT: print("b \(<#err#>!)") + // BOOL-DONT-HANDLE4-NEXT: } else { + // BOOL-DONT-HANDLE4-NEXT: print("c") + // BOOL-DONT-HANDLE4-NEXT: } + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR-SILLY %s + boolWithErr { success, err in + if success == false && err == nil { + print("ummm wat \(err!)") + return + } + print("not err") + } + + // BOOL-WITH-ERR-SILLY: let success = try await boolWithErr() + // BOOL-WITH-ERR-SILLY-NEXT: if success == false && <#err#> == nil { + // BOOL-WITH-ERR-SILLY-NEXT: print("ummm wat \(<#err#>!)") + // BOOL-WITH-ERR-SILLY-NEXT: <#return#> + // BOOL-WITH-ERR-SILLY-NEXT: } + // BOOL-WITH-ERR-SILLY-NEXT: print("not err") + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=BOOL-WITH-ERR-SILLY2 %s + boolWithErr { success, err in + if success { + print("ummm wat \(err!)") + } else { + print("ummm wat \(err!)") + } + } + + // The err unwrap is in both blocks, so it's not clear what to classify as. + + // BOOL-WITH-ERR-SILLY2: let success = try await boolWithErr() + // BOOL-WITH-ERR-SILLY2-NEXT: if success { + // BOOL-WITH-ERR-SILLY2-NEXT: print("ummm wat \(<#err#>!)") + // BOOL-WITH-ERR-SILLY2-NEXT: } else { + // BOOL-WITH-ERR-SILLY2-NEXT: print("ummm wat \(<#err#>!)") + // BOOL-WITH-ERR-SILLY2-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=MULTI-BOOL-WITH-ERR %s + multipleBoolWithErr { str, b1, b2, err in + if !b1 && !b2 { + print("a \(err!)") + } + if b1, b2 { + print("b \(err!)") + } + if !b1 { + print("c \(err!)") + } + if !b2 { + print("d \(err!)") + } + } + + // Don't handle the case where multiple flag checks are done in a single + // condition, because it's not exactly clear what the user is doing. It's a + // little unfortunate that we'll allow multiple flag checks in seperate + // conditions, but both of these cases seem somewhat uncommon, and there's no + // real way to completely enforce a single flag param across e.g multiple calls + // to the same function, so this is probably okay for now. + + // MULTI-BOOL-WITH-ERR: do { + // MULTI-BOOL-WITH-ERR-NEXT: let (str, b1, b2) = try await multipleBoolWithErr() + // MULTI-BOOL-WITH-ERR-NEXT: } catch let err { + // MULTI-BOOL-WITH-ERR-NEXT: if !<#b1#> && !<#b2#> { + // MULTI-BOOL-WITH-ERR-NEXT: print("a \(err)") + // MULTI-BOOL-WITH-ERR-NEXT: } + // MULTI-BOOL-WITH-ERR-NEXT: if <#b1#>, <#b2#> { + // MULTI-BOOL-WITH-ERR-NEXT: print("b \(err)") + // MULTI-BOOL-WITH-ERR-NEXT: } + // MULTI-BOOL-WITH-ERR-NEXT: print("c \(err)") + // MULTI-BOOL-WITH-ERR-NEXT: print("d \(err)") + // MULTI-BOOL-WITH-ERR-NEXT: } + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OPT-BOOL-WITH-ERR %s + optionalBoolWithErr { str, optBool, b, err in + if optBool != nil { + print("a \(err!)") + } + if optBool == nil { + print("b \(err!)") + } + if optBool == true { + print("c \(err!)") + } + if ((optBool) == (false)) { + print("d \(err!)") + } + if optBool == false { + print("e \(String(describing: err))") + } + if optBool != true { + print("f \(err!)") + } + if b { + print("g \(err!)") + } + } + + // It's a little unfortunate that print("a \(<#err#>!)") gets classified in the success + // block below, but it doesn't seem like a case that's likely to come up, as optBool + // would then be inaccessible in the error block. + + // OPT-BOOL-WITH-ERR: do { + // OPT-BOOL-WITH-ERR-NEXT: let (str, optBool, b) = try await optionalBoolWithErr() + // OPT-BOOL-WITH-ERR-NEXT: print("a \(<#err#>!)") + // OPT-BOOL-WITH-ERR-NEXT: if <#optBool#> == false { + // OPT-BOOL-WITH-ERR-NEXT: print("e \(String(describing: <#err#>))") + // OPT-BOOL-WITH-ERR-NEXT: } + // OPT-BOOL-WITH-ERR-NEXT: if <#optBool#> != true { + // OPT-BOOL-WITH-ERR-NEXT: print("f \(<#err#>!)") + // OPT-BOOL-WITH-ERR-NEXT: } + // OPT-BOOL-WITH-ERR-NEXT: } catch let err { + // OPT-BOOL-WITH-ERR-NEXT: print("b \(err)") + // OPT-BOOL-WITH-ERR-NEXT: print("c \(err)") + // OPT-BOOL-WITH-ERR-NEXT: print("d \(err)") + // OPT-BOOL-WITH-ERR-NEXT: print("g \(err)") + // OPT-BOOL-WITH-ERR-NEXT: } + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OBJC-BOOL-WITH-ERR %s + ClassWithHandlerMethods.firstBoolFlagSuccess("") { str, success, unrelated, err in + if !unrelated { + print(err!) + } + if !success { + print("oh no") + } + if !success { + print(err!) + } + if success { + print("woo") + } + if str != nil { + print("also woo") + } + } + + // OBJC-BOOL-WITH-ERR: do { + // OBJC-BOOL-WITH-ERR-NEXT: let (str, success, unrelated) = try await ClassWithHandlerMethods.firstBoolFlagSuccess("") + // OBJC-BOOL-WITH-ERR-NEXT: if !unrelated { + // OBJC-BOOL-WITH-ERR-NEXT: print(<#err#>!) + // OBJC-BOOL-WITH-ERR-NEXT: } + // OBJC-BOOL-WITH-ERR-NEXT: print("woo") + // OBJC-BOOL-WITH-ERR-NEXT: print("also woo") + // OBJC-BOOL-WITH-ERR-NEXT: } catch let err { + // OBJC-BOOL-WITH-ERR-NEXT: print("oh no") + // OBJC-BOOL-WITH-ERR-NEXT: print(err) + // OBJC-BOOL-WITH-ERR-NEXT: } + + // We cannot use refactor-check-compiles, as a placeholder cannot be force unwrapped. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 -I %S/Inputs -I %t -target %target-triple %clang-importer-sdk-nosource | %FileCheck -check-prefix=OBJC-BOOL-WITH-ERR2 %s + ClassWithHandlerMethods.secondBoolFlagFailure("") { str, unrelated, failure, err in + if unrelated { + print(err!) + } + if failure { + print("oh no") + } + if failure { + print(err!) + } + if !failure { + print("woo") + } + if str != nil { + print("also woo") + } + if failure && err == nil { + print("wat") + } + if failure && err != nil { + print("neat") + } + if failure, let _ = err { + print("neato") + } + } + + // OBJC-BOOL-WITH-ERR2: do { + // OBJC-BOOL-WITH-ERR2-NEXT: let (str, unrelated, failure) = try await ClassWithHandlerMethods.secondBoolFlagFailure("") + // OBJC-BOOL-WITH-ERR2-NEXT: if unrelated { + // OBJC-BOOL-WITH-ERR2-NEXT: print(<#err#>!) + // OBJC-BOOL-WITH-ERR2-NEXT: } + // OBJC-BOOL-WITH-ERR2-NEXT: print("woo") + // OBJC-BOOL-WITH-ERR2-NEXT: print("also woo") + // OBJC-BOOL-WITH-ERR2-NEXT: if failure && <#err#> == nil { + // OBJC-BOOL-WITH-ERR2-NEXT: print("wat") + // OBJC-BOOL-WITH-ERR2-NEXT: } + // OBJC-BOOL-WITH-ERR2-NEXT: } catch let err { + // OBJC-BOOL-WITH-ERR2-NEXT: print("oh no") + // OBJC-BOOL-WITH-ERR2-NEXT: print(err) + // OBJC-BOOL-WITH-ERR2-NEXT: print("neat") + // OBJC-BOOL-WITH-ERR2-NEXT: print("neato") + // OBJC-BOOL-WITH-ERR2-NEXT: } +} diff --git a/test/refactoring/ConvertAsync/convert_function.swift b/test/refactoring/ConvertAsync/convert_function.swift index 12e6ccff3e6a4..a88dcfc75921d 100644 --- a/test/refactoring/ConvertAsync/convert_function.swift +++ b/test/refactoring/ConvertAsync/convert_function.swift @@ -1,14 +1,38 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + enum CustomError : Error { case Bad } - -func simple(_ completion: (String) -> Void) { } -func simple2(arg: String, _ completion: (String) -> Void) { } -func simpleErr(arg: String, _ completion: (String?, Error?) -> Void) { } -func simpleRes(arg: String, _ completion: (Result) -> Void) { } func run(block: () -> Bool) -> Bool { return false } +func makeOptionalError() -> Error? { return nil } +func makeOptionalString() -> String? { return nil } + +func simple(_ completion: @escaping (String) -> Void) { } +func simple() async -> String { } -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NESTED %s +func simple2(arg: String, _ completion: @escaping (String) -> Void) { } +func simple2(arg: String) async -> String { } + +func simpleErr(arg: String, _ completion: @escaping (String?, Error?) -> Void) { } +func simpleErr(arg: String) async throws -> String { } + +func simpleRes(arg: String, _ completion: @escaping (Result) -> Void) { } +func simpleRes(arg: String) async throws -> String { } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ALREADY-ASYNC %s +func alreadyAsync() async { + simple { + print($0) + } +} +// ALREADY-ASYNC: func alreadyAsync() async { +// ALREADY-ASYNC-NEXT: let val0 = await simple() +// ALREADY-ASYNC-NEXT: print(val0) +// ALREADY-ASYNC-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NESTED %s func nested() { simple { simple2(arg: $0) { str2 in @@ -22,7 +46,16 @@ func nested() { // NESTED-NEXT: print(str2) // NESTED-NEXT: } -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):9 | %FileCheck -check-prefix=ATTRIBUTES %s +// Can't check for compilation since throws isn't added +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NO-THROWS %s +func noThrowsAdded() { + simpleErr(arg: "") { _, _ in } +} +// NO-THROWS: func noThrowsAdded() async { +// NO-THROWS-NEXT: let _ = try await simpleErr(arg: "") +// NO-THROWS-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):9 | %FileCheck -check-prefix=ATTRIBUTES %s @available(*, deprecated, message: "Deprecated") private func functionWithAttributes() { simple { str in @@ -36,8 +69,8 @@ private func functionWithAttributes() { // ATTRIBUTES-NEXT: print(str) // ATTRIBUTES-NEXT: } -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MANY-NESTED %s -func manyNested() { +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MANY-NESTED %s +func manyNested() throws { simple { str1 in print("simple") simple2(arg: str1) { str2 in @@ -58,7 +91,7 @@ func manyNested() { } } } -// MANY-NESTED: func manyNested() async { +// MANY-NESTED: func manyNested() async throws { // MANY-NESTED-NEXT: let str1 = await simple() // MANY-NESTED-NEXT: print("simple") // MANY-NESTED-NEXT: let str2 = await simple2(arg: str1) @@ -71,9 +104,8 @@ func manyNested() { // MANY-NESTED-NEXT: print("after") // MANY-NESTED-NEXT: } -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -func asyncParams(arg: String, _ completion: (String?, Error?) -> Void) { +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-SIMPLE %s +func asyncParams(arg: String, _ completion: @escaping (String?, Error?) -> Void) { simpleErr(arg: arg) { str, err in print("simpleErr") guard let str = str, err == nil else { @@ -91,8 +123,8 @@ func asyncParams(arg: String, _ completion: (String?, Error?) -> Void) { // ASYNC-SIMPLE-NEXT: print("after") // ASYNC-SIMPLE-NEXT: } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-SIMPLE %s -func asyncResErrPassed(arg: String, _ completion: (Result) -> Void) { +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-SIMPLE %s +func asyncResErrPassed(arg: String, _ completion: @escaping (Result) -> Void) { simpleErr(arg: arg) { str, err in print("simpleErr") guard let str = str, err == nil else { @@ -104,8 +136,8 @@ func asyncResErrPassed(arg: String, _ completion: (Result) -> Voi } } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ERR %s -func asyncResNewErr(arg: String, _ completion: (Result) -> Void) { +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-ERR %s +func asyncResNewErr(arg: String, _ completion: @escaping (Result) -> Void) { simpleErr(arg: arg) { str, err in print("simpleErr") guard let str = str, err == nil else { @@ -127,8 +159,8 @@ func asyncResNewErr(arg: String, _ completion: (Result) -> Void) // ASYNC-ERR-NEXT: } // ASYNC-ERR-NEXT: } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-UNHANDLED %s -func asyncUnhandledCompletion(_ completion: (String) -> Void) { +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CALL-NON-ASYNC-IN-ASYNC %s +func callNonAsyncInAsync(_ completion: @escaping (String) -> Void) { simple { str in let success = run { completion(str) @@ -139,19 +171,68 @@ func asyncUnhandledCompletion(_ completion: (String) -> Void) { } } } -// ASYNC-UNHANDLED: func asyncUnhandledCompletion() async -> String { -// ASYNC-UNHANDLED-NEXT: let str = await simple() -// ASYNC-UNHANDLED-NEXT: let success = run { -// ASYNC-UNHANDLED-NEXT: <#completion#>(str) -// ASYNC-UNHANDLED-NEXT: {{^}} return true{{$}} -// ASYNC-UNHANDLED-NEXT: } -// ASYNC-UNHANDLED-NEXT: if !success { -// ASYNC-UNHANDLED-NEXT: {{^}} return "bad"{{$}} -// ASYNC-UNHANDLED-NEXT: } -// ASYNC-UNHANDLED-NEXT: } +// CALL-NON-ASYNC-IN-ASYNC: func callNonAsyncInAsync() async -> String { +// CALL-NON-ASYNC-IN-ASYNC-NEXT: let str = await simple() +// CALL-NON-ASYNC-IN-ASYNC-NEXT: return await withCheckedContinuation { continuation in +// CALL-NON-ASYNC-IN-ASYNC-NEXT: let success = run { +// CALL-NON-ASYNC-IN-ASYNC-NEXT: continuation.resume(returning: str) +// CALL-NON-ASYNC-IN-ASYNC-NEXT: {{^}} return true{{$}} +// CALL-NON-ASYNC-IN-ASYNC-NEXT: } +// CALL-NON-ASYNC-IN-ASYNC-NEXT: if !success { +// CALL-NON-ASYNC-IN-ASYNC-NEXT: continuation.resume(returning: "bad") +// CALL-NON-ASYNC-IN-ASYNC-NEXT: } +// CALL-NON-ASYNC-IN-ASYNC-NEXT: } +// CALL-NON-ASYNC-IN-ASYNC-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CALL-NON-ASYNC-IN-ASYNC-COMMENT %s +func callNonAsyncInAsyncComment(_ completion: @escaping (String) -> Void) { + // a + simple { str in // b + // c + let success = run { + // d + completion(str) + // e + return true + // f + } + // g + if !success { + // h + completion("bad") + // i + } + // j + } + // k +} +// CALL-NON-ASYNC-IN-ASYNC-COMMENT: func callNonAsyncInAsyncComment() async -> String { +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // a +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: let str = await simple() +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // b +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // c +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: return await withCheckedContinuation { continuation in +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: let success = run { +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // d +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: continuation.resume(returning: str) +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // e +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: {{^}} return true{{$}} +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // f +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: } +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // g +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: if !success { +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // h +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: continuation.resume(returning: "bad") +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // i +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: } +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // j +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: {{ }} +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: // k +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: } +// CALL-NON-ASYNC-IN-ASYNC-COMMENT-NEXT: } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-HANDLER %s -func voidAndErrorCompletion(completion: (Void?, Error?) -> Void) { +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-HANDLER %s +func voidAndErrorCompletion(completion: @escaping (Void?, Error?) -> Void) { if .random() { completion((), nil) // Make sure we drop the () } else { @@ -166,8 +247,8 @@ func voidAndErrorCompletion(completion: (Void?, Error?) -> Void) { // VOID-AND-ERROR-HANDLER-NEXT: } // VOID-AND-ERROR-HANDLER-NEXT: } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix TOO-MUCH-VOID-AND-ERROR-HANDLER %s -func tooMuchVoidAndErrorCompletion(completion: (Void?, Void?, Error?) -> Void) { +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix TOO-MUCH-VOID-AND-ERROR-HANDLER %s +func tooMuchVoidAndErrorCompletion(completion: @escaping (Void?, Void?, Error?) -> Void) { if .random() { completion((), (), nil) // Make sure we drop the ()s } else { @@ -182,8 +263,8 @@ func tooMuchVoidAndErrorCompletion(completion: (Void?, Void?, Error?) -> Void) { // TOO-MUCH-VOID-AND-ERROR-HANDLER-NEXT: } // TOO-MUCH-VOID-AND-ERROR-HANDLER-NEXT: } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-RESULT-HANDLER %s -func voidResultCompletion(completion: (Result) -> Void) { +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-RESULT-HANDLER %s +func voidResultCompletion(completion: @escaping (Result) -> Void) { if .random() { completion(.success(())) // Make sure we drop the .success(()) } else { @@ -198,7 +279,402 @@ func voidResultCompletion(completion: (Result) -> Void) { // VOID-RESULT-HANDLER-NEXT: } // VOID-RESULT-HANDLER-NEXT: } -// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=NON-COMPLETION-HANDLER %s -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-COMPLETION-HANDLER %s -func functionWithSomeHandler(handler: (String) -> Void) {} -// NON-COMPLETION-HANDLER: func functionWithSomeHandler() async -> String {} +// rdar://77789360 Make sure we don't print a double return statement. +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING %s +func testReturnHandling(_ completion: @escaping (String?, Error?) -> Void) { + return completion("", nil) +} +// RETURN-HANDLING: func testReturnHandling() async throws -> String { +// RETURN-HANDLING-NEXT: {{^}} return ""{{$}} +// RETURN-HANDLING-NEXT: } + +// rdar://77789360 Make sure we don't print a double return statement and don't +// completely drop completion(a). +// Note we cannot use refactor-check-compiles here, as the placeholders mean we +// don't form valid AST. +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING2 %s +func testReturnHandling2(completion: @escaping (String) -> ()) { + simpleErr(arg: "") { x, err in + guard let _ = x else { + let a = "" + return completion(a) + } + let b = "" + return completion(b) + } +} +// RETURN-HANDLING2: func testReturnHandling2() async -> String { +// RETURN-HANDLING2-NEXT: do { +// RETURN-HANDLING2-NEXT: let x = try await simpleErr(arg: "") +// RETURN-HANDLING2-NEXT: let b = "" +// RETURN-HANDLING2-NEXT: {{^}}<#return#> b{{$}} +// RETURN-HANDLING2-NEXT: } catch let err { +// RETURN-HANDLING2-NEXT: let a = "" +// RETURN-HANDLING2-NEXT: {{^}}<#return#> a{{$}} +// RETURN-HANDLING2-NEXT: } +// RETURN-HANDLING2-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING3 %s +func testReturnHandling3(_ completion: @escaping (String?, Error?) -> Void) { + return (completion("", nil)) +} +// RETURN-HANDLING3: func testReturnHandling3() async throws -> String { +// RETURN-HANDLING3-NEXT: {{^}} return ""{{$}} +// RETURN-HANDLING3-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING4 %s +func testReturnHandling4(_ completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "xxx") { str, err in + if str != nil { + completion(str, err) + return + } + print("some error stuff") + completion(str, err) + } +} +// RETURN-HANDLING4: func testReturnHandling4() async throws -> String { +// RETURN-HANDLING4-NEXT: do { +// RETURN-HANDLING4-NEXT: let str = try await simpleErr(arg: "xxx") +// RETURN-HANDLING4-NEXT: return str +// RETURN-HANDLING4-NEXT: } catch let err { +// RETURN-HANDLING4-NEXT: print("some error stuff") +// RETURN-HANDLING4-NEXT: throw err +// RETURN-HANDLING4-NEXT: } +// RETURN-HANDLING4-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RDAR78693050 %s +func rdar78693050(_ completion: @escaping () -> Void) { + simple { str in + print(str) + } + if .random() { + return completion() + } + completion() +} + +// RDAR78693050: func rdar78693050() async { +// RDAR78693050-NEXT: let str = await simple() +// RDAR78693050-NEXT: print(str) +// RDAR78693050-NEXT: if .random() { +// RDAR78693050-NEXT: return +// RDAR78693050-NEXT: } +// RDAR78693050-NEXT: return +// RDAR78693050-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DISCARDABLE-RESULT %s +func withDefaultedCompletion(arg: String, completion: @escaping (String) -> Void = {_ in}) { + completion(arg) +} + +// DISCARDABLE-RESULT: @discardableResult +// DISCARDABLE-RESULT-NEXT: func withDefaultedCompletion(arg: String) async -> String { +// DISCARDABLE-RESULT-NEXT: return arg +// DISCARDABLE-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DEFAULT-ARG %s +func withDefaultArg(x: String = "") { +} +// DEFAULT-ARG: convert_function.swift [[# @LINE-2]]:1 -> [[# @LINE-1]]:2 +// DEFAULT-ARG-NOT: @discardableResult +// DEFAULT-ARG-NEXT: {{^}}func withDefaultArg(x: String = "") async + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=IMPLICIT-RETURN %s +func withImplicitReturn(completionHandler: @escaping (String) -> Void) { + simple { + completionHandler($0) + } +} +// IMPLICIT-RETURN: func withImplicitReturn() async -> String { +// IMPLICIT-RETURN-NEXT: let val0 = await simple() +// IMPLICIT-RETURN-NEXT: return val0 +// IMPLICIT-RETURN-NEXT: } + +// This code doesn't compile after refactoring because we can't return `nil` from the async function. +// But there's not much else we can do here. +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NIL-RESULT-AND-NIL-ERROR %s +func nilResultAndNilError(completion: @escaping (String?, Error?) -> Void) { + completion(nil, nil) +} +// NIL-RESULT-AND-NIL-ERROR: func nilResultAndNilError() async throws -> String { +// NIL-RESULT-AND-NIL-ERROR-NEXT: return nil +// NIL-RESULT-AND-NIL-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NIL-RESULT-AND-OPTIONAL-RELAYED-ERROR %s +func nilResultAndOptionalRelayedError(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion(nil, err) + } +} +// NIL-RESULT-AND-OPTIONAL-RELAYED-ERROR: func nilResultAndOptionalRelayedError() async throws -> String { +// NIL-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: let res = try await simpleErr(arg: "test") +// NIL-RESULT-AND-OPTIONAL-RELAYED-ERROR-EMPTY: +// NIL-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: } + +// This code doesn't compile after refactoring because we can't throw an optional error returned from makeOptionalError(). +// But it's not clear what the intended result should be either. +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NIL-RESULT-AND-OPTIONAL-COMPLEX-ERROR %s +func nilResultAndOptionalComplexError(completion: @escaping (String?, Error?) -> Void) { + completion(nil, makeOptionalError()) +} +// NIL-RESULT-AND-OPTIONAL-COMPLEX-ERROR: func nilResultAndOptionalComplexError() async throws -> String { +// NIL-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: throw makeOptionalError() +// NIL-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NIL-RESULT-AND-NON-OPTIONAL-ERROR %s +func nilResultAndNonOptionalError(completion: @escaping (String?, Error?) -> Void) { + completion(nil, CustomError.Bad) +} +// NIL-RESULT-AND-NON-OPTIONAL-ERROR: func nilResultAndNonOptionalError() async throws -> String { +// NIL-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: throw CustomError.Bad +// NIL-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: } + +// In this case, we are previously ignoring the error returned from simpleErr but are rethrowing it in the refactored case. +// That's probably fine although it changes semantics. +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR %s +func optionalRelayedResultAndNilError(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion(res, nil) + } +} +// OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR: func optionalRelayedResultAndNilError() async throws -> String { +// OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR-NEXT: let res = try await simpleErr(arg: "test") +// OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR-NEXT: return res +// OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-RELAYED-ERROR %s +func optionalRelayedResultAndOptionalRelayedError(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion(res, err) + } +} +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-RELAYED-ERROR: func optionalRelayedResultAndOptionalRelayedError() async throws -> String { +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: let res = try await simpleErr(arg: "test") +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: return res +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR %s +func optionalRelayedResultAndOptionalComplexError(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion(res, makeOptionalError()) + } +} +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR: func optionalRelayedResultAndOptionalComplexError() async throws -> String { +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: let res = try await simpleErr(arg: "test") +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: if let error = makeOptionalError() { +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: throw error +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } else { +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: return res +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } +// OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR %s +func optionalRelayedResultAndNonOptionalError(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion(res, CustomError.Bad) + } +} +// OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR: func optionalRelayedResultAndNonOptionalError() async throws -> String { +// OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: let res = try await simpleErr(arg: "test") +// OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: throw CustomError.Bad +// OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR %s +func nonOptionalRelayedResultAndNilError(completion: @escaping (String?, Error?) -> Void) { + simple { res in + completion(res, nil) + } +} +// NON-OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR: func nonOptionalRelayedResultAndNilError() async throws -> String { +// NON-OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR-NEXT: let res = await simple() +// NON-OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR-NEXT: return res +// NON-OPTIONAL-RELAYED-RESULT-AND-NIL-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR %s +func nonOptionalRelayedResultAndOptionalComplexError(completion: @escaping (String?, Error?) -> Void) { + simple { res in + completion(res, makeOptionalError()) + } +} +// NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR: func nonOptionalRelayedResultAndOptionalComplexError() async throws -> String { +// NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: let res = await simple() +// NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: if let error = makeOptionalError() { +// NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: throw error +// NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } else { +// NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: return res +// NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } +// NON-OPTIONAL-RELAYED-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR %s +func nonOptionalRelayedResultAndNonOptionalError(completion: @escaping (String?, Error?) -> Void) { + simple { res in + completion(res, CustomError.Bad) + } +} +// NON-OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR: func nonOptionalRelayedResultAndNonOptionalError() async throws -> String { +// NON-OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: let res = await simple() +// NON-OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: throw CustomError.Bad +// NON-OPTIONAL-RELAYED-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: } + +// The refactored code doesn't compile because we can't return an optional String from the async function. +// But it's not clear what the intended result should be either, because `error` is always `nil`. +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-COMPLEX-RESULT-AND-NIL-ERROR %s +func optionalComplexResultAndNilError(completion: @escaping (String?, Error?) -> Void) { + completion(makeOptionalString(), nil) +} +// OPTIONAL-COMPLEX-RESULT-AND-NIL-ERROR: func optionalComplexResultAndNilError() async throws -> String { +// OPTIONAL-COMPLEX-RESULT-AND-NIL-ERROR-NEXT: return makeOptionalString() +// OPTIONAL-COMPLEX-RESULT-AND-NIL-ERROR-NEXT: } + +// The refactored code doesn't compile because we can't return an optional +// String from the async function. +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-RELAYED-ERROR %s +func optionalComplexResultAndOptionalRelayedError(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion(makeOptionalString(), err) + } +} +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-RELAYED-ERROR: func optionalComplexResultAndOptionalRelayedError() async throws -> String { +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: let res = try await simpleErr(arg: "test") +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: return makeOptionalString() +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: } + +// The refactored code doesn't compile because we can't return an optional +// String or throw an optional Error from the async function. +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-COMPLEX-ERROR %s +func optionalComplexResultAndOptionalComplexError(completion: @escaping (String?, Error?) -> Void) { + completion(makeOptionalString(), makeOptionalError()) +} +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-COMPLEX-ERROR: func optionalComplexResultAndOptionalComplexError() async throws -> String { +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: if let error = makeOptionalError() { +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: throw error +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } else { +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: return makeOptionalString() +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } +// OPTIONAL-COMPLEX-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=OPTIONAL-COMPLEX-RESULT-AND-NON-OPTIONAL-ERROR %s +func optionalComplexResultAndNonOptionalError(completion: @escaping (String?, Error?) -> Void) { + completion(makeOptionalString(), CustomError.Bad) +} +// OPTIONAL-COMPLEX-RESULT-AND-NON-OPTIONAL-ERROR: func optionalComplexResultAndNonOptionalError() async throws -> String { +// OPTIONAL-COMPLEX-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: throw CustomError.Bad +// OPTIONAL-COMPLEX-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-OPTIONAL-RESULT-AND-NIL-ERROR %s +func nonOptionalResultAndNilError(completion: @escaping (String?, Error?) -> Void) { + completion("abc", nil) +} +// NON-OPTIONAL-RESULT-AND-NIL-ERROR: func nonOptionalResultAndNilError() async throws -> String { +// NON-OPTIONAL-RESULT-AND-NIL-ERROR-NEXT: return "abc" +// NON-OPTIONAL-RESULT-AND-NIL-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-OPTIONAL-RESULT-AND-OPTIONAL-RELAYED-ERROR %s +func nonOptionalResultAndOptionalRelayedError(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion("abc", err) + } +} +// NON-OPTIONAL-RESULT-AND-OPTIONAL-RELAYED-ERROR: func nonOptionalResultAndOptionalRelayedError() async throws -> String { +// NON-OPTIONAL-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: let res = try await simpleErr(arg: "test") +// NON-OPTIONAL-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: return "abc" +// NON-OPTIONAL-RESULT-AND-OPTIONAL-RELAYED-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-OPTIONAL-RESULT-AND-OPTIONAL-COMPLEX-ERROR %s +func nonOptionalResultAndOptionalComplexError(completion: @escaping (String?, Error?) -> Void) { + completion("abc", makeOptionalError()) +} +// NON-OPTIONAL-RESULT-AND-OPTIONAL-COMPLEX-ERROR: func nonOptionalResultAndOptionalComplexError() async throws -> String { +// NON-OPTIONAL-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: if let error = makeOptionalError() { +// NON-OPTIONAL-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: throw error +// NON-OPTIONAL-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } else { +// NON-OPTIONAL-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: return "abc" +// NON-OPTIONAL-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } +// NON-OPTIONAL-RESULT-AND-OPTIONAL-COMPLEX-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-OPTIONAL-RESULT-AND-NON-OPTIONAL-ERROR %s +func nonOptionalResultAndNonOptionalError(completion: @escaping (String?, Error?) -> Void) { + completion("abc", CustomError.Bad) +} +// NON-OPTIONAL-RESULT-AND-NON-OPTIONAL-ERROR: func nonOptionalResultAndNonOptionalError() async throws -> String { +// NON-OPTIONAL-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: throw CustomError.Bad +// NON-OPTIONAL-RESULT-AND-NON-OPTIONAL-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=WRAP-COMPLETION-CALL-IN-PARENS %s +func wrapCompletionCallInParenthesis(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + (completion(res, err)) + } +} +// WRAP-COMPLETION-CALL-IN-PARENS: func wrapCompletionCallInParenthesis() async throws -> String { +// WRAP-COMPLETION-CALL-IN-PARENS-NEXT: let res = try await simpleErr(arg: "test") +// WRAP-COMPLETION-CALL-IN-PARENS-NEXT: return res +// WRAP-COMPLETION-CALL-IN-PARENS-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=WRAP-RESULT-IN-PARENS %s +func wrapResultInParenthesis(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion((res).self, err) + } +} +// WRAP-RESULT-IN-PARENS: func wrapResultInParenthesis() async throws -> String { +// WRAP-RESULT-IN-PARENS-NEXT: let res = try await simpleErr(arg: "test") +// WRAP-RESULT-IN-PARENS-NEXT: return res +// WRAP-RESULT-IN-PARENS-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=TWO-COMPLETION-HANDLER-CALLS %s +func twoCompletionHandlerCalls(completion: @escaping (String?, Error?) -> Void) { + simpleErr(arg: "test") { (res, err) in + completion(res, err) + completion(res, err) + } +} +// TWO-COMPLETION-HANDLER-CALLS: func twoCompletionHandlerCalls() async throws -> String { +// TWO-COMPLETION-HANDLER-CALLS-NEXT: let res = try await simpleErr(arg: "test") +// TWO-COMPLETION-HANDLER-CALLS-NEXT: return res +// TWO-COMPLETION-HANDLER-CALLS-NEXT: return res +// TWO-COMPLETION-HANDLER-CALLS-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NESTED-IGNORED %s +func nestedIgnored() throws { + simple { _ in + print("done") + simple { _ in + print("done") + } + } +} +// NESTED-IGNORED: func nestedIgnored() async throws { +// NESTED-IGNORED-NEXT: let _ = await simple() +// NESTED-IGNORED-NEXT: print("done") +// NESTED-IGNORED-NEXT: let _ = await simple() +// NESTED-IGNORED-NEXT: print("done") +// NESTED-IGNORED-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=IGNORED-ERR %s +func nestedIgnoredErr() throws { + simpleErr(arg: "") { str, _ in + if str == nil { + print("error") + } + + simpleErr(arg: "") { str, _ in + if str == nil { + print("error") + } + } + } +} +// IGNORED-ERR: func nestedIgnoredErr() async throws { +// IGNORED-ERR-NEXT: do { +// IGNORED-ERR-NEXT: let str = try await simpleErr(arg: "") +// IGNORED-ERR-NEXT: do { +// IGNORED-ERR-NEXT: let str1 = try await simpleErr(arg: "") +// IGNORED-ERR-NEXT: } catch { +// IGNORED-ERR-NEXT: print("error") +// IGNORED-ERR-NEXT: } +// IGNORED-ERR-NEXT: } catch { +// IGNORED-ERR-NEXT: print("error") +// IGNORED-ERR-NEXT: } +// IGNORED-ERR-NEXT: } diff --git a/test/refactoring/ConvertAsync/convert_invalid.swift b/test/refactoring/ConvertAsync/convert_invalid.swift new file mode 100644 index 0000000000000..7d17fbdea47a8 --- /dev/null +++ b/test/refactoring/ConvertAsync/convert_invalid.swift @@ -0,0 +1,33 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +func callbackIntWithError(_ completion: @escaping (Bool, Error?) -> Void) {} + +// rdar://79864182 +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=INVALID-COND %s +callbackIntWithError { x, err in + if x { + print("ok") + } +} +// INVALID-COND: let x = try await callbackIntWithError() +// INVALID-COND-NEXT: if x { +// INVALID-COND-NEXT: print("ok") +// INVALID-COND-NEXT: } + +func withoutAsyncAlternative(closure: (Int) -> Void) {} + +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=UNKNOWN-ERROR-IN-CONTINUATION %s +func testUnknownErrorInContinuation(completionHandler: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternative { theValue in + completionHandler(theValue, MyUndefinedError()) + } +} +// UNKNOWN-ERROR-IN-CONTINUATION: func testUnknownErrorInContinuation() async throws -> Int { +// UNKNOWN-ERROR-IN-CONTINUATION-NEXT: return try await withCheckedThrowingContinuation { continuation in +// UNKNOWN-ERROR-IN-CONTINUATION-NEXT: withoutAsyncAlternative { theValue in +// UNKNOWN-ERROR-IN-CONTINUATION-NEXT: continuation.resume(throwing: MyUndefinedError()) +// UNKNOWN-ERROR-IN-CONTINUATION-NEXT: } +// UNKNOWN-ERROR-IN-CONTINUATION-NEXT: } +// UNKNOWN-ERROR-IN-CONTINUATION-NEXT: } diff --git a/test/refactoring/ConvertAsync/convert_params_multi.swift b/test/refactoring/ConvertAsync/convert_params_multi.swift index 761f36b681bce..40606ffa897c2 100644 --- a/test/refactoring/ConvertAsync/convert_params_multi.swift +++ b/test/refactoring/ConvertAsync/convert_params_multi.swift @@ -1,206 +1,226 @@ -func manyWithError(_ completion: (String?, Int?, Error?) -> Void) { } -func mixed(_ completion: (String?, Int) -> Void) { } -func mixedError(_ completion: (String?, Int, Error?) -> Void) { } +// RUN: %empty-directory(%t) -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s -manyWithError { res1, res2, err in - print("before") - if let bad = err { - print("got error \(bad)") - return - } - if let str = res1, let i = res2 { - print("got result \(str)") - print("got result \(i)") - } - print("after") -} -// MANYBOUND: do { -// MANYBOUND-NEXT: let (str, i) = try await manyWithError() -// MANYBOUND-NEXT: print("before") -// MANYBOUND-NEXT: print("got result \(str)") -// MANYBOUND-NEXT: print("got result \(i)") -// MANYBOUND-NEXT: print("after") -// MANYBOUND-NEXT: } catch let bad { -// MANYBOUND-NEXT: print("got error \(bad)") -// MANYBOUND-NEXT: } +// REQUIRES: concurrency -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s -manyWithError { res1, res2, err in - print("before") - if let str = res1 { - print("got result \(str)") - } else if let i = res2 { - print("got result \(i)") - } else { - print("got error \(err!)") - } - print("after") -} -// MANYUNBOUND-ERR: do { -// MANYUNBOUND-ERR-NEXT: let (str, i) = try await manyWithError() -// MANYUNBOUND-ERR-NEXT: print("before") -// MANYUNBOUND-ERR-NEXT: print("got result \(str)") -// MANYUNBOUND-ERR-NEXT: print("got result \(i)") -// MANYUNBOUND-ERR-NEXT: print("after") -// MANYUNBOUND-ERR-NEXT: } catch let err { -// MANYUNBOUND-ERR-NEXT: print("got error \(err)") -// MANYUNBOUND-ERR-NEXT: } +func manyWithError() async throws -> (String, Int) { ("", 0) } +func manyWithError(_ completion: @escaping (String?, Int?, Error?) -> Void) { } + +func mixed() async -> (String, Int) { ("", 0) } +func mixed(_ completion: @escaping (String?, Int) -> Void) { } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s -manyWithError { res1, res2, err in - print("before") - if let bad = err { - print("got error \(bad)") - return +func mixedError() async throws -> (String, Int) { ("", 0) } +func mixedError(_ completion: @escaping (String?, Int, Error?) -> Void) { } + +func testParamsMulti() async throws { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s + manyWithError { res1, res2, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + if let str = res1, let i = res2 { + print("got result \(str)") + print("got result \(i)") + } + print("after") } - if let str = res1 { - print("got result \(str)") + // MANYBOUND: do { + // MANYBOUND-NEXT: let (str, i) = try await manyWithError() + // MANYBOUND-NEXT: print("before") + // MANYBOUND-NEXT: print("got result \(str)") + // MANYBOUND-NEXT: print("got result \(i)") + // MANYBOUND-NEXT: print("after") + // MANYBOUND-NEXT: } catch let bad { + // MANYBOUND-NEXT: print("got error \(bad)") + // MANYBOUND-NEXT: } + + // FIXME: This case is a little tricky: Being in the else block of 'if let str = res1' + // should allow us to place 'if let i = res2' in the failure block. However, this + // is a success condition, so we still place it in the success block. Really what + // we need to do here is check to see if manyWithError has an existing async + // alternative that still returns optional success values, and allow success + // classification in that case. Otherwise, we'd probably be better off leaving + // the condition unhandled, as it's not clear what the user is doing. + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s + manyWithError { res1, res2, err in + print("before") + if let str = res1 { + print("got result \(str)") + } else if let i = res2 { + print("got result \(i)") + } else { + print("got error \(err!)") + } + print("after") } - if let i = res2 { - print("got result \(i)") + // MANYUNBOUND-ERR: do { + // MANYUNBOUND-ERR-NEXT: let (str, i) = try await manyWithError() + // MANYUNBOUND-ERR-NEXT: print("before") + // MANYUNBOUND-ERR-NEXT: print("got result \(str)") + // MANYUNBOUND-ERR-NEXT: print("got result \(i)") + // MANYUNBOUND-ERR-NEXT: print("after") + // MANYUNBOUND-ERR-NEXT: } catch let err { + // MANYUNBOUND-ERR-NEXT: print("got error \(err)") + // MANYUNBOUND-ERR-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s + manyWithError { res1, res2, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + if let str = res1 { + print("got result \(str)") + } + if let i = res2 { + print("got result \(i)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-COND %s -manyWithError { res1, res2, err in - print("before") - if res1 != nil && res2 == nil { - print("got result \(res1!)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-COND %s + manyWithError { res1, res2, err in + print("before") + if res1 != nil && res2 == nil { + print("got result \(res1!)") + } + print("after") } - print("after") -} -// MIXED-COND: convert_params_multi.swift -// MIXED-COND-NEXT: let (res1, res2) = try await manyWithError() -// MIXED-COND-NEXT: print("before") -// MIXED-COND-NEXT: if <#res1#> != nil && <#res2#> == nil { -// MIXED-COND-NEXT: print("got result \(res1)") -// MIXED-COND-NEXT: } -// MIXED-COND-NEXT: print("after") + // MIXED-COND: convert_params_multi.swift + // MIXED-COND-NEXT: let (res1, res2) = try await manyWithError() + // MIXED-COND-NEXT: print("before") + // MIXED-COND-NEXT: if <#res1#> != nil && <#res2#> == nil { + // MIXED-COND-NEXT: print("got result \(res1)") + // MIXED-COND-NEXT: } + // MIXED-COND-NEXT: print("after") -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-CONDELSE %s -manyWithError { res1, res2, err in - print("before") - if res1 != nil && res2 == nil { - print("got result \(res1!)") - } else { - print("bad") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-CONDELSE %s + manyWithError { res1, res2, err in + print("before") + if res1 != nil && res2 == nil { + print("got result \(res1!)") + } else { + print("bad") + } + print("after") } - print("after") -} -// MIXED-CONDELSE: var res1: String? = nil -// MIXED-CONDELSE-NEXT: var res2: Int? = nil -// MIXED-CONDELSE-NEXT: var err: Error? = nil -// MIXED-CONDELSE-NEXT: do { -// MIXED-CONDELSE-NEXT: (res1, res2) = try await manyWithError() -// MIXED-CONDELSE-NEXT: } catch { -// MIXED-CONDELSE-NEXT: err = error -// MIXED-CONDELSE-NEXT: } -// MIXED-CONDELSE-EMPTY: -// MIXED-CONDELSE-NEXT: print("before") -// MIXED-CONDELSE-NEXT: if res1 != nil && res2 == nil { -// MIXED-CONDELSE-NEXT: print("got result \(res1!)") -// MIXED-CONDELSE-NEXT: } else { -// MIXED-CONDELSE-NEXT: print("bad") -// MIXED-CONDELSE-NEXT: } -// MIXED-CONDELSE-NEXT: print("after") + // MIXED-CONDELSE: var res1: String? = nil + // MIXED-CONDELSE-NEXT: var res2: Int? = nil + // MIXED-CONDELSE-NEXT: var err: Error? = nil + // MIXED-CONDELSE-NEXT: do { + // MIXED-CONDELSE-NEXT: (res1, res2) = try await manyWithError() + // MIXED-CONDELSE-NEXT: } catch { + // MIXED-CONDELSE-NEXT: err = error + // MIXED-CONDELSE-NEXT: } + // MIXED-CONDELSE-EMPTY: + // MIXED-CONDELSE-NEXT: print("before") + // MIXED-CONDELSE-NEXT: if res1 != nil && res2 == nil { + // MIXED-CONDELSE-NEXT: print("got result \(res1!)") + // MIXED-CONDELSE-NEXT: } else { + // MIXED-CONDELSE-NEXT: print("bad") + // MIXED-CONDELSE-NEXT: } + // MIXED-CONDELSE-NEXT: print("after") -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s -manyWithError { res1, res2, err in - print("before") - guard let str = res1, let i = res2 else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s + manyWithError { res1, res2, err in + print("before") + guard let str = res1, let i = res2 else { + print("got error \(err!)") + return + } + print("got result \(str)") + print("got result \(i)") + print("after") } - print("got result \(str)") - print("got result \(i)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s -manyWithError { res1, res2, err in - print("before") - guard res1 != nil && res2 != nil && err == nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s + manyWithError { res1, res2, err in + print("before") + guard res1 != nil && res2 != nil && err == nil else { + print("got error \(err!)") + return + } + print("got result \(res1!)") + print("got result \(res2!)") + print("after") } - print("got result \(res1!)") - print("got result \(res2!)") - print("after") -} -// MANYUNBOUND: do { -// MANYUNBOUND-NEXT: let (res1, res2) = try await manyWithError() -// MANYUNBOUND-NEXT: print("before") -// MANYUNBOUND-NEXT: print("got result \(res1)") -// MANYUNBOUND-NEXT: print("got result \(res2)") -// MANYUNBOUND-NEXT: print("after") -// MANYUNBOUND-NEXT: } catch let err { -// MANYUNBOUND-NEXT: print("got error \(err)") -// MANYUNBOUND-NEXT: } + // MANYUNBOUND: do { + // MANYUNBOUND-NEXT: let (res1, res2) = try await manyWithError() + // MANYUNBOUND-NEXT: print("before") + // MANYUNBOUND-NEXT: print("got result \(res1)") + // MANYUNBOUND-NEXT: print("got result \(res2)") + // MANYUNBOUND-NEXT: print("after") + // MANYUNBOUND-NEXT: } catch let err { + // MANYUNBOUND-NEXT: print("got error \(err)") + // MANYUNBOUND-NEXT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s -manyWithError { res1, res2, err in - print("before") - guard res1 != nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s + manyWithError { res1, res2, err in + print("before") + guard res1 != nil else { + print("got error \(err!)") + return + } + print("got result \(res1!)") + print("got result \(res2!)") + print("after") } - print("got result \(res1!)") - print("got result \(res2!)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s -manyWithError { res1, res2, err in - print("before") - guard err == nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s + manyWithError { res1, res2, err in + print("before") + guard err == nil else { + print("got error \(err!)") + return + } + print("got result \(res1!)") + print("got result \(res2!)") + print("after") } - print("got result \(res1!)") - print("got result \(res2!)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED %s -mixed { str, num in - print("before") - if let res = str { - print("got result \(res)") + // Cannot use refactor-check-compiles, as cannot use non-optional 'str' in + // optional binding. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED %s + mixed { str, num in + print("before") + if let res = str { + print("got result \(res)") + } + print("\(num)") + print("after") } - print("\(num)") - print("after") -} -// MIXED: convert_params_multi.swift -// MIXED-NEXT: let (str, num) = await mixed() -// MIXED-NEXT: print("before") -// MIXED-NEXT: if let res = str { -// MIXED-NEXT: print("got result \(res)") -// MIXED-NEXT: } -// MIXED-NEXT: print("\(num)") -// MIXED-NEXT: print("after") -// MIXED-NOT: } + // MIXED: convert_params_multi.swift + // MIXED-NEXT: let (str, num) = await mixed() + // MIXED-NEXT: print("before") + // MIXED-NEXT: if let res = str { + // MIXED-NEXT: print("got result \(res)") + // MIXED-NEXT: } + // MIXED-NEXT: print("\(num)") + // MIXED-NEXT: print("after") + // MIXED-NOT: } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-ERROR %s -mixedError { str, num, err in - print("before") - if let res = str { - print("got result \(res)") - } else { - print("got \(err!)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-ERROR %s + mixedError { str, num, err in + print("before") + if let res = str { + print("got result \(res)") + } else { + print("got \(err!)") + } + print("\(num)") + print("after") } - print("\(num)") - print("after") + // MIXED-ERROR: convert_params_multi.swift + // MIXED-ERROR-NEXT: do { + // MIXED-ERROR-NEXT: let (res, num) = try await mixedError() + // MIXED-ERROR-NEXT: print("before") + // MIXED-ERROR-NEXT: print("got result \(res)") + // MIXED-ERROR-NEXT: print("\(num)") + // MIXED-ERROR-NEXT: print("after") + // MIXED-ERROR-NEXT: } catch let err { + // MIXED-ERROR-NEXT: print("got \(err)") + // MIXED-ERROR-NEXT: } + // MIXED-ERROR-NOT: } } -// MIXED-ERROR: convert_params_multi.swift -// MIXED-ERROR-NEXT: do { -// MIXED-ERROR-NEXT: let (res, num) = try await mixedError() -// MIXED-ERROR-NEXT: print("before") -// MIXED-ERROR-NEXT: print("got result \(res)") -// MIXED-ERROR-NEXT: print("\(num)") -// MIXED-ERROR-NEXT: print("after") -// MIXED-ERROR-NEXT: } catch let err { -// MIXED-ERROR-NEXT: print("got \(err)") -// MIXED-ERROR-NEXT: } -// MIXED-ERROR-NOT: } diff --git a/test/refactoring/ConvertAsync/convert_params_single.swift b/test/refactoring/ConvertAsync/convert_params_single.swift index 5c3396419416f..b3f47b5ab3647 100644 --- a/test/refactoring/ConvertAsync/convert_params_single.swift +++ b/test/refactoring/ConvertAsync/convert_params_single.swift @@ -1,480 +1,537 @@ -func withError(_ completion: (String?, Error?) -> Void) { } -func notOptional(_ completion: (String, Error?) -> Void) { } -func errorOnly(_ completion: (Error?) -> Void) { } +// RUN: %empty-directory(%t) + +// REQUIRES: concurrency + +func withError() async throws -> String { "" } +func withError(_ completion: @escaping (String?, Error?) -> Void) { } + +func notOptional() async throws -> String { "" } +func notOptional(_ completion: @escaping (String, Error?) -> Void) { } + +func errorOnly() async throws { } +func errorOnly(_ completion: @escaping (Error?) -> Void) { } + func test(_ str: String) -> Bool { return false } -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNRELATED %s -withError { res, err in - if test("unrelated") { - print("unrelated") - } else { - print("else unrelated") +func testParamsSingle() async throws { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNRELATED %s + withError { res, err in + if test("unrelated") { + print("unrelated") + } else { + print("else unrelated") + } } -} -// UNRELATED: convert_params_single.swift -// UNRELATED-NEXT: let res = try await withError() -// UNRELATED-NEXT: if test("unrelated") { -// UNRELATED-NEXT: print("unrelated") -// UNRELATED-NEXT: } else { -// UNRELATED-NEXT: print("else unrelated") -// UNRELATED-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - return + // UNRELATED: convert_params_single.swift + // UNRELATED-NEXT: let res = try await withError() + // UNRELATED-NEXT: if test("unrelated") { + // UNRELATED-NEXT: print("unrelated") + // UNRELATED-NEXT: } else { + // UNRELATED-NEXT: print("else unrelated") + // UNRELATED-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + if let str = res { + print("got result \(str)") + } + print("after") } - if let str = res { + // BOUND: do { + // BOUND-NEXT: let str = try await withError() + // BOUND-NEXT: print("before") + // BOUND-NEXT: print("got result \(str)") + // BOUND-NEXT: print("after") + // BOUND-NEXT: } catch let bad { + // BOUND-NEXT: print("got error \(bad)") + // BOUND-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND-COMMENT %s + withError { res, err in // a + // b + print("before") + // c + if let bad = err { // d + // e + print("got error \(bad)") + // f + return + // g + } + // h + if let str = res { // i + // j + print("got result \(str)") + // k + } + // l + print("after") + // m + } + // BOUND-COMMENT: do { + // BOUND-COMMENT-NEXT: let str = try await withError() + // BOUND-COMMENT-NEXT: // a + // BOUND-COMMENT-NEXT: // b + // BOUND-COMMENT-NEXT: print("before") + // BOUND-COMMENT-NEXT: // c + // BOUND-COMMENT-NEXT: // h + // BOUND-COMMENT-NEXT: // i + // BOUND-COMMENT-NEXT: // j + // BOUND-COMMENT-NEXT: print("got result \(str)") + // BOUND-COMMENT-NEXT: // k + // BOUND-COMMENT-NEXT: // l + // BOUND-COMMENT-NEXT: print("after") + // BOUND-COMMENT-NEXT: // m + // BOUND-COMMENT-NEXT: {{ }} + // BOUND-COMMENT-NEXT: } catch let bad { + // BOUND-COMMENT-NEXT: // d + // BOUND-COMMENT-NEXT: // e + // BOUND-COMMENT-NEXT: print("got error \(bad)") + // BOUND-COMMENT-NEXT: // f + // BOUND-COMMENT-NEXT: // g + // BOUND-COMMENT-NEXT: {{ }} + // BOUND-COMMENT-NEXT: } + + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s + withError { res, err in + print("before") + guard let str = res else { + print("got error \(err!)") + return + } print("got result \(str)") + print("after") } - print("after") -} -// BOUND: do { -// BOUND-NEXT: let str = try await withError() -// BOUND-NEXT: print("before") -// BOUND-NEXT: print("got result \(str)") -// BOUND-NEXT: print("after") -// BOUND-NEXT: } catch let bad { -// BOUND-NEXT: print("got error \(bad)") -// BOUND-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s -withError { res, err in - print("before") - guard let str = res else { - print("got error \(err!)") - return + // UNBOUND-ERR: do { + // UNBOUND-ERR-NEXT: let str = try await withError() + // UNBOUND-ERR-NEXT: print("before") + // UNBOUND-ERR-NEXT: print("got result \(str)") + // UNBOUND-ERR-NEXT: print("after") + // UNBOUND-ERR-NEXT: } catch let err { + // UNBOUND-ERR-NEXT: print("got error \(err)") + // UNBOUND-ERR-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + } else if let str = res { + print("got result \(str)") + } + print("after") } - print("got result \(str)") - print("after") -} -// UNBOUND-ERR: do { -// UNBOUND-ERR-NEXT: let str = try await withError() -// UNBOUND-ERR-NEXT: print("before") -// UNBOUND-ERR-NEXT: print("got result \(str)") -// UNBOUND-ERR-NEXT: print("after") -// UNBOUND-ERR-NEXT: } catch let err { -// UNBOUND-ERR-NEXT: print("got error \(err)") -// UNBOUND-ERR-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - } else if let str = res { - print("got result \(str)") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + if let str = res { + print("got result \(str)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + return + } + print("got result \(res!)") + print("after") } - if let str = res { - print("got result \(str)") + // UNBOUND-RES: do { + // UNBOUND-RES-NEXT: let res = try await withError() + // UNBOUND-RES-NEXT: print("before") + // UNBOUND-RES-NEXT: print("got result \(res)") + // UNBOUND-RES-NEXT: print("after") + // UNBOUND-RES-NEXT: } catch let bad { + // UNBOUND-RES-NEXT: print("got error \(bad)") + // UNBOUND-RES-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s + withError { res, err in + print("before") + if let str = res { + print("got result \(str)") + print("after") + return + } + print("got error \(err!)") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + } else { + print("got result \(res!)") + } + print("after") } - print("got result \(res!)") - print("after") -} -// UNBOUND-RES: do { -// UNBOUND-RES-NEXT: let res = try await withError() -// UNBOUND-RES-NEXT: print("before") -// UNBOUND-RES-NEXT: print("got result \(res)") -// UNBOUND-RES-NEXT: print("after") -// UNBOUND-RES-NEXT: } catch let bad { -// UNBOUND-RES-NEXT: print("got error \(bad)") -// UNBOUND-RES-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s -withError { res, err in - print("before") - if let str = res { - print("got result \(str)") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s + withError { res, err in + print("before") + if let str = res { + print("got result \(str)") + } else { + print("got error \(err!)") + } print("after") - return } - print("got error \(err!)") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - } else { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if err != nil { + print("got error \(err!)") + return + } print("got result \(res!)") + print("after") } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s -withError { res, err in - print("before") - if let str = res { - print("got result \(str)") - } else { + // UNBOUND: do { + // UNBOUND-NEXT: let res = try await withError() + // UNBOUND-NEXT: print("before") + // UNBOUND-NEXT: print("got result \(res)") + // UNBOUND-NEXT: print("after") + // UNBOUND-NEXT: } catch let err { + // UNBOUND-NEXT: print("got error \(err)") + // UNBOUND-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if res != nil { + print("got result \(res!)") + print("after") + return + } print("got error \(err!)") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if err != nil { - print("got error \(err!)") - return - } - print("got result \(res!)") - print("after") -} -// UNBOUND: do { -// UNBOUND-NEXT: let res = try await withError() -// UNBOUND-NEXT: print("before") -// UNBOUND-NEXT: print("got result \(res)") -// UNBOUND-NEXT: print("after") -// UNBOUND-NEXT: } catch let err { -// UNBOUND-NEXT: print("got error \(err)") -// UNBOUND-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if res != nil { - print("got result \(res!)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if err != nil { + print("got error \(err!)") + } else { + print("got result \(res!)") + } print("after") - return } - print("got error \(err!)") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if err != nil { - print("got error \(err!)") - } else { - print("got result \(res!)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if res != nil { + print("got result \(res!)") + } else { + print("got error \(err!)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if res != nil { - print("got result \(res!)") - } else { - print("got error \(err!)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if err == nil { + print("got result \(res!)") + } else { + print("got error \(err!)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if err == nil { - print("got result \(res!)") - } else { - print("got error \(err!)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if res == nil { + print("got error \(err!)") + } else { + print("got result \(res!)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if res == nil { - print("got error \(err!)") - } else { - print("got result \(res!)") + // Cannot use refactor-check-compiles because of the placeholder, compiler is unable to infer 'str'. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNHANDLEDNESTED %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + } else { + if let str = res { + print("got result \(str)") + } + } + print("after") } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNHANDLEDNESTED %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") - } else { + // UNHANDLEDNESTED: do { + // UNHANDLEDNESTED-NEXT: let res = try await withError() + // UNHANDLEDNESTED-NEXT: print("before") + // UNHANDLEDNESTED-NEXT: if let str = <#res#> { + // UNHANDLEDNESTED-NEXT: print("got result \(str)") + // UNHANDLEDNESTED-NEXT: } + // UNHANDLEDNESTED-NEXT: print("after") + // UNHANDLEDNESTED-NEXT: } catch let bad { + // UNHANDLEDNESTED-NEXT: print("got error \(bad)") + // UNHANDLEDNESTED-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOERR %s + withError { res, err in + print("before") if let str = res { print("got result \(str)") } + print("after") } - print("after") -} -// UNHANDLEDNESTED: do { -// UNHANDLEDNESTED-NEXT: let res = try await withError() -// UNHANDLEDNESTED-NEXT: print("before") -// UNHANDLEDNESTED-NEXT: if let str = <#res#> { -// UNHANDLEDNESTED-NEXT: print("got result \(str)") -// UNHANDLEDNESTED-NEXT: } -// UNHANDLEDNESTED-NEXT: print("after") -// UNHANDLEDNESTED-NEXT: } catch let bad { -// UNHANDLEDNESTED-NEXT: print("got error \(bad)") -// UNHANDLEDNESTED-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOERR %s -withError { res, err in - print("before") - if let str = res { - print("got result \(str)") - } - print("after") -} -// NOERR: convert_params_single.swift -// NOERR-NEXT: let str = try await withError() -// NOERR-NEXT: print("before") -// NOERR-NEXT: print("got result \(str)") -// NOERR-NEXT: print("after") -// NOERR-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NORES %s -withError { res, err in - print("before") - if let bad = err { - print("got error \(bad)") + // NOERR: convert_params_single.swift + // NOERR-NEXT: let str = try await withError() + // NOERR-NEXT: print("before") + // NOERR-NEXT: print("got result \(str)") + // NOERR-NEXT: print("after") + // NOERR-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NORES %s + withError { res, err in + print("before") + if let bad = err { + print("got error \(bad)") + } + print("after") } - print("after") -} -// NORES: do { -// NORES-NEXT: let res = try await withError() -// NORES-NEXT: print("before") -// NORES-NEXT: print("after") -// NORES-NEXT: } catch let bad { -// NORES-NEXT: print("got error \(bad)") -// NORES-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - if res != nil && err == nil { - print("got result \(res!)") - } else { - print("got error \(err!)") + // NORES: do { + // NORES-NEXT: let res = try await withError() + // NORES-NEXT: print("before") + // NORES-NEXT: print("after") + // NORES-NEXT: } catch let bad { + // NORES-NEXT: print("got error \(bad)") + // NORES-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + if ((res != (nil)) && err == nil) { + print("got result \(res!)") + } else { + print("got error \(err!)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-COND %s -withError { res, err in - print("before") - if res != nil && test(res!) { - print("got result \(res!)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-COND %s + withError { res, err in + print("before") + if res != nil && test(res!) { + print("got result \(res!)") + } + print("after") } - print("after") -} -// UNKNOWN-COND: convert_params_single.swift -// UNKNOWN-COND-NEXT: let res = try await withError() -// UNKNOWN-COND-NEXT: print("before") -// UNKNOWN-COND-NEXT: if <#res#> != nil && test(res) { -// UNKNOWN-COND-NEXT: print("got result \(res)") -// UNKNOWN-COND-NEXT: } -// UNKNOWN-COND-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-CONDELSE %s -withError { res, err in - print("before") - if res != nil && test(res!) { - print("got result \(res!)") - } else { - print("bad") + // UNKNOWN-COND: convert_params_single.swift + // UNKNOWN-COND-NEXT: let res = try await withError() + // UNKNOWN-COND-NEXT: print("before") + // UNKNOWN-COND-NEXT: if <#res#> != nil && test(res) { + // UNKNOWN-COND-NEXT: print("got result \(res)") + // UNKNOWN-COND-NEXT: } + // UNKNOWN-COND-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-CONDELSE %s + withError { res, err in + print("before") + if res != nil && test(res!) { + print("got result \(res!)") + } else { + print("bad") + } + print("after") } - print("after") -} -// UNKNOWN-CONDELSE: var res: String? = nil -// UNKNOWN-CONDELSE-NEXT: var err: Error? = nil -// UNKNOWN-CONDELSE-NEXT: do { -// UNKNOWN-CONDELSE-NEXT: res = try await withError() -// UNKNOWN-CONDELSE-NEXT: } catch { -// UNKNOWN-CONDELSE-NEXT: err = error -// UNKNOWN-CONDELSE-NEXT: } -// UNKNOWN-CONDELSE-EMPTY: -// UNKNOWN-CONDELSE-NEXT: print("before") -// UNKNOWN-CONDELSE-NEXT: if res != nil && test(res!) { -// UNKNOWN-CONDELSE-NEXT: print("got result \(res!)") -// UNKNOWN-CONDELSE-NEXT: } else { -// UNKNOWN-CONDELSE-NEXT: print("bad") -// UNKNOWN-CONDELSE-NEXT: } -// UNKNOWN-CONDELSE-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIBIND %s -withError { res, err in - print("before") - if let str = res { - print("got result \(str)") + // UNKNOWN-CONDELSE: var res: String? = nil + // UNKNOWN-CONDELSE-NEXT: var err: Error? = nil + // UNKNOWN-CONDELSE-NEXT: do { + // UNKNOWN-CONDELSE-NEXT: res = try await withError() + // UNKNOWN-CONDELSE-NEXT: } catch { + // UNKNOWN-CONDELSE-NEXT: err = error + // UNKNOWN-CONDELSE-NEXT: } + // UNKNOWN-CONDELSE-EMPTY: + // UNKNOWN-CONDELSE-NEXT: print("before") + // UNKNOWN-CONDELSE-NEXT: if res != nil && test(res!) { + // UNKNOWN-CONDELSE-NEXT: print("got result \(res!)") + // UNKNOWN-CONDELSE-NEXT: } else { + // UNKNOWN-CONDELSE-NEXT: print("bad") + // UNKNOWN-CONDELSE-NEXT: } + // UNKNOWN-CONDELSE-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIBIND %s + withError { res, err in + print("before") + if let str = res { + print("got result \(str)") + } + if let str2 = res { + print("got result \(str2)") + } + if case (let str3?) = (res) { + print("got result \(str3)") + } + print("after") } - if let str2 = res { - print("got result \(str2)") + // MULTIBIND: let str = try await withError() + // MULTIBIND-NEXT: print("before") + // MULTIBIND-NEXT: print("got result \(str)") + // MULTIBIND-NEXT: print("got result \(str)") + // MULTIBIND-NEXT: print("got result \(str)") + // MULTIBIND-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDRET %s + withError { res, err in + print("before") + if let str = res { + if test(str) { + return + } + print("got result \(str)") + } + print("after") } - print("after") -} -// MULTIBIND: var res: String? = nil -// MULTIBIND-NEXT: var err: Error? = nil -// MULTIBIND-NEXT: do { -// MULTIBIND-NEXT: res = try await withError() -// MULTIBIND-NEXT: } catch { -// MULTIBIND-NEXT: err = error -// MULTIBIND-NEXT: } -// MULTIBIND-EMPTY: -// MULTIBIND-NEXT: print("before") -// MULTIBIND-NEXT: if let str = res { -// MULTIBIND-NEXT: print("got result \(str)") -// MULTIBIND-NEXT: } -// MULTIBIND-NEXT: if let str2 = res { -// MULTIBIND-NEXT: print("got result \(str2)") -// MULTIBIND-NEXT: } -// MULTIBIND-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDRET %s -withError { res, err in - print("before") - if let str = res { - if test(str) { + // NESTEDRET: convert_params_single.swift + // NESTEDRET-NEXT: let str = try await withError() + // NESTEDRET-NEXT: print("before") + // NESTEDRET-NEXT: if test(str) { + // NESTEDRET-NEXT: <#return#> + // NESTEDRET-NEXT: } + // NESTEDRET-NEXT: print("got result \(str)") + // NESTEDRET-NEXT: print("after") + // NESTEDRET-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s + withError { res, err in + print("before") + guard let str = res, err == nil else { + print("got error \(err!)") return } print("got result \(str)") + print("after") } - print("after") -} -// NESTEDRET: convert_params_single.swift -// NESTEDRET-NEXT: let str = try await withError() -// NESTEDRET-NEXT: print("before") -// NESTEDRET-NEXT: if test(str) { -// NESTEDRET-NEXT: <#return#> -// NESTEDRET-NEXT: } -// NESTEDRET-NEXT: print("got result \(str)") -// NESTEDRET-NEXT: print("after") -// NESTEDRET-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s -withError { res, err in - print("before") - guard let str = res, err == nil else { - print("got error \(err!)") - return - } - print("got result \(str)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - guard res != nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + guard res != nil else { + print("got error \(err!)") + return + } + print("got result \(res!)") + print("after") } - print("got result \(res!)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - guard err == nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + guard err == nil else { + print("got error \(err!)") + return + } + print("got result \(res!)") + print("after") } - print("got result \(res!)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s -withError { res, err in - print("before") - guard res != nil && err == nil else { - print("got error \(err!)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s + withError { res, err in + print("before") + guard res != nil && err == nil else { + print("got error \(err!)") + return + } + print("got result \(res!)") + print("after") } - print("got result \(res!)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNWRAPPING %s -withError { str, err in - print("before") - guard err == nil else { return } - _ = str!.count - _ = str?.count - _ = str!.count.bitWidth - _ = str?.count.bitWidth - _ = (str?.count.bitWidth)! - _ = str!.first?.isWhitespace - _ = str?.first?.isWhitespace - _ = (str?.first?.isWhitespace)! - print("after") -} -// UNWRAPPING: let str = try await withError() -// UNWRAPPING-NEXT: print("before") -// UNWRAPPING-NEXT: _ = str.count -// UNWRAPPING-NEXT: _ = str.count -// UNWRAPPING-NEXT: _ = str.count.bitWidth -// UNWRAPPING-NEXT: _ = str.count.bitWidth - -// Note this transform results in invalid code as str.count.bitWidth is no -// longer optional, but arguably it's more useful than leaving str as a -// placeholder. In general, the tranform we perform here is locally valid -// within the optional chain, but may change the type of the overall chain. -// UNWRAPPING-NEXT: _ = (str.count.bitWidth)! - -// UNWRAPPING-NEXT: _ = str.first?.isWhitespace -// UNWRAPPING-NEXT: _ = str.first?.isWhitespace -// UNWRAPPING-NEXT: _ = (str.first?.isWhitespace)! -// UNWRAPPING-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NOT-OPTIONAL %s -notOptional { str, err in - print("before") - if let err2 = err { - print("got error \(err2)") - return + // Cannot use refactor-check-compiles as transform results in invalid code, + // see comment below. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNWRAPPING %s + withError { str, err in + print("before") + guard err == nil else { return } + _ = str!.count + _ = /*before*/str!/*after*/.count + _ = str?.count + _ = str!.count.bitWidth + _ = str?.count.bitWidth + _ = (str?.count.bitWidth)! + _ = str!.first?.isWhitespace + _ = str?.first?.isWhitespace + _ = (str?.first?.isWhitespace)! + print("after") } - print("got result \(str)") - print("after") -} -// NOT-OPTIONAL: do { -// NOT-OPTIONAL-NEXT: let str = try await notOptional() -// NOT-OPTIONAL-NEXT: print("before") -// NOT-OPTIONAL-NEXT: print("got result \(str)") -// NOT-OPTIONAL-NEXT: print("after") -// NOT-OPTIONAL-NEXT: } catch let err2 { -// NOT-OPTIONAL-NEXT: print("got error \(err2)") -// NOT-OPTIONAL-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ERROR-ONLY %s -errorOnly { err in - print("before") - if let err2 = err { - print("got error \(err2)") - return + // UNWRAPPING: let str = try await withError() + // UNWRAPPING-NEXT: print("before") + // UNWRAPPING-NEXT: _ = str.count + // UNWRAPPING-NEXT: _ = /*before*/str/*after*/.count + // UNWRAPPING-NEXT: _ = str.count + // UNWRAPPING-NEXT: _ = str.count.bitWidth + // UNWRAPPING-NEXT: _ = str.count.bitWidth + + // Note this transform results in invalid code as str.count.bitWidth is no + // longer optional, but arguably it's more useful than leaving str as a + // placeholder. In general, the tranform we perform here is locally valid + // within the optional chain, but may change the type of the overall chain. + // UNWRAPPING-NEXT: _ = (str.count.bitWidth)! + + // UNWRAPPING-NEXT: _ = str.first?.isWhitespace + // UNWRAPPING-NEXT: _ = str.first?.isWhitespace + // UNWRAPPING-NEXT: _ = (str.first?.isWhitespace)! + // UNWRAPPING-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOT-OPTIONAL %s + notOptional { str, err in + print("before") + if let err2 = err { + print("got error \(err2)") + return + } + print("got result \(str)") + print("after") + } + // NOT-OPTIONAL: do { + // NOT-OPTIONAL-NEXT: let str = try await notOptional() + // NOT-OPTIONAL-NEXT: print("before") + // NOT-OPTIONAL-NEXT: print("got result \(str)") + // NOT-OPTIONAL-NEXT: print("after") + // NOT-OPTIONAL-NEXT: } catch let err2 { + // NOT-OPTIONAL-NEXT: print("got error \(err2)") + // NOT-OPTIONAL-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ERROR-ONLY %s + errorOnly { err in + print("before") + if let err2 = err { + print("got error \(err2)") + return + } + print("after") } - print("after") + // ERROR-ONLY: convert_params_single.swift + // ERROR-ONLY-NEXT: do { + // ERROR-ONLY-NEXT: try await errorOnly() + // ERROR-ONLY-NEXT: print("before") + // ERROR-ONLY-NEXT: print("after") + // ERROR-ONLY-NEXT: } catch let err2 { + // ERROR-ONLY-NEXT: print("got error \(err2)") + // ERROR-ONLY-NEXT: } + // ERROR-ONLY-NOT: } } -// ERROR-ONLY: convert_params_single.swift -// ERROR-ONLY-NEXT: do { -// ERROR-ONLY-NEXT: try await errorOnly() -// ERROR-ONLY-NEXT: print("before") -// ERROR-ONLY-NEXT: print("after") -// ERROR-ONLY-NEXT: } catch let err2 { -// ERROR-ONLY-NEXT: print("got error \(err2)") -// ERROR-ONLY-NEXT: } -// ERROR-ONLY-NOT: } diff --git a/test/refactoring/ConvertAsync/convert_pattern.swift b/test/refactoring/ConvertAsync/convert_pattern.swift new file mode 100644 index 0000000000000..ae2d4377ae634 --- /dev/null +++ b/test/refactoring/ConvertAsync/convert_pattern.swift @@ -0,0 +1,567 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +enum E : Error { case e } + +func anyCompletion(_ completion: @escaping (Any?, Error?) -> Void) {} +func anyResultCompletion(_ completion: @escaping (Result) -> Void) {} + +func stringTupleParam(_ completion: @escaping ((String, String)?, Error?) -> Void) {} +func stringTupleParam() async throws -> (String, String) {} + +func stringTupleResult(_ completion: @escaping (Result<(String, String), Error>) -> Void) {} +func stringTupleResult() async throws -> (String, String) {} + +func mixedTupleResult(_ completion: @escaping (Result<((Int, Float), String), Error>) -> Void) {} +func mixedTupleResult() async throws -> ((Int, Float), String) {} + +func multipleTupleParam(_ completion: @escaping ((String, String)?, (Int, Int)?, Error?) -> Void) {} +func multipleTupleParam() async throws -> ((String, String), (Int, Int)) {} + +func testPatterns() async throws { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE %s + stringTupleParam { strs, err in + guard let (str1, str2) = strs else { return } + print(str1, str2) + } + + // INLINE: let (str1, str2) = try await stringTupleParam() + // INLINE-NEXT: print(str1, str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-VAR %s + stringTupleParam { strs, err in + guard var (str1, str2) = strs else { return } + print(str1, str2) + } + + // INLINE-VAR: var (str1, str2) = try await stringTupleParam() + // INLINE-VAR-NEXT: print(str1, str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-BLANK %s + stringTupleParam { strs, err in + guard var (_, str2) = strs else { return } + print(str2) + } + + // INLINE-BLANK: var (_, str2) = try await stringTupleParam() + // INLINE-BLANK-NEXT: print(str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-TYPED %s + stringTupleParam { strs, err in + guard let (str1, str2): (String, String) = strs else { return } + print(str1, str2) + } + + // INLINE-TYPED: let (str1, str2) = try await stringTupleParam() + // INLINE-TYPED-NEXT: print(str1, str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-CASE %s + stringTupleParam { strs, err in + guard case (let str1, var str2)? = strs else { return } + print(str1, str2) + } + + // INLINE-CASE: var (str1, str2) = try await stringTupleParam() + // INLINE-CASE-NEXT: print(str1, str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=INLINE-CASE-TYPED %s + stringTupleParam { strs, err in + guard case let (str1, str2)?: (String, String)? = strs else { return } + print(str1, str2) + } + + // INLINE-CASE-TYPED: let (str1, str2) = try await stringTupleParam() + // INLINE-CASE-TYPED-NEXT: print(str1, str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=OUT-OF-LINE %s + stringTupleParam { strs, err in + guard let (str1, str2) = strs else { return } + print(str1, str2, strs!) + } + + // OUT-OF-LINE: let strs = try await stringTupleParam() + // OUT-OF-LINE-NEXT: let (str1, str2) = strs + // OUT-OF-LINE-NEXT: print(str1, str2, strs) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=OUT-OF-LINE-VAR %s + stringTupleParam { strs, err in + guard var (str1, _) = strs else { return } + str1 = "" + print(str1, {strs!}) + } + + // OUT-OF-LINE-VAR: let strs = try await stringTupleParam() + // OUT-OF-LINE-VAR-NEXT: var (str1, _) = strs + // OUT-OF-LINE-VAR-NEXT: str1 = "" + // OUT-OF-LINE-VAR-NEXT: print(str1, {strs}) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=OUT-OF-LINE-CASE %s + stringTupleParam { strs, err in + guard case (let str1, var str2)? = strs else { return } + print(str1, str2, strs!) + } + + // OUT-OF-LINE-CASE: let strs = try await stringTupleParam() + // OUT-OF-LINE-CASE-NEXT: var (str1, str2) = strs + // OUT-OF-LINE-CASE-NEXT: print(str1, str2, strs) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=FALLBACK %s + stringTupleParam { strs, err in + guard let (str1, str2) = strs, str1 == "hi" else { fatalError() } + print(str1, str2, err) + } + + // FALLBACK: var strs: (String, String)? = nil + // FALLBACK-NEXT: var err: Error? = nil + // FALLBACK-NEXT: do { + // FALLBACK-NEXT: strs = try await stringTupleParam() + // FALLBACK-NEXT: } catch { + // FALLBACK-NEXT: err = error + // FALLBACK-NEXT: } + // FALLBACK-EMPTY: + // FALLBACK-NEXT: guard let (str1, str2) = strs, str1 == "hi" else { fatalError() } + // FALLBACK-NEXT: print(str1, str2, err) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=GUARD-AND-UNHANDLED %s + stringTupleParam { strs, err in + guard let (str1, str2) = strs else { fatalError() } + print(str1, str2) + if .random(), err == nil { + print("yay") + } else { + print("nay") + } + } + + // GUARD-AND-UNHANDLED: do { + // GUARD-AND-UNHANDLED-NEXT: let (str1, str2) = try await stringTupleParam() + // GUARD-AND-UNHANDLED-NEXT: print(str1, str2) + // GUARD-AND-UNHANDLED-NEXT: if .random(), <#err#> == nil { + // GUARD-AND-UNHANDLED-NEXT: print("yay") + // GUARD-AND-UNHANDLED-NEXT: } else { + // GUARD-AND-UNHANDLED-NEXT: print("nay") + // GUARD-AND-UNHANDLED-NEXT: } + // GUARD-AND-UNHANDLED-NEXT: } catch let err { + // GUARD-AND-UNHANDLED-NEXT: fatalError() + // GUARD-AND-UNHANDLED-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-BINDINGS %s + stringTupleParam { strs, err in + guard let x = strs else { return } + guard var (str1, str2) = strs else { return } + guard var y = strs else { return } + guard let z = strs else { return } + y = ("hello", "there") + print(x, y, z, str1, str2) + } + + // Make sure that we + // 1. Coalesce the z binding, as it is a let + // 2. Preserve the y binding, as it is a var + // 3. Print the multi-var binding out of line + // + // MIXED-BINDINGS: let x = try await stringTupleParam() + // MIXED-BINDINGS-NEXT: var (str1, str2) = x + // MIXED-BINDINGS-NEXT: var y = x + // MIXED-BINDINGS-NEXT: y = ("hello", "there") + // MIXED-BINDINGS-NEXT: print(x, y, x, str1, str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-BINDINGS2 %s + stringTupleParam { strs, err in + guard var (str1, str2) = strs else { return } + str1 = "hi" + guard var x = strs else { return } + x = ("hello", "there") + print(x, str1, str2) + } + + // MIXED-BINDINGS2: var x = try await stringTupleParam() + // MIXED-BINDINGS2-NEXT: var (str1, str2) = x + // MIXED-BINDINGS2-NEXT: str1 = "hi" + // MIXED-BINDINGS2-NEXT: x = ("hello", "there") + // MIXED-BINDINGS2-NEXT: print(x, str1, str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-BINDINGS3 %s + stringTupleParam { strs, err in + guard let (str1, str2) = strs else { return } + guard let x = strs else { return } + print(x, str1, str2) + } + + // MIXED-BINDINGS3: let x = try await stringTupleParam() + // MIXED-BINDINGS3-NEXT: let (str1, str2) = x + // MIXED-BINDINGS3-NEXT: print(x, str1, str2) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS %s + stringTupleParam { strs, err in + guard let x = strs else { return } + guard let y = strs else { return } + guard let z = strs else { return } + print(x, y, z) + } + + // ALIAS-BINDINGS: let x = try await stringTupleParam() + // ALIAS-BINDINGS-NEXT: print(x, x, x) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS2 %s + stringTupleParam { strs, err in + guard var x = strs else { return } + guard var y = strs else { return } + guard let z = strs else { return } + print(x, y, z) + } + + // ALIAS-BINDINGS2: let z = try await stringTupleParam() + // ALIAS-BINDINGS2-NEXT: var x = z + // ALIAS-BINDINGS2-NEXT: var y = z + // ALIAS-BINDINGS2-NEXT: print(x, y, z) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS3 %s + stringTupleParam { strs, err in + guard var x = strs else { return } + guard var y = strs else { return } + guard var z = strs else { return } + print(x, y, z) + } + + // ALIAS-BINDINGS3: var x = try await stringTupleParam() + // ALIAS-BINDINGS3-NEXT: var y = x + // ALIAS-BINDINGS3-NEXT: var z = x + // ALIAS-BINDINGS3-NEXT: print(x, y, z) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS4 %s + stringTupleParam { strs, err in + guard var x = strs else { return } + guard let y = strs else { return } + guard let z = strs else { return } + print(x, y, z) + } + + // ALIAS-BINDINGS4: let y = try await stringTupleParam() + // ALIAS-BINDINGS4-NEXT: var x = y + // ALIAS-BINDINGS4-NEXT: print(x, y, y) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ALIAS-BINDINGS5 %s + stringTupleParam { strs, err in + guard var x = strs else { return } + print(x) + } + + // ALIAS-BINDINGS5: var x = try await stringTupleParam() + // ALIAS-BINDINGS5-NEXT: print(x) + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=STRING-TUPLE-RESULT %s + stringTupleResult { res in + switch res { + case .success((let x, let y)): + print(x, y) + case .failure: + print("oh no") + } + } + // STRING-TUPLE-RESULT: do { + // STRING-TUPLE-RESULT-NEXT: let (x, y) = try await stringTupleResult() + // STRING-TUPLE-RESULT-NEXT: print(x, y) + // STRING-TUPLE-RESULT-NEXT: } catch { + // STRING-TUPLE-RESULT-NEXT: print("oh no") + // STRING-TUPLE-RESULT-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=STRING-TUPLE-RESULT %s + stringTupleResult { res in + switch res { + case let .success((x, y)): + print(x, y) + case .failure: + print("oh no") + } + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-TUPLE-RESULT %s + mixedTupleResult { res in + if case .failure(let err) = res { + print("oh no") + } + if case .success(((let x, let y), let z)) = res { + print("a", x, y, z) + } + switch res { + case .success(let ((x, _), z)): + print("b", x, z) + case .failure: + print("oh no again") + } + } + + // MIXED-TUPLE-RESULT: do { + // MIXED-TUPLE-RESULT-NEXT: let res = try await mixedTupleResult() + // MIXED-TUPLE-RESULT-NEXT: let ((x, y), z) = res + // MIXED-TUPLE-RESULT-NEXT: let ((x1, _), z1) = res + // MIXED-TUPLE-RESULT-NEXT: print("a", x, y, z) + // MIXED-TUPLE-RESULT-NEXT: print("b", x1, z1) + // MIXED-TUPLE-RESULT-NEXT: } catch let err { + // MIXED-TUPLE-RESULT-NEXT: print("oh no") + // MIXED-TUPLE-RESULT-NEXT: print("oh no again") + // MIXED-TUPLE-RESULT-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-TUPLE-RESULT2 %s + mixedTupleResult { res in + switch res { + case .success(((let x, let _), let z)): + print(x, z) + case .failure(let err): + print("oh no \(err)") + } + } + + // MIXED-TUPLE-RESULT2: do { + // MIXED-TUPLE-RESULT2-NEXT: let ((x, _), z) = try await mixedTupleResult() + // MIXED-TUPLE-RESULT2-NEXT: print(x, z) + // MIXED-TUPLE-RESULT2-NEXT: } catch let err { + // MIXED-TUPLE-RESULT2-NEXT: print("oh no \(err)") + // MIXED-TUPLE-RESULT2-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-TUPLE-RESULT3 %s + mixedTupleResult { res in + if let ((_, y), z) = try? res.get() { + print(y, z) + } else { + print("boo") + } + } + + // MIXED-TUPLE-RESULT3: do { + // MIXED-TUPLE-RESULT3-NEXT: let ((_, y), z) = try await mixedTupleResult() + // MIXED-TUPLE-RESULT3-NEXT: print(y, z) + // MIXED-TUPLE-RESULT3-NEXT: } catch { + // MIXED-TUPLE-RESULT3-NEXT: print("boo") + // MIXED-TUPLE-RESULT3-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIPLE-TUPLE-PARAM %s + multipleTupleParam { strs, ints, err in + guard let (str1, str2) = strs, let (int1, int2) = ints else { + print("ohno") + return + } + print(str1, str2, int1, int2) + } + // MULTIPLE-TUPLE-PARAM: do { + // MULTIPLE-TUPLE-PARAM-NEXT: let ((str1, str2), (int1, int2)) = try await multipleTupleParam() + // MULTIPLE-TUPLE-PARAM-NEXT: print(str1, str2, int1, int2) + // MULTIPLE-TUPLE-PARAM-NEXT: } catch let err { + // MULTIPLE-TUPLE-PARAM-NEXT: print("ohno") + // MULTIPLE-TUPLE-PARAM-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIPLE-TUPLE-PARAM2 %s + multipleTupleParam { strs, ints, err in + guard let (str1, str2) = strs, var (int1, int2) = ints else { + print("ohno") + return + } + print(str1, str2, int1, int2) + } + // MULTIPLE-TUPLE-PARAM2: do { + // MULTIPLE-TUPLE-PARAM2-NEXT: var ((str1, str2), (int1, int2)) = try await multipleTupleParam() + // MULTIPLE-TUPLE-PARAM2-NEXT: print(str1, str2, int1, int2) + // MULTIPLE-TUPLE-PARAM2-NEXT: } catch let err { + // MULTIPLE-TUPLE-PARAM2-NEXT: print("ohno") + // MULTIPLE-TUPLE-PARAM2-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIPLE-TUPLE-PARAM3 %s + multipleTupleParam { strs, ints, err in + guard let (str1, str2) = strs, let (int1, int2) = ints else { + print("ohno") + return + } + print(strs!) + print(str1, str2, int1, int2) + } + // MULTIPLE-TUPLE-PARAM3: do { + // MULTIPLE-TUPLE-PARAM3-NEXT: let (strs, (int1, int2)) = try await multipleTupleParam() + // MULTIPLE-TUPLE-PARAM3-NEXT: let (str1, str2) = strs + // MULTIPLE-TUPLE-PARAM3-NEXT: print(strs) + // MULTIPLE-TUPLE-PARAM3-NEXT: print(str1, str2, int1, int2) + // MULTIPLE-TUPLE-PARAM3-NEXT: } catch let err { + // MULTIPLE-TUPLE-PARAM3-NEXT: print("ohno") + // MULTIPLE-TUPLE-PARAM3-NEXT: } +} + +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NAME-COLLISION %s +func testNameCollision(_ completion: @escaping () -> Void) { + let a = "" + stringTupleParam { strs, err in + guard let (a, b) = strs else { return } + print(a, b) + } + let b = "" + stringTupleParam { strs, err in + guard let (a, b) = strs else { return } + print(a, b, strs!) + } + print(a, b) + completion() +} + +// TODO: `throws` isn't added to the function declaration +// NAME-COLLISION: func testNameCollision() async { +// NAME-COLLISION-NEXT: let a = "" +// NAME-COLLISION-NEXT: let (a1, b) = try await stringTupleParam() +// NAME-COLLISION-NEXT: print(a1, b) +// NAME-COLLISION-NEXT: let b1 = "" +// NAME-COLLISION-NEXT: let strs = try await stringTupleParam() +// NAME-COLLISION-NEXT: let (a2, b2) = strs +// NAME-COLLISION-NEXT: print(a2, b2, strs) +// NAME-COLLISION-NEXT: print(a, b1) +// NAME-COLLISION-NEXT: return +// NAME-COLLISION-NEXT: } + +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NAME-COLLISION2 %s +func testNameCollision2(_ completion: @escaping () -> Void) { + mixedTupleResult { res in + guard case let .success((x, y), z) = res else { return } + stringTupleParam { strs, err in + if let (x, y) = strs { + print("a", x, y) + } + } + print("b", x, y, z) + } +} + +// TODO: `throws` isn't added to the function declaration +// NAME-COLLISION2: func testNameCollision2() async { +// NAME-COLLISION2-NEXT: let ((x, y), z) = try await mixedTupleResult() +// NAME-COLLISION2-NEXT: let (x1, y1) = try await stringTupleParam() +// NAME-COLLISION2-NEXT: print("a", x1, y1) +// NAME-COLLISION2-NEXT: print("b", x, y, z) +// NAME-COLLISION2-NEXT: } + +// Cannot use refactor-check-compiles, as cannot pattern match with placeholders. +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=TEST-UNHANDLED %s +anyCompletion { val, err in + if let x = val { + print("a") + } + if let _ = val { + print("b") + } + if case let x? = val { + print("c") + } + if case let _? = val { + print("d") + } + if case E.e? = val { + print("e") + } + if case (let x as String)? = val { + print("f") + } + if case let "" as String = val { + print("g") + } +} + +// TEST-UNHANDLED: let x = try await anyCompletion() +// TEST-UNHANDLED-NEXT: print("a") +// TEST-UNHANDLED-NEXT: print("b") +// TEST-UNHANDLED-NEXT: print("c") +// TEST-UNHANDLED-NEXT: print("d") +// TEST-UNHANDLED-NEXT: if case E.e? = <#x#> { +// TEST-UNHANDLED-NEXT: print("e") +// TEST-UNHANDLED-NEXT: } +// TEST-UNHANDLED-NEXT: if case (let x as String)? = <#x#> { +// TEST-UNHANDLED-NEXT: print("f") +// TEST-UNHANDLED-NEXT: } +// TEST-UNHANDLED-NEXT: if case let "" as String = <#x#> { +// TEST-UNHANDLED-NEXT: print("g") +// TEST-UNHANDLED-NEXT: } + +// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 +anyResultCompletion { res in + switch res { + case .success(let unwrapped?): + print(unwrapped) + case .success(let x): + print(x) + case .failure: + print("oh no") + } +} + +// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 +anyResultCompletion { res in + switch res { + case .success(let str as String): + print(str) + case .success(let x): + print(x) + case .failure: + print("oh no") + } +} + +// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 +anyResultCompletion { res in + switch res { + case .success(E.e?): + print("ee") + case .success(let x): + print(x) + case .failure: + print("oh no") + } +} + +// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 +anyResultCompletion { res in + switch res { + case .success("" as String): + print("empty string") + case .success(let x): + print(x) + case .failure: + print("oh no") + } +} + +// FIXME: This should ideally be turned into a 'catch let e as E'. +// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 +anyResultCompletion { res in + switch res { + case .success(let a): + print(a) + case .failure(let e as E): + print("oh no e") + case .failure: + print("oh no") + } +} + +// FIXME: This should ideally be turned into a 'catch E.e'. +// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 +anyResultCompletion { res in + switch res { + case .success(let a): + print(a) + case .failure(E.e): + print("oh no ee") + case .failure: + print("oh no") + } +} + +// Make sure we handle a capture list okay. +class C { + // RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=CAPTURE %s + func foo() { + let _ = { [weak self] in + print(self!) + } + } +} +// CAPTURE: func foo() async { +// CAPTURE-NEXT: let _ = { [weak self] in +// CAPTURE-NEXT: print(self!) +// CAPTURE-NEXT: } +// CAPTURE-NEXT: } diff --git a/test/refactoring/ConvertAsync/convert_result.swift b/test/refactoring/ConvertAsync/convert_result.swift index 1d40008dbd6c9..c63a608b0f7c3 100644 --- a/test/refactoring/ConvertAsync/convert_result.swift +++ b/test/refactoring/ConvertAsync/convert_result.swift @@ -1,414 +1,531 @@ -func simple(_ completion: (Result) -> Void) { } -func simpleWithArg(_ arg: Int, _ completion: (Result) -> Void) { } -func noError(_ completion: (Result) -> Void) { } +// RUN: %empty-directory(%t) + +// REQUIRES: concurrency + +func simple(_ completion: @escaping (Result) -> Void) { } +func simple() async throws -> String { "" } + +func simpleWithArg(_ arg: Int) async throws -> String { "" } +func simpleWithArg(_ arg: Int, _ completion: @escaping (Result) -> Void) { } + +func noError() async -> String { "" } +func noError(_ completion: @escaping (Result) -> Void) { } + +func voidNoError() async {} +func voidNoError(completion: @escaping (Result) -> Void) {} + +func voidError() async throws {} +func voidError(completion: @escaping (Result) -> Void) {} + func test(_ str: String) -> Bool { return false } -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-RESULT %s -func voidResult(completion: (Result) -> Void) {} +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-RESULT %s +func voidResult(completion: @escaping (Result) -> Void) {} // VOID-RESULT: func voidResult() async {} -// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-RESULT %s -func voidAndErrorResult(completion: (Result) -> Void) {} +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-AND-ERROR-RESULT %s +func voidAndErrorResult(completion: @escaping (Result) -> Void) {} // VOID-AND-ERROR-RESULT: func voidAndErrorResult() async throws {} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SIMPLE %s -simple { res in - print("result \(res)") -} -// SIMPLE: let res = try await simple() -// SIMPLE-NEXT: print("result \(<#res#>)") +func testResultConversion() async throws { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SIMPLE %s + simple { res in + print("result \(res)") + } + // SIMPLE: let res = try await simple() + // SIMPLE-NEXT: print("result \(<#res#>)") -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NOERROR %s -noError { res in - print("result \(res)") -} -// NOERROR: let res = await noError() -// NOERROR-NEXT: print("result \(<#res#>)") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCK %s -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - case .failure(let err): - print("error \(err)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOERROR %s + noError { res in + print("result \(res)") } - print("after") -} -// DOBLOCK: do { -// DOBLOCK-NEXT: let str = try await simple() -// DOBLOCK-NEXT: print("before") -// DOBLOCK-NEXT: print("result \(str)") -// DOBLOCK-NEXT: print("after") -// DOBLOCK-NEXT: } catch let err { -// DOBLOCK-NEXT: print("error \(err)") -// DOBLOCK-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCK %s -simple { res in - print("before") - if case .success(let str) = res { - print("result \(str)") - } else if case .failure(let err) = res { - print("error \(err)") + // NOERROR: let res = await noError() + // NOERROR-NEXT: print("result \(<#res#>)") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCK %s + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + case .failure(let err): + print("error \(err)") + } + print("after") + } + // DOBLOCK: do { + // DOBLOCK-NEXT: let str = try await simple() + // DOBLOCK-NEXT: print("before") + // DOBLOCK-NEXT: print("result \(str)") + // DOBLOCK-NEXT: print("after") + // DOBLOCK-NEXT: } catch let err { + // DOBLOCK-NEXT: print("error \(err)") + // DOBLOCK-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCK %s + simple { res in + print("before") + if case .success(let str) = res { + print("result \(str)") + } else if case .failure(let err) = res { + print("error \(err)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCK %s -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - break - case .failure(let err): - print("error \(err)") - break + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCK %s + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + break + case .failure(let err): + print("error \(err)") + break + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCK %s -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - return - case .failure(let err): - print("error \(err)") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCK %s + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + return + case .failure(let err): + print("error \(err)") + return + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SUCCESS %s -simple { res in - print("before") - if case .success(let str) = res { - print("result \(str)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SUCCESS %s + simple { res in + print("before") + if case .success(let str) = res { + print("result \(str)") + } + print("after") } - print("after") -} -// SUCCESS: convert_result.swift -// SUCCESS-NEXT: let str = try await simple() -// SUCCESS-NEXT: print("before") -// SUCCESS-NEXT: print("result \(str)") -// SUCCESS-NEXT: print("after") -// SUCCESS-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SUCCESS %s -simple { res in - print("before") - guard case .success(let str) = res else { - return + // SUCCESS: convert_result.swift + // SUCCESS-NEXT: let str = try await simple() + // SUCCESS-NEXT: print("before") + // SUCCESS-NEXT: print("result \(str)") + // SUCCESS-NEXT: print("after") + // SUCCESS-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SUCCESS %s + simple { res in + print("before") + guard case .success(let str) = res else { + return + } + print("result \(str)") + print("after") } - print("result \(str)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCKUNBOUND %s -simple { res in - print("before") - guard case .success(let str) = res else { - print("err") - return - } - print("result \(str)") - print("after") -} -// DOBLOCKUNBOUND: do { -// DOBLOCKUNBOUND-NEXT: let str = try await simple() -// DOBLOCKUNBOUND-NEXT: print("before") -// DOBLOCKUNBOUND-NEXT: print("result \(str)") -// DOBLOCKUNBOUND-NEXT: print("after") -// DOBLOCKUNBOUND-NEXT: } catch { -// DOBLOCKUNBOUND-NEXT: print("err") -// DOBLOCKUNBOUND-NEXT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SUCCESS %s -simple { res in - print("before") - if let str = try? res.get() { + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCKUNBOUND %s + simple { res in + print("before") + guard case .success(let str) = res else { + print("err") + return + } print("result \(str)") + print("after") + } + // DOBLOCKUNBOUND: do { + // DOBLOCKUNBOUND-NEXT: let str = try await simple() + // DOBLOCKUNBOUND-NEXT: print("before") + // DOBLOCKUNBOUND-NEXT: print("result \(str)") + // DOBLOCKUNBOUND-NEXT: print("after") + // DOBLOCKUNBOUND-NEXT: } catch { + // DOBLOCKUNBOUND-NEXT: print("err") + // DOBLOCKUNBOUND-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SUCCESS %s + simple { res in + print("before") + if let str = try? res.get() { + print("result \(str)") + } + print("after") } - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DOBLOCKUNBOUND %s -simple { res in - print("before") - guard let str = try? res.get() else { - print("err") - return + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=DOBLOCKUNBOUND %s + simple { res in + print("before") + guard let str = try? res.get() else { + print("err") + return + } + print("result \(str)") + print("after") } - print("result \(str)") - print("after") -} -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=UNKNOWN %s -simple { res in - print("before \(res)") - if case .success(let str) = res { - print("result \(str) \(try! res.get())") + // Cannot use refactor-check-compiles, as cannot infer type of the 'get' member on the placeholder. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN %s + simple { res in + print("before \(res)") + if case .success(let str) = res { + print("result \(str) \(try! res.get())") + } + print("after") } - print("after") -} -// UNKNOWN: convert_result.swift -// UNKNOWN-NEXT: let str = try await simple() -// UNKNOWN-NEXT: print("before \(<#str#>)") -// UNKNOWN-NEXT: print("result \(str) \(try! <#str#>.get())") -// UNKNOWN-NEXT: print("after") -// UNKNOWN-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=UNKNOWNUNBOUND %s -simple { res in - print("before \(res)") - if case .success = res { - print("result \(res) \(try! res.get())") + // UNKNOWN: convert_result.swift + // UNKNOWN-NEXT: let str = try await simple() + // UNKNOWN-NEXT: print("before \(<#str#>)") + // UNKNOWN-NEXT: print("result \(str) \(try! <#str#>.get())") + // UNKNOWN-NEXT: print("after") + // UNKNOWN-NOT: } + + // Cannot use refactor-check-compiles, as cannot infer type of the 'get' member on the placeholder. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWNUNBOUND %s + simple { res in + print("before \(res)") + if case .success = res { + print("result \(res) \(try! res.get())") + } + print("after") } - print("after") -} -// UNKNOWNUNBOUND: convert_result.swift -// UNKNOWNUNBOUND-NEXT: let res = try await simple() -// UNKNOWNUNBOUND-NEXT: print("before \(<#res#>)") -// UNKNOWNUNBOUND-NEXT: print("result \(<#res#>) \(try! <#res#>.get())") -// UNKNOWNUNBOUND-NEXT: print("after") -// UNKNOWN-NOT: } - -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - if case .success(let str) = res { - print("result \(str)") + // UNKNOWNUNBOUND: convert_result.swift + // UNKNOWNUNBOUND-NEXT: let res = try await simple() + // UNKNOWNUNBOUND-NEXT: print("before \(<#res#>)") + // UNKNOWNUNBOUND-NEXT: print("result \(<#res#>) \(try! <#res#>.get())") + // UNKNOWNUNBOUND-NEXT: print("after") + // UNKNOWN-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIPLE-BINDS %s + simple { res in + print("before") + if case .success(let str) = res { + print("result \(str)") + } + if case .success(let str2) = res { + print("result \(str2)") + } + print("after") } - if case .success(let str2) = res { - print("result \(str2)") + // MULTIPLE-BINDS: convert_result.swift + // MULTIPLE-BINDS-NEXT: let str = try await simple() + // MULTIPLE-BINDS-NEXT: print("before") + // MULTIPLE-BINDS-NEXT: print("result \(str)") + // MULTIPLE-BINDS-NEXT: print("result \(str)") + // MULTIPLE-BINDS-NEXT: print("after") + // MULTIPLE-BINDS-NOT: } + + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + case .failure(let err): + print("error \(err)") + default: + print("default") + } + print("after") } - print("after") -} -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - case .failure(let err): - print("error \(err)") - default: - print("default") + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success(let str): + print("result \(str)") + default: + print("err") + } + print("after") } - print("after") -} -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success(let str): - print("result \(str)") - default: - print("err") + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success, .failure: + print("either") + } + print("after") } - print("after") -} -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success, .failure: - print("either") + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success, .failure: + print("either") + } + print("after") } - print("after") -} -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success, .failure: - print("either") + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success: + fallthrough + case .failure: + print("either") + } + print("after") } - print("after") -} -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success: - fallthrough - case .failure: - print("either") + // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 + simple { res in + print("before") + switch res { + case .success(let str) where str.hasPrefix("match"): + print("pattern matched result \(str)") + case .success(let str): + print("result \(str)") + case .failure(let err): + print("error \(err)") + } + print("after") } - print("after") -} -// RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -simple { res in - print("before") - switch res { - case .success(let str) where str.hasPrefix("match"): - print("pattern matched result \(str)") - case .success(let str): - print("result \(str)") - case .failure(let err): - print("error \(err)") + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDRET %s + simple { res in + print("before") + switch res { + case .success(let str): + if test(str) { + return + } + print("result \(str)") + case .failure: + break + } + print("after") } - print("after") -} - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NESTEDRET %s -simple { res in - print("before") - switch res { - case .success(let str): - if test(str) { - return + // NESTEDRET: convert_result.swift + // NESTEDRET-NEXT: let str = try await simple() + // NESTEDRET-NEXT: print("before") + // NESTEDRET-NEXT: if test(str) { + // NESTEDRET-NEXT: <#return#> + // NESTEDRET-NEXT: } + // NESTEDRET-NEXT: print("result \(str)") + // NESTEDRET-NEXT: print("after") + // NESTEDRET-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDBREAK %s + simple { res in + print("before") + switch res { + case .success(let str): + if test(str) { + break + } + print("result \(str)") + case .failure: + break } - print("result \(str)") - case .failure: - break + print("after") } - print("after") -} -// NESTEDRET: convert_result.swift -// NESTEDRET-NEXT: let str = try await simple() -// NESTEDRET-NEXT: print("before") -// NESTEDRET-NEXT: if test(str) { -// NESTEDRET-NEXT: <#return#> -// NESTEDRET-NEXT: } -// NESTEDRET-NEXT: print("result \(str)") -// NESTEDRET-NEXT: print("after") -// NESTEDRET-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NESTEDBREAK %s -simple { res in - print("before") - switch res { - case .success(let str): - if test(str) { + // NESTEDBREAK: convert_result.swift + // NESTEDBREAK-NEXT: let str = try await simple() + // NESTEDBREAK-NEXT: print("before") + // NESTEDBREAK-NEXT: if test(str) { + // NESTEDBREAK-NEXT: <#break#> + // NESTEDBREAK-NEXT: } + // NESTEDBREAK-NEXT: print("result \(str)") + // NESTEDBREAK-NEXT: print("after") + // NESTEDBREAK-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDBREAK-COMMENT %s + simple { res in // a + // b + print("before") + // c + switch res { + // d + case .success(let str): // e + if test(str) { + // f + break + // g + } + // h + print("result \(str)") + // i + case .failure: + // j break + // k } - print("result \(str)") - case .failure: - break + // l + print("after") + // m } - print("after") -} -// NESTEDBREAK: convert_result.swift -// NESTEDBREAK-NEXT: let str = try await simple() -// NESTEDBREAK-NEXT: print("before") -// NESTEDBREAK-NEXT: if test(str) { -// NESTEDBREAK-NEXT: <#break#> -// NESTEDBREAK-NEXT: } -// NESTEDBREAK-NEXT: print("result \(str)") -// NESTEDBREAK-NEXT: print("after") -// NESTEDBREAK-NOT: } - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-RESULT-CALL %s -voidResult { res in - print(res) -} -// VOID-RESULT-CALL: {{^}}await voidResult() -// VOID-RESULT-CALL: {{^}}print(<#res#>) - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-AND-ERROR-RESULT-CALL %s -voidAndErrorResult { res in - print(res) -} -// VOID-AND-ERROR-RESULT-CALL: {{^}}try await voidAndErrorResult() -// VOID-AND-ERROR-RESULT-CALL: {{^}}print(<#res#>) - -// Make sure we ignore an unrelated switch. -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=IGNORE-UNRELATED %s -simple { res in - print("before") - switch Bool.random() { - case true: - break - case false: - break + // NESTEDBREAK-COMMENT: let str = try await simple() + // NESTEDBREAK-COMMENT-NEXT: // a + // NESTEDBREAK-COMMENT-NEXT: // b + // NESTEDBREAK-COMMENT-NEXT: print("before") + // NESTEDBREAK-COMMENT-NEXT: // c + // NESTEDBREAK-COMMENT-NEXT: // d + // NESTEDBREAK-COMMENT-NEXT: // e + // NESTEDBREAK-COMMENT-NEXT: if test(str) { + // NESTEDBREAK-COMMENT-NEXT: // f + // NESTEDBREAK-COMMENT-NEXT: <#break#> + // NESTEDBREAK-COMMENT-NEXT: // g + // NESTEDBREAK-COMMENT-NEXT: } + // NESTEDBREAK-COMMENT-NEXT: // h + // NESTEDBREAK-COMMENT-NEXT: print("result \(str)") + // NESTEDBREAK-COMMENT-NEXT: // i + // NESTEDBREAK-COMMENT-NEXT: // j + // NESTEDBREAK-COMMENT-NEXT: // k + // NESTEDBREAK-COMMENT-NEXT: // l + // NESTEDBREAK-COMMENT-NEXT: print("after") + // NESTEDBREAK-COMMENT-NEXT: // m + // NESTEDBREAK-COMMENT-NEXT: {{ }} + // NESTEDBREAK-COMMENT-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ERROR-BLOCK-COMMENT %s + simple { res in + // a + print("before") + // b + switch res { + case .success(let str): + // c + print("result \(str)") + // d + case .failure: + // e + print("fail") + // f + return + // g + } + // h + print("after") + // i } - print("after") -} -// IGNORE-UNRELATED: let res = try await simple() -// IGNORE-UNRELATED-NEXT: print("before") -// IGNORE-UNRELATED-NEXT: switch Bool.random() { -// IGNORE-UNRELATED-NEXT: case true: -// IGNORE-UNRELATED-NEXT: {{^}} break{{$}} -// IGNORE-UNRELATED-NEXT: case false: -// IGNORE-UNRELATED-NEXT: {{^}} break{{$}} -// IGNORE-UNRELATED-NEXT: } -// IGNORE-UNRELATED-NEXT: print("after") - -// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=BREAK-RET-PLACEHOLDER %s -simpleWithArg({ return 0 }()) { res in - switch res { - case .success: - if .random() { break } - x: if .random() { break x } - case .failure: - break + // ERROR-BLOCK-COMMENT: do { + // ERROR-BLOCK-COMMENT-NEXT: let str = try await simple() + // ERROR-BLOCK-COMMENT-NEXT: // a + // ERROR-BLOCK-COMMENT-NEXT: print("before") + // ERROR-BLOCK-COMMENT-NEXT: // b + // ERROR-BLOCK-COMMENT-NEXT: // c + // ERROR-BLOCK-COMMENT-NEXT: print("result \(str)") + // ERROR-BLOCK-COMMENT-NEXT: // d + // ERROR-BLOCK-COMMENT-NEXT: // h + // ERROR-BLOCK-COMMENT-NEXT: print("after") + // ERROR-BLOCK-COMMENT-NEXT: // i + // ERROR-BLOCK-COMMENT-NEXT: {{ }} + // ERROR-BLOCK-COMMENT-NEXT: } catch { + // ERROR-BLOCK-COMMENT-NEXT: // e + // ERROR-BLOCK-COMMENT-NEXT: print("fail") + // ERROR-BLOCK-COMMENT-NEXT: // f + // ERROR-BLOCK-COMMENT-NEXT: // g + // ERROR-BLOCK-COMMENT-NEXT: {{ }} + // ERROR-BLOCK-COMMENT-NEXT: } + // ERROR-BLOCK-COMMENT-NOT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=VOID-RESULT-CALL %s + voidNoError { res in + print(res) } + // VOID-RESULT-CALL: {{^}}await voidNoError() + // VOID-RESULT-CALL: {{^}}print(<#res#>) - func foo(_ x: T) { - if .random() { return } + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=VOID-AND-ERROR-RESULT-CALL %s + voidError { res in + print(res) } - foo(res) - - let fn = { - if .random() { return } - return + // VOID-AND-ERROR-RESULT-CALL: {{^}}try await voidError() + // VOID-AND-ERROR-RESULT-CALL: {{^}}print(<#res#>) + + // Make sure we ignore an unrelated switch. + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=IGNORE-UNRELATED %s + simple { res in + print("before") + switch Bool.random() { + case true: + break + case false: + break + } + print("after") } - fn() + // IGNORE-UNRELATED: let res = try await simple() + // IGNORE-UNRELATED-NEXT: print("before") + // IGNORE-UNRELATED-NEXT: switch Bool.random() { + // IGNORE-UNRELATED-NEXT: case true: + // IGNORE-UNRELATED-NEXT: {{^}} break{{$}} + // IGNORE-UNRELATED-NEXT: case false: + // IGNORE-UNRELATED-NEXT: {{^}} break{{$}} + // IGNORE-UNRELATED-NEXT: } + // IGNORE-UNRELATED-NEXT: print("after") + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BREAK-RET-PLACEHOLDER %s + simpleWithArg({ return 0 }()) { res in + switch res { + case .success: + if .random() { break } + x: if .random() { break x } + case .failure: + break + } - _ = { return }() + func foo(_ x: T) { + if .random() { return } + } + foo(res) - switch Bool.random() { - case true: - break - case false: - if .random() { break } - y: if .random() { break y } - return - } + let fn = { + if .random() { return } + return + } + fn() - x: if .random() { - break x + _ = { return }() + + switch Bool.random() { + case true: + break + case false: + if .random() { break } + y: if .random() { break y } + return + } + + x: if .random() { + break x + } + if .random() { return } } - if .random() { return } -} -// Make sure we replace lifted break/returns with placeholders, but keep nested -// break/returns in e.g closures or labelled control flow in place. - -// BREAK-RET-PLACEHOLDER: let res = try await simpleWithArg({ return 0 }()) -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { <#break#> } -// BREAK-RET-PLACEHOLDER-NEXT: x: if .random() { break x } -// BREAK-RET-PLACEHOLDER-NEXT: func foo(_ x: T) { -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { return } -// BREAK-RET-PLACEHOLDER-NEXT: } -// BREAK-RET-PLACEHOLDER-NEXT: foo(<#res#>) -// BREAK-RET-PLACEHOLDER-NEXT: let fn = { -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { return } -// BREAK-RET-PLACEHOLDER-NEXT: {{^}} return{{$}} -// BREAK-RET-PLACEHOLDER-NEXT: } -// BREAK-RET-PLACEHOLDER-NEXT: fn() -// BREAK-RET-PLACEHOLDER-NEXT: _ = { return }() -// BREAK-RET-PLACEHOLDER-NEXT: switch Bool.random() { -// BREAK-RET-PLACEHOLDER-NEXT: case true: -// BREAK-RET-PLACEHOLDER-NEXT: {{^}} break{{$}} -// BREAK-RET-PLACEHOLDER-NEXT: case false: -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { break } -// BREAK-RET-PLACEHOLDER-NEXT: y: if .random() { break y } -// BREAK-RET-PLACEHOLDER-NEXT: <#return#> -// BREAK-RET-PLACEHOLDER-NEXT: } -// BREAK-RET-PLACEHOLDER-NEXT: x: if .random() { -// BREAK-RET-PLACEHOLDER-NEXT: {{^}} break x{{$}} -// BREAK-RET-PLACEHOLDER-NEXT: } -// BREAK-RET-PLACEHOLDER-NEXT: if .random() { <#return#> } + // Make sure we replace lifted break/returns with placeholders, but keep nested + // break/returns in e.g closures or labelled control flow in place. + + // BREAK-RET-PLACEHOLDER: let res = try await simpleWithArg({ return 0 }()) + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { <#break#> } + // BREAK-RET-PLACEHOLDER-NEXT: x: if .random() { break x } + // BREAK-RET-PLACEHOLDER-NEXT: func foo(_ x: T) { + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { return } + // BREAK-RET-PLACEHOLDER-NEXT: } + // BREAK-RET-PLACEHOLDER-NEXT: foo(<#res#>) + // BREAK-RET-PLACEHOLDER-NEXT: let fn = { + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { return } + // BREAK-RET-PLACEHOLDER-NEXT: {{^}} return{{$}} + // BREAK-RET-PLACEHOLDER-NEXT: } + // BREAK-RET-PLACEHOLDER-NEXT: fn() + // BREAK-RET-PLACEHOLDER-NEXT: _ = { return }() + // BREAK-RET-PLACEHOLDER-NEXT: switch Bool.random() { + // BREAK-RET-PLACEHOLDER-NEXT: case true: + // BREAK-RET-PLACEHOLDER-NEXT: {{^}} break{{$}} + // BREAK-RET-PLACEHOLDER-NEXT: case false: + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { break } + // BREAK-RET-PLACEHOLDER-NEXT: y: if .random() { break y } + // BREAK-RET-PLACEHOLDER-NEXT: <#return#> + // BREAK-RET-PLACEHOLDER-NEXT: } + // BREAK-RET-PLACEHOLDER-NEXT: x: if .random() { + // BREAK-RET-PLACEHOLDER-NEXT: {{^}} break x{{$}} + // BREAK-RET-PLACEHOLDER-NEXT: } + // BREAK-RET-PLACEHOLDER-NEXT: if .random() { <#return#> } +} diff --git a/test/refactoring/ConvertAsync/convert_to_continuation.swift b/test/refactoring/ConvertAsync/convert_to_continuation.swift new file mode 100644 index 0000000000000..5bc3993cc2f26 --- /dev/null +++ b/test/refactoring/ConvertAsync/convert_to_continuation.swift @@ -0,0 +1,709 @@ +// RUN: %empty-directory(%t) + +func withAsyncAlternative(completionHandler: @escaping (Int) -> Void) {} +func withAsyncAlternative() async -> Int { return 42 } +func withAsyncThrowingAlternative(completionHandler: @escaping (Int?, Error?) -> Void) {} +func withAsyncThrowingAlternative() async throws -> Int { return 42 } + +func withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName(closure: @escaping (Int) -> Void) {} +func withoutAsyncAlternativeBecauseOfReturnValue(completionHandler: @escaping (Int) -> Void) -> Bool { return true } +func withoutAsyncAlternativeThrowing(closure: @escaping (Int?, Error?) -> Void) {} +func withoutAsyncAlternativeThrowingWithMultipleResults(closure: @escaping (Int?, String?, Error?) -> Void) {} +func asyncVoidWithoutAlternative(completionHandler2: @escaping () -> Void) {} +func resultWithoutAlternative(completionHandler2: @escaping (Result) -> Void) {} + +func lottaClosures(x: () -> Void, y: () -> Void) -> Int? { nil } + +struct MyError: Error {} + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CREATE-CONTINUATION %s +func testCreateContinuation(completionHandler: @escaping (Int) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { + completionHandler($0) + } +} +// CREATE-CONTINUATION: func testCreateContinuation() async -> Int { +// CREATE-CONTINUATION-NEXT: return await withCheckedContinuation { continuation in +// CREATE-CONTINUATION-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { +// CREATE-CONTINUATION-NEXT: continuation.resume(returning: $0) +// CREATE-CONTINUATION-NEXT: } +// CREATE-CONTINUATION-NEXT: } +// CREATE-CONTINUATION-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CREATE-CONTINUATION-HANDLER-CALL-IN-PARENS %s +func testCreateContinuationWithCompletionHandlerCallInParens(completionHandler: @escaping (Int) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { + (completionHandler($0)) + } +} +// CREATE-CONTINUATION-HANDLER-CALL-IN-PARENS: func testCreateContinuationWithCompletionHandlerCallInParens() async -> Int { +// CREATE-CONTINUATION-HANDLER-CALL-IN-PARENS-NEXT: return await withCheckedContinuation { continuation in +// CREATE-CONTINUATION-HANDLER-CALL-IN-PARENS-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { +// CREATE-CONTINUATION-HANDLER-CALL-IN-PARENS-NEXT: continuation.resume(returning: $0) +// CREATE-CONTINUATION-HANDLER-CALL-IN-PARENS-NEXT: } +// CREATE-CONTINUATION-HANDLER-CALL-IN-PARENS-NEXT: } +// CREATE-CONTINUATION-HANDLER-CALL-IN-PARENS-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CREATE-CONTINUATION-BECAUSE-RETURN-VALUE %s +func testCreateContinuationBecauseOfReturnValue(completionHandler: @escaping (Int) -> Void) { + _ = withoutAsyncAlternativeBecauseOfReturnValue { + completionHandler($0) + } +} +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE: func testCreateContinuationBecauseOfReturnValue() async -> Int { +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-NEXT: return await withCheckedContinuation { continuation in +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-NEXT: _ = withoutAsyncAlternativeBecauseOfReturnValue { +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-NEXT: continuation.resume(returning: $0) +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-NEXT: } +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-NEXT: } +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2 %s +func testCreateContinuationBecauseOfReturnValue2(completionHandler: @escaping (Int) -> Void) { + let x = withoutAsyncAlternativeBecauseOfReturnValue { + completionHandler($0) + } + print(x) +} +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2: func testCreateContinuationBecauseOfReturnValue2() async -> Int { +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2-NEXT: return await withCheckedContinuation { continuation in +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2-NEXT: let x = withoutAsyncAlternativeBecauseOfReturnValue { +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2-NEXT: continuation.resume(returning: $0) +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2-NEXT: } +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2-NEXT: print(x) +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2-NEXT: } +// CREATE-CONTINUATION-BECAUSE-RETURN-VALUE-2-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=CONTINUATION-IN-NESTED-EXPRESSION %s +func testCompletionHandlerCallInNestedExpression(completionHandler: @escaping (Int) -> Void) { + print(withoutAsyncAlternativeBecauseOfReturnValue { + completionHandler($0) + }) +} +// CONTINUATION-IN-NESTED-EXPRESSION: func testCompletionHandlerCallInNestedExpression() async -> Int { +// CONTINUATION-IN-NESTED-EXPRESSION-NEXT: return await withCheckedContinuation { continuation in +// CONTINUATION-IN-NESTED-EXPRESSION-NEXT: print(withoutAsyncAlternativeBecauseOfReturnValue { +// CONTINUATION-IN-NESTED-EXPRESSION-NEXT: continuation.resume(returning: $0) +// CONTINUATION-IN-NESTED-EXPRESSION-NEXT: }) +// CONTINUATION-IN-NESTED-EXPRESSION-NEXT: } +// CONTINUATION-IN-NESTED-EXPRESSION-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION %s +func testThrowingContinuation(completionHandler: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theValue, theError) in + if let theError = theError { + completionHandler(nil, theError) + } else { + completionHandler(theValue!, nil) + } + } +} +// THROWING-CONTINUATION: func testThrowingContinuation() async throws -> Int { +// THROWING-CONTINUATION-NEXT: return try await withCheckedThrowingContinuation { continuation in +// THROWING-CONTINUATION-NEXT: withoutAsyncAlternativeThrowing { (theValue, theError) in +// THROWING-CONTINUATION-NEXT: if let theError = theError { +// THROWING-CONTINUATION-NEXT: continuation.resume(throwing: theError) +// THROWING-CONTINUATION-NEXT: } else { +// THROWING-CONTINUATION-NEXT: continuation.resume(returning: theValue!) +// THROWING-CONTINUATION-NEXT: } +// THROWING-CONTINUATION-NEXT: } +// THROWING-CONTINUATION-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT %s +func testThrowingContinuationRelayingErrorAndResult(completionHandler: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theValue, theError) in + completionHandler(theValue, theError) + } +} +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT: func testThrowingContinuationRelayingErrorAndResult() async throws -> Int { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: return try await withCheckedThrowingContinuation { continuation in +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: withoutAsyncAlternativeThrowing { (theValue, theError) in +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: if let error = theError { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: continuation.resume(throwing: error) +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: } else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: guard let theValue1 = theValue else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: fatalError("Expected non-nil result 'theValue1' in the non-error case") +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: continuation.resume(returning: theValue1) +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT %s +func testThrowingContinuationRelayingErrorAndComplexResult(completionHandler: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theValue, theError) in + completionHandler(theValue.map({ $0 + 1 }), theError) + } +} +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT: func testThrowingContinuationRelayingErrorAndComplexResult() async throws -> Int { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: return try await withCheckedThrowingContinuation { continuation in +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: withoutAsyncAlternativeThrowing { (theValue, theError) in +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: if let error = theError { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: continuation.resume(throwing: error) +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: } else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: guard let result = theValue.map({ $0 + 1 }) else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: fatalError("Expected non-nil result in the non-error case") +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: continuation.resume(returning: result) +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS %s +func testThrowingContinuationRelayingErrorAndTwoComplexResults(completionHandler: @escaping (Int?, Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theValue, theError) in + completionHandler(theValue.map({ $0 + 1 }), theValue.map({ $0 + 2 }), theError) + } +} +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS: func testThrowingContinuationRelayingErrorAndTwoComplexResults() async throws -> (Int, Int) { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: return try await withCheckedThrowingContinuation { continuation in +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: withoutAsyncAlternativeThrowing { (theValue, theError) in +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: if let error = theError { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: continuation.resume(throwing: error) +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: } else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: guard let result0 = theValue.map({ $0 + 1 }) else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: fatalError("Expected non-nil result 'result0' in the non-error case") +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: guard let result1 = theValue.map({ $0 + 2 }) else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: fatalError("Expected non-nil result 'result1' in the non-error case") +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: continuation.resume(returning: (result0, result1)) +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-TWO-COMPLEX-RESULTS-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE %s +func testThrowingContinuationRelayingErrorAndComplexResultWithTrailingClosure(completionHandler: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theValue, theError) in + completionHandler(theValue.map { $0 + 1 }, theError) + } +} +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE: func testThrowingContinuationRelayingErrorAndComplexResultWithTrailingClosure() async throws -> Int { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: return try await withCheckedThrowingContinuation { continuation in +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: withoutAsyncAlternativeThrowing { (theValue, theError) in +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: if let error = theError { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: continuation.resume(throwing: error) +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: } else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: guard let result = (theValue.map { $0 + 1 }) else { +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: fatalError("Expected non-nil result in the non-error case") +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: continuation.resume(returning: result) +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: } +// THROWING-CONTINUATION-RELAYING-ERROR-AND-COMPLEX-RESULT-WITH-TRAILING-CLOSURE-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-TRAILING-CLOSURES %s +func testThrowingContinuationRelayingErrorAndComplexResultWithMultipleTrailingClosures(completionHandler: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { theValue, theError in + completionHandler(lottaClosures {} y: {}, theError) + } +} +// MULTIPLE-TRAILING-CLOSURES: func testThrowingContinuationRelayingErrorAndComplexResultWithMultipleTrailingClosures() async throws -> Int { +// MULTIPLE-TRAILING-CLOSURES-NEXT: return try await withCheckedThrowingContinuation { continuation in +// MULTIPLE-TRAILING-CLOSURES-NEXT: withoutAsyncAlternativeThrowing { theValue, theError in +// MULTIPLE-TRAILING-CLOSURES-NEXT: if let error = theError { +// MULTIPLE-TRAILING-CLOSURES-NEXT: continuation.resume(throwing: error) +// MULTIPLE-TRAILING-CLOSURES-NEXT: } else { +// MULTIPLE-TRAILING-CLOSURES-NEXT: guard let result = (lottaClosures {} y: {}) else { +// MULTIPLE-TRAILING-CLOSURES-NEXT: fatalError("Expected non-nil result in the non-error case") +// MULTIPLE-TRAILING-CLOSURES-NEXT: } +// MULTIPLE-TRAILING-CLOSURES-NEXT: continuation.resume(returning: result) +// MULTIPLE-TRAILING-CLOSURES-NEXT: } +// MULTIPLE-TRAILING-CLOSURES-NEXT: } +// MULTIPLE-TRAILING-CLOSURES-NEXT: } +// MULTIPLE-TRAILING-CLOSURES-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT %s +func testAlwaysReturnBothResultAndCompletionHandler(completionHandler: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { theValue in + completionHandler(theValue, MyError()) + } +} +// THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT: func testAlwaysReturnBothResultAndCompletionHandler() async throws -> Int { +// THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT-NEXT: return try await withCheckedThrowingContinuation { continuation in +// THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { theValue in +// THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT-NEXT: continuation.resume(throwing: MyError()) +// THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT-NEXT: } +// THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT-NEXT: } +// THROWING-CONTINUATION-ALWAYS-RETURNING-ERROR-AND-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE %s +func testAmbiguousCallToCompletionHandlerWithAlwaysNilVariable(completionHandler: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { theValue in + let error: Error? = nil + completionHandler(theValue, error) + } +} +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE: func testAmbiguousCallToCompletionHandlerWithAlwaysNilVariable() async throws -> Int { +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: return try await withCheckedThrowingContinuation { continuation in +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { theValue in +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: let error: Error? = nil +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: if let error = error { +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: continuation.resume(throwing: error) +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: } else { +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: continuation.resume(returning: theValue) +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: } +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: } +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: } +// AMBIGUOUS-CALL-WITH-ALWAYS-NIL-VARIABLE-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=PREVIOUS-COMPLETION-HANDLER-CALL %s +func testPreviousCompletionHandlerCall(completionHandler: @escaping (Int) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { + print($0) + } + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { + completionHandler($0) + } +} +// PREVIOUS-COMPLETION-HANDLER-CALL: func testPreviousCompletionHandlerCall() async -> Int { +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: print($0) +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: } +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: return await withCheckedContinuation { continuation in +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: continuation.resume(returning: $0) +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: } +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: } +// PREVIOUS-COMPLETION-HANDLER-CALL-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=PREVIOUS-ASYNC-CALL %s +func testPreviousAsyncCall(completionHandler: @escaping (Int) -> Void) { + withAsyncAlternative { message in + print(message) + } + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { + completionHandler($0) + } +} +// PREVIOUS-ASYNC-CALL: func testPreviousAsyncCall() async -> Int { +// PREVIOUS-ASYNC-CALL-NEXT: let message = await withAsyncAlternative() +// PREVIOUS-ASYNC-CALL-NEXT: print(message) +// PREVIOUS-ASYNC-CALL-NEXT: return await withCheckedContinuation { continuation in +// PREVIOUS-ASYNC-CALL-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { +// PREVIOUS-ASYNC-CALL-NEXT: continuation.resume(returning: $0) +// PREVIOUS-ASYNC-CALL-NEXT: } +// PREVIOUS-ASYNC-CALL-NEXT: } +// PREVIOUS-ASYNC-CALL-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=IN-IF-ELSE %s +func testInIfElse(completionHandler: @escaping (Int) -> Void) { + if true { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { + completionHandler($0) + } + } else { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { + completionHandler($0) + } + } +} +// IN-IF-ELSE: func testInIfElse() async -> Int { +// IN-IF-ELSE-NEXT: if true { +// IN-IF-ELSE-NEXT: return await withCheckedContinuation { continuation in +// IN-IF-ELSE-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { +// IN-IF-ELSE-NEXT: continuation.resume(returning: $0) +// IN-IF-ELSE-NEXT: } +// IN-IF-ELSE-NEXT: } +// IN-IF-ELSE-NEXT: } else { +// IN-IF-ELSE-NEXT: return await withCheckedContinuation { continuation in +// IN-IF-ELSE-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { +// IN-IF-ELSE-NEXT: continuation.resume(returning: $0) +// IN-IF-ELSE-NEXT: } +// IN-IF-ELSE-NEXT: } +// IN-IF-ELSE-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-AFTER-CONTINUATION %s +func testAsyncAfterContinuation(completionHandler: @escaping (Int) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { + completionHandler($0) + } + withAsyncAlternative { + print($0) + } +} +// ASYNC-AFTER-CONTINUATION: func testAsyncAfterContinuation() async -> Int { +// ASYNC-AFTER-CONTINUATION-NEXT: return await withCheckedContinuation { continuation in +// ASYNC-AFTER-CONTINUATION-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { +// ASYNC-AFTER-CONTINUATION-NEXT: continuation.resume(returning: $0) +// ASYNC-AFTER-CONTINUATION-NEXT: } +// ASYNC-AFTER-CONTINUATION-NEXT: withAsyncAlternative { +// ASYNC-AFTER-CONTINUATION-NEXT: print($0) +// ASYNC-AFTER-CONTINUATION-NEXT: } +// ASYNC-AFTER-CONTINUATION-NEXT: } +// ASYNC-AFTER-CONTINUATION-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC %s +func testWithoutAsyncAlternativeNestedInWithoutAsyncAlternative(completionHandler: @escaping (Int) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { firstResult in + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { secondResult in + completionHandler(firstResult + secondResult) + } + } +} +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC: func testWithoutAsyncAlternativeNestedInWithoutAsyncAlternative() async -> Int { +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: return await withCheckedContinuation { continuation in +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { firstResult in +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { secondResult in +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: continuation.resume(returning: firstResult + secondResult) +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: } +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: } +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: } +// WITHOUT-ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: } + + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=WITHOUT-ASYNC-NESTED-IN-ASYNC %s +func testWithoutAsyncAlternativeNestedInAsyncAlternative(completionHandler: @escaping (Int) -> Void) { + withAsyncAlternative { firstResult in + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { secondResult in + completionHandler(firstResult + secondResult) + } + } +} +// WITHOUT-ASYNC-NESTED-IN-ASYNC: func testWithoutAsyncAlternativeNestedInAsyncAlternative() async -> Int { +// WITHOUT-ASYNC-NESTED-IN-ASYNC-NEXT: let firstResult = await withAsyncAlternative() +// WITHOUT-ASYNC-NESTED-IN-ASYNC-NEXT: return await withCheckedContinuation { continuation in +// WITHOUT-ASYNC-NESTED-IN-ASYNC-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { secondResult in +// WITHOUT-ASYNC-NESTED-IN-ASYNC-NEXT: continuation.resume(returning: firstResult + secondResult) +// WITHOUT-ASYNC-NESTED-IN-ASYNC-NEXT: } +// WITHOUT-ASYNC-NESTED-IN-ASYNC-NEXT: } +// WITHOUT-ASYNC-NESTED-IN-ASYNC-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ASYNC-NESTED-IN-WITHOUT-ASYNC %s +func testAsyncAlternativeNestedInWithoutAsyncAlternative(completionHandler: @escaping (Int) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { firstResult in + withAsyncAlternative { secondResult in + completionHandler(firstResult + secondResult) + } + } +} +// ASYNC-NESTED-IN-WITHOUT-ASYNC: func testAsyncAlternativeNestedInWithoutAsyncAlternative() async -> Int { +// ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: return await withCheckedContinuation { continuation in +// ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { firstResult in +// ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: withAsyncAlternative { secondResult in +// ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: continuation.resume(returning: firstResult + secondResult) +// ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: } +// ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: } +// ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: } +// ASYNC-NESTED-IN-WITHOUT-ASYNC-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SHADOW-CONT-NAME %s +func testShadowContName(completionHandler: @escaping (Int) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { continuation in + completionHandler(continuation) + } +} +// SHADOW-CONT-NAME: func testShadowContName() async -> Int { +// SHADOW-CONT-NAME-NEXT: return await withCheckedContinuation { continuation in +// SHADOW-CONT-NAME-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { continuation1 in +// SHADOW-CONT-NAME-NEXT: continuation.resume(returning: continuation1) +// SHADOW-CONT-NAME-NEXT: } +// SHADOW-CONT-NAME-NEXT: } +// SHADOW-CONT-NAME-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SHADOW-CONT-NAME-2 %s +func testShadowContName2(completionHandler: @escaping (Int) -> Void) { + let continuation = 3 + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { result in + completionHandler(result + continuation) + } +} +// SHADOW-CONT-NAME-2: func testShadowContName2() async -> Int { +// SHADOW-CONT-NAME-2-NEXT: let continuation = 3 +// SHADOW-CONT-NAME-2-NEXT: return await withCheckedContinuation { continuation1 in +// SHADOW-CONT-NAME-2-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { result in +// SHADOW-CONT-NAME-2-NEXT: continuation1.resume(returning: result + continuation) +// SHADOW-CONT-NAME-2-NEXT: } +// SHADOW-CONT-NAME-2-NEXT: } +// SHADOW-CONT-NAME-2-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=VOID-RETURN %s +func testVoidReturnValue(completionHandler: @escaping () -> Void) { + asyncVoidWithoutAlternative { + completionHandler() + } +} +// VOID-RETURN: func testVoidReturnValue() async { +// VOID-RETURN-NEXT: return await withCheckedContinuation { continuation in +// VOID-RETURN-NEXT: asyncVoidWithoutAlternative { +// VOID-RETURN-NEXT: continuation.resume(returning: ()) +// VOID-RETURN-NEXT: } +// VOID-RETURN-NEXT: } +// VOID-RETURN-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SIMPLE-RESULT %s +func testSimpleResult(completionHandler: @escaping (Result) -> Void) { + resultWithoutAlternative { result in + completionHandler(result) + } +} +// SIMPLE-RESULT: func testSimpleResult() async throws -> Int { +// SIMPLE-RESULT-NEXT: return try await withCheckedThrowingContinuation { continuation in +// SIMPLE-RESULT-NEXT: resultWithoutAlternative { result in +// SIMPLE-RESULT-NEXT: continuation.resume(with: result) +// SIMPLE-RESULT-NEXT: } +// SIMPLE-RESULT-NEXT: } +// SIMPLE-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RESULT-FROM-VALUE-AND-ERROR %s +func testResultFromValueAndError(completionHandler: @escaping (Result) -> Void) { + withoutAsyncAlternativeThrowing { (value, error) in + if let error = error { + completionHandler(.failure(error)) + } else { + completionHandler(.success(value!)) + } + } +} +// RESULT-FROM-VALUE-AND-ERROR: func testResultFromValueAndError() async throws -> Int { +// RESULT-FROM-VALUE-AND-ERROR-NEXT: return try await withCheckedThrowingContinuation { continuation in +// RESULT-FROM-VALUE-AND-ERROR-NEXT: withoutAsyncAlternativeThrowing { (value, error) in +// RESULT-FROM-VALUE-AND-ERROR-NEXT: if let error = error { +// RESULT-FROM-VALUE-AND-ERROR-NEXT: continuation.resume(with: .failure(error)) +// RESULT-FROM-VALUE-AND-ERROR-NEXT: } else { +// RESULT-FROM-VALUE-AND-ERROR-NEXT: continuation.resume(with: .success(value!)) +// RESULT-FROM-VALUE-AND-ERROR-NEXT: } +// RESULT-FROM-VALUE-AND-ERROR-NEXT: } +// RESULT-FROM-VALUE-AND-ERROR-NEXT: } +// RESULT-FROM-VALUE-AND-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-RETURN-VALUES-AND-ERROR %s +func testMultipleReturnValuesAndError(completion: @escaping (Int?, String?, Error?) -> Void) { + withoutAsyncAlternativeThrowingWithMultipleResults { (first, second, error) in + completion(first, second, error) + } +} +// MULTIPLE-RETURN-VALUES-AND-ERROR: func testMultipleReturnValuesAndError() async throws -> (Int, String) { +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: return try await withCheckedThrowingContinuation { continuation in +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: withoutAsyncAlternativeThrowingWithMultipleResults { (first, second, error) in +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: if let error = error { +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: continuation.resume(throwing: error) +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: } else { +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: guard let first1 = first else { +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: fatalError("Expected non-nil result 'first1' in the non-error case") +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: } +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: guard let second1 = second else { +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: fatalError("Expected non-nil result 'second1' in the non-error case") +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: } +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: continuation.resume(returning: (first1, second1)) +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: } +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: } +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: } +// MULTIPLE-RETURN-VALUES-AND-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-OPTIONAL-VALUE-FOR-RESULT-AND-ERROR %s +func testReturnNonOptionalValuesForResultAndError(completion: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { result in + completion(1, MyError()) + } +} +// NON-OPTIONAL-VALUE-FOR-RESULT-AND-ERROR: func testReturnNonOptionalValuesForResultAndError() async throws -> Int { +// NON-OPTIONAL-VALUE-FOR-RESULT-AND-ERROR-NEXT: return try await withCheckedThrowingContinuation { continuation in +// NON-OPTIONAL-VALUE-FOR-RESULT-AND-ERROR-NEXT: withoutAsyncAlternativeBecauseOfMismatchedCompletionHandlerName { result in +// NON-OPTIONAL-VALUE-FOR-RESULT-AND-ERROR-NEXT: continuation.resume(throwing: MyError()) +// NON-OPTIONAL-VALUE-FOR-RESULT-AND-ERROR-NEXT: } +// NON-OPTIONAL-VALUE-FOR-RESULT-AND-ERROR-NEXT: } +// NON-OPTIONAL-VALUE-FOR-RESULT-AND-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT %s +func testMixedOptionalAnNonOptionaResults(completion: @escaping (Int?, String?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theResult, error) in + completion(theResult, "hi", nil) + } +} +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT: func testMixedOptionalAnNonOptionaResults() async throws -> (Int, String) { +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: return try await withCheckedThrowingContinuation { continuation in +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: withoutAsyncAlternativeThrowing { (theResult, error) in +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: guard let theResult1 = theResult else { +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: fatalError("Expected non-nil result 'theResult1' in the non-error case") +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: } +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: continuation.resume(returning: (theResult1, "hi")) +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: } +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: } +// MIXED-OPTIONAL-AND-NON-OPTIONAL-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL %s +func testUseOptionalResultValueAfterCompletionHandlerCall(completion: @escaping (Int?, String?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theResult, error) in + completion(theResult, "hi", nil) + print(theResult.map { $0 + 1 } as Any) + } +} +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL: func testUseOptionalResultValueAfterCompletionHandlerCall() async throws -> (Int, String) { +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: return try await withCheckedThrowingContinuation { continuation in +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: withoutAsyncAlternativeThrowing { (theResult, error) in +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: guard let theResult1 = theResult else { +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: fatalError("Expected non-nil result 'theResult1' in the non-error case") +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: } +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: continuation.resume(returning: (theResult1, "hi")) +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: print(theResult.map { $0 + 1 } as Any) +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: } +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: } +// USE-OPTIONAL-RESULT-AFTER-COMPLETION-HANDLER-CALL-NEXT: } + +// We shouldn't need to unwrap `theResult` twice here, but the example is silly and I don't care too much. +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=PASS-SAME-RESULT-TWICE %s +func testPassSameResultTwice(completion: @escaping (Int?, Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theResult, error) in + completion(theResult, theResult, nil) + } +} +// PASS-SAME-RESULT-TWICE: func testPassSameResultTwice() async throws -> (Int, Int) { +// PASS-SAME-RESULT-TWICE-NEXT: return try await withCheckedThrowingContinuation { continuation in +// PASS-SAME-RESULT-TWICE-NEXT: withoutAsyncAlternativeThrowing { (theResult, error) in +// PASS-SAME-RESULT-TWICE-NEXT: guard let theResult1 = theResult else { +// PASS-SAME-RESULT-TWICE-NEXT: fatalError("Expected non-nil result 'theResult1' in the non-error case") +// PASS-SAME-RESULT-TWICE-NEXT: } +// PASS-SAME-RESULT-TWICE-NEXT: guard let theResult2 = theResult else { +// PASS-SAME-RESULT-TWICE-NEXT: fatalError("Expected non-nil result 'theResult2' in the non-error case") +// PASS-SAME-RESULT-TWICE-NEXT: } +// PASS-SAME-RESULT-TWICE-NEXT: continuation.resume(returning: (theResult1, theResult2)) +// PASS-SAME-RESULT-TWICE-NEXT: } +// PASS-SAME-RESULT-TWICE-NEXT: } +// PASS-SAME-RESULT-TWICE-NEXT: } + + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL %s +func testUseResultAfterAmbiguousCompletionHandlerCall(completion: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theResult, error) in + completion(theResult, error) + print(theResult as Any) + } +} +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL: func testUseResultAfterAmbiguousCompletionHandlerCall() async throws -> Int { +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: return try await withCheckedThrowingContinuation { continuation in +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: withoutAsyncAlternativeThrowing { (theResult, error) in +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: if let error = error { +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: continuation.resume(throwing: error) +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: } else { +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: guard let theResult1 = theResult else { +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: fatalError("Expected non-nil result 'theResult1' in the non-error case") +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: } +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: continuation.resume(returning: theResult1) +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: } +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: print(theResult as Any) +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: } +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: } +// USE-RESULT-AFTER-AMBIGUOUS-HANLDER-CALL-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=TWO-COMPLEITON-HANDLER-CALLS %s +func testTwoCompletionHandlerCalls(completion: @escaping (Int?, Error?) -> Void) { + withoutAsyncAlternativeThrowing { (theResult, error) in + completion(theResult, error) + completion(theResult, error) + } +} +// TWO-COMPLEITON-HANDLER-CALLS: func testTwoCompletionHandlerCalls() async throws -> Int { +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: return try await withCheckedThrowingContinuation { continuation in +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: withoutAsyncAlternativeThrowing { (theResult, error) in +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: if let error = error { +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: continuation.resume(throwing: error) +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } else { +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: guard let theResult1 = theResult else { +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: fatalError("Expected non-nil result 'theResult1' in the non-error case") +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: continuation.resume(returning: theResult1) +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: if let error = error { +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: continuation.resume(throwing: error) +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } else { +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: guard let theResult2 = theResult else { +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: fatalError("Expected non-nil result 'theResult2' in the non-error case") +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: continuation.resume(returning: theResult2) +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } +// TWO-COMPLEITON-HANDLER-CALLS-NEXT: } + + +// Reduced version of https://twitter.com/peterfriese/status/1397835146133479428 + +class DataTask { + let completionHandler: (String?, Error?) -> Void + + init(completionHandler: @escaping (String?, Error?) -> Void) { + self.completionHandler = completionHandler + } + func resume() { + completionHandler("mock result", nil) + } +} + +class URLSession { + static let shared = URLSession() + + func dataTask(completionHandler: @escaping (String?, Error?) -> Void) -> DataTask { + return DataTask(completionHandler: completionHandler) + } +} + +func processURLResult(_ data: String) throws -> Int { + return data.count +} + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=URL-SESSION %s +func testDataTask(_ completion: @escaping (Int?) -> Void) { + let dataTask = URLSession.shared.dataTask { (data, error) in + guard let data = data else { + return // Yes, there is a bug about not calling completion here, but that's copied from the Twitter link above + } + do { + let processed = try processURLResult(data) + completion(processed) + } catch { + completion(nil) + } + } + dataTask.resume() +} +// URL-SESSION: func testDataTask() async -> Int? { +// URL-SESSION-NEXT: return await withCheckedContinuation { continuation in +// URL-SESSION-NEXT: let dataTask1 = URLSession.shared.dataTask { (data, error) in +// URL-SESSION-NEXT: guard let data1 = data else { +// URL-SESSION-NEXT: return // Yes, there is a bug about not calling completion here, but that's copied from the Twitter link above +// URL-SESSION-NEXT: } +// URL-SESSION-NEXT: do { +// URL-SESSION-NEXT: let processed = try processURLResult(data1) +// URL-SESSION-NEXT: continuation.resume(returning: processed) +// URL-SESSION-NEXT: } catch { +// URL-SESSION-NEXT: continuation.resume(returning: nil) +// URL-SESSION-NEXT: } +// URL-SESSION-NEXT: } +// URL-SESSION-NEXT: dataTask1.resume() +// URL-SESSION-NEXT: } +// URL-SESSION-NEXT: } + + + +// Reduced version of rdar://79304583 + +class DispatchQueue { + init() {} + + func async(execute work: @escaping @convention(block) () -> Void) { + work() + } +} + +func syncComputation() -> Int { return 42 } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=DISPATCH %s +func testDispatch(_ completionHandler: @escaping (Int) -> Void) { + let queue = DispatchQueue() + queue.async { + completionHandler(syncComputation()) + } +} +// DISPATCH: func testDispatch() async -> Int { +// DISPATCH-NEXT: let queue = DispatchQueue() +// DISPATCH-NEXT: return await withCheckedContinuation { continuation in +// DISPATCH-NEXT: queue.async { +// DISPATCH-NEXT: continuation.resume(returning: syncComputation()) +// DISPATCH-NEXT: } +// DISPATCH-NEXT: } +// DISPATCH-NEXT: } diff --git a/test/refactoring/ConvertAsync/errors.swift b/test/refactoring/ConvertAsync/errors.swift index 20846cc2c1d2d..6c8482206391e 100644 --- a/test/refactoring/ConvertAsync/errors.swift +++ b/test/refactoring/ConvertAsync/errors.swift @@ -1,4 +1,6 @@ -func simple(completion: (String?, Error?) -> Void) { } +// REQUIRES: concurrency + +func simple(completion: @escaping (String?, Error?) -> Void) { } func mismatches() { // RUN: not %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 @@ -16,4 +18,4 @@ func mismatches() { } // RUN: not %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 -func missingBody(complete: (Int?, Error?) -> Void) +func missingBody(complete: @escaping (Int?, Error?) -> Void) diff --git a/test/refactoring/ConvertAsync/handler_position.swift b/test/refactoring/ConvertAsync/handler_position.swift new file mode 100644 index 0000000000000..3b598d08a4cf7 --- /dev/null +++ b/test/refactoring/ConvertAsync/handler_position.swift @@ -0,0 +1,43 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=FIRST-ARG %s +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=FIRST-ARG-WRAPPER %s +func completionFirst(/*c1s*/ completion: @escaping (String) -> Void /*c1e*/, /*c2s*/ arg: Int /*c2e*/) { } + +// FIRST-ARG: @available(*, renamed: "completionFirst(arg:)") +// FIRST-ARG: Task { +// FIRST-ARG-NEXT: let result = await completionFirst(arg: arg) +// FIRST-ARG-NEXT: completion(result) +// FIRST-ARG-NEXT: } +// FIRST-ARG: func completionFirst(/*c2s*/ arg: Int /*c2e*/) async -> String { } + +// FIRST-ARG-WRAPPER: @available(*, renamed: "completionFirst(arg:)") +// FIRST-ARG-WRAPPER: func completionFirst(/*c2s*/ arg: Int /*c2e*/) async -> String { +// FIRST-ARG-WRAPPER-NEXT: return await withCheckedContinuation { continuation in +// FIRST-ARG-WRAPPER-NEXT: completionFirst(completion: { result in +// FIRST-ARG-WRAPPER-NEXT: continuation.resume(returning: result) +// FIRST-ARG-WRAPPER-NEXT: }, arg: arg) +// FIRST-ARG-WRAPPER-NEXT: } +// FIRST-ARG-WRAPPER-NEXT: } + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix MIDDLE-ARG %s +// RUN: %refactor-check-compiles -add-async-wrapper -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIDDLE-ARG-WRAPPER %s +func completionMiddle(/*c1s*/ arg1: Int /*c1e*/, /*c2s*/ _ arg2: Int /*c2e*/, /*c3s*/ completion: @escaping (String) -> Void /*c3e*/, /*c4s*/ arg3: Int /*c4e*/) { } + +// MIDDLE-ARG: @available(*, renamed: "completionMiddle(arg1:_:arg3:)") +// MIDDLE-ARG: Task { +// MIDDLE-ARG-NEXT: let result = await completionMiddle(arg1: arg1, arg2, arg3: arg3) +// MIDDLE-ARG-NEXT: completion(result) +// MIDDLE-ARG-NEXT: } +// MIDDLE-ARG: func completionMiddle(/*c1s*/ arg1: Int /*c1e*/, /*c2s*/ _ arg2: Int /*c2e*/, /*c4s*/ arg3: Int /*c4e*/) async -> String { } + +// MIDDLE-ARG-WRAPPER: @available(*, renamed: "completionMiddle(arg1:_:arg3:)") +// MIDDLE-ARG-WRAPPER: func completionMiddle(/*c1s*/ arg1: Int /*c1e*/, /*c2s*/ _ arg2: Int /*c2e*/, /*c4s*/ arg3: Int /*c4e*/) async -> String { +// MIDDLE-ARG-WRAPPER-NEXT: return await withCheckedContinuation { continuation in +// MIDDLE-ARG-WRAPPER-NEXT: completionMiddle(arg1: arg1, arg2, completion: { result in +// MIDDLE-ARG-WRAPPER-NEXT: continuation.resume(returning: result) +// MIDDLE-ARG-WRAPPER-NEXT: }, arg3: arg3) +// MIDDLE-ARG-WRAPPER-NEXT: } +// MIDDLE-ARG-WRAPPER-NEXT: } diff --git a/test/refactoring/ConvertAsync/labeled_closure_params.swift b/test/refactoring/ConvertAsync/labeled_closure_params.swift new file mode 100644 index 0000000000000..c70d304f92bf0 --- /dev/null +++ b/test/refactoring/ConvertAsync/labeled_closure_params.swift @@ -0,0 +1,72 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-LABELED-RESULTS %s +func mutlipleLabeledResults(completion: @escaping (_ first: String, _ second: String) -> Void) { } +// MULTIPLE-LABELED-RESULTS: { +// MULTIPLE-LABELED-RESULTS-NEXT: Task { +// MULTIPLE-LABELED-RESULTS-NEXT: let result = await mutlipleLabeledResults() +// MULTIPLE-LABELED-RESULTS-NEXT: completion(result.first, result.second) +// MULTIPLE-LABELED-RESULTS-NEXT: } +// MULTIPLE-LABELED-RESULTS-NEXT: } +// MULTIPLE-LABELED-RESULTS: func mutlipleLabeledResults() async -> (first: String, second: String) { } + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-LABELED-RESULTS %s +func mixedLabeledResult(completion: @escaping (_ first: String, String) -> Void) { } +// MIXED-LABELED-RESULTS: { +// MIXED-LABELED-RESULTS-NEXT: Task { +// MIXED-LABELED-RESULTS-NEXT: let result = await mixedLabeledResult() +// MIXED-LABELED-RESULTS-NEXT: completion(result.first, result.1) +// MIXED-LABELED-RESULTS-NEXT: } +// MIXED-LABELED-RESULTS-NEXT: } +// MIXED-LABELED-RESULTS: func mixedLabeledResult() async -> (first: String, String) { } + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SINGLE-LABELED-RESULT %s +func singleLabeledResult(completion: @escaping (_ first: String) -> Void) { } +// SINGLE-LABELED-RESULT: { +// SINGLE-LABELED-RESULT-NEXT: Task { +// SINGLE-LABELED-RESULT-NEXT: let result = await singleLabeledResult() +// SINGLE-LABELED-RESULT-NEXT: completion(result) +// SINGLE-LABELED-RESULT-NEXT: } +// SINGLE-LABELED-RESULT-NEXT: } +// SINGLE-LABELED-RESULT: func singleLabeledResult() async -> String { } + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SINGLE-LABELED-RESULT-WITH-ERROR %s +func singleLabeledResultWithError(completion: @escaping (_ first: String?, _ error: Error?) -> Void) { } +// SINGLE-LABELED-RESULT-WITH-ERROR: { +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: Task { +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: do { +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: let result = try await singleLabeledResultWithError() +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: completion(result, nil) +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: } catch { +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: completion(nil, error) +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: } +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: } +// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: } +// SINGLE-LABELED-RESULT-WITH-ERROR: func singleLabeledResultWithError() async throws -> String { } + +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-LABELED-RESULT-WITH-ERROR %s +func multipleLabeledResultWithError(completion: @escaping (_ first: String?, _ second: String?, _ error: Error?) -> Void) { } +// MULTIPLE-LABELED-RESULT-WITH-ERROR: { +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: Task { +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: do { +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: let result = try await multipleLabeledResultWithError() +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: completion(result.first, result.second, nil) +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: } catch { +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: completion(nil, nil, error) +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: } +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: } +// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: } +// MULTIPLE-LABELED-RESULT-WITH-ERROR: func multipleLabeledResultWithError() async throws -> (first: String, second: String) { } + +func testConvertCall() { + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=CONVERT-CALL %s + mutlipleLabeledResults() { (a, b) in + print(a) + print(b) + } + // CONVERT-CALL: let (a, b) = await mutlipleLabeledResults() + // CONVERT-CALL-NEXT: print(a) + // CONVERT-CALL-NEXT: print(b) +} diff --git a/test/refactoring/ConvertAsync/path_classification.swift b/test/refactoring/ConvertAsync/path_classification.swift new file mode 100644 index 0000000000000..d2d7312009646 --- /dev/null +++ b/test/refactoring/ConvertAsync/path_classification.swift @@ -0,0 +1,238 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +func simpleWithError(completion: @escaping (String?, Error?) -> Void) {} +func simpleWithError() async throws -> String {} + +func testPathClassification() async throws { + + // Both of these test cases should produce the same refactoring. + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ELSE-IF-CLASSIFICATION %s + simpleWithError { str, err in + if err == nil { + print("a") + } else if .random() { + print("b") + } else { + print("c") + } + if err != nil { + print("d") + } else if .random() { + print("e") + } else { + print("f") + } + } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ELSE-IF-CLASSIFICATION %s + simpleWithError { str, err in + if let str = str { + print("a") + } else if .random() { + print("b") + } else { + print("c") + } + if str == nil { + print("d") + } else if .random() { + print("e") + } else { + print("f") + } + } + + // ELSE-IF-CLASSIFICATION: do { + // ELSE-IF-CLASSIFICATION-NEXT: let str = try await simpleWithError() + // ELSE-IF-CLASSIFICATION-NEXT: print("a") + // ELSE-IF-CLASSIFICATION-NEXT: if .random() { + // ELSE-IF-CLASSIFICATION-NEXT: print("e") + // ELSE-IF-CLASSIFICATION-NEXT: } else { + // ELSE-IF-CLASSIFICATION-NEXT: print("f") + // ELSE-IF-CLASSIFICATION-NEXT: } + // ELSE-IF-CLASSIFICATION-NEXT: } catch let err { + // ELSE-IF-CLASSIFICATION-NEXT: if .random() { + // ELSE-IF-CLASSIFICATION-NEXT: print("b") + // ELSE-IF-CLASSIFICATION-NEXT: } else { + // ELSE-IF-CLASSIFICATION-NEXT: print("c") + // ELSE-IF-CLASSIFICATION-NEXT: } + // ELSE-IF-CLASSIFICATION-NEXT: print("d") + // ELSE-IF-CLASSIFICATION-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ELSE-IF-CLASSIFICATION2 %s + simpleWithError { str, err in + if err == nil { + print("a") + } else if .random() { + print("b") + } + if .random() { + print("c") + } + if err != nil, .random() { + print("d") + } + } + + // Make sure the classification of 'b' into the error block doesn't affect the + // handling of 'c'. + // ELSE-IF-CLASSIFICATION2: do { + // ELSE-IF-CLASSIFICATION2-NEXT: let str = try await simpleWithError() + // ELSE-IF-CLASSIFICATION2-NEXT: print("a") + // ELSE-IF-CLASSIFICATION2-NEXT: if .random() { + // ELSE-IF-CLASSIFICATION2-NEXT: print("c") + // ELSE-IF-CLASSIFICATION2-NEXT: } + // ELSE-IF-CLASSIFICATION2-NEXT: } catch let err { + // ELSE-IF-CLASSIFICATION2-NEXT: if .random() { + // ELSE-IF-CLASSIFICATION2-NEXT: print("b") + // ELSE-IF-CLASSIFICATION2-NEXT: } + // ELSE-IF-CLASSIFICATION2-NEXT: if <#err#> != nil, .random() { + // ELSE-IF-CLASSIFICATION2-NEXT: print("d") + // ELSE-IF-CLASSIFICATION2-NEXT: } + // ELSE-IF-CLASSIFICATION2-NEXT: } + + // FIXME: This case is a little tricky: Being in the else block of 'err == nil' + // should allow us to place 'if let str = str' in the failure block. However, this + // is a success condition, so we still place it in the success block. Really what + // we need to do here is check to see if simpleWithError has an existing async + // alternative that still returns optional success values, and allow success + // classification in that case. Otherwise, we'd probably be better off leaving + // the condition unhandled, as it's not clear what the user is doing. + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ELSE-IF-CLASSIFICATION3 %s + simpleWithError { str, err in + if err == nil { + print("a") + } else if let str = str { + print("b") + } else { + print("c") + } + } + + // ELSE-IF-CLASSIFICATION3: do { + // ELSE-IF-CLASSIFICATION3-NEXT: let str = try await simpleWithError() + // ELSE-IF-CLASSIFICATION3-NEXT: print("a") + // ELSE-IF-CLASSIFICATION3-NEXT: print("b") + // ELSE-IF-CLASSIFICATION3-NEXT: } catch let err { + // ELSE-IF-CLASSIFICATION3-NEXT: print("c") + // ELSE-IF-CLASSIFICATION3-NEXT: } + + // FIXME: Similar to the case above. + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ELSE-IF-CLASSIFICATION4 %s + simpleWithError { str, err in + if err == nil { + print("a") + } else if let str = str { + print("b") + } else if .random() { + print("c") + } else { + print("d") + } + } + + // ELSE-IF-CLASSIFICATION4: do { + // ELSE-IF-CLASSIFICATION4-NEXT: let str = try await simpleWithError() + // ELSE-IF-CLASSIFICATION4-NEXT: print("a") + // ELSE-IF-CLASSIFICATION4-NEXT: print("b") + // ELSE-IF-CLASSIFICATION4-NEXT: } catch let err { + // ELSE-IF-CLASSIFICATION4-NEXT: if .random() { + // ELSE-IF-CLASSIFICATION4-NEXT: print("c") + // ELSE-IF-CLASSIFICATION4-NEXT: } else { + // ELSE-IF-CLASSIFICATION4-NEXT: print("d") + // ELSE-IF-CLASSIFICATION4-NEXT: } + // ELSE-IF-CLASSIFICATION4-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=ELSE-IF-CLASSIFICATION5 %s + simpleWithError { str, err in + if let err = err { + print("a") + } else if let str = str { + print("b") + return + } else if .random() { + print("c") + } else { + print("d") + } + } + + // ELSE-IF-CLASSIFICATION5: do { + // ELSE-IF-CLASSIFICATION5-NEXT: let str = try await simpleWithError() + // ELSE-IF-CLASSIFICATION5-NEXT: print("b") + // ELSE-IF-CLASSIFICATION5-NEXT: } catch let err { + // ELSE-IF-CLASSIFICATION5-NEXT: print("a") + // ELSE-IF-CLASSIFICATION5-NEXT: if .random() { + // ELSE-IF-CLASSIFICATION5-NEXT: print("c") + // ELSE-IF-CLASSIFICATION5-NEXT: } else { + // ELSE-IF-CLASSIFICATION5-NEXT: print("d") + // ELSE-IF-CLASSIFICATION5-NEXT: } + // ELSE-IF-CLASSIFICATION5-NEXT: } + + // Cannot use refactor-check-compiles, as 'err' cannot have its type inferred + // from placeholder. + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=IF-LET-RETURN-CLASSIFICATION %s + simpleWithError { str, err in + if let str = str { + print("a") + return + } + if .random(), let err = err { + print("b") + } else { + print("c") + } + } + + // IF-LET-RETURN-CLASSIFICATION: do { + // IF-LET-RETURN-CLASSIFICATION-NEXT: let str = try await simpleWithError() + // IF-LET-RETURN-CLASSIFICATION-NEXT: print("a") + // IF-LET-RETURN-CLASSIFICATION-NEXT: } catch let err { + // IF-LET-RETURN-CLASSIFICATION-NEXT: if .random(), let err = <#err#> { + // IF-LET-RETURN-CLASSIFICATION-NEXT: print("b") + // IF-LET-RETURN-CLASSIFICATION-NEXT: } else { + // IF-LET-RETURN-CLASSIFICATION-NEXT: print("c") + // IF-LET-RETURN-CLASSIFICATION-NEXT: } + // IF-LET-RETURN-CLASSIFICATION-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=GUARD-CLASSIFICATION %s + simpleWithError { str, err in + guard let str = str else { + print("a") + return + } + guard err == nil, .random() else { + print("b") + return + } + print("c") + } + + // GUARD-CLASSIFICATION: do { + // GUARD-CLASSIFICATION-NEXT: let str = try await simpleWithError() + // GUARD-CLASSIFICATION-NEXT: guard <#err#> == nil, .random() else { + // GUARD-CLASSIFICATION-NEXT: print("b") + // GUARD-CLASSIFICATION-NEXT: <#return#> + // GUARD-CLASSIFICATION-NEXT: } + // GUARD-CLASSIFICATION-NEXT: print("c") + // GUARD-CLASSIFICATION-NEXT: } catch let err { + // GUARD-CLASSIFICATION-NEXT: print("a") + // GUARD-CLASSIFICATION-NEXT: } + + // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=SILLY-CLASSIFICATION %s + simpleWithError { str, err in + guard let str = str else { return } + guard let err = err else { return } + print("urr") + } + + // In this case we just take whichever guard is last. + // SILLY-CLASSIFICATION: do { + // SILLY-CLASSIFICATION-NEXT: let str = try await simpleWithError() + // SILLY-CLASSIFICATION-NEXT: } catch let err { + // SILLY-CLASSIFICATION-NEXT: print("urr") + // SILLY-CLASSIFICATION-NEXT: } +} diff --git a/test/refactoring/ConvertAsync/variable_as_callback.swift b/test/refactoring/ConvertAsync/variable_as_callback.swift new file mode 100644 index 0000000000000..78d97bb3f3102 --- /dev/null +++ b/test/refactoring/ConvertAsync/variable_as_callback.swift @@ -0,0 +1,455 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +enum CustomError: Error { + case invalid + case insecure +} + +typealias SomeCallback = (String) -> Void + +func simple(completion: @escaping (String) -> Void) { } +func simple() async -> String { } + +func simpleWithArg(a: Int, completion: @escaping (String) -> Void) { } +func simpleWithArg(a: Int) async -> String { } + +func multipleResults(completion: @escaping (String, Int) -> Void) { } +func multipleResults() async -> (String, Int) { } + +func nonOptionalError(completion: @escaping (String, Error) -> Void) { } +func nonOptionalError() async -> (String, Error) { } + +func noParams(completion: @escaping () -> Void) { } +func noParams() async { } + +func error(completion: @escaping (String?, Error?) -> Void) { } +func error() async throws -> String { } + +func errorOnly(completion: @escaping (Error?) -> Void) { } +func errorOnly() async throws { } + +func errorNonOptionalResult(completion: @escaping (String, Error?) -> Void) { } +func errorNonOptionalResult() async throws -> String { } + +func alias(completion: @escaping SomeCallback) { } +func alias() async -> String { } + +func simpleResult(completion: @escaping (Result) -> Void) { } +func simpleResult() async -> String { } + +func errorResult(completion: @escaping (Result) -> Void) { } +func errorResult() async throws -> String { } + +func customErrorResult(completion: @escaping (Result) -> Void) { } +func customErrorResult() async throws -> String { } + +func optionalSingle(completion: @escaping (String?) -> Void) { } +func optionalSingle() async -> String? { } + +func manyOptional(_ completion: @escaping (String?, Int?) -> Void) { } +func manyOptional() async -> (String?, Int?) { } + +func generic(completion: @escaping (T, R) -> Void) { } +func generic() async -> (T, R) { } + +func genericResult(completion: @escaping (T?, Error?) -> Void) where T: Numeric { } +func genericResult() async throws -> T where T: Numeric { } + +func genericError(completion: @escaping (String?, E?) -> Void) where E: Error { } +func genericError() async throws -> String { } + +func defaultArgs(a: Int, b: Int = 10, completion: @escaping (String) -> Void) { } +func defaultArgs(a: Int, b: Int = 10) async -> String { } + + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=SIMPLE-WITH-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=SIMPLE-WITH %s +func testSimpleWithVariableCompletionHandler(completionHandler: @escaping (String) -> Void) { + simple(completion: completionHandler) +} +// SIMPLE-WITH-FUNC: func testSimpleWithVariableCompletionHandler() async -> String { +// SIMPLE-WITH-FUNC-NEXT: return await simple() +// SIMPLE-WITH-FUNC-NEXT: } + +// SIMPLE-WITH: let result = await simple() +// SIMPLE-WITH-NEXT: completionHandler(result) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=SIMPLE-WITH-ARG-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=SIMPLE-WITH-ARG %s +func testSimpleWithArgVariableCompletionHandler(b: Int, completionHandler: @escaping (String) -> Void) { + simpleWithArg(a: b, completion: completionHandler) +} +// SIMPLE-WITH-ARG-FUNC: func testSimpleWithArgVariableCompletionHandler(b: Int) async -> String { +// SIMPLE-WITH-ARG-FUNC-NEXT: return await simpleWithArg(a: b) +// SIMPLE-WITH-ARG-FUNC-NEXT: } + +// SIMPLE-WITH-ARG: let result = await simpleWithArg(a: b) +// SIMPLE-WITH-ARG-NEXT: completionHandler(result) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=SIMPLE-WITH-CONSTANT-ARG-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=SIMPLE-WITH-CONSTANT-ARG %s +func testSimpleWithConstantArgVariableCompletionHandler(completionHandler: @escaping (String) -> Void) { + simpleWithArg(a: 1, completion: completionHandler) +} +// SIMPLE-WITH-CONSTANT-ARG-FUNC: func testSimpleWithConstantArgVariableCompletionHandler() async -> String { +// SIMPLE-WITH-CONSTANT-ARG-FUNC-NEXT: return await simpleWithArg(a: 1) +// SIMPLE-WITH-CONSTANT-ARG-FUNC-NEXT: } + +// SIMPLE-WITH-CONSTANT-ARG: let result = await simpleWithArg(a: 1) +// SIMPLE-WITH-CONSTANT-ARG-NEXT: completionHandler(result) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=MULTIPLE-RESULTS-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=MULTIPLE-RESULTS %s +func testMultipleResultsVariableCompletionHandler(completionHandler: @escaping (String, Int) -> Void) { + multipleResults(completion: completionHandler) +} +// MULTIPLE-RESULTS-FUNC: func testMultipleResultsVariableCompletionHandler() async -> (String, Int) { +// MULTIPLE-RESULTS-FUNC-NEXT: return await multipleResults() +// MULTIPLE-RESULTS-FUNC-NEXT: } + +// MULTIPLE-RESULTS: let result = await multipleResults() +// MULTIPLE-RESULTS-NEXT: completionHandler(result.0, result.1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=NON-OPTIONAL-ERROR-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=NON-OPTIONAL-ERROR %s +func testNonOptionalErrorVariableCompletionHandler(completionHandler: @escaping (String, Error) -> Void) { + nonOptionalError(completion: completionHandler) +} +// NON-OPTIONAL-ERROR-FUNC: func testNonOptionalErrorVariableCompletionHandler() async -> (String, Error) { +// NON-OPTIONAL-ERROR-FUNC-NEXT: return await nonOptionalError() +// NON-OPTIONAL-ERROR-FUNC-NEXT: } + +// NON-OPTIONAL-ERROR: let result = await nonOptionalError() +// NON-OPTIONAL-ERROR-NEXT: completionHandler(result.0, result.1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=NO-PARAMS-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=NO-PARAMS %s +func testNoParamsVariableCompletionHandler(completionHandler: @escaping () -> Void) { + noParams(completion: completionHandler) +} +// NO-PARAMS-FUNC: func testNoParamsVariableCompletionHandler() async { +// NO-PARAMS-FUNC-NOT: return +// NO-PARAMS-FUNC-NEXT: await noParams() +// NO-PARAMS-FUNC-NEXT: } + +// NO-PARAMS: await noParams() +// NO-PARAMS-NEXT: completionHandler() + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=ERROR-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=ERROR %s +func testErrorWithVariableCompletionHandler(completionHandler: @escaping (String?, Error?) -> Void) { + error(completion: completionHandler) +} +// ERROR-FUNC: func testErrorWithVariableCompletionHandler() async throws -> String { +// ERROR-FUNC-NEXT: return try await error() +// ERROR-FUNC-NEXT: } + +// ERROR: do { +// ERROR-NEXT: let result = try await error() +// ERROR-NEXT: completionHandler(result, nil) +// ERROR-NEXT: } catch { +// ERROR-NEXT: completionHandler(nil, error) +// ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=ERROR-ONLY-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=ERROR-ONLY %s +func testErrorOnlyWithVariableCompletionHandler(completionHandler: @escaping (Error?) -> Void) { + errorOnly(completion: completionHandler) +} +// ERROR-ONLY-FUNC: func testErrorOnlyWithVariableCompletionHandler() async throws { +// ERROR-ONLY-FUNC-NOT: return +// ERROR-ONLY-FUNC-NEXT: try await errorOnly() +// ERROR-ONLY-FUNC-NEXT: } + +// ERROR-ONLY: do { +// ERROR-ONLY-NEXT: try await errorOnly() +// ERROR-ONLY-NEXT: completionHandler(nil) +// ERROR-ONLY-NEXT: } catch { +// ERROR-ONLY-NEXT: completionHandler(error) +// ERROR-ONLY-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=ERROR-NON-OPTIONAL-RESULT-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=ERROR-NON-OPTIONAL-RESULT %s +func testErrorNonOptionalResultWithVariableCompletionHandler(completionHandler: @escaping (String, Error?) -> Void) { + errorNonOptionalResult(completion: completionHandler) +} +// ERROR-NON-OPTIONAL-RESULT-FUNC: func testErrorNonOptionalResultWithVariableCompletionHandler() async throws -> String { +// ERROR-NON-OPTIONAL-RESULT-FUNC-NEXT: return try await errorNonOptionalResult() +// ERROR-NON-OPTIONAL-RESULT-FUNC-NEXT: } + +// ERROR-NON-OPTIONAL-RESULT: do { +// ERROR-NON-OPTIONAL-RESULT-NEXT: let result = try await errorNonOptionalResult() +// ERROR-NON-OPTIONAL-RESULT-NEXT: completionHandler(result, nil) +// ERROR-NON-OPTIONAL-RESULT-NEXT: } catch { +// ERROR-NON-OPTIONAL-RESULT-NEXT: completionHandler(<#String#>, error) +// ERROR-NON-OPTIONAL-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=ALIAS-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=ALIAS %s +func testAliasWithVariableCompletionHandler(completionHandler: @escaping SomeCallback) { + alias(completion: completionHandler) +} +// ALIAS-FUNC: func testAliasWithVariableCompletionHandler() async -> String { +// ALIAS-FUNC-NEXT: return await alias() +// ALIAS-FUNC-NEXT: } + +// ALIAS: let result = await alias() +// ALIAS-NEXT: completionHandler(result) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=SIMPLE-RESULT-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=SIMPLE-RESULT %s +func testSimpleResultVariableCompletionHandler(completionHandler: @escaping (Result) -> Void) { + simpleResult(completion: completionHandler) +} +// SIMPLE-RESULT-FUNC: func testSimpleResultVariableCompletionHandler() async -> String { +// SIMPLE-RESULT-FUNC-NEXT: return await simpleResult() +// SIMPLE-RESULT-FUNC-NEXT: } + +// SIMPLE-RESULT: let result = await simpleResult() +// SIMPLE-RESULT-NEXT: completionHandler(.success(result)) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=ERROR-RESULT-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=ERROR-RESULT %s +func testErrorResultVariableCompletionHandler(completionHandler: @escaping (Result) -> Void) { + errorResult(completion: completionHandler) +} +// ERROR-RESULT-FUNC: func testErrorResultVariableCompletionHandler() async throws -> String { +// ERROR-RESULT-FUNC-NEXT: return try await errorResult() +// ERROR-RESULT-FUNC-NEXT: } + +// ERROR-RESULT: do { +// ERROR-RESULT-NEXT: let result = try await errorResult() +// ERROR-RESULT-NEXT: completionHandler(.success(result)) +// ERROR-RESULT-NEXT: } catch { +// ERROR-RESULT-NEXT: completionHandler(.failure(error)) +// ERROR-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=CUSTOM-ERROR-RESULT-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=CUSTOM-ERROR-RESULT %s +func testErrorResultVariableCompletionHandler(completionHandler: @escaping (Result) -> Void) { + customErrorResult(completion: completionHandler) +} +// CUSTOM-ERROR-RESULT-FUNC: func testErrorResultVariableCompletionHandler() async throws -> String { +// CUSTOM-ERROR-RESULT-FUNC-NEXT: return try await customErrorResult() +// CUSTOM-ERROR-RESULT-FUNC-NEXT: } + +// CUSTOM-ERROR-RESULT: do { +// CUSTOM-ERROR-RESULT-NEXT: let result = try await customErrorResult() +// CUSTOM-ERROR-RESULT-NEXT: completionHandler(.success(result)) +// CUSTOM-ERROR-RESULT-NEXT: } catch { +// CUSTOM-ERROR-RESULT-NEXT: completionHandler(.failure(error as! CustomError)) +// CUSTOM-ERROR-RESULT-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=OPTIONAL-SINGLE-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=OPTIONAL-SINGLE %s +func testOptionalSingleVariableCompletionHandler(completionHandler: @escaping (String?) -> Void) { + optionalSingle(completion: completionHandler) +} +// OPTIONAL-SINGLE-FUNC: func testOptionalSingleVariableCompletionHandler() async -> String? { +// OPTIONAL-SINGLE-FUNC-NEXT: return await optionalSingle() +// OPTIONAL-SINGLE-FUNC-NEXT: } + +// OPTIONAL-SINGLE: let result = await optionalSingle() +// OPTIONAL-SINGLE-NEXT: completionHandler(result) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=MANY-OPTIONAL-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=MANY-OPTIONAL %s +func testManyOptionalVariableCompletionHandler(completionHandler: @escaping (String?, Int?) -> Void) { + manyOptional(completionHandler) +} +// MANY-OPTIONAL-FUNC: func testManyOptionalVariableCompletionHandler() async -> (String?, Int?) { +// MANY-OPTIONAL-FUNC-NEXT: return await manyOptional() +// MANY-OPTIONAL-FUNC-NEXT: } + +// MANY-OPTIONAL: let result = await manyOptional() +// MANY-OPTIONAL-NEXT: completionHandler(result.0, result.1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=GENERIC-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=GENERIC %s +func testGenericVariableCompletionHandler(completionHandler: @escaping (T, R) -> Void) { + generic(completion: completionHandler) +} +// GENERIC-FUNC: func testGenericVariableCompletionHandler() async -> (T, R) { +// GENERIC-FUNC-NEXT: return await generic() +// GENERIC-FUNC-NEXT: } + +// GENERIC: let result: (T, R) = await generic() +// GENERIC-NEXT: completionHandler(result.0, result.1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=SPECIALIZE-GENERIC-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=SPECIALIZE-GENERIC %s +func testSpecializeGenericsVariableCompletionHandler(completionHandler: @escaping (String, Int) -> Void) { + generic(completion: completionHandler) +} +// SPECIALIZE-GENERIC-FUNC: func testSpecializeGenericsVariableCompletionHandler() async -> (String, Int) { +// SPECIALIZE-GENERIC-FUNC-NEXT: return await generic() +// SPECIALIZE-GENERIC-FUNC-NEXT: } + +// SPECIALIZE-GENERIC: let result: (String, Int) = await generic() +// SPECIALIZE-GENERIC-NEXT: completionHandler(result.0, result.1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=GENERIC-RESULT-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=GENERIC-RESULT %s +func testGenericResultVariableCompletionHandler(completionHandler: @escaping (T?, Error?) -> Void) where T: Numeric { + genericResult(completion: completionHandler) +} +// GENERIC-RESULT-FUNC: func testGenericResultVariableCompletionHandler() async throws -> T where T: Numeric { +// GENERIC-RESULT-FUNC-NEXT: return try await genericResult() +// GENERIC-RESULT-FUNC-NEXT: } + +// GENERIC-RESULT: do { +// GENERIC-RESULT-NEXT: let result: T = try await genericResult() +// GENERIC-RESULT-NEXT: completionHandler(result, nil) +// GENERIC-RESULT-NEXT: } catch { +// GENERIC-RESULT-NEXT: completionHandler(nil, error) +// GENERIC-RESULT-NEXT: } + +// RUN: %refactor -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=GENERIC-ERROR-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=GENERIC-ERROR %s +func testGenericErrorVariableCompletionHandler(completionHandler: @escaping (String?, MyGenericError?) -> Void) where MyGenericError: Error { + genericError(completion: completionHandler) +} +// GENERIC-ERROR-FUNC: func testGenericErrorVariableCompletionHandler() async throws -> String where MyGenericError: Error { +// GENERIC-ERROR-FUNC-NEXT: return try await genericError() +// GENERIC-ERROR-FUNC-NEXT: } + +// GENERIC-ERROR: do { +// GENERIC-ERROR-NEXT: let result: String = try await genericError() +// GENERIC-ERROR-NEXT: completionHandler(result, nil) +// GENERIC-ERROR-NEXT: } catch { +// GENERIC-ERROR-NEXT: completionHandler(nil, (error as! MyGenericError)) +// GENERIC-ERROR-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=DEFAULT-ARGS-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=DEFAULT-ARGS %s +func testDefaultArgsVariableCompletionHandler(completionHandler: @escaping (String) -> Void) { + defaultArgs(a: 5, completion: completionHandler) +} +// DEFAULT-ARGS-FUNC: func testDefaultArgsVariableCompletionHandler() async -> String { +// DEFAULT-ARGS-FUNC-NEXT: return await defaultArgs(a: 5) +// DEFAULT-ARGS-FUNC-NEXT: } + +// DEFAULT-ARGS: let result = await defaultArgs(a: 5) +// DEFAULT-ARGS-NEXT: completionHandler(result) + +func myPrint(_ message: String) { + print(message) +} + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=GLOBAL-FUNC-AS-COMPLETION-HANDLER-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=GLOBAL-FUNC-AS-COMPLETION-HANDLER %s +func testGlobalFuncAsCompletionHandler() { + simple(completion: myPrint) +} +// GLOBAL-FUNC-AS-COMPLETION-HANDLER-FUNC: func testGlobalFuncAsCompletionHandler() async { +// GLOBAL-FUNC-AS-COMPLETION-HANDLER-FUNC-NEXT: let result = await simple() +// GLOBAL-FUNC-AS-COMPLETION-HANDLER-FUNC-NEXT: myPrint(result) +// GLOBAL-FUNC-AS-COMPLETION-HANDLER-FUNC-NEXT: } + +// GLOBAL-FUNC-AS-COMPLETION-HANDLER: let result = await simple() +// GLOBAL-FUNC-AS-COMPLETION-HANDLER-NEXT: myPrint(result) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):1 | %FileCheck -check-prefix=VARIABLE-AS-COMPLETION-HANDLER-FUNC %s +// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+3):3 | %FileCheck -check-prefix=VARIABLE-AS-COMPLETION-HANDLER %s +func testVariableAsCompletionHandler() { + let complete: (String) -> Void = { print($0) } + simple(completion: complete) +} +// VARIABLE-AS-COMPLETION-HANDLER-FUNC: func testVariableAsCompletionHandler() async { +// VARIABLE-AS-COMPLETION-HANDLER-FUNC-NEXT: let complete: (String) -> Void = { print($0) } +// VARIABLE-AS-COMPLETION-HANDLER-FUNC-NEXT: let result = await simple() +// VARIABLE-AS-COMPLETION-HANDLER-FUNC-NEXT: complete(result) +// VARIABLE-AS-COMPLETION-HANDLER-FUNC-NEXT: } + +// VARIABLE-AS-COMPLETION-HANDLER: let result = await simple() +// VARIABLE-AS-COMPLETION-HANDLER-NEXT: complete(result) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=PRINTING-WRAPPER-FUNC %s +func testPrintingWrapper(completionHandler: @escaping (String) -> Void) { + print("Starting") + simple(completion: completionHandler) + print("Operation scheduled") +} +// PRINTING-WRAPPER-FUNC: func testPrintingWrapper() async -> String { +// PRINTING-WRAPPER-FUNC-NEXT: print("Starting") +// PRINTING-WRAPPER-FUNC-NEXT: return await simple() +// PRINTING-WRAPPER-FUNC-NEXT: print("Operation scheduled") +// PRINTING-WRAPPER-FUNC-NEXT: } + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SHADOWING-BEFORE %s +func testShadowingBefore() { + let complete: (String) -> Void = { print($0) } + let result = 1 + simple(completion: complete) +} +// SHADOWING-BEFORE: func testShadowingBefore() async { +// SHADOWING-BEFORE-NEXT: let complete: (String) -> Void = { print($0) } +// SHADOWING-BEFORE-NEXT: let result = 1 +// SHADOWING-BEFORE-NEXT: let result1 = await simple() +// SHADOWING-BEFORE-NEXT: complete(result1) + +// RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SHADOWING-AFTER %s +func testShadowingAfter() { + let complete: (String) -> Void = { print($0) } + simple(completion: complete) + let result = 1 +} +// SHADOWING-AFTER: func testShadowingAfter() async { +// SHADOWING-AFTER-NEXT: let complete: (String) -> Void = { print($0) } +// SHADOWING-AFTER-NEXT: let result = await simple() +// SHADOWING-AFTER-NEXT: complete(result) +// SHADOWING-AFTER-NEXT: let result1 = 1 + +class Foo { + var foo: Foo + + init(foo: Foo) { + self.foo = foo + } + + func myFooPrint(_ message: String) { + print("FOO: \(message)") + } + + // RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=MEMBER-FUNC-AS-COMPLETION-HANDLER-FUNC %s + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):5 | %FileCheck -check-prefix=MEMBER-FUNC-AS-COMPLETION-HANDLER %s + func testMethodAsCompletionHandler() { + simple(completion: myFooPrint) + } + // MEMBER-FUNC-AS-COMPLETION-HANDLER-FUNC: func testMethodAsCompletionHandler() async { + // MEMBER-FUNC-AS-COMPLETION-HANDLER-FUNC-NEXT: let result = await simple() + // MEMBER-FUNC-AS-COMPLETION-HANDLER-FUNC-NEXT: myFooPrint(result) + // MEMBER-FUNC-AS-COMPLETION-HANDLER-FUNC-NEXT: } + + // MEMBER-FUNC-AS-COMPLETION-HANDLER: let result = await simple() + // MEMBER-FUNC-AS-COMPLETION-HANDLER-NEXT: myFooPrint(result) + + // RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=MEMBER-FUNC-ON-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC %s + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):5 | %FileCheck -check-prefix=MEMBER-FUNC-ON-OTHER-OBJECT-AS-COMPLETION-HANDLER %s + func testMethodOnOtherObjectAsCompletionHandler(foo: Foo) { + simple(completion: foo.myFooPrint) + } + // MEMBER-FUNC-ON-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC: func testMethodOnOtherObjectAsCompletionHandler(foo: Foo) async { + // MEMBER-FUNC-ON-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC-NEXT: let result = await simple() + // MEMBER-FUNC-ON-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC-NEXT: foo.myFooPrint(result) + // MEMBER-FUNC-ON-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC-NEXT: } + + // MEMBER-FUNC-ON-OTHER-OBJECT-AS-COMPLETION-HANDLER: let result = await simple() + // MEMBER-FUNC-ON-OTHER-OBJECT-AS-COMPLETION-HANDLER-NEXT: foo.myFooPrint(result) + + // RUN: %refactor-check-compiles -convert-to-async -dump-text -source-filename %s -pos=%(line+2):3 | %FileCheck -check-prefix=MEMBER-FUNC-ON-NESTED-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC %s + // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+2):5 | %FileCheck -check-prefix=MEMBER-FUNC-ON-NESTED-OTHER-OBJECT-AS-COMPLETION-HANDLER %s + func testMethodOnNestedOtherObjectAsCompletionHandler(foo: Foo) { + simple(completion: foo.foo.myFooPrint) + } + // MEMBER-FUNC-ON-NESTED-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC: func testMethodOnNestedOtherObjectAsCompletionHandler(foo: Foo) async { + // MEMBER-FUNC-ON-NESTED-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC-NEXT: let result = await simple() + // MEMBER-FUNC-ON-NESTED-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC-NEXT: foo.foo.myFooPrint(result) + // MEMBER-FUNC-ON-NESTED-OTHER-OBJECT-AS-COMPLETION-HANDLER-FUNC-NEXT: } + + // MEMBER-FUNC-ON-NESTED-OTHER-OBJECT-AS-COMPLETION-HANDLER: let result = await simple() + // MEMBER-FUNC-ON-NESTED-OTHER-OBJECT-AS-COMPLETION-HANDLER-NEXT: foo.foo.myFooPrint(result) + +} diff --git a/test/refactoring/ConvertAsync/void_handler.swift b/test/refactoring/ConvertAsync/void_handler.swift new file mode 100644 index 0000000000000..6fccd4c79f459 --- /dev/null +++ b/test/refactoring/ConvertAsync/void_handler.swift @@ -0,0 +1,15 @@ +// REQUIRES: concurrency + +// RUN: %empty-directory(%t) + +// A (Void) parameter has a warning in Swift, so this function is pulled into +// its own file +// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix VOID-HANDLER %s +func voidCompletion(completion: @escaping (Void) -> Void) {} +// VOID-HANDLER: { +// VOID-HANDLER-NEXT: Task { +// VOID-HANDLER-NEXT: await voidCompletion() +// VOID-HANDLER-NEXT: completion(()) +// VOID-HANDLER-NEXT: } +// VOID-HANDLER-NEXT: } +// VOID-HANDLER: func voidCompletion() async {} diff --git a/test/refactoring/ExtractFunction/Outputs/await/async1.swift.expected b/test/refactoring/ExtractFunction/Outputs/await/async1.swift.expected new file mode 100644 index 0000000000000..83dc90eeab244 --- /dev/null +++ b/test/refactoring/ExtractFunction/Outputs/await/async1.swift.expected @@ -0,0 +1,15 @@ +func longLongLongJourney() async -> Int { 0 } +func longLongLongAwryJourney() async throws -> Int { 0 } +func consumesAsync(_ fn: () async throws -> Void) rethrows {} + +fileprivate func new_name() async -> Int { +return await longLongLongJourney() +} + +func testThrowingClosure() async throws -> Int { + let x = await new_name() + let y = try await longLongLongAwryJourney() + 1 + try consumesAsync { try await longLongLongAwryJourney() } + return x + y +} + diff --git a/test/refactoring/ExtractFunction/Outputs/await/async2.swift.expected b/test/refactoring/ExtractFunction/Outputs/await/async2.swift.expected new file mode 100644 index 0000000000000..a9814f953badc --- /dev/null +++ b/test/refactoring/ExtractFunction/Outputs/await/async2.swift.expected @@ -0,0 +1,15 @@ +func longLongLongJourney() async -> Int { 0 } +func longLongLongAwryJourney() async throws -> Int { 0 } +func consumesAsync(_ fn: () async throws -> Void) rethrows {} + +fileprivate func new_name() async throws -> Int { +return try await longLongLongAwryJourney() + 1 +} + +func testThrowingClosure() async throws -> Int { + let x = await longLongLongJourney() + let y = try await new_name() + try consumesAsync { try await longLongLongAwryJourney() } + return x + y +} + diff --git a/test/refactoring/ExtractFunction/Outputs/await/consumes_async.swift.expected b/test/refactoring/ExtractFunction/Outputs/await/consumes_async.swift.expected new file mode 100644 index 0000000000000..807ec91e712b6 --- /dev/null +++ b/test/refactoring/ExtractFunction/Outputs/await/consumes_async.swift.expected @@ -0,0 +1,15 @@ +func longLongLongJourney() async -> Int { 0 } +func longLongLongAwryJourney() async throws -> Int { 0 } +func consumesAsync(_ fn: () async throws -> Void) rethrows {} + +fileprivate func new_name() throws { +try consumesAsync { try await longLongLongAwryJourney() } +} + +func testThrowingClosure() async throws -> Int { + let x = await longLongLongJourney() + let y = try await longLongLongAwryJourney() + 1 + try new_name() + return x + y +} + diff --git a/test/refactoring/ExtractFunction/Outputs/throw_errors/L13-17.swift.expected b/test/refactoring/ExtractFunction/Outputs/throw_errors/L13-17.swift.expected index 2524d18033f3a..3ff430cac263c 100644 --- a/test/refactoring/ExtractFunction/Outputs/throw_errors/L13-17.swift.expected +++ b/test/refactoring/ExtractFunction/Outputs/throw_errors/L13-17.swift.expected @@ -18,6 +18,6 @@ func foo2() throws { do { try foo1() } catch {} - new_name() + try new_name() } diff --git a/test/refactoring/ExtractFunction/Outputs/throw_errors/L8-8.swift.expected b/test/refactoring/ExtractFunction/Outputs/throw_errors/L8-8.swift.expected index 94b13efb1c86a..d4005871c1ffc 100644 --- a/test/refactoring/ExtractFunction/Outputs/throw_errors/L8-8.swift.expected +++ b/test/refactoring/ExtractFunction/Outputs/throw_errors/L8-8.swift.expected @@ -9,7 +9,7 @@ return try foo1() } func foo2() throws { - new_name() + try new_name() try! foo1() do { try foo1() diff --git a/test/refactoring/ExtractFunction/Outputs/throw_errors3/consumes_err.swift.expected b/test/refactoring/ExtractFunction/Outputs/throw_errors3/consumes_err.swift.expected new file mode 100644 index 0000000000000..56ed142698b15 --- /dev/null +++ b/test/refactoring/ExtractFunction/Outputs/throw_errors3/consumes_err.swift.expected @@ -0,0 +1,18 @@ +enum Err : Error { + case wat +} + +func throwsSomething() throws { throw Err.wat } +func consumesErrClosure(_ fn: () throws -> Void) {} +func rethrowsErrClosure(_ fn: () throws -> Void) rethrows {} + +fileprivate func new_name() { +consumesErrClosure { throw Err.wat } + consumesErrClosure { try throwsSomething() } +} + +func testThrowingClosure() throws { + new_name() + try rethrowsErrClosure { try throwsSomething() } +} + diff --git a/test/refactoring/ExtractFunction/Outputs/throw_errors3/rethrows_err.swift.expected b/test/refactoring/ExtractFunction/Outputs/throw_errors3/rethrows_err.swift.expected new file mode 100644 index 0000000000000..a72a0dc1f3b41 --- /dev/null +++ b/test/refactoring/ExtractFunction/Outputs/throw_errors3/rethrows_err.swift.expected @@ -0,0 +1,18 @@ +enum Err : Error { + case wat +} + +func throwsSomething() throws { throw Err.wat } +func consumesErrClosure(_ fn: () throws -> Void) {} +func rethrowsErrClosure(_ fn: () throws -> Void) rethrows {} + +fileprivate func new_name() throws { +consumesErrClosure { throw Err.wat } + consumesErrClosure { try throwsSomething() } + try rethrowsErrClosure { try throwsSomething() } +} + +func testThrowingClosure() throws { + try new_name() +} + diff --git a/test/refactoring/ExtractFunction/await.swift b/test/refactoring/ExtractFunction/await.swift new file mode 100644 index 0000000000000..760938e81c064 --- /dev/null +++ b/test/refactoring/ExtractFunction/await.swift @@ -0,0 +1,18 @@ +func longLongLongJourney() async -> Int { 0 } +func longLongLongAwryJourney() async throws -> Int { 0 } +func consumesAsync(_ fn: () async throws -> Void) rethrows {} + +func testThrowingClosure() async throws -> Int { + let x = await longLongLongJourney() + let y = try await longLongLongAwryJourney() + 1 + try consumesAsync { try await longLongLongAwryJourney() } + return x + y +} + +// RUN: %empty-directory(%t.result) +// RUN: %refactor -extract-function -source-filename %s -pos=6:11 -end-pos=6:38 >> %t.result/async1.swift +// RUN: diff -u %S/Outputs/await/async1.swift.expected %t.result/async1.swift +// RUN: %refactor -extract-function -source-filename %s -pos=7:11 -end-pos=7:50 >> %t.result/async2.swift +// RUN: diff -u %S/Outputs/await/async2.swift.expected %t.result/async2.swift +// RUN: %refactor -extract-function -source-filename %s -pos=8:1 -end-pos=8:60 >> %t.result/consumes_async.swift +// RUN: diff -u %S/Outputs/await/consumes_async.swift.expected %t.result/consumes_async.swift diff --git a/test/refactoring/ExtractFunction/throw_errors3.swift b/test/refactoring/ExtractFunction/throw_errors3.swift new file mode 100644 index 0000000000000..9b727c854f81e --- /dev/null +++ b/test/refactoring/ExtractFunction/throw_errors3.swift @@ -0,0 +1,19 @@ +enum Err : Error { + case wat +} + +func throwsSomething() throws { throw Err.wat } +func consumesErrClosure(_ fn: () throws -> Void) {} +func rethrowsErrClosure(_ fn: () throws -> Void) rethrows {} + +func testThrowingClosure() throws { + consumesErrClosure { throw Err.wat } + consumesErrClosure { try throwsSomething() } + try rethrowsErrClosure { try throwsSomething() } +} + +// RUN: %empty-directory(%t.result) +// RUN: %refactor -extract-function -source-filename %s -pos=10:1 -end-pos=11:47 >> %t.result/consumes_err.swift +// RUN: diff -u %S/Outputs/throw_errors3/consumes_err.swift.expected %t.result/consumes_err.swift +// RUN: %refactor -extract-function -source-filename %s -pos=10:1 -end-pos=12:51 >> %t.result/rethrows_err.swift +// RUN: diff -u %S/Outputs/throw_errors3/rethrows_err.swift.expected %t.result/rethrows_err.swift diff --git a/test/refactoring/ExtractRepeat/Outputs/await/one.swift.expected b/test/refactoring/ExtractRepeat/Outputs/await/one.swift.expected new file mode 100644 index 0000000000000..91574571e902f --- /dev/null +++ b/test/refactoring/ExtractRepeat/Outputs/await/one.swift.expected @@ -0,0 +1,13 @@ +func myAsync() async -> Int { 0 } +func myFoo() -> Int { 0 } + +func testExtract() async -> Int { + let new_name = myFoo() +let a = new_name + let b = new_name + let x = await myAsync() + let y = await myAsync() + return a + b + x + y +} + +// rdar://72199992 diff --git a/test/refactoring/ExtractRepeat/Outputs/await/two.swift.expected b/test/refactoring/ExtractRepeat/Outputs/await/two.swift.expected new file mode 100644 index 0000000000000..f89995a508ca7 --- /dev/null +++ b/test/refactoring/ExtractRepeat/Outputs/await/two.swift.expected @@ -0,0 +1,13 @@ +func myAsync() async -> Int { 0 } +func myFoo() -> Int { 0 } + +func testExtract() async -> Int { + let a = myFoo() + let b = myFoo() + let new_name = await myAsync() +let x = new_name + let y = new_name + return a + b + x + y +} + +// rdar://72199992 diff --git a/test/refactoring/ExtractRepeat/await.swift b/test/refactoring/ExtractRepeat/await.swift new file mode 100644 index 0000000000000..57c0f9aca5271 --- /dev/null +++ b/test/refactoring/ExtractRepeat/await.swift @@ -0,0 +1,17 @@ +func myAsync() async -> Int { 0 } +func myFoo() -> Int { 0 } + +func testExtract() async -> Int { + let a = myFoo() + let b = myFoo() + let x = await myAsync() + let y = await myAsync() + return a + b + x + y +} + +// rdar://72199992 +// RUN: %empty-directory(%t.result) +// RUN: %refactor -extract-repeat -source-filename %s -pos=5:11 -end-pos=5:18 >> %t.result/one.swift +// RUN: diff -u %S/Outputs/await/one.swift.expected %t.result/one.swift +// RUN: %refactor -extract-repeat -source-filename %s -pos=7:11 -end-pos=7:26 >> %t.result/two.swift +// RUN: diff -u %S/Outputs/await/two.swift.expected %t.result/two.swift diff --git a/test/refactoring/TrailingClosure/rdar81106400.swift b/test/refactoring/TrailingClosure/rdar81106400.swift new file mode 100644 index 0000000000000..61f9144ad24c5 --- /dev/null +++ b/test/refactoring/TrailingClosure/rdar81106400.swift @@ -0,0 +1,9 @@ +func lottaClosures(x: () -> Void, y: () -> Void, z: () -> Void) { + lottaClosures(x: {}) {} z: {} +} +// RUN: not %refactor -trailingclosure -source-filename %s -pos=2:3 + +func singleClosure(x: () -> Void) { + singleClosure {} +} +// RUN: not %refactor -trailingclosure -source-filename %s -pos=7:3 diff --git a/test/refactoring/rename/Outputs/local/captured_variable.swift.expected b/test/refactoring/rename/Outputs/local/captured_variable.swift.expected new file mode 100644 index 0000000000000..370abdb708b2f --- /dev/null +++ b/test/refactoring/rename/Outputs/local/captured_variable.swift.expected @@ -0,0 +1,57 @@ +func test1() { + if true { + let x = 1 + print(x) + } else { + let x = 2 + print(x) + } +} + +func test2(arg1: Int?, arg2: (Int, String)?) { + if let x = arg1 { + print(x) + } else if let (x, y) = arg2 { + print(x, y) + } +} + +func test3(arg: Int?) { + switch arg { + case let .some(x) where x == 0: + print(x) + case let .some(x) where x == 1, + let .some(x) where x == 2: + print(x) + fallthrough + case let .some(x) where x == 3: + print(x) + default: + break + } +} + +struct Err1 : Error { } +func test4(arg: () throws -> Void) { + do { + try arg() + } catch let x as Err1 { + print(x) + } catch let x { + print(x) + } +} + +func test5(_ x: Int) { + let x = x + print(x) +} + +func testCaputreVariable() { + let capturedVariableRenamed = 0 + + _ = { [capturedVariableRenamed] in + print(capturedVariableRenamed) + } +} + diff --git a/test/refactoring/rename/Outputs/local/casebind_1.swift.expected b/test/refactoring/rename/Outputs/local/casebind_1.swift.expected index 33730bd96d5ba..d490c430f6998 100644 --- a/test/refactoring/rename/Outputs/local/casebind_1.swift.expected +++ b/test/refactoring/rename/Outputs/local/casebind_1.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/casebind_2.swift.expected b/test/refactoring/rename/Outputs/local/casebind_2.swift.expected index 1bb1efeb1e13c..2e73b8aedc589 100644 --- a/test/refactoring/rename/Outputs/local/casebind_2.swift.expected +++ b/test/refactoring/rename/Outputs/local/casebind_2.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/catch_1.swift.expected b/test/refactoring/rename/Outputs/local/catch_1.swift.expected index 1aea4f8736ff3..456ef475a2113 100644 --- a/test/refactoring/rename/Outputs/local/catch_1.swift.expected +++ b/test/refactoring/rename/Outputs/local/catch_1.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/catch_2.swift.expected b/test/refactoring/rename/Outputs/local/catch_2.swift.expected index 4a54528a0f1d2..8b07d5e677c01 100644 --- a/test/refactoring/rename/Outputs/local/catch_2.swift.expected +++ b/test/refactoring/rename/Outputs/local/catch_2.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/ifbind_1.swift.expected b/test/refactoring/rename/Outputs/local/ifbind_1.swift.expected index 51bc18a5e75fc..20ac84bc5cfbf 100644 --- a/test/refactoring/rename/Outputs/local/ifbind_1.swift.expected +++ b/test/refactoring/rename/Outputs/local/ifbind_1.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/ifbind_2.swift.expected b/test/refactoring/rename/Outputs/local/ifbind_2.swift.expected index dfdc99cfa2f49..b1da11db2c21a 100644 --- a/test/refactoring/rename/Outputs/local/ifbind_2.swift.expected +++ b/test/refactoring/rename/Outputs/local/ifbind_2.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/localvar_1.swift.expected b/test/refactoring/rename/Outputs/local/localvar_1.swift.expected index e115478a22f4a..c34208b8bf305 100644 --- a/test/refactoring/rename/Outputs/local/localvar_1.swift.expected +++ b/test/refactoring/rename/Outputs/local/localvar_1.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/localvar_2.swift.expected b/test/refactoring/rename/Outputs/local/localvar_2.swift.expected index f7724b61d029e..900f57c7c5601 100644 --- a/test/refactoring/rename/Outputs/local/localvar_2.swift.expected +++ b/test/refactoring/rename/Outputs/local/localvar_2.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/param_1.swift.expected b/test/refactoring/rename/Outputs/local/param_1.swift.expected index baa0daa44182b..c44b9ecfffd68 100644 --- a/test/refactoring/rename/Outputs/local/param_1.swift.expected +++ b/test/refactoring/rename/Outputs/local/param_1.swift.expected @@ -47,3 +47,11 @@ func test5(_ xRenamed: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/Outputs/local/param_2.swift.expected b/test/refactoring/rename/Outputs/local/param_2.swift.expected index bc24db707b091..f90e802d80c88 100644 --- a/test/refactoring/rename/Outputs/local/param_2.swift.expected +++ b/test/refactoring/rename/Outputs/local/param_2.swift.expected @@ -47,3 +47,11 @@ func test5(_ x: Int) { print(xRenamed) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + diff --git a/test/refactoring/rename/local.swift b/test/refactoring/rename/local.swift index 393c7b06cd1f4..b91faa2cd07b8 100644 --- a/test/refactoring/rename/local.swift +++ b/test/refactoring/rename/local.swift @@ -47,6 +47,14 @@ func test5(_ x: Int) { print(x) } +func testCaputreVariable() { + let capturedVariable = 0 + + _ = { [capturedVariable] in + print(capturedVariable) + } +} + // RUN: %empty-directory(%t.result) // RUN: %refactor -rename -source-filename %s -pos=3:9 -new-name="xRenamed" >> %t.result/localvar_1.swift // RUN: %refactor -rename -source-filename %s -pos=7:11 -new-name="xRenamed" >> %t.result/localvar_2.swift @@ -68,3 +76,5 @@ func test5(_ x: Int) { // RUN: %refactor -rename -source-filename %s -pos=47:9 -new-name="xRenamed" >> %t.result/param_2.swift // RUN: diff -u %S/Outputs/local/param_1.swift.expected %t.result/param_1.swift // RUN: diff -u %S/Outputs/local/param_2.swift.expected %t.result/param_2.swift +// RUN: %refactor -rename -source-filename %s -pos=51:7 -new-name="capturedVariableRenamed" >> %t.result/captured_variable.swift +// RUN: diff -u %S/Outputs/local/captured_variable.swift.expected %t.result/captured_variable.swift diff --git a/test/stdlib/ArrayBridge.swift.gyb b/test/stdlib/ArrayBridge.swift.gyb index 6e02d9bd8c448..cfbbdf65697a9 100644 --- a/test/stdlib/ArrayBridge.swift.gyb +++ b/test/stdlib/ArrayBridge.swift.gyb @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2021 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 @@ -471,6 +471,8 @@ tests.test("testMutableArray") { } tests.test("rdar://problem/27905230") { + // Casting an NSArray to Array would trap because of an erroneous + // precondition. let dict = RDar27905230.mutableDictionaryOfMutableLists()! let arr = dict["list"]! expectEqual(arr[0] as! NSNull, NSNull()) @@ -482,4 +484,69 @@ tests.test("rdar://problem/27905230") { expectEqual(arr[5] as! Date, Date(timeIntervalSince1970: 0)) } +tests.test("verbatimBridged/Base/withUnsafeBufferPointer") { + let a = NSArray(array: [Base(0), Base(1), Base(2), Base(3)]) + let b = a as! [Base] + let success: Bool = b.withUnsafeBufferPointer { buffer in + expectEqual(buffer.count, 4) + guard buffer.count == 4 else { return false } + expectEqual(buffer[0].value, 0) + expectEqual(buffer[1].value, 1) + expectEqual(buffer[2].value, 2) + expectEqual(buffer[3].value, 3) + return true + } + expectTrue(success) +} + +// https://bugs.swift.org/browse/SR-14663 +tests.test("verbatimBridged/AnyObject/withUnsafeBufferPointer") { + let a = NSArray(array: [Base(0), Base(1), Base(2), Base(3)]) + let b = a as [AnyObject] + let success: Bool = b.withUnsafeBufferPointer { buffer in + expectEqual(buffer.count, 4) + guard buffer.count == 4 else { return false } + expectEqual((buffer[0] as? Base)?.value, 0) + expectEqual((buffer[1] as? Base)?.value, 1) + expectEqual((buffer[2] as? Base)?.value, 2) + expectEqual((buffer[3] as? Base)?.value, 3) + return true + } + expectTrue(success) +} + +tests.test("verbatimBridged/Base/withUnsafeMutableBufferPointer") { + let a = NSArray(array: [Base(0), Base(1), Base(2), Base(3)]) + var b = a as! [Base] + let success: Bool = b.withUnsafeMutableBufferPointer { buffer in + expectEqual(buffer.count, 4) + guard buffer.count == 4 else { return false } + expectEqual(buffer[0].value, 0) + expectEqual(buffer[1].value, 1) + expectEqual(buffer[2].value, 2) + expectEqual(buffer[3].value, 3) + buffer[0] = Base(4) + return true + } + expectTrue(success) + expectEqual(b[0].value, 4) +} + +tests.test("verbatimBridged/AnyObject/withUnsafeMutableBufferPointer") { + let a = NSArray(array: [Base(0), Base(1), Base(2), Base(3)]) + var b = a as [AnyObject] + let success: Bool = b.withUnsafeMutableBufferPointer { buffer in + expectEqual(buffer.count, 4) + guard buffer.count == 4 else { return false } + expectEqual((buffer[0] as? Base)?.value, 0) + expectEqual((buffer[1] as? Base)?.value, 1) + expectEqual((buffer[2] as? Base)?.value, 2) + expectEqual((buffer[3] as? Base)?.value, 3) + buffer[0] = Base(4) + return true + } + expectTrue(success) + expectEqual((b[0] as? Base)?.value, 4) +} + runAllTests() diff --git a/test/stdlib/ArrayBuffer_CopyContents.swift b/test/stdlib/ArrayBuffer_CopyContents.swift new file mode 100644 index 0000000000000..99a9539efd4e4 --- /dev/null +++ b/test/stdlib/ArrayBuffer_CopyContents.swift @@ -0,0 +1,80 @@ +//===--- ArrayBuffer_CopyContents.swift -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +// RUN: %target-run-simple-swift +// REQUIRES: executable_test +// REQUIRES: swift_stdlib_asserts +// REQUIRES: foundation + +import Foundation +import StdlibUnittest + +let suite = TestSuite("ArrayBuffer_CopyContents") +defer { runAllTests() } + + +var trackedCount = 0 +var nextBaseSerialNumber = 0 + +/// A type that will be bridged verbatim to Objective-C +class Thing: NSObject { + var value: Int + var serialNumber: Int + + func foo() { } + + required init(_ value: Int) { + trackedCount += 1 + nextBaseSerialNumber += 1 + serialNumber = nextBaseSerialNumber + self.value = value + } + + deinit { + assert(serialNumber > 0, "double destruction!") + trackedCount -= 1 + serialNumber = -serialNumber + } + + override func isEqual(_ other: Any?) -> Bool { + return (other as? Thing)?.value == self.value + } + + override var hash: Int { value } +} + + +suite.test("nativeArray/_copyContents") { + let array = [Thing(0), Thing(1), Thing(2), Thing(3)] + expectEqualSequence(array._copyToNewArray(), array) +} + +suite.test("nativeArraySlice/_copyContents") { + let array = (0 ..< 100).map { Thing($0) } + expectEqualSequence( + array[20 ..< 30]._copyToNewArray(), + (20 ..< 30).map { Thing($0) }) +} + +suite.test("bridgedArray/_copyContents") { + let array = NSArray(array: (0 ..< 5).map { Thing($0) }) as! [Thing] + expectEqualSequence( + array._copyToNewArray(), + (0 ..< 5).map { Thing($0) }) +} + +suite.test("bridgedArraySlice/_copyContents") { + let array = NSArray(array: (0 ..< 100).map { Thing($0) }) as! [Thing] + expectEqualSequence( + array[20 ..< 30]._copyToNewArray(), + (20 ..< 30).map { Thing($0) }) +} diff --git a/test/stdlib/Concurrency.swift b/test/stdlib/Concurrency.swift index c8e7ff03524e4..128f2e6eba2d9 100644 --- a/test/stdlib/Concurrency.swift +++ b/test/stdlib/Concurrency.swift @@ -6,9 +6,11 @@ import _Concurrency // Make sure the type shows up (including under its old name, for // short-term source compatibility) -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension PartialAsyncTask { + // expected-warning@-1 {{'PartialAsyncTask' is deprecated: renamed to 'UnownedJob'}} + // expected-note@-2 {{use 'UnownedJob' instead}} } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) extension UnownedJob { } diff --git a/test/stdlib/DefaultIndices.swift b/test/stdlib/DefaultIndices.swift index 87797b026ca37..5238599670b4d 100644 --- a/test/stdlib/DefaultIndices.swift +++ b/test/stdlib/DefaultIndices.swift @@ -23,7 +23,7 @@ extension Collection { } suite.test("Bidirectional dispatch") { - guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { + guard #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) else { // This used to cause a runtime trap until https://github.com/apple/swift/pull/32019 return } diff --git a/test/stdlib/Dispatch.swift b/test/stdlib/Dispatch.swift deleted file mode 100644 index e09f4a9e7abe0..0000000000000 --- a/test/stdlib/Dispatch.swift +++ /dev/null @@ -1,294 +0,0 @@ -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: libdispatch -// UNSUPPORTED: OS=linux-gnu -// UNSUPPORTED: OS=linux-android -// UNSUPPORTED: OS=openbsd - -import Dispatch -import StdlibUnittest - - -defer { runAllTests() } - -var DispatchAPI = TestSuite("DispatchAPI") - -DispatchAPI.test("constants") { - expectEqual(2147483648, DispatchSource.ProcessEvent.exit.rawValue) - expectEqual(0, DispatchData.empty.endIndex) - - // This is a lousy test, but really we just care that - // DISPATCH_QUEUE_CONCURRENT comes through at all. - _ = DispatchQueue.Attributes.concurrent -} - -DispatchAPI.test("OS_OBJECT support") { - let mainQueue = DispatchQueue.main as AnyObject - expectTrue(mainQueue is DispatchQueue) - - // This should not be optimized out, and should succeed. - expectNotNil(mainQueue as? DispatchQueue) -} - -DispatchAPI.test("DispatchGroup creation") { - let group = DispatchGroup() - expectNotNil(group) -} - -DispatchAPI.test("Dispatch sync return value") { - let value = 24 - let q = DispatchQueue(label: "Test") - let result = q.sync() { return 24 } - expectEqual(value, result) -} - -DispatchAPI.test("dispatch_block_t conversions") { - var counter = 0 - let closure = { () -> Void in - counter += 1 - } - - typealias Block = @convention(block) () -> () - let block = closure as Block - block() - expectEqual(1, counter) - - let closureAgain = block as () -> Void - closureAgain() - expectEqual(2, counter) -} - -if #available(OSX 10.10, iOS 8.0, *) { - DispatchAPI.test("dispatch_block_t identity") { - let block = DispatchWorkItem(flags: .inheritQoS) { - _ = 1 - } - - DispatchQueue.main.async(execute: block) - // This will trap if the block's pointer identity is not preserved. - block.cancel() - } -} - -DispatchAPI.test("DispatchTime comparisons") { - do { - let now = DispatchTime.now() - checkComparable([now, now + .milliseconds(1), .distantFuture], oracle: { - return $0 < $1 ? .lt : $0 == $1 ? .eq : .gt - }) - } - - do { - let now = DispatchWallTime.now() - checkComparable([now, now + .milliseconds(1), .distantFuture], oracle: { - return $0 < $1 ? .lt : $0 == $1 ? .eq : .gt - }) - } -} - -DispatchAPI.test("DispatchTime.create") { - var info = mach_timebase_info_data_t(numer: 1, denom: 1) - mach_timebase_info(&info) - let scales = info.numer != info.denom - - // Simple tests for non-overflow behavior - var time = DispatchTime(uptimeNanoseconds: 0) - expectEqual(time.uptimeNanoseconds, 0) - - time = DispatchTime(uptimeNanoseconds: 15 * NSEC_PER_SEC) - expectEqual(time.uptimeNanoseconds, 15 * NSEC_PER_SEC) - - // On platforms where the timebase scale is not 1, the next two cases - // overflow and become DISPATCH_TIME_FOREVER (UInt64.max) instead of trapping. - time = DispatchTime(uptimeNanoseconds: UInt64.max - 1) - expectEqual(time.uptimeNanoseconds, scales ? UInt64.max : UInt64.max - UInt64(1)) - - time = DispatchTime(uptimeNanoseconds: UInt64.max / 2) - expectEqual(time.uptimeNanoseconds, scales ? UInt64.max : UInt64.max / 2) - - // UInt64.max must always be returned as UInt64.max. - time = DispatchTime(uptimeNanoseconds: UInt64.max) - expectEqual(time.uptimeNanoseconds, UInt64.max) -} - -DispatchAPI.test("DispatchTime.addSubtract") { - var then = DispatchTime.now() + Double.infinity - expectEqual(DispatchTime.distantFuture, then) - - then = DispatchTime.now() + Double.nan - expectEqual(DispatchTime.distantFuture, then) - - then = DispatchTime.now() - Double.infinity - expectEqual(DispatchTime(uptimeNanoseconds: 1), then) - - then = DispatchTime.now() - Double.nan - expectEqual(DispatchTime.distantFuture, then) -} - -DispatchAPI.test("DispatchWallTime.addSubtract") { - let distantPastRawValue = DispatchWallTime.distantFuture.rawValue - UInt64(1) - - var then = DispatchWallTime.now() + Double.infinity - expectEqual(DispatchWallTime.distantFuture, then) - - then = DispatchWallTime.now() + Double.nan - expectEqual(DispatchWallTime.distantFuture, then) - - then = DispatchWallTime.now() - Double.infinity - expectEqual(distantPastRawValue, then.rawValue) - - then = DispatchWallTime.now() - Double.nan - expectEqual(DispatchWallTime.distantFuture, then) -} - -DispatchAPI.test("DispatchTime.uptimeNanos") { - let seconds = 1 - let nowMach = DispatchTime.now() - let oneSecondFromNowMach = nowMach + .seconds(seconds) - let nowNanos = nowMach.uptimeNanoseconds - let oneSecondFromNowNanos = oneSecondFromNowMach.uptimeNanoseconds - let diffNanos = oneSecondFromNowNanos - nowNanos - expectEqual(NSEC_PER_SEC, diffNanos) -} - -DispatchAPI.test("DispatchIO.initRelativePath") { - let q = DispatchQueue(label: "initRelativePath queue") - let chan = DispatchIO(type: .random, path: "_REL_PATH_", oflag: O_RDONLY, mode: 0, queue: q, cleanupHandler: { (error) in }) - expectEqual(chan, nil) -} - -if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) { - var block = DispatchWorkItem(qos: .unspecified, flags: .assignCurrentContext) {} - DispatchAPI.test("DispatchSource.replace") { - let g = DispatchGroup() - let q = DispatchQueue(label: "q") - let ds = DispatchSource.makeUserDataReplaceSource(queue: q) - var lastValue = UInt(0) - var nextValue = UInt(1) - let maxValue = UInt(1 << 24) - - ds.setEventHandler() { - let value = ds.data; - expectTrue(value > lastValue) // Values must increase - expectTrue((value & (value - 1)) == 0) // Must be power of two - lastValue = value - if value == maxValue { - g.leave() - } - } - ds.activate() - - g.enter() - block = DispatchWorkItem(qos: .unspecified, flags: .assignCurrentContext) { - ds.replace(data: nextValue) - nextValue <<= 1 - if nextValue <= maxValue { - q.asyncAfter( - deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(1), - execute: block) - } - } - q.asyncAfter( - deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(1), - execute: block) - - let result = g.wait(timeout: DispatchTime.now() + .seconds(30)) - expectTrue(result == .success) - } -} - -DispatchAPI.test("DispatchTimeInterval") { - // Basic tests that the correct value is stored and the == method works - for i in stride(from:1, through: 100, by: 5) { - expectEqual(DispatchTimeInterval.seconds(i), DispatchTimeInterval.milliseconds(i * 1000)) - expectEqual(DispatchTimeInterval.milliseconds(i), DispatchTimeInterval.microseconds(i * 1000)) - expectEqual(DispatchTimeInterval.microseconds(i), DispatchTimeInterval.nanoseconds(i * 1000)) - } - - - // Check some cases that used to cause arithmetic overflow when evaluating the rawValue for == - var t = DispatchTimeInterval.seconds(Int.max) - expectTrue(t == t) // This would crash. - - t = DispatchTimeInterval.seconds(-Int.max) - expectTrue(t == t) // This would crash. - - t = DispatchTimeInterval.milliseconds(Int.max) - expectTrue(t == t) // This would crash. - - t = DispatchTimeInterval.milliseconds(-Int.max) - expectTrue(t == t) // This would crash. - - t = DispatchTimeInterval.microseconds(Int.max) - expectTrue(t == t) // This would crash. - - t = DispatchTimeInterval.microseconds(-Int.max) - expectTrue(t == t) // This would crash. -} - -DispatchAPI.test("DispatchTimeInterval.never.equals") { - expectTrue(DispatchTimeInterval.never == DispatchTimeInterval.never) - expectTrue(DispatchTimeInterval.seconds(10) != DispatchTimeInterval.never); - expectTrue(DispatchTimeInterval.never != DispatchTimeInterval.seconds(10)); - expectTrue(DispatchTimeInterval.seconds(10) == DispatchTimeInterval.seconds(10)); -} - -// Only support 64bit -#if !(os(iOS) && (arch(i386) || arch(arm))) - -import Combine - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -private func clampedIntProduct(_ m1: Int, _ m2: UInt64) -> Int { - assert(m2 > 0, "multiplier must be positive") - guard m1 < Int.max, m2 < Int.max else { return Int.max } - let (result, overflow) = m1.multipliedReportingOverflow(by: Int(m2)) - if overflow { - return m1 > 0 ? Int.max : Int.min - } - return result -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension DispatchTimeInterval { - fileprivate var nanoseconds: Int { - switch self { - case .seconds(let s): return clampedIntProduct(s, NSEC_PER_SEC) - case .milliseconds(let ms): return clampedIntProduct(ms, NSEC_PER_MSEC) - case .microseconds(let us): return clampedIntProduct(us, NSEC_PER_USEC) - case .nanoseconds(let ns): return ns - case .never: return Int.max - } - } -} - -DispatchAPI.test("DispatchTime.SchedulerTimeType.Stridable") { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { - // Basic checks for time types - for i in stride(from:1, through: 100, by: 5) { - let time1 = DispatchTime(uptimeNanoseconds: UInt64(i)) - let time2 = DispatchTime(uptimeNanoseconds: UInt64(i + 1)) - let schedulerTime1 = DispatchQueue.SchedulerTimeType(time1) - let schedulerTime2 = DispatchQueue.SchedulerTimeType(time2) - let addedTime = time2.distance(to: time1) - let addedSchedulerTime = schedulerTime2.distance(to: schedulerTime1) - expectEqual(addedTime.nanoseconds, addedSchedulerTime.magnitude) - } - - - let time1 = DispatchQueue.SchedulerTimeType(.init(uptimeNanoseconds: 10000)) - let time2 = DispatchQueue.SchedulerTimeType(.init(uptimeNanoseconds: 10431)) - let addedTime = time2.distance(to: time1) - // The magnitude of the time sum is in nanosecond units. Although - // the units match up, the internal representation of the - // DispatchTime may round up to multiples of Mach timebase. This - // requires the difference being converted, which is performed here - // by constructing a `DispatchTime` from the delta. However, the - // parameter is a `UInt64` which requires us to perform the negation - // manually. - expectEqual(-Int(DispatchTime(uptimeNanoseconds: 10431 - 10000).uptimeNanoseconds), - addedTime.magnitude) - } -} - -#endif diff --git a/test/stdlib/DispatchDate.swift b/test/stdlib/DispatchDate.swift deleted file mode 100644 index 0c76441d6a583..0000000000000 --- a/test/stdlib/DispatchDate.swift +++ /dev/null @@ -1,42 +0,0 @@ -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: libdispatch -// REQUIRES: foundation - -import Dispatch -import Foundation -import StdlibUnittest - -var DispatchAPI = TestSuite("DispatchAPI") - -DispatchAPI.test("DispatchTime.addSubtractDateConstants") { - var then = DispatchTime.now() + Date.distantFuture.timeIntervalSinceNow - expectEqual(DispatchTime(uptimeNanoseconds: UInt64.max), then) - - then = DispatchTime.now() + Date.distantPast.timeIntervalSinceNow - expectEqual(DispatchTime(uptimeNanoseconds: 1), then) - - then = DispatchTime.now() - Date.distantFuture.timeIntervalSinceNow - expectEqual(DispatchTime(uptimeNanoseconds: 1), then) - - then = DispatchTime.now() - Date.distantPast.timeIntervalSinceNow - expectEqual(DispatchTime(uptimeNanoseconds: UInt64.max), then) -} - -DispatchAPI.test("DispatchWallTime.addSubtractDateConstants") { - let distantPastRawValue = DispatchWallTime.distantFuture.rawValue - UInt64(1) - - var then = DispatchWallTime.now() + Date.distantFuture.timeIntervalSinceNow - expectEqual(DispatchWallTime.distantFuture, then) - - then = DispatchWallTime.now() + Date.distantPast.timeIntervalSinceNow - expectEqual(distantPastRawValue, then.rawValue) - - then = DispatchWallTime.now() - Date.distantFuture.timeIntervalSinceNow - expectEqual(distantPastRawValue, then.rawValue) - - then = DispatchWallTime.now() - Date.distantPast.timeIntervalSinceNow - expectEqual(DispatchWallTime.distantFuture, then) -} - -runAllTests() diff --git a/test/stdlib/Error.swift b/test/stdlib/Error.swift index e0b8859e1504a..231dc0dd5e6c5 100644 --- a/test/stdlib/Error.swift +++ b/test/stdlib/Error.swift @@ -11,7 +11,7 @@ func shouldCheckErrorLocation() -> Bool { guard _isDebugAssertConfiguration() else { return false } // The runtime error location format changed after the 5.3 release. // (https://github.com/apple/swift/pull/34665) - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) { return true } else { return false diff --git a/test/stdlib/ForEachField.swift b/test/stdlib/ForEachField.swift index e80375fafbd8b..037137e41ce40 100644 --- a/test/stdlib/ForEachField.swift +++ b/test/stdlib/ForEachField.swift @@ -162,7 +162,7 @@ func checkFields( expectEqual(fields.count, count) } -@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) +@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func checkFieldsWithKeyPath( of type: T.Type, options: _EachFieldOptions = [], @@ -298,7 +298,7 @@ if #available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) { }) } - if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { tests.test("StructKeyPath") { checkFieldsWithKeyPath( of: TestStruct.self, diff --git a/test/stdlib/Inputs/ArrayTypesAndHelpers.swift b/test/stdlib/Inputs/ArrayTypesAndHelpers.swift deleted file mode 100644 index 571bea6f47c1c..0000000000000 --- a/test/stdlib/Inputs/ArrayTypesAndHelpers.swift +++ /dev/null @@ -1,718 +0,0 @@ -import Swift -import SwiftPrivate -import StdlibUnittest -#if _runtime(_ObjC) -import Darwin -import Foundation -#endif - - -#if _runtime(_ObjC) - -/// Expect some number of autoreleased value objects. -/// -/// - parameter opt: applies to platforms that have the return-autoreleased -/// optimization. -/// -/// - parameter unopt: applies to platforms that don't. -/// -/// FIXME: Some non-zero `opt` might be cases of missed return-autorelease. -func expectAutoreleasedValues( - opt: Int = 0, unopt: Int = 0) { - _expectAutoreleasedKeysAndValues(opt: (0, opt), unopt: (0, unopt)) -} - -func isNativeNSArray(_ d: NSArray) -> Bool { - let className: NSString = NSStringFromClass(type(of: d)) as NSString - return ["__SwiftDeferredNSArray", "_ContiguousArray", "_EmptyArray"].contains { - className.range(of: $0).length > 0 - } -} - -func isCocoaNSArray(_ a: NSArray) -> Bool { - let className: NSString = NSStringFromClass(type(of: a)) as NSString - return className.range(of: "NSArray").length > 0 || - className.range(of: "NSCFArray").length > 0 -} - -func getBridgedNSArrayOfRefTypesBridgedVerbatim() -> NSArray { - expectTrue(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self)) - - var a = Array() - a.reserveCapacity(32) - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - let bridged = convertArrayToNSArray(a) - - assert(isNativeNSArray(bridged)) - - return bridged -} - -func getBridgedEmptyNSArray() -> NSArray { - let a = Array() - - let bridged = convertArrayToNSArray(a) - assert(isNativeNSArray(bridged)) - - return bridged -} - -#endif - -func getDerivedAPIsArray() -> Array { - var a = Array() - a.append(1010) - a.append(1020) - a.append(1030) - return a -} - -func _makeExpectedArrayContents( - _ expected: [Int] -) -> [ExpectedArrayElement] { - var result = [ExpectedArrayElement]() - for value in expected { - result.append(ExpectedArrayElement(value: value)) - } - return result -} - -func _equalsWithoutElementIdentity( - _ lhs: [ExpectedArrayElement], _ rhs: [ExpectedArrayElement] -) -> Bool { - func stripIdentity( - _ list: [ExpectedArrayElement] - ) -> [ExpectedArrayElement] { - return list.map { ExpectedArrayElement(value: $0.value) } - } - - return stripIdentity(lhs).elementsEqual(stripIdentity(rhs)) -} - -struct ExpectedArrayElement : Comparable, CustomStringConvertible { - var value: Int - var valueIdentity: UInt - - init(value: Int, valueIdentity: UInt = 0) { - self.value = value - self.valueIdentity = valueIdentity - } - - var description: String { - return "(\(value), \(valueIdentity))" - } -} - -func == ( - lhs: ExpectedArrayElement, - rhs: ExpectedArrayElement -) -> Bool { - return - lhs.value == rhs.value && - lhs.valueIdentity == rhs.valueIdentity -} - -func < ( - lhs: ExpectedArrayElement, - rhs: ExpectedArrayElement -) -> Bool { - let lhsElements = [ lhs.value, Int(bitPattern: lhs.valueIdentity) ] - let rhsElements = [ rhs.value, Int(bitPattern: rhs.valueIdentity) ] - return lhsElements.lexicographicallyPrecedes(rhsElements) -} - -#if _runtime(_ObjC) - func _checkArrayFastEnumerationImpl( - _ expected: [Int], - _ a: NSArray, - _ makeEnumerator: () -> NSFastEnumeration, - _ useEnumerator: (NSArray, NSFastEnumeration, (AnyObject) -> ()) -> Void, - _ convertValue: @escaping (AnyObject) -> Int -) { - let expectedContentsWithoutIdentity = - _makeExpectedArrayContents(expected) - - var expectedContents = [ExpectedArrayElement]() - - for i in 0..<3 { - var actualContents = [ExpectedArrayElement]() - let sink = { - (value: AnyObject) in - actualContents.append(ExpectedArrayElement( - value: convertValue(value), - valueIdentity: unsafeBitCast(value, to: UInt.self))) - } - - useEnumerator(a, makeEnumerator(), sink) - - expectTrue( - _equalsWithoutElementIdentity( - expectedContentsWithoutIdentity, actualContents), - "expected: \(expectedContentsWithoutIdentity)\n" + - "actual: \(actualContents)\n") - - if i == 0 { - expectedContents = actualContents - } - - expectEqualSequence(expectedContents, actualContents) - } -} -#endif - -func getCOWFastArray() -> Array { - var a = Array() - a.reserveCapacity(10) - a.append(1) - a.append(2) - a.append(3) - return a -} - -func getCOWSlowArray() -> Array> { - var a = Array>() - a.reserveCapacity(10) - a.append(COWBox(1)) - a.append(COWBox(2)) - a.append(COWBox(3)) - return a -} - -#if _runtime(_ObjC) - func slurpFastEnumerationFromObjC( - _ a: NSArray, _ fe: NSFastEnumeration, _ sink: (AnyObject) -> Void -) { - let objcValues = NSMutableArray() - slurpFastEnumerationOfArrayFromObjCImpl(a, fe, objcValues) - for value in objcValues { - sink(value as AnyObject) - } -} - -import SlurpFastEnumeration - - func slurpFastEnumerationFromSwift( - _ a: NSArray, _ fe: NSFastEnumeration, _ sink: (AnyObject) -> Void, - maxItems: Int? = nil -) { - var state = NSFastEnumerationState() - - let bufferSize = 3 - let buffer = - UnsafeMutableBufferPointer.allocate(capacity: bufferSize) - defer { buffer.deallocate() } - - var itemsReturned = 0 - while true { - let returnedCount = fe.countByEnumerating( - with: &state, - objects: AutoreleasingUnsafeMutablePointer(buffer.baseAddress!), - count: buffer.count) - expectNotEqual(0, state.state) - expectNotNil(state.mutationsPtr) - if returnedCount == 0 { - break - } - for i in 0..= maxItems! { - return - } - } - - for _ in 0..<3 { - let returnedCount = fe.countByEnumerating( - with: &state, - objects: AutoreleasingUnsafeMutablePointer(buffer.baseAddress!), - count: buffer.count) - expectNotEqual(0, state.state) - expectNotNil(state.mutationsPtr) - expectEqual(0, returnedCount) - } -} - - func checkArrayFastEnumerationFromSwift( - _ expected: [Int], - _ a: NSArray, _ makeEnumerator: () -> NSFastEnumeration, - _ convertValue: @escaping (AnyObject) -> Int -) { - _checkArrayFastEnumerationImpl( - expected, a, makeEnumerator, - { (a, fe, sink) in - slurpFastEnumerationFromSwift(a, fe, sink) - }, - convertValue) -} - - func checkArrayFastEnumerationFromObjC( - _ expected: [Int], - _ a: NSArray, _ makeEnumerator: () -> NSFastEnumeration, - _ convertValue: @escaping (AnyObject) -> Int -) { - _checkArrayFastEnumerationImpl( - expected, a, makeEnumerator, - { (a, fe, sink) in - slurpFastEnumerationFromObjC(a, fe, sink) - }, - convertValue) -} - -func getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( - numElements: Int = 3 -) -> NSArray { - expectFalse(_isBridgedVerbatimToObjectiveC(TestBridgedValueTy.self)) - - var a = Array() - for i in 1..<(numElements + 1) { - a.append(TestBridgedValueTy(i * 10 + 1000)) - } - - let bridged = convertArrayToNSArray(a) - expectTrue(isNativeNSArray(bridged)) - - return bridged -} - - func checkArrayEnumeratorPartialFastEnumerationFromSwift( - _ expected: [Int], - _ a: NSArray, - maxFastEnumerationItems: Int, - _ convertValue: @escaping (AnyObject) -> Int -) { - _checkArrayFastEnumerationImpl( - expected, a, { a.objectEnumerator() }, - { (a, fe, sink) in - slurpFastEnumerationOfNSEnumeratorFromSwift( - a, fe as! NSEnumerator, sink, - maxFastEnumerationItems: maxFastEnumerationItems) - }, - convertValue) -} - - func slurpFastEnumerationOfNSEnumeratorFromSwift( - _ a: NSArray, _ enumerator: NSEnumerator, _ sink: (AnyObject) -> Void, - maxFastEnumerationItems: Int -) { - slurpFastEnumerationFromSwift( - a, enumerator, sink, maxItems: maxFastEnumerationItems) - while let value = enumerator.nextObject() { - sink(value as AnyObject) - } -} - -func _expectAutoreleasedKeysAndValues( - opt: (Int, Int) = (0, 0), unopt: (Int, Int) = (0, 0)) { - var expectedKeys = 0 - var expectedValues = 0 -#if arch(i386) - (expectedKeys, expectedValues) = unopt -#else - (expectedKeys, expectedValues) = opt -#endif - - TestObjCValueTy.objectCount -= expectedValues -} - -func isCocoaArray(_ a: Array) -> Bool { - return !isNativeArray(a) -} - -func isNativeArray(_ a: Array) -> Bool { - return a._buffer._storage.isNative -} - -func convertArrayToNSArray(_ a: [T]) -> NSArray { - return a._bridgeToObjectiveC() -} - -func convertNSArrayToArray(_ a: NSArray?) -> [T] { - if _slowPath(a == nil) { return [] } - var result: [T]? - Array._forceBridgeFromObjectiveC(a!, result: &result) - return result! -} - -func getAsNSArray(_ a: Array) -> NSArray { - let values = Array(a.map { TestObjCValueTy($0) }) - - // Return an `NSMutableArray` to make sure that it has a unique - // pointer identity. - let nsarray = NSMutableArray() - nsarray.addObjects(from: values) - return nsarray -} - -func getAsEquatableNSArray(_ a: Array) -> NSArray { - let values = Array(a.map { TestObjCEquatableValueTy($0) }) - - // Return an `NSMutableArray` to make sure that it has a unique - // pointer identity. - let nsarray = NSMutableArray() - nsarray.addObjects(from: values) - return nsarray -} - -func getAsNSMutableArray(_ a: Array) -> NSMutableArray { - let values = Array(a.map { TestObjCValueTy($0) }) - let nsarray = NSMutableArray() - nsarray.addObjects(from: values) - return nsarray -} - -func getBridgedVerbatimArrayAndNSMutableArray() - -> (Array, NSMutableArray) { - let nsa = getAsNSMutableArray([1010, 1020, 1030]) - return (convertNSArrayToArray(nsa), nsa) -} - -func getBridgedVerbatimArray() -> Array { - let nsa = getAsNSArray([1010, 1020, 1030]) - return convertNSArrayToArray(nsa) -} - -func getBridgedVerbatimArray(_ a: Array) -> Array { - let nsa = getAsNSArray(a) - return convertNSArrayToArray(nsa) -} - -func getBridgedNonverbatimArray() -> Array { - let nsa = getAsNSArray([1010, 1020, 1030 ]) - return Swift._forceBridgeFromObjectiveC(nsa, Array.self) -} - -func getBridgedNonverbatimArray(_ a: Array) -> Array { - let nsa = getAsNSArray(a) - return Swift._forceBridgeFromObjectiveC(nsa, Array.self) -} - -func getBridgedNonverbatimArrayAndNSMutableArray() - -> (Array, NSMutableArray) { - let nsa = getAsNSMutableArray([1010, 1020, 1030]) - return (Swift._forceBridgeFromObjectiveC(nsa, Array.self), nsa) -} - -func getBridgedVerbatimEquatableArray(_ a: Array) - -> Array { - let nsa = getAsEquatableNSArray(a) - return convertNSArrayToArray(nsa) -} - -func getBridgedNonverbatimEquatableArray(_ a: Array) - -> Array { - let nsa = getAsEquatableNSArray(a) - return Swift._forceBridgeFromObjectiveC(nsa, Array.self) -} - -func getHugeBridgedVerbatimArrayHelper() -> NSArray { - let values = (1...32).map { TestObjCValueTy(1000 + $0) } - - let nsa = NSMutableArray() - nsa.addObjects(from: values) - return nsa -} - -func getHugeBridgedVerbatimArray() -> Array { - let nsa = getHugeBridgedVerbatimArrayHelper() - return convertNSArrayToArray(nsa) -} - -func getHugeBridgedNonverbatimArray() - -> Array { - let nsa = getHugeBridgedVerbatimArrayHelper() - return Swift._forceBridgeFromObjectiveC(nsa, Array.self) -} - -func getBridgedNSArrayOfObj_ValueTypeCustomBridged() -> NSArray { - expectTrue(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self)) - - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - let bridged = convertArrayToNSArray(a) - expectTrue(isNativeNSArray(bridged)) - - return bridged -} - -func getBridgedNSArrayOfValue_ValueTypeCustomBridged() -> NSArray { - expectFalse(_isBridgedVerbatimToObjectiveC(TestBridgedValueTy.self)) - - var a = Array() - a.append(TestBridgedValueTy(1010)) - a.append(TestBridgedValueTy(1020)) - a.append(TestBridgedValueTy(1030)) - - let bridged = convertArrayToNSArray(a) - expectTrue(isNativeNSArray(bridged)) - - return bridged -} - -func getRoundtripBridgedNSArray() -> NSArray { - let values = [ 1010, 1020, 1030 ].map { TestObjCValueTy($0) } - - let nsa = NSArray(array: values) - - let a: Array = convertNSArrayToArray(nsa) - - let bridgedBack = convertArrayToNSArray(a) - expectTrue(isCocoaNSArray(bridgedBack)) - expectEqual(unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) - - return bridgedBack -} - -var _objcValueCount = _stdlib_AtomicInt(0) -var _objcValueSerial = _stdlib_AtomicInt(0) - -class TestObjCValueTy : NSObject { - class var objectCount: Int { - get { - return _objcValueCount.load() - } - set { - _objcValueCount.store(newValue) - } - } - - init(_ value: Int) { - _objcValueCount.fetchAndAdd(1) - serial = _objcValueSerial.addAndFetch(1) - self.value = value - } - - deinit { - assert(serial > 0, "double destruction") - _objcValueCount.fetchAndAdd(-1) - serial = -serial - } - - override var description: String { - assert(serial > 0, "dead TestObjCValueTy") - return value.description - } - - var value: Int - var serial: Int -} - -var _objcEquatableValueCount = _stdlib_AtomicInt(0) -var _objcEquatableValueSerial = _stdlib_AtomicInt(0) - -class TestObjCEquatableValueTy : NSObject { - class var objectCount: Int { - get { - return _objcEquatableValueCount.load() - } - set { - _objcEquatableValueCount.store(newValue) - } - } - - init(_ value: Int) { - _objcEquatableValueCount.fetchAndAdd(1) - serial = _objcEquatableValueSerial.addAndFetch(1) - self.value = value - } - - deinit { - assert(serial > 0, "double destruction") - _objcEquatableValueCount.fetchAndAdd(-1) - serial = -serial - } - - override func isEqual(_ object: Any!) -> Bool { - if let other = object { - if let otherObjcKey = other as? TestObjCEquatableValueTy { - return self.value == otherObjcKey.value - } - } - return false - } - - override var description: String { - assert(serial > 0, "dead TestObjCValueTy") - return value.description - } - - var value: Int - var serial: Int -} - -func == (lhs: TestObjCEquatableValueTy, rhs: TestObjCEquatableValueTy) -> Bool { - return lhs.value == rhs.value -} - -var _bridgedValueSerial = _stdlib_AtomicInt(0) -var _bridgedValueBridgeOperations = _stdlib_AtomicInt(0) - -struct TestBridgedValueTy : CustomStringConvertible, _ObjectiveCBridgeable { - static var bridgeOperations: Int { - get { - return _bridgedValueBridgeOperations.load() - } - set { - _bridgedValueBridgeOperations.store(newValue) - } - } - - init(_ value: Int) { - serial = _bridgedValueSerial.fetchAndAdd(1) - self.value = value - } - - var description: String { - assert(serial > 0, "dead TestBridgedValueTy") - return value.description - } - - func _bridgeToObjectiveC() -> TestObjCValueTy { - TestBridgedValueTy.bridgeOperations += 1 - return TestObjCValueTy(value) - } - - static func _forceBridgeFromObjectiveC( - _ x: TestObjCValueTy, - result: inout TestBridgedValueTy? - ) { - TestBridgedValueTy.bridgeOperations += 1 - result = TestBridgedValueTy(x.value) - } - - static func _conditionallyBridgeFromObjectiveC( - _ x: TestObjCValueTy, - result: inout TestBridgedValueTy? - ) -> Bool { - self._forceBridgeFromObjectiveC(x, result: &result) - return true - } - - static func _unconditionallyBridgeFromObjectiveC(_ source: TestObjCValueTy?) - -> TestBridgedValueTy { - var result: TestBridgedValueTy? = nil - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } - - var value: Int - var serial: Int -} - -var _bridgedEquatableValueSerial = _stdlib_AtomicInt(0) -var _bridgedEquatableValueBridgeOperations = _stdlib_AtomicInt(0) - -struct TestBridgedEquatableValueTy - : Equatable, CustomStringConvertible, _ObjectiveCBridgeable { - - static var bridgeOperations: Int { - get { - return _bridgedEquatableValueBridgeOperations.load() - } - set { - _bridgedEquatableValueBridgeOperations.store(newValue) - } - } - - init(_ value: Int) { - serial = _bridgedEquatableValueSerial.addAndFetch(1) - self.value = value - } - - var description: String { - assert(serial > 0, "dead TestBridgedValueTy") - return value.description - } - - func _bridgeToObjectiveC() -> TestObjCEquatableValueTy { - _bridgedEquatableValueBridgeOperations.fetchAndAdd(1) - return TestObjCEquatableValueTy(value) - } - - static func _forceBridgeFromObjectiveC( - _ x: TestObjCEquatableValueTy, - result: inout TestBridgedEquatableValueTy? - ) { - _bridgedEquatableValueBridgeOperations.fetchAndAdd(1) - result = TestBridgedEquatableValueTy(x.value) - } - - static func _conditionallyBridgeFromObjectiveC( - _ x: TestObjCEquatableValueTy, - result: inout TestBridgedEquatableValueTy? - ) -> Bool { - self._forceBridgeFromObjectiveC(x, result: &result) - return true - } - - static func _unconditionallyBridgeFromObjectiveC( - _ source: TestObjCEquatableValueTy? - ) -> TestBridgedEquatableValueTy { - var result: TestBridgedEquatableValueTy? = nil - _forceBridgeFromObjectiveC(source!, result: &result) - return result! - } - - var value: Int - var serial: Int -} - -func == (lhs: TestBridgedEquatableValueTy, rhs: TestBridgedEquatableValueTy) -> Bool { - return lhs.value == rhs.value -} - -@objc -class CustomImmutableNSArray : NSArray { - init(_privateInit: ()) { - super.init() - } - - override init() { - expectUnreachable() - super.init() - } - - override init(objects: UnsafePointer!, count cnt: Int) { - expectUnreachable() - super.init(objects: objects, count: cnt) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) not implemented by CustomImmutableNSArray") - } - - @objc(copyWithZone:) - override func copy(with zone: NSZone?) -> Any { - CustomImmutableNSArray.timesCopyWithZoneWasCalled += 1 - return self - } - - override func object(at index: Int) -> Any { - CustomImmutableNSArray.timesObjectAtIndexWasCalled += 1 - return getAsNSArray([1010, 1020, 1030]).object(at: index) - } - - override func objectEnumerator() -> NSEnumerator { - CustomImmutableNSArray.timesObjectEnumeratorWasCalled += 1 - return getAsNSArray([1010, 1020, 1030]).objectEnumerator() - } - - override var count: Int { - CustomImmutableNSArray.timesCountWasCalled += 1 - return 3 - } - - static var timesCopyWithZoneWasCalled = 0 - static var timesObjectAtIndexWasCalled = 0 - static var timesObjectEnumeratorWasCalled = 0 - static var timesCountWasCalled = 0 -} - -#endif diff --git a/test/stdlib/OptionalTraps.swift b/test/stdlib/OptionalTraps.swift index cf45564242cc0..c4feaf4f3c130 100644 --- a/test/stdlib/OptionalTraps.swift +++ b/test/stdlib/OptionalTraps.swift @@ -25,7 +25,7 @@ func shouldCheckErrorLocation() -> Bool { guard _isDebugAssertConfiguration() else { return false } // The runtime error location format changed after the 5.3 release. // (https://github.com/apple/swift/pull/34665) - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) { return true } else { return false diff --git a/test/stdlib/PrintFloat.swift.gyb b/test/stdlib/PrintFloat.swift.gyb index 33deedadd3e95..216216712b56e 100644 --- a/test/stdlib/PrintFloat.swift.gyb +++ b/test/stdlib/PrintFloat.swift.gyb @@ -6,6 +6,9 @@ // RUN: %line-directive %t/FloatingPointPrinting.swift -- %target-run %t/main.out --locale ru_RU.UTF-8 // REQUIRES: executable_test +// rdar://77087867 +// UNSUPPORTED: CPU=arm64_32 && OS=watchos + import StdlibUnittest #if canImport(Darwin) import Darwin diff --git a/test/stdlib/RangeTraps.swift b/test/stdlib/RangeTraps.swift index e1298094c1b8c..1773022a1986b 100644 --- a/test/stdlib/RangeTraps.swift +++ b/test/stdlib/RangeTraps.swift @@ -116,7 +116,7 @@ RangeTraps.test("throughNaN") _ = ...Double.nan } -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { // Debug check was introduced in https://github.com/apple/swift/pull/34961 RangeTraps.test("UncheckedHalfOpen") .xfail(.custom( diff --git a/test/stdlib/Reflection_objc.swift b/test/stdlib/Reflection_objc.swift index 58743473e992d..2e8d2fe437580 100644 --- a/test/stdlib/Reflection_objc.swift +++ b/test/stdlib/Reflection_objc.swift @@ -9,8 +9,8 @@ // REQUIRES: executable_test // REQUIRES: objc_interop -// rdar://problem/75006694 -// XFAIL: OS=macosx && CPU=arm64 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime // // DO NOT add more tests to this file. Add them to test/1_stdlib/Runtime.swift. @@ -69,8 +69,8 @@ print("ObjC quick look objects:") // CHECK-LABEL: ObjC enums: print("ObjC enums:") -// CHECK-NEXT: We cannot reflect NSComparisonResult yet -print("We cannot reflect \(ComparisonResult.orderedAscending) yet") +// CHECK-NEXT: We cannot properly reflect NSComparisonResult(rawValue: -1) yet +print("We cannot properly reflect \(ComparisonResult.orderedAscending) yet") // Don't crash when introspecting framework types such as NSURL. // diff --git a/test/stdlib/SmallString.swift b/test/stdlib/SmallString.swift index 9d4c51b0f623d..482bb563143a9 100644 --- a/test/stdlib/SmallString.swift +++ b/test/stdlib/SmallString.swift @@ -1,6 +1,7 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift // RUN: %target-build-swift -Xfrontend -disable-access-control -module-name a %t/main.swift %S/../Inputs/SmallStringTestUtilities.swift -o %t.out -O +// RUN: %target-codesign %t.out // RUN: %target-run %t.out // REQUIRES: executable_test diff --git a/test/stdlib/StringCreate.swift b/test/stdlib/StringCreate.swift index e31a6788e67d4..183f99c8b052e 100644 --- a/test/stdlib/StringCreate.swift +++ b/test/stdlib/StringCreate.swift @@ -57,22 +57,33 @@ if #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { } expectEqual(expected, actual) } - + let validUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3, 0xA9] let invalidUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3] - - let cafe1 = String(unsafeUninitializedCapacity: validUTF8.count) { - _ = $0.initialize(from: validUTF8) - return validUTF8.count - } - expectEqual("Café", cafe1) - - let cafe2 = String(unsafeUninitializedCapacity: invalidUTF8.count) { - _ = $0.initialize(from: invalidUTF8) - return invalidUTF8.count + let longerValidUTF8: [UInt8] = [0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x43, 0x61, 0x66, 0xC3, 0xA9] + let longerInvalidUTF8: [UInt8] = [0x21, 0x21, 0x43, 0x61, 0x66, 0xC3, 0x43, 0x61, 0x66, 0xC3, 0x43, 0x61, 0x66, 0xC3] + + func test(bufferSize: Int, input: [UInt8], expected: String) { + let strs = (0..<100).map { _ in + String(unsafeUninitializedCapacity: bufferSize) { buffer in + _ = buffer.initialize(from: input) + return input.count + } + } + for str in strs { + expectEqual(expected, str) + } } - expectEqual("Caf�", cafe2) - + + test(bufferSize: validUTF8.count, input: validUTF8, expected: "Café") + test(bufferSize: invalidUTF8.count, input: invalidUTF8, expected: "Caf�") + // Force non-smol strings by using a larger capacity + test(bufferSize: 16, input: validUTF8, expected: "Café") + test(bufferSize: 16, input: invalidUTF8, expected: "Caf�") + test(bufferSize: longerValidUTF8.count, input: longerValidUTF8, expected: "!!!!!!!!!!Café") + test(bufferSize: longerInvalidUTF8.count, input: longerInvalidUTF8, expected: "!!Caf�Caf�Caf�") + test(bufferSize: 16, input: longerValidUTF8, expected: "!!!!!!!!!!Café") + test(bufferSize: 16, input: longerInvalidUTF8, expected: "!!Caf�Caf�Caf�") let empty = String(unsafeUninitializedCapacity: 16) { _ in // Can't initialize the buffer (e.g. the capacity is too small). return 0 diff --git a/test/stdlib/TestAffineTransform.swift b/test/stdlib/TestAffineTransform.swift deleted file mode 100644 index 46faa38db7be3..0000000000000 --- a/test/stdlib/TestAffineTransform.swift +++ /dev/null @@ -1,432 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if os(macOS) - -#if FOUNDATION_XCTEST -import XCTest -class TestAffineTransformSuper : XCTestCase { } -#else -import StdlibUnittest -class TestAffineTransformSuper { } -#endif - -func expectEqualWithAccuracy(_ lhs: Double, _ rhs: Double, accuracy: Double, _ message: String = "", file: String = #file, line: UInt = #line) { - expectTrue(fabs(lhs - rhs) < accuracy, message, file: file, line: line) -} - -extension AffineTransform { - func transform(_ aRect: NSRect) -> NSRect { - return NSRect(origin: transform(aRect.origin), size: transform(aRect.size)) - } -} - -class TestAffineTransform : TestAffineTransformSuper { - private let accuracyThreshold = 0.001 - - func checkPointTransformation(_ transform: AffineTransform, point: NSPoint, expectedPoint: NSPoint, _ message: String = "", file: String = #file, line: UInt = #line) { - let newPoint = transform.transform(point) - expectEqualWithAccuracy(Double(newPoint.x), Double(expectedPoint.x), accuracy: accuracyThreshold, - "x (expected: \(expectedPoint.x), was: \(newPoint.x)): \(message)", file: file, line: line) - expectEqualWithAccuracy(Double(newPoint.y), Double(expectedPoint.y), accuracy: accuracyThreshold, - "y (expected: \(expectedPoint.y), was: \(newPoint.y)): \(message)", file: file, line: line) - } - - func checkSizeTransformation(_ transform: AffineTransform, size: NSSize, expectedSize: NSSize, _ message: String = "", file: String = #file, line: UInt = #line) { - let newSize = transform.transform(size) - expectEqualWithAccuracy(Double(newSize.width), Double(expectedSize.width), accuracy: accuracyThreshold, - "width (expected: \(expectedSize.width), was: \(newSize.width)): \(message)", file: file, line: line) - expectEqualWithAccuracy(Double(newSize.height), Double(expectedSize.height), accuracy: accuracyThreshold, - "height (expected: \(expectedSize.height), was: \(newSize.height)): \(message)", file: file, line: line) - } - - func checkRectTransformation(_ transform: AffineTransform, rect: NSRect, expectedRect: NSRect, _ message: String = "", file: String = #file, line: UInt = #line) { - let newRect = transform.transform(rect) - - checkPointTransformation(transform, point: newRect.origin, expectedPoint: expectedRect.origin, - "origin (expected: \(expectedRect.origin), was: \(newRect.origin)): \(message)", file: file, line: line) - checkSizeTransformation(transform, size: newRect.size, expectedSize: expectedRect.size, - "size (expected: \(expectedRect.size), was: \(newRect.size)): \(message)", file: file, line: line) - } - - func test_BasicConstruction() { - let defaultAffineTransform = AffineTransform() - let identityTransform = AffineTransform.identity - - expectEqual(defaultAffineTransform, identityTransform) - - // The diagonal entries (1,1) and (2,2) of the identity matrix are ones. The other entries are zeros. - // TODO: These should use DBL_MAX but it's not available as part of Glibc on Linux - expectEqualWithAccuracy(Double(identityTransform.m11), Double(1), accuracy: accuracyThreshold) - expectEqualWithAccuracy(Double(identityTransform.m22), Double(1), accuracy: accuracyThreshold) - - expectEqualWithAccuracy(Double(identityTransform.m12), Double(0), accuracy: accuracyThreshold) - expectEqualWithAccuracy(Double(identityTransform.m21), Double(0), accuracy: accuracyThreshold) - expectEqualWithAccuracy(Double(identityTransform.tX), Double(0), accuracy: accuracyThreshold) - expectEqualWithAccuracy(Double(identityTransform.tY), Double(0), accuracy: accuracyThreshold) - } - - func test_IdentityTransformation() { - let identityTransform = AffineTransform.identity - - func checkIdentityPointTransformation(_ point: NSPoint) { - checkPointTransformation(identityTransform, point: point, expectedPoint: point) - } - - checkIdentityPointTransformation(NSPoint()) - checkIdentityPointTransformation(NSPoint(x: CGFloat(24.5), y: CGFloat(10.0))) - checkIdentityPointTransformation(NSPoint(x: CGFloat(-7.5), y: CGFloat(2.0))) - - func checkIdentitySizeTransformation(_ size: NSSize) { - checkSizeTransformation(identityTransform, size: size, expectedSize: size) - } - - checkIdentitySizeTransformation(NSSize()) - checkIdentitySizeTransformation(NSSize(width: CGFloat(13.0), height: CGFloat(12.5))) - checkIdentitySizeTransformation(NSSize(width: CGFloat(100.0), height: CGFloat(-100.0))) - } - - func test_Translation() { - let point = NSPoint(x: CGFloat(0.0), y: CGFloat(0.0)) - - var noop = AffineTransform.identity - noop.translate(x: CGFloat(), y: CGFloat()) - checkPointTransformation(noop, point: point, expectedPoint: point) - - var translateH = AffineTransform.identity - translateH.translate(x: CGFloat(10.0), y: CGFloat()) - checkPointTransformation(translateH, point: point, expectedPoint: NSPoint(x: CGFloat(10.0), y: CGFloat())) - - var translateV = AffineTransform.identity - translateV.translate(x: CGFloat(), y: CGFloat(20.0)) - checkPointTransformation(translateV, point: point, expectedPoint: NSPoint(x: CGFloat(), y: CGFloat(20.0))) - - var translate = AffineTransform.identity - translate.translate(x: CGFloat(-30.0), y: CGFloat(40.0)) - checkPointTransformation(translate, point: point, expectedPoint: NSPoint(x: CGFloat(-30.0), y: CGFloat(40.0))) - } - - func test_Scale() { - let size = NSSize(width: CGFloat(10.0), height: CGFloat(10.0)) - - var noop = AffineTransform.identity - noop.scale(CGFloat(1.0)) - checkSizeTransformation(noop, size: size, expectedSize: size) - - var shrink = AffineTransform.identity - shrink.scale(CGFloat(0.5)) - checkSizeTransformation(shrink, size: size, expectedSize: NSSize(width: CGFloat(5.0), height: CGFloat(5.0))) - - var grow = AffineTransform.identity - grow.scale(CGFloat(3.0)) - checkSizeTransformation(grow, size: size, expectedSize: NSSize(width: CGFloat(30.0), height: CGFloat(30.0))) - - var stretch = AffineTransform.identity - stretch.scale(x: CGFloat(2.0), y: CGFloat(0.5)) - checkSizeTransformation(stretch, size: size, expectedSize: NSSize(width: CGFloat(20.0), height: CGFloat(5.0))) - } - - func test_Rotation_Degrees() { - let point = NSPoint(x: CGFloat(10.0), y: CGFloat(10.0)) - - var noop = AffineTransform.identity - noop.rotate(byDegrees: CGFloat()) - checkPointTransformation(noop, point: point, expectedPoint: point) - - var tenEighty = AffineTransform.identity - tenEighty.rotate(byDegrees: CGFloat(1080.0)) - checkPointTransformation(tenEighty, point: point, expectedPoint: point) - - var rotateCounterClockwise = AffineTransform.identity - rotateCounterClockwise.rotate(byDegrees: CGFloat(90.0)) - checkPointTransformation(rotateCounterClockwise, point: point, expectedPoint: NSPoint(x: CGFloat(-10.0), y: CGFloat(10.0))) - - var rotateClockwise = AffineTransform.identity - rotateClockwise.rotate(byDegrees: CGFloat(-90.0)) - checkPointTransformation(rotateClockwise, point: point, expectedPoint: NSPoint(x: CGFloat(10.0), y: CGFloat(-10.0))) - - var reflectAboutOrigin = AffineTransform.identity - reflectAboutOrigin.rotate(byDegrees: CGFloat(180.0)) - checkPointTransformation(reflectAboutOrigin, point: point, expectedPoint: NSPoint(x: CGFloat(-10.0), y: CGFloat(-10.0))) - } - - func test_Rotation_Radians() { - let point = NSPoint(x: CGFloat(10.0), y: CGFloat(10.0)) - - var noop = AffineTransform.identity - noop.rotate(byRadians: CGFloat()) - checkPointTransformation(noop, point: point, expectedPoint: point) - - var tenEighty = AffineTransform.identity - tenEighty.rotate(byRadians: 6 * .pi) - checkPointTransformation(tenEighty, point: point, expectedPoint: point) - - var rotateCounterClockwise = AffineTransform.identity - rotateCounterClockwise.rotate(byRadians: .pi / 2) - checkPointTransformation(rotateCounterClockwise, point: point, expectedPoint: NSPoint(x: CGFloat(-10.0), y: CGFloat(10.0))) - - var rotateClockwise = AffineTransform.identity - rotateClockwise.rotate(byRadians: -.pi / 2) - checkPointTransformation(rotateClockwise, point: point, expectedPoint: NSPoint(x: CGFloat(10.0), y: CGFloat(-10.0))) - - var reflectAboutOrigin = AffineTransform.identity - reflectAboutOrigin.rotate(byRadians: .pi) - checkPointTransformation(reflectAboutOrigin, point: point, expectedPoint: NSPoint(x: CGFloat(-10.0), y: CGFloat(-10.0))) - - var scaleThenRotate = AffineTransform(scale: 2) - scaleThenRotate.rotate(byRadians: .pi / 2) - checkPointTransformation(scaleThenRotate, point: point, expectedPoint: NSPoint(x: CGFloat(-20.0), y: CGFloat(20.0))) - } - - func test_Inversion() { - let point = NSPoint(x: CGFloat(10.0), y: CGFloat(10.0)) - - var translate = AffineTransform.identity - translate.translate(x: CGFloat(-30.0), y: CGFloat(40.0)) - - var rotate = AffineTransform.identity - translate.rotate(byDegrees: CGFloat(30.0)) - - var scale = AffineTransform.identity - scale.scale(CGFloat(2.0)) - - var identityTransform = AffineTransform.identity - - // append transformations - identityTransform.append(translate) - identityTransform.append(rotate) - identityTransform.append(scale) - - // invert transformations - scale.invert() - rotate.invert() - translate.invert() - - // append inverse transformations in reverse order - identityTransform.append(scale) - identityTransform.append(rotate) - identityTransform.append(translate) - - checkPointTransformation(identityTransform, point: point, expectedPoint: point) - } - - func test_TranslationComposed() { - var xyPlus5 = AffineTransform.identity - xyPlus5.translate(x: CGFloat(2.0), y: CGFloat(3.0)) - xyPlus5.translate(x: CGFloat(3.0), y: CGFloat(2.0)) - - checkPointTransformation(xyPlus5, point: NSPoint(x: CGFloat(-2.0), y: CGFloat(-3.0)), - expectedPoint: NSPoint(x: CGFloat(3.0), y: CGFloat(2.0))) - } - - func test_Scaling() { - var xyTimes5 = AffineTransform.identity - xyTimes5.scale(CGFloat(5.0)) - - checkPointTransformation(xyTimes5, point: NSPoint(x: CGFloat(-2.0), y: CGFloat(3.0)), - expectedPoint: NSPoint(x: CGFloat(-10.0), y: CGFloat(15.0))) - - var xTimes2YTimes3 = AffineTransform.identity - xTimes2YTimes3.scale(x: CGFloat(2.0), y: CGFloat(-3.0)) - - checkPointTransformation(xTimes2YTimes3, point: NSPoint(x: CGFloat(-1.0), y: CGFloat(3.5)), - expectedPoint: NSPoint(x: CGFloat(-2.0), y: CGFloat(-10.5))) - } - - func test_TranslationScaling() { - var xPlus2XYTimes5 = AffineTransform.identity - xPlus2XYTimes5.translate(x: CGFloat(2.0), y: CGFloat()) - xPlus2XYTimes5.scale(x: CGFloat(5.0), y: CGFloat(-5.0)) - - checkPointTransformation(xPlus2XYTimes5, point: NSPoint(x: CGFloat(1.0), y: CGFloat(2.0)), - expectedPoint: NSPoint(x: CGFloat(7.0), y: CGFloat(-10.0))) - } - - func test_ScalingTranslation() { - var xyTimes5XPlus3 = AffineTransform.identity - xyTimes5XPlus3.scale(CGFloat(5.0)) - xyTimes5XPlus3.translate(x: CGFloat(3.0), y: CGFloat()) - - checkPointTransformation(xyTimes5XPlus3, point: NSPoint(x: CGFloat(1.0), y: CGFloat(2.0)), - expectedPoint: NSPoint(x: CGFloat(20.0), y: CGFloat(10.0))) - } - - func test_AppendTransform() { - let point = NSPoint(x: CGFloat(10.0), y: CGFloat(10.0)) - - var identityTransform = AffineTransform.identity - identityTransform.append(identityTransform) - checkPointTransformation(identityTransform, point: point, expectedPoint: point) - - let translate = AffineTransform(translationByX: 10.0, byY: 0.0) - - let scale = AffineTransform(scale: 2.0) - - var translateThenScale = translate - translateThenScale.append(scale) - checkPointTransformation(translateThenScale, point: point, expectedPoint: NSPoint(x: CGFloat(40.0), y: CGFloat(20.0))) - } - - func test_PrependTransform() { - let point = NSPoint(x: CGFloat(10.0), y: CGFloat(10.0)) - - var identityTransform = AffineTransform.identity - identityTransform.append(identityTransform) - checkPointTransformation(identityTransform, point: point, expectedPoint: point) - - let translate = AffineTransform(translationByX: 10.0, byY: 0.0) - - let scale = AffineTransform(scale: 2.0) - - var scaleThenTranslate = translate - scaleThenTranslate.prepend(scale) - checkPointTransformation(scaleThenTranslate, point: point, expectedPoint: NSPoint(x: CGFloat(30.0), y: CGFloat(20.0))) - } - - func test_TransformComposition() { - let origin = NSPoint(x: CGFloat(10.0), y: CGFloat(10.0)) - let size = NSSize(width: CGFloat(40.0), height: CGFloat(20.0)) - let rect = NSRect(origin: origin, size: size) - let center = NSPoint(x: NSMidX(rect), y: NSMidY(rect)) - - let rotate = AffineTransform(rotationByDegrees: 90.0) - - let moveOrigin = AffineTransform(translationByX: -center.x, byY: -center.y) - - var moveBack = moveOrigin - moveBack.invert() - - var rotateAboutCenter = rotate - rotateAboutCenter.prepend(moveOrigin) - rotateAboutCenter.append(moveBack) - - // center of rect shouldn't move as its the rotation anchor - checkPointTransformation(rotateAboutCenter, point: center, expectedPoint: center) - } - - func test_hashing() { - guard #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) else { return } - - // the transforms are made up and the values don't matter - let a = AffineTransform(m11: 1.0, m12: 2.5, m21: 66.2, m22: 40.2, tX: -5.5, tY: 3.7) - let b = AffineTransform(m11: -55.66, m12: 22.7, m21: 1.5, m22: 0.0, tX: -22, tY: -33) - let c = AffineTransform(m11: 4.5, m12: 1.1, m21: 0.025, m22: 0.077, tX: -0.55, tY: 33.2) - let d = AffineTransform(m11: 7.0, m12: -2.3, m21: 6.7, m22: 0.25, tX: 0.556, tY: 0.99) - let e = AffineTransform(m11: 0.498, m12: -0.284, m21: -0.742, m22: 0.3248, tX: 12, tY: 44) - - // Samples testing that every component is properly hashed - let x1 = AffineTransform(m11: 1.0, m12: 2.0, m21: 3.0, m22: 4.0, tX: 5.0, tY: 6.0) - let x2 = AffineTransform(m11: 1.5, m12: 2.0, m21: 3.0, m22: 4.0, tX: 5.0, tY: 6.0) - let x3 = AffineTransform(m11: 1.0, m12: 2.5, m21: 3.0, m22: 4.0, tX: 5.0, tY: 6.0) - let x4 = AffineTransform(m11: 1.0, m12: 2.0, m21: 3.5, m22: 4.0, tX: 5.0, tY: 6.0) - let x5 = AffineTransform(m11: 1.0, m12: 2.0, m21: 3.0, m22: 4.5, tX: 5.0, tY: 6.0) - let x6 = AffineTransform(m11: 1.0, m12: 2.0, m21: 3.0, m22: 4.0, tX: 5.5, tY: 6.0) - let x7 = AffineTransform(m11: 1.0, m12: 2.0, m21: 3.0, m22: 4.0, tX: 5.0, tY: 6.5) - - @inline(never) - func bridged(_ t: AffineTransform) -> NSAffineTransform { - return t as NSAffineTransform - } - - let values: [[AffineTransform]] = [ - [AffineTransform.identity, NSAffineTransform() as AffineTransform], - [a, bridged(a) as AffineTransform], - [b, bridged(b) as AffineTransform], - [c, bridged(c) as AffineTransform], - [d, bridged(d) as AffineTransform], - [e, bridged(e) as AffineTransform], - [x1], [x2], [x3], [x4], [x5], [x6], [x7] - ] - checkHashableGroups(values) - } - - func test_AnyHashable() { - func makeNSAffineTransform(rotatedByDegrees angle: CGFloat) -> NSAffineTransform { - let result = NSAffineTransform() - result.rotate(byDegrees: angle) - return result - } - - let s1 = AffineTransform.identity - let s2 = AffineTransform(m11: -55.66, m12: 22.7, m21: 1.5, m22: 0.0, tX: -22, tY: -33) - let s3 = AffineTransform(m11: -55.66, m12: 22.7, m21: 1.5, m22: 0.0, tX: -22, tY: -33) - let s4 = makeNSAffineTransform(rotatedByDegrees: 10) as AffineTransform - let s5 = makeNSAffineTransform(rotatedByDegrees: 10) as AffineTransform - - let c1 = NSAffineTransform(transform: s1) - let c2 = NSAffineTransform(transform: s2) - let c3 = NSAffineTransform(transform: s3) - let c4 = makeNSAffineTransform(rotatedByDegrees: 10) - let c5 = makeNSAffineTransform(rotatedByDegrees: 10) - - let groups: [[AnyHashable]] = [ - [s1, c1], - [s2, c2, s3, c3], - [s4, c4, s5, c5] - ] - checkHashableGroups(groups) - - expectEqual(AffineTransform.self, type(of: (s1 as AnyHashable).base)) - expectEqual(AffineTransform.self, type(of: (s2 as AnyHashable).base)) - expectEqual(AffineTransform.self, type(of: (s3 as AnyHashable).base)) - expectEqual(AffineTransform.self, type(of: (s4 as AnyHashable).base)) - expectEqual(AffineTransform.self, type(of: (s5 as AnyHashable).base)) - - expectEqual(AffineTransform.self, type(of: (c1 as AnyHashable).base)) - expectEqual(AffineTransform.self, type(of: (c2 as AnyHashable).base)) - expectEqual(AffineTransform.self, type(of: (c3 as AnyHashable).base)) - expectEqual(AffineTransform.self, type(of: (c4 as AnyHashable).base)) - expectEqual(AffineTransform.self, type(of: (c5 as AnyHashable).base)) - } - - func test_unconditionallyBridgeFromObjectiveC() { - expectEqual(AffineTransform(), AffineTransform._unconditionallyBridgeFromObjectiveC(nil)) - } - - func test_rotation_compose() { - var t = AffineTransform.identity - t.translate(x: 1.0, y: 1.0) - t.rotate(byDegrees: 90) - t.translate(x: -1.0, y: -1.0) - let result = t.transform(NSPoint(x: 1.0, y: 2.0)) - expectEqualWithAccuracy(0.0, Double(result.x), accuracy: accuracyThreshold) - expectEqualWithAccuracy(1.0, Double(result.y), accuracy: accuracyThreshold) - } -} - -#if !FOUNDATION_XCTEST -var AffineTransformTests = TestSuite("TestAffineTransform") -AffineTransformTests.test("test_BasicConstruction") { TestAffineTransform().test_BasicConstruction() } -AffineTransformTests.test("test_IdentityTransformation") { TestAffineTransform().test_IdentityTransformation() } -AffineTransformTests.test("test_Translation") { TestAffineTransform().test_Translation() } -AffineTransformTests.test("test_Scale") { TestAffineTransform().test_Scale() } -AffineTransformTests.test("test_Rotation_Degrees") { TestAffineTransform().test_Rotation_Degrees() } -AffineTransformTests.test("test_Rotation_Radians") { TestAffineTransform().test_Rotation_Radians() } -AffineTransformTests.test("test_Inversion") { TestAffineTransform().test_Inversion() } -AffineTransformTests.test("test_TranslationComposed") { TestAffineTransform().test_TranslationComposed() } -AffineTransformTests.test("test_Scaling") { TestAffineTransform().test_Scaling() } -AffineTransformTests.test("test_TranslationScaling") { TestAffineTransform().test_TranslationScaling() } -AffineTransformTests.test("test_ScalingTranslation") { TestAffineTransform().test_ScalingTranslation() } -AffineTransformTests.test("test_AppendTransform") { TestAffineTransform().test_AppendTransform() } -AffineTransformTests.test("test_PrependTransform") { TestAffineTransform().test_PrependTransform() } -AffineTransformTests.test("test_TransformComposition") { TestAffineTransform().test_TransformComposition() } -AffineTransformTests.test("test_hashing") { TestAffineTransform().test_hashing() } -AffineTransformTests.test("test_AnyHashable") { TestAffineTransform().test_AnyHashable() } -AffineTransformTests.test("test_unconditionallyBridgeFromObjectiveC") { TestAffineTransform().test_unconditionallyBridgeFromObjectiveC() } -AffineTransformTests.test("test_rotation_compose") { TestAffineTransform().test_rotation_compose() } -runAllTests() -#endif - - -#endif diff --git a/test/stdlib/TestCalendar.swift b/test/stdlib/TestCalendar.swift deleted file mode 100644 index d6512605c3fd0..0000000000000 --- a/test/stdlib/TestCalendar.swift +++ /dev/null @@ -1,328 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %empty-directory(%t) -// -// RUN: %target-clang %S/Inputs/FoundationBridge/FoundationBridge.m -c -o %t/FoundationBridgeObjC.o -g -// RUN: %target-build-swift %s -I %S/Inputs/FoundationBridge/ -Xlinker %t/FoundationBridgeObjC.o -o %t/TestCalendar - -// RUN: %target-codesign %t/TestCalendar - -// RUN: %target-run %t/TestCalendar > %t.txt -// REQUIRES: executable_test -// REQUIRES: objc_interop - -// FIXME: rdar://problem/31207060 -// UNSUPPORTED: OS=ios -// UNSUPPORTED: OS=tvos -// UNSUPPORTED: OS=watchos - -import Foundation -import FoundationBridgeObjC - -#if FOUNDATION_XCTEST - import XCTest - class TestCalendarSuper : XCTestCase { } -#else - import StdlibUnittest - class TestCalendarSuper { } -#endif - -class TestCalendar : TestCalendarSuper { - - func test_copyOnWrite() { - var c = Calendar(identifier: .gregorian) - let c2 = c - expectEqual(c, c2) - - // Change the weekday and check result - let firstWeekday = c.firstWeekday - let newFirstWeekday = firstWeekday < 7 ? firstWeekday + 1 : firstWeekday - 1 - - c.firstWeekday = newFirstWeekday - expectEqual(newFirstWeekday, c.firstWeekday) - expectEqual(c2.firstWeekday, firstWeekday) - - expectNotEqual(c, c2) - - // Change the time zone and check result - let c3 = c - expectEqual(c, c3) - - let tz = c.timeZone - // Use two different identifiers so we don't fail if the current time zone happens to be the one returned - let aTimeZoneId = TimeZone.knownTimeZoneIdentifiers[1] - let anotherTimeZoneId = TimeZone.knownTimeZoneIdentifiers[0] - - let newTz = tz.identifier == aTimeZoneId ? TimeZone(identifier: anotherTimeZoneId)! : TimeZone(identifier: aTimeZoneId)! - - c.timeZone = newTz - expectNotEqual(c, c3) - - } - - func test_bridgingAutoupdating() { - let tester = CalendarBridgingTester() - - do { - let c = Calendar.autoupdatingCurrent - let result = tester.verifyAutoupdating(c) - expectTrue(result) - } - - // Round trip an autoupdating calendar - do { - let c = tester.autoupdatingCurrentCalendar() - let result = tester.verifyAutoupdating(c) - expectTrue(result) - } - } - - func test_equality() { - let autoupdating = Calendar.autoupdatingCurrent - let autoupdating2 = Calendar.autoupdatingCurrent - - expectEqual(autoupdating, autoupdating2) - - let current = Calendar.current - - expectNotEqual(autoupdating, current) - - // Make a copy of current - var current2 = current - expectEqual(current, current2) - - // Mutate something (making sure we don't use the current time zone) - if current2.timeZone.identifier == "America/Los_Angeles" { - current2.timeZone = TimeZone(identifier: "America/New_York")! - } else { - current2.timeZone = TimeZone(identifier: "America/Los_Angeles")! - } - expectNotEqual(current, current2) - - // Mutate something else - current2 = current - expectEqual(current, current2) - - current2.locale = Locale(identifier: "MyMadeUpLocale") - expectNotEqual(current, current2) - } - - func test_hash() { - let calendars: [Calendar] = [ - Calendar.autoupdatingCurrent, - Calendar(identifier: .buddhist), - Calendar(identifier: .gregorian), - Calendar(identifier: .islamic), - Calendar(identifier: .iso8601), - ] - checkHashable(calendars, equalityOracle: { $0 == $1 }) - - // autoupdating calendar isn't equal to the current, even though it's - // likely to be the same. - let calendars2: [Calendar] = [ - Calendar.autoupdatingCurrent, - Calendar.current, - ] - checkHashable(calendars2, equalityOracle: { $0 == $1 }) - } - - func test_properties() { - // Mainly we want to just make sure these go through to the NSCalendar implementation at this point. - if #available(iOS 8.0, OSX 10.7, *) { - var c = Calendar(identifier: .gregorian) - // Use english localization - c.locale = Locale(identifier: "en_US") - c.timeZone = TimeZone(identifier: "America/Los_Angeles")! - - expectEqual("AM", c.amSymbol) - expectEqual("PM", c.pmSymbol) - expectEqual(["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"], c.quarterSymbols) - expectEqual(["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"], c.standaloneQuarterSymbols) - expectEqual(["BC", "AD"], c.eraSymbols) - expectEqual(["Before Christ", "Anno Domini"], c.longEraSymbols) - expectEqual(["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"], c.veryShortMonthSymbols) - expectEqual(["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"], c.veryShortStandaloneMonthSymbols) - expectEqual(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], c.shortMonthSymbols) - expectEqual(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], c.shortStandaloneMonthSymbols) - expectEqual(["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], c.monthSymbols) - expectEqual(["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], c.standaloneMonthSymbols) - expectEqual(["Q1", "Q2", "Q3", "Q4"], c.shortQuarterSymbols) - expectEqual(["Q1", "Q2", "Q3", "Q4"], c.shortStandaloneQuarterSymbols) - expectEqual(["S", "M", "T", "W", "T", "F", "S"], c.veryShortStandaloneWeekdaySymbols) - expectEqual(["S", "M", "T", "W", "T", "F", "S"], c.veryShortWeekdaySymbols) - expectEqual(["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], c.shortStandaloneWeekdaySymbols) - expectEqual(["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], c.shortWeekdaySymbols) - expectEqual(["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], c.standaloneWeekdaySymbols) - expectEqual(["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], c.weekdaySymbols) - - // The idea behind these tests is not to test calendrical math, but to simply verify that we are getting some kind of result from calling through to the underlying Foundation and ICU logic. If we move that logic into this struct in the future, then we will need to expand the test cases. - - // This is a very special Date in my life: the exact moment when I wrote these test cases and therefore knew all of the answers. - let d = Date(timeIntervalSince1970: 1468705593.2533731) - let earlierD = c.date(byAdding: DateComponents(day: -10), to: d)! - - expectEqual(1..<29, c.minimumRange(of: .day)) - expectEqual(1..<54, c.maximumRange(of: .weekOfYear)) - expectEqual(0..<60, c.range(of: .second, in: .minute, for: d)) - - var d1 = Date() - var ti : TimeInterval = 0 - - expectTrue(c.dateInterval(of: .day, start: &d1, interval: &ti, for: d)) - expectEqual(Date(timeIntervalSince1970: 1468652400.0), d1) - expectEqual(86400, ti) - - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let dateInterval = c.dateInterval(of: .day, for: d) - expectEqual(DateInterval(start: d1, duration: ti), dateInterval) - } - - expectEqual(15, c.ordinality(of: .hour, in: .day, for: d)) - - expectEqual(Date(timeIntervalSince1970: 1468791993.2533731), c.date(byAdding: .day, value: 1, to: d)) - expectEqual(Date(timeIntervalSince1970: 1468791993.2533731), c.date(byAdding: DateComponents(day: 1), to: d)) - - expectEqual(Date(timeIntervalSince1970: 946627200.0), c.date(from: DateComponents(year: 1999, month: 12, day: 31))) - - let comps = c.dateComponents([.year, .month, .day], from: Date(timeIntervalSince1970: 946627200.0)) - expectEqual(1999, comps.year) - expectEqual(12, comps.month) - expectEqual(31, comps.day) - - expectEqual(10, c.dateComponents([.day], from: d, to: c.date(byAdding: DateComponents(day: 10), to: d)!).day) - - expectEqual(30, c.dateComponents([.day], from: DateComponents(year: 1999, month: 12, day: 1), to: DateComponents(year: 1999, month: 12, day: 31)).day) - - expectEqual(2016, c.component(.year, from: d)) - - expectEqual(Date(timeIntervalSince1970: 1468652400.0), c.startOfDay(for: d)) - - if #available(iOS 8, macOS 10.10, *) { - // Mac OS X 10.9 and iOS 7 had a bug in NSCalendar for hour, minute, and second granularities. - expectEqual(.orderedSame, c.compare(d, to: d + 10, toGranularity: .minute)) - } - - expectFalse(c.isDate(d, equalTo: d + 10, toGranularity: .second)) - expectTrue(c.isDate(d, equalTo: d + 10, toGranularity: .day)) - - expectFalse(c.isDate(earlierD, inSameDayAs: d)) - expectTrue(c.isDate(d, inSameDayAs: d)) - - expectFalse(c.isDateInToday(earlierD)) - expectFalse(c.isDateInYesterday(earlierD)) - expectFalse(c.isDateInTomorrow(earlierD)) - - expectTrue(c.isDateInWeekend(d)) // 😢 - - expectTrue(c.dateIntervalOfWeekend(containing: d, start: &d1, interval: &ti)) - - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let thisWeekend = DateInterval(start: Date(timeIntervalSince1970: 1468652400.0), duration: 172800.0) - - expectEqual(thisWeekend, DateInterval(start: d1, duration: ti)) - expectEqual(thisWeekend, c.dateIntervalOfWeekend(containing: d)) - } - - - expectTrue(c.nextWeekend(startingAfter: d, start: &d1, interval: &ti)) - - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let nextWeekend = DateInterval(start: Date(timeIntervalSince1970: 1469257200.0), duration: 172800.0) - - expectEqual(nextWeekend, DateInterval(start: d1, duration: ti)) - expectEqual(nextWeekend, c.nextWeekend(startingAfter: d)) - } - - // Enumeration - - var count = 0 - var exactCount = 0 - - // Find the days numbered '31' after 'd', allowing the algorithm to move to the next day if required - c.enumerateDates(startingAfter: d, matching: DateComponents(day: 31), matchingPolicy: .nextTime) { result, exact, stop in - // Just stop some arbitrary time in the future - if result! > d + 86400*365 { stop = true } - count += 1 - if exact { exactCount += 1 } - } - - /* - Optional(2016-07-31 07:00:00 +0000) - Optional(2016-08-31 07:00:00 +0000) - Optional(2016-10-01 07:00:00 +0000) - Optional(2016-10-31 07:00:00 +0000) - Optional(2016-12-01 08:00:00 +0000) - Optional(2016-12-31 08:00:00 +0000) - Optional(2017-01-31 08:00:00 +0000) - Optional(2017-03-01 08:00:00 +0000) - Optional(2017-03-31 07:00:00 +0000) - Optional(2017-05-01 07:00:00 +0000) - Optional(2017-05-31 07:00:00 +0000) - Optional(2017-07-01 07:00:00 +0000) - Optional(2017-07-31 07:00:00 +0000) - */ - - expectEqual(count, 13) - expectEqual(exactCount, 8) - - - expectEqual(Date(timeIntervalSince1970: 1469948400.0), c.nextDate(after: d, matching: DateComponents(day: 31), matchingPolicy: .nextTime)) - - - expectEqual(Date(timeIntervalSince1970: 1468742400.0), c.date(bySetting: .hour, value: 1, of: d)) - - expectEqual(Date(timeIntervalSince1970: 1468656123.0), c.date(bySettingHour: 1, minute: 2, second: 3, of: d, matchingPolicy: .nextTime)) - - expectTrue(c.date(d, matchesComponents: DateComponents(month: 7))) - expectFalse(c.date(d, matchesComponents: DateComponents(month: 7, day: 31))) - } - } - - func test_AnyHashableContainingCalendar() { - let values: [Calendar] = [ - Calendar(identifier: .gregorian), - Calendar(identifier: .japanese), - Calendar(identifier: .japanese) - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Calendar.self, type(of: anyHashables[0].base)) - expectEqual(Calendar.self, type(of: anyHashables[1].base)) - expectEqual(Calendar.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSCalendar() { - if #available(iOS 8.0, *) { - let values: [NSCalendar] = [ - NSCalendar(identifier: .gregorian)!, - NSCalendar(identifier: .japanese)!, - NSCalendar(identifier: .japanese)!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Calendar.self, type(of: anyHashables[0].base)) - expectEqual(Calendar.self, type(of: anyHashables[1].base)) - expectEqual(Calendar.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - } -} - -#if !FOUNDATION_XCTEST -var CalendarTests = TestSuite("TestCalendar") -CalendarTests.test("test_copyOnWrite") { TestCalendar().test_copyOnWrite() } -CalendarTests.test("test_bridgingAutoupdating") { TestCalendar().test_bridgingAutoupdating() } -CalendarTests.test("test_equality") { TestCalendar().test_equality() } -CalendarTests.test("test_hash") { TestCalendar().test_hash() } -CalendarTests.test("test_properties") { TestCalendar().test_properties() } -CalendarTests.test("test_AnyHashableContainingCalendar") { TestCalendar().test_AnyHashableContainingCalendar() } -CalendarTests.test("test_AnyHashableCreatedFromNSCalendar") { TestCalendar().test_AnyHashableCreatedFromNSCalendar() } -runAllTests() -#endif diff --git a/test/stdlib/TestCharacterSet.swift b/test/stdlib/TestCharacterSet.swift deleted file mode 100644 index 293efa8b1cdc8..0000000000000 --- a/test/stdlib/TestCharacterSet.swift +++ /dev/null @@ -1,363 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestCharacterSetSuper : XCTestCase { } -#else -import StdlibUnittest -class TestCharacterSetSuper { } -#endif - -class TestCharacterSet : TestCharacterSetSuper { - let capitalA = UnicodeScalar(0x0041)! // LATIN CAPITAL LETTER A - let capitalB = UnicodeScalar(0x0042)! // LATIN CAPITAL LETTER B - let capitalC = UnicodeScalar(0x0043)! // LATIN CAPITAL LETTER C - - func testBasicConstruction() { - // Create a character set - let cs = CharacterSet.letters - - // Use some method from it - let invertedCs = cs.inverted - expectTrue(!invertedCs.contains(capitalA), "Character set must not contain our letter") - - // Use another method from it - let originalCs = invertedCs.inverted - - expectTrue(originalCs.contains(capitalA), "Character set must contain our letter") - } - - func testMutability_copyOnWrite() { - var firstCharacterSet = CharacterSet(charactersIn: "ABC") - expectTrue(firstCharacterSet.contains(capitalA), "Character set must contain our letter") - expectTrue(firstCharacterSet.contains(capitalB), "Character set must contain our letter") - expectTrue(firstCharacterSet.contains(capitalC), "Character set must contain our letter") - - // Make a 'copy' (just the struct) - var secondCharacterSet = firstCharacterSet - // first: ABC, second: ABC - - // Mutate first and verify that it has correct content - firstCharacterSet.remove(charactersIn: "A") - // first: BC, second: ABC - - expectTrue(!firstCharacterSet.contains(capitalA), "Character set must not contain our letter") - expectTrue(secondCharacterSet.contains(capitalA), "Copy should not have been mutated") - - // Make a 'copy' (just the struct) of the second set, mutate it - let thirdCharacterSet = secondCharacterSet - // first: BC, second: ABC, third: ABC - - secondCharacterSet.remove(charactersIn: "B") - // first: BC, second: AC, third: ABC - - expectTrue(firstCharacterSet.contains(capitalB), "Character set must contain our letter") - expectTrue(!secondCharacterSet.contains(capitalB), "Character set must not contain our letter") - expectTrue(thirdCharacterSet.contains(capitalB), "Character set must contain our letter") - - firstCharacterSet.remove(charactersIn: "C") - // first: B, second: AC, third: ABC - - expectTrue(!firstCharacterSet.contains(capitalC), "Character set must not contain our letter") - expectTrue(secondCharacterSet.contains(capitalC), "Character set must not contain our letter") - expectTrue(thirdCharacterSet.contains(capitalC), "Character set must contain our letter") - } - - func testMutability_mutableCopyCrash() { - let cs = CharacterSet(charactersIn: "ABC") - (cs as NSCharacterSet).mutableCopy() // this should not crash - } - - func testMutability_SR_1782() { - var nonAlphanumeric = CharacterSet.alphanumerics.inverted - nonAlphanumeric.remove(charactersIn: " ") // this should not crash - } - - func testRanges() { - // Simple range check - let asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)!...UnicodeScalar(0x5A)!) - expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)!)) - expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!)) - expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!)) - expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5B)!)) - - // Some string filtering tests - let asciiLowercase = CharacterSet(charactersIn: UnicodeScalar(0x61)!...UnicodeScalar(0x7B)!) - let testString = "helloHELLOhello" - let expected = "HELLO" - - let result = testString.trimmingCharacters(in: asciiLowercase) - expectEqual(result, expected) - } - - func testClosedRanges_SR_2988() { - // "CharacterSet.insert(charactersIn: ClosedRange) crashes on a closed ClosedRange containing U+D7FF" - let problematicChar = UnicodeScalar(0xD7FF)! - let range = capitalA...problematicChar - var characters = CharacterSet(charactersIn: range) // this should not crash - expectTrue(characters.contains(problematicChar)) - characters.remove(charactersIn: range) // this should not crash - expectTrue(!characters.contains(problematicChar)) - characters.insert(charactersIn: range) // this should not crash - expectTrue(characters.contains(problematicChar)) - } - - func testUpperBoundaryInsert_SR_2988() { - // "CharacterSet.insert(_: Unicode.Scalar) crashes on U+D7FF" - let problematicChar = UnicodeScalar(0xD7FF)! - var characters = CharacterSet() - characters.insert(problematicChar) // this should not crash - expectTrue(characters.contains(problematicChar)) - characters.remove(problematicChar) // this should not crash - expectTrue(!characters.contains(problematicChar)) - } - - func testInsertAndRemove() { - var asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)!...UnicodeScalar(0x5A)!) - expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)!)) - expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!)) - expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!)) - - asciiUppercase.remove(UnicodeScalar(0x49)!) - expectTrue(!asciiUppercase.contains(UnicodeScalar(0x49)!)) - expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!)) - expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!)) - - - // Zero-length range - asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)!..? { - willSet { - if let p = _pointer { free(p.baseAddress) } - } - } - - init(length : Int) { - _length = length - super.init() - } - - required init?(coder aDecoder: NSCoder) { - // Not tested - fatalError() - } - - deinit { - if let p = _pointer { - free(p.baseAddress) - } - } - - override var length : Int { - get { - return _length - } - } - - override var bytes : UnsafeRawPointer { - if let d = _pointer { - return UnsafeRawPointer(d.baseAddress!) - } else { - // Need to allocate the buffer now. - // It doesn't matter if the buffer is uniquely referenced or not here. - let buffer = malloc(length) - memset(buffer, 1, length) - let bytePtr = buffer!.bindMemory(to: UInt8.self, capacity: length) - let result = UnsafeMutableBufferPointer(start: bytePtr, count: length) - _pointer = result - return UnsafeRawPointer(result.baseAddress!) - } - } - - override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { - if let d = _pointer { - // Get the real data from the buffer - memmove(buffer, d.baseAddress, length) - } else { - // A more efficient implementation of getBytes in the case where no one has asked for our backing bytes - memset(buffer, 1, length) - } - } - - override func copy(with zone: NSZone? = nil) -> Any { - return self - } - - override func mutableCopy(with zone: NSZone? = nil) -> Any { - return AllOnesData(length: _length) - } - } - - - class AllOnesData : NSMutableData { - - private var _length : Int - var _pointer : UnsafeMutableBufferPointer? { - willSet { - if let p = _pointer { free(p.baseAddress) } - } - } - - override init(length : Int) { - _length = length - super.init() - } - - required init?(coder aDecoder: NSCoder) { - // Not tested - fatalError() - } - - deinit { - if let p = _pointer { - free(p.baseAddress) - } - } - - override var length : Int { - get { - return _length - } - set { - if let ptr = _pointer { - // Copy the data to our new length buffer - let newBuffer = malloc(newValue)! - if newValue <= _length { - memmove(newBuffer, ptr.baseAddress, newValue) - } else if newValue > _length { - memmove(newBuffer, ptr.baseAddress, _length) - memset(newBuffer + _length, 1, newValue - _length) - } - let bytePtr = newBuffer.bindMemory(to: UInt8.self, capacity: newValue) - _pointer = UnsafeMutableBufferPointer(start: bytePtr, count: newValue) - } - _length = newValue - } - } - - override var bytes : UnsafeRawPointer { - if let d = _pointer { - return UnsafeRawPointer(d.baseAddress!) - } else { - // Need to allocate the buffer now. - // It doesn't matter if the buffer is uniquely referenced or not here. - let buffer = malloc(length) - memset(buffer, 1, length) - let bytePtr = buffer!.bindMemory(to: UInt8.self, capacity: length) - let result = UnsafeMutableBufferPointer(start: bytePtr, count: length) - _pointer = result - return UnsafeRawPointer(result.baseAddress!) - } - } - - override var mutableBytes: UnsafeMutableRawPointer { - let newBufferLength = _length - let newBuffer = malloc(newBufferLength) - if let ptr = _pointer { - // Copy the existing data to the new box, then return its pointer - memmove(newBuffer, ptr.baseAddress, newBufferLength) - } else { - // Set new data to 1s - memset(newBuffer, 1, newBufferLength) - } - let bytePtr = newBuffer!.bindMemory(to: UInt8.self, capacity: newBufferLength) - let result = UnsafeMutableBufferPointer(start: bytePtr, count: newBufferLength) - _pointer = result - _length = newBufferLength - return UnsafeMutableRawPointer(result.baseAddress!) - } - - override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { - if let d = _pointer { - // Get the real data from the buffer - memmove(buffer, d.baseAddress, length) - } else { - // A more efficient implementation of getBytes in the case where no one has asked for our backing bytes - memset(buffer, 1, length) - } - } - } - - var heldData: Data? - - // this holds a reference while applying the function which forces the internal ref type to become non-uniquely referenced - func holdReference(_ data: Data, apply: () -> Void) { - heldData = data - apply() - heldData = nil - } - - // MARK: - - - // String of course has its own way to get data, but this way tests our own data struct - func dataFrom(_ string : String) -> Data { - // Create a Data out of those bytes - return string.utf8CString.withUnsafeBufferPointer { (ptr) in - ptr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: ptr.count) { - // Subtract 1 so we don't get the null terminator byte. This matches NSString behavior. - return Data(bytes: $0, count: ptr.count - 1) - } - } - } - - // MARK: - - - func testBasicConstruction() { - - // Make sure that we were able to create some data - let hello = dataFrom("hello") - let helloLength = hello.count - expectEqual(hello[0], 0x68, "Unexpected first byte") - - let world = dataFrom(" world") - var helloWorld = hello - world.withUnsafeBytes { - helloWorld.append($0, count: world.count) - } - - expectEqual(hello[0], 0x68, "First byte should not have changed") - expectEqual(hello.count, helloLength, "Length of first data should not have changed") - expectEqual(helloWorld.count, hello.count + world.count, "The total length should include both buffers") - } - - func testInitializationWithArray() { - let data = Data(bytes: [1, 2, 3]) - expectEqual(3, data.count) - - let data2 = Data(bytes: [1, 2, 3].filter { $0 >= 2 }) - expectEqual(2, data2.count) - - let data3 = Data(bytes: [1, 2, 3, 4, 5][1..<3]) - expectEqual(2, data3.count) - } - - func testInitializationWithBufferPointer() { - let nilBuffer = UnsafeBufferPointer(start: nil, count: 0) - let data = Data(buffer: nilBuffer) - expectEqual(data, Data()) - - let validPointer = UnsafeMutablePointer.allocate(capacity: 2) - validPointer[0] = 0xCA - validPointer[1] = 0xFE - defer { validPointer.deallocate() } - - let emptyBuffer = UnsafeBufferPointer(start: validPointer, count: 0) - let data2 = Data(buffer: emptyBuffer) - expectEqual(data2, Data()) - - let shortBuffer = UnsafeBufferPointer(start: validPointer, count: 1) - let data3 = Data(buffer: shortBuffer) - expectEqual(data3, Data([0xCA])) - - let fullBuffer = UnsafeBufferPointer(start: validPointer, count: 2) - let data4 = Data(buffer: fullBuffer) - expectEqual(data4, Data([0xCA, 0xFE])) - - let tuple: (UInt16, UInt16, UInt16, UInt16) = (0xFF, 0xFE, 0xFD, 0xFC) - withUnsafeBytes(of: tuple) { - // If necessary, port this to big-endian. - let tupleBuffer: UnsafeBufferPointer = $0.bindMemory(to: UInt8.self) - let data5 = Data(buffer: tupleBuffer) - expectEqual(data5, Data([0xFF, 0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFC, 0x00])) - } - } - - func testInitializationWithMutableBufferPointer() { - let nilBuffer = UnsafeMutableBufferPointer(start: nil, count: 0) - let data = Data(buffer: nilBuffer) - expectEqual(data, Data()) - - let validPointer = UnsafeMutablePointer.allocate(capacity: 2) - validPointer[0] = 0xCA - validPointer[1] = 0xFE - defer { validPointer.deallocate() } - - let emptyBuffer = UnsafeMutableBufferPointer(start: validPointer, count: 0) - let data2 = Data(buffer: emptyBuffer) - expectEqual(data2, Data()) - - let shortBuffer = UnsafeMutableBufferPointer(start: validPointer, count: 1) - let data3 = Data(buffer: shortBuffer) - expectEqual(data3, Data([0xCA])) - - let fullBuffer = UnsafeMutableBufferPointer(start: validPointer, count: 2) - let data4 = Data(buffer: fullBuffer) - expectEqual(data4, Data([0xCA, 0xFE])) - - var tuple: (UInt16, UInt16, UInt16, UInt16) = (0xFF, 0xFE, 0xFD, 0xFC) - withUnsafeMutableBytes(of: &tuple) { - // If necessary, port this to big-endian. - let tupleBuffer: UnsafeMutableBufferPointer = $0.bindMemory(to: UInt8.self) - let data5 = Data(buffer: tupleBuffer) - expectEqual(data5, Data([0xFF, 0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFC, 0x00])) - } - } - - func testMutableData() { - let hello = dataFrom("hello") - let helloLength = hello.count - expectEqual(hello[0], 0x68, "Unexpected first byte") - - // Double the length - var mutatingHello = hello - mutatingHello.count *= 2 - - expectEqual(hello.count, helloLength, "The length of the initial data should not have changed") - expectEqual(mutatingHello.count, helloLength * 2, "The length should have changed") - - // Get the underlying data for hello2 - mutatingHello.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) in - expectEqual(bytes.pointee, 0x68, "First byte should be 0x68") - - // Mutate it - bytes.pointee = 0x67 - expectEqual(bytes.pointee, 0x67, "First byte should be 0x67") - - // Verify that the first data is still correct - expectEqual(hello[0], 0x68, "The first byte should still be 0x68") - } - } - - func testCustomData() { - let length = 5 - let allOnesData = Data(referencing: AllOnesData(length: length)) - expectEqual(1, allOnesData[0], "First byte of all 1s data should be 1") - - // Double the length - var allOnesCopyToMutate = allOnesData - allOnesCopyToMutate.count = allOnesData.count * 2 - - expectEqual(allOnesData.count, length, "The length of the initial data should not have changed") - expectEqual(allOnesCopyToMutate.count, length * 2, "The length should have changed") - - // Force the second data to create its storage - allOnesCopyToMutate.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) in - expectEqual(bytes.pointee, 1, "First byte should be 1") - - // Mutate the second data - bytes.pointee = 0 - expectEqual(bytes.pointee, 0, "First byte should be 0") - - // Verify that the first data is still 1 - expectEqual(allOnesData[0], 1, "The first byte should still be 1") - } - - } - - func testBridgingDefault() { - let hello = dataFrom("hello") - // Convert from struct Data to NSData - if let s = NSString(data: hello, encoding: String.Encoding.utf8.rawValue) { - expectTrue(s.isEqual(to: "hello"), "The strings should be equal") - } - - // Convert from NSData to struct Data - let goodbye = dataFrom("goodbye") - if let resultingData = NSString(string: "goodbye").data(using: String.Encoding.utf8.rawValue) { - expectEqual(resultingData[0], goodbye[0], "First byte should be equal") - } - } - - func testBridgingMutable() { - // Create a mutable data - var helloWorld = dataFrom("hello") - helloWorld.append(dataFrom("world")) - - // Convert from struct Data to NSData - if let s = NSString(data: helloWorld, encoding: String.Encoding.utf8.rawValue) { - expectTrue(s.isEqual(to: "helloworld"), "The strings should be equal") - } - - } - - func testBridgingCustom() { - // Let's use an AllOnesData with some Objective-C code - let allOnes = AllOnesData(length: 64) - - // Type-erased - let data = Data(referencing: allOnes) - - // Create a home for our test data - let dirPath = (NSTemporaryDirectory() as NSString).appendingPathComponent(NSUUID().uuidString) - try! FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) - let filePath = (dirPath as NSString).appendingPathComponent("temp_file") - guard FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil) else { expectTrue(false, "Unable to create temporary file"); return} - guard let fh = FileHandle(forWritingAtPath: filePath) else { expectTrue(false, "Unable to open temporary file"); return } - defer { try! FileManager.default.removeItem(atPath: dirPath) } - - // Now use this data with some Objective-C code that takes NSData arguments - fh.write(data) - - // Get the data back - do { - let url = URL(fileURLWithPath: filePath) - let readData = try Data.init(contentsOf: url) - expectEqual(data.count, readData.count, "The length of the data is not the same") - } catch { - expectTrue(false, "Unable to read back data") - return - } - } - - func testEquality() { - let d1 = dataFrom("hello") - let d2 = dataFrom("hello") - - // Use == explicitly here to make sure we're calling the right methods - expectTrue(d1 == d2, "Data should be equal") - } - - func testDataInSet() { - let d1 = dataFrom("Hello") - let d2 = dataFrom("Hello") - let d3 = dataFrom("World") - - var s = Set() - s.insert(d1) - s.insert(d2) - s.insert(d3) - - expectEqual(s.count, 2, "Expected only two entries in the Set") - } - - func testReplaceSubrange() { - var hello = dataFrom("Hello") - let world = dataFrom("World") - - hello[0] = world[0] - expectEqual(hello[0], world[0]) - - var goodbyeWorld = dataFrom("Hello World") - let goodbye = dataFrom("Goodbye") - let expected = dataFrom("Goodbye World") - - goodbyeWorld.replaceSubrange(0..<5, with: goodbye) - expectEqual(goodbyeWorld, expected) - } - - func testReplaceSubrange2() { - let hello = dataFrom("Hello") - let world = dataFrom(" World") - let goodbye = dataFrom("Goodbye") - let expected = dataFrom("Goodbye World") - - var mutateMe = hello - mutateMe.append(world) - - if let found = mutateMe.range(of: hello) { - mutateMe.replaceSubrange(found, with: goodbye) - } - expectEqual(mutateMe, expected) - } - - func testReplaceSubrange3() { - // The expected result - let expectedBytes : [UInt8] = [1, 2, 9, 10, 11, 12, 13] - let expected = expectedBytes.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - // The data we'll mutate - let someBytes : [UInt8] = [1, 2, 3, 4, 5] - var a = someBytes.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - // The bytes we'll insert - let b : [UInt8] = [9, 10, 11, 12, 13] - b.withUnsafeBufferPointer { - a.replaceSubrange(2..<5, with: $0) - } - expectEqual(expected, a) - } - - func testReplaceSubrange4() { - let expectedBytes : [UInt8] = [1, 2, 9, 10, 11, 12, 13] - let expected = Data(bytes: expectedBytes) - - // The data we'll mutate - let someBytes : [UInt8] = [1, 2, 3, 4, 5] - var a = Data(bytes: someBytes) - - // The bytes we'll insert - let b : [UInt8] = [9, 10, 11, 12, 13] - a.replaceSubrange(2..<5, with: b) - expectEqual(expected, a) - } - - func testReplaceSubrange5() { - var d = Data(bytes: [1, 2, 3]) - d.replaceSubrange(0..<0, with: [4]) - expectEqual(Data(bytes: [4, 1, 2, 3]), d) - - d.replaceSubrange(0..<4, with: [9]) - expectEqual(Data(bytes: [9]), d) - - d.replaceSubrange(0..= 65 && byte <= 90 } - - let allCaps = hello.filter(isCapital) - expectEqual(allCaps.count, 2) - - let capCount = hello.reduce(0) { isCapital($1) ? $0 + 1 : $0 } - expectEqual(capCount, 2) - - let allLower = hello.map { isCapital($0) ? $0 + 31 : $0 } - expectEqual(allLower.count, hello.count) - } - - func testCustomDeallocator() { - var deallocatorCalled = false - - // Scope the data to a block to control lifecycle - autoreleasepool { - let buffer = malloc(16)! - let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: 16) - var data = Data(bytesNoCopy: bytePtr, count: 16, deallocator: .custom({ (ptr, size) in - deallocatorCalled = true - free(UnsafeMutableRawPointer(ptr)) - })) - // Use the data - data[0] = 1 - } - - expectTrue(deallocatorCalled, "Custom deallocator was never called") - } - - func testCopyBytes() { - let c = 10 - let underlyingBuffer = malloc(c * MemoryLayout.stride)! - let u16Ptr = underlyingBuffer.bindMemory(to: UInt16.self, capacity: c) - let buffer = UnsafeMutableBufferPointer(start: u16Ptr, count: c) - - buffer[0] = 0 - buffer[1] = 0 - - var data = Data(capacity: c * MemoryLayout.stride) - data.resetBytes(in: 0...stride) - data[0] = 0xFF - data[1] = 0xFF - let copiedCount = data.copyBytes(to: buffer) - expectEqual(copiedCount, c * MemoryLayout.stride) - - expectEqual(buffer[0], 0xFFFF) - free(underlyingBuffer) - } - - func testCopyBytes_undersized() { - let a : [UInt8] = [1, 2, 3, 4, 5] - var data = a.withUnsafeBufferPointer { - return Data(buffer: $0) - } - let expectedSize = MemoryLayout.stride * a.count - expectEqual(expectedSize, data.count) - - let underlyingBuffer = unsafeBitCast(malloc(expectedSize - 1)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize - 1) - - // We should only copy in enough bytes that can fit in the buffer - let copiedCount = data.copyBytes(to: buffer) - expectEqual(expectedSize - 1, copiedCount) - - var index = 0 - for v in a[0...stride * a.count - expectEqual(expectedSize, data.count) - - let underlyingBuffer = unsafeBitCast(malloc(expectedSize + 1)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize + 1) - - let copiedCount = data.copyBytes(to: buffer) - expectEqual(expectedSize, copiedCount) - - free(underlyingBuffer) - } - - func testCopyBytes_ranges() { - - do { - // Equal sized buffer, data - let a : [UInt8] = [1, 2, 3, 4, 5] - var data = a.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - let underlyingBuffer = unsafeBitCast(malloc(data.count)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: data.count) - - var copiedCount : Int - - copiedCount = data.copyBytes(to: buffer, from: 0..<0) - expectEqual(0, copiedCount) - - copiedCount = data.copyBytes(to: buffer, from: 1..<1) - expectEqual(0, copiedCount) - - copiedCount = data.copyBytes(to: buffer, from: 0..<3) - expectEqual((0..<3).count, copiedCount) - - var index = 0 - for v in a[0..<3] { - expectEqual(v, buffer[index]) - index += 1 - } - free(underlyingBuffer) - } - - do { - // Larger buffer than data - let a : [UInt8] = [1, 2, 3, 4] - let data = a.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - let underlyingBuffer = unsafeBitCast(malloc(10)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: 10) - - var copiedCount : Int - - copiedCount = data.copyBytes(to: buffer, from: 0..<3) - expectEqual((0..<3).count, copiedCount) - - var index = 0 - for v in a[0..<3] { - expectEqual(v, buffer[index]) - index += 1 - } - free(underlyingBuffer) - } - - do { - // Larger data than buffer - let a : [UInt8] = [1, 2, 3, 4, 5, 6] - let data = a.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - let underlyingBuffer = unsafeBitCast(malloc(4)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: 4) - - var copiedCount : Int - - copiedCount = data.copyBytes(to: buffer, from: 0...stride * a.count - expectEqual(expectedSize, data.count) - - [false, true].withUnsafeBufferPointer { - data.append($0) - } - - expectedSize += MemoryLayout.stride * 2 - expectEqual(expectedSize, data.count) - - let underlyingBuffer = unsafeBitCast(malloc(expectedSize)!, to: UnsafeMutablePointer.self) - - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize) - let copiedCount = data.copyBytes(to: buffer) - expectEqual(copiedCount, expectedSize) - - free(underlyingBuffer) - } - - func test_basicDataMutation() { - let object = ImmutableDataVerifier() - - object.verifier.reset() - var data = object as Data - expectTrue(object.verifier.wasCopied) - expectFalse(object.verifier.wasMutableCopied, "Expected an invocation to mutableCopy") - - object.verifier.reset() - expectTrue(data.count == object.length) - expectFalse(object.verifier.wasCopied, "Expected an invocation to copy") - } - - func test_basicMutableDataMutation() { - let object = MutableDataVerifier() - - object.verifier.reset() - var data = object as Data - expectTrue(object.verifier.wasCopied) - expectFalse(object.verifier.wasMutableCopied, "Expected an invocation to mutableCopy") - - object.verifier.reset() - expectTrue(data.count == object.length) - expectFalse(object.verifier.wasCopied, "Expected an invocation to copy") - } - - func test_passing() { - let object = ImmutableDataVerifier() - let object_notPeepholed = object as Data - takesData(object_notPeepholed) - expectTrue(object.verifier.wasCopied) - } - - func test_passing_peepholed() { - let object = ImmutableDataVerifier() - takesData(object as Data) - expectFalse(object.verifier.wasCopied) // because of the peephole - } - - // intentionally structured so sizeof() != strideof() - struct MyStruct { - var time: UInt64 - let x: UInt32 - let y: UInt32 - let z: UInt32 - init() { - time = 0 - x = 1 - y = 2 - z = 3 - } - } - - func test_bufferSizeCalculation() { - // Make sure that Data is correctly using strideof instead of sizeof. - // n.b. if sizeof(MyStruct) == strideof(MyStruct), this test is not as useful as it could be - - // init - let stuff = [MyStruct(), MyStruct(), MyStruct()] - var data = stuff.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - expectEqual(data.count, MemoryLayout.stride * 3) - - - // append - stuff.withUnsafeBufferPointer { - data.append($0) - } - - expectEqual(data.count, MemoryLayout.stride * 6) - - // copyBytes - do { - // equal size - let underlyingBuffer = malloc(6 * MemoryLayout.stride)! - defer { free(underlyingBuffer) } - - let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 6) - let buffer = UnsafeMutableBufferPointer(start: ptr, count: 6) - - let byteCount = data.copyBytes(to: buffer) - expectEqual(6 * MemoryLayout.stride, byteCount) - } - - do { - // undersized - let underlyingBuffer = malloc(3 * MemoryLayout.stride)! - defer { free(underlyingBuffer) } - - let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 3) - let buffer = UnsafeMutableBufferPointer(start: ptr, count: 3) - - let byteCount = data.copyBytes(to: buffer) - expectEqual(3 * MemoryLayout.stride, byteCount) - } - - do { - // oversized - let underlyingBuffer = malloc(12 * MemoryLayout.stride)! - defer { free(underlyingBuffer) } - - let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 6) - let buffer = UnsafeMutableBufferPointer(start: ptr, count: 6) - - let byteCount = data.copyBytes(to: buffer) - expectEqual(6 * MemoryLayout.stride, byteCount) - } - } - - - // MARK: - - func test_classForCoder() { - // confirm internal bridged impl types are not exposed to archival machinery - let d = Data() as NSData - let expected: AnyClass = NSData.self as AnyClass - expectTrue(d.classForCoder == expected) - expectTrue(d.classForKeyedArchiver == expected) - } - - func test_AnyHashableContainingData() { - let values: [Data] = [ - Data(base64Encoded: "AAAA")!, - Data(base64Encoded: "AAAB")!, - Data(base64Encoded: "AAAB")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Data.self, type(of: anyHashables[0].base)) - expectEqual(Data.self, type(of: anyHashables[1].base)) - expectEqual(Data.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSData() { - let values: [NSData] = [ - NSData(base64Encoded: "AAAA")!, - NSData(base64Encoded: "AAAB")!, - NSData(base64Encoded: "AAAB")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Data.self, type(of: anyHashables[0].base)) - expectEqual(Data.self, type(of: anyHashables[1].base)) - expectEqual(Data.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_noCopyBehavior() { - let ptr = UnsafeMutableRawPointer.allocate(byteCount: 17, alignment: 1) - - var deallocated = false - autoreleasepool { - let data = Data(bytesNoCopy: ptr, count: 17, deallocator: .custom({ (bytes, length) in - deallocated = true - ptr.deallocate() - })) - expectFalse(deallocated) - let equal = data.withUnsafeBytes { (bytes: UnsafePointer) -> Bool in - return ptr == UnsafeMutableRawPointer(mutating: bytes) - } - - expectTrue(equal) - } - expectTrue(deallocated) - } - - func test_doubleDeallocation() { - let data = "12345679".data(using: .utf8)! - let len = data.withUnsafeBytes { (bytes: UnsafePointer) -> Int in - let slice = Data(bytesNoCopy: UnsafeMutablePointer(mutating: bytes), count: 1, deallocator: .none) - return slice.count - } - expectEqual(len, 1) - } - - func test_repeatingValueInitialization() { - var d = Data(repeating: 0x01, count: 3) - let elements = repeatElement(UInt8(0x02), count: 3) // ensure we fall into the sequence case - d.append(contentsOf: elements) - - expectEqual(d[0], 0x01) - expectEqual(d[1], 0x01) - expectEqual(d[2], 0x01) - - expectEqual(d[3], 0x02) - expectEqual(d[4], 0x02) - expectEqual(d[5], 0x02) - } - - func test_rangeSlice() { - var a: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7] - var d = Data(bytes: a) - for i in 0..? = nil, - expectedStartIndex: Int?, - _ message: @autoclosure () -> String = "", - file: String = #file, line: UInt = #line) { - if let index = expectedStartIndex { - let expectedRange: Range = index..<(index + fragment.count) - if let someRange = range { - expectEqual(data.firstRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line) - } else { - expectEqual(data.firstRange(of: fragment), expectedRange, message(), file: file, line: line) - } - } else { - if let someRange = range { - expectNil(data.firstRange(of: fragment, in: someRange), message(), file: file, line: line) - } else { - expectNil(data.firstRange(of: fragment), message(), file: file, line: line) - } - } - } - - assertFirstRange(base, base, expectedStartIndex: base.startIndex) - assertFirstRange(base, subdata, expectedStartIndex: 2) - assertFirstRange(base, oneByte, expectedStartIndex: 2) - - assertFirstRange(subdata, base, expectedStartIndex: nil) - assertFirstRange(subdata, subdata, expectedStartIndex: subdata.startIndex) - assertFirstRange(subdata, oneByte, expectedStartIndex: subdata.startIndex) - - assertFirstRange(oneByte, base, expectedStartIndex: nil) - assertFirstRange(oneByte, subdata, expectedStartIndex: nil) - assertFirstRange(oneByte, oneByte, expectedStartIndex: oneByte.startIndex) - - assertFirstRange(base, subdata, range: 1...14, expectedStartIndex: 2) - assertFirstRange(base, subdata, range: 6...8, expectedStartIndex: 6) - assertFirstRange(base, subdata, range: 8...10, expectedStartIndex: nil) - - assertFirstRange(base, oneByte, range: 1...14, expectedStartIndex: 2) - assertFirstRange(base, oneByte, range: 6...6, expectedStartIndex: 6) - assertFirstRange(base, oneByte, range: 8...9, expectedStartIndex: nil) - } - - do { // lastRange(of:in:) - func assertLastRange(_ data: Data, _ fragment: Data, range: ClosedRange? = nil, - expectedStartIndex: Int?, - _ message: @autoclosure () -> String = "", - file: String = #file, line: UInt = #line) { - if let index = expectedStartIndex { - let expectedRange: Range = index..<(index + fragment.count) - if let someRange = range { - expectEqual(data.lastRange(of: fragment, in: someRange), expectedRange, message(), file: file, line: line) - } else { - expectEqual(data.lastRange(of: fragment), expectedRange, message(), file: file, line: line) - } - } else { - if let someRange = range { - expectNil(data.lastRange(of: fragment, in: someRange), message(), file: file, line: line) - } else { - expectNil(data.lastRange(of: fragment), message(), file: file, line: line) - } - } - } - - assertLastRange(base, base, expectedStartIndex: base.startIndex) - assertLastRange(base, subdata, expectedStartIndex: 10) - assertLastRange(base, oneByte, expectedStartIndex: 14) - - assertLastRange(subdata, base, expectedStartIndex: nil) - assertLastRange(subdata, subdata, expectedStartIndex: subdata.startIndex) - assertLastRange(subdata, oneByte, expectedStartIndex: subdata.startIndex) - - assertLastRange(oneByte, base, expectedStartIndex: nil) - assertLastRange(oneByte, subdata, expectedStartIndex: nil) - assertLastRange(oneByte, oneByte, expectedStartIndex: oneByte.startIndex) - - assertLastRange(base, subdata, range: 1...14, expectedStartIndex: 10) - assertLastRange(base, subdata, range: 6...8, expectedStartIndex: 6) - assertLastRange(base, subdata, range: 8...10, expectedStartIndex: nil) - - assertLastRange(base, oneByte, range: 1...14, expectedStartIndex: 14) - assertLastRange(base, oneByte, range: 6...6, expectedStartIndex: 6) - assertLastRange(base, oneByte, range: 8...9, expectedStartIndex: nil) - } - } - - func test_sliceAppending() { - // https://bugs.swift.org/browse/SR-4473 - var fooData = Data() - let barData = Data([0, 1, 2, 3, 4, 5]) - let slice = barData.suffix(from: 3) - fooData.append(slice) - expectEqual(fooData[0], 0x03) - expectEqual(fooData[1], 0x04) - expectEqual(fooData[2], 0x05) - } - - func test_replaceSubrange() { - // https://bugs.swift.org/browse/SR-4462 - let data = Data(bytes: [0x01, 0x02]) - var dataII = Data(base64Encoded: data.base64EncodedString())! - dataII.replaceSubrange(0..<1, with: Data()) - expectEqual(dataII[0], 0x02) - } - - func test_sliceWithUnsafeBytes() { - let base = Data([0, 1, 2, 3, 4, 5]) - let slice = base[2..<4] - let segment = slice.withUnsafeBytes { (ptr: UnsafePointer) -> [UInt8] in - return [ptr.pointee, ptr.advanced(by: 1).pointee] - } - expectEqual(segment, [UInt8(2), UInt8(3)]) - } - - func test_sliceIteration() { - let base = Data([0, 1, 2, 3, 4, 5]) - let slice = base[2..<4] - var found = [UInt8]() - for byte in slice { - found.append(byte) - } - expectEqual(found[0], 2) - expectEqual(found[1], 3) - } - - func test_unconditionallyBridgeFromObjectiveC() { - expectEqual(Data(), Data._unconditionallyBridgeFromObjectiveC(nil)) - } - - func test_sliceIndexing() { - let d = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) - let slice = d[5..<10] - expectEqual(slice[5], d[5]) - } - - func test_sliceEquality() { - let d = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) - let slice = d[5..<7] - let expected = Data(bytes: [5, 6]) - expectEqual(expected, slice) - } - - func test_sliceEquality2() { - let d = Data(bytes: [5, 6, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) - let slice1 = d[0..<2] - let slice2 = d[5..<7] - expectEqual(slice1, slice2) - } - - func test_splittingHttp() { - func split(_ data: Data, on delimiter: String) -> [Data] { - let dataDelimiter = delimiter.data(using: .utf8)! - var found = [Data]() - let start = data.startIndex - let end = data.endIndex.advanced(by: -dataDelimiter.count) - guard end >= start else { return [data] } - var index = start - var previousIndex = index - while index < end { - let slice = data[index..(_:)` -- a discontiguous sequence of unknown length. - func test_appendingNonContiguousSequence_underestimatedCount() { - var d = Data() - - // d should go from .empty representation to .inline. - // Appending a small enough sequence to fit in .inline should actually be copied. - d.append(contentsOf: (0x00...0x01).makeIterator()) // `.makeIterator()` produces a sequence whose `.underestimatedCount` is 0. - expectEqual(Data([0x00, 0x01]), d) - - // Appending another small sequence should similarly still work. - d.append(contentsOf: (0x02...0x02).makeIterator()) // `.makeIterator()` produces a sequence whose `.underestimatedCount` is 0. - expectEqual(Data([0x00, 0x01, 0x02]), d) - - // If we append a sequence of elements larger than a single InlineData, the internal append here should buffer. - // We want to make sure that buffering in this way does not accidentally drop trailing elements on the floor. - d.append(contentsOf: (0x03...0x2F).makeIterator()) // `.makeIterator()` produces a sequence whose `.underestimatedCount` is 0. - expectEqual(Data([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F]), d) - } - - func test_sequenceInitializers() { - let seq = repeatElement(UInt8(0x02), count: 3) // ensure we fall into the sequence case - - let dataFromSeq = Data(seq) - expectEqual(3, dataFromSeq.count) - expectEqual(UInt8(0x02), dataFromSeq[0]) - expectEqual(UInt8(0x02), dataFromSeq[1]) - expectEqual(UInt8(0x02), dataFromSeq[2]) - - let array: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - - let dataFromArray = Data(array) - expectEqual(array.count, dataFromArray.count) - expectEqual(array[0], dataFromArray[0]) - expectEqual(array[1], dataFromArray[1]) - expectEqual(array[2], dataFromArray[2]) - expectEqual(array[3], dataFromArray[3]) - - let slice = array[1..<4] - - let dataFromSlice = Data(slice) - expectEqual(slice.count, dataFromSlice.count) - expectEqual(slice.first, dataFromSlice.first) - expectEqual(slice.last, dataFromSlice.last) - - let data = Data(bytes: [1, 2, 3, 4, 5, 6, 7, 8, 9]) - - let dataFromData = Data(data) - expectEqual(data, dataFromData) - - let sliceOfData = data[1..<3] - - let dataFromSliceOfData = Data(sliceOfData) - expectEqual(sliceOfData, dataFromSliceOfData) - } - - func test_reversedDataInit() { - let data = Data(bytes: [1, 2, 3, 4, 5, 6, 7, 8, 9]) - let reversedData = Data(data.reversed()) - let expected = Data(bytes: [9, 8, 7, 6, 5, 4, 3, 2, 1]) - expectEqual(expected, reversedData) - } - - func test_replaceSubrangeReferencingMutable() { - let mdataObj = NSMutableData(bytes: [0x01, 0x02, 0x03, 0x04], length: 4) - var data = Data(referencing: mdataObj) - let expected = data.count - data.replaceSubrange(4 ..< 4, with: Data(bytes: [])) - expectEqual(expected, data.count) - data.replaceSubrange(4 ..< 4, with: Data(bytes: [])) - expectEqual(expected, data.count) - } - - func test_replaceSubrangeReferencingImmutable() { - let dataObj = NSData(bytes: [0x01, 0x02, 0x03, 0x04], length: 4) - var data = Data(referencing: dataObj) - let expected = data.count - data.replaceSubrange(4 ..< 4, with: Data(bytes: [])) - expectEqual(expected, data.count) - data.replaceSubrange(4 ..< 4, with: Data(bytes: [])) - expectEqual(expected, data.count) - } - - func test_replaceSubrangeFromBridged() { - var data = NSData(bytes: [0x01, 0x02, 0x03, 0x04], length: 4) as Data - let expected = data.count - data.replaceSubrange(4 ..< 4, with: Data(bytes: [])) - expectEqual(expected, data.count) - data.replaceSubrange(4 ..< 4, with: Data(bytes: [])) - expectEqual(expected, data.count) - } - - func test_validateMutation_withUnsafeMutableBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - data.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 0xFF, 6, 7, 8, 9])) - } - - func test_validateMutation_appendBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - data.append("hello", count: 5) - expectEqual(data[data.startIndex.advanced(by: 5)], 0x5) - } - - func test_validateMutation_appendData() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - let other = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - data.append(other) - expectEqual(data[data.startIndex.advanced(by: 9)], 9) - expectEqual(data[data.startIndex.advanced(by: 10)], 0) - } - - func test_validateMutation_appendBuffer() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data[data.startIndex.advanced(by: 9)], 9) - expectEqual(data[data.startIndex.advanced(by: 10)], 0) - } - - func test_validateMutation_appendSequence() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - let seq = repeatElement(UInt8(1), count: 10) - data.append(contentsOf: seq) - expectEqual(data[data.startIndex.advanced(by: 9)], 9) - expectEqual(data[data.startIndex.advanced(by: 10)], 1) - } - - func test_validateMutation_appendContentsOf() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - data.append(contentsOf: bytes) - expectEqual(data[data.startIndex.advanced(by: 9)], 9) - expectEqual(data[data.startIndex.advanced(by: 10)], 0) - } - - func test_validateMutation_resetBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 0, 0, 0, 8, 9])) - } - - func test_validateMutation_replaceSubrange() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - let range: Range = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data, Data(bytes: [4, 0xFF, 6, 7, 8])) - } - - func test_validateMutation_slice_appendBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_appendData() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - let other = Data(bytes: [0xFF, 0xFF]) - data.append(other) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_appendBuffer() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_appendSequence() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - let seq = repeatElement(UInt8(0xFF), count: 2) - data.append(contentsOf: seq) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_appendContentsOf() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - data.append(contentsOf: bytes) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_resetBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [4, 0, 0, 0, 8])) - } - - func test_validateMutation_slice_replaceSubrange() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 0xFF, 6, 7, 8, 9])) - } - } - - func test_validateMutation_cow_appendBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - holdReference(data) { - data.append("hello", count: 5) - expectEqual(data[data.startIndex.advanced(by: 9)], 0x9) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x68) - } - } - - func test_validateMutation_cow_appendData() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - holdReference(data) { - let other = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - data.append(other) - expectEqual(data[data.startIndex.advanced(by: 9)], 9) - expectEqual(data[data.startIndex.advanced(by: 10)], 0) - } - } - - func test_validateMutation_cow_appendBuffer() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - holdReference(data) { - let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data[data.startIndex.advanced(by: 9)], 9) - expectEqual(data[data.startIndex.advanced(by: 10)], 0) - } - } - - func test_validateMutation_cow_appendSequence() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - holdReference(data) { - let seq = repeatElement(UInt8(1), count: 10) - data.append(contentsOf: seq) - expectEqual(data[data.startIndex.advanced(by: 9)], 9) - expectEqual(data[data.startIndex.advanced(by: 10)], 1) - } - } - - func test_validateMutation_cow_appendContentsOf() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - holdReference(data) { - let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - data.append(contentsOf: bytes) - expectEqual(data[data.startIndex.advanced(by: 9)], 9) - expectEqual(data[data.startIndex.advanced(by: 10)], 0) - } - } - - func test_validateMutation_cow_resetBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 0, 0, 0, 8, 9])) - } - } - - func test_validateMutation_cow_replaceSubrange() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data, Data(bytes: [4, 0xFF, 6, 7, 8])) - } - } - - func test_validateMutation_slice_cow_appendBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - holdReference(data) { - data.append("hello", count: 5) - expectEqual(data[data.startIndex.advanced(by: 4)], 0x8) - expectEqual(data[data.startIndex.advanced(by: 5)], 0x68) - } - } - - func test_validateMutation_slice_cow_appendData() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - holdReference(data) { - let other = Data(bytes: [0xFF, 0xFF]) - data.append(other) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_appendBuffer() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_appendSequence() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - holdReference(data) { - let seq = repeatElement(UInt8(0xFF), count: 2) - data.append(contentsOf: seq) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_appendContentsOf() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - data.append(contentsOf: bytes) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_resetBytes() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [4, 0, 0, 0, 8])) - } - } - - func test_validateMutation_slice_cow_replaceSubrange() { - var data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])[4..<9] - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 5)], 0xFF) - } - - func test_validateMutation_immutableBacking_appendBytes() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append("hello", count: 5) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 0x68) - } - - func test_validateMutation_immutableBacking_appendData() { - var data = NSData(bytes: "hello world", length: 11) as Data - let other = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - data.append(other) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 0) - } - - func test_validateMutation_immutableBacking_appendBuffer() { - var data = NSData(bytes: "hello world", length: 11) as Data - let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 0) - } - - func test_validateMutation_immutableBacking_appendSequence() { - var data = NSData(bytes: "hello world", length: 11) as Data - let seq = repeatElement(UInt8(1), count: 10) - data.append(contentsOf: seq) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 1) - } - - func test_validateMutation_immutableBacking_appendContentsOf() { - var data = NSData(bytes: "hello world", length: 11) as Data - let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - data.append(contentsOf: bytes) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 0) - } - - func test_validateMutation_immutableBacking_resetBytes() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x72, 0x6c, 0x64])) - } - - func test_validateMutation_immutableBacking_replaceSubrange() { - var data = NSData(bytes: "hello world", length: 11) as Data - let range: Range = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - - func test_validateMutation_slice_immutableBacking_appendBytes() { - let base: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = base.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_immutableBacking_appendData() { - let base: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = base.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_immutableBacking_appendBuffer() { - let base: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = base.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_immutableBacking_appendSequence() { - let base: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = base.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - data.append(contentsOf: repeatElement(UInt8(0xFF), count: 2)) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_immutableBacking_appendContentsOf() { - let base: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = base.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_immutableBacking_resetBytes() { - let base: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = base.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [4, 0, 0, 0, 8])) - } - - func test_validateMutation_slice_immutableBacking_replaceSubrange() { - let base: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = base.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - data.replaceSubrange(range, with: buffer) - } - expectEqual(data, Data(bytes: [4, 0xFF, 0xFF, 8])) - } - - func test_validateMutation_slice_immutableBacking_replaceSubrangeWithCollection() { - let base: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = base.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 5)], 0xFF) - } - } - - func test_validateMutation_cow_immutableBacking_appendBytes() { - var data = NSData(bytes: "hello world", length: 11) as Data - holdReference(data) { - data.append("hello", count: 5) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 0x68) - } - } - - func test_validateMutation_cow_immutableBacking_appendData() { - var data = NSData(bytes: "hello world", length: 11) as Data - holdReference(data) { - let other = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - data.append(other) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 0) - } - } - - func test_validateMutation_cow_immutableBacking_appendBuffer() { - var data = NSData(bytes: "hello world", length: 11) as Data - holdReference(data) { - let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 0) - } - } - - func test_validateMutation_cow_immutableBacking_appendSequence() { - var data = NSData(bytes: "hello world", length: 11) as Data - holdReference(data) { - let seq = repeatElement(UInt8(1), count: 10) - data.append(contentsOf: seq) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 1) - } - } - - func test_validateMutation_cow_immutableBacking_appendContentsOf() { - var data = NSData(bytes: "hello world", length: 11) as Data - holdReference(data) { - let bytes: [UInt8] = [1, 1, 2, 3, 4, 5, 6, 7, 8, 9] - data.append(contentsOf: bytes) - expectEqual(data[data.startIndex.advanced(by: 10)], 0x64) - expectEqual(data[data.startIndex.advanced(by: 11)], 1) - } - } - - func test_validateMutation_cow_immutableBacking_resetBytes() { - var data = NSData(bytes: "hello world", length: 11) as Data - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x72, 0x6c, 0x64])) - } - } - - func test_validateMutation_cow_immutableBacking_replaceSubrange() { - var data = NSData(bytes: "hello world", length: 11) as Data - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 1)..) in - data.replaceSubrange(range, with: buffer) - } - expectEqual(data, Data(bytes: [0x68, 0xff, 0xff, 0x64])) - } - } - - func test_validateMutation_cow_immutableBacking_replaceSubrangeWithCollection() { - var data = NSData(bytes: "hello world", length: 11) as Data - holdReference(data) { - let replacement: [UInt8] = [0xFF, 0xFF] - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - } - - func test_validateMutation_slice_cow_immutableBacking_appendBytes() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = baseBytes.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_immutableBacking_appendData() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = baseBytes.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - holdReference(data) { - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_immutableBacking_appendBuffer() { - var data = (NSData(bytes: "hello world", length: 11) as Data)[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer{ data.append($0) } - expectEqual(data, Data(bytes: [0x6f, 0x20, 0x77, 0x6f, 0x72, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_immutableBacking_appendSequence() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = baseBytes.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - holdReference(data) { - let bytes = repeatElement(UInt8(0xFF), count: 2) - data.append(contentsOf: bytes) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_immutableBacking_appendContentsOf() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = baseBytes.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - data.append(contentsOf: bytes) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_immutableBacking_resetBytes() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = baseBytes.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [4, 0, 0, 0, 8])) - } - } - - func test_validateMutation_slice_cow_immutableBacking_replaceSubrange() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - var data = baseBytes.withUnsafeBufferPointer { - return (NSData(bytes: $0.baseAddress!, length: $0.count) as Data)[4..<9] - } - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 5)], 0xFF) - } - - func test_validateMutation_mutableBacking_appendBytes() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var data = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - data.append(contentsOf: [7, 8, 9]) - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF])) - } - - func test_validateMutation_mutableBacking_appendData() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var data = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - data.append(contentsOf: [7, 8, 9]) - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF])) - } - - func test_validateMutation_mutableBacking_appendBuffer() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var data = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - data.append(contentsOf: [7, 8, 9]) - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF])) - } - - func test_validateMutation_mutableBacking_appendSequence() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var data = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - data.append(contentsOf: [7, 8, 9]) - data.append(contentsOf: repeatElement(UInt8(0xFF), count: 2)) - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF])) - } - - func test_validateMutation_mutableBacking_appendContentsOf() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var data = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - data.append(contentsOf: [7, 8, 9]) - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF])) - } - - func test_validateMutation_mutableBacking_resetBytes() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var data = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - data.append(contentsOf: [7, 8, 9]) - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [0, 1, 2, 3, 4, 0, 0, 0, 8, 9])) - } - - func test_validateMutation_mutableBacking_replaceSubrange() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var data = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - data.append(contentsOf: [7, 8, 9]) - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - - func test_validateMutation_slice_mutableBacking_appendBytes() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_mutableBacking_appendData() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_mutableBacking_appendBuffer() { - var base = NSData(bytes: "hello world", length: 11) as Data - base.append(contentsOf: [1, 2, 3, 4, 5, 6]) - var data = base[4..<9] - let bytes: [UInt8] = [1, 2, 3] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [0x6f, 0x20, 0x77, 0x6f, 0x72, 0x1, 0x2, 0x3])) - } - - func test_validateMutation_slice_mutableBacking_appendSequence() { - var base = NSData(bytes: "hello world", length: 11) as Data - base.append(contentsOf: [1, 2, 3, 4, 5, 6]) - var data = base[4..<9] - let seq = repeatElement(UInt8(1), count: 3) - data.append(contentsOf: seq) - expectEqual(data, Data(bytes: [0x6f, 0x20, 0x77, 0x6f, 0x72, 0x1, 0x1, 0x1])) - } - - func test_validateMutation_slice_mutableBacking_appendContentsOf() { - var base = NSData(bytes: "hello world", length: 11) as Data - base.append(contentsOf: [1, 2, 3, 4, 5, 6]) - var data = base[4..<9] - let bytes: [UInt8] = [1, 2, 3] - data.append(contentsOf: bytes) - expectEqual(data, Data(bytes: [0x6f, 0x20, 0x77, 0x6f, 0x72, 0x1, 0x2, 0x3])) - } - - func test_validateMutation_slice_mutableBacking_resetBytes() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [4, 0, 0, 0, 8])) - } - - func test_validateMutation_slice_mutableBacking_replaceSubrange() { - var base = NSData(bytes: "hello world", length: 11) as Data - base.append(contentsOf: [1, 2, 3, 4, 5, 6]) - var data = base[4..<9] - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - data.replaceSubrange(range, with: buffer) - } - expectEqual(data, Data(bytes: [0x6f, 0xFF, 0xFF, 0x72])) - } - - func test_validateMutation_slice_mutableBacking_replaceSubrangeWithCollection() { - var base = NSData(bytes: "hello world", length: 11) as Data - base.append(contentsOf: [1, 2, 3, 4, 5, 6]) - var data = base[4..<9] - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 5)], 0xFF) - } - } - - func test_validateMutation_cow_mutableBacking_appendBytes() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append(contentsOf: [1, 2, 3, 4, 5, 6]) - holdReference(data) { - data.append("hello", count: 5) - expectEqual(data[data.startIndex.advanced(by: 16)], 6) - expectEqual(data[data.startIndex.advanced(by: 17)], 0x68) - } - } - - func test_validateMutation_cow_mutableBacking_appendData() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append(contentsOf: [1, 2, 3, 4, 5, 6]) - holdReference(data) { - data.append("hello", count: 5) - expectEqual(data[data.startIndex.advanced(by: 16)], 6) - expectEqual(data[data.startIndex.advanced(by: 17)], 0x68) - } - } - - func test_validateMutation_cow_mutableBacking_appendBuffer() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append(contentsOf: [1, 2, 3, 4, 5, 6]) - holdReference(data) { - let other = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - data.append(other) - expectEqual(data[data.startIndex.advanced(by: 16)], 6) - expectEqual(data[data.startIndex.advanced(by: 17)], 0) - } - } - - func test_validateMutation_cow_mutableBacking_appendSequence() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append(contentsOf: [1, 2, 3, 4, 5, 6]) - holdReference(data) { - let seq = repeatElement(UInt8(1), count: 10) - data.append(contentsOf: seq) - expectEqual(data[data.startIndex.advanced(by: 16)], 6) - expectEqual(data[data.startIndex.advanced(by: 17)], 1) - } - } - - func test_validateMutation_cow_mutableBacking_appendContentsOf() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append(contentsOf: [1, 2, 3, 4, 5, 6]) - holdReference(data) { - let bytes: [UInt8] = [1, 1, 2, 3, 4, 5, 6, 7, 8, 9] - data.append(contentsOf: bytes) - expectEqual(data[data.startIndex.advanced(by: 16)], 6) - expectEqual(data[data.startIndex.advanced(by: 17)], 1) - } - } - - func test_validateMutation_cow_mutableBacking_resetBytes() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append(contentsOf: [1, 2, 3, 4, 5, 6]) - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x72, 0x6c, 0x64, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06])) - } - } - - func test_validateMutation_cow_mutableBacking_replaceSubrange() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append(contentsOf: [1, 2, 3, 4, 5, 6]) - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4)..) in - data.replaceSubrange(range, with: buffer) - } - expectEqual(data, Data(bytes: [0x68, 0x65, 0x6c, 0x6c, 0xff, 0xff, 0x6c, 0x64, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06])) - } - } - - func test_validateMutation_cow_mutableBacking_replaceSubrangeWithCollection() { - var data = NSData(bytes: "hello world", length: 11) as Data - data.append(contentsOf: [1, 2, 3, 4, 5, 6]) - holdReference(data) { - let replacement: [UInt8] = [0xFF, 0xFF] - let range: Range = data.startIndex.advanced(by: 4).. = data.startIndex.advanced(by: 4)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - } - - func test_validateMutation_slice_cow_mutableBacking_appendBytes() { - let bytes: [UInt8] = [0, 1, 2] - var base = bytes.withUnsafeBytes { (ptr) in - return NSData(bytes: ptr.baseAddress!, length: ptr.count) as Data - } - base.append(contentsOf: [3, 4, 5]) - var data = base[1..<4] - holdReference(data) { - let bytesToAppend: [UInt8] = [6, 7, 8] - bytesToAppend.withUnsafeBytes { (ptr) in - data.append(ptr.baseAddress!.assumingMemoryBound(to: UInt8.self), count: ptr.count) - } - expectEqual(data, Data(bytes: [1, 2, 3, 6, 7, 8])) - } - } - - func test_validateMutation_slice_cow_mutableBacking_appendData() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - holdReference(data) { - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_mutableBacking_appendBuffer() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer{ data.append($0) } - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_mutableBacking_appendSequence() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - holdReference(data) { - let bytes = repeatElement(UInt8(0xFF), count: 2) - data.append(contentsOf: bytes) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_mutableBacking_appendContentsOf() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - data.append(contentsOf: bytes) - expectEqual(data, Data(bytes: [4, 5, 6, 7, 8, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_mutableBacking_resetBytes() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [4, 0, 0, 0, 8])) - } - } - - func test_validateMutation_slice_cow_mutableBacking_replaceSubrange() { - let baseBytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6] - var base = baseBytes.withUnsafeBufferPointer { - return NSData(bytes: $0.baseAddress!, length: $0.count) as Data - } - base.append(contentsOf: [7, 8, 9]) - var data = base[4..<9] - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 1, 1, 1, 1])) - } - - func test_validateMutation_customBacking_appendBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_customBacking_appendData() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_customBacking_appendBuffer() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { (buffer) in - data.append(buffer) - } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - - } - - func test_validateMutation_customBacking_appendSequence() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - data.append(contentsOf: repeatElement(UInt8(0xFF), count: 2)) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_customBacking_appendContentsOf() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_customBacking_resetBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0, 0, 0, 1, 1])) - } - - func test_validateMutation_customBacking_replaceSubrange() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - let range: Range = 1..<4 - data.replaceSubrange(range, with: Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [1, 0xFF, 0xFF, 1, 1, 1, 1, 1, 1])) - } - - func test_validateMutation_customBacking_replaceSubrangeRange() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - let range: Range = 1..<4 - data.replaceSubrange(range, with: Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [1, 0xFF, 0xFF, 1, 1, 1, 1, 1, 1])) - } - - func test_validateMutation_customBacking_replaceSubrangeWithBuffer() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - let bytes: [UInt8] = [0xFF, 0xFF] - let range: Range = 1..<4 - bytes.withUnsafeBufferPointer { (buffer) in - data.replaceSubrange(range, with: buffer) - } - expectEqual(data, Data(bytes: [1, 0xFF, 0xFF, 1, 1, 1, 1, 1, 1])) - } - - func test_validateMutation_customBacking_replaceSubrangeWithCollection() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - let range: Range = 1..<4 - data.replaceSubrange(range, with: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [1, 0xFF, 0xFF, 1, 1, 1, 1, 1, 1])) - } - - func test_validateMutation_customBacking_replaceSubrangeWithBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - let bytes: [UInt8] = [0xFF, 0xFF] - let range: Range = 1..<5 - bytes.withUnsafeBufferPointer { (buffer) in - data.replaceSubrange(range, with: buffer.baseAddress!, count: buffer.count) - } - expectEqual(data, Data(bytes: [1, 0xFF, 0xFF, 1, 1, 1, 1, 1])) - } - - func test_validateMutation_slice_customBacking_withUnsafeMutableBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - data.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - - func test_validateMutation_slice_customBacking_appendBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBytes { ptr in - data.append(ptr.baseAddress!.assumingMemoryBound(to: UInt8.self), count: ptr.count) - } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customBacking_appendData() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customBacking_appendBuffer() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { (buffer) in - data.append(buffer) - } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customBacking_appendSequence() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - let seq = repeatElement(UInt8(0xFF), count: 2) - data.append(contentsOf: seq) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customBacking_appendContentsOf() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customBacking_resetBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [1, 0, 0, 0, 1])) - } - - func test_validateMutation_slice_customBacking_replaceSubrange() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 5)], 0xFF) - } - } - - func test_validateMutation_cow_customBacking_appendBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { (buffer) in - data.append(buffer.baseAddress!, count: buffer.count) - } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customBacking_appendData() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - holdReference(data) { - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customBacking_appendBuffer() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customBacking_appendSequence() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - holdReference(data) { - data.append(contentsOf: repeatElement(UInt8(0xFF), count: 2)) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customBacking_appendContentsOf() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - holdReference(data) { - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customBacking_resetBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0, 0, 0, 1, 1])) - } - } - - func test_validateMutation_cow_customBacking_replaceSubrange() { - var data = Data(referencing: AllOnesImmutableData(length: 10)) - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - } - - func test_validateMutation_slice_cow_customBacking_appendBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { (buffer) in - data.append(buffer.baseAddress!, count: buffer.count) - } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customBacking_appendData() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - holdReference(data) { - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customBacking_appendBuffer() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customBacking_appendSequence() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - holdReference(data) { - data.append(contentsOf: repeatElement(UInt8(0xFF), count: 2)) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customBacking_appendContentsOf() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - holdReference(data) { - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [1, 1, 1, 1, 1, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customBacking_resetBytes() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data, Data(bytes: [1, 0, 0, 0, 1])) - } - } - - func test_validateMutation_slice_cow_customBacking_replaceSubrange() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<9] - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 5)], 0xFF) - } - - func test_validateMutation_customMutableBacking_appendBytes() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_customMutableBacking_appendData() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_customMutableBacking_appendBuffer() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_customMutableBacking_appendSequence() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - data.append(contentsOf: repeatElement(UInt8(0xFF), count: 2)) - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_customMutableBacking_appendContentsOf() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_customMutableBacking_resetBytes() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - data.resetBytes(in: 5..<8) - expectEqual(data.count, 10) - expectEqual(data[data.startIndex.advanced(by: 0)], 1) - expectEqual(data[data.startIndex.advanced(by: 5)], 0) - expectEqual(data[data.startIndex.advanced(by: 6)], 0) - expectEqual(data[data.startIndex.advanced(by: 7)], 0) - } - - func test_validateMutation_customMutableBacking_replaceSubrange() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - - func test_validateMutation_slice_customMutableBacking_appendBytes() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customMutableBacking_appendData() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customMutableBacking_appendBuffer() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customMutableBacking_appendSequence() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customMutableBacking_appendContentsOf() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - - func test_validateMutation_slice_customMutableBacking_resetBytes() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - data.resetBytes(in: 5..<8) - - expectEqual(data[data.startIndex.advanced(by: 1)], 0) - expectEqual(data[data.startIndex.advanced(by: 2)], 0) - expectEqual(data[data.startIndex.advanced(by: 3)], 0) - } - - func test_validateMutation_slice_customMutableBacking_replaceSubrange() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 5).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 5)], 0xFF) - } - } - - func test_validateMutation_cow_customMutableBacking_appendBytes() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customMutableBacking_appendData() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - holdReference(data) { - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customMutableBacking_appendBuffer() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customMutableBacking_appendSequence() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - holdReference(data) { - data.append(contentsOf: repeatElement(UInt8(0xFF), count: 2)) - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customMutableBacking_appendContentsOf() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - holdReference(data) { - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_cow_customMutableBacking_resetBytes() { - var data = Data(referencing: AllOnesData(length: 10)) - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data.count, 10) - expectEqual(data[data.startIndex.advanced(by: 0)], 1) - expectEqual(data[data.startIndex.advanced(by: 5)], 0) - expectEqual(data[data.startIndex.advanced(by: 6)], 0) - expectEqual(data[data.startIndex.advanced(by: 7)], 0) - } - } - - func test_validateMutation_cow_customMutableBacking_replaceSubrange() { - var data = Data(referencing: AllOnesData(length: 1)) - data.count = 10 - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - } - - func test_validateMutation_slice_cow_customMutableBacking_appendBytes() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0.baseAddress!, count: $0.count) } - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customMutableBacking_appendData() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - holdReference(data) { - data.append(Data(bytes: [0xFF, 0xFF])) - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customMutableBacking_appendBuffer() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - holdReference(data) { - let bytes: [UInt8] = [0xFF, 0xFF] - bytes.withUnsafeBufferPointer { data.append($0) } - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customMutableBacking_appendSequence() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - holdReference(data) { - data.append(contentsOf: repeatElement(UInt8(0xFF), count: 2)) - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customMutableBacking_appendContentsOf() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - holdReference(data) { - data.append(contentsOf: [0xFF, 0xFF]) - expectEqual(data, Data(bytes: [0, 0, 0, 0, 0, 0xFF, 0xFF])) - } - } - - func test_validateMutation_slice_cow_customMutableBacking_resetBytes() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - holdReference(data) { - data.resetBytes(in: 5..<8) - expectEqual(data[data.startIndex.advanced(by: 1)], 0) - expectEqual(data[data.startIndex.advanced(by: 2)], 0) - expectEqual(data[data.startIndex.advanced(by: 3)], 0) - } - } - - func test_validateMutation_slice_cow_customMutableBacking_replaceSubrange() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<9] - holdReference(data) { - let range: Range = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1).. = data.startIndex.advanced(by: 1)..) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data, Data(bytes: [4, 0xFF])) - } - - func test_validateMutation_slice_immutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() { - var data = Data(referencing: NSData(bytes: "hello world", length: 11))[4..<6] - data.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - - func test_validateMutation_slice_mutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() { - var base = Data(referencing: NSData(bytes: "hello world", length: 11)) - base.append(contentsOf: [1, 2, 3, 4, 5, 6]) - var data = base[4..<6] - data.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - - func test_validateMutation_slice_customBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() { - var data = Data(referencing: AllOnesImmutableData(length: 10))[4..<6] - data.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - - func test_validateMutation_slice_customMutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() { - var base = Data(referencing: AllOnesData(length: 1)) - base.count = 10 - var data = base[4..<6] - data.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer) in - ptr.advanced(by: 1).pointee = 0xFF - } - expectEqual(data[data.startIndex.advanced(by: 1)], 0xFF) - } - - func test_byte_access_of_discontiguousData() { - var d = DispatchData.empty - let bytes: [UInt8] = [0, 1, 2, 3, 4, 5] - for _ in 0..<3 { - bytes.withUnsafeBufferPointer { - d.append($0) - } - } - let ref = d as AnyObject - let data = ref as! Data - - let cnt = data.count - 4 - let t = data.dropFirst(4).withUnsafeBytes { (bytes: UnsafePointer) in - return Data(bytes: bytes, count: cnt) - } - - expectEqual(Data(bytes: [4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]), t) - } - - func test_rangeOfSlice() { - let data = "FooBar".data(using: .ascii)! - let slice = data[3...] // Bar - - let range = slice.range(of: "a".data(using: .ascii)!) - expectEqual(range, Range(4..<5)) - } - - func test_nsdataSequence() { - if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { - let bytes: [UInt8] = Array(0x00...0xFF) - let data = bytes.withUnsafeBytes { NSData(bytes: $0.baseAddress, length: $0.count) } - - for byte in bytes { - expectEqual(data[Int(byte)], byte) - } - } - } - - func test_dispatchSequence() { - if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { - let bytes1: [UInt8] = Array(0x00..<0xF0) - let bytes2: [UInt8] = Array(0xF0..<0xFF) - var data = DispatchData.empty - bytes1.withUnsafeBytes { - data.append($0) - } - bytes2.withUnsafeBytes { - data.append($0) - } - - for byte in bytes1 { - expectEqual(data[Int(byte)], byte) - } - for byte in bytes2 { - expectEqual(data[Int(byte)], byte) - } - } - } - - func test_increaseCount() { - guard #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) else { return } - let initials: [Range] = [ - 0..<0, - 0..<2, - 0..<4, - 0..<8, - 0..<16, - 0..<32, - 0..<64 - ] - let diffs = [0, 1, 2, 4, 8, 16, 32] - for initial in initials { - for diff in diffs { - var data = Data(initial) - data.count += diff - expectEqualSequence( - Array(initial) + Array(repeating: 0, count: diff), - data) - } - } - } - - func test_decreaseCount() { - guard #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) else { return } - let initials: [Range] = [ - 0..<0, - 0..<2, - 0..<4, - 0..<8, - 0..<16, - 0..<32, - 0..<64 - ] - let diffs = [0, 1, 2, 4, 8, 16, 32] - for initial in initials { - for diff in diffs { - guard initial.count >= diff else { continue } - var data = Data(initial) - data.count -= diff - expectEqualSequence( - initial.dropLast(diff), - data) - } - } - } - - // This is a (potentially invalid) sequence that produces a configurable number of 42s and has a freely customizable `underestimatedCount`. - struct TestSequence: Sequence { - typealias Element = UInt8 - struct Iterator: IteratorProtocol { - var _remaining: Int - init(_ count: Int) { - _remaining = count - } - mutating func next() -> UInt8? { - guard _remaining > 0 else { return nil } - _remaining -= 1 - return 42 - } - } - let underestimatedCount: Int - let count: Int - - func makeIterator() -> Iterator { - return Iterator(count) - } - } - - func test_init_TestSequence() { - // Underestimated count - do { - let d = Data(TestSequence(underestimatedCount: 0, count: 10)) - expectEqual(10, d.count) - expectEqual(Array(repeating: 42 as UInt8, count: 10), Array(d)) - } - - // Very underestimated count (to exercise realloc path) - do { - let d = Data(TestSequence(underestimatedCount: 0, count: 1000)) - expectEqual(1000, d.count) - expectEqual(Array(repeating: 42 as UInt8, count: 1000), Array(d)) - } - - // Exact count - do { - let d = Data(TestSequence(underestimatedCount: 10, count: 10)) - expectEqual(10, d.count) - expectEqual(Array(repeating: 42 as UInt8, count: 10), Array(d)) - } - - // Overestimated count. This is an illegal case, so trapping would be fine. - // However, for compatibility with the implementation in Swift 5, Data - // handles this case by simply truncating itself to the actual size. - do { - let d = Data(TestSequence(underestimatedCount: 20, count: 10)) - expectEqual(10, d.count) - expectEqual(Array(repeating: 42 as UInt8, count: 10), Array(d)) - } - } - - func test_append_TestSequence() { - let base = Data(Array(repeating: 23 as UInt8, count: 10)) - - // Underestimated count - do { - var d = base - d.append(contentsOf: TestSequence(underestimatedCount: 0, count: 10)) - expectEqual(20, d.count) - expectEqual(Array(base) + Array(repeating: 42 as UInt8, count: 10), - Array(d)) - } - - // Very underestimated count (to exercise realloc path) - do { - var d = base - d.append(contentsOf: TestSequence(underestimatedCount: 0, count: 1000)) - expectEqual(1010, d.count) - expectEqual(Array(base) + Array(repeating: 42 as UInt8, count: 1000), Array(d)) - } - - // Exact count - do { - var d = base - d.append(contentsOf: TestSequence(underestimatedCount: 10, count: 10)) - expectEqual(20, d.count) - expectEqual(Array(base) + Array(repeating: 42 as UInt8, count: 10), Array(d)) - } - - // Overestimated count. This is an illegal case, so trapping would be fine. - // However, for compatibility with the implementation in Swift 5, Data - // handles this case by simply truncating itself to the actual size. - do { - var d = base - d.append(contentsOf: TestSequence(underestimatedCount: 20, count: 10)) - expectEqual(20, d.count) - expectEqual(Array(base) + Array(repeating: 42 as UInt8, count: 10), Array(d)) - } - } -} - -#if !FOUNDATION_XCTEST -var DataTests = TestSuite("TestData") -DataTests.test("testBasicConstruction") { TestData().testBasicConstruction() } -DataTests.test("testInitializationWithArray") { TestData().testInitializationWithArray() } -DataTests.test("testInitializationWithBufferPointer") { TestData().testInitializationWithBufferPointer() } -DataTests.test("testInitializationWithMutableBufferPointer") { TestData().testInitializationWithMutableBufferPointer() } -DataTests.test("testMutableData") { TestData().testMutableData() } -DataTests.test("testCustomData") { TestData().testCustomData() } -DataTests.test("testBridgingDefault") { TestData().testBridgingDefault() } -DataTests.test("testBridgingMutable") { TestData().testBridgingMutable() } -DataTests.test("testBridgingCustom") { TestData().testBridgingCustom() } -DataTests.test("testEquality") { TestData().testEquality() } -DataTests.test("testDataInSet") { TestData().testDataInSet() } -DataTests.test("testReplaceSubrange") { TestData().testReplaceSubrange() } -DataTests.test("testReplaceSubrange2") { TestData().testReplaceSubrange2() } -DataTests.test("testReplaceSubrange3") { TestData().testReplaceSubrange3() } -DataTests.test("testReplaceSubrange4") { TestData().testReplaceSubrange4() } -DataTests.test("testReplaceSubrange5") { TestData().testReplaceSubrange5() } -DataTests.test("testRange") { TestData().testRange() } -DataTests.test("testInsertData") { TestData().testInsertData() } -DataTests.test("testLoops") { TestData().testLoops() } -DataTests.test("testGenericAlgorithms") { TestData().testGenericAlgorithms() } -DataTests.test("testCustomDeallocator") { TestData().testCustomDeallocator() } -DataTests.test("testCopyBytes") { TestData().testCopyBytes() } -DataTests.test("testCopyBytes_undersized") { TestData().testCopyBytes_undersized() } -DataTests.test("testCopyBytes_oversized") { TestData().testCopyBytes_oversized() } -DataTests.test("testCopyBytes_ranges") { TestData().testCopyBytes_ranges() } -DataTests.test("test_base64Data_small") { TestData().test_base64Data_small() } -DataTests.test("test_base64Data_medium") { TestData().test_base64Data_medium() } -DataTests.test("test_discontiguousEnumerateBytes") { TestData().test_discontiguousEnumerateBytes() } -DataTests.test("test_basicReadWrite") { TestData().test_basicReadWrite() } -DataTests.test("test_writeFailure") { TestData().test_writeFailure() } -DataTests.test("test_genericBuffers") { TestData().test_genericBuffers() } -DataTests.test("test_basicDataMutation") { TestData().test_basicDataMutation() } -DataTests.test("test_basicMutableDataMutation") { TestData().test_basicMutableDataMutation() } -DataTests.test("test_passing") { TestData().test_passing() } -DataTests.test("test_bufferSizeCalculation") { TestData().test_bufferSizeCalculation() } -DataTests.test("test_classForCoder") { TestData().test_classForCoder() } -DataTests.test("test_AnyHashableContainingData") { TestData().test_AnyHashableContainingData() } -DataTests.test("test_AnyHashableCreatedFromNSData") { TestData().test_AnyHashableCreatedFromNSData() } -DataTests.test("test_noCopyBehavior") { TestData().test_noCopyBehavior() } -DataTests.test("test_doubleDeallocation") { TestData().test_doubleDeallocation() } -DataTests.test("test_repeatingValueInitialization") { TestData().test_repeatingValueInitialization() } -DataTests.test("test_rangeZoo") { TestData().test_rangeZoo() } -DataTests.test("test_rangeOfDataProtocol") { TestData().test_rangeOfDataProtocol() } -DataTests.test("test_sliceAppending") { TestData().test_sliceAppending() } -DataTests.test("test_replaceSubrange") { TestData().test_replaceSubrange() } -DataTests.test("test_sliceWithUnsafeBytes") { TestData().test_sliceWithUnsafeBytes() } -DataTests.test("test_sliceIteration") { TestData().test_sliceIteration() } -DataTests.test("test_unconditionallyBridgeFromObjectiveC") { TestData().test_unconditionallyBridgeFromObjectiveC() } -DataTests.test("test_sliceIndexing") { TestData().test_sliceIndexing() } -DataTests.test("test_sliceEquality") { TestData().test_sliceEquality() } -DataTests.test("test_sliceEquality2") { TestData().test_sliceEquality2() } -DataTests.test("test_splittingHttp") { TestData().test_splittingHttp() } -DataTests.test("test_map") { TestData().test_map() } -DataTests.test("test_dropFirst") { TestData().test_dropFirst() } -DataTests.test("test_dropFirst2") { TestData().test_dropFirst2() } -DataTests.test("test_copyBytes1") { TestData().test_copyBytes1() } -DataTests.test("test_copyBytes2") { TestData().test_copyBytes2() } -DataTests.test("test_sliceOfSliceViaRangeExpression") { TestData().test_sliceOfSliceViaRangeExpression() } -DataTests.test("test_appendingSlices") { TestData().test_appendingSlices() } -DataTests.test("test_appendingNonContiguousSequence_exactCount") { TestData().test_appendingNonContiguousSequence_exactCount() } -DataTests.test("test_appendingNonContiguousSequence_underestimatedCount") { TestData().test_appendingNonContiguousSequence_underestimatedCount() } -DataTests.test("test_sequenceInitializers") { TestData().test_sequenceInitializers() } -DataTests.test("test_reversedDataInit") { TestData().test_reversedDataInit() } -DataTests.test("test_replaceSubrangeReferencingMutable") { TestData().test_replaceSubrangeReferencingMutable() } -DataTests.test("test_replaceSubrangeReferencingImmutable") { TestData().test_replaceSubrangeReferencingImmutable() } -DataTests.test("test_replaceSubrangeFromBridged") { TestData().test_replaceSubrangeFromBridged() } -DataTests.test("test_validateMutation_withUnsafeMutableBytes") { TestData().test_validateMutation_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_appendBytes") { TestData().test_validateMutation_appendBytes() } -DataTests.test("test_validateMutation_appendData") { TestData().test_validateMutation_appendData() } -DataTests.test("test_validateMutation_appendBuffer") { TestData().test_validateMutation_appendBuffer() } -DataTests.test("test_validateMutation_appendSequence") { TestData().test_validateMutation_appendSequence() } -DataTests.test("test_validateMutation_appendContentsOf") { TestData().test_validateMutation_appendContentsOf() } -DataTests.test("test_validateMutation_resetBytes") { TestData().test_validateMutation_resetBytes() } -DataTests.test("test_validateMutation_replaceSubrange") { TestData().test_validateMutation_replaceSubrange() } -DataTests.test("test_validateMutation_replaceSubrangeRange") { TestData().test_validateMutation_replaceSubrangeRange() } -DataTests.test("test_validateMutation_replaceSubrangeWithBuffer") { TestData().test_validateMutation_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_replaceSubrangeWithCollection") { TestData().test_validateMutation_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_replaceSubrangeWithBytes") { TestData().test_validateMutation_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_appendBytes") { TestData().test_validateMutation_slice_appendBytes() } -DataTests.test("test_validateMutation_slice_appendData") { TestData().test_validateMutation_slice_appendData() } -DataTests.test("test_validateMutation_slice_appendBuffer") { TestData().test_validateMutation_slice_appendBuffer() } -DataTests.test("test_validateMutation_slice_appendSequence") { TestData().test_validateMutation_slice_appendSequence() } -DataTests.test("test_validateMutation_slice_appendContentsOf") { TestData().test_validateMutation_slice_appendContentsOf() } -DataTests.test("test_validateMutation_slice_resetBytes") { TestData().test_validateMutation_slice_resetBytes() } -DataTests.test("test_validateMutation_slice_replaceSubrange") { TestData().test_validateMutation_slice_replaceSubrange() } -DataTests.test("test_validateMutation_slice_replaceSubrangeRange") { TestData().test_validateMutation_slice_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_cow_withUnsafeMutableBytes") { TestData().test_validateMutation_cow_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_cow_appendBytes") { TestData().test_validateMutation_cow_appendBytes() } -DataTests.test("test_validateMutation_cow_appendData") { TestData().test_validateMutation_cow_appendData() } -DataTests.test("test_validateMutation_cow_appendBuffer") { TestData().test_validateMutation_cow_appendBuffer() } -DataTests.test("test_validateMutation_cow_appendSequence") { TestData().test_validateMutation_cow_appendSequence() } -DataTests.test("test_validateMutation_cow_appendContentsOf") { TestData().test_validateMutation_cow_appendContentsOf() } -DataTests.test("test_validateMutation_cow_resetBytes") { TestData().test_validateMutation_cow_resetBytes() } -DataTests.test("test_validateMutation_cow_replaceSubrange") { TestData().test_validateMutation_cow_replaceSubrange() } -DataTests.test("test_validateMutation_cow_replaceSubrangeRange") { TestData().test_validateMutation_cow_replaceSubrangeRange() } -DataTests.test("test_validateMutation_cow_replaceSubrangeWithBuffer") { TestData().test_validateMutation_cow_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_cow_replaceSubrangeWithCollection") { TestData().test_validateMutation_cow_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_cow_replaceSubrangeWithBytes") { TestData().test_validateMutation_cow_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_cow_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_cow_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_cow_appendBytes") { TestData().test_validateMutation_slice_cow_appendBytes() } -DataTests.test("test_validateMutation_slice_cow_appendData") { TestData().test_validateMutation_slice_cow_appendData() } -DataTests.test("test_validateMutation_slice_cow_appendBuffer") { TestData().test_validateMutation_slice_cow_appendBuffer() } -DataTests.test("test_validateMutation_slice_cow_appendSequence") { TestData().test_validateMutation_slice_cow_appendSequence() } -DataTests.test("test_validateMutation_slice_cow_appendContentsOf") { TestData().test_validateMutation_slice_cow_appendContentsOf() } -DataTests.test("test_validateMutation_slice_cow_resetBytes") { TestData().test_validateMutation_slice_cow_resetBytes() } -DataTests.test("test_validateMutation_slice_cow_replaceSubrange") { TestData().test_validateMutation_slice_cow_replaceSubrange() } -DataTests.test("test_validateMutation_slice_cow_replaceSubrangeRange") { TestData().test_validateMutation_slice_cow_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_cow_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_cow_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_cow_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_cow_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_cow_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_cow_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_immutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_immutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_immutableBacking_appendBytes") { TestData().test_validateMutation_immutableBacking_appendBytes() } -DataTests.test("test_validateMutation_immutableBacking_appendData") { TestData().test_validateMutation_immutableBacking_appendData() } -DataTests.test("test_validateMutation_immutableBacking_appendBuffer") { TestData().test_validateMutation_immutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_immutableBacking_appendSequence") { TestData().test_validateMutation_immutableBacking_appendSequence() } -DataTests.test("test_validateMutation_immutableBacking_appendContentsOf") { TestData().test_validateMutation_immutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_immutableBacking_resetBytes") { TestData().test_validateMutation_immutableBacking_resetBytes() } -DataTests.test("test_validateMutation_immutableBacking_replaceSubrange") { TestData().test_validateMutation_immutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_immutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_immutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_immutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_immutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_immutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_immutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_immutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_immutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_immutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_immutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_immutableBacking_appendBytes") { TestData().test_validateMutation_slice_immutableBacking_appendBytes() } -DataTests.test("test_validateMutation_slice_immutableBacking_appendData") { TestData().test_validateMutation_slice_immutableBacking_appendData() } -DataTests.test("test_validateMutation_slice_immutableBacking_appendBuffer") { TestData().test_validateMutation_slice_immutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_slice_immutableBacking_appendSequence") { TestData().test_validateMutation_slice_immutableBacking_appendSequence() } -DataTests.test("test_validateMutation_slice_immutableBacking_appendContentsOf") { TestData().test_validateMutation_slice_immutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_slice_immutableBacking_resetBytes") { TestData().test_validateMutation_slice_immutableBacking_resetBytes() } -DataTests.test("test_validateMutation_slice_immutableBacking_replaceSubrange") { TestData().test_validateMutation_slice_immutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_slice_immutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_slice_immutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_immutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_immutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_immutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_immutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_immutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_immutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_cow_immutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_cow_immutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_cow_immutableBacking_appendBytes") { TestData().test_validateMutation_cow_immutableBacking_appendBytes() } -DataTests.test("test_validateMutation_cow_immutableBacking_appendData") { TestData().test_validateMutation_cow_immutableBacking_appendData() } -DataTests.test("test_validateMutation_cow_immutableBacking_appendBuffer") { TestData().test_validateMutation_cow_immutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_cow_immutableBacking_appendSequence") { TestData().test_validateMutation_cow_immutableBacking_appendSequence() } -DataTests.test("test_validateMutation_cow_immutableBacking_appendContentsOf") { TestData().test_validateMutation_cow_immutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_cow_immutableBacking_resetBytes") { TestData().test_validateMutation_cow_immutableBacking_resetBytes() } -DataTests.test("test_validateMutation_cow_immutableBacking_replaceSubrange") { TestData().test_validateMutation_cow_immutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_cow_immutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_cow_immutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_cow_immutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_cow_immutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_cow_immutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_cow_immutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_cow_immutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_cow_immutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_cow_immutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_appendBytes") { TestData().test_validateMutation_slice_cow_immutableBacking_appendBytes() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_appendData") { TestData().test_validateMutation_slice_cow_immutableBacking_appendData() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_appendBuffer") { TestData().test_validateMutation_slice_cow_immutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_appendSequence") { TestData().test_validateMutation_slice_cow_immutableBacking_appendSequence() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_appendContentsOf") { TestData().test_validateMutation_slice_cow_immutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_resetBytes") { TestData().test_validateMutation_slice_cow_immutableBacking_resetBytes() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_replaceSubrange") { TestData().test_validateMutation_slice_cow_immutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_slice_cow_immutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_cow_immutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_cow_immutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_cow_immutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_cow_immutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_mutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_mutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_mutableBacking_appendBytes") { TestData().test_validateMutation_mutableBacking_appendBytes() } -DataTests.test("test_validateMutation_mutableBacking_appendData") { TestData().test_validateMutation_mutableBacking_appendData() } -DataTests.test("test_validateMutation_mutableBacking_appendBuffer") { TestData().test_validateMutation_mutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_mutableBacking_appendSequence") { TestData().test_validateMutation_mutableBacking_appendSequence() } -DataTests.test("test_validateMutation_mutableBacking_appendContentsOf") { TestData().test_validateMutation_mutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_mutableBacking_resetBytes") { TestData().test_validateMutation_mutableBacking_resetBytes() } -DataTests.test("test_validateMutation_mutableBacking_replaceSubrange") { TestData().test_validateMutation_mutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_mutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_mutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_mutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_mutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_mutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_mutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_mutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_mutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_mutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_mutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_mutableBacking_appendBytes") { TestData().test_validateMutation_slice_mutableBacking_appendBytes() } -DataTests.test("test_validateMutation_slice_mutableBacking_appendData") { TestData().test_validateMutation_slice_mutableBacking_appendData() } -DataTests.test("test_validateMutation_slice_mutableBacking_appendBuffer") { TestData().test_validateMutation_slice_mutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_slice_mutableBacking_appendSequence") { TestData().test_validateMutation_slice_mutableBacking_appendSequence() } -DataTests.test("test_validateMutation_slice_mutableBacking_appendContentsOf") { TestData().test_validateMutation_slice_mutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_slice_mutableBacking_resetBytes") { TestData().test_validateMutation_slice_mutableBacking_resetBytes() } -DataTests.test("test_validateMutation_slice_mutableBacking_replaceSubrange") { TestData().test_validateMutation_slice_mutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_slice_mutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_slice_mutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_mutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_mutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_mutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_mutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_mutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_mutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_cow_mutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_cow_mutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_cow_mutableBacking_appendBytes") { TestData().test_validateMutation_cow_mutableBacking_appendBytes() } -DataTests.test("test_validateMutation_cow_mutableBacking_appendData") { TestData().test_validateMutation_cow_mutableBacking_appendData() } -DataTests.test("test_validateMutation_cow_mutableBacking_appendBuffer") { TestData().test_validateMutation_cow_mutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_cow_mutableBacking_appendSequence") { TestData().test_validateMutation_cow_mutableBacking_appendSequence() } -DataTests.test("test_validateMutation_cow_mutableBacking_appendContentsOf") { TestData().test_validateMutation_cow_mutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_cow_mutableBacking_resetBytes") { TestData().test_validateMutation_cow_mutableBacking_resetBytes() } -DataTests.test("test_validateMutation_cow_mutableBacking_replaceSubrange") { TestData().test_validateMutation_cow_mutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_cow_mutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_cow_mutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_cow_mutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_cow_mutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_cow_mutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_cow_mutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_cow_mutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_cow_mutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_cow_mutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_appendBytes") { TestData().test_validateMutation_slice_cow_mutableBacking_appendBytes() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_appendData") { TestData().test_validateMutation_slice_cow_mutableBacking_appendData() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_appendBuffer") { TestData().test_validateMutation_slice_cow_mutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_appendSequence") { TestData().test_validateMutation_slice_cow_mutableBacking_appendSequence() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_appendContentsOf") { TestData().test_validateMutation_slice_cow_mutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_resetBytes") { TestData().test_validateMutation_slice_cow_mutableBacking_resetBytes() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_replaceSubrange") { TestData().test_validateMutation_slice_cow_mutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_slice_cow_mutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_cow_mutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_cow_mutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_cow_mutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_cow_mutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_customBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_customBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_customBacking_appendBytes") { TestData().test_validateMutation_customBacking_appendBytes() } -DataTests.test("test_validateMutation_customBacking_appendData") { TestData().test_validateMutation_customBacking_appendData() } -DataTests.test("test_validateMutation_customBacking_appendBuffer") { TestData().test_validateMutation_customBacking_appendBuffer() } -DataTests.test("test_validateMutation_customBacking_appendSequence") { TestData().test_validateMutation_customBacking_appendSequence() } -DataTests.test("test_validateMutation_customBacking_appendContentsOf") { TestData().test_validateMutation_customBacking_appendContentsOf() } -DataTests.test("test_validateMutation_customBacking_resetBytes") { TestData().test_validateMutation_customBacking_resetBytes() } -DataTests.test("test_validateMutation_customBacking_replaceSubrange") { TestData().test_validateMutation_customBacking_replaceSubrange() } -DataTests.test("test_validateMutation_customBacking_replaceSubrangeRange") { TestData().test_validateMutation_customBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_customBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_customBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_customBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_customBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_customBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_customBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_customBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_customBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_customBacking_appendBytes") { TestData().test_validateMutation_slice_customBacking_appendBytes() } -DataTests.test("test_validateMutation_slice_customBacking_appendData") { TestData().test_validateMutation_slice_customBacking_appendData() } -DataTests.test("test_validateMutation_slice_customBacking_appendBuffer") { TestData().test_validateMutation_slice_customBacking_appendBuffer() } -DataTests.test("test_validateMutation_slice_customBacking_appendSequence") { TestData().test_validateMutation_slice_customBacking_appendSequence() } -DataTests.test("test_validateMutation_slice_customBacking_appendContentsOf") { TestData().test_validateMutation_slice_customBacking_appendContentsOf() } -DataTests.test("test_validateMutation_slice_customBacking_resetBytes") { TestData().test_validateMutation_slice_customBacking_resetBytes() } -DataTests.test("test_validateMutation_slice_customBacking_replaceSubrange") { TestData().test_validateMutation_slice_customBacking_replaceSubrange() } -DataTests.test("test_validateMutation_slice_customBacking_replaceSubrangeRange") { TestData().test_validateMutation_slice_customBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_customBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_customBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_customBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_customBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_customBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_customBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_cow_customBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_cow_customBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_cow_customBacking_appendBytes") { TestData().test_validateMutation_cow_customBacking_appendBytes() } -DataTests.test("test_validateMutation_cow_customBacking_appendData") { TestData().test_validateMutation_cow_customBacking_appendData() } -DataTests.test("test_validateMutation_cow_customBacking_appendBuffer") { TestData().test_validateMutation_cow_customBacking_appendBuffer() } -DataTests.test("test_validateMutation_cow_customBacking_appendSequence") { TestData().test_validateMutation_cow_customBacking_appendSequence() } -DataTests.test("test_validateMutation_cow_customBacking_appendContentsOf") { TestData().test_validateMutation_cow_customBacking_appendContentsOf() } -DataTests.test("test_validateMutation_cow_customBacking_resetBytes") { TestData().test_validateMutation_cow_customBacking_resetBytes() } -DataTests.test("test_validateMutation_cow_customBacking_replaceSubrange") { TestData().test_validateMutation_cow_customBacking_replaceSubrange() } -DataTests.test("test_validateMutation_cow_customBacking_replaceSubrangeRange") { TestData().test_validateMutation_cow_customBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_cow_customBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_cow_customBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_cow_customBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_cow_customBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_cow_customBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_cow_customBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_cow_customBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_cow_customBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_cow_customBacking_appendBytes") { TestData().test_validateMutation_slice_cow_customBacking_appendBytes() } -DataTests.test("test_validateMutation_slice_cow_customBacking_appendData") { TestData().test_validateMutation_slice_cow_customBacking_appendData() } -DataTests.test("test_validateMutation_slice_cow_customBacking_appendBuffer") { TestData().test_validateMutation_slice_cow_customBacking_appendBuffer() } -DataTests.test("test_validateMutation_slice_cow_customBacking_appendSequence") { TestData().test_validateMutation_slice_cow_customBacking_appendSequence() } -DataTests.test("test_validateMutation_slice_cow_customBacking_appendContentsOf") { TestData().test_validateMutation_slice_cow_customBacking_appendContentsOf() } -DataTests.test("test_validateMutation_slice_cow_customBacking_resetBytes") { TestData().test_validateMutation_slice_cow_customBacking_resetBytes() } -DataTests.test("test_validateMutation_slice_cow_customBacking_replaceSubrange") { TestData().test_validateMutation_slice_cow_customBacking_replaceSubrange() } -DataTests.test("test_validateMutation_slice_cow_customBacking_replaceSubrangeRange") { TestData().test_validateMutation_slice_cow_customBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_cow_customBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_cow_customBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_cow_customBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_cow_customBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_cow_customBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_cow_customBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_customMutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_customMutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_customMutableBacking_appendBytes") { TestData().test_validateMutation_customMutableBacking_appendBytes() } -DataTests.test("test_validateMutation_customMutableBacking_appendData") { TestData().test_validateMutation_customMutableBacking_appendData() } -DataTests.test("test_validateMutation_customMutableBacking_appendBuffer") { TestData().test_validateMutation_customMutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_customMutableBacking_appendSequence") { TestData().test_validateMutation_customMutableBacking_appendSequence() } -DataTests.test("test_validateMutation_customMutableBacking_appendContentsOf") { TestData().test_validateMutation_customMutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_customMutableBacking_resetBytes") { TestData().test_validateMutation_customMutableBacking_resetBytes() } -DataTests.test("test_validateMutation_customMutableBacking_replaceSubrange") { TestData().test_validateMutation_customMutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_customMutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_customMutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_customMutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_customMutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_customMutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_customMutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_customMutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_customMutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_customMutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_customMutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_customMutableBacking_appendBytes") { TestData().test_validateMutation_slice_customMutableBacking_appendBytes() } -DataTests.test("test_validateMutation_slice_customMutableBacking_appendData") { TestData().test_validateMutation_slice_customMutableBacking_appendData() } -DataTests.test("test_validateMutation_slice_customMutableBacking_appendBuffer") { TestData().test_validateMutation_slice_customMutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_slice_customMutableBacking_appendSequence") { TestData().test_validateMutation_slice_customMutableBacking_appendSequence() } -DataTests.test("test_validateMutation_slice_customMutableBacking_appendContentsOf") { TestData().test_validateMutation_slice_customMutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_slice_customMutableBacking_resetBytes") { TestData().test_validateMutation_slice_customMutableBacking_resetBytes() } -DataTests.test("test_validateMutation_slice_customMutableBacking_replaceSubrange") { TestData().test_validateMutation_slice_customMutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_slice_customMutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_slice_customMutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_customMutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_customMutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_customMutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_customMutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_customMutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_customMutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_cow_customMutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_cow_customMutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_cow_customMutableBacking_appendBytes") { TestData().test_validateMutation_cow_customMutableBacking_appendBytes() } -DataTests.test("test_validateMutation_cow_customMutableBacking_appendData") { TestData().test_validateMutation_cow_customMutableBacking_appendData() } -DataTests.test("test_validateMutation_cow_customMutableBacking_appendBuffer") { TestData().test_validateMutation_cow_customMutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_cow_customMutableBacking_appendSequence") { TestData().test_validateMutation_cow_customMutableBacking_appendSequence() } -DataTests.test("test_validateMutation_cow_customMutableBacking_appendContentsOf") { TestData().test_validateMutation_cow_customMutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_cow_customMutableBacking_resetBytes") { TestData().test_validateMutation_cow_customMutableBacking_resetBytes() } -DataTests.test("test_validateMutation_cow_customMutableBacking_replaceSubrange") { TestData().test_validateMutation_cow_customMutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_cow_customMutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_cow_customMutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_cow_customMutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_cow_customMutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_cow_customMutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_cow_customMutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_cow_customMutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_cow_customMutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_withUnsafeMutableBytes") { TestData().test_validateMutation_slice_cow_customMutableBacking_withUnsafeMutableBytes() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_appendBytes") { TestData().test_validateMutation_slice_cow_customMutableBacking_appendBytes() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_appendData") { TestData().test_validateMutation_slice_cow_customMutableBacking_appendData() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_appendBuffer") { TestData().test_validateMutation_slice_cow_customMutableBacking_appendBuffer() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_appendSequence") { TestData().test_validateMutation_slice_cow_customMutableBacking_appendSequence() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_appendContentsOf") { TestData().test_validateMutation_slice_cow_customMutableBacking_appendContentsOf() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_resetBytes") { TestData().test_validateMutation_slice_cow_customMutableBacking_resetBytes() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_replaceSubrange") { TestData().test_validateMutation_slice_cow_customMutableBacking_replaceSubrange() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_replaceSubrangeRange") { TestData().test_validateMutation_slice_cow_customMutableBacking_replaceSubrangeRange() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_replaceSubrangeWithBuffer") { TestData().test_validateMutation_slice_cow_customMutableBacking_replaceSubrangeWithBuffer() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_replaceSubrangeWithCollection") { TestData().test_validateMutation_slice_cow_customMutableBacking_replaceSubrangeWithCollection() } -DataTests.test("test_validateMutation_slice_cow_customMutableBacking_replaceSubrangeWithBytes") { TestData().test_validateMutation_slice_cow_customMutableBacking_replaceSubrangeWithBytes() } -DataTests.test("test_sliceHash") { TestData().test_sliceHash() } -DataTests.test("test_slice_resize_growth") { TestData().test_slice_resize_growth() } -DataTests.test("test_hashEmptyData") { TestData().test_hashEmptyData() } -DataTests.test("test_validateMutation_slice_withUnsafeMutableBytes_lengthLessThanLowerBound") { TestData().test_validateMutation_slice_withUnsafeMutableBytes_lengthLessThanLowerBound() } -DataTests.test("test_validateMutation_slice_immutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound") { TestData().test_validateMutation_slice_immutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() } -DataTests.test("test_validateMutation_slice_mutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound") { TestData().test_validateMutation_slice_mutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() } -DataTests.test("test_validateMutation_slice_customBacking_withUnsafeMutableBytes_lengthLessThanLowerBound") { TestData().test_validateMutation_slice_customBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() } -DataTests.test("test_validateMutation_slice_customMutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound") { TestData().test_validateMutation_slice_customMutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() } -DataTests.test("test_byte_access_of_discontiguousData") { TestData().test_byte_access_of_discontiguousData() } -DataTests.test("test_rangeOfSlice") { TestData().test_rangeOfSlice() } -if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { - DataTests.test("test_nsdataSequence") { TestData().test_nsdataSequence() } - DataTests.test("test_dispatchSequence") { TestData().test_dispatchSequence() } -} -DataTests.test("test_increaseCount") { TestData().test_increaseCount() } -DataTests.test("test_decreaseCount") { TestData().test_decreaseCount() } -DataTests.test("test_increaseCount") { TestData().test_init_TestSequence() } -DataTests.test("test_decreaseCount") { TestData().test_append_TestSequence() } - - -// XCTest does not have a crash detection, whereas lit does -DataTests.test("bounding failure subdata") { - let data = "Hello World".data(using: .utf8)! - expectCrashLater() - let c = data.subdata(in: 5..<200) -} - -DataTests.test("bounding failure replace") { - var data = "Hello World".data(using: .utf8)! - expectCrashLater() - data.replaceSubrange(5..<200, with: Data()) -} - -DataTests.test("bounding failure replace2") { - var data = "a".data(using: .utf8)! - var bytes : [UInt8] = [1, 2, 3] - expectCrashLater() - bytes.withUnsafeBufferPointer { - // lowerBound ok, upperBound after end of data - data.replaceSubrange(0..<2, with: $0) - } -} - -DataTests.test("bounding failure replace3") { - var data = "a".data(using: .utf8)! - var bytes : [UInt8] = [1, 2, 3] - expectCrashLater() - bytes.withUnsafeBufferPointer { - // lowerBound is > length - data.replaceSubrange(2..<4, with: $0) - } -} - -DataTests.test("bounding failure replace4") { - var data = "a".data(using: .utf8)! - var bytes : [UInt8] = [1, 2, 3] - expectCrashLater() - // lowerBound is > length - data.replaceSubrange(2..<4, with: bytes) -} - -DataTests.test("bounding failure reset range") { - var data = "Hello World".data(using: .utf8)! - expectCrashLater() - data.resetBytes(in: 100..<200) -} - -DataTests.test("bounding failure append bad length") { - var data = "Hello World".data(using: .utf8)! - expectCrashLater() - data.append("hello", count: -2) -} - -DataTests.test("bounding failure append absurd length") { - var data = "Hello World".data(using: .utf8)! - expectCrashLater() - data.append("hello", count: Int.min) -} - -DataTests.test("bounding failure subscript") { - var data = "Hello World".data(using: .utf8)! - expectCrashLater() - data[100] = 4 -} - - -runAllTests() -#endif diff --git a/test/stdlib/TestData_Swift4.swift b/test/stdlib/TestData_Swift4.swift deleted file mode 100644 index 74b69176bc5e5..0000000000000 --- a/test/stdlib/TestData_Swift4.swift +++ /dev/null @@ -1,36 +0,0 @@ - -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %empty-directory(%t) -// RUN: %target-build-swift -swift-version 4 %s -o %t/TestData_Swift4 -// RUN: %target-codesign %t/TestData_Swift4 -// RUN: %target-run %t/TestData_Swift4 -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation -import StdlibUnittest - -var DataTests = TestSuite("TestDataSwift4") - -DataTests.test("functional map init usage") { - let res1 = [[UInt8(0), UInt8(1), UInt8(2)]].map(Data.init) // previously this could be done without being ambiguous (however in swift 4.2 an overload was added that makes it ambiguous as a function ref) - // the following two strategies are preferred to the previous version - let res2 = [[UInt8(0), UInt8(1), UInt8(2)]].map(Data.init(_:)) - let res3 = [[UInt8(0), UInt8(1), UInt8(2)]].map { Data($0) } - - expectEqual(res1.count, 1) - expectEqual(res2.count, 1) - expectEqual(res3.count, 1) - - expectEqual(res1[0], res2[0]) - expectEqual(res2[0], res3[0]) -} - -runAllTests() diff --git a/test/stdlib/TestDate.swift b/test/stdlib/TestDate.swift deleted file mode 100644 index e09c1de0010cb..0000000000000 --- a/test/stdlib/TestDate.swift +++ /dev/null @@ -1,229 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation -import CoreFoundation - -#if FOUNDATION_XCTEST -import XCTest -class TestDateSuper : XCTestCase { } -#else -import StdlibUnittest -class TestDateSuper { } -#endif - -class TestDate : TestDateSuper { - - func testDateComparison() { - let d1 = Date() - let d2 = d1 + 1 - - expectTrue(d2 > d1) - expectTrue(d1 < d2) - - let d3 = Date(timeIntervalSince1970: 12345) - let d4 = Date(timeIntervalSince1970: 12345) - - expectTrue(d3 == d4) - expectTrue(d3 <= d4) - expectTrue(d4 >= d3) - } - - func testDateMutation() { - let d0 = Date() - var d1 = Date() - d1 = d1 + 1 - let d2 = Date(timeIntervalSinceNow: 10) - - expectTrue(d2 > d1) - expectTrue(d1 != d0) - - let d3 = d1 - d1 += 10 - expectTrue(d1 > d3) - } - - func testCast() { - let d0 = NSDate() - let d1 = d0 as Date - expectEqual(d0.timeIntervalSinceReferenceDate, d1.timeIntervalSinceReferenceDate) - } - - func testDistantPast() { - let distantPast = Date.distantPast - let currentDate = Date() - expectTrue(distantPast < currentDate) - expectTrue(currentDate > distantPast) - expectTrue(distantPast.timeIntervalSince(currentDate) < 3600.0*24*365*100) /* ~1 century in seconds */ - } - - func testDistantFuture() { - let distantFuture = Date.distantFuture - let currentDate = Date() - expectTrue(currentDate < distantFuture) - expectTrue(distantFuture > currentDate) - expectTrue(distantFuture.timeIntervalSince(currentDate) > 3600.0*24*365*100) /* ~1 century in seconds */ - } - - func dateWithString(_ str: String) -> Date { - let formatter = DateFormatter() - // Note: Calendar(identifier:) is OSX 10.9+ and iOS 8.0+ whereas the CF version has always been available - formatter.calendar = Calendar(identifier: .gregorian) - formatter.locale = Locale(identifier: "en_US") - formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z" - return formatter.date(from: str)! as Date - } - - func testEquality() { - let date = dateWithString("2010-05-17 14:49:47 -0700") - let sameDate = dateWithString("2010-05-17 14:49:47 -0700") - expectEqual(date, sameDate) - expectEqual(sameDate, date) - - let differentDate = dateWithString("2010-05-17 14:49:46 -0700") - expectNotEqual(date, differentDate) - expectNotEqual(differentDate, date) - - let sameDateByTimeZone = dateWithString("2010-05-17 13:49:47 -0800") - expectEqual(date, sameDateByTimeZone) - expectEqual(sameDateByTimeZone, date) - - let differentDateByTimeZone = dateWithString("2010-05-17 14:49:47 -0800") - expectNotEqual(date, differentDateByTimeZone) - expectNotEqual(differentDateByTimeZone, date) - } - - func testTimeIntervalSinceDate() { - let referenceDate = dateWithString("1900-01-01 00:00:00 +0000") - let sameDate = dateWithString("1900-01-01 00:00:00 +0000") - let laterDate = dateWithString("2010-05-17 14:49:47 -0700") - let earlierDate = dateWithString("1810-05-17 14:49:47 -0700") - - let laterSeconds = laterDate.timeIntervalSince(referenceDate) - expectEqual(laterSeconds, 3483121787.0) - - let earlierSeconds = earlierDate.timeIntervalSince(referenceDate) - expectEqual(earlierSeconds, -2828311813.0) - - let sameSeconds = sameDate.timeIntervalSince(referenceDate) - expectEqual(sameSeconds, 0.0) - } - - func testDateComponents() { - // Make sure the optional init stuff works - let dc = DateComponents() - - expectNil(dc.year) - - let dc2 = DateComponents(year: 1999) - - expectNil(dc2.day) - expectEqual(1999, dc2.year) - } - - func test_DateHashing() { - let values: [Date] = [ - dateWithString("2010-05-17 14:49:47 -0700"), - dateWithString("2011-05-17 14:49:47 -0700"), - dateWithString("2010-06-17 14:49:47 -0700"), - dateWithString("2010-05-18 14:49:47 -0700"), - dateWithString("2010-05-17 15:49:47 -0700"), - dateWithString("2010-05-17 14:50:47 -0700"), - dateWithString("2010-05-17 14:49:48 -0700"), - ] - checkHashable(values, equalityOracle: { $0 == $1 }) - } - - func test_AnyHashableContainingDate() { - let values: [Date] = [ - dateWithString("2016-05-17 14:49:47 -0700"), - dateWithString("2010-05-17 14:49:47 -0700"), - dateWithString("2010-05-17 14:49:47 -0700"), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Date.self, type(of: anyHashables[0].base)) - expectEqual(Date.self, type(of: anyHashables[1].base)) - expectEqual(Date.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSDate() { - let values: [NSDate] = [ - NSDate(timeIntervalSince1970: 1000000000), - NSDate(timeIntervalSince1970: 1000000001), - NSDate(timeIntervalSince1970: 1000000001), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Date.self, type(of: anyHashables[0].base)) - expectEqual(Date.self, type(of: anyHashables[1].base)) - expectEqual(Date.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableContainingDateComponents() { - let values: [DateComponents] = [ - DateComponents(year: 2016), - DateComponents(year: 1995), - DateComponents(year: 1995), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(DateComponents.self, type(of: anyHashables[0].base)) - expectEqual(DateComponents.self, type(of: anyHashables[1].base)) - expectEqual(DateComponents.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSDateComponents() { - func makeNSDateComponents(year: Int) -> NSDateComponents { - let result = NSDateComponents() - result.year = year - return result - } - let values: [NSDateComponents] = [ - makeNSDateComponents(year: 2016), - makeNSDateComponents(year: 1995), - makeNSDateComponents(year: 1995), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(DateComponents.self, type(of: anyHashables[0].base)) - expectEqual(DateComponents.self, type(of: anyHashables[1].base)) - expectEqual(DateComponents.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_dateComponents_unconditionallyBridgeFromObjectiveC() { - expectEqual(DateComponents(), DateComponents._unconditionallyBridgeFromObjectiveC(nil)) - } -} - -#if !FOUNDATION_XCTEST -var DateTests = TestSuite("TestDate") -DateTests.test("testDateComparison") { TestDate().testDateComparison() } -DateTests.test("testDateMutation") { TestDate().testDateMutation() } -DateTests.test("testCast") { TestDate().testCast() } -DateTests.test("testDistantPast") { TestDate().testDistantPast() } -DateTests.test("testDistantFuture") { TestDate().testDistantFuture() } -DateTests.test("testEquality") { TestDate().testEquality() } -DateTests.test("testTimeIntervalSinceDate") { TestDate().testTimeIntervalSinceDate() } -DateTests.test("testDateComponents") { TestDate().testDateComponents() } -DateTests.test("test_DateHashing") { TestDate().test_DateHashing() } -DateTests.test("test_AnyHashableContainingDate") { TestDate().test_AnyHashableContainingDate() } -DateTests.test("test_AnyHashableCreatedFromNSDate") { TestDate().test_AnyHashableCreatedFromNSDate() } -DateTests.test("test_AnyHashableContainingDateComponents") { TestDate().test_AnyHashableContainingDateComponents() } -DateTests.test("test_AnyHashableCreatedFromNSDateComponents") { TestDate().test_AnyHashableCreatedFromNSDateComponents() } -DateTests.test("test_dateComponents_unconditionallyBridgeFromObjectiveC") { TestDate().test_dateComponents_unconditionallyBridgeFromObjectiveC() } -runAllTests() -#endif diff --git a/test/stdlib/TestDateInterval.swift b/test/stdlib/TestDateInterval.swift deleted file mode 100644 index cda651c8d03dc..0000000000000 --- a/test/stdlib/TestDateInterval.swift +++ /dev/null @@ -1,210 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestDateIntervalSuper : XCTestCase { } -#else -import StdlibUnittest -class TestDateIntervalSuper { } -#endif - -class TestDateInterval : TestDateIntervalSuper { - func dateWithString(_ str: String) -> Date { - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .gregorian) - formatter.locale = Locale(identifier: "en_US") - formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z" - return formatter.date(from: str)! as Date - } - - func test_compareDateIntervals() { - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let start = dateWithString("2010-05-17 14:49:47 -0700") - let duration: TimeInterval = 10000000.0 - let testInterval1 = DateInterval(start: start, duration: duration) - let testInterval2 = DateInterval(start: start, duration: duration) - expectEqual(testInterval1, testInterval2) - expectEqual(testInterval2, testInterval1) - expectEqual(testInterval1.compare(testInterval2), ComparisonResult.orderedSame) - - let testInterval3 = DateInterval(start: start, duration: 10000000000.0) - expectTrue(testInterval1 < testInterval3) - expectTrue(testInterval3 > testInterval1) - - let earlierStart = dateWithString("2009-05-17 14:49:47 -0700") - let testInterval4 = DateInterval(start: earlierStart, duration: duration) - - expectTrue(testInterval4 < testInterval1) - expectTrue(testInterval1 > testInterval4) - } - } - - func test_isEqualToDateInterval() { - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let start = dateWithString("2010-05-17 14:49:47 -0700") - let duration = 10000000.0 - let testInterval1 = DateInterval(start: start, duration: duration) - let testInterval2 = DateInterval(start: start, duration: duration) - - expectEqual(testInterval1, testInterval2) - - let testInterval3 = DateInterval(start: start, duration: 100.0) - expectNotEqual(testInterval1, testInterval3) - } - } - - func test_hashing() { - guard #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) else { return } - - let start1a = dateWithString("2019-04-04 17:09:23 -0700") - let start1b = dateWithString("2019-04-04 17:09:23 -0700") - let start2a = Date(timeIntervalSinceReferenceDate: start1a.timeIntervalSinceReferenceDate.nextUp) - let start2b = Date(timeIntervalSinceReferenceDate: start1a.timeIntervalSinceReferenceDate.nextUp) - let duration1 = 1800.0 - let duration2 = duration1.nextUp - let intervals: [[DateInterval]] = [ - [ - DateInterval(start: start1a, duration: duration1), - DateInterval(start: start1b, duration: duration1), - ], - [ - DateInterval(start: start1a, duration: duration2), - DateInterval(start: start1b, duration: duration2), - ], - [ - DateInterval(start: start2a, duration: duration1), - DateInterval(start: start2b, duration: duration1), - ], - [ - DateInterval(start: start2a, duration: duration2), - DateInterval(start: start2b, duration: duration2), - ], - ] - checkHashableGroups(intervals) - } - - func test_checkIntersection() { - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let start1 = dateWithString("2010-05-17 14:49:47 -0700") - let end1 = dateWithString("2010-08-17 14:49:47 -0700") - - let testInterval1 = DateInterval(start: start1, end: end1) - - let start2 = dateWithString("2010-02-17 14:49:47 -0700") - let end2 = dateWithString("2010-07-17 14:49:47 -0700") - - let testInterval2 = DateInterval(start: start2, end: end2) - - expectTrue(testInterval1.intersects(testInterval2)) - - let start3 = dateWithString("2010-10-17 14:49:47 -0700") - let end3 = dateWithString("2010-11-17 14:49:47 -0700") - - let testInterval3 = DateInterval(start: start3, end: end3) - - expectFalse(testInterval1.intersects(testInterval3)) - } - } - - func test_validIntersections() { - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let start1 = dateWithString("2010-05-17 14:49:47 -0700") - let end1 = dateWithString("2010-08-17 14:49:47 -0700") - - let testInterval1 = DateInterval(start: start1, end: end1) - - let start2 = dateWithString("2010-02-17 14:49:47 -0700") - let end2 = dateWithString("2010-07-17 14:49:47 -0700") - - let testInterval2 = DateInterval(start: start2, end: end2) - - let start3 = dateWithString("2010-05-17 14:49:47 -0700") - let end3 = dateWithString("2010-07-17 14:49:47 -0700") - - let testInterval3 = DateInterval(start: start3, end: end3) - - let intersection1 = testInterval2.intersection(with: testInterval1) - expectNotNil(intersection1) - expectEqual(testInterval3, intersection1) - - let intersection2 = testInterval1.intersection(with: testInterval2) - expectNotNil(intersection2) - expectEqual(intersection1, intersection2) - } - } - - func test_containsDate() { - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let start = dateWithString("2010-05-17 14:49:47 -0700") - let duration = 10000000.0 - - let testInterval = DateInterval(start: start, duration: duration) - let containedDate = dateWithString("2010-05-17 20:49:47 -0700") - - expectTrue(testInterval.contains(containedDate)) - - let earlierStart = dateWithString("2009-05-17 14:49:47 -0700") - expectFalse(testInterval.contains(earlierStart)) - } - } - - func test_AnyHashableContainingDateInterval() { - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let start = dateWithString("2010-05-17 14:49:47 -0700") - let duration = 10000000.0 - let values: [DateInterval] = [ - DateInterval(start: start, duration: duration), - DateInterval(start: start, duration: duration / 2), - DateInterval(start: start, duration: duration / 2), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(DateInterval.self, type(of: anyHashables[0].base)) - expectEqual(DateInterval.self, type(of: anyHashables[1].base)) - expectEqual(DateInterval.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - } - - func test_AnyHashableCreatedFromNSDateInterval() { - if #available(iOS 10.10, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - let start = dateWithString("2010-05-17 14:49:47 -0700") - let duration = 10000000.0 - let values: [NSDateInterval] = [ - NSDateInterval(start: start, duration: duration), - NSDateInterval(start: start, duration: duration / 2), - NSDateInterval(start: start, duration: duration / 2), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(DateInterval.self, type(of: anyHashables[0].base)) - expectEqual(DateInterval.self, type(of: anyHashables[1].base)) - expectEqual(DateInterval.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - } -} - -#if !FOUNDATION_XCTEST -var DateIntervalTests = TestSuite("TestDateInterval") -DateIntervalTests.test("test_compareDateIntervals") { TestDateInterval().test_compareDateIntervals() } -DateIntervalTests.test("test_isEqualToDateInterval") { TestDateInterval().test_isEqualToDateInterval() } -DateIntervalTests.test("test_hashing") { TestDateInterval().test_hashing() } -DateIntervalTests.test("test_checkIntersection") { TestDateInterval().test_checkIntersection() } -DateIntervalTests.test("test_validIntersections") { TestDateInterval().test_validIntersections() } -DateIntervalTests.test("test_AnyHashableContainingDateInterval") { TestDateInterval().test_AnyHashableContainingDateInterval() } -DateIntervalTests.test("test_AnyHashableCreatedFromNSDateInterval") { TestDateInterval().test_AnyHashableCreatedFromNSDateInterval() } -runAllTests() -#endif diff --git a/test/stdlib/TestDecimal.swift b/test/stdlib/TestDecimal.swift deleted file mode 100644 index ee0c7e492432f..0000000000000 --- a/test/stdlib/TestDecimal.swift +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright (c) 2014 - 2018 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %empty-directory(%t) -// -// RUN: %target-clang %S/Inputs/FoundationBridge/FoundationBridge.m -c -o %t/FoundationBridgeObjC.o -g -// RUN: %target-build-swift %s -I %S/Inputs/FoundationBridge/ -Xlinker %t/FoundationBridgeObjC.o -o %t/TestDecimal -// RUN: %target-codesign %t/TestDecimal - -// RUN: %target-run %t/TestDecimal > %t.txt -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation -import FoundationBridgeObjC - -#if FOUNDATION_XCTEST - import XCTest - class TestDecimalSuper : XCTestCase { } -#else - import StdlibUnittest - class TestDecimalSuper { } -#endif - -class TestDecimal : TestDecimalSuper { - func test_AdditionWithNormalization() { - - let biggie = Decimal(65536) - let smallee = Decimal(65536) - let answer = biggie/smallee - expectEqual(Decimal(1),answer) - - var one = Decimal(1) - var addend = Decimal(1) - var expected = Decimal() - var result = Decimal() - - expected._isNegative = 0; - expected._isCompact = 0; - - // 2 digits -- certain to work - addend._exponent = -1; - expectEqual(.noError, NSDecimalAdd(&result, &one, &addend, .plain), "1 + 0.1") - expected._exponent = -1; - expected._length = 1; - expected._mantissa.0 = 11; - expectEqual(.orderedSame, NSDecimalCompare(&expected, &result), "1.1 == 1 + 0.1") - - // 38 digits -- guaranteed by NSDecimal to work - addend._exponent = -37; - expectEqual(.noError, NSDecimalAdd(&result, &one, &addend, .plain), "1 + 1e-37") - expected._exponent = -37; - expected._length = 8; - expected._mantissa.0 = 0x0001; - expected._mantissa.1 = 0x0000; - expected._mantissa.2 = 0x36a0; - expected._mantissa.3 = 0x00f4; - expected._mantissa.4 = 0x46d9; - expected._mantissa.5 = 0xd5da; - expected._mantissa.6 = 0xee10; - expected._mantissa.7 = 0x0785; - expectEqual(.orderedSame, NSDecimalCompare(&expected, &result), "1 + 1e-37") - - // 39 digits -- not guaranteed to work but it happens to, so we make the test work either way - addend._exponent = -38; - let error = NSDecimalAdd(&result, &one, &addend, .plain) - expectTrue(error == .noError || error == .lossOfPrecision, "1 + 1e-38") - if error == .noError { - expected._exponent = -38; - expected._length = 8; - expected._mantissa.0 = 0x0001; - expected._mantissa.1 = 0x0000; - expected._mantissa.2 = 0x2240; - expected._mantissa.3 = 0x098a; - expected._mantissa.4 = 0xc47a; - expected._mantissa.5 = 0x5a86; - expected._mantissa.6 = 0x4ca8; - expected._mantissa.7 = 0x4b3b; - expectEqual(.orderedSame, NSDecimalCompare(&expected, &result), "1 + 1e-38") - } else { - expectEqual(.orderedSame, NSDecimalCompare(&one, &result), "1 + 1e-38") - } - - // 40 digits -- doesn't work; need to make sure it's rounding for us - addend._exponent = -39; - expectEqual(.lossOfPrecision, NSDecimalAdd(&result, &one, &addend, .plain), "1 + 1e-39") - expectEqual("1", result.description) - expectEqual(.orderedSame, NSDecimalCompare(&one, &result), "1 + 1e-39") - } - - func test_BasicConstruction() { - let zero = Decimal() - expectEqual(20, MemoryLayout.size) - expectEqual(0, zero._exponent) - expectEqual(0, zero._length) - expectEqual(0, zero._isNegative) - expectEqual(0, zero._isCompact) - expectEqual(0, zero._reserved) - let (m0, m1, m2, m3, m4, m5, m6, m7) = zero._mantissa - expectEqual(0, m0) - expectEqual(0, m1) - expectEqual(0, m2) - expectEqual(0, m3) - expectEqual(0, m4) - expectEqual(0, m5) - expectEqual(0, m6) - expectEqual(0, m7) - expectEqual(8, NSDecimalMaxSize) - expectEqual(32767, NSDecimalNoScale) - expectFalse(zero.isNormal) - expectTrue(zero.isFinite) - expectTrue(zero.isZero) - expectFalse(zero.isSubnormal) - expectFalse(zero.isInfinite) - expectFalse(zero.isNaN) - expectFalse(zero.isSignaling) - - if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { - let d1 = Decimal(1234567890123456789 as UInt64) - expectEqual(d1._exponent, 0) - expectEqual(d1._length, 4) - } - } - func test_Constants() { - expectEqual(8, NSDecimalMaxSize) - expectEqual(32767, NSDecimalNoScale) - let smallest = Decimal(_exponent: 127, _length: 8, _isNegative: 1, _isCompact: 1, _reserved: 0, _mantissa: (UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max)) - expectEqual(smallest, Decimal.leastFiniteMagnitude) - let biggest = Decimal(_exponent: 127, _length: 8, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max, UInt16.max)) - expectEqual(biggest, Decimal.greatestFiniteMagnitude) - let leastNormal = Decimal(_exponent: -127, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1, 0, 0, 0, 0, 0, 0, 0)) - expectEqual(leastNormal, Decimal.leastNormalMagnitude) - let leastNonzero = Decimal(_exponent: -127, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1, 0, 0, 0, 0, 0, 0, 0)) - expectEqual(leastNonzero, Decimal.leastNonzeroMagnitude) - let pi = Decimal(_exponent: -38, _length: 8, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (0x6623, 0x7d57, 0x16e7, 0xad0d, 0xaf52, 0x4641, 0xdfa7, 0xec58)) - expectEqual(pi, Decimal.pi) - expectEqual(10, Decimal.radix) - expectTrue(Decimal().isCanonical) - expectFalse(Decimal().isSignalingNaN) - expectFalse(Decimal.nan.isSignalingNaN) - expectTrue(Decimal.nan.isNaN) - expectEqual(.quietNaN, Decimal.nan.floatingPointClass) - expectEqual(.positiveZero, Decimal().floatingPointClass) - expectEqual(.negativeNormal, smallest.floatingPointClass) - expectEqual(.positiveNormal, biggest.floatingPointClass) - expectFalse(Double.nan.isFinite) - expectFalse(Double.nan.isInfinite) - } - - func test_Description() { - expectEqual("0", Decimal().description) - expectEqual("0", Decimal(0).description) - expectEqual("10", Decimal(_exponent: 1, _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (1, 0, 0, 0, 0, 0, 0, 0)).description) - expectEqual("10", Decimal(10).description) - expectEqual("123.458", Decimal(_exponent: -3, _length: 2, _isNegative: 0, _isCompact:1, _reserved: 0, _mantissa: (57922, 1, 0, 0, 0, 0, 0, 0)).description) - expectEqual("123.458", Decimal(123.458).description) - expectEqual("123", Decimal(UInt8(123)).description) - expectEqual("45", Decimal(Int8(45)).description) - expectEqual("3.14159265358979323846264338327950288419", Decimal.pi.description) - expectEqual("-30000000000", Decimal(sign: .minus, exponent: 10, significand: Decimal(3)).description) - expectEqual("300000", Decimal(sign: .plus, exponent: 5, significand: Decimal(3)).description) - expectEqual("5", Decimal(signOf: Decimal(3), magnitudeOf: Decimal(5)).description) - expectEqual("-5", Decimal(signOf: Decimal(-3), magnitudeOf: Decimal(5)).description) - expectEqual("5", Decimal(signOf: Decimal(3), magnitudeOf: Decimal(-5)).description) - expectEqual("-5", Decimal(signOf: Decimal(-3), magnitudeOf: Decimal(-5)).description) - } - - func test_ExplicitConstruction() { - var explicit = Decimal( - _exponent: 0x17f, - _length: 0xff, - _isNegative: 3, - _isCompact: 4, - _reserved: UInt32(1<<18 + 1<<17 + 1), - _mantissa: (6, 7, 8, 9, 10, 11, 12, 13) - ) - expectEqual(0x7f, explicit._exponent) - expectEqual(0x7f, explicit.exponent) - expectEqual(0x0f, explicit._length) - expectEqual(1, explicit._isNegative) - expectEqual(FloatingPointSign.minus, explicit.sign) - expectTrue(explicit.isSignMinus) - expectEqual(0, explicit._isCompact) - expectEqual(UInt32(1<<17 + 1), explicit._reserved) - let (m0, m1, m2, m3, m4, m5, m6, m7) = explicit._mantissa - expectEqual(6, m0) - expectEqual(7, m1) - expectEqual(8, m2) - expectEqual(9, m3) - expectEqual(10, m4) - expectEqual(11, m5) - expectEqual(12, m6) - expectEqual(13, m7) - explicit._isCompact = 5 - explicit._isNegative = 6 - expectEqual(0, explicit._isNegative) - expectEqual(1, explicit._isCompact) - expectEqual(FloatingPointSign.plus, explicit.sign) - expectFalse(explicit.isSignMinus) - expectTrue(explicit.isNormal) - - let significand = explicit.significand - expectEqual(0, significand._exponent) - expectEqual(0, significand.exponent) - expectEqual(0x0f, significand._length) - expectEqual(0, significand._isNegative) - expectEqual(1, significand._isCompact) - expectEqual(0, significand._reserved) - let (sm0, sm1, sm2, sm3, sm4, sm5, sm6, sm7) = significand._mantissa - expectEqual(6, sm0) - expectEqual(7, sm1) - expectEqual(8, sm2) - expectEqual(9, sm3) - expectEqual(10, sm4) - expectEqual(11, sm5) - expectEqual(12, sm6) - expectEqual(13, sm7) - - let ulp = explicit.ulp - expectEqual(0x7f, ulp.exponent) - expectEqual(8, ulp._length) - expectEqual(0, ulp._isNegative) - expectEqual(1, ulp._isCompact) - expectEqual(0, ulp._reserved) - expectEqual(1, ulp._mantissa.0) - expectEqual(0, ulp._mantissa.1) - expectEqual(0, ulp._mantissa.2) - expectEqual(0, ulp._mantissa.3) - expectEqual(0, ulp._mantissa.4) - expectEqual(0, ulp._mantissa.5) - expectEqual(0, ulp._mantissa.6) - expectEqual(0, ulp._mantissa.7) - } - - func test_Maths() { - for i in -2...10 { - for j in 0...5 { - expectEqual(Decimal(i*j), Decimal(i) * Decimal(j), "\(Decimal(i*j)) == \(i) * \(j)") - expectEqual(Decimal(i+j), Decimal(i) + Decimal(j), "\(Decimal(i+j)) == \(i)+\(j)") - expectEqual(Decimal(i-j), Decimal(i) - Decimal(j), "\(Decimal(i-j)) == \(i)-\(j)") - if j != 0 { - let approximation = Decimal(Double(i)/Double(j)) - let answer = Decimal(i) / Decimal(j) - let answerDescription = answer.description - let approximationDescription = approximation.description - var failed: Bool = false - var count = 0 - let SIG_FIG = 14 - for (a, b) in zip(answerDescription, approximationDescription) { - if a != b { - failed = true - break - } - if count == 0 && (a == "-" || a == "0" || a == ".") { - continue // don't count these as significant figures - } - if count >= SIG_FIG { - break - } - count += 1 - } - expectFalse(failed, "\(Decimal(i/j)) == \(i)/\(j)") - } - } - } - } - - func test_Misc() { - expectEqual(.minus, Decimal(-5.2).sign) - expectEqual(.plus, Decimal(5.2).sign) - var d = Decimal(5.2) - expectEqual(.plus, d.sign) - d.negate() - expectEqual(.minus, d.sign) - d.negate() - expectEqual(.plus, d.sign) - var e = Decimal(0) - e.negate() - expectEqual(e, 0) - expectTrue(Decimal(3.5).isEqual(to: Decimal(3.5))) - expectTrue(Decimal.nan.isEqual(to: Decimal.nan)) - expectTrue(Decimal(1.28).isLess(than: Decimal(2.24))) - expectFalse(Decimal(2.28).isLess(than: Decimal(2.24))) - expectTrue(Decimal(1.28).isTotallyOrdered(belowOrEqualTo: Decimal(2.24))) - expectFalse(Decimal(2.28).isTotallyOrdered(belowOrEqualTo: Decimal(2.24))) - expectTrue(Decimal(1.2).isTotallyOrdered(belowOrEqualTo: Decimal(1.2))) - expectTrue(Decimal.nan.isEqual(to: Decimal.nan)) - expectTrue(Decimal.nan.isLess(than: Decimal(0))) - expectFalse(Decimal.nan.isLess(than: Decimal.nan)) - expectTrue(Decimal.nan.isLessThanOrEqualTo(Decimal(0))) - expectTrue(Decimal.nan.isLessThanOrEqualTo(Decimal.nan)) - expectFalse(Decimal.nan.isTotallyOrdered(belowOrEqualTo: Decimal.nan)) - expectFalse(Decimal.nan.isTotallyOrdered(belowOrEqualTo: Decimal(2.3))) - expectTrue(Decimal(2) < Decimal(3)) - expectTrue(Decimal(3) > Decimal(2)) - expectEqual(Decimal(-9), Decimal(1) - Decimal(10)) - expectEqual(Decimal(3), Decimal(2).nextUp) - expectEqual(Decimal(2), Decimal(3).nextDown) - expectEqual(Decimal(-476), Decimal(1024).distance(to: Decimal(1500))) - expectEqual(Decimal(68040), Decimal(386).advanced(by: Decimal(67654))) - expectEqual(Decimal(1.234), abs(Decimal(1.234))) - expectEqual(Decimal(1.234), abs(Decimal(-1.234))) - if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { - expectTrue(Decimal.nan.magnitude.isNaN) - } - var a = Decimal(1234) - var r = a - expectEqual(.noError, NSDecimalMultiplyByPowerOf10(&r, &a, 1, .plain)) - expectEqual(Decimal(12340), r) - a = Decimal(1234) - expectEqual(.noError, NSDecimalMultiplyByPowerOf10(&r, &a, 2, .plain)) - expectEqual(Decimal(123400), r) - expectEqual(.overflow, NSDecimalMultiplyByPowerOf10(&r, &a, 128, .plain)) - expectTrue(r.isNaN) - a = Decimal(1234) - expectEqual(.noError, NSDecimalMultiplyByPowerOf10(&r, &a, -2, .plain)) - expectEqual(Decimal(12.34), r) - var ur = r - expectEqual(.underflow, NSDecimalMultiplyByPowerOf10(&ur, &r, -128, .plain)) - expectTrue(ur.isNaN) - a = Decimal(1234) - expectEqual(.noError, NSDecimalPower(&r, &a, 0, .plain)) - expectEqual(Decimal(1), r) - a = Decimal(8) - expectEqual(.noError, NSDecimalPower(&r, &a, 2, .plain)) - expectEqual(Decimal(64), r) - a = Decimal(-2) - expectEqual(.noError, NSDecimalPower(&r, &a, 3, .plain)) - expectEqual(Decimal(-8), r) - for i in -2...10 { - for j in 0...5 { - var actual = Decimal(i) - var result = actual - expectEqual(.noError, NSDecimalPower(&result, &actual, j, .plain)) - let expected = Decimal(pow(Double(i), Double(j))) - expectEqual(expected, result, "\(result) == \(i)^\(j)") - if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { - expectEqual(expected, pow(actual, j)) - } - } - } - } - - func test_MultiplicationOverflow() { - var multiplicand = Decimal(_exponent: 0, _length: 8, _isNegative: 0, _isCompact: 0, _reserved: 0, _mantissa: ( 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff )) - - var result = Decimal() - var multiplier = Decimal(1) - - multiplier._mantissa.0 = 2 - - expectEqual(.noError, NSDecimalMultiply(&result, &multiplicand, &multiplier, .plain), "2 * max mantissa") - expectEqual(.noError, NSDecimalMultiply(&result, &multiplier, &multiplicand, .plain), "max mantissa * 2") - - multiplier._exponent = 0x7f - expectEqual(.overflow, NSDecimalMultiply(&result, &multiplicand, &multiplier, .plain), "2e127 * max mantissa") - expectEqual(.overflow, NSDecimalMultiply(&result, &multiplier, &multiplicand, .plain), "max mantissa * 2e127") - } - - func test_NaNInput() { - var NaN = Decimal.nan - var one = Decimal(1) - var result = Decimal() - - expectNotEqual(.noError, NSDecimalAdd(&result, &NaN, &one, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN + 1") - expectNotEqual(.noError, NSDecimalAdd(&result, &one, &NaN, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "1 + NaN") - - expectNotEqual(.noError, NSDecimalSubtract(&result, &NaN, &one, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN - 1") - expectNotEqual(.noError, NSDecimalSubtract(&result, &one, &NaN, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "1 - NaN") - - expectNotEqual(.noError, NSDecimalMultiply(&result, &NaN, &one, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN * 1") - expectNotEqual(.noError, NSDecimalMultiply(&result, &one, &NaN, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "1 * NaN") - - expectNotEqual(.noError, NSDecimalDivide(&result, &NaN, &one, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN / 1") - expectNotEqual(.noError, NSDecimalDivide(&result, &one, &NaN, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "1 / NaN") - - expectNotEqual(.noError, NSDecimalPower(&result, &NaN, 0, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN ^ 0") - expectNotEqual(.noError, NSDecimalPower(&result, &NaN, 4, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN ^ 4") - expectNotEqual(.noError, NSDecimalPower(&result, &NaN, 5, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN ^ 5") - - expectNotEqual(.noError, NSDecimalMultiplyByPowerOf10(&result, &NaN, 0, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN e0") - expectNotEqual(.noError, NSDecimalMultiplyByPowerOf10(&result, &NaN, 4, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN e4") - expectNotEqual(.noError, NSDecimalMultiplyByPowerOf10(&result, &NaN, 5, .plain)) - expectTrue(NSDecimalIsNotANumber(&result), "NaN e5") - } - - func test_NegativeAndZeroMultiplication() { - var one = Decimal(1) - var zero = Decimal(0) - var negativeOne = Decimal(-1) - - var result = Decimal() - - expectEqual(.noError, NSDecimalMultiply(&result, &one, &one, .plain), "1 * 1") - expectEqual(.orderedSame, NSDecimalCompare(&one, &result), "1 * 1") - - expectEqual(.noError, NSDecimalMultiply(&result, &one, &negativeOne, .plain), "1 * -1") - expectEqual(.orderedSame, NSDecimalCompare(&negativeOne, &result), "1 * -1") - - expectEqual(.noError, NSDecimalMultiply(&result, &negativeOne, &one, .plain), "-1 * 1") - expectEqual(.orderedSame, NSDecimalCompare(&negativeOne, &result), "-1 * 1") - - expectEqual(.noError, NSDecimalMultiply(&result, &negativeOne, &negativeOne, .plain), "-1 * -1") - expectEqual(.orderedSame, NSDecimalCompare(&one, &result), "-1 * -1") - - expectEqual(.noError, NSDecimalMultiply(&result, &one, &zero, .plain), "1 * 0") - expectEqual(.orderedSame, NSDecimalCompare(&zero, &result), "1 * 0") - expectEqual(0, result._isNegative, "1 * 0") - - expectEqual(.noError, NSDecimalMultiply(&result, &zero, &one, .plain), "0 * 1") - expectEqual(.orderedSame, NSDecimalCompare(&zero, &result), "0 * 1") - expectEqual(0, result._isNegative, "0 * 1") - - expectEqual(.noError, NSDecimalMultiply(&result, &negativeOne, &zero, .plain), "-1 * 0") - expectEqual(.orderedSame, NSDecimalCompare(&zero, &result), "-1 * 0") - expectEqual(0, result._isNegative, "-1 * 0") - - expectEqual(.noError, NSDecimalMultiply(&result, &zero, &negativeOne, .plain), "0 * -1") - expectEqual(.orderedSame, NSDecimalCompare(&zero, &result), "0 * -1") - expectEqual(0, result._isNegative, "0 * -1") - } - - func test_Normalize() { - var one = Decimal(1) - var ten = Decimal(-10) - expectEqual(.noError, NSDecimalNormalize(&one, &ten, .plain)) - expectEqual(Decimal(1), one) - expectEqual(Decimal(-10), ten) - expectEqual(1, one._length) - expectEqual(1, ten._length) - one = Decimal(1) - ten = Decimal(10) - expectEqual(.noError, NSDecimalNormalize(&one, &ten, .plain)) - expectEqual(Decimal(1), one) - expectEqual(Decimal(10), ten) - expectEqual(1, one._length) - expectEqual(1, ten._length) - } - - func test_NSDecimal() { - var nan = Decimal.nan - expectTrue(NSDecimalIsNotANumber(&nan)) - var zero = Decimal() - expectFalse(NSDecimalIsNotANumber(&zero)) - var three = Decimal(3) - var guess = Decimal() - NSDecimalCopy(&guess, &three) - expectEqual(three, guess) - - var f = Decimal(_exponent: 0, _length: 2, _isNegative: 0, _isCompact: 0, _reserved: 0, _mantissa: (0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) - let before = f.description - expectEqual(0, f._isCompact) - NSDecimalCompact(&f) - expectEqual(1, f._isCompact) - let after = f.description - expectEqual(before, after) - } - - func test_RepeatingDivision() { - let repeatingNumerator = Decimal(16) - let repeatingDenominator = Decimal(9) - let repeating = repeatingNumerator / repeatingDenominator - - let numerator = Decimal(1010) - var result = numerator / repeating - - var expected = Decimal() - expected._exponent = -35; - expected._length = 8; - expected._isNegative = 0; - expected._isCompact = 1; - expected._reserved = 0; - expected._mantissa.0 = 51946; - expected._mantissa.1 = 3; - expected._mantissa.2 = 15549; - expected._mantissa.3 = 55864; - expected._mantissa.4 = 57984; - expected._mantissa.5 = 55436; - expected._mantissa.6 = 45186; - expected._mantissa.7 = 10941; - - expectEqual(.orderedSame, NSDecimalCompare(&expected, &result), "568.12500000000000000000000000000248554: \(expected.description) != \(result.description)"); - } - - func test_Round() { - var testCases = [ - // expected, start, scale, round - ( 0, 0.5, 0, Decimal.RoundingMode.down ), - ( 1, 0.5, 0, Decimal.RoundingMode.up ), - ( 2, 2.5, 0, Decimal.RoundingMode.bankers ), - ( 4, 3.5, 0, Decimal.RoundingMode.bankers ), - ( 5, 5.2, 0, Decimal.RoundingMode.plain ), - ( 4.5, 4.5, 1, Decimal.RoundingMode.down ), - ( 5.5, 5.5, 1, Decimal.RoundingMode.up ), - ( 6.5, 6.5, 1, Decimal.RoundingMode.plain ), - ( 7.5, 7.5, 1, Decimal.RoundingMode.bankers ), - - ( -1, -0.5, 0, Decimal.RoundingMode.down ), - ( -2, -2.5, 0, Decimal.RoundingMode.up ), - ( -5, -5.2, 0, Decimal.RoundingMode.plain ), - ( -4.5, -4.5, 1, Decimal.RoundingMode.down ), - ( -5.5, -5.5, 1, Decimal.RoundingMode.up ), - ( -6.5, -6.5, 1, Decimal.RoundingMode.plain ), - ( -7.5, -7.5, 1, Decimal.RoundingMode.bankers ), - ] - if #available(macOS 10.16, iOS 14, watchOS 7, tvOS 14, *) { - testCases += [ - ( -2, -2.5, 0, Decimal.RoundingMode.bankers ), - ( -4, -3.5, 0, Decimal.RoundingMode.bankers ), - ] - } - for testCase in testCases { - let (expected, start, scale, mode) = testCase - var num = Decimal(start) - var r = num - NSDecimalRound(&r, &num, scale, mode) - expectEqual(Decimal(expected), r) - let numnum = NSDecimalNumber(decimal:Decimal(start)) - let behavior = NSDecimalNumberHandler(roundingMode: mode, scale: Int16(scale), raiseOnExactness: false, raiseOnOverflow: true, raiseOnUnderflow: true, raiseOnDivideByZero: true) - let result = numnum.rounding(accordingToBehavior:behavior) - expectEqual(Double(expected), result.doubleValue) - } - } - - func test_ScanDecimal() { - let testCases = [ - // expected, value - ( 123.456e78, "123.456e78" ), - ( -123.456e78, "-123.456e78" ), - ( 123.456, " 123.456 " ), - ( 3.14159, " 3.14159e0" ), - ( 3.14159, " 3.14159e-0" ), - ( 0.314159, " 3.14159e-1" ), - ( 3.14159, " 3.14159e+0" ), - ( 31.4159, " 3.14159e+1" ), - ( 12.34, " 01234e-02"), - ] - for testCase in testCases { - let (expected, string) = testCase - let decimal = Decimal(string:string)! - let aboutOne = Decimal(expected) / decimal - let approximatelyRight = aboutOne >= Decimal(0.99999) && aboutOne <= Decimal(1.00001) - expectTrue(approximatelyRight, "\(expected) ~= \(decimal) : \(aboutOne) \(aboutOne >= Decimal(0.99999)) \(aboutOne <= Decimal(1.00001))" ) - } - guard let ones = Decimal(string:"111111111111111111111111111111111111111") else { - expectUnreachable("Unable to parse Decimal(string:'111111111111111111111111111111111111111')") - return - } - let num = ones / Decimal(9) - guard let answer = Decimal(string:"12345679012345679012345679012345679012.3") else { - expectUnreachable("Unable to parse Decimal(string:'12345679012345679012345679012345679012.3')") - return - } - expectEqual(answer,num,"\(ones) / 9 = \(answer) \(num)") - } - - func test_SimpleMultiplication() { - var multiplicand = Decimal() - multiplicand._isNegative = 0 - multiplicand._isCompact = 0 - multiplicand._length = 1 - multiplicand._exponent = 1 - - var multiplier = multiplicand - multiplier._exponent = 2 - - var expected = multiplicand - expected._isNegative = 0 - expected._isCompact = 0 - expected._exponent = 3 - expected._length = 1 - - var result = Decimal() - - for i in 1.. %t.txt -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestFileManagerSuper : XCTestCase { } -#else -import StdlibUnittest -class TestFileManagerSuper { } -#endif - -class TestFileManager : TestFileManagerSuper { - func testReplaceItem() { - let fm = FileManager.default - - // Temporary directory - let dirPath = (NSTemporaryDirectory() as NSString).appendingPathComponent(NSUUID().uuidString) - try! fm.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) - defer { try! FileManager.default.removeItem(atPath: dirPath) } - - let filePath = (dirPath as NSString).appendingPathComponent("temp_file") - try! "1".write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8) - - let newItemPath = (dirPath as NSString).appendingPathComponent("temp_file_new") - try! "2".write(toFile: newItemPath, atomically: true, encoding: String.Encoding.utf8) - - let result = try! fm.replaceItemAt(URL(fileURLWithPath:filePath, isDirectory:false), withItemAt:URL(fileURLWithPath:newItemPath, isDirectory:false)) - expectEqual(result!.path, filePath) - - let fromDisk = try! String(contentsOf: URL(fileURLWithPath:filePath, isDirectory:false), encoding: String.Encoding.utf8) - expectEqual(fromDisk, "2") - - } - - func testReplaceItem_error() { - let fm = FileManager.default - - // Temporary directory - let dirPath = (NSTemporaryDirectory() as NSString).appendingPathComponent(NSUUID().uuidString) - try! fm.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) - defer { try! FileManager.default.removeItem(atPath: dirPath) } - - let filePath = (dirPath as NSString).appendingPathComponent("temp_file") - try! "1".write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8) - - let newItemPath = (dirPath as NSString).appendingPathComponent("temp_file_new") - // Don't write the file. - - var threw = false - do { - let _ = try fm.replaceItemAt(URL(fileURLWithPath:filePath, isDirectory:false), withItemAt:URL(fileURLWithPath:newItemPath, isDirectory:false)) - } catch { - threw = true - } - expectTrue(threw, "Should have thrown") - - } - - func testDirectoryEnumerator_error() { - let fm = FileManager.default - let nonexistentURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())/nonexistent") - - var invoked = false - let e = fm.enumerator(at: nonexistentURL, includingPropertiesForKeys: []) { (url, err) in - invoked = true - expectEqual(nonexistentURL, url) - expectEqual((err as NSError).code, NSFileReadNoSuchFileError) - return true - } - - let url = e?.nextObject() - expectTrue(invoked) - expectTrue(url == nil) - - } - - func testDirectoryEnumerator_error_noHandler() { - let fm = FileManager.default - let nonexistentURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())/nonexistent") - - let e = fm.enumerator(at: nonexistentURL, includingPropertiesForKeys: []) - let url = e?.nextObject() - expectTrue(url == nil) - - } - - func testDirectoryEnumerator_simple() { - let fm = FileManager.default - let dirPath = (NSTemporaryDirectory() as NSString).appendingPathComponent(NSUUID().uuidString) - try! fm.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) - defer { try! FileManager.default.removeItem(atPath: dirPath) } - - let item1 = URL(fileURLWithPath: "\(dirPath)/1", isDirectory: false) - let item2 = URL(fileURLWithPath: "\(dirPath)/2", isDirectory: false) - - try! Data().write(to: item1) - try! Data().write(to: item2) - - let e = fm.enumerator(at: URL(fileURLWithPath: dirPath, isDirectory: true), includingPropertiesForKeys: []) - let result1 = e?.nextObject() - let result2 = e?.nextObject() - let result3 = e?.nextObject() - - // Avoid potential symlink discrepancy between the result and the original URL - expectEqual((result1! as! URL).lastPathComponent, item1.lastPathComponent) - expectEqual((result2! as! URL).lastPathComponent, item2.lastPathComponent) - expectTrue(result3 == nil) - - } - -} - -#if !FOUNDATION_XCTEST -var FMTests = TestSuite("TestFileManager") -FMTests.test("testReplaceItem") { TestFileManager().testReplaceItem() } -FMTests.test("testReplaceItem_error") { TestFileManager().testReplaceItem_error() } -FMTests.test("testDirectoryEnumerator_error") { TestFileManager().testDirectoryEnumerator_error() } -FMTests.test("testDirectoryEnumerator_error_noHandler") { TestFileManager().testDirectoryEnumerator_error_noHandler() } -FMTests.test("testDirectoryEnumerator_simple") { TestFileManager().testDirectoryEnumerator_simple() } - -runAllTests() -#endif - diff --git a/test/stdlib/TestHomeKit.swift b/test/stdlib/TestHomeKit.swift deleted file mode 100644 index 913a157db20ab..0000000000000 --- a/test/stdlib/TestHomeKit.swift +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %target-build-swift %s - -// REQUIRES: objc_interop - -// OS X does not have HomeKit. -// UNSUPPORTED: OS=macosx -import HomeKit - -if #available(iOS 8.0, watchOS 2.0, tvOS 10.0, *) { - let s: String = HMCharacteristicPropertySupportsEventNotification -} - diff --git a/test/stdlib/TestIndexPath.swift b/test/stdlib/TestIndexPath.swift deleted file mode 100644 index c777c7c544611..0000000000000 --- a/test/stdlib/TestIndexPath.swift +++ /dev/null @@ -1,798 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestIndexPathSuper : XCTestCase { } -#else -import StdlibUnittest -class TestIndexPathSuper { } -#endif - -class TestIndexPath: TestIndexPathSuper { - func testEmpty() { - let ip = IndexPath() - expectEqual(ip.count, 0) - } - - func testSingleIndex() { - let ip = IndexPath(index: 1) - expectEqual(ip.count, 1) - expectEqual(ip[0], 1) - - let highValueIp = IndexPath(index: .max) - expectEqual(highValueIp.count, 1) - expectEqual(highValueIp[0], .max) - - let lowValueIp = IndexPath(index: .min) - expectEqual(lowValueIp.count, 1) - expectEqual(lowValueIp[0], .min) - } - - func testTwoIndexes() { - let ip = IndexPath(indexes: [0, 1]) - expectEqual(ip.count, 2) - expectEqual(ip[0], 0) - expectEqual(ip[1], 1) - } - - func testManyIndexes() { - let ip = IndexPath(indexes: [0, 1, 2, 3, 4]) - expectEqual(ip.count, 5) - expectEqual(ip[0], 0) - expectEqual(ip[1], 1) - expectEqual(ip[2], 2) - expectEqual(ip[3], 3) - expectEqual(ip[4], 4) - } - - func testCreateFromSequence() { - let seq = repeatElement(5, count: 3) - let ip = IndexPath(indexes: seq) - expectEqual(ip.count, 3) - expectEqual(ip[0], 5) - expectEqual(ip[1], 5) - expectEqual(ip[2], 5) - } - - func testCreateFromLiteral() { - let ip: IndexPath = [1, 2, 3, 4] - expectEqual(ip.count, 4) - expectEqual(ip[0], 1) - expectEqual(ip[1], 2) - expectEqual(ip[2], 3) - expectEqual(ip[3], 4) - } - - func testDropLast() { - let ip: IndexPath = [1, 2, 3, 4] - let ip2 = ip.dropLast() - expectEqual(ip2.count, 3) - expectEqual(ip2[0], 1) - expectEqual(ip2[1], 2) - expectEqual(ip2[2], 3) - } - - func testDropLastFromEmpty() { - let ip: IndexPath = [] - let ip2 = ip.dropLast() - expectEqual(ip2.count, 0) - } - - func testDropLastFromSingle() { - let ip: IndexPath = [1] - let ip2 = ip.dropLast() - expectEqual(ip2.count, 0) - } - - func testDropLastFromPair() { - let ip: IndexPath = [1, 2] - let ip2 = ip.dropLast() - expectEqual(ip2.count, 1) - expectEqual(ip2[0], 1) - } - - func testDropLastFromTriple() { - let ip: IndexPath = [1, 2, 3] - let ip2 = ip.dropLast() - expectEqual(ip2.count, 2) - expectEqual(ip2[0], 1) - expectEqual(ip2[1], 2) - } - - func testStartEndIndex() { - let ip: IndexPath = [1, 2, 3, 4] - expectEqual(ip.startIndex, 0) - expectEqual(ip.endIndex, ip.count) - } - - func testIterator() { - let ip: IndexPath = [1, 2, 3, 4] - var iter = ip.makeIterator() - var sum = 0 - while let index = iter.next() { - sum += index - } - expectEqual(sum, 1 + 2 + 3 + 4) - } - - func testIndexing() { - let ip: IndexPath = [1, 2, 3, 4] - expectEqual(ip.index(before: 1), 0) - expectEqual(ip.index(before: 0), -1) // beyond range! - expectEqual(ip.index(after: 1), 2) - expectEqual(ip.index(after: 4), 5) // beyond range! - } - - func testCompare() { - let ip1: IndexPath = [1, 2] - let ip2: IndexPath = [3, 4] - let ip3: IndexPath = [5, 1] - let ip4: IndexPath = [1, 1, 1] - let ip5: IndexPath = [1, 1, 9] - - expectEqual(ip1.compare(ip1), ComparisonResult.orderedSame) - expectEqual(ip1 < ip1, false) - expectEqual(ip1 <= ip1, true) - expectEqual(ip1 == ip1, true) - expectEqual(ip1 >= ip1, true) - expectEqual(ip1 > ip1, false) - - expectEqual(ip1.compare(ip2), ComparisonResult.orderedAscending) - expectEqual(ip1 < ip2, true) - expectEqual(ip1 <= ip2, true) - expectEqual(ip1 == ip2, false) - expectEqual(ip1 >= ip2, false) - expectEqual(ip1 > ip2, false) - - expectEqual(ip1.compare(ip3), ComparisonResult.orderedAscending) - expectEqual(ip1 < ip3, true) - expectEqual(ip1 <= ip3, true) - expectEqual(ip1 == ip3, false) - expectEqual(ip1 >= ip3, false) - expectEqual(ip1 > ip3, false) - - expectEqual(ip1.compare(ip4), ComparisonResult.orderedDescending) - expectEqual(ip1 < ip4, false) - expectEqual(ip1 <= ip4, false) - expectEqual(ip1 == ip4, false) - expectEqual(ip1 >= ip4, true) - expectEqual(ip1 > ip4, true) - - expectEqual(ip1.compare(ip5), ComparisonResult.orderedDescending) - expectEqual(ip1 < ip5, false) - expectEqual(ip1 <= ip5, false) - expectEqual(ip1 == ip5, false) - expectEqual(ip1 >= ip5, true) - expectEqual(ip1 > ip5, true) - - expectEqual(ip2.compare(ip1), ComparisonResult.orderedDescending) - expectEqual(ip2 < ip1, false) - expectEqual(ip2 <= ip1, false) - expectEqual(ip2 == ip1, false) - expectEqual(ip2 >= ip1, true) - expectEqual(ip2 > ip1, true) - - expectEqual(ip2.compare(ip2), ComparisonResult.orderedSame) - expectEqual(ip2 < ip2, false) - expectEqual(ip2 <= ip2, true) - expectEqual(ip2 == ip2, true) - expectEqual(ip2 >= ip2, true) - expectEqual(ip2 > ip2, false) - - expectEqual(ip2.compare(ip3), ComparisonResult.orderedAscending) - expectEqual(ip2 < ip3, true) - expectEqual(ip2 <= ip3, true) - expectEqual(ip2 == ip3, false) - expectEqual(ip2 >= ip3, false) - expectEqual(ip2 > ip3, false) - - expectEqual(ip2.compare(ip4), ComparisonResult.orderedDescending) - expectEqual(ip2.compare(ip5), ComparisonResult.orderedDescending) - expectEqual(ip3.compare(ip1), ComparisonResult.orderedDescending) - expectEqual(ip3.compare(ip2), ComparisonResult.orderedDescending) - expectEqual(ip3.compare(ip3), ComparisonResult.orderedSame) - expectEqual(ip3.compare(ip4), ComparisonResult.orderedDescending) - expectEqual(ip3.compare(ip5), ComparisonResult.orderedDescending) - expectEqual(ip4.compare(ip1), ComparisonResult.orderedAscending) - expectEqual(ip4.compare(ip2), ComparisonResult.orderedAscending) - expectEqual(ip4.compare(ip3), ComparisonResult.orderedAscending) - expectEqual(ip4.compare(ip4), ComparisonResult.orderedSame) - expectEqual(ip4.compare(ip5), ComparisonResult.orderedAscending) - expectEqual(ip5.compare(ip1), ComparisonResult.orderedAscending) - expectEqual(ip5.compare(ip2), ComparisonResult.orderedAscending) - expectEqual(ip5.compare(ip3), ComparisonResult.orderedAscending) - expectEqual(ip5.compare(ip4), ComparisonResult.orderedDescending) - expectEqual(ip5.compare(ip5), ComparisonResult.orderedSame) - - let ip6: IndexPath = [1, 1] - expectEqual(ip6.compare(ip5), ComparisonResult.orderedAscending) - expectEqual(ip5.compare(ip6), ComparisonResult.orderedDescending) - } - - func testHashing() { - guard #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) else { return } - let samples: [IndexPath] = [ - [], - [1], - [2], - [Int.max], - [1, 1], - [2, 1], - [1, 2], - [1, 1, 1], - [2, 1, 1], - [1, 2, 1], - [1, 1, 2], - [Int.max, Int.max, Int.max], - ] - checkHashable(samples, equalityOracle: { $0 == $1 }) - - // this should not cause an overflow crash - _ = IndexPath(indexes: [Int.max >> 8, 2, Int.max >> 36]).hashValue - } - - func testEquality() { - let ip1: IndexPath = [1, 1] - let ip2: IndexPath = [1, 1] - let ip3: IndexPath = [1, 1, 1] - let ip4: IndexPath = [] - let ip5: IndexPath = [1] - - expectTrue(ip1 == ip2) - expectFalse(ip1 == ip3) - expectFalse(ip1 == ip4) - expectFalse(ip4 == ip1) - expectFalse(ip5 == ip1) - expectFalse(ip5 == ip4) - expectTrue(ip4 == ip4) - expectTrue(ip5 == ip5) - } - - func testSubscripting() { - var ip1: IndexPath = [1] - var ip2: IndexPath = [1, 2] - var ip3: IndexPath = [1, 2, 3] - - expectEqual(ip1[0], 1) - - expectEqual(ip2[0], 1) - expectEqual(ip2[1], 2) - - expectEqual(ip3[0], 1) - expectEqual(ip3[1], 2) - expectEqual(ip3[2], 3) - - ip1[0] = 2 - expectEqual(ip1[0], 2) - - ip2[0] = 2 - ip2[1] = 3 - expectEqual(ip2[0], 2) - expectEqual(ip2[1], 3) - - ip3[0] = 2 - ip3[1] = 3 - ip3[2] = 4 - expectEqual(ip3[0], 2) - expectEqual(ip3[1], 3) - expectEqual(ip3[2], 4) - - let ip4 = ip3[0..<2] - expectEqual(ip4.count, 2) - expectEqual(ip4[0], 2) - expectEqual(ip4[1], 3) - } - - func testAppending() { - var ip : IndexPath = [1, 2, 3, 4] - let ip2 = IndexPath(indexes: [5, 6, 7]) - - ip.append(ip2) - - expectEqual(ip.count, 7) - expectEqual(ip[0], 1) - expectEqual(ip[6], 7) - - let ip3 = ip.appending(IndexPath(indexes: [8, 9])) - expectEqual(ip3.count, 9) - expectEqual(ip3[7], 8) - expectEqual(ip3[8], 9) - - let ip4 = ip3.appending([10, 11]) - expectEqual(ip4.count, 11) - expectEqual(ip4[9], 10) - expectEqual(ip4[10], 11) - - let ip5 = ip.appending(8) - expectEqual(ip5.count, 8) - expectEqual(ip5[7], 8) - } - - func testAppendEmpty() { - var ip: IndexPath = [] - ip.append(1) - - expectEqual(ip.count, 1) - expectEqual(ip[0], 1) - - ip.append(2) - expectEqual(ip.count, 2) - expectEqual(ip[0], 1) - expectEqual(ip[1], 2) - - ip.append(3) - expectEqual(ip.count, 3) - expectEqual(ip[0], 1) - expectEqual(ip[1], 2) - expectEqual(ip[2], 3) - - ip.append(4) - expectEqual(ip.count, 4) - expectEqual(ip[0], 1) - expectEqual(ip[1], 2) - expectEqual(ip[2], 3) - expectEqual(ip[3], 4) - } - - func testAppendEmptyIndexPath() { - var ip: IndexPath = [] - ip.append(IndexPath(indexes: [])) - - expectEqual(ip.count, 0) - } - - func testAppendManyIndexPath() { - var ip: IndexPath = [] - ip.append(IndexPath(indexes: [1, 2, 3])) - - expectEqual(ip.count, 3) - expectEqual(ip[0], 1) - expectEqual(ip[1], 2) - expectEqual(ip[2], 3) - } - - func testAppendEmptyIndexPathToSingle() { - var ip: IndexPath = [1] - ip.append(IndexPath(indexes: [])) - - expectEqual(ip.count, 1) - expectEqual(ip[0], 1) - } - - func testAppendSingleIndexPath() { - var ip: IndexPath = [] - ip.append(IndexPath(indexes: [1])) - - expectEqual(ip.count, 1) - expectEqual(ip[0], 1) - } - - func testAppendSingleIndexPathToSingle() { - var ip: IndexPath = [1] - ip.append(IndexPath(indexes: [1])) - - expectEqual(ip.count, 2) - expectEqual(ip[0], 1) - expectEqual(ip[1], 1) - } - - func testAppendPairIndexPath() { - var ip: IndexPath = [] - ip.append(IndexPath(indexes: [1, 2])) - - expectEqual(ip.count, 2) - expectEqual(ip[0], 1) - expectEqual(ip[1], 2) - } - - func testAppendManyIndexPathToEmpty() { - var ip: IndexPath = [] - ip.append(IndexPath(indexes: [1, 2, 3])) - - expectEqual(ip.count, 3) - expectEqual(ip[0], 1) - expectEqual(ip[1], 2) - expectEqual(ip[2], 3) - } - - func testAppendByOperator() { - let ip1: IndexPath = [] - let ip2: IndexPath = [] - - let ip3 = ip1 + ip2 - expectEqual(ip3.count, 0) - - let ip4: IndexPath = [1] - let ip5: IndexPath = [2] - - let ip6 = ip4 + ip5 - expectEqual(ip6.count, 2) - expectEqual(ip6[0], 1) - expectEqual(ip6[1], 2) - - var ip7: IndexPath = [] - ip7 += ip6 - expectEqual(ip7.count, 2) - expectEqual(ip7[0], 1) - expectEqual(ip7[1], 2) - } - - func testAppendArray() { - var ip: IndexPath = [1, 2, 3, 4] - let indexes = [5, 6, 7] - - ip.append(indexes) - - expectEqual(ip.count, 7) - expectEqual(ip[0], 1) - expectEqual(ip[6], 7) - } - - func testRanges() { - let ip1 = IndexPath(indexes: [1, 2, 3]) - let ip2 = IndexPath(indexes: [6, 7, 8]) - - // Replace the whole range - var mutateMe = ip1 - mutateMe[0..<3] = ip2 - expectEqual(mutateMe, ip2) - - // Insert at the beginning - mutateMe = ip1 - mutateMe[0..<0] = ip2 - expectEqual(mutateMe, IndexPath(indexes: [6, 7, 8, 1, 2, 3])) - - // Insert at the end - mutateMe = ip1 - mutateMe[3..<3] = ip2 - expectEqual(mutateMe, IndexPath(indexes: [1, 2, 3, 6, 7, 8])) - - // Insert in middle - mutateMe = ip1 - mutateMe[2..<2] = ip2 - expectEqual(mutateMe, IndexPath(indexes: [1, 2, 6, 7, 8, 3])) - } - - func testRangeFromEmpty() { - let ip1 = IndexPath() - let ip2 = ip1[0..<0] - expectEqual(ip2.count, 0) - } - - func testRangeFromSingle() { - let ip1 = IndexPath(indexes: [1]) - let ip2 = ip1[0..<0] - expectEqual(ip2.count, 0) - let ip3 = ip1[0..<1] - expectEqual(ip3.count, 1) - expectEqual(ip3[0], 1) - } - - func testRangeFromPair() { - let ip1 = IndexPath(indexes: [1, 2]) - let ip2 = ip1[0..<0] - expectEqual(ip2.count, 0) - let ip3 = ip1[0..<1] - expectEqual(ip3.count, 1) - expectEqual(ip3[0], 1) - let ip4 = ip1[1..<1] - expectEqual(ip4.count, 0) - let ip5 = ip1[0..<2] - expectEqual(ip5.count, 2) - expectEqual(ip5[0], 1) - expectEqual(ip5[1], 2) - let ip6 = ip1[1..<2] - expectEqual(ip6.count, 1) - expectEqual(ip6[0], 2) - let ip7 = ip1[2..<2] - expectEqual(ip7.count, 0) - } - - func testRangeFromMany() { - let ip1 = IndexPath(indexes: [1, 2, 3]) - let ip2 = ip1[0..<0] - expectEqual(ip2.count, 0) - let ip3 = ip1[0..<1] - expectEqual(ip3.count, 1) - let ip4 = ip1[0..<2] - expectEqual(ip4.count, 2) - let ip5 = ip1[0..<3] - expectEqual(ip5.count, 3) - } - - func testRangeReplacementSingle() { - var ip1 = IndexPath(indexes: [1]) - ip1[0..<1] = IndexPath(indexes: [2]) - expectEqual(ip1[0], 2) - - ip1[0..<1] = IndexPath(indexes: []) - expectEqual(ip1.count, 0) - } - - func testRangeReplacementPair() { - var ip1 = IndexPath(indexes: [1, 2]) - ip1[0..<1] = IndexPath(indexes: [2, 3]) - expectEqual(ip1.count, 3) - expectEqual(ip1[0], 2) - expectEqual(ip1[1], 3) - expectEqual(ip1[2], 2) - - ip1[0..<1] = IndexPath(indexes: []) - expectEqual(ip1.count, 2) - } - - func testMoreRanges() { - var ip = IndexPath(indexes: [1, 2, 3]) - let ip2 = IndexPath(indexes: [5, 6, 7, 8, 9, 10]) - - ip[1..<2] = ip2 - expectEqual(ip, IndexPath(indexes: [1, 5, 6, 7, 8, 9, 10, 3])) - } - - func testIteration() { - let ip = IndexPath(indexes: [1, 2, 3]) - - var count = 0 - for _ in ip { - count += 1 - } - - expectEqual(3, count) - } - - func testDescription() { - let ip1: IndexPath = [] - let ip2: IndexPath = [1] - let ip3: IndexPath = [1, 2] - let ip4: IndexPath = [1, 2, 3] - - expectEqual(ip1.description, "[]") - expectEqual(ip2.description, "[1]") - expectEqual(ip3.description, "[1, 2]") - expectEqual(ip4.description, "[1, 2, 3]") - - expectEqual(ip1.debugDescription, ip1.description) - expectEqual(ip2.debugDescription, ip2.description) - expectEqual(ip3.debugDescription, ip3.description) - expectEqual(ip4.debugDescription, ip4.description) - } - - func testBridgeToObjC() { - let ip1: IndexPath = [] - let ip2: IndexPath = [1] - let ip3: IndexPath = [1, 2] - let ip4: IndexPath = [1, 2, 3] - - let nsip1 = ip1._bridgeToObjectiveC() - let nsip2 = ip2._bridgeToObjectiveC() - let nsip3 = ip3._bridgeToObjectiveC() - let nsip4 = ip4._bridgeToObjectiveC() - - expectEqual(nsip1.length, 0) - expectEqual(nsip2.length, 1) - expectEqual(nsip3.length, 2) - expectEqual(nsip4.length, 3) - } - - func testForceBridgeFromObjC() { - let nsip1 = NSIndexPath() - let nsip2 = NSIndexPath(index: 1) - let nsip3 = [1, 2].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in - return NSIndexPath(indexes: buffer.baseAddress, length: buffer.count) - } - let nsip4 = [1, 2, 3].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in - return NSIndexPath(indexes: buffer.baseAddress, length: buffer.count) - } - - var ip1: IndexPath? = IndexPath() - IndexPath._forceBridgeFromObjectiveC(nsip1, result: &ip1) - expectNotNil(ip1) - expectEqual(ip1!.count, 0) - - var ip2: IndexPath? = IndexPath() - IndexPath._forceBridgeFromObjectiveC(nsip2, result: &ip2) - expectNotNil(ip2) - expectEqual(ip2!.count, 1) - expectEqual(ip2![0], 1) - - var ip3: IndexPath? = IndexPath() - IndexPath._forceBridgeFromObjectiveC(nsip3, result: &ip3) - expectNotNil(ip3) - expectEqual(ip3!.count, 2) - expectEqual(ip3![0], 1) - expectEqual(ip3![1], 2) - - var ip4: IndexPath? = IndexPath() - IndexPath._forceBridgeFromObjectiveC(nsip4, result: &ip4) - expectNotNil(ip4) - expectEqual(ip4!.count, 3) - expectEqual(ip4![0], 1) - expectEqual(ip4![1], 2) - expectEqual(ip4![2], 3) - } - - func testConditionalBridgeFromObjC() { - let nsip1 = NSIndexPath() - let nsip2 = NSIndexPath(index: 1) - let nsip3 = [1, 2].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in - return NSIndexPath(indexes: buffer.baseAddress, length: buffer.count) - } - let nsip4 = [1, 2, 3].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in - return NSIndexPath(indexes: buffer.baseAddress, length: buffer.count) - } - - var ip1: IndexPath? = IndexPath() - expectTrue(IndexPath._conditionallyBridgeFromObjectiveC(nsip1, result: &ip1)) - expectNotNil(ip1) - expectEqual(ip1!.count, 0) - - var ip2: IndexPath? = IndexPath() - expectTrue(IndexPath._conditionallyBridgeFromObjectiveC(nsip2, result: &ip2)) - expectNotNil(ip2) - expectEqual(ip2!.count, 1) - expectEqual(ip2![0], 1) - - var ip3: IndexPath? = IndexPath() - expectTrue(IndexPath._conditionallyBridgeFromObjectiveC(nsip3, result: &ip3)) - expectNotNil(ip3) - expectEqual(ip3!.count, 2) - expectEqual(ip3![0], 1) - expectEqual(ip3![1], 2) - - var ip4: IndexPath? = IndexPath() - expectTrue(IndexPath._conditionallyBridgeFromObjectiveC(nsip4, result: &ip4)) - expectNotNil(ip4) - expectEqual(ip4!.count, 3) - expectEqual(ip4![0], 1) - expectEqual(ip4![1], 2) - expectEqual(ip4![2], 3) - } - - func testUnconditionalBridgeFromObjC() { - let nsip1 = NSIndexPath() - let nsip2 = NSIndexPath(index: 1) - let nsip3 = [1, 2].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in - return NSIndexPath(indexes: buffer.baseAddress, length: buffer.count) - } - let nsip4 = [1, 2, 3].withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> NSIndexPath in - return NSIndexPath(indexes: buffer.baseAddress, length: buffer.count) - } - - let ip1: IndexPath = IndexPath._unconditionallyBridgeFromObjectiveC(nsip1) - expectEqual(ip1.count, 0) - - var ip2: IndexPath = IndexPath._unconditionallyBridgeFromObjectiveC(nsip2) - expectEqual(ip2.count, 1) - expectEqual(ip2[0], 1) - - var ip3: IndexPath = IndexPath._unconditionallyBridgeFromObjectiveC(nsip3) - expectEqual(ip3.count, 2) - expectEqual(ip3[0], 1) - expectEqual(ip3[1], 2) - - var ip4: IndexPath = IndexPath._unconditionallyBridgeFromObjectiveC(nsip4) - expectEqual(ip4.count, 3) - expectEqual(ip4[0], 1) - expectEqual(ip4[1], 2) - expectEqual(ip4[2], 3) - } - - func testObjcBridgeType() { - expectTrue(IndexPath._getObjectiveCType() == NSIndexPath.self) - } - - func test_AnyHashableContainingIndexPath() { - let values: [IndexPath] = [ - IndexPath(indexes: [1, 2]), - IndexPath(indexes: [1, 2, 3]), - IndexPath(indexes: [1, 2, 3]), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(IndexPath.self, type(of: anyHashables[0].base)) - expectEqual(IndexPath.self, type(of: anyHashables[1].base)) - expectEqual(IndexPath.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSIndexPath() { - let values: [NSIndexPath] = [ - NSIndexPath(index: 1), - NSIndexPath(index: 2), - NSIndexPath(index: 2), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(IndexPath.self, type(of: anyHashables[0].base)) - expectEqual(IndexPath.self, type(of: anyHashables[1].base)) - expectEqual(IndexPath.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_unconditionallyBridgeFromObjectiveC() { - expectEqual(IndexPath(), IndexPath._unconditionallyBridgeFromObjectiveC(nil)) - } - - func test_slice_1ary() { - let indexPath: IndexPath = [0] - let res = indexPath.dropFirst() - expectEqual(0, res.count) - - let slice = indexPath[1..<1] - expectEqual(0, slice.count) - } - - func test_dropFirst() { - var pth = IndexPath(indexes:[1,2,3,4]) - while !pth.isEmpty { - // this should not crash - pth = pth.dropFirst() - } - } -} - -#if !FOUNDATION_XCTEST -var IndexPathTests = TestSuite("TestIndexPath") -IndexPathTests.test("testEmpty") { TestIndexPath().testEmpty() } -IndexPathTests.test("testSingleIndex") { TestIndexPath().testSingleIndex() } -IndexPathTests.test("testTwoIndexes") { TestIndexPath().testTwoIndexes() } -IndexPathTests.test("testManyIndexes") { TestIndexPath().testManyIndexes() } -IndexPathTests.test("testCreateFromSequence") { TestIndexPath().testCreateFromSequence() } -IndexPathTests.test("testCreateFromLiteral") { TestIndexPath().testCreateFromLiteral() } -IndexPathTests.test("testDropLast") { TestIndexPath().testDropLast() } -IndexPathTests.test("testDropLastFromEmpty") { TestIndexPath().testDropLastFromEmpty() } -IndexPathTests.test("testDropLastFromSingle") { TestIndexPath().testDropLastFromSingle() } -IndexPathTests.test("testDropLastFromPair") { TestIndexPath().testDropLastFromPair() } -IndexPathTests.test("testDropLastFromTriple") { TestIndexPath().testDropLastFromTriple() } -IndexPathTests.test("testStartEndIndex") { TestIndexPath().testStartEndIndex() } -IndexPathTests.test("testIterator") { TestIndexPath().testIterator() } -IndexPathTests.test("testIndexing") { TestIndexPath().testIndexing() } -IndexPathTests.test("testCompare") { TestIndexPath().testCompare() } -IndexPathTests.test("testHashing") { TestIndexPath().testHashing() } -IndexPathTests.test("testEquality") { TestIndexPath().testEquality() } -IndexPathTests.test("testSubscripting") { TestIndexPath().testSubscripting() } -IndexPathTests.test("testAppending") { TestIndexPath().testAppending() } -IndexPathTests.test("testAppendEmpty") { TestIndexPath().testAppendEmpty() } -IndexPathTests.test("testAppendEmptyIndexPath") { TestIndexPath().testAppendEmptyIndexPath() } -IndexPathTests.test("testAppendManyIndexPath") { TestIndexPath().testAppendManyIndexPath() } -IndexPathTests.test("testAppendEmptyIndexPathToSingle") { TestIndexPath().testAppendEmptyIndexPathToSingle() } -IndexPathTests.test("testAppendSingleIndexPath") { TestIndexPath().testAppendSingleIndexPath() } -IndexPathTests.test("testAppendSingleIndexPathToSingle") { TestIndexPath().testAppendSingleIndexPathToSingle() } -IndexPathTests.test("testAppendPairIndexPath") { TestIndexPath().testAppendPairIndexPath() } -IndexPathTests.test("testAppendManyIndexPathToEmpty") { TestIndexPath().testAppendManyIndexPathToEmpty() } -IndexPathTests.test("testAppendByOperator") { TestIndexPath().testAppendByOperator() } -IndexPathTests.test("testAppendArray") { TestIndexPath().testAppendArray() } -IndexPathTests.test("testRanges") { TestIndexPath().testRanges() } -IndexPathTests.test("testRangeFromEmpty") { TestIndexPath().testRangeFromEmpty() } -IndexPathTests.test("testRangeFromSingle") { TestIndexPath().testRangeFromSingle() } -IndexPathTests.test("testRangeFromPair") { TestIndexPath().testRangeFromPair() } -IndexPathTests.test("testRangeFromMany") { TestIndexPath().testRangeFromMany() } -IndexPathTests.test("testRangeReplacementSingle") { TestIndexPath().testRangeReplacementSingle() } -IndexPathTests.test("testRangeReplacementPair") { TestIndexPath().testRangeReplacementPair() } -IndexPathTests.test("testMoreRanges") { TestIndexPath().testMoreRanges() } -IndexPathTests.test("testIteration") { TestIndexPath().testIteration() } -IndexPathTests.test("testDescription") { TestIndexPath().testDescription() } -IndexPathTests.test("testBridgeToObjC") { TestIndexPath().testBridgeToObjC() } -IndexPathTests.test("testForceBridgeFromObjC") { TestIndexPath().testForceBridgeFromObjC() } -IndexPathTests.test("testConditionalBridgeFromObjC") { TestIndexPath().testConditionalBridgeFromObjC() } -IndexPathTests.test("testUnconditionalBridgeFromObjC") { TestIndexPath().testUnconditionalBridgeFromObjC() } -IndexPathTests.test("testObjcBridgeType") { TestIndexPath().testObjcBridgeType() } -IndexPathTests.test("test_AnyHashableContainingIndexPath") { TestIndexPath().test_AnyHashableContainingIndexPath() } -IndexPathTests.test("test_AnyHashableCreatedFromNSIndexPath") { TestIndexPath().test_AnyHashableCreatedFromNSIndexPath() } -IndexPathTests.test("test_unconditionallyBridgeFromObjectiveC") { TestIndexPath().test_unconditionallyBridgeFromObjectiveC() } -IndexPathTests.test("test_slice_1ary") { TestIndexPath().test_slice_1ary() } -IndexPathTests.test("test_dropFirst") { TestIndexPath().test_dropFirst() } -runAllTests() -#endif diff --git a/test/stdlib/TestIndexSet.swift b/test/stdlib/TestIndexSet.swift deleted file mode 100644 index c1529790e6f84..0000000000000 --- a/test/stdlib/TestIndexSet.swift +++ /dev/null @@ -1,875 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestIndexSetSuper : XCTestCase { } -#else -import StdlibUnittest -class TestIndexSetSuper { } -#endif - -class TestIndexSet : TestIndexSetSuper { - - func testEnumeration() { - let someIndexes = IndexSet(integersIn: 3...4) - let first = someIndexes.startIndex - let last = someIndexes.endIndex - - expectNotEqual(first, last) - - var count = 0 - var firstValue = 0 - var secondValue = 0 - for v in someIndexes { - if count == 0 { firstValue = v } - if count == 1 { secondValue = v } - count += 1 - } - - expectEqual(2, count) - expectEqual(3, firstValue) - expectEqual(4, secondValue) - } - - func testSubsequence() { - var someIndexes = IndexSet(integersIn: 1..<3) - someIndexes.insert(integersIn: 10..<20) - - let intersectingRange = someIndexes.indexRange(in: 5..<21) - expectFalse(intersectingRange.isEmpty) - - let sub = someIndexes[intersectingRange] - var count = 0 - for i in sub { - if count == 0 { - expectEqual(10, i) - } - if count == 9 { - expectEqual(19, i) - } - count += 1 - } - expectEqual(count, 10) - } - - func testIndexRange() { - var someIndexes = IndexSet(integersIn: 1..<3) - someIndexes.insert(integersIn: 10..<20) - - var r : Range - - r = someIndexes.indexRange(in: 1..<3) - expectEqual(1, someIndexes[r.lowerBound]) - expectEqual(10, someIndexes[r.upperBound]) - - r = someIndexes.indexRange(in: 0..<0) - expectEqual(r.lowerBound, r.upperBound) - - r = someIndexes.indexRange(in: 100..<201) - expectEqual(r.lowerBound, r.upperBound) - expectTrue(r.isEmpty) - - r = someIndexes.indexRange(in: 0..<100) - expectEqual(r.lowerBound, someIndexes.startIndex) - expectEqual(r.upperBound, someIndexes.endIndex) - - r = someIndexes.indexRange(in: 1..<11) - expectEqual(1, someIndexes[r.lowerBound]) - expectEqual(11, someIndexes[r.upperBound]) - - let empty = IndexSet() - expectTrue(empty.indexRange(in: 1..<3).isEmpty) - } - - func testMutation() { - var someIndexes = IndexSet(integersIn: 1..<3) - someIndexes.insert(3) - someIndexes.insert(4) - someIndexes.insert(5) - - someIndexes.insert(10) - someIndexes.insert(11) - - expectEqual(someIndexes.count, 7) - - someIndexes.remove(11) - - expectEqual(someIndexes.count, 6) - - someIndexes.insert(integersIn: 100...101) - expectEqual(8, someIndexes.count) - expectEqual(2, someIndexes.count(in: 100...101)) - - someIndexes.remove(integersIn: 100...101) - expectEqual(6, someIndexes.count) - expectEqual(0, someIndexes.count(in: 100...101)) - - someIndexes.insert(integersIn: 200..<202) - expectEqual(8, someIndexes.count) - expectEqual(2, someIndexes.count(in: 200..<202)) - - someIndexes.remove(integersIn: 200..<202) - expectEqual(6, someIndexes.count) - expectEqual(0, someIndexes.count(in: 200..<202)) - } - - func testContainsAndIntersects() { - let someIndexes = IndexSet(integersIn: 1..<10) - - expectTrue(someIndexes.contains(integersIn: 1..<10)) - expectTrue(someIndexes.contains(integersIn: 1...9)) - expectTrue(someIndexes.contains(integersIn: 2..<10)) - expectTrue(someIndexes.contains(integersIn: 2...9)) - expectTrue(someIndexes.contains(integersIn: 1..<9)) - expectTrue(someIndexes.contains(integersIn: 1...8)) - - expectFalse(someIndexes.contains(integersIn: 0..<10)) - expectFalse(someIndexes.contains(integersIn: 0...9)) - expectFalse(someIndexes.contains(integersIn: 2..<11)) - expectFalse(someIndexes.contains(integersIn: 2...10)) - expectFalse(someIndexes.contains(integersIn: 0..<9)) - expectFalse(someIndexes.contains(integersIn: 0...8)) - - expectTrue(someIndexes.intersects(integersIn: 1..<10)) - expectTrue(someIndexes.intersects(integersIn: 1...9)) - expectTrue(someIndexes.intersects(integersIn: 2..<10)) - expectTrue(someIndexes.intersects(integersIn: 2...9)) - expectTrue(someIndexes.intersects(integersIn: 1..<9)) - expectTrue(someIndexes.intersects(integersIn: 1...8)) - - expectTrue(someIndexes.intersects(integersIn: 0..<10)) - expectTrue(someIndexes.intersects(integersIn: 0...9)) - expectTrue(someIndexes.intersects(integersIn: 2..<11)) - expectTrue(someIndexes.intersects(integersIn: 2...10)) - expectTrue(someIndexes.intersects(integersIn: 0..<9)) - expectTrue(someIndexes.intersects(integersIn: 0...8)) - - expectFalse(someIndexes.intersects(integersIn: 0..<0)) - expectFalse(someIndexes.intersects(integersIn: 10...12)) - expectFalse(someIndexes.intersects(integersIn: 10..<12)) - } - - func testIteration() { - var someIndexes = IndexSet(integersIn: 1..<5) - someIndexes.insert(integersIn: 8..<11) - someIndexes.insert(15) - - let start = someIndexes.startIndex - let end = someIndexes.endIndex - - // Count forwards - var i = start - var count = 0 - while i != end { - count += 1 - i = someIndexes.index(after: i) - } - expectEqual(8, count) - - // Count backwards - i = end - count = 0 - while i != start { - i = someIndexes.index(before: i) - count += 1 - } - expectEqual(8, count) - - // Count using a for loop - count = 0 - for _ in someIndexes { - count += 1 - } - expectEqual(8, count) - - // Go the other way - count = 0 - for _ in someIndexes.reversed() { - count += 1 - } - expectEqual(8, count) - } - - func testRangeIteration() { - var someIndexes = IndexSet(integersIn: 1..<5) - someIndexes.insert(integersIn: 8..<11) - someIndexes.insert(15) - - var count = 0 - for r in someIndexes.rangeView { - // print("\(r)") - count += 1 - if count == 3 { - expectEqual(r, 15..<16) - } - } - expectEqual(3, count) - - // Backwards - count = 0 - for r in someIndexes.rangeView.reversed() { - // print("\(r)") - count += 1 - if count == 3 { - expectEqual(r, 1..<5) - } - } - expectEqual(3, count) - } - - func testSubrangeIteration() { - func expectRanges(_ ranges: [Range], in view: IndexSet.RangeView) { - expectEqual(ranges.count, view.count) - - for i in 0 ..< min(ranges.count, view.count) { - expectEqual(Range(ranges[i]), Range(view[i])) - } - } - - // Inclusive ranges for test: - // 2-4, 8-10, 15-19, 30-39, 60-79 - var indexes = IndexSet() - indexes.insert(integersIn: 2..<5) - indexes.insert(integersIn: 8...10) - indexes.insert(integersIn: Range(15..<20)) - indexes.insert(integersIn: Range(30...39)) - indexes.insert(integersIn: 60..<80) - - // Empty ranges should yield no results: - expectRanges([], in: indexes.rangeView(of: 0..<0)) - - // Ranges below contained indexes should yield no results: - expectRanges([], in: indexes.rangeView(of: 0...1)) - - // Ranges starting below first index but overlapping should yield a result: - expectRanges([2..<3], in: indexes.rangeView(of: 0...2)) - - // Ranges starting below first index but enveloping a range should yield a result: - expectRanges([2..<5], in: indexes.rangeView(of: 0...6)) - - // Ranges within subranges should yield a result: - expectRanges([2..<5], in: indexes.rangeView(of: 2...4)) - expectRanges([3..<5], in: indexes.rangeView(of: 3...4)) - expectRanges([3..<4], in: indexes.rangeView(of: 3..<4)) - - // Ranges starting within subranges and going over the end should yield a result: - expectRanges([3..<5], in: indexes.rangeView(of: 3...6)) - - // Ranges not matching any indexes should yield no results: - expectRanges([], in: indexes.rangeView(of: 5...6)) - expectRanges([], in: indexes.rangeView(of: 5..<8)) - - // Same as above -- overlapping with a range of indexes should slice it appropriately: - expectRanges([8..<9], in: indexes.rangeView(of: 6...8)) - expectRanges([8..<11], in: indexes.rangeView(of: 8...10)) - expectRanges([8..<11], in: indexes.rangeView(of: 8...13)) - - expectRanges([2..<5, 8..<10], in: indexes.rangeView(of: 0...9)) - expectRanges([2..<5, 8..<11], in: indexes.rangeView(of: 0...12)) - expectRanges([3..<5, 8..<11], in: indexes.rangeView(of: 3...14)) - - expectRanges([3..<5, 8..<11, 15..<18], in: indexes.rangeView(of: 3...17)) - expectRanges([3..<5, 8..<11, 15..<20], in: indexes.rangeView(of: 3...20)) - expectRanges([3..<5, 8..<11, 15..<20], in: indexes.rangeView(of: 3...21)) - - // Ranges inclusive of the end index should yield all of the contained ranges: - expectRanges([2..<5, 8..<11, 15..<20, 30..<40, 60..<80], in: indexes.rangeView(of: 0...80)) - expectRanges([2..<5, 8..<11, 15..<20, 30..<40, 60..<80], in: indexes.rangeView(of: 2..<80)) - expectRanges([2..<5, 8..<11, 15..<20, 30..<40, 60..<80], in: indexes.rangeView(of: 2...80)) - - // Ranges above the end index should yield no results: - expectRanges([], in: indexes.rangeView(of: 90..<90)) - expectRanges([], in: indexes.rangeView(of: 90...90)) - expectRanges([], in: indexes.rangeView(of: 90...100)) - } - - func testSlicing() { - var someIndexes = IndexSet(integersIn: 2..<5) - someIndexes.insert(integersIn: 8..<11) - someIndexes.insert(integersIn: 15..<20) - someIndexes.insert(integersIn: 30..<40) - someIndexes.insert(integersIn: 60..<80) - - var r : Range - - r = someIndexes.indexRange(in: 5..<25) - expectEqual(8, someIndexes[r.lowerBound]) - expectEqual(19, someIndexes[someIndexes.index(before: r.upperBound)]) - var count = 0 - for _ in someIndexes[r] { - count += 1 - } - - expectEqual(8, someIndexes.count(in: 5..<25)) - expectEqual(8, count) - - r = someIndexes.indexRange(in: 100...199) - expectTrue(r.isEmpty) - - let emptySlice = someIndexes[r] - expectEqual(0, emptySlice.count) - - let boundarySlice = someIndexes[someIndexes.indexRange(in: 2..<3)] - expectEqual(1, boundarySlice.count) - - let boundarySlice2 = someIndexes[someIndexes.indexRange(in: 79..<80)] - expectEqual(1, boundarySlice2.count) - - let largeSlice = someIndexes[someIndexes.indexRange(in: 0..<100000)] - expectEqual(someIndexes.count, largeSlice.count) - } - - func testEmptyIteration() { - var empty = IndexSet() - let start = empty.startIndex - let end = empty.endIndex - - expectEqual(start, end) - - var count = 0 - for _ in empty { - count += 1 - } - - expectEqual(count, 0) - - count = 0 - for _ in empty.rangeView { - count += 1 - } - - expectEqual(count, 0) - - empty.insert(5) - empty.remove(5) - - count = 0 - for _ in empty { - count += 1 - } - expectEqual(count, 0) - - count = 0 - for _ in empty.rangeView { - count += 1 - } - expectEqual(count, 0) - } - - func testSubsequences() { - var someIndexes = IndexSet(integersIn: 1..<5) - someIndexes.insert(integersIn: 8..<11) - someIndexes.insert(15) - - // Get a subsequence of this IndexSet - let range = someIndexes.indexRange(in: 4..<15) - let subSet = someIndexes[range] - - expectEqual(subSet.count, 4) - - // Iterate a subset - var count = 0 - for _ in subSet { - count += 1 - } - expectEqual(count, 4) - - // And in reverse - count = 0 - for _ in subSet.reversed() { - count += 1 - } - expectEqual(count, 4) - } - - func testFiltering() { - var someIndexes = IndexSet(integersIn: 1..<5) - someIndexes.insert(integersIn: 8..<11) - someIndexes.insert(15) - - // An array - let resultArray = someIndexes.filter { $0 % 2 == 0 } - expectEqual(resultArray.count, 4) - - let resultSet = someIndexes.filteredIndexSet { $0 % 2 == 0 } - expectEqual(resultSet.count, 4) - - let resultOutsideRange = someIndexes.filteredIndexSet(in: 20..<30, includeInteger: { _ in return true } ) - expectEqual(resultOutsideRange.count, 0) - - let resultInRange = someIndexes.filteredIndexSet(in: 0..<16, includeInteger: { _ in return true } ) - expectEqual(resultInRange.count, someIndexes.count) - } - - func testFilteringRanges() { - var someIndexes = IndexSet(integersIn: 1..<5) - someIndexes.insert(integersIn: 8..<11) - someIndexes.insert(15) - - let resultArray = someIndexes.rangeView.filter { $0.count > 1 } - expectEqual(resultArray.count, 2) - } - - func testShift() { - var someIndexes = IndexSet(integersIn: 1..<5) - someIndexes.insert(integersIn: 8..<11) - someIndexes.insert(15) - - let lastValue = someIndexes.last! - - someIndexes.shift(startingAt: 13, by: 1) - - // Count should not have changed - expectEqual(someIndexes.count, 8) - - // But the last value should have - expectEqual(lastValue + 1, someIndexes.last!) - - // Shift starting at something not in the set - someIndexes.shift(startingAt: 0, by: 1) - - // Count should not have changed, again - expectEqual(someIndexes.count, 8) - - // But the last value should have, again - expectEqual(lastValue + 2, someIndexes.last!) - } - - func testSymmetricDifference() { - var is1 : IndexSet - var is2 : IndexSet - var expected : IndexSet - - do { - is1 = IndexSet() - is1.insert(integersIn: 1..<3) - is1.insert(integersIn: 4..<11) - is1.insert(integersIn: 15..<21) - is1.insert(integersIn: 40..<51) - - is2 = IndexSet() - is2.insert(integersIn: 5..<18) - is2.insert(integersIn: 45..<61) - - expected = IndexSet() - expected.insert(integersIn: 1..<3) - expected.insert(4) - expected.insert(integersIn: 11..<15) - expected.insert(integersIn: 18..<21) - expected.insert(integersIn: 40..<45) - expected.insert(integersIn: 51..<61) - - expectEqual(expected, is1.symmetricDifference(is2)) - expectEqual(expected, is2.symmetricDifference(is1)) - } - - do { - is1 = IndexSet() - is1.insert(integersIn: 5..<18) - is1.insert(integersIn: 45..<61) - - is2 = IndexSet() - is2.insert(integersIn: 5..<18) - is2.insert(integersIn: 45..<61) - - expected = IndexSet() - expectEqual(expected, is1.symmetricDifference(is2)) - expectEqual(expected, is2.symmetricDifference(is1)) - } - - do { - is1 = IndexSet(integersIn: 1..<10) - is2 = IndexSet(integersIn: 20..<30) - - expected = IndexSet() - expected.insert(integersIn: 1..<10) - expected.insert(integersIn: 20..<30) - expectEqual(expected, is1.symmetricDifference(is2)) - expectEqual(expected, is2.symmetricDifference(is1)) - } - - do { - is1 = IndexSet(integersIn: 1..<10) - is2 = IndexSet(integersIn: 1..<11) - expected = IndexSet(integer: 10) - expectEqual(expected, is1.symmetricDifference(is2)) - expectEqual(expected, is2.symmetricDifference(is1)) - } - - do { - is1 = IndexSet(integer: 42) - is2 = IndexSet(integer: 42) - expectEqual(IndexSet(), is1.symmetricDifference(is2)) - expectEqual(IndexSet(), is2.symmetricDifference(is1)) - } - - do { - is1 = IndexSet(integer: 1) - is1.insert(3) - is1.insert(5) - is1.insert(7) - - is2 = IndexSet(integer: 0) - is2.insert(2) - is2.insert(4) - is2.insert(6) - - expected = IndexSet(integersIn: 0..<8) - expectEqual(expected, is1.symmetricDifference(is2)) - expectEqual(expected, is2.symmetricDifference(is1)) - } - - do { - is1 = IndexSet(integersIn: 0..<5) - is2 = IndexSet(integersIn: 3..<10) - - expected = IndexSet(integersIn: 0..<3) - expected.insert(integersIn: 5..<10) - - expectEqual(expected, is1.symmetricDifference(is2)) - expectEqual(expected, is2.symmetricDifference(is1)) - } - - do { - is1 = IndexSet([0, 2]) - is2 = IndexSet([0, 1, 2]) - expectEqual(IndexSet(integer: 1), is1.symmetricDifference(is2)) - } - } - - func testIntersection() { - var is1 : IndexSet - var is2 : IndexSet - var expected : IndexSet - - do { - is1 = IndexSet() - is1.insert(integersIn: 1..<3) - is1.insert(integersIn: 4..<11) - is1.insert(integersIn: 15..<21) - is1.insert(integersIn: 40..<51) - - is2 = IndexSet() - is2.insert(integersIn: 5..<18) - is2.insert(integersIn: 45..<61) - - expected = IndexSet() - expected.insert(integersIn: 5..<11) - expected.insert(integersIn: 15..<18) - expected.insert(integersIn: 45..<51) - - expectEqual(expected, is1.intersection(is2)) - expectEqual(expected, is2.intersection(is1)) - } - - do { - is1 = IndexSet() - is1.insert(integersIn: 5..<11) - is1.insert(integersIn: 20..<31) - - is2 = IndexSet() - is2.insert(integersIn: 11..<20) - is2.insert(integersIn: 31..<40) - - expectEqual(IndexSet(), is1.intersection(is2)) - expectEqual(IndexSet(), is2.intersection(is1)) - } - - do { - is1 = IndexSet(integer: 42) - is2 = IndexSet(integer: 42) - expectEqual(IndexSet(integer: 42), is1.intersection(is2)) - } - - do { - is1 = IndexSet(integer: 1) - is1.insert(3) - is1.insert(5) - is1.insert(7) - - is2 = IndexSet(integer: 0) - is2.insert(2) - is2.insert(4) - is2.insert(6) - - expected = IndexSet() - expectEqual(expected, is1.intersection(is2)) - expectEqual(expected, is2.intersection(is1)) - } - - do { - is1 = IndexSet(integersIn: 0..<5) - is2 = IndexSet(integersIn: 4..<10) - - expected = IndexSet(integer: 4) - - expectEqual(expected, is1.intersection(is2)) - expectEqual(expected, is2.intersection(is1)) - } - - do { - is1 = IndexSet([0, 2]) - is2 = IndexSet([0, 1, 2]) - expectEqual(is1, is1.intersection(is2)) - } - } - - func testUnion() { - var is1 : IndexSet - var is2 : IndexSet - var expected : IndexSet - - do { - is1 = IndexSet() - is1.insert(integersIn: 1..<3) - is1.insert(integersIn: 4..<11) - is1.insert(integersIn: 15..<21) - is1.insert(integersIn: 40..<51) - - is2 = IndexSet() - is2.insert(integersIn: 5..<18) - is2.insert(integersIn: 45..<61) - - expected = IndexSet() - expected.insert(integersIn: 1..<3) - expected.insert(integersIn: 4..<21) - expected.insert(integersIn: 40..<61) - - expectEqual(expected, is1.union(is2)) - expectEqual(expected, is2.union(is1)) - } - - do { - is1 = IndexSet() - is1.insert(integersIn: 5..<11) - is1.insert(integersIn: 20..<31) - - is2 = IndexSet() - is2.insert(integersIn: 11..<20) - is2.insert(integersIn: 31..<40) - - expected = IndexSet() - expected.insert(integersIn: 5..<11) - expected.insert(integersIn: 20..<31) - expected.insert(integersIn: 11..<20) - expected.insert(integersIn: 31..<40) - - expectEqual(expected, is1.union(is2)) - expectEqual(expected, is2.union(is1)) - } - - do { - is1 = IndexSet(integer: 42) - is2 = IndexSet(integer: 42) - - expectEqual(IndexSet(integer: 42), is1.union(is2)) - } - - do { - is1 = IndexSet() - is1.insert(integersIn: 5..<10) - is1.insert(integersIn: 15..<20) - - is2 = IndexSet() - is2.insert(integersIn: 1..<4) - is2.insert(integersIn: 15..<20) - - expected = IndexSet() - expected.insert(integersIn: 1..<4) - expected.insert(integersIn: 5..<10) - expected.insert(integersIn: 15..<20) - - expectEqual(expected, is1.union(is2)) - expectEqual(expected, is2.union(is1)) - } - - expectEqual(IndexSet(), IndexSet().union(IndexSet())) - - do { - is1 = IndexSet(integer: 1) - is1.insert(3) - is1.insert(5) - is1.insert(7) - - is2 = IndexSet(integer: 0) - is2.insert(2) - is2.insert(4) - is2.insert(6) - - expected = IndexSet() - expectEqual(expected, is1.intersection(is2)) - expectEqual(expected, is2.intersection(is1)) - } - - do { - is1 = IndexSet(integersIn: 0..<5) - is2 = IndexSet(integersIn: 3..<10) - - expected = IndexSet(integersIn: 0..<10) - - expectEqual(expected, is1.union(is2)) - expectEqual(expected, is2.union(is1)) - } - - do { - is1 = IndexSet() - is1.insert(2) - is1.insert(6) - is1.insert(21) - is1.insert(22) - - is2 = IndexSet() - is2.insert(8) - is2.insert(14) - is2.insert(21) - is2.insert(22) - is2.insert(24) - - expected = IndexSet() - expected.insert(2) - expected.insert(6) - expected.insert(21) - expected.insert(22) - expected.insert(8) - expected.insert(14) - expected.insert(21) - expected.insert(22) - expected.insert(24) - - expectEqual(expected, is1.union(is2)) - expectEqual(expected, is2.union(is1)) - } - } - - func test_findIndex() { - var i = IndexSet() - - // Verify nil result for empty sets - expectEqual(nil, i.first) - expectEqual(nil, i.last) - expectEqual(nil, i.integerGreaterThan(5)) - expectEqual(nil, i.integerLessThan(5)) - expectEqual(nil, i.integerGreaterThanOrEqualTo(5)) - expectEqual(nil, i.integerLessThanOrEqualTo(5)) - - i.insert(integersIn: 5..<10) - i.insert(integersIn: 15..<20) - - // Verify non-nil result - expectEqual(5, i.first) - expectEqual(19, i.last) - - expectEqual(nil, i.integerGreaterThan(19)) - expectEqual(5, i.integerGreaterThan(3)) - - expectEqual(nil, i.integerLessThan(5)) - expectEqual(5, i.integerLessThan(6)) - - expectEqual(nil, i.integerGreaterThanOrEqualTo(20)) - expectEqual(19, i.integerGreaterThanOrEqualTo(19)) - - expectEqual(nil, i.integerLessThanOrEqualTo(4)) - expectEqual(5, i.integerLessThanOrEqualTo(5)) - } - - // MARK: - - // MARK: Performance Testing - - func largeIndexSet() -> IndexSet { - var result = IndexSet() - - for i in 1..<10000 { - let start = i * 10 - let end = start + 9 - result.insert(integersIn: start.. Int { - let empty = UnsafeMutablePointer.allocate(capacity: 0) - defer { empty.deallocate() } - let length = snprintf(ptr: empty, 0, "%0.*g", DBL_DECIMAL_DIG, value) - return Int(length) - } - - // Duplicated to handle a special case - func localTestRoundTrip(of value: T) { - var payload: Data! = nil - do { - let encoder = JSONEncoder() - payload = try encoder.encode(value) - } catch { - expectUnreachable("Failed to encode \(T.self) to JSON: \(error)") - } - - do { - let decoder = JSONDecoder() - let decoded = try decoder.decode(T.self, from: payload) - - /// `snprintf`'s `%g`, which `JSONSerialization` uses internally for double values, does not respect - /// our precision requests in every case. This bug effects Darwin, FreeBSD, and Linux currently - /// causing this test (which uses the current time) to fail occasionally. - if formattedLength(of: (decoded as! Date).timeIntervalSinceReferenceDate) > DBL_DECIMAL_DIG + 2 { - let adjustedTimeIntervalSinceReferenceDate: (Date) -> Double = { date in - let adjustment = pow(10, Double(DBL_DECIMAL_DIG)) - return Double(floor(adjustment * date.timeIntervalSinceReferenceDate).rounded() / adjustment) - } - - let decodedAprox = adjustedTimeIntervalSinceReferenceDate(decoded as! Date) - let valueAprox = adjustedTimeIntervalSinceReferenceDate(value as! Date) - expectEqual(decodedAprox, valueAprox, "\(T.self) did not round-trip to an equal value after DBL_DECIMAL_DIG adjustment \(decodedAprox) != \(valueAprox).") - return - } - - expectEqual(decoded, value, "\(T.self) did not round-trip to an equal value. \((decoded as! Date).timeIntervalSinceReferenceDate) != \((value as! Date).timeIntervalSinceReferenceDate)") - } catch { - expectUnreachable("Failed to decode \(T.self) from JSON: \(error)") - } - } - - // Test the above `snprintf` edge case evaluation with a known triggering case - let knownBadDate = Date(timeIntervalSinceReferenceDate: 0.0021413276231263384) - localTestRoundTrip(of: knownBadDate) - - localTestRoundTrip(of: Date()) - - // Optional dates should encode the same way. - localTestRoundTrip(of: Optional(Date())) - } - - func testEncodingDateSecondsSince1970() { - // Cannot encode an arbitrary number of seconds since we've lost precision since 1970. - let seconds = 1000.0 - let expectedJSON = "1000".data(using: .utf8)! - - _testRoundTrip(of: Date(timeIntervalSince1970: seconds), - expectedJSON: expectedJSON, - dateEncodingStrategy: .secondsSince1970, - dateDecodingStrategy: .secondsSince1970) - - // Optional dates should encode the same way. - _testRoundTrip(of: Optional(Date(timeIntervalSince1970: seconds)), - expectedJSON: expectedJSON, - dateEncodingStrategy: .secondsSince1970, - dateDecodingStrategy: .secondsSince1970) - } - - func testEncodingDateMillisecondsSince1970() { - // Cannot encode an arbitrary number of seconds since we've lost precision since 1970. - let seconds = 1000.0 - let expectedJSON = "1000000".data(using: .utf8)! - - _testRoundTrip(of: Date(timeIntervalSince1970: seconds), - expectedJSON: expectedJSON, - dateEncodingStrategy: .millisecondsSince1970, - dateDecodingStrategy: .millisecondsSince1970) - - // Optional dates should encode the same way. - _testRoundTrip(of: Optional(Date(timeIntervalSince1970: seconds)), - expectedJSON: expectedJSON, - dateEncodingStrategy: .millisecondsSince1970, - dateDecodingStrategy: .millisecondsSince1970) - } - - func testEncodingDateISO8601() { - if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - let formatter = ISO8601DateFormatter() - formatter.formatOptions = .withInternetDateTime - - let timestamp = Date(timeIntervalSince1970: 1000) - let expectedJSON = "\"\(formatter.string(from: timestamp))\"".data(using: .utf8)! - - _testRoundTrip(of: timestamp, - expectedJSON: expectedJSON, - dateEncodingStrategy: .iso8601, - dateDecodingStrategy: .iso8601) - - - // Optional dates should encode the same way. - _testRoundTrip(of: Optional(timestamp), - expectedJSON: expectedJSON, - dateEncodingStrategy: .iso8601, - dateDecodingStrategy: .iso8601) - } - } - - func testEncodingDateFormatted() { - let formatter = DateFormatter() - formatter.dateStyle = .full - formatter.timeStyle = .full - - let timestamp = Date(timeIntervalSince1970: 1000) - let expectedJSON = "\"\(formatter.string(from: timestamp))\"".data(using: .utf8)! - - _testRoundTrip(of: timestamp, - expectedJSON: expectedJSON, - dateEncodingStrategy: .formatted(formatter), - dateDecodingStrategy: .formatted(formatter)) - - // Optional dates should encode the same way. - _testRoundTrip(of: Optional(timestamp), - expectedJSON: expectedJSON, - dateEncodingStrategy: .formatted(formatter), - dateDecodingStrategy: .formatted(formatter)) - } - - func testEncodingDateCustom() { - let timestamp = Date() - - // We'll encode a number instead of a date. - let encode = { (_ data: Date, _ encoder: Encoder) throws -> Void in - var container = encoder.singleValueContainer() - try container.encode(42) - } - let decode = { (_: Decoder) throws -> Date in return timestamp } - - let expectedJSON = "42".data(using: .utf8)! - _testRoundTrip(of: timestamp, - expectedJSON: expectedJSON, - dateEncodingStrategy: .custom(encode), - dateDecodingStrategy: .custom(decode)) - - // Optional dates should encode the same way. - _testRoundTrip(of: Optional(timestamp), - expectedJSON: expectedJSON, - dateEncodingStrategy: .custom(encode), - dateDecodingStrategy: .custom(decode)) - } - - func testEncodingDateCustomEmpty() { - let timestamp = Date() - - // Encoding nothing should encode an empty keyed container ({}). - let encode = { (_: Date, _: Encoder) throws -> Void in } - let decode = { (_: Decoder) throws -> Date in return timestamp } - - let expectedJSON = "{}".data(using: .utf8)! - _testRoundTrip(of: timestamp, - expectedJSON: expectedJSON, - dateEncodingStrategy: .custom(encode), - dateDecodingStrategy: .custom(decode)) - - // Optional dates should encode the same way. - _testRoundTrip(of: Optional(timestamp), - expectedJSON: expectedJSON, - dateEncodingStrategy: .custom(encode), - dateDecodingStrategy: .custom(decode)) - } - - // MARK: - Data Strategy Tests - func testEncodingData() { - let data = Data(bytes: [0xDE, 0xAD, 0xBE, 0xEF]) - - let expectedJSON = "[222,173,190,239]".data(using: .utf8)! - _testRoundTrip(of: data, - expectedJSON: expectedJSON, - dataEncodingStrategy: .deferredToData, - dataDecodingStrategy: .deferredToData) - - // Optional data should encode the same way. - _testRoundTrip(of: Optional(data), - expectedJSON: expectedJSON, - dataEncodingStrategy: .deferredToData, - dataDecodingStrategy: .deferredToData) - } - - func testEncodingDataBase64() { - let data = Data(bytes: [0xDE, 0xAD, 0xBE, 0xEF]) - - let expectedJSON = "\"3q2+7w==\"".data(using: .utf8)! - _testRoundTrip(of: data, expectedJSON: expectedJSON) - - // Optional data should encode the same way. - _testRoundTrip(of: Optional(data), expectedJSON: expectedJSON) - } - - func testEncodingDataCustom() { - // We'll encode a number instead of data. - let encode = { (_ data: Data, _ encoder: Encoder) throws -> Void in - var container = encoder.singleValueContainer() - try container.encode(42) - } - let decode = { (_: Decoder) throws -> Data in return Data() } - - let expectedJSON = "42".data(using: .utf8)! - _testRoundTrip(of: Data(), - expectedJSON: expectedJSON, - dataEncodingStrategy: .custom(encode), - dataDecodingStrategy: .custom(decode)) - - // Optional data should encode the same way. - _testRoundTrip(of: Optional(Data()), - expectedJSON: expectedJSON, - dataEncodingStrategy: .custom(encode), - dataDecodingStrategy: .custom(decode)) - } - - func testEncodingDataCustomEmpty() { - // Encoding nothing should encode an empty keyed container ({}). - let encode = { (_: Data, _: Encoder) throws -> Void in } - let decode = { (_: Decoder) throws -> Data in return Data() } - - let expectedJSON = "{}".data(using: .utf8)! - _testRoundTrip(of: Data(), - expectedJSON: expectedJSON, - dataEncodingStrategy: .custom(encode), - dataDecodingStrategy: .custom(decode)) - - // Optional Data should encode the same way. - _testRoundTrip(of: Optional(Data()), - expectedJSON: expectedJSON, - dataEncodingStrategy: .custom(encode), - dataDecodingStrategy: .custom(decode)) - } - - // MARK: - Non-Conforming Floating Point Strategy Tests - func testEncodingNonConformingFloats() { - _testEncodeFailure(of: Float.infinity) - _testEncodeFailure(of: Float.infinity) - _testEncodeFailure(of: -Float.infinity) - _testEncodeFailure(of: Float.nan) - - _testEncodeFailure(of: Double.infinity) - _testEncodeFailure(of: -Double.infinity) - _testEncodeFailure(of: Double.nan) - - // Optional Floats/Doubles should encode the same way. - _testEncodeFailure(of: Float.infinity) - _testEncodeFailure(of: -Float.infinity) - _testEncodeFailure(of: Float.nan) - - _testEncodeFailure(of: Double.infinity) - _testEncodeFailure(of: -Double.infinity) - _testEncodeFailure(of: Double.nan) - } - - func testEncodingNonConformingFloatStrings() { - let encodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") - let decodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") - - _testRoundTrip(of: Float.infinity, - expectedJSON: "\"INF\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - _testRoundTrip(of: -Float.infinity, - expectedJSON: "\"-INF\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - - // Since Float.nan != Float.nan, we have to use a placeholder that'll encode NaN but actually round-trip. - _testRoundTrip(of: FloatNaNPlaceholder(), - expectedJSON: "\"NaN\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - - _testRoundTrip(of: Double.infinity, - expectedJSON: "\"INF\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - _testRoundTrip(of: -Double.infinity, - expectedJSON: "\"-INF\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - - // Since Double.nan != Double.nan, we have to use a placeholder that'll encode NaN but actually round-trip. - _testRoundTrip(of: DoubleNaNPlaceholder(), - expectedJSON: "\"NaN\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - - // Optional Floats and Doubles should encode the same way. - _testRoundTrip(of: Optional(Float.infinity), - expectedJSON: "\"INF\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - _testRoundTrip(of: Optional(-Float.infinity), - expectedJSON: "\"-INF\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - _testRoundTrip(of: Optional(Double.infinity), - expectedJSON: "\"INF\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - _testRoundTrip(of: Optional(-Double.infinity), - expectedJSON: "\"-INF\"".data(using: .utf8)!, - nonConformingFloatEncodingStrategy: encodingStrategy, - nonConformingFloatDecodingStrategy: decodingStrategy) - } - - // MARK: - Key Strategy Tests - private struct EncodeMe : Encodable { - var keyName: String - func encode(to coder: Encoder) throws { - var c = coder.container(keyedBy: _TestKey.self) - try c.encode("test", forKey: _TestKey(stringValue: keyName)!) - } - } - - func testEncodingKeyStrategySnake() { - let toSnakeCaseTests = [ - ("simpleOneTwo", "simple_one_two"), - ("myURL", "my_url"), - ("singleCharacterAtEndX", "single_character_at_end_x"), - ("thisIsAnXMLProperty", "this_is_an_xml_property"), - ("single", "single"), // no underscore - ("", ""), // don't die on empty string - ("a", "a"), // single character - ("aA", "a_a"), // two characters - ("version4Thing", "version4_thing"), // numerics - ("partCAPS", "part_caps"), // only insert underscore before first all caps - ("partCAPSLowerAGAIN", "part_caps_lower_again"), // switch back and forth caps. - ("manyWordsInThisThing", "many_words_in_this_thing"), // simple lowercase + underscore + more - ("asdfĆqer", "asdf_ćqer"), - ("already_snake_case", "already_snake_case"), - ("dataPoint22", "data_point22"), - ("dataPoint22Word", "data_point22_word"), - ("_oneTwoThree", "_one_two_three"), - ("oneTwoThree_", "one_two_three_"), - ("__oneTwoThree", "__one_two_three"), - ("oneTwoThree__", "one_two_three__"), - ("_oneTwoThree_", "_one_two_three_"), - ("__oneTwoThree", "__one_two_three"), - ("__oneTwoThree__", "__one_two_three__"), - ("_test", "_test"), - ("_test_", "_test_"), - ("__test", "__test"), - ("test__", "test__"), - ("m͉̟̹y̦̳G͍͚͎̳r̤͉̤͕ͅea̲͕t͇̥̼͖U͇̝̠R͙̻̥͓̣L̥̖͎͓̪̫ͅR̩͖̩eq͈͓u̞e̱s̙t̤̺ͅ", "m͉̟̹y̦̳_g͍͚͎̳r̤͉̤͕ͅea̲͕t͇̥̼͖_u͇̝̠r͙̻̥͓̣l̥̖͎͓̪̫ͅ_r̩͖̩eq͈͓u̞e̱s̙t̤̺ͅ"), // because Itai wanted to test this - ("🐧🐟", "🐧🐟") // fishy emoji example? - ] - - for test in toSnakeCaseTests { - let expected = "{\"\(test.1)\":\"test\"}" - let encoded = EncodeMe(keyName: test.0) - - let encoder = JSONEncoder() - encoder.keyEncodingStrategy = .convertToSnakeCase - let resultData = try! encoder.encode(encoded) - let resultString = String(bytes: resultData, encoding: .utf8) - - expectEqual(expected, resultString) - } - } - - func testEncodingKeyStrategyCustom() { - let expected = "{\"QQQhello\":\"test\"}" - let encoded = EncodeMe(keyName: "hello") - - let encoder = JSONEncoder() - let customKeyConversion = { (_ path : [CodingKey]) -> CodingKey in - let key = _TestKey(stringValue: "QQQ" + path.last!.stringValue)! - return key - } - encoder.keyEncodingStrategy = .custom(customKeyConversion) - let resultData = try! encoder.encode(encoded) - let resultString = String(bytes: resultData, encoding: .utf8) - - expectEqual(expected, resultString) - } - - func testEncodingDictionaryStringKeyConversionUntouched() { - let expected = "{\"leaveMeAlone\":\"test\"}" - let toEncode: [String: String] = ["leaveMeAlone": "test"] - - let encoder = JSONEncoder() - encoder.keyEncodingStrategy = .convertToSnakeCase - let resultData = try! encoder.encode(toEncode) - let resultString = String(bytes: resultData, encoding: .utf8) - - expectEqual(expected, resultString) - } - - private struct EncodeFailure : Encodable { - var someValue: Double - } - - private struct EncodeFailureNested : Encodable { - var nestedValue: EncodeFailure - } - - func testEncodingDictionaryFailureKeyPath() { - let toEncode: [String: EncodeFailure] = ["key": EncodeFailure(someValue: Double.nan)] - - let encoder = JSONEncoder() - encoder.keyEncodingStrategy = .convertToSnakeCase - do { - _ = try encoder.encode(toEncode) - } catch EncodingError.invalidValue(_, let context) { - expectEqual(2, context.codingPath.count) - expectEqual("key", context.codingPath[0].stringValue) - expectEqual("someValue", context.codingPath[1].stringValue) - } catch { - expectUnreachable("Unexpected error: \(String(describing: error))") - } - } - - func testEncodingDictionaryFailureKeyPathNested() { - let toEncode: [String: [String: EncodeFailureNested]] = ["key": ["sub_key": EncodeFailureNested(nestedValue: EncodeFailure(someValue: Double.nan))]] - - let encoder = JSONEncoder() - encoder.keyEncodingStrategy = .convertToSnakeCase - do { - _ = try encoder.encode(toEncode) - } catch EncodingError.invalidValue(_, let context) { - expectEqual(4, context.codingPath.count) - expectEqual("key", context.codingPath[0].stringValue) - expectEqual("sub_key", context.codingPath[1].stringValue) - expectEqual("nestedValue", context.codingPath[2].stringValue) - expectEqual("someValue", context.codingPath[3].stringValue) - } catch { - expectUnreachable("Unexpected error: \(String(describing: error))") - } - } - - private struct EncodeNested : Encodable { - let nestedValue: EncodeMe - } - - private struct EncodeNestedNested : Encodable { - let outerValue: EncodeNested - } - - func testEncodingKeyStrategyPath() { - // Make sure a more complex path shows up the way we want - // Make sure the path reflects keys in the Swift, not the resulting ones in the JSON - let expected = "{\"QQQouterValue\":{\"QQQnestedValue\":{\"QQQhelloWorld\":\"test\"}}}" - let encoded = EncodeNestedNested(outerValue: EncodeNested(nestedValue: EncodeMe(keyName: "helloWorld"))) - - let encoder = JSONEncoder() - var callCount = 0 - - let customKeyConversion = { (_ path : [CodingKey]) -> CodingKey in - // This should be called three times: - // 1. to convert 'outerValue' to something - // 2. to convert 'nestedValue' to something - // 3. to convert 'helloWorld' to something - callCount = callCount + 1 - - if path.count == 0 { - expectUnreachable("The path should always have at least one entry") - } else if path.count == 1 { - expectEqual(["outerValue"], path.map { $0.stringValue }) - } else if path.count == 2 { - expectEqual(["outerValue", "nestedValue"], path.map { $0.stringValue }) - } else if path.count == 3 { - expectEqual(["outerValue", "nestedValue", "helloWorld"], path.map { $0.stringValue }) - } else { - expectUnreachable("The path mysteriously had more entries") - } - - let key = _TestKey(stringValue: "QQQ" + path.last!.stringValue)! - return key - } - encoder.keyEncodingStrategy = .custom(customKeyConversion) - let resultData = try! encoder.encode(encoded) - let resultString = String(bytes: resultData, encoding: .utf8) - - expectEqual(expected, resultString) - expectEqual(3, callCount) - } - - private struct DecodeMe : Decodable { - let found: Bool - init(from coder: Decoder) throws { - let c = try coder.container(keyedBy: _TestKey.self) - // Get the key that we expect to be passed in (camel case) - let camelCaseKey = try c.decode(String.self, forKey: _TestKey(stringValue: "camelCaseKey")!) - - // Use the camel case key to decode from the JSON. The decoder should convert it to snake case to find it. - found = try c.decode(Bool.self, forKey: _TestKey(stringValue: camelCaseKey)!) - } - } - - func testDecodingKeyStrategyCamel() { - let fromSnakeCaseTests = [ - ("", ""), // don't die on empty string - ("a", "a"), // single character - ("ALLCAPS", "ALLCAPS"), // If no underscores, we leave the word as-is - ("ALL_CAPS", "allCaps"), // Conversion from screaming snake case - ("single", "single"), // do not capitalize anything with no underscore - ("snake_case", "snakeCase"), // capitalize a character - ("one_two_three", "oneTwoThree"), // more than one word - ("one_2_three", "one2Three"), // numerics - ("one2_three", "one2Three"), // numerics, part 2 - ("snake_Ćase", "snakeĆase"), // do not further modify a capitalized diacritic - ("snake_ćase", "snakeĆase"), // capitalize a diacritic - ("alreadyCamelCase", "alreadyCamelCase"), // do not modify already camel case - ("__this_and_that", "__thisAndThat"), - ("_this_and_that", "_thisAndThat"), - ("this__and__that", "thisAndThat"), - ("this_and_that__", "thisAndThat__"), - ("this_aNd_that", "thisAndThat"), - ("_one_two_three", "_oneTwoThree"), - ("one_two_three_", "oneTwoThree_"), - ("__one_two_three", "__oneTwoThree"), - ("one_two_three__", "oneTwoThree__"), - ("_one_two_three_", "_oneTwoThree_"), - ("__one_two_three", "__oneTwoThree"), - ("__one_two_three__", "__oneTwoThree__"), - ("_test", "_test"), - ("_test_", "_test_"), - ("__test", "__test"), - ("test__", "test__"), - ("_", "_"), - ("__", "__"), - ("___", "___"), - ("m͉̟̹y̦̳G͍͚͎̳r̤͉̤͕ͅea̲͕t͇̥̼͖U͇̝̠R͙̻̥͓̣L̥̖͎͓̪̫ͅR̩͖̩eq͈͓u̞e̱s̙t̤̺ͅ", "m͉̟̹y̦̳G͍͚͎̳r̤͉̤͕ͅea̲͕t͇̥̼͖U͇̝̠R͙̻̥͓̣L̥̖͎͓̪̫ͅR̩͖̩eq͈͓u̞e̱s̙t̤̺ͅ"), // because Itai wanted to test this - ("🐧_🐟", "🐧🐟") // fishy emoji example? - ] - - for test in fromSnakeCaseTests { - // This JSON contains the camel case key that the test object should decode with, then it uses the snake case key (test.0) as the actual key for the boolean value. - let input = "{\"camelCaseKey\":\"\(test.1)\",\"\(test.0)\":true}".data(using: .utf8)! - - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - - let result = try! decoder.decode(DecodeMe.self, from: input) - - expectTrue(result.found) - } - } - - private struct DecodeMe2 : Decodable { var hello: String } - - func testDecodingKeyStrategyCustom() { - let input = "{\"----hello\":\"test\"}".data(using: .utf8)! - let decoder = JSONDecoder() - let customKeyConversion = { (_ path: [CodingKey]) -> CodingKey in - // This converter removes the first 4 characters from the start of all string keys, if it has more than 4 characters - let string = path.last!.stringValue - guard string.count > 4 else { return path.last! } - let newString = String(string.dropFirst(4)) - return _TestKey(stringValue: newString)! - } - decoder.keyDecodingStrategy = .custom(customKeyConversion) - let result = try! decoder.decode(DecodeMe2.self, from: input) - - expectEqual("test", result.hello) - } - - func testDecodingDictionaryStringKeyConversionUntouched() { - let input = "{\"leave_me_alone\":\"test\"}".data(using: .utf8)! - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - let result = try! decoder.decode([String: String].self, from: input) - - expectEqual(["leave_me_alone": "test"], result) - } - - func testDecodingDictionaryFailureKeyPath() { - let input = "{\"leave_me_alone\":\"test\"}".data(using: .utf8)! - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - do { - _ = try decoder.decode([String: Int].self, from: input) - } catch DecodingError.typeMismatch(_, let context) { - expectEqual(1, context.codingPath.count) - expectEqual("leave_me_alone", context.codingPath[0].stringValue) - } catch { - expectUnreachable("Unexpected error: \(String(describing: error))") - } - } - - private struct DecodeFailure : Decodable { - var intValue: Int - } - - private struct DecodeFailureNested : Decodable { - var nestedValue: DecodeFailure - } - - func testDecodingDictionaryFailureKeyPathNested() { - let input = "{\"top_level\": {\"sub_level\": {\"nested_value\": {\"int_value\": \"not_an_int\"}}}}".data(using: .utf8)! - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - do { - _ = try decoder.decode([String: [String : DecodeFailureNested]].self, from: input) - } catch DecodingError.typeMismatch(_, let context) { - expectEqual(4, context.codingPath.count) - expectEqual("top_level", context.codingPath[0].stringValue) - expectEqual("sub_level", context.codingPath[1].stringValue) - expectEqual("nestedValue", context.codingPath[2].stringValue) - expectEqual("intValue", context.codingPath[3].stringValue) - } catch { - expectUnreachable("Unexpected error: \(String(describing: error))") - } - } - - private struct DecodeMe3 : Codable { - var thisIsCamelCase : String - } - - func testEncodingKeyStrategySnakeGenerated() { - // Test that this works with a struct that has automatically generated keys - let input = "{\"this_is_camel_case\":\"test\"}".data(using: .utf8)! - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - let result = try! decoder.decode(DecodeMe3.self, from: input) - - expectEqual("test", result.thisIsCamelCase) - } - - func testDecodingKeyStrategyCamelGenerated() { - let encoded = DecodeMe3(thisIsCamelCase: "test") - let encoder = JSONEncoder() - encoder.keyEncodingStrategy = .convertToSnakeCase - let resultData = try! encoder.encode(encoded) - let resultString = String(bytes: resultData, encoding: .utf8) - expectEqual("{\"this_is_camel_case\":\"test\"}", resultString) - } - - func testKeyStrategySnakeGeneratedAndCustom() { - // Test that this works with a struct that has automatically generated keys - struct DecodeMe4 : Codable { - var thisIsCamelCase : String - var thisIsCamelCaseToo : String - private enum CodingKeys : String, CodingKey { - case thisIsCamelCase = "fooBar" - case thisIsCamelCaseToo - } - } - - // Decoding - let input = "{\"foo_bar\":\"test\",\"this_is_camel_case_too\":\"test2\"}".data(using: .utf8)! - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - let decodingResult = try! decoder.decode(DecodeMe4.self, from: input) - - expectEqual("test", decodingResult.thisIsCamelCase) - expectEqual("test2", decodingResult.thisIsCamelCaseToo) - - // Encoding - let encoded = DecodeMe4(thisIsCamelCase: "test", thisIsCamelCaseToo: "test2") - let encoder = JSONEncoder() - encoder.keyEncodingStrategy = .convertToSnakeCase - let encodingResultData = try! encoder.encode(encoded) - let encodingResultString = String(bytes: encodingResultData, encoding: .utf8) - expectEqual("{\"foo_bar\":\"test\",\"this_is_camel_case_too\":\"test2\"}", encodingResultString) - } - - func testKeyStrategyDuplicateKeys() { - // This test is mostly to make sure we don't assert on duplicate keys - struct DecodeMe5 : Codable { - var oneTwo : String - var numberOfKeys : Int - - enum CodingKeys : String, CodingKey { - case oneTwo - case oneTwoThree - } - - init() { - oneTwo = "test" - numberOfKeys = 0 - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - oneTwo = try container.decode(String.self, forKey: .oneTwo) - numberOfKeys = container.allKeys.count - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(oneTwo, forKey: .oneTwo) - try container.encode("test2", forKey: .oneTwoThree) - } - } - - let customKeyConversion = { (_ path: [CodingKey]) -> CodingKey in - // All keys are the same! - return _TestKey(stringValue: "oneTwo")! - } - - // Decoding - // This input has a dictionary with two keys, but only one will end up in the container - let input = "{\"unused key 1\":\"test1\",\"unused key 2\":\"test2\"}".data(using: .utf8)! - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .custom(customKeyConversion) - - let decodingResult = try! decoder.decode(DecodeMe5.self, from: input) - // There will be only one result for oneTwo (the second one in the json) - expectEqual(1, decodingResult.numberOfKeys) - - // Encoding - let encoded = DecodeMe5() - let encoder = JSONEncoder() - encoder.keyEncodingStrategy = .custom(customKeyConversion) - let decodingResultData = try! encoder.encode(encoded) - let decodingResultString = String(bytes: decodingResultData, encoding: .utf8) - - // There will be only one value in the result (the second one encoded) - expectEqual("{\"oneTwo\":\"test2\"}", decodingResultString) - } - - // MARK: - Encoder Features - func testNestedContainerCodingPaths() { - let encoder = JSONEncoder() - do { - let _ = try encoder.encode(NestedContainersTestType()) - } catch let error as NSError { - expectUnreachable("Caught error during encoding nested container types: \(error)") - } - } - - func testSuperEncoderCodingPaths() { - let encoder = JSONEncoder() - do { - let _ = try encoder.encode(NestedContainersTestType(testSuperEncoder: true)) - } catch let error as NSError { - expectUnreachable("Caught error during encoding nested container types: \(error)") - } - } - - func testInterceptDecimal() { - let expectedJSON = "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".data(using: .utf8)! - - // Want to make sure we write out a JSON number, not the keyed encoding here. - // 1e127 is too big to fit natively in a Double, too, so want to make sure it's encoded as a Decimal. - let decimal = Decimal(sign: .plus, exponent: 127, significand: Decimal(1)) - _testRoundTrip(of: decimal, expectedJSON: expectedJSON) - - // Optional Decimals should encode the same way. - _testRoundTrip(of: Optional(decimal), expectedJSON: expectedJSON) - } - - func testInterceptURL() { - // Want to make sure JSONEncoder writes out single-value URLs, not the keyed encoding. - let expectedJSON = "\"http:\\/\\/swift.org\"".data(using: .utf8)! - let url = URL(string: "http://swift.org")! - _testRoundTrip(of: url, expectedJSON: expectedJSON) - - // Optional URLs should encode the same way. - _testRoundTrip(of: Optional(url), expectedJSON: expectedJSON) - } - - func testInterceptURLWithoutEscapingOption() { - if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - // Want to make sure JSONEncoder writes out single-value URLs, not the keyed encoding. - let expectedJSON = "\"http://swift.org\"".data(using: .utf8)! - let url = URL(string: "http://swift.org")! - _testRoundTrip(of: url, expectedJSON: expectedJSON, outputFormatting: [.withoutEscapingSlashes]) - - // Optional URLs should encode the same way. - _testRoundTrip(of: Optional(url), expectedJSON: expectedJSON, outputFormatting: [.withoutEscapingSlashes]) - } - } - - // MARK: - Type coercion - func testTypeCoercion() { - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int8].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int16].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int32].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int64].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt8].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt16].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt32].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt64].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Float].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Double].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int8], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int16], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int32], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int64], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt8], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt16], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt32], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt64], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Float], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Double], as: [Bool].self) - } - - func testDecodingConcreteTypeParameter() { - let encoder = JSONEncoder() - guard let json = try? encoder.encode(Employee.testValue) else { - expectUnreachable("Unable to encode Employee.") - return - } - - let decoder = JSONDecoder() - guard let decoded = try? decoder.decode(Employee.self as Person.Type, from: json) else { - expectUnreachable("Failed to decode Employee as Person from JSON.") - return - } - - expectEqual(type(of: decoded), Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") - } - - // MARK: - Encoder State - // SR-6078 - func testEncoderStateThrowOnEncode() { - struct ReferencingEncoderWrapper : Encodable { - let value: T - init(_ value: T) { self.value = value } - - func encode(to encoder: Encoder) throws { - // This approximates a subclass calling into its superclass, where the superclass encodes a value that might throw. - // The key here is that getting the superEncoder creates a referencing encoder. - var container = encoder.unkeyedContainer() - let superEncoder = container.superEncoder() - - // Pushing a nested container on leaves the referencing encoder with multiple containers. - var nestedContainer = superEncoder.unkeyedContainer() - try nestedContainer.encode(value) - } - } - - // The structure that would be encoded here looks like - // - // [[[Float.infinity]]] - // - // The wrapper asks for an unkeyed container ([^]), gets a super encoder, and creates a nested container into that ([[^]]). - // We then encode an array into that ([[[^]]]), which happens to be a value that causes us to throw an error. - // - // The issue at hand reproduces when you have a referencing encoder (superEncoder() creates one) that has a container on the stack (unkeyedContainer() adds one) that encodes a value going through box_() (Array does that) that encodes something which throws (Float.infinity does that). - // When reproducing, this will cause a test failure via fatalError(). - _ = try? JSONEncoder().encode(ReferencingEncoderWrapper([Float.infinity])) - } - - func testEncoderStateThrowOnEncodeCustomDate() { - // This test is identical to testEncoderStateThrowOnEncode, except throwing via a custom Date closure. - struct ReferencingEncoderWrapper : Encodable { - let value: T - init(_ value: T) { self.value = value } - func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - let superEncoder = container.superEncoder() - var nestedContainer = superEncoder.unkeyedContainer() - try nestedContainer.encode(value) - } - } - - // The closure needs to push a container before throwing an error to trigger. - let encoder = JSONEncoder() - encoder.dateEncodingStrategy = .custom({ _, encoder in - let _ = encoder.unkeyedContainer() - enum CustomError : Error { case foo } - throw CustomError.foo - }) - - _ = try? encoder.encode(ReferencingEncoderWrapper(Date())) - } - - func testEncoderStateThrowOnEncodeCustomData() { - // This test is identical to testEncoderStateThrowOnEncode, except throwing via a custom Data closure. - struct ReferencingEncoderWrapper : Encodable { - let value: T - init(_ value: T) { self.value = value } - func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - let superEncoder = container.superEncoder() - var nestedContainer = superEncoder.unkeyedContainer() - try nestedContainer.encode(value) - } - } - - // The closure needs to push a container before throwing an error to trigger. - let encoder = JSONEncoder() - encoder.dataEncodingStrategy = .custom({ _, encoder in - let _ = encoder.unkeyedContainer() - enum CustomError : Error { case foo } - throw CustomError.foo - }) - - _ = try? encoder.encode(ReferencingEncoderWrapper(Data())) - } - - // MARK: - Decoder State - // SR-6048 - func testDecoderStateThrowOnDecode() { - // The container stack here starts as [[1,2,3]]. Attempting to decode as [String] matches the outer layer (Array), and begins decoding the array. - // Once Array decoding begins, 1 is pushed onto the container stack ([[1,2,3], 1]), and 1 is attempted to be decoded as String. This throws a .typeMismatch, but the container is not popped off the stack. - // When attempting to decode [Int], the container stack is still ([[1,2,3], 1]), and 1 fails to decode as [Int]. - let json = "[1,2,3]".data(using: .utf8)! - let _ = try! JSONDecoder().decode(EitherDecodable<[String], [Int]>.self, from: json) - } - - func testDecoderStateThrowOnDecodeCustomDate() { - // This test is identical to testDecoderStateThrowOnDecode, except we're going to fail because our closure throws an error, not because we hit a type mismatch. - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .custom({ decoder in - enum CustomError : Error { case foo } - throw CustomError.foo - }) - - let json = "1".data(using: .utf8)! - let _ = try! decoder.decode(EitherDecodable.self, from: json) - } - - func testDecoderStateThrowOnDecodeCustomData() { - // This test is identical to testDecoderStateThrowOnDecode, except we're going to fail because our closure throws an error, not because we hit a type mismatch. - let decoder = JSONDecoder() - decoder.dataDecodingStrategy = .custom({ decoder in - enum CustomError : Error { case foo } - throw CustomError.foo - }) - - let json = "1".data(using: .utf8)! - let _ = try! decoder.decode(EitherDecodable.self, from: json) - } - - // MARK: - Helper Functions - private var _jsonEmptyDictionary: Data { - return "{}".data(using: .utf8)! - } - - private func _testEncodeFailure(of value: T) { - do { - let _ = try JSONEncoder().encode(value) - expectUnreachable("Encode of top-level \(T.self) was expected to fail.") - } catch {} - } - - private func _testRoundTrip(of value: T, - expectedJSON json: Data? = nil, - outputFormatting: JSONEncoder.OutputFormatting = [], - dateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .deferredToDate, - dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, - dataEncodingStrategy: JSONEncoder.DataEncodingStrategy = .base64, - dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .base64, - keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy = .useDefaultKeys, - keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys, - nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy = .throw, - nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .throw) where T : Codable, T : Equatable { - var payload: Data! = nil - do { - let encoder = JSONEncoder() - encoder.outputFormatting = outputFormatting - encoder.dateEncodingStrategy = dateEncodingStrategy - encoder.dataEncodingStrategy = dataEncodingStrategy - encoder.nonConformingFloatEncodingStrategy = nonConformingFloatEncodingStrategy - encoder.keyEncodingStrategy = keyEncodingStrategy - payload = try encoder.encode(value) - } catch { - expectUnreachable("Failed to encode \(T.self) to JSON: \(error)") - } - - if let expectedJSON = json { - expectEqual(expectedJSON, payload, "Produced JSON not identical to expected JSON.") - } - - do { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = dateDecodingStrategy - decoder.dataDecodingStrategy = dataDecodingStrategy - decoder.nonConformingFloatDecodingStrategy = nonConformingFloatDecodingStrategy - decoder.keyDecodingStrategy = keyDecodingStrategy - let decoded = try decoder.decode(T.self, from: payload) - expectEqual(decoded, value, "\(T.self) did not round-trip to an equal value.") - } catch { - expectUnreachable("Failed to decode \(T.self) from JSON: \(error)") - } - } - - private func _testRoundTripTypeCoercionFailure(of value: T, as type: U.Type) where T : Codable, U : Codable { - do { - let data = try JSONEncoder().encode(value) - let _ = try JSONDecoder().decode(U.self, from: data) - expectUnreachable("Coercion from \(T.self) to \(U.self) was expected to fail.") - } catch {} - } -} - -// MARK: - Helper Global Functions -func expectEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String) { - if lhs.count != rhs.count { - expectUnreachable("\(prefix) [CodingKey].count mismatch: \(lhs.count) != \(rhs.count)") - return - } - - for (key1, key2) in zip(lhs, rhs) { - switch (key1.intValue, key2.intValue) { - case (.none, .none): break - case (.some(let i1), .none): - expectUnreachable("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != nil") - return - case (.none, .some(let i2)): - expectUnreachable("\(prefix) CodingKey.intValue mismatch: nil != \(type(of: key2))(\(i2))") - return - case (.some(let i1), .some(let i2)): - guard i1 == i2 else { - expectUnreachable("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != \(type(of: key2))(\(i2))") - return - } - } - - expectEqual(key1.stringValue, key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')") - } -} - -// MARK: - Test Types -/* FIXME: Import from %S/Inputs/Coding/SharedTypes.swift somehow. */ - -// MARK: - Empty Types -fileprivate struct EmptyStruct : Codable, Equatable { - static func ==(_ lhs: EmptyStruct, _ rhs: EmptyStruct) -> Bool { - return true - } -} - -fileprivate class EmptyClass : Codable, Equatable { - static func ==(_ lhs: EmptyClass, _ rhs: EmptyClass) -> Bool { - return true - } -} - -// MARK: - Single-Value Types -/// A simple on-off switch type that encodes as a single Bool value. -fileprivate enum Switch : Codable { - case off - case on - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - switch try container.decode(Bool.self) { - case false: self = .off - case true: self = .on - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .off: try container.encode(false) - case .on: try container.encode(true) - } - } -} - -/// A simple timestamp type that encodes as a single Double value. -fileprivate struct Timestamp : Codable, Equatable { - let value: Double - - init(_ value: Double) { - self.value = value - } - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - value = try container.decode(Double.self) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.value) - } - - static func ==(_ lhs: Timestamp, _ rhs: Timestamp) -> Bool { - return lhs.value == rhs.value - } -} - -/// A simple referential counter type that encodes as a single Int value. -fileprivate final class Counter : Codable, Equatable { - var count: Int = 0 - - init() {} - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - count = try container.decode(Int.self) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.count) - } - - static func ==(_ lhs: Counter, _ rhs: Counter) -> Bool { - return lhs === rhs || lhs.count == rhs.count - } -} - -// MARK: - Structured Types -/// A simple address type that encodes as a dictionary of values. -fileprivate struct Address : Codable, Equatable { - let street: String - let city: String - let state: String - let zipCode: Int - let country: String - - init(street: String, city: String, state: String, zipCode: Int, country: String) { - self.street = street - self.city = city - self.state = state - self.zipCode = zipCode - self.country = country - } - - static func ==(_ lhs: Address, _ rhs: Address) -> Bool { - return lhs.street == rhs.street && - lhs.city == rhs.city && - lhs.state == rhs.state && - lhs.zipCode == rhs.zipCode && - lhs.country == rhs.country - } - - static var testValue: Address { - return Address(street: "1 Infinite Loop", - city: "Cupertino", - state: "CA", - zipCode: 95014, - country: "United States") - } -} - -/// A simple person class that encodes as a dictionary of values. -fileprivate class Person : Codable, Equatable { - let name: String - let email: String - let website: URL? - - init(name: String, email: String, website: URL? = nil) { - self.name = name - self.email = email - self.website = website - } - - func isEqual(_ other: Person) -> Bool { - return self.name == other.name && - self.email == other.email && - self.website == other.website - } - - static func ==(_ lhs: Person, _ rhs: Person) -> Bool { - return lhs.isEqual(rhs) - } - - class var testValue: Person { - return Person(name: "Johnny Appleseed", email: "appleseed@apple.com") - } -} - -/// A class which shares its encoder and decoder with its superclass. -fileprivate class Employee : Person { - let id: Int - - init(name: String, email: String, website: URL? = nil, id: Int) { - self.id = id - super.init(name: name, email: email, website: website) - } - - enum CodingKeys : String, CodingKey { - case id - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - id = try container.decode(Int.self, forKey: .id) - try super.init(from: decoder) - } - - override func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(id, forKey: .id) - try super.encode(to: encoder) - } - - override func isEqual(_ other: Person) -> Bool { - if let employee = other as? Employee { - guard self.id == employee.id else { return false } - } - - return super.isEqual(other) - } - - override class var testValue: Employee { - return Employee(name: "Johnny Appleseed", email: "appleseed@apple.com", id: 42) - } -} - -/// A simple company struct which encodes as a dictionary of nested values. -fileprivate struct Company : Codable, Equatable { - let address: Address - var employees: [Employee] - - init(address: Address, employees: [Employee]) { - self.address = address - self.employees = employees - } - - static func ==(_ lhs: Company, _ rhs: Company) -> Bool { - return lhs.address == rhs.address && lhs.employees == rhs.employees - } - - static var testValue: Company { - return Company(address: Address.testValue, employees: [Employee.testValue]) - } -} - -/// An enum type which decodes from Bool?. -fileprivate enum EnhancedBool : Codable { - case `true` - case `false` - case fileNotFound - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if container.decodeNil() { - self = .fileNotFound - } else { - let value = try container.decode(Bool.self) - self = value ? .true : .false - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .true: try container.encode(true) - case .false: try container.encode(false) - case .fileNotFound: try container.encodeNil() - } - } -} - -/// A type which encodes as an array directly through a single value container. -struct Numbers : Codable, Equatable { - let values = [4, 8, 15, 16, 23, 42] - - init() {} - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let decodedValues = try container.decode([Int].self) - guard decodedValues == values else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "The Numbers are wrong!")) - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(values) - } - - static func ==(_ lhs: Numbers, _ rhs: Numbers) -> Bool { - return lhs.values == rhs.values - } - - static var testValue: Numbers { - return Numbers() - } -} - -/// A type which encodes as a dictionary directly through a single value container. -fileprivate final class Mapping : Codable, Equatable { - let values: [String : URL] - - init(values: [String : URL]) { - self.values = values - } - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - values = try container.decode([String : URL].self) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(values) - } - - static func ==(_ lhs: Mapping, _ rhs: Mapping) -> Bool { - return lhs === rhs || lhs.values == rhs.values - } - - static var testValue: Mapping { - return Mapping(values: ["Apple": URL(string: "http://apple.com")!, - "localhost": URL(string: "http://127.0.0.1")!]) - } -} - -struct NestedContainersTestType : Encodable { - let testSuperEncoder: Bool - - init(testSuperEncoder: Bool = false) { - self.testSuperEncoder = testSuperEncoder - } - - enum TopLevelCodingKeys : Int, CodingKey { - case a - case b - case c - } - - enum IntermediateCodingKeys : Int, CodingKey { - case one - case two - } - - func encode(to encoder: Encoder) throws { - if self.testSuperEncoder { - var topLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self) - expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(topLevelContainer.codingPath, [], "New first-level keyed container has non-empty codingPath.") - - let superEncoder = topLevelContainer.superEncoder(forKey: .a) - expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(topLevelContainer.codingPath, [], "First-level keyed container's codingPath changed.") - expectEqualPaths(superEncoder.codingPath, [TopLevelCodingKeys.a], "New superEncoder had unexpected codingPath.") - _testNestedContainers(in: superEncoder, baseCodingPath: [TopLevelCodingKeys.a]) - } else { - _testNestedContainers(in: encoder, baseCodingPath: []) - } - } - - func _testNestedContainers(in encoder: Encoder, baseCodingPath: [CodingKey]) { - expectEqualPaths(encoder.codingPath, baseCodingPath, "New encoder has non-empty codingPath.") - - // codingPath should not change upon fetching a non-nested container. - var firstLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "New first-level keyed container has non-empty codingPath.") - - // Nested Keyed Container - do { - // Nested container for key should have a new key pushed on. - var secondLevelContainer = firstLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self, forKey: .a) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "New second-level keyed container had unexpected codingPath.") - - // Inserting a keyed container should not change existing coding paths. - let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self, forKey: .one) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.one], "New third-level keyed container had unexpected codingPath.") - - // Inserting an unkeyed container should not change existing coding paths. - let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer(forKey: .two) - expectEqualPaths(encoder.codingPath, baseCodingPath + [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath + [], "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.two], "New third-level unkeyed container had unexpected codingPath.") - } - - // Nested Unkeyed Container - do { - // Nested container for key should have a new key pushed on. - var secondLevelContainer = firstLevelContainer.nestedUnkeyedContainer(forKey: .b) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "New second-level keyed container had unexpected codingPath.") - - // Appending a keyed container should not change existing coding paths. - let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 0)], "New third-level keyed container had unexpected codingPath.") - - // Appending an unkeyed container should not change existing coding paths. - let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer() - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 1)], "New third-level unkeyed container had unexpected codingPath.") - } - } -} - -// MARK: - Helper Types - -/// A key type which can take on any string or integer value. -/// This needs to mirror _JSONKey. -fileprivate struct _TestKey : CodingKey { - var stringValue: String - var intValue: Int? - - init?(stringValue: String) { - self.stringValue = stringValue - self.intValue = nil - } - - init?(intValue: Int) { - self.stringValue = "\(intValue)" - self.intValue = intValue - } - - init(index: Int) { - self.stringValue = "Index \(index)" - self.intValue = index - } -} - -fileprivate struct FloatNaNPlaceholder : Codable, Equatable { - init() {} - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(Float.nan) - } - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let float = try container.decode(Float.self) - if !float.isNaN { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Couldn't decode NaN.")) - } - } - - static func ==(_ lhs: FloatNaNPlaceholder, _ rhs: FloatNaNPlaceholder) -> Bool { - return true - } -} - -fileprivate struct DoubleNaNPlaceholder : Codable, Equatable { - init() {} - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(Double.nan) - } - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let double = try container.decode(Double.self) - if !double.isNaN { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Couldn't decode NaN.")) - } - } - - static func ==(_ lhs: DoubleNaNPlaceholder, _ rhs: DoubleNaNPlaceholder) -> Bool { - return true - } -} - -fileprivate enum EitherDecodable : Decodable { - case t(T) - case u(U) - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - do { - self = .t(try container.decode(T.self)) - } catch { - self = .u(try container.decode(U.self)) - } - } -} - -// MARK: - Run Tests - -#if !FOUNDATION_XCTEST -var JSONEncoderTests = TestSuite("TestJSONEncoder") -JSONEncoderTests.test("testEncodingTopLevelEmptyStruct") { TestJSONEncoder().testEncodingTopLevelEmptyStruct() } -JSONEncoderTests.test("testEncodingTopLevelEmptyClass") { TestJSONEncoder().testEncodingTopLevelEmptyClass() } -JSONEncoderTests.test("testEncodingTopLevelSingleValueEnum") { TestJSONEncoder().testEncodingTopLevelSingleValueEnum() } -JSONEncoderTests.test("testEncodingTopLevelSingleValueStruct") { TestJSONEncoder().testEncodingTopLevelSingleValueStruct() } -JSONEncoderTests.test("testEncodingTopLevelSingleValueClass") { TestJSONEncoder().testEncodingTopLevelSingleValueClass() } -JSONEncoderTests.test("testEncodingTopLevelStructuredStruct") { TestJSONEncoder().testEncodingTopLevelStructuredStruct() } -JSONEncoderTests.test("testEncodingTopLevelStructuredClass") { TestJSONEncoder().testEncodingTopLevelStructuredClass() } -JSONEncoderTests.test("testEncodingTopLevelStructuredSingleStruct") { TestJSONEncoder().testEncodingTopLevelStructuredSingleStruct() } -JSONEncoderTests.test("testEncodingTopLevelStructuredSingleClass") { TestJSONEncoder().testEncodingTopLevelStructuredSingleClass() } -JSONEncoderTests.test("testEncodingTopLevelDeepStructuredType") { TestJSONEncoder().testEncodingTopLevelDeepStructuredType()} -JSONEncoderTests.test("testEncodingClassWhichSharesEncoderWithSuper") { TestJSONEncoder().testEncodingClassWhichSharesEncoderWithSuper() } -JSONEncoderTests.test("testEncodingTopLevelNullableType") { TestJSONEncoder().testEncodingTopLevelNullableType() } -JSONEncoderTests.test("testEncodingMultipleNestedContainersWithTheSameTopLevelKey") { TestJSONEncoder().testEncodingMultipleNestedContainersWithTheSameTopLevelKey() } -JSONEncoderTests.test("testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey") { - expectCrash() { - TestJSONEncoder().testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() - } -} -JSONEncoderTests.test("testEncodingOutputFormattingDefault") { TestJSONEncoder().testEncodingOutputFormattingDefault() } -JSONEncoderTests.test("testEncodingOutputFormattingPrettyPrinted") { TestJSONEncoder().testEncodingOutputFormattingPrettyPrinted() } -JSONEncoderTests.test("testEncodingOutputFormattingSortedKeys") { TestJSONEncoder().testEncodingOutputFormattingSortedKeys() } -JSONEncoderTests.test("testEncodingOutputFormattingPrettyPrintedSortedKeys") { TestJSONEncoder().testEncodingOutputFormattingPrettyPrintedSortedKeys() } -// disabled for now due to a Date bug rdar://52618414 -// JSONEncoderTests.test("testEncodingDate") { TestJSONEncoder().testEncodingDate() } -JSONEncoderTests.test("testEncodingDateSecondsSince1970") { TestJSONEncoder().testEncodingDateSecondsSince1970() } -JSONEncoderTests.test("testEncodingDateMillisecondsSince1970") { TestJSONEncoder().testEncodingDateMillisecondsSince1970() } -JSONEncoderTests.test("testEncodingDateISO8601") { TestJSONEncoder().testEncodingDateISO8601() } -JSONEncoderTests.test("testEncodingDateFormatted") { TestJSONEncoder().testEncodingDateFormatted() } -JSONEncoderTests.test("testEncodingDateCustom") { TestJSONEncoder().testEncodingDateCustom() } -JSONEncoderTests.test("testEncodingDateCustomEmpty") { TestJSONEncoder().testEncodingDateCustomEmpty() } -JSONEncoderTests.test("testEncodingData") { TestJSONEncoder().testEncodingData() } -JSONEncoderTests.test("testEncodingDataBase64") { TestJSONEncoder().testEncodingDataBase64() } -JSONEncoderTests.test("testEncodingDataCustom") { TestJSONEncoder().testEncodingDataCustom() } -JSONEncoderTests.test("testEncodingDataCustomEmpty") { TestJSONEncoder().testEncodingDataCustomEmpty() } -JSONEncoderTests.test("testEncodingNonConformingFloats") { TestJSONEncoder().testEncodingNonConformingFloats() } -JSONEncoderTests.test("testEncodingNonConformingFloatStrings") { TestJSONEncoder().testEncodingNonConformingFloatStrings() } -JSONEncoderTests.test("testEncodingKeyStrategySnake") { TestJSONEncoder().testEncodingKeyStrategySnake() } -JSONEncoderTests.test("testEncodingKeyStrategyCustom") { TestJSONEncoder().testEncodingKeyStrategyCustom() } -JSONEncoderTests.test("testEncodingDictionaryStringKeyConversionUntouched") { TestJSONEncoder().testEncodingDictionaryStringKeyConversionUntouched() } -JSONEncoderTests.test("testEncodingKeyStrategyPath") { TestJSONEncoder().testEncodingKeyStrategyPath() } -JSONEncoderTests.test("testDecodingKeyStrategyCamel") { TestJSONEncoder().testDecodingKeyStrategyCamel() } -JSONEncoderTests.test("testDecodingKeyStrategyCustom") { TestJSONEncoder().testDecodingKeyStrategyCustom() } -JSONEncoderTests.test("testDecodingDictionaryStringKeyConversionUntouched") { TestJSONEncoder().testDecodingDictionaryStringKeyConversionUntouched() } -JSONEncoderTests.test("testEncodingKeyStrategySnakeGenerated") { TestJSONEncoder().testEncodingKeyStrategySnakeGenerated() } -JSONEncoderTests.test("testDecodingKeyStrategyCamelGenerated") { TestJSONEncoder().testDecodingKeyStrategyCamelGenerated() } -JSONEncoderTests.test("testKeyStrategySnakeGeneratedAndCustom") { TestJSONEncoder().testKeyStrategySnakeGeneratedAndCustom() } -JSONEncoderTests.test("testKeyStrategyDuplicateKeys") { TestJSONEncoder().testKeyStrategyDuplicateKeys() } -JSONEncoderTests.test("testNestedContainerCodingPaths") { TestJSONEncoder().testNestedContainerCodingPaths() } -JSONEncoderTests.test("testSuperEncoderCodingPaths") { TestJSONEncoder().testSuperEncoderCodingPaths() } -JSONEncoderTests.test("testInterceptDecimal") { TestJSONEncoder().testInterceptDecimal() } -JSONEncoderTests.test("testInterceptURL") { TestJSONEncoder().testInterceptURL() } -JSONEncoderTests.test("testInterceptURLWithoutEscapingOption") { TestJSONEncoder().testInterceptURLWithoutEscapingOption() } -JSONEncoderTests.test("testTypeCoercion") { TestJSONEncoder().testTypeCoercion() } -JSONEncoderTests.test("testDecodingConcreteTypeParameter") { TestJSONEncoder().testDecodingConcreteTypeParameter() } -JSONEncoderTests.test("testEncoderStateThrowOnEncode") { TestJSONEncoder().testEncoderStateThrowOnEncode() } -JSONEncoderTests.test("testEncoderStateThrowOnEncodeCustomDate") { TestJSONEncoder().testEncoderStateThrowOnEncodeCustomDate() } -JSONEncoderTests.test("testEncoderStateThrowOnEncodeCustomData") { TestJSONEncoder().testEncoderStateThrowOnEncodeCustomData() } -JSONEncoderTests.test("testDecoderStateThrowOnDecode") { TestJSONEncoder().testDecoderStateThrowOnDecode() } -JSONEncoderTests.test("testDecoderStateThrowOnDecodeCustomDate") { TestJSONEncoder().testDecoderStateThrowOnDecodeCustomDate() } -JSONEncoderTests.test("testDecoderStateThrowOnDecodeCustomData") { TestJSONEncoder().testDecoderStateThrowOnDecodeCustomData() } -JSONEncoderTests.test("testEncodingDictionaryFailureKeyPath") { TestJSONEncoder().testEncodingDictionaryFailureKeyPath() } -JSONEncoderTests.test("testEncodingDictionaryFailureKeyPathNested") { TestJSONEncoder().testEncodingDictionaryFailureKeyPathNested() } -JSONEncoderTests.test("testDecodingDictionaryFailureKeyPath") { TestJSONEncoder().testDecodingDictionaryFailureKeyPath() } -JSONEncoderTests.test("testDecodingDictionaryFailureKeyPathNested") { TestJSONEncoder().testDecodingDictionaryFailureKeyPathNested() } -runAllTests() -#endif diff --git a/test/stdlib/TestLocale.swift b/test/stdlib/TestLocale.swift deleted file mode 100644 index a438cb3668cb2..0000000000000 --- a/test/stdlib/TestLocale.swift +++ /dev/null @@ -1,152 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %empty-directory(%t) -// -// RUN: %target-clang %S/Inputs/FoundationBridge/FoundationBridge.m -c -o %t/FoundationBridgeObjC.o -g -// RUN: %target-build-swift %s -I %S/Inputs/FoundationBridge/ -Xlinker %t/FoundationBridgeObjC.o -o %t/TestLocale -// RUN: %target-codesign %t/TestLocale - -// RUN: %target-run %t/TestLocale > %t.txt -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation -import FoundationBridgeObjC - -#if FOUNDATION_XCTEST - import XCTest - class TestLocaleSuper : XCTestCase { } -#else - import StdlibUnittest - class TestLocaleSuper { } -#endif - -class TestLocale : TestLocaleSuper { - - func test_bridgingAutoupdating() { - let tester = LocaleBridgingTester() - - do { - let loc = Locale.autoupdatingCurrent - let result = tester.verifyAutoupdating(loc) - expectTrue(result) - } - - do { - let loc = tester.autoupdatingCurrentLocale() - let result = tester.verifyAutoupdating(loc) - expectTrue(result) - } - } - - func test_equality() { - let autoupdating = Locale.autoupdatingCurrent - let autoupdating2 = Locale.autoupdatingCurrent - - expectEqual(autoupdating, autoupdating2) - - let current = Locale.current - - expectNotEqual(autoupdating, current) - } - - func test_localizedStringFunctions() { - let locale = Locale(identifier: "en") - - expectEqual("English", locale.localizedString(forIdentifier: "en")) - expectEqual("France", locale.localizedString(forRegionCode: "fr")) - expectEqual("Spanish", locale.localizedString(forLanguageCode: "es")) - expectEqual("Simplified Han", locale.localizedString(forScriptCode: "Hans")) - expectEqual("Computer", locale.localizedString(forVariantCode: "POSIX")) - expectEqual("Buddhist Calendar", locale.localizedString(for: .buddhist)) - expectEqual("US Dollar", locale.localizedString(forCurrencyCode: "USD")) - expectEqual("Phonebook Sort Order", locale.localizedString(forCollationIdentifier: "phonebook")) - // Need to find a good test case for collator identifier - // expectEqual("something", locale.localizedString(forCollatorIdentifier: "en")) - } - - func test_properties() { - let locale = Locale(identifier: "zh-Hant-HK") - - expectEqual("zh-Hant-HK", locale.identifier) - expectEqual("zh", locale.languageCode) - expectEqual("HK", locale.regionCode) - expectEqual("Hant", locale.scriptCode) - expectEqual("POSIX", Locale(identifier: "en_POSIX").variantCode) - expectTrue(locale.exemplarCharacterSet != nil) - // The calendar we get back from Locale has the locale set, but not the one we create with Calendar(identifier:). So we configure our comparison calendar first. - var c = Calendar(identifier: .gregorian) - c.locale = Locale(identifier: "en_US") - expectEqual(c, Locale(identifier: "en_US").calendar) - expectEqual("「", locale.quotationBeginDelimiter) - expectEqual("」", locale.quotationEndDelimiter) - expectEqual("『", locale.alternateQuotationBeginDelimiter) - expectEqual("』", locale.alternateQuotationEndDelimiter) - expectEqual("phonebook", Locale(identifier: "en_US@collation=phonebook").collationIdentifier) - expectEqual(".", locale.decimalSeparator) - - - expectEqual(".", locale.decimalSeparator) - expectEqual(",", locale.groupingSeparator) - if #available(macOS 10.11, *) { - expectEqual("HK$", locale.currencySymbol) - } - expectEqual("HKD", locale.currencyCode) - - expectTrue(Locale.availableIdentifiers.count > 0) - expectTrue(Locale.isoLanguageCodes.count > 0) - expectTrue(Locale.isoRegionCodes.count > 0) - expectTrue(Locale.isoCurrencyCodes.count > 0) - expectTrue(Locale.commonISOCurrencyCodes.count > 0) - - expectTrue(Locale.preferredLanguages.count > 0) - - // Need to find a good test case for collator identifier - // expectEqual("something", locale.collatorIdentifier) - } - - func test_AnyHashableContainingLocale() { - let values: [Locale] = [ - Locale(identifier: "en"), - Locale(identifier: "uk"), - Locale(identifier: "uk"), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Locale.self, type(of: anyHashables[0].base)) - expectEqual(Locale.self, type(of: anyHashables[1].base)) - expectEqual(Locale.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSLocale() { - let values: [NSLocale] = [ - NSLocale(localeIdentifier: "en"), - NSLocale(localeIdentifier: "uk"), - NSLocale(localeIdentifier: "uk"), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Locale.self, type(of: anyHashables[0].base)) - expectEqual(Locale.self, type(of: anyHashables[1].base)) - expectEqual(Locale.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } -} - -#if !FOUNDATION_XCTEST -var LocaleTests = TestSuite("TestLocale") -LocaleTests.test("test_bridgingAutoupdating") { TestLocale().test_bridgingAutoupdating() } -LocaleTests.test("test_equality") { TestLocale().test_equality() } -LocaleTests.test("test_localizedStringFunctions") { TestLocale().test_localizedStringFunctions() } -LocaleTests.test("test_properties") { TestLocale().test_properties() } -LocaleTests.test("test_AnyHashableContainingLocale") { TestLocale().test_AnyHashableContainingLocale() } -LocaleTests.test("test_AnyHashableCreatedFromNSLocale") { TestLocale().test_AnyHashableCreatedFromNSLocale() } -runAllTests() -#endif diff --git a/test/stdlib/TestMeasurement.swift b/test/stdlib/TestMeasurement.swift deleted file mode 100644 index ca39f49dc1086..0000000000000 --- a/test/stdlib/TestMeasurement.swift +++ /dev/null @@ -1,244 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST - import XCTest - class TestMeasurementSuper : XCTestCase { } -#else - import StdlibUnittest - class TestMeasurementSuper { } -#endif - -// We define our own units here so that we can have closer control over checking the behavior of just struct Measurement and not the rest of Foundation -@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -class MyDimensionalUnit : Dimension { - class var unitA : MyDimensionalUnit { - return MyDimensionalUnit(symbol: "a", converter: UnitConverterLinear(coefficient: 1)) - } - class var unitKiloA : MyDimensionalUnit { - return MyDimensionalUnit(symbol: "ka", converter: UnitConverterLinear(coefficient: 1_000)) - } - class var unitMegaA : MyDimensionalUnit { - return MyDimensionalUnit(symbol: "Ma", converter: UnitConverterLinear(coefficient: 1_000_000)) - } - override class func baseUnit() -> MyDimensionalUnit { - return MyDimensionalUnit.unitA - } -} - -@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -class CustomUnit : Unit { - override init(symbol: String) { - super.init(symbol: symbol) - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - public static let bugs = CustomUnit(symbol: "bug") - public static let features = CustomUnit(symbol: "feature") -} - -@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -class TestMeasurement : TestMeasurementSuper { - - func testBasicConstruction() { - let m1 = Measurement(value: 3, unit: MyDimensionalUnit.unitA) - let m2 = Measurement(value: 3, unit: MyDimensionalUnit.unitA) - - let m3 = m1 + m2 - - expectEqual(6, m3.value) - expectEqual(m1, m2) - - let m10 = Measurement(value: 2, unit: CustomUnit.bugs) - let m11 = Measurement(value: 2, unit: CustomUnit.bugs) - let m12 = Measurement(value: 3, unit: CustomUnit.bugs) - - expectEqual(m10, m11) - expectNotEqual(m10, m12) - - // This test has 2 + 2 + 3 bugs - expectEqual((m10 + m11 + m12).value, 7) - } - - func testConversion() { - let m1 = Measurement(value: 1000, unit: MyDimensionalUnit.unitA) - let kiloM1 = Measurement(value: 1, unit: MyDimensionalUnit.unitKiloA) - - let result = m1.converted(to: MyDimensionalUnit.unitKiloA) - expectEqual(kiloM1, result) - - let sameResult = m1.converted(to: MyDimensionalUnit.unitA) - expectEqual(sameResult, m1) - - // This correctly fails to build - - // let m2 = Measurement(value: 1, unit: CustomUnit.bugs) - // m2.converted(to: MyDimensionalUnit.unitKiloA) - } - - func testOperators() { - // Which is bigger: 1 ka or 1 Ma? - let oneKiloA = Measurement(value: 1, unit: MyDimensionalUnit.unitKiloA) - let oneMegaA = Measurement(value: 1, unit: MyDimensionalUnit.unitMegaA) - - expectTrue(oneKiloA < oneMegaA) - expectFalse(oneKiloA > oneMegaA) - expectTrue(oneKiloA * 2000 > oneMegaA) - expectTrue(oneMegaA / 1_000_000 < oneKiloA) - expectTrue(2000 * oneKiloA > oneMegaA) - expectTrue(2 / oneMegaA > oneMegaA) - expectEqual(2 / (oneMegaA * 2), oneMegaA) - expectTrue(oneMegaA <= oneKiloA * 1000) - expectTrue(oneMegaA - oneKiloA <= oneKiloA * 1000) - expectTrue(oneMegaA >= oneKiloA * 1000) - expectTrue(oneMegaA >= ((oneKiloA * 1000) - oneKiloA)) - - // Dynamically different dimensions - expectEqual(Measurement(value: 1_001_000, unit: MyDimensionalUnit.unitA), oneMegaA + oneKiloA) - - var bugCount = Measurement(value: 1, unit: CustomUnit.bugs) - expectEqual(bugCount.value, 1) - bugCount = bugCount + Measurement(value: 4, unit: CustomUnit.bugs) - expectEqual(bugCount.value, 5) - } - - func testUnits() { - expectEqual(MyDimensionalUnit.unitA, MyDimensionalUnit.unitA) - expectTrue(MyDimensionalUnit.unitA == MyDimensionalUnit.unitA) - } - - func testMeasurementFormatter() { - let formatter = MeasurementFormatter() - let measurement = Measurement(value: 100, unit: UnitLength.kilometers) - let result = formatter.string(from: measurement) - - // Just make sure we get a result at all here - expectFalse(result.isEmpty) - } - - func testEquality() { - let fiveKM = Measurement(value: 5, unit: UnitLength.kilometers) - let fiveSeconds = Measurement(value: 5, unit: UnitDuration.seconds) - let fiveThousandM = Measurement(value: 5000, unit: UnitLength.meters) - - expectTrue(fiveKM == fiveThousandM) - expectEqual(fiveKM, fiveThousandM) - expectFalse(fiveKM == fiveSeconds) - } - - func testComparison() { - let fiveKM = Measurement(value: 5, unit: UnitLength.kilometers) - let fiveThousandM = Measurement(value: 5000, unit: UnitLength.meters) - let sixKM = Measurement(value: 6, unit: UnitLength.kilometers) - let sevenThousandM = Measurement(value: 7000, unit: UnitLength.meters) - - expectTrue(fiveKM < sixKM) - expectTrue(fiveKM < sevenThousandM) - expectTrue(fiveKM <= fiveThousandM) - } - - func testHashing() { - guard #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) else { return } - let lengths: [[Measurement]] = [ - [ - Measurement(value: 5, unit: UnitLength.kilometers), - Measurement(value: 5000, unit: UnitLength.meters), - Measurement(value: 5000, unit: UnitLength.meters), - ], - [ - Measurement(value: 1, unit: UnitLength.kilometers), - Measurement(value: 1000, unit: UnitLength.meters), - ], - [ - Measurement(value: 1, unit: UnitLength.meters), - Measurement(value: 1000, unit: UnitLength.millimeters), - ], - ] - checkHashableGroups(lengths) - - let durations: [[Measurement]] = [ - [ - Measurement(value: 3600, unit: UnitDuration.seconds), - Measurement(value: 60, unit: UnitDuration.minutes), - Measurement(value: 1, unit: UnitDuration.hours), - ], - [ - Measurement(value: 1800, unit: UnitDuration.seconds), - Measurement(value: 30, unit: UnitDuration.minutes), - Measurement(value: 0.5, unit: UnitDuration.hours), - ] - ] - checkHashableGroups(durations) - - let custom: [Measurement] = [ - Measurement(value: 1, unit: CustomUnit.bugs), - Measurement(value: 2, unit: CustomUnit.bugs), - Measurement(value: 3, unit: CustomUnit.bugs), - Measurement(value: 4, unit: CustomUnit.bugs), - Measurement(value: 1, unit: CustomUnit.features), - Measurement(value: 2, unit: CustomUnit.features), - Measurement(value: 3, unit: CustomUnit.features), - Measurement(value: 4, unit: CustomUnit.features), - ] - checkHashable(custom, equalityOracle: { $0 == $1 }) - } - - func test_AnyHashableContainingMeasurement() { - let values: [Measurement] = [ - Measurement(value: 100, unit: UnitLength.meters), - Measurement(value: 100, unit: UnitLength.kilometers), - Measurement(value: 100, unit: UnitLength.kilometers), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Measurement.self, type(of: anyHashables[0].base)) - expectEqual(Measurement.self, type(of: anyHashables[1].base)) - expectEqual(Measurement.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSMeasurement() { - let values: [NSMeasurement] = [ - NSMeasurement(doubleValue: 100, unit: UnitLength.meters), - NSMeasurement(doubleValue: 100, unit: UnitLength.kilometers), - NSMeasurement(doubleValue: 100, unit: UnitLength.kilometers), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Measurement.self, type(of: anyHashables[0].base)) - expectEqual(Measurement.self, type(of: anyHashables[1].base)) - expectEqual(Measurement.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } -} - -#if !FOUNDATION_XCTEST -if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { - let MeasurementTests = TestSuite("TestMeasurement") - MeasurementTests.test("testBasicConstruction") { TestMeasurement().testBasicConstruction() } - MeasurementTests.test("testConversion") { TestMeasurement().testConversion() } - MeasurementTests.test("testOperators") { TestMeasurement().testOperators() } - MeasurementTests.test("testUnits") { TestMeasurement().testUnits() } - MeasurementTests.test("testMeasurementFormatter") { TestMeasurement().testMeasurementFormatter() } - MeasurementTests.test("testEquality") { TestMeasurement().testEquality() } - MeasurementTests.test("testComparison") { TestMeasurement().testComparison() } - MeasurementTests.test("testHashing") { TestMeasurement().testHashing() } - MeasurementTests.test("test_AnyHashableContainingMeasurement") { TestMeasurement().test_AnyHashableContainingMeasurement() } - MeasurementTests.test("test_AnyHashableCreatedFromNSMeasurement") { TestMeasurement().test_AnyHashableCreatedFromNSMeasurement() } - runAllTests() -} -#endif diff --git a/test/stdlib/TestNSNumberBridging.swift b/test/stdlib/TestNSNumberBridging.swift index 2ec91b6d0e51d..d632e3d9a31db 100644 --- a/test/stdlib/TestNSNumberBridging.swift +++ b/test/stdlib/TestNSNumberBridging.swift @@ -14,7 +14,7 @@ // // RUN: %target-clang %S/Inputs/FoundationBridge/FoundationBridge.m -c -o %t/FoundationBridgeObjC.o -g // RUN: %target-build-swift %s -I %S/Inputs/FoundationBridge/ -Xlinker %t/FoundationBridgeObjC.o -o %t/TestNSNumberBridging -// +// RUN: %target-codesign %t/TestNSNumberBridging // RUN: %target-run %t/TestNSNumberBridging // REQUIRES: executable_test // REQUIRES: objc_interop diff --git a/test/stdlib/TestNSRange.swift b/test/stdlib/TestNSRange.swift deleted file mode 100644 index 3196ef9b0b7ee..0000000000000 --- a/test/stdlib/TestNSRange.swift +++ /dev/null @@ -1,164 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestNSRangeSuper : XCTestCase { } -#else -import StdlibUnittest -class TestNSRangeSuper { } -#endif - -class TestNSRange : TestNSRangeSuper { - func testEquality() { - let r1 = NSRange(location: 1, length: 10) - let r2 = NSRange(location: 1, length: 11) - let r3 = NSRange(location: 2, length: 10) - let r4 = NSRange(location: 1, length: 10) - let r5 = NSRange(location: NSNotFound, length: 0) - let r6 = NSRange(location: NSNotFound, length: 2) - - expectNotEqual(r1, r2) - expectNotEqual(r1, r3) - expectEqual(r1, r4) - expectNotEqual(r1, r5) - expectNotEqual(r5, r6) - } - - func testDescription() { - let r1 = NSRange(location: 0, length: 22) - let r2 = NSRange(location: 10, length: 22) - let r3 = NSRange(location: NSNotFound, length: 0) - let r4 = NSRange(location: NSNotFound, length: 22) - expectEqual("{0, 22}", r1.description) - expectEqual("{10, 22}", r2.description) - expectEqual("{\(NSNotFound), 0}", r3.description) - expectEqual("{\(NSNotFound), 22}", r4.description) - - expectEqual("{0, 22}", r1.debugDescription) - expectEqual("{10, 22}", r2.debugDescription) - expectEqual("{NSNotFound, 0}", r3.debugDescription) - expectEqual("{NSNotFound, 22}", r4.debugDescription) - } - - func testCreationFromString() { - let r1 = NSRange("") - expectNil(r1) - let r2 = NSRange("1") - expectNil(r2) - let r3 = NSRange("1 2") - expectEqual(NSRange(location: 1, length: 2), r3) - let r4 = NSRange("{1 8") - expectEqual(NSRange(location: 1, length: 8), r4) - let r5 = NSRange("1.8") - expectNil(r5) - let r6 = NSRange("1-9") - expectEqual(NSRange(location: 1, length: 9), r6) - let r7 = NSRange("{1,9}") - expectEqual(NSRange(location: 1, length: 9), r7) - let r8 = NSRange("{1,9}asdfasdf") - expectEqual(NSRange(location: 1, length: 9), r8) - let r9 = NSRange("{1,9}{2,7}") - expectEqual(NSRange(location: 1, length: 9), r9) - let r10 = NSRange("{1,9}") - expectEqual(NSRange(location: 1, length: 9), r10) - let r11 = NSRange("{1.0,9}") - expectEqual(NSRange(location: 1, length: 9), r11) - let r12 = NSRange("{1,9.0}") - expectEqual(NSRange(location: 1, length: 9), r12) - let r13 = NSRange("{1.2,9}") - expectNil(r13) - let r14 = NSRange("{1,9.8}") - expectNil(r14) - } - - func testHashing() { - let large = Int.max >> 2 - let samples: [NSRange] = [ - NSRange(location: 1, length: 1), - NSRange(location: 1, length: 2), - NSRange(location: 2, length: 1), - NSRange(location: 2, length: 2), - NSRange(location: large, length: large), - NSRange(location: 0, length: large), - NSRange(location: large, length: 0), - ] - checkHashable(samples, equalityOracle: { $0 == $1 }) - } - - func testBounding() { - let r1 = NSRange(location: 1000, length: 2222) - expectEqual(r1.location, r1.lowerBound) - expectEqual(r1.location + r1.length, r1.upperBound) - } - - func testContains() { - let r1 = NSRange(location: 1000, length: 2222) - expectFalse(r1.contains(3)) - expectTrue(r1.contains(1001)) - expectFalse(r1.contains(4000)) - } - - func testUnion() { - let r1 = NSRange(location: 10, length: 20) - let r2 = NSRange(location: 30, length: 5) - let union1 = r1.union(r2) - - expectEqual(Swift.min(r1.lowerBound, r2.lowerBound), union1.lowerBound) - expectEqual(Swift.max(r1.upperBound, r2.upperBound), union1.upperBound) - - let r3 = NSRange(location: 10, length: 20) - let r4 = NSRange(location: 11, length: 5) - let union2 = r3.union(r4) - - expectEqual(Swift.min(r3.lowerBound, r4.lowerBound), union2.lowerBound) - expectEqual(Swift.max(r3.upperBound, r4.upperBound), union2.upperBound) - - let r5 = NSRange(location: 10, length: 20) - let r6 = NSRange(location: 11, length: 29) - let union3 = r5.union(r6) - - expectEqual(Swift.min(r5.lowerBound, r6.upperBound), union3.lowerBound) - expectEqual(Swift.max(r5.upperBound, r6.upperBound), union3.upperBound) - } - - func testIntersection() { - let r1 = NSRange(location: 1, length: 7) - let r2 = NSRange(location: 2, length: 20) - let r3 = NSRange(location: 2, length: 2) - let r4 = NSRange(location: 10, length: 7) - - let intersection1 = r1.intersection(r2) - expectEqual(NSRange(location: 2, length: 6), intersection1) - let intersection2 = r1.intersection(r3) - expectEqual(NSRange(location: 2, length: 2), intersection2) - let intersection3 = r1.intersection(r4) - expectEqual(nil, intersection3) - } -} - -#if !FOUNDATION_XCTEST -var NSRangeTests = TestSuite("TestNSRange") - -NSRangeTests.test("testEquality") { TestNSRange().testEquality() } -NSRangeTests.test("testDescription") { TestNSRange().testDescription() } -NSRangeTests.test("testCreationFromString") { TestNSRange().testCreationFromString() } -NSRangeTests.test("testHashing") { TestNSRange().testHashing() } -NSRangeTests.test("testBounding") { TestNSRange().testBounding() } -NSRangeTests.test("testContains") { TestNSRange().testContains() } -NSRangeTests.test("testUnion") { TestNSRange().testUnion() } -NSRangeTests.test("testIntersection") { TestNSRange().testIntersection() } - -runAllTests() -#endif diff --git a/test/stdlib/TestNSString.swift b/test/stdlib/TestNSString.swift deleted file mode 100644 index 86020ea6fcc41..0000000000000 --- a/test/stdlib/TestNSString.swift +++ /dev/null @@ -1,78 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %empty-directory(%t) -// -// RUN: %target-clang %S/Inputs/FoundationBridge/FoundationBridge.m -c -o %t/FoundationBridgeObjC.o -g -// RUN: %target-build-swift %s -I %S/Inputs/FoundationBridge/ -Xlinker %t/FoundationBridgeObjC.o -sanitize=address -o %t/TestNSString -// RUN: %target-codesign %t/TestNSString - -// RUN: %target-run %t/TestNSString > %t.txt -// REQUIRES: executable_test -// REQUIRES: asan_runtime -// REQUIRES: objc_interop -// REQUIRES: rdar55727144 - -import Foundation -import FoundationBridgeObjC - -#if FOUNDATION_XCTEST - import XCTest - class TestNSStringSuper : XCTestCase { } -#else - import StdlibUnittest - class TestNSStringSuper { } -#endif - -class TestNSString : TestNSStringSuper { - - func test_equalOverflow() { - let cyrillic = "чебурашка@ящик-с-апельсинами.рф" - let other = getNSStringEqualTestString() - print(NSStringBridgeTestEqual(cyrillic, other)) - } - - func test_smallString_BOM() { - let bom = "\u{FEFF}" // U+FEFF (ZERO WIDTH NO-BREAK SPACE) -// expectEqual(1, NSString(string: bom).length) -// expectEqual(4, NSString(string: "\(bom)abc").length) -// expectEqual(5, NSString(string: "\(bom)\(bom)abc").length) -// expectEqual(4, NSString(string: "a\(bom)bc").length) -// expectEqual(13, NSString(string: "\(bom)234567890123").length) -// expectEqual(14, NSString(string: "\(bom)2345678901234").length) - - expectEqual(1, (bom as NSString).length) - expectEqual(4, ("\(bom)abc" as NSString).length) - expectEqual(5, ("\(bom)\(bom)abc" as NSString).length) - expectEqual(4, ("a\(bom)bc" as NSString).length) - expectEqual(13, ("\(bom)234567890123" as NSString).length) - expectEqual(14, ("\(bom)2345678901234" as NSString).length) - - let string = "\(bom)abc" - let middleIndex = string.index(string.startIndex, offsetBy: 2) - string.enumerateSubstrings(in: middleIndex.. %t.txt -// REQUIRES: executable_test -// REQUIRES: objc_interop - - -import Foundation -import FoundationBridgeObjC - -#if FOUNDATION_XCTEST - import XCTest - class TestNotificationSuper : XCTestCase { } -#else - import StdlibUnittest - class TestNotificationSuper { } -#endif - -class TestNotification : TestNotificationSuper { - func test_unconditionallyBridgeFromObjectiveC() { - expectEqual(Notification(name: Notification.Name("")), Notification._unconditionallyBridgeFromObjectiveC(nil)) - } - - func test_hashing() { - guard #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) else { return } - - let o1 = NSObject() - let o2 = NSObject() - let values: [Notification] = [ - /* 0 */ Notification(name: .init("a"), object: o1, userInfo: nil), - /* 1 */ Notification(name: .init("a"), object: o2, userInfo: nil), - /* 2 */ Notification(name: .init("b"), object: o1, userInfo: nil), - /* 3 */ Notification(name: .init("b"), object: o2, userInfo: nil), - /* 4 */ Notification(name: .init("a"), object: o1, userInfo: ["Foo": 1]), - /* 5 */ Notification(name: .init("a"), object: o1, userInfo: ["Foo": 2]), - /* 6 */ Notification(name: .init("a"), object: o1, userInfo: ["Bar": 1]), - /* 7 */ Notification(name: .init("a"), object: o1, userInfo: ["Foo": 1, "Bar": 2]), - ] - - let hashGroups: [Int: Int] = [ - 0: 0, - 1: 0, - 2: 1, - 3: 1, - 4: 2, - 5: 2, - 6: 3, - 7: 4 - ] - - checkHashable( - values, - equalityOracle: { $0 == $1 }, - hashEqualityOracle: { - // FIXME: Unfortunately while we have 8 different notifications, - // three pairs of them have colliding hash encodings. - hashGroups[$0] == hashGroups[$1] - }) - } -} - - -#if !FOUNDATION_XCTEST -var NotificationTests = TestSuite("TestNotification") -NotificationTests.test("test_unconditionallyBridgeFromObjectiveC") { TestNotification().test_unconditionallyBridgeFromObjectiveC() } -NotificationTests.test("test_hashing") { TestNotification().test_hashing() } - -private struct NonHashableValueType: Equatable { - let value: Int - init(_ value: Int) { - self.value = value - } -} - -NotificationTests.test("test_reflexivity_violation") - .xfail( - .custom({ true }, - reason: " Foundation.Notification's equality relation isn't reflexive")) - .code { - let name = Notification.Name("name") - let a = NonHashableValueType(1) - let b = NonHashableValueType(2) - // Currently none of these values compare equal to themselves: - let values: [Notification] = [ - Notification(name: name, object: a, userInfo: nil), - Notification(name: name, object: b, userInfo: nil), - Notification(name: name, object: nil, userInfo: ["foo": a]), - Notification(name: name, object: nil, userInfo: ["foo": b]), - ] - checkHashable(values, equalityOracle: { $0 == $1 }) -} - - -runAllTests() -#endif diff --git a/test/stdlib/TestPersonNameComponents.swift b/test/stdlib/TestPersonNameComponents.swift deleted file mode 100644 index 824fc657cce7a..0000000000000 --- a/test/stdlib/TestPersonNameComponents.swift +++ /dev/null @@ -1,111 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation -import CoreFoundation - -#if FOUNDATION_XCTEST -import XCTest -class TestPersonNameComponentsSuper : XCTestCase { } -#else -import StdlibUnittest -class TestPersonNameComponentsSuper { } -#endif - -class TestPersonNameComponents : TestPersonNameComponentsSuper { - @available(OSX 10.11, iOS 9.0, *) - func makePersonNameComponents(givenName: String, familyName: String) -> PersonNameComponents { - var result = PersonNameComponents() - result.givenName = givenName - result.familyName = familyName - return result - } - - func test_Hashing() { - guard #available(macOS 10.13, iOS 11.0, *) else { - // PersonNameComponents was available in earlier versions, but its - // hashing did not match its definition for equality. - return - } - - let values: [[PersonNameComponents]] = [ - [ - makePersonNameComponents(givenName: "Kevin", familyName: "Frank"), - makePersonNameComponents(givenName: "Kevin", familyName: "Frank"), - ], - [ - makePersonNameComponents(givenName: "John", familyName: "Frank"), - makePersonNameComponents(givenName: "John", familyName: "Frank"), - ], - [ - makePersonNameComponents(givenName: "Kevin", familyName: "Appleseed"), - makePersonNameComponents(givenName: "Kevin", familyName: "Appleseed"), - ], - [ - makePersonNameComponents(givenName: "John", familyName: "Appleseed"), - makePersonNameComponents(givenName: "John", familyName: "Appleseed"), - ] - ] - checkHashableGroups( - values, - // FIXME: PersonNameComponents hashes aren't seeded. - allowIncompleteHashing: true) - } - - func test_AnyHashableContainingPersonNameComponents() { - if #available(OSX 10.11, iOS 9.0, *) { - let values: [PersonNameComponents] = [ - makePersonNameComponents(givenName: "Kevin", familyName: "Frank"), - makePersonNameComponents(givenName: "John", familyName: "Appleseed"), - makePersonNameComponents(givenName: "John", familyName: "Appleseed"), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(PersonNameComponents.self, type(of: anyHashables[0].base)) - expectEqual(PersonNameComponents.self, type(of: anyHashables[1].base)) - expectEqual(PersonNameComponents.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - } - - @available(OSX 10.11, iOS 9.0, *) - func makeNSPersonNameComponents(givenName: String, familyName: String) -> NSPersonNameComponents { - let result = NSPersonNameComponents() - result.givenName = givenName - result.familyName = familyName - return result - } - - func test_AnyHashableCreatedFromNSPersonNameComponents() { - if #available(OSX 10.11, iOS 9.0, *) { - let values: [NSPersonNameComponents] = [ - makeNSPersonNameComponents(givenName: "Kevin", familyName: "Frank"), - makeNSPersonNameComponents(givenName: "John", familyName: "Appleseed"), - makeNSPersonNameComponents(givenName: "John", familyName: "Appleseed"), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(PersonNameComponents.self, type(of: anyHashables[0].base)) - expectEqual(PersonNameComponents.self, type(of: anyHashables[1].base)) - expectEqual(PersonNameComponents.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - } -} - -#if !FOUNDATION_XCTEST -var PersonNameComponentsTests = TestSuite("TestPersonNameComponents") -PersonNameComponentsTests.test("test_Hashing") { TestPersonNameComponents().test_Hashing() } -PersonNameComponentsTests.test("test_AnyHashableContainingPersonNameComponents") { TestPersonNameComponents().test_AnyHashableContainingPersonNameComponents() } -PersonNameComponentsTests.test("test_AnyHashableCreatedFromNSPersonNameComponents") { TestPersonNameComponents().test_AnyHashableCreatedFromNSPersonNameComponents() } -runAllTests() -#endif diff --git a/test/stdlib/TestPlistEncoder.swift b/test/stdlib/TestPlistEncoder.swift deleted file mode 100644 index 59965ea400dc8..0000000000000 --- a/test/stdlib/TestPlistEncoder.swift +++ /dev/null @@ -1,896 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Swift -import Foundation - -// MARK: - Test Suite - -#if FOUNDATION_XCTEST -import XCTest -class TestPropertyListEncoderSuper : XCTestCase { } -#else -import StdlibUnittest -class TestPropertyListEncoderSuper { } -#endif - -class TestPropertyListEncoder : TestPropertyListEncoderSuper { - // MARK: - Encoding Top-Level Empty Types - func testEncodingTopLevelEmptyStruct() { - let empty = EmptyStruct() - _testRoundTrip(of: empty, in: .binary, expectedPlist: _plistEmptyDictionaryBinary) - _testRoundTrip(of: empty, in: .xml, expectedPlist: _plistEmptyDictionaryXML) - } - - func testEncodingTopLevelEmptyClass() { - let empty = EmptyClass() - _testRoundTrip(of: empty, in: .binary, expectedPlist: _plistEmptyDictionaryBinary) - _testRoundTrip(of: empty, in: .xml, expectedPlist: _plistEmptyDictionaryXML) - } - - // MARK: - Encoding Top-Level Single-Value Types - func testEncodingTopLevelSingleValueEnum() { - let s1 = Switch.off - _testEncodeFailure(of: s1, in: .binary) - _testEncodeFailure(of: s1, in: .xml) - _testRoundTrip(of: TopLevelWrapper(s1), in: .binary) - _testRoundTrip(of: TopLevelWrapper(s1), in: .xml) - - let s2 = Switch.on - _testEncodeFailure(of: s2, in: .binary) - _testEncodeFailure(of: s2, in: .xml) - _testRoundTrip(of: TopLevelWrapper(s2), in: .binary) - _testRoundTrip(of: TopLevelWrapper(s2), in: .xml) - } - - func testEncodingTopLevelSingleValueStruct() { - let t = Timestamp(3141592653) - _testEncodeFailure(of: t, in: .binary) - _testEncodeFailure(of: t, in: .xml) - _testRoundTrip(of: TopLevelWrapper(t), in: .binary) - _testRoundTrip(of: TopLevelWrapper(t), in: .xml) - } - - func testEncodingTopLevelSingleValueClass() { - let c = Counter() - _testEncodeFailure(of: c, in: .binary) - _testEncodeFailure(of: c, in: .xml) - _testRoundTrip(of: TopLevelWrapper(c), in: .binary) - _testRoundTrip(of: TopLevelWrapper(c), in: .xml) - } - - // MARK: - Encoding Top-Level Structured Types - func testEncodingTopLevelStructuredStruct() { - // Address is a struct type with multiple fields. - let address = Address.testValue - _testRoundTrip(of: address, in: .binary) - _testRoundTrip(of: address, in: .xml) - } - - func testEncodingTopLevelStructuredClass() { - // Person is a class with multiple fields. - let person = Person.testValue - _testRoundTrip(of: person, in: .binary) - _testRoundTrip(of: person, in: .xml) - } - - func testEncodingTopLevelStructuredSingleStruct() { - // Numbers is a struct which encodes as an array through a single value container. - let numbers = Numbers.testValue - _testRoundTrip(of: numbers, in: .binary) - _testRoundTrip(of: numbers, in: .xml) - } - - func testEncodingTopLevelStructuredSingleClass() { - // Mapping is a class which encodes as a dictionary through a single value container. - let mapping = Mapping.testValue - _testRoundTrip(of: mapping, in: .binary) - _testRoundTrip(of: mapping, in: .xml) - } - - func testEncodingTopLevelDeepStructuredType() { - // Company is a type with fields which are Codable themselves. - let company = Company.testValue - _testRoundTrip(of: company, in: .binary) - _testRoundTrip(of: company, in: .xml) - } - - func testEncodingClassWhichSharesEncoderWithSuper() { - // Employee is a type which shares its encoder & decoder with its superclass, Person. - let employee = Employee.testValue - _testRoundTrip(of: employee, in: .binary) - _testRoundTrip(of: employee, in: .xml) - } - - func testEncodingTopLevelNullableType() { - // EnhancedBool is a type which encodes either as a Bool or as nil. - _testEncodeFailure(of: EnhancedBool.true, in: .binary) - _testEncodeFailure(of: EnhancedBool.true, in: .xml) - _testEncodeFailure(of: EnhancedBool.false, in: .binary) - _testEncodeFailure(of: EnhancedBool.false, in: .xml) - _testEncodeFailure(of: EnhancedBool.fileNotFound, in: .binary) - _testEncodeFailure(of: EnhancedBool.fileNotFound, in: .xml) - - _testRoundTrip(of: TopLevelWrapper(EnhancedBool.true), in: .binary) - _testRoundTrip(of: TopLevelWrapper(EnhancedBool.true), in: .xml) - _testRoundTrip(of: TopLevelWrapper(EnhancedBool.false), in: .binary) - _testRoundTrip(of: TopLevelWrapper(EnhancedBool.false), in: .xml) - _testRoundTrip(of: TopLevelWrapper(EnhancedBool.fileNotFound), in: .binary) - _testRoundTrip(of: TopLevelWrapper(EnhancedBool.fileNotFound), in: .xml) - } - - func testEncodingMultipleNestedContainersWithTheSameTopLevelKey() { - struct Model : Codable, Equatable { - let first: String - let second: String - - init(from coder: Decoder) throws { - let container = try coder.container(keyedBy: TopLevelCodingKeys.self) - - let firstNestedContainer = try container.nestedContainer(keyedBy: FirstNestedCodingKeys.self, forKey: .top) - self.first = try firstNestedContainer.decode(String.self, forKey: .first) - - let secondNestedContainer = try container.nestedContainer(keyedBy: SecondNestedCodingKeys.self, forKey: .top) - self.second = try secondNestedContainer.decode(String.self, forKey: .second) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: TopLevelCodingKeys.self) - - var firstNestedContainer = container.nestedContainer(keyedBy: FirstNestedCodingKeys.self, forKey: .top) - try firstNestedContainer.encode(self.first, forKey: .first) - - var secondNestedContainer = container.nestedContainer(keyedBy: SecondNestedCodingKeys.self, forKey: .top) - try secondNestedContainer.encode(self.second, forKey: .second) - } - - init(first: String, second: String) { - self.first = first - self.second = second - } - - static var testValue: Model { - return Model(first: "Johnny Appleseed", - second: "appleseed@apple.com") - } - enum TopLevelCodingKeys : String, CodingKey { - case top - } - - enum FirstNestedCodingKeys : String, CodingKey { - case first - } - enum SecondNestedCodingKeys : String, CodingKey { - case second - } - } - - let model = Model.testValue - let expectedXML = "\n\n\n\n\ttop\n\t\n\t\tfirst\n\t\tJohnny Appleseed\n\t\tsecond\n\t\tappleseed@apple.com\n\t\n\n\n".data(using: .utf8)! - _testRoundTrip(of: model, in: .xml, expectedPlist: expectedXML) - } - - func testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() { - struct Model : Encodable, Equatable { - let first: String - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: TopLevelCodingKeys.self) - - var firstNestedContainer = container.nestedContainer(keyedBy: FirstNestedCodingKeys.self, forKey: .top) - try firstNestedContainer.encode(self.first, forKey: .first) - - // The following line would fail as it attempts to re-encode into already encoded container is invalid. This will always fail - var secondNestedContainer = container.nestedUnkeyedContainer(forKey: .top) - try secondNestedContainer.encode("second") - } - - init(first: String) { - self.first = first - } - - static var testValue: Model { - return Model(first: "Johnny Appleseed") - } - enum TopLevelCodingKeys : String, CodingKey { - case top - } - - enum FirstNestedCodingKeys : String, CodingKey { - case first - } - } - - let model = Model.testValue - // This following test would fail as it attempts to re-encode into already encoded container is invalid. This will always fail - _testEncodeFailure(of: model, in: .xml) - } - - // MARK: - Encoder Features - func testNestedContainerCodingPaths() { - let encoder = JSONEncoder() - do { - let _ = try encoder.encode(NestedContainersTestType()) - } catch let error as NSError { - expectUnreachable("Caught error during encoding nested container types: \(error)") - } - } - - func testSuperEncoderCodingPaths() { - let encoder = JSONEncoder() - do { - let _ = try encoder.encode(NestedContainersTestType(testSuperEncoder: true)) - } catch let error as NSError { - expectUnreachable("Caught error during encoding nested container types: \(error)") - } - } - - func testEncodingTopLevelData() { - let data = try! JSONSerialization.data(withJSONObject: [], options: []) - _testRoundTrip(of: data, in: .binary, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: data, format: .binary, options: 0)) - _testRoundTrip(of: data, in: .xml, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: data, format: .xml, options: 0)) - } - - func testInterceptData() { - let data = try! JSONSerialization.data(withJSONObject: [], options: []) - let topLevel = TopLevelWrapper(data) - let plist = ["value": data] - _testRoundTrip(of: topLevel, in: .binary, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0)) - _testRoundTrip(of: topLevel, in: .xml, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)) - } - - func testInterceptDate() { - let date = Date(timeIntervalSinceReferenceDate: 0) - let topLevel = TopLevelWrapper(date) - let plist = ["value": date] - _testRoundTrip(of: topLevel, in: .binary, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0)) - _testRoundTrip(of: topLevel, in: .xml, expectedPlist: try! PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)) - } - - // MARK: - Type coercion - func testTypeCoercion() { - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int8].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int16].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int32].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Int64].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt8].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt16].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt32].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [UInt64].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Float].self) - _testRoundTripTypeCoercionFailure(of: [false, true], as: [Double].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int8], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int16], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int32], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [Int64], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt8], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt16], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt32], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0, 1] as [UInt64], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Float], as: [Bool].self) - _testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Double], as: [Bool].self) - } - - func testDecodingConcreteTypeParameter() { - let encoder = PropertyListEncoder() - guard let plist = try? encoder.encode(Employee.testValue) else { - expectUnreachable("Unable to encode Employee.") - return - } - - let decoder = PropertyListDecoder() - guard let decoded = try? decoder.decode(Employee.self as Person.Type, from: plist) else { - expectUnreachable("Failed to decode Employee as Person from plist.") - return - } - - expectEqual(type(of: decoded), Employee.self, "Expected decoded value to be of type Employee; got \(type(of: decoded)) instead.") - } - - // MARK: - Encoder State - // SR-6078 - func testEncoderStateThrowOnEncode() { - struct Wrapper : Encodable { - let value: T - init(_ value: T) { self.value = value } - - func encode(to encoder: Encoder) throws { - // This approximates a subclass calling into its superclass, where the superclass encodes a value that might throw. - // The key here is that getting the superEncoder creates a referencing encoder. - var container = encoder.unkeyedContainer() - let superEncoder = container.superEncoder() - - // Pushing a nested container on leaves the referencing encoder with multiple containers. - var nestedContainer = superEncoder.unkeyedContainer() - try nestedContainer.encode(value) - } - } - - struct Throwing : Encodable { - func encode(to encoder: Encoder) throws { - enum EncodingError : Error { case foo } - throw EncodingError.foo - } - } - - // The structure that would be encoded here looks like - // - // - // - // - // [throwing] - // - // - // - // - // The wrapper asks for an unkeyed container ([^]), gets a super encoder, and creates a nested container into that ([[^]]). - // We then encode an array into that ([[[^]]]), which happens to be a value that causes us to throw an error. - // - // The issue at hand reproduces when you have a referencing encoder (superEncoder() creates one) that has a container on the stack (unkeyedContainer() adds one) that encodes a value going through box_() (Array does that) that encodes something which throws (Throwing does that). - // When reproducing, this will cause a test failure via fatalError(). - _ = try? PropertyListEncoder().encode(Wrapper([Throwing()])) - } - - // MARK: - Encoder State - // SR-6048 - func testDecoderStateThrowOnDecode() { - let plist = try! PropertyListEncoder().encode([1,2,3]) - let _ = try! PropertyListDecoder().decode(EitherDecodable<[String], [Int]>.self, from: plist) - } - - // MARK: - Helper Functions - private var _plistEmptyDictionaryBinary: Data { - return Data(base64Encoded: "YnBsaXN0MDDQCAAAAAAAAAEBAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAJ")! - } - - private var _plistEmptyDictionaryXML: Data { - return "\n\n\n\n\n".data(using: .utf8)! - } - - private func _testEncodeFailure(of value: T, in format: PropertyListSerialization.PropertyListFormat) { - do { - let encoder = PropertyListEncoder() - encoder.outputFormat = format - let _ = try encoder.encode(value) - expectUnreachable("Encode of top-level \(T.self) was expected to fail.") - } catch {} - } - - private func _testRoundTrip(of value: T, in format: PropertyListSerialization.PropertyListFormat, expectedPlist plist: Data? = nil) where T : Codable, T : Equatable { - var payload: Data! = nil - do { - let encoder = PropertyListEncoder() - encoder.outputFormat = format - payload = try encoder.encode(value) - } catch { - expectUnreachable("Failed to encode \(T.self) to plist: \(error)") - } - - if let expectedPlist = plist { - expectEqual(expectedPlist, payload, "Produced plist not identical to expected plist.") - } - - do { - var decodedFormat: PropertyListSerialization.PropertyListFormat = .xml - let decoded = try PropertyListDecoder().decode(T.self, from: payload, format: &decodedFormat) - expectEqual(format, decodedFormat, "Encountered plist format differed from requested format.") - expectEqual(decoded, value, "\(T.self) did not round-trip to an equal value.") - } catch { - expectUnreachable("Failed to decode \(T.self) from plist: \(error)") - } - } - - private func _testRoundTripTypeCoercionFailure(of value: T, as type: U.Type) where T : Codable, U : Codable { - do { - let data = try PropertyListEncoder().encode(value) - let _ = try PropertyListDecoder().decode(U.self, from: data) - expectUnreachable("Coercion from \(T.self) to \(U.self) was expected to fail.") - } catch {} - } -} - -// MARK: - Helper Global Functions -func expectEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String) { - if lhs.count != rhs.count { - expectUnreachable("\(prefix) [CodingKey].count mismatch: \(lhs.count) != \(rhs.count)") - return - } - - for (key1, key2) in zip(lhs, rhs) { - switch (key1.intValue, key2.intValue) { - case (.none, .none): break - case (.some(let i1), .none): - expectUnreachable("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != nil") - return - case (.none, .some(let i2)): - expectUnreachable("\(prefix) CodingKey.intValue mismatch: nil != \(type(of: key2))(\(i2))") - return - case (.some(let i1), .some(let i2)): - guard i1 == i2 else { - expectUnreachable("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != \(type(of: key2))(\(i2))") - return - } - - break - } - - expectEqual(key1.stringValue, key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')") - } -} - -// MARK: - Test Types -/* FIXME: Import from %S/Inputs/Coding/SharedTypes.swift somehow. */ - -// MARK: - Empty Types -fileprivate struct EmptyStruct : Codable, Equatable { - static func ==(_ lhs: EmptyStruct, _ rhs: EmptyStruct) -> Bool { - return true - } -} - -fileprivate class EmptyClass : Codable, Equatable { - static func ==(_ lhs: EmptyClass, _ rhs: EmptyClass) -> Bool { - return true - } -} - -// MARK: - Single-Value Types -/// A simple on-off switch type that encodes as a single Bool value. -fileprivate enum Switch : Codable { - case off - case on - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - switch try container.decode(Bool.self) { - case false: self = .off - case true: self = .on - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .off: try container.encode(false) - case .on: try container.encode(true) - } - } -} - -/// A simple timestamp type that encodes as a single Double value. -fileprivate struct Timestamp : Codable, Equatable { - let value: Double - - init(_ value: Double) { - self.value = value - } - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - value = try container.decode(Double.self) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.value) - } - - static func ==(_ lhs: Timestamp, _ rhs: Timestamp) -> Bool { - return lhs.value == rhs.value - } -} - -/// A simple referential counter type that encodes as a single Int value. -fileprivate final class Counter : Codable, Equatable { - var count: Int = 0 - - init() {} - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - count = try container.decode(Int.self) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.count) - } - - static func ==(_ lhs: Counter, _ rhs: Counter) -> Bool { - return lhs === rhs || lhs.count == rhs.count - } -} - -// MARK: - Structured Types -/// A simple address type that encodes as a dictionary of values. -fileprivate struct Address : Codable, Equatable { - let street: String - let city: String - let state: String - let zipCode: Int - let country: String - - init(street: String, city: String, state: String, zipCode: Int, country: String) { - self.street = street - self.city = city - self.state = state - self.zipCode = zipCode - self.country = country - } - - static func ==(_ lhs: Address, _ rhs: Address) -> Bool { - return lhs.street == rhs.street && - lhs.city == rhs.city && - lhs.state == rhs.state && - lhs.zipCode == rhs.zipCode && - lhs.country == rhs.country - } - - static var testValue: Address { - return Address(street: "1 Infinite Loop", - city: "Cupertino", - state: "CA", - zipCode: 95014, - country: "United States") - } -} - -/// A simple person class that encodes as a dictionary of values. -fileprivate class Person : Codable, Equatable { - let name: String - let email: String - let website: URL? - - init(name: String, email: String, website: URL? = nil) { - self.name = name - self.email = email - self.website = website - } - - func isEqual(_ other: Person) -> Bool { - return self.name == other.name && - self.email == other.email && - self.website == other.website - } - - static func ==(_ lhs: Person, _ rhs: Person) -> Bool { - return lhs.isEqual(rhs) - } - - class var testValue: Person { - return Person(name: "Johnny Appleseed", email: "appleseed@apple.com") - } -} - -/// A class which shares its encoder and decoder with its superclass. -fileprivate class Employee : Person { - let id: Int - - init(name: String, email: String, website: URL? = nil, id: Int) { - self.id = id - super.init(name: name, email: email, website: website) - } - - enum CodingKeys : String, CodingKey { - case id - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - id = try container.decode(Int.self, forKey: .id) - try super.init(from: decoder) - } - - override func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(id, forKey: .id) - try super.encode(to: encoder) - } - - override func isEqual(_ other: Person) -> Bool { - if let employee = other as? Employee { - guard self.id == employee.id else { return false } - } - - return super.isEqual(other) - } - - override class var testValue: Employee { - return Employee(name: "Johnny Appleseed", email: "appleseed@apple.com", id: 42) - } -} - -/// A simple company struct which encodes as a dictionary of nested values. -fileprivate struct Company : Codable, Equatable { - let address: Address - var employees: [Employee] - - init(address: Address, employees: [Employee]) { - self.address = address - self.employees = employees - } - - static func ==(_ lhs: Company, _ rhs: Company) -> Bool { - return lhs.address == rhs.address && lhs.employees == rhs.employees - } - - static var testValue: Company { - return Company(address: Address.testValue, employees: [Employee.testValue]) - } -} - -/// An enum type which decodes from Bool?. -fileprivate enum EnhancedBool : Codable { - case `true` - case `false` - case fileNotFound - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if container.decodeNil() { - self = .fileNotFound - } else { - let value = try container.decode(Bool.self) - self = value ? .true : .false - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .true: try container.encode(true) - case .false: try container.encode(false) - case .fileNotFound: try container.encodeNil() - } - } -} - -/// A type which encodes as an array directly through a single value container. -struct Numbers : Codable, Equatable { - let values = [4, 8, 15, 16, 23, 42] - - init() {} - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let decodedValues = try container.decode([Int].self) - guard decodedValues == values else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "The Numbers are wrong!")) - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(values) - } - - static func ==(_ lhs: Numbers, _ rhs: Numbers) -> Bool { - return lhs.values == rhs.values - } - - static var testValue: Numbers { - return Numbers() - } -} - -/// A type which encodes as a dictionary directly through a single value container. -fileprivate final class Mapping : Codable, Equatable { - let values: [String : URL] - - init(values: [String : URL]) { - self.values = values - } - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - values = try container.decode([String : URL].self) - } - - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(values) - } - - static func ==(_ lhs: Mapping, _ rhs: Mapping) -> Bool { - return lhs === rhs || lhs.values == rhs.values - } - - static var testValue: Mapping { - return Mapping(values: ["Apple": URL(string: "http://apple.com")!, - "localhost": URL(string: "http://127.0.0.1")!]) - } -} - -struct NestedContainersTestType : Encodable { - let testSuperEncoder: Bool - - init(testSuperEncoder: Bool = false) { - self.testSuperEncoder = testSuperEncoder - } - - enum TopLevelCodingKeys : Int, CodingKey { - case a - case b - case c - } - - enum IntermediateCodingKeys : Int, CodingKey { - case one - case two - } - - func encode(to encoder: Encoder) throws { - if self.testSuperEncoder { - var topLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self) - expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(topLevelContainer.codingPath, [], "New first-level keyed container has non-empty codingPath.") - - let superEncoder = topLevelContainer.superEncoder(forKey: .a) - expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(topLevelContainer.codingPath, [], "First-level keyed container's codingPath changed.") - expectEqualPaths(superEncoder.codingPath, [TopLevelCodingKeys.a], "New superEncoder had unexpected codingPath.") - _testNestedContainers(in: superEncoder, baseCodingPath: [TopLevelCodingKeys.a]) - } else { - _testNestedContainers(in: encoder, baseCodingPath: []) - } - } - - func _testNestedContainers(in encoder: Encoder, baseCodingPath: [CodingKey]) { - expectEqualPaths(encoder.codingPath, baseCodingPath, "New encoder has non-empty codingPath.") - - // codingPath should not change upon fetching a non-nested container. - var firstLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "New first-level keyed container has non-empty codingPath.") - - // Nested Keyed Container - do { - // Nested container for key should have a new key pushed on. - var secondLevelContainer = firstLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self, forKey: .a) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "New second-level keyed container had unexpected codingPath.") - - // Inserting a keyed container should not change existing coding paths. - let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self, forKey: .one) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.one], "New third-level keyed container had unexpected codingPath.") - - // Inserting an unkeyed container should not change existing coding paths. - let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer(forKey: .two) - expectEqualPaths(encoder.codingPath, baseCodingPath + [], "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath + [], "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.two], "New third-level unkeyed container had unexpected codingPath.") - } - - // Nested Unkeyed Container - do { - // Nested container for key should have a new key pushed on. - var secondLevelContainer = firstLevelContainer.nestedUnkeyedContainer(forKey: .b) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "New second-level keyed container had unexpected codingPath.") - - // Appending a keyed container should not change existing coding paths. - let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self) - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 0)], "New third-level keyed container had unexpected codingPath.") - - // Appending an unkeyed container should not change existing coding paths. - let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer() - expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.") - expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.") - expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.") - expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 1)], "New third-level unkeyed container had unexpected codingPath.") - } - } -} - -// MARK: - Helper Types - -/// A key type which can take on any string or integer value. -/// This needs to mirror _PlistKey. -fileprivate struct _TestKey : CodingKey { - var stringValue: String - var intValue: Int? - - init?(stringValue: String) { - self.stringValue = stringValue - self.intValue = nil - } - - init?(intValue: Int) { - self.stringValue = "\(intValue)" - self.intValue = intValue - } - - init(index: Int) { - self.stringValue = "Index \(index)" - self.intValue = index - } -} - -/// Wraps a type T so that it can be encoded at the top level of a payload. -fileprivate struct TopLevelWrapper : Codable, Equatable where T : Codable, T : Equatable { - let value: T - - init(_ value: T) { - self.value = value - } - - static func ==(_ lhs: TopLevelWrapper, _ rhs: TopLevelWrapper) -> Bool { - return lhs.value == rhs.value - } -} - -fileprivate enum EitherDecodable : Decodable { - case t(T) - case u(U) - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if let t = try? container.decode(T.self) { - self = .t(t) - } else if let u = try? container.decode(U.self) { - self = .u(u) - } else { - throw DecodingError.dataCorruptedError(in: container, debugDescription: "Data was neither \(T.self) nor \(U.self).") - } - } -} - -// MARK: - Run Tests - -#if !FOUNDATION_XCTEST -var PropertyListEncoderTests = TestSuite("TestPropertyListEncoder") -PropertyListEncoderTests.test("testEncodingTopLevelEmptyStruct") { TestPropertyListEncoder().testEncodingTopLevelEmptyStruct() } -PropertyListEncoderTests.test("testEncodingTopLevelEmptyClass") { TestPropertyListEncoder().testEncodingTopLevelEmptyClass() } -PropertyListEncoderTests.test("testEncodingTopLevelSingleValueEnum") { TestPropertyListEncoder().testEncodingTopLevelSingleValueEnum() } -PropertyListEncoderTests.test("testEncodingTopLevelSingleValueStruct") { TestPropertyListEncoder().testEncodingTopLevelSingleValueStruct() } -PropertyListEncoderTests.test("testEncodingTopLevelSingleValueClass") { TestPropertyListEncoder().testEncodingTopLevelSingleValueClass() } -PropertyListEncoderTests.test("testEncodingTopLevelStructuredStruct") { TestPropertyListEncoder().testEncodingTopLevelStructuredStruct() } -PropertyListEncoderTests.test("testEncodingTopLevelStructuredClass") { TestPropertyListEncoder().testEncodingTopLevelStructuredClass() } -PropertyListEncoderTests.test("testEncodingTopLevelStructuredSingleStruct") { TestPropertyListEncoder().testEncodingTopLevelStructuredSingleStruct() } -PropertyListEncoderTests.test("testEncodingTopLevelStructuredSingleClass") { TestPropertyListEncoder().testEncodingTopLevelStructuredSingleClass() } -PropertyListEncoderTests.test("testEncodingTopLevelDeepStructuredType") { TestPropertyListEncoder().testEncodingTopLevelDeepStructuredType() } -PropertyListEncoderTests.test("testEncodingClassWhichSharesEncoderWithSuper") { TestPropertyListEncoder().testEncodingClassWhichSharesEncoderWithSuper() } -PropertyListEncoderTests.test("testEncodingTopLevelNullableType") { TestPropertyListEncoder().testEncodingTopLevelNullableType() } -PropertyListEncoderTests.test("testEncodingMultipleNestedContainersWithTheSameTopLevelKey") { - if #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) { - TestPropertyListEncoder().testEncodingMultipleNestedContainersWithTheSameTopLevelKey() - } -} -PropertyListEncoderTests.test("testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey") { - expectCrash() { - TestPropertyListEncoder().testEncodingConflictedTypeNestedContainersWithTheSameTopLevelKey() - } -} -PropertyListEncoderTests.test("testNestedContainerCodingPaths") { TestPropertyListEncoder().testNestedContainerCodingPaths() } -PropertyListEncoderTests.test("testSuperEncoderCodingPaths") { TestPropertyListEncoder().testSuperEncoderCodingPaths() } -PropertyListEncoderTests.test("testEncodingTopLevelData") { TestPropertyListEncoder().testEncodingTopLevelData() } -PropertyListEncoderTests.test("testInterceptData") { TestPropertyListEncoder().testInterceptData() } -PropertyListEncoderTests.test("testInterceptDate") { TestPropertyListEncoder().testInterceptDate() } -PropertyListEncoderTests.test("testTypeCoercion") { TestPropertyListEncoder().testTypeCoercion() } -PropertyListEncoderTests.test("testDecodingConcreteTypeParameter") { TestPropertyListEncoder().testDecodingConcreteTypeParameter() } -PropertyListEncoderTests.test("testEncoderStateThrowOnEncode") { TestPropertyListEncoder().testEncoderStateThrowOnEncode() } -PropertyListEncoderTests.test("testDecoderStateThrowOnDecode") { TestPropertyListEncoder().testDecoderStateThrowOnDecode() } -runAllTests() -#endif diff --git a/test/stdlib/TestProgress.swift b/test/stdlib/TestProgress.swift deleted file mode 100644 index 3667668c65bff..0000000000000 --- a/test/stdlib/TestProgress.swift +++ /dev/null @@ -1,76 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST - import XCTest - class TestProgressSuper : XCTestCase { } -#else - import StdlibUnittest - class TestProgressSuper { } -#endif - -class TestProgress : TestProgressSuper { - func testUserInfoConveniences() { - if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) { - let p = Progress(parent:nil, userInfo: nil) - - expectNil(p.userInfo[.throughputKey]) - expectNil(p.throughput) - p.throughput = 50 - expectEqual(p.throughput, 50) - expectNotNil(p.userInfo[.throughputKey]) - - expectNil(p.userInfo[.estimatedTimeRemainingKey]) - expectNil(p.estimatedTimeRemaining) - p.estimatedTimeRemaining = 100 - expectEqual(p.estimatedTimeRemaining, 100) - expectNotNil(p.userInfo[.estimatedTimeRemainingKey]) - - expectNil(p.userInfo[.fileTotalCountKey]) - expectNil(p.fileTotalCount) - p.fileTotalCount = 42 - expectEqual(p.fileTotalCount, 42) - expectNotNil(p.userInfo[.fileTotalCountKey]) - - expectNil(p.userInfo[.fileCompletedCountKey]) - expectNil(p.fileCompletedCount) - p.fileCompletedCount = 24 - expectEqual(p.fileCompletedCount, 24) - expectNotNil(p.userInfo[.fileCompletedCountKey]) - } - } - - func testPerformAsCurrent() { - if #available(OSX 10.11, iOS 8.0, *) { - // This test can be enabled once is in the SDK - /* - let p = Progress.discreteProgress(totalUnitCount: 10) - let r = p.performAsCurrent(withPendingUnitCount: 10) { - expectNotNil(Progress.current()) - return 42 - } - expectEqual(r, 42) - expectEqual(p.completedUnitCount, 10) - expectNil(Progress.current()) - */ - } - } -} - -#if !FOUNDATION_XCTEST -let ProgressTests = TestSuite("TestProgress") -ProgressTests.test("testUserInfoConveniences") { TestProgress().testUserInfoConveniences() } -ProgressTests.test("testPerformAsCurrent") { TestProgress().testPerformAsCurrent() } -runAllTests() -#endif diff --git a/test/stdlib/TestTimeZone.swift b/test/stdlib/TestTimeZone.swift deleted file mode 100644 index 90500756fbfb8..0000000000000 --- a/test/stdlib/TestTimeZone.swift +++ /dev/null @@ -1,103 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %empty-directory(%t) -// -// RUN: %target-clang %S/Inputs/FoundationBridge/FoundationBridge.m -c -o %t/FoundationBridgeObjC.o -g -// RUN: %target-build-swift %s -I %S/Inputs/FoundationBridge/ -Xlinker %t/FoundationBridgeObjC.o -o %t/TestTimeZone -// RUN: %target-codesign %t/TestTimeZone - -// RUN: %target-run %t/TestTimeZone > %t.txt -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation -import FoundationBridgeObjC - -#if FOUNDATION_XCTEST - import XCTest - class TestTimeZoneSuper : XCTestCase { } -#else - import StdlibUnittest - class TestTimeZoneSuper { } -#endif - -class TestTimeZone : TestTimeZoneSuper { - - func test_timeZoneBasics() { - let tz = TimeZone(identifier: "America/Los_Angeles")! - - expectTrue(!tz.identifier.isEmpty) - } - - func test_bridgingAutoupdating() { - let tester = TimeZoneBridgingTester() - - do { - let tz = TimeZone.autoupdatingCurrent - let result = tester.verifyAutoupdating(tz) - expectTrue(result) - } - - // Round trip an autoupdating calendar - do { - let tz = tester.autoupdatingCurrentTimeZone() - let result = tester.verifyAutoupdating(tz) - expectTrue(result) - } - } - - func test_equality() { - let autoupdating = TimeZone.autoupdatingCurrent - let autoupdating2 = TimeZone.autoupdatingCurrent - - expectEqual(autoupdating, autoupdating2) - - let current = TimeZone.current - - expectNotEqual(autoupdating, current) - } - - func test_AnyHashableContainingTimeZone() { - let values: [TimeZone] = [ - TimeZone(identifier: "America/Los_Angeles")!, - TimeZone(identifier: "Europe/Kiev")!, - TimeZone(identifier: "Europe/Kiev")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(TimeZone.self, type(of: anyHashables[0].base)) - expectEqual(TimeZone.self, type(of: anyHashables[1].base)) - expectEqual(TimeZone.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSTimeZone() { - let values: [NSTimeZone] = [ - NSTimeZone(name: "America/Los_Angeles")!, - NSTimeZone(name: "Europe/Kiev")!, - NSTimeZone(name: "Europe/Kiev")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(TimeZone.self, type(of: anyHashables[0].base)) - expectEqual(TimeZone.self, type(of: anyHashables[1].base)) - expectEqual(TimeZone.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } -} - -#if !FOUNDATION_XCTEST -var TimeZoneTests = TestSuite("TestTimeZone") -TimeZoneTests.test("test_timeZoneBasics") { TestTimeZone().test_timeZoneBasics() } -TimeZoneTests.test("test_bridgingAutoupdating") { TestTimeZone().test_bridgingAutoupdating() } -TimeZoneTests.test("test_equality") { TestTimeZone().test_equality() } -TimeZoneTests.test("test_AnyHashableContainingTimeZone") { TestTimeZone().test_AnyHashableContainingTimeZone() } -TimeZoneTests.test("test_AnyHashableCreatedFromNSTimeZone") { TestTimeZone().test_AnyHashableCreatedFromNSTimeZone() } -runAllTests() -#endif diff --git a/test/stdlib/TestURL.swift b/test/stdlib/TestURL.swift deleted file mode 100644 index 21f9205d61506..0000000000000 --- a/test/stdlib/TestURL.swift +++ /dev/null @@ -1,439 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestURLSuper : XCTestCase { } -#else -import StdlibUnittest -class TestURLSuper { } -#endif - -class TestURL : TestURLSuper { - - func testBasics() { - let url = URL(fileURLWithPath: NSTemporaryDirectory()) - - expectTrue(url.pathComponents.count > 0) - } - - func testProperties() { - let url = URL(fileURLWithPath: "/") - do { - let resourceValues = try url.resourceValues(forKeys: [.isVolumeKey, .nameKey]) - if let isVolume = resourceValues.isVolume { - expectTrue(isVolume) - } - expectNotNil(resourceValues.name) - } catch { - expectTrue(false, "Should not have thrown") - } - } - - func testSetProperties() { - // Create a temporary file - var file = URL(fileURLWithPath: NSTemporaryDirectory()) - let name = "my_great_file" + UUID().uuidString - file.appendPathComponent(name) - let data = Data(bytes: [1, 2, 3, 4, 5]) - do { - try data.write(to: file) - } catch { - expectTrue(false, "Unable to write data") - } - - // Modify an existing resource value - do { - var resourceValues = try file.resourceValues(forKeys: [.nameKey]) - expectNotNil(resourceValues.name) - expectEqual(resourceValues.name!, name) - - let newName = "goodbye cruel " + UUID().uuidString - resourceValues.name = newName - try file.setResourceValues(resourceValues) - } catch { - expectTrue(false, "Unable to set resources") - } - } - -#if os(macOS) - func testQuarantineProperties() { - // Test the quarantine stuff; it has special logic - if #available(OSX 10.11, iOS 9.0, *) { - // Create a temporary file - var file = URL(fileURLWithPath: NSTemporaryDirectory()) - let name = "my_great_file" + UUID().uuidString - file.appendPathComponent(name) - let data = Data(bytes: [1, 2, 3, 4, 5]) - do { - try data.write(to: file) - } catch { - expectTrue(false, "Unable to write data") - } - - // Set the quarantine info on a file - do { - var resourceValues = URLResourceValues() - resourceValues.quarantineProperties = ["LSQuarantineAgentName" : "TestURL"] - try file.setResourceValues(resourceValues) - } catch { - expectTrue(false, "Unable to set quarantine info") - } - - // Get the quarantine info back - do { - var resourceValues = try file.resourceValues(forKeys: [.quarantinePropertiesKey]) - expectEqual(resourceValues.quarantineProperties?["LSQuarantineAgentName"] as? String, "TestURL") - } catch { - expectTrue(false, "Unable to get quarantine info") - } - - // Clear the quarantine info - do { - var resourceValues = URLResourceValues() - resourceValues.quarantineProperties = nil // this effectively sets a flag - try file.setResourceValues(resourceValues) - - // Make sure that the resourceValues property returns nil - expectNil(resourceValues.quarantineProperties) - } catch { - expectTrue(false, "Unable to clear quarantine info") - } - - // Get the quarantine info back again - do { - var resourceValues = try file.resourceValues(forKeys: [.quarantinePropertiesKey]) - expectNil(resourceValues.quarantineProperties) - } catch { - expectTrue(false, "Unable to get quarantine info after clearing") - } - - } - } -#endif - - func testMoreSetProperties() { - // Create a temporary file - var file = URL(fileURLWithPath: NSTemporaryDirectory()) - let name = "my_great_file" + UUID().uuidString - file.appendPathComponent(name) - let data = Data(bytes: [1, 2, 3, 4, 5]) - do { - try data.write(to: file) - } catch { - expectTrue(false, "Unable to write data") - } - - do { - var resourceValues = try file.resourceValues(forKeys: [.labelNumberKey]) - expectNotNil(resourceValues.labelNumber) - - // set label number - resourceValues.labelNumber = 1 - try file.setResourceValues(resourceValues) - - // get label number - let _ = try file.resourceValues(forKeys: [.labelNumberKey]) - expectNotNil(resourceValues.labelNumber) - expectEqual(resourceValues.labelNumber!, 1) - } catch (let e as NSError) { - expectTrue(false, "Unable to load or set resources \(e)") - } catch { - expectTrue(false, "Unable to load or set resources (mysterious error)") - } - - // Construct values from scratch - do { - var resourceValues = URLResourceValues() - resourceValues.labelNumber = 2 - - try file.setResourceValues(resourceValues) - let resourceValues2 = try file.resourceValues(forKeys: [.labelNumberKey]) - expectNotNil(resourceValues2.labelNumber) - expectEqual(resourceValues2.labelNumber!, 2) - } catch (let e as NSError) { - expectTrue(false, "Unable to load or set resources \(e)") - } catch { - expectTrue(false, "Unable to load or set resources (mysterious error)") - } - - do { - try FileManager.default.removeItem(at: file) - } catch { - expectTrue(false, "Unable to remove file") - } - - } - - func testURLComponents() { - // Not meant to be a test of all URL components functionality, just some basic bridging stuff - let s = "http://www.apple.com/us/search/ipad?src=global%7Cnav" - var components = URLComponents(string: s)! - expectNotNil(components) - - expectNotNil(components.host) - expectEqual("www.apple.com", components.host) - - - if #available(OSX 10.11, iOS 9.0, *) { - let rangeOfHost = components.rangeOfHost! - expectNotNil(rangeOfHost) - expectEqual(s[rangeOfHost], "www.apple.com") - } - - if #available(OSX 10.10, iOS 8.0, *) { - let qi = components.queryItems! - expectNotNil(qi) - - expectEqual(1, qi.count) - let first = qi[0] - - expectEqual("src", first.name) - expectNotNil(first.value) - expectEqual("global|nav", first.value) - } - - if #available(OSX 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) { - components.percentEncodedQuery = "name1%E2%80%A2=value1%E2%80%A2&name2%E2%80%A2=value2%E2%80%A2" - var qi = components.queryItems! - expectNotNil(qi) - - expectEqual(2, qi.count) - - expectEqual("name1•", qi[0].name) - expectNotNil(qi[0].value) - expectEqual("value1•", qi[0].value) - - expectEqual("name2•", qi[1].name) - expectNotNil(qi[1].value) - expectEqual("value2•", qi[1].value) - - qi = components.percentEncodedQueryItems! - expectNotNil(qi) - - expectEqual(2, qi.count) - - expectEqual("name1%E2%80%A2", qi[0].name) - expectNotNil(qi[0].value) - expectEqual("value1%E2%80%A2", qi[0].value) - - expectEqual("name2%E2%80%A2", qi[1].name) - expectNotNil(qi[0].value) - expectEqual("value2%E2%80%A2", qi[1].value) - - qi[0].name = "%E2%80%A2name1" - qi[0].value = "%E2%80%A2value1" - qi[1].name = "%E2%80%A2name2" - qi[1].value = "%E2%80%A2value2" - - components.percentEncodedQueryItems = qi - - expectEqual("%E2%80%A2name1=%E2%80%A2value1&%E2%80%A2name2=%E2%80%A2value2", components.percentEncodedQuery) - } - } - - func testURLResourceValues() { - - let fileName = "temp_file" - var dir = URL(fileURLWithPath: NSTemporaryDirectory()) - dir.appendPathComponent(UUID().uuidString) - - try! FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true, attributes: nil) - - dir.appendPathComponent(fileName) - try! Data(bytes: [1,2,3,4]).write(to: dir) - - defer { - do { - try FileManager.default.removeItem(at: dir) - } catch { - // Oh well - } - } - - do { - let values = try dir.resourceValues(forKeys: [.nameKey, .isDirectoryKey]) - expectEqual(values.name, fileName) - expectFalse(values.isDirectory!) - expectEqual(nil, values.creationDate) // Didn't ask for this - } catch { - expectTrue(false, "Unable to get resource value") - } - - let originalDate : Date - do { - var values = try dir.resourceValues(forKeys: [.creationDateKey]) - expectNotEqual(nil, values.creationDate) - originalDate = values.creationDate! - } catch { - originalDate = Date() - expectTrue(false, "Unable to get creation date") - } - - let newDate = originalDate + 100 - - do { - var values = URLResourceValues() - values.creationDate = newDate - try dir.setResourceValues(values) - } catch { - expectTrue(false, "Unable to set resource value") - } - - do { - let values = try dir.resourceValues(forKeys: [.creationDateKey]) - expectEqual(newDate, values.creationDate) - } catch { - expectTrue(false, "Unable to get values") - } - } - - func test_AnyHashableContainingURL() { - let values: [URL] = [ - URL(string: "https://example.com/")!, - URL(string: "https://example.org/")!, - URL(string: "https://example.org/")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(URL.self, type(of: anyHashables[0].base)) - expectEqual(URL.self, type(of: anyHashables[1].base)) - expectEqual(URL.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSURL() { - let values: [NSURL] = [ - NSURL(string: "https://example.com/")!, - NSURL(string: "https://example.org/")!, - NSURL(string: "https://example.org/")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(URL.self, type(of: anyHashables[0].base)) - expectEqual(URL.self, type(of: anyHashables[1].base)) - expectEqual(URL.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableContainingURLComponents() { - let values: [URLComponents] = [ - URLComponents(string: "https://example.com/")!, - URLComponents(string: "https://example.org/")!, - URLComponents(string: "https://example.org/")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(URLComponents.self, type(of: anyHashables[0].base)) - expectEqual(URLComponents.self, type(of: anyHashables[1].base)) - expectEqual(URLComponents.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSURLComponents() { - let values: [NSURLComponents] = [ - NSURLComponents(string: "https://example.com/")!, - NSURLComponents(string: "https://example.org/")!, - NSURLComponents(string: "https://example.org/")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(URLComponents.self, type(of: anyHashables[0].base)) - expectEqual(URLComponents.self, type(of: anyHashables[1].base)) - expectEqual(URLComponents.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableContainingURLQueryItem() { - if #available(OSX 10.10, iOS 8.0, *) { - let values: [URLQueryItem] = [ - URLQueryItem(name: "foo", value: nil), - URLQueryItem(name: "bar", value: nil), - URLQueryItem(name: "bar", value: nil), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(URLQueryItem.self, type(of: anyHashables[0].base)) - expectEqual(URLQueryItem.self, type(of: anyHashables[1].base)) - expectEqual(URLQueryItem.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - } - - func test_AnyHashableCreatedFromNSURLQueryItem() { - if #available(OSX 10.10, iOS 8.0, *) { - let values: [NSURLQueryItem] = [ - NSURLQueryItem(name: "foo", value: nil), - NSURLQueryItem(name: "bar", value: nil), - NSURLQueryItem(name: "bar", value: nil), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(URLQueryItem.self, type(of: anyHashables[0].base)) - expectEqual(URLQueryItem.self, type(of: anyHashables[1].base)) - expectEqual(URLQueryItem.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - } - - func test_AnyHashableContainingURLRequest() { - let values: [URLRequest] = [ - URLRequest(url: URL(string: "https://example.com/")!), - URLRequest(url: URL(string: "https://example.org/")!), - URLRequest(url: URL(string: "https://example.org/")!), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(URLRequest.self, type(of: anyHashables[0].base)) - expectEqual(URLRequest.self, type(of: anyHashables[1].base)) - expectEqual(URLRequest.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSURLRequest() { - let values: [NSURLRequest] = [ - NSURLRequest(url: URL(string: "https://example.com/")!), - NSURLRequest(url: URL(string: "https://example.org/")!), - NSURLRequest(url: URL(string: "https://example.org/")!), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(URLRequest.self, type(of: anyHashables[0].base)) - expectEqual(URLRequest.self, type(of: anyHashables[1].base)) - expectEqual(URLRequest.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } -} - -#if !FOUNDATION_XCTEST -var URLTests = TestSuite("TestURL") -URLTests.test("testBasics") { TestURL().testBasics() } -URLTests.test("testProperties") { TestURL().testProperties() } -URLTests.test("testSetProperties") { TestURL().testSetProperties() } -#if os(macOS) -URLTests.test("testQuarantineProperties") { TestURL().testQuarantineProperties() } -#endif -URLTests.test("testMoreSetProperties") { TestURL().testMoreSetProperties() } -URLTests.test("testURLComponents") { TestURL().testURLComponents() } -URLTests.test("testURLResourceValues") { TestURL().testURLResourceValues() } -URLTests.test("test_AnyHashableContainingURL") { TestURL().test_AnyHashableContainingURL() } -URLTests.test("test_AnyHashableCreatedFromNSURL") { TestURL().test_AnyHashableCreatedFromNSURL() } -URLTests.test("test_AnyHashableContainingURLComponents") { TestURL().test_AnyHashableContainingURLComponents() } -URLTests.test("test_AnyHashableCreatedFromNSURLComponents") { TestURL().test_AnyHashableCreatedFromNSURLComponents() } -URLTests.test("test_AnyHashableContainingURLQueryItem") { TestURL().test_AnyHashableContainingURLQueryItem() } -URLTests.test("test_AnyHashableCreatedFromNSURLQueryItem") { TestURL().test_AnyHashableCreatedFromNSURLQueryItem() } -URLTests.test("test_AnyHashableContainingURLRequest") { TestURL().test_AnyHashableContainingURLRequest() } -URLTests.test("test_AnyHashableCreatedFromNSURLRequest") { TestURL().test_AnyHashableCreatedFromNSURLRequest() } -runAllTests() -#endif diff --git a/test/stdlib/TestUUID.swift b/test/stdlib/TestUUID.swift deleted file mode 100644 index 06825c119a4fb..0000000000000 --- a/test/stdlib/TestUUID.swift +++ /dev/null @@ -1,157 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestUUIDSuper : XCTestCase { } -#else -import StdlibUnittest -class TestUUIDSuper { } -#endif - - -class TestUUID : TestUUIDSuper { - func test_NS_Equality() { - let uuidA = NSUUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F") - let uuidB = NSUUID(uuidString: "e621e1f8-c36c-495a-93fc-0c247a3e6e5f") - let uuidC = NSUUID(uuidBytes: [0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f]) - let uuidD = NSUUID() - - expectEqual(uuidA, uuidB, "String case must not matter.") - expectEqual(uuidA, uuidC, "A UUID initialized with a string must be equal to the same UUID initialized with its UnsafePointer equivalent representation.") - expectNotEqual(uuidC, uuidD, "Two different UUIDs must not be equal.") - } - - func test_Equality() { - let uuidA = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F") - let uuidB = UUID(uuidString: "e621e1f8-c36c-495a-93fc-0c247a3e6e5f") - let uuidC = UUID(uuid: uuid_t(0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f)) - let uuidD = UUID() - - expectEqual(uuidA, uuidB, "String case must not matter.") - expectEqual(uuidA, uuidC, "A UUID initialized with a string must be equal to the same UUID initialized with its UnsafePointer equivalent representation.") - expectNotEqual(uuidC, uuidD, "Two different UUIDs must not be equal.") - } - - func test_NS_InvalidUUID() { - let uuid = NSUUID(uuidString: "Invalid UUID") - expectNil(uuid, "The convenience initializer `init?(uuidString string:)` must return nil for an invalid UUID string.") - } - - func test_InvalidUUID() { - let uuid = UUID(uuidString: "Invalid UUID") - expectNil(uuid, "The convenience initializer `init?(uuidString string:)` must return nil for an invalid UUID string.") - } - - func test_NS_uuidString() { - let uuid = NSUUID(uuidBytes: [0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f]) - expectEqual(uuid.uuidString, "E621E1F8-C36C-495A-93FC-0C247A3E6E5F") - } - - func test_uuidString() { - let uuid = UUID(uuid: uuid_t(0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f)) - expectEqual(uuid.uuidString, "E621E1F8-C36C-495A-93FC-0C247A3E6E5F") - } - - func test_description() { - let uuid = UUID() - expectEqual(uuid.description, uuid.uuidString, "The description must be the same as the uuidString.") - } - - func test_roundTrips() { - let ref = NSUUID() - let valFromRef = ref as UUID - var bytes: [UInt8] = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - let valFromBytes = bytes.withUnsafeMutableBufferPointer { buffer -> UUID in - ref.getBytes(buffer.baseAddress!) - return UUID(uuid: UnsafeRawPointer(buffer.baseAddress!).load(as: uuid_t.self)) - } - let valFromStr = UUID(uuidString: ref.uuidString) - expectEqual(ref.uuidString, valFromRef.uuidString) - expectEqual(ref.uuidString, valFromBytes.uuidString) - expectNotNil(valFromStr) - expectEqual(ref.uuidString, valFromStr!.uuidString) - } - - func test_hash() { - guard #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) else { return } - let values: [UUID] = [ - // This list takes a UUID and tweaks every byte while - // leaving the version/variant intact. - UUID(uuidString: "a53baa1c-b4f5-48db-9467-9786b76b256c")!, - UUID(uuidString: "a63baa1c-b4f5-48db-9467-9786b76b256c")!, - UUID(uuidString: "a53caa1c-b4f5-48db-9467-9786b76b256c")!, - UUID(uuidString: "a53bab1c-b4f5-48db-9467-9786b76b256c")!, - UUID(uuidString: "a53baa1d-b4f5-48db-9467-9786b76b256c")!, - UUID(uuidString: "a53baa1c-b5f5-48db-9467-9786b76b256c")!, - UUID(uuidString: "a53baa1c-b4f6-48db-9467-9786b76b256c")!, - UUID(uuidString: "a53baa1c-b4f5-49db-9467-9786b76b256c")!, - UUID(uuidString: "a53baa1c-b4f5-48dc-9467-9786b76b256c")!, - UUID(uuidString: "a53baa1c-b4f5-48db-9567-9786b76b256c")!, - UUID(uuidString: "a53baa1c-b4f5-48db-9468-9786b76b256c")!, - UUID(uuidString: "a53baa1c-b4f5-48db-9467-9886b76b256c")!, - UUID(uuidString: "a53baa1c-b4f5-48db-9467-9787b76b256c")!, - UUID(uuidString: "a53baa1c-b4f5-48db-9467-9786b86b256c")!, - UUID(uuidString: "a53baa1c-b4f5-48db-9467-9786b76c256c")!, - UUID(uuidString: "a53baa1c-b4f5-48db-9467-9786b76b266c")!, - UUID(uuidString: "a53baa1c-b4f5-48db-9467-9786b76b256d")!, - ] - checkHashable(values, equalityOracle: { $0 == $1 }) - } - - func test_AnyHashableContainingUUID() { - let values: [UUID] = [ - UUID(uuidString: "e621e1f8-c36c-495a-93fc-0c247a3e6e5f")!, - UUID(uuidString: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6")!, - UUID(uuidString: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(UUID.self, type(of: anyHashables[0].base)) - expectEqual(UUID.self, type(of: anyHashables[1].base)) - expectEqual(UUID.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSUUID() { - let values: [NSUUID] = [ - NSUUID(uuidString: "e621e1f8-c36c-495a-93fc-0c247a3e6e5f")!, - NSUUID(uuidString: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6")!, - NSUUID(uuidString: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6")!, - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(UUID.self, type(of: anyHashables[0].base)) - expectEqual(UUID.self, type(of: anyHashables[1].base)) - expectEqual(UUID.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } -} - -#if !FOUNDATION_XCTEST -var UUIDTests = TestSuite("TestUUID") -UUIDTests.test("test_NS_Equality") { TestUUID().test_NS_Equality() } -UUIDTests.test("test_Equality") { TestUUID().test_Equality() } -UUIDTests.test("test_NS_InvalidUUID") { TestUUID().test_NS_InvalidUUID() } -UUIDTests.test("test_InvalidUUID") { TestUUID().test_InvalidUUID() } -UUIDTests.test("test_NS_uuidString") { TestUUID().test_NS_uuidString() } -UUIDTests.test("test_uuidString") { TestUUID().test_uuidString() } -UUIDTests.test("test_description") { TestUUID().test_description() } -UUIDTests.test("test_roundTrips") { TestUUID().test_roundTrips() } -UUIDTests.test("test_hash") { TestUUID().test_hash() } -UUIDTests.test("test_AnyHashableContainingUUID") { TestUUID().test_AnyHashableContainingUUID() } -UUIDTests.test("test_AnyHashableCreatedFromNSUUID") { TestUUID().test_AnyHashableCreatedFromNSUUID() } -runAllTests() -#endif - diff --git a/test/stdlib/TestUserInfo.swift b/test/stdlib/TestUserInfo.swift deleted file mode 100644 index 7cd76b30ff999..0000000000000 --- a/test/stdlib/TestUserInfo.swift +++ /dev/null @@ -1,187 +0,0 @@ -// 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 -// -//===----------------------------------------------------------------------===// -// -// RUN: %target-run-simple-swift -// REQUIRES: executable_test -// REQUIRES: objc_interop - -import Foundation - -#if FOUNDATION_XCTEST -import XCTest -class TestUserInfoSuper : XCTestCase { } -#else -import StdlibUnittest -class TestUserInfoSuper : NSObject { } -#endif - -struct SubStruct: Equatable { - var i: Int - var str: String - - static func ==(lhs: SubStruct, rhs: SubStruct) -> Bool { - return lhs.i == rhs.i && - lhs.str == rhs.str - } -} - -struct SomeStructure: Hashable { - var i: Int - var str: String - var sub: SubStruct - - static func ==(lhs: SomeStructure, rhs: SomeStructure) -> Bool { - return lhs.i == rhs.i && - lhs.str == rhs.str && - lhs.sub == rhs.sub - } - - // FIXME: we don't care about this, but Any only finds == on Hashables - func hash(into hasher: inout Hasher) { - hasher.combine(i) - hasher.combine(str) - hasher.combine(sub.i) - hasher.combine(sub.str) - } -} - -/* - Notification and potentially other structures require a representation of a - userInfo dictionary. The Objective-C counterparts are represented via - NSDictionary which can only store a hashable key (actually - NSObject *) and a value of AnyObject (actually NSObject *). However - it is desired in swift to store Any in the value. These structure expositions - in swift have an adapter that allows them to pass a specialized NSDictionary - subclass to the Objective-C layer that can round trip the stored Any types back - out into Swift. - - In this case NSNotification -> Notification bridging is suitable to verify that - behavior. -*/ - -class TestUserInfo : TestUserInfoSuper { - var posted: Notification? - - func validate(_ testStructure: SomeStructure, _ value: SomeStructure) { - expectEqual(testStructure.i, value.i) - expectEqual(testStructure.str, value.str) - expectEqual(testStructure.sub.i, value.sub.i) - expectEqual(testStructure.sub.str, value.sub.str) - } - - func test_userInfoPost() { - let userInfoKey = "userInfoKey" - let notifName = Notification.Name(rawValue: "TestSwiftNotification") - let testStructure = SomeStructure(i: 5, str: "10", sub: SubStruct(i: 6, str: "11")) - let info: [AnyHashable : Any] = [ - AnyHashable(userInfoKey) : testStructure - ] - let note = Notification(name: notifName, userInfo: info) - expectNotNil(note.userInfo) - let nc = NotificationCenter.default - nc.addObserver(self, selector: #selector(TestUserInfo.notification(_:)), name: notifName, object: nil) - nc.post(note) - expectNotNil(posted) - if let notification = posted { - let postedInfo = notification.userInfo - expectNotNil(postedInfo) - if let userInfo = postedInfo { - let postedValue = userInfo[AnyHashable(userInfoKey)] as? SomeStructure - expectNotNil(postedValue) - if let value = postedValue { - validate(testStructure, value) - } - } - } - } - - func test_equality() { - let userInfoKey = "userInfoKey" - let notifName = Notification.Name(rawValue: "TestSwiftNotification") - let testStructure = SomeStructure(i: 5, str: "10", sub: SubStruct(i: 6, str: "11")) - let testStructure2 = SomeStructure(i: 6, str: "10", sub: SubStruct(i: 6, str: "11")) - let info1: [AnyHashable : Any] = [ - AnyHashable(userInfoKey) : testStructure - ] - let info2: [AnyHashable : Any] = [ - AnyHashable(userInfoKey) : "this can convert" - ] - let info3: [AnyHashable : Any] = [ - AnyHashable(userInfoKey) : testStructure2 - ] - - let note1 = Notification(name: notifName, userInfo: info1) - let note2 = Notification(name: notifName, userInfo: info1) - expectEqual(note1, note2) - - let note3 = Notification(name: notifName, userInfo: info2) - let note4 = Notification(name: notifName, userInfo: info2) - expectEqual(note3, note4) - - let note5 = Notification(name: notifName, userInfo: info3) - expectNotEqual(note1, note5) - } - - @objc func notification(_ notif: Notification) { - posted = notif - } - - // MARK: - - func test_classForCoder() { - // confirm internal bridged impl types are not exposed to archival machinery - // we have to be circuitous here, as bridging makes it very difficult to confirm this - // - // Gated on the availability of NSKeyedArchiver.archivedData(withRootObject:). - if #available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) { - let note = Notification(name: Notification.Name(rawValue: "TestSwiftNotification"), userInfo: [AnyHashable("key"):"value"]) - let archivedNote = NSKeyedArchiver.archivedData(withRootObject: note) - let noteAsPlist = try! PropertyListSerialization.propertyList(from: archivedNote, options: [], format: nil) - let plistAsData = try! PropertyListSerialization.data(fromPropertyList: noteAsPlist, format: .xml, options: 0) - let xml = NSString(data: plistAsData, encoding: String.Encoding.utf8.rawValue)! - expectEqual(xml.range(of: "_NSUserInfoDictionary").location, NSNotFound) - } - } - - func test_AnyHashableContainingNotification() { - let values: [Notification] = [ - Notification(name: Notification.Name(rawValue: "TestSwiftNotification")), - Notification(name: Notification.Name(rawValue: "TestOtherSwiftNotification")), - Notification(name: Notification.Name(rawValue: "TestOtherSwiftNotification")), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Notification.self, type(of: anyHashables[0].base)) - expectEqual(Notification.self, type(of: anyHashables[1].base)) - expectEqual(Notification.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } - - func test_AnyHashableCreatedFromNSNotification() { - let values: [NSNotification] = [ - NSNotification(name: Notification.Name(rawValue: "TestSwiftNotification"), object: nil), - NSNotification(name: Notification.Name(rawValue: "TestOtherSwiftNotification"), object: nil), - NSNotification(name: Notification.Name(rawValue: "TestOtherSwiftNotification"), object: nil), - ] - let anyHashables = values.map(AnyHashable.init) - expectEqual(Notification.self, type(of: anyHashables[0].base)) - expectEqual(Notification.self, type(of: anyHashables[1].base)) - expectEqual(Notification.self, type(of: anyHashables[2].base)) - expectNotEqual(anyHashables[0], anyHashables[1]) - expectEqual(anyHashables[1], anyHashables[2]) - } -} - -#if !FOUNDATION_XCTEST -var UserInfoTests = TestSuite("UserInfo") -UserInfoTests.test("test_userInfoPost") { TestUserInfo().test_userInfoPost() } -UserInfoTests.test("test_equality") { TestUserInfo().test_equality() } -UserInfoTests.test("test_classForCoder") { TestUserInfo().test_classForCoder() } -UserInfoTests.test("test_AnyHashableContainingNotification") { TestUserInfo().test_AnyHashableContainingNotification() } -UserInfoTests.test("test_AnyHashableCreatedFromNSNotification") { TestUserInfo().test_AnyHashableCreatedFromNSNotification() } -runAllTests() -#endif diff --git a/test/stdlib/UnsafePointerDiagnostics.swift b/test/stdlib/UnsafePointerDiagnostics.swift index 83fdb85d734a8..0f0ac66a360bf 100644 --- a/test/stdlib/UnsafePointerDiagnostics.swift +++ b/test/stdlib/UnsafePointerDiagnostics.swift @@ -61,14 +61,14 @@ func unsafePointerConversionAvailability( _ = UnsafeMutablePointer(umpi) _ = UnsafeMutablePointer(oumpi) - _ = UnsafeMutablePointer(rp) // expected-error {{no exact matches in call to initializer}} expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} - _ = UnsafeMutablePointer(mrp) // expected-error {{no exact matches in call to initializer}} expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} + _ = UnsafeMutablePointer(rp) // expected-error {{cannot convert value of type 'UnsafeRawPointer' to expected argument type 'UnsafeMutablePointer'}} expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} + _ = UnsafeMutablePointer(mrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer' to expected argument type 'UnsafeMutablePointer'}} expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} _ = UnsafeMutablePointer(umpv) // expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} _ = UnsafeMutablePointer(umpi) // expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} _ = UnsafeMutablePointer(umps) // expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} - _ = UnsafePointer(rp) // expected-error {{no exact matches in call to initializer}} expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} - _ = UnsafePointer(mrp) // expected-error {{no exact matches in call to initializer}} expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} + _ = UnsafePointer(rp) // expected-error {{cannot convert value of type 'UnsafeRawPointer' to expected argument type 'UnsafePointer'}} expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} + _ = UnsafePointer(mrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer' to expected argument type 'UnsafePointer'}} expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} _ = UnsafePointer(umpv) // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} _ = UnsafePointer(upv) // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} _ = UnsafePointer(umpi) // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} @@ -76,15 +76,15 @@ func unsafePointerConversionAvailability( _ = UnsafePointer(umps) // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} _ = UnsafePointer(ups) // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} - _ = UnsafeMutablePointer(rp) // expected-error {{no exact matches in call to initializer}} - _ = UnsafeMutablePointer(mrp) // expected-error {{no exact matches in call to initializer}} - _ = UnsafeMutablePointer(orp) // expected-error {{no exact matches in call to initializer}} - _ = UnsafeMutablePointer(omrp) // expected-error {{no exact matches in call to initializer}} + _ = UnsafeMutablePointer(rp) // expected-error {{cannot convert value of type 'UnsafeRawPointer' to expected argument type 'UnsafeMutablePointer'}} + _ = UnsafeMutablePointer(mrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer' to expected argument type 'UnsafeMutablePointer'}} + _ = UnsafeMutablePointer(orp) // expected-error {{cannot convert value of type 'UnsafeRawPointer?' to expected argument type 'UnsafeMutablePointer'}} + _ = UnsafeMutablePointer(omrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer?' to expected argument type 'UnsafeMutablePointer'}} - _ = UnsafePointer(rp) // expected-error {{no exact matches in call to initializer}} - _ = UnsafePointer(mrp) // expected-error {{no exact matches in call to initializer}} - _ = UnsafePointer(orp) // expected-error {{no exact matches in call to initializer}} - _ = UnsafePointer(omrp) // expected-error {{no exact matches in call to initializer}} + _ = UnsafePointer(rp) // expected-error {{cannot convert value of type 'UnsafeRawPointer' to expected argument type 'UnsafePointer'}} + _ = UnsafePointer(mrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer' to expected argument type 'UnsafePointer'}} + _ = UnsafePointer(orp) // expected-error {{cannot convert value of type 'UnsafeRawPointer?' to expected argument type 'UnsafePointer'}} + _ = UnsafePointer(omrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer?' to expected argument type 'UnsafePointer'}} _ = UnsafePointer(ups) // expected-error {{cannot convert value of type 'UnsafePointer' to expected argument type 'UnsafePointer'}} // expected-note@-1 {{arguments to generic parameter 'Pointee' ('String' and 'Int') are expected to be equal}} diff --git a/test/stdlib/UnsafePointerDiagnostics_warning.swift b/test/stdlib/UnsafePointerDiagnostics_warning.swift index ead8ac23a0d05..aa876119cbd45 100644 --- a/test/stdlib/UnsafePointerDiagnostics_warning.swift +++ b/test/stdlib/UnsafePointerDiagnostics_warning.swift @@ -201,10 +201,10 @@ func unsafePointerInitEphemeralConversions() { // expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} // FIXME: This is currently ambiguous. - _ = OpaquePointer(&foo) // expected-error {{no exact matches in call to initializer}} + _ = OpaquePointer(&foo) // expected-error {{ambiguous use of 'init(_:)'}} // FIXME: This is currently ambiguous. - _ = OpaquePointer(&arr) // expected-error {{no exact matches in call to initializer}} + _ = OpaquePointer(&arr) // expected-error {{ambiguous use of 'init(_:)'}} _ = OpaquePointer(arr) // expected-warning {{initialization of 'OpaquePointer' results in a dangling pointer}} // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} diff --git a/test/stdlib/UnsafeRawBufferPointer.swift b/test/stdlib/UnsafeRawBufferPointer.swift index cdc1bde447b69..d77e74135a318 100644 --- a/test/stdlib/UnsafeRawBufferPointer.swift +++ b/test/stdlib/UnsafeRawBufferPointer.swift @@ -407,6 +407,18 @@ UnsafeRawBufferPointerTestSuite.test("subscript.range.wide") { buffer[0..<2] = buffer[0..<3] } +UnsafeRawBufferPointerTestSuite.test("_copyContents") { + let a = Array(0..<20) + let b = UnsafeMutableBufferPointer.allocate(capacity: 10*a.count) + defer { b.deallocate() } + var (unwritten, written) = a.withUnsafeBytes { + bytes in + bytes._copyContents(initializing: b) + } + expectNil(unwritten.next()) + expectEqual(written, a.count) +} + UnsafeRawBufferPointerTestSuite.test("copyMemory.overflow") { var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } @@ -421,6 +433,19 @@ UnsafeRawBufferPointerTestSuite.test("copyMemory.overflow") { from: UnsafeRawBufferPointer(buffer)) } +// Use copyBytes without contiguous storage +UnsafeRawBufferPointerTestSuite.test("copyBytes.withoutContiguouseStorage") { + let ranges: [Range] = [0..<2, 1..<3, 2..<4, 3..<5] + var array = [UInt8](repeating: 0, count: 2) + for range in ranges { + array.withUnsafeMutableBytes { byte in + byte.copyBytes(from: range) + } + expectEqual(array.count, range.count) + expectEqual(array, Array(range)) + } +} + UnsafeRawBufferPointerTestSuite.test("copyBytes.sequence.overflow") { var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } diff --git a/test/stdlib/VarArgs.swift b/test/stdlib/VarArgs.swift index 4407f4c355e9f..43c0cf70544b4 100644 --- a/test/stdlib/VarArgs.swift +++ b/test/stdlib/VarArgs.swift @@ -3,6 +3,13 @@ import Swift +// Work around the inability for static-library based Swift runtime builds to +// directly link against Darwin.swiftmodule by using a benign dependency on +// StdlibUnittest. +// https://bugs.swift.org/browse/SR-15118 +import StdlibUnittest +runAllTests() + #if canImport(Darwin) import Darwin #if _runtime(_ObjC) diff --git a/test/stdlib/XCTest-smoke.swift b/test/stdlib/XCTest-smoke.swift deleted file mode 100644 index ebddbc4a7393e..0000000000000 --- a/test/stdlib/XCTest-smoke.swift +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: not test -e %platform-sdk-overlay-dir/XCTest.swiftmodule || %target-build-swift %s -o %t/main - -// REQUIRES: objc_interop - -import XCTest - -// Check that we can actually refer to functions from the XCTest overlay. -let optionalInt: Int? = nil -XCTAssertNil(optionalInt) - -// Check that the underlying framework was imported. -_ = XCTestCase.self diff --git a/test/stdlib/objc-array-slice.swift b/test/stdlib/objc-array-slice.swift index 5df964fdc97fa..b822d80676cb4 100644 --- a/test/stdlib/objc-array-slice.swift +++ b/test/stdlib/objc-array-slice.swift @@ -8,7 +8,7 @@ import Foundation -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { // This tests behavior that was fixed in // https://github.com/apple/swift/pull/36355 diff --git a/test/stdlib/symbol-visibility-linux.test-sh b/test/stdlib/symbol-visibility-linux.test-sh index 22c9f03c494cb..ae544917060d0 100644 --- a/test/stdlib/symbol-visibility-linux.test-sh +++ b/test/stdlib/symbol-visibility-linux.test-sh @@ -19,6 +19,9 @@ // RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \ // RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE19_M_emplace_back_auxIJS6_EEEvDpOT_ \ // RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE17_M_realloc_insertIJS6_EEEvN9__gnu_cxx17__normal_iteratorIPS6_S8_EEDpOT_ \ +// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE10_M_emplaceIJS0_ImS8_EEEES0_INSB_14_Node_iteratorIS9_Lb0ELb0EEEbESt17integral_constantIbLb1EEDpOT_ \ +// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE13_M_rehash_auxEmSt17integral_constantIbLb1EE \ +// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEED2Ev \ // RUN: -e _ZNSt3_V28__rotateIPcEET_S2_S2_S2_St26random_access_iterator_tag \ // RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_P13__va_list_tagEmSB_z \ // RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_St9__va_listEmSB_z \ @@ -38,6 +41,9 @@ // RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \ // RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE19_M_emplace_back_auxIJS6_EEEvDpOT_ \ // RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE17_M_realloc_insertIJS6_EEEvN9__gnu_cxx17__normal_iteratorIPS6_S8_EEDpOT_ \ +// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE10_M_emplaceIJS0_ImS8_EEEES0_INSB_14_Node_iteratorIS9_Lb0ELb0EEEbESt17integral_constantIbLb1EEDpOT_ \ +// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE13_M_rehash_auxEmSt17integral_constantIbLb1EE \ +// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEED2Ev \ // RUN: -e _ZNSt3_V28__rotateIPcEET_S2_S2_S2_St26random_access_iterator_tag \ // RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_P13__va_list_tagEmSB_z \ // RUN: -e _ZN9__gnu_cxx12__to_xstringINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEcEET_PFiPT0_mPKS8_St9__va_listEmSB_z \ diff --git a/test/stmt/async.swift b/test/stmt/async.swift index e125018e63274..a4319f4eaa040 100644 --- a/test/stmt/async.swift +++ b/test/stmt/async.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: concurrency @@ -6,5 +6,5 @@ func f() async -> Int { 0 } _ = await f() // expected-error{{'async' call in a function that does not support concurrency}} -spawn let y = await f() // expected-error{{'spawn let' in a function that does not support concurrency}} +async let y = await f() // expected-error{{'async let' in a function that does not support concurrency}} // expected-error@-1{{'async' call in a function that does not support concurrency}} diff --git a/test/stmt/errors_async.swift b/test/stmt/errors_async.swift index 8a2e43c809161..01554537c4c22 100644 --- a/test/stmt/errors_async.swift +++ b/test/stmt/errors_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency +// RUN: %target-swift-frontend -typecheck -verify %s // REQUIRES: concurrency @@ -6,7 +6,7 @@ enum MyError : Error { case bad } -@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +@available(SwiftStdlib 5.5, *) func shouldThrow() async { // expected-error@+1 {{errors thrown from here are not handled}} let _: Int = try await withUnsafeThrowingContinuation { continuation in diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index 20ddf6add1cfd..e3c3d6a3cb6eb 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -175,8 +175,8 @@ func testOptionalSequence() { } } -// Crash with (invalid) for each over an existential -func testExistentialSequence(s: Sequence) { // expected-error {{protocol 'Sequence' can only be used as a generic constraint because it has Self or associated type requirements}} +// FIXME: Should this be allowed? +func testExistentialSequence(s: Sequence) { for x in s { // expected-error {{protocol 'Sequence' as a type cannot conform to the protocol itself}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}} _ = x } diff --git a/test/stmt/if_while_var.swift b/test/stmt/if_while_var.swift index 78488ceaf8396..2b82d84ab659f 100644 --- a/test/stmt/if_while_var.swift +++ b/test/stmt/if_while_var.swift @@ -59,8 +59,8 @@ if let x = foo() { var opt: Int? = .none -if let x = opt {} // expected-warning {{value 'x' was defined but never used; consider replacing with boolean test}} -if var x = opt {} // expected-warning {{value 'x' was defined but never used; consider replacing with boolean test}} +if let x = opt {} // expected-warning {{value 'x' was defined but never used; consider replacing with boolean test}} {{4-12=}} {{15-15= != nil}} +if var x = opt {} // expected-warning {{value 'x' was defined but never used; consider replacing with boolean test}} {{4-12=}} {{15-15= != nil}} // Fix error message for invalid if-let let someInteger = 1 diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 5b16e9263356f..1f05959eb4448 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -412,7 +412,7 @@ func matching_pattern_recursion() { // Swift's break operator in switch should be indicated in errors func r18776073(_ a : Int?) { switch a { - case nil: // expected-error {{'case' label in a 'switch' should have at least one executable statement}} {{14-14= break}} + case nil: // expected-error {{'case' label in a 'switch' must have at least one executable statement}} {{14-14= break}} case _?: break } } diff --git a/test/type/opaque.swift b/test/type/opaque.swift index 4d82663b69262..0f1960a86cf81 100644 --- a/test/type/opaque.swift +++ b/test/type/opaque.swift @@ -10,7 +10,7 @@ extension String: P, Q { func paul() {}; mutating func priscilla() {}; func quin extension Array: P, Q { func paul() {}; mutating func priscilla() {}; func quinn() {} } class C {} -class D: C, P, Q { func paul() {}; func priscilla() {}; func quinn() {} } +class D: C, P, Q { func paul() {}; func priscilla() {}; func quinn() {}; func d() {} } let property: some P = 1 let deflessLet: some P // expected-error{{has no initializer}} {{educational-notes=opaque-type-inference}} @@ -75,17 +75,19 @@ struct Test { let zingle = {() -> some P in 1 } // expected-error{{'some' types are only implemented}} + +// Support for structural opaque result types is hidden behind a compiler flag +// until the proposal gets approved. +func twoOpaqueTypes() -> (some P, some P) { return (1, 2) } // expected-error{{'opaque' types cannot be nested inside other types}} +func asArrayElem() -> (some P)! { return [1] } // expected-error{{'opaque' types cannot be nested inside other types}} + // Invalid positions typealias Foo = some P // expected-error{{'some' types are only implemented}} func blibble(blobble: some P) {} // expected-error{{'some' types are only implemented}} - -let blubble: () -> some P = { 1 } // expected-error{{'some' types are only implemented}} - func blib() -> P & some Q { return 1 } // expected-error{{'some' should appear at the beginning}} -func blab() -> (P, some Q) { return (1, 2) } // expected-error{{'some' types are only implemented}} -func blob() -> (some P) -> P { return { $0 } } // expected-error{{'some' types are only implemented}} +func blab() -> some P? { return 1 } // expected-error{{must specify only}} expected-note{{did you mean to write an optional of an 'opaque' type?}} func blorb(_: T) { } // expected-error{{'some' types are only implemented}} func blub() -> T where T == some P { return 1 } // expected-error{{'some' types are only implemented}} expected-error{{cannot convert}} @@ -390,13 +392,19 @@ func rdar_51641323() { // Protocol requirements cannot have opaque return types protocol OpaqueProtocolRequirement { - // expected-error@+1 {{cannot be the return type of a protocol requirement}}{{3-3=associatedtype <#AssocType#>\n}}{{20-26=<#AssocType#>}} - func method() -> some P + // expected-error@+1 {{cannot be the return type of a protocol requirement}}{{3-3=associatedtype <#AssocType#>: P\n}}{{21-27=<#AssocType#>}} + func method1() -> some P - // expected-error@+1 {{cannot be the return type of a protocol requirement}}{{3-3=associatedtype <#AssocType#>\n}}{{13-19=<#AssocType#>}} + // expected-error@+1 {{cannot be the return type of a protocol requirement}}{{3-3=associatedtype <#AssocType#>: C & P & Q\n}}{{21-35=<#AssocType#>}} + func method2() -> some C & P & Q + + // expected-error@+1 {{cannot be the return type of a protocol requirement}}{{3-3=associatedtype <#AssocType#>: Nonsense\n}}{{21-34=<#AssocType#>}} + func method3() -> some Nonsense + + // expected-error@+1 {{cannot be the return type of a protocol requirement}}{{3-3=associatedtype <#AssocType#>: P\n}}{{13-19=<#AssocType#>}} var prop: some P { get } - // expected-error@+1 {{cannot be the return type of a protocol requirement}}{{3-3=associatedtype <#AssocType#>\n}}{{18-24=<#AssocType#>}} + // expected-error@+1 {{cannot be the return type of a protocol requirement}}{{3-3=associatedtype <#AssocType#>: P\n}}{{18-24=<#AssocType#>}} subscript() -> some P { get } } @@ -509,4 +517,7 @@ func takesOpaqueProtocol(generic: T) { _ = generic.asSome _ = generic.getAsSome() _ = generic[0] -} \ No newline at end of file +} + +func opaquePlaceholderFunc() -> some _ { 1 } // expected-error {{type placeholder not allowed here}} +var opaquePlaceholderVar: some _ = 1 // expected-error {{type placeholder not allowed here}} diff --git a/test/type/opaque_return_named.swift b/test/type/opaque_return_named.swift new file mode 100644 index 0000000000000..7f2dee866c8df --- /dev/null +++ b/test/type/opaque_return_named.swift @@ -0,0 +1,19 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-named-opaque-types -disable-availability-checking + +// Tests for experimental extensions to opaque return type support. + +func f0() -> () { } +func f1() -> () { } +func f2() -> () { } +func f4() async -> () { } + +func g0() -> { } // expected-error{{expected type for function result}} +func g1() -> async () { } // expected-error{{'async' may only occur before '->'}} +func g2() -> () async { } // expected-error{{'async' may only occur before '->'}} + +let x0: Int = 1 +var x1: (Int, Int) = (1, 1) +var x2: ( Int, Int) = (1, 1) // expected-error{{expected type}} expected-error{{cannot convert value of type '(Int, Int)' to specified type 'Int'}} +for _: Int in [1, 2, 3] { } + +struct S0 { subscript(i: Int) -> Int { 1 } } diff --git a/test/type/opaque_return_non_direct.swift b/test/type/opaque_return_non_direct.swift new file mode 100644 index 0000000000000..5e514c0fdabba --- /dev/null +++ b/test/type/opaque_return_non_direct.swift @@ -0,0 +1,39 @@ +// RUN: %target-swift-frontend -disable-availability-checking -typecheck -verify %s + +// rdar://81531010 - Failure to infer `Context` from contextual return type + +protocol View {} +struct EmptyView: View {} + +@resultBuilder struct ViewBuilder { + static func buildBlock() -> EmptyView { EmptyView() } +} + +struct Data: View { +} + +protocol Modifier { + associatedtype Body + associatedtype Content + + @ViewBuilder func body(content: Content) -> Body +} + +extension View { + func config(_: S) -> S.Body where S.Content == Self, S.Body: View { + fatalError() + } +} + +struct DataModifier : Modifier { + init(_: Data) {} + func body(content: Content) -> some View {} +} + +struct Test { + typealias Value = DataModifier.Body + + func test(data: Data) -> Value { + return data.config(DataModifier(data)) // Ok (Value is not considered an opaque type) + } +} diff --git a/test/type/opaque_return_structural.swift b/test/type/opaque_return_structural.swift new file mode 100644 index 0000000000000..78476eed8b0ee --- /dev/null +++ b/test/type/opaque_return_structural.swift @@ -0,0 +1,87 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-structural-opaque-types -disable-availability-checking + +// Tests for experimental extensions to opaque return type support. + +protocol P { func paul() } +protocol Q {} + +extension Int: P, Q { func paul() {} } +extension String: P, Q { func paul() {} } + +class C {} +class D: P, Q { func paul() {}; func d() {} } + + +// TODO: cases that we should support, but don't yet: +// +// WARNING: using '!' is not allowed here; treating this as '?' instead +// func asUnwrappedOptionalBase() -> (some P)! { return 1 } +// +// ERROR: generic parameter 'τ_0_0' could not be inferred +// func asHOFRetRet() -> () -> some P { return { 1 } } +// +// ERROR: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type +// func asHOFRetArg() -> (some P) -> () { return { (x: Int) -> () in } } +// +// ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions +// let x = { () -> some P in return 1 } + +func twoOpaqueTypes() -> (some P, some P) { return (1, 2) } // expected-error{{only one 'opaque' type is supported}} +func asTupleElemBad() -> (P, some Q) { return (1, C()) } // expected-note{{opaque return type declared here}} expected-error{{requires that 'C' conform to 'Q'}} + +func asTupleElem() -> (P, some Q) { return (1, 2) } +func asArrayElem() -> [some P] { return [1] } +func asOptionalBase() -> (some P)? { return 1 } + +let asTupleElemLet: (P, some Q) = (1, 2) +let asArrayElemLet: [some P] = [1] +let asOptionalBaseLet: (some P)? = 1 + +struct S1 { + var x: T +} +struct S2 { + var x: T + var y: U +} +struct R1 { + var x: T +} +struct R2 { + var x: T + var y: U +} + +func asUnconstrainedGeneric1() -> S1 { return S1(x: 1) } +func asUnconstrainedGeneric2() -> S2 { return S2(x: 1, y: 2) } +func asConstrainedGeneric1() -> R1 { return R1(x: 1) } +func asConstrainedGeneric2() -> R2 { return R2(x: 1, y: 2) } +func asNestedGenericDirect() -> S1> { return S1(x: S1(x: 1)) } +func asNestedGenericIndirect() -> S1> { return S1(x: S1(x: (1, 2))) } + +let asUnconstrainedGeneric2Let: S2 = S2(x: 1, y: 2) +let asNestedGenericIndirectLet: S1> = S1(x: S1(x: (1, 2))) + +// Tests an interesting SILGen case. For the underlying opaque type, we have to +// use the generic calling convention for closures. +func funcToAnyOpaqueCoercion() -> S1 { + let f: () -> () = {} + return S1(x: f) +} + +// TODO: We should give better error messages here. The opaque types have +// underlying types 'Int' and 'String', but the return statments have underlying +// types '(Int, Int)' and '(String, Int)'. +func structuralMismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> (some P, Int) { // expected-error{{do not have matching underlying types}} + if x { + return (y, 1) // expected-note{{return statement has underlying type 'Int'}} + } else { + return (z, 1) // expected-note{{return statement has underlying type 'String'}} + } +} + +func structuralMemberLookupBad() { + var tup: (some P, Int) = (D(), 1) + tup.0.paul(); + tup.0.d(); // expected-error{{value of type 'some P' has no member 'd'}} +} diff --git a/test/type/protocol_types.swift b/test/type/protocol_types.swift index 840dd52b3a311..d2d02dd612a3b 100644 --- a/test/type/protocol_types.swift +++ b/test/type/protocol_types.swift @@ -3,7 +3,7 @@ protocol HasSelfRequirements { func foo(_ x: Self) - func returnsOwnProtocol() -> HasSelfRequirements // expected-error{{protocol 'HasSelfRequirements' can only be used as a generic constraint because it has Self or associated type requirements}} {{educational-notes=associated-type-requirements}} + func returnsOwnProtocol() -> HasSelfRequirements } protocol Bar { // init() methods should not prevent use as an existential. @@ -36,10 +36,10 @@ func useCompoAsWhereRequirement(_ x: T) where T: HasSelfRequirements & Bar {} func useCompoAliasAsWhereRequirement(_ x: T) where T: Compo {} func useNestedCompoAliasAsWhereRequirement(_ x: T) where T: CompoAssocType.Compo {} -func useAsType(_ x: HasSelfRequirements) { } // expected-error{{protocol 'HasSelfRequirements' can only be used as a generic constraint}} -func useCompoAsType(_ x: HasSelfRequirements & Bar) { } // expected-error{{protocol 'HasSelfRequirements' can only be used as a generic constraint}} -func useCompoAliasAsType(_ x: Compo) { } // expected-error{{protocol 'HasSelfRequirements' can only be used as a generic constraint}} -func useNestedCompoAliasAsType(_ x: CompoAssocType.Compo) { } // expected-error{{protocol 'HasSelfRequirements' can only be used as a generic constraint}} +func useAsType(_: HasSelfRequirements, + _: HasSelfRequirements & Bar, + _: Compo, + _: CompoAssocType.Compo) { } struct TypeRequirement {} struct CompoTypeRequirement {} @@ -67,29 +67,43 @@ protocol HasAssoc { func foo() } -func testHasAssoc(_ x: Any) { - if let p = x as? HasAssoc { // expected-error {{protocol 'HasAssoc' can only be used as a generic constraint}} {{educational-notes=associated-type-requirements}} - p.foo() // don't crash here. +do { + enum MyError : Error { + case bad(Any) } -} -// rdar://problem/16803384 -protocol InheritsAssoc : HasAssoc { - func silverSpoon() + func checkIt(_ js: Any) throws { + switch js { + case let dbl as HasAssoc: + throw MyError.bad(dbl) + + default: + fatalError("wrong") + } + } } -func testInheritsAssoc(_ x: InheritsAssoc) { // expected-error {{protocol 'InheritsAssoc' can only be used as a generic constraint}} - x.silverSpoon() +func testHasAssoc(_ x: Any, _: HasAssoc) { + if let p = x as? HasAssoc { + p.foo() // don't crash here. + } + + struct ConformingType : HasAssoc { + typealias Assoc = Int + func foo() {} + + func method() -> HasAssoc {} + } } // SR-38 -var b: HasAssoc // expected-error {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} +var b: HasAssoc // Further generic constraint error testing - typealias used inside statements protocol P {} typealias MoreHasAssoc = HasAssoc & P func testHasMoreAssoc(_ x: Any) { - if let p = x as? MoreHasAssoc { // expected-error {{protocol 'HasAssoc' can only be used as a generic constraint}} + if let p = x as? MoreHasAssoc { p.foo() // don't crash here. } } @@ -103,37 +117,30 @@ struct Outer { typealias X = Struct1 _ = Struct1.self -typealias BadAlias = T +typealias AliasWhere = T where T : HasAssoc, T.Assoc == HasAssoc -// expected-error@-1 {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} -struct BadStruct +struct StructWhere where T : HasAssoc, T.Assoc == HasAssoc {} -// expected-error@-1 {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} -protocol BadProtocol where T == HasAssoc { - // expected-error@-1 {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} +protocol ProtocolWhere where T == HasAssoc { associatedtype T associatedtype U : HasAssoc where U.Assoc == HasAssoc - // expected-error@-1 {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} } extension HasAssoc where Assoc == HasAssoc {} -// expected-error@-1 {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} -func badFunction(_: T) +func FunctionWhere(_: T) where T : HasAssoc, T.Assoc == HasAssoc {} -// expected-error@-1 {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} -struct BadSubscript { +struct SubscriptWhere { subscript(_: T) -> Int where T : HasAssoc, T.Assoc == HasAssoc { - // expected-error@-1 {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} get {} set {} } @@ -141,5 +148,4 @@ struct BadSubscript { struct OuterGeneric { func contextuallyGenericMethod() where T == HasAssoc {} - // expected-error@-1 {{protocol 'HasAssoc' can only be used as a generic constraint because it has Self or associated type requirements}} } diff --git a/test/type/self.swift b/test/type/self.swift index d928a32ec64d2..04ac5b32d980d 100644 --- a/test/type/self.swift +++ b/test/type/self.swift @@ -360,3 +360,20 @@ do { } } } + +// https://bugs.swift.org/browse/SR-14731 +struct Generic { + func foo() -> Self {} + // expected-error@-1 {{cannot specialize 'Self'}} + // expected-note@-2 {{did you mean to explicitly reference 'Generic' instead?}}{{17-21=Generic}} +} + +struct NonGeneric { + func foo() -> Self {} + // expected-error@-1 {{cannot specialize 'Self'}} +} + +protocol P { + func foo() -> Self + // expected-error@-1 {{cannot specialize non-generic type 'Self'}} +} \ No newline at end of file diff --git a/test/type/types.swift b/test/type/types.swift index 135607476d43f..fd49961c7be83 100644 --- a/test/type/types.swift +++ b/test/type/types.swift @@ -18,7 +18,7 @@ var d3 : () -> Float = { 4 } var d4 : () -> Int = { d2 } // expected-error{{function produces expected type 'Int'; did you mean to call it with '()'?}} {{26-26=()}} -if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { +if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { var e0 : [Int] e0[] // expected-error {{missing argument for parameter #1 in call}} {{6-6=<#Int#>}} } diff --git a/tools/SourceKit/docs/Protocol.md b/tools/SourceKit/docs/Protocol.md index 16ba6a8e3f470..e784a39b9862e 100644 --- a/tools/SourceKit/docs/Protocol.md +++ b/tools/SourceKit/docs/Protocol.md @@ -310,15 +310,19 @@ entity ::= ``` diagnostic ::= { - : (int64) // The line upon which the diagnostic was emitted. - : (int64) // The column upon which the diagnostic was emitted. - : (string) // The absolute path to the file that was being parsed - // when the diagnostic was emitted. - : (UID) // The severity of the diagnostic. Can be one of: - // - source.diagnostic.severity.note - // - source.diagnostic.severity.warning - // - source.diagnostic.severity.error - : (string) // A description of the diagnostic. + : (string) // The internal ID of the diagnostic. + : (int64) // The line upon which the diagnostic was emitted. + : (int64) // The column upon which the diagnostic was emitted. + : (string) // The absolute path to the file that was being parsed + // when the diagnostic was emitted. + : (UID) // The severity of the diagnostic. Can be one of: + // - source.diagnostic.severity.note + // - source.diagnostic.severity.warning + // - source.diagnostic.severity.error + : (string) // A description of the diagnostic. + [opt] : (array) [UID*] // The categories of the diagnostic. Can be: + // - source.diagnostic.category.deprecation + // - source.diagnostic.category.no_usage } ``` @@ -781,6 +785,49 @@ expr-type-info ::= $ sourcekitd-test -req=collect-type /path/to/file.swift -- /path/to/file.swift ``` +## Variable Type + +This request collects the types of all variable declarations in a source file after type checking. +To fulfill this task, the client must provide the path to the Swift source file under +type checking and the necessary compiler arguments to help resolve all dependencies. + +### Request + +``` +{ + : (UID) , + : (string) // Absolute path to the file. + : [string*] // Array of zero or more strings for the compiler arguments, + // e.g ["-sdk", "/path/to/sdk"]. If key.sourcefile is provided, + // these must include the path to that file. + [opt] : (int64) // Offset of the requested range. Defaults to zero. + [opt] : (int64) // Length of the requested range. Defaults to the entire file. +} +``` + +### Response +``` +{ + : (array) [var-type-info*] // A list of variable declarations and types +} +``` + +``` +var-type-info ::= +{ + : (int64) // Offset of a variable identifier in the source file + : (int64) // Length of a variable identifier an expression in the source file + : (string) // Printed type of the variable declaration + (bool) // Whether the declaration has an explicit type annotation +} +``` + +### Testing + +``` +$ sourcekitd-test -req=collect-var-type /path/to/file.swift -- /path/to/file.swift +``` + # UIDs ## Keys diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index d1fac3447356e..c215af88245df 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -138,6 +138,27 @@ struct ExpressionTypesInFile { StringRef TypeBuffer; }; +struct VariableType { + /// The variable identifier's offset in the file. + unsigned VarOffset; + /// The variable identifier's length. + unsigned VarLength; + /// The offset of the type's string representation inside + /// `VariableTypesInFile.TypeBuffer`. + unsigned TypeOffset; + /// Whether the variable declaration has an explicit type annotation. + bool HasExplicitType; +}; + +struct VariableTypesInFile { + /// The typed variable declarations in the file. + std::vector Results; + /// A String containing the printed representation of all types in + /// `Results`. Entries in `Results` refer to their types by using + /// an offset into this string. + StringRef TypeBuffer; +}; + class CodeCompletionConsumer { virtual void anchor(); @@ -193,6 +214,11 @@ enum class DiagnosticSeverityKind { Error }; +enum class DiagnosticCategory { + Deprecation, + NoUsage +}; + struct DiagnosticEntryInfoBase { struct Fixit { unsigned Offset; @@ -200,11 +226,13 @@ struct DiagnosticEntryInfoBase { std::string Text; }; + std::string ID; std::string Description; unsigned Offset = 0; unsigned Line = 0; unsigned Column = 0; std::string Filename; + SmallVector Categories; SmallVector, 2> Ranges; SmallVector Fixits; SmallVector EducationalNotePaths; @@ -857,6 +885,15 @@ class LangSupport { std::function &)> Receiver) = 0; + /// Collects variable types for a range defined by `Offset` and `Length` in + /// the source file. If `Offset` or `Length` are empty, variable types for + /// the entire document are collected. + virtual void collectVariableTypes( + StringRef FileName, ArrayRef Args, + Optional Offset, Optional Length, + std::function &)> + Receiver) = 0; + virtual void getDocInfo(llvm::MemoryBuffer *InputBuf, StringRef ModuleName, ArrayRef Args, diff --git a/tools/SourceKit/lib/Support/Logging.cpp b/tools/SourceKit/lib/Support/Logging.cpp index 2e6bf63900d91..54589376e744f 100644 --- a/tools/SourceKit/lib/Support/Logging.cpp +++ b/tools/SourceKit/lib/Support/Logging.cpp @@ -63,7 +63,7 @@ Logger::~Logger() { OS << llvm::format("%7.4f] ", TR.getWallTime() - sBeginTR.getWallTime()); OS << Msg.str(); - fprintf(stderr, "%s: %s", LoggerName.c_str(), LogMsg.c_str()); + fprintf(stderr, "%s: %s\n", LoggerName.c_str(), LogMsg.c_str()); #if __APPLE__ // Use the Apple System Log facility. diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletion.h b/tools/SourceKit/lib/SwiftLang/CodeCompletion.h index f8098fbaf8390..692b40706d270 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletion.h +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletion.h @@ -26,6 +26,7 @@ using swift::ide::CodeCompletionDeclKind; using swift::ide::CodeCompletionKeywordKind; using swift::ide::CodeCompletionLiteralKind; using swift::ide::SemanticContextKind; +using swift::ide::CodeCompletionFlair; using swift::ide::CodeCompletionString; using SwiftResult = swift::ide::CodeCompletionResult; using swift::ide::CompletionKind; @@ -126,6 +127,7 @@ class CompletionBuilder { bool modified = false; Completion::ExpectedTypeRelation typeRelation; SemanticContextKind semanticContext; + CodeCompletionFlair flair; CodeCompletionString *completionString; llvm::SmallVector originalName; void *customKind = nullptr; @@ -151,6 +153,10 @@ class CompletionBuilder { modified = true; semanticContext = kind; } + void setFlair(CodeCompletionFlair value) { + modified = true; + flair = value; + } void setPopularityFactor(PopularityFactor val) { popularityFactor = val; } diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 40d23577beff7..db626fe301f7d 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -72,9 +72,7 @@ struct CodeCompletion::Group : public Item { std::vector SourceKit::CodeCompletion::extendCompletions( ArrayRef swiftResults, CompletionSink &sink, SwiftCompletionInfo &info, const NameToPopularityMap *nameToPopularity, - const Options &options, Completion *prefix, - Optional overrideContext, - Optional overrideOperatorContext) { + const Options &options, Completion *prefix, bool clearFlair) { ImportDepth depth; if (info.swiftASTContext) { @@ -125,10 +123,9 @@ std::vector SourceKit::CodeCompletion::extendCompletions( builder.setSemanticContext(prefix->getSemanticContext()); } - if (overrideOperatorContext && result->isOperator()) { - builder.setSemanticContext(*overrideOperatorContext); - } else if (overrideContext) { - builder.setSemanticContext(*overrideContext); + if (clearFlair) { + builder.setFlair(CodeCompletionFlair()); + builder.setSemanticContext(SemanticContextKind::None); } // If this result is not from the current module, try to get a popularity @@ -157,8 +154,8 @@ bool SourceKit::CodeCompletion::addCustomCompletions( CodeCompletionString::create(sink.allocator, chunk); CodeCompletion::SwiftResult swiftResult( CodeCompletion::SwiftResult::ResultKind::Pattern, - SemanticContextKind::ExpressionSpecific, - /*IsArgumentLabels=*/false, /*NumBytesToErase=*/0, completionString, + SemanticContextKind::Local, CodeCompletionFlairBit::ExpressionSpecific, + /*NumBytesToErase=*/0, completionString, CodeCompletionResult::ExpectedTypeRelation::Unknown); CompletionBuilder builder(sink, swiftResult); @@ -583,7 +580,6 @@ static double getSemanticContextScore(bool useImportDepth, Completion *completion) { double order = -1.0; switch (completion->getSemanticContext()) { - case SemanticContextKind::ExpressionSpecific: order = 0; break; case SemanticContextKind::Local: order = 1; break; case SemanticContextKind::CurrentNominal: order = 2; break; case SemanticContextKind::Super: order = 3; break; @@ -661,13 +657,13 @@ static ResultBucket getResultBucket(Item &item, bool hasRequiredTypes, if (completion->isNotRecommended() && !skipMetaGroups) return ResultBucket::NotRecommended; - if (completion->getSemanticContext() == - SemanticContextKind::ExpressionSpecific && - !skipMetaGroups) - return ResultBucket::ExpressionSpecific; - - if (completion->isArgumentLabels() && !skipMetaGroups) - return ResultBucket::ExpressionSpecific; + if (!skipMetaGroups) { + auto flair = completion->getFlair(); + if (flair.contains(CodeCompletionFlairBit::ExpressionSpecific) || + flair.contains(CodeCompletionFlairBit::SuperChain) || + flair.contains(CodeCompletionFlairBit::ArgumentLabels)) + return ResultBucket::ExpressionSpecific; + } if (completion->isOperator()) return ResultBucket::Operator; @@ -1126,6 +1122,7 @@ CompletionBuilder::CompletionBuilder(CompletionSink &sink, SwiftResult &base) : sink(sink), current(base) { typeRelation = current.getExpectedTypeRelation(); semanticContext = current.getSemanticContext(); + flair = current.getFlair(); completionString = const_cast(current.getCompletionString()); @@ -1170,15 +1167,14 @@ Completion *CompletionBuilder::finish() { if (current.getKind() == SwiftResult::Declaration) { base = SwiftResult( - semanticContext, current.isArgumentLabels(), - current.getNumBytesToErase(), completionString, + semanticContext, flair, current.getNumBytesToErase(), completionString, current.getAssociatedDeclKind(), current.isSystem(), - current.getModuleName(), current.getNotRecommendedReason(), - current.getBriefDocComment(), current.getAssociatedUSRs(), - current.getDeclKeywords(), typeRelation, opKind); + current.getModuleName(), current.getSourceFilePath(), + current.getNotRecommendedReason(), current.getDiagnosticSeverity(), + current.getDiagnosticMessage(), current.getBriefDocComment(), + current.getAssociatedUSRs(), typeRelation, opKind); } else { - base = SwiftResult(current.getKind(), semanticContext, - current.isArgumentLabels(), + base = SwiftResult(current.getKind(), semanticContext, flair, current.getNumBytesToErase(), completionString, typeRelation, opKind); } diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h index ce4a25f400f18..1c78607eecd86 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h @@ -43,6 +43,7 @@ struct Options { bool hideByNameStyle = true; bool fuzzyMatching = true; bool annotatedDescription = false; + bool includeObjectLiterals = true; unsigned minFuzzyLength = 2; unsigned showTopNonLiteralResults = 3; @@ -65,8 +66,7 @@ extendCompletions(ArrayRef swiftResults, CompletionSink &sink, SwiftCompletionInfo &info, const NameToPopularityMap *nameToPopularity, const Options &options, Completion *prefix = nullptr, - Optional overrideContext = None, - Optional overrideOperatorContext = None); + bool clearFlair = false); bool addCustomCompletions(CompletionSink &sink, std::vector &completions, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 83e883492c502..54b8e9b66417c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -97,7 +97,8 @@ struct SwiftCodeCompletionConsumer } void clearContext() { swiftContext = SwiftCompletionInfo(); } - void handleResults(MutableArrayRef Results) override { + void handleResults(CodeCompletionContext &context) override { + MutableArrayRef Results = context.takeResults(); assert(swiftContext.swiftASTContext); handleResultsImpl(Results, swiftContext); } @@ -121,7 +122,7 @@ static bool swiftCodeCompleteImpl( unsigned Offset, SwiftCodeCompletionConsumer &SwiftConsumer, ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, - bool annotateDescription, std::string &Error) { + const CodeCompletion::Options &opts, std::string &Error) { return Lang.performCompletionLikeOperation( UnresolvedInputFile, Offset, Args, FileSystem, Error, [&](CompilerInstance &CI, bool reusingASTContext) { @@ -130,7 +131,8 @@ static bool swiftCodeCompleteImpl( auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); CompletionContext.ReusingASTContext = reusingASTContext; - CompletionContext.setAnnotateResult(annotateDescription); + CompletionContext.setAnnotateResult(opts.annotatedDescription); + CompletionContext.setIncludeObjectLiterals(opts.includeObjectLiterals); std::unique_ptr callbacksFactory( ide::makeCodeCompletionCallbacksFactory(CompletionContext, SwiftConsumer)); @@ -214,8 +216,7 @@ void SwiftLangSupport::codeComplete( std::string Error; if (!swiftCodeCompleteImpl(*this, UnresolvedInputFile, Offset, SwiftConsumer, - Args, fileSystem, - CCOpts.annotatedDescription, Error)) { + Args, fileSystem, CCOpts, Error)) { SKConsumer.failed(Error); } } @@ -242,14 +243,14 @@ static void getResultStructure( for (; i < chunks.size(); ++i) { auto C = chunks[i]; if (C.is(ChunkKind::TypeAnnotation) || - C.is(ChunkKind::CallParameterClosureType) || - C.is(ChunkKind::CallParameterClosureExpr) || + C.is(ChunkKind::CallArgumentClosureType) || + C.is(ChunkKind::CallArgumentClosureExpr) || C.is(ChunkKind::Whitespace)) continue; if (C.is(ChunkKind::LeftParen) || C.is(ChunkKind::LeftBracket) || C.is(ChunkKind::BraceStmtWithCursor) || - C.is(ChunkKind::CallParameterBegin)) + C.is(ChunkKind::CallArgumentBegin)) break; if (C.hasText()) @@ -263,15 +264,15 @@ static void getResultStructure( for (; i < chunks.size(); ++i) { auto &C = chunks[i]; if (C.is(ChunkKind::TypeAnnotation) || - C.is(ChunkKind::CallParameterClosureType) || - C.is(ChunkKind::CallParameterClosureExpr) || + C.is(ChunkKind::CallArgumentClosureType) || + C.is(ChunkKind::CallArgumentClosureExpr) || C.is(ChunkKind::Whitespace)) continue; if (C.is(ChunkKind::BraceStmtWithCursor)) break; - if (C.is(ChunkKind::CallParameterBegin)) { + if (C.is(ChunkKind::CallArgumentBegin)) { CodeCompletionInfo::ParameterStructure param; ++i; @@ -280,23 +281,23 @@ static void getResultStructure( for (; i < chunks.size(); ++i) { if (chunks[i].endsPreviousNestedGroup(C.getNestingLevel())) break; - if (chunks[i].is(ChunkKind::CallParameterClosureType) || - chunks[i].is(ChunkKind::CallParameterClosureExpr)) + if (chunks[i].is(ChunkKind::CallArgumentClosureType) || + chunks[i].is(ChunkKind::CallArgumentClosureExpr)) continue; - if (isOperator && chunks[i].is(ChunkKind::CallParameterType)) + if (isOperator && chunks[i].is(ChunkKind::CallArgumentType)) continue; // Parameter name - if (chunks[i].is(ChunkKind::CallParameterName) || - chunks[i].is(ChunkKind::CallParameterInternalName)) { + if (chunks[i].is(ChunkKind::CallArgumentName) || + chunks[i].is(ChunkKind::CallArgumentInternalName)) { param.name.begin = textSize; param.isLocalName = - chunks[i].is(ChunkKind::CallParameterInternalName); + chunks[i].is(ChunkKind::CallArgumentInternalName); inName = true; } // Parameter type - if (chunks[i].is(ChunkKind::CallParameterType)) { + if (chunks[i].is(ChunkKind::CallArgumentType)) { unsigned start = textSize; unsigned prev = i - 1; // if i == 0, prev = ~0u. @@ -307,7 +308,7 @@ static void getResultStructure( } // Combine the whitespace after ':' into the type name. - if (prev != ~0u && chunks[prev].is(ChunkKind::CallParameterColon)) + if (prev != ~0u && chunks[prev].is(ChunkKind::CallArgumentColon)) start -= 1; param.afterColon.begin = start; @@ -492,23 +493,28 @@ bool SwiftToSourceKitCompletionAdapter::handleResult( static UIdent CCCtxCurrentModule("source.codecompletion.context.thismodule"); static UIdent CCCtxOtherModule("source.codecompletion.context.othermodule"); - switch (Result->getSemanticContext()) { - case SemanticContextKind::None: - Info.SemanticContext = CCCtxNone; break; - case SemanticContextKind::ExpressionSpecific: - Info.SemanticContext = CCCtxExpressionSpecific; break; - case SemanticContextKind::Local: - Info.SemanticContext = CCCtxLocal; break; - case SemanticContextKind::CurrentNominal: - Info.SemanticContext = CCCtxCurrentNominal; break; - case SemanticContextKind::Super: - Info.SemanticContext = CCCtxSuper; break; - case SemanticContextKind::OutsideNominal: - Info.SemanticContext = CCCtxOutsideNominal; break; - case SemanticContextKind::CurrentModule: - Info.SemanticContext = CCCtxCurrentModule; break; - case SemanticContextKind::OtherModule: - Info.SemanticContext = CCCtxOtherModule; break; + + if (Result->getFlair().contains(CodeCompletionFlairBit::ExpressionSpecific) || + Result->getFlair().contains(CodeCompletionFlairBit::SuperChain)) { + // NOTE: `CCCtxExpressionSpecific` is to maintain compatibility. + Info.SemanticContext = CCCtxExpressionSpecific; + } else { + switch (Result->getSemanticContext()) { + case SemanticContextKind::None: + Info.SemanticContext = CCCtxNone; break; + case SemanticContextKind::Local: + Info.SemanticContext = CCCtxLocal; break; + case SemanticContextKind::CurrentNominal: + Info.SemanticContext = CCCtxCurrentNominal; break; + case SemanticContextKind::Super: + Info.SemanticContext = CCCtxSuper; break; + case SemanticContextKind::OutsideNominal: + Info.SemanticContext = CCCtxOutsideNominal; break; + case SemanticContextKind::CurrentModule: + Info.SemanticContext = CCCtxCurrentModule; break; + case SemanticContextKind::OtherModule: + Info.SemanticContext = CCCtxOtherModule; break; + } } static UIdent CCTypeRelNotApplicable("source.codecompletion.typerelation.notapplicable"); @@ -728,6 +734,7 @@ static void translateCodeCompletionOptions(OptionsDictionary &from, static UIdent KeyFuzzyWeight("key.codecomplete.sort.fuzzyweight"); static UIdent KeyPopularityBonus("key.codecomplete.sort.popularitybonus"); static UIdent KeyAnnotatedDescription("key.codecomplete.annotateddescription"); + static UIdent KeyIncludeObjectLiterals("key.codecomplete.includeobjectliterals"); from.valueForOption(KeySortByName, to.sortByName); from.valueForOption(KeyUseImportDepth, to.useImportDepth); @@ -753,6 +760,7 @@ static void translateCodeCompletionOptions(OptionsDictionary &from, from.valueForOption(KeyHideByName, to.hideByNameStyle); from.valueForOption(KeyTopNonLiteral, to.showTopNonLiteralResults); from.valueForOption(KeyAnnotatedDescription, to.annotatedDescription); + from.valueForOption(KeyIncludeObjectLiterals, to.includeObjectLiterals); } /// Canonicalize a name that is in the format of a reference to a function into @@ -912,14 +920,15 @@ static void transformAndForwardResults( CodeCompletionString::create(innerSink.allocator, chunks); CodeCompletion::SwiftResult paren( CodeCompletion::SwiftResult::ResultKind::BuiltinOperator, - SemanticContextKind::ExpressionSpecific, /*IsArgumentLabels=*/false, + SemanticContextKind::CurrentNominal, + CodeCompletionFlairBit::ExpressionSpecific, exactMatch ? exactMatch->getNumBytesToErase() : 0, completionString, CodeCompletionResult::ExpectedTypeRelation::NotApplicable); SwiftCompletionInfo info; std::vector extended = extendCompletions(&paren, innerSink, info, nameToPopularity, options, - exactMatch, SemanticContextKind::ExpressionSpecific); + exactMatch); assert(extended.size() == 1); return extended.front(); }; @@ -1000,11 +1009,11 @@ static void transformAndForwardResults( auto topResults = filterInnerResults(results, options.addInnerResults, options.addInnerOperators, hasDot, hasQDot, hasInit, rules); - // FIXME: Overriding the default to context "None" is a hack so that they - // won't overwhelm other results that also match the filter text. + // FIXME: Clearing the flair (and semantic context) is a hack so that + // they won't overwhelm other results that also match the filter text. innerResults = extendCompletions( topResults, innerSink, info, nameToPopularity, options, exactMatch, - SemanticContextKind::None, SemanticContextKind::None); + /*clearFlair=*/true); }); auto *inputBuf = session->getBuffer(); @@ -1023,7 +1032,7 @@ static void transformAndForwardResults( std::string error; if (!swiftCodeCompleteImpl(lang, buffer.get(), str.size(), swiftConsumer, cargs, session->getFileSystem(), - options.annotatedDescription, error)) { + options, error)) { consumer.failed(error); return; } @@ -1123,8 +1132,7 @@ void SwiftLangSupport::codeCompleteOpen( // Invoke completion. if (!swiftCodeCompleteImpl(*this, inputBuf, offset, swiftConsumer, - extendedArgs, fileSystem, - CCOpts.annotatedDescription, error)) { + extendedArgs, fileSystem, CCOpts, error)) { consumer.failed(error); return; } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp index 4c40918371fd6..8d09ee644eb4a 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp @@ -269,7 +269,9 @@ static void initDocGenericParams(const Decl *D, DocEntityInfo &Info, // synthesized extention itself rather than a member, into its extended // nominal (the extension's own requirements shouldn't be considered in the // substitution). + unsigned TypeContextDepth = 0; SubstitutionMap SubMap; + ModuleDecl *M = nullptr; Type BaseType; if (SynthesizedTarget) { BaseType = SynthesizedTarget.getBaseNominal()->getDeclaredInterfaceType(); @@ -279,18 +281,34 @@ static void initDocGenericParams(const Decl *D, DocEntityInfo &Info, DC = cast(D)->getExtendedNominal(); else DC = D->getInnermostDeclContext()->getInnermostTypeContext(); - auto *M = DC->getParentModule(); + M = DC->getParentModule(); SubMap = BaseType->getContextSubstitutionMap(M, DC); + if (!SubMap.empty()) { + TypeContextDepth = SubMap.getGenericSignature() + .getGenericParams().back()->getDepth() + 1; + } } } auto SubstTypes = [&](Type Ty) { - return Ty.subst(SubMap, SubstFlags::DesugarMemberTypes); + if (SubMap.empty()) + return Ty; + + return Ty.subst( + [&](SubstitutableType *type) -> Type { + if (cast(type)->getDepth() < TypeContextDepth) + return Type(type).subst(SubMap); + return type; + }, + [&](CanType depType, Type substType, ProtocolDecl *proto) { + return M->lookupConformance(substType, proto); + }, + SubstFlags::DesugarMemberTypes); }; // FIXME: Not right for extensions of nested generic types if (GC->isGeneric()) { - for (auto *GP : GenericSig->getInnermostGenericParams()) { + for (auto *GP : GenericSig.getInnermostGenericParams()) { if (GP->getDecl()->isImplicit()) continue; Type TypeToPrint = GP; @@ -314,7 +332,7 @@ static void initDocGenericParams(const Decl *D, DocEntityInfo &Info, if (auto *typeDC = GC->getInnermostTypeContext()) Proto = typeDC->getSelfProtocolDecl(); - for (auto Req: GenericSig->getRequirements()) { + for (auto Req: GenericSig.getRequirements()) { if (Proto && Req.getKind() == RequirementKind::Conformance && Req.getFirstType()->isEqual(Proto->getSelfInterfaceType()) && @@ -428,6 +446,9 @@ static bool initDocEntityInfo(const Decl *D, Info.IsOptional = D->getAttrs().hasAttribute(); if (auto *AFD = dyn_cast(D)) { Info.IsAsync = AFD->hasAsync(); + } else if (auto *Storage = dyn_cast(D)) { + if (auto *Getter = Storage->getAccessor(AccessorKind::Get)) + Info.IsAsync = Getter->hasAsync(); } if (!IsRef) { @@ -538,7 +559,7 @@ static void passConforms(const ValueDecl *D, DocInfoConsumer &Consumer) { return; Consumer.handleConformsToEntity(EntInfo); } -static void passInherits(ArrayRef InheritedTypes, +static void passInherits(ArrayRef InheritedTypes, DocInfoConsumer &Consumer) { for (auto Inherited : InheritedTypes) { if (!Inherited.getType()) @@ -552,7 +573,7 @@ static void passInherits(ArrayRef InheritedTypes, if (auto ProtoComposition = Inherited.getType()->getAs()) { for (auto T : ProtoComposition->getMembers()) - passInherits(TypeLoc::withoutLoc(T), Consumer); + passInherits(InheritedEntry(TypeLoc::withoutLoc(T)), Consumer); continue; } @@ -615,9 +636,9 @@ static void reportRelated(ASTContext &Ctx, const Decl *D, // Otherwise, report the inheritance of the type alias itself. passInheritsAndConformancesForValueDecl(TAD, Consumer); } else if (const auto *TD = dyn_cast(D)) { - llvm::SmallVector AllInherits; - getInheritedForPrinting(TD, PrintOptions(), AllInherits); - passInherits(AllInherits, Consumer); + llvm::SmallVector AllInheritsForPrinting; + getInheritedForPrinting(TD, PrintOptions(), AllInheritsForPrinting); + passInherits(AllInheritsForPrinting, Consumer); passConforms(TD->getSatisfiedProtocolRequirements(/*Sorted=*/true), Consumer); } else if (auto *VD = dyn_cast(D)) { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index bde5d26d096e7..eb90ea0125e6c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -27,6 +27,7 @@ #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/DiagnosticsSIL.h" #include "swift/Basic/SourceManager.h" #include "swift/Demangling/ManglingUtils.h" #include "swift/Frontend/Frontend.h" @@ -83,7 +84,13 @@ void EditorDiagConsumer::handleDiagnostic(SourceManager &SM, } // Filter out benign diagnostics for editing. - if (Info.ID == diag::lex_editor_placeholder.ID) + // oslog_invalid_log_message is spuriously output for live issues as modules + // in the index build are built without function bodies (including inline + // functions). OSLogOptimization expects SIL for bodies and hence errors + // when there isn't any. Ignore in live issues for now and re-evaluate if + // this (not having SIL for inline functions) becomes a more widespread issue. + if (Info.ID == diag::lex_editor_placeholder.ID || + Info.ID == diag::oslog_invalid_log_message.ID) return; bool IsNote = (Info.Kind == DiagnosticKind::Note); @@ -100,6 +107,14 @@ void EditorDiagConsumer::handleDiagnostic(SourceManager &SM, DiagnosticEntryInfo SKInfo; + SKInfo.ID = DiagnosticEngine::diagnosticIDStringFor(Info.ID).str(); + + if (Info.Category == "deprecation") { + SKInfo.Categories.push_back(DiagnosticCategory::Deprecation); + } else if (Info.Category == "no-usage") { + SKInfo.Categories.push_back(DiagnosticCategory::NoUsage); + } + // Actually substitute the diagnostic arguments into the diagnostic text. llvm::SmallString<256> Text; { @@ -677,7 +692,8 @@ class SwiftDocumentSyntaxInfo { std::string PrimaryFile; /// Whether or not the AST stored in the source file is up-to-date or just an /// artifact of incremental syntax parsing - bool HasUpToDateAST; + bool IncrementalParsingEnabled; + bool IsParsed; public: SwiftDocumentSyntaxInfo(const CompilerInvocation &CompInv, @@ -713,13 +729,16 @@ class SwiftDocumentSyntaxInfo { Parser->getParser().Context.evaluator); Parser->getDiagnosticEngine().addConsumer(DiagConsumer); - // If there is a syntax parsing cache, incremental syntax parsing is - // performed and thus the generated AST may not be up-to-date. - HasUpToDateAST = CompInv.getMainFileSyntaxParsingCache() == nullptr; + IncrementalParsingEnabled = + CompInv.getMainFileSyntaxParsingCache() != nullptr; + IsParsed = false; } - void parse() { - Parser->parse(); + void parseIfNeeded() { + if (!IsParsed) { + Parser->parse(); + IsParsed = true; + } } SourceFile &getSourceFile() { @@ -738,7 +757,7 @@ class SwiftDocumentSyntaxInfo { return SM; } - bool hasUpToDateAST() { return HasUpToDateAST; } + bool isIncrementalParsingEnabled() { return IncrementalParsingEnabled; } ArrayRef getDiagnostics() { return DiagConsumer.getDiagnosticsForBuffer(BufferID); @@ -1074,6 +1093,7 @@ struct SwiftEditorDocument::Implementation { std::shared_ptr getSyntaxInfo() { llvm::sys::ScopedLock L(AccessMtx); + SyntaxInfo->parseIfNeeded(); return SyntaxInfo; } @@ -1949,9 +1969,10 @@ void SwiftEditorDocument::updateSemaInfo() { ::updateSemaInfo(SemanticInfo, EditableBuffer); } -void SwiftEditorDocument::parse(ImmutableTextSnapshotRef Snapshot, - SwiftLangSupport &Lang, bool BuildSyntaxTree, - SyntaxParsingCache *SyntaxCache) { +void SwiftEditorDocument::resetSyntaxInfo(ImmutableTextSnapshotRef Snapshot, + SwiftLangSupport &Lang, + bool BuildSyntaxTree, + SyntaxParsingCache *SyntaxCache) { llvm::sys::ScopedLock L(Impl.AccessMtx); assert(Impl.SemanticInfo && "Impl.SemanticInfo must be set"); @@ -1982,8 +2003,6 @@ void SwiftEditorDocument::parse(ImmutableTextSnapshotRef Snapshot, // Access to Impl.SyntaxInfo is guarded by Impl.AccessMtx Impl.SyntaxInfo.reset( new SwiftDocumentSyntaxInfo(CompInv, Snapshot, Args, Impl.FilePath)); - - Impl.SyntaxInfo->parse(); } static UIdent SemaDiagStage("source.diagnostic.stage.swift.sema"); @@ -1991,6 +2010,7 @@ static UIdent ParseDiagStage("source.diagnostic.stage.swift.parse"); void SwiftEditorDocument::readSyntaxInfo(EditorConsumer &Consumer, bool ReportDiags) { llvm::sys::ScopedLock L(Impl.AccessMtx); + Impl.SyntaxInfo->parseIfNeeded(); Impl.ParserDiagnostics = Impl.SyntaxInfo->getDiagnostics(); if (ReportDiags) { @@ -2105,8 +2125,8 @@ SwiftEditorDocument::getSyntaxTree() const { std::string SwiftEditorDocument::getFilePath() const { return Impl.FilePath; } -bool SwiftEditorDocument::hasUpToDateAST() const { - return Impl.SyntaxInfo->hasUpToDateAST(); +bool SwiftEditorDocument::isIncrementalParsingEnabled() const { + return Impl.SyntaxInfo->isIncrementalParsingEnabled(); } llvm::IntrusiveRefCntPtr @@ -2295,17 +2315,6 @@ ImmutableTextSnapshotRef SwiftEditorDocument::getLatestSnapshot() const { return Impl.EditableBuffer->getSnapshot(); } -std::pair -SwiftEditorDocument::getLineAndColumnInBuffer(unsigned Offset) { - llvm::sys::ScopedLock L(Impl.AccessMtx); - - auto SyntaxInfo = Impl.getSyntaxInfo(); - auto &SM = SyntaxInfo->getSourceManager(); - - auto Loc = SM.getLocForOffset(SyntaxInfo->getBufferID(), Offset); - return SM.getLineAndColumnInBuffer(Loc); -} - void SwiftEditorDocument::reportDocumentStructure(SourceFile &SrcFile, EditorConsumer &Consumer) { ide::SyntaxModelContext ModelContext(SrcFile); @@ -2335,7 +2344,7 @@ void SwiftLangSupport::editorOpen( EditorDoc = new SwiftEditorDocument(Name, *this, fileSystem); Snapshot = EditorDoc->initializeText( Buf, Args, Consumer.needsSemanticInfo(), fileSystem); - EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled()); + EditorDoc->resetSyntaxInfo(Snapshot, *this, Consumer.syntaxTreeEnabled()); if (EditorDocuments->getOrUpdate(Name, *this, EditorDoc)) { // Document already exists, re-initialize it. This should only happen // if we get OPEN request while the previous document is not closed. @@ -2349,13 +2358,20 @@ void SwiftLangSupport::editorOpen( if (!Snapshot) { Snapshot = EditorDoc->initializeText( Buf, Args, Consumer.needsSemanticInfo(), fileSystem); - EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled()); + EditorDoc->resetSyntaxInfo(Snapshot, *this, Consumer.syntaxTreeEnabled()); } if (Consumer.needsSemanticInfo()) { EditorDoc->updateSemaInfo(); } + if (!Consumer.documentStructureEnabled() && + !Consumer.syntaxMapEnabled() && + !Consumer.diagnosticsEnabled() && + !Consumer.syntaxTreeEnabled()) { + return; + } + EditorDoc->readSyntaxInfo(Consumer, /*ReportDiags=*/true); if (Consumer.syntaxTreeEnabled()) { @@ -2402,7 +2418,7 @@ void verifyIncrementalParse(SwiftEditorDocumentRef EditorDoc, SwiftDocumentSyntaxInfo ScratchSyntaxInfo(Invocation, EditorDoc->getLatestSnapshot(), Args, EditorDoc->getFilePath()); - ScratchSyntaxInfo.parse(); + ScratchSyntaxInfo.parseIfNeeded(); // Dump the from-scratch syntax tree std::string FromScratchTreeString; @@ -2507,6 +2523,15 @@ void SwiftLangSupport::editorReplaceText(StringRef Name, } // If client doesn't need any information, we doen't need to parse it. + + + SyntaxParsingCache *SyntaxCachePtr = nullptr; + if (SyntaxCache.hasValue()) { + SyntaxCachePtr = SyntaxCache.getPointer(); + } + EditorDoc->resetSyntaxInfo(Snapshot, *this, Consumer.syntaxTreeEnabled(), + SyntaxCachePtr); + if (!Consumer.documentStructureEnabled() && !Consumer.syntaxMapEnabled() && !Consumer.diagnosticsEnabled() && @@ -2514,12 +2539,6 @@ void SwiftLangSupport::editorReplaceText(StringRef Name, return; } - SyntaxParsingCache *SyntaxCachePtr = nullptr; - if (SyntaxCache.hasValue()) { - SyntaxCachePtr = SyntaxCache.getPointer(); - } - EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled(), - SyntaxCachePtr); // Do not report syntactic diagnostics; will be handled in readSemanticInfo. EditorDoc->readSyntaxInfo(Consumer, /*ReportDiags=*/false); @@ -2578,11 +2597,12 @@ void SwiftLangSupport::editorFormatText(StringRef Name, unsigned Line, return; } - if (!EditorDoc->hasUpToDateAST()) { - // An up-to-date AST is needed for formatting. If it does not exist, fall - // back to a full reparse of the file - EditorDoc->parse(EditorDoc->getLatestSnapshot(), *this, - /*BuildSyntaxTree=*/true); + if (EditorDoc->isIncrementalParsingEnabled()) { + // If incremental parsing is enabled, AST is not updated properly. Fall + // back to a full reparse of the file. + EditorDoc->resetSyntaxInfo(EditorDoc->getLatestSnapshot(), *this, + /*BuildSyntaxTree=*/true, + /*SyntaxCache=*/nullptr); } EditorDoc->formatText(Line, Length, Consumer); @@ -2616,5 +2636,13 @@ void SwiftLangSupport::editorExpandPlaceholder(StringRef Name, unsigned Offset, return; } + if (EditorDoc->isIncrementalParsingEnabled()) { + // If incremental parsing is enabled, AST is not updated properly. Fall + // back to a full reparse of the file. + EditorDoc->resetSyntaxInfo(EditorDoc->getLatestSnapshot(), *this, + /*BuildSyntaxTree=*/true, + /*SyntaxCache=*/nullptr); + } + EditorDoc->expandPlaceholder(Offset, Length, Consumer); } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 0a7898064f12b..29975f56c186d 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -105,11 +105,10 @@ class SwiftEditorDocument : void removeCachedAST(); ImmutableTextSnapshotRef getLatestSnapshot() const; - std::pair getLineAndColumnInBuffer(unsigned Offset); - void parse(ImmutableTextSnapshotRef Snapshot, SwiftLangSupport &Lang, - bool BuildSyntaxTree, - swift::SyntaxParsingCache *SyntaxCache = nullptr); + void resetSyntaxInfo(ImmutableTextSnapshotRef Snapshot, + SwiftLangSupport &Lang, bool BuildSyntaxTree, + swift::SyntaxParsingCache *SyntaxCache = nullptr); void readSyntaxInfo(EditorConsumer &consumer, bool ReportDiags); void readSemanticInfo(ImmutableTextSnapshotRef Snapshot, EditorConsumer& Consumer); @@ -129,7 +128,7 @@ class SwiftEditorDocument : /// Whether or not the AST stored for this document is up-to-date or just an /// artifact of incremental syntax parsing - bool hasUpToDateAST() const; + bool isIncrementalParsingEnabled() const; /// Returns the virtual filesystem associated with this document. llvm::IntrusiveRefCntPtr getFileSystem() const; @@ -604,6 +603,12 @@ class SwiftLangSupport : public LangSupport { bool CanonicalType, std::function &)> Receiver) override; + void collectVariableTypes( + StringRef FileName, ArrayRef Args, + Optional Offset, Optional Length, + std::function &)> Receiver) + override; + void semanticRefactoring(StringRef Filename, SemanticRefactoringInfo Info, ArrayRef Args, CategorizedEditsReceiver Receiver) override; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index c0805d69f1da4..e4f5388033ccb 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -352,6 +352,7 @@ class FullyAnnotatedDeclarationPrinter final : public XMLEscapingPrinter { return ExternalParamNameTag; return "tuple.element.argument_label"; case PrintNameContext::Keyword: + case PrintNameContext::IntroducerKeyword: return SyntaxKeywordTag; case PrintNameContext::GenericParameter: return GenericParamNameTag; @@ -639,7 +640,7 @@ static void mapLocToLatestSnapshot( } std::tie(Location.Line, Location.Column) = - EditorDoc->getLineAndColumnInBuffer(Location.Offset); + LatestSnap->getBuffer()->getLineAndColumn(Location.Offset); } @@ -959,6 +960,7 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo, /*EmitSynthesizedMembers*/ false, /*PrintMessages*/ false, /*SkipInheritedDocs*/ false, + /*IncludeSPISymbols*/ true, }; symbolgraphgen::printSymbolGraphForDecl(DInfo.VD, DInfo.BaseType, @@ -1057,6 +1059,8 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo, // differentiate them. PrintOptions PO; PO.SkipAttributes = true; + PO.PrintStaticKeyword = false; + PO.PrintSelfAccessKindKeyword = false; PO.SkipIntroducerKeywords = true; PO.ArgAndParamPrinting = PrintOptions::ArgAndParamPrintingMode::ArgumentOnly; @@ -1447,24 +1451,24 @@ static void resolveCursor( Range.Line = Pair.first; Range.Column = Pair.second; Range.Length = Length; - bool RangeStartMayNeedRename = false; + bool CollectRangeStartRefactorings = false; collectAvailableRefactorings(&AstUnit->getPrimarySourceFile(), Range, - RangeStartMayNeedRename, Kinds, {}); + CollectRangeStartRefactorings, Kinds, {}); for (RefactoringKind Kind : Kinds) { Actions.emplace_back(SwiftLangSupport::getUIDForRefactoringKind(Kind), getDescriptiveRefactoringKindName(Kind), /*UnavailableReason*/ StringRef()); } - if (!RangeStartMayNeedRename) { - // If Length is given, then the cursor-info request should only about - // collecting available refactorings for the range. + if (!CollectRangeStartRefactorings) { + // If Length is given then this request is only for refactorings, + // return straight away unless we need cursor based refactorings as + // well. CursorInfoData Data; Data.AvailableActions = llvm::makeArrayRef(Actions); Receiver(RequestResult::fromResult(Data)); return; } - // If the range start may need rename, we fall back to a regular cursor - // info request to get the available rename kinds. + // Fall through to collect cursor based refactorings } auto *File = &AstUnit->getPrimarySourceFile(); @@ -1473,12 +1477,6 @@ static void resolveCursor( CursorInfoRequest{CursorInfoOwner(File, Loc)}, ResolvedCursorInfo()); - if (CursorInfo.isInvalid()) { - CursorInfoData Info; - Info.InternalDiagnostic = "Unable to resolve cursor info."; - Receiver(RequestResult::fromResult(Info)); - return; - } CompilerInvocation CompInvok; ASTInvok->applyTo(CompInvok); @@ -1525,9 +1523,15 @@ static void resolveCursor( Receiver(RequestResult::fromResult(Info)); return; } - case CursorInfoKind::Invalid: { - llvm_unreachable("bad sema token kind"); - } + case CursorInfoKind::Invalid: + CursorInfoData Data; + if (Actionables) { + Data.AvailableActions = llvm::makeArrayRef(Actions); + } else { + Data.InternalDiagnostic = "Unable to resolve cursor info."; + } + Receiver(RequestResult::fromResult(Data)); + return; } } @@ -1788,8 +1792,10 @@ void SwiftLangSupport::getCursorInfo( std::string Error; SwiftInvocationRef Invok = ASTMgr->getInvocation(Args, InputFile, fileSystem, Error); + if (!Error.empty()) { + LOG_WARN_FUNC("error creating ASTInvocation: " << Error); + } if (!Invok) { - LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult::fromError(Error)); return; } @@ -2365,3 +2371,79 @@ void SwiftLangSupport::collectExpressionTypes(StringRef FileName, &OncePerASTToken, llvm::vfs::getRealFileSystem()); } + +void SwiftLangSupport::collectVariableTypes( + StringRef FileName, ArrayRef Args, Optional Offset, + Optional Length, + std::function &)> Receiver) { + std::string Error; + SwiftInvocationRef Invok = ASTMgr->getInvocation(Args, FileName, Error); + if (!Invok) { + LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); + Receiver(RequestResult::fromError(Error)); + return; + } + assert(Invok); + + class VariableTypeCollectorASTConsumer : public SwiftASTConsumer { + private: + std::function &)> Receiver; + Optional Offset; + Optional Length; + + public: + VariableTypeCollectorASTConsumer( + std::function &)> + Receiver, + Optional Offset, Optional Length) + : Receiver(std::move(Receiver)), Offset(Offset), Length(Length) {} + + void handlePrimaryAST(ASTUnitRef AstUnit) override { + auto &CompInst = AstUnit->getCompilerInstance(); + auto *SF = CompInst.getPrimarySourceFile(); + + // Construct the range for which variable types are to be queried. If + // offset/length are unset, the (default) range will be used, which + // corresponds to the entire document. + SourceRange Range; + if (Offset.hasValue() && Length.hasValue()) { + auto &SM = CompInst.getSourceMgr(); + unsigned BufferID = SF->getBufferID().getValue(); + SourceLoc Start = Lexer::getLocForStartOfToken(SM, BufferID, *Offset); + SourceLoc End = + Lexer::getLocForStartOfToken(SM, BufferID, *Offset + *Length); + Range = SourceRange(Start, End); + } + + std::vector Infos; + std::string TypeBuffer; + llvm::raw_string_ostream OS(TypeBuffer); + VariableTypesInFile Result; + + collectVariableType(*SF, Range, Infos, OS); + + for (auto Info : Infos) { + Result.Results.push_back({Info.Offset, Info.Length, Info.TypeOffset, Info.HasExplicitType}); + } + Result.TypeBuffer = OS.str(); + Receiver(RequestResult::fromResult(Result)); + } + + void cancelled() override { + Receiver(RequestResult::cancelled()); + } + + void failed(StringRef Error) override { + Receiver(RequestResult::fromError(Error)); + } + }; + + auto Collector = std::make_shared( + Receiver, Offset, Length); + /// FIXME: When request cancellation is implemented and Xcode adopts it, + /// don't use 'OncePerASTToken'. + static const char OncePerASTToken = 0; + getASTManager()->processASTAsync(Invok, std::move(Collector), + &OncePerASTToken, + llvm::vfs::getRealFileSystem()); +} diff --git a/tools/SourceKit/tools/sourcekitd-test/Options.td b/tools/SourceKit/tools/sourcekitd-test/Options.td index b1e346079a1e0..296ccfbfbbf6c 100644 --- a/tools/SourceKit/tools/sourcekitd-test/Options.td +++ b/tools/SourceKit/tools/sourcekitd-test/Options.td @@ -134,6 +134,9 @@ def cancel_on_subsequent_request_EQ : Joined<["-"], "cancel-on-subsequent-reques def time_request : Flag<["-"], "time-request">, HelpText<"Print the time taken to process the request">; +def measure_instructions : Flag<["-"], "measure-instructions">, + HelpText<"Measure how many instructions the execution of this request took in the SourceKit process.">; + def repeat_request : Separate<["-"], "repeat-request">, HelpText<"Repeat the request n times">, MetaVarName<"">; def repeat_request_EQ : Joined<["-"], "repeat-request=">, Alias; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index b65a1903914e2..d3a621dbdbef5 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -143,31 +143,67 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { .Case("find-rename-ranges", SourceKitRequest::FindRenameRanges) .Case("find-local-rename-ranges", SourceKitRequest::FindLocalRenameRanges) .Case("translate", SourceKitRequest::NameTranslation) - .Case("local-rename", SourceKitRequest::LocalRename) - .Case("extract-expr", SourceKitRequest::ExtractExpr) - .Case("extract-repeated", SourceKitRequest::ExtractRepeatedExpr) - .Case("extract-func", SourceKitRequest::ExtractFunction) - .Case("fill-stub", SourceKitRequest::FillProtocolStub) - .Case("expand-default", SourceKitRequest::ExpandDefault) - .Case("localize-string", SourceKitRequest::LocalizeString) .Case("markup-xml", SourceKitRequest::MarkupToXML) .Case("stats", SourceKitRequest::Statistics) .Case("track-compiles", SourceKitRequest::EnableCompileNotifications) .Case("collect-type", SourceKitRequest::CollectExpresstionType) + .Case("collect-var-type", SourceKitRequest::CollectVariableType) .Case("global-config", SourceKitRequest::GlobalConfiguration) .Case("dependency-updated", SourceKitRequest::DependencyUpdated) +#define SEMANTIC_REFACTORING(KIND, NAME, ID) .Case("refactoring." #ID, SourceKitRequest::KIND) +#include "swift/IDE/RefactoringKinds.def" .Default(SourceKitRequest::None); if (Request == SourceKitRequest::None) { llvm::errs() << "error: invalid request '" << InputArg->getValue() - << "'\nexpected one of " - << "version/demangle/mangle/index/complete/complete.open/complete.cursor/" - "complete.update/complete.cache.ondisk/complete.cache.setpopularapi/" - "cursor/related-idents/syntax-map/structure/format/expand-placeholder/" - "doc-info/sema/interface-gen/interface-gen-openfind-usr/find-interface/" - "open/close/edit/print-annotations/print-diags/extract-comment/module-groups/" - "range/syntactic-rename/find-rename-ranges/translate/markup-xml/stats/" - "track-compiles/collect-type\n"; + << "'\nexpected one of " + << "- version\n" + << "- compiler-version\n" + << "- demangle\n" + << "- mangle\n" + << "- index\n" + << "- complete\n" + << "- complete.open\n" + << "- complete.close\n" + << "- complete.update\n" + << "- complete.cache.ondisk\n" + << "- complete.setpopularapi\n" + << "- typecontextinfo\n" + << "- conformingmethods\n" + << "- cursor\n" + << "- related-idents\n" + << "- syntax-map\n" + << "- syntax-tree\n" + << "- structure\n" + << "- format\n" + << "- expand-placeholder\n" + << "- doc-info\n" + << "- sema\n" + << "- interface-gen\n" + << "- interface-gen-open\n" + << "- find-usr\n" + << "- find-interface\n" + << "- open\n" + << "- close\n" + << "- edit\n" + << "- print-annotations\n" + << "- print-diags\n" + << "- extract-comment\n" + << "- module-groups\n" + << "- range\n" + << "- syntactic-rename\n" + << "- find-rename-ranges\n" + << "- find-local-rename-ranges\n" + << "- translate\n" + << "- markup-xml\n" + << "- stats\n" + << "- track-compiles\n" + << "- collect-type\n" + << "- global-config\n" + << "- dependency-updated\n" +#define SEMANTIC_REFACTORING(KIND, NAME, ID) << "- refactoring." #ID "\n" +#include "swift/IDE/RefactoringKinds.def" + "\n"; return true; } break; @@ -346,6 +382,10 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { timeRequest = true; break; + case OPT_measure_instructions: + measureInstructions = true; + break; + case OPT_repeat_request: if (StringRef(InputArg->getValue()).getAsInteger(10, repeatRequest)) { llvm::errs() << "error: expected integer for 'cancel-on-subsequent-request'\n"; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index ba5fc8fd435cb..2da39d6f99388 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -64,6 +64,7 @@ enum class SourceKitRequest { SyntaxTree, EnableCompileNotifications, CollectExpresstionType, + CollectVariableType, GlobalConfiguration, DependencyUpdated, #define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND, @@ -113,6 +114,7 @@ struct TestOptions { bool CollectActionables = false; bool isAsyncRequest = false; bool timeRequest = false; + bool measureInstructions = false; bool DisableImplicitConcurrencyModuleImport = false; llvm::Optional CompletionCheckDependencyInterval; unsigned repeatRequest = 1; diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 5e3eff6949f20..1e446c4d644ed 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -67,6 +67,9 @@ static void printNameTranslationInfo(sourcekitd_variant_t Info, llvm::raw_ostrea static void printRangeInfo(sourcekitd_variant_t Info, StringRef Filename, llvm::raw_ostream &OS); static void printExpressionType(sourcekitd_variant_t Info, llvm::raw_ostream &OS); +static void printVariableType(sourcekitd_variant_t Info, + llvm::MemoryBuffer *SourceBuf, + llvm::raw_ostream &OS); static void printDocInfo(sourcekitd_variant_t Info, StringRef Filename); static void printInterfaceGen(sourcekitd_variant_t Info, bool CheckASCII); static void printSemanticInfo(); @@ -489,6 +492,49 @@ static int handleTestInvocation(ArrayRef Args, return 0; } +static void setRefactoringFields(sourcekitd_object_t &Req, TestOptions Opts, + sourcekitd_uid_t RefactoringKind, + llvm::MemoryBuffer *SourceBuf) { + if (Opts.Offset && !Opts.Line && !Opts.Col) { + auto LineCol = resolveToLineCol(Opts.Offset, SourceBuf); + Opts.Line = LineCol.first; + Opts.Col = LineCol.second; + } + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, + RequestSemanticRefactoring); + sourcekitd_request_dictionary_set_uid(Req, KeyActionUID, RefactoringKind); + sourcekitd_request_dictionary_set_string(Req, KeyName, Opts.Name.c_str()); + sourcekitd_request_dictionary_set_int64(Req, KeyLine, Opts.Line); + sourcekitd_request_dictionary_set_int64(Req, KeyColumn, Opts.Col); + sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length); +} + +/// Returns the number of instructions executed by the SourceKit process since +/// its launch. If SourceKit is running in-process this is the instruction count +/// of the current process. If it's running out-of process it is the instruction +/// count of the XPC process. +int64_t getSourceKitInstructionCount() { + sourcekitd_object_t Req = + sourcekitd_request_dictionary_create(nullptr, nullptr, 0); + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestStatistics); + sourcekitd_response_t Resp = sourcekitd_send_request_sync(Req); + sourcekitd_variant_t Info = sourcekitd_response_get_value(Resp); + sourcekitd_variant_t Results = + sourcekitd_variant_dictionary_get_value(Info, KeyResults); + __block size_t InstructionCount = 0; + sourcekitd_variant_array_apply( + Results, ^bool(size_t index, sourcekitd_variant_t value) { + auto UID = sourcekitd_variant_dictionary_get_uid(value, KeyKind); + if (UID == KindStatInstructionCount) { + InstructionCount = + sourcekitd_variant_dictionary_get_int64(value, KeyValue); + return false; + } + return true; + }); + return InstructionCount; +} + static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { if (!Opts.JsonRequestPath.empty()) return handleJsonRequestPath(Opts.JsonRequestPath, Opts); @@ -721,16 +767,21 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { break; } -#define SEMANTIC_REFACTORING(KIND, NAME, ID) case SourceKitRequest::KIND: \ - { \ - sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestSemanticRefactoring); \ - sourcekitd_request_dictionary_set_uid(Req, KeyActionUID, KindRefactoring##KIND); \ - sourcekitd_request_dictionary_set_string(Req, KeyName, Opts.Name.c_str()); \ - sourcekitd_request_dictionary_set_int64(Req, KeyLine, Opts.Line); \ - sourcekitd_request_dictionary_set_int64(Req, KeyColumn, Opts.Col); \ - sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length); \ - break; \ + case SourceKitRequest::CollectVariableType: { + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, + RequestCollectVariableType); + if (Opts.Length) { + sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); + sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length); } + addRequestOptionsDirect(Req, Opts); + break; + } + +#define SEMANTIC_REFACTORING(KIND, NAME, ID) \ + case SourceKitRequest::KIND: \ + setRefactoringFields(Req, Opts, KindRefactoring##KIND, SourceBuf.get()); \ + break; #include "swift/IDE/RefactoringKinds.def" case SourceKitRequest::MarkupToXML: { @@ -834,11 +885,24 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { break; case SourceKitRequest::ExpandPlaceholder: - sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); - sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); - sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false); - sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false); - sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema); + if (Opts.Length) { + // Single placeholder by location. + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorExpandPlaceholder); + sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); + sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); + sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length); + } else { + if (ByteOffset) { + llvm::errs() << "Missing '-length '\n"; + return 1; + } + // Expand all placeholders. + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); + sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); + sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false); + sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false); + sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema); + } break; case SourceKitRequest::SyntaxTree: @@ -1102,8 +1166,19 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { sourcekitd_request_release(files); } + int64_t BeforeInstructions; + if (Opts.measureInstructions) + BeforeInstructions = getSourceKitInstructionCount(); + if (!Opts.isAsyncRequest) { sourcekitd_response_t Resp = sendRequestSync(Req, Opts); + + if (Opts.measureInstructions) { + int64_t AfterInstructions = getSourceKitInstructionCount(); + llvm::errs() << "request instructions: " + << (AfterInstructions - BeforeInstructions); + } + sourcekitd_request_release(Req); return handleResponse(Resp, Opts, SemaName, std::move(SourceBuf), &InitOpts) @@ -1132,6 +1207,12 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { "-async not supported when sourcekitd is built without blocks support"); #endif + if (Opts.measureInstructions) { + int64_t AfterInstructions = getSourceKitInstructionCount(); + llvm::errs() << "request instructions: " + << (AfterInstructions - BeforeInstructions); + } + sourcekitd_request_release(Req); return 0; } @@ -1230,6 +1311,10 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, printExpressionType(Info, llvm::outs()); break; + case SourceKitRequest::CollectVariableType: + printVariableType(Info, SourceBuf.get(), llvm::outs()); + break; + case SourceKitRequest::DocInfo: printDocInfo(Info, SourceFile); break; @@ -1341,7 +1426,13 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, break; case SourceKitRequest::ExpandPlaceholder: - expandPlaceholders(SourceBuf.get(), llvm::outs()); + if (Opts.Length) { + // Single placeholder by location. + sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO); + } else { + // Expand all placeholders. + expandPlaceholders(SourceBuf.get(), llvm::outs()); + } break; case SourceKitRequest::ModuleGroups: printModuleGroupNames(Info, llvm::outs()); @@ -1915,6 +2006,36 @@ static void printExpressionType(sourcekitd_variant_t Info, llvm::raw_ostream &OS OS << "\n"; } +static void printVariableType(sourcekitd_variant_t Info, + llvm::MemoryBuffer *SourceBuf, + llvm::raw_ostream &OS) { + auto TypeBuffer = + sourcekitd_variant_dictionary_get_value(Info, KeyVariableTypeList); + unsigned Count = sourcekitd_variant_array_get_count(TypeBuffer); + if (!Count) { + OS << "cannot find variable types in the file\n"; + return; + } + OS << "\n"; + for (unsigned i = 0; i != Count; ++i) { + sourcekitd_variant_t Item = sourcekitd_variant_array_get_value(TypeBuffer, i); + unsigned Offset = sourcekitd_variant_dictionary_get_int64(Item, KeyVariableOffset); + unsigned Length = sourcekitd_variant_dictionary_get_int64(Item, KeyVariableLength); + auto Start = resolveToLineCol(Offset, SourceBuf); + auto End = resolveToLineCol(Offset + Length, SourceBuf); + bool HasExplicitType = sourcekitd_variant_dictionary_get_bool(Item, KeyVariableTypeExplicit); + auto PrintedType = sourcekitd_variant_dictionary_get_string(Item, KeyVariableType); + OS << "(" + << Start.first << ":" << Start.second + << ", " + << End.first << ":" << End.second + << "): " + << PrintedType + << " (explicit type: " << HasExplicitType << ")\n"; + } + OS << "\n"; +} + static void printFoundInterface(sourcekitd_variant_t Info, llvm::raw_ostream &OS) { const char *Name = sourcekitd_variant_dictionary_get_string(Info, diff --git a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h index c4df14e089860..d641edce7914f 100644 --- a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h +++ b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h @@ -60,6 +60,7 @@ enum class CustomBufferKind { DocStructureElementArray, AttributesArray, ExpressionTypeArray, + VariableTypeArray, RawData }; @@ -83,6 +84,7 @@ class ResponseBuilder { void set(SourceKit::UIdent Key, int64_t val); void set(SourceKit::UIdent Key, llvm::ArrayRef Strs); void set(SourceKit::UIdent Key, llvm::ArrayRef Strs); + void set(SourceKit::UIdent Key, llvm::ArrayRef UIDs); void setBool(SourceKit::UIdent Key, bool val); Array setArray(SourceKit::UIdent Key); Dictionary setDictionary(SourceKit::UIdent Key); diff --git a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/VariableTypeArray.h b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/VariableTypeArray.h new file mode 100644 index 0000000000000..b63c56fda2122 --- /dev/null +++ b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/VariableTypeArray.h @@ -0,0 +1,41 @@ +//===--- VariableTypeArray.h - ----------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SOURCEKITD_VARIABLE_TYPE_ARRAY_H +#define LLVM_SOURCEKITD_VARIABLE_TYPE_ARRAY_H + +#include "sourcekitd/Internal.h" + +namespace SourceKit { +struct VariableType; +} + +namespace sourcekitd { +VariantFunctions *getVariantFunctionsForVariableTypeArray(); + +class VariableTypeArrayBuilder { +public: + VariableTypeArrayBuilder(llvm::StringRef PrintedTypes); + ~VariableTypeArrayBuilder(); + + void add(const SourceKit::VariableType &VarType); + std::unique_ptr createBuffer(); + static VariantFunctions Funcs; + +private: + struct Implementation; + Implementation &Impl; +}; + +} // namespace sourcekitd + +#endif diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt index a5766a5498d61..0dc3235e11e62 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt @@ -12,6 +12,7 @@ add_sourcekit_library(sourcekitdAPI sourcekitdAPI-Common.cpp TokenAnnotationsArray.cpp ExpressionTypeArray.cpp + VariableTypeArray.cpp UIDHandling.cpp ) target_link_libraries(sourcekitdAPI PRIVATE diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index e4708f9474586..4582b55b526d9 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -16,6 +16,7 @@ #include "sourcekitd/DocSupportAnnotationArray.h" #include "sourcekitd/TokenAnnotationsArray.h" #include "sourcekitd/ExpressionTypeArray.h" +#include "sourcekitd/VariableTypeArray.h" #include "SourceKit/Core/Context.h" #include "SourceKit/Core/LangSupport.h" @@ -29,6 +30,7 @@ #include "swift/Basic/ExponentialGrowthAppendingBinaryByteStream.h" #include "swift/Basic/Mangler.h" +#include "swift/Basic/Statistic.h" #include "swift/Basic/Version.h" #include "swift/Demangling/Demangler.h" #include "swift/Demangling/ManglingMacros.h" @@ -193,6 +195,10 @@ static void reportCursorInfo(const RequestResult &Result, Respon static void reportExpressionTypeInfo(const RequestResult &Result, ResponseReceiver Rec); +static void +reportVariableTypeInfo(const RequestResult &Result, + ResponseReceiver Rec); + static void reportRangeInfo(const RequestResult &Result, ResponseReceiver Rec); static void reportNameInfo(const RequestResult &Result, ResponseReceiver Rec); @@ -918,6 +924,11 @@ void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) { dict.set(KeyValue, stat->value); }; + Statistic instructionCount( + UIdentFromSKDUID(KindStatInstructionCount), + "# of instructions executed since the SourceKit process was started"); + instructionCount.value.store(swift::getInstructionsExecuted()); + addStat(&instructionCount); addStat(&numRequests); addStat(&numSemaRequests); std::for_each(stats.begin(), stats.end(), addStat); @@ -1148,6 +1159,19 @@ static void handleSemanticRequest( }); } + if (ReqUID == RequestCollectVariableType) { + LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); + Optional Offset = Req.getOptionalInt64(KeyOffset).map( + [](int64_t v) -> unsigned { return v; }); + Optional Length = Req.getOptionalInt64(KeyLength).map( + [](int64_t v) -> unsigned { return v; }); + return Lang.collectVariableTypes( + *SourceFile, Args, Offset, Length, + [Rec](const RequestResult &Result) { + reportVariableTypeInfo(Result, Rec); + }); + } + if (ReqUID == RequestFindLocalRenameRanges) { int64_t Line = 0, Column = 0, Length = 0; if (Req.getInt64(KeyLine, Line, /*isOptional=*/false)) @@ -1975,6 +1999,30 @@ static void reportExpressionTypeInfo(const RequestResult Rec(Builder.createResponse()); } +//===----------------------------------------------------------------------===// +// ReportVariableTypeInfo +//===----------------------------------------------------------------------===// + +static void +reportVariableTypeInfo(const RequestResult &Result, + ResponseReceiver Rec) { + if (Result.isCancelled()) + return Rec(createErrorRequestCancelled()); + if (Result.isError()) + return Rec(createErrorRequestFailed(Result.getError())); + + const VariableTypesInFile &Info = Result.value(); + + ResponseBuilder Builder; + auto Dict = Builder.getDictionary(); + VariableTypeArrayBuilder ArrBuilder(Info.TypeBuffer); + for (auto &R : Info.Results) { + ArrBuilder.add(R); + } + Dict.setCustomBuffer(KeyVariableTypeList, ArrBuilder.createBuffer()); + Rec(Builder.createResponse()); +} + //===----------------------------------------------------------------------===// // FindRelatedIdents //===----------------------------------------------------------------------===// @@ -2824,6 +2872,28 @@ static void fillDictionaryForDiagnosticInfo( static void fillDictionaryForDiagnosticInfoBase( ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info) { + if (!Info.ID.empty()) + Elem.set(KeyID, Info.ID); + + if (!Info.Categories.empty()) { + SmallVector CategoryUIDs; + + static UIdent UIDKindDiagDeprecation(KindDiagDeprecation.str()); + static UIdent UIDKindDiagNoUsage(KindDiagNoUsage.str()); + + for (auto C : Info.Categories) { + switch (C) { + case DiagnosticCategory::Deprecation: + CategoryUIDs.push_back(UIDKindDiagDeprecation); + break; + case DiagnosticCategory::NoUsage: + CategoryUIDs.push_back(UIDKindDiagNoUsage); + break; + } + } + Elem.set(KeyCategories, CategoryUIDs); + } + Elem.set(KeyDescription, Info.Description); if (Info.Line != 0) { Elem.set(KeyLine, Info.Line); diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/VariableTypeArray.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/VariableTypeArray.cpp new file mode 100644 index 0000000000000..8ee4d97157dba --- /dev/null +++ b/tools/SourceKit/tools/sourcekitd/lib/API/VariableTypeArray.cpp @@ -0,0 +1,182 @@ +//===---------- VariableTypeArray.cpp -------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// + +#include "sourcekitd/VariableTypeArray.h" +#include "sourcekitd/CompactArray.h" +#include "SourceKit/Core/LLVM.h" +#include "SourceKit/Core/LangSupport.h" +#include "SourceKit/Support/UIdent.h" +#include "DictionaryKeys.h" + +#include "llvm/Support/MemoryBuffer.h" + +using namespace SourceKit; +using namespace sourcekitd; + +namespace { +class VariableTypeReader { + /// A string that contains the types of the variables that are reported by + /// this \c VariableTypeReader. Each type is null-terminated and \c + /// EntryReader references the types using offsets into this string. + const char *PrintedTypes; + // Four unsigned integers for: + // - Variable offset in the source buffer + // - Variable length in the source buffer + // - Offset of printed Variable type inside `PrintedTypes` + // - Whether the variable has an explicit type annotation + CompactArrayReader EntryReader; + + static uint64_t getHeaderValue(char *Buffer, unsigned Index) { + uint64_t HeaderField; + memcpy(&HeaderField, (uint64_t *)Buffer + Index, sizeof(HeaderField)); + return HeaderField; + } + +public: + VariableTypeReader(char *Buffer) + : EntryReader(Buffer + getHeaderValue(Buffer, 0)) { + // Read the printed type string buffer here. + CompactArrayReader Reader(Buffer + getHeaderValue(Buffer, 1)); + Reader.readEntries(0, PrintedTypes); + } + + uint64_t count() const { return EntryReader.getCount(); } + + VariableType getVariable(uint64_t Idx) { + VariableType Result; + unsigned HasExplicitType; + EntryReader.readEntries(Idx, Result.VarOffset, Result.VarLength, + Result.TypeOffset, HasExplicitType); + Result.HasExplicitType = static_cast(HasExplicitType); + return Result; + } + + const char *readPrintedType(unsigned Offset) { return PrintedTypes + Offset; } + + static bool dictionary_apply( + void *Buffer, size_t Index, + llvm::function_ref + Applier) { + VariableTypeReader Reader((char *)Buffer); + auto Result = Reader.getVariable(Index); +#define APPLY(K, Ty, Field) \ + do { \ + sourcekitd_uid_t Key = SKDUIDFromUIdent(K); \ + sourcekitd_variant_t Var = make##Ty##Variant(Field); \ + if (!Applier(Key, Var)) \ + return false; \ + } while (0) + + APPLY(KeyVariableOffset, Int, Result.VarOffset); + APPLY(KeyVariableLength, Int, Result.VarLength); + APPLY(KeyVariableType, String, Reader.readPrintedType(Result.TypeOffset)); + APPLY(KeyVariableTypeExplicit, Bool, Result.HasExplicitType); + return true; + } +}; +} // end of anonymous namespace + +struct VariableTypeArrayBuilder::Implementation { + /// A builder that builds values read by \c EntryReader in \c + /// VariableTypeReader. See \c VariableTypeReader::EntryReader for more info. + CompactArrayBuilder Builder; + /// A builder that builds the \c PrintedTypes string used by \c + /// VariableTypeReader. See \c VariableTypeReader::PrintedTypes for more info. + CompactArrayBuilder StrBuilder; + + Implementation(StringRef PrintedTypes) { StrBuilder.addEntry(PrintedTypes); } + static sourcekitd_variant_type_t get_type(sourcekitd_variant_t var) { + return SOURCEKITD_VARIANT_TYPE_ARRAY; + } + + static sourcekitd_variant_t array_get_value(sourcekitd_variant_t Array, + size_t Index) { + return {{(uintptr_t)&CompactVariantFuncs::Funcs, + (uintptr_t)Array.data[1], Index}}; + } + + // data[0] = VariableTypeArrayBuilder::funcs + // data[1] = custom buffer + static size_t array_get_count(sourcekitd_variant_t Array) { + VariableTypeReader Reader((char *)Array.data[1]); + return Reader.count(); + } + + std::unique_ptr createBuffer(CustomBufferKind Kind) { + std::array Builders = {&Builder, &StrBuilder}; + auto KindSize = sizeof(uint64_t); + size_t HeaderSize = sizeof(uint64_t) * Builders.size(); + auto AllSize = KindSize + HeaderSize; + for (auto *B : Builders) + AllSize += B->sizeInBytes(); + auto Result = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(AllSize); + *reinterpret_cast(Result->getBufferStart()) = (uint64_t)Kind; + + char *Start = Result->getBufferStart() + KindSize; + char *HeaderPtr = Start; + char *Ptr = Start + HeaderSize; + auto addBuilder = [&](CompactArrayBuilderImpl &Buffer) { + uint64_t Offset = Ptr - Start; + memcpy(HeaderPtr, &Offset, sizeof(Offset)); + HeaderPtr += sizeof(Offset); + Ptr += Buffer.copyInto(Ptr); + }; + for (auto *B : Builders) { + addBuilder(*B); + } + assert(Ptr == Result->getBufferEnd()); + return std::move(Result); + } +}; + +VariableTypeArrayBuilder::VariableTypeArrayBuilder(StringRef PrintedTypes) + : Impl(*new Implementation(PrintedTypes)) {} + +VariableTypeArrayBuilder::~VariableTypeArrayBuilder() { delete &Impl; } + +void VariableTypeArrayBuilder::add(const VariableType &VarType) { + Impl.Builder.addEntry(VarType.VarOffset, VarType.VarLength, + VarType.TypeOffset /*Printed type is null-terminated*/, + VarType.HasExplicitType); +} + +std::unique_ptr VariableTypeArrayBuilder::createBuffer() { + return Impl.createBuffer(CustomBufferKind::VariableTypeArray); +} + +VariantFunctions VariableTypeArrayBuilder::Funcs = { + Implementation::get_type, + nullptr /*AnnotArray_array_apply*/, + nullptr /*AnnotArray_array_get_bool*/, + Implementation::array_get_count, + nullptr /*AnnotArray_array_get_int64*/, + nullptr /*AnnotArray_array_get_string*/, + nullptr /*AnnotArray_array_get_uid*/, + Implementation::array_get_value, + nullptr /*AnnotArray_bool_get_value*/, + nullptr /*AnnotArray_dictionary_apply*/, + nullptr /*AnnotArray_dictionary_get_bool*/, + nullptr /*AnnotArray_dictionary_get_int64*/, + nullptr /*AnnotArray_dictionary_get_string*/, + nullptr /*AnnotArray_dictionary_get_value*/, + nullptr /*AnnotArray_dictionary_get_uid*/, + nullptr /*AnnotArray_string_get_length*/, + nullptr /*AnnotArray_string_get_ptr*/, + nullptr /*AnnotArray_int64_get_value*/, + nullptr /*AnnotArray_uid_get_value*/, + nullptr /*Annot_data_get_size*/, + nullptr /*Annot_data_get_ptr*/, +}; + +VariantFunctions *sourcekitd::getVariantFunctionsForVariableTypeArray() { + return &VariableTypeArrayBuilder::Funcs; +} diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-InProc.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-InProc.cpp index 157fd3caca72a..697d7eb849a7a 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-InProc.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-InProc.cpp @@ -18,6 +18,7 @@ #include "sourcekitd/RawData.h" #include "sourcekitd/TokenAnnotationsArray.h" #include "sourcekitd/ExpressionTypeArray.h" +#include "sourcekitd/VariableTypeArray.h" #include "sourcekitd/Logging.h" #include "SourceKit/Core/LLVM.h" #include "SourceKit/Support/UIdent.h" @@ -254,6 +255,7 @@ class SKDCustomData: public SKDObject { case CustomBufferKind::DocStructureElementArray: case CustomBufferKind::AttributesArray: case CustomBufferKind::ExpressionTypeArray: + case CustomBufferKind::VariableTypeArray: return SOURCEKITD_VARIANT_TYPE_ARRAY; case CustomBufferKind::RawData: return SOURCEKITD_VARIANT_TYPE_DATA; @@ -660,6 +662,15 @@ void ResponseBuilder::Dictionary::set(SourceKit::UIdent Key, static_cast(Impl)->set(SKDUIDFromUIdent(Key), ArrayObject); } +void ResponseBuilder::Dictionary::set(SourceKit::UIdent Key, + ArrayRef UIDs) { + auto ArrayObject = new SKDArray(); + for (auto UID : UIDs) { + ArrayObject->set(SOURCEKITD_ARRAY_APPEND, new SKDUID(SKDUIDFromUIdent(UID))); + } + static_cast(Impl)->set(SKDUIDFromUIdent(Key), ArrayObject); +} + void ResponseBuilder::Dictionary::setBool(UIdent Key, bool Val) { static_cast(Impl)->set(SKDUIDFromUIdent(Key), new SKDBool(Val)); } @@ -983,6 +994,9 @@ static sourcekitd_variant_t variantFromSKDObject(SKDObjectRef Object) { case CustomBufferKind::ExpressionTypeArray: return {{ (uintptr_t)getVariantFunctionsForExpressionTypeArray(), (uintptr_t)DataObject->getDataPtr(), 0 }}; + case CustomBufferKind::VariableTypeArray: + return {{ (uintptr_t)getVariantFunctionsForVariableTypeArray(), + (uintptr_t)DataObject->getDataPtr(), 0 }}; case CustomBufferKind::RawData: return {{ (uintptr_t)getVariantFunctionsForRawData(), (uintptr_t)DataObject->getDataPtr(), diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-XPC.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-XPC.cpp index 569b9be7a832c..8e6fba17bd4d4 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-XPC.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-XPC.cpp @@ -16,6 +16,7 @@ #include "sourcekitd/DocSupportAnnotationArray.h" #include "sourcekitd/TokenAnnotationsArray.h" #include "sourcekitd/ExpressionTypeArray.h" +#include "sourcekitd/VariableTypeArray.h" #include "sourcekitd/RawData.h" #include "sourcekitd/RequestResponsePrinterBase.h" #include "SourceKit/Support/UIdent.h" @@ -237,6 +238,16 @@ void ResponseBuilder::Dictionary::set(SourceKit::UIdent Key, xpc_release(arr); } +void ResponseBuilder::Dictionary::set(SourceKit::UIdent Key, + ArrayRef UIDs) { + xpc_object_t arr = xpc_array_create(nullptr, 0); + for (auto UID : UIDs) { + xpc_array_set_uint64(arr, XPC_ARRAY_APPEND, uintptr_t(SKDUIDFromUIdent(UID))); + } + xpc_dictionary_set_value(Impl, Key.c_str(), arr); + xpc_release(arr); +} + void ResponseBuilder::Dictionary::setBool(UIdent Key, bool val) { xpc_dictionary_set_bool(Impl, Key.c_str(), val); } @@ -614,6 +625,7 @@ static sourcekitd_variant_type_t XPCVar_get_type(sourcekitd_variant_t var) { case CustomBufferKind::DocStructureElementArray: case CustomBufferKind::AttributesArray: case CustomBufferKind::ExpressionTypeArray: + case CustomBufferKind::VariableTypeArray: return SOURCEKITD_VARIANT_TYPE_ARRAY; case CustomBufferKind::RawData: return SOURCEKITD_VARIANT_TYPE_DATA; @@ -777,6 +789,9 @@ static sourcekitd_variant_t variantFromXPCObject(xpc_object_t obj) { case CustomBufferKind::ExpressionTypeArray: return {{ (uintptr_t)getVariantFunctionsForExpressionTypeArray(), (uintptr_t)CUSTOM_BUF_START(obj), 0 }}; + case CustomBufferKind::VariableTypeArray: + return {{ (uintptr_t)getVariantFunctionsForVariableTypeArray(), + (uintptr_t)CUSTOM_BUF_START(obj), 0 }}; case sourcekitd::CustomBufferKind::RawData: return {{ (uintptr_t)getVariantFunctionsForRawData(), (uintptr_t)CUSTOM_BUF_START(obj), diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 41ed86909a54f..5da05eca0379f 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -1,20 +1,17 @@ +# The swift-frontend tool add_swift_host_tool(swift-frontend driver.cpp - autolink_extract_main.cpp - modulewrap_main.cpp - swift_api_digester_main.cpp - swift_indent_main.cpp - swift_symbolgraph_extract_main.cpp - swift_api_extract_main.cpp SWIFT_COMPONENT compiler + HAS_LIBSWIFT ) target_link_libraries(swift-frontend - PRIVATE - swiftAPIDigester - swiftDriver - swiftFrontendTool - swiftSymbolGraphGen - LLVMBitstreamReader) + PUBLIC + swiftDriverTool + libswift) + +# Create a `swift-driver` symlinks adjacent to the `swift-frontend` executable +# to ensure that `swiftc` forwards to the standalone driver when invoked. +swift_create_early_driver_symlinks(swift-frontend) swift_create_post_build_symlink(swift-frontend SOURCE "swift-frontend${CMAKE_EXECUTABLE_SUFFIX}" @@ -59,11 +56,6 @@ add_swift_tool_symlink(swift-autolink-extract swift-frontend autolink-driver) add_swift_tool_symlink(swift-indent swift-frontend editor-integration) add_swift_tool_symlink(swift-api-digester swift-frontend compiler) -# If building as part of clang, make sure the headers are installed. -if(NOT SWIFT_BUILT_STANDALONE) - add_dependencies(swift-frontend clang-resource-headers) -endif() - add_dependencies(compiler swift-frontend) swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "bin" diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 38c89f5676b37..2d836f052e45b 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -14,369 +14,8 @@ // //===----------------------------------------------------------------------===// -#include "swift/AST/DiagnosticEngine.h" -#include "swift/AST/DiagnosticsDriver.h" -#include "swift/Basic/LLVMInitialize.h" -#include "swift/Basic/PrettyStackTrace.h" -#include "swift/Basic/Program.h" -#include "swift/Basic/TaskQueue.h" -#include "swift/Basic/SourceManager.h" -#include "swift/Driver/Compilation.h" -#include "swift/Driver/Driver.h" -#include "swift/Driver/FrontendUtil.h" -#include "swift/Driver/Job.h" -#include "swift/Driver/ToolChain.h" -#include "swift/Frontend/Frontend.h" -#include "swift/Frontend/PrintingDiagnosticConsumer.h" -#include "swift/FrontendTool/FrontendTool.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/ConvertUTF.h" -#include "llvm/Support/Errno.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/StringSaver.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Support/raw_ostream.h" - -#include -#include - -#if defined(_WIN32) -#include -#endif - -using namespace swift; -using namespace swift::driver; - -std::string getExecutablePath(const char *FirstArg) { - void *P = (void *)(intptr_t)getExecutablePath; - return llvm::sys::fs::getMainExecutable(FirstArg, P); -} - -/// Run 'swift-autolink-extract'. -extern int autolink_extract_main(ArrayRef Args, const char *Argv0, - void *MainAddr); - -extern int modulewrap_main(ArrayRef Args, const char *Argv0, - void *MainAddr); - -/// Run 'swift-indent' -extern int swift_indent_main(ArrayRef Args, const char *Argv0, - void *MainAddr); - -/// Run 'swift-symbolgraph-extract' -extern int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, -void *MainAddr); - -/// Run 'swift-api-digester' -extern int swift_api_digester_main(ArrayRef Args, - const char *Argv0, void *MainAddr); - -/// Run 'swift-api-extract' -extern int swift_api_extract_main(ArrayRef Args, - const char *Argv0, void *MainAddr); - -/// Determine if the given invocation should run as a "subcommand". -/// -/// Examples of "subcommands" are 'swift build' or 'swift test', which are -/// usually used to invoke the Swift package manager executables 'swift-build' -/// and 'swift-test', respectively. -/// -/// \param ExecName The name of the argv[0] we were invoked as. -/// \param SubcommandName On success, the full name of the subcommand to invoke. -/// \param Args On return, the adjusted program arguments to use. -/// \returns True if running as a subcommand. -static bool shouldRunAsSubcommand(StringRef ExecName, - SmallString<256> &SubcommandName, - const ArrayRef Args, - bool &isRepl) { - assert(!Args.empty()); - - // If we are not run as 'swift', don't do anything special. This doesn't work - // with symlinks with alternate names, but we can't detect 'swift' vs 'swiftc' - // if we try and resolve using the actual executable path. - if (ExecName != "swift") - return false; - - // If there are no program arguments, always invoke as normal. - if (Args.size() == 1) - return false; - - // Otherwise, we have a program argument. If it looks like an option or a - // path, then invoke in interactive mode with the arguments as given. - StringRef FirstArg(Args[1]); - if (FirstArg.startswith("-") || FirstArg.contains('.') || - FirstArg.contains('/')) - return false; - - // Otherwise, we should have some sort of subcommand. Get the subcommand name - // and remove it from the program arguments. - StringRef Subcommand = Args[1]; - - // If the subcommand is the "built-in" 'repl', then use the - // normal driver. - if (Subcommand == "repl") { - isRepl = true; - return false; - } - - // Form the subcommand name. - SubcommandName.assign("swift-"); - SubcommandName.append(Subcommand); - - return true; -} - -static bool shouldDisallowNewDriver(StringRef ExecName, - const ArrayRef argv) { - // We are not invoking the driver, so don't forward. - if (ExecName != "swift" && ExecName != "swiftc") { - return true; - } - // If user specified using the old driver, don't forward. - if (llvm::find_if(argv, [](const char* arg) { - return StringRef(arg) == "-disallow-use-new-driver"; - }) != argv.end()) { - return true; - } - if (llvm::sys::Process::GetEnv("SWIFT_USE_OLD_DRIVER").hasValue()) { - return true; - } - return false; -} - -static bool appendSwiftDriverName(SmallString<256> &buffer) { - assert(llvm::sys::fs::exists(buffer)); - if (auto driverNameOp = llvm::sys::Process::GetEnv("SWIFT_USE_NEW_DRIVER")) { - llvm::sys::path::append(buffer, *driverNameOp); - return true; - } -#ifdef __APPLE__ - // FIXME: use swift-driver as the default driver for all platforms. - llvm::sys::path::append(buffer, "swift-driver"); - if (llvm::sys::fs::exists(buffer)) { - return true; - } - llvm::sys::path::remove_filename(buffer); - llvm::sys::path::append(buffer, "swift-driver-new"); - return true; -#else - return false; -#endif -} - -static int run_driver(StringRef ExecName, - const ArrayRef argv) { - // Handle integrated tools. - if (argv.size() > 1) { - StringRef FirstArg(argv[1]); - if (FirstArg == "-frontend") { - return performFrontend(llvm::makeArrayRef(argv.data()+2, - argv.data()+argv.size()), - argv[0], (void *)(intptr_t)getExecutablePath); - } - if (FirstArg == "-modulewrap") { - return modulewrap_main(llvm::makeArrayRef(argv.data()+2, - argv.data()+argv.size()), - argv[0], (void *)(intptr_t)getExecutablePath); - } - - // Run the integrated Swift frontend when called as "swift-frontend" but - // without a leading "-frontend". - if (!FirstArg.startswith("--driver-mode=") - && ExecName == "swift-frontend") { - return performFrontend(llvm::makeArrayRef(argv.data()+1, - argv.data()+argv.size()), - argv[0], (void *)(intptr_t)getExecutablePath); - } - } - - std::string Path = getExecutablePath(argv[0]); - - PrintingDiagnosticConsumer PDC; - - SourceManager SM; - DiagnosticEngine Diags(SM); - Diags.addConsumer(PDC); - - // Forwarding calls to the swift driver if the C++ driver is invoked as `swift` - // or `swiftc`, and an environment variable SWIFT_USE_NEW_DRIVER is defined. - if (!shouldDisallowNewDriver(ExecName, argv)) { - SmallString<256> NewDriverPath(llvm::sys::path::parent_path(Path)); - if (appendSwiftDriverName(NewDriverPath) && - llvm::sys::fs::exists(NewDriverPath)) { - SmallVector subCommandArgs; - // Rewrite the program argument. - subCommandArgs.push_back(NewDriverPath.c_str()); - if (ExecName == "swiftc") { - subCommandArgs.push_back("--driver-mode=swiftc"); - } else { - assert(ExecName == "swift"); - subCommandArgs.push_back("--driver-mode=swift"); - } - // Push these non-op frontend arguments so the build log can indicate - // the new driver is used. - subCommandArgs.push_back("-Xfrontend"); - subCommandArgs.push_back("-new-driver-path"); - subCommandArgs.push_back("-Xfrontend"); - subCommandArgs.push_back(NewDriverPath.c_str()); - - // Push on the source program arguments - subCommandArgs.insert(subCommandArgs.end(), argv.begin() + 1, argv.end()); - - // Execute the subcommand. - subCommandArgs.push_back(nullptr); - ExecuteInPlace(NewDriverPath.c_str(), subCommandArgs.data()); - - // If we reach here then an error occurred (typically a missing path). - std::string ErrorString = llvm::sys::StrError(); - llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0] - << " (" << ErrorString << ")\n"; - return 2; - } - } - - Driver TheDriver(Path, ExecName, argv, Diags); - switch (TheDriver.getDriverKind()) { - case Driver::DriverKind::AutolinkExtract: - return autolink_extract_main( - TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), - argv[0], (void *)(intptr_t)getExecutablePath); - case Driver::DriverKind::SwiftIndent: - return swift_indent_main( - TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), - argv[0], (void *)(intptr_t)getExecutablePath); - case Driver::DriverKind::SymbolGraph: - return swift_symbolgraph_extract_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); - case Driver::DriverKind::APIExtract: - return swift_api_extract_main( - TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], - (void *)(intptr_t)getExecutablePath); - case Driver::DriverKind::APIDigester: - return swift_api_digester_main( - TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], - (void *)(intptr_t)getExecutablePath); - default: - break; - } - - std::unique_ptr ArgList = - TheDriver.parseArgStrings(ArrayRef(argv).slice(1)); - if (Diags.hadAnyError()) - return 1; - - std::unique_ptr TC = TheDriver.buildToolChain(*ArgList); - if (Diags.hadAnyError()) - return 1; - - std::unique_ptr C = - TheDriver.buildCompilation(*TC, std::move(ArgList)); - - if (Diags.hadAnyError()) - return 1; - - if (C) { - std::unique_ptr TQ = TheDriver.buildTaskQueue(*C); - if (!TQ) - return 1; - return C->performJobs(std::move(TQ)).exitCode; - } - - return 0; -} +#include "swift/DriverTool/DriverTool.h" int main(int argc_, const char **argv_) { -#if defined(_WIN32) - LPWSTR *wargv_ = CommandLineToArgvW(GetCommandLineW(), &argc_); - std::vector utf8Args; - // We use UTF-8 as the internal character encoding. On Windows, - // arguments passed to wmain are encoded in UTF-16 - for (int i = 0; i < argc_; i++) { - const wchar_t *wideArg = wargv_[i]; - int wideArgLen = std::wcslen(wideArg); - utf8Args.push_back(""); - llvm::ArrayRef uRef((const char *)wideArg, - (const char *)(wideArg + wideArgLen)); - llvm::convertUTF16ToUTF8String(uRef, utf8Args[i]); - } - - std::vector utf8CStrs; - llvm::transform(utf8Args, std::back_inserter(utf8CStrs), - std::mem_fn(&std::string::c_str)); - argv_ = utf8CStrs.data(); -#endif - // Expand any response files in the command line argument vector - arguments - // may be passed through response files in the event of command line length - // restrictions. - SmallVector ExpandedArgs(&argv_[0], &argv_[argc_]); - llvm::BumpPtrAllocator Allocator; - llvm::StringSaver Saver(Allocator); - swift::driver::ExpandResponseFilesWithRetry(Saver, ExpandedArgs); - - // Initialize the stack trace using the parsed argument vector with expanded - // response files. - - // PROGRAM_START/InitLLVM overwrites the passed in arguments with UTF-8 - // versions of them on Windows. This also has the effect of overwriting the - // response file expansion. Since we handle the UTF-8 conversion above, we - // pass in a copy and throw away the modifications. - int ThrowawayExpandedArgc = ExpandedArgs.size(); - const char **ThrowawayExpandedArgv = ExpandedArgs.data(); - PROGRAM_START(ThrowawayExpandedArgc, ThrowawayExpandedArgv); - ArrayRef argv(ExpandedArgs); - - PrettyStackTraceSwiftVersion versionStackTrace; - - // Check if this invocation should execute a subcommand. - StringRef ExecName = llvm::sys::path::stem(argv[0]); - SmallString<256> SubcommandName; - bool isRepl = false; - if (shouldRunAsSubcommand(ExecName, SubcommandName, argv, isRepl)) { - // Preserve argv for the stack trace. - SmallVector subCommandArgs(argv.begin(), argv.end()); - subCommandArgs.erase(&subCommandArgs[1]); - // We are running as a subcommand, try to find the subcommand adjacent to - // the executable we are running as. - SmallString<256> SubcommandPath( - llvm::sys::path::parent_path(getExecutablePath(argv[0]))); - llvm::sys::path::append(SubcommandPath, SubcommandName); - - // If we didn't find the tool there, let the OS search for it. - if (!llvm::sys::fs::exists(SubcommandPath)) { - // Search for the program and use the path if found. If there was an - // error, ignore it and just let the exec fail. - auto result = llvm::sys::findProgramByName(SubcommandName); - if (!result.getError()) - SubcommandPath = *result; - } - - // Rewrite the program argument. - subCommandArgs[0] = SubcommandPath.c_str(); - - // Execute the subcommand. - subCommandArgs.push_back(nullptr); - ExecuteInPlace(SubcommandPath.c_str(), subCommandArgs.data()); - - // If we reach here then an error occurred (typically a missing path). - std::string ErrorString = llvm::sys::StrError(); - llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0] - << " (" << ErrorString << ")\n"; - return 2; - } - - if (isRepl) { - // Preserve argv for the stack trace. - SmallVector replArgs(argv.begin(), argv.end()); - replArgs.erase(&replArgs[1]); - return run_driver(ExecName, replArgs); - } else { - return run_driver(ExecName, argv); - } + return swift::mainEntry(argc_, argv_); } diff --git a/tools/libSwiftScan/CMakeLists.txt b/tools/libSwiftScan/CMakeLists.txt index 6e83ab6b620ee..30eeeb2396536 100644 --- a/tools/libSwiftScan/CMakeLists.txt +++ b/tools/libSwiftScan/CMakeLists.txt @@ -9,14 +9,17 @@ set(LLVM_EXPORTED_SYMBOL_FILE add_swift_host_library(libSwiftScan SHARED libSwiftScan.cpp - c-include-check.c) + c-include-check.c + HAS_LIBSWIFT) add_dependencies(libSwiftScan clang swiftDependencyScan) target_link_libraries(libSwiftScan PRIVATE - swiftDependencyScan) + swiftDependencyScan + swiftDriverTool + libswift) set_target_properties(libSwiftScan PROPERTIES diff --git a/tools/libSwiftScan/libSwiftScan.cpp b/tools/libSwiftScan/libSwiftScan.cpp index e8c423515b5fd..7ed6780554532 100644 --- a/tools/libSwiftScan/libSwiftScan.cpp +++ b/tools/libSwiftScan/libSwiftScan.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "swift/Basic/LLVMInitialize.h" +#include "swift/DriverTool/DriverTool.h" #include "swift/DependencyScan/DependencyScanImpl.h" #include "swift/DependencyScan/DependencyScanningTool.h" #include "swift/DependencyScan/StringUtils.h" @@ -95,6 +96,25 @@ void swiftscan_dependency_set_dispose(swiftscan_dependency_set_t *set) { delete set; } +//=== Scanner Cache Operations --------------------------------------------===// + +void swiftscan_scanner_cache_serialize(swiftscan_scanner_t scanner, + const char * path) { + DependencyScanningTool *ScanningTool = unwrap(scanner); + ScanningTool->serializeCache(path); +} + +bool swiftscan_scanner_cache_load(swiftscan_scanner_t scanner, + const char * path) { + DependencyScanningTool *ScanningTool = unwrap(scanner); + return ScanningTool->loadCache(path); +} + +void swiftscan_scanner_cache_reset(swiftscan_scanner_t scanner) { + DependencyScanningTool *ScanningTool = unwrap(scanner); + ScanningTool->resetCache(); +} + //=== Scanner Functions ---------------------------------------------------===// swiftscan_scanner_t swiftscan_scanner_create(void) { @@ -491,3 +511,7 @@ swiftscan_compiler_supported_features_query() { // TODO: We are yet to figure out how "Features" will be organized. return nullptr; } + +int invoke_swift_compiler(int argc, const char **argv) { + return swift::mainEntry(argc, argv); +} diff --git a/tools/libSwiftScan/libSwiftScan.exports b/tools/libSwiftScan/libSwiftScan.exports index e2c62ef1faa25..c0d63031b0888 100644 --- a/tools/libSwiftScan/libSwiftScan.exports +++ b/tools/libSwiftScan/libSwiftScan.exports @@ -54,3 +54,7 @@ swiftscan_import_set_dispose swiftscan_scanner_dispose swiftscan_compiler_supported_arguments_query swiftscan_compiler_supported_features_query +swiftscan_scanner_cache_serialize +swiftscan_scanner_cache_load +swiftscan_scanner_cache_reset +invoke_swift_compiler diff --git a/tools/libSwiftSyntaxParser/CMakeLists.txt b/tools/libSwiftSyntaxParser/CMakeLists.txt index 0b33758f923fa..9de30f18059b0 100644 --- a/tools/libSwiftSyntaxParser/CMakeLists.txt +++ b/tools/libSwiftSyntaxParser/CMakeLists.txt @@ -9,7 +9,8 @@ set(LLVM_EXPORTED_SYMBOL_FILE add_swift_host_library(libSwiftSyntaxParser SHARED c-include-check.c - libSwiftSyntaxParser.cpp) + libSwiftSyntaxParser.cpp + LLVM_LINK_COMPONENTS support) if(NOT SWIFT_BUILT_STANDALONE AND NOT CMAKE_C_COMPILER_ID MATCHES Clang) add_dependencies(libSwiftSyntaxParser clang) endif() diff --git a/tools/sil-func-extractor/SILFunctionExtractor.cpp b/tools/sil-func-extractor/SILFunctionExtractor.cpp index be96d55123a3a..57d6ffd5d4f5d 100644 --- a/tools/sil-func-extractor/SILFunctionExtractor.cpp +++ b/tools/sil-func-extractor/SILFunctionExtractor.cpp @@ -116,6 +116,11 @@ static llvm::cl::opt EnableOSSAModules( "this is disabled we do not serialize in OSSA " "form when optimizing.")); +static llvm::cl::opt EnableObjCInterop( + "enable-objc-interop", + llvm::cl::desc("Whether the Objective-C interop should be enabled. " + "The value is `true` by default on Darwin platforms.")); + // This function isn't referenced outside its translation unit, but it // can't use the "static" keyword because its address is used for // getMainExecutable (since some platforms don't support taking the @@ -250,6 +255,14 @@ int main(int argc, char **argv) { Invocation.getLangOptions().EnableAccessControl = false; Invocation.getLangOptions().EnableObjCAttrRequiresFoundation = false; + if (EnableObjCInterop == llvm::cl::BOU_UNSET) { + Invocation.getLangOptions().EnableObjCInterop = + Invocation.getLangOptions().Target.isOSDarwin(); + } else { + Invocation.getLangOptions().EnableObjCInterop = + EnableObjCInterop == llvm::cl::BOU_TRUE; + } + SILOptions &Opts = Invocation.getSILOptions(); Opts.EmitVerboseSIL = EmitVerboseSIL; Opts.EmitSortedSIL = EmitSortedSIL; diff --git a/tools/sil-opt/CMakeLists.txt b/tools/sil-opt/CMakeLists.txt index 4805bce5550ed..b5b5da637fb14 100644 --- a/tools/sil-opt/CMakeLists.txt +++ b/tools/sil-opt/CMakeLists.txt @@ -1,6 +1,7 @@ add_swift_host_tool(sil-opt SILOpt.cpp SWIFT_COMPONENT tools + HAS_LIBSWIFT ) target_link_libraries(sil-opt PRIVATE @@ -9,6 +10,7 @@ target_link_libraries(sil-opt swiftSIL swiftSILGen swiftSILOptimizer + libswift # Clang libraries included to appease the linker on linux. clangBasic clangCodeGen) diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp index 7681d2f5d10fb..f6bda1b47753b 100644 --- a/tools/sil-opt/SILOpt.cpp +++ b/tools/sil-opt/SILOpt.cpp @@ -20,6 +20,7 @@ #include "swift/AST/SILOptions.h" #include "swift/Basic/FileTypes.h" #include "swift/Basic/LLVMInitialize.h" +#include "swift/Basic/InitializeLibSwift.h" #include "swift/Frontend/DiagnosticVerifier.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" @@ -103,6 +104,10 @@ static llvm::cl::opt EnableExperimentalConcurrency("enable-experimental-concurrency", llvm::cl::desc("Enable experimental concurrency model.")); +static llvm::cl::opt +EnableExperimentalDistributed("enable-experimental-distributed", + llvm::cl::desc("Enable experimental distributed actors.")); + static llvm::cl::opt VerifyExclusivity("enable-verify-exclusivity", llvm::cl::desc("Verify the access markers used to enforce exclusivity.")); @@ -117,6 +122,14 @@ static llvm::cl::opt EnableOSSAModules( "this is disabled we do not serialize in OSSA " "form when optimizing.")); +static llvm::cl::opt EnableCopyPropagation( + "enable-copy-propagation", + llvm::cl::desc("Enable the copy propagation pass.")); + +static llvm::cl::opt DisableCopyPropagation( + "disable-copy-propagation", + llvm::cl::desc("Disable the copy propagation pass.")); + namespace { enum class EnforceExclusivityMode { Unchecked, // static only @@ -311,6 +324,11 @@ static llvm::cl::opt llvm::cl::desc("Ignore [always_inline] attribute."), llvm::cl::init(false)); +static llvm::cl::opt EnableRequirementMachine( + "requirement-machine", + llvm::cl::desc("Control usage of experimental generics implementation: " + "'on', 'off', or 'verify'.")); + static void runCommandLineSelectedPasses(SILModule *Module, irgen::IRGenModule *IRGenMod) { auto &opts = Module->getOptions(); @@ -353,6 +371,8 @@ int main(int argc, char **argv) { llvm::cl::ParseCommandLineOptions(argc, argv, "Swift SIL optimizer\n"); + initializeLibSwift(); + if (PrintStats) llvm::EnableStatistics(); @@ -395,6 +415,8 @@ int main(int argc, char **argv) { } Invocation.getLangOptions().EnableExperimentalConcurrency = EnableExperimentalConcurrency; + Invocation.getLangOptions().EnableExperimentalDistributed = + EnableExperimentalDistributed; Invocation.getLangOptions().EnableObjCInterop = EnableObjCInterop ? true : @@ -418,6 +440,23 @@ int main(int argc, char **argv) { Invocation.getDiagnosticOptions().VerifyMode = VerifyMode ? DiagnosticOptions::Verify : DiagnosticOptions::NoVerify; + if (EnableRequirementMachine.size()) { + auto value = llvm::StringSwitch>( + EnableRequirementMachine) + .Case("off", RequirementMachineMode::Disabled) + .Case("on", RequirementMachineMode::Enabled) + .Case("verify", RequirementMachineMode::Verify) + .Default(None); + + if (value) + Invocation.getLangOptions().EnableRequirementMachine = *value; + else { + fprintf(stderr, "Invalid value for -requirement-machine flag: %s\n", + EnableRequirementMachine.c_str()); + exit(-1); + } + } + // Setup the SIL Options. SILOptions &SILOpts = Invocation.getSILOptions(); SILOpts.InlineThreshold = SILInlineThreshold; @@ -463,6 +502,8 @@ int main(int argc, char **argv) { SILOpts.EnableSpeculativeDevirtualization = EnableSpeculativeDevirtualization; SILOpts.IgnoreAlwaysInline = IgnoreAlwaysInline; SILOpts.EnableOSSAModules = EnableOSSAModules; + SILOpts.EnableCopyPropagation = EnableCopyPropagation; + SILOpts.DisableCopyPropagation = DisableCopyPropagation; serialization::ExtendedValidationInfo extendedInfo; llvm::ErrorOr> FileBufOrErr = diff --git a/tools/swift-demangle/swift-demangle.cpp b/tools/swift-demangle/swift-demangle.cpp index 4c501846fe9a4..f331d4aa4715c 100644 --- a/tools/swift-demangle/swift-demangle.cpp +++ b/tools/swift-demangle/swift-demangle.cpp @@ -20,7 +20,6 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Regex.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" @@ -224,20 +223,114 @@ static void demangle(llvm::raw_ostream &os, llvm::StringRef name, DCtx.clear(); } -static int demangleSTDIN(const swift::Demangle::DemangleOptions &options) { - // This doesn't handle Unicode symbols, but maybe that's okay. - // Also accept the future mangling prefix. - llvm::Regex maybeSymbol("(_T|_?\\$[Ss])[_a-zA-Z0-9$.]+"); +static bool isValidInMangling(char ch) { + return (ch == '_' || ch == '$' || ch == '.' + || (ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9')); +} + +static bool findMaybeMangled(llvm::StringRef input, llvm::StringRef &match) { + const char *ptr = input.data(); + size_t len = input.size(); + const char *end = ptr + len; + enum { + Start, + SeenUnderscore, + SeenDollar, + FoundPrefix + } state = Start; + const char *matchStart = nullptr; + + // Find _T, $S, $s, _$S, _$s followed by a valid mangled string + while (ptr < end) { + switch (state) { + case Start: + while (ptr < end) { + char ch = *ptr++; + + if (ch == '_') { + state = SeenUnderscore; + matchStart = ptr - 1; + break; + } else if (ch == '$') { + state = SeenDollar; + matchStart = ptr - 1; + break; + } + } + break; + + case SeenUnderscore: + while (ptr < end) { + char ch = *ptr++; + + if (ch == 'T') { + state = FoundPrefix; + break; + } else if (ch == '$') { + state = SeenDollar; + break; + } else if (ch == '_') { + matchStart = ptr - 1; + } else { + state = Start; + break; + } + } + break; + + case SeenDollar: + while (ptr < end) { + char ch = *ptr++; + + if (ch == 'S' || ch == 's') { + state = FoundPrefix; + break; + } else if (ch == '_') { + state = SeenUnderscore; + matchStart = ptr - 1; + break; + } else if (ch == '$') { + matchStart = ptr - 1; + } else { + state = Start; + break; + } + } + break; + case FoundPrefix: + { + const char *mangled = ptr; + + while (ptr < end && isValidInMangling(*ptr)) + ++ptr; + + if (ptr == mangled) { + state = Start; + break; + } + + match = llvm::StringRef(matchStart, ptr - matchStart); + return true; + } + } + } + + return false; +} + +static int demangleSTDIN(const swift::Demangle::DemangleOptions &options) { swift::Demangle::Context DCtx; for (std::string mangled; std::getline(std::cin, mangled);) { llvm::StringRef inputContents(mangled); + llvm::StringRef match; - llvm::SmallVector matches; - while (maybeSymbol.match(inputContents, &matches)) { - llvm::outs() << substrBefore(inputContents, matches.front()); - demangle(llvm::outs(), matches.front(), DCtx, options); - inputContents = substrAfter(inputContents, matches.front()); + while (findMaybeMangled(inputContents, match)) { + llvm::outs() << substrBefore(inputContents, match); + demangle(llvm::outs(), match, DCtx, options); + inputContents = substrAfter(inputContents, match); } llvm::outs() << inputContents << '\n'; diff --git a/tools/swift-ide-test/ModuleAPIDiff.cpp b/tools/swift-ide-test/ModuleAPIDiff.cpp index 58bb7c47ff488..10fe5bc6f2ceb 100644 --- a/tools/swift-ide-test/ModuleAPIDiff.cpp +++ b/tools/swift-ide-test/ModuleAPIDiff.cpp @@ -740,12 +740,12 @@ class SMAModelGenerator : public DeclVisitor { if (!GS) return None; sma::GenericSignature ResultGS; - for (auto *GTPT : GS->getGenericParams()) { + for (auto *GTPT : GS.getGenericParams()) { sma::GenericParam ResultGP; ResultGP.Name = convertToIdentifier(GTPT->getName()); ResultGS.GenericParams.emplace_back(std::move(ResultGP)); } - for (auto &Req : GS->getRequirements()) { + for (auto &Req : GS.getRequirements()) { switch (Req.getKind()) { case RequirementKind::Superclass: case RequirementKind::Conformance: diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 48db939e05e31..9e2909d031159 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -266,6 +266,12 @@ static llvm::cl::list BuildConfigs("D", llvm::cl::desc("Conditional compilation flags"), llvm::cl::cat(Category)); +static llvm::cl::opt +ParseAsLibrary("parse-as-library", + llvm::cl::desc("Parse '-source-filename' as a library source file"), + llvm::cl::cat(Category), + llvm::cl::init(false)); + static llvm::cl::opt SDK("sdk", llvm::cl::desc("path to the SDK to build against"), llvm::cl::cat(Category)); @@ -459,6 +465,12 @@ CodeCOmpletionAnnotateResults("code-completion-annotate-results", llvm::cl::cat(Category), llvm::cl::init(false)); +static llvm::cl::opt +CodeCompletionSourceFileInfo("code-completion-sourcefileinfo", + llvm::cl::desc("print module source file information"), + llvm::cl::cat(Category), + llvm::cl::init(false)); + static llvm::cl::opt DebugClientDiscriminator("debug-client-discriminator", llvm::cl::desc("A discriminator to prefer in lookups"), @@ -751,16 +763,35 @@ static llvm::cl::opt EnableExperimentalConcurrency("enable-experimental-concurrency", llvm::cl::desc("Enable experimental concurrency model"), llvm::cl::init(false)); +static llvm::cl::opt +WarnConcurrency("warn-concurrency", + llvm::cl::desc("Additional concurrency warnings"), + llvm::cl::init(false)); static llvm::cl::opt DisableImplicitConcurrencyImport("disable-implicit-concurrency-module-import", llvm::cl::desc("Disable implicit import of _Concurrency module"), llvm::cl::init(false)); +static llvm::cl::opt EnableExperimentalNamedOpaqueTypes( + "enable-experimental-named-opaque-types", + llvm::cl::desc("Enable experimental support for named opaque result types"), + llvm::cl::Hidden, llvm::cl::cat(Category), llvm::cl::init(false)); + +static llvm::cl::opt +EnableExperimentalDistributed("enable-experimental-distributed", + llvm::cl::desc("Enable experimental distributed actors and functions"), + llvm::cl::init(false)); + static llvm::cl::list AccessNotesPath("access-notes-path", llvm::cl::desc("Path to access notes file"), llvm::cl::cat(Category)); +static llvm::cl::opt +AllowCompilerErrors("allow-compiler-errors", + llvm::cl::desc("Whether to attempt to continue despite compiler errors"), + llvm::cl::init(false)); + } // namespace options static std::unique_ptr @@ -888,7 +919,8 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, bool CodeCompletionDiagnostics, bool CodeCompletionKeywords, bool CodeCompletionComments, - bool CodeCompletionAnnotateResults) { + bool CodeCompletionAnnotateResults, + bool CodeCompletionSourceFileInfo) { std::unique_ptr OnDiskCache; if (!options::CompletionCachePath.empty()) { OnDiskCache = std::make_unique( @@ -897,6 +929,7 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, ide::CodeCompletionCache CompletionCache(OnDiskCache.get()); ide::CodeCompletionContext CompletionContext(CompletionCache); CompletionContext.setAnnotateResult(CodeCompletionAnnotateResults); + CompletionContext.setRequiresSourceFileInfo(CodeCompletionSourceFileInfo); // Create a CodeCompletionConsumer. std::unique_ptr Consumer( @@ -1091,7 +1124,9 @@ static int doBatchCodeCompletion(const CompilerInvocation &InitInvok, StringRef SourceFilename, bool CodeCompletionDiagnostics, bool CodeCompletionKeywords, - bool CodeCompletionComments) { + bool CodeCompletionComments, + bool CodeCompletionAnnotateResults, + bool CodeCompletionSourceFileInfo) { auto FileBufOrErr = llvm::MemoryBuffer::getFile(SourceFilename); if (!FileBufOrErr) { llvm::errs() << "error opening input file: " @@ -1222,11 +1257,14 @@ static int doBatchCodeCompletion(const CompilerInvocation &InitInvok, // Create a CodeCompletionConsumer. std::unique_ptr Consumer( new ide::PrintingCodeCompletionConsumer(OS, IncludeKeywords, - IncludeComments)); + IncludeComments, + CodeCompletionAnnotateResults)); // Create a factory for code completion callbacks that will feed the // Consumer. ide::CodeCompletionContext CompletionContext(CompletionCache); + CompletionContext.setAnnotateResult(CodeCompletionAnnotateResults); + CompletionContext.setRequiresSourceFileInfo(CodeCompletionSourceFileInfo); std::unique_ptr callbacksFactory( ide::makeCodeCompletionCallbacksFactory(CompletionContext, *Consumer)); @@ -3843,9 +3881,22 @@ int main(int argc, char *argv[]) { if (options::EnableExperimentalConcurrency) { InitInvok.getLangOptions().EnableExperimentalConcurrency = true; } + if (options::WarnConcurrency) { + InitInvok.getLangOptions().WarnConcurrency = true; + } if (options::DisableImplicitConcurrencyImport) { InitInvok.getLangOptions().DisableImplicitConcurrencyModuleImport = true; } + if (options::EnableExperimentalNamedOpaqueTypes) { + InitInvok.getLangOptions().EnableExperimentalNamedOpaqueTypes = true; + } + + if (options::EnableExperimentalDistributed) { + // distributed implies concurrency features: + InitInvok.getLangOptions().EnableExperimentalConcurrency = true; + // enable 'distributed' parsing and features + InitInvok.getLangOptions().EnableExperimentalDistributed = true; + } if (!options::Triple.empty()) InitInvok.setTargetTriple(options::Triple); @@ -3869,6 +3920,10 @@ int main(int argc, char *argv[]) { InitInvok.getFrontendOptions().AccessNotesPath = options::AccessNotesPath[options::AccessNotesPath.size()-1]; } + if (options::ParseAsLibrary) { + InitInvok.getFrontendOptions().InputMode = + swift::FrontendOptions::ParseInputMode::SwiftLibrary; + } InitInvok.getClangImporterOptions().PrecompiledHeaderOutputDir = options::PCHOutputDir; InitInvok.setImportSearchPaths(options::ImportPaths); @@ -3917,6 +3972,12 @@ int main(int argc, char *argv[]) { options::ExplicitSwiftModuleMap; InitInvok.getFrontendOptions().DisableImplicitModules = true; } + + if (options::AllowCompilerErrors) { + InitInvok.getFrontendOptions().AllowModuleWithCompilerErrors = true; + InitInvok.getLangOptions().AllowModuleWithCompilerErrors = true; + } + // Process the clang arguments last and allow them to override previously // set options. if (!CCArgs.empty()) { @@ -3992,7 +4053,9 @@ int main(int argc, char *argv[]) { options::SourceFilename, options::CodeCompletionDiagnostics, options::CodeCompletionKeywords, - options::CodeCompletionComments); + options::CodeCompletionComments, + options::CodeCOmpletionAnnotateResults, + options::CodeCompletionSourceFileInfo); break; case ActionType::CodeCompletion: @@ -4007,7 +4070,8 @@ int main(int argc, char *argv[]) { options::CodeCompletionDiagnostics, options::CodeCompletionKeywords, options::CodeCompletionComments, - options::CodeCOmpletionAnnotateResults); + options::CodeCOmpletionAnnotateResults, + options::CodeCompletionSourceFileInfo); break; case ActionType::REPLCodeCompletion: diff --git a/tools/swift-inspect/Sources/swift-inspect/main.swift b/tools/swift-inspect/Sources/swift-inspect/main.swift index 20e054ebc05f9..2fcf54e85e854 100644 --- a/tools/swift-inspect/Sources/swift-inspect/main.swift +++ b/tools/swift-inspect/Sources/swift-inspect/main.swift @@ -159,10 +159,10 @@ struct UniversalOptions: ParsableArguments { struct BacktraceOptions: ParsableArguments { @Flag(help: "Show the backtrace for each allocation") - var backtrace: Bool + var backtrace: Bool = false @Flag(help: "Show a long-form backtrace for each allocation") - var backtraceLong: Bool + var backtraceLong: Bool = false var style: Backtrace.Style? { backtrace ? .oneLine : diff --git a/tools/swift-refactor/CMakeLists.txt b/tools/swift-refactor/CMakeLists.txt index b0006759007b0..1fac04a19582e 100644 --- a/tools/swift-refactor/CMakeLists.txt +++ b/tools/swift-refactor/CMakeLists.txt @@ -1,6 +1,7 @@ add_swift_host_tool(swift-refactor swift-refactor.cpp SWIFT_COMPONENT tools + LLVM_LINK_COMPONENTS support ) target_link_libraries(swift-refactor PRIVATE diff --git a/tools/swift-refactor/swift-refactor.cpp b/tools/swift-refactor/swift-refactor.cpp index 5389b1233344b..d7d13453e1263 100644 --- a/tools/swift-refactor/swift-refactor.cpp +++ b/tools/swift-refactor/swift-refactor.cpp @@ -82,7 +82,9 @@ Action(llvm::cl::desc("kind:"), llvm::cl::init(RefactoringKind::None), clEnumValN(RefactoringKind::ConvertToAsync, "convert-to-async", "Convert the entire function to async"), clEnumValN(RefactoringKind::AddAsyncAlternative, - "add-async-alternative", "Add an async alternative of a function taking a callback"))); + "add-async-alternative", "Add an async alternative of a function taking a callback"), + clEnumValN(RefactoringKind::AddAsyncWrapper, + "add-async-wrapper", "Add an async alternative that forwards onto the function taking a callback"))); static llvm::cl::opt @@ -96,6 +98,10 @@ static llvm::cl::list InputFilenames(llvm::cl::Positional, llvm::cl::desc("[input files...]"), llvm::cl::ZeroOrMore); +static llvm::cl::opt + RewrittenOutputFile("rewritten-output-file", + llvm::cl::desc("Name of the rewritten output file")); + static llvm::cl::opt LineColumnPair("pos", llvm::cl::desc("Line:Column pair or /*label*/")); @@ -118,6 +124,20 @@ static llvm::cl::opt IsNonProtocolType("is-non-protocol-type", llvm::cl::desc("The symbol being renamed is a type and not a protocol")); +static llvm::cl::opt EnableExperimentalConcurrency( + "enable-experimental-concurrency", + llvm::cl::desc("Whether to enable experimental concurrency or not")); + +static llvm::cl::opt + SDK("sdk", llvm::cl::desc("Path to the SDK to build against")); + +static llvm::cl::list + ImportPaths("I", + llvm::cl::desc("Add a directory to the import search path")); + +static llvm::cl::opt +Triple("target", llvm::cl::desc("target triple")); + enum class DumpType { REWRITTEN, JSON, @@ -268,12 +288,21 @@ int main(int argc, char *argv[]) { Invocation.setMainExecutablePath( llvm::sys::fs::getMainExecutable(argv[0], reinterpret_cast(&anchorForGetMainExecutable))); + + Invocation.setSDKPath(options::SDK); + Invocation.setImportSearchPaths(options::ImportPaths); + if (!options::Triple.empty()) + Invocation.setTargetTriple(options::Triple); + Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( options::SourceFilename); Invocation.getLangOptions().AttachCommentsToDecls = true; Invocation.getLangOptions().CollectParsedToken = true; Invocation.getLangOptions().BuildSyntaxTree = true; - Invocation.getLangOptions().EnableExperimentalConcurrency = true; + Invocation.getLangOptions().DisableAvailabilityChecking = true; + + if (options::EnableExperimentalConcurrency) + Invocation.getLangOptions().EnableExperimentalConcurrency = true; for (auto FileName : options::InputFilenames) Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(FileName); @@ -375,9 +404,9 @@ int main(int argc, char *argv[]) { if (options::Action == RefactoringKind::None) { llvm::SmallVector Kinds; - bool RangeStartMayNeedRename = false; - collectAvailableRefactorings(SF, Range, RangeStartMayNeedRename, Kinds, - {&PrintDiags}); + bool CollectRangeStartRefactorings = false; + collectAvailableRefactorings(SF, Range, CollectRangeStartRefactorings, + Kinds, {&PrintDiags}); llvm::outs() << "Action begins\n"; for (auto Kind : Kinds) { llvm::outs() << getDescriptiveRefactoringKindName(Kind) << "\n"; @@ -390,21 +419,36 @@ int main(int argc, char *argv[]) { RefactoringConfig.Range = Range; RefactoringConfig.PreferredName = options::NewName; std::string Error; - std::unique_ptr pConsumer; + + StringRef RewrittenOutputFile = options::RewrittenOutputFile; + if (RewrittenOutputFile.empty()) + RewrittenOutputFile = "-"; + std::error_code EC; + llvm::raw_fd_ostream RewriteStream(RewrittenOutputFile, EC); + if (RewriteStream.has_error()) { + llvm::errs() << "Could not open rewritten output file"; + return 1; + } + + SmallVector> Consumers; + if (!options::RewrittenOutputFile.empty() || + options::DumpIn == options::DumpType::REWRITTEN) { + Consumers.emplace_back(new SourceEditOutputConsumer( + SF->getASTContext().SourceMgr, BufferID, RewriteStream)); + } switch (options::DumpIn) { case options::DumpType::REWRITTEN: - pConsumer.reset(new SourceEditOutputConsumer(SF->getASTContext().SourceMgr, - BufferID, - llvm::outs())); + // Already added break; case options::DumpType::JSON: - pConsumer.reset(new SourceEditJsonConsumer(llvm::outs())); + Consumers.emplace_back(new SourceEditJsonConsumer(llvm::outs())); break; case options::DumpType::TEXT: - pConsumer.reset(new SourceEditTextConsumer(llvm::outs())); + Consumers.emplace_back(new SourceEditTextConsumer(llvm::outs())); break; } - return refactorSwiftModule(CI.getMainModule(), RefactoringConfig, *pConsumer, - PrintDiags); + BroadcastingSourceEditConsumer BroadcastConsumer(Consumers); + return refactorSwiftModule(CI.getMainModule(), RefactoringConfig, + BroadcastConsumer, PrintDiags); } diff --git a/tools/swift-reflection-fuzzer/swift-reflection-fuzzer.cpp b/tools/swift-reflection-fuzzer/swift-reflection-fuzzer.cpp index a47ac394e3561..76ee1f1152e5e 100644 --- a/tools/swift-reflection-fuzzer/swift-reflection-fuzzer.cpp +++ b/tools/swift-reflection-fuzzer/swift-reflection-fuzzer.cpp @@ -28,8 +28,9 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Support/CommandLine.h" -#include -#include +#include +#include +#include #if defined(__APPLE__) && defined(__MACH__) #include @@ -73,6 +74,16 @@ class ObjectMemoryReader : public MemoryReader { *result = sizeof(void *); return true; } + case DLQ_GetPtrAuthMask: { + auto result = static_cast(outBuffer); +#if __has_feature(ptrauth_calls) + *result = static_cast( + ptrauth_strip(static_cast(0x0007ffffffffffff), 0)); +#else + *result = ~uintptr_t(0); +#endif + return true; + } case DLQ_GetSizeSize: { auto result = static_cast(outBuffer); *result = sizeof(size_t); @@ -131,6 +142,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { auto reader = std::make_shared(); NativeReflectionContext context(std::move(reader)); context.addImage(RemoteAddress(Data)); - context.getBuilder().dumpAllSections(std::cout); + context.getBuilder().dumpAllSections(stdout); return 0; // Non-zero return values are reserved for future use. } diff --git a/unittests/AST/TestContext.cpp b/unittests/AST/TestContext.cpp index 1cee32305944f..b6e9f6af31cf5 100644 --- a/unittests/AST/TestContext.cpp +++ b/unittests/AST/TestContext.cpp @@ -36,7 +36,8 @@ static Decl *createOptionalType(ASTContext &ctx, SourceFile *fileForLookups, TestContext::TestContext(ShouldDeclareOptionalTypes optionals) : Ctx(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, - ClangImporterOpts, SourceMgr, Diags)) { + ClangImporterOpts, SymbolGraphOpts, + SourceMgr, Diags)) { registerParseRequestFunctions(Ctx.evaluator); registerTypeCheckerRequestFunctions(Ctx.evaluator); auto stdlibID = Ctx.getIdentifier(STDLIB_NAME); diff --git a/unittests/AST/TestContext.h b/unittests/AST/TestContext.h index 1230ea94625d8..164ac91e73a36 100644 --- a/unittests/AST/TestContext.h +++ b/unittests/AST/TestContext.h @@ -16,6 +16,7 @@ #include "swift/AST/SourceFile.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/SourceManager.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/Support/Host.h" @@ -32,6 +33,7 @@ class TestContextBase { TypeCheckerOptions TypeCheckerOpts; SearchPathOptions SearchPathOpts; ClangImporterOptions ClangImporterOpts; + symbolgraphgen::SymbolGraphOptions SymbolGraphOpts; SourceManager SourceMgr; DiagnosticEngine Diags; diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt index 3c5b0d0fdc6e1..670d47527d019 100644 --- a/unittests/Basic/CMakeLists.txt +++ b/unittests/Basic/CMakeLists.txt @@ -3,7 +3,7 @@ set(generated_tests UnicodeGraphemeBreakTest.cpp.gyb) handle_gyb_sources( gyb_dependency_targets generated_tests - ${SWIFT_HOST_VARIANT_ARCH}) + ARCH ${SWIFT_HOST_VARIANT_ARCH}) add_swift_unittest(SwiftBasicTests BlotMapVectorTest.cpp @@ -34,7 +34,6 @@ add_swift_unittest(SwiftBasicTests TransformRangeTest.cpp TypeLookupError.cpp UnicodeTest.cpp - ValueEnumeratorTest.cpp ${generated_tests} ) diff --git a/unittests/Basic/OptionSetTest.cpp b/unittests/Basic/OptionSetTest.cpp index eef5ff3fd892e..e5bd90036c4bf 100644 --- a/unittests/Basic/OptionSetTest.cpp +++ b/unittests/Basic/OptionSetTest.cpp @@ -12,7 +12,6 @@ #include "swift/Basic/OptionSet.h" #include "swift/Basic/Range.h" -#include "swift/Basic/ValueEnumerator.h" #include "gtest/gtest.h" using namespace swift; diff --git a/unittests/Basic/RangeTest.cpp b/unittests/Basic/RangeTest.cpp index d73488779c52b..34d4d3d836629 100644 --- a/unittests/Basic/RangeTest.cpp +++ b/unittests/Basic/RangeTest.cpp @@ -12,7 +12,6 @@ #include "swift/Basic/Range.h" #include "swift/Basic/OptionSet.h" -#include "swift/Basic/ValueEnumerator.h" #include "gtest/gtest.h" using namespace swift; diff --git a/unittests/Basic/ValueEnumeratorTest.cpp b/unittests/Basic/ValueEnumeratorTest.cpp deleted file mode 100644 index 65bb9cd470bcd..0000000000000 --- a/unittests/Basic/ValueEnumeratorTest.cpp +++ /dev/null @@ -1,76 +0,0 @@ -//===--- ValueEnumeratorTest.cpp ------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "swift/Basic/ValueEnumerator.h" -#include "swift/Basic/OptionSet.h" -#include "swift/Basic/Range.h" -#include "gtest/gtest.h" - -using namespace swift; - -TEST(ValueEnumerator, basic) { - - { - ValueEnumerator Trans; - // Check that indexing is persistent. - EXPECT_EQ(Trans.getIndex(99), Trans.getIndex(99)); - EXPECT_EQ(Trans.getIndex(100), Trans.getIndex(100)); - - // Check that we don't have collisions. - bool SameIndex = Trans.getIndex(82) == Trans.getIndex(73); - EXPECT_FALSE(SameIndex); - - // Check that invalidation works. - // After invalidation the old index must not be equal to the new index. - size_t oldIndex = Trans.getIndex(99); - Trans.invalidateValue(99); - size_t newIndex = Trans.getIndex(99); - EXPECT_FALSE(newIndex == oldIndex); - } - - { - const char *string_1 = "hello"; - const char *string_2 = "goodbye"; - const char *string_3 = ":-)"; - ValueEnumerator Trans; - EXPECT_EQ(Trans.getIndex(nullptr), Trans.getIndex(nullptr)); - EXPECT_EQ(Trans.getIndex(string_1), Trans.getIndex(string_1)); - EXPECT_EQ(Trans.getIndex(string_2), Trans.getIndex(string_2)); - - // Check that invalidation works. - size_t oldIndex = Trans.getIndex(string_3); - Trans.invalidateValue(string_3); - size_t newIndex = Trans.getIndex(string_3); - EXPECT_FALSE(newIndex == oldIndex); - - // Check that different values don't give the same index. - EXPECT_FALSE(Trans.getIndex(string_2) == Trans.getIndex(string_3)); - } - - { - ValueEnumerator Trans; - // Check a bunch of integers. - for (int i = 1; i < 10000; i++) { - EXPECT_TRUE(Trans.getIndex(0) != Trans.getIndex(i)); - } - - // Check that there are no accidental collisions. - for (int i = 0; i < 10000; i++) { - for (int j = 1; j < 10; j++) { - EXPECT_TRUE(Trans.getIndex(i) != Trans.getIndex(i + j)); - } - } - - // Check that indexing is still persistent. - EXPECT_EQ(Trans.getIndex(100), Trans.getIndex(100)); - } -} diff --git a/unittests/ClangImporter/ClangImporterTests.cpp b/unittests/ClangImporter/ClangImporterTests.cpp index 1d26a442e8204..50085a8abbbcc 100644 --- a/unittests/ClangImporter/ClangImporterTests.cpp +++ b/unittests/ClangImporter/ClangImporterTests.cpp @@ -6,6 +6,7 @@ #include "swift/Basic/LangOptions.h" #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangImporter.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -72,11 +73,12 @@ TEST(ClangImporterTest, emitPCHInMemory) { swift::TypeCheckerOptions typeckOpts; INITIALIZE_LLVM(); swift::SearchPathOptions searchPathOpts; + swift::symbolgraphgen::SymbolGraphOptions symbolGraphOpts; swift::SourceManager sourceMgr; swift::DiagnosticEngine diags(sourceMgr); std::unique_ptr context( ASTContext::get(langOpts, typeckOpts, searchPathOpts, options, - sourceMgr, diags)); + symbolGraphOpts, sourceMgr, diags)); auto importer = ClangImporter::create(*context); std::string PCH = createFilename(cache, "bridging.h.pch"); diff --git a/unittests/FrontendTool/ModuleLoadingTests.cpp b/unittests/FrontendTool/ModuleLoadingTests.cpp index 4b36d13c18c60..2e5bc9df8630d 100644 --- a/unittests/FrontendTool/ModuleLoadingTests.cpp +++ b/unittests/FrontendTool/ModuleLoadingTests.cpp @@ -17,6 +17,7 @@ #include "swift/Frontend/ModuleInterfaceLoader.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/Serialization/Validation.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/VirtualFileSystem.h" @@ -100,10 +101,11 @@ class ModuleInterfaceLoaderTest : public testing::Test { langOpts.Target = llvm::Triple(llvm::sys::getDefaultTargetTriple()); SearchPathOptions searchPathOpts; ClangImporterOptions clangImpOpts; + symbolgraphgen::SymbolGraphOptions symbolGraphOpts; SILOptions silOpts; auto ctx = ASTContext::get(langOpts, typeckOpts, searchPathOpts, clangImpOpts, - sourceMgr, diags); + symbolGraphOpts, sourceMgr, diags); ctx->addModuleInterfaceChecker( std::make_unique(*ctx, cacheDir, diff --git a/unittests/Reflection/TypeRef.cpp b/unittests/Reflection/TypeRef.cpp index 77dcf6e4b14aa..ab0217a4855ae 100644 --- a/unittests/Reflection/TypeRef.cpp +++ b/unittests/Reflection/TypeRef.cpp @@ -123,23 +123,23 @@ TEST(TypeRefTest, UniqueFunctionTypeRef) { auto F1 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F2 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F3 = Builder.createFunctionType( Parameters2, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(F1, F2); EXPECT_NE(F2, F3); auto F4 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withThrows(true), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F5 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withThrows(true), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(F4, F5); EXPECT_NE(F4, F1); @@ -153,42 +153,42 @@ TEST(TypeRefTest, UniqueFunctionTypeRef) { auto F6 = Builder.createFunctionType( {Param1.withFlags(inoutFlags)}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F6_1 = Builder.createFunctionType( {Param1.withFlags(inoutFlags)}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(F6, F6_1); auto F7 = Builder.createFunctionType( {Param1.withFlags(variadicFlags)}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F7_1 = Builder.createFunctionType( {Param1.withFlags(variadicFlags)}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(F7, F7_1); auto F8 = Builder.createFunctionType( {Param1.withFlags(sharedFlags)}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F8_1 = Builder.createFunctionType( {Param1.withFlags(sharedFlags)}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(F8, F8_1); auto F9 = Builder.createFunctionType( {Param1.withFlags(ownedFlags)}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F9_1 = Builder.createFunctionType( {Param1.withFlags(ownedFlags)}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(F9, F9_1); auto F10 = Builder.createFunctionType( {Param1}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F10_1 = Builder.createFunctionType( {Param1.withLabel("foo")}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_NE(F10, F10_1); EXPECT_NE(F6, F7); @@ -204,10 +204,10 @@ TEST(TypeRefTest, UniqueFunctionTypeRef) { auto VoidVoid1 = Builder.createFunctionType(VoidParams, VoidResult, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto VoidVoid2 = Builder.createFunctionType(VoidParams, VoidResult, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(VoidVoid1, VoidVoid2); EXPECT_NE(VoidVoid1, F1); @@ -215,39 +215,39 @@ TEST(TypeRefTest, UniqueFunctionTypeRef) { // Test escaping. auto F11 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withEscaping(true), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F12 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withEscaping(true), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F13 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withEscaping(false), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(F11, F12); EXPECT_NE(F11, F13); // Test sendable. auto F14 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withConcurrent(true), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F15 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withConcurrent(true), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); auto F16 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withConcurrent(false), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); EXPECT_EQ(F14, F15); EXPECT_NE(F14, F16); // Test differentiable. auto F17 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withDifferentiable(true), - FunctionMetadataDifferentiabilityKind::Reverse); + FunctionMetadataDifferentiabilityKind::Reverse, nullptr); auto F18 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withDifferentiable(true), - FunctionMetadataDifferentiabilityKind::Reverse); + FunctionMetadataDifferentiabilityKind::Reverse, nullptr); auto F19 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withDifferentiable(false), - FunctionMetadataDifferentiabilityKind::Reverse); + FunctionMetadataDifferentiabilityKind::Reverse, nullptr); EXPECT_EQ(F17, F18); EXPECT_NE(F17, F19); @@ -257,13 +257,13 @@ TEST(TypeRefTest, UniqueFunctionTypeRef) { parameters[1].setNoDerivative(); auto f1 = Builder.createFunctionType( parameters, Result, FunctionTypeFlags().withDifferentiable(true), - FunctionMetadataDifferentiabilityKind::Reverse); + FunctionMetadataDifferentiabilityKind::Reverse, nullptr); auto f2 = Builder.createFunctionType( parameters, Result, FunctionTypeFlags().withDifferentiable(true), - FunctionMetadataDifferentiabilityKind::Reverse); + FunctionMetadataDifferentiabilityKind::Reverse, nullptr); auto f3 = Builder.createFunctionType( Parameters1, Result, FunctionTypeFlags().withDifferentiable(true), - FunctionMetadataDifferentiabilityKind::Reverse); + FunctionMetadataDifferentiabilityKind::Reverse, nullptr); EXPECT_EQ(f1, f2); EXPECT_NE(f1, f3); } @@ -509,7 +509,7 @@ TEST(TypeRefTest, DeriveSubstitutions) { auto Result = Builder.createTupleType({GTP00, GTP01}, ""); auto Func = Builder.createFunctionType( {Nominal}, Result, FunctionTypeFlags(), - FunctionMetadataDifferentiabilityKind::NonDifferentiable); + FunctionMetadataDifferentiabilityKind::NonDifferentiable, nullptr); std::string SubstOneName("subst1"); auto SubstOne = Builder.createNominalType(SubstOneName, /*parent*/ nullptr); diff --git a/unittests/Sema/BindingInferenceTests.cpp b/unittests/Sema/BindingInferenceTests.cpp index 69431f83bc461..7494628c5ec90 100644 --- a/unittests/Sema/BindingInferenceTests.cpp +++ b/unittests/Sema/BindingInferenceTests.cpp @@ -336,3 +336,62 @@ TEST_F(SemaTest, TestTransitiveProtocolInferenceThroughEquivalenceChains) { verifyProtocolInferenceResults(*bindings.TransitiveProtocols, {protocolTy0, protocolTy1}); } + +TEST_F(SemaTest, TestNoDoubleVoidClosureResultInference) { + ConstraintSystemOptions options; + ConstraintSystem cs(DC, options); + + auto verifyInference = [&](TypeVariableType *typeVar, unsigned numExpected) { + auto bindings = cs.getBindingsFor(typeVar); + TypeVarBindingProducer producer(bindings); + + llvm::SmallPtrSet inferredTypes; + + while (auto binding = producer()) { + ASSERT_TRUE(binding.hasValue()); + ASSERT_EQ(binding->getTypeVariable(), typeVar); + ASSERT_TRUE(inferredTypes.insert(binding->getType()).second); + } + + ASSERT_EQ(inferredTypes.size(), numExpected); + }; + + auto *closureResultLoc = + cs.getConstraintLocator({}, ConstraintLocator::ClosureResult); + + auto *closureResult = cs.createTypeVariable(closureResultLoc, /*options=*/0); + + cs.addConstraint(ConstraintKind::Subtype, getStdlibType("Int"), closureResult, + closureResultLoc); + cs.addConstraint(ConstraintKind::Subtype, closureResult, getStdlibType("Void"), + closureResultLoc); + + verifyInference(closureResult, 2); + + auto closureResultWithTransitiveVoid = cs.createTypeVariable(closureResultLoc, + /*options=*/0); + + auto contextualVar = cs.createTypeVariable({}, /*options=*/0); + + cs.addConstraint(ConstraintKind::Subtype, getStdlibType("Void"), + contextualVar, cs.getConstraintLocator({})); + + cs.addConstraint(ConstraintKind::Subtype, contextualVar, + closureResultWithTransitiveVoid, closureResultLoc); + + cs.addConstraint(ConstraintKind::Subtype, getStdlibType("Int"), + closureResultWithTransitiveVoid, closureResultLoc); + + verifyInference(closureResultWithTransitiveVoid, 2); + + auto closureResultWithoutVoid = + cs.createTypeVariable(closureResultLoc, /*options=*/0); + + // Supertype triggers `Void` inference + cs.addConstraint(ConstraintKind::Subtype, getStdlibType("Int"), + closureResultWithoutVoid, closureResultLoc); + cs.addConstraint(ConstraintKind::Subtype, closureResultWithoutVoid, + getStdlibType("String"), closureResultLoc); + + verifyInference(closureResultWithoutVoid, 3); +} diff --git a/unittests/Sema/ConstraintSimplificationTests.cpp b/unittests/Sema/ConstraintSimplificationTests.cpp index 7c52996f40739..24f3615a351e0 100644 --- a/unittests/Sema/ConstraintSimplificationTests.cpp +++ b/unittests/Sema/ConstraintSimplificationTests.cpp @@ -11,6 +11,10 @@ //===----------------------------------------------------------------------===// #include "SemaFixture.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Expr.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/Types.h" #include "swift/Sema/ConstraintSystem.h" using namespace swift; @@ -44,3 +48,81 @@ TEST_F(SemaTest, TestTrailingClosureMatchRecordingForIdenticalFunctions) { TrailingClosureMatching::Forward, {{0}, {1}}, None}; ASSERT_EQ(choice->second, expected); } + +/// Emulates code like this: +/// +/// func test(_: ((Int) -> Void)?) {} +/// +/// test { $0 } +/// +/// To make sure that closure resolution propagates contextual +/// information into the body of the closure even when contextual +/// type is wrapped in an optional. +TEST_F(SemaTest, TestClosureInferenceFromOptionalContext) { + ConstraintSystem cs(DC, ConstraintSystemOptions()); + + DeclAttributes closureAttrs; + + // Anonymous closure parameter + auto paramName = Context.getIdentifier("0"); + + auto *paramDecl = + new (Context) ParamDecl(/*specifierLoc=*/SourceLoc(), + /*argumentNameLoc=*/SourceLoc(), paramName, + /*parameterNameLoc=*/SourceLoc(), paramName, DC); + + paramDecl->setSpecifier(ParamSpecifier::Default); + + auto *closure = new (Context) ClosureExpr( + closureAttrs, + /*bracketRange=*/SourceRange(), + /*capturedSelfDecl=*/nullptr, ParameterList::create(Context, {paramDecl}), + /*asyncLoc=*/SourceLoc(), + /*throwsLoc=*/SourceLoc(), + /*arrowLoc=*/SourceLoc(), + /*inLoc=*/SourceLoc(), + /*explicitResultType=*/nullptr, + /*discriminator=*/0, + /*parent=*/DC); + + closure->setImplicit(); + + closure->setBody(BraceStmt::create(Context, /*startLoc=*/SourceLoc(), {}, + /*endLoc=*/SourceLoc()), + /*isSingleExpression=*/false); + + auto *closureLoc = cs.getConstraintLocator(closure); + + auto *paramTy = cs.createTypeVariable( + cs.getConstraintLocator(closure, LocatorPathElt::TupleElement(0)), + /*options=*/TVO_CanBindToInOut); + + auto *resultTy = cs.createTypeVariable( + cs.getConstraintLocator(closure, ConstraintLocator::ClosureResult), + /*options=*/0); + + auto extInfo = FunctionType::ExtInfo(); + + auto defaultTy = FunctionType::get({FunctionType::Param(paramTy, paramName)}, + resultTy, extInfo); + + cs.setClosureType(closure, defaultTy); + + auto *closureTy = cs.createTypeVariable(closureLoc, /*options=*/0); + + cs.addUnsolvedConstraint(Constraint::create( + cs, ConstraintKind::DefaultClosureType, closureTy, defaultTy, + cs.getConstraintLocator(closure), /*referencedVars=*/{})); + + auto contextualTy = + FunctionType::get({FunctionType::Param(getStdlibType("Int"))}, + Context.TheEmptyTupleType, extInfo); + + // Try to resolve closure: + // - external type `paramTy` should get bound to `Int`. + // - result type should be bound to `Void`. + cs.resolveClosure(closureTy, OptionalType::get(contextualTy), closureLoc); + + ASSERT_TRUE(cs.simplifyType(paramTy)->isEqual(getStdlibType("Int"))); + ASSERT_TRUE(cs.simplifyType(resultTy)->isEqual(Context.TheEmptyTupleType)); +} diff --git a/unittests/Sema/SemaFixture.cpp b/unittests/Sema/SemaFixture.cpp index 8a2fed7270d0d..ec4a63d4e3a40 100644 --- a/unittests/Sema/SemaFixture.cpp +++ b/unittests/Sema/SemaFixture.cpp @@ -30,7 +30,8 @@ using namespace swift::constraints::inference; SemaTest::SemaTest() : Context(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, - ClangImporterOpts, SourceMgr, Diags)) { + ClangImporterOpts, SymbolGraphOpts, + SourceMgr, Diags)) { INITIALIZE_LLVM(); registerParseRequestFunctions(Context.evaluator); diff --git a/unittests/Sema/SemaFixture.h b/unittests/Sema/SemaFixture.h index 422d4e097ac0c..c9c5f1734930c 100644 --- a/unittests/Sema/SemaFixture.h +++ b/unittests/Sema/SemaFixture.h @@ -20,6 +20,7 @@ #include "swift/Basic/Platform.h" #include "swift/Basic/SourceManager.h" #include "swift/Sema/ConstraintSystem.h" +#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Host.h" @@ -39,6 +40,7 @@ class SemaTestBase : public ::testing::Test { TypeCheckerOptions TypeCheckerOpts; SearchPathOptions SearchPathOpts; ClangImporterOptions ClangImporterOpts; + symbolgraphgen::SymbolGraphOptions SymbolGraphOpts; SourceManager SourceMgr; DiagnosticEngine Diags; diff --git a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp index 36ac4348bbaee..eb3c19f36b857 100644 --- a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp +++ b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp @@ -367,7 +367,8 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) { EXPECT_EQ(strlen("fog"), Info.Length); } -TEST_F(CursorInfoTest, CursorInfoMustWaitDueTokenRace) { +// This test is failing occassionally in CI: rdar://55314062 +TEST_F(CursorInfoTest, DISABLED_CursorInfoMustWaitDueTokenRace) { const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; diff --git a/unittests/runtime/Actor.cpp b/unittests/runtime/Actor.cpp index 11906c7bbe2a9..ad4bf86eca2be 100644 --- a/unittests/runtime/Actor.cpp +++ b/unittests/runtime/Actor.cpp @@ -140,10 +140,14 @@ static std::pair createTaskWithContext(JobPriority priority, Fn &&fn) { auto invoke = TaskContinuationFromLambda::get(std::move(fn)); - - auto pair = swift_task_create_f(JobFlags(JobKind::Task, priority), - invoke, - sizeof(Context)); + TaskCreateFlags flags; + flags.setPriority(priority); + auto pair = swift_task_create_common(flags.getOpaqueValue(), + nullptr, + nullptr, + invoke, + nullptr, + sizeof(Context)); return std::make_pair(pair.Task, static_cast(pair.InitialContext)); } @@ -158,6 +162,8 @@ template static void parkTask(AsyncTask *task, Context *context, Fn &&fn) { auto invoke = TaskContinuationFromLambda::get(std::move(fn)); + auto currentTask = swift_task_suspend(); + EXPECT_EQ(task, currentTask); task->ResumeTask = invoke; task->ResumeContext = context; } diff --git a/unittests/runtime/CMakeLists.txt b/unittests/runtime/CMakeLists.txt index 58cd7a21fd2fa..17ff1c944f6f2 100644 --- a/unittests/runtime/CMakeLists.txt +++ b/unittests/runtime/CMakeLists.txt @@ -50,6 +50,7 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND # some stdlib hooks to implement SwiftObject. list(APPEND PLATFORM_TARGET_LINK_LIBRARIES ${FOUNDATION_LIBRARY} + swift_Concurrency${SWIFT_PRIMARY_VARIANT_SUFFIX} swiftStdlibUnittest${SWIFT_PRIMARY_VARIANT_SUFFIX} ) elseif(SWIFT_HOST_VARIANT STREQUAL "Linux") @@ -75,7 +76,7 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND swift_Concurrency${SWIFT_PRIMARY_VARIANT_SUFFIX} ) - if(NOT "${SWIFT_PRIMARY_VARIANT_SDK}" IN_LIST SWIFT_APPLE_PLATFORMS) + if(NOT "${SWIFT_PRIMARY_VARIANT_SDK}" IN_LIST SWIFT_DARWIN_PLATFORMS) list(APPEND PLATFORM_TARGET_LINK_LIBRARIES dispatch${SWIFT_PRIMARY_VARIANT_SUFFIX} BlocksRuntime${SWIFT_PRIMARY_VARIANT_SUFFIX} @@ -83,6 +84,15 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND endif() endif() + if(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) +# list(APPEND PLATFORM_SOURCES +# DistributedActor.cpp +# ) + list(APPEND PLATFORM_TARGET_LINK_LIBRARIES + swift_Distributed${SWIFT_PRIMARY_VARIANT_SUFFIX} + ) + endif() + # Don't complain about these files not being in the sources list. set(LLVM_OPTIONAL_SOURCES weak.mm @@ -129,4 +139,3 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND ${PLATFORM_TARGET_LINK_LIBRARIES} ) endif() - diff --git a/unittests/runtime/CompatibilityOverrideConcurrency.cpp b/unittests/runtime/CompatibilityOverrideConcurrency.cpp index 622305680ff5d..68277115b19ba 100644 --- a/unittests/runtime/CompatibilityOverrideConcurrency.cpp +++ b/unittests/runtime/CompatibilityOverrideConcurrency.cpp @@ -116,10 +116,6 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_job_run) { swift_job_run(nullptr, ExecutorRef::generic()); } -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_getCurrent) { - swift_task_getCurrent(); -} - TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_getCurrentExecutor) { swift_task_getCurrentExecutor(); } @@ -142,23 +138,17 @@ TEST_F(CompatibilityOverrideConcurrencyTest, swift_task_enqueueMainExecutor(nullptr); } -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_create_f) { - swift_task_create_f(swift::JobFlags(), nullptr, 0); -} - -TEST_F(CompatibilityOverrideConcurrencyTest, - test_swift_task_create_group_future_f) { - swift_task_create_group_future_f(swift::JobFlags(), nullptr, nullptr, nullptr, - 0); +TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_create_common) { + swift_task_create_common(0, nullptr, nullptr, nullptr, nullptr, 0); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_future_wait) { - swift_task_future_wait(nullptr, nullptr, nullptr, nullptr); + swift_task_future_wait(nullptr, nullptr, nullptr, nullptr, nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_future_wait_throwing) { - swift_task_future_wait_throwing(nullptr, nullptr, nullptr, nullptr); + swift_task_future_wait_throwing(nullptr, nullptr, nullptr, nullptr, nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_continuation_resume) { @@ -175,16 +165,12 @@ TEST_F(CompatibilityOverrideConcurrencyTest, swift_continuation_throwingResumeWithError(nullptr, nullptr); } -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_asyncLet_start) { - swift_asyncLet_start(nullptr, nullptr, nullptr, nullptr); -} - TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_asyncLet_wait) { - swift_asyncLet_wait(nullptr, nullptr, nullptr, nullptr); + swift_asyncLet_wait(nullptr, nullptr, nullptr, nullptr, nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_asyncLet_wait_throwing) { - swift_asyncLet_wait(nullptr, nullptr, nullptr, nullptr); + swift_asyncLet_wait(nullptr, nullptr, nullptr, nullptr, nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_asyncLet_end) { @@ -192,7 +178,7 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_asyncLet_end) { } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_taskGroup_initialize) { - swift_taskGroup_initialize(nullptr); + swift_taskGroup_initialize(nullptr, nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_taskGroup_attachChild) { @@ -205,7 +191,8 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_taskGroup_destroy) { TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_taskGroup_wait_next_throwing) { - swift_taskGroup_wait_next_throwing(nullptr, nullptr, nullptr, nullptr); + swift_taskGroup_wait_next_throwing(nullptr, nullptr, nullptr, nullptr, + nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_taskGroup_isEmpty) { @@ -225,15 +212,19 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_taskGroup_addPending) { } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_localValuePush) { - swift_task_localValuePush(nullptr, nullptr, nullptr, nullptr); + swift_task_localValuePush(nullptr, nullptr, nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_localValueGet) { - swift_task_localValueGet(nullptr, nullptr); + swift_task_localValueGet(nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_localValuePop) { - swift_task_localValuePop(nullptr); + swift_task_localValuePop(); +} + +TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_localsCopyTo) { + swift_task_localsCopyTo(nullptr); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_addStatusRecord) { diff --git a/unittests/runtime/TaskStatus.cpp b/unittests/runtime/TaskStatus.cpp index 44511c1354ee6..c7ba403802b0f 100644 --- a/unittests/runtime/TaskStatus.cpp +++ b/unittests/runtime/TaskStatus.cpp @@ -52,14 +52,17 @@ static void simpleTaskInvokeFunction(SWIFT_ASYNC_CONTEXT AsyncContext *context, } template -static void withSimpleTask(JobFlags flags, T &&value, +static void withSimpleTask(TaskCreateFlags flags, T &&value, undeduced> invokeFn, BodyFunctionRef body) { auto taskAndContext = - swift_task_create_f(flags, - reinterpret_cast( - &simpleTaskInvokeFunction), - sizeof(ValueContext)); + swift_task_create_common(flags.getOpaqueValue(), + nullptr, + nullptr, + reinterpret_cast( + &simpleTaskInvokeFunction), + nullptr, + sizeof(ValueContext)); auto valueContext = static_cast*>(taskAndContext.InitialContext); @@ -75,7 +78,7 @@ template static void withSimpleTask(T &&value, undeduced> invokeFn, BodyFunctionRef bodyFn) { - withSimpleTask(JobKind::Task, std::forward(value), invokeFn, bodyFn); + withSimpleTask(TaskCreateFlags(), std::forward(value), invokeFn, bodyFn); } static ExecutorRef createFakeExecutor(uintptr_t value) { @@ -125,11 +128,11 @@ TEST(TaskStatusTest, cancellation_simple) { withSimpleTask(Storage{47}, [&](ValueContext *context) { auto task = swift_task_getCurrent(); - EXPECT_FALSE(task->isCancelled()); + EXPECT_FALSE(swift_task_isCancelled(task)); swift_task_cancel(task); - EXPECT_TRUE(task->isCancelled()); + EXPECT_TRUE(swift_task_isCancelled(task)); swift_task_cancel(task); - EXPECT_TRUE(task->isCancelled()); + EXPECT_TRUE(swift_task_isCancelled(task)); }, [&](AsyncTask *task) { swift_job_run(task, createFakeExecutor(1234)); }); @@ -143,7 +146,7 @@ TEST(TaskStatusTest, deadline) { withSimpleTask(Storage{47}, [&](ValueContext *context) { auto task = swift_task_getCurrent(); - EXPECT_FALSE(task->isCancelled()); + EXPECT_FALSE(swift_task_isCancelled(task)); TaskDeadline deadlineOne = { 1234 }; TaskDeadline deadlineTwo = { 2345 }; @@ -250,7 +253,7 @@ TEST(TaskStatusTest, deadline) { // Cancel. swift_task_cancel(task); - EXPECT_TRUE(task->isCancelled()); + EXPECT_TRUE(swift_task_isCancelled(task)); // We should report already cancelled now. nearest = swift_task_getNearestDeadline(task); @@ -275,7 +278,7 @@ TEST(TaskStatusTest, deadline) { nearest = swift_task_getNearestDeadline(task); EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind); - EXPECT_TRUE(task->isCancelled()); + EXPECT_TRUE(swift_task_isCancelled(task)); }, [&](AsyncTask *task) { swift_job_run(task, createFakeExecutor(1234)); }); diff --git a/userdocs/diagnostics/associated-type-requirements.md b/userdocs/diagnostics/associated-type-requirements.md deleted file mode 100644 index 47c5dda89ee47..0000000000000 --- a/userdocs/diagnostics/associated-type-requirements.md +++ /dev/null @@ -1,28 +0,0 @@ -# Using Protocols with `Self` or Associated Type Requirements - -Protocols in Swift may be used as types, as part of a generic constraint, or as part of an opaque result type. - -```swift -// `CustomStringConvertible` can be used as a type. -func foo(bar: CustomStringConvertible) { /* ... */ } - -// ...or as a generic constraint on `T`. -func bar(baz: T) { /* ... */ } - -// ...or as part of an opaque result type. -func baz() -> some CustomStringConvertible { /* ... */ } -``` - -While all Swift protocols can be used as generic constraints and as part of opaque result types, not all protocols can be used as types. Specifically, if a protocol has a requirement which references `Self` (in contexts other than a function's return type) or an associated type, it cannot be used as a type. For example, the protocol `Equatable` requires `static func == (lhs: Self, rhs: Self) -> Bool`, and the protocol `Identifiable` requires `var id: ID { get }`, where `ID` is an associated type. As a result, the following code is not allowed: - -```swift -func foo(bar: Equatable) { /* ... */ } -// error: protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements - -func foo(bar: Identifiable) { /* ... */ } -// error: protocol 'Identifiable' can only be used as a generic constraint because it has Self or associated type requirements -``` - -These `Self` or associated type requirements cannot be used via a protocol type because they do not have a well-defined meaning without a concrete conforming type. Therefore, Swift does not support the use of protocols as types if they have such `Self` or associated type requirements, since those types would be able to present only a potentially unusable subset of the interface required for instances of concrete conforming types. - -When working with protocols that have `Self` or associated type requirements, most use cases can be supported by constrained generics, opaque result types, or manual type erasure. To learn more, see the sections on [protocols](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html), [generics](https://docs.swift.org/swift-book/LanguageGuide/Generics.html), and [opaque types](https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html) in _The Swift Programming Language_. diff --git a/userdocs/diagnostics/complex-closure-inference.md b/userdocs/diagnostics/complex-closure-inference.md index ad769a86c01b1..99f53362af1ad 100644 --- a/userdocs/diagnostics/complex-closure-inference.md +++ b/userdocs/diagnostics/complex-closure-inference.md @@ -10,7 +10,7 @@ let doubler = { If a closure body is not a single expression, it will not be considered when inferring the closure type. This is consistent with how type inference works in other parts of the language, where it proceeds one statement at a time. For example, in the following code an error will be reported because the type of `evenDoubler` cannot be inferred from its surrounding context and no signature was provided: ```swift -// error: unable to infer complex closure return type; add explicit type to disambiguate +// error: cannot infer return type for closure with multiple statements; add explicit type to disambiguate let evenDoubler = { x in if x.isMultiple(of: 2) { return x * 2 diff --git a/userdocs/diagnostics/existential-member-access-limitations.md b/userdocs/diagnostics/existential-member-access-limitations.md new file mode 100644 index 0000000000000..4ae0be9fd5881 --- /dev/null +++ b/userdocs/diagnostics/existential-member-access-limitations.md @@ -0,0 +1,54 @@ +# Using Protocol Members with References to `Self` or `Self`-rooted Associated Types + +Protocol requirements and protocol extension members may be accessed via a conformance constraint on a generic parameter, an opaque result type, or via the protocol type itself: + +```swift +// An appropriately constrained generic parameter. +func foo(arg: T) { + let description: String = arg.description +} + +do { + // An appropriately constrained opaque result type. + func foo() -> some CustomStringConvertible { true } + + let description: String = foo().description +} + +// The protocol type. +func foo(arg: CustomStringConvertible) { + let description: String = arg.description +} +``` + +While the former two options enable full access to the protocol interface, not all members may be accessible when the protocol is used as a type and not a constraint. Specifically, a protocol member cannot be accessed on a protocol type when its type signature contains a reference to `Self` or a `Self`-rooted associated type. Accessing such members on a protocol type is not supported because today the compiler does not have a well-defined meaning and means of representation for `Self` and `Self`-rooted associated types with respect to a protocol type `P`. As a result, the following code is not allowed: + +```swift +protocol Shape { + func matches(_ other: Self) -> Bool +} + +func foo(_ shape: Shape) { + // error: member 'matches' cannot be used on value of protocol type 'Shape'; use a generic constraint instead + shape.matches(shape) +} + +func foo(_ arg: Identifiable) { + // error: member 'id' cannot be used on value of protocol type 'Identifiable'; use a generic constraint instead + _ = arg.id +} +``` + +An exception to this limitation are members that contain `Self` only in [covariant](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)) position (such as a method result type), where `Self` can be safely substituted with the protocol or protocol composition type used to access the member — a representable supertype. On the other hand, resorting to this ploy in contravariant parameter type position, like allowing one to pass a type-erased value to a method that accepts `Self`, is not type-safe and would expose the opportunity to pass in an argument of non-matching type. + +```swift +protocol Shape { + func duplicate() -> Self +} + +func duplicateShape(_ shape: Shape) -> Shape { + return shape.duplicate // OK, produces a value of type 'Shape' +} +``` + +Most use cases involving usage of protocol members that fall under the above restriction can instead be supported by constrained generics, opaque result types, or manual type-erasing wrappers. To learn more, see the sections on [protocols](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html), [generics](https://docs.swift.org/swift-book/LanguageGuide/Generics.html), and [opaque types](https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html) in the Language Guide. For a better understanding of existential types in particular, and an in-depth exploration of the relationships among these built-in abstraction models, we recommend reading the [design document for improving the UI of the generics model](https://forums.swift.org/t/improving-the-ui-of-generics/22814). diff --git a/userdocs/diagnostics/multiple-inheritance.md b/userdocs/diagnostics/multiple-inheritance.md new file mode 100644 index 0000000000000..ab65257a3c097 --- /dev/null +++ b/userdocs/diagnostics/multiple-inheritance.md @@ -0,0 +1,37 @@ +# Multiple Inheritance + +In some programming languages, a class can inherit the interface of multiple base classes. Known as multiple inheritance, this feature can add significant complexity to the language and is unsupported in Swift. Instead, Swift allows composition of interfaces using protocols. + +Consider the following example: + +```swift +protocol Utensil { + var name: String { get } +} + +protocol ServingUtensil: Utensil { + func serve() +} + +extension ServingUtensil { + func serve() { /* Default implementation. */ } +} + +protocol Fork: Utensil { + func spear() +} + +protocol Spoon: Utensil { + func scoop() +} + +struct CarvingFork: ServingUtensil, Fork { /* ... */ } +struct Spork: Spoon, Fork { /* ... */ } +struct Ladle: ServingUtensil, Spoon { /* ... */ } +``` + +Swift protocols can declare interfaces that must be implemented by each conforming type (like abstract class members in other programming languages such as C# or Java), and they can also provide overridable default implementations for those requirements in protocol extensions. + +When class inheritance and protocol conformances are used together, subclasses inherit protocol conformances from base classes, introducing additional complexity. For example, the default implementation of a protocol requirement not overridden in the conforming base class also cannot be overridden in any subclass ([SR-103](https://bugs.swift.org/browse/SR-103)). + +To learn more about defining and adopting protocols, see the [Protocols](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html) section in _The Swift Programming Language_. To learn more about class inheritance, see the [Inheritance](https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html) section in _The Swift Programming Language_. diff --git a/utils/api_checker/CMakeLists.txt b/utils/api_checker/CMakeLists.txt index 8452f1aa97955..e664a73ddd996 100644 --- a/utils/api_checker/CMakeLists.txt +++ b/utils/api_checker/CMakeLists.txt @@ -28,6 +28,6 @@ add_custom_command(OUTPUT "${dest}" add_custom_target("symlink_abi_checker_data" ALL DEPENDS "${dest}" COMMENT "Symlinking ABI checker baseline data to ${dest}") -if(TARGET swift-api-digester) - add_dependencies(swift-api-digester symlink_abi_checker_data) +if(TARGET swift-frontend) + add_dependencies(swift-frontend symlink_abi_checker_data) endif() diff --git a/utils/build-parser-lib b/utils/build-parser-lib index a5593e5b45a16..48dc69c1d8f2b 100755 --- a/utils/build-parser-lib +++ b/utils/build-parser-lib @@ -169,6 +169,7 @@ class Builder(object): "-DSWIFT_HOST_VARIANT=" + self.host, "-DSWIFT_HOST_VARIANT_SDK=" + host_sdk, "-DSWIFT_HOST_VARIANT_ARCH=" + self.arch, + "-DCMAKE_Swift_COMPILER_TARGET=" + host_triple, "-DCMAKE_C_FLAGS=" + llvm_c_flags, "-DCMAKE_CXX_FLAGS=" + llvm_c_flags, ] diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 9c215937da496..5b8ab8d7b89f2 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -14,10 +14,10 @@ # Buildbots for Darwin OSes #===------------------------------------------------------------------------===# [preset: mixin_buildbot_install_components] -swift-install-components=compiler;clang-builtin-headers;stdlib;sdk-overlay;parser-lib;editor-integration;tools;toolchain-tools;testsuite-tools;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers; +swift-install-components=back-deployment;compiler;clang-builtin-headers;stdlib;sdk-overlay;parser-lib;editor-integration;tools;toolchain-tools;testsuite-tools;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers; [preset: mixin_buildbot_install_components_with_clang] -swift-install-components=compiler;clang-resource-dir-symlink;stdlib;sdk-overlay;parser-lib;toolchain-tools;license;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers +swift-install-components=back-deployment;compiler;clang-resource-dir-symlink;stdlib;sdk-overlay;parser-lib;toolchain-tools;license;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers llvm-install-components=llvm-cov;llvm-profdata;IndexStore;clang;clang-resource-headers;compiler-rt;clangd;dsymutil [preset: mixin_buildbot_trunk_base] @@ -37,9 +37,13 @@ build-swift-stdlib-unittest-extra install-llvm install-swift +install-back-deploy-concurrency skip-test-cmark +# Build concurrency back-deployment binaries +back-deploy-concurrency + # Path to the root of the installation filesystem. install-destdir=%(install_destdir)s @@ -334,7 +338,8 @@ swiftpm swift-driver indexstore-db sourcekit-lsp - +# Failing to build in CI: rdar://78408440 +# swift-inspect swiftsyntax swiftsyntax-verify-generated-files @@ -600,6 +605,8 @@ swiftsyntax-verify-generated-files swift-driver indexstore-db sourcekit-lsp +# Failing to build in CI: rdar://78408440 +# swift-inspect install-llvm install-swift install-llbuild @@ -629,6 +636,12 @@ swift-primary-variant-arch=x86_64 # Don't build the benchmarks skip-build-benchmarks +# Skip playground tests +skip-test-playgroundsupport + +# Enable libswift +libswift + [preset: buildbot_incremental,tools=RA,stdlib=RD,smoketest=macosx,flto] mixin-preset=buildbot_incremental,tools=RA,stdlib=RD,smoketest=macosx build-subdir=buildbot_incremental @@ -1179,6 +1192,8 @@ lldb llbuild swiftpm swift-driver +# Failing to build in CI: rdar://78408440 +# swift-inspect swiftsyntax skstresstester swiftevolve @@ -1187,6 +1202,9 @@ libcxx indexstore-db sourcekit-lsp +# Build concurrency back-deployment binaries +back-deploy-concurrency + # Don't generate the SwiftSyntax gyb files. Instead verify that up-to-date ones # are checked in. swiftsyntax-verify-generated-files @@ -1206,6 +1224,9 @@ verbose-build build-ninja build-swift-stdlib-unittest-extra +# Don't build the benchmarks +skip-build-benchmarks + # When building for an Xcode toolchain, don't copy the Swift Resource/ directory # into the LLDB.framework. LLDB.framework will be installed alongside a Swift # compiler, so LLDB should use its resource directory directly. @@ -1228,6 +1249,7 @@ install-swiftevolve install-playgroundsupport install-libcxx install-sourcekit-lsp +install-back-deploy-concurrency install-destdir=%(install_destdir)s @@ -1503,9 +1525,14 @@ build-swift-stdlib-unittest-extra libcxx +# Enable libswift +libswift + # Build llbuild & swiftpm here llbuild swiftpm +# Failing to build in CI: rdar://78408440 +# swift-inspect swift-driver swiftsyntax @@ -1530,6 +1557,9 @@ swiftevolve # Build Playground support playgroundsupport +# Skip Playground tests +skip-test-playgroundsupport + # Skip build and test for tvOS skip-build-tvos skip-test-tvos @@ -1675,6 +1705,8 @@ skip-test-toolchain-benchmarks skip-test-llbuild +skip-early-swift-driver + #===------------------------------------------------------------------------===# # Test llbuild on macOS builder #===------------------------------------------------------------------------===# @@ -2311,6 +2343,7 @@ skip-test-cmark # This triggers the stdlib standalone build: don't build the native tools from # scratch, ie the compiler. build-swift-tools=0 +skip-early-swift-driver # Then set the paths to our native tools. If compiling against a toolchain, # these should all be the ./usr/bin directory. @@ -2320,6 +2353,22 @@ native-clang-tools-path=%(toolchain_path)s build-ninja lit-args=--filter=stdlib/ +only-executable-test + +[preset: stdlib_RD_standalone,build] +mixin-preset=stdlib_base_standalone + +build-subdir=stdlib_RD_standalone +release-debuginfo +no-assertions + +verbose-build + +[preset: stdlib_RD_standalone,build,test] +mixin-preset=stdlib_RD_standalone,build + +test +validation-test [preset: stdlib_RA_standalone,build] mixin-preset=stdlib_base_standalone @@ -2387,15 +2436,20 @@ verbose-build [preset: mixin_stdlib_minimal] enable-experimental-differentiable-programming=0 enable-experimental-concurrency=0 +enable-experimental-distributed=0 build-swift-dynamic-sdk-overlay=0 build-swift-dynamic-stdlib=0 build-swift-static-stdlib=1 swift-objc-interop=0 swift-enable-compatibility-overrides=0 swift-runtime-macho-no-dyld=1 +swift-stdlib-has-darwin-libmalloc=0 swift-stdlib-single-threaded-runtime=1 swift-stdlib-os-versioning=0 -extra-cmake-options=-DSWIFT_ENABLE_DISPATCH:BOOL=FALSE +extra-cmake-options= + -DSWIFT_ENABLE_DISPATCH:BOOL=FALSE + -DSWIFT_IMPLICIT_CONCURRENCY_IMPORT:BOOL=FALSE + -DSWIFT_STDLIB_ENABLE_PRESPECIALIZATION:BOOL=FALSE [preset: stdlib_S_standalone_minimal_macho_x86_64,build] mixin-preset= @@ -2405,6 +2459,7 @@ mixin-preset= stdlib-deployment-targets=freestanding-x86_64 swift-primary-variant-sdk=FREESTANDING swift-primary-variant-arch=x86_64 +swift-freestanding-flavor=apple swift-freestanding-sdk=macosx # For now, until clang/swiftc works correctly with "none-macho" as the OS part of target triple. swift-freestanding-triple-name=macosx11.0 @@ -2446,6 +2501,8 @@ build-subdir=compat_macos ios tvos watchos +back-deploy-concurrency +install-back-deploy-concurrency compiler-vendor=apple darwin-install-extract-symbols darwin-toolchain-alias=swift @@ -2455,7 +2512,7 @@ darwin-toolchain-display-name=Swift Development Snapshot darwin-toolchain-name=swift-DEVELOPMENT-SNAPSHOT darwin-toolchain-version=3.999.999 llvm-install-components=libclang;libclang-headers;dsymutil -swift-install-components=compiler;clang-builtin-headers;stdlib;sdk-overlay;license;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers +swift-install-components=back-deployment;compiler;clang-builtin-headers;stdlib;sdk-overlay;license;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers symbols-package=%(symbols_package)s install-symroot=%(install_symroot)s diff --git a/utils/build-script b/utils/build-script index 78b3b0b5cb371..8a0be41769618 100755 --- a/utils/build-script +++ b/utils/build-script @@ -18,7 +18,6 @@ from __future__ import absolute_import, print_function, unicode_literals import json import os -import pipes import platform import sys import time @@ -35,16 +34,16 @@ from build_swift.build_swift.constants import SWIFT_SOURCE_ROOT import six -from swift_build_support.swift_build_support import build_graph -from swift_build_support.swift_build_support import products +from swift_build_support.swift_build_support import build_script_invocation from swift_build_support.swift_build_support import shell from swift_build_support.swift_build_support import targets from swift_build_support.swift_build_support import workspace from swift_build_support.swift_build_support.cmake import CMake -from swift_build_support.swift_build_support.host_specific_configuration \ - import HostSpecificConfiguration from swift_build_support.swift_build_support.targets import StdlibDeploymentTarget from swift_build_support.swift_build_support.toolchain import host_toolchain +from swift_build_support.swift_build_support.utils \ + import exit_rejecting_arguments +from swift_build_support.swift_build_support.utils import fatal_error # ----------------------------------------------------------------------------- @@ -53,13 +52,11 @@ from swift_build_support.swift_build_support.toolchain import host_toolchain # These versions are community sourced. At any given time only the Xcode # version used by Swift CI is officially supported. See ci.swift.org _SUPPORTED_XCODE_BUILDS = [ - ("12.2 beta 3", "12B5035g"), - ("12.2 Release Candidate", "12B5044c"), - ("12.2", "12B45b"), ("12.3", "12C33"), ("12.4", "12D4e"), - ("12.5 beta 3", "12E5244e"), - ("12.5", "12E262") + ("12.5", "12E262"), + ("13.0 beta", "13A5154h"), + ("13.0 beta 4", "13A5201i") ] # ----------------------------------------------------------------------------- @@ -75,16 +72,6 @@ def print_note(message, stream=sys.stdout): stream.flush() -def fatal_error(message, stream=sys.stderr): - """Writes a message to the given stream and exits. By default this - function outputs to stderr. - """ - - stream.write('[{}] ERROR: {}\n'.format(sys.argv[0], message)) - stream.flush() - sys.exit(1) - - def clean_delay(): """Provide a short delay so accidentally invoked clean builds can be canceled. @@ -98,13 +85,6 @@ def clean_delay(): print('\b\b\b\bnow.') -def exit_rejecting_arguments(message, parser=None): - print(message, file=sys.stderr) - if parser: - parser.print_usage(sys.stderr) - sys.exit(2) # 2 is the same as `argparse` error exit code. - - def initialize_runtime_environment(): """Change the program environment for building. """ @@ -414,712 +394,10 @@ def apply_default_arguments(toolchain, args): '-DSWIFT_DARWIN_MODULE_ARCHS:STRING={}'.format( args.swift_darwin_module_archs)) - -# ----------------------------------------------------------------------------- -# Build Script Impl Wrapping - -class BuildScriptInvocation(object): - """Represent a single build script invocation. - """ - - def __init__(self, toolchain, args): - self.toolchain = toolchain - self.args = args - - self.workspace = workspace.Workspace( - source_root=SWIFT_SOURCE_ROOT, - build_root=os.path.join(SWIFT_BUILD_ROOT, args.build_subdir)) - - self.build_libparser_only = args.build_libparser_only - - @property - def install_all(self): - return self.args.install_all or self.args.infer_dependencies - - def build_ninja(self): - if not os.path.exists(self.workspace.source_dir("ninja")): - fatal_error( - "can't find source directory for ninja " - "(tried %s)" % (self.workspace.source_dir("ninja"))) - - ninja_build = products.Ninja.new_builder( - args=self.args, - toolchain=self.toolchain, - workspace=self.workspace, - host=StdlibDeploymentTarget.get_target_for_name( - self.args.host_target)) - ninja_build.build() - self.toolchain.ninja = ninja_build.ninja_bin_path - - def convert_to_impl_arguments(self): - """convert_to_impl_arguments() -> (env, args) - - Convert the invocation to an environment and list of arguments suitable - for invoking `build-script-impl`. - """ - - # Create local shadows, for convenience. - args = self.args - toolchain = self.toolchain - - cmake = CMake(args=args, - toolchain=self.toolchain) - - impl_args = [ - "--workspace", self.workspace.source_root, - "--build-dir", self.workspace.build_root, - "--install-prefix", args.install_prefix, - "--host-target", args.host_target, - "--stdlib-deployment-targets={}".format( - " ".join(args.stdlib_deployment_targets)), - "--host-cc", toolchain.cc, - "--host-cxx", toolchain.cxx, - "--darwin-xcrun-toolchain", args.darwin_xcrun_toolchain, - "--darwin-deployment-version-osx=%s" % ( - args.darwin_deployment_version_osx), - "--darwin-deployment-version-ios=%s" % ( - args.darwin_deployment_version_ios), - "--darwin-deployment-version-tvos=%s" % ( - args.darwin_deployment_version_tvos), - "--darwin-deployment-version-watchos=%s" % ( - args.darwin_deployment_version_watchos), - "--cmake", toolchain.cmake, - "--cmark-build-type", args.cmark_build_variant, - "--llvm-build-type", args.llvm_build_variant, - "--swift-build-type", args.swift_build_variant, - "--swift-stdlib-build-type", args.swift_stdlib_build_variant, - "--lldb-build-type", args.lldb_build_variant, - "--foundation-build-type", args.foundation_build_variant, - "--libdispatch-build-type", args.libdispatch_build_variant, - "--libicu-build-type", args.libicu_build_variant, - "--xctest-build-type", args.build_variant, - "--llbuild-build-type", args.build_variant, - "--swift-enable-assertions", str(args.swift_assertions).lower(), - "--swift-stdlib-enable-assertions", str( - args.swift_stdlib_assertions).lower(), - "--swift-analyze-code-coverage", str( - args.swift_analyze_code_coverage).lower(), - "--llbuild-enable-assertions", str( - args.llbuild_assertions).lower(), - "--lldb-assertions", str( - args.lldb_assertions).lower(), - "--cmake-generator", args.cmake_generator, - "--build-jobs", str(args.build_jobs), - "--common-cmake-options=%s" % ' '.join( - pipes.quote(opt) for opt in cmake.common_options()), - "--build-args=%s" % ' '.join( - pipes.quote(arg) for arg in cmake.build_args()), - "--dsymutil-jobs", str(args.dsymutil_jobs), - ] - - # Compute any product specific cmake arguments. - # - # NOTE: The sum(list(...)) is b/c compute_product_classes returns a - # tuple of lists of which the first is the build-script-impl products - # and the second is the non-build-script-impl-products. It guarantees - # that when we concatenate these two lists together we get a valid - # dependency graph. - for product_class in sum(list(self.compute_product_classes()), []): - if not product_class.is_build_script_impl_product(): - continue - - product_name = product_class.product_name() - product_source_name = product_class.product_source_name() - source_dir = self.workspace.source_dir(product_source_name) - - if not os.path.exists(source_dir): - fatal_error( - "can't find source directory for %s " - "(tried %s)" % (product_name, source_dir)) - - product = product_class( - args=args, - toolchain=self.toolchain, - source_dir=source_dir, - # FIXME: This is incorrect since it always assumes the host - # target I think? - build_dir=self.workspace.build_dir( - args.host_target, product_name)) - cmake_opts = product.cmake_options - - # FIXME: We should be using pipes.quote here but we run into issues - # with build-script-impl/cmake not being happy with all of the - # extra "'" in the strings. To fix this easily, we really need to - # just invoke cmake from build-script directly rather than futzing - # with build-script-impl. This makes even more sense since there - # really isn't a security issue here. - if cmake_opts: - impl_args += [ - "--{}-cmake-options={}".format( - product_name, ' '.join(cmake_opts)) - ] - - if args.build_stdlib_deployment_targets: - impl_args += [ - "--build-stdlib-deployment-targets", " ".join( - args.build_stdlib_deployment_targets)] - if args.cross_compile_hosts: - impl_args += [ - "--cross-compile-hosts", " ".join(args.cross_compile_hosts)] - - if args.test_paths: - impl_args += ["--test-paths", " ".join(args.test_paths)] - - if toolchain.ninja: - impl_args += ["--ninja-bin=%s" % toolchain.ninja] - if args.distcc: - impl_args += [ - "--distcc", - "--distcc-pump=%s" % toolchain.distcc_pump - ] - if args.sccache: - args.cmake_c_launcher = toolchain.sccache - args.cmake_cxx_launcher = toolchain.sccache - - # *NOTE* We use normal cmake to pass through tsan/ubsan options. We do - # NOT pass them to build-script-impl. - if args.enable_asan: - impl_args += ["--enable-asan"] - # If we are on linux, disable leak detection when running ASAN. We - # have a separate bot that checks for leaks. - if platform.system() == 'Linux': - os.environ['ASAN_OPTIONS'] = 'detect_leaks=0' - if args.enable_ubsan: - impl_args += ["--enable-ubsan"] - - # If we have lsan, we need to export our suppression list. The actual - # passing in of the LSAN flag is done via the normal cmake method. We - # do not pass the flag to build-script-impl. - if args.enable_lsan: - supp_file = os.path.join(SWIFT_SOURCE_ROOT, SWIFT_REPO_NAME, - "utils", - "lsan_leaks_suppression_list.txt") - os.environ['LSAN_OPTIONS'] = 'suppressions={}'.format(supp_file) - if args.verbose_build: - impl_args += ["--verbose-build"] - if args.install_symroot: - impl_args += [ - "--install-symroot", os.path.abspath(args.install_symroot) - ] - if args.install_destdir: - impl_args += [ - "--install-destdir", os.path.abspath(args.install_destdir) - ] - - if args.skip_build: - impl_args += ["--skip-build"] - if not args.build_benchmarks: - impl_args += ["--skip-build-benchmarks"] - - if args.swift_disable_dead_stripping: - args.extra_cmake_options.append('-DSWIFT_DISABLE_DEAD_STRIPPING:BOOL=TRUE') - - # Then add subproject install flags that either skip building them /or/ - # if we are going to build them and install_all is set, we also install - # them. - conditional_subproject_configs = [ - (args.build_cmark, "cmark"), - (args.build_llvm, "llvm"), - (args.build_swift, "swift"), - (args.build_foundation, "foundation"), - (args.build_xctest, "xctest"), - (args.build_lldb, "lldb"), - (args.build_llbuild, "llbuild"), - (args.build_libcxx, "libcxx"), - (args.build_libdispatch, "libdispatch"), - (args.build_libicu, "libicu") - ] - for (should_build, string_name) in conditional_subproject_configs: - if not should_build and not self.args.infer_dependencies: - impl_args += ["--skip-build-{}".format(string_name)] - elif self.install_all: - impl_args += ["--install-{}".format(string_name)] - - if args.build_swift_dynamic_stdlib: - impl_args += ["--build-swift-dynamic-stdlib"] - if args.build_swift_static_stdlib: - impl_args += ["--build-swift-static-stdlib"] - if args.build_swift_stdlib_unittest_extra: - impl_args += ["--build-swift-stdlib-unittest-extra"] - if args.build_swift_dynamic_sdk_overlay: - impl_args += ["--build-swift-dynamic-sdk-overlay"] - if args.build_swift_static_sdk_overlay: - impl_args += ["--build-swift-static-sdk-overlay"] - - if not args.build_android: - impl_args += ["--skip-build-android"] - if not args.build_clang_tools_extra: - impl_args += ["--skip-build-clang-tools-extra"] - - if not args.test and not args.long_test and not args.stress_test: - impl_args += ["--skip-test-swift"] - if not args.test: - impl_args += [ - "--skip-test-cmark", - "--skip-test-lldb", - "--skip-test-llbuild", - "--skip-test-xctest", - "--skip-test-foundation", - "--skip-test-libdispatch", - "--skip-test-libicu", - ] - if args.build_runtime_with_host_compiler: - impl_args += ["--build-runtime-with-host-compiler"] - if args.validation_test: - impl_args += ["--validation-test"] - if args.long_test: - impl_args += ["--long-test"] - if args.stress_test: - impl_args += ["--stress-test"] - if args.skip_local_build: - impl_args += ["--skip-local-build"] - if args.only_executable_test: - impl_args += ["--only-executable-test"] - if not args.benchmark: - impl_args += ["--skip-test-benchmarks"] - if args.build_libparser_only: - impl_args += ["--build-libparser-only"] - if args.android: - impl_args += [ - "--android-arch", args.android_arch, - "--android-ndk", args.android_ndk, - "--android-api-level", args.android_api_level, - "--android-ndk-gcc-version", args.android_ndk_gcc_version, - "--android-icu-uc", args.android_icu_uc, - "--android-icu-uc-include", args.android_icu_uc_include, - "--android-icu-i18n", args.android_icu_i18n, - "--android-icu-i18n-include", args.android_icu_i18n_include, - "--android-icu-data", args.android_icu_data, - ] - # If building natively on an Android host, only pass the API level. - if StdlibDeploymentTarget.Android.contains(StdlibDeploymentTarget - .host_target().name): - impl_args += ["--android-api-level", args.android_api_level] - if args.android_deploy_device_path: - impl_args += [ - "--android-deploy-device-path", - args.android_deploy_device_path, - ] - - if platform.system() == 'Darwin': - impl_args += [ - "--toolchain-prefix", - targets.darwin_toolchain_prefix( - args.install_prefix), - "--host-lipo", toolchain.lipo, - ] - - # Isolate build from the system; Darwin toolchains build against SDKs. - # For additional isolation, disable pkg-config. Homebrew's pkg-config - # prioritizes CommandLineTools paths, resulting in compile errors. - args.extra_cmake_options += [ - '-DCMAKE_IGNORE_PATH=/usr/lib;/usr/local/lib;/lib', - '-DPKG_CONFIG_EXECUTABLE=/usr/bin/false', - ] - - if toolchain.libtool is not None: - impl_args += [ - "--host-libtool", toolchain.libtool, - ] - if args.native_clang_tools_path is not None: - impl_args += [ - "--native-clang-tools-path=%s" % args.native_clang_tools_path - ] - if args.native_llvm_tools_path is not None: - impl_args += [ - "--native-llvm-tools-path=%s" % args.native_llvm_tools_path - ] - if args.native_swift_tools_path is not None: - impl_args += [ - "--native-swift-tools-path=%s" % args.native_swift_tools_path - ] - - # If we have extra_swift_args, combine all of them together and then - # add them as one command. - if args.extra_swift_args: - impl_args += [ - "--extra-swift-args=%s" % ';'.join(args.extra_swift_args) - ] - - # Enable macCatalyst - if args.maccatalyst: - (args.extra_cmake_options - .append('-DSWIFT_ENABLE_MACCATALYST:BOOL=TRUE')) - if args.maccatalyst_ios_tests: - impl_args += ["--darwin-test-maccatalyst-ios-like=1"] - - # If we have extra_cmake_options, combine all of them together and then - # add them as one command. - if args.extra_cmake_options: - impl_args += [ - "--extra-cmake-options=%s" % ' '.join( - pipes.quote(opt) for opt in args.extra_cmake_options) - ] - - if args.lto_type is not None: - impl_args += [ - "--llvm-enable-lto=%s" % args.lto_type, - "--swift-tools-enable-lto=%s" % args.lto_type - ] - if args.llvm_max_parallel_lto_link_jobs is not None: - impl_args += [ - "--llvm-num-parallel-lto-link-jobs=%s" % - min(args.llvm_max_parallel_lto_link_jobs, args.build_jobs) - ] - if args.swift_tools_max_parallel_lto_link_jobs is not None: - impl_args += [ - "--swift-tools-num-parallel-lto-link-jobs=%s" % - min(args.swift_tools_max_parallel_lto_link_jobs, - args.build_jobs) - ] - - impl_args += args.build_script_impl_args - - if args.dry_run: - impl_args += ["--dry-run"] - - if args.clang_profile_instr_use: - impl_args += [ - "--clang-profile-instr-use=%s" % - os.path.abspath(args.clang_profile_instr_use) - ] - - if args.lit_args: - impl_args += ["--llvm-lit-args=%s" % args.lit_args] - - if args.coverage_db: - impl_args += [ - "--coverage-db=%s" % - os.path.abspath(args.coverage_db) - ] - - if args.llvm_install_components: - impl_args += [ - "--llvm-install-components=%s" % args.llvm_install_components - ] - - if not args.clean_llbuild: - impl_args += [ - "--skip-clean-llbuild" - ] - - if args.llvm_ninja_targets: - impl_args += [ - "--llvm-ninja-targets=%s" % ' '.join(args.llvm_ninja_targets) - ] - - if args.llvm_ninja_targets_for_cross_compile_hosts: - impl_args += [ - "--llvm-ninja-targets-for-cross-compile-hosts=%s" % - ' '.join(args.llvm_ninja_targets_for_cross_compile_hosts) - ] - - # Compute the set of host-specific variables, which we pass through to - # the build script via environment variables. - host_specific_variables = self.compute_host_specific_variables() - impl_env = {} - for (host_target, options) in host_specific_variables.items(): - for (name, value) in options.items(): - # We mangle into an environment variable we can easily evaluate - # from the `build-script-impl`. - impl_env["HOST_VARIABLE_{}__{}".format( - host_target.replace("-", "_"), name)] = value - - return (impl_env, impl_args) - - def compute_host_specific_variables(self): - """compute_host_specific_variables(args) -> dict - - Compute the host-specific options, organized as a dictionary keyed by - host of options. - """ - - args = self.args - - options = {} - for host_target in [args.host_target] + args.cross_compile_hosts: - # Compute the host specific configuration. - try: - config = HostSpecificConfiguration(host_target, args) - except argparse.ArgumentError as e: - exit_rejecting_arguments(six.text_type(e)) - - # Convert into `build-script-impl` style variables. - options[host_target] = { - "SWIFT_SDKS": " ".join(sorted( - config.sdks_to_configure)), - "SWIFT_STDLIB_TARGETS": " ".join( - config.swift_stdlib_build_targets), - "SWIFT_BENCHMARK_TARGETS": " ".join( - config.swift_benchmark_build_targets), - "SWIFT_RUN_BENCHMARK_TARGETS": " ".join( - config.swift_benchmark_run_targets), - "SWIFT_TEST_TARGETS": " ".join( - config.swift_test_run_targets), - } - - return options - - def compute_product_classes(self): - """compute_product_classes() -> (list, list) - - Compute the list first of all build-script-impl products and then all - non-build-script-impl products. It is assumed that concatenating the two - lists together will result in a valid dependency graph for the - compilation. - - """ - - # FIXME: This is a weird division (returning a list of class objects), - # but it matches the existing structure of the `build-script-impl`. - impl_product_classes = [] - if self.args.build_cmark: - impl_product_classes.append(products.CMark) - - # If --skip-build-llvm is passed in, LLVM cannot be completely disabled, as - # Swift still needs a few LLVM targets like tblgen to be built for it to be - # configured. Instead, handle this in build-script-impl for now. - impl_product_classes.append(products.LLVM) - if self.args.build_libcxx: - impl_product_classes.append(products.LibCXX) - if self.args.build_libicu: - impl_product_classes.append(products.LibICU) - if self.args.build_swift: - impl_product_classes.append(products.Swift) - if self.args.build_lldb: - impl_product_classes.append(products.LLDB) - if self.args.build_libdispatch: - impl_product_classes.append(products.LibDispatch) - if self.args.build_foundation: - impl_product_classes.append(products.Foundation) - if self.args.build_xctest: - impl_product_classes.append(products.XCTest) - if self.args.build_llbuild: - impl_product_classes.append(products.LLBuild) - # Sanity check that all of our impl classes are actually - # build_script_impl products. - for prod in impl_product_classes: - assert(prod.is_build_script_impl_product()) - - product_classes = [] - if self.args.build_swiftpm: - product_classes.append(products.SwiftPM) - if self.args.build_swiftsyntax: - product_classes.append(products.SwiftSyntax) - if self.args.build_skstresstester: - product_classes.append(products.SKStressTester) - if self.args.build_swiftformat: - product_classes.append(products.SwiftFormat) - if self.args.build_swiftevolve: - product_classes.append(products.SwiftEvolve) - if self.args.build_indexstoredb: - product_classes.append(products.IndexStoreDB) - if self.args.build_playgroundsupport: - product_classes.append(products.PlaygroundSupport) - if self.args.build_sourcekitlsp: - product_classes.append(products.SourceKitLSP) - if self.args.build_toolchainbenchmarks: - product_classes.append(products.Benchmarks) - if self.args.build_swift_inspect: - product_classes.append(products.SwiftInspect) - if self.args.tsan_libdispatch_test: - product_classes.append(products.TSanLibDispatch) - - # Keep SwiftDriver at last. - # swift-driver's integration with the build scripts is not fully - # supported. Using swift-driver to build these products may hit - # failures. - if self.args.build_swift_driver or self.args.install_swift_driver: - product_classes.append(products.SwiftDriver) - # Sanity check that all of our non-impl classes are actually - # not build_script_impl products. - for prod in product_classes: - assert(not prod.is_build_script_impl_product()) - - # Now that we have our two lists of product_classes, if we are asked to - # infer dependencies, infer the dependencies now and then re-split the - # list. - if self.args.infer_dependencies: - combined = impl_product_classes + product_classes - if self.args.verbose_build: - print("-- Build Graph Inference --") - print("Initial Product List:") - for p in combined: - print(" {}".format(p.product_name())) - - # Now that we have produced the schedule, resplit. We require our - # dependencies to respect our build-script-impl property. This means - # that no build-script-impl products can have dependencies on - # non-build-script-impl products. Otherwise, it would be unsafe to - # re-order build-script-impl products in front of non - # build-script-impl products. - impl_product_classes = [] - product_classes = [] - is_darwin = platform.system() == 'Darwin' - final_schedule = build_graph.produce_scheduled_build(combined)[0] - for p in final_schedule: - if is_darwin and p.is_nondarwin_only_build_product(): - continue - - if p.is_build_script_impl_product(): - impl_product_classes.append(p) - else: - product_classes.append(p) - if self.args.verbose_build: - print("Final Build Order:") - for p in impl_product_classes: - print(" {}".format(p.product_name())) - for p in product_classes: - print(" {}".format(p.product_name())) - return (impl_product_classes, product_classes) - - def execute(self): - """Execute the invocation with the configured arguments.""" - - # Convert to a build-script-impl invocation. - (self.impl_env, self.impl_args) = self.convert_to_impl_arguments() - - # If using the legacy implementation, delegate all behavior to - # `build-script-impl`. - if self.args.legacy_impl: - # Execute the underlying build script implementation. - shell.call_without_sleeping([BUILD_SCRIPT_IMPL_PATH] + self.impl_args, - env=self.impl_env, echo=True) - return - - # Otherwise, we compute and execute the individual actions ourselves. - - # Compute the list of hosts to operate on. - all_host_names = [ - self.args.host_target] + self.args.cross_compile_hosts - all_hosts = [StdlibDeploymentTarget.get_target_for_name(name) - for name in all_host_names] - - # Compute the list of product classes to operate on. - # - # FIXME: This should really be per-host, but the current structure - # matches that of `build-script-impl`. - (impl_product_classes, product_classes) = self.compute_product_classes() - - # Execute each "pass". - - # Build... - for host_target in all_hosts: - # FIXME: We should only compute these once. - try: - config = HostSpecificConfiguration(host_target.name, self.args) - except argparse.ArgumentError as e: - exit_rejecting_arguments(six.text_type(e)) - print("Building the standard library for: {}".format( - " ".join(config.swift_stdlib_build_targets))) - if config.swift_test_run_targets and ( - self.args.test or self.args.long_test): - print("Running Swift tests for: {}".format( - " ".join(config.swift_test_run_targets))) - if config.swift_benchmark_run_targets and self.args.benchmark: - print("Running Swift benchmarks for: {}".format( - " ".join(config.swift_benchmark_run_targets))) - - for product_class in impl_product_classes: - self._execute_build_action(host_target, product_class) - - # Test... - for host_target in all_hosts: - for product_class in impl_product_classes: - self._execute_test_action(host_target, product_class) - - # Install... - for host_target in all_hosts: - for product_class in impl_product_classes: - self._execute_install_action(host_target, product_class) - - # Core Lipo... - self._execute_merged_host_lipo_core_action() - - # Non-build-script-impl products... - # Note: currently only supports building for the host. - for host_target in [self.args.host_target]: - for product_class in product_classes: - if product_class.is_build_script_impl_product(): - continue - product_source = product_class.product_source_name() - product_name = product_class.product_name() - if product_class.is_swiftpm_unified_build_product(): - build_dir = self.workspace.swiftpm_unified_build_dir( - host_target) - else: - build_dir = self.workspace.build_dir( - host_target, product_name) - product = product_class( - args=self.args, - toolchain=self.toolchain, - source_dir=self.workspace.source_dir(product_source), - build_dir=build_dir) - if product.should_clean(host_target): - print("--- Cleaning %s ---" % product_name) - product.clean(host_target) - if product.should_build(host_target): - print("--- Building %s ---" % product_name) - product.build(host_target) - if product.should_test(host_target): - print("--- Running tests for %s ---" % product_name) - product.test(host_target) - print("--- Finished tests for %s ---" % product_name) - if product.should_install(host_target) or \ - (self.install_all and product.should_build(host_target)): - print("--- Installing %s ---" % product_name) - product.install(host_target) - - # Extract symbols... - for host_target in all_hosts: - self._execute_extract_symbols_action(host_target) - - # Package... - for host_target in all_hosts: - self._execute_package_action(host_target) - - # Lipo... - self._execute_merged_host_lipo_action() - - def _execute_build_action(self, host_target, product_class): - action_name = "{}-{}-build".format(host_target.name, - product_class.product_name()) - self._execute_action(action_name) - - def _execute_test_action(self, host_target, product_class): - action_name = "{}-{}-test".format(host_target.name, - product_class.product_name()) - self._execute_action(action_name) - - def _execute_install_action(self, host_target, product_class): - action_name = "{}-{}-install".format(host_target.name, - product_class.product_name()) - self._execute_action(action_name) - - def _execute_extract_symbols_action(self, host_target): - action_name = "{}-extractsymbols".format(host_target.name) - self._execute_action(action_name) - - def _execute_package_action(self, host_target): - action_name = "{}-package".format(host_target.name) - self._execute_action(action_name) - - def _execute_merged_host_lipo_action(self): - self._execute_action("merged-hosts-lipo") - - def _execute_merged_host_lipo_core_action(self): - self._execute_action("merged-hosts-lipo-core") - - def _execute_action(self, action_name): - shell.call_without_sleeping( - [BUILD_SCRIPT_IMPL_PATH] + self.impl_args + - ["--only-execute", action_name], - env=self.impl_env, echo=self.args.verbose_build) - - # ----------------------------------------------------------------------------- # Main (preset) + def parse_preset_args(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, @@ -1359,7 +637,7 @@ def main_normal(): validate_arguments(toolchain, args) # Create the build script invocation. - invocation = BuildScriptInvocation(toolchain, args) + invocation = build_script_invocation.BuildScriptInvocation(toolchain, args) # Sanitize the runtime environment. initialize_runtime_environment() diff --git a/utils/build-script-impl b/utils/build-script-impl index dbd646351bb48..70e7375fccccd 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -64,6 +64,7 @@ KNOWN_SETTINGS=( build-runtime-with-host-compiler "" "use the host c++ compiler to build everything" build-stdlib-deployment-targets "all" "space-separated list that filters which of the configured targets to build the Swift standard library for, or 'all'" build-toolchain-only "" "If set, only build the necessary tools to build an external toolchain" + libswift "" "Build the compiler with libswift" cmake-generator "Unix Makefiles" "kind of build system to generate; see output of 'cmake --help' for choices" llvm-num-parallel-lto-link-jobs "" "The number of parallel link jobs to use when compiling llvm" reconfigure "" "force a CMake configuration run even if CMakeCache.txt already exists" @@ -103,6 +104,7 @@ KNOWN_SETTINGS=( darwin-deployment-version-watchos "2.0" "minimum deployment target version for watchOS" darwin-install-extract-symbols "" "whether to extract symbols with dsymutil during installations" darwin-install-extract-symbols-use-just-built-dsymutil "1" "whether we should extract symbols using the just built dsymutil" + darwin-symroot-path-filters "" "space-separated list of path patterns to consider for symbol generation" darwin-overlay-target "" "single overlay target to build, dependencies are computed later" darwin-sdk-deployment-targets "xctest-ios-8.0" "semicolon-separated list of triples like 'fookit-ios-9.0;barkit-watchos-9.0'" darwin-stdlib-install-name-dir "" "the directory of the install_name for standard library dylibs" @@ -137,11 +139,10 @@ KNOWN_SETTINGS=( common-cmake-options "" "CMake options used for all targets, including LLVM/Clang" extra-cmake-options "" "Extra options to pass to CMake for all targets" ninja-cmake-options "" "CMake options used for all ninja targets" - ninja-cmake-options "" "CMake options used for all ninja targets" ## Build ... build-llvm "1" "set to 1 to build LLVM and Clang" - build-sil-debugging-stdlib "0" "set to 1 to build the Swift standard library with -gsil to enable debugging and profiling on SIL level" + build-sil-debugging-stdlib "0" "set to 1 to build the Swift standard library with -sil-based-debuginfo to enable debugging and profiling on SIL level" build-swift-dynamic-sdk-overlay "" "set to 1 to build dynamic variants of the Swift SDK overlay" build-swift-dynamic-stdlib "" "set to 1 to build dynamic variants of the Swift standard library" build-swift-examples "1" "set to 1 to build examples" @@ -151,6 +152,11 @@ KNOWN_SETTINGS=( build-swift-stdlib-unittest-extra "0" "set to 1 to build optional StdlibUnittest components" build-swift-tools "1" "set to 1 to build Swift host tools" + ## Skip cleaning build directories ... + skip-clean-libdispatch "0" "skip cleaning the libdispatch build" + skip-clean-foundation "0" "skip cleaning the foundation build" + skip-clean-xctest "0" "skip cleaning the xctest build" + ## Test Options llvm-include-tests "1" "Set to true to generate testing targets for LLVM. Set to true by default." long-test "0" "set to run the long test suite" @@ -202,13 +208,16 @@ KNOWN_SETTINGS=( swift-stdlib-single-threaded-runtime "0" "whether to build stdlib as a single-threaded runtime only" swift-stdlib-os-versioning "1" "whether to build stdlib with availability based on OS versions (Darwin only)" swift-stdlib-stable-abi "" "should stdlib be built with stable ABI, if not set defaults to true on Darwin, false otherwise" + swift-stdlib-has-darwin-libmalloc "1" "whether the Darwin build of stdlib can use extended libmalloc APIs" swift-disable-dead-stripping "0" "turns off Darwin-specific dead stripping for Swift host tools" + common-swift-flags "" "Flags used for Swift targets other than the stdlib, like the corelibs" ## FREESTANDING Stdlib Options - swift-freestanding-sdk "" "which SDK to use when building the FREESTANDING stdlib" - swift-freestanding-triple-name "" "which triple name (e.g. 'none-macho') to use when building the FREESTANDING stdlib" - swift-freestanding-module-name "" "which .swiftmodule name (e.g. 'freestanding') to use when building the FREESTANDING stdlib" - swift-freestanding-archs "" "space-separated list of which architectures to build when building the FREESTANDING stdlib" + swift-freestanding-flavor "" "when building the FREESTANDING stdlib, which build style to use (options: apple, linux)" + swift-freestanding-sdk "" "which SDK to use when building the FREESTANDING stdlib" + swift-freestanding-triple-name "" "which triple name (e.g. 'none-macho') to use when building the FREESTANDING stdlib" + swift-freestanding-module-name "" "which .swiftmodule name (e.g. 'freestanding') to use when building the FREESTANDING stdlib" + swift-freestanding-archs "" "space-separated list of which architectures to build when building the FREESTANDING stdlib" ## Uncategorised install-prefix "" "installation prefix" @@ -227,9 +236,11 @@ KNOWN_SETTINGS=( cross-compile-hosts "" "space-separated list of targets to cross-compile host Swift tools for" cross-compile-with-host-tools "" "set to use the clang we build for the host to then build the cross-compile hosts" cross-compile-install-prefixes "" "semicolon-separated list of install prefixes to use for the cross-compiled hosts. The list expands, so if there are more cross-compile hosts than prefixes, unmatched hosts use the last prefix in the list" + cross-compile-deps-path "" "path for CMake to look for cross-compiled library dependencies, such as libXML2" skip-merge-lipo-cross-compile-tools "" "set to skip running merge-lipo after installing cross-compiled host Swift tools" coverage-db "" "If set, coverage database to use when prioritizing testing" skip-local-host-install "" "If we are cross-compiling multiple targets, skip an install pass locally if the hosts match" + enable-extract-symbol-dry-run-test "" "If we are dry-running, still run the extract symbol phase so that we can test it" ) components=( @@ -746,6 +757,7 @@ function set_build_options_for_host() { ) lldb_cmake_options+=( + -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="${cmake_osx_deployment_target}" -DCMAKE_OSX_SYSROOT:PATH="${cmake_os_sysroot}" -DCMAKE_OSX_ARCHITECTURES="${architecture}" ) @@ -1015,11 +1027,19 @@ if [ -z "${CMAKE}" ] ; then fi function xcrun_find_tool() { - xcrun --sdk macosx --toolchain "${DARWIN_XCRUN_TOOLCHAIN}" --find "$@" + if [[ "${DRY_RUN}" ]]; then + echo echo "$@" + else + xcrun --sdk macosx --toolchain "${DARWIN_XCRUN_TOOLCHAIN}" --find "$@" + fi } function find_just_built_local_host_llvm_tool() { - find $(build_directory "${LOCAL_HOST}" llvm) -name "$1" -type f -print + if [[ "${DRY_RUN}" ]]; then + echo echo "$1" + else + find $(build_directory "${LOCAL_HOST}" llvm) -name "$1" -type f -print + fi } function not() { @@ -1239,6 +1259,8 @@ function calculate_targets_for_host() { SWIFT_BENCHMARK_TARGETS=($(get_host_specific_variable ${host} SWIFT_BENCHMARK_TARGETS)) SWIFT_RUN_BENCHMARK_TARGETS=($(get_host_specific_variable ${host} SWIFT_RUN_BENCHMARK_TARGETS)) SWIFT_TEST_TARGETS=($(get_host_specific_variable ${host} SWIFT_TEST_TARGETS)) + SWIFT_FLAGS=($(get_host_specific_variable ${host} SWIFT_FLAGS)) + SWIFT_TARGET_CMAKE_OPTIONS=($(get_host_specific_variable ${host} SWIFT_TARGET_CMAKE_OPTIONS)) } @@ -1380,6 +1402,14 @@ function swift_c_flags() { fi } +function common_swift_flags() { + if [ "${module_cache}" == "" ] ; then + echo "error: a module cache path has not been set" + exit 1 + fi + echo -n "${SWIFT_FLAGS[@]} ${COMMON_SWIFT_FLAGS} -module-cache-path \"${module_cache}\" " +} + function cmake_config_opt() { product=$1 if [[ "${CMAKE_GENERATOR}" == "Xcode" ]] ; then @@ -1423,6 +1453,25 @@ function cmake_config_opt() { fi } +function copy_lib_stripping_architecture() { + local source="$1" + local dest="$2" + local arch="$3" + + # An alternative approach would be to use || to first + # attempt the removal of the slice and fall back to the + # copy when failing. + # However, this would leave unneeded error messages in the logs + # that may hinder investigation; in addition, in this scenario + # the `call` function seems to not propagate correctly failure + # exit codes. + if lipo -archs "${source}" | grep -q "${arch}"; then + call lipo -remove "${arch}" "${source}" -output "${dest}" + else + call cp "${source}" "${dest}" + fi +} + function copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain() { local clang_dest_dir="$1" @@ -1448,7 +1497,8 @@ function copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain() { if [[ ! -f "${DEST_LIB_PATH}" ]]; then if [[ -f "${HOST_LIB_PATH}" ]]; then if [[ "$OS" == "tvos" ]]; then - call lipo -remove i386 "${HOST_LIB_PATH}" -output "${DEST_LIB_PATH}" || call cp "${HOST_LIB_PATH}" "${DEST_LIB_PATH}" + # This is to avoid strip failures when generating a toolchain + copy_lib_stripping_architecture "${HOST_LIB_PATH}" "${DEST_LIB_PATH}" i386 else call cp "${HOST_LIB_PATH}" "${DEST_LIB_PATH}" fi @@ -1464,9 +1514,10 @@ function copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain() { if [[ ! -f "${DEST_SIM_LIB_PATH}" ]]; then if [[ -f "${HOST_SIM_LIB_PATH}" ]]; then if [[ "$OS" == "tvos" ]]; then - call lipo -remove i386 "${HOST_SIM_LIB_PATH}" -output "${DEST_SIM_LIB_PATH}" || call cp "${HOST_SIM_LIB_PATH}" "${DEST_SIM_LIB_PATH}" + # This is to avoid strip failures when generating a toolchain + copy_lib_stripping_architecture "${HOST_SIM_LIB_PATH}" "${DEST_SIM_LIB_PATH}" i386 else - call cp "${HOST_SIM_LIB_PATH}" "${DEST_SIM_LIB_PATH}" + call cp "${HOST_SIM_LIB_PATH}" "${DEST_SIM_LIB_PATH}" fi elif [[ -f "${HOST_LIB_PATH}" ]]; then # The simulator .a might not exist if the host @@ -1664,6 +1715,9 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_BUILD_TYPE:STRING="${CMARK_BUILD_TYPE}" "${cmark_cmake_options[@]}" ) + if [[ $(is_cross_tools_host ${host}) ]] ; then + cmake_options+=("${SWIFT_TARGET_CMAKE_OPTIONS[@]}") + fi build_targets=(all) ;; @@ -1791,6 +1845,7 @@ for host in "${ALL_HOSTS[@]}"; do -DCLANG_TABLEGEN=$(build_directory "${LOCAL_HOST}" llvm)/bin/clang-tblgen -DLLVM_NATIVE_BUILD=$(build_directory "${LOCAL_HOST}" llvm) ) + cmake_options+=("${SWIFT_TARGET_CMAKE_OPTIONS[@]}") fi ;; @@ -1904,6 +1959,7 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O2 -DNDEBUG" -DCMAKE_BUILD_TYPE:STRING="${SWIFT_BUILD_TYPE}" -DLLVM_ENABLE_ASSERTIONS:BOOL=$(true_false "${SWIFT_ENABLE_ASSERTIONS}") + -DSWIFT_TOOLS_ENABLE_LIBSWIFT:STRING=$(true_false "${LIBSWIFT}") -DSWIFT_ANALYZE_CODE_COVERAGE:STRING=$(toupper "${SWIFT_ANALYZE_CODE_COVERAGE}") -DSWIFT_STDLIB_BUILD_TYPE:STRING="${SWIFT_STDLIB_BUILD_TYPE}" -DSWIFT_STDLIB_ASSERTIONS:BOOL=$(true_false "${SWIFT_STDLIB_ENABLE_ASSERTIONS}") @@ -1912,6 +1968,7 @@ for host in "${ALL_HOSTS[@]}"; do -DSWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS:BOOL=$(true_false "${SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS}") -DSWIFT_RUNTIME_MACHO_NO_DYLD:BOOL=$(true_false "${SWIFT_RUNTIME_MACHO_NO_DYLD}") -DSWIFT_STDLIB_OS_VERSIONING:BOOL=$(true_false "${SWIFT_STDLIB_OS_VERSIONING}") + -DSWIFT_STDLIB_HAS_DARWIN_LIBMALLOC:BOOL=$(true_false "${SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC}") -DSWIFT_NATIVE_LLVM_TOOLS_PATH:STRING="${native_llvm_tools_path}" -DSWIFT_NATIVE_CLANG_TOOLS_PATH:STRING="${native_clang_tools_path}" -DSWIFT_NATIVE_SWIFT_TOOLS_PATH:STRING="${native_swift_tools_path}" @@ -2024,6 +2081,13 @@ for host in "${ALL_HOSTS[@]}"; do ) fi + if [ "${SWIFT_FREESTANDING_FLAVOR}" ] ; then + cmake_options=( + "${cmake_options[@]}" + -DSWIFT_FREESTANDING_FLAVOR:STRING="${SWIFT_FREESTANDING_FLAVOR}" + ) + fi + if [ "${SWIFT_FREESTANDING_SDK}" ] ; then cmake_options=( "${cmake_options[@]}" @@ -2202,7 +2266,7 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_CXX_COMPILER:PATH="${CLANG_BIN}/clang++" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" -DCMAKE_Swift_COMPILER:PATH=${SWIFTC_BIN} - -DCMAKE_Swift_FLAGS:STRING="-module-cache-path \"${module_cache}\"" + -DCMAKE_Swift_FLAGS:STRING="$(common_swift_flags)" -DLLBUILD_ENABLE_ASSERTIONS:BOOL=$(true_false "${LLBUILD_ENABLE_ASSERTIONS}") -DLLBUILD_SUPPORT_BINDINGS:=Swift @@ -2218,6 +2282,22 @@ for host in "${ALL_HOSTS[@]}"; do -DFoundation_DIR:PATH=$(build_directory ${host} foundation)/cmake/modules ) + if [[ $(is_cross_tools_host ${host}) ]] ; then + cmake_options+=("${SWIFT_TARGET_CMAKE_OPTIONS[@]}") + + # llbuild looks for the SQlite3 library using find_package(), + # so search for it in CROSS_COMPILE_DEPS_PATH using the CMake + # process for doing so and don't use cross-compiled + # executables, see the linked CMake docs for more info: + # + # https://cmake.org/cmake/help/latest/command/find_package.html + # https://cmake.org/cmake/help/latest/command/find_program.html + cmake_options+=( + -DCMAKE_FIND_ROOT_PATH:PATH="${CROSS_COMPILE_DEPS_PATH}" + -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER + ) + fi + # Ensure on Darwin platforms that we consider only the SQLite headers # from the SDK instead of picking ones found elsewhere # (e.g. in /usr/include ) @@ -2245,6 +2325,14 @@ for host in "${ALL_HOSTS[@]}"; do FOUNDATION_BUILD_DIR=$(build_directory ${host} foundation) SWIFT_BUILD_DIR=$(build_directory ${host} swift) + if [[ "${SKIP_CLEAN_XCTEST}" == "0" ]] + then + # The Swift project might have been changed, but CMake might + # not be aware and will not rebuild. + echo "Cleaning the XCTest build directory" + call rm -rf "${XCTEST_BUILD_DIR}" + fi + case "${host}" in macosx-*) # Staging: require opt-in for building with dispatch @@ -2272,19 +2360,13 @@ for host in "${ALL_HOSTS[@]}"; do continue ;; *) - # FIXME: Always re-build XCTest on non-darwin platforms. - # The Swift project might have been changed, but CMake might - # not be aware and will not rebuild. - echo "Cleaning the XCTest build directory" - call rm -rf "${XCTEST_BUILD_DIR}" - cmake_options=( ${cmake_options[@]} -DCMAKE_BUILD_TYPE:STRING="${XCTEST_BUILD_TYPE}" -DCMAKE_C_COMPILER:PATH="${CLANG_BIN}/clang" -DCMAKE_CXX_COMPILER:PATH="${CLANG_BIN}/clang++" -DCMAKE_Swift_COMPILER:PATH=${SWIFTC_BIN} - -DCMAKE_Swift_FLAGS:STRING="-module-cache-path \"${module_cache}\"" + -DCMAKE_Swift_FLAGS:STRING="$(common_swift_flags)" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" -DCMAKE_INSTALL_LIBDIR:PATH="lib" @@ -2300,6 +2382,9 @@ for host in "${ALL_HOSTS[@]}"; do -DENABLE_TESTING=YES ) + if [[ $(is_cross_tools_host ${host}) ]] ; then + cmake_options+=("${SWIFT_TARGET_CMAKE_OPTIONS[@]}") + fi ;; esac @@ -2334,11 +2419,13 @@ for host in "${ALL_HOSTS[@]}"; do LIBICU_BUILD_ARGS=() fi - # FIXME: Always re-build XCTest on non-darwin platforms. - # The Swift project might have been changed, but CMake might - # not be aware and will not rebuild. - echo "Cleaning the Foundation build directory" - call rm -rf "${build_dir}" + if [[ "${SKIP_CLEAN_FOUNDATION}" == "0" ]] + then + # The Swift project might have been changed, but CMake might + # not be aware and will not rebuild. + echo "Cleaning the Foundation build directory" + call rm -rf "${build_dir}" + fi # Set the PKG_CONFIG_PATH so that core-foundation can find the libraries and # header files @@ -2353,7 +2440,7 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_CXX_COMPILER:PATH=${CLANG_BIN}/clang++ -DCMAKE_SWIFT_COMPILER:PATH=${SWIFTC_BIN} -DCMAKE_Swift_COMPILER:PATH=${SWIFTC_BIN} - -DCMAKE_Swift_FLAGS:STRING="-module-cache-path \"${module_cache}\"" + -DCMAKE_Swift_FLAGS:STRING="$(common_swift_flags)" -DCMAKE_INSTALL_PREFIX:PATH=$(get_host_install_prefix ${host}) ${LIBICU_BUILD_ARGS[@]} @@ -2369,6 +2456,24 @@ for host in "${ALL_HOSTS[@]}"; do -DBUILD_SHARED_LIBS=$([[ ${product} == foundation_static ]] && echo "NO" || echo "YES") ) + if [[ $(is_cross_tools_host ${host}) ]] ; then + cmake_options+=("${SWIFT_TARGET_CMAKE_OPTIONS[@]}") + + # Foundation looks for the ICU, libXML2 and libcurl libraries + # using find_package(), so search for them in + # CROSS_COMPILE_DEPS_PATH using the CMake process for doing + # so, see the linked CMake docs for more info: + # + # https://cmake.org/cmake/help/latest/command/find_package.html + cmake_options+=( + -DCMAKE_FIND_ROOT_PATH:PATH="${CROSS_COMPILE_DEPS_PATH}" + ) + fi + if [[ "${host}" == "android-"* ]]; then + cmake_options+=( + -DCMAKE_HAVE_LIBC_PTHREAD=True + ) + fi ;; libdispatch|libdispatch_static) LIBDISPATCH_BUILD_DIR=$(build_directory ${host} ${product}) @@ -2381,11 +2486,13 @@ for host in "${ALL_HOSTS[@]}"; do exit 1 ;; *) - # FIXME: Always re-build XCTest on non-darwin platforms. - # The Swift project might have been changed, but CMake might - # not be aware and will not rebuild. - echo "Cleaning the libdispatch build directory" - call rm -rf "${LIBDISPATCH_BUILD_DIR}" + if [[ "${SKIP_CLEAN_LIBDISPATCH}" == "0" ]] + then + # The Swift project might have been changed, but CMake might + # not be aware and will not rebuild. + echo "Cleaning the libdispatch build directory" + call rm -rf "${LIBDISPATCH_BUILD_DIR}" + fi cmake_options=( -DENABLE_SWIFT=YES @@ -2395,7 +2502,7 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_CXX_COMPILER:PATH="${CLANG_BIN}/clang++" -DCMAKE_SWIFT_COMPILER:PATH="${SWIFTC_BIN}" -DCMAKE_Swift_COMPILER:PATH="${SWIFTC_BIN}" - -DCMAKE_Swift_FLAGS:STRING="-module-cache-path \"${module_cache}\"" + -DCMAKE_Swift_FLAGS:STRING="$(common_swift_flags)" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" -DCMAKE_INSTALL_LIBDIR:PATH="lib" @@ -2404,6 +2511,9 @@ for host in "${ALL_HOSTS[@]}"; do -DENABLE_TESTING=YES -DBUILD_SHARED_LIBS=$([[ ${product} == libdispatch_static ]] && echo "NO" || echo "YES") ) + if [[ $(is_cross_tools_host ${host}) ]] ; then + cmake_options+=("${SWIFT_TARGET_CMAKE_OPTIONS[@]}") + fi ;; esac @@ -3104,6 +3214,26 @@ function printJSONEndTimestamp() { printJSONTimestamp ${command} "end" } +function grep_that_allows_no_matches() { + # This will not cause the script to fail + # if no line in the input matches the pattern + grep "$@" || test $? = 1 +} + +function filter_paths() { + if [[ -n "$1" ]]; then + local -a filters + filters=($1) + local -a grep_arguments + for cfilter in "${filters[@]}"; do + grep_arguments+=('-e' "$cfilter") + done + grep_that_allows_no_matches "${grep_arguments[@]}" + else + cat + fi +} + for host in "${ALL_HOSTS[@]}"; do # Check if we should perform this action. if ! [[ $(should_execute_action "${host}-extractsymbols") ]]; then @@ -3131,24 +3261,21 @@ for host in "${ALL_HOSTS[@]}"; do # descibes host_symroot="${INSTALL_SYMROOT}/${host}" - # FIXME: Since it's hard to trace output pipe call, - # For now, We don't support dry-run trace for this block - # Instead, just echo we do "darwin_intall_extract_symbols". - if [[ "${DRY_RUN}" ]]; then - call darwin_install_extract_symbols - printJSONStartTimestamp dsymutil - echo xargs -n 1 -P ${DSYMUTIL_JOBS} dsymutil - printJSONEndTimestamp dsymutil - else - set -x + set -x - CURRENT_INSTALL_DIR=${host_install_destdir} - CURRENT_PREFIX="${TOOLCHAIN_PREFIX}" + CURRENT_INSTALL_DIR=${host_install_destdir} + CURRENT_PREFIX="${TOOLCHAIN_PREFIX}" - # Copy executables and shared libraries from the `host_install_destdir` to - # INSTALL_SYMROOT and run dsymutil on them. - (cd "${CURRENT_INSTALL_DIR}" && - find ./"${CURRENT_PREFIX}" -perm -0111 -type f -print | cpio --insecure -pdm "${host_symroot}") + # Copy executables and shared libraries from the `host_install_destdir` to + # INSTALL_SYMROOT and run dsymutil on them. + if [[ -n "${DRY_RUN}" ]] && [[ -z "${ENABLE_EXTRACT_SYMBOL_DRY_RUN_TEST}" ]]; then + echo "DRY_RUN! Coping executables and shared libraries from the `host_install_destdir` to + INSTALL_SYMROOT and runing dsymutil on them." + else + (cd "${CURRENT_INSTALL_DIR}" && + find ./"${CURRENT_PREFIX}" -perm -0111 -type f -print | \ + filter_paths "${DARWIN_SYMROOT_PATH_FILTERS}" | \ + cpio --insecure -pdm -v "${host_symroot}") dsymutil_path= if [[ -n "${DARWIN_INSTALL_EXTRACT_SYMBOLS_USE_JUST_BUILT_DSYMUTIL}" ]]; then @@ -3160,15 +3287,11 @@ for host in "${ALL_HOSTS[@]}"; do # Run dsymutil on executables and shared libraries. # # Exclude shell scripts and static archives. - # Exclude swift-api-digester dSYM to reduce debug toolchain size. # Tweak carefully the amount of parallelism -- dsymutil can be memory intensive and # as such too many instance can exhaust the memory and slow down/panic the machine printJSONStartTimestamp dsymutil (cd "${host_symroot}" && - find ./"${CURRENT_PREFIX}" -perm -0111 -type f -print | \ - grep -v '.py$' | \ - grep -v '.a$' | \ - grep -v 'swift-api-digester' | \ + find ./"${CURRENT_PREFIX}" -perm -0111 -type f -not -name "*.a" -not -name "*.py" -print | \ xargs -n 1 -P ${DSYMUTIL_JOBS} ${dsymutil_path}) printJSONEndTimestamp dsymutil @@ -3183,9 +3306,9 @@ for host in "${ALL_HOSTS[@]}"; do find "${CURRENT_INSTALL_DIR}${CURRENT_PREFIX}/" \ '(' -name "*.dylib" ')' -type f -print | \ xargs -n 1 -P ${BUILD_JOBS} $(xcrun_find_tool codesign) -f -s - - - { set +x; } 2>/dev/null fi + + { set +x; } 2>/dev/null fi done # Everything is 'installed', but some products may be awaiting lipo. @@ -3333,7 +3456,7 @@ if [[ ${#LIPO_SRC_DIRS[@]} -gt 0 ]]; then else LIPO_PATH="${HOST_LIPO}" fi - call "${SWIFT_SOURCE_DIR}"/utils/recursive-lipo --lipo=${LIPO_PATH} --copy-subdirs="$(get_host_install_prefix ${host})lib/swift $(get_host_install_prefix ${host})lib/swift_static" --explicit-src-files="$(get_host_install_prefix ${host})lib/swift/${host%%-*}/lib_InternalSwiftScan.dylib" --destination="$(get_host_install_destdir ${mergedHost})" ${LIPO_SRC_DIRS[@]} + call "${SWIFT_SOURCE_DIR}"/utils/recursive-lipo --lipo=${LIPO_PATH} --copy-subdirs="$(get_host_install_prefix ${host})lib/swift $(get_host_install_prefix ${host})lib/swift_static" --explicit-src-files="$(get_host_install_prefix ${host})lib/swift/${host%%-*}/lib_InternalSwiftScan.dylib $(get_host_install_prefix ${host})lib/swift/${host%%-*}/lib_InternalSwiftSyntaxParser.dylib" --destination="$(get_host_install_destdir ${mergedHost})" ${LIPO_SRC_DIRS[@]} if [[ $(should_execute_action "${mergedHost}-lipo") ]]; then # Build and test the lipo-ed package. diff --git a/utils/build-toolchain b/utils/build-toolchain index 5af6f66907c1a..135b7d1afe5f4 100755 --- a/utils/build-toolchain +++ b/utils/build-toolchain @@ -56,10 +56,12 @@ case $(uname -s) in Darwin) SWIFT_PACKAGE=buildbot_osx_package,no_test OS_SUFFIX=osx + BUILD_SUBDIR=buildbot_osx ;; Linux) SWIFT_PACKAGE=buildbot_linux,no_test OS_SUFFIX=linux + BUILD_SUBDIR=buildbot_linux ;; *) echo "Unrecognised platform $(uname -s)" @@ -77,8 +79,10 @@ while [ $# -ne 0 ]; do -t|--test) if [ "$(uname -s)" == "Linux" ]; then SWIFT_PACKAGE=buildbot_linux + BUILD_SUBDIR=buildbot_linux else SWIFT_PACKAGE=buildbot_osx_package + BUILD_SUBDIR=buildbot_osx fi ;; --distcc) @@ -137,7 +141,7 @@ DISPLAY_NAME_SHORT="Local Swift Development Snapshot" DISPLAY_NAME="${DISPLAY_NAME_SHORT} ${YEAR}-${MONTH}-${DAY}" SWIFT_INSTALLABLE_PACKAGE="${RESULT_DIR}/${ARCHIVE}" -SWIFT_INSTALL_DIR="${RESULT_DIR}/swift-nightly-install" +SWIFT_INSTALL_DIR="${RESULT_DIR}/${BUILD_SUBDIR}" SWIFT_INSTALL_SYMROOT="${RESULT_DIR}/swift-nightly-symroot" SWIFT_TOOLCHAIN_DIR="/Library/Developer/Toolchains/${TOOLCHAIN_NAME}.xctoolchain" SYMBOLS_PACKAGE="${RESULT_DIR}/${SYM_ARCHIVE}" diff --git a/utils/build-windows-toolchain.bat b/utils/build-windows-toolchain.bat new file mode 100644 index 0000000000000..0a7fe149f8693 --- /dev/null +++ b/utils/build-windows-toolchain.bat @@ -0,0 +1,813 @@ +:: build-windows-toolchain.bat +:: +:: This source file is part of the Swift.org open source project +:: +:: Copyright (c) 2014 - 2021 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 + +setlocal enableextensions enabledelayedexpansion +path %PATH%;%PYTHON_HOME% + +:: Identify the SourceRoot +:: Normalize the SourceRoot to make it easier to read the output. +cd %~dp0\..\.. +set SourceRoot=%CD% + +:: Identify the BuildRoot +set BuildRoot=%SourceRoot%\build + +md %BuildRoot% +subst T: /d +subst T: %BuildRoot% || (exit /b) +set BuildRoot=T: + +:: Identify the PackageRoot +set PackageRoot=%BuildRoot%\package + +md %PackageRoot% + +:: Identify the InstallRoot +set InstallRoot=%BuildRoot%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr +set PlatformRoot=%BuildRoot%\Library\Developer\Platforms\Windows.platform +set SDKInstallRoot=%PlatformRoot%\Developer\SDKs\Windows.sdk + +:: Setup temporary directories +md %BuildRoot%\tmp +set TEMP=%BuildRoot%\tmp +set TMP=%BuildRoot%\tmp +set TMPDIR=%BuildRoot%\tmp + +call :CloneDependencies || (exit /b) +call :CloneRepositories || (exit /b) + +md "%BuildRoot%\Library" + +:: TODO(compnerd) build ICU from source +curl.exe -sOL "https://github.com/unicode-org/icu/releases/download/release-67-1/icu4c-67_1-Win64-MSVC2017.zip" || (exit /b) +"%SystemDrive%\Program Files\Git\usr\bin\unzip.exe" -o icu4c-67_1-Win64-MSVC2017.zip -d %BuildRoot%\Library\icu-67.1 +md %BuildRoot%\Library\icu-67.1\usr\bin +copy %BuildRoot%\Library\icu-67.1\bin64\icudt67.dll %BuildRoot%\Library\icu-67.1\usr\bin || (exit /b) +copy %BuildRoot%\Library\icu-67.1\bin64\icuin67.dll %BuildRoot%\Library\icu-67.1\usr\bin || (exit /b) +copy %BuildRoot%\Library\icu-67.1\bin64\icuuc67.dll %BuildRoot%\Library\icu-67.1\usr\bin || (exit /b) + +:: FIXME(compnerd) is there a way to build the sources without downloading the amalgamation? +curl.exe -sOL "https://sqlite.org/2021/sqlite-amalgamation-3360000.zip" || (exit /b) +"%SystemDrive%\Program Files\Git\usr\bin\unzip.exe" -o sqlite-amalgamation-3360000.zip -d %SourceRoot% + +:: TODO(compnerd) use CMakeLists.txt from compnerd/swift-build +md %BuildRoot%\sqlite +cl /nologo /DWIN32 /D_WINDOWS /W3 /MD /O2 /Ob2 /DNDEBUG /Fo%BuildRoot%\sqlite\sqlite3.c.obj /Fd%BuildRoot%\sqlite\SQLite3.pdb /FS -c %SourceRoot%\sqlite-amalgamation-3360000\sqlite3.c +lib /nologo /machine:x64 /out:%BuildRoot%\sqlite\SQLite3.lib %BuildRoot%\sqlite\sqlite3.c.obj +md %BuildRoot%\Library\sqlite-3.36.0\usr\lib +md %BuildRoot%\Library\sqlite-3.36.0\usr\include +copy %BuildRoot%\sqlite\SQLite3.lib %BuildRoot%\Library\sqlite-3.36.0\usr\lib +copy %SourceRoot%\sqlite-amalgamation-3360000\sqlite3.h %BuildRoot%\Library\sqlite-3.36.0\usr\include +copy %SourceRoot%\sqlite-amalgamation-3360000\sqlite3ext.h %BuildRoot%\Library\sqlite-3.36.0\usr\include + +:: build zlib +cmake ^ + -B %BuildRoot%\zlib ^ + + -D BUILD_SHARED_LIBS=NO ^ + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=cl ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%BuildRoot%\Library\zlib-1.2.11\usr ^ + + -D SKIP_INSTALL_FILES=YES ^ + + -G Ninja ^ + -S %SourceRoot%\zlib || (exit /b) +cmake --build "%BUildRoot%\zlib" || (exit /b) +cmake --build "%BUildRoot%\zlib" --target install || (exit /b) + +:: build libxml2 +cmake ^ + -B %BuildRoot%\libxml2 ^ + + -D BUILD_SHARED_LIBS=OFF ^ + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=cl ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%BuildRoot%\Library\libxml2-2.9.12\usr ^ + + -D LIBXML2_WITH_ICONV=NO ^ + -D LIBXML2_WITH_ICU=NO ^ + -D LIBXML2_WITH_LZMA=NO ^ + -D LIBXML2_WITH_PYTHON=NO ^ + -D LIBXML2_WITH_TESTS=NO ^ + -D LIBXML2_WITH_THREADS=YES ^ + -D LIBXML2_WITH_ZLIB=NO ^ + + -G Ninja ^ + -S %SourceRoot%\libxml2 || (exit /b) +cmake --build "%BUildRoot%\libxml2" || (exit /b) +cmake --build "%BUildRoot%\libxml2" --target install || (exit /b) + +:: build curl +cmake ^ + -B %BuildRoot%\curl ^ + + -D BUILD_SHARED_LIBS=NO ^ + -D BUILD_TESTING=NO ^ + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=cl ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%BuildRoot%\Library\curl-7.77.0\usr ^ + + -D BUILD_CURL_EXE=NO ^ + -D CMAKE_USE_OPENSSL=NO ^ + -D CURL_CA_PATH=none ^ + -D CMAKE_USE_SCHANNEL=YES ^ + -D CMAKE_USE_LIBSSH2=NO ^ + -D HAVE_POLL_FINE=NO ^ + -D CURL_DISABLE_LDAP=YES ^ + -D CURL_DISABLE_LDAPS=YES ^ + -D CURL_DISABLE_TELNET=YES ^ + -D CURL_DISABLE_DICT=YES ^ + -D CURL_DISABLE_FILE=YES ^ + -D CURL_DISABLE_TFTP=YES ^ + -D CURL_DISABLE_RTSP=YES ^ + -D CURL_DISABLE_PROXY=YES ^ + -D CURL_DISABLE_POP3=YES ^ + -D CURL_DISABLE_IMAP=YES ^ + -D CURL_DISABLE_SMTP=YES ^ + -D CURL_DISABLE_GOPHER=YES ^ + -D CURL_ZLIB=YES ^ + -D ENABLE_UNIX_SOCKETS=NO ^ + -D ENABLE_THREADED_RESOLVER=NO ^ + + -D ZLIB_ROOT=%BuildRoot%\Library\zlib-1.2.11\usr ^ + -D ZLIB_LIBRARY=%BuildRoot%\Library\zlib-1.2.11\usr\lib\zlibstatic.lib ^ + + -G Ninja ^ + -S %SourceRoot%\curl || (exit /b) +cmake --build "%BuildRoot%\curl" || (exit /b) +cmake --build "%BuildRoot%\curl" --target install || (exit /b) + +:: Prepare system modules +copy /y "%SourceRoot%\swift\stdlib\public\Platform\ucrt.modulemap" "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" || (exit /b) +copy /y "%SourceRoot%\swift\stdlib\public\Platform\winsdk.modulemap" "%UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap" || (exit /b) +copy /y "%SourceRoot%\swift\stdlib\public\Platform\visualc.modulemap" "%VCToolsInstallDir%\include\module.modulemap" || (exit /b) +copy /y "%SourceRoot%\swift\stdlib\public\Platform\visualc.apinotes" "%VCToolsInstallDir%\include\visualc.apinotes" || (exit /b) + +:: Build Toolchain +cmake ^ + -B "%BuildRoot%\1" ^ + + -C %SourceRoot%\swift\cmake\caches\Windows-x86_64.cmake ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=cl ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=cl ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX="%InstallRoot%" ^ + + -D LLVM_DEFAULT_TARGET_TRIPLE=x86_64-unknown-windows-msvc ^ + + -D PACKAGE_VENDOR="swift.org" ^ + -D CLANG_VENDOR="swift.org" ^ + -D CLANG_VENDOR_UTI="org.swift" ^ + -D LLVM_APPEND_VC_REV=NO ^ + -D LLVM_VERSION_SUFFIX="" ^ + + -D SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY=YES ^ + -D SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED=YES ^ + -D SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING=YES ^ + + -D LLVM_EXTERNAL_SWIFT_SOURCE_DIR="%SourceRoot%\swift" ^ + -D LLVM_EXTERNAL_CMARK_SOURCE_DIR="%SourceRoot%\cmark" ^ + -D PYTHON_HOME=%PYTHON_HOME% ^ + -D PYTHON_EXECUTABLE=%PYTHON_HOME%\python.exe ^ + -D SWIFT_PATH_TO_LIBDISPATCH_SOURCE="%SourceRoot%\swift-corelibs-libdispatch" ^ + -D SWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE="%BuildRoot%\Library\icu-67.1\include\unicode" ^ + -D SWIFT_WINDOWS_x86_64_ICU_UC="%BuildRoot%\Library\icu-67.1\lib64\icuuc.lib" ^ + -D SWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE="%BuildRoot%\Library\icu-67.1\include" ^ + -D SWIFT_WINDOWS_x86_64_ICU_I18N="%BuildRoot%\Library\icu-67.1\lib64\icuin.lib" ^ + + -G Ninja ^ + -S llvm-project\llvm || (exit /b) +cmake --build "%BuildRoot%\1" || (exit /b) +cmake --build "%BuildRoot%\1" --target install || (exit /b) + +:: Build Swift Standard Library +cmake ^ + -B %BuildRoot%\2 ^ + + -C %SourceRoot%\swift\cmake\caches\Runtime-Windows-x86_64.cmake ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%SDKInstallRoot%\usr ^ + + -D LLVM_DIR=%BuildRoot%\1\lib\cmake\llvm ^ + -D SWIFT_NATIVE_SWIFT_TOOLS_PATH=%BuildRoot%\1\bin ^ + -D SWIFT_PATH_TO_LIBDISPATCH_SOURCE=%SourceRoot%\swift-corelibs-libdispatch ^ + -D SWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE="%BuildRoot%\Library\icu-67.1\include\unicode" ^ + -D SWIFT_WINDOWS_x86_64_ICU_UC="%BuildRoot%\Library\icu-67.1\lib64\icuuc.lib" ^ + -D SWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE="%BuildRoot%\Library\icu-67.1\include" ^ + -D SWIFT_WINDOWS_x86_64_ICU_I18N="%BuildRoot%\Library\icu-67.1\lib64\icuin.lib" ^ + + -D SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY=YES ^ + -D SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED=YES ^ + -D SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING=YES ^ + + -G Ninja ^ + -S %SourceRoot%\swift || (exit /b) +cmake --build %BuildRoot%\2 || (exit /b) +cmake --build %BuildRoot%\2 --target install || (exit /b) + +:: Build libdispatch +cmake ^ + -B %BuildRoot%\3 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%SDKInstallRoot%\usr ^ + + -D ENABLE_SWIFT=YES ^ + + -G Ninja ^ + -S %SourceRoot%\swift-corelibs-libdispatch || (exit /b) +cmake --build %BuildRoot%\3 || (exit /b) +cmake --build %BuildRoot%\3 --target install || (exit /b) + +:: Build Foundation +cmake ^ + -B %BuildRoot%\4 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%SDKInstallRoot%\usr ^ + + -D CURL_DIR=%BuildRoot%\Library\curl-7.77.0\usr\lib\cmake\CURL ^ + -D ICU_ROOT=%BuildRoot%\Library\icu-67.1 ^ + -D ICU_UC_LIBRARY=%BuildRoot%\Library\icu-67.1\lib64\icuuc67.lib ^ + -D ICU_I18N_LIBRARY=%BuildRoot%\Library\icu-67.1\lib64\icuin67.lib ^ + -D LIBXML2_LIBRARY=%BuildRoot%\Library\libxml2-2.9.12\usr\lib\libxml2s.lib ^ + -D LIBXML2_INCLUDE_DIR=%BuildRoot%\Library\libxml2-2.9.12\usr\include\libxml2 ^ + -D LIBXML2_DEFINITIONS="/DLIBXML_STATIC" ^ + -D ZLIB_LIBRARY=%BuildRoot%\Library\zlib-1.2.11\usr\lib\zlibstatic.lib ^ + -D ZLIB_INCLUDE_DIR=%BuildRoot%\Library\zlib-1.2.11\usr\include ^ + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + + -D ENABLE_TESTING=NO ^ + + -G Ninja ^ + -S %SourceRoot%\swift-corelibs-foundation || (exit /b) +cmake --build %BuildRoot%\4 || (exit /b) +cmake --build %BuildRoot%\4 --target install || (exit /b) + +:: Build XCTest +cmake ^ + -B %BuildRoot%\5 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%PlatformRoot%\Developer\Library\XCTest-development\usr ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + + -D ENABLE_TESTING=NO ^ + + -G Ninja ^ + -S %SourceRoot%\swift-corelibs-xctest || (exit /b) +cmake --build %BuildRoot%\5 || (exit /b) +cmake --build %BuildRoot%\5 --target install || (exit /b) + +:: Build swift-tools-support-core +cmake ^ + -B %BuildRoot%\6 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + -D SQLite3_INCLUDE_DIR=%BuildRoot%\Library\sqlite-3.36.0\usr\include ^ + -D SQLite3_LIBRARY=%BuildRoot%\Library\sqlite-3.36.0\usr\lib\SQLite3.lib ^ + + -G Ninja ^ + -S %SourceRoot%\swift-tools-support-core || (exit /b) +cmake --build %BuildRoot%\6 || (exit /b) +cmake --build %BuildRoot%\6 --target install || (exit /b) + +:: Build llbuild +cmake ^ + -B %BuildRoot%\7 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy -Xclang -fno-split-cold-code" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D LLBUILD_SUPPORT_BINDINGS=Swift ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + -D SQLite3_INCLUDE_DIR=%BuildRoot%\Library\sqlite-3.36.0\usr\include ^ + -D SQLite3_LIBRARY=%BuildRoot%\Library\sqlite-3.36.0\usr\lib\SQLite3.lib ^ + + -G Ninja ^ + -S %SourceRoot%\llbuild || (exit /b) +cmake --build %BuildRoot%\7 || (exit /b) +cmake --build %BuildRoot%\7 --target install || (exit /b) + +:: Build swift-argument-parser +cmake ^ + -B %BuildRoot%\8 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + -D XCTest_DIR=%BuildRoot%\5\cmake\modules ^ + + -G Ninja ^ + -S %SourceRoot%\swift-argument-parser || (exit /b) +cmake --build %BuildRoot%\8 || (exit /b) +cmake --build %BuildRoot%\8 --target install || (exit /b) + +:: Build Yams +cmake ^ + -B %BuildRoot%\9 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy /DYAML_DECLARE_EXPORT /DWIN32" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_Swift_FLAGS="-Xcc -DYAML_DECLARE_EXPORT -Xcc -DWIN32" ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + -D XCTest_DIR=%BuildRoot%\5\cmake\modules ^ + + -G Ninja ^ + -S %SourceRoot%\Yams || (exit /b) +cmake --build %BuildRoot%\9 || (exit /b) +cmake --build %BuildRoot%\9 --target install || (exit /b) + +:: Build swift-driver +cmake ^ + -B %BuildRoot%\10 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + -D XCTest_DIR=%BuildRoot%\5\cmake\modules ^ + -D TSC_DIR=%BuildRoot%\6\cmake\modules ^ + -D LLBuild_DIR=%BuildRoot%\7\cmake\modules ^ + -D ArgumentParser_DIR=%BuildRoot%\8\cmake\modules ^ + -D Yams_DIR=%BuildRoot%\9\cmake\modules ^ + + -G Ninja ^ + -S %SourceRoot%\swift-driver || (exit /b) +cmake --build %BuildRoot%\10 || (exit /b) +cmake --build %BuildRoot%\10 --target install || (exit /b) + +:: Build swift-crypto +cmake ^ + -B %BuildRoot%\11 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=cl ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=cl ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + + -G Ninja ^ + -S %SourceRoot%\swift-crypto || (exit /b) +cmake --build %BuildRoot%\11 || (exit /b) +cmake --build %BuildRoot%\11 --target install || (exit /b) + +:: Build swift-collections +cmake ^ + -B %BuildRoot%\12 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -G Ninja ^ + -S %SourceRoot%\swift-collections || (exit /b) +cmake --build %BuildRoot%\12 || (exit /b) +cmake --build %BuildRoot%\12 --target install || (exit /b) + +:: Build swift-package-manager +cmake ^ + -B %BuildRoot%\13 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + -D TSC_DIR=%BuildRoot%\6\cmake\modules ^ + -D LLBuild_DIR=%BuildRoot%\7\cmake\modules ^ + -D ArgumentParser_DIR=%BuildRoot%\8\cmake\modules ^ + -D Yams_DIR=%BuildRoot%\9\cmake\modules ^ + -D SwiftDriver_DIR=%BuildRoot%\10\cmake\modules ^ + -D SwiftCrypto_DIR=%BuildRoot%\11\cmake\modules ^ + -D SwiftCollections_DIR=%BuildRoot%\12\cmake\modules ^ + + -G Ninja ^ + -S %SourceRoot%\swiftpm || (exit /b) +cmake --build %BuildRoot%\13 || (exit /b) +cmake --build %BuildRoot%\13 --target install || (exit /b) + +:: Build IndexStoreDB +cmake ^ + -B %BuildRoot%\14 ^ + + -D BUILD_SHARED_LIBS=YES ^ + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy -Xclang -fno-split-cold-code" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + + -G Ninja ^ + -S %SourceRoot%\indexstore-db || (exit /b) +cmake --build %BuildRoot%\14 || (exit /b) +cmake --build %BuildRoot%\14 --target install || (exit /b) + +:: Build SourceKit-LSP +cmake ^ + -B %BuildRoot%\15 ^ + + -D BUILD_SHARED_LIBS=YES ^ + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy -Xclang -fno-split-cold-code" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%InstallRoot% ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + -D TSC_DIR=%BuildRoot%\6\cmake\modules ^ + -D LLBuild_DIR=%BuildRoot%\7\cmake\modules ^ + -D ArgumentParser_DIR=%BuildRoot%\8\cmake\modules ^ + -D Yams_DIR=%BuildRoot%\9\cmake\modules ^ + -D SwiftPM_DIR=%BuildRoot%\13\cmake\modules ^ + -D IndexStoreDB_DIR=%BuildRoot%\14\cmake\modules ^ + -D SwiftCollections_DIR=%BuildRoot%\12\cmake\modules ^ + + -G Ninja ^ + -S %SourceRoot%\sourcekit-lsp || (exit /b) +cmake --build %BuildRoot%\15 || (exit /b) +cmake --build %BuildRoot%\15 --target install || (exit /b) + +:: Create Configuration Files +python -c "import plistlib; print(str(plistlib.dumps({ 'DefaultProperties': { 'DEFAULT_USE_RUNTIME': 'MD' } }), encoding='utf-8'))" > %SDKInstallRoot%\SDKSettings.plist +:: TODO(compnerd) match the XCTest installation name +python -c "import plistlib; print(str(plistlib.dumps({ 'DefaultProperties': { 'XCTEST_VERSION': 'development' } }), encoding='utf-8'))" > %PlatformRoot%\Info.plist + +:: Package toolchain.msi +msbuild %SourceRoot%\swift-installer-scripts\platforms\Windows\toolchain.wixproj ^ + -p:RunWixToolsOutOfProc=true ^ + -p:OutputPath=%PackageRoot%\toolchain\ ^ + -p:IntermediateOutputPath=%PackageRoot%\toolchain\ ^ + -p:TOOLCHAIN_ROOT=%BuildRoot%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain +:: TODO(compnerd) actually perform the code-signing +:: signtool sign /f Apple_CodeSign.pfx /p Apple_CodeSign_Password /tr http://timestamp.digicert.com /fd sha256 %PackageRoot%\toolchain\toolchain.msi + +:: Package sdk.msi +msbuild %SourceRoot%\swift-installer-scripts\platforms\Windows\CustomActions\SwiftInstaller\SwiftInstaller.vcxproj -t:restore +msbuild %SourceRoot%\swift-installer-scripts\platforms\Windows\sdk.wixproj ^ + -p:RunWixToolsOutOfProc=true ^ + -p:OutputPath=%PackageRoot%\sdk\ ^ + -p:IntermediateOutputPath=%PackageRoot%\sdk\ ^ + -p:PLATFORM_ROOT=%PlatformRoot%\ ^ + -p:SDK_ROOT=%SDKInstallRoot%\ ^ + -p:SWIFT_SOURCE_DIR=%SourceRoot%\swift\ ^ + -p:PlatformToolset=v142 +:: TODO(compnerd) actually perform the code-signing +:: signtool sign /f Apple_CodeSign.pfx /p Apple_CodeSign_Password /tr http://timestamp.digicert.com /fd sha256 %PackageRoot%\sdk\sdk.msi + +:: Package runtime.msi +msbuild %SourceRoot%\swift-installer-scripts\platforms\Windows\runtime.wixproj ^ + -p:RunWixToolsOutOfProc=true ^ + -p:OutputPath=%PackageRoot%\runtime\ ^ + -p:IntermediateOutputPath=%PackageRoot%\runtime\ ^ + -p:SDK_ROOT=%SDKInstallRoot%\ +:: TODO(compnerd) actually perform the code-signing +:: signtool sign /f Apple_CodeSign.pfx /p Apple_CodeSign_Password /tr http://timestamp.digicert.com /fd sha256 %PackageRoot%\runtime\runtime.msi + +:: Package icu.msi +msbuild %SourceRoot%\swift-installer-scripts\platforms\Windows\icu.wixproj ^ + -p:RunWixToolsOutOfProc=true ^ + -p:OutputPath=%PackageRoot%\icu\ ^ + -p:IntermediateOutputPath=%PackageRoot%\icu\ ^ + -p:ProductVersion=67.1 ^ + -p:ProductVersionMajor=67 ^ + -p:ICU_ROOT=%BuildRoot% +:: TODO(compnerd) actually perform the code-signing +:: signtool sign /f Apple_CodeSign.pfx /p Apple_CodeSign_Password /tr http://timestamp.digicert.com /fd sha256 %PackageRoot%\icu\icu.msi + +:: Package devtools.msi +msbuild %SourceRoot%\swift-installer-scripts\platforms\Windows\devtools.wixproj ^ + -p:RunWixToolsOutOfProc=true ^ + -p:OutputPath=%PackageRoot%\devtools\ ^ + -p:IntermediateOutputPath=%PackageRoot%\devtools\ ^ + -p:DEVTOOLS_ROOT=%BuildRoot%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain +:: TODO(compnerd) actually perform the code-signing +:: signtool sign /f Apple_CodeSign.pfx /p Apple_CodeSign_Password /tr http://timestamp.digicert.com /fd sha256 %PackageRoot%\devtools\devtools.msi + +:: Collate MSIs +move %PackageRoot%\toolchain\toolchain.msi %PackageRoot% || (exit /b) +move %PackageRoot%\sdk\sdk.msi %PackageRoot% || (exit /b) +move %PackageRoot%\runtime\runtime.msi %PackageRoot% || (exit /b) +move %PackageRoot%\icu\icu.msi %PackageRoot% || (exit /b) +move %PackageRoot%\devtools\devtools.msi %PackageRoot% || (exit /b) + +:: Build Installer +msbuild %SourceRoot%\swift-installer-scripts\platforms\Windows\installer.wixproj ^ + -p:RunWixToolsOutOfProc=true ^ + -p:OutputPath=%PackageRoot%\installer\ ^ + -p:IntermediateOutputPath=%PackageRoot%\installer\ ^ + -p:MSI_LOCATION=%PackageRoot%\ +:: TODO(compnerd) actually perform the code-signing +:: signtool sign /f Apple_CodeSign.pfx /p Apple_CodeSign_Password /tr http://timestamp.digicert.com /fd sha256 %PackageRoot%\installer\installer.exe + +:: Stage Artifacts +md %BuildRoot%\artifacts +:: FIXME(compnerd) should we provide SDKs as standalone artifact? +move %PackageRoot%\sdk.msi %BuildRoot%\artifacts || (exit /b) +:: Redistributable libraries for developers +move %PackageRoot%\runtime.msi %BuildRoot%\artifacts || (exit /b) +move %PackageRoot%\icu.msi %BuildRoot%\artifacts || (exit /b) +:: Installer +move %PackageRoot%\installer\installer.exe %BuildRoot%\artifacts || (exit /b) + +:: TODO(compnerd) test LLVM + +:: Test Swift +:: TODO(compnerd) make lit adjust the path properly +path %BuildRoot%\3;%BuildRoot%\1\bin;%BuildRoot%\Library\icu-67.1\usr\bin;%PATH%;%SystemDrive%\Program Files\Git\usr\bin +cmake --build %BuildRoot%\1 --target check-swift || (exit /b) + +:: Test dispatch +cmake --build %BuildRoot%\3 --target ExperimentalTest || (exit /b) + +:: NOTE(compnerd) update the path *before* the build because the tests are +:: executed to shard the test suite. +path %BuildRoot%\5;%BuildRoot%\4\bin;%PATH% + +:: Rebuild Foundation (w/ testing) +cmake ^ + -B %BuildRoot%\4 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%SDKInstallRoot%\usr ^ + + -D CURL_DIR=%BuildRoot%\Library\curl-7.77.0\usr\lib\cmake\CURL ^ + -D ICU_ROOT=%BuildRoot%\Library\icu-67.1 ^ + -D ICU_UC_LIBRARY=%BuildRoot%\Library\icu-67.1\lib64\icuuc67.lib ^ + -D ICU_I18N_LIBRARY=%BuildRoot%\Library\icu-67.1\lib64\icuin67.lib ^ + -D LIBXML2_LIBRARY=%BuildRoot%\Library\libxml2-2.9.12\usr\lib\libxml2s.lib ^ + -D LIBXML2_INCLUDE_DIR=%BuildRoot%\Library\libxml2-2.9.12\usr\include\libxml2 ^ + -D LIBXML2_DEFINITIONS="/DLIBXML_STATIC" ^ + -D ZLIB_LIBRARY=%BuildRoot%\Library\zlib-1.2.11\usr\lib\zlibstatic.lib ^ + -D ZLIB_INCLUDE_DIR=%BuildRoot%\Library\zlib-1.2.11\usr\include ^ + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D XCTest_DIR=%BuildRoot%\5\cmake\modules ^ + + -D ENABLE_TESTING=YES ^ + + -G Ninja ^ + -S %SourceRoot%\swift-corelibs-foundation || (exit /b) +cmake --build %BuildRoot%\4 || (exit /b) + +:: Test Foundation +set CTEST_OUTPUT_ON_FAILURE=1 +cmake --build %BuildRoot%\4 --target test || (exit /b) + +:: Rebuild XCTest (w/ testing) +cmake ^ + -B %BuildRoot%\5 ^ + + -D CMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ^ + -D CMAKE_C_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_C_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_CXX_COMPILER=%BuildRoot%/1/bin/clang-cl.exe ^ + -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ + -D CMAKE_MT=mt ^ + -D CMAKE_Swift_COMPILER=%BuildRoot%/1/bin/swiftc.exe ^ + -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + + -D CMAKE_INSTALL_PREFIX=%PlatformRoot%\Developer\Library\XCTest-development\usr ^ + + -D dispatch_DIR=%BuildRoot%\3\cmake\modules ^ + -D Foundation_DIR=%BuildRoot%\4\cmake\modules ^ + + -D ENABLE_TESTING=YES ^ + -D XCTEST_PATH_TO_LIBDISPATCH_BUILD=%BuildRoot%\3 ^ + -D XCTEST_PATH_TO_LIBDISPATCH_SOURCE=%SourceRoot%\swift-corelibs-libdispatch ^ + -D XCTEST_PATH_TO_FOUNDATION_BUILD=%BuildRoot%\4 ^ + + -G Ninja ^ + -S %SourceRoot%\swift-corelibs-xctest || (exit /b) +cmake --build %BuildRoot%\5 || (exit /b) + +:: Test XCTest +cmake --build %BuildRoot%\5 --target check-xctest || (exit /b) + +:: Clean up the module cache +rd /s /q %LocalAppData%\clang\ModuleCache + +goto :end +endlocal + +:CloneRepositories +setlocal enableextensions enabledelayedexpansion + +if defined SKIP_UPDATE_CHECKOUT goto :eof + +if defined REPO_SCHEME set "args=--scheme %REPO_SCHEME%" + +:: Always enable symbolic links +git config --global core.symlink true + +:: Ensure that we have the files in the original line endings, the swift tests +:: depend on this being the case. +git -C "%SourceRoot%\swift" config --local core.autocrlf input +git -C "%SourceRoot%\swift" checkout-index --force --all + +set "args=%args% --skip-repository swift" +set "args=%args% --skip-repository ninja" +set "args=%args% --skip-repository icu" +set "args=%args% --skip-repository swift-integration-tests" +set "args=%args% --skip-repository swift-stress-tester" +set "args=%args% --skip-repository swift-xcode-playground-support" + +call "%SourceRoot%\swift\utils\update-checkout.cmd" %args% --clone --skip-tags --skip-history --github-comment "%ghprbCommentBody%" + +goto :eof +endlocal + +:CloneDependencies +setlocal enableextensions enabledelayedexpansion + +:: Always enable symbolic links +git config --global core.symlink true + +:: FIXME(compnerd) avoid the fresh clone +rd /s /q zlib libxml2 sqlite icu curl + +git clone --quiet --no-tags --depth 1 --branch v1.2.11 https://github.com/madler/zlib +git clone --quiet --no-tags --depth 1 --branch v2.9.12 https://github.com/gnome/libxml2 +git clone --quiet --no-tags --depth 1 --branch version-3.36.0 https://github.com/sqlite/sqlite +git clone --quiet --no-tags --depth 1 --branch maint/maint-67 https://github.com/unicode-org/icu +git clone --quiet --no-tags --depth 1 --branch curl-7_77_0 https://github.com/curl/curl + +goto :eof +endlocal + +:end diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 2929b8875f28d..f3567a42efc7c 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -1,369 +1,379 @@ -:: build-windows.bat -:: -:: This source file is part of the Swift.org open source project -:: -:: Copyright (c) 2014 - 2019 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 - -:: REQUIRED ENVIRONMENT VARIABLES -:: This script requires to be executed inside one Visual Studio command line, -:: in order for many of the tools and environment variables to be available. -:: Additionally, it needs the following variables: -:: - CMAKE_BUILD_TYPE: Kind of build: Release, RelWithDebInfo, Debug. -:: - PYTHON_HOME: The Python installation directory. -:: - REPO_SCHEME: Optional. The scheme name to checkout. - -:: REQUIRED PERMISSIONS -:: Practically, it is easier to be in the Adminstrators group to run the -:: script, but it should be possible to execute as a normal user. -:: The user will need permission to write files into the Windows SDK and the -:: VisualC++ folder. - -:: @echo off - -setlocal enableextensions enabledelayedexpansion - -PATH=%PATH%;%PYTHON_HOME% - -set icu_version_major=64 -set icu_version_minor=2 -set icu_version=%icu_version_major%_%icu_version_minor% -set icu_version_dashed=%icu_version_major%-%icu_version_minor% - -set "exitOnError=|| (exit /b)" -set current_directory=%~dp0 -set current_directory=%current_directory:~0,-1% -set source_root=%current_directory%\..\.. - -:: Resetting source_root with %CD% removes the ..\.. from the paths, and makes -:: the output easier to read. -cd %source_root% -set source_root=%CD% - -set full_build_root=%source_root%\build -mkdir %full_build_root% - -:: Use the shortest path we can for the build directory, to avoid Windows -:: path problems as much as we can. -subst T: /d -subst T: %full_build_root% %exitOnError% -set build_root=T: -set install_directory=%build_root%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr - -md %build_root%\tmp -set TMPDIR=%build_root%\tmp -set TMP=%build_root%\tmp -set TEMP=%build_root%\tmp - -md %build_root%\tmp\org.llvm.clang.9999 -set CUSTOM_CLANG_MODULE_CACHE=%build_root%\tmp\org.llvm.clang.9999 - -md %build_root%\tmp\org.swift.package-manager -set SWIFTPM_MODULECACHE_OVERRIDE=%build_root%\tmp\org.swift.package-manager - -call :clone_repositories %exitOnError% -call :download_icu %exitOnError% -:: TODO: Disabled until we need LLBuild/SwiftPM in this build script. -:: call :download_sqlite3 - -call :build_llvm %exitOnError% -path %PATH%;%install_directory%\bin - -call :build_cmark %exitOnError% - -call :prepare_platform_modules %exitOnError% -call :build_swift %exitOnError% - -call :build_lldb %exitOnError% - -call :build_libdispatch %exitOnError% - -path %source_root%\icu-%icu_version%\bin64;%install_directory%\bin;%build_root%\swift\bin;%build_root%\swift\libdispatch-prefix\bin;%PATH%;C:\Program Files\Git\usr\bin -call :test_swift %exitOnError% -call :test_libdispatch %exitOnError% - -goto :end -endlocal - -:clone_repositories -:: Clones the repositories used by the Windows build. -:: It supposes that the swift repository is already cloned by CI. -:: It supposes the %CD% is the source root. -setlocal enableextensions enabledelayedexpansion - -if defined REPO_SCHEME SET "scheme_arg=--scheme %REPO_SCHEME%" - -git -C "%source_root%\swift" config --local core.autocrlf input -git -C "%source_root%\swift" config --local core.symlink true -git -C "%source_root%\swift" checkout-index --force --all - -:: Always skip Swift, since it is checked out by Jenkins -@set "skip_repositories_arg=--skip-repository swift" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository llbuild" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository indexstore-db" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository ninja" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository sourcekit-lsp" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-argument-parser" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-corelibs-foundation" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-corelibs-xctest" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-driver" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-format" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-integration-tests" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swiftpm" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-stress-tester" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-syntax" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-tools-support-core" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-xcode-playground-support" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository tensorflow-swift-apis" -@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository yams" - -call "%source_root%\swift\utils\update-checkout.cmd" %scheme_arg% %skip_repositories_arg% --clone --skip-history --github-comment "%ghprbCommentBody%" >NUL 2>NUL - -goto :eof -endlocal - - -:download_icu -:: Downloads ICU, which will be used as a dependency for the Swift Standard -:: Library and Foundation. -setlocal enableextensions enabledelayedexpansion - -set file_name=icu4c-%icu_version%-Win64-MSVC2017.zip -curl -L -O "https://github.com/unicode-org/icu/releases/download/release-%icu_version_dashed%/%file_name%" %exitOnError% -:: unzip warns about the paths in the zip using slashes, which raises the -:: errorLevel to 1. We cannot use exitOnError, and have to ignore errors. -"C:\Program Files\Git\usr\bin\unzip.exe" -o %file_name% -d "%source_root%\icu-%icu_version%" -exit /b 0 - -goto :eof -endlocal - - -:download_sqlite3 -:: Downloads SQLite3, which will be used as a dependency for llbuild and -:: Swift Package Manager. -setlocal enableextensions enabledelayedexpansion - -set file_name=sqlite-amalgamation-3270200.zip -curl -L -O "https://www.sqlite.org/2019/%file_name%" %exitOnError% -"C:\Program Files\Git\usr\bin\unzip.exe" -o %file_name% %exitOnError% - -goto :eof -endlocal - - -:prepare_platform_modules -:: Create files into the right places of the Windows SDK to the files in the -:: swift repository, in order to consider the headers of the Windows SDK a -:: module to compile Swift code against them. -setlocal enableextensions enabledelayedexpansion - -copy /y "%source_root%\swift\stdlib\public\Platform\ucrt.modulemap" "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" %exitOnError% -copy /y "%source_root%\swift\stdlib\public\Platform\winsdk.modulemap" "%UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap" %exitOnError% -copy /y "%source_root%\swift\stdlib\public\Platform\visualc.modulemap" "%VCToolsInstallDir%\include\module.modulemap" %exitOnError% -copy /y "%source_root%\swift\stdlib\public\Platform\visualc.apinotes" "%VCToolsInstallDir%\include\visualc.apinotes" %exitOnError% - -goto :eof -endlocal - - -:build_llvm -:: Configures, builds, and installs LLVM -setlocal enableextensions enabledelayedexpansion - -cmake^ - -B "%build_root%\llvm"^ - -G Ninja^ - -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ - -DCMAKE_C_COMPILER=cl^ - -DCMAKE_CXX_COMPILER=cl^ - -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ - -DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-unknown-windows-msvc^ - -DLLVM_ENABLE_PDB:BOOL=YES^ - -DLLVM_ENABLE_ASSERTIONS:BOOL=YES^ - -DLLVM_ENABLE_PROJECTS:STRING=lld;clang^ - -DLLVM_TARGETS_TO_BUILD:STRING="AArch64;ARM;X86"^ - -DLLVM_INCLUDE_BENCHMARKS:BOOL=NO^ - -DLLVM_INCLUDE_DOCS:BOOL=NO^ - -DLLVM_INCLUDE_EXAMPLES:BOOL=NO^ - -DLLVM_INCLUDE_GO_TESTS:BOOL=NO^ - -DLLVM_TOOL_GOLD_BUILD:BOOL=NO^ - -DLLVM_ENABLE_OCAMLDOC:BOOL=NO^ - -DLLVM_ENABLE_LIBXML2:BOOL=NO^ - -DLLVM_ENABLE_ZLIB:BOOL=NO^ - -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON^ - -DENABLE_X86_RELAX_RELOCATIONS:BOOL=YES^ - -DLLVM_INSTALL_BINUTILS_SYMLINKS:BOOL=YES^ - -DLLVM_INSTALL_TOOLCHAIN_ONLY:BOOL=YES^ - -DLLVM_TOOLCHAIN_TOOLS:STRING="addr2line;ar;c++filt;dsymutil;dwp;llvm-ar;llvm-cov;llvm-cvtres;llvm-cxxfilt;llvm-dlltool;llvm-dwp;llvm-ranlib;llvm-lib;llvm-mt;llvm-nm;llvm-objdump;llvm-pdbutil;llvm-profdata;llvm-rc;llvm-readelf;llvm-readobj;llvm-size;llvm-strip;llvm-symbolizer;llvm-undname;nm;objcopy;objdump;ranlib;readelf;size;strings"^ - -DCLANG_TOOLS="clang;clang-format;clang-headers;clang-tidy"^ - -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ - -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -S "%source_root%\llvm-project\llvm" %exitOnError% - -cmake --build "%build_root%\llvm" %exitOnError% -cmake --build "%build_root%\llvm" --target install %exitOnError% - -goto :eof -endlocal - - -:build_cmark -:: Configures and builds CMark -setlocal enableextensions enabledelayedexpansion - -cmake^ - -B "%build_root%\cmark"^ - -G Ninja^ - -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ - -DCMAKE_C_COMPILER=cl^ - -DCMAKE_CXX_COMPILER=cl^ - -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ - -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -S "%source_root%\cmark" %exitOnError% - -cmake --build "%build_root%\cmark" %exitOnError% - -goto :eof -endlocal - - -:build_swift -:: Configures, builds, and installs Swift and the Swift Standard Library -setlocal enableextensions enabledelayedexpansion - -:: SWIFT_PARALLEL_LINK_JOBS=8 allows the build machine to use as many CPU as -:: possible, while not exhausting the RAM. -cmake^ - -B "%build_root%\swift"^ - -G Ninja^ - -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ - -DCMAKE_C_COMPILER=cl^ - -DCMAKE_CXX_COMPILER=cl^ - -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ - -DClang_DIR:PATH=%build_root%\llvm\lib\cmake\clang^ - -DSWIFT_PATH_TO_CMARK_BUILD:PATH=%build_root%\cmark^ - -DSWIFT_PATH_TO_CMARK_SOURCE:PATH=%source_root%\cmark^ - -DSWIFT_PATH_TO_LIBDISPATCH_SOURCE:PATH=%source_root%\swift-corelibs-libdispatch^ - -DLLVM_DIR:PATH=%build_root%\llvm\lib\cmake\llvm^ - -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON^ - -DSWIFT_INCLUDE_DOCS:BOOL=NO^ - -DSWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE:PATH=%source_root%\icu-%icu_version%\include\unicode^ - -DSWIFT_WINDOWS_x86_64_ICU_UC:PATH=%source_root%\icu-%icu_version%\lib64\icuuc.lib^ - -DSWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE:PATH=%source_root%\icu-%icu_version%\include^ - -DSWIFT_WINDOWS_x86_64_ICU_I18N:PATH=%source_root%\icu-%icu_version%\lib64\icuin.lib^ - -DSWIFT_BUILD_DYNAMIC_STDLIB:BOOL=YES^ - -DSWIFT_BUILD_DYNAMIC_SDK_OVERLAY:BOOL=YES^ - -DSWIFT_BUILD_STATIC_STDLIB:BOOL=NO^ - -DSWIFT_BUILD_STATIC_SDK_OVERLAY:BOOL=NO^ - -DLLVM_INSTALL_TOOLCHAIN_ONLY:BOOL=YES^ - -DSWIFT_BUILD_SOURCEKIT:BOOL=YES^ - -DSWIFT_ENABLE_SOURCEKIT_TESTS:BOOL=YES^ - -DSWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY=YES^ - -DSWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING=YES^ - -DSWIFT_INSTALL_COMPONENTS="autolink-driver;compiler;clang-resource-dir-symlink;stdlib;sdk-overlay;editor-integration;tools;sourcekit-inproc;swift-remote-mirror;swift-remote-mirror-headers"^ - -DSWIFT_PARALLEL_LINK_JOBS=8^ - -DPYTHON_EXECUTABLE:PATH=%PYTHON_HOME%\python.exe^ - -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ - -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -S "%source_root%\swift" %exitOnError% - -cmake --build "%build_root%\swift" %exitOnError% -cmake --build "%build_root%\swift" --target install %exitOnError% - -goto :eof -endlocal - - -:test_swift -:: Tests the Swift compiler and the Swift Standard Library -setlocal enableextensions enabledelayedexpansion - -cmake --build "%build_root%\swift" --target check-swift %exitOnError% - -goto :eof -endlocal - - -:build_lldb -:: Configures, builds, and installs LLDB -setlocal enableextensions enabledelayedexpansion - -cmake^ - -B "%build_root%\lldb"^ - -G Ninja^ - -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ - -DCMAKE_C_COMPILER=cl^ - -DCMAKE_CXX_COMPILER=cl^ - -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ - -DLLVM_DIR:PATH=%build_root%\llvm\lib\cmake\llvm^ - -DClang_DIR:PATH=%build_root%\llvm\lib\cmake\clang^ - -DSwift_DIR:PATH=%build_root%\swift\lib\cmake\swift^ - -DLLVM_ENABLE_ASSERTIONS:BOOL=YES^ - -DLLDB_USE_STATIC_BINDINGS:BOOL=YES^ - -DPYTHON_HOME:PATH=%PYTHON_HOME%^ - -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ - -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DLLDB_DISABLE_PYTHON=YES^ - -DLLDB_INCLUDE_TESTS:BOOL=NO^ - -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON^ - -S "%source_root%\llvm-project\lldb" %exitOnError% - -cmake --build "%build_root%\lldb" %exitOnError% -cmake --build "%build_root%\lldb" --target install %exitOnError% - -goto :eof -endlocal - - -:build_libdispatch -:: Configures, builds, and installs Dispatch -setlocal enableextensions enabledelayedexpansion - -cmake^ - -B "%build_root%\swift-corelibs-libdispatch"^ - -G Ninja^ - -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ - -DCMAKE_C_COMPILER=clang-cl^ - -DCMAKE_CXX_COMPILER=clang-cl^ - -DCMAKE_Swift_COMPILER=swiftc^ - -DSwift_DIR:PATH=%build_root%\swift\lib\cmake\swift^ - -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ - -DCMAKE_C_COMPILER_TARGET=x86_64-unknown-windows-msvc^ - -DCMAKE_CXX_COMPILER_TARGET=x86_64-unknown-windows-msvc^ - -DENABLE_SWIFT:BOOL=YES^ - -DENABLE_TESTING:BOOL=YES^ - -DCMAKE_C_FLAGS:STRING="${CMAKE_C_FLAGS} --target=x86_64-unknown-windows-msvc /GS- /Oy /Gw /Gy"^ - -DCMAKE_CXX_FLAGS:STRING="${CMAKE_CXX_FLAGS} --target=x86_64-unknown-windows-msvc /GS- /Oy /Gw /Gy"^ - -DCMAKE_EXE_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ - -DCMAKE_Swift_COMPILER_TARGET:STRING=x86_64-unknown-windows-msvc^ - -DCMAKE_Swift_FLAGS:STRING="-resource-dir \"%install_directory%\lib\swift\""^ - -DCMAKE_Swift_LINK_FLAGS:STRING="-resource-dir \"%install_directory%\lib\swift\""^ - -S "%source_root%\swift-corelibs-libdispatch" %exitOnError% - -cmake --build "%build_root%\swift-corelibs-libdispatch" %exitOnError% -cmake --build "%build_root%\swift-corelibs-libdispatch" --target install %exitOnError% - -goto :eof -endlocal - - -:test_libdispatch -:: Tests libdispatch C interface -setlocal enableextensions enabledelayedexpansion - -cmake --build "%build_root%\swift-corelibs-libdispatch" --target ExperimentalTest %exitOnError% - -goto :eof -endlocal - - -:end +:: build-windows.bat +:: +:: This source file is part of the Swift.org open source project +:: +:: Copyright (c) 2014 - 2021 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 + +:: REQUIRED ENVIRONMENT VARIABLES +:: This script requires to be executed inside one Visual Studio command line, +:: in order for many of the tools and environment variables to be available. +:: Additionally, it needs the following variables: +:: - CMAKE_BUILD_TYPE: Kind of build: Release, RelWithDebInfo, Debug. +:: - PYTHON_HOME: The Python installation directory. +:: - REPO_SCHEME: Optional. The scheme name to checkout. + +:: REQUIRED PERMISSIONS +:: Practically, it is easier to be in the Adminstrators group to run the +:: script, but it should be possible to execute as a normal user. +:: The user will need permission to write files into the Windows SDK and the +:: VisualC++ folder. + +:: @echo off + +setlocal enableextensions enabledelayedexpansion + +PATH=%PATH%;%PYTHON_HOME% + +set icu_version_major=64 +set icu_version_minor=2 +set icu_version=%icu_version_major%_%icu_version_minor% +set icu_version_dashed=%icu_version_major%-%icu_version_minor% + +set "exitOnError=|| (exit /b)" +set current_directory=%~dp0 +set current_directory=%current_directory:~0,-1% +set source_root=%current_directory%\..\.. + +:: Resetting source_root with %CD% removes the ..\.. from the paths, and makes +:: the output easier to read. +cd %source_root% +set source_root=%CD% + +set full_build_root=%source_root%\build +mkdir %full_build_root% + +:: Use the shortest path we can for the build directory, to avoid Windows +:: path problems as much as we can. +subst T: /d +subst T: %full_build_root% %exitOnError% +set build_root=T: +set install_directory=%build_root%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr + +md %build_root%\tmp +set TMPDIR=%build_root%\tmp +set TMP=%build_root%\tmp +set TEMP=%build_root%\tmp + +md %build_root%\tmp\org.llvm.clang.9999 +set CUSTOM_CLANG_MODULE_CACHE=%build_root%\tmp\org.llvm.clang.9999 + +md %build_root%\tmp\org.swift.package-manager +set SWIFTPM_MODULECACHE_OVERRIDE=%build_root%\tmp\org.swift.package-manager + +call :clone_repositories %exitOnError% +call :download_icu %exitOnError% +:: TODO: Disabled until we need LLBuild/SwiftPM in this build script. +:: call :download_sqlite3 + +call :build_llvm %exitOnError% +path %PATH%;%install_directory%\bin + +call :build_cmark %exitOnError% + +call :prepare_platform_modules %exitOnError% +call :build_swift %exitOnError% + +call :build_lldb %exitOnError% + +path %PATH%;C:\Program Files\Git\usr\bin +call :build_libdispatch %exitOnError% + +path %source_root%\icu-%icu_version%\bin64;%install_directory%\bin;%build_root%\swift\bin;%build_root%\swift\libdispatch-prefix\bin;%PATH% +call :test_swift %exitOnError% +call :test_libdispatch %exitOnError% + +goto :end +endlocal + +:clone_repositories +:: Clones the repositories used by the Windows build. +:: It supposes that the swift repository is already cloned by CI. +:: It supposes the %CD% is the source root. +setlocal enableextensions enabledelayedexpansion + +if defined REPO_SCHEME SET "scheme_arg=--scheme %REPO_SCHEME%" + +git -C "%source_root%\swift" config --local core.autocrlf input +git -C "%source_root%\swift" config --local core.symlink true +git -C "%source_root%\swift" checkout-index --force --all + +:: Always skip Swift, since it is checked out by Jenkins +@set "skip_repositories_arg=--skip-repository swift" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository llbuild" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository indexstore-db" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository ninja" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository sourcekit-lsp" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-argument-parser" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-corelibs-foundation" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-corelibs-xctest" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-driver" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-format" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-integration-tests" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swiftpm" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-stress-tester" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-syntax" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-tools-support-core" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository swift-xcode-playground-support" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository tensorflow-swift-apis" +@set "skip_repositories_arg=%skip_repositories_arg% --skip-repository yams" + +call "%source_root%\swift\utils\update-checkout.cmd" %scheme_arg% %skip_repositories_arg% --clone --skip-history --skip-tags --github-comment "%ghprbCommentBody%" >NUL 2>NUL + +goto :eof +endlocal + + +:download_icu +:: Downloads ICU, which will be used as a dependency for the Swift Standard +:: Library and Foundation. +setlocal enableextensions enabledelayedexpansion + +set file_name=icu4c-%icu_version%-Win64-MSVC2017.zip +curl -L -O "https://github.com/unicode-org/icu/releases/download/release-%icu_version_dashed%/%file_name%" %exitOnError% +:: unzip warns about the paths in the zip using slashes, which raises the +:: errorLevel to 1. We cannot use exitOnError, and have to ignore errors. +"C:\Program Files\Git\usr\bin\unzip.exe" -o %file_name% -d "%source_root%\icu-%icu_version%" +exit /b 0 + +goto :eof +endlocal + + +:download_sqlite3 +:: Downloads SQLite3, which will be used as a dependency for llbuild and +:: Swift Package Manager. +setlocal enableextensions enabledelayedexpansion + +set file_name=sqlite-amalgamation-3270200.zip +curl -L -O "https://www.sqlite.org/2019/%file_name%" %exitOnError% +"C:\Program Files\Git\usr\bin\unzip.exe" -o %file_name% %exitOnError% + +goto :eof +endlocal + + +:prepare_platform_modules +:: Create files into the right places of the Windows SDK to the files in the +:: swift repository, in order to consider the headers of the Windows SDK a +:: module to compile Swift code against them. +setlocal enableextensions enabledelayedexpansion + +copy /y "%source_root%\swift\stdlib\public\Platform\ucrt.modulemap" "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" %exitOnError% +copy /y "%source_root%\swift\stdlib\public\Platform\winsdk.modulemap" "%UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap" %exitOnError% +copy /y "%source_root%\swift\stdlib\public\Platform\visualc.modulemap" "%VCToolsInstallDir%\include\module.modulemap" %exitOnError% +copy /y "%source_root%\swift\stdlib\public\Platform\visualc.apinotes" "%VCToolsInstallDir%\include\visualc.apinotes" %exitOnError% + +goto :eof +endlocal + + +:build_llvm +:: Configures, builds, and installs LLVM +setlocal enableextensions enabledelayedexpansion + +cmake^ + -B "%build_root%\llvm"^ + -G Ninja^ + -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ + -DCMAKE_C_COMPILER=cl^ + -DCMAKE_CXX_COMPILER=cl^ + -DCMAKE_MT=mt^ + -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ + -DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-unknown-windows-msvc^ + -DLLVM_ENABLE_PDB:BOOL=YES^ + -DLLVM_ENABLE_ASSERTIONS:BOOL=YES^ + -DLLVM_ENABLE_PROJECTS:STRING=lld;clang^ + -DLLVM_TARGETS_TO_BUILD:STRING="AArch64;ARM;X86"^ + -DLLVM_INCLUDE_BENCHMARKS:BOOL=NO^ + -DLLVM_INCLUDE_DOCS:BOOL=NO^ + -DLLVM_INCLUDE_EXAMPLES:BOOL=NO^ + -DLLVM_INCLUDE_GO_TESTS:BOOL=NO^ + -DLLVM_TOOL_GOLD_BUILD:BOOL=NO^ + -DLLVM_ENABLE_OCAMLDOC:BOOL=NO^ + -DLLVM_ENABLE_LIBXML2:BOOL=NO^ + -DLLVM_ENABLE_ZLIB:BOOL=NO^ + -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON^ + -DENABLE_X86_RELAX_RELOCATIONS:BOOL=YES^ + -DLLVM_INSTALL_BINUTILS_SYMLINKS:BOOL=YES^ + -DLLVM_INSTALL_TOOLCHAIN_ONLY:BOOL=YES^ + -DLLVM_TOOLCHAIN_TOOLS:STRING="addr2line;ar;c++filt;dsymutil;dwp;llvm-ar;llvm-cov;llvm-cvtres;llvm-cxxfilt;llvm-dlltool;llvm-dwp;llvm-ranlib;llvm-lib;llvm-mt;llvm-nm;llvm-objdump;llvm-pdbutil;llvm-profdata;llvm-rc;llvm-readelf;llvm-readobj;llvm-size;llvm-strip;llvm-symbolizer;llvm-undname;nm;objcopy;objdump;ranlib;readelf;size;strings"^ + -DCLANG_TOOLS="clang;clang-format;clang-headers;clang-tidy"^ + -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ + -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -S "%source_root%\llvm-project\llvm" %exitOnError% + +cmake --build "%build_root%\llvm" %exitOnError% +cmake --build "%build_root%\llvm" --target install %exitOnError% + +goto :eof +endlocal + + +:build_cmark +:: Configures and builds CMark +setlocal enableextensions enabledelayedexpansion + +cmake^ + -B "%build_root%\cmark"^ + -G Ninja^ + -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ + -DCMAKE_C_COMPILER=cl^ + -DCMAKE_CXX_COMPILER=cl^ + -DCMAKE_MT=mt^ + -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ + -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -S "%source_root%\cmark" %exitOnError% + +cmake --build "%build_root%\cmark" %exitOnError% + +goto :eof +endlocal + + +:build_swift +:: Configures, builds, and installs Swift and the Swift Standard Library +setlocal enableextensions enabledelayedexpansion + +:: SWIFT_PARALLEL_LINK_JOBS=8 allows the build machine to use as many CPU as +:: possible, while not exhausting the RAM. +cmake^ + -B "%build_root%\swift"^ + -G Ninja^ + -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ + -DCMAKE_C_COMPILER=cl^ + -DCMAKE_CXX_COMPILER=cl^ + -DCMAKE_MT=mt^ + -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ + -DClang_DIR:PATH=%build_root%\llvm\lib\cmake\clang^ + -DSWIFT_PATH_TO_CMARK_BUILD:PATH=%build_root%\cmark^ + -DSWIFT_PATH_TO_CMARK_SOURCE:PATH=%source_root%\cmark^ + -DSWIFT_PATH_TO_LIBDISPATCH_SOURCE:PATH=%source_root%\swift-corelibs-libdispatch^ + -DLLVM_DIR:PATH=%build_root%\llvm\lib\cmake\llvm^ + -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON^ + -DSWIFT_INCLUDE_DOCS:BOOL=NO^ + -DSWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE:PATH=%source_root%\icu-%icu_version%\include\unicode^ + -DSWIFT_WINDOWS_x86_64_ICU_UC:PATH=%source_root%\icu-%icu_version%\lib64\icuuc.lib^ + -DSWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE:PATH=%source_root%\icu-%icu_version%\include^ + -DSWIFT_WINDOWS_x86_64_ICU_I18N:PATH=%source_root%\icu-%icu_version%\lib64\icuin.lib^ + -DSWIFT_BUILD_DYNAMIC_STDLIB:BOOL=YES^ + -DSWIFT_BUILD_DYNAMIC_SDK_OVERLAY:BOOL=YES^ + -DSWIFT_BUILD_STATIC_STDLIB:BOOL=NO^ + -DSWIFT_BUILD_STATIC_SDK_OVERLAY:BOOL=NO^ + -DLLVM_INSTALL_TOOLCHAIN_ONLY:BOOL=YES^ + -DSWIFT_BUILD_SOURCEKIT:BOOL=YES^ + -DSWIFT_ENABLE_SOURCEKIT_TESTS:BOOL=YES^ + -DSWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY=YES^ + -DSWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED=YES^ + -DSWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING=YES^ + -DSWIFT_INSTALL_COMPONENTS="autolink-driver;compiler;clang-resource-dir-symlink;stdlib;sdk-overlay;editor-integration;tools;sourcekit-inproc;swift-remote-mirror;swift-remote-mirror-headers"^ + -DSWIFT_PARALLEL_LINK_JOBS=8^ + -DPYTHON_EXECUTABLE:PATH=%PYTHON_HOME%\python.exe^ + -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ + -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -DSWIFT_LIT_ARGS="--time-tests"^ + -S "%source_root%\swift" %exitOnError% + +cmake --build "%build_root%\swift" %exitOnError% +cmake --build "%build_root%\swift" --target install %exitOnError% + +goto :eof +endlocal + + +:test_swift +:: Tests the Swift compiler and the Swift Standard Library +setlocal enableextensions enabledelayedexpansion + +cmake --build "%build_root%\swift" --target check-swift %exitOnError% + +goto :eof +endlocal + + +:build_lldb +:: Configures, builds, and installs LLDB +setlocal enableextensions enabledelayedexpansion + +cmake^ + -B "%build_root%\lldb"^ + -G Ninja^ + -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ + -DCMAKE_C_COMPILER=cl^ + -DCMAKE_CXX_COMPILER=cl^ + -DCMAKE_MT=mt^ + -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ + -DLLVM_DIR:PATH=%build_root%\llvm\lib\cmake\llvm^ + -DClang_DIR:PATH=%build_root%\llvm\lib\cmake\clang^ + -DSwift_DIR:PATH=%build_root%\swift\lib\cmake\swift^ + -DLLVM_ENABLE_ASSERTIONS:BOOL=YES^ + -DLLDB_USE_STATIC_BINDINGS:BOOL=YES^ + -DPYTHON_HOME:PATH=%PYTHON_HOME%^ + -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ + -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -DLLDB_DISABLE_PYTHON=YES^ + -DLLDB_INCLUDE_TESTS:BOOL=NO^ + -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON^ + -S "%source_root%\llvm-project\lldb" %exitOnError% + +cmake --build "%build_root%\lldb" %exitOnError% +cmake --build "%build_root%\lldb" --target install %exitOnError% + +goto :eof +endlocal + + +:build_libdispatch +:: Configures, builds, and installs Dispatch +setlocal enableextensions enabledelayedexpansion + +for /f "delims=" %%O in ('cygpath -m %install_directory%\lib\swift') do set RESOURCE_DIR=%%O + +cmake^ + -B "%build_root%\swift-corelibs-libdispatch"^ + -G Ninja^ + -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ + -DCMAKE_C_COMPILER=clang-cl^ + -DCMAKE_CXX_COMPILER=clang-cl^ + -DCMAKE_MT=mt^ + -DCMAKE_Swift_COMPILER=swiftc^ + -DSwift_DIR:PATH=%build_root%\swift\lib\cmake\swift^ + -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ + -DCMAKE_C_COMPILER_TARGET=x86_64-unknown-windows-msvc^ + -DCMAKE_CXX_COMPILER_TARGET=x86_64-unknown-windows-msvc^ + -DENABLE_SWIFT:BOOL=YES^ + -DENABLE_TESTING:BOOL=YES^ + -DCMAKE_C_FLAGS:STRING="${CMAKE_C_FLAGS} --target=x86_64-unknown-windows-msvc /GS- /Oy /Gw /Gy"^ + -DCMAKE_CXX_FLAGS:STRING="${CMAKE_CXX_FLAGS} --target=x86_64-unknown-windows-msvc /GS- /Oy /Gw /Gy"^ + -DCMAKE_EXE_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ + -DCMAKE_SHARED_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ + -DCMAKE_Swift_COMPILER_TARGET:STRING=x86_64-unknown-windows-msvc^ + -DCMAKE_Swift_FLAGS:STRING="-resource-dir \"%RESOURCE_DIR%\""^ + -DCMAKE_Swift_LINK_FLAGS:STRING="-resource-dir \"%RESOURCE_DIR%\""^ + -S "%source_root%\swift-corelibs-libdispatch" %exitOnError% + +cmake --build "%build_root%\swift-corelibs-libdispatch" %exitOnError% +cmake --build "%build_root%\swift-corelibs-libdispatch" --target install %exitOnError% + +goto :eof +endlocal + + +:test_libdispatch +:: Tests libdispatch C interface +setlocal enableextensions enabledelayedexpansion + +cmake --build "%build_root%\swift-corelibs-libdispatch" --target ExperimentalTest %exitOnError% + +goto :eof +endlocal + + +:end diff --git a/utils/build_swift/build_swift/defaults.py b/utils/build_swift/build_swift/defaults.py index f39163fd33fd8..cc910240066f5 100644 --- a/utils/build_swift/build_swift/defaults.py +++ b/utils/build_swift/build_swift/defaults.py @@ -49,7 +49,7 @@ CMAKE_GENERATOR = 'Ninja' COMPILER_VENDOR = 'none' -SWIFT_USER_VISIBLE_VERSION = Version('5.5') +SWIFT_USER_VISIBLE_VERSION = Version('5.6') CLANG_USER_VISIBLE_VERSION = Version('10.0.0') SWIFT_ANALYZE_CODE_COVERAGE = 'false' diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index b2481b758d4d2..215cc6d3525d2 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -183,6 +183,7 @@ def _apply_default_arguments(args): args.test_tvos = False args.test_watchos = False args.test_android = False + args.test_cmark = False args.test_swiftpm = False args.test_swift_driver = False args.test_swiftsyntax = False @@ -193,6 +194,12 @@ def _apply_default_arguments(args): args.test_swiftevolve = False args.test_toolchainbenchmarks = False + # --test implies --test-early-swift-driver + # (unless explicitly skipped with `--skip-test-early-swift-driver`) + if args.test and (args.build_early_swift_driver and + args.test_early_swift_driver is None): + args.test_early_swift_driver = True + # --skip-test-ios is merely a shorthand for host and simulator tests. if not args.test_ios: args.test_ios_host = False @@ -282,6 +289,9 @@ def create_argument_parser(): help='instead of building, write JSON to stdout containing ' 'various values used to build in this configuration') + option(['--reconfigure'], store_true, + help="Reconfigure all projects as we build") + option('--legacy-impl', store_true('legacy_impl'), help='use legacy implementation') @@ -586,6 +596,13 @@ def create_argument_parser(): option(['-b', '--llbuild'], toggle_true('build_llbuild'), help='build llbuild') + option(['--back-deploy-concurrency'], toggle_true('build_backdeployconcurrency'), + help='build back-deployment support for concurrency') + + option(['--install-back-deploy-concurrency'], + toggle_true('install_backdeployconcurrency'), + help='install back-deployment support libraries for concurrency') + option(['--libcxx'], toggle_true('build_libcxx'), help='build libcxx') @@ -610,6 +627,9 @@ def create_argument_parser(): option(['--swift-driver'], toggle_true('build_swift_driver'), help='build swift-driver') + option(['--skip-early-swift-driver'], toggle_false('build_early_swift_driver'), + help='skip building the early swift-driver') + option(['--indexstore-db'], toggle_true('build_indexstoredb'), help='build IndexStoreDB') option('--test-indexstore-db-sanitize-all', @@ -684,6 +704,12 @@ def create_argument_parser(): option('--symbols-package', store_path, help='if provided, an archive of the symbols directory will be ' 'generated at this path') + option('--darwin-symroot-path-filters', append, + type=argparse.ShellSplitType(), + help='Space separated list of patterns used to match ' + 'a subset of files to generate symbols for. ' + 'Only supported on Darwin. Can be called multiple times ' + 'to add multiple such options.') # ------------------------------------------------------------------------- in_group('Build variant') @@ -1040,13 +1066,25 @@ def create_argument_parser(): toggle_false('test_android_host'), help='skip testing Android device targets on the host machine (the ' 'phone itself)') - + option('--skip-clean-libdispatch', toggle_false('clean_libdispatch'), + help='skip cleaning up libdispatch') + option('--skip-clean-foundation', toggle_false('clean_foundation'), + help='skip cleaning up foundation') + option('--skip-clean-xctest', toggle_false('clean_xctest'), + help='skip cleaning up xctest') option('--skip-clean-llbuild', toggle_false('clean_llbuild'), help='skip cleaning up llbuild') + option('--clean-early-swift-driver', toggle_true('clean_early_swift_driver'), + help='Clean up the early SwiftDriver') + option('--skip-test-early-swift-driver', + store('test_early_swift_driver', const=False), + help='Test the early SwiftDriver against the host toolchain') option('--skip-clean-swiftpm', toggle_false('clean_swiftpm'), help='skip cleaning up swiftpm') option('--skip-clean-swift-driver', toggle_false('clean_swift_driver'), help='skip cleaning up Swift driver') + option('--skip-test-cmark', toggle_false('test_cmark'), + help='skip testing cmark') option('--skip-test-swiftpm', toggle_false('test_swiftpm'), help='skip testing swiftpm') option('--skip-test-swift-driver', toggle_false('test_swift_driver'), @@ -1152,6 +1190,10 @@ def create_argument_parser(): default=True, help='Enable experimental Swift concurrency model.') + option('--enable-experimental-distributed', toggle_true, + default=True, + help='Enable experimental Swift distributed actors.') + # ------------------------------------------------------------------------- in_group('Unsupported options') diff --git a/utils/build_swift/tests/build_swift/test_driver_arguments.py b/utils/build_swift/tests/build_swift/test_driver_arguments.py index b9c3ad10527cf..370e9f4538be4 100644 --- a/utils/build_swift/tests/build_swift/test_driver_arguments.py +++ b/utils/build_swift/tests/build_swift/test_driver_arguments.py @@ -586,6 +586,14 @@ def test_implied_defaults_test_optimize_for_size(self): namespace = self.parse_default_args(['--test-optimize-for-size']) self.assertTrue(namespace.test) + def test_implied_defaults_test_early_swift_driver(self): + namespace = self.parse_default_args(['--test']) + self.assertTrue(namespace.test_early_swift_driver) + + def test_implied_defaults_test_no_early_swift_driver(self): + namespace = self.parse_default_args(['--test --skip-early-swift-driver']) + self.assertTrue(namespace.test_early_swift_driver is None) + def test_implied_defaults_test_optimize_none_with_implicit_dynamic(self): namespace = self.parse_default_args( ['--test-optimize-none-with-implicit-dynamic']) diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index 9e62289166ee5..99fb84fba64c2 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -56,6 +56,7 @@ 'benchmark_num_onone_iterations': 3, 'build_android': False, 'build_args': [], + 'build_backdeployconcurrency': False, 'build_benchmarks': True, 'build_clang_tools_extra': True, 'build_cygwin': True, @@ -89,6 +90,7 @@ 'build_swift_stdlib_unittest_extra': False, 'build_swiftpm': False, 'build_swift_driver': False, + 'build_early_swift_driver': True, 'build_swiftsyntax': False, 'build_libparser_only': False, 'build_skstresstester': False, @@ -99,8 +101,8 @@ 'test_sourcekitlsp_sanitize_all': False, 'build_sourcekitlsp': False, 'install_swiftpm': False, - 'install_swift_driver': False, 'install_swiftsyntax': False, + 'install_swift_driver': False, 'swiftsyntax_verify_generated_files': False, 'install_playgroundsupport': False, 'install_sourcekitlsp': False, @@ -136,6 +138,7 @@ defaults.DARWIN_DEPLOYMENT_VERSION_TVOS, 'darwin_deployment_version_watchos': defaults.DARWIN_DEPLOYMENT_VERSION_WATCHOS, + 'darwin_symroot_path_filters': [], 'darwin_xcrun_toolchain': None, 'distcc': False, 'sccache': False, @@ -144,6 +147,7 @@ 'enable_asan': False, 'enable_experimental_differentiable_programming': True, 'enable_experimental_concurrency': True, + 'enable_experimental_distributed': True, 'enable_lsan': False, 'enable_sanitize_coverage': False, 'disable_guaranteed_normal_arguments': False, @@ -165,6 +169,7 @@ 'only_executable_test': False, 'only_non_executable_test': False, 'infer_dependencies': False, + 'install_backdeployconcurrency': False, 'install_prefix': targets.install_prefix(), 'install_symroot': None, 'install_destdir': None, @@ -195,6 +200,7 @@ 'native_llvm_tools_path': None, 'native_swift_tools_path': None, 'dump_config': False, + 'reconfigure': False, 'relocate_xdg_cache_home_under_build_subdir': False, 'show_sdks': False, 'skip_build': False, @@ -214,10 +220,15 @@ defaults.SWIFT_MAX_PARALLEL_LTO_LINK_JOBS, 'swift_user_visible_version': defaults.SWIFT_USER_VISIBLE_VERSION, 'symbols_package': None, + 'clean_libdispatch': True, + 'clean_foundation': True, + 'clean_xctest': True, 'clean_llbuild': True, 'clean_swiftpm': True, 'clean_swift_driver': True, + 'clean_early_swift_driver': False, 'test': None, + 'test_early_swift_driver': None, 'test_android': False, 'test_android_host': False, 'test_cygwin': False, @@ -241,6 +252,7 @@ 'test_watchos_host': False, 'test_watchos_simulator': False, 'test_playgroundsupport': True, + 'test_cmark': False, 'test_swiftpm': False, 'test_swift_driver': False, 'test_swiftsyntax': False, @@ -464,7 +476,12 @@ class BuildScriptImplOption(_BaseOption): SetOption('--skip-ios', dest='ios', value=False), SetOption('--skip-tvos', dest='tvos', value=False), SetOption('--skip-watchos', dest='watchos', value=False), + SetOption('--skip-test-early-swift-driver', + dest='test_early_swift_driver', value=False), + SetTrueOption('--back-deploy-concurrency', dest='build_backdeployconcurrency'), + SetTrueOption('--install-back-deploy-concurrency', + dest='install_backdeployconcurrency'), SetTrueOption('--benchmark'), SetTrueOption('--clean'), SetTrueOption('--dry-run'), @@ -500,6 +517,7 @@ class BuildScriptImplOption(_BaseOption): SetTrueOption('--legacy-impl', dest='legacy_impl'), SetTrueOption('--infer', dest='infer_dependencies'), + SetTrueOption('--reconfigure'), EnableOption('--android'), EnableOption('--build-external-benchmarks'), @@ -515,6 +533,7 @@ class BuildScriptImplOption(_BaseOption): EnableOption('--enable-asan'), EnableOption('--enable-experimental-differentiable-programming'), EnableOption('--enable-experimental-concurrency'), + EnableOption('--enable-experimental-distributed'), EnableOption('--enable-lsan'), EnableOption('--enable-sanitize-coverage'), EnableOption('--enable-tsan'), @@ -558,6 +577,7 @@ class BuildScriptImplOption(_BaseOption): EnableOption('--watchos'), EnableOption('--xctest', dest='build_xctest'), EnableOption('--swift-disable-dead-stripping'), + EnableOption('--clean-early-swift-driver', dest='clean_early_swift_driver'), DisableOption('--skip-build-cmark', dest='build_cmark'), DisableOption('--skip-build-llvm', dest='build_llvm'), @@ -582,7 +602,11 @@ class BuildScriptImplOption(_BaseOption): dest='build_watchos_device'), DisableOption('--skip-build-watchos-simulator', dest='build_watchos_simulator'), + DisableOption('--skip-clean-libdispatch', dest='clean_libdispatch'), + DisableOption('--skip-clean-foundation', dest='clean_foundation'), + DisableOption('--skip-clean-xctest', dest='clean_xctest'), DisableOption('--skip-clean-llbuild', dest='clean_llbuild'), + DisableOption('--skip-early-swift-driver', dest='build_early_swift_driver'), DisableOption('--skip-clean-swiftpm', dest='clean_swiftpm'), DisableOption('--skip-clean-swift-driver', dest='clean_swift_driver'), DisableOption('--skip-test-android', dest='test_android'), @@ -608,6 +632,7 @@ class BuildScriptImplOption(_BaseOption): dest='test_watchos_simulator'), DisableOption('--skip-test-playgroundsupport', dest='test_playgroundsupport'), + DisableOption('--skip-test-cmark', dest='test_cmark'), DisableOption('--skip-test-swiftpm', dest='test_swiftpm'), DisableOption('--skip-test-swift-driver', dest='test_swift_driver'), DisableOption('--skip-test-swiftsyntax', dest='test_swiftsyntax'), @@ -688,6 +713,7 @@ class BuildScriptImplOption(_BaseOption): AppendOption('--test-paths'), AppendOption('--llvm-ninja-targets'), AppendOption('--llvm-ninja-targets-for-cross-compile-hosts'), + AppendOption('--darwin-symroot-path-filters'), UnsupportedOption('--build-jobs'), UnsupportedOption('--common-cmake-options'), diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index 56363ac615006..e5d2d474d91b7 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -54,6 +54,7 @@ def __init__(self, internal_name, external_name): KEY('EnableSyntaxMap', 'key.enablesyntaxmap'), KEY('SyntaxTreeTransferMode', 'key.syntaxtreetransfermode'), KEY('EnableStructure', 'key.enablesubstructure'), + KEY('ID', 'key.id'), KEY('Description', 'key.description'), KEY('TypeName', 'key.typename'), KEY('RuntimeName', 'key.runtime_name'), @@ -154,6 +155,7 @@ def __init__(self, internal_name, external_name): KEY('ArgIndex', 'key.argindex'), KEY('Text', 'key.text'), KEY('Category', 'key.category'), + KEY('Categories', 'key.categories'), KEY('IsFunctionLike', 'key.is_function_like'), KEY('IsNonProtocolType', 'key.is_non_protocol_type'), KEY('RefactorActions', 'key.refactor_actions'), @@ -175,8 +177,13 @@ def __init__(self, internal_name, external_name): KEY('ExpressionOffset', 'key.expression_offset'), KEY('ExpressionLength', 'key.expression_length'), KEY('ExpressionType', 'key.expression_type'), + KEY('VariableTypeList', 'key.variable_type_list'), + KEY('VariableOffset', 'key.variable_offset'), + KEY('VariableLength', 'key.variable_length'), + KEY('VariableType', 'key.variable_type'), + KEY('VariableTypeExplicit', 'key.variable_type_explicit'), KEY('CanonicalizeType', 'key.canonicalize_type'), - KEY('InternalDiagnostic', "key.internal_diagnostic"), + KEY('InternalDiagnostic', 'key.internal_diagnostic'), KEY('VFSName', 'key.vfs.name'), KEY('VFSOptions', 'key.vfs.options'), KEY('Files', 'key.files'), @@ -249,6 +256,7 @@ def __init__(self, internal_name, external_name): 'source.request.enable-compile-notifications'), REQUEST('TestNotification', 'source.request.test_notification'), REQUEST('CollectExpressionType', 'source.request.expression.type'), + REQUEST('CollectVariableType', 'source.request.variable.type'), REQUEST('GlobalConfiguration', 'source.request.configuration.global'), REQUEST('DependencyUpdated', 'source.request.dependency_updated'), ] @@ -416,6 +424,8 @@ def __init__(self, internal_name, external_name): KIND('DiagNote', 'source.diagnostic.severity.note'), KIND('DiagWarning', 'source.diagnostic.severity.warning'), KIND('DiagError', 'source.diagnostic.severity.error'), + KIND('DiagDeprecation', 'source.diagnostic.category.deprecation'), + KIND('DiagNoUsage', 'source.diagnostic.category.no_usage'), KIND('CodeCompletionEverything', 'source.codecompletion.everything'), KIND('CodeCompletionModule', 'source.codecompletion.module'), KIND('CodeCompletionKeyword', 'source.codecompletion.keyword'), @@ -453,6 +463,7 @@ def __init__(self, internal_name, external_name): KIND('Unknown', 'source.syntacticrename.unknown'), KIND('StatNumRequests', 'source.statistic.num-requests'), KIND('StatNumSemaRequests', 'source.statistic.num-semantic-requests'), + KIND('StatInstructionCount', 'source.statistic.instruction-count'), KIND('SyntaxTreeOff', 'source.syntaxtree.transfer.off'), KIND('SyntaxTreeFull', 'source.syntaxtree.transfer.full'), KIND('Swift', 'source.lang.swift'), diff --git a/utils/gyb_syntax_support/Child.py b/utils/gyb_syntax_support/Child.py index 63e0a000ef912..936e302cfd728 100644 --- a/utils/gyb_syntax_support/Child.py +++ b/utils/gyb_syntax_support/Child.py @@ -33,11 +33,11 @@ def __init__(self, name, kind, description=None, is_optional=False, self.is_indented = is_indented self.requires_leading_newline = requires_leading_newline - # If the child has "token" anywhere in the kind, it's considered + # If the child ends with "token" in the kind, it's considered # a token node. Grab the existing reference to that token from the # global list. self.token_kind = \ - self.syntax_kind if "Token" in self.syntax_kind else None + self.syntax_kind if self.syntax_kind.endswith("Token") else None self.token = SYNTAX_TOKEN_MAP.get(self.token_kind) self.is_optional = is_optional diff --git a/utils/gyb_syntax_support/DeclNodes.py b/utils/gyb_syntax_support/DeclNodes.py index c74da73a0bf47..aa71a82cb14e0 100644 --- a/utils/gyb_syntax_support/DeclNodes.py +++ b/utils/gyb_syntax_support/DeclNodes.py @@ -172,7 +172,7 @@ 'required', 'static', 'unowned', 'weak', 'private', 'fileprivate', 'internal', 'public', 'open', 'mutating', 'nonmutating', 'indirect', '__consuming', - 'actor', 'async' + 'actor', 'async', 'distributed' ]), Child('DetailLeftParen', kind='LeftParenToken', is_optional=True), Child('Detail', kind='IdentifierToken', is_optional=True), diff --git a/utils/gyb_syntax_support/ExprNodes.py b/utils/gyb_syntax_support/ExprNodes.py index f55a66bd91442..fdd4c54c184c1 100644 --- a/utils/gyb_syntax_support/ExprNodes.py +++ b/utils/gyb_syntax_support/ExprNodes.py @@ -385,6 +385,8 @@ Node('ClosureSignature', kind='Syntax', children=[ + Child('Attributes', kind='AttributeList', + collection_element_name='Attribute', is_optional=True), Child('Capture', kind='ClosureCaptureSignature', is_optional=True), Child('Input', kind='Syntax', is_optional=True, @@ -595,7 +597,7 @@ # postfix '#if' expession Node('PostfixIfConfigExpr', kind='Expr', children=[ - Child('Base', kind='Expr'), + Child('Base', kind='Expr', is_optional=True), Child('Config', kind='IfConfigDecl'), ]), diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 248e5aaa9d3c2..fc445e62a313d 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -252,6 +252,7 @@ 'PoundFileIDExpr': 247, 'TargetFunctionEntry': 248, 'PostfixIfConfigExpr': 250, + 'UnavailabilityCondition': 251, } diff --git a/utils/gyb_syntax_support/StmtNodes.py b/utils/gyb_syntax_support/StmtNodes.py index 974490bf15e09..349e664e42dea 100644 --- a/utils/gyb_syntax_support/StmtNodes.py +++ b/utils/gyb_syntax_support/StmtNodes.py @@ -198,6 +198,7 @@ node_choices=[ Child('Expression', kind='Expr'), Child('Availablity', kind='AvailabilityCondition'), + Child('Unavailablity', kind='UnavailabilityCondition'), Child('MatchingPattern', kind='MatchingPatternCondition'), Child('OptionalBinding', @@ -236,6 +237,16 @@ Child('Initializer', kind='InitializerClause'), ]), + # unavailability-condition -> '#unavailable' '(' availability-spec ')' + Node('UnavailabilityCondition', kind='Syntax', + children=[ + Child('PoundUnavailableKeyword', kind='PoundUnavailableToken'), + Child('LeftParen', kind='LeftParenToken'), + Child('AvailabilitySpec', kind='AvailabilitySpecList', + collection_element_name='AvailabilityArgument'), + Child('RightParen', kind='RightParenToken'), + ]), + # condition-list -> condition # | condition ','? condition-list Node('ConditionElementList', kind='SyntaxCollection', diff --git a/utils/gyb_syntax_support/Token.py b/utils/gyb_syntax_support/Token.py index 6738c5a3aea59..e5b4db6b1edee 100644 --- a/utils/gyb_syntax_support/Token.py +++ b/utils/gyb_syntax_support/Token.py @@ -308,6 +308,8 @@ def macro_name(self): PoundConfig('PoundAvailable', 'available', text='#available', serialization_code=60), + PoundConfig('PoundUnavailable', 'unavailable', text='#unavailable', + serialization_code=123), PoundObjectLiteral('PoundFileLiteral', 'fileLiteral', text='#fileLiteral', serialization_code=76, diff --git a/utils/gyb_syntax_support/kinds.py b/utils/gyb_syntax_support/kinds.py index edbf34da0a2e2..9fda26d5cd557 100644 --- a/utils/gyb_syntax_support/kinds.py +++ b/utils/gyb_syntax_support/kinds.py @@ -38,3 +38,27 @@ def lowercase_first_word(name): if word_index == 0: return name return name[:word_index].lower() + name[word_index:] + + +def syntax_buildable_child_type(type_name, syntax_kind, is_token, + is_optional=False): + if syntax_kind in SYNTAX_BASE_KINDS: + buildable_type = syntax_kind + 'Buildable' + elif not is_token: + buildable_type = syntax_kind + else: + buildable_type = type_name + + if is_optional: + buildable_type += '?' + + return buildable_type + + +def syntax_buildable_default_init_value(child, token): + if child.is_optional: + return " = nil" + elif token and token.text: + return " = TokenSyntax.`%s`" % lowercase_first_word(token.name) + else: + return "" diff --git a/utils/refactor-check-compiles.py b/utils/refactor-check-compiles.py index e9b62c3368198..7af422672e549 100755 --- a/utils/refactor-check-compiles.py +++ b/utils/refactor-check-compiles.py @@ -21,16 +21,21 @@ def parse_args(): formatter_class=argparse.RawDescriptionHelpFormatter, description=""" A drop-in replacement for a 'swift-refactor -dump-text' call that - 1. Checkes that the file still compiles after the refactoring by doing + 1. Checks that the file still compiles after the refactoring by doing 'swift-refactor -dump-rewritten' and feeding the result to - 'swift-frontend -typecheck' + 'swift-frontend -typecheck -disable-availability-checking + -warn-on-editor-placeholder' 2. Outputting the result of the 'swift-refactor -dump-text' call - All arguments other than the following will be forwarded to : + All arguments other than the following will be forwarded to 'swift-refactor': - swift-frontend - swift-refactor - temp-dir + - enable-experimental-concurrency (sent to both) + - I (sent to both) + - sdk (sent to both) + - target (sent to both) """) parser.add_argument( @@ -61,40 +66,64 @@ def parse_args(): '-pos', help='The position to invoke the refactoring at' ) + parser.add_argument( + '-enable-experimental-concurrency', + action='store_true', + help=''' + Whether to enable experimental concurrency in both swift-refactor and + swift-frontend + ''' + ) + parser.add_argument( + '-I', + action='append', + help='Add a directory to the import search path' + ) + parser.add_argument( + '-sdk', + help='Path to the SDK to build against' + ) + parser.add_argument( + '-target', + help='The target triple to build for' + ) return parser.parse_known_args() def main(): - (args, unknown_args) = parse_args() + (args, extra_refactor_args) = parse_args() temp_file_name = os.path.split(args.source_filename)[-1] + '.' + \ args.pos.replace(':', '.') temp_file_path = os.path.join(args.temp_dir, temp_file_name) - # FIXME: `refactor-check-compiles` should generate both `-dump-text` and - # `dump-rewritten` from a single `swift-refactor` invocation (SR-14587). + + extra_both_args = [] + if args.enable_experimental_concurrency: + extra_both_args.append('-enable-experimental-concurrency') + if args.I: + for path in args.I: + extra_both_args += ['-I', path] + if args.sdk: + extra_both_args += ['-sdk', args.sdk] + if args.target: + extra_both_args += ['-target', args.target] + dump_text_output = run_cmd([ args.swift_refactor, '-dump-text', '-source-filename', args.source_filename, + '-rewritten-output-file', temp_file_path, '-pos', args.pos - ] + unknown_args, desc='producing edit') - - dump_rewritten_output = run_cmd([ - args.swift_refactor, - '-dump-rewritten', - '-source-filename', args.source_filename, - '-pos', args.pos - ] + unknown_args, desc='producing rewritten file') - with open(temp_file_path, 'wb') as f: - f.write(dump_rewritten_output) + ] + extra_refactor_args + extra_both_args, desc='producing edit').decode("utf-8") + sys.stdout.write(dump_text_output) run_cmd([ args.swift_frontend, '-typecheck', temp_file_path, - '-disable-availability-checking' - ], desc='checking that rewritten file compiles') - sys.stdout.write(dump_text_output) + '-disable-availability-checking', + '-warn-on-editor-placeholder' + ] + extra_both_args, desc='checking that rewritten file compiles') if __name__ == '__main__': diff --git a/utils/run-test b/utils/run-test index 780383604c85e..9e1fafab51e0f 100755 --- a/utils/run-test +++ b/utils/run-test @@ -37,6 +37,7 @@ TEST_SUBSETS = [ 'only_validation', 'only_long', 'only_stress', + 'only_early_swiftdriver' ] SWIFT_SOURCE_DIR = os.path.join(SWIFT_SOURCE_ROOT, 'swift') diff --git a/utils/swift-api-dump.py b/utils/swift-api-dump.py index f0071b85bf9f0..d3114e3583f88 100755 --- a/utils/swift-api-dump.py +++ b/utils/swift-api-dump.py @@ -330,6 +330,8 @@ def main(): extra_args = ['-skip-imports'] if args.enable_experimental_concurrency: extra_args = extra_args + ['-enable-experimental-concurrency'] + if args.enable_experimental_distributed: + extra_args = extra_args + ['-enable-experimental-distributed'] if args.swift_version: extra_args = extra_args + ['-swift-version', '%s' % args.swift_version] diff --git a/utils/swift-darwin-postprocess.py b/utils/swift-darwin-postprocess.py new file mode 100755 index 0000000000000..4076137a2f782 --- /dev/null +++ b/utils/swift-darwin-postprocess.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +# Postprocess binaries to prepare for their execution on Darwin platforms. +# This includes un-rpathization and codesigning. + +import argparse +import os +import re +import subprocess +import sys + +utils = os.path.dirname(os.path.realpath(__file__)) + + +def main(arguments): + parser = argparse.ArgumentParser( + description='Postprocess binaries to prepare for \ + their execution on Darwin platforms') + parser.add_argument('bins', nargs='+', help='one or more binary files') + + args = parser.parse_args(arguments) + + for bin in args.bins: + unrpathize(bin) + codesign(bin) + + +# This function rewrites binaries that use these `@rpath`-based load +# commands to use direct /usr/lib/swift paths instead, to work around +# an issue where the DYLD_LIBRARY_PATH override doesn't reliably work +# with runpath-relative loads of system libraries on some dyld versions. +# (rdar://78851265) +def unrpathize(filename): + dylibsOutput = subprocess.check_output( + ['xcrun', 'dyldinfo', '-dylibs', filename]) + + # Do not rewrite @rpath-relative load commands for these libraries: + # they are test support libraries that are never installed under + # /usr/lib/swift and they aren't loaded via DYLD_LIBRARY_PATH. + allow_list = { + 'libswiftDifferentiationUnittest.dylib', + 'libswiftLocalizationAnalysisTestHelper.dylib', + 'libswiftOSLogTestHelper.dylib', + 'libswiftRemoteMirror.dylib', + 'libswiftRuntimeUnittest.dylib', + 'libswiftStdlibCollectionUnittest.dylib', + 'libswiftStdlibUnicodeUnittest.dylib', + 'libswiftStdlibUnittest.dylib', + 'libswiftStdlibUnittestFoundationExtras.dylib', + 'libswiftSwiftPrivate.dylib', + 'libswiftSwiftPrivateLibcExtras.dylib', + 'libswiftSwiftPrivateThreadExtras.dylib', + 'libswiftSwiftReflectionTest.dylib', + } + + # The output from dyldinfo -dylibs is a line of header followed by one + # install name per line, indented with spaces. + dylib_regex = re.compile( + r"(^|.*\s)(?P@rpath/(?Plibswift.*\.dylib))\s*$") + + # Build a command to invoke install_name_tool. + command = ['install_name_tool'] + for binaryline in dylibsOutput.splitlines(): + line = binaryline.decode("utf-8", "strict") + match = dylib_regex.match(line) + if match and match.group('filename') not in allow_list: + command.append('-change') + command.append(match.group('path')) + command.append('/usr/lib/swift/' + match.group('filename')) + continue + + # Don't run the command if we didn't find any dylibs to change: + # it's invalid to invoke install_name_tool without any operations. + if len(command) == 1: + return + + # The last argument is the filename to operate on. + command.append(filename) + + subprocess.check_call(command) + + +def codesign(filename): + # "-" is the signing identity for ad-hoc signing. + command = ["/usr/bin/codesign", "--force", "--sign", "-", filename] + subprocess.check_call(command) + + +sys.exit(main(sys.argv[1:])) diff --git a/utils/swift_build_support/swift_build_support/build_script_invocation.py b/utils/swift_build_support/swift_build_support/build_script_invocation.py new file mode 100644 index 0000000000000..72fe0a567a54f --- /dev/null +++ b/utils/swift_build_support/swift_build_support/build_script_invocation.py @@ -0,0 +1,794 @@ +# ===-- build_script_invocation.py ---------------------------------------===# +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 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 +# +# ===---------------------------------------------------------------------===# + +import os +import pipes +import platform + +from build_swift.build_swift import argparse +from build_swift.build_swift.constants import BUILD_SCRIPT_IMPL_PATH +from build_swift.build_swift.constants import SWIFT_BUILD_ROOT +from build_swift.build_swift.constants import SWIFT_REPO_NAME +from build_swift.build_swift.constants import SWIFT_SOURCE_ROOT + +import six + +from swift_build_support.swift_build_support import products +from swift_build_support.swift_build_support import shell +from swift_build_support.swift_build_support import targets +from swift_build_support.swift_build_support import workspace +from swift_build_support.swift_build_support.cmake import CMake +from swift_build_support.swift_build_support.host_specific_configuration \ + import HostSpecificConfiguration +from swift_build_support.swift_build_support.productpipeline_list_builder \ + import ProductPipelineListBuilder +from swift_build_support.swift_build_support.targets \ + import StdlibDeploymentTarget +from swift_build_support.swift_build_support.utils \ + import exit_rejecting_arguments +from swift_build_support.swift_build_support.utils import fatal_error + + +class BuildScriptInvocation(object): + """Represent a single build script invocation. + """ + + def __init__(self, toolchain, args): + self.toolchain = toolchain + self.args = args + + self.workspace = workspace.Workspace( + source_root=SWIFT_SOURCE_ROOT, + build_root=os.path.join(SWIFT_BUILD_ROOT, args.build_subdir)) + + self.build_libparser_only = args.build_libparser_only + + @property + def install_all(self): + return self.args.install_all or self.args.infer_dependencies + + def build_ninja(self): + if not os.path.exists(self.workspace.source_dir("ninja")): + fatal_error( + "can't find source directory for ninja " + "(tried %s)" % (self.workspace.source_dir("ninja"))) + + ninja_build = products.Ninja.new_builder( + args=self.args, + toolchain=self.toolchain, + workspace=self.workspace, + host=StdlibDeploymentTarget.get_target_for_name( + self.args.host_target)) + ninja_build.build() + self.toolchain.ninja = ninja_build.ninja_bin_path + + def convert_to_impl_arguments(self): + """convert_to_impl_arguments() -> (env, args) + + Convert the invocation to an environment and list of arguments suitable + for invoking `build-script-impl`. + """ + + # Create local shadows, for convenience. + args = self.args + toolchain = self.toolchain + + cmake = CMake(args=args, + toolchain=self.toolchain) + + impl_args = [ + "--workspace", self.workspace.source_root, + "--build-dir", self.workspace.build_root, + "--install-prefix", args.install_prefix, + "--host-target", args.host_target, + "--stdlib-deployment-targets={}".format( + " ".join(args.stdlib_deployment_targets)), + "--host-cc", toolchain.cc, + "--host-cxx", toolchain.cxx, + "--darwin-xcrun-toolchain", args.darwin_xcrun_toolchain, + "--darwin-deployment-version-osx=%s" % ( + args.darwin_deployment_version_osx), + "--darwin-deployment-version-ios=%s" % ( + args.darwin_deployment_version_ios), + "--darwin-deployment-version-tvos=%s" % ( + args.darwin_deployment_version_tvos), + "--darwin-deployment-version-watchos=%s" % ( + args.darwin_deployment_version_watchos), + "--cmake", toolchain.cmake, + "--cmark-build-type", args.cmark_build_variant, + "--llvm-build-type", args.llvm_build_variant, + "--swift-build-type", args.swift_build_variant, + "--swift-stdlib-build-type", args.swift_stdlib_build_variant, + "--lldb-build-type", args.lldb_build_variant, + "--foundation-build-type", args.foundation_build_variant, + "--libdispatch-build-type", args.libdispatch_build_variant, + "--libicu-build-type", args.libicu_build_variant, + "--xctest-build-type", args.build_variant, + "--llbuild-build-type", args.build_variant, + "--swift-enable-assertions", str(args.swift_assertions).lower(), + "--swift-stdlib-enable-assertions", str( + args.swift_stdlib_assertions).lower(), + "--swift-analyze-code-coverage", str( + args.swift_analyze_code_coverage).lower(), + "--llbuild-enable-assertions", str( + args.llbuild_assertions).lower(), + "--lldb-assertions", str( + args.lldb_assertions).lower(), + "--cmake-generator", args.cmake_generator, + "--build-jobs", str(args.build_jobs), + "--common-cmake-options=%s" % ' '.join( + pipes.quote(opt) for opt in cmake.common_options()), + "--build-args=%s" % ' '.join( + pipes.quote(arg) for arg in cmake.build_args()), + "--dsymutil-jobs", str(args.dsymutil_jobs), + ] + + # Compute any product specific cmake arguments. + # + # NOTE: The sum(list(...)) is b/c compute_product_pipelines returns a + # tuple of lists of which the first is the build-script-impl products + # and the second is the non-build-script-impl-products. It guarantees + # that when we concatenate these two lists together we get a valid + # dependency graph. + for product_class in sum(list(self.compute_product_pipelines()[0]), []): + if not product_class.is_build_script_impl_product(): + continue + + product_name = product_class.product_name() + product_source_name = product_class.product_source_name() + source_dir = self.workspace.source_dir(product_source_name) + + if not os.path.exists(source_dir): + fatal_error( + "can't find source directory for %s " + "(tried %s)" % (product_name, source_dir)) + + product = product_class( + args=args, + toolchain=self.toolchain, + source_dir=source_dir, + # FIXME: This is incorrect since it always assumes the host + # target I think? + build_dir=self.workspace.build_dir( + args.host_target, product_name)) + cmake_opts = product.cmake_options + + # FIXME: We should be using pipes.quote here but we run into issues + # with build-script-impl/cmake not being happy with all of the + # extra "'" in the strings. To fix this easily, we really need to + # just invoke cmake from build-script directly rather than futzing + # with build-script-impl. This makes even more sense since there + # really isn't a security issue here. + if cmake_opts: + impl_args += [ + "--{}-cmake-options={}".format( + product_name, ' '.join(cmake_opts)) + ] + + if args.build_stdlib_deployment_targets: + impl_args += [ + "--build-stdlib-deployment-targets", " ".join( + args.build_stdlib_deployment_targets)] + if args.cross_compile_hosts: + impl_args += [ + "--cross-compile-hosts", " ".join(args.cross_compile_hosts)] + + if args.test_paths: + impl_args += ["--test-paths", " ".join(args.test_paths)] + + if toolchain.ninja: + impl_args += ["--ninja-bin=%s" % toolchain.ninja] + if args.distcc: + impl_args += [ + "--distcc", + "--distcc-pump=%s" % toolchain.distcc_pump + ] + if args.sccache: + args.cmake_c_launcher = toolchain.sccache + args.cmake_cxx_launcher = toolchain.sccache + + # *NOTE* We use normal cmake to pass through tsan/ubsan options. We do + # NOT pass them to build-script-impl. + if args.enable_asan: + impl_args += ["--enable-asan"] + # If we are on linux, disable leak detection when running ASAN. We + # have a separate bot that checks for leaks. + if platform.system() == 'Linux': + os.environ['ASAN_OPTIONS'] = 'detect_leaks=0' + if args.enable_ubsan: + impl_args += ["--enable-ubsan"] + + # If we have lsan, we need to export our suppression list. The actual + # passing in of the LSAN flag is done via the normal cmake method. We + # do not pass the flag to build-script-impl. + if args.enable_lsan: + supp_file = os.path.join(SWIFT_SOURCE_ROOT, SWIFT_REPO_NAME, + "utils", + "lsan_leaks_suppression_list.txt") + os.environ['LSAN_OPTIONS'] = 'suppressions={}'.format(supp_file) + if args.verbose_build: + impl_args += ["--verbose-build"] + if args.install_symroot: + impl_args += [ + "--install-symroot", os.path.abspath(args.install_symroot) + ] + if args.install_destdir: + impl_args += [ + "--install-destdir", os.path.abspath(args.install_destdir) + ] + + if args.skip_build: + impl_args += ["--skip-build"] + if not args.build_benchmarks: + impl_args += ["--skip-build-benchmarks"] + + if args.swift_disable_dead_stripping: + args.extra_cmake_options.append('-DSWIFT_DISABLE_DEAD_STRIPPING:BOOL=TRUE') + if args.build_backdeployconcurrency: + args.extra_cmake_options.append( + '-DSWIFT_BACK_DEPLOY_CONCURRENCY:BOOL=TRUE') + + # Then add subproject install flags that either skip building them /or/ + # if we are going to build them and install_all is set, we also install + # them. + conditional_subproject_configs = [ + (args.build_cmark, "cmark"), + (args.build_llvm, "llvm"), + (args.build_swift, "swift"), + (args.build_foundation, "foundation"), + (args.build_xctest, "xctest"), + (args.build_lldb, "lldb"), + (args.build_llbuild, "llbuild"), + (args.build_libcxx, "libcxx"), + (args.build_libdispatch, "libdispatch"), + (args.build_libicu, "libicu") + ] + for (should_build, string_name) in conditional_subproject_configs: + if not should_build and not self.args.infer_dependencies: + impl_args += ["--skip-build-{}".format(string_name)] + elif self.install_all: + impl_args += ["--install-{}".format(string_name)] + + if args.build_swift_dynamic_stdlib: + impl_args += ["--build-swift-dynamic-stdlib"] + if args.build_swift_static_stdlib: + impl_args += ["--build-swift-static-stdlib"] + if args.build_swift_stdlib_unittest_extra: + impl_args += ["--build-swift-stdlib-unittest-extra"] + if args.build_swift_dynamic_sdk_overlay: + impl_args += ["--build-swift-dynamic-sdk-overlay"] + if args.build_swift_static_sdk_overlay: + impl_args += ["--build-swift-static-sdk-overlay"] + + if not args.build_android: + impl_args += ["--skip-build-android"] + if not args.build_clang_tools_extra: + impl_args += ["--skip-build-clang-tools-extra"] + + if not args.test and not args.long_test and not args.stress_test: + impl_args += ["--skip-test-swift"] + if not args.test: + impl_args += [ + "--skip-test-cmark", + "--skip-test-lldb", + "--skip-test-llbuild", + "--skip-test-xctest", + "--skip-test-foundation", + "--skip-test-libdispatch", + "--skip-test-libicu", + ] + if args.build_runtime_with_host_compiler: + impl_args += ["--build-runtime-with-host-compiler"] + if args.validation_test: + impl_args += ["--validation-test"] + if args.long_test: + impl_args += ["--long-test"] + if args.stress_test: + impl_args += ["--stress-test"] + if args.skip_local_build: + impl_args += ["--skip-local-build"] + if args.only_executable_test: + impl_args += ["--only-executable-test"] + if not args.benchmark: + impl_args += ["--skip-test-benchmarks"] + if args.build_libparser_only: + impl_args += ["--build-libparser-only"] + if args.android: + impl_args += [ + "--android-arch", args.android_arch, + "--android-ndk", args.android_ndk, + "--android-api-level", args.android_api_level, + "--android-ndk-gcc-version", args.android_ndk_gcc_version, + "--android-icu-uc", args.android_icu_uc, + "--android-icu-uc-include", args.android_icu_uc_include, + "--android-icu-i18n", args.android_icu_i18n, + "--android-icu-i18n-include", args.android_icu_i18n_include, + "--android-icu-data", args.android_icu_data, + ] + # If building natively on an Android host, only pass the API level. + if StdlibDeploymentTarget.Android.contains(StdlibDeploymentTarget + .host_target().name): + impl_args += ["--android-api-level", args.android_api_level] + if args.android_deploy_device_path: + impl_args += [ + "--android-deploy-device-path", + args.android_deploy_device_path, + ] + + if platform.system() == 'Darwin': + impl_args += [ + "--toolchain-prefix", + targets.darwin_toolchain_prefix( + args.install_prefix), + "--host-lipo", toolchain.lipo, + ] + + # Isolate build from the system; Darwin toolchains build against SDKs. + # For additional isolation, disable pkg-config. Homebrew's pkg-config + # prioritizes CommandLineTools paths, resulting in compile errors. + args.extra_cmake_options += [ + '-DCMAKE_IGNORE_PATH=/usr/lib;/usr/local/lib;/lib', + '-DPKG_CONFIG_EXECUTABLE=/usr/bin/false', + ] + + if toolchain.libtool is not None: + impl_args += [ + "--host-libtool", toolchain.libtool, + ] + if args.native_clang_tools_path is not None: + impl_args += [ + "--native-clang-tools-path=%s" % args.native_clang_tools_path + ] + if args.native_llvm_tools_path is not None: + impl_args += [ + "--native-llvm-tools-path=%s" % args.native_llvm_tools_path + ] + if args.native_swift_tools_path is not None: + impl_args += [ + "--native-swift-tools-path=%s" % args.native_swift_tools_path + ] + + # If we have extra_swift_args, combine all of them together and then + # add them as one command. + if args.extra_swift_args: + impl_args += [ + "--extra-swift-args=%s" % ';'.join(args.extra_swift_args) + ] + + # Enable macCatalyst + if args.maccatalyst: + (args.extra_cmake_options + .append('-DSWIFT_ENABLE_MACCATALYST:BOOL=TRUE')) + if args.maccatalyst_ios_tests: + impl_args += ["--darwin-test-maccatalyst-ios-like=1"] + + # If we have extra_cmake_options, combine all of them together and then + # add them as one command. + if args.extra_cmake_options: + impl_args += [ + "--extra-cmake-options=%s" % ' '.join( + pipes.quote(opt) for opt in args.extra_cmake_options) + ] + + if args.lto_type is not None: + impl_args += [ + "--llvm-enable-lto=%s" % args.lto_type, + "--swift-tools-enable-lto=%s" % args.lto_type + ] + if args.llvm_max_parallel_lto_link_jobs is not None: + impl_args += [ + "--llvm-num-parallel-lto-link-jobs=%s" % + min(args.llvm_max_parallel_lto_link_jobs, args.build_jobs) + ] + if args.swift_tools_max_parallel_lto_link_jobs is not None: + impl_args += [ + "--swift-tools-num-parallel-lto-link-jobs=%s" % + min(args.swift_tools_max_parallel_lto_link_jobs, + args.build_jobs) + ] + + impl_args += args.build_script_impl_args + + if args.dry_run: + impl_args += ["--dry-run"] + + if args.reconfigure: + impl_args += ["--reconfigure"] + + if args.clang_profile_instr_use: + impl_args += [ + "--clang-profile-instr-use=%s" % + os.path.abspath(args.clang_profile_instr_use) + ] + + if args.lit_args: + impl_args += ["--llvm-lit-args=%s" % args.lit_args] + + if args.coverage_db: + impl_args += [ + "--coverage-db=%s" % + os.path.abspath(args.coverage_db) + ] + + if args.llvm_install_components: + impl_args += [ + "--llvm-install-components=%s" % args.llvm_install_components + ] + + if not args.clean_libdispatch: + impl_args += [ + "--skip-clean-libdispatch" + ] + + if not args.clean_foundation: + impl_args += [ + "--skip-clean-foundation" + ] + + if not args.clean_xctest: + impl_args += [ + "--skip-clean-xctest" + ] + + if not args.clean_llbuild: + impl_args += [ + "--skip-clean-llbuild" + ] + + if args.llvm_ninja_targets: + impl_args += [ + "--llvm-ninja-targets=%s" % ' '.join(args.llvm_ninja_targets) + ] + + if args.llvm_ninja_targets_for_cross_compile_hosts: + impl_args += [ + "--llvm-ninja-targets-for-cross-compile-hosts=%s" % + ' '.join(args.llvm_ninja_targets_for_cross_compile_hosts) + ] + + if args.darwin_symroot_path_filters: + impl_args += [ + "--darwin_symroot_path_filters=%s" % + ' '.join(args.darwin_symroot_path_filters) + ] + + # Compute the set of host-specific variables, which we pass through to + # the build script via environment variables. + host_specific_variables = self.compute_host_specific_variables() + impl_env = {} + for (host_target, options) in host_specific_variables.items(): + for (name, value) in options.items(): + # We mangle into an environment variable we can easily evaluate + # from the `build-script-impl`. + impl_env["HOST_VARIABLE_{}__{}".format( + host_target.replace("-", "_"), name)] = value + + return (impl_env, impl_args) + + def compute_host_specific_variables(self): + """compute_host_specific_variables(args) -> dict + + Compute the host-specific options, organized as a dictionary keyed by + host of options. + """ + + args = self.args + args.build_root = self.workspace.build_root + + options = {} + for host_target in [args.host_target] + args.cross_compile_hosts: + # Compute the host specific configuration. + try: + config = HostSpecificConfiguration(host_target, args) + except argparse.ArgumentError as e: + exit_rejecting_arguments(six.text_type(e)) + + # Convert into `build-script-impl` style variables. + options[host_target] = { + "SWIFT_SDKS": " ".join(sorted( + config.sdks_to_configure)), + "SWIFT_STDLIB_TARGETS": " ".join( + config.swift_stdlib_build_targets), + "SWIFT_BENCHMARK_TARGETS": " ".join( + config.swift_benchmark_build_targets), + "SWIFT_RUN_BENCHMARK_TARGETS": " ".join( + config.swift_benchmark_run_targets), + "SWIFT_TEST_TARGETS": " ".join( + config.swift_test_run_targets), + "SWIFT_FLAGS": config.swift_flags, + "SWIFT_TARGET_CMAKE_OPTIONS": config.cmake_options, + } + + return options + + def compute_product_pipelines(self): + """compute_product_pipelines() -> [[Product]] + + A list of lists of products. + + Compute lists of product pipelines that we should run. It is guaranteed + that all product pipeline lists consist of solely build-script-impl + products or build-script products. So one can always check the first + element to know if a pipeline returned from the builder is an impl + product or not. + """ + builder = ProductPipelineListBuilder(self.args) + + builder.begin_pipeline() + # If --skip-early-swift-driver is passed in, swift will be built + # as usual, but relying on its own C++-based (Legacy) driver. + # Otherwise, we build an "early" swift-driver using the host + # toolchain, which the later-built compiler will forward + # `swiftc` invocations to. That is, if we find a Swift compiler + # in the host toolchain. If the host toolchain is not equpipped with + # a Swift compiler, a warning is emitted. In the future, it may become + # mandatory that the host toolchain come with its own `swiftc`. + builder.add_product(products.EarlySwiftDriver, + is_enabled=self.args.build_early_swift_driver) + + builder.add_product(products.CMark, + is_enabled=self.args.build_cmark) + + # Begin a build-script-impl pipeline for handling the compiler toolchain + # and a subset of the tools that we build. We build these in this manner + # to preserve current build-script-impl run behavior as we transition + # the build-script code base. The main difference is that these are all + # build, tested, and installed all at once instead of performing build, + # test, install like a normal build-script product. + builder.begin_impl_pipeline(should_run_epilogue_operations=False) + + # If --skip-build-llvm is passed in, LLVM cannot be completely disabled, as + # Swift still needs a few LLVM targets like tblgen to be built for it to be + # configured. Instead, handle this in build-script-impl for now. + builder.add_impl_product(products.LLVM, + is_enabled=True) + builder.add_impl_product(products.LibCXX, + is_enabled=self.args.build_libcxx) + builder.add_impl_product(products.LibICU, + is_enabled=self.args.build_libicu) + builder.add_impl_product(products.Swift, + is_enabled=self.args.build_swift) + builder.add_impl_product(products.LLDB, + is_enabled=self.args.build_lldb) + + # Begin a new build-script-impl pipeline that builds libraries that we + # build as part of build-script-impl but that we should eventually move + # onto build-script products. + builder.begin_impl_pipeline(should_run_epilogue_operations=True) + builder.add_impl_product(products.LibDispatch, + is_enabled=self.args.build_libdispatch) + builder.add_impl_product(products.Foundation, + is_enabled=self.args.build_foundation) + builder.add_impl_product(products.XCTest, + is_enabled=self.args.build_xctest) + builder.add_impl_product(products.LLBuild, + is_enabled=self.args.build_llbuild) + + # Begin the post build-script-impl build phase. + builder.begin_pipeline() + + builder.add_product(products.BackDeployConcurrency, + is_enabled=self.args.build_backdeployconcurrency) + builder.add_product(products.SwiftPM, + is_enabled=self.args.build_swiftpm) + builder.add_product(products.SwiftSyntax, + is_enabled=self.args.build_swiftsyntax) + builder.add_product(products.SKStressTester, + is_enabled=self.args.build_skstresstester) + builder.add_product(products.SwiftFormat, + is_enabled=self.args.build_swiftformat) + builder.add_product(products.SwiftEvolve, + is_enabled=self.args.build_swiftevolve) + builder.add_product(products.IndexStoreDB, + is_enabled=self.args.build_indexstoredb) + builder.add_product(products.PlaygroundSupport, + is_enabled=self.args.build_playgroundsupport) + builder.add_product(products.SourceKitLSP, + is_enabled=self.args.build_sourcekitlsp) + builder.add_product(products.Benchmarks, + is_enabled=self.args.build_toolchainbenchmarks) + builder.add_product(products.SwiftInspect, + is_enabled=self.args.build_swift_inspect) + builder.add_product(products.TSanLibDispatch, + is_enabled=self.args.tsan_libdispatch_test) + + # Keep SwiftDriver at last. + # swift-driver's integration with the build scripts is not fully + # supported. Using swift-driver to build these products may hit + # failures. + builder.add_product(products.SwiftDriver, + is_enabled=self.args.build_swift_driver + or self.args.install_swift_driver) + + # Now that we have constructed our pass pipelines using our builder, get + # the final schedule and finalize the builder. + return builder.finalize(shouldInfer=self.args.infer_dependencies) + + def execute(self): + """Execute the invocation with the configured arguments.""" + + # Convert to a build-script-impl invocation. + (self.impl_env, self.impl_args) = self.convert_to_impl_arguments() + + # If using the legacy implementation, delegate all behavior to + # `build-script-impl`. + if self.args.legacy_impl: + # Execute the underlying build script implementation. + shell.call_without_sleeping([BUILD_SCRIPT_IMPL_PATH] + self.impl_args, + env=self.impl_env, echo=True) + return + + # Otherwise, we compute and execute the individual actions ourselves. + # Compute the list of hosts to operate on. + all_host_names = [ + self.args.host_target] + self.args.cross_compile_hosts + all_hosts = [StdlibDeploymentTarget.get_target_for_name(name) + for name in all_host_names] + + # Compute the list of lists of product classes to operate on. + # + # FIXME: This should really be per-host, but the current structure + # matches that of `build-script-impl`. + (product_pipelines, last_impl_index) = self.compute_product_pipelines() + + # Execute each "product pipeline". + for index in range(len(product_pipelines)): + perform_epilogue_opts = last_impl_index == index + pipeline = product_pipelines[index] + + # Skip empty pipelines. + if len(pipeline) == 0: + if perform_epilogue_opts: + self._execute_merged_host_lipo_core_action() + continue + + is_impl = pipeline[0].is_build_script_impl_product() + if is_impl: + self._execute_impl(pipeline, all_hosts, perform_epilogue_opts) + else: + assert(index != last_impl_index) + # Once we have performed our last impl pipeline, we no longer + # support cross compilation. + # + # This just maintains current behavior. + if index > last_impl_index: + self._execute(pipeline, [self.args.host_target]) + else: + self._execute(pipeline, all_host_names) + + # And then perform the rest of the non-core epilogue actions. + + # Extract symbols... + for host_target in all_hosts: + self._execute_extract_symbols_action(host_target) + + # Package... + for host_target in all_hosts: + self._execute_package_action(host_target) + + # Lipo... + self._execute_merged_host_lipo_action() + + def _execute_impl(self, pipeline, all_hosts, should_run_epilogue_operations): + # Build... + for host_target in all_hosts: + # FIXME: We should only compute these once. + try: + config = HostSpecificConfiguration(host_target.name, self.args) + except argparse.ArgumentError as e: + exit_rejecting_arguments(six.text_type(e)) + print("Building the standard library for: {}".format( + " ".join(config.swift_stdlib_build_targets))) + if config.swift_test_run_targets and ( + self.args.test or self.args.long_test): + print("Running Swift tests for: {}".format( + " ".join(config.swift_test_run_targets))) + if config.swift_benchmark_run_targets and self.args.benchmark: + print("Running Swift benchmarks for: {}".format( + " ".join(config.swift_benchmark_run_targets))) + + for product_class in pipeline: + self._execute_build_action(host_target, product_class) + + # Test... + for host_target in all_hosts: + for product_class in pipeline: + self._execute_test_action(host_target, product_class) + + # Install... + for host_target in all_hosts: + for product_class in pipeline: + self._execute_install_action(host_target, product_class) + + # And then we may be asked to perform several post-processing operations + # on what we have built. If we are not supposed to do so, bail now. + if not should_run_epilogue_operations: + return + + # Core Lipo... + self._execute_merged_host_lipo_core_action() + + def _execute(self, pipeline, all_host_names): + for host_target in all_host_names: + for product_class in pipeline: + # Execute clean, build, test, install + self.execute_product_build_steps(product_class, host_target) + + def _execute_build_action(self, host_target, product_class): + action_name = "{}-{}-build".format(host_target.name, + product_class.product_name()) + self._execute_action(action_name) + + def _execute_test_action(self, host_target, product_class): + action_name = "{}-{}-test".format(host_target.name, + product_class.product_name()) + self._execute_action(action_name) + + def _execute_install_action(self, host_target, product_class): + action_name = "{}-{}-install".format(host_target.name, + product_class.product_name()) + self._execute_action(action_name) + + def _execute_extract_symbols_action(self, host_target): + action_name = "{}-extractsymbols".format(host_target.name) + self._execute_action(action_name) + + def _execute_package_action(self, host_target): + action_name = "{}-package".format(host_target.name) + self._execute_action(action_name) + + def _execute_merged_host_lipo_action(self): + self._execute_action("merged-hosts-lipo") + + def _execute_merged_host_lipo_core_action(self): + self._execute_action("merged-hosts-lipo-core") + + def _execute_action(self, action_name): + shell.call_without_sleeping( + [BUILD_SCRIPT_IMPL_PATH] + self.impl_args + + ["--only-execute", action_name], + env=self.impl_env, echo=self.args.verbose_build) + + def execute_product_build_steps(self, product_class, host_target): + product_source = product_class.product_source_name() + product_name = product_class.product_name() + if product_class.is_swiftpm_unified_build_product(): + build_dir = self.workspace.swiftpm_unified_build_dir( + host_target) + else: + build_dir = self.workspace.build_dir( + host_target, product_name) + product = product_class( + args=self.args, + toolchain=self.toolchain, + source_dir=self.workspace.source_dir(product_source), + build_dir=build_dir) + if product.should_clean(host_target): + print("--- Cleaning %s ---" % product_name) + product.clean(host_target) + if product.should_build(host_target): + print("--- Building %s ---" % product_name) + product.build(host_target) + if product.should_test(host_target): + print("--- Running tests for %s ---" % product_name) + product.test(host_target) + print("--- Finished tests for %s ---" % product_name) + # Install the product if it should be installed specifically, or + # if it should be built and `install_all` is set to True. + # The exception is select before_build_script_impl products + # which set `is_ignore_install_all_product` to True, ensuring + # they are never installed. (e.g. earlySwiftDriver). + if product.should_install(host_target) or \ + (self.install_all and product.should_build(host_target) and + not product.is_ignore_install_all_product()): + print("--- Installing %s ---" % product_name) + product.install(host_target) diff --git a/utils/swift_build_support/swift_build_support/cmake.py b/utils/swift_build_support/swift_build_support/cmake.py index dd023fa946c21..69f8e110850e5 100644 --- a/utils/swift_build_support/swift_build_support/cmake.py +++ b/utils/swift_build_support/swift_build_support/cmake.py @@ -90,11 +90,15 @@ def __iadd__(self, other): class CMake(object): - def __init__(self, args, toolchain): + def __init__(self, args, toolchain, prefer_just_built_toolchain=False): + """If prefer_just_built_toolchain is set to True, we set the clang, clang++, + and Swift compilers from the installed toolchain. + """ self.args = args self.toolchain = toolchain + self.prefer_just_built_toolchain = prefer_just_built_toolchain - def common_options(self): + def common_options(self, product=None): """Return options used for all products, including LLVM/Clang """ args = self.args @@ -135,9 +139,21 @@ def common_options(self): if args.cmake_cxx_launcher: define("CMAKE_CXX_COMPILER_LAUNCHER:PATH", args.cmake_cxx_launcher) - define("CMAKE_C_COMPILER:PATH", toolchain.cc) - define("CMAKE_CXX_COMPILER:PATH", toolchain.cxx) + if self.prefer_just_built_toolchain and product: + toolchain_path = product.install_toolchain_path(args.host_target) + define("CMAKE_C_COMPILER:PATH", os.path.join(toolchain_path, + 'bin', 'clang')) + define("CMAKE_CXX_COMPILER:PATH", os.path.join(toolchain_path, + 'bin', 'clang++')) + define("CMAKE_Swift_COMPILER:PATH", os.path.join(toolchain_path, + 'bin', 'swiftc')) + else: + define("CMAKE_C_COMPILER:PATH", toolchain.cc) + define("CMAKE_CXX_COMPILER:PATH", toolchain.cxx) + define("CMAKE_Swift_COMPILER:PATH", toolchain.swiftc) define("CMAKE_LIBTOOL:PATH", toolchain.libtool) + define("CMAKE_AR:PATH", toolchain.ar) + define("CMAKE_RANLIB:PATH", toolchain.ranlib) if args.cmake_generator == 'Xcode': define("CMAKE_CONFIGURATION_TYPES", diff --git a/utils/swift_build_support/swift_build_support/compiler_stage.py b/utils/swift_build_support/swift_build_support/compiler_stage.py new file mode 100644 index 0000000000000..3c459fa834fb4 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/compiler_stage.py @@ -0,0 +1,43 @@ +# ===--- compiler_stage.py -----------------------------------------------===# +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 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 +# +# ===---------------------------------------------------------------------===# + +class StageArgs(object): + def __init__(self, stage, args): + self.__dict__['postfix'] = stage.postfix + self.__dict__['stage'] = stage + self.__dict__['args'] = args + assert(not isinstance(self.args, StageArgs)) + + def _get_stage_prefix(self): + return self.__dict__['postfix'] + + def __getattr__(self, key): + real_key = '{}{}'.format(key, self._get_stage_prefix()) + args = self.__dict__['args'] + if not hasattr(args, real_key): + return None + return getattr(args, real_key) + + def __setattr__(self, key, value): + real_key = '{}{}'.format(key, self._get_stage_prefix()) + args = self.__dict__['args'] + setattr(args, real_key, value) + + +class Stage(object): + def __init__(self, identifier, postfix=""): + self.identifier = identifier + self.postfix = postfix + + +STAGE_1 = Stage(1, "") +STAGE_2 = Stage(2, "_stage2") diff --git a/utils/swift_build_support/swift_build_support/host_specific_configuration.py b/utils/swift_build_support/swift_build_support/host_specific_configuration.py index 55079d51a7860..fc037d6fb44ae 100644 --- a/utils/swift_build_support/swift_build_support/host_specific_configuration.py +++ b/utils/swift_build_support/swift_build_support/host_specific_configuration.py @@ -14,43 +14,49 @@ import sys from argparse import ArgumentError +from . import compiler_stage from .targets import StdlibDeploymentTarget class HostSpecificConfiguration(object): - """Configuration information for an individual host.""" - def __init__(self, host_target, args): + def __init__(self, host_target, args, stage_dependent_args=None): """Initialize for the given `host_target`.""" + # If we were not passed a stage_dependent_args object, then we do not need + # to make a distinction in between them and can just use args. + if not isinstance(args, compiler_stage.StageArgs): + args = compiler_stage.StageArgs(compiler_stage.STAGE_1, args) + if stage_dependent_args is None: + stage_dependent_args = args # Compute the set of deployment targets to configure/build. - if host_target == args.host_target: + if host_target == stage_dependent_args.host_target: # This host is the user's desired product, so honor the requested # set of targets to configure/build. - stdlib_targets_to_configure = args.stdlib_deployment_targets - if "all" in args.build_stdlib_deployment_targets: + stdlib_targets_to_configure = stage_dependent_args.stdlib_deployment_targets + if "all" in stage_dependent_args.build_stdlib_deployment_targets: stdlib_targets_to_build = set(stdlib_targets_to_configure) else: stdlib_targets_to_build = set( - args.build_stdlib_deployment_targets).intersection( - set(args.stdlib_deployment_targets)) + stage_dependent_args.build_stdlib_deployment_targets).intersection( + set(stage_dependent_args.stdlib_deployment_targets)) else: # Otherwise, this is a host we are building as part of # cross-compiling, so we only need the target itself. stdlib_targets_to_configure = [host_target] - if (hasattr(args, 'stdlib_deployment_targets')): + if stage_dependent_args.stdlib_deployment_targets: # there are some build configs that expect # not to be building the stdlib for the target # since it will be provided by different means stdlib_targets_to_build = set( stdlib_targets_to_configure).intersection( - set(args.stdlib_deployment_targets)) + set(stage_dependent_args.stdlib_deployment_targets)) else: stdlib_targets_to_build = set(stdlib_targets_to_configure) - if (hasattr(args, 'stdlib_deployment_targets') and - args.stdlib_deployment_targets == []): + if hasattr(stage_dependent_args, 'stdlib_deployment_targets') and \ + stage_dependent_args.stdlib_deployment_targets == []: stdlib_targets_to_configure = [] stdlib_targets_to_build = [] @@ -59,11 +65,15 @@ def __init__(self, host_target, args): # FIXME: We should move the platform-derived arguments to be entirely # data driven, so that we can eliminate this code duplication and just # iterate over all supported platforms. - platforms_to_skip_build = self.__platforms_to_skip_build(args) - platforms_to_skip_test = self.__platforms_to_skip_test(args) + platforms_to_skip_build = \ + self.__platforms_to_skip_build(args, stage_dependent_args) + platforms_to_skip_test = \ + self.__platforms_to_skip_test(args, stage_dependent_args) platforms_archs_to_skip_test = \ - self.__platforms_archs_to_skip_test(args, host_target) - platforms_to_skip_test_host = self.__platforms_to_skip_test_host(args) + self.__platforms_archs_to_skip_test(args, stage_dependent_args, + host_target) + platforms_to_skip_test_host = \ + self.__platforms_to_skip_test_host(args, stage_dependent_args) # Compute the lists of **CMake** targets for each use case (configure # vs. build vs. run) and the SDKs to configure with. @@ -72,6 +82,8 @@ def __init__(self, host_target, args): self.swift_test_run_targets = [] self.swift_benchmark_build_targets = [] self.swift_benchmark_run_targets = [] + self.swift_flags = '' + self.cmake_options = '' for deployment_target_name in stdlib_targets_to_configure: # Get the target object. deployment_target = StdlibDeploymentTarget.get_target_for_name( @@ -123,7 +135,10 @@ def __init__(self, host_target, args): # Validation, long, and stress tests require building the full # standard library, whereas the other targets can build a # slightly smaller subset which is faster to build. - if args.build_swift_stdlib_unittest_extra or \ + # + # NOTE: We currently do not seperate testing options for + # stage1/stage2 compiler. This can change with time. + if stage_dependent_args.build_swift_stdlib_unittest_extra or \ args.validation_test or args.long_test or \ args.stress_test: self.swift_stdlib_build_targets.append( @@ -167,6 +182,14 @@ def __init__(self, host_target, args): else: subset_suffix = "" + # If the compiler is being tested after being built to use the + # standalone swift-driver, we build a test-target to + # run a reduced set of lit-tests that verify the early swift-driver. + if args.test_early_swift_driver and\ + not test_host_only: + self.swift_test_run_targets.append( + "check-swift-only_early_swiftdriver-{}".format(name)) + # Support for running the macCatalyst tests with # the iOS-like target triple. macosx_platform_match = re.search("macosx-(.*)", name) @@ -180,6 +203,7 @@ def __init__(self, host_target, args): (self.swift_test_run_targets .append("check-swift{}{}-{}".format( subset_suffix, suffix, name))) + if args.test_optimized and not test_host_only: self.swift_test_run_targets.append( "check-swift{}-optimize-{}".format( @@ -194,80 +218,92 @@ def __init__(self, host_target, args): "check-swift{}-optimize_none_with_implicit_dynamic-{}" .format(subset_suffix, name)) - def __platforms_to_skip_build(self, args): + # Only pull in these flags when cross-compiling with + # --cross-compile-hosts. + if deployment_target_name != args.host_target and \ + host_target != args.host_target: + self.add_flags_for_cross_compilation(args, deployment_target) + + def add_flags_for_cross_compilation(self, args, deployment_target): + self.swift_flags = deployment_target.platform.swift_flags(args) + self.cmake_options = deployment_target.platform.cmake_options(args) + + def __platforms_to_skip_build(self, args, stage_dependent_args): platforms_to_skip_build = set() - if not args.build_linux: + if not stage_dependent_args.build_linux: platforms_to_skip_build.add(StdlibDeploymentTarget.Linux) - if not args.build_freebsd: + if not stage_dependent_args.build_freebsd: platforms_to_skip_build.add(StdlibDeploymentTarget.FreeBSD) - if not args.build_cygwin: + if not stage_dependent_args.build_cygwin: platforms_to_skip_build.add(StdlibDeploymentTarget.Cygwin) - if not args.build_osx: + if not stage_dependent_args.build_osx: platforms_to_skip_build.add(StdlibDeploymentTarget.OSX) - if not args.build_ios_device: + if not stage_dependent_args.build_ios_device: platforms_to_skip_build.add(StdlibDeploymentTarget.iOS) - if not args.build_ios_simulator: + if not stage_dependent_args.build_ios_simulator: platforms_to_skip_build.add(StdlibDeploymentTarget.iOSSimulator) - if not args.build_tvos_device: + if not stage_dependent_args.build_tvos_device: platforms_to_skip_build.add(StdlibDeploymentTarget.AppleTV) - if not args.build_tvos_simulator: + if not stage_dependent_args.build_tvos_simulator: platforms_to_skip_build.add( StdlibDeploymentTarget.AppleTVSimulator) - if not args.build_watchos_device: + if not stage_dependent_args.build_watchos_device: platforms_to_skip_build.add(StdlibDeploymentTarget.AppleWatch) - if not args.build_watchos_simulator: + if not stage_dependent_args.build_watchos_simulator: platforms_to_skip_build.add( StdlibDeploymentTarget.AppleWatchSimulator) - if not args.build_android: + if not stage_dependent_args.build_android: platforms_to_skip_build.add(StdlibDeploymentTarget.Android) return platforms_to_skip_build - def __platforms_to_skip_test(self, args): + def __platforms_to_skip_test(self, args, stage_dependent_args): platforms_to_skip_test = set() - if not args.test_linux: + if not stage_dependent_args.test_linux: platforms_to_skip_test.add(StdlibDeploymentTarget.Linux) - if not args.test_freebsd: + if not stage_dependent_args.test_freebsd: platforms_to_skip_test.add(StdlibDeploymentTarget.FreeBSD) - if not args.test_cygwin: + if not stage_dependent_args.test_cygwin: platforms_to_skip_test.add(StdlibDeploymentTarget.Cygwin) - if not args.test_osx: + if not stage_dependent_args.test_osx: platforms_to_skip_test.add(StdlibDeploymentTarget.OSX) - if not args.test_ios_host and not args.only_non_executable_test: + if not stage_dependent_args.test_ios_host and not args.only_non_executable_test: platforms_to_skip_test.add(StdlibDeploymentTarget.iOS) elif not args.only_non_executable_test: raise ArgumentError(None, "error: iOS device tests are not " + "supported in open-source Swift.") - if not args.test_ios_simulator: + if not stage_dependent_args.test_ios_simulator: platforms_to_skip_test.add(StdlibDeploymentTarget.iOSSimulator) - if not args.test_tvos_host and not args.only_non_executable_test: + if not stage_dependent_args.test_tvos_host and \ + not args.only_non_executable_test: platforms_to_skip_test.add(StdlibDeploymentTarget.AppleTV) elif not args.only_non_executable_test: raise ArgumentError(None, "error: tvOS device tests are not " + "supported in open-source Swift.") - if not args.test_tvos_simulator: + if not stage_dependent_args.test_tvos_simulator: platforms_to_skip_test.add(StdlibDeploymentTarget.AppleTVSimulator) - if not args.test_watchos_host and not args.only_non_executable_test: + if not stage_dependent_args.test_watchos_host and \ + not args.only_non_executable_test: platforms_to_skip_test.add(StdlibDeploymentTarget.AppleWatch) elif not args.only_non_executable_test: raise ArgumentError(None, "error: watchOS device tests are not " + "supported in open-source Swift.") - if not args.test_watchos_simulator: + if not stage_dependent_args.test_watchos_simulator: platforms_to_skip_test.add( StdlibDeploymentTarget.AppleWatchSimulator) - if not args.test_android: + if not stage_dependent_args.test_android: platforms_to_skip_test.add(StdlibDeploymentTarget.Android) return platforms_to_skip_test - def __platforms_archs_to_skip_test(self, args, host_target): + def __platforms_archs_to_skip_test(self, args, stage_dependent_args, host_target): platforms_archs_to_skip_test = set() - if not args.test_ios_32bit_simulator: + if not stage_dependent_args.test_ios_32bit_simulator: platforms_archs_to_skip_test.add( StdlibDeploymentTarget.iOSSimulator.i386) - if not args.test_watchos_32bit_simulator: + if not stage_dependent_args.test_watchos_32bit_simulator: platforms_archs_to_skip_test.add( StdlibDeploymentTarget.AppleWatchSimulator.i386) if host_target == StdlibDeploymentTarget.OSX.x86_64.name: @@ -291,14 +327,17 @@ def __platforms_archs_to_skip_test(self, args, host_target): return platforms_archs_to_skip_test - def __platforms_to_skip_test_host(self, args): + def __platforms_to_skip_test_host(self, args, stage_dependent_args): platforms_to_skip_test_host = set() - if not args.test_android_host: + if not stage_dependent_args.test_android_host: platforms_to_skip_test_host.add(StdlibDeploymentTarget.Android) - if not args.test_ios_host and not args.only_non_executable_test: + if not stage_dependent_args.test_ios_host and \ + not args.only_non_executable_test: platforms_to_skip_test_host.add(StdlibDeploymentTarget.iOS) - if not args.test_tvos_host and not args.only_non_executable_test: + if not stage_dependent_args.test_tvos_host and \ + not args.only_non_executable_test: platforms_to_skip_test_host.add(StdlibDeploymentTarget.AppleTV) - if not args.test_watchos_host and not args.only_non_executable_test: + if not stage_dependent_args.test_watchos_host and \ + not args.only_non_executable_test: platforms_to_skip_test_host.add(StdlibDeploymentTarget.AppleWatch) return platforms_to_skip_test_host diff --git a/utils/swift_build_support/swift_build_support/productpipeline_list_builder.py b/utils/swift_build_support/swift_build_support/productpipeline_list_builder.py new file mode 100644 index 0000000000000..476094a4b67f1 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/productpipeline_list_builder.py @@ -0,0 +1,212 @@ +# ===-- productpipeline_list_builder.py ----------------------------------===# +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 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 +# +# ===---------------------------------------------------------------------===# + +import platform + +from swift_build_support.swift_build_support import build_graph + + +class ProductPipeline(object): + """Intermediate state object that is private to this file. We use it to maintain + internally if our products are impl products are not. This is used for + verification that we maintain this property in the product pipeline list + builder. + + This class is meant to just be state. + """ + def __init__(self, should_run_epilogue_operations, identity, is_impl): + assert(isinstance(identity, int)) + self.identity = identity + self.products = [] + self.is_impl = is_impl + self.should_run_epilogue_operations = should_run_epilogue_operations + + def append(self, product, is_enabled): + self.products.append((product, is_enabled)) + + def finalize(self): + result = self.products + self.products = [] + return result + + def __iter__(self): + return iter(self.products) + + def __getitem__(self, i): + return self.products[i] + + def __len__(self): + return len(self.products) + + +class ProductPipelineListBuilder(object): + """A builder object that constructs a list of product pipeline. A product + pipeline is a list of products that are all meant to be inferred together + and then run based on dependence scheduling from the build-graph. Each + inferred list is produced in the same order in which one began each + pipeline. + + NOTE: We require that a single product pipeline never contain both + build-script products and build-script-impl products at the same + time. This is important since build-script-impl pipelines first build all + of the products, then test all of the products, then install all of the + products. In contrast, build-script products always perform a + build/test/install for each product in order. + """ + def __init__(self, args): + self.args = args + self.current_count = 0 + self.current_pipeline = None + self.is_current_pipeline_impl = False + self.pipeline_list = [] + + def begin_pipeline(self): + if self.current_pipeline is not None: + self.pipeline_list.append(self.current_pipeline) + self.current_pipeline = ProductPipeline(False, identity=self.current_count, + is_impl=False) + self.current_count += 1 + self.is_current_pipeline_impl = False + + def begin_impl_pipeline(self, should_run_epilogue_operations=False): + if self.current_pipeline is not None: + self.pipeline_list.append(self.current_pipeline) + self.current_pipeline = ProductPipeline(should_run_epilogue_operations, + identity=self.current_count, + is_impl=True) + + self.current_count += 1 + self.is_current_pipeline_impl = True + + def reset(self): + self.current_count = 0 + self.current_pipeline = None + self.is_current_pipeline_impl = False + self.pipelinst_list = [] + + def add_product(self, product_cls, is_enabled): + """Add a non-impl product to the current pipeline begin constructed""" + assert(self.current_pipeline is not None) + assert(not self.is_current_pipeline_impl) + assert(not product_cls.is_build_script_impl_product()) + self.current_pipeline.append(product_cls, is_enabled) + + def add_impl_product(self, product_cls, is_enabled): + """Add a non-impl product to the current pipeline begin constructed""" + assert(self.current_pipeline is not None) + assert(self.is_current_pipeline_impl) + assert(product_cls.is_build_script_impl_product()) + self.current_pipeline.append(product_cls, is_enabled) + + def infer(self): + products_to_generation_index = {} + enabled_products = set() + inferred_pipeline_list = [] + last_impl_pipeline_index = None + for i in range(len(self.pipeline_list)): + pipeline = self.pipeline_list[i] + if pipeline.is_impl: + last_impl_pipeline_index = i + final_pipeline = [] + for pipeline_i in range(len(pipeline)): + (p, is_enabled) = pipeline[pipeline_i] + # Make sure p has not been added multiple times to the builder. + assert(p not in products_to_generation_index) + products_to_generation_index[p] = (i, pipeline_i) + if is_enabled: + final_pipeline.append(p) + enabled_products.add(p) + else: + final_pipeline.append(None) + inferred_pipeline_list.append(final_pipeline) + + # Go back through pipeline and make sure that all dependencies of + # our product are from our generation or earlier. If we find such a + # dependency error. + for (p, is_enabled) in pipeline: + assert(all(d in products_to_generation_index for d in + p.get_dependencies())) + + for i in range(len(inferred_pipeline_list)): + pipeline = inferred_pipeline_list[i] + + # Filter out any of the pipelines that before inference were not + # selected. + enabled_pipeline = filter(lambda x: x is not None, pipeline) + + if self.args.verbose_build: + print("-- Build Graph Inference --") + print("Initial Product List:") + for p in enabled_pipeline: + print(" {}".format(p.product_name())) + + if len(enabled_pipeline) == 0: + continue + + final_schedule = \ + build_graph.produce_scheduled_build(enabled_pipeline)[0] + + # Go through the schedule and remove all references to products that + # we know are associated with an earlier pipeline. If it isn't from + # an earlier pipeline or isn't in our current pipeline, through an + # error. There is a dependency from our pipeline list + is_darwin = platform.system() == 'Darwin' + for p in final_schedule: + if is_darwin and p.is_nondarwin_only_build_product(): + continue + (gen_offset, index) = products_to_generation_index[p] + # If we are from an earlier generation, our position in the + # inferred pipeline list may be None. Initialize it now. + assert(gen_offset <= i) + inferred_pipeline_list[gen_offset][index] = p + + filtered_results = [] + for pipeline in inferred_pipeline_list: + filtered_results.append([p for p in pipeline if p is not None]) + + if self.args.verbose_build: + print("Final Build Order:") + for pipeline in filtered_results: + for p in pipeline: + print(" {}".format(p.product_name())) + + return (filtered_results, last_impl_pipeline_index) + + def finalize(self, shouldInfer): + """Product a final schedule and return a list of our product pipelines. Resets + the builder when done so is a consuming operation. + """ + # Append the current pipeline if we have one. + if self.current_pipeline is not None: + self.pipeline_list.append(self.current_pipeline) + + result = None + + # Then if we are asked to do so run the inference algorithm on each + # pipeline. + if shouldInfer: + result = self.infer() + else: + # Handle the is_enabled bit. When we infer, we want to before the + # is_enabled after we register all of the generations. + r = [] + last_index = None + for i in range(len(self.pipeline_list)): + if self.pipeline_list[i].is_impl: + last_index = i + r.append([x[0] for x in self.pipeline_list[i] + if x[1] is True]) + result = (r, last_index) + + # Invalidates self.pipeline_list. + self.reset() + return result diff --git a/utils/swift_build_support/swift_build_support/products/__init__.py b/utils/swift_build_support/swift_build_support/products/__init__.py index 169fe506eeb0e..5fe35e9035c68 100644 --- a/utils/swift_build_support/swift_build_support/products/__init__.py +++ b/utils/swift_build_support/swift_build_support/products/__init__.py @@ -10,8 +10,10 @@ # # ---------------------------------------------------------------------------- +from .backdeployconcurrency import BackDeployConcurrency from .benchmarks import Benchmarks from .cmark import CMark +from .earlyswiftdriver import EarlySwiftDriver from .foundation import Foundation from .indexstoredb import IndexStoreDB from .libcxx import LibCXX @@ -35,6 +37,7 @@ from .xctest import XCTest __all__ = [ + 'BackDeployConcurrency', 'CMark', 'Ninja', 'Foundation', @@ -51,6 +54,7 @@ 'SwiftInspect', 'SwiftPM', 'SwiftDriver', + 'EarlySwiftDriver', 'XCTest', 'SwiftSyntax', 'SKStressTester', diff --git a/utils/swift_build_support/swift_build_support/products/backdeployconcurrency.py b/utils/swift_build_support/swift_build_support/products/backdeployconcurrency.py new file mode 100644 index 0000000000000..2bac419a8e30d --- /dev/null +++ b/utils/swift_build_support/swift_build_support/products/backdeployconcurrency.py @@ -0,0 +1,152 @@ +# swift_build_support/products/backdeployconcurrency.py ---------*- python -*- +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 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 +# +# ---------------------------------------------------------------------------- + +import os +import platform + +from . import cmake_product +from . import cmark +from . import libcxx +from . import libicu +from . import llvm +from . import swift + + +class BackDeployConcurrency(cmake_product.CMakeProduct): + @classmethod + def product_source_name(cls): + """product_source_name() -> str + + The name of the source code directory of this product. + """ + return os.path.join("swift") + + @classmethod + def is_build_script_impl_product(cls): + return False + + @classmethod + def is_before_build_script_impl_product(cls): + return False + + def should_build(self, host_target): + return platform.system() == 'Darwin' + + def build(self, host_target): + build_variant = 'RelWithDebInfo' + self.cmake_options.define('CMAKE_BUILD_TYPE:STRING', build_variant) + + (platform, arch) = host_target.split('-') + + common_c_flags = ' '.join(self.common_cross_c_flags(platform, arch)) + self.cmake_options.define('CMAKE_C_FLAGS', common_c_flags) + self.cmake_options.define('CMAKE_CXX_FLAGS', common_c_flags) + +# toolchain_file = self.generate_darwin_toolchain_file(platform, arch) +# self.cmake_options.define('CMAKE_TOOLCHAIN_FILE:PATH', toolchain_file) + self.cmake_options.define( + 'TOOLCHAIN_DIR:PATH', + self.install_toolchain_path(host_target)) + self.cmake_options.define( + 'SWIFT_NATIVE_SWIFT_TOOLS_PATH:PATH', + os.path.join(self.install_toolchain_path(host_target), 'bin')) + + self.cmake_options.define('SWIFT_EMBED_BITCODE_SECTION:BOOL', True) + self.cmake_options.define('SWIFT_ENABLE_MACCATALYST:BOOL', True) + self.cmake_options.define('CMAKE_CROSSCOMPILING:BOOL', True) + + # Only build the back-deployment concurrency library, nothing else + self.cmake_options.define( + 'BUILD_SWIFT_CONCURRENCY_BACK_DEPLOYMENT_LIBRARIES:BOOL', True) + self.cmake_options.define('SWIFT_INCLUDE_TOOLS:BOOL', False) + self.cmake_options.define( + 'SWIFT_BUILD_STDLIB_EXTRA_TOOLCHAIN_CONTENT:BOOL', False) + self.cmake_options.define( + 'SWIFT_BUILD_TEST_SUPPORT_MODULES:BOOL', False) + self.cmake_options.define('SWIFT_BUILD_STDLIB:BOOL', False) + self.cmake_options.define('SWIFT_BUILD_DYNAMIC_STDLIB:BOOL', False) + self.cmake_options.define('SWIFT_BUILD_STATIC_STDLIB:BOOL', False) + self.cmake_options.define('SWIFT_BUILD_REMOTE_MIRROR:BOOL', False) + self.cmake_options.define('SWIFT_BUILD_SDK_OVERLAY:BOOL', False) + self.cmake_options.define('SWIFT_BUILD_DYNAMIC_SDK_OVERLAY:BOOL', False) + self.cmake_options.define('SWIFT_BUILD_STATIC_SDK_OVERLAY:BOOL', False) + + self.cmake_options.define('SWIFT_HOST_VARIANT_ARCH:STRING', arch) + self.cmake_options.define('BUILD_STANDALONE:BOOL', True) + + # Only install the "stdlib" component, which contains the concurrency + # module. + self.cmake_options.define('SWIFT_INSTALL_COMPONENTS:STRING', 'back-deployment') + + # Figure out the SDKs to build. + # NOTE: This normally happens down in build-script-impl, so we have + # to re-implement the logic here. + sdks_to_build = ['OSX'] + if self.args.build_ios_device: + sdks_to_build.append('IOS') + if self.args.build_ios_simulator: + sdks_to_build.append('IOS_SIMULATOR') + if self.args.build_tvos_device: + sdks_to_build.append('TVOS') + if self.args.build_tvos_simulator: + sdks_to_build.append('TVOS_SIMULATOR') + if self.args.build_watchos_device: + sdks_to_build.append('WATCHOS') + if self.args.build_watchos_simulator: + sdks_to_build.append('WATCHOS_SIMULATOR') + self.cmake_options.define('SWIFT_SDKS:STRING', ';'.join(sdks_to_build)) + + build_root = os.path.dirname(self.build_dir) + llvm_build_dir = os.path.join( + '..', build_root, '%s-%s' % ('llvm', host_target)) + llvm_cmake_dir = os.path.join(llvm_build_dir, 'lib', 'cmake', 'llvm') + self.cmake_options.define('LLVM_DIR:PATH', llvm_cmake_dir) + + dest_dir = self.host_install_destdir(host_target) + self.cmake_options.define('SWIFT_DEST_ROOT:PATH=', dest_dir) + self.cmake_options.define('SWIFT_HOST_VARIANT_SDK:STRING', 'OSX') + self.cmake_options.define('CMAKE_INSTALL_PREFIX', "") + + # Configure back-deployment targets + self.cmake_options.define( + 'SWIFT_DARWIN_DEPLOYMENT_VERSION_OSX:STRING', '10.15') + self.cmake_options.define( + 'SWIFT_DARWIN_DEPLOYMENT_VERSION_IOS:STRING', '13.0') + self.cmake_options.define( + 'SWIFT_DARWIN_DEPLOYMENT_VERSION_MACCATALYST:STRING', '13.0') + self.cmake_options.define( + 'SWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS:STRING', '13.0') + self.cmake_options.define( + 'SWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS:STRING', '6.0') + + self.build_with_cmake(["back-deployment"], build_variant, []) + + def should_test(self, host_target): + return False + + def test(self, host_target): + raise RuntimeError("Testing not implemented") + + def should_install(self, host_target): + return self.args.install_backdeployconcurrency + + def install(self, host_target): + destdir = self.host_install_destdir(host_target) + self.args.install_prefix + self.install_with_cmake(["install-back-deployment"], destdir) + + @classmethod + def get_dependencies(cls): + return [cmark.CMark, + llvm.LLVM, + libcxx.LibCXX, + libicu.LibICU, + swift.Swift] diff --git a/utils/swift_build_support/swift_build_support/products/benchmarks.py b/utils/swift_build_support/swift_build_support/products/benchmarks.py index b7a14ecc658fa..62a311695273c 100644 --- a/utils/swift_build_support/swift_build_support/products/benchmarks.py +++ b/utils/swift_build_support/swift_build_support/products/benchmarks.py @@ -38,6 +38,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True @@ -47,19 +51,31 @@ def build(self, host_target): def should_test(self, host_target): return self.args.test_toolchainbenchmarks + def _get_test_environment(self, host_target): + if platform.system() == 'Darwin': + # the resulting binaries would search first in /usr/lib/swift, + # we need to prefer the libraries we just built + return {'DYLD_LIBRARY_PATH': os.path.join( + _get_toolchain_path(host_target, self, self.args), + 'usr', 'lib', 'swift', 'macosx')} + + return None + def test(self, host_target): """Just run a single instance of the command for both .debug and .release. """ cmdline = ['--num-iters=1', 'XorLoop'] + test_environment = self._get_test_environment(host_target) + bench_Onone = os.path.join(self.build_dir, 'bin', 'Benchmark_Onone') - shell.call([bench_Onone] + cmdline) + shell.call([bench_Onone] + cmdline, env=test_environment) bench_O = os.path.join(self.build_dir, 'bin', 'Benchmark_O') - shell.call([bench_O] + cmdline) + shell.call([bench_O] + cmdline, env=test_environment) bench_Osize = os.path.join(self.build_dir, 'bin', 'Benchmark_Osize') - shell.call([bench_Osize] + cmdline) + shell.call([bench_Osize] + cmdline, env=test_environment) def should_install(self, host_target): return False @@ -81,7 +97,11 @@ def get_dependencies(cls): swiftpm.SwiftPM] -def run_build_script_helper(host_target, product, args): +def _get_toolchain_path(host_target, product, args): + # TODO check if we should prefer using product.install_toolchain_path + # this logic initially was inside run_build_script_helper + # and was factored out so it can be used in testing as well + toolchain_path = swiftpm.SwiftPM.get_install_destdir(args, host_target, product.build_dir) @@ -90,6 +110,12 @@ def run_build_script_helper(host_target, product, args): toolchain_path += \ targets.darwin_toolchain_prefix(args.install_prefix) + return toolchain_path + + +def run_build_script_helper(host_target, product, args): + toolchain_path = _get_toolchain_path(host_target, product, args) + # Our source_dir is expected to be './$SOURCE_ROOT/benchmarks'. That is due # the assumption that each product is in its own build directory. This # product is not like that and has its package/tools instead in diff --git a/utils/swift_build_support/swift_build_support/products/cmake_product.py b/utils/swift_build_support/swift_build_support/products/cmake_product.py new file mode 100644 index 0000000000000..6e4e633078636 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/products/cmake_product.py @@ -0,0 +1,134 @@ +# swift_build_support/products/product.py -----------------------*- python -*- +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2021 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 +# +# ---------------------------------------------------------------------------- + +import os + +from . import product +from .. import cmake +from .. import shell + + +class CMakeProduct(product.Product): + def is_verbose(self): + return self.args.verbose_build + + def build_with_cmake(self, build_targets, build_type, build_args, + prefer_just_built_toolchain=False): + assert self.toolchain.cmake is not None + cmake_build = [] + _cmake = cmake.CMake(self.args, self.toolchain, + prefer_just_built_toolchain) + + if self.toolchain.distcc_pump: + cmake_build.append(self.toolchain.distcc_pump) + cmake_build.extend([self.toolchain.cmake, "--build"]) + + # If we are verbose... + if self.is_verbose(): + # And ninja, add a -v. + if self.args.cmake_generator == "Ninja": + build_args.append('-v') + + generator_output_path = "" + if self.args.cmake_generator == "Ninja": + generator_output_path = os.path.join(self.build_dir, "build.ninja") + + cmake_cache_path = os.path.join(self.build_dir, "CMakeCache.txt") + if self.args.reconfigure or not os.path.isfile(cmake_cache_path) or \ + (generator_output_path and not os.path.isfile(generator_output_path)): + if not os.path.exists(self.build_dir): + os.makedirs(self.build_dir) + + # Use `cmake-file-api` in case it is available. + query_dir = os.path.join(self.build_dir, ".cmake", "api", "v1", "query") + if not os.path.exists(query_dir): + os.makedirs(query_dir) + open(os.path.join(query_dir, "codemodel-v2"), 'a').close() + open(os.path.join(query_dir, "cache-v2"), 'a').close() + + env = None + if self.toolchain.distcc: + env = { + "DISTCC_HOSTS": "localhost,lzo,cpp" + } + + with shell.pushd(self.build_dir): + shell.call([self.toolchain.cmake] + list(self.cmake_options) + + list(_cmake.common_options(self)) + + self.args.extra_cmake_options + [self.source_dir], + env=env) + + if not self.args.skip_build or self.product_name() == "llvm": + if self.args.cmake_generator == "Xcode": + # Xcode generator uses "ALL_BUILD" instead of "all". + # Also, xcodebuild uses -target instead of bare names. + build_targets = build_targets[:] + build_targets = [val for target in build_targets + for val in ["-target", + target if target != "all" + else "ALL_BUILD"]] + + # Xcode can't restart itself if it turns out we need to reconfigure. + # Do an advance build to handle that. + shell.call(cmake_build + [self.build_dir, "--config", build_type]) + + shell.call(cmake_build + [self.build_dir, "--config", build_type, "--"] + + build_args + build_targets) + + def test_with_cmake(self, executable_target, results_targets, + build_type, build_args): + assert self.toolchain.cmake is not None + cmake_build = [] + + if self.toolchain.distcc_pump: + cmake_build.append(self.toolchain.distcc_pump) + + # If we are verbose... + if self.is_verbose(): + # And ninja, add a -v. + if self.args.cmake_generator == "Ninja": + build_args.append('-v') + + cmake_args = [self.toolchain.cmake, "--build", self.build_dir, + "--config", build_type, "--"] + cmake_build.extend(cmake_args + build_args) + + def target_flag(target): + if self.args.cmake_generator == "Xcode": + return ["-target", target] + return [target] + + if executable_target: + shell.call(cmake_build + target_flag(executable_target)) + + for target in results_targets: + if target: + test_target = target + print("--- %s ---" % target) + if test_target.startswith("check-swift") and self.args.test_paths: + test_target = test_target + "-custom" + + shell.call(cmake_build + target_flag(test_target)) + + print("--- %s finished ---" % target) + + def install_with_cmake(self, install_targets, install_destdir): + assert self.toolchain.cmake is not None + cmake_build = [] + + if self.toolchain.distcc_pump: + cmake_build.append(self.toolchain.distcc_pump) + cmake_args = [self.toolchain.cmake, "--build", self.build_dir, "--"] + cmake_build.extend(cmake_args + install_targets) + + environment = {'DESTDIR': install_destdir} + shell.call(cmake_build, env=environment) diff --git a/utils/swift_build_support/swift_build_support/products/cmark.py b/utils/swift_build_support/swift_build_support/products/cmark.py index cac4f2e03d1b9..b11313fab877b 100644 --- a/utils/swift_build_support/swift_build_support/products/cmark.py +++ b/utils/swift_build_support/swift_build_support/products/cmark.py @@ -10,19 +10,104 @@ # # ---------------------------------------------------------------------------- -from . import product +from . import cmake_product +from . import earlyswiftdriver -class CMark(product.Product): +class CMark(cmake_product.CMakeProduct): @classmethod def is_build_script_impl_product(cls): """is_build_script_impl_product -> bool Whether this product is produced by build-script-impl. """ + return False + + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ return True - # This is the root of the build-graph, so it doesn't have any dependencies. + # EarlySwiftDriver is the root of the graph, and is the only dependency of + # this product. @classmethod def get_dependencies(cls): - return [] + return [earlyswiftdriver.EarlySwiftDriver] + + def should_build(self, host_target): + """should_build() -> Bool + + Whether or not this product should be built with the given arguments. + """ + return self.args.build_cmark + + def build(self, host_target): + """build() -> void + + Perform the build, for a non-build-script-impl product. + """ + self.cmake_options.define('CMAKE_BUILD_TYPE:STRING', + self.args.cmark_build_variant) + + (platform, arch) = host_target.split('-') + + common_c_flags = ' '.join(self.common_cross_c_flags(platform, arch)) + self.cmake_options.define('CMAKE_C_FLAGS', common_c_flags) + self.cmake_options.define('CMAKE_CXX_FLAGS', common_c_flags) + + if host_target.startswith("macosx") or \ + host_target.startswith("iphone") or \ + host_target.startswith("appletv") or \ + host_target.startswith("watch"): + toolchain_file = self.generate_darwin_toolchain_file(platform, arch) + self.cmake_options.define('CMAKE_TOOLCHAIN_FILE:PATH', toolchain_file) + elif platform == "linux": + toolchain_file = self.generate_linux_toolchain_file(platform, arch) + self.cmake_options.define('CMAKE_TOOLCHAIN_FILE:PATH', toolchain_file) + + self.build_with_cmake(["all"], self.args.cmark_build_variant, []) + + def should_test(self, host_target): + """should_test() -> Bool + + Whether or not this product should be tested with the given arguments. + """ + if self.is_cross_compile_target(host_target): + return False + + return self.args.test_cmark + + def test(self, host_target): + """ + Perform the test phase for the product. + + This phase might build and execute the product tests. + """ + executable_target = 'api_test' + results_targets = ['test'] + if self.args.cmake_generator == 'Xcode': + # Xcode generator uses "RUN_TESTS" instead of "test". + results_targets = ['RUN_TESTS'] + + self.test_with_cmake(executable_target, results_targets, + self.args.cmark_build_variant, []) + + def should_install(self, host_target): + """should_install() -> Bool + + Whether or not this product should be installed with the given + arguments. + """ + return self.args.install_all + + def install(self, host_target): + """ + Perform the install phase for the product. + + This phase might copy the artifacts from the previous phases into a + destination directory. + """ + self.install_with_cmake(["install"], self.host_install_destdir(host_target)) diff --git a/utils/swift_build_support/swift_build_support/products/earlyswiftdriver.py b/utils/swift_build_support/swift_build_support/products/earlyswiftdriver.py new file mode 100644 index 0000000000000..34438b93bc1b1 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/products/earlyswiftdriver.py @@ -0,0 +1,153 @@ +# swift_build_support/products/swiftdriver.py -------------------*- python -*- +# +# 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 +# +# ---------------------------------------------------------------------------- + +import os + +from . import product +from .. import shell +from .. import toolchain + + +# SwiftDriver is a standalone compiler-driver application written in +# Swift. This build product is a "Special" SwiftDriver that gets built +# with the host toolchain to act as *the* compiler driver for the +# `swift` build directory compiler, hence it does not depend on any other +# build product of `build-script`. +# +# Today, if the host toolchain is not equipped with Swift, or the user +# explicitly opts out of using SwiftDriver (`-skip-early-swiftdriver`) +# (relying on a fallback to the legacy driver), a warning is emitted. +# In the future, a Swift-equipped host toolchain may become mandatory. +class EarlySwiftDriver(product.Product): + @classmethod + def product_source_name(cls): + return "swift-driver" + + @classmethod + def is_build_script_impl_product(cls): + return False + + @classmethod + def is_before_build_script_impl_product(cls): + return True + + def should_build(self, host_target): + if self.is_cross_compile_target(host_target): + return False + + if self.args.build_early_swift_driver: + if toolchain.host_toolchain().find_tool("swift") is None: + warn_msg = 'Host toolchain could not locate a '\ + 'compiler to build swift-driver. '\ + '(Try `--skip-early-swift-driver`)' + print('-- Warning: {}', warn_msg) + return False + else: + return True + return False + + @classmethod + def get_dependencies(cls): + return [] + + def should_clean(self, host_target): + return self.args.clean_early_swift_driver + + def clean(self, host_target): + run_build_script_helper('clean', host_target, self, self.args) + + def build(self, host_target): + run_build_script_helper('build', host_target, self, self.args) + + def should_test(self, host_target): + # EarlySwiftDriver is tested against the compiler's lit test + # suite driver subset, as a standalone test CMake target when + # `swift` is built. We do not run the driver's own tests here. + return False + + def test(self, host_target): + run_build_script_helper('test', host_target, self, self.args) + + def should_install(self, host_target): + # This product is for the swift-driver used with the build-directory compiler. + # If a toolchain install is required, please use the SwiftDriver (no 'Early') + # product with `--swift-driver --install-swift-driver`. + return False + + @classmethod + def is_ignore_install_all_product(cls): + # Ensures that `install_all` setting triggered by `--infer` does not + # affect products which specify `is_ignore_install_all_product` as + # True. This is useful for products which should not be installed into the + # toolchain (corresponding build products that use the just-built + # toolchain are the products that get installed, e.g. `swiftdriver` to + # `earlyswiftdriver`). + return True + + def install(self, host_target): + run_build_script_helper('install', host_target, self, self.args) + + +def run_build_script_helper(action, host_target, product, args): + build_root = os.path.dirname(product.build_dir) + script_path = os.path.join( + product.source_dir, 'Utilities', 'build-script-helper.py') + + # Building with the host toolchain for use with a local compiler build, + # use the toolchain which is supplying the `swiftc`. + swiftc_path = os.path.abspath(product.toolchain.swiftc) + toolchain_path = os.path.dirname(os.path.dirname(swiftc_path)) + + # Pass Dispatch directory down if we built it + dispatch_build_dir = os.path.join( + build_root, '%s-%s' % ('libdispatch', host_target)) + + # Pass Foundation directory down if we built it + foundation_build_dir = os.path.join( + build_root, '%s-%s' % ('foundation', host_target)) + + # Pass the swift lit tests if we're testing and the Swift tests were built + swift_build_dir = os.path.join( + build_root, 'swift-{}'.format(host_target)) + lit_test_dir = os.path.join( + swift_build_dir, 'test-{}'.format(host_target)) + + is_release = product.is_release() + configuration = 'release' if is_release else 'debug' + helper_cmd = [ + script_path, + action, + '--package-path', product.source_dir, + '--build-path', product.build_dir, + '--configuration', configuration, + '--toolchain', toolchain_path, + '--ninja-bin', product.toolchain.ninja, + '--cmake-bin', product.toolchain.cmake, + '--local_compiler_build' + ] + + if os.path.exists(dispatch_build_dir): + helper_cmd += [ + '--dispatch-build-dir', dispatch_build_dir + ] + if os.path.exists(foundation_build_dir): + helper_cmd += [ + '--foundation-build-dir', foundation_build_dir + ] + if os.path.exists(lit_test_dir) and action == 'test': + helper_cmd += [ + '--lit-test-dir', lit_test_dir + ] + if args.verbose_build: + helper_cmd.append('--verbose') + + shell.call(helper_cmd) diff --git a/utils/swift_build_support/swift_build_support/products/foundation.py b/utils/swift_build_support/swift_build_support/products/foundation.py index 99a362ac82693..79d843b31b1ec 100644 --- a/utils/swift_build_support/swift_build_support/products/foundation.py +++ b/utils/swift_build_support/swift_build_support/products/foundation.py @@ -28,6 +28,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def product_source_name(cls): """product_source_name() -> str diff --git a/utils/swift_build_support/swift_build_support/products/indexstoredb.py b/utils/swift_build_support/swift_build_support/products/indexstoredb.py index 83b4ac91811c4..28838a443e8dd 100644 --- a/utils/swift_build_support/swift_build_support/products/indexstoredb.py +++ b/utils/swift_build_support/swift_build_support/products/indexstoredb.py @@ -37,6 +37,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/libcxx.py b/utils/swift_build_support/swift_build_support/products/libcxx.py index d26b8bda83cf7..0103f8cd294ee 100644 --- a/utils/swift_build_support/swift_build_support/products/libcxx.py +++ b/utils/swift_build_support/swift_build_support/products/libcxx.py @@ -24,6 +24,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def get_dependencies(cls): return [cmark.CMark, diff --git a/utils/swift_build_support/swift_build_support/products/libdispatch.py b/utils/swift_build_support/swift_build_support/products/libdispatch.py index 62bf2475948d5..d5ff58a826ad2 100644 --- a/utils/swift_build_support/swift_build_support/products/libdispatch.py +++ b/utils/swift_build_support/swift_build_support/products/libdispatch.py @@ -27,6 +27,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def product_source_name(cls): """product_source_name() -> str diff --git a/utils/swift_build_support/swift_build_support/products/libicu.py b/utils/swift_build_support/swift_build_support/products/libicu.py index 39e1b60262f70..038ab9f6eb8b7 100644 --- a/utils/swift_build_support/swift_build_support/products/libicu.py +++ b/utils/swift_build_support/swift_build_support/products/libicu.py @@ -25,6 +25,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def product_source_name(cls): """product_source_name() -> str diff --git a/utils/swift_build_support/swift_build_support/products/llbuild.py b/utils/swift_build_support/swift_build_support/products/llbuild.py index e723b03ecb199..b622379c211d2 100644 --- a/utils/swift_build_support/swift_build_support/products/llbuild.py +++ b/utils/swift_build_support/swift_build_support/products/llbuild.py @@ -30,6 +30,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def get_dependencies(cls): return [cmark.CMark, diff --git a/utils/swift_build_support/swift_build_support/products/lldb.py b/utils/swift_build_support/swift_build_support/products/lldb.py index 73a9017bbe407..be6004740c77b 100644 --- a/utils/swift_build_support/swift_build_support/products/lldb.py +++ b/utils/swift_build_support/swift_build_support/products/lldb.py @@ -27,6 +27,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def get_dependencies(cls): return [cmark.CMark, diff --git a/utils/swift_build_support/swift_build_support/products/llvm.py b/utils/swift_build_support/swift_build_support/products/llvm.py index 1bc27bc89e38a..7480f14503ce0 100644 --- a/utils/swift_build_support/swift_build_support/products/llvm.py +++ b/utils/swift_build_support/swift_build_support/products/llvm.py @@ -43,6 +43,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @property def _compiler_vendor_flags(self): if self.args.compiler_vendor == "none": diff --git a/utils/swift_build_support/swift_build_support/products/ninja.py b/utils/swift_build_support/swift_build_support/products/ninja.py index fecd25adc8d27..722fb61b3d63d 100644 --- a/utils/swift_build_support/swift_build_support/products/ninja.py +++ b/utils/swift_build_support/swift_build_support/products/ninja.py @@ -30,6 +30,10 @@ class Ninja(product.Product): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def new_builder(cls, args, toolchain, workspace, host): return NinjaBuilder(cls, args, toolchain, workspace) diff --git a/utils/swift_build_support/swift_build_support/products/playgroundsupport.py b/utils/swift_build_support/swift_build_support/products/playgroundsupport.py index 0e98b442b14be..90d7c16116fa3 100644 --- a/utils/swift_build_support/swift_build_support/products/playgroundsupport.py +++ b/utils/swift_build_support/swift_build_support/products/playgroundsupport.py @@ -46,6 +46,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return self.args.build_playgroundsupport diff --git a/utils/swift_build_support/swift_build_support/products/product.py b/utils/swift_build_support/swift_build_support/products/product.py index 075b934d10ac1..3ac454e86c12e 100644 --- a/utils/swift_build_support/swift_build_support/products/product.py +++ b/utils/swift_build_support/swift_build_support/products/product.py @@ -13,7 +13,10 @@ import abc import os +from build_swift.build_swift.wrappers import xcrun + from .. import cmake +from .. import shell from .. import targets @@ -58,6 +61,29 @@ def is_build_script_impl_product(cls): """ raise NotImplementedError + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + Such products must be non-build_script_impl products. + Because such products are built ahead of the compiler, they are + built using the host toolchain. + """ + raise NotImplementedError + + @classmethod + def is_ignore_install_all_product(cls): + """is_ignore_install_all_product -> bool + + Whether this product is to ignore the install-all directive + and insted always respect its own should_install. + This is useful when we run -install-all but have products + which should never be installed into the toolchain + (e.g. earlyswiftdriver) + """ + return False + @classmethod def is_swiftpm_unified_build_product(cls): """is_swiftpm_unified_build_product -> bool @@ -155,6 +181,8 @@ def __init__(self, args, toolchain, source_dir, build_dir): self.source_dir = source_dir self.build_dir = build_dir self.cmake_options = cmake.CMakeOptions() + self.common_c_flags = ['-Wno-unknown-warning-option', + '-Werror=unguarded-availability-new'] def is_release(self): """is_release() -> Bool @@ -179,6 +207,163 @@ def install_toolchain_path(self, host_target): return targets.toolchain_path(install_destdir, self.args.install_prefix) + def is_darwin_host(self, host_target): + return host_target.startswith("macosx") or \ + host_target.startswith("iphone") or \ + host_target.startswith("appletv") or \ + host_target.startswith("watch") + + def should_include_host_in_lipo(self, host_target): + return self.args.cross_compile_hosts and \ + self.is_darwin_host(host_target) + + def host_install_destdir(self, host_target): + if self.args.cross_compile_hosts: + # If cross compiling tools, install into a host-specific subdirectory. + if self.should_include_host_in_lipo(host_target): + # If this is one of the hosts we should lipo, + # install in to a temporary subdirectory. + return '%s/intermediate-install/%s' % \ + (self.args.install_destdir, host_target) + elif host_target == "merged-hosts": + # This assumes that all hosts are merged to the lipo. + return self.args.install_destdir + else: + return '%s/%s' % (self.args.install_destdir, host_target) + else: + return self.args.install_destdir + + def is_cross_compile_target(self, host_target): + return self.args.cross_compile_hosts and \ + host_target in self.args.cross_compile_hosts + + def generate_darwin_toolchain_file(self, platform, arch): + shell.makedirs(self.build_dir) + toolchain_file = os.path.join(self.build_dir, 'BuildScriptToolchain.cmake') + + cmake_osx_sysroot = xcrun.sdk_path(platform) + + target = None + if platform == 'macosx': + target = '{}-apple-macosx{}'.format( + arch, self.args.darwin_deployment_version_osx) + elif platform == 'iphonesimulator': + target = '{}-apple-ios{}'.format( + arch, self.args.darwin_deployment_version_ios) + elif platform == 'iphoneos': + target = '{}-apple-ios{}'.format( + arch, self.args.darwin_deployment_version_ios) + elif platform == 'appletvsimulator': + target = '{}-apple-tvos{}'.format( + arch, self.args.darwin_deployment_version_tvos) + elif platform == 'appletvos': + target = '{}-apple-tvos{}'.format( + arch, self.args.darwin_deployment_version_tvos) + elif platform == 'watchsimulator': + target = '{}-apple-watchos{}'.format( + arch, self.args.darwin_deployment_version_watchos) + elif platform == 'watchos': + target = '{}-apple-watchos{}'.format( + arch, self.args.darwin_deployment_version_watchos) + else: + raise RuntimeError("Unhandled platform?!") + + toolchain_args = {} + + toolchain_args['CMAKE_SYSTEM_NAME'] = 'Darwin' + toolchain_args['CMAKE_OSX_SYSROOT'] = cmake_osx_sysroot + toolchain_args['CMAKE_OSX_ARCHITECTURES'] = arch + + if self.toolchain.cc.endswith('clang'): + toolchain_args['CMAKE_C_COMPILER_TARGET'] = target + if self.toolchain.cxx.endswith('clang++'): + toolchain_args['CMAKE_CXX_COMPILER_TARGET'] = target + # Swift always supports cross compiling. + toolchain_args['CMAKE_Swift_COMPILER_TARGET'] = target + + # Sort by the key so that we always produce the same toolchain file + data = sorted(toolchain_args.items(), key=lambda x: x[0]) + if not self.args.dry_run: + with open(toolchain_file, 'w') as f: + f.writelines("set({} {})\n".format(k, v) for k, v in data) + else: + print("DRY_RUN! Writing Toolchain file to path: {}".format(toolchain_file)) + + return toolchain_file + + def get_linux_abi(self, arch): + # Map tuples of (platform, arch) to ABI + # + # E.x.: Hard ABI or Soft ABI for Linux map to gnueabihf + arch_platform_to_abi = { + # For now always map to hard float ABI. + 'armv7': ('arm', 'gnueabihf') + } + + # Default is just arch, gnu + sysroot_arch, abi = arch_platform_to_abi.get(arch, (arch, 'gnu')) + return sysroot_arch, abi + + def get_linux_sysroot(self, platform, arch): + if not self.is_cross_compile_target('{}-{}'.format(platform, arch)): + return None + sysroot_arch, abi = self.get_linux_abi(arch) + # $ARCH-$PLATFORM-$ABI + # E.x.: aarch64-linux-gnu + sysroot_dirname = '{}-{}-{}'.format(sysroot_arch, platform, abi) + return os.path.join(os.sep, 'usr', sysroot_dirname) + + def get_linux_target(self, platform, arch): + sysroot_arch, abi = self.get_linux_abi(arch) + return '{}-unknown-linux-{}'.format(sysroot_arch, abi) + + def generate_linux_toolchain_file(self, platform, arch): + shell.makedirs(self.build_dir) + toolchain_file = os.path.join(self.build_dir, 'BuildScriptToolchain.cmake') + + toolchain_args = {} + + toolchain_args['CMAKE_SYSTEM_NAME'] = 'Linux' + toolchain_args['CMAKE_SYSTEM_PROCESSOR'] = arch + + # We only set the actual sysroot if we are actually cross + # compiling. This is important since otherwise cmake seems to change the + # RUNPATH to be a relative rather than an absolute path, breaking + # certain cmark tests (and maybe others). + maybe_sysroot = self.get_linux_sysroot(platform, arch) + if maybe_sysroot is not None: + toolchain_args['CMAKE_SYSROOT'] = maybe_sysroot + + target = self.get_linux_target(platform, arch) + if self.toolchain.cc.endswith('clang'): + toolchain_args['CMAKE_C_COMPILER_TARGET'] = target + if self.toolchain.cxx.endswith('clang++'): + toolchain_args['CMAKE_CXX_COMPILER_TARGET'] = target + # Swift always supports cross compiling. + toolchain_args['CMAKE_Swift_COMPILER_TARGET'] = target + toolchain_args['CMAKE_FIND_ROOT_PATH_MODE_PROGRAM'] = 'NEVER' + toolchain_args['CMAKE_FIND_ROOT_PATH_MODE_LIBRARY'] = 'ONLY' + toolchain_args['CMAKE_FIND_ROOT_PATH_MODE_INCLUDE'] = 'ONLY' + toolchain_args['CMAKE_FIND_ROOT_PATH_MODE_PACKAGE'] = 'ONLY' + + # Sort by the key so that we always produce the same toolchain file + data = sorted(toolchain_args.items(), key=lambda x: x[0]) + if not self.args.dry_run: + with open(toolchain_file, 'w') as f: + f.writelines("set({} {})\n".format(k, v) for k, v in data) + else: + print("DRY_RUN! Writing Toolchain file to path: {}".format(toolchain_file)) + + return toolchain_file + + def common_cross_c_flags(self, platform, arch): + cross_flags = [] + + if self.is_release(): + cross_flags.append('-fno-stack-protector') + + return self.common_c_flags + cross_flags + class ProductBuilder(object): """ diff --git a/utils/swift_build_support/swift_build_support/products/skstresstester.py b/utils/swift_build_support/swift_build_support/products/skstresstester.py index 0656a2bfd7d3a..5e753a5624b8f 100644 --- a/utils/swift_build_support/swift_build_support/products/skstresstester.py +++ b/utils/swift_build_support/swift_build_support/products/skstresstester.py @@ -43,6 +43,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def is_swiftpm_unified_build_product(cls): return True diff --git a/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py b/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py index aa3d22602aeac..c7349ab7e60de 100644 --- a/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py +++ b/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py @@ -33,6 +33,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/swift.py b/utils/swift_build_support/swift_build_support/products/swift.py index 1e4b4feb2df9b..177c77e4d87c3 100644 --- a/utils/swift_build_support/swift_build_support/products/swift.py +++ b/utils/swift_build_support/swift_build_support/products/swift.py @@ -11,6 +11,7 @@ # ---------------------------------------------------------------------------- from . import cmark +from . import earlyswiftdriver from . import libcxx from . import libicu from . import llvm @@ -52,6 +53,9 @@ def __init__(self, args, toolchain, source_dir, build_dir): # Add experimental concurrency flag. self.cmake_options.extend(self._enable_experimental_concurrency) + # Add experimental distributed flag. + self.cmake_options.extend(self._enable_experimental_distributed) + @classmethod def is_build_script_impl_product(cls): """is_build_script_impl_product -> bool @@ -60,6 +64,10 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + return False + @property def _runtime_sanitizer_flags(self): sanitizer_list = [] @@ -142,9 +150,15 @@ def _enable_experimental_concurrency(self): return [('SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY:BOOL', self.args.enable_experimental_concurrency)] + @property + def _enable_experimental_distributed(self): + return [('SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED:BOOL', + self.args.enable_experimental_distributed)] + @classmethod def get_dependencies(cls): return [cmark.CMark, + earlyswiftdriver.EarlySwiftDriver, llvm.LLVM, libcxx.LibCXX, libicu.LibICU] diff --git a/utils/swift_build_support/swift_build_support/products/swiftdriver.py b/utils/swift_build_support/swift_build_support/products/swiftdriver.py index 4f2bd72c3f127..3bd5755de35be 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftdriver.py +++ b/utils/swift_build_support/swift_build_support/products/swiftdriver.py @@ -27,6 +27,10 @@ from .. import targets +# SwiftDriver is a standalone compiler-driver application written in +# Swift. This build product is *the* driver product that is +# installed into a resulting toolchain. It is built-with and depends-on +# other build products of this build (compiler, package-manager, etc). class SwiftDriver(product.Product): @classmethod def product_source_name(cls): @@ -36,6 +40,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return self.args.build_swift_driver @@ -124,6 +132,11 @@ def run_build_script_helper(action, host_target, product, args): helper_cmd += [ '--lit-test-dir', lit_test_dir ] + # Pass Cross compile host info + if swiftpm.SwiftPM.has_cross_compile_hosts(args): + helper_cmd += ['--cross-compile-hosts'] + for cross_compile_host in args.cross_compile_hosts: + helper_cmd += [cross_compile_host] if args.verbose_build: helper_cmd.append('--verbose') diff --git a/utils/swift_build_support/swift_build_support/products/swiftformat.py b/utils/swift_build_support/swift_build_support/products/swiftformat.py index 940ccd1043e6c..42769f1ea388a 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftformat.py +++ b/utils/swift_build_support/swift_build_support/products/swiftformat.py @@ -42,6 +42,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def is_swiftpm_unified_build_product(cls): return True diff --git a/utils/swift_build_support/swift_build_support/products/swiftinspect.py b/utils/swift_build_support/swift_build_support/products/swiftinspect.py index b2c54190f1b1e..be00b0e4cc2c0 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftinspect.py +++ b/utils/swift_build_support/swift_build_support/products/swiftinspect.py @@ -32,12 +32,16 @@ class SwiftInspect(product.Product): @classmethod def product_source_name(cls): - return "swift-dt" + return "swift-inspect" @classmethod def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/swiftpm.py b/utils/swift_build_support/swift_build_support/products/swiftpm.py index 25e982e23f173..4a97f377ef408 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftpm.py +++ b/utils/swift_build_support/swift_build_support/products/swiftpm.py @@ -34,6 +34,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/swiftsyntax.py b/utils/swift_build_support/swift_build_support/products/swiftsyntax.py index 00cdef0dd0c5c..f32c453aad37f 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftsyntax.py +++ b/utils/swift_build_support/swift_build_support/products/swiftsyntax.py @@ -41,6 +41,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def is_swiftpm_unified_build_product(cls): return True diff --git a/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py b/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py index 09b8eccaa8d1a..c30b938147eaf 100644 --- a/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py +++ b/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py @@ -39,6 +39,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/xctest.py b/utils/swift_build_support/swift_build_support/products/xctest.py index 56eda1461a72c..97278cbeceee8 100644 --- a/utils/swift_build_support/swift_build_support/products/xctest.py +++ b/utils/swift_build_support/swift_build_support/products/xctest.py @@ -29,6 +29,10 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def product_source_name(cls): """product_source_name() -> str diff --git a/utils/swift_build_support/swift_build_support/targets.py b/utils/swift_build_support/swift_build_support/targets.py index 91876f3e5c6a7..34364458ec736 100644 --- a/utils/swift_build_support/swift_build_support/targets.py +++ b/utils/swift_build_support/swift_build_support/targets.py @@ -68,6 +68,18 @@ def contains(self, target_name): return True return False + def swift_flags(self, args): + """ + Swift compiler flags for a platform, useful for cross-compiling + """ + return '' + + def cmake_options(self, args): + """ + CMake flags to build for a platform, useful for cross-compiling + """ + return '' + class DarwinPlatform(Platform): def __init__(self, name, archs, sdk_name=None, is_simulator=False): @@ -136,6 +148,29 @@ def uses_host_tests(self): """ return True + def swift_flags(self, args): + flags = '-target %s-unknown-linux-android%s ' % (args.android_arch, + args.android_api_level) + + flags += '-resource-dir %s/swift-%s-%s/lib/swift ' % ( + args.build_root, self.name, args.android_arch) + + android_toolchain_path = '%s/toolchains/llvm/prebuilt/%s' % ( + args.android_ndk, StdlibDeploymentTarget.host_target().name) + + flags += '-sdk %s/sysroot ' % (android_toolchain_path) + flags += '-tools-directory %s/bin' % (android_toolchain_path) + return flags + + def cmake_options(self, args): + options = '-DCMAKE_SYSTEM_NAME=Android ' + options += '-DCMAKE_SYSTEM_VERSION=%s ' % (args.android_api_level) + options += '-DCMAKE_SYSTEM_PROCESSOR=%s ' % (args.android_arch if not + args.android_arch == 'armv7' + else 'armv7-a') + options += '-DCMAKE_ANDROID_NDK:PATH=%s' % (args.android_ndk) + return options + class Target(object): """ @@ -154,7 +189,7 @@ def name(self): class StdlibDeploymentTarget(object): - OSX = DarwinPlatform("macosx", archs=["x86_64", "arm64", "arm64e"], + OSX = DarwinPlatform("macosx", archs=["x86_64", "arm64"], sdk_name="OSX") iOS = DarwinPlatform("iphoneos", archs=["armv7", "armv7s", "arm64", "arm64e"], diff --git a/utils/swift_build_support/swift_build_support/toolchain.py b/utils/swift_build_support/swift_build_support/toolchain.py index aa79f69fc3826..ff27b421805bf 100644 --- a/utils/swift_build_support/swift_build_support/toolchain.py +++ b/utils/swift_build_support/swift_build_support/toolchain.py @@ -62,6 +62,8 @@ def _getter(self): _register("llvm_cov", "llvm-cov") _register("lipo", "lipo") _register("libtool", "libtool") +_register("ranlib", "ranlib") +_register("ar", "ar") _register("sccache", "sccache") _register("swiftc", "swiftc") diff --git a/utils/swift_build_support/swift_build_support/utils.py b/utils/swift_build_support/swift_build_support/utils.py new file mode 100644 index 0000000000000..0316424235b8b --- /dev/null +++ b/utils/swift_build_support/swift_build_support/utils.py @@ -0,0 +1,32 @@ +# ===-- utils.py ---------------------------------------------------------===# +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 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 +# +# ===---------------------------------------------------------------------===# + +from __future__ import absolute_import, print_function, unicode_literals + +import sys + + +def fatal_error(message, stream=sys.stderr): + """Writes a message to the given stream and exits. By default this + function outputs to stderr. + """ + + stream.write('[{}] ERROR: {}\n'.format(sys.argv[0], message)) + stream.flush() + sys.exit(1) + + +def exit_rejecting_arguments(message, parser=None): + print(message, file=sys.stderr) + if parser: + parser.print_usage(sys.stderr) + sys.exit(2) # 2 is the same as `argparse` error exit code. diff --git a/utils/swift_build_support/tests/products/test_cmark.py b/utils/swift_build_support/tests/products/test_cmark.py new file mode 100644 index 0000000000000..77b25bfa56158 --- /dev/null +++ b/utils/swift_build_support/tests/products/test_cmark.py @@ -0,0 +1,143 @@ +# tests/products/test_ninja.py ----------------------------------*- python -*- +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2021 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 +# ---------------------------------------------------------------------------- + +import argparse +import os +import shutil +import sys +import tempfile +import unittest +try: + # py2 + from StringIO import StringIO +except ImportError: + # py3 + from io import StringIO + +# from swift_build_support import cmake +from swift_build_support import shell +from swift_build_support.products import CMark +from swift_build_support.targets import StdlibDeploymentTarget +from swift_build_support.toolchain import host_toolchain +from swift_build_support.workspace import Workspace + + +class CMarkTestCase(unittest.TestCase): + + def setUp(self): + # Setup workspace + tmpdir1 = os.path.realpath(tempfile.mkdtemp()) + tmpdir2 = os.path.realpath(tempfile.mkdtemp()) + os.makedirs(os.path.join(tmpdir1, 'cmark')) + + self.workspace = Workspace(source_root=tmpdir1, + build_root=tmpdir2) + + self.host = StdlibDeploymentTarget.host_target() + + # Setup toolchain + self.toolchain = host_toolchain() + self.toolchain.cc = '/path/to/cc' + self.toolchain.cxx = '/path/to/cxx' + + # Setup args + self.args = argparse.Namespace( + build_cmark=True, + cmake_generator="Ninja", + cmark_build_type="Release", + rebuild=False, + extra_cmake_options=[], + skip_build=False, + darwin_deployment_version_osx="10.9", + cmark_build_variant="Debug", + export_compile_commands=False, + reconfigure=False, + distcc=None, + sccache=None, + cmake_c_launcher=None, + cmake_cxx_launcher=None, + clang_user_visible_version=None, + build_ninja=False, + enable_asan=False, + enable_lsan=False, + enable_sanitize_coverage=False, + enable_tsan=False, + enable_ubsan=False) + + # Setup shell + shell.dry_run = True + self._orig_stdout = sys.stdout + self._orig_stderr = sys.stderr + self.stdout = StringIO() + self.stderr = StringIO() + sys.stdout = self.stdout + sys.stderr = self.stderr + + def tearDown(self): + shutil.rmtree(self.workspace.build_root) + shutil.rmtree(self.workspace.source_root) + sys.stdout = self._orig_stdout + sys.stderr = self._orig_stderr + shell.dry_run = False + self.workspace = None + self.toolchain = None + self.args = None + + def test_build(self): + # Test disabled until we've moved to cmake toolchains + True +# cmark = CMark( +# args=self.args, +# toolchain=self.toolchain, +# source_dir=self.workspace.source_root, +# build_dir=self.workspace.build_root) + +# cmark.build(host_target=self.host.name) +# _cmake = cmake.CMake(self.args, self.toolchain) + +# self.assertEqual(self.stdout.getvalue(), """\ +# + pushd {build_dir} +# + {cmake} -DCMAKE_BUILD_TYPE:STRING={build_variant} {cmake_args} {source_dir} +# + popd +# + {cmake} --build {build_dir} --config {build_variant} -- all +# """.format(build_dir=self.workspace.build_root, +# source_dir=self.workspace.source_root, +# cmake=self.toolchain.cmake, +# cmake_args=' '.join(_cmake.common_options()), +# build_variant=self.args.cmark_build_variant)) + + def test_should_test(self): + cmark = CMark( + args=argparse.Namespace(test_cmark=True, cross_compile_hosts=[]), + toolchain=self.toolchain, + source_dir=self.workspace.source_root, + build_dir=self.workspace.build_root) + + self.assertTrue(cmark.should_test(self.host.name)) + + def test_should_skip_test(self): + cmark = CMark( + args=argparse.Namespace(test_cmark=False, cross_compile_hosts=[]), + toolchain=self.toolchain, + source_dir=self.workspace.source_root, + build_dir=self.workspace.build_root) + + self.assertFalse(cmark.should_test(self.host.name)) + + def test_should_skip_test_cross_compile(self): + cmark = CMark( + args=argparse.Namespace(test_cmark=True, + cross_compile_hosts=[self.host.name]), + toolchain=self.toolchain, + source_dir=self.workspace.source_root, + build_dir=self.workspace.build_root) + + self.assertFalse(cmark.should_test(self.host.name)) diff --git a/utils/swift_build_support/tests/products/test_earlyswiftdriver.py b/utils/swift_build_support/tests/products/test_earlyswiftdriver.py new file mode 100644 index 0000000000000..d572b25b59066 --- /dev/null +++ b/utils/swift_build_support/tests/products/test_earlyswiftdriver.py @@ -0,0 +1,83 @@ +# tests/products/test_ninja.py ----------------------------------*- python -*- +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2021 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 +# ---------------------------------------------------------------------------- + +import argparse +import os +import shutil +import sys +import tempfile +import unittest +try: + # py2 + from StringIO import StringIO +except ImportError: + # py3 + from io import StringIO + +from swift_build_support import shell +from swift_build_support.products import EarlySwiftDriver +from swift_build_support.targets import StdlibDeploymentTarget +from swift_build_support.toolchain import host_toolchain +from swift_build_support.workspace import Workspace + + +class EarlySwiftDriverTestCase(unittest.TestCase): + + def setUp(self): + # Setup workspace + tmpdir1 = os.path.realpath(tempfile.mkdtemp()) + tmpdir2 = os.path.realpath(tempfile.mkdtemp()) + os.makedirs(os.path.join(tmpdir1, 'cmark')) + + self.workspace = Workspace(source_root=tmpdir1, + build_root=tmpdir2) + + self.host = StdlibDeploymentTarget.host_target() + + # Setup toolchain + self.toolchain = host_toolchain() + self.toolchain.cc = '/path/to/cc' + self.toolchain.cxx = '/path/to/cxx' + + self.cross_compile_hosts = ["macosx-arm64", "linux-x86_64", "linux-aarch64"] + + # Setup args + self.args = argparse.Namespace( + cross_compile_hosts=self.cross_compile_hosts) + + # Setup shell + shell.dry_run = True + self._orig_stdout = sys.stdout + self._orig_stderr = sys.stderr + self.stdout = StringIO() + self.stderr = StringIO() + sys.stdout = self.stdout + sys.stderr = self.stderr + + def tearDown(self): + shutil.rmtree(self.workspace.build_root) + shutil.rmtree(self.workspace.source_root) + sys.stdout = self._orig_stdout + sys.stderr = self._orig_stderr + shell.dry_run = False + self.workspace = None + self.toolchain = None + self.args = None + + def test_should_build(self): + early_swift_driver = EarlySwiftDriver( + args=self.args, + toolchain=self.toolchain, + source_dir=self.workspace.source_root, + build_dir=self.workspace.build_root) + + for target in self.cross_compile_hosts: + self.assertFalse(early_swift_driver.should_build(target)) diff --git a/utils/swift_build_support/tests/products/test_swift.py b/utils/swift_build_support/tests/products/test_swift.py index 318ba79d8264f..091452a4bd983 100644 --- a/utils/swift_build_support/tests/products/test_swift.py +++ b/utils/swift_build_support/tests/products/test_swift.py @@ -59,7 +59,8 @@ def setUp(self): force_optimized_typechecker=False, enable_stdlibcore_exclusivity_checking=False, enable_experimental_differentiable_programming=False, - enable_experimental_concurrency=False) + enable_experimental_concurrency=False, + enable_experimental_distributed=False) # Setup shell shell.dry_run = True @@ -92,6 +93,7 @@ def test_by_default_no_cmake_options(self): '-DSWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING:BOOL=FALSE', '-DSWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING:BOOL=FALSE', '-DSWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY:BOOL=FALSE', + '-DSWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED:BOOL=FALSE' ] self.assertEqual(set(swift.cmake_options), set(expected)) @@ -108,7 +110,8 @@ def test_swift_runtime_tsan(self): '-DSWIFT_FORCE_OPTIMIZED_TYPECHECKER:BOOL=FALSE', '-DSWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING:BOOL=FALSE', '-DSWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING:BOOL=FALSE', - '-DSWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY:BOOL=FALSE' + '-DSWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY:BOOL=FALSE', + '-DSWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED:BOOL=FALSE' ] self.assertEqual(set(swift.cmake_options), set(flags_set)) @@ -334,3 +337,16 @@ def test_experimental_concurrency_flags(self): 'TRUE'], [x for x in swift.cmake_options if 'DSWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY' in x]) + + def test_experimental_distributed_flags(self): + self.args.enable_experimental_distributed = True + swift = Swift( + args=self.args, + toolchain=self.toolchain, + source_dir='/path/to/src', + build_dir='/path/to/build') + self.assertEqual( + ['-DSWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED:BOOL=' + 'TRUE'], + [x for x in swift.cmake_options + if 'DSWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED' in x]) diff --git a/utils/swift_build_support/tests/test_cmake.py b/utils/swift_build_support/tests/test_cmake.py index 8b84dc06c230c..937e531d5e4c1 100644 --- a/utils/swift_build_support/tests/test_cmake.py +++ b/utils/swift_build_support/tests/test_cmake.py @@ -47,7 +47,10 @@ def default_args(self): """ return Namespace(host_cc="/path/to/clang", host_cxx="/path/to/clang++", + host_swiftc="/path/to/swiftc", host_libtool="/path/to/libtool", + host_ar="/path/to/ar", + host_ranlib="/path/to/ranlib", enable_asan=False, enable_ubsan=False, enable_tsan=False, @@ -80,7 +83,10 @@ def cmake(self, args): toolchain = host_toolchain() toolchain.cc = args.host_cc toolchain.cxx = args.host_cxx + toolchain.swiftc = args.host_swiftc toolchain.libtool = args.host_libtool + toolchain.ar = args.host_ar + toolchain.ranlib = args.host_ranlib if args.distcc: toolchain.distcc = self.mock_distcc_path() if args.sccache: @@ -96,7 +102,10 @@ def test_common_options_defaults(self): ["-G", "Ninja", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_asan(self): @@ -109,7 +118,10 @@ def test_common_options_asan(self): "-DLLVM_USE_SANITIZER=Address", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_ubsan(self): @@ -122,7 +134,10 @@ def test_common_options_ubsan(self): "-DLLVM_USE_SANITIZER=Undefined", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_tsan(self): @@ -135,7 +150,10 @@ def test_common_options_tsan(self): "-DLLVM_USE_SANITIZER=Thread", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_asan_ubsan(self): @@ -149,7 +167,10 @@ def test_common_options_asan_ubsan(self): "-DLLVM_USE_SANITIZER=Address;Undefined", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_ubsan_tsan(self): @@ -163,7 +184,10 @@ def test_common_options_ubsan_tsan(self): "-DLLVM_USE_SANITIZER=Undefined;Thread", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_asan_ubsan_tsan(self): @@ -178,7 +202,10 @@ def test_common_options_asan_ubsan_tsan(self): "-DLLVM_USE_SANITIZER=Address;Undefined;Thread", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_lsan(self): @@ -191,7 +218,10 @@ def test_common_options_lsan(self): "-DLLVM_USE_SANITIZER=Leaks", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_coverage_sanitizer(self): @@ -204,7 +234,10 @@ def test_common_options_coverage_sanitizer(self): "-DLLVM_USE_SANITIZE_COVERAGE=ON", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_export_compile_commands(self): @@ -217,7 +250,10 @@ def test_common_options_export_compile_commands(self): "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_distcc(self): @@ -231,7 +267,10 @@ def test_common_options_distcc(self): "-DCMAKE_CXX_COMPILER_LAUNCHER:PATH=" + self.mock_distcc_path(), "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_sccache(self): @@ -245,7 +284,10 @@ def test_common_options_sccache(self): "-DCMAKE_CXX_COMPILER_LAUNCHER:PATH=" + self.mock_sccache_path(), "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_launcher(self): @@ -262,7 +304,10 @@ def test_common_options_launcher(self): "-DCMAKE_CXX_COMPILER_LAUNCHER:PATH=" + cmake_cxx_launcher, "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_xcode(self): @@ -274,7 +319,10 @@ def test_common_options_xcode(self): ["-G", "Xcode", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_CONFIGURATION_TYPES=" + "Debug;Release;MinSizeRel;RelWithDebInfo"]) @@ -287,7 +335,10 @@ def test_common_options_clang_compiler_version(self): ["-G", "Ninja", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_clang_user_visible_version(self): @@ -299,7 +350,10 @@ def test_common_options_clang_user_visible_version(self): ["-G", "Ninja", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DLLVM_VERSION_MAJOR:STRING=9", "-DLLVM_VERSION_MINOR:STRING=0", "-DLLVM_VERSION_PATCH:STRING=0", @@ -317,7 +371,10 @@ def test_common_options_build_ninja(self): ["-G", "Ninja", "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_MAKE_PROGRAM=" + self.which_ninja(args)]) def test_common_options_full(self): @@ -340,7 +397,10 @@ def test_common_options_full(self): "-DCMAKE_CXX_COMPILER_LAUNCHER:PATH=" + self.mock_distcc_path(), "-DCMAKE_C_COMPILER:PATH=/path/to/clang", "-DCMAKE_CXX_COMPILER:PATH=/path/to/clang++", + "-DCMAKE_Swift_COMPILER:PATH=/path/to/swiftc", "-DCMAKE_LIBTOOL:PATH=/path/to/libtool", + "-DCMAKE_AR:PATH=/path/to/ar", + "-DCMAKE_RANLIB:PATH=/path/to/ranlib", "-DCMAKE_CONFIGURATION_TYPES=" + "Debug;Release;MinSizeRel;RelWithDebInfo", "-DLLVM_VERSION_MAJOR:STRING=9", diff --git a/utils/swift_build_support/tests/test_host_specific_configuration.py b/utils/swift_build_support/tests/test_host_specific_configuration.py index 5b61db9305bcf..dc4370f950ff9 100644 --- a/utils/swift_build_support/tests/test_host_specific_configuration.py +++ b/utils/swift_build_support/tests/test_host_specific_configuration.py @@ -101,6 +101,17 @@ def test_should_only_configure_when_cross_compiling_different_stdlib_targets(sel self.assertEqual(len(hsc.swift_stdlib_build_targets), 0) + def test_should_not_build_stdlib_when_targets_are_empty(self): + args = self.default_args() + args.host_target = 'macosx-x86_64' + args.stdlib_deployment_targets = [] + + hsc = HostSpecificConfiguration('macosx-arm64', args) + + self.assertEqual(len(hsc.sdks_to_configure), 0) + + self.assertEqual(len(hsc.swift_stdlib_build_targets), 0) + def generate_should_skip_building_platform( host_target, sdk_name, build_target, build_arg_name): def test(self): diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 4a56f6263e08c..b45976f3ce5f8 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -10,16 +10,24 @@ "remote": { "id": "apple/swift-llbuild" } }, "swift-argument-parser": { "remote": { "id": "apple/swift-argument-parser" } }, + "swift-atomics": { + "remote": { "id": "apple/swift-atomics" } }, + "swift-collections": { + "remote": { "id": "apple/swift-collections" } }, "swift-crypto": { "remote": { "id": "apple/swift-crypto" } }, "swift-driver": { "remote": { "id": "apple/swift-driver" } }, + "swift-numerics": { + "remote": { "id": "apple/swift-numerics" } }, "swift-tools-support-core": { "remote": { "id": "apple/swift-tools-support-core" } }, "swiftpm": { "remote": { "id": "apple/swift-package-manager" } }, "swift-syntax": { "remote": { "id": "apple/swift-syntax" } }, + "swift-system": { + "remote": { "id": "apple/swift-system" } }, "swift-stress-tester": { "remote": { "id": "apple/swift-stress-tester" } }, "swift-corelibs-xctest": { @@ -51,6 +59,8 @@ "remote": { "id": "apple/sourcekit-lsp" } }, "swift-format": { "remote": { "id": "apple/swift-format" } }, + "swift-installer-scripts": { + "remote": { "id": "apple/swift-installer-scripts" } }, "llvm-project": { "remote": { "id": "apple/llvm-project" } } }, @@ -65,10 +75,14 @@ "llbuild": "main", "swift-tools-support-core": "main", "swiftpm": "main", - "swift-argument-parser": "0.4.3", + "swift-argument-parser": "0.4.4", + "swift-atomics": "0.0.3", + "swift-collections": "0.0.5", "swift-crypto": "1.1.5", "swift-driver": "main", + "swift-numerics": "0.1.0", "swift-syntax": "main", + "swift-system": "0.0.2", "swift-stress-tester": "main", "swift-corelibs-xctest": "main", "swift-corelibs-foundation": "main", @@ -78,10 +92,11 @@ "ninja": "release", "icu": "release-65-1", "yams": "4.0.2", - "cmake": "v3.16.5", + "cmake": "v3.19.6", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "release/5.5": { @@ -93,6 +108,64 @@ "llbuild": "release/5.5", "swift-tools-support-core": "release/5.5", "swiftpm": "release/5.5", + "swift-argument-parser": "0.4.3", + "swift-crypto": "1.1.5", + "swift-driver": "release/5.5", + "swift-syntax": "release/5.5", + "swift-stress-tester": "release/5.5", + "swift-corelibs-xctest": "release/5.5", + "swift-corelibs-foundation": "release/5.5", + "swift-corelibs-libdispatch": "release/5.5", + "swift-integration-tests": "release/5.5", + "swift-xcode-playground-support": "release/5.5", + "ninja": "release", + "icu": "release-65-1", + "yams": "4.0.2", + "cmake": "v3.16.5", + "indexstore-db": "release/5.5", + "sourcekit-lsp": "release/5.5", + "swift-format": "main", + "swift-installer-scripts": "main" + } + }, + "release/5.5-08092021": { + "aliases": ["release/5.5-08092021", "swift/release/5.5-08092021"], + "repos": { + "llvm-project": "swift/release/5.5-08092021", + "swift": "release/5.5-08092021", + "cmark": "release/5.5-08092021", + "llbuild": "release/5.5-08092021", + "swift-tools-support-core": "release/5.5-08092021", + "swiftpm": "release/5.5-08092021", + "swift-argument-parser": "0.4.3", + "swift-crypto": "1.1.5", + "swift-driver": "release/5.5-08092021", + "swift-syntax": "release/5.5-08092021", + "swift-stress-tester": "release/5.5-08092021", + "swift-corelibs-xctest": "release/5.5-08092021", + "swift-corelibs-foundation": "release/5.5-08092021", + "swift-corelibs-libdispatch": "release/5.5-08092021", + "swift-integration-tests": "release/5.5-08092021", + "swift-xcode-playground-support": "release/5.5-08092021", + "ninja": "release", + "icu": "release-65-1", + "yams": "4.0.2", + "cmake": "v3.16.5", + "indexstore-db": "release/5.5-08092021", + "sourcekit-lsp": "release/5.5-08092021", + "swift-format": "main", + "swift-installer-scripts": "main" + } + }, + "concurrency-5.5-abi-2": { + "aliases": ["concurrency-5.5-abi-2"], + "repos": { + "llvm-project": "swift/release/5.5", + "swift": "concurrency-5.5-abi-2", + "cmark": "release/5.5", + "llbuild": "release/5.5", + "swift-tools-support-core": "release/5.5", + "swiftpm": "release/5.5", "swift-argument-parser": "0.4.1", "swift-crypto": "1.1.5", "swift-driver": "release/5.5", @@ -109,22 +182,27 @@ "cmake": "v3.16.5", "indexstore-db": "release/5.5", "sourcekit-lsp": "release/5.5", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "rebranch": { - "aliases": ["rebranch", "swift/rebranch"], + "aliases": ["rebranch", "swift/rebranch", "stable/20210726"], "repos": { - "llvm-project": "swift/rebranch", + "llvm-project": "stable/20210726", "swift": "rebranch", "cmark": "main", "llbuild": "main", "swift-tools-support-core": "main", "swiftpm": "main", - "swift-argument-parser": "0.3.0", + "swift-argument-parser": "0.4.4", + "swift-atomics": "0.0.3", + "swift-collections": "0.0.5", "swift-crypto": "1.1.5", "swift-driver": "main", + "swift-numerics": "0.1.0", "swift-syntax": "main", + "swift-system": "0.0.2", "swift-stress-tester": "main", "swift-corelibs-xctest": "main", "swift-corelibs-foundation": "main", @@ -133,29 +211,33 @@ "swift-xcode-playground-support": "main", "ninja": "release", "icu": "release-65-1", - "yams": "3.0.1", - "cmake": "v3.16.5", + "yams": "4.0.2", + "cmake": "v3.19.6", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "next" : { "aliases": ["next", "master-next", - "swift/next", "stable-next", "upstream", "next-upstream", "upstream-with-swift"], "repos": { - "llvm-project": "swift/next", + "llvm-project": "next", "swift": "next", "cmark": "main", "llbuild": "main", "swift-tools-support-core": "main", "swiftpm": "main", - "swift-argument-parser": "0.4.3", - "swift-crypto": "1.1.5", + "swift-argument-parser": "0.4.4", + "swift-atomics": "0.0.3", + "swift-collections": "0.0.5", + "swift-crypto": "1.1.5", "swift-driver": "main", + "swift-numerics": "0.1.0", "swift-syntax": "main", + "swift-system": "0.0.2", "swift-stress-tester": "main", "swift-corelibs-xctest": "main", "swift-corelibs-foundation": "main", @@ -165,10 +247,11 @@ "ninja": "release", "icu": "release-65-1", "yams": "4.0.2", - "cmake": "v3.16.5", + "cmake": "v3.19.6", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "swift-3.0-branch" : { @@ -188,7 +271,8 @@ "ninja": "release", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "swift-3.1-branch" : { @@ -209,7 +293,8 @@ "ninja": "release", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "swift-4.0-branch" : { @@ -230,7 +315,8 @@ "ninja": "release", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "swift-4.1-branch" : { @@ -251,7 +337,8 @@ "ninja": "release", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "swift-4.2-branch" : { @@ -272,7 +359,8 @@ "ninja": "release", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "swift-5.0-branch" : { @@ -294,7 +382,8 @@ "icu": "release-61-1", "indexstore-db": "main", "sourcekit-lsp": "main", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "swift-5.1-branch" : { @@ -316,7 +405,8 @@ "icu": "release-61-1", "indexstore-db": "swift-5.1-branch", "sourcekit-lsp": "swift-5.1-branch", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "swift-5.2-branch": { @@ -340,7 +430,8 @@ "cmake": "v3.15.1", "indexstore-db": "swift-5.2-branch", "sourcekit-lsp": "swift-5.2-branch", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "release/5.3": { @@ -364,7 +455,8 @@ "cmake": "v3.16.5", "indexstore-db": "release/5.3", "sourcekit-lsp": "release/5.3", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "release/5.4": { @@ -391,7 +483,8 @@ "cmake": "v3.16.5", "indexstore-db": "release/5.4", "sourcekit-lsp": "release/5.4", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } }, "release/5.3-20201012": { @@ -415,7 +508,8 @@ "cmake": "v3.16.5", "indexstore-db": "release/5.3-20201012", "sourcekit-lsp": "release/5.3-20201012", - "swift-format": "main" + "swift-format": "main", + "swift-installer-scripts": "main" } } } diff --git a/utils/update_checkout/update_checkout/update_checkout.py b/utils/update_checkout/update_checkout/update_checkout.py index 5a51b927a67b3..820199126b5ae 100755 --- a/utils/update_checkout/update_checkout/update_checkout.py +++ b/utils/update_checkout/update_checkout/update_checkout.py @@ -260,7 +260,7 @@ def update_all_repositories(args, config, scheme_name, cross_repos_pr): def obtain_additional_swift_sources(pool_args): (args, repo_name, repo_info, repo_branch, remote, with_ssh, scheme_name, - skip_history, skip_repository_list) = pool_args + skip_history, skip_tags, skip_repository_list) = pool_args env = dict(os.environ) env.update({'GIT_TERMINAL_PROMPT': 0}) @@ -270,14 +270,14 @@ def obtain_additional_swift_sources(pool_args): print("Cloning '" + repo_name + "'") if skip_history: - shell.run(['git', 'clone', - '--recursive', '--depth', '1', '--branch', - repo_branch, remote, repo_name], + shell.run(['git', 'clone', '--recursive', '--depth', '1', + '--branch', repo_branch, remote, repo_name] + + (['--no-tags'] if skip_tags else []), env=env, echo=True) else: - shell.run(['git', 'clone', - '--recursive', remote, repo_name], + shell.run(['git', 'clone', '--recursive', remote, repo_name] + + (['--no-tags'] if skip_tags else []), env=env, echo=True) if scheme_name: @@ -297,7 +297,8 @@ def obtain_additional_swift_sources(pool_args): def obtain_all_additional_swift_sources(args, config, with_ssh, scheme_name, - skip_history, skip_repository_list): + skip_history, skip_tags, + skip_repository_list): pool_args = [] with shell.pushd(args.source_root, dry_run=False, echo=False): @@ -342,7 +343,7 @@ def obtain_all_additional_swift_sources(args, config, with_ssh, scheme_name, continue pool_args.append([args, repo_name, repo_info, repo_branch, remote, - with_ssh, scheme_name, skip_history, + with_ssh, scheme_name, skip_history, skip_tags, skip_repository_list]) if not pool_args: @@ -470,6 +471,10 @@ def main(): "--skip-history", help="Skip histories when obtaining sources", action="store_true") + parser.add_argument( + "--skip-tags", + help="Skip tags when obtaining sources", + action="store_true") parser.add_argument( "--skip-repository", metavar="DIRECTORY", @@ -548,6 +553,7 @@ def main(): clone = args.clone clone_with_ssh = args.clone_with_ssh skip_history = args.skip_history + skip_tags = args.skip_tags scheme = args.scheme github_comment = args.github_comment @@ -584,6 +590,7 @@ def main(): clone_with_ssh, scheme, skip_history, + skip_tags, skip_repo_list) # Quick check whether somebody is calling update in an empty directory diff --git a/utils/viewcfg b/utils/viewcfg index cfbb31bf2ff25..146fc267b1f42 100755 --- a/utils/viewcfg +++ b/utils/viewcfg @@ -82,6 +82,7 @@ def parse_args(): Simple regex based transform of SIL and LLVM-IR into graphviz form. """) parser.add_argument("output_suffix", nargs='?') + parser.add_argument("--disable_inst_dump", action='store_true') parser.add_argument("--input_file", type=argparse.FileType('r'), default=sys.stdin) parser.add_argument("--renderer", choices=["Graphviz", "dot"], @@ -140,7 +141,7 @@ def main(): if adj_name not in block_map: new_blocks[adj_name] = Block(adj_name, None) - block_map = dict(block_map.items() + new_blocks.items()) + block_map.update(new_blocks.items()) # Add missing edges if we didn't see a successor in the terminator # but the block is mentioned in the pred list of the successor. @@ -159,15 +160,15 @@ def main(): node [fontname = "{font}"]; edge [fontname = "{font}"];""".format(font=fontname)) for block in block_list: - if block.content is not None: + if block.content is None or args.disable_inst_dump: out_file.write( "\tNode" + str(block.index) + - " [shape=record,label=\"{" + block.content + "}\"];\n") + " [shape=record,color=gray,fontcolor=gray,label=\"{" + + block.name + "}\"];\n") else: out_file.write( "\tNode" + str(block.index) + - " [shape=record,color=gray,fontcolor=gray,label=\"{" + - block.name + "}\"];\n") + " [shape=record,label=\"{" + block.content + "}\"];\n") for succ_name in block.get_succs(): succ_block = block_map[succ_name] diff --git a/validation-test/BuildSystem/CMakeLists.txt b/validation-test/BuildSystem/CMakeLists.txt new file mode 100644 index 0000000000000..b1ec0895e14ab --- /dev/null +++ b/validation-test/BuildSystem/CMakeLists.txt @@ -0,0 +1,10 @@ + +# Only test this if we found a Swift compiler. Currently only enable this test +# if we build on Darwin since we do not have a host toolchain available when +# compiling on the bots for Linux meaning this path would not be tested. +if (CMAKE_Swift_COMPILER AND APPLE) + # We do not support this with the Xcode generator currently. + if (NOT CMAKE_GENERATOR STREQUAL "Xcode") + add_subdirectory(swift-cmake) + endif() +endif() diff --git a/validation-test/BuildSystem/android_cross_compile.test b/validation-test/BuildSystem/android_cross_compile.test new file mode 100644 index 0000000000000..8a35f88ae549e --- /dev/null +++ b/validation-test/BuildSystem/android_cross_compile.test @@ -0,0 +1,11 @@ +# REQUIRES: standalone_build +# UNSUPPORTED: OS=macosx +# UNSUPPORTED: OS=ios +# UNSUPPORTED: OS=tvos +# UNSUPPORTED: OS=watchos + +# RUN: %empty-directory(%t) +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --cmake %cmake --libdispatch --cross-compile-hosts=android-aarch64 --skip-local-build --android --android-ndk %t/ndk/ --android-arch aarch64 --android-icu-uc %t/lib/libicuuc.so --android-icu-uc-include %t/include/ --android-icu-i18n %t/lib/libicui18n.so --android-icu-i18n-include %t/include/ --android-icu-data %t/lib/libicudata.so 2>&1 | %FileCheck %s + +# CHECK: -DCMAKE_Swift_FLAGS{{.*}}-target {{.*}}unknown-linux-android{{.*}} -sdk +# CHECK: -DCMAKE_SYSTEM_NAME=Android {{.*}} -DCMAKE_ANDROID_NDK diff --git a/validation-test/BuildSystem/cmark_crosscompile_using_toolchain_always.test b/validation-test/BuildSystem/cmark_crosscompile_using_toolchain_always.test new file mode 100644 index 0000000000000..25dbee7709ea1 --- /dev/null +++ b/validation-test/BuildSystem/cmark_crosscompile_using_toolchain_always.test @@ -0,0 +1,9 @@ +# REQUIRES: standalone_build +# REQUIRES: OS=macosx + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-llvm --skip-build-swift 2>&1 | %FileCheck %s + +# CHECK: DRY_RUN! Writing Toolchain file to path:{{.*}}BuildScriptToolchain.cmake +# CHECK: cmake {{.*}}-DCMAKE_TOOLCHAIN_FILE:PATH={{.*}}BuildScriptToolchain.cmake {{.*}}cmark diff --git a/validation-test/BuildSystem/core_lipo_and_non_core_epilogue_opts.test b/validation-test/BuildSystem/core_lipo_and_non_core_epilogue_opts.test new file mode 100644 index 0000000000000..00f25e08b229b --- /dev/null +++ b/validation-test/BuildSystem/core_lipo_and_non_core_epilogue_opts.test @@ -0,0 +1,16 @@ +# REQUIRES: standalone_build +# REQUIRES: OS=macosx + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --cmake %cmake --infer --swiftpm --verbose-build 2>&1 | %FileCheck %s + +# Make sure we build swift, llvm, llbuild, then do core lipo, then build +# swiftpm, and finally finish by running the rest of the epilogue operations. + +# CHECK: cmake -G Ninja {{.*}}llvm-project/llvm +# CHECK: cmake -G Ninja {{.*}}swift +# CHECK: --only-execute {{.*}}-llbuild-build +# CHECK: --only-execute merged-hosts-lipo-core +# CHECK: --- Building swiftpm --- +# CHECK: --only-execute merged-hosts-lipo diff --git a/validation-test/BuildSystem/default_build_still_performs_epilogue_opts_after_split.test b/validation-test/BuildSystem/default_build_still_performs_epilogue_opts_after_split.test new file mode 100644 index 0000000000000..93392324307c8 --- /dev/null +++ b/validation-test/BuildSystem/default_build_still_performs_epilogue_opts_after_split.test @@ -0,0 +1,13 @@ +# REQUIRES: standalone_build + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --verbose-build --dry-run --cmake %cmake 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --verbose-build --dry-run --cmake %cmake --infer 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --verbose-build --dry-run --cmake %cmake --infer --swiftpm 2>&1 | %FileCheck %s + +# Make sure we run the merged hosts lipo step regardless of whether or not we +# are building anything from the 2nd build-script-impl phase with or without +# infer. + +# CHECK: --only-execute merged-hosts-lipo \ No newline at end of file diff --git a/validation-test/BuildSystem/dsymutil_jobs.test b/validation-test/BuildSystem/dsymutil_jobs.test index d20190363a36f..c0f4d70875ad9 100644 --- a/validation-test/BuildSystem/dsymutil_jobs.test +++ b/validation-test/BuildSystem/dsymutil_jobs.test @@ -1,10 +1,12 @@ +# REQUIRES: standalone_build,OS=macosx + # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --darwin-install-extract-symbols --dsymutil-jobs 5 --cmake %cmake 2>&1 | %FileCheck %s - -# REQUIRES: standalone_build,OS=macosx +# RUN: mkdir -p %t/destdir +# RUN: mkdir -p %t/symroot/macosx-%target-cpu +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --enable-extract-symbol-dry-run-test=1 --host-target=macosx-%target-cpu --darwin-install-extract-symbols --dsymutil-jobs 5 --cmake %cmake --install-symroot=%t/symroot --install-destdir=%t/destdir --toolchain-prefix= 2>&1 | %FileCheck %s # CHECK: --- Extracting symbols --- # CHECK: { "command": "dsymutil", "start": " -# CHECK-NEXT: xargs -n 1 -P 5 dsymutil -# CHECK-NEXT: { "command": "dsymutil", "end": " +# CHECK: xargs -n 1 -P 5 {{.*}}dsymutil +# CHECK: { "command": "dsymutil", "end": " diff --git a/validation-test/BuildSystem/extractsymbols-darwin-symroot-path-filters.test b/validation-test/BuildSystem/extractsymbols-darwin-symroot-path-filters.test new file mode 100644 index 0000000000000..997c3ed39fe22 --- /dev/null +++ b/validation-test/BuildSystem/extractsymbols-darwin-symroot-path-filters.test @@ -0,0 +1,111 @@ +# REQUIRES: standalone_build +# REQUIRES: OS=macosx + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: split-file %s %t + +# Even though we are running build-script with dry-run, +# symbol extraction runs real commands against the file system. +# Thus we generate a series of files +# to exercise the filtering logic +# RUN: mkdir -p %t/destdir/bin +# RUN: mkdir -p %t/destdir/lib +# RUN: %swiftc_driver %t/hello.swift -o %t/destdir/bin/swift-demangle +# RUN: %swiftc_driver %t/hello.swift -o %t/destdir/bin/swift-def-to-yaml-converter +# RUN: ln -s %t/destdir/swift-demangle %t/destdir/bin/swift-api-digester +# RUN: cp %t/swift-util.py %t/destdir/bin +# RUN: chmod a+x %t/destdir/bin/swift-util.py +# RUN: %swiftc_driver %t/dylib.swift -emit-library -o %t/destdir/lib/libswiftDemangle.dylib +# RUN: %swiftc_driver %t/dylib.swift -emit-library -o %t/destdir/lib/lib_InternalSwiftScan.dylib +# RUN: %swiftc_driver %t/dylib.swift -emit-library -static -o %t/destdir/lib/libswiftASTSectionImporter.a +# RUN: mkdir -p %t/symroot/macosx-%target-cpu + +# test build-script-impl on its own +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script-impl --dry-run --enable-extract-symbol-dry-run-test=1 --build-dir=%t/build --workspace=%swift_src_root/.. --cmake %cmake --only-execute macosx-%target-cpu-extractsymbols --host-cc /usr/bin/true --darwin-install-extract-symbols=1 --host-target=macosx-%target-cpu --install-symroot=%t/symroot --install-destdir=%t/destdir --build-jobs=1 --darwin-symroot-path-filters="/lib/ /swift-demangle" 2>&1 | tee %t/build-script-impl-output.txt +# RUN: %FileCheck --input-file=%t/build-script-impl-output.txt %s +# RUN: %FileCheck --input-file=%t/build-script-impl-output.txt --check-prefixes CHECK-SKIPPED %s + +# ensure build-script pass the argument to build-script-impl +# RUN: %empty-directory(%t/symroot) +# RUN: mkdir -p %t/symroot/macosx-%target-cpu +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --enable-extract-symbol-dry-run-test=1 --cmake %cmake --darwin-install-extract-symbols=1 --install-destdir=%t/destdir --toolchain-prefix="" --install-symroot=%t/symroot --darwin-symroot-path-filters="/lib/ /swift-demangle" --jobs=1 --host-target=macosx-%target-cpu 2>&1 | tee %t/build-script-output.txt +# RUN: %FileCheck --input-file=%t/build-script-output.txt %s +# RUN: %FileCheck --input-file=%t/build-script-output.txt --check-prefixes CHECK-SKIPPED %s + +# ensure we get all the values if we specify the flag multiple times +# RUN: %empty-directory(%t/symroot) +# RUN: mkdir -p %t/symroot/macosx-%target-cpu +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --enable-extract-symbol-dry-run-test=1 --cmake %cmake --darwin-install-extract-symbols=1 --install-destdir=%t/destdir --toolchain-prefix="" --install-symroot=%t/symroot --darwin-symroot-path-filters="/lib/" --darwin-symroot-path-filters="/swift-demangle" --jobs=1 --host-target=macosx-%target-cpu 2>&1 | tee %t/build-script-output.txt +# RUN: %FileCheck --input-file=%t/build-script-output.txt %s +# RUN: %FileCheck --input-file=%t/build-script-output.txt --check-prefixes CHECK-SKIPPED %s + + +# CHECK: --- Extracting symbols --- + +# Ensure we copy all the files in lib and the swift-demangle executable +# CHECK-LABEL: cpio +# CHECK-DAG: swift-demangle +# CHECK-DAG: libswiftDemangle.dylib +# CHECK-DAG: lib_InternalSwiftScan.dylib + +# Ensure we generate symbols for the file in the symroot +# CHECK-LABEL: "command": "dsymutil", "start" +# CHECK-DAG: dsymutil {{.*}}swift-demangle +# CHECK-DAG: dsymutil {{.*}}libswiftDemangle.dylib +# CHECK-DAG: dsymutil {{.*}}lib_InternalSwiftScan.dylib + +# Ensure we strip the files in the installation directory +# (which are not subject to the filters) +# CHECK-LABEL: xcrun_find_tool strip +# CHECK-DAG: strip {{.*}}swift-demangle +# CHECK-DAG: strip {{.*}}swift-def-to-yaml-converter +# CHECK-DAG: strip {{.*}}libswiftDemangle.dylib +# CHECK-DAG: strip {{.*}}lib_InternalSwiftScan.dylib +# CHECK-DAG: strip {{.*}}libswiftASTSectionImporter.a +# CHECK-DAG: strip {{.*}}swift-util.py + +# Ensure we codesign dylibs +# CHECK-LABEL: xcrun_find_tool codesign +# CHECK-DAG: codesign {{.*}}libswiftDemangle.dylib +# CHECK-DAG: codesign {{.*}}lib_InternalSwiftScan.dylib + + +# CHECK-SKIPPED: --- Extracting symbols --- + +# Ensure we don't copy files not included by the filters +# CHECK-SKIPPED-LABEL: cpio +# CHECK-SKIPPED-NOT: swift-util.py +# CHECK-SKIPPED-NOT: swift-def-to-yaml-converter +# CHECK-SKIPPED-NOT: libswiftASTSectionImporter.a +# CHECK-SKIPPED-NOT: swift-api-digester + +# Ensure we don't generate symbols for files we did not copy +# CHECK-SKIPPED-LABEL: "command": "dsymutil", "start" +# CHECK-SKIPPED-NOT: dsymutil {{.*}}swift-def-to-yaml-converter +# CHECK-SKIPPED-NOT: dsymutil {{.*}}libswiftASTSectionImporter.a +# CHECK-SKIPPED-NOT: dsymutil {{.*}}swift-util.py +# CHECK-SKIPPED-NOT: dsymutil {{.*}}swift-api-digester + +# Ensure we don't strip symlinks +# CHECK-SKIPPED-LABEL: xcrun_find_tool strip +# CHECK-SKIPPED-NOT: strip {{.*}}swift-api-digester + +# Ensure we don't codesign executables, symlinks, +# static archives and python scripts +# CHECK-SKIPPED-LABEL: xcrun_find_tool codesign +# CHECK-SKIPPED-NOT: codesign {{.*}}swift-demangle +# CHECK-SKIPPED-NOT: codesign {{.*}}libswiftASTSectionImporter.a +# CHECK-SKIPPED-NOT: codesign {{.*}}swift-util.py +# CHECK-SKIPPED-NOT: codesign {{.*}}swift-api-digester +# CHECK-SKIPPED-NOT: codesign {{.*}}swift-def-to-yaml-converter + +#--- hello.swift +print("hello") + +#--- dylib.swift +func greet(person: String) -> String { + return "Hello \(person)" +} +#--- swift-util.py +print("hello") diff --git a/validation-test/BuildSystem/extractsymbols-default-behaviour.test b/validation-test/BuildSystem/extractsymbols-default-behaviour.test new file mode 100644 index 0000000000000..1ec173556f4f8 --- /dev/null +++ b/validation-test/BuildSystem/extractsymbols-default-behaviour.test @@ -0,0 +1,93 @@ +# REQUIRES: standalone_build +# REQUIRES: OS=macosx + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: split-file %s %t + +# Even though we are running build-script with dry-run, +# symbol extraction runs real commands against the file system. +# Thus we generate a series of files +# to target each of the cases handled by the code +# RUN: mkdir -p %t/destdir +# RUN: %swiftc_driver %t/hello.swift -o %t/destdir/swift-demangle +# RUN: ln -s %t/destdir/swift-demangle %t/destdir/swift-api-digester +# RUN: cp %t/swift-util.py %t/destdir/ +# RUN: chmod a+x %t/destdir/swift-util.py +# RUN: %swiftc_driver %t/dylib.swift -emit-library -o %t/destdir/libswiftDemangle.dylib +# RUN: %swiftc_driver %t/dylib.swift -emit-library -static -o %t/destdir/libswiftASTSectionImporter.a +# Targets marked with INSTALL_WITH_SHARED are executable (e.g. compatibility libraries) +# RUN: cp %t/destdir/libswiftASTSectionImporter.a %t/destdir/libswiftCompatibility51.a +# RUN: chmod a+x %t/destdir/libswiftCompatibility51.a +# RUN: mkdir -p %t/symroot/macosx-%target-cpu + +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script-impl --dry-run --enable-extract-symbol-dry-run-test=1 --build-dir=%t/build --workspace=%swift_src_root/.. --cmake %cmake --only-execute macosx-%target-cpu-extractsymbols --host-cc /usr/bin/true --darwin-install-extract-symbols=1 --host-target=macosx-%target-cpu --install-symroot=%t/symroot --install-destdir=%t/destdir --build-jobs=1 > %t/build-script-impl-output.txt 2>&1 +# RUN: %FileCheck --input-file=%t/build-script-impl-output.txt %s +# RUN: %FileCheck --input-file=%t/build-script-impl-output.txt --check-prefixes CHECK-SKIPPED %s + +# CHECK: --- Extracting symbols --- + +# Ensure we copy executable regular files to the symroot +# CHECK-LABEL: cpio +# CHECK-DAG: swift-demangle +# CHECK-DAG: swift-util.py +# CHECK-DAG: libswiftDemangle.dylib +# CHECK-DAG: libswiftCompatibility51.a + +# Ensure we extract symbols only for executables and +# and dylibs +# CHECK-LABEL: command": "dsymutil", "start" +# CHECK-DAG: dsymutil {{.*}}swift-demangle +# CHECK-DAG: dsymutil {{.*}}libswiftDemangle.dylib + +# Ensure we strip executables, shared libraries and static +# libraries +# CHECK-LABEL: xcrun_find_tool strip +# CHECK-DAG: strip {{.*}}swift-demangle +# CHECK-DAG: strip {{.*}}libswiftDemangle.dylib +# CHECK-DAG: strip {{.*}}libswiftASTSectionImporter.a +# CHECK-DAG: strip {{.*}}libswiftCompatibility51.a +# CHECK-DAG: strip {{.*}}swift-util.py + +# Ensure we codesign dylibds +# CHECK-LABEL: xcrun_find_tool codesign +# CHECK: codesign {{.*}}libswiftDemangle.dylib + +# CHECK-SKIPPED: --- Extracting symbols --- + +# Ensure symroot does not contain symlinks and static archives +# that are not executable +# CHECK-SKIPPED-LABEL: cpio +# CHECK-SKIPPED-NOT: swift-api-digester +# CHECK-SKIPPED-NOT: libswiftASTSectionImporter.a + +# Ensure we don't extract symbols for static archives, symlinks +# and Python scripts +# CHECK-SKIPPED-LABEL: command": "dsymutil", "start" +# CHECK-SKIPPED-NOT: dsymutil {{.*}}libswiftASTSectionImporter.a +# CHECK-SKIPPED-NOT: dsymutil {{.*}}libswiftCompatibility51.a +# CHECK-SKIPPED-NOT: dsymutil {{.*}}swift-util.py +# CHECK-SKIPPED-NOT: dsymutil {{.*}}swift-api-digester + +# Ensure we don't strip symlinks +# CHECK-SKIPPED-LABEL: xcrun_find_tool strip +# CHECK-SKIPPED-NOT: strip {{.*}}swift-api-digester + +# Ensure we don't codesign executables, symlinks, +# static archives and python scripts +# CHECK-SKIPPED-LABEL: xcrun_find_tool codesign +# CHECK-SKIPPED-NOT: codesign {{.*}}swift-demangle +# CHECK-SKIPPED-NOT: codesign {{.*}}libswiftASTSectionImporter.a +# CHECK-SKIPPED-NOT: codesign {{.*}}libswiftCompatibility51.a +# CHECK-SKIPPED-NOT: codesign {{.*}}swift-util.py +# CHECK-SKIPPED-NOT: codesign {{.*}}swift-api-digester + +#--- hello.swift +print("hello") + +#--- dylib.swift +func greet(person: String) -> String { + return "Hello \(person)" +} +#--- swift-util.py +print("hello") diff --git a/validation-test/BuildSystem/extractsymbols-dry-run-does-not-fail.test b/validation-test/BuildSystem/extractsymbols-dry-run-does-not-fail.test new file mode 100644 index 0000000000000..dc15c1170112d --- /dev/null +++ b/validation-test/BuildSystem/extractsymbols-dry-run-does-not-fail.test @@ -0,0 +1,24 @@ +# REQUIRES: standalone_build +# REQUIRES: OS=macosx + +# As a result of incremental changes, +# in dry-run mode the symbol extraction +# still runs some real commands against the installation +# and symbol directories (e.g. find and cpio) +# This test explictly checks that such commands +# do not cause build-script to fail when run +# against empty directories (which is the typical +# scenario in dry-run) +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: mkdir -p %t/destdir +# RUN: mkdir -p %t/symroot/macosx-%target-cpu +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script-impl --dry-run --build-dir=%t/build --workspace=%swift_src_root/.. --cmake %cmake --only-execute macosx-%target-cpu-extractsymbols --host-cc /usr/bin/true --darwin-install-extract-symbols=1 --host-target=macosx-%target-cpu --install-symroot=%t/symroot --install-destdir=%t/destdir --build-jobs=1 2>&1 | %FileCheck %s + +# CHECK: --- Extracting symbols --- + +# CHECK-NOT: {{^}}echo dsymutil + +# CHECK-NOT: {{^[^\+].*}}strip + +# CHECK-NOT: {{^[^\+].*}}codesign diff --git a/validation-test/BuildSystem/infer_dumps_deps_if_verbose_build.test b/validation-test/BuildSystem/infer_dumps_deps_if_verbose_build.test index 16fc073567452..26160000c2a3a 100644 --- a/validation-test/BuildSystem/infer_dumps_deps_if_verbose_build.test +++ b/validation-test/BuildSystem/infer_dumps_deps_if_verbose_build.test @@ -1,27 +1,41 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --verbose-build --dry-run --infer --swiftpm --cmake %cmake 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --verbose-build --dry-run --infer --swiftpm --cmake %cmake 2>&1 | %FileCheck %s # REQUIRES: standalone_build -# Build and install the SwiftPM dependencies first. -# -# CHECK: --- Installing cmark --- -# CHECK: --- Installing llvm --- -# CHECK: --- Installing swift --- -# CHECK: --- Installing llbuild --- - # Just make sure we compute the build graph/emit output. # # CHECK: -- Build Graph Inference -- # CHECK: Initial Product List: -# CHECK: cmark +# CHECK: earlyswiftdriver +# CHECK: -- Build Graph Inference -- +# CHECK-NEXT: Initial Product List: # CHECK: llvm # CHECK: swift +# CHECK-NOT: llbuild +# CHECK: -- Build Graph Inference -- +# CHECK-NEXT: Initial Product List: +# CHECK: swiftpm +# CHECK-NOT: llbuild # CHECK: Final Build Order: +# CHECK: earlyswiftdriver # CHECK: cmark # CHECK: llvm # CHECK: swift +# CHECK: llbuild +# CHECK: swiftpm + +# Ensure early SwiftDriver is built first +# +# CHECK: --- Building earlyswiftdriver --- + +# Build and install the SwiftPM dependencies first. +# +# CHECK: --- Installing cmark --- +# CHECK: --- Installing llvm --- +# CHECK: --- Installing swift --- +# CHECK: --- Installing llbuild --- # Then make sure we are installing/building SwiftPM last. # diff --git a/validation-test/BuildSystem/infer_implies_install_all.test b/validation-test/BuildSystem/infer_implies_install_all.test index 48b570c66ec12..16fee74e3d318 100644 --- a/validation-test/BuildSystem/infer_implies_install_all.test +++ b/validation-test/BuildSystem/infer_implies_install_all.test @@ -1,6 +1,6 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --infer --swiftpm --cmake %cmake 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --infer --swiftpm --cmake %cmake 2>&1 | %FileCheck %s # REQUIRES: standalone_build diff --git a/validation-test/BuildSystem/install_all.test b/validation-test/BuildSystem/install_all.test index 41b841ae9204e..37f2514d37132 100644 --- a/validation-test/BuildSystem/install_all.test +++ b/validation-test/BuildSystem/install_all.test @@ -1,6 +1,6 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake 2>&1 | %FileCheck %s # REQUIRES: standalone_build diff --git a/validation-test/BuildSystem/install_all_linux.test b/validation-test/BuildSystem/install_all_linux.test index 60dd5ab950ab0..1777a9a5091c6 100644 --- a/validation-test/BuildSystem/install_all_linux.test +++ b/validation-test/BuildSystem/install_all_linux.test @@ -1,6 +1,6 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --llbuild --swiftpm --foundation --libdispatch --cmake %cmake 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --llbuild --swiftpm --foundation --libdispatch --cmake %cmake 2>&1 | %FileCheck %s # REQUIRES: standalone_build # REQUIRES: OS=linux-gnu diff --git a/validation-test/BuildSystem/llvm-targets-options-for-cross-compile-hosts.test b/validation-test/BuildSystem/llvm-targets-options-for-cross-compile-hosts.test index b809a59ab4c68..d5e90278698b2 100644 --- a/validation-test/BuildSystem/llvm-targets-options-for-cross-compile-hosts.test +++ b/validation-test/BuildSystem/llvm-targets-options-for-cross-compile-hosts.test @@ -3,7 +3,7 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llvm-ninja-targets="lib/all clangDependencyScanning" --cross-compile-hosts=iphoneos-arm64e --cmake %cmake 2>&1 | %FileCheck --check-prefix=LLVM-NINJA-TARGETS-APPLY-TO-CROSS-COMPILE-HOSTS-TOO-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llvm-ninja-targets="lib/all clangDependencyScanning" --cross-compile-hosts=iphoneos-arm64e --cmake %cmake 2>&1 | %FileCheck --check-prefix=LLVM-NINJA-TARGETS-APPLY-TO-CROSS-COMPILE-HOSTS-TOO-CHECK %s # LLVM-NINJA-TARGETS-APPLY-TO-CROSS-COMPILE-HOSTS-TOO-CHECK: cmake --build {{.*}}/llvm-{{[^/]*}} lib/all clangDependencyScanning # LLVM-NINJA-TARGETS-APPLY-TO-CROSS-COMPILE-HOSTS-TOO-CHECK: cmake --build {{.*}}/llvm-iphoneos-arm64e {{.*}} lib/all clangDependencyScanning @@ -11,7 +11,7 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llvm-ninja-targets="lib/all clangDependencyScanning" --llvm-ninja-targets-for-cross-compile-hosts="bin/clang" --cross-compile-hosts=iphoneos-arm64e --cmake %cmake 2>&1 | %FileCheck --check-prefix=LLVM-NINJA-TARGETS-FOR-CROSS-COMPILE-HOSTS-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llvm-ninja-targets="lib/all clangDependencyScanning" --llvm-ninja-targets-for-cross-compile-hosts="bin/clang" --cross-compile-hosts=iphoneos-arm64e --cmake %cmake 2>&1 | %FileCheck --check-prefix=LLVM-NINJA-TARGETS-FOR-CROSS-COMPILE-HOSTS-CHECK %s # LLVM-NINJA-TARGETS-FOR-CROSS-COMPILE-HOSTS-CHECK: cmake --build {{.*}}/llvm-{{[^/]*}} lib/all clangDependencyScanning # LLVM-NINJA-TARGETS-FOR-CROSS-COMPILE-HOSTS-CHECK: cmake --build {{.*}}/llvm-iphoneos-arm64e {{.*}} bin/clang @@ -19,7 +19,7 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llvm-ninja-targets-for-cross-compile-hosts="bin/clang" --cross-compile-hosts=iphoneos-arm64e --cmake %cmake 2>&1 | %FileCheck --check-prefix=ONLY-LLVM-NINJA-TARGETS-FOR-CROSS-COMPILE-HOSTS-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llvm-ninja-targets-for-cross-compile-hosts="bin/clang" --cross-compile-hosts=iphoneos-arm64e --cmake %cmake 2>&1 | %FileCheck --check-prefix=ONLY-LLVM-NINJA-TARGETS-FOR-CROSS-COMPILE-HOSTS-CHECK %s # ONLY-LLVM-NINJA-TARGETS-FOR-CROSS-COMPILE-HOSTS-CHECK: cmake --build {{.*}}/llvm-{{[^/]*}} all # ONLY-LLVM-NINJA-TARGETS-FOR-CROSS-COMPILE-HOSTS-CHECK: cmake --build {{.*}}/llvm-iphoneos-arm64e {{.*}} bin/clang diff --git a/validation-test/BuildSystem/llvm-targets-options.test b/validation-test/BuildSystem/llvm-targets-options.test index db6b6130b7438..b164bdaf60d67 100644 --- a/validation-test/BuildSystem/llvm-targets-options.test +++ b/validation-test/BuildSystem/llvm-targets-options.test @@ -2,30 +2,30 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --build-llvm=0 --cmake %cmake 2>&1 | %FileCheck --check-prefix=BUILD-LLVM-0-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --build-llvm=0 --cmake %cmake 2>&1 | %FileCheck --check-prefix=BUILD-LLVM-0-CHECK %s # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --build-llvm=0 --llvm-ninja-targets="lib/all clangDependencyScanning" --cmake %cmake 2>&1 | %FileCheck --check-prefix=BUILD-LLVM-0-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --build-llvm=0 --llvm-ninja-targets="lib/all clangDependencyScanning" --cmake %cmake 2>&1 | %FileCheck --check-prefix=BUILD-LLVM-0-CHECK %s # BUILD-LLVM-0-CHECK: cmake --build {{.*}}/llvm-{{[^/]*}} clean # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-CHECK %s # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build --llvm-ninja-targets="lib/all bin/clang" --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build --llvm-ninja-targets="lib/all bin/clang" --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-CHECK %s # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build-llvm --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build-llvm --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-CHECK %s # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build-llvm --llvm-ninja-targets="bin/clang clangDependencyScanning" --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build-llvm --llvm-ninja-targets="bin/clang clangDependencyScanning" --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-CHECK %s # SKIP-BUILD-CHECK: cmake --build {{.*}}/llvm-{{[^/]*}} llvm-tblgen clang-resource-headers intrinsics_gen clang-tablegen-targets # SKIP-BUILD-CHECK-SAME: FileCheck not llvm-nm @@ -33,7 +33,7 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build-llvm --build-toolchain-only=1 --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-LLVM-BUILD-TOOLCHAIN-ONLY-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --skip-build-llvm --build-toolchain-only=1 --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-BUILD-LLVM-BUILD-TOOLCHAIN-ONLY-CHECK %s # SKIP-BUILD-LLVM-BUILD-TOOLCHAIN-ONLY-CHECK: cmake --build {{.*}}/llvm-{{[^/]*}} llvm-tblgen clang-resource-headers intrinsics_gen clang-tablegen-targets # SKIP-BUILD-LLVM-BUILD-TOOLCHAIN-ONLY-CHECK-NOT: FileCheck not llvm-nm @@ -41,7 +41,7 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llvm-ninja-targets="lib/all clangDependencyScanning" --cmake %cmake 2>&1 | %FileCheck --check-prefix=LLVM-NINJA-TARGETS-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llvm-ninja-targets="lib/all clangDependencyScanning" --cmake %cmake 2>&1 | %FileCheck --check-prefix=LLVM-NINJA-TARGETS-CHECK %s # LLVM-NINJA-TARGETS-CHECK: cmake --build {{.*}}/llvm-{{[^/]*}} lib/all clangDependencyScanning diff --git a/validation-test/BuildSystem/reconfigure-passed-to-build-script-impl.test b/validation-test/BuildSystem/reconfigure-passed-to-build-script-impl.test new file mode 100644 index 0000000000000..29e291394f408 --- /dev/null +++ b/validation-test/BuildSystem/reconfigure-passed-to-build-script-impl.test @@ -0,0 +1,7 @@ +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --reconfigure --verbose --cmake %cmake 2>&1| %FileCheck %s + +# REQUIRES: standalone_build + +# CHECK: build-script-impl{{.*}} --reconfigure diff --git a/validation-test/BuildSystem/relocate_xdg_cache_home_under_build_subdir.test b/validation-test/BuildSystem/relocate_xdg_cache_home_under_build_subdir.test index d0f7b8f9f00c5..1bf4fbde39ce9 100644 --- a/validation-test/BuildSystem/relocate_xdg_cache_home_under_build_subdir.test +++ b/validation-test/BuildSystem/relocate_xdg_cache_home_under_build_subdir.test @@ -1,6 +1,6 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --llbuild --swiftpm --foundation --libdispatch --relocate-xdg-cache-home-under-build-subdir --verbose-build --cmake %cmake 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --llbuild --swiftpm --foundation --libdispatch --relocate-xdg-cache-home-under-build-subdir --verbose-build --cmake %cmake 2>&1 | %FileCheck %s # REQUIRES: standalone_build # REQUIRES: OS=linux-gnu diff --git a/validation-test/BuildSystem/skip-local-build.test-sh b/validation-test/BuildSystem/skip-local-build.test-sh index b658957c70a85..e80f5afae2832 100644 --- a/validation-test/BuildSystem/skip-local-build.test-sh +++ b/validation-test/BuildSystem/skip-local-build.test-sh @@ -1,8 +1,10 @@ # REQUIRES: standalone_build -# -# RUN: %swift_src_root/utils/build-script --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG + +# RUN: %empty-directory(%t) +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --cmake %cmake --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG # CONFIG: "skip_local_build": true -# RUN: %swift_src_root/utils/build-script --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY +# RUN: %empty-directory(%t) +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --cmake %cmake --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY # DRY: build-script-impl # DRY-SAME: --skip-local-build diff --git a/validation-test/BuildSystem/skip_clean_corelibs.test b/validation-test/BuildSystem/skip_clean_corelibs.test new file mode 100644 index 0000000000000..173f3f829f944 --- /dev/null +++ b/validation-test/BuildSystem/skip_clean_corelibs.test @@ -0,0 +1,27 @@ +# REQUIRES: standalone_build +# UNSUPPORTED: OS=macosx +# UNSUPPORTED: OS=ios +# UNSUPPORTED: OS=tvos +# UNSUPPORTED: OS=watchos + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --xctest --cmake %cmake 2>&1 | %FileCheck --check-prefix=CLEAN-CORELIBS-CHECK %s + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --xctest --skip-clean-libdispatch --skip-clean-foundation --skip-clean-xctest --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-CLEAN-CORELIBS-CHECK %s + +# CLEAN-CORELIBS-CHECK: Cleaning the libdispatch build directory +# CLEAN-CORELIBS-CHECK-NEXT: rm -rf +# CLEAN-CORELIBS-CHECK: Cleaning the Foundation build directory +# CLEAN-CORELIBS-CHECK-NEXT: rm -rf +# CLEAN-CORELIBS-CHECK: Cleaning the XCTest build directory +# CLEAN-CORELIBS-CHECK-NEXT: rm -rf + +# SKIP-CLEAN-CORELIBS-CHECK-NOT: Cleaning the libdispatch build directory +# SKIP-CLEAN-CORELIBS-CHECK-NOT: rm -rf {{.*/libdispatch-[^/]*}} +# SKIP-CLEAN-CORELIBS-CHECK-NOT: Cleaning the Foundation build directory +# SKIP-CLEAN-CORELIBS-CHECK-NOT: rm -rf {{.*/foundation-[^/]*}} +# SKIP-CLEAN-CORELIBS-CHECK-NOT: Cleaning the XCTest build directory +# SKIP-CLEAN-CORELIBS-CHECK-NOT: rm -rf {{.*/xctest-[^/]*}} diff --git a/validation-test/BuildSystem/skip_clean_llbuild.test b/validation-test/BuildSystem/skip_clean_llbuild.test index 20af0a47c0357..7a9e6adf4aa2e 100644 --- a/validation-test/BuildSystem/skip_clean_llbuild.test +++ b/validation-test/BuildSystem/skip_clean_llbuild.test @@ -2,11 +2,11 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llbuild --cmake %cmake 2>&1 | %FileCheck --check-prefix=CLEAN-LLBUILD-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llbuild --cmake %cmake 2>&1 | %FileCheck --check-prefix=CLEAN-LLBUILD-CHECK %s # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llbuild --skip-clean-llbuild --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-CLEAN-LLBUILD-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --llbuild --skip-clean-llbuild --cmake %cmake 2>&1 | %FileCheck --check-prefix=SKIP-CLEAN-LLBUILD-CHECK %s # CLEAN-LLBUILD-CHECK: Cleaning the llbuild build directory # CLEAN-LLBUILD-CHECK-NEXT: rm -rf diff --git a/validation-test/BuildSystem/skip_cmark_swift_llvm.test b/validation-test/BuildSystem/skip_cmark_swift_llvm.test index b703a4f5b6f26..65f83da2c9222 100644 --- a/validation-test/BuildSystem/skip_cmark_swift_llvm.test +++ b/validation-test/BuildSystem/skip_cmark_swift_llvm.test @@ -2,15 +2,15 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-cmark 2>&1 | %FileCheck --check-prefix=SKIP-CMARK-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-cmark 2>&1 | %FileCheck --check-prefix=SKIP-CMARK-CHECK %s # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-llvm 2>&1 | %FileCheck --check-prefix=SKIP-LLVM-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-llvm 2>&1 | %FileCheck --check-prefix=SKIP-LLVM-CHECK %s # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-swift 2>&1 | %FileCheck --check-prefix=SKIP-SWIFT-CHECK %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --install-all --cmake %cmake --skip-build-swift 2>&1 | %FileCheck --check-prefix=SKIP-SWIFT-CHECK %s # SKIP-CMARK-CHECK-NOT: cmake --build {{.*}}cmark- # SKIP-CMARK-CHECK: cmake --build {{.*}}llvm- @@ -20,16 +20,16 @@ # SKIP-CMARK-CHECK: --- Installing swift --- # SKIP-LLVM-CHECK: cmake --build {{.*}}cmark- +# SKIP-LLVM-CHECK: --- Installing cmark --- # SKIP-LLVM-CHECK: cmake --build {{.*}}llvm-tblgen # SKIP-LLVM-CHECK: cmake --build {{.*}}swift- -# SKIP-LLVM-CHECK: --- Installing cmark --- # SKIP-LLVM-CHECK-NOT: --- Installing llvm --- # SKIP-LLVM-CHECK: --- Installing swift --- # SKIP-SWIFT-CHECK: cmake --build {{.*}}cmark- +# SKIP-SWIFT-CHECK: --- Installing cmark --- # SKIP-SWIFT-CHECK: cmake --build {{.*}}llvm- # SKIP-SWIFT-CHECK-NOT: cmake --build {{.*}}swift- -# SKIP-SWIFT-CHECK: --- Installing cmark --- # SKIP-SWIFT-CHECK: --- Installing llvm --- # SKIP-SWIFT-CHECK-NOT: --- Installing swift --- diff --git a/validation-test/BuildSystem/swift-cmake/CMakeLists.txt b/validation-test/BuildSystem/swift-cmake/CMakeLists.txt new file mode 100644 index 0000000000000..784966258113d --- /dev/null +++ b/validation-test/BuildSystem/swift-cmake/CMakeLists.txt @@ -0,0 +1,32 @@ + +add_swift_host_library(TestCPPLib + STATIC + CPPLib.cpp) + +add_swift_host_library(TestPureSwiftSharedLib + SHARED + PURE_SWIFT + Klass.swift) +target_link_libraries(TestPureSwiftSharedLib PRIVATE TestCPPLib) +target_link_options(TestPureSwiftSharedLib PRIVATE "SHELL:-import-objc-header ${CMAKE_CURRENT_SOURCE_DIR}/CPPLib.h") + +add_swift_host_library(TestPureSwiftStaticLib + STATIC + PURE_SWIFT + Klass.swift) +target_link_libraries(TestPureSwiftStaticLib PRIVATE TestCPPLib) +target_compile_options(TestPureSwiftStaticLib PRIVATE "SHELL:-import-objc-header ${CMAKE_CURRENT_SOURCE_DIR}/CPPLib.h") + +add_swift_host_tool(TestCPPToolLinkSwiftSharedLib + CPPTool.cpp + SWIFT_COMPONENT testsuite-tools + ) +target_link_libraries(TestCPPToolLinkSwiftSharedLib PRIVATE TestPureSwiftSharedLib) + +add_swift_host_tool(TestCPPToolLinkSwiftStaticLib + CPPTool.cpp + SWIFT_COMPONENT testsuite-tools + ) +target_link_libraries(TestCPPToolLinkSwiftStaticLib + PRIVATE + TestPureSwiftStaticLib) diff --git a/validation-test/BuildSystem/swift-cmake/CPPLib.cpp b/validation-test/BuildSystem/swift-cmake/CPPLib.cpp new file mode 100644 index 0000000000000..7cfbb541f1c64 --- /dev/null +++ b/validation-test/BuildSystem/swift-cmake/CPPLib.cpp @@ -0,0 +1,9 @@ + +#include +#include "CPPLib.h" + +extern "C" { + void CPPLib_log() { + printf("I am in cpplib log!\n"); + } +} diff --git a/validation-test/BuildSystem/swift-cmake/CPPLib.h b/validation-test/BuildSystem/swift-cmake/CPPLib.h new file mode 100644 index 0000000000000..acc99e4ac65f8 --- /dev/null +++ b/validation-test/BuildSystem/swift-cmake/CPPLib.h @@ -0,0 +1,13 @@ + +#ifndef SWIFT_TEST_CPPLIB +#define SWIFT_TEST_CPPLIB + +#ifdef __cplusplus +extern "C" { +#endif + void CPPLib_log(); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stdlib/public/Darwin/Foundation/URLCache.swift b/validation-test/BuildSystem/swift-cmake/CPPTool.cpp similarity index 54% rename from stdlib/public/Darwin/Foundation/URLCache.swift rename to validation-test/BuildSystem/swift-cmake/CPPTool.cpp index 2a2f7588e9791..8df8957707cfb 100644 --- a/stdlib/public/Darwin/Foundation/URLCache.swift +++ b/validation-test/BuildSystem/swift-cmake/CPPTool.cpp @@ -1,20 +1,26 @@ -//===----------------------------------------------------------------------===// +//===--- CPPTool.cpp ------------------------------------------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2021 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 // //===----------------------------------------------------------------------===// +/// +/// Calls a method from one of the swift libraries. +/// +//===----------------------------------------------------------------------===// -@_exported import Foundation // Clang module +#include + +extern "C" { + int doSomething(); +} -extension URLCache { - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public convenience init(memoryCapacity: Int, diskCapacity: Int, directory: URL? = nil) { - self.init(__memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, directoryURL: directory) - } +int main(int argc, char *argv[]) { + printf("doSomething: %d\n", doSomething()); + return 0; } diff --git a/validation-test/BuildSystem/swift-cmake/Klass.swift b/validation-test/BuildSystem/swift-cmake/Klass.swift new file mode 100644 index 0000000000000..8ec49fc79fbd4 --- /dev/null +++ b/validation-test/BuildSystem/swift-cmake/Klass.swift @@ -0,0 +1,19 @@ +// This isn't a test but lit wants to treat it as a test! Make it exit -1 to +// make sure lit doesn't run this file and add a REQUIRES line so we actually +// never do that. +// +// RUN: exit -1 +// REQUIRES: not_a_test + +public class Klass {} + +public func useKlass(_ k: Klass) {} + +@_cdecl("doSomething") +public func doSomething() -> Int { + CPPLib_log() + let x: Int = 34 + // Make sure we link against SwiftCore. + print("Swift is going to return \(34)") + return x +} diff --git a/validation-test/BuildSystem/swift-cmake/RunCPPTools.test b/validation-test/BuildSystem/swift-cmake/RunCPPTools.test new file mode 100644 index 0000000000000..43b65f0554d57 --- /dev/null +++ b/validation-test/BuildSystem/swift-cmake/RunCPPTools.test @@ -0,0 +1,11 @@ +# REQUIRES: standalone_build,OS=macosx + +# Test that makes sure when we link in a pure swift shared lib into a cxx tool +# that we set rpaths right and thus can run the test. + +# RUN: %swift_obj_root/bin/TestCPPToolLinkSwiftSharedLib | %FileCheck %s +# RUN: %swift_obj_root/bin/TestCPPToolLinkSwiftStaticLib | %FileCheck %s + +# CHECK: I am in cpplib log! +# CHECK: Swift is going to return 34 +# CHECK: doSomething: 34 diff --git a/validation-test/BuildSystem/test_early_swift_driver_and_infer.swift b/validation-test/BuildSystem/test_early_swift_driver_and_infer.swift new file mode 100644 index 0000000000000..ec3190613e681 --- /dev/null +++ b/validation-test/BuildSystem/test_early_swift_driver_and_infer.swift @@ -0,0 +1,7 @@ +# REQUIRES: standalone_build + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --swiftpm --infer --cmake %cmake 2>&1 | %FileCheck %s + +# CHECK: --- Building earlyswiftdriver --- diff --git a/validation-test/BuildSystem/test_early_swift_driver_and_test.test b/validation-test/BuildSystem/test_early_swift_driver_and_test.test new file mode 100644 index 0000000000000..e9eb63a766131 --- /dev/null +++ b/validation-test/BuildSystem/test_early_swift_driver_and_test.test @@ -0,0 +1,8 @@ +# REQUIRES: standalone_build + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --test --cmake %cmake 2>&1 | %FileCheck %s + +# CHECK: --- Building earlyswiftdriver --- +# CHECK: cmake --build {{.*}}check-swift-only_early_swiftdriver \ No newline at end of file diff --git a/validation-test/BuildSystem/test_skip_early_swift_driver.test b/validation-test/BuildSystem/test_skip_early_swift_driver.test new file mode 100644 index 0000000000000..532d7444176e4 --- /dev/null +++ b/validation-test/BuildSystem/test_skip_early_swift_driver.test @@ -0,0 +1,7 @@ +# REQUIRES: standalone_build + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --cmake %cmake --skip-early-swift-driver 2>&1 | %FileCheck %s + +# CHECK-NOT: --- Building earlyswiftdriver --- \ No newline at end of file diff --git a/validation-test/CMakeLists.txt b/validation-test/CMakeLists.txt new file mode 100644 index 0000000000000..396a4f0ef2418 --- /dev/null +++ b/validation-test/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(BuildSystem) diff --git a/validation-test/Casting/Inputs/BoxingCasts.swift.gyb b/validation-test/Casting/Inputs/BoxingCasts.swift.gyb index 35959ef589c53..9f43789842311 100644 --- a/validation-test/Casting/Inputs/BoxingCasts.swift.gyb +++ b/validation-test/Casting/Inputs/BoxingCasts.swift.gyb @@ -173,6 +173,11 @@ contents = [ extra_targets=["StructInt.Type"], roundtrips=False, # Compiler bug rejects roundtrip cast T? => Any => T? ), + Contents(name="ClassInt.Type", + constructor="ClassInt.self", + hashable=False, + protocols=[], + ), Contents(name="PublicProtocol.Protocol", constructor="PublicProtocol.self", hashable=False, @@ -193,7 +198,7 @@ contents = [ % nextTestNumber() BoxingCasts.test("${testFunctionName()}(): Casting ${box.name}(${content.name}) to ${target}") { // TODO: Selectively enable/disable cases that work with earlier stdlib - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { ${testFunctionName()}() } } @@ -219,7 +224,7 @@ func ${testFunctionName()}() { % nextTestNumber() BoxingCasts.test("${testFunctionName()}(): Casting ${box.name}(${innerBox.name}(${content.name})) to ${target}") { // TODO: Selectively enable/disable cases that work with earlier stdlib - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { ${testFunctionName()}() } } diff --git a/validation-test/IDE/crashers_2_fixed/rdar76686564.swift b/validation-test/IDE/crashers_2_fixed/rdar76686564.swift new file mode 100644 index 0000000000000..0a36d87d5f7ff --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/rdar76686564.swift @@ -0,0 +1,28 @@ +// RUN: %target-swift-ide-test --conforming-methods -code-completion-token=COMPLETE_EXPR --conforming-methods-expected-types=s:14swift_ide_test10MySequenceP -source-filename %s +// RUN: %target-swift-ide-test --conforming-methods -code-completion-token=COMPLETE_STMT --conforming-methods-expected-types=s:14swift_ide_test10MySequenceP -source-filename %s + +protocol MySequence { + associatedtype Element +} + +struct Foo: MySequence { + typealias Element = X +} + +struct ArgumentDefinition { + fileprivate func bashCompletionWords() -> Foo { fatalError() } +} + +func myFlatMap(_ transform: (ArgumentDefinition) -> SegmentOfResult) -> Foo { + fatalError() +} + +func generateArgumentWords() { + // Explicitly coerce the type using 'as'. This is type checked as an expression. + _ = myFlatMap { $0.#^COMPLETE_EXPR^# } as Foo +} + +func generateArgumentWords() -> Foo { + // Implicitly coerce the type from the return type. This is type checked as a stmt. + return myFlatMap { $0.#^COMPLETE_STMT^# } +} diff --git a/validation-test/IDE/crashers_2_fixed/rdar78017503.swift b/validation-test/IDE/crashers_2_fixed/rdar78017503.swift new file mode 100644 index 0000000000000..00b91955d1fba --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/rdar78017503.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-ide-test -code-completion -code-completion-token=COMPLETE -source-filename=%s + +@resultBuilder +struct TupleBuilder { + static func buildBlock() -> () { } +} + +func testPatternMatching() { + @TupleBuilder var x3 { + let x: Int? = nil + #^COMPLETE^# + } +} diff --git a/validation-test/IDE/crashers_2_fixed/rdar78781163.swift b/validation-test/IDE/crashers_2_fixed/rdar78781163.swift new file mode 100644 index 0000000000000..4d41fead8a146 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/rdar78781163.swift @@ -0,0 +1,9 @@ +// RUN: %swift-ide-test -code-completion -code-completion-token=COMPLETE -source-filename %s + +class C { + func foo() { + #^COMPLETE^# + default + } + func bar() {} +} diff --git a/validation-test/IDE/crashers_2_fixed/sr14494.swift b/validation-test/IDE/crashers_2_fixed/sr14494.swift new file mode 100644 index 0000000000000..26a3dd4a494f8 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/sr14494.swift @@ -0,0 +1,37 @@ +// RUN: %target-swift-ide-test -code-completion -code-completion-token=COMPLETE -source-filename=%s | %FileCheck %s + +protocol MyView { + associatedtype Body : MyView + @MyViewBuilder var body: Self.Body { get } +} + +@resultBuilder public struct MyViewBuilder { + static func buildBlock() -> MyZStack { fatalError() } + static func buildBlock(_ content: Content) -> Content { content } +} + +struct MyAlignment { + static let center: MyAlignment +} + +struct MyZStack : MyView { + init(alignment: MyAlignment, @MyViewBuilder content: () -> Content) { + fatalError() + } + + func my_updating(body: (inout State) -> Void) {} +} + +struct BottomMenu: MyView { + var body: some MyView { + let a = MyZStack(alignment: .#^COMPLETE^#center, content: {}) + .my_updating(body: { state in + state = false + }) + } +} + +// CHECK: Begin completions, 2 items +// CHECK: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: center[#MyAlignment#]; name=center +// CHECK: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#MyAlignment#]; name=init() +// CHECK: End completions diff --git a/validation-test/IDE/crashers_2_fixed/sr14687.swift b/validation-test/IDE/crashers_2_fixed/sr14687.swift new file mode 100644 index 0000000000000..dce0ed83a6afe --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/sr14687.swift @@ -0,0 +1,5 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=COMPLETE + +class Foo { + let searchSubject = Bar Void) -> some View2 { fatalError() } +} + +@resultBuilder struct ViewBuilder2 { + static func buildBlock() -> Never { fatalError() } + static func buildBlock(_ content: Content) -> Content where Content : View2 { fatalError() } +} + +struct HStack2 : View2 { + init(@ViewBuilder2 content: () -> Content) { fatalError() } +} + +struct ItemImage2 : View2 { + init(path: String) {} +} + +struct ForEach2: View2 { + init(_ data: [Listing], @ViewBuilder2 content: (Data) -> Content) {} +} + + +struct TodayNookazonSection { + + let listings: [Listing] + + @available(macOS 10.15, *) + @ViewBuilder2 var body: some View2 { + ForEach2(listings) { listing in + HStack2 { + HStack2 { + ItemImage2(path#^CC^#: "abc") + } + .onTapGesturf { + listing + } + } + } + } +} diff --git a/validation-test/IDE/crashers_2_fixed/sr14755.swift b/validation-test/IDE/crashers_2_fixed/sr14755.swift new file mode 100644 index 0000000000000..8984d9c50e87d --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/sr14755.swift @@ -0,0 +1,3 @@ +// RUN: %swift-ide-test --code-completion --source-filename %s --code-completion-token=COMPLETE + +Foo.#^COMPLETE^#bar ?? baz(_) = baba \ No newline at end of file diff --git a/validation-test/IDE/crashers_fixed/sr14979.swift b/validation-test/IDE/crashers_fixed/sr14979.swift new file mode 100644 index 0000000000000..8db55f1e74b4d --- /dev/null +++ b/validation-test/IDE/crashers_fixed/sr14979.swift @@ -0,0 +1,30 @@ +// RUN: %swift-ide-test --code-completion --source-filename %s -code-completion-token CC + +func foo() { + let months: [[String]] = [] + let abcd: Int + let x = ForEach2(months) { group in + HStack2(spacing: 3) { + useKeyPath(id: \.abcd#^CC^#) + } + } +} + + +struct ForEach2 where Data : RandomAccessCollection { + init(_ data: Data, content: @escaping (Data.Element) -> Void) {} +} + +struct Bar { + let abcd: Int +} + +func useKeyPath(id: KeyPath) {} + +struct HStack2 { + init(spacing: Double, @ViewBuilder2 content: () -> Content) +} + +@resultBuilder struct ViewBuilder2 { + static func buildBlock(_ content: Content) -> Content { fatalError() } +} diff --git a/validation-test/IDE/slow/rdar45511835.swift b/validation-test/IDE/slow/rdar45511835.swift index 7028c898e5f01..fcc08139ad088 100644 --- a/validation-test/IDE/slow/rdar45511835.swift +++ b/validation-test/IDE/slow/rdar45511835.swift @@ -10,4 +10,4 @@ func testing() { .map { $0 + "b" as String } .filter { $0 != "" } #^COMPLETE^# } -// CHECK: Decl[InfixOperatorFunction]/{{.*}}: [' ']+ {#[String]#}[#[String]#]; name=+ [String] +// CHECK: Decl[InfixOperatorFunction]/{{.*}}: [' ']+ {#[String]#}[#[String]#]; name=+ diff --git a/validation-test/Parse/delayed-members-open-if.swift b/validation-test/Parse/delayed-members-open-if.swift new file mode 100644 index 0000000000000..96f72bd0c7aed --- /dev/null +++ b/validation-test/Parse/delayed-members-open-if.swift @@ -0,0 +1,4 @@ +// RUN: not %target-swift-frontend -typecheck -experimental-skip-all-function-bodies %s + +#if os( +struct Anything { diff --git a/validation-test/Parse/delayed-members-open-prop-scope.swift b/validation-test/Parse/delayed-members-open-prop-scope.swift new file mode 100644 index 0000000000000..8af5f82897b52 --- /dev/null +++ b/validation-test/Parse/delayed-members-open-prop-scope.swift @@ -0,0 +1,6 @@ +// RUN: not %target-swift-frontend -typecheck -experimental-skip-all-function-bodies %s + +struct A { + let prop: Int = { + +struct B { diff --git a/validation-test/ParseableInterface/verify_all_overlays.py b/validation-test/ParseableInterface/verify_all_overlays.py index e1ce62e248f54..3b5a1aba2f69c 100755 --- a/validation-test/ParseableInterface/verify_all_overlays.py +++ b/validation-test/ParseableInterface/verify_all_overlays.py @@ -16,9 +16,7 @@ # Expected failures by platform # ----------------------------- -# macosx: XCTest -# ios: XCTest -# tvos: XCTest +# (none) from __future__ import print_function diff --git a/validation-test/Reflection/existentials.swift b/validation-test/Reflection/existentials.swift index ea8b44587b350..a3cd7ba04ae3e 100644 --- a/validation-test/Reflection/existentials.swift +++ b/validation-test/Reflection/existentials.swift @@ -71,6 +71,8 @@ reflect(any: mc) // CHECK-64: Mangled name: $s12existentials7MyClassCyS2iG // CHECK-64: Demangled name: existentials.MyClass +// CHECK-64: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + // CHECK-32: Reflecting an existential. // CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} // CHECK-32: Type reference: @@ -82,6 +84,40 @@ reflect(any: mc) // CHECK-32: Mangled name: $s12existentials7MyClassCyS2iG // CHECK-32: Demangled name: existentials.MyClass +// CHECK-32: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + +// Reflect and unwrap class existential should print the exact same info, +// EXCEPT for the start of instance data address. +reflect(any: mc, shouldUnwrapClassExistential: true) + +// CHECK-64: Reflecting an existential and unwrapping class. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (bound_generic_class existentials.MyClass +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int)) +// CHECK-64: Type info: +// CHECK-64: (reference kind=strong refcounting=native) +// CHECK-64: Mangled name: $s12existentials7MyClassCyS2iG +// CHECK-64: Demangled name: existentials.MyClass + +// CHECK-64: Start of instance data: +// CHECK-64-NOT: ![[ADDR]] + +// CHECK-32: Reflecting an existential and unwrapping class. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (bound_generic_class existentials.MyClass +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int)) +// CHECK-32: Type info: +// CHECK-32: (reference kind=strong refcounting=native) +// CHECK-32: Mangled name: $s12existentials7MyClassCyS2iG +// CHECK-32: Demangled name: existentials.MyClass + +// CHECK-32: Start of instance data: +// CHECK-32-NOT: ![[ADDR]] + // This value fits in the 3-word buffer in the container. var smallStruct = MyStruct(x: 1, y: 2, z: 3) reflect(any: smallStruct) @@ -111,6 +147,9 @@ reflect(any: smallStruct) // CHECK-64-NEXT: Mangled name: $s12existentials8MyStructVyS3iG // CHECK-64-NEXT: Demangled name: existentials.MyStruct +// CHECK-64: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + + // CHECK-32: Reflecting an existential. // CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} // CHECK-32: Type reference: @@ -136,6 +175,66 @@ reflect(any: smallStruct) // CHECK-32-NEXT: Mangled name: $s12existentials8MyStructVyS3iG // CHECK-32-NEXT: Demangled name: existentials.MyStruct +// CHECK-32: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + +reflect(any: smallStruct, shouldUnwrapClassExistential: true) + +// CHECK-64: Reflecting an existential and unwrapping class. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (bound_generic_struct existentials.MyStruct +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int)) + +// CHECK-64: Type info: +// CHECK-64: (struct size=24 alignment=8 stride=24 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=x offset=0 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64-NEXT: (field name=y offset=8 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64-NEXT: (field name=z offset=16 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))) +// CHECK-64-NEXT: Mangled name: $s12existentials8MyStructVyS3iG +// CHECK-64-NEXT: Demangled name: existentials.MyStruct + +// CHECK-64: Start of instance data: +// CHECK-64-NOT: ![[ADDR]] + +// CHECK-32: Reflecting an existential and unwrapping class. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (bound_generic_struct existentials.MyStruct +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int)) + +// CHECK-32: Type info: +// CHECK-32: (struct size=12 alignment=4 stride=12 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=x offset=0 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32-NEXT: (field name=y offset=4 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32-NEXT: (field name=z offset=8 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))) +// CHECK-32-NEXT: Mangled name: $s12existentials8MyStructVyS3iG +// CHECK-32-NEXT: Demangled name: existentials.MyStruct + +// CHECK-32: Start of instance data: +// CHECK-32-NOT: ![[ADDR]] + // This value will be copied into a heap buffer, with a // pointer to it in the existential. var largeStruct = MyStruct(x: (1,1,1), y: (2,2,2), z: (3,3,3)) @@ -204,6 +303,9 @@ reflect(any: largeStruct) // CHECK-64-NEXT: Mangled name: $s12existentials8MyStructVySi_S2itSi_S2itSi_S2itG // CHECK-64-NEXT: Demangled name: existentials.MyStruct<(Swift.Int, Swift.Int, Swift.Int), (Swift.Int, Swift.Int, Swift.Int), (Swift.Int, Swift.Int, Swift.Int)> +// CHECK-64: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + + // CHECK-32: Reflecting an existential. // CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} // CHECK-32: Type reference: @@ -267,6 +369,142 @@ reflect(any: largeStruct) // CHECK-32-NEXT: Mangled name: $s12existentials8MyStructVySi_S2itSi_S2itSi_S2itG // CHECK-32-NEXT: Demangled name: existentials.MyStruct<(Swift.Int, Swift.Int, Swift.Int), (Swift.Int, Swift.Int, Swift.Int), (Swift.Int, Swift.Int, Swift.Int)> +// CHECK-32: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + +reflect(any: largeStruct, shouldUnwrapClassExistential: true) + +// CHECK-64: Reflecting an existential and unwrapping class. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (bound_generic_struct existentials.MyStruct +// CHECK-64-NEXT: (tuple +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int)) +// CHECK-64-NEXT: (tuple +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int)) +// CHECK-64-NEXT: (tuple +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int) +// CHECK-64-NEXT: (struct Swift.Int))) +// CHECK-64: Type info: +// CHECK-64-NEXT: (struct size=72 alignment=8 stride=72 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=x offset=0 +// CHECK-64-NEXT: (tuple size=24 alignment=8 stride=24 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field offset=0 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64-NEXT: (field offset=8 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64-NEXT: (field offset=16 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64-NEXT: (field name=y offset=24 +// CHECK-64-NEXT: (tuple size=24 alignment=8 stride=24 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field offset=0 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64-NEXT: (field offset=8 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64-NEXT: (field offset=16 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-64-NEXT: (field name=z offset=48 +// CHECK-64-NEXT: (tuple size=24 alignment=8 stride=24 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field offset=0 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64-NEXT: (field offset=8 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-64-NEXT: (field offset=16 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-64-NEXT: Mangled name: $s12existentials8MyStructVySi_S2itSi_S2itSi_S2itG +// CHECK-64-NEXT: Demangled name: existentials.MyStruct<(Swift.Int, Swift.Int, Swift.Int), (Swift.Int, Swift.Int, Swift.Int), (Swift.Int, Swift.Int, Swift.Int)> + +// CHECK-64: Start of instance data: +// CHECK-64-NOT: ![[ADDR]] + +// CHECK-32: Reflecting an existential and unwrapping class. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (bound_generic_struct existentials.MyStruct +// CHECK-32-NEXT: (tuple +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int)) +// CHECK-32-NEXT: (tuple +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int)) +// CHECK-32-NEXT: (tuple +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int) +// CHECK-32-NEXT: (struct Swift.Int))) +// CHECK-32: Type info: +// CHECK-32: (struct size=36 alignment=4 stride=36 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=x offset=0 +// CHECK-32-NEXT: (tuple size=12 alignment=4 stride=12 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field offset=0 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32-NEXT: (field offset=4 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32-NEXT: (field offset=8 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32-NEXT: (field name=y offset=12 +// CHECK-32-NEXT: (tuple size=12 alignment=4 stride=12 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field offset=0 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32-NEXT: (field offset=4 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32-NEXT: (field offset=8 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))))) +// CHECK-32-NEXT: (field name=z offset=24 +// CHECK-32-NEXT: (tuple size=12 alignment=4 stride=12 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field offset=0 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32-NEXT: (field offset=4 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1)))) +// CHECK-32-NEXT: (field offset=8 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))))) +// CHECK-32-NEXT: Mangled name: $s12existentials8MyStructVySi_S2itSi_S2itSi_S2itG +// CHECK-32-NEXT: Demangled name: existentials.MyStruct<(Swift.Int, Swift.Int, Swift.Int), (Swift.Int, Swift.Int, Swift.Int), (Swift.Int, Swift.Int, Swift.Int)> + +// CHECK-32: Start of instance data: +// CHECK-32-NOT: ![[ADDR]] + // Function type: reflect(any: {largeStruct}) // CHECK-64: Mangled name: $s12existentials8MyStructVySi_S2itSi_S2itSi_S2itGyc @@ -346,6 +584,8 @@ reflect(any: he) // CHECK-64-NEXT: Mangled name: $s12existentials8HasErrorV // CHECK-64-NEXT: Demangled name: existentials.HasError +// CHECK-64: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + // CHECK-32: Reflecting an existential. // CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} // CHECK-32: Type reference: @@ -382,7 +622,91 @@ reflect(any: he) // CHECK-32-NEXT: Mangled name: $s12existentials8HasErrorV // CHECK-32-NEXT: Demangled name: existentials.HasError -reflect(error: MyError()) +// CHECK-32: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + + +reflect(any: he, shouldUnwrapClassExistential: true) + +// CHECK-64: Reflecting an existential and unwrapping class. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F+]}} +// CHECK-64: Type reference: +// CHECK-64: (struct existentials.HasError) + +// CHECK-64: Type info: +// CHECK-64: (struct size=144 alignment=8 stride=144 +// CHECK-64-NEXT: (field name=singleError offset=0 +// CHECK-64-NEXT: (error_existential size=8 alignment=8 stride=8 num_extra_inhabitants=[[#num_extra_inhabitants_64bit]] bitwise_takable=1 +// CHECK-64-NEXT: (field name=error offset=0 +// CHECK-64-NEXT: (reference kind=strong refcounting=unknown)))) +// CHECK-64-NEXT: (field name=errorInComposition offset=8 +// CHECK-64-NEXT: (opaque_existential size=48 alignment=8 stride=48 num_extra_inhabitants=[[#num_extra_inhabitants_64bit]] bitwise_takable=1 +// CHECK-64-NEXT: (field name=metadata offset=24 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=[[#num_extra_inhabitants_64bit]] bitwise_takable=1)) +// CHECK-64-NEXT: (field name=wtable offset=32 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1 bitwise_takable=1)) +// CHECK-64-NEXT: (field name=wtable offset=40 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1 bitwise_takable=1)))) +// CHECK-64-NEXT: (field name=customError offset=56 +// CHECK-64-NEXT: (opaque_existential size=40 alignment=8 stride=40 num_extra_inhabitants=[[#num_extra_inhabitants_64bit]] bitwise_takable=1 +// CHECK-64-NEXT: (field name=metadata offset=24 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=[[#num_extra_inhabitants_64bit]] bitwise_takable=1)) +// CHECK-64-NEXT: (field name=wtable offset=32 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1 bitwise_takable=1)))) +// CHECK-64-NEXT: (field name=customErrorInComposition offset=96 +// CHECK-64-NEXT: (opaque_existential size=48 alignment=8 stride=48 num_extra_inhabitants=[[#num_extra_inhabitants_64bit]] bitwise_takable=1 +// CHECK-64-NEXT: (field name=metadata offset=24 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=[[#num_extra_inhabitants_64bit]] bitwise_takable=1)) +// CHECK-64-NEXT: (field name=wtable offset=32 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1 bitwise_takable=1)) +// CHECK-64-NEXT: (field name=wtable offset=40 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1 bitwise_takable=1))))) +// CHECK-64-NEXT: Mangled name: $s12existentials8HasErrorV +// CHECK-64-NEXT: Demangled name: existentials.HasError + +// CHECK-64: Start of instance data: +// CHECK-64-NOT: ![[ADDR]] + +// CHECK-32: Reflecting an existential and unwrapping class. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (struct existentials.HasError) + +// CHECK-32: Type info: +// CHECK-32: (struct size=72 alignment=4 stride=72 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32-NEXT: (field name=singleError offset=0 +// CHECK-32-NEXT: (error_existential size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32-NEXT: (field name=error offset=0 +// CHECK-32-NEXT: (reference kind=strong refcounting=unknown)))) +// CHECK-32-NEXT: (field name=errorInComposition offset=4 +// CHECK-32-NEXT: (opaque_existential size=24 alignment=4 stride=24 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32-NEXT: (field name=metadata offset=12 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)) +// CHECK-32-NEXT: (field name=wtable offset=16 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1 bitwise_takable=1)) +// CHECK-32-NEXT: (field name=wtable offset=20 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1 bitwise_takable=1)))) +// CHECK-32-NEXT: (field name=customError offset=28 +// CHECK-32-NEXT: (opaque_existential size=20 alignment=4 stride=20 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32-NEXT: (field name=metadata offset=12 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)) +// CHECK-32-NEXT: (field name=wtable offset=16 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1 bitwise_takable=1)))) +// CHECK-32-NEXT: (field name=customErrorInComposition offset=48 +// CHECK-32-NEXT: (opaque_existential size=24 alignment=4 stride=24 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32-NEXT: (field name=metadata offset=12 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)) +// CHECK-32-NEXT: (field name=wtable offset=16 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1 bitwise_takable=1)) +// CHECK-32-NEXT: (field name=wtable offset=20 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1 bitwise_takable=1))))) +// CHECK-32-NEXT: Mangled name: $s12existentials8HasErrorV +// CHECK-32-NEXT: Demangled name: existentials.HasError + +// CHECK-32: Start of instance data: +// CHECK-32-NOT: ![[ADDR]] + +let error = MyError() +reflect(error: error) // CHECK-64: Reflecting an error existential. // CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} @@ -398,6 +722,8 @@ reflect(error: MyError()) // CHECK-64-NEXT: Mangled name: $s12existentials7MyErrorV // CHECK-64-NEXT: Demangled name: existentials.MyError +// CHECK-64: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + // CHECK-32: Reflecting an error existential. // CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} // CHECK-32: Type reference: @@ -412,4 +738,41 @@ reflect(error: MyError()) // CHECK-32-NEXT: Mangled name: $s12existentials7MyErrorV // CHECK-32-NEXT: Demangled name: existentials.MyError +// CHECK-32: Start of instance data: [[ADDR:0x[0-9a-fA-F]+]] + +reflectUnwrappingClassExistential(error: error) +// CHECK-64: Reflecting an error existential and unwrapping class. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (struct existentials.MyError) + +// CHECK-64: Type info: +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=i offset=0 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64-NEXT: (field name=_value offset=0 +// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))) +// CHECK-64-NEXT: Mangled name: $s12existentials7MyErrorV +// CHECK-64-NEXT: Demangled name: existentials.MyError + +// CHECK-64: Start of instance data: +// CHECK-64-NOT: ![[ADDR]] + +// CHECK-32: Reflecting an error existential and unwrapping class. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (struct existentials.MyError) + +// CHECK-32: Type info: +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=i offset=0 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32-NEXT: (field name=_value offset=0 +// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0 bitwise_takable=1))))) +// CHECK-32-NEXT: Mangled name: $s12existentials7MyErrorV +// CHECK-32-NEXT: Demangled name: existentials.MyError + +// CHECK-32: Start of instance data: +// CHECK-32-NOT: ![[ADDR]] + doneReflecting() diff --git a/validation-test/Runtime/weak-reference-racetests-dispatch.swift b/validation-test/Runtime/weak-reference-racetests-dispatch.swift index dcb2e17d94df9..cda4ba1df3ca2 100644 --- a/validation-test/Runtime/weak-reference-racetests-dispatch.swift +++ b/validation-test/Runtime/weak-reference-racetests-dispatch.swift @@ -1,4 +1,5 @@ // RUN: %target-build-swift %s %import-libdispatch -o %t_binary +// RUN: %target-codesign %t_binary // RUN: %target-run %t_binary // REQUIRES: executable_test // REQUIRES: stress_test diff --git a/validation-test/SIL/parse_stdlib.sil b/validation-test/SIL/parse_stdlib.sil index 7b373654d4216..751d275837c0f 100644 --- a/validation-test/SIL/parse_stdlib.sil +++ b/validation-test/SIL/parse_stdlib.sil @@ -3,3 +3,4 @@ // RUN: %target-sil-opt -enable-sil-verify-all=true %t.sil > /dev/null // REQUIRES: long_test // REQUIRES: nonexecutable_test +// REQUIRES: rdar81658645 diff --git a/validation-test/SILOptimizer/string_switch.swift b/validation-test/SILOptimizer/string_switch.swift index 555c1d972847c..268e95147cd49 100644 --- a/validation-test/SILOptimizer/string_switch.swift +++ b/validation-test/SILOptimizer/string_switch.swift @@ -1,5 +1,6 @@ // RUN: %target-build-swift -O %s -module-name=test -Xllvm -sil-disable-pass=FunctionSignatureOpts -o %t.out // RUN: %target-build-swift -O %s -module-name=test -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil | %FileCheck %s +// RUN: %target-codesign %t.out // RUN: %target-run %t.out // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib // REQUIRES: stress_test diff --git a/validation-test/Sema/SwiftUI/sr14213.swift b/validation-test/Sema/SwiftUI/sr14213.swift index e35dd5a0f95dd..2b6759d731957 100644 --- a/validation-test/Sema/SwiftUI/sr14213.swift +++ b/validation-test/Sema/SwiftUI/sr14213.swift @@ -8,7 +8,7 @@ import SwiftUI struct Experiment: View { var body: some View { HStack { // expected-error {{generic parameter 'Content' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}}emacs - Slider(value: <#T##Binding#>, in: <#T##ClosedRange#>, label: <#T##() -> _#>) // expected-error 3 {{editor placeholder in source file}} expected-error {{expected type for function result}} + Slider(value: <#T##Binding#>, in: <#T##ClosedRange#>, label: <#T##() -> _#>) // expected-error 3 {{editor placeholder in source file}} // expected-error@-1 {{protocol 'BinaryFloatingPoint' as a type cannot conform to 'Comparable'}} // expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}} } diff --git a/validation-test/Sema/sr14692.swift b/validation-test/Sema/sr14692.swift new file mode 100644 index 0000000000000..4f2aa9ac9faed --- /dev/null +++ b/validation-test/Sema/sr14692.swift @@ -0,0 +1,28 @@ +// RUN: %target-typecheck-verify-swift + +enum Foo { case foo } +enum Bar { case bar } + +@resultBuilder struct ViewBuilder2 { + static func buildBlock(_ content: MyView) -> MyView { fatalError() } + static func buildIf(_ content: MyView?) -> MyView { fatalError() } +} + +func makeView(@ViewBuilder2 content: () -> MyView) { fatalError() } + +struct MyView { + init() { fatalError() } + + func qadding(bar: Foo) -> MyView { fatalError() } // expected-note{{incorrect labels for candidate (have: '(_:)', expected: '(bar:)')}} + func qadding(foo: Foo) -> MyView { fatalError() } // expected-note{{incorrect labels for candidate (have: '(_:)', expected: '(foo:)')}} +} + +func testCase() { + let array: [Int]? = [] + + makeView() { + if array?.isEmpty == false { + MyView().qadding(.foo) // expected-error{{no exact matches in call to instance method 'qadding'}} + } + } + } \ No newline at end of file diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar78102266.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar78102266.swift new file mode 100644 index 0000000000000..704728f2f1653 --- /dev/null +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar78102266.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend %s -typecheck + +struct Info { +} + +class Test { + var info: Info = Info() + + init() throws {} +} + +_ = try Test().info // Ok +_ = try! Test().info // Ok diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar79268378.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar79268378.swift new file mode 100644 index 0000000000000..9805e2a93387c --- /dev/null +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar79268378.swift @@ -0,0 +1,29 @@ +// RUN: not %target-swift-frontend %s -typecheck + +struct Result { +} + +func wrapper(_: Result?) { +} + +extension Optional where Wrapped == Result { + static func test(_: String) -> Result { Result() } +} + +extension Result { + static func test(_: R) -> Result where R.Bound == Int { + Result() + } +} + +protocol P {} + +struct Value : P { + init() {} + init(_: R) {} +} + +func example(_: T1, _: T2) { +} + +example(Value(), Value(wrapper(.test(0)))) diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar79523605.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar79523605.swift new file mode 100644 index 0000000000000..b38675a266e22 --- /dev/null +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar79523605.swift @@ -0,0 +1,5 @@ +// RUN: %target-swift-frontend %s -typecheck -verify + +func test() -> Any? { + 0 as! Any? // expected-warning {{forced cast from 'Int' to 'Any?' always succeeds; did you mean to use 'as'?}} +} diff --git a/validation-test/Sema/type_checker_crashers_fixed/sr14817.swift b/validation-test/Sema/type_checker_crashers_fixed/sr14817.swift new file mode 100644 index 0000000000000..199b26a857eac --- /dev/null +++ b/validation-test/Sema/type_checker_crashers_fixed/sr14817.swift @@ -0,0 +1,25 @@ +// RUN: %target-typecheck-verify-swift + +@resultBuilder +struct MyResultBuilder { + static func buildBlock(_ elements: Int...) -> Int { fatalError() } +} + +struct VariableDecl2 { + init( // expected-note {{found this candidate}} + paramClosure: () -> Int? = { nil }, + paramInt: Int, + @MyResultBuilder paramResultBuilder: () -> Int? = { nil } + ) { fatalError() } + + init( // expected-note {{found this candidate}} + paramInt: Int, + paramClosure: () -> Int? = { nil }, + @MyResultBuilder paramResultBuilder: () -> Int? = { nil } + ) { + fatalError() + } +} + +let buildable = VariableDecl2(paramInt: 1) { // expected-error {{ambiguous use of 'init'}} +} diff --git a/validation-test/Sema/type_checker_crashers_fixed/sr14893.swift b/validation-test/Sema/type_checker_crashers_fixed/sr14893.swift new file mode 100644 index 0000000000000..5072b2828e6ea --- /dev/null +++ b/validation-test/Sema/type_checker_crashers_fixed/sr14893.swift @@ -0,0 +1,29 @@ +// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.15 -swift-version 5 + +// REQUIRES: OS=macosx + +enum Category { +case first +} + +protocol View {} + +extension View { + func test(_ tag: Category) -> some View { + Image() + } +} + +@resultBuilder struct ViewBuilder { + static func buildBlock(_ content: Content) -> Content where Content : View { fatalError() } +} + +struct Image : View { +} + +struct MyView { + @ViewBuilder var body: some View { + let icon: Category! = Category.first // expected-error {{using '!' is not allowed here; perhaps '?' was intended?}} {{23-24=?}} + Image().test(icon) + } +} diff --git a/validation-test/Sema/type_checker_perf/fast/rdar74853403.swift b/validation-test/Sema/type_checker_perf/fast/rdar74853403.swift new file mode 100644 index 0000000000000..8251c40d30e5b --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/rdar74853403.swift @@ -0,0 +1,14 @@ +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: tools-release,no_asan + +func makeString(_ strings: [String]) -> String { "" } +func makeString(_ string: String) -> String { "" } + +func test(message: inout String, d: [String: String]?) { + message += d.map { + $0.reduce("") { + $0 + makeString($1.key) + "" + makeString($1.value) + "" + makeString($1.key) + "" + } + } ?? "" +} + diff --git a/validation-test/Sema/type_checker_perf/slow/rdar19737632.swift b/validation-test/Sema/type_checker_perf/slow/rdar19737632.swift index b47a6144edd09..21d04ecceb649 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar19737632.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar19737632.swift @@ -4,5 +4,8 @@ let a = "a" let b = "b" let c = 42 -_ = "a=" + a + ";b=" + b + ";c=" + c +let d = 0.0 +let e: Float = 1.0 + +_ = "a=" + a + ";b=" + b + ";c=" + c + ";d=" + d + ";e=" + e // expected-error@-1 {{reasonable time}} diff --git a/validation-test/Serialization/AllowErrors/invalid-attr-refs.swift b/validation-test/Serialization/AllowErrors/invalid-attr-refs.swift new file mode 100644 index 0000000000000..dfcf6cfeeed23 --- /dev/null +++ b/validation-test/Serialization/AllowErrors/invalid-attr-refs.swift @@ -0,0 +1,14 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/errors.swiftmodule -experimental-allow-module-with-compiler-errors %s + +// Property wrappers are invalid on top level code, check allowing errors +// does not crash + +@propertyWrapper +public struct Wrapper { + public var wrappedValue: T + public init() {} +} +@Wrapper +public var topLevelVar: Int = 10 diff --git a/validation-test/Serialization/AllowErrors/invalid-attr.swift b/validation-test/Serialization/AllowErrors/invalid-attr.swift new file mode 100644 index 0000000000000..93a0b2ed9e58b --- /dev/null +++ b/validation-test/Serialization/AllowErrors/invalid-attr.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/errors.swiftmodule -experimental-allow-module-with-compiler-errors %s + +// @discardableResult is not allowed on a struct, make sure we don't crash +// when allowing errors + +@discardableResult +struct SomeStruct {} diff --git a/validation-test/Serialization/AllowErrors/invalid-pattern.swift b/validation-test/Serialization/AllowErrors/invalid-pattern.swift new file mode 100644 index 0000000000000..b0bff08cb58e3 --- /dev/null +++ b/validation-test/Serialization/AllowErrors/invalid-pattern.swift @@ -0,0 +1,7 @@ +// RUN: %empty-directory(%t) + +// -parse-as-library added so that the PDB isn't added to a TopLevelCodeDecl, +// which isn't serialized at all +// RUN: %target-swift-frontend -emit-module -o %t/errors.swiftmodule -module-name errors -experimental-allow-module-with-compiler-errors -parse-as-library %s + +let self = 1 diff --git a/validation-test/Serialization/AllowErrors/invalid-witness.swift b/validation-test/Serialization/AllowErrors/invalid-witness.swift new file mode 100644 index 0000000000000..cef74d81d78c8 --- /dev/null +++ b/validation-test/Serialization/AllowErrors/invalid-witness.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/errors.partial.swiftmodule -experimental-allow-module-with-compiler-errors %s +// RUN: %target-swift-frontend -merge-modules -module-name errors -emit-module -o %t/errors.swiftmodule -experimental-allow-module-with-compiler-errors %t/errors.partial.swiftmodule + +public protocol SomeProto { + init(from: SomeProto) +} + +public struct A {} +public struct B: SomeProto { + let a: A +} + +public let thing = B(a: A()) diff --git a/validation-test/Serialization/AllowErrors/unresolved-type.swift b/validation-test/Serialization/AllowErrors/unresolved-type.swift new file mode 100644 index 0000000000000..137df41cf53a3 --- /dev/null +++ b/validation-test/Serialization/AllowErrors/unresolved-type.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -module-name errors -emit-module -o %t/errors.swiftmodule -experimental-allow-module-with-compiler-errors %s + +protocol SomeProto {} + +extension SomeProto { + func someFunc(arg: + +enum SomeEnum { + case a +} diff --git a/validation-test/Serialization/bridging-header-first.swift b/validation-test/Serialization/bridging-header-first.swift index 42e1ac06d6075..a05b57c65c3fa 100644 --- a/validation-test/Serialization/bridging-header-first.swift +++ b/validation-test/Serialization/bridging-header-first.swift @@ -18,7 +18,7 @@ // CHECK-DUMP: IMPORTED_MODULE // CHECK-DUMP-SAME: 'Module' // CHECK-DUMP: IMPORTED_MODULE -// CHECK-DUMP-SAME: 'Swift' +// CHECK-DUMP: 'Swift' import Module diff --git a/validation-test/Serialization/rdar77804605.swift b/validation-test/Serialization/rdar77804605.swift new file mode 100644 index 0000000000000..2b9e5b62266e8 --- /dev/null +++ b/validation-test/Serialization/rdar77804605.swift @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module-path %t/WrappedParameter.swiftmodule -emit-module-source-info-path %t/WrappedParameter.swiftsourceinfo -module-name WrappedParameter -enable-testing %s +// RUN: %target-swift-frontend -merge-modules -emit-module %t/WrappedParameter.swiftmodule -module-name WrappedParameter -o %t/WrappedParameter.swiftmodule + +// Make sure wrapped parameters don't crash in merge-modules when +// they were compiled with -emit-module-source-info and -enable-testing. + +@propertyWrapper +struct ProjectionWrapper { + var wrappedValue: Value + + var projectedValue: Self { self } + + public init(projectedValue: Self) { + self = projectedValue + } +} + +func test(@ProjectionWrapper value: Int) {} diff --git a/validation-test/Serialization/rdar80449046.swift b/validation-test/Serialization/rdar80449046.swift new file mode 100644 index 0000000000000..156d198d55ebe --- /dev/null +++ b/validation-test/Serialization/rdar80449046.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module %s + +@propertyWrapper +public struct TestWrapper { + public var wrappedValue: Int + public init(wrappedValue: Int) { self.wrappedValue = wrappedValue } +} + +@frozen public struct Test { + @TestWrapper public var x: Int = 42 +} + diff --git a/validation-test/StdlibUnittest/AtomicInt.swift b/validation-test/StdlibUnittest/AtomicInt.swift index 9bc3e8b3f1f8a..1f57876f54c45 100644 --- a/validation-test/StdlibUnittest/AtomicInt.swift +++ b/validation-test/StdlibUnittest/AtomicInt.swift @@ -1,6 +1,7 @@ // RUN: %empty-directory(%t) // // RUN: %target-build-swift -module-name a %s -o %t.out -O +// RUN: %target-codesign %t.out // RUN: %target-run %t.out // REQUIRES: executable_test // REQUIRES: stress_test diff --git a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift index 84d1ffc73c1c3..176928ec6781b 100644 --- a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift +++ b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift @@ -43,4 +43,4 @@ struct X { var y: Int = 0 } var x = X() x ~> \X.y ≈> { a in a += 1; return 3 } // expected-error@-1 {{referencing operator function '~>' on 'P' requires that 'M, R>' conform to 'P'}} -// expected-error@-2 {{unable to infer complex closure return type; add explicit type to disambiguate}} +// expected-error@-2 {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar79383990.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar79383990.h new file mode 100644 index 0000000000000..dbe29c0ce3eba --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar79383990.h @@ -0,0 +1,12 @@ +@import Foundation; + +typedef NS_ENUM(NSInteger, BackgroundActivityResult) { + BackgroundActivityResultFinished = 1, + BackgroundActivityResultDeferred = 2, +}; + +typedef void (^BackgroundActivityCompletionHandler)(BackgroundActivityResult result); + +@interface BackgroundActivityScheduler : NSObject +- (void)scheduleWithBlock:(void (^)(BackgroundActivityCompletionHandler completionHandler))block; +@end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704382.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704382.h new file mode 100644 index 0000000000000..4c62941f022c6 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704382.h @@ -0,0 +1,25 @@ +#include + +#pragma clang assume_nonnull begin + +typedef NSString *FileProviderItemIdentifier NS_EXTENSIBLE_STRING_ENUM; +FileProviderItemIdentifier const + FileProviderRootContainerItemIdentifier NS_SWIFT_NAME(FileProviderItemIdentifier.rootContainer); +FileProviderItemIdentifier const + FileProviderWorkingSetContainerItemIdentifier NS_SWIFT_NAME(FileProviderItemIdentifier.workingSet); +FileProviderItemIdentifier const + FileProviderTrashContainerItemIdentifier NS_SWIFT_NAME(FileProviderItemIdentifier.trashContainer); +typedef NSString *FileProviderDomainIdentifier NS_EXTENSIBLE_STRING_ENUM; + +@interface PFXObject : NSObject ++ (void)getIdentifierForUserVisibleFileAtURL:(NSURL *)url + completionHandler: + (void (^)(FileProviderItemIdentifier __nullable + itemIdentifier, + FileProviderDomainIdentifier __nullable + domainIdentifier, + NSError *__nullable error)) + completionHandler; +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704382.m b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704382.m new file mode 100644 index 0000000000000..8007fa5396f64 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704382.m @@ -0,0 +1,23 @@ +#include "rdar80704382.h" + +#pragma clang assume_nonnull begin + +@implementation PFXObject +- (instancetype)init { + if (self = [super init]) { + } + return self; +} ++ (void)getIdentifierForUserVisibleFileAtURL:(NSURL *)url + completionHandler: + (void (^)(FileProviderItemIdentifier __nullable + itemIdentifier, + FileProviderDomainIdentifier __nullable + domainIdentifier, + NSError *__nullable error)) + completionHandler { + completionHandler(@"item_id", @"file_id", NULL); +} +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984.h new file mode 100644 index 0000000000000..255967c1ce020 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984.h @@ -0,0 +1,21 @@ +#include + +#pragma clang assume_nonnull begin + +typedef void (^CompletionHandler)(int status, NSUInteger bytesTransferred); + +@interface PFXObject : NSObject { +} +- (BOOL)enqueueErroryRequestWithError:(NSError *_Nullable *)error + completionHandler: + (nullable CompletionHandler)completionHandler; +- (BOOL)enqueueSyncSuccessfulErroryRequestWithError:(NSError *_Nullable *)error + completionHandler:(nullable CompletionHandler) + completionHandler; +- (BOOL)enqueueAsyncSuccessfulErroryRequestWithError:(NSError *_Nullable *)error + completionHandler: + (nullable CompletionHandler) + completionHandler; +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984.m b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984.m new file mode 100644 index 0000000000000..2eef915fa6d6f --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984.m @@ -0,0 +1,35 @@ +#include "rdar80704984.h" + +#pragma clang assume_nonnull begin + +@implementation PFXObject +- (instancetype)init { + if (self = [super init]) { + } + return self; +} +- (BOOL)enqueueErroryRequestWithError:(NSError *_Nullable *)error + completionHandler: + (nullable CompletionHandler)completionHandler { + *error = [[NSError alloc] initWithDomain:@"d" code:1 userInfo:nil]; + return NO; +} +- (BOOL)enqueueSyncSuccessfulErroryRequestWithError:(NSError *_Nullable *)error + completionHandler:(nullable CompletionHandler) + completionHandler { + completionHandler(0, 1); + return YES; +} +- (BOOL)enqueueAsyncSuccessfulErroryRequestWithError:(NSError *_Nullable *)error + completionHandler: + (nullable CompletionHandler) + completionHandler; +{ + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(0, 2); + }); + return YES; +} +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984_2.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984_2.h new file mode 100644 index 0000000000000..7a467b532ae4d --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984_2.h @@ -0,0 +1,24 @@ +#include + +#pragma clang assume_nonnull begin + +@interface PFXObject : NSObject { +} +- (BOOL)failReturnWithError:(NSError *_Nullable *)error + completionHandler: + (void (^_Nonnull)(NSError *_Nullable error))completionHandler; +- (BOOL)failInvokeSyncWithError:(NSError *_Nullable *)error + completionHandler: + (void (^_Nonnull)(NSError *_Nullable error))completionHandler; +- (BOOL)failInvokeAsyncWithError:(NSError *_Nullable *)error + completionHandler:(void (^_Nonnull)(NSError *_Nullable error)) + completionHandler; +- (BOOL)succeedSyncWithError:(NSError *_Nullable *)error + completionHandler: + (void (^_Nonnull)(NSError *_Nullable error))completionHandler; +- (BOOL)succeedAsyncWithError:(NSError *_Nullable *)error + completionHandler: + (void (^_Nonnull)(NSError *_Nullable error))completionHandler; +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984_2.m b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984_2.m new file mode 100644 index 0000000000000..d97d14afa2b76 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar80704984_2.m @@ -0,0 +1,51 @@ +#include "rdar80704984_2.h" + +#pragma clang assume_nonnull begin + +@implementation PFXObject +- (instancetype)init { + if (self = [super init]) { + } + return self; +} +- (BOOL)failReturnWithError:(NSError *_Nullable *)error + completionHandler: + (void (^_Nonnull)(NSError *_Nullable error))completionHandler { + *error = [NSError errorWithDomain:@"failReturn" code:1 userInfo:nil]; + return NO; +} +- (BOOL)failInvokeSyncWithError:(NSError *_Nullable *)error + completionHandler:(void (^_Nonnull)(NSError *_Nullable error)) + completionHandler { + completionHandler([NSError errorWithDomain:@"failInvokeSync" + code:2 + userInfo:nil]); + return YES; +} +- (BOOL)failInvokeAsyncWithError:(NSError *_Nullable *)error + completionHandler:(void (^_Nonnull)(NSError *_Nullable error)) + completionHandler { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + completionHandler([NSError errorWithDomain:@"failInvokeAsync" + code:2 + userInfo:nil]); + }); + return YES; +} +- (BOOL)succeedSyncWithError:(NSError *_Nullable *)error + completionHandler: + (void (^_Nonnull)(NSError *_Nullable error))completionHandler { + completionHandler(nil); + return YES; +} +- (BOOL)succeedAsyncWithError:(NSError *_Nullable *)error + completionHandler: + (void (^_Nonnull)(NSError *_Nullable error))completionHandler { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + completionHandler(nil); + }); + return YES; +} +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.h new file mode 100644 index 0000000000000..781130727fb21 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.h @@ -0,0 +1,29 @@ +#include + +#pragma clang assume_nonnull begin + +@interface PFXObject : NSObject { +} +- (void)continuePassSyncWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)continuePassAsyncWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)continueFailSyncWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)continueFailAsyncWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)continueIncorrectWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.m b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.m new file mode 100644 index 0000000000000..298f83bdbae9d --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.m @@ -0,0 +1,54 @@ +#include "rdar81590807.h" + +#pragma clang assume_nonnull begin + +@implementation PFXObject +- (void)continuePassSyncWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + ^{ + NSLog(@"passSync"); + }, + NULL, YES); +} +- (void)continuePassAsyncWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + completionHandler( + ^{ + NSLog(@"passAsync"); + }, + NULL, YES); + }); +} +- (void)continueFailSyncWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + NULL, [NSError errorWithDomain:@"failSync" code:1 userInfo:nil], NO); +} +- (void)continueFailAsyncWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + completionHandler( + NULL, [NSError errorWithDomain:@"failAsync" code:2 userInfo:nil], NO); + }); +} +- (void)continueIncorrectWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + completionHandler(NULL, NULL, NO); + }); +} +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.h new file mode 100644 index 0000000000000..deae0ddbd09eb --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.h @@ -0,0 +1,24 @@ +#include + +#pragma clang assume_nonnull begin + +@interface PFXObject : NSObject { +} +- (void)findAnswerSyncSuccessAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerSyncSuccess(completionHandler:)"))); +- (void)findAnswerAsyncSuccessAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerAsyncSuccess(completionHandler:)"))); +- (void)findAnswerSyncFailAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerSyncFail(completionHandler:)"))); +- (void)findAnswerAsyncFailAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerAsyncFail(completionHandler:)"))); +- (void)findAnswerIncorrectAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerIncorrect(completionHandler:)"))); +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.m b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.m new file mode 100644 index 0000000000000..4d8bf897c6dc0 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.m @@ -0,0 +1,37 @@ +#include "rdar81590807_2.h" + +#pragma clang assume_nonnull begin + +@implementation PFXObject +- (void)findAnswerSyncSuccessAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerSyncSuccess(completionHandler:)"))) { + handler(@"syncSuccess", NULL); +} +- (void)findAnswerAsyncSuccessAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerAsyncSuccess(completionHandler:)"))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + handler(@"asyncSuccess", NULL); + }); +} +- (void)findAnswerSyncFailAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerSyncFail(completionHandler:)"))) { + handler(NULL, [NSError errorWithDomain:@"syncFail" code:1 userInfo:nil]); +} +- (void)findAnswerAsyncFailAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerAsyncFail(completionHandler:)"))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + handler(NULL, [NSError errorWithDomain:@"asyncFail" code:2 userInfo:nil]); + }); +} +- (void)findAnswerIncorrectAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerIncorrect(completionHandler:)"))) { + handler(NULL, NULL); +} +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81617749.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81617749.h new file mode 100644 index 0000000000000..fd609fd09719a --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81617749.h @@ -0,0 +1,123 @@ +#include + +#pragma clang assume_nonnull begin + +typedef void (^CompletionHandler)(void); + +@interface PFXObject : NSObject +- (void)performSingleFlaggy1WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))); +- (void)performSingleFlaggy2WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 2))); + +- (void)performSingleErrory1WithCompletionHandler: + (void (^)(NSError *_Nullable, + CompletionHandler _Nullable))completionHandler; +- (void)performSingleErrory2WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, + NSError *_Nullable))completionHandler; + +- (void)performSingleBothy12WithCompletionHandler: + (void (^)(NSError *_Nullable, BOOL, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))); +- (void)performSingleBothy13WithCompletionHandler: + (void (^)(NSError *_Nullable, CompletionHandler _Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)performSingleBothy21WithCompletionHandler: + (void (^)(BOOL, NSError *_Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))); +- (void)performSingleBothy23WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)performSingleBothy31WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))); +- (void)performSingleBothy32WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))); + +- (void)performDoubleFlaggy1WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))); +- (void)performDoubleFlaggy2WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))); +- (void)performDoubleFlaggy3WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, CompletionHandler _Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); + +- (void)performDoubleErrory1WithCompletionHandler: + (void (^)(NSError *_Nullable, CompletionHandler _Nullable, + CompletionHandler _Nullable))completionHandler; +- (void)performDoubleErrory2WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, NSError *_Nullable, + CompletionHandler _Nullable))completionHandler; +- (void)performDoubleErrory3WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, CompletionHandler _Nullable, + NSError *_Nullable))completionHandler; + +- (void)performDoubleBothy12WithCompletionHandler: + (void (^)(NSError *_Nullable, BOOL, CompletionHandler _Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))); +- (void)performDoubleBothy13WithCompletionHandler: + (void (^)(NSError *_Nullable, CompletionHandler _Nullable, BOOL, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)performDoubleBothy14WithCompletionHandler: + (void (^)(NSError *_Nullable, CompletionHandler _Nullable, + CompletionHandler _Nullable, BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 4))); + +- (void)performDoubleBothy21WithCompletionHandler: + (void (^)(BOOL, NSError *_Nullable, CompletionHandler _Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))); +- (void)performDoubleBothy23WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, NSError *_Nullable, BOOL, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)performDoubleBothy24WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, NSError *_Nullable, + CompletionHandler _Nullable, BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 4))); + +- (void)performDoubleBothy31WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable, NSError *_Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))); +- (void)performDoubleBothy32WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL, NSError *_Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))); +- (void)performDoubleBothy34WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, CompletionHandler _Nullable, + NSError *_Nullable, BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 4))); + +- (void)performDoubleBothy41WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable, CompletionHandler _Nullable, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))); +- (void)performDoubleBothy42WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL, CompletionHandler _Nullable, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))); +- (void)performDoubleBothy43WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, CompletionHandler _Nullable, BOOL, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81617749.m b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81617749.m new file mode 100644 index 0000000000000..eea44ee51123a --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81617749.m @@ -0,0 +1,343 @@ +#include "rdar81617749.h" + +#pragma clang assume_nonnull begin + +@implementation PFXObject +- (void)performSingleFlaggy1WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))) { + completionHandler(YES, ^{ + fprintf(stdout, "%s\n", "performSingleFlaggy1"); + }); +} +- (void)performSingleFlaggy2WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 2))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performSingleFlaggy2"); + }, + YES); +} + +- (void)performSingleErrory1WithCompletionHandler: + (void (^)(NSError *_Nullable, + CompletionHandler _Nullable))completionHandler { + completionHandler(NULL, ^{ + fprintf(stdout, "%s\n", "performSingleErrory1"); + }); +} +- (void)performSingleErrory2WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, + NSError *_Nullable))completionHandler { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performSingleErrory2"); + }, + NULL); +} + +- (void)performSingleBothy12WithCompletionHandler: + (void (^)(NSError *_Nullable, BOOL, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))) { + completionHandler(NULL, YES, ^{ + fprintf(stdout, "%s\n", "performSingleBothy12"); + }); +} +- (void)performSingleBothy13WithCompletionHandler: + (void (^)(NSError *_Nullable, CompletionHandler _Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + NULL, + ^{ + fprintf(stdout, "%s\n", "performSingleBothy13"); + }, + YES); +} +- (void)performSingleBothy21WithCompletionHandler: + (void (^)(BOOL, NSError *_Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))) { + completionHandler(YES, NULL, ^{ + fprintf(stdout, "%s\n", "performSingleBothy21"); + }); +} +- (void)performSingleBothy23WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performSingleBothy23"); + }, + NULL, YES); +} +- (void)performSingleBothy31WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))) { + completionHandler( + YES, + ^{ + fprintf(stdout, "%s\n", "performSingleBothy31"); + }, + NULL); +} +- (void)performSingleBothy32WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performSingleBothy32"); + }, + YES, NULL); +} + +- (void)performDoubleFlaggy1WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))) { + completionHandler( + YES, + ^{ + fprintf(stdout, "%s\n", "performDoubleFlaggy1, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleFlaggy1, part 2"); + }); +} +- (void)performDoubleFlaggy2WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleFlaggy2, part 1"); + }, + YES, + ^{ + fprintf(stdout, "%s\n", "performDoubleFlaggy2, part 2"); + }); +} +- (void)performDoubleFlaggy3WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, CompletionHandler _Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleFlaggy3, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleFlaggy3, part 2"); + }, + YES); +} + +- (void)performDoubleErrory1WithCompletionHandler: + (void (^)(NSError *_Nullable, CompletionHandler _Nullable, + CompletionHandler _Nullable))completionHandler { + completionHandler( + NULL, + ^{ + fprintf(stdout, "%s\n", "performDoubleErrory1, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleErrory1, part 2"); + }); +} +- (void)performDoubleErrory2WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, NSError *_Nullable, + CompletionHandler _Nullable))completionHandler { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleErrory2, part 1"); + }, + NULL, + ^{ + fprintf(stdout, "%s\n", "performDoubleErrory2, part 2"); + }); +} +- (void)performDoubleErrory3WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, CompletionHandler _Nullable, + NSError *_Nullable))completionHandler { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleErrory3, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleErrory3, part 2"); + }, + NULL); +} + +- (void)performDoubleBothy12WithCompletionHandler: + (void (^)(NSError *_Nullable, BOOL, CompletionHandler _Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))) { + completionHandler( + NULL, YES, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy12, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy12, part 2"); + }); +} +- (void)performDoubleBothy13WithCompletionHandler: + (void (^)(NSError *_Nullable, CompletionHandler _Nullable, BOOL, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + NULL, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy13, part 1"); + }, + YES, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy13, part 2"); + }); +} +- (void)performDoubleBothy14WithCompletionHandler: + (void (^)(NSError *_Nullable, CompletionHandler _Nullable, + CompletionHandler _Nullable, BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 4))) { + completionHandler( + NULL, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy14, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy14, part 2"); + }, + YES); +} + +- (void)performDoubleBothy21WithCompletionHandler: + (void (^)(BOOL, NSError *_Nullable, CompletionHandler _Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))) { + completionHandler( + YES, NULL, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy21, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy21, part 2"); + }); +} +- (void)performDoubleBothy23WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, NSError *_Nullable, BOOL, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy23, part 1"); + }, + NULL, YES, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy23, part 2"); + }); +} +- (void)performDoubleBothy24WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, NSError *_Nullable, + CompletionHandler _Nullable, BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 4))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy24, part 1"); + }, + NULL, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy24, part 2"); + }, + YES); +} + +- (void)performDoubleBothy31WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable, NSError *_Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))) { + completionHandler( + YES, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy31, part 1"); + }, + NULL, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy31, part 2"); + }); +} +- (void)performDoubleBothy32WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL, NSError *_Nullable, + CompletionHandler _Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy32, part 1"); + }, + YES, NULL, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy32, part 2"); + }); +} +- (void)performDoubleBothy34WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, CompletionHandler _Nullable, + NSError *_Nullable, BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 4))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy34, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy34, part 2"); + }, + NULL, YES); +} + +- (void)performDoubleBothy41WithCompletionHandler: + (void (^)(BOOL, CompletionHandler _Nullable, CompletionHandler _Nullable, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 1))) { + completionHandler( + YES, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy41, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy41, part 2"); + }, + NULL); +} +- (void)performDoubleBothy42WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, BOOL, CompletionHandler _Nullable, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 2))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy42, part 1"); + }, + YES, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy42, part 2"); + }, + NULL); +} +- (void)performDoubleBothy43WithCompletionHandler: + (void (^)(CompletionHandler _Nullable, CompletionHandler _Nullable, BOOL, + NSError *_Nullable))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy43, part 1"); + }, + ^{ + fprintf(stdout, "%s\n", "performDoubleBothy43, part 2"); + }, + YES, NULL); +} +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/rdar57728533.swift b/validation-test/compiler_crashers_2_fixed/rdar57728533.swift new file mode 100644 index 0000000000000..03371e8af990d --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar57728533.swift @@ -0,0 +1,10 @@ +// RUN: not %target-swift-frontend -typecheck %s + +protocol Item { + associatedtype Rule + func isValide(valid: Rule) -> Bool +} + +protocol PairableItem: Item { + associatedtype AssociatedItem: PairableItem where AssociatedItem.Rule: Rule +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar79383990.swift b/validation-test/compiler_crashers_2_fixed/rdar79383990.swift new file mode 100644 index 0000000000000..2c2f4ce080775 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar79383990.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-frontend %s -emit-silgen -disable-availability-checking -import-objc-header %S/Inputs/rdar79383990.h +// REQUIRES: objc_interop +// REQUIRES: OS=macosx + +import Foundation + +func test(s: BackgroundActivityScheduler) async { + _ = await s.schedule() +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar80296242.swift b/validation-test/compiler_crashers_2_fixed/rdar80296242.swift new file mode 100644 index 0000000000000..0cce254a61e5d --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar80296242.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-frontend -emit-sil %s + +public class C { + public func foo() -> Self { + let arr = [self] + + bar(arr) + + return self + } +} + +@_transparent public func bar(_ xs: T) { + for x in xs { _ = x } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar80704382.swift b/validation-test/compiler_crashers_2_fixed/rdar80704382.swift new file mode 100644 index 0000000000000..87da3b5acfedf --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar80704382.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar80704382.m -I %S/Inputs -c -o %t/rdar80704382.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar80704382.h -Xlinker %t/rdar80704382.o -parse-as-library %s -o %t/main +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +// rdar://82123254 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +func run() async throws { + // CHECK: item_id + // CHECK: file_id + print(try await PFXObject.identifierForUserVisibleFile(at: URL(fileURLWithPath: "/tmp/file"))) +} + +@main struct Main { + static func main() async throws { + try await run() + } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar80704984.swift b/validation-test/compiler_crashers_2_fixed/rdar80704984.swift new file mode 100644 index 0000000000000..4add2fa7d4988 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar80704984.swift @@ -0,0 +1,55 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar80704984.m -I %S/Inputs -c -o %t/rdar80704984.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar80704984.h -Xlinker %t/rdar80704984.o -parse-as-library %s -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +// rdar://82123254 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +func run1(on object: PFXObject) async throws { + do { + try await object.enqueueErroryRequest() + fatalError(); + } catch let error { + // CHECK: Domain=d Code=1 + print(error) + } +} +func run2(on object: PFXObject) async throws { + // CHECK: (0, 1) + print(try await object.enqueueSyncSuccessfulErroryRequest()) +} + +func run3(on object: PFXObject) async throws { + // CHECK: (0, 2) + print(try await object.enqueueAsyncSuccessfulErroryRequest()) +} + +func runAll(on object: PFXObject) async throws { + do { + try await object.enqueueErroryRequest() + fatalError(); + } catch let error { + // CHECK: Domain=d Code=1 + print(error) + } + // CHECK: (0, 1) + print(try await object.enqueueSyncSuccessfulErroryRequest()) + // CHECK: (0, 2) + print(try await object.enqueueAsyncSuccessfulErroryRequest()) +} + +@main struct Main { + static func main() async throws { + let object = PFXObject() + try await run1(on: object) + try await run2(on: object) + try await run3(on: object) + try await runAll(on: object) + } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar80704984_2.swift b/validation-test/compiler_crashers_2_fixed/rdar80704984_2.swift new file mode 100644 index 0000000000000..59cc23c150346 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar80704984_2.swift @@ -0,0 +1,98 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar80704984_2.m -I %S/Inputs -c -o %t/rdar80704984_2.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar80704984_2.h -Xlinker %t/rdar80704984_2.o -parse-as-library %s -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +// rdar://82123254 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +func run1(on object: PFXObject) async throws { + do { + try await object.failReturn() + fatalError() + } + catch let error { + // CHECK: Error Domain=failReturn Code=1 "(null)" + print(error) + } +} + +func run2(on object: PFXObject) async throws { + do { + try await object.failInvokeSync() + fatalError() + } + catch let error { + // CHECK: Error Domain=failInvokeSync Code=2 "(null)" + print(error) + } +} + +func run3(on object: PFXObject) async throws { + do { + try await object.failInvokeAsync() + fatalError() + } + catch let error { + // CHECK: Error Domain=failInvokeAsync Code=2 "(null)" + print(error) + } +} + +func run4(on object: PFXObject) async throws { + // CHECK: () + print(try await object.succeedSync()) +} + +func run5(on object: PFXObject) async throws { + // CHECK: () + print(try await object.succeedAsync()) +} + +func runAll(on object: PFXObject) async throws { + do { + try await object.failReturn() + fatalError() + } + catch let error { + // CHECK: Error Domain=failReturn Code=1 "(null)" + print(error) + } + do { + try await object.failInvokeSync() + fatalError() + } + catch let error { + // CHECK: Error Domain=failInvokeSync Code=2 "(null)" + print(error) + } + do { + try await object.failInvokeAsync() + fatalError() + } + catch let error { + // CHECK: Error Domain=failInvokeAsync Code=2 "(null)" + print(error) + } + // CHECK: () + print(try await object.succeedSync()) + // CHECK: () + print(try await object.succeedAsync()) +} + +@main struct Main { + static func main() async throws { + let object = PFXObject() + try await run1(on: object) + try await run2(on: object) + try await run3(on: object) + try await run4(on: object) + try await run5(on: object) + try await runAll(on: object) + } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar81590807.swift b/validation-test/compiler_crashers_2_fixed/rdar81590807.swift new file mode 100644 index 0000000000000..2d82ccc13825c --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar81590807.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar81590807.m -I %S/Inputs -c -o %t/rdar81590807.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar81590807.h -Xlinker %t/rdar81590807.o -parse-as-library %s -o %t/main +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main > %t/log 2>&1 || true +// RUN: %FileCheck %s < %t/log + +// Unsupported because the crash on continueIncorrect is just an illegal +// instruction rather than a nice fatal error. +// UNSUPPORTED: swift_test_mode_optimize +// UNSUPPORTED: swift_test_mode_optimize_size + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=ios + +// rdar://82123254 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +func run(on object: PFXObject) async throws { + // CHECK: passSync + let cl1 = try await object.continuePassSync() + cl1() + // CHECK: passAsync + let cl2 = try await object.continuePassAsync() + cl2() + do { + let cl = try await object.continueFailSync() + // CHECK-NOT: oh no failSync + fputs("oh no failSync\n", stderr) + } + catch let error { + // CHECK: Error Domain=failSync Code=1 "(null)" + fputs("\(error)\n", stderr) + } + do { + let cl = try await object.continueFailAsync() + // CHECK-NOT: oh no failAsync + fputs("oh no failAsync\n", stderr) + } + catch let error { + // CHECK: Error Domain=failAsync Code=2 "(null)" + fputs("\(error)\n", stderr) + } + // CHECK: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value + print(try await object.continueIncorrect()) +} + +@main struct Main { + static func main() async throws { + let object = PFXObject() + try await run(on: object) + } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar81590807_2.swift b/validation-test/compiler_crashers_2_fixed/rdar81590807_2.swift new file mode 100644 index 0000000000000..4f43f9359028e --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar81590807_2.swift @@ -0,0 +1,46 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar81590807_2.m -I %S/Inputs -c -o %t/rdar81590807_2.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar81590807_2.h -Xlinker %t/rdar81590807_2.o -parse-as-library %s -o %t/main +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=ios + +// rdar://82123254 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +func run(on object: PFXObject) async throws { + // CHECK: syncSuccess + print("\(try await object.findAnswerSyncSuccess())\n") + // CHECK: asyncSuccess + print("\(try await object.findAnswerAsyncSuccess())\n") + do { + try await object.findAnswerSyncFail() + // CHECK-NOT: oh no syncFail + print("\("oh no syncFail")\n") + } + catch let error { + // CHECK: Error Domain=syncFail Code=1 "(null)" + print("\(error)\n") + } + do { + try await object.findAnswerAsyncFail() + // CHECK-NOT: oh no asyncFail + print("\("oh no asyncFail")\n") + } + catch let error { + // CHECK: Error Domain=asyncFail Code=2 "(null)" + print("\(error)\n") + } + // CHECK: <<>> + print("<<\(try await object.findAnswerIncorrect())>>\n") +} + +@main struct Main { + static func main() async throws { + let object = PFXObject() + try await run(on: object) + } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar81617749.swift b/validation-test/compiler_crashers_2_fixed/rdar81617749.swift new file mode 100644 index 0000000000000..eedb2b0d37555 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar81617749.swift @@ -0,0 +1,144 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar81617749.m -I %S/Inputs -c -o %t/rdar81617749.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar81617749.h -Xlinker %t/rdar81617749.o -parse-as-library %s -o %t/main +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +// Enable with rdar://81617749 +// UNSUPPORTED: CPU=i386 && OS=watchos + +// rdar://82123254 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +func run(on object: PFXObject) async throws { + // CHECK: performSingleFlaggy1 + print(try await object.performSingleFlaggy1()?()) + // CHECK: performSingleFlaggy2 + print(try await object.performSingleFlaggy2()?()) + // CHECK: performSingleErrory1 + print(try await object.performSingleErrory1()()) + // CHECK: performSingleErrory2 + print(try await object.performSingleErrory2()()) + + // CHECK: performSingleBothy12 + print(try await object.performSingleBothy12()()) + // CHECK: performSingleBothy13 + print(try await object.performSingleBothy13()()) + + // CHECK: performSingleBothy21 + print(try await object.performSingleBothy21()()) + + // CHECK: performSingleBothy23 + print(try await object.performSingleBothy23()()) + // CHECK: performSingleBothy31 + print(try await object.performSingleBothy31()()) + // CHECK: performSingleBothy32 + print(try await object.performSingleBothy32()()) + + // CHECK: performDoubleFlaggy1, part 1 + // CHECK: performDoubleFlaggy1, part 2 + let rFlaggy1 = try await object.performDoubleFlaggy1() + rFlaggy1.0?() + rFlaggy1.1?() + // CHECK: performDoubleFlaggy2, part 1 + // CHECK: performDoubleFlaggy2, part 2 + let rFlaggy2 = try await object.performDoubleFlaggy2() + rFlaggy2.0?() + rFlaggy2.1?() + // CHECK: performDoubleFlaggy3, part 1 + // CHECK: performDoubleFlaggy3, part 2 + let rFlaggy3 = try await object.performDoubleFlaggy3() + rFlaggy3.0?() + rFlaggy3.1?() + + // CHECK: performDoubleErrory1, part 1 + // CHECK: performDoubleErrory1, part 2 + let rErrory1 = try await object.performDoubleErrory1() + rErrory1.0() + rErrory1.1() + // CHECK: performDoubleErrory2, part 1 + // CHECK: performDoubleErrory2, part 2 + let rErrory2 = try await object.performDoubleErrory2() + rErrory2.0() + rErrory2.1() + // CHECK: performDoubleErrory3, part 1 + // CHECK: performDoubleErrory3, part 2 + let rErrory3 = try await object.performDoubleErrory3() + rErrory3.0() + rErrory3.1() + + // CHECK: performDoubleBothy12, part 1 + // CHECK: performDoubleBothy12, part 2 + let rBothy12 = try await object.performDoubleBothy12() + rBothy12.0() + rBothy12.1() + // CHECK: performDoubleBothy13, part 1 + // CHECK: performDoubleBothy13, part 2 + let rBothy13 = try await object.performDoubleBothy13() + rBothy13.0() + rBothy13.1() + // CHECK: performDoubleBothy14, part 1 + // CHECK: performDoubleBothy14, part 2 + let rBothy14 = try await object.performDoubleBothy14() + rBothy14.0() + rBothy14.1() + + // CHECK: performDoubleBothy21, part 1 + // CHECK: performDoubleBothy21, part 2 + let rBothy21 = try await object.performDoubleBothy21() + rBothy21.0() + rBothy21.1() + // CHECK: performDoubleBothy23, part 1 + // CHECK: performDoubleBothy23, part 2 + let rBothy23 = try await object.performDoubleBothy23() + rBothy23.0() + rBothy23.1() + // CHECK: performDoubleBothy24, part 1 + // CHECK: performDoubleBothy24, part 2 + let rBothy24 = try await object.performDoubleBothy24() + rBothy24.0() + rBothy24.1() + + // CHECK: performDoubleBothy31, part 1 + // CHECK: performDoubleBothy31, part 2 + let rBothy31 = try await object.performDoubleBothy31() + rBothy31.0() + rBothy31.1() + // CHECK: performDoubleBothy32, part 1 + // CHECK: performDoubleBothy32, part 2 + let rBothy32 = try await object.performDoubleBothy32() + rBothy32.0() + rBothy32.1() + // CHECK: performDoubleBothy34, part 1 + // CHECK: performDoubleBothy34, part 2 + let rBothy34 = try await object.performDoubleBothy34() + rBothy34.0() + rBothy34.1() + + // CHECK: performDoubleBothy41, part 1 + // CHECK: performDoubleBothy41, part 2 + let rBothy41 = try await object.performDoubleBothy41() + rBothy41.0() + rBothy41.1() + // CHECK: performDoubleBothy42, part 1 + // CHECK: performDoubleBothy42, part 2 + let rBothy42 = try await object.performDoubleBothy42() + rBothy42.0() + rBothy42.1() + // CHECK: performDoubleBothy43, part 1 + // CHECK: performDoubleBothy43, part 2 + let rBothy43 = try await object.performDoubleBothy43() + rBothy43.0() + rBothy43.1() +} + +@main struct Main { + static func main() async throws { + let object = PFXObject() + try await run(on: object) + } +} diff --git a/validation-test/compiler_crashers_2_fixed/sr14672.swift b/validation-test/compiler_crashers_2_fixed/sr14672.swift new file mode 100644 index 0000000000000..741a8321bb7d2 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr14672.swift @@ -0,0 +1,23 @@ +// RUN: %target-swift-frontend -emit-ir %s + +public protocol PublicContent { + associatedtype Model + init(_ model: Model) +} + +public protocol PublicConvertible { + associatedtype Public + func toPublic() -> Public +} + +extension PublicConvertible where Public: PublicContent, Public.Model == Self { + public func toPublic() -> Public { + Public(self) + } +} + +extension Array: PublicConvertible where Element: PublicConvertible { + public func toPublic() -> [Element.Public] { + map { $0.toPublic() } + } +} diff --git a/validation-test/compiler_crashers_2_fixed/sr14894.swift b/validation-test/compiler_crashers_2_fixed/sr14894.swift new file mode 100644 index 0000000000000..084134a05addb --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr14894.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-frontend -typecheck -verify %s + +struct S { + private let data: [[String]] + private func f() {} + + func test() { + // expected-error@+1 {{static method 'buildBlock' requires that 'ForEach<[String], ()>' conform to 'View'}} + ForEach(data) { group in + ForEach(group) { month in + self.f() + } + } + } +} + +struct Wrapper {} + +protocol View {} + +@resultBuilder struct Builder { + // expected-note@+1 {{where 'Content' = 'ForEach<[String], ()>'}} + static func buildBlock(_ content: Content) -> Content { fatalError() } +} + +struct ForEach where Data : RandomAccessCollection { + init(_ data: Wrapper, @Builder content: (Wrapper) -> Content) where C : MutableCollection {} + init(_ data: Data, @Builder content: @escaping (Data.Element) -> Content) {} +} diff --git a/validation-test/compiler_crashers_2_fixed/sr9584.swift b/validation-test/compiler_crashers_2_fixed/sr9584.swift new file mode 100644 index 0000000000000..04a22340f5827 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr9584.swift @@ -0,0 +1,14 @@ +// RUN: not %target-swift-frontend -typecheck %s + +struct S {} + +protocol P { + associatedtype A: P = Self + static func f(_ x: A) -> A +} + +extension S: P where N: P { + static func f(_ x: X) -> S where A == X, X.A == N { + return S() + } +} diff --git a/validation-test/compiler_scale/explicit_requirements_perf.swift b/validation-test/compiler_scale/explicit_requirements_perf.swift new file mode 100644 index 0000000000000..62bea10911ee9 --- /dev/null +++ b/validation-test/compiler_scale/explicit_requirements_perf.swift @@ -0,0 +1,9 @@ +// RUN: %scale-test --begin 1 --end 20 --step 1 --select NumRedundantRequirementSteps --polynomial-threshold 2 %s + +protocol P {} + +func f(_: T) where +%for i in range(0, N): + T : P, +%end + T : P {} diff --git a/validation-test/execution/arc_36509461.swift b/validation-test/execution/arc_36509461.swift index 0d42cc5c1f1b4..c3f1854209f75 100644 --- a/validation-test/execution/arc_36509461.swift +++ b/validation-test/execution/arc_36509461.swift @@ -10,9 +10,6 @@ // REQUIRES: objc_interop // REQUIRES: OS=macosx -// rdar://problem/47367694 tracks re-enabling this test for backward deployment. -// UNSUPPORTED: remote_run - import Foundation struct FakeUUID { diff --git a/validation-test/stdlib/Array.swift.gyb b/validation-test/stdlib/Array.swift.gyb index aa00b1df293e2..90849ee16aaa8 100644 --- a/validation-test/stdlib/Array.swift.gyb +++ b/validation-test/stdlib/Array.swift.gyb @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swiftgyb +// RUN: %enable-cow-checking %target-run-simple-swiftgyb // REQUIRES: executable_test import StdlibUnittest diff --git a/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_MutableRandomAccessCollectionRef.swift b/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_MutableRandomAccessCollectionRef.swift index 109f88c2a2c70..8f2bfd586a09f 100644 --- a/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_MutableRandomAccessCollectionRef.swift +++ b/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_MutableRandomAccessCollectionRef.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_MutableRandomAccessCollectionVal.swift b/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_MutableRandomAccessCollectionVal.swift index bbdc43410bacb..da4b613222aa1 100644 --- a/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_MutableRandomAccessCollectionVal.swift +++ b/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_MutableRandomAccessCollectionVal.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_RangeReplaceableRandomAccessCollectionRef.swift b/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_RangeReplaceableRandomAccessCollectionRef.swift index ef2ef1a0c52db..31befaaa7de4c 100644 --- a/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_RangeReplaceableRandomAccessCollectionRef.swift +++ b/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_RangeReplaceableRandomAccessCollectionRef.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_RangeReplaceableRandomAccessCollectionVal.swift b/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_RangeReplaceableRandomAccessCollectionVal.swift index 4b1ec47f55536..2167da803b34d 100644 --- a/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_RangeReplaceableRandomAccessCollectionVal.swift +++ b/validation-test/stdlib/Array/ArraySliceWithNonZeroStartIndex_RangeReplaceableRandomAccessCollectionVal.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ArraySlice_MutableRandomAccessCollectionRef.swift b/validation-test/stdlib/Array/ArraySlice_MutableRandomAccessCollectionRef.swift index b749ac0364914..3da0aad9f00d1 100644 --- a/validation-test/stdlib/Array/ArraySlice_MutableRandomAccessCollectionRef.swift +++ b/validation-test/stdlib/Array/ArraySlice_MutableRandomAccessCollectionRef.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ArraySlice_MutableRandomAccessCollectionVal.swift b/validation-test/stdlib/Array/ArraySlice_MutableRandomAccessCollectionVal.swift index d14d727cdbb16..feb8359926a9a 100644 --- a/validation-test/stdlib/Array/ArraySlice_MutableRandomAccessCollectionVal.swift +++ b/validation-test/stdlib/Array/ArraySlice_MutableRandomAccessCollectionVal.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ArraySlice_RangeReplaceableRandomAccessCollectionRef.swift b/validation-test/stdlib/Array/ArraySlice_RangeReplaceableRandomAccessCollectionRef.swift index 57901605e20e9..e95695e71e295 100644 --- a/validation-test/stdlib/Array/ArraySlice_RangeReplaceableRandomAccessCollectionRef.swift +++ b/validation-test/stdlib/Array/ArraySlice_RangeReplaceableRandomAccessCollectionRef.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ArraySlice_RangeReplaceableRandomAccessCollectionVal.swift b/validation-test/stdlib/Array/ArraySlice_RangeReplaceableRandomAccessCollectionVal.swift index f86fab51d5631..d1606e625b7e8 100644 --- a/validation-test/stdlib/Array/ArraySlice_RangeReplaceableRandomAccessCollectionVal.swift +++ b/validation-test/stdlib/Array/ArraySlice_RangeReplaceableRandomAccessCollectionVal.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/Array_MutableRandomAccessCollectionRef.swift b/validation-test/stdlib/Array/Array_MutableRandomAccessCollectionRef.swift index 3e9b734b3e34b..a5a417731e567 100644 --- a/validation-test/stdlib/Array/Array_MutableRandomAccessCollectionRef.swift +++ b/validation-test/stdlib/Array/Array_MutableRandomAccessCollectionRef.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/Array_MutableRandomAccessCollectionVal.swift b/validation-test/stdlib/Array/Array_MutableRandomAccessCollectionVal.swift index 66bd1f82352e6..40b631d53a0bb 100644 --- a/validation-test/stdlib/Array/Array_MutableRandomAccessCollectionVal.swift +++ b/validation-test/stdlib/Array/Array_MutableRandomAccessCollectionVal.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/Array_RangeReplaceableRandomAccessCollectionRef.swift b/validation-test/stdlib/Array/Array_RangeReplaceableRandomAccessCollectionRef.swift index 0fb00394032f8..237686864050b 100644 --- a/validation-test/stdlib/Array/Array_RangeReplaceableRandomAccessCollectionRef.swift +++ b/validation-test/stdlib/Array/Array_RangeReplaceableRandomAccessCollectionRef.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/Array_RangeReplaceableRandomAccessCollectionVal.swift b/validation-test/stdlib/Array/Array_RangeReplaceableRandomAccessCollectionVal.swift index 0d0f71968af18..d6eb7fa4d8a45 100644 --- a/validation-test/stdlib/Array/Array_RangeReplaceableRandomAccessCollectionVal.swift +++ b/validation-test/stdlib/Array/Array_RangeReplaceableRandomAccessCollectionVal.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ContiguousArray_MutableRandomAccessCollectionRef.swift b/validation-test/stdlib/Array/ContiguousArray_MutableRandomAccessCollectionRef.swift index b4028f705c8ee..344518cc9985b 100644 --- a/validation-test/stdlib/Array/ContiguousArray_MutableRandomAccessCollectionRef.swift +++ b/validation-test/stdlib/Array/ContiguousArray_MutableRandomAccessCollectionRef.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ContiguousArray_MutableRandomAccessCollectionVal.swift b/validation-test/stdlib/Array/ContiguousArray_MutableRandomAccessCollectionVal.swift index 6a822814346ad..6d1b04ebd3b75 100644 --- a/validation-test/stdlib/Array/ContiguousArray_MutableRandomAccessCollectionVal.swift +++ b/validation-test/stdlib/Array/ContiguousArray_MutableRandomAccessCollectionVal.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ContiguousArray_RangeReplaceableRandomAccessCollectionRef.swift b/validation-test/stdlib/Array/ContiguousArray_RangeReplaceableRandomAccessCollectionRef.swift index c395654818ef1..1d611bc5ae5e0 100644 --- a/validation-test/stdlib/Array/ContiguousArray_RangeReplaceableRandomAccessCollectionRef.swift +++ b/validation-test/stdlib/Array/ContiguousArray_RangeReplaceableRandomAccessCollectionRef.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/ContiguousArray_RangeReplaceableRandomAccessCollectionVal.swift b/validation-test/stdlib/Array/ContiguousArray_RangeReplaceableRandomAccessCollectionVal.swift index da06fd9ede81a..dfce2dd3686e5 100644 --- a/validation-test/stdlib/Array/ContiguousArray_RangeReplaceableRandomAccessCollectionVal.swift +++ b/validation-test/stdlib/Array/ContiguousArray_RangeReplaceableRandomAccessCollectionVal.swift @@ -3,7 +3,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/Array/Inputs/ArrayConformanceTests.swift.gyb b/validation-test/stdlib/Array/Inputs/ArrayConformanceTests.swift.gyb index f98eabd098819..b0d49c75fd84e 100644 --- a/validation-test/stdlib/Array/Inputs/ArrayConformanceTests.swift.gyb +++ b/validation-test/stdlib/Array/Inputs/ArrayConformanceTests.swift.gyb @@ -23,7 +23,7 @@ // Do Not Edit Directly! //===----------------------------------------------------------------------===// -// RUN: %target-run-simple-swift +// RUN: %enable-cow-checking %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: optimized_stdlib diff --git a/validation-test/stdlib/ArrayBridging.swift b/validation-test/stdlib/ArrayBridging.swift index 6c24bfe8dd0cf..2d3a4292705c2 100644 --- a/validation-test/stdlib/ArrayBridging.swift +++ b/validation-test/stdlib/ArrayBridging.swift @@ -3,6 +3,7 @@ // RUN: %target-clang -fobjc-arc %S/Inputs/SlurpFastEnumeration/SlurpFastEnumeration.m -c -o %t/SlurpFastEnumeration.o // RUN: echo '#sourceLocation(file: "%s", line: 1)' > "%t/main.swift" && cat "%s" >> "%t/main.swift" && chmod -w "%t/main.swift" // RUN: %target-build-swift -Xfrontend -disable-access-control -I %S/Inputs/SlurpFastEnumeration/ %t/main.swift %S/Inputs/DictionaryKeyValueTypes.swift %S/Inputs/DictionaryKeyValueTypesObjC.swift -Xlinker %t/SlurpFastEnumeration.o -o %t.out -O -swift-version 4 +// RUN: %target-codesign %t.out // RUN: %target-run %t.out // REQUIRES: executable_test diff --git a/validation-test/stdlib/ArrayNew.swift.gyb b/validation-test/stdlib/ArrayNew.swift.gyb deleted file mode 100644 index 1a6477aeab9e9..0000000000000 --- a/validation-test/stdlib/ArrayNew.swift.gyb +++ /dev/null @@ -1,1138 +0,0 @@ -// RUN: %empty-directory(%t) -// -// RUN: %gyb %s -o %t/main.swift -// RUN: %target-clang -fobjc-arc %S/Inputs/SlurpFastEnumeration/SlurpFastEnumeration.m -c -o %t/SlurpFastEnumeration.o -// RUN: %line-directive %t/main.swift -- %target-build-swift %S/Inputs/DictionaryKeyValueTypes.swift %S/Inputs/DictionaryKeyValueTypesObjC.swift %t/main.swift -I %S/Inputs/SlurpFastEnumeration/ -Xlinker %t/SlurpFastEnumeration.o -o %t/Array -Xfrontend -disable-access-control -swift-version 4.2 -// RUN: %target-codesign %t/Array && %line-directive %t/main.swift -- %target-run %t/Array -// REQUIRES: executable_test - -// FIXME: rdar://problem/55944126 -// UNSUPPORTED: CPU=armv7s || CPU=armv7k - -import StdlibUnittest -import StdlibCollectionUnittest - - -%{ -all_array_types = ['ContiguousArray', 'ArraySlice', 'Array'] -}% - -%for Self in all_array_types: -extension ${Self} { - typealias _BufferID = UnsafeRawPointer? - var _bufferID: _BufferID { - return unsafeBitCast(_owner, to: _BufferID.self) - } -} -%end - -var ArrayTestSuite = TestSuite("Array") - -//===----------------------------------------------------------------------===// -// COW tests -// FIXME: incomplete. -//===----------------------------------------------------------------------===// - -func withInoutInt(_ x: inout Int, body: (_ x: inout Int) -> Void) { - body(&x) -} - -func withInoutT(_ x: inout T, body: (_ x: inout T) -> Void) { - body(&x) -} - -% for element_type in ['TestValueTy', 'TestBridgedValueTy']: -% for array_type in all_array_types: -% if element_type == 'TestBridgedValueTy': -#if _runtime(_ObjC) -% end - -ArrayTestSuite.test("${array_type}<${element_type}>/subscript(_: Int)/COW") - .code { - var a: ${array_type}<${array_type}<${element_type}>> = [[ - ${element_type}(10), ${element_type}(20), ${element_type}(30), - ${element_type}(40), ${element_type}(50), ${element_type}(60), - ${element_type}(70) - ]] - let identityOuter = a._bufferID - var identityInner = a[0]._bufferID - - func checkIdentity(_ stackTrace: SourceLocStack) { -% if element_type == 'TestValueTy': - // Does not reallocate storage because we changed a property based on a - // reference; array storage was not changed. Writeback of the inner array - // does not happen. - expectEqual(identityOuter, a._bufferID, stackTrace: stackTrace) - expectEqual(identityInner, a[0]._bufferID, stackTrace: stackTrace) -% else: - expectEqual(identityOuter, a._bufferID, stackTrace: stackTrace) - - // Should not reallocate storage. - expectEqual(identityInner, a[0]._bufferID, stackTrace: stackTrace) -% end - } - - // Mutating through a subscript expression. - a[0][0] = ${element_type}(1010) - - checkIdentity(SourceLocStack().withCurrentLoc()) - - a[0][1].value = 1020 - - checkIdentity(SourceLocStack().withCurrentLoc()) - - withInoutT(&a) { - (x: inout ${array_type}<${array_type}<${element_type}>>) in - x[0][2].value += 1000 - } - - checkIdentity(SourceLocStack().withCurrentLoc()) - - withInoutT(&a[0]) { - (x: inout ${array_type}<${element_type}>) in - x[3].value += 1000 - } - - checkIdentity(SourceLocStack().withCurrentLoc()) - - // This will reallocate storage unless Array uses addressors for subscript. - //withInoutT(&a[0][4]) { - // (x: inout ${element_type}) in - // x.value += 1000 - //} - - // FIXME: both of these lines crash the compiler. - // Passing an expression based on addressors as - // 'inout' crashes SILGen - //withInoutT(&a[0][5].value, { $0 += 1000 }) - //withInoutInt(&a[0][6].value, { $0 += 1000 }) - - // Don't change the last element. - - expectEqual(1010, a[0][0].value) - expectEqual(1020, a[0][1].value) - expectEqual(1030, a[0][2].value) - expectEqual(1040, a[0][3].value) - expectEqual(50, a[0][4].value) - expectEqual(60, a[0][5].value) - expectEqual(70, a[0][6].value) -} - -ArrayTestSuite.test("${array_type}<${element_type}>/subscript(_: Range)/COW") - .code { - var a: ${array_type}<${array_type}<${element_type}>> = [[ - ${element_type}(10), ${element_type}(20), ${element_type}(30), - ${element_type}(40), ${element_type}(50), ${element_type}(60), - ${element_type}(70), ${element_type}(80), ${element_type}(90), - ]] - let identityOuter = a._bufferID - var identityInner = a[0]._bufferID - - func checkIdentity(_ stackTrace: SourceLocStack) { -% if element_type == 'TestValueTy': - // Does not reallocate storage because we changed a property based on a - // reference; array storage was not changed. - expectEqual(identityOuter, a._bufferID, stackTrace: stackTrace) - expectEqual(identityInner, a[0]._bufferID, stackTrace: stackTrace) -% else: - expectEqual(identityOuter, a._bufferID, stackTrace: stackTrace) - // Writeback happens in subscript(Range), but array notices that the new - // value did not change. - // Another writeback happens in Array.subscript(Int), but this is not what we - // want. - expectNotEqual(identityInner, a[0]._bufferID, stackTrace: stackTrace) - identityInner = a[0]._bufferID -% end - } - - // Mutating through a subscript expression. - a[0..<1][0][0] = ${element_type}(1010) - - // Reallocates storage because of the writeback in Array.subscript(Int). - expectEqual(identityOuter, a._bufferID) - expectNotEqual(identityInner, a[0]._bufferID) - identityInner = a[0]._bufferID - - a[0..<1][0][1].value = 1020 - - checkIdentity(SourceLocStack().withCurrentLoc()) - - withInoutT(&a) { - (x: inout ${array_type}<${array_type}<${element_type}>>) in - x[0..<1][0][2].value += 1000 - } - - checkIdentity(SourceLocStack().withCurrentLoc()) - - withInoutT(&a[0..<1]) { - (x: inout ArraySlice<${array_type}<${element_type}>>) in - x[0][3].value += 1000 - } - - checkIdentity(SourceLocStack().withCurrentLoc()) - - withInoutT(&a[0..<1][0]) { - (x: inout ${array_type}<${element_type}>) in - x[4].value += 1000 - } - - checkIdentity(SourceLocStack().withCurrentLoc()) - - withInoutT(&a[0..<1][0][5]) { - (x: inout ${element_type}) in - x.value += 1000 - } - - // Reallocates storage because of the writeback in Array.subscript(Int) - // (writeback is being requested for the array element even though it is not - // needed). - expectEqual(identityOuter, a._bufferID) - expectNotEqual(identityInner, a[0]._bufferID) - identityInner = a[0]._bufferID - - withInoutT(&a[0..<1][0][6].value) { - (x: inout Int) in - x += 1000 - } - - checkIdentity(SourceLocStack().withCurrentLoc()) - - withInoutInt(&a[0..<1][0][7].value) { - (x: inout Int) in - x += 1000 - } - - checkIdentity(SourceLocStack().withCurrentLoc()) - - // Don't change the last element. - - expectEqual(1010, a[0][0].value) - expectEqual(1020, a[0][1].value) - expectEqual(1030, a[0][2].value) - expectEqual(1040, a[0][3].value) - expectEqual(1050, a[0][4].value) - expectEqual(1060, a[0][5].value) - expectEqual(1070, a[0][6].value) - expectEqual(1080, a[0][7].value) - expectEqual(90, a[0][8].value) -} - -% if element_type == 'TestBridgedValueTy': -#endif // _runtime(_ObjC) -% end -% end -% end - -#if _runtime(_ObjC) -import Darwin -import StdlibUnittestFoundationExtras -import Foundation - -// FIXME: all the tests below are applicable to ArraySlice, too. - -//===----------------------------------------------------------------------===// -// NSArray -> Array bridging tests -// FIXME: incomplete. -//===----------------------------------------------------------------------===// - -func isNativeArray(_ a: Array) -> Bool { - return a._hoistableIsNativeTypeChecked() -} - -func isCocoaArray(_ a: Array) -> Bool { - return !isNativeArray(a) -} - -func getAsImmutableNSArray(_ a: Array) -> NSArray { - var elements = a.map { TestObjCValueTy($0) as AnyObject } - return NSArray(objects: &elements, count: elements.count) -} - -func getAsNSArray(_ a: Array) -> NSArray { - // Return an `NSMutableArray` to make sure that it has a unique - // pointer identity. - return getAsNSMutableArray(a) -} - -func getAsNSMutableArray(_ a: Array) -> NSMutableArray { - let result = NSMutableArray() - for element in a { - result.add(TestObjCValueTy(element)) - } - return result -} - -@objc -class CustomImmutableNSArray : NSArray { - init(_privateInit: ()) { - super.init() - } - - override init() { - expectUnreachable() - super.init() - } - - override init(objects: UnsafePointer?, count: Int) { - super.init(objects: objects, count: count) - } - - required init(coder aDecoder: NSCoder) { - fatalError("init(coder:) not implemented by CustomImmutableNSArray") - } - - @objc(copyWithZone:) - override func copy(with zone: NSZone?) -> Any { - CustomImmutableNSArray.timesCopyWithZoneWasCalled += 1 - return self - } - - @objc - override func object(at index: Int) -> Any { - CustomImmutableNSArray.timesObjectAtIndexWasCalled += 1 - return _data[index] - } - - @objc - override var count: Int { - CustomImmutableNSArray.timesCountWasCalled += 1 - return _data.count - } - - @objc - override func countByEnumerating( - with state: UnsafeMutablePointer, - objects: AutoreleasingUnsafeMutablePointer, - count: Int - ) -> Int { - var theState = state.pointee - if theState.state == 0 { - theState.state = 1 - theState.itemsPtr = - AutoreleasingUnsafeMutablePointer(_data._baseAddressIfContiguous) - theState.mutationsPtr = _fastEnumerationStorageMutationsPtr - state.pointee = theState - return _data.count - } - return 0 - } - - let _data = [ 10, 20, 30 ].map { TestObjCValueTy($0) } - - static var timesCopyWithZoneWasCalled = 0 - static var timesObjectAtIndexWasCalled = 0 - static var timesCountWasCalled = 0 -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.BridgeUsingAs") { - do { - let source = [ 10, 20, 30 ] - let nsa = getAsNSArray(source) - var result = nsa as Array - expectTrue(isCocoaArray(result)) - expectType(Array.self, &result) - checkSequence(source.map { TestObjCValueTy($0) as AnyObject }, result) { - ($0 as! TestObjCValueTy).value == ($1 as! TestObjCValueTy).value - } - } - do { - let source = [ 10, 20, 30 ] - let nsa = getAsNSArray(source) - var result = nsa as! Array - expectTrue(isCocoaArray(result)) - expectType(Array.self, &result) - checkSequence(source.map { TestObjCValueTy($0) }, result) { - $0.value == $1.value - } - } -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.BridgeUsingAs") { - let source = [ 10, 20, 30 ] - let nsa = getAsNSArray(source) - var result = nsa as! Array - expectTrue(isNativeArray(result)) - expectType(Array.self, &result) - checkSequence(source.map { TestBridgedValueTy($0) }, result) { - $0.value == $1.value - } -} - - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.ArrayIsCopied") { - let source = [ 10, 20, 30 ] - let nsa = getAsNSMutableArray(source) - let result = nsa as Array - expectTrue(isCocoaArray(result)) - - // Delete the value from NSMutableArray. - expectEqual(20, (nsa[1] as! TestObjCValueTy).value) - nsa.removeObject(at: 1) - expectEqual(30, (nsa[1] as! TestObjCValueTy).value) - - // Check that the Array is not affected. - expectEqual(20, (result[1] as! TestObjCValueTy).value) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ArrayIsCopied") { - let source = [ 10, 20, 30 ] - let nsa = getAsNSMutableArray(source) - var result = nsa as AnyObject as! Array - expectTrue(isNativeArray(result)) - - // Delete the value from NSMutableArray. - expectEqual(20, (nsa[1] as! TestObjCValueTy).value) - nsa.removeObject(at: 1) - expectEqual(30, (nsa[1] as! TestObjCValueTy).value) - - // Check that the Array is not affected. - expectEqual(20, result[1].value) -} - - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.NSArrayIsRetained") { - let nsa = NSArray(array: getAsNSArray([ 10, 20, 30 ])) - let a: Array = convertNSArrayToArray(nsa) - let bridgedBack: NSArray = convertArrayToNSArray(a) - - expectEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _fixLifetime(nsa) - _fixLifetime(a) - _fixLifetime(bridgedBack) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.NSArrayIsCopied") { - let nsa = NSArray(array: getAsNSArray([ 10, 20, 30 ])) - let a: Array = convertNSArrayToArray(nsa) - let bridgedBack: NSArray = convertArrayToNSArray(a) - - expectNotEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _fixLifetime(nsa) - _fixLifetime(a) - _fixLifetime(bridgedBack) -} - - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.ImmutableArrayIsRetained") { - let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) - - CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 - CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 - CustomImmutableNSArray.timesCountWasCalled = 0 - let a: Array = convertNSArrayToArray(nsa) - expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) - expectEqual(0, CustomImmutableNSArray.timesObjectAtIndexWasCalled) - expectEqual(0, CustomImmutableNSArray.timesCountWasCalled) - - let bridgedBack: NSArray = convertArrayToNSArray(a) - - expectEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _fixLifetime(nsa) - _fixLifetime(a) - _fixLifetime(bridgedBack) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ImmutableArrayIsCopied") - .skip(.iOSAny("")) - .skip(.tvOSAny("")) - .code { - let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) - - CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 - CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 - CustomImmutableNSArray.timesCountWasCalled = 0 - TestBridgedValueTy.bridgeOperations = 0 - var a: Array = [] - - // FIXME: bridging shouldn't dump array contents into the autorelease pool. - autoreleasepoolIfUnoptimizedReturnAutoreleased { - a = convertNSArrayToArray(nsa) - expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) - expectEqual(3, CustomImmutableNSArray.timesObjectAtIndexWasCalled) - expectNotEqual(0, CustomImmutableNSArray.timesCountWasCalled) - expectEqual(3, TestBridgedValueTy.bridgeOperations) - } - - let bridgedBack: NSArray = convertArrayToNSArray(a) - - expectNotEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _fixLifetime(nsa) - _fixLifetime(a) - _fixLifetime(bridgedBack) -} - -// FIXME: test API calls on the BridgedFromObjC arrays. - -//===----------------------------------------------------------------------===// -// Array -> NSArray bridging tests -// -// Element is bridged verbatim. -// -// FIXME: incomplete. -//===----------------------------------------------------------------------===// - -ArrayTestSuite.test("BridgedToObjC.Verbatim.BridgeUsingAs") { - let source = [ 10, 20, 30 ].map { TestObjCValueTy($0) } - let result = source as NSArray - expectTrue(isNativeNSArray(result)) - expectEqual(3, result.count) - autoreleasepoolIfUnoptimizedReturnAutoreleased { - expectEqual(10, (result[0] as! TestObjCValueTy).value) - expectEqual(20, (result[1] as! TestObjCValueTy).value) - expectEqual(30, (result[2] as! TestObjCValueTy).value) - } -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/count/empty") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) - expectEqual(0, a.count) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/count") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged() - expectEqual(3, a.count) -} - -for index in [ -100, -1, 0, 1, 100 ] { - ArrayTestSuite.test( - "BridgedToObjC/Verbatim/objectAtIndex/empty/trap/\(index)") - .crashOutputMatches("Array index out of range") - .code { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) - expectCrashLater() - a.object(at: index) - } -} - -for index in [ -100, -1, 3, 4, 100 ] { - ArrayTestSuite.test("BridgedToObjC/Verbatim/objectAtIndex/trap/\(index)") - .crashOutputMatches("Array index out of range") - .code { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - expectCrashLater() - a.object(at: index) - } -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/objectAtIndex") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - - var v: AnyObject = a.object(at: 0) as AnyObject - expectEqual(10, (v as! TestObjCValueTy).value) - let idValue0 = unsafeBitCast(v, to: UInt.self) - - v = a.object(at: 1) as AnyObject - expectEqual(20, (v as! TestObjCValueTy).value) - let idValue1 = unsafeBitCast(v, to: UInt.self) - - v = a.object(at: 2) as AnyObject - expectEqual(30, (v as! TestObjCValueTy).value) - let idValue2 = unsafeBitCast(v, to: UInt.self) - - expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) - expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) - expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) -} - -for indexRange in [ - -2..<(-2), 1..<1, - 0..<4, -2..<(-1), -1..<2, 0..<1, 2..<4, 4..<5 -] as [Range] { - ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects/empty/trap/\(indexRange)") - .crashOutputMatches("Array index out of range") - .code { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged( - numElements: 0, capacity: 16) - let buffer = UnsafeMutablePointer.allocate(capacity: 16) - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<0)) - expectCrashLater() - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) - } -} - -for indexRange in [ 0..<4, -2..<(-1), -1..<2, 2..<4, 4..<5 ] as [Range] { - ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects/trap/\(indexRange)") - .crashOutputMatches("Array index out of range") - .code { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged( - numElements: 3, capacity: 16) - let buffer = UnsafeMutablePointer.allocate(capacity: 16) - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) - expectCrashLater() - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) - } -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - let buffer = UnsafeMutablePointer.allocate(capacity: 16) - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) - - var v: AnyObject = buffer[0] - expectEqual(10, (v as! TestObjCValueTy).value) - let idValue0 = unsafeBitCast(v, to: UInt.self) - - v = buffer[1] - expectEqual(20, (v as! TestObjCValueTy).value) - let idValue1 = unsafeBitCast(v, to: UInt.self) - - v = buffer[2] - expectEqual(30, (v as! TestObjCValueTy).value) - let idValue2 = unsafeBitCast(v, to: UInt.self) - - expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) - expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) - expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) - - buffer.deallocate() - _fixLifetime(a) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/copyWithZone") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - let copy: AnyObject = a.copy(with: nil) as AnyObject - expectEqual( - unsafeBitCast(a, to: UInt.self), unsafeBitCast(copy, to: UInt.self)) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/Empty") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) - - checkArrayFastEnumerationFromSwift( - [], a, { a }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/3") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - - checkArrayFastEnumerationFromSwift( - [ 10, 20, 30 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/7") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 7) - - checkArrayFastEnumerationFromSwift( - [ 10, 20, 30, 40, 50, 60, 70 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromObjC/Empty") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) - - checkArrayFastEnumerationFromObjC( - [], a, { a }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromObjC") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - - checkArrayFastEnumerationFromObjC( - [ 10, 20, 30 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift/Empty") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) - - checkArrayFastEnumerationFromSwift( - [], a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - - checkArrayFastEnumerationFromSwift( - [ 10, 20, 30 ], - a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift/Partial") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 9) - - checkArrayEnumeratorPartialFastEnumerationFromSwift( - [ 10, 20, 30, 40, 50, 60, 70, 80, 90 ], - a, maxFastEnumerationItems: 5, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromObjC") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - - checkArrayFastEnumerationFromObjC( - [ 10, 20, 30 ], - a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Reallocate") { - let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) - - var v: AnyObject = a[0] as AnyObject - expectEqual(10, (v as! TestObjCValueTy).value) - let idValue0 = unsafeBitCast(v, to: UInt.self) - - v = a[1] as AnyObject - expectEqual(20, (v as! TestObjCValueTy).value) - let idValue1 = unsafeBitCast(v, to: UInt.self) - - v = a[2] as AnyObject - expectEqual(30, (v as! TestObjCValueTy).value) - let idValue2 = unsafeBitCast(v, to: UInt.self) - - // Bridge back to native array. - var native: [TestObjCValueTy] = convertNSArrayToArray(a) - native[0] = TestObjCValueTy(110) - native[1] = TestObjCValueTy(120) - native[2] = TestObjCValueTy(130) - native.append(TestObjCValueTy(140)) - - // Ensure that the compiler does not elide mutation of the native array. - _blackHole(native) - - // Check that mutating the native array did not affect the bridged array. - expectEqual(3, a.count) - expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) - expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) - expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) -} - -ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Adopt") { - // Bridge back to native array. - var native: [TestObjCValueTy] = - getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) as! Array - - let identity1 = unsafeBitCast(native, to: UInt.self) - - // Mutate elements, but don't change count. - native[0] = TestObjCValueTy(110) - native[1] = TestObjCValueTy(120) - native[2] = TestObjCValueTy(130) - - // Expect no reallocations. - expectEqual(identity1, unsafeBitCast(native, to: UInt.self)) -} - -//===----------------------------------------------------------------------===// -// Array -> NSArray bridging tests -// -// Element is bridged non-verbatim. -// -// FIXME: incomplete. -//===----------------------------------------------------------------------===// - -ArrayTestSuite.test("BridgedToObjC.Nonverbatim.BridgeUsingAs") { - let source = [ 10, 20, 30 ].map { TestBridgedValueTy($0) } - var result = source as NSArray - expectTrue(isNativeNSArray(result)) - expectEqual(3, result.count) - autoreleasepoolIfUnoptimizedReturnAutoreleased { - expectEqual(10, (result[0] as! TestBridgedValueTy).value) - expectEqual(20, (result[1] as! TestBridgedValueTy).value) - expectEqual(30, (result[2] as! TestBridgedValueTy).value) - } -} - -ArrayTestSuite.test("BridgedToObjC/Custom/count/empty") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) - expectEqual(0, a.count) - - expectEqual(0, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/count") { - let a = getBridgedNSArrayOfValueTypeCustomBridged() - expectEqual(3, a.count) - - expectEqual(0, TestBridgedValueTy.bridgeOperations) -} - -for index in [ -100, -1, 0, 1, 100 ] { - ArrayTestSuite.test( - "BridgedToObjC/Custom/objectAtIndex/empty/trap/\(index)") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) - expectCrashLater() - a.object(at: index) - } -} - -for index in [ -100, -1, 3, 4, 100 ] { - ArrayTestSuite.test("BridgedToObjC/Custom/objectAtIndex/trap/\(index)") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - expectCrashLater() - a.object(at: index) - } -} - -ArrayTestSuite.test("BridgedToObjC/Custom/objectAtIndex") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - - var v: AnyObject = a.object(at: 0) as AnyObject - expectEqual(10, (v as! TestObjCValueTy).value) - let idValue0 = unsafeBitCast(v, to: UInt.self) - - v = a.object(at: 1) as AnyObject - expectEqual(20, (v as! TestObjCValueTy).value) - let idValue1 = unsafeBitCast(v, to: UInt.self) - - v = a.object(at: 2) as AnyObject - expectEqual(30, (v as! TestObjCValueTy).value) - let idValue2 = unsafeBitCast(v, to: UInt.self) - - expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) - expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) - expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) - - expectEqual(3, TestBridgedValueTy.bridgeOperations) -} - -for indexRange in [ - -2..<(-2), 1..<1, - 0..<4, -2..<(-1), -1..<2, 0..<1, 2..<4, 4..<5 -] as [Range] { - ArrayTestSuite.test("BridgedToObjC/Custom/getObjects/empty/trap/\(indexRange)") - .crashOutputMatches("Array index out of range") - .code { - let a = getBridgedNSArrayOfValueTypeCustomBridged( - numElements: 0, capacity: 16) - let buffer = UnsafeMutablePointer.allocate(capacity: 16) - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<0)) - expectCrashLater() - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) - } -} - -for indexRange in [ 0..<4, -2..<(-1), -1..<2, 2..<4, 4..<5 ] as [Range] { - ArrayTestSuite.test("BridgedToObjC/Custom/getObjects/trap/\(indexRange)") - .crashOutputMatches("Array index out of range") - .code { - let a = getBridgedNSArrayOfValueTypeCustomBridged( - numElements: 3, capacity: 16) - let buffer = UnsafeMutablePointer.allocate(capacity: 16) - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) - expectCrashLater() - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) - } -} - -ArrayTestSuite.test("BridgedToObjC/Custom/getObjects") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - let buffer = UnsafeMutablePointer.allocate(capacity: 16) - a.available_getObjects( - AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) - - var v: AnyObject = buffer[0] - expectEqual(10, (v as! TestObjCValueTy).value) - let idValue0 = unsafeBitCast(v, to: UInt.self) - - v = buffer[1] - expectEqual(20, (v as! TestObjCValueTy).value) - let idValue1 = unsafeBitCast(v, to: UInt.self) - - v = buffer[2] - expectEqual(30, (v as! TestObjCValueTy).value) - let idValue2 = unsafeBitCast(v, to: UInt.self) - - expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) - expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) - expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) - - buffer.deallocate() - _fixLifetime(a) - - expectEqual(3, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/copyWithZone") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - let copy: AnyObject = a.copy(with: nil) as AnyObject - expectEqual( - unsafeBitCast(a, to: UInt.self), - unsafeBitCast(copy, to: UInt.self)) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/Empty") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) - - checkArrayFastEnumerationFromSwift( - [], a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(0, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/3") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - - checkArrayFastEnumerationFromSwift( - [ 10, 20, 30 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(3, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/7") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 7) - - checkArrayFastEnumerationFromSwift( - [ 10, 20, 30, 40, 50, 60, 70 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(7, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromObjC/Empty") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) - - checkArrayFastEnumerationFromObjC( - [], a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(0, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromObjC") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - - checkArrayFastEnumerationFromObjC( - [ 10, 20, 30 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(3, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift/Empty") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) - - checkArrayFastEnumerationFromSwift( - [], a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(0, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - - checkArrayFastEnumerationFromSwift( - [ 10, 20, 30 ], - a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(3, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift/Partial") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 9) - - checkArrayEnumeratorPartialFastEnumerationFromSwift( - [ 10, 20, 30, 40, 50, 60, 70, 80, 90 ], - a, maxFastEnumerationItems: 5, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(9, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromObjC") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - - checkArrayFastEnumerationFromObjC( - [ 10, 20, 30 ], - a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) - - expectEqual(3, TestBridgedValueTy.bridgeOperations) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Cast") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - - var v: AnyObject = a[0] as AnyObject - expectEqual(10, (v as! TestObjCValueTy).value) - let idValue0 = unsafeBitCast(v, to: UInt.self) - - v = a[1] as AnyObject - expectEqual(20, (v as! TestObjCValueTy).value) - let idValue1 = unsafeBitCast(v, to: UInt.self) - - v = a[2] as AnyObject - expectEqual(30, (v as! TestObjCValueTy).value) - let idValue2 = unsafeBitCast(v, to: UInt.self) - - // Bridge back to native array with a cast. - var native: [TestObjCValueTy] = convertNSArrayToArray(a) - native[0] = TestObjCValueTy(110) - native[1] = TestObjCValueTy(120) - native[2] = TestObjCValueTy(130) - native.append(TestObjCValueTy(140)) - - // Ensure that the compiler does not elide mutation of the native array. - _blackHole(native) - - // Check that mutating the native array did not affect the bridged array. - expectEqual(3, a.count) - expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) - expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) - expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Reallocate") { - let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) - - var v: AnyObject = a[0] as AnyObject - expectEqual(10, (v as! TestObjCValueTy).value) - let idValue0 = unsafeBitCast(v, to: UInt.self) - - v = a[1] as AnyObject - expectEqual(20, (v as! TestObjCValueTy).value) - let idValue1 = unsafeBitCast(v, to: UInt.self) - - v = a[2] as AnyObject - expectEqual(30, (v as! TestObjCValueTy).value) - let idValue2 = unsafeBitCast(v, to: UInt.self) - - // Bridge back to native array. - var native: [TestBridgedValueTy] = convertNSArrayToArray(a) - native[0] = TestBridgedValueTy(110) - native[1] = TestBridgedValueTy(120) - native[2] = TestBridgedValueTy(130) - native.append(TestBridgedValueTy(140)) - - // Ensure that the compiler does not elide mutation of the native array. - _blackHole(native) - - // Check that mutating the native array did not affect the bridged array. - expectEqual(3, a.count) - expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) - expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) - expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) -} - -ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Adopt") { - // Bridge back to native array. - var native: [TestBridgedValueTy] = convertNSArrayToArray( - getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3)) - let identity1 = unsafeBitCast(native, to: UInt.self) - - // Mutate elements, but don't change count. - native[0] = TestBridgedValueTy(110) - native[1] = TestBridgedValueTy(120) - native[2] = TestBridgedValueTy(130) - - // Expect no reallocations. - expectEqual(identity1, unsafeBitCast(native, to: UInt.self)) -} - -//===----------------------------------------------------------------------===// -// NSArray -> Array -> NSArray bridging tests. -//===----------------------------------------------------------------------===// - -ArrayTestSuite.test("BridgedToObjC.Verbatim.RoundtripThroughSwiftArray") { -% for (MiddleType, AsCast) in [ -% ('Array', 'as'), -% ('Array', 'as!'), -% ]: - do { - let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) - let a: ${MiddleType} = convertNSArrayToArray(nsa) - let bridgedBack = convertArrayToNSArray(a) - - expectEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _fixLifetime(nsa) - _fixLifetime(a) - _fixLifetime(bridgedBack) - } - do { - let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) - let a = nsa ${AsCast} ${MiddleType} - let bridgedBack: NSArray = a as NSArray - - expectEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _fixLifetime(nsa) - _fixLifetime(a) - _fixLifetime(bridgedBack) - } -% end -} - -ArrayTestSuite.test("BridgedToObjC.Nonverbatim.RoundtripThroughSwiftArray") { - do { - TestBridgedValueTy.bridgeOperations = 0 - let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) - let a: Array = convertNSArrayToArray(nsa) - let _ = convertArrayToNSArray(a) - expectEqual(3, TestBridgedValueTy.bridgeOperations) - } - do { - TestBridgedValueTy.bridgeOperations = 0 - let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) - let a = nsa as! Array - let _: NSArray = a as NSArray - expectEqual(3, TestBridgedValueTy.bridgeOperations) - } -} - -ArrayTestSuite.test("append(contentsOf: NSArray)") { - // A stray runtime `is` test caused this particular operation to fail in 5.3. - // rdar://70448247 - let nsarray: NSArray = [2, 3, 4] - var array: [Any] = [1] - array.append(contentsOf: nsarray) - expectEqual(array as? [Int], [1, 2, 3, 4]) -} - -ArrayTestSuite.setUp { - resetLeaksOfDictionaryKeysValues() - resetLeaksOfObjCDictionaryKeysValues() - TestBridgedValueTy.bridgeOperations = 0 -} - -ArrayTestSuite.tearDown { - if _isDebugAssertConfiguration() { - // The return autorelease optimization does not happen reliable. - expectNoLeaksOfDictionaryKeysValues() - expectNoLeaksOfObjCDictionaryKeysValues() - } -} - -#endif // _runtime(_ObjC) - -runAllTests() diff --git a/validation-test/stdlib/Arrays.swift.gyb b/validation-test/stdlib/Arrays.swift.gyb index 3db4ed16eeef8..30b9fd12dfedc 100644 --- a/validation-test/stdlib/Arrays.swift.gyb +++ b/validation-test/stdlib/Arrays.swift.gyb @@ -2,20 +2,16 @@ // // RUN: %gyb %s -o %t/main.swift // RUN: %target-clang -fobjc-arc %S/Inputs/SlurpFastEnumeration/SlurpFastEnumeration.m -c -o %t/SlurpFastEnumeration.o -// RUN: %line-directive %t/main.swift -- %target-build-swift %t/main.swift %S/Inputs/ArrayTypesAndHelpers.swift -I %S/Inputs/SlurpFastEnumeration/ -Xlinker %t/SlurpFastEnumeration.o -o %t/Arrays -Xfrontend -disable-access-control +// RUN: %line-directive %t/main.swift -- %target-build-swift %t/main.swift %S/Inputs/DictionaryKeyValueTypes.swift -I %S/Inputs/SlurpFastEnumeration/ -Xlinker %t/SlurpFastEnumeration.o -o %t/Arrays -Xfrontend -disable-access-control // -// RUN: %target-codesign %t/Arrays && %line-directive %t/main.swift -- %target-run %t/Arrays +// RUN: %target-codesign %t/Arrays +// RUN: %enable-cow-checking %line-directive %t/main.swift -- %target-run %t/Arrays // REQUIRES: executable_test import Swift import StdlibUnittest import StdlibCollectionUnittest -#if _runtime(_ObjC) -import Foundation -import StdlibUnittestFoundationExtras -#endif - let CopyToNativeArrayBufferTests = TestSuite("CopyToNativeArrayBufferTests") extension Array { @@ -24,6 +20,33 @@ extension Array { } } +func getCOWFastArray() -> Array { + var a = Array() + a.reserveCapacity(10) + a.append(1) + a.append(2) + a.append(3) + return a +} + +func getCOWSlowArray() -> Array> { + var a = Array>() + a.reserveCapacity(10) + a.append(COWBox(1)) + a.append(COWBox(2)) + a.append(COWBox(3)) + return a +} + +func getDerivedAPIsArray() -> Array { + var a = Array() + a.append(1010) + a.append(1020) + a.append(1030) + return a +} + + CopyToNativeArrayBufferTests.test("Sequence._copyToContiguousArray()") { do { // Call from a static context. @@ -89,12 +112,184 @@ CopyToNativeArrayBufferTests.test("Collection._copyToContiguousArray()") { } } +func withInoutInt(_ x: inout Int, body: (_ x: inout Int) -> Void) { + body(&x) +} + +func withInoutT(_ x: inout T, body: (_ x: inout T) -> Void) { + body(&x) +} + %{ all_array_types = ['ContiguousArray', 'ArraySlice', 'Array'] }% var ArrayTestSuite = TestSuite("Array") +% for array_type in all_array_types: + +extension ${array_type} { + typealias _BufferID = UnsafeRawPointer? + var _bufferID: _BufferID { + return unsafeBitCast(_owner, to: _BufferID.self) + } +} + +ArrayTestSuite.test("${array_type}/subscript(_: Int)/COW") + .code { + var a: ${array_type}<${array_type}> = [[ + TestValueTy(10), TestValueTy(20), TestValueTy(30), + TestValueTy(40), TestValueTy(50), TestValueTy(60), + TestValueTy(70) + ]] + let identityOuter = a._bufferID + var identityInner = a[0]._bufferID + + func checkIdentity(_ stackTrace: SourceLocStack) { + // Does not reallocate storage because we changed a property based on a + // reference; array storage was not changed. Writeback of the inner array + // does not happen. + expectEqual(identityOuter, a._bufferID, stackTrace: stackTrace) + expectEqual(identityInner, a[0]._bufferID, stackTrace: stackTrace) + } + + // Mutating through a subscript expression. + a[0][0] = TestValueTy(1010) + + checkIdentity(SourceLocStack().withCurrentLoc()) + + a[0][1].value = 1020 + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a) { + (x: inout ${array_type}<${array_type}>) in + x[0][2].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a[0]) { + (x: inout ${array_type}) in + x[3].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + // This will reallocate storage unless Array uses addressors for subscript. + //withInoutT(&a[0][4]) { + // (x: inout TestValueTy) in + // x.value += 1000 + //} + + // FIXME: both of these lines crash the compiler. + // Passing an expression based on addressors as + // 'inout' crashes SILGen + //withInoutT(&a[0][5].value, { $0 += 1000 }) + //withInoutInt(&a[0][6].value, { $0 += 1000 }) + + // Don't change the last element. + + expectEqual(1010, a[0][0].value) + expectEqual(1020, a[0][1].value) + expectEqual(1030, a[0][2].value) + expectEqual(1040, a[0][3].value) + expectEqual(50, a[0][4].value) + expectEqual(60, a[0][5].value) + expectEqual(70, a[0][6].value) +} + +ArrayTestSuite.test("${array_type}/subscript(_: Range)/COW") + .code { + var a: ${array_type}<${array_type}> = [[ + TestValueTy(10), TestValueTy(20), TestValueTy(30), + TestValueTy(40), TestValueTy(50), TestValueTy(60), + TestValueTy(70), TestValueTy(80), TestValueTy(90), + ]] + let identityOuter = a._bufferID + var identityInner = a[0]._bufferID + + func checkIdentity(_ stackTrace: SourceLocStack) { + // Does not reallocate storage because we changed a property based on a + // reference; array storage was not changed. + expectEqual(identityOuter, a._bufferID, stackTrace: stackTrace) + expectEqual(identityInner, a[0]._bufferID, stackTrace: stackTrace) + } + + // Mutating through a subscript expression. + a[0..<1][0][0] = TestValueTy(1010) + + // Reallocates storage because of the writeback in Array.subscript(Int). + expectEqual(identityOuter, a._bufferID) + expectNotEqual(identityInner, a[0]._bufferID) + identityInner = a[0]._bufferID + + a[0..<1][0][1].value = 1020 + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a) { + (x: inout ${array_type}<${array_type}>) in + x[0..<1][0][2].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a[0..<1]) { + (x: inout ArraySlice<${array_type}>) in + x[0][3].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a[0..<1][0]) { + (x: inout ${array_type}) in + x[4].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a[0..<1][0][5]) { + (x: inout TestValueTy) in + x.value += 1000 + } + + // Reallocates storage because of the writeback in Array.subscript(Int) + // (writeback is being requested for the array element even though it is not + // needed). + expectEqual(identityOuter, a._bufferID) + expectNotEqual(identityInner, a[0]._bufferID) + identityInner = a[0]._bufferID + + withInoutT(&a[0..<1][0][6].value) { + (x: inout Int) in + x += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutInt(&a[0..<1][0][7].value) { + (x: inout Int) in + x += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + // Don't change the last element. + + expectEqual(1010, a[0][0].value) + expectEqual(1020, a[0][1].value) + expectEqual(1030, a[0][2].value) + expectEqual(1040, a[0][3].value) + expectEqual(1050, a[0][4].value) + expectEqual(1060, a[0][5].value) + expectEqual(1070, a[0][6].value) + expectEqual(1080, a[0][7].value) + expectEqual(90, a[0][8].value) +} + +% end + ArrayTestSuite.test("sizeof") { var a = [ 10, 20, 30 ] #if arch(i386) || arch(arm) || arch(arm64_32) @@ -240,1257 +435,6 @@ ArrayTestSuite.test("Hashable") { } -#if _runtime(_ObjC) -//===----------------------------------------------------------------------===// -// NSArray -> Array bridging tests. -//===----------------------------------------------------------------------===// - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.ArrayIsCopied") { - var (a, nsa) = getBridgedVerbatimArrayAndNSMutableArray() - expectTrue(isCocoaArray(a)) - - // Find an existing value. - do { - let v = a[0] as! TestObjCValueTy - expectEqual(1010, v.value) - } - - // Remove the value from the NSMutableArray. - nsa.removeObject(at: 0) - - // Find an existing value, again. - do { - let v = a[0] as! TestObjCValueTy - expectEqual(1010, v.value) - } -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ArrayIsCopied") { - var (a, nsa) = getBridgedNonverbatimArrayAndNSMutableArray() - expectTrue(isNativeArray(a)) - - // Find an existing value. - do { - let v = a[0] - expectEqual(1010, v.value) - } - - // Remove the value from the NSMutableArray. - nsa.removeObject(at: 0) - - // Find an existing value, again. - do { - let v = a[0] - expectEqual(1010, v.value) - } -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.NSArrayIsRetained") { - let nsa: NSArray = autoreleasepool { - NSArray(array: getAsNSArray([1010, 1020, 1030])) - } - - let a: [AnyObject] = convertNSArrayToArray(nsa) - - let bridgedBack: NSArray = convertArrayToNSArray(a) - - expectEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _blackHole(nsa) - _blackHole(a) - _blackHole(bridgedBack) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.NSArrayIsCopied") { - let nsa: NSArray = autoreleasepool { - NSArray(array: getAsNSArray([1010, 1020, 1030])) - } - - let a: [TestBridgedValueTy] = convertNSArrayToArray(nsa) - - let bridgedBack: NSArray = convertArrayToNSArray(a) - - expectNotEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _blackHole(nsa) - _blackHole(a) - _blackHole(bridgedBack) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.ImmutableArrayIsRetained") { - let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) - - CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 - CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 - CustomImmutableNSArray.timesObjectEnumeratorWasCalled = 0 - CustomImmutableNSArray.timesCountWasCalled = 0 - let a: [AnyObject] = convertNSArrayToArray(nsa) - expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) - expectEqual(0, CustomImmutableNSArray.timesObjectAtIndexWasCalled) - expectEqual(0, CustomImmutableNSArray.timesObjectEnumeratorWasCalled) - expectEqual(0, CustomImmutableNSArray.timesCountWasCalled) - - let bridgedBack: NSArray = convertArrayToNSArray(a) - expectEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _blackHole(nsa) - _blackHole(a) - _blackHole(bridgedBack) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ImmutableArrayIsCopied") { - let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) - - CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 - CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 - CustomImmutableNSArray.timesObjectEnumeratorWasCalled = 0 - CustomImmutableNSArray.timesCountWasCalled = 0 - TestBridgedValueTy.bridgeOperations = 0 - let a: [TestBridgedValueTy] = convertNSArrayToArray(nsa) - //FIXME: Why is this copied? - expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) - expectEqual(3, CustomImmutableNSArray.timesObjectAtIndexWasCalled) - expectNotEqual(0, CustomImmutableNSArray.timesCountWasCalled) - expectEqual(3, TestBridgedValueTy.bridgeOperations) - - let bridgedBack: NSArray = convertArrayToNSArray(a) - expectNotEqual( - unsafeBitCast(nsa, to: Int.self), - unsafeBitCast(bridgedBack, to: Int.self)) - - _blackHole(nsa) - _blackHole(a) - _blackHole(bridgedBack) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.Subscript") { - let a = getBridgedVerbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isCocoaArray(a)) - - // Find an existing value. - do { - var v = a[0] - expectEqual(1010, (v as! TestObjCValueTy).value) - - v = a[1] - expectEqual(1020, (v as! TestObjCValueTy).value) - - v = a[2] - expectEqual(1030, (v as! TestObjCValueTy).value) - } - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Subscript") { - var a = getBridgedNonverbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isNativeArray(a)) - - // Find an existing value. - do { - var v = a[0] - expectEqual(1010, v.value) - - v = a[1] - expectEqual(1020, v.value) - - v = a[2] - expectEqual(1030, v.value) - } - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.RemoveAt") { - var a = getBridgedVerbatimArray() - let identity = a._rawIdentifier() - expectTrue(isCocoaArray(a)) - - let index = 0 - expectEqual(1010, (a[index] as! TestObjCValueTy).value) - expectEqual(identity, a._rawIdentifier()) - - let removedElement = a.remove(at: index) - expectNotEqual(identity, a._rawIdentifier()) - expectTrue(isNativeArray(a)) - expectEqual(1010, (removedElement as! TestObjCValueTy).value) - expectEqual(2, a.count) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.RemoveAt") { - var a = getBridgedNonverbatimArray() - let identity = a._rawIdentifier() - expectTrue(isNativeArray(a)) - - let index = 0 - expectEqual(1010, a[index].value) - expectEqual(identity, a._rawIdentifier()) - - let removedElement = a.remove(at: index) - expectEqual(identity, a._rawIdentifier()) - expectTrue(isNativeArray(a)) - expectEqual(1010, removedElement.value) - expectEqual(2, a.count) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.RemoveAll") { - do { - var a = getBridgedVerbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isCocoaArray(a)) - let originalCapacity = a.count - expectEqual(3, a.count) - expectEqual(1010, (a[0] as! TestObjCValueTy).value) - - a.removeAll() - expectNotEqual(identity1, a._rawIdentifier()) - expectLT(a._buffer.capacity, originalCapacity) - expectEqual(0, a.count) - } - - do { - var a = getBridgedVerbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isCocoaArray(a)) - let originalCapacity = a.count - expectEqual(3, a.count) - expectEqual(1010, (a[0] as! TestObjCValueTy).value) - - a.removeAll(keepingCapacity: true) - expectNotEqual(identity1, a._rawIdentifier()) - expectGE(a._buffer.capacity, originalCapacity) - expectEqual(0, a.count) - } - - do { - var a1 = getBridgedVerbatimArray() - let identity1 = a1._rawIdentifier() - expectTrue(isCocoaArray(a1)) - let originalCapacity = a1.count - expectEqual(3, a1.count) - expectEqual(1010, (a1[0] as! TestObjCValueTy).value) - - var a2 = a1 - a2.removeAll() - let identity2 = a2._rawIdentifier() - expectEqual(identity1, a1._rawIdentifier()) - expectNotEqual(identity2, identity1) - expectEqual(3, a1.count) - expectEqual(1010, (a1[0] as! TestObjCValueTy).value) - expectLT(a2._buffer.capacity, originalCapacity) - expectEqual(0, a2.count) - } - - do { - var a1 = getBridgedVerbatimArray() - let identity1 = a1._rawIdentifier() - expectTrue(isCocoaArray(a1)) - let originalCapacity = a1.count - expectEqual(3, a1.count) - expectEqual(1010, (a1[0] as! TestObjCValueTy).value) - - var a2 = a1 - a2.removeAll(keepingCapacity: true) - let identity2 = a2._rawIdentifier() - expectEqual(identity1, a1._rawIdentifier()) - expectNotEqual(identity2, identity1) - expectEqual(3, a1.count) - expectEqual(1010, (a1[0] as! TestObjCValueTy).value) - expectGE(a2._buffer.capacity, originalCapacity) - expectEqual(0, a2.count) - } -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.RemoveAll") { - do { - var a = getBridgedNonverbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isNativeArray(a)) - let originalCapacity = a.count - expectEqual(3, a.count) - expectEqual(1010, a[0].value) - - a.removeAll() - expectNotEqual(identity1, a._rawIdentifier()) - expectLT(a._buffer.capacity, originalCapacity) - expectEqual(0, a.count) - } - - do { - var a = getBridgedNonverbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isNativeArray(a)) - let originalCapacity = a.count - expectEqual(3, a.count) - expectEqual(1010, a[0].value) - - a.removeAll(keepingCapacity: true) - expectEqual(identity1, a._rawIdentifier()) - expectGE(a._buffer.capacity, originalCapacity) - expectEqual(0, a.count) - } - - do { - var a1 = getBridgedNonverbatimArray() - let identity1 = a1._rawIdentifier() - expectTrue(isNativeArray(a1)) - let originalCapacity = a1.count - expectEqual(3, a1.count) - expectEqual(1010, a1[0].value) - - var a2 = a1 - a2.removeAll() - let identity2 = a2._rawIdentifier() - expectEqual(identity1, a1._rawIdentifier()) - expectNotEqual(identity2, identity1) - expectEqual(3, a1.count) - expectEqual(1010, a1[0].value) - expectLT(a2._buffer.capacity, originalCapacity) - expectEqual(0, a2.count) - } - - do { - var a1 = getBridgedNonverbatimArray() - let identity1 = a1._rawIdentifier() - expectTrue(isNativeArray(a1)) - let originalCapacity = a1.count - expectEqual(3, a1.count) - expectEqual(1010, a1[0].value) - - var a2 = a1 - a2.removeAll(keepingCapacity: true) - let identity2 = a2._rawIdentifier() - expectEqual(identity1, a1._rawIdentifier()) - expectNotEqual(identity2, identity1) - expectEqual(3, a1.count) - expectEqual(1010, a1[0].value) - expectGE(a2._buffer.capacity, originalCapacity) - expectEqual(0, a2.count) - } -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.Count") { - let a = getBridgedVerbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isCocoaArray(a)) - - expectEqual(3, a.count) - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Count") { - let a = getBridgedNonverbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isNativeArray(a)) - - expectEqual(3, a.count) - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate") { - let a = getBridgedVerbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isCocoaArray(a)) - - var iter = a.makeIterator() - var values = Array() - while let value = iter.next() { - values.append((value as! TestObjCValueTy).value) - } - expectEqual(values, [1010, 1020, 1030]) - expectNil(iter.next()) - expectNil(iter.next()) - expectNil(iter.next()) - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate") { - let a = getBridgedNonverbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isNativeArray(a)) - - var iter = a.makeIterator() - var values = Array() - while let value = iter.next() { - values.append(value.value) - } - expectEqual(values, [1010, 1020, 1030]) - expectNil(iter.next()) - expectNil(iter.next()) - expectNil(iter.next()) - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate_Empty") { - let a = getBridgedVerbatimArray([]) - let identity1 = a._rawIdentifier() - expectTrue(isCocoaArray(a)) - - var iter = a.makeIterator() - expectNil(iter.next()) - expectNil(iter.next()) - expectNil(iter.next()) - expectNil(iter.next()) - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate_Empty") { - let a = getBridgedNonverbatimArray([]) - let identity1 = a._rawIdentifier() - expectTrue(isNativeArray(a)) - - var iter = a.makeIterator() - expectNil(iter.next()) - expectNil(iter.next()) - expectNil(iter.next()) - expectNil(iter.next()) - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate_Huge") { - let a = getHugeBridgedVerbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isCocoaArray(a)) - - var iter = a.makeIterator() - var values = Array() - while let value = iter.next() { - values.append((value as! TestObjCValueTy).value) - } - var expectedValues = Array() - for i in 1...32 { - expectedValues += [1000 + i] - } - expectEqual(values, expectedValues) - expectNil(iter.next()) - expectNil(iter.next()) - expectNil(iter.next()) - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate_Huge") { - let a = getHugeBridgedNonverbatimArray() - let identity1 = a._rawIdentifier() - expectTrue(isNativeArray(a)) - - var iter = a.makeIterator() - var values = Array() - while let value = iter.next() { - values.append(value.value) - } - var expectedValues = Array() - for i in 1...32 { - expectedValues += [1000 + i] - } - expectEqual(values, expectedValues) - expectNil(iter.next()) - expectNil(iter.next()) - expectNil(iter.next()) - expectEqual(identity1, a._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.EqualityTest_Empty") { - let a1 = getBridgedVerbatimEquatableArray([]) - let identity1 = a1._rawIdentifier() - expectTrue(isCocoaArray(a1)) - - let a2 = getBridgedVerbatimEquatableArray([]) - let identity2 = a2._rawIdentifier() - expectTrue(isCocoaArray(a2)) - - // We can't check that `identity1 != identity2` because Foundation might be - // returning the same singleton NSArray for empty arrays. - - expectEqual(a1, a2) - expectEqual(identity1, a1._rawIdentifier()) - expectEqual(identity2, a2._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.EqualityTest_Empty") { - var a1 = getBridgedNonverbatimEquatableArray([]) - a1.append(TestBridgedEquatableValueTy(1)) - a1.removeLast() - let identity1 = a1._rawIdentifier() - expectTrue(isNativeArray(a1)) - - let a2 = getBridgedNonverbatimEquatableArray([]) - let identity2 = a2._rawIdentifier() - expectTrue(isNativeArray(a2)) - expectNotEqual(identity1, identity2) - - expectEqual(a1, a2) - expectEqual(identity1, a1._rawIdentifier()) - expectEqual(identity2, a2._rawIdentifier()) -} - -ArrayTestSuite.test("BridgedFromObjC.Verbatim.EqualityTest_Small") { - func helper(_ na1: Array, _ na2: Array, _ expectedEq: Bool) { - let a1 = getBridgedVerbatimEquatableArray(na1) - let identity1 = a1._rawIdentifier() - expectTrue(isCocoaArray(a1)) - - var a2 = getBridgedVerbatimEquatableArray(na2) - var identity2 = a2._rawIdentifier() - expectTrue(isCocoaArray(a2)) - - do { - let eq1 = (a1 == a2) - expectEqual(eq1, expectedEq) - - let eq2 = (a2 == a1) - expectEqual(eq2, expectedEq) - - let neq1 = (a1 != a2) - expectNotEqual(neq1, expectedEq) - - let neq2 = (a2 != a1) - expectNotEqual(neq2, expectedEq) - } - expectEqual(identity1, a1._rawIdentifier()) - expectEqual(identity2, a2._rawIdentifier()) - - a2.append(TestObjCEquatableValueTy(1111)) - a2.removeLast() - expectTrue(isNativeArray(a2)) - expectNotEqual(identity2, a2._rawIdentifier()) - identity2 = a2._rawIdentifier() - - do { - let eq1 = (a1 == a2) - expectEqual(eq1, expectedEq) - - let eq2 = (a2 == a1) - expectEqual(eq2, expectedEq) - - let neq1 = (a1 != a2) - expectNotEqual(neq1, expectedEq) - - let neq2 = (a2 != a1) - expectNotEqual(neq2, expectedEq) - } - expectEqual(identity1, a1._rawIdentifier()) - expectEqual(identity2, a2._rawIdentifier()) - } - - helper([], [], true) - - helper([1010], - [1010], - true) - - helper([1010, 1020], - [1010, 1020], - true) - - helper([1010, 1020, 1030], - [1010, 1020, 1030], - true) - - helper([1010, 1020, 1030], - [1010, 1020, 1111], - false) - - helper([1010, 1020, 1030], - [1010, 1020], - false) - - helper([1010, 1020, 1030], - [1010], - false) - - helper([1010, 1020, 1030], - [], - false) - - helper([1010, 1020, 1030], - [1010, 1020, 1030, 1040], - false) -} - -//===--- -// Array -> NSArray bridging tests. -// -// Values are bridged verbatim. -//===--- - -ArrayTestSuite.test("BridgedToObjC.Verbatim.Count") { - let d = getBridgedNSArrayOfRefTypesBridgedVerbatim() - - expectEqual(3, d.count) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectForKey") { - let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() - - var v: AnyObject? = a.object(at: 0) as AnyObject - expectEqual(1010, (v as! TestObjCValueTy).value) - let idValue10 = unsafeBitCast(v, to: UInt.self) - - v = a.object(at: 1) as AnyObject - expectEqual(1020, (v as! TestObjCValueTy).value) - let idValue20 = unsafeBitCast(v, to: UInt.self) - - v = a.object(at: 2) as AnyObject - expectEqual(1030, (v as! TestObjCValueTy).value) - let idValue30 = unsafeBitCast(v, to: UInt.self) - - for _ in 0..<3 { - expectEqual(idValue10, unsafeBitCast( - a.object(at: 0) as AnyObject, to: UInt.self)) - - expectEqual(idValue20, unsafeBitCast( - a.object(at: 1) as AnyObject, to: UInt.self)) - - expectEqual(idValue30, unsafeBitCast( - a.object(at: 2) as AnyObject, to: UInt.self)) - } - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.KeyEnumerator.NextObject") { - let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() - - var capturedIdentities = Array() - - for _ in 0..<3 { - let enumerator = a.objectEnumerator() - - var values = Array() - var identities = Array() - while let value = enumerator.nextObject() { - let valueObj = (value as! TestObjCValueTy) - values.append(valueObj.value) - - let identity = unsafeBitCast(valueObj, to: UInt.self) - identities.append(identity) - } - expectEqual([ 1010, 1020, 1030 ], values) - - if capturedIdentities.isEmpty { - capturedIdentities = identities - } else { - expectEqual(capturedIdentities, identities) - } - - expectNil(enumerator.nextObject()) - expectNil(enumerator.nextObject()) - expectNil(enumerator.nextObject()) - } - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.NextObject_Empty") { - let a = getBridgedEmptyNSArray() - let enumerator = a.objectEnumerator() - - expectNil(enumerator.nextObject()) - expectNil(enumerator.nextObject()) - expectNil(enumerator.nextObject()) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration.UseFromSwift") { - let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() - - checkArrayFastEnumerationFromSwift( - [ 1010, 1020, 1030 ], - a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration.UseFromObjC") { - let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() - - checkArrayFastEnumerationFromObjC( - [ 1010, 1020, 1030 ], - a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration_Empty") { - let a = getBridgedEmptyNSArray() - - checkArrayFastEnumerationFromSwift( - [], a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) - - checkArrayFastEnumerationFromObjC( - [], a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration.UseFromSwift") { - let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() - - checkArrayFastEnumerationFromSwift( - [ 1010, 1020, 1030 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration.UseFromObjC") { - let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() - - checkArrayFastEnumerationFromObjC( - [ 1010, 1020, 1030 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration_Empty") { - let a = getBridgedEmptyNSArray() - - checkArrayFastEnumerationFromSwift( - [], a, { a }, - { ($0 as! TestObjCValueTy).value }) - - checkArrayFastEnumerationFromObjC( - [], a, { a }, - { ($0 as! TestObjCValueTy).value }) -} - -//===--- -// Array -> NSArray bridging tests. -// -// Values are bridged non-verbatim. -//===--- - -ArrayTestSuite.test("BridgedToObjC.KeyValue_ValueTypesCustomBridged") { - let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() - let enumerator = a.objectEnumerator() - - var values = Array() - while let valueObj = enumerator.nextObject() { - let value: AnyObject = valueObj as AnyObject - let v = (value as! TestObjCValueTy).value - values.append(v) - } - expectEqual([ 1010, 1020, 1030 ], values) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromSwift") { - let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() - - checkArrayFastEnumerationFromSwift( - [ 1010, 1020, 1030 ], - a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromSwift.Partial") { - let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( - numElements: 9) - - checkArrayEnumeratorPartialFastEnumerationFromSwift( - [ 1010, 1020, 1030, 1040, 1050, - 1060, 1070, 1080, 1090 ], - a, maxFastEnumerationItems: 5, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 9) -} - -ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromObjC") { - let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() - - checkArrayFastEnumerationFromObjC( - [ 1010, 1020, 1030 ], - a, { a.objectEnumerator() }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration.UseFromSwift") { - let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() - - checkArrayFastEnumerationFromSwift( - [ 1010, 1020, 1030 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration.UseFromObjC") { - let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() - - checkArrayFastEnumerationFromObjC( - [ 1010, 1020, 1030 ], - a, { a }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration_Empty") { - let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( - numElements: 0) - - checkArrayFastEnumerationFromSwift( - [], a, { a }, - { ($0 as! TestObjCValueTy).value }) - - checkArrayFastEnumerationFromObjC( - [], a, { a }, - { ($0 as! TestObjCValueTy).value }) -} - -ArrayTestSuite.test("BridgedToObjC.Key_ValueTypeCustomBridged") { - let a = getBridgedNSArrayOfObj_ValueTypeCustomBridged() - let enumerator = a.objectEnumerator() - - var values = Array() - while let valueObj = enumerator.nextObject() { - let value: AnyObject = valueObj as AnyObject - let v = (value as! TestObjCValueTy).value - values.append(v) - } - expectEqual([ 1010, 1020, 1030 ], values) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("BridgedToObjC.Value_ValueTypeCustomBridged") { - let a = getBridgedNSArrayOfValue_ValueTypeCustomBridged() - let enumerator = a.objectEnumerator() - - var values = Array() - while let valueObj = enumerator.nextObject() { - let value: AnyObject = valueObj as AnyObject - let v = (value as! TestObjCValueTy).value - values.append(v) - } - expectEqual([ 1010, 1020, 1030 ], values) - - expectAutoreleasedValues(unopt: 3) -} - -//===--- -// NSArray -> Array -> NSArray bridging tests. -//===--- - -ArrayTestSuite.test("BridgingRoundtrip") { - let a = getRoundtripBridgedNSArray() - let enumerator = a.objectEnumerator() - - var values = Array() - while let valueObj = enumerator.nextObject() { - let value: AnyObject = valueObj as AnyObject - let v = (value as! TestObjCValueTy).value - values.append(v) - } - expectEqual([ 1010, 1020, 1030 ], values) -} - -//===--- -// NSArray -> Array implicit conversion. -//===--- - -ArrayTestSuite.test("NSArrayToArrayConversion") { - let values = [ 1010, 1020, 1030 ].map { TestObjCValueTy($0) } - - let nsa = NSArray(array: values) - - let a: Array = nsa as Array - - var bridgedValues = Array() - for value in a { - let v = (value as! TestObjCValueTy).value - bridgedValues.append(v) - } - expectEqual([ 1010, 1020, 1030 ], bridgedValues) -} - -ArrayTestSuite.test("ArrayToNSArrayConversion") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - let nsa: NSArray = a as NSArray - - checkArrayFastEnumerationFromSwift( - [ 1010, 1020, 1030 ], - nsa, { a as NSArray }, - { ($0 as! TestObjCValueTy).value }) - - expectAutoreleasedValues(unopt: 3) -} - -//===--- -// Array upcasts -//===--- - -ArrayTestSuite.test("ArrayUpcastEntryPoint") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - var aAsAnyObject: Array = _arrayForceCast(a) - - expectEqual(3, aAsAnyObject.count) - var v: AnyObject = aAsAnyObject[0] - expectEqual(1010, (v as! TestObjCValueTy).value) - - v = aAsAnyObject[1] - expectEqual(1020, (v as! TestObjCValueTy).value) - - v = aAsAnyObject[2] - expectEqual(1030, (v as! TestObjCValueTy).value) -} - -ArrayTestSuite.test("ArrayUpcast") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - var dAsAnyObject: Array = a - - expectEqual(3, dAsAnyObject.count) - var v: AnyObject = dAsAnyObject[0] - expectEqual(1010, (v as! TestObjCValueTy).value) - - v = dAsAnyObject[1] - expectEqual(1020, (v as! TestObjCValueTy).value) - - v = dAsAnyObject[2] - expectEqual(1030, (v as! TestObjCValueTy).value) -} - -ArrayTestSuite.test("ArrayUpcastBridgedEntryPoint") { - var a = Array() - a.append(TestBridgedValueTy(1010)) - a.append(TestBridgedValueTy(1020)) - a.append(TestBridgedValueTy(1030)) - - do { - var aOO: Array = _arrayConditionalCast(a)! - - expectEqual(3, aOO.count) - var v: AnyObject = aOO[0] - expectEqual(1010, (v as! TestBridgedValueTy).value) - - v = aOO[1] - expectEqual(1020, (v as! TestBridgedValueTy).value) - - v = aOO[2] - expectEqual(1030, (v as! TestBridgedValueTy).value) - } -} - -ArrayTestSuite.test("ArrayUpcastBridged") { - var a = Array() - a.append(TestBridgedValueTy(1010)) - a.append(TestBridgedValueTy(1020)) - a.append(TestBridgedValueTy(1030)) - - do { - var aOO = a as Array - - expectEqual(3, aOO.count) - var v: AnyObject = aOO[0] - expectEqual(1010, (v as! TestBridgedValueTy).value) - - v = aOO[1] - expectEqual(1020, (v as! TestBridgedValueTy).value) - - v = aOO[2] - expectEqual(1030, (v as! TestBridgedValueTy).value) - } -} - -//===--- -// Array downcasts -//===--- - -ArrayTestSuite.test("ArrayDowncastEntryPoint") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - // Successful downcast. - let aCC: Array = _arrayForceCast(a) - expectEqual(3, aCC.count) - var v = aCC[0] - expectEqual(1010, v.value) - - v = aCC[1] - expectEqual(1020, v.value) - - v = aCC[2] - expectEqual(1030, v.value) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("ArrayDowncast") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - // Successful downcast. - let aCC = a as! Array - expectEqual(3, aCC.count) - var v = aCC[0] - expectEqual(1010, v.value) - - v = aCC[1] - expectEqual(1020, v.value) - - v = aCC[2] - expectEqual(1030, v.value) - - expectAutoreleasedValues(unopt: 3) -} - -ArrayTestSuite.test("ArrayDowncastConditionalEntryPoint") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - // Successful downcast. - if let aCC - = _arrayConditionalCast(a) as Array? { - expectEqual(3, aCC.count) - var v = aCC[0] - expectEqual(1010, v.value) - - v = aCC[1] - expectEqual(1020, v.value) - - v = aCC[2] - expectEqual(1030, v.value) - } else { - expectTrue(false) - } - - // Unsuccessful downcast - a[0] = 17 as NSNumber - a[1] = "hello" as NSString - if let _ = _arrayConditionalCast(a) as Array? { - expectTrue(false) - } -} - -ArrayTestSuite.test("ArrayDowncastConditional") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - // Successful downcast. - if let aCC = a as? Array { - expectEqual(3, aCC.count) - var v = aCC[0] - expectEqual(1010, v.value) - - v = aCC[1] - expectEqual(1020, v.value) - - v = aCC[2] - expectEqual(1030, v.value) - } else { - expectTrue(false) - } - - // Unsuccessful downcast - a[0] = 17 as NSNumber - a[1] = "hello" as NSString - if let _ = a as? Array { - expectTrue(false) - } -} - -ArrayTestSuite.test("ArrayBridgeFromObjectiveCEntryPoint") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - // Successful downcast. - let aCV: Array = _arrayConditionalCast(a)! - do { - expectEqual(3, aCV.count) - var v = aCV[0] - expectEqual(1010, v.value) - - v = aCV[1] - expectEqual(1020, v.value) - - v = aCV[2] - expectEqual(1030, v.value) - } - - // // Successful downcast. - let aVC: Array = _arrayConditionalCast(a)! - do { - expectEqual(3, aVC.count) - var v = aVC[0] - expectEqual(1010, v.value) - - v = aVC[1] - expectEqual(1020, v.value) - - v = aVC[2] - expectEqual(1030, v.value) - } -} - -ArrayTestSuite.test("ArrayBridgeFromObjectiveC") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - // Successful downcast. - let aCV = a as! Array - do { - expectEqual(3, aCV.count) - var v = aCV[0] - expectEqual(1010, v.value) - - v = aCV[1] - expectEqual(1020, v.value) - - v = aCV[2] - expectEqual(1030, v.value) - } - - // Successful downcast. - let aVC = a as! Array - do { - expectEqual(3, aVC.count) - var v = aVC[0] - expectEqual(1010, v.value) - - v = aVC[1] - expectEqual(1020, v.value) - - v = aVC[2] - expectEqual(1030, v.value) - } -} - -ArrayTestSuite.test("ArrayBridgeFromObjectiveCConditionalEntryPoint") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - // Successful downcast. - if let aCV = _arrayConditionalCast(a) as Array? { - expectEqual(3, aCV.count) - var v = aCV[0] - expectEqual(1010, v.value) - - v = aCV[1] - expectEqual(1020, v.value) - - v = aCV[2] - expectEqual(1030, v.value) - } else { - expectTrue(false) - } - - // Successful downcast. - if let aVC = _arrayConditionalCast(a) as Array? { - expectEqual(3, aVC.count) - var v = aVC[0] - expectEqual(1010, v.value) - - v = aVC[1] - expectEqual(1020, v.value) - - v = aVC[2] - expectEqual(1030, v.value) - } else { - expectTrue(false) - } - - // Unsuccessful downcasts - a[0] = 17 as NSNumber - a[1] = "hello" as NSString - if let _ = _arrayConditionalCast(a) as Array? { - expectTrue(false) - } - if let _ = _arrayConditionalCast(a) as Array? { - expectTrue(false) - } -} - -ArrayTestSuite.test("ArrayBridgeFromObjectiveCConditional") { - var a = Array() - a.append(TestObjCValueTy(1010)) - a.append(TestObjCValueTy(1020)) - a.append(TestObjCValueTy(1030)) - - // Successful downcast. - if let aCV = a as? Array { - expectEqual(3, aCV.count) - var v = aCV[0] - expectEqual(1010, v.value) - - v = aCV[1] - expectEqual(1020, v.value) - - v = aCV[2] - expectEqual(1030, v.value) - } else { - expectTrue(false) - } - - // Successful downcast. - if let aVC = a as? Array { - expectEqual(3, aVC.count) - var v = aVC[0] - expectEqual(1010, v.value) - - v = aVC[1] - expectEqual(1020, v.value) - - v = aVC[2] - expectEqual(1030, v.value) - } else { - expectTrue(false) - } - - // Unsuccessful downcasts - a[0] = 17 as NSNumber - a[1] = "hello" as NSString - if let _ = a as? Array { - expectTrue(false) - } - if let _ = a as? Array { - expectTrue(false) - } -} - -#endif - //===--- // Check that iterators traverse a snapshot of the collection. //===--- diff --git a/validation-test/stdlib/ArraysObjc.swift.gyb b/validation-test/stdlib/ArraysObjc.swift.gyb new file mode 100644 index 0000000000000..976c0b2fc76ed --- /dev/null +++ b/validation-test/stdlib/ArraysObjc.swift.gyb @@ -0,0 +1,2527 @@ +// RUN: %empty-directory(%t) +// +// RUN: %gyb %s -o %t/main.swift +// RUN: %target-clang -fobjc-arc %S/Inputs/SlurpFastEnumeration/SlurpFastEnumeration.m -c -o %t/SlurpFastEnumeration.o +// RUN: %line-directive %t/main.swift -- %target-build-swift %S/Inputs/DictionaryKeyValueTypes.swift %S/Inputs/DictionaryKeyValueTypesObjC.swift %t/main.swift -I %S/Inputs/SlurpFastEnumeration/ -Xlinker %t/SlurpFastEnumeration.o -o %t/Array -Xfrontend -disable-access-control -swift-version 4.2 +// RUN: %target-codesign %t/Array && %line-directive %t/main.swift -- %target-run %t/Array + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +// FIXME: rdar://problem/55944126 +// FIXME: rdar://problem/79626782 (test fails for watchsimulator-i386) +// UNSUPPORTED: CPU=armv7s || CPU=armv7k || CPU=i386 + +import Darwin +import Foundation +import StdlibUnittest +import StdlibUnittestFoundationExtras +import StdlibCollectionUnittest + +%{ +all_array_types = ['ContiguousArray', 'ArraySlice', 'Array'] +}% + +extension Array { + func _rawIdentifier() -> Int { + return unsafeBitCast(self, to: Int.self) + } +} + +%for Self in all_array_types: +extension ${Self} { + typealias _BufferID = UnsafeRawPointer? + var _bufferID: _BufferID { + return unsafeBitCast(_owner, to: _BufferID.self) + } +} +%end + +func withInoutInt(_ x: inout Int, body: (_ x: inout Int) -> Void) { + body(&x) +} + +func withInoutT(_ x: inout T, body: (_ x: inout T) -> Void) { + body(&x) +} + +func isCocoaNSArray(_ a: NSArray) -> Bool { + let className: NSString = NSStringFromClass(type(of: a)) as NSString + return className.range(of: "NSArray").length > 0 || + className.range(of: "NSCFArray").length > 0 +} + +func getBridgedNSArrayOfRefTypesBridgedVerbatim() -> NSArray { + expectTrue(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self)) + + var a = Array() + a.reserveCapacity(32) + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + let bridged = convertArrayToNSArray(a) + + assert(isNativeNSArray(bridged)) + + return bridged +} + +func getBridgedEmptyNSArray() -> NSArray { + let a = Array() + + let bridged = convertArrayToNSArray(a) + assert(isNativeNSArray(bridged)) + + return bridged +} + +func getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( + numElements: Int = 3 +) -> NSArray { + expectFalse(_isBridgedVerbatimToObjectiveC(TestBridgedValueTy.self)) + + var a = Array() + for i in 1..<(numElements + 1) { + a.append(TestBridgedValueTy(i * 10 + 1000)) + } + + let bridged = convertArrayToNSArray(a) + expectTrue(isNativeNSArray(bridged)) + + return bridged +} + +func getAsEquatableNSArray(_ a: Array) -> NSArray { + let values = Array(a.map { TestObjCEquatableValueTy($0) }) + + // Return an `NSMutableArray` to make sure that it has a unique + // pointer identity. + let nsarray = NSMutableArray() + nsarray.addObjects(from: values) + return nsarray +} + +func getBridgedVerbatimArrayAndNSMutableArray() + -> (Array, NSMutableArray) { + let nsa = getAsNSMutableArray([1010, 1020, 1030]) + return (convertNSArrayToArray(nsa), nsa) +} + +func getBridgedVerbatimArray() -> Array { + let nsa = getAsNSArray([1010, 1020, 1030]) + return convertNSArrayToArray(nsa) +} + +func getBridgedVerbatimArray(_ a: Array) -> Array { + let nsa = getAsNSArray(a) + return convertNSArrayToArray(nsa) +} + +func getBridgedNonverbatimArray() -> Array { + let nsa = getAsNSArray([1010, 1020, 1030 ]) + return Swift._forceBridgeFromObjectiveC(nsa, Array.self) +} + +func getBridgedNonverbatimArray(_ a: Array) -> Array { + let nsa = getAsNSArray(a) + return Swift._forceBridgeFromObjectiveC(nsa, Array.self) +} + +func getBridgedNonverbatimArrayAndNSMutableArray() + -> (Array, NSMutableArray) { + let nsa = getAsNSMutableArray([1010, 1020, 1030]) + return (Swift._forceBridgeFromObjectiveC(nsa, Array.self), nsa) +} + +func getBridgedVerbatimEquatableArray(_ a: Array) + -> Array { + let nsa = getAsEquatableNSArray(a) + return convertNSArrayToArray(nsa) +} + +func getBridgedNonverbatimEquatableArray(_ a: Array) + -> Array { + let nsa = getAsEquatableNSArray(a) + return Swift._forceBridgeFromObjectiveC(nsa, Array.self) +} + +func getHugeBridgedVerbatimArrayHelper() -> NSArray { + let values = (1...32).map { TestObjCValueTy(1000 + $0) } + + let nsa = NSMutableArray() + nsa.addObjects(from: values) + return nsa +} + +func getHugeBridgedVerbatimArray() -> Array { + let nsa = getHugeBridgedVerbatimArrayHelper() + return convertNSArrayToArray(nsa) +} + +func getHugeBridgedNonverbatimArray() + -> Array { + let nsa = getHugeBridgedVerbatimArrayHelper() + return Swift._forceBridgeFromObjectiveC(nsa, Array.self) +} + +func getBridgedNSArrayOfObj_ValueTypeCustomBridged() -> NSArray { + expectTrue(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self)) + + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + let bridged = convertArrayToNSArray(a) + expectTrue(isNativeNSArray(bridged)) + + return bridged +} + +func getBridgedNSArrayOfValue_ValueTypeCustomBridged() -> NSArray { + expectFalse(_isBridgedVerbatimToObjectiveC(TestBridgedValueTy.self)) + + var a = Array() + a.append(TestBridgedValueTy(1010)) + a.append(TestBridgedValueTy(1020)) + a.append(TestBridgedValueTy(1030)) + + let bridged = convertArrayToNSArray(a) + expectTrue(isNativeNSArray(bridged)) + + return bridged +} + +func getRoundtripBridgedNSArray() -> NSArray { + let values = [ 1010, 1020, 1030 ].map { TestObjCValueTy($0) } + + let nsa = NSArray(array: values) + + let a: Array = convertNSArrayToArray(nsa) + + let bridgedBack = convertArrayToNSArray(a) + expectTrue(isCocoaNSArray(bridgedBack)) + expectEqual(unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) + + return bridgedBack +} + + +var ArrayTestSuite = TestSuite("Array") + +//===----------------------------------------------------------------------===// +// NSArray -> Array bridging tests. +//===----------------------------------------------------------------------===// + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.ArrayIsCopied") { + var (a, nsa) = getBridgedVerbatimArrayAndNSMutableArray() + expectTrue(isCocoaArray(a)) + + // Find an existing value. + do { + let v = a[0] as! TestObjCValueTy + expectEqual(1010, v.value) + } + + // Remove the value from the NSMutableArray. + nsa.removeObject(at: 0) + + // Find an existing value, again. + do { + let v = a[0] as! TestObjCValueTy + expectEqual(1010, v.value) + } +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ArrayIsCopied") { + var (a, nsa) = getBridgedNonverbatimArrayAndNSMutableArray() + expectTrue(isNativeArray(a)) + + // Find an existing value. + do { + let v = a[0] + expectEqual(1010, v.value) + } + + // Remove the value from the NSMutableArray. + nsa.removeObject(at: 0) + + // Find an existing value, again. + do { + let v = a[0] + expectEqual(1010, v.value) + } +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.NSArrayIsRetained") { + let nsa: NSArray = autoreleasepool { + NSArray(array: getAsNSArray([1010, 1020, 1030])) + } + + let a: [AnyObject] = convertNSArrayToArray(nsa) + + let bridgedBack: NSArray = convertArrayToNSArray(a) + + expectEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _blackHole(nsa) + _blackHole(a) + _blackHole(bridgedBack) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.NSArrayIsCopied") { + let nsa: NSArray = autoreleasepool { + NSArray(array: getAsNSArray([1010, 1020, 1030])) + } + + let a: [TestBridgedValueTy] = convertNSArrayToArray(nsa) + + let bridgedBack: NSArray = convertArrayToNSArray(a) + + expectNotEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _blackHole(nsa) + _blackHole(a) + _blackHole(bridgedBack) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.ImmutableArrayIsRetained") { + let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) + + CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 + CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 + CustomImmutableNSArray.timesObjectEnumeratorWasCalled = 0 + CustomImmutableNSArray.timesCountWasCalled = 0 + let a: [AnyObject] = convertNSArrayToArray(nsa) + expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) + expectEqual(0, CustomImmutableNSArray.timesObjectAtIndexWasCalled) + expectEqual(0, CustomImmutableNSArray.timesObjectEnumeratorWasCalled) + expectEqual(0, CustomImmutableNSArray.timesCountWasCalled) + + let bridgedBack: NSArray = convertArrayToNSArray(a) + expectEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _blackHole(nsa) + _blackHole(a) + _blackHole(bridgedBack) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ImmutableArrayIsCopied") { + let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) + + CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 + CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 + CustomImmutableNSArray.timesObjectEnumeratorWasCalled = 0 + CustomImmutableNSArray.timesCountWasCalled = 0 + TestBridgedValueTy.bridgeOperations = 0 + let a: [TestBridgedValueTy] = convertNSArrayToArray(nsa) + //FIXME: Why is this copied? + expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) + expectEqual(3, CustomImmutableNSArray.timesObjectAtIndexWasCalled) + expectNotEqual(0, CustomImmutableNSArray.timesCountWasCalled) + expectEqual(3, TestBridgedValueTy.bridgeOperations) + + let bridgedBack: NSArray = convertArrayToNSArray(a) + expectNotEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _blackHole(nsa) + _blackHole(a) + _blackHole(bridgedBack) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.Subscript") { + let a = getBridgedVerbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isCocoaArray(a)) + + // Find an existing value. + do { + var v = a[0] + expectEqual(1010, (v as! TestObjCValueTy).value) + + v = a[1] + expectEqual(1020, (v as! TestObjCValueTy).value) + + v = a[2] + expectEqual(1030, (v as! TestObjCValueTy).value) + } + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Subscript") { + var a = getBridgedNonverbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isNativeArray(a)) + + // Find an existing value. + do { + var v = a[0] + expectEqual(1010, v.value) + + v = a[1] + expectEqual(1020, v.value) + + v = a[2] + expectEqual(1030, v.value) + } + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.RemoveAt") { + var a = getBridgedVerbatimArray() + let identity = a._rawIdentifier() + expectTrue(isCocoaArray(a)) + + let index = 0 + expectEqual(1010, (a[index] as! TestObjCValueTy).value) + expectEqual(identity, a._rawIdentifier()) + + let removedElement = a.remove(at: index) + expectNotEqual(identity, a._rawIdentifier()) + expectTrue(isNativeArray(a)) + expectEqual(1010, (removedElement as! TestObjCValueTy).value) + expectEqual(2, a.count) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.RemoveAt") { + var a = getBridgedNonverbatimArray() + let identity = a._rawIdentifier() + expectTrue(isNativeArray(a)) + + let index = 0 + expectEqual(1010, a[index].value) + expectEqual(identity, a._rawIdentifier()) + + let removedElement = a.remove(at: index) + expectEqual(identity, a._rawIdentifier()) + expectTrue(isNativeArray(a)) + expectEqual(1010, removedElement.value) + expectEqual(2, a.count) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.RemoveAll") { + do { + var a = getBridgedVerbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isCocoaArray(a)) + let originalCapacity = a.count + expectEqual(3, a.count) + expectEqual(1010, (a[0] as! TestObjCValueTy).value) + + a.removeAll() + expectNotEqual(identity1, a._rawIdentifier()) + expectLT(a._buffer.capacity, originalCapacity) + expectEqual(0, a.count) + } + + do { + var a = getBridgedVerbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isCocoaArray(a)) + let originalCapacity = a.count + expectEqual(3, a.count) + expectEqual(1010, (a[0] as! TestObjCValueTy).value) + + a.removeAll(keepingCapacity: true) + expectNotEqual(identity1, a._rawIdentifier()) + expectGE(a._buffer.capacity, originalCapacity) + expectEqual(0, a.count) + } + + do { + var a1 = getBridgedVerbatimArray() + let identity1 = a1._rawIdentifier() + expectTrue(isCocoaArray(a1)) + let originalCapacity = a1.count + expectEqual(3, a1.count) + expectEqual(1010, (a1[0] as! TestObjCValueTy).value) + + var a2 = a1 + a2.removeAll() + let identity2 = a2._rawIdentifier() + expectEqual(identity1, a1._rawIdentifier()) + expectNotEqual(identity2, identity1) + expectEqual(3, a1.count) + expectEqual(1010, (a1[0] as! TestObjCValueTy).value) + expectLT(a2._buffer.capacity, originalCapacity) + expectEqual(0, a2.count) + } + + do { + var a1 = getBridgedVerbatimArray() + let identity1 = a1._rawIdentifier() + expectTrue(isCocoaArray(a1)) + let originalCapacity = a1.count + expectEqual(3, a1.count) + expectEqual(1010, (a1[0] as! TestObjCValueTy).value) + + var a2 = a1 + a2.removeAll(keepingCapacity: true) + let identity2 = a2._rawIdentifier() + expectEqual(identity1, a1._rawIdentifier()) + expectNotEqual(identity2, identity1) + expectEqual(3, a1.count) + expectEqual(1010, (a1[0] as! TestObjCValueTy).value) + expectGE(a2._buffer.capacity, originalCapacity) + expectEqual(0, a2.count) + } +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.RemoveAll") { + do { + var a = getBridgedNonverbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isNativeArray(a)) + let originalCapacity = a.count + expectEqual(3, a.count) + expectEqual(1010, a[0].value) + + a.removeAll() + expectNotEqual(identity1, a._rawIdentifier()) + expectLT(a._buffer.capacity, originalCapacity) + expectEqual(0, a.count) + } + + do { + var a = getBridgedNonverbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isNativeArray(a)) + let originalCapacity = a.count + expectEqual(3, a.count) + expectEqual(1010, a[0].value) + + a.removeAll(keepingCapacity: true) + expectEqual(identity1, a._rawIdentifier()) + expectGE(a._buffer.capacity, originalCapacity) + expectEqual(0, a.count) + } + + do { + var a1 = getBridgedNonverbatimArray() + let identity1 = a1._rawIdentifier() + expectTrue(isNativeArray(a1)) + let originalCapacity = a1.count + expectEqual(3, a1.count) + expectEqual(1010, a1[0].value) + + var a2 = a1 + a2.removeAll() + let identity2 = a2._rawIdentifier() + expectEqual(identity1, a1._rawIdentifier()) + expectNotEqual(identity2, identity1) + expectEqual(3, a1.count) + expectEqual(1010, a1[0].value) + expectLT(a2._buffer.capacity, originalCapacity) + expectEqual(0, a2.count) + } + + do { + var a1 = getBridgedNonverbatimArray() + let identity1 = a1._rawIdentifier() + expectTrue(isNativeArray(a1)) + let originalCapacity = a1.count + expectEqual(3, a1.count) + expectEqual(1010, a1[0].value) + + var a2 = a1 + a2.removeAll(keepingCapacity: true) + let identity2 = a2._rawIdentifier() + expectEqual(identity1, a1._rawIdentifier()) + expectNotEqual(identity2, identity1) + expectEqual(3, a1.count) + expectEqual(1010, a1[0].value) + expectGE(a2._buffer.capacity, originalCapacity) + expectEqual(0, a2.count) + } +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.Count") { + let a = getBridgedVerbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isCocoaArray(a)) + + expectEqual(3, a.count) + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Count") { + let a = getBridgedNonverbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isNativeArray(a)) + + expectEqual(3, a.count) + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate") { + let a = getBridgedVerbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isCocoaArray(a)) + + var iter = a.makeIterator() + var values = Array() + while let value = iter.next() { + values.append((value as! TestObjCValueTy).value) + } + expectEqual(values, [1010, 1020, 1030]) + expectNil(iter.next()) + expectNil(iter.next()) + expectNil(iter.next()) + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate") { + let a = getBridgedNonverbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isNativeArray(a)) + + var iter = a.makeIterator() + var values = Array() + while let value = iter.next() { + values.append(value.value) + } + expectEqual(values, [1010, 1020, 1030]) + expectNil(iter.next()) + expectNil(iter.next()) + expectNil(iter.next()) + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate_Empty") { + let a = getBridgedVerbatimArray([]) + let identity1 = a._rawIdentifier() + expectTrue(isCocoaArray(a)) + + var iter = a.makeIterator() + expectNil(iter.next()) + expectNil(iter.next()) + expectNil(iter.next()) + expectNil(iter.next()) + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate_Empty") { + let a = getBridgedNonverbatimArray([]) + let identity1 = a._rawIdentifier() + expectTrue(isNativeArray(a)) + + var iter = a.makeIterator() + expectNil(iter.next()) + expectNil(iter.next()) + expectNil(iter.next()) + expectNil(iter.next()) + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate_Huge") { + let a = getHugeBridgedVerbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isCocoaArray(a)) + + var iter = a.makeIterator() + var values = Array() + while let value = iter.next() { + values.append((value as! TestObjCValueTy).value) + } + var expectedValues = Array() + for i in 1...32 { + expectedValues += [1000 + i] + } + expectEqual(values, expectedValues) + expectNil(iter.next()) + expectNil(iter.next()) + expectNil(iter.next()) + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate_Huge") { + let a = getHugeBridgedNonverbatimArray() + let identity1 = a._rawIdentifier() + expectTrue(isNativeArray(a)) + + var iter = a.makeIterator() + var values = Array() + while let value = iter.next() { + values.append(value.value) + } + var expectedValues = Array() + for i in 1...32 { + expectedValues += [1000 + i] + } + expectEqual(values, expectedValues) + expectNil(iter.next()) + expectNil(iter.next()) + expectNil(iter.next()) + expectEqual(identity1, a._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.EqualityTest_Empty") { + let a1 = getBridgedVerbatimEquatableArray([]) + let identity1 = a1._rawIdentifier() + expectTrue(isCocoaArray(a1)) + + let a2 = getBridgedVerbatimEquatableArray([]) + let identity2 = a2._rawIdentifier() + expectTrue(isCocoaArray(a2)) + + // We can't check that `identity1 != identity2` because Foundation might be + // returning the same singleton NSArray for empty arrays. + + expectEqual(a1, a2) + expectEqual(identity1, a1._rawIdentifier()) + expectEqual(identity2, a2._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.EqualityTest_Empty") { + var a1 = getBridgedNonverbatimEquatableArray([]) + a1.append(TestBridgedEquatableValueTy(1)) + a1.removeLast() + let identity1 = a1._rawIdentifier() + expectTrue(isNativeArray(a1)) + + let a2 = getBridgedNonverbatimEquatableArray([]) + let identity2 = a2._rawIdentifier() + expectTrue(isNativeArray(a2)) + expectNotEqual(identity1, identity2) + + expectEqual(a1, a2) + expectEqual(identity1, a1._rawIdentifier()) + expectEqual(identity2, a2._rawIdentifier()) +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.EqualityTest_Small") { + func helper(_ na1: Array, _ na2: Array, _ expectedEq: Bool) { + let a1 = getBridgedVerbatimEquatableArray(na1) + let identity1 = a1._rawIdentifier() + expectTrue(isCocoaArray(a1)) + + var a2 = getBridgedVerbatimEquatableArray(na2) + var identity2 = a2._rawIdentifier() + expectTrue(isCocoaArray(a2)) + + do { + let eq1 = (a1 == a2) + expectEqual(eq1, expectedEq) + + let eq2 = (a2 == a1) + expectEqual(eq2, expectedEq) + + let neq1 = (a1 != a2) + expectNotEqual(neq1, expectedEq) + + let neq2 = (a2 != a1) + expectNotEqual(neq2, expectedEq) + } + expectEqual(identity1, a1._rawIdentifier()) + expectEqual(identity2, a2._rawIdentifier()) + + a2.append(TestObjCEquatableValueTy(1111)) + a2.removeLast() + expectTrue(isNativeArray(a2)) + expectNotEqual(identity2, a2._rawIdentifier()) + identity2 = a2._rawIdentifier() + + do { + let eq1 = (a1 == a2) + expectEqual(eq1, expectedEq) + + let eq2 = (a2 == a1) + expectEqual(eq2, expectedEq) + + let neq1 = (a1 != a2) + expectNotEqual(neq1, expectedEq) + + let neq2 = (a2 != a1) + expectNotEqual(neq2, expectedEq) + } + expectEqual(identity1, a1._rawIdentifier()) + expectEqual(identity2, a2._rawIdentifier()) + } + + helper([], [], true) + + helper([1010], + [1010], + true) + + helper([1010, 1020], + [1010, 1020], + true) + + helper([1010, 1020, 1030], + [1010, 1020, 1030], + true) + + helper([1010, 1020, 1030], + [1010, 1020, 1111], + false) + + helper([1010, 1020, 1030], + [1010, 1020], + false) + + helper([1010, 1020, 1030], + [1010], + false) + + helper([1010, 1020, 1030], + [], + false) + + helper([1010, 1020, 1030], + [1010, 1020, 1030, 1040], + false) +} + +//===--- +// Array -> NSArray bridging tests. +// +// Values are bridged verbatim. +//===--- + +ArrayTestSuite.test("BridgedToObjC.Verbatim.Count") { + let d = getBridgedNSArrayOfRefTypesBridgedVerbatim() + + expectEqual(3, d.count) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectForKey") { + let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() + + var v: AnyObject? = a.object(at: 0) as AnyObject + expectEqual(1010, (v as! TestObjCValueTy).value) + let idValue10 = unsafeBitCast(v, to: UInt.self) + + v = a.object(at: 1) as AnyObject + expectEqual(1020, (v as! TestObjCValueTy).value) + let idValue20 = unsafeBitCast(v, to: UInt.self) + + v = a.object(at: 2) as AnyObject + expectEqual(1030, (v as! TestObjCValueTy).value) + let idValue30 = unsafeBitCast(v, to: UInt.self) + + for _ in 0..<3 { + expectEqual(idValue10, unsafeBitCast( + a.object(at: 0) as AnyObject, to: UInt.self)) + + expectEqual(idValue20, unsafeBitCast( + a.object(at: 1) as AnyObject, to: UInt.self)) + + expectEqual(idValue30, unsafeBitCast( + a.object(at: 2) as AnyObject, to: UInt.self)) + } + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.KeyEnumerator.NextObject") { + let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() + + var capturedIdentities = Array() + + for _ in 0..<3 { + let enumerator = a.objectEnumerator() + + var values = Array() + var identities = Array() + while let value = enumerator.nextObject() { + let valueObj = (value as! TestObjCValueTy) + values.append(valueObj.value) + + let identity = unsafeBitCast(valueObj, to: UInt.self) + identities.append(identity) + } + expectEqual([ 1010, 1020, 1030 ], values) + + if capturedIdentities.isEmpty { + capturedIdentities = identities + } else { + expectEqual(capturedIdentities, identities) + } + + expectNil(enumerator.nextObject()) + expectNil(enumerator.nextObject()) + expectNil(enumerator.nextObject()) + } + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.NextObject_Empty") { + let a = getBridgedEmptyNSArray() + let enumerator = a.objectEnumerator() + + expectNil(enumerator.nextObject()) + expectNil(enumerator.nextObject()) + expectNil(enumerator.nextObject()) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration.UseFromSwift") { + let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() + + checkArrayFastEnumerationFromSwift( + [ 1010, 1020, 1030 ], + a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration.UseFromObjC") { + let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() + + checkArrayFastEnumerationFromObjC( + [ 1010, 1020, 1030 ], + a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration_Empty") { + let a = getBridgedEmptyNSArray() + + checkArrayFastEnumerationFromSwift( + [], a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) + + checkArrayFastEnumerationFromObjC( + [], a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration.UseFromSwift") { + let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() + + checkArrayFastEnumerationFromSwift( + [ 1010, 1020, 1030 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration.UseFromObjC") { + let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() + + checkArrayFastEnumerationFromObjC( + [ 1010, 1020, 1030 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration_Empty") { + let a = getBridgedEmptyNSArray() + + checkArrayFastEnumerationFromSwift( + [], a, { a }, + { ($0 as! TestObjCValueTy).value }) + + checkArrayFastEnumerationFromObjC( + [], a, { a }, + { ($0 as! TestObjCValueTy).value }) +} + +//===--- +// Array -> NSArray bridging tests. +// +// Values are bridged non-verbatim. +//===--- + +ArrayTestSuite.test("BridgedToObjC.KeyValue_ValueTypesCustomBridged") { + let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() + let enumerator = a.objectEnumerator() + + var values = Array() + while let valueObj = enumerator.nextObject() { + let value: AnyObject = valueObj as AnyObject + let v = (value as! TestObjCValueTy).value + values.append(v) + } + expectEqual([ 1010, 1020, 1030 ], values) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromSwift") { + let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() + + checkArrayFastEnumerationFromSwift( + [ 1010, 1020, 1030 ], + a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromSwift.Partial") { + let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( + numElements: 9) + + checkArrayEnumeratorPartialFastEnumerationFromSwift( + [ 1010, 1020, 1030, 1040, 1050, + 1060, 1070, 1080, 1090 ], + a, maxFastEnumerationItems: 5, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 9) +} + +ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromObjC") { + let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() + + checkArrayFastEnumerationFromObjC( + [ 1010, 1020, 1030 ], + a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration.UseFromSwift") { + let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() + + checkArrayFastEnumerationFromSwift( + [ 1010, 1020, 1030 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration.UseFromObjC") { + let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() + + checkArrayFastEnumerationFromObjC( + [ 1010, 1020, 1030 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration_Empty") { + let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( + numElements: 0) + + checkArrayFastEnumerationFromSwift( + [], a, { a }, + { ($0 as! TestObjCValueTy).value }) + + checkArrayFastEnumerationFromObjC( + [], a, { a }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC.Key_ValueTypeCustomBridged") { + let a = getBridgedNSArrayOfObj_ValueTypeCustomBridged() + let enumerator = a.objectEnumerator() + + var values = Array() + while let valueObj = enumerator.nextObject() { + let value: AnyObject = valueObj as AnyObject + let v = (value as! TestObjCValueTy).value + values.append(v) + } + expectEqual([ 1010, 1020, 1030 ], values) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("BridgedToObjC.Value_ValueTypeCustomBridged") { + let a = getBridgedNSArrayOfValue_ValueTypeCustomBridged() + let enumerator = a.objectEnumerator() + + var values = Array() + while let valueObj = enumerator.nextObject() { + let value: AnyObject = valueObj as AnyObject + let v = (value as! TestObjCValueTy).value + values.append(v) + } + expectEqual([ 1010, 1020, 1030 ], values) + + expectAutoreleasedValues(unopt: 3) +} + +//===--- +// NSArray -> Array -> NSArray bridging tests. +//===--- + +ArrayTestSuite.test("BridgingRoundtrip") { + let a = getRoundtripBridgedNSArray() + let enumerator = a.objectEnumerator() + + var values = Array() + while let valueObj = enumerator.nextObject() { + let value: AnyObject = valueObj as AnyObject + let v = (value as! TestObjCValueTy).value + values.append(v) + } + expectEqual([ 1010, 1020, 1030 ], values) +} + +//===--- +// NSArray -> Array implicit conversion. +//===--- + +ArrayTestSuite.test("NSArrayToArrayConversion") { + let values = [ 1010, 1020, 1030 ].map { TestObjCValueTy($0) } + + let nsa = NSArray(array: values) + + let a: Array = nsa as Array + + var bridgedValues = Array() + for value in a { + let v = (value as! TestObjCValueTy).value + bridgedValues.append(v) + } + expectEqual([ 1010, 1020, 1030 ], bridgedValues) +} + +ArrayTestSuite.test("ArrayToNSArrayConversion") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + let nsa: NSArray = a as NSArray + + checkArrayFastEnumerationFromSwift( + [ 1010, 1020, 1030 ], + nsa, { a as NSArray }, + { ($0 as! TestObjCValueTy).value }) + + expectAutoreleasedValues(unopt: 3) +} + +//===--- +// Array upcasts +//===--- + +ArrayTestSuite.test("ArrayUpcastEntryPoint") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + var aAsAnyObject: Array = _arrayForceCast(a) + + expectEqual(3, aAsAnyObject.count) + var v: AnyObject = aAsAnyObject[0] + expectEqual(1010, (v as! TestObjCValueTy).value) + + v = aAsAnyObject[1] + expectEqual(1020, (v as! TestObjCValueTy).value) + + v = aAsAnyObject[2] + expectEqual(1030, (v as! TestObjCValueTy).value) +} + +ArrayTestSuite.test("ArrayUpcast") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + var dAsAnyObject: Array = a + + expectEqual(3, dAsAnyObject.count) + var v: AnyObject = dAsAnyObject[0] + expectEqual(1010, (v as! TestObjCValueTy).value) + + v = dAsAnyObject[1] + expectEqual(1020, (v as! TestObjCValueTy).value) + + v = dAsAnyObject[2] + expectEqual(1030, (v as! TestObjCValueTy).value) +} + +ArrayTestSuite.test("ArrayUpcastBridgedEntryPoint") { + var a = Array() + a.append(TestBridgedValueTy(1010)) + a.append(TestBridgedValueTy(1020)) + a.append(TestBridgedValueTy(1030)) + + do { + var aOO: Array = _arrayConditionalCast(a)! + + expectEqual(3, aOO.count) + var v: AnyObject = aOO[0] + expectEqual(1010, (v as! TestBridgedValueTy).value) + + v = aOO[1] + expectEqual(1020, (v as! TestBridgedValueTy).value) + + v = aOO[2] + expectEqual(1030, (v as! TestBridgedValueTy).value) + } +} + +ArrayTestSuite.test("ArrayUpcastBridged") { + var a = Array() + a.append(TestBridgedValueTy(1010)) + a.append(TestBridgedValueTy(1020)) + a.append(TestBridgedValueTy(1030)) + + do { + var aOO = a as Array + + expectEqual(3, aOO.count) + var v: AnyObject = aOO[0] + expectEqual(1010, (v as! TestBridgedValueTy).value) + + v = aOO[1] + expectEqual(1020, (v as! TestBridgedValueTy).value) + + v = aOO[2] + expectEqual(1030, (v as! TestBridgedValueTy).value) + } +} + +//===--- +// Array downcasts +//===--- + +ArrayTestSuite.test("ArrayDowncastEntryPoint") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + // Successful downcast. + let aCC: Array = _arrayForceCast(a) + expectEqual(3, aCC.count) + var v = aCC[0] + expectEqual(1010, v.value) + + v = aCC[1] + expectEqual(1020, v.value) + + v = aCC[2] + expectEqual(1030, v.value) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("ArrayDowncast") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + // Successful downcast. + let aCC = a as! Array + expectEqual(3, aCC.count) + var v = aCC[0] + expectEqual(1010, v.value) + + v = aCC[1] + expectEqual(1020, v.value) + + v = aCC[2] + expectEqual(1030, v.value) + + expectAutoreleasedValues(unopt: 3) +} + +ArrayTestSuite.test("ArrayDowncastConditionalEntryPoint") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + // Successful downcast. + if let aCC + = _arrayConditionalCast(a) as Array? { + expectEqual(3, aCC.count) + var v = aCC[0] + expectEqual(1010, v.value) + + v = aCC[1] + expectEqual(1020, v.value) + + v = aCC[2] + expectEqual(1030, v.value) + } else { + expectTrue(false) + } + + // Unsuccessful downcast + a[0] = 17 as NSNumber + a[1] = "hello" as NSString + if let _ = _arrayConditionalCast(a) as Array? { + expectTrue(false) + } +} + +ArrayTestSuite.test("ArrayDowncastConditional") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + // Successful downcast. + if let aCC = a as? Array { + expectEqual(3, aCC.count) + var v = aCC[0] + expectEqual(1010, v.value) + + v = aCC[1] + expectEqual(1020, v.value) + + v = aCC[2] + expectEqual(1030, v.value) + } else { + expectTrue(false) + } + + // Unsuccessful downcast + a[0] = 17 as NSNumber + a[1] = "hello" as NSString + if let _ = a as? Array { + expectTrue(false) + } +} + +ArrayTestSuite.test("ArrayBridgeFromObjectiveCEntryPoint") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + // Successful downcast. + let aCV: Array = _arrayConditionalCast(a)! + do { + expectEqual(3, aCV.count) + var v = aCV[0] + expectEqual(1010, v.value) + + v = aCV[1] + expectEqual(1020, v.value) + + v = aCV[2] + expectEqual(1030, v.value) + } + + // // Successful downcast. + let aVC: Array = _arrayConditionalCast(a)! + do { + expectEqual(3, aVC.count) + var v = aVC[0] + expectEqual(1010, v.value) + + v = aVC[1] + expectEqual(1020, v.value) + + v = aVC[2] + expectEqual(1030, v.value) + } +} + +ArrayTestSuite.test("ArrayBridgeFromObjectiveC") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + // Successful downcast. + let aCV = a as! Array + do { + expectEqual(3, aCV.count) + var v = aCV[0] + expectEqual(1010, v.value) + + v = aCV[1] + expectEqual(1020, v.value) + + v = aCV[2] + expectEqual(1030, v.value) + } + + // Successful downcast. + let aVC = a as! Array + do { + expectEqual(3, aVC.count) + var v = aVC[0] + expectEqual(1010, v.value) + + v = aVC[1] + expectEqual(1020, v.value) + + v = aVC[2] + expectEqual(1030, v.value) + } +} + +ArrayTestSuite.test("ArrayBridgeFromObjectiveCConditionalEntryPoint") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + // Successful downcast. + if let aCV = _arrayConditionalCast(a) as Array? { + expectEqual(3, aCV.count) + var v = aCV[0] + expectEqual(1010, v.value) + + v = aCV[1] + expectEqual(1020, v.value) + + v = aCV[2] + expectEqual(1030, v.value) + } else { + expectTrue(false) + } + + // Successful downcast. + if let aVC = _arrayConditionalCast(a) as Array? { + expectEqual(3, aVC.count) + var v = aVC[0] + expectEqual(1010, v.value) + + v = aVC[1] + expectEqual(1020, v.value) + + v = aVC[2] + expectEqual(1030, v.value) + } else { + expectTrue(false) + } + + // Unsuccessful downcasts + a[0] = 17 as NSNumber + a[1] = "hello" as NSString + if let _ = _arrayConditionalCast(a) as Array? { + expectTrue(false) + } + if let _ = _arrayConditionalCast(a) as Array? { + expectTrue(false) + } +} + +ArrayTestSuite.test("ArrayBridgeFromObjectiveCConditional") { + var a = Array() + a.append(TestObjCValueTy(1010)) + a.append(TestObjCValueTy(1020)) + a.append(TestObjCValueTy(1030)) + + // Successful downcast. + if let aCV = a as? Array { + expectEqual(3, aCV.count) + var v = aCV[0] + expectEqual(1010, v.value) + + v = aCV[1] + expectEqual(1020, v.value) + + v = aCV[2] + expectEqual(1030, v.value) + } else { + expectTrue(false) + } + + // Successful downcast. + if let aVC = a as? Array { + expectEqual(3, aVC.count) + var v = aVC[0] + expectEqual(1010, v.value) + + v = aVC[1] + expectEqual(1020, v.value) + + v = aVC[2] + expectEqual(1030, v.value) + } else { + expectTrue(false) + } + + // Unsuccessful downcasts + a[0] = 17 as NSNumber + a[1] = "hello" as NSString + if let _ = a as? Array { + expectTrue(false) + } + if let _ = a as? Array { + expectTrue(false) + } +} + +% for array_type in all_array_types: + +ArrayTestSuite.test("${array_type}/subscript(_: Int)/COW") + .code { + var a: ${array_type}<${array_type}> = [[ + TestBridgedValueTy(10), TestBridgedValueTy(20), TestBridgedValueTy(30), + TestBridgedValueTy(40), TestBridgedValueTy(50), TestBridgedValueTy(60), + TestBridgedValueTy(70) + ]] + let identityOuter = a._bufferID + var identityInner = a[0]._bufferID + + func checkIdentity(_ stackTrace: SourceLocStack) { + expectEqual(identityOuter, a._bufferID, stackTrace: stackTrace) + + // Should not reallocate storage. + expectEqual(identityInner, a[0]._bufferID, stackTrace: stackTrace) + } + + // Mutating through a subscript expression. + a[0][0] = TestBridgedValueTy(1010) + + checkIdentity(SourceLocStack().withCurrentLoc()) + + a[0][1].value = 1020 + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a) { + (x: inout ${array_type}<${array_type}>) in + x[0][2].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a[0]) { + (x: inout ${array_type}) in + x[3].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + // This will reallocate storage unless Array uses addressors for subscript. + //withInoutT(&a[0][4]) { + // (x: inout TestBridgedValueTy) in + // x.value += 1000 + //} + + // FIXME: both of these lines crash the compiler. + // Passing an expression based on addressors as + // 'inout' crashes SILGen + //withInoutT(&a[0][5].value, { $0 += 1000 }) + //withInoutInt(&a[0][6].value, { $0 += 1000 }) + + // Don't change the last element. + + expectEqual(1010, a[0][0].value) + expectEqual(1020, a[0][1].value) + expectEqual(1030, a[0][2].value) + expectEqual(1040, a[0][3].value) + expectEqual(50, a[0][4].value) + expectEqual(60, a[0][5].value) + expectEqual(70, a[0][6].value) +} + +ArrayTestSuite.test("${array_type}/subscript(_: Range)/COW") + .code { + var a: ${array_type}<${array_type}> = [[ + TestBridgedValueTy(10), TestBridgedValueTy(20), TestBridgedValueTy(30), + TestBridgedValueTy(40), TestBridgedValueTy(50), TestBridgedValueTy(60), + TestBridgedValueTy(70), TestBridgedValueTy(80), TestBridgedValueTy(90), + ]] + let identityOuter = a._bufferID + var identityInner = a[0]._bufferID + + func checkIdentity(_ stackTrace: SourceLocStack) { + expectEqual(identityOuter, a._bufferID, stackTrace: stackTrace) + // Writeback happens in subscript(Range), but array notices that the new + // value did not change. + // Another writeback happens in Array.subscript(Int), but this is not what we + // want. + expectNotEqual(identityInner, a[0]._bufferID, stackTrace: stackTrace) + identityInner = a[0]._bufferID + } + + // Mutating through a subscript expression. + a[0..<1][0][0] = TestBridgedValueTy(1010) + + // Reallocates storage because of the writeback in Array.subscript(Int). + expectEqual(identityOuter, a._bufferID) + expectNotEqual(identityInner, a[0]._bufferID) + identityInner = a[0]._bufferID + + a[0..<1][0][1].value = 1020 + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a) { + (x: inout ${array_type}<${array_type}>) in + x[0..<1][0][2].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a[0..<1]) { + (x: inout ArraySlice<${array_type}>) in + x[0][3].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a[0..<1][0]) { + (x: inout ${array_type}) in + x[4].value += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutT(&a[0..<1][0][5]) { + (x: inout TestBridgedValueTy) in + x.value += 1000 + } + + // Reallocates storage because of the writeback in Array.subscript(Int) + // (writeback is being requested for the array element even though it is not + // needed). + expectEqual(identityOuter, a._bufferID) + expectNotEqual(identityInner, a[0]._bufferID) + identityInner = a[0]._bufferID + + withInoutT(&a[0..<1][0][6].value) { + (x: inout Int) in + x += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + withInoutInt(&a[0..<1][0][7].value) { + (x: inout Int) in + x += 1000 + } + + checkIdentity(SourceLocStack().withCurrentLoc()) + + // Don't change the last element. + + expectEqual(1010, a[0][0].value) + expectEqual(1020, a[0][1].value) + expectEqual(1030, a[0][2].value) + expectEqual(1040, a[0][3].value) + expectEqual(1050, a[0][4].value) + expectEqual(1060, a[0][5].value) + expectEqual(1070, a[0][6].value) + expectEqual(1080, a[0][7].value) + expectEqual(90, a[0][8].value) +} + +% end + +// FIXME: all the tests below are applicable to ArraySlice, too. + +//===----------------------------------------------------------------------===// +// NSArray -> Array bridging tests +// FIXME: incomplete. +//===----------------------------------------------------------------------===// + +func isNativeArray(_ a: Array) -> Bool { + return a._hoistableIsNativeTypeChecked() +} + +func isCocoaArray(_ a: Array) -> Bool { + return !isNativeArray(a) +} + +func getAsImmutableNSArray(_ a: Array) -> NSArray { + var elements = a.map { TestObjCValueTy($0) as AnyObject } + return NSArray(objects: &elements, count: elements.count) +} + +func getAsNSArray(_ a: Array) -> NSArray { + // Return an `NSMutableArray` to make sure that it has a unique + // pointer identity. + return getAsNSMutableArray(a) +} + +func getAsNSMutableArray(_ a: Array) -> NSMutableArray { + let result = NSMutableArray() + for element in a { + result.add(TestObjCValueTy(element)) + } + return result +} + +@objc +class CustomImmutableNSArray : NSArray { + init(_privateInit: ()) { + super.init() + } + + override init() { + expectUnreachable() + super.init() + } + + override init(objects: UnsafePointer?, count: Int) { + super.init(objects: objects, count: count) + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) not implemented by CustomImmutableNSArray") + } + + @objc(copyWithZone:) + override func copy(with zone: NSZone?) -> Any { + CustomImmutableNSArray.timesCopyWithZoneWasCalled += 1 + return self + } + + @objc + override func object(at index: Int) -> Any { + CustomImmutableNSArray.timesObjectAtIndexWasCalled += 1 + return _data[index] + } + + @objc + override var count: Int { + CustomImmutableNSArray.timesCountWasCalled += 1 + return _data.count + } + + @objc + override func countByEnumerating( + with state: UnsafeMutablePointer, + objects: AutoreleasingUnsafeMutablePointer, + count: Int + ) -> Int { + var theState = state.pointee + if theState.state == 0 { + theState.state = 1 + theState.itemsPtr = + AutoreleasingUnsafeMutablePointer(_data._baseAddressIfContiguous) + theState.mutationsPtr = _fastEnumerationStorageMutationsPtr + state.pointee = theState + return _data.count + } + return 0 + } + + let _data = [ 10, 20, 30 ].map { TestObjCValueTy($0) } + + static var timesCopyWithZoneWasCalled = 0 + static var timesObjectAtIndexWasCalled = 0 + static var timesObjectEnumeratorWasCalled = 0 + static var timesCountWasCalled = 0 +} + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.BridgeUsingAs") { + do { + let source = [ 10, 20, 30 ] + let nsa = getAsNSArray(source) + var result = nsa as Array + expectTrue(isCocoaArray(result)) + expectType(Array.self, &result) + checkSequence(source.map { TestObjCValueTy($0) as AnyObject }, result) { + ($0 as! TestObjCValueTy).value == ($1 as! TestObjCValueTy).value + } + } + do { + let source = [ 10, 20, 30 ] + let nsa = getAsNSArray(source) + var result = nsa as! Array + expectTrue(isCocoaArray(result)) + expectType(Array.self, &result) + checkSequence(source.map { TestObjCValueTy($0) }, result) { + $0.value == $1.value + } + } +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.BridgeUsingAs") { + let source = [ 10, 20, 30 ] + let nsa = getAsNSArray(source) + var result = nsa as! Array + expectTrue(isNativeArray(result)) + expectType(Array.self, &result) + checkSequence(source.map { TestBridgedValueTy($0) }, result) { + $0.value == $1.value + } +} + + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.ArrayIsCopied") { + let source = [ 10, 20, 30 ] + let nsa = getAsNSMutableArray(source) + let result = nsa as Array + expectTrue(isCocoaArray(result)) + + // Delete the value from NSMutableArray. + expectEqual(20, (nsa[1] as! TestObjCValueTy).value) + nsa.removeObject(at: 1) + expectEqual(30, (nsa[1] as! TestObjCValueTy).value) + + // Check that the Array is not affected. + expectEqual(20, (result[1] as! TestObjCValueTy).value) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ArrayIsCopied") { + let source = [ 10, 20, 30 ] + let nsa = getAsNSMutableArray(source) + var result = nsa as AnyObject as! Array + expectTrue(isNativeArray(result)) + + // Delete the value from NSMutableArray. + expectEqual(20, (nsa[1] as! TestObjCValueTy).value) + nsa.removeObject(at: 1) + expectEqual(30, (nsa[1] as! TestObjCValueTy).value) + + // Check that the Array is not affected. + expectEqual(20, result[1].value) +} + + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.NSArrayIsRetained") { + let nsa = NSArray(array: getAsNSArray([ 10, 20, 30 ])) + let a: Array = convertNSArrayToArray(nsa) + let bridgedBack: NSArray = convertArrayToNSArray(a) + + expectEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _fixLifetime(nsa) + _fixLifetime(a) + _fixLifetime(bridgedBack) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.NSArrayIsCopied") { + let nsa = NSArray(array: getAsNSArray([ 10, 20, 30 ])) + let a: Array = convertNSArrayToArray(nsa) + let bridgedBack: NSArray = convertArrayToNSArray(a) + + expectNotEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _fixLifetime(nsa) + _fixLifetime(a) + _fixLifetime(bridgedBack) +} + + +ArrayTestSuite.test("BridgedFromObjC.Verbatim.ImmutableArrayIsRetained") { + let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) + + CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 + CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 + CustomImmutableNSArray.timesCountWasCalled = 0 + let a: Array = convertNSArrayToArray(nsa) + expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) + expectEqual(0, CustomImmutableNSArray.timesObjectAtIndexWasCalled) + expectEqual(0, CustomImmutableNSArray.timesCountWasCalled) + + let bridgedBack: NSArray = convertArrayToNSArray(a) + + expectEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _fixLifetime(nsa) + _fixLifetime(a) + _fixLifetime(bridgedBack) +} + +ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ImmutableArrayIsCopied") + .skip(.iOSAny("")) + .skip(.tvOSAny("")) + .code { + let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) + + CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 + CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 + CustomImmutableNSArray.timesCountWasCalled = 0 + TestBridgedValueTy.bridgeOperations = 0 + var a: Array = [] + + // FIXME: bridging shouldn't dump array contents into the autorelease pool. + autoreleasepoolIfUnoptimizedReturnAutoreleased { + a = convertNSArrayToArray(nsa) + expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) + expectEqual(3, CustomImmutableNSArray.timesObjectAtIndexWasCalled) + expectNotEqual(0, CustomImmutableNSArray.timesCountWasCalled) + expectEqual(3, TestBridgedValueTy.bridgeOperations) + } + + let bridgedBack: NSArray = convertArrayToNSArray(a) + + expectNotEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _fixLifetime(nsa) + _fixLifetime(a) + _fixLifetime(bridgedBack) +} + +// FIXME: test API calls on the BridgedFromObjC arrays. + +//===----------------------------------------------------------------------===// +// Array -> NSArray bridging tests +// +// Element is bridged verbatim. +// +// FIXME: incomplete. +//===----------------------------------------------------------------------===// + +ArrayTestSuite.test("BridgedToObjC.Verbatim.BridgeUsingAs") { + let source = [ 10, 20, 30 ].map { TestObjCValueTy($0) } + let result = source as NSArray + expectTrue(isNativeNSArray(result)) + expectEqual(3, result.count) + autoreleasepoolIfUnoptimizedReturnAutoreleased { + expectEqual(10, (result[0] as! TestObjCValueTy).value) + expectEqual(20, (result[1] as! TestObjCValueTy).value) + expectEqual(30, (result[2] as! TestObjCValueTy).value) + } +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/count/empty") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) + expectEqual(0, a.count) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/count") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged() + expectEqual(3, a.count) +} + +for index in [ -100, -1, 0, 1, 100 ] { + ArrayTestSuite.test( + "BridgedToObjC/Verbatim/objectAtIndex/empty/trap/\(index)") + .crashOutputMatches("Array index out of range") + .code { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) + expectCrashLater() + a.object(at: index) + } +} + +for index in [ -100, -1, 3, 4, 100 ] { + ArrayTestSuite.test("BridgedToObjC/Verbatim/objectAtIndex/trap/\(index)") + .crashOutputMatches("Array index out of range") + .code { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + expectCrashLater() + a.object(at: index) + } +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/objectAtIndex") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + + var v: AnyObject = a.object(at: 0) as AnyObject + expectEqual(10, (v as! TestObjCValueTy).value) + let idValue0 = unsafeBitCast(v, to: UInt.self) + + v = a.object(at: 1) as AnyObject + expectEqual(20, (v as! TestObjCValueTy).value) + let idValue1 = unsafeBitCast(v, to: UInt.self) + + v = a.object(at: 2) as AnyObject + expectEqual(30, (v as! TestObjCValueTy).value) + let idValue2 = unsafeBitCast(v, to: UInt.self) + + expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) + expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) + expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) +} + +for indexRange in [ + -2..<(-2), 1..<1, + 0..<4, -2..<(-1), -1..<2, 0..<1, 2..<4, 4..<5 +] as [Range] { + ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects/empty/trap/\(indexRange)") + .crashOutputMatches("Array index out of range") + .code { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged( + numElements: 0, capacity: 16) + let buffer = UnsafeMutablePointer.allocate(capacity: 16) + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<0)) + expectCrashLater() + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) + } +} + +for indexRange in [ 0..<4, -2..<(-1), -1..<2, 2..<4, 4..<5 ] as [Range] { + ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects/trap/\(indexRange)") + .crashOutputMatches("Array index out of range") + .code { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged( + numElements: 3, capacity: 16) + let buffer = UnsafeMutablePointer.allocate(capacity: 16) + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) + expectCrashLater() + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) + } +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + let buffer = UnsafeMutablePointer.allocate(capacity: 16) + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) + + var v: AnyObject = buffer[0] + expectEqual(10, (v as! TestObjCValueTy).value) + let idValue0 = unsafeBitCast(v, to: UInt.self) + + v = buffer[1] + expectEqual(20, (v as! TestObjCValueTy).value) + let idValue1 = unsafeBitCast(v, to: UInt.self) + + v = buffer[2] + expectEqual(30, (v as! TestObjCValueTy).value) + let idValue2 = unsafeBitCast(v, to: UInt.self) + + expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) + expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) + expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) + + buffer.deallocate() + _fixLifetime(a) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/copyWithZone") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + let copy: AnyObject = a.copy(with: nil) as AnyObject + expectEqual( + unsafeBitCast(a, to: UInt.self), unsafeBitCast(copy, to: UInt.self)) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/Empty") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) + + checkArrayFastEnumerationFromSwift( + [], a, { a }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/3") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + + checkArrayFastEnumerationFromSwift( + [ 10, 20, 30 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/7") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 7) + + checkArrayFastEnumerationFromSwift( + [ 10, 20, 30, 40, 50, 60, 70 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromObjC/Empty") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) + + checkArrayFastEnumerationFromObjC( + [], a, { a }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromObjC") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + + checkArrayFastEnumerationFromObjC( + [ 10, 20, 30 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift/Empty") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) + + checkArrayFastEnumerationFromSwift( + [], a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + + checkArrayFastEnumerationFromSwift( + [ 10, 20, 30 ], + a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift/Partial") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 9) + + checkArrayEnumeratorPartialFastEnumerationFromSwift( + [ 10, 20, 30, 40, 50, 60, 70, 80, 90 ], + a, maxFastEnumerationItems: 5, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromObjC") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + + checkArrayFastEnumerationFromObjC( + [ 10, 20, 30 ], + a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Reallocate") { + let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) + + var v: AnyObject = a[0] as AnyObject + expectEqual(10, (v as! TestObjCValueTy).value) + let idValue0 = unsafeBitCast(v, to: UInt.self) + + v = a[1] as AnyObject + expectEqual(20, (v as! TestObjCValueTy).value) + let idValue1 = unsafeBitCast(v, to: UInt.self) + + v = a[2] as AnyObject + expectEqual(30, (v as! TestObjCValueTy).value) + let idValue2 = unsafeBitCast(v, to: UInt.self) + + // Bridge back to native array. + var native: [TestObjCValueTy] = convertNSArrayToArray(a) + native[0] = TestObjCValueTy(110) + native[1] = TestObjCValueTy(120) + native[2] = TestObjCValueTy(130) + native.append(TestObjCValueTy(140)) + + // Ensure that the compiler does not elide mutation of the native array. + _blackHole(native) + + // Check that mutating the native array did not affect the bridged array. + expectEqual(3, a.count) + expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) + expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) + expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) +} + +ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Adopt") { + // Bridge back to native array. + var native: [TestObjCValueTy] = + getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) as! Array + + let identity1 = unsafeBitCast(native, to: UInt.self) + + // Mutate elements, but don't change count. + native[0] = TestObjCValueTy(110) + native[1] = TestObjCValueTy(120) + native[2] = TestObjCValueTy(130) + + // Expect no reallocations. + expectEqual(identity1, unsafeBitCast(native, to: UInt.self)) +} + +//===----------------------------------------------------------------------===// +// Array -> NSArray bridging tests +// +// Element is bridged non-verbatim. +// +// FIXME: incomplete. +//===----------------------------------------------------------------------===// + +ArrayTestSuite.test("BridgedToObjC.Nonverbatim.BridgeUsingAs") { + let source = [ 10, 20, 30 ].map { TestBridgedValueTy($0) } + var result = source as NSArray + expectTrue(isNativeNSArray(result)) + expectEqual(3, result.count) + autoreleasepoolIfUnoptimizedReturnAutoreleased { + expectEqual(10, (result[0] as! TestBridgedValueTy).value) + expectEqual(20, (result[1] as! TestBridgedValueTy).value) + expectEqual(30, (result[2] as! TestBridgedValueTy).value) + } +} + +ArrayTestSuite.test("BridgedToObjC/Custom/count/empty") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) + expectEqual(0, a.count) + + expectEqual(0, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/count") { + let a = getBridgedNSArrayOfValueTypeCustomBridged() + expectEqual(3, a.count) + + expectEqual(0, TestBridgedValueTy.bridgeOperations) +} + +for index in [ -100, -1, 0, 1, 100 ] { + ArrayTestSuite.test( + "BridgedToObjC/Custom/objectAtIndex/empty/trap/\(index)") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) + expectCrashLater() + a.object(at: index) + } +} + +for index in [ -100, -1, 3, 4, 100 ] { + ArrayTestSuite.test("BridgedToObjC/Custom/objectAtIndex/trap/\(index)") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + expectCrashLater() + a.object(at: index) + } +} + +ArrayTestSuite.test("BridgedToObjC/Custom/objectAtIndex") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + + var v: AnyObject = a.object(at: 0) as AnyObject + expectEqual(10, (v as! TestObjCValueTy).value) + let idValue0 = unsafeBitCast(v, to: UInt.self) + + v = a.object(at: 1) as AnyObject + expectEqual(20, (v as! TestObjCValueTy).value) + let idValue1 = unsafeBitCast(v, to: UInt.self) + + v = a.object(at: 2) as AnyObject + expectEqual(30, (v as! TestObjCValueTy).value) + let idValue2 = unsafeBitCast(v, to: UInt.self) + + expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) + expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) + expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) + + expectEqual(3, TestBridgedValueTy.bridgeOperations) +} + +for indexRange in [ + -2..<(-2), 1..<1, + 0..<4, -2..<(-1), -1..<2, 0..<1, 2..<4, 4..<5 +] as [Range] { + ArrayTestSuite.test("BridgedToObjC/Custom/getObjects/empty/trap/\(indexRange)") + .crashOutputMatches("Array index out of range") + .code { + let a = getBridgedNSArrayOfValueTypeCustomBridged( + numElements: 0, capacity: 16) + let buffer = UnsafeMutablePointer.allocate(capacity: 16) + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<0)) + expectCrashLater() + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) + } +} + +for indexRange in [ 0..<4, -2..<(-1), -1..<2, 2..<4, 4..<5 ] as [Range] { + ArrayTestSuite.test("BridgedToObjC/Custom/getObjects/trap/\(indexRange)") + .crashOutputMatches("Array index out of range") + .code { + let a = getBridgedNSArrayOfValueTypeCustomBridged( + numElements: 3, capacity: 16) + let buffer = UnsafeMutablePointer.allocate(capacity: 16) + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) + expectCrashLater() + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) + } +} + +ArrayTestSuite.test("BridgedToObjC/Custom/getObjects") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + let buffer = UnsafeMutablePointer.allocate(capacity: 16) + a.available_getObjects( + AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) + + var v: AnyObject = buffer[0] + expectEqual(10, (v as! TestObjCValueTy).value) + let idValue0 = unsafeBitCast(v, to: UInt.self) + + v = buffer[1] + expectEqual(20, (v as! TestObjCValueTy).value) + let idValue1 = unsafeBitCast(v, to: UInt.self) + + v = buffer[2] + expectEqual(30, (v as! TestObjCValueTy).value) + let idValue2 = unsafeBitCast(v, to: UInt.self) + + expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) + expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) + expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) + + buffer.deallocate() + _fixLifetime(a) + + expectEqual(3, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/copyWithZone") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + let copy: AnyObject = a.copy(with: nil) as AnyObject + expectEqual( + unsafeBitCast(a, to: UInt.self), + unsafeBitCast(copy, to: UInt.self)) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/Empty") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) + + checkArrayFastEnumerationFromSwift( + [], a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(0, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/3") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + + checkArrayFastEnumerationFromSwift( + [ 10, 20, 30 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(3, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/7") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 7) + + checkArrayFastEnumerationFromSwift( + [ 10, 20, 30, 40, 50, 60, 70 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(7, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromObjC/Empty") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) + + checkArrayFastEnumerationFromObjC( + [], a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(0, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromObjC") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + + checkArrayFastEnumerationFromObjC( + [ 10, 20, 30 ], + a, { a }, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(3, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift/Empty") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) + + checkArrayFastEnumerationFromSwift( + [], a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(0, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + + checkArrayFastEnumerationFromSwift( + [ 10, 20, 30 ], + a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(3, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift/Partial") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 9) + + checkArrayEnumeratorPartialFastEnumerationFromSwift( + [ 10, 20, 30, 40, 50, 60, 70, 80, 90 ], + a, maxFastEnumerationItems: 5, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(9, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromObjC") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + + checkArrayFastEnumerationFromObjC( + [ 10, 20, 30 ], + a, { a.objectEnumerator() }, + { ($0 as! TestObjCValueTy).value }) + + expectEqual(3, TestBridgedValueTy.bridgeOperations) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Cast") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + + var v: AnyObject = a[0] as AnyObject + expectEqual(10, (v as! TestObjCValueTy).value) + let idValue0 = unsafeBitCast(v, to: UInt.self) + + v = a[1] as AnyObject + expectEqual(20, (v as! TestObjCValueTy).value) + let idValue1 = unsafeBitCast(v, to: UInt.self) + + v = a[2] as AnyObject + expectEqual(30, (v as! TestObjCValueTy).value) + let idValue2 = unsafeBitCast(v, to: UInt.self) + + // Bridge back to native array with a cast. + var native: [TestObjCValueTy] = convertNSArrayToArray(a) + native[0] = TestObjCValueTy(110) + native[1] = TestObjCValueTy(120) + native[2] = TestObjCValueTy(130) + native.append(TestObjCValueTy(140)) + + // Ensure that the compiler does not elide mutation of the native array. + _blackHole(native) + + // Check that mutating the native array did not affect the bridged array. + expectEqual(3, a.count) + expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) + expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) + expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Reallocate") { + let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) + + var v: AnyObject = a[0] as AnyObject + expectEqual(10, (v as! TestObjCValueTy).value) + let idValue0 = unsafeBitCast(v, to: UInt.self) + + v = a[1] as AnyObject + expectEqual(20, (v as! TestObjCValueTy).value) + let idValue1 = unsafeBitCast(v, to: UInt.self) + + v = a[2] as AnyObject + expectEqual(30, (v as! TestObjCValueTy).value) + let idValue2 = unsafeBitCast(v, to: UInt.self) + + // Bridge back to native array. + var native: [TestBridgedValueTy] = convertNSArrayToArray(a) + native[0] = TestBridgedValueTy(110) + native[1] = TestBridgedValueTy(120) + native[2] = TestBridgedValueTy(130) + native.append(TestBridgedValueTy(140)) + + // Ensure that the compiler does not elide mutation of the native array. + _blackHole(native) + + // Check that mutating the native array did not affect the bridged array. + expectEqual(3, a.count) + expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) + expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) + expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) +} + +ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Adopt") { + // Bridge back to native array. + var native: [TestBridgedValueTy] = convertNSArrayToArray( + getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3)) + let identity1 = unsafeBitCast(native, to: UInt.self) + + // Mutate elements, but don't change count. + native[0] = TestBridgedValueTy(110) + native[1] = TestBridgedValueTy(120) + native[2] = TestBridgedValueTy(130) + + // Expect no reallocations. + expectEqual(identity1, unsafeBitCast(native, to: UInt.self)) +} + +//===----------------------------------------------------------------------===// +// NSArray -> Array -> NSArray bridging tests. +//===----------------------------------------------------------------------===// + +ArrayTestSuite.test("BridgedToObjC.Verbatim.RoundtripThroughSwiftArray") { +% for (MiddleType, AsCast) in [ +% ('Array', 'as'), +% ('Array', 'as!'), +% ]: + do { + let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) + let a: ${MiddleType} = convertNSArrayToArray(nsa) + let bridgedBack = convertArrayToNSArray(a) + + expectEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _fixLifetime(nsa) + _fixLifetime(a) + _fixLifetime(bridgedBack) + } + do { + let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) + let a = nsa ${AsCast} ${MiddleType} + let bridgedBack: NSArray = a as NSArray + + expectEqual( + unsafeBitCast(nsa, to: Int.self), + unsafeBitCast(bridgedBack, to: Int.self)) + + _fixLifetime(nsa) + _fixLifetime(a) + _fixLifetime(bridgedBack) + } +% end +} + +ArrayTestSuite.test("BridgedToObjC.Nonverbatim.RoundtripThroughSwiftArray") { + do { + TestBridgedValueTy.bridgeOperations = 0 + let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) + let a: Array = convertNSArrayToArray(nsa) + let _ = convertArrayToNSArray(a) + expectEqual(3, TestBridgedValueTy.bridgeOperations) + } + do { + TestBridgedValueTy.bridgeOperations = 0 + let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) + let a = nsa as! Array + let _: NSArray = a as NSArray + expectEqual(3, TestBridgedValueTy.bridgeOperations) + } +} + +ArrayTestSuite.test("append(contentsOf: NSArray)") { + // A stray runtime `is` test caused this particular operation to fail in 5.3. + // rdar://70448247 + let nsarray: NSArray = [2, 3, 4] + var array: [Any] = [1] + array.append(contentsOf: nsarray) + expectEqual(array as? [Int], [1, 2, 3, 4]) +} + +ArrayTestSuite.setUp { + resetLeaksOfDictionaryKeysValues() + resetLeaksOfObjCDictionaryKeysValues() + TestBridgedValueTy.bridgeOperations = 0 +} + +ArrayTestSuite.tearDown { + if _isDebugAssertConfiguration() { + // The return autorelease optimization does not happen reliable. + expectNoLeaksOfDictionaryKeysValues() + expectNoLeaksOfObjCDictionaryKeysValues() + } +} + +runAllTests() + diff --git a/validation-test/stdlib/CollectionDiagnostics.swift b/validation-test/stdlib/CollectionDiagnostics.swift index 55fc2ee11e6f5..f0c60275c02a5 100644 --- a/validation-test/stdlib/CollectionDiagnostics.swift +++ b/validation-test/stdlib/CollectionDiagnostics.swift @@ -135,6 +135,83 @@ struct RangeReplaceableCollection_SubSequence_IsDefaulted : RangeReplaceableColl } } +// +// A Collection that does not use `Slice` as its SubSequence should +// require its own implementation of the Range subscript getter. +// The only valid default implementation of that Collection requirement +// returns `Slice`. +// + +// expected-error@+2 {{type 'CollectionWithNonDefaultSubSequence' does not conform to protocol 'Collection'}} +// expected-error@+1 {{unavailable subscript 'subscript(_:)' was used to satisfy a requirement of protocol 'Collection'}} +struct CollectionWithNonDefaultSubSequence: Collection { + public var startIndex: Int + public var endIndex: Int + + public typealias SubSequence = Self + + public func index(after i: Int) -> Int { i+1 } + public subscript(position: Int) -> Int { position } +} + +// expected-error@+2 {{type 'MutableCollectionWithNonDefaultSubSequence' does not conform to protocol 'MutableCollection'}} +// expected-error@+1 {{unavailable subscript 'subscript(_:)' was used to satisfy a requirement of protocol 'MutableCollection'}} +struct MutableCollectionWithNonDefaultSubSequence: MutableCollection { + public var startIndex: Int + public var endIndex: Int + + public typealias SubSequence = Self + + public func index(after i: Int) -> Int { i+1 } + public subscript(position: Int) -> Int { + get { position } + set { _ = newValue } + } + + public subscript(bounds: Range) -> Self { + Self(startIndex: bounds.startIndex, endIndex: bounds.endIndex) + } +} + +struct MutableCollectionWithDefaultSubSequence: MutableCollection { + public var startIndex: Int + public var endIndex: Int + + public func index(after i: Int) -> Int { i+1 } + public subscript(position: Int) -> Int { + get { position } + set { _ = newValue } + } +} + +func subscriptMutableCollectionIgnored() { + let cs: MutableCollectionWithNonDefaultSubSequence + cs = .init(startIndex: 0, endIndex: 10) + + let badSlice: Slice + badSlice = cs[0..<2] // expected-error {{'subscript(_:)' is unavailable}} + cs[3..<5] = badSlice // expected-error {{'subscript(_:)' is unavailable}} + + let ds: MutableCollectionWithDefaultSubSequence + ds = .init(startIndex: 0, endIndex: 10) + + let goodSlice: Slice + goodSlice = ds[0..<2] + ds[3..<5] = goodSlice +} + +// expected-error@+2 {{type 'IncompleteRangeReplaceableCollection' does not conform to protocol 'RangeReplaceableCollection'}} +// expected-error@+1 {{unavailable instance method 'replaceSubrange(_:with:)' was used to satisfy a requirement of protocol 'RangeReplaceableCollection'}} +struct IncompleteRangeReplaceableCollection: RangeReplaceableCollection { + var startIndex: Int + var endIndex: Int + + func index(after i: Int) -> Int { i+1 } + subscript(position: Int) -> Int { position } + + init() { startIndex = 0; endIndex = 0 } + } + // FIXME: Remove -verify-ignore-unknown. // :0: error: unexpected note produced: possibly intended match // :0: error: unexpected note produced: possibly intended match diff --git a/validation-test/stdlib/DictionaryBridging.swift b/validation-test/stdlib/DictionaryBridging.swift index eed83d32a67ec..4d4ad4af12778 100644 --- a/validation-test/stdlib/DictionaryBridging.swift +++ b/validation-test/stdlib/DictionaryBridging.swift @@ -3,6 +3,7 @@ // RUN: %target-clang -fobjc-arc %S/Inputs/SlurpFastEnumeration/SlurpFastEnumeration.m -c -o %t/SlurpFastEnumeration.o // RUN: echo '#sourceLocation(file: "%s", line: 1)' > "%t/main.swift" && cat "%s" >> "%t/main.swift" && chmod -w "%t/main.swift" // RUN: %target-build-swift -Xfrontend -disable-access-control -I %S/Inputs/SlurpFastEnumeration/ %t/main.swift %S/Inputs/DictionaryKeyValueTypes.swift %S/Inputs/DictionaryKeyValueTypesObjC.swift -Xlinker %t/SlurpFastEnumeration.o -o %t.out -O -swift-version 4.2 +// RUN: %target-codesign %t.out // RUN: %target-run %t.out // REQUIRES: executable_test // REQUIRES: stress_test diff --git a/validation-test/stdlib/ParameterPassing.swift.gyb b/validation-test/stdlib/ParameterPassing.swift.gyb index d4b7a65249fae..ccc6cb041611a 100644 --- a/validation-test/stdlib/ParameterPassing.swift.gyb +++ b/validation-test/stdlib/ParameterPassing.swift.gyb @@ -12,6 +12,7 @@ // RUN: %empty-directory(%t) // RUN: %gyb %s -o %t/ParameterPassing.swift // RUN: %line-directive %t/ParameterPassing.swift -- %target-build-swift %t/ParameterPassing.swift -o %t/a.out_Release -O +// RUN: %target-codesign %t/a.out_Release // RUN: %target-run %t/a.out_Release // REQUIRES: executable_test // REQUIRES: long_test diff --git a/validation-test/stdlib/Range.swift.gyb b/validation-test/stdlib/Range.swift.gyb index 1b8e69c20135f..9be69c4cd1c79 100644 --- a/validation-test/stdlib/Range.swift.gyb +++ b/validation-test/stdlib/Range.swift.gyb @@ -344,7 +344,7 @@ ${TestSuite}.test("init(uncheckedBounds:)") expectEqual(upperInt, r.upperBound.value) } -if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) { // Debug check was introduced in https://github.com/apple/swift/pull/34961 ${TestSuite}.test("init(uncheckedBounds:) with invalid values") .xfail( diff --git a/validation-test/stdlib/SIMDParameterPassing.swift.gyb b/validation-test/stdlib/SIMDParameterPassing.swift.gyb index f442396cfbb6e..05a11cc4b7106 100644 --- a/validation-test/stdlib/SIMDParameterPassing.swift.gyb +++ b/validation-test/stdlib/SIMDParameterPassing.swift.gyb @@ -12,6 +12,7 @@ // RUN: %empty-directory(%t) // RUN: %gyb %s -o %t/SIMDParameterPassing.swift // RUN: %line-directive %t/SIMDParameterPassing.swift -- %target-build-swift %t/SIMDParameterPassing.swift -o %t/a.out_Release -O +// RUN: %target-codesign %t/a.out_Release // RUN: %target-run %t/a.out_Release // REQUIRES: executable_test diff --git a/validation-test/stdlib/Stride.swift b/validation-test/stdlib/Stride.swift index 8d7ae39de6d00..8e793b516a64f 100644 --- a/validation-test/stdlib/Stride.swift +++ b/validation-test/stdlib/Stride.swift @@ -9,7 +9,7 @@ var StrideTestSuite = TestSuite("Stride") StrideTestSuite.test("to") { checkSequence(Array(0...4), stride(from: 0, to: 5, by: 1)) checkSequence(Array(1...5).reversed(), stride(from: 5, to: 0, by: -1)) - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { // This used to crash before https://github.com/apple/swift/pull/34860 checkSequence(stride(from: 0, to: 127, by: 3).map { Int8($0) }, stride(from: 0, to: 127 as Int8, by: 3)) @@ -19,7 +19,7 @@ StrideTestSuite.test("to") { StrideTestSuite.test("through") { checkSequence(Array(0...5), stride(from: 0, through: 5, by: 1)) checkSequence(Array(0...5).reversed(), stride(from: 5, through: 0, by: -1)) - if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { // This used to crash before https://github.com/apple/swift/pull/34860 checkSequence(stride(from: 0, through: 127, by: 3).map { Int8($0) }, stride(from: 0, through: 127 as Int8, by: 3)) diff --git a/validation-test/stdlib/SwiftNativeNSBase.swift b/validation-test/stdlib/SwiftNativeNSBase.swift index a8d875adfe5df..0145addd941f1 100644 --- a/validation-test/stdlib/SwiftNativeNSBase.swift +++ b/validation-test/stdlib/SwiftNativeNSBase.swift @@ -55,6 +55,7 @@ SwiftNativeNSBaseTestSuite.test("UnwantedCdtors") .skip(.iOSSimulatorAny(/*Range(0...13), reason: */"lazy objc naming is not available on these OSes")) .skip(.tvOSSimulatorAny(/*TODO: 0...13, reason: */"lazy objc naming is not available on these OSes")) .skip(.watchOSSimulatorAny(/*TODO: 0...6, reason: */"lazy objc naming is not available on these OSes")) + .skip(.always("rdar://78931257 This test crashes on macOS 12")) .code { expectTrue(TestSwiftNativeNSBase_UnwantedCdtors()) } diff --git a/validation-test/stdlib/Unicode.swift.gyb b/validation-test/stdlib/Unicode.swift.gyb index 8a7b8e69cbd5e..e1f5b7d465603 100644 --- a/validation-test/stdlib/Unicode.swift.gyb +++ b/validation-test/stdlib/Unicode.swift.gyb @@ -210,6 +210,8 @@ UnicodeScalarTests.test("init") { expectEqual("h", UnicodeScalar(UInt16(104))) expectEqual("i", UnicodeScalar(UInt8(105))) expectEqual(nil, UnicodeScalar(UInt32(0xD800))) + expectEqual("j", Unicode.Scalar(Int(0x6A))) + expectEqual(nil, Unicode.Scalar(-0x6A)) } var UTF8Decoder = TestSuite("UTF8Decoder") diff --git a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb index 901df932836f3..26aa6f9e40984 100644 --- a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb +++ b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb @@ -361,6 +361,22 @@ UnsafeMutableBufferPointerTestSuite.test("withContiguous(Mutable)StorageIfAvaila expectEqual(result1, result2) } +UnsafeMutableBufferPointerTestSuite.test("initialize(from: Slice)") { + let o = 91 + let c = 1_000 + let a = Slice(base: Array(0...allocate(capacity: c-o) + defer { buffer.deallocate() } + + var (iterator, copied) = buffer.initialize(from: a) + expectEqual(iterator.next(), nil) + expectEqual(copied, c-o) + + let t = buffer.indices.randomElement()! + expectEqual(a[t+o], buffer[t]) +} + UnsafeMutableBufferPointerTestSuite.test("Slice.withContiguousStorageIfAvailable") { guard #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) else { return diff --git a/validation-test/stdlib/XCTest.swift b/validation-test/stdlib/XCTest.swift deleted file mode 100644 index 17a4bde588d22..0000000000000 --- a/validation-test/stdlib/XCTest.swift +++ /dev/null @@ -1,535 +0,0 @@ -// RUN: rm -rf %t ; mkdir -p %t -// RUN: %target-build-swift %s -o %t/a.out -swift-version 4 - -// The actual XCTest overlay is not maintained in this repository -- it is -// distributed in Xcode (libXCTestSwiftSupport.dylib), along with -// XCTest.framework itself. -// -// The codebase here builds the obsolete /usr/lib/swift/libswiftXCTest.dylib -// that currently ships in the OS. There is no expectation that that library is -// usable for anything; it only exists to maintain a superficial level of binary -// compatibility with existing apps that happen to link to it by mistake. -// -// Accordingly, this test is now a build-only test. The code here is only -// compiled, it is never run. -// -// rdar://problem/55270944 - -// REQUIRES: objc_interop - -// FIXME: Add a feature for "platforms that support XCTest". -// REQUIRES: OS=macosx -// UNSUPPORTED: remote_run - -import StdlibUnittest - - -import XCTest - -var XCTestTestSuite = TestSuite("XCTest") - -// NOTE: When instantiating test cases for a particular test method using the -// XCTestCase(selector:) initializer, those test methods must be marked -// as dynamic. Objective-C XCTest uses runtime introspection to -// instantiate an NSInvocation with the given selector. - - -func execute(observers: [XCTestObservation] = [], _ run: () -> Void) { - for observer in observers { - XCTestObservationCenter.shared.addTestObserver(observer) - } - - run() - - for observer in observers { - XCTestObservationCenter.shared.removeTestObserver(observer) - } -} - -class FailureDescriptionObserver: NSObject, XCTestObservation { - var failureDescription: String? - - typealias LineNumber=Int - - func testCase(_ testCase: XCTestCase, didFailWithDescription description: String, inFile filePath: String?, atLine lineNumber: LineNumber) { - failureDescription = description - } -} - -XCTestTestSuite.test("exceptions") { - class ExceptionTestCase: XCTestCase { - dynamic func test_raises() { - NSException(name: NSExceptionName(rawValue: "XCTestTestSuiteException"), reason: nil, userInfo: nil).raise() - } - - func test_raisesDuringAssertion() { - let exception = NSException(name: NSExceptionName(rawValue: "XCTestTestSuiteException"), reason: nil, userInfo: nil) - XCTAssertNoThrow(exception.raise()) - } - - func test_continueAfterFailureWithAssertions() { - self.continueAfterFailure = false - func triggerFailure() { XCTFail("I'm outta here!") } - - XCTAssertNoThrow(triggerFailure()) - - // Should not be reached: - NSException(name: NSExceptionName(rawValue: "XCTestTestSuiteException"), reason: nil, userInfo: nil).raise() - } - } - - let testCase = ExceptionTestCase(selector: #selector(ExceptionTestCase.test_raises)) - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(0, testRun.failureCount) - expectEqual(1, testRun.unexpectedExceptionCount) - expectEqual(1, testRun.totalFailureCount) - expectFalse(testRun.hasSucceeded) - - let assertionTestCase = ExceptionTestCase(selector: #selector(ExceptionTestCase.test_raisesDuringAssertion)) - execute(assertionTestCase.run) - let assertionTestRun = assertionTestCase.testRun! - expectEqual(1, assertionTestRun.executionCount) - expectEqual(0, assertionTestRun.failureCount) - expectEqual(1, assertionTestRun.unexpectedExceptionCount) - - let continueAfterFailureTestCase = ExceptionTestCase(selector: #selector(ExceptionTestCase.test_continueAfterFailureWithAssertions)) - execute(continueAfterFailureTestCase.run) - let continueAfterFailureTestRun = continueAfterFailureTestCase.testRun! - expectEqual(1, continueAfterFailureTestRun.executionCount) - expectEqual(1, continueAfterFailureTestRun.failureCount) - expectEqual(0, continueAfterFailureTestRun.unexpectedExceptionCount) -} - -XCTestTestSuite.test("XCTAssertEqual/T") { - class AssertEqualTestCase: XCTestCase { - dynamic func test_whenEqual_passes() { - XCTAssertEqual(1, 1) - } - - dynamic func test_whenNotEqual_fails() { - XCTAssertEqual(1, 2) - } - } - - let passingTestCase = AssertEqualTestCase(selector: #selector(AssertEqualTestCase.test_whenEqual_passes)) - execute(passingTestCase.run) - let passingTestRun = passingTestCase.testRun! - expectTrue(passingTestRun.hasSucceeded) - - let failingTestCase = AssertEqualTestCase(selector: #selector(AssertEqualTestCase.test_whenNotEqual_fails)) - let observer = FailureDescriptionObserver() - execute(observers: [observer], failingTestCase.run) - let failingTestRun = failingTestCase.testRun! - expectEqual(1, failingTestRun.failureCount) - expectEqual(0, failingTestRun.unexpectedExceptionCount) - expectTrue(observer.failureDescription!.starts(with: "XCTAssertEqual failed: (\"1\") is not equal to (\"2\")")) -} - -XCTestTestSuite.test("XCTAssertEqual/Optional") { - class AssertEqualOptionalTestCase: XCTestCase { - dynamic func test_whenOptionalsAreEqual_passes() { - XCTAssertEqual(Optional(1),Optional(1)) - } - - dynamic func test_whenOptionalsAreNotEqual_fails() { - XCTAssertEqual(Optional(1),Optional(2)) - } - } - - let passingTestCase = AssertEqualOptionalTestCase(selector: #selector(AssertEqualOptionalTestCase.test_whenOptionalsAreEqual_passes)) - execute(passingTestCase.run) - let passingTestRun = passingTestCase.testRun! - expectEqual(1, passingTestRun.testCaseCount) - expectEqual(1, passingTestRun.executionCount) - expectEqual(0, passingTestRun.failureCount) - expectEqual(0, passingTestRun.unexpectedExceptionCount) - expectEqual(0, passingTestRun.totalFailureCount) - expectTrue(passingTestRun.hasSucceeded) - - let failingTestCase = AssertEqualOptionalTestCase(selector: #selector(AssertEqualOptionalTestCase.test_whenOptionalsAreNotEqual_fails)) - let observer = FailureDescriptionObserver() - execute(observers: [observer], failingTestCase.run) - let failingTestRun = failingTestCase.testRun! - expectEqual(1, failingTestRun.testCaseCount) - expectEqual(1, failingTestRun.executionCount) - expectEqual(1, failingTestRun.failureCount) - expectEqual(0, failingTestRun.unexpectedExceptionCount) - expectEqual(1, failingTestRun.totalFailureCount) - expectFalse(failingTestRun.hasSucceeded) - expectTrue(observer.failureDescription!.starts(with: "XCTAssertEqual failed: (\"Optional(1)\") is not equal to (\"Optional(2)\")")) -} - -XCTestTestSuite.test("XCTAssertEqual/Array") { - class AssertEqualArrayTestCase: XCTestCase { - dynamic func test_whenArraysAreEqual_passes() { - XCTAssertEqual(["foo", "bar", "baz"], - ["foo", "bar", "baz"]) - } - - dynamic func test_whenArraysAreNotEqual_fails() { - XCTAssertEqual(["foo", "baz", "bar"], - ["foo", "bar", "baz"]) - } - } - - let passingTestCase = AssertEqualArrayTestCase(selector: #selector(AssertEqualArrayTestCase.test_whenArraysAreEqual_passes)) - execute(passingTestCase.run) - let passingTestRun = passingTestCase.testRun! - expectEqual(1, passingTestRun.testCaseCount) - expectEqual(1, passingTestRun.executionCount) - expectEqual(0, passingTestRun.failureCount) - expectEqual(0, passingTestRun.unexpectedExceptionCount) - expectEqual(0, passingTestRun.totalFailureCount) - expectTrue(passingTestRun.hasSucceeded) - - let failingTestCase = AssertEqualArrayTestCase(selector: #selector(AssertEqualArrayTestCase.test_whenArraysAreNotEqual_fails)) - execute(failingTestCase.run) - let failingTestRun = failingTestCase.testRun! - expectEqual(1, failingTestRun.testCaseCount) - expectEqual(1, failingTestRun.executionCount) - expectEqual(1, failingTestRun.failureCount) - expectEqual(0, failingTestRun.unexpectedExceptionCount) - expectEqual(1, failingTestRun.totalFailureCount) - expectFalse(failingTestRun.hasSucceeded) -} - -XCTestTestSuite.test("XCTAssertEqual/Dictionary") { - class AssertEqualDictionaryTestCase: XCTestCase { - dynamic func test_whenDictionariesAreEqual_passes() { - XCTAssertEqual(["foo": "bar", "baz": "flim"], - ["baz": "flim", "foo": "bar"]) - } - - dynamic func test_whenDictionariesAreNotEqual_fails() { - XCTAssertEqual(["foo": ["bar": "baz"] as NSDictionary], - ["foo": ["bar": "flim"] as NSDictionary]) - } - } - - let passingTestCase = AssertEqualDictionaryTestCase(selector: #selector(AssertEqualDictionaryTestCase.test_whenDictionariesAreEqual_passes)) - execute(passingTestCase.run) - let passingTestRun = passingTestCase.testRun! - expectEqual(1, passingTestRun.testCaseCount) - expectEqual(1, passingTestRun.executionCount) - expectEqual(0, passingTestRun.failureCount) - expectEqual(0, passingTestRun.unexpectedExceptionCount) - expectEqual(0, passingTestRun.totalFailureCount) - expectTrue(passingTestRun.hasSucceeded) - - let failingTestCase = AssertEqualDictionaryTestCase(selector: #selector(AssertEqualDictionaryTestCase.test_whenDictionariesAreNotEqual_fails)) - execute(failingTestCase.run) - let failingTestRun = failingTestCase.testRun! - expectEqual(1, failingTestRun.testCaseCount) - expectEqual(1, failingTestRun.executionCount) - expectEqual(1, failingTestRun.failureCount) - expectEqual(0, failingTestRun.unexpectedExceptionCount) - expectEqual(1, failingTestRun.totalFailureCount) - expectFalse(failingTestRun.hasSucceeded) -} - -XCTestTestSuite.test("XCTAssertEqual/XCTAssertNotEqual + accuracy") { - class AssertEqualTestCase: XCTestCase { - dynamic func test_whenEqual_passes() { - XCTAssertEqual(1, 1.09, accuracy: 0.1) - XCTAssertNotEqual(1, 1.11, accuracy: 0.1) - } - - dynamic func test_whenNotEqual_fails() { - XCTAssertEqual(1, 1.11, accuracy: 0.1) - XCTAssertNotEqual(1, 1.09, accuracy: 0.1) - } - } - - let passingTestCase = AssertEqualTestCase(selector: #selector(AssertEqualTestCase.test_whenEqual_passes)) - execute(passingTestCase.run) - expectTrue(passingTestCase.testRun!.hasSucceeded) - - let failingTestCase = AssertEqualTestCase(selector: #selector(AssertEqualTestCase.test_whenNotEqual_fails)) - execute(failingTestCase.run) - let failingTestRun = failingTestCase.testRun! - expectEqual(2, failingTestRun.failureCount) - expectEqual(0, failingTestRun.unexpectedExceptionCount) -} - -XCTestTestSuite.test("XCTAssertThrowsError") { - class ErrorTestCase: XCTestCase { - var doThrow = true - var errorCode = 42 - - dynamic func throwSomething() throws { - if doThrow { - throw NSError(domain: "MyDomain", code: errorCode, userInfo: nil) - } - } - - dynamic func test_throws() { - XCTAssertThrowsError(try throwSomething()) { - error in - let nserror = error as NSError - XCTAssertEqual(nserror.domain, "MyDomain") - XCTAssertEqual(nserror.code, 42) - } - } - } - - // Try success case - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_throws)) - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(0, testRun.failureCount) - expectEqual(0, testRun.unexpectedExceptionCount) - expectEqual(0, testRun.totalFailureCount) - expectTrue(testRun.hasSucceeded) - } - - // Now try when it does not throw - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_throws)) - testCase.doThrow = false - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(1, testRun.failureCount) - expectEqual(0, testRun.unexpectedExceptionCount) - expectEqual(1, testRun.totalFailureCount) - expectFalse(testRun.hasSucceeded) - } - - - // Now try when it throws the wrong thing - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_throws)) - testCase.errorCode = 23 - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(1, testRun.failureCount) - expectEqual(0, testRun.unexpectedExceptionCount) - expectEqual(1, testRun.totalFailureCount) - expectFalse(testRun.hasSucceeded) - } - -} - -XCTestTestSuite.test("XCTAssertNoThrow") { - class ErrorTestCase: XCTestCase { - var doThrow = true - var errorCode = 42 - - dynamic func throwSomething() throws { - if doThrow { - throw NSError(domain: "MyDomain", code: errorCode, userInfo: nil) - } - } - - dynamic func test_throws() { - XCTAssertNoThrow(try throwSomething()) - } - } - - // Success - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_throws)) - testCase.doThrow = false - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(0, testRun.failureCount) - expectEqual(0, testRun.unexpectedExceptionCount) - expectEqual(0, testRun.totalFailureCount) - expectTrue(testRun.hasSucceeded) - } - - // Failure - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_throws)) - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(1, testRun.failureCount) - expectEqual(0, testRun.unexpectedExceptionCount) - expectEqual(1, testRun.totalFailureCount) - expectFalse(testRun.hasSucceeded) - } - - // Throws wrong thing - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_throws)) - testCase.errorCode = 23 - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(1, testRun.failureCount) - expectEqual(0, testRun.unexpectedExceptionCount) - expectEqual(1, testRun.totalFailureCount) - expectFalse(testRun.hasSucceeded) - } -} - -XCTestTestSuite.test("XCTAsserts with throwing expressions") { - class ErrorTestCase: XCTestCase { - var doThrow = true - var errorCode = 42 - - dynamic func throwSomething() throws -> String { - if doThrow { - throw NSError(domain: "MyDomain", code: errorCode, userInfo: nil) - } - return "Hello" - } - - dynamic func test_withThrowing() { - XCTAssertEqual(try throwSomething(), "Hello") - } - } - - // Try success case - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_withThrowing)) - testCase.doThrow = false - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(0, testRun.failureCount) - expectEqual(0, testRun.unexpectedExceptionCount) - expectEqual(0, testRun.totalFailureCount) - expectTrue(testRun.hasSucceeded) - } - - // Now try when the expression throws - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_withThrowing)) - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(0, testRun.failureCount) - expectEqual(1, testRun.unexpectedExceptionCount) - expectEqual(1, testRun.totalFailureCount) - expectFalse(testRun.hasSucceeded) - } -} - -XCTestTestSuite.test("Test methods that wind up throwing") { - class ErrorTestCase: XCTestCase { - var doThrow = true - var errorCode = 42 - - dynamic func throwSomething() throws { - if doThrow { - throw NSError(domain: "MyDomain", code: errorCode, userInfo: nil) - } - } - - dynamic func test_withThrowing() throws { - try throwSomething() - } - } - - // Try success case - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_withThrowing)) - testCase.doThrow = false - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(0, testRun.failureCount) - expectEqual(0, testRun.unexpectedExceptionCount) - expectEqual(0, testRun.totalFailureCount) - expectTrue(testRun.hasSucceeded) - } - - // Now try when the expression throws - do { - let testCase = ErrorTestCase(selector: #selector(ErrorTestCase.test_withThrowing)) - execute(testCase.run) - let testRun = testCase.testRun! - - expectEqual(1, testRun.testCaseCount) - expectEqual(1, testRun.executionCount) - expectEqual(0, testRun.failureCount) - expectEqual(1, testRun.unexpectedExceptionCount) - expectEqual(1, testRun.totalFailureCount) - expectFalse(testRun.hasSucceeded) - } - -} - -XCTestTestSuite.test("XCTContext/runActivity(named:block:)") { - class RunActivityTestCase: XCTestCase { - - dynamic func test_noThrow_void() { - var blockCalled = false - XCTContext.runActivity(named: "noThrow") { activity in - blockCalled = true - } - expectTrue(blockCalled) - } - - dynamic func test_noThrow_returns_string() { - var blockCalled = false - let value = XCTContext.runActivity(named: "noThrow") { activity -> String in - blockCalled = true - return "Activities can return values now!" - } - expectEqual(value, "Activities can return values now!") - expectTrue(blockCalled) - } - - dynamic func test_throwing() { - var blockCalled = false - var catchCalled = false - do { - try XCTContext.runActivity(named: "throwing") { activity in - blockCalled = true - throw NSError(domain: "MyDomain", code: -1, userInfo: nil) - } - } catch { - catchCalled = true - } - expectTrue(blockCalled) - expectTrue(catchCalled) - } - } -} - -#if os(macOS) -if #available(macOS 10.11, *) { - XCTestTestSuite.test("XCUIElement/typeKey(_:modifierFlags:)") { - class TypeKeyTestCase: XCTestCase { - func testTypeKey() { - XCUIApplication().typeKey("a", modifierFlags: []) - XCUIApplication().typeKey(.delete, modifierFlags: []) - } - } - } -} -#endif - - -runAllTests() - -